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