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.cli;
39  
40  import ffx.numerics.Potential;
41  import ffx.potential.MolecularAssembly;
42  import ffx.potential.utils.PotentialsFunctions;
43  import ffx.potential.utils.PotentialsUtils;
44  import ffx.utilities.FFXCommand;
45  import ffx.utilities.FFXBinding;
46  import org.apache.log4j.PropertyConfigurator;
47  
48  import javax.annotation.Nullable;
49  import java.io.File;
50  import java.util.ArrayList;
51  import java.util.List;
52  import java.util.Properties;
53  
54  import static org.apache.commons.io.FilenameUtils.getFullPath;
55  
56  /**
57   * Base class for scripts in the Potentials package, providing some key functions.
58   *
59   * @author Michael J. Schnieders
60   * @since 1.0
61   */
62  public abstract class PotentialCommand extends FFXCommand {
63  
64    /**
65     * An instance of PotentialFunctions passed into the current context.
66     */
67    public PotentialsFunctions potentialFunctions;
68  
69    /**
70     * An active MolecularAssembly passed into the current context or loaded by the Script from a file
71     * argument.
72     */
73    public MolecularAssembly activeAssembly;
74  
75    /**
76     * A temporary directory that contains script artifacts. Temporary files are often created by unit
77     * tests and then deleted.
78     */
79    public File baseDir = null;
80  
81    /**
82     * Default constructor.
83     */
84    public PotentialCommand() {
85      super();
86    }
87  
88    /**
89     * Create a Script using the supplied Binding.
90     *
91     * @param binding Binding with variables to use.
92     */
93    public PotentialCommand(FFXBinding binding) {
94      super(binding);
95    }
96  
97    /**
98     * Create a Script using the supplied command line arguments.
99     *
100    * @param args The command line arguments.
101    */
102   public PotentialCommand(String[] args) {
103     super(args);
104   }
105 
106   /**
107    * Reclaims resources associated with all Potential objects associated with this script.
108    *
109    * @return If all Potentials had resources reclaimed.
110    */
111   public boolean destroyPotentials() {
112     boolean allSucceeded = true;
113     for (Potential potential : getPotentials()) {
114       if (potential != null) {
115         allSucceeded = allSucceeded && potential.destroy();
116       }
117     }
118     return allSucceeded;
119   }
120 
121   /**
122    * Set the Active Assembly. This is a work-around for a strange Groovy static compilation bug where
123    * direct assignment of activeAssembly in Groovy scripts that extend PotentialScript fails (a NPE
124    * result).
125    *
126    * @param molecularAssembly The MolecularAssembly that should be active.
127    */
128   public void setActiveAssembly(MolecularAssembly molecularAssembly) {
129     activeAssembly = molecularAssembly;
130   }
131 
132   /**
133    * Returns a List of all Potential objects associated with this script. Should be written to
134    * tolerate nulls, as many tests run help() and exit without instantiating their Potentials.
135    *
136    * @return All Potentials. Sometimes empty, never null.
137    */
138   public List<Potential> getPotentials() {
139     List<Potential> potentialList = new ArrayList<>();
140     if (activeAssembly != null && activeAssembly.getPotentialEnergy() != null) {
141       potentialList.add(activeAssembly.getPotentialEnergy());
142     }
143     return potentialList;
144   }
145 
146   /**
147    * {@inheritDoc}
148    *
149    * <p>Execute the BaseScript init method, then load potential functions.
150    */
151   @Override
152   public boolean init() {
153     if (!super.init()) {
154       return false;
155     }
156 
157     potentialFunctions = (PotentialsFunctions) binding.getVariable("functions");
158 
159     if (potentialFunctions == null) {
160       // Potential package is running.
161       potentialFunctions = new PotentialsUtils();
162       binding.setVariable("functions", potentialFunctions);
163       // Turn off log4j.
164       System.setProperty("log4j.threshold", "OFF");
165       System.setProperty("log4j.rootLogger", "OFF");
166       System.setProperty("log4j1.compatibility", "true");
167       Properties properties = new Properties();
168       properties.setProperty("log4j.threshold", "OFF");
169       properties.setProperty("log4j2.level", "OFF");
170       properties.setProperty("org.apache.logging.log4j.level", "OFF");
171       PropertyConfigurator.configure(properties);
172     }
173 
174     try {
175       activeAssembly = (MolecularAssembly) binding.getVariable("active");
176     } catch (Exception e) {
177       activeAssembly = null;
178     }
179 
180     try {
181       // A temporary directory for script artifacts.
182       baseDir = (File) binding.getVariable("baseDir");
183     } catch (Exception e) {
184       // Ignore.
185     }
186 
187     return true;
188   }
189 
190   /**
191    * Check that we can write into the current base directory. If not, update the baseDir based on the
192    * supplied filename, including updating the script Binding instance.
193    *
194    * @param dirFromFilename Set the base directory variable <code>baseDir</code> using
195    *                        this filename if it's not set to a writeable directory.
196    * @return Return the base directory as a String (including an appended
197    * <code>File.separator</code>).
198    */
199   public String getBaseDirString(String dirFromFilename) {
200     if (baseDir == null || !baseDir.exists() || !baseDir.isDirectory() || !baseDir.canWrite()) {
201       File file = new File(dirFromFilename);
202       baseDir = new File(getFullPath(file.getAbsolutePath()));
203       binding.setVariable("baseDir", baseDir);
204     }
205     return baseDir.toString() + File.separator;
206   }
207 
208   /**
209    * If a filename is supplied, open it and return the MolecularAssembly. Otherwise, the current
210    * activeAssembly is returned (which may be null).
211    *
212    * @param filename Filename to open.
213    * @return The active assembly.
214    */
215   public MolecularAssembly getActiveAssembly(@Nullable String filename) {
216     if (filename != null) {
217       // Open the supplied file.
218       MolecularAssembly[] assemblies = {potentialFunctions.open(filename)};
219       activeAssembly = assemblies[0];
220     }
221     return activeAssembly;
222   }
223 
224   /**
225    * If a filename is supplied, open it and return the MolecularAssemblies.
226    * Otherwise, the current activeAssembly is returned (which may be null).
227    *
228    * @param filename Filename to open.
229    * @return The active assemblies.
230    */
231   public MolecularAssembly[] getActiveAssemblies(@Nullable String filename) {
232     MolecularAssembly[] assemblies;
233     if (filename != null) {
234       // Open the supplied file.
235       assemblies = potentialFunctions.openAll(filename);
236       activeAssembly = assemblies[0];
237       return assemblies;
238     } else {
239       assemblies = new MolecularAssembly[]{activeAssembly};
240     }
241     return assemblies;
242   }
243 
244 }