.. role:: new :class: new .. _ch-advanced_features: Advanced features ================= This chapter explains the advanced features that ChemApp for Python has beyond the abilities of the C/Fortran ChemApp, such as using the state functions and object oriented approach to model processes. Content Overview ---------------- * Result objects ``chemapp.core.EquilibriumCalculationResult`` and ``chemapp.core.StreamCalculationResult`` * ChemApp logging * ``chemapp.core.Object`` * Input using chemical formulae using :py:meth:`~chemapp.friendly.EquilibriumCalculation.set_IA_cfs` * :ref:`Update checks & persistent configuration ` One of the core features of ChemApp for Python is taking advantage of object oriented structures to allow using calculation results of previous calculations as input for further calculations, and therefore allowing e.g. process models to be rapidly deployed without much tedious setup processes. This takes advantage of the ``State`` classes and ``EquilibriumCalculationResult`` as well as ``StreamCalculationResult`` objects mentioned in :ref:`the core section `. The practical use of the logging abilities of ChemApp for Python is another feature that allows quickly generating helpful information to debug and analyze your program code. Furthermore, a convenient way to enter incoming amounts using chemical formula instead of phase constituents or system components is introduced, using the :py:meth:`~chemapp.friendly.EquilibriumCalculation.set_IA_cfs` routine of the ``EquilibriumCalculation`` classes. Result Objects -------------- Most thermochemical calculations related to process manages are using multiple calculation steps to describe. With ChemApp for Python, it is possible to easily use results of previous calculations not only to store and investigate the calculation results, but also as input for following calculations. To work with calculation results as inputs for new calculations, they have to be converted into ``StreamState`` objects, which are objects that are defining input streams in ChemApp. They do carry an initial temperature, initial pressure and phase (constituent) amounts, all of which is necessary to define an input stream into ChemApp for Python. One of the main applications of stream or initial conditions based calculations are in process modelling and in designing of material flow in multistage reaction schemes. Functions to split, separate and combine ``StreamState`` objects are available, as well as directly creating streams from equilibrium results filtered by e.g. matter of state, phase or constituents. Creation of result objects ^^^^^^^^^^^^^^^^^^^^^^^^^^ After executing a calculation, a ``EquilibriumCalculationResult`` and ``StreamCalculationResult`` can be created using the class routine ``get_result_object`` of ``EquilibriumCalculation`` or ``StreamCalculation``. The result objects are relatively rich data container classes, that expose reasonable information of the calculation as properties, such as the conditions and inputs, as well as e.g. the units in which the results are stored. All fetchable thermodynamic data is stored in the object for all phases and constituents. Most of the properties are constructed of the various data structures that ChemApp for Python provides, such as ``PhaseState`` classes. It is recommended to always create a result object from a calculation, if any type of aggregating or deeper analysis of certain results is planned. The computational cost of collecting the data is relatively low. However, each calculation result object size can quickly become large for large datafiles. .. TODO: maybe talk about the _convert_to_indexed_primitive_doc, or better even, make creation of DataFrame available! :new:`Creation of stream objects` --------------------------------- Note: An extended introduction to stream functionalities can be found in :doc:`../stream-functionalities/main`. From these objects, the following routines can be used to create ``StreamState`` classes, which then can be used as inputs for new calculations: * ``create_stream`` * ``create_stream_by_state`` * ``create_gas_stream`` * ``create_liquid_stream`` * ``create_solid_stream`` Of these routines, ``create_stream`` is the most versatile and allows for various types of filters and selections, namely by specifically including or excluding certain phases, as well as including or excluding certain elements. It also allows to set a certain threshold to ignore residual amounts of phase constituents. :new:`Update checks & persistent configuration` ----------------------------------------------- The update mechanism and the persistent configuration (including the density preference stored via ``Info.set_density_config``) are described in detail in the dedicated chapter below. It explains how automatic and forced update checks work, what is stored in ``chemapp_python.cfg`` in the user home directory, and how to select a ``VolumeEstimateSetting``. More information on this: :ref:`Update checks & persistent configuration `. ChemApp Logging --------------- A debugging functionality has been added to ChemApp for Python that allows to output the ChemApp library calls in the original FORTRAN language calls to the ChemAPP API. It allows for users to generate a complete stream of the setup that was used in calculations, which can be very helpful to guide through e.g. presumed bugs in ChemApp, in some library code or to check if the program does indeed do as intended. If a function returns a result, this result is printed directly after the function. Therefore it can also be used to quickly find off-by-one errors or other typical programming mishaps. It can be enabled by using .. code-block:: python from chemapp.basic import enable_logging, disable_logging # enable logging to 'calls.log' enable_logging() # and can subsequently deactivated using disable_logging() The output is appended to the file 'calls.log' in the current working directory, and together with the datafile used in the calculation should be a valuable debugging ressource, especially if you consider contacting GTT for maintenance or support. Input of complex chemical formulae ---------------------------------- ChemApp for Python allows you to specify incoming amounts using full chemical formula strings instead of listing system components or phase constituents individually. This is done via the class method :py:meth:`~chemapp.friendly.EquilibriumCalculation.set_IA_cfs` (and the analogous :py:meth:`~chemapp.friendly.StreamCalculation.set_IA_cfs` in stream calculations). The parsing, validation and convenient (pretty) printing of chemical formulae is handled by the :class:`~chemapp.core.Composition` class, which provides a rich interface around a formula viewed as an ordered mapping ``{element: amount}``. .. important:: Calling :py:meth:`~chemapp.friendly.EquilibriumCalculation.set_IA_cfs` REPLACES previously defined incoming amount conditions for the involved system components. It is not additive. If you want to change or extend the incoming amounts and use :py:meth:`~chemapp.friendly.EquilibriumCalculation.set_IA_cfs`, you have to use :py:meth:`~chemapp.friendly.EquilibriumCalculation.set_IA_sc` or :py:meth:`~chemapp.friendly.EquilibriumCalculation.set_IA_pc` only after :py:meth:`~chemapp.friendly.EquilibriumCalculation.set_IA_cfs` has been called (the same semantics apply for the ``StreamCalculation`` variants). Rationale & workflow ^^^^^^^^^^^^^^^^^^^^ 1. You supply one or more chemical formula strings plus the corresponding incoming amount values. 2. Each formula string is internally converted into a ``Composition`` object. 3. The composition is validated: only elements that can be represented by the system components of the loaded datafile are accepted. Parentheses and fractional / decimal subscripts (e.g. ``Cr0.3Mn0.7C``) are expanded. 4. The element amounts are mapped onto system components; the resulting system component incoming amounts replace any prior ones. Accepted input formats ^^^^^^^^^^^^^^^^^^^^^^ Examples of accepted formula strings: * ``Fe2O3`` * ``Na(OH)2`` (parentheses are expanded) * ``Cr0.3Mn0.7C`` (decimal amounts) * ``Al2SiO5`` If a composition cannot be expressed solely with the available system components an error is raised. Using :py:meth:`~chemapp.friendly.EquilibriumCalculation.set_IA_cfs` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Basic usage: .. code-block:: python from chemapp.friendly import EquilibriumCalculation # Replace (not add to) any previously defined incoming amounts EquilibriumCalculation.set_IA_cfs( cfs=["CaO", "SiO2", "Al2O3"], values=[10.0, 5.0, 2.0] ) As long as all compositions can be expressed using the system components of the loaded data file, you can use: .. code-block:: python EquilibriumCalculation.set_IA_cfs( ["Cr0.3Mn0.7C", "Na(OH)2"], [1.2, 4.5] ) Leveraging ``Composition`` explicitly ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ While for :py:meth:`~chemapp.friendly.EquilibriumCalculation.set_IA_cfs` you just pass formula strings, working directly with the ``Composition`` class (``from chemapp.core import Composition``) to inspect or transform formulae beforehand (e.g. for validation, normalization, pretty printing or logging) can be helpful. Key features of ``Composition`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Ordered storage of elements as entered * Validation & tolerant parsing (parentheses, decimals) * Multiple string representations for consistent reporting * Scaling reduction to smallest integer ratio * Dictionary export (optionally reduced) Properties & methods ~~~~~~~~~~~~~~~~~~~~ * ``formula`` - canonical internal string representation * ``pretty_formula`` - elements sorted by Pauling order, amounts of 1 omitted * ``reduced_formula`` - smallest common denominator applied * ``pretty_reduced_formula`` - combination of sorting + reduction * ``elements`` - ``frozenset`` of element symbols * ``unordered`` - original input order {element: amount} * ``to_dict(reduced=False)`` - export mapping, optionally reduced Examples ~~~~~~~~ Creating compositions: .. code-block:: python from chemapp.core import Composition c1 = Composition("Fe2O3") c2 = Composition("Cs(OH)3") c3 = Composition({"Cr": 0.3, "Mn": 0.7, "C": 1}) # from dict c4 = Composition(c1) # copy from existing Composition Inspecting representations: .. code-block:: python c = Composition("Fe4O6") c.formula # 'Fe4O6' c.pretty_formula # e.g. 'Fe4O6' c.reduced_formula # 'Fe2O3' c.pretty_reduced_formula # 'Fe2O3' c.to_dict() # {'Fe': 4.0, 'O': 6.0} c.to_dict(reduced=True) # {'Fe': 2.0, 'O': 3.0} Dealing with parentheses & reordering: .. code-block:: python Composition("Cs(OH)3").formula # 'Cs1O3H3' Composition("Cs(OH)3").pretty_formula # 'CsO3H3' Composition("Cs(OH)3").reduced_formula # 'CsO3H3' (already reduced) Logging / pretty printing in reports: .. code-block:: python comps = [Composition(f) for f in ["Fe2O3", "Na(OH)2", "Cr0.3Mn0.7C"]] for comp in comps: print(f"Input: {comp.formula} Pretty: {comp.pretty_reduced_formula}") Using it to e.g. validate user input: .. code-block:: python formulas = ["Fe2O3", "Na(OH)2", "Cr0.3Mn0.7C"] amounts = [3.0, 1.5, 0.8] try: compositions = [Composition(f) for f in formulas] except: # this would be a place where you could catch validation errors ... # Single replacing call EquilibriumCalculation.set_IA_cfs(formulas, amounts)