mirror of
https://github.com/Cantera/cantera.git
synced 2025-02-25 18:55:29 -06:00
Merge fc7926b5ca
into 0720efb02d
This commit is contained in:
commit
1d1e6745dd
@ -32,6 +32,8 @@
|
||||
enum LogLevel { INFO, WARN , ERROR };
|
||||
|
||||
//! Represents a callback that is invoked to produce log output.
|
||||
//! TODO: Only needed in the main CLib library. Should be moved once the
|
||||
//! traditional CLib is removed.
|
||||
typedef void
|
||||
(*LogCallback)(enum LogLevel logLevel, const char* category, const char* message);
|
||||
|
||||
|
@ -19,6 +19,7 @@ enum flow_t { NetFlow, OneWayFlow };
|
||||
|
||||
// forward references
|
||||
class Path;
|
||||
class ReactionPathBuilder;
|
||||
|
||||
/**
|
||||
* Nodes in reaction path graphs.
|
||||
@ -161,6 +162,15 @@ class ReactionPathDiagram
|
||||
public:
|
||||
ReactionPathDiagram() = default;
|
||||
|
||||
//! Construct new reaction path diagram.
|
||||
/**
|
||||
* The method creates a reaction path diagram for the fluxes of `element`
|
||||
* according to instantaneous net reaction rates.
|
||||
* @param kin Shared pointer to Kinetics object.
|
||||
* @param element_ Element used for the calculation of net reaction rates.
|
||||
*/
|
||||
ReactionPathDiagram(shared_ptr<Kinetics> kin, const string& element_);
|
||||
|
||||
/**
|
||||
* Destructor. Deletes all nodes and paths in the diagram.
|
||||
*/
|
||||
@ -188,6 +198,12 @@ public:
|
||||
|
||||
void writeData(std::ostream& s);
|
||||
|
||||
//! Get a (roughly) human-readable representation of the reaction path diagram.
|
||||
/**
|
||||
* @see writeData
|
||||
*/
|
||||
string getData();
|
||||
|
||||
/**
|
||||
* Export the reaction path diagram. This method writes to stream
|
||||
* @c s the commands for the 'dot' program in the @c GraphViz
|
||||
@ -202,7 +218,19 @@ public:
|
||||
*/
|
||||
void exportToDot(std::ostream& s);
|
||||
|
||||
//! Export string in @c dot format.
|
||||
/**
|
||||
* Return a string containing the reaction path diagram formatted for use
|
||||
* by Graphviz's 'dot' program.
|
||||
* @see exportToDot
|
||||
*/
|
||||
string getDot();
|
||||
|
||||
void add(ReactionPathDiagram& d);
|
||||
|
||||
//! Add fluxes from other ReactionPathDiagram to this diagram.
|
||||
void add(shared_ptr<ReactionPathDiagram> d);
|
||||
|
||||
SpeciesNode* node(size_t k) {
|
||||
return m_nodes[k];
|
||||
}
|
||||
@ -221,6 +249,8 @@ public:
|
||||
|
||||
void addNode(size_t k, const string& nm, double x = 0.0);
|
||||
|
||||
//! Include only species and fluxes that are directly connected to a species.
|
||||
//! Set to -1 to include all species.
|
||||
void displayOnly(size_t k=npos) {
|
||||
m_local = k;
|
||||
}
|
||||
@ -251,31 +281,63 @@ public:
|
||||
}
|
||||
vector<size_t> species();
|
||||
vector<int> reactions();
|
||||
|
||||
//! Undocumented.
|
||||
/**
|
||||
* @todo Add documentation.
|
||||
*/
|
||||
void findMajorPaths(double threshold, size_t lda, double* a);
|
||||
|
||||
//! Set name of the font used.
|
||||
void setFont(const string& font) {
|
||||
m_font = font;
|
||||
}
|
||||
// public attributes
|
||||
|
||||
string title;
|
||||
string bold_color = "blue";
|
||||
string normal_color = "steelblue";
|
||||
string dashed_color = "gray";
|
||||
string element;
|
||||
string m_font = "Helvetica";
|
||||
//! Get the way flows are drawn. Either 'NetFlow' or 'OneWayFlow'
|
||||
const string flowType() const;
|
||||
|
||||
//! Get the way flows are drawn. Either 'NetFlow' or 'OneWayFlow'
|
||||
void setFlowType(const string& fType);
|
||||
|
||||
//! Build the reaction path diagram.
|
||||
/**
|
||||
* Called automatically by methods which return representations of the diagram,
|
||||
* for example writeDot().
|
||||
*/
|
||||
void build();
|
||||
|
||||
//! Get logging messages generated while building the reaction path diagram.
|
||||
string getLog();
|
||||
|
||||
//! @name Public Attributes
|
||||
//! @{
|
||||
|
||||
string title; //!< Reaction path diagram title.
|
||||
string bold_color = "blue"; //!< Color for bold lines.
|
||||
string normal_color = "steelblue"; //!< Color for normal-weight lines.
|
||||
string dashed_color = "gray"; //!< Color for dashed lines.
|
||||
string element; //!< Element used for the construction of a reaction path diagram.
|
||||
string m_font = "Helvetica"; //!< Reaction path diagram font.
|
||||
//! Threshold for the minimum flux relative value that will be plotted.
|
||||
double threshold = 0.005;
|
||||
double bold_min = 0.2;
|
||||
double dashed_max = 0.0;
|
||||
double label_min = 0.0;
|
||||
double x_size = -1.0;
|
||||
double y_size = -1.0;
|
||||
string name = "reaction_paths";
|
||||
string dot_options = "center=1;";
|
||||
double bold_min = 0.2; //!< Minimum relative flux for bold lines.
|
||||
double dashed_max = 0.0; //!< Maximum relative flux for dashed lines.
|
||||
double label_min = 0.0; //!< Minimum relative flux for labels.
|
||||
double x_size = -1.0; //!< Maximum size (x-dimension).
|
||||
double y_size = -1.0; //!< Maximum size (y-dimension).
|
||||
string name = "reaction_paths"; //!< Name used for dot export.
|
||||
string dot_options = "center=1;"; //!< Options for the 'dot' program.
|
||||
//! The way flows are drawn. Either 'NetFlow' or 'OneWayFlow'
|
||||
flow_t flow_type = NetFlow;
|
||||
double scale = -1;
|
||||
//! The scaling factor for the fluxes.
|
||||
//! Set to -1 to normalize by the maximum net flux.
|
||||
double scale = -1; //!< Scale to use for normalization.
|
||||
//! The arrow width. If < 0, then scale with flux value.
|
||||
double arrow_width = -5.0;
|
||||
bool show_details = false;
|
||||
double arrow_hue = 0.6666;
|
||||
bool show_details = false; //!< Boolean flag to show details.
|
||||
double arrow_hue = 0.6666; //!< Unused.
|
||||
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
double m_flxmax = 0.0;
|
||||
@ -291,6 +353,11 @@ protected:
|
||||
//! Indices of reactions that are included in the diagram
|
||||
set<size_t> m_rxns;
|
||||
size_t m_local = npos;
|
||||
|
||||
bool m_isBuilt = false; //!< Boolean indicating whether diagram is built.
|
||||
shared_ptr<Kinetics> m_kin; //!< Kinetics used by ReactionPathBuilder
|
||||
shared_ptr<ReactionPathBuilder> m_builder; //!< Shared pointer to ReactionPathBuilder
|
||||
std::stringstream m_log; //!< Logging stream.
|
||||
};
|
||||
|
||||
|
||||
@ -334,6 +401,18 @@ protected:
|
||||
map<string, size_t> m_enamemap;
|
||||
};
|
||||
|
||||
|
||||
//! Create a new reaction path diagram.
|
||||
/**
|
||||
* Returns a shared ReactionPath instance where the fluxes of `element`
|
||||
* are calculated according to instantaneous net reaction rates.
|
||||
* @param kin Shared pointer to Kinetics object.
|
||||
* @param element Element used for the calculation of net reaction rates.
|
||||
* @return shared_ptr<ReactionPathDiagram>
|
||||
*/
|
||||
shared_ptr<ReactionPathDiagram> newReactionPathDiagram(
|
||||
shared_ptr<Kinetics> kin, const string& element);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -4,18 +4,11 @@
|
||||
#cython: language_level=3
|
||||
#distutils: language = c++
|
||||
|
||||
from .ctcxx cimport *
|
||||
from .kinetics cimport *
|
||||
|
||||
cdef extern from "<sstream>":
|
||||
cdef cppclass CxxStringStream "std::stringstream":
|
||||
string str()
|
||||
|
||||
|
||||
cdef extern from "cantera/kinetics/ReactionPath.h":
|
||||
cdef enum CxxFlow_t "flow_t":
|
||||
CxxNetFlow "Cantera::NetFlow"
|
||||
CxxOneWayFlow "Cantera::OneWayFlow"
|
||||
cdef shared_ptr[CxxReactionPathDiagram] CxxNewReactionPathDiagram "Cantera::newReactionPathDiagram"(
|
||||
shared_ptr[CxxKinetics], string) except +translate_exception
|
||||
|
||||
cdef cppclass CxxReactionPathDiagram "Cantera::ReactionPathDiagram":
|
||||
cbool show_details
|
||||
@ -29,24 +22,20 @@ cdef extern from "cantera/kinetics/ReactionPath.h":
|
||||
double label_min
|
||||
double scale
|
||||
double arrow_width
|
||||
CxxFlow_t flow_type
|
||||
string title
|
||||
void setFont(string)
|
||||
string flowType()
|
||||
void setFlowType(string) except +translate_exception
|
||||
string m_font
|
||||
void add(CxxReactionPathDiagram&) except +translate_exception
|
||||
void exportToDot(CxxStringStream&)
|
||||
void writeData(CxxStringStream&)
|
||||
void add(shared_ptr[CxxReactionPathDiagram]) except +translate_exception
|
||||
void displayOnly(size_t)
|
||||
|
||||
cdef cppclass CxxReactionPathBuilder "Cantera::ReactionPathBuilder":
|
||||
void init(CxxStringStream&, CxxKinetics&) except +translate_exception
|
||||
void build(CxxKinetics&, string&, CxxStringStream&, CxxReactionPathDiagram&, cbool)
|
||||
void build() except +translate_exception
|
||||
string getDot() except +translate_exception
|
||||
string getData() except +translate_exception
|
||||
string getLog() except +translate_exception
|
||||
|
||||
|
||||
cdef class ReactionPathDiagram:
|
||||
cdef CxxReactionPathDiagram diagram
|
||||
cdef CxxReactionPathBuilder builder
|
||||
cdef shared_ptr[CxxReactionPathDiagram] _diagram
|
||||
cdef CxxReactionPathDiagram* diagram
|
||||
cdef Kinetics kinetics
|
||||
cdef str element
|
||||
cdef pybool built
|
||||
cdef CxxStringStream* _log
|
||||
|
@ -2,32 +2,25 @@
|
||||
# at https://cantera.org/license.txt for license and copyright information.
|
||||
|
||||
from pathlib import Path
|
||||
from cython.operator cimport dereference as deref
|
||||
|
||||
from ._utils cimport *
|
||||
|
||||
|
||||
cdef class ReactionPathDiagram:
|
||||
def __cinit__(self, *args, **kwargs):
|
||||
self._log = new CxxStringStream()
|
||||
|
||||
def __dealloc__(self):
|
||||
del self._log
|
||||
|
||||
def __init__(self, Kinetics kin, str element):
|
||||
def __cinit__(self, _SolutionBase contents, str element, *args, **kwargs):
|
||||
"""
|
||||
Create a reaction path diagram for the fluxes of the element ``element``
|
||||
according the the net reaction rates determined by the `Kinetics` object
|
||||
``kin``.
|
||||
"""
|
||||
self.kinetics = kin
|
||||
self.builder.init(deref(self._log), deref(kin.kinetics))
|
||||
self.element = element
|
||||
self.built = False
|
||||
self.kinetics = contents
|
||||
cdef shared_ptr[CxxKinetics] cxx_kin = contents.base.kinetics()
|
||||
self._diagram = CxxNewReactionPathDiagram(cxx_kin, stringify(element))
|
||||
self.diagram = self._diagram.get()
|
||||
|
||||
property show_details:
|
||||
"""
|
||||
Get/Set whether to show the details of which reactions contribute to the
|
||||
flux.
|
||||
Get/Set whether to show the details of which reactions contribute to the flux.
|
||||
"""
|
||||
def __get__(self):
|
||||
return self.diagram.show_details
|
||||
@ -36,8 +29,7 @@ cdef class ReactionPathDiagram:
|
||||
|
||||
property threshold:
|
||||
"""
|
||||
Get/Set the threshold for the minimum flux relative value that will be
|
||||
plotted.
|
||||
Get/Set the threshold for the minimum flux relative value that will be plotted.
|
||||
"""
|
||||
def __get__(self):
|
||||
return self.diagram.threshold
|
||||
@ -113,18 +105,9 @@ cdef class ReactionPathDiagram:
|
||||
property flow_type:
|
||||
""" Get/Set the way flows are drawn. Either 'NetFlow' or 'OneWayFlow' """
|
||||
def __get__(self):
|
||||
if self.diagram.flow_type == CxxNetFlow:
|
||||
return 'NetFlow'
|
||||
else:
|
||||
return 'OneWayFlow'
|
||||
|
||||
return pystr(self.diagram.flowType())
|
||||
def __set__(self, str value):
|
||||
if value == 'OneWayFlow':
|
||||
self.diagram.flow_type = CxxOneWayFlow
|
||||
elif value == 'NetFlow':
|
||||
self.diagram.flow_type = CxxNetFlow
|
||||
else:
|
||||
raise ValueError('Invalid flow_type: {!r}'.format(value))
|
||||
self.diagram.setFlowType(stringify(value))
|
||||
|
||||
property arrow_width:
|
||||
""" Get/Set the arrow width. If < 0, then scale with flux value. """
|
||||
@ -142,7 +125,7 @@ cdef class ReactionPathDiagram:
|
||||
|
||||
def add(self, ReactionPathDiagram other):
|
||||
""" Add fluxes from `other` to this diagram """
|
||||
self.diagram.add(other.diagram)
|
||||
self.diagram.add(other._diagram)
|
||||
|
||||
def display_only(self, int k):
|
||||
"""
|
||||
@ -156,11 +139,7 @@ cdef class ReactionPathDiagram:
|
||||
Return a string containing the reaction path diagram formatted for use
|
||||
by Graphviz's 'dot' program.
|
||||
"""
|
||||
if not self.built:
|
||||
self.build()
|
||||
cdef CxxStringStream out
|
||||
self.diagram.exportToDot(out)
|
||||
return pystr(out.str())
|
||||
return pystr(self.diagram.getDot())
|
||||
|
||||
def write_dot(self, filename):
|
||||
"""
|
||||
@ -171,24 +150,16 @@ cdef class ReactionPathDiagram:
|
||||
|
||||
def get_data(self):
|
||||
"""
|
||||
Get a (roughly) human-readable representation of the reaction path
|
||||
diagram.
|
||||
Get a (roughly) human-readable representation of the reaction path diagram.
|
||||
"""
|
||||
if not self.built:
|
||||
self.build()
|
||||
cdef CxxStringStream out
|
||||
self.diagram.writeData(out)
|
||||
return pystr(out.str())
|
||||
return pystr(self.diagram.getData())
|
||||
|
||||
def build(self, verbose=False):
|
||||
"""
|
||||
Build the reaction path diagram. Called automatically by methods which
|
||||
return representations of the diagram, for example `write_dot()`.
|
||||
"""
|
||||
self.builder.build(deref(self.kinetics.kinetics),
|
||||
stringify(self.element), deref(self._log),
|
||||
self.diagram, True)
|
||||
self.built = True
|
||||
self.diagram.build()
|
||||
if verbose:
|
||||
print(self.log)
|
||||
|
||||
@ -197,4 +168,4 @@ cdef class ReactionPathDiagram:
|
||||
Logging messages generated while building the reaction path diagram
|
||||
"""
|
||||
def __get__(self):
|
||||
return pystr(self._log.str())
|
||||
return pystr(self.diagram.getLog())
|
||||
|
@ -25,14 +25,19 @@ _XML_PATH = _TAG_PATH / "doxygen" / "xml"
|
||||
@dataclass(frozen=True)
|
||||
@with_unpack_iter
|
||||
class TagInfo:
|
||||
"""Represents information parsed from a doxygen tag file."""
|
||||
"""
|
||||
Represents information parsed from a doxygen tag file.
|
||||
|
||||
May represent a member function or a variable.
|
||||
"""
|
||||
|
||||
base: str = "" #: Qualified scope (skipping Cantera namespace)
|
||||
type: str = "" #: Return type
|
||||
name: str = "" #: Function name
|
||||
name: str = "" #: Function/variable name
|
||||
arglist: str = "" #: Function argument list (original XML string)
|
||||
anchorfile: str = "" #: doxygen anchor file
|
||||
anchor: str = "" #: doxygen anchor
|
||||
kind: str = "" #: Member kind
|
||||
|
||||
@classmethod
|
||||
def from_xml(cls: Self, qualified_name: str, xml: str) -> Self:
|
||||
@ -47,7 +52,8 @@ class TagInfo:
|
||||
xml_tree.find("name").text,
|
||||
xml_tree.find("arglist").text,
|
||||
xml_tree.find("anchorfile").text.replace(".html", ".xml"),
|
||||
xml_tree.find("anchor").text)
|
||||
xml_tree.find("anchor").text,
|
||||
xml_tree.attrib.get("kind", ""))
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return all([self.type, self.name, self.arglist, self.anchorfile, self.anchor])
|
||||
@ -154,13 +160,15 @@ class TagFileParser:
|
||||
|
||||
# Get known functions from namespace and methods from classes
|
||||
self._known = xml_members("function", namespace)
|
||||
self._known.update(xml_members("variable", namespace))
|
||||
for name, cls in classes.items():
|
||||
prefix = f"{name}::"
|
||||
self._known.update(xml_members("function", cls, prefix))
|
||||
self._known.update(xml_members("variable", cls, prefix))
|
||||
|
||||
def exists(self, cxx_func: str) -> bool:
|
||||
def exists(self, cxx_member: str) -> bool:
|
||||
"""Check whether doxygen tag exists."""
|
||||
return cxx_func in self._known
|
||||
return cxx_member in self._known
|
||||
|
||||
def detect(self, name: str, bases: Iterable[str], permissive: bool = True) -> str:
|
||||
"""Detect qualified method name."""
|
||||
@ -178,17 +186,17 @@ class TagFileParser:
|
||||
|
||||
def tag_info(self, func_string: str) -> TagInfo:
|
||||
"""Look up tag information based on (partial) function signature."""
|
||||
cxx_func = func_string.split("(")[0].split(" ")[-1]
|
||||
if cxx_func not in self._known:
|
||||
msg = f"Could not find {cxx_func!r} in doxygen tag file."
|
||||
cxx_member = func_string.split("(")[0].split(" ")[-1]
|
||||
if cxx_member not in self._known:
|
||||
msg = f"Could not find {cxx_member!r} in doxygen tag file."
|
||||
_LOGGER.critical(msg)
|
||||
sys.exit(1)
|
||||
ix = 0
|
||||
if len(self._known[cxx_func]) > 1:
|
||||
if len(self._known[cxx_member]) > 1:
|
||||
# Disambiguate functions with same name
|
||||
# TODO: current approach does not use information on default arguments
|
||||
known_args = [ET.fromstring(xml).find("arglist").text
|
||||
for xml in self._known[cxx_func]]
|
||||
for xml in self._known[cxx_member]]
|
||||
known_args = [ArgList.from_xml(al).short_str() for al in known_args]
|
||||
args = re.findall(re.compile(r"(?<=\().*(?=\))"), func_string)
|
||||
if not args and "()" in known_args:
|
||||
@ -214,13 +222,18 @@ class TagFileParser:
|
||||
_LOGGER.critical(msg)
|
||||
sys.exit(1)
|
||||
|
||||
return TagInfo.from_xml(cxx_func, self._known[cxx_func][ix])
|
||||
return TagInfo.from_xml(cxx_member, self._known[cxx_member][ix])
|
||||
|
||||
def cxx_func(self, func_string: str) -> CFunc:
|
||||
"""Generate annotated C++ function specification."""
|
||||
def cxx_member(self, func_string: str, setter: bool = False) -> CFunc | Param:
|
||||
"""Generate annotated C++ function/variable specification."""
|
||||
details = tag_lookup(self.tag_info(func_string))
|
||||
ret_param = Param.from_xml(details.type)
|
||||
|
||||
if details.kind == "variable":
|
||||
direction = "in" if setter else "out"
|
||||
return Param(ret_param.p_type, details.name,
|
||||
details.briefdescription, direction, None, details.base)
|
||||
|
||||
# Merge attributes from doxygen signature and doxygen annotations
|
||||
args = ArgList.from_xml(details.arglist).params # from signature
|
||||
args_annotated = details.parameterlist # from documentation
|
||||
@ -250,7 +263,8 @@ def tag_lookup(tag_info: TagInfo) -> TagDetails:
|
||||
|
||||
xml_details = xml_file.read_text()
|
||||
id_ = tag_info.id
|
||||
regex = re.compile(rf'<memberdef kind="function" id="{id_}"[\s\S]*?</memberdef>')
|
||||
kind_ = tag_info.kind
|
||||
regex = re.compile(rf'<memberdef kind="{kind_}" id="{id_}"[\s\S]*?</memberdef>')
|
||||
matches = re.findall(regex, xml_details)
|
||||
|
||||
if not matches:
|
||||
|
@ -33,6 +33,10 @@ based on a recipe, which is subsequently used to scaffold API functions using de
|
||||
- `getter`: Implements a getter method of a C++ class.
|
||||
- `setter`: Implements a setter method of a C++ class.
|
||||
- `method`: Generic method of a C++ class.
|
||||
- `variable-getter`: Implements a getter for a C++ class member variable or a variable
|
||||
defined in the `Cantera` namespace. Functionality is inferred automatically.
|
||||
- `variable-setter`: Implements a getter for a C++ class member variable or a variable
|
||||
defined in the `Cantera` namespace. Field disambiguates from a `variable-getter`.
|
||||
- `noop`: No operation.
|
||||
- `reserved`: Reserved (hard-coded) CLib functions which include service functions for
|
||||
CLib storage (examples: `cabinetSize`, `parentHandle`) or functions that do not have
|
||||
@ -44,14 +48,15 @@ Recipes include all information required for the auto-generation of a correspond
|
||||
CLib function. Each recipe uses the following fields:
|
||||
|
||||
- `name`: Name of the CLib function to be generated (without prefix).
|
||||
- `implements`: Optional name or signature of the implemented C++ function/method. If
|
||||
left empty, *sourcegen* searches for doxygen tags matching the `name` field.
|
||||
A qualified name is sufficient if C++ functions/methods are unique, for example
|
||||
`Func1::type`. A full signature is required whenever shortened signatures with
|
||||
default arguments are used and/or multiple C++ function/method variants exist, for
|
||||
example `Phase::moleFraction(size_t)`.
|
||||
- `implements`: Optional name or signature of the implemented C++ function/method or
|
||||
variable. If left empty, *sourcegen* searches for doxygen tags matching the `name`
|
||||
field. A name is sufficient if C++ functions/methods are unique, for example
|
||||
`Func1::type`. A signature is required whenever shortened signatures with default
|
||||
arguments are used and/or multiple C++ function/method variants exist, for example
|
||||
`Phase::moleFraction(size_t)`. The scope (part preceding `::`) may be omitted, as
|
||||
it can be inferred based on the fields `base`, `parents` or `derived`.
|
||||
- `uses`: Optional list of auxiliary C++ class methods used by the CLib function. The
|
||||
exact usage depends on the type of the implemented CLib function.
|
||||
- `what`: Optional override for auto-detected recipe/CLib function type.
|
||||
- `brief`: Optional override for brief description from doxygen documentation.
|
||||
- `code`: Optional custom code to override auto-generated code.
|
||||
- `code`: Optional custom code to override auto-generated code (experimental).
|
||||
|
@ -12,6 +12,8 @@ recipes:
|
||||
- name: getGitCommit
|
||||
implements: gitCommit # inconsistent API (preexisting)
|
||||
- name: getCanteraError
|
||||
- name: setLogWriter
|
||||
- name: setLogCallback
|
||||
- name: addCanteraDirectory
|
||||
implements: addDirectory # inconsistent API (preexisting)
|
||||
- name: getDataDirectories
|
||||
@ -24,7 +26,21 @@ recipes:
|
||||
- name: suppress_thermo_warnings # inconsistent API (snake_case; preexisting)
|
||||
- name: use_legacy_rate_constants # inconsistent API (snake_case; preexisting)
|
||||
- name: appdelete
|
||||
- name: Avogadro
|
||||
- name: Boltzmann
|
||||
- name: Planck
|
||||
- name: ElectronCharge
|
||||
- name: lightSpeed
|
||||
- name: OneAtm
|
||||
- name: OneBar
|
||||
- name: fineStructureConstant
|
||||
- name: ElectronMass
|
||||
- name: GasConstant
|
||||
- name: StefanBoltz
|
||||
- name: Faraday
|
||||
- name: permeability0
|
||||
implements: permeability_0
|
||||
- name: epsilon0
|
||||
implements: epsilon_0
|
||||
- name: clearStorage
|
||||
- name: resetStorage
|
||||
- name: setLogWriter
|
||||
- name: setLogCallback
|
||||
|
85
interfaces/sourcegen/sourcegen/_data/ctrdiag_auto.yaml
Normal file
85
interfaces/sourcegen/sourcegen/_data/ctrdiag_auto.yaml
Normal file
@ -0,0 +1,85 @@
|
||||
# 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.
|
||||
|
||||
docstring: |-
|
||||
Auto-generated CLib API for %Cantera's ReactionPathDiagram class.
|
||||
Implements a replacement for CLib's traditional @c ctrpath library.
|
||||
prefix: rdiag3
|
||||
base: ReactionPathDiagram
|
||||
parents: [] # List of parent classes
|
||||
derived: [] # List of specializations
|
||||
recipes:
|
||||
- name: newReactionPathDiagram # absorbs rbuild_init
|
||||
- name: showDetails
|
||||
implements: show_details
|
||||
- name: setShowDetails # replacement for rdiag_detailed/rdiag_brief
|
||||
implements: show_details
|
||||
what: variable-setter
|
||||
- name: threshold
|
||||
- name: setThreshold
|
||||
implements: threshold
|
||||
what: variable-setter
|
||||
- name: boldThreshold
|
||||
implements: bold_min
|
||||
- name: setBoldThreshold
|
||||
implements: bold_min
|
||||
what: variable-setter
|
||||
- name: normalThreshold
|
||||
implements: dashed_max
|
||||
- name: setNormalThreshold
|
||||
implements: dashed_max
|
||||
what: variable-setter
|
||||
- name: labelThreshold
|
||||
implements: label_min
|
||||
- name: setLabelThreshold
|
||||
implements: label_min
|
||||
what: variable-setter
|
||||
- name: boldColor
|
||||
implements: bold_color
|
||||
- name: setBoldColor
|
||||
implements: bold_color
|
||||
what: variable-setter
|
||||
- name: normalColor
|
||||
implements: normal_color
|
||||
- name: setNormalColor
|
||||
implements: normal_color
|
||||
what: variable-setter
|
||||
- name: dashedColor
|
||||
implements: dashed_color
|
||||
- name: setDashedColor
|
||||
implements: dashed_color
|
||||
what: variable-setter
|
||||
- name: dotOptions
|
||||
implements: dot_options
|
||||
- name: setDotOptions
|
||||
implements: dot_options
|
||||
what: variable-setter
|
||||
- name: font
|
||||
implements: m_font
|
||||
- name: setFont
|
||||
- name: scale
|
||||
- name: setScale
|
||||
implements: scale
|
||||
what: variable-setter
|
||||
- name: flowType
|
||||
- name: setFlowType
|
||||
- name: arrowWidth
|
||||
implements: arrow_width
|
||||
- name: setArrowWidth
|
||||
implements: arrow_width
|
||||
what: variable-setter
|
||||
- name: title
|
||||
- name: setTitle
|
||||
implements: title
|
||||
what: variable-setter
|
||||
- name: add
|
||||
- name: displayOnly
|
||||
- name: getDot # previously part of rdiag_write
|
||||
- name: getData # previously part of rdiag_write
|
||||
- name: build # previously rbuild_build
|
||||
- name: getLog # New in Cantera 3.2
|
||||
- name: findMajor
|
||||
implements: findMajorPaths
|
||||
- name: del
|
||||
- name: cabinetSize
|
||||
- name: parentHandle
|
@ -14,7 +14,15 @@ recipes:
|
||||
- name: type # previously: ctkin_getReactionType
|
||||
- name: usesThirdBody
|
||||
- name: valid
|
||||
# - name: id <--- member variable (access not yet implemented)
|
||||
- name: id
|
||||
- name: setId
|
||||
implements: id
|
||||
what: variable-setter
|
||||
- name: allowNonreactantOrders
|
||||
implements: allow_nonreactant_orders
|
||||
- name: setAllowNonreactantOrders
|
||||
implements: allow_nonreactant_orders
|
||||
what: variable-setter
|
||||
- name: del
|
||||
- name: cabinetSize
|
||||
- name: parentHandle
|
||||
|
@ -23,6 +23,7 @@ class Param:
|
||||
description: str = "" #: Parameter description (optional annotation)
|
||||
direction: str = "" #: Direction of parameter (optional annotation)
|
||||
default: Any = None #: Default value (optional)
|
||||
base: str = "" #: Base (optional). Only used if param represents a member variable
|
||||
|
||||
@classmethod
|
||||
def from_str(cls: Self, param: str, doc: str = "") -> Self:
|
||||
@ -165,23 +166,28 @@ class CFunc(Func):
|
||||
if len(lines) == 1:
|
||||
return cls(*func, brief, None, "", "", [])
|
||||
returns = ""
|
||||
args = []
|
||||
doc_args = {p.name: p for p in func.arglist}
|
||||
for ix, line in enumerate(lines[:-1]):
|
||||
line = line.strip().lstrip("*").strip()
|
||||
if ix == 1 and not brief:
|
||||
brief = line
|
||||
elif line.startswith("@param"):
|
||||
# assume that variables are documented in order
|
||||
arg = func.arglist[len(args)].long_str()
|
||||
args.append(Param.from_str(arg, line))
|
||||
# match parameter name
|
||||
keys = [k for k in doc_args.keys() if line.split()[1] == k]
|
||||
if len(keys) == 1:
|
||||
key = keys[0]
|
||||
doc_args[key] = Param.from_str(doc_args[key].long_str(), line)
|
||||
elif line.startswith("@returns"):
|
||||
returns = line.lstrip("@returns").strip()
|
||||
args = ArgList(args)
|
||||
args = ArgList(list(doc_args.values()))
|
||||
return cls(func.ret_type, func.name, args, brief, None, returns, "", [])
|
||||
|
||||
def short_declaration(self) -> str:
|
||||
"""Return a short string representation."""
|
||||
ret = (f"{self.name}{self.arglist.short_str()}").strip()
|
||||
if self.arglist is None:
|
||||
ret = (f"{self.name}").strip()
|
||||
else:
|
||||
ret = (f"{self.name}{self.arglist.short_str()}").strip()
|
||||
if self.base:
|
||||
return f"{self.ret_type} {self.base}::{ret}"
|
||||
return f"{self.ret_type} {ret}"
|
||||
|
@ -164,8 +164,8 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
sys.exit(1)
|
||||
return params
|
||||
|
||||
@staticmethod
|
||||
def _reverse_crosswalk(c_func: CFunc, base: str) -> tuple[dict[str, str], set[str]]:
|
||||
def _reverse_crosswalk(
|
||||
self, c_func: CFunc, base: str) -> tuple[dict[str, str], set[str]]:
|
||||
"""Translate CLib arguments back to Jinja argument list."""
|
||||
handle = ""
|
||||
args = []
|
||||
@ -180,28 +180,41 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
return cxx_type.split("<")[-1].split(">")[0]
|
||||
|
||||
c_args = c_func.arglist
|
||||
cxx_func = c_func.implements
|
||||
if not cxx_func:
|
||||
cxx_member = c_func.implements
|
||||
if not cxx_member:
|
||||
if c_func.name.endswith("new"):
|
||||
# Default constructor
|
||||
cxx_func = CFunc("auto", f"make_shared<{base}>", ArgList([]), "", None)
|
||||
cxx_func = CFunc("auto", f"make_shared<{base}>", ArgList([]))
|
||||
elif len(c_args) and "char*" in c_args[-1].p_type:
|
||||
cxx_func = CFunc("string", "dummy", ArgList([]), "", None, "", "base")
|
||||
else:
|
||||
cxx_func = CFunc("void", "dummy", ArgList([]), "", None, "", "base")
|
||||
elif isinstance(cxx_member, Param):
|
||||
if len(c_args) and "char*" in c_args[-1].p_type:
|
||||
cxx_func = CFunc("string", cxx_member.name, None,
|
||||
"", None, "", cxx_member.base)
|
||||
else:
|
||||
cxx_func = CFunc(cxx_member.p_type, cxx_member.name, None,
|
||||
"", None, "", cxx_member.base)
|
||||
else:
|
||||
cxx_func = cxx_member
|
||||
cxx_ix = 0
|
||||
check_array = False
|
||||
for c_ix, c_par in enumerate(c_func.arglist):
|
||||
c_name = c_par.name
|
||||
if cxx_ix >= len(cxx_func.arglist):
|
||||
if isinstance(cxx_member, Param) or cxx_ix >= len(cxx_func.arglist):
|
||||
if c_ix == 0 and cxx_func.base and "len" not in c_name.lower():
|
||||
handle = c_name
|
||||
c_ix += 1
|
||||
if c_ix == len(c_args):
|
||||
if isinstance(cxx_member, Param) and cxx_member.direction == "out":
|
||||
pass
|
||||
elif isinstance(cxx_member, Param):
|
||||
break
|
||||
elif c_ix == len(c_args):
|
||||
break
|
||||
cxx_type = cxx_func.ret_type
|
||||
|
||||
# Handle output buffer
|
||||
# Handle output buffer and/or variable assignments
|
||||
cxx_type = cxx_func.ret_type
|
||||
if "string" in cxx_type:
|
||||
buffer = ["string out",
|
||||
f"copyString(out, {c_args[c_ix+1].name}, "
|
||||
@ -212,6 +225,11 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
"std::copy(out.begin(), out.end(), "
|
||||
f"{c_args[c_ix+1].name});",
|
||||
"int(out.size())"]
|
||||
elif "bool" in cxx_type:
|
||||
buffer = [f"{cxx_type} out", "", "int(out)"]
|
||||
elif cxx_type in self._config.ret_type_crosswalk:
|
||||
# can pass values directly
|
||||
buffer = []
|
||||
else:
|
||||
msg = (f"Scaffolding failed for {c_func.name!r}: reverse crosswalk "
|
||||
f"not implemented for {cxx_type!r}:\n{c_func.declaration()}")
|
||||
@ -219,7 +237,10 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
exit(1)
|
||||
break
|
||||
|
||||
cxx_arg = cxx_func.arglist[cxx_ix]
|
||||
if isinstance(cxx_member, Param):
|
||||
cxx_arg = cxx_member
|
||||
else:
|
||||
cxx_arg = cxx_func.arglist[cxx_ix]
|
||||
if c_name != cxx_arg.name:
|
||||
# Encountered object handle or length indicator
|
||||
if c_ix == 0:
|
||||
@ -306,6 +327,14 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
elif cxx_rtype.endswith("void"):
|
||||
buffer = ["", "", "0"]
|
||||
|
||||
if isinstance(cxx_member, Param) and cxx_member.direction == "in":
|
||||
c_name = c_func.arglist[-1].name
|
||||
if cxx_rtype.endswith("bool"):
|
||||
lines = [f"bool {c_name}_ = ({c_name} != 0);"]
|
||||
args.append(f"{c_name}_")
|
||||
else:
|
||||
args.append(c_name)
|
||||
|
||||
ret = {
|
||||
"base": base, "handle": handle, "lines": lines, "buffer": buffer,
|
||||
"shared": shared, "checks": checks, "error": error, "cxx_rbase": cxx_rbase,
|
||||
@ -333,6 +362,12 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
elif recipe.what == "function":
|
||||
template = loader.from_string(self._templates["clib-function"])
|
||||
|
||||
elif recipe.what == "variable-getter":
|
||||
template = loader.from_string(self._templates["clib-variable-getter"])
|
||||
|
||||
elif recipe.what == "variable-setter":
|
||||
template = loader.from_string(self._templates["clib-variable-setter"])
|
||||
|
||||
elif recipe.what == "constructor":
|
||||
template = loader.from_string(self._templates["clib-constructor"])
|
||||
|
||||
@ -376,7 +411,9 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
|
||||
def _resolve_recipe(self, recipe: Recipe) -> CFunc:
|
||||
"""Build CLib header from recipe and doxygen annotations."""
|
||||
def merge_params(implements: str, cxx_func: CFunc) -> tuple[list[Param], int]:
|
||||
def merge_params(
|
||||
implements: str, cxx_member: CFunc | Param
|
||||
) -> tuple[list[Param], CFunc]:
|
||||
"""Create preliminary CLib argument list."""
|
||||
obj_handle = []
|
||||
if "::" in implements:
|
||||
@ -384,22 +421,28 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
what = implements.split("::")[0]
|
||||
obj_handle.append(
|
||||
Param("int", "handle", f"Handle to queried {what} object."))
|
||||
if isinstance(cxx_member, Param):
|
||||
if recipe.what.endswith("setter"):
|
||||
return obj_handle + [cxx_member], cxx_member
|
||||
return obj_handle, cxx_member
|
||||
|
||||
if "(" not in implements:
|
||||
return obj_handle + cxx_func.arglist.params, cxx_func
|
||||
return obj_handle + cxx_member.arglist.params, cxx_member
|
||||
|
||||
# Signature may skip C++ default parameters
|
||||
args_short = CFunc.from_str(implements).arglist
|
||||
if len(args_short) < len(cxx_func.arglist):
|
||||
cxx_arglist = ArgList(cxx_func.arglist[:len(args_short)])
|
||||
cxx_func = CFunc(cxx_func.ret_type, cxx_func.name,
|
||||
cxx_arglist, cxx_func.brief, cxx_func.implements,
|
||||
cxx_func.returns, cxx_func.base, cxx_func.uses)
|
||||
if len(args_short) < len(cxx_member.arglist):
|
||||
cxx_arglist = ArgList(cxx_member.arglist[:len(args_short)])
|
||||
cxx_member = CFunc(cxx_member.ret_type, cxx_member.name,
|
||||
cxx_arglist, cxx_member.brief, cxx_member.implements,
|
||||
cxx_member.returns, cxx_member.base, cxx_member.uses)
|
||||
|
||||
return obj_handle + cxx_func.arglist.params, cxx_func
|
||||
return obj_handle + cxx_member.arglist.params, cxx_member
|
||||
|
||||
func_name = f"{recipe.prefix}_{recipe.name}"
|
||||
reserved = ["cabinetSize", "parentHandle",
|
||||
"getCanteraError", "clearStorage", "resetStorage"]
|
||||
"getCanteraError", "setLogWriter", "setLogCallback",
|
||||
"clearStorage", "resetStorage"]
|
||||
if recipe.name in reserved:
|
||||
recipe.what = "reserved"
|
||||
loader = Environment(loader=BaseLoader)
|
||||
@ -414,10 +457,14 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
bases = [recipe.base] + recipe.parents + recipe.derived
|
||||
if not recipe.implements:
|
||||
recipe.implements = self._doxygen_tags.detect(recipe.name, bases)
|
||||
elif recipe.base and "::" not in recipe.implements:
|
||||
parts = list(recipe.implements.partition("("))
|
||||
parts[0] = self._doxygen_tags.detect(parts[0], bases)
|
||||
recipe.implements = "".join(parts)
|
||||
recipe.uses = [self._doxygen_tags.detect(uu.split("(")[0], bases, False)
|
||||
for uu in recipe.uses]
|
||||
|
||||
cxx_func = None
|
||||
cxx_member = None
|
||||
ret_param = Param("void")
|
||||
args = []
|
||||
brief = ""
|
||||
@ -425,40 +472,54 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
if recipe.implements:
|
||||
msg = f" generating {func_name!r} -> {recipe.implements}"
|
||||
_LOGGER.debug(msg)
|
||||
parts = list(recipe.implements.partition("("))
|
||||
if not self._doxygen_tags.exists(parts[0]):
|
||||
parts[0] = self._doxygen_tags.detect(parts[0], bases, False)
|
||||
recipe.implements = "".join(parts)
|
||||
cxx_func = self._doxygen_tags.cxx_func(recipe.implements)
|
||||
cxx_member = self._doxygen_tags.cxx_member(
|
||||
recipe.implements, recipe.what.endswith("setter"))
|
||||
|
||||
# Convert C++ return type to format suitable for crosswalk:
|
||||
# Incompatible return parameters are buffered and appended to back
|
||||
ret_param, buffer_params = self._ret_crosswalk(
|
||||
cxx_func.ret_type, recipe.derived)
|
||||
par_list, cxx_func = merge_params(recipe.implements, cxx_func)
|
||||
prop_params = self._prop_crosswalk(par_list)
|
||||
brief = cxx_func.brief
|
||||
args = prop_params + buffer_params
|
||||
if isinstance(cxx_member, CFunc):
|
||||
# Convert C++ return type to format suitable for crosswalk:
|
||||
# Incompatible return parameters are buffered and appended to back
|
||||
ret_param, buffer_params = self._ret_crosswalk(
|
||||
cxx_member.ret_type, recipe.derived)
|
||||
par_list, cxx_member = merge_params(recipe.implements, cxx_member)
|
||||
prop_params = self._prop_crosswalk(par_list)
|
||||
args = prop_params + buffer_params
|
||||
brief = cxx_member.brief
|
||||
elif recipe.what == "variable-setter":
|
||||
ret_param = Param("int")
|
||||
par_list, cxx_member = merge_params(recipe.implements, cxx_member)
|
||||
args = self._prop_crosswalk(par_list)
|
||||
brief = cxx_member.description
|
||||
else:
|
||||
# Variable getter
|
||||
prop_params, cxx_member = merge_params(recipe.implements, cxx_member)
|
||||
ret_param, buffer_params = self._ret_crosswalk(
|
||||
cxx_member.p_type, recipe.derived)
|
||||
args = prop_params + buffer_params
|
||||
brief = cxx_member.description
|
||||
|
||||
if recipe.what and cxx_func:
|
||||
if recipe.what and cxx_member:
|
||||
# Recipe type and corresponding C++ function are known
|
||||
pass
|
||||
|
||||
elif cxx_func:
|
||||
elif cxx_member and isinstance(cxx_member, Param):
|
||||
# Recipe represents a variable getter/setter
|
||||
recipe.what = "variable-getter"
|
||||
|
||||
elif cxx_member:
|
||||
# Autodetection of CLib function purpose ("what")
|
||||
cxx_arglen = len(cxx_func.arglist)
|
||||
if not cxx_func.base:
|
||||
if (cxx_func.name.startswith("new") and
|
||||
any(base in cxx_func.ret_type
|
||||
cxx_arglen = len(cxx_member.arglist)
|
||||
if not cxx_member.base:
|
||||
if (cxx_member.name.startswith("new") and
|
||||
any(base in cxx_member.ret_type
|
||||
for base in [recipe.base] + recipe.derived)):
|
||||
recipe.what = "constructor"
|
||||
else:
|
||||
recipe.what = "function"
|
||||
elif "void" not in cxx_func.ret_type and cxx_arglen == 0:
|
||||
elif "void" not in cxx_member.ret_type and cxx_arglen == 0:
|
||||
recipe.what = "getter"
|
||||
elif "void" in cxx_func.ret_type and cxx_arglen == 1:
|
||||
p_type = cxx_func.arglist[0].p_type
|
||||
if cxx_func.name.startswith("get"):
|
||||
elif "void" in cxx_member.ret_type and cxx_arglen == 1:
|
||||
p_type = cxx_member.arglist[0].p_type
|
||||
if cxx_member.name.startswith("get"):
|
||||
recipe.what = "getter"
|
||||
elif "*" in p_type and not p_type.startswith("const"):
|
||||
recipe.what = "getter" # getter assigns to existing array
|
||||
@ -505,8 +566,8 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
|
||||
if recipe.brief:
|
||||
brief = recipe.brief
|
||||
uses = [self._doxygen_tags.cxx_func(uu) for uu in recipe.uses]
|
||||
return CFunc(ret_param.p_type, func_name, ArgList(args), brief, cxx_func,
|
||||
uses = [self._doxygen_tags.cxx_member(uu) for uu in recipe.uses]
|
||||
return CFunc(ret_param.p_type, func_name, ArgList(args), brief, cxx_member,
|
||||
ret_param.description, None, uses)
|
||||
|
||||
def _write_header(self, headers: HeaderFile) -> None:
|
||||
@ -528,11 +589,13 @@ class CLibSourceGenerator(SourceGenerator):
|
||||
annotations=self._scaffold_annotation(c_func, recipe.what)))
|
||||
declarations = "\n\n".join(declarations)
|
||||
|
||||
preamble = self._config.preambles.get(headers.base)
|
||||
|
||||
guard = f"__{filename.name.upper().replace('.', '_')}__"
|
||||
template = loader.from_string(self._templates["clib-header-file"])
|
||||
output = template.render(
|
||||
name=filename.stem, guard=guard, declarations=declarations,
|
||||
prefix=headers.prefix, base=headers.base, docstring=headers.docstring)
|
||||
name=filename.stem, guard=guard, preamble=preamble, prefix=headers.prefix,
|
||||
declarations=declarations, base=headers.base, docstring=headers.docstring)
|
||||
|
||||
out = (Path(self._out_dir) /
|
||||
"include" / "cantera" / "clib_experimental" / filename.name)
|
||||
|
@ -17,6 +17,7 @@ class Config:
|
||||
"int": "int",
|
||||
"size_t": "int",
|
||||
"double": "double",
|
||||
"const double": "double",
|
||||
"shared_ptr<T>": "int",
|
||||
"string": "char*",
|
||||
"const string": "char*",
|
||||
@ -34,6 +35,7 @@ class Config:
|
||||
"double* const": "double*",
|
||||
"const double*": "const double*",
|
||||
"const double* const": "const double*",
|
||||
"string": "const char*",
|
||||
"const string&": "const char*",
|
||||
"shared_ptr<T>": "int",
|
||||
"const shared_ptr<T>": "int",
|
||||
@ -41,9 +43,13 @@ class Config:
|
||||
"const vector<shared_ptr<T>>&": "int[]",
|
||||
}
|
||||
|
||||
includes: dict[str, list[str]]
|
||||
preambles: dict[str, str] #: Preamble text for each header file
|
||||
|
||||
includes: dict[str, list[str]] #: Include directives for each implementation file
|
||||
|
||||
@classmethod
|
||||
def from_parsed(cls: Self, *, includes: dict[str, list[str]] | None = None) -> Self:
|
||||
def from_parsed(cls: Self, *,
|
||||
preambles: dict[str, str] | None = None,
|
||||
includes: dict[str, list[str]] | None = None) -> Self:
|
||||
"""Create dataclass while including information parsed externally."""
|
||||
return cls(includes or {})
|
||||
return cls(preambles or {}, includes or {})
|
||||
|
@ -8,13 +8,18 @@ ignore_files: []
|
||||
|
||||
# Dictionary of file names and list of functions to ignore.
|
||||
# Example: ctkin_auto.yaml: [phase]
|
||||
ignore_funcs:
|
||||
ct_auto.yaml: [setLogWriter, setLogCallback]
|
||||
ignore_funcs: {}
|
||||
|
||||
# Cabinets with associated includes
|
||||
# Cabinets with associated preambles (headers)
|
||||
preambles:
|
||||
"": |-
|
||||
#include "../clib/clib_defs.h"
|
||||
|
||||
# Cabinets with associated includes (implementation files)
|
||||
includes:
|
||||
"":
|
||||
- cantera/base/global.h
|
||||
- cantera/base/ExternalLogger.h
|
||||
Solution:
|
||||
- cantera/base/Solution.h
|
||||
Interface:
|
||||
@ -32,5 +37,7 @@ includes:
|
||||
- cantera/thermo/ThermoPhase.h
|
||||
Reaction:
|
||||
- cantera/kinetics/Reaction.h
|
||||
ReactionPathDiagram:
|
||||
- cantera/kinetics/ReactionPath.h
|
||||
Func1:
|
||||
- cantera/numerics/Func1Factory.h
|
||||
|
@ -53,6 +53,19 @@ clib-reserved-getCanteraError-h: |-
|
||||
*/
|
||||
int {{ prefix }}_getCanteraError(int bufLen, char* buf);
|
||||
|
||||
clib-reserved-setLogWriter-h: |-
|
||||
/**
|
||||
* Undocumented.
|
||||
*/
|
||||
int {{ prefix }}_setLogWriter(void* logger);
|
||||
|
||||
clib-reserved-setLogCallback-h: |-
|
||||
/**
|
||||
* Undocumented.
|
||||
* @param writer Callback that is invoked to produce log output.
|
||||
*/
|
||||
int {{ prefix }}_setLogCallback(LogCallback writer);
|
||||
|
||||
clib-reserved-resetStorage-h: |-
|
||||
/**
|
||||
* Delete all objects and erase mapping.
|
||||
@ -98,6 +111,10 @@ clib-header-file: |-
|
||||
#ifndef {{ guard }}
|
||||
#define {{ guard }}
|
||||
|
||||
{% if preamble %}
|
||||
{{ preamble }}
|
||||
|
||||
{% endif %}
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -133,6 +150,81 @@ clib-function: |-
|
||||
return handleAllExceptions({{ error[0] }}, {{ error[1] }});
|
||||
}
|
||||
|
||||
clib-variable-getter: |-
|
||||
// variable (getter): {{ cxx_implements }}
|
||||
try {
|
||||
{% if handle %}
|
||||
## access class member variable
|
||||
{% if cxx_base == base %}
|
||||
## object can be accessed directly
|
||||
{% if buffer and buffer[0] %}
|
||||
{{ buffer[0] }} = {{ base }}Cabinet::at({{ handle }})->{{ cxx_name }};
|
||||
## CLib/C++ variable crosswalk needed
|
||||
{% if buffer[1] %}
|
||||
{{ buffer[1] }}
|
||||
{% endif %}
|
||||
return {{ buffer[2] }};
|
||||
{% else %}{# not buffer[0] #}
|
||||
## no crosswalk needed
|
||||
return {{ base }}Cabinet::at({{ handle }})->{{ cxx_name }};
|
||||
{% endif %}{# buffer[0] #}
|
||||
{% else %}{# base #}
|
||||
## object needs a cast as method is defined for specialization
|
||||
{% if buffer and buffer[0] %}
|
||||
{{ buffer[0] }} = {{ base }}Cabinet::as<{{ cxx_base }}>({{ handle }})->{{ cxx_name }};
|
||||
## CLib/C++ variable crosswalk needed
|
||||
{% if buffer[1] %}
|
||||
{{ buffer[1] }}
|
||||
{% endif %}
|
||||
return {{ buffer[2] }};
|
||||
{% else %}{# not buffer[0] #}
|
||||
## no crosswalk needed
|
||||
return {{ base }}Cabinet::as<{{ cxx_base }}>({{ handle }})->{{ cxx_name }};
|
||||
{% endif %}{# buffer[0] #}
|
||||
{% endif %}{# base #}
|
||||
{% else %}{# not handle #}
|
||||
## variable is defined in root namespace
|
||||
{% if buffer and buffer[0] %}
|
||||
## CLib/C++ variable crosswalk needed
|
||||
{{ buffer[0] }} = {{ cxx_name }};
|
||||
{% if buffer[1] %}
|
||||
{{ buffer[1] }}
|
||||
{% endif %}
|
||||
return {{ buffer[2] }};
|
||||
{% else %}{# not buffer[0] #}
|
||||
## no crosswalk needed
|
||||
return {{ cxx_name }};
|
||||
{% endif %}{# buffer[0] #}
|
||||
{% endif %}{# handle #}
|
||||
} catch (...) {
|
||||
return handleAllExceptions({{ error[0] }}, {{ error[1] }});
|
||||
}
|
||||
|
||||
clib-variable-setter: |-
|
||||
// variable (setter): {{ cxx_implements }}
|
||||
try {
|
||||
{% for line in lines %}
|
||||
## add lines used for CLib/C++ variable crosswalk
|
||||
{{ line }}
|
||||
{% endfor %}
|
||||
{% if handle %}
|
||||
## access class member variable
|
||||
{% if cxx_base == base %}
|
||||
## object can be accessed directly
|
||||
{{ base }}Cabinet::at({{ handle }})->{{ cxx_name }} = {{ cxx_args[0] }};
|
||||
{% else %}{# not base #}
|
||||
## object needs a cast as method is defined for specialization
|
||||
{{ base }}Cabinet::as<{{ cxx_base }}>({{ handle }})->{{ cxx_name }} = {{ cxx_args[0] }};
|
||||
{% endif %}{# base #}
|
||||
{% else %}{# not handle #}
|
||||
## variable is defined in root namespace
|
||||
{{ cxx_name }} = {{ cxx_args[0] }};
|
||||
{% endif %}{# handle #}
|
||||
return 0;
|
||||
} catch (...) {
|
||||
return handleAllExceptions(-1, ERR);
|
||||
}
|
||||
|
||||
clib-constructor: |-
|
||||
## CLib constructor template: instantiates new object
|
||||
// constructor: {{ cxx_implements }}
|
||||
@ -295,7 +387,7 @@ clib-array-getter: |-
|
||||
// no size checking specified
|
||||
{% endif %}
|
||||
{% if buffer and buffer[0] %}
|
||||
{{ buffer[0] }} = obj->{{ cxx_name }}({{ cxx_args[0] }});;
|
||||
{{ buffer[0] }} = obj->{{ cxx_name }}({{ cxx_args[0] }});
|
||||
{% if buffer[1] %}
|
||||
{{ buffer[1] }}
|
||||
{% endif %}
|
||||
@ -374,6 +466,27 @@ clib-reserved-getCanteraError-cpp: |-
|
||||
return handleAllExceptions(-1, ERR);
|
||||
}
|
||||
|
||||
clib-reserved-setLogWriter-cpp: |-
|
||||
// reserved: setLogger
|
||||
try {
|
||||
Logger* logwriter = (Logger*)logger;
|
||||
setLogger(logwriter);
|
||||
return 0;
|
||||
} catch (...) {
|
||||
return handleAllExceptions(-1, ERR);
|
||||
}
|
||||
|
||||
clib-reserved-setLogCallback-cpp: |-
|
||||
// reserved: setLogger
|
||||
static unique_ptr<Logger> logwriter;
|
||||
try {
|
||||
logwriter = make_unique<ExternalLogger>(writer);
|
||||
setLogger(logwriter.get());
|
||||
return 0;
|
||||
} catch (...) {
|
||||
return handleAllExceptions(-1, ERR);
|
||||
}
|
||||
|
||||
clib-reserved-resetStorage-cpp: |-
|
||||
// reserved: void Cabinet<T>::reset()
|
||||
try {
|
||||
|
@ -6,6 +6,7 @@
|
||||
// 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"
|
||||
#include "cantera/kinetics/ReactionPath.h"
|
||||
#include "cantera/kinetics/Reaction.h"
|
||||
#include "cantera/thermo/ThermoPhase.h"
|
||||
@ -78,6 +79,17 @@ void Path::writeLabel(ostream& s, double threshold)
|
||||
}
|
||||
}
|
||||
|
||||
ReactionPathDiagram::ReactionPathDiagram(
|
||||
shared_ptr<Kinetics> kin, const string& element_) : element(element_), m_kin(kin)
|
||||
{
|
||||
if (!m_kin) {
|
||||
throw CanteraError("ReactionPathDiagram::ReactionPathDiagram",
|
||||
"Kinetics object must not be empty.");
|
||||
}
|
||||
m_builder = make_shared<ReactionPathBuilder>();
|
||||
m_builder->init(m_log, *m_kin.get());
|
||||
}
|
||||
|
||||
ReactionPathDiagram::~ReactionPathDiagram()
|
||||
{
|
||||
// delete the nodes
|
||||
@ -125,6 +137,11 @@ void ReactionPathDiagram::add(ReactionPathDiagram& d)
|
||||
}
|
||||
}
|
||||
|
||||
void ReactionPathDiagram::add(shared_ptr<ReactionPathDiagram> d)
|
||||
{
|
||||
add(*d.get());
|
||||
}
|
||||
|
||||
void ReactionPathDiagram::findMajorPaths(double athreshold, size_t lda, double* a)
|
||||
{
|
||||
double netmax = 0.0;
|
||||
@ -148,6 +165,57 @@ void ReactionPathDiagram::findMajorPaths(double athreshold, size_t lda, double*
|
||||
}
|
||||
}
|
||||
|
||||
const string ReactionPathDiagram::flowType() const
|
||||
{
|
||||
if (flow_type == OneWayFlow) {
|
||||
return "OneWayFlow";
|
||||
}
|
||||
return "NetFlow";
|
||||
}
|
||||
|
||||
void ReactionPathDiagram::setFlowType(const string& fType)
|
||||
{
|
||||
if (fType == "OneWayFlow") {
|
||||
flow_type = OneWayFlow;
|
||||
} else if (fType == "NetFlow") {
|
||||
flow_type = NetFlow;
|
||||
} else {
|
||||
throw CanteraError("ReactionPathDiagram::setFlowType",
|
||||
"Unknown flow type '{}'", fType);
|
||||
}
|
||||
}
|
||||
|
||||
void ReactionPathDiagram::build()
|
||||
{
|
||||
m_builder->build(*m_kin.get(), element, m_log, *this, true);
|
||||
m_isBuilt = true;
|
||||
}
|
||||
|
||||
string ReactionPathDiagram::getDot()
|
||||
{
|
||||
if (!m_isBuilt) {
|
||||
build();
|
||||
}
|
||||
std::stringstream out;
|
||||
exportToDot(out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
string ReactionPathDiagram::getData()
|
||||
{
|
||||
if (!m_isBuilt) {
|
||||
build();
|
||||
}
|
||||
std::stringstream out;
|
||||
writeData(out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
string ReactionPathDiagram::getLog()
|
||||
{
|
||||
return m_log.str();
|
||||
}
|
||||
|
||||
void ReactionPathDiagram::writeData(ostream& s)
|
||||
{
|
||||
s << title << endl;
|
||||
@ -791,4 +859,11 @@ int ReactionPathBuilder::build(Kinetics& s, const string& element,
|
||||
return 1;
|
||||
}
|
||||
|
||||
shared_ptr<ReactionPathDiagram> newReactionPathDiagram(
|
||||
shared_ptr<Kinetics> kin, const string& element)
|
||||
{
|
||||
return shared_ptr<ReactionPathDiagram>(
|
||||
new ReactionPathDiagram(kin, element));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -209,6 +209,13 @@ TEST(ct3, transport)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ct3, constants)
|
||||
{
|
||||
ASSERT_EQ(ct3_Avogadro(), Avogadro);
|
||||
ASSERT_EQ(ct3_GasConstant(), GasConstant);
|
||||
ASSERT_EQ(ct3_OneAtm(), OneAtm);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
@ -10,9 +10,11 @@
|
||||
#include "cantera/clib_experimental/ctthermo3.h"
|
||||
#include "cantera/clib_experimental/ctkin3.h"
|
||||
#include "cantera/clib_experimental/ctrxn3.h"
|
||||
#include "cantera/clib_experimental/ctrdiag3.h"
|
||||
|
||||
using namespace Cantera;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::StartsWith;
|
||||
|
||||
string reportError(); // forward declaration
|
||||
|
||||
@ -59,7 +61,7 @@ TEST(ctkin3, exceptions)
|
||||
EXPECT_THAT(err, HasSubstr("IndexError: 998 outside valid range of 0 to 28."));
|
||||
}
|
||||
|
||||
TEST(ctkin3, reaction)
|
||||
TEST(ctrxn3, reaction)
|
||||
{
|
||||
int sol0 = sol3_newSolution("h2o2.yaml", "", "none");
|
||||
int kin = sol3_kinetics(sol0);
|
||||
@ -72,15 +74,76 @@ TEST(ctkin3, reaction)
|
||||
int buflen = rxn3_type(rxn, 0, 0);
|
||||
vector<char> buf(buflen);
|
||||
rxn3_type(rxn, buflen, buf.data());
|
||||
string rxnType = buf.data();
|
||||
ASSERT_EQ(rxnType, reaction->type());
|
||||
ASSERT_EQ(rxnType, "three-body-Arrhenius");
|
||||
string text = buf.data();
|
||||
ASSERT_EQ(text, reaction->type());
|
||||
ASSERT_EQ(text, "three-body-Arrhenius");
|
||||
|
||||
buflen = rxn3_equation(rxn, 0, 0);
|
||||
buf.resize(buflen);
|
||||
rxn3_equation(rxn, buflen, buf.data());
|
||||
string rxnEqn = buf.data();
|
||||
ASSERT_EQ(rxnEqn, reaction->equation());
|
||||
ASSERT_EQ(rxnEqn, "2 O + M <=> O2 + M");
|
||||
text = buf.data();
|
||||
ASSERT_EQ(text, reaction->equation());
|
||||
ASSERT_EQ(text, "2 O + M <=> O2 + M");
|
||||
ASSERT_EQ(rxn3_usesThirdBody(rxn), 1);
|
||||
ASSERT_EQ(rxn3_allowNonreactantOrders(rxn), 0);
|
||||
ASSERT_EQ(rxn3_allowNonreactantOrders(rxn),
|
||||
int(reaction->allow_nonreactant_orders));
|
||||
|
||||
buflen = rxn3_id(rxn, 0, 0);
|
||||
buf.resize(buflen);
|
||||
rxn3_id(rxn, buflen, buf.data());
|
||||
text = buf.data();
|
||||
ASSERT_EQ(text, reaction->id);
|
||||
ASSERT_EQ(text, "");
|
||||
|
||||
rxn3_setId(rxn, "spam");
|
||||
buflen = rxn3_id(rxn, 0, 0);
|
||||
buf.resize(buflen);
|
||||
rxn3_id(rxn, buflen, buf.data());
|
||||
text = buf.data();
|
||||
ASSERT_EQ(text, "spam");
|
||||
}
|
||||
|
||||
TEST(ctrdiag3, diagram)
|
||||
{
|
||||
int sol = sol3_newSolution("h2o2.yaml", "", "none");
|
||||
int thermo = sol3_thermo(sol);
|
||||
int kin = sol3_kinetics(sol);
|
||||
thermo3_set_TP(thermo, 1000., ct3_OneAtm());
|
||||
|
||||
int diag = rdiag3_newReactionPathDiagram(kin, "H");
|
||||
ASSERT_EQ(rdiag3_boldThreshold(diag), 0.2);
|
||||
|
||||
rdiag3_setFlowType(diag, "spam");
|
||||
string text = reportError();
|
||||
EXPECT_THAT(text, HasSubstr("Unknown flow type 'spam'"));
|
||||
int buflen = rdiag3_flowType(diag, 0, 0);
|
||||
vector<char> buf(buflen);
|
||||
rdiag3_flowType(diag, buflen, buf.data());
|
||||
text = buf.data();
|
||||
ASSERT_EQ(text, "NetFlow");
|
||||
rdiag3_setFlowType(diag, "OneWayFlow");
|
||||
buflen = rdiag3_flowType(diag, 0, 0);
|
||||
buf.resize(buflen);
|
||||
rdiag3_flowType(diag, buflen, buf.data());
|
||||
text = buf.data();
|
||||
ASSERT_EQ(text, "OneWayFlow");
|
||||
|
||||
buflen = rdiag3_getLog(diag, 0, 0);
|
||||
buf.resize(buflen);
|
||||
rdiag3_getLog(diag, buflen, buf.data());
|
||||
text = buf.data();
|
||||
EXPECT_THAT(text, StartsWith("\nReaction 1:"));
|
||||
|
||||
buflen = rdiag3_getData(diag, 0, 0);
|
||||
buf.resize(buflen);
|
||||
rdiag3_getData(diag, buflen, buf.data());
|
||||
text = buf.data();
|
||||
EXPECT_THAT(text, StartsWith("\nH H2 \nH H2"));
|
||||
|
||||
buflen = rdiag3_getDot(diag, 0, 0);
|
||||
buf.resize(buflen);
|
||||
rdiag3_getDot(diag, buflen, buf.data());
|
||||
text = buf.data();
|
||||
EXPECT_THAT(text, StartsWith("digraph reaction_paths"));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user