Hello,
I already posted before on this forum about interpolation issues with N1curl elements. I received advice that worked well for homogeneous fields, but I was not able to get the same precision with dipolar fields.
Using the same mesh, I have interpolated a homogeneous potential field and the associated magnetic field to a very satisfactory precision using both N1curl and Lagrange elements.
When interpolating homogeneous field potential in N1curl
L2 norm of A_nedelec_DG - A_DG = 3.21e-17
L2 norm of B_nedelec_DG - B_DG = 3.21e-17
When interpolating homogeneous field potential in Lagrange
L2 norm of A_lagrange_DG - A_DG = 8.13e-19
L2 norm of B_lagrange_DG - B_DG = 8.13e-19
However, the same method did not work for a dipolar field.
When interpolating dipole potential in N1curl
L2 norm of A_nedelec_DG - A_DG = 1.06e-02
L2 norm of B_nedelec_DG - B_DG = 1.06e-02
When interpolating dipole potential in Lagrange
L2 norm of A_lagrange_DG - A_DG = 1.22e-17
L2 norm of B_lagrange_DG - B_DG = 1.22e-17
Is there anything I am missing regarding N1curl interpolation ? Why is interpolation so bad for the dipole field/potential ?
I have put the errors I get with homogenous field potentials.
Here is a (fairly long) MWE exploring my interpolation process, for clarity :
A_DG : Potential interpolated from numpy, into DG
A_nedelec_DG : Potential interpolated from numpy, into N1curl, into DG
A_lagrange_DG : Potential interpolated from numpy, into Lagrange, into DG
B_DG : Field interpolated from numpy, into DG
B_nedelec_DG : Field interpolated from numpy, into N1curl, into DG
B_lagrange_DG : Field interpolated from numpy, into Lagrange, into DG
curl_A_nedelec_DG: curl of the potential, interpolated from numpy, into N1curl, into DG
"""MWE highlighting my issues with Nédélec interpolation."""
import gmsh
import numpy as np
import pandas as pd
import dolfinx
from dolfinx.fem import FunctionSpace, VectorFunctionSpace, Function
from mpi4py import MPI
import ufl
VACUUM_PERMEABILITY = 4 * np.pi * 1e-7
DIPOLE_MU = np.array([0, 0, 1])
DIPOLE_POSITION = np.array([0, 0, 0])
def generate_mesh():
"""Generate empty domain."""
bkg_tag = 0
sphere_tag = 1
# Mesh definition
gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 0)
gmsh.model.add("MWE")
gmsh.model.occ.addSphere(0, 0, 0, 1, tag=bkg_tag)
gmsh.model.occ.addSphere(0, 0, 0, 1e-1, tag=sphere_tag)
gmsh.model.occ.synchronize()
gmsh.model.addPhysicalGroup(3, [1], tag=bkg_tag)
gmsh.model.addPhysicalGroup(3, [2], tag=sphere_tag)
gmsh.option.setNumber("Mesh.Algorithm", 2)
gmsh.model.mesh.generate(3)
gmsh.model.mesh.refine()
gmsh.model.mesh.optimize("Netgen")
# gmsh.fltk.run()
mesh, _, _ = dolfinx.io.gmshio.model_to_mesh(gmsh.model, MPI.COMM_WORLD, 0, gdim=3)
gmsh.finalize()
return mesh
def numpy_potential():
"""Get numpy function for dipolar potential field."""
def background_potential(x):
"""Return dipolar potential."""
temp_mu_dipole = np.einsum("n,d->dn", np.ones(x.shape[1]), DIPOLE_MU)
temp_position = np.einsum("n,d->dn", np.ones(x.shape[1]), DIPOLE_POSITION)
r_dipole = x - temp_position
norm_r_dipole = np.linalg.norm(r_dipole, axis=0)
produit_vect = np.cross(temp_mu_dipole, r_dipole, axis=0)
# Potential expression obtained from https://en.wikipedia.org/wiki/Magnetic_dipole
bkg_potential = (
VACUUM_PERMEABILITY * produit_vect / (4 * np.pi * norm_r_dipole**3)
)
return bkg_potential
return background_potential
def numpy_to_nedelec(mesh, numpy_potential):
"""Nedelec expression from numpy."""
# V = FunctionSpace(mesh, ("N1curl", 1))
V = VectorFunctionSpace(mesh, ("Lagrange", 1))
A_nedelec = Function(V)
A_nedelec.interpolate(numpy_potential())
A_nedelec.x.scatter_forward()
return A_nedelec
def numpy_to_DG(mesh, numpy_potential):
"""DG expression from numpy."""
V_DG = VectorFunctionSpace(mesh, ("DG", 1))
A_DG = Function(V_DG)
A_DG.interpolate(numpy_potential())
A_DG.x.scatter_forward()
return A_DG, V_DG
mesh = generate_mesh()
background_potential = numpy_potential()
A_nedelec = numpy_to_nedelec(mesh, numpy_potential)
A_DG, V_DG = numpy_to_DG(mesh, numpy_potential)
comm = V_DG.mesh.comm
A_error = Function(V_DG)
A_nedelec_DG = Function(V_DG)
A_nedelec_DG.interpolate(A_nedelec)
A_error.x.array[:] = A_nedelec_DG.x.array - A_DG.x.array
error_L2 = dolfinx.fem.form(ufl.inner(A_error, A_error) * ufl.dx)
print(
f"L2 norm of A_nedelec_DG - A_DG = {np.real(np.sqrt(comm.allreduce(dolfinx.fem.assemble_scalar(error_L2), MPI.SUM))):.2e}"
)
####################################################################################################
####################################################################################################
curl_A_DG = Function(V_DG)
curl_A_DG.interpolate(
dolfinx.fem.Expression(
ufl.as_vector(
(
A_DG.sub(2).dx(1) - A_DG.sub(1).dx(2), # Dz/Dy - Dy/Dz
A_DG.sub(0).dx(2) - A_DG.sub(2).dx(0), # Dx/Dz - Dz/Dx
A_DG.sub(1).dx(0) - A_DG.sub(0).dx(1), # Dy/Dx - Dx/Dy
)
),
V_DG.element.interpolation_points(),
)
)
curl_A_nedelec_DG = Function(V_DG)
curl_A_nedelec_DG.interpolate(
dolfinx.fem.Expression(
ufl.as_vector(
(
A_nedelec_DG.sub(2).dx(1) - A_nedelec_DG.sub(1).dx(2), # Dz/Dy - Dy/Dz
A_nedelec_DG.sub(0).dx(2) - A_nedelec_DG.sub(2).dx(0), # Dx/Dz - Dz/Dx
A_nedelec_DG.sub(1).dx(0) - A_nedelec_DG.sub(0).dx(1), # Dy/Dx - Dx/Dy
)
),
V_DG.element.interpolation_points(),
)
)
curl_error = Function(V_DG)
curl_error.x.array[:] = curl_A_nedelec_DG.x.array - curl_A_DG.x.array
curl_error_L2 = dolfinx.fem.form(ufl.inner(curl_error, curl_error) * ufl.dx)
print(
f"L2 norm of curl_A_nedelec_DG - curl_A_DG = {np.real(np.sqrt(comm.allreduce(dolfinx.fem.assemble_scalar(curl_error_L2), MPI.SUM))):.2e}"
)
def numpy_field():
"""Get numpy function for dipolar magnetic field."""
def background_field(x):
"""Return dipolar field."""
######################################### DIPOLE #########################################
temp_mu_dipole = np.einsum("n,d->dn", np.ones(x.shape[1]), DIPOLE_MU)
temp_position = np.einsum("n,d->dn", np.ones(x.shape[1]), DIPOLE_POSITION)
r_dipole = x - temp_position
norm_r_dipole = np.linalg.norm(r_dipole, axis=0)
dot_product = np.einsum("dn,dn->n", temp_mu_dipole, r_dipole)
bkg_field = (VACUUM_PERMEABILITY / (4 * np.pi * norm_r_dipole**3)) * (
(3 * r_dipole * dot_product / norm_r_dipole**2) - temp_mu_dipole
)
return bkg_field
return background_field
background_field = numpy_field()
B_nedelec = numpy_to_nedelec(mesh, numpy_potential)
B_DG, _ = numpy_to_DG(mesh, numpy_potential)
comm = V_DG.mesh.comm
B_error = Function(V_DG)
B_nedelec_DG = Function(V_DG)
B_nedelec_DG.interpolate(B_nedelec)
B_error.x.array[:] = B_nedelec_DG.x.array - B_DG.x.array
error_L2 = dolfinx.fem.form(ufl.inner(B_error, B_error) * ufl.dx)
print(
f"L2 norm of B_nedelec_DG - B_DG = {np.real(np.sqrt(comm.allreduce(dolfinx.fem.assemble_scalar(error_L2), MPI.SUM))):.2e}"
)
####################################################################################################
B_error.x.array[:] = B_nedelec_DG.x.array - curl_A_nedelec_DG.x.array
error_L2 = dolfinx.fem.form(ufl.inner(B_error, B_error) * ufl.dx)
print(
f"L2 norm of B_nedelec_DG - curl_A_nedelec_DG = {np.real(np.sqrt(comm.allreduce(dolfinx.fem.assemble_scalar(error_L2), MPI.SUM))):.2e}"
)
B_error.x.array[:] = B_DG.x.array - curl_A_DG.x.array
error_L2 = dolfinx.fem.form(ufl.inner(B_error, B_error) * ufl.dx)
print(
f"L2 norm of B_DG - curl_A_DG = {np.real(np.sqrt(comm.allreduce(dolfinx.fem.assemble_scalar(error_L2), MPI.SUM))):.2e}"
)
## When interpolating dipole potential in N1curl
# L2 norm of A_nedelec_DG - A_DG = 1.06e-02 <- why is this error so high
# L2 norm of B_nedelec_DG - B_DG = 1.06e-02 <- why is this error so high
# L2 norm of curl_A_nedelec_DG - curl_A_DG = 5.50e-01
# L2 norm of B_nedelec_DG - curl_A_nedelec_DG = 7.20e-03
# L2 norm of B_DG - curl_A_DG = 5.54e-01
## When interpolating dipole potential in Lagrange
# L2 norm of A_lagrange_DG - A_DG = 1.22e-17
# L2 norm of B_lagrange_DG - B_DG = 1.22e-17
# L2 norm of curl_A_lagrange_DG - curl_A_DG = 6.12e-16
# L2 norm of B_lagrange_DG - curl_A_lagrange_DG = 5.54e-01
# L2 norm of B_DG - curl_A_DG = 5.54e-01
## When interpolating homogeneous field potential in N1curl
# L2 norm of A_nedelec_DG - A_DG = 3.21e-17
# L2 norm of B_nedelec_DG - B_DG = 3.21e-17
# L2 norm of curl_A_nedelec_DG - curl_A_DG = 1.22e-16
# L2 norm of B_nedelec_DG - curl_A_nedelec_DG = 2.01e-01 <- is curl introducing big errors ?
# L2 norm of B_DG - curl_A_DG = 2.01e-01 <- is curl introducing big errors ?
## When interpolating homogeneous field potential in Lagrange
# L2 norm of A_lagrange_DG - A_DG = 8.13e-19
# L2 norm of B_lagrange_DG - B_DG = 8.13e-19
# L2 norm of curl_A_lagrange_DG - curl_A_DG = 3.61e-17
# L2 norm of B_lagrange_DG - curl_A_lagrange_DG = 2.01e-01 <- is curl introducing big errors ?
# L2 norm of B_DG - curl_A_DG = 2.01e-01 <- is curl introducing big errors ?
Any help is appreciated ! Thank you