From 8a9d8f74f5af9b3de7468b14c2d6904eabbc64f0 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 25 Jan 2020 16:25:52 -0600 Subject: [PATCH] [numerics] Add previous value option to tabulated Func1 objects --- include/cantera/numerics/Func1.h | 19 ++++++++++++-- interfaces/cython/cantera/_cantera.pxd | 4 +-- interfaces/cython/cantera/func1.pyx | 29 ++++++++++++++------- src/numerics/Func1.cpp | 36 ++++++++++++++++---------- 4 files changed, 61 insertions(+), 27 deletions(-) diff --git a/include/cantera/numerics/Func1.h b/include/cantera/numerics/Func1.h index 2af040f2d93..e738aa31511 100644 --- a/include/cantera/numerics/Func1.h +++ b/include/cantera/numerics/Func1.h @@ -279,7 +279,8 @@ class Pow1 : public Func1 class Tabulated1 : public Func1 { public: - Tabulated1(const std::vector& tvec, const std::vector& fvec) : + Tabulated1(const std::vector& tvec, const std::vector& fvec, + const std::string& method = "linear") : Func1() { if (tvec.size() != fvec.size()) { throw CanteraError("Tabulated1::Tabulated1", @@ -291,6 +292,15 @@ class Tabulated1 : public Func1 } m_tvec = tvec; m_fvec = fvec; + if (method == "linear") { + m_isLinear = true; + } else if (method == "previous") { + m_isLinear = false; + } else { + throw CanteraError("Tabulated1::Tabulated1", + "interpolation method '{}' is not implemented", + method); + } } Tabulated1(const Tabulated1& b) : @@ -311,13 +321,18 @@ class Tabulated1 : public Func1 } virtual double eval(double t) const; virtual Func1& duplicate() const { - return *(new Tabulated1(m_tvec, m_fvec)); + if (m_isLinear) { + return *(new Tabulated1(m_tvec, m_fvec, "linear")); + } else { + return *(new Tabulated1(m_tvec, m_fvec, "previous")); + } } virtual Func1& derivative() const; private: std::vector m_tvec; std::vector m_fvec; + bool m_isLinear; }; diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 542ebc334f4..5be223fd628 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -40,7 +40,7 @@ cdef extern from "cantera/cython/funcWrapper.h": cdef extern from "cantera/numerics/Func1.h": cdef cppclass CxxTabulated1 "Cantera::Tabulated1": - CxxTabulated1(vector[double]&, vector[double]&) except +translate_exception + CxxTabulated1(vector[double]&, vector[double]&, string) except +translate_exception double eval(double) except +translate_exception cdef cppclass CxxConst1 "Cantera::Const1": @@ -1021,7 +1021,7 @@ cdef class Func1: cdef object exception cpdef void _set_callback(self, object) except * cpdef void _set_const(self, double) except * - cpdef void _set_tables(self, object, object) except * + cpdef void _set_tables(self, object, object, string) except * cdef class ReactorBase: cdef CxxReactorBase* rbase diff --git a/interfaces/cython/cantera/func1.pyx b/interfaces/cython/cantera/func1.pyx index a8f5e1ce15d..1a7560c84aa 100644 --- a/interfaces/cython/cantera/func1.pyx +++ b/interfaces/cython/cantera/func1.pyx @@ -58,22 +58,29 @@ cdef class Func1: points and corresponding function values. Inputs are specified either by two iterable objects containing sample point location and function values, or a single array that concatenates those inputs in two columns. Between - sample points, values are evaluated using a linear interpolation, whereas - outside the sample interval, the value at the closest end point is returned. + sample points, values are evaluated based on the keyword argument + 'interpolation'; options are 'linear' (linear interpolation, default) or + 'previous' (nearest previous value). Outside the sample interval, the value + at the closest end point is returned. + Examples for tabulated `Func1` object are:: >>> t1 = Func1([[0, 2], [1, 1], [2, 0]]) >>> [t1(v) for v in [-0.5, 0, 0.5, 1.5, 2, 2.5]] [2.0, 2.0, 1.5, 0.5, 0.0, 0.0] - >>> t2 = Func1([0, 1, 2], [2, 1, 0]) + >>> t2 = Func1([[0, 2], [1, 1], [2, 0]], interpolation='previous') >>> [t2(v) for v in [-0.5, 0, 0.5, 1.5, 2, 2.5]] - [2.0, 2.0, 1.5, 0.5, 0.0, 0.0] + [2.0, 2.0, 2.0, 1.0, 0.0, 0.0] - >>> t3 = Func1(np.array([0, 1, 2]), (2, 1, 0)) + >>> t3 = Func1([0, 1, 2], [2, 1, 0]) >>> [t3(v) for v in [-0.5, 0, 0.5, 1.5, 2, 2.5]] [2.0, 2.0, 1.5, 0.5, 0.0, 0.0] + >>> t4 = Func1(np.array([0, 1, 2]), (2, 1, 0)) + >>> [t4(v) for v in [-0.5, 0, 0.5, 1.5, 2, 2.5]] + [2.0, 2.0, 1.5, 0.5, 0.0, 0.0] + Note that all methods which accept `Func1` objects will also accept the callable object and create the wrapper on their own, so it is generally unnecessary to explicitly create a `Func1` object. @@ -82,7 +89,7 @@ cdef class Func1: self.exception = None self.callable = None - def __init__(self, *args): + def __init__(self, *args, **kwargs): if len(args) == 1: c = args[0] if hasattr(c, '__call__'): @@ -104,7 +111,8 @@ cdef class Func1: if arr.shape[1] == 2: time = arr[:, 0] fval = arr[:, 1] - self._set_tables(time, fval) + method = kwargs.get('interpolation', 'linear') + self._set_tables(time, fval, stringify(method)) else: raise ValueError( "Invalid dimensions: specification of " @@ -121,7 +129,8 @@ cdef class Func1: elif len(args) == 2: # tabulated function (two arguments mimic C++ interface) time, fval = args - self._set_tables(time, fval) + method = kwargs.get('interpolation', 'linear') + self._set_tables(time, fval, stringify(method)) else: raise ValueError("Invalid number of arguments") @@ -134,13 +143,13 @@ cdef class Func1: cpdef void _set_const(self, double c) except *: self.func = (new CxxConst1(c)) - cpdef void _set_tables(self, time, fval) except *: + cpdef void _set_tables(self, time, fval, method) except *: cdef vector[double] tvec, fvec for t in time: tvec.push_back(t) for f in fval: fvec.push_back(f) - self.func = (new CxxTabulated1(tvec, fvec)) + self.func = (new CxxTabulated1(tvec, fvec, method)) def __dealloc__(self): del self.func diff --git a/src/numerics/Func1.cpp b/src/numerics/Func1.cpp index 7feeb10fa2d..20530f5117a 100644 --- a/src/numerics/Func1.cpp +++ b/src/numerics/Func1.cpp @@ -227,10 +227,14 @@ double Tabulated1::eval(double t) const { while (t > m_tvec[ix+1]) { ix++; } - double df = m_fvec[ix+1] - m_fvec[ix]; - df /= m_tvec[ix+1] - m_tvec[ix]; - df *= t - m_tvec[ix]; - return m_fvec[ix] + df; + if (m_isLinear) { + double df = m_fvec[ix+1] - m_fvec[ix]; + df /= m_tvec[ix+1] - m_tvec[ix]; + df *= t - m_tvec[ix]; + return m_fvec[ix] + df; + } else { + return m_fvec[ix]; + } } } else { return 0.; @@ -241,20 +245,26 @@ Func1& Tabulated1::derivative() const { std::vector tvec; std::vector dvec; size_t siz = m_tvec.size(); - if (siz>1) { - for (size_t i=1; i1) { + for (size_t i=1; i