Tutorial 5 – SHARC MD with NN Model

In Tutorial 3, we showed how to train a NN model for predicting energies, forces and nonadiabatic couplings. Here we demonstrate, how to use the trained model, when running surface hopping molecular dynamics simulations with SHARC 3.0.

Some Text about SHARC …

[1]:
%source $SHARC/sharcvars.sh
UsageError: Line magic function `%source` not found.
[2]:
import os, sys
import spainn
from spainn.interface.sharcinterface import SHARC_NN
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[2], line 2
      1 import os, sys
----> 2 import spainn
      3 from spainn.interface.sharcinterface import SHARC_NN

ModuleNotFoundError: No module named 'spainn'

Molecule out of Dataset

In the following we create an Atoms object from the symbols and positions of our target molecule.

[3]:
# molecular symbols of target structure (same as database)
symbols = 'CNHHHH'
# positions of atoms in target structure (not included in database)
positions = np.array(
    [[ 0.0000,  0.0000,  0.0000 ],
     [ 2.4321,  0.0000,  0.0000 ],
     [-1.0111,  1.7951,  0.0000 ],
     [ 3.4373,  1.6202, -0.2566 ],
     [ 3.4373, -1.6202,  0.2566 ],
     [-1.0111, -1.7951,  0.0000 ]]
)
# create ase Atoms object
target_mol = ase.Atoms(symbols=symbols, positions=positions)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 4
      2 symbols = 'CNHHHH'
      3 # positions of atoms in target structure (not included in database)
----> 4 positions = np.array(
      5     [[ 0.0000,  0.0000,  0.0000 ],
      6      [ 2.4321,  0.0000,  0.0000 ],
      7      [-1.0111,  1.7951,  0.0000 ],
      8      [ 3.4373,  1.6202, -0.2566 ],
      9      [ 3.4373, -1.6202,  0.2566 ],
     10      [-1.0111, -1.7951,  0.0000 ]]
     11 )
     12 # create ase Atoms object
     13 target_mol = ase.Atoms(symbols=symbols, positions=positions)

NameError: name 'np' is not defined

Define the calculator

In this step, we define a calculator for predicting for the target geometry the properties. Therefore, we have to specify the path to the trained model (see Tutorial 3) as well as the neighbor list for building the representation.

[4]:
from schnetpack.transform import MatScipyNeighborList
from spainn.interface import NacCalculator

# set up calculator for predictions
calc = NacCalculator(
    model_file=os.path.join(os.getcwd(), 'train', 'best_model_E_F_C'),
    neighbor_list=MatScipyNeighborList(cutoff=10.0)
)
# apply calculator to target molecule
target_mol.calc = calc
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[4], line 1
----> 1 from schnetpack.transform import MatScipyNeighborList
      2 from spainn.interface import NacCalculator
      4 # set up calculator for predictions

ModuleNotFoundError: No module named 'schnetpack'

In the next step, we get the predictions from our model, by calling the get_properties function for the set of properties, we are interested in (here energies and nonadiabatic couplings). Note, the NacCalculator always returns nonadiabatic couplings \(\mathbf{C}_{ij}\) rather than the smoothed nonadiabatic couplings \(\mathbf{C}_{ij}^s\) employed in the training. Nevertheless, the predictions, need to be requested with the same property key as used in the training.

[5]:
# predict energies and nacs from NN model
pred = target_mol.get_properties(['energy', 'smooth_nacs'])
pred
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[5], line 2
      1 # predict energies and nacs from NN model
----> 2 pred = target_mol.get_properties(['energy', 'smooth_nacs'])
      3 pred

NameError: name 'target_mol' is not defined

Mean absolute error for energies

[6]:
# reference energies from (6,4)-CASSCF/cc-pVDZ calculations
ref = {'energy': np.array([-94.69603647, -94.43388056, -94.40410243])}

mae = np.mean(
    np.abs(np.asarray(pred['energy']).flatten()
           - np.asarray(ref['energy']).flatten()
          ))

print('MAE: '+str(mae))
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[6], line 2
      1 # reference energies from (6,4)-CASSCF/cc-pVDZ calculations
----> 2 ref = {'energy': np.array([-94.69603647, -94.43388056, -94.40410243])}
      4 mae = np.mean(
      5     np.abs(np.asarray(pred['energy']).flatten()
      6            - np.asarray(ref['energy']).flatten()
      7           ))
      9 print('MAE: '+str(mae))

NameError: name 'np' is not defined

Visualization of prediction quality for energies

[7]:
# define plot
plt.figure(figsize=(5, 5))
plt.scatter(ref['energy'], pred['energy'], s=100.0)
plt.rcParams["figure.autolayout"] = True
plt.rcParams["font.size"] = 14

# get limits of axis and aplly same for x and y-axis (square plot)
limits = [*plt.xlim(), *plt.ylim()]
lower_lim = min(limits)
upper_lim = max(limits)
plt.xlim(lower_lim, upper_lim)
plt.ylim(lower_lim, upper_lim)

# plot y=x line to guide eye
plt.plot([lower_lim-2.5, upper_lim+2.5], [lower_lim-2.5, upper_lim+2.5], c='k', alpha=0.2, linewidth=8, linestyle='-')

plt.xlabel('E$_i$(ref) / Ha')
plt.ylabel('E$_i$(NN) / Ha')
plt.title('MAE: '+str(round(mae, 4)))

plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[7], line 2
      1 # define plot
----> 2 plt.figure(figsize=(5, 5))
      3 plt.scatter(ref['energy'], pred['energy'], s=100.0)
      4 plt.rcParams["figure.autolayout"] = True

NameError: name 'plt' is not defined