1 /*-------------------------------------------------------------------
2 Copyright 2011 Ravishankar Sundararaman
3 
4 This file is part of JDFTx.
5 
6 JDFTx is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 JDFTx is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with JDFTx.  If not, see <http://www.gnu.org/licenses/>.
18 -------------------------------------------------------------------*/
19 
20 #include <commands/command.h>
21 #include <electronic/Everything.h>
22 #include <core/Units.h>
23 #include <config.h>
24 
25 struct CommandIonSpecies : public Command
26 {
CommandIonSpeciesCommandIonSpecies27 	CommandIonSpecies() : Command("ion-species", "jdftx/Ionic/Species")
28 	{
29 		format = "[<path>/]<id>[<suffix>].<format>\n"
30 			"\t  | [<path>/]$ID[<suffix>].<format>";
31 		comments =
32 			"Read pseudopotential from file [<path>/]<id>.<format>, which will be referred\n"
33 			"to internally by <id> in all other commands and in the output. Note that <id>\n"
34 			"is the start of the basename of the file, obtained by removing the path,\n"
35 			"extension and any suffix starting with non-aphanumeric characters eg.\n"
36 			"Br.fhi, ../Br.fhi and /home/foo/Br_theNotSoBadOne.uspp will all have <id> = Br.\n"
37 			"\n"
38 			"If the filename contains the string $ID, then this command specifies an\n"
39 			"entire set of pseudopotentials. Every time command ion encounters an otherwise\n"
40 			"undefined species, it will search for a pseudopotential file with this pattern\n"
41 			"(replacing $ID with the as yet undefined <id> needed by the ion command).\n"
42 			"If there are multiple such patterns, then they will be searched in the order\n"
43 			"that they appear in the input file.\n"
44 			"\n"
45 			"Currently supported <format>'s are:\n"
46 			"+ .fhi   ABINIT format FHI98 norm-conserving pseudopotentials (eg. generated by OPIUM).\n"
47 			"+ .uspp  Ultrasoft pseudopotentials generated by the USPP program (native binary format).\n"
48 			"+ .upf   Quantum Espresso Universal Pseudopotential Format (only the XML-like version 2).\n"
49 			"\n"
50 			"If [<path>/]<id>.pulay exists, pulay data (derivative of total energy with respect to\n"
51 			"number of planewaves per unit volume) will be read from that file. This is useful for\n"
52 			"lattice minimization at low cutoffs; see script calcPulay for generating such files.";
53 		allowMultiple = true;
54 	}
55 
processCommandIonSpecies56 	void process(ParamList& pl, Everything& e)
57 	{	string filename; pl.get(filename, string(), "filename", true);
58 		if(filename.find("$ID")!=string::npos)
59 			e.iInfo.pspFilenamePatterns.push_back(filename); //set of psps (wildcard)
60 		else
61 			addSpecies(filename, e); //specific psp
62 	}
63 
addSpeciesCommandIonSpecies64 	static void addSpecies(string filename, Everything& e, bool fromWildcard=false)
65 	{	std::shared_ptr<SpeciesInfo> specie(new SpeciesInfo);
66 		specie->potfilename = filename;
67 		specie->fromWildcard = fromWildcard;
68 
69 		//Split filename into segments:
70 
71 		//--- check extension and get format:
72 		size_t lastDot = specie->potfilename.find_last_of(".");
73 		if(lastDot==string::npos)
74 			throw string("filename '" + specie->potfilename +"' does not have an extension from which format can be determined");
75 		string extension = specie->potfilename.substr(lastDot+1);
76 		if(extension=="fhi") specie->pspFormat = SpeciesInfo::Fhi;
77 		else if(extension=="uspp") specie->pspFormat = SpeciesInfo::Uspp;
78 		else if(extension=="upf") specie->pspFormat = SpeciesInfo::UPF;
79 		else throw string("unknown pseudopotential format extension '" + extension + "'");
80 
81 		//--- remove leading path:
82 		specie->name = specie->potfilename.substr(0, lastDot); //Remove extension
83 		specie->pulayfilename = specie->name + ".pulay"; //Tentative pulay filename (note includes path)
84 		size_t lastSlash = specie->name.find_last_of("\\/");
85 		if(lastSlash != string::npos)
86 			specie->name = specie->name.substr(lastSlash+1);
87 
88 		//--- remove suffix (if any)
89 		for(unsigned i=0; i<specie->name.length(); i++)
90 			if(!isalnum(specie->name[i]))
91 			{	specie->name = specie->name.substr(0,i);
92 				break;
93 			}
94 
95 		//--- fix capitalization (for aesthetic purposes in output ionpos etc. (internally case-insensitive))
96 		specie->name[0] = toupper(specie->name[0]);
97 
98 		//Check for a pulay file:
99 		if(!isReadable(specie->pulayfilename))
100 			specie->pulayfilename = "none"; //disable if such a file does not exist
101 
102 		//Check for duplicates, add to the list:
103 		for(auto sp: e.iInfo.species)
104 			if(specie->name==sp->name)
105 				throw string("Ion species "+specie->name+" has been defined more than once");
106 		e.iInfo.species.push_back(specie);
107 	}
108 
printStatusCommandIonSpecies109 	void printStatus(Everything& e, int iRep)
110 	{	int iPrint=0;
111 		for(auto sp: e.iInfo.species)
112 			if(not sp->fromWildcard)
113 			{	if(iPrint==iRep) { logPrintf("%s", sp->potfilename.c_str()); return; }
114 				iPrint++;
115 			}
116 		for(string pattern: e.iInfo.pspFilenamePatterns)
117 		{	if(iPrint==iRep) { logPrintf("%s", pattern.c_str()); return; }
118 			iPrint++;
119 		}
120 	}
121 }
122 commandIonSpecies;
123 
getPseudopotentialPrefixes()124 const std::vector<string>& getPseudopotentialPrefixes()
125 {	static std::vector<string> prefixes;
126 	if(!prefixes.size())
127 	{	prefixes.push_back(""); //search paths relative to current directory first
128 		prefixes.push_back(JDFTX_BUILD_DIR "/pseudopotentials/"); //search paths relative to psp library in build next
129 		if(strlen(JDFTX_INSTALL_DIR)) //search paths relative to psp library in install target, if any, last
130 			prefixes.push_back(JDFTX_INSTALL_DIR "/share/jdftx/pseudopotentials/");
131 	}
132 	return prefixes;
133 }
134 
135 //Return all uppercase/lowercase variations of s:
getCaseVariations(string s)136 std::vector<string> getCaseVariations(string s)
137 {	std::vector<string> result;
138 	//Estimate length:
139 	int nAlpha = 0; for(char c: s) if(isalpha(c)) nAlpha++;
140 	size_t nResults = ((size_t)1) << nAlpha; //2^nAlpha
141 	result.reserve(1<<nAlpha);
142 	//Loop over all case variations:
143 	for(char& c: s) c = tolower(c); //make all lower case
144 	while(result.size() < nResults)
145 	{	result.push_back(s);
146 		//Generate next case combination:
147 		for(char& c: s)
148 			if(isalpha(c))
149 			{	if(islower(c)) { c = toupper(c); break; } //on l->u, don't toggle next char's case
150 				else { c = tolower(c); } // on u->l, toggle next char's case
151 			}
152 	}
153 	return result;
154 }
155 
findSpecies(string id,Everything & e)156 std::shared_ptr<SpeciesInfo> findSpecies(string id, Everything& e)
157 {
158 	//Search existing species first:
159 	for(auto sp: e.iInfo.species)
160 		if(sp->name == id)
161 			return sp;
162 
163 	//Search wildcards in order:
164 	const std::vector<string>& prefixes = getPseudopotentialPrefixes();
165 	std::vector<string> idVariants = getCaseVariations(id);
166 	for(const string& pspFilenamePattern: e.iInfo.pspFilenamePatterns)
167 		for(const string& prefix: prefixes)
168 		{	string pattern = prefix + pspFilenamePattern;
169 			size_t idPos = pattern.find("$ID"); assert(idPos != string::npos);
170 			string patternLeft = pattern.substr(0, idPos);
171 			string patternRight = pattern.substr(idPos+3);
172 			for(const string& idVariant: idVariants)
173 			{	string fname = patternLeft + idVariant + patternRight;
174 				if(fileSize(fname.c_str()) > 0) //file exists and non-empty
175 				{	CommandIonSpecies::addSpecies(fname, e, true);
176 					return e.iInfo.species.back();
177 				}
178 			}
179 		}
180 
181 	return 0; //not found
182 }
183 
184 
185 struct CommandChargeball : public Command
186 {
CommandChargeballCommandChargeball187 	CommandChargeball() : Command("chargeball", "jdftx/Ionic/Species")
188 	{
189 		format = "<species-id> <norm> <width>";
190 		comments =
191 			"Gaussian chargeball of norm <norm> and width <width> for species <id>\n"
192 			"This feature is deprecated; when possible, use a pseudopotential with\n"
193 			"partial core correction instead.";
194 		allowMultiple = true;
195 
196 		require("ion-species");
197 	}
198 
processCommandChargeball199 	void process(ParamList& pl, Everything& e)
200 	{	//Find species:
201 		string id; pl.get(id, string(), "species-id", true);
202 		auto sp = findSpecies(id, e);
203 		if(!sp) throw string("Species "+id+" has not been defined");
204 		if(sp->Z_chargeball) throw string("chargeball defined multiple times for species "+id);
205 		//Read parameters:
206 		pl.get(sp->Z_chargeball, 0., "norm", true);
207 		pl.get(sp->width_chargeball, 0., "width", true);
208 	}
209 
printStatusCommandChargeball210 	void printStatus(Everything& e, int iRep)
211 	{	int iCB = 0;
212 		for(auto sp: e.iInfo.species)
213 			if(sp->Z_chargeball)
214 			{	if(iCB == iRep)
215 				{	logPrintf("%s %lg %lg", sp->name.c_str(), sp->Z_chargeball, sp->width_chargeball);
216 					break;
217 				}
218 				iCB++;
219 			}
220 	}
221 }
222 commandChargeball;
223 
224 
225 struct CommandTauCore : public Command
226 {
CommandTauCoreCommandTauCore227 	CommandTauCore() : Command("tau-core", "jdftx/Ionic/Species")
228 	{
229 		format = "<species-id> [<rCut>=0] [<plot>=yes|no]";
230 		comments =
231 			"Control generation of kinetic energy core correction for species <id>.\n"
232 			"The core KE density is set to the Thomas-Fermi + von-Weisacker functional\n"
233 			"of the core electron density (if any), and is pseudized inside within <rCut>.\n"
234 			"\n"
235 			"If <rCut>=0, it is chosen to be 1.5 times the location of\n"
236 			"the first radial maximum in the TF+VW KE density.\n"
237 			"\n"
238 			"Optionally, if <plot>=yes, the resulting core KE density\n"
239 			"(and electron density) are output to a gnuplot-friendly file.";
240 		allowMultiple = true;
241 
242 		require("ion-species");
243 	}
244 
processCommandTauCore245 	void process(ParamList& pl, Everything& e)
246 	{	//Find species:
247 		string id; pl.get(id, string(), "species-id", true);
248 		auto sp = findSpecies(id, e);
249 		if(!sp) throw string("Species "+id+" has not been defined");
250 		//Read parameters:
251 		pl.get(sp->tauCore_rCut, 0., "rCut", true);
252 		pl.get(sp->tauCorePlot, false, boolMap, "plot");
253 	}
254 
printStatusCommandTauCore255 	void printStatus(Everything& e, int iRep)
256 	{	if(unsigned(iRep) < e.iInfo.species.size())
257 		{	const SpeciesInfo& sp = *(e.iInfo.species[iRep]);
258 			logPrintf("%s %lg %s", sp.name.c_str(), sp.tauCore_rCut, boolMap.getString(sp.tauCorePlot));
259 		}
260 	}
261 }
262 commandTauCore;
263 
264 
265 struct CommandSetVDW : public Command
266 {
CommandSetVDWCommandSetVDW267 	CommandSetVDW() : Command("setVDW", "jdftx/Ionic/Species")
268 	{	format = "<species> <C6> <R0> [ <species2> ... ]";
269 		comments =
270 			"Manually adjust DFT-D2 vdW parameters from the default (atomic number based) values.\n"
271 			"Specify C6 in J-nm^6/mol and R0 in Angstrom.";
272 
273 		require("ion");
274 	}
275 
processCommandSetVDW276 	void process(ParamList& pl, Everything& e)
277 	{	string id;
278 		pl.get(id, string(), "species", true);
279 		while(id.length())
280 		{	auto sp = findSpecies(id, e);
281 			if(sp)
282 			{	double C6; double R0;
283 				pl.get(C6, 0.0, "C6", true);
284 				pl.get(R0, 0.0, "R0", true);
285 				sp->vdwOverride = std::make_shared<VanDerWaals::AtomParams>(C6,R0); //note constructor does conversions to a.u
286 			}
287 			else throw string("Species "+id+" has not been defined");
288 			//Check for additional species:
289 			pl.get(id, string(), "species");
290 		}
291 	}
292 
printStatusCommandSetVDW293 	void printStatus(Everything& e, int iRep)
294 	{	bool first = true;
295 		for(auto sp: e.iInfo.species)
296 			if(sp->vdwOverride)
297 			{	if(!first) logPrintf(" \\\n"); first = false;
298 				logPrintf("\t%s %lg %lg", sp->name.c_str(),
299 					sp->vdwOverride->C6 / (Joule*pow(1e-9*meter,6)/mol),
300 					sp->vdwOverride->R0 / Angstrom);
301 			}
302 	}
303 }
304 commandSetVDW;
305 
306 
307 struct CommandSetAtomicRadius : public Command
308 {
CommandSetAtomicRadiusCommandSetAtomicRadius309 	CommandSetAtomicRadius() : Command("set-atomic-radius", "jdftx/Ionic/Species")
310 	{	format = "<species> <Ri> [ <species2> ... ]";
311 		comments =
312 			"Override atomic radii used by some solvation models. Ri should be specified in Angstroms.";
313 
314 		require("ion");
315 	}
316 
processCommandSetAtomicRadius317 	void process(ParamList& pl, Everything& e)
318 	{	string id;
319 		pl.get(id, string(), "species", true);
320 		while(id.length())
321 		{	auto sp = findSpecies(id, e);
322 			if(sp)
323 			{	double Ri;
324 				pl.get(Ri, 0.0, "Ri", true);
325 				sp->atomicRadiusOverride = std::make_shared<double>(Ri*Angstrom);
326 			}
327 			else throw string("Species "+id+" has not been defined");
328 			//Check for additional species:
329 			pl.get(id, string(), "species");
330 		}
331 	}
332 
printStatusCommandSetAtomicRadius333 	void printStatus(Everything& e, int iRep)
334 	{	bool first = true;
335 		for(auto sp: e.iInfo.species)
336 			if(sp->atomicRadiusOverride)
337 			{	if(!first) logPrintf(" \\\n"); first = false;
338 				logPrintf("\t%s %lg", sp->name.c_str(), *(sp->atomicRadiusOverride)/Angstrom);
339 			}
340 	}
341 }
342 commandSetAtomicRadius;
343 
344 
345 struct CommandAddU : public Command
346 {
CommandAddUCommandAddU347 	CommandAddU() : Command("add-U", "jdftx/Electronic/Functional")
348 	{	format = "<species> <orbDesc> <UminusJ> [Vext <atom> <V>] ... [ <species2> ... ]";
349 		comments =
350 			"Add U correction (DFT+U) to specified species and orbitals, in the simplified\n"
351 			"rotationally-invariant scheme of [Dudarev et al, Phys. Rev. B 57, 1505], where\n"
352 			"the correction depends only on U - J.\n"
353 			"+ <species> is a species identifier (see command ion-species)\n"
354 			"+ <orbDesc> is one of s,p,d or f.\n"
355 			"+ <UminusJ> = U-J is the on-site correction energy in hartrees.\n"
356 			"+ Vext <atom> <V>: optionally specify an external potential on the atomic projection\n"
357 			"   which may be used to calculate U from linear response. <atom> is the atom\n"
358 			"   number of this species (1-based) to perturb by strength <V> (in Eh). Multiple\n"
359 			"   Vext's may appear per U channel to perturb multiple atoms simultaneously.\n"
360 			"\n"
361 			"Repeat the sequence for corrections to multiple species.\n"
362 			"If pseudoatom has multiple shells of same angular momentum, prefix <orbDesc>\n"
363 			"with a number e.g. 1p or 2p to select the first or second p shell respectively.";
364 
365 		require("ion");
366 	}
367 
processCommandAddU368 	void process(ParamList& pl, Everything& e)
369 	{	e.eInfo.hasU = false;
370 		string id;
371 		pl.get(id, string(), "species", true);
372 		SpeciesInfo::PlusU* plusUprev = 0;
373 		while(id.length())
374 		{	auto sp = findSpecies(id, e);
375 			if(sp)
376 			{	SpeciesInfo::PlusU plusU;
377 				//Get the orbital description:
378 				string orbCode; pl.get(orbCode, string(), "orbDesc", true);
379 				size_t lPos = string("spdf").find_first_of(orbCode.back());
380 				if(lPos==string::npos) throw  "'" + orbCode + "' is not a valid orbital code.";
381 				plusU.l = int(lPos);
382 				plusU.n = 0;
383 				if(orbCode.length() > 1)
384 				{	plusU.n = atoi(orbCode.substr(0, orbCode.length()-1).c_str()) - 1;
385 					if(plusU.n<0) throw string("Principal quantum number in orbital description must be a positive integer");
386 				}
387 				//Get the value:
388 				pl.get(plusU.UminusJ, 0., "UminusJ", true);
389 				//Add U descriptor to species:
390 				sp->plusU.push_back(plusU);
391 				e.eInfo.hasU = true;
392 				//Prepare for a possible Vext:
393 				plusUprev = &sp->plusU.back();
394 				plusUprev->Vext.assign(sp->atpos.size(), 0.);
395 			}
396 			else if(id=="Vext" && plusUprev)
397 			{	size_t atom; double V;
398 				pl.get(atom, size_t(0), "atom", true);
399 				pl.get(V, 0., "V", true);
400 				if(atom<1 || atom>plusUprev->Vext.size())
401 					throw string("Atom number for Vext must be >= 1 and <= nAtoms");
402 				plusUprev->Vext[atom-1] = V;
403 			}
404 			else throw string("Species "+id+" has not been defined");
405 			//Check for additional species:
406 			pl.get(id, string(), "species");
407 		}
408 		Citations::add("Simplified rotationally-invariant DFT+U", "S. L. Dudarev et al., Phys. Rev. B 57, 1505 (1998)");
409 	}
410 
printStatusCommandAddU411 	void printStatus(Everything& e, int iRep)
412 	{	bool first = true;
413 		for(auto sp: e.iInfo.species)
414 			for(auto plusU: sp->plusU)
415 			{	if(!first) logPrintf(" \\\n"); first = false;
416 				ostringstream oss;
417 				if(plusU.n) oss << (plusU.n + 1);
418 				oss << string("spdf")[plusU.l];
419 				logPrintf("\t%s %s %lg", sp->name.c_str(), oss.str().c_str(), plusU.UminusJ);
420 				for(size_t atom=1; atom<=plusU.Vext.size(); atom++)
421 					if(plusU.Vext[atom-1]) logPrintf("  Vext %lu %lg", atom, plusU.Vext[atom-1]);
422 			}
423 	}
424 }
425 commandAddU;
426