diff --git a/2023_ModelFLOWsapp_Hetheringtonetal.pdf b/2023_ModelFLOWsapp_Hetheringtonetal.pdf deleted file mode 100644 index 1891089..0000000 Binary files a/2023_ModelFLOWsapp_Hetheringtonetal.pdf and /dev/null differ diff --git a/README.md b/README.md deleted file mode 100644 index c82a87e..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# ModelFLOWs-app -ModelFLOWs application diff --git a/v0.1_ModelFLOWs_app/AEmodel.py b/v0.1_ModelFLOWs_app/AEmodel.py new file mode 100644 index 0000000..73509f9 --- /dev/null +++ b/v0.1_ModelFLOWs_app/AEmodel.py @@ -0,0 +1,654 @@ +def autoencoders(): + import numpy as np + np.seterr(all='raise') + + import pandas as pd + import os + os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' + import matplotlib.pyplot as plt + import time + import hdf5storage + + pd.set_option('display.max_columns',100) + pd.set_option('display.max_rows',100) + + plt.rcParams['font.size'] = 12 + + from numpy import linalg as LA + + import tensorflow as tf + from tensorflow import keras + from keras.layers import Input, Dense + from keras.models import Model + import tensorflow as tf + + import matplotlib.animation as animation + + import data_load + + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def video(Tensor, vel, Title): + nt = Tensor.shape[-1] + + if nt in range(200, 500): + Tensor[..., ::5] + + elif nt > 500: + Tensor[..., ::15] + + frames = Tensor.shape[-1] + + fig, ax = plt.subplots(figsize = (8, 4), num = f'CLOSE TO CONTINUE RUN - {Title}') + fig.tight_layout() + + def animate(i): + ax.clear() + ax.contourf(Tensor[vel, :, :, i]) + ax.set_title(Title) + + interval = 2 + anim = animation.FuncAnimation(fig, animate, frames = frames, interval = interval*1e+2, blit = False) + + plt.show() + + # Error functions + def mean_absolute_percentage_error(y_true, y_pred): + epsilon = 1e-10 + y_true, y_pred = np.array(y_true), np.array(y_pred) + return np.mean(np.abs((y_true - y_pred) / np.maximum(epsilon,np.abs(y_true)))) * 100 + + def smape(A, F): + return ((100.0/len(A)) * np.sum(2 * np.abs(F - A) / (np.abs(A) + np.abs(F))+ np.finfo(float).eps)) + + # Relative root mean square error + def RRMSE (real, predicted): + RRMSE = np.linalg.norm(np.reshape(real-predicted,newshape=(np.size(real),1)),ord=2)/np.linalg.norm(np.reshape(real,newshape=(np.size(real),1))) + return RRMSE + + ################## INPUTS ################# + + print('\nDeep Learning Autoencoders model') + print('\n-----------------------------') + print('Inputs:\n') + + while True: + filetype = input('Select the input file format (.mat, .npy, .csv, .pkl, .h5): ') + print('\n\tWarning: This model can only be trained with 2-Dimensional data (as in: (variables, nx, ny, time))\n') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + # Load data + path0 = os.getcwd() + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + Tensor, _ = data_load.main(filetype) + + tensor = Tensor # Select only the first two components + nvar, ny, nx, nt = tensor.shape + + print(''' +-----------------------AE----------------------- + +Available AE algorithms +1) Spatial modes (compression of temporal dimension) +2) Temporal modes (compression of spatial dimension) + + ''') + while True: + type = input('Select an AE ALGORITHM (1/2): ') + if type.isdigit(): + if int(type) == 1: + type_AE=1 + break + elif int(type) == 2: + type_AE=2 + break + else: + print('\tError: Please select a valid AE algorithm\n') + else: + print('\tError: Please introduce a valid input\n') + + + # AEs parameters + while True: + hyp_batch = input('Select batch size (recommended power of 2). Continue with 64: ') + if not hyp_batch: + hyp_batch = 64 + break + elif hyp_batch.isdigit(): + hyp_batch = int(hyp_batch) + break + else: + print('\tError: Please introduce a number (must be integer)\n') + + while True: + hyp_epoch = input('Select training epochs. Continue with 100: ') + if not hyp_epoch: + hyp_epoch = 100 + break + elif hyp_epoch.isdigit(): + hyp_epoch = int(hyp_epoch) + break + else: + print('\tError: Please introduce a number (must be integer)\n') + + while True: + test_prop = input('Select test data percentage (0-1). (Recommended values >= 0.2). Continue with 0.20: ') + if not test_prop: + test_prop = 0.2 + break + elif is_float(test_prop): + test_prop = float(test_prop) + break + else: + print('\tError: Please select a number\n') + + while True: + encoding_dim = input('Select autoencoder dimensions. Continue with 10: ') + if not encoding_dim: + encoding_dim = 10 + break + elif encoding_dim.isdigit(): + encoding_dim = int(encoding_dim) + break + else: + print('\tError: Please introduce a number (must be integer)\n') + + print('\n-----------------------------') + print('Model Parameters summary:\n') + print(f'Batch Size: {hyp_batch}') + print(f'Training Epochs: {hyp_epoch}') + print(f'Test split: {test_prop}') + print(f'Autoencoder dimensions: {encoding_dim}') + + print('\n-----------------------------') + + ## Decisions + print('Outputs: \n') + + filen = input('Enter file name to save the outputs or continue with default file name: ') + if not filen: + filen = f'{timestr}_AE_solution' + else: + filen = f'{filen}' + + # Create new folder: + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f'{path0}/{filen}') + + # Save mat + folder_save = f'{path0}/{filen}' + file_savemat = "AE_output.mat" + + while True: + dec1 = input('Would you like to plot the reconstruction vs the original data? (y/n). Continue with Yes: ') + if not dec1 or dec1.strip().lower() in ['y', 'yes']: + decision1 = True + break + if dec1.strip().lower() in ['n', 'no']: + decision1 = False + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + dec2 = input(f'Would you like to plot the modes? (y/n). Continue with Yes: ') + if not dec2 or dec2.strip().lower() in ['y', 'yes']: + decision2 = True + dec2_save = input(f'Would you like to save the mode plots? (y/n). Continue with No: ') + if not dec2_save or dec2_save.strip().lower() in ['n', 'no']: + decision2_save = False + break + elif dec2_save.strip().lower() in ['y', 'yes']: + decision2_save = True + break + else: + print('\tError: Select yes or no (y/n)\n') + elif dec2.strip().lower() in ['n', 'no']: + decision2 = False + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + dec5 = input(f'Would you like to plot the temporal coefficient of the modes? (y/n). Continue with Yes: ') + if not dec5 or dec5.strip().lower() in ['y', 'yes']: + decision5 = True + dec5_save = input(f'Would you like to save the temporal coefficient plots? (y/n). Continue with No: ') + if not dec5_save or dec5_save.strip().lower() in ['n', 'no']: + decision5_save = False + break + elif dec5_save.strip().lower() in ['y', 'yes']: + decision5_save = True + break + else: + print('\tError: Select yes or no (y/n)\n') + elif dec5.strip().lower() in ['n', 'no']: + decision5 = False + break + else: + print('\tError: Select yes or no (y/n)\n') + + + while True: + dec3 = input(f'Would you like to save the output to a .mat file? (y/n). Continue with Yes: ') + if not dec3 or dec3.strip().lower() in ['y', 'yes']: + decision3 = True + break + elif dec3.strip().lower() in ['n', 'no']: + decision3 = False + break + else: + print('\tError: Select yes or no (y/n)\n') + + print('\n') + + RedStep=1 + tensor = tensor[:,0::RedStep,0::RedStep,0::RedStep] + ncomp, ny, nx, ntt = tensor.shape + + min_val=np.array(2) + + ## scale between [0,1] + min_val=np.zeros(ncomp,); max_val=np.zeros(ncomp,); range_val=np.zeros(ncomp,); std_val=np.zeros(ncomp,) + + tensor_norm=np.zeros(tensor.shape) + + for j in range(ncomp): + min_val [j] = np.amin(tensor[j,:,:,:]) + max_val [j] = np.amax(tensor[j,:,:,:]) + range_val [j] = np.ptp(tensor[j,:,:,:]) + std_val [j] =np.std(tensor[j,:,:,:]) + tensor_norm[j,...] = (tensor[j,...]-min_val[j])/range_val[j] + + # # ***AUTOENCODERS*** + # 3. Perform the ANN + tf.random.set_seed(221) ## Remove this to experience the randomness!!!! + keras.backend.clear_session() + + nxy2=ny*nx*ncomp + TT=tensor_norm.transpose((3,1,2,0)) + ntt, ny, nx, ncomp = TT.shape + + if type_AE ==1: + dim=nt + X_scale=np.reshape(TT,(dim,nxy2),order='F') + X_scale=X_scale.transpose((1,0)) + + elif type_AE ==2: + dim=nxy2 + X_scale=np.reshape(TT,(ntt,nxy2),order='F') + + + input_img = Input(shape=(dim,)) + encoded = Dense(encoding_dim, activation='linear')(input_img) + decoded = Dense(dim, activation='linear')(encoded) + autoencoder = Model(input_img, decoded) + encoder = Model(input_img, encoded) + decoder = Model(encoded, decoded) + + # We compile the autoencoder + autoencoder.compile(optimizer='adam', loss='mse') + # Get a summary + print('Model summary\n') + autoencoder.summary() + + # We do the splitting into test and train sets + from sklearn.model_selection import train_test_split + x_train, x_test, y_train, y_test = train_test_split(X_scale, + X_scale, + test_size=test_prop) + + # CALLBACK : Early Stoping + callback = tf.keras.callbacks.EarlyStopping(monitor='loss', patience=5, min_delta = 0.001) + print('\nTraining Model Please Wait...\n') + t0 = time.time() + History=autoencoder.fit(x_train, x_train, + epochs=hyp_epoch, + batch_size=hyp_batch, + callbacks = [callback], + shuffle=True, + validation_data=(x_test, x_test)) + + t1 = time.time() + + # get convergence history + loss_linlin=History.history['loss'] + loss_v=History.history['val_loss'] + + print(f"\nTraining complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes\n") + # Prediction of the encoding/decoding + + print('Model predicting. Please wait...\n') + t0 = time.time() + + z=encoder.predict(X_scale) + x_tilde=autoencoder.predict(X_scale) + + t1 = time.time() + + print(f"\nPrediction complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + #Check error + + Err=np.linalg.norm(x_tilde-X_scale)/np.linalg.norm(X_scale) + print('\nNeural Network RRMSE with all modes: '+str(np.round(Err*100, 3))+'%\n') + + # ###### Plot RRMSE when reconstructing from mode 1 to nm + + rrmse_ =np.zeros((z.shape[1],)) + contrib=np.zeros((z.shape[1],)) + for nm in range(0,encoding_dim): + + z1=np.zeros(z.shape) + z1[:,0:nm] = z[:,0:nm] + + xx = decoder.predict(z1) + rrmse = RRMSE(X_scale,xx) + print('Adding mode: '+ str(nm+1) + ' - Updated Neural Network RRMSE: '+str(np.round(rrmse*100, 3))+'%\n') + rrmse_[nm]=rrmse + + print(f'\nATTENTION!: All plots will be saved to {path0}/{filen}\n') + print('Please CLOSE all figures to continue the run\n') + + fig, ax = plt.subplots(figsize=(6, 4), num = 'CLOSE TO CONTINUE RUN - Reconstruction RRMSE per added mode') # This creates the figure + plt.plot(np.arange(0,encoding_dim)+1,rrmse_*100) + plt.scatter(np.arange(0,encoding_dim)+1,rrmse_*100) + plt.title('Reconstruction RRMSE per added mode') + plt.xlabel('Mode number added') + plt.ylabel('RRMSE (%)') + fig.tight_layout() + plt.savefig(f'{path0}/{filen}/RRMSE_training.png') + plt.show() + plt.close() + + + # ###### Plot the relative RRMSE when eliminating each mode + + incr_rr_=np.zeros((encoding_dim,)) + for idel in range(0,encoding_dim): + array=np.arange(0,encoding_dim) + array=np.delete(array,idel) + + z1=np.zeros(z.shape) + z1[:,array]=z[:,array] + xx = decoder.predict(z1) + rrmse = RRMSE(X_scale,xx) + + incr_rr = rrmse- RRMSE(X_scale,x_tilde) + incr_rr_[idel]=incr_rr + print('Eliminated mode: '+str(idel+1)+' - New Neural Network RRMSE: '+str(np.round(rrmse*100, 3))+'% - Neural Network RRMSE increase (compared to RRMSE with all modes): '+str(np.round(incr_rr*100, 3)) + '%\n') + + fig, ax = plt.subplots(figsize=(6, 4), num = 'Neural Network RRMSE after eliminating mode "n"') # This creates the figure + plt.xlabel('Eliminated mode "n"') + plt.ylabel('RRMSE increase (%)') + plt.title('Neural Network RRMSE after eliminating mode "n"') + plt.plot(np.arange(1,encoding_dim+1),incr_rr_*100) + plt.scatter(np.arange(1,encoding_dim+1),incr_rr_*100) + fig.tight_layout() + plt.savefig(f'{path0}/{filen}/RRMSE_after_mode_elimination.png') + plt.show() + plt.close() + + ## indexes for the sorting + + I = np.argsort(incr_rr_) + modes_sorted = np.flip(I) + + ## modes + z_sort = z[:,modes_sorted] + + lim=int(dim/ncomp) + RR=x_tilde[:,0:dim:1] + + + if type_AE ==1: + ntt= x_tilde.shape[1] + RR=x_tilde[:,0:dim:1] + RR = np.transpose(RR,(1,0)) + ZZT= np.reshape(RR,(ntt,ny,nx,ncomp),order='F') + ZZT= np.transpose(ZZT,(3,1,2,0)) + + elif type_AE ==2: + ntt= x_tilde.shape[0] + RR=x_tilde[:,0:dim:1] + ntt= x_tilde.shape[0] + ZZT=np.reshape(RR,(ntt,ny,nx,ncomp),order='F') + ZZT= np.transpose(ZZT,(3,1,2,0)) + + + # ## Decisions + print('ATTENTION! The run will not continue until all figure windows are closed') + print('Recommendation: Close figures and check folder\n') + + if not os.path.exists(f'{path0}/{filen}/plots'): + os.mkdir(f'{path0}/{filen}/plots') + + titles = [] + [titles.append(f'Component {i+1}') for i in range(ncomp)] + titles_ = [] + [titles_.append(f'comp_{i+1}') for i in range(ncomp)] + + if decision1 == True: + while True: + while True: + compp = input(f'Select component to plot (must be lower than {ncomp}). Continue with 1: ') + if not compp : + iv = 0 + break + elif compp.isdigit(): + if int(compp) <= ZZT.shape[0]: + iv = int(compp) - 1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + while True: + timee = input(f'Select the snapshot to represent (must be lower than {ZZT.shape[-1]}). Continue with time snapshot 1: ') + if not timee: + it = 0 + break + elif timee.isdigit(): + if int(timee) <= ZZT.shape[-1]: + it = int(timee) - 1 + break + else: + print(f'\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + + # CONTOUR AUTOENCODER-- CHECK RECONSTRUCTION + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - Real Data vs Reconstructed Data') + fig.suptitle(f'Real vs Reconst. data: {titles[iv]} - snapshot: {it + 1}') + ax[0].contourf(tensor[iv,:,:,it]) + ax[0].set_title(f'Real data') + ax[1].contourf(ZZT[iv,:,:,it]) + ax[1].set_title(f'Reconstructed data') + fig.tight_layout() + plt.savefig(f'{path0}/{filen}/plots/RealReconst_data_snapshot_{it+1}_{titles_[iv]}.png') + plt.show() + plt.close() + while True: + Resp = input('Do you want to plot other figures? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Please select yes or no (y/n)\n') + Resp = 1 + if Resp == 0: + break + + while True: + opt = input(f'Select the number of snapshots to save. Continue with {int(ZZT.shape[-1])}: ') + if not opt: + n_time = ZZT.shape[-1] + break + elif opt.isdigit(): + if int(opt) <= int(ZZT.shape[-1]): + n_time = int(opt) + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + if int(opt) != 0: + if not os.path.exists(f'{path0}/{filen}/snapshots'): + os.mkdir(f'{path0}/{filen}/snapshots') + + print('\n') + print(f'Saving snapshots to {path0}/{filen}/snapshots') + for iv in range(ZZT.shape[0]): + print(f'\nComponent: {iv+1}') + for it in range(n_time): + print(f'\tSnapshot: {it+1}') + fig, ax = plt.subplots() + ax.contourf(ZZT[iv,:, :, it]) + ax.set_title(f'Reconstructed data - Component {iv+1} - snapshot {it+1}') + fig.tight_layout() + plt.savefig(f'{path0}/{filen}/snapshots/Reconst_data_snap_{it+1}_{titles_[iv]}.png') + plt.close(fig) + + print('\nAll snapshot plots have been saved\n') + + if not os.path.exists(f'{path0}/{filen}/autoencoder_dims'): + os.mkdir(f'{path0}/{filen}/autoencoder_dims') + + if decision2 == True: + while True: + AEnum = input(f'Select number of modes to plot (max. is the number of encoder dimensions: {encoding_dim}). Continue with 2: ') + if not AEnum: + AEnum = 2 + break + elif AEnum.isdigit(): + AEnum = int(AEnum) + break + else: + print('\tError: Please introduce a valid number (must be integer)\n') + + for AEnum in range(min(AEnum,encoding_dim)): + + if type_AE ==1: + #SET AUTOENCODER TO PLOT + MODE=np.transpose(z_sort[:,AEnum]) + AE=MODE[0:int(nx*ny*ncomp)] + Rec_AE=np.reshape(AE,(ny,nx,ncomp),order='F') + + elif type_AE ==2: + MODE=np.transpose(z_sort[:,AEnum]) + RECONST_AUTO=np.matmul(MODE, X_scale) + AE = RECONST_AUTO[0:int(nx*ny*ncomp)] + Rec_AE=np.reshape(AE,(ny,nx,ncomp),order='F') + + + for comp in range(Rec_AE.shape[-1]): + fig, ax = plt.subplots(figsize=(8, 4), num = f'CLOSE TO CONTINUE RUN - Mode {AEnum+1} - component {comp}') + plt.contourf(Rec_AE[:,:,comp]) + plt.title(f'Mode {AEnum+1} - component {comp+1}') + fig.tight_layout() + if decision2_save ==True: + plt.savefig(f'{path0}/{filen}/autoencoder_dims/mode_{AEnum+1}_comp_{comp}.png') + plt.show() + plt.close() + + ########## Save to .mat ########## + if decision3 == True: + + mdic = {"z": z,"z_sort": z_sort, "X_scale":X_scale, "nx":nx,"ny":ny,"ncomp":ncomp,"rrmse":rrmse_,"incr_rr":incr_rr_,"modes_sorted":modes_sorted} + + file_mat= str(f'{path0}/{filen}/' + file_savemat) + + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + titles = [] + [titles.append(f'Component {i+1}') for i in range(ZZT.shape[0])] + + if decision5 == True: + while True: + AEnum = input(f'Select number of modes to plot their temporal coefficient (max. is the number of encoder dimensions: {encoding_dim}). Continue with 2: ') + if not AEnum: + AEnum = 2 + break + elif AEnum.isdigit(): + AEnum = int(AEnum) + break + else: + print('\tError: Please introduce a valid number (must be integer)\n') + + for AEnum in range(min(AEnum,encoding_dim)): + + if type_AE ==1: + ss=np.transpose(z_sort[:,AEnum]) + coef_t = np.matmul(ss,X_scale) + + elif type_AE ==2: + ss=np.transpose(z_sort[:,AEnum]) + coef_t = ss + + + fig, ax = plt.subplots(figsize=(8, 4), num = f'CLOSE TO CONTINUE RUN - Mode {AEnum+1}') + plt.plot(coef_t) + plt.xlabel('Snapshots') + plt.ylabel('Amplitude') + plt.title(f'Temporal coefficient of Mode {AEnum+1}') + fig.tight_layout() + if decision5_save ==True: + plt.savefig(f'{path0}/{filen}/autoencoder_dims/coeft_mode_{AEnum+1}.png') + plt.show() + plt.close() + + + while True: + dec4 = input(f'Plot video of original data and reconstructed data? (y/n). Continue with Yes: ') + if not dec4 or dec4.strip().lower() in ['y', 'yes']: + decision4 = True + break + elif dec4.strip().lower() in ['n', 'no']: + decision4 = False + return + else: + print('\tError: Select yes or no (y/n)\n') + + + if decision4 == True: + while True: + vidvel = input(f'Select a component (max. is {ncomp}). Continue with 1: ') + if not vidvel or vidvel.strip().lower() == '1': + vel = 0 + video(tensor, vel, Title = f'Original Data - {titles[vel]}') + video(ZZT, vel, Title = f'Reconstructed data - {titles[vel]}') + + elif vidvel.isdigit(): + if int(vidvel) <= ncomp: + vel = int(vidvel) - 1 + video(tensor, vel, Title = f'Original Data - {titles[vel]}') + video(ZZT, vel, Title = f'Reconstructed data - {titles[vel]}') + else: + print('\tError: Selected component is out of bounds\n') + else: + print("\tError: Select a valid component\n") + + while True: + resp = input('Would you like to plot another component? (y/n). Continue with Yes: ') + if resp.strip().lower() in ['y', 'yes']: + break + elif resp.strip().lower() in ['n', 'no']: + return + else: + print('\tError: Select yes or no (y/n)\n') + + \ No newline at end of file diff --git a/v0.1_ModelFLOWs_app/DLsuperresolution.py b/v0.1_ModelFLOWs_app/DLsuperresolution.py new file mode 100644 index 0000000..86064a9 --- /dev/null +++ b/v0.1_ModelFLOWs_app/DLsuperresolution.py @@ -0,0 +1,1147 @@ +def DNNreconstruct(): + import numpy as np + import data_load + from sklearn.metrics import mean_squared_error, mean_absolute_error, median_absolute_error, r2_score, mean_absolute_percentage_error + import pandas as pd + import matplotlib.pyplot as plt + import time + import tensorflow as tf + from tensorflow.keras.layers import Dense, Reshape, Flatten + from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping + from tensorflow.keras.optimizers import Adam + from tensorflow.keras.models import Model + from tensorflow.keras.layers import Input + from tensorflow.keras.layers import Dense, Flatten + import scipy + import scipy.io + import hdf5storage + import os + os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' + from math import floor + + + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + tf.keras.backend.set_floatx('float64') + + pd.set_option('display.max_columns',100) + pd.set_option('display.max_rows',100) + + path0 = os.getcwd() + + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def downsampling(): + while True: + filetype = input('Select the downsampled input file format (.mat, .npy, .csv, .pkl, .h5): ') + print('\n\tWarning: This model can only be trained with 2-Dimensional or 3-Dimensional data (as in: (variables, nx, ny, time) or (variables, nx, ny, nz, time))\n') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + Tensor, _ = data_load.main(filetype) + + dim = Tensor.ndim + + if dim == 3: + var_sel=Tensor + ny_sel=Tensor.shape[0] + nx_sel=Tensor.shape[1] + elif dim >= 4: + if dim == 4: + if Tensor.shape[2] < Tensor.shape[1]: + Tensor = np.transpose(Tensor,(3,1,2,0)) + else: + Tensor = np.transpose(Tensor,(3,2,1,0)) + + if dim == 5: + Tensor = np.transpose(Tensor,(4,1,2,3,0)) + + var_sel=Tensor + ny_sel=Tensor.shape[1] + nx_sel=Tensor.shape[2] + + return var_sel, ny_sel, nx_sel + + def scale_val(x, min_val, max_val): + return((x - min_val)/(max_val - min_val)) + + + def descale_val(x, min_val, max_val): + return(x * (max_val - min_val) + min_val) + + + def custom_scale(tensor): + min_val = np.amin(tensor) + max_val = sum(np.amax(np.abs(tensor),axis=1)) + med_val = np.mean(tensor) + range_val = np.ptp(tensor) + std_val =np.std(tensor) + print('min_val/max_val=',min_val, max_val) + print('med_val=',med_val) + print('range_val=',range_val) + print('std_val=',std_val) + print(np.quantile(tensor.flatten(), np.arange(0,1,0.1))) + tensor_norm = XUds / max_val_XUds + print(np.quantile(tensor_norm.flatten(), np.arange(0,1,0.1))) + print(scipy.stats.describe(tensor_norm.flatten())) + + return tensor_norm + + def mean_absolute_percentage_error(y_true, y_pred): + epsilon = 1e-10 + y_true, y_pred = np.array(y_true), np.array(y_pred) + return np.mean(np.abs((y_true - y_pred) / np.maximum(epsilon,np.abs(y_true)))) * 100 + + def smape(A, F): + return ((100.0/len(A)) * np.sum(2 * np.abs(F - A) / (np.abs(A) + np.abs(F))+ np.finfo(float).eps)) + + def RRMSE (real, predicted): + RRMSE = np.linalg.norm(np.reshape(real-predicted,newshape=(np.size(real),1)),ord=2)/np.linalg.norm(np.reshape(real,newshape=(np.size(real),1))) + return RRMSE + + def error_tables(Xr_test, Xr_hat, Xr_sel,tabname1,tabname2, path0, filename, nvar): + results_table = pd.DataFrame(index=['MSE','MAE','MAD','R2','SMAPE','RRMSE', 'MAPE'],columns=['All']) + for i in range(1): + results_table.iloc[0,i] = mean_squared_error( Xr_test.flatten(), Xr_hat.flatten()) + results_table.iloc[1,i] = mean_absolute_error(Xr_test.flatten(), Xr_hat.flatten()) + results_table.iloc[2,i] = median_absolute_error( Xr_test.flatten(), Xr_hat.flatten()) + results_table.iloc[3,i] = r2_score(Xr_test.flatten(), Xr_hat.flatten()) + results_table.iloc[4,i] = smape( Xr_test.flatten(), Xr_hat.flatten()) + results_table.iloc[5,i] = RRMSE( np.reshape(Xr_test,(-1,1)), np.reshape(Xr_hat,(-1,1)))*100 + results_table.iloc[6,i] = mean_absolute_percentage_error( Xr_test.flatten(), Xr_hat.flatten()) + df1 = pd.DataFrame(results_table) + df1.to_csv(f'{path0}/{filename}/{tabname1}.csv') + + num_layers = Xr_sel.shape[3] + + cols = [] + for i in range(1, nvar + 1): + cols.append(f'var{i}') + + results_table2 = pd.DataFrame(index=['MSE','MAE','MAD','R2','SMAPE','RRMSE','MAPE'],columns=cols) + for i in range(num_layers): + results_table2.iloc[0,i] = mean_squared_error( Xr_test[...,i].flatten(), Xr_hat[...,i].flatten()) + results_table2.iloc[1,i] = mean_absolute_error(Xr_test[...,i].flatten(), Xr_hat[...,i].flatten()) + results_table2.iloc[2,i] = median_absolute_error( Xr_test[...,i].flatten(), Xr_hat[...,i].flatten()) + results_table2.iloc[3,i] = r2_score(Xr_test[...,i].flatten(), Xr_hat[...,i].flatten()) + results_table2.iloc[4,i] = smape( Xr_test[...,i].flatten(), Xr_hat[...,i].flatten()) + results_table2.iloc[5,i] = RRMSE( np.reshape(Xr_test[...,i],(-1,1)), np.reshape(Xr_hat[...,i],(-1,1)))*100 + results_table2.iloc[6,i] = mean_absolute_percentage_error( Xr_test[...,i].flatten(), Xr_hat[...,i].flatten()) + df2 = pd.DataFrame(results_table2) + df2.to_csv(f'{path0}/{filename}/{tabname2}.csv') + + + return results_table, results_table2 + + def figures_results_ylim(n_snap,var, yg, xg, y, x, Xr_test, Xr_sel, Xr_hat, figname): + fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(22, 5), num=f'CLOSE TO CONTINUE RUN - Snapshot comparison') + fig.tight_layout() + + im1 = ax[0].contourf(yg,xg,np.transpose(Xr_test[n_snap,...,var],(1,0)), + vmin=np.min(np.transpose(Xr_test[n_snap,...,var],(1,0))), + vmax=np.max(np.transpose(Xr_test[n_snap,...,var],(1,0)))) + #ax[0].set_ylim(bottom=0,top=16) + ax[0].set_axis_off() + ax[0].set_title('Real Data') + + im2 = ax[1].contourf(y,x,np.transpose(Xr_sel[n_snap,...,var],(1,0)), + vmin=np.min(np.transpose(Xr_test[n_snap,...,var],(1,0))), + vmax=np.max(np.transpose(Xr_test[n_snap,...,var],(1,0)))) + #ax[1].set_ylim(bottom=0,top=16) + ax[1].set_axis_off() + ax[1].set_title('Initial Data') + + im3 = ax[2].contourf(yg,xg,np.transpose(Xr_hat[n_snap,...,var],(1,0)), + vmin=np.min(np.transpose(Xr_test[n_snap,...,var],(1,0))), + vmax=np.max(np.transpose(Xr_test[n_snap,...,var],(1,0)))) + #ax[2].set_ylim(bottom=0,top=16) + ax[2].set_axis_off() + ax[2].set_title('Reconstruction') + + fig.subplots_adjust(right=0.8) + cbar_ax = fig.add_axes([0.85, 0.09, 0.01, 0.85]) + cbar = fig.colorbar(im1, cax = cbar_ax) + cbar.ax.tick_params(labelsize=24) + plt.savefig(figname) + plt.show() + + + def figures_results(n_snap, var, yg, xg, y, x, Xr_test, Xr_sel, Xr_hat, figname): + fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(22, 5)) + fig.tight_layout() + + im1 = ax[0].contourf(yg,xg,np.transpose(Xr_test[n_snap,...,var],(1,0)), + vmin=np.min(np.transpose(Xr_test[n_snap,...,var],(1,0))), + vmax=np.max(np.transpose(Xr_test[n_snap,...,var],(1,0)))) + ax[0].set_title('Real Data') + ax[0].set_axis_off() + + im2 = ax[1].contourf(y,x,np.transpose(Xr_sel[n_snap,...,var],(1,0)), + vmin=np.min(np.transpose(Xr_sel[n_snap,...,var],(1,0))), + vmax=np.max(np.transpose(Xr_sel[n_snap,...,var],(1,0)))) + ax[1].set_title('Initial Data') + ax[1].set_axis_off() + + im3 = ax[2].contourf(yg,xg,np.transpose(Xr_hat[n_snap,...,var],(1,0)), + vmin=np.min(np.transpose(Xr_hat[n_snap,...,var],(1,0))), + vmax=np.max(np.transpose(Xr_hat[n_snap,...,var],(1,0)))) + ax[2].set_title('Reconstruction') + ax[2].set_axis_off() + + fig.subplots_adjust(right=0.8) + cbar_ax = fig.add_axes([0.85, 0.09, 0.01, 0.85]) + cbar = fig.colorbar(im1, cax=cbar_ax) + cbar.ax.tick_params(labelsize=24) + plt.savefig(figname) + plt.close(fig) + + def videos_results(videoname, var, n_train, test_ind,xg, yg, x, y, Xr_test, Xr_hat, Xr_sel): + from matplotlib import animation + figure, ax = plt.subplots(nrows=1, ncols=3, figsize=(22, 5)) + figure.tight_layout() + + def animation_function(i): + figure.suptitle(f'Snapshot: {n_train+i}', fontsize=20) + im1 = ax[0].contourf(yg,xg,np.transpose(Xr_test[i,...,var],(1,0)), + vmin=np.min(np.transpose(Xr_test[0,...,var],(1,0))), + vmax=np.max(np.transpose(Xr_test[0,...,var],(1,0)))) + ax[0].set_axis_off() + + im2 = ax[1].contourf(y,x,np.transpose(Xr_sel[i,...,var],(1,0)), + vmin=np.min(np.transpose(Xr_test[0,...,var],(1,0))), + vmax=np.max(np.transpose(Xr_test[0,...,var],(1,0)))) + ax[1].set_axis_off() + + im3 = ax[2].contourf(yg,xg,np.transpose(Xr_hat[i,...,var],(1,0)), + vmin=np.min(np.transpose(Xr_test[0,...,var],(1,0))), + vmax=np.max(np.transpose(Xr_test[0,...,var],(1,0)))) + ax[2].set_axis_off() + + figure.subplots_adjust(right=0.8, top=0.9) + cbar_ax = figure.add_axes([0.85, 0.09, 0.01, 0.85]) + cbar = figure.colorbar(im1, cax=cbar_ax) + cbar.ax.tick_params(labelsize=20) + + + anim = animation.FuncAnimation(figure, animation_function, frames=test_ind.size, interval=1000, blit=False) + writergif = animation.PillowWriter(fps=5) + anim.save(videoname, writer=writergif) + + # Showing the video slows down the performance + + def custom_loss(y_actual,y_pred): + import tensorflow as tf + a = tf.norm((y_actual-y_pred),ord = 'euclidean') + b = tf.norm(y_actual, ord='euclidean') + custom_loss = a/b + + return custom_loss + + # Inputs + print('\nDeep Learning Data Enhancement Model') + print('------------------------') + print('Inputs: \n') + + [Xr_sel, ny_sel, nx_sel]=downsampling() + print('\nDownsampled Data Summary:\n') + print('Xr_sel: ', Xr_sel.shape) + print('ny_sel =', ny_sel) + print('nx_sel =', nx_sel) + print() + + while True: + filetype = input('Select the ground truth input file format (.mat, .npy, .csv, .pkl, .h5): ') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + Tensor, _ = data_load.main(filetype) + + dim = Tensor.ndim + + if dim == 4: + dims = 'Data2D' + if Tensor.shape[2] < Tensor.shape[1]: + Xr = np.transpose(Tensor,(3,1,2,0)) + else: + Xr = np.transpose(Tensor,(3,2,1,0)) + nt, ny, nx, nvar = Xr.shape + + print('\nData Summary:\n') + print('Data shape: ',Xr.shape) + print('nvar =',nvar,'ny =', ny, 'nx =', nx, 'nt =', nt) + print() + + if dim == 5: + dims = 'Data3D' + Xr = np.transpose(Tensor,(4,1,2,3,0)) + nt, ny, nx, nz, nvar = Xr.shape + + print('\nData Summary:\n') + print('Data shape: ',Xr.shape) + print('nvar =',nvar,'ny =', ny, 'nx =', nx, 'nz =', nz, 'nt =', nt) + print('\n') + + while True: + decision1 = input('Apply a first scaling to the input data? (y/n). Continue with No: ') + if not decision1 or decision1.strip().lower() in ['n', 'no']: + decision1='no-scaling' + break + elif decision1.strip().lower() in ['y', 'yes']: + decision1='scaling' + break + else: + print('\tError: Select yes or no (y/n)\n') + + if decision1 == 'scaling': + while True: + decision2 = input('Output scaling graphs? (y/n). Continue with No: ') + if not decision2 or decision2.strip().lower() in ['n', 'no']: + decision2 = 'no' + break + elif decision2.strip().lower() in ['y', 'yes']: + decision2 = 'yes' + break + else: + print('\tError: Select yes or no (y/n)\n') + else: + decision2 = 'no' + + while True: + decision3 = input('Apply a second scaling to the input data? (y/n). Continue with No: ') + if not decision3 or decision3.strip().lower() in ['n', 'no']: + decision3='no' + break + elif decision3.strip().lower() in ['y', 'yes']: + decision3='yes' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + decision4 = input('Shuffle train and test data? (y/n). Continue with No: ') + if not decision4 or decision4.strip().lower() in ['n', 'no']: + decision4='no-shuffle' + break + elif decision4.strip().lower() in ['y', 'yes']: + decision4 = 'shuffle' + break + else: + print('\tError: Select yes or no (y/n)\n') + + print('\n-----------------------------') + print('Model Configuration: \n') + + while True: + hyper = input('Use optimal hyperparameters? (y/n). Continue with No: ') + if not hyper or hyper.strip().lower() in ['n', 'no']: + hyper = 'no' + break + elif hyper.strip().lower() in ['y', 'yes']: + hyper='yes' + break + else: + print('\tError: Select yes or no (y/n)\n') + + if hyper == 'yes': + print(''' +Available hyperparameter tuners: +1) RandomSearch: All the hyperparameter combinations are chosen randomly. +2) Hyperband: Randomly sample all the combinations of hyperparameter and train the model for few epochs with the combinations, selecting the best candidates based on the results. +3) BayesianOptimization: Chooses first few combinations randomly, then based on the performance on these hyperparameters it chooses the next best possible hyperparameters. + ''') + while True: + tuner_ = input('Select a hyperparameter tuner (1/2/3). Continue with RandomSearch: ') + if not tuner_ or tuner_ == '1': + tuner_ = 'RandomSearch' + break + elif tuner_ == '2': + tuner_ = 'Hyperband' + break + elif tuner_ == '3': + tuner_ = 'Bayesian' + break + else: + print('\tError: Select a valid tuner\n') + + while True: + bs = input('Select batch size (recommended power of 2). Continue with 16: ') + if not bs: + bs = 16 + break + if bs.isdigit(): + bs = int(bs) + break + else: + print('\tError: Select a valid number format (must be integer)\n') + + if hyper=='yes': + pass + + elif hyper == 'no': + + while True: + neurons = input('Select the number of neurons per layer. Continue with 100: ') + if not neurons: + neurons = 100 + break + elif neurons.isdigit(): + neurons = int(neurons) + break + else: + print('\tError: Select a valid number format (must be integer)\n') + + while True: + act_func = input('Select hidden layer activation function (relu, elu, softmax, sigmoid, tanh, linear). Continue with relu: ') + if not act_func or act_func.strip().lower() == 'relu': + act_func = 'relu' + break + elif act_func.strip().lower() == 'elu': + act_func = 'elu' + break + elif act_func.strip().lower() == 'softmax': + act_func = 'softmax' + break + elif act_func.strip().lower() == 'sigmoid': + act_func = 'sigmoid' + break + elif act_func.strip().lower() == 'tanh': + act_func = 'tanh' + break + elif act_func.strip().lower() == 'linear': + act_func = 'linear' + break + else: + print('\tError: Please select a valid option\n') + + while True: + act_fun1 = input('Select output layer activation function (tanh, relu, elu, linear). Continue with relu: ') + if not act_fun1 or act_fun1.strip().lower() == 'relu': + act_fun1 = 'relu' + break + elif act_fun1.strip().lower() == 'tanh': + act_fun1 = 'tanh' + break + elif act_fun1.strip().lower() == 'elu': + act_fun1 = 'elu' + break + elif act_fun1.strip().lower() == 'linear': + act_fun1 = 'linear' + break + else: + print('\tError: Please select a valid option\n') + + while True: + learn_rate = input('Select the model learning rate. Continue with 1e-3: ') + if not learn_rate: + learn_rate=0.001 + break + elif is_float(learn_rate): + learn_rate = float(learn_rate) + break + else: + print('\tError: Please select a number\n') + + while True: + lossf = input('Select a loss function ("mse", "custom"). Continue with mse: ') + if not lossf or lossf.strip().lower() == 'mse': + lossf = 'mse' + break + elif lossf.lower().strip() == 'custom': + lossf='custom' + break + else: + print('\tError: Select a valid option\n') + + if lossf=='custom': + loss_function=custom_loss + if lossf=='mse': + loss_function='mse' + + print('\n-----------------------------') + print(f''' +HYPERPARAMETERS SUMMARY:\n +Hidden Layer activation function: {act_func} +Output Layer activation function: {act_fun1} +Batch size: {bs} +Number of neurons: {neurons} +Learning rate: {learn_rate} +Loss function: {lossf} + ''') + + print('-----------------------------') + print('Training configuration: \n') + while True: + n_train = input(f'Select the number of samples for the training data. Continue with {round(int(nt)*0.8)} samples (80% aprox. RECOMMENDED): ') + if not n_train: + n_train = round(int(nt)*0.8) + break + elif n_train.isdigit(): + n_train = int(n_train) + break + else: + print('\tError: Select a valid number format (must be integer)') + + while True: + epoch = input('Select the number of training epoch. Continue with 500: ') + if not epoch: + epoch = 500 + break + elif epoch.isdigit(): + epoch = int(epoch) + break + else: + print('\tError: Select a valid number format (must be integer)') + + print('\n-----------------------------') + print('Outputs: \n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_DL_superresolution' + else: + filen = f'{filen}' + + while True: + decision5 = input('Would you like to save the results? (y/n). Continue with Yes: ') + if not decision5 or decision5.strip().lower() in ['y', 'yes']: + decision5='yes' + break + elif decision5.strip().lower() in ['n', 'no']: + decision5 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + + while True: + decision7 = input('Plot data comparison between the reconstruction, initial data, and ground truth? (y/n). Continue with Yes: ') + if not decision7 or decision7.strip().lower() in ['y', 'yes']: + decision7='yes' + break + elif decision7.strip().lower() in ['n', 'no']: + decision7 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + decisionx = input('Create comparison videos of the test data versus the predicted data? (y/n). Continue with No: ') + if not decisionx or decisionx.strip().lower() in ['n', 'no']: + decisionx='no' + break + elif decisionx.strip().lower() in ['y', 'yes']: + decisionx = 'yes' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + decision_20 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not decision_20 or decision_20.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f'{path0}/{filen}') + + filename1 = f'{decision1}_{decision4}' + + if not os.path.exists(f'{path0}/{filen}/{filename1}'): + os.mkdir(f'{path0}/{filen}/{filename1}') + + filename = f'{filen}/{filename1}' + + if Xr.ndim==3: + nt=100 + Xr= np.repeat(Xr[np.newaxis,...],nt, axis=0) + Xr_sel= np.repeat(Xr_sel[np.newaxis,...],nt, axis=0) + print('Xr: ',Xr.shape) + print('Xr_sel: ',Xr_sel.shape) + + if dims=='Data3D': + Xr_sel=np.reshape(Xr_sel,(nt,ny_sel,nx_sel*nz,nvar),order='F') + Xr=np.reshape(Xr,(nt,ny,nx*nz,nvar),order='F') + print('Xr_sel: ',Xr_sel.shape) + + # SCALING + + if decision1=='scaling': + print('Scaling Data') + var_sel_min = [] + var_sel_max = [] + for i in range(nvar): + var_sel_min.append(np.min(Xr_sel[...,i])) + var_sel_max.append(np.max(Xr_sel[...,i])) + print(f'var{i+1}_sel_min: {var_sel_min[i]}, var{i+1}_sel_max: {var_sel_max[i]}') + + var_sel_min = np.array(var_sel_min) + var_sel_max = np.array(var_sel_max) + + var_sel_sc = [] + for i in range(nvar): + var_sel_sc.append(np.array(scale_val(Xr_sel[...,i], var_sel_min[i], var_sel_max[i]))) + print(f'var{i+1}_sel_sc shape: {np.array(var_sel_sc[i]).shape}') + + Xr_sel_sc = np.stack(var_sel_sc, axis=3) + print('var_sel_sc shape: ', Xr_sel_sc.shape) + + var_min = [] + var_max = [] + for i in range(nvar): + var_min.append(np.min(Xr[...,i])) + var_max.append(np.max(Xr[...,i])) + print(f'var{i+1}_min: {var_min[i]}, var{i+1}_max: {var_max[i]}') + + var_min = np.array(var_min) + var_max = np.array(var_max) + + var_sc = [] + for i in range(nvar): + var_sc.append(scale_val(Xr[...,i], var_min[i], var_max[i])) + print(f'var{i+1}_sel_sc shape: ',var_sc[i].shape) + + var_sc = np.array(var_sc) + Xr_sc = np.stack(var_sc, axis=3) + print('var_sel_sc shape: ', Xr_sc.shape) + print('\nData Scaled') + + else: + None + + print('\nPlease CLOSE ALL FIGURES to continue the run\n') + + if decision2 == 'yes': + plt.subplots(num=f'CLOSE TO CONTINUE RUN') + plt.title('Ground truth data distribution') + plt.hist(Xr.flatten(), bins='auto') + plt.ylim(0,2e5) + plt.xlim(-5,5) + plt.tight_layout() + plt.show() + + plt.subplots(num=f'CLOSE TO CONTINUE RUN') + plt.title('Scaled downsampled data distribution') + plt.hist(Xr_sc.flatten(), bins='auto') + plt.tight_layout() + plt.show() + + # SVD + print('Performing SVD') + if 'nz' in locals(): + XUds=np.zeros([nt,ny_sel,ny_sel,nvar]) + XSds=np.zeros([nt,ny_sel,ny_sel,nvar]) + XVds=np.zeros([nt,ny_sel,nx_sel*nz,nvar]) + else: + XUds=np.zeros([nt,ny_sel,nx_sel,nvar]) + XSds=np.zeros([nt,nx_sel,nx_sel,nvar]) + XVds=np.zeros([nt,nx_sel,nx_sel,nvar]) + + if 'Xr_sel_sc' in locals(): + for i in range(np.size(Xr_sel_sc,0)): + U_var_sel_sc = [] + S_var_sel_sc = [] + V_var_sel_sc = [] + for j in range(nvar): + + U, S, V = np.linalg.svd(Xr_sel_sc[i,...,j], full_matrices=False) + U_var_sel_sc.append(U) + S_var_sel_sc.append(np.diag(S)) + V_var_sel_sc.append(V) + + XUds_= np.stack(U_var_sel_sc, axis=-1) + XSds_= np.stack(S_var_sel_sc, axis=-1) + XVds_= np.stack(V_var_sel_sc, axis=-1) + + XUds[i,...]=XUds_ + XSds[i,...]=XSds_ + XVds[i,...]=XVds_ + + print('\nSVD Summary:') + print('XUds: ',XUds.shape) + print('XSds: ',XSds.shape) + print('XVds: ',XVds.shape) + print('\n') + + else: + for i in range(np.size(Xr_sel,0)): + U_var_sel = [] + S_var_sel = [] + V_var_sel = [] + for j in range(nvar): + + U, S, V = np.linalg.svd(Xr_sel[i,...,j], full_matrices=False) + U_var_sel.append(U) + S_var_sel.append(np.diag(S)) + V_var_sel.append(V) + + XUds_= np.stack(U_var_sel, axis=-1) + XSds_= np.stack(S_var_sel, axis=-1) + XVds_= np.stack(V_var_sel, axis=-1) + + XUds[i,...]=XUds_ + XSds[i,...]=XSds_ + XVds[i,...]=XVds_ + + print('\nSVD Summary:') + print('XUds: ',XUds.shape) + print('XSds: ',XSds.shape) + print('XVds: ',XVds.shape) + print('\n') + + print('SVD Complete\n') + + plt.subplots(num=f'CLOSE TO CONTINUE RUN') + plt.hist(XUds.flatten(), bins='auto') + plt.title('Left singular vectors data distribution') + plt.tight_layout() + plt.show() + plt.close() + + plt.subplots(num=f'CLOSE TO CONTINUE RUN') + plt.hist(XVds.flatten(), bins='auto') + plt.title('Right singular vectors data distribution') + plt.tight_layout() + plt.show() + plt.close() + + ind=np.linspace(0,nt-1,nt,dtype=int) + + if decision4=='shuffle': + np.random.shuffle(ind) + ind + + train_ind = ind[0:n_train] + test_ind = ind[n_train:] + + if 'Xr_sel_sc' in locals(): + Xr_train = Xr_sc[train_ind] + Xr_test = Xr_sc[test_ind] + else: + Xr_train = Xr[train_ind] + Xr_test = Xr[test_ind] + + XUds_train = XUds[train_ind] + XUds_test = XUds[test_ind] + XSds_train = XSds[train_ind] + XSds_test = XSds[test_ind] + XVds_train = XVds[train_ind] + XVds_test = XVds[test_ind] + print('\nTrain-test split summary: \n') + print('Xr_train: ',Xr_train.shape) + print('Xr_test: ',Xr_test.shape) + print('XUds_train: ',XUds_train.shape) + print('XUds_test: ',XUds_test.shape) + print('XSds_train: ',XSds_train.shape) + print('XSds_test: ',XSds_test.shape) + print('XVds_train: ',XVds_train.shape) + print('XVds_test: ',XVds_test.shape) + print('\n') + + np.product(Xr_train.shape)+np.product(Xr_test.shape) + + if not os.path.exists(f"{path0}/{filename}/weights"): + os.makedirs(f"{path0}/{filename}/weights") + + file_name = f"{path0}/{filename}/weights/Interp_dense_NN_v1.0" + + save_best_weights = file_name + '_best.h5' + save_last_weights = file_name + '_last.h5' + save_summary_stats = file_name + '.csv' + + # Model inputs + in_U_dim = XUds.shape[1:] + in_S_dim = XSds.shape[1:] + in_V_dim = XVds.shape[1:] + out_dim = Xr_train.shape[1:] + + # Neural Network construction + + if hyper == 'no': + + def create_model_1(in_U_dim, in_S_dim, in_V_dim, out_dim): + in_U = Input(shape=(*in_U_dim,),name='in_u') + in_S = Input(shape=(*in_S_dim,),name='in_s') + in_V = Input(shape=(*in_V_dim,),name='in_v') + + u = Flatten(name='u_1')(in_U) + u = Dense (neurons, activation=act_func, name='u_2')(u) + XUus = Dense(out_dim[0]*in_U_dim[1]*in_U_dim[2],activation=act_fun1,name='u_3')(u) + + v = Flatten(name='v_1')(in_V) + v = Dense (neurons, activation=act_func, name='v_2')(v) + XVus = Dense(in_V_dim[0]*out_dim[1]*in_V_dim[2],activation=act_fun1,name='v_3')(v) + + XUus_reshape = Reshape((out_dim[0],in_U_dim[1],in_U_dim[2]),name='reshape_u')(XUus) + XVus_reshape = Reshape((in_V_dim[0],out_dim[1],in_V_dim[2]),name='reshape_v')(XVus) + + X_hat = tf.einsum('ijkl,iknl,inpl->ijpl', XUus_reshape, in_S, XVus_reshape) + + m = Model(inputs=[in_U,in_S,in_V],outputs= X_hat) + # m_upsampled_matrices = Model(inputs=[in_U,in_S,in_V],outputs= [XUus_reshape,XVus_reshape]) + + m.compile(loss=loss_function, optimizer=Adam(learn_rate), metrics=['mse']) + + # return(m, m_upsampled_matrices) + return m + + # model, model_upsampled_matrices = create_model_1(in_U_dim, in_S_dim, in_V_dim, out_dim) + model = create_model_1(in_U_dim, in_S_dim, in_V_dim, out_dim) + + print('Model Summary:\n') + model.summary() + + #print('\nUnsampled Matrices Model Summary:\n') + #model_upsampled_matrices.summary() + + if hyper == 'yes': + import keras_tuner as kt + def create_model_hp(hp): + hp_activation = hp.Choice('hidden_layer_activation_function', values = ['relu', 'linear', 'tanh', 'elu', 'sigmoid']) + hp_neurons = hp.Int('hp_neurons', min_value = 10, max_value = 100, step = 10) + hp_activation_1 = hp.Choice('output_layer_activation_function', values = ['relu', 'linear', 'tanh', 'elu', 'sigmoid']) + hp_learning_rate = hp.Choice('learning_rate', values=[1e-2, 1e-3, 5e-3, 1e-4]) + + in_U = Input(shape=(*in_U_dim,),name='in_u') + in_S = Input(shape=(*in_S_dim,),name='in_s') + in_V = Input(shape=(*in_V_dim,),name='in_v') + + u = Flatten(name='u_1')(in_U) + u = Dense (hp_neurons, activation=hp_activation, name='u_2')(u) + XUus = Dense(out_dim[0]*in_U_dim[1]*in_U_dim[2],activation=hp_activation_1,name='u_3')(u) + + v = Flatten(name='v_1')(in_V) + v = Dense (hp_neurons, activation=hp_activation, name='v_2')(v) + XVus = Dense(in_V_dim[0]*out_dim[1]*in_V_dim[2],activation=hp_activation_1,name='v_3')(v) + + XUus_reshape = Reshape((out_dim[0],in_U_dim[1],in_U_dim[2]),name='reshape_u')(XUus) + XVus_reshape = Reshape((in_V_dim[0],out_dim[1],in_V_dim[2]),name='reshape_v')(XVus) + + X_hat = tf.einsum('ijkl,iknl,inpl->ijpl', XUus_reshape, in_S, XVus_reshape) + + m = Model(inputs=[in_U,in_S,in_V],outputs= X_hat) + + m.compile(loss='mse', optimizer=Adam(hp_learning_rate), metrics=['mse']) + + return m + + if tuner_ == 'Hyperband': + tuner = kt.Hyperband(create_model_hp, objective = 'val_loss', max_epochs = 10, factor = 3, directory = 'dir_1', project_name = 'x', overwrite = True) + + elif tuner_ == 'RandomSearch': + tuner = kt.RandomSearch(create_model_hp, objective = 'val_loss', max_trials = 10, directory = 'dir_1', project_name = 'x', overwrite = True) + + elif tuner_ == 'Bayesian': + tuner = kt.BayesianOptimization(create_model_hp, objective = 'val_loss', max_trials = 10, beta = 3, directory = 'dir_1', project_name = 'x', overwrite = True) + + stop_early = tf.keras.callbacks.EarlyStopping(monitor = 'val_loss', patience = 10) + + print('\nSearching for optimal hyperparameters...\n') + + tuner.search([XUds_train,XSds_train,XVds_train], + Xr_train, + batch_size=bs, + epochs=50, + validation_split=0.15, + verbose=1, + shuffle=True, + callbacks=[stop_early]) + + best_hps = tuner.get_best_hyperparameters(num_trials = 1)[0] + + print('\n-----------------------------') + print(f''' +HYPERPARAMETERS SUMMARY:\n +Hidden Layer activation function: {best_hps.get('hidden_layer_activation_function')} +Output Layer activation function: {best_hps.get('output_layer_activation_function')} +Number of neurons: {best_hps.get('hp_neurons')} +Learning rate: {best_hps.get('learning_rate')} +Loss function: 'mse' + ''') + + model = tuner.hypermodel.build(best_hps) + + print('Model Summary:\n') + model.summary() + + # Model training + + print('\nTraining Model Please Wait...\n') + + t0 = time.time() + callbacks = [ModelCheckpoint(save_best_weights, monitor='val_loss', save_best_only=True, mode='auto'), + EarlyStopping(monitor='val_loss', patience=3, verbose=1, mode='auto')] + + history = model.fit([XUds_train,XSds_train,XVds_train],Xr_train, + batch_size=bs, + epochs=epoch, + validation_split=0.15, + verbose=1, + shuffle=True, + initial_epoch = 0, + callbacks=callbacks) + + t1 = time.time() + + print('\nModel Trained Successfully!') + print(f"\nTraining complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + # Training stats + + summary_stats = pd.DataFrame({'epoch': [ i + 1 for i in history.epoch ], + 'train_acc': history.history['mse'], + 'valid_acc': history.history['val_mse'], + 'train_loss': history.history['loss'], + 'valid_loss': history.history['val_loss']}) + + summary_stats.to_csv(save_summary_stats) + + print(f'\nATTENTION!: All plots will be saved to {path0}/{filename}\n') + print('Please CLOSE all figures to continue the run\n') + + plt.subplots(num=f'CLOSE TO CONTINUE RUN - Loss function evolution') + plt.yscale("log") + plt.title('Training vs. Validation loss') + plt.plot(summary_stats.train_loss, 'b', label = 'Train loss') + plt.plot(summary_stats.valid_loss, 'g', label = 'Valid. loss') + plt.legend(loc = 'upper right') + plt.tight_layout() + plt.show() + plt.close() + + plt.subplots(num=f'CLOSE TO CONTINUE RUN - Accuracy evolution') + plt.title('Training vs. Validation accuracy') + plt.plot(summary_stats.train_acc, 'b', label = 'Train accuracy') + plt.plot(summary_stats.valid_acc, 'g', label = 'Valid. accuracy') + plt.legend(loc = 'upper right') + plt.tight_layout() + plt.show() + plt.close() + + min_loss, idx = min((loss, idx) for (idx, loss) in enumerate(history.history['val_loss'])) + print('Minimum val_loss at epoch', '{:d}'.format(idx+1), '=', '{:.4f}'.format(min_loss)) + min_loss = round(min_loss, 4) + + # Load the best model epoch + + model.load_weights(save_best_weights) + + # Model prediction on training data + + print('\nModel predicting. Please wait\n') + t0 = time.time() + Xr_hat = model.predict([XUds_test, XSds_test, XVds_test]) + + t1 = time.time() + print(f"\nPrediction complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes\n") + + + if decision1=='scaling': + Xr_test = Xr[test_ind] + + for i in range(nvar): + Xr_hat[...,i] = descale_val(Xr_hat[...,i], var_min[i], var_max[i]) + else: + None + + if dims=='Data3D': + Xr_test=np.reshape(Xr_test,(nt-n_train,ny,nx,nz,nvar),order='F') + Xr_hat=np.reshape(Xr_hat,(nt-n_train,ny,nx,nz,nvar),order='F') + Xr_sel=np.reshape(Xr_sel,(nt,ny_sel,nx_sel,nz,nvar),order='F') + print('Xr_test: ',Xr_test.shape) + print('Xr_hat: ',Xr_hat.shape) + print('Xr_sel: ',Xr_sel.shape) + print('\n') + + xg = np.linspace(0,nx,nx) + yg = np.linspace(0,ny,ny) + + x = np.linspace(0,nx,nx_sel) + y = np.linspace(0,ny,ny_sel) + xx, yy = np.meshgrid(x, y) + + # Saving results + if decision5 == 'yes': + os.makedirs(f"{path0}/{filename}/figures") + os.makedirs(f"{path0}/{filename}/tables") + os.makedirs(f"{path0}/{filename}/videos") + + while True: + while True: + nt = input(f'Introduce the snapshot to plot (default is first predicted snapshot {n_train + 1}). Cannot be higher than {n_train + int(test_ind.size)}: ') + if not nt: + nt = 0 + break + elif nt.isdigit(): + if int(nt) > n_train and int(nt) < n_train + int(test_ind.size): + nt = int(nt) - n_train - 1 + break + else: + print('\tError: Selected snapshot is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + while True: + nv = input(f'Introduce the component to plot (default component 1). Maximum number of components is {Xr_test.shape[-1]}: ') + if not nv: + nv = 0 + break + elif nv.isdigit(): + if int(nv) <= nvar: + nv = int(nv)-1 + break + else: + print('\tError: Selected component is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + if int(nv) > 0: + var_num = int(nv) + 1 + else: + var_num = 1 + + if int(nt) > 0: + snap_num = n_train + int(nt) + 1 + else: + snap_num = n_train + 1 + + if 'nz' in locals(): + n5 = int(Xr.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - Snapshot Comparison XY plane') + fig.suptitle(f'XY plane - Component {var_num} Snapshot {snap_num}') + ax[0].contourf(yg,xg,np.transpose(Xr_test[nt,..., n5, nv], (1,0))) + ax[0].set_title('Real Data - XY Plane') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(yg,xg,np.transpose(Xr_hat[nt,..., n5, nv], (1,0))) + ax[1].set_title('Predicted Data - XY Plane') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.tight_layout() + plt.show() + plt.close() + + else: + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - Snapshot Comparison') + fig.suptitle(f'Component {var_num} Snapshot {snap_num}') + ax[0].contourf(yg,xg,np.transpose(Xr_test[nt,...,nv], (1,0))) + ax[0].set_title('Real Data') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(yg,xg,np.transpose(Xr_hat[nt,...,nv], (1,0))) + ax[1].set_title('Predicted Data') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.tight_layout() + plt.show() + plt.close() + + Resp = input('Do you want to plot other snapshots? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + else: + print('\tError: Select yes or no (y/n)\n') + + if decision5 == 'yes': + tabname1 = "tables/NN_table1" + tabname2 = "tables/NN_table2" + if 'nz' in locals(): + n5 = int(Xr.shape[3] / 2) + Xr_test_p0=Xr_test[...,n5,:] + Xr_hat_p0=Xr_hat[...,n5,:] + Xr_sel_p0=Xr_sel[...,n5,:] + + [results_table, results_table2] = error_tables(Xr_test_p0, Xr_hat_p0, Xr_sel_p0,tabname1,tabname2, path0, filename, nvar) + else: + [results_table, results_table2] = error_tables(Xr_test, Xr_hat, Xr_sel, tabname1, tabname2, path0, filename, nvar) + + if decision5 == 'yes': + print('\nPerformance measures on Test data, for all measures:\n') + print(results_table) + + + print('\nPerformance measures on Test data, for one specific layer of measures:\n') + print(results_table2) + print('\n') + + if decision5 == 'yes': + print(f'Saving first 3 snapshots comparison plots for each variable to: {path0}/{filename}/figures') + for var in range(nvar): + print(f'\nvariable: {var+1}') + files3 = os.listdir(f'{path0}/{filename}/figures') + os.makedirs(f"{path0}/{filename}/figures/var{var}",exist_ok=True) + + for n_snap in range(3): + print(f'\tSnapshot number: {n_snap}') + if 'nz' in locals(): + n5 = int(Xr.shape[3] / 2) + figname = f"{path0}/{filename}/figures/var{var}/var{var}_snap{n_train+n_snap}_p{n5}.png" + Xr_test_sel=Xr_test[...,n5,:] + Xr_hat_sel=Xr_hat[...,n5,:] + Xr_sel_sel=Xr_sel[...,n5,:] + figures_results(n_snap, var, yg, xg, y, x, Xr_test_sel, Xr_sel_sel, Xr_hat_sel, figname) + else: + figname = f"{path0}/{filename}/figures/var{var}/var{var}_{n_train+n_snap}.png" + figures_results(n_snap, var, yg, xg, y, x, Xr_test, Xr_sel, Xr_hat, figname) + + # Video creation + + if not decision_20 or decision_20.strip().lower() in ['npy', '.npy']: + np.save(f'{path0}/{filename}/reconstruction.npy', Xr_hat) + + if decision_20.strip().lower() in ['.mat', 'mat']: + mdic1 = {"reconstruction": Xr_hat} + file_mat1 = str(f'{path0}/{filename}/reconstructiom.mat') + hdf5storage.savemat(file_mat1, mdic1, appendmat=True, format='7.3') + + if decisionx == 'yes': + for var in range(nvar): + print(f'Generating video for variable {nvar}. Video will be saved to {path0}/{filename}/videos') + if 'nz' in locals (): + n5 = int(Xr.shape[3] / 2) + videoname=f"{path0}/{filename}/videos/var{var}_p{n5}.gif" + Xr_test_sel=Xr_test[...,n5,:] + Xr_hat_sel=Xr_hat[...,n5,:] + Xr_sel_sel=Xr_sel[...,n5,:] + videos_results(videoname, var, n_train, test_ind, xg, yg, x, y, Xr_test_sel, Xr_hat_sel, Xr_sel_sel) + else: + videoname=f"{path0}/{filename}/videos/var{var}.gif" + videos_results(videoname, var, n_train, test_ind, xg, yg, x, y, Xr_test, Xr_hat, Xr_sel) + + if decision7=='yes': + print(f'\nPlots will be saved to {path0}/{filename}/ylim/') + for var in range(nvar): + os.makedirs(f"{path0}/{filename}/ylim", exist_ok=True) + os.makedirs(f"{path0}/{filename}/ylim/var{var}", exist_ok=True) + figname = f"{path0}/{filename}/ylim/var{var}/var{var}_scatter.png" + files3 = os.listdir(f'{path0}/{filename}/ylim') + + os.makedirs(f"{path0}/{filename}/ylim/var{var}",exist_ok=True) + + for n_snap in range(0, 1): + print(f'\nGenerating comparison plot for variable {var}') + if 'nz' in locals(): + n5 = int(Xr.shape[3] / 2) + figname = f"{path0}/{filename}/ylim/var{var}/var{var}_snap{n_train+n_snap}_p{n5}.png" + + Xr_test_sel=Xr_test[...,n5,:] + Xr_hat_sel=Xr_hat[...,n5,:] + Xr_sel_sel=Xr_sel[...,n5,:] + figures_results_ylim(n_snap, var, yg, xg, y, x, Xr_test_sel, Xr_sel_sel, Xr_hat_sel, figname) + + else: + figname = f"{path0}/{filename}/ylim/var{var}/var{var}_{n_train+n_snap}.png" + figures_results_ylim(n_snap, var, yg, xg, y, x, Xr_test, Xr_sel, Xr_hat, figname) \ No newline at end of file diff --git a/v0.1_ModelFLOWs_app/DMDd.py b/v0.1_ModelFLOWs_app/DMDd.py new file mode 100644 index 0000000..6ad1354 --- /dev/null +++ b/v0.1_ModelFLOWs_app/DMDd.py @@ -0,0 +1,512 @@ +import numpy as np +import numba as nb + +def matlen(var): + '''Equivalent to Matlab's length()''' + if np.size(np.shape(var))==1: + x = np.size(var) + else: + x = max(np.shape(var)) + return x + +def error(V,Vrec): + '''Relative RMS and max errors''' + return np.linalg.norm(V-Vrec)/np.linalg.norm(V),np.linalg.norm(V-Vrec,np.inf)/np.linalg.norm(V,np.inf) + +# @nb.njit(cache=True) + +def truncatedSVD(A,esvd): + '''Decomposition into singular values, truncated on esvd''' + U,s,Wh=np.linalg.svd(A,full_matrices=False) + n=0 + norm=np.linalg.norm(s) + for i in range(s.size): + if np.linalg.norm(s[i:])/norm<=esvd: + break + else: + n+=1 + return U[:,:n],s[:n],Wh[:n,:] + +def unfold(A,dim): + '''Turns tensor into matrix keeping the columns on dim''' + ax=np.arange(A.ndim) + return np.reshape(np.moveaxis(A,ax,np.roll(ax,dim)),(A.shape[dim],A.size//A.shape[dim])) + +def fold(B,shape,dim): + '''Reverse operation to the unfold function''' + ax=np.arange(len(shape)) + shape=np.roll(shape,-dim) + A=np.reshape(B,shape) + return np.moveaxis(A,ax,np.roll(ax,-dim)) + +def tprod(S,U): + '''Tensor product of an ndim-array and multiple matrices''' + T = S + shap = list(np.shape(S)) + for i in range(0,np.size(U)): + x = np.count_nonzero(U[0][i]) + if not x==0: + shap[i] = np.shape(U[0][i])[0] + H = unfold(T,i) + T = fold(np.dot(U[0][i],H), shap, i) + return T + +def tensormatmul(S,U,dim): + '''Internal product of tensor and matrix in dim''' + shape=np.array(S.shape) + shape[dim]=U.shape[0] + return fold(U@unfold(S,dim),shape,dim) + +def truncHOSVD(A,esvd): + '''Decomposition into singular values for tensors, truncated in esvd''' + Ulist=[] + slist=[] + S=A.copy() + for i in range(A.ndim): + [U,s,Vh]=truncatedSVD(unfold(A,i),esvd/np.sqrt(A.ndim)) + Ulist.append(U) + S=tensormatmul(S,U.T,i) + for i in range(A.ndim): + s=np.zeros(S.shape[0]) + ax=np.arange(A.ndim) + for j in range(S.shape[0]): + s[j]=np.linalg.norm(S[j]) + slist.append(s) + S=np.moveaxis(S,ax,np.roll(ax,1)) + return S,Ulist,slist + +def dmd1(V,t,esvd,edmd): + '''First order dynamic modal decomposition: + Input: + -V (IxJ): Snapshot matrix. + -t (J): Time vector. + -esvd: First tolerance (SVD). + -edmd: Second tolerance (DMD modes). + Output: + -u (Ixn): mode matrix (columns) sorted from biggest to smallest amplitude, put to scale. + -areal (n): amplitude vector (sorted) used to put u to scale. + -eigval: eigenvalues + -delta (n): growth rate vector. + -omega (n): frequency vector. + -DMDmode: Modes''' + dt=t[1]-t[0] + + #Reduced snapshots: + U,s,Wh=truncatedSVD(V,esvd) #I*r', r'*r', r'*J +# Vvir=np.conj(U[:,:n].T)@V +# Vvir=np.diag(s[:n])@Wh[:n,:] #r'*J +# Vvir=s[:n]*Wh[:n,:] + Vvir=np.diag(s)@Wh #r'*J + n=s.size + + #Spatial complexity kk: + NormS=np.linalg.norm(s,ord=2) + kk=0 + for k in range(0,n): + if np.linalg.norm(s[k:n],2)/NormS>esvd: + kk=kk+1 + print(f'Spatial complexity: {kk}') + + #Koopman matrix reconstruction: + Uvir,svir,Wvirh=np.linalg.svd(Vvir[:,:-1],full_matrices=False) #r'*r', r'*r', r'*(J-1) + Rvir=Vvir[:,1:]@Wvirh.conj().T@np.diag(svir**-1)@Uvir.conj().T #r'*r' + eigval,eigvec=np.linalg.eig(Rvir) #r'*r' + + #Frequencies and Growthrate: + delta=np.log(eigval).real/dt #r' + omega=np.log(eigval).imag/dt #r' + + #Amplitudes: + A=np.zeros((eigvec.shape[0]*Vvir.shape[1],eigvec.shape[1])) #(r'*J)*r' + b=np.zeros(eigvec.shape[0]*Vvir.shape[1])#(r'*J)*1 + for i in range(Vvir.shape[1]): + A[i*eigvec.shape[0]:(i+1)*eigvec.shape[0],:]=eigvec@np.diag(eigval**i) + b[i*eigvec.shape[0]:(i+1)*eigvec.shape[0]]=Vvir[:,i] + + Ua,sa,Wa=np.linalg.svd(A,full_matrices=False) #(r'*J)*r', r'*r', r'*r' + a=Wa.conj().T@np.diag(sa**-1)@Ua.conj().T@b #r' + + #Modes: + uvir=eigvec@np.diag(a) #r'*r' +# u=U[:,:n]@uvir #I*r' + u=U@uvir #I*r' +# areal=np.zeros(a.size) #Real amplitudes +# for i in range(u.shape[1]): +# areal[i]=np.linalg.norm(u[:,i])/np.sqrt(V.shape[1]) + areal=np.linalg.norm(u,axis=0)/np.sqrt(V.shape[0]) + + #Spectral complexity: + kk3=0 + for m in range(0,np.size(areal)): + if areal[m]/np.max(areal)>edmd: + kk3=kk3+1 + print(f'Spectral complexity: {kk3}') + + idx=np.flip(np.argsort(areal)) + u=u[:,idx] + areal=areal[idx] + eigval=eigval[idx] + delta=delta[idx] + omega=omega[idx] + + #Filter important ones: + mask=(areal/areal[0])>edmd + + #Mode Matrix: + ModeMatr=np.zeros((kk3,4)) + for ii in range(0,kk3): + ModeMatr[ii,0]=ii+1 + ModeMatr[ii,1]=delta[mask][ii] + ModeMatr[ii,2]=omega[mask][ii] + ModeMatr[ii,3]=areal[mask][ii] + # print('Mode Number, GrowthRate, Frequency and Amplitude of each mode:') + # print(ModeMatr) + + #Calculate modes: + u=u[:,mask] + U = U[:,0:kk] + DMDmode=np.zeros((V.shape[0],kk3),dtype=np.complex128) + Amplitude0=np.zeros(kk3) + for m in range(0,kk3): + NormMode=np.linalg.norm(np.dot(U,u[:,m]),ord=2)/np.sqrt(V.shape[0]) + Amplitude0[m]=NormMode + DMDmode[:,m]=np.dot(U,u[:,m])/NormMode + + return u,areal[mask],eigval[mask],delta[mask],omega[mask],DMDmode + +#@nb.njit(cache=True) +def hodmd(V,d,t,esvd,edmd): + '''High order (d) modal decomposition: + Input: + -V (IxJ): snapshot matrix. + -t (J): time vector. + -d: parameter of DMD-d, higher order Koopman assumption (int>=1). + -esvd: first tolerance (SVD). + -edmd: second tolerance (DMD-d modes). + Output: + -u (Ixn): mode matrix (columns) sorted from biggest to smallest amplitude, put to scale. + -areal (n): amplitude vector (sorted) used to put u to scale. + -eigval: eigenvalues + -delta (n): growth rate vector. + -omega (n): frequency vector. + -DMDmode: Modes''' + dt=t[1]-t[0] + + #Reduced snapshots: + U,s,Wh=truncatedSVD(V,esvd) #I*n, n*n, n*J +# Vvir=np.conj(U[:,:n].T)@V + Vvir=np.diag(s)@Wh #n*J + #print("Tamaño Vvir: ",Vvir.shape) +# Vvir=s*Wh + n=s.size + + #Spatial complexity kk: + NormS=np.linalg.norm(s,ord=2) + kk=0 + for k in range(0,n): + if np.linalg.norm(s[k:n],2)/NormS>esvd: + kk=kk+1 + print(f'Spatial complexity: {kk}') + + #Reduced and grouped snapshots: + Vdot=np.zeros((d*n,Vvir.shape[1]-d+1),dtype=Vvir.dtype) #(d*n)*(J-d+1) + for j in range(d): + Vdot[j*n:(j+1)*n,:]=Vvir[:,j:Vvir.shape[1]-d+j+1] + #print("Size Vdot: ",Vdot.shape) + + #Reduced, grouped and again reduced snapshots: + Udot,sdot,Whdot=truncatedSVD(Vdot,esvd) #(d*n)*N, N*N, N*(J-d+1) + Vvd=np.diag(sdot)@Whdot #N*(J-d+1) + #print("Size Vvd: ",Vvd.shape) + + #Spatial dimension reduction: + print(f'Spatial dimension reduction: {np.size(sdot)}') + + #Koopman matrix reconstruction: + Uvd,svd,Whvd=np.linalg.svd(Vvd[:,:-1],full_matrices=False) #N*r', r'*r', r'*(J-d) + Rvd=Vvd[:,1:]@Whvd.conj().T@np.diag(svd**-1)@Uvd.conj().T #r'*r' + eigval,eigvec=np.linalg.eig(Rvd) #r'*r'==N*N + #print("Size Rvd: ",Rvd.shape) + + #Frequencies and growthrate: + delta=np.log(eigval).real/dt #r' + omega=np.log(eigval).imag/dt #r' + + #Modes + q=(Udot@eigvec)[(d-1)*n:d*n,:] #Taking steps back n*N + Uvir=q/np.linalg.norm(q,axis=0) #n*N + #print("Size Uvir: ",Uvir.shape) + + #Amplitudes: + A=np.zeros((Uvir.shape[0]*Vvir.shape[1],Uvir.shape[1]),dtype=np.complex128) #(n*J)*N + #print("Size A: ",A.shape) + b=np.zeros(Uvir.shape[0]*Vvir.shape[1],dtype=Vvir.dtype)#(n*J)*1 + for i in range(Vvir.shape[1]): + A[i*Uvir.shape[0]:(i+1)*Uvir.shape[0],:]=Uvir@np.diag(eigval**i) + b[i*Uvir.shape[0]:(i+1)*Uvir.shape[0]]=Vvir[:,i] +# print(A[:Uvir.shape[0],:]) + Ua,sa,Wa=np.linalg.svd(A,full_matrices=False) #(n*J)*N, N*N, N*N + a=Wa.conj().T@np.diag(sa**-1)@Ua.conj().T@b #N + +# print(eigval) + #Modes + uvir=Uvir@np.diag(a) #n*N + u=U@uvir #I*N + areal=np.linalg.norm(u,axis=0)/np.sqrt(V.shape[0]) + #print("Size ufull: ",u.shape) + + #Spectral complexity: + kk3=0 + for m in range(0,np.size(areal)): + if areal[m]/np.max(areal)>edmd: + kk3=kk3+1 + print(f'Spectral complexity: {kk3}') + + idx=np.flip(np.argsort(areal)) + u=u[:,idx] + areal=areal[idx] + eigval=eigval[idx] + delta=delta[idx] + omega=omega[idx] + + #Filter important ones: + mask=(areal/areal[0])>edmd + + #Mode Matrix: + ModeMatr=np.zeros((kk3,4)) + for ii in range(0,kk3): + ModeMatr[ii,0]=ii+1 + ModeMatr[ii,1]=delta[mask][ii] + ModeMatr[ii,2]=omega[mask][ii] + ModeMatr[ii,3]=areal[mask][ii] + # print('Mode Number, GrowthRate, Frequency and Amplitude of each mode:') + # print(ModeMatr) + + #Calculate DMD modes: + u=u[:,mask] + U = U[:,0:kk] + DMDmode=np.zeros((V.shape[0],kk3),dtype=np.complex128) + Amplitude0=np.zeros(kk3) + for m in range(0,kk3): + NormMode=np.linalg.norm(np.dot(U.T,u[:,m]),ord=2)/np.sqrt(V.shape[0]) + Amplitude0[m]=NormMode + DMDmode[:,m]=u[:,m]/NormMode + + return u,areal[mask],eigval[mask],delta[mask],omega[mask],DMDmode + +def tensorHODMD(V,d,t,esvd,edmd): + '''High order (d) modal decomposition for tensors: + Input: + -V (I1xI2x...xJ): Snapshots. + -t (J): Time vector. + -d: parameter of DMD-d, higher order Koopman assumption (int>=1). + -esvd: first tolerance (SVD). + -edmd: second tolerance (DMD-d modes). + Output: + -u (I1xI2x...xn): mode tensor (columns) sorted from biggest to smallest amplitude, put to scale. + -areal (n): amplitude vector (sorted) used to put u to scale. + -eigval: eigenvalues + -delta (n): growth rate vector. + -omega (n): frequency vector. + -DMDmode: Modes''' + + #Reduced snapshots: + S,Vl,sl=truncHOSVD(V,esvd) #I*n, n*n, n*J + Svir=S.copy() + for i in range(S.ndim-1): + Svir=tensormatmul(Svir,Vl[i],i) + Svir=Svir/sl[-1] + + uvir,a,eigval,delta,omega,DMDmode=hodmd((sl[-1]*Vl[-1]).T,d,t,esvd,edmd) + u=tensormatmul(Svir,uvir.T,V.ndim-1) + + return u,a,eigval,delta,omega,DMDmode + +def remake(u,t,mu): + '''Reconstructs original data from DMD-d results: + Input: + -u (Ixn): Mode matrix (columns). + -t (J): Time vector. + -delta (n): vector de ratios de crecimiento. + -omega (n): vector de frecuencias. + -mu: np.exp(np.dot((t[1]-t[0]),delta[iii]+np.dot(complex(0,1),omega[iii]))) + Output: + -vrec (IxJ): reconstructed snapshots''' + + vrec=np.zeros((u.shape[0],t.size),dtype=np.complex128) + for i in range(t.size): + for j in range(mu.shape[0]): + vrec[:,i]+=u[:,j]*mu[j]**i#*np.exp((delta[j]+omega[j]*1j)*t[i]) + return vrec + +#@nb.njit(cache=True) +def remakeTens(u,t,mu): + '''Reconstructs original data from DMD-d results: + Input: + -u (Ixn): Mode matrix (columns). + -t (J): Time vector. + -delta (n): vector de ratios de crecimiento. + -omega (n): vector de frecuencias. + -mu: np.exp(np.dot((t[1]-t[0]),delta[iii]+np.dot(complex(0,1),omega[iii]))) + Output: + -vrec (IxJ): reconstructed snapshots''' + + shape=np.array(u.shape,dtype=np.int32) + shape[-1]=t.size + vrec=np.zeros(tuple(shape),dtype=np.complex128) + idx=[slice(None)]*shape.size + for i in range(t.size): + for j in range(u.shape[-1]): + idx[-1]=i + vrec[tuple(idx)]+=u.take(j,axis=-1)*mu[j]**i#*np.exp((delta[j]+omega[j]*1j)*t[i]) + return vrec + +def hodmd_IT(Vvir,d,t,esvd,edmd): + '''High order (d) modal decomposition: + Input: + -V (IxJ): snapshot matrix. + -t (J): time vector. + -d: parameter of DMD-d, higher order Koopman assumption (int>=1). + -esvd: first tolerance (SVD). + -edmd: second tolerance (DMD-d modes). + Output: + -u (Ixn): mode matrix (columns) sorted from biggest to smallest amplitude, put to scale. + -areal (n): amplitude vector (sorted) used to put u to scale. + -eigval: eigenvalues + -delta (n): growth rate vector. + -omega (n): frequency vector. + -DMDmode: Modes''' + dt=t[1]-t[0] + N=Vvir.shape[0] + K=Vvir.shape[1] + + #Reduced and grouped snapshots: + Vdot=np.zeros((d*N,Vvir.shape[1]-d+1),dtype=Vvir.dtype) #(d*n)*(J-d+1) + for j in range(d): + Vdot[j*N:(j+1)*N,:]=Vvir[:,j:Vvir.shape[1]-d+j+1] + #print("Size Vdot: ",Vdot.shape) + + #Reduced, grouped and again reduced snapshots: + Udot,sdot,Whdot=truncatedSVD(Vdot,esvd) #(d*n)*N, N*N, N*(J-d+1) + Vvd=np.diag(sdot)@Whdot #N*(J-d+1) + #print("Size Vvd: ",Vvd.shape) + + #Spatial dimension reduction: + print(f'Spatial dimension reduction: {np.size(sdot)}') + + #Koopman matrix reconstruction: + Uvd,svd,Whvd=np.linalg.svd(Vvd[:,:-1],full_matrices=False) #N*r', r'*r', r'*(J-d) + Rvd=Vvd[:,1:]@Whvd.conj().T@np.diag(svd**-1)@Uvd.conj().T #r'*r' + eigval,eigvec=np.linalg.eig(Rvd) #r'*r'==N*N + #print("Size Rvd: ",Rvd.shape) + + #Frequencies and growthrate: + delta=np.log(eigval).real/dt #r' + omega=np.log(eigval).imag/dt #r' + + #Modes + q=(Udot@eigvec)[(d-1)*N:d*N,:] #Taking steps back n*N + Uvir=q/np.linalg.norm(q,axis=0) #n*N + #print("Size Uvir: ",Uvir.shape) + + #Amplitudes: + A=np.zeros((Uvir.shape[0]*Vvir.shape[1],Uvir.shape[1]),dtype=np.complex128) #(n*J)*N + #print("Size A: ",A.shape) + b=np.zeros(Uvir.shape[0]*Vvir.shape[1],dtype=Vvir.dtype)#(n*J)*1 + for i in range(Vvir.shape[1]): + A[i*Uvir.shape[0]:(i+1)*Uvir.shape[0],:]=Uvir@np.diag(eigval**i) + b[i*Uvir.shape[0]:(i+1)*Uvir.shape[0]]=Vvir[:,i] +# print(A[:Uvir.shape[0],:]) + Ua,sa,Wa=np.linalg.svd(A,full_matrices=False) #(n*J)*N, N*N, N*N + a=Wa.conj().T@np.diag(sa**-1)@Ua.conj().T@b #N + +# print(eigval) + #Modes + u=np.zeros((np.shape(Uvir)[0],np.size(eigval)),dtype=np.complex128) + for m in range(0,np.size(eigval)): + u[:,m]=np.dot(a[m],Uvir[:,m]) + #uvir=Uvir@np.diag(a) #n*N + #u=U@uvir #I*N + #areal=np.linalg.norm(u,axis=0)/np.sqrt(V.shape[0]) + #print("Size ufull: ",u.shape) + areal=np.zeros(np.size(eigval),dtype=np.complex128) + for mm in range(0,np.size(eigval)): + GR=delta[mm] + AmplGR=np.exp(np.dot(GR,t)) + AmplN=np.linalg.norm(AmplGR,ord=2)/np.sqrt(K) + areal[mm]=np.dot(np.linalg.norm(u[:,mm],ord=2),AmplN) + areal=np.real(areal) + + idx=np.flip(np.argsort(areal,axis=0)) + u=u[:,idx] + areal=areal[idx] + eigval=eigval[idx] + delta=delta[idx] + omega=omega[idx] + + #Spectral complexity: + kk3=0 + for m in range(0,np.size(areal)): + if areal[m]/np.max(areal)>edmd: + kk3=kk3+1 + print(f'Spectral complexity: {kk3}') + + return u[:,0:kk3],areal[0:kk3],eigval[0:kk3],delta[0:kk3],omega[0:kk3] + +def remakeTens_IT(t,t0,u,delta,omega): + '''Reconstructs original data from DMD-d results: + Input: + -u (Ixn): Mode matrix (columns). + -t (J): Time vector. + -delta (n): vector de ratios de crecimiento. + -omega (n): vector de frecuencias. + -mu: np.exp(np.dot((t[1]-t[0]),delta[iii]+np.dot(complex(0,1),omega[iii]))) + Output: + -vrec (IxJ): reconstructed snapshots''' + + dt=t-t0 + icomp=complex(0,1) + mu=np.zeros(np.size(delta),dtype=np.complex128) + for iii in range(0,np.size(delta)): + mu[iii] = np.exp(np.dot(dt,delta[iii]+np.dot(icomp,omega[iii]))) + return np.dot(u,mu) + +def reconst_IT(hatMode,Time,U,S,sv,nn,TimePos,GrowthRate,Frequency): + '''Reconstructs original data from DMD-d results.''' + N = np.shape(hatMode)[0] + K = matlen(Time) + hatTReconst = np.zeros((N,K), dtype=np.complex128) + + # Reconstruction using the DMD expansion: + for k in range(0,K): + hatTReconst[:,k] = remakeTens_IT(Time[k],Time[0],hatMode,GrowthRate,Frequency) + + # Reconstruction of the original tensor using the reduced tensor and the tensor core: + Unondim = U + UTnondim = np.zeros(shape=np.shape(U), dtype=object) + UTnondim[0][TimePos-1] = np.zeros((nn[TimePos-1],np.shape(hatTReconst)[1]),dtype=np.complex128) + for kk in range(0,nn[TimePos-1]): + UTnondim[0][TimePos-1][kk,:] = hatTReconst[kk,:]/sv[0][TimePos-1][kk] + + Unondim[0][TimePos-1] = np.transpose(UTnondim[0][TimePos-1]) + TensorReconst = tprod(S,Unondim) + return TensorReconst + +def modes_IT(N,hatMode,Amplitude,U,S,nn,TimePos): + '''Calculate DMD modes from results''' + hatMode_m = np.zeros((N,np.size(Amplitude)), dtype=hatMode.dtype) + for ii in range(0,np.size(Amplitude)): + hatMode_m[:,ii] = hatMode[:,ii]/Amplitude[ii] + + ModesT = np.zeros((nn[TimePos-1],np.shape(hatMode_m)[1]), dtype=hatMode_m.dtype) + for kk in range(0,nn[TimePos-1]): + ModesT[kk,:] = hatMode_m[kk,:] + + # Temporal DMD modes in reduced dimension: + Modes = U + Modes[0,TimePos-1] = np.transpose(ModesT) + + # Reconstruction of the temporal DMD modes: + DMDmode = tprod(S,Modes) + + return DMDmode \ No newline at end of file diff --git a/v0.1_ModelFLOWs_app/FullDLmodel.py b/v0.1_ModelFLOWs_app/FullDLmodel.py new file mode 100644 index 0000000..a33c5a2 --- /dev/null +++ b/v0.1_ModelFLOWs_app/FullDLmodel.py @@ -0,0 +1,1454 @@ +def FullDL(model_type): + import numpy as np + import pandas as pd + import matplotlib.pyplot as plt + import time + import math + import os + import data_load + os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' + + from sklearn.metrics import mean_squared_error, mean_absolute_error + from sklearn.metrics import median_absolute_error, r2_score + + import hdf5storage + + # Remove logs from tensorflow about CUDA + import logging, os + logging.disable(logging.WARNING) + os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" + + import tensorflow as tf + from tensorflow import keras + + from tensorflow.keras.layers import Dense, Reshape, BatchNormalization + from tensorflow.keras.layers import TimeDistributed, LSTM, Flatten, Conv3D + from tensorflow.keras.layers import MaxPooling3D, Permute, Input + from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping + + from tensorflow.keras.models import Model + + ################################################################################ + # Load Data and Preprocess + ################################################################################ + + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def load_preprocess(k, p, tensor_train: np.ndarray, tensor_test: np.ndarray, + train_size: float = 0.75, val_size: float = 0.25, + batch_size: int = 8, model_type = 'rnn'): + + # Use to raise errors + flag = {'check': True, + 'text': None} + outputs = [0,0,0,0,0,0,0,0,0,0,0,0,0] + outputs[0] = flag + + if (train_size + val_size != 1.0): + + flag = {'check': False, + 'text': "Error: Params 'train_size', 'val_size' " + + "must add up to 1, e.g., train_size = 0.8, " + + "val_size = 0.2"} + outputs[0] = flag + + return outputs + + if (tensor_train.ndim != 4 or tensor_test.ndim != 4): + + flag = {'check': False, + 'text': "Error: Params 'tensor_train' and 'tensor_test' has to have a " + + "number of dimensions equal to 4, and the temporal dimension has to " + + "be the last one"} + outputs[0] = flag + + return outputs + + if (model_type == 'cnn'): + + min_val = np.amin(tensor_train) + range_val = np.ptp(tensor_train) + + elif (model_type == 'rnn'): + + min_val = 0 + range_val = 1 + + else: + + flag = {'check': False, + 'text': "Error: Param 'model_type' has to be an string equal to " + + "'cnn' or 'rnn'"} + outputs[0] = flag + + return outputs + + + tensor_train_norm = (tensor_train - min_val)/range_val + tensor_test_norm = (tensor_test - min_val)/range_val + + # Dataset configuration + total_length = tensor_train_norm.shape[3] + channels_n = tensor_train_norm.shape[0] + dim_x = tensor_train_norm.shape[1] + dim_y = tensor_train_norm.shape[2] + + """ + Data Generator (Rolling Window) + """ + if (model_type == 'rnn'): + class DataGenerator(tf.keras.utils.Sequence): + 'Generates data for Keras' + def __init__(self, data, list_IDs, batch_size=5, dim=(2,35,50), k = 624, + p = 1, shuffle=True, till_end = False, only_test = False): + 'Initialization' + self.data = data + self.dim = dim + self.batch_size = batch_size + self.list_IDs = list_IDs + self.shuffle = shuffle + self.p = p + self.k = k + self.till_end = till_end + self.only_test = only_test + self.on_epoch_end() + + def __len__(self): + 'Denotes the number of batches per epoch' + if self.till_end: + lenx = math.ceil((len(self.list_IDs) / self.batch_size)) + else: + lenx = int(np.floor(len(self.list_IDs) / self.batch_size)) + return lenx + + def __getitem__(self, index): + 'Generate one batch of data' + # Generate indexes of the batch + indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size] + + # Find list of IDs + list_IDs_temp = [self.list_IDs[k] for k in indexes] + + # Generate data + X, y = self.__data_generation(list_IDs_temp) + if self.only_test: + return X + else: + return X, y + + def on_epoch_end(self): + 'Updates indexes after each epoch' + self.indexes = np.arange(len(self.list_IDs)) + if self.shuffle == True: + np.random.shuffle(self.indexes) + + def __data_generation(self, list_IDs_temp): + 'Generates data containing batch_size samples' # X : (n_samples, *dim, depth) + # Initialization + X = np.empty((self.batch_size, *self.dim, self.k)) + y = [np.empty((self.batch_size, *self.dim))]*self.p + + y_inter = np.empty((self.batch_size, *self.dim, p)) + + # Generate data + lenn = len(list_IDs_temp) + for i, ID in enumerate(list_IDs_temp): + # Store Xtrain + X[i,:,:,:,:] = self.data[:,:,:,ID:ID+k] + # Store Ytrain + y_inter[i,:,:,:,:] = self.data[:,:,:,ID+k:ID+k+p] + + for j in range(self.p): + y[j] = y_inter[:,:,:,:,j] + y[j] = np.reshape(y[j], (lenn, -1)) + + X = X.transpose((0,4,2,3,1)) + X = np.reshape(X, (X.shape[0],X.shape[1],-1)) + + return X, y + + elif (model_type == 'cnn'): + + class DataGenerator(tf.keras.utils.Sequence): + 'Generates data for Keras' + # IMPORTANT: In Synthetic jet: 1 piston cycle=624 snapshots---SET k=624 + def __init__(self, data, list_IDs, batch_size=5, dim=(2,35,50), + k = 624, p = 1, + shuffle=True, till_end = False, only_test = False): + 'Initialization' + self.data = data + self.dim = dim + self.batch_size = batch_size + self.list_IDs = list_IDs + self.shuffle = shuffle + self.p = p + self.k = k + self.till_end = till_end + self.only_test = only_test + self.on_epoch_end() + + def __len__(self): + 'Denotes the number of batches per epoch' + if self.till_end: + lenx = math.ceil((len(self.list_IDs) / self.batch_size)) + else: + lenx = int(np.floor(len(self.list_IDs) / self.batch_size)) + return lenx + + def __getitem__(self, index): + 'Generate one batch of data' + # Generate indexes of the batch + indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size] + + # Find list of IDs + list_IDs_temp = [self.list_IDs[k] for k in indexes] + + # Generate data + X, y = self.__data_generation(list_IDs_temp) + if self.only_test: + return X + else: + return X, y + + def on_epoch_end(self): + 'Updates indexes after each epoch' + self.indexes = np.arange(len(self.list_IDs)) + if self.shuffle == True: + np.random.shuffle(self.indexes) + + def __data_generation(self, list_IDs_temp): + 'Generates data containing batch_size samples' # X : (n_samples, *dim, depth) + # Initialization + X = np.empty((self.batch_size, *self.dim, self.k)) + y = [np.empty((self.batch_size, *self.dim))]*self.p + + y_inter = np.empty((self.batch_size, *self.dim, p)) + + # Generate data + lenn = len(list_IDs_temp) + for i, ID in enumerate(list_IDs_temp): + # Store Xtrain + X[i,:,:,:,:] = self.data[:,:,:,ID:ID+k] + # Store Ytrain + y_inter[i,:,:,:,:] = self.data[:,:,:,ID+k:ID+k+p] + + for j in range(self.p): + y[j] = y_inter[:,:,:,:,j] + y[j] = np.reshape(y[j], (lenn, -1)) + + X = X.transpose((0,4,2,3,1)) + + return X, y + + + """ + Create training, validation and test sets + """ + + # Prepare the dataset indexes + period_transitorio = 0 + stride_train = 1 + stride_val = 1 + stride_test = 1 + + dim=(channels_n, dim_x, dim_y) + + test_length = tensor_test.shape[-1] + val_length = int(val_size * total_length) + train_length = int(train_size * total_length) + + if(batch_size < 0 or + not isinstance(batch_size, int) or + batch_size > np.min([val_length,train_length])-k-1): + + flag = {'check': False, + 'text': "Error: Param 'batch_size' has to be an integer " + + "number greater than 0 and, in this case, lower than or equal to " + + f"{np.min([val_length,train_length])-k-1}"} + outputs[0] = flag + + return outputs + + if int(train_length-period_transitorio-(k+p)) < 0: + train_n = 0 + elif int((train_length-period_transitorio-(k+p))//stride_train) == 0: + train_n = 1 + else: + train_n = int(((train_length-period_transitorio)-(k+p))//stride_train) + 1 + + if int(test_length-period_transitorio-(k+p)) < 0: + test_n = 0 + elif int((test_length-period_transitorio-(k+p))//stride_test) == 0: + test_n = 1 + else: + test_n = int((test_length-period_transitorio-(k+p))//stride_test) + 1 + + if int(val_length-(k+p)) < 0: + val_n = 0 + elif int((val_length-(k+p))//stride_val) == 0: + val_n = 1 + else: + val_n = int((val_length-(k+p))//stride_val) + 1 + + # Indices for the beginnings of each batch + train_idxs = np.empty([train_n], dtype='int') + val_idxs = np.empty([val_n], dtype='int') + test_idxs = np.empty([test_n], dtype='int') + + j = period_transitorio + for i in range(train_n): + train_idxs[i] = j + j = j+stride_train + + j = train_length + for i in range(val_n): + val_idxs[i] = j + j = j+stride_val + + j = 0 + for i in range(test_n): + test_idxs[i] = j + j = j+stride_test + + # Generators + training_generator = DataGenerator(tensor_train_norm, train_idxs, + dim = dim, + batch_size = batch_size, + k = k, p = p, till_end = False, + only_test = False, + shuffle = True) + validation_generator = DataGenerator(tensor_train_norm, val_idxs, + dim = dim, + batch_size = batch_size, + k = k, p = p, till_end = False, + only_test = False, + shuffle = False) + + test_generator = DataGenerator(tensor_test_norm, test_idxs, + dim = dim, + batch_size = batch_size, + k = k, p = p, till_end = False, + only_test = True, + shuffle = False) + """ + print ('test_length: ', test_length) + print ('val_length: ', val_length) + print ('train_length: ', train_length) + print() + print ('test_n: ', test_n) + print ('val_n: ', val_n) + print ('train_n: ', train_n) + print() + print('test_generator_len: ', len(test_generator)) + print('validation_generator_len: ', len(validation_generator)) + print('training_generator_len: ', len(training_generator)) + """ + # preparar Ytest + test_n_adjusted = int(test_n/batch_size)*batch_size + Ytest = [np.empty([test_n_adjusted, channels_n, dim_x, dim_y], dtype='float64')] * p + Ytest_fl = [np.empty([test_n_adjusted, channels_n * dim_x * dim_y ], dtype='float64')] * p + + Ytest_inter = np.empty([test_n_adjusted, channels_n, dim_x, dim_y, p], dtype='float64') + + for i in range(test_n_adjusted): + j = test_idxs[i] + Ytest_inter[i,:,:,:,:] = tensor_test_norm[:,:,:,j+k:j+k+p] + + for r in range(p): + Ytest[r] = Ytest_inter[:,:,:,:,r] + Ytest_fl[r] = np.copy(np.reshape(Ytest[r], (test_n_adjusted, -1))) + + outputs = [ + flag, training_generator, validation_generator, test_generator, tensor_test, + tensor_test_norm, min_val, range_val, dim_x, dim_y, channels_n, Ytest, + Ytest_fl] + + return outputs + + ################################################################################ + # RNN Model + ################################################################################ + + def lstm_model(neurons, shared_dim, act_fun, act_fun2, lr, num_epochs, k, p, dim_x, dim_y, channels_n, training_generator, + validation_generator, path_saved: str = './'): + + flag = {'check': True, + 'text': None} + + if (num_epochs <= 0 or not isinstance(num_epochs, int)): + + flag = {'check': False, + 'text': "Error: Param 'num_epochs' has to be an integer " + + "number greater than 0, e.g, num_epochs = 70"} + + return [flag, 0, 0] + + if (not isinstance(path_saved, str)): + + flag = {'check': False, + 'text': "Error: Param 'path_saved' has to be an string " + + "indicating a path to save the files, e.g, path_saved = './"} + + return [flag, 0, 0] + + # Reset Model + tf.keras.backend.clear_session + + # Create Model + def create_model(neurons, in_shape, out_dim, shared_dim, act_fun, act_fun2, lr, p = 3): + x = Input(shape=in_shape) + + v = LSTM(neurons)(x) + v = Dense(p*neurons, activation= act_fun)(v) + v = Reshape((p,neurons))(v) + + tt = [1]*p + + r = TimeDistributed(Dense(shared_dim, activation=act_fun))(v) + s = tf.split(r, tt, 1) + for i in range(p): + s[i] = Flatten()(s[i]) + + o = [] + for i in range(p): + o.append( Dense(out_dim, activation=act_fun2)(s[i])) + + m = Model(inputs=x, outputs=o) + optimizer = keras.optimizers.Adam(learning_rate=lr) + m.compile(loss='mse', optimizer=optimizer, metrics=['mse']) + return(m) + + in_shape = [k, dim_x * dim_y * channels_n] + out_dim = dim_x * dim_y * channels_n + + model= create_model(neurons, in_shape, out_dim, shared_dim, act_fun, act_fun2, lr, p) + + print('Model Summary:\n') + model.summary() + + save_string = path_saved + 'RNN_model' + + # save the best weights + save_best_weights = save_string + '.h5' + save_summary_stats = save_string + '.csv' + save_last_weights = save_string + '_last_w.h5' + save_results_metrics = save_string + '_results_metrics.csv' + + """ + Training + """ + + np.random.seed(247531338) + + t0 = time.time() + # Model training + callbacks = [ModelCheckpoint( + save_best_weights, + monitor='val_loss', + save_best_only=True, + mode='auto'), + EarlyStopping( + monitor='val_loss', + patience=10, + verbose=1, + mode='auto', + min_delta = 0.001) + ] + + print('\nTraining Model Please Wait...\n') + history = model.fit(training_generator, + validation_data=validation_generator, + epochs=num_epochs, + verbose=2, + callbacks=callbacks) + t1 = time.time() + print('\nModel Trained Successfully!') + print(f"\nTraining complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + """ + Save Model + """ + + # save the last weights + model.save_weights(save_last_weights) + + # Aggregate the summary statistics + summary_stats = pd.DataFrame({'epoch': [ i + 1 for i in history.epoch ], + 'train_loss': history.history['loss'], + 'valid_loss': history.history['val_loss']}) + + summary_stats.to_csv(save_summary_stats) + + print('Please CLOSE all figures to continue the run\n') + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - Loss function evolution') + ax.plot(summary_stats.train_loss, 'b', label = 'Training loss') # blue + ax.plot(summary_stats.valid_loss, 'g--', label = 'Validation loss') # green + ax.grid() + ax.legend() + figName = path_saved + "loss_evolution_lstm_model.jpg" + plt.savefig(figName, format = 'jpg') + plt.show() + + return flag, model, save_best_weights + + def lstm_model_hp(num_epochs, k, p, dim_x, dim_y, channels_n, training_generator, + validation_generator, path_saved: str = './'): + + flag = {'check': True, + 'text': None} + + if (num_epochs <= 0 or not isinstance(num_epochs, int)): + + flag = {'check': False, + 'text': "Error: Param 'num_epochs' has to be an integer " + + "number greater than 0, e.g, num_epochs = 70"} + + return [flag, 0, 0] + + if (not isinstance(path_saved, str)): + + flag = {'check': False, + 'text': "Error: Param 'path_saved' has to be an string " + + "indicating a path to save the files, e.g, path_saved = './"} + + return [flag, 0, 0] + + # Reset Model + tf.keras.backend.clear_session + + import keras_tuner as kt + def create_model_hp(hp): + hp_activation = hp.Choice('hidden_layer_activation_function', values = ['relu', 'linear', 'tanh', 'elu']) + hp_neurons = hp.Int('hp_neurons', min_value = 10, max_value = 100, step = 10) + hp_activation_1 = hp.Choice('output_layer_activation_function', values = ['relu', 'linear', 'tanh', 'elu']) + hp_learning_rate = hp.Choice('learning_rate', values=[1e-2, 1e-3, 5e-3, 1e-4]) + hp_shared_dims = hp.Int('shared dims', min_value = 10, max_value = 100, step = 10) + + x = Input(shape=[k, dim_x * dim_y * channels_n]) + + v = LSTM(hp_neurons)(x) + v = Dense(p*hp_neurons, activation = hp_activation)(v) + v = Reshape((p,hp_neurons))(v) + + tt = [1]*p + + r = TimeDistributed(Dense(hp_shared_dims, activation = hp_activation))(v) + s = tf.split(r, tt, 1) + for i in range(p): + s[i] = Flatten()(s[i]) + + o = [] + for i in range(p): + o.append(Dense(dim_x * dim_y * channels_n, activation = hp_activation_1)(s[i])) + + m = Model(inputs=x, outputs=o) + optimizer = keras.optimizers.Adam(learning_rate=hp_learning_rate) + m.compile(loss='mse', optimizer=optimizer, metrics=['mse']) + return(m) + + if tuner_ == 'Hyperband': + tuner = kt.Hyperband(create_model_hp, objective = 'val_loss', max_epochs = 10, factor = 3, directory = 'dir_1', project_name = 'x', overwrite = True) + + elif tuner_ == 'RandomSearch': + tuner = kt.RandomSearch(create_model_hp, objective = 'val_loss', max_trials = 10, directory = 'dir_1', project_name = 'x', overwrite = True) + + elif tuner_ == 'Bayesian': + tuner = kt.BayesianOptimization(create_model_hp, objective = 'val_loss', max_trials = 10, beta = 3, directory = 'dir_1', project_name = 'x', overwrite = True) + + stop_early = tf.keras.callbacks.EarlyStopping(monitor = 'val_loss', patience = 10) + + print('\nSearching for optimal hyperparameters...') + + tuner.search(training_generator, + validation_data=validation_generator, + epochs=10, + verbose=1, + callbacks=[stop_early]) + + best_hps = tuner.get_best_hyperparameters(num_trials = 1)[0] + + print('\n-----------------------------') + print(f''' +HYPERPARAMETERS SUMMARY:\n +Hidden Layer activation function: {best_hps.get('hidden_layer_activation_function')} +Output Layer activation function: {best_hps.get('output_layer_activation_function')} +Number of neurons: {best_hps.get('hp_neurons')} +Number of shared dimensions: {best_hps.get('shared dims')} +Learning rate: {best_hps.get('learning_rate')} +Loss function: 'mse' + ''') + + model = tuner.hypermodel.build(best_hps) + + print('Model Summary:\n') + model.summary() + + save_string = path_saved + 'RNN_model' + + # save the best weights + save_best_weights = save_string + '.h5' + save_summary_stats = save_string + '.csv' + save_last_weights = save_string + '_last_w.h5' + save_results_metrics = save_string + '_results_metrics.csv' + + t0 = time.time() + + callbacks = [ModelCheckpoint(save_best_weights, monitor='val_loss', save_best_only=True, mode='auto'), + EarlyStopping(monitor='val_loss', patience=10, verbose=1, mode='auto', min_delta = 0.001)] + + print('\nTraining Model Please Wait...\n') + + history = model.fit(training_generator, + validation_data=validation_generator, + epochs=num_epochs, + verbose=1, + callbacks=callbacks) + + t1 = time.time() + print('\nModel Trained Successfully!') + + print(f"\nTraining complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + """ + Save Model + """ + + # save the last weights + model.save_weights(save_last_weights) + + # Aggregate the summary statistics + summary_stats = pd.DataFrame({'epoch': [ i + 1 for i in history.epoch ], + 'train_loss': history.history['loss'], + 'valid_loss': history.history['val_loss']}) + + summary_stats.to_csv(save_summary_stats) + + print('Please CLOSE all figures to continue the run\n') + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - Loss function evolution') + ax.plot(summary_stats.train_loss, 'b', label = 'Training loss') # blue + ax.plot(summary_stats.valid_loss, 'g--', label = 'Validation loss') # green + ax.grid() + ax.legend() + figName = path_saved + "loss_evolution_lstm_model.jpg" + plt.savefig(figName, format = 'jpg') + plt.show() + + return flag, model, save_best_weights + + ################################################################################ + # CNN Model + ################################################################################ + + def cnn_model(shared_dim, act_fun, act_fun1, lr, num_epochs, k, p, dim_x, dim_y, channels_n, training_generator, + validation_generator, path_saved: str = './'): + + flag = {'check': True, + 'text': None} + + if (num_epochs <= 0 or not isinstance(num_epochs, int)): + + flag = {'check': False, + 'text': "Error: Param 'num_epochs' has to be an integer " + + "number greater than 0, e.g, num_epochs = 70"} + + return [flag, 0, 0] + + if (not isinstance(path_saved, str)): + + flag = {'check': False, + 'text': "Error: Param 'path_saved' has to be an string " + + "indicating a path to save the files, e.g, path_saved = './"} + + return [flag, 0, 0] + + # Reset Model + tf.keras.backend.clear_session + + # Create Model + def create_model(in_shape, out_dim, p = 3, shared_dim = shared_dim, act_fun= act_fun, act_fun1 = act_fun1, lr = lr): + x = Input(shape=in_shape) + Fm = Input(shape=in_shape) + + v = Conv3D(5, + kernel_size=(2,2,2), + activation=act_fun, + input_shape=in_shape, + data_format='channels_last')(x) + v = MaxPooling3D(pool_size=(1,2,2), + padding='valid', + data_format='channels_last')(v) + v = BatchNormalization()(v) + v = Conv3D(10, + kernel_size=(2,2,2), + activation=act_fun, + input_shape=in_shape, + data_format='channels_last')(v) + v = MaxPooling3D(pool_size=(1,2,2), + padding='valid', + data_format='channels_last')(v) + v = BatchNormalization()(v) + v = Conv3D(20, + kernel_size=(2,2,2), + activation=act_fun, + input_shape=in_shape, + data_format='channels_last')(v) + v = MaxPooling3D(pool_size=(1,2,2), + padding='valid', + data_format='channels_last')(v) + v = BatchNormalization()(v) + v = Conv3D(p, + kernel_size=(1,1,1), + activation=act_fun, + input_shape=in_shape, + data_format='channels_last')(v) + v = Permute((4,1,2,3))(v) + v = Reshape((p,v.shape[2]*v.shape[3]*v.shape[4]))(v) + + tt = [1]*p + + r = TimeDistributed(Dense(shared_dim, activation=act_fun))(v) + s = tf.split(r, tt, 1) + for i in range(p): + s[i] = Flatten()(s[i]) + + o = [] + for i in range(p): + o.append( Dense(out_dim, activation=act_fun1)(s[i])) + + m = Model(inputs=x, outputs=o) + optimizer = tf.keras.optimizers.Adam(learning_rate = lr) + m.compile(loss='mse', optimizer=optimizer, metrics=['mse']) + return(m) + + in_shape = [k, dim_x, dim_y, channels_n] + out_dim = dim_x * dim_y * channels_n + + # Define model + model= create_model(in_shape, out_dim, p, shared_dim) + + print('Model Summary:\n') + model.summary() + + save_string = path_saved + 'CNN_model' + + # save the best weights + save_best_weights = save_string + '.h5' + save_summary_stats = save_string + '.csv' + save_last_weights = save_string + '_last_w.h5' + save_results_metrics = save_string + '_results_metrics.csv' + + """ + Training + """ + + np.random.seed(247531338) + + t0 = time.time() + # Model training + callbacks = [ModelCheckpoint( + save_best_weights, + monitor='val_loss', + save_best_only=True, + mode='auto') + ] + + print('\nTraining Model Please Wait...\n') + history = model.fit(training_generator, + validation_data=validation_generator, + epochs=num_epochs, + verbose=2, + callbacks=callbacks) + t1 = time.time() + print('\nModel Trained Successfully!') + print(f"\nTraining complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + """ + Save Model + """ + + # save the last weights + model.save_weights(save_last_weights) + + # Aggregate the summary statistics + summary_stats = pd.DataFrame({'epoch': [ i + 1 for i in history.epoch ], + 'train_loss': history.history['loss'], + 'valid_loss': history.history['val_loss']}) + + summary_stats.to_csv(save_summary_stats) + + print('Please CLOSE all figures to continue the run\n') + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - Loss function evolution') + ax.plot(summary_stats.train_loss, 'b', label = 'Training loss') # blue + ax.plot(summary_stats.valid_loss, 'g--', label = 'Validation loss') # green + ax.grid() + ax.legend() + figName = path_saved + "loss_evolution_cnn_model.jpg" + plt.savefig(figName, format = 'jpg') + plt.show() + + return flag, model, save_best_weights + + def cnn_model_hp(num_epochs, k, p, dim_x, dim_y, channels_n, training_generator, + validation_generator, path_saved: str = './'): + + flag = {'check': True, + 'text': None} + + if (num_epochs <= 0 or not isinstance(num_epochs, int)): + + flag = {'check': False, + 'text': "Error: Param 'num_epochs' has to be an integer " + + "number greater than 0, e.g, num_epochs = 70"} + + return [flag, 0, 0] + + if (not isinstance(path_saved, str)): + + flag = {'check': False, + 'text': "Error: Param 'path_saved' has to be an string " + + "idicating a path to save the files, e.g, path_saved = './"} + + return [flag, 0, 0] + + # Reset Model + tf.keras.backend.clear_session + + # Create Model + import keras_tuner as kt + def create_model_hp(hp): + hp_activation = hp.Choice('hidden_layer_activation_function', values = ['relu', 'linear', 'tanh', 'elu']) + hp_activation_1 = hp.Choice('output_layer_activation_function', values = ['relu', 'linear', 'tanh', 'elu']) + hp_learning_rate = hp.Choice('learning_rate', values=[1e-2, 1e-3, 5e-3, 1e-4]) + hp_shared_dims = hp.Int('shared dims', min_value = 10, max_value = 100, step = 10) + + in_shape = [k, dim_x, dim_y, channels_n] + + x = Input(shape=in_shape) + + v = Conv3D(5, + kernel_size=(2,2,2), + activation=hp_activation, + input_shape=in_shape, + data_format='channels_last')(x) + v = MaxPooling3D(pool_size=(1,2,2), + padding='valid', + data_format='channels_last')(v) + v = BatchNormalization()(v) + v = Conv3D(10, + kernel_size=(2,2,2), + activation=hp_activation, + input_shape=in_shape, + data_format='channels_last')(v) + v = MaxPooling3D(pool_size=(1,2,2), + padding='valid', + data_format='channels_last')(v) + v = BatchNormalization()(v) + v = Conv3D(20, + kernel_size=(2,2,2), + activation=hp_activation, + input_shape=in_shape, + data_format='channels_last')(v) + v = MaxPooling3D(pool_size=(1,2,2), + padding='valid', + data_format='channels_last')(v) + v = BatchNormalization()(v) + v = Conv3D(p, + kernel_size=(1,1,1), + activation=hp_activation, + input_shape=in_shape, + data_format='channels_last')(v) + v = Permute((4,1,2,3))(v) + v = Reshape((p,v.shape[2]*v.shape[3]*v.shape[4]))(v) + + tt = [1]*p + + r = TimeDistributed(Dense(hp_shared_dims, activation=hp_activation))(v) + s = tf.split(r, tt, 1) + for i in range(p): + s[i] = Flatten()(s[i]) + + o = [] + for i in range(p): + o.append(Dense(dim_x * dim_y * channels_n , activation=hp_activation_1)(s[i])) + + m = Model(inputs=x, outputs=o) + optimizer = tf.keras.optimizers.Adam(learning_rate = hp_learning_rate) + m.compile(loss='mse', optimizer=optimizer, metrics=['mse']) + return(m) + + + if tuner_ == 'Hyperband': + tuner = kt.Hyperband(create_model_hp, objective = 'val_loss', max_epochs = 10, factor = 3, directory = 'dir_1', project_name = 'x', overwrite = True) + + elif tuner_ == 'RandomSearch': + tuner = kt.RandomSearch(create_model_hp, objective = 'val_loss', max_trials = 10, directory = 'dir_1', project_name = 'x', overwrite = True) + + elif tuner_ == 'Bayesian': + tuner = kt.BayesianOptimization(create_model_hp, objective = 'val_loss', max_trials = 10, beta = 3, directory = 'dir_1', project_name = 'x', overwrite = True) + + stop_early = tf.keras.callbacks.EarlyStopping(monitor = 'val_loss', patience = 10) + + print('\nSearching for optimal hyperparameters...\n') + + tuner.search(training_generator, + validation_data=validation_generator, + epochs=3, + verbose=1, + callbacks=[stop_early]) + + best_hps = tuner.get_best_hyperparameters(num_trials = 1)[0] + print('\n-----------------------------') + print(f''' +HYPERPARAMETERS SUMMARY:\n +Hidden Layer activation function: {best_hps.get('hidden_layer_activation_function')} +Output Layer activation function: {best_hps.get('output_layer_activation_function')} +Number of shared dimensions: {best_hps.get('shared dims')} +Learning rate: {best_hps.get('learning_rate')} +Loss function: 'mse' + ''') + + model = tuner.hypermodel.build(best_hps) + + print('Model Summary:\n') + model.summary() + + save_string = path_saved + 'CNN_model' + + # save the best weights + save_best_weights = save_string + '.h5' + save_summary_stats = save_string + '.csv' + save_last_weights = save_string + '_last_w.h5' + save_results_metrics = save_string + '_results_metrics.csv' + + t0 = time.time() + + callbacks = [ModelCheckpoint(save_best_weights, monitor='val_loss', save_best_only=True, mode='auto'), + EarlyStopping(monitor='val_loss', patience=2, verbose=1, mode='auto', min_delta = 0.0001)] + + print('\nTraining Model Please Wait...\n') + + history = model.fit(training_generator, + validation_data=validation_generator, + epochs=num_epochs, + verbose=1, + callbacks=callbacks) + + t1 = time.time() + print('\nModel Trained Successfully!') + + print(f"\nTraining complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + """ + Save Model + """ + + # save the last weights + model.save_weights(save_last_weights) + + # Aggregate the summary statistics + summary_stats = pd.DataFrame({'epoch': [ i + 1 for i in history.epoch ], + 'train_loss': history.history['loss'], + 'valid_loss': history.history['val_loss']}) + + summary_stats.to_csv(save_summary_stats) + + print('Please CLOSE all figures to continue the run\n') + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - Loss function evolution') + ax.plot(summary_stats.train_loss, 'b', label = 'Training loss') # blue + ax.plot(summary_stats.valid_loss, 'g--', label = 'Validation loss') # green + ax.grid() + ax.legend() + figName = path_saved + "loss_evolution_lstm_model.jpg" + plt.savefig(figName, format = 'jpg') + plt.show() + + return flag, model, save_best_weights + + ################################################################################ + # Inference + ################################################################################ + + def RRMSE (real, predicted): + RRMSE = np.linalg.norm(np.reshape(real-predicted,newshape=(np.size(real),1)),ord=2)/np.linalg.norm(np.reshape(real,newshape=(np.size(real),1))) + return RRMSE + + def smape(A, F): + return ((100.0/len(A)) * + np.sum(2 * np.abs(F - A) / (np.abs(A) + np.abs(F))+ np.finfo(float).eps)) + + def inference(model, save_best_weights: str, test_generator, + Ytest_fl: np.ndarray, Ytest: np.ndarray, min_val: float, + range_val: float, p: int, path_saved: str = './', + model_type = 'rnn'): + + + t0 = time.time() + # Load model with best weights' configuration obtain from training + model.load_weights(save_best_weights) + Ytest_hat_fl = model.predict( + test_generator, + max_queue_size=10, + workers=6, + use_multiprocessing=False, + verbose=0) + + t1 = time.time() + print('\nModel predicting. Please wait') + print(f"\nPrediction complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + if not output0 or output0.strip().lower() in ['.npy', 'npy']: + np.save(path_saved + '/TensorPred.npy', Ytest_hat_fl) + + elif output0.strip().lower() in ['.mat', 'mat']: + mdic = {"Pred": Ytest_hat_fl} + file_mat= str(path_saved + '/TensorPred.mat') + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + # print('Error measure of the first prediction for each sample on Test set') + lag = 0 + num_sec = Ytest_hat_fl[0].shape[0] + + print('\nPerformance measures on Test data, per sec') + results_table = pd.DataFrame(index=['MSE','MAE','MAD','R2','SMAPE','RRMSE'],columns=range(num_sec)) + for i in range(num_sec): + results_table.iloc[0,i] = mean_squared_error(Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[1,i] = mean_absolute_error(Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[2,i] = median_absolute_error(Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[3,i] = r2_score(Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[4,i] = smape(Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[5,i] = RRMSE(np.reshape(Ytest_fl[lag][i,:],(-1,1)), np.reshape(Ytest_hat_fl[lag][i,:],(-1,1))) + print(results_table) + + # print(results_table) + savename = path_saved + "table_" + model_type + f"_first_prediction.csv" + results_table.to_csv(savename, index=True) + + # print('Error measure of the second prediction for each sample on Test set') + lag = 1 + num_sec = Ytest_hat_fl[0].shape[0] + print('\nPerformance measures on Test data, per sec, for time lag = 1') + results_table = pd.DataFrame(index=['MSE','MAE','MAD','R2','SMAPE','RRMSE'],columns=range(num_sec)) + for i in range(num_sec): + results_table.iloc[0,i] = mean_squared_error( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[1,i] = mean_absolute_error( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[2,i] = median_absolute_error( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[3,i] = r2_score( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[4,i] = smape( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[5,i] = RRMSE( np.reshape(Ytest_fl[lag][i,:],(-1,1)), np.reshape(Ytest_hat_fl[lag][i,:],(-1,1))) + + print(results_table) + savename = path_saved + "table_" + model_type + f"_second_prediction.csv" + results_table.to_csv(savename, index=True) + + print('\nPerformance measures on Test data, for all time, per time-ahead lag') + results_table_global = pd.DataFrame(index=['MSE','MAE','MAD','R2','SMAPE','RRMSE'],columns=range(p)) + for i in range(p): + results_table_global.iloc[0,i] = mean_squared_error(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[1,i] = mean_absolute_error(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[2,i] = median_absolute_error(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[3,i] = r2_score(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[4,i] = smape(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[5,i] = RRMSE( np.reshape(Ytest_fl[i].flatten(),(-1,1)), np.reshape(Ytest_hat_fl[i].flatten(),(-1,1))) + + results_table_global['mean'] = results_table_global.mean(axis=1) + + print(results_table_global) + savename = path_saved + "table_" + model_type + f"_mean.csv" + results_table_global.to_csv(savename, index=True) + + # Reshape predictions and targets for plotting + Ytest_hat_lag_0 = np.reshape( + Ytest_hat_fl[0], + (Ytest[0].shape[0], Ytest[0].shape[1], + Ytest[0].shape[2], Ytest[0].shape[3])) + + Ytest_hat_lag_1 = np.reshape( + Ytest_hat_fl[1], + (Ytest[1].shape[0], Ytest[1].shape[1], + Ytest[1].shape[2], Ytest[1].shape[3])) + + Ytest_lag_0 = np.reshape( + Ytest_fl[0], + (Ytest[0].shape[0], Ytest[0].shape[1], + Ytest[0].shape[2], Ytest[0].shape[3])) + + Ytest_lag_1 = np.reshape( + Ytest_fl[1], + (Ytest[1].shape[0], Ytest[1].shape[1], + Ytest[1].shape[2], Ytest[1].shape[3])) + + Ytest_hat_lag_0 = Ytest_hat_lag_0 * range_val + min_val + Ytest_hat_lag_1 = Ytest_hat_lag_1 * range_val + min_val + Ytest_lag_0 = Ytest_lag_0 * range_val + min_val + Ytest_lag_1 = Ytest_lag_1 * range_val + min_val + + return [Ytest_hat_lag_0, Ytest_hat_lag_1, Ytest_lag_0, Ytest_lag_1] + + def plot_results(Ytest_hat_lag_0: np.ndarray, Ytest_hat_lag_1: np.ndarray, + Ytest_lag_0: np.ndarray, Ytest_lag_1: np.ndarray, index: int, + path_saved: str = './', model_type = 'rnn'): + + flag = {'check': True, + 'text': None} + + if (index < 0 or index >= Ytest_lag_0.shape[0] or not isinstance(index, int)): + + flag = {'check': False, + 'text': "Error: Param 'index' has to be an non-negative integer " + + f"number lower than or equal to {Ytest_lag_0.shape[0] - 1} , " + + "e.g, num_epochs = 0"} + + return flag + + fig = plt.figure(figsize=(15,7), num = 'CLOSE TO CONTINUE RUN - Snapshot comparison') + plt.subplot(2,3,1) + plt.contourf(Ytest_lag_0[index,0,:,:], 10) + plt.title(f"Ground Truth - Sample {2*index+1} of test set") + plt.subplot(2,3,2) + plt.contourf(Ytest_hat_lag_0[index,0,:,:], 10) + plt.title(f"Prediction - Sample {2*index+1} of test set") + plt.subplot(2,3,3) + plt.contourf(np.abs(Ytest_hat_lag_0[index,0,:,:] - Ytest_lag_0[index,0,:,:]), 10) + plt.title(f"Absolute Error - Sample {2*index+1} of test set") + plt.colorbar() + + plt.subplot(2,3,4) + plt.contourf(Ytest_lag_1[index,0,:,:], 10) + plt.title(f"Ground Truth - Sample {2*index+2} of test set") + plt.subplot(2,3,5) + plt.contourf(Ytest_hat_lag_1[index,0,:,:], 10) + plt.title(f"Prediction - Sample {2*index+2} of test set") + plt.subplot(2,3,6) + plt.contourf(np.abs(Ytest_hat_lag_1[index,0,:,:] - Ytest_lag_1[index,0,:,:]), 10) + plt.title(f"Absolute Error - Sample {2*index+2} of test set") + plt.colorbar() + + fig.tight_layout() + + figName = path_saved + "predictions_" + model_type + f"_model_sample_{2*index+1}.jpg" + plt.savefig(figName, format = 'jpg') + #plt.show() + plt.show() + + return flag + + ################################################################################ + # Module Main + ################################################################################ + model = model_type.upper() + print(f"\n{model} Model\n") + print('-----------------------------') + print("Inputs: \n") + path0 = os.getcwd() + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + while True: + filetype = input('Select the input file format (.mat, .npy, .csv, .pkl, .h5): ') + print('\n\tWarning: This model can only be trained with 2-Dimensional data (as in: (variables, nx, ny, time))\n') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + Ten_orig, _ = data_load.main(filetype) + + while True: + train_size = input('Select train data percentage (0-1). Continue with 0.75: ') # test set proportion + if not train_size: + train_size = 0.75 + break + elif is_float(train_size): + train_size = np.round(float(train_size), 2) + break + else: + print('\tError: Please select a number\n') + + tensor_train = Ten_orig[...,:int(train_size*Ten_orig.shape[-1])] + tensor_test = Ten_orig[...,int(train_size*Ten_orig.shape[-1]):] + + val_size = np.round(1.0-train_size, 2) + + while True: + batch_size = input('Select batch size (recommended power of 2). Continue with 8: ') + if not batch_size: + batch_size = 8 + break + elif batch_size.isdigit(): + batch_size = int(batch_size) + break + else: + print('\tError: Select a valid number (must be integer)\n') + if model_type == 'cnn': + while True: + epoch = input('Select training epochs. Continue with 5: ') + if not epoch: + epoch = 5 + break + elif epoch.isdigit(): + epoch = int(epoch) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + elif model_type == 'rnn': + while True: + epoch = input('Select training epochs. Continue with 20: ') + if not epoch: + epoch = 20 + break + elif epoch.isdigit(): + epoch = int(epoch) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + k = input('Select number of snapshots used as predictors. Continue with 10: ') # number of snapshots used as predictors + if not k: + k = 10 + break + elif k.isdigit(): + k = int(k) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + p = input('Select number of snapshots used as time-ahead predictions. Continue with 2: ') # number of snapshots used as predictors + if not p: + p = 2 + break + elif p.isdigit(): + p = int(p) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + print('\n-----------------------------') + print('Model Parameters: \n') + + while True: + hyper = input('Use optimal hyperparameters? (y/n). Continue with No: ') + if not hyper or hyper.strip().lower() in ['no', 'n']: + hyper = 'No' + break + elif hyper.strip().lower() in ['yes', 'y']: + hyper = 'Yes' + break + else: + print('\tError: Select yes or no (y/n)\n') + + if hyper == 'Yes': + print(''' +Available hyperparameter tuners: +1) RandomSearch: All the hyperparameter combinations are chosen randomly. +2) Hyperband: Randomly sample all the combinations of hyperparameter and train the model for few epochs with the combinations, selecting the best candidates based on the results. +3) BayesianOptimization: Chooses first few combinations randomly, then based on the performance on these hyperparameters it chooses the next best possible hyperparameters. + ''') + while True: + tuner_ = input('Select a hyperparameter tuner (1/2/3). Continue with RandomSearch: ') + if not tuner_ or tuner_ == '1': + tuner_ = 'RandomSearch' + break + elif tuner_ == '2': + tuner_ = 'Hyperband' + break + elif tuner_ == '3': + tuner_ = 'Bayesian' + break + else: + print('\tError: Select a valid tuner\n') + + if hyper == 'No': + while True: + act_fun = input('Select hidden layer activation function (relu, elu, softmax, sigmoid, tanh, linear). Continue with relu: ') + if not act_fun or act_fun.strip().lower() == 'relu': + act_fun = 'relu' + break + elif act_fun.strip().lower() == 'elu': + act_fun = 'elu' + break + elif act_fun.strip().lower() == 'softmax': + act_fun = 'softmax' + break + elif act_fun.strip().lower() == 'sigmoid': + act_fun = 'sigmoid' + break + elif act_fun.strip().lower() == 'tanh': + act_fun = 'tanh' + break + elif act_fun.strip().lower() == 'linear': + act_fun = 'linear' + break + else: + print('\tError: Please select a valid option\n') + + while True: + act_fun2 = input('Select output layer activation function (tanh, relu, sigmoid, linear). Continue with tanh: ') + if not act_fun2 or act_fun2.strip().lower() == 'tanh': + act_fun2 = 'tanh' + break + elif act_fun2.strip().lower() == 'relu': + act_fun2 = 'relu' + break + elif act_fun2.strip().lower() == 'sigmoid': + act_fun2 = 'sigmoid' + break + elif act_fun2.strip().lower() == 'linear': + act_fun2 = 'linear' + break + else: + print('\tError: Please select a valid option\n') + if model_type == 'rnn': + while True: + neurons = input('Select number of neurons per layer. Continue with 100: ') + if not neurons: + neurons = 100 + break + elif neurons.isdigit(): + neurons = int(neurons) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + shared_dim = input('Select number of shared dims. Continue with 100: ') + if not shared_dim: + shared_dim = 100 + break + elif shared_dim.isdigit(): + shared_dim = int(shared_dim) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + lr = input('Select model learning rate. Continue with 2e-3: ') + if not lr: + lr = 0.002 + break + elif is_float(lr): + lr = float(lr) + break + else: + print('\tError: Please select a number\n') + + if model_type == 'rnn': + print('\n-----------------------------') + print(f''' + HYPERPARAMETERS SUMMARY:\n + Hidden Layer activation function: {act_fun} + Output Layer activation function: {act_fun2} + Number of neurons: {neurons} + Number of shared dimensions: {shared_dim} + Learning rate: {lr} + Loss function: mse + ''') + print('-----------------------------') + else: + print('\n-----------------------------') + print(f''' + HYPERPARAMETERS SUMMARY:\n + Hidden Layer activation function: {act_fun} + Output Layer activation function: {act_fun2} + Number of shared dimensions: {shared_dim} + Learning rate: {lr} + Loss function: mse + ''') + print('-----------------------------') + + print('Outputs:\n ') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_{model}_solution' + else: + filen = f'{filen}' + + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f'{path0}/{filen}') + + while True: + num = input(f'Select number snapshot to plot (must be multiple of {p}): ') + if num.isdigit(): + if int(num) % int(p) == 0 and int(num) > 1: + num = int(int(num)/int(p)) + break + else: + print('\tError: introduced value is not multiple of two\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + while True: + output0 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not output0 or output0.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + + path_saved = f'{path0}/{filen}/' + + + flag, training_generator, validation_generator, test_generator, tensor_test, \ + tensor_test_norm, min_val, range_val, dim_x, dim_y, channels_n, Ytest, \ + Ytest_fl = load_preprocess(k, p, tensor_train, tensor_test, + train_size = train_size, val_size = val_size, + batch_size = batch_size, model_type = model_type) + + if (not flag['check']): + print(flag['text']) + + else: + if (model_type == 'rnn'): + if hyper == 'No': + flag, model, save_best_weights = lstm_model(neurons, shared_dim, act_fun, act_fun2, lr, epoch, k, p, + dim_x, dim_y, channels_n, training_generator, + validation_generator, path_saved) + if hyper == 'Yes': + flag, model, save_best_weights = lstm_model_hp(epoch, k, p, dim_x, dim_y, channels_n, training_generator, + validation_generator, path_saved) + + elif (model_type == 'cnn'): + if hyper == 'No': + flag, model, save_best_weights = cnn_model(shared_dim, act_fun, act_fun2, lr, epoch, k, p, + dim_x, dim_y, channels_n, training_generator, + validation_generator, path_saved) + if hyper == 'Yes': + flag, model, save_best_weights = cnn_model_hp(epoch, k, p, dim_x, dim_y, channels_n, training_generator, + validation_generator, path_saved) + + if (not flag['check']): + + print(flag['text']) + + + else: + + Ytest_hat_lag_0, Ytest_hat_lag_1, \ + Ytest_lag_0, Ytest_lag_1 = inference(model, + save_best_weights, test_generator, Ytest_fl, Ytest, + min_val, range_val, p, path_saved, model_type) + + # Plot results + print(f'\nATTENTION!: All plots will be saved to {path0}/{filen}\n') + print('Please CLOSE all figures to continue the run\n') + for checkPoint in range(num): + index = int(checkPoint) + flag = plot_results(Ytest_hat_lag_0, Ytest_hat_lag_1, + Ytest_lag_0, Ytest_lag_1, index, + path_saved, model_type) + + if (not flag['check']): + print(flag['text']) + + checkPoint = 1 + diff --git a/v0.1_ModelFLOWs_app/GappyRepair.py b/v0.1_ModelFLOWs_app/GappyRepair.py new file mode 100644 index 0000000..7c77fb9 --- /dev/null +++ b/v0.1_ModelFLOWs_app/GappyRepair.py @@ -0,0 +1,428 @@ +def GappyRepair(): + import numpy as np + import hosvd + import os + from scipy.interpolate import griddata + from numpy import linalg as LA + import matplotlib.pyplot as plt + import data_load + import hdf5storage + import time + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + print('\nGappy SVD') + + # Load data + path0 = os.getcwd() + + print('\n-----------------------------') + print('Inputs: \n') + while True: + filetype = input('Select the gappy input file format (.mat, .npy, .csv, .pkl, .h5): ') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + A_gappy, _ = data_load.main(filetype) + + while True: + Truth = input('Would you like to select ground truth data? (y/n). Continue with No: ') + if not Truth or Truth.strip().lower() in ['n', 'no']: + break + elif Truth.strip().lower() in ['y', 'yes']: + while True: + filetype = input('Select the ground truth input file format (.mat, .npy, .csv, .pkl, .h5): ') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + Tensor, _ = data_load.main(filetype) + break + else: + print('\tError: Please select yes or no (y/n)\n') + + ## INPUTS + if A_gappy.ndim <= 2: + method = 'svd' + else: + method = 'hosvd' + + while True: + m = input('Number of modes to retain during reconstruction: ') # Number of modes to retain on the reconstruction + if m.isdigit(): + m = int(m) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + print(f''' +The selected file has {np.sum(np.isnan(A_gappy))} NaN values... + +This data must be filled in with one of the following options: +Zeros: NaN values are substituted with zeros +Mean: NaN values are substituted with the mean value of the array or tensor +Interp_1d: NaN values are substituted by interpolation (thought for arrays) +Interp_2d: NaN values are substituted by interpolation (thought for matrices) +Tensor_interp: NaN values are substituted by interpolation (thought for tensors) + +The interpolation options are: +Linear interpolation +Nearest interpolation + ''') + + while True: + decision_1 = input('How would you like to complete the missing data? (zeros, mean, interp_1d, interp_2d, tensor_interp). Continue with zeros: ') + if not decision_1 or decision_1.strip().lower() == 'zeros': + decision_1 = 'zeros' + break + elif decision_1.strip().lower() == 'mean': + decision_1 = 'mean' + break + elif decision_1.strip().lower() == 'interp_1d': + decision_1 = 'interp_1d' + break + elif decision_1.strip().lower() == 'interp_2d': + decision_1 = 'interp_2d' + break + elif decision_1.strip().lower() == 'tensor_interp': + decision_1 = 'tensor_interp' + break + else: + print('\tError: Please select a valid option\n') + + if decision_1 in ['interp_2d', 'tensor_interp']: + method_ = input('Select an interpolation method (linear/nearest): ') + if method_.strip().lower() == 'linear': + method_ = 'linear' + elif method_.strip().lower() == 'nearest': + method_ = 'nearest' + else: + print('\tError: Select a valid interpolation method\n') + else: + method_ = None + + ## OUTPUTS + print('\n-----------------------------') + print('Gappy SVD summary:') + print('\n' + f'Method used: {method.upper()}') + print(f'Number of retained modes during reconstruction: {m}') + print(f'Data completed using: {decision_1}') + print('\n-----------------------------') + print('Outputs:' + '\n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_Gappy_SVD_solution_{decision_1}_nmodes_{m}' + else: + filen = f'{filen}' + + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f"{path0}/{filen}") + + while True: + output_1 = input('Plot singular values decay before and after? (y/n). Continue with Yes: ') + if not output_1 or output_1.strip().lower() in ['y', 'yes']: + output_1 = 'yes' + break + elif output_1.strip().lower() in ['n', 'no']: + output_1 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output_2 = input('Plot surface comparison? (y/n). Continue with Yes: ') + if not output_2 or output_2.strip().lower() in ['y', 'yes']: + output_2 = 'yes' + break + elif output_2.strip().lower() in ['n', 'no']: + output_2 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + decision_2 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not decision_2 or decision_2.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + + N = sum(np.isnan(A_gappy.flatten())) + + # Initial reconstruction + print('\nCompleting data. Please wait...') + if decision_1 == 'zeros': + A0_1 = np.nan_to_num(A_gappy, nan = 0) + elif decision_1 == 'mean': + A0_1 = np.nan_to_num(A_gappy, nan = 0) + A0_1 = np.nan_to_num(A_gappy,nan=sum(A0_1.flatten())/(A0_1.size-N)) + elif decision_1 == 'interp_1d': + A0_1 = np.zeros(A_gappy.shape) + y = np.linspace(0, 1, A_gappy.shape[1]) + for j in range(np.size(A_gappy,1)): + A_gappycolumn = A_gappy[:,j] + A0_1[:,j] = np.interp(y, y[np.isfinite(A_gappy[:,j])], A_gappycolumn[np.isfinite(A_gappy[:,j])]) + elif decision_1 == 'interp_2d': + if A_gappy.ndim == 2: + x = np.linspace(0, 1, A_gappy.shape[0]) + y = np.linspace(0, 1, A_gappy.shape[1]) + xv, yv = np.meshgrid(x, y) + xnumber = xv[np.isfinite(A_gappy)] + ynumber = yv[np.isfinite(A_gappy)] + A0_1 = griddata(np.transpose(np.array([xnumber, ynumber])), A_gappy[np.isfinite(A_gappy)] , (xv, yv), method=method_) + + elif decision_1 == 'tensor_interp': + if A_gappy.ndim == 3: + shape = A_gappy.shape + A_gappy_re = np.reshape(A_gappy, (A_gappy.shape[0] * A_gappy.shape[1], A_gappy.shape[2])) + for j in range(A_gappy_re.shape[-1]): + velocity_values = A_gappy_re[:, j] + nan_mask = np.isnan(velocity_values) + + non_nan_indices = np.where(~nan_mask)[0] + + interpolated_values = griddata(non_nan_indices, velocity_values[~nan_mask], np.arange(A_gappy_re.shape[0]), method=method_) + + A_gappy_re[nan_mask, j] = interpolated_values[nan_mask] + + A0_1 = np.reshape(A_gappy_re, shape) + + if A_gappy.ndim == 4: + shape = A_gappy.shape + A_gappy_re = np.reshape(A_gappy, (A_gappy.shape[0], A_gappy.shape[1] * A_gappy.shape[2], A_gappy.shape[3])) + for i in range(A_gappy_re.shape[0]): + for j in range(A_gappy_re.shape[-1]): + velocity_values = A_gappy_re[i, :, j] + nan_mask = np.isnan(velocity_values) + + non_nan_indices = np.where(~nan_mask)[0] + + interpolated_values = griddata(non_nan_indices, velocity_values[~nan_mask], np.arange(A_gappy_re.shape[1]), method=method_) + + A_gappy_re[i, nan_mask, j] = interpolated_values[nan_mask] + + A0_1 = np.reshape(A_gappy_re, shape) + + elif A_gappy.ndim == 5: + shape = A_gappy.shape + reshaped = np.reshape(A_gappy, (A_gappy.shape[0], A_gappy.shape[1] * A_gappy.shape[2] * A_gappy.shape[3], A_gappy.shape[4])) + + x, y, z = np.meshgrid(np.arange(A_gappy.shape[1]), np.arange(A_gappy.shape[2]), np.arange(A_gappy.shape[3]), indexing='ij') + coordinates = np.stack((x, y, z), axis=-1) + + for i in range(A_gappy.shape[0]): + for j in range(A_gappy.shape[-1]): + velocity_values = reshaped[i, :, j] + + nan_mask = np.isnan(velocity_values) + + non_nan_coords = coordinates[~nan_mask] + + non_nan_values = velocity_values[~nan_mask] + + interpolated_values = griddata(non_nan_coords, non_nan_values, coordinates, method=method_) + + reshaped[i, nan_mask, j] = interpolated_values[nan_mask] + + A0_1 = np.reshape(reshaped, shape) + + print('Initial reconstruction complete!') + A_s = A0_1.copy() + MSE_gaps = np.zeros(500) + + for ii in range(500): + print(f'\nIteration number: {ii+1}') + + if method == 'svd': + print('\nPerforming SVD. Please wait...') + [U,S,V]=LA.svd(A_s) + print('SVD complete!') + S = np.diag(S) + A_reconst = U[:,0:m] @ S[0:m,0:m] @ V[0:m,:] + elif method == 'hosvd': + n = m*np.ones(np.shape(A_s.shape)) + print('\nPerforming HOSVD. Please wait...') + A_reconst = hosvd.HOSVD_function(A_s,n)[0] + print('HOSVD complete!') + + MSE_gaps[ii] = LA.norm(A_reconst[np.isnan(A_gappy)]-A_s[np.isnan(A_gappy)])/N + + if ii>3 and MSE_gaps[ii]>=MSE_gaps[ii-1]: + break + else: + A_s[np.isnan(A_gappy)] = A_reconst[np.isnan(A_gappy)] + + if output_1 or output_2 == 'yes': + print(f'\nATTENTION!: All plots will be saved to {path0}/{filen}\n') + print('Please CLOSE all figures to continue the run') + + if output_1 == 'yes': + print('\nPlotting singular values decay') + if method =='svd': + print('\nPerforming SVD. Please wait...') + [U0,S0,V0]=LA.svd(A0_1) + [U,S,V]=LA.svd(A_s) + print('SVD complete!\n') + plt.semilogy(S0/S0[0],'kx') + plt.semilogy(S/S[0],'rx') + plt.ylabel('S(i,i)/S(1,1)') + plt.legend(['Initial Reconstruction','Final Reconstruction']) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/GappyMatrix_Reconstr.png') + plt.show() + elif method == 'hosvd': + print('\nPerforming HOSVD. Please wait...') + sv0 = hosvd.HOSVD_function(A0_1,n)[3] + sv = hosvd.HOSVD_function(A_s,n)[3] + print('HOSVD complete!\n') + cmap = plt.cm.get_cmap('jet') + rgba = cmap(np.linspace(0,1,A_s.ndim)) + for i in range(A_s.ndim): + plt.semilogy(sv0[0,i]/sv0[0,i][0], linestyle = 'none', marker = 'x',color = rgba[i]) + plt.semilogy(sv[0,i]/sv[0,i][0], linestyle = 'none', marker = '+', color = rgba[i]) + plt.legend(['Original Reconstruction','Final Reconstruction']) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/GappyTensor_Reconstr.png') + plt.show() + + if Truth.strip().lower() in ['y', 'yes']: + if Tensor.ndim == 2: + Tensor0 = Tensor[:A_s.shape[0], :A_s.shape[1]].copy() + RRMSE = np.linalg.norm(np.reshape(Tensor0 - A_s ,newshape=(np.size(Tensor0),1)),ord=2)/np.linalg.norm(np.reshape(Tensor0,newshape=(np.size(Tensor0),1))) + print(f'\nError made during reconstruction: {np.round(RRMSE*100, 3)}%') + + if Tensor.ndim == 3: + Tensor0 = Tensor[:A_s.shape[0], :A_s.shape[1], :A_s.shape[2]].copy() + RRMSE = np.linalg.norm(np.reshape(Tensor0 - A_s ,newshape=(np.size(Tensor0),1)),ord=2)/np.linalg.norm(np.reshape(Tensor0,newshape=(np.size(Tensor0),1))) + print(f'\nError made during reconstruction: {np.round(RRMSE*100, 3)}%') + + if Tensor.ndim == 4: + Tensor0 = Tensor[:A_s.shape[0], :A_s.shape[1], :A_s.shape[2], :A_s.shape[3]].copy() + RRMSE = np.linalg.norm(np.reshape(Tensor0 - A_s ,newshape=(np.size(Tensor0),1)),ord=2)/np.linalg.norm(np.reshape(Tensor0,newshape=(np.size(Tensor0),1))) + print(f'\nError made during reconstruction: {np.round(RRMSE*100, 3)}%') + + if Tensor.ndim == 5: + Tensor0 = Tensor[:A_s.shape[0], :A_s.shape[1], :A_s.shape[2], :A_s.shape[3], :A_s.shape[4]].copy() + RRMSE = np.linalg.norm(np.reshape(Tensor0 - A_s ,newshape=(np.size(Tensor0),1)),ord=2)/np.linalg.norm(np.reshape(Tensor0,newshape=(np.size(Tensor0),1))) + print(f'\nError made during reconstruction: {np.round(RRMSE*100, 3)}%') + + if output_2 == 'yes': + if method == 'hosvd': + if A_gappy.ndim == 5: + nz = int(A_gappy.shape[3] / 2) + for var in range(A_gappy.shape[0]): + fig, ax = plt.subplots(figsize=(10, 10), num = f'CLOSE TO CONTINUE RUN - XY plane initial data for component {var+1}') + heatmap = ax.imshow(A_gappy[var, ..., nz, 0], cmap='coolwarm') + ax.set_title(f'XY Plane initial gappy data - Component {var+1}') + ax.set_xlabel('X') + ax.set_ylabel('Y') + heatmap.set_clim(np.nanmin(A_gappy[var, ..., nz, 0]), np.nanmax(A_gappy[var, ..., nz, 0])) + plt.show() + if A_gappy.ndim == 4: + for var in range(A_gappy.shape[0]): + fig, ax = plt.subplots(figsize=(10, 10), num = f'CLOSE TO CONTINUE RUN - Initial data for component {var+1}') + heatmap = ax.imshow(A_gappy[var, ..., 0], cmap='coolwarm') + ax.set_title(f'Initial gappy data - Component {var+1}') + ax.set_xlabel('X') + ax.set_ylabel('Y') + heatmap.set_clim(np.nanmin(A_gappy[var, ..., 0]), np.nanmax(A_gappy[var, ..., 0])) + plt.show() + elif A_gappy.ndim == 3: + fig, ax = plt.subplots(figsize=(10, 10), num = 'CLOSE TO CONTINUE RUN - Initial data') + heatmap = ax.imshow(A_gappy[..., 0], cmap='coolwarm') + ax.set_title('Initial gappy data') + ax.set_xlabel('X') + ax.set_ylabel('Y') + heatmap.set_clim(np.nanmin(A_gappy[..., 0]), np.nanmax(A_gappy[..., 0])) + plt.show() + + elif method == 'svd': + fig, ax = plt.subplots(figsize=(10, 10), num = 'CLOSE TO CONTINUE RUN - Initial data') + heatmap = ax.imshow(A_gappy, cmap='coolwarm') + ax.set_title('Initial gappy data') + ax.set_xlabel('X') + ax.set_ylabel('Y') + heatmap.set_clim(np.nanmin(A_gappy), np.nanmax(A_gappy)) + plt.show() + + # Initial and final reconstruction vs ground truth + if Truth.strip().lower() in ['y', 'yes']: + ncols = 3 + else: + ncols = 2 + + if method == 'svd': + fig, ax = plt.subplots(1, ncols, num = 'CLOSE TO CONTINUE RUN - Data comparison') + plt.suptitle('Data comparison') + ax[0].contourf(A_gappy) + ax[0].set_title('Initial data') + ax[1].contourf(A_s) + ax[1].set_title('Reconstructed data') + if ncols == 3: + ax[2].contourf(Tensor) + ax[2].set_title('Ground truth') + plt.savefig(f'{path0}/{filen}/data_comparison.png') + plt.show() + + if method == 'hosvd': + if A_gappy.ndim == 4: + fig, ax = plt.subplots(1, ncols, num = 'CLOSE TO CONTINUE RUN - Data comparison') + plt.suptitle('Data comparison') + im0 = ax[0].contourf(A_gappy[0, ..., 0]) + ax[0].set_title('Initial data') + im1 = ax[1].contourf(A_s[0, ..., 0]) + ax[1].set_title('Reconstructed data') + if ncols == 3: + im2 = ax[2].contourf(Tensor[0, ..., 0]) + ax[2].set_title('Ground truth') + fig.colorbar(im2, ax=ax[2]) + else: + fig.colorbar(im1, ax=ax[1]) + plt.savefig(f'{path0}/{filen}/data_comparison.png') + plt.show() + + if A_gappy.ndim == 5: + nz = int(A_gappy.shape[3] / 2) + fig, ax = plt.subplots(1, ncols, num = 'CLOSE TO CONTINUE RUN - Data comparison') + plt.suptitle('XY plane data comparison') + ax[0].contourf(A_gappy[0, ..., nz, 0]) + ax[0].set_title('Initial data') + ax[1].contourf(A_s[0, ..., nz, 0]) + ax[1].set_title('Reconstructed data') + if ncols == 3: + ax[2].contourf(Tensor[0, ..., nz, 0]) + ax[2].set_title('Ground truth') + plt.savefig(f'{path0}/{filen}/data_comparison.png') + plt.show() + + if A_gappy.ndim == 3: + fig, ax = plt.subplots(1, ncols, num = 'CLOSE TO CONTINUE RUN - Data comparison') + plt.suptitle('Data comparison') + ax[0].contourf(A_gappy[..., 0]) + ax[0].set_title('Initial data') + ax[1].contourf(A_s[..., 0]) + ax[1].set_title('Reconstructed data') + if ncols == 3: + ax[2].contourf(Tensor[..., 0]) + ax[2].set_title('Ground truth') + plt.savefig(f'{path0}/{filen}/data_comparison.png') + plt.show() + + if not decision_2 or decision_2.strip().lower() in ['npy', '.npy']: + np.save(f'{path0}/{filen}/reconstructed.npy', A_s) + + if decision_2.strip().lower() in ['.mat', 'mat']: + mdic1 = {"reconstructed": A_s} + file_mat1 = str(f'{path0}/{filen}/reconstructed.mat') + hdf5storage.savemat(file_mat1, mdic1, appendmat=True, format='7.3') + + + + + + + + diff --git a/v0.1_ModelFLOWs_app/HODMD.py b/v0.1_ModelFLOWs_app/HODMD.py new file mode 100644 index 0000000..3f1f7c1 --- /dev/null +++ b/v0.1_ModelFLOWs_app/HODMD.py @@ -0,0 +1,636 @@ +def HODMD(): + import DMDd + import os + import numpy as np + import matplotlib.pyplot as plt + from math import floor + os.environ['KMP_DUPLICATE_LIB_OK']='True' + import data_load + import scipy.io + import hdf5storage + import time + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + import matplotlib.animation as animation + + import warnings + warnings.filterwarnings("ignore", message="Casting complex values to real discards the imaginary part") + + + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def video(Tensor, vel, Title): + + nt = Tensor.shape[-1] + + if nt in range(200, 500): + Tensor[..., ::5] + + elif nt > 500: + Tensor[..., ::15] + + frames = Tensor.shape[-1] + + fig, ax = plt.subplots(figsize = (8, 4), num = f'CLOSE TO CONTINUE RUN - {Title}') + fig.tight_layout() + + def animate(i): + ax.clear() + ax.contourf(Tensor[vel, :, :, i]) + ax.set_title(Title) + + interval = 2 + anim = animation.FuncAnimation(fig, animate, frames = frames, interval = interval*1e+2, blit = False) + + plt.show() + + print('\nHODMD\n') + + ################### Input ################### + print('-----------------------------') + print('Inputs:' + '\n') + + ##Snapshot matrix Tensor: + + path0 = os.getcwd() + + while True: + filetype = input('Select the input file format (.mat, .npy, .csv, .pkl, .h5): ') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + Tensor, _ = data_load.main(filetype) + + ##Number of snapshots SNAP: + while True: + SNAP = input(f'Introduce number of snapshots. Continue with {Tensor.shape[-1]} snapshots: ') + if not SNAP: + SNAP = int(Tensor.shape[-1]) + break + elif SNAP.isdigit(): + SNAP = int(SNAP) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + ##d Parameter: + while True: + print(f'Interval of recommended number of HODMD windows (d): [{int(np.round(SNAP/10))}, {int(np.round(SNAP/2))}]. Other values are accepted') + d = input(f'Introduce number of HODMD windows (d): ') + if d.isdigit(): + d = int(d) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + ## Tolerances: + while True: + varepsilon1 = input('Introduce first tolerance (SVD). Continue with 1e-10: ') + if not varepsilon1: + varepsilon1 = 1e-10 + break # SVD + elif is_float(varepsilon1): + varepsilon1 = float(varepsilon1) + break + else: + print('\tError: Please introduce a number\n') + + while True: + varepsilon = input('Introduce second tolerance (HODMD). Continue with 1e-3: ') + if not varepsilon: + varepsilon = 1e-3 + break # DMD + elif is_float(varepsilon): + varepsilon = float(varepsilon) + break + else: + print('\tError: Please introduce a number\n') + + while True: + deltaT = input('Introduce time step (deltaT). Continue with 1: ') + if not deltaT: + deltaT = 1 + break + elif is_float(deltaT): + deltaT = float(deltaT) + break + else: + print('\tError: Please introduce a number\n') + + + print('\n-----------------------------') + print('HODMD summary:') + print('\n' + f'Number of snapshots set at: {SNAP}') + print(f'd Parameter set at: {d}') + print(f'Tolerances set at {varepsilon1} for SVD and {varepsilon} for HODMD') + print(f'Time gradient set at deltaT: {deltaT}') + #----------------------------------------------------------------------------------------------------------------------------------- + ################### Output ################### + + print('\n-----------------------------') + print('Outputs:' + '\n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_HODMD_solution' + else: + filen = f'{filen}' + + while True: + decision_2 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not decision_2 or decision_2.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + + print('') + + # Create new folder: + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f'{path0}/{filen}') + if not os.path.exists(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}'): + os.mkdir(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}') + if not os.path.exists(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes'): + os.mkdir(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes') + + + Time = np.linspace(0,SNAP-1,num=SNAP)*deltaT + Tensor = Tensor[..., :SNAP] + Tensor0 = Tensor.copy() + dims = Tensor.ndim + shape = Tensor.shape + + if dims > 2: + dims_prod = np.prod(shape[:-1]) + Tensor = np.reshape(Tensor, (dims_prod, shape[-1])) + + notone=0 + for i in range(0,np.size(np.shape(Tensor))): + if np.shape(Tensor)[i] != 1: + notone=notone+1 + + if notone <= 2: + if d==1: + print('Performing DMD. Please wait...\n') + [u,Amplitude,Eigval,GrowthRate,Frequency,DMDmode] = DMDd.dmd1(Tensor, Time, varepsilon1, varepsilon) + print('\nDMD complete!') + dt=Time[1]-Time[0] + icomp=complex(0,1) + mu=np.zeros(np.size(GrowthRate),dtype=np.complex128) + for iii in range(0,np.size(GrowthRate)): + mu[iii] = np.exp(np.dot(dt,GrowthRate[iii]+np.dot(icomp,Frequency[iii]))) + Reconst=DMDd.remake(u,Time,mu) + else: + print('Performing HODMD. Please wait...\n') + [u,Amplitude,Eigval,GrowthRate,Frequency,DMDmode] = DMDd.hodmd(Tensor, d, Time, varepsilon1, varepsilon) + print('\nHODMD complete!') + dt=Time[1]-Time[0] + icomp=complex(0,1) + mu=np.zeros(np.size(GrowthRate),dtype=np.complex128) + for iii in range(0,np.size(GrowthRate)): + mu[iii] = np.exp(np.dot(dt,GrowthRate[iii]+np.dot(icomp,Frequency[iii]))) + Reconst=DMDd.remake(u,Time,mu) + + if dims <= 2: + # RMS Error: + Norm2V = np.linalg.norm(Tensor0.flatten(), 2) + diff = (Tensor0 - Reconst).flatten() + Norm2diff = np.linalg.norm(diff, ord=2) + RelativeErrorRMS = Norm2diff/Norm2V + print('\n' + f'The relative error (RMS) is: ' + format(RelativeErrorRMS,'e')) + + # Max Error: + NormInfV = np.linalg.norm(Tensor0.flatten(), ord=1) + NormInfdiff = np.linalg.norm(diff, ord=1) + RelativeErrorMax = NormInfdiff/NormInfV + print(f'\nThe relative error (MAX) is: ' + format(RelativeErrorMax, 'e') + '\n') + + elif dims > 2: + newshape = [] + newshape.append(shape[:-1]) + newshape.append(DMDmode.shape[-1]) + newshape = list(newshape[0]) + [newshape[1]] + DMDmode = np.reshape(DMDmode, np.array(newshape)) + Reconst = np.reshape(Reconst, shape) + RRMSE = np.linalg.norm(np.reshape(Tensor0-Reconst,newshape=(np.size(Tensor0),1)),ord=2)/np.linalg.norm(np.reshape(Tensor0,newshape=(np.size(Tensor0),1))) + print(f'\nRelative mean square error made in the calculations: {np.round(RRMSE*100, 3)}%\n') + + GRFreqAmp = np.zeros((np.size(GrowthRate),3)) + for ind in range (0,np.size(GrowthRate)): + GRFreqAmp[ind,0] = GrowthRate[ind] + GRFreqAmp[ind,1] = Frequency[ind] + GRFreqAmp[ind,2] = Amplitude[ind] + + print('GrowthRate, Frequency and Amplitude:') + print(GRFreqAmp) + + if not decision_2 or decision_2.strip().lower() in ['npy', '.npy']: + np.save(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/Reconst.npy', Reconst) + np.save(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmodes.npy', DMDmode) + + + if decision_2.strip().lower() in ['.mat', 'mat']: + mdic = {"Reconst": Reconst} + file_mat = str(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/Reconst.mat') + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + mdic1 = {"DMDmodes": DMDmode} + file_mat1 = str(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes.mat') + hdf5storage.savemat(file_mat1, mdic1, appendmat=True, format='7.3') + + # Result plots: + print(f'\nATTENTION!: All plots will be saved to {path0}/{filen}/d_{d}_tol_{varepsilon1}\n') + print('Please CLOSE all figures to continue the run') + + plt.figure(num='CLOSE TO CONTINUE RUN - Frequency/GrowthRate') + plt.plot(Frequency,GrowthRate, 'k+') + plt.yscale('log') + plt.xlabel('Frequency ($\omega_{n}$)') + plt.ylabel('GrowthRate ($\delta_{n}$)') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/GrFreq.png') + plt.show() + plt.close() + + plt.figure(num='CLOSE TO CONTINUE RUN - Frequency/Amplitude') + plt.plot(Frequency, Amplitude/np.amax(Amplitude),'r+') + plt.yscale('log') # Logarithmic scale in y axis + plt.xlabel('Frequency ($\omega_{n}$)') + plt.ylabel('Amplitude divided by max. amplitude ($a_{n}$)') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/FrAmp.png') + plt.show() + plt.close() + + if dims <=2: + print('\nPlotting component comparison') + plt.figure(num='CLOSE TO END RUN - Reconstruction') + plt.plot(Time[:],Reconst[0,:], 'k-x', label = 'Reconstructed Data') + plt.plot(Time[:],Tensor0[0,:], 'r-+', label = 'Real Data') + plt.xlabel('Time') + plt.ylabel('Data') + plt.title('Real Data vs. Reconstructed Data') + plt.legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/OrigReconst.png') + plt.show() + plt.close() + + print(f'\nSaving DMDmodes plots to {path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes\n') + + fig, ax = plt.subplots(1, 2) + fig.suptitle(f'DMDmode') + ax[0].contourf(DMDmode.real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode.imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmode.png') + plt.show() + + print(f'\nSaving first 3 DMDmode plots to {path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes\n') + if dims == 3: + for t in range(3): + fig, ax = plt.subplots(1, 2) + fig.suptitle(f'DMDmode') + ax[0].contourf(DMDmode[..., t].real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode[..., t].imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmode_{t+1}.png') + plt.show() + + print('\nPlotting component comparison') + while True: + while True: + x = input(f'Select X coordinate (must be in range [0, {Tensor0.shape[1] - 1}]): ') + if x.isdigit(): + if int(x) in range(0, Tensor0.shape[1]): + x = int(x) + break + else: + print('\tError: Selected value is out of bounds\n') + elif not x: + continue + while True: + y = input(f'Select Y coordinate (must be in range [0, {Tensor0.shape[0] - 1}]): ') + if y.isdigit(): + if int(y) in range(0, Tensor0.shape[0]): + y = int(y) + break + else: + print('\tError: Selected value is out of bounds\n') + elif not y: + continue + else: + print('\tError: Select a valid number format (must be integer)\n') + + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - Component comparison') + fig.suptitle(f'Real Data vs Reconstruction') + ax[0].contourf(Tensor0[:, :, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(Time[:], Reconst[y, x, :], 'k-x', label = 'Reconstructed Data') + ax[1].plot(Time[:], Tensor0[y, x, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/OrigReconst.png') + plt.show() + plt.close() + + while True: + Resp = input('Do you want to plot another component? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + if Resp == 1: + continue + + if dims > 3: + for ModComp in range(DMDmode.shape[0]): + for ModeNum in range(3): + if DMDmode.ndim == 4: + fig, ax = plt.subplots(1, 2) + fig.suptitle(f'DMDmode - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[ModComp,:,:,ModeNum].real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode[ModComp,:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmodeComp_{ModComp+1}_ModeNum_{ModeNum+1}.png') + plt.close(fig) + + elif DMDmode.ndim == 5: + nz = int(Tensor0.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode XY plane') + fig.suptitle(f'DMDmode XY plane - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[ModComp,:,:,nz,ModeNum].real) + ax[0].set_title('Real part - XY Plane') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode[ModComp,:,:,nz,ModeNum].imag) + ax[1].set_title('Imaginary part - XY Plane') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmode_XY_Comp_{ModComp+1}_ModeNum_{ModeNum+1}.png') + plt.close(fig) + + print('Plotting component comparison') + while True: + while True: + c = input(f'Select a component (max is {Tensor0.shape[0]}): ') + if c.isdigit(): + if int(c) <= Tensor0.shape[0]: + c = int(c) - 1 + break + else: + print("\tError: Selected component doesn't exist\n") + elif not c: + continue + + while True: + x = input(f'Select X coordinate (must be in range [0, {Tensor0.shape[2] - 1}]): ') + if x.isdigit(): + if int(x) in range(0, Tensor0.shape[2]): + x = int(x) + break + else: + print('\tError: Selected value is out of bounds\n') + elif not x: + continue + + while True: + y = input(f'Select Y coordinate (must be in range [0, {Tensor0.shape[1] - 1}]): ') + if y.isdigit(): + if int(y) in range(0, Tensor0.shape[1]): + y = int(y) + break + else: + print('\tError: Selected value is out of bounds\n') + elif not y: + continue + else: + print('\tError: Select a valid number format (must be integer)\n') + if dims == 4: + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - Component comparison') + fig.suptitle(f'Real Data vs Reconstruction - Component {c+1}') + ax[0].contourf(Tensor0[c, :, :, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(Time[:], Reconst[c, y, x, :], 'k-x', label = 'Reconstructed Data') + ax[1].plot(Time[:], Tensor0[c, y, x, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/OrigReconst.png') + plt.show() + plt.close() + + elif dims == 5: + nz = int(Tensor0.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - Component comparison') + fig.suptitle(f'Real Data vs Reconstruction - XY plane - Component {c+1}') + ax[0].contourf(Tensor0[c, :, :, nz, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(Time[:], Reconst[c, y, x, nz, :], 'k-x', label = 'Reconstructed Data') + ax[1].plot(Time[:], Tensor0[c, y, x, nz, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/OrigReconst.png') + plt.show() + plt.close() + + while True: + Resp = input('Do you want to plot another component? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + if Resp == 1: + continue + + + if not os.path.exists(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes'): + os.mkdir(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes') + + print(f'Select component and temporal mode to plot a DMD mode') + while True: + while True: + ModeNum = input(f'Introduce the DMD mode to plot (default is 1). Maximum number of modes is {DMDmode.shape[-1]}: ') + if not ModeNum: + ModeNum = 0 + break + elif ModeNum.isdigit(): + if int(ModeNum) <= DMDmode.shape[-1]: + ModeNum = int(ModeNum)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + while True: + ModComp = input(f'Introduce the component to plot (default component 1). Maximum number of components is {DMDmode.shape[0]}: ') + if not ModComp: + ModComp = 0 + break + elif ModComp.isdigit(): + if int(ModComp) <= DMDmode.shape[0]: + ModComp = int(ModComp)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + if dims==4: + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode') + fig.suptitle(f'DMDmode - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[ModComp,:,:,ModeNum].real) + ax[0].set_title('Real part') + + ax[1].contourf(DMDmode[ModComp,:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + plt.show() + + elif dims==5: + nz = int(Tensor0.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode XY plane') + fig.suptitle(f'DMDmode XY plane - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[ModComp,:,:,nz,ModeNum].real) + ax[0].set_title('Real part - XY Plane') + + ax[1].contourf(DMDmode[ModComp,:,:,nz,ModeNum].imag) + ax[1].set_title('Imaginary part - XY Plane') + plt.show() + + while True: + Resp = input('Do you want to plot another mode? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 1: + continue + + elif Resp == 0: + break + + while True: + dec4 = input(f'Plot video of original data and reconstructed data? (y/n). Continue with Yes: ') + if not dec4 or dec4.strip().lower() in ['y', 'yes']: + decision4 = True + break + if dec4.strip().lower() in ['n', 'no']: + decision4 = False + return + else: + print('\tError: Please select yes or no (y/n)\n') + + if dims == 5: + while True: + nz = int(Tensor0.shape[3] / 2) + plane = input('Select a plane (XY, XZ, YZ)') + if plane.strip().lower() == 'xy': + Tensor0 = Tensor0[:, :, :, nz, :] + Reconst = Reconst[:, :, :, nz, :] + break + elif plane.strip().lower() == 'xz': + Tensor0 = Tensor0[:, :, 0, :, :] + Reconst = Reconst[:, :, 0, :, :] + break + elif plane.strip().lower() == 'yz': + Tensor0 = Tensor0[:, 0, :, :, :] + Reconst = Reconst[:, 0, :, :, :] + break + else: + print('\tError: Select a valid plane\n') + + else: + pass + + titles = [] + [titles.append(f'Component {i+1}') for i in range(Tensor0.shape[0])] + + while True: + if decision4 == True: + vidvel = input(f'Select a component (max is {Tensor0.shape[0]}). Continue with component 1: ') + if not vidvel: + vel = 0 + video(Tensor0, vel, Title = f'Original Data - {titles[vel]}') + video(Reconst, vel, Title = f'Reconstructed data - {titles[vel]}') + break + elif vidvel.isdigit(): + if int(vidvel) <= Tensor0.shape[0]: + vel = int(vidvel) - 1 + video(Tensor0, vel, Title = f'Original Data - {titles[vel]}') + video(Reconst, vel, Title = f'Reconstructed data - {titles[vel]}') + break + else: + print("\tError: Select a valid component\n") + else: + print('\tError: Introduce a valid format (must be integer)\n') + + while True: + ch1 = input('Would you like to plot another component? (y/n). Continue with No: ') + if ch1.strip().lower() in ['y', 'yes']: + while True: + vidvel = input(f'Select a component (max is {Tensor0.shape[0]}): ') + if vidvel.isdigit(): + if int(vidvel) <= Tensor0.shape[0]: + vel = int(vidvel) - 1 + video(Tensor0, vel, Title = f'Original Data - {titles[vel]}') + video(Reconst, vel, Title = f'Reconstructed data - {titles[vel]}') + break + else: + print("\tError: Select a valid component\n") + else: + print('\tError: Introduce a valid format (must be integer)\n') + continue + elif not ch1 or ch1.strip().lower() in ['n', 'no']: + return + + else: + print('\tError: Select yes or no (y/n)\n') \ No newline at end of file diff --git a/v0.1_ModelFLOWs_app/HODMD_pred.py b/v0.1_ModelFLOWs_app/HODMD_pred.py new file mode 100644 index 0000000..b38988f --- /dev/null +++ b/v0.1_ModelFLOWs_app/HODMD_pred.py @@ -0,0 +1,735 @@ +def HODMDpred(): + import DMDd + import warnings + warnings.filterwarnings("ignore", message="Casting complex values to real discards the imaginary part") + import os + import numpy as np + import matplotlib.pyplot as plt + import matplotlib.animation as animation + from math import floor + os.environ['KMP_DUPLICATE_LIB_OK']='True' + import data_load + import hdf5storage + import time + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + print('\nPredictive HODMD') + + ################### Input ################### + print('\n-----------------------------') + print('Inputs:' + '\n') + + ##Snapshot matrix Tensor: + + path0 = os.getcwd() + + + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def video(Tensor, vel, Title): + + nt = Tensor.shape[-1] + + if nt in range(200, 500): + Tensor[..., ::5] + + elif nt > 500: + Tensor[..., ::15] + + frames = Tensor.shape[-1] + + fig, ax = plt.subplots(figsize = (8, 4), num = f'CLOSE TO CONTINUE RUN - {Title}') + fig.tight_layout() + + def animate(i): + ax.clear() + ax.contourf(Tensor[vel, :, :, i]) + ax.set_title(Title) + + interval = 2 + anim = animation.FuncAnimation(fig, animate, frames = frames, interval = interval*1e+2, blit = False) + + plt.show() + + while True: + filetype = input('Select the input file format (.mat, .npy, .csv, .pkl, .h5): ') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + Tensor, _ = data_load.main(filetype) + + ##Number of snapshots SNAP: + while True: + SNAP = input(f'Introduce number of snapshots. Continue with {Tensor.shape[-1]} snapshots: ') + if not SNAP: + SNAP = int(Tensor.shape[-1]) + break + elif SNAP.isdigit(): + SNAP = int(SNAP) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + ##d Parameter: + while True: + print(f'Interval of recommended number of HODMD windows (d): [{int(np.round(SNAP/10))}, {int(np.round(SNAP/2))}]. Other values are accepted') + d = input(f'Introduce number of HODMD windows (d): ') + if d.isdigit(): + d = int(d) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + ## Tolerances: + while True: + varepsilon1 = input('Introduce first tolerance (SVD). Continue with 1e-10: ') + if not varepsilon1: + varepsilon1 = 1e-10 + break # SVD + elif is_float(varepsilon1): + varepsilon1 = float(varepsilon1) + break + else: + print('\tError: Please introduce a number\n') + + while True: + varepsilon = input('Introduce second tolerance (HODMD). Continue with 1e-3: ') + if not varepsilon: + varepsilon = 1e-3 + break # DMD + elif is_float(varepsilon): + varepsilon = float(varepsilon) + break + else: + print('\tError: Please introduce a number\n') + + while True: + deltaT = input('Introduce time step (deltaT). Continue with 1: ') + if not deltaT: + deltaT = 1 + break + elif is_float(deltaT): + deltaT = float(deltaT) + break + else: + print('\tError: Please introduce a number\n') + + Time = np.linspace(0,SNAP-1,num=SNAP)*deltaT + + print(f'The temporal interval of the data analysed is [{Time[0]}, {Time[-1]}]') + while True: + TimeFin = input('Introduce final time to predict your solution. Continue with reconstruction: ') + if not TimeFin: + TimePred=Time + break + elif is_float(TimeFin): + if float(TimeFin) >= Time[-1]: + TimeFin = float(TimeFin) / deltaT + SnapDif=floor(abs(TimeFin-Time[-1])*deltaT) + TimePred = np.linspace(0,SNAP+SnapDif-1,num=SNAP+SnapDif)*deltaT + break + else: + print(f'\tError: Introduce a time higher than {Time[-1]}\n') + else: + print('\tError: Please introduce a number\n') + + # Set tolerance for growth rates + TolGR=0 + while True: + TolGRset = input('Do you want to retain the modes with growth rate (in absolute value) < tolerance? Yes or No (y/n). Continue with Yes: ') + if TolGRset.strip().lower() in ['y', 'yes'] or not TolGRset: + TolGR=1 + TolGRvalue = input('Introduce the tolerance for the growth rate of the modes retained. Continue with 0.5: ') + if not TolGRvalue: + TolGRvalue = 0.5 + break + elif is_float(TolGRvalue): + TolGRvalue = float(TolGRvalue) + break + else: + print('\tError: Please introduce a number\n') + elif TolGRset.strip().lower() in ['n', 'no']: + break + else: + print('\tError: Select yes or no (y/n)\n') + + + while True: + TolGRset2 = input('Do you want to set the growth rate to 0 for the temporal predictions? Yes or No (y/n). Continue with No: ') + if TolGRset2.strip().lower() in ['n', 'no'] or not TolGRset2: + GRset=0 + break + elif TolGRset2.strip().lower() in ['y', 'yes']: + GRset=1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + print('\n-----------------------------') + print('HODMD prediction summary:') + print('\n' + f'Number of snapshots set at: {SNAP}') + print(f'd Parameter set at: {d}') + print(f'Tolerances set at {varepsilon1} for SVD and {varepsilon} for HODMD') + print(f'Time gradient set at deltaT: {deltaT}') + + ################### Output ################### + + print('\n-----------------------------') + print('Outputs:' + '\n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_HODMD_pred_solution' + else: + filen = f'{filen}' + + while True: + decision_2 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not decision_2 or decision_2.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + + print('') + + # Create new folder: + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f'{path0}/{filen}') + if not os.path.exists(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}'): + os.mkdir(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}') + if not os.path.exists(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes'): + os.mkdir(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes') + + Tensor = Tensor[..., :SNAP] + Tensor0 = Tensor.copy() + dims = Tensor.ndim + shape = Tensor.shape + + if dims > 2: + dims_prod = np.prod(shape[:-1]) + Tensor = np.reshape(Tensor, (dims_prod, shape[-1])) + + notone=0 + for i in range(0,np.size(np.shape(Tensor))): + if np.shape(Tensor)[i] != 1: + notone=notone+1 + + if notone <= 2: + if d==1: + print('Performing DMD. Please wait...\n') + [u,Amplitude,Eigval,GrowthRate,Frequency,DMDmode] = DMDd.dmd1(Tensor, Time, varepsilon1, varepsilon) + print('\nDMD complete!') + dt=Time[1]-Time[0] + icomp=complex(0,1) + if TolGR==1: + ind = np.where(np.abs(GrowthRate) 2: + newshape = [] + newshape.append(shape[:-1]) + newshape.append(DMDmode.shape[-1]) + newshape = list(newshape[0]) + [newshape[1]] + DMDmode = np.reshape(DMDmode, np.array(newshape)) + newshape1 = [] + newshape1.append(shape[:-1]) + newshape1.append(Reconst.shape[-1]) + newshape1 = list(newshape1[0]) + [newshape1[1]] + Reconst = np.reshape(Reconst, np.array(newshape1)) + + RRMSE = np.linalg.norm(np.reshape(Tensor0-Reconst[..., :SNAP],newshape=(np.size(Tensor0),1)),ord=2)/np.linalg.norm(np.reshape(Tensor0,newshape=(np.size(Tensor0),1))) + print(f'\nRelative mean square error made in the calculations: {np.round(RRMSE*100, 3)}%') + + if not decision_2 or decision_2.strip().lower() in ['npy', '.npy']: + np.save(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/Reconst.npy', Reconst) + np.save(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmodes.npy', DMDmode) + + + if decision_2.strip().lower() in ['.mat', 'mat']: + mdic = {"Reconst": Reconst} + file_mat = str(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/Reconst.mat') + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + mdic1 = {"DMDmodes": DMDmode} + file_mat1 = str(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes.mat') + hdf5storage.savemat(file_mat1, mdic1, appendmat=True, format='7.3') + + # Result plots: + print(f'\nATTENTION!: All plots will be saved to {path0}/{filen}/d_{d}_tol_{varepsilon1}\n') + print('Please CLOSE all figures to continue the run') + + plt.figure(num='CLOSE TO CONTINUE RUN - Frequency/GrowthRate') + plt.plot(Frequency,GrowthRate, 'k+') + plt.yscale('log') + plt.xlabel('Frequency ($\omega_{n}$)') + plt.ylabel('GrowthRate ($\delta_{n}$)') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/GrFreq.png') + plt.show() + plt.close() + + plt.figure(num='CLOSE TO CONTINUE RUN - Frequency/Amplitude') + plt.plot(Frequency, Amplitude/np.amax(Amplitude),'r+') + plt.yscale('log') # Logarithmic scale in y axis + plt.xlabel('Frequency ($\omega_{n}$)') + plt.ylabel('Amplitude divided by max. amplitude ($a_{n}$)') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/FrAmp.png') + plt.show() + plt.close() + + if dims <=2: + print('\nPlotting component comparison') + plt.figure(num='CLOSE TO END RUN - Prediction') + plt.plot(TimePred[:],Reconst[0,:], 'k-x', label = 'Predicted Data') + plt.plot(Time[:],Tensor0[0,:], 'r-+', label = 'Real Data') + plt.xlabel('Time') + plt.ylabel('Data') + plt.title('Real Data vs. Predicted Data') + plt.tight_layout() + plt.legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/OrigPred.png') + plt.show() + plt.close() + + print(f'\nSaving DMDmode plot to {path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes\n') + + fig, ax = plt.subplots(1, 2) + fig.suptitle(f'DMDmode') + ax[0].contourf(DMDmode.real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode.imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmode.png') + plt.show() + + print(f'\nSaving first 3 DMDmode plots to {path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes\n') + + if dims == 3: + for ModeNum in range(3): + fig, ax = plt.subplots(1, 2) + fig.suptitle(f'DMDmode - Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[:,:,ModeNum].real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode[:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/ModeNum_{ModeNum+1}.png') + plt.show() + + print('\nPlotting component comparison') + while True: + while True: + x = input(f'Select X coordinate (must be in range [0, {Tensor0.shape[1] - 1}]): ') + if x.isdigit(): + if int(x) in range(0, Tensor0.shape[1]): + x = int(x) + break + else: + print('\tError: Selected value is out of bounds\n') + elif not x: + continue + + while True: + y = input(f'Select Y coordinate (must be in range [0, {Tensor0.shape[0] - 1}]): ') + if y.isdigit(): + if int(y) in range(0, Tensor0.shape[0]): + y = int(y) + break + else: + print('\tError: Selected value is out of bounds\n') + elif not y: + continue + else: + print('\tError: Select a valid number format (must be integer)\n') + + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - Component comparison') + fig.suptitle(f'Real Data vs Prediction') + ax[0].contourf(Tensor0[:, :, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(TimePred[:], Reconst[y, x, :], 'k-x', label = 'Predicted Data') + ax[1].plot(Time[:], Tensor0[y, x, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/OrigPred.png') + plt.show() + plt.close() + + while True: + Resp = input('Do you want to plot another component? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + if Resp == 1: + continue + + if dims > 3: + for ModComp in range(DMDmode.shape[0]): + for ModeNum in range(3): + if DMDmode.ndim == 4: + fig, ax = plt.subplots(1, 2) + fig.suptitle(f'DMDmode - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[ModComp,:,:,ModeNum].real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode[ModComp,:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmodeComp_{ModComp+1}_ModeNum_{ModeNum+1}.png') + plt.close(fig) + + elif DMDmode.ndim == 5: + nz = int(Tensor0.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode XY plane') + fig.suptitle(f'DMDmode XY plane - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[ModComp,:,:,nz,ModeNum].real) + ax[0].set_title('Real part - XY Plane') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode[ModComp,:,:,nz,ModeNum].imag) + ax[1].set_title('Imaginary part - XY Plane') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmode_XY_Comp_{ModComp+1}_ModeNum_{ModeNum+1}.png') + plt.close(fig) + + print('\nPlotting component comparison') + while True: + while True: + c = input(f'Select a component (max is {Tensor0.shape[0]}): ') + if c.isdigit(): + if int(c) <= Tensor0.shape[0]: + c = int(c) - 1 + break + else: + print("\tError: Selected component doesn't exist\n") + elif not c: + continue + + while True: + x = input(f'Select X coordinate (must be in range [0, {Tensor0.shape[2] - 1}]): ') + if x.isdigit(): + if int(x) in range(0, Tensor0.shape[2]): + x = int(x) + break + else: + print('\tError: Selected value is out of bounds\n') + elif not x: + continue + + while True: + y = input(f'Select Y coordinate (must be in range [0, {Tensor0.shape[1] - 1}]): ') + if y.isdigit(): + if int(y) in range(0, Tensor0.shape[1]): + y = int(y) + break + else: + print('\tError: Selected value is out of bounds\n') + elif not y: + continue + else: + print('\tError: Select a valid number format (must be integer)\n') + if dims == 4: + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - Component comparison') + fig.suptitle(f'Real Data vs Prediction - Component {c+1}') + ax[0].contourf(Tensor0[c, :, :, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(TimePred[:], Reconst[c, y, x, :], 'k-x', label = 'Predicted Data') + ax[1].plot(Time[:], Tensor0[c, y, x, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/OrigPred.png') + plt.show() + plt.close() + + elif dims == 5: + nz = int(Tensor0.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - Component comparison') + fig.suptitle(f'Real Data vs Prediction - XY plane - Component {c+1}') + ax[0].contourf(Tensor0[c, :, :, nz, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(TimePred[:], Reconst[c, y, x, nz, :], 'k-x', label = 'Predicted Data') + ax[1].plot(Time[:], Tensor0[c, y, x, nz, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/OrigPred.png') + plt.show() + plt.close() + + while True: + Resp = input('Do you want to plot another component? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + if Resp == 1: + continue + + + if not os.path.exists(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes'): + os.mkdir(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes') + + print(f'\nSelect component and temporal mode to plot a DMD mode') + while True: + while True: + ModeNum = input(f'Introduce the DMD mode to plot (default is 1). Maximum number of modes is {DMDmode.shape[-1]}: ') + if not ModeNum: + ModeNum = 0 + break + elif ModeNum.isdigit(): + if int(ModeNum) <= DMDmode.shape[-1]: + ModeNum = int(ModeNum)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + while True: + ModComp = input(f'Introduce the component to plot (default component 1). Maximum number of components is {DMDmode.shape[0]}: ') + if not ModComp: + ModComp = 0 + break + elif ModComp.isdigit(): + if int(ModComp) <= DMDmode.shape[0]: + ModComp = int(ModComp)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + if dims==4: + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode') + fig.suptitle(f'DMDmode - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[ModComp,:,:,ModeNum].real) + ax[0].set_title('Real part') + + ax[1].contourf(DMDmode[ModComp,:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + plt.show() + + elif dims==5: + nz = int(Tensor0.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode XY plane') + fig.suptitle(f'DMDmode XY plane - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[ModComp,:,:,nz,ModeNum].real) + ax[0].set_title('Real part - XY Plane') + + ax[1].contourf(DMDmode[ModComp,:,:,nz,ModeNum].imag) + ax[1].set_title('Imaginary part - XY Plane') + plt.show() + + while True: + Resp = input('Do you want to plot another mode? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + if Resp == 1: + continue + + while True: + dec4 = input(f'Plot video for original data and predicted data? (y/n). Continue with Yes: ') + if not dec4 or dec4.strip().lower() in ['y', 'yes']: + decision4 = True + break + if dec4.strip().lower() in ['n', 'no']: + decision4 = False + return + else: + print('\tError: Please select yes or no (y/n)\n') + + if dims == 5: + nz = int(Tensor0.shape[3] / 2) + while True: + plane = input('Select a plane (XY, XZ, YZ)') + if plane.strip().lower() == 'xy': + Tensor0 = Tensor0[:, :, :, nz, :] + Reconst = Reconst[:, :, :, nz, :] + break + elif plane.strip().lower() == 'xz': + Tensor0 = Tensor0[:, :, 0, :, :] + Reconst = Reconst[:, :, 0, :, :] + break + elif plane.strip().lower() == 'yz': + Tensor0 = Tensor0[:, 0, :, :, :] + Reconst = Reconst[:, 0, :, :, :] + break + else: + print('\tError: Select a valid plane\n') + + else: + pass + + titles = [] + [titles.append(f'Component {i+1}') for i in range(Tensor0.shape[0])] + + while True: + if decision4 == True: + vidvel = input(f'Select a component (max is {Tensor0.shape[0]}). Continue with component 1: ') + if not vidvel: + vel = 0 + video(Tensor0, vel, Title = f'Original Data - {titles[vel]}') + video(Reconst, vel, Title = f'Predicted data - {titles[vel]}') + break + elif vidvel.isdigit(): + if int(vidvel) <= Tensor0.shape[0]: + vel = int(vidvel) - 1 + video(Tensor0, vel, Title = f'Original Data - {titles[vel]}') + video(Reconst, vel, Title = f'Predicted data - {titles[vel]}') + break + else: + print("\tError: Select a valid component\n") + else: + print('\tError: Introduce a valid format (must be integer)\n') + + while True: + ch1 = input('Would you like to plot another component? (y/n). Continue with No: ') + if ch1.strip().lower() in ['y', 'yes']: + while True: + vidvel = input(f'Select a component (max is {Tensor0.shape[0]}): ') + if vidvel.isdigit(): + if int(vidvel) <= Tensor0.shape[0]: + vel = int(vidvel) - 1 + video(Tensor0, vel, Title = f'Original Data - {titles[vel]}') + video(Reconst, vel, Title = f'Predicted data - {titles[vel]}') + break + else: + print("\tError: Select a valid component\n") + else: + print('\tError: Introduce a valid format (must be integer)\n') + continue + elif not ch1 or ch1.strip().lower() in ['n', 'no']: + return + + else: + print('\tError: Select yes or no (y/n)\n') \ No newline at end of file diff --git a/v0.1_ModelFLOWs_app/HybCNNpredmodel.py b/v0.1_ModelFLOWs_app/HybCNNpredmodel.py new file mode 100644 index 0000000..3b95aae --- /dev/null +++ b/v0.1_ModelFLOWs_app/HybCNNpredmodel.py @@ -0,0 +1,1244 @@ +def hybCNN(): + import numpy as np + import pandas as pd + import os + os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' + import matplotlib.pyplot as plt + import time + import math + + import hdf5storage + import data_load + import scipy.io + + + from numpy import linalg as LA + + + from sklearn.metrics import mean_squared_error, mean_absolute_error, median_absolute_error, r2_score + + import tensorflow as tf + from tensorflow import keras + + from tensorflow.keras.layers import Dense, Reshape, TimeDistributed, Flatten, Convolution1D + from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau + from tensorflow.keras.models import Model + from tensorflow.keras.layers import Input + + print("\nHybrid SVD + CNN Model\n") + print('-----------------------------') + print("Inputs: \n") + + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def mean_absolute_percentage_error(y_true, y_pred): + epsilon = 1e-10 + y_true, y_pred = np.array(y_true), np.array(y_pred) + return np.mean(np.abs((y_true - y_pred) / np.maximum(epsilon,np.abs(y_true)))) * 100 + + def smape(A, F): + return ((100.0/len(A)) * np.sum(2 * np.abs(F - A) / (np.abs(A) + np.abs(F))+ np.finfo(float).eps)) + + def RRMSE (real, predicted): + RRMSE = np.linalg.norm(np.reshape(real-predicted,newshape=(np.size(real),1)),ord=2)/np.linalg.norm(np.reshape(real,newshape=(np.size(real),1))) + return RRMSE + + def custom_loss(y_actual,y_pred): + if decision2 == 'no-scaling': + Ten = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_pred) + Ten_actual = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_actual) + elif decision2 == 'MaxPerMode': + Ten = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_pred* (max_val)) + Ten_actual = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_actual* (max_val)) + elif decision2 == 'auto': + Ten = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_pred* (std_val)+med_val) + Ten_actual = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_actual* (std_val)+med_val) + elif decision2 == 'range': + Ten = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_pred* (range_val)+min_val) + Ten_actual = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_actual* (range_val)+min_val) + + Ten = tf.keras.layers.Reshape((nx*ny,nv))(Ten) + Ten_actual = tf.keras.layers.Reshape((nx*ny,nv))(Ten_actual) + + output_list = [] + output_list2 = [] + + for iter in range(Ten.shape[2]-1): + variable = Ten[:,:,iter+1] + variable_ac = Ten_actual[:,:,iter+1] + + if decision1 == 'no-scaling': + output_list.append(variable) + output_list2.append(variable_ac) + else: + Med = tf.cast(tf.reshape(Media_tiempo[iter+1,:,:,0],[nx*ny]),tf.float32) + output_list.append(variable*(Factor[iter+1])+Media[iter+1]+Med) + output_list2.append(variable_ac*(Factor[iter+1])+Media[iter+1]+Med) + + Ten2 = tf.stack(output_list) + Ten2_ac = tf.stack(output_list2) + + pred_sum_spec = tf.math.reduce_sum(Ten2,axis=0) + pred_sum_spec = Reshape((nx,ny))(pred_sum_spec) + + ac_sum_spec = tf.math.reduce_sum(Ten2_ac,axis=0) + ac_sum_spec = Reshape((nx,ny))(ac_sum_spec) + + custom_loss = tf.reduce_mean(tf.square(y_actual-y_pred)) + tf.reduce_mean(tf.square(ac_sum_spec - pred_sum_spec)) + + return custom_loss + + # Load Data + path0 = os.getcwd() + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + while True: + filetype = input('Select the input file format (.mat, .npy, .csv, .pkl, .h5): ') + print('\n\tWarning: This model can only be trained with 2-Dimensional data (as in: (variables, nx, ny, time))\n') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + data, _ = data_load.main(filetype) + + + data = tf.transpose(data, (3, 2, 1, 0)) + + print('\n-----------------------------') + print("SVD Parameters: \n") + + while True: + n_modes = input('Select number of SVD modes. Continue with 18: ') + if not n_modes: + n_modes = 18 + break + elif n_modes.isdigit(): + n_modes = int(n_modes) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + decision1 = input('Select first scaling: scaling of implemented variables (auto, pareto, range, no-scaling). Continue with auto: ') + if not decision1 or decision1.strip().lower() == 'auto': + decision1 = 'auto' + break + elif decision1.strip().lower() == 'pareto': + decision1 = 'pareto' + break + elif decision1.strip().lower() == 'range': + decision1 = 'range' + break + elif decision1.strip().lower() == 'no-scaling': + decision1 = 'no-scaling' + break + else: + print('\tError: Select a valid option\n') + + while True: + decision2 = input('Select the scaling of the SVD temporal coefficients implemented (MaxPerMode, auto, range, no-scaling). Continue with MaxPerMode: ') + if not decision2 or decision2.strip().lower() == 'maxpermode': + decision2 = 'MaxPerMode' + break + elif decision2.strip().lower() == 'auto': + decision2 = 'auto' + break + elif decision2.strip().lower() == 'range': + decision2 = 'range' + break + elif decision2.strip().lower() == 'no-scaling': + decision2 = 'no-scaling' + break + else: + print('\tError: Select a valid option\n') + + # Inputs + # Model configuration + print('\n-----------------------------') + print('Neural Network Training Configuration: \n') + + while True: + hyper = input('Use optimal hyperparameters? (y/n). Continue with No: ') + if not hyper or hyper.strip().lower() in ['no', 'n']: + hyper = 'No' + break + elif hyper.strip().lower() in ['yes', 'y']: + hyper = 'Yes' + break + else: + print('\tError: Select yes or no (y/n)\n') + + if hyper == 'Yes': + print(''' + Available hyperparameter tuners: + 1) RandomSearch: All the hyperparameter combinations are chosen randomly. + 2) Hyperband: Randomly sample all the combinations of hyperparameter and train the model for few epochs with the combinations, selecting the best candidates based on the results. + 3) BayesianOptimization: Chooses first few combinations randomly, then based on the performance on these hyperparameters it chooses the next best possible hyperparameters. + ''') + while True: + tuner_ = input('Select a hyperparameter tuner (1/2/3). Continue with RandomSearch: ') + if not tuner_ or tuner_ == '1': + tuner_ = 'RandomSearch' + break + elif tuner_ == '2': + tuner_ = 'Hyperband' + break + elif tuner_ == '3': + tuner_ = 'Bayesian' + break + else: + print('\tError: Select a valid tuner\n') + + while True: + test_prop = input('Select test data percentage (0-1). Continue with 0.20: ') # test set proportion + if not test_prop: + test_prop = 0.2 + break + elif is_float(test_prop): + test_prop = float(test_prop) + break + else: + print('\tError: Please select a number\n') + + while True: + val_prop = input('Select validation data percentage (0-1). Continue with 0.20: ') # test set proportion + if not val_prop: + val_prop = 0.2 + break + elif is_float(val_prop): + val_prop = float(val_prop)# validation set proportion val_length = (1-test_prop)*val_prop // train_length = (1-test_prop)*(1-val_prop) + break + else: + print('\tError: Please select a number\n') + + while True: + batch_size = input('Select batch size (recommended power of 2). Continue with 8: ') + if not batch_size: + batch_size = 8 + break + elif batch_size.isdigit(): + batch_size = int(batch_size) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + epoch = input('Select training epochs. Continue with 500: ') + if not epoch: + epoch = 500 + break + elif epoch.isdigit(): + epoch = int(epoch) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + k = input('Select number of snapshots used as predictors (min. is 6). Continue with 10: ') # number of snapshots used as predictors + if not k: + k = 10 + break + elif k.isdigit(): + if int(k) >= 6: + k = int(k) + break + else: + print('\tError: Invalid value') + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + p = input(f'Select number of snapshots used as time-ahead predictions (max. is {k-4}). Continue with {k-4}: ') # number of snapshots used as predictors + if not p: + p = k-4 + break + elif p.isdigit(): + if int(p) <= k-4: + p = int(p) + break + else: + print('\tError: Invalid value') + else: + print('\tError: Select a valid number (must be integer)\n') + + if hyper == 'Yes': + pass + + if hyper == 'No': + print('\n-----------------------------') + print('Model Parameters: \n') + while True: + act_func1 = input('Select hidden layer activation function (relu, elu, softmax, sigmoid, tanh, linear). Continue with relu: ') + if not act_func1 or act_func1.strip().lower() == 'relu': + act_func1 = 'relu' + break + elif act_func1.strip().lower() == 'elu': + act_func1 = 'elu' + break + elif act_func1.strip().lower() == 'softmax': + act_func1 = 'softmax' + break + elif act_func1.strip().lower() == 'sigmoid': + act_func1 = 'sigmoid' + break + elif act_func1.strip().lower() == 'tanh': + act_func1 = 'tanh' + break + elif act_func1.strip().lower() == 'linear': + act_func1 = 'linear' + break + else: + print('\tError: Please select a valid option\n') + + while True: + act_func2 = input('Select output layer activation function (tanh, relu, sigmoid, linear). Continue with tanh: ') + if not act_func2 or act_func2.strip().lower() == 'tanh': + act_func2 = 'tanh' + break + elif act_func2.strip().lower() == 'relu': + act_func2 = 'relu' + break + elif act_func2.strip().lower() == 'sigmoid': + act_func2 = 'sigmoid' + break + elif act_func2.strip().lower() == 'linear': + act_func2 = 'linear' + break + else: + print('\tError: Please select a valid option\n') + + while True: + neurons = input('Select number of neurons per layer. Continue with 100: ') + if not neurons: + neurons = 100 + break + elif neurons.isdigit(): + neurons = int(neurons) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + shared_dim = input('Select number of shared dims. Continue with 100: ') + if not shared_dim: + shared_dim = 100 + break + elif shared_dim.isdigit(): + shared_dim = int(shared_dim) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + lr = input('Select model learning rate. Continue with 2e-3: ') + if not lr: + lr = 0.002 + break + elif is_float(lr): + lr = float(lr) + break + else: + print('\tError: Please select a number\n') + + while True: + lf = input('Select loss function (mse, custom_loss, mae). Continue with mse: ') + if not lf or lf.strip().lower() == 'mse': + lf = 'mse' + break + elif lf.strip().lower() == 'custom_loss': + lf = custom_loss + break + elif lf.strip().lower() == 'mae': + lf = 'mae' + break + else: + print('\tError: Please select a valid option\n') + + print('\n-----------------------------') + print(f''' +HYPERPARAMETERS SUMMARY:\n +Hidden Layer activation function: {act_func1} +Output Layer activation function: {act_func2} +Number of neurons: {neurons} +Number of shared dimensions: {shared_dim} +Learning rate: {lr} +Loss function: {lf} + ''') + + print('-----------------------------') + print('Outputs: \n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_hybCNN_Solution' + else: + filen = f'{filen}' + + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f'{path0}/{filen}') + + while True: + output0 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not output0 or output0.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + while True: + output1 = input('Save error made by SVD for each variable (y/n). Continue with Yes: ') + if not output1 or output1.strip().lower() in ['y', 'yes']: + output1 = 'yes' + break + elif output1.strip().lower() in ['n', 'no']: + output1 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output3 = input('Plot loss function evolution (y/n). Continue with Yes: ') + if not output3 or output3.strip().lower() in ['y', 'yes']: + output3 = 'yes' + break + elif output3.strip().lower() in ['n', 'no']: + output3 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output4 = input('Save RRMSE for each variable (Original data) (y/n). Continue with Yes: ') + if not output4 or output4.strip().lower() in ['y', 'yes']: + output4 = 'yes' + break + elif output4.strip().lower() in ['n', 'no']: + output4 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output5 = input('Save RRMSE for each variable (Truncated data) (y/n). Continue with Yes: ') + if not output5 or output5.strip().lower() in ['y', 'yes']: + output5 = 'yes' + break + elif output5.strip().lower() in ['n', 'no']: + output5 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output6 = input('Plot RRMSE temporal matrix (y/n). Continue with Yes: ') + if not output6 or output6.strip().lower() in ['y', 'yes']: + output6 = 'yes' + break + elif output6.strip().lower() in ['n', 'no']: + output6 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output7 = input('Plot mode comparison (y/n). Continue with Yes: ') + if not output7 or output7.strip().lower() in ['y', 'yes']: + output7 = 'yes' + break + elif output7.strip().lower() in ['n', 'no']: + output7 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + if output7 == 'yes': # Indicate the number of the mode you want to compare + while True: + output71 = input('Select the number of the first "n" modes to compare. Continue with 5: ') + if not output71: + maxN = 5 + nModes = list(range(1, maxN+1)) + break + elif output71.isdigit(): + maxN = int(output71) + nModes = list(range(1, maxN+1)) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + output8 = input('Plot snapshot comparison (y/n). Continue with Yes: ') + if not output8 or output8.strip().lower() in ['y', 'yes']: + output8 = 'yes' + break + elif output8.strip().lower() in ['n', 'no']: + output8 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + if output8 == 'yes': # Indicate the number of the variable and time step (nv, nt) + while True: + output81 = input(f'Select number of first "n" variables. Continue with first {int(data.shape[-1])}: ') + if not output81: + output81 = int(data.shape[-1]) + break + elif output81.isdigit(): + output81 = int(output81) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + output82 = input(f'Select time step to plot. Continue with {int(data.shape[0]/100)}: ') + if not output82: + output82 = int(data.shape[0]/100) + break + elif output82.isdigit(): + output82 = int(output82) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + index = np.array([[i, output82] for i in range(output81)]) + + nt, ny, nx, nv = np.shape(data) + + # Original tensor reconstruction + Mat_orig = np.reshape(data,[nt,nv*nx*ny]) + Mat_orig = np.transpose(Mat_orig) + Tensor_orig = np.reshape(Mat_orig,[nv,nx,ny,nt], order='F') + sum_spec = np.sum(np.sum(Tensor_orig[1:,:,:,:],axis=3),axis=0)/(nt) + + x = np.linspace(0, 1, nx) + y = np.linspace(0, 1, ny) + xv, yv = np.meshgrid(y, x) + + # Centering and scaling + if decision1 == 'no-scaling': + pass + else: + #Dummy variables + print('\nScaling Data') + Tensor_orig1 = np.zeros(Tensor_orig.shape) + Factor = np.zeros(data.shape[3]) + Media = np.zeros(data.shape[3]) + Media_tiempo = np.zeros(np.shape(Tensor_orig)) + + for iter in range(nt): + Media_tiempo[:,:,:,iter] = np.mean(Tensor_orig,axis=3) + + Tensor_orig2 = Tensor_orig-Media_tiempo + + for iter in range(data.shape[3]): + variable = Tensor_orig2[iter,:,:,:] + if decision1 == 'range': + Factor[iter] = np.amax(variable)-np.amin(variable) #Range scaling + if decision1 == 'auto': + Factor[iter] = np.std(variable) #Auto scaling + if decision1 == 'pareto': + Factor[iter] = np.sqrt(np.std(variable)) #Pareto scaling + Media[iter] = np.mean(variable) + Tensor_orig1[iter,:,:,:] = (variable-Media[iter])/(Factor[iter]) + + Mat_orig = np.reshape(Tensor_orig1,[nv*nx*ny,nt],order='F') + + print('Data Scaled\n') + # Perform SVD + print('Performing SVD') + U, S, V = np.linalg.svd(Mat_orig, full_matrices=False) + + Modes = n_modes + S = np.diag(S) + + U = U[:,0:Modes] + S = S[0:Modes,0:Modes] + V = V[0:Modes,:] + + print('SVD Complete\n') + + # AML Matrix construction + AML = np.dot(S,V) + tensor = AML + + if output1 == 'yes': + Mat_trunc = np.dot(U,AML) + Ten_trunc = np.reshape(Mat_trunc,[nv,nx,ny,nt], order='F') + + if decision1 == 'no': + pass + else: + for iter in range(data.shape[3]): + variable = Ten_trunc[iter,:,:,:] + Ten_trunc[iter,:,:,:] = variable*(Factor[iter])+Media[iter]+Media_tiempo[iter,:,:,:] + + if output1 == 'yes': + RRMSE_SVD = np.zeros(data.shape[3]) + for iter in range(data.shape[3]): + diff = Ten_trunc[iter,:,:,:] - Tensor_orig[iter,:,:,:] + RRMSE_SVD[iter] = LA.norm(np.reshape(diff.flatten(),(-1,1)),ord=2)/LA.norm(np.reshape(Tensor_orig[iter,:,:,:].flatten(),(-1,1)),ord=2) + print(f'SVD RRMSE error for variable {iter+1}: {np.round(RRMSE_SVD[iter]*100, 3)}%') + + if not output0 or output0.strip().lower() in ['.npy', 'npy']: + np.save(f'{path0}/{filen}/RRMSE_SVD.npy',RRMSE_SVD) + + elif output0.strip().lower() in ['.mat', 'mat']: + mdic = {"RRMSE_SVD": RRMSE_SVD} + file_mat= str(f'{path0}/{filen}/RRMSE_SVD.mat') + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + # Scaling of temporal coeficients + if decision2 == 'no': + tensor_norm = tensor + elif decision2 == 'range': + min_val = np.amin(tensor) + range_val = np.ptp(tensor) + tensor_norm = (tensor-min_val)/range_val + elif decision2 == 'auto': + med_val = np.mean(tensor) + std_val =np.std(tensor) + tensor_norm = (tensor-med_val)/std_val + elif decision2 == 'MaxPerMode': + max_val = sum(np.amax(np.abs(tensor),axis=1)) + tensor_norm = tensor / max_val + + # Dataset configuration + total_length = tensor_norm.shape[1] # number of snapshots + channels_n = 0 # number of channels, assuming each snapshot is an image with n channels + dim_x = tensor_norm.shape[0] # following the simil that each snapshot is an image, the dimension x of that image + dim_y = 0 # following the simil that each snapshot is an image, the dimension y of that image + + print('\n-----------------------------') + print('Dataset configuration: \n') + print('total_length: ', total_length) + print('channels_n: ', channels_n) + print('dim_x: ', dim_x) + print('dim_y: ', dim_y) + + # Preparing training and test datasets + class DataGenerator(tf.keras.utils.Sequence): + 'Generates data for Keras' + def __init__(self, data, list_IDs, batch_size=5, dim=(20), + k = 624, p = 1, + shuffle=True, till_end = False, only_test = False): + 'Initialization' + self.data = data + self.dim = dim + self.batch_size = batch_size + self.list_IDs = list_IDs + self.shuffle = shuffle + self.p = p + self.k = k + self.till_end = till_end + self.only_test = only_test + self.on_epoch_end() + + def __len__(self): + 'Denotes the number of batches per epoch' + if self.till_end: + lenx = math.ceil((len(self.list_IDs) / self.batch_size)) + else: + lenx = int(np.floor(len(self.list_IDs) / self.batch_size)) + return lenx + + def __getitem__(self, index): + 'Generate one batch of data' + # Generate indexes of the batch + indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size] + + # Find list of IDs + list_IDs_temp = [self.list_IDs[k] for k in indexes] + + # Generate data + X, y = self.__data_generation(list_IDs_temp) + if self.only_test: + return X + else: + return X, y + + def on_epoch_end(self): + 'Updates indexes after each epoch' + self.indexes = np.arange(len(self.list_IDs)) + if self.shuffle == True: + np.random.shuffle(self.indexes) + + def __data_generation(self, list_IDs_temp): + 'Generates data containing batch_size samples' # X : (n_samples, *dim, depth) + # Initialization + X = np.empty((self.batch_size, self.dim, self.k)) + y = [np.empty((self.batch_size, self.dim))]*self.p + + y_inter = np.empty((self.batch_size, self.dim, p)) + + # Generate data + lenn = len(list_IDs_temp) + for i, ID in enumerate(list_IDs_temp): + # Store Xtrain + X[i,:,:] = self.data[:,ID:ID+k] + # Store Ytrain + y_inter[i,:,:] = self.data[:,ID+k:ID+k+p] + + for j in range(self.p): + y[j] = y_inter[:,:,j] + y[j] = np.reshape(y[j], (lenn, -1)) + X = X.transpose((0,2,1)) + return X, y + + # Prepare the dataset indexes + period_transitorio = 0 + stride_train = 1 + stride_val = 1 + stride_test = 1 + dim=(dim_x) + + test_length = int(test_prop * total_length) + val_length = int((total_length - test_length) * val_prop) + train_length = total_length - val_length - test_length + + if int(train_length-period_transitorio-(k+p)) < 0: + train_n = 0 + elif int((train_length-period_transitorio-(k+p))//stride_train) == 0: + train_n = 1 + else: + train_n = int(((train_length-period_transitorio)-(k+p))//stride_train) + + if int(test_length-(k+p)) < 0: + test_n = 0 + elif int((test_length-(k+p))//stride_test) == 0: + test_n = 1 + else: + test_n = int((test_length-(k+p))//stride_test) + + if int(val_length-(k+p)) < 0: + val_n = 0 + elif int((val_length-(k+p))//stride_val) == 0: + val_n = 1 + else: + val_n = int((val_length-(k+p))//stride_val) + + # Indices for the beginnings of each batch + train_idxs = np.empty([train_n], dtype='int') + val_idxs = np.empty([val_n], dtype='int') + test_idxs = np.empty([test_n], dtype='int') + + j = period_transitorio + for i in range(train_n): + train_idxs[i] = j + j = j+stride_train + + j = train_length + for i in range(val_n): + val_idxs[i] = j + j = j+stride_val + + j = train_length + val_length + for i in range(test_n): + test_idxs[i] = j + j = j+stride_test + + # Generators + training_generator = DataGenerator(tensor_norm, train_idxs, + dim = dim, + batch_size = batch_size, + k = k, p = p, till_end = False, + only_test = False, + shuffle = True) + validation_generator = DataGenerator(tensor_norm, val_idxs, + dim = dim, + batch_size = batch_size, + k = k, p = p, till_end = False, + only_test = False, + shuffle = False) + test_generator = DataGenerator(tensor_norm, test_idxs, + dim = dim, + batch_size = batch_size, + k = k, p = p, till_end = False, + only_test = True, + shuffle = False) + + print('\n-----------------------------') + print('Model training summary: \n') + print ('test_length: ', test_length) + print ('val_length: ', val_length) + print ('train_length: ', train_length) + print() + print ('test_n: ', test_n) + print ('val_n: ', val_n) + print ('train_n: ', train_n) + print() + print('test_generator_len: ', len(test_generator)) + print('validation_generator_len: ', len(validation_generator)) + print('training_generator_len: ', len(training_generator)) + + # Prepare Ytest + test_n_adjusted = int(test_n/batch_size)*batch_size # multiplo de batch_size + Ytest = [np.empty([test_n_adjusted, dim_x], dtype='float64')] * p + Ytest_fl = [np.empty([test_n_adjusted, dim_x ], dtype='float64')] * p + + Ytest_inter = np.empty([test_n_adjusted, dim_x, p], dtype='float64') + + for i in range(test_n_adjusted): + j = test_idxs[i] + Ytest_inter[i,:,:] = tensor_norm[:,j+k:j+k+p] + + for r in range(p): + Ytest[r] = Ytest_inter[:,:,r] + Ytest_fl[r] = np.copy(np.reshape(Ytest[r], (test_n_adjusted, -1))) + + if hyper == 'No': + def create_model_cnn(in_shape, out_dim, p = 3, shared_dim = 1000, act_fun= act_func1): + x = Input(shape=in_shape) + + v = Convolution1D(30,3)(x) + v = Convolution1D(60,3)(v) + v = Dense(neurons, activation= act_fun)(v) + + tt = [1]*p + + r = TimeDistributed( Dense(shared_dim, activation=act_fun))(v) + s = tf.split(r, tt, 1) + for i in range(p): + s[i] = Flatten()(s[i]) + + o = [] + for i in range(p): + o.append( Dense(out_dim, activation=act_func2)(s[i]) ) + + m = Model(inputs=x, outputs=o) + opt = keras.optimizers.Adam(learning_rate=lr) + m.compile(loss=lf, optimizer=opt, metrics=[lf]) + + return(m) + + # Create the model + in_shape = [k, dim_x] + out_dim = dim_x + + model= create_model_cnn(in_shape,out_dim,p,shared_dim) + + print('\nModel Summary:\n') + model.summary() + + # save the best weights + save_string = 'hybrid_CNN_model' + + # save the best weights + save_best_weights = f'{path0}/{filen}/' + save_string + '.h5' + save_summary_stats = f'{path0}/{filen}/' + save_string + '.csv' + save_last_weights = f'{path0}/{filen}/' + save_string + '_last_w.h5' + save_results_metrics = f'{path0}/{filen}/' + save_string + '_results_metrics.csv' + + # Model training + np.random.seed(247531338) + + t0 = time.time() + # Model training + callbacks = [ModelCheckpoint(save_best_weights, monitor='val_loss', save_best_only=True, mode='auto'), + EarlyStopping(monitor='val_loss', patience=20, verbose=1, mode='auto', min_delta = 0.0001)] + + print('\nTraining Model Please Wait...\n') + history = model.fit(training_generator, + validation_data=validation_generator, + epochs=epoch, + verbose=1, + callbacks=callbacks) + t1 = time.time() + print('\nModel Trained Successfully!') + print(f"\nTraining complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + if hyper == 'Yes': + import keras_tuner as kt + def create_model_hp(hp): + hp_activation = hp.Choice('hidden_layer_activation_function', values = ['relu', 'tanh', 'linear', 'sigmoid']) + hp_neurons = hp.Int('neurons', min_value = 10, max_value = 100, step = 10) + hp_activation_1 = hp.Choice('output_layer_activation_function', values = ['relu', 'tanh', 'linear', 'sigmoid']) + hp_learning_rate = hp.Choice('learning_rate', values=[1e-2, 1e-3, 5e-3, 1e-4]) + hp_shared_dims = hp.Int('shared dims', min_value = 10, max_value = 100, step = 10) + + x = Input(shape=[k, dim_x]) + + v = Convolution1D(30,3)(x) + v = Convolution1D(60,3)(v) + v = Dense(hp_neurons, activation= hp_activation)(v) + tt = [1]*p + + r = TimeDistributed(Dense(hp_shared_dims, activation=hp_activation))(v) + s = tf.split(r, tt, 1) + for i in range(p): + s[i] = Flatten()(s[i]) + + o = [] + for i in range(p): + o.append(Dense(dim_x , activation=hp_activation_1)(s[i])) + + m = Model(inputs=x, outputs=o) + + opt = keras.optimizers.Adam(learning_rate=hp_learning_rate) + + m.compile(loss='mse', optimizer=opt, metrics=['mse']) + return(m) + + if tuner_ == 'Hyperband': + tuner = kt.Hyperband(create_model_hp, objective = 'val_loss', max_epochs = 10, factor = 3, directory = 'dir_1', project_name = 'x', overwrite = True) + + elif tuner_ == 'RandomSearch': + tuner = kt.RandomSearch(create_model_hp, objective = 'val_loss', max_trials = 10, directory = 'dir_1', project_name = 'x', overwrite = True) + + elif tuner_ == 'Bayesian': + tuner = kt.BayesianOptimization(create_model_hp, objective = 'val_loss', max_trials = 10, beta = 3, directory = 'dir_1', project_name = 'x', overwrite = True) + + stop_early = tf.keras.callbacks.EarlyStopping(monitor = 'val_loss', patience = 10) + + print('\nSearching for optimal hyperparameters...\n') + + tuner.search(training_generator, + validation_data=validation_generator, + epochs=10, + verbose=1, + callbacks=[stop_early]) + + best_hps = tuner.get_best_hyperparameters(num_trials = 1)[0] + + print('\n-----------------------------') + print(f''' +HYPERPARAMETERS SUMMARY:\n +Hidden Layer activation function: {best_hps.get('hidden_layer_activation_function')} +Output Layer activation function: {best_hps.get('output_layer_activation_function')} +Number of neurons: {best_hps.get('neurons')} +Number of shared dimensions: {best_hps.get('shared dims')} +Learning rate: {best_hps.get('learning_rate')} +Loss function: 'mse' + ''') + + model = tuner.hypermodel.build(best_hps) + + print('Model Summary:\n') + model.summary() + + t0 = time.time() + + save_string = 'hybrid_CNN_model' + + save_best_weights = f'{path0}/{filen}/' + save_string + '.h5' + save_summary_stats = f'{path0}/{filen}/' + save_string + '.csv' + save_last_weights = f'{path0}/{filen}/' + save_string + '_last_w.h5' + save_results_metrics = f'{path0}/{filen}/' + save_string + '_results_metrics.csv' + + callbacks = [ModelCheckpoint(save_best_weights, monitor='val_loss', save_best_only=True, mode='auto'), + EarlyStopping(monitor='val_loss', patience=10, verbose=1, mode='auto', min_delta = 0.0001)] + + print('\nTraining Model Please Wait...\n') + + history = model.fit(training_generator, + validation_data=validation_generator, + epochs=epoch, + verbose=1, + callbacks=callbacks) + + t1 = time.time() + print('\nModel Trained Successfully!') + + print(f"\nTraining complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + + print('Please CLOSE all figures to continue the run\n') + + # save the last weights + model.save_weights(save_last_weights) + + + # Aggregate the summary statistics + summary_stats = pd.DataFrame({'epoch': [ i + 1 for i in history.epoch ], + 'train_loss': history.history['loss'], + 'valid_loss': history.history['val_loss']}) + + summary_stats.to_csv(save_summary_stats) + + if output3 == 'yes': + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - Loss function evolution') + + ax.plot(summary_stats.train_loss, 'b',linewidth = 3) # blue + ax.plot(summary_stats.valid_loss, 'g--',linewidth = 3) # green + ax.set_xlabel('Training epochs',fontsize = 20) + ax.set_ylabel('Loss function',fontsize = 20) + + ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/Lossfun_training.png') + plt.show() + plt.close() + + + # Find the min validation loss during the training + min_loss, idx = min((loss, idx) for (idx, loss) in enumerate(history.history['val_loss'])) + print('Minimum val_loss at epoch', '{:d}'.format(idx+1), '=', '{:.6f}'.format(min_loss)) + min_loss = round(min_loss, 4) + + # Inference + t0 = time.time() + + model.load_weights(save_best_weights) + print('\nModel predicting. Please wait\n') + Ytest_hat_fl = model.predict(test_generator, verbose=1) + + t1 = time.time() + print("\nPrediction complete. Time elapsed: %f" % ((t1 - t0) / 60.)) + + print('\nPerformance measures on Test data, per sec') + lag = 0 + num_sec = Ytest_hat_fl[0].shape[0] + results_table = pd.DataFrame(index=['MSE','MAE','MAD','R2','SMAPE','RRMSE'],columns=range(num_sec)) + for i in range(num_sec): + results_table.iloc[0,i] = mean_squared_error( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[1,i] = mean_absolute_error( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[2,i] = median_absolute_error( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[3,i] = r2_score( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[4,i] = smape( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[5,i] = RRMSE( np.reshape(Ytest_fl[lag][i,:],(-1,1)), np.reshape(Ytest_hat_fl[lag][i,:],(-1,1))) + print(results_table) + results_table.to_csv(f'{path0}/{filen}/Results.csv', index = False, sep=',') + + print('\nPerformance measures on Test data, for all time, per time-ahead lag') + + results_table_global = pd.DataFrame(index=['MSE','MAE','MAD','R2','SMAPE','RRMSE'],columns=range(p)) + for i in range(p): + results_table_global.iloc[0,i] = mean_squared_error(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[1,i] = mean_absolute_error(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[2,i] = median_absolute_error(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[3,i] = r2_score(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[4,i] = smape(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[5,i] = RRMSE( np.reshape(Ytest_fl[i].flatten(),(-1,1)), np.reshape(Ytest_hat_fl[i].flatten(),(-1,1))) + + results_table_global['mean'] = results_table_global.mean(axis=1) + print(results_table_global) + results_table_global.to_csv(f'{path0}/{filen}/Global_results.csv', index = False, sep=',') + + print(f'\nATTENTION!: All plots will be saved to {path0}/{filen}\n') + print('Please CLOSE all figures to continue the run\n') + + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - MSE score') + ax.plot(range(num_sec),results_table.iloc[0,:], 'b') # green + ax.set_title("MSE score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("MSE",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/MSE_score.png') + plt.show() + plt.close() + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - MAE score') + ax.plot(range(num_sec),results_table.iloc[1,:], 'b') # green + ax.set_title("MAE score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("MAE",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/MAE_score.png') + plt.show() + plt.close() + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - MAD score') + ax.plot(range(num_sec),results_table.iloc[2,:], 'b') # green + ax.set_title("MAD score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("MAD",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/MAD_score.png') + plt.show() + plt.close() + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - R2 score') + ax.plot(range(num_sec),results_table.iloc[3,:], 'b') # green + ax.set_title("R2 score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("R2",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/R2_score.png') + plt.show() + plt.close() + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - SMAPE score') + ax.plot(range(num_sec),results_table.iloc[4,:], 'b') # green + ax.set_title("SMAPE score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("SMAPE",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/SMAPE_score.png') + plt.show() + plt.close() + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - RRMSE score') + ax.plot(range(num_sec),results_table.iloc[5,:], 'b') # green + ax.set_title("RRMSE score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("RRMSE",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/RRMSE_score.png') + plt.show() + plt.close() + + # Fill the output arrays + Ytest_hat_fl = model.predict(test_generator, verbose=1) + + # Create the multidimensional arrays for the results and ground-truth values + mat_pred = np.zeros((Ytest_fl[0].shape[0],p,Ytest_fl[0].shape[1])) + + + # Fill the output arrays + for i in range(p): + for j in range(Ytest_fl[0].shape[0]): + mat_pred[j,i,:]=Ytest_hat_fl[i][j,:] + + if decision2 == 'no': + pass + elif decision2 == 'auto': + mat_pred = mat_pred * std_val + med_val + elif decision2 == 'range': + mat_pred = mat_pred * range_val + min_val + elif decision2 == 'MaxPerMode': + mat_pred = mat_pred * max_val + + new_dim = [mat_pred.shape[0],mat_pred.shape[2]] + + AML_pre_LSTM = np.transpose(np.squeeze(mat_pred[:,0,:])) + num_snap = AML_pre_LSTM.shape[1] + + mat_time_slice_index = np.zeros((Ytest_fl[0].shape[0],p),dtype=int) + for i in range(p): + for j in range(Ytest_fl[0].shape[0]): + mat_time_slice_index[j,i]=test_idxs[j]+k+i + + time_lag = mat_time_slice_index[0,0] + + # Matrix reconstruction + + Mat_pre = np.dot(U,AML_pre_LSTM) + + # data reconstruction + Ten_pre = np.reshape(Mat_pre, [nv,nx,ny,AML_pre_LSTM.shape[1]], order='F') + + for iter in range(data.shape[3]): + variable = Ten_pre[iter,:,:,:] + Ten_pre[iter,:,:,:] = variable*(Factor[iter])+Media[iter]+Media_tiempo[iter,:,:,:Ten_pre.shape[3]] + + print('\n') + # RRMSE Measure with Original data + if output4 == 'yes': + RRMSE_orNN = np.zeros(Tensor_orig.shape[0]) + for iter in range(Tensor_orig1.shape[0]): + + diff = Ten_pre[iter,:,:,:] - Tensor_orig[iter,:,:,time_lag:time_lag+num_snap] + RRMSE_orNN[iter] = LA.norm(np.reshape(diff.flatten(),(-1,1)),ord=2)/LA.norm(np.reshape(Tensor_orig[iter,:,:,time_lag:time_lag+num_snap].flatten(),(-1,1)),ord=2) + + print(f'RRMSE measure with original tensor - variable {iter+1}: {np.round(RRMSE_orNN[iter]*100, 3)}%') + + if not output0 or output0.strip().lower() in ['.npy', 'npy']: + np.save(f"{path0}/{filen}/RRMSE_orNN.npy", RRMSE_orNN) + + elif output0.strip().lower() in ['.mat', 'mat']: + mdic = {"RRMSE_orNN": RRMSE_orNN} + file_mat= str(f"{path0}/{filen}/RRMSE_orNN.mat") + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + print() + # RRMSE with Truncated + if output5 == 'yes': + RRMSE_trNN = np.zeros(Tensor_orig.shape[0]) + for iter in range(Tensor_orig1.shape[0]): + + diff = Ten_pre[iter,:,:,:] - Ten_trunc[iter,:,:,time_lag:time_lag+num_snap] + RRMSE_trNN[iter] = LA.norm(np.reshape(diff.flatten(),(-1,1)),ord=2)/LA.norm(np.reshape(Ten_trunc[iter,:,:,time_lag:time_lag+num_snap].flatten(),(-1,1)),ord=2) + + print(f'RRMSE measure with truncated tensor - variable {iter+1}: {np.round(RRMSE_trNN[iter]*100, 3)}%') + + if not output0 or output0.strip().lower() in ['.npy', 'npy']: + np.save(f"{path0}/{filen}/RRMSE_trNN.npy", RRMSE_trNN) + + elif output0.strip().lower() in ['.mat', 'mat']: + mdic = {"RRMSE_trNN": RRMSE_trNN} + file_mat= str(f"{path0}/{filen}/RRMSE_trNN.mat") + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + # RRMSE Measure + if output6 == 'yes': + diff = AML_pre_LSTM - AML[:,time_lag:time_lag+num_snap] + globalRRMSE = LA.norm(np.reshape(diff.flatten(),(-1,1)),ord=2)/LA.norm(np.reshape(AML[:,time_lag:time_lag+num_snap].flatten(),(-1,1)),ord=2) + print(f'\nGlobal RRMSE measure - {np.round(globalRRMSE*100, 3)}%') + + rrmse_arr = np.zeros(num_snap) + for i in range(num_snap): + diff = AML[:,time_lag+i]-AML_pre_LSTM[:,i] + rrmse_arr[i] = LA.norm(diff.flatten(),2)/LA.norm(np.transpose(AML[:,i]).flatten(),2) + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - RRMSE Measure') + ax.plot(rrmse_arr,'k*-') + ax.set_xlabel('Time') + ax.set_ylabel('RRMSE') + ax.set_title('RRMSE measure') + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/rrmse_measure.png') + plt.show() + plt.close() + + # AML Comparison + if output7 == 'yes': + # AML Comparation + if not os.path.exists(f'{path0}/{filen}/Mode_Comparison'): + os.mkdir(f"{path0}/{filen}/Mode_Comparison") + + AML_pre_1 = np.zeros([AML.shape[0],time_lag]) + AML_pre_1 = np.concatenate((AML_pre_1,AML_pre_LSTM),axis=1) + + for i in nModes: + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - AML vs AMLPRE per mode') + ax.plot(AML[i-1,time_lag:],'k*-') + ax.plot(AML_pre_1[i-1,time_lag:],'m*-') + ax.set_xlabel("Time") + ax.set_title(f"Comparation between AML and AMLPRE at mode {i}") + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/Mode_Comparison/aMLComparation_m_{i}_test.png') + plt.show() + plt.close() + + # data Comparison + if output8 == 'yes': + if not os.path.exists(f'{path0}/{filen}/Snapshots'): + os.mkdir(f"{path0}/{filen}/Snapshots") + + for i in range(index.shape[0]): + namefig_orig1 = f'{path0}/{filen}/Snapshots/snap_comp_{index[i,0]+1}_t_{index[i,1]+1}.png' + fig, ax = plt.subplots(num = f'CLOSE TO CONTINUE RUN - Snapshot comparison - snap_comp_{index[i,0]+1}_t_{index[i,1]+1}') + + ax.contourf(xv,yv,Tensor_orig[int(index[i,0]),:,:,time_lag+int(index[i,1])],100,cmap='jet', + vmin = np.amin(Tensor_orig[int(index[i,0]),:,:,time_lag+int(index[i,1])]), + vmax = np.amax(Tensor_orig[int(index[i,0]),:,:,time_lag+int(index[i,1])])) + + ax.contourf(-xv,yv,Ten_pre[int(index[i,0]),:,:,int(index[i,1])],100,cmap='jet', + vmin = np.amin(Tensor_orig[int(index[i,0]),:,:,time_lag+int(index[i,1])]), + vmax = np.amax(Tensor_orig[int(index[i,0]),:,:,time_lag+int(index[i,1])])) + + ax.set_title('Prediction vs. Original Data') + ax = plt.gca() + ax.set_aspect(1) + props = dict(boxstyle='round', facecolor='white', alpha=1) + ax.annotate('', xy=(0.5, -0.005), xycoords='axes fraction', xytext=(0.5, 1.005), + arrowprops=dict(arrowstyle='-', lw = 3, color='k')) + plt.savefig(namefig_orig1) + plt.tight_layout() + plt.show() + + if not output0 or output0.strip().lower() in ['.npy', 'npy']: + np.save(f"{path0}/{filen}/TensorPred.npy", Ten_pre) + + elif output0.strip().lower() in ['.mat', 'mat']: + mdic = {"Ten_pre": Ten_pre} + file_mat= str(f"{path0}/{filen}/TensorPred.mat") + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + + + + + diff --git a/v0.1_ModelFLOWs_app/HybRNNpredmodel.py b/v0.1_ModelFLOWs_app/HybRNNpredmodel.py new file mode 100644 index 0000000..106a6a0 --- /dev/null +++ b/v0.1_ModelFLOWs_app/HybRNNpredmodel.py @@ -0,0 +1,1251 @@ +def hybRNN(): + import numpy as np + import pandas as pd + import os + os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' + import matplotlib.pyplot as plt + import time + import hdf5storage + import scipy.io + import data_load + from numpy import linalg as LA + + from sklearn.metrics import mean_squared_error, mean_absolute_error, median_absolute_error, r2_score + + import tensorflow as tf + from tensorflow import keras + from tensorflow.keras.layers import Dense, Reshape, TimeDistributed, Flatten + from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau + from tensorflow.keras.models import Model + from tensorflow.keras.layers import Input, LSTM + + pd.set_option('display.max_columns',100) + pd.set_option('display.max_rows',100) + + def mean_absolute_percentage_error(y_true, y_pred): + epsilon = 1e-10 + y_true, y_pred = np.array(y_true), np.array(y_pred) + return np.mean(np.abs((y_true - y_pred) / np.maximum(epsilon,np.abs(y_true)))) * 100 + + def smape(A, F): + return ((100.0/len(A)) * np.sum(2 * np.abs(F - A) / (np.abs(A) + np.abs(F))+ np.finfo(float).eps)) + + def RRMSE (real, predicted): + RRMSE = np.linalg.norm(np.reshape(real-predicted,newshape=(np.size(real),1)),ord=2)/np.linalg.norm(np.reshape(real,newshape=(np.size(real),1))) + return RRMSE + + def custom_loss(y_actual,y_pred): + if decision2 == 'no-scaling': + Ten = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_pred) + Ten_actual = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_actual) + elif decision2 == 'MaxPerMode': + Ten = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_pred* (max_val)) + Ten_actual = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_actual* (max_val)) + elif decision2 == 'auto': + Ten = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_pred* (std_val)+med_val) + Ten_actual = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_actual* (std_val)+med_val) + elif decision2 == 'range': + Ten = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_pred* (range_val)+min_val) + Ten_actual = tf.einsum('ij,kj->ki',tf.convert_to_tensor(U, dtype=tf.float32),y_actual* (range_val)+min_val) + + Ten = tf.keras.layers.Reshape((nx*ny,nv))(Ten) + Ten_actual = tf.keras.layers.Reshape((nx*ny,nv))(Ten_actual) + + output_list = [] + output_list2 = [] + + for iter in range(Ten.shape[2]-1): + variable = Ten[:,:,iter+1] + variable_ac = Ten_actual[:,:,iter+1] + + if decision1 == 'no-scaling': + output_list.append(variable) + output_list2.append(variable_ac) + else: + Med = tf.cast(tf.reshape(Media_tiempo[iter+1,:,:,0],[nx*ny]),tf.float32) + output_list.append(variable*(Factor[iter+1])+Media[iter+1]+Med) + output_list2.append(variable_ac*(Factor[iter+1])+Media[iter+1]+Med) + + Ten2 = tf.stack(output_list) + Ten2_ac = tf.stack(output_list2) + + pred_sum_spec = tf.math.reduce_sum(Ten2,axis=0) + pred_sum_spec = Reshape((nx,ny))(pred_sum_spec) + + ac_sum_spec = tf.math.reduce_sum(Ten2_ac,axis=0) + ac_sum_spec = Reshape((nx,ny))(ac_sum_spec) + + custom_loss = tf.reduce_mean(tf.square(y_actual-y_pred)) + tf.reduce_mean(tf.square(ac_sum_spec - pred_sum_spec)) + + return custom_loss + + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + + print("\nHybrid SVD + RNN Model\n") + print('-----------------------------') + print("Inputs: \n") + + # Load Data + path0 = os.getcwd() + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + while True: + filetype = input('Select the input file format (.mat, .npy, .csv, .pkl, .h5): ') + print('\n\tWarning: This model can only be trained with 2-Dimensional data (as in: (variables, nx, ny, time))\n') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + data, _ = data_load.main(filetype) + + + data = tf.transpose(data, (3, 2, 1, 0)) + + ################ INPUTS ################## + print('\n-----------------------------') + print("SVD Parameters: \n") + + while True: + n_modes = input('Select number of SVD modes. Continue with 18: ') + if not n_modes: + n_modes = 18 + break + elif n_modes.isdigit(): + n_modes = int(n_modes) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + decision1 = input('Select first scaling: scaling of implemented variables (auto, pareto, range, no-scaling). Continue with auto: ') + if not decision1 or decision1.strip().lower() == 'auto': + decision1 = 'auto' + break + elif decision1.strip().lower() == 'pareto': + decision1 = 'pareto' + break + elif decision1.strip().lower() == 'range': + decision1 = 'range' + break + elif decision1.strip().lower() == 'no-scaling': + decision1 = 'no-scaling' + break + else: + print('\tError: Select a valid option\n') + + while True: + decision2 = input('Select the scaling of the SVD temporal coefficients implemented (MaxPerMode, auto, range, no-scaling). Continue with MaxPerMode: ') + if not decision2 or decision2.strip().lower() == 'maxpermode': + decision2 = 'MaxPerMode' + break + elif decision2.strip().lower() == 'auto': + decision2 = 'auto' + break + elif decision2.strip().lower() == 'range': + decision2 = 'range' + break + elif decision2.strip().lower() == 'no-scaling': + decision2 = 'no-scaling' + break + else: + print('\tError: Select a valid option\n') + + # Model configuration + print('\n-----------------------------') + print('Neural Network Training Configuration: \n') + + while True: + hyper = input('Use optimal hyperparameters? (y/n). Continue with No: ') + if not hyper or hyper.strip().lower() in ['no', 'n']: + hyper = 'No' + break + elif hyper.strip().lower() in ['yes', 'y']: + hyper = 'Yes' + break + else: + print('\tError: Select yes or no (y/n)\n') + + if hyper == 'Yes': + print(''' +Available hyperparameter tuners: +1) RandomSearch: All the hyperparameter combinations are chosen randomly. +2) Hyperband: Randomly sample all the combinations of hyperparameter and train the model for few epochs with the combinations, selecting the best candidates based on the results. +3) BayesianOptimization: Chooses first few combinations randomly, then based on the performance on these hyperparameters it chooses the next best possible hyperparameters. + ''') + while True: + tuner_ = input('Select a hyperparameter tuner (1/2/3). Continue with RandomSearch: ') + if not tuner_ or tuner_ == '1': + tuner_ = 'RandomSearch' + break + elif tuner_ == '2': + tuner_ = 'Hyperband' + break + elif tuner_ == '3': + tuner_ = 'Bayesian' + break + else: + print('\tError: Select a valid tuner\n') + + while True: + test_prop = input('Select test data percentage (0-1). Continue with 0.20: ') # test set proportion + if not test_prop: + test_prop = 0.2 + break + elif is_float(test_prop): + test_prop = float(test_prop) + break + else: + print('\tError: Please select a number\n') + + while True: + val_prop = input('Select validation data percentage (0-1). Continue with 0.20: ') # test set proportion + if not val_prop: + val_prop = 0.2 + break + elif is_float(val_prop): + val_prop = float(val_prop)# validation set proportion val_length = (1-test_prop)*val_prop // train_length = (1-test_prop)*(1-val_prop) + break + else: + print('\tError: Please select a number\n') + + while True: + batch_size = input('Select batch size (recommended power of 2). Continue with 8: ') + if not batch_size: + batch_size = 8 + break + elif batch_size.isdigit(): + batch_size = int(batch_size) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + epoch = input('Select training epochs. Continue with 500: ') + if not epoch: + epoch = 500 + break + elif epoch.isdigit(): + epoch = int(epoch) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + k = input('Select number of snapshots used as predictors (min. is 6). Continue with 10: ') # number of snapshots used as predictors + if not k: + k = 10 + break + elif k.isdigit(): + if int(k) >= 6: + k = int(k) + break + else: + print('\tError: Invalid value') + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + p = input(f'Select number of snapshots used as time-ahead predictions (max. is {k-4}). Continue with {k-4}: ') # number of snapshots used as predictors + if not p: + p = k-4 + break + elif p.isdigit(): + if int(p) <= k-4: + p = int(p) + break + else: + print('\tError: Invalid value') + else: + print('\tError: Select a valid number (must be integer)\n') + + + if hyper == 'Yes': + pass + + elif hyper == 'No': + print('\n-----------------------------') + print('Model Parameters: \n') + while True: + act_func1 = input('Select hidden layer activation function (relu, elu, softmax, sigmoid, tanh, linear). Continue with relu: ') + if not act_func1 or act_func1.strip().lower() == 'relu': + act_func1 = 'relu' + break + elif act_func1.strip().lower() == 'elu': + act_func1 = 'elu' + break + elif act_func1.strip().lower() == 'softmax': + act_func1 = 'softmax' + break + elif act_func1.strip().lower() == 'sigmoid': + act_func1 = 'sigmoid' + break + elif act_func1.strip().lower() == 'tanh': + act_func1 = 'tanh' + break + elif act_func1.strip().lower() == 'linear': + act_func1 = 'linear' + break + else: + print('\tError: Please select a valid option\n') + + while True: + act_func2 = input('Select output layer activation function (tanh, relu, sigmoid, linear). Continue with tanh: ') + if not act_func2 or act_func2.strip().lower() == 'tanh': + act_func2 = 'tanh' + break + elif act_func2.strip().lower() == 'relu': + act_func2 = 'relu' + break + elif act_func2.strip().lower() == 'sigmoid': + act_func2 = 'sigmoid' + break + elif act_func2.strip().lower() == 'linear': + act_func2 = 'linear' + break + else: + print('\tError: Please select a valid option\n') + + while True: + neurons = input('Select number of neurons per layer. Continue with 100: ') + if not neurons: + neurons = 100 + break + elif neurons.isdigit(): + neurons = int(neurons) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + shared_dim = input('Select number of shared dims. Continue with 80: ') + if not shared_dim: + shared_dim = 80 + break + elif shared_dim.isdigit(): + shared_dim = int(shared_dim) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + lr = input('Select model learning rate. Continue with 2e-3: ') + if not lr: + lr = 0.002 + break + elif is_float(lr): + lr = float(lr) + break + else: + print('\tError: Please select a number\n') + + while True: + lf = input('Select loss function (mse, custom_loss, mae). Continue with mse: ') + if not lf or lf.strip().lower() == 'mse': + lf = 'mse' + break + elif lf.strip().lower() == 'custom_loss': + lf = custom_loss + break + elif lf.strip().lower() == 'mae': + lf = 'mae' + break + else: + print('\tError: Please select a valid option\n') + + print('\n-----------------------------') + print(f''' +HYPERPARAMETERS SUMMARY:\n +Hidden Layer activation function: {act_func1} +Output Layer activation function: {act_func2} +Number of neurons: {neurons} +Number of shared dimensions: {shared_dim} +Learning rate: {lr} +Loss function: {lf} +''') + + print('-----------------------------') + print('Outputs: \n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_hybRNN_Solution' + else: + filen = f'{filen}' + + while True: + output0 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not output0 or output0.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + while True: + output1 = input('Save error made by SVD for each variable (y/n). Continue with Yes: ') + if not output1 or output1.strip().lower() in ['y', 'yes']: + output1 = 'yes' + break + elif output1.strip().lower() in ['n', 'no']: + output1 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output3 = input('Plot loss function evolution (y/n). Continue with Yes: ') + if not output3 or output3.strip().lower() in ['y', 'yes']: + output3 = 'yes' + break + elif output3.strip().lower() in ['n', 'no']: + output3 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output4 = input('Save RRMSE for each variable (Original data) (y/n). Continue with Yes: ') + if not output4 or output4.strip().lower() in ['y', 'yes']: + output4 = 'yes' + break + elif output4.strip().lower() in ['n', 'no']: + output4 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output5 = input('Save RRMSE for each variable (Truncated data) (y/n). Continue with Yes: ') + if not output5 or output5.strip().lower() in ['y', 'yes']: + output5 = 'yes' + break + elif output5.strip().lower() in ['n', 'no']: + output5 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output6 = input('Plot RRMSE temporal matrix (y/n). Continue with Yes: ') + if not output6 or output6.strip().lower() in ['y', 'yes']: + output6 = 'yes' + break + elif output6.strip().lower() in ['n', 'no']: + output6 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + while True: + output7 = input('Plot mode comparison (y/n). Continue with Yes: ') + if not output7 or output7.strip().lower() in ['y', 'yes']: + output7 = 'yes' + break + elif output7.strip().lower() in ['n', 'no']: + output7 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + if output7 == 'yes': # Indicate the number of the mode you want to compare + while True: + output71 = input('Select the number of the first "n" modes to compare. Continue with 5: ') + if not output71: + maxN = 5 + nModes = list(range(1, maxN+1)) + break + elif output71.isdigit(): + maxN = int(output71) + nModes = list(range(1, maxN+1)) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + output8 = input('Plot snapshot comparison (y/n). Continue with Yes: ') + if not output8 or output8.strip().lower() in ['y', 'yes']: + output8 = 'yes' + break + elif output8.strip().lower() in ['n', 'no']: + output8 = 'no' + break + else: + print('\tError: Select yes or no (y/n)\n') + + if output8 == 'yes': # Indicate the number of the variable and time step (nv, nt) + while True: + output81 = input(f'Select number of first "n" variables. Continue with all {int(data.shape[-1])}: ') + if not output81: + output81 = int(data.shape[-1]) + break + elif output81.isdigit(): + output81 = int(output81) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + while True: + output82 = input(f'Select time step to plot. Continue with {int(data.shape[0]/100)}: ') + if not output82: + output82 = int(data.shape[0]/100) + break + elif output82.isdigit(): + output82 = int(output82) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + index = np.array([[i, output82] for i in range(output81)]) + + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f"{path0}/{filen}") + + nt, ny, nx, nv = np.shape(data) + # Original Tensor Reconstruction + Mat_orig = np.reshape(data,[nt,nv*nx*ny]) + Mat_orig = np.transpose(Mat_orig) + Tensor_orig = np.reshape(Mat_orig,[nv,nx,ny,nt], order='F') + sum_spec = np.sum(np.sum(Tensor_orig[1:,:,:,:],axis=3),axis=0)/(nt) + + x = np.linspace(0, 1, nx) + y = np.linspace(0, 1, ny) + xv, yv = np.meshgrid(y, x) + + # Centering and Scaling + if decision1 == 'no-scaling': + pass + else: + #Dummy variables + print('\nScaling Data') + Tensor_orig1 = np.zeros(Tensor_orig.shape) + Factor = np.zeros(data.shape[3]) + Media = np.zeros(data.shape[3]) + Media_tiempo = np.zeros(np.shape(Tensor_orig)) + + for iter in range(nt): + Media_tiempo[:,:,:,iter] = np.mean(Tensor_orig,axis=3) + + Tensor_orig2 = Tensor_orig-Media_tiempo + + for iter in range(data.shape[3]): + variable = Tensor_orig2[iter,:,:,:] + if decision1 == 'range': + Factor[iter] = np.amax(variable)-np.amin(variable) #Range scaling + if decision1 == 'auto': + Factor[iter] = np.std(variable) #Auto scaling + if decision1 == 'pareto': + Factor[iter] = np.sqrt(np.std(variable)) #Pareto scaling + Media[iter] = np.mean(variable) + Tensor_orig1[iter,:,:,:] = (variable-Media[iter])/(Factor[iter]) + + Mat_orig = np.reshape(Tensor_orig1,[nv*nx*ny,nt],order='F') + + print('Data Scaled\n') + + # Perform SVD + print('Performing SVD') + U, S, V = np.linalg.svd(Mat_orig, full_matrices=False) + + Modes = n_modes + S = np.diag(S) + + U = U[:,0:Modes] + S = S[0:Modes,0:Modes] + V = V[0:Modes,:] + + print('SVD Complete\n') + + AML = np.dot(S,V) + tensor = AML + + if output1 == 'yes': + Mat_trunc = np.dot(U,AML) + Ten_trunc = np.reshape(Mat_trunc,[nv,nx,ny,nt], order='F') + + if decision1 == 'no-scaling': + pass + else: + for iter in range(data.shape[3]): + variable = Ten_trunc[iter,:,:,:] + Ten_trunc[iter,:,:,:] = variable*(Factor[iter])+Media[iter]+Media_tiempo[iter,:,:,:] + + if output1 == 'yes': + RRMSE_SVD = np.zeros(data.shape[3]) + for iter in range(data.shape[3]): + diff = Ten_trunc[iter,:,:,:] - Tensor_orig[iter,:,:,:] + RRMSE_SVD[iter] = LA.norm(np.reshape(diff.flatten(),(-1,1)),ord=2)/LA.norm(np.reshape(Tensor_orig[iter,:,:,:].flatten(),(-1,1)),ord=2) + print(f'SVD RRMSE error for variable {iter+1}: {np.round(RRMSE_SVD[iter]*100, 3)}%') + if not output0 or output0.strip().lower() in ['.npy', 'npy']: + np.save(f'{path0}/{filen}/RRMSE_SVD.npy',RRMSE_SVD) + + elif output0.strip().lower() in ['.mat', 'mat']: + mdic = {"RRMSE_SVD": RRMSE_SVD} + file_mat= str(f'{path0}/{filen}/RRMSE_SVD.mat') + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + if decision2 == 'no-scaling': + tensor_norm = tensor + elif decision2 == 'range': + min_val = np.amin(tensor) + range_val = np.ptp(tensor) + tensor_norm = (tensor-min_val)/range_val + elif decision2 == 'auto': + med_val = np.mean(tensor) + std_val =np.std(tensor) + tensor_norm = (tensor-med_val)/std_val + elif decision2 == 'MaxPerMode': + max_val = sum(np.amax(np.abs(tensor),axis=1)) + tensor_norm = tensor / max_val + + # Dataset configuration + total_length = tensor_norm.shape[1] # number of snapshots + channels_n = 0 # number of channels, assuming each snapshot is an image with n channels + dim_x = tensor_norm.shape[0] # following the simil that each snapshot is an image, the dimension x of that image + dim_y = 0 # following the simil that each snapshot is an image, the dimension y of that image + + print('\n-----------------------------') + print('Dataset configuration: \n') + print('total_length: ', total_length) + print('channels_n: ', channels_n) + print('dim_x: ', dim_x) + print('dim_y: ', dim_y) + + # Data generator + import math + class DataGenerator(tf.keras.utils.Sequence): + 'Generates data for Keras' + def __init__(self, data, list_IDs, batch_size=5, dim=(20), + k = 624, p = 1, + shuffle=True, till_end = False, only_test = False): + 'Initialization' + self.data = data + self.dim = dim + self.batch_size = batch_size + self.list_IDs = list_IDs + self.shuffle = shuffle + self.p = p + self.k = k + self.till_end = till_end + self.only_test = only_test + self.on_epoch_end() + + def __len__(self): + 'Denotes the number of batches per epoch' + if self.till_end: + lenx = math.ceil((len(self.list_IDs) / self.batch_size)) + else: + lenx = int(np.floor(len(self.list_IDs) / self.batch_size)) + return lenx + + def __getitem__(self, index): + 'Generate one batch of data' + # Generate indexes of the batch + indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size] + + # Find list of IDs + list_IDs_temp = [self.list_IDs[k] for k in indexes] + + # Generate data + X, y = self.__data_generation(list_IDs_temp) + if self.only_test: + return X + else: + return X, y + + def on_epoch_end(self): + 'Updates indexes after each epoch' + self.indexes = np.arange(len(self.list_IDs)) + if self.shuffle == True: + np.random.shuffle(self.indexes) + + def __data_generation(self, list_IDs_temp): + 'Generates data containing batch_size samples' # X : (n_samples, *dim, depth) + # Initialization + X = np.empty((self.batch_size, self.dim, self.k)) + y = [np.empty((self.batch_size, self.dim))]*self.p + + y_inter = np.empty((self.batch_size, self.dim, p)) + + # Generate data + lenn = len(list_IDs_temp) + for i, ID in enumerate(list_IDs_temp): + # Store Xtrain + X[i,:,:] = self.data[:,ID:ID+k] + # Store Ytrain + y_inter[i,:,:] = self.data[:,ID+k:ID+k+p] + + for j in range(self.p): + y[j] = y_inter[:,:,j] + y[j] = np.reshape(y[j], (lenn, -1)) + + X = X.transpose((0,2,1)) + + return X, y + + + # Prepare the dataset indexes + period_transitorio = 0 + stride_train = 1 + stride_val = 1 + stride_test = 1 + + dim=(dim_x) + + test_length = int(test_prop * total_length) + val_length = int((total_length - test_length) * val_prop) + train_length = total_length - val_length - test_length + + if int(train_length-period_transitorio-(k+p)) < 0: + train_n = 0 + elif int((train_length-period_transitorio-(k+p))//stride_train) == 0: + train_n = 1 + else: + train_n = int(((train_length-period_transitorio)-(k+p))//stride_train) + + if int(test_length-(k+p)) < 0: + test_n = 0 + elif int((test_length-(k+p))//stride_test) == 0: + test_n = 1 + else: + test_n = int((test_length-(k+p))//stride_test) + + if int(val_length-(k+p)) < 0: + val_n = 0 + elif int((val_length-(k+p))//stride_val) == 0: + val_n = 1 + else: + val_n = int((val_length-(k+p))//stride_val) + + # Indices for the beginnings of each batch + train_idxs = np.empty([train_n], dtype='int') + val_idxs = np.empty([val_n], dtype='int') + test_idxs = np.empty([test_n], dtype='int') + + j = period_transitorio + for i in range(train_n): + train_idxs[i] = j + j = j+stride_train + + j = train_length + for i in range(val_n): + val_idxs[i] = j + j = j+stride_val + + j = train_length + val_length + for i in range(test_n): + test_idxs[i] = j + j = j+stride_test + + + + # Generators + training_generator = DataGenerator(tensor_norm, train_idxs, + dim = dim, + batch_size = batch_size, + k = k, p = p, till_end = False, + only_test = False, + shuffle = True) + validation_generator = DataGenerator(tensor_norm, val_idxs, + dim = dim, + batch_size = batch_size, + k = k, p = p, till_end = False, + only_test = False, + shuffle = False) + test_generator = DataGenerator(tensor_norm, test_idxs, + dim = dim, + batch_size = batch_size, + k = k, p = p, till_end = False, + only_test = True, + shuffle = False) + + print('\n-----------------------------') + print('Model training summary: \n') + print ('test_length: ', test_length) + print ('val_length: ', val_length) + print ('train_length: ', train_length) + print() + print ('test_n: ', test_n) + print ('val_n: ', val_n) + print ('train_n: ', train_n) + print() + print('test_generator_len: ', len(test_generator)) + print('validation_generator_len: ', len(validation_generator)) + print('training_generator_len: ', len(training_generator)) + + # Prepare Ytest + test_n_adjusted = int(test_n/batch_size)*batch_size # multiplo de batch_size + Ytest = [np.empty([test_n_adjusted, dim_x], dtype='float64')] * p + Ytest_fl = [np.empty([test_n_adjusted, dim_x ], dtype='float64')] * p + + Ytest_inter = np.empty([test_n_adjusted, dim_x, p], dtype='float64') + + for i in range(test_n_adjusted): + j = test_idxs[i] + Ytest_inter[i,:,:] = tensor_norm[:,j+k:j+k+p] + + for r in range(p): + Ytest[r] = Ytest_inter[:,:,r] + Ytest_fl[r] = np.copy(np.reshape(Ytest[r], (test_n_adjusted, -1)) ) + + + + if hyper == 'No': + def create_model(in_shape, out_dim, p = 3, shared_dim = 1000, act_fun= act_func1, act_func2 = act_func2, lr = lr, lf = lf): + x = Input(shape=in_shape) + + + v = LSTM(neurons)(x) + + v = Dense(p*neurons, activation= act_fun)(v) + + v = Reshape((p,neurons))(v) + tt = [1]*p + + r = TimeDistributed( Dense(shared_dim, activation=act_fun))(v) + s = tf.split(r, tt, 1) + for i in range(p): + s[i] = Flatten()(s[i]) + + o = [] + for i in range(p): + o.append( Dense(out_dim, activation=act_func2)(s[i]) ) + + m = Model(inputs=x, outputs=o) + opt = keras.optimizers.Adam(learning_rate=lr) + m.compile(loss=lf, optimizer=opt, metrics=[lf]) + return(m) + + #create the model + + in_shape = [k, dim_x] + out_dim = dim_x + + model = create_model(in_shape,out_dim,p,shared_dim) + + print('\nModel Summary:\n') + model.summary() + + # save the best weights + save_string = 'hybrid_RNN_model' + + # save the best weights + save_best_weights = f'{path0}/{filen}/' + save_string + '.h5' + save_summary_stats = f'{path0}/{filen}/' + save_string + '.csv' + save_last_weights = f'{path0}/{filen}/' + save_string + '_last_w.h5' + save_results_metrics = f'{path0}/{filen}/' + save_string + '_results_metrics.csv' + + + # Training + np.random.seed(247531338) + t0 = time.time() + # Model training + callbacks = [ModelCheckpoint(save_best_weights, monitor='val_loss', save_best_only=True, mode='auto'), + EarlyStopping(monitor='val_loss', patience=10, verbose=1, mode='auto', min_delta = 0.0001)] + + print('\nTraining Model Please Wait...\n') + + history = model.fit(training_generator, + validation_data=validation_generator, + epochs=epoch, + verbose=1, + callbacks=callbacks) + t1 = time.time() + print('\nModel Trained Successfully!') + + print(f"\nTraining complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + if hyper == 'Yes': + import keras_tuner as kt + + def create_model_hp(hp): + hp_activation = hp.Choice('hidden_layer_activation_function', values = ['relu', 'tanh', 'linear', 'sigmoid']) + hp_neurons = hp.Int('neurons', min_value = 10, max_value = 100, step = 10) + hp_activation_1 = hp.Choice('output_layer_activation_function', values = ['relu', 'tanh', 'linear', 'sigmoid']) + hp_learning_rate = hp.Choice('learning_rate', values=[1e-2, 1e-3, 5e-3, 1e-4]) + hp_shared_dims = hp.Int('shared dims', min_value = 10, max_value = 100, step = 10) + + x = Input(shape=[k, dim_x]) + + v = LSTM(hp_neurons)(x) + v = Dense(p*hp_neurons, activation = hp_activation)(v) + v = Reshape((p,hp_neurons))(v) + tt = [1]*p + + r = TimeDistributed(Dense(hp_shared_dims, activation=hp_activation))(v) + s = tf.split(r, tt, 1) + for i in range(p): + s[i] = Flatten()(s[i]) + + o = [] + for i in range(p): + o.append(Dense(dim_x , activation=hp_activation_1)(s[i])) + + m = Model(inputs=x, outputs=o) + + opt = keras.optimizers.Adam(learning_rate=hp_learning_rate) + + m.compile(loss='mse', optimizer=opt, metrics=['mse']) + + return(m) + + if tuner_ == 'Hyperband': + tuner = kt.Hyperband(create_model_hp, objective = 'val_loss', max_epochs = 10, factor = 3, directory = 'dir_1', project_name = 'x', overwrite = True) + + elif tuner_ == 'RandomSearch': + tuner = kt.RandomSearch(create_model_hp, objective = 'val_loss', max_trials = 10, directory = 'dir_1', project_name = 'x', overwrite = True) + + elif tuner_ == 'Bayesian': + tuner = kt.BayesianOptimization(create_model_hp, objective = 'val_loss', max_trials = 10, beta = 3, directory = 'dir_1', project_name = 'x', overwrite = True) + + stop_early = tf.keras.callbacks.EarlyStopping(monitor = 'val_loss', patience = 10) + + print('\nSearching for optimal hyperparameters...\n') + + tuner.search(training_generator, + validation_data=validation_generator, + epochs=10, + verbose=1, + callbacks=[stop_early]) + + best_hps = tuner.get_best_hyperparameters(num_trials = 1)[0] + + print('\n-----------------------------') + print(f''' +HYPERPARAMETERS SUMMARY:\n +Hidden Layer activation function: {best_hps.get('hidden_layer_activation_function')} +Output Layer activation function: {best_hps.get('output_layer_activation_function')} +Number of neurons: {best_hps.get('neurons')} +Number of shared dimensions: {best_hps.get('shared dims')} +Learning rate: {best_hps.get('learning_rate')} +Loss function: 'mse' + ''') + + model = tuner.hypermodel.build(best_hps) + + print('Model Summary:\n') + model.summary() + + t0 = time.time() + + save_string = 'hybrid_RNN_model' + + save_best_weights = f'{path0}/{filen}/' + save_string + '.h5' + save_summary_stats = f'{path0}/{filen}/' + save_string + '.csv' + save_last_weights = f'{path0}/{filen}/' + save_string + '_last_w.h5' + save_results_metrics = f'{path0}/{filen}/' + save_string + '_results_metrics.csv' + + callbacks = [ModelCheckpoint(save_best_weights, monitor='val_loss', save_best_only=True, mode='auto'), + EarlyStopping(monitor='val_loss', patience=10, verbose=1, mode='auto', min_delta = 0.0001)] + + print('\nTraining Model Please Wait...\n') + + history = model.fit(training_generator, + validation_data=validation_generator, + epochs=epoch, + verbose=1, + callbacks=callbacks) + + t1 = time.time() + print('\nModel Trained Successfully!') + + print(f"\nTraining complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + print('\nPlease CLOSE ALL FIGURES to continue the run\n') + + model.save_weights(save_last_weights) + + # Aggregate the summary statistics + summary_stats = pd.DataFrame({'epoch': [ i + 1 for i in history.epoch ], + #'train_acc': history.history['mean_squared_error'], + #'valid_acc': history.history['val_mean_squared_error'], + 'train_loss': history.history['loss'], + 'valid_loss': history.history['val_loss']}) + + summary_stats.to_csv(save_summary_stats) + + if output3 == 'yes': + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - Loss function evolution') + + ax.plot(summary_stats.train_loss, 'b',linewidth = 3) # blue + ax.plot(summary_stats.valid_loss, 'g--',linewidth = 3) # green + ax.set_xlabel('Training epochs',fontsize = 20) + ax.set_ylabel('Loss function',fontsize = 20) + ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/Lossfun_training.png') + plt.show() + plt.close() + + + + # Find the min validation loss during the training + min_loss, idx = min((loss, idx) for (idx, loss) in enumerate(history.history['val_loss'])) + print('\nMinimum val_loss at epoch', '{:d}'.format(idx+1), '=', '{:.6f}'.format(min_loss)) + min_loss = round(min_loss, 4) + + # Inference + t0 = time.time() + + print('\nModel predicting. Please wait\n') + + model.load_weights(save_best_weights) + + Ytest_hat_fl = model.predict(test_generator, verbose=1) + + t1 = time.time() + print(f"\nPrediction complete. Time elapsed: {np.round(((t1 - t0) / 60.), 2)} minutes") + + print('\nPerformance measures on Test data, per sec') + lag = 0 + num_sec = Ytest_hat_fl[0].shape[0] + results_table = pd.DataFrame(index=['MSE','MAE','MAD','R2','SMAPE','RRMSE'],columns=range(num_sec)) + for i in range(num_sec): + results_table.iloc[0,i] = mean_squared_error( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[1,i] = mean_absolute_error( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[2,i] = median_absolute_error( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[3,i] = r2_score( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[4,i] = smape( Ytest_fl[lag][i,:], Ytest_hat_fl[lag][i,:]) + results_table.iloc[5,i] = RRMSE( np.reshape(Ytest_fl[lag][i,:],(-1,1)), np.reshape(Ytest_hat_fl[lag][i,:],(-1,1))) + print(results_table) + results_table.to_csv(f'{path0}/{filen}/Results.csv', index = False, sep=',') + + + print('\nPerformance measures on Test data, for all time, per time-ahead lag') + + results_table_global = pd.DataFrame(index=['MSE','MAE','MAD','R2','SMAPE','RRMSE'],columns=range(p)) + for i in range(p): + results_table_global.iloc[0,i] = mean_squared_error(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[1,i] = mean_absolute_error(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[2,i] = median_absolute_error(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[3,i] = r2_score(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[4,i] = smape(Ytest_fl[i].flatten(), Ytest_hat_fl[i].flatten()) + results_table_global.iloc[5,i] = RRMSE( np.reshape(Ytest_fl[i].flatten(),(-1,1)), np.reshape(Ytest_hat_fl[i].flatten(),(-1,1))) + + results_table_global['mean'] = results_table_global.mean(axis=1) + print(results_table_global) + results_table_global.to_csv(f'{path0}/{filen}/Global_results.csv', index = False, sep=',') + + print(f'\nATTENTION!: All plots will be saved to {path0}/{timestr}_CNN1D_Solution\n') + print('Please CLOSE all figures to continue the run\n') + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - MSE score') + ax.plot(range(num_sec),results_table.iloc[0,:], 'b') # green + ax.set_title("MSE score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("MSE",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/MSE_score.png') + plt.show() + plt.close() + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - MAE score') + ax.plot(range(num_sec),results_table.iloc[1,:], 'b') # green + ax.set_title("MAE score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("MAE",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/MAE_score.png') + plt.show() + plt.close() + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - MAD score') + ax.plot(range(num_sec),results_table.iloc[2,:], 'b') # green + ax.set_title("MAD score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("MAD",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/MAD_score.png') + plt.show() + plt.close() + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - R2 score') + ax.plot(range(num_sec),results_table.iloc[3,:], 'b') # green + ax.set_title("R2 score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("R2",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/R2_score.png') + plt.show() + plt.close() + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - SMAPE score') + ax.plot(range(num_sec),results_table.iloc[4,:], 'b') # green + ax.set_title("SMAPE score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("SMAPE",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/SMAPE_score.png') + plt.show() + plt.close() + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - RRMSE score') + ax.plot(range(num_sec),results_table.iloc[5,:], 'b') # green + ax.set_title("RRMSE score vs. forecast time index",fontsize=18) + ax.set_xlabel("time", fontsize=15) + ax.set_ylabel("RRMSE",fontsize=15) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/RRMSE_score.png') + plt.show() + plt.close() + + Ytest_hat_fl = model.predict(test_generator, verbose=1) + + # Create the multidimensional arrays for the results and ground-truth values + mat_pred = np.zeros((Ytest_fl[0].shape[0],p,Ytest_fl[0].shape[1])) + + + # Fill the output arrays + for i in range(p): + for j in range(Ytest_fl[0].shape[0]): + mat_pred[j,i,:]=Ytest_hat_fl[i][j,:] + + if decision2 == 'no-scaling': + pass + elif decision2 == 'auto': + mat_pred = mat_pred * std_val + med_val + elif decision2 == 'range': + mat_pred = mat_pred * range_val + min_val + elif decision2 == 'MaxPerMode': + mat_pred = mat_pred * max_val + + + # Data Extraction and Tensor Reconstruction + new_dim = [mat_pred.shape[0],mat_pred.shape[2]] + + AML_pre_LSTM = np.transpose(np.squeeze(mat_pred[:,0,:])) + num_snap = AML_pre_LSTM.shape[1] + + mat_time_slice_index = np.zeros((Ytest_fl[0].shape[0],p),dtype=int) + for i in range(p): + for j in range(Ytest_fl[0].shape[0]): + mat_time_slice_index[j,i]=test_idxs[j]+k+i + + time_lag = mat_time_slice_index[0,0] + + # Matrix reconstruction + + Mat_pre = np.dot(U,AML_pre_LSTM) + + # Tensor reconstruction + Ten_pre = np.reshape(Mat_pre, [nv,nx,ny,AML_pre_LSTM.shape[1]], order='F') + + for iter in range(data.shape[3]): + variable = Ten_pre[iter,:,:,:] + Ten_pre[iter,:,:,:] = variable*(Factor[iter])+Media[iter]+Media_tiempo[iter,:,:,:Ten_pre.shape[3]] + + print('\n') + # RRMSE Measure with Original Tensor + if output4 == 'yes': + RRMSE_orNN = np.zeros(Tensor_orig.shape[0]) + for iter in range(Tensor_orig1.shape[0]): + + diff = Ten_pre[iter,:,:,:] - Tensor_orig[iter,:,:,time_lag:time_lag+num_snap] + RRMSE_orNN[iter] = LA.norm(np.reshape(diff.flatten(),(-1,1)),ord=2)/LA.norm(np.reshape(Tensor_orig[iter,:,:,time_lag:time_lag+num_snap].flatten(),(-1,1)),ord=2) + + print(f'RRMSE measure with original tensor - variable {iter+1}: {np.round(RRMSE_orNN[iter]*100, 3)}%') + + if not output0 or output0.strip().lower() in ['.npy', 'npy']: + np.save(f"{path0}/{filen}/RRMSE_orNN.npy", RRMSE_orNN) + + elif output0.strip().lower() in ['.mat', 'mat']: + mdic = {"RRMSE_orNN": RRMSE_orNN} + file_mat= str(f"{path0}/{filen}/RRMSE_orNN.mat") + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + print() + # RRMSE with Truncated + if output5 == 'yes': + RRMSE_trNN = np.zeros(Tensor_orig.shape[0]) + for iter in range(Tensor_orig1.shape[0]): + + diff = Ten_pre[iter,:,:,:] - Ten_trunc[iter,:,:,time_lag:time_lag+num_snap] + RRMSE_trNN[iter] = LA.norm(np.reshape(diff.flatten(),(-1,1)),ord=2)/LA.norm(np.reshape(Ten_trunc[iter,:,:,time_lag:time_lag+num_snap].flatten(),(-1,1)),ord=2) + + print(f'RRMSE measure with truncated tensor - variable {iter+1}: {np.round(RRMSE_trNN[iter]*100, 3)}%') + + if not output0 or output0.strip().lower() in ['.npy', 'npy']: + np.save(f"{path0}/{filen}/RRMSE_trNN.npy", RRMSE_trNN) + + elif output0.strip().lower() in ['.mat', 'mat']: + mdic = {"RRMSE_trNN": RRMSE_trNN} + file_mat= str(f"{path0}/{filen}/RRMSE_trNN.mat") + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + # RRMSE Measure + if output6 == 'yes': + diff = AML_pre_LSTM - AML[:,time_lag:time_lag+num_snap] + globalRRMSE = LA.norm(np.reshape(diff.flatten(),(-1,1)),ord=2)/LA.norm(np.reshape(AML[:,time_lag:time_lag+num_snap].flatten(),(-1,1)),ord=2) + print(f'\nGlobal RRMSE measure - {np.round(globalRRMSE*100, 3)}%') + + rrmse_arr = np.zeros(num_snap) + for i in range(num_snap): + diff = AML[:,time_lag+i]-AML_pre_LSTM[:,i] + rrmse_arr[i] = LA.norm(diff.flatten(),2)/LA.norm(np.transpose(AML[:,i]).flatten(),2) + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - RRMSE Measure') + ax.plot(rrmse_arr,'k*-') + ax.set_title('RRMSE measure') + ax.set_xlabel('Time') + ax.set_ylabel('RRMSE') + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/rrmse_measure.png') + plt.show() + plt.close() + + # AML Comparison + if output7 == 'yes': + # AML Comparation + if not os.path.exists(f'{path0}/{filen}/Mode_Comparison'): + os.mkdir(f"{path0}/{filen}/Mode_Comparison") + + AML_pre_1 = np.zeros([AML.shape[0],time_lag]) + AML_pre_1 = np.concatenate((AML_pre_1,AML_pre_LSTM),axis=1) + + for i in nModes: + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - AML vs AMLPRE per mode') + ax.plot(AML[i-1,time_lag:],'k*-') + ax.plot(AML_pre_1[i-1,time_lag:],'m*-') + ax.set_xlabel("Time") + ax.set_title(f"Comparation between AML and AMLPRE at mode {i}") + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/Mode_Comparison/aMLComparation_m_{i}_test.png') + plt.show() + plt.close() + + # Tensor Comparison + if output8 == 'yes': + if not os.path.exists(f'{path0}/{filen}/Snapshots'): + os.mkdir(f"{path0}/{filen}/Snapshots") + + for i in range(index.shape[0]): + namefig_orig1 = f'{path0}/{filen}/Snapshots/snap_comp_{index[i,0]+1}_t_{index[i,1]+1}.png' + fig, ax = plt.subplots(num = f'CLOSE TO CONTINUE RUN - Snapshot comparison - snap_comp_{index[i,0]+1}_t_{index[i,1]+1}') + + ax.contourf(xv,yv,Tensor_orig[int(index[i,0]),:,:,time_lag+int(index[i,1])],100,cmap='jet', + vmin = np.amin(Tensor_orig[int(index[i,0]),:,:,time_lag+int(index[i,1])]), + vmax = np.amax(Tensor_orig[int(index[i,0]),:,:,time_lag+int(index[i,1])])) + + ax.contourf(-xv,yv,Ten_pre[int(index[i,0]),:,:,int(index[i,1])],100,cmap='jet', + vmin = np.amin(Tensor_orig[int(index[i,0]),:,:,time_lag+int(index[i,1])]), + vmax = np.amax(Tensor_orig[int(index[i,0]),:,:,time_lag+int(index[i,1])])) + + ax.set_title('Prediction vs. Original Data') + ax = plt.gca() + ax.set_aspect(1) + props = dict(boxstyle='round', facecolor='white', alpha=1) + ax.annotate('', xy=(0.5, -0.005), xycoords='axes fraction', xytext=(0.5, 1.005), + arrowprops=dict(arrowstyle='-', lw = 3, color='k')) + plt.savefig(namefig_orig1) + plt.show() + + if not output0 or output0.strip().lower() in ['.npy', 'npy']: + np.save(f"{path0}/{filen}/TensorPred.npy", Ten_pre) + + elif output0.strip().lower() in ['.mat', 'mat']: + mdic = {"Ten_pre": Ten_pre} + file_mat= str(f"{path0}/{filen}/TensorPred.mat") + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') diff --git a/v0.1_ModelFLOWs_app/ModelFLOWs_app.py b/v0.1_ModelFLOWs_app/ModelFLOWs_app.py new file mode 100644 index 0000000..6bfbf63 --- /dev/null +++ b/v0.1_ModelFLOWs_app/ModelFLOWs_app.py @@ -0,0 +1,349 @@ +import warnings +warnings.filterwarnings("ignore", message="loaded more than 1 DLL from .libs") + +import AEmodel +import DLsuperresolution +import FullDLmodel +import Superresolution +import GappyRepair +import mdHODMD +import mdHODMD_pred +import HODMD +import HODMD_pred +import mainHOSVD +import HybCNNpredmodel +import HybRNNpredmodel +import SVD + +__version__ = '0.1' +__author__ = 'ModelFLOWs Research Group - E.T.S.I. Aeronautica y del Espacio - Universidad Politecnica de Madrid' + +print('\n\n') +print(''' + __ __ _ _ _____ _ ___ __ __ + | \/ | ___ __| | ___ | || ___|| | / _ \\\ \ / /___ __ _ _ __ _ __ + | |\/| | / _ \ / _` | / _ \| || |_ | | | | | |\ \ /\ / // __| _____ / _` || '_ \ | '_ \ + | | | || (_) || (_| || __/| || _| | |___| |_| | \ V V / \__ \|_____|| (_| || |_) || |_) | + |_| |_| \___/ \__,_| \___||_||_| |_____|\___/ \_/\_/ |___/ \__,_|| .__/ | .__/ + |_| |_| + + ModelFLOWs Application + ---------------------- +''') + +print(f''' +Authors: {__author__} + +Version: {__version__} + + +This data-driven application consists of two modules: +- Modal Decomposition +- Deep Learning + +Both blocks consist of algorithms capable of: +- detecting patterns, +- repairing and enhancing data, +- and predicting data from complex flow databases + +The databases can be in the following formats: +- MATLAB ".mat" +- Numpy ".npy" +- Pickle ".pkl" +- Pandas ".csv" +- h5 ".h5" + +This application takes in databases with the following shapes: +- 1D Arrays -> (1, n) +- 2D Matrices -> (x, y) +- 3D Tensors -> (y, x, t) +- 4D Tensors -> (m, y, x, t) +- 5D Tensors -> (m, y, x, z, t) + +IMPORTANT: The data that defines the spatial mesh (x, y, z) must be located in the specified positions + +For more information please visit: https://modelflows.github.io/modelflowsapp/ +''') + +while True: + print(''' +----------------------MODULES---------------------- + +Modules: +1) Modal Decomposition +2) Deep Learning + + ''') + while True: + module = input('Select a MODULE (1/2): ') + if module.isdigit(): + if int(module) == 1: + break + elif int(module) == 2: + break + else: + print('\tError: Please select a valid module\n') + else: + print('\tError: Please introduce a valid input\n') + + print(''' +----------------------ACTIONS---------------------- + +Operations: +1) Pattern detection +2) Data repairing +3) Prediction +0) Exit + + ''') + + while True: + operation = input('Select an OPERATION (1/2/3): ') + if operation.isdigit(): + if int(operation) == 1: + break + elif int(operation) == 2: + break + elif int(operation) == 3: + break + elif int(operation) == 0: + break + else: + print('\tError: Please select a valid operation\n') + else: + print('\tError: Please introduce a valid input\n') + + if int(module) == 1 and int(operation) == 1: + print(''' +-----------------PATTERN DETECTION----------------- + +Algorithms or mathematic methods for pattern detection +1) SVD +2) HOSVD +3) HODMD +0) Exit + + ''') + while True: + option = input('Select an ALGORITHM (1/2/3): ') + if option.isdigit(): + if int(option) == 3: + break + elif int(option) == 2: + mainHOSVD.HOSVD() + break + elif int(option) == 1: + SVD.SVD() + break + elif int(option) == 0: + break + else: + print('\tError: Please select a valid algorithm\n') + else: + print('\tError: Please introduce a valid input\n') + + if int(option) == 3: + print(''' +-----------------------HODMD----------------------- + +Available HODMD algorithms +1) HODMD +2) Multi-dimensional HODMD +0) Exit + + ''') + while True: + type = input('Select a HODMD ALGORITHM (1/2): ') + if type.isdigit(): + if int(type) == 1: + HODMD.HODMD() + break + elif int(type) == 2: + mdHODMD.mdHODMD() + break + elif int(type) == 0: + break + else: + print('\tError: Please select a valid HODMD algorithm\n') + else: + print('\tError: Please introduce a valid input\n') + + if int(module) == 1 and int(operation) == 2: + print(''' +----------------DATA RECONSTRUCTION---------------- + +Gappy operations +1) Data repairing +2) Data enhancement (superresolution) +0) Exit + + ''') + while True: + option = input('Select an OPTION (1/2): ') + if option.isdigit(): + if int(option) == 1: + GappyRepair.GappyRepair() + break + elif int(option) == 2: + Superresolution.GappyResolution() + break + elif int(option) == 0: + break + else: + print('\tError: Please select a valid option\n') + else: + print('\tError: Please introduce a valid input\n') + + if int(module) == 1 and int(operation) == 3: + print(''' +---------------------PREDICTION-------------------- + +Algorithms or mathematic methods for prediction +1) Predictive HODMD +2) Predictive Multi-dimensional HODMD +0) Exit + + ''') + while True: + type = input('Select a HODMD ALGORITHM (1/2): ') + if type.isdigit(): + if int(type) == 1: + HODMD_pred.HODMDpred() + break + elif int(type) == 2: + mdHODMD_pred.mdHODMDpred() + break + elif int(type) == 0: + break + else: + print('\tError: Please select a valid HODMD algorithm\n') + else: + print('\tError: Please introduce a valid input\n') + + if int(module) == 2 and int(operation) == 1: + print(''' +-----------------PATTERN DETECTION----------------- + + ''') + AEmodel.autoencoders() + + if int(module) == 2 and int(operation) == 2: + print(''' +----------------DATA RECONSTRUCTION---------------- + + ''') + DLsuperresolution.DNNreconstruct() + + if int(module) == 2 and int(operation) == 3: + print(''' +---------------------PREDICTION-------------------- + +Model type: +1) Full Deep Learning model +2) Hybrid Deep Learning model (SVD + DL) +0) Exit + + ''') + while True: + option = input('Select a MODEL TYPE (1/2): ') + if option.isdigit(): + if int(option) == 1: + break + elif int(option) == 2: + break + elif int(option) == 0: + break + else: + print('\tError: Please select a valid model type\n') + else: + print('\tError: Please introduce a valid input\n') + + if int(option) == 1: + print(''' +--------------FULL DEEP LEARNING MODEL------------- + +Model architecture: +1) CNN +2) RNN +0) Exit + + ''') + while True: + type = input('Select a MODEL ARCHITECTURE (1/2): ') + if type.isdigit(): + if int(type) == 1: + FullDLmodel.FullDL('cnn') + break + elif int(type) == 2: + FullDLmodel.FullDL('rnn') + break + elif int(type) == 0: + break + else: + print('\tError: Please select a valid model architecture\n') + else: + print('\tError: Please introduce a valid input\n') + + elif int(option) == 2: + print(''' +-------------HYBRID DEEP LEARNING MODEL------------ + +Model architecture: +1) SVD + CNN +2) SVD + RNN +0) Exit + + ''') + + while True: + type = input('Select a MODEL ARCHITECTURE (1/2): ') + if type.isdigit(): + if int(type) == 1: + HybCNNpredmodel.hybCNN() + break + elif int(type) == 2: + HybRNNpredmodel.hybRNN() + break + elif int(type) == 0: + break + else: + print('\tError: Please select a valid model architecture\n') + else: + print('\tError: Please introduce a valid input\n') + + while True: + cont = input('\nWould you like to perform another operation? (y/n): ') + if cont.strip().lower() in ['y', 'yes']: + print('\n') + break + if cont.strip().lower() in ['n', 'no']: + print('\n\n') + print(''' + __ __ _ _ _____ _ ___ __ __ + | \/ | ___ __| | ___ | || ___|| | / _ \\\ \ / /___ __ _ _ __ _ __ + | |\/| | / _ \ / _` | / _ \| || |_ | | | | | |\ \ /\ / // __| _____ / _` || '_ \ | '_ \ + | | | || (_) || (_| || __/| || _| | |___| |_| | \ V V / \__ \|_____|| (_| || |_) || |_) | + |_| |_| \___/ \__,_| \___||_||_| |_____|\___/ \_/\_/ |___/ \__,_|| .__/ | .__/ + |_| |_| + + Exiting ModelFLOWs-app... + ------------------------- + ''') + exit() + else: + print('\tError: Please select YES or NO (y/n)\n') + + if cont.strip().lower() in ['y', 'yes']: + continue + + + + + + + + + + + + diff --git a/v0.1_ModelFLOWs_app/Requirements.txt b/v0.1_ModelFLOWs_app/Requirements.txt new file mode 100644 index 0000000..aed4941 --- /dev/null +++ b/v0.1_ModelFLOWs_app/Requirements.txt @@ -0,0 +1,8 @@ +tensorflow==2.10 +scikit-learn==1.2 +ffmpeg +hdf5storage +numba==0.56.4 +scipy==1.9.3 +keras-tuner +protobuf==3.20 \ No newline at end of file diff --git a/v0.1_ModelFLOWs_app/SVD.py b/v0.1_ModelFLOWs_app/SVD.py new file mode 100644 index 0000000..2e84aff --- /dev/null +++ b/v0.1_ModelFLOWs_app/SVD.py @@ -0,0 +1,222 @@ +import numpy as np +import data_load +import os +import matplotlib.pyplot as plt +path0 = os.getcwd() +import time +import hdf5storage +timestr = time.strftime("%Y-%m-%d_%H.%M.%S") +os.environ['KMP_DUPLICATE_LIB_OK']='True' + + +def svdtrunc(A): + U, S, V = np.linalg.svd(A, full_matrices = False) + return U, S, V + +def SVD(): + + print('\nSVD Algorithm') + print('\n-----------------------------') + print('Inputs:' + '\n') + + while True: + filetype = input('Select the input file format (.mat, .npy, .csv, .pkl, .h5): ') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + Tensor, _ = data_load.main(filetype) + + while True: + n_modes = input('Select number of SVD modes. Continue with 18: ') + if not n_modes: + n_modes = 18 + break + if n_modes.isdigit(): + n_modes = int(n_modes) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + print('\n-----------------------------') + print('SVD summary:') + print(f'Number of modes to retain: {n_modes}') + + print('\n-----------------------------') + print('Outputs:\n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_SVD_solution_modes_{n_modes}' + else: + filen = f'{filen}' + + while True: + decision_2 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not decision_2 or decision_2.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + + print('') + + dims = Tensor.ndim + shape = Tensor.shape + + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f"{path0}/{filen}") + + if dims > 2: + dims_prod = np.prod(shape[:-1]) + Tensor0 = np.reshape(Tensor, (dims_prod, shape[-1])) + + U, S, V = svdtrunc(Tensor0) + + S = np.diag(S) + + U = U[:,0:n_modes] + S = S[0:n_modes,0:n_modes] + V = V[0:n_modes,:] + + Reconst = (U @ S) @ V + + svd_modes = np.dot(U, S) + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - SVD modes') + for i in range(S.shape[0]): + ax.plot(i+1, S[i][i] / S[0][0], "k*") + + ax.set_yscale('log') # Logarithmic scale in y axis + ax.set_xlabel('SVD modes') + ax.set_ylabel('Singular values') + ax.set_title('SVD modes vs. Singular values') + plt.savefig(f'{path0}/{filen}/svd_modes_plot.png', bbox_inches='tight') + plt.show() + plt.close() + + if dims <= 2: + print(f'SVD modes shape: {svd_modes.shape}') + print(f'Reconstructed tensor shape: {Reconst.shape}') + + # Usado para verificar. Borrar despues + + Norm2V = np.linalg.norm(Tensor.flatten(), 2) + diff = (Tensor - Reconst).flatten() + Norm2diff = np.linalg.norm(diff, ord=2) + RelativeErrorRMS = Norm2diff/Norm2V + print('\n' + f'The relative error (RMS) is: ' + format(RelativeErrorRMS,'e')) + + fig, ax = plt.subplots(num=f'CLOSE TO CONTINUE RUN - SVD mode') + ax.contourf(svd_modes) + ax.set_title('SVD mode') + ax.set_xlabel('X') + ax.set_ylabel('Y') + plt.show() + + elif dims > 2: + newshape = [] + newshape.append(shape[:-1]) + newshape.append(svd_modes.shape[-1]) + newshape = list(newshape[0]) + [newshape[1]] + svd_modes = np.reshape(svd_modes, np.array(newshape)) + print(f'SVD modes shape: {svd_modes.shape}') + Reconst = np.reshape(Reconst, shape) + print(f'Reconstructed tensor: {Reconst.shape}') + + # Usado para verificar. Borrar despues + + RRMSE = np.linalg.norm(np.reshape(Tensor-Reconst,newshape=(np.size(Tensor),1)),ord=2)/np.linalg.norm(np.reshape(Tensor,newshape=(np.size(Tensor),1))) + print(f'\nRelative mean square error made in the calculations: {np.round(RRMSE*100, 3)}%\n') + + if not decision_2 or decision_2.strip().lower() in ['npy', '.npy']: + np.save(f'{path0}/{filen}/Reconstruction.npy', Reconst) + np.save(f'{path0}/{filen}/svd_modes.npy', svd_modes) + + if decision_2.strip().lower() in ['.mat', 'mat']: + mdic0 = {"Reconst": Reconst} + mdic1 = {"svd_modes": svd_modes} + + file_mat0 = str(f'{path0}/{filen}/Reconstruction.mat') + file_mat1 = str(f'{path0}/{filen}/svd_modes.mat') + + hdf5storage.savemat(file_mat0, mdic0, appendmat=True, format='7.3') + hdf5storage.savemat(file_mat1, mdic1, appendmat=True, format='7.3') + + + print('Please CLOSE all figures to continue the run\n') + + while True: + while True: + ModeNum = input(f'Introduce the mode number to plot (default mode 1). Maximum number of modes is {svd_modes.shape[-1]}: ') + if not ModeNum: + ModeNum = 0 + break + elif ModeNum.isdigit(): + if int(ModeNum) <= svd_modes.shape[-1]: + ModeNum = int(ModeNum)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + if dims > 3: + while True: + ModComp = input(f'Introduce the component to plot (default component 1). Maximum number of components is {svd_modes.shape[0]}: ') + if not ModComp: + ModComp = 0 + break + elif ModComp.isdigit(): + if int(ModComp) <= svd_modes.shape[0]: + ModComp = int(ModComp)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + elif dims==3: + fig, ax = plt.subplots(num=f'CLOSE TO CONTINUE RUN - SVD mode') + ax.contourf(svd_modes[:,:,ModeNum]) + ax.set_title(f'SVD modes - Mode Number {ModeNum+1}') + ax.set_xlabel('X') + ax.set_ylabel('Y') + plt.show() + + if dims==4: + fig, ax = plt.subplots(num=f'CLOSE TO CONTINUE RUN - SVD mode') + ax.contourf(svd_modes[ModComp,:,:,ModeNum]) + ax.set_title(f'SVD modes - Component {ModComp+1} - Mode Number {ModeNum+1}') + ax.set_xlabel('X') + ax.set_ylabel('Y') + plt.show() + + elif dims==5: + fig, ax = plt.subplots(num=f'CLOSE TO CONTINUE RUN - SVD mode XY plane') + ax.contourf(svd_modes[ModComp,:,:,0,ModeNum]) + ax.set_title(f'SVD modes XY plane - Component {ModComp+1} - Mode Number {ModeNum+1}') + ax.set_xlabel('X') + ax.set_ylabel('Y') + plt.show() + + while True: + Resp = input('Do you want to plot another mode? Yes or No (y/n). Press enter to continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + if Resp == 1: + continue + + + + + diff --git a/v0.1_ModelFLOWs_app/Superresolution.py b/v0.1_ModelFLOWs_app/Superresolution.py new file mode 100644 index 0000000..1604ea9 --- /dev/null +++ b/v0.1_ModelFLOWs_app/Superresolution.py @@ -0,0 +1,259 @@ +def GappyResolution(): + import numpy as np + from numpy import linalg as LA + import matplotlib.pyplot as plt + import data_load + import os + import time + import hosvd + import hdf5storage + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + ## INPUTS + path0 = os.getcwd() + + print('\nData Enhancement') + print('\n-----------------------------') + print('Inputs: \n') + while True: + filetype = input('Select the downsampled input file format (.mat, .npy, .csv, .pkl, .h5): ') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + A_down, _ = data_load.main(filetype) + + if A_down.ndim == 2: + method = 'svd' + m = [0, 1] + else: + method = 'hosvd' + if A_down.ndim == 3: + m = [0, 1] + elif A_down.ndim == 4: + m = [1, 2] + elif A_down.ndim == 5: + m = [1, 2, 3] + + while True: + aug = input('Introduce the enhancement factor (2^factor): ') + if aug.isdigit(): + enhance = int(aug) + break + else: + print('\tError: Please introduce a number (must be integer)\n') + + + # Output + print('\n-----------------------------') + print('Data enhancement summary:') + print('\n' + f'Method used: {method.upper()}') + print(f'Enhacement scale: {2**enhance}') + print('\n-----------------------------') + print('Outputs:' + '\n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_Gappy_data_enhancement_factor_{enhance}' + else: + filen = f'{filen}' + + while True: + decision = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not decision or decision.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + + print('') + + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f"{path0}/{filen}") + + A_d = A_down + + for ii in range(enhance): + if method =='svd': + print('Performing SVD. Please wait...') + [U,S,V]=LA.svd(A_d, full_matrices=False) + S = np.diag(S) + print('SVD complete!\n') + + if ii == 0: + nm = S.shape[0] + + x = np.linspace(0, 1, A_d.shape[0]*2) + y = np.linspace(0, 1, A_d.shape[1]*2) + U_dens = np.zeros((x.shape[0],U.shape[1])) + V_dens = np.zeros((V.shape[0],y.shape[0])) + + for j in range(S.shape[0]): + Udenscolumn = U[:,j] + U_dens[:,j] = np.interp(x, x[0:x.shape[0]:2], Udenscolumn) + Vdenscolumn = V[j,:] + V_dens[j,:] = np.interp(y, y[0:y.shape[0]:2], Vdenscolumn) + + A_reconst = U_dens @ S @ V_dens + + print('Performing SVD. Please wait...') + [Ur,Sr,Vr] = LA.svd(A_reconst) + print('SVD complete!\n') + Sr = np.diag(Sr) + A_d = Ur[:,:nm] @ Sr[:nm,:nm] @ Vr[:nm,:] + + elif method =='hosvd': + + if ii == 0: + n = A_d.shape + + _ , S, U, _ , _ = hosvd.HOSVD_function(A_d,n) + Udens = U + + for dim in m: + x = np.linspace(0, 1, U[0][dim].shape[0]*2) + U_dens = np.zeros((x.shape[0],S.shape[dim])) + for j in range(S.shape[dim]): + Udenscolumn = U[0][dim][:,j] + U_dens[:,j] = np.interp(x, x[0:x.shape[0]:2], Udenscolumn) + Udens[0][dim] = U_dens + + print('Performing HOSVD. Please wait...') + A_d = hosvd.tprod(S, Udens) + A_d = hosvd.HOSVD_function(A_d,n)[0] + print('HOSVD complete!\n') + + A_reconst = A_d + + print(f''' +Reconstruction complete! + +Reconstruction summary: +Original data shape: {A_down.shape} +Enhanced data shape: {A_reconst.shape} + + ''') + + if not decision or decision.strip().lower() in ['npy', '.npy']: + np.save(f"{path0}/{filen}/enhanced_data.npy", A_reconst) + + if decision.strip().lower() in ['.mat', 'mat']: + mdic = {"Enhancement": A_reconst} + file_mat= str(f'{path0}/{filen}/enhanced_data.mat') + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + dims = A_reconst.ndim + + print('Please CLOSE all figures to continue the run\n') + + if dims == 2: + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - Original data vs. Enhanced data') + plt.suptitle('Original data vs. Enhanced data') + ax[0].contourf(A_down) + ax[0].set_title('Original data') + ax[0].xaxis.grid(True, zorder = 0) + ax[0].yaxis.grid(True, zorder = 0) + ax[1].contourf(A_reconst) + ax[1].set_title('Enhanced data') + ax[1].xaxis.grid(True, zorder = 0) + ax[1].yaxis.grid(True, zorder = 0) + plt.show() + + if dims > 2: + while True: + if dims > 3: + while True: + v = input(f'Select a component to plot (max. is {A_down.shape[0]}). Continue with 1: ') + if not v: + v = 0 + break + elif v.isdigit(): + if int(v) <= A_down.shape[0]: + v = int(v) - 1 + break + else: + print('\tError: Selected value is out of range') + else: + print('\tError: Select a valid number format (must be integer)\n') + while True: + t = input(f'Select a snapshot to plot (max. is {A_down.shape[-1]}). Continue with 1: ') + if not t: + t = 0 + break + elif t.isdigit(): + if int(t) <= A_down.shape[-1]: + t = int(t) - 1 + break + else: + print('\tError: Selected value is out of range') + else: + print('\tError: Select a valid number format (must be integer)\n') + + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - Original data vs. Enhanced data') + plt.suptitle('Original data vs. Enhanced data') + + if dims == 3: + ax[0].contourf(A_down[:, :, t]) + ax[0].set_title('Original data') + ax[0].xaxis.grid(True, zorder = 0) + ax[0].yaxis.grid(True, zorder = 0) + ax[1].contourf(A_reconst[:, :, t]) + ax[1].set_title('Enhanced data') + ax[1].xaxis.grid(True, zorder = 0) + ax[1].yaxis.grid(True, zorder = 0) + plt.show() + + if dims == 4: + ax[0].contourf(A_down[v, :, :, t]) + ax[0].set_title('Original data') + ax[0].xaxis.grid(True, zorder = 0) + ax[0].yaxis.grid(True, zorder = 0) + ax[1].contourf(A_reconst[v, :, :, t]) + ax[1].set_title('Enhanced data') + ax[1].xaxis.grid(True, zorder = 0) + ax[1].yaxis.grid(True, zorder = 0) + plt.show() + + if dims == 5: + nz = int(A_down.shape[3] / 2) + ax[0].contourf(A_down[v, :, :, nz, t]) + ax[0].set_title('Original data - XY Plane') + ax[0].xaxis.grid(True, zorder = 0) + ax[0].yaxis.grid(True, zorder = 0) + ax[1].contourf(A_reconst[v, :, :, nz, t]) + ax[1].set_title('Enhanced data - XY Plane') + ax[1].xaxis.grid(True, zorder = 0) + ax[1].yaxis.grid(True, zorder = 0) + plt.show() + + while True: + Resp = input('Do you want to plot another figure? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + if Resp == 1: + continue + + + + + + + + + + + + + + + diff --git a/v0.1_ModelFLOWs_app/data_load.py b/v0.1_ModelFLOWs_app/data_load.py new file mode 100644 index 0000000..e7fd297 --- /dev/null +++ b/v0.1_ModelFLOWs_app/data_load.py @@ -0,0 +1,148 @@ +import hdf5storage +import numpy as np +import pickle +import pandas as pd +import h5py +import os +from sys import platform + +def load_data(format, wantedfile): + # LOAD MATLAB .mat FILES + if format == 'mat': + # Check the operating system + if platform in ["linux", "linux2"]: + # linux + Tensor_ = hdf5storage.loadmat(f'{wantedfile}') + Tensor = list(Tensor_.values())[-1] + elif platform == "darwin": + # OS X + Tensor_ = hdf5storage.loadmat(f'{wantedfile}') + Tensor = list(Tensor_.values())[-1] + elif platform in ["win32", "win64"]: + # Windows + Tensor_ = hdf5storage.loadmat(f'{wantedfile}') + Tensor = list(Tensor_.values())[-1] + return Tensor + +#----------------------------------------------------------------------- + + # LOAD NUMPY .npy FILES + elif format == 'npy': + # Check the operating system + if platform in ["linux", "linux2"]: + # linux + Tensor = np.load(f'{wantedfile}') + elif platform == "darwin": + # OS X + Tensor = np.load(f'{wantedfile}') + elif platform in ["win32", "win64"]: + # Windows + Tensor = np.load(f'{wantedfile}') + return Tensor + +#----------------------------------------------------------------------- + + # LOAD .csv FILES + elif format == 'csv': + # Check the operating system + if platform in ["linux", "linux2"]: + # linux + Tensor = pd.read_csv(f'{wantedfile}') + Tensor = np.array(Tensor) + elif platform == "darwin": + # OS X + Tensor = pd.read_csv(f'{wantedfile}') + Tensor = np.array(Tensor) + elif platform in ["win32", "win64"]: + # Windows + Tensor = pd.read_csv(f'{wantedfile}') + Tensor = np.array(Tensor) + return Tensor + +#----------------------------------------------------------------------- + + # LOAD .h5 FILES + elif format == 'h5': + # Check the operating system + if platform in ["linux", "linux2"]: + # linux + with h5py.File(f'{wantedfile}', 'r+') as file: + for dataset_name in file: + Tensor = file[dataset_name][()] + elif platform == "darwin": + # OS X + with h5py.File(f'{wantedfile}', 'r+') as file: + for dataset_name in file: + Tensor = file[dataset_name][()] + elif platform in ["win32", "win64"]: + # Windows + with h5py.File(f'{wantedfile}', 'r+') as file: + for dataset_name in file: + Tensor = file[dataset_name][()] + return Tensor + +#----------------------------------------------------------------------- + + # LOAD .pkl FILES + elif format == 'pkl': + # Check the operating system + if platform in ["linux", "linux2"]: + # linux + with open(f'{wantedfile}', 'rb') as file: + Tensor = pickle.load(file) + elif platform == "darwin": + # OS X + with open(f'{wantedfile}', 'rb') as file: + Tensor = pickle.load(file) + elif platform in ["win32", "win64"]: + # Windows + with open(f'{wantedfile}', 'rb') as file: + Tensor = pickle.load(file) + return Tensor + +def main(filetype): + while True: + if filetype.strip().lower() in ['mat', '.mat']: + wantedfile = input('Introduce name of input data file (as in: Desktop/Folder/data.mat): ') + if os.path.exists(f'{wantedfile}'): + Tensor = load_data('mat', wantedfile) + break + else: + print(f'\tError: File does not exist in the selected path\n') + + elif filetype.strip().lower() in ['.npy', 'npy']: + wantedfile = input('Introduce name of input data file (as in: Desktop/Folder/data.npy): ') + if os.path.exists(f'{wantedfile}'): + Tensor = load_data('npy', wantedfile) + break + else: + print(f'\tError: File does not exist in the selected path\n') + + elif filetype.strip().lower() in ['csv', '.csv']: + wantedfile = input('Introduce name of input data file (as in: Desktop/Folder/data.csv): ') + if os.path.exists(f'{wantedfile}'): + Tensor = load_data('csv', wantedfile) + break + else: + print(f'\tError: File does not exist in the selected path\n') + + elif filetype.strip().lower() in ['pkl', '.pkl']: + wantedfile = input('Introduce name of input data file (as in: Desktop/Folder/data.pkl): ') + if os.path.exists(f'{wantedfile}'): + Tensor = load_data('pkl', wantedfile) + break + else: + print(f'\tError: File does not exist in the selected path\n') + + elif filetype.strip().lower() in ['h5', '.h5']: + wantedfile = input('Introduce name of input data file (as in: Desktop/Folder/data.h5): ') + if os.path.exists(f'{wantedfile}'): + Tensor = load_data('h5', wantedfile) + break + else: + print(f'\tError: File does not exist in the selected path\n') + + database = wantedfile.strip('.mat').strip('.npy').strip('.csv').strip('.pkl').strip('.h5') + + return Tensor, database + diff --git a/v0.1_ModelFLOWs_app/hosvd.py b/v0.1_ModelFLOWs_app/hosvd.py new file mode 100644 index 0000000..c53a1ea --- /dev/null +++ b/v0.1_ModelFLOWs_app/hosvd.py @@ -0,0 +1,105 @@ +import numpy as np +from sklearn.utils.extmath import randomized_svd + +def matlen(var): + '''Equivalent to Matlab's length()''' + if np.size(np.shape(var))==1: + x = np.size(var) + else: + x = max(np.shape(var)) + return x + +def unfold(A,dim): + '''Turns tensor into matrix keeping the columns on dim''' + ax=np.arange(A.ndim) + return np.reshape(np.moveaxis(A,ax,np.roll(ax,dim)),(A.shape[dim],A.size//A.shape[dim])) + + +def fold(B,dim,shape): + '''Reverse operation to the unfold function''' + ax=np.arange(len(shape)) + shape=np.roll(shape,-dim) + A=np.reshape(B,shape) + return np.moveaxis(A,ax,np.roll(ax,-dim)) + +def tprod(S,U): + '''Tensor product of an ndim-array and multiple matrices''' + T = S + shap = list(np.shape(S)) + for i in range(0,np.size(U)): + x = np.count_nonzero(U[0][i]) + if not x==0: + shap[i] = np.shape(U[0][i])[0] + H = unfold(T,i) + T = fold(np.dot(U[0][i],H),i,shap) + return T + +def svdtrunc(A, n): + '''Truncated svd''' + U, S, _ = randomized_svd(A, n_components = n) + return U, S + +def HOSVD_function(T,n): + '''Perform hosvd to tensor''' + P = T.ndim + U = np.zeros(shape=(1,P), dtype=object) + UT = np.zeros(shape=(1,P), dtype=object) + sv = np.zeros(shape=(1,P), dtype=object) + producto = n[0] + + for i in range(1,P): + producto = producto * n[i] + + n = list(n) # to be able to assign values + + for i in range(0,P): + n[i] = int(np.amin((n[i],producto/n[i]))) + A = unfold(T, i) # Modificado de unfold a unfolding + Uaux = [] + # SVD based reduction of the current dimension (i): + Ui, svi = svdtrunc(A, n[i]) + if n[i] < 2: + Uaux = np.zeros((np.shape(Ui)[0],2)) + Uaux[:,0] = Ui[:,0] + U[0][i] = Uaux + else: + U[0][i] = Ui[:,0:n[i]] + # U[i] = Ui + UT[0][i] = np.transpose(U[0][i]) + sv[0][i] = svi + S = tprod(T, UT) + TT = tprod(S, U) + return TT, S, U, sv, n + +def HOSVD(Tensor,varepsilon1,nn,n,TimePos): + '''Perform hosvd to input data and retain all the singular values''' + + if np.iscomplex(Tensor.any()) == False: + Tensor = Tensor.astype(np.float32) + [TT,S,U,sv,n] = HOSVD_function(Tensor,n) # El problema está aquí dentro + ## Set the truncation of the singular values using varepsilon1 + # (automatic truncation) + for i in range(1,np.size(nn)): + count = 0 + for j in range(0,np.shape(sv[0][i])[0]): + if sv[0][i][j]/sv[0][i][0]<=varepsilon1: + pass + else: + count = count+1 + + nn[i] = count + print(f'Initial number of singular values: {n}') + print(f'Number of singular values retained: {nn}') + + ## Perform HOSVD retaining n singular values: reconstruction of the modes + [TT,S2,U,sv2,nn2] = HOSVD_function(Tensor,nn) + + ## Construct the reduced matrix containing the temporal modes: + UT = np.zeros(shape=np.shape(U), dtype=object) + hatT = [] + for pp in range(0,np.size(nn2)): + UT[0][pp] = np.transpose(U[0][pp]) + for kk in range(0,nn2[TimePos-1]): + hatT.append(np.dot(sv2[0][TimePos-1][kk],UT[0][TimePos-1][kk,:])) + hatT = np.reshape(hatT, newshape=(len(hatT),np.size(hatT[0]))) + return hatT,U,S2,sv,nn2,n,TT \ No newline at end of file diff --git a/v0.1_ModelFLOWs_app/mainHOSVD.py b/v0.1_ModelFLOWs_app/mainHOSVD.py new file mode 100644 index 0000000..dd142c6 --- /dev/null +++ b/v0.1_ModelFLOWs_app/mainHOSVD.py @@ -0,0 +1,385 @@ +def HOSVD(): + import hosvd + import os + import numpy as np + import matplotlib.pyplot as plt + import matplotlib.animation as animation + os.environ['KMP_DUPLICATE_LIB_OK']='True' + import data_load + import hdf5storage + import time + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def video(Tensor, vel, Title): + + nt = Tensor.shape[-1] + + if nt in range(200, 500): + Tensor[..., ::5] + + elif nt > 500: + Tensor[..., ::15] + + frames = Tensor.shape[-1] + + fig, ax = plt.subplots(figsize = (8, 4), num = f'CLOSE TO CONTINUE RUN - {Title}') + fig.tight_layout() + + def animate(i): + ax.clear() + ax.contourf(Tensor[vel, ..., i]) + ax.set_title(Title) + + interval = 2 + anim = animation.FuncAnimation(fig, animate, frames = frames, interval = interval*1e+2, blit = False) + + plt.show() + + print('\nHOSVD Algorithm') + print('\n-----------------------------') + + + ################### Input ################### + #------------------------------------------------------------------------------------------------------------ + print('Inputs:' + '\n') + + # Detect current working directory: + path0 = os.getcwd() + + while True: + filetype = input('Select the input file format (.mat, .npy, .csv, .pkl, .h5): ') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + Tensor, _ = data_load.main(filetype) + + + ## Number of snapshots SNAP: + while True: + SNAP = input(f'Introduce number of snapshots. Continue with {Tensor.shape[-1]} snapshots: ') + if not SNAP: + SNAP = int(Tensor.shape[-1]) + break + elif SNAP.isdigit(): + SNAP = int(SNAP) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + ## Tolerances: + while True: + varepsilon1 = input('Introduce SVD tolerance. Continue with 1e-3: ') + if not varepsilon1: + varepsilon1 = 1e-3 + break # SVD + elif is_float(varepsilon1): + varepsilon1 = float(varepsilon1) + break + else: + print('\tError: Please introduce a number\n') + + #------------------------------------------------------------------------------------------------------ + ################### Output ################### + + print('\n-----------------------------') + print('HOSVD summary:') + print('\n' + f'Number of snapshots set at: {SNAP}') + print(f'Tolerance set at {varepsilon1} for SVD') + + print('\n-----------------------------') + print('Outputs:' + '\n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_HOSVD_solution_tol_{varepsilon1}' + else: + filen = f'{filen}' + + while True: + decision_2 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not decision_2 or decision_2.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + + print('') + + TimePos = Tensor.ndim + + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f"{path0}/{filen}") + + Tensor0 = Tensor.copy() + shapeTens = list(np.shape(Tensor)) + shapeTens[-1] = SNAP + Tensor = np.zeros(shapeTens) + + Tensor[..., :] = Tensor0[..., 0:SNAP] + + nn0 = np.array(Tensor.shape) + nn = np.array(nn0) + nn[1:np.size(nn)] = 0 + + print('Performing HOSVD. Please wait...\n') + hatT, U, S, sv, nn1, n, TT = hosvd.HOSVD(Tensor, varepsilon1, nn, nn0, TimePos) + print('\nHOSVD complete!\n') + + RRMSE = np.linalg.norm(np.reshape(Tensor-TT,newshape=(np.size(Tensor),1)),ord=2)/np.linalg.norm(np.reshape(Tensor,newshape=(np.size(Tensor),1))) + print(f'Relative mean square error made in the calculations: {np.round(RRMSE*100, 3)}%\n') + + print(f'\nATTENTION!: All plots will be saved to {path0}/{filen}\n') + print('Please CLOSE all figures to continue the run\n') + + markers = ['yo', 'gx', 'r*', 'bv', 'y+'] + labels = ["Variable Singular Values", + "X Space Singular Values", + "Y Space Singular Values", + "Z Space Singular Values", + "Time Singular Values"] + + fig, ax = plt.subplots(num = 'CLOSE TO CONTINUE RUN - Total Modes vs Retained Singular Values') + sub_axes = plt.axes([.6, .3, .25, .2]) + + if np.array(n).size == 4: + labels.remove("Z Space Singular Values") + + if np.array(n).size == 3: + labels.remove("Variable Singular Values") + labels.remove("Z Space Singular Values") + + for i in range(np.array(n).size): + ax.plot(sv[0, i] / sv[0, i][0], markers[i]) + sub_axes.plot(sv[0, i][:nn1[i]] / sv[0, i][0], markers[i]) + + ax.hlines(y=varepsilon1, xmin = 0, xmax=np.array(n).max(), linewidth=2, color='black', label = f'SVD tolerance: {varepsilon1}') + + ax.set_yscale('log') # Logarithmic scale in y axis + sub_axes.set_yscale('log') + ax.set_xlabel('SVD modes') + ax.set_ylabel('Singular values') + + ax.legend(labels, loc='best') + ax.set_title('SVD Modes vs Singular values') + sub_axes.set_title('Retained singular values', fontsize = 8) + plt.savefig(f'{path0}/{filen}/SingularValues.png', bbox_inches='tight') + plt.show() + plt.close() + + U = U[0].copy() + + print('\nCalculating SVD modes. Please wait...') + if TimePos == 3: + svd_modes = np.einsum('ijl, ai, bj -> abl', S, U[0], U[1]) + if TimePos == 4: + svd_modes = np.einsum('ijkl, ai, bj, ck -> abcl', S, U[0], U[1], U[2]) + if TimePos == 5: + svd_modes = np.einsum('ijkml, ai, bj, ck, dm -> abcdl', S, U[0], U[1], U[2], U[3]) + print('SVD modes calculation complete!\n') + + if not os.path.exists(f'{path0}/{filen}/svd_modes'): + os.mkdir(f'{path0}/{filen}/svd_modes') + + print(f'Saving first 3 SVD mode plots to {path0}/{filen}/svd_modes') + + if TimePos==3: + for ModeNum in range(3): + fig, ax = plt.subplots(num=f'CLOSE TO CONTINUE RUN - SVD mode') + ax.contourf(svd_modes[:,:,ModeNum]) + ax.set_title(f'SVD modes - Mode Number {ModeNum+1}') + ax.set_xlabel('X') + ax.set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/svd_modes/ModeNum_{ModeNum+1}.png') + plt.show() + + if TimePos > 3: + for ModComp in range(svd_modes.shape[0]): + for ModeNum in range(3): + if TimePos==4: + fig, ax = plt.subplots(num=f'CLOSE TO CONTINUE RUN - SVD mode') + ax.contourf(svd_modes[ModComp,:,:,ModeNum]) + ax.set_title(f'SVD modes - Component {ModComp+1} Mode Number {ModeNum+1}') + ax.set_xlabel('X') + ax.set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/svd_modes/SVDmodeComp_{ModComp+1}_ModeNum_{ModeNum+1}.png') + + + elif TimePos==5: + nz = int(Tensor.shape[3] / 2) + fig, ax = plt.subplots(num=f'CLOSE TO CONTINUE RUN - SVD mode XY plane') + ax.contourf(svd_modes[ModComp,:,:,nz,ModeNum]) + ax.set_title(f'SVD modes XY plane - Component {ModComp+1} Mode Number {ModeNum+1}') + ax.set_xlabel('X') + ax.set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/svd_modes/XY_SVDmodeComp_{ModComp+1}_ModeNum_{ModeNum+1}.png') + + + while True: + while True: + ModeNum = input(f'Introduce the mode number to plot (default mode 1). Maximum number of modes is {svd_modes.shape[-1]}: ') + if not ModeNum: + ModeNum = 0 + break + elif ModeNum.isdigit(): + if int(ModeNum) <= svd_modes.shape[-1]: + ModeNum = int(ModeNum)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + while True: + ModComp = input(f'Introduce the component to plot (default component 1). Maximum number of components is {svd_modes.shape[0]}: ') + if not ModComp: + ModComp = 0 + break + elif ModComp.isdigit(): + if int(ModComp) <= svd_modes.shape[0]: + ModComp = int(ModComp)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + if TimePos==4: + fig, ax = plt.subplots(num=f'CLOSE TO CONTINUE RUN - SVD mode') + ax.contourf(svd_modes[ModComp,:,:,ModeNum]) + ax.set_title(f'SVD modes - Component {ModComp+1} Mode Number {ModeNum+1}') + ax.set_xlabel('X') + ax.set_ylabel('Y') + plt.show() + + elif TimePos==5: + nz = int(Tensor.shape[3] / 2) + fig, ax = plt.subplots(num=f'CLOSE TO CONTINUE RUN - SVD mode XY plane') + ax.contourf(svd_modes[ModComp,:,:,nz,ModeNum]) + ax.set_title(f'SVD modes XY plane - Component {ModComp+1} Mode Number {ModeNum+1}') + ax.set_xlabel('X') + ax.set_ylabel('Y') + plt.show() + + while True: + Resp = input('Do you want to plot another mode? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + if Resp == 1: + continue + + if not decision_2 or decision_2.strip().lower() in ['npy', '.npy']: + np.save(f'{path0}/{filen}/Reconstruction.npy', TT) + np.save(f'{path0}/{filen}/hatT.npy', hatT) + np.save(f'{path0}/{filen}/svd_modes.npy', svd_modes) + + if decision_2.strip().lower() in ['.mat', 'mat']: + mdic0 = {"Reconst": TT} + mdic1 = {"hatT": hatT} + mdic2 = {"svd_modes": svd_modes} + + file_mat0 = str(f'{path0}/{filen}/Reconstruction.mat') + file_mat1 = str(f'{path0}/{filen}/hatT.mat') + file_mat2 = str(f'{path0}/{filen}/svd_modes.mat') + + hdf5storage.savemat(file_mat0, mdic0, appendmat=True, format='7.3') + hdf5storage.savemat(file_mat1, mdic1, appendmat=True, format='7.3') + hdf5storage.savemat(file_mat2, mdic2, appendmat=True, format='7.3') + + if TimePos <= 3: + return + + elif TimePos > 3: + while True: + dec4 = input(f'Plot video of original data and reconstructed data? (y/n). Continue with Yes: ') + if not dec4 or dec4.strip().lower() in ['y', 'yes']: + decision4 = True + break + if dec4.strip().lower() in ['n', 'no']: + decision4 = False + return + else: + print('\tError: Please select yes or no (y/n)\n') + + if TimePos == 5: + nz = int(Tensor.shape[3] / 2) + while True: + plane = input('Select a plane (XY, XZ, YZ)') + if plane.strip().lower() == 'xy': + Tensor = Tensor[:, :, :, nz, :] + TT = TT[:, :, :, nz, :] + break + elif plane.strip().lower() == 'xz': + Tensor = Tensor[:, :, 0, :, :] + TT = TT[:, :, 0, :, :] + break + elif plane.strip().lower() == 'yz': + Tensor = Tensor[:, 0, :, :, :] + TT = TT[:, 0, :, :, :] + break + else: + print('\tError: Select a valid plane\n') + + else: + pass + + titles = [] + [titles.append(f'Component {i+1}') for i in range(Tensor.shape[0])] + + while True: + if decision4 == True: + vidvel = input(f'Select a component (max is {Tensor.shape[0]}). Continue with component 1: ') + if not vidvel: + vel = 0 + video(Tensor, vel, Title = f'Original Data - {titles[vel]}') + video(TT, vel, Title = f'Reconstructed data - {titles[vel]}') + break + elif vidvel.isdigit(): + if int(vidvel) <= Tensor.shape[0]: + vel = int(vidvel) - 1 + video(Tensor, vel, Title = f'Original Data - {titles[vel]}') + video(TT, vel, Title = f'Reconstructed data - {titles[vel]}') + break + else: + print("\tError: Select a valid component\n") + else: + print('\tError: Introduce a valid format (must be integer)\n') + + while True: + ch1 = input('Would you like to plot another component? (y/n). Continue with No: ') + if ch1.strip().lower() in ['y', 'yes']: + while True: + vidvel = input(f'Select a component (max is {Tensor.shape[0]}): ') + if vidvel.isdigit(): + if int(vidvel) <= Tensor.shape[0]: + vel = int(vidvel) - 1 + video(Tensor, vel, Title = f'Original Data - {titles[vel]}') + video(TT, vel, Title = f'Reconstructed data - {titles[vel]}') + break + else: + print("\tError: Select a valid component\n") + else: + print('\tError: Introduce a valid format (must be integer)\n') + continue + elif not ch1 or ch1.strip().lower() in ['n', 'no']: + return + + else: + print('\tError: Select yes or no (y/n)\n') + diff --git a/v0.1_ModelFLOWs_app/mdHODMD.py b/v0.1_ModelFLOWs_app/mdHODMD.py new file mode 100644 index 0000000..df7e631 --- /dev/null +++ b/v0.1_ModelFLOWs_app/mdHODMD.py @@ -0,0 +1,865 @@ +def mdHODMD(): + import DMDd + import hosvd + import warnings + warnings.filterwarnings("ignore", message="Casting complex values to real discards the imaginary part") + + import numpy as np + import matplotlib.pyplot as plt + import matplotlib.animation as animation + + import os + os.environ['KMP_DUPLICATE_LIB_OK']='True' + + import data_load + import hdf5storage + import time + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def video(Tensor, vel, Title): + + nt = Tensor.shape[-1] + + if nt in range(200, 500): + Tensor[..., ::5] + + elif nt > 500: + Tensor[..., ::15] + + frames = Tensor.shape[-1] + + fig, ax = plt.subplots(figsize = (8, 4), num = f'CLOSE TO CONTINUE RUN - {Title}') + fig.tight_layout() + + def animate(i): + ax.clear() + ax.contourf(Tensor[vel, :, :, i]) + ax.set_title(Title) + + interval = 2 + anim = animation.FuncAnimation(fig, animate, frames = frames, interval = interval*1e+2, blit = False) + + plt.show() + + print('\nMulti-dimensional HODMD' + '\n') + print('-----------------------------') + + print(''' +The Multi-dimensional HODMD algorithm has the following options: +1) Non-iterative Multi-dimensional HODMD +2) Iterative Multi-dimensional HODMD + ''') + + while True: + typeHODMD = input('Select a type of Multi-dimensional HODMD (1/2): ') + if typeHODMD.isdigit(): + if int(typeHODMD) == 1: + print('\n\nNon-iterative Multi-dimensional HODMD') + print('\n-----------------------------') + n_iter = 1 + type = 'non-iter' + break + elif int(typeHODMD) == 2: + print('\n\nIterative Multi-dimensional HODMD') + print('\n-----------------------------') + n_iter = 1000 + type = 'iter' + break + else: + print('\tError: Select a valid option\n') + else: + print('\tError: Invalid value (must be integer)\n') + # Load data + path0 = os.getcwd() + print('Inputs:' + '\n') + + while True: + filetype = input('Select the input file format (.mat, .npy, .csv, .pkl, .h5): ') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + Tensor, _ = data_load.main(filetype) + + ##Number of snapshots SNAP: + while True: + SNAP = input(f'Introduce number of snapshots. Continue with {Tensor.shape[-1]} snapshots: ') + if not SNAP: + SNAP = int(Tensor.shape[-1]) + break + elif SNAP.isdigit(): + SNAP = int(SNAP) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + ## d Parameter: + print('''\nHODMD parameter (d) options: + Option 1: Select only 1 value for d. + Option 2: Select more than one value for d. + ''') + + d_list = [] + print(f'Interval of recommended number of HODMD windows (d): [{int(np.round(SNAP/10))}, {int(np.round(SNAP/2))}]. Other values are accepted') + + while True: + d_dec = input('Select an option (1/2). Continue with option 1: ') + if not d_dec or d_dec == '1': + d = input(f'Introduce number of HODMD windows (d): ') + if d.isdigit(): + d = int(d) + d_list.append(d) + break + else: + print('\tError: Select a valid number (must be integer)\n') + elif d_dec == '2': + break + else: + print('\tError: Select a valid option\n') + + if d_dec == '2': + while True: + d_list_len = input('Select the number of values for d. Continue with 2: ') + if d_list_len.isdigit(): + d_list_len = int(d_list_len) + for d in range(d_list_len): + d_val = input(f'Introduce value number {d+1} for d: ') + if d_val.isdigit(): + d_list.append(int(d_val)) + else: + print('\tError: Invalid value (must be integer)\n') + + if all(isinstance(x, int) for x in d_list): + print(f'Selected values: {d_list}\n') + break + else: + print('\tError: One or more selected values are invalid\n') + else: + print('\tError: Select a valid number (must be integer)\n') + + # SVD Tolerance + + print('''\nSVD tolerance options: + Option 1: Select only 1 value for SVD tolerance. + Option 2: Select more than one SVD tolerance value. + ''') + + tol1_list = [] + + while True: + tol_dec = input('Select an option (1/2). Continue with option 1: ') + if not tol_dec or tol_dec == '1': + varepsilon1 = input('Introduce SVD tolerance. Continue with 1e-3: ') + if not varepsilon1: + varepsilon1 = 1e-3 + tol1_list.append(varepsilon1) + break + elif is_float(varepsilon1): + varepsilon1 = float(varepsilon1) + tol1_list.append(varepsilon1) + break + else: + print('\tError:Please introduce a number\n') + elif tol_dec == '2': + break + else: + print('\tError: Select a valid option\n') + + ## Tolerance list + if tol_dec == '2': + while True: + tol1_list_len = input('Select the number of values for SVD tolerance. Continue with 2: ') + if tol1_list_len.isdigit(): + tol1_list = [] + tol1_list_len = int(tol1_list_len) + for tol in range(tol1_list_len): + tol_val = input(f'Introduce SVD tolerance number {tol+1}: ') + if is_float(tol_val): + tol1_list.append(float(tol_val)) + else: + print('\tError: Invalid value\n') + + if all(isinstance(x, float) for x in tol1_list): + print(f'Selected values: {tol1_list}\n') + break + else: + print('\tError: One or more selected values are invalid\n') + else: + print('\tError: Select a valid number (must be integer)\n') + + tol2_list = [] + + if len(tol1_list) == 1: + while True: + vardec = input(f'HODMD tolerance value equal to SVD tolerance ({tol1_list[0]}) (y/n). Continue with Yes: ') + if not vardec or vardec.strip().lower() in ['y', 'yes']: + tol2_list = tol1_list + break + elif vardec.strip().lower() in ['n', 'no']: + varepsilon2 = input('Introduce second tolerance (HODMD). Continue with 1e-3: ') + if not varepsilon2: + varepsilon2 = 1e-3 + tol2_list.append(varepsilon2) + break + elif is_float(varepsilon2): + varepsilon2 = float(varepsilon2) + tol2_list.append(varepsilon2) + break + else: + print('\tError: Please introduce a number\n') + else: + print('\tError: Select yes or no (y/n)') + + if len(tol1_list) > 1: + tol2_list = tol1_list + + + ##Time: + while True: + deltaT = input('Introduce time step (deltaT). Continue with 1: ') + if not deltaT: + deltaT = 1 + break + elif is_float(deltaT): + deltaT = float(deltaT) + break + else: + print('\tError: Please introduce a number\n') + + + Time = np.linspace(0,SNAP-1,num=SNAP)*deltaT + + ## Position of temporal variable: + TimePos = Tensor.ndim + + #-------------------------------------------------------------------------------------------------------------------------------------- + ################### Output ################### + + print('\n-----------------------------') + print('Iterative HODMD summary:') + print('\n' + f'Number of snapshots set at: {SNAP}') + print(f'd Parameter(s) set at: {d_list}') + print(f'SVD tolerance(s) {tol1_list}') + print(f'HODMD tolerance(s): {tol2_list}') + print(f'Time gradient set at deltaT: {deltaT}') + + print('\n-----------------------------') + print('Outputs:\n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_{type}_mdHODMD_solution' + else: + filen = f'{filen}' + + while True: + decision_2 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not decision_2 or decision_2.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + + print('') + + ## Save results in folder: + + Tensor0 = Tensor + shapeTens = list(np.shape(Tensor)) + shapeTens[-1] = SNAP + Tensor = np.zeros(shapeTens) + + Tensor[..., :] = Tensor0[..., 0:SNAP] + TensorR = Tensor.copy() + + ## ALGORITHM: + + ## ITERATIVE: + nn0 = np.shape(Tensor) + nn = np.array(nn0) + nn[1:np.size(nn)] = 0 + + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f"{path0}/{filen}") + + Frequencies = [] + Amplitudes = [] + GrowthRates = [] + d_val = [] + tol_val = [] + DMDmode_list = [] + Tensor_rec_list = [] + + for d in d_list: + for varepsilon1 in tol1_list: + if len(tol1_list) > 1: + varepsilon2 = varepsilon1 + elif not vardec or vardec.strip().lower() in ['y', 'yes']: + varepsilon2 = varepsilon1 + if not os.path.exists(f'{path0}/{filen}/{d}_tol_{varepsilon1}'): + os.mkdir(f"{path0}/{filen}/d_{d}_tol_{varepsilon1}") + if not os.path.exists(f'{path0}/{filen}/{d}_tol_{varepsilon1}/DMDmodes'): + os.mkdir(f"{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes") + print(f'\nRunning HODMD for d = {d} and tol = {varepsilon1}') + for zz in range(0,n_iter): + if n_iter > 1: + print(f'Iteration number: {zz+1}') + if zz != 0: + del S,U,Frequency,Amplitude,GrowthRate,hatT,hatMode,sv,TensorR,nnin + TensorR = TensorReconst + + ## Perform HOSVD decomposition to calculate the reduced temporal matrix: hatT + nnin = nn + nnin = tuple(nnin) + print('\nPerforming HOSVD. Please wait...\n') + [hatT,U,S,sv,nn1,n,TT] = hosvd.HOSVD(TensorR,varepsilon1,nn,nn0,TimePos) + print('\nHOSVD complete!\n') + # hatT: Reduced temporal matrix + + ## Perform HODMD to the reduced temporal matrix hatT: + print('Performing HODMD. Please wait...\n') + [hatMode,Amplitude,Eigval,GrowthRate,Frequency] = DMDd.hodmd_IT(hatT,d,Time,varepsilon1,varepsilon2) + print('\nHODMD complete!\n') + + ## Reconstruct the original Tensor using the DMD expansion: + TensorReconst = DMDd.reconst_IT(hatMode,Time,U,S,sv,nn1,TimePos,GrowthRate,Frequency) + + RRMSE = np.linalg.norm(np.reshape(Tensor-TensorReconst,newshape=(np.size(Tensor),1)),ord=2)/np.linalg.norm(np.reshape(Tensor,newshape=(np.size(Tensor),1))) + print(f'Relative mean square error made in the calculations: {np.round(RRMSE*100, 3)}%\n') + + GRFreqAmp = np.zeros((np.size(GrowthRate),3)) + for ind in range (0,np.size(GrowthRate)): + GRFreqAmp[ind,0] = GrowthRate[ind] + GRFreqAmp[ind,1] = Frequency[ind] + GRFreqAmp[ind,2] = Amplitude[ind] + + print('GrowthRate, Frequency and Amplitude:') + print(GRFreqAmp) + + ## Break the loop when the number of singular values is the same in two consecutive iterations: + + num = 0 + for ii in range(1,np.size(nn1)): + if nnin[ii]==nn1[ii]: + num = num+1 + + if num==np.size(nn1)-1: + break + nn = nn1 + print('\n') + + if n_iter > 1: + print(f'Final number of iterations for d = {d} and tol = {varepsilon1}: {zz+1}') + print(f'\nRelative mean square error made in the calculations: {np.round(RRMSE*100, 3)}%\n') + + Frequencies.append(Frequency) + Amplitudes.append(Amplitude) + GrowthRates.append(GrowthRate) + d_val.append(d) + tol_val.append(varepsilon1) + + + if not decision_2 or decision_2.strip().lower() in ['npy', '.npy']: + np.save(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/GRFreqAmp.npy', GRFreqAmp) + + if decision_2.strip().lower() in ['.mat', 'mat']: + mdic = {"GRFreqAmp": GRFreqAmp} + file_mat = str(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/GRFreqAmp.mat') + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + # Tensor reconstruction + TensorReconst = DMDd.reconst_IT(hatMode,Time,U,S,sv,nn1,TimePos,GrowthRate,Frequency) + Tensor_rec_list.append(TensorReconst) + + ## Save the reconstruction of the tensor and the Growth rates, frequencies and amplitudes: + + # Reconstruction: + if not decision_2 or decision_2.strip().lower() in ['npy', '.npy']: + np.save(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/TensorReconst.npy', TensorReconst) + + if decision_2.strip().lower() in ['.mat', 'mat']: + mdic = {"TensorReconst": TensorReconst} + file_mat = str(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/TensorReconst.mat') + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + ## Calculate DMD modes: + print('Calculating DMD modes...') + N = np.shape(hatT)[0] + DMDmode = DMDd.modes_IT(N,hatMode,Amplitude,U,S,nn1,TimePos) + DMDmode_list.append(DMDmode) + + # Save DMD modes: + if not decision_2 or decision_2.strip().lower() in ['npy', '.npy']: + np.save(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmode.npy',DMDmode) + + if decision_2.strip().lower() in ['.mat', 'mat']: + mdic = {"DMDmode": DMDmode} + file_mat = str(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmode.mat') + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + print(f'\nSaving first 3 DMDmode plots to {path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes\n') + + if TimePos == 3: + for ModeNum in range(3): + fig, ax = plt.subplots(1, 2) + fig.suptitle(f'DMDmode - Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[:,:,ModeNum].real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode[:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/ModeNum_{ModeNum+1}.png') + plt.close(fig) + + for ModComp in range(DMDmode.shape[0]): + for ModeNum in range(3): + if TimePos==4: + fig, ax = plt.subplots(1, 2) + fig.suptitle(f'DMDmode - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[ModComp,:,:,ModeNum].real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode[ModComp,:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmodeComp_{ModComp+1}_ModeNum_{ModeNum+1}.png') + plt.close(fig) + + elif TimePos==5: + nz = int(Tensor.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode XY plane') + fig.suptitle(f'DMDmode XY plane - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode[ModComp,:,:,nz,ModeNum].real) + ax[0].set_title('Real part - XY Plane') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode[ModComp,:,:,nz,ModeNum].imag) + ax[1].set_title('Imaginary part - XY Plane') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes/DMDmode_XY_Comp_{ModComp+1}_ModeNum_{ModeNum+1}.png') + plt.close(fig) + else: + pass + + # Frequency vs amplitudes comparison: + colored_markers = ['bo', 'kx', 'ro', 'cx', 'mo', 'yx', 'k^', 'bs', 'gs', 'rs', 'cs', 'ms', 'ys', 'ks', 'b^', 'g^', 'r^', 'c^', 'm^', 'y^'] + + total_cases = len(d_list) * len(tol1_list) + + if total_cases > 1: + + plt.figure(num='CLOSE TO CONTINUE RUN - Frequency/Amplitude for all selected cases') + for i in range(total_cases): + plt.plot(Frequencies[i], Amplitudes[i], colored_markers[i], label = f'd = {d_val[i]} | tol = {tol_val[i]}', alpha = .5) + plt.yscale('log') + plt.xlabel('Frequency ($\omega_{m}$)') + plt.ylabel('Amplitude ($a_{m}$)') + plt.title(f'Frequency vs Amplitude for all selected cases') + plt.tight_layout() + plt.legend(loc='upper right') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/FrequencyAmplitude.png') + plt.show() + plt.close() + + print(f''' +Run cases - Summary +----------------------------- + ''') + + i = 0 + for d in d_list: + for tol in tol1_list: + print(f'case {i+1}: d = {d} and tol = {tol}') + i += 1 + + + print('\n-----------------------------\n') + + print(f'\nATTENTION!: All plots will be saved to {path0}/{filen}\n') + print('Please CLOSE all figures to continue the run\n') + + print('\nPlotting Frequency/GrowthRate and Frequency/Amplitude plots') + while True: + while True: + case = input(f'Select the case to plot (default case 1): ') + if not case: + case = 0 + break + elif case.isdigit(): + if int(case) <= total_cases: + case = int(case)-1 + break + else: + print('\tError: Selected case does not exist\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + # Frequency vs absolute value of GrowthRate: + plt.figure(num=f'CLOSE TO CONTINUE RUN - Case {case + 1} - Frequency/GrowthRate') + plt.plot(Frequencies[case],np.abs(GrowthRates[case]), 'k+') + plt.yscale('log') # Logarithmic scale in y axis + plt.xlabel('Frequency ($\omega_{m}$)') + plt.ylabel('Absolute value of GrowthRate (|$\delta_{m}$|)') + plt.title(f'd = {d_val[case]} tol = {tol_val[case]}') + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d_val[case]}_tol_{tol_val[case]}/FrequencyGrowthRate.png') + plt.show() + plt.close() + + # Frequency vs amplitudes: + plt.figure(num=f'CLOSE TO CONTINUE RUN - Case {case + 1} - Frequency/Amplitude') + plt.plot(Frequencies[case], Amplitudes[case],'r+') + plt.yscale('log') # Logarithmic scale in y axis + plt.xlabel('Frequency ($\omega_{m}$)') + plt.ylabel('Amplitude ($a_{m}$)') + plt.title(f'd = {d_val[case]} tol = {tol_val[case]}') + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d_val[case]}_tol_{tol_val[case]}/FrequencyAmplitude.png') + plt.show() + plt.close() + + while True: + Resp = input('Do you want to plot another case? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + print('\nPlotting component comparison') + dims = Tensor.ndim + while True: + while True: + case = input(f'Select the case to plot (default case 1): ') + if not case: + case = 0 + break + elif case.isdigit(): + if int(case) <= total_cases: + case = int(case)-1 + break + else: + print('\tError: Selected case does not exist\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + if dims > 3: + while True: + c = input(f'Select a component (max is {Tensor.shape[0]}): ') + if c.isdigit(): + if int(c) <= Tensor.shape[0]: + c = int(c) - 1 + break + else: + print("\tError: Selected component doesn't exist\n") + elif not c: + continue + + while True: + x = input(f'Select X coordinate (must be in range [0, {Tensor.shape[2] - 1}]): ') + if x.isdigit(): + if int(x) in range(0, Tensor.shape[2]): + x = int(x) + break + else: + print(Tensor.shape[2]) + print('\tError: Selected value is out of bounds\n') + elif not x: + continue + + while True: + y = input(f'Select Y coordinate (must be in range [0, {Tensor.shape[1] - 1}]): ') + if y.isdigit(): + if int(y) in range(0, Tensor.shape[1]): + y = int(y) + break + else: + print('\tError: Selected value is out of bounds\n') + elif not y: + continue + else: + print('\tError: Select a valid number format (must be integer)\n') + + if dims == 3: + fig, ax = plt.subplots(1, 2, num = f'CLOSE TO CONTINUE RUN - Case {case + 1} - component comparison') + fig.suptitle(f'Real Data vs Reconstruction - Component comparison') + ax[0].contourf(Tensor0[:, :, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(Time[:], Tensor_rec_list[case][y, x, :].real, 'k-x', label = 'Reconstructed Data') + ax[1].plot(Time[:], Tensor[y, x, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d_val[case]}_tol_{tol_val[case]}/OrigReconst.png') + plt.show() + plt.close() + + if dims == 4: + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - component comparison') + fig.suptitle(f'Real Data vs Reconstruction - Case {case + 1} - Component comparison') + ax[0].contourf(Tensor0[c, :, :, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(Time[:], Tensor_rec_list[case][c, y, x, :].real, 'k-x', label = 'Reconstructed Data') + ax[1].plot(Time[:], Tensor[c, y, x, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d_val[case]}_tol_{tol_val[case]}/OrigReconst.png') + plt.show() + plt.close() + + + elif dims == 5: + nz = int(Tensor.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - component comparison') + fig.suptitle(f'Real Data vs Reconstruction - Case {case + 1} - Component comparison XY plane') + ax[0].contourf(Tensor0[c, :, :, nz, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(Time[:], Tensor_rec_list[case][c, y, x, nz, :].real, 'k-x', label = 'Reconstructed Data') + ax[1].plot(Time[:], Tensor[c, y, x, nz, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d_val[case]}_tol_{tol_val[case]}/OrigReconst.png') + plt.show() + plt.close() + + while True: + Resp = input('Do you want to plot another case? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + print(f'Select a case, component and temporal mode to plot a DMD mode') + while True: + while True: + case = input(f'Select the case to plot (default case 1): ') + if not case: + case = 0 + break + elif case.isdigit(): + if int(case) <= total_cases: + case = int(case)-1 + break + else: + print('\tError: Selected case does not exist\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + while True: + ModeNum = input(f'Introduce the mode number to plot (default mode 1). Maximum number of modes is {DMDmode_list[case].shape[-1]}: ') + if not ModeNum: + ModeNum = 0 + break + elif ModeNum.isdigit(): + if int(ModeNum) <=DMDmode_list[case].shape[-1]: + ModeNum = int(ModeNum)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + if TimePos > 3: + while True: + ModComp = input(f'Introduce the component to plot (default component 1). Maximum number of components is {DMDmode_list[case].shape[0]}: ') + if not ModComp: + ModComp = 0 + break + elif ModComp.isdigit(): + if int(ModComp) <= DMDmode_list[case].shape[0]: + ModComp = int(ModComp)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + if TimePos==3: + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode') + fig.suptitle(f'Case {case + 1} - DMDmode - Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode_list[case][:,:,ModeNum].real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode_list[case][:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.show() + + + if TimePos==4: + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode') + fig.suptitle(f'Case {case + 1} - DMDmode - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode_list[case][ModComp,:,:,ModeNum].real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode_list[case][ModComp,:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.show() + + elif TimePos==5: + nz = int(Tensor.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode XY plane') + fig.suptitle(f'Case {case + 1} - DMDmode XY plane - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode_list[case][ModComp,:,:,nz,ModeNum].real) + ax[0].set_title('Real part - XY Plane') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode_list[case][ModComp,:,:,nz,ModeNum].imag) + ax[1].set_title('Imaginary part - XY Plane') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.show() + + while True: + Resp = input('Do you want to plot another mode? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + if Resp == 1: + continue + + if TimePos <= 3: + return + + elif TimePos > 3: + while True: + dec4 = input(f'Plot video of original data and reconstructed data? (y/n). Continue with Yes: ') + if not dec4 or dec4.strip().lower() in ['y', 'yes']: + decision4 = True + break + if dec4.strip().lower() in ['n', 'no']: + decision4 = False + return + else: + print('\tError: Please select yes or no (y/n)\n') + + if total_cases > 1: + while True: + case = input(f'Select the case to plot (default case 1): ') + if not case: + case = 0 + break + elif case.isdigit(): + if int(case) <= total_cases: + case = int(case)-1 + break + else: + print('\tError: Selected case does not exist\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + else: + case = 0 + + if dims == 5: + nz = int(Tensor.shape[3] / 2) + while True: + plane = input('Select a plane (XY, XZ, YZ)') + if plane.strip().lower() == 'xy': + Tensor = Tensor[:, :, :, nz, :] + Tensor_rec_list[case] = Tensor_rec_list[case][:, :, :, nz, :] + break + elif plane.strip().lower() == 'xz': + Tensor = Tensor[:, :, 0, :, :] + Tensor_rec_list[case] = Tensor_rec_list[case][:, :, 0, :, :] + break + elif plane.strip().lower() == 'yz': + Tensor = Tensor[:, 0, :, :, :] + Tensor_rec_list[case] = Tensor_rec_list[case][:, 0, :, :, :] + break + else: + print('\tError: Select a valid plane\n') + + else: + pass + + titles = [] + [titles.append(f'Component {i+1}') for i in range(Tensor.shape[0])] + + while True: + if decision4 == True: + vidvel = input(f'Select a component (max is {Tensor.shape[0]}). Continue with component 1: ') + if not vidvel: + vel = 0 + video(Tensor, vel, Title = f'Original Data - {titles[vel]}') + video(Tensor_rec_list[case], vel, Title = f'Reconstructed data - {titles[vel]}') + break + elif vidvel.isdigit(): + if int(vidvel) <= Tensor.shape[0]: + vel = int(vidvel) - 1 + video(Tensor, vel, Title = f'Original Data - {titles[vel]}') + video(Tensor_rec_list[case], vel, Title = f'Reconstructed data - {titles[vel]}') + break + else: + print("\tError: Select a valid component\n") + else: + print('\tError: Introduce a valid format (must be integer)\n') + + while True: + ch1 = input('Would you like to plot another component? (y/n). Continue with No: ') + if ch1.strip().lower() in ['y', 'yes']: + while True: + vidvel = input(f'Select a component (max is {Tensor.shape[0]}): ') + if vidvel.isdigit(): + if int(vidvel) <= Tensor.shape[0]: + vel = int(vidvel) - 1 + video(Tensor, vel, Title = f'Original Data - {titles[vel]}') + video(Tensor_rec_list[case], vel, Title = f'Reconstructed data - {titles[vel]}') + break + else: + print("\tError: Select a valid component\n") + else: + print('\tError: Introduce a valid format (must be integer)\n') + continue + elif not ch1 or ch1.strip().lower() in ['n', 'no']: + return + + else: + print('\tError: Select yes or no (y/n)\n') + diff --git a/v0.1_ModelFLOWs_app/mdHODMD_pred.py b/v0.1_ModelFLOWs_app/mdHODMD_pred.py new file mode 100644 index 0000000..212d151 --- /dev/null +++ b/v0.1_ModelFLOWs_app/mdHODMD_pred.py @@ -0,0 +1,925 @@ +def mdHODMDpred(): + import DMDd + import hosvd + + import warnings + warnings.filterwarnings("ignore", message="Casting complex values to real discards the imaginary part") + + import numpy as np + import matplotlib.pyplot as plt + import os + from math import floor + os.environ['KMP_DUPLICATE_LIB_OK']='True' + import matplotlib.animation as animation + import data_load + import hdf5storage + import time + timestr = time.strftime("%Y-%m-%d_%H.%M.%S") + + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def video(Tensor, vel, Title): + + nt = Tensor.shape[-1] + + if nt in range(200, 500): + Tensor[..., ::5] + + elif nt > 500: + Tensor[..., ::15] + + frames = Tensor.shape[-1] + + fig, ax = plt.subplots(figsize = (8, 4), num = f'CLOSE TO CONTINUE RUN - {Title}') + fig.tight_layout() + + def animate(i): + ax.clear() + ax.contourf(Tensor[vel, :, :, i]) + ax.set_title(Title) + + interval = 2 + anim = animation.FuncAnimation(fig, animate, frames = frames, interval = interval*1e+2, blit = False) + + plt.show() + + + print('\nMulti-dimensional predictive HODMD' + '\n') + print('-----------------------------') + + print(''' +The Multi-dimensional predictive HODMD algorithm has the following options: +1) Non-iterative Multi-dimensional predictive HODMD +2) Iterative Multi-dimensional predictive HODMD + ''') + + while True: + typeHODMD = input('Select a type of Multi-dimensional predictive HODMD (1/2): ') + if typeHODMD.isdigit(): + if int(typeHODMD) == 1: + print('\n\nNon-iterative Multi-dimensional predictive HODMD') + print('\n-----------------------------') + n_iter = 1 + type = 'non-iter' + break + elif int(typeHODMD) == 2: + print('\n\nIterative Multi-dimensional predictive HODMD') + print('\n-----------------------------') + n_iter = 1000 + type = 'iter' + break + else: + print('\tError: Select a valid option\n') + else: + print('\tError: Invalid value (must be integer)\n') + # Load data + path0 = os.getcwd() + print('Inputs:' + '\n') + + while True: + filetype = input('Select the input file format (.mat, .npy, .csv, .pkl, .h5): ') + if filetype.strip().lower() in ['mat', '.mat', 'npy', '.npy', 'csv', '.csv', 'pkl', '.pkl', 'h5', '.h5']: + break + else: + print('\tError: The selected input file format is not supported\n') + + Tensor, _ = data_load.main(filetype) + + ##Number of snapshots nsnap: + while True: + nsnap = input(f'Introduce number of snapshots. Continue with {Tensor.shape[-1]} snapshots: ') + if not nsnap: + nsnap = int(Tensor.shape[-1]) + break + elif nsnap.isdigit(): + nsnap = int(nsnap) + break + else: + print('\tError: Select a valid number (must be integer)\n') + + print('''\nHODMD parameter (d) options: + Option 1: Select only 1 value for d. + Option 2: Select more than one value for d. + ''') + + d_list = [] + print(f'Interval of recommended number of HODMD windows (d): [{int(np.round(nsnap/10))}, {int(np.round(nsnap/2))}]. Other values are accepted') + + while True: + d_dec = input('Select an option (1/2). Continue with option 1: ') + if not d_dec or d_dec == '1': + d = input(f'Introduce number of HODMD windows (d): ') + if d.isdigit(): + d = int(d) + d_list.append(d) + break + else: + print('\tError: Select a valid number (must be integer)\n') + elif d_dec == '2': + break + else: + print('\tError: Select a valid option\n') + + if d_dec == '2': + while True: + d_list_len = input('Select the number of values for d. Continue with 2: ') + if d_list_len.isdigit(): + d_list_len = int(d_list_len) + for d in range(d_list_len): + d_val = input(f'Introduce value number {d+1} for d: ') + if d_val.isdigit(): + d_list.append(int(d_val)) + else: + print('\tError: Invalid value (must be integer)\n') + + if all(isinstance(x, int) for x in d_list): + print(f'Selected values: {d_list}\n') + break + else: + print('\tError: One or more selected values are invalid\n') + else: + print('\tError: Select a valid number (must be integer)\n') + + + # SVD Tolerance + + print('''\nSVD tolerance options: + Option 1: Select only 1 value for SVD tolerance. + Option 2: Select more than one SVD tolerance value. + ''') + + tol1_list = [] + + while True: + tol_dec = input('Select an option (1/2). Continue with option 1: ') + if not tol_dec or tol_dec == '1': + varepsilon1 = input('Introduce SVD tolerance. Continue with 1e-3: ') + if not varepsilon1: + varepsilon1 = 1e-3 + tol1_list.append(varepsilon1) + break + elif is_float(varepsilon1): + varepsilon1 = float(varepsilon1) + tol1_list.append(varepsilon1) + break + else: + print('\tError:Please introduce a number\n') + elif tol_dec == '2': + break + else: + print('\tError: Select a valid option\n') + + ## Tolerance list + if tol_dec == '2': + while True: + tol1_list_len = input('Select the number of values for SVD tolerance. Continue with 2: ') + if tol1_list_len.isdigit(): + tol1_list = [] + tol1_list_len = int(tol1_list_len) + for tol in range(tol1_list_len): + tol_val = input(f'Introduce SVD tolerance number {tol+1}: ') + if is_float(tol_val): + tol1_list.append(float(tol_val)) + else: + print('\tError: Invalid value\n') + + if all(isinstance(x, float) for x in tol1_list): + print(f'Selected values: {tol1_list}\n') + break + else: + print('\tError: One or more selected values are invalid\n') + else: + print('\tError: Select a valid number (must be integer)\n') + + tol2_list = [] + + if len(tol1_list) == 1: + while True: + vardec = input(f'HODMD tolerance value equal to SVD tolerance ({tol1_list[0]}) (y/n). Continue with Yes: ') + if not vardec or vardec.strip().lower() in ['y', 'yes']: + tol2_list = tol1_list + break + elif vardec.strip().lower() in ['n', 'no']: + varepsilon2 = input('Introduce second tolerance (HODMD). Continue with 1e-3: ') + if not varepsilon2: + varepsilon2 = 1e-3 + tol2_list.append(varepsilon2) + break + elif is_float(varepsilon2): + varepsilon2 = float(varepsilon2) + tol2_list.append(varepsilon2) + break + else: + print('\tError: Please introduce a number\n') + else: + print('\tError: Select yes or no (y/n)') + + if len(tol1_list) > 1: + tol2_list = tol1_list + + + ##Time: + while True: + deltaT = input('Introduce time step (deltaT). Continue with 1: ') + if not deltaT: + deltaT = 1 + break + elif is_float(deltaT): + deltaT = float(deltaT) + break + else: + print('\tError: Please introduce a number\n') + + Time = np.linspace(0,nsnap-1,num=nsnap)*deltaT + + print(f'The temporal interval of the data analysed is [{Time[0]}, {Time[-1]}]') + while True: + TimeFin = input('Introduce final time to predict your solution. Continue with reconstruction: ') + if not TimeFin: + TimePred=Time + break + elif is_float(TimeFin): + if float(TimeFin) >= Time[-1]: + TimeFin = float(TimeFin) / deltaT + SnapDif=floor(abs(TimeFin-Time[-1])*deltaT) + TimePred = np.linspace(0,nsnap+SnapDif-1,num=nsnap+SnapDif)*deltaT + break + else: + print(f'\tError: Introduce a time higher than {Time[-1]}\n') + else: + print('\tError: Please introduce a number\n') + + Tensor = Tensor[..., 0:nsnap] + + TimePos = Tensor.ndim + + # Set tolerance for growth rates + TolGR=0 + while True: + TolGRset = input('Do you want to retain the modes with growth rate (in absolute value) < tolerance? Yes or No (y/n). Continue with Yes: ') + if TolGRset.strip().lower() in ['y', 'yes'] or not TolGRset: + TolGR=1 + TolGRvalue = input('Introduce the tolerance for the growth rate of the modes retained. Continue with 0.5: ') + if not TolGRvalue: + TolGRvalue = 0.5 + break + elif is_float(TolGRvalue): + TolGRvalue = float(TolGRvalue) + break + else: + print('\tError: Please introduce a number\n') + elif TolGRset.strip().lower() in ['n', 'no']: + break + else: + print('\tError: Select yes or no (y/n)\n') + + + while True: + TolGRset2 = input('Do you want to set the growth rate to 0 for the temporal predictions? Yes or No (y/n). Continue with No: ') + if TolGRset2.strip().lower() in ['n', 'no'] or not TolGRset2: + GRset=0 + break + elif TolGRset2.strip().lower() in ['y', 'yes']: + GRset=1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + + print('\n-----------------------------') + print('Iterative HODMD summary:') + print('\n' + f'Number of snapshots set at: {nsnap}') + print(f'd Parameter(s) set at: {d_list}') + print(f'SVD tolerance(s) {tol1_list}') + print(f'HODMD tolerance(s): {tol2_list}') + print(f'Time gradient set at deltaT: {deltaT}') + + print('\n-----------------------------') + print('Outputs:\n') + + filen = input('Enter folder name to save the outputs or continue with default folder name: ') + if not filen: + filen = f'{timestr}_{type}_mdHODMD_pred_solution' + else: + filen = f'{filen}' + + while True: + decision_2 = input('Select format of saved files (.mat, .npy). Continue with ".npy": ') + if not decision_2 or decision_2.strip().lower() in ['mat', '.mat', 'npy', '.npy']: + break + else: + print('\tError: Please select a valid output format\n') + + print('') + + if not os.path.exists(f'{path0}/{filen}'): + os.mkdir(f"{path0}/{filen}") + + ################### ITERATIVE algorithm + nn0 = np.shape(Tensor) + nn = np.array(nn0) + nn[1:np.size(nn)] = 0 + TensorR=np.copy(Tensor) + + Frequencies = [] + Amplitudes = [] + GrowthRates = [] + d_val = [] + tol_val = [] + DMDmode_list = [] + Tensor_pred_list = [] + + for d in d_list: + for varepsilon1 in tol1_list: + if len(tol1_list) > 1: + varepsilon2 = varepsilon1 + elif not vardec or vardec.strip().lower() in ['y', 'yes']: + varepsilon2 = varepsilon1 + if not os.path.exists(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}'): + os.mkdir(f"{path0}/{filen}/d_{d}_tol_{varepsilon1}") + if not os.path.exists(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes'): + os.mkdir(f"{path0}/{filen}/d_{d}_tol_{varepsilon1}/DMDmodes") + print(f'\nRunning HODMD for d = {d} and tol = {varepsilon1}') + for zz in range(0,n_iter): + if n_iter > 1: + print(f'Iteration number: {zz+1}') + + if zz != 0: + del S,U,Frequency,GrowthRate,hatT,hatMode,sv,TensorR,nnin + TensorR = TensorReconst + # Reconstruction + ## Perform HOSVD decomposition to calculate the reduced temporal matrix: hatT + nnin = nn + nnin = tuple(nnin) + print('\nPerforming HOSVD. Please wait...\n') + [hatT,U,S,sv,nn1,n, _] = hosvd.HOSVD(TensorR,varepsilon1,nn,nn0,TimePos) + print('\nHOSVD complete\n') + + ## Perform HODMD to the reduced temporal matrix hatT: + print('Performing HODMD. Please wait...\n') + [hatMode,Amplitude,Eigval,GrowthRate,Frequency] = DMDd.hodmd_IT(hatT,d,Time,varepsilon1,varepsilon2) + print('\nHODMD complete\n') + + ## Reconstruct the original Tensor using the DMD expansion: + TensorReconst = DMDd.reconst_IT(hatMode,Time,U,S,sv,nn1,TimePos,GrowthRate,Frequency) + + RRMSE = np.linalg.norm(np.reshape(Tensor - TensorReconst,newshape=(np.size(Tensor),1)),ord=2)/np.linalg.norm(np.reshape(Tensor,newshape=(np.size(Tensor),1))) + print(f'Relative mean square error made in the calculations: {np.round(RRMSE*100, 3)}%\n') + + + GRFreqAmp = np.zeros((np.size(GrowthRate),3)) + for ind in range (0,np.size(GrowthRate)): + GRFreqAmp[ind,0] = GrowthRate[ind] + GRFreqAmp[ind,1] = Frequency[ind] + GRFreqAmp[ind,2] = Amplitude[ind] + + print('GrowthRate, Frequency and Amplitude:\n') + print(GRFreqAmp) + print('\n') + + ## Break the loop when the number of singular values is the same in two consecutive iterations: + + num = 0 + for ii in range(1,np.size(nn1)): + if nnin[ii]==nn1[ii]: + num = num+1 + + if num==np.size(nn1)-1: + break + nn = nn1 + + if n_iter > 1: + print(f'Final number of iterations for d = {d} and tol = {varepsilon1}: {zz+1}') + print(f'\nRelative mean square error made in the calculations: {np.round(RRMSE*100, 3)}%\n') + + if not decision_2 or decision_2.strip().lower() in ['npy', '.npy']: + np.save(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/GRFreqAmp.npy', GRFreqAmp) + + if decision_2.strip().lower() in ['.mat', 'mat']: + mdic = {"GRFreqAmp": GRFreqAmp} + file_mat = str(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/GRFreqAmp.mat') + hdf5storage.savemat(file_mat, mdic, appendmat=True, format='7.3') + + ######### PREDICTIONS + + + if TolGR==1: + ind = np.where(np.abs(GrowthRate) 1: + + plt.figure(num='CLOSE TO CONTINUE RUN - Frequency/Amplitude for all selected cases') + for i in range(total_cases): + plt.plot(Frequencies[i], Amplitudes[i], colored_markers[i], label = f'd = {d_val[i]} | tol = {tol_val[i]}', alpha = .5) + plt.yscale('log') + plt.xlabel('Frequency ($\omega_{m}$)') + plt.ylabel('Amplitude ($a_{m}$)') + plt.title(f'Frequency vs Amplitude for all selected cases') + plt.tight_layout() + plt.legend(loc = 'upper right') + plt.savefig(f'{path0}/{filen}/d_{d}_tol_{varepsilon1}/FrequencyAmplitude.png') + plt.show() + plt.close() + + print(f''' +Run cases - Summary +----------------------------- + ''') + + i = 0 + for d in d_list: + for tol in tol1_list: + print(f'case {i+1}: d = {d} and tol = {tol}') + i += 1 + + print('\n-----------------------------\n') + + print(f'\nATTENTION!: All plots will be saved to {path0}/{filen}\n') + print('Please CLOSE all figures to continue the run\n') + ## Result plots: + print('\nPlotting Frequency/GrowthRate and Frequency/Amplitude plots') + while True: + while True: + case = input(f'Select the case to plot (default case 1): ') + if not case: + case = 0 + break + elif case.isdigit(): + if int(case) <= total_cases: + case = int(case)-1 + break + else: + print('\tError: Selected case does not exist\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + # Frequency vs absolute value of GrowthRate: + plt.figure(num=f'CLOSE TO CONTINUE RUN - Case {case + 1} - Frequency/GrowthRate') + plt.plot(Frequencies[case],np.abs(GrowthRates[case]), 'k+') + plt.yscale('log') # Logarithmic scale in y axis + plt.xlabel('Frequency ($\omega_{m}$)') + plt.ylabel('Absolute value of GrowthRate (|$\delta_{m}$|)') + plt.title(f'd = {d_val[case]} tol = {tol_val[case]}') + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d_val[case]}_tol_{tol_val[case]}/FrequencyGrowthRate.png') + plt.show() + plt.close() + + # Frequency vs amplitudes: + plt.figure(num=f'CLOSE TO CONTINUE RUN - Case {case + 1} - Frequency/Amplitude') + plt.plot(Frequencies[case], Amplitudes[case],'r+') + plt.yscale('log') # Logarithmic scale in y axis + plt.xlabel('Frequency ($\omega_{m}$)') + plt.ylabel('Amplitude ($a_{m}$)') + plt.title(f'd = {d_val[case]} tol = {tol_val[case]}') + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d_val[case]}_tol_{tol_val[case]}/FrequencyAmplitude.png') + plt.show() + plt.close() + + while True: + Resp = input('Do you want to plot another case? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + print('\nPlotting component comparison') + dims = Tensor.ndim + while True: + while True: + case = input(f'Select the case to plot (default case 1): ') + if not case: + case = 0 + break + elif case.isdigit(): + if int(case) <= total_cases: + case = int(case)-1 + break + else: + print('\tError: Selected case does not exist\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + if dims > 3: + while True: + c = input(f'Select a component (max is {Tensor.shape[0]}): ') + if c.isdigit(): + if int(c) <= Tensor.shape[0]: + c = int(c) - 1 + break + else: + print("\tError: Selected component doesn't exist\n") + elif not c: + continue + + while True: + x = input(f'Select X coordinate (must be in range [0, {Tensor.shape[2] - 1}]): ') + if x.isdigit(): + if int(x) in range(0, Tensor.shape[2]): + x = int(x) + break + else: + print(Tensor.shape[2]) + print('\tError: Selected value is out of bounds\n') + elif not x: + continue + + while True: + y = input(f'Select Y coordinate (must be in range [0, {Tensor.shape[1] - 1}]): ') + if y.isdigit(): + if int(y) in range(0, Tensor.shape[1]): + y = int(y) + break + else: + print('\tError: Selected value is out of bounds\n') + elif not y: + continue + else: + print('\tError: Select a valid number format (must be integer)\n') + + if dims == 3: + fig, ax = plt.subplots(1, 2, num = f'CLOSE TO CONTINUE RUN - Case {case + 1} - component comparison') + fig.suptitle(f'Real Data vs Reconstruction - Component comparison') + ax[0].contourf(Tensor[:, :, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(TimePred[:], Tensor_pred_list[case][y, x, :].real, 'k-x', label = 'Reconstructed Data') + ax[1].plot(Time[:], Tensor[y, x, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d_val[case]}_tol_{tol_val[case]}/OrigReconst.png') + plt.show() + plt.close() + + if dims == 4: + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - component comparison') + fig.suptitle(f'Real Data vs Reconstruction - Case {case + 1} - Component comparison') + ax[0].contourf(Tensor[c, :, :, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(TimePred[:], Tensor_pred_list[case][c, y, x, :].real, 'k-x', label = 'Reconstructed Data') + ax[1].plot(Time[:], Tensor[c, y, x, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d_val[case]}_tol_{tol_val[case]}/OrigReconst.png') + plt.show() + plt.close() + + + elif dims == 5: + nz = int(Tensor.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num = 'CLOSE TO CONTINUE RUN - component comparison') + fig.suptitle(f'Real Data vs Reconstruction - Case {case + 1} - Component comparison XY plane') + ax[0].contourf(Tensor[c, :, :, nz, 0]) + ax[0].scatter(x, y, c='black', s=50) + ax[1].plot(TimePred[:], Tensor_pred_list[case][c, y, x, nz, :].real, 'k-x', label = 'Reconstructed Data') + ax[1].plot(Time[:], Tensor[c, y, x, nz, :], 'r-+', label = 'Real Data') + ax[1].set_xlabel('Time') + ax[1].set_ylabel('Data') + ax[1].legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0) + plt.tight_layout() + plt.savefig(f'{path0}/{filen}/d_{d_val[case]}_tol_{tol_val[case]}/OrigReconst.png') + plt.show() + plt.close() + + while True: + Resp = input('Do you want to plot another case? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + print('\n-----------------------------') + print(f'\nSelect a case, component and temporal mode to plot a DMD mode') + while True: + while True: + case = input(f'Select the case to plot (default case 1): ') + if not case: + case = 0 + break + elif case.isdigit(): + if int(case) <= total_cases: + case = int(case)-1 + break + else: + print('\tError: Selected case does not exist\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + while True: + ModeNum = input(f'Introduce the mode number to plot (default mode 1). Maximum number of modes is {DMDmode_list[case].shape[-1]}: ') + if not ModeNum: + ModeNum = 0 + break + elif ModeNum.isdigit(): + if int(ModeNum) <=DMDmode_list[case].shape[-1]: + ModeNum = int(ModeNum)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + if TimePos > 3: + while True: + ModComp = input(f'Introduce the component to plot (default component 1). Maximum number of components is {DMDmode_list[case].shape[0]}: ') + if not ModComp: + ModComp = 0 + break + elif ModComp.isdigit(): + if int(ModComp) <= DMDmode_list[case].shape[0]: + ModComp = int(ModComp)-1 + break + else: + print('\tError: Selected value is out of bounds\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + + if TimePos==3: + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode') + fig.suptitle(f'Case {case + 1} - DMDmode - Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode_list[case][:,:,ModeNum].real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode_list[case][:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.show() + + if TimePos==4: + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode') + fig.suptitle(f'Case {case + 1} - DMDmode - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode_list[case][ModComp,:,:,ModeNum].real) + ax[0].set_title('Real part') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode_list[case][ModComp,:,:,ModeNum].imag) + ax[1].set_title('Imaginary part') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.show() + + elif TimePos==5: + nz = int(Tensor.shape[3] / 2) + fig, ax = plt.subplots(1, 2, num=f'CLOSE TO CONTINUE RUN - DMD mode XY plane') + fig.suptitle(f'Case {case + 1} - DMDmode XY plane - Component {ModComp+1} Mode Number {ModeNum+1}') + ax[0].contourf(DMDmode_list[case][ModComp,:,:,nz,ModeNum].real) + ax[0].set_title('Real part - XY Plane') + ax[0].set_xlabel('X') + ax[0].set_ylabel('Y') + + ax[1].contourf(DMDmode_list[case][ModComp,:,:,nz,ModeNum].imag) + ax[1].set_title('Imaginary part - XY Plane') + ax[1].set_xlabel('X') + ax[1].set_ylabel('Y') + plt.show() + + while True: + Resp = input('Do you want to plot another mode? Yes or No (y/n). Continue with No: ') + if not Resp or Resp.strip().lower() in ['n', 'no']: + Resp = 0 + break + elif Resp.strip().lower() in ['y', 'yes']: + Resp = 1 + break + else: + print('\tError: Select yes or no (y/n)\n') + + if Resp == 0: + break + + if dims <= 3: + return + + if dims > 3: + while True: + dec4 = input(f'Plot video of original data and predicted data? (y/n). Continue with Yes: ') + if not dec4 or dec4.strip().lower() in ['y', 'yes']: + decision4 = True + break + if dec4.strip().lower() in ['n', 'no']: + decision4 = False + return + else: + print('\tError: Please select yes or no (y/n)\n') + + if total_cases > 1: + while True: + case = input(f'Select the case to plot (default case 1): ') + if not case: + case = 0 + break + elif case.isdigit(): + if int(case) <= total_cases: + case = int(case)-1 + break + else: + print('\tError: Selected case does not exist\n') + else: + print('\tError: Select a valid number format (must be integer)\n') + else: + case = 0 + + if dims == 5: + nz = int(Tensor.shape[3] / 2) + while True: + plane = input('Select a plane (XY, XZ, YZ)') + if plane.strip().lower() == 'xy': + Tensor = Tensor[:, :, :, nz, :] + Tensor_pred_list[case] = Tensor_pred_list[case][:, :, :, nz, :] + break + elif plane.strip().lower() == 'xz': + Tensor = Tensor[:, :, 0, :, :] + Tensor_pred_list[case] = Tensor_pred_list[case][:, :, 0, :, :] + break + elif plane.strip().lower() == 'yz': + Tensor = Tensor[:, 0, :, :, :] + Tensor_pred_list[case] = Tensor_pred_list[case][:, 0, :, :, :] + break + else: + print('\tError: Select a valid plane\n') + + else: + pass + + titles = [] + [titles.append(f'Component {i+1}') for i in range(Tensor.shape[0])] + + while True: + if decision4 == True: + vidvel = input(f'Select a component (max is {Tensor.shape[0]}). Continue with component 1: ') + if not vidvel: + vel = 0 + video(Tensor, vel, Title = f'Original Data - {titles[vel]}') + video(Tensor_pred_list[case], vel, Title = f'Predicted data - {titles[vel]}') + break + elif vidvel.isdigit(): + if int(vidvel) <= Tensor.shape[0]: + vel = int(vidvel) - 1 + video(Tensor, vel, Title = f'Original Data - {titles[vel]}') + video(Tensor_pred_list[case], vel, Title = f'Predicted data - {titles[vel]}') + break + else: + print("\tError: Select a valid component\n") + else: + print('\tError: Introduce a valid format (must be integer)\n') + + while True: + ch1 = input('Would you like to plot another component? (y/n). Continue with No: ') + if ch1.strip().lower() in ['y', 'yes']: + while True: + vidvel = input(f'Select a component (max is {Tensor.shape[0]}): ') + if vidvel.isdigit(): + if int(vidvel) <= Tensor.shape[0]: + vel = int(vidvel) - 1 + video(Tensor, vel, Title = f'Original Data - {titles[vel]}') + video(Tensor_pred_list[case], vel, Title = f'Predicted data - {titles[vel]}') + break + else: + print("\tError: Select a valid component\n") + else: + print('\tError: Introduce a valid format (must be integer)\n') + continue + elif not ch1 or ch1.strip().lower() in ['n', 'no']: + return + + else: + print('\tError: Select yes or no (y/n)\n') \ No newline at end of file diff --git a/Tutorial v0.1 ModelFLOWs-app.pdf b/v0.1_Tutorial_ModelFLOWs-app.pdf similarity index 98% rename from Tutorial v0.1 ModelFLOWs-app.pdf rename to v0.1_Tutorial_ModelFLOWs-app.pdf index f05ce1b..ac898ff 100644 Binary files a/Tutorial v0.1 ModelFLOWs-app.pdf and b/v0.1_Tutorial_ModelFLOWs-app.pdf differ diff --git a/v0.1_desktop/sample.txt b/v0.1_desktop/sample.txt deleted file mode 100644 index e69de29..0000000 diff --git a/v0.1_main/sample.txt b/v0.1_main/sample.txt deleted file mode 100644 index e69de29..0000000 diff --git a/v0.1_web-browser/sample.txt b/v0.1_web-browser/sample.txt deleted file mode 100644 index e69de29..0000000