Compute the L2 norm of a Field

Hi!

We have a Field with index parameter in 2D and scalar value and we want to compute its L^2 norm:

\|X\|_{L^2}^2 = \int_\mathcal{D} X(\omega,{\bf t})^2 d{\bf t}

Is there a method in OT to compute this? Otherwise, it is necessary to take into account the function values and the volume of the vertices with Mesh.computeWeights(), which is doable more requires some computations.

Best regards,

Michaël

It is quite tricky. I can see several options:

  • Approximate the true squared L^2 norm using a quadrature rule in the spirit of the midpoint rule (it is exactly this rule for 1D meshes). For that, use mesh.computeWeights()
  • Compute the the exact squared L^2 norm of a piecewise interpolation of the field given its values at the vertices. For that, use mesh.computeP1Gram()

Here is a script to compare these options, as well as reference values obtained using adaptive quadratures of the model or the P_1 interpolation:

import openturns as ot

integrator = ot.GaussKronrod(65536, 1e-15, ot.GaussKronrodRule(ot.GaussKronrodRule.G7K15))

# The reference function
model = ot.SymbolicFunction("x", "sin(x+x^2/4)")
# An helper function
square = ot.SymbolicFunction("x", "x^2")

N = 10
domain = ot.Interval(-2.0, 2.0)
mesh = ot.IntervalMesher([N]).build(domain)

# Here we deform the mesh in order to have a nonuniform sampling of the domain
deform = ot.SymbolicFunction("x", "x+0.1*sin(pi_*x)")
mesh.setVertices(deform(mesh.getVertices()))

# g = mesh.draw()
# g.add(model.draw(domain.getLowerBound(), domain.getUpperBound()))
# Show(g)

###################################################
# Exact squared L2 norm using adaptive quadrature #
###################################################
refL2NormSquared = integrator.integrate(ot.ComposedFunction(square, model), domain)[0]
print("refL2NormSquared   =%.12g" % refL2NormSquared, "rel. err=%.4e" % abs(1-refL2NormSquared/refL2NormSquared))
##############################################################################
# Mid-point approximation of the true squared L2 norm using the mesh weights #
##############################################################################
values = model(mesh.getVertices())
field = ot.Field(mesh, values)
fieldSquared = ot.ValueFunction(square, mesh)(field)
weightL2NormSquared = fieldSquared.asPoint().dot(mesh.computeWeights())
print("weightL2NormSquared=%.12g" % weightL2NormSquared, "rel. err=%.4e" % abs(1-weightL2NormSquared/refL2NormSquared))
########################################################################
# Exact squared L2 norm of a P1 interpolation using the P1 Gram matrix #
########################################################################
gram = mesh.computeP1Gram()
gramL2NormSquared = values.asPoint().dot(gram*values.asPoint())
print("gramL2NormSquared  =%.12g" % gramL2NormSquared, "rel. err=%.4e" % abs(1-gramL2NormSquared/refL2NormSquared))
#########################################################################
# Exact squared L2 norm of a P1 interpolation using adaptive quadrature #
#########################################################################
myFieldSquaredFunction = ot.ComposedFunction(square, ot.Function(ot.P1LagrangeEvaluation(field)))
quadL2NormSquared = integrator.integrate(myFieldSquaredFunction, domain)[0]
print("quadL2NormSquared  =%.12g" % quadL2NormSquared, "rel. err=%.4e" % abs(1-quadL2NormSquared/refL2NormSquared))

I get the following results:

refL2NormSquared   =1.82317483123 rel. err=0.0000e+00
weightL2NormSquared=1.84393295915 rel. err=1.1386e-02
gramL2NormSquared  =1.74962050461 rel. err=4.0344e-02
quadL2NormSquared  =1.74962050461 rel. err=4.0344e-02

As you can see, there are several ways to do the job. The cheapest option (and here, the most accurate one) is based on the mesh weights. If your mesh has n vertices in dimension d and m simplexes, it needs a storage of size n for a computational cost \mathcal{O}(md). The option based on the Gram matrix needs a storage of size n^2 (no sparse representation of the Gram matrix) and a computational cost \mathcal{O}(md^2). Note that this last option is essentially a one-liner.

1 Like

thanks, I’m coding the norm for the mesh weights then