2121import numpy as np
2222
2323cimport cython
24- from raysect.core.math.cython.utility cimport find_index
2524from raysect.core.math.cython.interpolation.cubic cimport calc_coefficients_1d, evaluate_cubic_1d
2625from cherab.core.math.function cimport autowrap_function1d
2726
@@ -73,7 +72,6 @@ cdef class Caching1D(Function1D):
7372 """
7473
7574 # The implementation works as follows:
76- # - From space_area and resolution, generate an x grid of sampled points.
7775 # - Store a list of which points have already been evaluated, and their values.
7876 # - For each new value x to evaluate, check if the sample points either side
7977 # have already been evaluated.
@@ -84,6 +82,9 @@ cdef class Caching1D(Function1D):
8482 # - Use raysect.core.math.cython.interpolation.cubic's utilities to calculate
8583 # the polynomial coefficients for the 1D cubic and then evaluate the cubic
8684 # interpolation of the function at the position x.
85+ # - Note that the sampled points have uniform spacing: this enables a few
86+ # optimisations such as analytic calculations of the array index and x
87+ # values for the nearest samples to the requested x.
8788
8889 def __init__ (self , function1d , space_area , resolution , no_boundary_error = False , function_boundaries = None ):
8990 self ._function = autowrap_function1d(function1d)
@@ -93,14 +94,9 @@ cdef class Caching1D(Function1D):
9394 if resolution <= 0 :
9495 raise ValueError (" Resolution must be greater than zero." )
9596 self ._resolution = resolution
96- nsamples = int ((self ._xmax - self ._xmin) / resolution + 1 )
97- xsamples = np.linspace(self ._xmin, self ._xmax, nsamples)
98- fsamples = np.empty_like(xsamples)
99- sampled = np.full_like(fsamples, False , dtype = ' uint8' )
100- self ._nsamples = nsamples
101- self ._xsamples = xsamples
102- self ._fsamples = fsamples
103- self ._sampled = sampled
97+ self ._nsamples = int ((self ._xmax - self ._xmin) / resolution + 1 )
98+ self ._fsamples = np.empty(self ._nsamples)
99+ self ._sampled = np.full(self ._nsamples, False , dtype = ' uint8' )
104100 self ._no_boundary_error = no_boundary_error
105101 # TODO: normalise using function_boundaries to avoid numerical issues.
106102
@@ -111,7 +107,7 @@ cdef class Caching1D(Function1D):
111107 cdef double x, fsamp
112108
113109 if not self ._sampled[index]:
114- x = self ._xsamples[ index]
110+ x = self ._xmin + index * self ._resolution
115111 fsamp = self ._function.evaluate(x)
116112 self ._sampled[index] = True
117113 self ._fsamples[index] = fsamp
@@ -135,7 +131,8 @@ cdef class Caching1D(Function1D):
135131 double dfdx[2 ] # Function derivative evaluations
136132
137133 if self ._xmin <= x < self ._xmax:
138- lower_index = find_index(self ._xsamples, x)
134+ # Sampling is uniform, so lower_index is determined analytically.
135+ lower_index = int ((x - self ._xmin) // self ._resolution)
139136 f[0 ] = self ._get_and_cache(lower_index)
140137 f[1 ] = self ._get_and_cache(lower_index + 1 )
141138 # Calculate derivatives. calc_coefficiets_1d requires derivatives
@@ -151,7 +148,9 @@ cdef class Caching1D(Function1D):
151148 fnext = self ._get_and_cache(lower_index + 2 )
152149 dfdx[1 ] = (fnext - f[0 ]) / 2
153150 calc_coefficients_1d(f, dfdx, a)
154- xnorm = (x - self ._xsamples[lower_index]) / self ._resolution
151+ # lower_x = xmin + resolution * index
152+ # xnorm = (x - lower_x) / res = (x - xmin) / resolution - index
153+ xnorm = (x - self ._xmin) / self ._resolution - lower_index
155154 feval = evaluate_cubic_1d(a, xnorm)
156155 return feval
157156
0 commit comments