Skip to content

String Path

The string method is a common approach for simulating pathways between energy minima. It models these paths as "strings" that are composed of a discrete set of points, called "images," which each correspond to a different geometry of the system along the reaction path. The simulation begins with a guess for the geometry of each image. Over the course of multiple iterations, the string is relaxed by allowing the individual images to descend along the potential energy surface. The images at the endpoints (which correspond to "reactant" and "product" states) are permitted to fully relax into energy minima, while the intermediate images are constrained to occupy a position between their neighboring images.

Upon convergence, the images form a discrete string that follows the minimal energy path from the reactant state to the product state. The string method is conceptually similar to the Nudged Elastic Band (NEB) method, differing primarily in the manner in which the images interact: while the NEB method applies a spring force between adjacent images in order to ensure that they are properly spaced, the string method accomplishes this using a Lagrange multiplier.

As with other iterative relaxation methods, the number of steps required for convergence is dependent upon the quality of the initial guess. It can sometimes be preferable to perform independent geometry relaxations of the endpoints before utilizing the string method.

By default, images are aligned (the RMSD is minimized) towards the starting image. In general this will produce a lower energy string. For some images this may not be desirable and can be turned off by setting align_initial_images to False in the details section.

Examples

The following example simulates the reaction path for isomerization of HCN into CNH:

import sierra
from sierra.inputs import *

import numpy as np

# Construct an initial guess for the reaction path
symbols = ["H", "C", "N"]
cn_geometry = [0, 0, 1.1, 0, 0, -1.1]
initial_images = []
for degree in np.linspace(0, np.pi, 8):
    geometry = [0, np.sin(degree) * 2.3, np.cos(degree) * 2.3] + cn_geometry
    initial_images.append(Molecule(symbols=symbols, geometry=geometry))

# Run the String Path Workflow
inp = StringPathInput(
    initial_string=initial_images,
    details={"max_iterations": 60, "time_step": 0.1, "trust_radius": 0.2},
)
ret = sierra.run(inp)

# Print the geometry of each image in the reaction path
for index, image in enumerate(ret.final_string):
    print("Image " + str(index + 1) + " geometry:")
    #> Image 1 geometry:
    #> Image 2 geometry:
    """
    [[ 0.          0.          2.75347514]
     [ 0.          0.          0.83881242]
     [ 0.          0.         -1.29228756]]
    """
    #> Image 3 geometry:
    """
    [[ 0.          0.32186689  2.61230172]
     [ 0.         -0.444149    0.87550359]
     [ 0.          0.12228211 -1.18780531]]
    """
    #> Image 4 geometry:
    """
    [[ 0.          0.56509899  2.3375639 ]
     [ 0.         -0.82405571  0.93343731]
     [ 0.          0.25895671 -0.97100121]]
    """
    #> Image 5 geometry:
    """
    [[ 0.          0.76609393  2.06966877]
     [ 0.         -1.18002147  0.91048689]
     [ 0.          0.41392754 -0.68015566]]
    """
    #> Image 6 geometry:
    """
    [[ 0.          0.99679701  1.87166118]
     [ 0.         -1.50195635  0.76048018]
     [ 0.          0.50515933 -0.33214135]]
    """
    #> Image 7 geometry:
    """
    [[ 0.          1.2850534   1.73364462]
     [ 0.         -1.74491802  0.53346002]
     [ 0.          0.45986462  0.03289536]]
    """
    #> Image 8 geometry:
    """
    [[ 0.          1.611249    1.59015843]
     [ 0.         -1.89487198  0.32486507]
     [ 0.          0.28362298  0.3849765 ]]
    """
    print(image.geometry)
    """
    [[ 0.          1.83570771  1.32879427]
     [ 0.         -1.9487609   0.16992009]
     [ 0.          0.11305319  0.80128564]]
    """

The following example examines the dissociation curve of H2. It provides only the starting and ending images, with an additional 9 images being automatically generated to form a string between these endpoints. The endpoints are frozen in order to prevent them from relaxing to the minimum energy bond length for H2.

import sierra
from sierra.inputs import *

start = {"symbols": ["H", "H"], "geometry": [0, 0, 0, 0, 0, 1.0]}
end = {"symbols": ["H", "H"], "geometry": [0, 0, 0, 0, 0, 2.0]}

inp = StringPathInput(
    initial_string=[start, end],
    details={
        "n_intermediates": 9,
        "freeze_start_image": True,
        "freeze_end_image": True,
    },
)
ret = sierra.run(inp)

