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.algorithms.mc;
39  
40  import ffx.algorithms.dynamics.thermostats.Thermostat;
41  import ffx.potential.ForceFieldEnergy;
42  import ffx.potential.MolecularAssembly;
43  import ffx.potential.bonded.AminoAcidUtils;
44  import ffx.potential.bonded.AminoAcidUtils.AminoAcid3;
45  import ffx.potential.bonded.Residue;
46  import ffx.potential.parsers.PDBFilter;
47  import org.apache.commons.io.FilenameUtils;
48  
49  import java.io.File;
50  import java.util.List;
51  import java.util.concurrent.ThreadLocalRandom;
52  import java.util.logging.Logger;
53  
54  import static java.lang.String.format;
55  import static org.apache.commons.math3.util.FastMath.min;
56  
57  /**
58   * Conformational Biased Monte Carlo (applied to ALL torsions of a peptide side-chain).
59   *
60   * <p>This method is described by Frenkel/Smit in "Understanding Molecular Simulation" Chapters
61   * 13.2,13.3 This uses the "conformational biasing" method to select whole rotamer transformations
62   * that are frequently accepted.
63   *
64   * @author Stephen D. LuCore
65   */
66  public class RosenbluthCBMC implements MonteCarloListener {
67  
68    private static final Logger logger = Logger.getLogger(RosenbluthCBMC.class.getName());
69  
70    /** The MolecularAssembly to operate on. */
71    private final MolecularAssembly molecularAssembly;
72    /** The ForceFieldEnergy in use. */
73    private final ForceFieldEnergy forceFieldEnergy;
74    /** The temperature in use. */
75    private final double temperature;
76    /** At each move, one of these residues will be chosen as the target. */
77    private final List<Residue> targets;
78    /** Number of mcUpdate() calls (e.g. MD steps) between move proposals. */
79    private final int mcFrequency;
80    /** Size of the trial sets, k. */
81    private final int trialSetSize;
82    /** Keeps track of calls to mcUpdate (e.g. MD steps). */
83    private int steps = 0;
84    /** Counters for proposed and accepted moves. */
85    private int numMovesProposed = 0;
86    /** Writes PDBs of each trial set and original/proposed configurations. */
87    private final boolean writeSnapshots;
88    /** PDBFilter to write out result. */
89    private PDBFilter writer = null;
90  
91    /**
92     * RRMC constructor.
93     *
94     * @param targets Residues to undergo RRMC.
95     * @param mcFrequency Number of MD steps between RRMC proposals.
96     * @param trialSetSize Larger values cost more but increase acceptance.
97     * @param molecularAssembly a {@link ffx.potential.MolecularAssembly} object.
98     * @param ffe a {@link ffx.potential.ForceFieldEnergy} object.
99     * @param thermostat a {@link ffx.algorithms.dynamics.thermostats.Thermostat} object.
100    * @param writeSnapshots a boolean.
101    */
102   public RosenbluthCBMC(MolecularAssembly molecularAssembly, ForceFieldEnergy ffe,
103       Thermostat thermostat, List<Residue> targets, int mcFrequency, int trialSetSize,
104       boolean writeSnapshots) {
105     this.targets = targets;
106     this.mcFrequency = mcFrequency;
107     this.trialSetSize = trialSetSize;
108     this.molecularAssembly = molecularAssembly;
109     this.forceFieldEnergy = ffe;
110     this.writeSnapshots = writeSnapshots;
111 
112     if (thermostat != null) {
113       temperature = thermostat.getTargetTemperature();
114     } else {
115       temperature = 298.15;
116     }
117 
118     for (int i = targets.size() - 1; i >= 0; i--) {
119       AminoAcid3 name = AminoAcidUtils.AminoAcid3.valueOf(targets.get(i).getName());
120       if (name == AminoAcidUtils.AminoAcid3.GLY || name == AminoAcidUtils.AminoAcid3.PRO
121           || name == AminoAcidUtils.AminoAcid3.ALA) {
122         targets.remove(i);
123       }
124     }
125     if (targets.isEmpty()) {
126       logger.severe(" Empty target list for CMBC.");
127     }
128   }
129 
130   /**
131    * cbmcStep.
132    *
133    * @return a boolean.
134    */
135   public boolean cbmcStep() {
136     numMovesProposed++;
137     boolean accepted;
138 
139     // Select a target residue.
140     int index = ThreadLocalRandom.current().nextInt(targets.size());
141     Residue target = targets.get(index);
142     RosenbluthChiAllMove cbmcMove = new RosenbluthChiAllMove(molecularAssembly, target, trialSetSize,
143         forceFieldEnergy, temperature, writeSnapshots, numMovesProposed, true);
144     if (cbmcMove.getMode() == RosenbluthChiAllMove.MODE.CHEAP) {
145       return cbmcMove.wasAccepted();
146     }
147     double Wn = cbmcMove.getWn();
148     double Wo = cbmcMove.getWo();
149     double criterion = min(1, Wn / Wo);
150     double rng = ThreadLocalRandom.current().nextDouble();
151     logger.info(format("    rng:    %5.2f", rng));
152     if (rng < criterion) {
153       cbmcMove.move();
154       logger.info(format(" Accepted!  Energy: %.4f\n", cbmcMove.finalEnergy));
155       accepted = true;
156       write();
157     } else {
158       logger.info(" Denied.\n");
159       accepted = false;
160     }
161 
162     return accepted;
163   }
164 
165   /**
166    * controlStep.
167    *
168    * @return a boolean.
169    */
170   public boolean controlStep() {
171     int index = ThreadLocalRandom.current().nextInt(targets.size());
172     Residue target = targets.get(index);
173     RosenbluthChiAllMove cbmcMove = new RosenbluthChiAllMove(molecularAssembly, target, -1,
174         forceFieldEnergy, temperature, false, numMovesProposed, true);
175     return cbmcMove.wasAccepted();
176   }
177 
178   /** {@inheritDoc} */
179   @Override
180   public boolean mcUpdate(double temperature) {
181     steps++;
182     if (steps % mcFrequency == 0) {
183       return cbmcStep();
184     }
185     return false;
186   }
187 
188   /** Write out a PDB file. */
189   private void write() {
190     if (writer == null) {
191       writer = new PDBFilter(molecularAssembly.getFile(), molecularAssembly, null, null);
192     }
193     String filename = molecularAssembly.getFile().getAbsolutePath();
194     if (!filename.contains("_mc")) {
195       filename = FilenameUtils.removeExtension(filename) + "_mc.pdb";
196     }
197     File file = new File(filename);
198     writer.writeFile(file, false);
199   }
200 }