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