#!/usr/bin/env python
# =============================================================================
# MODULE DOCSTRING
# =============================================================================
"""
Python wrapper for the plumed sum_hills program.
"""
# =============================================================================
# GLOBAL IMPORTS
# =============================================================================
import logging
import os
import subprocess
from . import io as plumedio
# =============================================================================
# GLOBAL VARIABLES
# =============================================================================
logger = logging.getLogger(__name__)
# =============================================================================
# UTILITY FUNCTIONS
# =============================================================================
[docs]
def run_plumed_sum_hills(
metad_file_path,
output_file_path=None,
stride=None,
min_to_zero=None,
cv_min=None,
cv_max=None,
n_bins=None,
overwrite=False,
create_time_file=True
):
"""
Run the plumed sum_hills command.
Parameters
----------
metad_file_path : str
The path to the file defined in the 'METAD' action of the PLUMED
script where the hills are saved. Corresponds to the '--hills'
option in the ``plumed sum_hills`` program.
output_file_path : str, optional
The path to the output file with the free energy surface. By default,
the function generates a fes.dat file in the same directory as
the metad file or, if ``stride`` is passed, all the files for
each stride are saved in a sub directory called 'fes'.
min_to_zero : bool, optional
If ``True``, the minimum free energy will be shifted to zero.
The default is identical to the program.
stride : int, optional
If given, the free energy surface is estimated every ``stride``
Gaussian depositions. The default is identical to the program.
cv_min : float, optional
The minimum value of the CV for the grid. It corresponds to the
--min option. The default is identical to the program.
cv_max : float, optional
The maximum value of the CV for the grid. It corresponds to the
--max option. The default is identical to the program.
n_bins : int, optional
The number of bins in the grid used to represent the free energy
surface. The default is identical to the program.
overwrite : bool or 'warning', optional
If the output file exist, if ``True``, the output file is
overwritten; If ``False`` an exception is raised; If the string
'warning', the file is not overwritten, but only a warning is
raised; If the string 'ignore', the output file is not
overwritten and the function returns silently.
create_time_file : bool, optional
By default, when ``stride`` is passed, the function creates an
extra 'fes_time.dat' file with a single column with the time of
each 'fes.dat' file. Set this to ``False`` to turn off this feature.
"""
# Check that plumed is installed.
from . import check_plumed_is_installed
check_plumed_is_installed()
# Handle default output path.
if output_file_path is None:
# Check if we need to create a subdirectory.
output_dir_path = os.path.dirname(metad_file_path)
if stride is None:
output_file_path = os.path.join(output_dir_path, 'fes.dat')
else:
output_dir_path = os.path.join(output_dir_path, 'fes')
os.makedirs(output_dir_path, exist_ok=True)
# With stride, PLUMED will use the --outfile as a prefix.
output_file_path = os.path.join(output_dir_path, 'fes_')
# Check if we need to overwrite the file. If stride is set, we
# check if the first fes has been computed.
overwritten_file_path = output_file_path
if stride is not None:
overwritten_file_path += '0.dat'
# Raise exception/warning if needed.
if os.path.exists(overwritten_file_path) and overwrite is not True:
if overwrite == 'warning':
logger.warning(f'{output_file_path} already exist. Skipping the '
'calculation of the FES.')
elif overwrite != 'ignore':
raise RuntimeError(f'{output_file_path} already exist. Please '
'delete the file or set overwrite=True.')
return
# Check if we need to create a time file and read the number of depositions.
# TODO: When the number of depositions is divisible by stride, it seems that
# TODO: PLUMED creates twice the final FES for some reason, which result in
# TODO: an extra fes_*.dat file. Ask about this behavior on the issue tracker.
if stride is not None and create_time_file:
# Read the time information.
time_data = plumedio.read_table(metad_file_path, col_names=['time'])
# Slice time array according to stride.
time_data['time'] = time_data['time'][stride-1::stride]
# Write time data.
time_file_path = os.path.join(output_file_path + 'time.dat')
plumedio.write_table(time_data, time_file_path)
# Command to invoke.
sum_hills_cmd = [
'plumed', 'sum_hills',
'--hills', metad_file_path,
]
# Add optional commands.
if output_file_path is not None:
sum_hills_cmd.extend(['--outfile', output_file_path])
if min_to_zero is not None:
sum_hills_cmd.append('--mintozero')
if stride is not None:
sum_hills_cmd.extend(['--stride', str(stride)])
if cv_min is not None:
sum_hills_cmd.extend(['--min', str(cv_min)])
if cv_max is not None:
sum_hills_cmd.extend(['--max', str(cv_max)])
if n_bins is not None:
sum_hills_cmd.extend(['--bin', str(n_bins)])
# Run plumed sum_hills.
subprocess.check_output(sum_hills_cmd)