Correlation Plots for Computed Results

This example illustrates how to plot, for example, forces predicted by one engine against forces predicted by another.

Downloads: Notebook | Script ?

Requires: AMS2027 or later

Related tutorials
Related documentation
../_images/plot_correlation_26_0_53502258.png

Initial imports

from scm.inputs import AMS, DFTB, GFNFF
import scm.plams as plams
from scm.plams.tools.plot import plot_correlation, get_correlation_xy
import matplotlib.pyplot as plt

Define two engines to compare

Here we choose GFNFF and GFN1-xTB

e1 = GFNFF()

e2 = DFTB(Model="GFN1-xTB")

Let’s use a glycine molecule generated from SMILES:

from scm.base import ChemicalSystem

glycine = ChemicalSystem.from_smiles("C(C(=O)O)N")
plams.view(glycine, width=200, height=200, padding=-0.5, direction="along_pca3")
image generated from notebook

Run a single-point calculation storing the Gradients (negative forces):

def serial_single_point_settings(engine):
    settings = plams.Settings()
    settings.input = AMS(Task="SinglePoint", Properties={"Gradients": True}, Engine=engine)
    settings.runscript.nproc = 1
    return settings
job1 = plams.AMSJob(settings=serial_single_point_settings(e1), name="glycine-engine1", molecule=glycine)
job2 = plams.AMSJob(settings=serial_single_point_settings(e2), name="glycine-engine2", molecule=glycine)
job1.run()
job2.run()
[19.10|10:20:22] JOB glycine-engine1 STARTED
[19.10|10:20:22] JOB glycine-engine1 RUNNING
[19.10|10:20:22] JOB glycine-engine1 FINISHED
[19.10|10:20:22] JOB glycine-engine1 SUCCESSFUL
[19.10|10:20:22] JOB glycine-engine2 STARTED
[19.10|10:20:22] JOB glycine-engine2 RUNNING
[19.10|10:20:23] JOB glycine-engine2 FINISHED
[19.10|10:20:23] JOB glycine-engine2 SUCCESSFUL





<scm.plams.interfaces.adfsuite.ams.AMSResults at 0x1479ea850>
plot_correlation(
    job1,
    job2,
    section="AMSResults",
    variable="Gradients",
    file="engine",
);
image generated from notebook

To get the actual numbers, use get_correlation_xy:

x, y = plams.tools.plot.get_correlation_xy(job1, job2, section="AMSResults", variable="Gradients", file="engine")
print("x")
print(x)
print("y")
print(y)
x
[ 0.00253757 -0.00068745 -0.00162608 -0.04972831  0.01789185  0.04319717
  0.02181405 -0.02616327 -0.04633804  0.00034844 -0.00110677 -0.00293056
  0.01431108 -0.01123263 -0.02527101 -0.00490688 -0.00809675 -0.0019926
 -0.00148234  0.00329265  0.00891155  0.03155499  0.01072519  0.00599753
 -0.02079454 -0.00457986  0.00808843  0.00634593  0.01995704  0.0119636 ]
y
[ 0.0068496   0.00181009 -0.02415863 -0.0688199   0.02558776  0.06788392
  0.02012588 -0.03969176 -0.06713017  0.01189279  0.01231691  0.0114869
  0.01498596 -0.01890143 -0.01177816 -0.00730492 -0.0112359   0.00078268
 -0.000786    0.00195096  0.00525741  0.03916115  0.00988606  0.00294437
 -0.02177257 -0.00105184  0.0055369   0.00566801  0.01932915  0.00917479]

Compare multiple jobs

smiles_list = ["CC=C", "CCCO", "C(C(=O)O)N"]
names = ["propene", "propanol", "glycine"]
molecules = [ChemicalSystem.from_smiles(x) for x in smiles_list]
imgs = {n: plams.view(mol, width=200, height=200, direction="along_pca3") for n, mol in zip(names, molecules)}
plams.plot_image_grid(imgs, rows=1);
image generated from notebook
jobs1 = [
    plams.AMSJob(settings=serial_single_point_settings(e1), name="e1" + name, molecule=mol)
    for name, mol in zip(names, molecules)
]
jobs2 = [
    plams.AMSJob(settings=serial_single_point_settings(e2), name="e2" + name, molecule=mol)
    for name, mol in zip(names, molecules)
]
for job in jobs1 + jobs2:
    job.run()
