# Path: openmmtools/utils.py
# def temporary_directory():
# def time_it(task_name):
# def with_timer(task_name):
#     def _with_timer(func):
#         def _wrapper(*args, **kwargs):
#     def __init__(self):
#     def reset_timing_statistics(self, benchmark_id=None):
#     def start(self, benchmark_id):
#     def stop(self, benchmark_id):
#     def partial(self, benchmark_id):
#     def report_timing(self, clear=True):
# def sanitize_expression(expression, variables):
# def math_eval(expression, variables=None, functions=None):
#     def _math_eval(node):
# def _changes_state(func):
#     def wrapper(self, *args, **kwargs):
#     def __init__(self, *args, **kwargs):
#     def __getitem__(self, item):
#     def __init__(self, tracked_quantity, *args, **kwargs):
#     def __getitem__(self, item):
#     def __setitem__(self, key, value):
# def is_quantity_close(quantity1, quantity2, rtol=1e-10, atol=0.0):
# def quantity_from_string(expression):
# def typename(atype):
# def platform_supports_precision(platform, precision):
# def get_available_platforms(minimum_precision='mixed'):
# def get_fastest_platform(minimum_precision='mixed'):
# def serialize(instance, **kwargs):
# def deserialize(serialization):
# def with_metaclass(metaclass, *bases):
#         def __new__(cls, name, this_bases, d):
#     def __subclasshook__(cls, subclass):
# def find_all_subclasses(parent_cls, discard_abstract=False, include_parent=True):
# def find_subclass(parent_cls, subcls_name):
#     def __init__(self, *args, **kwargs):
#     def is_restorable(cls, openmm_object):
#     def restore_interface(cls, openmm_object):
#     def _hash_parameter_name(self):
#     def _get_hash_parameter_name(cls, openmm_object):
#     def _add_global_parameter(cls, openmm_object, parameter_name, parameter_value):
#     def _get_global_parameter(cls, openmm_object, parameter_name):
#     def _get_force_parameter_by_name(cls, force, parameter_name):
#     def deserialize_xml(cls, xml_serialization):
#     def __deepcopy__(self, memo):
#     def __copy__(self):
#     def _is_force(openmm_object):
#     def _compute_class_hash(openmm_class):
# class Timer(object):
# class TrackedQuantity(unit.Quantity):
# class TrackedQuantityView(unit.Quantity):
#     class Metaclass(metaclass):
# class SubhookedABCMeta(with_metaclass(abc.ABCMeta)):
# class RestorableOpenMMObjectError(Exception):
# class RestorableOpenMMObject(object):
# _RESERVED_WORDS_PATTERNS = {
#     'lambda': re.compile(r'(?<![a-zA-Z0-9_])lambda(?![a-zA-Z0-9_])')
# }
# _VALID_UNITS = {method: getattr(unit, method) for method in dir(unit) if type(getattr(unit, method)) is unit.Unit}
# _VALID_UNIT_FUNCTIONS = {method: getattr(unit, method) for method in dir(unit)
#                          if callable(getattr(unit, method)) and type(getattr(unit, method)) is not type}
#     SUPPORTED_PRECISIONS = ['single', 'mixed', 'double']
# _SERIALIZED_MANGLED_PREFIX = '_serialized__'
# 
# Path: openmmtools/constants.py
# ONE_4PI_EPS0 = 1/(4*pi*EPSILON0) * EPSILON0.unit  # we need it unitless
# 
# STANDARD_STATE_VOLUME = LITER / (u.AVOGADRO_CONSTANT_NA*u.mole)


import abc
import collections
import copy
import inspect
import logging
import math
import re
import scipy
import numpy as np
    import openmm
    import doctest
    from openmm import unit
    from simtk import openmm, unit
from openmmtools import utils
from openmmtools.constants import ONE_4PI_EPS0, STANDARD_STATE_VOLUME
        elif max_volume is None:
            raise TypeError('max_volume must be provided with NPT ensemble')

        # Non periodic systems reweighted to a square-well restraint must always have a cutoff.
        if (not thermodynamic_state.is_periodic and square_well is True and
                    radius_cutoff is None and energy_cutoff is None and max_volume is None):
            raise TypeError('One between radius_cutoff, energy_cutoff, or max_volume '
                            'must be provided when reweighting non-periodic thermodynamic '
                            'states to a square-well restraint.')

        # If we evaluate the square well potential with no cutoffs,
        # just use the volume of the periodic box.
        if square_well is True and energy_cutoff is None and radius_cutoff is None:
            restraint_volume = max_volume
        # If we evaluate the square well potential with no energy cutoff,
        # this can easily be solved analytically.
        elif square_well is True and radius_cutoff is not None:
            restraint_volume = _compute_sphere_volume(radius_cutoff)
        # Use numerical integration.
        else:
            restraint_volume = self._compute_restraint_volume(
                thermodynamic_state, square_well, radius_cutoff, energy_cutoff)

        # Bound the restraint volume to the periodic box volume.
        if max_volume is not None and restraint_volume > max_volume:
            debug_msg = 'Limiting the restraint volume to {} nm^3 (original was {} nm^3)'
            logger.debug(debug_msg.format(max_volume / unit.nanometers**3,
                                          restraint_volume / unit.nanometers**3))
            restraint_volume = max_volume


