1D Case - Figure 10#
Comparison of residual loss
\[L_r = \frac{\lambda_1}{n_{\Omega}}\sum_{i=0}^{n_{\Omega}-1}\left( \nabla \cdot \underline{\underline{\sigma}} - \underline f \right)^2 + \frac{\lambda_1}{n_{\Omega}}\sum_{i=0}^{n_{\Omega}-1}\left( \underline{\underline{\sigma}}(\underline u) - \underline{\underline{\sigma}} \right)^2 \]
weak formulation loss
\[ L_w = \sum_{i=0}^{n-1} \left(\int_\Omega \underline{\underline{\sigma}}(\underline u):\underline{\underline{\epsilon}}(\underline u_i^*) - \int_{\partial\Omega_N} \underline t \underline u_i^* - \int_\Omega\underline f \underline u_i^*\right)^2\]
and potential energy loss
\[L_p = \frac{1}{2}\int_\Omega \underline{\underline{\sigma}}(\underline u):\underline{\underline{\epsilon}}(\underline u)
- \int_\Omega\underline f \underline u
- \int_{\partial\Omega_N} \underline t \underline u\]
#%% Libraries import
import sys
# sys.path.append("../neurom/")
from neurom.HiDeNN_PDE import MeshNN, NeuROM, MeshNN_2D, MeshNN_1D
# Import pre-processing functions
import neurom.src.Pre_processing as pre
# Import torch librairies
import torch
import torch.nn as nn
# Import Training funcitons
from neurom.src.Training import Training_1D_FEM_LBFGS, Training_1D_Mixed_LBFGS
#Import post processing libraries
import neurom.Post.Plots as Pplot
import time
import os
import torch._dynamo as dynamo
mps_device = torch.device("mps")
from importlib import reload # Python 3.4+
import tomllib
import numpy as numpy
import argparse
# Load default configuration file (defines dimension, domain, boundary conditions, number of training iterations etc.)
Default_config_file = 'Configurations/config_1D.toml'
with open(Default_config_file, mode="rb") as f:
config = tomllib.load(f)
# Experiment setting: Potential energy loss
# 6 mesh resolutions, 5-point quadrature
mesh_resolution_pe = [10,21,41,80,160]
loss_u_pe = numpy.zeros((len(mesh_resolution_pe)))
loss_grad_pe = numpy.zeros((len(mesh_resolution_pe)))
config["interpolation"]["n_integr_points"] = 5
config["solver"]["IntegralMethod"] = "Gaussian_quad"
config["solver"]["FrozenMesh"] = True
for res in range(len(mesh_resolution_pe)):
config["interpolation"]["np"] = mesh_resolution_pe[res]
# Load parameters
if config["interpolation"]["dimension"] == 1:
Mat = pre.Material( flag_lame = True, # If True should input lmbda and mu instead of E and nu
coef1 = config["material"]["E"], # Young Modulus
coef2 = config["geometry"]["A"] # Section area of the 1D bar
)
elif config["interpolation"]["dimension"] == 2:
try:
Mat = pre.Material( flag_lame = False, # If True should input lmbda and mu instead of E and nu
coef1 = config["material"]["E"], # Young Modulus
coef2 = config["material"]["nu"] # Poisson's ratio
)
except:
Mat = pre.Material( flag_lame = True, # If True should input lmbda and mu instead of E and nu
coef1 = config["material"]["lmbda"], # First Lame's coef
coef2 = config["material"]["mu"] # Second Lame's coef
)
MaxElemSize = pre.ElementSize(
dimension = config["interpolation"]["dimension"],
L = config["geometry"]["L"],
order = config["interpolation"]["order"],
np = config["interpolation"]["np"],
)
Excluded = []
Mesh_object = pre.Mesh(
config["geometry"]["Name"], # Create the mesh object
MaxElemSize,
config["interpolation"]["order"],
config["interpolation"]["dimension"]
)
Mesh_object.AddBorders(config["Borders"]["Borders"])
Mesh_object.AddBCs( # Include Boundary physical domains infos (BCs+volume)
config["geometry"]["Volume_element"],
Excluded,
config["DirichletDictionryList"]
)
Mesh_object.MeshGeo() # Mesh the .geo file if .msh does not exist
Mesh_object.ReadMesh()
print(config["solver"]["IntegralMethod"])
print()
# Vtk file not necessary if not using reference element implementation
if config["solver"]["IntegralMethod"] == "Gaussian_quad":
Mesh_object.ExportMeshVtk1D()
# Build the assembly weight matrix if needed
if config["interpolation"]["dimension"] ==1 and config["solver"]["IntegralMethod"] == "Trapezoidal":
Mesh_object.AssemblyMatrix()
if int(Mesh_object.dim) != int(Mesh_object.dimension):
raise ValueError("The dimension of the provided geometry does not match the job dimension")
if config["solver"]["TrainingStrategy"]=="Integral":
match config["solver"]["IntegralMethod"]:
case "Gaussian_quad":
Model_FEM = MeshNN_1D(Mesh_object, config["interpolation"]["n_integr_points"])
case "Trapezoidal":
Model_FEM = MeshNN(Mesh_object)
if config["solver"]["TrainingStrategy"]=="Mixed":
if config["solver"]["IntegralMethod"] == "Gaussian_quad":
Model_FEM = MeshNN_1D(Mesh_object, config["interpolation"]["n_integr_points"])
Model_test = MeshNN_1D(Mesh_object, config["interpolation"]["n_integr_points"])
Model_test.Freeze_Mesh()
# Default setting
Model_FEM.Freeze_Mesh()
Model_FEM.UnFreeze_FEM()
if not config["solver"]["FrozenMesh"]:
Model_FEM.UnFreeze_Mesh()
if config["solver"]["TrainingStrategy"]=="Mixed":
Model_FEM = Training_1D_FEM_LBFGS(Model_FEM, config, Mat, Model_test)
else:
Model_FEM = Training_1D_FEM_LBFGS(Model_FEM, config, Mat)
loss_u_pe[res], loss_grad_pe[res] = Pplot.Normalized_error_1D(Model_FEM,config,Mat)
# Experiment setting: Weak formulation loss
# 4 mesh resolutions, 3-,4- and 5-point quadrature, fixed mesh
mesh_resolution_w = [10,21,41,80]
quadrature_points = [3,4,5]
loss_u_w = numpy.zeros((len(mesh_resolution_w),len(quadrature_points)))
loss_grad_w = numpy.zeros((len(mesh_resolution_w),len(quadrature_points)))
config["solver"]["TrainingStrategy"] = "Mixed"
config["solver"]["IntegralMethod"] = "Gaussian_quad"
config["solver"]["FrozenMesh"] = True
for res in range(len(mesh_resolution_w)):
config["interpolation"]["np"] = mesh_resolution_w[res]
for q in range(len(quadrature_points)):
config["interpolation"]["n_integr_points"] = quadrature_points[q]
# Load parameters
Mat = pre.Material( flag_lame = True, # If True should input lmbda and mu instead of E and nu
coef1 = config["material"]["E"], # Young Modulus
coef2 = config["material"]["A"] # Poisson's ratio
)
MaxElemSize = pre.ElementSize(
dimension = config["interpolation"]["dimension"],
L = config["geometry"]["L"],
order = config["interpolation"]["order"],
np = config["interpolation"]["np"],
)
Excluded = []
Mesh_object = pre.Mesh(
config["geometry"]["Name"], # Create the mesh object
MaxElemSize,
config["interpolation"]["order"],
config["interpolation"]["dimension"]
)
Mesh_object.AddBorders(config["Borders"]["Borders"])
Mesh_object.AddBCs( # Include Boundary physical domains infos (BCs+volume)
config["geometry"]["Volume_element"],
Excluded,
config["DirichletDictionryList"]
)
Mesh_object.MeshGeo() # Mesh the .geo file if .msh does not exist
Mesh_object.ReadMesh()
print(config["solver"]["IntegralMethod"])
print()
# Vtk file not necessary if not using reference element implementation
if config["solver"]["IntegralMethod"] == "Gaussian_quad":
Mesh_object.ExportMeshVtk1D()
# Build the assembly weight matrix if needed
if config["interpolation"]["dimension"] ==1 and config["solver"]["IntegralMethod"] == "Trapezoidal":
Mesh_object.AssemblyMatrix()
if int(Mesh_object.dim) != int(Mesh_object.dimension):
raise ValueError("The dimension of the provided geometry does not match the job dimension")
if config["solver"]["TrainingStrategy"]=="Integral":
match config["solver"]["IntegralMethod"]:
case "Gaussian_quad":
Model_FEM = MeshNN_1D(Mesh_object, config["interpolation"]["n_integr_points"])
case "Trapezoidal":
Model_FEM = MeshNN(Mesh_object)
if config["solver"]["TrainingStrategy"]=="Mixed":
if config["solver"]["IntegralMethod"] == "Gaussian_quad":
Model_FEM = MeshNN_1D(Mesh_object, config["interpolation"]["n_integr_points"])
Model_test = MeshNN_1D(Mesh_object, config["interpolation"]["n_integr_points"])
Model_test.Freeze_Mesh()
# Default setting
Model_FEM.Freeze_Mesh()
Model_FEM.UnFreeze_FEM()
if not config["solver"]["FrozenMesh"]:
Model_FEM.UnFreeze_Mesh()
if config["solver"]["TrainingStrategy"]=="Mixed":
Model_FEM = Training_1D_FEM_LBFGS(Model_FEM, config, Mat, Model_test)
else:
Model_FEM = Training_1D_FEM_LBFGS(Model_FEM, config, Mat)
loss_u_w[res,q], loss_grad_w[res,q] = Pplot.Normalized_error_1D(Model_FEM,config,Mat)
# Load default configuration file for resudal loss formulation = two independent models (defines dimension, domain, boundary conditions, number of training iterations etc.)
Default_config_file = 'Configurations/config_1D_Mixed.toml'
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-cf',type=str, help = 'path to the desired configuration file', default=Default_config_file, action = 'store')
args, unknown = parser.parse_known_args()
inputs = vars(args)
print(f"* Executing job in {args.cf}")
with open(args.cf, mode="rb") as f:
config = tomllib.load(f)
# Experiment setting: Resdiual loss function
# Tested variants: 5 mesh resolutions, 4 training sets, fixed mesh
mesh_resolution_r = [10,20,40,80,160] # cumulative number of mesh nodes and element mid-points
training_points = [10,25,50,75]
loss_u_r = numpy.zeros((len(mesh_resolution_r),len(training_points)))
loss_grad_r = numpy.zeros((len(mesh_resolution_r),len(training_points)))
config["solver"]["TrainingStrategy"] = "Mixed"
config["solver"]["IntegralMethod"] = "None"
config["solver"]["FrozenMesh"] = True
for res in range(len(mesh_resolution_r)):
config["interpolation"]["np"] = mesh_resolution_r[res]+mesh_resolution_r[res]-1
for q in range(len(training_points)):
config["training"]["Points_per_element"] = training_points[q]
#%% Initialise material
Mat = pre.Material( flag_lame = False, # If True should input lmbda and mu instead of E and nu
coef1 = config["material"]["E"], # Young Modulus
coef2 = config["material"]["nu"] # Poisson's ratio
)
#%% Create mesh object
# Definition of the (initial) element size of the mesh
MaxElemSize = pre.ElementSize(
dimension = config["interpolation"]["dimension"],
L = config["geometry"]["L"],
order = config["interpolation"]["order_u"],
np = config["interpolation"]["np"],
MaxElemSize2D = config["interpolation"]["MaxElemSize2D"]
)
Excluded = []
Mesh_object_u = pre.Mesh(
config["geometry"]["Name"], # Create the mesh object
MaxElemSize,
config["interpolation"]["order_u"],
config["interpolation"]["dimension"]
)
Mesh_object_u.AddBorders(config["Borders_u"]["Borders"])
Mesh_object_u.AddBCs( # Include Boundary physical domains infos (BCs+volume)
config["geometry"]["Volume_element"],
Excluded,
config["DirichletDictionryList_u"]
)
Mesh_object_u.MeshGeo() # Mesh the .geo file if .msh does not exist
Mesh_object_u.ReadMesh()
Mesh_object_u.AssemblyMatrix() # Build the assembly weight matrix
Mesh_object_du = pre.Mesh(
config["geometry"]["Name"], # Create the mesh object
MaxElemSize,
config["interpolation"]["order_du"],
config["interpolation"]["dimension"]
)
Mesh_object_du.AddBorders(config["Borders_du"]["Borders"])
Mesh_object_du.AddBCs( # Include Boundary physical domains infos (BCs+volume)
config["geometry"]["Volume_element"],
Excluded,
config["DirichletDictionryList_du"]
)
Mesh_object_du.MeshGeo() # Mesh the .geo file if .msh does not exist
Mesh_object_du.ReadMesh()
Mesh_object_du.AssemblyMatrix() # Build the assembly weight matrix
if int(Mesh_object_u.dim) != int(Mesh_object_u.dimension):
raise ValueError("The dimension of the provided geometry does not match the job dimension")
#%% Application of the Space HiDeNN
match config["interpolation"]["dimension"]:
case 1:
Model_FEM_u = MeshNN(Mesh_object_u)
Model_FEM_du = MeshNN(Mesh_object_du)
# Set the coordinates as untrainable
Model_FEM_u.Freeze_Mesh()
Model_FEM_du.Freeze_Mesh()
# Make nodal values trainable (except the BC). Default choice
Model_FEM_u.UnFreeze_FEM()
Model_FEM_du.UnFreeze_FEM()
if not config["solver"]["FrozenMesh"]:
Model_FEM_u.UnFreeze_Mesh()
Model_FEM_du.UnFreeze_Mesh()
Model_FEM_u, Model_FEM_du = Training_1D_Mixed_LBFGS(Model_FEM_u, Model_FEM_du, config, Mat)
loss_u_r[res,q], loss_grad_r[res,q] = Pplot.Normalized_error_1D(Model_FEM_u,config,Mat,Model_FEM_du)
import matplotlib.pyplot as plt
import matplotlib
plt.rcParams['text.usetex'] = False
# Plot normalized displacement error
fig = matplotlib.pyplot.gcf()
ax = plt.gca()
plt.plot(mesh_resolution_r, loss_u_r[:,0],'-', color = "cyan", label = 'Residual loss, 10 p./e.')
plt.plot(mesh_resolution_r, loss_u_r[:,1],'-', color = "darkblue", label = 'Residual loss, 25 p./e.')
plt.plot(mesh_resolution_r, loss_u_r[:,2],'-', color = "darkgreen", label = 'Residual loss, 50 p./e.')
plt.plot(mesh_resolution_r, loss_u_r[:,3],'-', color = "yellowgreen", label = 'Residual loss, 75 p./e.')
plt.plot(mesh_resolution_w, loss_u_w[:,0],'-', color = "plum", label = 'Weak eq. loss, G.q.(3)')
plt.plot(mesh_resolution_w, loss_u_w[:,1],'-', color = "pink", label = 'Weak eq. loss, G.q.(4)')
plt.plot(mesh_resolution_w, loss_u_w[:,1],'-', color = "indigo", label = 'Weak eq. loss, G.q.(5)')
plt.plot(mesh_resolution_pe, loss_u_pe,'--', color = "gray", label = 'Potential energy loss, G.q.(5)')
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_ylim([0.0011, 1.3])
plt.xlabel("Number of mesh nodes")
plt.ylabel("Normalized displacement error")
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5),frameon=False )
plt.show()
# Plot normalized strain error
fig = matplotlib.pyplot.gcf()
ax = plt.gca()
plt.plot(mesh_resolution_r, loss_grad_r[:,0],'-', color = "cyan", label = 'Residual loss, 10 p./e.')
plt.plot(mesh_resolution_r, loss_grad_r[:,1],'-', color = "darkblue", label = 'Residual loss, 25 p./e.')
plt.plot(mesh_resolution_r, loss_grad_r[:,2],'-', color = "darkgreen", label = 'Residual loss, 50 p./e.')
plt.plot(mesh_resolution_r, loss_grad_r[:,3],'-', color = "yellowgreen", label = 'Residual loss, 75 p./e.')
plt.plot(mesh_resolution_w, loss_grad_w[:,0],'-', color = "plum", label = 'Weak eq. loss, G.q.(3)')
plt.plot(mesh_resolution_w, loss_grad_w[:,1],'-', color = "pink", label = 'Weak eq. loss, G.q.(4)')
plt.plot(mesh_resolution_w, loss_grad_w[:,1],'-', color = "indigo", label = 'Weak eq. loss, G.q.(5)')
plt.plot(mesh_resolution_pe, loss_grad_pe,'--', color = "gray", label = 'Potential energy loss, G.q.(5)')
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_ylim([0.005, 1.1])
plt.xlabel("Number of mesh nodes")
plt.ylabel("Normalized strain error")
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5),frameon=False )
plt.show()