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.algorithms.commands;
39  
40  import ffx.algorithms.cli.AlgorithmsCommand;
41  import ffx.numerics.Potential;
42  import ffx.potential.MolecularAssembly;
43  import ffx.potential.cli.AlchemicalOptions;
44  import ffx.potential.cli.TopologyOptions;
45  import ffx.potential.parsers.SystemFilter;
46  import ffx.potential.parsers.XYZFilter;
47  import ffx.utilities.FFXBinding;
48  import org.apache.commons.configuration2.CompositeConfiguration;
49  import org.apache.commons.io.FilenameUtils;
50  import picocli.CommandLine.Command;
51  import picocli.CommandLine.Mixin;
52  import picocli.CommandLine.Option;
53  import picocli.CommandLine.Parameters;
54  
55  import java.io.File;
56  import java.util.ArrayList;
57  import java.util.Collections;
58  import java.util.List;
59  
60  import static java.lang.String.format;
61  
62  /**
63   * The SortArc script sort Monte Carlo archive files by lambda value. It presently assumes
64   * that the number of files composing the first end of the window equals the number of files
65   * composing the other end.
66   * <br>
67   * Usage:
68   * <br>
69   * ffxc SortArc [options] &lt;structures1&gt; &lt;structures2&gt;
70   */
71  
72  @Command(description = " Unwind .ARC files for nWindows", name = "SortArc")
73  public class SortArc extends AlgorithmsCommand {
74  
75    @Mixin
76    private AlchemicalOptions alchemicalOptions;
77  
78    @Mixin
79    private TopologyOptions topologyOptions;
80  
81    @Option(names = {"--nw", "--nWindows"}, paramLabel = "-1",
82        description = "If set, auto-determine lambda values and subdirectories (overrides other flags).")
83    private int nWindows = -1;
84  
85    @Option(names = {"--bT", "--sortByTemp"}, paramLabel = "false",
86        description = "If set, sort archive files by temperature values")
87    private boolean sortTemp = false;
88  
89    @Option(names = {"--sT", "--startTemp"}, paramLabel = "298.15",
90        defaultValue = "298.15",
91        description = "Sets the starting temperature for the exponential temperature ladder if sorting by temperature.")
92    private double lowTemperature = 298.15;
93  
94    @Option(names = {"--ex", "--exponent"}, paramLabel = "0.5",
95        defaultValue = "0.5",
96        description = "Sets the exponent for the exponential temperature ladder if sorting by temperature.")
97    private double exponent = 0.05;
98  
99    /**
100    * The final argument(s) should be filenames for lambda windows in order..
101    */
102   @Parameters(arity = "1..*", paramLabel = "files",
103       description = "Trajectory files for the first end of the window, followed by trajectories for the other end")
104   List<String> filenames = null;
105 
106   private double[] lambdaValues;
107   private double[] temperatureValues;
108   private SystemFilter[] openers;
109   private SystemFilter[][] writers;
110   private String[] files;
111   private CompositeConfiguration additionalProperties;
112   private List<String> windowFiles = new ArrayList<>();
113   MolecularAssembly[] topologies;
114   MolecularAssembly ma;
115 
116   /**
117    * Sets an optional Configuration with additional properties.
118    *
119    * @param additionalProps
120    */
121   public void setProperties(CompositeConfiguration additionalProps) {
122     this.additionalProperties = additionalProps;
123   }
124 
125   /**
126    * SortArc Constructor.
127    */
128   public SortArc() {
129     super();
130   }
131 
132   /**
133    * SortArc Constructor.
134    *
135    * @param binding The Binding to use.
136    */
137   public SortArc(FFXBinding binding) {
138     super(binding);
139   }
140 
141   /**
142    * SortArc constructor that sets the command line arguments.
143    *
144    * @param args Command line arguments.
145    */
146   public SortArc(String[] args) {
147     super(args);
148   }
149 
150   @Override
151   public SortArc run() {
152     if (!init()) {
153       return this;
154     }
155 
156     // Determine the number of topologies to be read and allocate the array.
157     int numTopologies = topologyOptions.getNumberOfTopologies(filenames);
158     int threadsPerTopology = topologyOptions.getThreadsPerTopology(numTopologies);
159     topologies = new MolecularAssembly[numTopologies];
160 
161     // Turn on computation of lambda derivatives if softcore atoms exist.
162     alchemicalOptions.setAlchemicalProperties();
163     topologyOptions.setAlchemicalProperties(numTopologies);
164 
165     files = new String[numTopologies];
166     for (int i = 0; i < numTopologies; i++) {
167       files[i] = filenames.get(i);
168     }
169 
170     if (nWindows != -1) {
171       for (int i = 0; i < nWindows; i++) {
172         for (int j = 0; j < numTopologies; j++) {
173           String fullPathToFile = FilenameUtils.getFullPath(files[j]);
174           String directoryFullPath = fullPathToFile.replace(files[j], "") + i;
175           windowFiles.add(directoryFullPath + File.separator + i);
176         }
177 
178       }
179 
180       lambdaValues = new double[nWindows];
181       temperatureValues = new double[nWindows];
182       for (int i = 0; i < nWindows; i++) {
183         if (sortTemp) {
184           temperatureValues[i] = lowTemperature * Math.exp(exponent * i);
185         } else {
186           lambdaValues[i] = alchemicalOptions.getInitialLambda(nWindows, i, false);
187         }
188       }
189     }
190 
191     if (filenames == null) {
192       return this;
193     }
194 
195     String[][] archiveFullPaths = new String[nWindows][numTopologies];
196     File file = new File(files[0]);
197     String directoryPath = file.getAbsoluteFile().getParent() + File.separator;
198     String[][] archiveNewPath = new String[nWindows][numTopologies];
199     File[][] saveFile = new File[nWindows][numTopologies];
200     File[][] arcFiles = new File[nWindows][numTopologies];
201 
202     for (int j = 0; j < numTopologies; j++) {
203       String archiveName = FilenameUtils.getBaseName(files[j]) + ".arc";
204       for (int i = 0; i < nWindows; i++) {
205         archiveFullPaths[i][j] = directoryPath + i + File.separator + archiveName;
206         File arcFile = new File(archiveFullPaths[i][j]);
207         arcFiles[i][j] = arcFile;
208         archiveNewPath[i][j] = directoryPath + i + File.separator + FilenameUtils.getBaseName(files[j]) + "_E" + i + ".arc";
209         saveFile[i][j] = new File(archiveNewPath[i][j]);
210       }
211     }
212 
213     openers = new XYZFilter[numTopologies];
214     writers = new XYZFilter[nWindows][numTopologies];
215 
216     for (int j = 0; j < numTopologies; j++) {
217       if (filenames.get(j).contains(".pdb")) {
218         ma = alchemicalOptions.openFile(algorithmFunctions, topologyOptions,
219             threadsPerTopology, archiveFullPaths[0][j], j);
220       } else {
221         ma = alchemicalOptions.openFile(algorithmFunctions, topologyOptions,
222             threadsPerTopology, filenames.get(j), j);
223       }
224       topologies[j] = ma;
225       openers[j] = algorithmFunctions.getFilter();
226 
227       for (int i = 0; i < nWindows; i++) {
228         File arc = saveFile[i][j];
229         writers[i][j] = new XYZFilter(arc, topologies[j], topologies[j].getForceField(), additionalProperties);
230       }
231     }
232 
233     double tolerance;
234     if (sortTemp) {
235       tolerance = 1.0e-2;
236     } else {
237       tolerance = 1.0e-4;
238     }
239 
240     for (int j = 0; j < numTopologies; j++) {
241       for (int i = 0; i < nWindows; i++) {
242         logger.info(format(" Initializing %d topologies for each end", numTopologies));
243         openers[j].setFile(arcFiles[i][j]);
244         topologies[j].setFile(arcFiles[i][j]);
245         logger.info("Set file to:" + arcFiles[i][j].toString());
246 
247         int snapshots = openers[j].countNumModels();
248         logger.info(String.valueOf(snapshots));
249 
250         for (int n = 0; n < snapshots; n++) {
251           boolean resetPosition = (n == 0);
252           openers[j].readNext(resetPosition, false);
253           String remarkLine = openers[j].getRemarkLines()[0];
254 
255           double lambda = 0;
256           double temp = 0;
257           if (remarkLine.contains(" Lambda: ")) {
258             String[] tokens = remarkLine.split(" +");
259             for (int p = 0; p < tokens.length; p++) {
260               if (tokens[p].startsWith("Lambda")) {
261                 lambda = Double.parseDouble(tokens[p + 1]);
262               }
263               if (tokens[p].startsWith("Temp")) {
264                 temp = Double.parseDouble(tokens[p + 1]);
265               }
266             }
267 
268           }
269 
270           double diff;
271           for (int k = 0; k < nWindows; k++) {
272             if (sortTemp) {
273               diff = Math.abs(temperatureValues[k] - temp);
274             } else {
275               diff = Math.abs(lambdaValues[k] - lambda);
276             }
277 
278             if (diff < tolerance) {
279               writers[k][j].writeFile(saveFile[k][j], true, new String[]{remarkLine});
280               //set topology back to archive being read in
281               topologies[j].setFile(arcFiles[i][j]);
282               break;
283             }
284           }
285         }
286       }
287     }
288     return this;
289   }
290 
291   @Override
292   public List<Potential> getPotentials() {
293     return Collections.emptyList();
294   }
295 }