-
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Hi, First of all, congratulations for double-checking the tabulated potential against a reference potential. Poorly documented features like The dihedral potential is defined on the inclusive range Using testsuite/python/interactions_dihedral.py@ The forces on particles 1 and 3 are obtained by symmetry from the forces on particles 0 and 2, and are thus not represented. The deviation is less than Your first histogram is quite puzzling to me. Maybe your definition of the tabulated dihedral force doesn't match ESPResSo's internal definition. Unfortunately, there aren't many users for the
For dihedrals the negative derivative is used. Also, when the multiplicity of the energy is Please note it is not possible to make further deductions from this plot. In particular, the fact that the tabulated force The script to generate these plots is reproduced below. I hope this was helpful. potential energy surface script (click to unroll)import espressomd
import espressomd.interactions
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
plt.rcParams.update({"font.size": 12})
radians_formatter = mticker.FuncFormatter(lambda x, _: rf"${x / np.pi:.0f}\pi$" if x != 0. else "0")
radians_locator = mticker.MultipleLocator(base=np.pi)
system = espressomd.System(box_l=[10.0, 10.0, 10.0])
system.cell_system.skin = 0.4
system.time_step = 0.1
def potential_energy_surface(bond, resolution=60):
p0 = system.part.add(pos=[5., 6., 5.])
p1 = system.part.add(pos=[5., 5., 5.])
p2 = system.part.add(pos=[6., 5., 5.])
p3 = system.part.add(pos=[5., 6., 5.])
p1.add_bond((bond, p0, p2, p3))
v = np.array([0., 1., 0.])
k = np.array([1., 0., 0.])
xdata = np.linspace(0., 2. * np.pi, num=resolution, endpoint=True)
ydata = []
zdata = []
for phi in xdata:
p3.pos = p2.pos + v * np.cos(phi) + np.cross(k, v) * np.sin(phi) + k * np.dot(k, v) * (1. - np.cos(phi))
system.integrator.run(recalc_forces=True, steps=0)
ydata.append(system.analysis.energy()["bonded"])
zdata.append([p0.f, p1.f, p2.f ,p3.f])
system.part.clear()
ydata = np.array(ydata)
zdata = np.array(zdata)
return (xdata, ydata, zdata)
def make_tabulated(dihedral, N):
assert N % 2 == 0
phi = np.linspace(0., 2. * np.pi, num=N + 1, endpoint=True)
tab_energy = dihedral.bend * (1. - np.cos(dihedral.mult * phi - dihedral.phase))
div = np.sin(phi)
div[0] = div[N // 2] = div[N] = 1.
tab_force = -dihedral.bend * dihedral.mult * np.sin(dihedral.mult * phi - dihedral.phase) / div
tab_force[0] = tab_force[N // 2] = tab_force[N] = 0.
dihedral_tabulated = espressomd.interactions.TabulatedDihedral(
energy=tab_energy, force=tab_force)
return dihedral_tabulated
dihedral = espressomd.interactions.Dihedral(bend=20., mult=1, phase=np.pi)
dihedral_tab = make_tabulated(dihedral, 40)
system.bonded_inter.add(dihedral)
system.bonded_inter.add(dihedral_tab)
xdata, ydata, zdata = potential_energy_surface(dihedral)
xdata, ydata_tab, zdata_tab = potential_energy_surface(dihedral_tab)
# plot potential energy surface
fig, ax = plt.subplots(figsize=(5, 3))
ax.plot(xdata, ydata, "-", label="classic")
ax.plot(xdata, ydata_tab, "o", mfc="none", label="tabulated")
ax.xaxis.set_major_formatter(radians_formatter)
ax.xaxis.set_major_locator(radians_locator)
ax.xaxis.set_label_text("Dihedral angle")
ax.yaxis.set_label_text("Energy (reduced units)")
ax.set_title("Potential energy surface")
ax.legend(loc="upper center")
fig.tight_layout()
plt.show()
# plot force deviations
fig, ax = plt.subplots(figsize=(5, 3))
for i in [0, 2]:
diff = np.linalg.norm(zdata[:,i] - zdata_tab[:,i], axis=1)
N = len(diff)
# erase singularities at 0, pi, 2*pi
for j in [0, 1, N // 2 - 1, N // 2, N - 2, N - 1]:
diff[j] = np.nan
ax.plot(xdata, diff, "-", label=rf"$\Delta F_{i}$")
ax.xaxis.set_major_formatter(radians_formatter)
ax.xaxis.set_major_locator(radians_locator)
ax.xaxis.set_label_text("Dihedral angle")
ax.yaxis.set_label_text("Force deviation (reduced units)")
ax.set_title("Numerical error in the forces")
ax.legend(loc="upper center")
fig.tight_layout()
plt.show()
# plot multiplicity of the derivative
dihedral = espressomd.interactions.Dihedral(bend=20., mult=2, phase=np.pi)
dihedral_tab = make_tabulated(dihedral, 180)
tab_energy = np.copy(dihedral_tab.energy)
tab_force = np.copy(dihedral_tab.force)
N = len(tab_force)
tab_phi = np.linspace(0., 2. * np.pi, num=N, endpoint=True)
for j in [0, 1, N // 2 - 1, N // 2, N - 2, N - 1]:
tab_force[j] = np.nan
fig, ax = plt.subplots(figsize=(5, 3))
ax.plot(tab_phi, tab_force, label="force")
ax.plot(tab_phi, tab_energy, label="energy")
ax.xaxis.set_major_formatter(radians_formatter)
ax.xaxis.set_major_locator(radians_locator)
ax.yaxis.set_major_locator(mticker.MultipleLocator(base=2. * dihedral.bend))
ax.xaxis.set_label_text("Dihedral angle")
ax.yaxis.set_label_text("Functional (reduced units)")
ax.legend(loc="upper center")
ax.spines["bottom"].set_position(("data", 0))
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
fig.tight_layout()
plt.show() |
Beta Was this translation helpful? Give feedback.
I would say yes. The source code is a bit cryptic, so I cannot answer you with full certainty.
The term$1/\sin(\phi)$ is due to how the derivative is obtained. The quantity $-\partial_{\phi} V(\phi)$ yields the magnitude of the force, but one still needs to compute the gradient of the dihedral angle with respect to the particle coordinates, in order to get a force vector.
There are several ways of expressing the derivative. Using the chain rule, and depending on whether the source code evaluates$\cos(\phi)$ …