View Javadoc
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 }