The POD-DL-ROM
THE POD-DL-ROM
Script by Elise Grosjean (ENSTA-Paris, elise.grosjean@ensta-paris.fr)
import sys
#!{sys.executable} -m pip install numpy
#!{sys.executable} -m pip install matplotlib
#!{sys.executable} -m pip install scikit-fem
#!{sys.executable} -m pip install tensorflow
Let us consider a parameterized problem. Having (previously computed) solutions for different parameter values, we aim at approaching much faster “online” the solution associated to a new parameter value. To do so, we will employ a POD-DL-ROM strategy as in the paper “POD-DL-ROM: Enhancing deep learning-based reduced order models for nonlinear parametrized PDEs by proper orthogonal decomposition”. The idea is to first compress the training solutions with a POD before relying on a DL-ROM approach (see also “A comprehensive deep learning-based approach to reduced order modeling of nonlinear time-dependent parametrized PDEs”).
This prior dimensionality reduction through a randomized singular value decomposition (rSVD) avoids to feed training data of large dimension $\mathcal{N}_h$ (even if the number of modes $N$ required for the POD is big, we assume here that $N << \mathcal{N}_h$ where $\mathcal{N}_h$ denotes the number of degrees of freedom. Indeed, although extremely efficient at testing time, when evaluating the PDE solution for any new testing-parameter instance, DL-ROMs require an expensive training stage, because of the extremely large number of network parameters (weights/biases) to be estimated. When $N$ is large (i.e. when the dimension of the linear trial subspace becomes very large), this ROM technique shows efficient computational performances during both offline and online stages, compared to classical projection-based ROMs.
To sum up this ROM technique, a two-step dimensionality reduction is performed: first, POD (realized through rSVD) is applied on a set of High-Fidelity (HF) snapshots, then a DL-ROM is built to approximate the map between the parameters and the POD coordinates.
After the data compression with a rSVD, the training strategy consists in:
- 1/ encoding the compressed solution with a CAE that goes from $\mathbb{R}^N \to \mathbb{R}^n$ with the dimension of the latent space $n$ as close as possible to the number of parameters ($n<
- 2/ learning to approximate the latent space with a DFNN,
- 3/ decoding this approximation to find the original solution.
# import packages
import skfem # for Finite Element Method
import numpy as np
import matplotlib.pyplot as plt
from sklearn.utils.extmath import randomized_svd
import random
import tensorflow as tf
import itertools
random.seed(10)
2025-04-15 13:49:21.531644: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
1) An unsteady advection–diffusion–reaction equation:
We are going to use in this example an unsteady advection-diffusion-reaction problem with the Finite Element Method (FEM), which consists in solving on a unit square (denoted $\Omega$) the following equations:
$$\begin{align*} &\partial_t u - \nabla \cdot(\mu_1 \nabla u) + b(t;\mu)\cdot \nabla u + c u =f(\mu_3,\mu_4), \textrm{ in } \Omega \times (0,T),\\ & \mu_1 \nabla u \cdot n=0, \textrm{ on } \partial \Omega \times (0,T),\\ & u(0)=0, \textrm{ in } \Omega,\\ \end{align*}$$Here, $\mu=(\mu_1,\mu_2,\mu_3,\mu_4)$ is our parameter of interest. We employ P1 elements with a backward time scheme of order 2.
In the paper “POD-DL-ROM: Enhancing deep learning-based reduced order models for nonlinear parametrized PDEs by proper orthogonal decomposition”, the parameter set is given by $[0.002, 0.005] × [30, 70] × [0.4, 0.6]^2$.
We tested the method below with $\mu_1=0.004$, $\mu_2 \in [10,100]$, $\mu_3 \in [0.4, 0.6]$ and $\mu_4=0.5$ and 20 time steps (to reduce the costs of the offline stage).
""" First we define a mesh for the unit square with the boundary decomposition """
mesh= skfem.MeshTri().refined(6).with_boundaries(
{
"left": lambda x: x[0] == 0,
"right": lambda x: x[0] == 1,
"down": lambda x: x[1] == 0,
"up": lambda x: x[1] == 1,
}
)
print(mesh)
mesh
<skfem MeshTri1 object>
Number of elements: 8192
Number of vertices: 4225
Number of nodes: 4225
Named boundaries [# facets]: left [64], bottom [64], right [64], top [64], down [64], up [64]
""" Then we assemble the matrices of our model problem """
# Assembling matrices
from skfem.assembly import BilinearForm, LinearForm
from skfem.helpers import grad, dot,div,ddot
@BilinearForm
def laplace(u, v, _):
return dot(grad(u), grad(v))
@BilinearForm
def b1(u, v, w):
telt=w.telt
#print(telt)
mu2=w.mu2
b=[np.cos(np.pi/mu2*telt),np.sin(np.pi/mu2*telt)]
return b[0]*grad(u)[0]*v+b[1]*grad(u)[1]*v
@BilinearForm
def mass(u, v, _):
return u * v
@LinearForm
def linearterm(v, w):
mu3=w.mu3
mu4=w.mu4
f=10*np.exp(-((w.x[0]-mu3)**2+(w.x[1]-mu4)**2)/0.07**2)
return f*v
c=1
""" Solving the problem """
from skfem import *
element = {'u': ElementTriP1()} #[u]= Solution with P1 FEM elements
def visualize_solution(Mesh,solution):
## Function that allows us to visualize the solution
from skfem.visuals.matplotlib import plot
return plot(Mesh, solution, shading='gouraud', colorbar=True)
def SolveProblem_BDF2(mu, Mesh):
""" Backward Differentiation Formula (BDF) of order 2 """
mu1,mu2,mu3,mu4 = mu[0]
T = 10*np.pi #final time
dt = 5*np.pi / 10 #time step
basis = {variable: Basis(Mesh, e) for variable, e in element.items()} # FEM space
u_prev = basis['u'].zeros() # u_n (initial condition)
t_list=[0] #time steps
u_list=[u_prev]
u_curr = u_prev.copy() # u_{n+1}
M = mass.assemble(basis['u'])
laplaceMat = mu1 * laplace.assemble(basis['u'])
lineartermV = linearterm.assemble(basis['u'], mu3=mu3, mu4=mu4)
# implicit scheme - order 1 for init
t = dt
B1 = b1.assemble(basis['u'], mu2=mu2, telt=t)
K1 = M / dt + laplaceMat + B1 + c*M #c=1
lineartermT = lineartermV + M @ u_prev / dt
u_curr = solve(K1, lineartermT) # u_{n+1}
t_list.append(dt)
u_list.append(u_curr)
# BDF2 iteration
for t in np.arange(2 * dt, T , dt):
B2 = b1.assemble(basis['u'], mu2=mu2, telt=t)
K2 = (3 / (2 * dt)) * M + laplaceMat + B2 + c * M
lineartermT = lineartermV + (2 / dt) * M @ u_curr - (1 / (2 * dt)) * M @ u_prev
u_next = solve(K2, lineartermT) # u_{n+2}
u_prev, u_curr = u_curr, u_next # Update
t_list.append(t)
u_list.append(u_curr)
return u_list,t_list #[u_curr], [t] #
velocity,t_list=SolveProblem_BDF2([[0.0045,50,0.2, 0.475]],mesh)
visualize_solution(mesh,velocity[-1]).show()
velocity,t_list=SolveProblem_BDF2([[0.006,35,0.475, 0.475]],mesh)
visualize_solution(mesh,velocity[-1]).show()
print("Number of time stepps", len(velocity))
Number of time stepps 20
2) The POD-DL-ROM method
We are now able to proceed with the offline and online parts of the POD-DL-ROM method.
OFFLINE PART
We define one mesh and the associated finite element spaces (as before).
## FINE MESH
FineMesh = skfem.MeshTri().refined(6)
FineBasis = {variable: Basis(FineMesh, e, intorder=4)
for variable, e in element.items()} # FEM space
NumberOfNodesFineMesh = FineMesh.p.shape[1]
print("Number of nodes: ",NumberOfNodesFineMesh)
#num_dofs_uFineMesh = basis['u'].doflocs.shape[1] # or np.shape(velocity)[0] for DOFs
Number of nodes: 4225
# """ TRAINING set """
print("-----------------------------------")
print(" Offline ")
print("-----------------------------------")
# The parameter set is given by $[0.002, 0.005] × [30, 70] × [0.4, 0.6]^2$
# and we take for the training set 4*5*5*5 uniformly distributed in each parametric direction
#Nt = 100 time instances
mu1_values = 0.004 # np.linspace(0.002, 0.005, 4) # 4 valeurs dans [0.002, 0.005]
mu2_values = np.linspace(10, 100, 16) # 5 valeurs dans [30, 70]
mu3_values = np.linspace(0.4, 0.6, 6) # 5 valeurs dans [0.4, 0.6]
mu4_values = 0.5 #np.linspace(0.4, 0.6, 5)
mu5_values = t_list # additional parameter = time
print("mu1: ",mu1_values)
print("mu2: ",mu2_values)
print("mu3: ",mu3_values)
print("mu4: ",mu4_values)
#param_combinations = list(itertools.product(mu1_values, mu2_values, mu3_values, mu4_values))
param_combinations = list(itertools.product( mu2_values, mu3_values))
FineSnapshots=[]
muParam=[]
for mu in param_combinations:
#print(mu[0])
mutmp=[mu1_values,mu[0],mu[1],mu4_values]
solution,_=SolveProblem_BDF2([mutmp],FineMesh)
mu_full = np.hstack([[mu] * len(velocity) , np.array(mu5_values).reshape((len(velocity),1))]) # we add time as a parameter mu5
mu_full = mu_full.tolist()
muParam += mu_full
FineSnapshots.append(solution)
print("Number of training parameters: ",len(muParam))
print("First parameter:", muParam[0])
print("Shape snapshots matrix: ",np.shape(np.array(FineSnapshots)))
visualize_solution(FineMesh,solution[-1]).show()
-----------------------------------
Offline
-----------------------------------
mu1: 0.004
mu2: [ 10. 16. 22. 28. 34. 40. 46. 52. 58. 64. 70. 76. 82. 88.
94. 100.]
mu3: [0.4 0.44 0.48 0.52 0.56 0.6 ]
mu4: 0.5
Number of training parameters: 1920
First parameter: [10.0, 0.4, 0.0]
Shape snapshots matrix: (96, 20, 4225)
Now we proceed with the data compression (Random Singular Decomposition Value: see https://reducedbasis.github.io/post/rsvd/ for more details).
## randomSVD on the snapshots
NumberOfModes=64
import time
start_time=time.time()
FineSnapshotsMat=np.concatenate(FineSnapshots)
print("shape snapshots: ", FineSnapshotsMat.shape) #NS, NumberOfDegreesOfFreedrom (NS=Ntrain*NT)
U, Sigma, VT = randomized_svd(np.array(FineSnapshotsMat).transpose(),
n_components=NumberOfModes,
n_iter=5,
random_state=42) #m=10 (oversampling default value)
ReducedBasis = U[:, :NumberOfModes] # (Ndof x NumberOfModes)
EigenValues = Sigma[:NumberOfModes] ** 2
print("shape reduced basis: " , np.shape(ReducedBasis))
end_time = time.time()
execution_time = end_time - start_time
print(f"Temps d'exécution : {execution_time:.6f} secondes")
shape snapshots: (1920, 4225)
shape reduced basis: (4225, 64)
Temps d'exécution : 0.334519 secondes
Now that we have computed the snapshots, we can decompose them in one training part and one testing part
# In view of the DL-ROM strategy, we decompose Mtrain in [Mtrain, Mval] where Mval is of size alpha*NS
#(and NS=Ntrain*NT, NT beeing the number of time steps)
# but first a random shuffle
FineSnapshotsMat=np.concatenate(FineSnapshots)
print("shape snapshots: ", FineSnapshotsMat.shape) #NS, NumberOfDegreesOfFreedrom (NS=Ntrain*NT)
#randomly shuffle to decompose in training/testing parts
indices = list(range(len(FineSnapshotsMat)))
random.shuffle(indices)
FineSnapshots_training = [FineSnapshotsMat[i,:] for i in indices] #R^NS=R^Ntrain*NT
SVector=FineSnapshots_training.copy() # full snapshots
MVector = np.array([muParam[i] for i in indices]) #full parameters
alpha = 0.1 # e.g. 10% for testing
n = MVector.shape[0]
split_point = int((1 - alpha) * n)
#parameters
M1 = MVector[:split_point] # 90%
M2 = MVector[split_point:] # 10%
#values
S1 = SVector[:split_point]
S2 = SVector[split_point:]
print("S1 (snapshots for training) shape:", np.shape(S1))
print("M2 (parameters for testing) shape:", np.shape(M2))
shape snapshots: (1920, 4225)
S1 (snapshots for training) shape: (1728, 4225)
M2 (parameters for testing) shape: (192, 3)
We compress the training and testing snapshots
print("------------------------------------------------")
print(" Compress training/testing snapshots ")
print("------------------------------------------------")
VTS1=np.zeros((np.shape(S1)[0],NumberOfModes))
VTS2=np.zeros((np.shape(S2)[0],NumberOfModes))
print(np.shape(S1))
for i,snap in enumerate(S1):#FineSnapshotsMat:
ExactSolution =snap #size Nh
CompressedSolutionU= ReducedBasis.transpose()@ExactSolution #snapshot compression
VTS1[i]=ReducedBasis.transpose()@ExactSolution #snapshot compression
#ReconstructedCompressedSolution = np.dot(VTS1[i], ReducedBasis.transpose())
for i,snap in enumerate(S2):#FineSnapshotsMat:
ExactSolution =snap #size Nh
VTS2[i]=ReducedBasis.transpose()@ExactSolution #snapshot compression
#ReconstructedCompressedSolution = np.dot(VTS2[i], ReducedBasis.transpose())
print("Compressed size",VTS1.shape)
ReconstructedCompressedSolution = np.dot(VTS2[-1], ReducedBasis.transpose())
visualize_solution(FineMesh,ReconstructedCompressedSolution).show()
------------------------------------------------
Compress training/testing snapshots
------------------------------------------------
(1728, 4225)
Compressed size (1728, 64)
## Normalization
"""
## parameters training M1 and test M2 matrices ##
##
"""
print(M1.shape)
Mmax=np.zeros(M1.shape[1])
Mmin=np.zeros(M1.shape[1])
for j in range(M1.shape[1]):
Mmax[j]=np.max(M1[:,j])
Mmin[j]=np.min(M1[:,j])
# Training parameters
M1train=np.zeros(np.shape(M1))
for j in range(M1.shape[1]):
M1train[:,j]=(M1[:,j]-Mmin[j])/(Mmax[j]-Mmin[j])
#M1train[:,4]=mu5_values[0] ##if only one time step
M1train=M1train.transpose()
# Testing part
M2test=np.zeros(np.shape(M2))
for j in range(M2.shape[1]):
M2test[:,j]=(M2[:,j]-Mmin[j])/(Mmax[j]-Mmin[j])
#M2test[:,4]=mu5_values[0]
"""
## snapshots training S1 and test S2 matrices ##
##
"""
Smax=np.max(VTS1)
Smin=np.min(VTS1)
S1train=(VTS1-Smin)/(Smax-Smin)
S1train=S1train.transpose()
S2test=(VTS2-Smin)/(Smax-Smin)
NS1=S1train.shape[1]
print("number of training parameters: ",NS1)
#print(np.shape(S2))
(1728, 3)
number of training parameters: 1728
0
The key ingredient here is the DFNN that allows to avoid the use of the encoder during the testing time, part that is costly.
from tensorflow.keras import layers, models, initializers
# 1/ encode the solution with a CAE that goes from R^N -> R^n with n as close as possible to the number of parameters (n<=N)
# 2/ learn to approximate the latent space of the parameters with a DFNN
# 3/ decode this approximation to approach the original solution
print("shape S1 training:",np.shape(S1train))
print("shape S2 validation:",np.shape(S2test))
print("shape M1 parameters:",np.shape(M1train))
print("shape M2 parameters:",np.shape(M2test))
#Training inputs:
# Optimizers (learning rate = eta)
eta=5e-4
#NS1=S1train.shape[1]
print("Total number of training parameters:", NS1)
N_epochs=2500 # Number of iterations
print("Maximum iterations:", N_epochs)
BatchSize=108 #batch size (allows to save CPU memory)
print("Number of minibatches:",NS1/BatchSize)
assert NS1%BatchSize==0
NbMiniBatches=int(NS1/BatchSize) # Number of Minibatches
# We can add an initialization of theta_0 from a coarser model
n=M1train.shape[0]
print("latent space dim:",n)
"""================"""
""" Initialization """
"""================"""
def build_encoder(latent_dim=n):
# Input: need a latent space of dimension n < NumberOfModes
# Output: Encoder (CAE)
# Three conv filters:
# 1. 32 output pictures where each filter is a 5*5 matrix that scans the picture
# It yields 5*5*1=25 weights for one filter and one bias
# elu activation function as in the paper "A comprehensive DL-based approach to ROM of nonlinear time-dependent parametrized PDEs"
# 2. 64 output pictures stride=2 -> jump one pixel at a time
inputs = tf.keras.Input(shape=(8, 8, 1)) # picture of size 8*8 = sqrt(NumberOfModes)*sqrt(NumberOfModes)
x = layers.Conv2D(32, kernel_size=5, strides=1, padding='same', activation='elu')(inputs) # 8x8x32
x = layers.Conv2D(64, kernel_size=5, strides=2, padding='same', activation='elu')(x) # 4x4x64
x = layers.Conv2D(128, kernel_size=5, strides=2, padding='same', activation='elu')(x) # 2x2x128
x = layers.Flatten()(x)
x = layers.Dense(128, activation='elu')(x)
latent = layers.Dense(latent_dim, activation='elu', name='latent_vector')(x)
return tf.keras.Model(inputs, latent, name='encoder')
def build_dfnn(latent_dim=n):
inputs = tf.keras.Input(shape=(n,))
print(inputs.shape)
x = tf.keras.layers.Dense(NumberOfModes, activation='elu')(inputs)
x = tf.keras.layers.Dense(NumberOfModes, activation='elu')(x)
x = tf.keras.layers.Dense(latent_dim)(x)
print(x.shape)
return tf.keras.Model(inputs, x)
def build_decoder(latent_dim=n):
inputs = tf.keras.Input(shape=(latent_dim,))
x = layers.Dense(128, activation='elu')(inputs)
x = layers.Dense(2 * 2 * 128, activation='elu')(inputs)
x = layers.Reshape((2, 2, 128))(x) # last encoded part
x = layers.Conv2DTranspose(64, kernel_size=5, strides=2, padding='same', activation='elu')(x) # 4x4x64
x = layers.Conv2DTranspose(32, kernel_size=5, strides=2,padding='same', activation='elu')(x) # 8x8x32
outputs = layers.Conv2DTranspose(1, kernel_size=5, padding='same', activation='linear')(x)# Last layer to obtain a picture 8x8x1
return tf.keras.Model(inputs, outputs, name='decoder')
encoder = build_encoder(latent_dim=n)
loss_enc = tf.keras.losses.MeanSquaredError()
theta_E_stock = []
dfnn = build_dfnn(latent_dim=n)
loss_dfnn = tf.keras.losses.MeanSquaredError()
theta_DFNN_stock = []
decoder = build_decoder(latent_dim=n)
loss_dec = tf.keras.losses.MeanSquaredError()
#loss_dec2 = lambda y_true, y_pred: 1 - tf.keras.losses.CosineSimilarity()(y_true, y_pred) #can try other errors
theta_D_stock = []
optimizer = tf.keras.optimizers.Adam(learning_rate=eta)
"""========================"""
""" Parameter optimization """
"""========================"""
# Step 2: reshape in picture frame for CAE
print(S2test.shape)
S2_reshaped=tf.reshape(S2test,((-1,int(np.sqrt(NumberOfModes)),int(np.sqrt(NumberOfModes)),1))) # for encoder
S2_batch=tf.reshape(S2test, (-1, NumberOfModes)) #for loss
ne=0
while ne<=N_epochs:
#if ne==500 and total_loss>=total_loss_prec: #early-stopping criterion
# print(f"early stopping...ne=={ne}==500 and {total_loss}>={total_loss_prec}" )
# break
#total_loss_prec=total_loss
"""==========="""
## Training part
"""==========="""
for k in range(NbMiniBatches):
# Iteration (ne, k)
iteration = NbMiniBatches * ne + k
# Step 1: sample minibatch [M_batch,S_batch] from M1, S1
start = k * BatchSize
end = start + BatchSize
M1_batch = M1train.T[start:end,:] # shape (n, 120)
S1_batch = S1train.T[start:end,:] # shape (N, 120)
# Step 2: reshape in picture frame for CAE
S1_batch_reshaped = tf.reshape(tf.cast(S1_batch, tf.float32),(BatchSize, int(np.sqrt(NumberOfModes)), int(np.sqrt(NumberOfModes)), 1))
# Step 3: encoder from S1_batch [Convolutional autoencoder (CAE)] + DFNN + decoder
# with tensorflow and an activation function ELU
with tf.GradientTape() as tape: # for automatic differentiation
# step a/
S1tilde_n_batch = encoder(S1_batch_reshaped,training=True) #latent space that we want to approximate of shape (BatchSize, latent_dim)
# step b/
latent_pred = dfnn(M1_batch, training=True) # function to approximate the latent space. shape: (BatchSize, latent_dim)
# step c/
S_reconstructed = decoder(latent_pred, training=True)
St1_batch = tf.reshape(S_reconstructed, (BatchSize, NumberOfModes))
# Step 4: Losses
loss_int = loss_dfnn(S1tilde_n_batch, latent_pred) # latent space loss (S1tilde_n_batch= true latent space)
loss_rec = loss_dec(S1_batch, St1_batch) # reconstruction loss
total_loss = loss_int+loss_rec #alpha * loss_int + beta * loss_rec
# Weights and biases
weights_biases = (encoder.trainable_weights + dfnn.trainable_weights + decoder.trainable_weights)
# Total loss gradient w.r.t. parameters (weights/biases): tape=automatic diff
grads = tape.gradient(total_loss, weights_biases)
# Step 5: Update weights/biases with one optimizer
optimizer.apply_gradients(zip(grads, weights_biases))
"""==========="""
## Validation part
"""==========="""
# we store the weights_biases
if ne % 1000 == 0:
theta_E_stock.append(encoder.get_weights())
theta_DFNN_stock.append(dfnn.get_weights())
theta_D_stock.append(decoder.get_weights())
# Redo step 3: with encoder from S2_batch [Convolutional autoencoder (CAE)]
# with tensorflow and an activation function ELU
# step a/
S2tilde_n = encoder(S2_reshaped,training=True) #latent space that we want to approximate of shape (BatchSize, latent_dim)
# step b/
latent_pred = dfnn(M2test, training=True) # function to approximate the latent space. shape: (BatchSize, latent_dim)
# step c/
S_reconstructed = decoder(latent_pred, training=True)
St2 = tf.reshape(S_reconstructed, (-1, NumberOfModes))
# Step 4: Losses #for early criterion
loss_int = loss_dfnn(S2tilde_n, latent_pred) # latent space loss (S2tilde_n= true latent space)
loss_rec = loss_dec(S2_batch, St2) # reconstruction loss
total_loss = loss_int + loss_rec
if ne % 500 == 0:
print(f"loss_recStep {ne}/{N_epochs}. Loss testing : {total_loss.numpy():.6f}")
ne+=1
ExactSolution=S2_batch[10]
ExactSolution=ExactSolution*(Smax-Smin)+Smin
ExactSolution = np.dot(ExactSolution, ReducedBasis.transpose())
CompressedSolutionU =St2[10]
CompressedSolutionU=CompressedSolutionU*(Smax-Smin)+Smin
ReconstructedCompressedSolution = np.dot(CompressedSolutionU, ReducedBasis.transpose())
#print(np.linalg.norm((S2_batch[10].numpy()-St2[10].numpy())))
visualize_solution(FineMesh,ExactSolution).show()
visualize_solution(FineMesh,ReconstructedCompressedSolution).show()
shape S1 training: (64, 1728)
shape S2 validation: (192, 64)
shape M1 parameters: (3, 1728)
shape M2 parameters: (192, 3)
Total number of training parameters: 1728
Maximum iterations: 2500
Number of minibatches: 16.0
Number of minibatches: 16
latent space dim: 3
(None, 3)
(None, 3)
(192, 64)
loss_recStep 0/2500. Loss testing : 0.041778
loss_recStep 500/2500. Loss testing : 0.001538
loss_recStep 1000/2500. Loss testing : 0.000781
loss_recStep 1500/2500. Loss testing : 0.000497
loss_recStep 2000/2500. Loss testing : 0.000385
loss_recStep 2500/2500. Loss testing : 0.000307
# validation visualization
print(S2test.shape)
S2_batch=tf.reshape(S2test, (-1, NumberOfModes))
latent_pred = dfnn(M2test, training=True) # function to approximate the latent space. shape: (BatchSize, latent_dim)
S_reconstructed = decoder(latent_pred, training=True)
St2 = tf.reshape(S_reconstructed, (-1, NumberOfModes))
ExactSolution=S2_batch[25]
ExactSolution=ExactSolution*(Smax-Smin)+Smin
ExactSolution = np.dot(ExactSolution, ReducedBasis.transpose())
CompressedSolutionU =St2[25]#St[i] #size Nh
CompressedSolutionU=CompressedSolutionU*(Smax-Smin)+Smin
ReconstructedCompressedSolution = np.dot(CompressedSolutionU, ReducedBasis.transpose())
visualize_solution(FineMesh,ExactSolution).show()
visualize_solution(FineMesh,ReconstructedCompressedSolution).show()
(192, 64)
Now we can proceed with the online step
""" TESTING set """
print("-----------------------------------")
print(" Online ")
print("-----------------------------------")
# The parameter set is given by $[0.002, 0.005] × [30, 70] × [0.4, 0.6]^2$
# Nt = 20 time instances
# testing values (different from the training ones)
mu1_values = 0.004 # np.linspace(0.002, 0.005, 4) # 4 valeurs dans [0.002, 0.005]
mu2_values = np.linspace(35, 65, 4) # 5 valeurs dans [30, 70]
mu3_values = np.linspace(0.425, 0.575, 4) # 5 valeurs dans [0.4, 0.6]
mu4_values = 0.5 #np.linspace(0.4, 0.6, 5)
mu5_values = t_list # additional parameter = time
print("mu1: ",mu1_values)
print("mu2: ",mu2_values)
print("mu3: ",mu3_values)
print("mu4: ",mu4_values)
param_combinations = list(itertools.product( mu2_values, mu3_values))
FineSnapshots_testing=[]
muParam=[]
for mu in param_combinations:
mutmp=[mu1_values,mu[0],mu[1],mu4_values]
solution,_=SolveProblem_BDF2([mutmp],FineMesh)
mu_full = np.hstack([[mu] * len(solution) , np.array(mu5_values).reshape((len(solution),1))]) # we add time as a parameter mu5
mu_full = mu_full.tolist()
muParam += mu_full
FineSnapshots_testing.append(solution)
visualize_solution(FineMesh,solution[-1]).show()
-----------------------------------
Online
-----------------------------------
mu1: 0.004
mu2: [35. 45. 55. 65.]
mu3: [0.425 0.475 0.525 0.575]
mu4: 0.5
FineSnapshotsMat=np.concatenate(FineSnapshots_testing)
print("shape snapshots: ", FineSnapshotsMat.shape) #NS, NumberOfDegreesOfFreedrom (NS=Ntest*NT)
indices = list(range(len(FineSnapshotsMat)))
FineSnapshots_testing = [FineSnapshotsMat[i,:] for i in indices] #R^NS=R^Ntest*NT
S2 = FineSnapshots_testing.copy() # full snapshots
M2 = np.array([muParam[i] for i in indices]) #full parameters
print("S2 (snapshots for testing) shape:", np.shape(S2))
print("M2 (parameters for testing) shape:", np.shape(M2))
shape snapshots: (320, 4225)
S2 (snapshots for testing) shape: (320, 4225)
M2 (parameters for testing) shape: (320, 3)
print("-----------------------------------")
print(" Data Compression ")
print("-----------------------------------")
VTS2=np.zeros((np.shape(S2)[0],NumberOfModes))
for i,snap in enumerate(S2):#FineSnapshotsMat:
ExactSolution =snap #size Nh
VTS2[i]=ReducedBasis.transpose()@ExactSolution #snapshot compression
#ReconstructedCompressedSolution = VTS2[i]
CompressedSolutionU=VTS2[1]
ReconstructedCompressedSolution = np.dot(CompressedSolutionU, ReducedBasis.transpose())
print("Compressed size",VTS2.shape)
visualize_solution(FineMesh,ReconstructedCompressedSolution).show()
-----------------------------------
Data Compression
-----------------------------------
Compressed size (320, 64)
## Normalization
"""
## parameters test M2 matrix ##
##
"""
# Testing part
M2test=np.zeros(np.shape(M2))
for j in range(M2.shape[1]):
M2test[:,j]=(M2[:,j]-Mmin[j])/(Mmax[j]-Mmin[j])
#M2test[:,4]=mu5_values[0] #if one time step
"""
## snapshots test S2 matrix ##
##
"""
S2test=(VTS2-Smin)/(Smax-Smin)
"""==========="""
## Testing part
"""==========="""
print(S2test.shape)
S2_batch=tf.reshape(S2test, (-1, NumberOfModes))
latent_pred = dfnn(M2test, training=True) # function to approximate the latent space. shape: (BatchSize, latent_dim)
S_reconstructed = decoder(latent_pred, training=True)
St2 = tf.reshape(S_reconstructed, (-1, NumberOfModes))
ExactSolution=S2_batch[10]
ExactSolution=ExactSolution*(Smax-Smin)+Smin
ExactSolution = np.dot(ExactSolution, ReducedBasis.transpose()) #true compressed
CompressedSolutionU =St2[10]#St[i] #size Nh
CompressedSolutionU=CompressedSolutionU*(Smax-Smin)+Smin
ReconstructedCompressedSolution = np.dot(CompressedSolutionU, ReducedBasis.transpose())
visualize_solution(FineMesh,FineSnapshots_testing[10]).show() #true solution FineSnapshots_testing[i]
visualize_solution(FineMesh,ExactSolution).show() #true compressed
visualize_solution(FineMesh,ReconstructedCompressedSolution).show() #POD-DL-ROM approx
(320, 64)
"""==================="""
## Testing part accuracy
"""==================="""
L2compressionErrors=[]
L2=mass.assemble(FineBasis['u'],nu=1)
print(np.shape(VTS2))
for i,snap in enumerate(St2[:10]):
ExactSolution=FineSnapshots_testing[i]
CompressedSolutionU=snap*(Smax-Smin)+Smin #POD_DL_ROM compressed data
ReconstructedCompressedSolution = np.dot(CompressedSolutionU, ReducedBasis.transpose())
norml2ExactSolution=np.sqrt(ExactSolution@(L2@ExactSolution))
t=np.abs(ReconstructedCompressedSolution-ExactSolution)
if norml2ExactSolution !=0:
relL2Error=np.sqrt(t@L2@t)/norml2ExactSolution
else:
relL2Error = np.sqrt(t@L2@t) #np.linalg.norm(ReconstructedCompressedSolution-ExactSolution)
L2compressionErrors.append(relL2Error)
print("L2 averaged compression error =", np.mean(L2compressionErrors))
visualize_solution(FineMesh,ExactSolution).show()
visualize_solution(FineMesh,ReconstructedCompressedSolution).show()
(320, 64)
L2 averaged compression error = 0.07518568436754877