Basic Tutorial

# Load in the relevant packages

from ase.io import read,write
from ase.build import bulk
import numpy as np
import ase.db as db
from ase.visualize import view
from ase.calculators.lj import LennardJones
import matplotlib.pyplot as plt

Fit_on_forces=True  #Enable/disable option for fitting CCS potential to atomic forces. 

Generate the CCS_fit input file; structures.json

The next commands fetch the training set data from the ASE database containing the LJ structures and the corresponding energies. The reference data is called LJ.db since the reference energies and forces are obtained from a Lennard Jones potential.

from ccs_fit.scripts.ccs_fetch import ccs_fetch

ccs_fetch(mode="CCS",DFT_DB="LJ.db",include_forces=Fit_on_forces)
    Fetching data:   0%|          | 0/40 [00:00<?, ?it/s]
    Fetching data:  18%|█▊        | 7/40 [00:00<00:00, 65.69it/s]
    Fetching data:  35%|███▌      | 14/40 [00:00<00:00, 48.30it/s]
    Fetching data:  50%|█████     | 20/40 [00:00<00:00, 42.40it/s]
    Fetching data:  68%|██████▊   | 27/40 [00:00<00:00, 50.06it/s]
    Fetching data:  82%|████████▎ | 33/40 [00:00<00:00, 42.96it/s]
    Fetching data: 100%|██████████| 40/40 [00:00<00:00, 49.67it/s]
    Fetching data: 100%|██████████| 40/40 [00:00<00:00, 48.25it/s]

Fit training data to Curvature Constrained Splines

Finally, the splines are fitted to the target defined in the structures.json input file. The splines can be restricted to be fully repulsive (“rep”), or have a turning point/switch (“sw”), which is defined by the “Swtype” key. A more comprehensive guide on the fitting options can be found in Advanced_Tutorials/Search_Mode.

### Generate input.json file
import json

input={
    "General": {
        "interface": "CCS",
        "merging"  : "True"
    },
    "Twobody": {
                "Ne-Ne": {
                        "Rcut": 5.0,
                        "Resolution": 0.01,
                        "Swtype": "sw",
                        "const_type" : "Mono"
                }
        }
}
#SAVE TO FILE
with open('CCS_input.json', 'w') as f:
    json.dump(input, f, indent=8)
#RUN FIT
from ccs_fit import ccs_fit

