Example 5 - stripy smoothing operations#

SRFPACK is a Fortran 77 software package that constructs a smooth interpolatory or approximating surface to data values associated with arbitrarily distributed points. It employs automatically selected tension factors to preserve shape properties of the data and avoid overshoot and undershoot associated with steep gradients.

Here we demonstrate how to access SRFPACK smoothing through the stripy interface.

Notebook contents#

The next example is Ex6-Scattered-Data

Define a computational mesh#

Use the (usual) icosahedron with face points included.

import stripy as stripy

xmin = 0.0
xmax = 10.0
ymin = 0.0
ymax = 10.0
extent = [xmin, xmax, ymin, ymax]

spacingX = 0.2
spacingY = 0.2

mesh = stripy.cartesian_meshes.elliptical_mesh(extent, spacingX, spacingY, refinement_levels=1)
mesh = stripy.Triangulation(mesh.x, mesh.y, permute=True)
print(mesh.npoints)
1747

Analytic function with noise and short wavelengths#

Define a relatively smooth function that we can interpolate from the coarse mesh to the fine mesh and analyse

import numpy as np

def analytic(xs, ys, k1, k2):
     return  np.cos(k1*xs) * np.sin(k2*ys) 
    
def analytic_noisy(xs, ys, k1, k2, noise, short):
     return  np.cos(k1*xs) * np.sin(k2*ys) + \
             short * (np.cos(k1*5.0*xs) * np.sin(k2*5.0*ys)) + \
             noise * np.random.random(xs.shape)

# def analytic_ddlon(xs, ys, k1, k2):
#      return -k1 * np.sin(k1*xs) * np.sin(k2*ys) / np.cos(ys)

# def analytic_ddlat(xs, ys, k1, k2):
#      return k2 * np.cos(k1*xs) * np.cos(k2*ys) 


analytic_sol   = analytic(mesh.x, mesh.y, 0.1, 1.0)
analytic_sol_n = analytic_noisy(mesh.x, mesh.y, 0.1, 1.0, 0.2, 0.0)
%matplotlib inline
import matplotlib.pyplot as plt

def axis_mesh_field(fig, ax, mesh, field, label):

    ax.axis('off')

    x0 = mesh.x
    y0 = mesh.y
    
    trip = ax.tripcolor(x0, y0, mesh.simplices, field, cmap=plt.cm.RdBu)
    fig.colorbar(trip, ax=ax)
    
    ax.set_title(str(label))
    return

fig = plt.figure(figsize=(10, 8), facecolor="none")
ax = fig.add_subplot(111)
axis_mesh_field(fig, ax, mesh, analytic_sol-analytic_sol_n, "noisy solution")
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[3], line 1
----> 1 get_ipython().run_line_magic('matplotlib', 'inline')
      2 import matplotlib.pyplot as plt
      4 def axis_mesh_field(fig, ax, mesh, field, label):

File ~/.local/lib/python3.12/site-packages/IPython/core/interactiveshell.py:2482, in InteractiveShell.run_line_magic(self, magic_name, line, _stack_depth)
   2480     kwargs['local_ns'] = self.get_local_scope(stack_depth)
   2481 with self.builtin_trap:
-> 2482     result = fn(*args, **kwargs)
   2484 # The code below prevents the output from being displayed
   2485 # when using magics with decorator @output_can_be_silenced
   2486 # when the last Python token in the expression is a ';'.
   2487 if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):

File ~/.local/lib/python3.12/site-packages/IPython/core/magics/pylab.py:103, in PylabMagics.matplotlib(self, line)
     98     print(
     99         "Available matplotlib backends: %s"
    100         % _list_matplotlib_backends_and_gui_loops()
    101     )
    102 else:
--> 103     gui, backend = self.shell.enable_matplotlib(args.gui)
    104     self._show_matplotlib_backend(args.gui, backend)

File ~/.local/lib/python3.12/site-packages/IPython/core/interactiveshell.py:3667, in InteractiveShell.enable_matplotlib(self, gui)
   3664     import matplotlib_inline.backend_inline
   3666 from IPython.core import pylabtools as pt
-> 3667 gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select)
   3669 if gui != None:
   3670     # If we have our first gui selection, store it
   3671     if self.pylab_gui_select is None:

File ~/.local/lib/python3.12/site-packages/IPython/core/pylabtools.py:338, in find_gui_and_backend(gui, gui_select)
    321 def find_gui_and_backend(gui=None, gui_select=None):
    322     """Given a gui string return the gui and mpl backend.
    323 
    324     Parameters
   (...)
    335     'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg').
    336     """
--> 338     import matplotlib
    340     if _matplotlib_manages_backends():
    341         backend_registry = matplotlib.backends.registry.backend_registry

ModuleNotFoundError: No module named 'matplotlib'

Smoothing operations#

The Triangulation.smoothing method directly wraps the SRFPACK smoother that smooths a surface f described by values on the mesh vertices to find a new surface f’ (also described on the mesh vertices) by choosing nodal function values and gradients to minimize the linearized curvature of F subject to a bound on the deviation from the data values.

