- Thank you received: 0
Issues with Gridfunction generated from IntegrationRuleSpace method
2 years 6 months ago #4388
by s.mokbel
Hello,
I would like to use IntegrationRuleSpace to project data from a numpy array back into a Gridfunction. To test this, I have a stationary Navier Stokes example on a unit square, adapted from one of the ngsolve tutorials.
In this code, I am:
1. running Newtons method until I achieve a tolerance of 1e-13.
2. taking that low-tolerance solution, and projecting it to a uniform numpy array
3. Putting this numpy array back into a Gridfunction using the IntegrationRuleSpace method
4. Running Newtons method on this interpolated Gridfunction (I expect the tolerance to be close to 1e-13 and for the method to stop after 1 iteration. However, the tolerance is very high, around 1e-2).
While the results look good visually, the projected gfu and original gfu are very different. Why is the solution so different if I'm projecting from the same, low-tolerance gridfunction?
The attached file is ready to run. The tolerance will be printed out in the terminal for both cases. Additionally, you can uncomment lines 232-239 to visualize the numpy arrays. By default, the code will save two VTU files: The original, <1e-13 Gridfunction, and its projected Gridfunction. These two results look very similar yet produce very different results in the solver.
I would like to use IntegrationRuleSpace to project data from a numpy array back into a Gridfunction. To test this, I have a stationary Navier Stokes example on a unit square, adapted from one of the ngsolve tutorials.
In this code, I am:
1. running Newtons method until I achieve a tolerance of 1e-13.
2. taking that low-tolerance solution, and projecting it to a uniform numpy array
3. Putting this numpy array back into a Gridfunction using the IntegrationRuleSpace method
4. Running Newtons method on this interpolated Gridfunction (I expect the tolerance to be close to 1e-13 and for the method to stop after 1 iteration. However, the tolerance is very high, around 1e-2).
While the results look good visually, the projected gfu and original gfu are very different. Why is the solution so different if I'm projecting from the same, low-tolerance gridfunction?
The attached file is ready to run. The tolerance will be printed out in the terminal for both cases. Additionally, you can uncomment lines 232-239 to visualize the numpy arrays. By default, the code will save two VTU files: The original, <1e-13 Gridfunction, and its projected Gridfunction. These two results look very similar yet produce very different results in the solver.
2 years 6 months ago #4389
by s.mokbel
Replied by s.mokbel on topic Issues with Gridfunction generated from IntegrationRuleSpace method
Code:
from netgen import gui
from netgen.geom2d import unit_square
from ngsolve import *
import numpy as np
from ngsolve.comp import IntegrationRuleSpace
from ngsolve.solvers import *
import seaborn as sns
import matplotlib.pyplot as plt
mesh = Mesh(unit_square.GenerateMesh(maxh=0.05))
def SimpleNewtonSolve(gfu,a,tol=1e-13,maxits=25):
res = gfu.vec.CreateVector()
du = gfu.vec.CreateVector()
fes = gfu.space
for it in range(maxits):
print ("Iteration {:3} ".format(it),end="")
a.Apply(gfu.vec, res)
a.AssembleLinearization(gfu.vec)
du.data = a.mat.Inverse(fes.FreeDofs()) * res
gfu.vec.data -= du
#stopping criteria
stopcritval = sqrt(abs(InnerProduct(du,res)))
print ("<A u",it,", A u",it,">_{-1}^0.5 = ", stopcritval)
if stopcritval < tol:
break
mesh = Mesh(unit_square.GenerateMesh(maxh=0.05)); nu = Parameter(1)
V = VectorH1(mesh,order=3,dirichlet="bottom|right|top|left")
Q = H1(mesh,order=2);
N = NumberSpace(mesh);
X = FESpace([V,Q,N])
(u,p,lam), (v,q,mu) = X.TnT()
a = BilinearForm(X)
a += (nu*InnerProduct(grad(u),grad(v))+InnerProduct(grad(u)*u,v)
-div(u)*q-div(v)*p-lam*q-mu*p)*dx
gfu = GridFunction(X)
gfu.components[0].Set(CoefficientFunction((4*x*(1-x),0)),
definedon=mesh.Boundaries("top"))
SimpleNewtonSolve(gfu,a)
vtk = VTKOutput(ma=mesh,
coefs=[gfu.components[0], gfu.components[1]],
names = ["u", "p"],
filename="original_gfu",
subdivision=3)
# Exporting the results:
vtk.Do()
# Project to uniform Grid
def create_np_data(gfu: GridFunction, mesh: Mesh, n: int, m: int):
"""
Function to create numpy array from Gridfunction data.
Args:
gfu_prof_components: The original gridfunction to load the values into.
mesh: The mesh used in this simulation.
n: Desired number of discrete cells (elements) in the uniform grid (numpy array)
in the x direction.
m: Desired number of discrete cells (elements) in the uniform grid (numpy array)
in the y direction.
sol_path_str: Optional directory string for the .sol file.
Returns:
Numpy array of field data Ux, Uy, P.
"""
# Obtain mesh dimensions from vertices.
vertices = mesh.vertices
v1, v3 = vertices[0], vertices[2]
x0, xn, y0, yn = v1.point[0], v3.point[0], v1.point[1], v3.point[1]
# Initialize numpy arrays for field data.
output_Ux = np.zeros((n,m))
output_Uy = np.zeros((n,m))
output_P = np.zeros((n,m))
output_fields = np.empty((1, 3, n, m))
# Create uniform grid points based on mesh dimensions.
x_interp = np.linspace(x0,xn,m)
y_interp = np.linspace(y0,yn,n)
gfu_comp = gfu.components
x_idx = 0
for p1 in x_interp:
y_idx = 0
for p2 in y_interp:
try:
val_Ux = gfu_comp[0](mesh(p1,p2))[0]
val_Uy = gfu_comp[0](mesh(p1, p2))[1]
val_P = gfu_comp[1](mesh(p1, p2))
output_P[y_idx, x_idx] = val_P
output_Ux[y_idx, x_idx] = val_Ux
output_Uy[y_idx, x_idx] = val_Uy
except Exception:
# Catch any points that might not be in the mesh,
# and set their values to an arbitrary number
output_P[y_idx, x_idx] = -10
output_Ux[y_idx, x_idx] = -10
output_Uy[y_idx, x_idx] = -10
y_idx += 1
x_idx += 1
output_fields[0,0], output_fields[0,1], output_fields[0,2] = output_Ux, output_Uy, output_P
return output_fields
def numpy_to_gfu(np_data: np.array,gfu_init: GridFunction, mesh: Mesh, n: int, m: int):
"""
Function to create gridfunction from numpy array.
Arguments:
np_data: Numpy array containing projected low-tol solution
gfu_init: The initial gridfunction where the simulation last left off.
mesh: Corresponding mesh
n: Desired number of discrete cells (elements) in the uniform grid (numpy array)
in the x direction.
m: Desired number of discrete cells (elements) in the uniform grid (numpy array)
in the y direction.
"""
interp_ord = 3
for idx in range(2): # Looping through both components -> u and p
# Get FES - same finite element spaces as defined above.
if idx == 0:
X = VectorH1(mesh, order=3, dirichlet="bottom|right|top|left")
else:
X = H1(mesh, order=2)
interp_ord -= 1
# Get the dimensions of the problem.
if len(gfu_init.components) > 0:
# Get dimension of field.
dim = gfu_init.components[idx].dim
else:
dim = gfu_init.dim
if dim >= 2:
fesir = IntegrationRuleSpace(mesh=mesh, order=interp_ord) ** dim
fesir_irs = IntegrationRuleSpace(mesh=mesh, order=interp_ord)
else:
fesir = IntegrationRuleSpace(mesh=mesh, order=interp_ord)
fesir_irs = fesir
# Working with 2d data - the fesir coordinate data always has dimension 2.
fesir_coor = IntegrationRuleSpace(mesh=mesh, order=interp_ord) ** 2
# Create IRS Gridfunction for data and corresponding coordinates.
gfuir = GridFunction(fesir)
gfuir_coor = GridFunction(fesir_coor)
# Interpolate point-values in integration points.
if len(gfu_init.components) > 0:
gfuir.Interpolate(gfu_init.components[idx])
else:
gfuir.Interpolate(gfu_init)
gfuir_coor.Interpolate(CF((x, y)))
vertices = mesh.vertices
v1, v3 = vertices[0], vertices[2]
x0, xn, y0, yn = v1.point[0], v3.point[0], v1.point[1], v3.point[1]
########################################################
# Reverse interpolate #
########################################################
# Create uniform grid points based on mesh dimensions.
x_interp = np.linspace(x0, xn, m)
y_interp = np.linspace(y0, yn, n)
# Uniform indices.
x_ind = np.arange(m)
y_ind = np.arange(n)
# Coordinates from IntegrationRuleSpace.
coord_x = gfuir_coor.components[0]
coord_y = gfuir_coor.components[1]
for i in range(len(coord_x.vec)):
p1 = coord_x.vec
[i]p2 = coord_y.vec
[i]# Get corresponding indices.
x_idx = int(np.interp(p1, x_interp, x_ind))
y_idx = int(np.interp(p2, y_interp, y_ind))
# Fill gfuir data with numpy data.
if dim >= 2:
# Since this is for 2D problems, we are only considering 2 components.
gfuir.components[0].vec[i] = np_data[0][y_idx, x_idx]
gfuir.components[1].vec[i] = np_data[1][y_idx, x_idx]
else:
gfuir.vec[i] = np_data[2][y_idx, x_idx]
# Get integration rules.
irs = fesir_irs.GetIntegrationRules()
fes = X
p, q = fes.TnT()
mass = BilinearForm(p * q * dx).Assemble().mat
invmass = mass.Inverse(inverse="sparsecholesky")
rhs = LinearForm(gfuir * q * dx(intrules=irs))
rhs.Assemble()
gfu_init.components[idx].vec.data = invmass * rhs.vec
idx += 1
return gfu_init
n = 256
m = 256
# Create projected numpy arrays containing solutions.
np_data = create_np_data(gfu=gfu, mesh=mesh, n=n, m=m)
# Uncomment to view numpy arrays:
# vis = sns.heatmap(np_data[0][0], vmin=np.min(np_data[0][0]), vmax=np.max(np_data[0][0]))
# plt.show()
#
# vis = sns.heatmap(np_data[0][1], vmin=np.min(np_data[0][1]), vmax=np.max(np_data[0][1]))
# plt.show()
#
# vis = sns.heatmap(np_data[0][2], vmin=np.min(np_data[0][2]), vmax=np.max(np_data[0][2]))
# plt.show()
gfu_from_np = numpy_to_gfu(np_data[0], gfu, mesh, n, m)
vtk = VTKOutput(ma=mesh,
coefs=[gfu_from_np.components[0], gfu_from_np.components[1]],
names = ["u", "p"],
filename="projected_gfu",
subdivision=3)
# Exporting the results:
vtk.Do()
print(" NEW SOLVE .......")
# Tolerance is still high, even though the data is coming from a low-tolerance solution
print("Tolerance is still high, even though we're using the low-tolerance interpolated gfu")
SimpleNewtonSolve(gfu_from_np,a)[/i][/i][/i][/i][/i]
Time to create page: 0.101 seconds