Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extensible reaction rates #1382

Merged
merged 26 commits into from
Sep 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5cf3be2
Minimal implementation of ExtensibleRate
speth Aug 8, 2022
ebb20ef
Pass an arbitrary argument to user rate function
speth Aug 11, 2022
150a8a8
Make Delegator ownership optional for ExtensibleRate
speth Aug 12, 2022
68f4597
[SCons] Extract Python module-building environment setup
speth Aug 12, 2022
1b443a2
Add machinery for managing user extensions
speth Aug 13, 2022
263a12a
Scan user-provided Python module for ExtensibleRate classes
speth Aug 12, 2022
9845032
Register ExtensibleRate types with ReactionRateFactory
speth Aug 27, 2022
c302229
Allow Delegator to delete Python rates
speth Aug 27, 2022
0cbe50c
Provide better error messages for Python extensions
speth Aug 28, 2022
b5a647a
Use decorator to register ExtensibleRate objects
speth Aug 28, 2022
0132c48
Implement delegation of ReactionRate::setParameters
speth Aug 29, 2022
7a0ca24
Handle sharing of ExtensibleRate among copies of ReactionRateDelegator
speth Aug 29, 2022
bc4685c
Provide user-defined name for ExtensibleRate objects
speth Aug 29, 2022
3c6d662
Add C++ test for Python ExtensibleRate
speth Aug 29, 2022
5bbd17f
Update custom_reaction example to also show ExtensibleRate
speth Aug 30, 2022
b0b1e93
Add more documentation for ExtensibleRate
speth Aug 30, 2022
dc8bff0
Add library directory containing libpython3.x
speth Aug 31, 2022
eb2823e
Get ExtensibleRate to work on Windows GitHub Actions
speth Aug 31, 2022
68a8758
Improve error reporting during python extension module initialization
speth Sep 4, 2022
214a2aa
Set PYTHONHOME when embedding Python on Windows
speth Sep 4, 2022
0dc2eb0
Improve handling of dynamic library paths on Mac/Windows
speth Sep 5, 2022
bebeeb7
Ensure registration of extension rates in host Cantera library
speth Sep 5, 2022
06267d8
[CI] Switch to Homebrew Python on macOS
speth Sep 5, 2022
2ec2012
Test error conditions for ExtensibleRate
speth Sep 7, 2022
ecd63cd
Update documentation of "version added"
speth Sep 9, 2022
d7efc72
Make Python extensions compatible with venv
speth Sep 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- name: Install Apt dependencies
run: |
sudo apt update
sudo apt install libboost-dev gfortran libopenmpi-dev
sudo apt install libboost-dev gfortran libopenmpi-dev libpython3-dev
- name: Upgrade pip
run: python3 -m pip install -U pip setuptools wheel
- name: Install Python dependencies
Expand Down Expand Up @@ -108,26 +108,22 @@ jobs:
name: Checkout the repository
with:
submodules: recursive
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- name: Install Brew dependencies
run: brew install boost libomp
run: |
brew install boost libomp scons python@${{ matrix.python-version }}
- name: Upgrade pip
run: python3 -m pip install -U pip 'setuptools>=47.0.0,<48' wheel
- name: Install Python dependencies
run: python3 -m pip install ruamel.yaml scons numpy cython h5py pandas pytest
run: python3 -m pip install ruamel.yaml numpy cython h5py pandas pytest
pytest-github-actions-annotate-failures
- name: Install typing_extensions for Python 3.7
if: matrix.python-version == '3.7'
run: python3 -m pip install typing_extensions
- name: Build Cantera
run: python3 `which scons` build env_vars=all -j3 debug=n --debug=time
run: scons build env_vars=all -j3 debug=n --debug=time
- name: Test Cantera
run:
python3 `which scons` test show_long_tests=yes verbose_tests=yes --debug=time
scons test show_long_tests=yes verbose_tests=yes --debug=time

