@@ -1421,109 +1421,162 @@ \subsection{Functions of \Data objects}
14211421
14221422
14231423\subsection {Interpolating Data }
1424- \index {interpolateTable }
1424+ \index {InterpolationTable }
14251425\label {sec:interpolation }
1426- In some cases, it may be useful to produce Data objects which fit some user
1427- defined function.
1428- Manually modifying each value in the Data object is not a good idea since it
1429- depends on knowing the location and order of each data point in the domain.
1430- Instead, \escript can use an interpolation table to produce a \Data object .
1426+ In some cases it is useful to produce \ Data objects which approximate a
1427+ user- defined function without accessing individual data points directly .
1428+ \escript provides the \class {InterpolationTable} class for this purpose.
1429+ It performs order-1 (linear) or order-0 (piecewise constant) interpolation
1430+ from a regular-grid lookup table onto a mesh .
14311431
1432- The following example is available as \file {int_save.py} in the \ExampleDirectory .
1433- We will produce a \Data object which approximates a sine curve.
1432+ The following examples are available as \file {int\_ save.py} in the
1433+ \ExampleDirectory .
1434+
1435+ \subsubsection {Setting up the domain }
14341436
14351437\begin {python }
1436- from esys.escript import saveDataCSV, sup, interpolateTable
1437- import numpy
1438- from esys.finley import Rectangle
1438+ from esys.escript import InterpolationTable, saveDataCSV, sup
1439+ import numpy
1440+ from esys.finley import Rectangle, Brick
1441+
1442+ n = 4
1443+ r = Rectangle(n, n)
1444+ x = r.getX() # Data with shape (2,)
1445+ toobig = 100 # RuntimeError if any interpolated value exceeds this
1446+ \end {python }
1447+
1448+ \subsubsection {1-D interpolation (order-1) }
1449+
1450+ We approximate a sine curve using a nine-point table covering $ [0 ,1 ]$ :
14391451
1440- n=4
1441- r=Rectangle(n,n)
1442- x=r.getX()
1443- toobig=100
1452+ \begin {python }
1453+ sine_table = numpy.array([0, 0.70710678, 1, 0.70710678, 0,
1454+ -0.70710678, -1, -0.70710678, 0])
1455+ minval, maxval = 0., 1.
1456+ # n-1 intervals between n sample points
1457+ step = (maxval - minval) / (len(sine_table) - 1)
1458+
1459+ interp = InterpolationTable(sine_table, origin=minval, step=step,
1460+ order=1, undef=toobig)
1461+ result = interp(x[0]) # x[0] has shape ()
14441462\end {python }
14451463
1446- \noindent First we produce an interpolation table:
1464+ \noindent Alternatively, the domain extent can be given directly with
1465+ \var {extend} instead of \var {step}:
1466+
14471467\begin {python }
1448- sine_table=[0, 0.70710678118654746, 1, 0.70710678118654746, 0 ,
1449- -0.70710678118654746, - 1, -0.70710678118654746, 0]
1468+ interp = InterpolationTable( sine_table, origin=minval ,
1469+ extend=(maxval - minval), order= 1, undef=toobig)
14501470\end {python }
1451- %
1452- We wish to identify $ 0 $ and $ 1 $ with the ends of the curve, that is
1453- with the first and eighth value in the table.
1471+
1472+ \noindent By default, values outside the table range are clamped to the
1473+ nearest boundary entry. Passing \code {check\_ boundaries=True} raises a
1474+ \exception {RuntimeError} instead.
1475+
1476+ \subsubsection {1-D interpolation (order-0, cell-centred) }
1477+
1478+ Order-0 (piecewise constant) uses a cell-centred convention: cell $ i$
1479+ covers $ [\var {origin}+i\cdot \var {step},\; \var {origin}+(i+1 )\cdot \var {step})$
1480+ with value \code {table[i]}.
1481+ The domain therefore spans $ [\var {origin},\; \var {origin}+n\cdot \var {step}]$
1482+ where $ n$ is the length of the table, and \code {extend} gives that total width
1483+ directly:
14541484
14551485\begin {python }
1456- numslices=len(sine_table)-1
1457- minval=0.
1458- maxval=1.
1459- step=sup(maxval-minval)/numslices
1486+ interp0 = InterpolationTable(sine_table, origin=minval,
1487+ extend=(maxval - minval), order=0, undef=toobig)
1488+ result0 = interp0(x[0])
14601489\end {python }
1461- %
1462- So the values $ v$ from the input lie in the interval
1463- \var {minval} $ \leq v <$ \var {maxval}.
1464- \var {step} represents the gap (in the input range) between entries in the table.
1465- By default, values of $ v$ outside the table argument range (minval, maxval)
1466- will be pushed back into the range, i.e. if $ v <$ \var {minval} the value
1467- \var {minval} will be used to evaluate the table.
1468- Similarly, for values $ v>$ \var {maxval} the value \var {maxval} is used.
14691490
1470- Now we produce our new \Data object:
1491+ \subsubsection {Table indexing convention }
1492+ \label {sec:table_indexing }
1493+
1494+ \class {InterpolationTable} supports two table layouts selected by the
1495+ \var {table\_ indexing} keyword argument:
1496+
1497+ \begin {description }
1498+ \item [\code {"ijk"} (default)] The \emph {first } numpy index runs along the
1499+ $ x$ -axis (first coordinate), the second along $ y$ , the third along $ z$ .
1500+ A 2-D table has shape \code {(nx, ny)}, a 3-D table \code {(nx, ny, nz)}.
1501+ This is the natural numpy row-major order for spatial data indexed by
1502+ coordinate.
1503+ \item [\code {"kji"}] The \emph {first } numpy index runs along the $ z$ -axis,
1504+ the second along $ y$ , the third along $ x$ .
1505+ A 2-D table has shape \code {(ny, nx)}, a 3-D table \code {(nz, ny, nx)}.
1506+ This matches the convention used by the legacy \function {interpolateTable}
1507+ function.
1508+ \end {description }
1509+
1510+ \subsubsection {2-D interpolation }
1511+
1512+ We interpolate from a plane where $ (x,y)\mapsto x + y\cdot 10 $ for
1513+ $ x,y\in [0 ,9 ]$ .
1514+ Using the default \code {"ijk"} convention the table has shape
1515+ \code {(nx, ny)} with \code {table[ix, iy]} holding the value at grid
1516+ point $ (x_{\min }+\var {ix}\cdot \var {dx},\; y_{\min }+\var {iy}\cdot \var {dy})$ :
14711517
14721518\begin {python }
1473- result=interpolateTable(sine_table, x[0], minval, step, toobig)
1519+ nx = ny = 10
1520+ xstep = ystep = (maxval - minval) / (nx - 1)
1521+ xmin = ymin = minval
1522+
1523+ # "ijk" : table[ix, iy], shape (nx, ny)
1524+ table2 = numpy.array([[ix + iy * 10 for iy in range(ny)]
1525+ for ix in range(nx)], dtype=float)
1526+
1527+ interp2 = InterpolationTable(table2, origin=(xmin, ymin),
1528+ step=(xstep, ystep), order=1, undef=toobig)
1529+ result2 = interp2(x) # x has shape (2,)
14741530\end {python }
1475- Any values which interpolate to larger than \var {toobig} will raise an
1476- exception. You can switch on boundary checking by adding
1477- \code {check_boundaries=True} to the argument list.
14781531
1479- Now consider a 2D example. We will interpolate from a plane where $ \forall x,y \in [ 0 , 9 ]:(x,y)=x+y \cdot 10 $ .
1532+ \subsubsection { 3-D interpolation }
14801533
14811534\begin {python }
1482- from esys.escript import whereZero
1483- table2=[]
1484- for y in range(0,10):
1485- r=[]
1486- for x in range(0,10):
1487- r.append(x+y*10)
1488- table2.append(r)
1489- xstep=(maxval-minval)/(10-1)
1490- ystep=(maxval-minval)/(10-1)
1491-
1492- xmin=minval
1493- ymin=minval
1494-
1495- result2=interpolateTable(table2, x2, (xmin, ymin), (xstep, ystep), toobig)
1535+ from esys.finley import Brick
1536+ b = Brick(n, n, n)
1537+ x3 = b.getX() # shape (3,)
1538+ toobig = 1000000
1539+
1540+ nz = 10
1541+ zstep = (maxval - minval) / (nz - 1)
1542+ zmin = minval
1543+
1544+ # "ijk" : table[ix, iy, iz], shape (nx, ny, nz)
1545+ table3 = numpy.array([[[ix + iy*10 + iz*100 for iz in range(nz)]
1546+ for iy in range(ny)]
1547+ for ix in range(nx)], dtype=float)
1548+
1549+ interp3 = InterpolationTable(table3, origin=(xmin, ymin, zmin),
1550+ step=(xstep, ystep, zstep),
1551+ order=1, undef=toobig)
1552+ result3 = interp3(x3)
14961553\end {python }
14971554
1498- We can check the values using \function {whereZero}.
1499- For example, for $ x=0 $ :
1555+ \subsubsection {Accessor methods }
1556+
1557+ After construction, the parameters can be queried:
1558+
15001559\begin {python }
1501- print(result2*whereZero(x[0]))
1560+ interp.getOrder() # 0 or 1
1561+ interp.getNdim() # 1, 2, or 3
1562+ interp.getOrigin() # tuple of starting coordinates
1563+ interp.getStep() # tuple of cell sizes
1564+ interp.getExtend() # tuple of domain extents
1565+ interp.getTableIndexing() # "ijk" or "kji"
15021566\end {python }
15031567
1504- Finally let us look at a 3D example. Note that the parameter tuples should be
1505- $ (x,y,z)$ but that in the interpolation table, $ x$ is the innermost dimension.
1568+ \subsubsection {Deprecated \function {interpolateTable} }
1569+
1570+ The free function \function {interpolateTable} is deprecated and will be
1571+ removed in a future release. It uses the \code {"kji"} table convention.
1572+ Replace calls of the form
1573+ \begin {python }
1574+ result = interpolateTable(tab, dat, start, step, undef)
1575+ \end {python }
1576+ with
15061577\begin {python }
1507- b=Brick(n,n,n)
1508- x3=b.getX()
1509- toobig=1000000
1510-
1511- table3=[]
1512- for z in range(0,10):
1513- face=[]
1514- for y in range(0,10):
1515- r=[]
1516- for x in range(0,10):
1517- r.append(x+y*10+z*100)
1518- face.append(r)
1519- table3.append(face);
1520-
1521- zstep=(maxval-minval)/(10-1)
1522-
1523- zmin=minval
1524-
1525- result3=interpolateTable(table3, x3, (xmin, ymin, zmin),
1526- (xstep, ystep, zstep), toobig)
1578+ result = InterpolationTable(tab, start, step, order=1, undef=undef,
1579+ table_indexing="kji")(dat)
15271580\end {python }
15281581
15291582
0 commit comments