ccs_fit("CCS_input.json")
    Generating one-body information from training-set.
        Added elements:  ['Ne']
    Applying monotonic constraints for pair:  Ne-Ne
    Merging intervals for pair Ne-Ne; number of intervals reduced from 412 to 411. 
    Finding optimum switch:   0%|          | 0/411 [00:00<?, ?it/s]
    Finding optimum switch:   0%|          | 1/411 [00:00<01:31,  4.50it/s]
    Finding optimum switch:   0%|          | 2/411 [00:00<01:27,  4.68it/s]
    Finding optimum switch:   1%|          | 3/411 [00:00<01:26,  4.72it/s]
    Finding optimum switch:   1%|          | 4/411 [00:00<01:27,  4.64it/s]
    Finding optimum switch:   1%|          | 5/411 [00:01<01:27,  4.66it/s]
    Finding optimum switch:   1%|▏         | 6/411 [00:01<01:24,  4.78it/s]
    Finding optimum switch:   2%|▏         | 7/411 [00:01<01:21,  4.93it/s]
    Finding optimum switch:   2%|▏         | 8/411 [00:01<01:20,  5.04it/s]
    Finding optimum switch:   2%|▏         | 9/411 [00:01<01:17,  5.19it/s]
    Finding optimum switch:   2%|▏         | 10/411 [00:02<01:16,  5.24it/s]
    Finding optimum switch:   3%|▎         | 11/411 [00:02<01:14,  5.34it/s]
    Finding optimum switch:   3%|▎         | 12/411 [00:02<01:12,  5.50it/s]
    Finding optimum switch:   3%|▎         | 13/411 [00:02<01:07,  5.92it/s]
    Finding optimum switch:   3%|▎         | 14/411 [00:02<01:05,  6.03it/s]
    Finding optimum switch:   4%|▎         | 15/411 [00:02<01:05,  6.09it/s]
    Finding optimum switch:   4%|▍         | 16/411 [00:02<01:05,  6.00it/s]
    Finding optimum switch:   4%|▍         | 17/411 [00:03<01:05,  6.02it/s]
    Finding optimum switch:   4%|▍         | 18/411 [00:03<01:04,  6.05it/s]
    Finding optimum switch:   5%|▍         | 19/411 [00:03<01:02,  6.29it/s]
    Finding optimum switch:   5%|▍         | 20/411 [00:03<01:00,  6.44it/s]
    Finding optimum switch:   5%|▌         | 21/411 [00:03<00:58,  6.70it/s]
    Finding optimum switch:   5%|▌         | 22/411 [00:03<00:56,  6.90it/s]
    Finding optimum switch:   6%|▌         | 23/411 [00:04<00:54,  7.09it/s]
    Finding optimum switch:   6%|▌         | 24/411 [00:04<00:53,  7.28it/s]
    Finding optimum switch:   6%|▌         | 25/411 [00:04<00:53,  7.26it/s]
    Finding optimum switch:   6%|▋         | 26/411 [00:04<00:54,  7.09it/s]
    Finding optimum switch:   7%|▋         | 27/411 [00:04<00:55,  6.91it/s]
    Finding optimum switch:   7%|▋         | 28/411 [00:04<00:56,  6.78it/s]
    Finding optimum switch:   7%|▋         | 29/411 [00:04<00:58,  6.56it/s]
    Finding optimum switch:   7%|▋         | 30/411 [00:05<01:01,  6.22it/s]
    Finding optimum switch:   8%|▊         | 31/411 [00:05<01:01,  6.18it/s]
    Finding optimum switch:   8%|▊         | 32/411 [00:05<01:00,  6.21it/s]
    Finding optimum switch:   8%|▊         | 33/411 [00:05<00:59,  6.37it/s]
    Finding optimum switch:   8%|▊         | 34/411 [00:05<00:58,  6.41it/s]
    Finding optimum switch:   9%|▊         | 35/411 [00:05<01:01,  6.13it/s]
    Finding optimum switch:   9%|▉         | 36/411 [00:06<01:05,  5.72it/s]
    Finding optimum switch:   9%|▉         | 37/411 [00:06<01:06,  5.67it/s]
    Finding optimum switch:   9%|▉         | 38/411 [00:06<01:05,  5.69it/s]
    Finding optimum switch:   9%|▉         | 39/411 [00:06<01:07,  5.52it/s]
    Finding optimum switch:  10%|▉         | 40/411 [00:06<01:07,  5.51it/s]
    Finding optimum switch:  10%|▉         | 41/411 [00:06<01:06,  5.55it/s]
    Finding optimum switch:  10%|█         | 42/411 [00:07<01:04,  5.74it/s]
    Finding optimum switch:  10%|█         | 43/411 [00:07<01:03,  5.80it/s]
    Finding optimum switch:  11%|█         | 44/411 [00:07<01:02,  5.84it/s]
    Finding optimum switch:  11%|█         | 45/411 [00:07<01:02,  5.89it/s]
    Finding optimum switch:  11%|█         | 46/411 [00:07<01:01,  5.96it/s]
    Finding optimum switch:  11%|█▏        | 47/411 [00:07<01:00,  6.05it/s]
    Finding optimum switch:  12%|█▏        | 48/411 [00:08<00:59,  6.07it/s]
    Finding optimum switch:  12%|█▏        | 49/411 [00:08<00:58,  6.14it/s]
    Finding optimum switch:  12%|█▏        | 50/411 [00:08<00:58,  6.20it/s]
    Finding optimum switch:  12%|█▏        | 51/411 [00:08<00:58,  6.15it/s]
    Finding optimum switch:  13%|█▎        | 52/411 [00:08<00:57,  6.20it/s]
    Finding optimum switch:  13%|█▎        | 53/411 [00:08<00:56,  6.35it/s]
    Finding optimum switch:  13%|█▎        | 54/411 [00:09<00:57,  6.26it/s]
    Finding optimum switch:  13%|█▎        | 55/411 [00:09<00:56,  6.26it/s]
    Finding optimum switch:  14%|█▎        | 56/411 [00:09<00:56,  6.33it/s]
    Finding optimum switch:  14%|█▍        | 57/411 [00:09<00:56,  6.27it/s]
    Finding optimum switch:  14%|█▍        | 58/411 [00:09<00:56,  6.28it/s]
    Finding optimum switch:  14%|█▍        | 59/411 [00:09<00:55,  6.35it/s]
    Finding optimum switch:  15%|█▍        | 60/411 [00:10<00:56,  6.25it/s]
    Finding optimum switch:  15%|█▍        | 61/411 [00:10<00:55,  6.27it/s]
    Finding optimum switch:  15%|█▌        | 62/411 [00:10<00:54,  6.40it/s]
    Finding optimum switch:  15%|█▌        | 63/411 [00:10<00:54,  6.37it/s]
    Finding optimum switch:  16%|█▌        | 64/411 [00:10<00:55,  6.30it/s]
    Finding optimum switch:  16%|█▌        | 65/411 [00:10<00:54,  6.30it/s]
    Finding optimum switch:  16%|█▌        | 66/411 [00:10<00:54,  6.34it/s]
    Finding optimum switch:  16%|█▋        | 67/411 [00:11<00:54,  6.30it/s]
    Finding optimum switch:  17%|█▋        | 68/411 [00:11<00:53,  6.36it/s]
    Finding optimum switch:  17%|█▋        | 69/411 [00:11<00:53,  6.35it/s]
    Finding optimum switch:  17%|█▋        | 70/411 [00:11<00:53,  6.34it/s]
    Finding optimum switch:  17%|█▋        | 71/411 [00:11<00:53,  6.39it/s]
    Finding optimum switch:  18%|█▊        | 72/411 [00:11<00:53,  6.34it/s]
    Finding optimum switch:  18%|█▊        | 73/411 [00:12<00:54,  6.15it/s]
    Finding optimum switch:  18%|█▊        | 74/411 [00:12<00:54,  6.20it/s]
    Finding optimum switch:  18%|█▊        | 75/411 [00:12<00:53,  6.28it/s]
    Finding optimum switch:  18%|█▊        | 76/411 [00:12<00:53,  6.25it/s]
    Finding optimum switch:  19%|█▊        | 77/411 [00:12<00:52,  6.39it/s]
    Finding optimum switch:  19%|█▉        | 78/411 [00:12<00:50,  6.55it/s]
    Finding optimum switch:  19%|█▉        | 79/411 [00:13<00:49,  6.68it/s]
    Finding optimum switch:  19%|█▉        | 80/411 [00:13<00:49,  6.65it/s]
    Finding optimum switch:  20%|█▉        | 81/411 [00:13<00:48,  6.74it/s]
    Finding optimum switch:  20%|█▉        | 82/411 [00:13<00:47,  6.88it/s]
    Finding optimum switch:  20%|██        | 83/411 [00:13<00:47,  6.91it/s]
    Finding optimum switch:  20%|██        | 84/411 [00:13<00:46,  7.00it/s]
    Finding optimum switch:  21%|██        | 85/411 [00:13<00:46,  7.06it/s]
    Finding optimum switch:  21%|██        | 86/411 [00:14<00:46,  7.04it/s]
    Finding optimum switch:  21%|██        | 87/411 [00:14<00:46,  6.91it/s]
    Finding optimum switch:  21%|██▏       | 88/411 [00:14<00:47,  6.85it/s]
    Finding optimum switch:  22%|██▏       | 89/411 [00:14<00:47,  6.82it/s]
    Finding optimum switch:  22%|██▏       | 90/411 [00:14<00:47,  6.77it/s]
    Finding optimum switch:  22%|██▏       | 91/411 [00:14<00:47,  6.76it/s]
    Finding optimum switch:  22%|██▏       | 92/411 [00:14<00:47,  6.75it/s]
    Finding optimum switch:  23%|██▎       | 93/411 [00:15<00:46,  6.81it/s]
    Finding optimum switch:  23%|██▎       | 94/411 [00:15<00:46,  6.75it/s]
    Finding optimum switch:  23%|██▎       | 95/411 [00:15<00:46,  6.81it/s]
    Finding optimum switch:  23%|██▎       | 95/411 [00:15<00:51,  6.18it/s]