[19.10|10:20:24] JOB e1propene STARTED
[19.10|10:20:24] JOB e1propene RUNNING
[19.10|10:20:24] JOB e1propene FINISHED
[19.10|10:20:24] JOB e1propene SUCCESSFUL
[19.10|10:20:24] JOB e1propanol STARTED
[19.10|10:20:24] JOB e1propanol RUNNING
[19.10|10:20:25] JOB e1propanol FINISHED
[19.10|10:20:25] JOB e1propanol SUCCESSFUL
[19.10|10:20:25] JOB e1glycine STARTED
[19.10|10:20:25] Job e1glycine previously run as glycine-engine1, using old results
... output trimmed ....
[19.10|10:20:25] JOB e2propene RUNNING
[19.10|10:20:25] JOB e2propene FINISHED
[19.10|10:20:25] JOB e2propene SUCCESSFUL
[19.10|10:20:25] JOB e2propanol STARTED
[19.10|10:20:25] JOB e2propanol RUNNING
[19.10|10:20:25] JOB e2propanol FINISHED
[19.10|10:20:25] JOB e2propanol SUCCESSFUL
[19.10|10:20:25] JOB e2glycine STARTED
[19.10|10:20:25] Job e2glycine previously run as glycine-engine2, using old results
[19.10|10:20:25] JOB e2glycine COPIED

The correlation plot can be plotted as before. You can also add a unit conversion to get your preferred units, and add custom xlabel and ylabel:

unit = "eV/angstrom"
multiplier = plams.Units.convert(1.0, "hartree/bohr", unit)

plot_correlation(
    jobs1,
    jobs2,
    section="AMSResults",
    variable="Gradients",
    file="engine",
    xlabel="Engine 1",
    ylabel="Engine 2",
    unit=unit,
    multiplier=multiplier,
);
image generated from notebook
plot_correlation(
    jobs1,
    jobs2,
    section="AMSResults",
    variable="Charges",
    file="engine",
    xlabel="Engine 1",
    ylabel="Engine 2",
);
image generated from notebook

Use Task Replay to compare multiple frames from a trajectory

The forces from an MD job can be stored with writeenginegradients=True

md = plams.AMSNVEJob(
    settings=AMS(Engine=e1),
    name="nve-md-e1",
    molecule=glycine,
    velocities=400,
    nsteps=100,
    samplingfreq=10,
    writeenginegradients=True,
)
md.run();
[19.10|10:20:26] JOB nve-md-e1 STARTED
[19.10|10:20:26] JOB nve-md-e1 RUNNING
[19.10|10:20:27] JOB nve-md-e1 FINISHED
[19.10|10:20:27] JOB nve-md-e1 SUCCESSFUL

When using the Replay task, set Properties.Gradients to get the forces:

replay_s = AMS(
    Task="Replay",
    Properties={"Gradients": True},
    Replay={"File": md.results.rkfpath()},
    Engine=e2,
)
replay = plams.AMSJob(settings=replay_s, name="replay-e2")
replay.run();
[19.10|10:20:27] JOB replay-e2 STARTED
[19.10|10:20:27] JOB replay-e2 RUNNING
[19.10|10:20:28] JOB replay-e2 FINISHED
[19.10|10:20:28] JOB replay-e2 SUCCESSFUL

For the MD job the gradients (negative forces) are stored in History%EngineGradients, but for the Replay job they are stored in History%Gradients. Use the alt_variable to specify the variable for the second job:

plot_correlation(md, replay, section="History", variable="EngineGradients", alt_variable="Gradients", file="ams");
image generated from notebook

See also

Python Script

#!/usr/bin/env python
# coding: utf-8

# ## Initial imports

