View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2025.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.xray;
39  
40  import ffx.potential.MolecularAssembly;
41  import ffx.potential.bonded.Atom;
42  import ffx.potential.bonded.MSNode;
43  import ffx.potential.bonded.Molecule;
44  import ffx.potential.bonded.Residue;
45  
46  import java.util.ArrayList;
47  import java.util.List;
48  import java.util.logging.Level;
49  import java.util.logging.Logger;
50  
51  import static java.lang.String.format;
52  
53  /**
54   * RefinementModel class.
55   *
56   * @author Timothy D. Fenn
57   * @since 1.0
58   */
59  public class RefinementModel {
60  
61    private static final Logger logger = Logger.getLogger(RefinementModel.class.getName());
62    /** An atom list. */
63    private final List<Atom> totalAtomList;
64    /** An atom array. */
65    private final Atom[] totalAtomArray;
66    /** An atom list. */
67    private final List<Atom> activeAtomList;
68    /** An atom array. */
69    private final Atom[] activeAtomArray;
70    /** Map between atom in different molecular assemblies. */
71    private final List<List<Integer>> xIndex;
72  
73    private final List<List<Residue>> altResidues;
74    private final List<List<Molecule>> altMolecules;
75  
76    /**
77     * Constructor for RefinementModel.
78     *
79     * @param molecularAssemblies an array of {@link ffx.potential.MolecularAssembly} objects.
80     */
81    public RefinementModel(MolecularAssembly[] molecularAssemblies) {
82      this(molecularAssemblies, false);
83    }
84  
85    /**
86     * Constructor for RefinementModel.
87     *
88     * @param molecularAssemblies an array of {@link ffx.potential.MolecularAssembly} objects.
89     * @param refineMolOcc a boolean.
90     */
91    @SuppressWarnings("unchecked")
92    public RefinementModel(MolecularAssembly[] molecularAssemblies, boolean refineMolOcc) {
93      List<Atom> atomList;
94  
95      // Build alternate conformer list for occupancy refinement (if necessary).
96      altResidues = new ArrayList<>();
97      altMolecules = new ArrayList<>();
98      List<MSNode> nodeList0 = molecularAssemblies[0].getNodeList();
99      List<Residue> tempResidues = null;
100     List<Molecule> tempMolecules = null;
101     boolean alternateConformer;
102 
103     // By residue/molecule.
104     for (int i = 0; i < nodeList0.size(); i++) {
105       alternateConformer = false;
106       MSNode iNode = nodeList0.get(i);
107 
108       // First set up alternate residue restraint list.
109       for (Atom a : iNode.getAtomList()) {
110         if (!a.getUse()) {
111           continue;
112         }
113         if (a.getAltLoc() == null) {
114           logger.severe(
115               format(
116                   " Atom %s has a null alternate location. Likely cause: attempting X-ray refinement from a .xyz file",
117                   a));
118         }
119         if (!a.getAltLoc().equals(' ') || a.getOccupancy() < 1.0) {
120           if (iNode instanceof Residue) {
121             tempResidues = new ArrayList<>();
122             tempResidues.add((Residue) iNode);
123             altResidues.add(tempResidues);
124             alternateConformer = true;
125             break;
126           } else if (iNode instanceof Molecule) {
127             if (refineMolOcc) {
128               tempMolecules = new ArrayList<>();
129               tempMolecules.add((Molecule) iNode);
130               altMolecules.add(tempMolecules);
131             }
132             alternateConformer = true;
133             break;
134           }
135         }
136       }
137       if (alternateConformer) {
138         for (int j = 1; j < molecularAssemblies.length; j++) {
139           List<MSNode> nlist = molecularAssemblies[j].getNodeList();
140           MSNode node = nlist.get(i);
141           for (Atom a : node.getAtomList()) {
142             if (!a.getUse()) {
143               continue;
144             }
145             if (!a.getAltLoc().equals(' ') && !a.getAltLoc().equals('A')) {
146               if (node instanceof Residue) {
147                 if (tempResidues != null) {
148                   tempResidues.add((Residue) node);
149                 }
150                 break;
151               } else if (node instanceof Molecule) {
152                 if (tempMolecules != null) {
153                   tempMolecules.add((Molecule) node);
154                 }
155                 break;
156               }
157             }
158           }
159         }
160       }
161     }
162 
163     // For mapping between atoms between different molecular assemblies.
164     // xIndex = new List[molecularAssemblies.length];
165     xIndex = new ArrayList<>(molecularAssemblies.length);
166     for (int i = 0; i < molecularAssemblies.length; i++) {
167       // xIndex[i] = new ArrayList<>();
168       xIndex.add(new ArrayList<>());
169     }
170 
171     // Create the atomList to be used for SF calculations.
172     totalAtomList = new ArrayList<>();
173 
174     // Create the activeAtomList (i.e. atoms that can move).
175     activeAtomList = new ArrayList<>();
176 
177     // Root list.
178     atomList = molecularAssemblies[0].getAtomList();
179     int index = 0;
180     for (Atom a : atomList) {
181       if (!a.getUse()) {
182         continue;
183       }
184       totalAtomList.add(a);
185 //      a.setFormFactorIndex(index);
186 //      xIndex[0].add(index);
187 //      index++;
188       if (a.isActive()) {
189         activeAtomList.add(a);
190         a.setFormFactorIndex(index);
191         // xIndex[0].add(index);
192         xIndex.get(0).add(index);
193         index++;
194       }
195     }
196 
197     // Now add cross-references to root and any alternate atoms not in root
198     for (int i = 1; i < molecularAssemblies.length; i++) {
199       atomList = molecularAssemblies[i].getAtomList();
200       for (Atom a : atomList) {
201         if (!a.getUse()) {
202           continue;
203         }
204         Atom root = molecularAssemblies[0].findAtom(a);
205         if (root != null && root.getAltLoc().equals(a.getAltLoc())) {
206 //          xIndex[i].add(root.getFormFactorIndex());
207 //          a.setFormFactorIndex(root.getFormFactorIndex());
208           if (a.isActive()) {
209             // xIndex[i].add(root.getFormFactorIndex());
210             xIndex.get(i).add(root.getFormFactorIndex());
211             a.setFormFactorIndex(root.getFormFactorIndex());
212           }
213         } else {
214 //          xIndex[i].add(index);
215 //          index++;
216           totalAtomList.add(a);
217           if (a.isActive()) {
218             activeAtomList.add(a);
219             // xIndex[i].add(index);
220             xIndex.get(i).add(index);
221             index++;
222           }
223         }
224       }
225     }
226 
227     totalAtomArray = totalAtomList.toArray(new Atom[0]);
228     activeAtomArray = activeAtomList.toArray(new Atom[0]);
229 
230     /*
231      Make sure the occupancy values make sense, otherwise print warnings
232      (since this could destabilize the refinement, should we error out?)
233     */
234     for (List<Residue> list : altResidues) {
235       double tocc = 0.0;
236       for (Residue r : list) {
237         for (Atom a : r.getAtomList()) {
238           if (a.getOccupancy() < 1.0 || a.getOccupancy() > 1.0) {
239             tocc += a.getOccupancy();
240             break;
241           }
242         }
243       }
244       if (list.size() == 1) {
245         Residue r = list.get(0);
246         logger.log(Level.INFO,
247             "  Residue {0} is a single conformer with non-unity occupancy.\n  Occupancy will be refined independently.\n",
248             r.getChainID() + " " + r);
249       } else if (tocc < 1.0 || tocc > 1.0) {
250         Residue r = list.get(0);
251         logger.log(Level.INFO,
252             "  Residue {0} occupancy does not sum to 1.0.\n  This should be fixed or checked due to possible instability in refinement.\n",
253             r.getChainID() + " " + r);
254       }
255     }
256   }
257 
258   /**
259    * Getter for the field <code>activeAtomArray</code>.
260    *
261    * @return the activeAtomArray
262    */
263   public Atom[] getActiveAtomArray() {
264     return activeAtomArray;
265   }
266 
267   /**
268    * Getter for the field <code>activeAtomList</code>.
269    *
270    * @return the activeAtomList
271    */
272   public List<Atom> getActiveAtomList() {
273     return activeAtomList;
274   }
275 
276   /**
277    * Getter for the field <code>altMolecules</code>.
278    *
279    * @return the altMolecules
280    */
281   public List<List<Molecule>> getAltMolecules() {
282     return altMolecules;
283   }
284 
285   /**
286    * Getter for the field <code>altResidues</code>.
287    *
288    * @return the altResidues
289    */
290   public List<List<Residue>> getAltResidues() {
291     return altResidues;
292   }
293 
294   /**
295    * Getter for the field <code>totalAtomArray</code>.
296    *
297    * @return the totalAtomArray
298    */
299   public Atom[] getTotalAtomArray() {
300     return totalAtomArray;
301   }
302 
303   /**
304    * Getter for the field <code>xIndex</code>.
305    *
306    * @return the xIndex
307    */
308   List<List<Integer>> getxIndex() {
309     return xIndex;
310   }
311 
312   /**
313    * Getter for the field <code>totalAtomList</code>.
314    *
315    * @return the totalAtomList
316    */
317   List<Atom> getTotalAtomList() {
318     return totalAtomList;
319   }
320 }