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.xray.commands;
39  
40  import ffx.algorithms.cli.AlgorithmsCommand;
41  import ffx.algorithms.cli.MinimizeOptions;
42  import ffx.numerics.Potential;
43  import ffx.potential.MolecularAssembly;
44  import ffx.utilities.FFXBinding;
45  import ffx.xray.DiffractionData;
46  import ffx.xray.RefinementMinimize;
47  import ffx.xray.RefinementMinimize.RefinementMode;
48  import ffx.xray.cli.XrayOptions;
49  import org.apache.commons.configuration2.CompositeConfiguration;
50  import picocli.CommandLine.Command;
51  import picocli.CommandLine.Mixin;
52  import picocli.CommandLine.Option;
53  import picocli.CommandLine.Parameters;
54  
55  import java.util.ArrayList;
56  import java.util.Arrays;
57  import java.util.List;
58  import java.util.stream.Collectors;
59  
60  import static java.lang.String.format;
61  import static org.apache.commons.io.FilenameUtils.removeExtension;
62  
63  /**
64   * The X-ray Minimize script.
65   * <br>
66   * Usage:
67   * <br>
68   * ffxc xray.Minimize [options] &lt;filename [file2...]&gt;
69   */
70  @Command(description = " Refine an X-ray/Neutron target.", name = "xray.Minimize")
71  public class Minimize extends AlgorithmsCommand {
72  
73    @Mixin
74    private MinimizeOptions minimizeOptions;
75  
76    @Mixin
77    private XrayOptions xrayOptions;
78  
79    /**
80     * -t or --threeStage Perform refinement in 3 stages: coordinates, b-factors, and then occupancies (overrides mode setting if true).
81     */
82    @Option(names = {"-t", "--threeStage"}, paramLabel = "false",
83        description = "Perform refinement in 3 stages: coordinates, b-factors, and then occupancies (overrides mode setting if true)")
84    private boolean threeStage = false;
85  
86    /**
87     * -E or --eps3 RMS gradient convergence criteria for three stage refinement (default of -1.0 automatically determine eps for each stage).
88     */
89    @Option(names = {"-E", "--eps3"}, paramLabel = "-1.0", arity = "3",
90        description = "RMS gradient convergence criteria for three stage refinement (default of -1.0 automatically determines eps for each stage).")
91    private double[] eps3 = {-1.0, -1.0, -1.0};
92  
93    /**
94     * One or more filenames.
95     */
96    @Parameters(arity = "1..*", paramLabel = "files", description = "PDB and Diffraction input files.")
97    private List<String> filenames;
98    private MolecularAssembly[] molecularAssemblies;
99    private DiffractionData diffractionData;
100 
101   /**
102    * Minimize constructor.
103    */
104   public Minimize() {
105     super();
106   }
107 
108   /**
109    * Minimize constructor that sets the command line arguments.
110    *
111    * @param args Command line arguments.
112    */
113   public Minimize(String[] args) {
114     super(args);
115   }
116 
117   /**
118    * Minimize constructor.
119    *
120    * @param binding The Binding to use.
121    */
122   public Minimize(FFXBinding binding) {
123     super(binding);
124   }
125 
126   @Override
127   public Minimize run() {
128 
129     if (!init()) {
130       return this;
131     }
132 
133     xrayOptions.init();
134 
135     String filename;
136     if (filenames != null && !filenames.isEmpty()) {
137       // Each alternate conformer is returned in a separate MolecularAssembly.
138       molecularAssemblies = algorithmFunctions.openAll(filenames.get(0));
139       activeAssembly = molecularAssemblies[0];
140       filename = filenames.get(0);
141     } else if (activeAssembly == null) {
142       logger.info(helpString());
143       return this;
144     } else {
145       molecularAssemblies = new MolecularAssembly[]{activeAssembly};
146       filename = activeAssembly.getFile().getAbsolutePath();
147     }
148 
149     logger.info("\n Running xray.Minimize on " + filename);
150 
151     // Combine script flags (in parseResult) with properties.
152     CompositeConfiguration properties = activeAssembly.getProperties();
153     xrayOptions.setProperties(parseResult, properties);
154 
155     // Set up diffraction data (can be multiple files)
156     diffractionData = xrayOptions.getDiffractionData(filenames, molecularAssemblies, properties);
157     diffractionData.scaleBulkFit();
158     diffractionData.printStats();
159     algorithmFunctions.energy(molecularAssemblies);
160 
161     // RMS gradient convergence criteria for three stage refinement
162     double coordinateEPS = eps3[0];
163     double bfactorEPS = eps3[1];
164     double occupancyEPS = eps3[2];
165 
166     // The number of corrections used in the BFGS update.
167     int nBFGS = minimizeOptions.getNBFGS();
168 
169     // Maximum number of refinement cycles.
170     int maxIterations = minimizeOptions.getIterations();
171 
172     if (threeStage) {
173       // Coordinates
174       RefinementMinimize refinementMinimize = new RefinementMinimize(diffractionData,
175           RefinementMode.COORDINATES);
176       if (coordinateEPS < 0.0) {
177         coordinateEPS = refinementMinimize.getEps();
178       }
179       if (maxIterations < Integer.MAX_VALUE) {
180         logger.info(format(
181             "\n RMS gradient convergence criteria: %8.5f, Maximum iterations %d", coordinateEPS,
182             maxIterations));
183       } else {
184         logger.info(format("\n RMS gradient convergence criteria: %8.5f", coordinateEPS));
185       }
186       refinementMinimize.minimize(nBFGS, coordinateEPS, maxIterations);
187       diffractionData.scaleBulkFit();
188       diffractionData.printStats();
189       algorithmFunctions.energy(molecularAssemblies);
190 
191       // B-factors
192       refinementMinimize = new RefinementMinimize(diffractionData, RefinementMode.BFACTORS);
193       if (bfactorEPS < 0.0) {
194         bfactorEPS = refinementMinimize.getEps();
195       }
196       if (maxIterations < Integer.MAX_VALUE) {
197         logger.info(
198             format("\n RMS gradient convergence criteria: %8.5f, Maximum iterations %d", bfactorEPS,
199                 maxIterations));
200       } else {
201         logger.info(format("\n RMS gradient convergence criteria: %8.5f", bfactorEPS));
202       }
203       refinementMinimize.minimize(nBFGS, bfactorEPS, maxIterations);
204       diffractionData.scaleBulkFit();
205       diffractionData.printStats();
206 
207       // Occupancies
208       if (
209           !diffractionData.getAltResidues().isEmpty() || !diffractionData.getAltMolecules().isEmpty()) {
210         refinementMinimize = new RefinementMinimize(diffractionData, RefinementMode.OCCUPANCIES);
211         if (occupancyEPS < 0.0) {
212           occupancyEPS = refinementMinimize.getEps();
213         }
214         if (maxIterations < Integer.MAX_VALUE) {
215           logger.info(format("\n RMS gradient convergence criteria: %8.5f, Maximum iterations %d",
216               occupancyEPS, maxIterations));
217         } else {
218           logger.info(format("\n RMS gradient convergence criteria: %8.5f", occupancyEPS));
219         }
220         refinementMinimize.minimize(occupancyEPS, maxIterations);
221         diffractionData.scaleBulkFit();
222         diffractionData.printStats();
223       } else {
224         logger.info("Occupancy refinement not necessary, skipping");
225       }
226 
227     } else {
228       // Type of refinement.
229       RefinementMode refinementMode = xrayOptions.refinementMode;
230       RefinementMinimize refinementMinimize = new RefinementMinimize(diffractionData, refinementMode);
231       double eps = minimizeOptions.getEps();
232       if (eps < 0.0) {
233         eps = refinementMinimize.getEps();
234       }
235 
236       if (maxIterations < Integer.MAX_VALUE) {
237         logger.info(format("\n RMS gradient convergence criteria: %8.5f, Maximum iterations %d", eps,
238             maxIterations));
239       } else {
240         logger.info(format("\n RMS gradient convergence criteria: %8.5f", eps));
241       }
242       refinementMinimize.minimize(eps, maxIterations);
243       diffractionData.scaleBulkFit();
244       diffractionData.printStats();
245     }
246 
247     // Print the final energy of each conformer.
248     algorithmFunctions.energy(molecularAssemblies);
249 
250     logger.info(" ");
251     diffractionData.writeModel(removeExtension(filename) + ".pdb");
252     diffractionData.writeData(removeExtension(filename) + ".mtz");
253 
254     return this;
255   }
256 
257   @Override
258   public List<Potential> getPotentials() {
259     if (molecularAssemblies == null) {
260       return new ArrayList<>();
261     } else {
262       return Arrays.stream(molecularAssemblies)
263           .filter(a -> a != null)
264           .map(MolecularAssembly::getPotentialEnergy)
265           .filter(e -> e != null)
266           .collect(Collectors.toList());
267     }
268   }
269 
270   @Override
271   public boolean destroyPotentials() {
272     return diffractionData == null ? true : diffractionData.destroy();
273   }
274 }