By default, calling (almost) any function in TeNPy will not print output, appart 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.warn(...)
. 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.
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`ouput_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)
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 ~tenpy.simulations.simulation.Simulation
class, it will automatically call ~tenpy.tools.misc.setup_logging
for you, saving the log to the same filename as the :cfgSimulation.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):
Of course, you can also explicilty call the ~tenpy.tools.misc.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'}})
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.
If necessary, import the necessary modules and create a logger at the top of your module:
import warnings import logging logger = logging.getLogger(__name__)
Inside your funtions/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.warn("Something bad happend") logger.info("calculation finished after %d steps", n_steps)
You can use printf-formatting for the arguments of
logger.debug(...), logger.info(...), logger.warn(...)
, 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 genereic types, use "%s"
or "%r"
, which converts the other arguments to strings with str(...)
or repr(...)
, respectively.