Logging and terminal output

By default, calling (almost) any function in TeNPy will not print output, apart from error messages, tracebacks, and warnings. Instead, we use Python’s logging module to allow fine-grained redirecting of status messages etc.

Of course, when you get an error message, you should be concerned to find out what it is about and how to fix it. (If you believe it is a bug, report it.) Warnings can be reported either using warnings.warn(...) or with the logging mechanism logger.warning(...). The former is used for warnings about things in your setup that you should fix. The latter give you notifications about bad things that can happen in calculations, e.g. bad conditioning of a matrix, but there is not much you can do about it. Those warnings indicate that you should take your results with a grain of salt and carefully double-check them.

Configuring logging

If you also want to see status messages (e.g. during a DMRG run whenever a checkpoint is reached), you can use set the logging level to logging.INFO with the following, basic setup:

import logging
logging.basicConfig(level=logging.INFO)

We use this snippet in our examples to activate the printing of info messages to the standard output stream. For really detailed output, you can even set the level to logging.DEBUG. logging.basicConfig() also takes a filename argument, which allows to redirect the output to a file instead of stdout. Note that you should call basicConfig only once; subsequent calls have no effect.

More detailed configurations can be made through logging.config. For example, the following both prints log messages to stdout and saves them to`output_filename.log`:

import logging.config
conf = {
    'version': 1
    'disable_existing_loggers': False,
    'formatters': {'custom': {'format': '%(levelname)-8s: %(message)s'}},
    'handlers': {'to_file': {'class': 'logging.FileHandler',
                             'filename': 'output_filename.log',
                             'formatter': 'custom',
                             'level': 'INFO',
                             'mode': 'a'},
                'to_stdout': {'class': 'logging.StreamHandler',
                              'formatter': 'custom',
                              'level': 'INFO',
                              'stream': 'ext://sys.stdout'}},
    'root': {'handlers': ['to_stdout', 'to_file'], 'level': 'DEBUG'},
}
logging.config.dictConfig(conf)

Note

Whether you use logging.config.fileConfig() or the logging.config.dictConfig(), make sure that you also set disable_existing_loggers=False. Otherwise, it will not work as expected in the case where you import tenpy before setting up the logging.

To also capture warnings, you might also want to call logging.captureWarnings().

In fact, the above is the default configuration used by tenpy.tools.misc.setup_logging(). If you use a Simulation class, it will automatically call setup_logging() for you, saving the log to the same filename as the Simulation.output_filename but with a .log ending. Moreover, you can easily adjust the log levels with simple parameters, for example with the following configuration (using [yaml] notation):

log_params:
    to_stdout:     # nothing in yaml -> None in python => no logging to stdout
    to_file: INFO
    logger_levels:
        tenpy.tools.params : WARNING  # suppress INFO/DEBUG output for any logging of parameters

Of course, you can also explicitly call the setup_logging() yourself, if you don’t use the Simulation classes:

tenpy.tools.misc.setup_logging({'to_stdout': None, 'to_file': 'INFO', 'filename': 'my_log.txt',
                                'log_levels': {'tenpy.tools.params': 'WARNING'}})

How to write your own logging (and warning) code

Of course, you can still use simple print(...) statements in your code, and they will just appear on your screen. In fact, this is one of the benefits of logging: you can make sure that you only get the print statements you have put yourself, and at the same time redirect the logging messages of tenpy to a file, if you want.

However, these print(...) statements are not re-directed to the log-files. Therefore, if you write your own sub-classes like Models, I would recommended that you also use the loggers instead of simple print statements. You can read the official logging tutorial for details, but it’s actually straight-forward, and just requires at most two steps.

  1. If necessary, import the necessary modules and create a logger at the top of your module:

    import warnings
    import logging
    logger = logging.getLogger(__name__)
    

    Note

    Most TeNPy classes that you might want to subclass, like models, algorithm engines or simulations, provide a Logger as self.logger class attribute. In that case you can even skip this step and just use self.logger instead of logger in the snippets below.

  2. Inside your functions/methods/…, make calls like this:

    if is_likely_bad(options['parameter']):
        # this can be fixed by the user!
        warnings.warn("This is a bad parameter, you shouldn't do this!")
    if "old_parameter" in options:
        warnings.warn("Use `new_parameter` instead of `old_parameter`", FutureWarning, 2)
    
    logger.info("starting some lengthy calculation")
    n_steps = do_calculation()
    if something_bad_happened():
        # the user can't do anything about it
        logger.warning("Something bad happened")
    logger.info("calculation finished after %d steps", n_steps)
    

    You can use printf-formatting for the arguments of logger.debug(...), logger.info(...), logger.warning(...), as illustrated in the last line.

In summary, instead of just print("do X") statements, use self.logger.info("do X") inside TeNPy classes, or just logger.info("do X") for the module-wide logger, which you can initialize right at the top of your file with the import statements. If you have non-string arguments, add a formatter string, e.g. replace print(max(psi.chi)) with logger.info("%d", max(psi.chi)), or even better, logger.info("max(chi)=%d", max(psi.chi)). For generic types, use "%s" or "%r", which converts the other arguments to strings with str(...) or repr(...), respectively.