.. _logging: Logging ======= Tkwant provides a logger to record events and internal states during a calculation. Enabling the logger, which by default prints only warning messages to standard output (stdout), can help to find errors in the user code and also facilitate debugging tkwant. Logging is enabled by putting the following line of code at the beginning of a Python script right after importing the tkwant package: .. jupyter-execute:: :hide-code: import tkwant .. jupyter-execute:: import logging tkwant.logging.level = logging.INFO # possible levels (increasing verbosity): WARNING, INFO, DEBUG A typical log message, here a warning, looks like this: .. jupyter-execute:: :hide-code: print("WARNING:tkwant.manybody:1452:rank=0: no occupied states found, the chemical potential is probably wrong.") The verbosity of the logger can increased to print also status and debug information, see `Level`_. Logging can be also completely turned off as desribed in `Handlers`_, or narrowed to a specific part as desribed in `Filters`_. A complete `Example`_ is shown below. Tkwant's logger is based on logging module of the Python standard library. We refer to the `Logging HOWTO `__ tutorial for more information about Python's logging module. Level ~~~~~ Logging has several severity levels to control which level of log messages are recorded. By default, tkwant is logging only warning messages (corresponding to ``logging.WARNING``). To change the log level, one has set ``tkwant.logging.level`` to a level, as defined in the Python logging module. As an example, to log also progress information, the additional line of code must be added: .. jupyter-execute:: import tkwant import logging tkwant.logging.level = logging.INFO # possible levels (increasing verbosity): WARNING, INFO, DEBUG Again, the logging level must be set *after* importing the tkwant module and *before* executing any tkwant code. The most verbose output is generated by setting the level to ``logging.DEBUG``. Handlers ~~~~~~~~ The handler specify the format of the logging output. Tkwant has implemented two predefined stream handlers: ``tkwant.logging.debug_handler`` and ``tkwant.logging.simple_handler``. The **debug_handler** aka ``tkwant.logging.debug_handler`` is the default handler. With this handler, a typical log output looks like: .. jupyter-execute:: :hide-code: print("INFO:tkwant.manybody:229:rank=0: distribution function: zero-temperature fermi-dirac") The format is: *level*: *module-name*: *line-number*: *MPI-rank*: *log message* Note that the MPI rank corresponds to the rank of tkwant's global MPI communicator that might be different to the rank of sub-communicators. The **simple_handler** is less verbose and prints only the log message. It can be set with: .. jupyter-execute:: import tkwant tkwant.logging.handler = tkwant.logging.simple_handler With this handler, a typical log output looks like: .. jupyter-execute:: :hide-code: print("distribution function: zero-temperature fermi-dirac") The format is: *log message* Alternative handlers whose API matches the logging module of the Python standard library can be set in the same way. Turning off logging completely is possible by setting the handler to the ``NullHandler``: .. jupyter-execute:: import logging tkwant.logging.level = logging.NullHandler() # suppress logging The logging handler must be set *after* importing the tkwant module and *before* executing any tkwant code. Filters ~~~~~~~ The logging output can be filtered to reduce the output to specific parts. To log only logging events triggered by a certain module, as e.g. ``tkwant.leads``, on can set .. jupyter-execute:: import logging tkwant.logging.filter = logging.Filter('tkwant.leads') Alternatively, one can also define a filter function. The following code logs only the messages from MPI with rank zero (which is Tkwant's default behavior): .. jupyter-execute:: def rank_filter(record): return True if record.rank == 0 else False tkwant.logging.filter = rank_filter To print the logging output from all MPI ranks one has to set: .. jupyter-execute:: tkwant.logging.filter = None If one likes to log only messages containing the word *interval* one can use .. jupyter-execute:: def message_filter(record): return True if 'interval' in record.getMessage() else False tkwant.logging.filter = message_filter Again, the filter must be set *after* importing the tkwant module and *before* executing any tkwant code. The documentation of the Python standard library logging module provides more information to write custom filters. Example ~~~~~~~ As an example, we show the toy example from :ref:`getting_started` with enabled logging and the generated output. Note that much logging output is generated by the call to ``tkwant.manybody.State()`` and reveals the preprocessing steps of the automatic high-level approach. .. jupyter-execute:: import numpy as np import matplotlib.pyplot as plt import kwant import tkwant #-------------------- enable logging -------------------------------- import logging tkwant.logging.level = logging.INFO #-------------------------------------------------------------------- def v(time, tau=8): """Time dependent perturbation V(t)""" if time < tau: return time / tau return 1 def create_system(length): def onsite_potential(site, time): """Time dependent onsite potential (static part + V(t))""" return 1 + v(time) # system building lat = kwant.lattice.square(a=1, norbs=1) syst = kwant.Builder() # central scattering region syst[(lat(x, 0) for x in range(length))] = 1 syst[lat.neighbors()] = -1 # time dependent onsite-potential at the leftmost site syst[lat(0, 0)] = onsite_potential # add leads sym = kwant.TranslationalSymmetry((-1, 0)) lead_left = kwant.Builder(sym) lead_left[lat(0, 0)] = 1 lead_left[lat.neighbors()] = -1 syst.attach_lead(lead_left) syst.attach_lead(lead_left.reversed()) return syst # parameters tmax = 20 length = 5 # create system syst = create_system(length).finalized() times = np.linspace(0, tmax) # define an observable density_operator = kwant.operator.Density(syst) # do the actual tkwant simulation state = tkwant.manybody.State(syst, tmax=tmax) densities = [] for time in times: state.evolve(time) state.refine_intervals(rtol=1e-3, atol=1e-3) density = state.evaluate(density_operator) densities.append(density) # plot the result plt.plot(times, densities) plt.xlabel(r'time $t$') plt.ylabel(r'charge density $n$') plt.show() References ---------- `Logging HOWTO `__ `Python standard library logging module documentation `__