# Print the geometry of each of the images
for index, image in enumerate(ret.final_string):
    print("Image " + str(index + 1) + " geometry:")
    #> Image 1 geometry:
    #> Image 2 geometry:
    #> [[0. 0. 0.]
    #>  [0. 0. 1.]]
    #> Image 3 geometry:
    #> [[ 0.    0.   -0.05]
    #>  [ 0.    0.    1.05]]
    #> Image 4 geometry:
    #> [[ 0.   0.  -0.1]
    #>  [ 0.   0.   1.1]]
    #> Image 5 geometry:
    #> [[ 0.    0.   -0.15]
    #>  [ 0.    0.    1.15]]
    #> Image 6 geometry:
    #> [[ 0.   0.  -0.2]
    #>  [ 0.   0.   1.2]]
    #> Image 7 geometry:
    #> [[ 0.    0.   -0.25]
    #>  [ 0.    0.    1.25]]
    #> Image 8 geometry:
    #> [[ 0.   0.  -0.3]
    #>  [ 0.   0.   1.3]]
    #> Image 9 geometry:
    #> [[ 0.    0.   -0.35]
    #>  [ 0.    0.    1.35]]
    #> Image 10 geometry:
    #> [[ 0.   0.  -0.4]
    #>  [ 0.   0.   1.4]]
    #> Image 11 geometry:
    #> [[ 0.    0.   -0.45]
    #>  [ 0.    0.    1.45]]
    print(image.geometry)
    #> [[ 0.   0.  -0.5]
    #>  [ 0.   0.   1.5]]

StringPathInput

Class for describing input for the String Path workflow

Fields

energy_method

The method for final energy evaluation. If None the final energies are computed using the level of theory specified by optimization_method.

initial_string

An initial guess for every image in the reaction path, provided in order from the first (e.g., reactant) image to the last (e.g., product) image. If 'n_intermediates' is specified, this number of intermediates will be created between each image in the current string.

optimization_method

The method with which to optimize the string.

workflow
  • Type: WorkflowIdentifier
  • Default: WorkflowIdentifier(name='string_path', image=None)
details

Detail Fields

align_initial_string

If true, align the initial string towards the first (e.g. reactant) images.

  • Type: bool
  • Default: True
convergence_threshold

A convergence threshold that indicates the maximum geometry change for each image between iterations that will be considered converged.

freeze_end_image

If true, the geometry of the last (e.g., product) image is not permitted to relax.

  • Type: bool
  • Default: False
freeze_start_image

If true, the geometry of the first (e.g., reactant) image is not permitted to relax.

  • Type: bool
  • Default: False
initial_string_coordinates

Coordinate system to use for building initial strings.

  • Type: InitialStringCoordinates
  • Default: InitialStringCoordinates.irc
max_iterations

The maximum number of iterations to complete.

  • Type: int
  • Default: 60
n_intermediates

The number of intermediate images that will be automatically generated when the user does not specify the intermediate images via the initial_intermediates option.

  • Type: int
  • Default: 0
smooth_factor

The degree to which a "smooth" progression between the geometries of adjacent images is enforced. This parameter does not affect the geometries of the endpoints. Values must be in the range [0, 1], with 0 corresponding to no smoothing and 1 forcing the geometries of non-endpoint images to an average of the geometries of adjacent images.

  • Type: float
  • Default: 0.01
step_size

The step size (\(\Delta t\)) used to evolve the string (\(\psi\)).

Given the gradients (\(\nabla V\)) the string evolves using steepest-decent dynamics. The new position of the i-th image before re-parameterization can be computed [ \psi^*_i = \psi^n_i - \Delta t \nabla V\left(\psi^n_i\right) ] where \(\psi^n_i\) is the i-th image on the string at the n-th iteration.

  • Type: float
  • Default: 0.01
trust_radius

The maximum allowed atom displacement each iteration.

StringPathResult

Class for describing results from the String Path workflow

Fields

All the fields in StringPathInput and the following:

computation_history
converged
  • Type: bool
  • Default: False
energy_history
  • Type: List[Array]
  • Default: []
final_energies
  • Type: List[float]
  • Default: []
final_string

Functions

StringPathResult.visualize

visualize() -> None:

The results of a string path calculation can be displayed by using this function. CAVEAT: This function can only be used inside a jupyter notebook.

Returns

None