---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In[4], line 4
      1 #RUN FIT
      2 from ccs_fit import ccs_fit
----> 4 ccs_fit("CCS_input.json")

File ~/work/CCS/CCS/src/ccs_fit/fitting/main.py:411, in twp_fit(filename)
    399 nn = Objective(
    400     atom_pairs,
    401     atom_onebodies,
   (...)
    407     force_ewald=ewald_forces,
    408 )
    410 # Solve QP problem
--> 411 predicted_energies, mse, xx_unfolded = nn.solution()

File ~/work/CCS/CCS/src/ccs_fit/fitting/objective.py:169, in Objective.solution(self)
    167     hh = np.zeros(gg.shape[0])
    168     bb = np.zeros(aa.shape[0])
--> 169     sol = self.solver(pp, qq, matrix(gg), matrix(hh),
    170                       matrix(aa), matrix(bb))
    171     obj.append(float(self.eval_obj(sol["x"])))
    173 obj = np.asarray(obj)

File ~/work/CCS/CCS/src/ccs_fit/fitting/objective.py:300, in Objective.solver(pp, qq, gg, hh, aa, bb, maxiter, tol)
    298     sol = solvers.qp(pp, qq, gg, hh, aa, bb)
    299 else:
--> 300     sol = solvers.qp(pp, qq, gg, hh)
    302 return sol

