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 ffx.numerics.Potential;
41 import ffx.potential.MolecularAssembly;
42 import ffx.potential.cli.AtomSelectionOptions;
43 import ffx.potential.cli.PotentialCommand;
44 import ffx.utilities.FFXBinding;
45 import ffx.xray.DiffractionData;
46 import ffx.xray.refine.RefinedParameter;
47 import ffx.xray.refine.RefinementMode;
48 import ffx.xray.refine.RefinementModel;
49 import ffx.xray.XRayEnergy;
50 import ffx.xray.cli.XrayOptions;
51 import org.apache.commons.configuration2.CompositeConfiguration;
52 import picocli.CommandLine.Command;
53 import picocli.CommandLine.Mixin;
54 import picocli.CommandLine.Option;
55 import picocli.CommandLine.Parameters;
56
57 import java.util.ArrayList;
58 import java.util.List;
59
60 import static ffx.utilities.StringUtils.parseAtomRanges;
61 import static java.lang.String.format;
62 import static org.apache.commons.math3.util.FastMath.sqrt;
63
64
65
66
67
68
69
70
71 @Command(description = " Test the potential energy gradient.", name = "test.Gradient")
72 public class Gradient extends PotentialCommand {
73
74 @Mixin
75 AtomSelectionOptions atomSelectionOptions;
76
77 @Mixin
78 XrayOptions xrayOptions;
79
80
81
82
83 @Option(names = {"-d", "--dx"}, defaultValue = "1.0e-5", paramLabel = "1.0e-5",
84 description = "Finite-difference step size.")
85 public double dx = 1.0e-5;
86
87
88
89
90 @Option(names = {"--tol", "--tolerance"}, defaultValue = "1.0e-3",
91 paramLabel = "1.0e-3 kcal/mol/param", description = "Gradient error tolerance.")
92 public double tolerance = 1.0e-3;
93
94
95
96
97 @Option(names = {"--params", "--gradientParams"}, paramLabel = "ALL", defaultValue = "ALL",
98 description = "Ranges of degrees of freedom to test [ALL, NONE, Range(s): 1-3,6-N].")
99 public String gradientParams = "ALL";
100
101
102
103
104
105 @Parameters(arity = "1..*", paramLabel = "files", description = "PDB and Diffraction input files.")
106 private List<String> filenames;
107
108 private MolecularAssembly[] molecularAssemblies;
109 private DiffractionData diffractionData;
110 private XRayEnergy xrayEnergy;
111 public int nFailures = 0;
112
113
114
115
116 public Gradient() {
117 super();
118 }
119
120
121
122
123
124
125 public Gradient(FFXBinding binding) {
126 super(binding);
127 }
128
129
130
131
132
133
134 public Gradient(String[] args) {
135 super(args);
136 }
137
138
139
140
141 @Override
142 public Gradient run() {
143
144 if (!init()) {
145 return this;
146 }
147
148 xrayOptions.init();
149
150 String filename;
151 if (filenames != null && !filenames.isEmpty()) {
152
153 molecularAssemblies = potentialFunctions.openAll(filenames.getFirst());
154 activeAssembly = molecularAssemblies[0];
155 } else if (activeAssembly == null) {
156 logger.info(helpString());
157 return this;
158 } else {
159 molecularAssemblies = new MolecularAssembly[]{activeAssembly};
160 }
161
162
163 filename = activeAssembly.getFile().getAbsolutePath();
164
165
166 for (MolecularAssembly assembly : molecularAssemblies) {
167 atomSelectionOptions.setActiveAtoms(assembly);
168 }
169
170
171 CompositeConfiguration properties = activeAssembly.getProperties();
172 xrayOptions.setProperties(parseResult, properties);
173
174
175 diffractionData = xrayOptions.getDiffractionData(filenames, molecularAssemblies, properties);
176 diffractionData.scaleBulkFit();
177 diffractionData.printStats();
178 xrayEnergy = new XRayEnergy(diffractionData);
179
180 RefinementModel refinementModel = diffractionData.getRefinementModel();
181 RefinementMode refinementMode = refinementModel.getRefinementMode();
182
183 logger.info("\n Testing the gradient of " + filename);
184 logger.info(refinementMode.toString());
185 logger.info(refinementModel.toString());
186
187 int n = refinementModel.getNumParameters();
188 double[] x = new double[n];
189 double[] g = new double[n];
190
191 refinementModel.getParameters(x);
192 xrayEnergy.energyAndGradient(x, g);
193
194 List<RefinedParameter> refinedParameters = refinementModel.getRefinedParameters();
195 int numParameters = refinedParameters.size();
196
197 List<Integer> parametersToTest;
198 if (gradientParams.equalsIgnoreCase("NONE")) {
199 logger.info(" The gradient of no parameters will be evaluated.");
200 return this;
201 } else if (gradientParams.equalsIgnoreCase("ALL")) {
202 logger.info(" Checking the gradient for all parameters.\n");
203 parametersToTest = new ArrayList<>();
204 for (int i = 0; i < numParameters; i++) {
205 parametersToTest.add(i);
206 }
207 } else {
208 parametersToTest = parseAtomRanges(" Parameters", gradientParams, numParameters);
209 logger.info(" Checking the gradient for parameters in the range: " + gradientParams + "\n");
210 }
211
212 for (Integer parameter : parametersToTest) {
213 RefinedParameter refinedParameter = refinedParameters.get(parameter);
214 int index = refinedParameter.getIndex();
215 int numParams = refinedParameter.getNumberOfParameters();
216 double[] grad = new double[numParams];
217 double error = 0.0;
218
219 for (int i = 0; i < numParams; i++) {
220 double orig = x[index + i];
221 x[index + i] = orig + dx;
222 double eplus = xrayEnergy.energy(x);
223 x[index + i] = orig - dx;
224 double eminus = xrayEnergy.energy(x);
225 x[index + i] = orig;
226 grad[i] = (eplus - eminus) / (2.0 * dx);
227 double diff = grad[i] - g[index + i];
228 error += diff * diff;
229 }
230 error = sqrt(error);
231 if (numParams == 1) {
232 logger.info(format("\n Refinement parameter %d with index %d", parameter + 1, index));
233 } else {
234 logger.info(format("\n Refinement parameter %d with indices %d - %s",
235 parameter + 1, index, index + numParams - 1));
236 }
237 logger.info(format(" %s", refinedParameter));
238 if (error > tolerance) {
239 logger.info(format(" Failed: %10.6f", error));
240 } else {
241 logger.info(format(" Passed: %10.6f", error));
242 }
243 StringBuilder numeric = new StringBuilder(" Numeric: ");
244 StringBuilder analytic = new StringBuilder(" Analytic: ");
245 for (int i = 0; i < numParams; i++) {
246 numeric.append(format(" %10.6f", grad[i]));
247 analytic.append(format(" %10.6f", g[index + i]));
248 }
249 logger.info(numeric.toString());
250 logger.info(analytic.toString());
251 }
252 return this;
253 }
254
255 @Override
256 public List<Potential> getPotentials() {
257 List<Potential> potentials = getPotentialsFromAssemblies(molecularAssemblies);
258 if (xrayEnergy != null) {
259 potentials.add(xrayEnergy);
260 }
261 return potentials;
262 }
263
264 }