-
Notifications
You must be signed in to change notification settings - Fork 1
Implement Angle Interactions #282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 15 commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
173bf27
Implement angle potentials and unit test.
clpetix 1f8266b
Update Angle and unit tests.
clpetix f41d65f
Implement and unittest cosine angle.
clpetix 65ad394
Fix documentation for angle potentials and update examples.
clpetix ad58150
Keep angles.py consistent with bonds.py.
clpetix 724b686
Update documentation.
clpetix 869a946
Add angle to documentation.
clpetix 875f8ab
Add angle potential tabulator.
clpetix f7a2a0f
Attach angles to HOOMD
clpetix ae271e4
Add angle potentials to LAMMPS.
clpetix d0542bb
Finalize adding angle support to lammps.py.
clpetix 1768f78
Make test_angles conform with test_bonds.
clpetix 7a2509b
Add in tests for HOOMD and LAMMPS.
clpetix 639b4ac
Fix angle derivative test.
clpetix 7882c8c
Make docstring underline appropriate length.
clpetix 5946188
Refactor CosineSquaredAngle to HarmonicCosineAngle.
clpetix b7f8212
Put in factor of half in HarmonicCosineAngle.
clpetix f796f50
Fix AnglePotentialTabulator doc string.
clpetix a6eaf7c
Fix bonded exclusions when None.
clpetix 5f3488f
Use multiple inheritance for BondSpline and AngleSpline.
clpetix a7513ac
Document AnglePotential to describe how the angle is calculated.
clpetix e71ae01
Fix documentation.
clpetix bf89323
Fix documentation order.
clpetix 43a6262
Refactor to use generalized coordinate name in BondedSpline.
clpetix aab76b7
Fix documentation for BondSpline.
clpetix 1db111b
Add a bound check on angle spline.
clpetix 657b30a
Merge branch 'main' into feature/angle-interactions
clpetix eb46f04
Apply suggestions from code review
clpetix a82e867
Ensure bond and angle use r and theta throughout.
clpetix 2188a73
Apply suggestions from code review
mphoward File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,329 @@ | ||
| import numpy | ||
|
|
||
| from relentless.model.potential.bond import BondSpline | ||
|
|
||
| from . import potential | ||
|
|
||
|
|
||
| class AngleParameters(potential.Parameters): | ||
| """Parameters for a angle potential.""" | ||
|
|
||
| pass | ||
|
|
||
|
|
||
| class AnglePotential(potential.BondedPotential): | ||
| r"""Abstract base class for an angle potential.""" | ||
|
|
||
| pass | ||
|
|
||
|
|
||
| class HarmonicAngle(AnglePotential): | ||
| r"""Harmonic angle potential. | ||
|
|
||
| .. math:: | ||
|
|
||
| u(\theta) = \frac{k}{2} (\theta - \theta_0)^2 | ||
|
|
||
| where :math:`\theta` is the angle between three bonded particles. The parameters | ||
| for each type are: | ||
|
|
||
| +-------------+--------------------------------------------------+ | ||
| | Parameter | Description | | ||
| +=============+==================================================+ | ||
| | ``k`` | Spring constant :math:`k`. | | ||
| +-------------+--------------------------------------------------+ | ||
| | ``theta0`` | Minimum-energy angle :math:`\theta_0`. | | ||
| +-------------+--------------------------------------------------+ | ||
|
|
||
| Parameters | ||
| ---------- | ||
| types : tuple[str] | ||
| Types. | ||
| name : str | ||
| Unique name of the potential. Defaults to ``__u[id]``, where ``id`` is the | ||
| unique integer ID of the potential. | ||
|
|
||
| Attributes | ||
| ---------- | ||
| coeff : :class:`AngleParameters` | ||
| Parameters of the potential for each type. | ||
|
|
||
| Examples | ||
| -------- | ||
| Harmonic Angle:: | ||
|
|
||
| >>> u = relentless.potential.angle.HarmonicAngle(("A",)) | ||
| >>> u.coeff["A"].update({'k': 1000, 'theta0': 1.9}) | ||
|
|
||
| """ | ||
|
|
||
| def __init__(self, types, name=None): | ||
| super().__init__(keys=types, params=("k", "theta0"), name=name) | ||
|
|
||
| def energy(self, type_, theta): | ||
| """Evaluate angle energy.""" | ||
| params = self.coeff.evaluate(type_) | ||
| k = params["k"] | ||
| theta0 = params["theta0"] | ||
|
|
||
| theta, u, s = self._zeros(theta) | ||
|
|
||
| u = 0.5 * k * (theta - theta0) ** 2 | ||
|
|
||
| if s: | ||
| u = u.item() | ||
| return u | ||
|
|
||
| def force(self, type_, theta): | ||
| """Evaluate angle force.""" | ||
| params = self.coeff.evaluate(type_) | ||
| k = params["k"] | ||
| theta0 = params["theta0"] | ||
|
|
||
| theta, f, s = self._zeros(theta) | ||
|
|
||
| f = -k * (theta - theta0) | ||
|
|
||
| if s: | ||
| f = f.item() | ||
| return f | ||
|
|
||
| def _derivative(self, param, theta, k, theta0): | ||
| r"""Evaluate angle derivative with respect to a variable.""" | ||
| theta, d, s = self._zeros(theta) | ||
|
|
||
| if param == "k": | ||
| d = (theta - theta0) ** 2 / 2 | ||
| elif param == "theta0": | ||
| d = -k * (theta - theta0) | ||
| else: | ||
| raise ValueError("Unknown parameter") | ||
|
|
||
| if s: | ||
| d = d.item() | ||
| return d | ||
|
|
||
|
|
||
| class CosineSquaredAngle(AnglePotential): | ||
clpetix marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| r"""Cosine squared angle potential. | ||
|
|
||
| .. math:: | ||
|
|
||
| u(\theta) = k * (cos(\theta) - cos(\theta_0))^2 | ||
clpetix marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| where :math:`\theta` is the angle between three bonded particles. The parameters | ||
| for each type are: | ||
|
|
||
| +-------------+--------------------------------------------------+ | ||
| | Parameter | Description | | ||
| +=============+==================================================+ | ||
| | ``k`` | Spring constant :math:`k`. | | ||
| +-------------+--------------------------------------------------+ | ||
| | ``theta0`` | Minimum-energy angle :math:`\theta_0`. | | ||
| +-------------+--------------------------------------------------+ | ||
|
|
||
| Parameters | ||
| ---------- | ||
| types : tuple[str] | ||
| Types. | ||
| name : str | ||
| Unique name of the potential. Defaults to ``__u[id]``, where ``id`` is the | ||
| unique integer ID of the potential. | ||
|
|
||
| Attributes | ||
| ---------- | ||
| coeff : :class:`AngleParameters` | ||
| Parameters of the potential for each type. | ||
|
|
||
| Examples | ||
| -------- | ||
| Cosine Squared Angle:: | ||
|
|
||
| >>> u = relentless.potential.angle.CosineSquaredAngle(("A",)) | ||
| >>> u.coeff["A"].update({'k': 1000, 'theta0': 1}) | ||
|
|
||
| """ | ||
|
|
||
| def __init__(self, types, name=None): | ||
| super().__init__(keys=types, params=("k", "theta0"), name=name) | ||
|
|
||
| def energy(self, type_, theta): | ||
| """Evaluate angle energy.""" | ||
| params = self.coeff.evaluate(type_) | ||
| k = params["k"] | ||
| theta0 = params["theta0"] | ||
|
|
||
| theta, u, s = self._zeros(theta) | ||
|
|
||
| u = k * (numpy.cos(theta) - numpy.cos(theta0)) ** 2 | ||
|
|
||
| if s: | ||
| u = u.item() | ||
| return u | ||
|
|
||
| def force(self, type_, theta): | ||
| """Evaluate angle force.""" | ||
| params = self.coeff.evaluate(type_) | ||
| k = params["k"] | ||
| theta0 = params["theta0"] | ||
|
|
||
| theta, f, s = self._zeros(theta) | ||
|
|
||
| f = 2 * k * (numpy.cos(theta) - numpy.cos(theta0)) * numpy.sin(theta) | ||
|
|
||
| if s: | ||
| f = f.item() | ||
| return f | ||
|
|
||
| def _derivative(self, param, theta, k, theta0): | ||
| r"""Evaluate angle derivative with respect to a variable.""" | ||
| theta, d, s = self._zeros(theta) | ||
|
|
||
| if param == "k": | ||
| d = (numpy.cos(theta) - numpy.cos(theta0)) ** 2 | ||
| elif param == "theta0": | ||
| d = 2 * k * (numpy.cos(theta) - numpy.cos(theta0)) * numpy.sin(theta0) | ||
| else: | ||
| raise ValueError("Unknown parameter") | ||
|
|
||
| if s: | ||
| d = d.item() | ||
| return d | ||
|
|
||
|
|
||
| class CosineAngle(AnglePotential): | ||
| r"""Cosine angle potential. | ||
|
|
||
| .. math:: | ||
|
|
||
| u(\theta) = k * (1 + cos(\theta)) | ||
clpetix marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| where :math:`\theta` is the angle between three bonded particles. The parameters | ||
| for each type are: | ||
|
|
||
| +-------------+--------------------------------------------------+ | ||
| | Parameter | Description | | ||
| +=============+==================================================+ | ||
| | ``k`` | Spring constant :math:`k`. | | ||
| +-------------+--------------------------------------------------+ | ||
|
|
||
| Parameters | ||
| ---------- | ||
| types : tuple[str] | ||
| Types. | ||
| name : str | ||
| Unique name of the potential. Defaults to ``__u[id]``, where ``id`` is the | ||
| unique integer ID of the potential. | ||
|
|
||
| Attributes | ||
| ---------- | ||
| coeff : :class:`AngleParameters` | ||
| Parameters of the potential for each type. | ||
|
|
||
| Examples | ||
| -------- | ||
| Cosine Angle:: | ||
|
|
||
| >>> u = relentless.potential.angle.CosineAngle(("A",)) | ||
| >>> u.coeff["A"].update({'k': 1000}) | ||
|
|
||
| """ | ||
|
|
||
| def __init__(self, types, name=None): | ||
| super().__init__(keys=types, params=("k",), name=name) | ||
|
|
||
| def energy(self, type_, theta): | ||
| """Evaluate angle energy.""" | ||
| params = self.coeff.evaluate(type_) | ||
| k = params["k"] | ||
|
|
||
| theta, u, s = self._zeros(theta) | ||
|
|
||
| u = k * (1 + numpy.cos(theta)) | ||
|
|
||
| if s: | ||
| u = u.item() | ||
| return u | ||
|
|
||
| def force(self, type_, theta): | ||
| """Evaluate angle force.""" | ||
| params = self.coeff.evaluate(type_) | ||
| k = params["k"] | ||
|
|
||
| theta, f, s = self._zeros(theta) | ||
|
|
||
| f = k * numpy.sin(theta) | ||
|
|
||
| if s: | ||
| f = f.item() | ||
| return f | ||
|
|
||
| def _derivative(self, param, theta, k): | ||
| r"""Evaluate angle derivative with respect to a variable.""" | ||
| theta, d, s = self._zeros(theta) | ||
|
|
||
| if param == "k": | ||
| d = 1 + numpy.cos(theta) | ||
| else: | ||
| raise ValueError("Unknown parameter") | ||
|
|
||
| if s: | ||
| d = d.item() | ||
| return d | ||
|
|
||
|
|
||
| class AngleSpline(BondSpline): | ||
| """Spline angle potential. | ||
|
|
||
| The angle potential is defined by interpolation through a set of knot points. | ||
| The interpolation scheme uses Akima splines. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| types : tuple[str] | ||
| Types. | ||
| num_knots : int | ||
| Number of knots. | ||
| mode : str | ||
| Mode for storing the values of the knots in | ||
| :class:`~relentless.variable.Variable` that can be optimized. If | ||
| ``mode='value'``, the knot amplitudes are manipulated directly. | ||
| If ``mode='diff'``, the amplitude of the *last* knot is fixed, and | ||
| differences between neighboring knots are manipulated for all other | ||
| knots. Defaults to ``'diff'``. | ||
| name : str | ||
| Unique name of the potential. Defaults to ``__u[id]``, where ``id`` is the | ||
| unique integer ID of the potential. | ||
|
|
||
| Examples | ||
| -------- | ||
| The spline potential is setup from a tabulated potential instead of | ||
| specifying knot parameters directly:: | ||
|
|
||
| spline = relentless.potential.angle.AngleSpline(types=(angleA,), num_knots=3) | ||
| spline.from_array(("angleA",),[0,1,2],[4,2,0]) | ||
|
|
||
| However, the knot variables can be iterated over and manipulated directly:: | ||
|
|
||
| for r,k in spline.knots("angleA"): | ||
| k.value = 1.0 | ||
|
|
||
| """ | ||
|
|
||
| def __init__(self, types, num_knots, mode="diff", name=None): | ||
| super().__init__(types=types, num_knots=num_knots, mode=mode, name=name) | ||
|
|
||
| def from_array(self, types, theta, u): | ||
| return super().from_array(types=types, r=theta, u=u) | ||
|
|
||
| def energy(self, types, theta): | ||
| """Evaluate angle energy.""" | ||
| return super().energy(types=types, r=theta) | ||
|
|
||
| def force(self, types, theta): | ||
| """Evaluate angle force.""" | ||
| return super().force(types=types, r=theta) | ||
|
|
||
| def _derivative(self, param, theta, **params): | ||
| """Evaluate angle derivative with respect to a variable.""" | ||
| return super()._derivative(param=param, r=theta, **params) | ||
clpetix marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.