Figure 3. The computational workflow of the “run_dynamics” function of the “dynamics.heom” module.
The second part of the current HEOM implementation is the Python module, ”dynamics.heom” (Figure 1, right), which encompasses a number of workflows for intuitive and convenient user-software interaction. The module contains two main sub-modules “compute” and “save”. The “compute” sub-module defines high-level functions for transforming and handling data as well as a “run_dynamics” function, which serves as the entry point for users to execute HEOM calculations. The workflow of this function (Figure 3) involves calling both the Python-level functions and the Python-exposed C++ functions of the “dyn/heom” module.
It is worth describing the structure of the main computational unit – the “run_dynamics” function – in somewhat more detail. First, the simulation parameters are passed into this function as a Python dictionary. A local copy is first created. The keywords present in this dictionary are checked and if some expected keywords are not found (e.g. user didn’t specify them), they are initialized to the default values defined in the “run_dynamics” function. The operation is done via the “check_input” function of the util.libutil library of Libra:
import util.libutil as comn
comn.check_input(params, default_params, critical_params)
One can specify a list of keywords that are considered critical (needed for the execution of the code), but that cannot generally be assigned to default values (e.g. system-specific). The program terminates if the input dictionary does not provide any key-value pair with the key names listed in the “critical_params” variable. The function “check_input” would modify the local copy of the control parameters dictionary, to setup the undetermined variables to the default values, but it would not affect the original instance of the parameters dictionary, which is important in a situation when the same parameters dictionary is passed into several consecutive calls of the “run_dynamics” function. On the implementation side, we exercise caution to copy the input dictionary by value (via copy constructor) to the internal variables, whose key-value pairs may be reset to some default values. The approach of setting up the default values and indicating the critical parameters is widely used in other modules of the Libra code, including the modules for fully quantum (“exact”) and trajectory-surface hopping (“tsh”) calculations.
Another sub-module of the “dynamics.heom” is the “save” module. It implements methods to store results of simulations in HDF5 format. The archiving of the results computed in the dynamics can be done in a regular mode, such that the HDF5 files are updated on the fly. However, this mode is very time-consuming and an alternative mode, “memory” (“mem”), is preferred. In the “mem” mode, all the propagated variables are stored in the operating system (RAM) for the entire duration of the calculations and are saved to the disk only when the propagation phase is completed. This saving mode is much faster, but it has an obvious drawback of potential data loss if the calculations are terminated prematurely. In addition, lengthy and complex simulations in this mode may require an increased amount of operating memory the user needs to reserve on their computational systems.
Finally, the “plot” module is present in the “dynamics.heom” module and is intended to provide “out-of-box” recipes for plotting results of calculations. However, such printing is rather straightforward and may be easier to customize in the users’ scripts. For these reasons, the “plot” module can be regarded as a placeholder for the time being. It is worth mentioning that an analogous “plot” module is present in other higher-level modules of Libra, such as “tsh” and “exact”. The plotting of the results of those calculations may be somewhat more involved and the “plot” does define certain plotting recipes.

3.2. Software Functionalities and Sample code snippets.

3.2.1. Calculation of density matrix evolution

An example snippet to run the HEOM calculations using Libra is shown in Figure 4. As we discuss above, the main function to call is the “run_dynamics” of the “libra_py.dynamics.heom” module. The major fraction of the snippet only sets up the parameters needed to run the calculations. In particular, one can define the system’s Hamiltonian matrix as one of the Libra’s built-in data types “CMATRIX” that holds the complex-valued matrices of arbitrary dimensions. In this example, we illustrate a setup for a 3-level system, similar to the one utilized previously by Shi et al.25 In this model, two higher energy states are degenerate and are separated from the ground state by the \(\Omega=1.5\ J\) gap, where \(J\) is the electronic coupling. Unlike the model of Shi et al., in this demonstration we make all pairs of states coupled to each other through the matrix elements\(H_{\text{ij}}=H_{\text{ji}}=-J,\ i\neq j\) . We start the simulation with a ground-state density matrix,\(\rho=\left.\ |0\right\rangle\left\langle 0|\right.\ \).