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.xray.commands.test;
39
40 import edu.rit.pj.Comm;
41 import ffx.algorithms.cli.AlgorithmsCommand;
42 import ffx.algorithms.cli.DynamicsOptions;
43 import ffx.algorithms.cli.LambdaParticleOptions;
44 import ffx.algorithms.dynamics.MolecularDynamics;
45 import ffx.algorithms.dynamics.integrators.IntegratorEnum;
46 import ffx.algorithms.dynamics.thermostats.ThermostatEnum;
47 import ffx.algorithms.thermodynamics.HistogramData;
48 import ffx.algorithms.thermodynamics.LambdaData;
49 import ffx.algorithms.thermodynamics.OrthogonalSpaceTempering;
50 import ffx.algorithms.thermodynamics.OrthogonalSpaceTempering.OptimizationParameters;
51 import ffx.numerics.Potential;
52 import ffx.potential.ForceFieldEnergy;
53 import ffx.potential.MolecularAssembly;
54 import ffx.potential.bonded.Atom;
55 import ffx.potential.bonded.MSNode;
56 import ffx.realspace.cli.RealSpaceOptions;
57 import ffx.utilities.FFXBinding;
58 import ffx.xray.DiffractionData;
59 import ffx.xray.RefinementEnergy;
60 import ffx.xray.RefinementMinimize.RefinementMode;
61 import ffx.xray.cli.XrayOptions;
62 import org.apache.commons.configuration2.CompositeConfiguration;
63 import org.apache.commons.io.FilenameUtils;
64 import picocli.CommandLine.Command;
65 import picocli.CommandLine.Mixin;
66 import picocli.CommandLine.Option;
67 import picocli.CommandLine.Parameters;
68
69 import java.io.File;
70 import java.util.Collections;
71 import java.util.List;
72 import java.util.logging.Logger;
73
74
75
76
77
78
79
80
81 @Command(description = " Simulated annealing on an X-ray target.", name = "xray.test.Alchemical")
82 public class Alchemical extends AlgorithmsCommand {
83
84 @Mixin
85 private DynamicsOptions dynamicsOptions;
86
87 @Mixin
88 private LambdaParticleOptions lambdaParticleOptions;
89
90 @Mixin
91 private XrayOptions xrayOptions;
92
93 private static final Logger logger = Logger.getLogger(RealSpaceOptions.class.getName());
94
95
96
97
98 @Option(names = {"-I", "--onlyIons"}, paramLabel = "false",
99 description = "Set to only optimize ions (of a single type).")
100 private boolean onlyIons = false;
101
102
103
104
105 @Option(names = {"--itype", "--iontype"}, paramLabel = "null",
106 description = "Specify which ion to run optimization on. If none is specified, default behavior chooses the first ion found in the PDB file.")
107 private String[] ionType = null;
108
109
110
111
112 @Option(names = {"-N", "--neutralize"}, paramLabel = "false",
113 description = "Neutralize the crystal's charge by adding more of the selected ion")
114 private boolean neutralize = false;
115
116
117
118
119 @Option(names = {"-W", "--onlyWater"}, paramLabel = "false",
120 description = "Set to only optimize water.")
121 private boolean onlyWater = false;
122
123
124
125
126 private RefinementMode refinementMode = RefinementMode.COORDINATES;
127
128
129
130
131 @Parameters(arity = "1..*", paramLabel = "files", description = "PDB and Real Space input files.")
132 private List<String> filenames;
133
134
135 private double lambda = 1.0;
136
137
138 private ThermostatEnum thermostat = ThermostatEnum.ADIABATIC;
139
140
141 private IntegratorEnum integrator = IntegratorEnum.STOCHASTIC;
142
143
144 private String fileType = "PDB";
145
146
147 private boolean runOST = true;
148
149
150 private boolean initVelocities = true;
151
152 private OrthogonalSpaceTempering orthogonalSpaceTempering;
153
154
155
156
157 public Alchemical() {
158 super();
159 }
160
161
162
163
164
165 public Alchemical(String[] args) {
166 super(args);
167 }
168
169
170
171
172
173 public Alchemical(FFXBinding binding) {
174 super(binding);
175 }
176
177 @Override
178 public Alchemical run() {
179
180 if (!init()) {
181 return this;
182 }
183 dynamicsOptions.init();
184 xrayOptions.init();
185 System.setProperty("lambdaterm", "true");
186
187 String modelfilename;
188 MolecularAssembly[] assemblies;
189 if (filenames != null && filenames.size() > 0) {
190 assemblies = algorithmFunctions.openAll(filenames.get(0));
191 activeAssembly = assemblies[0];
192 modelfilename = filenames.get(0);
193 } else if (activeAssembly == null) {
194 logger.info(helpString());
195 return this;
196 } else {
197 modelfilename = activeAssembly.getFile().getAbsolutePath();
198 assemblies = new MolecularAssembly[]{activeAssembly};
199 }
200
201 logger.info("\n Running Alchemical Changes on " + modelfilename);
202
203 File structureFile = new File(FilenameUtils.normalize(modelfilename));
204 structureFile = new File(structureFile.getAbsolutePath());
205 String baseFilename = FilenameUtils.removeExtension(structureFile.getName());
206 File histogramRestart = new File(baseFilename + ".his");
207 File lambdaRestart = new File(baseFilename + ".lam");
208 File dyn = new File(baseFilename + ".dyn");
209
210 Comm world = Comm.world();
211 int size = world.size();
212 int rank = 0;
213 double[] energyArray = new double[world.size()];
214 for (int i = 0; i < world.size(); i++) {
215 energyArray[i] = Double.MAX_VALUE;
216 }
217
218
219 if (size > 1) {
220 rank = world.rank();
221 File rankDirectory = new File(
222 structureFile.getParent() + File.separator + Integer.toString(rank));
223 if (!rankDirectory.exists()) {
224 rankDirectory.mkdir();
225 }
226 lambdaRestart = new File(rankDirectory.getPath() + File.separator + baseFilename + ".lam");
227 dyn = new File(rankDirectory.getPath() + File.separator + baseFilename + ".dyn");
228 structureFile = new File(rankDirectory.getPath() + File.separator + structureFile.getName());
229 }
230 if (!dyn.exists()) {
231 dyn = null;
232 }
233
234
235 Atom[] atoms = activeAssembly.getAtomArray();
236
237
238 ForceFieldEnergy forceFieldEnergy = activeAssembly.getPotentialEnergy();
239 forceFieldEnergy.setPrintOnFailure(false, false);
240
241
242
243
244
245 for (int i = 0; i <= atoms.length; i++) {
246 Atom ai = atoms[i - 1];
247 ai.setUse(true);
248 ai.setActive(false);
249 ai.setApplyLambda(false);
250 }
251
252 double crystalCharge = activeAssembly.getCharge(true);
253 logger.info(" Overall crystal charge: " + crystalCharge);
254 List<MSNode> ions = assemblies[0].getIons();
255 List<MSNode> water = assemblies[0].getWater();
256
257
258 if (!onlyWater) {
259 logger.info("Doing ions.");
260 if (ions == null || ions.size() == 0) {
261 logger.info("\n Please add an ion to the PDB file to scan with.");
262 return this;
263 }
264 for (MSNode msNode : ions) {
265
266 boolean ionSelected = false;
267 if (ionType != null) {
268 logger.info("Selecting ion.");
269 List<Atom> atomList = msNode.getAtomList();
270 if (!atomList.isEmpty()) {
271 String atomName = atomList.get(0).getName();
272 for (String targetIonType : ionType) {
273 if (atomName.equals(targetIonType)) {
274 ionSelected = true;
275 break;
276 }
277 }
278 }
279 }
280
281 if (ionSelected) {
282 logger.info("Ion has been selected.");
283 for (Atom atom : msNode.getAtomList()) {
284 System.out.println("Activating ions");
285 atom.setUse(true);
286 atom.setActive(true);
287 atom.setApplyLambda(true);
288 logger.info(" Alchemical atom: " + atom.toString());
289 }
290 } else {
291 logger.info("Ion has not been selected.");
292 if (neutralize) {
293 logger.info("Neutralizing crystal.");
294 double ionCharge = 0;
295 for (Atom atom : msNode.getAtomList()) {
296 ionCharge += atom.getMultipoleType().getCharge();
297 }
298 logger.info("Ion charge is: " + Double.toString(ionCharge));
299 int numIons = (int) (-1 * (Math.ceil(crystalCharge / ionCharge)));
300 if (numIons > 0) {
301 List<Atom> atomList = msNode.getAtomList();
302 String atomName = atomList.isEmpty() ? "" : atomList.get(0).getName();
303 logger.info(numIons + " " + atomName
304 + " ions needed to neutralize the crystal.");
305 ionType = new String[]{atomName};
306 for (Atom atom : msNode.getAtomList()) {
307 atom.setUse(true);
308 atom.setActive(true);
309 atom.setApplyLambda(true);
310 logger.info(" Alchemical atom: " + atom.toString());
311 }
312 }
313 } else {
314 List<Atom> atomList = msNode.getAtomList();
315 String atomName = atomList.isEmpty() ? "" : atomList.get(0).getName();
316 ionType = new String[]{atomName};
317 for (Atom atom : msNode.getAtomList()) {
318 atom.setUse(true);
319 atom.setActive(true);
320 atom.setApplyLambda(true);
321 logger.info(" Alchemical atom: " + atom.toString());
322 }
323 }
324 }
325 }
326 }
327
328
329 if (onlyWater) {
330 for (MSNode msNode : water) {
331 for (Atom atom : msNode.getAtomList()) {
332
333 atom.setUse(true);
334 atom.setActive(true);
335 atom.setApplyLambda(true);
336 logger.info(" Water atom: " + atom.toString());
337 }
338 }
339 }
340
341
342 CompositeConfiguration properties = assemblies[0].getProperties();
343 xrayOptions.setProperties(parseResult, properties);
344
345 DiffractionData diffractionData = xrayOptions.getDiffractionData(filenames, assemblies, properties);
346 RefinementEnergy refinementEnergy = xrayOptions.toXrayEnergy(diffractionData);
347
348 double[] x = new double[refinementEnergy.getNumberOfVariables()];
349 x = refinementEnergy.getCoordinates(x);
350 refinementEnergy.energy(x, true);
351
352 refinementEnergy.setLambda(lambda);
353
354 CompositeConfiguration props = assemblies[0].getProperties();
355
356 HistogramData histogramData = HistogramData.readHistogram(histogramRestart);
357 if (lambdaRestart == null) {
358 String filename = histogramRestart.toString().replaceFirst("\\.his$", ".lam");
359 lambdaRestart = new File(filename);
360 }
361 LambdaData lambdaData = LambdaData.readLambdaData(lambdaRestart);
362
363 OrthogonalSpaceTempering orthogonalSpaceTempering = new OrthogonalSpaceTempering(refinementEnergy,
364 refinementEnergy, histogramData, lambdaData, props, dynamicsOptions, lambdaParticleOptions, algorithmListener);
365
366 orthogonalSpaceTempering.setLambda(lambda);
367 orthogonalSpaceTempering.getOptimizationParameters().setOptimization(true, activeAssembly);
368
369 MolecularDynamics molDyn = new MolecularDynamics(assemblies[0],
370 orthogonalSpaceTempering, algorithmListener, thermostat, integrator);
371
372 algorithmFunctions.energy(assemblies[0]);
373
374 molDyn.dynamic(dynamicsOptions.getSteps(), dynamicsOptions.getDt(), dynamicsOptions.getReport(),
375 dynamicsOptions.getWrite(), dynamicsOptions.getTemperature(), true,
376 fileType, dynamicsOptions.getWrite(), dyn);
377
378 logger.info(" Searching for low energy coordinates");
379 OptimizationParameters opt = orthogonalSpaceTempering.getOptimizationParameters();
380 double[] lowEnergyCoordinates = opt.getOptimumCoordinates();
381 double currentOSTOptimum = opt.getOptimumEnergy();
382 if (lowEnergyCoordinates != null) {
383 forceFieldEnergy.setCoordinates(lowEnergyCoordinates);
384 } else {
385 logger.info(" OST stage did not succeed in finding a minimum.");
386 }
387
388 return this;
389 }
390
391 @Override
392 public List<Potential> getPotentials() {
393 return orthogonalSpaceTempering == null ? Collections.emptyList() :
394 Collections.singletonList(orthogonalSpaceTempering);
395 }
396 }