File ~/.cache/pypoetry/virtualenvs/ccs-fit-oj5MS1KF-py3.9/lib/python3.9/site-packages/cvxopt/coneprog.py:4485, in qp(P, q, G, h, A, b, solver, kktsolver, initvals, **kwargs)
   4475         pinfres, dinfres = None, None
   4477     return {'status': status, 'x': x, 's': s, 'y': y, 'z': z,
   4478         'primal objective': pcost, 'dual objective': dcost,
   4479         'gap': gap, 'relative gap': relgap,
   (...)
   4482         'residual as primal infeasibility certificate': pinfres,
   4483         'residual as dual infeasibility certificate': dinfres}
-> 4485 return coneqp(P, q, G, h, None, A,  b, initvals, kktsolver = kktsolver, options = options)

File ~/.cache/pypoetry/virtualenvs/ccs-fit-oj5MS1KF-py3.9/lib/python3.9/site-packages/cvxopt/coneprog.py:2174, in coneqp(P, q, G, h, dims, A, b, initvals, kktsolver, xnewcopy, xdot, xaxpy, xscal, ynewcopy, ydot, yaxpy, yscal, **kwargs)
   2172 f0 = 0.5 * (xdot(x, rx) + xdot(x, q))
   2173 fA(y, rx, beta = 1.0, trans = 'T')
-> 2174 fG(z, rx, beta = 1.0, trans = 'T')
   2175 resx = math.sqrt(xdot(rx, rx))
   2177 # ry = A*x - b

File ~/.cache/pypoetry/virtualenvs/ccs-fit-oj5MS1KF-py3.9/lib/python3.9/site-packages/cvxopt/coneprog.py:1897, in coneqp.<locals>.fG(x, y, trans, alpha, beta)
   1896 def fG(x, y, trans = 'N', alpha = 1.0, beta = 0.0):
-> 1897     misc.sgemv(G, x, y, dims, trans = trans, alpha = alpha,
   1898         beta = beta)

File ~/.cache/pypoetry/virtualenvs/ccs-fit-oj5MS1KF-py3.9/lib/python3.9/site-packages/cvxopt/misc.py:830, in sgemv(A, x, y, dims, trans, alpha, beta, n, offsetA, offsetx, offsety)
    828 if n is None: n = A.size[1]
    829 if trans == 'T' and alpha:  trisc(x, dims, offsetx)
