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-2024.
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 static java.lang.String.format;
41  
42  import ffx.potential.MolecularAssembly;
43  import ffx.potential.bonded.Atom;
44  import ffx.potential.bonded.MSNode;
45  import ffx.potential.bonded.Molecule;
46  import ffx.potential.bonded.Residue;
47  import java.util.ArrayList;
48  import java.util.List;
49  import java.util.logging.Level;
50  import java.util.logging.Logger;
51  
52  /**
53   * RefinementModel class.
54   *
55   * @author Timothy D. Fenn
56   * @since 1.0
57   */
58  public class RefinementModel {
59  
60    private static final Logger logger = Logger.getLogger(RefinementModel.class.getName());
61    /** An atom list. */
62    private final List<Atom> totalAtomList;
63    /** An atom array. */
64    private final Atom[] totalAtomArray;
65    /** An atom list. */
66    private final List<Atom> activeAtomList;
67    /** An atom array. */
68    private final Atom[] activeAtomArray;
69    /** Map between atom in different molecular assemblies. */
70    private final List<List<Integer>> xIndex;
71  
72    private final List<List<Residue>> altResidues;
73    private final List<List<Molecule>> altMolecules;
74  
75    /**
76     * Constructor for RefinementModel.
77     *
78     * @param molecularAssemblies an array of {@link ffx.potential.MolecularAssembly} objects.
79     */
80    public RefinementModel(MolecularAssembly[] molecularAssemblies) {
81      this(molecularAssemblies, false);
82    }
83  
84    /**
85     * Constructor for RefinementModel.
86     *
87     * @param molecularAssemblies an array of {@link ffx.potential.MolecularAssembly} objects.
88     * @param refineMolOcc a boolean.
89     */
90    @SuppressWarnings("unchecked")
91    public RefinementModel(MolecularAssembly[] molecularAssemblies, boolean refineMolOcc) {
92      List<Atom> atomList;
93  
94      // Build alternate conformer list for occupancy refinement (if necessary).
95      altResidues = new ArrayList<>();
96      altMolecules = new ArrayList<>();
97      List<MSNode> nodeList0 = molecularAssemblies[0].getNodeList();
98      List<Residue> tempResidues = null;
99      List<Molecule> tempMolecules = null;
100     boolean alternateConformer;
101 
102     // By residue/molecule.
103     for (int i = 0; i < nodeList0.size(); i++) {
104       alternateConformer = false;
105       MSNode iNode = nodeList0.get(i);
106 
107       // First set up alternate residue restraint list.
108       for (Atom a : iNode.getAtomList()) {
109         if (!a.getUse()) {
110           continue;
111         }
112         if (a.getAltLoc() == null) {
113           logger.severe(
114               format(
115                   " Atom %s has a null alternate location. Likely cause: attempting X-ray refinement from a .xyz file",
116                   a));
117         }
118         if (!a.getAltLoc().equals(' ') || a.getOccupancy() < 1.0) {
119           if (iNode instanceof Residue) {
120             tempResidues = new ArrayList<>();
121             tempResidues.add((Residue) iNode);
122             altResidues.add(tempResidues);
123             alternateConformer = true;
124             break;
125           } else if (iNode instanceof Molecule) {
126             if (refineMolOcc) {
127               tempMolecules = new ArrayList<>();
128               tempMolecules.add((Molecule) iNode);
129               altMolecules.add(tempMolecules);
130             }
131             alternateConformer = true;
132             break;
133           }
134         }
135       }
136       if (alternateConformer) {
137         for (int j = 1; j < molecularAssemblies.length; j++) {
138           List<MSNode> nlist = molecularAssemblies[j].getNodeList();
139           MSNode node = nlist.get(i);
140           for (Atom a : node.getAtomList()) {
141             if (!a.getUse()) {
142               continue;
143             }
144             if (!a.getAltLoc().equals(' ') && !a.getAltLoc().equals('A')) {
145               if (node instanceof Residue) {
146                 if (tempResidues != null) {
147                   tempResidues.add((Residue) node);
148                 }
149                 break;
150               } else if (node instanceof Molecule) {
151                 if (tempMolecules != null) {
152                   tempMolecules.add((Molecule) node);
153                 }
154                 break;
155               }
156             }
157           }
158         }
159       }
160     }
161 
162     // For mapping between atoms between different molecular assemblies.
163     // xIndex = new List[molecularAssemblies.length];
164     xIndex = new ArrayList<>(molecularAssemblies.length);
165     for (int i = 0; i < molecularAssemblies.length; i++) {
166       // xIndex[i] = new ArrayList<>();
167       xIndex.add(new ArrayList<>());
168     }
169 
170     // Create the atomList to be used for SF calculations.
171     totalAtomList = new ArrayList<>();
172 
173     // Create the activeAtomList (i.e. atoms that can move).
174     activeAtomList = new ArrayList<>();
175 
176     // Root list.
177     atomList = molecularAssemblies[0].getAtomList();
178     int index = 0;
179     for (Atom a : atomList) {
180       if (!a.getUse()) {
181         continue;
182       }
183       totalAtomList.add(a);
184 //      a.setFormFactorIndex(index);
185 //      xIndex[0].add(index);
186 //      index++;
187       if (a.isActive()) {
188         activeAtomList.add(a);
189         a.setFormFactorIndex(index);
190         // xIndex[0].add(index);
191         xIndex.get(0).add(index);
192         index++;
193       }
194     }
195 
196     // Now add cross-references to root and any alternate atoms not in root
197     for (int i = 1; i < molecularAssemblies.length; i++) {
198       atomList = molecularAssemblies[i].getAtomList();
199       for (Atom a : atomList) {
200         if (!a.getUse()) {
201           continue;
202         }
203         Atom root = molecularAssemblies[0].findAtom(a);
204         if (root != null && root.getAltLoc().equals(a.getAltLoc())) {
205 //          xIndex[i].add(root.getFormFactorIndex());
206 //          a.setFormFactorIndex(root.getFormFactorIndex());
207           if (a.isActive()) {
208             // xIndex[i].add(root.getFormFactorIndex());
209             xIndex.get(i).add(root.getFormFactorIndex());
210             a.setFormFactorIndex(root.getFormFactorIndex());
211           }
212         } else {
213 //          xIndex[i].add(index);
214 //          index++;
215           totalAtomList.add(a);
216           if (a.isActive()) {
217             activeAtomList.add(a);
218             // xIndex[i].add(index);
219             xIndex.get(i).add(index);
220             index++;
221           }
222         }
223       }
224     }
225 
226     totalAtomArray = totalAtomList.toArray(new Atom[0]);
227     activeAtomArray = activeAtomList.toArray(new Atom[0]);
228 
229     /*
230      Make sure the occupancy values make sense, otherwise print warnings
231      (since this could destabilize the refinement, should we error out?)
232     */
233     for (List<Residue> list : altResidues) {
234       double tocc = 0.0;
235       for (Residue r : list) {
236         for (Atom a : r.getAtomList()) {
237           if (a.getOccupancy() < 1.0 || a.getOccupancy() > 1.0) {
238             tocc += a.getOccupancy();
239             break;
240           }
241         }
242       }
243       if (list.size() == 1) {
244         Residue r = list.get(0);
245         logger.log(Level.INFO,
246             "  Residue {0} is a single conformer with non-unity occupancy.\n  Occupancy will be refined independently.\n",
247             r.getChainID() + " " + r);
248       } else if (tocc < 1.0 || tocc > 1.0) {
249         Residue r = list.get(0);
250         logger.log(Level.INFO,
251             "  Residue {0} occupancy does not sum to 1.0.\n  This should be fixed or checked due to possible instability in refinement.\n",
252             r.getChainID() + " " + r);
253       }
254     }
255   }
256 
257   /**
258    * Getter for the field <code>activeAtomArray</code>.
259    *
260    * @return the activeAtomArray
261    */
262   public Atom[] getActiveAtomArray() {
263     return activeAtomArray;
264   }
265 
266   /**
267    * Getter for the field <code>activeAtomList</code>.
268    *
269    * @return the activeAtomList
270    */
271   public List<Atom> getActiveAtomList() {
272     return activeAtomList;
273   }
274 
275   /**
276    * Getter for the field <code>altMolecules</code>.
277    *
278    * @return the altMolecules
279    */
280   public List<List<Molecule>> getAltMolecules() {
281     return altMolecules;
282   }
283 
284   /**
285    * Getter for the field <code>altResidues</code>.
286    *
287    * @return the altResidues
288    */
289   public List<List<Residue>> getAltResidues() {
290     return altResidues;
291   }
292 
293   /**
294    * Getter for the field <code>totalAtomArray</code>.
295    *
296    * @return the totalAtomArray
297    */
298   public Atom[] getTotalAtomArray() {
299     return totalAtomArray;
300   }
301 
302   /**
303    * Getter for the field <code>xIndex</code>.
304    *
305    * @return the xIndex
306    */
307   List<List<Integer>> getxIndex() {
308     return xIndex;
309   }
310 
311   /**
312    * Getter for the field <code>totalAtomList</code>.
313    *
314    * @return the totalAtomList
315    */
316   List<Atom> getTotalAtomList() {
317     return totalAtomList;
318   }
319 }