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.algorithms.cli; 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.MolecularAssembly; 47 import ffx.utilities.FFXScript; 48 import groovy.lang.Binding; 49 50 import javax.annotation.Nullable; 51 import java.io.File; 52 import java.util.ArrayList; 53 import java.util.List; 54 55 /** 56 * Base class for scripts in the Algorithms package, providing some key functions. 57 * 58 * @author Michael J. Schnieders 59 * @since 1.0 60 */ 61 public class AlgorithmsScript extends FFXScript { 62 63 /** 64 * An instance of AlgorithmFunctions passed into the current context. 65 */ 66 public AlgorithmFunctions algorithmFunctions; 67 68 /** 69 * An active MolecularAssembly passed into the current context or loaded by the Script from a file 70 * argument. 71 */ 72 public MolecularAssembly activeAssembly; 73 74 /** 75 * An instance of the AlgorithmListener interface. 76 */ 77 public AlgorithmListener algorithmListener; 78 79 /** 80 * The directory in which to place output files. Mostly for tests. 81 */ 82 protected File baseDir; 83 84 public AlgorithmsScript() { 85 this(new Binding()); 86 } 87 88 public AlgorithmsScript(Binding binding) { 89 super(binding); 90 } 91 92 /** 93 * Reclaims resources associated with all Potential objects associated with this script. 94 * 95 * @return If all Potentials had resources reclaimed. 96 */ 97 public boolean destroyPotentials() { 98 boolean allSucceeded = true; 99 for (Potential potent : getPotentials()) { 100 logger.fine(format(" Potential %s is being destroyed. ", potent)); 101 allSucceeded = allSucceeded && potent.destroy(); 102 } 103 return allSucceeded; 104 } 105 106 /** 107 * Returns a List of all Potential objects associated with this script. 108 * 109 * @return All Potentials. Sometimes empty, never null. 110 */ 111 public List<Potential> getPotentials() { 112 List<Potential> plist = new ArrayList<>(); 113 if (activeAssembly != null && activeAssembly.getPotentialEnergy() != null) { 114 plist.add(activeAssembly.getPotentialEnergy()); 115 } 116 return plist; 117 } 118 119 /** 120 * {@inheritDoc} 121 * 122 * <p>Execute the BaseScript init method, then load algorithm functions. 123 */ 124 @Override 125 public boolean init() { 126 if (!super.init()) { 127 return false; 128 } 129 130 Binding binding = getBinding(); 131 132 if (binding.hasVariable("functions")) { 133 algorithmFunctions = (AlgorithmFunctions) binding.getVariable("functions"); 134 } else { 135 algorithmFunctions = new AlgorithmUtils(); 136 binding.setVariable("functions", algorithmFunctions); 137 } 138 139 activeAssembly = null; 140 if (binding.hasVariable("active")) { 141 activeAssembly = (MolecularAssembly) binding.getVariable("active"); 142 } 143 144 algorithmListener = null; 145 if (binding.hasVariable("listener")) { 146 algorithmListener = (AlgorithmListener) binding.getVariable("listener"); 147 } 148 149 if (binding.hasVariable("baseDir")) { 150 baseDir = (File) binding.getVariable("baseDir"); 151 } 152 153 return true; 154 } 155 156 /** 157 * Sets the directory this script should save files to. Mostly used for tests. 158 * 159 * @param baseDir Directory to save output to. 160 */ 161 public void setBaseDir(File baseDir) { 162 this.baseDir = baseDir; 163 } 164 165 /** 166 * Return a File in the base directory with the same name as the input file. 167 * <p> 168 * This will just be the original file if baseDir was never set, which is the case for production 169 * runs. 170 * 171 * @param file File to find a save location for. 172 * @return Returns a File in the base directory with the same name as the input file. 173 */ 174 protected File saveDirFile(File file) { 175 if (baseDir == null || !baseDir.exists() || !baseDir.isDirectory() || !baseDir.canWrite()) { 176 return file; 177 } else { 178 String baseName = file.getName(); 179 String newName = baseDir.getAbsolutePath() + File.separator + baseName; 180 return new File(newName); 181 } 182 } 183 184 /** 185 * If a filename is supplied, open it and return the MolecularAssembly. Otherwise, the current 186 * activeAssembly is returned (which may be null). 187 * 188 * @param filename Filename to open. 189 * @return The active assembly. 190 */ 191 public MolecularAssembly getActiveAssembly(@Nullable String filename) { 192 if (filename != null) { 193 // Open the supplied file. 194 MolecularAssembly[] assemblies = {algorithmFunctions.open(filename)}; 195 activeAssembly = assemblies[0]; 196 } 197 return activeAssembly; 198 } 199 200 /** 201 * If a filename is supplied, open it and return the MolecularAssemblies. Otherwise, the current 202 * activeAssembly is returned (which may be null). 203 * 204 * @param filename Filename to open. 205 * @return The active assemblies. 206 */ 207 public MolecularAssembly[] getActiveAssemblies(@Nullable String filename) { 208 MolecularAssembly[] assemblies; 209 if (filename != null) { 210 // Open the supplied file. 211 assemblies = algorithmFunctions.openAll(filename); 212 activeAssembly = assemblies[0]; 213 return assemblies; 214 } else { 215 assemblies = new MolecularAssembly[]{activeAssembly}; 216 } 217 return assemblies; 218 } 219 220 /** 221 * Set the Active Assembly. This is a work-around for a strange Groovy static compilation bug where 222 * direct assignment of activeAssembly in Groovy scripts that extend AlgorithmsScript fails (a NPE 223 * results). 224 * 225 * @param molecularAssembly The MolecularAssembly that should be active. 226 */ 227 public void setActiveAssembly(MolecularAssembly molecularAssembly) { 228 activeAssembly = molecularAssembly; 229 } 230 231 }