--> 830 base.gemv(A, x, y, trans = trans, alpha = alpha, beta = beta, m = m,
    831     n = n, offsetA = offsetA, offsetx = offsetx, offsety = offsety)
    832 if trans == 'T' and alpha: triusc(x, dims, offsetx)

KeyboardInterrupt: 

Validate your potential

Make sure your potential (at least) reproduce the data points in your training-set. Performing further tests on strucutres not included in the training set is recomended but not included in the tutorial.

from ccs_fit.scripts.ccs_validate import ccs_validate
ccs_validate(mode="CCS",CCS_params="CCS_params.json",DFT_DB="LJ.db")
  0%|          | 0/40 [00:00<?, ?it/s]/home/thism292/anaconda3/envs/CCS_fit/lib/python3.9/site-packages/ase/utils/__init__.py:62: FutureWarning: Please use atoms.cell.rank instead
  warnings.warn(warning)
100%|██████████| 40/40 [00:01<00:00, 29.38it/s]
with open("CCS_params.json", "r") as f:
    CCS_params = json.load(f)

with open("structures.json", "r") as f:
    training_set = json.load(f)

r=np.array(CCS_params["Two_body"]["Ne-Ne"]["r"])
e=CCS_params["Two_body"]["Ne-Ne"]["spl_a"]
e_LJ= 4 * ((1 / r) ** 12 - (1 / r) ** 6)
plt.xlim(0.5,3)
plt.ylim(-1.5,1)
plt.xlabel('Distance (Å)')
plt.ylabel('Potential (eV)')
plt.plot(r,e_LJ,color='black',label="Ref. Lennard-Jones potential")
plt.plot(r,e,'--',color='red',label="Fitted potential")
plt.legend()
plt.show()

err=np.loadtxt("CCS_validate.dat")
plt.xlabel('Reference energy (eV)')
plt.ylabel('Validation energy (eV)')
plt.plot( [min(err[:,0]),max(err[:,0])],[min(err[:,0]),max(err[:,0])],'--',color='black'  )
plt.scatter(err[:,0],err[:,1],facecolors='none', edgecolors='red')
plt.show()
plt.xlabel('Reference energy (eV)')
plt.ylabel('Error in fit (eV)')
plt.scatter(err[:,0],err[:,2],facecolors='none', edgecolors='red')
plt.show()

try:
    err_F=np.loadtxt("CCS_error_forces.out")
    plt.xlabel('Reference force (eV/Å)')
    plt.ylabel('Fitted force (eV/Å)')
    plt.plot( [min(err_F[:,0]),max(err_F[:,0])],[min(err_F[:,0]),max(err_F[:,0])],'--',color='black')
    plt.scatter(err_F[:,0],err_F[:,1],facecolors='none', edgecolors='red',alpha=0.1 )
    plt.show()
except:
    pass

d=[]
for t in training_set["energies"]:
    d.extend(training_set["energies"][t]["Ne-Ne"])
    
plt.hist(d)
plt.show()
_images/971430295eb7eb283c429fb99eace35825ccc993413d1ffd3017fef659a19f13.png _images/98a10e14ede4412fa478de0e30ee956128a10f06f4f5c808967da50e8c709e9f.png _images/ef59bba4ac83a2dba0e3b188ad5f179910109472b29149286b7b97fd13090a57.png _images/23603dfd03fa702da9eed603ebfe10902bbffdcd8d8b163ffe18f832909cf98b.png _images/808c6ce0f0b739d0593e810508f145d59a7ddedaf9465b89adbea2bd0161f208.png
from ccs_fit.scripts.ccs_export_FF import write_FF

write_FF("CCS_params.json")

Cleaning up

import glob
import os

def rm(file):
    try:
        os.remove(file)
    except OSError:
        pass


list=glob.glob("CALCULATED_DATA/*")
for file in list:
    rm(file)
list=glob.glob("CCS_*")
for file in list:
    rm(file)
rm("structures.json")
rm("file_list")
rm("ccs.spl")