# Path: openmmtools/utils.py
# def temporary_directory():
# def time_it(task_name):
# def with_timer(task_name):
#     def _with_timer(func):
#         def _wrapper(*args, **kwargs):
#     def __init__(self):
#     def reset_timing_statistics(self, benchmark_id=None):
#     def start(self, benchmark_id):
#     def stop(self, benchmark_id):
#     def partial(self, benchmark_id):
#     def report_timing(self, clear=True):
# def sanitize_expression(expression, variables):
# def math_eval(expression, variables=None, functions=None):
#     def _math_eval(node):
# def _changes_state(func):
#     def wrapper(self, *args, **kwargs):
#     def __init__(self, *args, **kwargs):
#     def __getitem__(self, item):
#     def __init__(self, tracked_quantity, *args, **kwargs):
#     def __getitem__(self, item):
#     def __setitem__(self, key, value):
# def is_quantity_close(quantity1, quantity2, rtol=1e-10, atol=0.0):
# def quantity_from_string(expression):
# def typename(atype):
# def platform_supports_precision(platform, precision):
# def get_available_platforms(minimum_precision='mixed'):
# def get_fastest_platform(minimum_precision='mixed'):
# def serialize(instance, **kwargs):
# def deserialize(serialization):
# def with_metaclass(metaclass, *bases):
#         def __new__(cls, name, this_bases, d):
#     def __subclasshook__(cls, subclass):
# def find_all_subclasses(parent_cls, discard_abstract=False, include_parent=True):
# def find_subclass(parent_cls, subcls_name):
#     def __init__(self, *args, **kwargs):
#     def is_restorable(cls, openmm_object):
#     def restore_interface(cls, openmm_object):
#     def _hash_parameter_name(self):
#     def _get_hash_parameter_name(cls, openmm_object):
#     def _add_global_parameter(cls, openmm_object, parameter_name, parameter_value):
#     def _get_global_parameter(cls, openmm_object, parameter_name):
#     def _get_force_parameter_by_name(cls, force, parameter_name):
#     def deserialize_xml(cls, xml_serialization):
#     def __deepcopy__(self, memo):
#     def __copy__(self):
#     def _is_force(openmm_object):
#     def _compute_class_hash(openmm_class):
# class Timer(object):
# class TrackedQuantity(unit.Quantity):
# class TrackedQuantityView(unit.Quantity):
#     class Metaclass(metaclass):
# class SubhookedABCMeta(with_metaclass(abc.ABCMeta)):
# class RestorableOpenMMObjectError(Exception):
# class RestorableOpenMMObject(object):
# _RESERVED_WORDS_PATTERNS = {
#     'lambda': re.compile(r'(?<![a-zA-Z0-9_])lambda(?![a-zA-Z0-9_])')
# }
# _VALID_UNITS = {method: getattr(unit, method) for method in dir(unit) if type(getattr(unit, method)) is unit.Unit}
# _VALID_UNIT_FUNCTIONS = {method: getattr(unit, method) for method in dir(unit)
#                          if callable(getattr(unit, method)) and type(getattr(unit, method)) is not type}
#     SUPPORTED_PRECISIONS = ['single', 'mixed', 'double']
# _SERIALIZED_MANGLED_PREFIX = '_serialized__'
# 
# Path: openmmtools/constants.py
# ONE_4PI_EPS0 = 1/(4*pi*EPSILON0) * EPSILON0.unit  # we need it unitless
# 
# STANDARD_STATE_VOLUME = LITER / (u.AVOGADRO_CONSTANT_NA*u.mole)


# Path: openmmtools/forces.py
import abc
import collections
import copy
import inspect
import logging
import math
import re
import scipy
import numpy as np
    import openmm
    import doctest
    from openmm import unit
    from simtk import openmm, unit
from openmmtools import utils
from openmmtools.constants import ONE_4PI_EPS0, STANDARD_STATE_VOLUME

        elif max_volume is None:
            raise TypeError('max_volume must be provided with NPT ensemble')

        # Non periodic systems reweighted to a square-well restraint must always have a cutoff.
        if (not thermodynamic_state.is_periodic and square_well is True and
                    radius_cutoff is None and energy_cutoff is None and max_volume is None):
            raise TypeError('One between radius_cutoff, energy_cutoff, or max_volume '
                            'must be provided when reweighting non-periodic thermodynamic '
                            'states to a square-well restraint.')

        # If we evaluate the square well potential with no cutoffs,
        # just use the volume of the periodic box.
        if square_well is True and energy_cutoff is None and radius_cutoff is None:
            restraint_volume = max_volume
        # If we evaluate the square well potential with no energy cutoff,
        # this can easily be solved analytically.
        elif square_well is True and radius_cutoff is not None:
            restraint_volume = _compute_sphere_volume(radius_cutoff)
        # Use numerical integration.
        else:
            restraint_volume = self._compute_restraint_volume(
                thermodynamic_state, square_well, radius_cutoff, energy_cutoff)

        # Bound the restraint volume to the periodic box volume.
        if max_volume is not None and restraint_volume > max_volume:
            debug_msg = 'Limiting the restraint volume to {} nm^3 (original was {} nm^3)'
            logger.debug(debug_msg.format(max_volume / unit.nanometers**3,
                                          restraint_volume / unit.nanometers**3))
            restraint_volume = max_volume


        return -math.log(STANDARD_STATE_VOLUME / restraint_volume)