from scm.inputs import AMS, DFTB, GFNFF
import scm.plams as plams
from scm.plams.tools.plot import plot_correlation, get_correlation_xy
import matplotlib.pyplot as plt


# ## Define two engines to compare
# 
# Here we choose GFNFF and GFN1-xTB

e1 = GFNFF()

e2 = DFTB(Model="GFN1-xTB")


# Let's use a glycine molecule generated from SMILES:

from scm.base import ChemicalSystem

glycine = ChemicalSystem.from_smiles("C(C(=O)O)N")
plams.view(glycine, width=200, height=200, padding=-0.5, direction="along_pca3", picture_path="picture1.png")


# Run a single-point calculation storing the Gradients (negative forces):

def serial_single_point_settings(engine):
    settings = plams.Settings()
    settings.input = AMS(Task="SinglePoint", Properties={"Gradients": True}, Engine=engine)
    settings.runscript.nproc = 1
    return settings


job1 = plams.AMSJob(settings=serial_single_point_settings(e1), name="glycine-engine1", molecule=glycine)
job2 = plams.AMSJob(settings=serial_single_point_settings(e2), name="glycine-engine2", molecule=glycine)


job1.run()
job2.run()


plot_correlation(
    job1,
    job2,
    section="AMSResults",
    variable="Gradients",
    file="engine",
);


# To get the actual numbers, use ``get_correlation_xy``:

x, y = plams.tools.plot.get_correlation_xy(job1, job2, section="AMSResults", variable="Gradients", file="engine")
print("x")
print(x)
print("y")
print(y)


# ## Compare multiple jobs

smiles_list = ["CC=C", "CCCO", "C(C(=O)O)N"]
names = ["propene", "propanol", "glycine"]
molecules = [ChemicalSystem.from_smiles(x) for x in smiles_list]
imgs = {n: plams.view(mol, width=200, height=200, direction="along_pca3") for n, mol in zip(names, molecules)}
plams.plot_image_grid(imgs, rows=1, save_path="picture2.png");


jobs1 = [
    plams.AMSJob(settings=serial_single_point_settings(e1), name="e1" + name, molecule=mol)
    for name, mol in zip(names, molecules)
]
jobs2 = [
    plams.AMSJob(settings=serial_single_point_settings(e2), name="e2" + name, molecule=mol)
    for name, mol in zip(names, molecules)
]


for job in jobs1 + jobs2:
    job.run()


# The correlation plot can be plotted as before. You can also add a unit conversion to get your preferred units, and add custom xlabel and ylabel:

unit = "eV/angstrom"
multiplier = plams.Units.convert(1.0, "hartree/bohr", unit)

plot_correlation(
    jobs1,
    jobs2,
    section="AMSResults",
    variable="Gradients",
    file="engine",
    xlabel="Engine 1",
    ylabel="Engine 2",
    unit=unit,
    multiplier=multiplier,
);


plot_correlation(
    jobs1,
    jobs2,
    section="AMSResults",
    variable="Charges",
    file="engine",
    xlabel="Engine 1",
    ylabel="Engine 2",
);


# ## Use Task Replay to compare multiple frames from a trajectory

# The forces from an MD job can be stored with ``writeenginegradients=True``

md = plams.AMSNVEJob(
    settings=AMS(Engine=e1),
    name="nve-md-e1",
    molecule=glycine,
    velocities=400,
    nsteps=100,
    samplingfreq=10,
    writeenginegradients=True,
)
md.run();


# When using the Replay task, set ``Properties.Gradients`` to get the forces:

replay_s = AMS(
    Task="Replay",
    Properties={"Gradients": True},
    Replay={"File": md.results.rkfpath()},
    Engine=e2,
)
replay = plams.AMSJob(settings=replay_s, name="replay-e2")
replay.run();


# For the MD job the gradients (negative forces) are stored in ``History%EngineGradients``, but for the Replay job they are stored in ``History%Gradients``. Use the ``alt_variable`` to specify the variable for the second job:

plot_correlation(md, replay, section="History", variable="EngineGradients", alt_variable="Gradients", file="ams");