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.algorithms.optimize.manybody; 39 40 import static ffx.crystal.SymOp.applyFracSymOp; 41 import static ffx.potential.bonded.RotamerLibrary.applyRotamer; 42 43 import ffx.crystal.Crystal; 44 import ffx.crystal.SymOp; 45 import ffx.potential.bonded.Atom; 46 import ffx.potential.bonded.Residue; 47 import ffx.potential.bonded.ResidueState; 48 import ffx.potential.bonded.Rotamer; 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.Comparator; 52 import java.util.List; 53 54 /** 55 * Contains a cell used for box optimization, its residues, the fractional coordinates within a 56 * crystal it takes up, its overall (linear) index, and its indices along the a, b, and c crystal 57 * axes. 58 */ 59 public class BoxOptCell { 60 61 // fracCoords indexed by 1-3 min x,y,z, 4-6 are max x,y,z 62 private final double[] fracCoords = new double[6]; 63 private final int[] indexXYZ = new int[3]; 64 private final int linearIndex; 65 private final ArrayList<Residue> residues = new ArrayList<>(); 66 67 /** 68 * Constructs a BoxOptCell object, which takes up a set of fractional coordinates within the 69 * Crystal, the Residues contained within, and the index of the cell along the crystal's a, b, and 70 * c axes. 71 * 72 * @param fractionalCoordinates Fractional coordinates contained, indexed by 1-3 min x,y,z, 4-6 73 * max x,y,z 74 * @param indices Index of cell along a, b, and c (x, y, and z). 75 * @param linearIndex Index of box in linear box array. 76 */ 77 public BoxOptCell(double[] fractionalCoordinates, int[] indices, int linearIndex) { 78 System.arraycopy(fractionalCoordinates, 0, fracCoords, 0, fracCoords.length); 79 System.arraycopy(indices, 0, indexXYZ, 0, indexXYZ.length); 80 this.linearIndex = linearIndex; 81 } 82 83 /** 84 * Add a residue to the box. 85 * 86 * @param residue Residue to be added. 87 */ 88 public void addResidue(Residue residue) { 89 residues.add(residue); 90 } 91 92 /** 93 * Checks if any rotamer of a Residue is inside this BoxOptCell. 94 * 95 * @param residue Residue to check. 96 * @param crystal A Crystal. 97 * @param symOp A symmetry operator to apply. 98 * @param variableOnly If using only variable (protein side-chain, nucleic acid backbone) 99 * atoms. 100 * @return If contained inside this BoxOptCell. 101 */ 102 public boolean anyRotamerInsideCell(Residue residue, Crystal crystal, SymOp symOp, 103 boolean variableOnly) { 104 ResidueState incomingState = residue.storeState(); 105 Rotamer[] rotamers = residue.getRotamers(); 106 boolean inside = Arrays.stream(rotamers).anyMatch((Rotamer r) -> { 107 applyRotamer(residue, r); 108 return residueInsideCell(residue, crystal, symOp, variableOnly); 109 }); 110 residue.revertState(incomingState); 111 return inside; 112 } 113 114 /** 115 * Checks if an Atom would be contained inside this cell. 116 * 117 * @param atom Atom to check. 118 * @param crystal A Crystal. 119 * @param symOp A symmetry operator to apply. 120 * @return If contained. 121 */ 122 public boolean atomInsideCell(Atom atom, Crystal crystal, SymOp symOp) { 123 double[] atXYZ = new double[3]; 124 atXYZ = atom.getXYZ(atXYZ); 125 crystal.toFractionalCoordinates(atXYZ, atXYZ); 126 moveValuesBetweenZeroAndOne(atXYZ); 127 applyFracSymOp(atXYZ, atXYZ, symOp); 128 return checkIfContained(atXYZ); 129 } 130 131 /** 132 * Moves an array of doubles to be within 0.0 and 1.0 by addition or subtraction of a multiple of 133 * 1.0. Typical use is moving an atom placed outside crystal boundaries from the symmetry mate back 134 * into the crystal. 135 * 136 * @param valuesToMove Doubles to be moved between 0 and 1. 137 */ 138 public static void moveValuesBetweenZeroAndOne(double[] valuesToMove) { 139 for (int i = 0; i < valuesToMove.length; i++) { 140 valuesToMove[i] = moveBetweenZeroAndOne(valuesToMove[i]); 141 } 142 } 143 144 /** 145 * Moves a double to be within 0.0 and 1.0 by addition or subtraction of a multiple of 1.0. Typical 146 * use is moving an atom place outside crystal boundaries from the symmetry mate back into the 147 * crystal. 148 * 149 * @param value Double to be moved between 0 and 1. 150 * @return Shifted double. 151 */ 152 private static double moveBetweenZeroAndOne(double value) { 153 if (value < 0.0) { 154 int belowZero = (int) (value); 155 belowZero = 1 + (-1 * belowZero); 156 value = value + belowZero; 157 } else { 158 value = value % 1.0; 159 } 160 return value; 161 } 162 163 /** 164 * Returns the linear index of this Box. 165 * 166 * @return Linear index. 167 */ 168 public int getLinearIndex() { 169 return linearIndex; 170 } 171 172 /** 173 * Returns an array of the Residues contained within the cell. 174 * 175 * @return Array of Residues. 176 */ 177 public Residue[] getResidues() { 178 return (Residue[]) residues.toArray(); 179 } 180 181 /** 182 * Returns a copy of the ArrayList of residues. 183 * 184 * @return ArrayList of Residues in the cell. 185 */ 186 public ArrayList<Residue> getResiduesAsList() { 187 return new ArrayList<>(residues); 188 } 189 190 /** 191 * Returns the x, y, and z indices of this box. 192 * 193 * @return Box indices. 194 */ 195 public int[] getXYZIndex() { 196 int[] returnedIndices = new int[3]; 197 System.arraycopy(indexXYZ, 0, returnedIndices, 0, returnedIndices.length); 198 return returnedIndices; 199 } 200 201 /** 202 * Checks if a Residue is inside this BoxOptCell. 203 * 204 * @param residue Residue to check. 205 * @param crystal A Crystal. 206 * @param symOp A symmetry operator to apply. 207 * @param variableOnly If using only variable (protein side-chain, nucleic acid backbone) 208 * atoms. 209 * @return If contained inside this BoxOptCell. 210 */ 211 public boolean residueInsideCell(Residue residue, Crystal crystal, SymOp symOp, 212 boolean variableOnly) { 213 List<Atom> atoms = variableOnly ? residue.getVariableAtoms() : residue.getAtomList(); 214 return atoms.stream().anyMatch(a -> atomInsideCell(a, crystal, symOp)); 215 } 216 217 /** Sorts residues in the box. */ 218 public void sortBoxResidues() { 219 Comparator<Residue> comparator = Comparator.comparing(Residue::getChainID) 220 .thenComparingInt(Residue::getResidueNumber); 221 residues.sort(comparator); 222 } 223 224 /** 225 * Check if an atom's fractional coordinates would be contained by the box. 226 * 227 * @param atomFracCoords Atomic fractional coordinates 228 * @return If contained. 229 */ 230 private boolean checkIfContained(double[] atomFracCoords) { 231 for (int i = 0; i < 3; i++) { 232 if ((fracCoords[i] > atomFracCoords[i]) || (fracCoords[i + 3] < atomFracCoords[i])) { 233 return false; 234 } 235 } 236 return true; 237 } 238 }