View Javadoc
1   //******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2025.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  //******************************************************************************
38  package ffx.potential.commands;
39  
40  import ffx.crystal.Crystal;
41  import ffx.crystal.LatticeSystem;
42  import ffx.potential.bonded.Atom;
43  import ffx.potential.cli.PotentialCommand;
44  import ffx.utilities.FFXBinding;
45  import picocli.CommandLine.Command;
46  import picocli.CommandLine.Option;
47  import picocli.CommandLine.Parameters;
48  
49  import java.io.BufferedWriter;
50  import java.io.File;
51  import java.io.FileWriter;
52  import java.io.IOException;
53  import java.util.HashMap;
54  import java.util.Iterator;
55  import java.util.Map;
56  
57  import static java.lang.String.format;
58  import static org.apache.commons.io.FilenameUtils.getName;
59  import static org.apache.commons.io.FilenameUtils.removeExtension;
60  import static org.apache.commons.math3.util.FastMath.cos;
61  
62  /**
63   * Generate Quantum Espresso (QE) input from an XYZ file.
64   * <p>
65   * Usage:
66   * ffxc XYZtoQE [options] &lt;filename&gt;
67   */
68  @Command(name = "XYZtoQE", description = "Generate QE input from a XYZ file.")
69  public class ExportQE extends PotentialCommand {
70  
71    /**
72     * Number of structural optimization steps performed in this run.
73     */
74    @Option(names = {"--ns", "--nstep"}, paramLabel = "500", defaultValue = "500",
75        description = "Number of structural optimization steps performed in this run.")
76    private int nStep;
77  
78    /**
79     * Convergence threshold on total energy (a.u) for ionic minimization.
80     */
81    @Option(names = {"--ec", "--etot_conv_thr"}, paramLabel = "1.0e-6", defaultValue = "1.0e-6",
82        description = "Convergence threshold on total energy (a.u) for ionic minimization.")
83    private double etotConvThr;
84  
85    /**
86     * Convergence threshold on forces (a.u) for ionic minimization.
87     */
88    @Option(names = {"--ef", "--forc_conv_thr"}, paramLabel = "1.0e-4", defaultValue = "1.0e-4",
89        description = "Convergence threshold on forces (a.u) for ionic minimization.")
90    private double forcConvThr;
91  
92    /**
93     * Kinetic energy cutoff (Ry) for wavefunctions.
94     */
95    @Option(names = {"--ke", "--ecutwfc"}, paramLabel = "50.0", defaultValue = "50.0",
96        description = "Kinetic energy cutoff (Ry) for wavefunctions.")
97    private double ecutwfc;
98  
99    /**
100    * Kinetic energy cutoff (Ry) for charge density and potential.
101    */
102   @Option(names = {"--rho", "--ecutrho"}, paramLabel = "500.0", defaultValue = "500.0",
103       description = "Kinetic energy cutoff (Ry) for charge density and potential.")
104   private double ecutrho;
105 
106   /**
107    * Maximum number of iterations in a SCF step.
108    */
109   @Option(names = {"--em", "--electron_maxstep"}, paramLabel = "1500", defaultValue = "1500",
110       description = "Maximum number of iterations in a scf step.")
111   private int electronMaxstep;
112 
113   /**
114    * Convergence threshold for self consistency.
115    */
116   @Option(names = {"--ct", "--conv_thr"}, paramLabel = "1.0e-8", defaultValue = "1.0e-8",
117       description = "Convergence threshold for self consistency.")
118   private double convThr;
119 
120   /**
121    * Mixing factor for self-consistency.
122    */
123   @Option(names = {"--mb", "--mixing_beta"}, paramLabel = "0.5", defaultValue = "0.5",
124       description = "Mixing factor for self-consistency.")
125   private double mixingBeta;
126 
127   /**
128    * Perform QE calculation on hexagonal rather than rhombohedral representation.
129    */
130   @Option(names = {"--hx", "--hexagonal"}, paramLabel = "true", defaultValue = "true",
131       description = "Perform QE on hexagonal system.")
132   private boolean hexagonal;
133 
134   /**
135    * The final argument should be one filename (XYZ).
136    */
137   @Parameters(arity = "1", paramLabel = "file",
138       description = "XYZ file to be converted.")
139   private String filename = null;
140 
141   public ExportQE() {
142     super();
143   }
144 
145   public ExportQE(FFXBinding binding) {
146     super(binding);
147   }
148 
149   public ExportQE(String[] args) {
150     super(args);
151   }
152 
153   @Override
154   public ExportQE run() {
155     // Init the context and bind variables.
156     if (!init()) {
157       return this;
158     }
159 
160     // Load the MolecularAssembly.
161     activeAssembly = getActiveAssembly(filename);
162     if (activeAssembly == null) {
163       logger.info(helpString());
164       return this;
165     }
166 
167     // Set the filename.
168     filename = activeAssembly.getFile().getAbsolutePath();
169     logger.info(format("\n Converting %s to QE format\n", filename));
170 
171     String dirString = getBaseDirString(filename);
172     String name = removeExtension(getName(filename));
173     File modelFile = new File(dirString + name + ".in");
174 
175     Crystal crystal = activeAssembly.getCrystal().getUnitCell();
176     double xtalA = crystal.a;
177     double xtalB = crystal.b;
178     double xtalC = crystal.c;
179 
180     HashMap<String, Double> atomTypes = new HashMap<>();
181     StringBuilder atomicPositions = new StringBuilder();
182 
183     Atom[] atoms = activeAssembly.getAtomArray();
184     for (Atom atom : atoms) {
185       if (!atomTypes.containsKey(atom.getName())) {
186         atomTypes.put(atom.getName(), atom.getAtomType().atomicWeight);
187       }
188       double[] xyz = atom.getXYZ(null);
189       crystal.toFractionalCoordinates(xyz, xyz);
190       atomicPositions.append(format("%2s %16.12f %16.12f %16.12f%n", atom.getName(), xyz[0], xyz[1], xyz[2]));
191     }
192 
193     try (BufferedWriter bwQE = new BufferedWriter(new FileWriter(modelFile))) {
194       // general variables controlling the run
195       bwQE.write(format("&CONTROL%n" +
196           "\tcalculation = 'vc-relax',%n" +
197           "\trestart_mode = 'from_scratch',%n" +
198           "\tprefix = '%s',%n" +
199           "\tetot_conv_thr = %6.4E,%n" +
200           "\tforc_conv_thr = %6.4E,%n" +
201           "\tnstep = %d,%n" +
202           "/%n", name, etotConvThr, forcConvThr, nStep));
203 
204       // structural information on the system under investigation
205       bwQE.write(format("&SYSTEM%n" +
206               "\tspace_group = %d,%n" +
207               "\tnat = %d,%n" +
208               "\tntyp = %d,%n" +
209               "\ta = %16.12f%n" +
210               "\tb = %16.12f%n" +
211               "\tc = %16.12f%n" +
212               "\tcosAB = %16.12f%n" +
213               "\tcosAC = %16.12f%n" +
214               "\tcosBC = %16.12f%n" +
215               "\tecutwfc = %6.4f,%n" +
216               "\tecutrho = %6.4f,%n" +
217               "\tvdw_corr = 'XDM',%n",
218           crystal.spaceGroup.number,
219           activeAssembly.getAtomList().size(),
220           atomTypes.size(),
221           xtalA, xtalB, xtalC,
222           cos(crystal.gamma), cos(crystal.beta), cos(crystal.alpha),
223           ecutwfc, ecutrho));
224       if (crystal.spaceGroup.latticeSystem == LatticeSystem.HEXAGONAL_LATTICE) {
225         bwQE.write("\trhombohedral = .FALSE.,\n");
226       }
227       bwQE.write("/\n");
228 
229       // electronic variables: self-consistency, smearing
230       bwQE.write(format("&ELECTRONS%n" +
231           "\telectron_maxstep = %d,%n" +
232           "\tconv_thr = %6.4E,%n" +
233           "\tscf_must_converge = .TRUE.,%n" +
234           "\tmixing_beta = %5.3f,%n" +
235           "/%n", electronMaxstep, convThr, mixingBeta));
236 
237       // ionic variables: relaxation, dynamics
238       bwQE.write("&IONS\n" +
239           "\tion_dynamics = 'bfgs',\n" +
240           "/\n");
241 
242       // variable-cell optimization or dynamics
243       bwQE.write("&CELL\n" +
244           "\tcell_dynamics = 'bfgs',\n" +
245           "/\n");
246 
247       // ATOMIC_SPECIES
248       StringBuilder line = new StringBuilder();
249       Iterator<Map.Entry<String, Double>> it = atomTypes.entrySet().iterator();
250       while (it.hasNext()) {
251         Map.Entry<String, Double> pair = it.next();
252         line.append(" ").append(pair.getKey()).append(" ").append(pair.getValue())
253             .append(" ").append(pair.getKey()).append(".b86bpbe.UPF\n");
254       }
255       bwQE.write("ATOMIC_SPECIES\n" + line + "\n");
256 
257       // ATOMIC_POSITIONS
258       bwQE.write("ATOMIC_POSITIONS crystal_sg\n" + atomicPositions + "\n");
259 
260       // K_POINTS automatic
261       int k1;
262       int k2;
263       int k3;
264       if (xtalA < 5) {
265         k1 = 8;
266       } else if (xtalA <= 7) {
267         k1 = 6;
268       } else if (xtalA <= 12) {
269         k1 = 4;
270       } else {
271         k1 = 2;
272       }
273       if (xtalB < 5) {
274         k2 = 8;
275       } else if (xtalB <= 7) {
276         k2 = 6;
277       } else if (xtalB <= 12) {
278         k2 = 4;
279       } else {
280         k2 = 2;
281       }
282       if (xtalC < 5) {
283         k3 = 8;
284       } else if (xtalC <= 7) {
285         k3 = 6;
286       } else if (xtalC <= 12) {
287         k3 = 4;
288       } else {
289         k3 = 2;
290       }
291       bwQE.write("K_POINTS automatic\n" + k1 + " " + k2 + " " + k3 + " 1 1 1\n");
292     } catch (IOException e) {
293       logger.severe(" Error writing QE input file: " + e.getMessage());
294     }
295 
296     logger.info(format(" Saved QE file: %s", modelFile));
297     return this;
298   }
299 }