help(mesh.smoothing)
Help on method smoothing in module stripy.cartesian:

smoothing(f, w, sm, smtol, gstol, sigma=None) method of stripy.cartesian.Triangulation instance
    Smooths a surface `f` by choosing nodal function values and gradients to
    minimize the linearized curvature of F subject to a bound on the
    deviation from the data values. This is more appropriate than interpolation
    when significant errors are present in the data.

    Args:
        f : array of floats, shape (n,)
            field to apply smoothing on
        w : array of floats, shape (n,)
            weights associated with data value in `f`
            `w[i] = 1/sigma_f**2` is a good rule of thumb.
        sm : float
            positive parameter specifying an upper bound on Q2(f).
            generally `n-sqrt(2n) <= sm <= n+sqrt(2n)`
        smtol : float [0,1]
            specifies relative error in satisfying the constraint
            `sm(1-smtol) <= Q2 <= sm(1+smtol)` between 0 and 1.
        gstol : float
            tolerance for convergence.
            `gstol = 0.05*mean(sigma_f)**2` is a good rule of thumb.
        sigma : array of floats, shape (6n-12)
            precomputed array of spline tension factors from
            `get_spline_tension_factors(zdata, tol=1e-3, grad=None)`

    Returns:
        f_smooth : array of floats, shape (n,)
            smoothed version of f
        derivatives : tuple of floats, shape (n,3)
            \\( \partial f \partial y , \partial f \partial y \\) first derivatives
            of `f_smooth` in the x and y directions
        err : error indicator
            0 indicates no error, +ve values indicate warnings, -ve values are errors
stripy_smoothed,  dds, err = mesh.smoothing(analytic_sol_n, np.ones_like(analytic_sol_n), 10.0, 0.01, 0.01)
stripy_smoothed2, dds2, err = mesh.smoothing(analytic_sol_n, np.ones_like(analytic_sol_n), 1.0, 0.1, 0.01)
stripy_smoothed3, dds3, err = mesh.smoothing(analytic_sol_n, np.ones_like(analytic_sol_n), 20.0, 0.1, 0.01)

delta_n  = analytic_sol_n - stripy_smoothed
delta_ns = analytic_sol   - stripy_smoothed

delta_n2  = analytic_sol_n - stripy_smoothed2
delta_ns2 = analytic_sol   - stripy_smoothed2

delta_n3  = analytic_sol_n - stripy_smoothed3
delta_ns3 = analytic_sol   - stripy_smoothed3
stripy_smoothed, dds
(array([-0.6937222 , -0.6255765 , -0.74599534, ...,  0.13869102,
        -0.08533771, -0.18200916], shape=(1747,), dtype=float32),
 [array([ 0.05978034,  0.01304296,  0.0180961 , ..., -0.04763257,
         -0.02883827, -0.03573802], shape=(1747,), dtype=float32),
  array([ 0.24045323,  0.40880513,  0.07467127, ..., -0.4163194 ,
         -0.34768966, -0.2820406 ], shape=(1747,), dtype=float32)])

Results of smoothing with different value of sm#

fig, ax = plt.subplots(2,2, figsize=(12, 10), facecolor="none")

axis_mesh_field(fig, ax[0,0], mesh, analytic_sol_n, label="original")
axis_mesh_field(fig, ax[0,1], mesh, stripy_smoothed, label="smoothed1")
axis_mesh_field(fig, ax[1,0], mesh, stripy_smoothed2, label="smoothed2")
axis_mesh_field(fig, ax[1,1], mesh, stripy_smoothed3, label="smoothed3")

plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[7], line 1
----> 1 fig, ax = plt.subplots(2,2, figsize=(12, 10), facecolor="none")
      3 axis_mesh_field(fig, ax[0,0], mesh, analytic_sol_n, label="original")
      4 axis_mesh_field(fig, ax[0,1], mesh, stripy_smoothed, label="smoothed1")

NameError: name 'plt' is not defined

The next example is Ex6-Scattered-Data

fig, ax = plt.subplots(2,2, figsize=(12,10), facecolor="none")

axis_mesh_field(fig, ax[0,0], mesh, analytic_sol_n, label="original")
axis_mesh_field(fig, ax[0,1], mesh, dds[1], label="smoothed1")
axis_mesh_field(fig, ax[1,0], mesh, dds2[1], label="smoothed2")
axis_mesh_field(fig, ax[1,1], mesh, dds3[1], label="smoothed3")

plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[8], line 1
----> 1 fig, ax = plt.subplots(2,2, figsize=(12,10), facecolor="none")
      3 axis_mesh_field(fig, ax[0,0], mesh, analytic_sol_n, label="original")
      4 axis_mesh_field(fig, ax[0,1], mesh, dds[1], label="smoothed1")

NameError: name 'plt' is not defined

The next notebook is Ex6-Scattered-Data