1 package ffx.xray.refine;
2
3 import ffx.potential.bonded.Atom;
4
5 import javax.annotation.Nullable;
6
7 import static java.lang.String.format;
8
9 /**
10 * Represents a set of coordinates that can be refined during a refinement process.
11 * This class encapsulates an {@link Atom} object and any additional atoms that are constrained
12 * to follow its coordinates.
13 */
14 public class RefinedCoordinates extends RefinedParameter {
15
16 /**
17 * Optimization scale factor for atomic coordinates.
18 */
19 private static final double COORDINATE_SCALE = 12.0;
20
21 /**
22 * Constructs a new RefinableCoordinates instance for the specified atom.
23 * This instance represents a set of coordinates that can be refined,
24 * with the given atom serving as the primary reference point during the refinement process.
25 * Changes to the primary atom's coordinates will also affect any constrained atoms
26 * associated with this instance.
27 *
28 * @param atom the {@link Atom} whose coordinates are being refined; this atom serves
29 * as the primary reference point for the refinement process.
30 */
31 public RefinedCoordinates(Atom atom) {
32 super(atom);
33 }
34
35 @Override
36 public void addConstrainedAtom(Atom atom) {
37 // Apply the constraint.
38 atom.setActive(true);
39 atom.setXYZ(getCoordinates(null));
40 constrainedAtoms.add(atom);
41 }
42
43 @Override
44 public void addConstrainedAtomThatScatters(Atom atom) {
45 // Apply the constraint.
46 atom.setActive(true);
47 atom.setXYZ(getCoordinates(null));
48 constrainedAtomsThatScatter.add(atom);
49 }
50
51 /**
52 * Retrieves the XYZ coordinates of the primary atom, updating the provided array or
53 * returning a new array if the input array is null.
54 *
55 * @param xyz an array of doubles where the XYZ coordinates should be stored. If the input
56 * array is null, a new double array will be created and returned.
57 * @return an array of doubles containing the XYZ coordinates of the primary atom. If the input
58 * array is not null, it will be updated with the coordinates and returned.
59 */
60 public double[] getCoordinates(@Nullable double[] xyz) {
61 return atom.getXYZ(xyz);
62 }
63
64 /**
65 * Sets the coordinates of the primary atom and updates the coordinates of
66 * all constrained atoms to match the new values.
67 *
68 * @param xyz an array of doubles representing the XYZ coordinates to be set for the primary atom and its constrained atoms
69 */
70 public void setCoordinates(double[] xyz) {
71 atom.setXYZ(xyz);
72 for (Atom a : constrainedAtoms) {
73 a.setXYZ(xyz);
74 }
75 for (Atom a : constrainedAtomsThatScatter) {
76 a.setXYZ(xyz);
77 }
78 }
79
80 @Override
81 public int getNumberOfParameters() {
82 return 3;
83 }
84
85 /**
86 * Updates a subset of the parameter array with the XYZ coordinates of the primary atom.
87 *
88 * @param parameters an array of doubles where the XYZ coordinates of the primary atom
89 * will be stored. The x, y, and z values are assigned sequentially
90 * starting at the current index value.
91 */
92 @Override
93 public void getParameters(double[] parameters) {
94 parameters[index] = atom.getX();
95 parameters[index + 1] = atom.getY();
96 parameters[index + 2] = atom.getZ();
97 }
98
99 /**
100 * Updates the XYZ coordinates of the primary atom and all associated constrained atoms
101 * using the provided parameters array.
102 *
103 * @param parameters an array of doubles containing all refined parameters.
104 */
105 @Override
106 public void setParameters(double[] parameters) {
107 double[] xyz = new double[3];
108 xyz[0] = parameters[index];
109 xyz[1] = parameters[index + 1];
110 xyz[2] = parameters[index + 2];
111 setCoordinates(xyz);
112 }
113
114 /**
115 * Updates a subset of the parameter array with the XYZ velocity components of the primary atom.
116 *
117 * @param velocity an array of doubles where the XYZ velocity components of the primary atom
118 * will be stored. The x, y, and z components are assigned sequentially
119 * starting at the current index value.
120 */
121 @Override
122 public void getVelocity(double[] velocity) {
123 double[] v = new double[3];
124 atom.getVelocity(v);
125 velocity[index] = v[0];
126 velocity[index + 1] = v[1];
127 velocity[index + 2] = v[2];
128 }
129
130 /**
131 * Sets the velocity components of the primary atom using the provided parameters array.
132 * The velocity is updated with values sequentially extracted from the array at the current index.
133 *
134 * @param velocity an array of doubles containing velocity data. The x, y, and z components
135 * of the velocity are taken sequentially starting at the current index.
136 */
137 @Override
138 public void setVelocity(double[] velocity) {
139 double[] v = new double[3];
140 v[0] = velocity[index];
141 v[1] = velocity[index + 1];
142 v[2] = velocity[index + 2];
143 atom.setVelocity(v);
144 }
145
146 /**
147 * Updates a subset of the parameter array with the XYZ acceleration components of the primary atom.
148 *
149 * @param acceleration an array of doubles where the XYZ acceleration components of the primary atom
150 * will be stored. The x, y, and z components are assigned sequentially
151 * starting at the current index value.
152 */
153 @Override
154 public void getAcceleration(double[] acceleration) {
155 double[] a = new double[3];
156 atom.getAcceleration(a);
157 acceleration[index] = a[0];
158 acceleration[index + 1] = a[1];
159 acceleration[index + 2] = a[2];
160 }
161
162 /**
163 * Sets the acceleration components for the primary atom.
164 * The method extracts the x, y, and z components of acceleration
165 * from the provided array starting at a specified index and
166 * assigns them to the primary atom.
167 *
168 * @param acceleration an array of doubles containing the XYZ acceleration
169 * components. The x, y, and z components are retrieved
170 * sequentially from the array starting at the current index.
171 */
172 @Override
173 public void setAcceleration(double[] acceleration) {
174 double[] a = new double[3];
175 a[0] = acceleration[index];
176 a[1] = acceleration[index + 1];
177 a[2] = acceleration[index + 2];
178 atom.setAcceleration(a);
179 }
180
181 /**
182 * Updates a subset of the parameter array with the XYZ previous acceleration components of the primary atom.
183 *
184 * @param previousAcceleration an array of doubles where the XYZ previous acceleration components
185 * of the primary atom will be stored. The x, y, and z components
186 * are assigned sequentially starting at the current index value.
187 */
188 @Override
189 public void getPreviousAcceleration(double[] previousAcceleration) {
190 double[] a = new double[3];
191 atom.getPreviousAcceleration(a);
192 previousAcceleration[index] = a[0];
193 previousAcceleration[index + 1] = a[1];
194 previousAcceleration[index + 2] = a[2];
195 }
196
197 /**
198 * Sets the previous acceleration components for the primary atom.
199 * The method extracts the x, y, and z components of previous acceleration
200 * from the provided array starting at a specified index and
201 * assigns them to the primary atom.
202 *
203 * @param previousAcceleration an array of doubles containing the XYZ previous acceleration
204 * components. The x, y, and z components are retrieved
205 * sequentially from the array starting at the current index.
206 */
207 @Override
208 public void setPreviousAcceleration(double[] previousAcceleration) {
209 double[] a = new double[3];
210 a[0] = previousAcceleration[index];
211 a[1] = previousAcceleration[index + 1];
212 a[2] = previousAcceleration[index + 2];
213 atom.setPreviousAcceleration(a);
214 }
215
216 /**
217 * Sets the scaling factors for optimization parameters at specific indices.
218 * This method updates a portion of the provided array with predefined scaling values.
219 *
220 * @param optimizationScaling an array of doubles representing scaling factors for optimization.
221 * The method modifies specific indices within this array.
222 */
223 @Override
224 public void setOptimizationScaling(double[] optimizationScaling) {
225 optimizationScaling[index] = COORDINATE_SCALE;
226 optimizationScaling[index + 1] = COORDINATE_SCALE;
227 optimizationScaling[index + 2] = COORDINATE_SCALE;
228 }
229
230 /**
231 * Initialize the coordinate gradient to zero.
232 */
233 @Override
234 public void zeroGradient() {
235 atom.setXYZGradient(0.0, 0.0, 0.0);
236 atom.setLambdaXYZGradient(0.0, 0.0, 0.0);
237 for (Atom a : constrainedAtoms) {
238 a.setXYZGradient(0.0, 0.0, 0.0);
239 a.setLambdaXYZGradient(0.0, 0.0, 0.0);
240 }
241 for (Atom a : constrainedAtomsThatScatter) {
242 a.setXYZGradient(0.0, 0.0, 0.0);
243 a.setLambdaXYZGradient(0.0, 0.0, 0.0);
244 }
245 }
246
247 /**
248 * Updates the gradient array with the contributions from the primary atom
249 * and any constrained atoms that scatter. The XYZ gradient values are
250 * retrieved for each relevant atom and added to the corresponding indices
251 * of the provided gradient array.
252 *
253 * @param gradient an array of doubles where the gradient contributions
254 * will be accumulated.
255 */
256 @Override
257 public void getGradient(double[] gradient) {
258 double[] xyz = new double[3];
259 atom.getXYZGradient(xyz);
260 gradient[index] = xyz[0];
261 gradient[index + 1] = xyz[1];
262 gradient[index + 2] = xyz[2];
263 for (Atom a : constrainedAtomsThatScatter) {
264 a.getXYZGradient(xyz);
265 gradient[index] += xyz[0];
266 gradient[index + 1] += xyz[1];
267 gradient[index + 2] += xyz[2];
268 }
269 }
270
271 /**
272 * Updates the provided array with the atomic mass of the primary atom.
273 *
274 * @param mass an array of doubles where the atomic mass will be stored.
275 * @param defaultMass the default mass is ignored for coordinates.
276 */
277 @Override
278 public void getMass(double[] mass, double defaultMass) {
279 double m = atom.getMass();
280 mass[index] = m;
281 mass[index + 1] = m;
282 mass[index + 2] = m;
283 }
284
285 @Override
286 public String toString() {
287 double[] xyz = new double[3];
288 atom.getXYZ(xyz);
289 StringBuilder sb = new StringBuilder(" Atomic Coordinates: ");
290 for (int i = 0; i < 3; i++) {
291 sb.append(format(" %10.6f", xyz[i]));
292 }
293 return sb.toString();
294 }
295 }