001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.nbio.structure.xtal;
022
023import org.biojava.nbio.structure.jama.EigenvalueDecomposition;
024import org.biojava.nbio.structure.jama.Matrix;
025import org.biojava.nbio.structure.xtal.io.TransfAlgebraicAdapter;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029import javax.vecmath.AxisAngle4d;
030import javax.vecmath.Matrix3d;
031import javax.vecmath.Matrix4d;
032import javax.vecmath.Vector3d;
033import javax.xml.bind.JAXBContext;
034import javax.xml.bind.JAXBException;
035import javax.xml.bind.Marshaller;
036import javax.xml.bind.annotation.XmlAccessType;
037import javax.xml.bind.annotation.XmlAccessorType;
038import javax.xml.bind.annotation.XmlRootElement;
039import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
040import java.io.ByteArrayOutputStream;
041import java.io.PrintStream;
042import java.io.Serializable;
043import java.util.ArrayList;
044import java.util.List;
045import java.util.regex.Matcher;
046import java.util.regex.Pattern;
047
048
049/**
050 * A crystallographic space group. We store the standard numeric identifier,
051 * the international short symbol and the transformations corresponding to
052 * each space group (as Matrix4ds and in algebraic notation).
053 * The information for all (protein crystallography) space groups can be
054 * parsed from the XML file in the resource directory.
055 *
056 * See: http://en.wikipedia.org/wiki/Space_group
057 *
058 * @author duarte_j
059 * @see SymoplibParser
060 */
061@XmlRootElement(name = "SpaceGroup", namespace ="http://www.biojava.org")
062@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
063public class SpaceGroup implements Serializable {
064
065        private static final long serialVersionUID = 1L;
066        private static final Logger logger = LoggerFactory.getLogger(SpaceGroup.class);
067
068
069        private static final Pattern splitPat1 = Pattern.compile("((?:[+-]?[XYZ])+)([+-][0-9/.]+)");
070        private static final Pattern splitPat2 = Pattern.compile("([+-]?[0-9/.]+)((?:[+-][XYZ])+)");
071        private static final Pattern coordPat = Pattern.compile("(?:([+-])?([XYZ]))+?"); // the last +? is for ungreedy matching
072        private static final Pattern transCoefPat = Pattern.compile("([-+]?[0-9.]+)(?:/([0-9.]+))?");
073
074        private static final Pattern nonEnantPat = Pattern.compile("[-abcmnd]");
075
076        protected static final double DELTA=0.0000001;
077
078        private  int id;
079        private  int multiplicity;
080        private  int primitiveMultiplicity;
081        private  String shortSymbol;
082        private  String altShortSymbol;
083        private  List<Matrix4d> transformations;
084        private  List<String> transfAlgebraic;
085        private  Vector3d[] cellTranslations; // in space groups I, C, F or H there are pure cell translations corresponding to recenterings
086
087        private AxisAngle4d[] axisAngles;
088
089        private int[] axisTypes; // indices of array are transformIds
090
091        private BravaisLattice bravLattice;
092
093        @SuppressWarnings("unused")
094        private SpaceGroup(){
095                // required by JAXB
096
097        }
098
099        public SpaceGroup(int id, int multiplicity, int primitiveMultiplicity, String shortSymbol, String altShortSymbol, BravaisLattice bravLattice) {
100                this.id = id;
101                this.multiplicity = multiplicity;
102                this.primitiveMultiplicity = primitiveMultiplicity;
103                this.shortSymbol = shortSymbol;
104                this.altShortSymbol = altShortSymbol;
105                transformations = new ArrayList<Matrix4d>(multiplicity);
106                transfAlgebraic = new ArrayList<String>(multiplicity);
107                cellTranslations = new Vector3d[multiplicity/primitiveMultiplicity];
108                this.bravLattice = bravLattice;
109        }
110
111        /**
112         * Get the space group for the given international short name, using
113         * the PDB format, e.g. 'P 21 21 21' or 'C 1 c 1'
114         * @param shortName
115         * @return the SpaceGroup or null if the shortName is not valid
116         * @see SymoplibParser#getSpaceGroup(String)
117         */
118        public static SpaceGroup parseSpaceGroup(String shortName) {
119                return SymoplibParser.getSpaceGroup(shortName);
120        }
121
122        public void addTransformation(String transfAlgebraic) {
123                this.transfAlgebraic.add(transfAlgebraic);
124                this.transformations.add(getMatrixFromAlgebraic(transfAlgebraic));
125        }
126
127        protected void initializeCellTranslations() {
128                if ( cellTranslations != null && cellTranslations.length >0) {
129                        // we already initialized this
130                        return;
131                }
132                cellTranslations = new Vector3d[multiplicity/primitiveMultiplicity];
133                cellTranslations[0] = new Vector3d(0,0,0);
134
135                if ( transformations == null){
136                        logger.warn("transformations == null" + this.toXML());
137                }
138
139                if (multiplicity==primitiveMultiplicity) {
140                        return;
141                }
142                int fold = multiplicity/primitiveMultiplicity;
143
144
145
146                for (int n=1;n<fold;n++) {
147                        if ( transformations.size() < (n* primitiveMultiplicity)){
148                                logger.warn("WARNING number of transformations < " +(n*primitiveMultiplicity));
149                                logger.warn(this.toXML());
150                        }
151                        Matrix4d t = transformations.get(n*primitiveMultiplicity);
152                        cellTranslations[n] = new Vector3d(t.m03,t.m13,t.m23);
153                }
154        }
155
156        public int getMultiplicity() {
157                return multiplicity;
158        }
159
160        public int getPrimitiveMultiplicity() {
161                return primitiveMultiplicity;
162        }
163
164        public Vector3d[] getCellTranslations() {
165                return cellTranslations;
166        }
167
168        public Vector3d getCellTranslation(int i) {
169                return cellTranslations[i];
170        }
171
172        public static Matrix4d getMatrixFromAlgebraic(String transfAlgebraic) {
173                String[] parts = transfAlgebraic.toUpperCase().split(",");
174                double[] xCoef = convertAlgebraicStrToCoefficients(parts[0].trim());
175                double[] yCoef = convertAlgebraicStrToCoefficients(parts[1].trim());
176                double[] zCoef = convertAlgebraicStrToCoefficients(parts[2].trim());
177
178                Matrix4d mat = new Matrix4d();
179                mat.setIdentity();
180                mat.setRotation(new Matrix3d(xCoef[0],xCoef[1],xCoef[2],yCoef[0],yCoef[1],yCoef[2],zCoef[0],zCoef[1],zCoef[2]));
181                mat.setTranslation(new Vector3d(xCoef[3],yCoef[3],zCoef[3]));
182                return mat;
183                //return new Matrix4d(xCoef[0],xCoef[1],xCoef[2],xCoef[3],
184                //                                      yCoef[0],yCoef[1],yCoef[2],yCoef[3],
185                //                                      zCoef[0],zCoef[1],zCoef[2],zCoef[3],
186                //                                      0,0,0,1);
187        }
188
189        private static double[] convertAlgebraicStrToCoefficients(String algString) {
190                String letters = null;
191                String noLetters = null;
192                Matcher m = splitPat1.matcher(algString);
193                if (m.matches()) {
194                        letters = m.group(1);
195                        noLetters = m.group(2);
196                } else {
197                        m = splitPat2.matcher(algString);
198                        if (m.matches()) {
199                                letters = m.group(2);
200                                noLetters = m.group(1);
201                        } else {
202                                letters = algString;
203                        }
204                }
205                double[] coefficients = new double[4];
206                m = coordPat.matcher(letters);
207                while(m.find()){
208                        String sign = "";
209                        if (m.group(1)!=null) {
210                                sign = m.group(1);
211                        }
212                        double s = 1.0;
213                        if (sign.equals("-")){
214                                s = -1.0;
215                        }
216                        String coord = m.group(2);
217                        if (coord.equals("X")) {
218                                coefficients[0] = s;
219                        } else if (coord.equals("Y")) {
220                                coefficients[1] = s;
221                        } else if (coord.equals("Z")) {
222                                coefficients[2] = s;
223                        }
224                }
225                if (noLetters!=null) {
226                        m = transCoefPat.matcher(noLetters);
227                        if (m.matches()) {
228                                double num = Double.parseDouble(m.group(1));
229                                double den = 1;
230                                if (m.group(2)!=null) {
231                                        den = Double.parseDouble(m.group(2));
232                                }
233                                coefficients[3] = num/den;
234                        }
235                } else {
236                        coefficients[3]=0;
237                }
238                return coefficients;
239        }
240
241        /**
242         * Gets the standard numeric identifier for the space group.
243         * See for example http://en.wikipedia.org/wiki/Space_group
244         * or the IUCr crystallographic tables
245         * @return
246         */
247        public int getId() {
248                return id;
249        }
250
251        /**
252         * Gets the international short name (as used in PDB),
253         * e.g. "P 21 21 21" or "C 1 c 1"
254         * @return
255         */
256        public String getShortSymbol() {
257                return shortSymbol;
258        }
259
260        /**
261         * Gets the alternative international short name (as sometimes used in PDB),
262         * e.g. "I 1 2 1" instead of "I 2"
263         * @return
264         */
265        public String getAltShortSymbol() {
266                return altShortSymbol;
267        }
268
269        /**
270         * Gets all transformations except for the identity in crystal axes basis.
271         * @return
272         */
273        public List<Matrix4d> getTransformations() {
274                List<Matrix4d> transfs = new ArrayList<Matrix4d>();
275                for (int i=1;i<this.transformations.size();i++){
276                        transfs.add(transformations.get(i));
277                }
278                return transfs;
279        }
280
281        private void calcRotAxesAndAngles() {
282
283                axisAngles = new AxisAngle4d[multiplicity];
284
285                // identity operator (transformId==0)
286                axisAngles[0] = new AxisAngle4d(new Vector3d(0,0,0), 0.0);
287
288                for (int i=1;i<this.transformations.size();i++){
289                        Matrix3d r = new Matrix3d(transformations.get(i).m00,transformations.get(i).m01,transformations.get(i).m02,
290                                        transformations.get(i).m10,transformations.get(i).m11,transformations.get(i).m12,
291                                        transformations.get(i).m20,transformations.get(i).m21,transformations.get(i).m22);
292
293                        axisAngles[i] = getRotAxisAndAngle(r);
294
295                }
296        }
297
298        /**
299         * Calculates the axis fold type (1, 2, 3, 4, 5, 6 for rotations or -1, -2, -3, -4, -6 improper rotations)
300         * from the trace of the rotation matrix, see for instance
301         * http://www.crystallography.fr/mathcryst/pdf/Gargnano/Aroyo_Gargnano_1.pdf
302         */
303        private void calcAxisFoldTypes() {
304                axisTypes = new int[multiplicity];
305
306                for (int i=0;i<this.transformations.size();i++){
307
308                        axisTypes[i] = getRotAxisType(transformations.get(i));
309
310                }
311        }
312
313        public AxisAngle4d getRotAxisAngle(int transformId) {
314                if (this.axisAngles == null) calcRotAxesAndAngles();
315                return this.axisAngles[transformId];
316        }
317
318        /**
319         * Returns true if both given transform ids belong to the same crystallographic axis (a, b or c)
320         * For two non-rotation transformations (i.e. identity operators) it returns true
321         * @param tId1
322         * @param tId2
323         * @return
324         */
325        public boolean areInSameAxis(int tId1, int tId2) {
326                if (tId1==tId2) return true;
327
328                if (axisAngles== null) calcRotAxesAndAngles();
329
330                if (getAxisFoldType(tId1)==1 && getAxisFoldType(tId2)==1) return true;
331
332                // we can't deal yet with improper rotations: we return false whenever either of them is improper
333                if (getAxisFoldType(tId1)<0 || getAxisFoldType(tId2)<0) return false;
334
335                Vector3d axis1 = new Vector3d(axisAngles[tId1].x, axisAngles[tId1].y, axisAngles[tId1].z);
336                Vector3d axis2 = new Vector3d(axisAngles[tId2].x, axisAngles[tId2].y, axisAngles[tId2].z);
337
338                // TODO revise: we might need to consider that the 2 are in same direction but opposite senses
339                // the method is not used at the moment anyway
340                if (deltaComp(axis1.angle(axis2), 0.0, DELTA)) return true;
341
342                return false;
343        }
344
345        /**
346         * Given a transformId returns the type of axis of rotation: 1 (no rotation), 2, 3, 4 or 6 -fold
347         * and for improper rotations: -1, -2, -3, -4 and -6
348         *
349         * @param transformId
350         * @return
351         */
352        public int getAxisFoldType(int transformId) {
353                if (axisTypes== null) calcAxisFoldTypes();
354                return axisTypes[transformId];
355        }
356
357        /**
358         * Gets a transformation by index expressed in crystal axes basis.
359         * Index 0 corresponds always to the identity transformation.
360         * Beware the returned Matrix4d is not a copy but it stays linked
361         * to the one stored in this SpaceGroup object
362         * @param i
363         * @return
364         */
365        public Matrix4d getTransformation(int i) {
366                return transformations.get(i);
367        }
368
369        /**
370         * Gets a transformation algebraic string given its index.
371         * Index 0 corresponds always to the identity transformation.
372         * @param i
373         * @return
374         */
375        public String getTransfAlgebraic(int i) {
376                return transfAlgebraic.get(i);
377        }
378
379        @Override
380        public boolean equals(Object o) {
381                if (! (o instanceof SpaceGroup)) {
382                        return false;
383                }
384                SpaceGroup other = (SpaceGroup) o;
385                if (other.getId()==this.getId()) {
386                        return true;
387                }
388                return false;
389        }
390
391        /**
392         * Gets the number of symmetry operators corresponding to this SpaceGroup (counting
393         * the identity operator)
394         * @return
395         */
396        public int getNumOperators() {
397                return this.transformations.size();
398        }
399
400        public static String getAlgebraicFromMatrix(Matrix4d m) {
401                String x = formatAlg(m.m00,m.m01,m.m02,m.m03);
402                String y = formatAlg(m.m10,m.m11,m.m12,m.m13);
403                String z = formatAlg(m.m20,m.m21,m.m22,m.m23);
404                String alg = x+","+y+","+z;
405                return alg;
406        }
407
408        private static String formatAlg(double xcoef, double ycoef, double zcoef, double trans) {
409                boolean[] leading = {false,false,false};
410                if (xcoef!=0) {
411                        leading[0] = true;
412                } else if (ycoef!=0) {
413                        leading[1] = true;
414                } else if (zcoef!=0) {
415                        leading[2] = true;
416                }
417                String x = deltaComp(xcoef,0,DELTA)?"":formatCoef(xcoef,leading[0])+"X";
418                String y = deltaComp(ycoef,0,DELTA)?"":formatCoef(ycoef,leading[1])+"Y";
419                String z = deltaComp(zcoef,0,DELTA)?"":formatCoef(zcoef, leading[2])+"Z";
420                String t = deltaComp(trans,0,DELTA)?"":formatTransCoef(trans);
421                return x+y+z+t;
422
423        }
424
425        private static String formatCoef(double c, boolean leading) {
426                if (leading) {
427                        return (deltaComp(Math.abs(c),1,DELTA)?(c>0?"":"-"):String.format("%4.2f",c));
428                } else {
429                        return (deltaComp(Math.abs(c),1,DELTA)?(c>0?"+":"-"):String.format("%+4.2f",c));
430                }
431        }
432
433        private static String formatTransCoef(double c) {
434                if (Math.abs((Math.rint(c)-c))<DELTA) { // this is an integer
435                        return String.format("%+d",(int)Math.rint(c));
436                } else { // it is a fraction
437                        int num,den;
438                        int floor = (int)Math.floor(c);
439                        double decPart = c - floor;
440                        if (deltaComp(decPart,0.3333333,DELTA)) {
441                                num=1;den=3;
442                        } else if (deltaComp(decPart,0.6666667,DELTA)) {
443                                num=2;den=3;
444                        } else if (deltaComp(decPart,0.2500000,DELTA)) {
445                                num=1;den=4;
446                        } else if (deltaComp(decPart,0.5000000,DELTA)) {
447                                num=1;den=2;
448                        } else if (deltaComp(decPart,0.7500000,DELTA)) {
449                                num=3;den=4;
450                        } else if (deltaComp(decPart,0.1666667,DELTA)) {
451                                num=1;den=6;
452                        } else if (deltaComp(decPart,0.8333333,DELTA)) {
453                                num=5;den=6;
454                        } else {
455                                num=0;den=0; // this in an error
456                        }
457                        num = floor*den+num;
458                        return String.format("%+d/%d", num,den);
459                        //return String.format("%+4.2f",c);
460                }
461        }
462
463        protected static boolean deltaComp(double d1, double d2, double delta) {
464                return Math.abs(d1-d2)<delta;
465        }
466
467        public BravaisLattice getBravLattice() {
468                return bravLattice;
469        }
470
471        public boolean isEnantiomorphic() {
472                Matcher m = nonEnantPat.matcher(shortSymbol);
473                if (m.find()) {
474                        return false;
475                }
476                return true;
477        }
478
479        /**
480         * Given a rotation matrix calculates the rotation axis and angle for it.
481         * The angle is calculated from the trace, the axis from the eigenvalue
482         * decomposition.
483         * If given matrix is improper rotation or identity matrix then
484         * axis (0,0,0) and angle 0 are returned.
485         * @param m
486         * @return
487         * @throws IllegalArgumentException if given matrix is not a rotation matrix (determinant not 1 or -1)
488         */
489        public static AxisAngle4d getRotAxisAndAngle(Matrix3d m) {
490                double determinant = m.determinant();
491
492                if (!(Math.abs(determinant)-1.0<DELTA)) throw new IllegalArgumentException("Given matrix is not a rotation matrix");
493
494                AxisAngle4d axisAndAngle = new AxisAngle4d(new Vector3d(0,0,0),0);
495
496                double[] d = {m.m00,m.m10,m.m20,
497                                m.m01,m.m11,m.m21,
498                                m.m02,m.m12,m.m22};
499
500                Matrix r = new Matrix(d,3);
501
502                if (!deltaComp(r.det(), 1.0, DELTA)) {
503                        // improper rotation: we return axis 0,0,0 and angle 0
504                        return axisAndAngle;
505                }
506
507                EigenvalueDecomposition evd = new EigenvalueDecomposition(r);
508
509                Matrix eval = evd.getD();
510                if (deltaComp(eval.get(0, 0),1.0,DELTA) && deltaComp(eval.get(1, 1),1.0,DELTA) && deltaComp(eval.get(2, 2),1.0,DELTA)) {
511                        // the rotation is an identity: we return axis 0,0,0 and angle 0
512                        return axisAndAngle;
513                }
514                int indexOfEv1;
515                for (indexOfEv1=0;indexOfEv1<3;indexOfEv1++) {
516                        if (deltaComp(eval.get(indexOfEv1, indexOfEv1),1,DELTA)) break;
517                }
518                Matrix evec = evd.getV();
519                axisAndAngle.set(new Vector3d(evec.get(0,indexOfEv1), evec.get(1, indexOfEv1), evec.get(2, indexOfEv1)),
520                                Math.acos((eval.trace()-1.0)/2.0));
521
522                return axisAndAngle;
523        }
524
525        /**
526         * Given a transformation matrix containing a rotation returns the type of rotation:
527         * 1 for identity, 2 for 2-fold rotation, 3 for 3-fold rotation, 4 for 4-fold rotation,
528         * 6 for 6-fold rotation,
529         * -1 for inversions, -2 for mirror planes, -3 for 3-fold improper rotation,
530         * -4 for 4-fold improper rotation and -6 for 6-fold improper rotation
531         * @param m
532         * @return
533         */
534        public static int getRotAxisType(Matrix4d m) {
535                int axisType = 0;
536
537                Matrix3d rot = new Matrix3d(m.m00,m.m01,m.m02,
538                                m.m10,m.m11,m.m12,
539                                m.m20,m.m21,m.m22);
540
541                double determinant = rot.determinant();
542
543                if (!deltaComp(determinant,1.0,DELTA) && !deltaComp(determinant, -1.0, DELTA)) {
544                        throw new IllegalArgumentException("Given matrix does not seem to be a rotation matrix.");
545                }
546
547                int trace = (int)(rot.m00+rot.m11+rot.m22);
548                if (determinant>0) {
549                        switch (trace) {
550                        case 3:
551                                axisType=1;
552                                break;
553                        case -1:
554                                axisType=2;
555                                break;
556                        case 0:
557                                axisType=3;
558                                break;
559                        case 1:
560                                axisType=4;
561                                break;
562                        case 2:
563                                axisType=6;
564                                break;
565                        default:
566                                throw new RuntimeException("Trace of transform does not correspond to one of the expected types. This is most likely a bug");
567                        }
568                } else {
569                        switch (trace) {
570                        case -3:
571                                axisType=-1;
572                                break;
573                        case 1:
574                                axisType=-2;
575                                break;
576                        case 0:
577                                axisType=-3;
578                                break;
579                        case -1:
580                                axisType=-4;
581                                break;
582                        case -2:
583                                axisType=-6;
584                                break;
585                        default:
586                                throw new RuntimeException("Trace of transform does not correspond to one of the expected types. This is most likely a bug");
587                        }
588                }
589                return axisType;
590        }
591
592        @Override
593        public String toString() {
594                return getShortSymbol();
595        }
596
597        public String toXML(){
598
599
600                JAXBContext jaxbContextStringSortedSet = null;
601
602                try {
603                        jaxbContextStringSortedSet= JAXBContext.newInstance(SpaceGroup.class);
604                } catch (JAXBException e){
605                        logger.error("Error converting to XML",e);
606                        return null;
607                }
608
609                ByteArrayOutputStream baos = new ByteArrayOutputStream();
610                PrintStream ps = new PrintStream(baos);
611
612
613                try {
614
615                        Marshaller m = jaxbContextStringSortedSet.createMarshaller();
616
617                        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
618
619                        m.marshal( this, ps);
620
621
622                } catch (JAXBException e){
623                        logger.error("Error converting to XML",e);
624                }
625
626                return baos.toString();
627        }
628
629        //@XmlElementWrapper(name="transfAlgebraicList", namespace="http://www.biojava.org")
630        //@XmlElement
631        @XmlJavaTypeAdapter(TransfAlgebraicAdapter.class)
632        public List<String> getTransfAlgebraic() {
633                return transfAlgebraic;
634        }
635
636        public void setTransfAlgebraic(List<String> transfAlgebraic) {
637                //System.out.println("setting transfAlgebraic " + transfAlgebraic);
638                if ( transformations == null || transformations.size() == 0)
639                        transformations = new ArrayList<Matrix4d>(transfAlgebraic.size());
640
641                if ( this.transfAlgebraic == null || this.transfAlgebraic.size() == 0)
642                        this.transfAlgebraic = new ArrayList<String>(transfAlgebraic.size());
643
644                for ( String transf : transfAlgebraic){
645                        addTransformation(transf);
646                }
647        }
648
649
650        public int[] getAxisTypes() {
651                return axisTypes;
652        }
653
654        public void setAxisTypes(int[] axisTypes) {
655                this.axisTypes = axisTypes;
656        }
657
658        public static long getSerialversionuid() {
659                return serialVersionUID;
660        }
661
662        public static Pattern getSplitpat1() {
663                return splitPat1;
664        }
665
666        public static Pattern getSplitpat2() {
667                return splitPat2;
668        }
669
670        public static Pattern getCoordpat() {
671                return coordPat;
672        }
673
674        public static Pattern getTranscoefpat() {
675                return transCoefPat;
676        }
677
678        public static Pattern getNonenantpat() {
679                return nonEnantPat;
680        }
681
682        public static double getDelta() {
683                return DELTA;
684        }
685
686        public void setId(int id) {
687                this.id = id;
688        }
689
690        public void setMultiplicity(int multiplicity) {
691
692                this.multiplicity = multiplicity;
693        }
694
695        public void setPrimitiveMultiplicity(int primitiveMultiplicity) {
696                this.primitiveMultiplicity = primitiveMultiplicity;
697        }
698
699        public void setShortSymbol(String shortSymbol) {
700                this.shortSymbol = shortSymbol;
701        }
702
703        public void setAltShortSymbol(String altShortSymbol) {
704                this.altShortSymbol = altShortSymbol;
705        }
706
707
708        public void setBravLattice(BravaisLattice bravLattice) {
709                this.bravLattice = bravLattice;
710        }
711
712}
713