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