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 System.setProperty("sor-scf-fallback", "false");
170 System.setProperty("direct-scf-fallback", "true");
171
172
173 double titrationPH = manyBodyOptions.getTitrationPH();
174 double inclusionCutoff = manyBodyOptions.getInclusionCutoff();
175 int mutatingResidue = manyBodyOptions.getInterestedResidue();
176 boolean onlyTitration = manyBodyOptions.getOnlyTitration();
177 double pHRestraint = manyBodyOptions.getPHRestraint();
178
179 if (manyBodyOptions.getTitration()) {
180 System.setProperty("manybody-titration", "true");
181 }
182
183
184 boolean lambdaTerm = alchemicalOptions.hasSoftcore();
185 if (lambdaTerm) {
186
187 System.setProperty("lambdaterm", "true");
188
189 System.setProperty("elec-lambdaterm", "false");
190
191 System.setProperty("intramolecular-softcore", "true");
192 }
193
194 System.setProperty("ro-ensembleEnergy", ensembleEnergy);
195
196
197 activeAssembly = getActiveAssembly(filename);
198 if (activeAssembly == null) {
199 logger.info(helpString());
200 return this;
201 }
202
203
204 filename = activeAssembly.getFile().getAbsolutePath();
205
206
207 String unfoldedFileName = null;
208 if (unfolded) {
209
210 unfoldedFileName = "wt" + mutatingResidue + ".pdb";
211 List<Atom> atoms = activeAssembly.getAtomList();
212 Set<Atom> excludeAtoms = new HashSet<>();
213 for (Atom atom : atoms) {
214 if (atom.getResidueNumber() < mutatingResidue - 1 || atom.getResidueNumber() > mutatingResidue + 1) {
215 excludeAtoms.add(atom);
216 } else if (atom.getResidueNumber() == mutatingResidue - 1 && "H".equals(atom.getName())) {
217 excludeAtoms.add(atom);
218 }
219 }
220 File file = new File(unfoldedFileName);
221 PDBFilter pdbFilter = new PDBFilter(file, activeAssembly, activeAssembly.getForceField(),
222 activeAssembly.getProperties());
223 pdbFilter.writeFile(file, false, excludeAtoms, true, true);
224
225
226 activeAssembly = getActiveAssembly(unfoldedFileName);
227 filename = activeAssembly.getFile().getAbsolutePath();
228 }
229
230
231 double[] boltzmannWeights = new double[2];
232 double[][] titrateBoltzmann = null;
233 double totalBoltzmann = 0;
234 List<Residue> residueList = activeAssembly.getResidueList();
235
236 String mutatedFileName = "";
237
238 if (mutatingResidue != -1) {
239 FFXBinding mutatorBinding = new FFXBinding();
240 if (unfolded) {
241 String[] args = {"-r", String.valueOf(mutatingResidue), "-n", resName, unfoldedFileName};
242 mutatorBinding.setVariable("args", args);
243 } else {
244 String[] args = {"-r", String.valueOf(mutatingResidue), "-n", resName, "--ch", mutatingChain, filename};
245 mutatorBinding.setVariable("args", args);
246 }
247 MutatePDB mutatePDB = new MutatePDB(mutatorBinding);
248 mutatePDB.run();
249 mutatedFileName = (String) mutatorBinding.getVariable("versionFileName");
250 }
251
252 String listResidues = "";
253
254
255 if ((mutatingResidue != -1 && inclusionCutoff != -1) || onlyTitration) {
256 listResidues = manyBodyOptions.selectInclusionResidues(residueList, mutatingResidue, onlyTitration, inclusionCutoff);
257 }
258
259
260 int numLoop = 1;
261 if (mutatingResidue != -1) {
262 numLoop = 2;
263 }
264
265
266 int[] optimalRotamers;
267 Set<Atom> excludeAtoms = new HashSet<>();
268
269
270 for (int j = 0; j < numLoop; j++) {
271
272
273 if (j > 0) {
274 activeAssembly = getActiveAssembly(mutatedFileName);
275 activeAssembly.getPotentialEnergy().energy();
276 filename = activeAssembly.getFile().getAbsolutePath();
277 }
278
279 if (activeAssembly == null) {
280 logger.info(helpString());
281 return this;
282 }
283
284 CompositeConfiguration properties = activeAssembly.getProperties();
285
286
287 if (properties.getBoolean("standardizeAtomNames", false)) {
288 renameAtomsToPDBStandard(activeAssembly);
289 }
290
291
292 activeAssembly.getPotentialEnergy().setPrintOnFailure(false, false);
293 potentialEnergy = activeAssembly.getPotentialEnergy();
294
295
296 if (!pKa || onlyTitration) {
297 manyBodyOptions.setListResidues(listResidues);
298 }
299
300
301 List<Residue> residues = manyBodyOptions.collectResidues(activeAssembly);
302 if (residues == null || residues.isEmpty()) {
303 logger.info(" There are no residues in the active system to optimize.");
304 return this;
305 }
306
307
308 TitrationManyBody titrationManyBody = null;
309 if (manyBodyOptions.getTitration()) {
310 logger.info("\n Adding titration hydrogen to : " + filename + "\n");
311
312
313 List<Integer> resNumberList = new ArrayList<>();
314 for (Residue residue : residues) {
315 resNumberList.add(residue.getResidueNumber());
316 }
317
318
319 titrationManyBody = new TitrationManyBody(filename, activeAssembly.getForceField(),
320 resNumberList, titrationPH, manyBodyOptions);
321 activeAssembly = titrationManyBody.getProtonatedAssembly();
322 potentialEnergy = activeAssembly.getPotentialEnergy();
323 }
324
325
326 if (lambdaTerm) {
327 alchemicalOptions.setFirstSystemAlchemistry(activeAssembly);
328 LambdaInterface lambdaInterface = potentialEnergy;
329 double lambda = alchemicalOptions.getInitialLambda();
330 logger.info(format(" Setting ManyBody softcore lambda to: %5.3f", lambda));
331 lambdaInterface.setLambda(lambda);
332 }
333
334
335 RotamerOptimization rotamerOptimization = new RotamerOptimization(activeAssembly, potentialEnergy, algorithmListener);
336 rotamerOptimization.setPrintFiles(printFiles);
337 rotamerOptimization.setWriteEnergyRestart(printFiles);
338 rotamerOptimization.setPHRestraint(pHRestraint);
339 rotamerOptimization.setRecomputeSelf(recomputeSelf);
340 rotamerOptimization.setpH(titrationPH);
341
342 manyBodyOptions.initRotamerOptimization(rotamerOptimization, activeAssembly);
343
344
345 List<Residue> selectedResidues = rotamerOptimization.getResidues();
346 rotamerOptimization.initFraction(selectedResidues);
347
348 logger.info("\n Initial Potential Energy:");
349 potentialEnergy.energy(false, true);
350
351 logger.info("\n Initial Rotamer Torsion Angles:");
352 RotamerLibrary.measureRotamers(selectedResidues, false);
353
354
355 rotamerOptimization.optimize(manyBodyOptions.getAlgorithm(selectedResidues.size()));
356
357 int[] currentRotamers = new int[selectedResidues.size()];
358
359
360 try {
361 rotamerOptimization.getFractions(selectedResidues.toArray(new Residue[0]), 0, currentRotamers);
362 } catch (Exception e) {
363 logger.severe(" Error calculating fractions: " + e.getMessage());
364 return this;
365 }
366
367
368 boltzmannWeights[j] = rotamerOptimization.getTotalBoltzmann();
369
370
371 populationArray = rotamerOptimization.getFraction();
372 if (printBoltzmann) {
373 titrateBoltzmann = rotamerOptimization.getPopulationBoltzmann();
374 totalBoltzmann = rotamerOptimization.getTotalBoltzmann();
375 }
376
377
378 optimalRotamers = rotamerOptimization.getOptimumRotamers();
379 if (manyBodyOptions.getTitration()) {
380
381 titrationManyBody.excludeExcessAtoms(excludeAtoms, optimalRotamers, selectedResidues);
382 }
383
384
385 if (pKa) {
386 rotamerOptimization.getProtonationPopulations(selectedResidues.toArray(new Residue[0]));
387 }
388
389
390 String populationFilename = "populations.txt";
391 if (j > 0) {
392 populationFilename = "populations." + j + ".txt";
393 }
394 try (FileWriter fileWriter = new FileWriter(populationFilename)) {
395 int residueIndex = 0;
396 for (Residue residue : selectedResidues) {
397 fileWriter.write("\n");
398 double protonationBoltzmannSum = 0.0;
399
400 Rotamer[] rotamers = residue.getRotamers();
401 for (int rotIndex = 0; rotIndex < rotamers.length; rotIndex++) {
402 String rotPop = format("%.6f", populationArray[residueIndex][rotIndex]);
403 fileWriter.write(residue.getName() + residue.getResidueNumber() + "\t" +
404 rotamers[rotIndex].toString() + "\t" + rotPop + "\n");
405 if (pKa) {
406 switch (rotamers[rotIndex].getName()) {
407 case "HIS":
408 case "LYS":
409 case "GLH":
410 case "ASH":
411 case "CYS":
412 if (printBoltzmann) {
413 protonationBoltzmannSum += titrateBoltzmann[residueIndex][rotIndex];
414 }
415 break;
416 default:
417 break;
418 }
419 }
420
421 }
422
423 if (printBoltzmann) {
424 logger.info("\n Residue " + residue.getName() + residue.getResidueNumber()
425 + " Protonated Boltzmann: " + protonationBoltzmannSum);
426 }
427 residueIndex += 1;
428 }
429 logger.info("\n Total Boltzmann: " + totalBoltzmann);
430 logger.info("\n Successfully wrote to the populations file: " + populationFilename);
431 } catch (Exception e) {
432 logger.severe("Error writing populations file: " + e.getMessage());
433 }
434 }
435
436
437 System.setProperty("standardizeAtomNames", "false");
438 File modelFile = saveDirFile(activeAssembly.getFile());
439 PDBFilter pdbFilter = new PDBFilter(modelFile, activeAssembly, activeAssembly.getForceField(),
440 activeAssembly.getProperties());
441 if (manyBodyOptions.getTitration()) {
442 String remark = format("Titration pH: %6.3f", titrationPH);
443 if (!pdbFilter.writeFile(modelFile, false, excludeAtoms, true, true, new String[]{remark})) {
444 logger.info(format(" Save failed for %s", activeAssembly));
445 }
446 } else {
447 if (!pdbFilter.writeFile(modelFile, false, excludeAtoms, true, true)) {
448 logger.info(format(" Save failed for %s", activeAssembly));
449 }
450 }
451 if (mutatingResidue != -1) {
452 CompositeConfiguration properties = activeAssembly.getProperties();
453 String temp = properties.getString("temperature", "298.15");
454 double temperature = 298.15;
455 if (temp != null) {
456 temperature = parseDouble(temp);
457 }
458 double kT = temperature * Constants.R;
459
460 double dG = -kT * log(boltzmannWeights[1] / boltzmannWeights[0]);
461 logger.info(format("\n Mutation Free Energy Difference: %12.8f (kcal/mol)", dG));
462 }
463
464 return this;
465 }
466
467
468
469
470
471 public double[][] getPopulationArray() {
472 return populationArray;
473 }
474
475
476
477
478
479
480 public ForceFieldEnergy getPotential() {
481 return potentialEnergy;
482 }
483
484 }