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-2024.
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.ui;
39  
40  import static java.lang.String.format;
41  
42  import ffx.algorithms.AlgorithmFunctions;
43  import ffx.algorithms.AlgorithmListener;
44  import ffx.algorithms.AlgorithmUtils;
45  import ffx.numerics.Potential;
46  import ffx.potential.ForceFieldEnergy;
47  import ffx.potential.MolecularAssembly;
48  import ffx.potential.bonded.RotamerLibrary;
49  import ffx.potential.parameters.ForceField;
50  import ffx.potential.parsers.ForceFieldFilter;
51  import ffx.potential.parsers.PDBFilter;
52  import ffx.potential.parsers.PDBFilter.Mutation;
53  import ffx.potential.parsers.SystemFilter;
54  import ffx.utilities.Keyword;
55  import java.io.File;
56  import java.util.List;
57  import java.util.Optional;
58  import java.util.logging.Level;
59  import org.apache.commons.configuration2.CompositeConfiguration;
60  import org.apache.commons.io.FilenameUtils;
61  
62  /**
63   * UIUtils implements core and extended functionality for many Force Field X algorithms and scripts,
64   * such as opening and closing structure files, basic force field evaluations, molecular dynamics,
65   * etc. This implementation performs additional tasks for the FFX graphical user interface, such as
66   * updating the GUI and tree structure. This is widely used by our scripts. The AlgorithmUtils and
67   * PotentialsUtils implementations of their respective interfaces lack the additional functionality
68   * provided here.
69   *
70   * @author Jacob M. Litman
71   * @author Michael J. Schnieders
72   */
73  public class UIUtils extends AlgorithmUtils implements AlgorithmFunctions {
74  
75    private final ModelingShell modelingShell;
76    private final MainPanel mainPanel;
77    private SystemFilter lastFilter;
78  
79    public UIUtils(ModelingShell modelingShell, MainPanel mainPanel) {
80      this.modelingShell = modelingShell;
81      this.mainPanel = mainPanel;
82    }
83  
84    @Override
85    public void close(MolecularAssembly assembly) {
86      Optional<FFXSystem> origSys = switchTo(assembly);
87      mainPanel.closeWait();
88      origSys.ifPresent(this::switchBack);
89    }
90  
91    @Override
92    public void closeAll(MolecularAssembly[] assemblies) {
93      mainPanel.closeAll();
94    }
95  
96    @Override
97    public ForceFieldEnergy energy(MolecularAssembly molecularAssembly) {
98      Optional<FFXSystem> origSys = switchTo(molecularAssembly);
99      ForceFieldEnergy forceFieldEnergy = modelingShell.energy();
100     origSys.ifPresent(this::switchBack);
101     return forceFieldEnergy;
102   }
103 
104   @Override
105   public MolecularAssembly getActiveAssembly() {
106     return mainPanel.getHierarchy().getActive();
107   }
108 
109   @Override
110   public List<String> getArguments() {
111     return modelingShell.getArgs();
112   }
113 
114   @Override
115   public AlgorithmListener getDefaultListener() {
116     return modelingShell;
117   }
118 
119   @Override
120   public SystemFilter getFilter() {
121     return lastFilter;
122   }
123 
124   @Override
125   public boolean isLocal() {
126     return false;
127   }
128 
129   @Override
130   public void md(
131       MolecularAssembly assembly,
132       int nStep,
133       double timeStep,
134       double printInterval,
135       double saveInterval,
136       double temperature,
137       boolean initVelocities,
138       File dyn) {
139     Optional<FFXSystem> origSys = switchTo(assembly);
140     modelingShell.md(
141         nStep, timeStep, printInterval, saveInterval, temperature, initVelocities, dyn);
142     origSys.ifPresent(this::switchBack);
143   }
144 
145   @Override
146   public Potential minimize(MolecularAssembly assembly, double eps) {
147     Optional<FFXSystem> origSys = switchTo(assembly);
148     Potential pot = modelingShell.minimize(eps);
149 
150     origSys.ifPresent(this::switchBack);
151     return pot;
152   }
153 
154   @Override
155   public FFXSystem[] open(String[] files, int nThreads) {
156     FFXSystem[] systems = mainPanel.openWait(files, nThreads);
157     lastFilter = mainPanel.getFilter();
158     return systems;
159   }
160 
161   @Override
162   public FFXSystem open(String file) {
163     FFXSystem[] systems = mainPanel.openWait(file);
164     lastFilter = mainPanel.getFilter();
165     if (systems == null) {
166       return null;
167     }
168     return systems[0];
169   }
170 
171   @Override
172   public FFXSystem[] openAll(String file) {
173     FFXSystem[] systems = mainPanel.openWait(file);
174     lastFilter = mainPanel.getFilter();
175     return systems;
176   }
177 
178   @Override
179   public FFXSystem[] openAll(String[] files) {
180     FFXSystem[] systems = mainPanel.openWait(files);
181     lastFilter = mainPanel.getFilter();
182     return systems;
183   }
184 
185   @Override
186   public FFXSystem[] openAll(String file, int nThreads) {
187     FFXSystem[] systems = mainPanel.openWait(file, nThreads);
188     lastFilter = mainPanel.getFilter();
189     return systems;
190   }
191 
192   /**
193    * Mutates file on-the-fly as it is being opened. Used to open files for pHMD in fully-protonated
194    * form.
195    *
196    * @param file a {@link java.io.File} object.
197    * @param mutations a {@link java.util.List} object.
198    * @return a {@link ffx.potential.MolecularAssembly} object.
199    */
200   @Override
201   public MolecularAssembly openWithMutations(File file, List<Mutation> mutations) {
202     file = new File(FilenameUtils.normalize(file.getAbsolutePath()));
203     // Set the Current Working Directory based on this file.
204     mainPanel.setCWD(file.getParentFile());
205 
206     // Create the CompositeConfiguration properties.
207     CompositeConfiguration properties = Keyword.loadProperties(file);
208     // Create an FFXSystem for this file.
209     FFXSystem newSystem = new FFXSystem(file, "Open with mutations", properties);
210     // Create a Force Field.
211     ForceFieldFilter forceFieldFilter = new ForceFieldFilter(properties);
212     ForceField forceField = forceFieldFilter.parse();
213     String[] patches = properties.getStringArray("patch");
214     for (String patch : patches) {
215       logger.info(" Attempting to read force field patch from " + patch + ".");
216       CompositeConfiguration patchConfiguration = new CompositeConfiguration();
217       patchConfiguration.addProperty("parameters", patch);
218       forceFieldFilter = new ForceFieldFilter(patchConfiguration);
219       ForceField patchForceField = forceFieldFilter.parse();
220       forceField.append(patchForceField);
221       if (RotamerLibrary.addRotPatch(patch)) {
222         logger.info(format(" Loaded rotamer definitions from patch %s.", patch));
223       }
224     }
225     newSystem.setForceField(forceField);
226 
227     PDBFilter pdbFilter = new PDBFilter(file, newSystem, forceField, properties);
228     pdbFilter.mutate(mutations);
229     UIFileOpener opener = new UIFileOpener(pdbFilter, mainPanel);
230     opener.run();
231     lastFilter = pdbFilter;
232     if (opener.getAllAssemblies().length > 1) {
233       logger.log(
234           Level.WARNING, "Found multiple assemblies in file {0}, opening first.", file.getName());
235     }
236     return opener.getAssembly();
237   }
238 
239   @Override
240   public double returnEnergy(MolecularAssembly assembly) {
241     Optional<FFXSystem> origSys = switchTo(assembly);
242     double e = modelingShell.returnEnergy();
243     origSys.ifPresent(this::switchBack);
244     return e;
245   }
246 
247   @Override
248   public void save(MolecularAssembly assembly, File file) {
249     saveAsXYZ(assembly, file);
250   }
251 
252   @Override
253   public void saveAsXYZinP1(MolecularAssembly assembly, File file) {
254     Optional<FFXSystem> origSys = switchTo(assembly);
255     mainPanel.saveAsP1(file);
256     lastFilter = mainPanel.getFilter();
257     origSys.ifPresent(this::switchBack);
258   }
259 
260   @Override
261   public void saveAsPDB(MolecularAssembly assembly, File file) {
262     Optional<FFXSystem> origSys = switchTo(assembly);
263     mainPanel.saveAsPDB(file);
264     lastFilter = mainPanel.getFilter();
265     origSys.ifPresent(this::switchBack);
266   }
267 
268   @Override
269   public void saveAsPDB(MolecularAssembly assembly, File file, boolean writeEnd, boolean append) {
270     Optional<FFXSystem> origSys = switchTo(assembly);
271     mainPanel.saveAsPDB(file, false, append);
272     lastFilter = mainPanel.getFilter();
273     origSys.ifPresent(this::switchBack);
274   }
275 
276   @Override
277   public void saveAsPDB(MolecularAssembly[] assemblies, File file) {
278     mainPanel.saveAsPDB(assemblies, file);
279     lastFilter = mainPanel.getFilter();
280   }
281 
282   @Override
283   public void saveAsXYZ(MolecularAssembly assembly, File file) {
284     Optional<FFXSystem> origSys = switchTo(assembly);
285     mainPanel.saveAsXYZ(file);
286     lastFilter = mainPanel.getFilter();
287     origSys.ifPresent(this::switchBack);
288   }
289 
290   @Override
291   public void saveAsPDBinP1(MolecularAssembly assembly, File file) {
292     Optional<FFXSystem> origSys = switchTo(assembly);
293     mainPanel.savePDBasP1(file);
294     lastFilter = mainPanel.getFilter();
295     origSys.ifPresent(this::switchBack);
296   }
297 
298   @Override
299   public double time() {
300     return modelingShell.time();
301   }
302 
303   /**
304    * Switches the hierarchy's active system to assembly if assembly is present inside the hierarchy;
305    * returns an Optional FFXSystem of the prior active system, or an empty Optional if no switch was
306    * made
307    *
308    * @param assembly To switch to
309    * @return Original system if switched, else empty Optional
310    */
311   private Optional<FFXSystem> switchTo(MolecularAssembly assembly) {
312     Optional<FFXSystem> origSystem;
313     if (!(assembly instanceof FFXSystem)) {
314       origSystem = Optional.empty();
315       return origSystem;
316     }
317 
318     Hierarchy hierarchy = mainPanel.getHierarchy();
319     FFXSystem activeSys = hierarchy.getActive();
320     FFXSystem assemblySys = (FFXSystem) assembly;
321 
322     for (FFXSystem sys : hierarchy.getSystems()) {
323       if (sys == assemblySys) {
324         origSystem = Optional.of(activeSys);
325         hierarchy.setActive(assemblySys);
326         return origSystem;
327       }
328     }
329 
330     origSystem = Optional.empty();
331     return origSystem;
332   }
333 
334   /**
335    * Switches the hierarchy's active system back to what it was.
336    *
337    * @param origSystem The active system.
338    */
339   private void switchBack(FFXSystem origSystem) {
340     if (origSystem != null) {
341       Hierarchy hierarchy = mainPanel.getHierarchy();
342       hierarchy.setActive(origSystem);
343     }
344   }
345 }