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.potential.cli;
39
40 import ffx.potential.MolecularAssembly;
41 import ffx.potential.bonded.Atom;
42 import ffx.potential.utils.PotentialsFunctions;
43 import picocli.CommandLine.ArgGroup;
44 import picocli.CommandLine.Option;
45
46 import javax.annotation.Nullable;
47 import java.util.logging.Logger;
48 import java.util.regex.Pattern;
49
50 import static ffx.potential.cli.AtomSelectionOptions.actOnAtoms;
51 import static ffx.potential.cli.AtomSelectionOptions.actOnResidueAtoms;
52 import static java.lang.String.format;
53
54
55
56
57
58
59
60
61 public class AlchemicalOptions {
62
63
64
65
66 public static final Logger logger = Logger.getLogger(AlchemicalOptions.class.getName());
67
68
69
70
71 public static final Pattern rangeRegEx = Pattern.compile("([0-9]+)-?([0-9]+)?");
72
73
74
75
76 @ArgGroup(heading = "%n Alchemical Options%n", validate = false)
77 private final AlchemicalOptionGroup group = new AlchemicalOptionGroup();
78
79
80
81
82
83
84
85 public static void setUnchargedAtoms(MolecularAssembly assembly, String unchargedAtoms) {
86 actOnAtoms(assembly, unchargedAtoms, (Atom a, Boolean b) -> a.setElectrostatics(!b), "Uncharged");
87 }
88
89
90
91
92
93
94
95
96 public static void setAlchemicalAtoms(MolecularAssembly assembly, String alchemicalAtoms, String alchemicalResidues) {
97 actOnAtoms(assembly, alchemicalAtoms, Atom::setApplyLambda, "Alchemical");
98
99 actOnResidueAtoms(assembly, alchemicalResidues, Atom::setApplyLambda, "Alchemical");
100 }
101
102
103
104
105
106
107 public String getAlchemicalAtoms() {
108 return group.alchemicalAtoms;
109 }
110
111
112
113
114
115
116 public String getAlchemicalResidues() { return group.alchemicalResidues; }
117
118
119
120
121
122
123 public String getUnchargedAtoms() {
124 return group.unchargedAtoms;
125 }
126
127
128
129
130
131
132 public double getInitialLambda() {
133 return getInitialLambda(false);
134 }
135
136
137
138
139
140
141
142 public double getInitialLambda(boolean quiet) {
143 return getInitialLambda(1, 0, quiet);
144 }
145
146
147
148
149
150
151
152
153 public double getInitialLambda(int size, int rank) {
154 return getInitialLambda(size, rank, false);
155 }
156
157
158
159
160
161
162
163
164
165 public double getInitialLambda(int size, int rank, boolean quiet) {
166
167
168
169
170
171 double initialLambda = group.initialLambda;
172 if (initialLambda < 0.0 || initialLambda > 1.0) {
173 if (rank == 0 || size < 2) {
174 initialLambda = 0.0;
175 } else if (rank == size - 1) {
176 initialLambda = 1.0;
177 } else {
178 double dL = 1.0 / (size - 1);
179 initialLambda = dL * rank;
180 }
181
182 if (!quiet) {
183 logger.info(format(" Setting lambda to %5.3f.", initialLambda));
184 }
185 }
186 return initialLambda;
187 }
188
189
190
191
192
193
194 public boolean hasSoftcore() {
195 String alchemicalAtoms = getAlchemicalAtoms();
196 boolean atoms = (alchemicalAtoms != null
197 && !alchemicalAtoms.equalsIgnoreCase("NONE")
198 && !alchemicalAtoms.equalsIgnoreCase(""));
199 String alchemicalResidues = getAlchemicalResidues();
200 boolean residues = (alchemicalResidues != null
201 && !alchemicalResidues.equalsIgnoreCase("NONE")
202 && !alchemicalResidues.equalsIgnoreCase(""));
203 return (atoms || residues);
204 }
205
206
207
208
209
210
211 public void setFirstSystemAlchemistry(MolecularAssembly topology) {
212 setAlchemicalAtoms(topology, getAlchemicalAtoms(), getAlchemicalResidues());
213 }
214
215
216
217
218
219
220
221
222
223
224
225 public MolecularAssembly openFile(PotentialsFunctions potentialFunctions,
226 TopologyOptions topologyOptions, int threadsPer,
227 String toOpen, int topNum) {
228 potentialFunctions.openAll(toOpen, threadsPer);
229 MolecularAssembly molecularAssembly = potentialFunctions.getActiveAssembly();
230 return processFile(topologyOptions, molecularAssembly, topNum);
231 }
232
233
234
235
236
237
238
239
240
241 public MolecularAssembly processFile(@Nullable TopologyOptions topologyOptions,
242 MolecularAssembly molecularAssembly, int topNum) {
243
244 int remainder = (topNum % 2) + 1;
245 switch (remainder) {
246 case 1 -> {
247 setFirstSystemAlchemistry(molecularAssembly);
248 setFirstSystemUnchargedAtoms(molecularAssembly);
249 }
250 case 2 -> {
251 if (topologyOptions == null) {
252 throw new IllegalArgumentException(
253 " For >= 2 systems, topologyOptions must not be empty!");
254 }
255 topologyOptions.setSecondSystemAlchemistry(molecularAssembly);
256 topologyOptions.setSecondSystemUnchargedAtoms(molecularAssembly);
257 }
258 }
259 return molecularAssembly;
260 }
261
262
263
264
265
266
267 public void setFirstSystemUnchargedAtoms(MolecularAssembly topology) {
268 setUnchargedAtoms(topology, getUnchargedAtoms());
269 }
270
271
272
273
274 public void setAlchemicalProperties() {
275 if (hasSoftcore()) {
276 System.setProperty("lambdaterm", "true");
277 }
278 }
279
280
281
282
283 private static class AlchemicalOptionGroup {
284
285
286
287
288 @Option(
289 names = {"-l", "--lambda"},
290 paramLabel = "-1",
291 description = "Initial lambda value.")
292 double initialLambda = -1.0;
293
294
295
296
297 @Option(
298 names = {"--ac", "--alchemicalAtoms"},
299 paramLabel = "<selection>",
300 defaultValue = "",
301 description = "Specify alchemical atoms [ALL, NONE, Range(s): 1-3,6-N].")
302 String alchemicalAtoms = "";
303
304
305
306
307 @Option(
308 names = {"--acRes", "--alchemicalResidues"},
309 paramLabel = "<selection>",
310 defaultValue = "",
311 description = "Specify alchemical residues by chain and residue number [A4,B21]")
312 String alchemicalResidues = "";
313
314
315
316
317
318 @Option(
319 names = {"--uc", "--unchargedAtoms"},
320 paramLabel = "<selection>",
321 defaultValue = "",
322 description = "Specify atoms without electrostatics [ALL, NONE, Range(s): 1-3,6-N].")
323 String unchargedAtoms = "";
324 }
325 }