1 // -*- C++ -*-
2 //
3 // asap_kim_api.cpp: Common interfaces classes for Asap potentials in OpenKIM.
4 //
5 // Copyright (C) 2012-2013 Jakob Schiotz and the Department of Physics,
6 // Technical University of Denmark.  Email: schiotz@fysik.dtu.dk
7 //
8 // This file is part of Asap version 3.
9 // Asap is released under the GNU Lesser Public License (LGPL) version 3.
10 // However, the parts of Asap distributed within the OpenKIM project
11 // (including this file) are also released under the Common Development
12 // and Distribution License (CDDL) version 1.0.
13 //
14 // This program is free software: you can redistribute it and/or
15 // modify it under the terms of the GNU Lesser General Public License
16 // version 3 as published by the Free Software Foundation.  Permission
17 // to use other versions of the GNU Lesser General Public License may
18 // granted by Jakob Schiotz or the head of department of the
19 // Department of Physics, Technical University of Denmark, as
20 // described in section 14 of the GNU General Public License.
21 //
22 // This program is distributed in the hope that it will be useful,
23 // but WITHOUT ANY WARRANTY; without even the implied warranty of
24 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 // GNU General Public License for more details.
26 //
27 // You should have received a copy of the GNU General Public License
28 // and the GNU Lesser Public License along with this program.  If not,
29 // see <http://www.gnu.org/licenses/>.
30 
31 
32 #include "KimAsapPython.h"
33 #include "asap_kim_api.h"
34 #include "Potential.h"
35 #include "KimNeighborLocator.h"
36 #include "KIM_ModelDriverHeaders.hpp"
37 #include "SymTensor.h"
38 #include "Debug.h"
39 #include <stdlib.h>
40 #include <string.h>
41 
AsapKimPotential(KIM::ModelDriverCreate * const modelDriverCreate,bool supportvirial)42 AsapKimPotential::AsapKimPotential(KIM::ModelDriverCreate * const modelDriverCreate,
43 				   bool supportvirial)
44 {
45   CONSTRUCTOR;
46   int error = 0;
47   int numparamfiles = 0;
48 
49   potential = NULL;
50   atoms = NULL;
51 
52   // Register the units.  Conversion happens in the specific model.
53 
54   // Store the parameter file name(s) for later use by reinit.
55   modelDriverCreate->GetNumberOfParameterFiles(&numparamfiles);
56   paramfile_names.resize(numparamfiles);
57   for (int i = 0; i < numparamfiles; ++i)
58   {
59     std::string const * paramFileName;
60     error = modelDriverCreate->GetParameterFileName(i, &paramFileName);
61     if (error)
62       throw AsapError("AsapKimPotential: Unable to get parameter file name");
63     paramfile_names[i] = *paramFileName;
64   }
65 
66   this->supportvirial = supportvirial;
67   error = modelDriverCreate->SetModelNumbering(KIM::NUMBERING::zeroBased);
68   assert(error == 0);   // Should not be able to fail.
69 
70   // Store pointers -  XXXXX FIX THESE !!
71   error = (
72     modelDriverCreate->SetRoutinePointer(
73       KIM::MODEL_ROUTINE_NAME::ComputeArgumentsCreate,
74       KIM::LANGUAGE_NAME::cpp,
75       true,
76       reinterpret_cast<KIM::Function *>(AsapKimPotential::ComputeArgumentsCreate))
77     || modelDriverCreate->SetRoutinePointer(
78       KIM::MODEL_ROUTINE_NAME::ComputeArgumentsDestroy,
79       KIM::LANGUAGE_NAME::cpp,
80       true,
81       reinterpret_cast<KIM::Function *>(AsapKimPotential::ComputeArgumentsDestroy))
82     || modelDriverCreate->SetRoutinePointer(
83       KIM::MODEL_ROUTINE_NAME::Compute,
84       KIM::LANGUAGE_NAME::cpp,
85       true,
86       reinterpret_cast<KIM::Function *>(AsapKimPotential::Compute_static))
87     || modelDriverCreate->SetRoutinePointer(
88       KIM::MODEL_ROUTINE_NAME::Destroy,
89       KIM::LANGUAGE_NAME::cpp,
90       true,
91       reinterpret_cast<KIM::Function *>(AsapKimPotential::Destroy))
92     //|| modelDriverCreate->SetRefreshPointer(
93     //  KIM::LANGUAGE_NAME::cpp,
94     //  (KIM::Function *)2)
95     );
96   assert(error == 0);  // Should not be able to fail.
97 }
98 
~AsapKimPotential()99 AsapKimPotential::~AsapKimPotential()
100 {
101   DESTRUCTOR;
102   if (potential != NULL)
103     delete potential;
104   if (atoms != NULL)
105     AsapAtoms_DECREF(atoms);
106 }
107 
SetPotential(Potential * pot)108 void AsapKimPotential::SetPotential(Potential *pot)
109 {
110   potential = pot;
111   potential_as_kimmixin = dynamic_cast<PotentialKimMixin*>(pot);
112   assert(potential_as_kimmixin != NULL);
113 }
114 
CreateNeighborList(KimAtoms * atoms,double cutoff,double drift)115 PyAsap_NeighborLocatorObject *AsapKimPotential::CreateNeighborList(KimAtoms *atoms,
116                                                                    double cutoff,
117                                                                    double drift)
118 {
119   int ier;
120   PyAsap_NeighborLocatorObject *nblist;
121   atoms->SetPBC(true, true, true);
122   nblist = PyAsap_NewKimNeighborLocator(atoms, cutoff);
123   return nblist;
124 }
125 
126 // static member function
ComputeArgumentsCreate(KIM::ModelCompute const * const modelCompute,KIM::ModelComputeArgumentsCreate * const modelComputeArgumentsCreate)127 int AsapKimPotential::ComputeArgumentsCreate(
128   KIM::ModelCompute const * const modelCompute,
129   KIM::ModelComputeArgumentsCreate * const modelComputeArgumentsCreate)
130 {
131   AsapKimPotential *modelObject;
132   modelCompute->GetModelBufferPointer(reinterpret_cast<void **>(&modelObject));
133 
134   return modelObject->potential_as_kimmixin->ComputeArgumentsCreate(
135       modelComputeArgumentsCreate);
136 }
137 
138 // static member function
ComputeArgumentsDestroy(KIM::ModelCompute const * const modelCompute,KIM::ModelComputeArgumentsDestroy * const modelComputeArgumentsDestroy)139 int AsapKimPotential::ComputeArgumentsDestroy(
140   KIM::ModelCompute const * const modelCompute,
141   KIM::ModelComputeArgumentsDestroy * const modelComputeArgumentsDestroy)
142 {
143   AsapKimPotential *modelObject;
144   modelCompute->GetModelBufferPointer(reinterpret_cast<void **>(&modelObject));
145 
146   return modelObject->potential_as_kimmixin->ComputeArgumentsDestroy(
147       modelComputeArgumentsDestroy);
148 }
149 
150 
151 // static member function
Destroy(KIM::ModelDestroy * const modelDestroy)152 int AsapKimPotential::Destroy(KIM::ModelDestroy * const modelDestroy)
153 {
154   AsapKimPotential *modelObject;
155   modelDestroy->GetModelBufferPointer(reinterpret_cast<void **>(&modelObject));
156 
157   if (modelObject != NULL)
158   {
159     // delete object itself
160     delete modelObject;
161   }
162 
163   // everything is good
164   return false;
165 }
166 
167 
168 // static member function
Compute_static(KIM::ModelCompute const * const modelCompute,KIM::ModelComputeArguments const * const modelComputeArguments)169 int AsapKimPotential::Compute_static(KIM::ModelCompute const * const modelCompute,
170 				     KIM::ModelComputeArguments const * const modelComputeArguments)
171 {
172   AsapKimPotential * modelObject;
173   modelCompute->GetModelBufferPointer(reinterpret_cast<void **>(&modelObject));
174 
175   return modelObject->Compute(modelCompute, modelComputeArguments);
176 }
177 
178 #define MYLOG(x) modelCompute->LogEntry(KIM::LOG_VERBOSITY::error, x, __LINE__, __FILE__)
179 
Compute(KIM::ModelCompute const * const modelCompute,KIM::ModelComputeArguments const * const modelComputeArguments)180 int AsapKimPotential::Compute(KIM::ModelCompute const * const modelCompute,
181 			      KIM::ModelComputeArguments const * const modelComputeArguments)
182 {
183   int error;
184   assert(potential != NULL);
185 
186   // Information from the atoms
187   int nAtoms = 0;
188   int *nAtoms_p = NULL;
189 
190   const double *coords = NULL;
191   const int *particleSpecies = NULL;
192   const int *particleContributing = NULL;
193 
194   error =
195     modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::numberOfParticles,
196 					      &nAtoms_p);
197   if (error)
198   {
199     MYLOG("Failed to get number of atoms.");
200     return error;
201   }
202   assert(nAtoms_p != NULL);
203   nAtoms = *nAtoms_p;
204   assert(nAtoms >= 0);
205 
206   error =
207     modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::coordinates,
208 					      &coords)
209     || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::particleSpeciesCodes,
210 						 &particleSpecies)
211     || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::particleContributing,
212 						 &particleContributing);
213   if (error)
214   {
215     MYLOG("Failed to get coordinates, species or contribution pointer.");
216     return error;
217   }
218   assert(coords != NULL && particleSpecies != NULL && particleContributing != NULL);
219 
220   // Quantities to be computed
221   double *energy = NULL;
222   double *forces = NULL;
223   double *particleEnergy = NULL;
224   double *virial = NULL;
225   double *particleVirial = NULL;
226 
227   error =
228     modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::partialEnergy,
229 					      &energy)
230     || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::partialParticleEnergy,
231 						 &particleEnergy)
232     || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::partialForces,
233 						 &forces);
234   if (error)
235   {
236     MYLOG("Failed to get energy or force pointer.");
237     return error;
238   }
239   if (supportvirial)
240   {
241     error =(
242       modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::partialVirial,
243 						&virial)
244       || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::partialParticleVirial,
245 						 &particleVirial)
246       );
247     if (error)
248     {
249       MYLOG("Failed to get virial pointers.");
250       return error;
251     }
252   }
253 
254   // Create or update the KIM replacement of the ASAP Atoms access object.
255   if (atoms == NULL)
256     {
257       // First call, create the Atoms interface object
258       atoms = new KimAtoms();
259       assert(atoms != NULL);
260       atoms->ReInit(modelComputeArguments, nAtoms, coords, particleSpecies, particleContributing);
261       try {
262 	potential->SetAtoms(NULL, atoms);
263       }
264       catch (AsapError &e)
265       {
266 	string msg = e.GetMessage();
267 	MYLOG(msg.c_str());
268 	return 1;
269       }
270     }
271   else
272     {
273       atoms->ReInit(modelComputeArguments, nAtoms, coords, particleSpecies, particleContributing);
274     }
275 
276   // Now do the actual computation
277   try
278   {
279       if (particleEnergy != NULL)
280         {
281           const vector<double> &energies_v = potential->GetPotentialEnergies(NULL);
282           assert(energies_v.size() == nAtoms);
283           for (int i = 0; i < nAtoms; i++)
284             particleEnergy[i] = energies_v[i];
285         }
286       if (energy != NULL)
287         *energy = potential->GetPotentialEnergy(NULL);
288       if (particleVirial != NULL)
289         {
290           const vector<SymTensor> &virials = potential->GetVirials(NULL);
291           assert(virials.size() == nAtoms);
292           const double *virials_ptr = (double *) &virials[0];
293           for (int i = 0; i < 6*(nAtoms); i++)
294             particleVirial[i] = virials_ptr[i];
295         }
296       if (virial != NULL)
297         {
298           SymTensor v = potential->GetVirial(NULL);
299           for (int i = 0; i < 6; i++)
300             virial[i] = v[i];
301         }
302       if (forces != NULL)
303         {
304           const vector<Vec> &forces_v = potential->GetForces(NULL);
305           assert(forces_v.size() == nAtoms);
306           const double *forces_v_ptr = (double *) &forces_v[0];
307           for (int i = 0; i < 3*(nAtoms); i++)
308             forces[i] = forces_v_ptr[i];
309         }
310   }
311   catch (AsapError &e)
312   {
313       string msg = e.GetMessage();
314       MYLOG(msg.c_str());
315       return 1;
316   }
317   return 0;
318 }
319