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