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.\ \).