1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.algorithms.commands;
39
40 import ffx.algorithms.cli.AlgorithmsCommand;
41 import ffx.algorithms.cli.ManyBodyOptions;
42 import ffx.algorithms.optimize.RotamerOptimization;
43 import ffx.algorithms.optimize.TitrationManyBody;
44 import ffx.potential.ForceFieldEnergy;
45 import ffx.potential.MolecularAssembly;
46 import ffx.potential.bonded.Atom;
47 import ffx.potential.bonded.LambdaInterface;
48 import ffx.potential.bonded.Residue;
49 import ffx.potential.bonded.Rotamer;
50 import ffx.potential.bonded.RotamerLibrary;
51 import ffx.potential.cli.AlchemicalOptions;
52 import ffx.potential.parsers.PDBFilter;
53 import ffx.utilities.Constants;
54 import ffx.utilities.FFXBinding;
55 import org.apache.commons.configuration2.CompositeConfiguration;
56 import picocli.CommandLine.Command;
57 import picocli.CommandLine.Option;
58 import picocli.CommandLine.Mixin;
59 import picocli.CommandLine.Parameters;
60
61 import java.io.File;
62 import java.io.FileWriter;
63 import java.util.ArrayList;
64 import java.util.HashSet;
65 import java.util.List;
66 import java.util.Set;
67
68 import static ffx.potential.bonded.NamingUtils.renameAtomsToPDBStandard;
69 import static java.lang.Double.parseDouble;
70 import static java.lang.String.format;
71 import static org.apache.commons.math3.util.FastMath.log;
72
73
74
75
76
77
78
79
80 @Command(description = " Run GenZ function for free energy change.", name = "GenZ")
81 public class GenZ extends AlgorithmsCommand {
82
83 @Mixin
84 private ManyBodyOptions manyBodyOptions;
85
86 @Mixin
87 private AlchemicalOptions alchemicalOptions;
88
89 @Option(names = {"--resC", "--residueChain"}, paramLabel = "A", defaultValue = "A",
90 description = "The chain that is mutating.")
91 private String mutatingChain;
92
93 @Option(names = {"-n", "--residueName"}, paramLabel = "ALA", defaultValue = "",
94 description = "Mutant residue.")
95 private String resName;
96
97 @Option(names = {"--rEE", "--ro-ensembleEnergy"}, paramLabel = "0.0", defaultValue = "0.0",
98 description = "Keep permutations within ensemble Energy kcal/mol from the GMEC.")
99 private String ensembleEnergy;
100
101 @Option(names = {"--un", "--unfolded"}, paramLabel = "false", defaultValue = "false",
102 description = "Run the unfolded state tripeptide.")
103 private boolean unfolded;
104
105 @Option(names = {"--pKa"}, paramLabel = "false", defaultValue = "false",
106 description = "Calculating protonation populations for pKa shift.")
107 private boolean pKa;
108
109 @Option(names = {"--pB", "--printBoltzmann"}, paramLabel = "false", defaultValue = "false",
110 description = "Save the Boltzmann weights of protonated residue and total Boltzmann weights.")
111 private boolean printBoltzmann;
112
113 @Option(names = {"--pF", "--printFiles"}, paramLabel = "false", defaultValue = "false",
114 description = "Write to an energy restart file and ensemble file.")
115 private boolean printFiles;
116
117 @Option(names = {"--rCS", "--recomputeSelf"}, paramLabel = "false", defaultValue = "false",
118 description = "Recompute the self energies after loading a restart file.")
119 private boolean recomputeSelf;
120
121
122
123
124 @Parameters(arity = "1", paramLabel = "file", defaultValue = "",
125 description = "XYZ or PDB input file.")
126 private String filename;
127
128 ForceFieldEnergy potentialEnergy;
129
130
131
132 private double[][] populationArray;
133
134
135
136
137 public GenZ() {
138 super();
139 }
140
141
142
143
144
145
146 public GenZ(FFXBinding binding) {
147 super(binding);
148 }
149
150
151
152
153
154
155 public GenZ(String[] args) {
156 super(args);
157 }
158
159
160
161
162 @Override
163 public GenZ run() {
164 if (!init()) {
165 return this;
166 }
167
168
169 double titrationPH = manyBodyOptions.getTitrationPH();
170 double inclusionCutoff = manyBodyOptions.getInclusionCutoff();
171 int mutatingResidue = manyBodyOptions.getInterestedResidue();
172 boolean onlyTitration = manyBodyOptions.getOnlyTitration();
173 double pHRestraint = manyBodyOptions.getPHRestraint();
174
175 if (manyBodyOptions.getTitration()) {
176 System.setProperty("manybody-titration", "true");
177 }
178
179
180 boolean lambdaTerm = alchemicalOptions.hasSoftcore();
181 if (lambdaTerm) {
182
183 System.setProperty("lambdaterm", "true");
184
185 System.setProperty("elec-lambdaterm", "false");
186
187 System.setProperty("intramolecular-softcore", "true");
188 }
189
190 System.setProperty("ro-ensembleEnergy", ensembleEnergy);
191
192
193 activeAssembly = getActiveAssembly(filename);
194 if (activeAssembly == null) {
195 logger.info(helpString());
196 return this;
197 }
198
199
200 filename = activeAssembly.getFile().getAbsolutePath();
201
202
203 String unfoldedFileName = null;
204 if (unfolded) {
205
206 unfoldedFileName = "wt" + mutatingResidue + ".pdb";
207 List<Atom> atoms = activeAssembly.getAtomList();
208 Set<Atom> excludeAtoms = new HashSet<>();
209 for (Atom atom : atoms) {
210 if (atom.getResidueNumber() < mutatingResidue - 1 || atom.getResidueNumber() > mutatingResidue + 1) {
211 excludeAtoms.add(atom);
212 } else if (atom.getResidueNumber() == mutatingResidue - 1 && "H".equals(atom.getName())) {
213 excludeAtoms.add(atom);
214 }
215 }
216 File file = new File(unfoldedFileName);
217 PDBFilter pdbFilter = new PDBFilter(file, activeAssembly, activeAssembly.getForceField(),
218 activeAssembly.getProperties());
219 pdbFilter.writeFile(file, false, excludeAtoms, true, true);
220
221
222 activeAssembly = getActiveAssembly(unfoldedFileName);
223 filename = activeAssembly.getFile().getAbsolutePath();
224 }
225
226
227 double[] boltzmannWeights = new double[2];
228 double[][] titrateBoltzmann = null;
229 double totalBoltzmann = 0;
230 List<Residue> residueList = activeAssembly.getResidueList();
231
232 String mutatedFileName = "";
233
234 if (mutatingResidue != -1) {
235 FFXBinding mutatorBinding = new FFXBinding();
236 if (unfolded) {
237 String[] args = {"-r", String.valueOf(mutatingResidue), "-n", resName, unfoldedFileName};
238 mutatorBinding.setVariable("args", args);
239 } else {
240 String[] args = {"-r", String.valueOf(mutatingResidue), "-n", resName, "--ch", mutatingChain, filename};
241 mutatorBinding.setVariable("args", args);
242 }
243 MutatePDB mutatePDB = new MutatePDB(mutatorBinding);
244 mutatePDB.run();
245 mutatedFileName = (String) mutatorBinding.getVariable("versionFileName");
246 }
247
248 String listResidues = "";
249
250
251 if ((mutatingResidue != -1 && inclusionCutoff != -1) || onlyTitration) {
252 listResidues = manyBodyOptions.selectInclusionResidues(residueList, mutatingResidue, onlyTitration, inclusionCutoff);
253 }
254
255
256 int numLoop = 1;
257 if (mutatingResidue != -1) {
258 numLoop = 2;
259 }
260
261
262 int[] optimalRotamers;
263 Set<Atom> excludeAtoms = new HashSet<>();
264
265
266 for (int j = 0; j < numLoop; j++) {
267
268
269 if (j > 0) {
270 activeAssembly = getActiveAssembly(mutatedFileName);
271 activeAssembly.getPotentialEnergy().energy();
272 filename = activeAssembly.getFile().getAbsolutePath();
273 }
274
275 if (activeAssembly == null) {
276 logger.info(helpString());
277 return this;
278 }
279
280 CompositeConfiguration properties = activeAssembly.getProperties();
281
282
283 if (properties.getBoolean("standardizeAtomNames", false)) {
284 renameAtomsToPDBStandard(activeAssembly);
285 }
286
287
288 activeAssembly.getPotentialEnergy().setPrintOnFailure(false, false);
289 potentialEnergy = activeAssembly.getPotentialEnergy();
290
291
292 if (!pKa || onlyTitration) {
293 manyBodyOptions.setListResidues(listResidues);
294 }
295
296
297 List<Residue> residues = manyBodyOptions.collectResidues(activeAssembly);
298 if (residues == null || residues.isEmpty()) {
299 logger.info(" There are no residues in the active system to optimize.");
300 return this;
301 }
302
303
304 TitrationManyBody titrationManyBody = null;
305 if (manyBodyOptions.getTitration()) {
306 logger.info("\n Adding titration hydrogen to : " + filename + "\n");
307
308
309 List<Integer> resNumberList = new ArrayList<>();
310 for (Residue residue : residues) {
311 resNumberList.add(residue.getResidueNumber());
312 }
313
314
315 titrationManyBody = new TitrationManyBody(filename, activeAssembly.getForceField(),
316 resNumberList, titrationPH, manyBodyOptions);
317 activeAssembly = titrationManyBody.getProtonatedAssembly();
318 potentialEnergy = activeAssembly.getPotentialEnergy();
319 }
320
321
322 if (lambdaTerm) {
323 alchemicalOptions.setFirstSystemAlchemistry(activeAssembly);
324 LambdaInterface lambdaInterface = potentialEnergy;
325 double lambda = alchemicalOptions.getInitialLambda();
326 logger.info(format(" Setting ManyBody softcore lambda to: %5.3f", lambda));
327 lambdaInterface.setLambda(lambda);
328 }
329
330
331 RotamerOptimization rotamerOptimization = new RotamerOptimization(activeAssembly, potentialEnergy, algorithmListener);
332 rotamerOptimization.setPrintFiles(printFiles);
333 rotamerOptimization.setWriteEnergyRestart(printFiles);
334 rotamerOptimization.setPHRestraint(pHRestraint);
335 rotamerOptimization.setRecomputeSelf(recomputeSelf);
336 rotamerOptimization.setpH(titrationPH);
337
338 manyBodyOptions.initRotamerOptimization(rotamerOptimization, activeAssembly);
339
340
341 List<Residue> selectedResidues = rotamerOptimization.getResidues();
342 rotamerOptimization.initFraction(selectedResidues);
343
344 logger.info("\n Initial Potential Energy:");
345 potentialEnergy.energy(false, true);
346
347 logger.info("\n Initial Rotamer Torsion Angles:");
348 RotamerLibrary.measureRotamers(selectedResidues, false);
349
350
351 rotamerOptimization.optimize(manyBodyOptions.getAlgorithm(selectedResidues.size()));
352
353 int[] currentRotamers = new int[selectedResidues.size()];
354
355
356 try {
357 rotamerOptimization.getFractions(selectedResidues.toArray(new Residue[0]), 0, currentRotamers);
358 } catch (Exception e) {
359 logger.severe(" Error calculating fractions: " + e.getMessage());
360 return this;
361 }
362
363
364 boltzmannWeights[j] = rotamerOptimization.getTotalBoltzmann();
365
366
367 populationArray = rotamerOptimization.getFraction();
368 if (printBoltzmann) {
369 titrateBoltzmann = rotamerOptimization.getPopulationBoltzmann();
370 totalBoltzmann = rotamerOptimization.getTotalBoltzmann();
371 }
372
373
374 optimalRotamers = rotamerOptimization.getOptimumRotamers();
375 if (manyBodyOptions.getTitration()) {
376
377 titrationManyBody.excludeExcessAtoms(excludeAtoms, optimalRotamers, selectedResidues);
378 }
379
380
381 if (pKa) {
382 rotamerOptimization.getProtonationPopulations(selectedResidues.toArray(new Residue[0]));
383 }
384
385
386 String populationFilename = "populations.txt";
387 if (j > 0) {
388 populationFilename = "populations." + j + ".txt";
389 }
390 try (FileWriter fileWriter = new FileWriter(populationFilename)) {
391 int residueIndex = 0;
392 for (Residue residue : selectedResidues) {
393 fileWriter.write("\n");
394 double protonationBoltzmannSum = 0.0;
395
396 Rotamer[] rotamers = residue.getRotamers();
397 for (int rotIndex = 0; rotIndex < rotamers.length; rotIndex++) {
398 String rotPop = format("%.6f", populationArray[residueIndex][rotIndex]);
399 fileWriter.write(residue.getName() + residue.getResidueNumber() + "\t" +
400 rotamers[rotIndex].toString() + "\t" + rotPop + "\n");
401 if (pKa) {
402 switch (rotamers[rotIndex].getName()) {
403 case "HIS":
404 case "LYS":
405 case "GLH":
406 case "ASH":
407 case "CYS":
408 if (printBoltzmann) {
409 protonationBoltzmannSum += titrateBoltzmann[residueIndex][rotIndex];
410 }
411 break;
412 default:
413 break;
414 }
415 }
416
417 }
418
419 if (printBoltzmann) {
420 logger.info("\n Residue " + residue.getName() + residue.getResidueNumber()
421 + " Protonated Boltzmann: " + protonationBoltzmannSum);
422 }
423 residueIndex += 1;
424 }
425 logger.info("\n Total Boltzmann: " + totalBoltzmann);
426 logger.info("\n Successfully wrote to the populations file: " + populationFilename);
427 } catch (Exception e) {
428 logger.severe("Error writing populations file: " + e.getMessage());
429 }
430 }
431
432
433 System.setProperty("standardizeAtomNames", "false");
434 File modelFile = saveDirFile(activeAssembly.getFile());
435 PDBFilter pdbFilter = new PDBFilter(modelFile, activeAssembly, activeAssembly.getForceField(),
436 activeAssembly.getProperties());
437 if (manyBodyOptions.getTitration()) {
438 String remark = format("Titration pH: %6.3f", titrationPH);
439 if (!pdbFilter.writeFile(modelFile, false, excludeAtoms, true, true, new String[]{remark})) {
440 logger.info(format(" Save failed for %s", activeAssembly));
441 }
442 } else {
443 if (!pdbFilter.writeFile(modelFile, false, excludeAtoms, true, true)) {
444 logger.info(format(" Save failed for %s", activeAssembly));
445 }
446 }
447 if (mutatingResidue != -1) {
448 CompositeConfiguration properties = activeAssembly.getProperties();
449 String temp = properties.getString("temperature", "298.15");
450 double temperature = 298.15;
451 if (temp != null) {
452 temperature = parseDouble(temp);
453 }
454 double kT = temperature * Constants.R;
455
456 double dG = -kT * log(boltzmannWeights[1] / boltzmannWeights[0]);
457 logger.info(format("\n Mutation Free Energy Difference: %12.8f (kcal/mol)", dG));
458 }
459
460 return this;
461 }
462
463
464
465
466
467 public double[][] getPopulationArray() {
468 return populationArray;
469 }
470
471
472
473
474
475
476 public ForceFieldEnergy getPotential() {
477 return potentialEnergy;
478 }
479
480 }