/* * This file is part of MoleculeViewer. * * MoleculeViewer is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MoleculeViewer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with MoleculeViewer. If not, see <http://www.gnu.org/licenses/>. */ package astex; /* Copyright Astex Technology Ltd. 1999 */ /* Copyright David Hall, Boston University, 2011 */ /* * 15-02-02 mjh * fix space group look up to look at the quoted name * in the space group file, rather than the compact * name that is specified on the same line. * 30-11-99 mjh * created */ import java.util.*; /** * A class for generating symmetry related copies of molecules. */ public class Symmetry { /** The unit cell for this symmetry object. */ public double unitCell[] = new double[6]; /** The matrix that converts fractional to cartesian coordinates. */ public Matrix fractionalToCartesian = new Matrix(); /** The matrix that converts cartesian to fractional coordinates. */ public Matrix cartesianToFractional = new Matrix(); /** * The SCALE matrix from the PDB file if one exists. */ public Matrix scale = null; /** The space group number. */ private int spaceGroupNumber = 0; /** The space group name. */ private String spaceGroupName = null; /** The original space group name. */ private String originalSpaceGroupName = null; /** The list of symmetry operators. */ private List<Matrix> symmetryOperators = null; /** The default location of the symmetry library. */ private static final String symmetryLibrary = "symmetry.properties"; /** Get the symmetry operators for the specified space group. */ public List<Matrix> getSymmetryOperators(){ if(symmetryOperators != null){ return Collections.unmodifiableList(symmetryOperators); } FILE file = FILE.open(symmetryLibrary); String compactedOriginalName = null; // compact the original space group name if(originalSpaceGroupName != null){ compactedOriginalName = originalSpaceGroupName.replace(" ",""); } while(file.nextLine()){ String line = file.getCurrentLineAsString(); StringTokenizer tokenizer = new StringTokenizer(line); String numberToken = tokenizer.nextToken(); String operatorCountToken = tokenizer.nextToken(); tokenizer.nextToken(); String shortName = tokenizer.nextToken(); String name = getSpaceGroupName(line); int number = FILE.readInteger(numberToken); int operatorCount = FILE.readInteger(operatorCountToken); if(number == spaceGroupNumber || (compactedOriginalName != null && name.equals(compactedOriginalName)) || (spaceGroupName != null && shortName.equals(spaceGroupName))){ System.out.println("spacegroup matched symmetry definition"); System.out.println(line); symmetryOperators = new ArrayList<Matrix>(operatorCount); for(int i = 0; i < operatorCount; i++){ file.nextLine(); String operatorString = file.getCurrentLineAsString(); readSymmetryOperator(operatorString); } break; }else{ for(int i = 0; i < operatorCount; i++){ file.nextLine(); } } } file.close(); if(symmetryOperators == null){ return Collections.emptyList(); } return Collections.unmodifiableList(symmetryOperators); } /** * Return the symmetry name from the spage group line. * * The line is of the form * 4 2 2 P21 PG2 MONOCLINIC 'P 1 21 1' * * Previous versions compressed the name from the pdb file * and compared it to P21, but we need to compare it to * 'P 1 21 1'. This method returns that compacted name. */ private static String getSpaceGroupName(String spaceGroupDescription){ int firstApostrophe = spaceGroupDescription.indexOf('\''); int lastApostrophe = spaceGroupDescription.lastIndexOf('\''); String spaceGroupName = spaceGroupDescription.substring(firstApostrophe,lastApostrophe); spaceGroupName = spaceGroupName.replace(" ",""); return spaceGroupName; } /** Decode one symmetry operator. */ private void readSymmetryOperator(String line){ StringTokenizer lineTokenizer = new StringTokenizer(line, ","); String xToken = lineTokenizer.nextToken().trim(); String yToken = lineTokenizer.nextToken().trim(); String zToken = lineTokenizer.nextToken().trim(); double c[] = new double[4]; Matrix m = new Matrix(); m.setIdentity(); decodeSymmetryToken(xToken, c); m.m00 = c[0]; m.m10 = c[1]; m.m20 = c[2]; m.m30 = c[3]; decodeSymmetryToken(yToken, c); m.m01 = c[0]; m.m11 = c[1]; m.m21 = c[2]; m.m31 = c[3]; decodeSymmetryToken(zToken, c); m.m02 = c[0]; m.m12 = c[1]; m.m22 = c[2]; m.m32 = c[3]; symmetryOperators.add(m); } /** The positive axes. */ private static String positiveAxes[] = {"X", "Y", "Z"}; /** The fractions. */ private static String fractions[] = { "1/2", "1/3", "2/3", "1/4", "3/4", "1/6", "5/6" }; private static double fractionValues[] = { 1./2., 1./3., 2./3., 1./4., 3./4., 1./6., 5./6. }; /** Decode the symmetry token in the String. */ private static void decodeSymmetryToken(String token, double components[]){ for(int i = 0; i < components.length; i++){ components[i] = 0.0; } for(int i = 0; i < 3; i++){ if(token.indexOf("-" + positiveAxes[i]) != -1){ components[i] = -1.0; }else if(token.indexOf(positiveAxes[i]) != -1){ components[i] = 1.0; } } for(int i = 0; i < fractions.length; i++){ if(token.indexOf("-" + fractions[i]) != -1){ components[3] = - fractionValues[i]; break; }else if(token.indexOf(fractions[i]) != -1){ components[3] = fractionValues[i]; break; } } } /** Set the unit cell. */ public void setUnitCell(double newCell[]){ System.arraycopy(newCell, 0, unitCell, 0, 6); cartesianToFractional = new Matrix(); fractionalToCartesian = new Matrix(); generateMatrices(unitCell, cartesianToFractional, fractionalToCartesian); } /** * Generate the cartesian/fractional interconversion matrices. * This is more involved than might appear at first. * We need to handle a variety of special cases, like * when the unit cell does not follow the standard pdb convention. * Also there can be mistakes in the supplied SCALE records * which we need to trap. */ public void prepareSymmetry(){ if(scale == null){ // no symmetry or definitely no special cases // matrices should already be defined return; } Matrix s = scale; Matrix c2f = getCartesianToFractionalMatrix(); if(!s.equals(c2f)){ System.err.println("prepareSymmetry: SCALE does not match " + "calculated cartesian->fractional matrix"); System.err.println("prepareSymmetry: fixing symmetry"); Matrix sinv = new Matrix(); sinv.invert(s); Matrix tmp = new Matrix(s); tmp.transform(sinv); Matrix f2c = getFractionalToCartesianMatrix(); Matrix check = new Matrix(s); check.transform(f2c); check.m30 = 0.0; check.m31 = 0.0; check.m32 = 0.0; Matrix checkt = new Matrix(check); checkt.transpose(); check.transform(checkt); // this should really take account of the // unit cell parameters but this value seems // to work ok... if(check.isIdentity(1.e-2)){ // reorientation was pure rotation c2f.set(s); f2c.set(sinv); }else{ System.err.println("prepareSymmetry: inconsistent " + "scale matrix - ignored"); // remove all trace of the scale matrix as it was bogus scale = null; } } } /** Set the space group number. */ public void setSpaceGroupNumber(int number){ spaceGroupNumber = number; spaceGroupName = null; } /** Get the space group number. */ public int getSpaceGroupNumber(){ return spaceGroupNumber; } /** Set the space group name. */ public void setSpaceGroupName(String name){ spaceGroupName = name; spaceGroupNumber = 0; } /** Set the original space group name (with spaces). */ public void setOriginalSpaceGroupName(String s){ originalSpaceGroupName = s; } /** Get the original space group name. */ public String getOriginalSpaceGroupName(){ return originalSpaceGroupName; } /** Get the fractionalising matrix. */ public Matrix getCartesianToFractionalMatrix(){ return cartesianToFractional; } /** Get the defractionalising matrix. */ public Matrix getFractionalToCartesianMatrix(){ return fractionalToCartesian; } /** Return the square of the argument. */ private static double SQ(double x){ return x*x; } /** Generate the fractional to cartesian matrices. */ private static void generateMatrices(double cell[], Matrix cartesianToFractional, Matrix fractionalToCartesian){ double cabg[] = new double[3]; double cabgs[] = new double[3]; double sabg[] = new double[3]; double abcs[] = new double[3]; double sabgs1; double volume; /* Initialise the transformation matrices. */ cartesianToFractional.setIdentity(); fractionalToCartesian.setIdentity(); for(int i = 0; i < 3; i++){ cabg[i]=Math.cos(Math.PI*cell[i+3]/180.0); sabg[i]=Math.sin(Math.PI*cell[i+3]/180.0); } cabgs[0]=(cabg[1]*cabg[2]-cabg[0])/(sabg[1]*sabg[2]); cabgs[1]=(cabg[2]*cabg[0]-cabg[1])/(sabg[2]*sabg[0]); cabgs[2]=(cabg[0]*cabg[1]-cabg[2])/(sabg[0]*sabg[1]); volume=cell[0]*cell[1]*cell[2]* Math.sqrt(1.0+2.0*cabg[0]*cabg[1]*cabg[2] -SQ(cabg[0])-SQ(cabg[1])-SQ(cabg[2])); abcs[0]=cell[1]*cell[2]*sabg[0]/ volume; abcs[1]=cell[0]*cell[2]*sabg[1]/ volume; abcs[2]=cell[0]*cell[1]*sabg[2]/ volume; sabgs1=Math.sqrt(1.0-SQ(cabgs[0])); /* Cartesian to fractional conversion matrix. */ cartesianToFractional.m00=1.0/cell[0]; cartesianToFractional.m10=-cabg[2]/(sabg[2]*cell[0]); cartesianToFractional.m20=-(cabg[2]*sabg[1]*cabgs[0]+cabg[1]*sabg[2])/ (sabg[1]*sabgs1*sabg[2]*cell[0]); cartesianToFractional.m11=1.0/(sabg[2]*cell[1]); cartesianToFractional.m21=cabgs[0]/(sabgs1*sabg[2]*cell[1]); cartesianToFractional.m22=1.0/(sabg[1]*sabgs1*cell[2]); /* Fractional to cartesian matrix. */ fractionalToCartesian.m00= cell[0]; fractionalToCartesian.m10= cabg[2]*cell[1]; fractionalToCartesian.m20= cabg[1]*cell[2]; fractionalToCartesian.m11= sabg[2]*cell[1]; fractionalToCartesian.m21=-sabg[1]*cabgs[0]*cell[2]; fractionalToCartesian.m22=sabg[1]*sabgs1*cell[2]; } }