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 }