Source code for pyretis.forcefield.forcefield
# -*- coding: utf-8 -*-
# Copyright (c) 2023, PyRETIS Development Team.
# Distributed under the LGPLv2.1+ License. See LICENSE for more info.
"""This file contains a class for a generic force field.
This module defines the class used for representing a force field.
The forcefield class is built up of potential functions.
Important classes defined here
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ForceField (:py:class:`.ForceField`)
A class representing a generic Force Field.
"""
import logging
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
logger.addHandler(logging.NullHandler())
__all__ = ['ForceField']
[docs]class ForceField:
"""Represents a generic force field.
This class described a generic Force Field.
A force field is assumed to consist of a number of potential
functions with parameters.
Attributes
----------
desc : string
Description of the force field.
potential : list
The potential functions that the force field is built up from.
params : list
The parameters for the corresponding potential functions.
"""
[docs] def __init__(self, desc, potential=None, params=None):
"""Initialise the force field object.
Parameters
----------
desc : string
Description of the force field.
potential : list, optional
Potential functions that the force field is built up from.
params : list, optional
Parameters for the potential(s). If too few parameters are
given, we will just assume a `None`.
"""
self.desc = desc
self.potential = []
self.params = []
if potential is not None:
if params is None:
for pot in potential:
self.add_potential(pot)
else:
for i, pot in enumerate(potential):
try:
param = params[i]
except IndexError:
param = None
msg = 'No parameters given for potential no. {} ({})'
msgtxt = msg.format(i, pot)
logger.warning(msgtxt)
self.add_potential(pot, parameters=param)
[docs] def add_potential(self, potential, parameters=None):
"""Add a potential with parameters to the force field.
Parameters
----------
potential : object like :py:class:`.PotentialFunction`
Potential function to add.
parameters : dict, optional
Parameters for the potential.
Returns
-------
out : boolean
Returns `True` and updates `self.potential` and
`self.params` if the potential was added. Returns
`False` otherwise.
"""
if potential is None:
msg = ('Trying to add empty potential to force field.\n'
'This was ignored -- please check your settings.')
logger.warning(msg)
return False
self.potential.append(potential)
if parameters is not None:
potential.set_parameters(parameters)
self.params.append(parameters)
return True
[docs] def remove_potential(self, potential):
"""Remove a selected potential from the force field.
Parameters
----------
potential : object like :py:class:`.PotentialFunction`
The potential function to remove.
Returns
-------
out : None or tuple
Returns `None` if not potential was removed, otherwise it
will return the removed potential and its parameters.
"""
if potential in self.potential:
idx = self.potential.index(potential)
potrm = self.potential.pop(idx)
paramrm = self.params.pop(idx)
return potrm, paramrm
logger.warning('Potential not found in the force field functions')
return None, None
[docs] def update_potential_parameters(self, potential, params):
"""Update the potential parameters of the given potential function.
Parameters
----------
potential : object like :py:class:`.PotentialFunction`
Potential to update. Should be in `self.potential`.
params : dict
The new parameters to set.
Returns
-------
out : None
Returns `None` but will update parameters of the selected
potential and modify the corresponding `self.params`.
"""
if potential in self.potential:
potential.set_parameters(params)
self.params[self.potential.index(potential)] = params
else:
logger.warning('Unknow potential. Will not update!')
[docs] def evaluate_force(self, system):
"""Evaluate the force on the particles.
Parameters
----------
system : object like :py:class:`.System`
The system we evaluate the forces in.
Returns
-------
out[0] : numpy.array
The forces on the particles.
out[1] : numpy.array
The virial.
"""
force = None
virial = None
for pot in self.potential:
if force is None or virial is None:
force, virial = pot.force(system)
else:
forcei, viriali = pot.force(system)
force += forcei
virial += viriali
return force, virial
[docs] def evaluate_potential(self, system):
"""Evaluate the potential energy.
Parameters
----------
system : object like :py:class:`.System`
The system we evaluate the potential in.
Returns
-------
out : float
The potential energy.
"""
v_pot = None
for pot in self.potential:
if v_pot is None:
v_pot = pot.potential(system)
else:
v_pot += pot.potential(system)
return v_pot
[docs] def evaluate_potential_and_force(self, system):
"""Evaluate the potential energy and the force.
Parameters
----------
system : object like :py:class:`.System`
The system we evaluate the potential energy and force in.
Returns
-------
out[0] : float
The potential energy.
out[1] : numpy.array
The calculated forces.
out[2] : numpy.array
The calculated virial.
"""
v_pot = None
force = None
virial = None
for pot in self.potential:
if v_pot is None or force is None or virial is None:
v_pot, force, virial = pot.potential_and_force(system)
else:
v_poti, forcei, viriali = pot.potential_and_force(system)
v_pot += v_poti
force += forcei
virial += viriali
return v_pot, force, virial
[docs] def __str__(self):
"""Return a string representation of the force field.
The string representation is built using the string
descriptions of the potential functions.
Returns
-------
out : string
Description of the force field and the potential functions
included in the force field.
"""
msg = [f'Force field: {self.desc}']
if len(self.potential) < 1:
msg.append('No potential functions added yet!')
else:
msg.append('Potential functions:')
for i, pot in enumerate(self.potential):
msg.append(f'{i + 1}: {pot}')
return '\n'.join(msg)
[docs] def print_potentials(self):
"""Print information on potentials in the force field.
This is intended as a lighter alternative to `self.__str__`
which can be verbose. This function will not actually do the
printing, but it returns a string which can be printed.
Returns
-------
out : string
Description of the potential functions in this force field.
"""
msg = [f'Force field: {self.desc}']
for i, pot in enumerate(self.potential):
msg.append(f'\t{i + 1}: {pot.desc}')
return '\n'.join(msg)