# Coverage is its own job because macOS builds of the samples
# use Homebrew gfortran which is not compatible for coverage
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ include/cantera/base/system.h.gch
include/cantera/ext/
interfaces/matlab/ctpath.m
interfaces/matlab/Contents.m
src/extensions/pythonExtensions.h
stage/
.sconsign.dblite
.sconf_temp
Expand Down
9 changes: 8 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@
(for example, comment lines starting with `//!` or comment blocks starting with
`/*!`; do not use `///` or `/**` in new code)
* Doxygen-style groupings should bracket code using `//! @{` and `//! @}`
* Indicate the version added for new functions and classes with an annotation like
`@since New in Cantera X.Y` where `X.Y` is the next Cantera version. This notation
should also be used indicate significant changes in behavior.
* Avoid defining non-trivial functions in header files
* Header files should include an 'include guard'
* Protected and private member variable names are generally prefixed with
Expand Down Expand Up @@ -99,7 +102,11 @@

* Style generally follows PEP8 (https://www.python.org/dev/peps/pep-0008/)
* Code in `.py` and `.pyx` files needs to be written to work with Python 3
* The minimum Python version that Cantera supports is Python 3.6, so code should only use features added in Python 3.6 or earlier
* The minimum Python version that Cantera supports is Python 3.7, so code should only
use features added in Python 3.7 or earlier
* Indicate the version added for new functions and classes with an annotation like
`.. versionadded:: X.Y` where `X.Y` is the next Cantera version. Significant changes
in behavior should be indicated with `.. versionchanged:: X.Y`.
* Please use double quotes in all new Python code

## C#
Expand Down
3 changes: 2 additions & 1 deletion SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ config_options = [
"""Environment variables to propagate through to SCons. Either the
string 'all' or a comma separated list of variable names, for example,
'LD_LIBRARY_PATH,HOME'.""",
"PATH,LD_LIBRARY_PATH,PYTHONPATH"),
"PATH,LD_LIBRARY_PATH,DYLD_LIBRARY_PATH,PYTHONPATH"),
BoolOption(
"use_pch",
"Use a precompiled-header to speed up compilation",
Expand Down Expand Up @@ -1910,6 +1910,7 @@ cdefine("CT_USE_SYSTEM_EIGEN_PREFIXED", "system_eigen_prefixed")
cdefine('CT_USE_SYSTEM_FMT', 'system_fmt')
cdefine('CT_USE_SYSTEM_YAMLCPP', 'system_yamlcpp')
cdefine('CT_USE_DEMANGLE', 'has_demangle')
cdefine('CT_HAS_PYTHON', 'python_package', 'full')

config_h_build = env.Command('build/src/config.h.build',
'include/cantera/base/config.h.in',
Expand Down
5 changes: 5 additions & 0 deletions doc/sphinx/cython/kinetics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ CustomRate
.. autoclass:: CustomRate(k)
:no-undoc-members:

ExtensibleRate
^^^^^^^^^^^^^^
.. autoclass:: ExtensibleRate()
:no-undoc-members:

InterfaceRateBase
^^^^^^^^^^^^^^^^^
.. autoclass:: InterfaceRateBase
Expand Down
2 changes: 2 additions & 0 deletions doc/sphinx/cython/utilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Global Functions
.. autofunction:: debug_mode_enabled
.. autofunction:: add_module_directory

.. autofunction:: extension(name: str)

Exceptions
----------

Expand Down
63 changes: 63 additions & 0 deletions include/cantera/base/Delegator.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

#include "cantera/base/global.h"
#include "cantera/base/ctexceptions.h"
#include "cantera/base/ExtensionManager.h"
#include <array>
#include <list>

namespace Cantera
{
Expand Down Expand Up @@ -133,6 +135,21 @@ class Delegator
*m_funcs_v_d[name] = makeDelegate(func, when, *m_funcs_v_d[name]);
}

//! set delegates for member functions with the signature
//! `void(AnyMap&, UnitStack&)`
void setDelegate(const std::string& name,
const std::function<void(const AnyMap&, const UnitStack&)>& func,
const std::string& when)
{
if (!m_funcs_v_cAMr_cUSr.count(name)) {
throw NotImplementedError("Delegator::setDelegate",
"for function named '{}' with signature "
"'void(const AnyMap&, const UnitStack&)'.",
name);
}
*m_funcs_v_cAMr_cUSr[name] = makeDelegate(func, when, *m_funcs_v_cAMr_cUSr[name]);
}

//! Set delegates for member functions with the signature `void(double*)`
void setDelegate(const std::string& name,
const std::function<void(std::array<size_t, 1>, double*)>& func,
Expand Down Expand Up @@ -189,6 +206,18 @@ class Delegator
*m_funcs_v_dp_dp_dp[name] = makeDelegate(func, when, *m_funcs_v_dp_dp_dp[name]);
}

//! set delegates for member functions with the signature `double(void*)`
void setDelegate(const std::string& name,
const std::function<int(double&, void*)>& func,
const std::string& when)
{
if (!m_funcs_d_vp.count(name)) {
throw NotImplementedError("Delegator::setDelegate",
"for function named '{}' with signature 'double(void*)'.", name);
}
*m_funcs_d_vp[name] = makeDelegate(func, when, m_base_d_vp[name]);
}

//! Set delegates for member functions with the signature `string(size_t)`
void setDelegate(const std::string& name,
const std::function<int(std::string&, size_t)>& func,
Expand All @@ -215,6 +244,10 @@ class Delegator
*m_funcs_sz_csr[name] = makeDelegate(func, when, m_base_sz_csr[name]);
}

void holdExternalHandle(const shared_ptr<ExternalHandle>& handle) {
m_handles.push_back(handle);
}

protected:
//! Install a function with the signature `void()` as being delegatable
void install(const std::string& name, std::function<void()>& target,
Expand All @@ -240,6 +273,18 @@ class Delegator
m_funcs_v_d[name] = &target;
}

//! Install a function with the signature `void(const AnyMap&, const UnitStack&)`
//! as being delegatable
void install(const std::string& name,
std::function<void(const AnyMap&, const UnitStack&)>& target,
const std::function<void(const AnyMap&, const UnitStack&)>& func)
{
target = func;
m_funcs_v_cAMr_cUSr[name] = &target;
}



//! Install a function with the signature `void(double*)` as being delegatable
void install(const std::string& name,
std::function<void(std::array<size_t, 1>, double*)>& target,
Expand Down Expand Up @@ -278,6 +323,14 @@ class Delegator
m_funcs_v_dp_dp_dp[name] = &target;
}

//! Install a function with the signature `double(void*)` as being delegatable
void install(const std::string& name, std::function<double(void*)>& target,
const std::function<double(void*)>& func)
{
target = func;
m_funcs_d_vp[name] = &target;
}

//! Install a function with the signature `string(size_t)` as being delegatable
void install(const std::string& name,
std::function<std::string(size_t)>& target,
Expand Down Expand Up @@ -394,6 +447,8 @@ class Delegator
//! - `d` for `double`
//! - `s` for `std::string`
//! - `sz` for `size_t`
//! - `AM` for `AnyMap`
//! - `US` for `UnitStack`
//! - prefix `c` for `const` arguments
//! - suffix `r` for reference arguments
//! - suffix `p` for pointer arguments
Expand All @@ -403,6 +458,8 @@ class Delegator
std::map<std::string, std::function<void()>*> m_funcs_v;
std::map<std::string, std::function<void(bool)>*> m_funcs_v_b;
std::map<std::string, std::function<void(double)>*> m_funcs_v_d;
std::map<std::string,
std::function<void(const AnyMap&, const UnitStack&)>*> m_funcs_v_cAMr_cUSr;
std::map<std::string,
std::function<void(std::array<size_t, 1>, double*)>*> m_funcs_v_dp;
std::map<std::string,
Expand All @@ -413,6 +470,9 @@ class Delegator
std::function<void(std::array<size_t, 3>, double*, double*, double*)>*> m_funcs_v_dp_dp_dp;

// Delegates with a return value
std::map<std::string, std::function<double(void*)>> m_base_d_vp;
std::map<std::string, std::function<double(void*)>*> m_funcs_d_vp;

std::map<std::string,
std::function<std::string(size_t)>> m_base_s_sz;
std::map<std::string,
Expand All @@ -423,6 +483,9 @@ class Delegator
std::map<std::string,
std::function<size_t(const std::string&)>*> m_funcs_sz_csr;
//! @}

//! Cleanup functions to be called from the destructor
std::list<shared_ptr<ExternalHandle>> m_handles;
};

}
Expand Down
39 changes: 39 additions & 0 deletions include/cantera/base/ExtensionManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//! @file ExtensionManager.h

#ifndef CT_EXTENSIONMANAGER_H
#define CT_EXTENSIONMANAGER_H

// This file is part of Cantera. See License.txt in the top-level directory or
// at https://cantera.org/license.txt for license and copyright information.

#include "cantera/base/ctexceptions.h"

namespace Cantera
{

//! Base class for managing user-defined Cantera extensions written in other languages
//!
//! @since New in Cantera 3.0
class ExtensionManager
{
public:
virtual ~ExtensionManager() = default;

//! Register ReactionRate defined in a user extension with ReactionRateFactory
//! @param extensionName
virtual void registerRateBuilders(const std::string& extensionName) {
throw NotImplementedError("ExtensionManager::registerRateBuilders");
};
};

//! A base class for managing the lifetime of an external object, such as a Python
//! object used by a Delegator
class ExternalHandle
{
public:
virtual ~ExternalHandle() = default;
};

}

#endif
46 changes: 46 additions & 0 deletions include/cantera/base/ExtensionManagerFactory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! @file ExtensionManagerFactory.h

#ifndef CT_EXTENSIONMANAGERFACTORY_H
#define CT_EXTENSIONMANAGERFACTORY_H

// This file is part of Cantera. See License.txt in the top-level directory or
// at https://cantera.org/license.txt for license and copyright information.

#include "FactoryBase.h"
#include "ExtensionManager.h"

namespace Cantera
{

//! A factory class for creating ExtensionManager objects
//!
//! @since New in Cantera 3.0
class ExtensionManagerFactory : public Factory<ExtensionManager>
{
public:
//! Create a new ExtensionManager
static shared_ptr<ExtensionManager> build(const std::string& extensionType) {
return shared_ptr<ExtensionManager>(factory().create(extensionType));
}

//! Delete the static instance of this factory
virtual void deleteFactory();

private:
//! Static function that returns the static instance of the factory, creating it
//! if necessary.
static ExtensionManagerFactory& factory();

//! static member of the single factory instance
static ExtensionManagerFactory* s_factory;

//! Private constructor prevents direct usage
ExtensionManagerFactory();

//! Decl for locking mutex for thermo factory singleton
static std::mutex s_mutex;
};

}

#endif
1 change: 1 addition & 0 deletions include/cantera/base/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ typedef int ftnlen; // Fortran hidden string length type
{CT_USE_SYSTEM_FMT!s}
{CT_USE_SYSTEM_YAMLCPP!s}
{CT_USE_DEMANGLE!s}
{CT_HAS_PYTHON!s}

//--------- operating system --------------------------------------

Expand Down
1 change: 1 addition & 0 deletions include/cantera/base/ctexceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#ifndef CT_CTEXCEPTIONS_H
#define CT_CTEXCEPTIONS_H

#include "ct_defs.h"
#include "cantera/base/fmt.h"
#include <exception>

Expand Down
10 changes: 10 additions & 0 deletions include/cantera/base/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Cantera
{

class Logger;
class AnyMap;

/*!
* @defgroup inputfiles Input File Handling
Expand Down Expand Up @@ -77,6 +78,15 @@ void addDirectory(const std::string& dir);
std::string getDataDirectories(const std::string& sep);
//! @}

//! @copydoc Application::loadExtension
void loadExtension(const std::string& extType, const std::string& name);

//! Load extensions providing user-defined models from the `extensions` section of the
//! given node. @see Application::loadExtension
//!
//! @since New in Cantera 3.0
void loadExtensions(const AnyMap& node);

//! Delete and free all memory associated with the application
/*!
* Delete all global data. It should be called at the end of the
Expand Down
Loading