Cantera / cantera

Chemical kinetics, thermodynamics, and transport tool suite
https://cantera.org
Other
624 stars 350 forks source link

Make Func1 API's more consistent #1758

Closed ischoegl closed 3 months ago

ischoegl commented 3 months ago

Changes proposed in this pull request

This PR ensures that Python and MATLAB API's are consistent as far as possible (follow-up to #1741).

If applicable, provide an example illustrating new features this pull request is introducing

In MATLAB:

>> out = Func1('sin') / 2  % Func1 operators work as of PR 1741

out = 

   0.5\sin(x)

>> out.type()  % simplification introduced in this PR (used to be 'ratio')

ans =

    'times-constant'

In Python:

In [1]: import cantera as ct

In [2]: a = ct.Func1(lambda x: x**2)

In [3]: b = a * 0  # Func1 operator introduced in this PR

In [4]: b.type
Out[4]: 'constant'

In [5]: b(1.5)
Out[5]: 0.0

In [6]: c = 2 * a

In [7]: c.type
Out[7]: 'times-constant'

In [8]: c(1.5), 2 * 1.5**2
Out[8]: (4.5, 4.5)

In [9]: f5 = ct.Func1("pow", 3.)  # MATLAB-like constructor (used to be 'ct.Func1.cxx_functor')
   ...: f5.type
Out[9]: 'pow'

In [10]: f5(2.0)
Out[10]: 8.0

Checklist

codecov[bot] commented 3 months ago

Codecov Report

Attention: Patch coverage is 73.64341% with 34 lines in your changes missing coverage. Please review.

Project coverage is 73.20%. Comparing base (3196d5e) to head (7e0ebe1). Report is 3 commits behind head on main.

Files Patch % Lines
src/numerics/Func1.cpp 45.83% 9 Missing and 17 partials :warning:
src/clib/ctfunc.cpp 60.00% 8 Missing :warning:
Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #1758 +/- ## ========================================== + Coverage 73.12% 73.20% +0.07% ========================================== Files 381 381 Lines 54174 54289 +115 Branches 9223 9242 +19 ========================================== + Hits 39615 39740 +125 + Misses 11598 11577 -21 - Partials 2961 2972 +11 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

ischoegl commented 3 months ago

For the sake of consistency, I ended up making Python and MATLAB Func1 API's more symmetric. While there are no callbacks in MATLAB, Python now allows for MATLAB-like construction of functors.

PS: ~Pushed one additional commit to improve coverage of specialized Func1 functions.~ Rebased/squashed.

ischoegl commented 3 months ago

I ended up adding a section to our custom_reaction.py benchmark that uses C++ functors. Here are results:

% python samples/python/kinetics/custom_reactions.py
Average time of 100 simulation runs for 'gri30.yaml' (CH4)
- New framework (YAML): 18.90 μs/step (T_final=2780.21)
- Two Custom reactions (Python): 19.27 μs/step (T_final=2780.21) ...+1.92%
- Two Custom reactions (C++): 18.93 μs/step (T_final=2780.21) ...+0.15%
- Two Extensible reactions: 19.74 μs/step (T_final=2780.21) ...+4.45%

i.e. unsurprisingly the C++ Func1 performs more or less the same as the original mechanism (on some runs, it was actually faster, so it is within the accuracy of this poor man's benchmark).

While I don't believe that C++ functors are all that important to the Python API, I believe that having an interface equivalent to MATLAB is overall beneficial.

ischoegl commented 3 months ago

1763 should be merged before, so custom_reaction.py can be updated once this is rebased.

ischoegl commented 3 months ago

Rebased and incorporated #1763.

ischoegl commented 3 months ago

Rebased once more and took care of a couple of paper cuts; PR should be ready for a review.

ischoegl commented 3 months ago

@speth ... I updated the Python docstring as follows:

     [...]
     Note that `Func1` objects also allow for direct access to functor objects
     implemented in C++ based on associated type specifiers::

         >>> f5 = Func1("exp", 3.)  # C++ 'Exp1' functor
         >>> f5.cxx_type
         'Cantera::Exp1'
         >>> f5.write()
         '\\exp(3x)'
         >>> f5(2.)
         403.4287934927351
         >>> f6 = Func1("Arrhenius", [9630.0, 2.0, 2012.878])  # C++ 'Arrhenius1' functor
         >>> f6(1500)
         5662665826.195515

     For implemented C++ functor types, see the Cantera C++ :ct:`Func1` documentation.

     `Func1` objects support operator overloading which facilitates the construction of
     compound functions, where some standard simplifications are implemented::

         >>> f7 = 2 * f5 + 3
         >>> f7.write()
         '2\\exp(3x) + 3'
         >>> f7(2.)
         809.8575869854702
         >>> f8 = f5 * f5
         >>> f8.write()
         '\\exp(6x)'
         >>> f8(2.)
         162754.79141900392

which also illustrates Func1.write (added in 3.0) and a Func1.cxx_type that I added in an update to this PR.