SgInfo is library of ANSI C routines
The design of the routines is based on the notation introduced by Hall which enables unambiguous and practically unrestricted definition of space group, setting, and origin. Input Hall symbols are translated into Seitz matrices, which, in turn, are used to generate the full set of symmetry operations. After the input Hall symbol has been translated, the matrices are sorted and reduced back to an output Hall symbol. For a given space group and setting, the reduction process will always result in the same Hall symbol, regardless of the way the group was generated; i.e., Seitz matrices from any source may be passed to the symmetry generating routines and a well defined Hall symbol will be produced.
Part of the library is a table which associates ITVA space group numbers, Schönflies symbols, Hermann-Mauguin symbols, and Hall symbols. This table is used in two ways. Space group numbers and symbols can be translated to a Hall symbol, which is used to generate the symmetry matrices. Alternatively, the Hall symbol produced by the reduction algorithm of the library is used to find the appropriate entry in the table.
What follows in this document, is meant to be an
There is also a
where each structure, subroutine, and macro is explained in detail.
Table of Content for this document:
The SgInfo package comes with a demonstration program which also has a certain value on its own. Running sginfo without arguments will produce a little help screen:
usage: sginfo [options] [SpaceGroupName_or_# [SpaceGroupName_or_#]] -Hall|VolA|VolI select conventions -ListTable[=#] print [parts of] internal table -CIF print internal table in CIF format -XYZ print something like "-x, y+1/2, z" -AllXYZ print all symmetry operations -Maple print symmetry matrices in Maple format -Space print symmetry file for AVS SpaceModule -Shelx print Shelx LATT & SYMM cards -Schakal print Schakal DU & SY cards -hklList print simple hkl listing -Standard compute transformation to "standard" setting -UnitCell="a..g" unit cell constants a, b, c, alpha, beta, gamma -v be more verbose -Verify debug option: verify transformations -ClearError debug option: clear errors and continue examples: sginfo 68 sginfo C2/m:c2 -XYZ sginfo "Oh^3" -Shelx sginfo -Hall "-F 4y 2" -Standard sginfo -VolI 15 -VolA 15 sginfo -ListTable=68
Try some of the examples:
% sginfo 68 Space Group 68:1 D2h^22 Ccca:1 C 2 2 -1bc Point Group mmm Laue Group mmm Orthorhombic Note: Inversion operation off origin Order 16 Order P 8 s.i.Vector Modulus 1 0 0 2 0 0 1 2
What does all this mean?
The first line tells you what the lookup in the internal table has produced for "68". This is the entry for space group 68, Origin Choice 1 (:1). This space group has the Schönflies symbol D2h^22, the Herman-Mauguin symbol Ccca and the Hall symbol "C 2 2 -1bc".
After sginfo has found the table entry, it translates the Hall symbol to symmetry matrices. Next - through repetive multiplication of these generator matrices - the whole group, i.e. all symmetry matrices of this space group, are computed.
Knowing all symmetry matrices, sginfo derives that space group 68 belongs to point group mmm, laue group mmm, and the orthorhombic crystal system. Furthermore, space group 68 is centro-symmetric, but the inversion operation is not at the origin (of course this is what ITVA Origin Choice 1 means, but sginfo finds out without looking at this).
The maximum number of equivalent positions in space group 68 is indicated by Order 16. To help people who want to use the SgInfo library routines in their own applications, SgInfo also stores the maximum number of positions of the primitive "P" subgroup of the space group. In this case Order P 8 is reported.
Direct Methods people might be delighted to see that SgInfo also knows how to derive semi-invariant vectors and moduli from a set of symmetry matrices. Here we have two semi-invariant vectors - one along x (1 0 0), one along z (0 0 1) - with modulus "2".
In the next example, sginfo demonstrates its ability to look up Schönflies symbols. In addition, a list of the symmetry operations in SHELX format is requested. For all who do not have SHELX-TL(TM), this could help avoiding a lot of typos:
% sginfo "Oh^3" -Shelx Space Group 223 Oh^3 Pm-3n -P 4n 2 3 Point Group m-3m Laue Group m-3m Cubic Order 48 Order P 48 s.i.Vector Modulus 1 1 1 2 LATT 1 SYMM .5-Y, .5+X, .5+Z SYMM -X, -Y, Z SYMM .5+Y, .5-X, .5+Z SYMM .5+X, .5-Z, .5+Y SYMM X, -Y, -Z SYMM .5+X, .5+Z, .5-Y SYMM .5+Z, .5+Y, .5-X SYMM -X, Y, -Z SYMM .5-Z, .5+Y, .5+X SYMM Z, X, Y SYMM Y, Z, X SYMM -Y, -Z, X SYMM Z, -X, -Y SYMM -Y, Z, -X SYMM -Z, -X, Y SYMM -Z, X, -Y SYMM Y, -Z, -X SYMM .5+Y, .5+X, .5-Z SYMM .5-Y, .5-X, .5-Z SYMM .5-X, .5+Z, .5+Y SYMM .5-X, .5-Z, .5-Y SYMM .5+Z, .5-Y, .5+X SYMM .5-Z, .5-Y, .5-X
The next example again demonstrates two new features: how to enter a Hall symbol from the command line and how to find out which space group is generated by this Hall symbol:
% sginfo -Hall "-F 4y 2" -Standard Setting A: Hall Symbol -F 4y 2 Point Group 4/mmm Laue Group 4/mmm Tetragonal Unique Axis y Order 64 Order P 16 s.i.Vector Modulus 1 1 1 2 Setting B: Space Group 139 D4h^17 I4/mmm -I 4 2 Point Group 4/mmm Laue Group 4/mmm Tetragonal Unique Axis z Order 32 Order P 16 s.i.Vector Modulus 0 0 1 2 Change of Basis Setting A -> Setting B: CBMx = x+z, x-z, y InvCBMx = 1/2*x+1/2*y, z, 1/2*x-1/2*y
First of all, sginfo processes the Hall symbol and reports the results under "Setting A". Since "Unique Axis y" is a very strange setting for a tetragonal space group, sginfo cannot find an entry in the internal table and hence only reports the (newly generated) Hall symbol.
Fortunately there is the "-Standard" option, which tells sginfo to try harder to find out about the ITVA space group number. Therefore sginfo tries to match the generators of all space groups in the internal table (which do belong to the same point group) to the actual set of symmetry matrices by building the proper change-of-basis matrix, in this case coming up with space group I4/mmm.
The result of the space group finding process is not necessarily the "standard" setting, but a certain reference setting. Therefore sginfo evaluates which "conventions", either option "-VolI" or "-VolA" (which is the default) are currently selected, obtains the corresponding standard setting und reports the results under "Setting B". Again sginfo starts to search for the reference setting, this time only to get the corresponding change-of-basis matrix.
In the last step, sginfo combines the change-of-basis matrices for "Setting A -> Reference Setting" and "Setting B -> Reference Setting" to obtain the matrix and its inverse for "Setting A -> Setting B".
If you do not trust sginfo and/or want to see the individual change-of-basis matrices to the reference setting, run sginfo with the "-Verify" option set.
Maybe it is sensible to add the following short definition:
"CBMx" transforms
the coordinates of Setting A to coordinates of
Setting B.
"InvCBMx" transforms
the coordinates of Setting B to coordinates of
Setting A.
BTW: the execution time on a 486 DX2 66 MHz PC running Linux is less than 0.5 seconds for the whole process above (two times generation of the symmetry operations, two times finding the space group with construction of the change-of-basis matrix).
The last example illustrates how easy it is to get the change-of-basis matrix for the transformation of the "old" standard setting of ITVI to the "new" standard setting of ITVA:
% sginfo -VolI 15 -VolA 15 Setting A: Space Group 15:-c1 C2h^6 C2/c:-c1 = B112/b = B2/b -B 2b Point Group 2/m Laue Group 2/m Monoclinic Unique Axis z Order 8 Order P 4 s.i.Vector Modulus 0 1 0 2 0 0 1 2 Setting B: Space Group 15:b1 C2h^6 C2/c:b1 = C12/c1 -C 2yc Point Group 2/m Laue Group 2/m Monoclinic Unique Axis y Order 8 Order P 4 s.i.Vector Modulus 1 0 0 2 0 0 1 2 Change of Basis Setting A -> Setting B: CBMx = x-y-1/4, z+1/4, -y InvCBMx = x-z+1/4, -z, y-1/4
A xray powder pattern has been indexed with the POWDER program of D. Taupin [1]. The unit cell as found by the program was monoclinic, a=8.63289, b=9.10859, c=17.699, gamma=108.45. Examination of systematic absences suggested space group P 1 1 21/n.
What is the standard setting of this space group and what are the corresponding cell constants?
% sginfo "P 1 1 21/n" -Standard -UnitCell="8.63289 9.10859 17.699 108.45" Setting A: Space Group 14:c2 C2h^5 P21/c:c2 = P1121/n -P 2n Point Group 2/m Laue Group 2/m Monoclinic Unique Axis z Order 4 Order P 4 s.i.Vector Modulus 1 0 0 2 0 1 0 2 0 0 1 2 Setting B: Space Group 14:b1 C2h^5 P21/c:b1 = P121/c1 -P 2ybc Point Group 2/m Laue Group 2/m Monoclinic Unique Axis y Order 4 Order P 4 s.i.Vector Modulus 1 0 0 2 0 1 0 2 0 0 1 2 Change of Basis Setting A -> Setting B: CBMx = x-y, z, -y InvCBMx = x-z, -z, y Setting A UnitCell 8.63289 9.10859 17.699 90 90 108.45 Setting B UnitCell 8.63289 17.699 10.3789 90 123.644 90
If you have the ITVA at hand, it is not a big trick to obtain the standard setting P 1 21/c 1. What is more of a problem is to find the correct change-of-basis matrix, even if you have the ITVA. This is where sginfo steps in and can save you a lot of cumbersome reasoning. Having CBMx, it is now straightforward to compute the new cell parameters.
The formula for the transformation of the metrical matrix G(A) of Setting A to the metrical matrix G(B) of Setting B is (see e.g. Boisen & Gibbs [2]):
G(B) = transpose(InvCBMx) * G(A) * InvCBMx
By applying this formula sginfo finally produces the cell parameters for Setting B.
For those who not want to learn more about SgInfo at this point and want to play on their own machine right away: here are some hot links:
For all who want to test their compilier: some source code:
You want more Space Group Info?
There is more available:
Warning:
If you do not know the basics of C,
the rest of this document will be very boring.
The SgInfo library routines are grouped into five C source code files and one C header file:
sginfo.h header file with global definitions sgclib.c core library routines sgio.c input/output routines sgfind.c space group finding routines sghkl.c reciprocal space routines sgsi.c semi-invariant routines
To demonstrate how to use the library there are two additional files with driver routines:
sginfo.c the driver for the examples above sgquick.c a very simple template driver
Have a look at sgquick.c. This driver takes one command line argument, e.g. sgquick "VolA P2", and sends this string to BuildSpgrInfo().
The first half of BuildSgInfo() does nothing more than splitting the input string SgName into the part which selects the conventions (like the -Hall|VolA|VolI options in the preceding examples) and the space group symbol itself.
The first call to the SgInfo library is FindTabSgNameEntry(). This routine maps space group numbers, Schönflies symbols, and Hermann-Mauguin symbols to Hall symbols.
In the next section we allocate memory to hold the Seitz matrices and some additional parameters. Then the SgInfo structure is initialized, the Hall symbol is translated to Seitz matrices, thereby generating the whole group through multiplication.
The final step is a call to CompleteSgInfo(). This routine sorts and reduces the list of Seitz matrices, determines crystal system and point group, and a new Hall symbol is derived from the symmetry operations. This new Hall symbol is used to find the entry in the internal table; if the entry is known in advance, SgInfo just verifies if the new result is the same (or signals an "Internal Error" otherwise). If there is no matching entry, SgInfo does not report the ITVA space group name and number at this point.
#include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include "sginfo.h" static int str_ibegin(const char *s1, const char *s2) /* string ignore-case */ { /* begin */ char u1, u2; while (*s1 && *s2) { u1 = toupper(*s1++); u2 = toupper(*s2++); if (u1 < u2) return -1; else if (u1 > u2) return 1; } if (*s2) return -1; return 0; } int BuildSgInfo(T_SgInfo *SgInfo, const char *SgName) { int VolLetter; const T_TabSgName *tsgn; /* look for "VolA", "VolI", or "Hall" */ while (*SgName && isspace(*SgName)) SgName++; VolLetter = -1; if (isdigit(*SgName)) VolLetter = 'A'; else if (str_ibegin(SgName, "VolA") == 0) { VolLetter = 'A'; SgName += 4; } else if ( str_ibegin(SgName, "VolI") == 0 || str_ibegin(SgName, "Vol1") == 0) { VolLetter = 'I'; SgName += 4; } else if (str_ibegin(SgName, "Hall") == 0) { VolLetter = 0; SgName += 4; } while (*SgName && isspace(*SgName)) SgName++; /* default is "VolA" */ if (VolLetter == -1) VolLetter = 'A'; /* if we do not have a Hall symbol do a table look-up */ tsgn = NULL; if (VolLetter) { tsgn = FindTabSgNameEntry(SgName, VolLetter); if (tsgn == NULL) return -1; /* no matching table entry */ SgName = tsgn->HallSymbol; } /* Allocate memory for the list of Seitz matrices and a supporting list which holds the characteristics of the rotation parts of the Seitz matrices */ SgInfo->MaxList = 192; /* absolute maximum number of symops */ SgInfo->ListSeitzMx = malloc(SgInfo->MaxList * sizeof (*SgInfo->ListSeitzMx)); if (SgInfo->ListSeitzMx == NULL) { SetSgError("Not enough core"); return -1; } SgInfo->ListRotMxInfo = malloc(SgInfo->MaxList * sizeof (*SgInfo->ListRotMxInfo)); if (SgInfo->ListRotMxInfo == NULL) { SetSgError("Not enough core"); return -1; } /* Initialize the SgInfo structure */ InitSgInfo(SgInfo); SgInfo->TabSgName = tsgn; /* in case we know the table entry */ /* Translate the Hall symbol and generate the whole group */ ParseHallSymbol(SgName, SgInfo); if (SgError != NULL) return -1; /* Do some book-keeping and derive crystal system, point group, and - if not already set - find the entry in the internal table of space group symbols */ return CompleteSgInfo(SgInfo); } int main(int argc, char *argv[]) { T_SgInfo SgInfo; if (argc == 2) { if (BuildSgInfo(&SgInfo, argv[1]) != 0) fprintf(stderr, "%s\n", SgError); else { ListSgInfo(&SgInfo, 1, 0, stdout); if (SgError) fprintf(stderr, "%s\n", SgError); } } return 0; }
Of course there is no big point in passing a pointer to a structure to some strange routine without knowing what you get back. We need to investigate the definition of T_SgInfo, taken from sginfo.h:
typedef struct { int GenOption; int Centric; int InversionOffOrigin; const T_LatticeInfo *LatticeInfo; int StatusLatticeTr; int OriginShift[3]; int nList; int MaxList; T_RTMx *ListSeitzMx; T_RotMxInfo *ListRotMxInfo; int OrderL; int OrderP; int XtalSystem; int UniqueRefAxis; int UniqueDirCode; int ExtraInfo; int PointGroup; int nGenerator; int Generator_iList[4]; char HallSymbol[MaxLenHallSymbol + 1]; const T_TabSgName *TabSgName; const int *CCMx_LP; int n_si_Vector; int si_Vector[9]; int si_Modulus[3]; } T_SgInfo;
Live is not that easy, the definition of T_SgInfo obviously needs four previous definitions, namely of T_LatticeInfo, T_RTMx, T_RotMxInfo, and T_TabSgName.
Now we approach T_SgInfo the following way: we just want to know how to get hold of the symmetry matrices.
In case of a primitive and acentric space group this is pretty simple: you need
Here we go:
typedef union { struct { int R[9], T[3]; } s; int a[12]; } T_RTMx;
T_RTMx is a union of a struct with 12 int and a simple array of 12 int.
If you are scared by unions of structs and arrays, you might be delighted to learn that T_LatticeInfo as well as T_RotMxInfo as well as T_TabSgName are simple structs of basic data types. But we save this for later.
How does a "Rotation-Translation-Matrix" T_RTMx look like for, e.g., a 2-fold screw along z, also known as as "-x, -y, z+1/2"?
The debugger always knows the truth:
(dbx) print SgInfo->ListSeitzMx[1] union { s = struct { R = { [0] -1 [1] 0 [2] 0 [3] 0 [4] -1 [5] 0 [6] 0 [7] 0 [8] 1 } T = { [0] 0 [1] 0 [2] 6 } } a = { [0] -1 [1] 0 [2] 0 [3] 0 [4] -1 [5] 0 [6] 0 [7] 0 [8] 1 [9] 0 [10] 0 [11] 6 } }
The rotation part R is pretty obvious,
"1" encodes
"x"
or "y"
or "z",
depending on the position;
"-1" encodes
"-x"
or "-y"
or "-z".
The translation part T needs a further definition:
the Seitz Matrix Translation Base Factor
"STBF". This factor is defined to be
12 in sginfo.h.
Therefore T[2] = 6 translates
to "+6/12 = +1/2".
Using the ITVA "augmented 4*4 matrix" notation, the mapping of a position (x,y,z) to the symmetry equivalent position (xs,ys,zs) by an element of SgInfo.ListSeitzMx can be summerized by:
( xs ) ( R[0] R[1] R[2] T[0]/STBF ) ( x ) ( ys ) = ( R[3] R[4] R[5] T[1]/STBF ) ( y ) ( zs ) ( R[6] R[7] R[8] T[2]/STBF ) ( z ) ( 1 ) ( 0 0 0 1 ) ( 1 )
For primitve centro-symmetric space groups things are slightly more involved. You have to evaluate two variables, SgInfo.Centric and SgInfo.InversionOffOrigin.
If - after calling CompleteSgInfo() - both variables are set to 0, the space group is acentric. If SgInfo.Centric is set to -1, there is an inversion operation at the origin and SgInfo.ListSeitzMx will contain only an acentric subset of the space group. If you need all symmetry operations you have to - mathematically spoken - multiply each of the matrices in SgInfo.ListSeitzMx with the inversion matrix. In practive this means you simply have to take each element of a matrix with reversed sign.
If SgInfo.Centric is 0 but SgInfo.InversionOffOrigin is set to 1, SgInfo.ListSeitzMx will contain the whole (primitive) space group. No further action has to be taken to get all symmetry operations. In fact, if you only need to know how to get all operations, you just need to take care of SgInfo.Centric.
typedef struct { int Code; int nTrVector; const int *TrVector; } T_LatticeInfo;
LatticeInfo.Code is one of {'P', 'A', 'B', 'C', 'I', 'R', 'S', 'T', 'F'}. Except for 'S' and 'T' this should be clear. For now forget about these strange lattice codes 'S' and 'T'.
Understanding LatticeInfo.nTrVector is equally straightforward. It gives the number of lattice translation vectors which are stored in the location to which LatticeInfo.TrVector points.
TrVector[0..2] gives the first vector (in any case { 0,0,0 }), TrVector[3..5] gives the second, and so on, four at maximum. The elements of LatticeInfo.TrVector are multiplied by STBF, hence compatible to the translation parts of the Seitz matrices.
A little table summarizes all possibilities for SgInfo.LatticeInfo:
Code nTrVector TrVector 'P' 1 0,0,0 'A' 2 0,0,0 0 ,1/2,1/2 'B' 2 0,0,0 1/2, 0 ,1/2 'C' 2 0,0,0 1/2,1/2, 0 'I' 2 0,0,0 1/2,1/2,1/2 'R' 3 0,0,0 2/3,1/3,1/3 1/3,2/3,2/3 'S' 3 0,0,0 1/3,1/3,2/3 2/3,2/3,1/3 'T' 3 0,0,0 1/3,2/3,1/3 2/3,1/3,2/3 'F' 4 0,0,0 0 ,1/2,1/2 1/2, 0 ,1/2 1/2,1/2, 0
Maybe the whole SgInfo structure stuff got somewhat abstract. A small piece of code says more than 1000 words!
So far you have seen how to pass a space group symbol or number to SgInfo and how to obtain the symmetry matrices. But this is not all you can do with SgInfo.
Say, you have a program which takes atom coordinates, looks for symmetry elements and produces a list of the operations found. Now you can either take the International Tables to find out what space group you are dealing with, or you can make life easier for yourself and use SgInfo: just pass the list of operations to the library and wait a few milli-seconds for the space group finder to do the job for you. You will not only get the space group number, but also the change-of-basis matrix from your arbitrary setting to a reference setting.
Here is how to do that:
#include <everything_you_like.h> #include "sginfo.h" int main(int argc, char *argv[]) { /* This is your program, you do anything you can to find weird symmetry matrices. You did not forget to declare SgInfo plus making two calls to malloc() to get memory for SgInfo.ListSeitzMx and SgInfo.ListRotMxInfo. Copy this from sgquick.c. This time you do not have space group symbols in advance and you do not need the table look-up. You just start with: */ InitSgInfo(&SgInfo); while (you_think_you_have_to_loop) { /* Fine. Now, everytime you found a new operation, you set up a buffer SeitzMx (do never, never, never ever set SgInfo.ListSeitzMx directly!). Passing the buffer SeitzMx to the library is exceptionally simple: */ if (Add2ListSeitzMx(&SgInfo, &SeitzMx) < 0) { /* if Add2ListSeitzMx() returns a value < 0 an error has occured and the global variable SgError has been set. A possible consequence: */ fprintf(stderr, "%s\n", SgError); exit(1); } } /* Fine. After you have passed _all_ symmetry operations - including a possible inversion operation and lattice centring vectors (rotation part of SeitzMx = identity, translation part = centring vector) - via Add2ListSeitzMx() you are ready to call: */ if (CompleteSgInfo(&SgInfo) != 0) { /* an error has occured, evaluate SgError, terminate */ } /* Fine. Now you know you have a "legal" space group. Maybe you are lucky and you hit a setting which is in the internal table. Simply ask: */ if (SgInfo.TabSgName != NULL) { /* Yes, really! If you are not interested in change-of-basis matrices you are done. You could, e.g., print out the table entry and terminate: */ PrintTabSgNameEntry(SgInfo.TabSgName, 0, 0, stdout); putc('\n', stdout); exit(0); } /* Hm. Now you have to have declared const T_TabSgName *ReferenceTabSgName; T_RTMx CBMx, InvCBMx; somewhere before. Try the hard way: */ ReferenceTabSgName = FindReferenceSpaceGroup(&SgInfo, &CBMx, &InvCBMx); if (ReferenceTabSgName == NULL) { /* This is really bad! If you arrive here an internal error has occured and SgError has been set. This means, either you have corrupted SgInfo in some way or I (see bottom line) have made some stupid error. Let me know! Do it right now! */ } /* Bingo! For the sake of simplicity we ignore CBMx and InvCBMx here. Print the reference setting and finish: */ PrintTabSgNameEntry(ReferenceTabSgName, 0, 0, stdout); putc('\n', stdout); return 0; } /* P.S.: If you made it to this point you might like to learn that there are two routines which help you adding the inversion operation and lattice centring vectors: AddInversion2ListSeitzMx(); AddLatticeTr2ListSeitzMx(); Look for more details in the reference section. */
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
(1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
(3) Neither the name of the SgInfo - Space Group Info copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You are under no obligation whatsoever to provide any bug fixes, patches, or upgrades to the features, functionality or performance of the source code ("Enhancements") to anyone; however, if you choose to make your Enhancements available either publicly, or directly to the SgInfo - Space Group Info copyright holder, without imposing a separate written license agreement for such Enhancements, then you hereby grant the following license: a nonexclusive, royalty-free perpetual license to install, use, modify, prepare derivative works, incorporate into other computer software, distribute, and sublicense such enhancements or derivative works thereof, in binary and source code form.