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.test;
39  
40  import edu.rit.pj.ParallelTeam;
41  import ffx.algorithms.cli.AlgorithmsCommand;
42  import ffx.potential.MolecularAssembly;
43  import ffx.potential.cli.TopologyOptions;
44  import ffx.potential.extended.ExtendedSystem;
45  import ffx.potential.parsers.XPHFilter;
46  import ffx.utilities.FFXBinding;
47  import org.apache.commons.configuration2.CompositeConfiguration;
48  import org.apache.commons.io.FilenameUtils;
49  import picocli.CommandLine.Command;
50  import picocli.CommandLine.Mixin;
51  import picocli.CommandLine.Option;
52  import picocli.CommandLine.Parameters;
53  
54  import java.io.File;
55  import java.util.ArrayList;
56  import java.util.List;
57  
58  /**
59   * The SortXPH command unwinds .ARC files from CpHMD runs.
60   * <br>
61   * Usage:
62   * <br>
63   * ffxc test.SortXPH [options] &lt;structures1&gt; &lt;structures2&gt;
64   */
65  @Command(description = " The SortXPH command unwinds .ARC files from CpHMD runs.", name = "test.SortXPH")
66  public class SortXPH extends AlgorithmsCommand {
67  
68    @Mixin
69    private TopologyOptions topology;
70  
71    @Option(names = {"--nw", "--nWindows"}, paramLabel = "-1", defaultValue = "-1",
72        description = "If set, auto-determine lambda values and subdirectories (overrides other flags).")
73    private int nWindows;
74  
75    @Option(names = {"--bT", "--sortByTemp"}, paramLabel = "false", defaultValue = "false",
76        description = "If set, sort archive files by temperature values")
77    private boolean sortTemp;
78  
79    @Option(names = {"--sT", "--startTemp"}, paramLabel = "298.15", defaultValue = "298.15",
80        description = "Sets the starting temperature for the exponential temperature ladder if sorting by temperature.")
81    private double lowTemperature;
82  
83    @Option(names = {"--pH"}, paramLabel = "7.4", defaultValue = "7.4",
84        description = "Sets the middle of the pH ladder")
85    private double pH;
86  
87    @Option(names = {"--pHGaps"}, paramLabel = "1", defaultValue = "1",
88        description = "Sets the size of the gaps in the pH ladder")
89    private double pHGap;
90  
91    @Option(names = {"--ex", "--exponent"}, paramLabel = "0.5", defaultValue = "0.5",
92        description = "Sets the exponent for the exponential temperature ladder if sorting by temperature.")
93    private double exponent;
94  
95    /**
96     * The final argument(s) should be filenames for lambda windows in order.
97     */
98    @Parameters(arity = "1", paramLabel = "file",
99        description = "Trajectory files for the first end of the window, followed by trajectories for the other end")
100   private String filename = null;
101 
102   private double[] lambdaValues;
103   private double[] temperatureValues;
104   private double[] pHValues;
105   private XPHFilter opener;
106   private XPHFilter[] writers;
107   private CompositeConfiguration additionalProperties;
108   private List<String> windowFiles = new ArrayList<>();
109   private MolecularAssembly molecularAssembly;
110   private int threadsAvail = ParallelTeam.getDefaultThreadCount();
111   private int threadsPer = threadsAvail;
112   private ExtendedSystem extendedSystem;
113 
114   /**
115    * Sets an optional Configuration with additional properties.
116    *
117    * @param additionalProps additional properties
118    */
119   public void setProperties(CompositeConfiguration additionalProps) {
120     this.additionalProperties = additionalProps;
121   }
122 
123   /**
124    * SortXPH Constructor.
125    */
126   public SortXPH() {
127     super();
128   }
129 
130   /**
131    * SortXPH Constructor.
132    *
133    * @param binding The Binding to use.
134    */
135   public SortXPH(FFXBinding binding) {
136     super(binding);
137   }
138 
139   /**
140    * SortXPH constructor that sets the command line arguments.
141    *
142    * @param args Command line arguments.
143    */
144   public SortXPH(String[] args) {
145     super(args);
146   }
147 
148   /**
149    * {@inheritDoc}
150    */
151   @Override
152   public SortXPH run() {
153     logger.info(" Running");
154 
155     if (!init()) {
156       return this;
157     }
158 
159     if (nWindows != -1) {
160       for (int i = 0; i < nWindows; i++) {
161         String fullPathToFile = FilenameUtils.getFullPath(filename);
162         String directoryFullPath = fullPathToFile.replace(filename, "") + i;
163         windowFiles.add(directoryFullPath + File.separator + i);
164       }
165 
166       lambdaValues = new double[nWindows];
167       temperatureValues = new double[nWindows];
168       pHValues = new double[nWindows];
169       for (int i = 0; i < nWindows; i++) {
170         if (sortTemp) {
171           temperatureValues[i] = lowTemperature * Math.exp(exponent * i);
172         } else {
173           double range = nWindows * pHGap;
174           double pHMin = pH - range / 2;
175           if (nWindows % 2 != 0) {
176             pHMin += pHGap / 2;
177           }
178           pHValues[i] = pHMin + i * pHGap;
179         }
180       }
181     }
182 
183     String[] archiveFullPaths = new String[nWindows];
184     File file = new File(filename);
185     String directoryPath = file.getAbsoluteFile().getParent() + File.separator;
186     String[] archiveNewPath = new String[nWindows];
187     File[] saveFile = new File[nWindows];
188     File[] arcFiles = new File[nWindows];
189     String archiveName = FilenameUtils.getBaseName(filename) + ".arc";
190 
191     writers = new XPHFilter[nWindows];
192     for (int i = 0; i < nWindows; i++) {
193       archiveFullPaths[i] = directoryPath + i + File.separator + archiveName;
194       File arcFile = new File(archiveFullPaths[i]);
195       arcFiles[i] = arcFile;
196       archiveNewPath[i] = directoryPath + i + File.separator + FilenameUtils.getBaseName(filename) + "_E" + i + ".arc";
197       saveFile[i] = new File(archiveNewPath[i]);
198     }
199 
200     molecularAssembly = getActiveAssembly(filename);
201     extendedSystem = new ExtendedSystem(molecularAssembly, pH, null);
202     molecularAssembly.getPotentialEnergy().attachExtendedSystem(extendedSystem);
203 
204     int numParallel = topology.getTopologiesInParallel(1);
205     threadsPer = threadsAvail / numParallel;
206 
207     for (int i = 0; i < nWindows; i++) {
208       File arc = saveFile[i];
209       writers[i] = new XPHFilter(arc, molecularAssembly, molecularAssembly.getForceField(),
210           additionalProperties, extendedSystem);
211     }
212     opener = new XPHFilter(algorithmFunctions.getFilter(), extendedSystem);
213 
214     double tolerance;
215     if (sortTemp) {
216       tolerance = 1.0e-2;
217     } else {
218       tolerance = 1.0e-1;
219     }
220 
221     for (int i = 0; i < nWindows; i++) {
222 
223       opener.setFile(arcFiles[i]);
224       molecularAssembly.setFile(arcFiles[i]);
225       logger.info("Set file to:" + arcFiles[i].toString());
226 
227       int snapshots = opener.countNumModels();
228       logger.info(String.valueOf(snapshots));
229 
230       for (int n = 0; n < snapshots; n++) {
231         boolean resetPosition = n == 0;
232 
233         //TODO: Fix ReadNext to actually read in esv
234         opener.readNext(resetPosition, false);
235 
236         String remarkLine = opener.getRemarkLines()[0];
237 
238         double snapshotLambda = 0;
239         double snapshotTemp = 0;
240         double snapshotPH = 0;
241         if (remarkLine.contains(" Lambda: ")) {
242           String[] tokens = remarkLine.split(" +");
243           for (int p = 0; p < tokens.length; p++) {
244             if (tokens[p].startsWith("Lambda")) {
245               snapshotLambda = Double.parseDouble(tokens[p + 1]);
246             }
247             if (tokens[p].startsWith("Temp")) {
248               snapshotTemp = Double.parseDouble(tokens[p + 1]);
249             }
250             if (tokens[p].startsWith("pH")) {
251               snapshotPH = Double.parseDouble(tokens[p + 1]);
252             }
253           }
254         }
255 
256         double diff;
257         for (int k = 0; k < nWindows; k++) {
258           if (sortTemp) {
259             diff = Math.abs(temperatureValues[k] - snapshotTemp);
260           } else {
261             diff = Math.abs(pHValues[k] - snapshotPH);
262           }
263 
264           /*
265           if (diff < tolerance) {
266             double[] lambdas = opener.getExtendedSystem().getLambdaArray();
267             logger.info("************ Lambdas: " + Arrays.toString(lambdas));
268             writers[k].getExtendedSystem().setLambdaArray(lambdas);
269             logger.info(" Writing to XPH");
270             writers[k].writeFile(saveFile[k], true, new String[]{remarkLine});
271             // Set topology back to archive being read in
272             molecularAssembly.setFile(arcFiles[i]);
273             break;
274           }
275            */
276         }
277       }
278     }
279     return this;
280   }
281 }