Codebase list xrayutilities / b10ccb9
New upstream version 1.6.0 Alexandre Marie 4 years ago
206 changed file(s) with 48777 addition(s) and 48748 deletion(s). Raw diff Collapse all Expand all
0 v1.6.0, 2020-01-08
1
2 * use of tox to run unittests consistently in a virtual environment
3 * build element database during installation from sources
4 * new source layout which offers many advantages
5 (https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure)
6 * remove Python2 code path, improve code readability
7 * fix use of the agg matplotlib backend with FitModel
8 * from this point on Github master will be broken on Python 2
9
10 v1.5.4, 2020-01-07 final python2 compatible release
11
12 * fix issue #84: edge case problemes in XRDML parser
13 * matplotlib compatibility improvements
14
015 v1.5.3, 2019-10-09
116
217 * issue #87: fix matplotlib inline backend in FitModel.fit
00 include LICENSE.txt
11 include CHANGES.txt
2 include VERSION
32
43 # documentation files
54 include README.md
1110 graft examples/data
1211
1312 # C source files and tests
14 graft xrayutilities/src
13 graft src
1514 recursive-include tests *.py
1615
1716 # include database files
18 graft xrayutilities/materials/data
17 graft lib/xrayutilities/materials/data
1918
2019 # exclude development files
2120 global-exclude .gitignore
2221 exclude .travis.yml
2322 exclude appveyor.yml
23 exclude .coveragerc
24 exclude tox.ini
25 exclude release.txt
00 Metadata-Version: 2.1
11 Name: xrayutilities
2 Version: 1.5.3
2 Version: 1.6.0
33 Summary: package for x-ray diffraction data evaluation
44 Home-page: http://xrayutilities.sourceforge.net
55 Author: Eugen Wintersberger, Dominik Kriegner
1111 =============
1212
1313 [![Build
14 Status Travis CI](https://travis-ci.org/dkriegner/xrayutilities.svg?branch=master)](https://travis-ci.org/dkriegner/xrayutilities)
14 Status Travis CI](https://travis-ci.com/dkriegner/xrayutilities.svg?branch=master)](https://travis-ci.com/dkriegner/xrayutilities)
1515 [![Build Status AppVeyor](https://ci.appveyor.com/api/projects/status/t8cb5jj0atklxay3/branch/master?svg=true)](https://ci.appveyor.com/project/dkriegner/xrayutilities)
1616
1717
2424 diffraction is included.
2525
2626
27 Copyright (C) 2009-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
27 Copyright (C) 2009-2020 Dominik Kriegner <dominik.kriegner@gmail.com>
2828
2929 Copyright (C) 2009-2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
3030
8989 ------------
9090 The following requirements are needed for installing and using *xrayutilities*:
9191
92 - Python (version 2.7 or >= 3.2)
93 - C-compiler (preferential with OpenMP support)
92 - Python (>= 3.3, for Python 2.7 support use version up to 1.5.x)
9493 - h5py
9594 - scipy (version >= 0.13.0)
96 - numpy (version >= 1.8)
95 - numpy (version >= 1.9)
9796 - setuptools (to provide the pkg_resources module)
9897 - lmfit (optional)
9998 - matplotlib (optional)
100 - python-lzma (optional)
10199
102100 When building from source you also might need:
103101
102 - C-compiler (preferential with OpenMP support)
104103 - python dev headers
105104 - unittest2 (optional - only if you want to run the tests)
105 - matplotlib (optional - only if running the tests/example scripts)
106106 - sphinx (optional - only when you want to build the documentation)
107107 - numpydoc (optional - only when you want to build the documentation)
108 - rst2pdf (optional - only when you want to build the documentation)
108109
109110 refer to your operating system documentation to find out how to install
110111 those packages. On Microsoft Windows refer to the Documentation for the
111 easiest way of the installation (Python(x,y) or WinPython).
112 easiest way of the installation (Anaconda, Python(x,y), or WinPython).
112113
113114 Python-2.7 and Python-3.X compatibility
114115 =======================================
115116
116 The current development focuses on Python-3.X and we ask all users to update to
117 Python-3 if possible, however, xrayutilities can be used with Python-2.7 as
118 well. Care was taken to make this possible from the same code-base.
117 The current development is for Python-3.X only. xrayutilities up to version
118 1.5.x can be used with Python-2.7 as well.
119119
120120 The Python package configuration
121121 ================================
196196
197197 Platform: UNKNOWN
198198 Classifier: Programming Language :: C
199 Classifier: Programming Language :: Python :: 2.7
200 Classifier: Programming Language :: Python :: 3.2
201199 Classifier: Programming Language :: Python :: 3.3
202200 Classifier: Programming Language :: Python :: 3.4
203201 Classifier: Programming Language :: Python :: 3.5
207205 Classifier: Intended Audience :: Science/Research
208206 Classifier: Development Status :: 5 - Production/Stable
209207 Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
210 Provides-Extra: lzma
208 Requires-Python: ~=3.3
209 Description-Content-Type: text/markdown
211210 Provides-Extra: plot
212211 Provides-Extra: fit
212 Provides-Extra: lzma
11 =============
22
33 [![Build
4 Status Travis CI](https://travis-ci.org/dkriegner/xrayutilities.svg?branch=master)](https://travis-ci.org/dkriegner/xrayutilities)
4 Status Travis CI](https://travis-ci.com/dkriegner/xrayutilities.svg?branch=master)](https://travis-ci.com/dkriegner/xrayutilities)
55 [![Build Status AppVeyor](https://ci.appveyor.com/api/projects/status/t8cb5jj0atklxay3/branch/master?svg=true)](https://ci.appveyor.com/project/dkriegner/xrayutilities)
66
77
1414 diffraction is included.
1515
1616
17 Copyright (C) 2009-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
17 Copyright (C) 2009-2020 Dominik Kriegner <dominik.kriegner@gmail.com>
1818
1919 Copyright (C) 2009-2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
2020
7979 ------------
8080 The following requirements are needed for installing and using *xrayutilities*:
8181
82 - Python (version 2.7 or >= 3.2)
83 - C-compiler (preferential with OpenMP support)
82 - Python (>= 3.3, for Python 2.7 support use version up to 1.5.x)
8483 - h5py
8584 - scipy (version >= 0.13.0)
86 - numpy (version >= 1.8)
85 - numpy (version >= 1.9)
8786 - setuptools (to provide the pkg_resources module)
8887 - lmfit (optional)
8988 - matplotlib (optional)
90 - python-lzma (optional)
9189
9290 When building from source you also might need:
9391
92 - C-compiler (preferential with OpenMP support)
9493 - python dev headers
9594 - unittest2 (optional - only if you want to run the tests)
95 - matplotlib (optional - only if running the tests/example scripts)
9696 - sphinx (optional - only when you want to build the documentation)
9797 - numpydoc (optional - only when you want to build the documentation)
98 - rst2pdf (optional - only when you want to build the documentation)
9899
99100 refer to your operating system documentation to find out how to install
100101 those packages. On Microsoft Windows refer to the Documentation for the
101 easiest way of the installation (Python(x,y) or WinPython).
102 easiest way of the installation (Anaconda, Python(x,y), or WinPython).
102103
103104 Python-2.7 and Python-3.X compatibility
104105 =======================================
105106
106 The current development focuses on Python-3.X and we ask all users to update to
107 Python-3 if possible, however, xrayutilities can be used with Python-2.7 as
108 well. Care was taken to make this possible from the same code-base.
107 The current development is for Python-3.X only. xrayutilities up to version
108 1.5.x can be used with Python-2.7 as well.
109109
110110 The Python package configuration
111111 ================================
+0
-1
VERSION less more
0 1.5.3
5353
5454 # General information about the project.
5555 project = u'xrayutilities'
56 copyright = u'2019, Dominik Kriegner, Eugen Wintersberger'
56 copyright = u'2020, Dominik Kriegner, Eugen Wintersberger'
5757
5858 # The version info for the project you're documenting, acts as replacement for
5959 # |version| and |release|, also used in various other places throughout the
6060 # built documents.
6161 #
62 with open(os.path.join('..', '..', 'VERSION')) as version_file:
62 moduleroot = os.path.join('..', '..', 'lib', 'xrayutilities')
63 with open(os.path.join(moduleroot, 'VERSION')) as version_file:
6364 fullversion = version_file.read().strip()
6465 # The short X.Y version.
6566 version = re.match(r'^(\d+\.)?(\d+)?', fullversion).group()
0 cxrayutilities module
1 =====================
2
3 .. automodule:: cxrayutilities
4 :members:
5 :undoc-members:
6 :show-inheritance:
173173 To keep the coding effort as small as possible *xrayutilities* depends on a
174174 large number of third party libraries and Python modules.
175175
176 The needed dependencies are:
176 The needed runtime dependencies are:
177 * **Python** the scripting language in which most of *xrayutilities* code is written in. (>= 3.3, for Python 2.7 use *xrayutilities* 1.5.X or older)
178 * **setuptools** python package installer
179 * **Numpy** a Python module providing numerical array objects (version >= 1.9)
180 * **Scipy** a Python module providing standard numerical routines, which is heavily using numpy arrays (version >= 0.13.0)
181 * **h5py** a powerful Python interface to HDF5.
182 * **setuptools** needed to provide the *pkg_resources* module
183
184 For several features optional dependencies are needed:
185 * **Matplotlib** a Python module for high quality 1D and 2D plotting (optional)
186 * **lmfit** a Python module for least-squares minimization with bounds and constraints (optionally needed for fitting XRR/XRD data)
187 * **IPython** although not a dependency of *xrayutilities* the IPython shell is perfectly suited for the interactive use of the *xrayutilities* python package.
188
189 Additionally, the following Python modules are needed when building *xrayutilities* from source:
177190 * **C-compiler** Gnu Compiler Collection or any compatible C compiler. On windows you most probably want to use the Microsoft compilers.
178 * **HDF5** a versatile binary data format (library is implemented in C).
179 Although the library is not called directly, it is needed by the h5py Python
180 module (see below).
181 * **Python** the scripting language in which most of *xrayutilities* code is written in. (version 2.7 or >= 3.2, including python dev headers)
182 * **setuptools** python package installer
183 * **git** a version control system used to keep track on the *xrayutilities* development. (only needed for development)
184
185 Additionally, the following Python modules are needed in order to make *xrayutilities* work as intended:
186 * **Numpy** a Python module providing numerical array objects (version >= 1.9)
187 * **Scipy** a Python module providing standard numerical routines, which is heavily using numpy arrays (version >= 0.11.0)
188 * **h5py** a powerful Python interface to HDF5.
189 * **Matplotlib** a Python module for high quality 1D and 2D plotting (optionally)
190 * **lmfit** a Python module for least-squares minimization with bounds and constraints (optionally needed for fitting XRR/XRD data)
191 * **lzma** a Python module to (un)compress .xz files (included in the standard library in Python versions >=3.3) (optional)
192 * **IPython** although not a dependency of *xrayutilities* the IPython shell is perfectly suited for the interactive use of the *xrayutilities* python package.
191 * **Python dev header**
192 * **unittest2** needed for running the unit tests (optional)
193193
194194 For building the documention (which you do not need to do) the requirements are:
195195 * **sphinx** the Python documentation generator
77 ----------------------------------------
88
99 .. automodule:: xrayutilities.analysis.line_cuts
10 :members:
11 :undoc-members:
12 :show-inheritance:
10 :members:
11 :undoc-members:
12 :show-inheritance:
1313
1414 xrayutilities.analysis.misc module
1515 ----------------------------------
1616
1717 .. automodule:: xrayutilities.analysis.misc
18 :members:
19 :undoc-members:
20 :show-inheritance:
18 :members:
19 :undoc-members:
20 :show-inheritance:
2121
2222 xrayutilities.analysis.sample\_align module
2323 -------------------------------------------
2424
2525 .. automodule:: xrayutilities.analysis.sample_align
26 :members:
27 :undoc-members:
28 :show-inheritance:
26 :members:
27 :undoc-members:
28 :show-inheritance:
2929
3030
3131 Module contents
3232 ---------------
3333
3434 .. automodule:: xrayutilities.analysis
35 :members:
36 :undoc-members:
37 :show-inheritance:
35 :members:
36 :undoc-members:
37 :show-inheritance:
77 ---------------------------
88
99 .. automodule:: xrayutilities.io.cbf
10 :members:
11 :undoc-members:
12 :show-inheritance:
10 :members:
11 :undoc-members:
12 :show-inheritance:
1313
1414 xrayutilities.io.desy\_tty08 module
1515 -----------------------------------
1616
1717 .. automodule:: xrayutilities.io.desy_tty08
18 :members:
19 :undoc-members:
20 :show-inheritance:
18 :members:
19 :undoc-members:
20 :show-inheritance:
2121
2222 xrayutilities.io.edf module
2323 ---------------------------
2424
2525 .. automodule:: xrayutilities.io.edf
26 :members:
27 :undoc-members:
28 :show-inheritance:
26 :members:
27 :undoc-members:
28 :show-inheritance:
2929
3030 xrayutilities.io.fastscan module
3131 --------------------------------
3232
3333 .. automodule:: xrayutilities.io.fastscan
34 :members:
35 :undoc-members:
36 :show-inheritance:
34 :members:
35 :undoc-members:
36 :show-inheritance:
3737
3838 xrayutilities.io.filedir module
3939 -------------------------------
4040
4141 .. automodule:: xrayutilities.io.filedir
42 :members:
43 :undoc-members:
44 :show-inheritance:
42 :members:
43 :undoc-members:
44 :show-inheritance:
4545
4646 xrayutilities.io.helper module
4747 ------------------------------
4848
4949 .. automodule:: xrayutilities.io.helper
50 :members:
51 :undoc-members:
52 :show-inheritance:
50 :members:
51 :undoc-members:
52 :show-inheritance:
5353
5454 xrayutilities.io.ill\_numor module
5555 ----------------------------------
5656
5757 .. automodule:: xrayutilities.io.ill_numor
58 :members:
59 :undoc-members:
60 :show-inheritance:
58 :members:
59 :undoc-members:
60 :show-inheritance:
6161
6262 xrayutilities.io.imagereader module
6363 -----------------------------------
6464
6565 .. automodule:: xrayutilities.io.imagereader
66 :members:
67 :undoc-members:
68 :show-inheritance:
66 :members:
67 :undoc-members:
68 :show-inheritance:
6969
7070 xrayutilities.io.panalytical\_xml module
7171 ----------------------------------------
7272
7373 .. automodule:: xrayutilities.io.panalytical_xml
74 :members:
75 :undoc-members:
76 :show-inheritance:
74 :members:
75 :undoc-members:
76 :show-inheritance:
7777
7878 xrayutilities.io.pdcif module
7979 -----------------------------
8080
8181 .. automodule:: xrayutilities.io.pdcif
82 :members:
83 :undoc-members:
84 :show-inheritance:
82 :members:
83 :undoc-members:
84 :show-inheritance:
8585
8686 xrayutilities.io.rigaku\_ras module
8787 -----------------------------------
8888
8989 .. automodule:: xrayutilities.io.rigaku_ras
90 :members:
91 :undoc-members:
92 :show-inheritance:
90 :members:
91 :undoc-members:
92 :show-inheritance:
9393
9494 xrayutilities.io.rotanode\_alignment module
9595 -------------------------------------------
9696
9797 .. automodule:: xrayutilities.io.rotanode_alignment
98 :members:
99 :undoc-members:
100 :show-inheritance:
98 :members:
99 :undoc-members:
100 :show-inheritance:
101101
102102 xrayutilities.io.seifert module
103103 -------------------------------
104104
105105 .. automodule:: xrayutilities.io.seifert
106 :members:
107 :undoc-members:
108 :show-inheritance:
106 :members:
107 :undoc-members:
108 :show-inheritance:
109109
110110 xrayutilities.io.spec module
111111 ----------------------------
112112
113113 .. automodule:: xrayutilities.io.spec
114 :members:
115 :undoc-members:
116 :show-inheritance:
114 :members:
115 :undoc-members:
116 :show-inheritance:
117117
118118 xrayutilities.io.spectra module
119119 -------------------------------
120120
121121 .. automodule:: xrayutilities.io.spectra
122 :members:
123 :undoc-members:
124 :show-inheritance:
122 :members:
123 :undoc-members:
124 :show-inheritance:
125125
126126
127127 Module contents
128128 ---------------
129129
130130 .. automodule:: xrayutilities.io
131 :members:
132 :undoc-members:
133 :show-inheritance:
131 :members:
132 :undoc-members:
133 :show-inheritance:
77 -----------------------------------
88
99 .. automodule:: xrayutilities.materials.atom
10 :members:
11 :undoc-members:
12 :show-inheritance:
10 :members:
11 :undoc-members:
12 :show-inheritance:
1313
1414 xrayutilities.materials.cif module
1515 ----------------------------------
1616
1717 .. automodule:: xrayutilities.materials.cif
18 :members:
19 :undoc-members:
20 :show-inheritance:
18 :members:
19 :undoc-members:
20 :show-inheritance:
2121
2222 xrayutilities.materials.database module
2323 ---------------------------------------
2424
2525 .. automodule:: xrayutilities.materials.database
26 :members:
27 :undoc-members:
28 :show-inheritance:
26 :members:
27 :undoc-members:
28 :show-inheritance:
2929
3030 xrayutilities.materials.elements module
3131 ---------------------------------------
3232
3333 .. automodule:: xrayutilities.materials.elements
34 :members:
35 :undoc-members:
36 :show-inheritance:
34 :members:
35 :undoc-members:
36 :show-inheritance:
3737
3838 xrayutilities.materials.heuslerlib module
3939 -----------------------------------------
4040
4141 .. automodule:: xrayutilities.materials.heuslerlib
42 :members:
43 :undoc-members:
44 :show-inheritance:
42 :members:
43 :undoc-members:
44 :show-inheritance:
4545
4646 xrayutilities.materials.material module
4747 ---------------------------------------
4848
4949 .. automodule:: xrayutilities.materials.material
50 :members:
51 :undoc-members:
52 :show-inheritance:
50 :members:
51 :undoc-members:
52 :show-inheritance:
5353
5454 xrayutilities.materials.plot module
5555 -----------------------------------
5656
5757 .. automodule:: xrayutilities.materials.plot
58 :members:
59 :undoc-members:
60 :show-inheritance:
58 :members:
59 :undoc-members:
60 :show-inheritance:
6161
6262 xrayutilities.materials.predefined\_materials module
6363 ----------------------------------------------------
6464
6565 .. automodule:: xrayutilities.materials.predefined_materials
66 :members:
67 :undoc-members:
68 :show-inheritance:
66 :members:
67 :undoc-members:
68 :show-inheritance:
6969
7070 xrayutilities.materials.spacegrouplattice module
7171 ------------------------------------------------
7272
7373 .. automodule:: xrayutilities.materials.spacegrouplattice
74 :members:
75 :undoc-members:
76 :show-inheritance:
74 :members:
75 :undoc-members:
76 :show-inheritance:
7777
7878 xrayutilities.materials.wyckpos module
7979 --------------------------------------
8080
8181 .. automodule:: xrayutilities.materials.wyckpos
82 :members:
83 :undoc-members:
84 :show-inheritance:
82 :members:
83 :undoc-members:
84 :show-inheritance:
8585
8686
8787 Module contents
8888 ---------------
8989
9090 .. automodule:: xrayutilities.materials
91 :members:
92 :undoc-members:
93 :show-inheritance:
91 :members:
92 :undoc-members:
93 :show-inheritance:
77 ---------------------------------
88
99 .. automodule:: xrayutilities.math.algebra
10 :members:
11 :undoc-members:
12 :show-inheritance:
10 :members:
11 :undoc-members:
12 :show-inheritance:
1313
1414 xrayutilities.math.fit module
1515 -----------------------------
1616
1717 .. automodule:: xrayutilities.math.fit
18 :members:
19 :undoc-members:
20 :show-inheritance:
18 :members:
19 :undoc-members:
20 :show-inheritance:
2121
2222 xrayutilities.math.functions module
2323 -----------------------------------
2424
2525 .. automodule:: xrayutilities.math.functions
26 :members:
27 :undoc-members:
28 :show-inheritance:
26 :members:
27 :undoc-members:
28 :show-inheritance:
2929
3030 xrayutilities.math.misc module
3131 ------------------------------
3232
3333 .. automodule:: xrayutilities.math.misc
34 :members:
35 :undoc-members:
36 :show-inheritance:
34 :members:
35 :undoc-members:
36 :show-inheritance:
3737
3838 xrayutilities.math.transforms module
3939 ------------------------------------
4040
4141 .. automodule:: xrayutilities.math.transforms
42 :members:
43 :undoc-members:
44 :show-inheritance:
42 :members:
43 :undoc-members:
44 :show-inheritance:
4545
4646 xrayutilities.math.vector module
4747 --------------------------------
4848
4949 .. automodule:: xrayutilities.math.vector
50 :members:
51 :undoc-members:
52 :show-inheritance:
50 :members:
51 :undoc-members:
52 :show-inheritance:
5353
5454
5555 Module contents
5656 ---------------
5757
5858 .. automodule:: xrayutilities.math
59 :members:
60 :undoc-members:
61 :show-inheritance:
59 :members:
60 :undoc-members:
61 :show-inheritance:
55
66 .. toctree::
77
8 xrayutilities.analysis
9 xrayutilities.io
10 xrayutilities.materials
11 xrayutilities.math
12 xrayutilities.simpack
8 xrayutilities.analysis
9 xrayutilities.io
10 xrayutilities.materials
11 xrayutilities.math
12 xrayutilities.simpack
1313
1414 Submodules
1515 ----------
1818 ---------------------------
1919
2020 .. automodule:: xrayutilities.config
21 :members:
22 :undoc-members:
23 :show-inheritance:
21 :members:
22 :undoc-members:
23 :show-inheritance:
2424
2525 xrayutilities.exception module
2626 ------------------------------
2727
2828 .. automodule:: xrayutilities.exception
29 :members:
30 :undoc-members:
31 :show-inheritance:
29 :members:
30 :undoc-members:
31 :show-inheritance:
3232
3333 xrayutilities.experiment module
3434 -------------------------------
3535
3636 .. automodule:: xrayutilities.experiment
37 :members:
38 :undoc-members:
39 :show-inheritance:
37 :members:
38 :undoc-members:
39 :show-inheritance:
4040
4141 xrayutilities.gridder module
4242 ----------------------------
4343
4444 .. automodule:: xrayutilities.gridder
45 :members:
46 :undoc-members:
47 :show-inheritance:
45 :members:
46 :undoc-members:
47 :show-inheritance:
4848
4949 xrayutilities.gridder2d module
5050 ------------------------------
5151
5252 .. automodule:: xrayutilities.gridder2d
53 :members:
54 :undoc-members:
55 :show-inheritance:
53 :members:
54 :undoc-members:
55 :show-inheritance:
5656
5757 xrayutilities.gridder3d module
5858 ------------------------------
5959
6060 .. automodule:: xrayutilities.gridder3d
61 :members:
62 :undoc-members:
63 :show-inheritance:
61 :members:
62 :undoc-members:
63 :show-inheritance:
6464
6565 xrayutilities.mpl\_helper module
6666 --------------------------------
6767
6868 .. automodule:: xrayutilities.mpl_helper
69 :members:
70 :undoc-members:
71 :show-inheritance:
69 :members:
70 :undoc-members:
71 :show-inheritance:
7272
7373 xrayutilities.normalize module
7474 ------------------------------
7575
7676 .. automodule:: xrayutilities.normalize
77 :members:
78 :undoc-members:
79 :show-inheritance:
77 :members:
78 :undoc-members:
79 :show-inheritance:
8080
8181 xrayutilities.q2ang\_fit module
8282 -------------------------------
8383
8484 .. automodule:: xrayutilities.q2ang_fit
85 :members:
86 :undoc-members:
87 :show-inheritance:
85 :members:
86 :undoc-members:
87 :show-inheritance:
8888
8989 xrayutilities.utilities module
9090 ------------------------------
9191
9292 .. automodule:: xrayutilities.utilities
93 :members:
94 :undoc-members:
95 :show-inheritance:
93 :members:
94 :undoc-members:
95 :show-inheritance:
9696
9797 xrayutilities.utilities\_noconf module
9898 --------------------------------------
9999
100100 .. automodule:: xrayutilities.utilities_noconf
101 :members:
102 :undoc-members:
103 :show-inheritance:
101 :members:
102 :undoc-members:
103 :show-inheritance:
104104
105105
106106 Module contents
107107 ---------------
108108
109109 .. automodule:: xrayutilities
110 :members:
111 :undoc-members:
112 :show-inheritance:
110 :members:
111 :undoc-members:
112 :show-inheritance:
77 -------------------------------------------
88
99 .. automodule:: xrayutilities.simpack.darwin_theory
10 :members:
11 :undoc-members:
12 :show-inheritance:
10 :members:
11 :undoc-members:
12 :show-inheritance:
1313
1414 xrayutilities.simpack.fit module
1515 --------------------------------
1616
1717 .. automodule:: xrayutilities.simpack.fit
18 :members:
19 :undoc-members:
20 :show-inheritance:
18 :members:
19 :undoc-members:
20 :show-inheritance:
2121
2222 xrayutilities.simpack.helpers module
2323 ------------------------------------
2424
2525 .. automodule:: xrayutilities.simpack.helpers
26 :members:
27 :undoc-members:
28 :show-inheritance:
26 :members:
27 :undoc-members:
28 :show-inheritance:
2929
3030 xrayutilities.simpack.models module
3131 -----------------------------------
3232
3333 .. automodule:: xrayutilities.simpack.models
34 :members:
35 :undoc-members:
36 :show-inheritance:
34 :members:
35 :undoc-members:
36 :show-inheritance:
3737
3838 xrayutilities.simpack.mosaicity module
3939 --------------------------------------
4040
4141 .. automodule:: xrayutilities.simpack.mosaicity
42 :members:
43 :undoc-members:
44 :show-inheritance:
42 :members:
43 :undoc-members:
44 :show-inheritance:
4545
4646 xrayutilities.simpack.powder module
4747 -----------------------------------
4848
4949 .. automodule:: xrayutilities.simpack.powder
50 :members:
51 :undoc-members:
52 :show-inheritance:
50 :members:
51 :undoc-members:
52 :show-inheritance:
5353
5454 xrayutilities.simpack.powdermodel module
5555 ----------------------------------------
5656
5757 .. automodule:: xrayutilities.simpack.powdermodel
58 :members:
59 :undoc-members:
60 :show-inheritance:
58 :members:
59 :undoc-members:
60 :show-inheritance:
6161
6262 xrayutilities.simpack.smaterials module
6363 ---------------------------------------
6464
6565 .. automodule:: xrayutilities.simpack.smaterials
66 :members:
67 :undoc-members:
68 :show-inheritance:
66 :members:
67 :undoc-members:
68 :show-inheritance:
6969
7070
7171 Module contents
7272 ---------------
7373
7474 .. automodule:: xrayutilities.simpack
75 :members:
76 :undoc-members:
77 :show-inheritance:
75 :members:
76 :undoc-members:
77 :show-inheritance:
1717 +<a class="reference external image-reference" href="https://pypi.python.org/pypi/xrayutilities/"><img alt="Supported Python versions" style="max-height: 25px; max-width:193px;" src="https://img.shields.io/pypi/pyversions/xrayutilities.svg" /></a>
1818 +<a class="reference external image-reference" href="https://pypi.python.org/pypi/xrayutilities/"><img alt="Development Status" style="max-height: 25px; max-width:114px;" src="https://img.shields.io/pypi/status/xrayutilities.svg" /></a>
1919 +<a class="reference external image-reference" href="https://pypi.python.org/pypi/xrayutilities/"><img alt="License" style="max-height: 25px; max-width:119px;" src="https://img.shields.io/pypi/l/xrayutilities.svg" /></a>
20 +<a class="reference external image-reference" href="https://travis-ci.org/dkriegner/xrayutilities"><img alt="Test status" style="max-height: 25px; max-width:119px;" src="https://travis-ci.org/dkriegner/xrayutilities.svg?branch=master" /></a>
20 +<a class="reference external image-reference" href="https://travis-ci.com/dkriegner/xrayutilities"><img alt="Test status" style="max-height: 25px; max-width:119px;" src="https://travis-ci.com/dkriegner/xrayutilities.svg?branch=master" /></a>
2121 +<a class="reference external image-reference" href="https://ci.appveyor.com/project/dkriegner/xrayutilities"><img alt="Test status Windows" style="max-height: 25px; max-width:119px;" src="https://ci.appveyor.com/api/projects/status/t8cb5jj0atklxay3/branch/master?svg=true" /></a>
2222 +
2323 <p>If you look for downloading the package go to <a class="reference external" href="https://sourceforge.net/projects/xrayutilities">Sourceforge</a> or <a class="reference external" href="https://github.com/dkriegner/xrayutilities">GitHub</a> (source distribution) or the <a class="reference external" href="https://pypi.python.org/pypi/xrayutilities">Python package index</a> (MS Windows binary).</p>
2828
2929 import xrayutilities_id01_functions as id01
3030
31 s = xu.io.SPECFile(specfile)
31 s = xu.io.SPECFile(specfile) # insert specfile name here
3232 specscan = s.scan3
3333 en = id01.getmono_energy(specscan)
3434 # template for the CCD file names
1212 # You should have received a copy of the GNU General Public License
1313 # along with this program; if not, see <http://www.gnu.org/licenses/>.
1414 #
15 # Copyright (C) 2012-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
15 # Copyright (C) 2012-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
1616
1717 import numpy
1818 import xrayutilities as xu
3737 # printing of information about the defined material:
3838 print(InP)
3939
40 # A very primitive visualization of the unit cell can be performed using
41 InP.show_unitcell()
42 # for more sophisticated plotting I suggest you export a CIF file and use the
43 # software of your choice:
44 # InP.toCIF('filename.cif')
45
4046 # for some purposes it might be necessary to convert the SGLattice to space
4147 # group P1
4248 InP_p1 = xu.materials.Crystal(
2525
2626 import xrayutilities_id01_functions as id01
2727
28 sample = 'SAMPLENAME' # here used for the specfilename
2829 try:
2930 s
3031 except NameError:
4747 # three different visualization possibilities
4848 # plot the intensity as contour plot
4949 plt.figure(figsize=(12, 6))
50 ax = []
51 for i in range(1, 4):
52 ax.append(plt.subplot(1, 3, i))
5053
5154 MIN = 1
5255 MAX = 3e5
53 plt.subplot(131)
56 plt.sca(ax[0])
5457 plt.title('Gridder2D')
5558 # data on a regular grid of 200x800 points
5659 gridder = xu.Gridder2D(200, 300)
5861 cf = plt.pcolormesh(gridder.xaxis, gridder.yaxis, gridder.data.T,
5962 norm=LogNorm(MIN, MAX))
6063
61 plt.subplot(132)
64 plt.sca(ax[1])
6265 plt.title('FuzzyGridder2D')
6366 # data on a regular grid with FuzzyGridding
6467 gridder = xu.FuzzyGridder2D(200, 300)
6669 cf = plt.pcolormesh(gridder.xaxis, gridder.yaxis, gridder.data.T,
6770 norm=LogNorm(MIN, MAX))
6871
69 plt.subplot(133)
72 plt.sca(ax[2])
7073 plt.title('pcolormesh')
7174 # using pcolor-variants
7275 npixel = 255
7578 psd.shape = qy.shape
7679 plt.pcolormesh(qy, qz, psd, norm=LogNorm(MIN, MAX))
7780
78 for i in range(1, 4):
79 plt.subplot(1, 3, i)
81 for a in ax:
82 plt.sca(a)
8083 plt.xlabel(r'$Q_{[110]}$ ($\mathrm{\AA}^{-1}$)')
8184 plt.ylabel(r'$Q_{[001]}$ ($\mathrm{\AA}^{-1}$)')
8285 plt.xlim(-0.13, 0.13)
6363 # threshold = ccdraw.max()*0.1 #take 10% of maximum intensity
6464
6565 # determine hot pixels by comparison with the threshold value
66 hotpixelnumbers = numpy.where(ccdraw > threshold)
66 hotpixelnumbers = numpy.where(ccdavg > threshold)
1616
1717 # ALSO LOOK AT THE FILE xrayutilities_example_plot_3D_ESRF_ID01.py
1818
19 import collections
19 import collections.abc
2020 import re
2121
2222 import xrayutilities as xu
113113 nav = experiment._A2QConversion._area_nav
114114 roi = experiment._A2QConversion._area_roi
115115
116 if not isinstance(scannr, collections.Iterable):
116 if not isinstance(scannr, collections.abc.Iterable):
117117 scannr = [scannr, ]
118118 for snr in scannr:
119119 specscan = getattr(specfile, 'scan%d' % snr)
158158 plt.ylabel(r'$Q_{[\bar1\bar1\bar1]}$ ($\mathrm{\AA}^{-1}$)')
159159 cb = plt.colorbar(cf)
160160 cb.set_label(r"$\log($Int$)$ (cps)")
161
162 # clean up HDF5 file (not needed in real life!)
163 os.remove(h5file)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import xrayutilities as xu
18
19 # very often its useful/needed to get oriented in reciprocal space. The
20 # following convenience function can visualize the reciprocal space position of
21 # Bragg peaks and limitations of coplanar diffraction geometry (Laue zoens). If
22 # you use an interactive matplotlib backend you get the Miller indices of the
23 # Bragg peak upon mouse movement over the peak and its Bragg angles are printed
24 # to the console upon clicking. The printed angles correspond to the output of
25 # Q2Ang of the experimental class. All this is shown below for a substrate/film
26 # system.
27
28 Si = xu.materials.Si
29 Ge = xu.materials.Ge
30
31 geom = xu.HXRD(Si.Q(1, 1, -2), Si.Q(1, 1, 1))
32
33 ax, h = xu.materials.show_reciprocal_space_plane(Si, geom, ttmax=160)
34 ax, h2 = xu.materials.show_reciprocal_space_plane(Ge, geom, ax=ax)
35
36 ax.figure.show()
37
38 # with default settings only Bragg peaks close to the coplanar plane are shown.
39 # This can be changed by optional settings 'projection' and 'maxqout'
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 xrayutilities is a Python package for assisting with x-ray diffraction
20 experiments. Its the python package included in *xrayutilities*.
21
22 It helps with planning experiments as well as analyzing the data.
23
24 Authors:
25 Dominik Kriegner <dominik.kriegner@gmail.com> and
26 Eugen Wintersberger <eugen.wintersberger@desy.de>
27 """
28 import os
29
30 # load configuration
31 from . import __path__, analysis, config, io, materials, math, simpack
32 from .experiment import (GID, GISAXS, HXRD, Experiment, FourC, NonCOP,
33 PowderExperiment, QConversion)
34 from .gridder import FuzzyGridder1D, Gridder1D, npyGridder1D
35 from .gridder2d import FuzzyGridder2D, Gridder2D, Gridder2DList
36 from .gridder3d import FuzzyGridder3D, Gridder3D
37 from .normalize import (IntensityNormalizer, blockAverage1D, blockAverage2D,
38 blockAverageCCD, blockAveragePSD)
39 from .q2ang_fit import Q2AngFit
40 from .utilities import (clear_bit, en2lam, energy, lam2en, makeNaturalName,
41 maplog, set_bit, wavelength)
42
43 # load package version
44 with open(os.path.join(__path__[0], 'VERSION')) as version_file:
45 __version__ = version_file.read().strip()
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2011-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 xrayutilities.analysis is a package for assisting with the analysis of
19 x-ray diffraction data, mainly reciprocal space maps
20
21 Routines for obtaining line cuts from gridded reciprocal space maps are
22 offered, with the ability to integrate the intensity perpendicular to the
23 line cut direction.
24 """
25
26 from .line_cuts import (get_arbitrary_line, get_omega_scan, get_qx_scan,
27 get_qy_scan, get_qz_scan, get_radial_scan,
28 get_ttheta_scan)
29 from .misc import coplanar_intensity, getangles, getunitvector
30 from .sample_align import (area_detector_calib, area_detector_calib_hkl,
31 fit_bragg_peak, linear_detector_calib, miscut_calc,
32 psd_chdeg, psd_refl_align)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2018-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import numpy
18
19 from .. import config, math
20 from ..experiment import HXRD
21 from ..gridder import FuzzyGridder1D
22
23
24 def _get_cut(pos_along, pos_perp, intensity, dis, npoints):
25 """
26 obtain a line cut from 2D data using a FuzzyGridder1D to do the hard work.
27 Data points with value of pos_perp smaller than `dis` will be considered in
28 the line cut
29
30 Parameters
31 ----------
32 pos_along : array-like
33 position along the cut which should be taken
34 pos_perp : array-like
35 distance from the line cut axis. only data points with distance < `dis`
36 will be considered
37 intensity : array-like
38 data points, `pos_along`, `pos_perp`, and `intensity` must have the
39 same shape
40 dis : float
41 maximum distance to be allowed for contributing data points
42 npoints : int
43 number of points in the output data
44
45 Returns
46 -------
47 x : ndarray
48 gridded position along the cut axis
49 d : ndarray
50 gridded data values for every position `x` along the cut line
51 mask: ndarray
52 mask which is 1 for every used data point and 0 for the rest
53 """
54 pos_a = pos_along.ravel()
55 pos_p = pos_perp.ravel()
56 ma = numpy.abs(pos_p) < dis
57 g1d = FuzzyGridder1D(npoints)
58 width = (numpy.max(pos_a[ma]) - numpy.min(pos_a[ma])) / float(npoints)
59 g1d(pos_a[ma], intensity.ravel()[ma], width=width)
60 return g1d.xaxis, g1d.data, ma.astype(numpy.int8).reshape(intensity.shape)
61
62
63 def get_qz_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
64 r"""
65 extracts a qz scan from reciprocal space map data with integration along
66 either, the perpendicular plane in q-space, omega (sample rocking angle) or
67 2theta direction. For the integration in angular space (omega, or 2theta)
68 the coplanar diffraction geometry with qy and qz as diffraction plane is
69 assumed. This is consistent with the coplanar geometry implemented in the
70 HXRD-experiment class.
71
72 This function works for 2D and 3D input data in the same way!
73
74 Parameters
75 ----------
76 qpos : list of array-like objects
77 arrays of y, z (list with two components) or x, y, z (list with three
78 components) momentum transfers
79 intensity : array-like
80 2D or 3D array of reciprocal space intensity with shape equal to the
81 qpos entries
82 cutpos : float or tuple/list
83 x/y-position at which the line scan should be extracted. this must be a
84 float for 2D data and a tuple with two values for 3D data
85 npoints : int
86 number of points in the output data
87 intrange : float
88 integration range in along `intdir`, either in 1/\AA (`q`) or degree
89 ('omega', or '2theta'). data will be integrated from
90 `-intrange/2 .. +intrange/2`
91
92 intdir : {'q', 'omega', '2theta'}, optional
93 integration direction: 'q': perpendicular Q-plane (default), 'omega':
94 sample rocking angle, or '2theta': scattering angle.
95 wl : float or str, optional
96 wavelength used to determine angular integration positions
97
98 Note:
99 For 3D data the angular integration directions although applicable for
100 any set of data only makes sense when the data are aligned into the
101 y/z-plane.
102
103 Returns
104 -------
105 qz, qzint : ndarray
106 qz scan coordinates and intensities
107 used_mask : ndarray
108 mask of used data, shape is the same as the input intensity: True for
109 points which contributed, False for all others
110
111 Examples
112 --------
113 >>> qzcut, qzcut_int, mask = get_qz_scan([qy, qz], inten, 3.0, 200,
114 intrange=0.3)
115 """
116 intdir = kwargs.get('intdir', 'q')
117 lam = kwargs.get('wl', config.WAVELENGTH)
118 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
119
120 # make all data 3D
121 if len(qpos) == 2:
122 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
123 lcut = [0, cutpos]
124 else:
125 lqpos = qpos
126 lcut = cutpos
127
128 # make line cuts with selected integration direction
129 if intdir == 'q':
130 qperp = numpy.sqrt((lqpos[0]-lcut[0])**2 + (lqpos[1]-lcut[1])**2)
131 ret = _get_cut(lqpos[2], qperp, intensity, intrange/2., npoints)
132 elif intdir == 'omega':
133 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='realTilt')
134 q = 4 * numpy.pi / lam * numpy.sin(numpy.radians(tt/2))
135 ocut = numpy.degrees(numpy.arcsin(lcut[1]/q)) + tt / 2
136 qzpos = q * numpy.cos(numpy.radians(ocut - tt/2))
137 ret = _get_cut(qzpos, om-ocut, intensity, intrange/2., npoints)
138 elif intdir == '2theta':
139 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='realTilt')
140 q = 4 * numpy.pi / lam * numpy.sin(numpy.radians(tt/2))
141 ttcut = 2 * (om - numpy.degrees(numpy.arcsin(lcut[1]/q)))
142 qzpos = 4 * numpy.pi / lam * numpy.sin(numpy.radians(ttcut/2)) *\
143 numpy.cos(numpy.radians(om - ttcut/2))
144 ret = _get_cut(qzpos, tt-ttcut, intensity, intrange/2., npoints)
145
146 return ret
147
148
149 def get_qy_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
150 r"""
151 extracts a qy scan from reciprocal space map data with integration along
152 either, the perpendicular plane in q-space, omega (sample rocking angle) or
153 2theta direction. For the integration in angular space (omega, or 2theta)
154 the coplanar diffraction geometry with qy and qz as diffraction plane is
155 assumed. This is consistent with the coplanar geometry implemented in the
156 HXRD-experiment class.
157
158 This function works for 2D and 3D input data in the same way!
159
160 Parameters
161 ----------
162 qpos : list of array-like objects
163 arrays of y, z (list with two components) or x, y, z (list with three
164 components) momentum transfers
165 intensity : array-like
166 2D or 3D array of reciprocal space intensity with shape equal to the
167 qpos entries
168 cutpos : float or tuple/list
169 x/z-position at which the line scan should be extracted. this must be a
170 float for 2D data (z-position) and a tuple with two values for 3D data
171 npoints : int
172 number of points in the output data
173 intrange : float
174 integration range in along `intdir`, either in 1/\AA (`q`) or degree
175 ('omega', or '2theta'). data will be integrated from
176 `-intrange .. +intrange`
177
178 intdir : {'q', 'omega', '2theta'}, optional
179 integration direction: 'q': perpendicular Q-plane (default), 'omega':
180 sample rocking angle, or '2theta': scattering angle.
181 wl : float or str, optional
182 wavelength used to determine angular integration positions
183
184 Note:
185 For 3D data the angular integration directions although applicable for
186 any set of data only makes sense when the data are aligned into the
187 y/z-plane.
188
189 Returns
190 -------
191 qy, qyint : ndarray
192 qy scan coordinates and intensities
193 used_mask : ndarray
194 mask of used data, shape is the same as the input intensity: True for
195 points which contributed, False for all others
196
197 Examples
198 --------
199 >>> qycut, qycut_int, mask = get_qy_scan([qy, qz], inten, 5.0, 250,
200 intrange=0.02, intdir='2theta')
201 """
202 intdir = kwargs.get('intdir', 'q')
203 lam = kwargs.get('wl', config.WAVELENGTH)
204 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
205
206 # make all data 3D
207 if len(qpos) == 2:
208 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
209 lcut = [0, cutpos]
210 else:
211 lqpos = qpos
212 lcut = cutpos
213
214 # make line cuts with selected integration direction
215 if intdir == 'q':
216 qperp = numpy.sqrt((lqpos[0]-lcut[0])**2 + (lqpos[2]-lcut[1])**2)
217 ret = _get_cut(lqpos[1], qperp, intensity, intrange/2., npoints)
218 elif intdir == 'omega':
219 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='real')
220 q = 4 * numpy.pi / lam * numpy.sin(numpy.radians(tt/2))
221 ocut = tt / 2 + (numpy.sign(lqpos[1].ravel()) *
222 numpy.degrees(numpy.arccos(lcut[1]/q)))
223 qypos = q * numpy.sin(numpy.radians(ocut - tt/2))
224 ret = _get_cut(qypos, om-ocut, intensity, intrange/2., npoints)
225 elif intdir == '2theta':
226 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='real')
227 ttcut = (om - numpy.degrees(numpy.arcsin(numpy.sin(numpy.radians(om)) -
228 lcut[1]*lam/(2*numpy.pi)))) % 360
229 ttcut2 = (om +
230 numpy.degrees(numpy.arcsin(numpy.sin(numpy.radians(om)) -
231 lcut[1]*lam/(2*numpy.pi))) +
232 180) % 360
233 mask = numpy.abs(tt - om) > 90
234 ttcut[mask] = ttcut2[mask]
235 q = 4 * numpy.pi / lam * numpy.sin(numpy.radians(ttcut/2))
236 qypos = q * numpy.sin(numpy.radians(om - ttcut/2))
237 ret = _get_cut(qypos, tt-ttcut, intensity, intrange/2., npoints)
238
239 return ret
240
241
242 def get_qx_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
243 r"""
244 extracts a qx scan from 3D reciprocal space map data with integration along
245 either, the perpendicular plane in q-space, omega (sample rocking angle) or
246 2theta direction. For the integration in angular space (omega, or 2theta)
247 the coplanar diffraction geometry with qy and qz as diffraction plane is
248 assumed. This is consistent with the coplanar geometry implemented in the
249 HXRD-experiment class.
250
251 Parameters
252 ----------
253 qpos : list of array-like objects
254 arrays of x, y, z (list with three components) momentum transfers
255 intensity : array-like
256 3D array of reciprocal space intensity with shape equal to the
257 qpos entries
258 cutpos : tuple/list
259 y/z-position at which the line scan should be extracted. this must be
260 and a tuple/list with the qy, qz cut position
261 npoints : int
262 number of points in the output data
263 intrange : float
264 integration range in along `intdir`, either in 1/\AA (`q`) or degree
265 ('omega', or '2theta'). data will be integrated from
266 `-intrange .. +intrange`
267
268 intdir : {'q', 'omega', '2theta'}, optional
269 integration direction: 'q': perpendicular Q-plane (default), 'omega':
270 sample rocking angle, or '2theta': scattering angle.
271 wl : float or str, optional
272 wavelength used to determine angular integration positions
273
274 Note:
275 The angular integration directions although applicable for
276 any set of data only makes sense when the data are aligned into the
277 y/z-plane.
278
279 Returns
280 -------
281 qx, qxint : ndarray
282 qx scan coordinates and intensities
283 used_mask : ndarray
284 mask of used data, shape is the same as the input intensity: True for
285 points which contributed, False for all others
286
287 Examples
288 --------
289 >>> qxcut, qxcut_int, mask = get_qx_scan([qx, qy, qz], inten, [0, 2.0],
290 250, intrange=0.01)
291 """
292 intdir = kwargs.get('intdir', 'q')
293 lam = kwargs.get('wl', config.WAVELENGTH)
294 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
295
296 # make line cuts with selected integration direction
297 if intdir == 'q':
298 qperp = numpy.sqrt((qpos[1]-cutpos[0])**2 + (qpos[2]-cutpos[1])**2)
299 ret = _get_cut(qpos[0], qperp, intensity, intrange/2., npoints)
300 elif intdir == 'omega':
301 # needs testing
302 om, chi, phi, tt = hxrd.Q2Ang(*qpos, trans=False, geometry='realTilt')
303 ocut, dmy, dmy, ttcut = hxrd.Q2Ang(qpos[0], cutpos[0], cutpos[1],
304 trans=False, geometry='realTilt')
305 ret = _get_cut(qpos[0], om-ocut, intensity, intrange/2., npoints)
306 elif intdir == '2theta':
307 # needs testing
308 om, chi, phi, tt = hxrd.Q2Ang(*qpos, trans=False, geometry='realTilt')
309 ocut, dmy, dmy, ttcut = hxrd.Q2Ang(qpos[0], cutpos[0], cutpos[1],
310 trans=False, geometry='realTilt')
311 ret = _get_cut(qpos[0], tt-ttcut, intensity, intrange/2., npoints)
312
313 return ret
314
315
316 def get_omega_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
317 """
318 extracts an omega scan from reciprocal space map data with integration
319 along either the 2theta, or radial (omega-2theta) direction. The coplanar
320 diffraction geometry with qy and qz as diffraction plane is assumed. This
321 is consistent with the coplanar geometry implemented in the HXRD-experiment
322 class.
323
324 This function works for 2D and 3D input data in the same way!
325
326 Parameters
327 ----------
328 qpos : list of array-like objects
329 arrays of y, z (list with two components) or x, y, z (list with three
330 components) momentum transfers
331 intensity : array-like
332 2D or 3D array of reciprocal space intensity with shape equal to the
333 qpos entries
334 cutpos : tuple or list
335 y/z-position or x/y/z-position at which the line scan should be
336 extracted. this must be have two entries for 2D data (z-position) and a
337 three for 3D data
338 npoints : int
339 number of points in the output data
340 intrange : float
341 integration range in along `intdir` in degree. data will be integrated
342 from `-intrange .. +intrange`
343
344 intdir : {'2theta', 'radial'}, optional
345 integration direction: '2theta': scattering angle (default), or
346 'radial': omega-2theta direction.
347 wl : float or str, optional
348 wavelength used to determine angular integration positions
349
350 Note:
351 Although applicable for any set of data, the extraction only makes
352 sense when the data are aligned into the y/z-plane.
353
354 Returns
355 -------
356 om, omint : ndarray
357 omega scan coordinates and intensities
358 used_mask : ndarray
359 mask of used data, shape is the same as the input intensity: True for
360 points which contributed, False for all others
361
362 Examples
363 --------
364 >>> omcut, omcut_int, mask = get_omega_scan([qy, qz], inten, [2.0, 5.0],
365 250, intrange=0.1)
366 """
367 intdir = kwargs.get('intdir', '2theta')
368 lam = kwargs.get('wl', config.WAVELENGTH)
369 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
370
371 # make all data 3D
372 if len(qpos) == 2:
373 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
374 lcut = [0, cutpos[0], cutpos[1]]
375 else:
376 lqpos = qpos
377 lcut = cutpos
378
379 # make line cuts with selected integration direction
380 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='realTilt')
381 ocut, dmy, dmy, ttcut = hxrd.Q2Ang(*lcut, trans=False, geometry='realTilt')
382 if intdir == '2theta':
383 ret = _get_cut(om, tt-ttcut, intensity, intrange/2., npoints)
384 elif intdir == 'radial':
385 ret = _get_cut(om-(tt-ttcut)/2, tt-ttcut, intensity, intrange/2.,
386 npoints)
387
388 return ret
389
390
391 def get_radial_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
392 """
393 extracts a radial scan from reciprocal space map data with integration
394 along either the omega or 2theta direction. The coplanar
395 diffraction geometry with qy and qz as diffraction plane is assumed. This
396 is consistent with the coplanar geometry implemented in the HXRD-experiment
397 class.
398
399 This function works for 2D and 3D input data in the same way!
400
401 Parameters
402 ----------
403 qpos : list of array-like objects
404 arrays of y, z (list with two components) or x, y, z (list with three
405 components) momentum transfers
406 intensity : array-like
407 2D or 3D array of reciprocal space intensity with shape equal to the
408 qpos entries
409 cutpos : tuple or list
410 y/z-position or x/y/z-position at which the line scan should be
411 extracted. this must be have two entries for 2D data (z-position) and a
412 three for 3D data
413 npoints : int
414 number of points in the output data
415 intrange : float
416 integration range in along `intdir` in degree. data will be integrated
417 from `-intrange .. +intrange`
418
419 intdir : {'omega', '2theta'}, optional
420 integration direction: 'omega': sample rocking angle (default),
421 '2theta': scattering angle
422 wl : float or str, optional
423 wavelength used to determine angular integration positions
424
425 Note:
426 Although applicable for any set of data, the extraction only makes
427 sense when the data are aligned into the y/z-plane.
428
429 Returns
430 -------
431 tt, omttint : ndarray
432 omega-2theta scan coordinates (2theta values) and intensities
433 used_mask : ndarray
434 mask of used data, shape is the same as the input intensity: True for
435 points which contributed, False for all others
436
437 Examples
438 --------
439 >>> ttcut, omtt_int, mask = get_radial_scan([qy, qz], inten, [2.0, 5.0],
440 250, intrange=0.1)
441 """
442 intdir = kwargs.get('intdir', 'omega')
443 lam = kwargs.get('wl', config.WAVELENGTH)
444 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
445
446 # make all data 3D
447 if len(qpos) == 2:
448 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
449 lcut = [0, cutpos[0], cutpos[1]]
450 else:
451 lqpos = qpos
452 lcut = cutpos
453
454 # make line cuts with selected integration direction
455 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='realTilt')
456 ocut, dmy, dmy, ttcut = hxrd.Q2Ang(*lcut, trans=False, geometry='realTilt')
457 if intdir == 'omega':
458 ret = _get_cut(tt, om-((tt-ttcut)/2+ocut), intensity, intrange/2.,
459 npoints)
460 elif intdir == '2theta':
461 offcut = ttcut/2 - ocut
462 ret = _get_cut(tt-2*(tt/2-om-offcut), 2*(tt/2-om-offcut), intensity,
463 intrange/2., npoints)
464
465 return ret
466
467
468 def get_ttheta_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
469 """
470 extracts a 2theta scan from reciprocal space map data with integration
471 along either the omega or radial direction. The coplanar
472 diffraction geometry with qy and qz as diffraction plane is assumed. This
473 is consistent with the coplanar geometry implemented in the HXRD-experiment
474 class.
475
476 This function works for 2D and 3D input data in the same way!
477
478 Parameters
479 ----------
480 qpos : list of array-like objects
481 arrays of y, z (list with two components) or x, y, z (list with three
482 components) momentum transfers
483 intensity : array-like
484 2D or 3D array of reciprocal space intensity with shape equal to the
485 qpos entries
486 cutpos : tuple or list
487 y/z-position or x/y/z-position at which the line scan should be
488 extracted. this must be have two entries for 2D data (z-position) and a
489 three for 3D data
490 npoints : int
491 number of points in the output data
492 intrange : float
493 integration range in along `intdir` in degree. data will be integrated
494 from `-intrange .. +intrange`
495
496 intdir : {'omega', 'radial'}, optional
497 integration direction: 'omega': sample rocking angle (default),
498 'radial': omega-2theta direction
499 wl : float or str, optional
500 wavelength used to determine angular integration positions
501
502 Note:
503 Although applicable for any set of data, the extraction only makes
504 sense when the data are aligned into the y/z-plane.
505
506 Returns
507 -------
508 tt, ttint : ndarray
509 2theta scan coordinates and intensities
510 used_mask : ndarray
511 mask of used data, shape is the same as the input intensity: True for
512 points which contributed, False for all others
513
514 Examples
515 --------
516 >>> ttcut, tt_int, mask = get_ttheta_scan([qy, qz], inten, [2.0, 5.0],
517 250, intrange=0.1)
518 """
519 intdir = kwargs.get('intdir', 'omega')
520 lam = kwargs.get('wl', config.WAVELENGTH)
521 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
522
523 # make all data 3D
524 if len(qpos) == 2:
525 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
526 lcut = [0, cutpos[0], cutpos[1]]
527 else:
528 lqpos = qpos
529 lcut = cutpos
530
531 # make line cuts with selected integration direction
532 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='realTilt')
533 ocut, dmy, dmy, ttcut = hxrd.Q2Ang(*lcut, trans=False, geometry='realTilt')
534 if intdir == 'omega':
535 ret = _get_cut(tt, om-ocut, intensity, intrange/2., npoints)
536 elif intdir == 'radial':
537 ret = _get_cut(tt-2*(om-ocut), 2*(om-ocut), intensity,
538 intrange/2., npoints)
539
540 return ret
541
542
543 def get_arbitrary_line(qpos, intensity, point, vec, npoints, intrange):
544 """
545 extracts a line scan from reciprocal space map data along an arbitrary line
546 defined by the point 'point' and propergation vector 'vec'. Integration of
547 the data is performed in a cylindrical volume along the line.
548 This function works for 2D and 3D input data!
549
550 Parameters
551 ----------
552 qpos : list of array-like objects
553 arrays of x, y (list with two components) or x, y, z (list with three
554 components) momentum transfers
555 intensity : array-like
556 2D or 3D array of reciprocal space intensity with shape equal to the
557 qpos entries
558 point : tuple, list or array-like
559 point on the extraction line (2 or 3 coordinates)
560 vec : tuple, list or array-like
561 propergation vector of the extraction line (2 or 3 coordinates)
562 npoints : int
563 number of points in the output data
564 intrange : float
565 radius of the cylindrical integration volume around the extraction line
566
567 Returns
568 -------
569 qpos, qint : ndarray
570 line scan coordinates and intensities
571 used_mask : ndarray
572 mask of used data, shape is the same as the input intensity: True for
573 points which contributed, False for all others
574
575 Examples
576 --------
577 >>> qcut, qint, mask = get_arbitrary_line([qx, qy, qz], inten,
578 (1.1, 2.2, 0.0),
579 (1, 1, 1), 200, 0.1)
580 """
581 # make all data 3D
582 if len(qpos) == 2:
583 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
584 lpoint = [0, point[0], point[1]]
585 lvec = [0, vec[0], vec[1]]
586 else:
587 lqpos = qpos
588 lpoint = point
589 lvec = vec
590
591 # make line cut
592 lqpos = numpy.reshape(numpy.asarray([lqpos[0].ravel(), lqpos[1].ravel(),
593 lqpos[2].ravel()]).T, (-1, 3))
594 qalong = math.VecDot(lqpos, math.VecUnit(lvec))
595 qdistance = math.distance(lqpos[:, 0], lqpos[:, 1], lqpos[:, 2], lpoint,
596 lvec)
597 return _get_cut(qalong, qdistance, intensity, intrange, npoints)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2012-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 miscellaneous functions helpful in the analysis and experiment
19 """
20
21 import numpy
22
23 from .. import config, math
24
25
26 def getangles(peak, sur, inp):
27 """
28 calculates the chi and phi angles for a given peak
29
30 Parameters
31 ----------
32 peak : list or array-like
33 hkl for the peak of interest
34 sur : list or array-like
35 hkl of the surface
36 inp : list or array-like
37 inplane reference peak or direction
38
39 Returns
40 -------
41 list
42 [chi, phi] for the given peak on surface sur with inplane direction inp
43 as reference
44
45 Examples
46 --------
47 To get the angles for the -224 peak on a 111 surface type
48
49 >>> [chi, phi] = getangles([-2, 2, 4], [1, 1, 1], [2, 2, 4])
50 """
51
52 # transform input to numpy.arrays
53 peak = numpy.array(peak)
54 sur = numpy.array(sur)
55 inp = numpy.array(inp)
56
57 peak = peak / numpy.linalg.norm(peak)
58 sur = sur / numpy.linalg.norm(sur)
59 inp = inp / numpy.linalg.norm(inp)
60
61 # calculate reference inplane direction
62 inplane = numpy.cross(numpy.cross(sur, inp), sur)
63 inplane = inplane / numpy.linalg.norm(inplane)
64 if config.VERBOSITY >= config.INFO_ALL:
65 print("XU.analyis.getangles: reference inplane direction: ", inplane)
66
67 # calculate inplane direction of peak
68 pinp = numpy.cross(numpy.cross(sur, peak), sur)
69 pinp = pinp / numpy.linalg.norm(pinp)
70 if(numpy.linalg.norm(numpy.cross(sur, peak)) <= config.EPSILON):
71 pinp = inplane
72 if config.VERBOSITY >= config.INFO_ALL:
73 print("XU.analyis.getangles: peaks inplane direction: ", pinp)
74
75 # calculate angles
76 r2d = 180. / numpy.pi
77 chi = numpy.arccos(numpy.dot(sur, peak)) * r2d
78 if(numpy.dot(sur, peak) >= 1. - config.EPSILON):
79 chi = 0.
80 phi = 0.
81 elif(numpy.dot(sur, peak) <= -1. + config.EPSILON):
82 chi = 180.
83 phi = 0.
84 elif(numpy.dot(sur, numpy.cross(inplane, pinp)) <= config.EPSILON):
85 if numpy.dot(pinp, inplane) >= 1.0:
86 phi = 0.
87 elif numpy.dot(pinp, inplane) <= -1.0:
88 phi = 180.
89 else:
90 phi = numpy.sign(numpy.dot(sur, numpy.cross(inplane, pinp))) *\
91 numpy.arccos(numpy.dot(pinp, inplane)) * r2d
92 else:
93 phi = numpy.sign(numpy.dot(sur, numpy.cross(inplane, pinp))) * \
94 numpy.arccos(numpy.dot(pinp, inplane)) * r2d
95 phi = phi - round(phi / 360.) * 360
96
97 return (chi, phi)
98
99
100 def getunitvector(chi, phi, ndir=(0, 0, 1), idir=(1, 0, 0)):
101 """
102 return unit vector determined by spherical angles and definition of the
103 polar axis and inplane reference direction (phi=0)
104
105 Parameters
106 ----------
107 chi, phi : float
108 spherical angles (polar and azimuthal) in degree
109 ndir : tuple, list or array-like
110 polar/z-axis (determines chi=0)
111 idir : tuple, list or array-like
112 azimuthal axis (determines phi=0)
113 """
114 chi_axis = numpy.cross(ndir, idir)
115 v = math.rotarb(ndir, chi_axis, chi)
116 v = math.rotarb(v, ndir, phi)
117 return v / numpy.linalg.norm(v)
118
119
120 def coplanar_intensity(mat, exp, hkl, thickness, thMono, sample_width=10,
121 beam_width=1):
122 """
123 Calculates the expected intensity of a Bragg peak from an epitaxial thin
124 film measured in coplanar geometry (integration over omega and 2theta in
125 angular space!)
126
127 Parameters
128 ----------
129 mat : Crystal
130 Crystal instance for structure factor calculation
131 exp : Experiment
132 Experimental(HXRD) class for the angle calculation
133 hkl : list, tuple or array-like
134 Miller indices of the peak to calculate
135 thickness : float
136 film thickness in nm
137 thMono : float
138 Bragg angle of the monochromator (deg)
139 sample_width : float, optional
140 width of the sample along the beam
141 beam_width : float, optional
142 width of the beam in the same units as the sample size
143
144 Returns
145 -------
146 float
147 intensity of the peak
148 """
149 # angle calculation for geometrical factors
150 om, chi, phi, tt = exp.Q2Ang(mat.Q(hkl))
151
152 # structure factor calculation
153 r = abs(mat.StructureFactor(mat.Q(hkl)))**2
154
155 # polarization factor
156 Cmono = numpy.cos(2 * numpy.radians(thMono))
157 P = (1 + Cmono * numpy.cos(numpy.radians(tt))**2) / ((1 + Cmono))
158 # Lorentz factor to be used when integrating in angular space
159 L = 1 / numpy.sin(numpy.radians(tt))
160 # shape factor: changing illumination with the incidence angle
161 shapef = beam_width / (numpy.sin(numpy.radians(om)) * sample_width)
162 if shapef > 1:
163 shapef = 1
164
165 # absorption correction
166 mu = 1 / (mat.absorption_length() * 1e3)
167 mu_eff = mu * (abs(1 / numpy.sin(numpy.radians(om))) +
168 abs(1 / numpy.sin(numpy.radians(tt - om))))
169 Nblocks = (1 - numpy.exp(-mu_eff * thickness)) / mu_eff
170
171 return r * P * L * shapef * Nblocks
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2011-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 functions to help with experimental alignment during experiments, especially
19 for experiments with linear and area detectors
20 """
21
22 import glob
23 import math
24 import numbers
25 import re
26 import time
27
28 import numpy
29 import scipy.optimize as optimize
30 from numpy import cos, degrees, radians, sin, tan
31 from scipy.ndimage.measurements import center_of_mass
32 from scipy.odr import models
33 from scipy.odr import odrpack as odr
34
35 from .. import config, cxrayutilities
36 from .. import math as xumath
37 from .. import utilities
38 from ..exception import InputError
39 from ..math import fwhm_exp
40
41 # regular expression to check goniometer circle syntax
42 circleSyntax = re.compile("[xyz][+-]")
43
44 #################################################
45 # channel per degree calculation
46 #################################################
47
48
49 def psd_chdeg(angles, channels, stdev=None, usetilt=True, plot=True,
50 datap="kx", modelline="r--", modeltilt="b-", fignum=None,
51 mlabel="fit", mtiltlabel="fit w/tilt", dlabel="data",
52 figtitle=True):
53 """
54 function to determine the channels per degree using a linear
55 fit of the function nchannel = center_ch+chdeg*tan(angles)
56 or the equivalent including a detector tilt
57
58 Parameters
59 ----------
60 angles : array-like
61 detector angles for which the position of the beam was measured
62 channels : array-like
63 detector channels where the beam was found
64
65 stdev : array-like, optional
66 standard deviation of the beam position
67 plot : bool, optional
68 flag to specify if a visualization of the fit should be done
69 usetilt : bool, optional
70 whether to use model considering a detector tilt, i.e. deviation angle
71 of the pixel direction from orthogonal to the primary beam
72 (default: True)
73
74 Other Parameters
75 ----------------
76 datap : str, optional
77 plot format of data points
78 modelline : str, optional
79 plot format of modelline
80 modeltilt : str, optional
81 plot format of modeltilt
82 fignum : int or str, optional
83 figure number to use for the plot
84 mlabel : str
85 label of the model w/o tilt to be used in the plot
86 mtiltlabel : str
87 label of the model with tilt to be used in the plot
88 dlabel : str
89 label of the data line to be used in the plot
90 figtitle : bool
91 flag to tell if the figure title should show the fit parameters
92
93 Returns
94 -------
95 pixelwidth : float
96 the width of one detector channel @ 1m distance, which is negative in
97 case the hit channel number decreases upon an increase of the detector
98 angle.
99 centerch : float
100 center channel of the detector
101 tilt : float
102 tilt of the detector from perpendicular to the beam (will be zero in
103 case of usetilt=False)
104
105 Note:
106 L/pixelwidth*pi/180 = channel/degree for large detector distance with the
107 sample detector disctance L
108 """
109
110 if stdev is None:
111 stdevu = numpy.ones(len(channels))
112 else:
113 stdevu = stdev
114
115 # define detector model and other functions needed for the tilt
116 def straight_tilt(p, x):
117 """
118 model for straight-linear detectors including tilt
119
120 Parameters
121 ----------
122 p : list
123 [L/w_pix*pi/180 ~= channel/degree, center_channel, detector_tilt]
124 with L sample detector disctance, and w_pix the width of one
125 detector channel
126 x : array-like
127 independent variable of the model: detector angle (degree)
128 """
129 rad = radians(x)
130 r = math.degrees(p[0]) * sin(rad) / \
131 cos(rad - math.radians(p[2])) + p[1]
132 return r
133
134 def straight_tilt_der_x(p, x):
135 """
136 derivative of straight-linear detector model with respect to the angle
137 for parameter description see straigt_tilt
138 """
139 rad = radians(x)
140 p2 = math.radians(p[2])
141 r = math.degrees(p[0]) * \
142 (cos(rad) / cos(rad - p2) +
143 sin(rad) / cos(rad - p2) ** 2 * sin(rad - p2))
144 return r
145
146 def straight_tilt_der_p(p, x):
147 """
148 derivative of straight-linear detector model with respect to the
149 paramters for parameter description see straigt_tilt
150 """
151 rad = radians(x)
152 p2 = math.radians(p[2])
153 r = numpy.concatenate([degrees(sin(rad)) / cos(rad - p2),
154 numpy.ones(x.shape, dtype=numpy.float),
155 - math.degrees(p[0]) * sin(rad) /
156 cos(rad - p2) ** 2 * sin(rad - p2)])
157 r.shape = (3,) + x.shape
158 return r
159
160 # fit linear
161 model = models.unilinear
162 data = odr.RealData(angles, channels, sy=stdevu)
163 my_odr = odr.ODR(data, model)
164 # fit type 2 for least squares
165 my_odr.set_job(fit_type=2)
166 fitlin = my_odr.run()
167
168 # fit linear with tangens angle
169 model = models.unilinear
170 data = odr.RealData(degrees(tan(radians(angles))),
171 channels, sy=stdevu)
172 my_odr = odr.ODR(data, model)
173 # fit type 2 for least squares
174 my_odr.set_job(fit_type=2)
175 fittan = my_odr.run()
176
177 if usetilt:
178 # fit tilted straight detector model
179 model = odr.Model(straight_tilt, fjacd=straight_tilt_der_x,
180 fjacb=straight_tilt_der_p)
181 data = odr.RealData(angles, channels, sy=stdevu)
182 my_odr = odr.ODR(data, model, beta0=[fittan.beta[0],
183 fittan.beta[1], 0])
184 # fit type 2 for least squares
185 my_odr.set_job(fit_type=2)
186 fittilt = my_odr.run()
187
188 if plot:
189 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.psd_chdeg')
190
191 if plot:
192 markersize = 6.0
193 markeredgewidth = 1.5
194 linewidth = 2.0
195 if fignum is None:
196 plt.figure()
197 else:
198 plt.figure(fignum)
199 # first plot to show linear model
200 ax1 = plt.subplot(211)
201 angr = angles.max() - angles.min()
202 angp = numpy.linspace(angles.min() - angr * 0.1,
203 angles.max() + angr * .1, 1000)
204 if modelline:
205 plt.plot(angp, models._unilin(fittan.beta,
206 degrees(tan(radians(angp)))),
207 modelline, label=mlabel, lw=linewidth)
208 plt.plot(angp, models._unilin(fitlin.beta, angp), 'k-', label='')
209 if usetilt:
210 plt.plot(angp, straight_tilt(fittilt.beta, angp),
211 modeltilt, label=mtiltlabel, lw=linewidth)
212 if stdev is None:
213 plt.plot(angles, channels, datap, ms=markersize,
214 mew=markeredgewidth, mec=datap[0],
215 mfc='none', label=dlabel)
216 else:
217 plt.errorbar(angles, channels, fmt=datap, yerr=stdevu,
218 ms=markersize, mew=markeredgewidth, mec=datap[0],
219 mfc='none', label=dlabel, ecolor='0.5')
220 plt.grid(True)
221 leg = plt.legend(numpoints=1)
222 leg.get_frame().set_alpha(0.8)
223
224 plt.ylabel("channel number")
225
226 # lower plot to show deviations from linear model
227 plt.subplot(212, sharex=ax1)
228 if modelline:
229 plt.plot(angp, models._unilin(fittan.beta,
230 degrees(tan(radians(angp)))) -
231 models._unilin(fitlin.beta, angp),
232 modelline, label=mlabel, lw=linewidth)
233 if usetilt:
234 plt.plot(angp, straight_tilt(fittilt.beta, angp) -
235 models._unilin(fitlin.beta, angp),
236 modeltilt, label=mtiltlabel, lw=linewidth)
237 if stdev is None:
238 plt.plot(angles, channels - models._unilin(fitlin.beta, angles),
239 datap, ms=markersize, mew=markeredgewidth, mec=datap[0],
240 mfc='none', label=dlabel)
241 else:
242 plt.errorbar(angles,
243 channels - models._unilin(fitlin.beta, angles),
244 fmt=datap, yerr=stdevu, ms=markersize,
245 mew=markeredgewidth, mec=datap[0], mfc='none',
246 label=dlabel, ecolor='0.5')
247 plt.xlabel("detector angle (deg)")
248 plt.ylabel("ch. num. - linear trend")
249 plt.grid(True)
250 plt.hlines(0, angp.min(), angp.max())
251
252 if figtitle:
253 if usetilt:
254 plt.suptitle(
255 "L/w*pi/180: %8.2f; center channel: %8.2f; tilt: %5.2fdeg"
256 % (fittilt.beta[0], fittilt.beta[1], fittilt.beta[2]))
257 else:
258 plt.suptitle("L/w*pi/180: %8.2f; center channel: %8.2f"
259 % (fittan.beta[0], fittan.beta[1]))
260
261 if usetilt:
262 fit = fittilt
263 else:
264 fit = fittan
265
266 if config.VERBOSITY >= config.INFO_LOW:
267 if usetilt:
268 print("XU.analysis.psd_chdeg: channelwidth@1m / center channel /"
269 " tilt: %8.4e / %8.2f / %6.3fdeg"
270 % (abs(1 / math.degrees(fit.beta[0])),
271 fit.beta[1], fit.beta[2]))
272 print("XU.analysis.psd_chdeg: error of channelwidth / "
273 "center channel / tilt: %8.4e / %8.3f / %6.3fdeg"
274 % (math.radians(fit.sd_beta[0] / fit.beta[0] ** 2),
275 fit.sd_beta[1], fit.sd_beta[2]))
276 else:
277 print("XU.analysis.psd_chdeg: channelwidth@1m / center channel: "
278 "%8.4e / %8.2f"
279 % (1 / math.degrees(fit.beta[0]), fit.beta[1]))
280 print("XU.analysis.psd_chdeg: error of channelwidth / "
281 "center channel: %8.4e / %8.3f"
282 % (math.radians(fit.sd_beta[0] / fit.beta[0] ** 2),
283 fit.sd_beta[1]))
284
285 if usetilt:
286 return (1. / math.degrees(fit.beta[0]), fit.beta[1], fit.beta[2])
287 else:
288 return (1. / math.degrees(fit.beta[0]), fit.beta[1], 0.)
289
290
291 #################################################
292 # channel per degree calculation from scan with
293 # linear detector (determined maximum by peak_fit)
294 #################################################
295 def linear_detector_calib(angle, mca_spectra, **keyargs):
296 """
297 function to calibrate the detector distance/channel per degrees
298 for a straight linear detector mounted on a detector arm
299
300 Parameters
301 ----------
302 angle : array-like
303 array of angles in degree of measured detector spectra
304 mca_spectra : array-like
305 corresponding detector spectra (shape: (len(angle), Nchannels)
306
307 r_i : str, optional
308 primary beam direction as vector [xyz][+-]; default: 'y+'
309 detaxis : str, optional
310 detector arm rotation axis [xyz][+-]; default: 'x+'
311
312 Other parameters
313 ----------------
314 plot : bool
315 flag to specify if a visualization of the fit should be done
316 usetilt : bool
317 whether to use model considering a detector tilt, i.e. deviation angle
318 of the pixel direction from orthogonal to the primary beam
319 (default: True)
320
321 Returns
322 -------
323 pixelwidth : float
324 width of the pixel at one meter distance, pixelwidth is negative in
325 case the hit channel number decreases upon an increase of the detector
326 angle
327 center_channel : float
328 central channel of the detector
329 detector_tilt : float, optional
330 if usetilt=True the fitted tilt of the detector is also returned
331
332 Note:
333 L/pixelwidth*pi/180 ~= channel/degree, with the sample detector
334 distance L
335
336 The function also prints out how a linear detector can be initialized using
337 the results obtained from this calibration. Carefully check the results
338
339 See Also
340 --------
341 psd_chdeg : low level function with more configurable options
342 """
343
344 if "detaxis" in keyargs:
345 detrotaxis = keyargs["detaxis"]
346 keyargs.pop("detaxis")
347 else: # use default
348 detrotaxis = 'x+'
349 if "r_i" in keyargs:
350 r_i = keyargs["r_i"]
351 keyargs.pop("r_i")
352 else: # use default
353 r_i = 'y+'
354
355 # max intensity per spectrum
356 mca_int = mca_spectra.sum(axis=1)
357 mca_avg = numpy.average(mca_int)
358 mca_rowmax = numpy.max(mca_int)
359 mca_std = numpy.std(mca_int)
360
361 # determine positions
362 pos = []
363 posstd = []
364 ang = []
365 nignored = 0
366 for i in range(len(mca_spectra)):
367 row = mca_spectra[i, :]
368 row_int = row.sum()
369 if ((abs(row_int - mca_avg) > 3 * mca_std) or
370 (row_int - mca_rowmax * 0.7 < 0)):
371 if config.VERBOSITY >= config.DEBUG:
372 print("XU.analysis.linear_detector_calib: spectrum #%d "
373 "out of intensity range -> ignored" % i)
374 nignored += 1
375 continue
376
377 maxp = numpy.argmax(row)
378 fwhm = fwhm_exp(numpy.arange(row.size), row)
379 N = int(7 * numpy.ceil(fwhm)) // 2 * 2
380
381 # fit beam position
382 # determine maximal usable length of array around peak position
383 Nuse = min(maxp + N // 2, len(row) - 1) - max(maxp - N // 2, 0)
384 param, perr, itlim = xumath.peak_fit(
385 numpy.arange(Nuse),
386 row[max(maxp - N // 2, 0):min(maxp + N // 2, len(row) - 1)],
387 peaktype='PseudoVoigt')
388 if param[0] > 0 and param[0] < Nuse and perr[0] < Nuse/2.:
389 param[0] += max(maxp - N // 2, 0)
390 pos.append(param[0])
391 posstd.append(perr[0])
392 ang.append(angle[i])
393
394 ang = numpy.array(ang)
395 pos = numpy.array(pos)
396 posstd = numpy.array(posstd)
397 if config.VERBOSITY >= config.INFO_ALL:
398 print("XU.analysis.linear_detector_calib: using %d out of %d given "
399 "spectra." % (len(ang), len(angle)))
400 if config.VERBOSITY >= config.DEBUG:
401 print("XU.analysis.linear_detector_calib: determined peak positions:")
402 print(zip(pos, posstd))
403
404 detparam = psd_chdeg(ang, pos, stdev=posstd, **keyargs)
405 if numpy.sign(detparam[0]) > 0:
406 sign = '-'
407 else:
408 sign = '+'
409
410 detaxis = ' '
411 detd = numpy.cross(xumath.getVector(detrotaxis), xumath.getVector(r_i))
412 argm = numpy.abs(detd).argmax()
413
414 def flipsign(char, val):
415 if numpy.sign(val) < 0:
416 if char == '+':
417 return '-'
418 else:
419 return '+'
420 else:
421 return char
422
423 if argm == 0:
424 detaxis = 'x' + flipsign(sign, detd[argm])
425 elif argm == 1:
426 detaxis = 'y' + flipsign(sign, detd[argm])
427 elif argm == 2:
428 detaxis = 'z' + flipsign(sign, detd[argm])
429
430 if config.VERBOSITY >= config.INFO_LOW:
431 print("XU.analysis.linear_detector_calib:\n\tused/total spectra: %d/%d"
432 % (mca_spectra.shape[0] - nignored, mca_spectra.shape[0]))
433 print("\tdetector rotation axis (given by user/default input): %s"
434 % detrotaxis)
435 if len(detparam) == 3:
436 tilt = detparam[2]
437 else:
438 tilt = 0
439 print("\tdetector initialization with: init_linear('%s', %.2f, %d"
440 ", pixelwidth=%.4e, distance=1., tilt=%.2f)"
441 % (detaxis, abs(detparam[1]), mca_spectra.shape[1],
442 abs(detparam[0]), tilt))
443
444 return detparam
445
446
447 ######################################################
448 # detector parameter calculation from scan with
449 # area detector (determine maximum by center of mass)
450 ######################################################
451 def area_detector_calib(angle1, angle2, ccdimages, detaxis, r_i, plot=True,
452 cut_off=0.7, start=(None, None, 1, 0, 0, 0, 0),
453 fix=(False, False, True, False, False, False, False),
454 fig=None, wl=None, plotlog=False, nwindow=50,
455 debug=False):
456 """
457 function to calibrate the detector parameters of an area detector
458 it determines the detector tilt possible rotations and offsets in the
459 detector arm angles
460
461 Parameters
462 ----------
463 angle1 : array-like
464 outer detector arm angle
465 angle2 : array-like
466 inner detector arm angle
467 ccdimages : array-like
468 images of the ccd taken at the angles given above
469 detaxis : list of str
470 detector arm rotation axis; default: ['z+', 'y-']
471 r_i : str
472 primary beam direction [xyz][+-]; default 'x+'
473
474 plot : bool, optional
475 flag to determine if results and intermediate results should be
476 plotted; default: True
477 cut_off : float, optional
478 cut off intensity to decide if image is used for the determination or
479 not; default: 0.7 = 70%
480 start : tuple, optional
481 sequence of start values of the fit for parameters, which can not be
482 estimated automatically or might want to be fixed. These are: pwidth1,
483 pwidth2, distance, tiltazimuth, tilt, detector_rotation,
484 outerangle_offset. By default (None, None, 1, 0, 0, 0, 0) is used.
485 fix : tuple of bool
486 fix parameters of start (default: (False, False, True, False, False,
487 False, False)) It is strongly recommended to either fix the distance or
488 the pwidth1, 2 values.
489 fig : Figure, optional
490 matplotlib figure used for plotting the error default: None (creates
491 own figure)
492 wl : float or str
493 wavelength of the experiment in Angstrom (default: config.WAVELENGTH)
494 value does not really matter here but does affect the scaling of the
495 error
496 plotlog : bool
497 flag to specify if the created error plot should be on log-scale
498 nwindow : int
499 window size for determination of the center of mass position after the
500 center of mass of every full image is determined, the center of mass is
501 determined again using a window of size nwindow in order to reduce the
502 effect of hot pixels.
503 debug : bool
504 flag to specify that you want to see verbose output and saving of
505 images to show if the CEN determination works
506 """
507
508 if plot:
509 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.area_'
510 'detector_calib')
511
512 if wl is None:
513 wl = config.WAVELENGTH
514 else:
515 wl = utilities.wavelength(wl)
516
517 for i in [0, 1]:
518 if fix[i] and not numpy.isscalar(start[i]):
519 raise ValueError("XU.analysis.area_detector_calib: start value for"
520 " pwidth%d must be given if it should be fixed "
521 "during the fit" % (i+1))
522
523 t0 = time.time()
524 Npoints = len(angle1)
525 if debug:
526 print("number of given images: %d" % Npoints)
527
528 # determine center of mass position from detector images
529 # also use only images with an intensity larger than 70% of the average
530 # intensity
531 n1 = numpy.zeros(0, dtype=numpy.double)
532 n2 = n1
533 ang1 = n1
534 ang2 = n1
535
536 avg = 0
537 for i in range(Npoints):
538 avg += numpy.sum(ccdimages[i])
539 avg /= float(Npoints)
540 (N1, N2) = ccdimages[0].shape
541
542 if debug:
543 print("average intensity per image: %.1f" % avg)
544
545 for i in range(Npoints):
546 if debug and i == 0:
547 print("angle1, angle2, cen1, cen2")
548 img = ccdimages[i]
549 if numpy.sum(img) > cut_off * avg:
550 cen1, cen2 = _peak_position(img, nwindow, plot=debug and plot)
551 n1 = numpy.append(n1, cen1)
552 n2 = numpy.append(n2, cen2)
553 ang1 = numpy.append(ang1, angle1[i])
554 ang2 = numpy.append(ang2, angle2[i])
555 if debug:
556 print("%8.3f %8.3f \t%.2f %.2f" % (angle1[i], angle2[i],
557 cen1, cen2))
558 Nused = len(ang1)
559
560 if debug:
561 print("Nused / Npoints: %d / %d" % (Nused, Npoints))
562
563 # determine detector directions
564 detdir1, detdir2 = _determine_detdir(
565 ang1 - start[6], ang2, n1, n2, detaxis, r_i)
566
567 if debug:
568 print("determined detector directions:[%s, %s]" % (detdir1, detdir2))
569
570 epslist = []
571 paramlist = []
572 epsmin = numpy.inf
573 fitmin = None
574
575 print("tiltaz tilt detrot offset: error (relative) (fittime)")
576 print("------------------------------------------------------------")
577 # find optimal detector rotation (however keep other parameters free)
578 detrot = start[5]
579 if not fix[5]:
580 for detrotstart in numpy.linspace(start[5] - 1, start[5] + 1, 40):
581 start = start[:5] + (detrotstart,) + (start[6],)
582 eps, param, fit = _area_detector_calib_fit(
583 ang1, ang2, n1, n2, detaxis, r_i, detdir1, detdir2,
584 start=start, fix=fix, full_output=True, wl=wl, debug=debug)
585 epslist.append(eps)
586 paramlist.append(param)
587 if epslist[-1] < epsmin:
588 epsmin = epslist[-1]
589 parammin = param
590 fitmin = fit
591 detrot = param[7]
592 if debug:
593 print(eps, param)
594
595 Ntiltaz = 1 if fix[3] else 5
596 Ntilt = 1 if fix[4] else 6
597 Noffset = 1 if fix[6] else 100
598 if fix[6]:
599 Ntilt = Ntilt * 8 if not fix[4] else Ntilt
600 Ntiltaz = Ntiltaz * 7 if not fix[3] else Ntiltaz
601
602 startparam = start[:5] + (detrot,) + (start[6],)
603 if debug:
604 print("start params: %s" % str(startparam))
605
606 Ntot = Ntiltaz * Ntilt * Noffset
607 ict = 0
608 for tiltazimuth in numpy.linspace(startparam[3] if fix[3] else 0,
609 360, Ntiltaz, endpoint=False):
610 for tilt in numpy.linspace(
611 startparam[4] if fix[4] else max(0, startparam[4]-2),
612 max(0, startparam[4]-2)+4, Ntilt):
613 for offset in numpy.linspace(
614 startparam[6] if fix[6] else - 3 + startparam[6],
615 3 + startparam[6], Noffset):
616 t1 = time.time()
617 start = (startparam[0], startparam[1], startparam[2],
618 tiltazimuth, tilt, startparam[5], offset)
619 eps, param, fit = _area_detector_calib_fit(
620 ang1, ang2, n1, n2, detaxis, r_i, detdir1, detdir2,
621 start=start, fix=fix, full_output=True, wl=wl)
622 epslist.append(eps)
623 paramlist.append(param)
624 t2 = time.time()
625 print("%d/%d\t%6.1f %6.2f %8.3f %8.3f: %10.4e (%4.2f) "
626 "(%5.2fsec)" % ((ict, Ntot) + start[3:] +
627 (epslist[-1],
628 epslist[-1] / epsmin, t2 - t1)))
629 ict += 1
630
631 if epslist[-1] < epsmin:
632 print("************************")
633 print("new global minimum found")
634 epsmin = epslist[-1]
635 parammin = param
636 fitmin = fit
637 print("new best parameters: %.2f %.2f %10.4e %10.4e %8.4f "
638 "%.1f %.2f %.3f %.3f" % parammin)
639 print("************************\n")
640
641 (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
642 outerangle_offset) = parammin
643
644 if plot:
645 if fig:
646 plt.figure(fig.number)
647 else:
648 plt.figure("CCD Calib fit")
649 plt.clf()
650 nparams = numpy.array(paramlist)
651 neps = numpy.array(epslist)
652 labels = (
653 'cch1 (1)',
654 'cch2 (1)',
655 r'pwidth1 ($\mu$m)',
656 r'pwidth2 ($\mu$m)',
657 'distance (m)',
658 'tiltazimuth (deg)',
659 'tilt (deg)',
660 'detrot (deg)',
661 'outerangle offset (deg)')
662 xscale = (1., 1., 1.e6, 1.e6, 1., 1., 1., 1., 1.)
663 for p in range(9):
664 ax = plt.subplot(3, 3, p + 1)
665 if plotlog:
666 plt.semilogy(nparams[:, p] * xscale[p], neps, 'k.')
667 else:
668 plt.scatter(nparams[:, p] * xscale[p], neps, c=nparams[:, -1],
669 s=10, marker='o', cmap=plt.cm.gnuplot,
670 edgecolor='none')
671 plt.xlabel(labels[p])
672 if plotlog:
673 plt.semilogy(parammin[p] * xscale[p], epsmin, 'ko', ms=8,
674 mew=2.5, mec='k', mfc='w')
675 else:
676 plt.plot(parammin[p] * xscale[p], epsmin, 'ko', ms=8, mew=2.5,
677 mec='k', mfc='w')
678 plt.ylim(epsmin * 0.7, epsmin * 2.)
679 plt.locator_params(nbins=4, axis='x')
680 if p > 1:
681 if fix[p-2]:
682 ax.set_facecolor('0.85')
683 plt.tight_layout()
684
685 if config.VERBOSITY >= config.INFO_LOW:
686 print("total time needed for fit: %.2fsec" % (time.time() - t0))
687 print("fitted parameters: epsilon: %10.4e (%d,%s) "
688 % (epsmin, fitmin.info, repr(fitmin.stopreason)))
689 print("param: (cch1, cch2, pwidth1, pwidth2, tiltazimuth, tilt, "
690 "detrot, outerangle_offset)")
691 print("param: %.2f %.2f %10.4e %10.4e %.4f %.1f %.2f %.3f %.3f"
692 % (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt,
693 detrot, outerangle_offset))
694
695 if config.VERBOSITY > 0:
696 print("please check the resulting data (consider setting plot=True)")
697 print("detector rotation axis / primary beam direction "
698 "(given by user): %s / %s" % (repr(detaxis), r_i))
699 print("detector pixel directions / distance: %s %s / %g"
700 % (detdir1, detdir2, 1.))
701 print("\tdetector initialization with: init_area('%s', '%s', "
702 "cch1=%.2f, cch2=%.2f, Nch1=%d, Nch2=%d, pwidth1=%.4e, "
703 "pwidth2=%.4e, distance=%.5f, detrot=%.3f, tiltazimuth=%.1f, "
704 "tilt=%.3f)" % (detdir1, detdir2, cch1, cch2, N1, N2, pwidth1,
705 pwidth2, distance, detrot, tiltazimuth, tilt))
706 print("AND ALWAYS USE an (additional) OFFSET of %.4fdeg in the "
707 "OUTER DETECTOR ANGLE!" % (outerangle_offset))
708
709 return (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth,
710 tilt, detrot, outerangle_offset), eps
711
712
713 def _peak_position(img, nwindow, plot=False):
714 """
715 function to determine the peak position on the detector using the center of
716 mass (COM)
717
718 Parameters
719 ----------
720 img : array-like
721 detector image data as 2D array
722 nwindow : int
723 to avoid influence of hot pixels far away from the peak position the
724 center of mass approach is repeated with a window around the COM of the
725 full image. COM of the size (nwindow, nwindow) is returned
726 plot : bool, optional
727 the result of the of the determination can be saved as a plot
728 """
729 nw = nwindow // 2
730 [cen1r, cen2r] = center_of_mass(img)
731 for i in range(11): # refine center of mass multiple times
732 [cen1, cen2] = center_of_mass(
733 img[max(int(cen1r) - nw, 0):
734 min(int(cen1r) + nw, img.shape[0]),
735 max(int(cen2r) - nw, 0):
736 min(int(cen2r) + nw, img.shape[1])])
737 cen1 += max(int(cen1r) - nw, 0)
738 cen2 += max(int(cen2r) - nw, 0)
739 if numpy.linalg.norm((cen1 - cen1r, cen2 - cen2r)) > 3:
740 cen1r, cen2r = (cen1, cen2)
741 else:
742 break
743 if i == 10 and config.VERBOSITY >= config.INFO_LOW:
744 print("XU.analysis._peak_position: Warning: peak position "
745 "determination not converged, consider debug mode!")
746 if plot:
747 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis._peak_'
748 'position')
749 if plot:
750 plt.figure("_ccd")
751 plt.imshow(utilities.maplog(img), origin='low')
752 plt.plot(cen2, cen1, "wo", mfc='none')
753 plt.axis([cen2 - nw, cen2 + nw, cen1 - nw, cen1 + nw])
754 plt.colorbar()
755 fnr = len(glob.glob('xu_calib_ccd_img*.png'))
756 plt.savefig("xu_calib_ccd_img%d.png" % (fnr + 1))
757 plt.close("_ccd")
758
759 return cen1, cen2
760
761
762 def _determine_detdir(ang1, ang2, n1, n2, detaxis, r_i):
763 """
764 determines detector pixel direction from correlation analysis of linear
765 fits to the observed pixel numbers of the primary beam.
766 """
767 # center channel and detector pixel direction and pixel size
768 (s1, i1), r1 = xumath.linregress(ang1, n1)
769 (s2, i2), r2 = xumath.linregress(ang1, n2)
770 (s3, i3), r3 = xumath.linregress(ang2, n1)
771 (s4, i4), r4 = xumath.linregress(ang2, n2)
772
773 # determine detector directions
774 s = ord('x') + ord('y') + ord('z')
775 c1 = ord(detaxis[0][0]) + ord(r_i[0])
776 c2 = ord(detaxis[1][0]) + ord(r_i[0])
777 sign1 = numpy.sign(numpy.sum(numpy.cross(xumath.getVector(detaxis[0]),
778 xumath.getVector(r_i))))
779 sign2 = numpy.sign(numpy.sum(numpy.cross(xumath.getVector(detaxis[1]),
780 xumath.getVector(r_i))))
781 if ((r1 + r2 > r3 + r4) and r1 > r2) or ((r1 + r2 < r3 + r4) and r3 < r4):
782 detdir1 = chr(s - c1)
783 detdir2 = chr(s - c2)
784 if numpy.sign(s1) > 0:
785 if sign1 > 0:
786 detdir1 += '-'
787 else:
788 detdir1 += '+'
789 else:
790 if sign1 > 0:
791 detdir1 += '+'
792 else:
793 detdir1 += '-'
794
795 if numpy.sign(s4) > 0:
796 if sign2 > 0:
797 detdir2 += '-'
798 else:
799 detdir2 += '+'
800 else:
801 if sign2 > 0:
802 detdir2 += '+'
803 else:
804 detdir2 += '-'
805 else:
806 detdir1 = chr(s - c2)
807 detdir2 = chr(s - c1)
808 if numpy.sign(s3) > 0:
809 if sign2 > 0:
810 detdir1 += '-'
811 else:
812 detdir1 += '+'
813 else:
814 if sign2 > 0:
815 detdir1 += '+'
816 else:
817 detdir1 += '-'
818
819 if numpy.sign(s2) > 0:
820 if sign1 > 0:
821 detdir2 += '-'
822 else:
823 detdir2 += '+'
824 else:
825 if sign1 > 0:
826 detdir2 += '+'
827 else:
828 detdir2 += '-'
829
830 return detdir1, detdir2
831
832
833 def _area_detector_calib_fit(ang1, ang2, n1, n2, detaxis, r_i, detdir1,
834 detdir2, start=(None, None, 1, 0, 0, 0, 0),
835 fix=(False, False, True, False, False, False,
836 False),
837 full_output=False, wl=1., debug=False):
838 """
839 INTERNAL FUNCTION
840 function to calibrate the detector parameters of an area detector
841 it determines the detector tilt possible rotations and offsets in the
842 detector arm angles
843
844 parameters
845 ----------
846 angle1 : array-like
847 outer detector arm angle
848 angle2 : array-like
849 inner detector arm angle
850 n1, n2 : int
851 pixel number at which the primary beam was observed
852 detaxis : list of str
853 detector arm rotation axis; default: ['z+', 'y-']
854 r_i : str
855 primary beam direction [xyz][+-]; default 'x+'
856 detdir1, detdir2 : str
857 detector pixel directions of first and second pixel coordinates;
858 e.g. 'y+'
859
860 start : tuple, optional
861 sequence of start values of the fit for parameters, which can not be
862 estimated automatically or might want to be fixed. These are: pwidth1,
863 pwidth2, distance, tiltazimuth, tilt, detector_rotation,
864 outerangle_offset. By default (None, None, 1, 0, 0, 0, 0) is used.
865 fix : tuple of bool
866 fix parameters of start (default: (False, False, True, False, False,
867 False, False)) It is strongly recommended to either fix the distance or
868 the pwidth1, 2 values.
869 fig : Figure, optional
870 matplotlib figure used for plotting the error default: None (creates
871 own figure)
872 full_output : bool
873 flag to tell if to return fit object with final parameters and detector
874 directions
875 wl : float or str
876 wavelength of the experiment in Angstrom (default: 1)
877 value does not really matter here but does affect the scaling of the
878 error
879 debug : bool
880 flag to tell if you want to see debug output of the script (switch this
881 to true only if you can handle it :))
882
883 Returns
884 -------
885 float
886 final epsilon of the fit
887 param : list, optional
888 if full_output: fit parameters
889 fit : object, optional
890 if full_output: fit object
891 """
892
893 def areapixel(params, detectorDir1, detectorDir2, r_i, detectorAxis,
894 *args, **kwargs):
895 """
896 angular to momentum space conversion for pixels of an area detector the
897 center pixel is in direction of self.r_i when detector angles are zero
898
899 the detector geometry must be given to the routine
900
901 Parameters
902 ----------
903 params : list
904 parameters of the detector calibration model
905 (cch1, cch2, pwidth1, pwidth2, tiltazimuth, tilt, detrot)
906
907 - cch1, cch2: center pixel, in direction of self.r_i at zero
908 detectorAngles;
909 - pwidth1, pwidth2: width of one pixel (same unit as distance);
910 - distance: distance of center pixel from center of rotation;
911 - tiltazimuth: direction of the tilt vector in the detector plane
912 (in degree);
913 - tilt: tilt of the detector plane around an axis normal to the
914 direction given by the tiltazimuth;
915 - detrot: detector rotation around the primary beam direction as
916 given by r_i;
917
918 detectorDir1 : str
919 direction of the detector (along the pixel direction 1); e.g. 'z+'
920 means higher pixel numbers at larger z positions
921 detectorDir2 : str
922 direction of the detector (along the pixel direction 2); e.g. 'x+'
923 r_i : str
924 primary beam direction e.g. 'x+'
925 detectorAxis : list or tuple
926 detector circles e.g. ['z+', 'y-'] would mean a detector arm with a
927 two rotations
928 *args : array-like
929 detector angles and channel numbers;
930 *dAngles* as numpy array, lists or Scalars in total
931 len(detectorAxis) must be given starting with the outer most
932 circle. All arguments must have the same shape or length.
933 *channel numbers* n1 and n2 where the primary beam hits the
934 detector with same length as the detector values
935
936 delta : list, optional
937 giving delta angles to correct the given ones for misalignment
938 delta must be an numpy array or list of len(*dAngles) used angles
939 are than *args - delta
940 wl : float or str, optional
941 x-ray wavelength in angstroem (default: 1 (since it does not matter
942 here))
943 deg : bool, optional
944 flag to tell if angles are passed as degree (default: True)
945
946 Returns
947 -------
948 ndarray
949 reciprocal space position of detector pixels n1, n2 in a
950 numpy.ndarray of shape ( len(args) , 3 )
951 """
952
953 # check detector circle argument
954 if isinstance(detectorAxis, (str, list, tuple)):
955 if isinstance(detectorAxis, str):
956 dAxis = list([detectorAxis])
957 else:
958 dAxis = list(detectorAxis)
959 for circ in dAxis:
960 if not isinstance(circ, str) or len(circ) != 2:
961 raise InputError("QConversionPixel: incorrect detector "
962 "circle type or syntax (%s)" % repr(circ))
963 if not circleSyntax.search(circ):
964 raise InputError("QConversionPixel: incorrect detector "
965 "circle syntax (%s)" % circ)
966 else:
967 raise TypeError("Qconversion error: invalid type for detectorAxis,"
968 " must be str, list or tuple")
969 # add detector rotation around primary beam
970 dAxis += [r_i]
971 _detectorAxis_str = ''
972 for circ in dAxis:
973 _detectorAxis_str += circ
974
975 Nd = len(dAxis)
976 Nargs = Nd + 2 - 1
977
978 # check detectorDir
979 if not isinstance(detectorDir1, str) or len(detectorDir1) != 2:
980 raise InputError("QConversionPixel: incorrect detector direction1 "
981 "type or syntax (%s)" % repr(detectorDir1))
982 if not circleSyntax.search(detectorDir1):
983 raise InputError("QConversionPixel: incorrect detector direction1 "
984 "syntax (%s)" % detectorDir1)
985 _area_detdir1 = detectorDir1
986 if not isinstance(detectorDir2, str) or len(detectorDir2) != 2:
987 raise InputError("QConversionPixel: incorrect detector direction2 "
988 "type or syntax (%s)" % repr(detectorDir2))
989 if not circleSyntax.search(detectorDir2):
990 raise InputError("QConversionPixel: incorrect detector direction2 "
991 "syntax (%s)" % detectorDir2)
992 _area_detdir2 = detectorDir2
993
994 # parse parameter arguments
995 _area_cch1 = float(params[0])
996 _area_cch2 = float(params[1])
997 _area_pwidth1 = float(params[2])
998 _area_pwidth2 = float(params[3])
999 _area_distance = float(params[4])
1000 _area_tiltazimuth = math.radians(params[5])
1001 _area_tilt = math.radians(params[6])
1002 _area_rot = float(params[7])
1003 _area_ri = xumath.getVector(r_i) * _area_distance
1004
1005 # kwargs
1006 wl = utilities.wavelength(kwargs.get('wl', 1.))
1007 deg = kwargs.get('deg', True)
1008
1009 delta = numpy.asarray(kwargs.get('delta', numpy.zeros(Nd)),
1010 dtype=numpy.double)
1011 if delta.size != Nd - 1:
1012 raise InputError("QConversionPixel: keyword argument delta "
1013 "does not have an appropriate shape")
1014
1015 # prepare angular arrays from *args
1016 # need one sample angle and one detector angle array
1017 if len(args) != Nargs:
1018 raise InputError("QConversionPixel: wrong amount (%d) of arguments"
1019 " given, number of arguments should be %d"
1020 % (len(args), Nargs))
1021
1022 try:
1023 Npoints = len(args[0])
1024 except (TypeError, IndexError):
1025 Npoints = 1
1026
1027 dAngles = numpy.array((), dtype=numpy.double)
1028 for i in range(Nd - 1):
1029 arg = args[i]
1030 if not isinstance(arg, (numbers.Number, tuple,
1031 list, numpy.ndarray)):
1032 raise TypeError("QConversionPixel: invalid type for one of "
1033 "the detector coordinates, must be scalar, "
1034 "list or array")
1035 elif isinstance(arg, numbers.Number):
1036 arg = numpy.array([arg], dtype=numpy.double)
1037 elif isinstance(arg, list):
1038 arg = numpy.array(arg, dtype=numpy.double)
1039 arg = arg - delta[i]
1040 dAngles = numpy.concatenate((dAngles, arg))
1041 # add detector rotation around primary beam
1042 dAngles = numpy.concatenate((dAngles,
1043 numpy.ones(arg.shape,
1044 dtype=numpy.double) *
1045 _area_rot))
1046
1047 # read channel numbers
1048 n1 = numpy.array((), dtype=numpy.double)
1049 n2 = numpy.array((), dtype=numpy.double)
1050
1051 arg = args[Nd - 1]
1052 if not isinstance(arg, (numbers.Number, tuple, list, numpy.ndarray)):
1053 raise TypeError("QConversionPixel: invalid type for one of the "
1054 "detector coordinates, must be scalar, list or "
1055 "array")
1056 elif isinstance(arg, numbers.Number):
1057 arg = numpy.array([arg], dtype=numpy.double)
1058 elif isinstance(arg, list):
1059 arg = numpy.array(arg, dtype=numpy.double)
1060 n1 = arg
1061
1062 arg = args[Nd]
1063 if not isinstance(arg, (numbers.Number, tuple, list, numpy.ndarray)):
1064 raise TypeError("QConversionPixel: invalid type for one of the "
1065 "detector coordinates, must be scalar, list or "
1066 "array")
1067 elif isinstance(arg, numbers.Number):
1068 arg = numpy.array([arg], dtype=numpy.double)
1069 elif isinstance(arg, list):
1070 arg = numpy.array(arg, dtype=numpy.double)
1071 n2 = arg
1072
1073 dAngles.shape = (Nd, Npoints)
1074 dAngles = dAngles.transpose()
1075
1076 if deg:
1077 dAngles = radians(dAngles)
1078
1079 qpos = cxrayutilities.ang2q_conversion_area_pixel(
1080 dAngles, n1, n2, _area_ri, _detectorAxis_str,
1081 _area_cch1, _area_cch2, _area_pwidth1, _area_pwidth2,
1082 _area_detdir1, _area_detdir2, _area_tiltazimuth, _area_tilt,
1083 wl, config.NTHREADS)
1084
1085 return qpos[:, 0], qpos[:, 1], qpos[:, 2]
1086
1087 def afunc(param, x, detectorDir1, detectorDir2, r_i, detectorAxis, wl):
1088 """
1089 function for fitting the detector parameters
1090 basically this is a wrapper for the areapixel function
1091
1092 parameters
1093 ----------
1094 param : list
1095 fit parameters (cch1, cch2, pwidth1, pwidth2, distance,
1096 tiltazimuth, tilt, detrot, outerangle_offset)
1097 x : array-like
1098 independent variables (angle1, angle2, n1, n2) with
1099 shape (4, Npoints)
1100 detectorDir1 : str
1101 direction of the detector (along the pixel direction 1); e.g. 'z+'
1102 means higher pixel numbers at larger z positions
1103 detectorDir2 : str
1104 direction of the detector (along the pixel direction 2); e.g. 'x+'
1105 r_i : str
1106 primary beam direction e.g. 'x+'
1107 detectorAxis : list or tuple
1108 detector circles e.g. ['z+', 'y-'] would mean a detector arm with a
1109 two rotations
1110 wl : float or str
1111 wavelength of the experiment in Angstroem
1112
1113 Returns
1114 -------
1115 ndarray
1116 reciprocal space position of detector pixels n1, n2 in a
1117 numpy.ndarray of shape (3, x.shape[1])
1118 """
1119
1120 angle1 = x[0, :]
1121 angle2 = x[1, :]
1122 n1 = x[2, :]
1123 n2 = x[3, :]
1124
1125 # use only positive tilt
1126 param[6] = abs(param[6])
1127
1128 (qx, qy, qz) = areapixel(
1129 param[:-1], detectorDir1, detectorDir2, r_i, detectorAxis,
1130 angle1, angle2, n1, n2, delta=[param[-1], 0.], wl=wl)
1131
1132 return qx ** 2 + qy ** 2 + qz ** 2
1133
1134 Npoints = len(ang1)
1135
1136 # guess initial parameters
1137 # center channel and detector pixel direction and pixel size
1138 (s1, i1), r1 = xumath.linregress(ang1 - start[6], n1)
1139 (s2, i2), r2 = xumath.linregress(ang1 - start[6], n2)
1140 (s3, i3), r3 = xumath.linregress(ang2, n1)
1141 (s4, i4), r4 = xumath.linregress(ang2, n2)
1142
1143 distance = start[2]
1144 if ((r1 + r2 > r3 + r4) and r1 > r2) or ((r1 + r2 < r3 + r4) and r3 < r4):
1145 cch1 = i1
1146 cch2 = i4
1147 pwidth1 = 2 * distance / numpy.abs(s1) * math.tan(math.radians(0.5))
1148 pwidth2 = 2 * distance / numpy.abs(s4) * math.tan(math.radians(0.5))
1149 else:
1150 cch1 = i3
1151 cch2 = i2
1152 pwidth1 = 2 * distance / numpy.abs(s3) * math.tan(math.radians(0.5))
1153 pwidth2 = 2 * distance / numpy.abs(s2) * math.tan(math.radians(0.5))
1154 if numpy.isscalar(start[0]):
1155 pwidth1 = start[0]
1156 if numpy.isscalar(start[1]):
1157 pwidth2 = start[1]
1158 if numpy.isscalar(start[0]) or numpy.isscalar(start[1]):
1159 # find biggest correlation and recalculate distance
1160 idxmax = numpy.argmax((r1, r2, r3, r4))
1161 s = (s1, s2, s3, s4)[idxmax]
1162 distance = abs(s) / math.tan(math.radians(0.5)) / 2
1163 distance *= pwidth1 if idxmax < 2 else pwidth2
1164 tilt = abs(start[4])
1165 tiltazimuth = start[3]
1166 detrot = start[5]
1167 outerangle_offset = start[6]
1168 # parameters for the fitting
1169 param = (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt,
1170 detrot, outerangle_offset)
1171 if debug:
1172 print("initial parameters: ")
1173 print("primary beam / detector pixel directions / distance: "
1174 "%s / %s %s / %e" % (r_i, detdir1, detdir2, distance))
1175 print("param: (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, "
1176 "tilt, detrot, outerangle_offset)")
1177 print("param: %.2f %.2f %10.4e %10.4e %.3f %.1f %.2f %.3f %.3f"
1178 % param)
1179
1180 # set data
1181 x = numpy.empty((4, Npoints), dtype=numpy.double)
1182 x[0, :] = ang1
1183 x[1, :] = ang2
1184 x[2, :] = n1
1185 x[3, :] = n2
1186 data = odr.Data(x, y=1)
1187 # define model for fitting
1188 model = odr.Model(afunc, extra_args=(detdir1, detdir2, r_i, detaxis, wl),
1189 implicit=True)
1190 # check if parameters need to be fixed
1191 ifixb = ()
1192 for i in range(len(fix)):
1193 ifixb += (int(not fix[i]),)
1194
1195 my_odr = odr.ODR(data, model, beta0=param, ifixb=(1, 1) + ifixb,
1196 ifixx=(0, 0, 0, 0),
1197 stpb=(0.4, 0.4, pwidth1/50., pwidth2/50.,
1198 distance/1000, 2, 0.125, 0.01, 0.01),
1199 sclb=(1/abs(cch1), 1/abs(cch2),
1200 1/pwidth1, 1/pwidth2, 1/distance, 1/90.,
1201 1/0.2, 1/0.2, 1/0.2),
1202 maxit=200, ndigit=12, sstol=1e-11, partol=1e-11)
1203 if debug:
1204 my_odr.set_iprint(final=1)
1205 my_odr.set_iprint(iter=2)
1206
1207 fit = my_odr.run()
1208
1209 (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
1210 outerangle_offset) = fit.beta
1211 # fix things in parameters
1212 tiltazimuth = tiltazimuth % 360.
1213 tilt = abs(tilt)
1214
1215 final_q = afunc([cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt,
1216 detrot, outerangle_offset],
1217 x, detdir1, detdir2, r_i, detaxis, wl)
1218 final_error = numpy.mean(final_q)
1219
1220 if debug:
1221 print("fitted parameters: (%e, %d, %s) " % (final_error, fit.info,
1222 repr(fit.stopreason)))
1223 print("primary beam / detector pixel directions / distance: "
1224 "%s / %s %s / %e" % (r_i, detdir1, detdir2, distance))
1225 print("param: (cch1, cch2, pwidth1, pwidth2, disance, tiltazimuth, "
1226 "tilt, detrot, outerangle_offset)")
1227 print("param: %.2f %.2f %10.4e %10.4e %.3f %.1f %.2f %.3f %.3f"
1228 % (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt,
1229 detrot, outerangle_offset))
1230
1231 if full_output:
1232 return final_error, (cch1, cch2, pwidth1, pwidth2, distance,
1233 tiltazimuth, tilt, detrot, outerangle_offset), fit
1234 else:
1235 return final_error
1236
1237
1238 # #####################################################
1239 # detector parameter calculation from scan with
1240 # area detector (determine maximum by center of mass)
1241 # #####################################################
1242 def area_detector_calib_hkl(sampleang, angle1, angle2, ccdimages, hkls,
1243 experiment, material, detaxis, r_i, plot=True,
1244 cut_off=0.7,
1245 start=(None, None, 1, 0, 0, 0, 0, 0, 0, 'config'),
1246 fix=(False, False, True, False, False, False,
1247 False, False, False, False),
1248 fig=None, plotlog=False, nwindow=50, debug=False):
1249 """
1250 function to calibrate the detector parameters of an area detector
1251 it determines the detector tilt possible rotations and offsets in the
1252 detector arm angles
1253
1254 in this variant not only scans through the primary beam but also scans at a
1255 set of symmetric reflections can be used for the detector parameter
1256 determination. for this not only the detector parameters but in addition
1257 the sample orientation and wavelength need to be fit. Both images from the
1258 primary beam hkl = (0, 0, 0) and from a symmetric reflection
1259 hkl = (h, k, l) need to be given for a successful run.
1260
1261 Parameters
1262 ----------
1263 sampleang : array-like
1264 sample rocking angle (needed to align the reflections (same rotation
1265 direction as inner detector rotation)) other sample angle are not
1266 allowed to be changed during the scans
1267 angle1 : array-like
1268 outer detector arm angle
1269 angle2 : array-like
1270 inner detector arm angle
1271 ccdimages : array-like
1272 images of the ccd taken at the angles given above
1273 hkls : list or array-like
1274 hkl values for every image
1275 experiment : Experiment
1276 Experiment class object needed to get the UB matrix for the hkl peak
1277 treatment
1278 material : Crystal
1279 material used as reference crystal
1280 detaxis : list of str
1281 detector arm rotation axis; default: ['z+', 'y-']
1282 r_i : str
1283 primary beam direction [xyz][+-]; default 'x+'
1284
1285 plot : bool, optional
1286 flag to determine if results and intermediate results should be
1287 plotted; default: True
1288 cut_off : float, optional
1289 cut off intensity to decide if image is used for the determination or
1290 not; default: 0.7 = 70%
1291 start : tuple, optional
1292 sequence of start values of the fit for parameters, which can not be
1293 estimated automatically or might want to be fixed. These are: pwidth1,
1294 pwidth2, distance, tiltazimuth, tilt, detector_rotation,
1295 outerangle_offset, sampletilt, sampletiltazimuth, wavelength. By
1296 default (None, None, 1, 0, 0, 0, 0, 0, 0, 'config').
1297 fix : tuple of bool
1298 fix parameters of start (default: (False, False, True, False, False,
1299 False, False, False, False, False)) It is strongly recommended to
1300 either fix the distance or the pwidth1, 2 values.
1301 fig : Figure, optional
1302 matplotlib figure used for plotting the error default: None (creates
1303 own figure)
1304 plotlog : bool
1305 flag to specify if the created error plot should be on log-scale
1306 nwindow : int
1307 window size for determination of the center of mass position after the
1308 center of mass of every full image is determined, the center of mass is
1309 determined again using a window of size nwindow in order to reduce the
1310 effect of hot pixels.
1311 debug : bool
1312 flag to specify that you want to see verbose output and saving of
1313 images to show if the CEN determination works
1314 """
1315 if plot:
1316 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.area_'
1317 'detector_calib_hkl')
1318
1319 if start[-1] == 'config':
1320 start[-1] = config.WAVELENGTH
1321 elif isinstance(start[-1], str):
1322 start[-1] = utilities.wavelength(start[-1])
1323
1324 t0 = time.time()
1325 Npoints = len(angle1)
1326 if debug:
1327 print("number of given images: %d" % Npoints)
1328
1329 # determine center of mass position from detector images also use only
1330 # images with an intensity larger than cut_off of the average intensity.
1331 # the image selection is only performed for images in the primary beam
1332 n1 = numpy.zeros(0, dtype=numpy.double)
1333 n2 = n1
1334 ang1 = n1
1335 ang2 = n1
1336 sang = n1
1337 usedhkls = []
1338
1339 avg = 0
1340 imgpbcnt = 0
1341 for i in range(Npoints):
1342 if (numpy.all(hkls[i] == (0, 0, 0))):
1343 avg += numpy.sum(ccdimages[i])
1344 imgpbcnt += 1
1345
1346 if imgpbcnt > 0:
1347 avg /= float(imgpbcnt)
1348 else:
1349 raise ValueError("XU.analyis.area_detector_calib_hkl: no required "
1350 "images in the primary beam given!")
1351 (N1, N2) = ccdimages[0].shape
1352
1353 if debug:
1354 print("average intensity per image in the primary beam: %.1f" % avg)
1355
1356 for i in range(Npoints):
1357 if debug and i == 0:
1358 print("angle1, angle2, cen1, cen2")
1359 img = ccdimages[i]
1360 if ((numpy.sum(img) > cut_off * avg) or
1361 (numpy.all(hkls[i] != (0, 0, 0)))):
1362 cen1, cen2 = _peak_position(img, nwindow, plot=debug and plot)
1363
1364 n1 = numpy.append(n1, cen1)
1365 n2 = numpy.append(n2, cen2)
1366 ang1 = numpy.append(ang1, angle1[i])
1367 ang2 = numpy.append(ang2, angle2[i])
1368 sang = numpy.append(sang, sampleang[i])
1369 usedhkls.append(hkls[i])
1370 if debug:
1371 print("%8.3f %8.3f \t%.2f %.2f" % (angle1[i], angle2[i],
1372 cen1, cen2))
1373
1374 Nused = len(ang1)
1375 usedhkls = numpy.array(usedhkls)
1376
1377 if debug:
1378 print("Nused / Npoints: %d / %d" % (Nused, Npoints))
1379
1380 # guess initial parameters
1381 n10 = numpy.zeros(0, dtype=numpy.double)
1382 n20 = n10
1383 ang10 = n10
1384 ang20 = n10
1385 for i in range(Nused):
1386 if numpy.all(usedhkls[i] == (0, 0, 0)):
1387 n10 = numpy.append(n10, n1[i])
1388 n20 = numpy.append(n20, n2[i])
1389 ang10 = numpy.append(ang10, ang1[i])
1390 ang20 = numpy.append(ang20, ang2[i])
1391
1392 detdir1, detdir2 = _determine_detdir(ang10 - start[3], ang20, n10, n20,
1393 detaxis, r_i)
1394
1395 epslist = []
1396 paramlist = []
1397 epsmin = numpy.inf
1398 fitmin = None
1399
1400 print("tiltaz tilt detrot offset sampletilt+azimuth wavelength: "
1401 "error (relative) (fittime)")
1402 print("------------------------------------------------------------")
1403 # find optimal detector rotation (however keep other parameters free)
1404 detrot = start[5]
1405 if not fix[5]:
1406 for detrotstart in numpy.linspace(start[5] - 1, start[5] + 1, 20):
1407 start = start[:5] + (detrotstart,) + start[6:]
1408 eps, param, fit = _area_detector_calib_fit2(
1409 sang, ang1, ang2, n1, n2, usedhkls, experiment, material,
1410 detaxis, r_i, detdir1, detdir2, start=start, fix=fix,
1411 full_output=True)
1412 epslist.append(eps)
1413 paramlist.append(param)
1414 if epslist[-1] < epsmin:
1415 epsmin = epslist[-1]
1416 parammin = param
1417 fitmin = fit
1418 detrot = param[7]
1419 if debug:
1420 print("single fit")
1421 print(param)
1422
1423 Ntiltaz = 1 if fix[3] else 5
1424 Ntilt = 1 if fix[4] else 6
1425 Noffset = 1 if fix[6] else 100
1426 if fix[6]:
1427 Ntilt = Ntilt * 8 if not fix[4] else Ntilt
1428 Ntiltaz = Ntiltaz * 7 if not fix[3] else Ntiltaz
1429
1430 startparam = start[:5] + (detrot,) + start[6:]
1431
1432 Ntot = Ntiltaz * Ntilt * Noffset
1433 ict = 0
1434 for tiltazimuth in numpy.linspace(startparam[3] if fix[3] else 0, 360,
1435 Ntiltaz, endpoint=False):
1436 for tilt in numpy.linspace(startparam[4] if fix[4] else 0, 4, Ntilt):
1437 for offset in numpy.linspace(
1438 startparam[6] if fix[6] else - 3 + startparam[6],
1439 3 + startparam[6], Noffset):
1440 t1 = time.time()
1441 start = (start[:3] + (tiltazimuth, tilt, detrot, offset) +
1442 start[7:])
1443 eps, param, fit = _area_detector_calib_fit2(
1444 sang, ang1, ang2, n1, n2, usedhkls, experiment, material,
1445 detaxis, r_i, detdir1, detdir2, start=start, fix=fix,
1446 full_output=True)
1447 epslist.append(eps)
1448 paramlist.append(param)
1449 t2 = time.time()
1450 print("%d/%d\t%6.1f %6.2f %8.3f %8.3f %8.3f %7.2f %8.4f: "
1451 "%10.4e (%4.2f) (%5.2fsec)"
1452 % ((ict, Ntot) + start[3:] +
1453 (epslist[-1], epslist[-1] / epsmin, t2 - t1)))
1454 ict += 1
1455
1456 if epslist[-1] < epsmin:
1457 print("************************")
1458 print("new global minimum found")
1459 epsmin = epslist[-1]
1460 parammin = param
1461 fitmin = fit
1462 print("new best parameters: %.2f %.2f %10.4e %10.4e %8.4f "
1463 "%.1f %.2f %.3f %.3f %.3f %.2f %.4f" % parammin)
1464 print("************************\n")
1465
1466 (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
1467 outerangle_offset, stilt, stazimuth, wavelength) = parammin
1468
1469 if plot:
1470 if fig:
1471 plt.figure(fig.number)
1472 else:
1473 figlabel = "CCD Calib fit %d"
1474 i = 1
1475 while figlabel % i in plt.get_figlabels():
1476 i += 1
1477 plt.figure(figlabel % i)
1478 nparams = numpy.array(paramlist)
1479 neps = numpy.array(epslist)
1480 labels = (
1481 'cch1 (1)',
1482 'cch2 (1)',
1483 r'pwidth1 ($\mu$m@1m)',
1484 r'pwidth2 ($\mu$m@1m)',
1485 'distance (m)',
1486 'tiltazimuth (deg)',
1487 'tilt (deg)',
1488 'detrot (deg)',
1489 'outerangle offset (deg)',
1490 'sample tilt (deg)',
1491 'st azimuth (deg)',
1492 'wavelength (AA)')
1493 xscale = (1., 1., 1.e6, 1.e6, 1., 1., 1., 1., 1., 1., 1., 1.)
1494 for p in range(12):
1495 ax = plt.subplot(3, 4, p + 1)
1496 if plotlog:
1497 plt.semilogy(nparams[:, p] * xscale[p], neps, 'k.')
1498 else:
1499 plt.scatter(nparams[:, p] * xscale[p], neps, c=nparams[:, -1],
1500 s=10, marker='o', cmap=plt.cm.gnuplot,
1501 edgecolor='none')
1502 plt.xlabel(labels[p])
1503 if plotlog:
1504 plt.semilogy(parammin[p] * xscale[p], epsmin, 'ko',
1505 ms=8, mew=2.5, mec='k', mfc='w')
1506 else:
1507 plt.plot(parammin[p] * xscale[p], epsmin, 'ko',
1508 ms=8, mew=2.5, mec='k', mfc='w')
1509 plt.ylim(epsmin * 0.7, epsmin * 2.)
1510 plt.locator_params(nbins=4, axis='x')
1511 if p > 1:
1512 if fix[p-2]:
1513 ax.set_facecolor('0.85')
1514 plt.tight_layout()
1515
1516 if config.VERBOSITY >= config.INFO_LOW:
1517 print("total time needed for fit: %.2fsec" % (time.time() - t0))
1518 print("fitted parameters: epsilon: %10.4e (%d,%s) "
1519 % (epsmin, fitmin.info, repr(fitmin.stopreason)))
1520 print("param: (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, "
1521 "tilt, detrot, outerangle_offset, sampletilt, stazimuth, "
1522 "wavelength)")
1523 print("param: %.2f %.2f %10.4e %10.4e %.4f %.1f %.2f %.3f %.3f %.3f "
1524 "%.2f %.4f" % (cch1, cch2, pwidth1, pwidth2, distance,
1525 tiltazimuth, tilt, detrot, outerangle_offset,
1526 stilt, stazimuth, wavelength))
1527
1528 if config.VERBOSITY > 0:
1529 print("please check the resulting data (consider setting plot=True)")
1530 print("detector rotation axis / primary beam direction (given by user)"
1531 ": %s / %s" % (repr(detaxis), r_i))
1532 print("detector pixel directions / distance: %s %s / %e"
1533 % (detdir1, detdir2, distance))
1534 print("\tdetector initialization with: init_area('%s', '%s', "
1535 "cch1=%.2f, cch2=%.2f, Nch1=%d, Nch2=%d, pwidth1=%.4e, "
1536 "pwidth2=%.4e, distance=%.5f, detrot=%.3f, tiltazimuth=%.1f, "
1537 "tilt=%.3f)" % (detdir1, detdir2, cch1, cch2, N1, N2, pwidth1,
1538 pwidth2, distance, detrot, tiltazimuth, tilt))
1539 print("AND ALWAYS USE an (additional) OFFSET of %.4fdeg in the "
1540 "OUTER DETECTOR ANGLE!" % (outerangle_offset))
1541
1542 return (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth,
1543 tilt, detrot, outerangle_offset, stilt, stazimuth, wavelength), eps
1544
1545
1546 def _area_detector_calib_fit2(sang, ang1, ang2, n1, n2, hkls, experiment,
1547 material, detaxis, r_i, detdir1, detdir2,
1548 start=(None, None, 1, 0, 0, 0, 0, 0, 0, 1.0),
1549 fix=(False, False, True, False, False, False,
1550 False, False, False, False),
1551 full_output=False, debug=False):
1552 """
1553 INTERNAL FUNCTION
1554 function to calibrate the detector parameters of an area detector
1555 it determines the detector tilt possible rotations and offsets in the
1556 detector arm angles
1557
1558 Parameters
1559 ----------
1560 sang : array-like
1561 sample rocking angle (rotation direction of inner detector rotation)
1562 angle1 : array-like
1563 outer detector arm angle
1564 angle2 : array-like
1565 inner detector arm angle
1566 n1, n2 : array-like
1567 pixel number at which the beam was observed
1568 hkls : tuple or list
1569 Miller indices of the reflection were the images were taken (use
1570 (0, 0, 0)) for primary beam
1571 experiment : Experiment
1572 Experiment class object needed to get the UB matrix needed for the hkl
1573 peak treatment
1574 material : Crystal
1575 material used as reference crystal
1576 detaxis : str
1577 detector arm rotation axis default: ['z+', 'y-']
1578 detdir1, detdir2 : str
1579 detector pixel directions of first and second pixel coordinates;
1580 e.g. 'y+'
1581 r_i : str
1582 primary beam direction [xyz][+-] default 'x+'
1583
1584 start : tuple or list, optional
1585 sequence of start values of the fit for parameters, which can not be
1586 estimated automatically. these are: pwidth1, pwidth2, distance,
1587 tiltazimuth, tilt, detector_rotation, outerangle_offset, sampletilt,
1588 sampletiltazimuth, wavelength.
1589 By default: (None, None, 1, 0, 0, 0, 0, 0, 0, 1.0)
1590 fix : tuple or list, optional
1591 fix parameters of start
1592 full_output : bool, optional
1593 flag to tell if to return fit object with final parameters and detector
1594 directions
1595 debug : bool, optional
1596 flag to tell if you want to see debug output of the script (switch this
1597 to true only if you can handle it :))
1598
1599 Returns
1600 -------
1601 float
1602 final epsilon of the fit
1603 param : list, optional
1604 if full_output: fit parameters
1605 fit : object, optional
1606 if full_output: fit object
1607 """
1608
1609 def areapixel2(params, detectorDir1, detectorDir2, r_i, detectorAxis,
1610 *args, **kwargs):
1611 """
1612 angular to momentum space conversion for pixels of an area detector the
1613 center pixel is in direction of self.r_i when detector angles are zero
1614
1615 the detector geometry must be given to the routine
1616
1617 Parameters
1618 ----------
1619 params : list or tuple
1620 parameters of the detector calibration model
1621 (cch1, cch2, pwidth1, pwidth2, tiltazimuth, tilt, detrot):
1622 cch1, 2: center pixel, in direction of r_i at zero detectorAngles;
1623 pwidth1, 2: width of one pixel (same unit as distance);
1624 distance: distance of center pixel from center of rotation;
1625 tiltazimuth: direction of the tilt vector in the detector plane (in
1626 degree);
1627 tilt: tilt of the detector plane around an axis normal to the
1628 direction given by the tiltazimuth;
1629 detrot: detector rotation around the primary beam direction as
1630 given by r_i
1631 detectorDir1 : str
1632 direction of the detector (along the pixel direction 1); e.g. 'z+'
1633 means higher pixel numbers at larger z positions
1634 detectorDir2 : str
1635 direction of the detector (along the pixel direction 2); e.g. 'x+'
1636 r_i : str
1637 primary beam direction e.g. 'x+'
1638 detectorAxis : list or tuple
1639 detector circles; e.g. ['z+', 'y-'] would mean a detector arm with
1640 a two rotations
1641 *args : array-like
1642 sample, detector angles and channel numbers;
1643 *sAngle* sample rocking angle as numpy array, lists or Scalars.
1644 *dAngles* as numpy array, lists or Scalars. In total
1645 len(detectorAxis) values must be given starting with the outer most
1646 circle. All arguments must have the same shape or length.
1647 *channel numbers* `n1` and `n2` where the primary beam hits the
1648 detector with the same length as the detector values
1649
1650 delta : list of array-like, optional
1651 giving delta angles to correct the given ones for misalignment.
1652 delta must be an numpy array or list of len(*dAngles). used angles
1653 are than *args - delta
1654 UB : array-like, optional
1655 orientation matrix of the sample
1656 wl : float or str, optional
1657 x-ray wavelength in angstroem
1658 deg : bool, optional
1659 flag to tell if angles are passed as degree (default: True)
1660
1661 Returns
1662 -------
1663 ndarray
1664 reciprocal space position of detector pixels n1, n2 in a
1665 numpy.ndarray of shape (len(args) , 3)
1666 """
1667
1668 # check detector circle argument
1669 if isinstance(detectorAxis, str):
1670 dAxis = list([detectorAxis])
1671 else:
1672 dAxis = list(detectorAxis)
1673
1674 _sampleAxis_str = dAxis[-1]
1675 # add detector rotation around primary beam
1676 dAxis += [r_i]
1677 _detectorAxis_str = ''
1678 for circ in dAxis:
1679 _detectorAxis_str += circ
1680
1681 Nd = len(dAxis)
1682 Nargs = Nd + 2 - 1 + 1
1683
1684 # check detectorDir
1685 _area_detdir1 = detectorDir1
1686 _area_detdir2 = detectorDir2
1687
1688 # parse parameter arguments
1689 _area_cch1 = float(params[0])
1690 _area_cch2 = float(params[1])
1691 _area_pwidth1 = float(params[2])
1692 _area_pwidth2 = float(params[3])
1693 _area_distance = float(params[4])
1694 _area_tiltazimuth = math.radians(params[5])
1695 _area_tilt = math.radians(params[6])
1696 _area_rot = float(params[7])
1697 _area_ri = xumath.getVector(r_i) * _area_distance
1698
1699 # kwargs
1700 wl = utilities.wavelength(kwargs.get('wl', 1.))
1701 deg = kwargs.get('deg', True)
1702 UB = kwargs.get('UB', numpy.identity(3))
1703
1704 delta = numpy.asarray(kwargs.get('delta', numpy.zeros(Nd)),
1705 dtype=numpy.double)
1706 if delta.size != Nd - 1 + 1:
1707 raise InputError("QConversionPixel: keyword argument delta "
1708 "does not have an appropriate shape")
1709
1710 # prepare angular arrays from *args
1711 # need one sample angle and one detector angle array
1712 if len(args) != Nargs:
1713 raise InputError("QConversionPixel: wrong amount (%d) of "
1714 "arguments given, number of arguments should be "
1715 "%d" % (len(args), Nargs))
1716
1717 try:
1718 Npoints = len(args[0])
1719 except (TypeError, IndexError):
1720 Npoints = 1
1721
1722 sAngles = numpy.array((), dtype=numpy.double)
1723 for i in range(1):
1724 arg = args[i]
1725 if isinstance(arg, numbers.Number):
1726 arg = numpy.array([arg], dtype=numpy.double)
1727 elif isinstance(arg, list):
1728 arg = numpy.array(arg, dtype=numpy.double)
1729 arg = arg - delta[i]
1730 sAngles = numpy.concatenate((sAngles, arg))
1731
1732 dAngles = numpy.array((), dtype=numpy.double)
1733 for i in range(1, Nd):
1734 arg = args[i]
1735 if isinstance(arg, numbers.Number):
1736 arg = numpy.array([arg], dtype=numpy.double)
1737 elif isinstance(arg, list):
1738 arg = numpy.array(arg, dtype=numpy.double)
1739 arg = arg - delta[i]
1740 dAngles = numpy.concatenate((dAngles, arg))
1741 # add detector rotation around primary beam
1742 dAngles = numpy.concatenate((
1743 dAngles,
1744 numpy.ones(arg.shape, dtype=numpy.double) * _area_rot))
1745
1746 # read channel numbers
1747 n1 = numpy.array((), dtype=numpy.double)
1748 n2 = numpy.array((), dtype=numpy.double)
1749
1750 arg = args[Nd]
1751 if isinstance(arg, numbers.Number):
1752 arg = numpy.array([arg], dtype=numpy.double)
1753 elif isinstance(arg, list):
1754 arg = numpy.array(arg, dtype=numpy.double)
1755 n1 = arg
1756
1757 arg = args[Nd + 1]
1758 if isinstance(arg, numbers.Number):
1759 arg = numpy.array([arg], dtype=numpy.double)
1760 elif isinstance(arg, list):
1761 arg = numpy.array(arg, dtype=numpy.double)
1762 n2 = arg
1763
1764 sAngles.shape = (1, Npoints)
1765 sAngles = sAngles.transpose()
1766 dAngles.shape = (Nd, Npoints)
1767 dAngles = dAngles.transpose()
1768
1769 if deg:
1770 sAngles = radians(sAngles)
1771 dAngles = radians(dAngles)
1772
1773 qpos = cxrayutilities.ang2q_conversion_area_pixel2(
1774 sAngles, dAngles, n1, n2, _area_ri, _sampleAxis_str,
1775 _detectorAxis_str, _area_cch1, _area_cch2, _area_pwidth1,
1776 _area_pwidth2, _area_detdir1, _area_detdir2, _area_tiltazimuth,
1777 _area_tilt, UB, wl, config.NTHREADS)
1778
1779 return qpos[:, 0], qpos[:, 1], qpos[:, 2]
1780
1781 def afunc(param, x, detectorDir1, detectorDir2, r_i, detectorAxis):
1782 """
1783 function for fitting the detector parameters
1784 basically this is a wrapper for the areapixel function
1785
1786 parameters
1787 ----------
1788 param : list or tuple
1789 fit parameters (cch1, cch2, pwidth1, pwidth2, distance,
1790 tiltazimuth, tilt, detrot, outerangle_offset, sampletilt,
1791 sampletiltazimuth, wavelength)
1792 x : array-like
1793 independent variables; contains (sang, angle1, angle2, n1, n2,
1794 hkls) with shape (8, Npoints)
1795 detectorDir1 : str
1796 direction of the detector (along the pixel direction 1); e.g. 'z+'
1797 means higher pixel numbers at larger z positions
1798 detectorDir2 : str
1799 direction of the detector (along the pixel direction 2); e.g. 'x+'
1800 r_i : str
1801 primary beam direction e.g. 'x+'
1802 detectorAxis : list or tuple
1803 detector circles; e.g. ['z+', 'y-'] would mean a detector arm with
1804 a two rotations
1805
1806 Returns
1807 -------
1808 ndarray
1809 reciprocal space position of detector pixels n1, n2 in a
1810 numpy.ndarray of shape (3, x.shape[1])
1811 """
1812
1813 sang = x[0, :]
1814 angle1 = x[1, :]
1815 angle2 = x[2, :]
1816 n1 = x[3, :]
1817 n2 = x[4, :]
1818 H = x[5, :]
1819 K = x[6, :]
1820 L = x[7, :]
1821
1822 # use only positive tilt and sample tilt
1823 param[6] = abs(param[6])
1824 param[9] = abs(param[9])
1825 wl = param[11]
1826 cp = math.cos(math.radians(param[10]))
1827 sp = math.sin(math.radians(param[10]))
1828 cc = math.cos(math.radians(param[9]))
1829 sc = math.sin(math.radians(param[9]))
1830
1831 # UB matrix due to tilt at symmetric peak
1832 U1 = numpy.array(((cp * cc, -sp, cp * sc),
1833 (sp * cc, cp, sp * sc),
1834 (-sc, 0, cc)), dtype=numpy.double)
1835 U2 = experiment._transform.matrix
1836 U = numpy.dot(U1, U2)
1837 B = material.B
1838 ubmat = numpy.dot(U, B)
1839
1840 (qx, qy, qz) = areapixel2(
1841 param[:-4], detectorDir1, detectorDir2, r_i, detectorAxis,
1842 sang, angle1, angle2, n1, n2, delta=[0, param[8], 0.],
1843 distance=1., UB=ubmat, wl=wl)
1844
1845 return (qx - H) ** 2 + (qy - K) ** 2 + (qz - L) ** 2
1846
1847 Npoints = len(ang1)
1848
1849 # guess initial parameters
1850 n10 = numpy.zeros(0, dtype=numpy.double)
1851 n20 = n10
1852 ang10 = n10
1853 ang20 = n10
1854 n1s = numpy.zeros(0, dtype=numpy.double)
1855 n2s = n1s
1856 ang1s = n1s
1857 ang2s = n1s
1858 sangs = n1s
1859
1860 for i in range(Npoints):
1861 if numpy.all(hkls[i] == (0, 0, 0)):
1862 n10 = numpy.append(n10, n1[i])
1863 n20 = numpy.append(n20, n2[i])
1864 ang10 = numpy.append(ang10, ang1[i])
1865 ang20 = numpy.append(ang20, ang2[i])
1866 else:
1867 n1s = numpy.append(n1s, n1[i])
1868 n2s = numpy.append(n2s, n2[i])
1869 ang1s = numpy.append(ang1s, ang1[i])
1870 ang2s = numpy.append(ang2s, ang2[i])
1871 sangs = numpy.append(sangs, sang[i])
1872
1873 # center channel and detector pixel direction and pixel size
1874 (s1, i1), r1 = xumath.linregress(ang10 - start[3], n10)
1875 (s2, i2), r2 = xumath.linregress(ang10 - start[3], n20)
1876 (s3, i3), r3 = xumath.linregress(ang20, n10)
1877 (s4, i4), r4 = xumath.linregress(ang20, n20)
1878
1879 distance = start[2]
1880 if ((r1 + r2 > r3 + r4) and r1 > r2) or ((r1 + r2 < r3 + r4) and r3 < r4):
1881 cch1 = i1
1882 cch2 = i4
1883 pwidth1 = 2 * distance / numpy.abs(s1) * math.tan(math.radians(0.5))
1884 pwidth2 = 2 * distance / numpy.abs(s4) * math.tan(math.radians(0.5))
1885 else:
1886 cch1 = i3
1887 cch2 = i2
1888 pwidth1 = 2 * distance / numpy.abs(s3) * math.tan(math.radians(0.5))
1889 pwidth2 = 2 * distance / numpy.abs(s2) * math.tan(math.radians(0.5))
1890 if numpy.isscalar(start[0]):
1891 pwidth1 = start[0]
1892 if numpy.isscalar(start[1]):
1893 pwidth2 = start[1]
1894 if numpy.isscalar(start[0]) or numpy.isscalar(start[1]):
1895 # find biggest correlation and recalculate distance
1896 idxmax = numpy.argmax((r1, r2, r3, r4))
1897 s = (s1, s2, s3, s4)[idxmax]
1898 distance = abs(s) / math.tan(math.radians(0.5)) / 2
1899 distance *= pwidth1 if idxmax < 2 else pwidth2
1900 tilt = abs(start[4])
1901 tiltazimuth = start[3]
1902 detrot = start[5]
1903 outerangle_offset = start[6]
1904 sampletilt = start[7]
1905 stazimuth = start[8]
1906 wavelength = start[9]
1907 # parameters for the fitting
1908 param = (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
1909 outerangle_offset, sampletilt, stazimuth, wavelength)
1910
1911 # determine better start values for sample tilt and azimuth
1912 (qx, qy, qz) = areapixel2(
1913 param[:-4], detdir1, detdir2, r_i, detaxis, sangs, ang1s, ang2s,
1914 n1s, n2s, delta=[0, param[8], 0.], wl=wavelength)
1915 if debug:
1916 print("average qx: %.3f(%.3f)" % (numpy.average(qx), numpy.std(qx)))
1917 print("average qy: %.3f(%.3f)" % (numpy.average(qy), numpy.std(qy)))
1918 print("average qz: %.3f(%.3f)" % (numpy.average(qz), numpy.std(qz)))
1919
1920 qvecav = (numpy.average(qx), numpy.average(qy), numpy.average(qz))
1921 sampletilt = xumath.VecAngle(experiment.Transform(experiment.ndir),
1922 qvecav, deg=True)
1923 stazimuth = -xumath.VecAngle(
1924 experiment.Transform(experiment.idir),
1925 qvecav - xumath.VecDot(experiment.Transform(experiment.ndir), qvecav) *
1926 experiment.Transform(experiment.ndir), deg=True)
1927 param = (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
1928 outerangle_offset, sampletilt, stazimuth, wavelength)
1929
1930 if debug:
1931 print("initial parameters: ")
1932 print("primary beam / detector pixel directions / distance: %s / "
1933 "%s %s / %e" % (r_i, detdir1, detdir2, distance))
1934 print("param: (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, "
1935 "tilt, detrot, outerangle_offset, sampletilt, stazimuth, "
1936 "wavelength)")
1937 print("param: %.2f %.2f %10.4e %10.4e %.3f %.1f %.2f %.3f %.3f %.3f "
1938 "%.2f %.4f" % param)
1939
1940 # set data
1941 x = numpy.empty((8, Npoints), dtype=numpy.double)
1942 x[0, :] = sang
1943 x[1, :] = ang1
1944 x[2, :] = ang2
1945 x[3, :] = n1
1946 x[4, :] = n2
1947 x[5, :] = hkls[:, 0]
1948 x[6, :] = hkls[:, 1]
1949 x[7, :] = hkls[:, 2]
1950
1951 data = odr.Data(x, y=1)
1952 # define model for fitting
1953 model = odr.Model(afunc, extra_args=(detdir1, detdir2, r_i, detaxis),
1954 implicit=True)
1955 # check if parameters need to be fixed
1956 ifixb = ()
1957 for i in range(len(fix)):
1958 ifixb += (int(not fix[i]),)
1959
1960 my_odr = odr.ODR(data, model, beta0=param, ifixb=(1, 1) + ifixb,
1961 ifixx=(0, 0, 0, 0, 0, 0, 0, 0),
1962 stpb=(0.4, 0.4, pwidth1/50., pwidth2/50., distance/1000,
1963 2, 0.125, 0.01, 0.01, 0.01, 1., 0.0001),
1964 sclb=(1/abs(cch1), 1/abs(cch2), 1/pwidth1,
1965 1/pwidth2, 1/distance, 1/90., 1/0.2, 1/0.2, 1/0.2,
1966 1/0.1, 1/90., 1.),
1967 maxit=200, ndigit=12, sstol=1e-11, partol=1e-11)
1968 if debug:
1969 my_odr.set_iprint(final=1)
1970 my_odr.set_iprint(iter=2)
1971
1972 fit = my_odr.run()
1973
1974 (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
1975 outerangle_offset, sampletilt, stazimuth, wavelength) = fit.beta
1976 # fix things in parameters
1977 tiltazimuth = tiltazimuth % 360.
1978 stazimuth = stazimuth % 360.
1979 tilt = abs(tilt)
1980 sampletilt = abs(sampletilt)
1981
1982 final_q = afunc([cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt,
1983 detrot, outerangle_offset, sampletilt, stazimuth,
1984 wavelength],
1985 x, detdir1, detdir2, r_i, detaxis)
1986 final_error = numpy.mean(final_q)
1987
1988 if debug:
1989 print("fitted parameters: (%e, %d, %s) " % (final_error, fit.info,
1990 repr(fit.stopreason)))
1991 print("primary beam / detector pixel directions / distance: %s / %s "
1992 "%s / %e" % (r_i, detdir1, detdir2, distance))
1993 print("param: (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, "
1994 "tilt, detrot, outerangle_offset, sampletilt, sampletiltazimuth,"
1995 " wavelength)")
1996 print("param: %.2f %.2f %10.4e %10.4e %.3f %.1f %.2f %.3f %.3f %.3f "
1997 "%.2f %.4f" % (cch1, cch2, pwidth1, pwidth2, distance,
1998 tiltazimuth, tilt, detrot, outerangle_offset,
1999 sampletilt, stazimuth, wavelength))
2000
2001 if full_output:
2002 return final_error, (cch1, cch2, pwidth1, pwidth2, distance,
2003 tiltazimuth, tilt, detrot, outerangle_offset,
2004 sampletilt, stazimuth, wavelength), fit
2005 else:
2006 return final_error
2007
2008
2009 #################################################
2010 def psd_refl_align(primarybeam, angles, channels, plot=True):
2011 """
2012 function which calculates the angle at which the sample
2013 is parallel to the beam from various angles and detector channels
2014 from the reflected beam. The function can be used during the half
2015 beam alignment with a linear detector.
2016
2017 Parameters
2018 ----------
2019 primarybeam : int
2020 primary beam channel number
2021 angles : list or array-like
2022 incidence angles
2023 channels : list or array-like
2024 corresponding detector channels
2025 plot : bool, optional
2026 flag to specify if a visualization of the fit is wanted default : True
2027
2028 Returns
2029 -------
2030 float
2031 angle at which the sample is parallel to the beam
2032
2033 Examples
2034 --------
2035 >>> psd_refl_align(500, [0, 0.1, 0.2, 0.3], [550, 600, 640, 700])
2036 """
2037 if plot:
2038 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.psd_refl_'
2039 'align')
2040
2041 p, rsq = xumath.linregress(numpy.asarray(channels), numpy.asarray(angles))
2042 zeropos = numpy.polyval(p, primarybeam)
2043
2044 if plot:
2045 xmin = min(min(channels), primarybeam)
2046 xmax = max(max(channels), primarybeam)
2047 ymin = min(min(angles), zeropos)
2048 ymax = max(max(angles), zeropos)
2049 # open new figure for the plot
2050 plt.figure()
2051 plt.plot(channels, angles, 'kx', ms=8., mew=2.)
2052 plt.plot([xmin - (xmax - xmin) * 0.1, xmax + (xmax - xmin) * 0.1],
2053 numpy.polyval(p,
2054 [xmin - (xmax - xmin) * 0.1,
2055 xmax + (xmax - xmin) * 0.1]),
2056 'g-',
2057 linewidth=1.5)
2058 ax = plt.gca()
2059 plt.grid()
2060 ax.set_xlim(xmin - (xmax - xmin) * 0.15, xmax + (xmax - xmin) * 0.15)
2061 ax.set_ylim(ymin - (ymax - ymin) * 0.15, ymax + (ymax - ymin) * 0.15)
2062 plt.vlines(primarybeam,
2063 ymin - (ymax - ymin) * 0.1,
2064 ymax + (ymax - ymin) * 0.1,
2065 linewidth=1.5)
2066 plt.xlabel("PSD Channel")
2067 plt.ylabel("sample angle")
2068 plt.tight_layout()
2069
2070 if config.VERBOSITY >= config.INFO_LOW:
2071 print("XU.analysis.psd_refl_align: sample is parallel to beam at "
2072 "goniometer angle %8.4f (R^2=%6.4f)" % (zeropos, rsq))
2073 return zeropos
2074
2075
2076 #################################################
2077 # miscut calculation from alignment in 2 and
2078 # more azimuths
2079 #################################################
2080 def miscut_calc(phi, aomega, zeros=None, omega0=None, plot=True):
2081 """
2082 function to calculate the miscut direction and miscut angle of a sample
2083 by fitting a sinusoidal function to the variation of the aligned
2084 omega values of more than two reflections.
2085 The function can also be used to fit reflectivity alignment values
2086 in various azimuths.
2087
2088 Parameters
2089 ----------
2090 phi : list, tuple or array-like
2091 azimuths in which the reflection was aligned (deg)
2092 aomega : list, tuple or array-like
2093 aligned omega values (deg)
2094 zeros : list, tuple or array-like, optional
2095 angles at which surface is parallel to the beam (deg). For the analysis
2096 the angles (aomega - zeros) are used.
2097 omega0 : float, optional
2098 if specified the nominal value of the reflection is not included as fit
2099 parameter, but is fixed to the specified value. This value is MANDATORY
2100 if ONLY TWO AZIMUTHs are given.
2101 plot : bool, optional
2102 flag to specify if a visualization of the fit is wanted.
2103 default: True
2104
2105 Returns
2106 -------
2107 omega0 : float
2108 the omega value of the reflection should be close to the nominal one
2109 phi0 : float
2110 the azimuth in which the primary beam looks upstairs
2111 miscut : float
2112 amplitude of the sinusoidal variation == miscut angle
2113 """
2114 if plot:
2115 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.miscut_'
2116 'calc')
2117
2118 if zeros is not None:
2119 om = (numpy.array(aomega) - numpy.array(zeros))
2120 else:
2121 om = numpy.array(aomega)
2122
2123 a = numpy.array(phi)
2124
2125 if omega0 is None:
2126 # first guess for the parameters
2127 # omega0, phi0, miscut
2128 p0 = (om.mean(), a[om.argmax()], om.max() - om.min())
2129
2130 def fitfunc(p, phi):
2131 return abs(p[2]) * \
2132 cos(radians(phi - (p[1] % 360.))) + p[0]
2133
2134 else:
2135 # first guess for the parameters
2136 p0 = (a[om.argmax()], om.max() - om.min()) # omega0, phi0, miscut
2137
2138 def fitfunc(p, phi):
2139 return abs(p[1]) * \
2140 cos(radians(phi - (p[0] % 360.))) + omega0
2141
2142 def errfunc(p, phi, om):
2143 return fitfunc(p, phi) - om
2144
2145 p1, success = optimize.leastsq(errfunc, p0, args=(a, om), maxfev=10000)
2146 if config.VERBOSITY >= config.INFO_ALL:
2147 print("xu.analysis.miscut_calc: leastsq optimization return value: "
2148 "%d" % success)
2149
2150 if plot:
2151 plt.figure()
2152 plt.plot(a, om, 'kx', mew=2, ms=8)
2153 linx = numpy.linspace(a.min() - 45, a.min() + 360 - 45, num=1000)
2154 plt.plot(linx, fitfunc(p1, linx), 'g-', linewidth=1.5)
2155 plt.grid()
2156 plt.xlabel("azimuth")
2157 plt.ylabel("aligned sample angle")
2158
2159 if omega0 is None:
2160 ret = [p1[0], p1[1] % 360., abs(p1[2])]
2161 else:
2162 ret = [omega0] + [p1[0] % 360., abs(p1[1])]
2163
2164 if config.VERBOSITY >= config.INFO_LOW:
2165 print("xu.analysis.miscut_calc: \n"
2166 "\t fitted reflection angle: %8.3f \n"
2167 "\t looking upstairs at phi: %8.2f \n"
2168 "\t miscut angle: %8.3f \n" % (ret[0], ret[1], ret[2]))
2169
2170 return ret
2171
2172
2173 #################################################
2174 # correct substrate Bragg peak position in
2175 # reciprocal space maps
2176 #################################################
2177 def fit_bragg_peak(om, tt, psd, omalign, ttalign, exphxrd, frange=(0.03, 0.03),
2178 peaktype='Gauss', plot=True):
2179 r"""
2180 helper function to determine the Bragg peak position in a reciprocal
2181 space map used to obtain the position needed for correction of the data.
2182 the determination is done by fitting a two dimensional Gaussian
2183 (xrayutilities.math.Gauss2d) or Lorentzian
2184 (xrayutilities.math.Lorentz2d)
2185
2186 PLEASE ALWAYS CHECK THE RESULT CAREFULLY!
2187
2188 Parameters
2189 ----------
2190 om, tt : array-like
2191 angular coordinates of the measurement either with size of psd or of
2192 psd.shape[0]
2193 psd : array-like
2194 intensity values needed for fitting
2195 omalign : float
2196 aligned omega value, used as first guess in the fit
2197 ttalign : float
2198 aligned two theta values used as first guess in the fit these values
2199 are also used to set the range for the fit: the peak should be within
2200 +/-frange\AA^{-1} of those values
2201 exphxrd : Experiment
2202 experiment class used for the conversion between angular and reciprocal
2203 space.
2204 frange : tuple of float, optional
2205 data range used for the fit in both directions (see above for details
2206 default:(0.03, 0.03) unit: \AA^{-1})
2207 peaktype : {'Gauss', 'Lorentz'}
2208 peak type to fit
2209 plot : bool, optional
2210 if True (default) function will plot the result of the fit in
2211 comparison with the measurement.
2212
2213 Returns
2214 -------
2215 omfit, ttfit : float
2216 fitted angular values
2217 params : list
2218 fit parameters (of the Gaussian/Lorentzian)
2219 covariance : ndarray
2220 covariance matrix of the fit parameters
2221 """
2222 if plot:
2223 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.fit_bragg'
2224 '_peak')
2225
2226 if peaktype == 'Gauss':
2227 func = xumath.Gauss2d
2228 elif peaktype == 'Lorentz':
2229 func = xumath.Lorentz2d
2230 else:
2231 raise InputError("peaktype must be either 'Gauss' or 'Lorentz'")
2232
2233 if om.size != psd.size:
2234 [qx, qy, qz] = exphxrd.Ang2Q.linear(om, tt)
2235 else:
2236 [qx, qy, qz] = exphxrd.Ang2Q(om, tt)
2237 [qxsub, qysub, qzsub] = exphxrd.Ang2Q(omalign, ttalign)
2238 params = [qysub, qzsub, 0.001, 0.001, psd.max(), 0, 0.]
2239 drange = [qysub - frange[0], qysub + frange[0], qzsub - frange[1],
2240 qzsub + frange[1]]
2241 params, covariance = xumath.fit_peak2d(
2242 qy.flatten(), qz.flatten(), psd.flatten(), params, drange,
2243 func, maxfev=10000)
2244 # correct params
2245 params[6] = params[6] % (numpy.pi)
2246 if params[5] < 0:
2247 params[5] = 0
2248
2249 [omfit, dummy, dummy, ttfit] = exphxrd.Q2Ang((0, params[0], params[1]),
2250 trans=False, geometry="real")
2251 if config.VERBOSITY >= config.INFO_LOW:
2252 print("XU.analysis.fit_bragg_peak:fitted peak angles: \n\tom =%8.4f\n"
2253 "\ttt =%8.4f" % (omfit, ttfit))
2254
2255 if plot:
2256 plt.figure()
2257 plt.clf()
2258 from ..gridder2d import Gridder2D
2259 gridder = Gridder2D(50, 50)
2260 mask = (qy.flatten() > drange[0]) * (qy.flatten() < drange[1]) * \
2261 (qz.flatten() > drange[2]) * (qz.flatten() < drange[3])
2262 gridder(qy.flatten()[mask], qz.flatten()[mask], psd.flatten()[mask])
2263 # calculate intensity which should be plotted
2264 INT = utilities.maplog(gridder.data.transpose(), 4, 0)
2265 QXm = gridder.xmatrix
2266 QZm = gridder.ymatrix
2267 cl = plt.contour(gridder.xaxis, gridder.yaxis,
2268 utilities.maplog(func(QXm, QZm, *params), 4, 0).T,
2269 8, colors='k', linestyles='solid')
2270 cf = plt.contourf(gridder.xaxis, gridder.yaxis, INT, 35)
2271 cf.collections[0].set_label('data')
2272 cl.collections[0].set_label('fit')
2273 # plt.legend() # for some reason not working?
2274 plt.colorbar(extend='min')
2275 plt.title("plot shows only coarse data! fit used raw data!")
2276
2277 return omfit, ttfit, params, covariance
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module to parse xrayutilities user-specific config file
19 the parsed values are provide as global constants for the use
20 in other parts of xrayutilities. The config file with the default constants
21 is found in the python installation path of xrayutilities. It is however not
22 recommended to change things there, instead the user-specific config file
23 ~/.xrayutilities.conf or the local xrayutilities.conf file should be used.
24 """
25
26 import configparser
27 import os.path
28 from ast import literal_eval
29
30 from . import __path__, utilities_noconf
31
32 # so far parsed config variables are
33 #
34 # wavelength
35 # energy
36 # verbosity
37 # nthreads
38 # dynlow
39 # dynhigh
40 # epsilon
41 # database filename for atomic structure factors
42 # kappa_plane and kappa_angle
43
44 xuParser = configparser.ConfigParser()
45 xuParser.optionxform = str
46
47
48 def trytomake(obj, key, typefunc):
49 try:
50 obj[key] = typefunc(obj[key])
51 except KeyError:
52 pass
53
54
55 # read global default values for configuration variables
56 with open(os.path.join(__path__[0], "xrayutilities_default.conf")) as conffile:
57 xuParser.read_file(conffile)
58
59 # read user configuration and local configuration if available
60 cfiles = xuParser.read([
61 os.path.expanduser(os.path.join("~", ".xrayutilities.conf")),
62 "xrayutilities.conf"])
63
64 # set global variables according to configuration
65 sect = "xrayutilities"
66 INFO_LOW = xuParser.getint(sect, "info_low")
67 INFO_ALL = xuParser.getint(sect, "info_all")
68 DEBUG = xuParser.getint(sect, "debug")
69
70 VERBOSITY = xuParser.getint(sect, "verbosity")
71 try:
72 WAVELENGTH = xuParser.getfloat(sect, "wavelength")
73 except ValueError:
74 WAVELENGTH = xuParser.get(sect, "wavelength")
75 except configparser.NoOptionError:
76 pass
77
78 try:
79 ENERGY = xuParser.getfloat(sect, "energy")
80 except ValueError:
81 ENERGY = xuParser.get(sect, "energy")
82 except configparser.NoOptionError:
83 ENERGY = utilities_noconf.lam2en(utilities_noconf.wavelength(WAVELENGTH))
84 WAVELENGTH = utilities_noconf.en2lam(utilities_noconf.energy(ENERGY))
85
86 # number of threads in parallel section of c-code
87 NTHREADS = xuParser.getint(sect, "nthreads")
88
89 # default parameters for the maplog function
90 DYNLOW = xuParser.getfloat(sect, "dynlow")
91 DYNHIGH = xuParser.getfloat(sect, "dynhigh")
92
93 # small number needed for error checks
94 EPSILON = xuParser.getfloat(sect, "epsilon")
95
96 # name of the database with atomic scattering factors
97 DBNAME = xuParser.get(sect, "dbname")
98
99 # kappa goniometer specific config parameters
100 KAPPA_PLANE = xuParser.get(sect, "kappa_plane")
101 KAPPA_ANGLE = xuParser.getfloat(sect, "kappa_angle")
102
103 # parser Powder profile related variables
104 POWDER = dict()
105
106 subsec = 'classoptions'
107 POWDER[subsec] = dict(xuParser.items("powder"))
108 trytomake(POWDER[subsec], 'oversampling', int)
109 for k in ('gaussian_smoother_bins_sigma', 'window_width'):
110 trytomake(POWDER[subsec], k, float)
111
112 subsec = 'global'
113 POWDER[subsec] = dict(xuParser.items("powder.global"))
114 for k in ('diffractometer_radius', 'equatorial_divergence_deg'):
115 trytomake(POWDER[subsec], k, float)
116
117 subsec = 'emission'
118 POWDER[subsec] = dict(xuParser.items("powder.emission"))
119 for k in ('crystallite_size_gauss', 'crystallite_size_lor',
120 'strain_lor', 'strain_gauss'):
121 trytomake(POWDER[subsec], k, float)
122 for k in ('emiss_wavelengths', 'emiss_intensities',
123 'emiss_gauss_widths', 'emiss_lor_widths'):
124 trytomake(POWDER[subsec], k, literal_eval)
125 if 'emiss_wavelengths' in POWDER[subsec]:
126 POWDER[subsec]['emiss_wavelengths'] = tuple(
127 utilities_noconf.wavelength(wl) * 1e-10
128 for wl in POWDER[subsec]['emiss_wavelengths'])
129
130 subsec = 'axial'
131 POWDER[subsec] = dict(xuParser.items("powder.axial"))
132 trytomake(POWDER[subsec], 'n_integral_points', int)
133 for k in ('slit_length_source', 'slit_length_target', 'length_sample',
134 'angI_deg', 'angD_deg'):
135 trytomake(POWDER[subsec], k, float)
136
137 subsec = 'absorption'
138 POWDER[subsec] = dict(xuParser.items("powder.absorption"))
139 for k in ('absorption_coefficient', 'sample_thickness'):
140 trytomake(POWDER[subsec], k, float)
141
142 subsec = 'si_psd'
143 POWDER[subsec] = dict(xuParser.items("powder.si_psd"))
144 trytomake(POWDER[subsec], 'si_psd_window_bounds', literal_eval)
145
146 subsec = 'receiver_slit'
147 POWDER[subsec] = dict(xuParser.items("powder.receiver_slit"))
148 trytomake(POWDER[subsec], 'slit_width', float)
149
150 subsec = 'tube_tails'
151 POWDER[subsec] = dict(xuParser.items("powder.tube_tails"))
152 for k in ('main_width', 'tail_left', 'tail_right', 'tail_intens'):
153 trytomake(POWDER[subsec], k, float)
154
155 if VERBOSITY >= DEBUG:
156 print("XU.config: xrayutilities configuration files: %s" % repr(cfiles))
157 print("xrayutilities configuration:")
158 for (name, value) in xuParser.items("xrayutilities"):
159 print("%s: %s" % (name, value))
160 print("---")
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 xrayutilities derives its own exceptions which are raised
19 upon wrong input when calling one of xrayutilities functions.
20 none of the pre-defined exceptions is made for that purpose.
21 """
22
23 # other used Exception should mainly be the python built-in exceptions
24 #
25 # * TypeError
26 # Raised when an operation or function is applied to an object of
27 # inappropriate type
28 #
29 # * ValueError
30 # Raised when a operation or function receives an argument that
31 # has the right type but an inappropriate value
32 #
33 # * UserWarning
34 # Base class for warnings generated by user code
35
36
37 class InputError(Exception):
38
39 """
40 Exception raised for errors in the input.
41 Either wrong datatype not handled by TypeError or missing mandatory
42 keyword argument (Note that the obligation to give keyword arguments
43 might depend on the value of the arguments itself)
44
45 Parameters
46 ----------
47 expr : str
48 input expression in which the error occurred
49 msg : str
50 explanation of the error
51 """
52
53 def __init__(self, msg):
54 self.msg = msg
55
56 def __str__(self):
57 return self.msg
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17 # Copyright (C) 2012 Tanja Etzelstorfer <tanja.etzelstorfer@jku.at>
18
19 """
20 module helping with planning and analyzing experiments.
21 various classes are provided for describing experimental geometries,
22 calculationof angular coordinates of Bragg reflections, conversion of angular
23 coordinates to Q-space and determination of powder diffraction peak positions.
24
25 The strength of the module is the versatile QConversion module which can be
26 configured to describe almost any goniometer geometry.
27 """
28
29 import copy
30 import numbers
31 import re
32 import warnings
33
34 import numpy
35 from numpy.linalg import norm
36
37 # package internal imports
38 from . import config, cxrayutilities, math, utilities
39 from .exception import InputError
40
41 # regular expression to check goniometer circle syntax
42 directionSyntax = re.compile("[xyz][+-]")
43 circleSyntaxDetector = re.compile("([xyz][+-])|(t[xyz])")
44 circleSyntaxSample = re.compile("[xyzk][+-]")
45
46
47 class QConversion(object):
48
49 """
50 Class for the conversion of angular coordinates to momentum space for
51 arbitrary goniometer geometries and X-ray energy. Both angular scans
52 (where some goniometer angles change during data acquisition) and energy
53 scans (where the energy is varied during acquisition) as well as mixed
54 cases can be treated.
55
56 the class is configured with the initialization and does provide three
57 distinct routines for conversion to momentum space for
58
59 * point detector: point(...) or __call__()
60 * linear detector: linear(...)
61 * area detector: area(...)
62
63 linear() and area() can only be used after the init_linear()
64 or init_area() routines were called
65 """
66
67 _valid_init_kwargs = {'en': 'x-ray energy',
68 'wl': 'x-ray wavelength',
69 'UB': 'orientation/orthonormalization matrix'}
70 _valid_call_kwargs = {'delta': 'angle offsets',
71 'wl': 'x-ray wavelength',
72 'en': 'x-ray energy',
73 'UB': 'orientation/orthonormalization matrix',
74 'deg': 'True if angles are in degrees',
75 'sampledis': 'sample displacement vector'}
76 _valid_linear_kwargs = {'Nav': 'number of channels for block-average',
77 'roi': 'region of interest'}
78
79 def __init__(self, sampleAxis, detectorAxis, r_i, **kwargs):
80 """
81 initialize QConversion object.
82 This means the rotation axis of the sample and detector circles
83 need to be given: starting with the outer most circle.
84
85 Parameters
86 ----------
87 sampleAxis : list or tuple
88 sample circles, e.g. ['x+', 'z+'] would mean two sample circles
89 whereas the outer one turns righthanded around the x axis and the
90 inner one turns righthanded around z.
91
92 detectorAxis : list or tuple
93 detector circles e.g. ['x-'] would mean a detector arm with a
94 single motor turning lefthanded around the x-axis.
95
96 r_i : array-like
97 vector giving the direction of the primary beam (length is relevant
98 only if translations are involved)
99
100 kwargs : dict, optional
101 optional keyword arguments
102 wl : float or str, optional
103 wavelength of the x-rays in Angstroem
104 en : float or str, optional
105 energy of the x-rays in electronvolt
106 UB : array-like, optional
107 matrix for conversion from (hkl) coordinates to Q of sample used to
108 determine not Q but (hkl) (default: identity matrix)
109 """
110
111 utilities.check_kwargs(kwargs, self._valid_init_kwargs,
112 self.__class__.__name__)
113
114 # initialize some needed variables
115 self._kappa_dir = numpy.array((numpy.nan, numpy.nan, numpy.nan))
116
117 # set/check sample and detector axis geometry
118 self._set_sampleAxis(sampleAxis)
119 self._set_detectorAxis(detectorAxis)
120
121 # r_i: primary beam direction
122 if isinstance(r_i, (list, tuple, numpy.ndarray)):
123 self.r_i = numpy.array(r_i, dtype=numpy.double)
124 if self.r_i.size != 3:
125 print("XU.QConversion: warning invalid primary beam "
126 "direction given -> using [0, 1, 0]")
127 self.r_i = numpy.array([0, 1, 0], dtype=numpy.double)
128 else:
129 raise TypeError("QConversion: invalid type of primary beam "
130 "direction r_i, must be tuple, list or "
131 "numpy.ndarray")
132
133 # kwargs
134 self._set_wavelength(kwargs.get("wl", config.WAVELENGTH))
135 if "en" in kwargs:
136 self._set_energy(kwargs["en"])
137
138 self._set_UB(kwargs.get('UB', numpy.identity(3)))
139
140 self._linear_init = False
141 self._area_init = False
142 self._area_detrotaxis_set = False
143
144 def _set_energy(self, energy):
145 self._en = utilities.energy(energy)
146 self._wl = utilities.en2lam(self._en)
147
148 def _set_wavelength(self, wl):
149 self._wl = utilities.wavelength(wl)
150 self._en = utilities.lam2en(self._wl)
151
152 def _get_energy(self):
153 return self._en
154
155 def _get_wavelength(self):
156 return self._wl
157
158 def _set_sampleAxis(self, sampleAxis):
159 """
160 property handler for _sampleAxis
161 checks if a syntactically correct list of sample circles is given
162
163 Parameters
164 ----------
165 sampleAxis : list or tuple
166 sample circles, e.g. ['x+', 'z+']
167 """
168
169 if isinstance(sampleAxis, (str, list, tuple)):
170 if isinstance(sampleAxis, str):
171 sAxis = list([sampleAxis])
172 else:
173 sAxis = list(sampleAxis)
174 for circ in sAxis:
175 if not isinstance(circ, str) or len(circ) != 2:
176 raise InputError("QConversion: incorrect sample circle "
177 "type or syntax (%s)" % repr(circ))
178 if not circleSyntaxSample.search(circ):
179 raise InputError("QConversion: incorrect sample circle "
180 "syntax (%s)" % circ)
181 if circ[0] == 'k': # determine kappa rotation axis
182 # determine reference direction
183 if config.KAPPA_PLANE[0] == 'x':
184 self._kappa_dir = numpy.array((1., 0, 0))
185 # turn reference direction
186 if config.KAPPA_PLANE[1] == 'y':
187 self._kappa_dir = math.ZRotation(
188 config.KAPPA_ANGLE)(self._kappa_dir)
189 elif config.KAPPA_PLANE[1] == 'z':
190 self._kappa_dir = math.YRotation(
191 -1 * config.KAPPA_ANGLE)(self._kappa_dir)
192 else:
193 raise TypeError("Qconverision init: invalid "
194 "kappa_plane in config!")
195 elif config.KAPPA_PLANE[0] == 'y':
196 self._kappa_dir = numpy.array((0, 1., 0))
197 # turn reference direction
198 if config.KAPPA_PLANE[1] == 'z':
199 self._kappa_dir = math.XRotation(
200 config.KAPPA_ANGLE)(self._kappa_dir)
201 elif config.KAPPA_PLANE[1] == 'x':
202 self._kappa_dir = math.ZRotation(
203 -1 * config.KAPPA_ANGLE)(self._kappa_dir)
204 else:
205 raise TypeError("Qconverision init: invalid "
206 "kappa_plane in config!")
207 elif config.KAPPA_PLANE[0] == 'z':
208 self._kappa_dir = numpy.array((0, 0, 1.))
209 # turn reference direction
210 if config.KAPPA_PLANE[1] == 'x':
211 self._kappa_dir = math.YRotation(
212 config.KAPPA_ANGLE)(self._kappa_dir)
213 elif config.KAPPA_PLANE[1] == 'y':
214 self._kappa_dir = math.XRotation(
215 -1 * config.KAPPA_ANGLE)(self._kappa_dir)
216 else:
217 raise TypeError("Qconverision init: invalid "
218 "kappa_plane in config!")
219 else:
220 raise TypeError("Qconverision init: invalid "
221 "kappa_plane in config!")
222
223 # rotation sense
224 if circ[1] == '-':
225 self._kappa_dir *= -1
226
227 if config.VERBOSITY >= config.DEBUG:
228 print("XU.QConversion: kappa_dir: (%5.3f %5.3f %5.3f)"
229 % tuple(self._kappa_dir))
230
231 else:
232 raise TypeError("Qconversion error: invalid type for sampleAxis, "
233 "must be str, list, or tuple")
234 self._sampleAxis = sAxis
235 self._sampleAxis_str = ''
236 for circ in self._sampleAxis:
237 self._sampleAxis_str += circ
238
239 def _get_sampleAxis(self):
240 """
241 property handler for _sampleAxis
242
243 Returns
244 -------
245 list
246 sample axes following the syntax /[xyzk][+-]/
247 """
248 return self._sampleAxis
249
250 def _set_detectorAxis(self, detectorAxis, detrot=False):
251 """
252 property handler for _detectorAxis_
253 checks if a syntactically correct list of detector circles is given
254
255 Parameters
256 ----------
257 detectorAxis : list or tuple
258 detector circles, e.g. ['x+']
259 detrot : bool, optional
260 flag to tell that the detector rotation is going to be added (used
261 internally to avoid double adding of detector rotation axis)
262 """
263 has_translations = False
264 if isinstance(detectorAxis, (str, list, tuple)):
265 if isinstance(detectorAxis, str):
266 dAxis = list([detectorAxis])
267 else:
268 dAxis = list(detectorAxis)
269 for circ in dAxis:
270 if not isinstance(circ, str) or len(circ) != 2:
271 raise InputError("QConversion: incorrect detector circle "
272 "type or syntax (%s)" % repr(circ))
273 if not circleSyntaxDetector.search(circ):
274 raise InputError("QConversion: incorrect detector circle "
275 "syntax (%s)" % circ)
276 if circ[0] == 't':
277 has_translations = True
278 else:
279 raise TypeError("Qconversion error: invalid type for "
280 "detectorAxis, must be str, list or tuple")
281 self._detectorAxis = dAxis
282 self._detectorAxis_str = ''
283 self._has_translations = has_translations
284 for circ in self._detectorAxis:
285 self._detectorAxis_str += circ
286 if detrot:
287 self._area_detrotaxis_set = True
288 else:
289 self._area_init = False
290
291 def _get_detectorAxis(self):
292 """
293 property handler for _detectorAxis
294
295 Returns
296 -------
297 list of detector axis following the syntax /[xyz][+-]/
298 """
299 return self._detectorAxis
300
301 def _get_UB(self):
302 return self._UB
303
304 def _set_UB(self, UB):
305 """
306 set the orientation matrix used in the Qconversion
307 needs to be (3, 3) matrix
308 """
309 tmp = numpy.array(UB)
310 if tmp.shape != (3, 3) and tmp.size != 9:
311 raise InputError("QConversion: incorrect shape of UB matrix "
312 "(shape: %s)" % str(tmp.shape))
313 else:
314 self._UB = tmp.reshape((3, 3))
315
316 energy = property(_get_energy, _set_energy)
317 wavelength = property(_get_wavelength, _set_wavelength)
318 sampleAxis = property(_get_sampleAxis, _set_sampleAxis)
319 detectorAxis = property(_get_detectorAxis, _set_detectorAxis)
320 UB = property(_get_UB, _set_UB)
321
322 def __str__(self):
323 pstr = 'QConversion geometry \n'
324 pstr += '---------------------------\n'
325 pstr += 'sample geometry(%d): ' % len(self._sampleAxis) + \
326 self._sampleAxis_str + '\n'
327 if self._sampleAxis_str.find('k') != -1:
328 pstr += ('kappa rotation axis (%5.3f %5.3f %5.3f)\n'
329 % tuple(self._kappa_dir))
330 pstr += 'detector geometry(%d): ' % len(self._detectorAxis) + \
331 self._detectorAxis_str + '\n'
332 pstr += ('primary beam direction: (%5.2f %5.2f %5.2f) \n'
333 % (self.r_i[0], self.r_i[1], self.r_i[2]))
334
335 if self._linear_init:
336 pstr += '\n linear detector initialized:\n'
337 pstr += 'linear detector mount direction: ' + \
338 self._linear_detdir + '\n'
339 pstr += ('number of channels/center channel: %d/%d\n'
340 % (self._linear_Nch, self._linear_cch))
341 pstr += ('distance to center of rotation/pixel width: '
342 '%10.4g/%10.4g\n'
343 % (self._linear_distance, self._linear_pixwidth))
344 chpdeg = 2 * self._linear_distance / \
345 self._linear_pixwidth * numpy.tan(numpy.radians(0.5))
346 pstr += 'corresponds to channel per degree: %8.2f\n' % (chpdeg)
347 if self._area_init:
348 pstr += '\n area detector initialized:\n'
349 pstr += 'area detector mount directions: %s/%s\n' % (
350 self._area_detdir1, self._area_detdir2)
351 pstr += ('number of channels/center channels: (%d,%d) / (%d,%d)\n'
352 % (self._area_Nch1, self._area_Nch2,
353 self._area_cch1, self._area_cch2))
354 pstr += ('distance to center of rotation/pixel width: '
355 '%10.4g/ (%10.4g,%10.4g) \n'
356 % (self._area_distance, self._area_pwidth1,
357 self._area_pwidth2))
358 chpdeg1 = 2 * self._area_distance / \
359 self._area_pwidth1 * numpy.tan(numpy.radians(0.5))
360 chpdeg2 = 2 * self._area_distance / \
361 self._area_pwidth2 * numpy.tan(numpy.radians(0.5))
362 pstr += 'corresponds to channel per degree: (%8.2f,%8.2f)\n' % (
363 chpdeg1, chpdeg2)
364
365 return pstr
366
367 def _checkInput(self, *args):
368 """
369 helper function to check that the arguments given to the QConversion
370 routines have the correct shape. It determines the number of points in
371 the input from the longest array/list given and checks that only inputs
372 combatible with this length are given
373
374 Parameters
375 ----------
376 args : list
377 arguments from the QConversion routine (sample and detector angles)
378
379 Returns
380 -------
381 Npoints : int
382 integer to tell the number of points given
383 """
384 np = 1
385 for a in args:
386 # optain size of input
387 if isinstance(a, numpy.ndarray):
388 anp = a.size
389 elif isinstance(a, (list, tuple)):
390 anp = len(a)
391 elif isinstance(a, numbers.Number):
392 anp = 1
393 else:
394 raise TypeError('QConversion: Input argument #%d has an '
395 'invalid type.' % args.index(a))
396 # check if the input field is valid
397 if anp > 1 and np == 1:
398 np = anp
399 elif anp > 1 and np != anp:
400 raise InputError('QConversion: Several input-arrays arguments '
401 'with different shape are an invalid input!')
402
403 return np
404
405 def _reshapeInput(self, npoints, delta, circles, *args, **kwargs):
406 """
407 helper function to reshape the input of arguments to
408 (len(args), npoints) The input arguments must be either scalars or are
409 of length npoints.
410
411 Parameters
412 ----------
413 npoints : int
414 length of the input arrays
415 delta : list or array-like
416 value to substract from the input arguments as array with len(args)
417 circles : list
418 list of circle description to decide if degree/radians conversion
419 is needed
420 args : list
421 input arrays and scalars
422 kwargs : dict, optional
423 optional keyword argument to tell if values of rotation axis should
424 be converted to radiants (name= 'deg', default=True)
425
426 Returns
427 -------
428 inarr : ndarray
429 array of shape (len(args), npoints) with the input arguments
430 retshape : tuple
431 shape of return values
432 """
433
434 inarr = numpy.empty((len(args), npoints), dtype=numpy.double)
435 retshape = (npoints,) # default value
436 deg2rad = kwargs.get('deg', True)
437
438 for i in range(len(args)):
439 arg = args[i]
440 if not isinstance(
441 arg,
442 (numbers.Number,
443 list,
444 tuple,
445 numpy.ndarray)):
446 raise TypeError("QConversion: invalid type for one of the "
447 "sample coordinates, must be scalar, list or "
448 "array")
449 elif isinstance(arg, numbers.Number):
450 arg = numpy.ones(npoints, dtype=numpy.double) * arg
451 elif isinstance(arg, (list, tuple)):
452 arg = numpy.array(arg, dtype=numpy.double)
453 else: # determine return value shape
454 retshape = arg.shape
455 arg = arg - delta[i]
456 if deg2rad and circleSyntaxSample.search(circles[i]):
457 inarr[i, :] = numpy.radians(numpy.ravel(arg))
458 else:
459 inarr[i, :] = numpy.ravel(arg)
460
461 return inarr, retshape
462
463 def _parse_common_kwargs(self, **kwargs):
464 """
465 parse common keyword arguments to QConversion calls
466
467 Parameters
468 ----------
469 delta : list or array-like, optional
470 delta angles to correct the given ones for misalignment.
471 delta must be an numpy array or list of len(*args). used angles are
472 than ``*args - delta``
473 UB : array-like, optional
474 matrix for conversion from (hkl) coordinates to Q of sample used to
475 determine not Q but (hkl) (default: self.UB)
476 wl : float or str, optional
477 x-ray wavelength in angstroem (default: self._wl)
478 en : float, optional
479 x-ray energy in eV (default is converted self._wl). both wavelength
480 and energy can also be an array which enables the QConversion for
481 energy scans. Note that the `en` keyword overrules the `wl`
482 keyword!
483 deg : bool, optional
484 flag to tell if angles are passed as degree (default: True)
485 sampledis : tuple or list or array-like
486 sample displacement vector in relative units of the detector
487 distance (default: (0, 0, 0))
488 """
489 flags = 0
490 if self._has_translations:
491 flags = utilities.set_bit(flags, 0)
492
493 Ns = len(self.sampleAxis)
494 Nd = len(self.detectorAxis)
495 if self._area_detrotaxis_set:
496 Nd -= 1 # do not consider detector rotation for point detector
497 Ncirc = Ns + Nd
498
499 # kwargs
500 wl = utilities.wavelength(kwargs.get('wl', self._wl))
501 if 'en' in kwargs:
502 wl = utilities.lam2en(utilities.energy(kwargs['en']))
503
504 deg = kwargs.get('deg', True)
505
506 delta = numpy.asarray(kwargs.get('delta', numpy.zeros(Ncirc)),
507 dtype=numpy.double)
508 if delta.size != Ncirc:
509 raise InputError("QConversion: keyword argument delta does "
510 "not have an appropriate shape")
511
512 UB = numpy.asarray(kwargs.get('UB', self.UB))
513
514 sd = numpy.asarray(kwargs.get('sampledis', [0, 0, 0]))
515 if 'sampledis' in kwargs:
516 flags = utilities.set_bit(flags, 2)
517
518 return Ns, Nd, Ncirc, wl, deg, delta, UB, sd, flags
519
520 def __call__(self, *args, **kwargs):
521 """
522 wrapper function for point(...)
523 """
524 return self.point(*args, **kwargs)
525
526 def point(self, *args, **kwargs):
527 """
528 angular to momentum space conversion for a point detector
529 located in direction of self.r_i when detector angles are zero
530
531 Parameters
532 ----------
533 args : ndarray, list or Scalars
534 sample and detector angles; in total `len(self.sampleAxis) +
535 len(detectorAxis)` must be given, always starting with the outer
536 most circle. all arguments must have the same shape or length but
537 can be mixed with Scalars (i.e. if an angle is always the same it
538 can be given only once instead of an array)
539
540 - sAngles :
541 sample circle angles, number of arguments must correspond to
542 len(self.sampleAxis)
543 - dAngles :
544 detector circle angles, number of arguments must correspond to
545 len(self.detectorAxis)
546
547 kwargs : dict, optional
548 optional keyword arguments
549 delta : list or array-like, optional
550 delta angles to correct the given ones for misalignment.
551 delta must be an numpy array or list of ``len(*args)``. used angles
552 are then ``*args - delta``
553 UB : array-like, optional
554 matrix for conversion from (hkl) coordinates to Q of sample used to
555 determine not Q but (hkl) (default: self.UB)
556 wl : float or str, optional
557 x-ray wavelength in angstroem (default: self._wl)
558 en : float, optional
559 x-ray energy in eV (default is converted self._wl). both wavelength
560 and energy can also be an array which enables the QConversion for
561 energy scans. Note that the `en` keyword overrules the `wl`
562 keyword!
563 deg : bool, optional
564 flag to tell if angles are passed as degree (default: True)
565 sampledis : tuple or list or array-like
566 sample displacement vector in relative units of the detector
567 distance (default: (0, 0, 0))
568
569 Returns
570 -------
571 ndarray
572 reciprocal space positions as numpy.ndarray with shape ``(N , 3)``
573 where `N` corresponds to the number of points given in the input
574 """
575
576 utilities.check_kwargs(kwargs, self._valid_call_kwargs, 'Ang2Q/point')
577
578 Ns, Nd, Ncirc, wl, deg, delta, UB, sd, flags = \
579 self._parse_common_kwargs(**kwargs)
580
581 # prepare angular arrays from *args
582 # need one sample angle and one detector angle array
583 if len(args) != Ncirc:
584 raise InputError("QConversion: wrong amount (%d) of arguments "
585 "given, number of arguments should be %d"
586 % (len(args), Ncirc))
587
588 # determine the number of points
589 a = args + (wl,)
590 Npoints = self._checkInput(*a)
591
592 # reshape/recast input arguments for sample and detector angles
593 sAngles, retshape = self._reshapeInput(Npoints, delta[:Ns],
594 self.sampleAxis, *args[:Ns],
595 deg=deg)
596 dAngles = self._reshapeInput(Npoints, delta[Ns:],
597 self.detectorAxis, *args[Ns:],
598 deg=deg)[0]
599 wl = numpy.ravel(self._reshapeInput(Npoints, (0, ), 'a',
600 wl, deg=False)[0])
601
602 sAngles = sAngles.transpose()
603 dAngles = dAngles.transpose()
604
605 sAxis = self._sampleAxis_str
606 dAxis = self._detectorAxis_str
607
608 if self._area_detrotaxis_set:
609 # do not consider detector rotation for point detector
610 dAxis = self._detectorAxis_str[:-2]
611 else:
612 dAxis = self._detectorAxis_str
613
614 if config.VERBOSITY >= config.DEBUG:
615 print("XU.QConversion: Ns, Nd: %d %d" % (Ns, Nd))
616 print("XU.QConversion: sAngles / dAngles %s / %s"
617 % (str(sAngles), str(dAngles)))
618
619 qpos = cxrayutilities.ang2q_conversion(
620 sAngles, dAngles, self.r_i, sAxis, dAxis,
621 self._kappa_dir, UB, sd, wl, config.NTHREADS, flags)
622
623 if Npoints == 1:
624 return (qpos[0, 0], qpos[0, 1], qpos[0, 2])
625 else:
626 return numpy.reshape(qpos[:, 0], retshape), \
627 numpy.reshape(qpos[:, 1], retshape), \
628 numpy.reshape(qpos[:, 2], retshape)
629
630 def init_linear(self, detectorDir, cch, Nchannel, distance=None,
631 pixelwidth=None, chpdeg=None, tilt=0, **kwargs):
632 """
633 initialization routine for linear detectors
634 detector direction as well as distance and pixel size or
635 channels per degree must be given.
636
637 Parameters
638 ----------
639 detectorDir : str
640 direction of the detector (along the pixel array); e.g. 'z+'
641 cch : float
642 center channel, in direction of self.r_i at zero detectorAngles
643 Nchannel : int
644 total number of detector channels
645 distance : float, optional
646 distance of center channel from center of rotation
647 pixelwidth : float, optional
648 width of one pixel (same unit as distance)
649 chpdeg : float, optional
650 channels per degree (only absolute value is relevant) sign
651 determined through detectorDir
652 tilt : float, optional
653 tilt of the detector axis from the detectorDir (in degree)
654 kwargs: dict, optional
655 optional keyword arguments
656 Nav : int, optional
657 number of channels to average to reduce data size (default: 1)
658 roi : tuple or list
659 region of interest for the detector pixels; e.g. [100, 900]
660
661 Note:
662 Either distance and pixelwidth or chpdeg must be given !!
663
664 Note:
665 the channel numbers run from 0 .. Nchannel-1
666
667 """
668
669 utilities.check_kwargs(kwargs, self._valid_linear_kwargs,
670 'init_linear')
671
672 # detectorDir
673 if not isinstance(detectorDir, str) or len(detectorDir) != 2:
674 raise InputError("QConversion: incorrect detector direction type "
675 "or syntax (%s)" % repr(detectorDir))
676 if not directionSyntax.search(detectorDir):
677 raise InputError("QConversion: incorrect detector direction "
678 "syntax (%s)" % detectorDir)
679 self._linear_detdir = detectorDir
680
681 self._linear_Nch = int(Nchannel)
682 self._linear_cch = float(cch)
683 self._linear_tilt = numpy.radians(tilt)
684
685 if distance is not None and pixelwidth is not None:
686 self._linear_distance = float(distance)
687 self._linear_pixwidth = float(pixelwidth)
688 elif chpdeg is not None:
689 self._linear_distance = 1.0
690 self._linear_pixwidth = 2 * self._linear_distance / \
691 numpy.abs(float(chpdeg)) * numpy.tan(numpy.radians(0.5))
692 else:
693 # not all needed values were given
694 raise InputError("QConversion: not all mandatory arguments were "
695 "given -> read API doc, need distance and "
696 "pixelwidth or chpdeg")
697
698 # kwargs
699 self._linear_roi = kwargs.get('roi', [0, self._linear_Nch])
700 self._linear_nav = kwargs.get('Nav', 1)
701
702 # rescale r_i
703 self.r_i = math.VecUnit(self.r_i) * self._linear_distance
704
705 self._linear_init = True
706
707 def _get_detparam_linear(self, oroi, nav):
708 """
709 initialize linear detector geometry for C subroutines. This function
710 considers the Nav and roi options.
711 """
712 cch = self._linear_cch / float(nav)
713 pwidth = self._linear_pixwidth * nav
714 roi = numpy.array(oroi)
715 roi[0] = numpy.floor(oroi[0] / float(nav))
716 roi[1] = numpy.ceil((oroi[1] - oroi[0]) / float(nav)) + roi[0]
717 roi = roi.astype(numpy.int32)
718 return cch, pwidth, roi
719
720 def linear(self, *args, **kwargs):
721 """
722 angular to momentum space conversion for a linear detector
723 the cch of the detector must be in direction of self.r_i when
724 detector angles are zero
725
726 the detector geometry must be initialized by the init_linear(...)
727 routine
728
729 Parameters
730 ----------
731 args : ndarray, list or Scalars
732 sample and detector angles; in total `len(self.sampleAxis) +
733 len(detectorAxis)` must be given, always starting with the outer
734 most circle. all arguments must have the same shape or length but
735 can be mixed with Scalars (i.e. if an angle is always the same it
736 can be given only once instead of an array)
737
738 - sAngles :
739 sample circle angles, number of arguments must correspond to
740 len(self.sampleAxis)
741 - dAngles :
742 detector circle angles, number of arguments must correspond to
743 len(self.detectorAxis)
744
745 kwargs : dict, optional
746 optional keyword arguments
747 delta : list or array-like, optional
748 delta angles to correct the given ones for misalignment.
749 delta must be an numpy array or list of ``len(*args)``. used angles
750 are then ``*args - delta``
751 UB : array-like, optional
752 matrix for conversion from (hkl) coordinates to Q of sample used to
753 determine not Q but (hkl) (default: self.UB)
754 Nav : int, optional
755 number of channels to average to reduce data size (default:
756 self._linear_nav)
757 roi : list or tuple, optional
758 region of interest for the detector pixels; e.g. [100, 900]
759 (default: self._linear_roi)
760 wl : float or str, optional
761 x-ray wavelength in angstroem (default: self._wl)
762 en : float, optional
763 x-ray energy in eV (default is converted self._wl). both wavelength
764 and energy can also be an array which enables the QConversion for
765 energy scans. Note that the `en` keyword overrules the `wl`
766 keyword!
767 deg : bool, optional
768 flag to tell if angles are passed as degree (default: True)
769 sampledis : tuple or list or array-like
770 sample displacement vector in relative units of the detector
771 distance (default: (0, 0, 0))
772
773 Returns
774 -------
775 reciprocal space position of all detector pixels in a numpy.ndarray of
776 shape ( (*)*(self._linear_roi[1]-self._linear_roi[0]+1) , 3 )
777 """
778
779 if not self._linear_init:
780 raise Exception("QConversion: linear detector not initialized -> "
781 "call Ang2Q.init_linear(...)")
782
783 valid_kwargs = copy.copy(self._valid_call_kwargs)
784 valid_kwargs.update(self._valid_linear_kwargs)
785 utilities.check_kwargs(kwargs, valid_kwargs, 'Ang2Q/linear')
786
787 Ns, Nd, Ncirc, wl, deg, delta, UB, sd, flags = \
788 self._parse_common_kwargs(**kwargs)
789
790 # extra keyword arguments
791 nav = kwargs.get('Nav', self._linear_nav)
792 oroi = kwargs.get('roi', self._linear_roi)
793
794 # prepare angular arrays from *args
795 # need one sample angle and one detector angle array
796 if len(args) != Ncirc:
797 raise InputError("QConversion: wrong amount (%d) of arguments "
798 "given, number of arguments should be %d"
799 % (len(args), Ncirc))
800
801 # determine the number of points
802 a = args + (wl,)
803 Npoints = self._checkInput(*a)
804
805 # reshape/recast input arguments for sample and detector angles
806 sAngles, retshape = self._reshapeInput(Npoints, delta[:Ns],
807 self.sampleAxis, *args[:Ns],
808 deg=deg)
809 dAngles = self._reshapeInput(Npoints, delta[Ns:],
810 self.detectorAxis, *args[Ns:],
811 deg=deg)[0]
812 wl = numpy.ravel(self._reshapeInput(Npoints, (0, ), 'a',
813 wl, deg=False)[0])
814
815 sAngles = sAngles.transpose()
816 dAngles = dAngles.transpose()
817
818 cch, pwidth, roi = self._get_detparam_linear(oroi, nav)
819 sAxis = self._sampleAxis_str
820 dAxis = self._detectorAxis_str
821
822 qpos = cxrayutilities.ang2q_conversion_linear(
823 sAngles, dAngles, self.r_i, sAxis, dAxis, self._kappa_dir,
824 cch, pwidth, roi, self._linear_detdir, self._linear_tilt,
825 UB, sd, wl, config.NTHREADS, flags)
826
827 # reshape output
828 if Npoints == 1:
829 qpos.shape = (Npoints * (roi[1] - roi[0]), 3)
830 return qpos[:, 0], qpos[:, 1], qpos[:, 2]
831 else:
832 qpos.shape = (Npoints, (roi[1] - roi[0]), 3)
833 return qpos[:, :, 0], qpos[:, :, 1], qpos[:, :, 2]
834
835 def init_area(self, detectorDir1, detectorDir2, cch1, cch2, Nch1, Nch2,
836 distance=None, pwidth1=None, pwidth2=None, chpdeg1=None,
837 chpdeg2=None, detrot=0, tiltazimuth=0, tilt=0, **kwargs):
838 """
839 initialization routine for area detectors
840 detector direction as well as distance and pixel size or
841 channels per degree must be given. Two separate pixel sizes and
842 channels per degree for the two orthogonal directions can be given
843
844 Parameters
845 ----------
846 detectorDir1 : str
847 direction of the detector (along the pixel direction 1); e.g. 'z+'
848 means higher pixel numbers at larger z positions
849 detectorDir2 : str
850 direction of the detector (along the pixel direction 2); e.g. 'x+'
851 cch1, cch2 : float
852 center pixel, in direction of self.r_i at zero detectorAngles
853 Nch1, Nch2 : int
854 number of detector pixels along direction 1, 2
855 distance : float, optional
856 distance of center pixel from center of rotation
857 pwidth1, pwidth2 : float, optional
858 width of one pixel (same unit as distance)
859 chpdeg1, chpdeg2 : float, optional
860 channels per degree (only absolute value is relevant) sign
861 determined through `detectorDir1, detectorDir2`
862 detrot : float, optional
863 angle of the detector rotation around primary beam direction (used
864 to correct misalignments)
865 tiltazimuth : float, optional
866 direction of the tilt vector in the detector plane (in degree)
867 tilt : float, optional
868 tilt of the detector plane around an axis normal to the direction
869 given by the tiltazimuth
870
871 kwargs : dict, optional
872 optional keyword arguments
873 Nav : tuple or list, optional
874 number of channels to average to reduce data size (default: [1, 1])
875 roi : tuple or list, optional
876 region of interest for the detector pixels; e.g.
877 [100, 900, 200, 800]
878
879 Note:
880 Either distance and pwidth1, pwidth2 or chpdeg1, chpdeg2 must
881 be given !!
882
883 Note:
884 the channel numbers run from 0 .. NchX-1
885 """
886
887 utilities.check_kwargs(kwargs, self._valid_linear_kwargs, 'init_area')
888
889 # detectorDir
890 if not isinstance(detectorDir1, str) or len(detectorDir1) != 2:
891 raise InputError("QConversion: incorrect detector direction1 type "
892 "or syntax (%s)" % repr(detectorDir1))
893 if not directionSyntax.search(detectorDir1):
894 raise InputError("QConversion: incorrect detector direction1 "
895 "syntax (%s)" % detectorDir1)
896 self._area_detdir1 = detectorDir1
897 if not isinstance(detectorDir2, str) or len(detectorDir2) != 2:
898 raise InputError("QConversion: incorrect detector direction2 type "
899 "or syntax (%s)" % repr(detectorDir2))
900 if not directionSyntax.search(detectorDir2):
901 raise InputError("QConversion: incorrect detector direction2 "
902 "syntax (%s)" % detectorDir2)
903 self._area_detdir2 = detectorDir2
904
905 # other none keyword arguments
906 self._area_Nch1 = int(Nch1)
907 self._area_Nch2 = int(Nch2)
908 self._area_cch1 = int(cch1)
909 self._area_cch2 = int(cch2)
910
911 # if detector rotation is present add new motor to consider it in
912 # conversion
913 self._area_detrot = numpy.radians(detrot)
914 if self._area_detrot != 0.:
915 if self._area_detrotaxis_set:
916 self._set_detectorAxis(
917 self._get_detectorAxis()[:-1] + [math.getSyntax(self.r_i)],
918 detrot=True)
919 else:
920 self._set_detectorAxis(
921 self._get_detectorAxis() + [math.getSyntax(self.r_i)],
922 detrot=True)
923
924 self._area_tiltazimuth = numpy.radians(tiltazimuth)
925 self._area_tilt = numpy.radians(tilt)
926
927 # mandatory keyword arguments
928 if (distance is not None and pwidth1 is not None and
929 pwidth2 is not None):
930 self._area_distance = float(distance)
931 self._area_pwidth1 = float(pwidth1)
932 self._area_pwidth2 = float(pwidth2)
933 elif chpdeg1 is not None and chpdeg2 is not None:
934 self._area_distance = 1.0
935 self._area_pwidth1 = 2 * self._area_distance / \
936 numpy.abs(float(chpdeg1)) * numpy.tan(numpy.radians(0.5))
937 self._area_pwidth2 = 2 * self._area_distance / \
938 numpy.abs(float(chpdeg2)) * numpy.tan(numpy.radians(0.5))
939 else:
940 # not all needed values were given
941 raise InputError("Qconversion error: not all mandatory arguments "
942 "were given -> read API doc")
943
944 # kwargs
945 self._area_roi = kwargs.get('roi', [0, self._area_Nch1,
946 0, self._area_Nch2])
947 self._area_nav = kwargs.get('Nav', [1, 1])
948
949 # rescale r_i
950 self.r_i = math.VecUnit(self.r_i) * self._area_distance
951
952 self._area_init = True
953
954 def _get_detparam_area(self, oroi, nav):
955 """
956 initialize CCD geomtry for C subroutines. This function considers the
957 Nav and roi options.
958 """
959 cch1 = self._area_cch1 / float(nav[0])
960 cch2 = self._area_cch2 / float(nav[1])
961 pwidth1 = self._area_pwidth1 * nav[0]
962 pwidth2 = self._area_pwidth2 * nav[1]
963 roi = numpy.array(oroi)
964 roi[0] = numpy.floor(oroi[0] / float(nav[0]))
965 roi[1] = numpy.ceil((oroi[1] - oroi[0]) / float(nav[0])) + roi[0]
966 roi[2] = numpy.floor(oroi[2] / float(nav[1]))
967 roi[3] = numpy.ceil((oroi[3] - oroi[2]) / float(nav[1])) + roi[2]
968 roi = roi.astype(numpy.int32)
969 return cch1, cch2, pwidth1, pwidth2, roi
970
971 def area(self, *args, **kwargs):
972 """
973 angular to momentum space conversion for a area detector
974 the center pixel defined by the init_area routine must be
975 in direction of self.r_i when detector angles are zero
976
977 the detector geometry must be initialized by the init_area(...) routine
978
979 Parameters
980 ----------
981 args : ndarray, list or Scalars
982 sample and detector angles; in total `len(self.sampleAxis) +
983 len(detectorAxis)` must be given, always starting with the outer
984 most circle. all arguments must have the same shape or length but
985 can be mixed with Scalars (i.e. if an angle is always the same it
986 can be given only once instead of an array)
987
988 - sAngles :
989 sample circle angles, number of arguments must correspond to
990 len(self.sampleAxis)
991 - dAngles :
992 detector circle angles, number of arguments must correspond to
993 len(self.detectorAxis)
994
995 kwargs : dict, optional
996 optional keyword arguments
997 delta : list or array-like, optional
998 delta angles to correct the given ones for misalignment.
999 delta must be an numpy array or list of ``len(*args)``. used angles
1000 are then ``*args - delta``
1001 UB : array-like, optional
1002 matrix for conversion from (hkl) coordinates to Q of sample used to
1003 determine not Q but (hkl) (default: self.UB)
1004 Nav : tuple or list, optional
1005 number of channels to average to reduce data size e.g. [2, 2]
1006 (default: self._area_nav)
1007 roi : list or tuple, optional
1008 region of interest for the detector pixels; e.g.
1009 [100, 900, 200, 800] (default: self._area_roi)
1010 wl : float or str, optional
1011 x-ray wavelength in angstroem (default: self._wl)
1012 en : float, optional
1013 x-ray energy in eV (default is converted self._wl). both wavelength
1014 and energy can also be an array which enables the QConversion for
1015 energy scans. Note that the `en` keyword overrules the `wl`
1016 keyword!
1017 deg : bool, optional
1018 flag to tell if angles are passed as degree (default: True)
1019 sampledis : tuple or list or array-like
1020 sample displacement vector in relative units of the detector
1021 distance (default: (0, 0, 0))
1022
1023
1024 Returns
1025 -------
1026 reciprocal space position of all detector pixels in a numpy.ndarray of
1027 shape ((*)*(self._area_roi[1] - self._area_roi[0]+1) *
1028 (self._area_roi[3] - self._area_roi[2] + 1) , 3) were detectorDir1 is
1029 the fastest varing
1030 """
1031
1032 if not self._area_init:
1033 raise Exception("QConversion: area detector not initialized -> "
1034 "call Ang2Q.init_area(...)")
1035
1036 valid_kwargs = copy.copy(self._valid_call_kwargs)
1037 valid_kwargs.update(self._valid_linear_kwargs)
1038 utilities.check_kwargs(kwargs, valid_kwargs, 'Ang2Q/area')
1039
1040 Ns, Nd, Ncirc, wl, deg, delta, UB, sd, flags = \
1041 self._parse_common_kwargs(**kwargs)
1042
1043 # extra keyword arguments
1044 nav = kwargs.get('Nav', self._area_nav)
1045 oroi = kwargs.get('roi', self._area_roi)
1046
1047 # prepare angular arrays from *args
1048 # need one sample angle and one detector angle array
1049 if len(args) != Ncirc:
1050 raise InputError("QConversion: wrong amount (%d) of arguments "
1051 "given, number of arguments should be %d"
1052 % (len(args), Ncirc))
1053
1054 # determine the number of points
1055 a = args + (wl,)
1056 Npoints = self._checkInput(*a)
1057
1058 # reshape/recast input arguments for sample and detector angles
1059 sAngles, retshape = self._reshapeInput(Npoints, delta[:Ns],
1060 self.sampleAxis, *args[:Ns],
1061 deg=deg)
1062 wl = numpy.ravel(self._reshapeInput(Npoints, (0, ), 'a',
1063 wl, deg=False)[0])
1064
1065 if self._area_detrotaxis_set:
1066 Nd = Nd + 1
1067 if deg:
1068 a = args[Ns:] + (numpy.degrees(self._area_detrot),)
1069 else:
1070 a = args[Ns:] + (self._area_detrot,)
1071 dAngles = self._reshapeInput(
1072 Npoints, numpy.append(delta[Ns:], 0),
1073 self.detectorAxis, *a, deg=deg)[0]
1074 else:
1075 dAngles = self._reshapeInput(Npoints, delta[Ns:],
1076 self.detectorAxis, *args[Ns:],
1077 deg=deg)[0]
1078
1079 sAngles = sAngles.transpose()
1080 dAngles = dAngles.transpose()
1081
1082 cch1, cch2, pwidth1, pwidth2, roi = self._get_detparam_area(oroi, nav)
1083
1084 if config.VERBOSITY >= config.DEBUG:
1085 print("QConversion.area: roi, number of points per frame: %s, %d"
1086 % (str(roi), (roi[1] - roi[0]) * (roi[3] - roi[2])))
1087 print("QConversion.area: cch1, cch2: %5.2f %5.2f" % (cch1, cch2))
1088
1089 sAxis = self._sampleAxis_str
1090 dAxis = self._detectorAxis_str
1091
1092 qpos = cxrayutilities.ang2q_conversion_area(
1093 sAngles, dAngles, self.r_i, sAxis, dAxis, self._kappa_dir,
1094 cch1, cch2, pwidth1, pwidth2, roi, self._area_detdir1,
1095 self._area_detdir2, self._area_tiltazimuth, self._area_tilt,
1096 UB, sd, wl, config.NTHREADS, flags)
1097
1098 # reshape output
1099 if Npoints == 1:
1100 qpos.shape = ((roi[1] - roi[0]), (roi[3] - roi[2]), 3)
1101 return qpos[:, :, 0], qpos[:, :, 1], qpos[:, :, 2]
1102 else:
1103 qpos.shape = (Npoints, (roi[1] - roi[0]), (roi[3] - roi[2]), 3)
1104 return qpos[:, :, :, 0], qpos[:, :, :, 1], qpos[:, :, :, 2]
1105
1106 def transformSample2Lab(self, vector, *args):
1107 """
1108 transforms a vector from the sample coordinate frame to the laboratory
1109 coordinate system by applying the sample rotations from inner to outer
1110 circle.
1111
1112 Parameters
1113 ----------
1114 vector : sequence, list or numpy array
1115 vector to transform
1116 args : list
1117 goniometer angles (sample angles or full goniometer angles can be
1118 given. If more angles than the sample circles are given they will
1119 be ignored)
1120
1121 Returns
1122 -------
1123 ndarray
1124 rotated vector as numpy.array
1125 """
1126 rotvec = vector
1127 for i in range(len(self.sampleAxis)-1, -1, -1):
1128 a = args[i]
1129 axis = self.sampleAxis[i]
1130 rota = math.getVector(axis)
1131 rotvec = math.rotarb(rotvec, rota, a)
1132 return rotvec
1133
1134 def getDetectorPos(self, *args, **kwargs):
1135 """
1136 obtains the detector position vector by applying the detector arm
1137 rotations.
1138
1139 Parameters
1140 ----------
1141 args : list
1142 detector angles. Only detector arm angles as described by the
1143 detectorAxis attribute must be given.
1144 kwargs : dict, optional
1145 optional keyword arguments
1146 dim : int, optional
1147 dimension of the detector for which the position should be
1148 determined
1149 roi : tuple or list, optional
1150 region of interest for the detector pixels; (default:
1151 self._area_roi/self._linear_roi)
1152 Nav : tuple or list, optional
1153 number of channels to average to reduce data size; (default:
1154 self._area_nav/self._linear_nav)
1155 deg : bool, optional
1156 flag to tell if angles are passed as degree (default: True)
1157
1158 Returns
1159 -------
1160 ndarray
1161 numpy array of length 3 with vector components of the detector
1162 direction. The length of the vector is k.
1163 """
1164
1165 valid_kwargs = copy.copy(self._valid_linear_kwargs)
1166 valid_kwargs['dim'] = 'dimensionality of the detector'
1167 valid_kwargs['deg'] = 'True if angles are in degrees'
1168 utilities.check_kwargs(kwargs, valid_kwargs, 'get_detector_pos')
1169
1170 dim = kwargs.get('dim', 0)
1171
1172 if dim == 1 and not self._linear_init:
1173 raise Exception("QConversion: linear detector not initialized -> "
1174 "call Ang2Q.init_linear(...)")
1175 elif dim == 2 and not self._area_init:
1176 raise Exception("QConversion: area detector not initialized -> "
1177 "call Ang2Q.init_area(...)")
1178
1179 Nd = len(self.detectorAxis)
1180 if self._area_detrotaxis_set:
1181 Nd = Nd - 1
1182
1183 # kwargs
1184 deg = kwargs.get('deg', True)
1185
1186 if 'roi' in kwargs:
1187 oroi = kwargs['roi']
1188 else:
1189 if dim == 1:
1190 oroi = self._linear_roi
1191 elif dim == 2:
1192 oroi = self._area_roi
1193
1194 if 'Nav' in kwargs:
1195 nav = kwargs['Nav']
1196 else:
1197 if dim == 1:
1198 nav = self._linear_nav
1199 elif dim == 2:
1200 nav = self._area_nav
1201
1202 # prepare angular arrays from *args
1203 # need one sample angle and one detector angle array
1204 if len(args) != Nd:
1205 raise InputError("QConversion: wrong amount (%d) of arguments "
1206 "given, number of arguments should be %d"
1207 % (len(args), Nd))
1208
1209 # determine the number of points and reshape input arguments
1210 Npoints = self._checkInput(*args)
1211
1212 if dim == 2 and self._area_detrotaxis_set:
1213 Nd = Nd + 1
1214 if deg:
1215 a = args + (numpy.degrees(self._area_detrot),)
1216 else:
1217 a = args + (self._area_detrot,)
1218 dAngles, retshape = self._reshapeInput(
1219 Npoints, numpy.append(numpy.zeros(Nd), 0),
1220 self.detectorAxis, *a, deg=deg)
1221 else:
1222 dAngles, retshape = self._reshapeInput(Npoints, numpy.zeros(Nd),
1223 self.detectorAxis, *args,
1224 deg=deg)
1225
1226 dAngles = dAngles.transpose()
1227
1228 if dim == 2:
1229 cch1, cch2, pwidth1, pwidth2, roi = self._get_detparam_area(oroi,
1230 nav)
1231 elif dim == 1:
1232 cch, pwidth, roi = self._get_detparam_linear(oroi, nav)
1233
1234 dAxis = self._detectorAxis_str
1235
1236 if dim == 2:
1237 cfunc = cxrayutilities.ang2q_detpos_area
1238 dpos = cfunc(dAngles, self.r_i, dAxis, cch1, cch2, pwidth1,
1239 pwidth2, roi, self._area_detdir1, self._area_detdir2,
1240 self._area_tiltazimuth, self._area_tilt,
1241 config.NTHREADS)
1242
1243 # reshape output
1244 if Npoints == 1:
1245 dpos.shape = ((roi[1] - roi[0]), (roi[3] - roi[2]), 3)
1246 return dpos[:, :, 0], dpos[:, :, 1], dpos[:, :, 2]
1247 else:
1248 dpos.shape = (Npoints, (roi[1] - roi[0]), (roi[3] - roi[2]), 3)
1249 return dpos[:, :, :, 0], dpos[:, :, :, 1], dpos[:, :, :, 2]
1250
1251 elif dim == 1:
1252 cfunc = cxrayutilities.ang2q_detpos_linear
1253 dpos = cfunc(dAngles, self.r_i, dAxis, cch, pwidth, roi,
1254 self._linear_detdir, self._linear_tilt,
1255 config.NTHREADS)
1256
1257 # reshape output
1258 if Npoints == 1:
1259 dpos.shape = (Npoints * (roi[1] - roi[0]), 3)
1260 return dpos[:, 0], dpos[:, 1], dpos[:, 2]
1261 else:
1262 dpos.shape = (Npoints, (roi[1] - roi[0]), 3)
1263 return dpos[:, :, 0], dpos[:, :, 1], dpos[:, :, 2]
1264
1265 else:
1266 cfunc = cxrayutilities.ang2q_detpos
1267 dpos = cfunc(dAngles, self.r_i, dAxis, config.NTHREADS)
1268
1269 if Npoints == 1:
1270 return (dpos[0, 0], dpos[0, 1], dpos[0, 2])
1271 else:
1272 return numpy.reshape(dpos[:, 0], retshape), \
1273 numpy.reshape(dpos[:, 1], retshape), \
1274 numpy.reshape(dpos[:, 2], retshape)
1275
1276 def getDetectorDistance(self, *args, **kwargs):
1277 """
1278 obtains the detector distance by applying the detector arm movements.
1279 This is especially interesting for the case of 1 or 2D detectors to
1280 perform certain geometric corrections.
1281
1282 Parameters
1283 ----------
1284 args : list
1285 detector angles. Only detector arm angles as described by the
1286 detectorAxis attribute must be given.
1287 kwargs : dict, optional
1288 optional keyword arguments
1289 dim : int, optional
1290 dimension of the detector for which the position should be
1291 determined
1292 roi : tuple or list, optional
1293 region of interest for the detector pixels; (default:
1294 self._area_roi/self._linear_roi)
1295 Nav : tuple or list, optional
1296 number of channels to average to reduce data size; (default:
1297 self._area_nav/self._linear_nav)
1298 deg : bool, optional
1299 flag to tell if angles are passed as degree (default: True)
1300
1301 Returns
1302 -------
1303 ndarray
1304 numpy array with the detector distance
1305 """
1306 x, y, z = self.getDetectorPos(*args, **kwargs)
1307 return numpy.sqrt(x**2 + y**2 + z**2)
1308
1309
1310 class Experiment(object):
1311
1312 """
1313 base class for describing experiments
1314 users should use the derived classes: HXRD, GID, PowderExperiment
1315 """
1316
1317 _valid_init_kwargs = {'en': 'x-ray energy',
1318 'wl': 'x-ray wavelength',
1319 'qconv': 'reciprocal space conversion',
1320 'sampleor': 'sample orientation'}
1321
1322 def __init__(self, ipdir, ndir, **keyargs):
1323 """
1324 initialization of an Experiment class needs the sample orientation
1325 given by the samples surface normal and an second not colinear
1326 direction specifying the inplane reference direction in the crystal
1327 coordinate system. The orientation of the surface normal in the lab
1328 coordinate system can also be given or is automatically determined by
1329 the goniometer type (see argument sampleor).
1330
1331 Parameters
1332 ----------
1333 ipdir : list or tuple or array-like
1334 inplane reference direction (ipdir points into the primary beam
1335 direction at zero angles)
1336 ndir : list or tuple or array-like
1337 surface normal of your sample (ndir points in a direction
1338 perpendicular to the primary beam and the innermost detector
1339 rotation axis)
1340
1341 keyargs : dict, optional
1342 optional keyword arguments
1343 qconv : QConversion, optional
1344 QConversion object to use for the Ang2Q conversion
1345 sampleor : {'det', 'sam', '[xyz][+-]'}, optional
1346 sample orientation specifies the orientation of the sample surface
1347 with respect to the coordinate system in which the goniometer
1348 rotations are given. You can use the [xyz][+-] synthax to specify
1349 the nominal surface orientation (when all goniometer angles are
1350 zero). In addition two special values 'det' and 'sam' are
1351 available, which will let the code determine the orientation from
1352 either the inner most detector or sample rotation. Default is
1353 'det'.
1354 wl : float or str
1355 wavelength of the x-rays in Angstroem (default: 1.5406A)
1356 en : float or str
1357 energy of the x-rays in eV (default: 8048eV == 1.5406A ).
1358 the en keyword overrules the wl keyword
1359
1360 Note:
1361 The qconv argument does not change the Q2Ang function's
1362 behavior. See Q2AngFit function in case you want to calculate
1363 for arbitrary goniometers with some restrictions.
1364 """
1365
1366 utilities.check_kwargs(keyargs, self._valid_init_kwargs,
1367 self.__class__.__name__)
1368
1369 if isinstance(ipdir, (list, tuple, numpy.ndarray)):
1370 self.idir = math.VecUnit(ipdir)
1371 else:
1372 raise TypeError("Inplane direction must be list or numpy array")
1373
1374 if isinstance(ndir, (list, tuple, numpy.ndarray)):
1375 self.ndir = math.VecUnit(ndir)
1376 else:
1377 raise TypeError("normal direction must be list or numpy array")
1378
1379 # test the given direction to be not parallel and warn if not
1380 # perpendicular
1381 if(norm(numpy.cross(self.idir, self.ndir)) < config.EPSILON):
1382 raise InputError("given inplane direction is parallel to normal "
1383 "direction, they must be linear independent!")
1384 if(numpy.abs(numpy.dot(self.idir, self.ndir)) > config.EPSILON):
1385 self.idir = numpy.cross(
1386 numpy.cross(self.ndir, self.idir),
1387 self.ndir)
1388 self.idir = self.idir / norm(self.idir)
1389 warnings.warn("Experiment: given inplane direction is not "
1390 "perpendicular to normal direction\n -> Experiment "
1391 "class uses the following direction with the same "
1392 "azimuth:\n %s" % (' '.join(map(
1393 str, numpy.round(self.idir, 3)))))
1394
1395 # initialize Ang2Q conversion
1396 self._A2QConversion = keyargs.get(
1397 'qconv', QConversion('x+', 'x+', [0, 1, 0]))
1398 self.Ang2Q = self._A2QConversion
1399
1400 self._sampleor = keyargs.get('sampleor', 'det')
1401
1402 # set the coordinate transform for the azimuth used in the experiment
1403 self.scatplane = math.VecUnit(numpy.cross(self.idir, self.ndir))
1404 self._set_transform(self.scatplane, self.idir,
1405 self.ndir, self._sampleor)
1406
1407 # calculate the energy from the wavelength
1408 self._set_wavelength(keyargs.get('wl', config.WAVELENGTH))
1409 if "en" in keyargs:
1410 self._set_energy(keyargs["en"])
1411
1412 def __str__(self):
1413
1414 ostr = "scattering plane normal: (%f %f %f)\n" % (self.scatplane[0],
1415 self.scatplane[1],
1416 self.scatplane[2])
1417 ostr += "inplane azimuth: (%f %f %f)\n" % (self.idir[0],
1418 self.idir[1],
1419 self.idir[2])
1420 ostr += "second refercence direction: (%f %f %f)\n" % (self.ndir[0],
1421 self.ndir[1],
1422 self.ndir[2])
1423 ostr += "energy: %f (eV)\n" % self._en
1424 ostr += "wavelength: %f (Anstrom)\n" % (self._wl)
1425 ostr += self._A2QConversion.__str__()
1426
1427 return ostr
1428
1429 def _set_transform(self, v1, v2, v3, sampleor='det'):
1430 """
1431 set new transformation of the coordinate system to use in the
1432 experimental class.
1433
1434 The sampleor variable determines the sample surface orientation with
1435 respect to the coordinate system in which the goniometer rotations are
1436 given. You can use the [xyz][+-] synthax to specify the nominal surface
1437 orientation (when all goniometer angles are zero). In addition two
1438 special values 'det' and 'sam' are available, which will let the code
1439 determine the orientation from either the inner most detector or sample
1440 rotation. 'det' means the surface is in the plane spanned by the inner
1441 most detector rotation (rotation around primary beam is ignored) and
1442 perpendicular to the primary beam. 'sam' means the surface orientation
1443 is along the innermost sample circles rotation direction (in this case
1444 this should be the azimuth motor to yield the expected results).
1445 Default is 'det'.
1446
1447 Restrictions: the given direction can not be along the primary beam.
1448 If one needs that case, let the maintainer know. Currently this case is
1449 caught and a different axis is automatically used as z-axis.
1450 """
1451 # turn idir to Y and ndir to Z
1452 self._t1 = math.CoordinateTransform(v1, v2, v3)
1453
1454 if sampleor == 'det':
1455 yi = self._A2QConversion.r_i
1456 idc = self._A2QConversion.detectorAxis[-1]
1457 xi = math.getVector(idc)
1458 if norm(numpy.cross(xi, yi)) < config.EPSILON:
1459 # this is the case when a detector rotation around the primary
1460 # beam direction is installed
1461 idc = self._A2QConversion.detectorAxis[-2]
1462 xi = math.getVector(idc)
1463 zi = math.VecUnit(numpy.cross(xi, yi))
1464 elif sampleor == 'sam':
1465 yi = self._A2QConversion.r_i
1466 isc = self._A2QConversion.sampleAxis[-1]
1467 zi = numpy.abs(math.getVector(isc))
1468 if numpy.all(numpy.abs(yi) == numpy.abs(zi)):
1469 zi = numpy.roll(zi, 1)
1470 if config.VERBOSITY >= config.INFO_LOW:
1471 print("XU.Experiment: Warning, sample orientation "
1472 "convention failed. Using (%.3f %.3f %.3f) "
1473 "as internal z-axis" % (zi[0], zi[1], zi[2]))
1474 xi = math.VecUnit(numpy.cross(yi, zi))
1475 else:
1476 yi = self._A2QConversion.r_i
1477 try:
1478 zi = math.getVector(sampleor)
1479 except InputError:
1480 raise InputError('invalid value of sample orientation, use '
1481 'either [xyz][+-] syntax or det/sam!')
1482 if numpy.all(numpy.abs(yi) == numpy.abs(zi)):
1483 zi = numpy.roll(zi, 1)
1484 if config.VERBOSITY >= config.INFO_LOW:
1485 print("XU.Experiment: Warning, sample orientation "
1486 "convention failed. Using (%.3f %.3f %.3f) "
1487 "as internal z-axis" % (zi[0], zi[1], zi[2]))
1488 xi = math.VecUnit(numpy.cross(yi, zi))
1489 # turn r_i to Y and Z defined by detector rotation plane
1490 self._t2 = math.CoordinateTransform(xi, yi, zi)
1491
1492 self._transform = math.Transform(
1493 numpy.dot(numpy.linalg.inv(self._t2.matrix), self._t1.matrix))
1494
1495 def _set_energy(self, energy):
1496 self._en = utilities.energy(energy)
1497 self._wl = utilities.en2lam(self._en)
1498 self.k0 = numpy.pi * 2. / self._wl
1499 self._A2QConversion.wavelength = self._wl
1500
1501 def _set_wavelength(self, wl):
1502 self._wl = utilities.wavelength(wl)
1503 self._en = utilities.lam2en(self._wl)
1504 self.k0 = numpy.pi * 2. / self._wl
1505 self._A2QConversion.wavelength = self._wl
1506
1507 def _get_energy(self):
1508 return self._en
1509
1510 def _get_wavelength(self):
1511 return self._wl
1512
1513 energy = property(_get_energy, _set_energy)
1514 wavelength = property(_get_wavelength, _set_wavelength)
1515
1516 def _set_inplane_direction(self, dir):
1517 self.idir = math.VecUnit(dir)
1518 v1 = numpy.cross(self.ndir, self.idir)
1519 self._set_transform(v1, self.idir, self.ndir, self._sampleor)
1520
1521 def _get_inplane_direction(self):
1522 return self.idir
1523
1524 def _set_normal_direction(self, dir):
1525 self.ndir = math.VecUnit(dir)
1526 v1 = numpy.cross(self.ndir, self.idir)
1527 self._set_transform(v1, self.idir, self.ndir, self._sampleor)
1528
1529 def _get_normal_direction(self):
1530 return self.ndir
1531
1532 def Q2Ang(self, qvec):
1533 pass
1534
1535 def Ang2HKL(self, *args, **kwargs):
1536 """
1537 angular to (h, k, l) space conversion.
1538 It will set the UB argument to Ang2Q and pass all other parameters
1539 unchanged. See Ang2Q for description of the rest of the arguments.
1540
1541 Parameters
1542 ----------
1543 args : list
1544 arguments forwarded to Ang2Q
1545 kwargs : dict, optional
1546 optional keyword arguments
1547 B : array-like, optional
1548 reciprocal space conversion matrix of a Crystal. You can specify
1549 the matrix B (default identiy matrix) shape needs to be (3, 3)
1550 mat : Crystal, optional
1551 Crystal object to use to obtain a B matrix (e.g. xu.materials.Si)
1552 can be used as alternative to the B keyword argument B is favored
1553 in case both are given
1554 U : array-like, optional
1555 orientation matrix U can be given. If none is given the orientation
1556 defined in the Experiment class is used.
1557 dettype : {'point', 'linear', 'area'}, optional
1558 detector type: decides which routine of Ang2Q to call. default
1559 'point'
1560 delta : ndarray, list or tuple, optional
1561 giving delta angles to correct the given ones for misalignment.
1562 delta must be an numpy array or list of length 2. used angles are
1563 than ``(om, tt) - delta``
1564 wl : float or str, optional
1565 x-ray wavelength in angstroem (default: self._wl)
1566 en : float or str, optional
1567 x-ray energy in eV (default: converted self._wl)
1568 deg : bool, optional
1569 flag to tell if angles are passed as degree (default: True)
1570 sampledis : tuple, list or array-like, optional
1571 sample displacement vector in relative units of the detector
1572 distance (default: (0, 0, 0))
1573
1574 Returns
1575 -------
1576 ndarray
1577 H K L coordinates as numpy.ndarray with shape `(N , 3)` where `N`
1578 corresponds to the number of points given in the input (args)
1579 """
1580
1581 valid_kwargs = {'B': 'orthonormalization matrix',
1582 'U': 'orientation matrix',
1583 'mat': 'material object',
1584 'dettype': 'string with detector type'}
1585 valid_kwargs.update(QConversion._valid_call_kwargs)
1586 del valid_kwargs['UB']
1587 utilities.check_kwargs(kwargs, valid_kwargs, 'Ang2HKL')
1588
1589 if "B" in kwargs:
1590 B = numpy.array(kwargs['B'])
1591 kwargs.pop("B")
1592 elif "mat" in kwargs:
1593 mat = kwargs['mat']
1594 B = mat.B
1595 kwargs.pop("mat")
1596 else:
1597 B = numpy.identity(3)
1598
1599 if "U" in kwargs:
1600 U = numpy.array(kwargs['U'])
1601 kwargs.pop("U")
1602 else:
1603 U = self._transform.matrix
1604
1605 kwargs['UB'] = numpy.dot(U, B)
1606
1607 if "dettype" in kwargs:
1608 typ = kwargs['dettype']
1609 if typ not in ('point', 'linear', 'area'):
1610 raise InputError("wrong dettype given: needs to be one of "
1611 "'point', 'linear', 'area'")
1612 kwargs.pop("dettype")
1613 else:
1614 typ = 'point'
1615
1616 if typ == 'linear':
1617 return self.Ang2Q.linear(*args, **kwargs)
1618 elif typ == 'area':
1619 return self.Ang2Q.area(*args, **kwargs)
1620 else:
1621 return self.Ang2Q(*args, **kwargs)
1622
1623 def Transform(self, v):
1624 """
1625 transforms a vector, matrix or tensor of rank 4 (e.g. elasticity
1626 tensor) to the coordinate frame of the Experiment class. This is for
1627 example necessary before any Q2Ang-conversion can be performed.
1628
1629 Parameters
1630 ----------
1631 v : object to transform, list or numpy array of shape
1632 (n,) (n, n), (n, n, n, n) where n is the rank of the
1633 transformation matrix
1634
1635 Returns
1636 -------
1637 transformed object of the same shape as v
1638 """
1639 return self._transform(v)
1640
1641 def TiltAngle(self, q, deg=True):
1642 """
1643 TiltAngle(q, deg=True):
1644 Return the angle between a q-space position and the surface normal.
1645
1646 Parameters
1647 ----------
1648 q : list or numpy array with the reciprocal space position
1649
1650 optional keyword arguments:
1651 deg : True/False whether the return value should be in degree or
1652 radians (default: True)
1653 """
1654
1655 if isinstance(q, list):
1656 qt = numpy.array(q, dtype=numpy.double)
1657 elif isinstance(q, numpy.ndarray):
1658 qt = q
1659 else:
1660 raise TypeError("q-space position must be list or numpy array")
1661
1662 return math.VecAngle(self.ndir, qt, deg)
1663
1664 def _prepare_qvec(self, Q):
1665 """
1666 check and reshape input to have the same q array for all possible types
1667 of input
1668 """
1669 if len(Q) < 3:
1670 Q = Q[0]
1671 if len(Q) < 3:
1672 raise InputError("need 3 q-space vector components")
1673
1674 if isinstance(Q, (list, tuple, numpy.ndarray)):
1675 q = numpy.asarray(Q, dtype=numpy.double)
1676 else:
1677 raise TypeError("Q vector must be a list, tuple or numpy array")
1678
1679 if len(q.shape) != 2:
1680 q = q.reshape(3, -1)
1681 return q.T
1682
1683
1684 class HXRD(Experiment):
1685
1686 """
1687 class describing high angle x-ray diffraction experiments
1688 the class helps with calculating the angles of Bragg reflections
1689 as well as helps with analyzing measured data
1690
1691 the class describes a two circle (omega, twotheta) goniometer to
1692 help with coplanar x-ray diffraction experiments. Nevertheless 3D data
1693 can be treated with the use of linear and area detectors.
1694 see help self.Ang2Q
1695 """
1696
1697 def __init__(self, idir, ndir, geometry='hi_lo', **keyargs):
1698 """
1699 initialization routine for the HXRD Experiment class
1700
1701 Parameters
1702 ----------
1703 idir, ndir, keyargs :
1704 same as for the Experiment base class -> please look at the
1705 docstring of Experiment.__init__ for more details
1706 geometry : {'hi_lo', 'lo_hi', 'real'}, optional
1707 determines the scattering geometry :
1708
1709 - 'hi_lo' (default) high incidence-low exit
1710 - 'lo_hi' low incidence - high exit
1711 - 'real' general geometry - q-coordinates determine
1712 high or low incidence
1713
1714 """
1715 if "qconv" not in keyargs:
1716 keyargs['qconv'] = QConversion('x+', 'x+', [0, 1, 0])
1717
1718 if geometry in ["hi_lo", "lo_hi", "real"]:
1719 self.geometry = geometry
1720 else:
1721 raise InputError("HXRD: invalid value for the geometry "
1722 "argument given")
1723
1724 Experiment.__init__(self, idir, ndir, **keyargs)
1725
1726 if config.VERBOSITY >= config.DEBUG:
1727 print(
1728 "XU.HXRD.__init__: \nEnergy: %s \nGeometry: %s \n%s---" %
1729 (self._en, self.geometry, str(
1730 self.Ang2Q)))
1731
1732 def Ang2Q(self, om, tt, **kwargs):
1733 """
1734 angular to momentum space conversion for a point detector. Also see
1735 help HXRD.Ang2Q for procedures which treat line and area detectors
1736
1737 Parameters
1738 ----------
1739 om, tt : float or array-like
1740 sample and detector angles as numpy array, lists or Scalars must be
1741 given. All arguments must have the same shape or length. However,
1742 if one angle is always the same its enough to give one scalar
1743 value.
1744
1745 kwargs : dict, optional
1746 optional keyword arguments
1747 delta : list or array-like
1748 giving delta angles to correct the given ones for misalignment.
1749 delta must be an numpy array or list of length 2. Used angles are
1750 than om, tt - delta
1751 UB : array-like
1752 matrix for conversion from (hkl) coordinates to Q of sample used to
1753 determine not Q but (hkl) (default: identity matrix)
1754 wl : float or str, optional
1755 x-ray wavelength in angstroem (default: self._wl)
1756 deg : bool, optional
1757 flag to tell if angles are passed as degree (default: True)
1758
1759 Returns
1760 -------
1761 ndarray
1762 reciprocal space positions as numpy.ndarray with shape `(N , 3)`
1763 where `N` corresponds to the number of points given in the input
1764 """
1765 # dummy function to have some documentation string available
1766 # the real function is generated dynamically in the __init__ routine
1767 pass
1768
1769 def Q2Ang(self, *Q, **keyargs):
1770 """
1771 Convert a reciprocal space vector Q to COPLANAR scattering angles. The
1772 keyword argument trans determines whether Q should be transformed to
1773 the experimental coordinate frame or not. The coplanar scattering
1774 angles correspond to a goniometer with sample rotations
1775 ['x+', 'y+', 'z-'] and detector rotation 'x+' and primary beam along y.
1776 This is a standard four circle diffractometer.
1777
1778 Note:
1779 The behavior of this function is unchanged if the goniometer
1780 definition is changed!
1781
1782 Parameters
1783 ----------
1784 Q : list, tuple or array-like
1785 array of shape (3) with q-space vector components or 3
1786 separate lists with qx, qy, qz
1787
1788 trans : bool, optional
1789 apply coordinate transformation on Q (default True)
1790 deg : book, optional
1791 (default True) determines if the angles are returned in radians or
1792 degrees
1793 geometry : {'hi_lo', 'lo_hi', 'real', 'realTilt'}, optional
1794 determines the scattering geometry (default: self.geometry):
1795
1796 - 'hi_lo' high incidence and low exit
1797 - 'lo_hi' low incidence and high exit
1798 - 'real' general geometry with angles determined by
1799 q-coordinates (azimuth); this and upper geometries
1800 return [omega, 0, phi, twotheta]
1801 - 'realTilt' general geometry with angles determined by
1802 q-coordinates (tilt); returns [omega, chi, phi, twotheta]
1803
1804 refrac : bool, optional
1805 determines if refraction is taken into account;
1806 if True then also a material must be given (default: False)
1807 mat : Crystal
1808 Crystal object; needed to obtain its optical properties for
1809 refraction correction, otherwise not used
1810 full_output : bool, optional
1811 determines if additional output is given to determine scattering
1812 angles more accurately in case refraction is set to True. default:
1813 False
1814 fi, fd : tuple or list
1815 if refraction correction is applied one can optionally specify the
1816 facet through which the beam enters (fi) and exits (fd) fi, fd must
1817 be the surface normal vectors (not transformed & not necessarily
1818 normalized). If omitted the normal direction of the experiment is
1819 used.
1820
1821 Returns
1822 -------
1823 ndarray
1824 **full_output=False**: a numpy array of shape (4) with four
1825 scattering angles which are [omega, chi, phi, twotheta];
1826
1827 - omega : incidence angle with respect to surface
1828 - chi : sample tilt for the case of non-coplanar geometry
1829 - phi : sample azimuth with respect to inplane reference
1830 direction
1831 - twotheta : scattering angle/detector angle
1832
1833 **full_output=True**: a numpy array of shape (6) with five angles
1834 which are [omega, chi, phi, twotheta, psi_i, psi_d]
1835
1836 - psi_i : offset of the incidence beam from the scattering plane
1837 due to refraction
1838 - pdi_d : offset ot the diffracted beam from the scattering plane
1839 due to refraction
1840 """
1841
1842 valid_kwargs = {'trans': 'flag, perform coordinate transformation',
1843 'deg': 'flag, return degrees',
1844 'geometry': 'geometry string',
1845 'refrac': 'flag',
1846 'mat': 'Crystal instance',
1847 'fi': 'incidence facet',
1848 'fd': 'exit facet',
1849 'full_output': 'see docstring for details'}
1850 utilities.check_kwargs(keyargs, valid_kwargs, 'Q2Ang')
1851
1852 q = self._prepare_qvec(Q)
1853
1854 # parse keyword arguments
1855 geom = keyargs.get('geometry', self.geometry)
1856 if geom not in ["hi_lo", "lo_hi", "real", "realTilt"]:
1857 raise InputError("HXRD: invalid value for the geometry argument "
1858 "given\n valid entries are: hi_lo, lo_hi, real, "
1859 "realTilt")
1860 trans = keyargs.get('trans', True)
1861 deg = keyargs.get('deg', True)
1862 mat = keyargs.get('mat', None) # material for optical properties
1863
1864 refrac = keyargs.get('refrac', False)
1865 if refrac and mat is None: # check if material is available
1866 raise InputError("keyword argument 'mat' must be set when "
1867 "'refrac' is set to True!")
1868
1869 foutp = keyargs.get('full_output', False)
1870 fi = keyargs.get('fi', self.ndir) # incidence facet
1871 fi = math.VecUnit(self.Transform(fi))
1872
1873 fd = keyargs.get('fd', self.ndir) # exit facet
1874 fd = math.VecUnit(self.Transform(fd))
1875
1876 # set parameters for the calculation
1877 z = self.Transform(self.ndir) # z
1878 y = self.Transform(self.idir) # y
1879 x = self.Transform(self.scatplane) # x
1880 if refrac:
1881 n = numpy.real(
1882 mat.idx_refraction(
1883 self.energy)) # index of refraction
1884 k = self.k0 * n
1885 else:
1886 k = self.k0
1887
1888 # start calculation for each given Q-point
1889 if foutp:
1890 angle = numpy.zeros((6, q.shape[0]))
1891 else:
1892 angle = numpy.zeros((4, q.shape[0]))
1893
1894 if trans:
1895 q = self.Transform(q)
1896
1897 if config.VERBOSITY >= config.DEBUG:
1898 print("XU.HXRD.Q2Ang: q= %s" % repr(q))
1899
1900 qa = math.VecNorm(q)
1901 tth = 2. * numpy.arcsin(qa / 2. / k)
1902
1903 # calculation of the sample azimuth phi (scattering plane
1904 # spanned by qvec[1] and qvec[2] directions)
1905
1906 chi = -numpy.arctan2(math.VecDot(q, x), math.VecDot(q, z))
1907 if numpy.any(numpy.abs(math.VecDot(q, z)) < config.EPSILON):
1908 if config.VERBOSITY >= config.INFO_LOW:
1909 print("XU.HXRD: some position is perpendicular to ndir-"
1910 "reference direction (might be inplane or "
1911 "unreachable)")
1912
1913 if geom == 'hi_lo':
1914 # +: high incidence geometry
1915 om = tth / 2. + math.VecAngle(q, z)
1916 phi = -numpy.arctan2(math.VecDot(q, x), math.VecDot(q, y))
1917 elif geom == 'lo_hi':
1918 # -: low incidence geometry
1919 om = tth / 2. - math.VecAngle(q, z)
1920 phi = -numpy.arctan2(-1 * math.VecDot(q, x),
1921 -1 * math.VecDot(q, y))
1922 elif geom == 'real':
1923 phi = -numpy.arctan2(math.VecDot(q, x), math.VecDot(q, y))
1924 sign = numpy.ones(q.shape[0])
1925 m = numpy.abs(phi) > numpy.pi / 2.
1926 phi = -numpy.arctan2(math.VecDot(q, x), math.VecDot(q, y))
1927 sign[m] = -1
1928 phi[m] = -numpy.arctan2(-1 * math.VecDot(q[m], x),
1929 -1 * math.VecDot(q[m], y))
1930 om = tth / 2 + sign * math.VecAngle(q, z)
1931 elif geom == 'realTilt':
1932 phi = 0.
1933 om = tth / 2 + numpy.arctan2(
1934 math.VecDot(q, y),
1935 numpy.sqrt(math.VecDot(q, z) ** 2 + math.VecDot(q, x) ** 2))
1936
1937 # refraction correction at incidence and exit facet
1938 psi_i = numpy.zeros_like(tth)
1939 psi_d = numpy.zeros_like(tth) # if refrac is false and full_output
1940 if refrac:
1941 beta = tth - om
1942
1943 ki = k * (numpy.cos(om)[:, numpy.newaxis] * y[numpy.newaxis, :] -
1944 numpy.sin(om)[:, numpy.newaxis] * z[numpy.newaxis, :])
1945 kd = k * (numpy.cos(beta)[:, numpy.newaxis] * y[numpy.newaxis, :] +
1946 numpy.sin(beta)[:, numpy.newaxis] * z[numpy.newaxis, :])
1947
1948 # refraction at incidence facet
1949 m = math.VecDot(ki, fi) > 0
1950 if numpy.any(m):
1951 print("XU.HXRD: Warning, incidence facet not hit by "
1952 "primary beam for all positions! check your input!")
1953 om[m] = numpy.nan
1954 tth[m] = numpy.nan
1955 mnot = numpy.logical_not(m)
1956 cosbi = numpy.abs(math.VecDot(ki, fi) / math.VecNorm(ki))
1957 cosb0 = numpy.sqrt(1 - n ** 2 * (1 - cosbi ** 2))
1958
1959 ki0 = self.k0 * (n * math.VecUnit(ki) -
1960 (numpy.sign(math.VecDot(ki, fi)) *
1961 (n * cosbi - cosb0))[:, numpy.newaxis] *
1962 fi[numpy.newaxis, :])
1963 om[mnot] = math.VecAngle(ki0, y)
1964 psi_i[mnot] = numpy.arcsin(math.VecDot(ki0, x) / self.k0)
1965 if config.VERBOSITY >= config.DEBUG:
1966 print("XU.HXRD.Q2Ang: ki, ki0 = %s %s"
1967 % (repr(ki), repr(ki0)))
1968
1969 # refraction at exit facet
1970 m = math.VecDot(kd, fd) < 0
1971 if numpy.any(m):
1972 print("XU.HXRD: Warning, exit facet not hit by "
1973 "diffracted beam! check your input!")
1974 om[m] = numpy.nan
1975 tth[m] = numpy.nan
1976
1977 cosbd = numpy.abs(math.VecDot(kd, fd) / math.VecNorm(kd))
1978 cosb0 = numpy.sqrt(1 - n ** 2 * (1 - cosbd ** 2))
1979
1980 kd0 = self.k0 * (n * math.VecUnit(kd) -
1981 (numpy.sign(math.VecDot(kd, fd)) *
1982 (n * cosbd - cosb0))[:, numpy.newaxis] *
1983 fd[numpy.newaxis, :])
1984 tth[mnot] = math.VecAngle(ki0, kd0)
1985 psi_d[mnot] = numpy.arcsin(numpy.dot(kd0, x) / self.k0)
1986 if config.VERBOSITY >= config.DEBUG:
1987 print("XU.HXRD.Q2Ang: kd, kd0 = %s %s"
1988 % (repr(kd), repr(kd0)))
1989
1990 if geom == 'realTilt':
1991 angle[0, :] = om
1992 angle[1, :] = chi
1993 angle[3, :] = tth
1994 else:
1995 angle[0, :] = om
1996 angle[2, :] = phi
1997 angle[3, :] = tth
1998 if foutp:
1999 angle[4, :] = psi_i
2000 angle[5, :] = psi_d
2001
2002 if q.shape[0] == 1:
2003 angle = angle.flatten()
2004 if config.VERBOSITY >= config.INFO_ALL:
2005 print("XU.HXRD.Q2Ang: om, chi, phi, tth,[psi_i, psi_d] = %s"
2006 % repr(angle))
2007
2008 if deg:
2009 return numpy.degrees(angle)
2010 else:
2011 return angle
2012
2013
2014 class FourC(HXRD):
2015
2016 """
2017 class describing high angle x-ray diffraction experiments
2018 the class helps with calculating the angles of Bragg reflections
2019 as well as helps with analyzing measured data
2020
2021 the class describes a four circle (omega, chi, phi, twotheta) goniometer to
2022 help with coplanar x-ray diffraction experiments. Nevertheless 3D data can
2023 be treated with the use of linear and area detectors. see help self.Ang2Q
2024 """
2025
2026 def __init__(self, idir, ndir, **keyargs):
2027 """
2028 initialization routine for the FourC Experiment class
2029
2030 Parameters
2031 ----------
2032 idir, ndir, keyargs :
2033 same as for the Experiment base class -> please look at the
2034 docstring of Experiment.__init__ for more details
2035 geometry : {'hi_lo', 'lo_hi', 'real'}, optional
2036 determines the scattering geometry :
2037
2038 - 'hi_lo' (default) high incidence-low exit
2039 - 'lo_hi' low incidence - high exit
2040 - 'real' general geometry - q-coordinates determine
2041 high or low incidence
2042
2043 """
2044 if "qconv" not in keyargs:
2045 # 3S+1D goniometer (standard four-circle goniometer,
2046 # omega, chi, phi, theta)
2047 keyargs['qconv'] = QConversion(['x+', 'y+', 'z-'],
2048 'x+', [0, 1, 0])
2049
2050 HXRD.__init__(self, idir, ndir, **keyargs)
2051
2052
2053 class NonCOP(Experiment):
2054
2055 """
2056 class describing high angle x-ray diffraction experiments. The class helps
2057 with calculating the angles of Bragg reflections as well as helps with
2058 analyzing measured data for NON-COPLANAR measurements, where the tilt is
2059 used to align asymmetric peaks, like in the case of a polefigure
2060 measurement.
2061
2062 The class describes a four circle (omega, chi, phi, twotheta) goniometer to
2063 help with x-ray diffraction experiments. Linear and area detectors can be
2064 treated as described in "help self.Ang2Q"
2065 """
2066
2067 def __init__(self, idir, ndir, **keyargs):
2068 """
2069 initialization routine for the NonCOP Experiment class
2070
2071 Parameters
2072 ----------
2073 idir, ndir, keyargs :
2074 same as for the Experiment base class
2075 """
2076 if "qconv" not in keyargs:
2077 # 3S+1D goniometer (standard four-circle goniometer,
2078 # omega, chi, phi, theta)
2079 keyargs['qconv'] = QConversion(['x+', 'y+', 'z-'],
2080 'x+', [0, 1, 0])
2081
2082 Experiment.__init__(self, idir, ndir, **keyargs)
2083
2084 def Ang2Q(self, om, chi, phi, tt, **kwargs):
2085 """
2086 angular to momentum space conversion for a point detector. Also see
2087 help NonCOP.Ang2Q for procedures which treat line and area detectors
2088
2089 Parameters
2090 ----------
2091 om, chi, phi, tt : float or array-like
2092 sample and detector angles as numpy array, lists or Scalars must be
2093 given. All arguments must have the same shape or length. However,
2094 if one angle is always the same its enough to give one scalar
2095 value.
2096
2097 kwargs : dict, optional
2098 optional keyword arguments
2099 delta : list, tuple or array-like, optional
2100 giving delta angles to correct the given ones for misalignment
2101 delta must be an numpy array or list of length 4. Used angles are
2102 than om, chi, phi, tt - delta
2103 UB : array-like, optional
2104 matrix for conversion from (hkl) coordinates to Q of sample used to
2105 determine not Q but (hkl) (default: identity matrix)
2106 wl : float or str, optional
2107 x-ray wavelength in angstroem (default: self._wl)
2108 deg : bool, optional
2109 flag to tell if angles are passed as degree (default: True)
2110
2111 Returns
2112 -------
2113 ndarray
2114 reciprocal space positions as numpy.ndarray with shape `(N , 3)`
2115 where `N` corresponds to the number of points given in the input
2116 """
2117 # dummy function to have some documentation string available
2118 # the real function is generated dynamically in the __init__ routine
2119 pass
2120
2121 def Q2Ang(self, *Q, **keyargs):
2122 """
2123 Convert a reciprocal space vector Q to NON-COPLANAR scattering angles.
2124 The keyword argument trans determines whether Q should be transformed
2125 to the experimental coordinate frame or not.
2126
2127 Note:
2128 The behavior of this function is unchanged if the goniometer
2129 definition is changed!
2130
2131 Parameters
2132 ----------
2133 Q : list, tuple or array-like
2134 array of shape (3) with q-space vector components or 3
2135 separate lists with qx, qy, qz
2136
2137 trans : bool, optional
2138 apply coordinate transformation on Q (default True)
2139 deg : book, optional
2140 (default True) determines if the angles are returned in radians or
2141 degrees
2142
2143 Returns
2144 -------
2145 ndarray
2146 a numpy array of shape (4) with four scattering
2147 angles which are [omega, chi, phi, twotheta];
2148
2149 - omega : incidence angle with respect to surface
2150 - chi : sample tilt for the case of non-coplanar geometry
2151 - phi : sample azimuth with respect to inplane reference
2152 direction
2153 - twotheta : scattering angle/detector angle
2154 """
2155
2156 valid_kwargs = {'trans': 'coordinate transformation flag',
2157 'deg': 'degree-flag'}
2158 utilities.check_kwargs(keyargs, valid_kwargs, 'Q2Ang')
2159
2160 q = self._prepare_qvec(Q)
2161
2162 trans = keyargs.get('trans', True)
2163 deg = keyargs.get('deg', True)
2164
2165 angle = numpy.zeros((4, q.shape[0]))
2166 # set parameters for the calculation
2167 z = self.Transform(self.ndir) # z
2168 y = self.Transform(self.idir) # y
2169 x = self.Transform(self.scatplane) # x
2170
2171 if trans:
2172 q = self.Transform(q)
2173
2174 if config.VERBOSITY >= config.DEBUG:
2175 print("XU.NonCop.Q2Ang: q= %s" % repr(q))
2176
2177 qa = math.VecNorm(q)
2178 tth = 2. * numpy.arcsin(qa / 2. / self.k0)
2179 om = tth / 2.
2180
2181 # calculation of the sample azimuth
2182 # the sign depends on the phi movement direction
2183 phi = -1 * numpy.arctan2(
2184 math.VecDot(q, x),
2185 math.VecDot(q, y)) - numpy.pi / 2.
2186
2187 chi = (math.VecAngle(q, z))
2188
2189 angle[0, :] = om
2190 angle[1, :] = chi
2191 angle[2, :] = phi
2192 angle[3, :] = tth
2193
2194 if q.shape[0] == 1:
2195 angle = angle.flatten()
2196 if config.VERBOSITY >= config.INFO_ALL:
2197 print("XU.HXRD.Q2Ang: [om, chi, phi, tth] = %s" % repr(angle))
2198
2199 if deg:
2200 return numpy.degrees(angle)
2201 else:
2202 return angle
2203
2204
2205 class GID(Experiment):
2206
2207 """
2208 class describing grazing incidence x-ray diffraction experiments
2209 the class helps with calculating the angles of Bragg reflections
2210 as well as it helps with analyzing measured data
2211
2212 the class describes a four circle (alpha_i, azimuth, twotheta, beta)
2213 goniometer to help with GID experiments at the ROTATING ANODE.
2214 3D data can be treated with the use of linear and area detectors.
2215 see help self.Ang2Q
2216
2217 Using this class the default sample surface orientation is determined by
2218 the inner most sample rotation (which is usually the azimuth motor).
2219 """
2220
2221 def __init__(self, idir, ndir, **keyargs):
2222 """
2223 initialization routine for the GID Experiment class
2224
2225 - ``idir`` defines the inplane reference direction (idir points into
2226 the PB direction at zero angles)
2227 - ``ndir`` defines the surface normal of your sample (ndir points
2228 along the innermost sample rotation axis)
2229
2230 Parameters
2231 ----------
2232 idir, ndir, keyargs :
2233 same as for the Experiment base class
2234 """
2235 if 'sampleor' not in keyargs:
2236 keyargs['sampleor'] = 'sam'
2237
2238 if "qconv" not in keyargs:
2239 # 2S+2D goniometer
2240 keyargs['qconv'] = QConversion(['z-', 'x+'], ['x+', 'z-'],
2241 [0, 1, 0])
2242
2243 Experiment.__init__(self, idir, ndir, **keyargs)
2244
2245 def Q2Ang(self, Q, trans=True, deg=True, **kwargs):
2246 """
2247 calculate the GID angles needed in the experiment
2248 the inplane reference direction defines the direction were
2249 the reference direction is parallel to the primary beam
2250 (i.e. lattice planes perpendicular to the beam)
2251
2252 Note:
2253 The behavior of this function is unchanged if the goniometer
2254 definition is changed!
2255
2256 Parameters
2257 ----------
2258 Q : list, tuple or array-like
2259 array of shape (3) with q-space vector components or 3
2260 separate lists with qx, qy, qz
2261
2262 trans : bool, optional
2263 apply coordinate transformation on Q (default True)
2264 deg : book, optional
2265 (default True) determines if the angles are returned in radians or
2266 degrees
2267
2268 Returns
2269 -------
2270 ndarray
2271 a numpy array of shape (4) with four GID scattering
2272 angles which are [alpha_i, azimuth, twotheta, beta];
2273
2274 - alpha_i : incidence angle to surface (at the moment always 0)
2275 - azimuth : sample rotation with respect to the inplane
2276 reference direction
2277 - twotheta : scattering angle
2278 - beta : exit angle from surface (at the moment always 0)
2279
2280 """
2281
2282 valid_kwargs = {'trans': 'coordinate transformation flag',
2283 'deg': 'degree-flag'}
2284 utilities.check_kwargs(kwargs, valid_kwargs, 'Q2Ang')
2285
2286 if isinstance(Q, list):
2287 q = numpy.array(Q, dtype=numpy.double)
2288 elif isinstance(Q, numpy.ndarray):
2289 q = Q
2290 else:
2291 raise TypeError("Q vector must be a list or numpy array")
2292
2293 if trans:
2294 q = self.Transform(q)
2295
2296 if config.VERBOSITY >= config.INFO_ALL:
2297 print("XU.GID.Q2Ang: q = %s" % repr(q))
2298
2299 # set parameters for the calculation
2300 z = self.Transform(self.ndir) # z
2301 y = self.Transform(self.idir) # y
2302 x = self.Transform(self.scatplane) # x
2303
2304 # check if reflection is inplane
2305 if numpy.abs(math.VecDot(q, z)) >= 0.001:
2306 raise InputError("Reflection not reachable in GID geometry (Q: %s)"
2307 % str(q))
2308
2309 # calculate angle to inplane reference direction
2310 aref = numpy.arctan2(math.VecDot(q, x), math.VecDot(q, y))
2311
2312 # calculate scattering angle
2313 qa = math.VecNorm(q)
2314 tth = 2. * numpy.arcsin(qa / 2. / self.k0)
2315 azimuth = numpy.pi / 2 + aref + tth / 2.
2316
2317 if deg:
2318 ang = [0, numpy.degrees(azimuth), numpy.degrees(tth), 0]
2319 else:
2320 ang = [0, azimuth, tth, 0]
2321
2322 if config.VERBOSITY >= config.INFO_ALL:
2323 print("XU.GID.Q2Ang: [ai, azimuth, tth, beta] = %s \n difference "
2324 "to inplane reference which is %5.2f" % (str(ang), aref))
2325
2326 return ang
2327
2328 def Ang2Q(self, ai, phi, tt, beta, **kwargs):
2329 """
2330 angular to momentum space conversion for a point detector. Also see
2331 help GID.Ang2Q for procedures which treat line and area detectors
2332
2333 Parameters
2334 ----------
2335 ai, phi, tt, beta : float or array-like
2336 sample and detector angles as numpy array, lists or Scalars must be
2337 given. All arguments must have the same shape or length. However,
2338 if one angle is always the same its enough to give one scalar
2339 value.
2340
2341 kwargs : dict, optional
2342 optional keyword arguments
2343 delta : list, tuple or array-like, optional
2344 giving delta angles to correct the given ones for misalignment
2345 delta must be an numpy array or list of length 4. Used angles are
2346 then ``ai, phi, tt, beta - delta``
2347 UB : array-like, optional
2348 matrix for conversion from (hkl) coordinates to Q of sample used to
2349 determine not Q but (hkl) (default: identity matrix)
2350 wl : float or str, optional
2351 x-ray wavelength in angstroem (default: self._wl)
2352 deg : bool, optional
2353 flag to tell if angles are passed as degree (default: True)
2354
2355 Returns
2356 -------
2357 ndarray
2358 reciprocal space positions as numpy.ndarray with shape `(N , 3)`
2359 where `N` corresponds to the number of points given in the input
2360 """
2361 # dummy function to have some documentation string available
2362 # the real function is generated dynamically in the __init__ routine
2363 pass
2364
2365
2366 class GISAXS(Experiment):
2367
2368 """
2369 class describing grazing incidence x-ray diffraction experiments
2370 the class helps with calculating the angles of Bragg reflections
2371 as well as it helps with analyzing measured data
2372
2373 the class describes a three circle (alpha_i, twotheta, beta)
2374 goniometer to help with GISAXS experiments at the ROTATING ANODE.
2375 3D data can be treated with the use of linear and area detectors.
2376 see help self.Ang2Q
2377 """
2378
2379 def __init__(self, idir, ndir, **keyargs):
2380 """
2381 initialization routine for the GISAXS Experiment class
2382
2383 ``idir`` defines the inplane reference direction (idir points into the
2384 PB direction at zero angles)
2385
2386 Parameters
2387 ----------
2388 idir, ndir, keyargs :
2389 same as for the Experiment base class
2390 """
2391 if "qconv" not in keyargs:
2392 # 1S+2D goniometer
2393 keyargs['qconv'] = QConversion(['x+'], ['x+', 'z-'],
2394 [0, 1, 0])
2395
2396 Experiment.__init__(self, idir, ndir, **keyargs)
2397
2398 def Q2Ang(self, Q, trans=True, deg=True, **kwargs):
2399 pass
2400
2401 def Ang2Q(self, ai, tt, beta, **kwargs):
2402 """
2403 angular to momentum space conversion for a point detector. Also see
2404 help GISAXS.Ang2Q for procedures which treat line and area detectors
2405
2406 Parameters
2407 ----------
2408 ai, tt, beta : float or array-like
2409 sample and detector angles as numpy array, lists or Scalars must be
2410 given. all arguments must have the same shape or length. Howevver,
2411 if one angle is always the same its enough to give one scalar
2412 value.
2413
2414 kwargs : dict, optional
2415 optional keyword arguments
2416 delta : list, tuple or array-like, optional
2417 giving delta angles to correct the given ones for misalignment
2418 delta must be an numpy array or list of length 3. Used angles are
2419 then ``ai, tt, beta - delta``
2420 UB : array-like, optional
2421 matrix for conversion from (hkl) coordinates to Q of sample used to
2422 determine not Q but (hkl) (default: identity matrix)
2423 wl : float or str, optional
2424 x-ray wavelength in angstroem (default: self._wl)
2425 deg : bool, optional
2426 flag to tell if angles are passed as degree (default: True)
2427
2428 Returns
2429 -------
2430 ndarray
2431 reciprocal space positions as numpy.ndarray with shape `(N , 3)`
2432 where `N` corresponds to the number of points given in the input
2433 """
2434 # dummy function to have some documentation string available
2435 # the real function is generated dynamically in the __init__ routine
2436 pass
2437
2438
2439 class PowderExperiment(Experiment):
2440 """
2441 Experimental class for powder diffraction which helps to convert theta
2442 angles to momentum transfer space
2443 """
2444
2445 def __init__(self, **kwargs):
2446 """
2447 class constructor which takes the same keyword arguments as the
2448 Experiment class
2449
2450 Parameters
2451 ----------
2452 kwargs : dict, optional
2453 keyword arguments same as for the Experiment base class
2454 """
2455 Experiment.__init__(self, [0, 1, 0], [0, 0, 1], **kwargs)
2456 self.Ang2Q = self._Ang2Q
2457
2458 def _Ang2Q(self, th, deg=True):
2459 """
2460 Converts theta angles to reciprocal space positions
2461 returns the absolute value of momentum transfer
2462 """
2463 if deg:
2464 lth = numpy.radians(th)
2465 else:
2466 lth = th
2467
2468 qpos = 2 * self.k0 * numpy.sin(lth)
2469 return qpos
2470
2471 def Q2Ang(self, qpos, deg=True):
2472 """
2473 Converts reciprocal space values to theta angles
2474 """
2475 th = numpy.arcsin(qpos / (2 * self.k0))
2476
2477 if deg:
2478 th = numpy.degrees(th)
2479
2480 return th
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010, 2013
16 # Eugen Wintersberger <eugen.wintersberger@desy.de>
17 # Copyright (C) 2009 Mario Keplinger <mario.keplinger@jku.at>
18 # Copyright (C) 2009-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
19
20 import abc
21
22 import numpy
23
24 from . import config, cxrayutilities, utilities
25 from .exception import InputError
26
27
28 def delta(min_value, max_value, n):
29 """
30 Compute the stepsize along an axis of a grid.
31
32 Parameters
33 ----------
34 min_value : axis minimum value
35 max_value : axis maximum value
36 n : number of steps
37 """
38 if n != 1:
39 return (float(max_value) - float(min_value)) / float(n - 1)
40 else:
41 return numpy.inf
42
43
44 def axis(min_value, max_value, n):
45 """
46 Compute the a grid axis.
47
48 Parameters
49 ----------
50 min_value : float
51 axis minimum value
52 max_value : float
53 axis maximum value
54 n : int
55 number of steps
56 """
57
58 if n != 1:
59 d = delta(min_value, max_value, n)
60 a = min_value + d * numpy.arange(0, n)
61 else:
62 a = (min_value + max_value) / 2.
63
64 return a
65
66
67 def ones(*args):
68 """
69 Compute ones for matrix generation. The shape is determined by the number
70 of input arguments.
71 """
72 return numpy.ones(args, dtype=numpy.double)
73
74
75 class Gridder(utilities.ABC):
76 """
77 Basis class for gridders in xrayutilities. A gridder is a function mapping
78 irregular spaced data onto a regular grid by binning the data into equally
79 sized elements.
80
81 There are different ways of defining the regular grid of a Gridder. In
82 xrayutilities the data bins extend beyond the data range in the input data,
83 but the given position being the center of these bins, extends from the
84 minimum to the maximum of the data! The main motivation for this was to
85 create a Gridder, which when feeded with N equidistant data points and
86 gridded with N bins would not change the data position (not the case with
87 numpy.histogramm functions!). Of course this leads to the fact that for
88 homogeneous point density the first and last bin in any direction are not
89 filled as the other bins.
90
91 A different definition is used by numpy histogram functions where the bins
92 extend only to the end of the data range. (see numpy histogram,
93 histrogram2d, ...)
94 """
95
96 def __init__(self):
97 """
98 Constructor defining default properties of any Gridder class
99 """
100
101 self.flags = 0
102 # by default every call to gridder will start a new gridding
103 self.keep_data = False
104 self.normalize = True
105 # flag to allow for sequential gridding with fixed data range
106 self.fixed_range = False
107
108 # no data initialization necessary in c-code
109 self.flags = utilities.set_bit(self.flags, 0)
110
111 if not hasattr(self, '_gdata'):
112 self._gdata = numpy.empty(0)
113 if not hasattr(self, '_gnorm'):
114 self._gnorm = numpy.empty(0)
115
116 if config.VERBOSITY >= config.INFO_ALL:
117 self.flags = utilities.set_bit(self.flags, 3)
118
119 @abc.abstractmethod
120 def __call__(self):
121 """
122 abstract call method which every implementation of a Gridder has to
123 override
124 """
125 pass
126
127 def Normalize(self, bool):
128 """
129 set or unset the normalization flag. Normalization needs to be done to
130 obtain proper gridding but may want to be disabled in certain cases
131 when sequential gridding is performed
132 """
133 if bool not in [False, True]:
134 raise TypeError("Normalize flag must be a boolan value "
135 "(True/False)!")
136 self.normalize = bool
137 if bool:
138 self.flags = utilities.clear_bit(self.flags, 2)
139 else:
140 self.flags = utilities.set_bit(self.flags, 2)
141
142 def KeepData(self, bool):
143 if bool not in [False, True]:
144 raise TypeError("Keep Data flag must be a boolan value"
145 "(True/False)!")
146
147 self.keep_data = bool
148
149 def __get_data(self):
150 """
151 return gridded data (performs normalization if switched on)
152 """
153 if self.normalize:
154 tmp = numpy.copy(self._gdata)
155 mask = (self._gnorm != 0)
156 tmp[mask] /= self._gnorm[mask].astype(numpy.double)
157 return tmp
158 else:
159 return self._gdata.copy()
160
161 data = property(__get_data)
162
163 def _prepare_array(self, a):
164 """
165 prepare array for passing to c-code
166 """
167 if isinstance(a, (list, tuple, numpy.float, numpy.int)):
168 a = numpy.asarray(a)
169 return a.reshape(a.size)
170
171 def Clear(self):
172 """
173 Clear so far gridded data to reuse this instance of the Gridder
174 """
175 self._gdata[...] = 0
176 self._gnorm[...] = 0
177
178
179 class Gridder1D(Gridder):
180
181 def __init__(self, nx):
182 Gridder.__init__(self)
183 if nx <= 0:
184 raise InputError('nx must be a positiv integer!')
185
186 self.nx = nx
187 self.xmin = 0
188 self.xmax = 0
189 self._gdata = numpy.zeros(nx, dtype=numpy.double)
190 self._gnorm = numpy.zeros(nx, dtype=numpy.double)
191
192 def savetxt(self, filename, header=''):
193 """
194 save gridded data to a txt file with two columns. The first column is
195 the data coordinate and the second the corresponding data value
196
197 Parameters
198 ----------
199 filename : str
200 output filename
201 header : str, optional
202 optional header for the data file.
203 """
204 numpy.savetxt(filename, numpy.vstack((self.xaxis, self.data)).T,
205 header=header, fmt='%.6g %.4g')
206
207 def __get_xaxis(self):
208 """
209 Returns the xaxis of the gridder
210 the returned values correspond to the center of the data bins used by
211 the gridding algorithm
212 """
213 return axis(self.xmin, self.xmax, self.nx)
214
215 xaxis = property(__get_xaxis)
216
217 def dataRange(self, min, max, fixed=True):
218 """
219 define minimum and maximum data range, usually this is deduced
220 from the given data automatically, however, for sequential
221 gridding it is useful to set this before the first call of the
222 gridder. data outside the range are simply ignored
223
224 Parameters
225 ----------
226 min : float
227 minimum value of the gridding range
228 max : float
229 maximum value of the gridding range
230 fixed : bool, optional
231 flag to turn fixed range gridding on (True (default)) or off
232 (False)
233 """
234 self.fixed_range = fixed
235 self.xmin = min
236 self.xmax = max
237
238 def _checktransinput(self, x, data):
239 """
240 common checks and reshape commands for the input data. This function
241 checks the data type and shape of the input data.
242 """
243 if not self.keep_data:
244 self.Clear()
245
246 x = self._prepare_array(x)
247 data = self._prepare_array(data)
248
249 if x.size != data.size:
250 raise InputError("XU.%s: size of given datasets (x, data)"
251 " is not equal!" % self.__class__.__name__)
252
253 if not self.fixed_range:
254 # assume that with setting keep_data the user wants to call the
255 # gridder more often and obtain a reasonable result
256 self.dataRange(x.min(), x.max(), self.keep_data)
257
258 return x, data
259
260 def __call__(self, x, data):
261 """
262 Perform gridding on a set of data. After running the gridder
263 the 'data' object in the class is holding the gridded data.
264
265 Parameters
266 ----------
267 x : ndarray
268 numpy array of arbitrary shape with x positions
269 data : ndarray
270 numpy array of arbitrary shape with data values
271 """
272 x, data = self._checktransinput(x, data)
273 # remove normalize flag for C-code, normalization is always performed
274 # in python
275 flags = utilities.set_bit(self.flags, 2)
276 cxrayutilities.gridder1d(x, data, self.nx, self.xmin, self.xmax,
277 self._gdata, self._gnorm, flags)
278
279
280 class FuzzyGridder1D(Gridder1D):
281 """
282 An 1D binning class considering every data point to have a finite width.
283 If necessary one data point will be split fractionally over different
284 data bins. This is numerically more effort but represents better the
285 typical case of a experimental data, which do not represent a mathematical
286 point but have a finite width (e.g. X-ray data from a 1D detector).
287 """
288
289 def __call__(self, x, data, width=None):
290 """
291 Perform gridding on a set of data. After running the gridder
292 the 'data' object in the class is holding the gridded data.
293
294 Parameters
295 ----------
296 x : ndarray
297 numpy array of arbitrary shape with x positions
298 data : ndarray
299 numpy array of arbitrary shape with data values
300 width : float, optional
301 width of one data point. If not given half the bin size will be
302 used.
303 """
304 x, data = self._checktransinput(x, data)
305
306 if not width:
307 width = delta(self.xmin, self.xmax, self.nx) / 2.
308
309 # remove normalize flag for C-code, normalization is always performed
310 # in python
311 flags = utilities.set_bit(self.flags, 2)
312 cxrayutilities.fuzzygridder1d(x, data, self.nx, self.xmin, self.xmax,
313 self._gdata, self._gnorm, width, flags)
314
315
316 class npyGridder1D(Gridder1D):
317
318 def __get_xaxis(self):
319 """
320 Returns the xaxis of the gridder
321 the returned values correspond to the center of the data bins used by
322 the numpy.histogram function
323 """
324 # no -1 here to be consistent with numpy.histogram
325 dx = (float(self.xmax - self.xmin)) / float(self.nx)
326 ax = self.xmin + dx * numpy.arange(0, self.nx) + dx / 2.
327 return ax
328
329 xaxis = property(__get_xaxis)
330
331 def __call__(self, x, data):
332 """
333 Perform gridding on a set of data. After running the gridder
334 the 'data' object in the class is holding the gridded data.
335
336 Parameters
337 ----------
338 x : ndarray
339 numpy array of arbitrary shape with x positions
340 data : ndarray
341 numpy array of arbitrary shape with data values
342 """
343
344 x, data = self._checktransinput(x, data)
345
346 # use only non-NaN data values
347 mask = numpy.invert(numpy.isnan(data))
348 ldata = data[mask]
349 lx = x[mask]
350
351 if not self.fixed_range:
352 # assume that with setting keep_data the user wants to call the
353 # gridder more often and obtain a reasonable result
354 self.dataRange(lx.min(), lx.max(), self.keep_data)
355
356 # grid the data using numpy histogram
357 tmpgdata, bins = numpy.histogram(lx, weights=ldata, bins=self.nx,
358 range=(self.xmin, self.xmax))
359 tmpgnorm, bins = numpy.histogram(lx, bins=self.nx,
360 range=(self.xmin, self.xmax))
361 if self.keep_data:
362 self._gnorm += tmpgnorm
363 self._gdata += tmpgdata
364 else:
365 self._gnorm = tmpgnorm
366 self._gdata = tmpgdata
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010, 2013
16 # Eugen Wintersberger <eugen.wintersberger@desy.de>
17 # Copyright (C) 2009 Mario Keplinger <mario.keplinger@jku.at>
18 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
19
20 import numpy
21
22 from . import cxrayutilities, exception, utilities
23 from .gridder import Gridder, axis, delta, ones
24
25
26 class Gridder2D(Gridder):
27
28 def __init__(self, nx, ny):
29 Gridder.__init__(self)
30
31 # check input
32 if nx <= 0 or ny <= 0:
33 raise exception.InputError('Neither nx nor ny can be smaller'
34 'than 1!')
35
36 self.xmin = None
37 self.ymin = None
38 self.xmax = None
39 self.ymax = None
40
41 self.nx = nx
42 self.ny = ny
43
44 self._allocate_memory()
45
46 def _allocate_memory(self):
47 """
48 Class method to allocate memory for the gridder based on the nx, ny
49 class attributes.
50 """
51
52 self._gdata = numpy.zeros((self.nx, self.ny), dtype=numpy.double)
53 self._gnorm = numpy.zeros((self.nx, self.ny), dtype=numpy.double)
54
55 def savetxt(self, filename, header=''):
56 """
57 save gridded data to a txt file with two columns. The first two columns
58 are the data coordinates and the last one the corresponding data
59 value.
60
61 Parameters
62 ----------
63 filename : str
64 output filename
65 header : str, optional
66 optional header for the data file.
67 """
68 numpy.savetxt(filename, numpy.vstack((self.xmatrix.flat,
69 self.ymatrix.flat,
70 self.data.flat)).T,
71 header=header, fmt='%.6g %.6g %.4g')
72
73 def SetResolution(self, nx, ny):
74 """
75 Reset the resolution of the gridder. In this case the original data
76 stored in the object will be deleted.
77
78 Parameters
79 ----------
80 nx : int
81 number of points in x-direction
82 ny : int
83 number of points in y-direction
84 """
85 self.nx = nx
86 self.ny = ny
87
88 self._allocate_memory()
89
90 def __get_xaxis(self):
91 return axis(self.xmin, self.xmax, self.nx)
92
93 def __get_yaxis(self):
94 return axis(self.ymin, self.ymax, self.ny)
95
96 def __get_xmatrix(self):
97 return ones(self.nx, self.ny) * self.xaxis[:, numpy.newaxis]
98
99 def __get_ymatrix(self):
100 return ones(self.nx, self.ny) * self.yaxis[numpy.newaxis, :]
101
102 yaxis = property(__get_yaxis)
103 xaxis = property(__get_xaxis)
104 xmatrix = property(__get_xmatrix)
105 ymatrix = property(__get_ymatrix)
106
107 def dataRange(self, xmin, xmax, ymin, ymax, fixed=True):
108 """
109 define minimum and maximum data range, usually this is deduced
110 from the given data automatically, however, for sequential
111 gridding it is useful to set this before the first call of the
112 gridder. data outside the range are simply ignored
113
114 Parameters
115 ----------
116 xmin, ymin : float
117 minimum value of the gridding range in x, y
118 xmax, ymax : float
119 maximum value of the gridding range in x, y
120 fixed : bool, optional
121 flag to turn fixed range gridding on (True (default)) or off
122 (False)
123 """
124 self.fixed_range = fixed
125 self.xmin = xmin
126 self.xmax = xmax
127 self.ymin = ymin
128 self.ymax = ymax
129
130 def _checktransinput(self, x, y, data):
131 """
132 common checks and reshape commands for the input data. This function
133 checks the data type and shape of the input data.
134 """
135 if not self.keep_data:
136 self.Clear()
137
138 x = self._prepare_array(x)
139 y = self._prepare_array(y)
140 data = self._prepare_array(data)
141
142 if x.size != y.size or y.size != data.size:
143 raise exception.InputError("XU.%s: size of given datasets "
144 "(x, y, data) is not equal!"
145 % self.__class__.__name__)
146
147 if not self.fixed_range:
148 # assume that with setting keep_data the user wants to call the
149 # gridder more often and obtain a reasonable result
150 self.dataRange(x.min(), x.max(), y.min(), y.max(), self.keep_data)
151
152 return x, y, data
153
154 def __call__(self, x, y, data):
155 """
156 Perform gridding on a set of data. After running the gridder
157 the 'data' object in the class is holding the gridded data.
158
159 Parameters
160 ----------
161 x : ndarray
162 numpy array of arbitrary shape with x positions
163 y : ndarray
164 numpy array of arbitrary shape with y positions
165 data : ndarray
166 numpy array of arbitrary shape with data values
167 """
168 x, y, data = self._checktransinput(x, y, data)
169 # remove normalize flag for C-code
170 flags = utilities.set_bit(self.flags, 2)
171 cxrayutilities.gridder2d(x, y, data, self.nx, self.ny,
172 self.xmin, self.xmax,
173 self.ymin, self.ymax,
174 self._gdata, self._gnorm, flags)
175
176
177 class FuzzyGridder2D(Gridder2D):
178 """
179 An 2D binning class considering every data point to have a finite area.
180 If necessary one data point will be split fractionally over different
181 data bins. This is numerically more effort but represents better the
182 typical case of a experimental data, which do not represent a mathematical
183 point but have a finite size (e.g. X-ray data from a 2D detector or
184 reciprocal space maps measured with point/linear detector).
185
186 Currently only a rectangular area can be considered during the gridding.
187 """
188
189 def __call__(self, x, y, data, **kwargs):
190 """
191 Perform gridding on a set of data. After running the gridder
192 the 'data' object in the class is holding the gridded data.
193
194 Parameters
195 ----------
196 x : ndarray
197 numpy array of arbitrary shape with x positions
198 y : ndarray
199 numpy array of arbitrary shape with y positions
200 data : ndarray
201 numpy array of arbitrary shape with data values
202 width : float or tuple or list, optional
203 width of one data point. If not given half the bin size will be
204 used. The width can be given as scalar if it is equal for both data
205 dimensions, or as sequence of length 2.
206 """
207
208 valid_kwargs = {'width': 'specifiying fuzzy data size'}
209 utilities.check_kwargs(kwargs, valid_kwargs,
210 self.__class__.__name__)
211
212 x, y, data = self._checktransinput(x, y, data)
213
214 if 'width' in kwargs:
215 try:
216 length = len(kwargs['width'])
217 except TypeError:
218 length = 1
219 if length == 2:
220 wx, wy = kwargs['width']
221 else:
222 wx = kwargs['width']
223 wy = wx
224 else:
225 wx = delta(self.xmin, self.xmax, self.nx) / 2.
226 wy = delta(self.ymin, self.ymax, self.ny) / 2.
227 # remove normalize flag for C-code
228 flags = utilities.set_bit(self.flags, 2)
229 cxrayutilities.fuzzygridder2d(x, y, data, self.nx, self.ny,
230 self.xmin, self.xmax,
231 self.ymin, self.ymax,
232 self._gdata, self._gnorm, wx, wy, flags)
233
234
235 class Gridder2DList(Gridder2D):
236
237 """
238 special version of a 2D gridder which performs no actual averaging of the
239 data in one grid/bin but just collects the data-objects belonging to one
240 bin for further treatment by the user
241 """
242
243 def _allocate_memory(self):
244 """
245 Class method to allocate memory for the gridder based on the nx, ny
246 class attributes.
247 """
248
249 self._gdata = numpy.empty((self.nx, self.ny), dtype=list)
250 for i in range(self.nx):
251 for j in range(self.ny):
252 self._gdata[i, j] = []
253 self._gnorm = numpy.zeros((self.nx, self.ny), dtype=numpy.int)
254
255 def Clear(self):
256 self._allocate_memory()
257
258 def __get_data(self):
259 """
260 return gridded data, in this special version no normalization is
261 defined!
262 """
263 return self._gdata.copy()
264
265 data = property(__get_data)
266
267 def __call__(self, x, y, data):
268 """
269 Perform gridding on a set of data. After running the gridder the 'data'
270 object in the class is holding the lists of data-objects belonging to
271 one bin/grid-point.
272
273 Parameters
274 ----------
275 x : ndarray
276 numpy array of arbitrary shape with x positions
277 y : ndarray
278 numpy array of arbitrary shape with y positions
279 data : ndarray, list or tuple
280 data of same length as x, y but of arbitrary type
281 """
282
283 x, y, data = self._checktransinput(x, y, data)
284
285 # perform gridding this should be moved to native code if possible
286 def gindex(x, min, delt):
287 return numpy.round((x - min) / delt).astype(numpy.int)
288
289 xdelta = delta(self.xmin, self.xmax, self.nx)
290 ydelta = delta(self.ymin, self.ymax, self.ny)
291
292 for i in range(len(x)):
293 xidx = gindex(x[i], self.xmin, xdelta)
294 yidx = gindex(y[i], self.ymin, ydelta)
295 self._gdata[xidx, yidx].append(data[i])
296 self._gnorm[xidx, yidx] += 1
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010, 2013
16 # Eugen Wintersberger <eugen.wintersberger@desy.de>
17 # Copyright (C) 2009 Mario Keplinger <mario.keplinger@jku.at>
18 # Copyright (C) 2009-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
19
20 import numpy
21
22 from . import cxrayutilities, exception, utilities
23 from .gridder import Gridder, axis, delta, ones
24
25
26 class Gridder3D(Gridder):
27
28 def __init__(self, nx, ny, nz):
29 Gridder.__init__(self)
30
31 # check input
32 if nx <= 0 or ny <= 0 or nz <= 0:
33 raise exception.InputError('None of nx, ny and nz can be smaller '
34 'than 1!')
35
36 self.xmin = 0
37 self.xmax = 0
38 self.ymin = 0
39 self.ymax = 0
40 self.zmin = 0
41 self.zmax = 0
42
43 self.nx = nx
44 self.nz = nz
45 self.ny = ny
46
47 self._allocate_memory()
48
49 def _allocate_memory(self):
50 """
51 Class method to allocate memory for the gridder based on the nx, ny
52 class attributes.
53 """
54 self._gdata = numpy.zeros((self.nx, self.ny, self.nz),
55 dtype=numpy.double)
56 self._gnorm = numpy.zeros((self.nx, self.ny, self.nz),
57 dtype=numpy.double)
58
59 def SetResolution(self, nx, ny, nz):
60 self.nx = nx
61 self.ny = ny
62 self.nz = nz
63
64 self._allocate_memory()
65
66 def __get_xaxis(self):
67 return axis(self.xmin, self.xmax, self.nx)
68
69 def __get_yaxis(self):
70 return axis(self.ymin, self.ymax, self.ny)
71
72 def __get_zaxis(self):
73 return axis(self.zmin, self.zmax, self.nz)
74
75 def __get_xmatrix(self):
76 return ones(self.nx, self.ny, self.nz) *\
77 self.xaxis[:, numpy.newaxis, numpy.newaxis]
78
79 def __get_ymatrix(self):
80 return ones(self.nx, self.ny, self.nz) *\
81 self.yaxis[numpy.newaxis, :, numpy.newaxis]
82
83 def __get_zmatrix(self):
84 return ones(self.nx, self.ny, self.nz) *\
85 self.zaxis[numpy.newaxis, numpy.newaxis, :]
86
87 zaxis = property(__get_zaxis)
88 zmatrix = property(__get_zmatrix)
89 xaxis = property(__get_xaxis)
90 xmatrix = property(__get_xmatrix)
91 yaxis = property(__get_yaxis)
92 ymatrix = property(__get_ymatrix)
93
94 def dataRange(self, xmin, xmax, ymin, ymax, zmin, zmax, fixed=True):
95 """
96 define minimum and maximum data range, usually this is deduced
97 from the given data automatically, however, for sequential
98 gridding it is useful to set this before the first call of the
99 gridder. data outside the range are simply ignored
100
101 Parameters
102 ----------
103 xmin, ymin, zmin : float
104 minimum value of the gridding range in x, y, z
105 xmax, ymax, zmax : float
106 maximum value of the gridding range in x, y, z
107 fixed : bool, optional
108 flag to turn fixed range gridding on (True (default)) or off
109 (False)
110 """
111 self.fixed_range = fixed
112 self.xmin = xmin
113 self.xmax = xmax
114 self.ymin = ymin
115 self.ymax = ymax
116 self.zmin = zmin
117 self.zmax = zmax
118
119 def _checktransinput(self, x, y, z, data):
120 """
121 common checks and reshape commands for the input data. This function
122 checks the data type and shape of the input data.
123 """
124 if not self.keep_data:
125 self.Clear()
126
127 x = self._prepare_array(x)
128 y = self._prepare_array(y)
129 z = self._prepare_array(z)
130 data = self._prepare_array(data)
131
132 if x.size != y.size or y.size != z.size or z.size != data.size:
133 raise exception.InputError("XU.%s: size of given datasets "
134 "(x, y, z, data) is not equal!"
135 % self.__class__.__name__)
136
137 if not self.fixed_range:
138 # assume that with setting keep_data the user wants to call the
139 # gridder more often and obtain a reasonable result
140 self.dataRange(x.min(), x.max(),
141 y.min(), y.max(),
142 z.min(), z.max(),
143 self.keep_data)
144
145 return x, y, z, data
146
147 def __call__(self, x, y, z, data):
148 """
149 Perform gridding on a set of data. After running the gridder
150 the 'data' object in the class is holding the gridded data.
151
152 Parameters
153 ----------
154 x : ndarray
155 numpy array of arbitrary shape with x positions
156 y : ndarray
157 numpy array of arbitrary shape with y positions
158 z : ndarray
159 numpy array fo arbitrary shape with z positions
160 data : ndarray
161 numpy array of arbitrary shape with data values
162 """
163
164 x, y, z, data = self._checktransinput(x, y, z, data)
165
166 # remove normalize flag for C-code
167 flags = utilities.set_bit(self.flags, 2)
168 cxrayutilities.gridder3d(x, y, z, data, self.nx, self.ny, self.nz,
169 self.xmin, self.xmax,
170 self.ymin, self.ymax,
171 self.zmin, self.zmax,
172 self._gdata, self._gnorm, flags)
173
174
175 class FuzzyGridder3D(Gridder3D):
176 """
177 An 3D binning class considering every data point to have a finite volume.
178 If necessary one data point will be split fractionally over different
179 data bins. This is numerically more effort but represents better the
180 typical case of a experimental data, which do not represent a mathematical
181 point but have a finite size.
182
183 Currently only a quader can be considered as volume during the gridding.
184 """
185
186 def __call__(self, x, y, z, data, **kwargs):
187 """
188 Perform gridding on a set of data. After running the gridder
189 the 'data' object in the class is holding the gridded data.
190
191 Parameters
192 ----------
193 x : ndarray
194 numpy array of arbitrary shape with x positions
195 y : ndarray
196 numpy array of arbitrary shape with y positions
197 z : ndarray
198 numpy array fo arbitrary shape with z positions
199 data : ndarray
200 numpy array of arbitrary shape with data values
201 width : float, tuple or list, optional
202 width of one data point. If not given half the bin size will be
203 used. The width can be given as scalar if it is equal for all three
204 dimensions, or as sequence of length 3.
205 """
206
207 valid_kwargs = {'width': 'specifiying fuzzy data size'}
208 utilities.check_kwargs(kwargs, valid_kwargs,
209 self.__class__.__name__)
210
211 x, y, z, data = self._checktransinput(x, y, z, data)
212
213 if 'width' in kwargs:
214 try:
215 length = len(kwargs['width'])
216 except TypeError:
217 length = 1
218 if length == 3:
219 wx, wy, wz = kwargs['width']
220 else:
221 wx = kwargs['width']
222 wy = wx
223 wz = wx
224 else:
225 wx = delta(self.xmin, self.xmax, self.nx) / 2.
226 wy = delta(self.ymin, self.ymax, self.ny) / 2.
227 wz = delta(self.zmin, self.zmax, self.nz) / 2.
228
229 # remove normalize flag for C-code
230 flags = utilities.set_bit(self.flags, 2)
231 cxrayutilities.fuzzygridder3d(x, y, z, data, self.nx, self.ny, self.nz,
232 self.xmin, self.xmax,
233 self.ymin, self.ymax,
234 self.zmin, self.zmax,
235 self._gdata, self._gnorm,
236 wx, wy, wz, flags)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 from .cbf import CBFDirectory, CBFFile
19 from .desy_tty08 import gettty08_scan, tty08File
20 from .edf import EDFDirectory, EDFFile
21 from .fastscan import FastScan, FastScanCCD, FastScanSeries
22 from .helper import xu_h5open, xu_open
23 from .ill_numor import numor_scan, numorFile
24 from .imagereader import (ImageReader, PerkinElmer, Pilatus100K, RoperCCD,
25 TIFFRead, get_tiff)
26 from .panalytical_xml import XRDMLFile, getxrdml_map, getxrdml_scan
27 from .pdcif import pdCIF, pdESG
28 from .rigaku_ras import RASFile, RASScan, getras_scan
29 # parser for the alignment log file of the rotating anode
30 from .rotanode_alignment import RA_Alignment
31 from .seifert import SeifertMultiScan, SeifertScan, getSeifert_map
32 from .spec import SPECFile, SPECLog, SPECScan, geth5_scan, getspec_scan
33 from .spectra import SPECTRAFile, geth5_spectra_map
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2013, 2015 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 # module for handling files stored in the CBF data format
18
19 import os.path
20 import re
21
22 import numpy
23
24 from .. import config, cxrayutilities, utilities
25 from .filedir import FileDirectory
26 from .helper import xu_h5open, xu_open
27
28 cbf_name_start_num = re.compile(r"^\d")
29
30
31 class CBFFile(object):
32
33 def __init__(self, fname, nxkey="X-Binary-Size-Fastest-Dimension",
34 nykey="X-Binary-Size-Second-Dimension",
35 dtkey="DataType", path=None):
36 """
37 CBF detector image parser
38
39 Parameters
40 ----------
41 fname : str
42 name of the CBF file of type .cbf or .cbf.gz
43 nxkey : str, optional
44 name of the header key that holds the number of points in
45 x-direction
46 nykey : str, optional
47 name of the header key that holds the number of points in
48 y-direction
49 dtkey : str, optional
50 name of the header key that holds the datatype for the binary data
51 path : str, optional
52 path to the CBF file
53 """
54
55 self.filename = fname
56 if path:
57 self.full_filename = os.path.join(path, fname)
58 else:
59 self.full_filename = self.filename
60
61 # evaluate keyword arguments
62 self.nxkey = nxkey
63 self.nykey = nykey
64 self.dtkey = dtkey
65
66 # create attributes for holding data
67 self.data = None
68 self.ReadData()
69
70 def ReadData(self):
71 """
72 Read the CCD data into the .data object
73 this function is called by the initialization
74 """
75 with xu_open(self.full_filename, 'rb') as fid:
76 tmp = numpy.fromfile(file=fid, dtype="u1").tostring()
77 tmp2 = tmp.decode('ascii', 'ignore')
78 # read header information
79 pos = tmp2.index(self.nxkey + ':') + len(self.nxkey + ':')
80 self.xdim = int(tmp2[pos:pos + 6].strip())
81 pos = tmp2.index(self.nykey + ':') + len(self.nykey + ':')
82 self.ydim = int(tmp2[pos:pos + 6].strip())
83
84 self.data = cxrayutilities.cbfread(tmp, self.xdim, self.ydim)
85 self.data.shape = (self.ydim, self.xdim)
86
87 def Save2HDF5(self, h5f, group="/", comp=True):
88 """
89 Saves the data stored in the EDF file in a HDF5 file as a HDF5 array.
90 By default the data is stored in the root group of the HDF5 file - this
91 can be changed by passing the name of a target group or a path to the
92 target group via the "group" keyword argument.
93
94 Parameters
95 ----------
96 h5f : file-handle or str
97 a HDF5 file object or name
98 group : str, optional
99 group where to store the data (default to the root of the file)
100 comp : bool, optional
101 activate compression - true by default
102 """
103 with xu_h5open(h5f, 'a') as h5:
104 if isinstance(group, str):
105 g = h5.get(group)
106 else:
107 g = group
108
109 # create the array name
110 name = os.path.split(self.filename)[-1]
111 name = os.path.splitext(name)[0]
112 # perform a second time for case of .cbf.gz files
113 name = os.path.splitext(name)[0]
114 name = utilities.makeNaturalName(name)
115 if cbf_name_start_num.match(name):
116 name = "ccd_" + name
117 if config.VERBOSITY >= config.INFO_ALL:
118 print("xu.io.CBFFile: HDF5 group name: %s" % name)
119
120 # create the array description
121 desc = "CBF CCD data from file %s " % (self.filename)
122
123 # create the dataset for the array
124 kwds = {'fletcher32': True}
125 if comp:
126 kwds['compression'] = 'gzip'
127
128 try:
129 ca = g.create_dataset(name, data=self.data, **kwds)
130 except ValueError:
131 del g[name]
132 ca = g.create_dataset(name, data=self.data, **kwds)
133
134 ca.attrs['TITLE'] = desc
135
136
137 class CBFDirectory(FileDirectory):
138
139 """
140 Parses a directory for CBF files, which can be stored to a HDF5 file for
141 further usage
142 """
143
144 def __init__(self, datapath, ext="cbf", **keyargs):
145 """
146 Parameters
147 ----------
148 datapath : str
149 directory of the CBF files
150 ext : str, optional
151 extension of the ccd files in the datapath (default: "cbf")
152 keyargs : dict, optional
153 further keyword arguments are passed to CBFFile
154 """
155 super().__init__(datapath, ext, CBFFile, **keyargs)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2013-2014 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17
18 """
19 class for reading data + header information from tty08 data files
20
21 tty08 is a system used at beamline P08 at Hasylab Hamburg and creates simple
22 ASCII files to save the data. Information is easily read from the multicolumn
23 data file. the functions below enable also to parse the information of the
24 header
25 """
26
27 import glob
28 import os.path
29 import re
30
31 import numpy
32 import numpy.lib.recfunctions
33
34 from ..exception import InputError
35 # relative imports from xrayutilities
36 from .helper import xu_open
37
38 re_columns = re.compile(r"/\*H")
39 re_command = re.compile(r"^/\*C command")
40 re_comment = re.compile(r"^/\*")
41 re_date = re.compile(r"^/\*D date")
42 re_epoch = re.compile(r"^/\*T epoch")
43 re_initmopo = re.compile(r"^/\*M")
44
45
46 class tty08File(object):
47
48 """
49 Represents a tty08 data file. The file is read during the
50 Constructor call. This class should work for data stored at
51 beamline P08 using the tty08 acquisition system.
52
53 Parameters
54 ----------
55 filename : str
56 tty08-filename
57 mcadir : str, optional
58 directory name of MCA files
59 """
60
61 def __init__(self, filename, path=None, mcadir=None):
62 self.filename = filename
63 if path is None:
64 self.full_filename = self.filename
65 else:
66 self.full_filename = os.path.join(path, self.filename)
67
68 self.Read()
69
70 if mcadir is not None:
71 self.mca_directory = mcadir
72 self.mca_files = sorted(glob.glob(
73 os.path.join(self.mca_directory, '*')))
74
75 if self.mca_files:
76 self.ReadMCA()
77
78 def ReadMCA(self):
79 self.mca = numpy.empty((len(self.mca_files),
80 numpy.loadtxt(self.mca_files[0]).shape[0]),
81 dtype=numpy.float)
82 for i in range(len(self.mca_files)):
83 mcadata = numpy.loadtxt(self.mca_files[i])
84
85 self.mca[i, :] = mcadata[:, 1]
86
87 if i == 0:
88 if len(mcadata.shape) == 2:
89 self.mca_channels = mcadata[:, 0]
90 else:
91 self.mca_channels = numpy.arange(0, mcadata.shape[0])
92
93 mcatemp = self.mca.view([('MCA',
94 (self.mca.dtype, self.mca.shape[1]))])
95 self.data = numpy.lib.recfunctions.merge_arrays([self.data, mcatemp],
96 flatten=True)
97
98 def Read(self):
99 """
100 Read the data from the file
101 """
102
103 with xu_open(self.full_filename) as fid:
104 # read header
105 self.init_mopo = {}
106 for line in fid:
107 line = line.decode('ascii')
108
109 if re_command.match(line):
110 m = line.split(':')
111 self.scan_command = m[1].strip()
112 if re_date.match(line):
113 m = line.split(':', 1)
114 self.scan_date = m[1].strip()
115 if re_epoch.match(line):
116 m = line.split(':', 1)
117 self.epoch = float(m[1])
118 if re_initmopo.match(line):
119 m = line[3:]
120 m = m.split(';')
121 for e in m:
122 e = e.split('=')
123 self.init_mopo[e[0].strip()] = float(e[1])
124
125 if re_columns.match(line):
126 self.columns = tuple(line.split()[1:])
127 # here all necessary information is read and we can start
128 # reading the data
129 break
130 self.data = numpy.loadtxt(fid, comments="/")
131
132 self.data = numpy.rec.fromrecords(self.data, names=self.columns)
133
134
135 def gettty08_scan(scanname, scannumbers, *args, **keyargs):
136 """
137 function to obtain the angular cooridinates as well as intensity values
138 saved in TTY08 datafiles. Especially usefull for reciprocal space map
139 measurements, and to combine date from several scans
140
141 further more it is possible to obtain even more positions from
142 the data file if more than two string arguments with its names are given
143
144 Parameters
145 ----------
146 scanname : str
147 name of the scans, for multiple scans this needs to be a template
148 string
149 scannumbers : int, tuple or list
150 number of the scans of the reciprocal space map
151
152 args : str, optional
153 names of the motors. to read reciprocal space maps measured in coplanar
154 diffraction give:
155
156 - `omname`: the name of the omega motor (or its equivalent)
157 - `ttname`: the name of the two theta motor (or its equivalent)
158
159 keyargs : dict, optional
160 keyword arguments are passed on to tty08File
161
162 Returns
163 -------
164 [ang1, ang2, ...] : list, optional
165 angular positions of the center channel of the position sensitive
166 detector (numpy.ndarray 1D), omitted if no `args` are given
167 MAP : ndarray
168 All the data values as stored in the data file (includes the
169 intensities e.g. MAP['MCA']).
170
171 Examples
172 --------
173 >>> [om, tt], MAP = xu.io.gettty08_scan('text%05d.dat', 36, 'omega',
174 >>> 'gamma')
175 """
176
177 if isinstance(scannumbers, (list, tuple)):
178 scanlist = scannumbers
179 else:
180 scanlist = list([scannumbers])
181
182 angles = dict.fromkeys(args)
183 for key in angles:
184 if not isinstance(key, str):
185 raise InputError("*arg values need to be strings with motornames")
186 angles[key] = numpy.zeros(0)
187 buf = numpy.zeros(0)
188 MAP = numpy.zeros(0)
189
190 for nr in scanlist:
191 scan = tty08File(scanname % nr, **keyargs)
192 sdata = scan.data
193 if MAP.dtype == numpy.float64:
194 MAP.dtype = sdata.dtype
195 # append scan data to MAP, where all data are stored
196 MAP = numpy.append(MAP, sdata)
197 # check type of scan
198 for i in range(len(args)):
199 motname = args[i]
200 scanlength = len(sdata)
201 try:
202 buf = sdata[motname]
203 except ValueError:
204 buf = scan.init_mopo[motname] * numpy.ones(scanlength)
205 angles[motname] = numpy.concatenate((angles[motname], buf))
206
207 retval = []
208 for motname in args:
209 # create return values in correct order
210 retval.append(angles[motname])
211
212 if not args:
213 return MAP
214 elif len(args) == 1:
215 return retval[0], MAP
216 else:
217 return retval, MAP
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2010-2012,2014-2015
17 # Dominik Kriegner <dominik.kriegner@gmail.com>
18 # Copyright (C) 2012 Tanja Etzelstorfer <tanja.etzelstorfer@jku.at>
19
20 # module for handling files stored in the EDF data format developed by the ESRF
21
22 import os.path
23 import re
24 import struct
25
26 import numpy
27
28 from .. import config, utilities
29 from .filedir import FileDirectory
30 from .helper import xu_h5open, xu_open
31
32 edf_kv_split = re.compile(r"\s*=\s*") # key value sepeartor for header data
33 edf_eokv = re.compile(r";") # end of line for a header
34 # regular expressions for several ASCII representations of numbers
35 edf_integer_value = re.compile(r"\d+")
36 edf_float_value = re.compile(r"[+-]*\d+\.*\d*")
37 edf_float_e_value = re.compile(r"[+-]*\d+\.\d*e[+-]*\d*")
38 edf_name_start_num = re.compile(r"^\d")
39
40 # dictionary mapping EDF data type keywords onto struct data types
41 DataTypeDict = {"SignedByte": "b",
42 "SignedShort": "h",
43 "SignedInteger": "i",
44 "SignedLong": "i",
45 "FloatValue": "f",
46 "DoubleValue": "d",
47 "UnsignedByte": "B",
48 "UnsignedShort": "H",
49 "UnsignedInt": "I",
50 "UnsignedLong": "L"}
51 # SignedLong is only 4byte, on my 64bit machine using SignedLong:"l" caused
52 # troubles
53 # UnsignedLong is only 4byte, on my 64bit machine using UnsignedLong:"L"
54 # caused troubles ("I" works)
55
56
57 class EDFFile(object):
58
59 def __init__(self, fname, nxkey="Dim_1", nykey="Dim_2",
60 dtkey="DataType", path="", header=True, keep_open=False):
61 """
62 Parameters
63 ----------
64 fname : str
65 name of the EDF file of type .edf or .edf.gz
66
67 nxkey : str, optional
68 name of the header key that holds the number of points in
69 x-direction
70 nykey : str, optional
71 name of the header key that holds the number of points in
72 y-direction
73 dtkey : str, optional
74 name of the header key that holds the datatype for the binary data
75 path : str, optional
76 path to the EDF file
77 header : bool, optional
78 has header (default true)
79 keep_open : bool, optional
80 if True the file handle is kept open between multiple calls which
81 can cause significant speed-ups
82 """
83
84 self.filename = fname
85 self.full_filename = os.path.join(path, fname)
86
87 # evaluate keyword arguments
88 self.nxkey = nxkey
89 self.nykey = nykey
90 self.dtkey = dtkey
91 self.headerflag = header
92
93 # create attributes for holding data
94 self._data = {}
95 self._headers = []
96 self._data_offsets = []
97 self._data_read = False
98 self._dimx = []
99 self._dimy = []
100 self._byte_order = []
101 self._fmt_str = []
102 self._dtype = []
103
104 self.Parse()
105 if keep_open:
106 self.fid = xu_open(self.full_filename, 'rb')
107 else:
108 self.fid = None
109
110 self.nimages = len(self._data_offsets)
111 self.header = self._headers[0]
112
113 def Parse(self):
114 """
115 Parse file to find the number of entries and read the respective
116 header information
117 """
118 header = {}
119 offset = 0
120
121 with xu_open(self.full_filename, 'rb') as fid:
122 if config.VERBOSITY >= config.INFO_ALL:
123 print("XU.io.EDFFile.Parse: file: %s" % self.full_filename)
124
125 if self.headerflag:
126 while True: # until end of file
127 hdr_flag = False
128 ml_value_flag = False # marks a multiline header
129 for line in fid: # until end of header
130 linelength = len(line)
131 offset += linelength
132 line = line.decode('ascii', 'ignore')
133 if config.VERBOSITY >= config.DEBUG:
134 print(line)
135 if line == "":
136 break
137 # remove leading and trailing whitespace symbols
138 line = line.strip()
139
140 if line == "{" and not hdr_flag:
141 # start with header
142 hdr_flag = True
143 header = {}
144 continue
145
146 if hdr_flag:
147 # stop reading when the end of the header
148 # is reached
149 if line == "}":
150 # place offset reading here - here we get the
151 # real starting position of the binary data!!
152 break
153
154 # continue if the line has no content
155 if line == "":
156 continue
157
158 # split key and value of the header entry
159 if not ml_value_flag:
160 try:
161 key, value = edf_kv_split.split(line, 1)
162 except ValueError:
163 print("XU.io.EDFFile.Parse: "
164 "line: %s" % line)
165
166 key = key.strip()
167 value = value.strip()
168
169 # if the value extends over multiple lines set
170 # the multiline value flag
171 if value[-1] != ";":
172 ml_value_flag = True
173 else:
174 value = value[:-1]
175 value = value.strip()
176 header[key] = value
177 else:
178 value = value + line
179 if value[-1] == ";":
180 ml_value_flag = False
181
182 value = value[:-1]
183 value = value.strip()
184 header[key] = value
185 else:
186 break
187 # append header to class variables
188 self._byte_order.append(header["ByteOrder"])
189 self._fmt_str.append(DataTypeDict[header[self.dtkey]])
190 self._dimx.append(int(header[self.nxkey]))
191 self._dimy.append(int(header[self.nykey]))
192 self._dtype.append(header[self.dtkey])
193
194 self._headers.append(header)
195 self._data_offsets.append(offset)
196 # jump over data block
197 tot_nofp = self._dimx[-1] * self._dimy[-1]
198 dsize = tot_nofp * struct.calcsize(self._fmt_str[-1])
199 fid.seek(offset + dsize, 0)
200 offset += dsize
201
202 else: # in case of no header also save one set of defaults
203 self._byte_order.append('LowByteFirst')
204 self._fmt_str.append(DataTypeDict['UnsignedShort'])
205 self._dimx.append(516)
206 self._dimy.append(516)
207 self._dtype.append('UnsignedShort')
208 self._headers.append(header)
209 self._data_offsets.append(offset)
210
211 # try to parse motor positions and counters from last found header
212 # into separate dictionary
213 if 'motor_mne' in header:
214 tkeys = header['motor_mne'].split()
215 try:
216 tval = numpy.array(header['motor_pos'].split(),
217 dtype=numpy.double)
218 self.motors = dict(zip(tkeys, tval))
219 except ValueError:
220 print("XU.io.EDFFile.ReadData: Warning: header conversion "
221 "of motor positions failed")
222
223 if 'counter_mne' in header:
224 tkeys = header['counter_mne'].split()
225 try:
226 tval = numpy.array(header['counter_pos'].split(),
227 dtype=numpy.double)
228 self.counters = dict(zip(tkeys, tval))
229 except ValueError:
230 print("XU.io.EDFFile.ReadData: Warning: header conversion "
231 "of counter values failed")
232
233 def ReadData(self, nimg=0):
234 """
235 Read the CCD data of the specified image and return the data
236 this function is called automatically when the 'data' property is
237 accessed, but can also be called manually when only a certain image
238 from the file is needed.
239
240 Parameters
241 ----------
242 nimg : int, optional
243 number of the image which should be read (starts with 0)
244 """
245 if self.fid:
246 binfid = self.fid
247 # move to the data section - jump over the header
248 binfid.seek(self._data_offsets[nimg], 0)
249 # read the data
250 tot_nofp = self._dimx[nimg] * self._dimy[nimg]
251 fmt_str = self._fmt_str[nimg]
252 bindata = binfid.read(tot_nofp * struct.calcsize(fmt_str))
253 else:
254 with xu_open(self.full_filename, 'rb') as binfid:
255 # move to the data section - jump over the header
256 binfid.seek(self._data_offsets[nimg], 0)
257 # read the data
258 tot_nofp = self._dimx[nimg] * self._dimy[nimg]
259 fmt_str = self._fmt_str[nimg]
260 bindata = binfid.read(tot_nofp * struct.calcsize(fmt_str))
261 if config.VERBOSITY >= config.DEBUG:
262 print("XU.io.EDFFile: read binary data: nofp: %d len: %d"
263 % (tot_nofp, len(bindata)))
264 print("XU.io.EDFFile: format: %s" % fmt_str)
265
266 try:
267 data = numpy.frombuffer(bindata, count=tot_nofp, dtype=fmt_str)
268 except ValueError:
269 if fmt_str == 'L':
270 fmt_str = 'I'
271 try:
272 data = numpy.frombuffer(bindata, count=tot_nofp,
273 dtype=fmt_str)
274 except ValueError:
275 raise IOError("XU.io.EDFFile: data format (%s) has "
276 "different byte-length, from amount of data "
277 "one expects %d bytes per entry"
278 % (fmt_str, len(bindata) / tot_nofp))
279 else:
280 raise IOError("XU.io.EDFFile: data format (%s) has different "
281 "byte-length, from amount of data one expects "
282 "%d bytes per entry"
283 % (fmt_str, len(bindata) / tot_nofp))
284
285 data.shape = (self._dimy[nimg], self._dimx[nimg])
286
287 if self._byte_order[nimg] != "LowByteFirst": # data = data.byteswap()
288 print("XU.io.EDFFile.ReadData: check byte order - "
289 "not low byte first")
290
291 return data
292
293 @property
294 def data(self):
295 if not self._data_read:
296 for i in range(self.nimages):
297 self._data[i] = self.ReadData(i)
298 self._data_read = True
299 if self.nimages == 1:
300 return self._data[0]
301 else:
302 return self._data
303
304 def Save2HDF5(self, h5f, group="/", comp=True):
305 """
306 Saves the data stored in the EDF file in a HDF5 file as a HDF5 array.
307 By default the data is stored in the root group of the HDF5 file - this
308 can be changed by passing the name of a target group or a path to the
309 target group via the "group" keyword argument.
310
311 Parameters
312 ----------
313 h5f : file-handle or str
314 a HDF5 file object or name
315 group : str, optional
316 group where to store the data (default to the root of the file)
317 comp : bool, optional
318 activate compression - true by default
319 """
320 with xu_h5open(h5f, 'a') as h5:
321 if isinstance(group, str):
322 if group == '/':
323 g = h5
324 else:
325 if group in h5:
326 del h5[group]
327 g = h5.create_group(group)
328 else:
329 g = group
330
331 # create the array name
332 ca_name = os.path.split(self.filename)[-1]
333 ca_name = os.path.splitext(ca_name)[0]
334 # perform a second time for case of .edf.gz files
335 ca_name = os.path.splitext(ca_name)[0]
336 ca_name = utilities.makeNaturalName(ca_name)
337 if edf_name_start_num.match(ca_name):
338 ca_name = "ccd_" + ca_name
339 if config.VERBOSITY >= config.INFO_ALL:
340 print(ca_name)
341
342 # create the array description
343 ca_desc = "EDF CCD data from file %s " % (self.filename)
344 kwds = {'fletcher32': True}
345 if comp:
346 kwds['compression'] = 'gzip'
347
348 if self.nimages != 1:
349 ca_name += '_{n:04d}'
350
351 for n in range(self.nimages):
352 d = self.ReadData(n)
353 name = ca_name.format(n=n)
354 try:
355 ca = g.create_dataset(name, data=d, **kwds)
356 except ValueError:
357 del g[name]
358 ca = g.create_dataset(name, data=d, **kwds)
359
360 ca.attrs['TITLE'] = ca_desc
361
362 # finally we have to append the attributes
363 for k in self.header:
364 ca.attrs[utilities.makeNaturalName(k)] = self.header[k]
365
366
367 class EDFDirectory(FileDirectory):
368
369 """
370 Parses a directory for EDF files, which can be stored to a HDF5 file for
371 further usage
372 """
373
374 def __init__(self, datapath, ext="edf", **keyargs):
375 """
376
377 Parameters
378 ----------
379 datapath : str
380 directory of the EDF file
381 ext : str, optional
382 extension of the ccd files in the datapath (default: "edf")
383 keyargs : dict, optional
384 further keyword arguments are passed to EDFFile
385 """
386 super().__init__(datapath, ext, EDFFile, **keyargs)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2014 Raphael Grifone <raphael.grifone@esrf.fr>
16 # Copyright (C) 2014-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 modules to help with the analysis of FastScan data acquired at the ESRF.
20 FastScan data are X-ray data (various detectors possible) acquired during
21 scanning the sample in real space with a Piezo Scanner. The same functions
22 might be used to analze traditional SPEC mesh scans.
23
24 The module provides three core classes:
25
26 * FastScan
27 * FastScanCCD
28 * FastScanSeries
29
30 where the first two are able to parse single mesh/FastScans when one is
31 interested in data of a single channel detector or are detector and the last
32 one is able to parse full series of such mesh scans with either type of
33 detector
34
35 see examples/xrayutilities_kmap_ESRF.py for an example script
36 """
37
38 import os.path
39 import re
40
41 import h5py
42 import numpy
43
44 from .. import config, utilities
45 from ..gridder import delta
46 from ..gridder2d import Gridder2D, Gridder2DList
47 from ..gridder3d import Gridder3D
48 from ..normalize import blockAverage2D
49 from .edf import EDFFile
50 from .spec import SPECFile
51
52
53 class FastScan(object):
54
55 """
56 class to help parsing and treating fast scan data. FastScan is the
57 aquisition of X-ray data while scanning the sample with piezo stages in
58 real space. It's is available at several beamlines at the ESRF synchrotron
59 light-source.
60 """
61
62 def __init__(self, filename, scannr,
63 xmotor='adcX', ymotor='adcY', path=""):
64 """
65 Constructor routine for the FastScan object. It initializes the object
66 and parses the spec-scan for the needed data which are saved in
67 properties of the FastScan object.
68
69 Parameters
70 ----------
71 filename : str
72 file name of the fast scan spec file
73 scannr : int
74 scannr of the to be parsed fast scan
75 xmotor : str, optional
76 motor name of the x-motor (default: 'adcX' (ID01))
77 ymotor : str, optional
78 motor name of the y-motor (default: 'adcY' (ID01))
79 path : str, optional
80 optional path of the FastScan spec file
81 """
82 self.scannr = scannr
83 self.xmotor = xmotor
84 self.ymotor = ymotor
85
86 if isinstance(filename, SPECFile):
87 self.specfile = filename
88 self.filename = self.specfile.filename
89 self.full_filename = self.specfile.full_filename
90 self.specscan = getattr(self.specfile, 'scan%d' % self.scannr)
91 else:
92 self.filename = filename
93 self.full_filename = os.path.join(path, filename)
94 self.filename = os.path.basename(self.full_filename)
95 self.specscan = None
96
97 # read the scan
98 self.parse()
99
100 def parse(self):
101 """
102 parse the specfile for the scan number specified in the constructor and
103 store the needed informations in the object properties
104 """
105
106 # parse the file
107 if not self.specscan:
108 self.specfile = SPECFile(self.full_filename)
109 self.specscan = getattr(self.specfile, 'scan%d' % self.scannr)
110 self.specscan.ReadData()
111
112 self.xvalues = self.specscan.data[self.xmotor]
113 self.yvalues = self.specscan.data[self.ymotor]
114
115 self.data = self.specscan.data
116
117 def motorposition(self, motorname):
118 """
119 read the position of motor with name given by motorname from the data
120 file. In case the motor is included in the data columns the returned
121 object is an array with all the values from the file (although retrace
122 clean is respected if already performed). In the case the motor is not
123 moved during the scan only one value is returned.
124
125 Parameters
126 ----------
127 motorname : str
128 name of the motor for which the position is wanted
129
130 Returns
131 -------
132 ndarray
133 motor position(s) of motor with name motorname during the scan
134 """
135 if self.specscan:
136 # try reading value from data
137 try:
138 return self.data[motorname]
139 except ValueError:
140 try:
141 return self.specscan.init_motor_pos['INIT_MOPO_%s'
142 % motorname]
143 except KeyError:
144 raise ValueError("given motorname '%s' not found in the "
145 "Spec-data" % motorname)
146 else:
147 return None
148
149 def retrace_clean(self):
150 """
151 function to clean the data of the scan from retrace artifacts created
152 by the zig-zag scanning motion of the piezo actuators the function
153 cleans the xvalues, yvalues and data attribute of the FastScan object.
154 """
155
156 # set window to determin the slope
157 window = [-1, 0, 1]
158 # calc the slope of x_motor movement using a window for better acuracy
159 slope = numpy.convolve(self.xvalues, window, mode='same') / \
160 numpy.convolve(numpy.arange(len(self.xvalues)), window, 'same')
161 # select where slope is above the slope mean value
162 # this can be modified if data points are missing of the retrace does
163 # not clean all points
164 mask = numpy.where(slope > slope.mean())
165
166 # reduce data size by cutting out retrace
167 self.xvalues = self.xvalues[mask]
168 self.yvalues = self.yvalues[mask]
169 self.data = self.data[mask]
170
171 def grid2D(self, nx, ny, **kwargs):
172 """
173 function to grid the counter data and return the gridded X, Y and
174 Intensity values.
175
176 Parameters
177 ----------
178 nx, ny : int
179 number of bins in x, and y direction
180 counter : str, optional
181 name of the counter to use for gridding (default: 'mpx4int' (ID01))
182 gridrange : tuple, optional
183 range for the gridder: format: ((xmin, xmax), (ymin, ymax))
184
185 Returns
186 -------
187 Gridder2D
188 Gridder2D object with X, Y, data on regular x, y-grid
189 """
190 self.counter = kwargs.get('counter', 'mpx4int')
191 gridrange = kwargs.get('gridrange', None)
192
193 # define gridder
194 g2d = Gridder2D(nx, ny)
195 if gridrange:
196 g2d.dataRange(gridrange[0][0], gridrange[0][1],
197 gridrange[1][0], gridrange[1][1])
198
199 # check if counter is in data fields
200 if self.counter not in self.data.dtype.fields:
201 raise ValueError("field named '%s' not found in data parsed from "
202 "scan #%d in file %s"
203 % (self.counter, self.scannr, self.filename))
204
205 # grid data
206 g2d(self.xvalues, self.yvalues, self.data[self.counter])
207
208 # return gridded data
209 return g2d
210
211
212 class FastScanCCD(FastScan):
213
214 """
215 class to help parsing and treating fast scan data including CCD frames.
216 FastScan is the aquisition of X-ray data while scanning the sample with
217 piezo stages in real space. It's is available at several beamlines at the
218 ESRF synchrotron light-source. During such fast scan at every grid point
219 CCD frames are recorded and need to be analyzed
220 """
221
222 def __init__(self, *args, **kwargs):
223 """
224 Parameters
225 ----------
226 imagefiletype : str, optional
227 image file extension, either 'edf' / 'edf.gz' (default) or 'h5'
228
229 other parameters are passed on to FastScanCCD
230 """
231 self.imagefiletype = kwargs.pop('imagefiletype', 'edf')
232 self.imgfile = None
233 self.nimages = None
234 super().__init__(*args, **kwargs)
235
236 def _getCCDnumbers(self, ccdnr):
237 """
238 internal function to return the ccd frame numbers from the data object
239 or take them from the argument.
240 """
241 if isinstance(ccdnr, str):
242 # check if counter is in data fields
243 try:
244 ccdnumbers = self.data[ccdnr]
245 except ValueError:
246 raise ValueError("field named '%s' not found in data parsed "
247 "from scan #%d in file %s"
248 % (ccdnr, self.scannr, self.filename))
249 elif isinstance(ccdnr, (list, tuple, numpy.ndarray)):
250 ccdnumbers = ccdnr
251 else:
252 raise ValueError("xu.FastScanCCD: wrong data type for "
253 "argument 'ccdnr'")
254 return ccdnumbers
255
256 def _gridCCDnumbers(self, nx, ny, ccdnr, gridrange=None):
257 """
258 internal function to grid the CCD frame number to produce a list of
259 ccd-files per bin needed for the further treatment
260
261 Parameters
262 ----------
263 nx, ny : int
264 number of bins in x, and y direction
265 ccdnr : str or array-like
266 array with ccd file numbers of length length(FastScanCCD.data) OR a
267 string with the data column name for the file ccd-numbers
268
269 gridrange : tuple, optional
270 range for the gridder: format: ((xmin, xmax), (ymin, ymax))
271
272 Returns
273 -------
274 gridder-object
275 regular x, y-grid as well as 4-dimensional data object
276 """
277 g2l = Gridder2DList(nx, ny)
278 if gridrange:
279 g2l.dataRange(gridrange[0][0], gridrange[0][1],
280 gridrange[1][0], gridrange[1][1])
281
282 ccdnumbers = self._getCCDnumbers(ccdnr)
283 # assign ccd frames to grid
284 g2l(self.xvalues, self.yvalues, ccdnumbers)
285
286 return g2l
287
288 def _read_image(self, filename, imgindex, nav, roi, filterfunc):
289 """
290 helper function to obtain one frame from an EDF/HDF5 file
291
292 Parameters
293 ----------
294 filename : str
295 EDF file name
296 imgindex : int
297 index of frame inside the given EDF file
298 nav : tuple or list
299 number of detector pixel which will be averaged together (reduces
300 the date size)
301 roi : tuple
302 region of interest on the 2D detector. should be a list of lower
303 and upper bounds of detector channels for the two pixel directions
304 (default: None)
305 filterfunc : callable
306 function applied to the CCD-frames before any processing. this
307 function should take a single argument which is the ccddata which
308 need to be returned with the same shape! e.g. remove hot pixels,
309 flat/darkfield correction
310
311 Returns
312 -------
313 ndarray
314 numpy 2D array with the detector frame
315 """
316 if roi is None:
317 kwdict = {}
318 else:
319 kwdict = {'roi': roi}
320 if 'edf' in self.imagefiletype:
321 if not self.imgfile:
322 self.imgfile = EDFFile(filename, keep_open=True)
323 else:
324 if self.imgfile.filename != filename:
325 self.imgfile = EDFFile(filename, keep_open=True)
326 ccdfilt = self.imgfile.ReadData(imgindex)
327 else:
328 fileroot = os.path.splitext(os.path.splitext(filename)[0])[0]
329 if not self.imgfile:
330 self.imgfile = h5py.File(fileroot + '.h5', 'r')
331 ccdfilt = self.imgfile.get(
332 os.path.split(fileroot)[-1] + '_%04d' % imgindex).value
333 if filterfunc:
334 ccdfilt = filterfunc(ccdfilt)
335 if roi is None and nav[0] == 1 and nav[1] == 1:
336 return ccdfilt
337 else:
338 return blockAverage2D(ccdfilt, nav[0], nav[1], **kwdict)
339
340 def _get_image_number(self, imgnum, imgoffset, fileoffset, ccdfiletmp):
341 """
342 function to obtain the image and file number. The logic for obtain this
343 is likely to change between beamtimes.
344
345 Parameters
346 ----------
347 imgnum : int
348 running image number from the data file
349 imgoffset : int
350 offset in the image number
351 fileoffset : int
352 offset in the file number
353 ccdfiletmp : str
354 ccd file template string
355 """
356 if 'edf' in self.imagefiletype:
357 if not self.imgfile:
358 self.imgfile = EDFFile(ccdfiletmp % fileoffset, keep_open=True)
359 if self.nimages is None:
360 self.nimages = self.imgfile.nimages
361 else:
362 fileroot = os.path.splitext(os.path.splitext(ccdfiletmp
363 % fileoffset)[0])[0]
364 if not self.imgfile:
365 self.imgfile = h5py.File(fileroot + '.h5', 'r')
366 if self.nimages is None:
367 self.nimages = len(self.imgfile.items())
368 filenumber = int((imgnum - imgoffset) // self.nimages + fileoffset)
369 imgindex = int((imgnum - imgoffset) % self.nimages)
370 return imgindex, filenumber
371
372 def getccdFileTemplate(self, specscan, datadir=None, keepdir=0,
373 replacedir=None):
374 """
375 function to extract the CCD file template string from the comment
376 in the SPEC-file scan-header.
377
378 Parameters
379 ----------
380 specscan : SpecScan
381 spec-scan object from which header the CCD directory should be
382 extracted
383 datadir : str, optional
384 the CCD filenames are usually parsed from the scan object. With
385 this option the directory used for the data can be overwritten.
386 Specify the datadir as simple string. Alternatively the innermost
387 directory structure can be automatically taken from the specfile.
388 If this is needed specify the number of directories which should be
389 kept using the keepdir option.
390 keepdir : int, optional
391 number of directories which should be taken from the specscan.
392 (default: 0)
393 replacedir : int, optional
394 number of outer most directory names which should be replaced in
395 the output (default = None). One can either give keepdir, or
396 replacedir, with replace taking preference if both are given.
397
398 Returns
399 -------
400 fmtstr : str
401 format string for the CCD file name using one number to build the
402 real file name
403 filenr : int
404 starting file number
405 """
406 hline = specscan.getheader_element('C imageFile')
407 re_ccdfiles = re.compile(r'dir\[([a-zA-Z0-9_.%/]*)\] '
408 r'prefix\[([a-zA-Z0-9_.%/]*)\] '
409 r'idxFmt\[([a-zA-Z0-9_.%/]*)\] '
410 r'nextNr\[([0-9]*)\] '
411 r'suffix\[([a-zA-Z0-9_.%/]*)\]')
412 m = re_ccdfiles.match(hline)
413 if m:
414 path, prefix, idxFmt, num, suffix = m.groups()
415 else:
416 ValueError('spec-scan does not contain images or the '
417 'corresponding header line is not detected correctly')
418 ccdtmp = os.path.join(path, prefix + idxFmt + suffix)
419 r = utilities.exchange_filepath(ccdtmp, datadir, keepdir, replacedir)
420 return r, int(num)
421
422 def getCCD(self, ccdnr, roi=None, datadir=None, keepdir=0,
423 replacedir=None, nav=[1, 1], filterfunc=None):
424 """
425 function to read the ccd files and return the raw X, Y and DATA values.
426 DATA represents a 3D object with first dimension representing the data
427 point index and the remaining two dimensions representing detector
428 channels
429
430 Parameters
431 ----------
432 ccdnr : array-like or str
433 array with ccd file numbers of length length(FastScanCCD.data) OR a
434 string with the data column name for the file ccd-numbers
435
436 roi : tuple, optional
437 region of interest on the 2D detector. should be a list of lower
438 and upper bounds of detector channels for the two pixel directions
439 (default: None)
440 datadir : str, optional
441 the CCD filenames are usually parsed from the SPEC file. With this
442 option the directory used for the data can be overwritten. Specify
443 the datadir as simple string. Alternatively the innermost
444 directory structure can be automatically taken from the specfile.
445 If this is needed specify the number of directories which should be
446 kept using the keepdir option.
447 keepdir : int, optional
448 number of directories which should be taken from the SPEC file.
449 (default: 0)
450 replacedir : int, optional
451 number of outer most directory names which should be replaced in
452 the output (default = None). One can either give keepdir, or
453 replacedir, with replace taking preference if both are given.
454 nav : tuple or list, optional
455 number of detector pixel which will be averaged together (reduces
456 the date size)
457 filterfunc : callable
458 function applied to the CCD-frames before any processing. this
459 function should take a single argument which is the ccddata which
460 need to be returned with the same shape! e.g. remove hot pixels,
461 flat/darkfield correction
462
463 Returns
464 -------
465 X, Y : ndarray
466 x, y-array (1D)
467 DATA : ndarray
468 3-dimensional data object
469 """
470 ccdnumbers = self._getCCDnumbers(ccdnr)
471
472 ccdtemplate, nextNr = self.getccdFileTemplate(
473 self.specscan, datadir, keepdir=keepdir, replacedir=replacedir)
474
475 # read ccd shape from first image
476 filename = ccdtemplate % nextNr
477 ccdshape = self._read_image(filename, 0, nav, roi, filterfunc).shape
478 ccddata = numpy.empty((self.xvalues.size, ccdshape[0], ccdshape[1]))
479 if config.VERBOSITY >= config.INFO_ALL:
480 print('XU.io.FastScanCCD: allocated ccddata array with %d bytes'
481 % ccddata.nbytes)
482
483 # go through the ccd-frames
484 for i, imgnum in enumerate(ccdnumbers):
485 # read ccd-frames
486 imgindex, filenumber = self._get_image_number(imgnum, nextNr,
487 nextNr, ccdtemplate)
488 filename = ccdtemplate % filenumber
489 ccd = self._read_image(filename, imgindex, nav, roi, filterfunc)
490 ccddata[i, :, :] = ccd
491 return self.xvalues, self.yvalues, ccddata
492
493 def processCCD(self, ccdnr, roi, datadir=None, keepdir=0,
494 replacedir=None, filterfunc=None):
495 """
496 function to read a region of interest (ROI) from the ccd files and
497 return the raw X, Y and intensity from ROI.
498
499 Parameters
500 ----------
501 ccdnr : array-like or str
502 array with ccd file numbers of length length(FastScanCCD.data) OR a
503 string with the data column name for the file ccd-numbers
504 roi : tuple or list
505 region of interest on the 2D detector. Either a list of lower and
506 upper bounds of detector channels for the two pixel directions as
507 tuple or a list of mask arrays
508 datadir : str, optional
509 the CCD filenames are usually parsed from the SPEC file. With this
510 option the directory used for the data can be overwritten. Specify
511 the datadir as simple string. Alternatively the innermost
512 directory structure can be automatically taken from the specfile.
513 If this is needed specify the number of directories which should be
514 kept using the keepdir option.
515 keepdir : int, optional
516 number of directories which should be taken from the SPEC file.
517 (default: 0)
518 replacedir : int, optional
519 number of outer most directory names which should be replaced in
520 the output (default = None). One can either give keepdir, or
521 replacedir, with replace taking preference if both are given.
522 filterfunc : callable, optional
523 function applied to the CCD-frames before any processing. this
524 function should take a single argument which is the ccddata which
525 need to be returned with the same shape! e.g. remove hot pixels,
526 flat/darkfield correction
527
528 Returns
529 -------
530 X, Y, DATA : ndarray
531 x, y-array (1D) as well as 1-dimensional data object
532 """
533 ccdnumbers = self._getCCDnumbers(ccdnr)
534
535 ccdtemplate, nextNr = self.getccdFileTemplate(
536 self.specscan, datadir, keepdir=keepdir, replacedir=replacedir)
537
538 if isinstance(roi, list):
539 lmask = roi
540 lroi = None
541 else:
542 lmask = [numpy.ones((roi[1]-roi[0], roi[3]-roi[2])), ]
543 lroi = roi
544 ccdroi = numpy.empty((len(lmask), self.xvalues.size))
545
546 # go through the ccd-frames
547 for i, imgnum in enumerate(ccdnumbers):
548 # read ccd-frames
549 imgindex, filenumber = self._get_image_number(imgnum, nextNr,
550 nextNr, ccdtemplate)
551 filename = ccdtemplate % filenumber
552 ccd = self._read_image(filename, imgindex, [1, 1], lroi,
553 filterfunc)
554 for j, m in enumerate(lmask):
555 ccdroi[j, i] = numpy.sum(ccd[m])
556 if len(lmask) == 1:
557 return self.xvalues, self.yvalues, ccdroi[0]
558 else:
559 return self.xvalues, self.yvalues, ccdroi
560
561 def gridCCD(self, nx, ny, ccdnr, roi=None, datadir=None, keepdir=0,
562 replacedir=None, nav=[1, 1], gridrange=None, filterfunc=None):
563 """
564 function to grid the internal data and ccd files and return the gridded
565 X, Y and DATA values. DATA represents a 4D object with first two
566 dimensions representing X, Y and the remaining two dimensions
567 representing detector channels
568
569 Parameters
570 ----------
571 nx, ny : int
572 number of bins in x, and y direction
573 ccdnr : array-like or str
574 array with ccd file numbers of length length(FastScanCCD.data) OR a
575 string with the data column name for the file ccd-numbers
576
577 roi : tuple, optional
578 region of interest on the 2D detector. should be a list of lower
579 and upper bounds of detector channels for the two pixel directions
580 (default: None)
581 datadir : str, optional
582 the CCD filenames are usually parsed from the SPEC file. With this
583 option the directory used for the data can be overwritten. Specify
584 the datadir as simple string. Alternatively the innermost
585 directory structure can be automatically taken from the specfile.
586 If this is needed specify the number of directories which should be
587 kept using the keepdir option.
588 keepdir : int, optional
589 number of directories which should be taken from the SPEC file.
590 (default: 0)
591 replacedir : int, optional
592 number of outer most directory names which should be replaced in
593 the output (default = None). One can either give keepdir, or
594 replacedir, with replace taking preference if both are given.
595 nav : tuple or list, optional
596 number of detector pixel which will be averaged together (reduces
597 the date size)
598 gridrange : tuple
599 range for the gridder: format: ((xmin, xmax), (ymin, ymax))
600 filterfunc : callable
601 function applied to the CCD-frames before any processing. this
602 function should take a single argument which is the ccddata which
603 need to be returned with the same shape! e.g. remove hot pixels,
604 flat/darkfield correction
605
606 Returns
607 -------
608 X, Y: ndarray
609 regular x, y-grid
610 DATA : ndarray
611 4-dimensional data object
612 """
613
614 g2l = self._gridCCDnumbers(nx, ny, ccdnr, gridrange=gridrange)
615 gdata = g2l.data
616
617 ccdtemplate, nextNr = self.getccdFileTemplate(
618 self.specscan, datadir, keepdir=keepdir, replacedir=replacedir)
619
620 # read ccd shape from first image
621 filename = ccdtemplate % nextNr
622 ccdshape = self._read_image(filename, 0, nav, roi, filterfunc).shape
623 ccddata = numpy.empty((self.xvalues.size, ccdshape[0], ccdshape[1]))
624 if config.VERBOSITY >= config.INFO_ALL:
625 print('XU.io.FastScanCCD: allocated ccddata array with %d bytes'
626 % ccddata.nbytes)
627
628 # go through the gridded data and average the ccd-frames
629 for i in range(gdata.shape[0]):
630 for j in range(gdata.shape[1]):
631 if not gdata[i, j]:
632 continue
633 else:
634 framecount = 0
635 # read ccd-frames and average them
636 for imgnum in gdata[i, j]:
637 imgindex, filenumber = self._get_image_number(
638 imgnum, nextNr, nextNr, ccdtemplate)
639 filename = ccdtemplate % filenumber
640 ccd = self._read_image(filename, imgindex, nav,
641 roi, filterfunc)
642 ccddata[i, j, ...] += ccd
643 framecount += 1
644 ccddata[i, j, ...] /= float(framecount)
645
646 return g2l.xmatrix, g2l.ymatrix, ccddata
647
648
649 class FastScanSeries(object):
650
651 """
652 class to help parsing and treating a series of fast scan data including CCD
653 frames. FastScan is the aquisition of X-ray data while scanning the sample
654 with piezo stages in real space. It's is available at several beamlines at
655 the ESRF synchrotron light-source. During such fast scan at every grid
656 point CCD frames are recorded and need to be analyzed.
657
658 For the series of FastScans we assume that they are measured at different
659 goniometer angles and therefore transform the data to reciprocal space.
660 """
661
662 def __init__(self, filenames, scannrs, nx, ny, *args, **kwargs):
663 """
664 Constructor routine for the FastScanSeries object. It initializes the
665 object and creates a list of FastScanCCD objects. Importantly it also
666 expects the motor names of the angles needed for reciprocal space
667 conversion.
668
669 Parameters
670 ----------
671 filenames : list or str
672 file names of the fast scan spec files, in case of more than one
673 filename supply a list of names and also a list of scan numbers for
674 the different files in the 'scannrs' argument
675 scannrs : list
676 scannrs of the to be parsed fast scans. in case of one specfile
677 this is a list of numbers (e.g. [1, 2, 3]). when multiple filenames
678 are given supply a separate list for every file (e.g. [[1, 2,
679 3],[2, 4]])
680 nx, ny : int
681 grid-points for the real space grid
682 args : str
683 motor names for the Reciprocal space conversion. The order needs be
684 as required by the ``QConversion.area()`` function.
685 xmotor : str, optional
686 motor name of the x-motor (default: 'adcX' (ID01))
687 ymotor : str, optional
688 motor name of the y-motor (default: 'adcY' (ID01))
689 ccdnr : str, optional
690 name of the ccd-number data column (default: 'imgnr' (ID01))
691 counter : str, optional
692 name of a defined counter (roi) in the spec file (default:
693 'mpx4int' (ID01))
694 path : str, optional
695 path of the FastScan spec file (default: '')
696 """
697
698 if 'ccdnr' in kwargs:
699 self.ccdnr = kwargs['ccdnr']
700 kwargs.pop("ccdnr")
701 else:
702 self.ccdnr = 'imgnr'
703
704 if 'counter' in kwargs:
705 self.counter = kwargs['counter']
706 kwargs.pop("counter")
707 else:
708 self.counter = 'mpx4int'
709
710 if 'path' in kwargs:
711 self.path = kwargs['path']
712 kwargs.pop("path")
713 else:
714 self.path = ''
715
716 self.fastscans = []
717 self.nx = nx
718 self.ny = ny
719 self.motor_pos = None
720
721 self.gonio_motors = []
722 # save motor names
723 for arg in args:
724 if not isinstance(arg, str):
725 raise ValueError("one of the motor name arguments is not of "
726 "type 'str' but %s" % str(type(arg)))
727 self.gonio_motors.append(arg)
728
729 # create list of FastScans
730 if isinstance(filenames, str):
731 filenames = [filenames]
732 scannrs = [scannrs]
733 if isinstance(filenames, (tuple, list)):
734 for fname in filenames:
735 full_filename = os.path.join(self.path, fname)
736 specfile = SPECFile(full_filename)
737 for snrs in scannrs[filenames.index(fname)]:
738 self.fastscans.append(FastScanCCD(specfile,
739 snrs, **kwargs))
740 else:
741 raise ValueError("argument 'filenames' is not of "
742 "appropriate type!")
743
744 self._init_minmax()
745 for fs in self.fastscans:
746 self._update_minmax(fs)
747
748 def _init_minmax(self):
749 self.gridded = False
750 self.xmin = numpy.min(self.fastscans[0].xvalues)
751 self.ymin = numpy.min(self.fastscans[0].yvalues)
752 self.xmax = numpy.max(self.fastscans[0].xvalues)
753 self.ymax = numpy.max(self.fastscans[0].yvalues)
754
755 def _update_minmax(self, fs):
756 if numpy.max(fs.xvalues) > self.xmax:
757 self.xmax = numpy.max(fs.xvalues)
758 if numpy.max(fs.yvalues) > self.ymax:
759 self.ymax = numpy.max(fs.yvalues)
760 if numpy.min(fs.xvalues) < self.xmin:
761 self.xmin = numpy.min(fs.xvalues)
762 if numpy.min(fs.yvalues) < self.ymin:
763 self.ymin = numpy.min(fs.yvalues)
764
765 def retrace_clean(self):
766 """
767 perform retrace clean for every FastScan in the series
768 """
769 self._init_minmax()
770
771 for fs in self.fastscans:
772 fs.retrace_clean()
773 self._update_minmax(fs)
774
775 def align(self, deltax, deltay):
776 """
777 Since a sample drift or shift due to rotation often occurs between
778 different FastScans it should be corrected before combining them. Since
779 determining such a shift is not straight-forward in general the user
780 needs to supply the routine with the shifts in order correct the
781 x, y-values for the different FastScans. Such a routine could for
782 example use the integrated CCD intensities and determine the shift
783 using a cross-convolution.
784
785 Parameters
786 ----------
787 deltax, deltay : list
788 list of shifts in x/y-direction for every FastScan in the data
789 structure
790 """
791 self._init_minmax()
792 for fs in self.fastscans:
793 i = self.fastscans.index(fs)
794 fs.xvalues += deltax[i]
795 fs.yvalues += deltay[i]
796 self._update_minmax(fs)
797
798 def read_motors(self):
799 """
800 read motor values from the series of fast scans
801 """
802 self.motor_pos = numpy.zeros((len(self.fastscans),
803 len(self.gonio_motors)))
804 for i in range(len(self.fastscans)):
805 fs = self.fastscans[i]
806 for j in range(len(self.gonio_motors)):
807 mname = self.gonio_motors[j]
808 self.motor_pos[i, j] = fs.motorposition(mname)
809
810 def get_average_RSM(self, qnx, qny, qnz, qconv, datadir=None, keepdir=0,
811 replacedir=None, roi=None, nav=(1, 1),
812 filterfunc=None):
813 """
814 function to return the reciprocal space map data averaged over all x, y
815 positions from a series of FastScan measurements. It necessary to give
816 the QConversion-object to be used for the reciprocal space conversion.
817 The QConversion-object is expected to have the 'area' conversion
818 routines configured properly. This function needs to read all detector
819 images, so be prepared to lean back for a moment!
820
821 Parameters
822 ----------
823 qnx, qny, qnz : int
824 number of points used for the 3D Gridder
825 qconv : QConversion
826 QConversion-object to be used for the conversion of the CCD-data to
827 reciprocal space
828
829 roi : tuple, optional
830 region of interest on the 2D detector. should be a list of lower
831 and upper bounds of detector channels for the two pixel directions
832 (default: None)
833 nav : tuple or list, optional
834 number of detector pixel which will be averaged together (reduces
835 the date size)
836 filterfunc : callable, optional
837 function applied to the CCD-frames before any processing. this
838 function should take a single argument which is the ccddata which
839 need to be returned with the same shape! e.g. remove hot pixels,
840 flat/darkfield correction
841 datadir : str, optional
842 the CCD filenames are usually parsed from the SPEC file. With this
843 option the directory used for the data can be overwritten. Specify
844 the datadir as simple string. Alternatively the innermost
845 directory structure can be automatically taken from the specfile.
846 If this is needed specify the number of directories which should be
847 kept/replaced using the keepdir/replacedir option.
848 keepdir : int, optional
849 number of directories which should be taken from the SPEC file.
850 (default: 0)
851 replacedir : int, optional
852 number of outer most directory names which should be replaced in
853 the output (default = None). One can either give keepdir, or
854 replacedir, with replace taking preference if both are given.
855
856 Returns
857 -------
858 Gridder3D
859 gridded reciprocal space map
860 """
861 if self.motor_pos is None:
862 self.read_motors()
863
864 # determine q-coordinates
865 kwargs = {'Nav': nav}
866 if roi:
867 kwargs['roi'] = roi
868 qx, qy, qz = qconv.area(*self.motor_pos.T, **kwargs)
869
870 # define gridder with fixed optimized q-range
871 g3d = Gridder3D(qnx, qny, qnz)
872 g3d.keep_data = True
873 g3d.dataRange(qx.min(), qx.max(), qy.min(),
874 qy.max(), qz.min(), qz.max(), fixed=True)
875
876 # start parsing the images and grid the data frame by frame
877 for fsidx, fsccd in enumerate(self.fastscans):
878 ccdtemplate, nextNr = fsccd.getccdFileTemplate(
879 fsccd.specscan, datadir, keepdir=keepdir,
880 replacedir=replacedir)
881
882 ccdnumbers = fsccd._getCCDnumbers(self.ccdnr)
883 ccdav = numpy.zeros_like(qx[fsidx, ...])
884
885 # go through the ccdframes
886 for i, imgnum in enumerate(ccdnumbers):
887 # read ccdframes
888 imgindex, filenumber = fsccd._get_image_number(
889 imgnum, nextNr, nextNr, ccdtemplate)
890 filename = ccdtemplate % filenumber
891 ccd = fsccd._read_image(filename, imgindex, nav, roi,
892 filterfunc)
893 ccdav += ccd
894 g3d(qx[fsidx, ...], qy[fsidx, ...], qz[fsidx, ...], ccdav)
895
896 return g3d
897
898 def get_sxrd_for_qrange(self, qrange, qconv, datadir=None, keepdir=0,
899 replacedir=None, roi=None, nav=(1, 1),
900 filterfunc=None):
901 """
902 function to return the real space data averaged over a certain q-range
903 from a series of FastScan measurements. It necessary to give the
904 QConversion-object to be used for the reciprocal space conversion. The
905 QConversion-object is expected to have the 'area' conversion routines
906 configured properly.
907
908 Note:
909 This function assumes that all FastScans were performed in the same
910 real space positions, no gridding or aligning is performed!
911
912 Parameters
913 ----------
914 qrange : list or tuple
915 q-limits defining a box in reciprocal space. six values are
916 needed: [minx, maxx, miny, ..., maxz]
917 qconv : QConversion
918 QConversion object to be used for the conversion of the CCD-data to
919 reciprocal space
920
921 roi : tuple, optional
922 region of interest on the 2D detector. should be a list of lower
923 and upper bounds of detector channels for the two pixel directions
924 (default: None)
925 nav : tuple or list, optional
926 number of detector pixel which will be averaged together (reduces
927 the date size)
928 filterfunc : callable, optional
929 function applied to the CCD-frames before any processing. this
930 function should take a single argument which is the ccddata which
931 need to be returned with the same shape! e.g. remove hot pixels,
932 flat/darkfield correction
933 datadir : str, optional
934 the CCD filenames are usually parsed from the SPEC file. With this
935 option the directory used for the data can be overwritten. Specify
936 the datadir as simple string. Alternatively the innermost
937 directory structure can be automatically taken from the specfile.
938 If this is needed specify the number of directories which should be
939 kept/replaced using the keepdir/replacedir option.
940 keepdir : int, optional
941 number of directories which should be taken from the SPEC file.
942 (default: 0)
943 replacedir : int, optional
944 number of outer most directory names which should be replaced in
945 the output (default = None). One can either give keepdir, or
946 replacedir, with replace taking preference if both are given.
947
948 Returns
949 -------
950 xvalues, yvalues, data : ndarray
951 x, y, and data values
952 """
953 if self.motor_pos is None:
954 self.read_motors()
955
956 # determine q-coordinates
957 kwargs = {'Nav': nav}
958 if roi:
959 kwargs['roi'] = roi
960 qx, qy, qz = qconv.area(*self.motor_pos.T, **kwargs)
961 output = numpy.zeros_like(self.fastscans[0].xvalues)
962
963 # parse the images only if some q coordinates fall into the ROI
964 for fsidx, fsccd in enumerate(self.fastscans):
965 mask = numpy.logical_and.reduce((
966 qx[fsidx] > qrange[0], qx[fsidx] < qrange[1],
967 qy[fsidx] > qrange[2], qy[fsidx] < qrange[3],
968 qz[fsidx] > qrange[4], qz[fsidx] < qrange[5]))
969
970 if numpy.any(mask):
971 ccdtemplate, nextNr = fsccd.getccdFileTemplate(
972 fsccd.specscan, datadir, keepdir=keepdir,
973 replacedir=replacedir)
974
975 ccdnumbers = fsccd._getCCDnumbers(self.ccdnr)
976
977 # go through the ccdframes
978 for i, imgnum in enumerate(ccdnumbers):
979 # read ccdframes
980 imgindex, filenumber = fsccd._get_image_number(
981 imgnum, nextNr, nextNr, ccdtemplate)
982 filename = ccdtemplate % filenumber
983 ccd = fsccd._read_image(filename, imgindex, nav, roi,
984 filterfunc)
985 output[i] += numpy.sum(ccd[mask])
986
987 return fsccd.xvalues, fsccd.yvalues, output
988
989 def getCCDFrames(self, posx, posy, typ='real'):
990 """
991 function to determine the list of ccd-frame numbers for a specific real
992 space position. The real space position must be within the data limits
993 of the FastScanSeries otherwise an ValueError is thrown
994
995 Parameters
996 ----------
997 posx : float
998 real space x-position or index in x direction
999 posy : float
1000 real space y-position or index in y direction
1001
1002 typ : {'real', 'index'}, optional
1003 type of coordinates. specifies if the position is specified as real
1004 space coordinate or as index. (default: 'real')
1005
1006 Returns
1007 -------
1008 list
1009 ``[[motorpos1, ccdnrs1], [motorpos2, ccdnrs2], ...]`` where
1010 motorposN is from the N-ths FastScan in the series and ccdnrsN is
1011 the list of according CCD-frames
1012 """
1013
1014 # determine grid point for position x, y
1015 if typ == 'real':
1016 # grid point calculation
1017 def gindex(x, min, delt):
1018 return numpy.round((x - min) / delt)
1019
1020 xdelta = delta(self.xmin, self.xmax, self.nx)
1021 ydelta = delta(self.ymin, self.ymax, self.ny)
1022 xidx = gindex(posx, self.xmin, xdelta)
1023 yidx = gindex(posy, self.ymin, ydelta)
1024 elif typ == 'index':
1025 xidx = posx
1026 yidx = posy
1027 else:
1028 raise ValueError("given value of 'typ' is invalid.")
1029
1030 if xidx >= self.nx or xidx < 0:
1031 raise ValueError("specified x-position is out of the data range")
1032 if yidx > self.ny or yidx < 0:
1033 raise ValueError("specified y-position is out of the data range")
1034
1035 # read motor values and perform gridding for all subscans
1036 if not self.gridded:
1037 self.read_motors()
1038 self.glist = []
1039 for fs in self.fastscans:
1040 g2l = fs._gridCCDnumbers(
1041 self.nx, self.ny, self.ccdnr,
1042 gridrange=((self.xmin, self.xmax), (self.ymin, self.ymax)))
1043 self.glist.append(g2l) # contains the ccdnumbers in g2l.data
1044
1045 self.gridded = True
1046
1047 # return the ccdnumbers and goniometer angles for this position
1048 ret = []
1049 for i in range(len(self.glist)):
1050 motorpos = self.motor_pos[i]
1051 ccdnrs = self.glist[i].data[xidx, yidx]
1052 ret.append([motorpos, ccdnrs])
1053
1054 return ret
1055
1056 def rawRSM(self, posx, posy, qconv, roi=None, nav=[1, 1], typ='real',
1057 datadir=None, keepdir=0, replacedir=None, filterfunc=None,
1058 **kwargs):
1059 """
1060 function to return the reciprocal space map data at a certain
1061 x, y-position from a series of FastScan measurements. It necessary to
1062 give the QConversion-object to be used for the reciprocal space
1063 conversion. The QConversion-object is expected to have the 'area'
1064 conversion routines configured properly.
1065
1066 Parameters
1067 ----------
1068 posx : float
1069 real space x-position or index in x direction
1070 posy : float
1071 real space y-position or index in y direction
1072 qconv : QConversion
1073 QConversion-object to be used for the conversion of the CCD-data to
1074 reciprocal space
1075
1076 roi : tuple, optional
1077 region of interest on the 2D detector. should be a list of lower
1078 and upper bounds of detector channels for the two pixel directions
1079 (default: None)
1080 nav : tuple or list, optional
1081 number of detector pixel which will be averaged together (reduces
1082 the date size)
1083 typ : {'real', 'index'}, optional
1084 type of coordinates. specifies if the position is specified as real
1085 space coordinate or as index. (default: 'real')
1086 filterfunc : callable, optional
1087 function applied to the CCD-frames before any processing. this
1088 function should take a single argument which is the ccddata which
1089 need to be returned with the same shape! e.g. remove hot pixels,
1090 flat/darkfield correction
1091 UB : array-like, optional
1092 sample orientation matrix
1093 datadir : str, optional
1094 the CCD filenames are usually parsed from the SPEC file. With this
1095 option the directory used for the data can be overwritten. Specify
1096 the datadir as simple string. Alternatively the innermost
1097 directory structure can be automatically taken from the specfile.
1098 If this is needed specify the number of directories which should be
1099 kept using the keepdir option.
1100 keepdir : int, optional
1101 number of directories which should be taken from the SPEC file.
1102 (default: 0)
1103 replacedir : int, optional
1104 number of outer most directory names which should be replaced in
1105 the output (default = None). One can either give keepdir, or
1106 replacedir, with replace taking preference if both are given.
1107
1108 Returns
1109 -------
1110 qx, qy, qz : ndarray
1111 reciprocal space positions of the reciprocal space map
1112 ccddata : ndarray
1113 raw data of the reciprocal space map
1114 valuelist : ndarray
1115 valuelist containing the ccdframe numbers and corresponding motor
1116 positions
1117 """
1118 U = kwargs.get('UB', numpy.identity(3))
1119
1120 # get CCDframe numbers and motor values
1121 valuelist = self.getCCDFrames(posx, posy, typ)
1122 # load ccd frames and convert to reciprocal space
1123 fsccd = self.fastscans[0]
1124 ccdtemplate, nextNr = fsccd.getccdFileTemplate(
1125 fsccd.specscan, datadir, keepdir=keepdir, replacedir=replacedir)
1126
1127 # read ccd shape from first image
1128 imgindex, filenumber = fsccd._get_image_number(
1129 valuelist[0][1], nextNr, nextNr, ccdtemplate)
1130 filename = ccdtemplate % filenumber
1131 ccdshape = fsccd._read_image(filename, imgindex, nav, roi,
1132 filterfunc).shape
1133 ccddata = numpy.zeros((len(self.fastscans), ccdshape[0], ccdshape[1]))
1134 motors = []
1135
1136 for i in range(len(self.gonio_motors)):
1137 motors.append(numpy.zeros(0))
1138 # go through the gridded data and average the ccdframes
1139 for i in range(len(self.fastscans)):
1140 imotors, ccdnrs = valuelist[i]
1141 fsccd = self.fastscans[i]
1142 # append motor positions
1143 for j in range(len(self.gonio_motors)):
1144 motors[j] = numpy.append(motors[j], imotors[j])
1145 # read CCD
1146 if not ccdnrs:
1147 continue
1148 else:
1149 ccdtemplate, nextNr = fsccd.getccdFileTemplate(fsccd.specscan)
1150 framecount = 0
1151 # read ccd-frames and average them
1152 for imgnum in ccdnrs:
1153 imgindex, filenumber = fsccd._get_image_number(
1154 imgnum, nextNr, nextNr, ccdtemplate)
1155 filename = ccdtemplate % filenumber
1156 ccd = fsccd._read_image(filename, imgindex, nav, roi,
1157 filterfunc)
1158 ccddata[i, ...] += ccd
1159 framecount += 1
1160 ccddata[i, ...] /= float(framecount)
1161
1162 qx, qy, qz = qconv.area(*motors, roi=roi, Nav=nav, UB=U)
1163 return qx, qy, qz, ccddata, valuelist
1164
1165 def gridRSM(self, posx, posy, qnx, qny, qnz, qconv, roi=None, nav=[1, 1],
1166 typ='real', filterfunc=None, **kwargs):
1167 """
1168 function to calculate the reciprocal space map at a certain
1169 x, y-position from a series of FastScan measurements it is necessary to
1170 specify the number of grid-oints for the reciprocal space map and the
1171 QConversion-object to be used for the reciprocal space conversion. The
1172 QConversion-object is expected to have the 'area' conversion routines
1173 configured properly.
1174
1175 Parameters
1176 ----------
1177 posx : float
1178 real space x-position or index in x direction
1179 posy : float
1180 real space y-position or index in y direction
1181 qnx, qny, qnz : int
1182 number of points in the Qx, Qy, Qz direction of the gridded
1183 reciprocal space map
1184 qconv : QConversion
1185 QConversion-object to be used for the conversion of the CCD-data to
1186 reciprocal space
1187 roi : tuple, optional
1188 region of interest on the 2D detector. should be a list of lower
1189 and upper bounds of detector channels for the two pixel directions
1190 (default: None)
1191 nav : tuple or list, optional
1192 number of detector pixel which will be averaged together (reduces
1193 the date size)
1194 typ : {'real', 'index'}, optional
1195 type of coordinates. specifies if the position is specified as real
1196 space coordinate or as index. (default: 'real')
1197 filterfunc : callable, optional
1198 function applied to the CCD-frames before any processing. this
1199 function should take a single argument which is the ccddata which
1200 need to be returned with the same shape! e.g. remove hot pixels,
1201 flat/darkfield correction
1202 UB : ndarray
1203 sample orientation matrix
1204
1205 Returns
1206 -------
1207 Gridder3D
1208 object with gridded reciprocal space map
1209 """
1210 qx, qy, qz, ccddata, vallist = self.rawRSM(
1211 posx, posy, qconv, roi=roi, nav=nav,
1212 typ=typ, filterfunc=filterfunc, **kwargs)
1213 # perform 3D gridding and return the data or gridder
1214 g = Gridder3D(qnx, qny, qnz)
1215 g(qx, qy, qz, ccddata)
1216 return g
1217
1218 def grid2Dall(self, nx, ny, **kwargs):
1219 """
1220 function to grid the counter data and return the gridded X, Y and
1221 Intensity values from all the FastScanSeries.
1222
1223 Parameters
1224 ----------
1225 nx, ny : int
1226 number of bins in x, and y direction
1227
1228 counter : str, optional
1229 name of the counter to use for gridding (default: 'mpx4int' (ID01))
1230 gridrange : tuple, optional
1231 range for the gridder: format: ((xmin, xmax), (ymin, ymax))
1232
1233 Returns
1234 -------
1235 Gridder2D
1236 object with X, Y, data on regular x, y-grid
1237 """
1238 counter = kwargs.get('counter', 'mpx4int')
1239 gridrange = kwargs.get('gridrange', ((self.xmin, self.xmax),
1240 (self.ymin, self.ymax)))
1241
1242 # define gridder
1243 g2d = Gridder2D(nx, ny)
1244 if gridrange:
1245 g2d.dataRange(gridrange[0][0], gridrange[0][1],
1246 gridrange[1][0], gridrange[1][1])
1247 g2d.KeepData(True)
1248
1249 for fs in self.fastscans:
1250 # check if counter is in data fields
1251 if counter not in fs.data.dtype.fields:
1252 raise ValueError("field named '%s' not found in data parsed "
1253 "from scan #%d in file %s"
1254 % (counter, fs.scannr, fs.filename))
1255
1256 # grid data
1257 g2d(fs.xvalues, fs.yvalues, fs.data[counter])
1258
1259 # return gridded data
1260 return g2d
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import glob
18 import os.path
19
20 from .. import config
21 from .helper import xu_h5open
22
23
24 class FileDirectory(object):
25
26 """
27 Parses a directory for files, which can be stored to a HDF5 file for
28 further usage. The file parser is given to the constructor and must provide
29 a Save2HDF5 method.
30 """
31
32 def __init__(self, datapath, ext, parser, **keyargs):
33 """
34
35 Parameters
36 ----------
37 datapath : str
38 directory of the files
39 ext : str
40 extension of the files in the datapath
41 parser : class
42 Parser class for the data files.
43 keyargs : dict
44 further keyword arguments are passed to the constructor of the
45 parser
46 """
47
48 self.datapath = os.path.normpath(datapath)
49 self.extension = ext
50 self.parser = parser
51
52 # create list of files to read
53 self.files = glob.glob(os.path.join(
54 self.datapath, '*.%s' % (self.extension)))
55
56 if not self.files:
57 print("XU.io.FileDirectory: no file found in %s" % (self.datapath))
58 return
59
60 if config.VERBOSITY >= config.INFO_ALL:
61 print("XU.io.FileDirectory: %d files found in %s"
62 % (len(self.files), self.datapath))
63
64 self.init_keyargs = keyargs
65
66 def Save2HDF5(self, h5f, group="", comp=True):
67 """
68 Saves the data stored in the found files in the specified directory in
69 a HDF5 file as a HDF5 arrays in a subgroup. By default the data is
70 stored in a group given by the foldername - this can be changed by
71 passing the name of a target group or a path to the target group via
72 the "group" keyword argument.
73
74 Parameters
75 ----------
76 h5f : file-handle or str
77 a HDF5 file object or name
78 group : str, optional
79 group where to store the data (defaults to pathname if group is
80 empty string)
81 comp : bool, optional
82 activate compression - true by default
83 """
84 with xu_h5open(h5f, 'a') as h5:
85 if isinstance(group, str):
86 if group == "":
87 group = os.path.split(self.datapath)[1]
88 g = h5.get(group)
89 if not g:
90 g = h5.create_group(group)
91 else:
92 g = group
93
94 for infile in self.files:
95 # read EDFFile and save to hdf5
96 filename = os.path.split(infile)[1]
97 e = self.parser(filename, path=self.datapath,
98 **self.init_keyargs)
99 e.Save2HDF5(h5, group=g, comp=comp)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2013-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17
18 """
19 convenience functions to open files for various data file reader
20
21 these functions should be used in new parsers since they transparently allow to
22 open gzipped and bzipped files
23 """
24
25 import bz2
26 import gzip
27 import lzma
28
29 import h5py
30
31 from .. import config
32 from ..exception import InputError
33
34
35 def xu_open(filename, mode='rb'):
36 """
37 function to open a file no matter if zipped or not. Files with extension
38 '.gz', '.bz2', and '.xz' are assumed to be compressed and transparently
39 opened to read like usual files.
40
41 Parameters
42 ----------
43 filename : str
44 filename of the file to open (full including path)
45 mode : str, optional
46 mode in which the file should be opened
47
48 Returns
49 -------
50 file-handle
51 handle of the opened file
52
53 Raises
54 ------
55 IOError
56 If the file does not exist an IOError is raised by the open routine,
57 which is not caught within the function
58 """
59 if config.VERBOSITY >= config.INFO_ALL:
60 print("XU:io: opening file %s" % filename)
61 if filename.endswith('.gz'):
62 fid = gzip.open(filename, mode)
63 elif filename.endswith('.bz2'):
64 fid = bz2.BZ2File(filename, mode)
65 elif filename.endswith('.xz'):
66 fid = lzma.open(filename, mode)
67 else:
68 fid = open(filename, mode)
69
70 return fid
71
72
73 class xu_h5open(object):
74 """
75 helper object to decide if a HDF5 file has to be opened/closed when
76 using with a 'with' statement.
77 """
78
79 def __init__(self, f, mode='r'):
80 """
81 Parameters
82 ----------
83 f : str
84 filename or h5py.File instance
85 mode : str, optional
86 mode in which the file should be opened. ignored in case a file
87 handle is passed as f
88 """
89 self.closeFile = True
90 self.fid = None
91 self.mode = mode
92 if isinstance(f, h5py.File):
93 self.fid = f
94 self.closeFile = False
95 self.filename = f.filename
96 elif isinstance(f, str):
97 self.filename = f
98 else:
99 raise InputError("f argument of wrong type was passed, "
100 "should be string or filename")
101
102 def __enter__(self):
103 if self.fid:
104 if not self.fid.id.valid:
105 self.fid = h5py.File(self.filename, self.mode)
106 else:
107 self.fid = h5py.File(self.filename, self.mode)
108 return self.fid
109
110 def __exit__(self, type, value, traceback):
111 if self.closeFile:
112 self.fid.close()
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module for reading ILL data files (station D23): numor files
19 """
20
21 import collections.abc
22 import os.path
23 import re
24
25 import numpy
26
27 from ..exception import InputError
28 # relative imports from xrayutilities
29 from .helper import xu_open
30
31 re_comment = re.compile(r"^A+$")
32 re_basicinfo = re.compile(r"^R+$")
33 re_values = re.compile(r"^F+$")
34 re_spectrum = re.compile(r"^S+$")
35 re_header = re.compile(r"^I+$")
36
37
38 class numorFile(object):
39 """
40 Represents a ILL data file (numor). The file is read during the Constructor
41 call. This class should work for created at station D23 using the mad
42 acquisition system.
43
44 Parameters
45 ----------
46 filename : str
47 a string with the name of the data file
48 """
49
50 columns = {0: ('detector', 'monitor', 'time', 'gamma', 'omega', 'psi'),
51 1: ('detector', 'monitor', 'time', 'gamma'),
52 2: ('detector', 'monitor', 'time', 'omega'),
53 5: ('detector', 'monitor', 'time', 'psi')}
54
55 def __init__(self, filename, path=None):
56 """
57 constructor for the data file parser
58
59 Parameters
60 ----------
61 filename : str
62 a string with the name of the data file
63 path : str, optional
64 directory of the data file
65 """
66 self.filename = filename
67 if path is None:
68 self.full_filename = self.filename
69 else:
70 self.full_filename = os.path.join(path, self.filename)
71
72 self.Read()
73
74 def getline(self, fid):
75 return fid.readline().decode('ascii')
76
77 def ssplit(self, string):
78 """
79 multispace split. splits string at two or more spaces after stripping
80 it.
81 """
82 return re.split(r'\s\s+', string.strip())
83
84 def Read(self):
85 """
86 Read the data from the file
87 """
88 with xu_open(self.full_filename) as fid:
89 self.filesize = os.stat(self.full_filename).st_size
90 # read header
91 self.init_mopo = {}
92 self.comments = []
93 self.header = {}
94 self._data = []
95 while fid.tell() < self.filesize:
96 line = self.getline(fid)
97
98 if re_comment.match(line):
99 # read AAAA sections
100 line = self.getline(fid)
101 desc = []
102 for j in range(int(line.split()[1])):
103 desc += self.ssplit(self.getline(fid))
104 comval = self.ssplit(self.getline(fid))
105 self.comments.append((desc, comval))
106
107 if re_basicinfo.match(line):
108 # read RRRR section
109 info = self.ssplit(self.getline(fid))
110 self.dataversion = int(info[2])
111 self.runnumber = int(info[0])
112
113 if int(info[1]) > 0:
114 headerdesc = ''
115 for j in range(int(info[1])):
116 headerdesc += self.getline(fid) + '\n'
117 self.comments.append((['Fileheader'], [headerdesc]))
118
119 if re_header.match(line):
120 # read IIII section: integer header values
121 info = self.ssplit(self.getline(fid))
122 names = []
123 values = []
124
125 for j in range(int(info[1])):
126 names += self.getline(fid).split()
127 values = numpy.fromfile(fid, dtype=int,
128 count=int(info[0]), sep=' ')
129 self.header = {k: v for k, v in zip(names, values)}
130
131 if re_values.match(line):
132 # read FFFF section: initial motor positions
133 info = self.ssplit(self.getline(fid))
134 names = []
135 values = []
136
137 for j in range(int(info[1])):
138 names += self.ssplit(self.getline(fid))
139 values = numpy.fromfile(fid, dtype=float,
140 count=int(info[0]), sep=' ')
141 self.init_mopo = {k: v for k, v in zip(names, values)}
142
143 if re_spectrum.match(line):
144 # read SSSS section: initial motor positions
145 info = self.ssplit(self.getline(fid))
146 self.nspectra = int(info[2])
147
148 if re_values.match(self.getline(fid)):
149 # read FFFF section: subspectrum data
150 nval = int(self.getline(fid))
151 # check if nval is multiple of npdone
152 if nval % self.header['npdone'] != 0:
153 raise InputError("File corrupted, wrong number of "
154 "data values (%d) found." % nval)
155
156 self._data.append(numpy.fromfile(fid, dtype=float,
157 count=nval, sep=' '))
158
159 if int(info[1]) == 0:
160 break
161 # make data columns accessible by names
162 data = numpy.reshape(self._data[0],
163 (self.header['npdone'],
164 nval // self.header['npdone']))
165 self.data = numpy.rec.fromrecords(
166 data, names=self.columns[self.header['manip']])
167
168 def __str__(self):
169 ostr = 'Numor: %d (%s)\n' % (self.runnumber, self.filename)
170 ostr += 'Comments: %s\n' % " ".join(
171 s for c in self.comments for s in c[1])
172 ostr += 'Npoints/Ndone: %(nkmes)d/%(npdone)d\n' % (self.header)
173 ostr += 'Nspectra: %d\n' % self.nspectra
174 ostr += 'Ncolumns: %s' % self.data.shape[1]
175 return ostr
176
177
178 def numor_scan(scannumbers, *args, **kwargs):
179 """
180 function to obtain the angular cooridinates as well as intensity values
181 saved in numor datafiles. Especially useful for combining several scans
182 into one data object.
183
184 Parameters
185 ----------
186 scannumbers : int or str or iterable
187 number of the numors, or list of numbers. This will be transformed to a
188 string and used as a filename
189 args : str, optional
190 names of the motors e.g.: 'omega', 'gamma'
191 kwargs : dict
192 keyword arguments are passed on to numorFile. e.g. 'path' for the files
193 directory
194
195 Returns
196 -------
197 [ang1, ang2, ...] : list
198 angular positions list, omitted if no args are given
199 data : ndarray
200 all the data values.
201
202 Examples
203 --------
204 >>> [om, gam], data = xu.io.numor_scan(414363, 'omega', 'gamma')
205 """
206
207 if isinstance(scannumbers, (str, int)):
208 scanlist = list([scannumbers])
209 elif isinstance(scannumbers, collections.abc.Iterable):
210 scanlist = scannumbers
211 else:
212 raise TypeError('scannumbers is of invalid type (%s)'
213 % type(scannumbers))
214
215 angles = dict.fromkeys(args)
216 for key in angles:
217 if not isinstance(key, str):
218 raise InputError("*arg values need to be strings with motornames")
219 angles[key] = numpy.zeros(0)
220 buf = numpy.zeros(0)
221 MAP = numpy.zeros(0)
222
223 for nr in scanlist:
224 scan = numorFile(str(nr), **kwargs)
225 sdata = scan.data
226 if MAP.dtype == numpy.float64:
227 MAP.dtype = sdata.dtype
228 # append scan data to MAP, where all data are stored
229 MAP = numpy.append(MAP, sdata)
230 # check type of scan
231 for i in range(len(args)):
232 motname = args[i]
233 scanlength = len(sdata)
234 try:
235 buf = sdata[motname]
236 except ValueError:
237 mv = [v for k, v in scan.init_mopo.items()
238 if motname in k][0]
239 buf = mv * numpy.ones(scanlength)
240 angles[motname] = numpy.concatenate((angles[motname], buf))
241
242 retval = []
243 for motname in args:
244 # create return values in correct order
245 retval.append(angles[motname])
246
247 if not args:
248 return MAP
249 elif len(args) == 1:
250 return retval[0], MAP
251 else:
252 return retval, MAP
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2012-2015 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import os.path
18 import time
19
20 import numpy
21
22 from .. import config
23 from ..exception import InputError
24 # relative imports from xrayutilities
25 from .helper import xu_open
26
27
28 class ImageReader(object):
29
30 """
31 parse CCD frames in the form of tiffs or binary data (``*.bin``)
32 to numpy arrays. ignore the header since it seems to contain
33 no useful data
34
35 The routine was tested so far with
36
37 1. RoperScientific files with 4096x4096 pixels created at Hasylab Hamburg,
38 which save an 16bit integer per point.
39 2. Perkin Elmer images created at Hasylab Hamburg with 2048x2048 pixels.
40 """
41
42 def __init__(self, nop1, nop2, hdrlen=0, flatfield=None, darkfield=None,
43 dtype=numpy.int16, byte_swap=False):
44 """
45 initialize the ImageReader reader, which includes setting the dimension
46 of the images as well as defining the data used for flat- and darkfield
47 correction!
48
49 Parameters
50 ----------
51 nop1, nop2 : int
52 number of pixels in the first and second dimension of the image
53 hdrlen : int, optional
54 length of the file header which should be ignored
55 flatfield : str or ndarray, optional
56 filename or data for flatfield correction. supported file types
57 include (.bin/.tif (also compressed .xz or .gz) and .npy files).
58 otherwise a 2D numpy array should be given
59 darkfield : str or ndarray, optional
60 filename or data for darkfield correction. same types as for flat
61 field are supported.
62 dtype : numpy.dtype, optional
63 datatype of the stored values (default: numpy.int16)
64 byte_swap : bool, optional
65 flag which determines bytes are swapped after reading
66 """
67
68 # save number of pixels per image
69 self.nop1 = nop1
70 self.nop2 = nop2
71 self.dtype = dtype
72 self.hdrlen = hdrlen
73 self.byteswap = byte_swap
74
75 # read flatfield
76 if flatfield:
77 if isinstance(flatfield, str):
78 if os.path.splitext(flatfield)[1] == '.npy':
79 self.flatfield = numpy.load(flatfield)
80 elif os.path.splitext(flatfield)[1] in \
81 ['.gz', '.xz', '.bin', '.tif']:
82 # read without flatc and darkc
83 self.flatfield = self.readImage(flatfield)
84 else:
85 raise InputError("Error: unknown filename for "
86 "flatfield correction!")
87 elif isinstance(flatfield, numpy.ndarray):
88 self.flatfield = flatfield
89 else:
90 raise InputError("Error: unsupported type for "
91 "flatfield correction!")
92
93 # read darkfield
94 if darkfield:
95 if isinstance(darkfield, str):
96 if os.path.splitext(darkfield)[1] == '.npy':
97 self.darkfield = numpy.load(darkfield)
98 elif os.path.splitext(darkfield)[1] in \
99 ['.gz', '.xz', '.bin', '.tif']:
100 # read without flatc and darkc
101 self.darkfield = self.readImage(darkfield)
102 else:
103 raise InputError("Error: unknown filename for "
104 "darkfield correction!")
105 elif isinstance(darkfield, numpy.ndarray):
106 self.darkfield = darkfield
107 else:
108 raise InputError("Error: unsupported type for "
109 "darkfield correction!")
110
111 if flatfield:
112 self.flatc = True
113 if config.VERBOSITY >= config.INFO_ALL:
114 print("XU.io.ImageReader: flatfield correction enabled")
115 else:
116 self.flatc = False
117 if darkfield:
118 self.darkc = True
119 if config.VERBOSITY >= config.INFO_ALL:
120 print("XU.io.ImageReader: darkfield correction enabled")
121 else:
122 self.darkc = False
123
124 def readImage(self, filename, path=None):
125 """
126 read image file
127 and correct for dark- and flatfield in case the necessary data are
128 available.
129
130 returned data = ((image data)-(darkfield))/flatfield*average(flatfield)
131
132 Parameters
133 ----------
134 filename : str
135 filename of the image to be read. so far only single filenames are
136 supported. The data might be compressed. supported extensions:
137 .tif, .bin and .bin.xz
138 path : str, optional
139 path of the data files
140 """
141 if path:
142 full_filename = os.path.join(path, filename)
143 else:
144 full_filename = filename
145
146 if config.VERBOSITY >= config.INFO_ALL:
147 print("XU.io.ImageReader.readImage: file %s" % (full_filename))
148 t1 = time.time()
149
150 with xu_open(full_filename) as fh:
151 # jump over header
152 fh.seek(self.hdrlen)
153 # read image
154 rlen = numpy.dtype(self.dtype).itemsize * self.nop1 * self.nop2
155 img = numpy.frombuffer(fh.read(rlen), dtype=self.dtype)
156 if self.byteswap:
157 img = img.byteswap()
158 img.shape = (self.nop1, self.nop2) # reshape the data
159 # darkfield correction
160 if self.darkc:
161 img = (img - self.darkfield).astype(numpy.float32)
162 # flatfield correction
163 if self.flatc:
164 img = img.astype(numpy.float32) / self.flatfield
165
166 if config.VERBOSITY >= config.INFO_ALL:
167 t2 = time.time()
168 print("XU.io.ImageReader.readImage: parsing time %8.3f"
169 % (t2 - t1))
170
171 return img
172
173
174 dlen = {'char': 1,
175 'byte': 1,
176 'word': 2,
177 'dword': 4,
178 'rational': 8, # not implemented correctly
179 'float': 4,
180 'double': 8}
181
182 dtypes = {1: 'byte',
183 2: 'char',
184 3: 'word',
185 4: 'dword',
186 5: 'rational', # not implemented correctly
187 6: 'byte',
188 7: 'byte',
189 8: 'word',
190 9: 'dword',
191 10: 'rational', # not implemented correctly
192 11: 'float',
193 12: 'double'}
194
195 nptyp = {1: numpy.byte,
196 2: numpy.char,
197 3: numpy.uint16,
198 4: numpy.uint32,
199 5: numpy.uint32,
200 6: numpy.int8,
201 7: numpy.byte,
202 8: numpy.int16,
203 9: numpy.int32,
204 10: numpy.int32,
205 11: numpy.float32,
206 12: numpy.float64}
207
208 tiffdtype = {1: {8: numpy.uint8, 16: numpy.uint16, 32: numpy.uint32},
209 2: {8: numpy.int8, 16: numpy.int16, 32: numpy.int32},
210 3: {16: numpy.float16, 32: numpy.float32}}
211
212 tifftags = {256: 'ImageWidth', # width
213 257: 'ImageLength', # height
214 258: 'BitsPerSample',
215 259: 'Compression',
216 262: 'PhotometricInterpretation',
217 272: 'Model',
218 273: 'StripOffsets',
219 282: 'XResolution',
220 283: 'YResolution',
221 305: 'Software',
222 339: 'SampleFormat'}
223
224
225 class TIFFRead(ImageReader):
226 """
227 class to Parse a TIFF file including extraction of information from the
228 file header in order to determine the image size and data type
229
230 The data stored in the image are available in the 'data' property.
231 """
232
233 def __init__(self, filename, path=None):
234 """
235 initialization of the class which will prepare the parser and parse
236 the files content into class properties
237
238 Parameters
239 ----------
240 filename : str
241 file name of the TIFF-like image file
242 path : str, optional
243 path of the data file
244 """
245 if path:
246 full_filename = os.path.join(path, filename)
247 else:
248 full_filename = filename
249
250 with xu_open(full_filename, 'rb') as fh:
251 self.byteorder = fh.read(2*dlen['char'])
252 self.version = numpy.frombuffer(fh.read(dlen['word']),
253 dtype=numpy.uint16)[0]
254 if self.byteorder not in (b'II', b'MM') or self.version != 42:
255 raise TypeError("Not a TIFF file (%s)" % filename)
256 if self.byteorder != b'II':
257 raise NotImplementedError("The 'MM' byte order is not yet "
258 "implemented, please file a bug!")
259
260 fh.seek(4)
261 self.ifdoffset = numpy.frombuffer(fh.read(dlen['dword']),
262 dtype=numpy.uint32)[0]
263 fh.seek(self.ifdoffset)
264
265 self.ntags = numpy.frombuffer(fh.read(dlen['word']),
266 dtype=numpy.uint16)[0]
267
268 self._parseImgTags(fh, self.ntags)
269
270 fh.seek(self.ifdoffset + 2 + 12 * self.ntags)
271 nextimgoffset = numpy.frombuffer(fh.read(dlen['dword']),
272 dtype=numpy.uint32)[0]
273 if nextimgoffset != 0:
274 raise NotImplementedError("Multiple images per file are not "
275 "supported, please file a bug!")
276
277 # check if image type is supported
278 if self.imgtags.get('Compression', 1) != 1:
279 raise NotImplementedError("Compression is not supported, "
280 "please file a bug report!")
281 if self.imgtags.get('PhotometricInterpretation', 0) not in (0, 1):
282 raise NotImplementedError("RGB and colormap is not supported")
283
284 sf = self.imgtags.get('SampleFormat', 1)
285 bs = self.imgtags.get('BitsPerSample', 1)
286 if isinstance(self.imgtags['StripOffsets'], numpy.ndarray):
287 hdrlen = self.imgtags['StripOffsets'][0]
288 else:
289 hdrlen = self.imgtags['StripOffsets']
290 ImageReader.__init__(self,
291 self.imgtags['ImageLength'],
292 self.imgtags['ImageWidth'],
293 hdrlen=hdrlen,
294 dtype=tiffdtype[sf][bs],
295 byte_swap=False)
296
297 self.data = self.readImage(filename, path)
298
299 def _parseImgTags(self, fh, ntags):
300 """
301 parse TIFF image tags from Image File Directory header
302
303 Parameters
304 ----------
305 fh : file-handle
306 file handle of the TIFF file
307 ntags : int
308 number of tags in the Image File Directory
309 """
310
311 self.imgtags = {}
312 for i in range(ntags):
313 ftag = numpy.frombuffer(fh.read(dlen['word']),
314 dtype=numpy.uint16)[0]
315 ftype = numpy.frombuffer(fh.read(dlen['word']),
316 dtype=numpy.uint16)[0]
317 flength = numpy.frombuffer(fh.read(dlen['dword']),
318 dtype=numpy.uint32)[0]
319 fdoffset = numpy.frombuffer(fh.read(dlen['dword']),
320 dtype=numpy.uint32)[0]
321
322 pos = fh.tell()
323 if flength*dlen[dtypes[ftype]] <= 4:
324 fdoffset = pos - dlen['dword']
325 fh.seek(fdoffset)
326 if ftype == 2:
327 fdata = fh.read(flength * dlen[dtypes[ftype]]).decode("ASCII")
328 fdata = fdata.rstrip('\0')
329 else:
330 rlen = flength * dlen[dtypes[ftype]]
331 fdata = numpy.frombuffer(fh.read(rlen), dtype=nptyp[ftype])
332 if flength == 1:
333 fdata = fdata[0]
334 fh.seek(pos)
335
336 # add field to tags
337 self.imgtags[tifftags.get(ftag, ftag)] = fdata
338
339
340 class PerkinElmer(ImageReader):
341 """
342 parse PerkinElmer CCD frames (``*.tif``) to numpy arrays
343 Ignore the header since it seems to contain no useful data
344
345 The routine was tested only for files with 2048x2048 pixel images
346 created at Hasylab Hamburg which save an 32bit float per point.
347 """
348
349 def __init__(self, **keyargs):
350 """
351 initialize the PerkinElmer reader, which includes setting the dimension
352 of the images as well as defining the data used for flat- and darkfield
353 correction!
354
355 Parameters
356 ----------
357 flatfield : str or ndarray, optional
358 filename or data for flatfield correction. supported file types
359 include (.bin .bin.xz and .npy files). otherwise a 2D numpy
360 array should be given
361 darkfield : str or ndarray, optional
362 filename or data for darkfield correction. same types as for flat
363 field are supported.
364 """
365
366 ImageReader.__init__(self, 2048, 2048, hdrlen=8, dtype=numpy.float32,
367 byte_swap=False, **keyargs)
368
369
370 class RoperCCD(ImageReader):
371
372 """
373 parse RoperScientific CCD frames (``*.bin``) to numpy arrays
374 Ignore the header since it seems to contain no useful data
375
376 The routine was tested only for files with 4096x4096 pixel images
377 created at Hasylab Hamburg which save an 16bit integer per point.
378 """
379
380 def __init__(self, **keyargs):
381 """
382 initialize the RoperCCD reader, which includes setting the dimension of
383 the images as well as defining the data used for flat- and darkfield
384 correction!
385
386 Parameters
387 ----------
388 flatfield : str or ndarray, optional
389 filename or data for flatfield correction. supported file types
390 include (.bin .bin.xz and .npy files). otherwise a 2D numpy
391 array should be given
392 darkfield : str or ndarray, optional
393 filename or data for darkfield correction. same types as for flat
394 field are supported.
395 """
396
397 ImageReader.__init__(self, 4096, 4096, hdrlen=216, dtype=numpy.int16,
398 byte_swap=False, **keyargs)
399
400
401 class Pilatus100K(ImageReader):
402 """
403 parse Dectris Pilatus 100k frames (``*.tiff``) to numpy arrays
404 Ignore the header since it seems to contain no useful data
405 """
406
407 def __init__(self, **keyargs):
408 """
409 initialize the Piulatus100k reader, which includes setting the
410 dimension of the images as well as defining the data used for flat- and
411 darkfield correction!
412
413 Parameters
414 ----------
415 flatfield : str or ndarray, optional
416 filename or data for flatfield correction. supported file types
417 include (.bin .bin.xz and .npy files). otherwise a 2D numpy
418 array should be given
419 darkfield : str or ndarray, optional
420 filename or data for darkfield correction. same types as for flat
421 field are supported.
422 """
423
424 ImageReader.__init__(self, 195, 487, hdrlen=4096, dtype=numpy.int32,
425 byte_swap=False, **keyargs)
426
427
428 def get_tiff(filename, path=None):
429 """
430 read tiff image file and return the data
431
432 Parameters
433 ----------
434 filename : str
435 filename of the image to be read. so far only single filenames are
436 supported. The data might be compressed.
437 path : str, optional
438 path of the data file
439 """
440
441 if config.VERBOSITY >= config.INFO_ALL:
442 print("XU.io.get_tiff: file %s" % (filename))
443 t1 = time.time()
444
445 t = TIFFRead(filename, path=path)
446
447 if config.VERBOSITY >= config.INFO_ALL:
448 t2 = time.time()
449 print("XU.io.get_tiff: parsing time %8.3f" % (t2 - t1))
450
451 return t.data
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 Panalytical XML (www.XRDML.com) data file parser
19
20 based on the native python xml.dom.minidom module.
21 want to keep the number of dependancies as small as possible
22 """
23
24 import os.path
25 import warnings
26 from xml.etree import cElementTree as ElementTree
27
28 import numpy
29
30 from .. import config
31 from .helper import xu_open
32
33
34 class XRDMLMeasurement(object):
35
36 """
37 class to handle scans in a XRDML datafile
38 """
39
40 def __init__(self, measurement, namespace=''):
41 """
42 initialization routine for a XRDML measurement which parses are all
43 scans within this measurement.
44 """
45
46 self.namespace = namespace
47 # get scans in <xrdMeasurement>
48 slist = measurement.findall(self.namespace + "scan")
49
50 self.hkl = (numpy.nan, numpy.nan, numpy.nan)
51 self.material = ""
52 self.ddict = {}
53 for field in ["countTime", "detector", "counts",
54 "beamAttenuationFactors", "hkl"]:
55 self.ddict[field] = []
56 is_scalar = 0
57
58 # loop over all scan entries - scan points
59 for s in slist:
60 # check if scan is complete
61 scanstatus = s.get("status")
62 if scanstatus in ("Aborted", "Not finished") and len(slist) > 1:
63 if config.VERBOSITY >= config.INFO_LOW:
64 print("XU.io.XRDMLFile: subscan has been aborted "
65 "(part of the data unavailable)!")
66 else:
67 self.scanmotname = s.get("scanAxis")
68 reflection = s.find(self.namespace + "reflection")
69 if reflection:
70 m = reflection.find(self.namespace + "material")
71 if m is not None:
72 self.material = m.text
73 hkl = reflection.find(self.namespace + "hkl")
74 if hkl:
75 hkl_h = int(hkl.find(self.namespace + "h").text)
76 hkl_k = int(hkl.find(self.namespace + "k").text)
77 hkl_l = int(hkl.find(self.namespace + "l").text)
78 self.hkl = (hkl_h, hkl_k, hkl_l)
79 points = s.find(self.namespace + "dataPoints")
80
81 # add count time to output data
82 countTime = points.find(self.namespace +
83 "commonCountingTime").text
84 self.ddict["countTime"].append(float(countTime))
85
86 # check for intensities first to get number of points in scan
87 int_elem = points.find(self.namespace + "intensities")
88 if int_elem is not None:
89 data = int_elem.text
90 hascounts = False
91 else:
92 ct_elem = points.find(self.namespace + "counts")
93 ct_npy = numpy.fromstring(ct_elem.text, sep=" ")
94 self.ddict["counts"].append(ct_npy.tolist())
95 data = ct_elem.text
96 hascounts = True
97 # count time normalization; output is counts/sec
98 ct_rate = numpy.fromstring(data, sep=" ") / float(countTime)
99 nofpoints = ct_rate.size
100 # if present read beamAttenuationFactors
101 # they are already corrected in the data file, but may be
102 # interesting
103 attfact = points.find(self.namespace +
104 "beamAttenuationFactors")
105 if attfact is not None:
106 atten = numpy.fromstring(attfact.text, sep=" ")
107 atten_list = atten.tolist()
108 self.ddict["beamAttenuationFactors"].append(atten_list)
109 hasatten = True
110 else:
111 hasatten = False
112
113 if hascounts and hasatten:
114 self.ddict["detector"].append((ct_rate*atten).tolist())
115 else:
116 self.ddict["detector"].append(ct_rate.tolist())
117
118 # read the axes position
119 pos = points.findall(self.namespace + "positions")
120 for p in pos:
121 # read axis name and unit
122 aname = p.get("axis")
123 # aunit = p.get("unit")
124
125 # read axis data
126 listp = p.findall(self.namespace + "listPositions")
127 s = p.findall(self.namespace + "startPosition")
128 e = p.findall(self.namespace + "endPosition")
129 c = p.find(self.namespace + "commonPosition")
130 if listp: # listPositions
131 listp = listp[0]
132 data_list = numpy.fromstring(listp.text, sep=" ")
133 data_list = data_list.tolist()
134 elif s and e: # start endPosition
135 data_list = numpy.linspace(
136 float(s[0].text), float(e[0].text),
137 nofpoints).tolist()
138 elif c is not None: # commonPosition
139 data_list = numpy.fromstring(c.text, sep=" ")
140 data_list = data_list.tolist()
141 is_scalar = 1
142 else:
143 raise ValueError(
144 "no positions for axis {} found".format(aname))
145
146 # have to append the data to the data dictionary in case
147 # the scan is complete!
148 if aname not in self.ddict:
149 self.ddict[aname] = []
150 if not is_scalar:
151 self.ddict[aname].append(data_list)
152 else:
153 self.ddict[aname].append(data_list[0])
154 is_scalar = 0
155
156 # finally all scan data needs to be converted to numpy arrays
157 for k in self.ddict:
158 self.ddict[k] = numpy.array(self.ddict[k])
159
160 # flatten output if only one scan was present
161 if len(slist) == 1:
162 for k in self.ddict:
163 self.ddict[k] = numpy.ravel(self.ddict[k])
164
165 # save scanmot-values and detector counts in special arrays
166 if self.scanmotname in ['2Theta-Omega', 'Gonio']:
167 self.scanmot = self.ddict['2Theta']
168 elif self.scanmotname == 'Omega-2Theta':
169 self.scanmot = self.ddict['Omega']
170 elif self.scanmotname in self.ddict:
171 self.scanmot = self.ddict[self.scanmotname]
172 else:
173 warnings.warn('XU.io: unknown scan motor name in XRDML-File')
174 self.int = self.ddict['detector']
175
176 def __getitem__(self, key):
177 return self.ddict[key]
178
179 def __str__(self):
180 ostr = "XRDML Measurement\n"
181 if self.material:
182 ostr += "Material: '%s'; hkl: %s\n" % (self.material,
183 str(self.hkl))
184 for k in self.ddict:
185 ostr += "%s with %s points\n" % (k, str(self.ddict[k].shape))
186
187 return ostr
188
189
190 class XRDMLFile(object):
191
192 """
193 class to handle XRDML data files. The class is supplied with a file
194 name and uses the XRDMLScan class to parse the xrdMeasurement in the
195 file
196 """
197
198 def __init__(self, fname, path=""):
199 """
200 initialization routine supplied with a filename
201 the file is automatically parsed and the data are available
202 in the "scan" object. If more <xrdMeasurement> tags are present, which
203 should not be the case, their data is present in the "scans" object.
204
205 Parameters
206 ----------
207 fname : str
208 filename of the XRDML file
209 path : str, optional
210 path to the XRDML file
211 """
212 self.full_filename = os.path.join(path, fname)
213 self.filename = os.path.basename(self.full_filename)
214 with xu_open(self.full_filename) as fid:
215 d = ElementTree.parse(fid)
216 root = d.getroot()
217 try:
218 namespace = root.tag[:root.tag.index('}')+1]
219 except ValueError:
220 namespace = ''
221
222 slist = root.findall(namespace+"xrdMeasurement")
223
224 # determine the number of scans in the file
225 self.nscans = len(slist)
226 self.scans = []
227 for s in slist:
228 self.scans.append(XRDMLMeasurement(s, namespace))
229
230 if self.nscans == 1:
231 self.scan = self.scans[0]
232
233 def __str__(self):
234 ostr = "XRDML File: %s\n" % self.filename
235 for s in self.scans:
236 ostr += s.__str__()
237
238 return ostr
239
240
241 def getxrdml_map(filetemplate, scannrs=None, path=".", roi=None):
242 """
243 parses multiple XRDML file and concatenates the results for parsing the
244 xrayutilities.io.XRDMLFile class is used. The function can be used for
245 parsing maps measured with the PIXCel 1D detector (and in limited way also
246 for data acquired with a point detector -> see getxrdml_scan instead).
247
248 Parameters
249 ----------
250 filetemplate : str
251 template string for the file names, can contain a %d which is replaced
252 by the scan number or be a list of filenames
253 scannrs : int or list, optional
254 scan number(s)
255 path : str, optional
256 common path to the filenames
257 roi : tuple, optional
258 region of interest for the PIXCel detector, for other measurements this
259 is not useful!
260
261 Returns
262 -------
263 om, tt, psd : ndarray
264 motor positions and data as flattened numpy arrays
265
266 Examples
267 --------
268 >>> om, tt, psd = xrayutilities.io.getxrdml_map("samplename_%d.xrdml",
269 >>> [1, 2], path="./data")
270 """
271 def getOmPixcel(omraw, ttraw):
272 """
273 function to reshape the Omega values into a form needed for
274 further treatment with xrayutilities
275 """
276 return (omraw[:, numpy.newaxis] * numpy.ones(ttraw.shape)).flatten()
277
278 # read raw data and convert to reciprocal space
279 om = numpy.zeros(0)
280 tt = numpy.zeros(0)
281 psd = numpy.zeros(0)
282 # create scan names
283 if scannrs is None:
284 files = [filetemplate]
285 else:
286 files = list()
287 if not getattr(scannrs, '__iter__', False):
288 scannrs = [scannrs]
289 for nr in scannrs:
290 files.append(filetemplate % nr)
291
292 # parse files
293 for f in files:
294 d = XRDMLFile(os.path.join(path, f))
295 s = d.scan
296 if len(s['detector'].shape) == 1:
297 raise TypeError("XU.getxrdml_map: This function can only be used "
298 "to parse reciprocal space map files")
299
300 if roi is None:
301 roi = [0, s['detector'].shape[1]]
302 if s['Omega'].size < s['2Theta'].size:
303 om = numpy.concatenate(
304 (om, getOmPixcel(s['Omega'], s['2Theta'][:, roi[0]:roi[1]])))
305 tt = numpy.concatenate(
306 (tt, s['2Theta'][:, roi[0]:roi[1]].flatten()))
307 elif s['Omega'].size > s['2Theta'].size:
308 om = numpy.concatenate((om, s['Omega'].flatten()))
309 tt = numpy.concatenate((
310 tt,
311 numpy.ravel(s['2Theta'][:, numpy.newaxis] *
312 numpy.ones(s['Omega'].shape))))
313 else:
314 om = numpy.concatenate((om, s['Omega'].flatten()))
315 tt = numpy.concatenate(
316 (tt, s['2Theta'][:, roi[0]:roi[1]].flatten()))
317 psd = numpy.concatenate(
318 (psd, s['detector'][:, roi[0]:roi[1]].flatten()))
319
320 return om, tt, psd
321
322
323 def getxrdml_scan(filetemplate, *motors, **kwargs):
324 """
325 parses multiple XRDML file and concatenates the results for parsing the
326 xrayutilities.io.XRDMLFile class is used. The function can be used for
327 parsing arbitrary scans and will return the the motor values of the scan
328 motor and additionally the positions of the motors given by in the
329 ``*motors`` argument
330
331 Parameters
332 ----------
333 filetemplate : str
334 template string for the file names, can contain a %d which is replaced
335 by the scan number or be a list of filenames given by the scannrs
336 keyword argument
337
338 motors : str
339 motor names to return: e.g.: 'Omega', '2Theta', ... one can also use
340 abbreviations:
341
342 - 'Omega' = 'om' = 'o'
343 - '2Theta' = 'tt' = 't'
344 - 'Chi' = 'c'
345 - 'Phi' = 'p'
346
347 scannrs : int or list, optional
348 scan number(s)
349 path : str, optional
350 common path to the filenames
351
352 Returns
353 -------
354 scanmot, mot1, mot2,..., detectorint : ndarray
355 motor positions and data as flattened numpy arrays
356
357 Examples
358 --------
359 >>> scanmot, om, tt, inte = xrayutilities.io.getxrdml_scan(
360 >>> "samplename_1.xrdml", 'om', 'tt', path="./data")
361 """
362 flatten = True
363 # parse keyword arguments
364 path = kwargs.get('path', '.')
365 scannrs = kwargs.get('scannrs', None)
366
367 validmotors = ['Omega', '2Theta', 'Psi', 'Chi', 'Phi', 'Z', 'X', 'Y']
368 validmotorslow = [mot.lower() for mot in validmotors]
369 # create correct motor names from input values
370 motnames = []
371 for mot in motors:
372 if mot.lower() in validmotorslow:
373 motnames.append(validmotors[validmotorslow.index(mot.lower())])
374 elif mot.lower() in ['phi', 'p']:
375 motnames.append('Phi')
376 elif mot.lower() in ['chi', 'c']:
377 motnames.append('Chi')
378 elif mot.lower() in ['psi']:
379 motnames.append('Psi')
380 elif mot.lower() in ['tt', 't']:
381 motnames.append('2Theta')
382 elif mot.lower() in ['om', 'o']:
383 motnames.append('Omega')
384 else:
385 raise ValueError("XU: invalid motor name given")
386
387 motvals = numpy.empty((len(motnames) + 1, 0))
388 detvals = numpy.empty(0)
389 # create scan names
390 if scannrs is None:
391 if isinstance(filetemplate, list):
392 files = filetemplate
393 else:
394 files = [filetemplate]
395 else:
396 files = list()
397 if not numpy.iterable(scannrs):
398 scannrs = [scannrs]
399 for nr in scannrs:
400 files.append(filetemplate % nr)
401
402 # parse files
403 if len(files) == 1:
404 flatten = False
405 for f in files:
406 d = XRDMLFile(os.path.join(path, f))
407 s = d.scan
408 detshape = s['detector'].shape
409
410 if len(detshape) == 2:
411 angles = numpy.ravel(s.scanmot)
412 angles.shape = (1, angles.size)
413 for mot in motnames:
414 if s[mot].shape != detshape:
415 angles = numpy.vstack((
416 angles,
417 numpy.ravel(s[mot][:, numpy.newaxis] *
418 numpy.ones(detshape))))
419 else:
420 angles = numpy.vstack((angles, numpy.ravel(s[mot])))
421 motvals = numpy.concatenate((motvals, angles), axis=1)
422 dval = numpy.ravel(s['detector'])
423 detvals = numpy.concatenate((detvals, dval))
424 if not flatten:
425 detvals.shape = detshape
426 motvals.shape = (len(motnames) + 1, detshape[0], detshape[1])
427 else:
428 detvals = numpy.concatenate((detvals, s['detector']))
429 angles = s.scanmot
430 angles.shape = (1, angles.size)
431 for mot in motnames:
432 try:
433 angles = numpy.vstack((angles, s[mot]))
434 except ValueError: # motor is not array
435 angles = numpy.vstack(
436 (angles, s[mot] * numpy.ones(detshape)))
437 motvals = numpy.concatenate((motvals, angles), axis=1)
438
439 # make return value
440 ret = []
441 for i in range(motvals.shape[0]):
442 ret.append(motvals[i, ...])
443 ret.append(detvals)
444 return ret
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2014 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import copy
18 import re
19 import shlex
20
21 import numpy
22
23 from .. import config
24 from . import xu_open
25
26 re_label = re.compile(r'^\s*_')
27 re_default = re.compile(r'^\s*_('
28 'pd_meas_counts_total|'
29 'pd_meas_intensity_total|'
30 'pd_proc_intensity_total|'
31 'pd_proc_intensity_net|'
32 'pd_calc_intensity_total|'
33 'pd_calc_intensity_net)')
34 re_loop = re.compile(r'^\s*loop_')
35 re_nop = re.compile(r'^\s*_(pd_meas_number_of_points|pd_meas_detector_id)')
36 re_multiline = re.compile(r';')
37
38
39 def remove_comments(line, sep='#'):
40 for s in sep:
41 line = line.split(s)[0]
42 return line
43
44
45 class pdCIF(object):
46
47 """
48 the class implements a primitive parser for pdCIF-like files. It reads
49 every entry and collects the information in the header attribute. The first
50 loop containing one of the intensity fields is assumed to be the data the
51 user is interested in and is transfered to the data array which is stored
52 as numpy record array the columns can be accessed by name
53
54 intensity fields:
55
56 - `_pd_meas_counts_total`
57 - `_pd_meas_intensity_total`
58 - `_pd_proc_intensity_total`
59 - `_pd_proc_intensity_net`
60 - `_pd_calc_intensity_total`
61 - `_pd_calc_intensity_net`
62
63 alternatively the data column name can be given as argument to the
64 constructor
65 """
66
67 def __init__(self, filename, datacolumn=None):
68 """
69 contructor of the pdCIF class
70
71 Parameters
72 ----------
73 filename : str
74 filename of the file to be parsed
75 datacolumn : str, optional
76 name of data column to identify the data loop (default =None; means
77 that a list of default names is used)
78 """
79 self.filename = filename
80 self.datacolumn = datacolumn
81 self.header = {}
82 self.data = None
83
84 self.Parse()
85
86 def Parse(self):
87 """
88 parser of the pdCIF file. the method reads the data from the file and
89 fills the data and header attributes with content
90 """
91 with xu_open(self.filename) as fh:
92 self._parse_single(fh)
93
94 def _parse_single(self, fh, breakAfterData=False):
95 """
96 internal routine to parse a single loop of the pdCIF file
97
98 Parameters
99 ----------
100 fh : file-handle
101 breakAfterData : bool, optional
102 allowing to stop the parsing after data loop was found
103 (default:False)
104 """
105 loopStart = False
106 dataLoop = False
107 dataDone = False
108 loopheader = []
109 numOfEntries = -1
110 multiline = None
111
112 while True:
113 line = fh.readline().decode('ascii')
114 if not line:
115 break
116
117 line = remove_comments(line)
118 if re_loop.match(line):
119 loopStart = True
120 remainingline = re.sub('loop_', '', line).strip()
121 if re_label.match(remainingline):
122 if ((self.datacolumn is None and re_default.match(line)) or
123 line.strip() == self.datacolumn):
124 dataLoop = True
125 loopheader.append(remainingline)
126 continue
127
128 if multiline:
129 multiline += line
130 if re_multiline.match(line): # end of multiline
131 val = multiline
132 self.header[label] = val
133 multiline = None
134 continue
135
136 if re_label.match(line) and not loopStart:
137 # parse header
138 split = line.split(None, 1)
139 label = split[0].strip()
140 try:
141 val = split[1].strip()
142 self.header[label] = val
143 # convert data format of header line
144 if re_nop.match(line):
145 numOfEntries = int(val)
146 try:
147 self.header[label] = float(val)
148 except ValueError:
149 self.header[label] = val
150 except IndexError:
151 # try if multiline
152 line2 = fh.readline().decode('ascii')
153 if re_multiline.match(line2):
154 multiline = line2
155 else: # single value must be in second line
156 self.header[label] = line2
157
158 elif re_label.match(line) and loopStart:
159 # read loop entries
160 if ((self.datacolumn is None and re_default.match(line)) or
161 line.strip() == self.datacolumn):
162 dataLoop = True
163 loopheader.append(line.strip())
164
165 elif loopStart:
166 fh.seek(fh.tell() - len(line))
167 if numOfEntries != -1 and dataLoop and not dataDone:
168 self.data = self._parse_loop_numpy(fh, loopheader,
169 numOfEntries)
170 dataDone = True
171 if breakAfterData:
172 break
173 elif dataLoop and not dataDone:
174 self._parse_loop(fh, loopheader)
175 length = len(self.header[loopheader[0]])
176 dtypes = [(str(entry), type(self.header[entry][0]))
177 for entry in loopheader]
178 for i in range(len(dtypes)):
179 if dtypes[i][1] is str:
180 dtypes[i] = (str(dtypes[i][0]), numpy.str_, 64)
181 self.data = numpy.zeros(length, dtype=dtypes)
182 for entry in loopheader:
183 self.data[entry] = self.header.pop(entry)
184 dataDone = True
185 if breakAfterData:
186 break
187 else:
188 try:
189 self._parse_loop(fh, loopheader)
190 except ValueError:
191 if config.VERBOSITY >= config.INFO_LOW:
192 print('XU.io.pdCIF: unable to handle loop at %d'
193 % fh.tell())
194 dataLoop = False
195 loopStart = False
196 loopheader = []
197 numOfEntries = -1
198
199 def _parse_loop_numpy(self, filehandle, fields, nentry):
200 """
201 function to parse a loop using numpy routines
202
203 Parameters
204 ----------
205 filehandle : file-handle
206 filehandle object to use as data source
207 fields : iterable
208 field names in the loop
209 nentry : int
210 number of entries in the loop
211
212 Returns
213 -------
214 data : ndarray
215 data read from the file as numpy record array
216 """
217 tmp = numpy.fromfile(filehandle, count=nentry * len(fields), sep=' ')
218 data = numpy.rec.fromarrays(tmp.reshape((-1, len(fields))).T,
219 names=fields)
220 return data
221
222 def _parse_loop(self, filehandle, fields):
223 """
224 function to parse a loop using python loops routines. the fields are
225 added to the fileheader dictionary
226
227 Parameters
228 ----------
229 filehandle : file-handle
230 filehandle object to use as data source
231 fields : iterable
232 field names in the loop
233
234 """
235 fh = filehandle
236
237 for f in fields:
238 self.header[f] = []
239 while True:
240 line = fh.readline().decode('ascii')
241 if not line:
242 break
243
244 if re_label.match(line) or line.strip() == '':
245 fh.seek(fh.tell() - len(line))
246 break
247 row = shlex.split(line, comments=True)
248 for i in range(len(fields)):
249 try:
250 self.header[fields[i]].append(float(row[i]))
251 except ValueError:
252 self.header[fields[i]].append(row[i])
253 except IndexError: # maybe multiline field
254 line2 = fh.readline().decode('ascii')
255 line2 = remove_comments(line2)
256 if re_multiline.match(line2):
257 multiline = line2
258 while True:
259 line = fh.readline().decode('ascii')
260 line = remove_comments(line)
261 if not line:
262 fh.seek(fh.tell() - len(line))
263 break
264 if re_multiline.match(line) and line.strip()[1:]:
265 multiline += line
266 else:
267 self.header[fields[i]].append(multiline)
268 break
269 else:
270 fh.seek(fh.tell() - len(line2))
271 raise ValueError('a column is missing for label %s '
272 'in a loop' % fields[i])
273
274
275 class pdESG(pdCIF):
276
277 """
278 class for parsing multiple pdCIF loops in one file.
279 This includes especially ``*.esg`` files which are supposed to
280 consist of multiple loops of pdCIF data with equal length.
281
282 Upon parsing the class tries to combine the data of these different
283 scans into a single data matrix -> same shape of subscan data is assumed
284 """
285
286 def __init__(self, filename, datacolumn=None):
287 self.filename = filename
288 self.datacolumn = datacolumn
289 self.fileheader = {}
290 self.header = {}
291 self.data = None
292
293 self.Parse()
294
295 def Parse(self):
296 """
297 parser of the pdCIF file. the method reads the data from the file and
298 fills the data and header attributes with content
299 """
300 with xu_open(self.filename) as fh:
301 # parse first header and loop
302 self._parse_single(fh, breakAfterData=True)
303 self.fileheader = copy.deepcopy(self.header)
304 self.header = {}
305 fdata = self.data
306 datasize = self.data.size
307 nscan = 1
308 tell = 0
309 while True: # try to parse all scans
310 tell = fh.tell()
311 self._parse_single(fh, breakAfterData=True)
312 if tell == fh.tell():
313 break
314 # copy changing data from header
315 for key in self.header:
316 if key in self.fileheader:
317 if not isinstance(self.fileheader[key], list):
318 self.fileheader[key] = [self.fileheader[key], ]
319 self.fileheader[key].append(self.header[key])
320 else:
321 self.fileheader[key] = self.header[key]
322
323 fdata = numpy.append(fdata, self.data)
324 nscan += 1
325
326 # convert data for output to user
327 for key in self.fileheader:
328 if isinstance(self.fileheader[key], list):
329 self.fileheader[key] = numpy.array(self.fileheader[key])
330 self.data = numpy.empty(fdata.shape)
331 self.data[...] = fdata[...]
332 self.data.shape = (nscan, datasize)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2015-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17
18 """
19 class for reading data + header information from Rigaku RAS (3-column ASCII)
20 files
21
22 Such datafiles are generated by the Smartlab Guidance software from Rigaku.
23 """
24
25 import os.path
26 import re
27 from itertools import islice
28
29 import numpy
30 import numpy.lib.recfunctions
31
32 from .. import config
33 from ..exception import InputError
34 # relative imports from xrayutilities
35 from .helper import xu_open
36
37 re_measstart = re.compile(r"^\*RAS_DATA_START")
38 re_measend = re.compile(r"^\*RAS_DATA_END")
39 re_headerstart = re.compile(r"^\*RAS_HEADER_START")
40 re_headerend = re.compile(r"^\*RAS_HEADER_END")
41 re_datastart = re.compile(r"^\*RAS_INT_START")
42 re_dataend = re.compile(r"^\*RAS_INT_END")
43 re_scanaxis = re.compile(r"^\*MEAS_SCAN_AXIS_X_INTERNAL")
44 re_intstart = re.compile(r"^\*RAS_INT_START")
45 re_datestart = re.compile(r"^\*MEAS_SCAN_START_TIME")
46 re_datestop = re.compile(r"^\*MEAS_SCAN_END_TIME")
47 re_initmoponame = re.compile(r"^\*MEAS_COND_AXIS_NAME_INTERNAL")
48 re_initmopovalue = re.compile(r"^\*MEAS_COND_AXIS_POSITION")
49 re_datacount = re.compile(r"^\*MEAS_DATA_COUNT")
50 re_measspeed = re.compile(r"^\*MEAS_SCAN_SPEED ")
51 re_measstep = re.compile(r"^\*MEAS_SCAN_STEP ")
52
53
54 class RASFile(object):
55
56 """
57 Represents a RAS data file. The file is read during the
58 constructor call
59
60 Parameters
61 ----------
62 filename : str
63 name of the ras-file
64 path : str, optional
65 path to the data file
66 """
67
68 def __init__(self, filename, path=None):
69 self.filename = filename
70 if path is None:
71 self.full_filename = self.filename
72 else:
73 self.full_filename = os.path.join(path, self.filename)
74
75 self.scans = []
76 self.Read()
77
78 def Read(self):
79 """
80 Read the data from the file
81 """
82 with xu_open(self.full_filename) as fid:
83 while True:
84 t = fid.tell()
85 line = fid.readline()
86 line = line.decode('ascii', 'ignore')
87 if config.VERBOSITY >= config.DEBUG:
88 print("XU.io.RASFile: %d: '%s'" % (t, line))
89 if re_measstart.match(line):
90 continue
91 elif re_headerstart.match(line):
92 s = RASScan(self.full_filename, t)
93 self.scans.append(s)
94 fid.seek(s.fidend) # set handle to after scan
95 elif re_measend.match(line) or line in (None, ''):
96 break
97 else:
98 continue
99 if len(self.scans) > 0:
100 self.scan = self.scans[0]
101
102
103 class RASScan(object):
104
105 """
106 Represents a single Scan portion of a RAS data file. The scan is parsed
107 during the constructor call
108
109 Parameters
110 ----------
111 filename : str
112 file name of the data file
113 pos : int
114 seek position of the 'RAS_HEADER_START' line
115 """
116
117 def __init__(self, filename, pos):
118 self.filename = filename
119 self.fidpos = pos
120 self.fidend = pos
121 with xu_open(self.filename) as self.fid:
122 self.fid.seek(self.fidpos)
123 self._parse_header()
124 self._parse_data()
125 self.fidend = self.fid.tell()
126
127 def _parse_header(self):
128 """
129 Read the data from the file
130 """
131 # read header
132 self.header = []
133 keys = {}
134 position = {}
135 offset = self.fid.tell()
136 for line in self.fid:
137 offset += len(line)
138 line = line.decode('ascii', 'ignore')
139 self.header.append(line)
140 if config.VERBOSITY >= config.DEBUG:
141 print("XU.io.RASScan: %d: '%s'" % (offset, line))
142
143 if re_datestart.match(line):
144 m = line.split(' ', 1)[-1].strip()
145 self.scan_start = m.strip('"')
146 elif re_datestop.match(line):
147 m = line.split(' ', 1)[-1].strip()
148 self.scan_stop = m.strip('"')
149 elif re_initmoponame.match(line):
150 idx = int(line.split('-', 1)[-1].split()[0])
151 moname = line.split(' ', 1)[-1].strip().strip('"')
152 keys[idx] = moname
153 elif re_initmopovalue.match(line):
154 idx = int(line.split('-', 1)[-1].split()[0])
155 mopos = line.split(' ', 1)[-1].strip().strip('"')
156 try:
157 mopos = float(mopos)
158 except ValueError:
159 pass
160 position[idx] = mopos
161 elif re_scanaxis.match(line):
162 self.scan_axis = line.split(' ', 1)[-1].strip().strip('"')
163 elif re_datacount.match(line):
164 length = line.split(' ', 1)[-1].strip().strip('"')
165 self.length = int(float(length))
166 elif re_measspeed.match(line):
167 speed = line.split(' ', 1)[-1].strip().strip('"')
168 self.meas_speed = float(speed)
169 elif re_measstep.match(line):
170 step = line.split(' ', 1)[-1].strip().strip('"')
171 self.meas_step = float(step)
172 elif re_headerend.match(line):
173 break
174
175 # generate header dictionary
176 self.init_mopo = {}
177 for k in keys:
178 self.init_mopo[keys[k]] = position[k]
179 self.fid.seek(offset)
180
181 def _parse_data(self):
182 line = self.fid.readline().decode('ascii', 'ignore')
183 offset = self.fid.tell()
184 if re_datastart.match(line):
185 lines = islice(self.fid, self.length)
186 self.data = numpy.genfromtxt(lines)
187 self.data = numpy.rec.fromrecords(self.data,
188 names=[self.scan_axis,
189 'int',
190 'att'])
191 self.fid.seek(offset)
192 lines = islice(self.fid, self.length)
193 dlength = numpy.sum([len(line) for line in lines])
194 if config.VERBOSITY >= config.DEBUG:
195 print("XU.io.RASScan: offset %d; data-length %d"
196 % (offset, dlength))
197 self.fid.seek(offset + dlength)
198 else:
199 raise IOError('File handle at wrong position to read data!')
200
201
202 def getras_scan(scanname, scannumbers, *args, **kwargs):
203 """
204 function to obtain the angular cooridinates as well as intensity values
205 saved in RAS datafiles. Especially useful for reciprocal space map
206 measurements, and to combine date from several scans
207
208 further more it is possible to obtain even more positions from
209 the data file if more than two string arguments with its names are given
210
211 Parameters
212 ----------
213 scanname : str
214 name of the scans, for multiple scans this needs to be a template
215 string
216 scannumbers : int, tuple or list
217 number of the scans of the reciprocal space map
218 args : str, optional
219 names of the motors. to read reciprocal space maps measured in coplanar
220 diffraction give:
221
222 - omname: name of the omega motor (or its equivalent)
223 - ttname: name of the two theta motor (or its equivalent)
224
225 kwargs : dict
226 keyword arguments forwarded to RASFile function
227
228 Returns
229 -------
230 [ang1, ang2, ...] : list
231 angular positions are extracted from the respective scan header, or
232 motor positions during the scan. this is omitted if no `args` are given
233 rasdata : ndarray
234 the data values (includes the intensities e.g. rasdata['int']).
235
236 Examples
237 --------
238 >>> [om, tt], MAP = xu.io.getras_scan('text%05d.ras', 36, 'Omega',
239 >>> 'TwoTheta')
240 """
241
242 if isinstance(scannumbers, (list, tuple)):
243 scanlist = scannumbers
244 else:
245 scanlist = list([scannumbers])
246
247 angles = dict.fromkeys(args)
248 for key in angles:
249 if not isinstance(key, str):
250 raise InputError("*arg values need to be strings with motornames")
251 angles[key] = numpy.zeros(0)
252 buf = numpy.zeros(0)
253 MAP = numpy.zeros(0)
254
255 for nr in scanlist:
256 rasfile = RASFile(scanname % nr, **kwargs)
257 for scan in rasfile.scans:
258 sdata = scan.data
259 if MAP.dtype == numpy.float64:
260 MAP.dtype = sdata.dtype
261 # append scan data to MAP, where all data are stored
262 MAP = numpy.append(MAP, sdata)
263 # check type of scan
264 for i in range(len(args)):
265 motname = args[i]
266 scanlength = len(sdata)
267 try:
268 buf = sdata[motname]
269 except ValueError:
270 buf = scan.init_mopo[motname] * numpy.ones(scanlength)
271 angles[motname] = numpy.concatenate((angles[motname], buf))
272
273 retval = []
274 for motname in args:
275 # create return values in correct order
276 retval.append(angles[motname])
277
278 if not args:
279 return MAP
280 elif len(args) == 1:
281 return retval[0], MAP
282 else:
283 return retval, MAP
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 parser for the alignment log file of the rotating anode
19 """
20
21 import re
22
23 import numpy
24
25 from .. import config, utilities
26 from .helper import xu_open
27
28 LOG_comment = re.compile(r"^#C")
29 LOG_peakname = re.compile(r"^#P")
30 LOG_motorname = re.compile(r"^#M")
31 LOG_datetime = re.compile(r"^#D")
32 LOG_tagline = re.compile(r"^#")
33 # denotes a numeric value
34 LOG_num_value = re.compile(r"[+-]*\d*\.*\d*e*[+-]*\d+")
35
36
37 class RA_Alignment(object):
38
39 """
40 class to parse the data file created by the alignment routine
41 (tpalign) at the rotating anode spec installation
42
43 this routine does an iterative alignment procedure and saves the
44 center of mass values were it moves after each scan. It iterates
45 between two different peaks and iteratively aligns at each peak between
46 two different motors (om/chi at symmetric peaks, om/phi at asymmetric
47 peaks)
48 """
49
50 def __init__(self, filename):
51 """
52 initialization function to initialize the objects variables and
53 opens the file
54
55 Parameters
56 ----------
57 filename : str
58 filename of the alignment log file
59 """
60
61 self.filename = filename
62 try:
63 self.fid = xu_open(self.filename)
64 except OSError:
65 self.fid = None
66 raise IOError("error opening alignment log file %s"
67 % self.filename)
68
69 self.peaks = []
70 self.alignnames = []
71 self.motorpos = []
72 self.intensities = []
73 self.iterations = []
74
75 self.Parse()
76
77 def Parse(self):
78 """
79 parser to read the alignment log and obtain the aligned values
80 at every iteration.
81 """
82
83 currentpeakname = None
84 currentmotname = None
85 opencommenttag = False
86 dataline = False
87 iteration = 0
88
89 if self.fid is None:
90 raise Exception("RA_Alignment: file was not opened by "
91 "initialization!")
92
93 for line in self.fid.readlines():
94 # for loop to read every line in the file
95 line = line.decode('ascii')
96
97 # check for new tag in the current line
98 if LOG_tagline.match(line):
99 opencommenttag = False
100
101 if LOG_comment.match(line):
102 # comment line or block starts
103 opencommenttag = True
104 continue
105
106 elif LOG_datetime.match(line):
107 # data is so far ignored
108 continue
109
110 elif LOG_peakname.match(line):
111 # line with peak name found
112 pname = LOG_peakname.sub("", line)
113 pname = pname.strip()
114 # check if we found a new peakname
115 try:
116 self.peaks.index(pname)
117 except ValueError:
118 self.peaks.append(pname)
119 currentpeakname = pname # set current peak name
120 iteration += 1 # increment iteration counter
121
122 elif LOG_motorname.match(line):
123 # line with motorname is found
124 motname = LOG_motorname.sub("", line)
125 motname = motname.strip()
126 # check if a peakname is already set
127 if currentpeakname is None:
128 if config.VERBOSITY >= config.INFO_LOW:
129 print("RA_Alignment: Warning: a peakname should "
130 "be given before a motor data line")
131 currentpeakname = "somepeak"
132 currentmotname = currentpeakname + "_" + motname
133 # check if we found a new peak/motor name combination
134 try:
135 self.alignnames.index(currentmotname)
136 except ValueError:
137 # new peak/motor combination
138 self.alignnames.append(currentmotname)
139 # create necessary data structures
140 self.motorpos.append([])
141 self.intensities.append([])
142 self.iterations.append([])
143 # next line contains motor position and intensity
144 dataline = True
145 elif opencommenttag:
146 # ignore line because it is part of a comment block
147 continue
148
149 elif dataline:
150 # dataline with motorposition and intensity is found
151 line_list = LOG_num_value.findall(line)
152 idx = self.alignnames.index(currentmotname)
153 self.motorpos[idx].append(float(line_list[0]))
154 self.intensities[idx].append(float(line_list[1]))
155 self.iterations[idx].append(iteration)
156 dataline = False
157
158 # convert data to numpy array and combine position and intensity
159 self.data = []
160 for i, k in enumerate(self.keys()):
161 self.data.append(numpy.array((self.motorpos[i],
162 self.intensities[i],
163 self.iterations[i])))
164
165 def __str__(self):
166 """
167 returns a string describing the content of the alignment file
168 """
169 ostr = ""
170 ostr += "Peaknames: " + repr(self.peaks) + "\n"
171 ostr += "aligned values: " + repr(self.alignnames)
172 return ostr
173
174 def __del__(self):
175 try:
176 self.fid.close()
177 except AttributeError:
178 pass
179
180 def keys(self):
181 """
182 returns a list of keys for which aligned values were parsed
183 """
184 return self.alignnames
185
186 def get(self, key):
187 return self.__getitem__(key)
188
189 def __getitem__(self, key):
190 """
191 returns the values to the corresponding key
192 """
193 if key in self.alignnames:
194 i = self.alignnames.index(key)
195 return self.data[i]
196 else:
197 raise KeyError("RA_Alignment: unknown key given!")
198
199 def plot(self, pname):
200 """
201 function to plot the alignment history for a given peak
202
203 Parameters
204 ----------
205 pname : str
206 peakname for which the alignment should be plotted
207 """
208 flag, plt = utilities.import_matplotlib_pyplot('XU.io.RA_ALignment')
209 if not flag:
210 return
211
212 if pname not in self.peaks:
213 print("RA_Alignment.plot: error peakname not found!")
214 return
215
216 # get number aligned axis for the current peak
217 axnames = []
218 for k in self.keys():
219 if k.find(pname) >= 0:
220 axnames.append(k)
221
222 fig, ax = plt.subplots(nrows=len(axnames), sharex=True)
223
224 for an, axis in zip(axnames, ax):
225 d = self.get(an)
226 plt.sca(axis)
227 plt.plot(d[2], d[0], 'k.-')
228 plt.ylabel(re.sub(pname + "_", "", an))
229 axis.twinx()
230 plt.plot(d[2], d[1], 'r.-')
231 plt.ylabel("Int (cps)", color='r')
232 plt.grid()
233
234 plt.xlabel("Peak iteration number")
235 plt.suptitle(pname)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2013 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 a set of routines to convert Seifert ASCII files to HDF5
20 in fact there exist two posibilities how the data is stored (depending on the
21 use detector):
22
23 1. as a simple line scan (using the point detector)
24 2. as a map using the PSD
25
26 In the first case the data ist stored
27 """
28
29 import itertools
30 import os.path
31 import re
32
33 import numpy
34
35 from .. import config
36 from .helper import xu_open
37
38 # define some regular expressions
39 nscans_re = re.compile(r"^&NumScans=\d+")
40 scan_data_re = re.compile(r"^\#Values\d+")
41 scan_partab_re = re.compile(r"^\#ScanTableParameter")
42 novalues_re = re.compile(r"^&NoValues=\d+")
43 scanaxis_re = re.compile(r"^&ScanAxis=.*")
44
45 # some constant regular expressions
46 re_measparams = re.compile(r"#MeasParameter")
47 re_rsmparams = re.compile(r"#RsmParameter")
48 re_data = re.compile(r"#Values")
49 re_keyvalue = re.compile(r"&\S+=\S")
50 re_invalidkeyword = re.compile(r"^\d+\S")
51 re_multiblank = re.compile(r"\s+")
52 re_position = re.compile(r"&Pos=[+-]*\d*\.\d*")
53 re_start = re.compile(r"&Start=[+-]*\d*\.\d*")
54 re_end = re.compile(r"&End=[+-]*\d*\.\d*")
55 re_step = re.compile(r"&Step=[+-]*\d*\.\d*")
56 re_time = re.compile(r"&Time=\d*.\d*")
57 re_stepscan = re.compile(r"^&Start")
58 re_dataline = re.compile(r"^[+-]*\d*\.\d*")
59 re_absorber = re.compile(r"^&Axis=A6")
60
61
62 def repair_key(key):
63 """
64 Repair a key string in the sense that the string is changed in a way that
65 it can be used as a valid Python identifier. For that purpose all blanks
66 within the string will be replaced by _ and leading numbers get an
67 preceeding _.
68 """
69
70 if re_invalidkeyword.match(key):
71 key = "_" + key
72
73 # now replace all blanks
74 key = key.replace(" ", "_")
75
76 return key
77
78
79 class SeifertHeader(object):
80 """
81 helper class to represent a Seifert (NJA) scan file header
82 """
83
84 def __init__(self):
85 pass
86
87 def __str__(self):
88 ostr = ""
89 for k in self.__dict__.keys():
90 value = self.__getattribute__(k)
91 if isinstance(value, float):
92 ostr += k + " = %f\n" % value
93 else:
94 ostr += k + " = %s\n" % value
95
96 return ostr
97
98
99 class SeifertMultiScan(object):
100 """
101 Class to parse a Seifert (NJA) multiscan file
102 """
103
104 def __init__(self, filename, m_scan, m2, path=""):
105 """
106 Parse data from a multiscan Seifert file.
107
108 Parameters
109 ----------
110 filename : str
111 name of the NJA file
112 m_scan : str
113 name of the scan axis
114 m2 : str
115 name of the second moving motor
116 path : str, optional
117 path to the datafile
118 """
119 self.Filename = os.path.join(path, filename)
120
121 self.nscans = 0 # total number of scans
122 self.npscan = 0 # number of points per scan
123 self.ctime = 0 # counting time
124 self.re_m2 = re.compile(r"^&Axis=%s\s+&Task=Drive" % m2)
125 self.re_sm = re.compile(r"^&ScanAxis=%s" % m_scan)
126 self.scan_motor_name = m_scan
127 self.sec_motor_name = m2
128
129 self.m2_pos = []
130 self.sm_pos = []
131 self.data = []
132 self.n_sm_pos = 0
133
134 with xu_open(self.Filename) as self.fid:
135 if config.VERBOSITY >= config.INFO_LOW:
136 print("XU.io.SeifertScan: parsing file: %s" % self.Filename)
137 self.parse()
138
139 def parse(self):
140 self.data = []
141 m2_tmppos = None
142 self.sm_pos = []
143 self.m2_pos = []
144
145 # flag to check if all header information was parsed
146 header_complete = False
147
148 for line in self.fid:
149 lb = line.decode('ascii').strip()
150
151 # the first thing needed is the number of scans in the file (in
152 # file header)
153 if nscans_re.match(lb):
154 t = lb.split("=")[1]
155 self.nscans = int(t)
156
157 if self.re_m2.match(lb):
158 t = re_position.findall(lb)[0]
159 t = t.split("=")[1]
160 m2_tmppos = float(t)
161
162 if novalues_re.match(lb):
163 t = lb.split("=")[1]
164 self.n_sm_pos = int(t)
165 header_complete = True
166
167 if header_complete:
168 # append motor positions of second motor
169 self.m2_pos.append([[m2_tmppos] * self.n_sm_pos])
170
171 # reset header flag
172 header_complete = False
173 # read data lines (number of lines determined by number of
174 # values)
175 datalines = itertools.islice(self.fid, self.n_sm_pos)
176 t = numpy.loadtxt(datalines)
177 self.data.append(t[:, 1])
178 self.sm_pos.append(t[:, 0])
179
180 # after reading all the data
181 self.m2_pos = numpy.array(self.m2_pos, dtype=numpy.double)
182 self.sm_pos = numpy.array(self.sm_pos, dtype=numpy.double)
183 self.data = numpy.array(self.data, dtype=numpy.double)
184
185 self.data.shape = (self.nscans, self.n_sm_pos)
186 self.m2_pos.shape = (self.nscans, self.n_sm_pos)
187 self.sm_pos.shape = (self.nscans, self.n_sm_pos)
188
189
190 class SeifertScan(object):
191 """
192 Class to parse a single Seifert (NJA) scan file
193 """
194
195 def __init__(self, filename, path=""):
196 """
197 Constructor for a SeifertScan object.
198
199 Parameters
200 ----------
201 filename : str
202 a string with the name of the file to read
203 path : str, optional
204 path to the datafile
205 """
206 self.Filename = os.path.join(path, filename)
207
208 self.hdr = SeifertHeader()
209 self.data = []
210 self.axispos = {}
211
212 with xu_open(self.Filename) as self.fid:
213 if config.VERBOSITY >= config.INFO_LOW:
214 print("XU.io.SeifertScan: parsing file: %s" % self.Filename)
215 self.parse()
216
217 if self.hdr.NumScans != 1:
218 self.data.shape = (int(self.data.shape[0] / self.hdr.NoValues),
219 int(self.hdr.NoValues), 2)
220
221 def parse(self):
222 if config.VERBOSITY >= config.INFO_ALL:
223 print("XU.io.SeifertScan.parse: starting the parser")
224 self.data = []
225 for line in self.fid:
226 lb = line.decode('ascii')
227 # remove leading and trailing whitespace and newline characeters
228 lb = lb.strip()
229
230 # every line is broken into its content
231 llist = re_multiblank.split(lb)
232 tmplist = []
233 axes = ""
234 for e in llist:
235 # if the entry is a key value pair
236 if re_keyvalue.match(e):
237 (key, value) = e.split("=")
238 # remove leading & from the key
239 key = key[1:]
240 # have to manage malformed key names that cannot be used as
241 # Python identifiers (leading numbers or blanks inside the
242 # name)
243 key = repair_key(key)
244
245 # try to convert the values to float numbers
246 # leave them as strings if this is not possible
247 try:
248 value = float(value)
249 except ValueError:
250 pass
251
252 if key == "Axis":
253 axes = value
254 if value not in self.axispos:
255 self.axispos[value] = []
256 elif key == "Pos":
257 self.axispos[axes] += [value, ]
258
259 self.hdr.__setattr__(key, value)
260 else:
261 try:
262 tmplist.append(float(e))
263 except ValueError:
264 pass
265
266 if tmplist != []:
267 self.data.append(tmplist)
268
269 # in the end we convert the data list to a numeric array
270 self.data = numpy.array(self.data, dtype=numpy.float)
271 for key in self.axispos:
272 self.axispos[key] = numpy.array(self.axispos[key])
273
274
275 def getSeifert_map(filetemplate, scannrs=None, path=".", scantype="map",
276 Nchannels=1280):
277 """
278 parses multiple Seifert ``*.nja`` files and concatenates the results. for
279 parsing the xrayutilities.io.SeifertMultiScan class is used. The function
280 can be used for parsing maps measured with the Meteor1D and point detector.
281
282 Parameters
283 ----------
284 filetemplate : str
285 template string for the file names, can contain a %d which is replaced
286 by the scan number or be a list of filenames
287 scannrs : int or list, optional
288 scan number(s)
289 path : str, optional
290 common path to the filenames
291 scantype : {'map', 'tsk'}, optional
292 type of datafile: can be either 'map' (reciprocal space map measured
293 with a regular Seifert job (default)) or 'tsk' (MCA spectra measured
294 using the TaskInterpreter)
295 Nchannels : int, optional
296 number of channels of the MCA (needed for 'tsk' measurements)
297
298 Returns
299 -------
300 om, tt, psd : ndarray
301 positions and data as flattened numpy arrays
302
303 Examples
304 --------
305 >>> om, tt, psd = xrayutilities.io.getSeifert_map("samplename_%d.xrdml",
306 >>> [1, 2], path="./data")
307 """
308 # read raw data and convert to reciprocal space
309 om = numpy.zeros(0)
310 tt = numpy.zeros(0)
311 if scantype == "map":
312 psd = numpy.zeros(0)
313 else:
314 psd = numpy.zeros((0, Nchannels))
315 # create scan names
316 if scannrs is None:
317 files = [filetemplate]
318 else:
319 files = list()
320 if not getattr(scannrs, '__iter__', False):
321 scannrs = [scannrs]
322 for nr in scannrs:
323 files.append(filetemplate % nr)
324
325 # parse files
326 for f in files:
327 if scantype == "map":
328 d = SeifertMultiScan(os.path.join(path, f), 'T', 'O')
329
330 om = numpy.concatenate((om, d.m2_pos.flatten()))
331 tt = numpy.concatenate((tt, d.sm_pos.flatten()))
332 psd = numpy.concatenate((psd, d.data.flatten()))
333 else: # scantype == "tsk":
334 d = SeifertScan(os.path.join(path, f))
335
336 om = numpy.concatenate((om, d.axispos['O'].flatten()))
337 tt = numpy.concatenate((tt, d.axispos['T'].flatten()))
338 psd = numpy.concatenate((psd, d.data[:, :, 1]))
339
340 return om, tt, psd
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 a class for observing a SPEC data file
20
21 Motivation:
22
23 SPEC files can become quite large. Therefore, subsequently reading
24 the entire file to extract a single scan is a quite cumbersome procedure.
25 This module is a proof of concept code to write a file observer starting
26 a reread of the file starting from a stored offset (last known scan position)
27 """
28
29 import os.path
30 import re
31
32 import numpy
33
34 from .. import config, utilities
35 from ..exception import InputError
36 # relative imports from xrayutilities
37 from .helper import xu_h5open, xu_open
38
39 # define some uesfull regular expressions
40 SPEC_time_format = re.compile(r"\d\d:\d\d:\d\d")
41 SPEC_multi_blank = re.compile(r"\s+")
42 SPEC_multi_blank2 = re.compile(r"\s\s+")
43 # denotes a numeric value
44 SPEC_int_value = re.compile(r"[+-]?\d+")
45 SPEC_num_value = re.compile(
46 r"([+-]?\d*\.*\d*[eE]*[+-]*\d+|[+-]?[Ii][Nn][Ff]|[Nn][Aa][Nn])")
47 SPEC_dataline = re.compile(r"^[+-]*\d.*")
48
49 SPEC_scan = re.compile(r"^#S")
50 SPEC_initmoponames = re.compile(r"#O\d+")
51 SPEC_initmopopos = re.compile(r"#P\d+")
52 SPEC_datetime = re.compile(r"^#D")
53 SPEC_exptime = re.compile(r"^#T")
54 SPEC_nofcols = re.compile(r"^#N")
55 SPEC_colnames = re.compile(r"^#L")
56 SPEC_MCAFormat = re.compile(r"^#@MCA")
57 SPEC_MCAChannels = re.compile(r"^#@CHANN")
58 SPEC_headerline = re.compile(r"^#")
59 SPEC_scanbroken = re.compile(r"#C[a-zA-Z0-9: .]*Scan aborted")
60 SPEC_scanresumed = re.compile(r"#C[a-zA-Z0-9: .]*Scan resumed")
61 SPEC_commentline = re.compile(r"#C")
62 SPEC_newheader = re.compile(r"^#E")
63 SPEC_errorbm20 = re.compile(r"^MI:")
64 scan_status_flags = ["OK", "NODATA", "ABORTED", "CORRUPTED"]
65
66
67 class SPECScan(object):
68 """
69 Represents a single SPEC scan. This class is usually not called by the
70 user directly but used via the SPECFile class.
71 """
72
73 def __init__(self, name, scannr, command, date, time, itime, colnames,
74 hoffset, doffset, fname, imopnames, imopvalues, scan_status):
75 """
76 Constructor for the SPECScan class.
77
78 Parameters
79 ----------
80 name : str
81 name of the scan
82 scannr : int
83 Number of the scan in the specfile
84 command : str
85 command used to write the scan
86 date : str
87 starting date of the scan
88 time : str
89 starting time of the scan
90 itime : int
91 integration time
92 colnames : list
93 list of names of the data columns
94 hoffset : int
95 file byte offset to the header of the scan
96 doffset : int
97 file byte offset to the data section of the scan
98 fname : str
99 file name of the SPEC file the scan belongs to
100 imopnames : list of str
101 motor names for the initial motor positions array
102 imopvalues : list
103 intial motor positions array
104 scan_status : {'OK', 'NODATA', 'CORRUPTED', 'ABORTED'}
105 scan status as string
106 """
107 self.name = name # name of the scan
108 self.nr = scannr # number of the scan
109 self.command = command # command used to record the data
110 self.date = date # date the command has been sent
111 self.time = time # time the command has been sent
112 self.colnames = colnames # list with column names
113 self.hoffset = hoffset # file offset where the header data starts
114 self.doffset = doffset # file offset where the data section starts
115 self.fname = fname # full file name of the file holding the data
116 # flag to force resave to hdf5 file in Save2HDF5()
117 self.ischanged = True
118 self.header = []
119 self.fid = None
120
121 if scan_status in scan_status_flags:
122 self.scan_status = scan_status
123 else:
124 self.scan_status = "CORRUPTED"
125 if config.VERBOSITY >= config.INFO_ALL:
126 print("XU.io.spec.SPECScan: unknown scan status flag - "
127 "set to CORRUPTED")
128
129 # setup the initial motor positions dictionary - set the motor names
130 # dictionary holding the initial motor positions
131 self.init_motor_pos = {}
132 if len(imopnames) == len(imopvalues):
133 for i in range(len(imopnames)):
134 natmotname = utilities.makeNaturalName(imopnames[i])
135 self.init_motor_pos[
136 "INIT_MOPO_" + natmotname] = float(imopvalues[i])
137 else:
138 print("XU.io.spec.SPECScan: Warning: incorrect number of "
139 "initial motor positions in scan %03d" % (self.nr))
140 if config.VERBOSITY >= config.INFO_ALL:
141 print(imopnames)
142 print(imopvalues)
143 # ASSUME ORDER DID NOT CHANGE!! (which might be wrong)
144 # in fact this is sign for a broken spec file
145 # number of initial motor positions should not change without new
146 # file header!
147 for i in range(min(len(imopnames), len(imopvalues))):
148 natmotname = utilities.makeNaturalName(imopnames[i])
149 self.init_motor_pos[
150 "INIT_MOPO_" + natmotname] = float(imopvalues[i])
151 # read the rest of the positions into dummy INIT_MOPO__NONAME__%03d
152 for i in range(len(imopnames), len(imopvalues)):
153 self.init_motor_pos["INIT_MOPO___NONAME__%03d" % (i)] = \
154 float(imopvalues[i])
155
156 # some additional attributes for the MCA data
157 # False if scan contains no MCA data, True otherwise
158 self.has_mca = False
159 self.mca_column_format = 0 # number of columns used to save MCA data
160 self.mca_channels = 0 # number of channels stored from the MCA
161 self.mca_nof_lines = 0 # number of lines used to store MCA data
162 self.mca_start_channel = 0 # first channel of the MCA that is stored
163 self.mca_stop_channel = 0 # last channel of the MCA that is stored
164
165 # a numpy record array holding the data - this is set using by the
166 # ReadData method.
167 self.data = None
168
169 # check for duplicate values in column names
170 for i in range(len(self.colnames)):
171 name = self.colnames[i]
172 cnt = self.colnames.count(name)
173 if cnt > 1:
174 # have multiple entries
175 cnt = 1
176 for j in range(self.colnames.index(name) + 1,
177 len(self.colnames)):
178 if self.colnames[j] == name:
179 self.colnames[j] = name + "_%i" % cnt
180 cnt += 1
181
182 def SetMCAParams(self, mca_column_format, mca_channels,
183 mca_start, mca_stop):
184 """
185 Set the parameters used to save the MCA data to the file. This method
186 calculates the number of lines used to store the MCA data from the
187 number of columns and the
188
189 Parameters
190 ----------
191 mca_column_format : int
192 number of columns used to save the data
193 mca_channels : int
194 number of MCA channels stored
195 mca_start : int
196 first channel that is stored
197 mca_stop : int
198 last channel that is stored
199 """
200 self.has_mca = True
201 self.mca_column_format = mca_column_format
202 self.mca_channels = mca_channels
203 self.mca_start_channel = mca_start
204 self.mca_stop_channel = mca_stop
205
206 # calculate the number of lines per data point for the mca
207 self.mca_nof_lines = int(mca_channels / mca_column_format)
208 if mca_channels % mca_column_format != 0:
209 # some additional values have to be read
210 self.mca_nof_lines = self.mca_nof_lines + 1
211
212 if config.VERBOSITY >= config.DEBUG:
213 print("XU.io.SPECScan.SetMCAParams: number of channels: %d"
214 % self.mca_channels)
215 print("XU.io.SPECScan.SetMCAParams: number of columns: %d"
216 % self.mca_column_format)
217 print("XU.io.SPECScan.SetMCAParams: number of lines to read "
218 "for MCA: %d" % self.mca_nof_lines)
219
220 def __str__(self):
221 # build a proper string to print the scan information
222 str_rep = "|%4i|" % (self.nr)
223 str_rep = str_rep + "%50s|%10s|%10s|" % (self.command, self.time,
224 self.date)
225
226 if self.has_mca:
227 str_rep = str_rep + "MCA: %5i" % (self.mca_channels)
228
229 str_rep = str_rep + "\n"
230 return str_rep
231
232 def ClearData(self):
233 """
234 Delete the data stored in a scan after it is no longer
235 used.
236 """
237
238 self.__delattr__("data")
239 self.data = None
240
241 def ReadData(self):
242 """
243 Set the data attribute of the scan class.
244 """
245
246 if self.scan_status == "NODATA":
247 if config.VERBOSITY >= config.INFO_LOW:
248 print("XU.io.SPECScan.ReadData: %s has been aborted - "
249 "no data available!" % self.name)
250 self.data = None
251 return None
252
253 if not self.has_mca:
254 if config.VERBOSITY >= config.INFO_ALL:
255 print("XU.io.SPECScan.ReadData: scan %d contains no MCA data"
256 % self.nr)
257
258 with xu_open(self.fname) as self.fid:
259 # read header lines
260 self.fid.seek(self.hoffset, 0)
261 self.header = []
262 while self.fid.tell() < self.doffset:
263 line = self.fid.readline().decode('ascii', 'ignore')
264 self.header.append(line.strip())
265
266 self.fid.seek(self.doffset, 0)
267
268 # create dictionary to hold the data
269 if self.has_mca:
270 type_desc = {"names": self.colnames + ["MCA"],
271 "formats": len(self.colnames) * [numpy.float32] +
272 [(numpy.uint32, self.mca_channels)]}
273 else:
274 type_desc = {"names": self.colnames,
275 "formats": len(self.colnames) * [numpy.float32]}
276
277 if config.VERBOSITY >= config.DEBUG:
278 print("xu.io.SPECScan.ReadData: type descriptor: %s"
279 % (repr(type_desc)))
280
281 record_list = [] # from this list the record array while be built
282
283 mca_counter = 0
284 scan_aborted_flag = False
285
286 for line in self.fid:
287 line = line.decode('ascii', 'ignore')
288 line = line.strip()
289 if not line:
290 continue
291
292 # check if scan is broken
293 if (SPEC_scanbroken.findall(line) != [] or
294 scan_aborted_flag):
295 # need to check next line(s) to know if scan is resumed
296 # read until end of comment block or end of file
297 if not scan_aborted_flag:
298 scan_aborted_flag = True
299 self.scan_status = "ABORTED"
300 if config.VERBOSITY >= config.INFO_ALL:
301 print("XU.io.SPECScan.ReadData: %s aborted"
302 % self.name)
303 continue
304 elif SPEC_scanresumed.match(line):
305 self.scan_status = "OK"
306 scan_aborted_flag = False
307 if config.VERBOSITY >= config.INFO_ALL:
308 print("XU.io.SPECScan.ReadData: %s resumed"
309 % self.name)
310 continue
311 elif SPEC_commentline.match(line):
312 continue
313 elif SPEC_errorbm20.match(line):
314 print(line)
315 continue
316 else:
317 break
318
319 if SPEC_headerline.match(line) or \
320 SPEC_commentline.match(line):
321 if SPEC_scanresumed.match(line):
322 continue
323 elif SPEC_commentline.match(line):
324 continue
325 else:
326 break
327
328 if mca_counter == 0:
329 # the line is a scalar data line
330 line_list = SPEC_num_value.findall(line)
331 if config.VERBOSITY >= config.DEBUG:
332 print("XU.io.SPECScan.ReadData: %s" % line)
333 print("XU.io.SPECScan.ReadData: read scalar values %s"
334 % repr(line_list))
335 # convert strings to numbers
336 line_list = map(float, line_list)
337
338 # increment the MCA counter if MCA data is stored
339 if self.has_mca:
340 mca_counter = mca_counter + 1
341 # create a temporary list for the mca data
342 mca_tmp_list = []
343 else:
344 record_list.append(tuple(line_list))
345 else:
346 # reading MCA spectrum
347 mca_tmp_list += map(int, SPEC_int_value.findall(line))
348
349 # increment MCA counter
350 mca_counter = mca_counter + 1
351 # if mca_counter exceeds the number of lines used to store
352 # MCA data: append everything to the record list
353 if mca_counter > self.mca_nof_lines:
354 record_list.append(tuple(list(line_list) +
355 [mca_tmp_list]))
356 mca_counter = 0
357
358 # convert the data to numpy arrays
359 ncol = len(record_list[0])
360 if config.VERBOSITY >= config.INFO_LOW:
361 print("XU.io.SPECScan.ReadData: %s: %d %d %d"
362 % (self.name, len(record_list), ncol,
363 len(type_desc["names"])))
364 if ncol == len(type_desc["names"]):
365 try:
366 self.data = numpy.rec.fromrecords(record_list,
367 dtype=type_desc)
368 except ValueError:
369 self.scan_status = 'NODATA'
370 print("XU.io.SPECScan.ReadData: %s exception while "
371 "parsing data" % self.name)
372 else:
373 self.scan_status = 'NODATA'
374
375 def plot(self, *args, **keyargs):
376 """
377 Plot scan data to a matplotlib figure. If newfig=True a new
378 figure instance will be created. If logy=True (default is False)
379 the y-axis will be plotted with a logarithmic scale.
380
381 Parameters
382 ----------
383 args : list
384 arguments for the plot: first argument is the name of x-value
385 column the following pairs of arguments are the y-value names and
386 plot styles allowed are 3, 5, 7,... number of arguments
387 keyargs : dict, optional
388 newfig : bool, optional
389 if True a new figure instance will be created otherwise an existing
390 one will be used
391 logy : bool, optional
392 if True a semilogy plot will be done
393 """
394 flag, plt = utilities.import_matplotlib_pyplot('XU.io.SPECScan')
395 if not flag:
396 return
397
398 newfig = keyargs.get('newfig', True)
399 logy = keyargs.get('logy', False)
400
401 try:
402 xname = args[0]
403 xdata = self.data[xname]
404 except ValueError:
405 raise InputError("name of the x-axis is invalid!")
406
407 alist = args[1:]
408 leglist = []
409
410 if len(alist) % 2 != 0:
411 raise InputError("wrong number of yname/style arguments!")
412
413 if newfig:
414 plt.figure()
415 plt.subplots_adjust(left=0.08, right=0.95)
416
417 for i in range(0, len(alist), 2):
418 yname = alist[i]
419 ystyle = alist[i + 1]
420 try:
421 ydata = self.data[yname]
422 except ValueError:
423 raise InputError("no column with name %s exists!" % yname)
424 continue
425 if logy:
426 plt.semilogy(xdata, ydata, ystyle)
427 else:
428 plt.plot(xdata, ydata, ystyle)
429
430 leglist.append(yname)
431
432 plt.xlabel("%s" % xname)
433 plt.legend(leglist)
434 plt.title("scan %i %s\n%s %s"
435 % (self.nr, self.command, self.date, self.time))
436 # need to adjust axis limits properly
437 lim = plt.axis()
438 plt.axis([xdata.min(), xdata.max(), lim[2], lim[3]])
439
440 def Save2HDF5(self, h5f, group="/", title="", optattrs={}, comp=True):
441 """
442 Save a SPEC scan to an HDF5 file. The method creates a group with the
443 name of the scan and stores the data there as a table object with name
444 "data". By default the scan group is created under the root group of
445 the HDF5 file. The title of the scan group is ususally the scan
446 command. Metadata of the scan are stored as attributes to the scan
447 group. Additional custom attributes to the scan group can be passed as
448 a dictionary via the optattrs keyword argument.
449
450 Parameters
451 ----------
452 h5f : file-handle or str
453 a HDF5 file object or its filename
454
455 group : str, optional
456 name or group object of the HDF5 group where to store the data
457 title : str, optional
458 a string with the title for the data, defaults to the name of scan
459 if empty
460 optattrs : dict, optional
461 a dictionary with optional attributes to store for the data
462 comp : bool, optional
463 activate compression - true by default
464 """
465
466 with xu_h5open(h5f, 'a') as h5:
467 # check if data object has been already written
468 if self.data is None:
469 raise InputError("XU.io.SPECScan.Save2HDF5: No data has been"
470 "read so far - call ReadData method of the "
471 "scan")
472 return None
473
474 # parse keyword arguments:
475 if isinstance(group, str):
476 rootgroup = h5.get(group)
477 else:
478 rootgroup = group
479
480 if title != "":
481 group_title = title
482 else:
483 group_title = self.name
484 group_title = group_title.replace(".", "_")
485
486 # create the dataset and fill it
487 copy_count = 0
488 if self.ischanged and group_title in rootgroup:
489 del rootgroup[group_title]
490 raw_grp_title = group_title
491 # if the group already exists the name must be changed and
492 # another will be made to create the group.
493 while group_title in rootgroup:
494 group_title = raw_grp_title + "_%i" % (copy_count)
495 copy_count = copy_count + 1
496 g = rootgroup.create_group(group_title)
497
498 kwds = {'fletcher32': True}
499 if comp:
500 kwds['compression'] = 'gzip'
501
502 g.create_dataset("data", data=self.data, **kwds)
503
504 # write attribute data for the scan
505 g.attrs['ScanNumber'] = numpy.uint(self.nr)
506 g.attrs['Command'] = self.command
507 g.attrs['Date'] = self.date
508 g.attrs['Time'] = self.time
509 g.attrs['scan_status'] = self.scan_status
510
511 # write the initial motor positions as attributes
512 for k in self.init_motor_pos:
513 g.attrs[k] = numpy.float(self.init_motor_pos[k])
514
515 # if scan contains MCA data write also MCA parameters
516 g.attrs['has_mca'] = self.has_mca
517 g.attrs['mca_start_channel'] = numpy.uint(self.mca_start_channel)
518 g.attrs['mca_stop_channel'] = numpy.uint(self.mca_stop_channel)
519 g.attrs['mca_nof_channels'] = numpy.uint(self.mca_channels)
520
521 for k in optattrs:
522 g.attrs[k] = optattrs[k]
523
524 h5.flush()
525
526 def getheader_element(self, key, firstonly=True):
527 """
528 return the value-string of the first appearance of this SPECScan's
529 header element, or a list of all values if firstonly=False
530
531 Parameters
532 ----------
533 specscan : SPECScan
534 key : str
535 name of the key to return; e.g. 'UMONO' or 'D'
536 firstonly : bool, optional
537 flag to specify if all instances or only the first one should be
538 returned
539
540 Returns
541 -------
542 valuestring : str
543 header value (if firstonly=True)
544 [str1, str2, ...] : list
545 header values (if firstonly=False)
546 """
547 if not self.header:
548 self.ReadData()
549 re_key = re.compile(r'^#%s (.*)' % key)
550 ret = []
551 for line in self.header:
552 m = re_key.match(line)
553 if m:
554 if firstonly:
555 ret = m.groups()[0]
556 break
557 else:
558 ret.append(m.groups()[0])
559 return ret
560
561
562 class SPECFile(object):
563
564 """
565 This class represents a single SPEC file. The class provides
566 methodes for updateing an already opened file which makes it particular
567 interesting for interactive use.
568 """
569
570 def __init__(self, filename, path=""):
571 """
572 SPECFile init routine
573
574 Parameters
575 ----------
576 filename : str
577 filename of the spec file
578 path : str, optional
579 path to the specfile
580 """
581 self.full_filename = os.path.join(path, filename)
582 self.filename = os.path.basename(self.full_filename)
583
584 # list holding scan objects
585 self.scan_list = []
586 self.fid = None
587 self.last_offset = 0
588
589 # initially parse the file
590 self.init_motor_names_fh = [] # this list will hold the names of the
591 # motors saved in initial motor positions given in the file header
592 self.init_motor_names_sh = [] # this list will hold the names of the
593 # motors saved in initial motor positions given in the scan header
594 self.init_motor_names = [] # this list will hold the names of the
595 # motors saved in initial motor positions from either the file or
596 # scan header
597
598 self.Parse()
599
600 def __getitem__(self, index):
601 """
602 function to return the n-th scan in the spec-file. be aware that
603 numbering starts at 0! If scans are missing the relation between the
604 given number and the "number" of the returned scan might be not
605 trivial.
606
607 See also
608 --------
609 scanI
610 attributes of the SPECFile object, where 'I' is the scan number
611 """
612 return self.scan_list[index]
613
614 def __getattr__(self, name):
615 """
616 return scanX objects where X stands for the scan number in the SPECFile
617 which for this purpose is assumed to be unique. (otherwise the first
618 instance of scan number X is returned)
619 """
620 if name.startswith("scan"):
621 index = name[4:]
622
623 try:
624 scannr = int(index)
625 except ValueError:
626 raise AttributeError("scannumber needs to be convertable to "
627 "integer")
628
629 # try to find the scan in the list of scans
630 s = None
631 for scan in self.scan_list:
632 if scan.nr == scannr:
633 s = scan
634 break
635
636 if s is not None:
637 return s
638 else:
639 raise AttributeError("requested scan-number not found")
640 else:
641 raise AttributeError("SPECFile has no attribute '%s'" % name)
642
643 def __len__(self):
644 return self.scan_list.__len__()
645
646 def __str__(self):
647 ostr = ""
648 for i in range(len(self.scan_list)):
649 ostr = ostr + "%5i" % (i)
650 ostr = ostr + self.scan_list[i].__str__()
651
652 return ostr
653
654 def Save2HDF5(self, h5f, comp=True, optattrs={}):
655 """
656 Save the entire file in an HDF5 file. For that purpose a group is set
657 up in the root group of the file with the name of the file without
658 extension and leading path. If the method is called after an previous
659 update only the scans not written to the file meanwhile are saved.
660
661 Parameters
662 ----------
663 h5f : file-handle or str
664 a HDF5 file object or its filename
665 comp : bool, optional
666 activate compression - true by default
667 """
668 with xu_h5open(h5f, 'a') as h5:
669 groupname = os.path.splitext(os.path.splitext(self.filename)[0])[0]
670 try:
671 g = h5.create_group(groupname)
672 except ValueError:
673 g = h5.get(groupname)
674
675 g.attrs['TITLE'] = "Data of SPEC - File %s" % (self.filename)
676 for k in optattrs:
677 g.attrs[k] = optattrs[k]
678 for s in self.scan_list:
679 if (((s.name not in g) or s.ischanged) and
680 s.scan_status != "NODATA"):
681 s.ReadData()
682 if s.data is not None:
683 s.Save2HDF5(h5, group=g, comp=comp)
684 s.ClearData()
685 s.ischanged = False
686
687 def Update(self):
688 """
689 reread the file and add newly added files. The parsing starts at the
690 data offset of the last scan gathered during the last parsing run.
691 """
692
693 # reparse the SPEC file
694 if config.VERBOSITY >= config.INFO_LOW:
695 print("XU.io.SPECFile.Update: reparsing file for new scans ...")
696 # mark last found scan as not saved to force reread
697 idx = len(self.scan_list)
698 if idx > 0:
699 lastscan = self.scan_list[idx - 1]
700 lastscan.ischanged = True
701 self.Parse()
702
703 def Parse(self):
704 """
705 Parses the file from the starting at last_offset and adding found scans
706 to the scan list.
707 """
708 with xu_open(self.full_filename) as self.fid:
709 # move to the last read position in the file
710 self.fid.seek(self.last_offset, 0)
711 scan_started = False
712 scan_has_mca = False
713 # list with the motors from whome the initial
714 # position is stored.
715 init_motor_values = []
716
717 if config.VERBOSITY >= config.DEBUG:
718 print('XU.io.SPECFile: start parsing')
719
720 for line in self.fid:
721 linelength = len(line)
722 line = line.decode('ascii', 'ignore')
723 if config.VERBOSITY >= config.DEBUG:
724 print('parsing line: %s' % line)
725
726 # remove trailing and leading blanks from the read line
727 line = line.strip()
728
729 # fill the list with the initial motor names in the header
730 if SPEC_newheader.match(line):
731 self.init_motor_names_fh = []
732
733 elif SPEC_initmoponames.match(line) and not scan_started:
734 if config.VERBOSITY >= config.DEBUG:
735 print("XU.io.SPECFile.Parse: found initial motor "
736 "names in file header")
737 line = SPEC_initmoponames.sub("", line)
738 line = line.strip()
739 self.init_motor_names_fh = self.init_motor_names_fh + \
740 SPEC_multi_blank2.split(line)
741
742 # if the line marks the beginning of a new scan
743 elif SPEC_scan.match(line) and not scan_started:
744 if config.VERBOSITY >= config.DEBUG:
745 print("XU.io.SPECFile.Parse: found scan")
746 line_list = SPEC_multi_blank.split(line)
747 scannr = int(line_list[1])
748 scancmd = "".join(" " + x + " " for x in line_list[2:])
749 scan_started = True
750 scan_has_mca = False
751 scan_header_offset = self.last_offset
752 scan_status = "OK"
753 # define some necessary variables which could be missing in
754 # the scan header
755 itime = numpy.nan
756 time = ''
757 if config.VERBOSITY >= config.INFO_ALL:
758 print("XU.io.SPECFile.Parse: processing scan nr. %d "
759 "..." % scannr)
760 # set the init_motor_names to the ones found in
761 # the file header
762 self.init_motor_names_sh = []
763 self.init_motor_names = self.init_motor_names_fh
764
765 # if the line contains the date and time information
766 elif SPEC_datetime.match(line) and scan_started:
767 if config.VERBOSITY >= config.DEBUG:
768 print("XU.io.SPECFile.Parse: found date and time")
769 # fetch the time from the line data
770 time = SPEC_time_format.findall(line)[0]
771 line = SPEC_time_format.sub("", line)
772 line = SPEC_datetime.sub("", line)
773 date = SPEC_multi_blank.sub(" ", line).strip()
774
775 # if the line contains the integration time
776 elif SPEC_exptime.match(line) and scan_started:
777 if config.VERBOSITY >= config.DEBUG:
778 print("XU.io.SPECFile.Parse: found exposure time")
779 itime = float(SPEC_num_value.findall(line)[0])
780 # read the initial motor names in the scan header if present
781 elif SPEC_initmoponames.match(line) and scan_started:
782 if config.VERBOSITY >= config.DEBUG:
783 print("XU.io.SPECFile.Parse: found initial motor "
784 "names in scan header")
785 line = SPEC_initmoponames.sub("", line)
786 line = line.strip()
787 self.init_motor_names_sh = self.init_motor_names_sh + \
788 SPEC_multi_blank2.split(line)
789 self.init_motor_names = self.init_motor_names_sh
790 # read the initial motor positions
791 elif SPEC_initmopopos.match(line) and scan_started:
792 if config.VERBOSITY >= config.DEBUG:
793 print("XU.io.SPECFile.Parse: found initial motor "
794 "positions")
795 line = SPEC_initmopopos.sub("", line)
796 line = line.strip()
797 line_list = SPEC_multi_blank.split(line)
798 # sometimes initial motor position are simply empty and
799 # this should not lead to an error
800 try:
801 for value in line_list:
802 init_motor_values.append(float(value))
803 except ValueError:
804 pass
805
806 # if the line contains the number of colunmns
807 elif SPEC_nofcols.match(line) and scan_started:
808 if config.VERBOSITY >= config.DEBUG:
809 print("XU.io.SPECFile.Parse: found number of columns")
810 line = SPEC_nofcols.sub("", line)
811 line = line.strip()
812 nofcols = int(line)
813
814 # if the line contains the column names
815 elif SPEC_colnames.match(line) and scan_started:
816 if config.VERBOSITY >= config.DEBUG:
817 print("XU.io.SPECFile.Parse: found column names")
818 line = SPEC_colnames.sub("", line)
819 line = line.strip()
820 col_names = SPEC_multi_blank.split(line)
821
822 # this is a fix in the case that blanks are allowed in
823 # motor and detector names (only a single balanks is
824 # supported meanwhile)
825 if len(col_names) > nofcols:
826 col_names = SPEC_multi_blank2.split(line)
827
828 elif SPEC_MCAFormat.match(line) and scan_started:
829 mca_col_number = int(SPEC_num_value.findall(
830 line)[0])
831 scan_has_mca = True
832
833 elif SPEC_MCAChannels.match(line) and scan_started:
834 line_list = SPEC_num_value.findall(line)
835 mca_channels = int(line_list[0])
836 mca_start = int(line_list[1])
837 mca_stop = int(line_list[2])
838
839 elif (SPEC_scanbroken.findall(line) != [] and
840 scan_started):
841 # this is the case when a scan is broken and no data has
842 # been written, but nevertheless a comment is in the file
843 # that tells us that the scan was aborted
844 scan_data_offset = self.last_offset
845 s = SPECScan("scan_%i" % (scannr), scannr, scancmd,
846 date, time, itime, col_names,
847 scan_header_offset, scan_data_offset,
848 self.full_filename, self.init_motor_names,
849 init_motor_values, "NODATA")
850
851 self.scan_list.append(s)
852
853 # reset control flags
854 scan_started = False
855 scan_has_mca = False
856 # reset initial motor positions flag
857 init_motor_values = []
858
859 elif SPEC_dataline.match(line) and scan_started:
860 # this is now the real end of the header block. at this
861 # point we know that there is enough information about the
862 # scan
863
864 # save the data offset
865 scan_data_offset = self.last_offset
866
867 # create an SPECFile scan object and add it to the scan
868 # list the name of the group consists of the prefix scan
869 # and the number of the scan in the file - this shoule make
870 # it easier to find scans in the HDF5 file.
871 s = SPECScan("scan_%i" % (scannr), scannr, scancmd, date,
872 time, itime, col_names, scan_header_offset,
873 scan_data_offset, self.full_filename,
874 self.init_motor_names, init_motor_values,
875 scan_status)
876 if scan_has_mca:
877 s.SetMCAParams(mca_col_number, mca_channels, mca_start,
878 mca_stop)
879
880 self.scan_list.append(s)
881
882 # reset control flags
883 scan_started = False
884 scan_has_mca = False
885 # reset initial motor positions flag
886 init_motor_values = []
887
888 elif SPEC_scan.match(line) and scan_started:
889 # this should only be the case when there are two
890 # consecutive file headers in the data file without any
891 # data or abort notice of the first scan; first store
892 # current scan as aborted then start new scan parsing
893 s = SPECScan("scan_%i" % (scannr), scannr, scancmd,
894 date, time, itime, col_names,
895 scan_header_offset, None,
896 self.full_filename, self.init_motor_names,
897 init_motor_values, "NODATA")
898 self.scan_list.append(s)
899
900 # reset control flags
901 scan_started = False
902 scan_has_mca = False
903 # reset initial motor positions flag
904 init_motor_values = []
905
906 # start parsing of new scan
907 if config.VERBOSITY >= config.DEBUG:
908 print("XU.io.SPECFile.Parse: found scan "
909 "(after aborted scan)")
910 line_list = SPEC_multi_blank.split(line)
911 scannr = int(line_list[1])
912 scancmd = "".join(" " + x + " " for x in line_list[2:])
913 scan_started = True
914 scan_has_mca = False
915 scan_header_offset = self.last_offset
916 scan_status = "OK"
917 self.init_motor_names_sh = []
918 self.init_motor_names = self.init_motor_names_fh
919
920 # store the position of the file pointer
921 self.last_offset += linelength
922
923 # if reading of the file is finished store the data offset of the
924 # last scan as the last offset for the next parsing run of the file
925 self.last_offset = self.scan_list[-1].doffset
926
927
928 class SPECCmdLine(object):
929
930 def __init__(self, n, prompt, cmdl, out=""):
931 self.linenumber = n
932 self.prompt = prompt
933 self.command = cmdl
934 self.out = out
935
936 def __str__(self):
937 ostr = "%i.%s> %s" % (self.linenumber, self.prompt, self.command)
938 return ostr
939
940
941 class SPECLog(object):
942 """
943 class to parse a SPEC log file to find the command history
944 """
945
946 def __init__(self, filename, prompt, path=""):
947 """
948 init routine for a class to read a SPEC log file
949
950 Parameters
951 ----------
952 filename : str
953 SPEC log file name
954 prompt : str
955 SPEC command prompt (e.g. 'PSIC' or 'SPEC')
956 path : str, optional
957 directory where the SPEC log can be found
958 """
959 self.filename = filename
960 self.full_filename = os.path.join(path, self.filename)
961
962 self.prompt = prompt
963 self.prompt_re = re.compile(r"%s>" % self.prompt)
964
965 self.cmdl_list = []
966 self.line_counter = 0
967 self.Parse()
968
969 def Parse(self):
970 with xu_open(self.full_filename, 'r') as fid:
971 for line in fid:
972 line = line.decode('ascii', 'ignore')
973 self.line_counter += 1
974
975 line = line.strip()
976 if self.prompt_re.findall(line):
977 [line, cmd] = self.prompt_re.split(line)
978 self.cmdl_list.append(SPECCmdLine(int(float(line)),
979 self.prompt, cmd))
980
981 def __getitem__(self, index):
982 """
983 function to return the n-th cmd in the spec-log.
984 """
985 return self.cmdl_list[index]
986
987 def __str__(self):
988 ostr = "%s with %d lines\n" % (self.filename, self.line_counter)
989
990 for cmd in self.cmdl_list:
991 ostr = ostr + cmd.__str__() + "\n"
992
993 return ostr
994
995
996 def geth5_scan(h5f, scans, *args, **kwargs):
997 """
998 function to obtain the angular cooridinates as well as intensity values
999 saved in an HDF5 file, which was created from a spec file by the Save2HDF5
1000 method. Especially useful for reciprocal space map measurements.
1001
1002 further more it is possible to obtain even more positions from
1003 the data file if more than two string arguments with its names are given
1004
1005 Parameters
1006 ----------
1007 h5f : file-handle or str
1008 file object of a HDF5 file opened using h5py or its filename
1009 scans : int, tuple or list
1010 number of the scans of the reciprocal space map
1011 args : str, optional
1012 names of the motors. to read reciprocal space maps measured in coplanar
1013 diffraction give:
1014
1015 - omname: name of the omega motor (or its equivalent)
1016 - ttname: name of the two theta motor (or its equivalent)
1017
1018 kwargs : dict, optional
1019 samplename: str, optional
1020 string with the hdf5-group containing the scan data if ommited the
1021 first child node of h5f.root will be used
1022 rettype: {'list', 'numpy'}, optional
1023 how to return motor positions. by default a list of arrays is returned.
1024 when rettype == 'numpy' a record array will be returned.
1025
1026 Returns
1027 -------
1028 [ang1, ang2, ...] : list
1029 angular positions of the center channel of the position sensitive
1030 detector (numpy.ndarray 1D), this list is omitted if no `args` are
1031 given
1032 MAP : ndarray
1033 the data values as stored in the data file (includes the intensities
1034 e.g. MAP['MCA']).
1035
1036 Examples
1037 --------
1038 >>> [om, tt], MAP = xu.io.geth5_scan(h5file, 36, 'omega', 'gamma')
1039 """
1040
1041 with xu_h5open(h5f) as h5:
1042 gname = kwargs.get("samplename", list(h5.keys())[0])
1043 h5g = h5.get(gname)
1044
1045 if numpy.iterable(scans):
1046 scanlist = scans
1047 else:
1048 scanlist = list([scans])
1049
1050 angles = dict.fromkeys(args)
1051 for key in angles:
1052 if not isinstance(key, str):
1053 raise InputError("*arg values need to be strings with "
1054 "motornames")
1055 angles[key] = numpy.zeros(0)
1056 buf = numpy.zeros(0)
1057 MAP = numpy.zeros(0)
1058
1059 for nr in scanlist:
1060 h5scan = h5g.get("scan_%d" % nr)
1061 sdata = numpy.asarray(h5scan.get('data'))
1062 if MAP.dtype == numpy.float64:
1063 MAP.dtype = sdata.dtype
1064 # append scan data to MAP, where all data are stored
1065 MAP = numpy.append(MAP, sdata)
1066 # check type of scan
1067 notscanmotors = []
1068 for i in range(len(args)):
1069 motname = args[i]
1070 try:
1071 buf = sdata[motname]
1072 scanshape = buf.shape
1073 angles[motname] = numpy.concatenate((angles[motname], buf))
1074 except ValueError:
1075 notscanmotors.append(i)
1076 if len(notscanmotors) == len(args):
1077 scanshape = len(sdata)
1078 for i in notscanmotors:
1079 motname = args[i]
1080 natmotname = utilities.makeNaturalName(motname)
1081 buf = numpy.ones(scanshape) * \
1082 h5scan.attrs["INIT_MOPO_%s" % natmotname]
1083 angles[motname] = numpy.concatenate((angles[motname], buf))
1084
1085 # create return values in correct order
1086 def create_retval():
1087 retval = []
1088 for motname in args:
1089 retval.append(angles[motname])
1090 return retval
1091
1092 rettype = kwargs.get('rettype', 'list')
1093 if rettype == 'numpy':
1094 retval = numpy.core.records.fromarrays([angles[m] for m in args],
1095 names=args)
1096 else:
1097 retval = create_retval()
1098
1099 if not args:
1100 return MAP
1101 else:
1102 return retval, MAP
1103
1104
1105 def getspec_scan(specf, scans, *args, **kwargs):
1106 """
1107 function to obtain the angular cooridinates as well as intensity values
1108 saved in a SPECFile. Especially useful to combine the data from multiple
1109 scans.
1110
1111 further more it is possible to obtain even more positions from
1112 the data file if more than two string arguments with its names are given
1113
1114 Parameters
1115 ----------
1116 specf : SPECFile
1117 file object
1118 scans : int, tuple or list
1119 number of the scans
1120 args : str
1121 names of the motors and counters
1122 rettype : {'list', 'numpy'}, optional
1123 how to return motor positions. by default a list of arrays is returned.
1124 when rettype == 'numpy' a record array will be returned.
1125
1126 Returns
1127 -------
1128 [ang1, ang2, ...] : list
1129 coordinates and counters from the SPEC file
1130
1131 Examples
1132 --------
1133 >>> [om, tt, cnt2] = xu.io.getspec_scan(s, 36, 'omega', 'gamma',
1134 >>> 'Counter2')
1135 """
1136 if not args:
1137 return
1138
1139 if numpy.iterable(scans):
1140 scanlist = scans
1141 else:
1142 scanlist = list([scans])
1143
1144 angles = dict.fromkeys(args)
1145 for key in angles:
1146 if not isinstance(key, str):
1147 raise InputError("*arg values need to be strings with "
1148 "motornames")
1149 angles[key] = numpy.zeros(0)
1150 buf = numpy.zeros(0)
1151
1152 for nr in scanlist:
1153 sscan = specf.__getattr__("scan%d" % nr)
1154 sscan.ReadData()
1155 sdata = sscan.data
1156 # check type of scan
1157 notscanmotors = []
1158 for i in range(len(args)):
1159 motname = args[i]
1160 try:
1161 buf = sdata[motname]
1162 scanshape = buf.shape
1163 angles[motname] = numpy.concatenate((angles[motname], buf))
1164 except ValueError:
1165 notscanmotors.append(i)
1166 if len(notscanmotors) == len(args):
1167 scanshape = len(sdata)
1168 for i in notscanmotors:
1169 motname = args[i]
1170 buf = (numpy.ones(scanshape) *
1171 sscan.init_motor_pos["INIT_MOPO_%s"
1172 % utilities.makeNaturalName(motname)])
1173 angles[motname] = numpy.concatenate((angles[motname], buf))
1174
1175 # create return values in correct order
1176 def create_retval():
1177 retval = []
1178 for motname in args:
1179 retval.append(angles[motname])
1180 return retval
1181
1182 rettype = kwargs.get('rettype', 'list')
1183 if rettype == 'numpy':
1184 retval = numpy.core.records.fromarrays([angles[m] for m in args],
1185 names=args)
1186 else:
1187 retval = create_retval()
1188
1189 return retval
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2015 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 module to handle spectra data
20 """
21
22 import glob
23 import re
24
25 import numpy
26 import numpy.lib.recfunctions
27 from numpy import rec
28
29 from .. import config
30 from .helper import xu_h5open
31
32 re_wspaces = re.compile(r"\s+")
33 re_colname = re.compile(r"^Col")
34
35 re_comment_section = re.compile(r"^%c")
36 re_parameter_section = re.compile(r"^%p")
37 re_data_section = re.compile(r"^%d")
38 re_end_section = re.compile(r"^!")
39 re_unit = re.compile(r"\[.+\]")
40 re_obracket = re.compile(r"\[")
41 re_cbracket = re.compile(r"\]")
42 re_underscore = re.compile(r"_")
43 re_column = re.compile(r"^Col")
44 re_col_name = re.compile(r"\d+\s+.+\s*\[")
45 re_col_index = re.compile(r"\d+\s+")
46 re_col_type = re.compile(r"\[.+\]")
47 re_num = re.compile(r"[0-9]")
48
49 dtype_map = {"FLOAT": "f4",
50 "DOUBLE": "f8"}
51
52
53 class SPECTRAFileComments(dict):
54 """
55 Class that describes the comments in the header of a SPECTRA file.
56 The different comments are accessible via the comment keys.
57 """
58
59 def __init__(self):
60 pass
61
62 def __getattr__(self, name):
63 if name in self:
64 return self[name]
65
66
67 class SPECTRAFileParameters(dict):
68
69 def __init__(self):
70 pass
71
72 def __getattr__(self, name):
73 if name in self:
74 return self[name]
75
76 def __str__(self):
77 ostr = ""
78 lmax_key = 0
79 lmax_item = 0
80
81 # find the length of the longest key
82 for k in self:
83 if len(k) > lmax_key:
84 lmax_key = len(k)
85
86 i = self[k]
87 if not isinstance(i, str):
88 # if the item is not a string it must be converted
89 i = "%f" % i
90
91 if len(i) > lmax_item:
92 lmax_item = len(i)
93
94 # define the format string for a single key-value pair
95 kvfmt = "|%%-%is = %%-%is" % (lmax_key, lmax_item)
96
97 cnt = 0
98 ostr += (3 * (lmax_key + lmax_item + 4) + 1) * "-" + "\n"
99 ostr += "|Parameters:" + (3 * (lmax_key + lmax_item)) * " " + "|\n"
100 ostr += (3 * (lmax_key + lmax_item + 4) + 1) * "-" + "\n"
101 for key in self:
102 value = self[key]
103 if not isinstance(value, str):
104 value = "%f" % value
105
106 ostr += kvfmt % (key, value)
107 cnt += 1
108 if cnt == 3:
109 ostr += "|\n"
110 cnt = 0
111
112 if cnt != 0:
113 ostr += "|\n"
114 ostr += (3 * (lmax_key + lmax_item + 4) + 1) * "-" + "\n"
115
116 return ostr
117
118
119 class SPECTRAFileDataColumn(object):
120
121 def __init__(self, index, name, unit, type):
122 self.index = int(index)
123 self.name = name
124 self.unit = unit
125 self.type = type
126
127 def __str__(self):
128 ostr = "%i %s %s %s" % (self.index, self.name, self.unit, self.type)
129 return ostr
130
131
132 class SPECTRAFileData(object):
133
134 def __init__(self):
135 self.collist = []
136 self.data = None
137
138 def append(self, col):
139 self.collist.append(col)
140
141 def __getitem__(self, key):
142 try:
143 return self.data[key]
144 except IndexError:
145 print("XU.io.specta.SPECTRAFileData: data contains no column "
146 "named: %s!" % key)
147
148 def __str__(self):
149 ostr = ""
150
151 # determine the maximum lenght of every column string
152 lmax = 0
153 for c in self.collist:
154 if len(c.__str__()) > lmax:
155 lmax = len(c.__str__())
156
157 lmax += 3
158
159 # want to print in three columns
160 nc = 3
161 nres = len(self.collist) % nc
162 nrows = (len(self.collist) - nres) / nc
163
164 fmtstr = "| %%-%is| %%-%is| %%-%is|\n" % (lmax, lmax, lmax)
165
166 ostr += (3 * lmax + 7) * "-" + "\n"
167 ostr += "|Column names:" + (3 * lmax - 8) * " " + "|\n"
168 ostr += (3 * lmax + 7) * "-" + "\n"
169 # full output rows
170 for i in range(nrows):
171 c1 = self.collist[i * nc + 0]
172 c2 = self.collist[i * nc + 1]
173 c3 = self.collist[i * nc + 2]
174 ostr += fmtstr % (c1.__str__(), c2.__str__(), c3.__str__())
175
176 # residual output row
177 c = ['', '', '']
178 for j in range(nres):
179 c[j] = self.collist[-nres + j]
180
181 ostr += fmtstr % (c[0].__str__(), c[1].__str__(), c[2].__str__())
182
183 ostr += (3 * lmax + 7) * "-" + "\n"
184 return ostr
185
186
187 class SPECTRAFile(object):
188
189 """
190 Represents a SPECTRA data file. The file is read during the
191 Constructor call. This class should work for data stored at
192 beamlines P08 and BW2 at HASYLAB.
193
194 Parameters
195 ----------
196 filename : str
197 a string with the name of the SPECTRA file
198
199 mcatmp : str, optional
200 template for the MCA files
201 mcastart, mcastop : int, optional
202 start and stop index for the MCA files, if not given, the class tries
203 to determine the start and stop index automatically.
204 """
205
206 def __init__(self, filename, mcatmp=None, mcastart=None, mcastop=None):
207 self.filename = filename
208 self.comments = SPECTRAFileComments()
209 self.params = SPECTRAFileParameters()
210 self.data = SPECTRAFileData()
211 self.mca = None
212 self.mca_channels = None
213
214 self.Read() # reads the .fio data file
215
216 if mcatmp is not None:
217 self.mca_file_template = mcatmp
218
219 if mcastart is not None and mcastop is not None:
220 self.mca_start_index = mcastart
221 self.mca_stop_index = mcastop
222 else:
223 # try to determine the number of MCA spectra automatically
224 spat = self.mca_file_template.replace("%i", "*")
225 lst = glob.glob(spat)
226 self.mca_start_index = 1
227 self.mca_stop_index = 0
228 if lst:
229 self.mca_stop_index = self.data.data.size # len(l)
230
231 if self.mca_stop_index != 0:
232 self.ReadMCA()
233
234 def Save2HDF5(self, h5file, name, group="/", mcaname="MCA"):
235 """
236 Saves the scan to an HDF5 file. The scan is saved to a
237 seperate group of name "name". h5file is either a string
238 for the file name or a HDF5 file object.
239 If the mca attribute is not None mca data will be stored to an
240 chunked array of with name mcaname.
241
242 Parameters
243 ----------
244 h5file : file-handle or str
245 HDF5 file object or name
246 name : str
247 name of the group where to store the data
248
249 group : str, optional
250 root group where to store the data
251 mcaname : str, optional
252 Name of the MCA in the HDF5 file
253
254 Returns
255 -------
256 bool or None
257 The method returns None in the case of everything went fine, True
258 otherwise.
259 """
260 with xu_h5open(h5file, 'w') as h5:
261 # create the group where to store the data
262 try:
263 g = h5.create_group(group + '/' + name)
264 except ValueError:
265 print("XU.io.spectra.Save2HDF5: cannot create group %s for "
266 "writing data!" % name)
267 return True
268
269 # start with saving scan comments
270 for k in self.comments:
271 try:
272 g.attrs[k] = self.comments[k]
273 except IndexError:
274 print("XU.io.spectra.Save2HDF5: cannot save file comment "
275 "%s = %s to group %s!" % (k, self.comments[k], name))
276
277 # save scan parameters
278 for k in self.params:
279 try:
280 g.attrs[k] = self.params[k]
281 except IndexError:
282 print("XU.io.spectra.Save2HDF5: cannot save file parametes"
283 " %s to group %s!" % (k, name))
284
285 # ----------finally we need to save the data -------------------
286 kwds = {'fletcher32': True, 'compression': 'gzip'}
287
288 try:
289 g.create_dataset("data", data=self.data.data, **kwds)
290 except (RuntimeError, ValueError):
291 print("XU.io.spectra.Save2HDF5: cannot create table for "
292 "storing scan data!")
293 return True
294
295 # if there is MCA data - store this
296 if self.mca is not None:
297 try:
298 c = g.create_dataset(mcaname, data=self.mca, **kwds)
299 except (RuntimeError, ValueError):
300 print("XU.io.spectra.Save2HDF5: cannot create carray %s "
301 "for MCA data!" % mcaname)
302 return True
303
304 # set MCA specific attributes
305 c.attrs["channels"] = self.mca_channels
306 c.attrs["nchannels"] = self.mca_channels.shape[0]
307
308 h5.flush()
309
310 return None
311
312 def ReadMCA(self):
313 dlist = []
314 for i in range(self.mca_start_index, self.mca_stop_index + 1):
315 fname = self.mca_file_template % i
316 data = numpy.loadtxt(fname)
317
318 if i == self.mca_start_index:
319 if len(data.shape) == 2:
320 self.mca_channels = data[:, 0]
321 else:
322 self.mca_channels = numpy.arange(0, data.shape[0])
323
324 if len(data.shape) == 2:
325 dlist.append(data[:, 1].tolist())
326 else:
327 dlist.append(data.tolist())
328
329 self.mca = numpy.array(dlist, dtype=float)
330
331 def __str__(self):
332 ostr = self.params.__str__()
333 ostr += self.data.__str__()
334
335 return ostr
336
337 def Read(self):
338 """
339 Read the data from the file.
340 """
341
342 def addkeyval(lst, k, v):
343 """
344 add new key to a list. if key already exists a number will be
345 appended to the key name
346
347 Parameters
348 ----------
349 lst : list
350 k : str
351 key
352 v : object
353 value
354 """
355 kcnt = 0
356 key = k
357 while key in lst:
358 key = k + "_%i" % (kcnt + 1)
359 kcnt += 1
360 lst[key] = v
361
362 col_names = []
363 col_types = []
364 rec_list = []
365 with open(self.filename, 'rb') as fid:
366 for line in fid:
367 line = line.decode('utf8', 'ignore')
368 line = line.strip()
369
370 # read the next line if the line starts with a "!"
371 if re_end_section.match(line):
372 continue
373
374 # select the which section to read
375 if re_comment_section.match(line):
376 read_mode = 1
377 continue
378
379 if re_parameter_section.match(line):
380 read_mode = 2
381 continue
382
383 if re_data_section.match(line):
384 read_mode = 3
385 continue
386
387 # here we decide how to proceed with the data
388 if read_mode == 1:
389 # read the file comments
390 try:
391 (key, value) = line.split("=")
392 except ValueError:
393 # avoid annoying output
394 if config.VERBOSITY >= config.INFO_ALL:
395 print("XU.io.SPECTRAFile.Read: cannot interpret "
396 "the comment string: %s" % (line))
397 continue
398
399 key = key.strip()
400 # remove whitespaces to be conform with natural naming
401 key = key.replace(' ', '')
402 key = key.replace(':', '_')
403 # remove possible number at first position
404 if re_num.findall(key[0]) != []:
405 key = "_" + key
406 value = value.strip()
407 if config.VERBOSITY >= config.DEBUG:
408 print("XU.io.SPECTRAFile.Read: comment: k, v: %s, %s"
409 % (key, value))
410
411 try:
412 value = float(value)
413 except ValueError:
414 pass
415
416 # need to handle the case, that a key may appear several
417 # times in the list
418 addkeyval(self.comments, key, value)
419
420 elif read_mode == 2:
421 # read scan parameters
422 try:
423 (key, value) = line.split("=")
424 except ValueError:
425 print("XU.io.SPECTRAFile.Read: cannot interpret the "
426 "parameter string: %s" % (line))
427
428 key = key.strip()
429 # remove whitespaces to be conform with natural naming
430 key = key.replace(' ', '')
431 key = key.replace(':', '_')
432 # remove possible number at first position
433 if re_num.findall(key[0]) != []:
434 key = "_" + key
435 value = value.strip()
436 if config.VERBOSITY >= config.DEBUG:
437 print("XU.io.SPECTRAFile.Read: parameter: k, v: %s, %s"
438 % (key, value))
439
440 try:
441 value = float(value)
442 except ValueError:
443 # if the conversion of the parameter to float
444 # fails it will be saved as a string
445 pass
446
447 # need to handle the case, that a key may appear several
448 # times in the list
449 addkeyval(self.params, key, value)
450
451 elif read_mode == 3:
452 if re_column.match(line):
453 try:
454 unit = re_unit.findall(line)[0]
455 except IndexError:
456 unit = "NONE"
457
458 try:
459 sline = re_obracket.split(line)
460 if len(sline) == 1:
461 raise IndexError
462 lval = sline[0]
463 rval = re_cbracket.split(line)[-1]
464 dtype = rval.strip()
465 lv = re_wspaces.split(lval)
466 index = int(lv[1])
467 name = "".join(lv[2:])
468 name = name.replace(':', '_')
469 except IndexError:
470 lv = re_wspaces.split(line)
471 index = int(lv[1])
472 dtype = lv[-1]
473 name = "".join(lv[2:-1])
474 name = name.replace(':', '_')
475
476 # store column definition
477 self.data.append(
478 SPECTRAFileDataColumn(index, name, unit, dtype))
479
480 if name in col_names:
481 name += "%s_1" % name
482 col_names.append("%s" % name)
483 col_types.append("%s" % (dtype_map[dtype]))
484
485 else:
486 # read data
487 dlist = re_wspaces.split(line)
488 for i in range(len(dlist)):
489 dlist[i] = float(dlist[i])
490
491 rec_list.append(tuple(dlist))
492
493 if config.VERBOSITY >= config.DEBUG:
494 print("XU.io.SPECTRAFile.Read: data columns: name, type: %s, %s"
495 % (col_names, col_types))
496 if rec_list:
497 self.data.data = rec.fromrecords(rec_list, formats=col_types,
498 names=col_names)
499 else:
500 self.data.data = None
501
502
503 def geth5_spectra_map(h5file, scans, *args, **kwargs):
504 """
505 function to obtain the omega and twotheta as well as intensity values
506 for a reciprocal space map saved in an HDF5 file, which was created
507 from a spectra file by the Save2HDF5 method.
508
509 further more it is possible to obtain even more positions from
510 the data file if more than two string arguments with its names are given
511
512 Parameters
513 ----------
514 h5f : file-handle or str
515 file object of a HDF5 file opened using h5py
516 scans : int, tuple or list
517 number of the scans of the reciprocal space map
518 args: str, optional
519 arbitrary number of motor names
520
521 - omname: name of the omega motor (or its equivalent)
522 - ttname: name of the two theta motor (or its equivalent)
523
524 kwargs : dict, optional
525 mca : str, optional
526 name of the mca data (if available) otherwise None (default: "MCA")
527 samplename : str, optional
528 string with the hdf5-group containing the scan data if omitted the
529 first child node of h5f.root will be used to determine the sample name
530
531 Returns
532 -------
533 [ang1, ang2, ...] : list
534 angular positions of the center channel of the position
535 sensitive detector (numpy.ndarray 1D). one entry for every
536 `args`-argument given to the function
537 MAP : ndarray
538 the data values as stored in the data file (includes the intensities
539 e.g. MAP['MCA']).
540 """
541
542 with xu_h5open(h5file) as h5:
543 mca = kwargs.get('mca', 'MCA')
544
545 if "samplename" in kwargs:
546 basename = kwargs["samplename"]
547 else:
548 nodename = list(h5)[0]
549 basenlist = re_underscore.split(nodename)
550 basename = "_".join(basenlist[:-1])
551 if config.VERBOSITY >= config.DEBUG:
552 print("XU.io.spectra.geth5_spectra_map: using \'%s\' as "
553 "basename" % (basename))
554
555 if isinstance(scans, (list, tuple)):
556 scanlist = scans
557 else:
558 scanlist = list([scans])
559
560 angles = dict.fromkeys(args)
561 for key in angles:
562 angles[key] = numpy.zeros(0)
563 buf = numpy.zeros(0)
564 MAP = numpy.zeros(0)
565
566 for nr in scanlist:
567 h5scan = h5.get(basename + "_%05d" % nr)
568 sdata = h5scan.get('data')
569 if mca:
570 mcanode = h5.get(basename + "_%05d/%s" % (nr, mca))
571 mcadata = numpy.asarray(mcanode)
572
573 # append scan data to MAP, where all data are stored
574 mcatemp = mcadata.view([(mca, (mcadata.dtype, mcadata.shape[1]))])
575 sdtmp = numpy.lib.recfunctions.merge_arrays([sdata, mcatemp],
576 flatten=True)
577 if MAP.dtype == numpy.float64:
578 MAP.dtype = sdtmp.dtype
579 MAP = numpy.append(MAP, sdtmp)
580
581 # check type of scan
582 notscanmotors = []
583 for i in range(len(args)):
584 motname = args[i]
585 try:
586 buf = sdata[motname]
587 scanshape = buf.shape
588 angles[motname] = numpy.concatenate((angles[motname], buf))
589 except ValueError:
590 notscanmotors.append(i)
591 for i in notscanmotors:
592 motname = args[i]
593 buf = numpy.ones(scanshape) * \
594 h5scan.attrs.get("%s" % motname)
595 angles[motname] = numpy.concatenate((angles[motname], buf))
596
597 retval = []
598 for motname in args:
599 # create return values in correct order
600 retval.append(angles[motname])
601
602 return retval, MAP
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2010-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 from . import elements
19 from .atom import Atom
20 from .cif import CIFFile, cifexport
21 from .database import (DataBase, add_f0_from_intertab, add_f0_from_xop,
22 add_f1f2_from_ascii_file, add_f1f2_from_henkedb,
23 add_f1f2_from_kissel, add_mass_from_NIST,
24 init_material_db)
25 from .material import (Alloy, Amorphous, Crystal, CubicAlloy,
26 CubicElasticTensor, HexagonalElasticTensor, Material,
27 PseudomorphicMaterial, WZTensorFromCub)
28 from .plot import show_reciprocal_space_plane
29 from .predefined_materials import *
30 from .spacegrouplattice import SGLattice
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2012-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16 """
17 script to create the HDF5 database from the raw data of XOP
18 his file is only needed for administration and also used during the unit tests
19
20 Optionally it accepts two command line arguments. The first one is the filename
21 of the database. If the filename has no path or a relative path the data-folder
22 given as second argument will be used as root of the output file.
23
24 The second optional argument is the directory of the source data file used to
25 generate the database. If no path is given the 'data' sub-folder of the scripts
26 directory is used. The script therefore works if called from the extracted
27 tarball directory but not after installation. After installation the respective
28 data path must be specified. The script can be run with 0, one or two command
29 line arguments as described above.
30
31 See tests/test_materials_database for an example.
32 """
33
34 import lzma
35 import os.path
36 import sys
37
38 # local import
39 from database import (DataBase, add_color_from_JMOL, add_f0_from_intertab,
40 add_f1f2_from_kissel, add_mass_from_NIST,
41 add_radius_from_WIKI, init_material_db)
42
43 verbose = False
44
45 if len(sys.argv) > 1:
46 fname = sys.argv[1]
47 if len(sys.argv) > 2:
48 dataroot = sys.argv[2]
49 else:
50 dataroot = os.path.join(os.path.dirname(__file__), 'data')
51
52 dbf = DataBase(fname)
53 dbf.Create('elementdata',
54 'Database with elemental data from XOP and Kissel databases')
55
56 init_material_db(dbf)
57
58 # add a dummy element, this is useful not only for testing and should be
59 # kept in future! It can be used for structure factor calculation tests, and
60 # shows how the a database entry can be generated manually
61 dbf.SetMaterial('dummy')
62 dbf.SetF0([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) # atomic structure factors
63 dbf.SetF1F2((0, 1e5), (0, 0), (0, 0)) # zero dispersion correction
64
65 add_mass_from_NIST(dbf, os.path.join(dataroot, 'nist_atom.dat'), verbose)
66 add_color_from_JMOL(dbf, os.path.join(dataroot, 'colors.dat'), verbose)
67 add_radius_from_WIKI(dbf, os.path.join(dataroot, 'atomic_radius.dat'), verbose)
68
69 # add F0(Q) for every element
70 # with lzma.open(os.path.join('data', 'f0_xop.dat.xz'), 'r') as xop:
71 # add_f0_from_xop(dbf, xop, verbose)
72 with lzma.open(os.path.join(dataroot, 'f0_InterTables.dat.xz'), 'r') as itf:
73 add_f0_from_intertab(dbf, itf, verbose)
74
75 # add F1 and F2 from database
76 with lzma.open(os.path.join(dataroot, 'f1f2_asf_Kissel.dat.xz'), 'r') as kf:
77 add_f1f2_from_kissel(dbf, kf, verbose)
78 # with lzma.open(os.path.join(dataroot, 'f1f2_Henke.dat'), 'r') as hf:
79 # add_f1f2_from_henkedb(dbf, hf, verbose)
80
81 # Also its possible to add custom data from different databases; e.g.
82 # created by Hepaestus (http://bruceravel.github.io/demeter/). This is also
83 # possible for specific elements only, therefore extract the data from
84 # Hephaestus or any other source producing ASCII files with three columns
85 # (energy (eV), f1, f2). To import such data use:
86 # add_f1f2_from_ascii_file(dbf, os.path.join(dataroot, 'Ga.f1f2'), 'Ga',
87 # verbose)
88
89 dbf.Close()
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module containing the Atom class which handles the database access for atomic
19 scattering factors and the atomic mass.
20 """
21 import hashlib
22 import os.path
23 import re
24
25 import numpy
26
27 from .. import config, utilities
28 from . import __path__, database
29
30 _db = database.DataBase(os.path.join(__path__[0], "data", config.DBNAME))
31 _db.Open()
32
33
34 def get_key(*args):
35 """
36 generate a hash key for several possible types of arguments
37 """
38 tup = []
39 for a in args:
40 if isinstance(a, numpy.ndarray):
41 tup.append(hashlib.md5(a).digest())
42 elif isinstance(a, list):
43 tup.append(hash(tuple(a)))
44 else:
45 tup.append(hash(a))
46 return hash(tuple(tup))
47
48
49 class Atom(object):
50 max_cache_length = 1000
51
52 def __init__(self, name, num):
53 self.name = name
54 self.ostate = re.sub('[A-Za-z]', '', name)
55 for r, o in zip(('dot', 'p', 'm'), ('.', '+', '-')):
56 self.ostate = self.ostate.replace(o, r)
57
58 self.basename = re.sub('[^A-Za-z]', '', name)
59 self.num = num
60 self.__weight = None
61 self.__color = None
62 self.__radius = numpy.nan
63 self._dbcache = dict([(prop, []) for prop in ('f0', 'f1', 'f2', 'f')])
64
65 def __key__(self):
66 """ key function to return the elements number """
67 return self.num
68
69 def __lt__(self, other_el):
70 """ make elements sortable by their key """
71 return self.__key__() < other_el.__key__()
72
73 @property
74 def weight(self):
75 if not self.__weight:
76 _db.SetMaterial(self.basename)
77 self.__weight = _db.weight
78 return self.__weight
79
80 @property
81 def color(self):
82 if self.__color is None:
83 _db.SetMaterial(self.basename)
84 self.__color = _db.color
85 return self.__color
86
87 @property
88 def radius(self):
89 if self.__radius is numpy.nan:
90 _db.SetMaterial(self.basename)
91 self.__radius = _db.radius
92 return self.__radius
93
94 def get_cache(self, prop, key):
95 """
96 check if a cached value exists to speed up repeated database requests
97
98 Returns
99 -------
100 bool
101 True then result contains the cached otherwise False and result is
102 None
103 result : database value
104 """
105 history = self._dbcache[prop]
106 for idx, (k, result) in enumerate(history):
107 if k == key:
108 history.insert(0, history.pop(idx)) # move to front
109 return True, result
110 return False, None
111
112 def set_cache(self, prop, key, result):
113 """
114 set result to be cached to speed up future calls
115 """
116 history = self._dbcache[prop]
117 if len(history) == self.max_cache_length:
118 history.pop(-1)
119 history.insert(0, (key, result))
120
121 def f0(self, q):
122 key = get_key(q)
123 f, res = self.get_cache('f0', key)
124 if f:
125 return res
126 _db.SetMaterial(self.basename)
127 res = _db.GetF0(q, self.ostate)
128 self.set_cache('f0', key, res)
129 return res
130
131 def f1(self, en='config'):
132 key = get_key(en)
133 f, res = self.get_cache('f1', key)
134 if f:
135 return res
136 if isinstance(en, str) and en == 'config':
137 en = utilities.energy(config.ENERGY)
138
139 _db.SetMaterial(self.basename)
140 res = _db.GetF1(utilities.energy(en))
141 self.set_cache('f1', key, res)
142 return res
143
144 def f2(self, en='config'):
145 key = get_key(en)
146 f, res = self.get_cache('f2', key)
147 if f:
148 return res
149 if isinstance(en, str) and en == 'config':
150 en = utilities.energy(config.ENERGY)
151
152 _db.SetMaterial(self.basename)
153 res = _db.GetF2(utilities.energy(en))
154 self.set_cache('f2', key, res)
155 return res
156
157 def f(self, q, en='config'):
158 """
159 function to calculate the atomic structure factor F
160
161 Parameters
162 ----------
163 q : float, array-like
164 momentum transfer
165 en : float or str, optional
166 energy for which F should be calculated, if omitted the value from
167 the xrayutilities configuration is used
168
169 Returns
170 -------
171 float or array-like
172 value(s) of the atomic structure factor
173 """
174 key = get_key(q, en)
175 f, res = self.get_cache('f', key)
176 if f:
177 return res
178
179 res = self.f0(q) + self.f1(en) + 1.j * self.f2(en)
180 self.set_cache('f2', key, res)
181 return res
182
183 def __str__(self):
184 ostr = self.name
185 ostr += " (%2d)" % self.num
186 return ostr
187
188 def __repr__(self):
189 return self.__str__()
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import copy
18 import io
19 import itertools
20 import operator
21 import os
22 import re
23 import shlex
24
25 import numpy
26 import scipy.optimize
27
28 from .. import config
29 from . import elements
30 from . import spacegrouplattice as sgl
31 from . import wyckpos
32
33 re_data = re.compile(r"^data_", re.IGNORECASE)
34 re_loop = re.compile(r"^loop_", re.IGNORECASE)
35 re_symop = re.compile(r"^\s*("
36 "_space_group_symop_operation_xyz|"
37 "_symmetry_equiv_pos_as_xyz)", re.IGNORECASE)
38 re_name = re.compile(r"^\s*_chemical_formula_sum", re.IGNORECASE)
39 re_atom = re.compile(r"^\s*_atom_site_label\s*$", re.IGNORECASE)
40 re_atomtyp = re.compile(r"^\s*_atom_site_type_symbol\s*$", re.IGNORECASE)
41 re_atomx = re.compile(r"^\s*_atom_site_fract_x", re.IGNORECASE)
42 re_atomy = re.compile(r"^\s*_atom_site_fract_y", re.IGNORECASE)
43 re_atomz = re.compile(r"^\s*_atom_site_fract_z", re.IGNORECASE)
44 re_uiso = re.compile(r"^\s*_atom_site_U_iso_or_equiv", re.IGNORECASE)
45 re_biso = re.compile(r"^\s*_atom_site_B_iso_or_equiv", re.IGNORECASE)
46 re_atomocc = re.compile(r"^\s*_atom_site_occupancy", re.IGNORECASE)
47 re_labelline = re.compile(r"^\s*_")
48 re_emptyline = re.compile(r"^\s*$")
49 re_quote = re.compile(r"'")
50 re_spacegroupnr = re.compile(r"^\s*(_space_group_IT_number|"
51 "_symmetry_Int_Tables_number)", re.IGNORECASE)
52 re_spacegroupname = re.compile(r"^\s*(_symmetry_space_group_name_H-M|"
53 "_space_group_name_H-M_alt)",
54 re.IGNORECASE)
55 re_spacegroupsetting = re.compile(r"^\s*_symmetry_cell_setting", re.IGNORECASE)
56 re_cell_a = re.compile(r"^\s*_cell_length_a", re.IGNORECASE)
57 re_cell_b = re.compile(r"^\s*_cell_length_b", re.IGNORECASE)
58 re_cell_c = re.compile(r"^\s*_cell_length_c", re.IGNORECASE)
59 re_cell_alpha = re.compile(r"^\s*_cell_angle_alpha", re.IGNORECASE)
60 re_cell_beta = re.compile(r"^\s*_cell_angle_beta", re.IGNORECASE)
61 re_cell_gamma = re.compile(r"^\s*_cell_angle_gamma", re.IGNORECASE)
62 re_comment = re.compile(r"^\s*#")
63
64
65 def testwp(parint, wp, cifpos, digits):
66 """
67 test if a Wyckoff position can describe the given position from a CIF file
68
69 Parameters
70 ----------
71 parint : int
72 telling which Parameters the given Wyckoff position has
73 wp : str or tuple
74 expression of the Wyckoff position
75 cifpos : list, or tuple or array-like
76 (x, y, z) position of the atom in the CIF file
77 digits : int
78 number of digits for which for a comparison of floating point numbers
79 will be rounded to
80
81 Returns
82 -------
83 foundflag : bool
84 flag to tell if the positions match
85 pars : array-like or None
86 parameters associated with the position or None if no parameters are
87 needed
88 """
89 def check_numbers_match(p1, p2, digits):
90 p1 = p1 - numpy.round(p1, digits) // 1
91 p2 = p2 - numpy.round(p2, digits) // 1
92 if numpy.round(p1, digits) == numpy.round(p2, digits):
93 return True
94 else:
95 return False
96
97 def get_pardict(parint, x):
98 i = 0
99 pardict = {}
100 if parint & 1:
101 pardict['x'] = x[i]
102 i += 1
103 if parint & 2:
104 pardict['y'] = x[i]
105 i += 1
106 if parint & 4:
107 pardict['z'] = x[i]
108 return pardict
109
110 wyckp = wp.strip('()').split(',')
111 # test agreement in positions witout variables
112 match = numpy.asarray([False, False, False])
113 variables = []
114 for i in range(3):
115 v = re.findall(r'[xyz]', wyckp[i])
116 if v == []:
117 pos = eval(wyckp[i])
118 match[i] = check_numbers_match(pos, cifpos[i], digits)
119 if not match[i]:
120 return False, None
121 else:
122 variables += v
123
124 if numpy.all(match):
125 return True, None
126
127 # check if with proper choice of the variables a correspondence of the
128 # positions can be obtained
129 def fmin(x, parint, wyckp, cifpos):
130 evalexp = []
131 cifp = []
132 for i in range(3):
133 if not match[i]:
134 evalexp.append(wyckp[i])
135 cifp.append(cifpos[i])
136 pardict = get_pardict(parint, x)
137 wpos = [eval(e, pardict) for e in evalexp]
138 return numpy.linalg.norm(numpy.asarray(wpos)-numpy.asarray(cifp))
139
140 x0 = []
141 if 'x' in variables:
142 x0.append(cifpos[0])
143 if 'y' in variables:
144 x0.append(cifpos[1])
145 if 'z' in variables:
146 x0.append(cifpos[2])
147
148 opt = scipy.optimize.minimize(fmin, x0, args=(parint, wyckp, cifpos))
149 pardict = get_pardict(parint, opt.x)
150 for i in range(3):
151 if not match[i]:
152 pos = eval(wyckp[i], pardict)
153 match[i] = check_numbers_match(pos, cifpos[i], digits)
154 if numpy.all(match):
155 return True, opt.x
156 else:
157 return False, None
158
159
160 class CIFFile(object):
161 """
162 class for parsing CIF (Crystallographic Information File) files. The class
163 aims to provide an additional way of creating material classes instead of
164 manual entering of the information the lattice constants and unit cell
165 structure are parsed from the CIF file.
166
167 If multiple datasets are present in the CIF file this class will attempt to
168 parse all of them into the the data dictionary. By default all methods
169 access the first data set found in the file.
170 """
171 def __init__(self, filestr, digits=3):
172 """
173 initialization of the CIFFile class
174
175 Parameters
176 ----------
177 filestr : str, bytes
178 CIF filename or string representation of the CIF file
179 digits : int, optional
180 number of digits to check if position is unique
181 """
182 self.digits = digits
183
184 if os.path.isfile(filestr):
185 self.filename = filestr
186 try:
187 self.fid = open(self.filename, "rb")
188 except OSError:
189 raise IOError("cannot open CIF file %s" % self.filename)
190 else:
191 if filestr.count('\n') == 0:
192 print('XU.material.CIFFile: "filestr" contains only one line '
193 'but a file with that name does not exist! Continuing '
194 'with the assumption this one line string is the '
195 'content of a CIF file!')
196 self.filename = '__from_str__'
197 if isinstance(filestr, bytes):
198 self.fid = io.BytesIO(filestr)
199 else:
200 self.fid = io.BytesIO(bytes(filestr.encode('ascii')))
201
202 if config.VERBOSITY >= config.INFO_ALL:
203 print('XU.material: parsing CIF file %s' % self.filename)
204 self._default_dataset = None
205 self.data = {}
206 self.Parse()
207
208 def __del__(self):
209 """
210 class destructor which closes open files
211 """
212 if self.fid is not None:
213 self.fid.close()
214
215 def Parse(self):
216 """
217 function to parse a CIF file. The function reads all the included data
218 sets and adds them to the data dictionary.
219
220 """
221 fidpos = self.fid.tell()
222 while True:
223 line = self.fid.readline()
224 if not line:
225 break
226 fidpos = self.fid.tell()
227 line = line.decode('ascii', 'ignore')
228 m = re_data.match(line)
229 if m:
230 self.fid.seek(fidpos)
231 name = line[m.end():].strip()
232 self.data[name] = CIFDataset(self.fid, name, self.digits)
233 if self.data[name].has_atoms and not self._default_dataset:
234 self._default_dataset = name
235
236 def SGLattice(self, dataset=None, use_p1=False):
237 """
238 create a SGLattice object with the structure from the CIF dataset
239
240 Parameters
241 ----------
242 dataset : str, optional
243 name of the dataset to use. if None the default one will be used.
244 use_p1 : bool, optional
245 force the use of P1 symmetry, default False
246 """
247 if not dataset:
248 dataset = self._default_dataset
249 return self.data[dataset].SGLattice(use_p1=use_p1)
250
251 def __str__(self):
252 """
253 returns a string with positions and names of the atoms for all datasets
254 """
255 ostr = ""
256 ostr += "CIF-File: %s\n" % self.filename
257 for ds in self.data:
258 ostr += "\nDataset: %s" % ds
259 if ds == self._default_dataset:
260 ostr += " (default)"
261 ostr += "\n"
262 ostr += str(self.data[ds])
263 return ostr
264
265
266 class CIFDataset(object):
267 """
268 class for parsing CIF (Crystallographic Information File) files. The class
269 aims to provide an additional way of creating material classes instead of
270 manual entering of the information the lattice constants and unit cell
271 structure are parsed from the CIF file
272 """
273
274 def __init__(self, fid, name, digits):
275 """
276 initialization of the CIFDataset class. This class parses one data
277 block.
278
279 Parameters
280 ----------
281 fid : filehandle
282 file handle set to the beginning of the data block to be parsed
283 name : str
284 identifier string of the dataset
285 digits : int
286 number of digits to check if position is unique
287 """
288 self.name = name
289 self.digits = digits
290 self.has_atoms = False
291
292 if config.VERBOSITY >= config.INFO_ALL:
293 print('XU.material: parsing cif dataset %s' % self.name)
294 self.Parse(fid)
295 self.SymStruct()
296
297 def Parse(self, fid):
298 """
299 function to parse a CIF data set. The function reads the
300 space group symmetry operations and the basic atom positions
301 as well as the lattice constants and unit cell angles
302 """
303
304 self.symops = []
305 self.atoms = []
306 self.lattice_const = numpy.zeros(3, dtype=numpy.double)
307 self.lattice_angles = numpy.zeros(3, dtype=numpy.double)
308
309 loop_start = False
310 symop_loop = False
311 atom_loop = False
312
313 def get_element(cifstring):
314 el = re.sub(r"['\"]", r"", cifstring)
315 if '+' in el or '-' in el:
316 for r, o in zip(('dot', 'p', 'm'), ('.', '+', '-')):
317 # move special character to last position
318 el = el.replace(o, r)
319 else:
320 el = re.sub(r"([0-9])", r"", el)
321 el = re.sub(r"\(\w*\)", r"", el)
322 try:
323 element = getattr(elements, el)
324 except AttributeError: # el not found, typ. due to oxidation state
325 f = re.search('[0-9]', el)
326 if not f and el == '?':
327 element = elements.dummy
328 else:
329 elname = el[:f.start()]
330 if hasattr(elements, elname):
331 # here one might want to find a closer alternative than
332 # the neutral atom, but the effect this has should be
333 # minimal, currently simply the neutral atom is used
334 if config.VERBOSITY >= config.INFO_LOW:
335 print('XU.material: element %s used instead of %s'
336 % (elname, cifstring))
337 element = getattr(elements, elname)
338 else:
339 raise ValueError('XU.material: element (%s) could not'
340 ' be found' % (cifstring))
341 return element
342
343 def floatconv(string):
344 """
345 helper function to convert string with possible error
346 given in brackets to float
347 """
348 try:
349 f = float(re.sub(r"\(.+\)", r"", string))
350 except ValueError:
351 f = numpy.nan
352 return f
353
354 def intconv(string):
355 """
356 helper function to convert string to integer
357 """
358 try:
359 i = int(string)
360 except ValueError:
361 i = None
362 return i
363
364 fidpos = fid.tell()
365 for line in fid.readlines():
366 linelen = len(line)
367 line = line.decode('ascii', 'ignore')
368 if config.VERBOSITY >= config.DEBUG:
369 print(line)
370 print(fid.tell(), fidpos)
371
372 if re_data.match(line):
373 fid.seek(fidpos)
374 break
375 fidpos += linelen
376
377 # ignore comment lines
378 if re_comment.match(line):
379 continue
380
381 if re_loop.match(line): # start of loop
382 if config.VERBOSITY >= config.DEBUG:
383 print('XU.material: loop start found')
384 loop_start = True
385 loop_labels = []
386 symop_loop = False
387 atom_loop = False
388 ax_idx = None
389 ay_idx = None
390 az_idx = None
391 uiso_idx = None
392 biso_idx = None
393 occ_idx = None
394 elif re_labelline.match(line):
395 if re_cell_a.match(line):
396 self.lattice_const[0] = floatconv(line.split()[1])
397 elif re_cell_b.match(line):
398 self.lattice_const[1] = floatconv(line.split()[1])
399 elif re_cell_c.match(line):
400 self.lattice_const[2] = floatconv(line.split()[1])
401 elif re_cell_alpha.match(line):
402 self.lattice_angles[0] = floatconv(line.split()[1])
403 elif re_cell_beta.match(line):
404 self.lattice_angles[1] = floatconv(line.split()[1])
405 elif re_cell_gamma.match(line):
406 self.lattice_angles[2] = floatconv(line.split()[1])
407 elif re_spacegroupnr.match(line):
408 i = intconv(line.split()[1])
409 if i:
410 self.sgrp_nr = i
411 elif re_spacegroupname.match(line):
412 self.sgrp_name = ''.join(line.split()[1:]).strip("'")
413 elif re_spacegroupsetting.match(line):
414 i = intconv(line.split()[1])
415 if i:
416 self.sgrp_setting = i
417 elif re_name.match(line):
418 try:
419 self.name = shlex.split(line)[1]
420 except IndexError:
421 self.name = None
422 if loop_start:
423 loop_labels.append(line.strip())
424 if re_symop.match(line): # start of symmetry op. loop
425 if config.VERBOSITY >= config.DEBUG:
426 print('XU.material: symop-loop identified')
427 symop_loop = True
428 symop_idx = len(loop_labels) - 1
429 elif re_atom.match(line) or re_atomtyp.match(line):
430 # start of atom position loop
431 if config.VERBOSITY >= config.DEBUG:
432 print('XU.material: atom position-loop identified')
433 atom_loop = True
434 if re_atomtyp.match(line):
435 alab_idx = len(loop_labels) - 1
436 elif not list(filter(re_atomtyp.match, loop_labels)):
437 # ensure precedence of atom_site_type_symbol
438 alab_idx = len(loop_labels) - 1
439 elif re_atomx.match(line):
440 ax_idx = len(loop_labels) - 1
441 if config.VERBOSITY >= config.DEBUG:
442 print('XU.material: atom position x: col%d'
443 % ax_idx)
444 elif re_atomy.match(line):
445 ay_idx = len(loop_labels) - 1
446 elif re_atomz.match(line):
447 az_idx = len(loop_labels) - 1
448 elif re_uiso.match(line):
449 uiso_idx = len(loop_labels) - 1
450 elif re_biso.match(line):
451 biso_idx = len(loop_labels) - 1
452 elif re_atomocc.match(line):
453 occ_idx = len(loop_labels) - 1
454
455 elif re_emptyline.match(line):
456 loop_start = False
457 symop_loop = False
458 atom_loop = False
459 continue
460 elif symop_loop: # symmetry operation entry
461 loop_start = False
462 entry = shlex.split(line)[symop_idx]
463 if re_quote.match(entry):
464 opstr = entry
465 else:
466 opstr = "'" + entry + "'"
467 opstr = re.sub(r"^'", r"(", opstr)
468 opstr = re.sub(r"'$", r")", opstr)
469 # add a comma to a fraction to avoid int division problems
470 opstr = re.sub(r"/([1-9])", r"/\1.", opstr)
471 self.symops.append(opstr)
472 elif atom_loop: # atom label and position
473 loop_start = False
474 asplit = line.split()
475 try:
476 atom = get_element(asplit[alab_idx])
477 apos = (floatconv(asplit[ax_idx]),
478 floatconv(asplit[ay_idx]),
479 floatconv(asplit[az_idx]))
480 occ = floatconv(asplit[occ_idx]) if occ_idx else 1
481 if numpy.isnan(occ):
482 occ = 1
483 uiso = floatconv(asplit[uiso_idx]) if uiso_idx else 0
484 biso = floatconv(asplit[biso_idx]) if biso_idx else 0
485 if numpy.isnan(uiso):
486 uiso = 0
487 if numpy.isnan(biso):
488 biso = 0
489 if biso == 0:
490 biso = 8 * numpy.pi**2 * uiso
491 self.atoms.append((atom, apos, occ, biso))
492 except IndexError:
493 if config.VERBOSITY >= config.INFO_LOW:
494 print('XU.material: could not parse atom line: "%s"'
495 % line.strip())
496 if self.atoms:
497 self.has_atoms = True
498
499 def SymStruct(self):
500 """
501 function to obtain the list of different atom positions in the unit
502 cell for the different types of atoms and determine the space group
503 number and origin choice if available. The data are obtained from the
504 data parsed from the CIF file.
505 """
506
507 def rem_white(string):
508 return string.replace(' ', '')
509
510 if hasattr(self, 'sgrp_name'):
511 # determine spacegroup
512 cifsgn = rem_white(self.sgrp_name).split(':')[0]
513 for nr, name in sgl.sgrp_name.items():
514 if cifsgn == rem_white(name):
515 self.sgrp_nr = int(nr)
516 if not hasattr(self, 'sgrp_nr'):
517 # try ignoring the minuses
518 for nr, name in sgl.sgrp_name.items():
519 if cifsgn == rem_white(name.replace('-', '')):
520 self.sgrp_nr = int(nr)
521 if len(self.sgrp_name.split(':')) > 1:
522 self.sgrp_suf = ':' + self.sgrp_name.split(':')[1]
523 elif hasattr(self, 'sgrp_setting'):
524 self.sgrp_suf = ':%d' % self.sgrp_setting
525
526 if not hasattr(self, 'sgrp_suf'):
527 if hasattr(self, 'sgrp_nr'):
528 self.sgrp_suf = sgl.get_possible_sgrp_suf(self.sgrp_nr)
529 else:
530 self.sgrp_suf = ''
531 if isinstance(self.sgrp_suf, str):
532 suffixes = [self.sgrp_suf, ]
533 else:
534 suffixes = copy.copy(self.sgrp_suf)
535 for sgrp_suf in suffixes:
536 self.sgrp_suf = sgrp_suf
537 if hasattr(self, 'sgrp_nr'):
538 self.sgrp = str(self.sgrp_nr) + self.sgrp_suf
539 allwyckp = wyckpos.wp[self.sgrp]
540 if config.VERBOSITY >= config.INFO_ALL:
541 print('XU.material: attempting space group %s' % self.sgrp)
542
543 # determine all unique positions for definition of a P1 space group
544 symops = self.symops
545 if not symops and hasattr(self, 'sgrp') and self.atoms:
546 label = sorted(allwyckp, key=lambda s: int(s[:-1]))
547 symops = allwyckp[label[-1]][1]
548 if config.VERBOSITY >= config.INFO_ALL:
549 print('XU.material: no symmetry operations in CIF-Dataset '
550 'using built in general positions.')
551 self.unique_positions = []
552 for el, (x, y, z), occ, biso in self.atoms:
553 unique_pos = []
554 for symop in symops:
555 pos = eval(symop, {'x': x, 'y': y, 'z': z})
556 pos = numpy.asarray(pos)
557 # check that position is within unit cell
558 pos = pos - numpy.round(pos, self.digits) // 1
559 # check if position is unique
560 unique = True
561 for upos in unique_pos:
562 if (numpy.round(upos, self.digits) ==
563 numpy.round(pos, self.digits)).all():
564 unique = False
565 if unique:
566 unique_pos.append(pos)
567 self.unique_positions.append((el, unique_pos, occ, biso))
568
569 # determine Wyckoff positions and free parameters of unit cell
570 if hasattr(self, 'sgrp'):
571 self.wp = []
572 self.occ = []
573 self.elements = []
574 self.biso = []
575 keys = list(allwyckp)
576 wpn = [int(re.sub(r"([a-zA-Z])", r"", k)) for k in keys]
577 for i, (el, (x, y, z), occ, biso) in enumerate(self.atoms):
578 # candidate positions from number of unique atoms
579 natoms = len(self.unique_positions[i][1])
580 wpcand = []
581 for j, n in enumerate(wpn):
582 if n == natoms:
583 wpcand.append((keys[j], allwyckp[keys[j]]))
584 for j, (k, wp) in enumerate(
585 sorted(wpcand, key=operator.itemgetter(1))):
586 parint, poslist = wp
587 for positem in poslist:
588 foundwp, xyz = testwp(parint, positem,
589 (x, y, z), self.digits)
590 if foundwp:
591 if xyz is None:
592 self.wp.append(k)
593 else:
594 self.wp.append((k, list(xyz)))
595 self.elements.append(el)
596 self.occ.append(occ)
597 self.biso.append(biso)
598 break
599 if foundwp:
600 break
601 if config.VERBOSITY >= config.INFO_ALL:
602 print('XU.material: %d of %d Wyckoff positions identified'
603 % (len(self.wp), len(self.atoms)))
604 if len(self.wp) < len(self.atoms):
605 print('XU.material: space group %s seems not to fit'
606 % self.sgrp)
607
608 # free unit cell parameters
609 self.crystal_system, nargs = sgl.sgrp_sym[self.sgrp_nr]
610 self.crystal_system += self.sgrp_suf
611 self.uc_params = []
612 p2i = {'a': 0, 'b': 1, 'c': 2,
613 'alpha': 0, 'beta': 1, 'gamma': 2}
614 for pname in sgl.sgrp_params[self.crystal_system][0]:
615 if pname in ('a', 'b', 'c'):
616 self.uc_params.append(self.lattice_const[p2i[pname]])
617 if pname in ('alpha', 'beta', 'gamma'):
618 self.uc_params.append(self.lattice_angles[p2i[pname]])
619
620 if len(self.wp) == len(self.atoms):
621 if config.VERBOSITY >= config.INFO_ALL:
622 print('XU.material: identified space group as %s'
623 % self.sgrp)
624 break
625
626 def SGLattice(self, use_p1=False):
627 """
628 create a SGLattice object with the structure from the CIF file
629 """
630 if not use_p1:
631 if hasattr(self, 'sgrp'):
632 if len(self.wp) == len(self.atoms):
633 return sgl.SGLattice(self.sgrp, *self.uc_params,
634 atoms=self.elements, pos=self.wp,
635 occ=self.occ, b=self.biso)
636 else:
637 if config.VERBOSITY >= config.INFO_LOW:
638 print('XU.material: Wyckoff positions missing, '
639 'using P1')
640 else:
641 if config.VERBOSITY >= config.INFO_LOW:
642 print('XU.material: space-group detection failed, '
643 'using P1')
644
645 atoms = []
646 pos = []
647 occ = []
648 biso = []
649 for element, positions, o, b in self.unique_positions:
650 for p in positions:
651 atoms.append(element)
652 pos.append(('1a', p))
653 occ.append(o)
654 biso.append(b)
655
656 return sgl.SGLattice(1, *itertools.chain(self.lattice_const,
657 self.lattice_angles),
658 atoms=atoms, pos=pos, occ=occ, b=biso)
659
660 def __str__(self):
661 """
662 returns a string with positions and names of the atoms
663 """
664 ostr = ""
665 ostr += "unit cell structure:"
666 if hasattr(self, 'sgrp'):
667 ostr += " %s %s %s\n" % (self.sgrp, self.crystal_system,
668 getattr(self, 'sgrp_name', ''))
669 else:
670 ostr += "\n"
671 ostr += "a: %8.4f b: %8.4f c: %8.4f\n" % tuple(self.lattice_const)
672 ostr += "alpha: %6.2f beta: %6.2f gamma: %6.2f\n" % tuple(
673 self.lattice_angles)
674 if self.unique_positions:
675 ostr += "Unique atom positions in unit cell\n"
676 for atom in self.unique_positions:
677 ostr += atom[0].name + " (%d): \n" % atom[0].num
678 for pos in atom[1]:
679 ostr += str(numpy.round(pos, self.digits)) + "\n"
680 return ostr
681
682
683 def cifexport(filename, mat):
684 """
685 function to export a Crystal instance to CIF file. This in particular
686 includes the atomic coordinates, however, ignores for example the elastic
687 parameters.
688 """
689
690 def unique_label(basename, names):
691 num = 1
692 name = '{name}{num:d}'.format(name=basename, num=num)
693 while name in names:
694 num += 1
695 name = '{name}{num:d}'.format(name=basename, num=num)
696 return name
697
698 general = """data_global
699 _chemical_formula_sum '{chemsum}'
700 _cell_length_a {a:.5f}
701 _cell_length_b {b:.5f}
702 _cell_length_c {c:.5f}
703 _cell_angle_alpha {alpha:.4f}
704 _cell_angle_beta {beta:.4f}
705 _cell_angle_gamma {gamma:.4f}
706 _cell_volume {vol:.3f}
707 _space_group_crystal_system {csystem}
708 _space_group_IT_number {sgrpnr}
709 _space_group_name_H-M_alt '{hmsymb}'
710 """
711
712 csystem = mat.lattice.crystal_system
713 if len(mat.lattice.space_group_suf) > 0:
714 csystem = csystem[:-len(mat.lattice.space_group_suf)]
715
716 ctxt = general.format(chemsum=mat.chemical_composition(with_spaces=True),
717 a=mat.a, b=mat.b, c=mat.c,
718 alpha=mat.alpha, beta=mat.beta, gamma=mat.gamma,
719 vol=mat.lattice.UnitCellVolume(),
720 csystem=csystem,
721 sgrpnr=mat.lattice.space_group_nr,
722 hmsymb=mat.lattice.name)
723
724 sgrpsuf = mat.lattice.space_group_suf[1:]
725 if sgrpsuf:
726 ctxt += '_symmetry_cell_setting {suf}\n'.format(suf=sgrpsuf)
727
728 symloop = """
729 loop_
730 _space_group_symop_operation_xyz
731 """
732
733 gplabel = sorted(wyckpos.wp[mat.lattice.space_group],
734 key=lambda s: int(s[:-1]))[-1]
735 gp = wyckpos.wp[mat.lattice.space_group][gplabel]
736
737 for pos in gp[1]:
738 symloop += "'" + pos.strip('()') + "'\n"
739
740 atomloop = """
741 loop_
742 _atom_site_label
743 _atom_site_type_symbol
744 _atom_site_symmetry_multiplicity
745 _atom_site_Wyckoff_symbol
746 _atom_site_fract_x
747 _atom_site_fract_y
748 _atom_site_fract_z
749 _atom_site_occupancy
750 _atom_site_B_iso_or_equiv
751 """
752 nidx = 0
753 allatoms = list(mat.lattice.base())
754 names = []
755 for at, pos, occ, b in mat.lattice._wbase:
756 wm, wl, dummy = re.split('([a-z])', pos[0])
757 nsite = int(wm)
758 x, y, z = allatoms[nidx][1]
759 names.append(unique_label(at.name, names))
760 atomloop += '%s %s %d %c %.5f %.5f %.5f %.4f %.4f\n' % (
761 names[-1], at.name, nsite, wl, x, y, z, occ, b)
762 nidx += nsite
763
764 with open(filename, 'w') as f:
765 f.write(ctxt)
766 f.write(symloop)
767 f.write(atomloop)
0 Notes about origin/copyright of files:
1
2 f[01]*.dat.xz: database files from XOP/DABAX project [1]
3
4 nist_atom.dat: included from NIST Physical Reference Database [2]
5
6 colors.dat: colors for atoms as found in Jmol [3]
7
8 atomic_radius.dat: atomic radii from Wikipedia [4]
9
10 [1] http://ftp.esrf.eu/pub/scisoft/xop2.3/
11 [2] http://www.nist.gov/pml/data/comp.cfm
12 [3] http://jmol.sourceforge.net/jscolors/
13 [4] https://en.wikipedia.org/wiki/Atomic_radii_of_the_elements_(data_page)
0 1,H,hydrogen,25
1 2,He,helium,120
2 3,Li,lithium,145
3 4,Be,beryllium,105
4 5,B,boron,85
5 6,C,carbon,70
6 7,N,nitrogen,65
7 8,O,oxygen,60
8 9,F,fluorine,50
9 10,Ne,neon,160
10 11,Na,sodium,180
11 12,Mg,magnesium,150
12 13,Al,aluminium,125
13 14,Si,silicon,110
14 15,P,phosphorus,100
15 16,S,sulfur,100
16 17,Cl,chlorine,100
17 18,Ar,argon,71
18 19,K,potassium,220
19 20,Ca,calcium,180
20 21,Sc,scandium,160
21 22,Ti,titanium,140
22 23,V,vanadium,135
23 24,Cr,chromium,140
24 25,Mn,manganese,140
25 26,Fe,iron,140
26 27,Co,cobalt,135
27 28,Ni,nickel,135
28 29,Cu,copper,135
29 30,Zn,zinc,135
30 31,Ga,gallium,130
31 32,Ge,germanium,125
32 33,As,arsenic,115
33 34,Se,selenium,115
34 35,Br,bromine,115
35 36,Kr,krypton,nan
36 37,Rb,rubidium,235
37 38,Sr,strontium,200
38 39,Y,yttrium,180
39 40,Zr,zirconium,155
40 41,Nb,niobium,145
41 42,Mo,molybdenum,145
42 43,Tc,technetium,135
43 44,Ru,ruthenium,130
44 45,Rh,rhodium,135
45 46,Pd,palladium,140
46 47,Ag,silver,160
47 48,Cd,cadmium,155
48 49,In,indium,155
49 50,Sn,tin,145
50 51,Sb,antimony,145
51 52,Te,tellurium,140
52 53,I,iodine,140
53 54,Xe,xenon,nan
54 55,Cs,caesium,260
55 56,Ba,barium,215
56 57,La,lanthanum,195
57 58,Ce,cerium,185
58 59,Pr,praseodymium,185
59 60,Nd,neodymium,185
60 61,Pm,promethium,185
61 62,Sm,samarium,185
62 63,Eu,europium,185
63 64,Gd,gadolinium,180
64 65,Tb,terbium,175
65 66,Dy,dysprosium,175
66 67,Ho,holmium,175
67 68,Er,erbium,175
68 69,Tm,thulium,175
69 70,Yb,ytterbium,175
70 71,Lu,lutetium,175
71 72,Hf,hafnium,155
72 73,Ta,tantalum,145
73 74,W,tungsten,135
74 75,Re,rhenium,135
75 76,Os,osmium,130
76 77,Ir,iridium,135
77 78,Pt,platinum,135
78 79,Au,gold,135
79 80,Hg,mercury,150
80 81,Tl,thallium,190
81 82,Pb,lead,180
82 83,Bi,bismuth,160
83 84,Po,polonium,190
84 85,At,astatine,nan
85 86,Rn,radon,nan
86 87,Fr,francium,nan
87 88,Ra,radium,215
88 89,Ac,actinium,195
89 90,Th,thorium,180
90 91,Pa,protactinium,180
91 92,U,uranium,175
92 93,Np,neptunium,175
93 94,Pu,plutonium,175
94 95,Am,americium,175
95 96,Cm,curium,nan
96 97,Bk,berkelium,nan
97 98,Cf,californium,nan
98 99,Es,einsteinium,nan
99 100,Fm,fermium,nan
100 101,Md,mendelevium,nan
101 102,No,nobelium,nan
102 103,Lr,lawrencium,nan
103 104,Rf,rutherfordium,nan
104 105,Db,dubnium,nan
105 106,Sg,seaborgium,nan
106 107,Bh,bohrium,nan
107 108,Hs,hassium,nan
108 109,Mt,meitnerium,nan
109 110,Ds,darmstadtium,nan
110 111,Rg,roentgenium,nan
111 112,Cn,copernicium,nan
112 113,Nh,nihonium,nan
113 114,Fl,flerovium,nan
114 115,Mc,moscovium,nan
115 116,Lv,livermorium,nan
116 117,Ts,tennessine,nan
117 118,Og,oganesson,nan
0 1 H [255,255,255]
1 2 He [217,255,255]
2 3 Li [204,128,255]
3 4 Be [194,255,0]
4 5 B [255,181,181]
5 6 C [144,144,144]
6 7 N [48,80,248]
7 8 O [255,13,13]
8 9 F [144,224,80]
9 10 Ne [179,227,245]
10 11 Na [171,92,242]
11 12 Mg [138,255,0]
12 13 Al [191,166,166]
13 14 Si [240,200,160]
14 15 P [255,128,0]
15 16 S [255,255,48]
16 17 Cl [31,240,31]
17 18 Ar [128,209,227]
18 19 K [143,64,212]
19 20 Ca [61,255,0]
20 21 Sc [230,230,230]
21 22 Ti [191,194,199]
22 23 V [166,166,171]
23 24 Cr [138,153,199]
24 25 Mn [156,122,199]
25 26 Fe [224,102,51]
26 27 Co [240,144,160]
27 28 Ni [80,208,80]
28 29 Cu [200,128,51]
29 30 Zn [125,128,176]
30 31 Ga [194,143,143]
31 32 Ge [102,143,143]
32 33 As [189,128,227]
33 34 Se [255,161,0]
34 35 Br [166,41,41]
35 36 Kr [92,184,209]
36 37 Rb [112,46,176]
37 38 Sr [0,255,0]
38 39 Y [148,255,255]
39 40 Zr [148,224,224]
40 41 Nb [115,194,201]
41 42 Mo [84,181,181]
42 43 Tc [59,158,158]
43 44 Ru [36,143,143]
44 45 Rh [10,125,140]
45 46 Pd [0,105,133]
46 47 Ag [192,192,192]
47 48 Cd [255,217,143]
48 49 In [166,117,115]
49 50 Sn [102,128,128]
50 51 Sb [158,99,181]
51 52 Te [212,122,0]
52 53 I [148,0,148]
53 54 Xe [66,158,176]
54 55 Cs [87,23,143]
55 56 Ba [0,201,0]
56 57 La [112,212,255]
57 58 Ce [255,255,199]
58 59 Pr [217,255,199]
59 60 Nd [199,255,199]
60 61 Pm [163,255,199]
61 62 Sm [143,255,199]
62 63 Eu [97,255,199]
63 64 Gd [69,255,199]
64 65 Tb [48,255,199]
65 66 Dy [31,255,199]
66 67 Ho [0,255,156]
67 68 Er [0,230,117]
68 69 Tm [0,212,82]
69 70 Yb [0,191,56]
70 71 Lu [0,171,36]
71 72 Hf [77,194,255]
72 73 Ta [77,166,255]
73 74 W [33,148,214]
74 75 Re [38,125,171]
75 76 Os [38,102,150]
76 77 Ir [23,84,135]
77 78 Pt [208,208,224]
78 79 Au [255,209,35]
79 80 Hg [184,184,208]
80 81 Tl [166,84,77]
81 82 Pb [87,89,97]
82 83 Bi [158,79,181]
83 84 Po [171,92,0]
84 85 At [117,79,69]
85 86 Rn [66,130,150]
86 87 Fr [66,0,102]
87 88 Ra [0,125,0]
88 89 Ac [112,171,250]
89 90 Th [0,186,255]
90 91 Pa [0,161,255]
91 92 U [0,143,255]
92 93 Np [0,128,255]
93 94 Pu [0,107,255]
94 95 Am [84,92,242]
95 96 Cm [120,92,227]
96 97 Bk [138,79,227]
97 98 Cf [161,54,212]
98 99 Es [179,31,212]
99 100 Fm [179,31,186]
100 101 Md [179,13,166]
101 102 No [189,13,135]
102 103 Lr [199,0,102]
103 104 Rf [204,0,89]
104 105 Db [209,0,79]
105 106 Sg [217,0,69]
106 107 Bh [224,0,56]
107 108 Hs [230,0,46]
108 109 Mt [235,0,38]
0 # This file is part of xrayutilities.
1 #
2 # data included from
3 # NIST > Physical Reference Data > Atomic Weights
4 # see http://www.nist.gov/pml/data/comp.cfm
5 # use settings:
6 # Linearized ASCII Output
7 # Most common isotopes
8
9 Atomic Number = 1
10 Atomic Symbol = H
11 Mass Number = 1
12 Relative Atomic Mass = 1.00782503223(9)
13 Isotopic Composition = 0.999885(70)
14 Standard Atomic Weight = [1.00784,1.00811]
15 Notes = m
16
17 Atomic Number = 1
18 Atomic Symbol = D
19 Mass Number = 2
20 Relative Atomic Mass = 2.01410177812(12)
21 Isotopic Composition = 0.000115(70)
22 Standard Atomic Weight = [1.00784,1.00811]
23 Notes = m
24
25 Atomic Number = 1
26 Atomic Symbol = T
27 Mass Number = 3
28 Relative Atomic Mass = 3.0160492779(24)
29 Isotopic Composition =
30 Standard Atomic Weight = [1.00784,1.00811]
31 Notes = m
32
33 Atomic Number = 2
34 Atomic Symbol = He
35 Mass Number = 3
36 Relative Atomic Mass = 3.0160293201(25)
37 Isotopic Composition = 0.00000134(3)
38 Standard Atomic Weight = 4.002602(2)
39 Notes = g,r
40
41 Atomic Number = 2
42 Atomic Symbol = He
43 Mass Number = 4
44 Relative Atomic Mass = 4.00260325413(6)
45 Isotopic Composition = 0.99999866(3)
46 Standard Atomic Weight = 4.002602(2)
47 Notes = g,r
48
49 Atomic Number = 3
50 Atomic Symbol = Li
51 Mass Number = 6
52 Relative Atomic Mass = 6.0151228874(16)
53 Isotopic Composition = 0.0759(4)
54 Standard Atomic Weight = [6.938,6.997]
55 Notes = m
56
57 Atomic Number = 3
58 Atomic Symbol = Li
59 Mass Number = 7
60 Relative Atomic Mass = 7.0160034366(45)
61 Isotopic Composition = 0.9241(4)
62 Standard Atomic Weight = [6.938,6.997]
63 Notes = m
64
65 Atomic Number = 4
66 Atomic Symbol = Be
67 Mass Number = 9
68 Relative Atomic Mass = 9.012183065(82)
69 Isotopic Composition = 1
70 Standard Atomic Weight = 9.0121831(5)
71 Notes =
72
73 Atomic Number = 5
74 Atomic Symbol = B
75 Mass Number = 10
76 Relative Atomic Mass = 10.01293695(41)
77 Isotopic Composition = 0.199(7)
78 Standard Atomic Weight = [10.806,10.821]
79 Notes = m
80
81 Atomic Number = 5
82 Atomic Symbol = B
83 Mass Number = 11
84 Relative Atomic Mass = 11.00930536(45)
85 Isotopic Composition = 0.801(7)
86 Standard Atomic Weight = [10.806,10.821]
87 Notes = m
88
89 Atomic Number = 6
90 Atomic Symbol = C
91 Mass Number = 12
92 Relative Atomic Mass = 12.0000000(00)
93 Isotopic Composition = 0.9893(8)
94 Standard Atomic Weight = [12.0096,12.0116]
95 Notes =
96
97 Atomic Number = 6
98 Atomic Symbol = C
99 Mass Number = 13
100 Relative Atomic Mass = 13.00335483507(23)
101 Isotopic Composition = 0.0107(8)
102 Standard Atomic Weight = [12.0096,12.0116]
103 Notes =
104
105 Atomic Number = 6
106 Atomic Symbol = C
107 Mass Number = 14
108 Relative Atomic Mass = 14.0032419884(40)
109 Isotopic Composition =
110 Standard Atomic Weight = [12.0096,12.0116]
111 Notes =
112
113 Atomic Number = 7
114 Atomic Symbol = N
115 Mass Number = 14
116 Relative Atomic Mass = 14.00307400443(20)
117 Isotopic Composition = 0.99636(20)
118 Standard Atomic Weight = [14.00643,14.00728]
119 Notes =
120
121 Atomic Number = 7
122 Atomic Symbol = N
123 Mass Number = 15
124 Relative Atomic Mass = 15.00010889888(64)
125 Isotopic Composition = 0.00364(20)
126 Standard Atomic Weight = [14.00643,14.00728]
127 Notes =
128
129 Atomic Number = 8
130 Atomic Symbol = O
131 Mass Number = 16
132 Relative Atomic Mass = 15.99491461957(17)
133 Isotopic Composition = 0.99757(16)
134 Standard Atomic Weight = [15.99903,15.99977]
135 Notes =
136
137 Atomic Number = 8
138 Atomic Symbol = O
139 Mass Number = 17
140 Relative Atomic Mass = 16.99913175650(69)
141 Isotopic Composition = 0.00038(1)
142 Standard Atomic Weight = [15.99903,15.99977]
143 Notes =
144
145 Atomic Number = 8
146 Atomic Symbol = O
147 Mass Number = 18
148 Relative Atomic Mass = 17.99915961286(76)
149 Isotopic Composition = 0.00205(14)
150 Standard Atomic Weight = [15.99903,15.99977]
151 Notes =
152
153 Atomic Number = 9
154 Atomic Symbol = F
155 Mass Number = 19
156 Relative Atomic Mass = 18.99840316273(92)
157 Isotopic Composition = 1
158 Standard Atomic Weight = 18.998403163(6)
159 Notes =
160
161 Atomic Number = 10
162 Atomic Symbol = Ne
163 Mass Number = 20
164 Relative Atomic Mass = 19.9924401762(17)
165 Isotopic Composition = 0.9048(3)
166 Standard Atomic Weight = 20.1797(6)
167 Notes = g,m
168
169 Atomic Number = 10
170 Atomic Symbol = Ne
171 Mass Number = 21
172 Relative Atomic Mass = 20.993846685(41)
173 Isotopic Composition = 0.0027(1)
174 Standard Atomic Weight = 20.1797(6)
175 Notes = g,m
176
177 Atomic Number = 10
178 Atomic Symbol = Ne
179 Mass Number = 22
180 Relative Atomic Mass = 21.991385114(18)
181 Isotopic Composition = 0.0925(3)
182 Standard Atomic Weight = 20.1797(6)
183 Notes = g,m
184
185 Atomic Number = 11
186 Atomic Symbol = Na
187 Mass Number = 23
188 Relative Atomic Mass = 22.9897692820(19)
189 Isotopic Composition = 1
190 Standard Atomic Weight = 22.98976928(2)
191 Notes =
192
193 Atomic Number = 12
194 Atomic Symbol = Mg
195 Mass Number = 24
196 Relative Atomic Mass = 23.985041697(14)
197 Isotopic Composition = 0.7899(4)
198 Standard Atomic Weight = [24.304,24.307]
199 Notes =
200
201 Atomic Number = 12
202 Atomic Symbol = Mg
203 Mass Number = 25
204 Relative Atomic Mass = 24.985836976(50)
205 Isotopic Composition = 0.1000(1)
206 Standard Atomic Weight = [24.304,24.307]
207 Notes =
208
209 Atomic Number = 12
210 Atomic Symbol = Mg
211 Mass Number = 26
212 Relative Atomic Mass = 25.982592968(31)
213 Isotopic Composition = 0.1101(3)
214 Standard Atomic Weight = [24.304,24.307]
215 Notes =
216
217 Atomic Number = 13
218 Atomic Symbol = Al
219 Mass Number = 27
220 Relative Atomic Mass = 26.98153853(11)
221 Isotopic Composition = 1
222 Standard Atomic Weight = 26.9815385(7)
223 Notes =
224
225 Atomic Number = 14
226 Atomic Symbol = Si
227 Mass Number = 28
228 Relative Atomic Mass = 27.97692653465(44)
229 Isotopic Composition = 0.92223(19)
230 Standard Atomic Weight = [28.084,28.086]
231 Notes =
232
233 Atomic Number = 14
234 Atomic Symbol = Si
235 Mass Number = 29
236 Relative Atomic Mass = 28.97649466490(52)
237 Isotopic Composition = 0.04685(8)
238 Standard Atomic Weight = [28.084,28.086]
239 Notes =
240
241 Atomic Number = 14
242 Atomic Symbol = Si
243 Mass Number = 30
244 Relative Atomic Mass = 29.973770136(23)
245 Isotopic Composition = 0.03092(11)
246 Standard Atomic Weight = [28.084,28.086]
247 Notes =
248
249 Atomic Number = 15
250 Atomic Symbol = P
251 Mass Number = 31
252 Relative Atomic Mass = 30.97376199842(70)
253 Isotopic Composition = 1
254 Standard Atomic Weight = 30.973761998(5)
255 Notes =
256
257 Atomic Number = 16
258 Atomic Symbol = S
259 Mass Number = 32
260 Relative Atomic Mass = 31.9720711744(14)
261 Isotopic Composition = 0.9499(26)
262 Standard Atomic Weight = [32.059,32.076]
263 Notes =
264
265 Atomic Number = 16
266 Atomic Symbol = S
267 Mass Number = 33
268 Relative Atomic Mass = 32.9714589098(15)
269 Isotopic Composition = 0.0075(2)
270 Standard Atomic Weight = [32.059,32.076]
271 Notes =
272
273 Atomic Number = 16
274 Atomic Symbol = S
275 Mass Number = 34
276 Relative Atomic Mass = 33.967867004(47)
277 Isotopic Composition = 0.0425(24)
278 Standard Atomic Weight = [32.059,32.076]
279 Notes =
280
281 Atomic Number = 16
282 Atomic Symbol = S
283 Mass Number = 36
284 Relative Atomic Mass = 35.96708071(20)
285 Isotopic Composition = 0.0001(1)
286 Standard Atomic Weight = [32.059,32.076]
287 Notes =
288
289 Atomic Number = 17
290 Atomic Symbol = Cl
291 Mass Number = 35
292 Relative Atomic Mass = 34.968852682(37)
293 Isotopic Composition = 0.7576(10)
294 Standard Atomic Weight = [35.446,35.457]
295 Notes = m
296
297 Atomic Number = 17
298 Atomic Symbol = Cl
299 Mass Number = 37
300 Relative Atomic Mass = 36.965902602(55)
301 Isotopic Composition = 0.2424(10)
302 Standard Atomic Weight = [35.446,35.457]
303 Notes = m
304
305 Atomic Number = 18
306 Atomic Symbol = Ar
307 Mass Number = 36
308 Relative Atomic Mass = 35.967545105(28)
309 Isotopic Composition = 0.003336(21)
310 Standard Atomic Weight = 39.948(1)
311 Notes = g,r
312
313 Atomic Number = 18
314 Atomic Symbol = Ar
315 Mass Number = 38
316 Relative Atomic Mass = 37.96273211(21)
317 Isotopic Composition = 0.000629(7)
318 Standard Atomic Weight = 39.948(1)
319 Notes = g,r
320
321 Atomic Number = 18
322 Atomic Symbol = Ar
323 Mass Number = 40
324 Relative Atomic Mass = 39.9623831237(24)
325 Isotopic Composition = 0.996035(25)
326 Standard Atomic Weight = 39.948(1)
327 Notes = g,r
328
329 Atomic Number = 19
330 Atomic Symbol = K
331 Mass Number = 39
332 Relative Atomic Mass = 38.9637064864(49)
333 Isotopic Composition = 0.932581(44)
334 Standard Atomic Weight = 39.0983(1)
335 Notes =
336
337 Atomic Number = 19
338 Atomic Symbol = K
339 Mass Number = 40
340 Relative Atomic Mass = 39.963998166(60)
341 Isotopic Composition = 0.000117(1)
342 Standard Atomic Weight = 39.0983(1)
343 Notes =
344
345 Atomic Number = 19
346 Atomic Symbol = K
347 Mass Number = 41
348 Relative Atomic Mass = 40.9618252579(41)
349 Isotopic Composition = 0.067302(44)
350 Standard Atomic Weight = 39.0983(1)
351 Notes =
352
353 Atomic Number = 20
354 Atomic Symbol = Ca
355 Mass Number = 40
356 Relative Atomic Mass = 39.962590863(22)
357 Isotopic Composition = 0.96941(156)
358 Standard Atomic Weight = 40.078(4)
359 Notes = g
360
361 Atomic Number = 20
362 Atomic Symbol = Ca
363 Mass Number = 42
364 Relative Atomic Mass = 41.95861783(16)
365 Isotopic Composition = 0.00647(23)
366 Standard Atomic Weight = 40.078(4)
367 Notes = g
368
369 Atomic Number = 20
370 Atomic Symbol = Ca
371 Mass Number = 43
372 Relative Atomic Mass = 42.95876644(24)
373 Isotopic Composition = 0.00135(10)
374 Standard Atomic Weight = 40.078(4)
375 Notes = g
376
377 Atomic Number = 20
378 Atomic Symbol = Ca
379 Mass Number = 44
380 Relative Atomic Mass = 43.95548156(35)
381 Isotopic Composition = 0.02086(110)
382 Standard Atomic Weight = 40.078(4)
383 Notes = g
384
385 Atomic Number = 20
386 Atomic Symbol = Ca
387 Mass Number = 46
388 Relative Atomic Mass = 45.9536890(24)
389 Isotopic Composition = 0.00004(3)
390 Standard Atomic Weight = 40.078(4)
391 Notes = g
392
393 Atomic Number = 20
394 Atomic Symbol = Ca
395 Mass Number = 48
396 Relative Atomic Mass = 47.95252276(13)
397 Isotopic Composition = 0.00187(21)
398 Standard Atomic Weight = 40.078(4)
399 Notes = g
400
401 Atomic Number = 21
402 Atomic Symbol = Sc
403 Mass Number = 45
404 Relative Atomic Mass = 44.95590828(77)
405 Isotopic Composition = 1
406 Standard Atomic Weight = 44.955908(5)
407 Notes =
408
409 Atomic Number = 22
410 Atomic Symbol = Ti
411 Mass Number = 46
412 Relative Atomic Mass = 45.95262772(35)
413 Isotopic Composition = 0.0825(3)
414 Standard Atomic Weight = 47.867(1)
415 Notes =
416
417 Atomic Number = 22
418 Atomic Symbol = Ti
419 Mass Number = 47
420 Relative Atomic Mass = 46.95175879(38)
421 Isotopic Composition = 0.0744(2)
422 Standard Atomic Weight = 47.867(1)
423 Notes =
424
425 Atomic Number = 22
426 Atomic Symbol = Ti
427 Mass Number = 48
428 Relative Atomic Mass = 47.94794198(38)
429 Isotopic Composition = 0.7372(3)
430 Standard Atomic Weight = 47.867(1)
431 Notes =
432
433 Atomic Number = 22
434 Atomic Symbol = Ti
435 Mass Number = 49
436 Relative Atomic Mass = 48.94786568(39)
437 Isotopic Composition = 0.0541(2)
438 Standard Atomic Weight = 47.867(1)
439 Notes =
440
441 Atomic Number = 22
442 Atomic Symbol = Ti
443 Mass Number = 50
444 Relative Atomic Mass = 49.94478689(39)
445 Isotopic Composition = 0.0518(2)
446 Standard Atomic Weight = 47.867(1)
447 Notes =
448
449 Atomic Number = 23
450 Atomic Symbol = V
451 Mass Number = 50
452 Relative Atomic Mass = 49.94715601(95)
453 Isotopic Composition = 0.00250(4)
454 Standard Atomic Weight = 50.9415(1)
455 Notes =
456
457 Atomic Number = 23
458 Atomic Symbol = V
459 Mass Number = 51
460 Relative Atomic Mass = 50.94395704(94)
461 Isotopic Composition = 0.99750(4)
462 Standard Atomic Weight = 50.9415(1)
463 Notes =
464
465 Atomic Number = 24
466 Atomic Symbol = Cr
467 Mass Number = 50
468 Relative Atomic Mass = 49.94604183(94)
469 Isotopic Composition = 0.04345(13)
470 Standard Atomic Weight = 51.9961(6)
471 Notes =
472
473 Atomic Number = 24
474 Atomic Symbol = Cr
475 Mass Number = 52
476 Relative Atomic Mass = 51.94050623(63)
477 Isotopic Composition = 0.83789(18)
478 Standard Atomic Weight = 51.9961(6)
479 Notes =
480
481 Atomic Number = 24
482 Atomic Symbol = Cr
483 Mass Number = 53
484 Relative Atomic Mass = 52.94064815(62)
485 Isotopic Composition = 0.09501(17)
486 Standard Atomic Weight = 51.9961(6)
487 Notes =
488
489 Atomic Number = 24
490 Atomic Symbol = Cr
491 Mass Number = 54
492 Relative Atomic Mass = 53.93887916(61)
493 Isotopic Composition = 0.02365(7)
494 Standard Atomic Weight = 51.9961(6)
495 Notes =
496
497 Atomic Number = 25
498 Atomic Symbol = Mn
499 Mass Number = 55
500 Relative Atomic Mass = 54.93804391(48)
501 Isotopic Composition = 1
502 Standard Atomic Weight = 54.938044(3)
503 Notes =
504
505 Atomic Number = 26
506 Atomic Symbol = Fe
507 Mass Number = 54
508 Relative Atomic Mass = 53.93960899(53)
509 Isotopic Composition = 0.05845(35)
510 Standard Atomic Weight = 55.845(2)
511 Notes =
512
513 Atomic Number = 26
514 Atomic Symbol = Fe
515 Mass Number = 56
516 Relative Atomic Mass = 55.93493633(49)
517 Isotopic Composition = 0.91754(36)
518 Standard Atomic Weight = 55.845(2)
519 Notes =
520
521 Atomic Number = 26
522 Atomic Symbol = Fe
523 Mass Number = 57
524 Relative Atomic Mass = 56.93539284(49)
525 Isotopic Composition = 0.02119(10)
526 Standard Atomic Weight = 55.845(2)
527 Notes =
528
529 Atomic Number = 26
530 Atomic Symbol = Fe
531 Mass Number = 58
532 Relative Atomic Mass = 57.93327443(53)
533 Isotopic Composition = 0.00282(4)
534 Standard Atomic Weight = 55.845(2)
535 Notes =
536
537 Atomic Number = 27
538 Atomic Symbol = Co
539 Mass Number = 59
540 Relative Atomic Mass = 58.93319429(56)
541 Isotopic Composition = 1
542 Standard Atomic Weight = 58.933194(4)
543 Notes =
544
545 Atomic Number = 28
546 Atomic Symbol = Ni
547 Mass Number = 58
548 Relative Atomic Mass = 57.93534241(52)
549 Isotopic Composition = 0.68077(19)
550 Standard Atomic Weight = 58.6934(4)
551 Notes = r
552
553 Atomic Number = 28
554 Atomic Symbol = Ni
555 Mass Number = 60
556 Relative Atomic Mass = 59.93078588(52)
557 Isotopic Composition = 0.26223(15)
558 Standard Atomic Weight = 58.6934(4)
559 Notes = r
560
561 Atomic Number = 28
562 Atomic Symbol = Ni
563 Mass Number = 61
564 Relative Atomic Mass = 60.93105557(52)
565 Isotopic Composition = 0.011399(13)
566 Standard Atomic Weight = 58.6934(4)
567 Notes = r
568
569 Atomic Number = 28
570 Atomic Symbol = Ni
571 Mass Number = 62
572 Relative Atomic Mass = 61.92834537(55)
573 Isotopic Composition = 0.036346(40)
574 Standard Atomic Weight = 58.6934(4)
575 Notes = r
576
577 Atomic Number = 28
578 Atomic Symbol = Ni
579 Mass Number = 64
580 Relative Atomic Mass = 63.92796682(58)
581 Isotopic Composition = 0.009255(19)
582 Standard Atomic Weight = 58.6934(4)
583 Notes = r
584
585 Atomic Number = 29
586 Atomic Symbol = Cu
587 Mass Number = 63
588 Relative Atomic Mass = 62.92959772(56)
589 Isotopic Composition = 0.6915(15)
590 Standard Atomic Weight = 63.546(3)
591 Notes = r
592
593 Atomic Number = 29
594 Atomic Symbol = Cu
595 Mass Number = 65
596 Relative Atomic Mass = 64.92778970(71)
597 Isotopic Composition = 0.3085(15)
598 Standard Atomic Weight = 63.546(3)
599 Notes = r
600
601 Atomic Number = 30
602 Atomic Symbol = Zn
603 Mass Number = 64
604 Relative Atomic Mass = 63.92914201(71)
605 Isotopic Composition = 0.4917(75)
606 Standard Atomic Weight = 65.38(2)
607 Notes = r
608
609 Atomic Number = 30
610 Atomic Symbol = Zn
611 Mass Number = 66
612 Relative Atomic Mass = 65.92603381(94)
613 Isotopic Composition = 0.2773(98)
614 Standard Atomic Weight = 65.38(2)
615 Notes = r
616
617 Atomic Number = 30
618 Atomic Symbol = Zn
619 Mass Number = 67
620 Relative Atomic Mass = 66.92712775(96)
621 Isotopic Composition = 0.0404(16)
622 Standard Atomic Weight = 65.38(2)
623 Notes = r
624
625 Atomic Number = 30
626 Atomic Symbol = Zn
627 Mass Number = 68
628 Relative Atomic Mass = 67.92484455(98)
629 Isotopic Composition = 0.1845(63)
630 Standard Atomic Weight = 65.38(2)
631 Notes = r
632
633 Atomic Number = 30
634 Atomic Symbol = Zn
635 Mass Number = 70
636 Relative Atomic Mass = 69.9253192(21)
637 Isotopic Composition = 0.0061(10)
638 Standard Atomic Weight = 65.38(2)
639 Notes = r
640
641 Atomic Number = 31
642 Atomic Symbol = Ga
643 Mass Number = 69
644 Relative Atomic Mass = 68.9255735(13)
645 Isotopic Composition = 0.60108(9)
646 Standard Atomic Weight = 69.723(1)
647 Notes =
648
649 Atomic Number = 31
650 Atomic Symbol = Ga
651 Mass Number = 71
652 Relative Atomic Mass = 70.92470258(87)
653 Isotopic Composition = 0.39892(9)
654 Standard Atomic Weight = 69.723(1)
655 Notes =
656
657 Atomic Number = 32
658 Atomic Symbol = Ge
659 Mass Number = 70
660 Relative Atomic Mass = 69.92424875(90)
661 Isotopic Composition = 0.2057(27)
662 Standard Atomic Weight = 72.630(8)
663 Notes =
664
665 Atomic Number = 32
666 Atomic Symbol = Ge
667 Mass Number = 72
668 Relative Atomic Mass = 71.922075826(81)
669 Isotopic Composition = 0.2745(32)
670 Standard Atomic Weight = 72.630(8)
671 Notes =
672
673 Atomic Number = 32
674 Atomic Symbol = Ge
675 Mass Number = 73
676 Relative Atomic Mass = 72.923458956(61)
677 Isotopic Composition = 0.0775(12)
678 Standard Atomic Weight = 72.630(8)
679 Notes =
680
681 Atomic Number = 32
682 Atomic Symbol = Ge
683 Mass Number = 74
684 Relative Atomic Mass = 73.921177761(13)
685 Isotopic Composition = 0.3650(20)
686 Standard Atomic Weight = 72.630(8)
687 Notes =
688
689 Atomic Number = 32
690 Atomic Symbol = Ge
691 Mass Number = 76
692 Relative Atomic Mass = 75.921402726(19)
693 Isotopic Composition = 0.0773(12)
694 Standard Atomic Weight = 72.630(8)
695 Notes =
696
697 Atomic Number = 33
698 Atomic Symbol = As
699 Mass Number = 75
700 Relative Atomic Mass = 74.92159457(95)
701 Isotopic Composition = 1
702 Standard Atomic Weight = 74.921595(6)
703 Notes =
704
705 Atomic Number = 34
706 Atomic Symbol = Se
707 Mass Number = 74
708 Relative Atomic Mass = 73.922475934(15)
709 Isotopic Composition = 0.0089(4)
710 Standard Atomic Weight = 78.971(8)
711 Notes = r
712
713 Atomic Number = 34
714 Atomic Symbol = Se
715 Mass Number = 76
716 Relative Atomic Mass = 75.919213704(17)
717 Isotopic Composition = 0.0937(29)
718 Standard Atomic Weight = 78.971(8)
719 Notes = r
720
721 Atomic Number = 34
722 Atomic Symbol = Se
723 Mass Number = 77
724 Relative Atomic Mass = 76.919914154(67)
725 Isotopic Composition = 0.0763(16)
726 Standard Atomic Weight = 78.971(8)
727 Notes = r
728
729 Atomic Number = 34
730 Atomic Symbol = Se
731 Mass Number = 78
732 Relative Atomic Mass = 77.91730928(20)
733 Isotopic Composition = 0.2377(28)
734 Standard Atomic Weight = 78.971(8)
735 Notes = r
736
737 Atomic Number = 34
738 Atomic Symbol = Se
739 Mass Number = 80
740 Relative Atomic Mass = 79.9165218(13)
741 Isotopic Composition = 0.4961(41)
742 Standard Atomic Weight = 78.971(8)
743 Notes = r
744
745 Atomic Number = 34
746 Atomic Symbol = Se
747 Mass Number = 82
748 Relative Atomic Mass = 81.9166995(15)
749 Isotopic Composition = 0.0873(22)
750 Standard Atomic Weight = 78.971(8)
751 Notes = r
752
753 Atomic Number = 35
754 Atomic Symbol = Br
755 Mass Number = 79
756 Relative Atomic Mass = 78.9183376(14)
757 Isotopic Composition = 0.5069(7)
758 Standard Atomic Weight = [79.901,79.907]
759 Notes =
760
761 Atomic Number = 35
762 Atomic Symbol = Br
763 Mass Number = 81
764 Relative Atomic Mass = 80.9162897(14)
765 Isotopic Composition = 0.4931(7)
766 Standard Atomic Weight = [79.901,79.907]
767 Notes =
768
769 Atomic Number = 36
770 Atomic Symbol = Kr
771 Mass Number = 78
772 Relative Atomic Mass = 77.92036494(76)
773 Isotopic Composition = 0.00355(3)
774 Standard Atomic Weight = 83.798(2)
775 Notes = g,m
776
777 Atomic Number = 36
778 Atomic Symbol = Kr
779 Mass Number = 80
780 Relative Atomic Mass = 79.91637808(75)
781 Isotopic Composition = 0.02286(10)
782 Standard Atomic Weight = 83.798(2)
783 Notes = g,m
784
785 Atomic Number = 36
786 Atomic Symbol = Kr
787 Mass Number = 82
788 Relative Atomic Mass = 81.91348273(94)
789 Isotopic Composition = 0.11593(31)
790 Standard Atomic Weight = 83.798(2)
791 Notes = g,m
792
793 Atomic Number = 36
794 Atomic Symbol = Kr
795 Mass Number = 83
796 Relative Atomic Mass = 82.91412716(32)
797 Isotopic Composition = 0.11500(19)
798 Standard Atomic Weight = 83.798(2)
799 Notes = g,m
800
801 Atomic Number = 36
802 Atomic Symbol = Kr
803 Mass Number = 84
804 Relative Atomic Mass = 83.9114977282(44)
805 Isotopic Composition = 0.56987(15)
806 Standard Atomic Weight = 83.798(2)
807 Notes = g,m
808
809 Atomic Number = 36
810 Atomic Symbol = Kr
811 Mass Number = 86
812 Relative Atomic Mass = 85.9106106269(41)
813 Isotopic Composition = 0.17279(41)
814 Standard Atomic Weight = 83.798(2)
815 Notes = g,m
816
817 Atomic Number = 37
818 Atomic Symbol = Rb
819 Mass Number = 85
820 Relative Atomic Mass = 84.9117897379(54)
821 Isotopic Composition = 0.7217(2)
822 Standard Atomic Weight = 85.4678(3)
823 Notes = g
824
825 Atomic Number = 37
826 Atomic Symbol = Rb
827 Mass Number = 87
828 Relative Atomic Mass = 86.9091805310(60)
829 Isotopic Composition = 0.2783(2)
830 Standard Atomic Weight = 85.4678(3)
831 Notes = g
832
833 Atomic Number = 38
834 Atomic Symbol = Sr
835 Mass Number = 84
836 Relative Atomic Mass = 83.9134191(13)
837 Isotopic Composition = 0.0056(1)
838 Standard Atomic Weight = 87.62(1)
839 Notes = g,r
840
841 Atomic Number = 38
842 Atomic Symbol = Sr
843 Mass Number = 86
844 Relative Atomic Mass = 85.9092606(12)
845 Isotopic Composition = 0.0986(1)
846 Standard Atomic Weight = 87.62(1)
847 Notes = g,r
848
849 Atomic Number = 38
850 Atomic Symbol = Sr
851 Mass Number = 87
852 Relative Atomic Mass = 86.9088775(12)
853 Isotopic Composition = 0.0700(1)
854 Standard Atomic Weight = 87.62(1)
855 Notes = g,r
856
857 Atomic Number = 38
858 Atomic Symbol = Sr
859 Mass Number = 88
860 Relative Atomic Mass = 87.9056125(12)
861 Isotopic Composition = 0.8258(1)
862 Standard Atomic Weight = 87.62(1)
863 Notes = g,r
864
865 Atomic Number = 39
866 Atomic Symbol = Y
867 Mass Number = 89
868 Relative Atomic Mass = 88.9058403(24)
869 Isotopic Composition = 1
870 Standard Atomic Weight = 88.90584(2)
871 Notes =
872
873 Atomic Number = 40
874 Atomic Symbol = Zr
875 Mass Number = 90
876 Relative Atomic Mass = 89.9046977(20)
877 Isotopic Composition = 0.5145(40)
878 Standard Atomic Weight = 91.224(2)
879 Notes = g
880
881 Atomic Number = 40
882 Atomic Symbol = Zr
883 Mass Number = 91
884 Relative Atomic Mass = 90.9056396(20)
885 Isotopic Composition = 0.1122(5)
886 Standard Atomic Weight = 91.224(2)
887 Notes = g
888
889 Atomic Number = 40
890 Atomic Symbol = Zr
891 Mass Number = 92
892 Relative Atomic Mass = 91.9050347(20)
893 Isotopic Composition = 0.1715(8)
894 Standard Atomic Weight = 91.224(2)
895 Notes = g
896
897 Atomic Number = 40
898 Atomic Symbol = Zr
899 Mass Number = 94
900 Relative Atomic Mass = 93.9063108(20)
901 Isotopic Composition = 0.1738(28)
902 Standard Atomic Weight = 91.224(2)
903 Notes = g
904
905 Atomic Number = 40
906 Atomic Symbol = Zr
907 Mass Number = 96
908 Relative Atomic Mass = 95.9082714(21)
909 Isotopic Composition = 0.0280(9)
910 Standard Atomic Weight = 91.224(2)
911 Notes = g
912
913 Atomic Number = 41
914 Atomic Symbol = Nb
915 Mass Number = 93
916 Relative Atomic Mass = 92.9063730(20)
917 Isotopic Composition = 1
918 Standard Atomic Weight = 92.90637(2)
919 Notes =
920
921 Atomic Number = 42
922 Atomic Symbol = Mo
923 Mass Number = 92
924 Relative Atomic Mass = 91.90680796(84)
925 Isotopic Composition = 0.1453(30)
926 Standard Atomic Weight = 95.95(1)
927 Notes = g
928
929 Atomic Number = 42
930 Atomic Symbol = Mo
931 Mass Number = 94
932 Relative Atomic Mass = 93.90508490(48)
933 Isotopic Composition = 0.0915(9)
934 Standard Atomic Weight = 95.95(1)
935 Notes = g
936
937 Atomic Number = 42
938 Atomic Symbol = Mo
939 Mass Number = 95
940 Relative Atomic Mass = 94.90583877(47)
941 Isotopic Composition = 0.1584(11)
942 Standard Atomic Weight = 95.95(1)
943 Notes = g
944
945 Atomic Number = 42
946 Atomic Symbol = Mo
947 Mass Number = 96
948 Relative Atomic Mass = 95.90467612(47)
949 Isotopic Composition = 0.1667(15)
950 Standard Atomic Weight = 95.95(1)
951 Notes = g
952
953 Atomic Number = 42
954 Atomic Symbol = Mo
955 Mass Number = 97
956 Relative Atomic Mass = 96.90601812(49)
957 Isotopic Composition = 0.0960(14)
958 Standard Atomic Weight = 95.95(1)
959 Notes = g
960
961 Atomic Number = 42
962 Atomic Symbol = Mo
963 Mass Number = 98
964 Relative Atomic Mass = 97.90540482(49)
965 Isotopic Composition = 0.2439(37)
966 Standard Atomic Weight = 95.95(1)
967 Notes = g
968
969 Atomic Number = 42
970 Atomic Symbol = Mo
971 Mass Number = 100
972 Relative Atomic Mass = 99.9074718(11)
973 Isotopic Composition = 0.0982(31)
974 Standard Atomic Weight = 95.95(1)
975 Notes = g
976
977 Atomic Number = 43
978 Atomic Symbol = Tc
979 Mass Number = 97
980 Relative Atomic Mass = 96.9063667(40)
981 Isotopic Composition =
982 Standard Atomic Weight = [98]
983 Notes =
984
985 Atomic Number = 43
986 Atomic Symbol = Tc
987 Mass Number = 98
988 Relative Atomic Mass = 97.9072124(36)
989 Isotopic Composition =
990 Standard Atomic Weight = [98]
991 Notes =
992
993 Atomic Number = 43
994 Atomic Symbol = Tc
995 Mass Number = 99
996 Relative Atomic Mass = 98.9062508(10)
997 Isotopic Composition =
998 Standard Atomic Weight = [98]
999 Notes =
1000
1001 Atomic Number = 44
1002 Atomic Symbol = Ru
1003 Mass Number = 96
1004 Relative Atomic Mass = 95.90759025(49)
1005 Isotopic Composition = 0.0554(14)
1006 Standard Atomic Weight = 101.07(2)
1007 Notes = g
1008
1009 Atomic Number = 44
1010 Atomic Symbol = Ru
1011 Mass Number = 98
1012 Relative Atomic Mass = 97.9052868(69)
1013 Isotopic Composition = 0.0187(3)
1014 Standard Atomic Weight = 101.07(2)
1015 Notes = g
1016
1017 Atomic Number = 44
1018 Atomic Symbol = Ru
1019 Mass Number = 99
1020 Relative Atomic Mass = 98.9059341(11)
1021 Isotopic Composition = 0.1276(14)
1022 Standard Atomic Weight = 101.07(2)
1023 Notes = g
1024
1025 Atomic Number = 44
1026 Atomic Symbol = Ru
1027 Mass Number = 100
1028 Relative Atomic Mass = 99.9042143(11)
1029 Isotopic Composition = 0.1260(7)
1030 Standard Atomic Weight = 101.07(2)
1031 Notes = g
1032
1033 Atomic Number = 44
1034 Atomic Symbol = Ru
1035 Mass Number = 101
1036 Relative Atomic Mass = 100.9055769(12)
1037 Isotopic Composition = 0.1706(2)
1038 Standard Atomic Weight = 101.07(2)
1039 Notes = g
1040
1041 Atomic Number = 44
1042 Atomic Symbol = Ru
1043 Mass Number = 102
1044 Relative Atomic Mass = 101.9043441(12)
1045 Isotopic Composition = 0.3155(14)
1046 Standard Atomic Weight = 101.07(2)
1047 Notes = g
1048
1049 Atomic Number = 44
1050 Atomic Symbol = Ru
1051 Mass Number = 104
1052 Relative Atomic Mass = 103.9054275(28)
1053 Isotopic Composition = 0.1862(27)
1054 Standard Atomic Weight = 101.07(2)
1055 Notes = g
1056
1057 Atomic Number = 45
1058 Atomic Symbol = Rh
1059 Mass Number = 103
1060 Relative Atomic Mass = 102.9054980(26)
1061 Isotopic Composition = 1
1062 Standard Atomic Weight = 102.90550(2)
1063 Notes =
1064
1065 Atomic Number = 46
1066 Atomic Symbol = Pd
1067 Mass Number = 102
1068 Relative Atomic Mass = 101.9056022(28)
1069 Isotopic Composition = 0.0102(1)
1070 Standard Atomic Weight = 106.42(1)
1071 Notes = g
1072
1073 Atomic Number = 46
1074 Atomic Symbol = Pd
1075 Mass Number = 104
1076 Relative Atomic Mass = 103.9040305(14)
1077 Isotopic Composition = 0.1114(8)
1078 Standard Atomic Weight = 106.42(1)
1079 Notes = g
1080
1081 Atomic Number = 46
1082 Atomic Symbol = Pd
1083 Mass Number = 105
1084 Relative Atomic Mass = 104.9050796(12)
1085 Isotopic Composition = 0.2233(8)
1086 Standard Atomic Weight = 106.42(1)
1087 Notes = g
1088
1089 Atomic Number = 46
1090 Atomic Symbol = Pd
1091 Mass Number = 106
1092 Relative Atomic Mass = 105.9034804(12)
1093 Isotopic Composition = 0.2733(3)
1094 Standard Atomic Weight = 106.42(1)
1095 Notes = g
1096
1097 Atomic Number = 46
1098 Atomic Symbol = Pd
1099 Mass Number = 108
1100 Relative Atomic Mass = 107.9038916(12)
1101 Isotopic Composition = 0.2646(9)
1102 Standard Atomic Weight = 106.42(1)
1103 Notes = g
1104
1105 Atomic Number = 46
1106 Atomic Symbol = Pd
1107 Mass Number = 110
1108 Relative Atomic Mass = 109.90517220(75)
1109 Isotopic Composition = 0.1172(9)
1110 Standard Atomic Weight = 106.42(1)
1111 Notes = g
1112
1113 Atomic Number = 47
1114 Atomic Symbol = Ag
1115 Mass Number = 107
1116 Relative Atomic Mass = 106.9050916(26)
1117 Isotopic Composition = 0.51839(8)
1118 Standard Atomic Weight = 107.8682(2)
1119 Notes = g
1120
1121 Atomic Number = 47
1122 Atomic Symbol = Ag
1123 Mass Number = 109
1124 Relative Atomic Mass = 108.9047553(14)
1125 Isotopic Composition = 0.48161(8)
1126 Standard Atomic Weight = 107.8682(2)
1127 Notes = g
1128
1129 Atomic Number = 48
1130 Atomic Symbol = Cd
1131 Mass Number = 106
1132 Relative Atomic Mass = 105.9064599(12)
1133 Isotopic Composition = 0.0125(6)
1134 Standard Atomic Weight = 112.414(4)
1135 Notes = g
1136
1137 Atomic Number = 48
1138 Atomic Symbol = Cd
1139 Mass Number = 108
1140 Relative Atomic Mass = 107.9041834(12)
1141 Isotopic Composition = 0.0089(3)
1142 Standard Atomic Weight = 112.414(4)
1143 Notes = g
1144
1145 Atomic Number = 48
1146 Atomic Symbol = Cd
1147 Mass Number = 110
1148 Relative Atomic Mass = 109.90300661(61)
1149 Isotopic Composition = 0.1249(18)
1150 Standard Atomic Weight = 112.414(4)
1151 Notes = g
1152
1153 Atomic Number = 48
1154 Atomic Symbol = Cd
1155 Mass Number = 111
1156 Relative Atomic Mass = 110.90418287(61)
1157 Isotopic Composition = 0.1280(12)
1158 Standard Atomic Weight = 112.414(4)
1159 Notes = g
1160
1161 Atomic Number = 48
1162 Atomic Symbol = Cd
1163 Mass Number = 112
1164 Relative Atomic Mass = 111.90276287(60)
1165 Isotopic Composition = 0.2413(21)
1166 Standard Atomic Weight = 112.414(4)
1167 Notes = g
1168
1169 Atomic Number = 48
1170 Atomic Symbol = Cd
1171 Mass Number = 113
1172 Relative Atomic Mass = 112.90440813(45)
1173 Isotopic Composition = 0.1222(12)
1174 Standard Atomic Weight = 112.414(4)
1175 Notes = g
1176
1177 Atomic Number = 48
1178 Atomic Symbol = Cd
1179 Mass Number = 114
1180 Relative Atomic Mass = 113.90336509(43)
1181 Isotopic Composition = 0.2873(42)
1182 Standard Atomic Weight = 112.414(4)
1183 Notes = g
1184
1185 Atomic Number = 48
1186 Atomic Symbol = Cd
1187 Mass Number = 116
1188 Relative Atomic Mass = 115.90476315(17)
1189 Isotopic Composition = 0.0749(18)
1190 Standard Atomic Weight = 112.414(4)
1191 Notes = g
1192
1193 Atomic Number = 49
1194 Atomic Symbol = In
1195 Mass Number = 113
1196 Relative Atomic Mass = 112.90406184(91)
1197 Isotopic Composition = 0.0429(5)
1198 Standard Atomic Weight = 114.818(1)
1199 Notes =
1200
1201 Atomic Number = 49
1202 Atomic Symbol = In
1203 Mass Number = 115
1204 Relative Atomic Mass = 114.903878776(12)
1205 Isotopic Composition = 0.9571(5)
1206 Standard Atomic Weight = 114.818(1)
1207 Notes =
1208
1209 Atomic Number = 50
1210 Atomic Symbol = Sn
1211 Mass Number = 112
1212 Relative Atomic Mass = 111.90482387(61)
1213 Isotopic Composition = 0.0097(1)
1214 Standard Atomic Weight = 118.710(7)
1215 Notes = g
1216
1217 Atomic Number = 50
1218 Atomic Symbol = Sn
1219 Mass Number = 114
1220 Relative Atomic Mass = 113.9027827(10)
1221 Isotopic Composition = 0.0066(1)
1222 Standard Atomic Weight = 118.710(7)
1223 Notes = g
1224
1225 Atomic Number = 50
1226 Atomic Symbol = Sn
1227 Mass Number = 115
1228 Relative Atomic Mass = 114.903344699(16)
1229 Isotopic Composition = 0.0034(1)
1230 Standard Atomic Weight = 118.710(7)
1231 Notes = g
1232
1233 Atomic Number = 50
1234 Atomic Symbol = Sn
1235 Mass Number = 116
1236 Relative Atomic Mass = 115.90174280(10)
1237 Isotopic Composition = 0.1454(9)
1238 Standard Atomic Weight = 118.710(7)
1239 Notes = g
1240
1241 Atomic Number = 50
1242 Atomic Symbol = Sn
1243 Mass Number = 117
1244 Relative Atomic Mass = 116.90295398(52)
1245 Isotopic Composition = 0.0768(7)
1246 Standard Atomic Weight = 118.710(7)
1247 Notes = g
1248
1249 Atomic Number = 50
1250 Atomic Symbol = Sn
1251 Mass Number = 118
1252 Relative Atomic Mass = 117.90160657(54)
1253 Isotopic Composition = 0.2422(9)
1254 Standard Atomic Weight = 118.710(7)
1255 Notes = g
1256
1257 Atomic Number = 50
1258 Atomic Symbol = Sn
1259 Mass Number = 119
1260 Relative Atomic Mass = 118.90331117(78)
1261 Isotopic Composition = 0.0859(4)
1262 Standard Atomic Weight = 118.710(7)
1263 Notes = g
1264
1265 Atomic Number = 50
1266 Atomic Symbol = Sn
1267 Mass Number = 120
1268 Relative Atomic Mass = 119.90220163(97)
1269 Isotopic Composition = 0.3258(9)
1270 Standard Atomic Weight = 118.710(7)
1271 Notes = g
1272
1273 Atomic Number = 50
1274 Atomic Symbol = Sn
1275 Mass Number = 122
1276 Relative Atomic Mass = 121.9034438(26)
1277 Isotopic Composition = 0.0463(3)
1278 Standard Atomic Weight = 118.710(7)
1279 Notes = g
1280
1281 Atomic Number = 50
1282 Atomic Symbol = Sn
1283 Mass Number = 124
1284 Relative Atomic Mass = 123.9052766(11)
1285 Isotopic Composition = 0.0579(5)
1286 Standard Atomic Weight = 118.710(7)
1287 Notes = g
1288
1289 Atomic Number = 51
1290 Atomic Symbol = Sb
1291 Mass Number = 121
1292 Relative Atomic Mass = 120.9038120(30)
1293 Isotopic Composition = 0.5721(5)
1294 Standard Atomic Weight = 121.760(1)
1295 Notes = g
1296
1297 Atomic Number = 51
1298 Atomic Symbol = Sb
1299 Mass Number = 123
1300 Relative Atomic Mass = 122.9042132(23)
1301 Isotopic Composition = 0.4279(5)
1302 Standard Atomic Weight = 121.760(1)
1303 Notes = g
1304
1305 Atomic Number = 52
1306 Atomic Symbol = Te
1307 Mass Number = 120
1308 Relative Atomic Mass = 119.9040593(33)
1309 Isotopic Composition = 0.0009(1)
1310 Standard Atomic Weight = 127.60(3)
1311 Notes = g
1312
1313 Atomic Number = 52
1314 Atomic Symbol = Te
1315 Mass Number = 122
1316 Relative Atomic Mass = 121.9030435(16)
1317 Isotopic Composition = 0.0255(12)
1318 Standard Atomic Weight = 127.60(3)
1319 Notes = g
1320
1321 Atomic Number = 52
1322 Atomic Symbol = Te
1323 Mass Number = 123
1324 Relative Atomic Mass = 122.9042698(16)
1325 Isotopic Composition = 0.0089(3)
1326 Standard Atomic Weight = 127.60(3)
1327 Notes = g
1328
1329 Atomic Number = 52
1330 Atomic Symbol = Te
1331 Mass Number = 124
1332 Relative Atomic Mass = 123.9028171(16)
1333 Isotopic Composition = 0.0474(14)
1334 Standard Atomic Weight = 127.60(3)
1335 Notes = g
1336
1337 Atomic Number = 52
1338 Atomic Symbol = Te
1339 Mass Number = 125
1340 Relative Atomic Mass = 124.9044299(16)
1341 Isotopic Composition = 0.0707(15)
1342 Standard Atomic Weight = 127.60(3)
1343 Notes = g
1344
1345 Atomic Number = 52
1346 Atomic Symbol = Te
1347 Mass Number = 126
1348 Relative Atomic Mass = 125.9033109(16)
1349 Isotopic Composition = 0.1884(25)
1350 Standard Atomic Weight = 127.60(3)
1351 Notes = g
1352
1353 Atomic Number = 52
1354 Atomic Symbol = Te
1355 Mass Number = 128
1356 Relative Atomic Mass = 127.90446128(93)
1357 Isotopic Composition = 0.3174(8)
1358 Standard Atomic Weight = 127.60(3)
1359 Notes = g
1360
1361 Atomic Number = 52
1362 Atomic Symbol = Te
1363 Mass Number = 130
1364 Relative Atomic Mass = 129.906222748(12)
1365 Isotopic Composition = 0.3408(62)
1366 Standard Atomic Weight = 127.60(3)
1367 Notes = g
1368
1369 Atomic Number = 53
1370 Atomic Symbol = I
1371 Mass Number = 127
1372 Relative Atomic Mass = 126.9044719(39)
1373 Isotopic Composition = 1
1374 Standard Atomic Weight = 126.90447(3)
1375 Notes =
1376
1377 Atomic Number = 54
1378 Atomic Symbol = Xe
1379 Mass Number = 124
1380 Relative Atomic Mass = 123.9058920(19)
1381 Isotopic Composition = 0.000952(3)
1382 Standard Atomic Weight = 131.293(6)
1383 Notes = g,m
1384
1385 Atomic Number = 54
1386 Atomic Symbol = Xe
1387 Mass Number = 126
1388 Relative Atomic Mass = 125.9042983(38)
1389 Isotopic Composition = 0.000890(2)
1390 Standard Atomic Weight = 131.293(6)
1391 Notes = g,m
1392
1393 Atomic Number = 54
1394 Atomic Symbol = Xe
1395 Mass Number = 128
1396 Relative Atomic Mass = 127.9035310(11)
1397 Isotopic Composition = 0.019102(8)
1398 Standard Atomic Weight = 131.293(6)
1399 Notes = g,m
1400
1401 Atomic Number = 54
1402 Atomic Symbol = Xe
1403 Mass Number = 129
1404 Relative Atomic Mass = 128.9047808611(60)
1405 Isotopic Composition = 0.264006(82)
1406 Standard Atomic Weight = 131.293(6)
1407 Notes = g,m
1408
1409 Atomic Number = 54
1410 Atomic Symbol = Xe
1411 Mass Number = 130
1412 Relative Atomic Mass = 129.903509349(10)
1413 Isotopic Composition = 0.040710(13)
1414 Standard Atomic Weight = 131.293(6)
1415 Notes = g,m
1416
1417 Atomic Number = 54
1418 Atomic Symbol = Xe
1419 Mass Number = 131
1420 Relative Atomic Mass = 130.90508406(24)
1421 Isotopic Composition = 0.212324(30)
1422 Standard Atomic Weight = 131.293(6)
1423 Notes = g,m
1424
1425 Atomic Number = 54
1426 Atomic Symbol = Xe
1427 Mass Number = 132
1428 Relative Atomic Mass = 131.9041550856(56)
1429 Isotopic Composition = 0.269086(33)
1430 Standard Atomic Weight = 131.293(6)
1431 Notes = g,m
1432
1433 Atomic Number = 54
1434 Atomic Symbol = Xe
1435 Mass Number = 134
1436 Relative Atomic Mass = 133.90539466(90)
1437 Isotopic Composition = 0.104357(21)
1438 Standard Atomic Weight = 131.293(6)
1439 Notes = g,m
1440
1441 Atomic Number = 54
1442 Atomic Symbol = Xe
1443 Mass Number = 136
1444 Relative Atomic Mass = 135.907214484(11)
1445 Isotopic Composition = 0.088573(44)
1446 Standard Atomic Weight = 131.293(6)
1447 Notes = g,m
1448
1449 Atomic Number = 55
1450 Atomic Symbol = Cs
1451 Mass Number = 133
1452 Relative Atomic Mass = 132.9054519610(80)
1453 Isotopic Composition = 1
1454 Standard Atomic Weight = 132.90545196(6)
1455 Notes =
1456
1457 Atomic Number = 56
1458 Atomic Symbol = Ba
1459 Mass Number = 130
1460 Relative Atomic Mass = 129.9063207(28)
1461 Isotopic Composition = 0.00106(1)
1462 Standard Atomic Weight = 137.327(7)
1463 Notes =
1464
1465 Atomic Number = 56
1466 Atomic Symbol = Ba
1467 Mass Number = 132
1468 Relative Atomic Mass = 131.9050611(11)
1469 Isotopic Composition = 0.00101(1)
1470 Standard Atomic Weight = 137.327(7)
1471 Notes =
1472
1473 Atomic Number = 56
1474 Atomic Symbol = Ba
1475 Mass Number = 134
1476 Relative Atomic Mass = 133.90450818(30)
1477 Isotopic Composition = 0.02417(18)
1478 Standard Atomic Weight = 137.327(7)
1479 Notes =
1480
1481 Atomic Number = 56
1482 Atomic Symbol = Ba
1483 Mass Number = 135
1484 Relative Atomic Mass = 134.90568838(29)
1485 Isotopic Composition = 0.06592(12)
1486 Standard Atomic Weight = 137.327(7)
1487 Notes =
1488
1489 Atomic Number = 56
1490 Atomic Symbol = Ba
1491 Mass Number = 136
1492 Relative Atomic Mass = 135.90457573(29)
1493 Isotopic Composition = 0.07854(24)
1494 Standard Atomic Weight = 137.327(7)
1495 Notes =
1496
1497 Atomic Number = 56
1498 Atomic Symbol = Ba
1499 Mass Number = 137
1500 Relative Atomic Mass = 136.90582714(30)
1501 Isotopic Composition = 0.11232(24)
1502 Standard Atomic Weight = 137.327(7)
1503 Notes =
1504
1505 Atomic Number = 56
1506 Atomic Symbol = Ba
1507 Mass Number = 138
1508 Relative Atomic Mass = 137.90524700(31)
1509 Isotopic Composition = 0.71698(42)
1510 Standard Atomic Weight = 137.327(7)
1511 Notes =
1512
1513 Atomic Number = 57
1514 Atomic Symbol = La
1515 Mass Number = 138
1516 Relative Atomic Mass = 137.9071149(37)
1517 Isotopic Composition = 0.0008881(71)
1518 Standard Atomic Weight = 138.90547(7)
1519 Notes = g
1520
1521 Atomic Number = 57
1522 Atomic Symbol = La
1523 Mass Number = 139
1524 Relative Atomic Mass = 138.9063563(24)
1525 Isotopic Composition = 0.9991119(71)
1526 Standard Atomic Weight = 138.90547(7)
1527 Notes = g
1528
1529 Atomic Number = 58
1530 Atomic Symbol = Ce
1531 Mass Number = 136
1532 Relative Atomic Mass = 135.90712921(41)
1533 Isotopic Composition = 0.00185(2)
1534 Standard Atomic Weight = 140.116(1)
1535 Notes = g
1536
1537 Atomic Number = 58
1538 Atomic Symbol = Ce
1539 Mass Number = 138
1540 Relative Atomic Mass = 137.905991(11)
1541 Isotopic Composition = 0.00251(2)
1542 Standard Atomic Weight = 140.116(1)
1543 Notes = g
1544
1545 Atomic Number = 58
1546 Atomic Symbol = Ce
1547 Mass Number = 140
1548 Relative Atomic Mass = 139.9054431(23)
1549 Isotopic Composition = 0.88450(51)
1550 Standard Atomic Weight = 140.116(1)
1551 Notes = g
1552
1553 Atomic Number = 58
1554 Atomic Symbol = Ce
1555 Mass Number = 142
1556 Relative Atomic Mass = 141.9092504(29)
1557 Isotopic Composition = 0.11114(51)
1558 Standard Atomic Weight = 140.116(1)
1559 Notes = g
1560
1561 Atomic Number = 59
1562 Atomic Symbol = Pr
1563 Mass Number = 141
1564 Relative Atomic Mass = 140.9076576(23)
1565 Isotopic Composition = 1
1566 Standard Atomic Weight = 140.90766(2)
1567 Notes =
1568
1569 Atomic Number = 60
1570 Atomic Symbol = Nd
1571 Mass Number = 142
1572 Relative Atomic Mass = 141.9077290(20)
1573 Isotopic Composition = 0.27152(40)
1574 Standard Atomic Weight = 144.242(3)
1575 Notes = g
1576
1577 Atomic Number = 60
1578 Atomic Symbol = Nd
1579 Mass Number = 143
1580 Relative Atomic Mass = 142.9098200(20)
1581 Isotopic Composition = 0.12174(26)
1582 Standard Atomic Weight = 144.242(3)
1583 Notes = g
1584
1585 Atomic Number = 60
1586 Atomic Symbol = Nd
1587 Mass Number = 144
1588 Relative Atomic Mass = 143.9100930(20)
1589 Isotopic Composition = 0.23798(19)
1590 Standard Atomic Weight = 144.242(3)
1591 Notes = g
1592
1593 Atomic Number = 60
1594 Atomic Symbol = Nd
1595 Mass Number = 145
1596 Relative Atomic Mass = 144.9125793(20)
1597 Isotopic Composition = 0.08293(12)
1598 Standard Atomic Weight = 144.242(3)
1599 Notes = g
1600
1601 Atomic Number = 60
1602 Atomic Symbol = Nd
1603 Mass Number = 146
1604 Relative Atomic Mass = 145.9131226(20)
1605 Isotopic Composition = 0.17189(32)
1606 Standard Atomic Weight = 144.242(3)
1607 Notes = g
1608
1609 Atomic Number = 60
1610 Atomic Symbol = Nd
1611 Mass Number = 148
1612 Relative Atomic Mass = 147.9168993(26)
1613 Isotopic Composition = 0.05756(21)
1614 Standard Atomic Weight = 144.242(3)
1615 Notes = g
1616
1617 Atomic Number = 60
1618 Atomic Symbol = Nd
1619 Mass Number = 150
1620 Relative Atomic Mass = 149.9209022(18)
1621 Isotopic Composition = 0.05638(28)
1622 Standard Atomic Weight = 144.242(3)
1623 Notes = g
1624
1625 Atomic Number = 61
1626 Atomic Symbol = Pm
1627 Mass Number = 145
1628 Relative Atomic Mass = 144.9127559(33)
1629 Isotopic Composition =
1630 Standard Atomic Weight = [145]
1631 Notes =
1632
1633 Atomic Number = 61
1634 Atomic Symbol = Pm
1635 Mass Number = 147
1636 Relative Atomic Mass = 146.9151450(19)
1637 Isotopic Composition =
1638 Standard Atomic Weight = [145]
1639 Notes =
1640
1641 Atomic Number = 62
1642 Atomic Symbol = Sm
1643 Mass Number = 144
1644 Relative Atomic Mass = 143.9120065(21)
1645 Isotopic Composition = 0.0307(7)
1646 Standard Atomic Weight = 150.36(2)
1647 Notes = g
1648
1649 Atomic Number = 62
1650 Atomic Symbol = Sm
1651 Mass Number = 147
1652 Relative Atomic Mass = 146.9149044(19)
1653 Isotopic Composition = 0.1499(18)
1654 Standard Atomic Weight = 150.36(2)
1655 Notes = g
1656
1657 Atomic Number = 62
1658 Atomic Symbol = Sm
1659 Mass Number = 148
1660 Relative Atomic Mass = 147.9148292(19)
1661 Isotopic Composition = 0.1124(10)
1662 Standard Atomic Weight = 150.36(2)
1663 Notes = g
1664
1665 Atomic Number = 62
1666 Atomic Symbol = Sm
1667 Mass Number = 149
1668 Relative Atomic Mass = 148.9171921(18)
1669 Isotopic Composition = 0.1382(7)
1670 Standard Atomic Weight = 150.36(2)
1671 Notes = g
1672
1673 Atomic Number = 62
1674 Atomic Symbol = Sm
1675 Mass Number = 150
1676 Relative Atomic Mass = 149.9172829(18)
1677 Isotopic Composition = 0.0738(1)
1678 Standard Atomic Weight = 150.36(2)
1679 Notes = g
1680
1681 Atomic Number = 62
1682 Atomic Symbol = Sm
1683 Mass Number = 152
1684 Relative Atomic Mass = 151.9197397(18)
1685 Isotopic Composition = 0.2675(16)
1686 Standard Atomic Weight = 150.36(2)
1687 Notes = g
1688
1689 Atomic Number = 62
1690 Atomic Symbol = Sm
1691 Mass Number = 154
1692 Relative Atomic Mass = 153.9222169(20)
1693 Isotopic Composition = 0.2275(29)
1694 Standard Atomic Weight = 150.36(2)
1695 Notes = g
1696
1697 Atomic Number = 63
1698 Atomic Symbol = Eu
1699 Mass Number = 151
1700 Relative Atomic Mass = 150.9198578(18)
1701 Isotopic Composition = 0.4781(6)
1702 Standard Atomic Weight = 151.964(1)
1703 Notes = g
1704
1705 Atomic Number = 63
1706 Atomic Symbol = Eu
1707 Mass Number = 153
1708 Relative Atomic Mass = 152.9212380(18)
1709 Isotopic Composition = 0.5219(6)
1710 Standard Atomic Weight = 151.964(1)
1711 Notes = g
1712
1713 Atomic Number = 64
1714 Atomic Symbol = Gd
1715 Mass Number = 152
1716 Relative Atomic Mass = 151.9197995(18)
1717 Isotopic Composition = 0.0020(1)
1718 Standard Atomic Weight = 157.25(3)
1719 Notes = g
1720
1721 Atomic Number = 64
1722 Atomic Symbol = Gd
1723 Mass Number = 154
1724 Relative Atomic Mass = 153.9208741(17)
1725 Isotopic Composition = 0.0218(3)
1726 Standard Atomic Weight = 157.25(3)
1727 Notes = g
1728
1729 Atomic Number = 64
1730 Atomic Symbol = Gd
1731 Mass Number = 155
1732 Relative Atomic Mass = 154.9226305(17)
1733 Isotopic Composition = 0.1480(12)
1734 Standard Atomic Weight = 157.25(3)
1735 Notes = g
1736
1737 Atomic Number = 64
1738 Atomic Symbol = Gd
1739 Mass Number = 156
1740 Relative Atomic Mass = 155.9221312(17)
1741 Isotopic Composition = 0.2047(9)
1742 Standard Atomic Weight = 157.25(3)
1743 Notes = g
1744
1745 Atomic Number = 64
1746 Atomic Symbol = Gd
1747 Mass Number = 157
1748 Relative Atomic Mass = 156.9239686(17)
1749 Isotopic Composition = 0.1565(2)
1750 Standard Atomic Weight = 157.25(3)
1751 Notes = g
1752
1753 Atomic Number = 64
1754 Atomic Symbol = Gd
1755 Mass Number = 158
1756 Relative Atomic Mass = 157.9241123(17)
1757 Isotopic Composition = 0.2484(7)
1758 Standard Atomic Weight = 157.25(3)
1759 Notes = g
1760
1761 Atomic Number = 64
1762 Atomic Symbol = Gd
1763 Mass Number = 160
1764 Relative Atomic Mass = 159.9270624(18)
1765 Isotopic Composition = 0.2186(19)
1766 Standard Atomic Weight = 157.25(3)
1767 Notes = g
1768
1769 Atomic Number = 65
1770 Atomic Symbol = Tb
1771 Mass Number = 159
1772 Relative Atomic Mass = 158.9253547(19)
1773 Isotopic Composition = 1
1774 Standard Atomic Weight = 158.92535(2)
1775 Notes =
1776
1777 Atomic Number = 66
1778 Atomic Symbol = Dy
1779 Mass Number = 156
1780 Relative Atomic Mass = 155.9242847(17)
1781 Isotopic Composition = 0.00056(3)
1782 Standard Atomic Weight = 162.500(1)
1783 Notes = g
1784
1785 Atomic Number = 66
1786 Atomic Symbol = Dy
1787 Mass Number = 158
1788 Relative Atomic Mass = 157.9244159(31)
1789 Isotopic Composition = 0.00095(3)
1790 Standard Atomic Weight = 162.500(1)
1791 Notes = g
1792
1793 Atomic Number = 66
1794 Atomic Symbol = Dy
1795 Mass Number = 160
1796 Relative Atomic Mass = 159.9252046(20)
1797 Isotopic Composition = 0.02329(18)
1798 Standard Atomic Weight = 162.500(1)
1799 Notes = g
1800
1801 Atomic Number = 66
1802 Atomic Symbol = Dy
1803 Mass Number = 161
1804 Relative Atomic Mass = 160.9269405(20)
1805 Isotopic Composition = 0.18889(42)
1806 Standard Atomic Weight = 162.500(1)
1807 Notes = g
1808
1809 Atomic Number = 66
1810 Atomic Symbol = Dy
1811 Mass Number = 162
1812 Relative Atomic Mass = 161.9268056(20)
1813 Isotopic Composition = 0.25475(36)
1814 Standard Atomic Weight = 162.500(1)
1815 Notes = g
1816
1817 Atomic Number = 66
1818 Atomic Symbol = Dy
1819 Mass Number = 163
1820 Relative Atomic Mass = 162.9287383(20)
1821 Isotopic Composition = 0.24896(42)
1822 Standard Atomic Weight = 162.500(1)
1823 Notes = g
1824
1825 Atomic Number = 66
1826 Atomic Symbol = Dy
1827 Mass Number = 164
1828 Relative Atomic Mass = 163.9291819(20)
1829 Isotopic Composition = 0.28260(54)
1830 Standard Atomic Weight = 162.500(1)
1831 Notes = g
1832
1833 Atomic Number = 67
1834 Atomic Symbol = Ho
1835 Mass Number = 165
1836 Relative Atomic Mass = 164.9303288(21)
1837 Isotopic Composition = 1
1838 Standard Atomic Weight = 164.93033(2)
1839 Notes =
1840
1841 Atomic Number = 68
1842 Atomic Symbol = Er
1843 Mass Number = 162
1844 Relative Atomic Mass = 161.9287884(20)
1845 Isotopic Composition = 0.00139(5)
1846 Standard Atomic Weight = 167.259(3)
1847 Notes = g
1848
1849 Atomic Number = 68
1850 Atomic Symbol = Er
1851 Mass Number = 164
1852 Relative Atomic Mass = 163.9292088(20)
1853 Isotopic Composition = 0.01601(3)
1854 Standard Atomic Weight = 167.259(3)
1855 Notes = g
1856
1857 Atomic Number = 68
1858 Atomic Symbol = Er
1859 Mass Number = 166
1860 Relative Atomic Mass = 165.9302995(22)
1861 Isotopic Composition = 0.33503(36)
1862 Standard Atomic Weight = 167.259(3)
1863 Notes = g
1864
1865 Atomic Number = 68
1866 Atomic Symbol = Er
1867 Mass Number = 167
1868 Relative Atomic Mass = 166.9320546(22)
1869 Isotopic Composition = 0.22869(9)
1870 Standard Atomic Weight = 167.259(3)
1871 Notes = g
1872
1873 Atomic Number = 68
1874 Atomic Symbol = Er
1875 Mass Number = 168
1876 Relative Atomic Mass = 167.9323767(22)
1877 Isotopic Composition = 0.26978(18)
1878 Standard Atomic Weight = 167.259(3)
1879 Notes = g
1880
1881 Atomic Number = 68
1882 Atomic Symbol = Er
1883 Mass Number = 170
1884 Relative Atomic Mass = 169.9354702(26)
1885 Isotopic Composition = 0.14910(36)
1886 Standard Atomic Weight = 167.259(3)
1887 Notes = g
1888
1889 Atomic Number = 69
1890 Atomic Symbol = Tm
1891 Mass Number = 169
1892 Relative Atomic Mass = 168.9342179(22)
1893 Isotopic Composition = 1
1894 Standard Atomic Weight = 168.93422(2)
1895 Notes =
1896
1897 Atomic Number = 70
1898 Atomic Symbol = Yb
1899 Mass Number = 168
1900 Relative Atomic Mass = 167.9338896(22)
1901 Isotopic Composition = 0.00123(3)
1902 Standard Atomic Weight = 173.054(5)
1903 Notes = g
1904
1905 Atomic Number = 70
1906 Atomic Symbol = Yb
1907 Mass Number = 170
1908 Relative Atomic Mass = 169.9347664(22)
1909 Isotopic Composition = 0.02982(39)
1910 Standard Atomic Weight = 173.054(5)
1911 Notes = g
1912
1913 Atomic Number = 70
1914 Atomic Symbol = Yb
1915 Mass Number = 171
1916 Relative Atomic Mass = 170.9363302(22)
1917 Isotopic Composition = 0.1409(14)
1918 Standard Atomic Weight = 173.054(5)
1919 Notes = g
1920
1921 Atomic Number = 70
1922 Atomic Symbol = Yb
1923 Mass Number = 172
1924 Relative Atomic Mass = 171.9363859(22)
1925 Isotopic Composition = 0.2168(13)
1926 Standard Atomic Weight = 173.054(5)
1927 Notes = g
1928
1929 Atomic Number = 70
1930 Atomic Symbol = Yb
1931 Mass Number = 173
1932 Relative Atomic Mass = 172.9382151(22)
1933 Isotopic Composition = 0.16103(63)
1934 Standard Atomic Weight = 173.054(5)
1935 Notes = g
1936
1937 Atomic Number = 70
1938 Atomic Symbol = Yb
1939 Mass Number = 174
1940 Relative Atomic Mass = 173.9388664(22)
1941 Isotopic Composition = 0.32026(80)
1942 Standard Atomic Weight = 173.054(5)
1943 Notes = g
1944
1945 Atomic Number = 70
1946 Atomic Symbol = Yb
1947 Mass Number = 176
1948 Relative Atomic Mass = 175.9425764(24)
1949 Isotopic Composition = 0.12996(83)
1950 Standard Atomic Weight = 173.054(5)
1951 Notes = g
1952
1953 Atomic Number = 71
1954 Atomic Symbol = Lu
1955 Mass Number = 175
1956 Relative Atomic Mass = 174.9407752(20)
1957 Isotopic Composition = 0.97401(13)
1958 Standard Atomic Weight = 174.9668(1)
1959 Notes = g
1960
1961 Atomic Number = 71
1962 Atomic Symbol = Lu
1963 Mass Number = 176
1964 Relative Atomic Mass = 175.9426897(20)
1965 Isotopic Composition = 0.02599(13)
1966 Standard Atomic Weight = 174.9668(1)
1967 Notes = g
1968
1969 Atomic Number = 72
1970 Atomic Symbol = Hf
1971 Mass Number = 174
1972 Relative Atomic Mass = 173.9400461(28)
1973 Isotopic Composition = 0.0016(1)
1974 Standard Atomic Weight = 178.49(2)
1975 Notes =
1976
1977 Atomic Number = 72
1978 Atomic Symbol = Hf
1979 Mass Number = 176
1980 Relative Atomic Mass = 175.9414076(22)
1981 Isotopic Composition = 0.0526(7)
1982 Standard Atomic Weight = 178.49(2)
1983 Notes =
1984
1985 Atomic Number = 72
1986 Atomic Symbol = Hf
1987 Mass Number = 177
1988 Relative Atomic Mass = 176.9432277(20)
1989 Isotopic Composition = 0.1860(9)
1990 Standard Atomic Weight = 178.49(2)
1991 Notes =
1992
1993 Atomic Number = 72
1994 Atomic Symbol = Hf
1995 Mass Number = 178
1996 Relative Atomic Mass = 177.9437058(20)
1997 Isotopic Composition = 0.2728(7)
1998 Standard Atomic Weight = 178.49(2)
1999 Notes =
2000
2001 Atomic Number = 72
2002 Atomic Symbol = Hf
2003 Mass Number = 179
2004 Relative Atomic Mass = 178.9458232(20)
2005 Isotopic Composition = 0.1362(2)
2006 Standard Atomic Weight = 178.49(2)
2007 Notes =
2008
2009 Atomic Number = 72
2010 Atomic Symbol = Hf
2011 Mass Number = 180
2012 Relative Atomic Mass = 179.9465570(20)
2013 Isotopic Composition = 0.3508(16)
2014 Standard Atomic Weight = 178.49(2)
2015 Notes =
2016
2017 Atomic Number = 73
2018 Atomic Symbol = Ta
2019 Mass Number = 180
2020 Relative Atomic Mass = 179.9474648(24)
2021 Isotopic Composition = 0.0001201(32)
2022 Standard Atomic Weight = 180.94788(2)
2023 Notes =
2024
2025 Atomic Number = 73
2026 Atomic Symbol = Ta
2027 Mass Number = 181
2028 Relative Atomic Mass = 180.9479958(20)
2029 Isotopic Composition = 0.9998799(32)
2030 Standard Atomic Weight = 180.94788(2)
2031 Notes =
2032
2033 Atomic Number = 74
2034 Atomic Symbol = W
2035 Mass Number = 180
2036 Relative Atomic Mass = 179.9467108(20)
2037 Isotopic Composition = 0.0012(1)
2038 Standard Atomic Weight = 183.84(1)
2039 Notes =
2040
2041 Atomic Number = 74
2042 Atomic Symbol = W
2043 Mass Number = 182
2044 Relative Atomic Mass = 181.94820394(91)
2045 Isotopic Composition = 0.2650(16)
2046 Standard Atomic Weight = 183.84(1)
2047 Notes =
2048
2049 Atomic Number = 74
2050 Atomic Symbol = W
2051 Mass Number = 183
2052 Relative Atomic Mass = 182.95022275(90)
2053 Isotopic Composition = 0.1431(4)
2054 Standard Atomic Weight = 183.84(1)
2055 Notes =
2056
2057 Atomic Number = 74
2058 Atomic Symbol = W
2059 Mass Number = 184
2060 Relative Atomic Mass = 183.95093092(94)
2061 Isotopic Composition = 0.3064(2)
2062 Standard Atomic Weight = 183.84(1)
2063 Notes =
2064
2065 Atomic Number = 74
2066 Atomic Symbol = W
2067 Mass Number = 186
2068 Relative Atomic Mass = 185.9543628(17)
2069 Isotopic Composition = 0.2843(19)
2070 Standard Atomic Weight = 183.84(1)
2071 Notes =
2072
2073 Atomic Number = 75
2074 Atomic Symbol = Re
2075 Mass Number = 185
2076 Relative Atomic Mass = 184.9529545(13)
2077 Isotopic Composition = 0.3740(2)
2078 Standard Atomic Weight = 186.207(1)
2079 Notes =
2080
2081 Atomic Number = 75
2082 Atomic Symbol = Re
2083 Mass Number = 187
2084 Relative Atomic Mass = 186.9557501(16)
2085 Isotopic Composition = 0.6260(2)
2086 Standard Atomic Weight = 186.207(1)
2087 Notes =
2088
2089 Atomic Number = 76
2090 Atomic Symbol = Os
2091 Mass Number = 184
2092 Relative Atomic Mass = 183.9524885(14)
2093 Isotopic Composition = 0.0002(1)
2094 Standard Atomic Weight = 190.23(3)
2095 Notes = g
2096
2097 Atomic Number = 76
2098 Atomic Symbol = Os
2099 Mass Number = 186
2100 Relative Atomic Mass = 185.9538350(16)
2101 Isotopic Composition = 0.0159(3)
2102 Standard Atomic Weight = 190.23(3)
2103 Notes = g
2104
2105 Atomic Number = 76
2106 Atomic Symbol = Os
2107 Mass Number = 187
2108 Relative Atomic Mass = 186.9557474(16)
2109 Isotopic Composition = 0.0196(2)
2110 Standard Atomic Weight = 190.23(3)
2111 Notes = g
2112
2113 Atomic Number = 76
2114 Atomic Symbol = Os
2115 Mass Number = 188
2116 Relative Atomic Mass = 187.9558352(16)
2117 Isotopic Composition = 0.1324(8)
2118 Standard Atomic Weight = 190.23(3)
2119 Notes = g
2120
2121 Atomic Number = 76
2122 Atomic Symbol = Os
2123 Mass Number = 189
2124 Relative Atomic Mass = 188.9581442(17)
2125 Isotopic Composition = 0.1615(5)
2126 Standard Atomic Weight = 190.23(3)
2127 Notes = g
2128
2129 Atomic Number = 76
2130 Atomic Symbol = Os
2131 Mass Number = 190
2132 Relative Atomic Mass = 189.9584437(17)
2133 Isotopic Composition = 0.2626(2)
2134 Standard Atomic Weight = 190.23(3)
2135 Notes = g
2136
2137 Atomic Number = 76
2138 Atomic Symbol = Os
2139 Mass Number = 192
2140 Relative Atomic Mass = 191.9614770(29)
2141 Isotopic Composition = 0.4078(19)
2142 Standard Atomic Weight = 190.23(3)
2143 Notes = g
2144
2145 Atomic Number = 77
2146 Atomic Symbol = Ir
2147 Mass Number = 191
2148 Relative Atomic Mass = 190.9605893(21)
2149 Isotopic Composition = 0.373(2)
2150 Standard Atomic Weight = 192.217(3)
2151 Notes =
2152
2153 Atomic Number = 77
2154 Atomic Symbol = Ir
2155 Mass Number = 193
2156 Relative Atomic Mass = 192.9629216(21)
2157 Isotopic Composition = 0.627(2)
2158 Standard Atomic Weight = 192.217(3)
2159 Notes =
2160
2161 Atomic Number = 78
2162 Atomic Symbol = Pt
2163 Mass Number = 190
2164 Relative Atomic Mass = 189.9599297(63)
2165 Isotopic Composition = 0.00012(2)
2166 Standard Atomic Weight = 195.084(9)
2167 Notes =
2168
2169 Atomic Number = 78
2170 Atomic Symbol = Pt
2171 Mass Number = 192
2172 Relative Atomic Mass = 191.9610387(32)
2173 Isotopic Composition = 0.00782(24)
2174 Standard Atomic Weight = 195.084(9)
2175 Notes =
2176
2177 Atomic Number = 78
2178 Atomic Symbol = Pt
2179 Mass Number = 194
2180 Relative Atomic Mass = 193.9626809(10)
2181 Isotopic Composition = 0.3286(40)
2182 Standard Atomic Weight = 195.084(9)
2183 Notes =
2184
2185 Atomic Number = 78
2186 Atomic Symbol = Pt
2187 Mass Number = 195
2188 Relative Atomic Mass = 194.9647917(10)
2189 Isotopic Composition = 0.3378(24)
2190 Standard Atomic Weight = 195.084(9)
2191 Notes =
2192
2193 Atomic Number = 78
2194 Atomic Symbol = Pt
2195 Mass Number = 196
2196 Relative Atomic Mass = 195.96495209(99)
2197 Isotopic Composition = 0.2521(34)
2198 Standard Atomic Weight = 195.084(9)
2199 Notes =
2200
2201 Atomic Number = 78
2202 Atomic Symbol = Pt
2203 Mass Number = 198
2204 Relative Atomic Mass = 197.9678949(23)
2205 Isotopic Composition = 0.07356(130)
2206 Standard Atomic Weight = 195.084(9)
2207 Notes =
2208
2209 Atomic Number = 79
2210 Atomic Symbol = Au
2211 Mass Number = 197
2212 Relative Atomic Mass = 196.96656879(71)
2213 Isotopic Composition = 1
2214 Standard Atomic Weight = 196.966569(5)
2215 Notes =
2216
2217 Atomic Number = 80
2218 Atomic Symbol = Hg
2219 Mass Number = 196
2220 Relative Atomic Mass = 195.9658326(32)
2221 Isotopic Composition = 0.0015(1)
2222 Standard Atomic Weight = 200.592(3)
2223 Notes =
2224
2225 Atomic Number = 80
2226 Atomic Symbol = Hg
2227 Mass Number = 198
2228 Relative Atomic Mass = 197.96676860(52)
2229 Isotopic Composition = 0.0997(20)
2230 Standard Atomic Weight = 200.592(3)
2231 Notes =
2232
2233 Atomic Number = 80
2234 Atomic Symbol = Hg
2235 Mass Number = 199
2236 Relative Atomic Mass = 198.96828064(46)
2237 Isotopic Composition = 0.1687(22)
2238 Standard Atomic Weight = 200.592(3)
2239 Notes =
2240
2241 Atomic Number = 80
2242 Atomic Symbol = Hg
2243 Mass Number = 200
2244 Relative Atomic Mass = 199.96832659(47)
2245 Isotopic Composition = 0.2310(19)
2246 Standard Atomic Weight = 200.592(3)
2247 Notes =
2248
2249 Atomic Number = 80
2250 Atomic Symbol = Hg
2251 Mass Number = 201
2252 Relative Atomic Mass = 200.97030284(69)
2253 Isotopic Composition = 0.1318(9)
2254 Standard Atomic Weight = 200.592(3)
2255 Notes =
2256
2257 Atomic Number = 80
2258 Atomic Symbol = Hg
2259 Mass Number = 202
2260 Relative Atomic Mass = 201.97064340(69)
2261 Isotopic Composition = 0.2986(26)
2262 Standard Atomic Weight = 200.592(3)
2263 Notes =
2264
2265 Atomic Number = 80
2266 Atomic Symbol = Hg
2267 Mass Number = 204
2268 Relative Atomic Mass = 203.97349398(53)
2269 Isotopic Composition = 0.0687(15)
2270 Standard Atomic Weight = 200.592(3)
2271 Notes =
2272
2273 Atomic Number = 81
2274 Atomic Symbol = Tl
2275 Mass Number = 203
2276 Relative Atomic Mass = 202.9723446(14)
2277 Isotopic Composition = 0.2952(1)
2278 Standard Atomic Weight = [204.382,204.385]
2279 Notes =
2280
2281 Atomic Number = 81
2282 Atomic Symbol = Tl
2283 Mass Number = 205
2284 Relative Atomic Mass = 204.9744278(14)
2285 Isotopic Composition = 0.7048(1)
2286 Standard Atomic Weight = [204.382,204.385]
2287 Notes =
2288
2289 Atomic Number = 82
2290 Atomic Symbol = Pb
2291 Mass Number = 204
2292 Relative Atomic Mass = 203.9730440(13)
2293 Isotopic Composition = 0.014(1)
2294 Standard Atomic Weight = 207.2(1)
2295 Notes = g,r
2296
2297 Atomic Number = 82
2298 Atomic Symbol = Pb
2299 Mass Number = 206
2300 Relative Atomic Mass = 205.9744657(13)
2301 Isotopic Composition = 0.241(1)
2302 Standard Atomic Weight = 207.2(1)
2303 Notes = g,r
2304
2305 Atomic Number = 82
2306 Atomic Symbol = Pb
2307 Mass Number = 207
2308 Relative Atomic Mass = 206.9758973(13)
2309 Isotopic Composition = 0.221(1)
2310 Standard Atomic Weight = 207.2(1)
2311 Notes = g,r
2312
2313 Atomic Number = 82
2314 Atomic Symbol = Pb
2315 Mass Number = 208
2316 Relative Atomic Mass = 207.9766525(13)
2317 Isotopic Composition = 0.524(1)
2318 Standard Atomic Weight = 207.2(1)
2319 Notes = g,r
2320
2321 Atomic Number = 83
2322 Atomic Symbol = Bi
2323 Mass Number = 209
2324 Relative Atomic Mass = 208.9803991(16)
2325 Isotopic Composition = 1
2326 Standard Atomic Weight = 208.98040(1)
2327 Notes =
2328
2329 Atomic Number = 84
2330 Atomic Symbol = Po
2331 Mass Number = 209
2332 Relative Atomic Mass = 208.9824308(20)
2333 Isotopic Composition =
2334 Standard Atomic Weight = [209]
2335 Notes =
2336
2337 Atomic Number = 84
2338 Atomic Symbol = Po
2339 Mass Number = 210
2340 Relative Atomic Mass = 209.9828741(13)
2341 Isotopic Composition =
2342 Standard Atomic Weight = [209]
2343 Notes =
2344
2345 Atomic Number = 85
2346 Atomic Symbol = At
2347 Mass Number = 210
2348 Relative Atomic Mass = 209.9871479(83)
2349 Isotopic Composition =
2350 Standard Atomic Weight = [210]
2351 Notes =
2352
2353 Atomic Number = 85
2354 Atomic Symbol = At
2355 Mass Number = 211
2356 Relative Atomic Mass = 210.9874966(30)
2357 Isotopic Composition =
2358 Standard Atomic Weight = [210]
2359 Notes =
2360
2361 Atomic Number = 86
2362 Atomic Symbol = Rn
2363 Mass Number = 211
2364 Relative Atomic Mass = 210.9906011(73)
2365 Isotopic Composition =
2366 Standard Atomic Weight = [222]
2367 Notes =
2368
2369 Atomic Number = 86
2370 Atomic Symbol = Rn
2371 Mass Number = 220
2372 Relative Atomic Mass = 220.0113941(23)
2373 Isotopic Composition =
2374 Standard Atomic Weight = [222]
2375 Notes =
2376
2377 Atomic Number = 86
2378 Atomic Symbol = Rn
2379 Mass Number = 222
2380 Relative Atomic Mass = 222.0175782(25)
2381 Isotopic Composition =
2382 Standard Atomic Weight = [222]
2383 Notes =
2384
2385 Atomic Number = 87
2386 Atomic Symbol = Fr
2387 Mass Number = 223
2388 Relative Atomic Mass = 223.0197360(25)
2389 Isotopic Composition =
2390 Standard Atomic Weight = [223]
2391 Notes =
2392
2393 Atomic Number = 88
2394 Atomic Symbol = Ra
2395 Mass Number = 223
2396 Relative Atomic Mass = 223.0185023(27)
2397 Isotopic Composition =
2398 Standard Atomic Weight = [226]
2399 Notes =
2400
2401 Atomic Number = 88
2402 Atomic Symbol = Ra
2403 Mass Number = 224
2404 Relative Atomic Mass = 224.0202120(23)
2405 Isotopic Composition =
2406 Standard Atomic Weight = [226]
2407 Notes =
2408
2409 Atomic Number = 88
2410 Atomic Symbol = Ra
2411 Mass Number = 226
2412 Relative Atomic Mass = 226.0254103(25)
2413 Isotopic Composition =
2414 Standard Atomic Weight = [226]
2415 Notes =
2416
2417 Atomic Number = 88
2418 Atomic Symbol = Ra
2419 Mass Number = 228
2420 Relative Atomic Mass = 228.0310707(26)
2421 Isotopic Composition =
2422 Standard Atomic Weight = [226]
2423 Notes =
2424
2425 Atomic Number = 89
2426 Atomic Symbol = Ac
2427 Mass Number = 227
2428 Relative Atomic Mass = 227.0277523(25)
2429 Isotopic Composition =
2430 Standard Atomic Weight = [227]
2431 Notes =
2432
2433 Atomic Number = 90
2434 Atomic Symbol = Th
2435 Mass Number = 230
2436 Relative Atomic Mass = 230.0331341(19)
2437 Isotopic Composition =
2438 Standard Atomic Weight = 232.0377(4)
2439 Notes = g
2440
2441 Atomic Number = 90
2442 Atomic Symbol = Th
2443 Mass Number = 232
2444 Relative Atomic Mass = 232.0380558(21)
2445 Isotopic Composition = 1
2446 Standard Atomic Weight = 232.0377(4)
2447 Notes = g
2448
2449 Atomic Number = 91
2450 Atomic Symbol = Pa
2451 Mass Number = 231
2452 Relative Atomic Mass = 231.0358842(24)
2453 Isotopic Composition = 1
2454 Standard Atomic Weight = 231.03588(2)
2455 Notes =
2456
2457 Atomic Number = 92
2458 Atomic Symbol = U
2459 Mass Number = 233
2460 Relative Atomic Mass = 233.0396355(29)
2461 Isotopic Composition =
2462 Standard Atomic Weight = 238.02891(3)
2463 Notes = g,m
2464
2465 Atomic Number = 92
2466 Atomic Symbol = U
2467 Mass Number = 234
2468 Relative Atomic Mass = 234.0409523(19)
2469 Isotopic Composition = 0.000054(5)
2470 Standard Atomic Weight = 238.02891(3)
2471 Notes = g,m
2472
2473 Atomic Number = 92
2474 Atomic Symbol = U
2475 Mass Number = 235
2476 Relative Atomic Mass = 235.0439301(19)
2477 Isotopic Composition = 0.007204(6)
2478 Standard Atomic Weight = 238.02891(3)
2479 Notes = g,m
2480
2481 Atomic Number = 92
2482 Atomic Symbol = U
2483 Mass Number = 236
2484 Relative Atomic Mass = 236.0455682(19)
2485 Isotopic Composition =
2486 Standard Atomic Weight = 238.02891(3)
2487 Notes = g,m
2488
2489 Atomic Number = 92
2490 Atomic Symbol = U
2491 Mass Number = 238
2492 Relative Atomic Mass = 238.0507884(20)
2493 Isotopic Composition = 0.992742(10)
2494 Standard Atomic Weight = 238.02891(3)
2495 Notes = g,m
2496
2497 Atomic Number = 93
2498 Atomic Symbol = Np
2499 Mass Number = 236
2500 Relative Atomic Mass = 236.046570(54)
2501 Isotopic Composition =
2502 Standard Atomic Weight = [237]
2503 Notes =
2504
2505 Atomic Number = 93
2506 Atomic Symbol = Np
2507 Mass Number = 237
2508 Relative Atomic Mass = 237.0481736(19)
2509 Isotopic Composition =
2510 Standard Atomic Weight = [237]
2511 Notes =
2512
2513 Atomic Number = 94
2514 Atomic Symbol = Pu
2515 Mass Number = 238
2516 Relative Atomic Mass = 238.0495601(19)
2517 Isotopic Composition =
2518 Standard Atomic Weight = [244]
2519 Notes =
2520
2521 Atomic Number = 94
2522 Atomic Symbol = Pu
2523 Mass Number = 239
2524 Relative Atomic Mass = 239.0521636(19)
2525 Isotopic Composition =
2526 Standard Atomic Weight = [244]
2527 Notes =
2528
2529 Atomic Number = 94
2530 Atomic Symbol = Pu
2531 Mass Number = 240
2532 Relative Atomic Mass = 240.0538138(19)
2533 Isotopic Composition =
2534 Standard Atomic Weight = [244]
2535 Notes =
2536
2537 Atomic Number = 94
2538 Atomic Symbol = Pu
2539 Mass Number = 241
2540 Relative Atomic Mass = 241.0568517(19)
2541 Isotopic Composition =
2542 Standard Atomic Weight = [244]
2543 Notes =
2544
2545 Atomic Number = 94
2546 Atomic Symbol = Pu
2547 Mass Number = 242
2548 Relative Atomic Mass = 242.0587428(20)
2549 Isotopic Composition =
2550 Standard Atomic Weight = [244]
2551 Notes =
2552
2553 Atomic Number = 94
2554 Atomic Symbol = Pu
2555 Mass Number = 244
2556 Relative Atomic Mass = 244.0642053(56)
2557 Isotopic Composition =
2558 Standard Atomic Weight = [244]
2559 Notes =
2560
2561 Atomic Number = 95
2562 Atomic Symbol = Am
2563 Mass Number = 241
2564 Relative Atomic Mass = 241.0568293(19)
2565 Isotopic Composition =
2566 Standard Atomic Weight =
2567 Notes =
2568
2569 Atomic Number = 95
2570 Atomic Symbol = Am
2571 Mass Number = 243
2572 Relative Atomic Mass = 243.0613813(24)
2573 Isotopic Composition =
2574 Standard Atomic Weight =
2575 Notes =
2576
2577 Atomic Number = 96
2578 Atomic Symbol = Cm
2579 Mass Number = 243
2580 Relative Atomic Mass = 243.0613893(22)
2581 Isotopic Composition =
2582 Standard Atomic Weight =
2583 Notes =
2584
2585 Atomic Number = 96
2586 Atomic Symbol = Cm
2587 Mass Number = 244
2588 Relative Atomic Mass = 244.0627528(19)
2589 Isotopic Composition =
2590 Standard Atomic Weight =
2591 Notes =
2592
2593 Atomic Number = 96
2594 Atomic Symbol = Cm
2595 Mass Number = 245
2596 Relative Atomic Mass = 245.0654915(22)
2597 Isotopic Composition =
2598 Standard Atomic Weight =
2599 Notes =
2600
2601 Atomic Number = 96
2602 Atomic Symbol = Cm
2603 Mass Number = 246
2604 Relative Atomic Mass = 246.0672238(22)
2605 Isotopic Composition =
2606 Standard Atomic Weight =
2607 Notes =
2608
2609 Atomic Number = 96
2610 Atomic Symbol = Cm
2611 Mass Number = 247
2612 Relative Atomic Mass = 247.0703541(47)
2613 Isotopic Composition =
2614 Standard Atomic Weight =
2615 Notes =
2616
2617 Atomic Number = 96
2618 Atomic Symbol = Cm
2619 Mass Number = 248
2620 Relative Atomic Mass = 248.0723499(56)
2621 Isotopic Composition =
2622 Standard Atomic Weight =
2623 Notes =
2624
2625 Atomic Number = 97
2626 Atomic Symbol = Bk
2627 Mass Number = 247
2628 Relative Atomic Mass = 247.0703073(59)
2629 Isotopic Composition =
2630 Standard Atomic Weight =
2631 Notes =
2632
2633 Atomic Number = 97
2634 Atomic Symbol = Bk
2635 Mass Number = 249
2636 Relative Atomic Mass = 249.0749877(27)
2637 Isotopic Composition =
2638 Standard Atomic Weight =
2639 Notes =
2640
2641 Atomic Number = 98
2642 Atomic Symbol = Cf
2643 Mass Number = 249
2644 Relative Atomic Mass = 249.0748539(23)
2645 Isotopic Composition =
2646 Standard Atomic Weight =
2647 Notes =
2648
2649 Atomic Number = 98
2650 Atomic Symbol = Cf
2651 Mass Number = 250
2652 Relative Atomic Mass = 250.0764062(22)
2653 Isotopic Composition =
2654 Standard Atomic Weight =
2655 Notes =
2656
2657 Atomic Number = 98
2658 Atomic Symbol = Cf
2659 Mass Number = 251
2660 Relative Atomic Mass = 251.0795886(48)
2661 Isotopic Composition =
2662 Standard Atomic Weight =
2663 Notes =
2664
2665 Atomic Number = 98
2666 Atomic Symbol = Cf
2667 Mass Number = 252
2668 Relative Atomic Mass = 252.0816272(56)
2669 Isotopic Composition =
2670 Standard Atomic Weight =
2671 Notes =
2672
2673 Atomic Number = 99
2674 Atomic Symbol = Es
2675 Mass Number = 252
2676 Relative Atomic Mass = 252.082980(54)
2677 Isotopic Composition =
2678 Standard Atomic Weight =
2679 Notes =
2680
2681 Atomic Number = 100
2682 Atomic Symbol = Fm
2683 Mass Number = 257
2684 Relative Atomic Mass = 257.0951061(69)
2685 Isotopic Composition =
2686 Standard Atomic Weight =
2687 Notes =
2688
2689 Atomic Number = 101
2690 Atomic Symbol = Md
2691 Mass Number = 258
2692 Relative Atomic Mass = 258.0984315(50)
2693 Isotopic Composition =
2694 Standard Atomic Weight =
2695 Notes =
2696
2697 Atomic Number = 101
2698 Atomic Symbol = Md
2699 Mass Number = 260
2700 Relative Atomic Mass = 260.10365(34#)
2701 Isotopic Composition =
2702 Standard Atomic Weight =
2703 Notes =
2704
2705 Atomic Number = 102
2706 Atomic Symbol = No
2707 Mass Number = 259
2708 Relative Atomic Mass = 259.10103(11#)
2709 Isotopic Composition =
2710 Standard Atomic Weight =
2711 Notes =
2712
2713 Atomic Number = 103
2714 Atomic Symbol = Lr
2715 Mass Number = 262
2716 Relative Atomic Mass = 262.10961(22#)
2717 Isotopic Composition =
2718 Standard Atomic Weight =
2719 Notes =
2720
2721 Atomic Number = 104
2722 Atomic Symbol = Rf
2723 Mass Number = 267
2724 Relative Atomic Mass = 267.12179(62#)
2725 Isotopic Composition =
2726 Standard Atomic Weight =
2727 Notes =
2728
2729 Atomic Number = 105
2730 Atomic Symbol = Db
2731 Mass Number = 268
2732 Relative Atomic Mass = 268.12567(57#)
2733 Isotopic Composition =
2734 Standard Atomic Weight =
2735 Notes =
2736
2737 Atomic Number = 106
2738 Atomic Symbol = Sg
2739 Mass Number = 271
2740 Relative Atomic Mass = 271.13393(63#)
2741 Isotopic Composition =
2742 Standard Atomic Weight =
2743 Notes =
2744
2745 Atomic Number = 107
2746 Atomic Symbol = Bh
2747 Mass Number = 272
2748 Relative Atomic Mass = 272.13826(58#)
2749 Isotopic Composition =
2750 Standard Atomic Weight =
2751 Notes =
2752
2753 Atomic Number = 108
2754 Atomic Symbol = Hs
2755 Mass Number = 270
2756 Relative Atomic Mass = 270.13429(27#)
2757 Isotopic Composition =
2758 Standard Atomic Weight =
2759 Notes =
2760
2761 Atomic Number = 109
2762 Atomic Symbol = Mt
2763 Mass Number = 276
2764 Relative Atomic Mass = 276.15159(59#)
2765 Isotopic Composition =
2766 Standard Atomic Weight =
2767 Notes =
2768
2769 Atomic Number = 110
2770 Atomic Symbol = Ds
2771 Mass Number = 281
2772 Relative Atomic Mass = 281.16451(59#)
2773 Isotopic Composition =
2774 Standard Atomic Weight =
2775 Notes =
2776
2777 Atomic Number = 111
2778 Atomic Symbol = Rg
2779 Mass Number = 280
2780 Relative Atomic Mass = 280.16514(61#)
2781 Isotopic Composition =
2782 Standard Atomic Weight =
2783 Notes =
2784
2785 Atomic Number = 112
2786 Atomic Symbol = Cn
2787 Mass Number = 285
2788 Relative Atomic Mass = 285.17712(60#)
2789 Isotopic Composition =
2790 Standard Atomic Weight =
2791 Notes =
2792
2793 Atomic Number = 113
2794 Atomic Symbol = Nh
2795 Mass Number = 284
2796 Relative Atomic Mass = 284.17873(62#)
2797 Isotopic Composition =
2798 Standard Atomic Weight =
2799 Notes =
2800
2801 Atomic Number = 114
2802 Atomic Symbol = Fl
2803 Mass Number = 289
2804 Relative Atomic Mass = 289.19042(60#)
2805 Isotopic Composition =
2806 Standard Atomic Weight =
2807 Notes =
2808
2809 Atomic Number = 115
2810 Atomic Symbol = Mc
2811 Mass Number = 288
2812 Relative Atomic Mass = 288.19274(62#)
2813 Isotopic Composition =
2814 Standard Atomic Weight =
2815 Notes =
2816
2817 Atomic Number = 116
2818 Atomic Symbol = Lv
2819 Mass Number = 293
2820 Relative Atomic Mass = 293.20449(60#)
2821 Isotopic Composition =
2822 Standard Atomic Weight =
2823 Notes =
2824
2825 Atomic Number = 117
2826 Atomic Symbol = Ts
2827 Mass Number = 292
2828 Relative Atomic Mass = 292.20746(75#)
2829 Isotopic Composition =
2830 Standard Atomic Weight =
2831 Notes =
2832
2833 Atomic Number = 118
2834 Atomic Symbol = Og
2835 Mass Number = 294
2836 Relative Atomic Mass = 294.21392(71#)
2837 Isotopic Composition =
2838 Standard Atomic Weight =
2839 Notes =
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 module to handle the access to the optical parameters database
20 """
21
22 import re
23
24 import h5py
25 import numpy
26 import scipy.constants
27
28
29 class DataBase(object):
30
31 def __init__(self, fname):
32 self.fname = fname
33 self.h5file = None # HDF5 file object holding the database
34 self.h5group = None # Group pointing to the actual element
35 self.f0_params = None
36 self.f1_en = None
37 self.f1 = None
38 self.f2_en = None
39 self.f2 = None
40 self.weight = None
41 self.color = None
42 self.radius = numpy.nan
43 self.matname = None
44
45 def Create(self, dbname, dbdesc):
46 """
47 Creates a new database. If the database file already exists
48 its content is delete.
49
50 Parameters
51 ----------
52 dbname : str
53 name of the database
54 dbdesc : str
55 a short description of the database
56 """
57 if self.h5file is not None:
58 print("database already opened - "
59 "close first to create new database")
60 return None
61
62 # tryp to open the database file
63 try:
64 self.h5file = h5py.File(self.fname, 'w')
65 except OSError:
66 print('cannot create database file %s!' % (self.fname))
67 raise
68
69 # set attributes to the root group with database name and
70 # description
71 self.h5file.attrs['DBName'] = dbname
72 self.h5file.attrs['DBDesc'] = dbdesc
73
74 def Open(self, mode='r'):
75 """
76 Open an existing database file.
77 """
78 if self.h5file is not None:
79 print('database already opened - '
80 'close first to open new database!')
81 return
82
83 try:
84 self.h5file = h5py.File(self.fname, mode)
85 except OSError:
86 print("cannot open database file %s!" % (self.fname))
87
88 def Close(self):
89 """
90 Close an opend database file.
91 """
92 if self.h5file is None:
93 print("no database file opened!")
94 return
95
96 self.h5file.close()
97 self.h5file = None
98
99 def CreateMaterial(self, name, description):
100 """
101 This method creates a new material. If the material group already
102 exists the procedure is aborted.
103
104 Parameters
105 ----------
106 name : str
107 name of the material
108 description : str
109 description of the material
110 """
111 if self.h5file is None:
112 print("no database file opened!")
113 return
114
115 if name in self.h5file:
116 # if the material node already exists a warning message is printed
117 print("material node already exists")
118 else:
119 g = self.h5file.create_group(name)
120 g.attrs['name'] = description
121
122 def SetWeight(self, weight):
123 """
124 Save weight of the element as float
125
126 Parameters
127 ----------
128 weight : float
129 atomic standard weight of the element
130 """
131 if not isinstance(weight, float):
132 raise TypeError("weight parameter must be a float!")
133
134 self.h5group.attrs['atomic_standard_weight'] = weight
135 self.h5file.flush()
136
137 def SetColor(self, color):
138 """
139 Save color of the element for visualization
140
141 Parameters
142 ----------
143 color : tuple, str
144 matplotlib color for the element
145 """
146 if not isinstance(color, (tuple, str)):
147 raise TypeError("color parameter must be a tuple or str!")
148
149 self.h5group.attrs['color'] = color
150 self.h5file.flush()
151
152 def SetRadius(self, radius):
153 """
154 Save atomic radius for visualization
155
156 Parameters
157 ----------
158 radius: float
159 atomic radius in Angstrom
160 """
161 if not isinstance(radius, float):
162 raise TypeError("radius parameter must be a float!")
163
164 self.h5group.attrs['atomic_radius'] = radius
165 self.h5file.flush()
166
167 def SetF0(self, parameters, subset='default'):
168 """
169 Save f0 fit parameters for the set material. The fit parameters
170 are stored in the following order:
171 c, a1, b1,......., a4, b4
172
173 Parameters
174 ----------
175 parameters : list or array-like
176 fit parameters
177 subset : str, optional
178 name the f0 dataset
179 """
180 if isinstance(parameters, list):
181 p = numpy.array(parameters, dtype=numpy.float32)
182 elif isinstance(parameters, numpy.ndarray):
183 p = parameters.astype(numpy.float32)
184 else:
185 raise TypeError("f0 fit parameters must be a "
186 "list or a numpy array!")
187
188 if not subset:
189 subset = 'default'
190
191 try:
192 del self.h5group['f0/%s' % subset]
193 except KeyError:
194 pass
195
196 self.h5group.create_dataset('f0/%s' % subset, data=p)
197 self.h5file.flush()
198
199 def SetF1F2(self, en, f1, f2):
200 """
201 Set f1, f2 values for the active material.
202
203 Parameters
204 ----------
205 en : list or array-like
206 energy in (eV)
207 f1 : list or array-like
208 f1 values
209 f2 : list or array-like
210 f2 values
211 """
212 if isinstance(en, (list, tuple)):
213 end = numpy.array(en, dtype=numpy.float32)
214 elif isinstance(en, numpy.ndarray):
215 end = en.astype(numpy.float32)
216 else:
217 raise TypeError("energy values must be a list or a numpy array!")
218
219 if isinstance(f1, (list, tuple)):
220 f1d = numpy.array(f1, dtype=numpy.float32)
221 elif isinstance(f1, numpy.ndarray):
222 f1d = f1.astype(numpy.float32)
223 else:
224 raise TypeError("f1 values must be a list or a numpy array!")
225
226 if isinstance(f2, (list, tuple)):
227 f2d = numpy.array(f2, dtype=numpy.float32)
228 elif isinstance(f2, numpy.ndarray):
229 f2d = f2.astype(numpy.float32)
230 else:
231 raise TypeError("f2 values must be a list or a numpy array!")
232
233 try:
234 del self.h5group['en_f12']
235 except KeyError:
236 pass
237
238 try:
239 del self.h5group['f1']
240 except KeyError:
241 pass
242
243 try:
244 del self.h5group['f2']
245 except KeyError:
246 pass
247
248 self.h5group.create_dataset('en_f12', data=end)
249 self.h5group.create_dataset('f1', data=f1d)
250 self.h5group.create_dataset('f2', data=f2d)
251 self.h5file.flush()
252
253 def SetMaterial(self, name):
254 """
255 Set a particular material in the database as the actual material. All
256 operations like setting and getting optical constants are done for this
257 particular material.
258
259 Parameters
260 ----------
261 name : str
262 name of the material
263 """
264 if self.matname == name:
265 return
266 try:
267 self.h5group = self.h5file[name]
268 except KeyError:
269 print("XU.materials.database: material '%s' not existing!" % name)
270
271 try:
272 self.f0_params = self.h5group['f0']
273 except KeyError:
274 self.f0_params = None
275 try:
276 self.f1_en = self.h5group['en_f12']
277 self.f1 = self.h5group['f1']
278 except KeyError:
279 self.f1_en = None
280 self.f1 = None
281 try:
282 self.f2_en = self.h5group['en_f12']
283 self.f2 = self.h5group['f2']
284 except KeyError:
285 self.f2_en = None
286 self.f2 = None
287 try:
288 self.weight = self.h5group.attrs['atomic_standard_weight']
289 except KeyError:
290 self.weight = None
291 try:
292 self.radius = self.h5group.attrs['atomic_radius']
293 except KeyError:
294 self.radius = numpy.nan
295 try:
296 self.color = self.h5group.attrs['color']
297 except KeyError:
298 self.color = None
299 self.matname = name
300
301 def GetF0(self, q, dset='default'):
302 """
303 Obtain the f0 scattering factor component for a particular
304 momentum transfer q.
305
306 Parameters
307 ----------
308 q : float or array-like
309 momentum transfer
310 dset : str, optional
311 specifies which dataset (different oxidation states)
312 should be used
313 """
314 # get parameters from file
315 if not dset:
316 dset = 'default'
317 f0_params = self.f0_params[dset]
318 # calculate f0
319 if isinstance(q, (numpy.ndarray, list, tuple)):
320 ql = numpy.asarray(q)
321 f0 = f0_params[0] * numpy.ones(ql.shape)
322 else:
323 ql = q
324 f0 = f0_params[0]
325 k = ql / (4. * numpy.pi)
326
327 for i in range(1, len(f0_params) - 1, 2):
328 a = f0_params[i]
329 b = f0_params[i + 1]
330 f0 += a * numpy.exp(-b * k ** 2)
331
332 return f0
333
334 def GetF1(self, en):
335 """
336 Return the second, energy dependent, real part of the scattering
337 factor for a certain energy en.
338
339 Parameters
340 ----------
341 en : float or array-like
342 energy
343 """
344 if1 = numpy.interp(en, self.f1_en, self.f1,
345 left=numpy.nan, right=numpy.nan)
346
347 return if1
348
349 def GetF2(self, en):
350 """
351 Return the imaginary part of the scattering
352 factor for a certain energy en.
353
354 Parameters
355 ----------
356 en : float or array-like
357 energy
358 """
359 if2 = numpy.interp(en, self.f2_en, self.f2,
360 left=numpy.nan, right=numpy.nan)
361
362 return if2
363
364
365 def init_material_db(db):
366 db.CreateMaterial("dummy", "Dummy atom")
367 db.CreateMaterial("H", "Hydrogen")
368 db.CreateMaterial("D", "Deuterium")
369 db.CreateMaterial("T", "Tritium")
370 db.CreateMaterial("He", "Helium")
371 db.CreateMaterial("Li", "Lithium")
372 db.CreateMaterial("Be", "Berylium")
373 db.CreateMaterial("B", "Bor")
374 db.CreateMaterial("C", "Carbon")
375 db.CreateMaterial("N", "Nitrogen")
376 db.CreateMaterial("O", "Oxygen")
377 db.CreateMaterial("F", "Flourine")
378 db.CreateMaterial("Ne", "Neon")
379 db.CreateMaterial("Na", "Sodium")
380 db.CreateMaterial("Mg", "Magnesium")
381 db.CreateMaterial("Al", "Aluminium")
382 db.CreateMaterial("Si", "Silicon")
383 db.CreateMaterial("P", "Phosphorus")
384 db.CreateMaterial("S", "Sulfur")
385 db.CreateMaterial("Cl", "Chlorine")
386 db.CreateMaterial("Ar", "Argon")
387 db.CreateMaterial("K", "Potassium")
388 db.CreateMaterial("Ca", "Calcium")
389 db.CreateMaterial("Sc", "Scandium")
390 db.CreateMaterial("Ti", "Titanium")
391 db.CreateMaterial("V", "Vanadium")
392 db.CreateMaterial("Cr", "Chromium")
393 db.CreateMaterial("Mn", "Manganese")
394 db.CreateMaterial("Fe", "Iron")
395 db.CreateMaterial("Co", "Cobalt")
396 db.CreateMaterial("Ni", "Nickel")
397 db.CreateMaterial("Cu", "Copper")
398 db.CreateMaterial("Zn", "Zinc")
399 db.CreateMaterial("Ga", "Gallium")
400 db.CreateMaterial("Ge", "Germanium")
401 db.CreateMaterial("As", "Arsenic")
402 db.CreateMaterial("Se", "Selenium")
403 db.CreateMaterial("Br", "Bromine")
404 db.CreateMaterial("Kr", "Krypton")
405 db.CreateMaterial("Rb", "Rubidium")
406 db.CreateMaterial("Sr", "Strontium")
407 db.CreateMaterial("Y", "Yttrium")
408 db.CreateMaterial("Zr", "Zirconium")
409 db.CreateMaterial("Nb", "Niobium")
410 db.CreateMaterial("Mo", "Molybdenum")
411 db.CreateMaterial("Tc", "Technetium")
412 db.CreateMaterial("Ru", "Ruthenium")
413 db.CreateMaterial("Rh", "Rhodium")
414 db.CreateMaterial("Pd", "Palladium")
415 db.CreateMaterial("Ag", "Silver")
416 db.CreateMaterial("Cd", "Cadmium")
417 db.CreateMaterial("In", "Indium")
418 db.CreateMaterial("Sn", "Tin")
419 db.CreateMaterial("Sb", "Antimony")
420 db.CreateMaterial("Te", "Tellurium")
421 db.CreateMaterial("I", "Iodine")
422 db.CreateMaterial("Xe", "Xenon")
423 db.CreateMaterial("Cs", "Caesium")
424 db.CreateMaterial("Ba", "Barium")
425 db.CreateMaterial("La", "Lanthanum")
426 db.CreateMaterial("Ce", "Cerium")
427 db.CreateMaterial("Pr", "Praseordymium")
428 db.CreateMaterial("Nd", "Neodymium")
429 db.CreateMaterial("Pm", "Promethium")
430 db.CreateMaterial("Sm", "Samarium")
431 db.CreateMaterial("Eu", "Europium")
432 db.CreateMaterial("Gd", "Gadolinium")
433 db.CreateMaterial("Tb", "Terbium")
434 db.CreateMaterial("Dy", "Dysprosium")
435 db.CreateMaterial("Ho", "Holmium")
436 db.CreateMaterial("Er", "Erbium")
437 db.CreateMaterial("Tm", "Thulium")
438 db.CreateMaterial("Yb", "Ytterbium")
439 db.CreateMaterial("Lu", "Lutetium")
440 db.CreateMaterial("Hf", "Hafnium")
441 db.CreateMaterial("Ta", "Tantalum")
442 db.CreateMaterial("W", "Tungsten")
443 db.CreateMaterial("Re", "Rhenium")
444 db.CreateMaterial("Os", "Osmium")
445 db.CreateMaterial("Ir", "Iridium")
446 db.CreateMaterial("Pt", "Platinum")
447 db.CreateMaterial("Au", "Gold")
448 db.CreateMaterial("Hg", "Mercury")
449 db.CreateMaterial("Tl", "Thallium")
450 db.CreateMaterial("Pb", "Lead")
451 db.CreateMaterial("Bi", "Bismuth")
452 db.CreateMaterial("Po", "Polonium")
453 db.CreateMaterial("At", "Astatine")
454 db.CreateMaterial("Rn", "Radon")
455 db.CreateMaterial("Fr", "Fancium")
456 db.CreateMaterial("Ra", "Radium")
457 db.CreateMaterial("Ac", "Actinium")
458 db.CreateMaterial("Th", "Thorium")
459 db.CreateMaterial("Pa", "Protactinium")
460 db.CreateMaterial("U", "Urianium")
461 db.CreateMaterial("Np", "Neptunium")
462 db.CreateMaterial("Pu", "Plutonium")
463 db.CreateMaterial("Am", "Americium")
464 db.CreateMaterial("Cm", "Curium")
465 db.CreateMaterial("Bk", "Berkelium")
466 db.CreateMaterial("Cf", "Californium")
467 db.CreateMaterial("Es", "Einsteinium")
468 db.CreateMaterial("Fm", "Fermium")
469 db.CreateMaterial("Md", "Mendelevium")
470 db.CreateMaterial("No", "Nobelium")
471 db.CreateMaterial("Lr", "Lawrencium")
472 db.CreateMaterial("Rf", "Rutherfordium")
473 db.CreateMaterial("Db", "Dubnium")
474 db.CreateMaterial("Sg", "Seaborgium")
475 db.CreateMaterial("Bh", "Bohrium")
476 db.CreateMaterial("Hs", "Hassium")
477 db.CreateMaterial("Mt", "Meitnerium")
478 db.CreateMaterial("Ds", "Darmstadtium")
479 db.CreateMaterial("Rg", "Roentgenium")
480 db.CreateMaterial("Cn", "Copernicium")
481 db.CreateMaterial("Nh", "Nihonium")
482 db.CreateMaterial("Fl", "Flerovium")
483 db.CreateMaterial("Mc", "Moscovium")
484 db.CreateMaterial("Lv", "Livermorium")
485 db.CreateMaterial("Ts", "Tennessine")
486 db.CreateMaterial("Og", "Oganesson")
487
488
489 # functions to read database files
490 def add_f0_from_intertab(db, itf, verbose=False):
491 """
492 Read f0 data from International Tables of Crystallography and add
493 it to the database.
494 """
495 # some regular expressions
496 elementstr = re.compile(r"^#S")
497 multiblank = re.compile(r"\s+")
498 while True:
499 lb = itf.readline().decode("utf-8")
500 if lb == "":
501 break
502 lb = lb.strip()
503
504 if elementstr.match(lb):
505 # found new element
506 lb = multiblank.split(lb)
507
508 # determine oxidation state and element name
509 elemstate = re.sub('[A-Za-z]', '', lb[2])
510 for r, o in zip(('dot', 'p', 'm'), ('.', '+', '-')):
511 elemstate = elemstate.replace(o, r)
512 if elemstate == 'p2': # fix wrong name in the source file
513 elemstate = '2p'
514 ename = re.sub('[^A-Za-z]', '', lb[2])
515
516 if verbose:
517 print("{pyname} = Atom('{name}', {num})".format(
518 pyname=ename+elemstate, name=lb[2], num=lb[1]))
519 db.SetMaterial(ename)
520 # make two dummy reads
521 for i in range(2):
522 itf.readline()
523 # read fit parameters
524 lb = itf.readline().decode("utf-8")
525 lb = lb.strip()
526 lb = multiblank.split(lb)
527 a1 = float(lb[0])
528 a2 = float(lb[1])
529 a3 = float(lb[2])
530 a4 = float(lb[3])
531 c = float(lb[4])
532 b1 = float(lb[5])
533 b2 = float(lb[6])
534 b3 = float(lb[7])
535 b4 = float(lb[8])
536 db.SetF0([c, a1, b1, a2, b2, a3, b3, a4, b4], subset=elemstate)
537
538
539 def add_f0_from_xop(db, xop, verbose=False):
540 """
541 Read f0 data from f0_xop.dat and add
542 it to the database.
543 """
544 # some regular expressions
545 elementstr = re.compile(r"^#S")
546 multiblank = re.compile(r"\s+")
547
548 while True:
549 lb = xop.readline().decode("utf-8")
550 if lb == "":
551 break
552 lb = lb.strip()
553
554 if elementstr.match(lb):
555 # found new element
556 lb = multiblank.split(lb)
557 # determine oxidation state and element name
558 elemstate = re.sub('[A-Za-z]', '', lb[2])
559 for r, o in zip(('dot', 'p', 'm'), ('.', '+', '-')):
560 elemstate = elemstate.replace(o, r)
561 ename = re.sub('[^A-Za-z]', '', lb[2])
562
563 if verbose:
564 print("{pyname} = Atom('{name}', {num})".format(
565 pyname=ename+elemstate, name=lb[2], num=lb[1]))
566 db.SetMaterial(ename)
567
568 # make nine dummy reads
569 for i in range(9):
570 xop.readline()
571 # read fit parameters
572 lb = xop.readline().decode("utf-8")
573 lb = lb.strip()
574 lb = multiblank.split(lb)
575 a1 = float(lb[0])
576 a2 = float(lb[1])
577 a3 = float(lb[2])
578 a4 = float(lb[3])
579 a5 = float(lb[4])
580 c = float(lb[5])
581 b1 = float(lb[6])
582 b2 = float(lb[7])
583 b3 = float(lb[8])
584 b4 = float(lb[9])
585 b5 = float(lb[10])
586 db.SetF0([c, a1, b1, a2, b2, a3, b3, a4, b4, a5, b5])
587
588
589 def add_f1f2_from_henkedb(db, hf, verbose=False):
590 """
591 Read f1 and f2 data from Henke database and add
592 it to the database.
593 """
594 # some regular expressions
595 elementstr = re.compile(r"^#S")
596 multiblank = re.compile(r"\s+")
597 invalidelem = re.compile(r"[^A-Za-z]")
598
599 while True:
600 lb = hf.readline().decode("utf-8")
601 if lb == "":
602 break
603 lb = lb.strip()
604
605 if elementstr.match(lb):
606 # found new element
607 lb = multiblank.split(lb)
608 enum = lb[1]
609 ename = lb[2]
610 # check if this is not some funny isotope
611
612 if invalidelem.findall(ename) == []:
613 if verbose:
614 print("set element %s" % ename)
615 db.SetMaterial(ename)
616 # make one dummy read
617 for i in range(5):
618 hf.readline()
619
620 # read data
621 en_list = []
622 f1_list = []
623 f2_list = []
624 while True:
625 lb = hf.readline().decode("utf-8")
626 lb = lb.strip()
627 lb = multiblank.split(lb)
628 en = float(lb[0])
629 # to account for wrong f1 definition in Henke db
630 f1 = float(lb[1]) - float(enum)
631 f2 = float(lb[2])
632 en_list.append(en)
633 f1_list.append(f1)
634 f2_list.append(f2)
635 if en == 30000.:
636 db.SetF1F2(en_list, f1_list, f2_list)
637 break
638
639
640 def add_f1f2_from_kissel(db, kf, verbose=False):
641 """
642 Read f1 and f2 data from Henke database and add
643 it to the database.
644 """
645 # some regular expressions
646 elementstr = re.compile(r"^#S")
647 multiblank = re.compile(r"\s+")
648 invalidelem = re.compile(r"[^A-Za-z]")
649
650 while True:
651 lb = kf.readline().decode("utf-8")
652 if lb == "":
653 break
654 lb = lb.strip()
655
656 if elementstr.match(lb):
657 # found new element
658 lb = multiblank.split(lb)
659 enum = lb[1]
660 ename = lb[2]
661 # check if this is not some funny isotope
662
663 if invalidelem.findall(ename) == []:
664 if verbose:
665 print("set element %s" % ename)
666 db.SetMaterial(ename)
667 # make 28 dummy reads
668 for i in range(28):
669 kf.readline()
670
671 # read data
672 en_list = []
673 f1_list = []
674 f2_list = []
675 while True:
676 lb = kf.readline().decode("utf-8")
677 lb = lb.strip()
678 lb = multiblank.split(lb)
679 en = float(lb[0]) * 1000 # convert energy
680 # to account for wrong f1 definition in Henke db
681 f1 = float(lb[4]) - float(enum)
682 f2 = float(lb[5])
683 en_list.append(en)
684 f1_list.append(f1)
685 f2_list.append(f2)
686 if en == 10000000.:
687 db.SetF1F2(en_list, f1_list, f2_list)
688 break
689
690
691 def add_f1f2_from_ascii_file(db, asciifile, element, verbose=False):
692 """
693 Read f1 and f2 data for specific element from ASCII file (3 columns) and
694 save it to the database.
695 """
696
697 # parse the f1f2 file
698 try:
699 af = numpy.loadtxt(asciifile)
700 except OSError:
701 print("cannot open f1f2 database file")
702 return None
703 db.SetMaterial(element)
704
705 en = af[:, 0]
706 f1 = af[:, 1]
707 f2 = af[:, 2]
708 db.SetF1F2(en, f1, f2)
709
710
711 def add_mass_from_NIST(db, nistfile, verbose=False):
712 """
713 Read atoms standard mass and save it to the database.
714 The mass of the natural isotope mixture is taken from the NIST data!
715 """
716 # some regular expressions
717 isotope = re.compile(r"^Atomic Number =")
718 standardw = re.compile(r"^Standard Atomic Weight")
719 relativew = re.compile(r"^Relative Atomic Mass")
720 number = re.compile(r"[0-9.]+")
721 multiblank = re.compile(r"\s+")
722
723 # parse the nist file
724 with open(nistfile, "r") as nf:
725 while True:
726 lb = nf.readline()
727 if lb == "":
728 break
729 lb = lb.strip()
730
731 if isotope.match(lb):
732 # found new element
733 lb = multiblank.split(lb)
734 lb = nf.readline()
735 lb = lb.strip()
736 lb = multiblank.split(lb)
737 ename = lb[-1]
738
739 if verbose:
740 print("set element %s" % ename)
741 db.SetMaterial(ename)
742
743 # read data
744 while True:
745 lb = nf.readline()
746 lb = lb.strip()
747 if relativew.match(lb):
748 lb = multiblank.split(lb)
749 # extract fallback weight
750 w = float(number.findall(lb[-1])[0])
751 db.SetWeight(w * scipy.constants.atomic_mass)
752 elif standardw.match(lb):
753 lb = multiblank.split(lb)
754 # extract average weight
755 try:
756 w = float(number.findall(lb[-1])[0])
757 db.SetWeight(w * scipy.constants.atomic_mass)
758 except IndexError:
759 pass
760 break
761
762
763 def add_color_from_JMOL(db, cfile, verbose=False):
764 """
765 Read color from JMOL color table and save it to the database.
766 """
767 with open(cfile, "r") as f:
768 for line in f.readlines():
769 s = line.split()
770 ename = s[1]
771 color = [float(num)/255. for num in s[2].strip('[]').split(',')]
772 color = tuple(color)
773 if verbose:
774 print("set element %s" % ename)
775 db.SetMaterial(ename)
776 db.SetColor(color)
777
778
779 def add_radius_from_WIKI(db, dfile, verbose=False):
780 """
781 Read radius from Wikipedia radius table and save it to the database.
782 """
783 with open(dfile, "r") as f:
784 for line in f.readlines():
785 s = line.split(',')
786 ename = s[1]
787 radius = float(s[3]) / 100.
788 if verbose:
789 print("set element %s" % ename)
790 db.SetMaterial(ename)
791 db.SetRadius(radius)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2010-2015 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 from .atom import Atom
19
20 dummy = Atom('dummy', 0)
21 H = Atom('H', 1)
22 Hdot = Atom('H.', 1)
23 H1m = Atom('H1-', 1)
24 D = Atom('D', 1)
25 T = Atom('T', 1)
26 He = Atom('He', 2)
27 Li = Atom('Li', 3)
28 Li1p = Atom('Li1+', 3)
29 Be = Atom('Be', 4)
30 Be2p = Atom('Be2+', 4)
31 B = Atom('B', 5)
32 C = Atom('C', 6)
33 Cdot = Atom('C.', 6)
34 N = Atom('N', 7)
35 O = Atom('O', 8)
36 O1m = Atom('O1-', 8)
37 O2mdot = Atom('O2-.', 8)
38 O2m = Atom('O2-.', 8)
39 F = Atom('F', 9)
40 F1m = Atom('F1-', 9)
41 Ne = Atom('Ne', 10)
42 Na = Atom('Na', 11)
43 Na1p = Atom('Na1+', 11)
44 Mg = Atom('Mg', 12)
45 Mg2p = Atom('Mg2+', 12)
46 Al = Atom('Al', 13)
47 Al3p = Atom('Al3+', 13)
48 Si = Atom('Si', 14)
49 Sidot = Atom('Si.', 14)
50 Si4p = Atom('Si4+', 14)
51 P = Atom('P', 15)
52 S = Atom('S', 16)
53 Cl = Atom('Cl', 17)
54 Cl1m = Atom('Cl1-', 17)
55 Ar = Atom('Ar', 18)
56 K = Atom('K', 19)
57 K1p = Atom('K1+', 19)
58 Ca = Atom('Ca', 20)
59 Ca2p = Atom('Ca2+', 20)
60 Sc = Atom('Sc', 21)
61 Sc3p = Atom('Sc3+', 21)
62 Ti = Atom('Ti', 22)
63 Ti2p = Atom('Ti2+', 22)
64 Ti3p = Atom('Ti3+', 22)
65 Ti4p = Atom('Ti4+', 22)
66 V = Atom('V', 23)
67 V2p = Atom('V2+', 23)
68 V3p = Atom('V3+', 23)
69 V5p = Atom('V5+', 23)
70 Cr = Atom('Cr', 24)
71 Cr2p = Atom('Cr2+', 24)
72 Cr3p = Atom('Cr3+', 24)
73 Mn = Atom('Mn', 25)
74 Mn2p = Atom('Mn2+', 25)
75 Mn3p = Atom('Mn3+', 25)
76 Mn4p = Atom('Mn4+', 25)
77 Fe = Atom('Fe', 26)
78 Fe2p = Atom('Fe2+', 26)
79 Fe3p = Atom('Fe3+', 26)
80 Co = Atom('Co', 27)
81 Co2p = Atom('Co2+', 27)
82 Co3p = Atom('Co3+', 27)
83 Ni = Atom('Ni', 28)
84 Ni2p = Atom('Ni2+', 28)
85 Ni3p = Atom('Ni3+', 28)
86 Cu = Atom('Cu', 29)
87 Cu1p = Atom('Cu1+', 29)
88 Cu2p = Atom('Cu2+', 29)
89 Zn = Atom('Zn', 30)
90 Zn2p = Atom('Zn2+', 30)
91 Ga = Atom('Ga', 31)
92 Ga3p = Atom('Ga3+', 31)
93 Ge = Atom('Ge', 32)
94 Ge4p = Atom('Ge4+', 32)
95 As = Atom('As', 33)
96 Se = Atom('Se', 34)
97 Br = Atom('Br', 35)
98 Br1m = Atom('Br1-', 35)
99 Kr = Atom('Kr', 36)
100 Rb = Atom('Rb', 37)
101 Rb1p = Atom('Rb1+', 37)
102 Sr = Atom('Sr', 38)
103 Sr2p = Atom('Sr2+', 38)
104 Y = Atom('Y', 39)
105 Y3p = Atom('Y3+', 39)
106 Zr = Atom('Zr', 40)
107 Zr4p = Atom('Zr4+', 40)
108 Nb = Atom('Nb', 41)
109 Nb3p = Atom('Nb3+', 41)
110 Nb5p = Atom('Nb5+', 41)
111 Mo = Atom('Mo', 42)
112 Mo3p = Atom('Mo3+', 42)
113 Mo5p = Atom('Mo5+', 42)
114 Mo6p = Atom('Mo6+', 42)
115 Tc = Atom('Tc', 43)
116 Ru = Atom('Ru', 44)
117 Ru3p = Atom('Ru3+', 44)
118 Ru4p = Atom('Ru4+', 44)
119 Rh = Atom('Rh', 45)
120 Rh3p = Atom('Rh3+', 45)
121 Rh4p = Atom('Rh4+', 45)
122 Pd = Atom('Pd', 46)
123 Pd2p = Atom('Pd2+', 46)
124 Pd4p = Atom('Pd4+', 46)
125 Ag = Atom('Ag', 47)
126 Ag1p = Atom('Ag1+', 47)
127 Ag2p = Atom('Ag2+', 47)
128 Cd = Atom('Cd', 48)
129 Cd2p = Atom('Cd2+', 48)
130 In = Atom('In', 49)
131 In3p = Atom('In3+', 49)
132 Sn = Atom('Sn', 50)
133 Sn2p = Atom('Sn2+', 50)
134 Sn4p = Atom('Sn4+', 50)
135 Sb = Atom('Sb', 51)
136 Sb3p = Atom('Sb3+', 51)
137 Sb5p = Atom('Sb5+', 51)
138 Te = Atom('Te', 52)
139 I = Atom('I', 53)
140 I1m = Atom('I1-', 53)
141 Xe = Atom('Xe', 54)
142 Cs = Atom('Cs', 55)
143 Cs1p = Atom('Cs1+', 55)
144 Ba = Atom('Ba', 56)
145 Ba2p = Atom('Ba2+', 56)
146 La = Atom('La', 57)
147 La3p = Atom('La3+', 57)
148 Ce = Atom('Ce', 58)
149 Ce3p = Atom('Ce3+', 58)
150 Ce4p = Atom('Ce4+', 58)
151 Pr = Atom('Pr', 59)
152 Pr3p = Atom('Pr3+', 59)
153 Pr4p = Atom('Pr4+', 59)
154 Nd = Atom('Nd', 60)
155 Nd3p = Atom('Nd3+', 60)
156 Pm = Atom('Pm', 61)
157 Pm3p = Atom('Pm3+', 61)
158 Sm = Atom('Sm', 62)
159 Sm3p = Atom('Sm3+', 62)
160 Eu = Atom('Eu', 63)
161 Eu2p = Atom('Eu2+', 63)
162 Eu3p = Atom('Eu3+', 63)
163 Gd = Atom('Gd', 64)
164 Gd3p = Atom('Gd3+', 64)
165 Tb = Atom('Tb', 65)
166 Tb3p = Atom('Tb3+', 65)
167 Dy = Atom('Dy', 66)
168 Dy3p = Atom('Dy3+', 66)
169 Ho = Atom('Ho', 67)
170 Ho3p = Atom('Ho3+', 67)
171 Er = Atom('Er', 68)
172 Er3p = Atom('Er3+', 68)
173 Tm = Atom('Tm', 69)
174 Tm3p = Atom('Tm3+', 69)
175 Yb = Atom('Yb', 70)
176 Yb2p = Atom('Yb2+', 70)
177 Yb3p = Atom('Yb3+', 70)
178 Lu = Atom('Lu', 71)
179 Lu3p = Atom('Lu3+', 71)
180 Hf = Atom('Hf', 72)
181 Hf4p = Atom('Hf4+', 72)
182 Ta = Atom('Ta', 73)
183 Ta5p = Atom('Ta5+', 73)
184 W = Atom('W', 74)
185 W6p = Atom('W6+', 74)
186 Re = Atom('Re', 75)
187 Os = Atom('Os', 76)
188 Os4p = Atom('Os4+', 76)
189 Ir = Atom('Ir', 77)
190 Ir3p = Atom('Ir3+', 77)
191 Ir4p = Atom('Ir4+', 77)
192 Pt = Atom('Pt', 78)
193 Pt2p = Atom('Pt2+', 78)
194 Pt4p = Atom('Pt4+', 78)
195 Au = Atom('Au', 79)
196 Au1p = Atom('Au1+', 79)
197 Au3p = Atom('Au3+', 79)
198 Hg = Atom('Hg', 80)
199 Hg1p = Atom('Hg1+', 80)
200 Hg2p = Atom('Hg2+', 80)
201 Tl = Atom('Tl', 81)
202 Tl1p = Atom('Tl1+', 81)
203 Tl3p = Atom('Tl3+', 81)
204 Pb = Atom('Pb', 82)
205 Pb2p = Atom('Pb2+', 82)
206 Pb4p = Atom('Pb4+', 82)
207 Bi = Atom('Bi', 83)
208 Bi3p = Atom('Bi3+', 83)
209 Bi5p = Atom('Bi5+', 83)
210 Po = Atom('Po', 84)
211 At = Atom('At', 85)
212 Rn = Atom('Rn', 86)
213 Fr = Atom('Fr', 87)
214 Ra = Atom('Ra', 88)
215 Ra2p = Atom('Ra2+', 88)
216 Ac = Atom('Ac', 89)
217 Ac3p = Atom('Ac3+', 89)
218 Th = Atom('Th', 90)
219 Th4p = Atom('Th4+', 90)
220 Pa = Atom('Pa', 91)
221 U = Atom('U', 92)
222 U3p = Atom('U3+', 92)
223 U4p = Atom('U4+', 92)
224 U6p = Atom('U6+', 92)
225 Np = Atom('Np', 93)
226 Np3p = Atom('Np3+', 93)
227 Np4p = Atom('Np4+', 93)
228 Np6p = Atom('Np6+', 93)
229 Pu = Atom('Pu', 94)
230 Pu3p = Atom('Pu3+', 94)
231 Pu4p = Atom('Pu4+', 94)
232 Pu6p = Atom('Pu6+', 94)
233 Am = Atom('Am', 95)
234 Cm = Atom('Cm', 96)
235 Bk = Atom('Bk', 97)
236 Cf = Atom('Cf', 98)
237 Es = Atom("Es", 99)
238 Fm = Atom("Fm", 100)
239 Md = Atom("Md", 101)
240 No = Atom("No", 102)
241 Lr = Atom("Lr", 103)
242 Rf = Atom("Rf", 104)
243 Db = Atom("Db", 105)
244 Sg = Atom("Sg", 106)
245 Bh = Atom("Bh", 107)
246 Hs = Atom("Hs", 108)
247 Mt = Atom("Mt", 109)
248 Ds = Atom("Ds", 110)
249 Rg = Atom("Rg", 111)
250 Cn = Atom("Cn", 112)
251 Uut = Atom("Uut", 113)
252 Uuq = Atom("Uuq", 114)
253 Uup = Atom("Uup", 115)
254 Uuh = Atom("Uuh", 116)
255 Uus = Atom("Uus", 117)
256 Uuo = Atom("Uuo", 118)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 implement convenience functions to define Heusler materials.
19 """
20
21 from . import elements
22 from .material import Crystal
23 from .spacegrouplattice import SGLattice
24
25 __all__ = ['FullHeuslerCubic225', 'FullHeuslerCubic225_A2',
26 'FullHeuslerCubic225_B2', 'FullHeuslerCubic225_DO3',
27 'HeuslerHexagonal194', 'HeuslerTetragonal119',
28 'HeuslerTetragonal139', 'InverseHeuslerCubic216']
29
30
31 def _check_elements(*elem):
32 ret = []
33 for el in elem:
34 if isinstance(el, str):
35 ret.append(getattr(elements, el))
36 else:
37 ret.append(el)
38 return ret
39
40
41 def FullHeuslerCubic225(X, Y, Z, a, biso=[0, 0, 0], occ=[1, 1, 1]):
42 """
43 Full Heusler structure with formula X2YZ.
44 Strukturberichte symbol L2_1; space group Fm-3m (225)
45
46 Parameters
47 ----------
48 X, Y, Z : str or Element
49 elements
50 a : float
51 cubic lattice parameter in Angstroem
52 biso : list of floats, optional
53 Debye Waller factors for X, Y, Z elements
54 occ : list of floats, optional
55 occupation numbers for the elements X, Y, Z
56
57 Returns
58 -------
59 Crystal
60 Crystal describing the Heusler material
61 """
62 x, y, z = _check_elements(X, Y, Z)
63 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
64 SGLattice(225, a, atoms=[x, y, z], pos=['8c', '4a', '4b'],
65 b=biso, occ=occ))
66
67
68 def FullHeuslerCubic225_B2(X, Y, Z, a, b2dis, biso=[0, 0, 0], occ=[1, 1, 1]):
69 """
70 Full Heusler structure with formula X2YZ.
71 Strukturberichte symbol L2_1; space group Fm-3m (225) with B2-type (CsCl)
72 disorder
73
74 Parameters
75 ----------
76 X, Y, Z : str or Element
77 elements
78 a : float
79 cubic lattice parameter in Angstroem
80 b2dis : float
81 amount of B2-type disorder (0: fully ordered, 1: fully disordered)
82 biso : list of floats, optional
83 Debye Waller factors for X, Y, Z elements
84 occ : list of floats, optional
85 occupation numbers for the elements X, Y, Z
86
87 Returns
88 -------
89 Crystal
90 Crystal describing the Heusler material
91 """
92 x, y, z = _check_elements(X, Y, Z)
93 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
94 SGLattice(225, a,
95 atoms=[x, y, z, y, z],
96 pos=['8c', '4a', '4b', '4b', '4a'],
97 occ=[1*occ[0], (1-b2dis/2.)*occ[1],
98 (1-b2dis/2.)*occ[2], b2dis/2.*occ[1],
99 b2dis/2.*occ[2]],
100 b=biso + [biso[1], biso[2]]))
101
102
103 def FullHeuslerCubic225_A2(X, Y, Z, a, a2dis, biso=[0, 0, 0], occ=[1, 1, 1]):
104 """
105 Full Heusler structure with formula X2YZ.
106 Strukturberichte symbol L2_1; space group Fm-3m (225) with A2-type (W)
107 disorder
108
109 Parameters
110 ----------
111 X, Y, Z : str or Element
112 elements
113 a : float
114 cubic lattice parameter in Angstroem
115 a2dis : float
116 amount of A2-type disorder (0: fully ordered, 1: fully disordered)
117 biso : list of floats, optional
118 Debye Waller factors for X, Y, Z elements
119 occ : list of floats, optional
120 occupation numbers for the elements X, Y, Z
121
122 Returns
123 -------
124 Crystal
125 Crystal describing the Heusler material
126 """
127 x, y, z = _check_elements(X, Y, Z)
128 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
129 SGLattice(225, a,
130 atoms=[x, x, x, y, y, y, z, z, z],
131 pos=['8c', '4a', '4b',
132 '8c', '4a', '4b',
133 '8c', '4a', '4b'],
134 occ=[(1-a2dis/2.)*occ[0], a2dis/2.*occ[0],
135 a2dis/2.*occ[0], a2dis/4.*occ[1],
136 (1-a2dis*3./4.)*occ[1], a2dis/4.*occ[1],
137 a2dis/4.*occ[2], a2dis/4.*occ[2],
138 (1-a2dis*3./4.)*occ[2]],
139 b=[biso[0], ]*3 + [biso[1], ]*3 + [biso[2], ]*3))
140
141
142 def FullHeuslerCubic225_DO3(X, Y, Z, a, do3disxy, do3disxz, biso=[0, 0, 0],
143 occ=[1, 1, 1]):
144 """
145 Full Heusler structure with formula X2YZ.
146 Strukturberichte symbol L2_1; space group Fm-3m (225) with DO_3-type (BiF3)
147 disorder, either between atoms X <-> Y or X <-> Z.
148
149 Parameters
150 ----------
151 X, Y, Z : str or Element
152 elements
153 a : float
154 cubic lattice parameter in Angstroem
155 do3disxy : float
156 amount of DO_3-type disorder between X and Y atoms (0: fully ordered,
157 1: fully disordered)
158 do3disxz : float
159 amount of DO_3-type disorder between X and Z atoms (0: fully ordered,
160 1: fully disordered)
161 biso : list of floats, optional
162 Debye Waller factors for X, Y, Z elements
163 occ : list of floats, optional
164 occupation numbers for the elements X, Y, Z
165
166 Returns
167 -------
168 Crystal
169 Crystal describing the Heusler material
170 """
171 x, y, z = _check_elements(X, Y, Z)
172 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
173 SGLattice(225, a,
174 atoms=[x, y, z,
175 x, y,
176 x, z],
177 pos=['8c', '4a', '4b',
178 '4a', '8c',
179 '4b', '8c'],
180 occ=[(1-do3disxy/3.-do3disxz/3.)*occ[0],
181 (1-do3disxy*2/3.)*occ[1],
182 (1-do3disxz*2/3.)*occ[2],
183 do3disxy*2/3.*occ[0], do3disxy*1/3.*occ[1],
184 do3disxz*2/3.*occ[0], do3disxz*1/3.*occ[2]],
185 b=biso + [biso[0], biso[1]] + [biso[0], biso[2]]))
186
187
188 def InverseHeuslerCubic216(X, Y, Z, a, biso=[0, 0, 0], occ=[1, 1, 1]):
189 """
190 Full Heusler structure with formula (XY)X'Z structure;
191 space group F-43m (216)
192
193 Parameters
194 ----------
195 X, Y, Z : str or Element
196 elements
197 a : float
198 cubic lattice parameter in Angstroem
199
200 Returns
201 -------
202 Crystal
203 Crystal describing the Heusler material
204 """
205 x, y, z = _check_elements(X, Y, Z)
206 return Crystal('(%s%s)%s\'%s' % (x.basename, y.basename,
207 x.basename, z.basename),
208 SGLattice(216, a, atoms=[x, x, y, z],
209 pos=['4a', '4d', '4b', '4c'],
210 b=[biso[0], ] + biso,
211 occ=[occ[0], ] + occ))
212
213
214 def HeuslerTetragonal139(X, Y, Z, a, c, biso=[0, 0, 0], occ=[1, 1, 1]):
215 """
216 Tetragonal Heusler structure with formula X2YZ
217 space group I4/mmm (139)
218
219 Parameters
220 ----------
221 X, Y, Z : str or Element
222 elements
223 a, c : float
224 tetragonal lattice parameters in Angstroem
225
226 Returns
227 -------
228 Crystal
229 Crystal describing the Heusler material
230 """
231 x, y, z = _check_elements(X, Y, Z)
232 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
233 SGLattice(139, a, c,
234 atoms=[x, y, z],
235 pos=['4d', '2b', '2a'],
236 b=biso, occ=occ))
237
238
239 def HeuslerTetragonal119(X, Y, Z, a, c, biso=[0, 0, 0], occ=[1, 1, 1]):
240 """
241 Tetragonal Heusler structure with formula X2YZ
242 space group I-4m2 (119)
243
244 Parameters
245 ----------
246 X, Y, Z : str or Element
247 elements
248 a, c : float
249 tetragonal lattice parameters in Angstroem
250
251 Returns
252 -------
253 Crystal
254 Crystal describing the Heusler material
255 """
256 x, y, z = _check_elements(X, Y, Z)
257 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
258 SGLattice(119, a, c,
259 atoms=[x, x, y, z],
260 pos=['2b', '2c', '2d', '2a'],
261 b=[biso[0], ] + biso,
262 occ=[occ[0], ] + occ))
263
264
265 def HeuslerHexagonal194(X, Y, Z, a, c, biso=[0, 0, 0], occ=[1, 1, 1]):
266 """
267 Hexagonal Heusler structure with formula XYZ
268 space group P63/mmc (194)
269
270 Parameters
271 ----------
272 X, Y, Z : str or Element
273 elements
274 a, c : float
275 hexagonal lattice parameters in Angstroem
276
277 Returns
278 -------
279 Crystal
280 Crystal describing the Heusler material
281 """
282 x, y, z = _check_elements(X, Y, Z)
283 return Crystal('%s%s%s' % (x.basename, y.basename, z.basename),
284 SGLattice(194, a, c,
285 atoms=[x, y, z],
286 pos=['2a', '2c', '2d'],
287 b=biso, occ=occ))
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17 # Copyright (C) 2012 Tanja Etzelstorfer <tanja.etzelstorfer@jku.at>
18
19 """
20 Classes decribing materials. Materials are devided with respect to their
21 crystalline state in either Amorphous or Crystal types. While for most
22 materials their crystalline state is defined few materials are also included as
23 amorphous which can be useful for calculation of their optical properties.
24 """
25 import abc
26 import copy
27 import numbers
28 import operator
29 import re
30 import warnings
31 from math import ceil, copysign
32
33 import numpy
34 import scipy.optimize
35 from pkg_resources import parse_version
36
37 from .. import config, math, utilities
38 from ..exception import InputError
39 from ..math import VecCross, VecDot, VecNorm
40 from . import cif, elements
41 from .atom import Atom
42 from .spacegrouplattice import WyckoffBase
43
44 numpy.seterr(divide='ignore', invalid='ignore')
45
46 map_ijkl2ij = {"00": 0, "11": 1, "22": 2,
47 "12": 3, "20": 4, "01": 5,
48 "21": 6, "02": 7, "10": 8}
49 map_ij2ijkl = {"0": [0, 0], "1": [1, 1], "2": [2, 2],
50 "3": [1, 2], "4": [2, 0], "5": [0, 1],
51 "6": [2, 1], "7": [0, 2], "8": [1, 0]}
52
53
54 def index_map_ijkl2ij(i, j):
55 return map_ijkl2ij["%i%i" % (i, j)]
56
57
58 def index_map_ij2ijkl(ij):
59 return map_ij2ijkl["%i" % ij]
60
61
62 def Cij2Cijkl(cij):
63 """
64 Converts the elastic constants matrix (tensor of rank 2) to
65 the full rank 4 cijkl tensor.
66
67 Parameters
68 ----------
69 cij : array-like
70 (6, 6) cij matrix
71
72 Returns
73 -------
74 cijkl ndarray
75 (3, 3, 3, 3) cijkl tensor as numpy array
76 """
77
78 # first have to build a 9x9 matrix from the 6x6 one
79 m = numpy.zeros((9, 9), dtype=numpy.double)
80 m[0:6, 0:6] = cij[:, :]
81 m[6:9, 0:6] = cij[3:6, :]
82 m[0:6, 6:9] = cij[:, 3:6]
83 m[6:9, 6:9] = cij[3:6, 3:6]
84
85 # now create the full tensor
86 cijkl = numpy.empty((3, 3, 3, 3), dtype=numpy.double)
87
88 for i in range(0, 3):
89 for j in range(0, 3):
90 for k in range(0, 3):
91 for l in range(0, 3):
92 mi = index_map_ijkl2ij(i, j)
93 mj = index_map_ijkl2ij(k, l)
94 cijkl[i, j, k, l] = m[mi, mj]
95 return cijkl
96
97
98 def Cijkl2Cij(cijkl):
99 """
100 Converts the full rank 4 tensor of the elastic constants to
101 the (6, 6) matrix of elastic constants.
102
103 Parameters
104 ----------
105 cijkl ndarray
106 (3, 3, 3, 3) cijkl tensor as numpy array
107
108 Returns
109 -------
110 cij : array-like
111 (6, 6) cij matrix
112 """
113
114 cij = numpy.empty((6, 6), dtype=numpy.double)
115
116 for i in range(6):
117 for j in range(6):
118 ij = index_map_ij2ijkl(i)
119 kl = index_map_ij2ijkl(j)
120 cij[i, j] = cijkl[ij[0], ij[1], kl[0], kl[1]]
121
122 return cij
123
124
125 class Material(utilities.ABC):
126 """
127 base class for all Materials. common properties of amorphous and
128 crystalline materials are described by this class from which Amorphous and
129 Crystal are derived from.
130 """
131
132 def __init__(self, name, cij=None):
133 if cij is None:
134 self.cij = numpy.zeros((6, 6), dtype=numpy.double)
135 self.cijkl = numpy.zeros((3, 3, 3, 3), dtype=numpy.double)
136 elif isinstance(cij, (tuple, list, numpy.ndarray)):
137 self.cij = numpy.asarray(cij, dtype=numpy.double)
138 self.cijkl = Cij2Cijkl(self.cij)
139 else:
140 raise TypeError("Elastic constants must be a list or numpy array!")
141
142 self.name = name
143 self.transform = None
144 self._density = None
145
146 def __getattr__(self, name):
147 if name.startswith("c"):
148 index = name[1:]
149 if len(index) > 2:
150 raise AttributeError("Cij indices must be between 1 and 6")
151
152 i = int(index[0])
153 j = int(index[1])
154
155 if i > 6 or i < 1 or j > 6 or j < 1:
156 raise AttributeError("Cij indices must be between 1 and 6")
157
158 if callable(self.transform):
159 cij = Cijkl2Cij(self.transform(Cij2Cijkl(self.cij)))
160 else:
161 cij = self.cij
162
163 return cij[i - 1, j - 1]
164 else:
165 object.__getattribute__(self, name)
166
167 def _getmu(self):
168 return self.cij[3, 3]
169
170 def _getlam(self):
171 return self.cij[0, 1]
172
173 def _getnu(self):
174 return self.lam / 2. / (self.mu + self.lam)
175
176 def _getdensity(self):
177 return self._density
178
179 density = property(_getdensity)
180 mu = property(_getmu)
181 lam = property(_getlam)
182 nu = property(_getnu)
183
184 @abc.abstractmethod
185 def delta(self, en='config'):
186 """
187 abstract method which every implementation of a Material has to
188 override
189 """
190 pass
191
192 @abc.abstractmethod
193 def ibeta(self, en='config'):
194 """
195 abstract method which every implementation of a Material has to
196 override
197 """
198 pass
199
200 def chi0(self, en='config'):
201 """
202 calculates the complex chi_0 values often needed in simulations.
203 They are closely related to delta and beta
204 (n = 1 + chi_r0/2 + i*chi_i0/2 vs. n = 1 - delta + i*beta)
205 """
206 return (-2 * self.delta(en) + 2j * self.ibeta(en))
207
208 def idx_refraction(self, en="config"):
209 """
210 function to calculate the complex index of refraction of a material
211 in the x-ray range
212
213 Parameters
214 ----------
215 en : energy of the x-rays, if omitted the value from the
216 xrayutilities configuration is used
217
218 Returns
219 -------
220 n (complex)
221 """
222 n = 1. - self.delta(en) + 1.j * self.ibeta(en)
223 return n
224
225 def critical_angle(self, en='config', deg=True):
226 """
227 calculate critical angle for total external reflection
228
229 Parameters
230 ----------
231 en : float or str, optional
232 energy of the x-rays in eV, if omitted the value from the
233 xrayutilities configuration is used
234 deg : bool, optional
235 return angle in degree if True otherwise radians (default:True)
236
237 Returns
238 -------
239 float
240 Angle of total external reflection
241 """
242 rn = 1. - self.delta(en)
243
244 alphac = numpy.arccos(rn)
245 if deg:
246 alphac = numpy.degrees(alphac)
247
248 return alphac
249
250 def absorption_length(self, en='config'):
251 """
252 wavelength dependent x-ray absorption length defined as
253 mu = lambda/(2*pi*2*beta) with lambda and beta as the x-ray
254 wavelength and complex part of the refractive index respectively.
255
256 Parameters
257 ----------
258 en : float or str, optional
259 energy of the x-rays in eV
260
261 Returns
262 -------
263 float
264 the absorption length in um
265 """
266 if isinstance(en, str) and en == 'config':
267 en = utilities.energy(config.ENERGY)
268 return utilities.en2lam(en) / (2 * numpy.pi * self.ibeta(en) * 2) / 1e4
269
270 def __str__(self):
271 ostr = "%s: %s\n" % (self.__class__.__name__, self.name)
272 if numpy.any(self.cij):
273 ostr += "Elastic tensor (6x6):\n"
274 d = numpy.get_printoptions()
275 numpy.set_printoptions(precision=2, linewidth=78, suppress=False)
276 ostr += str(self.cij) + '\n'
277 numpy.set_printoptions(d)
278
279 return ostr
280
281
282 class Amorphous(Material):
283 """
284 amorphous materials are described by this class
285 """
286
287 def __init__(self, name, density, atoms=None, cij=None):
288 """
289 constructor of an amorphous material. The amorphous material is
290 described by its density and atom composition.
291
292 Parameters
293 ----------
294 name : str
295 name of the material. To allow automatic parsing of the chemical
296 elements use the abbreviation of the chemical element from the
297 periodic table. To specify alloys, use e.g. 'Ir0.2Mn0.8' or 'H2O'.
298 density : float
299 mass density in kg/m^3
300 atoms : list, optional
301 list of atoms together with their fractional content. When the
302 name is a simply chemical formula then this can be None. To
303 specify more complicated materials use [('Ir', 0.2), ('Mn', 0.8),
304 ...]. Instead of the elements as string you can also use an Atom
305 object. If the contents to not add up to 1 they will be corrected
306 without notice.
307 cij : array-like, optional
308 elasticity matrix
309 """
310 super().__init__(name, cij)
311 self._density = density
312 self.base = list()
313 if atoms is None:
314 comp = Amorphous.parseChemForm(name)
315 if config.VERBOSITY >= config.DEBUG:
316 print("XU.materials.Amorphous: using '%s' as chemical formula"
317 % ''.join(['%s%.2f ' % (e.name, c) for e, c in comp]))
318 for (e, c) in comp:
319 self.base.append((e, c))
320 else:
321 frsum = numpy.sum([at[1] for at in atoms])
322 for at, fr in atoms:
323 if not isinstance(at, Atom):
324 a = getattr(elements, at)
325 else:
326 a = at
327 self.base.append((a, fr/frsum))
328
329 @staticmethod
330 def parseChemForm(cstring):
331 """
332 Parse a string containing a simple chemical formula and transform it to
333 a list of elements together with their relative atomic fraction. e.g.
334 'H2O' -> [(H, 2/3), (O, 1/3)], where H and O are the Element objects of
335 Hydrogen and Oxygen. Note that every chemical element needs to start
336 with a capital letter! Complicated formulas containing bracket are not
337 supported!
338
339 Parameters
340 ----------
341 cstring : str
342 string containing the chemical fomula
343
344 Returns
345 -------
346 list of tuples
347 chemical element and atomic fraction
348 """
349 if re.findall(r'[\(\)]', cstring):
350 raise ValueError('unsupported chemical formula (%s) given.'
351 % cstring)
352 elems = re.findall('[A-Z][^A-Z]*', cstring)
353 r = re.compile(r"([a-zA-Z]+)([0-9\.]+)")
354 ret = []
355 csum = 0
356 for e in elems:
357 if r.match(e):
358 elstr, cont = r.match(e).groups()
359 cont = float(cont)
360 else:
361 elstr, cont = (e, 1.0)
362 ret.append((elstr, cont))
363 csum += cont
364 for i, r in enumerate(ret):
365 ret[i] = (getattr(elements, r[0]), r[1]/csum)
366 return ret
367
368 def _get_f(self, q, en):
369 """
370 optimized method to calculate the atomic scattering factor for all
371 atoms in the unit cell by calling the database only as much as needed.
372
373 Parameters
374 ----------
375 q : float or array-like
376 momentum transfer for which the atomic scattering factor should be
377 calculated
378 en : float or str
379 x-ray energy (eV)
380
381 Returns
382 -------
383 list
384 atomic scattering factors for every atom in the unit cell
385 """
386 f = {}
387 for at, occ in self.base:
388 if at.num not in f:
389 f[at.num] = at.f(q, en)
390 return [f[a.num] for a, o in self.base]
391
392 def delta(self, en='config'):
393 """
394 function to calculate the real part of the deviation of the
395 refractive index from 1 (n=1-delta+i*beta)
396
397 Parameters
398 ----------
399 en : float, array-like or str, optional
400 energy of the x-rays in eV
401
402 Returns
403 -------
404 float or array-like
405 """
406 re = scipy.constants.physical_constants['classical electron radius'][0]
407 re *= 1e10
408 if isinstance(en, str) and en == 'config':
409 en = utilities.energy(config.ENERGY)
410
411 lam = utilities.en2lam(en)
412 delta = 0.
413 m = 0.
414 f = self._get_f(0., en)
415 for (at, occ), fa in zip(self.base, f):
416 delta += numpy.real(fa) * occ
417 m += at.weight * occ
418
419 delta *= re / (2 * numpy.pi) * lam ** 2 / (m / self.density) * 1e-30
420 return delta
421
422 def ibeta(self, en='config'):
423 """
424 function to calculate the imaginary part of the deviation
425 of the refractive index from 1 (n=1-delta+i*beta)
426
427 Parameters
428 ----------
429 en : float, array-like or str, optional
430 energy of the x-rays in eV
431
432 Returns
433 -------
434 float or array-like
435 """
436 re = scipy.constants.physical_constants['classical electron radius'][0]
437 re *= 1e10
438 if isinstance(en, str) and en == 'config':
439 en = utilities.energy(config.ENERGY)
440
441 lam = utilities.en2lam(en)
442 beta = 0.
443 m = 0.
444 f = self._get_f(0., en)
445 for (at, occ), fa in zip(self.base, f):
446 beta += numpy.imag(fa) * occ
447 m += at.weight * occ
448
449 beta *= re / (2 * numpy.pi) * lam ** 2 / (m / self.density) * 1e-30
450 return beta
451
452 def chi0(self, en='config'):
453 """
454 calculates the complex chi_0 values often needed in simulations.
455 They are closely related to delta and beta
456 (n = 1 + chi_r0/2 + i*chi_i0/2 vs. n = 1 - delta + i*beta)
457 """
458 re = scipy.constants.physical_constants['classical electron radius'][0]
459 re *= 1e10
460 if isinstance(en, str) and en == 'config':
461 en = utilities.energy(config.ENERGY)
462
463 lam = utilities.en2lam(en)
464 beta = 0.
465 delta = 0.
466 m = 0.
467 f = self._get_f(0., en)
468 for (at, occ), f0 in zip(self.base, f):
469 beta += numpy.imag(f0) * occ
470 delta += numpy.real(f0) * occ
471 m += at.weight * occ
472
473 beta *= re / (2 * numpy.pi) * lam ** 2 / (m / self.density) * 1e-30
474 delta *= re / (2 * numpy.pi) * lam ** 2 / (m / self.density) * 1e-30
475 return (-2 * delta + 2j * beta)
476
477 def __str__(self):
478 ostr = super().__str__()
479 ostr += "density: %.2f\n" % self.density
480 if self.base:
481 ostr += "atoms: "
482 for at, o in self.base:
483 ostr += "(%s, %.3f) " % (at.name, o)
484 ostr += "\n"
485
486 return ostr
487
488
489 class Crystal(Material):
490 """
491 Crystalline materials are described by this class
492 """
493
494 def __init__(self, name, lat, cij=None, thetaDebye=None):
495 super().__init__(name, cij)
496
497 self.lattice = lat
498 if isinstance(thetaDebye, numbers.Number):
499 self.thetaDebye = float(thetaDebye)
500 else:
501 self.thetaDebye = thetaDebye
502
503 @classmethod
504 def fromCIF(cls, ciffilestr):
505 """
506 Create a Crystal from a CIF file. The default data-set from the cif
507 file will be used to create the Crystal.
508
509 Parameters
510 ----------
511 ciffilestr : str, bytes
512 filename of the CIF file or string representation of the CIF file
513
514 Returns
515 -------
516 Crystal
517 """
518 cf = cif.CIFFile(ciffilestr)
519 lat = cf.SGLattice()
520 return cls(cf.data[cf._default_dataset].name, lat)
521
522 def loadLatticefromCIF(self, ciffilestr):
523 """
524 load the unit cell data (lattice) from the CIF file. Other material
525 properties stay unchanged.
526
527 Parameters
528 ----------
529 ciffilestr : str, bytes
530 filename of the CIF file or string representation of the CIF file
531 """
532 cf = cif.CIFFile(ciffilestr)
533 self.lattice = cf.SGLattice()
534
535 def toCIF(self, ciffilename):
536 """
537 Export the Crystal to a CIF file.
538
539 Parameters
540 ----------
541 ciffilename : str
542 filename of the CIF file
543 """
544 cif.cifexport(ciffilename, self)
545
546 @property
547 def a(self):
548 return self.lattice.a
549
550 @property
551 def b(self):
552 return self.lattice.b
553
554 @property
555 def c(self):
556 return self.lattice.c
557
558 @property
559 def alpha(self):
560 return self.lattice.alpha
561
562 @property
563 def beta(self):
564 return self.lattice.beta
565
566 @property
567 def gamma(self):
568 return self.lattice.gamma
569
570 @property
571 def a1(self):
572 return self.lattice._ai[0, :]
573
574 @property
575 def a2(self):
576 return self.lattice._ai[1, :]
577
578 @property
579 def a3(self):
580 return self.lattice._ai[2, :]
581
582 @property
583 def B(self):
584 return self.lattice.qtransform.matrix
585
586 def __eq__(self, other):
587 """
588 compare if another Crystal instance is equal to the current one.
589 Currently this considers only the lattice to be equal. Additional
590 parameters like thetaDebye and the eleastic parameters are ignored.
591
592 Parameters
593 ----------
594 other: Crystal
595 another instance of Crystal to compare
596 """
597 return self.lattice == other.lattice
598
599 def Q(self, *hkl):
600 """
601 Return the Q-space position for a certain material.
602
603 Parameters
604 ----------
605 hkl : list or array-like
606 Miller indices (or Q(h, k, l) is also possible)
607
608 """
609 return self.lattice.GetQ(*hkl)
610
611 def HKL(self, *q):
612 """
613 Return the HKL-coordinates for a certain Q-space position.
614
615 Parameters
616 ----------
617 q : list or array-like
618 Q-position. its also possible to use HKL(qx, qy, qz).
619
620 """
621 return self.lattice.GetHKL(*q)
622
623 def chemical_composition(self, natoms=None, with_spaces=False, ndigits=2):
624 """
625 determine chemical composition from occupancy of atomic positions.
626
627 Parameters
628 ----------
629 mat : Crystal
630 instance of Crystal
631 natoms : int, optional
632 number of atoms to normalize the formula, if None some automatic
633 normalization is attempted using the greatest common divisor of the
634 number of atoms per unit cell. If the number of atoms of any
635 element is fractional natoms=1 is used.
636 with_spaces : bool, optional
637 add spaces between the different entries in the output string for
638 CIF combatibility
639 ndigits : int, optional
640 number of digits to which floating point numbers are rounded to
641
642 Returns
643 -------
644 str
645 representation of the chemical composition
646 """
647 elem = {}
648 for a in self.lattice.base():
649 e = a[0].name
650 occ = a[2]
651 if e in elem:
652 elem[e] += occ
653 else:
654 elem[e] = occ
655 natom = sum([elem[e] for e in elem])
656 isint = True
657 for e in elem:
658 if not float(elem[e]).is_integer():
659 isint = False
660 # determine number of atoms
661 if not natoms:
662 if isint:
663 gcd = math.gcd([int(elem[e]) for e in elem])
664 natoms = natom/gcd
665 else:
666 natoms = 1
667
668 # generate output strig
669 cstr = ''
670 fmtstr = '%d' if isint else '%%.%df' % ndigits
671 for e in elem:
672 n = elem[e] / float(natom) * natoms
673 cstr += e
674 if n != 1:
675 cstr += fmtstr % n
676 cstr += ' ' if with_spaces else ''
677 return cstr.strip()
678
679 def environment(self, *pos, **kwargs):
680 """
681 Returns a list of neighboring atoms for a given position within the the
682 unit cell.
683
684 Parameters
685 ----------
686 pos : list or array-like
687 fractional coordinate in the unit cell
688 maxdist : float
689 maximum distance wanted in the list of neighbors (default: 7)
690
691 Returns
692 -------
693 list of tuples
694 (distance, atomType, multiplicity) giving distance sorted list of
695 atoms
696 """
697
698 valid_kwargs = {'maxdist': 'maximum distance needed in the output'}
699 utilities.check_kwargs(kwargs, valid_kwargs, 'Crystal.environment')
700 maxdist = kwargs.get('maxdist', 7)
701
702 if len(pos) < 3:
703 pos = pos[0]
704 if len(pos) < 3:
705 raise InputError("need 3 coordinates of the "
706 "reference position")
707
708 refpos = self.a1 * \
709 pos[0] + self.a2 * pos[1] + self.a3 * pos[2]
710
711 lst = []
712 Na = 2 * int(ceil(maxdist / math.VecNorm(self.a1)))
713 Nb = 2 * int(ceil(maxdist / math.VecNorm(self.a2)))
714 Nc = 2 * int(ceil(maxdist / math.VecNorm(self.a3)))
715 if self.lattice.nsites > 0:
716 for a, p, o, b in self.lattice.base():
717 ucpos = (self.a1 * p[0] + self.a2 * p[1] + self.a3 * p[2])
718 for i in range(-Na, Na + 1):
719 for j in range(-Nb, Nb + 1):
720 for k in range(-Nc, Nc + 1):
721 atpos = ucpos + (self.a1 * i + self.a2 * j +
722 self.a3 * k)
723 distance = math.VecNorm(atpos - refpos)
724 if distance <= maxdist:
725 lst.append((distance, a, o))
726 else:
727 for i in range(-Na, Na + 1):
728 for j in range(-Nb, Nb + 1):
729 for k in range(-Nc, Nc + 1):
730 atpos = (self.a1 * i + self.a2 * j + self.a3 * k)
731 distance = math.VecNorm(atpos - refpos)
732 if distance <= maxdist:
733 lst.append((distance, '__dummy__', 1.))
734
735 # sort
736 lst.sort(key=operator.itemgetter(1))
737 lst.sort(key=operator.itemgetter(0))
738 rl = []
739 if len(lst) >= 1:
740 mult = lst[0][2]
741 else:
742 return rl
743 for i in range(1, len(lst)):
744 if (abs(lst[i - 1][0] - lst[i][0]) < config.EPSILON and
745 lst[i - 1][1] == lst[i][1]):
746 mult += lst[i - 1][2] # add occupancy
747 else:
748 rl.append((lst[i - 1][0], lst[i - 1][1], mult))
749 mult = lst[i][2]
750 rl.append((lst[-1][0], lst[-1][1], mult))
751
752 return rl
753
754 def planeDistance(self, *hkl):
755 """
756 determines the lattice plane spacing for the planes specified by (hkl)
757
758 Parameters
759 ----------
760 h, k, l : list, tuple or floats
761 Miller indices of the lattice planes given either as list, tuple or
762 seperate arguments
763
764 Returns
765 -------
766 float
767 the lattice plane spacing
768
769 Examples
770 --------
771 >>> xu.materials.Si.planeDistance(0, 0, 4)
772 1.3577600000000001
773
774 or
775
776 >>> xu.materials.Si.planeDistance((1, 1, 1))
777 3.1356124059796255
778 """
779 if len(hkl) < 3:
780 hkl = hkl[0]
781 if len(hkl) < 3:
782 raise InputError("need 3 indices for the lattice point")
783
784 return 2 * numpy.pi / math.VecNorm(self.Q(hkl))
785
786 def _getdensity(self):
787 """
788 calculates the mass density of an material from the mass of the atoms
789 in the unit cell.
790
791 Returns
792 -------
793 float
794 mass density in kg/m^3
795 """
796 m = 0.
797 for at, pos, occ, b in self.lattice.base():
798 m += at.weight * occ
799
800 return m / self.lattice.UnitCellVolume() * 1e30
801
802 density = property(_getdensity)
803
804 def _get_f(self, q, en):
805 """
806 optimized method to calculate the atomic scattering factor for all
807 atoms in the unit cell by calling the database only as much as needed.
808
809 Parameters
810 ----------
811 q : float or array-like
812 momentum transfer for which the atomic scattering factor should be
813 calculated
814 en : float or str
815 x-ray energy (eV)
816
817 Returns
818 -------
819 list
820 atomic scattering factors for every atom in the unit cell
821 """
822 f = {}
823 if self.lattice.nsites > 0:
824 for at, pos, occ, b in self.lattice.base():
825 if at.num not in f:
826 f[at.num] = at.f(q, en)
827 return [f[a.num] for a, p, o, b in self.lattice.base()]
828 else:
829 return None
830
831 def _get_lamen(self, en):
832 if isinstance(en, str) and en == 'config':
833 en = utilities.energy(config.ENERGY)
834 lam = utilities.en2lam(en)
835 return lam, en
836
837 def delta(self, en='config'):
838 """
839 function to calculate the real part of the deviation of the
840 refractive index from 1 (n=1-delta+i*beta)
841
842 Parameters
843 ----------
844 en : float or str, optional
845 x-ray energy eV, if omitted the value from the xrayutilities
846 configuration is used
847
848 Returns
849 -------
850 float
851 """
852 re = scipy.constants.physical_constants['classical electron radius'][0]
853 re *= 1e10
854
855 lam, en = self._get_lamen(en)
856 delta = 0.
857 f = self._get_f(0, en)
858 for (at, pos, occ, b), fa in zip(self.lattice.base(), f):
859 delta += numpy.real(fa) * occ
860
861 delta *= re / (2 * numpy.pi) * lam ** 2 / \
862 self.lattice.UnitCellVolume()
863 return delta
864
865 def ibeta(self, en='config'):
866 """
867 function to calculate the imaginary part of the deviation
868 of the refractive index from 1 (n=1-delta+i*beta)
869
870 Parameters
871 ----------
872 en : float or str, optional
873 x-ray energy eV, if omitted the value from the xrayutilities
874 configuration is used
875
876 Returns
877 -------
878 float
879 """
880 re = scipy.constants.physical_constants['classical electron radius'][0]
881 re *= 1e10
882
883 lam, en = self._get_lamen(en)
884 beta = 0.
885 f = self._get_f(0, en)
886 for (at, pos, occ, b), fa in zip(self.lattice.base(), f):
887 beta += numpy.imag(fa) * occ
888
889 beta *= re / (2 * numpy.pi) * lam ** 2 / self.lattice.UnitCellVolume()
890 return beta
891
892 def chi0(self, en='config'):
893 """
894 calculates the complex chi_0 values often needed in simulations.
895 They are closely related to delta and beta
896 (n = 1 + chi_r0/2 + i*chi_i0/2 vs. n = 1 - delta + i*beta)
897 """
898 re = scipy.constants.physical_constants['classical electron radius'][0]
899 re *= 1e10
900
901 lam, en = self._get_lamen(en)
902 beta = 0.
903 delta = 0.
904 if self.lattice.nsites > 0:
905 f = self._get_f(0, en)
906 for (at, pos, occ, b), f0 in zip(self.lattice.base(), f):
907 beta += numpy.imag(f0) * occ
908 delta += numpy.real(f0) * occ
909
910 v = self.lattice.UnitCellVolume()
911 beta *= re / (2 * numpy.pi) * lam ** 2 / v
912 delta *= re / (2 * numpy.pi) * lam ** 2 / v
913 return (-2 * delta + 2j * beta)
914
915 def _debyewallerfactor(self, temp, qnorm):
916 """
917 Calculate the Debye Waller temperature factor according to the Debye
918 temperature
919
920 Parameters
921 ----------
922 temp : float
923 actual temperature (K)
924 qnorm : float or array-like
925 norm of the q-vector(s) for which the factor should be calculated
926
927 Returns
928 -------
929 float or array-like
930 the Debye Waller factor(s) with the same shape as qnorm
931 """
932 if temp != 0 and self.thetaDebye:
933 # W(q) = 3/2* hbar^2*q^2/(m*kB*tD) * (D1(tD/T)/(tD/T) + 1/4)
934 # DWF = exp(-W(q)) consistent with Vaclav H. and several books
935 hbar = scipy.constants.hbar
936 kb = scipy.constants.Boltzmann
937 x = self.thetaDebye / float(temp)
938 m = 0.
939 im = 0
940 for a, p, o, b in self.lattice.base():
941 m += a.weight
942 im += 1
943 m = m / float(im)
944 exponentf = 3 / 2. * hbar ** 2 * 1.0e20 / \
945 (m * kb * self.thetaDebye) * (math.Debye1(x) / x + 0.25)
946 if config.VERBOSITY >= config.DEBUG:
947 print("XU.materials.Crystal: DWF = exp(-W*q**2) W= %g"
948 % exponentf)
949 dwf = numpy.exp(-exponentf * qnorm ** 2)
950 else:
951 dwf = 1.0
952 return dwf
953
954 def chih(self, q, en='config', temp=0, polarization='S'):
955 """
956 calculates the complex polarizability of a material for a certain
957 momentum transfer and energy
958
959 Parameters
960 ----------
961 q : list, tuple or array-like
962 momentum transfer vector in (1/A)
963 en : float or str, optional
964 x-ray energy eV, if omitted the value from the xrayutilities
965 configuration is used
966 temp : float, optional
967 temperature used for Debye-Waller-factor calculation
968 polarization : {'S', 'P'}, optional
969 sigma or pi polarization
970
971 Returns
972 -------
973 tuple
974 (abs(chih_real), abs(chih_imag)) complex polarizability
975 """
976
977 if isinstance(q, (list, tuple)):
978 q = numpy.array(q, dtype=numpy.double)
979 elif isinstance(q, numpy.ndarray):
980 pass
981 else:
982 raise TypeError("q must be a list or numpy array!")
983 qnorm = math.VecNorm(q)
984
985 if isinstance(en, str) and en == 'config':
986 en = utilities.energy(config.ENERGY)
987
988 if polarization not in ('S', 'P'):
989 raise ValueError("polarization must be 'S':sigma or 'P': pi!")
990
991 if self.lattice.nsites == 0:
992 return (0, 0)
993
994 dwf = self._debyewallerfactor(temp, qnorm)
995
996 sr = 0. + 0.j
997 si = 0. + 0.j
998 # a: atom, p: position, o: occupancy, b: temperature-factor
999 f = self._get_f(qnorm, en)
1000 for (a, p, o, b), F in zip(self.lattice.base(), f):
1001 r = self.lattice.GetPoint(p)
1002 if temp == 0:
1003 dwf = numpy.exp(-b * qnorm ** 2 / (4 * numpy.pi) ** 2)
1004 fr = numpy.real(F) * o
1005 fi = numpy.imag(F) * o
1006 sr += fr * numpy.exp(-1.j * math.VecDot(q, r)) * dwf
1007 si += fi * numpy.exp(-1.j * math.VecDot(q, r)) * dwf
1008
1009 # classical electron radius
1010 c = scipy.constants
1011 r_e = 1 / (4 * numpy.pi * c.epsilon_0) * c.e ** 2 / \
1012 (c.electron_mass * c.speed_of_light ** 2) * 1e10
1013 lam = utilities.en2lam(en)
1014
1015 fact = -lam ** 2 * r_e / (numpy.pi * self.lattice.UnitCellVolume())
1016 rchi = numpy.abs(fact * sr)
1017 ichi = numpy.abs(fact * si)
1018 if polarization == 'P':
1019 theta = numpy.arcsin(qnorm * utilities.en2lam(en) / (4*numpy.pi))
1020 rchi *= numpy.cos(2 * theta)
1021 ichi *= numpy.cos(2 * theta)
1022
1023 return rchi, ichi
1024
1025 def dTheta(self, Q, en='config'):
1026 """
1027 function to calculate the refractive peak shift
1028
1029 Parameters
1030 ----------
1031 Q : list, tuple or array-like
1032 momentum transfer vector (1/A)
1033 en : float or str, optional
1034 x-ray energy eV, if omitted the value from the xrayutilities
1035 configuration is used
1036
1037 Returns
1038 -------
1039 float
1040 peak shift in degree
1041 """
1042
1043 if isinstance(en, str) and en == 'config':
1044 en = utilities.energy(config.ENERGY)
1045 lam = utilities.en2lam(en)
1046 dth = numpy.degrees(
1047 2 * self.delta(en) / numpy.sin(2 * numpy.arcsin(
1048 lam * VecNorm(Q) / (4 * numpy.pi))))
1049 return dth
1050
1051 def __str__(self):
1052 ostr = super().__str__()
1053 ostr += "Lattice:\n"
1054 ostr += str(self.lattice)
1055 return ostr
1056
1057 def StructureFactor(self, q, en='config', temp=0):
1058 """
1059 calculates the structure factor of a material
1060 for a certain momentum transfer and energy
1061 at a certain temperature of the material
1062
1063 Parameters
1064 ----------
1065 q : list, tuple or array-like
1066 vectorial momentum transfer
1067 en : float or str, optional
1068 x-ray energy eV, if omitted the value from the xrayutilities
1069 configuration is used
1070 temp : float
1071 temperature used for Debye-Waller-factor calculation
1072
1073 Returns
1074 -------
1075 complex
1076 the complex structure factor
1077 """
1078
1079 if isinstance(q, (list, tuple)):
1080 q = numpy.array(q, dtype=numpy.double)
1081 elif isinstance(q, numpy.ndarray):
1082 pass
1083 else:
1084 raise TypeError("q must be a list or numpy array!")
1085
1086 if isinstance(en, str) and en == 'config':
1087 en = utilities.energy(config.ENERGY)
1088
1089 if self.lattice.nsites == 0:
1090 return 1.
1091
1092 qnorm = math.VecNorm(q)
1093 dwf = self._debyewallerfactor(temp, qnorm)
1094
1095 s = 0. + 0.j
1096 f = self._get_f(qnorm, en)
1097 # a: atom, p: position, o: occupancy, b: temperature-factor
1098 for (a, p, o, b), fq in zip(self.lattice.base(), f):
1099 r = self.lattice.GetPoint(p)
1100 if temp == 0:
1101 dwf = numpy.exp(-b * qnorm ** 2 /
1102 (4 * numpy.pi) ** 2)
1103 s += fq * o * numpy.exp(-1.j * math.VecDot(q, r)) * dwf
1104
1105 return s
1106
1107 def StructureFactorForEnergy(self, q0, en, temp=0):
1108 """
1109 calculates the structure factor of a material
1110 for a certain momentum transfer and a bunch of energies
1111
1112 Parameters
1113 ----------
1114 q0 : list, tuple or array-like
1115 vectorial momentum transfer
1116 en : list, tuple or array-like
1117 energy values in eV
1118 temp : float
1119 temperature used for Debye-Waller-factor calculation
1120
1121 Returns
1122 -------
1123 array-like
1124 complex valued structure factor array
1125 """
1126 if isinstance(q0, (list, tuple)):
1127 q = numpy.array(q0, dtype=numpy.double)
1128 elif isinstance(q0, numpy.ndarray):
1129 q = q0
1130 else:
1131 raise TypeError("q must be a list or numpy array!")
1132 qnorm = math.VecNorm(q)
1133
1134 if isinstance(en, (list, tuple)):
1135 en = numpy.array(en, dtype=numpy.double)
1136 elif isinstance(en, numpy.ndarray):
1137 pass
1138 else:
1139 raise TypeError("Energy data must be provided as a list "
1140 "or numpy array!")
1141
1142 if self.lattice.nsites == 0:
1143 return numpy.ones(len(en))
1144
1145 dwf = self._debyewallerfactor(temp, qnorm)
1146
1147 s = 0. + 0.j
1148 f = self._get_f(qnorm, en)
1149 # a: atom, p: position, o: occupancy, b: temperature-factor
1150 for (a, p, o, b), fq in zip(self.lattice.base(), f):
1151 if temp == 0:
1152 dwf = numpy.exp(-b * qnorm ** 2 / (4 * numpy.pi) ** 2)
1153 r = self.lattice.GetPoint(p)
1154 s += fq * o * dwf * numpy.exp(-1.j * math.VecDot(q, r))
1155
1156 return s
1157
1158 def StructureFactorForQ(self, q, en0='config', temp=0):
1159 """
1160 calculates the structure factor of a material
1161 for a bunch of momentum transfers and a certain energy
1162
1163 Parameters
1164 ----------
1165 q : list of vectors or array-like
1166 vectorial momentum transfers; list of vectores (list, tuple or
1167 array) of length 3
1168 e.g.: (Si.Q(0, 0, 4), Si.Q(0, 0, 4.1),...) or
1169 numpy.array([Si.Q(0, 0, 4), Si.Q(0, 0, 4.1)])
1170 en0 : float or str, optional
1171 x-ray energy eV, if omitted the value from the xrayutilities
1172 configuration is used
1173 temp : float
1174 temperature used for Debye-Waller-factor calculation
1175
1176 Returns
1177 -------
1178 array-like
1179 complex valued structure factor array
1180 """
1181 if isinstance(q, (list, tuple, numpy.ndarray)):
1182 q = numpy.asarray(q, dtype=numpy.double)
1183 else:
1184 raise TypeError("q must be a list or numpy array!")
1185 if len(q.shape) != 2:
1186 raise ValueError("q does not have the correct shape (shape = %s)"
1187 % str(q.shape))
1188 qnorm = numpy.linalg.norm(q, axis=1)
1189
1190 if isinstance(en0, str) and en0 == 'config':
1191 en0 = utilities.energy(config.ENERGY)
1192
1193 if self.lattice.nsites == 0:
1194 return numpy.ones(len(q))
1195
1196 dwf = self._debyewallerfactor(temp, qnorm)
1197
1198 s = 0. + 0.j
1199 f = self._get_f(qnorm, en0)
1200 # a: atom, p: position, o: occupancy, b: temperature-factor
1201 for (a, p, o, b), fq in zip(self.lattice.base(), f):
1202 if temp == 0:
1203 dwf = numpy.exp(-b * qnorm ** 2 / (4 * numpy.pi) ** 2)
1204
1205 r = self.lattice.GetPoint(p)
1206 s += fq * o * numpy.exp(-1.j * numpy.dot(q, r)) * dwf
1207
1208 return s
1209
1210 def ApplyStrain(self, strain):
1211 """
1212 Applies a certain strain on the lattice of the material. The result is
1213 a change in the base vectors of the real space as well as reciprocal
1214 space lattice. The full strain matrix (3x3) needs to be given.
1215
1216 Note:
1217 NO elastic response of the material will be considered!
1218 """
1219 # let strain act on the unit cell vectors
1220 self.lattice.ApplyStrain(strain)
1221
1222 def GetMismatch(self, mat):
1223 """
1224 Calculate the mismatch strain between the material and a second
1225 material
1226 """
1227 raise NotImplementedError("XU.material.GetMismatch: "
1228 "not implemented yet")
1229
1230 def distances(self):
1231 """
1232 function to obtain distances of atoms in the crystal up to the unit
1233 cell size (largest value of a, b, c is the cut-off)
1234
1235 returns a list of tuples with distance d and number of occurence n
1236 [(d1, n1), (d2, n2),...]
1237
1238 Note:
1239 if the base of the material is empty the list will be empty
1240 """
1241
1242 if self.lattice.nsites == 0:
1243 return []
1244
1245 cutoff = numpy.max((self.lattice.a, self.lattice.b, self.lattice.c))
1246
1247 tmp_data = []
1248
1249 for at1 in self.lattice.base():
1250 for at2 in self.lattice.base():
1251 dis = math.VecNorm(self.lattice.GetPoint(at1[1] - at2[1]))
1252 dis2 = math.VecNorm(self.lattice.GetPoint(
1253 at1[1] - at2[1] + numpy.array((1, 0, 0))))
1254 dis3 = math.VecNorm(self.lattice.GetPoint(
1255 at1[1] - at2[1] + numpy.array((0, 1, 0))))
1256 dis4 = math.VecNorm(self.lattice.GetPoint(
1257 at1[1] - at2[1] + numpy.array((0, 0, 1))))
1258 dis5 = math.VecNorm(self.lattice.GetPoint(
1259 at1[1] - at2[1] + numpy.array((-1, 0, 0))))
1260 dis6 = math.VecNorm(self.lattice.GetPoint(
1261 at1[1] - at2[1] + numpy.array((0, -1, 0))))
1262 dis7 = math.VecNorm(self.lattice.GetPoint(
1263 at1[1] - at2[1] + numpy.array((0, 0, -1))))
1264 distances = sorted([dis, dis2, dis3, dis4, dis5, dis6, dis7])
1265
1266 for dis in distances:
1267 if dis < cutoff:
1268 tmp_data.append(dis)
1269
1270 # sort the list and compress equal entries
1271 tmp_data.sort()
1272
1273 self._distances = [0]
1274 self._dis_hist = [0]
1275 for dis in tmp_data:
1276 if numpy.round(dis - self._distances[-1],
1277 int(abs(numpy.log10(config.EPSILON)))) == 0:
1278 self._dis_hist[-1] += 1
1279 else:
1280 self._distances.append(dis)
1281 self._dis_hist.append(1)
1282
1283 # create return value
1284 ret = []
1285 for i in range(len(self._distances)):
1286 ret.append((self._distances[i], self._dis_hist[i]))
1287
1288 return ret
1289
1290 def show_unitcell(self, fig=None, subplot=111, scale=0.6, complexity=11,
1291 linewidth=2):
1292 """
1293 primitive visualization of the unit cell using matplotlibs basic 3D
1294 functionality -> expect rendering inaccuracies!
1295
1296 Note:
1297 For more precise visualization export to CIF and use a proper
1298 crystal structure viewer.
1299
1300 Parameters
1301 ----------
1302 fig : matplotlib Figure or None, optional
1303 subplot : int or list, optional
1304 subplot to use for the visualization. This argument of fowarded to
1305 the first argument of matplotlibs `add_subplot` function
1306 scale : float, optional
1307 scale the size of the atoms by this additional factor. By default
1308 the size of the atoms corresponds to 60% of their atomic radius.
1309 complexity : int, optional
1310 number of steps to approximate the atoms as spheres. higher values
1311 cause significant slower plotting.
1312 linewidth : float, optional
1313 line thickness of the unit cell outline
1314 """
1315 plot, plt = utilities.import_matplotlib_pyplot('XU.materials')
1316 try:
1317 import mpl_toolkits.mplot3d
1318 except ImportError:
1319 plot = False
1320
1321 if not plot:
1322 print('matplotlib (including mplot3d) needed for show_unitcell()')
1323 return
1324
1325 if fig is None:
1326 fig = plt.figure()
1327 ax = fig.add_subplot(subplot, projection='3d')
1328
1329 phi, theta = numpy.mgrid[0:numpy.pi:1j*complexity,
1330 0:2*numpy.pi:1j*complexity]
1331
1332 for a, pos, occ, b in self.lattice.base():
1333 r = a.radius * scale
1334 for i in range(-1, 2):
1335 for j in range(-1, 2):
1336 for k in range(-1, 2):
1337 atpos = (pos + [i, j, k])
1338 inunitcell = True
1339 for l in range(3):
1340 if (atpos[l] < -config.EPSILON or
1341 atpos[l] > 1+config.EPSILON):
1342 inunitcell = False
1343 if inunitcell:
1344 vecpos = atpos[0]*self.a1 + atpos[1]*self.a2 +\
1345 atpos[2]*self.a3
1346 x = r*numpy.sin(phi)*numpy.cos(theta) + vecpos[0]
1347 y = r*numpy.sin(phi)*numpy.sin(theta) + vecpos[1]
1348 z = r*numpy.cos(phi) + vecpos[2]
1349 ax.plot_surface(x, y, z, rstride=1, cstride=1,
1350 color=a.color, alpha=occ,
1351 linewidth=0)
1352
1353 # plot unit cell outlines
1354 ax.plot([0, self.a1[0]], [0, self.a1[1]], [0, self.a1[2]], color='k',
1355 lw=linewidth)
1356 ax.plot([0, self.a2[0]], [0, self.a2[1]], [0, self.a2[2]], color='k',
1357 lw=linewidth)
1358 ax.plot([0, self.a3[0]], [0, self.a3[1]], [0, self.a3[2]], color='k',
1359 lw=linewidth)
1360 ax.plot([self.a1[0], self.a1[0]+self.a2[0]],
1361 [self.a1[1], self.a1[1]+self.a2[1]],
1362 [self.a1[2], self.a1[2]+self.a2[2]], color='k', lw=linewidth)
1363 ax.plot([self.a1[0], self.a1[0]+self.a3[0]],
1364 [self.a1[1], self.a1[1]+self.a3[1]],
1365 [self.a1[2], self.a1[2]+self.a3[2]], color='k', lw=linewidth)
1366 ax.plot([self.a2[0], self.a1[0]+self.a2[0]],
1367 [self.a2[1], self.a1[1]+self.a2[1]],
1368 [self.a2[2], self.a1[2]+self.a2[2]], color='k', lw=linewidth)
1369 ax.plot([self.a2[0], self.a2[0]+self.a3[0]],
1370 [self.a2[1], self.a2[1]+self.a3[1]],
1371 [self.a2[2], self.a2[2]+self.a3[2]], color='k', lw=linewidth)
1372 ax.plot([self.a3[0], self.a1[0]+self.a3[0]],
1373 [self.a3[1], self.a1[1]+self.a3[1]],
1374 [self.a3[2], self.a1[2]+self.a3[2]], color='k', lw=linewidth)
1375 ax.plot([self.a3[0], self.a2[0]+self.a3[0]],
1376 [self.a3[1], self.a2[1]+self.a3[1]],
1377 [self.a3[2], self.a2[2]+self.a3[2]], color='k', lw=linewidth)
1378 ax.plot([self.a1[0]+self.a2[0], self.a1[0]+self.a2[0]+self.a3[0]],
1379 [self.a1[1]+self.a2[1], self.a1[1]+self.a2[1]+self.a3[1]],
1380 [self.a1[2]+self.a2[2], self.a1[2]+self.a2[2]+self.a3[2]],
1381 color='k', lw=linewidth)
1382 ax.plot([self.a1[0]+self.a3[0], self.a1[0]+self.a2[0]+self.a3[0]],
1383 [self.a1[1]+self.a3[1], self.a1[1]+self.a2[1]+self.a3[1]],
1384 [self.a1[2]+self.a3[2], self.a1[2]+self.a2[2]+self.a3[2]],
1385 color='k', lw=linewidth)
1386 ax.plot([self.a2[0]+self.a3[0], self.a1[0]+self.a2[0]+self.a3[0]],
1387 [self.a2[1]+self.a3[1], self.a1[1]+self.a2[1]+self.a3[1]],
1388 [self.a2[2]+self.a3[2], self.a1[2]+self.a2[2]+self.a3[2]],
1389 color='k', lw=linewidth)
1390
1391 if parse_version(plt.matplotlib.__version__) < parse_version('3.1.0'):
1392 ax.set_aspect("equal")
1393
1394 if config.VERBOSITY >= config.INFO_LOW:
1395 warnings.warn("show_unitcell: 3D projection might appear distorted"
1396 "(limited 3D capabilities of matplotlib!). Use CIF "
1397 "export and other viewers for better visualization.")
1398 plt.tight_layout()
1399
1400
1401 def CubicElasticTensor(c11, c12, c44):
1402 """
1403 Assemble the 6x6 matrix of elastic constants for a cubic material from the
1404 three independent components of a cubic crystal
1405
1406 Parameters
1407 ----------
1408 c11, c12, c44 : float
1409 independent components of the elastic tensor of cubic materials
1410
1411 Returns
1412 -------
1413 cij : ndarray
1414 6x6 matrix with elastic constants
1415 """
1416 m = numpy.zeros((6, 6), dtype=numpy.double)
1417 m[0, 0] = c11
1418 m[1, 1] = c11
1419 m[2, 2] = c11
1420 m[3, 3] = c44
1421 m[4, 4] = c44
1422 m[5, 5] = c44
1423 m[0, 1] = m[0, 2] = c12
1424 m[1, 0] = m[1, 2] = c12
1425 m[2, 0] = m[2, 1] = c12
1426
1427 return m
1428
1429
1430 def HexagonalElasticTensor(c11, c12, c13, c33, c44):
1431 """
1432 Assemble the 6x6 matrix of elastic constants for a hexagonal material from
1433 the five independent components of a hexagonal crystal
1434
1435 Parameters
1436 ----------
1437 c11, c12, c13, c33, c44 : float
1438 independent components of the elastic tensor of a hexagonal material
1439
1440 Returns
1441 -------
1442 cij : ndarray
1443 6x6 matrix with elastic constants
1444 """
1445 m = numpy.zeros((6, 6), dtype=numpy.double)
1446 m[0, 0] = m[1, 1] = c11
1447 m[2, 2] = c33
1448 m[3, 3] = m[4, 4] = c44
1449 m[5, 5] = 0.5 * (c11 - c12)
1450 m[0, 1] = m[1, 0] = c12
1451 m[0, 2] = m[1, 2] = m[2, 0] = m[2, 1] = c13
1452
1453 return m
1454
1455
1456 def WZTensorFromCub(c11ZB, c12ZB, c44ZB):
1457 """
1458 Determines the hexagonal elastic tensor from the values of the cubic
1459 elastic tensor under the assumptions presented in Phys. Rev. B 6, 4546
1460 (1972), which are valid for the WZ <-> ZB polymorphs.
1461
1462 Parameters
1463 ----------
1464 c11, c12, c44 : float
1465 independent components of the elastic tensor of cubic materials
1466
1467 Returns
1468 -------
1469 cij : ndarray
1470 6x6 matrix with elastic constants
1471
1472 Implementation according to a patch submitted by Julian Stangl
1473 """
1474 # matrix conversions: cubic (111) to hexagonal (001) direction
1475 P = (1 / 6.) * numpy.array([[3, 3, 6],
1476 [2, 4, 8],
1477 [1, 5, -2],
1478 [2, 4, -4],
1479 [2, -2, 2],
1480 [1, -1, 4]])
1481 Q = (1 / (3 * numpy.sqrt(2))) * numpy.array([1, -1, -2])
1482
1483 cZBvec = numpy.array([c11ZB, c12ZB, c44ZB])
1484 cWZvec_BAR = numpy.dot(P, cZBvec)
1485 delta = numpy.dot(Q, cZBvec)
1486 D = numpy.array([delta**2 / cWZvec_BAR[2], 0, -delta**2 / cWZvec_BAR[2],
1487 0, delta**2 / cWZvec_BAR[0], delta**2 / cWZvec_BAR[2]])
1488 cWZvec = cWZvec_BAR - D.T
1489
1490 return HexagonalElasticTensor(cWZvec[0], cWZvec[2], cWZvec[3],
1491 cWZvec[1], cWZvec[4])
1492
1493
1494 class Alloy(Crystal):
1495 """
1496 alloys two materials from the same crystal system. If the materials have
1497 the same space group the Wyckoff positions within the unit cell will also
1498 reflect the alloying.
1499 """
1500
1501 def __init__(self, matA, matB, x):
1502 self.check_compatibility(matA, matB)
1503 lat = copy.deepcopy(matA.lattice)
1504 super().__init__("None", lat, matA.cij)
1505 self.matA = matA
1506 self.matB = matB
1507 self._setxb(x)
1508
1509 @staticmethod
1510 def check_compatibility(matA, matB):
1511 csA = matA.lattice.crystal_system.split(':')[0]
1512 csB = matB.lattice.crystal_system.split(':')[0]
1513 if csA != csB:
1514 raise InputError("Crystal systems of the two materials are "
1515 "incompatible!")
1516
1517 @staticmethod
1518 def lattice_const_AB(latA, latB, x, name=''):
1519 """
1520 method to calculated the interpolation of lattice parameters and unit
1521 cell angles of the Alloy. By default linear interpolation between the
1522 value of material A and B is performed.
1523
1524 Parameters
1525 ----------
1526 latA, latB : float or vector
1527 property (lattice parameter/angle) of material A and B. A property
1528 can be a scalar or vector.
1529 x : float
1530 fraction of material B in the alloy.
1531 name : str, optional
1532 label of the property which is interpolated. Can be 'a', 'b', 'c',
1533 'alpha', 'beta', or 'gamma'.
1534 """
1535 return (latB - latA) * x + latA
1536
1537 def _getxb(self):
1538 return self._xb
1539
1540 def _setxb(self, x):
1541 self._xb = x
1542 self.name = ("%s(%2.2f)%s(%2.2f)"
1543 % (self.matA.name, 1-x, self.matB.name, x))
1544 # modify the free parameters of the lattice
1545 for k in self.lattice.free_parameters:
1546 setattr(self.lattice, k,
1547 self.lattice_const_AB(getattr(self.matA, k),
1548 getattr(self.matB, k), x, name=k))
1549 # set elastic constants
1550 self.cij = (self.matB.cij - self.matA.cij) * x + self.matA.cij
1551 self.cijkl = (self.matB.cijkl - self.matA.cijkl) * x + self.matA.cijkl
1552 # alloying in unit cell
1553 if self.matA.lattice.space_group == self.matB.lattice.space_group:
1554 self.lattice._wbase = WyckoffBase()
1555 for a, wp, o, b in self.matA.lattice._wbase:
1556 self.lattice._wbase.append(a, wp, occ=o*(1-x), b=b)
1557 for a, wp, o, b in self.matB.lattice._wbase:
1558 if (a, wp, o, b) in self.lattice._wbase:
1559 idx = self.lattice._wbase.index((a, wp, o, b))
1560 occ = self.lattice._wbase[idx][2]
1561 self.lattice._wbase[idx] = (a, wp, occ+o*x, b)
1562 else:
1563 self.lattice._wbase.append(a, wp, occ=o*x, b=b)
1564
1565 x = property(_getxb, _setxb)
1566
1567 def _checkfinitenumber(self, arg, name=""):
1568 if isinstance(arg, numbers.Number) and numpy.isfinite(arg):
1569 return float(arg)
1570 else:
1571 raise TypeError("argument (%s) must be a scalar!" % name)
1572
1573 def _checkarray(self, arg, name=""):
1574 if isinstance(arg, (list, tuple, numpy.ndarray)):
1575 return numpy.asarray(arg, dtype=numpy.double)
1576 else:
1577 raise TypeError("argument (%s) must be of type "
1578 "list, tuple or numpy.ndarray" % name)
1579
1580 def _definehelpers(self, hkl, cijA, cijB):
1581 """
1582 define helper functions for solving the content from reciprocal space
1583 positions
1584 """
1585 def a1(x):
1586 return self.lattice_const_AB(self.matA.a1, self.matB.a1,
1587 x, name='a')
1588
1589 def a2(x):
1590 return self.lattice_const_AB(self.matA.a2, self.matB.a2,
1591 x, name='b')
1592
1593 def a3(x):
1594 return self.lattice_const_AB(self.matA.a3, self.matB.a3,
1595 x, name='c')
1596
1597 def V(x):
1598 return numpy.dot(a3(x), numpy.cross(a1(x), a2(x)))
1599
1600 def b1(x):
1601 return 2 * numpy.pi / V(x) * numpy.cross(a2(x), a3(x))
1602
1603 def b2(x):
1604 return 2 * numpy.pi / V(x) * numpy.cross(a3(x), a1(x))
1605
1606 def b3(x):
1607 return 2 * numpy.pi / V(x) * numpy.cross(a1(x), a2(x))
1608
1609 def qhklx(x):
1610 return hkl[0] * b1(x) + hkl[1] * b2(x) + hkl[2] * b3(x)
1611
1612 def frac(x):
1613 return ((cijB[0, 2] + cijB[1, 2] - (cijA[0, 2] + cijA[1, 2])) * x +
1614 (cijA[0, 2] + cijA[1, 2])) / \
1615 ((cijB[2, 2] - cijA[2, 2]) * x + cijA[2, 2])
1616
1617 return a1, a2, a3, V, b1, b2, b3, qhklx, frac
1618
1619 def RelaxationTriangle(self, hkl, sub, exp):
1620 """
1621 function which returns the relaxation triangle for a
1622 Alloy of given composition. Reciprocal space coordinates are
1623 calculated using the user-supplied experimental class
1624
1625 Parameters
1626 ----------
1627 hkl : list or array-like
1628 Miller Indices
1629 sub : Crystal, or float
1630 substrate material or lattice constant
1631 exp : Experiment
1632 object from which the Transformation object and ndir are needed
1633
1634 Returns
1635 -------
1636 qy, qz : float
1637 reciprocal space coordinates of the corners of the relaxation
1638 triangle
1639
1640 """
1641 hkl = self._checkarray(hkl, "hkl")
1642 trans = exp._transform
1643 ndir = exp.ndir / VecNorm(exp.ndir)
1644
1645 if isinstance(sub, Crystal):
1646 asub = sub.lattice.a
1647 elif isinstance(sub, float):
1648 asub = sub
1649 else:
1650 raise TypeError("Second argument (sub) must be of type float or "
1651 "an instance of xrayutilities.materials.Crystal")
1652
1653 # test if inplane direction of hkl is the same as the one for the
1654 # experiment otherwise warn the user
1655 hklinplane = VecCross(VecCross(exp.ndir, hkl), exp.ndir)
1656 if (VecNorm(VecCross(hklinplane, exp.idir)) > config.EPSILON):
1657 warnings.warn("Alloy: given hkl differs from the geometry of the "
1658 "Experiment instance in the azimuthal direction")
1659
1660 # transform elastic constants to correct coordinate frame
1661 cijA = Cijkl2Cij(trans(self.matA.cijkl, rank=4))
1662 cijB = Cijkl2Cij(trans(self.matB.cijkl, rank=4))
1663
1664 a1, a2, a3, V, b1, b2, b3, qhklx, frac = self._definehelpers(hkl,
1665 cijA,
1666 cijB)
1667
1668 qr_i = trans(qhklx(self.x))[1]
1669 qr_p = trans(qhklx(self.x))[2]
1670 qs_i = copysign(2*numpy.pi/asub * VecNorm(VecCross(ndir, hkl)), qr_i)
1671 qs_p = 2*numpy.pi/asub * abs(VecDot(ndir, hkl))
1672
1673 # calculate pseudomorphic points for A and B
1674 def abulk(x):
1675 return math.VecNorm(a1(x))
1676
1677 def aperp(x):
1678 return abulk(self.x) * (1 + frac(x) * (1 - asub / abulk(self.x)))
1679
1680 qp_i = copysign(2*numpy.pi/asub * VecNorm(VecCross(ndir, hkl)), qr_i)
1681 qp_p = 2*numpy.pi/aperp(self.x) * abs(VecDot(ndir, hkl))
1682
1683 # assembly return values
1684 qy = numpy.array([qr_i, qp_i, qs_i, qr_i], dtype=numpy.double)
1685 qz = numpy.array([qr_p, qp_p, qs_p, qr_p], dtype=numpy.double)
1686
1687 return qy, qz
1688
1689
1690 class CubicAlloy(Alloy):
1691
1692 def __init__(self, matA, matB, x):
1693 # here one could check if material is really cubic!!
1694 Alloy.__init__(self, matA, matB, x)
1695
1696 def ContentBsym(self, q_perp, hkl, inpr, asub, relax):
1697 """
1698 function that determines the content of B
1699 in the alloy from the reciprocal space position
1700 of a symetric peak. As an additional input the substrates
1701 lattice parameter and the degree of relaxation must be given
1702
1703 Parameters
1704 ----------
1705 q_perp : float
1706 perpendicular peak position of the reflection hkl of the alloy in
1707 reciprocal space
1708 hkl : list
1709 Miller indices of the measured symmetric reflection (also defines
1710 the surface normal
1711 inpr : list
1712 Miller indices of a Bragg peak defining the inplane reference
1713 direction
1714 asub : float
1715 substrate lattice parameter
1716 relax : float
1717 degree of relaxation (needed to obtain the content from symmetric
1718 reciprocal space position)
1719
1720 Returns
1721 -------
1722 content : float
1723 the content of B in the alloy determined from the input variables
1724
1725 """
1726
1727 # check input parameters
1728 q_perp = self._checkfinitenumber(q_perp, "q_perp")
1729 hkl = self._checkarray(hkl, "hkl")
1730 inpr = self._checkarray(inpr, "inpr")
1731 asub = self._checkfinitenumber(asub, "asub")
1732 relax = self._checkfinitenumber(relax, "relax")
1733
1734 # calculate lattice constants from reciprocal space positions
1735 n = self.Q(hkl) / VecNorm(self.Q(hkl))
1736 # the following line is not generally true! only cubic materials
1737 aperp = 2 * numpy.pi / q_perp * abs(VecDot(n, hkl))
1738
1739 # transform the elastic tensors to a coordinate frame attached to the
1740 # surface normal
1741 inp1 = VecCross(n, inpr) / VecNorm(VecCross(n, inpr))
1742 inp2 = VecCross(n, inp1)
1743 trans = math.CoordinateTransform(inp1, inp2, n)
1744
1745 if config.VERBOSITY >= config.DEBUG:
1746 print("XU.materials.Alloy.ContentB: inp1/inp2: ", inp1, inp2)
1747 cijA = Cijkl2Cij(trans(self.matA.cijkl, rank=4))
1748 cijB = Cijkl2Cij(trans(self.matB.cijkl, rank=4))
1749
1750 a1, a2, a3, V, b1, b2, b3, qhklx, frac = self._definehelpers(hkl,
1751 cijA,
1752 cijB)
1753
1754 # the following line is not generally true! only cubic materials
1755 def abulk_perp(x):
1756 return abs(2 * numpy.pi / numpy.inner(qhklx(x), n) *
1757 numpy.inner(n, hkl))
1758
1759 # can we use abulk_perp here? for cubic materials this should work?!
1760 def ainp(x):
1761 return asub + relax * (abulk_perp(x) - asub)
1762
1763 if config.VERBOSITY >= config.DEBUG:
1764 print("XU.materials.Alloy.ContentB: abulk_perp: %8.5g"
1765 % (abulk_perp(0.)))
1766
1767 def equation(x):
1768 return ((aperp - abulk_perp(x)) +
1769 (ainp(x) - abulk_perp(x)) * frac(x))
1770
1771 x = scipy.optimize.brentq(equation, -0.1, 1.1)
1772
1773 return x
1774
1775 def ContentBasym(self, q_inp, q_perp, hkl, sur):
1776 """
1777 function that determines the content of B
1778 in the alloy from the reciprocal space position
1779 of an asymmetric peak.
1780
1781 Parameters
1782 ----------
1783 q_inp : float
1784 inplane peak position of reflection hkl of the alloy in reciprocal
1785 space
1786 q_perp : float
1787 perpendicular peak position of the reflection hkl of the alloy in
1788 reciprocal space
1789 hkl : list
1790 Miller indices of the measured asymmetric reflection
1791 sur : list
1792 Miller indices of the surface (determines the perpendicular
1793 direction)
1794
1795 Returns
1796 -------
1797 content : float
1798 content of B in the alloy determined from the input variables
1799 list
1800 [a_inplane a_perp, a_bulk_perp(x), eps_inplane, eps_perp];
1801 lattice parameters calculated from the reciprocal space positions
1802 as well as the strain (eps) of the layer
1803 """
1804
1805 # check input parameters
1806 q_inp = self._checkfinitenumber(q_inp, "q_inp")
1807 q_perp = self._checkfinitenumber(q_perp, "q_perp")
1808 hkl = self._checkarray(hkl, "hkl")
1809 sur = self._checkarray(sur, "sur")
1810
1811 # check if reflection is asymmetric
1812 if math.VecNorm(math.VecCross(self.Q(hkl), self.Q(sur))) < 1.e-8:
1813 raise InputError("Miller indices of a symmetric reflection were"
1814 "given where an asymmetric reflection is needed")
1815
1816 # calculate lattice constants from reciprocal space positions
1817 n = self.Q(sur) / VecNorm(self.Q(sur))
1818 q_hkl = self.Q(hkl)
1819 # the following two lines are not generally true! only cubic materials
1820 ainp = 2 * numpy.pi / abs(q_inp) * VecNorm(VecCross(n, hkl))
1821 aperp = 2 * numpy.pi / abs(q_perp) * abs(VecDot(n, hkl))
1822
1823 # transform the elastic tensors to a coordinate frame attached to the
1824 # surface normal
1825 inp1 = VecCross(n, q_hkl) / VecNorm(VecCross(n, q_hkl))
1826 inp2 = VecCross(n, inp1)
1827 trans = math.CoordinateTransform(inp1, inp2, n)
1828
1829 cijA = Cijkl2Cij(trans(self.matA.cijkl, rank=4))
1830 cijB = Cijkl2Cij(trans(self.matB.cijkl, rank=4))
1831
1832 a1, a2, a3, V, b1, b2, b3, qhklx, frac = self._definehelpers(hkl,
1833 cijA,
1834 cijB)
1835
1836 # the following two lines are not generally true! only cubic materials
1837 def abulk_inp(x):
1838 return abs(2 * numpy.pi / numpy.inner(qhklx(x), inp2) *
1839 VecNorm(VecCross(n, hkl)))
1840
1841 def abulk_perp(x):
1842 return abs(2 * numpy.pi / numpy.inner(qhklx(x), n) *
1843 numpy.inner(n, hkl))
1844
1845 if config.VERBOSITY >= config.DEBUG:
1846 print("XU.materials.Alloy.ContentB: abulk_inp/perp: %8.5g %8.5g"
1847 % (abulk_inp(0.), abulk_perp(0.)))
1848
1849 def equation(x):
1850 return ((aperp - abulk_perp(x)) +
1851 (ainp - abulk_inp(x)) * frac(x))
1852
1853 x = scipy.optimize.brentq(equation, -0.1, 1.1)
1854
1855 eps_inplane = (ainp - abulk_perp(x)) / abulk_perp(x)
1856 eps_perp = (aperp - abulk_perp(x)) / abulk_perp(x)
1857
1858 return x, [ainp, aperp, abulk_perp(x), eps_inplane, eps_perp]
1859
1860
1861 def PseudomorphicMaterial(sub, layer, relaxation=0, trans=None):
1862 """
1863 This function returns a material whos lattice is pseudomorphic on a
1864 particular substrate material. The two materials must have similar unit
1865 cell definitions for the algorithm to work correctly, i.e. it does not work
1866 for combiniations of materials with different lattice symmetry. It is also
1867 crucial that the layer object includes values for the elastic tensor.
1868
1869 Parameters
1870 ----------
1871 sub : Crystal
1872 substrate material
1873 layer : Crystal
1874 bulk material of the layer, including its elasticity tensor
1875 relaxation : float, optional
1876 degree of relaxation 0: pseudomorphic, 1: relaxed (default: 0)
1877 trans : Tranform
1878 Transformation which transforms lattice directions into a surface
1879 orientated coordinate frame (x, y inplane, z out of plane). If None a
1880 (001) surface geometry of a cubic material is assumed.
1881
1882 Returns
1883 -------
1884 An instance of Crystal holding the new pseudomorphically
1885 strained material.
1886
1887 Raises
1888 ------
1889 InputError
1890 If the layer material has no elastic parameters
1891 """
1892 def get_inplane(lat):
1893 """determine inplane lattice parameter"""
1894 return (math.VecNorm(lat.GetPoint(trans.inverse((1, 0, 0)))) +
1895 math.VecNorm(lat.GetPoint(trans.inverse((0, 1, 0))))) / 2.
1896
1897 if not trans:
1898 trans = math.Transform(numpy.identity(3))
1899
1900 if numpy.all(layer.cijkl == 0):
1901 raise InputError("'layer' argument needs elastic parameters")
1902
1903 # calculate the strain
1904 asub = get_inplane(sub.lattice)
1905 abulk = get_inplane(layer.lattice)
1906 apar = asub + (abulk - asub) * relaxation
1907 epar = (apar - abulk) / abulk
1908 cT = trans(layer.cijkl, rank=4)
1909
1910 eperp = -epar * (cT[1, 1, 2, 2] + cT[2, 2, 0, 0]) / (cT[2, 2, 2, 2])
1911 eps = trans.inverse(numpy.diag((epar, epar, eperp)), rank=2)
1912 if config.VERBOSITY >= config.INFO_ALL:
1913 print("XU.materials.PseudomorphicMaterial: applying strain (inplane, "
1914 "perpendicular): %.4g %.4g" % (epar, eperp))
1915
1916 # create the pseudomorphic material
1917 pmlatt = copy.deepcopy(layer.lattice)
1918 pmat = Crystal(layer.name, pmlatt, layer.cij)
1919 pmat.ApplyStrain(eps)
1920 return pmat
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import math
18 from math import pi
19
20 import numpy
21
22 from .. import config, utilities
23 from ..math import VecNorm
24
25
26 def show_reciprocal_space_plane(
27 mat, exp, ttmax=None, maxqout=0.01, scalef=100, ax=None, color=None,
28 show_Laue=True, show_legend=True, projection='perpendicular',
29 label=None):
30 """
31 show a plot of the coplanar diffraction plane with peak positions for the
32 respective material. the size of the spots is scaled with the strength of
33 the structure factor
34
35 Parameters
36 ----------
37 mat: Crystal
38 instance of Crystal for structure factor calculations
39 exp: Experiment
40 instance of Experiment (needs to be HXRD, or FourC for onclick action
41 to work correctly). defines the inplane and out of plane direction as
42 well as the sample azimuth
43 ttmax: float, optional
44 maximal 2Theta angle to consider, by default 180deg
45 maxqout: float, optional
46 maximal out of plane q for plotted Bragg peaks as fraction of exp.k0
47 scalef: float, optional
48 scale factor for the marker size
49 ax: matplotlib.Axes, optional
50 matplotlib Axes to use for the plot, useful if multiple materials
51 should be plotted in one plot
52 color: matplotlib color, optional
53 show_Laue: bool, optional
54 flag to indicate if the Laue zones should be indicated
55 show_legend: bool, optional
56 flag to indiate if a legend should be shown
57 projection: 'perpendicular', 'polar', optional
58 type of projection for Bragg peaks which do not fall into the
59 diffraction plane. 'perpendicular' (default) uses only the inplane
60 component in the scattering plane, whereas 'polar' uses the vectorial
61 absolute value of the two inplane components. See also the 'maxqout'
62 option.
63 label: None or str, optional
64 label to be used for the legend. If 'None' the name of the material
65 will be used.
66
67 Returns
68 -------
69 Axes, plot_handle
70 """
71 def get_peaks(mat, exp, ttmax):
72 """
73 Parameters
74 ----------
75 mat: Crystal
76 instance of Crystal for structure factor calculations
77 exp: Experiment
78 instance of Experiment (likely HXRD, or FourC)
79 tt_cutoff: float
80 maximal 2Theta angle to consider, by default 180deg
81
82 Returns
83 -------
84 ndarray
85 data array with columns for 'q', 'qvec', 'hkl', 'r' for the Bragg
86 peaks
87 """
88 # calculate maximal Bragg indices
89 hma = int(math.ceil(VecNorm(mat.a1) * exp.k0 / pi *
90 math.sin(math.radians(ttmax / 2.))))
91 hmi = -hma
92 kma = int(math.ceil(VecNorm(mat.a2) * exp.k0 / pi *
93 math.sin(math.radians(ttmax / 2.))))
94 kmi = -kma
95 lma = int(math.ceil(VecNorm(mat.a3) * exp.k0 / pi *
96 math.sin(math.radians(ttmax / 2.))))
97 lmi = -lma
98
99 # calculate structure factors
100 qmax = 2 * exp.k0 * math.sin(math.radians(ttmax/2.))
101 hkl = numpy.mgrid[hma:hmi-1:-1,
102 kma:kmi-1:-1,
103 lma:lmi-1:-1].reshape(3, -1).T
104 q = mat.Q(hkl)
105 qnorm = VecNorm(q)
106 m = qnorm < qmax
107
108 data = numpy.zeros(numpy.sum(m), dtype=[('q', numpy.double),
109 ('qvec', numpy.ndarray),
110 ('r', numpy.double),
111 ('hkl', numpy.ndarray)])
112 data['q'] = qnorm[m]
113 data['qvec'] = list(exp.Transform(q[m]))
114 rref = abs(mat.StructureFactor((0, 0, 0), exp.energy)) ** 2
115 data['r'] = numpy.abs(mat.StructureFactorForQ(q[m], exp.energy)) ** 2
116 data['r'] /= rref
117 data['hkl'] = list(hkl[m])
118
119 return data
120
121 plot, plt = utilities.import_matplotlib_pyplot('XU.materials')
122
123 if not plot:
124 print('matplotlib needed for show_reciprocal_space_plane')
125 return
126
127 if ttmax is None:
128 ttmax = 180
129
130 d = get_peaks(mat, exp, ttmax)
131 k0 = exp.k0
132
133 if not ax:
134 fig = plt.figure(figsize=(9, 5))
135 ax = plt.subplot(111)
136 else:
137 fig = ax.get_figure()
138 plt.sca(ax)
139
140 plt.axis('scaled')
141 ax.set_autoscaley_on(False)
142 ax.set_autoscalex_on(False)
143 plt.xlim(-2.05*k0, 2.05*k0)
144 plt.ylim(-0.05*k0, 2.05*k0)
145
146 if show_Laue:
147 c = plt.matplotlib.patches.Circle((0, 0), 2*k0, facecolor='#FF9180',
148 edgecolor='none')
149 ax.add_patch(c)
150 qmax = 2 * k0 * math.sin(math.radians(ttmax/2.))
151 c = plt.matplotlib.patches.Circle((0, 0), qmax, facecolor='#FFFFFF',
152 edgecolor='none')
153 ax.add_patch(c)
154
155 c = plt.matplotlib.patches.Circle((0, 0), 2*k0, facecolor='none',
156 edgecolor='0.5')
157 ax.add_patch(c)
158 c = plt.matplotlib.patches.Circle((k0, 0), k0, facecolor='none',
159 edgecolor='0.5')
160 ax.add_patch(c)
161 c = plt.matplotlib.patches.Circle((-k0, 0), k0, facecolor='none',
162 edgecolor='0.5')
163 ax.add_patch(c)
164 plt.hlines(0, -2*k0, 2*k0, color='0.5', lw=0.5)
165 plt.vlines(0, -2*k0, 2*k0, color='0.5', lw=0.5)
166
167 # generate mask for plotting
168 m = numpy.zeros_like(d, dtype=numpy.bool)
169 for i, (q, r) in enumerate(zip(d['qvec'], d['r'])):
170 if (abs(q[0]) < maxqout*k0 and r > config.EPSILON):
171 m[i] = True
172
173 x = numpy.empty_like(d['r'][m])
174 y = numpy.empty_like(d['r'][m])
175 s = numpy.empty_like(d['r'][m])
176 for i, (qv, r) in enumerate(zip(d['qvec'][m], d['r'][m])):
177 if projection == 'perpendicular':
178 x[i] = qv[1]
179 else:
180 x[i] = numpy.sign(qv[1])*numpy.sqrt(qv[0]**2 + qv[1]**2)
181 y[i] = qv[2]
182 s[i] = r*scalef
183 label = label if label else mat.name
184 h = plt.scatter(x, y, s=s, zorder=2, label=label)
185 if color:
186 h.set_color(color)
187
188 plt.xlabel(r'$Q$ inplane ($\mathrm{\AA^{-1}}$)')
189 plt.ylabel(r'$Q$ out of plane ($\mathrm{\AA^{-1}}$)')
190
191 if show_legend:
192 if len(fig.legends) == 1:
193 fig.legends[0].remove()
194 fig.legend(*ax.get_legend_handles_labels(), loc='upper right')
195 plt.tight_layout()
196
197 annot = ax.annotate("", xy=(0, 0), xytext=(20, 20),
198 textcoords="offset points",
199 bbox=dict(boxstyle="round", fc="w"),
200 arrowprops=dict(arrowstyle="->"))
201 annot.set_visible(False)
202
203 def update_annot(ind):
204 pos = h.get_offsets()[ind["ind"][0]]
205 annot.xy = pos
206 text = "{}\n{}".format(mat.name,
207 str(d['hkl'][m][ind['ind'][0]]))
208 annot.set_text(text)
209 annot.get_bbox_patch().set_facecolor(h.get_facecolor()[0])
210 annot.get_bbox_patch().set_alpha(0.2)
211
212 def hover(event):
213 vis = annot.get_visible()
214 if event.inaxes == ax:
215 cont, ind = h.contains(event)
216 if cont:
217 update_annot(ind)
218 annot.set_visible(True)
219 fig.canvas.draw_idle()
220 else:
221 if vis:
222 annot.set_visible(False)
223 fig.canvas.draw_idle()
224
225 def click(event):
226 if event.inaxes == ax:
227 cont, ind = h.contains(event)
228 if cont:
229 popts = numpy.get_printoptions()
230 numpy.set_printoptions(precision=4, suppress=True)
231 angles = exp.Q2Ang(d['qvec'][m][ind['ind'][0]], trans=False,
232 geometry='real')
233 text = "{}\nhkl: {}\nangles: {}".format(
234 mat.name, str(d['hkl'][m][ind['ind'][0]]), str(angles))
235 numpy.set_printoptions(**popts)
236 print(text)
237
238 fig.canvas.mpl_connect("motion_notify_event", hover)
239 fig.canvas.mpl_connect("button_press_event", click)
240
241 return ax, h
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2013-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import numpy
18
19 from . import elements as e
20 from .heuslerlib import *
21 from .material import (Crystal, CubicAlloy, CubicElasticTensor,
22 HexagonalElasticTensor, WZTensorFromCub)
23 from .spacegrouplattice import SGLattice
24
25 # some predefined materials
26 # PLEASE use N/m^2 as unit for cij for newly entered material ( 1 dyn/cm^2 =
27 # 0.1 N/m^2 = 0.1 GPa)
28 # Use Kelvin as unit for the Debye temperature
29 # ref http://www.semiconductors.co.uk/propiviv5431.htm
30 C = Crystal("C", SGLattice('227:1', 3.5668, atoms=[e.C, ], pos=['8a', ]),
31 CubicElasticTensor(1.0764e12, 0.125e12, 0.577e12))
32 C_HOPG = Crystal('HOPG', SGLattice(194, 2.4612, 6.7079,
33 atoms=[e.C, e.C], pos=['2b', '2c']))
34 Si = Crystal("Si", SGLattice('227:1', 5.43104, atoms=[e.Si, ], pos=['8a', ]),
35 CubicElasticTensor(165.77e+9, 63.93e+9, 79.62e+9), thetaDebye=640)
36 Ge = Crystal("Ge", SGLattice('227:1', 5.65785, atoms=[e.Ge, ], pos=['8a', ]),
37 CubicElasticTensor(128.5e+9, 48.3e+9, 66.8e+9), thetaDebye=374)
38 InAs = Crystal("InAs", SGLattice(216, 6.0583, atoms=[e.In, e.As],
39 pos=['4a', '4c']),
40 CubicElasticTensor(8.34e+10, 4.54e+10, 3.95e+10),
41 thetaDebye=280)
42 InP = Crystal("InP", SGLattice(216, 5.8687, atoms=[e.In, e.P],
43 pos=['4a', '4c']),
44 CubicElasticTensor(10.11e+10, 5.61e+10, 4.56e+10),
45 thetaDebye=425)
46 InSb = Crystal("InSb", SGLattice(216, 6.47937, atoms=[e.In, e.Sb],
47 pos=['4a', '4c']),
48 CubicElasticTensor(6.66e+10, 3.65e+10, 3.02e+10),
49 thetaDebye=160)
50 GaP = Crystal("GaP", SGLattice(216, 5.4505, atoms=[e.Ga, e.P],
51 pos=['4a', '4c']),
52 CubicElasticTensor(14.05e+10, 6.20e+10, 7.03e+10),
53 thetaDebye=445)
54 GaAs = Crystal("GaAs", SGLattice(216, 5.65325, atoms=[e.Ga, e.As],
55 pos=['4a', '4c']),
56 CubicElasticTensor(11.9e+10, 5.34e+10, 5.96e+10),
57 thetaDebye=360)
58 AlAs = Crystal("AlAs", SGLattice(216, 5.6611, atoms=[e.Al, e.As],
59 pos=['4a', '4c']),
60 CubicElasticTensor(12.02e+10, 5.70e+10, 5.99e+10),
61 thetaDebye=446)
62 GaSb = Crystal("GaSb", SGLattice(216, 6.09593, atoms=[e.Ga, e.Sb],
63 pos=['4a', '4c']),
64 CubicElasticTensor(8.83e+10, 4.02e+10, 4.32e+10),
65 thetaDebye=266)
66 # from Cryst. Growth Des. 15, 4795-4803 (2015)
67 GaAsWZ = Crystal("GaAs(WZ)",
68 SGLattice(186, 3.9845, 6.5701, atoms=[e.Ga, e.As],
69 pos=[('2b', 0), ('2b', 3/8.)]),
70 WZTensorFromCub(11.9e+10, 5.34e+10, 5.96e+10))
71 GaAs4H = Crystal("GaAs(4H)",
72 SGLattice(186, 3.9900, 13.0964,
73 atoms=[e.Ga, e.Ga, e.As, e.As],
74 pos=[('2a', 0), ('2b', 1/4.),
75 ('2a', 3/16.), ('2b', 7/16.)]))
76 # from Phys. Rev. B 88, 115315 (2013)
77 GaPWZ = Crystal("GaP(WZ)",
78 SGLattice(186, 3.8419, 6.3353, atoms=[e.Ga, e.P],
79 pos=[('2b', 0), ('2b', 0.37385)]),
80 WZTensorFromCub(14.05e+10, 6.20e+10, 7.03e+10))
81 # from Nanotechnology 22 425704 (2011)
82 InPWZ = Crystal("InP(WZ)",
83 SGLattice(186, 4.1423, 6.8013, atoms=[e.In, e.P],
84 pos=[('2b', 0), ('2b', 3/8.)]),
85 WZTensorFromCub(10.11e+10, 5.61e+10, 4.56e+10))
86 # from Nano Lett., 2011, 11 (4), pp 1483-1489
87 InAsWZ = Crystal("InAs(WZ)",
88 SGLattice(186, 4.2742, 7.0250, atoms=[e.In, e.As],
89 pos=[('2b', 0), ('2b', 3/8.)]),
90 WZTensorFromCub(8.34e+10, 4.54e+10, 3.95e+10))
91 InAs4H = Crystal("InAs(4H)",
92 SGLattice(186, 4.2780, 14.0171,
93 atoms=[e.In, e.In, e.As, e.As],
94 pos=[('2a', 0), ('2b', 1/4.),
95 ('2a', 3/16.), ('2b', 7/16.)]))
96 InSbWZ = Crystal("InSb(WZ)",
97 SGLattice(186, 4.5712, 7.5221, atoms=[e.In, e.Sb],
98 pos=[('2b', 0), ('2b', 3/8.)]),
99 WZTensorFromCub(6.66e+10, 3.65e+10, 3.02e+10))
100 InSb4H = Crystal("InSb(4H)",
101 SGLattice(186, 4.5753, 15.0057,
102 atoms=[e.In, e.In, e.Sb, e.Sb],
103 pos=[('2a', 0), ('2b', 1/4.),
104 ('2a', 3/16.), ('2b', 7/16.)]))
105
106 # ? Unit of elastic constants for CdTe, PbTe, PbSe ?
107 PbTe = Crystal("PbTe",
108 SGLattice(225, 6.464, atoms=[e.Pb, e.Te], pos=['4a', '4b']),
109 CubicElasticTensor(93.6, 7.7, 13.4))
110 PbSe = Crystal("PbSe",
111 SGLattice(225, 6.128, atoms=[e.Pb, e.Se], pos=['4a', '4b']),
112 CubicElasticTensor(123.7, 19.3, 15.9))
113 CdTe = Crystal("CdTe", SGLattice(216, 6.482, atoms=[e.Cd, e.Te],
114 pos=['4a', '4c']),
115 CubicElasticTensor(53.5, 36.7, 19.9))
116 CdSe = Crystal("CdSe", SGLattice(186, 4.5712, 7.5221, atoms=[e.In, e.Sb],
117 pos=[('2b', 0), ('2b', 3/8.)]),
118 HexagonalElasticTensor(7.490e10, 4.609e10, 3.926e10,
119 8.451e10, 1.315e10))
120 CdSe_ZB = Crystal("CdSe(ZB)", SGLattice(216, 6.052, atoms=[e.Cd, e.Se],
121 pos=['4a', '4c']))
122 HgSe = Crystal("HgSe", SGLattice(216, 6.085, atoms=[e.Hg, e.Se],
123 pos=['4a', '4c']),
124 CubicElasticTensor(6.1e10, 4.4e10, 2.2e10))
125 NaCl = Crystal("NaCl",
126 SGLattice(225, 5.6402, atoms=[e.Na, e.Cl], pos=['4a', '4b']))
127 MgO = Crystal("MgO",
128 SGLattice(225, 4.212, atoms=[e.Mg, e.O], pos=['4a', '4b']))
129 GaN = Crystal("GaN",
130 SGLattice(186, 3.189, 5.186, atoms=[e.Ga, e.N],
131 pos=[('2b', 0), ('2b', 3/8.)]),
132 HexagonalElasticTensor(390.e9, 145.e9, 106.e9, 398.e9, 105.e9),
133 thetaDebye=600)
134 BaF2 = Crystal("BaF2", SGLattice(225, 6.2001, atoms=[e.Ba, e.F],
135 pos=['4a', '8c']))
136 SrF2 = Crystal("SrF2", SGLattice(225, 5.8007, atoms=[e.Sr, e.F],
137 pos=['4a', '8c']))
138 CaF2 = Crystal("CaF2", SGLattice(225, 5.4631, atoms=[e.Ca, e.F],
139 pos=['4a', '8c']))
140 MnO = Crystal("MnO", SGLattice(225, 4.444, atoms=[e.Mn, e.O],
141 pos=['4a', '4b']))
142 MnTe = Crystal("MnTe", SGLattice(186, 4.1429, 6.7031, atoms=[e.Mn, e.Te],
143 pos=[('2a', 0), ('2b', 0.25)]))
144 GeTe = Crystal("GeTe",
145 SGLattice('160:R', 5.996, 88.18, atoms=[e.Ge, e.Ge, e.Te, e.Te],
146 pos=[('1a', -0.237), ('3b', (0.5-0.237, -0.237)),
147 ('1a', 0.237), ('3b', (0.5+0.237, +0.237))]))
148 SnTe = Crystal("SnTe",
149 SGLattice(225, 6.3268, atoms=[e.Sn, e.Te], pos=['4a', '4b']))
150 Al = Crystal("Al", SGLattice(225, 4.04958, atoms=[e.Al, ], pos=['4a', ]))
151 Au = Crystal("Au", SGLattice(225, 4.0782, atoms=[e.Au, ], pos=['4a', ]))
152 Fe = Crystal("Fe", SGLattice(229, 2.8665, atoms=[e.Fe, ], pos=['2a', ]))
153 Cr = Crystal("Cr", SGLattice(229, 2.910, atoms=[e.Cr, ], pos=['2a', ]))
154 Co = Crystal("Co", SGLattice(194, 2.5071, 4.0695, atoms=[e.Co, ],
155 pos=['2c', ]))
156 Ti = Crystal("Ti", SGLattice(194, 2.9508, 4.6855, atoms=[e.Ti, ],
157 pos=['2c', ]))
158 Mo = Crystal("Mo", SGLattice(229, 3.147, atoms=[e.Mo, ], pos=['2a', ]))
159 Ru = Crystal("Ru", SGLattice(194, 2.7059, 4.2815, atoms=[e.Ru, ],
160 pos=['2c', ]))
161 Rh = Crystal("Rh", SGLattice(225, 3.8034, atoms=[e.Rh, ], pos=['4a', ]))
162 V = Crystal("V", SGLattice(229, 3.024, atoms=[e.V, ], pos=['2a', ]))
163 Ta = Crystal("Ta", SGLattice(229, 3.306, atoms=[e.Ta, ], pos=['2a', ]))
164 Nb = Crystal("Nb", SGLattice(229, 3.3004, atoms=[e.Nb, ], pos=['2a', ]))
165 Pt = Crystal("Pt", SGLattice(225, 3.9242, atoms=[e.Pt, ], pos=['4a', ]))
166 Ag2Se = Crystal("Ag2Se", SGLattice(19, 4.333, 7.062, 7.764,
167 atoms=[e.Ag, e.Ag, e.Se],
168 pos=[('4a', (0.107, 0.369, 0.456)),
169 ('4a', (0.728, 0.029, 0.361)),
170 ('4a', (0.358, 0.235, 0.149))]))
171 TiO2 = Crystal("TiO2", SGLattice(136, 4.59, 2.96, atoms=[e.Ti, e.O],
172 pos=['2a', ('4f', 0.30479)]))
173 MnO2 = Crystal("MnO2", SGLattice(136, 4.40, 2.87, atoms=[e.Mn, e.O],
174 pos=['2a', ('4f', 0.30479)]))
175 VO2_Rutile = Crystal("VO2", SGLattice(136, 4.55, 2.88, atoms=[e.V, e.O],
176 pos=['2a', ('4f', 0.305)]))
177 VO2_Baddeleyite = Crystal("VO2", SGLattice(14, 5.75, 5.42, 5.38, 122.6,
178 atoms=[e.V, e.O, e.O],
179 pos=[('4e', (0.242, 0.975, 0.025)),
180 ('4e', (0.1, 0.21, 0.20)),
181 ('4e', (0.39, 0.69, 0.29))]))
182 SiO2 = Crystal("SiO2", SGLattice(154, 4.916, 5.4054, atoms=[e.Si, e.O],
183 pos=[('3a', 0.46970),
184 ('6c', (0.41350, 0.26690,
185 0.11910+2/3.))]))
186 In = Crystal("In", SGLattice(139, 3.2523, 4.9461, atoms=[e.In, ],
187 pos=['2a', ]))
188 Sb = Crystal("Sb", SGLattice('166:H', 4.307, 11.273, atoms=[e.Sb, ],
189 pos=[('6c', 0.23349), ]))
190 Sn = Crystal("Sn", SGLattice('141:1', 5.8197, 3.17488, atoms=[e.Sn, ],
191 pos=['4a', ]))
192 Ag = Crystal("Ag", SGLattice(225, 4.0853, atoms=[e.Ag, ], pos=['4a', ]))
193 SnAlpha = Crystal("Sn-alpha", SGLattice('227:1', 6.4912, atoms=[e.Sn, ],
194 pos=['8a', ]))
195 Cu = Crystal("Cu", SGLattice(225, 3.61496, atoms=[e.Cu, ], pos=['4a', ]))
196 CaTiO3 = Crystal("CaTiO3", SGLattice(221, 3.795, atoms=[e.Ca, e.Ti, e.O],
197 pos=['1a', '1b', '3c']))
198 SrTiO3 = Crystal("SrTiO3", SGLattice(221, 3.905, atoms=[e.Sr, e.Ti, e.O],
199 pos=['1a', '1b', '3c']))
200 BaTiO3 = Crystal("BaTiO3", SGLattice(99, 3.992, 4.036,
201 atoms=[e.Ba, e.Ti, e.O, e.O],
202 pos=[('1a', 1.000), ('1b', 0.5274),
203 ('1b', 0.9993), ('2c', 0.5125)]))
204 # BiFeO3 = Crystal("BiFeO3", SGLattice())
205 # BiFeO3 = Crystal(
206 # "BiFeO3",
207 # lattice.PerovskiteTypeRhombohedral(elements.Bi, elements.Fe, elements.O,
208 # 3.965, 89.3))
209 FeO = Crystal("FeO", SGLattice(225, 4.332, atoms=[e.Fe, e.O],
210 pos=['4a', '4b']))
211 CoO = Crystal("CoO", SGLattice(225, 4.214, atoms=[e.Co, e.O],
212 pos=['4a', '4b']))
213 Fe3O4 = Crystal("Fe3O4", SGLattice('227:2', 8.3958, atoms=[e.Fe, e.Fe, e.O],
214 pos=['8a', '16d', ('32e', 0.255)]))
215 Co3O4 = Crystal("Co3O4", SGLattice('227:2', 8.0821, atoms=[e.Co, e.Co, e.O],
216 pos=['8a', '16d', ('32e', 0.255)]))
217 FeRh = Crystal("FeRh", SGLattice(221, 2.993, atoms=[e.Fe, e.Rh],
218 pos=['1a', '1b']))
219 Ir20Mn80 = Crystal("Ir20Mn80", SGLattice(225, 3.780, atoms=[e.Ir, e.Mn],
220 pos=['4a', '4a'], occ=[0.2, 0.8]))
221 CoFe = Crystal("CoFe", SGLattice(221, 2.8508, atoms=[e.Co, e.Fe],
222 pos=['1a', '1b']))
223 CoGa = Crystal("CoGa", SGLattice(221, 2.883, atoms=[e.Co, e.Ga],
224 pos=['1a', '1b']))
225 LaB6 = Crystal("LaB6", SGLattice(221, 4.15692, atoms=[e.La, e.B],
226 pos=['1a', ('6f', 0.19750)]))
227 Al2O3 = Crystal("Al2O3", SGLattice('167:H', 4.7602, 12.9933,
228 atoms=[e.Al, e.O],
229 pos=[('12c', 0.35216), ('18e', 0.30624)]))
230 CuMnAs = Crystal("CuMnAs", SGLattice('129:2', 3.8200, 6.3180,
231 atoms=[e.Cu, e.Mn, e.As],
232 pos=['2b', ('2c', -0.8300),
233 ('2c', -0.2347)],
234 occ=[1, 0.86, 0.96],
235 b=[3.5531, 1.8950, 1.2633]))
236 Mn3Ge_cub = Crystal("Mn3Ge (cub)", SGLattice('221', 3.8019,
237 atoms=[e.Mn, e.Ge],
238 pos=['3c', '1a']))
239 Mn3Ge = Crystal("Mn3Ge (hex)", SGLattice('194', 5.34, 4.31,
240 atoms=[e.Mn, e.Ge],
241 pos=[('6h', 1/6.), '2d']))
242 Pt3Cr = Crystal("Pt3Cr", SGLattice('221', 3.876,
243 atoms=[e.Pt, e.Cr], pos=['3c', '1a']))
244 TiN = Crystal("TiN",
245 SGLattice(225, 4.235, atoms=[e.Ti, e.N], pos=['4a', '4b']))
246
247
248 # Alloys with special properties
249 class SiGe(CubicAlloy):
250
251 def __init__(self, x):
252 """
253 Si_{1-x} Ge_x cubic compound
254 """
255 super().__init__(Si, Ge, x)
256
257 @staticmethod
258 def lattice_const_AB(latA, latB, x, **kwargs):
259 """
260 method to calculate the lattice parameter of the SiGe alloy with
261 composition Si_{1-x}Ge_x
262 """
263 return latA + (0.2 * x + 0.027 * x ** 2) * \
264 (latB - latA) / numpy.linalg.norm(latB - latA)
265
266
267 class AlGaAs(CubicAlloy):
268
269 def __init__(self, x):
270 """
271 Al_{1-x} Ga_x As cubic compound
272 """
273 super().__init__(AlAs, GaAs, x)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2017-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16 """
17 module handling crystal lattice structures. A SGLattice consists of a space
18 group number and the position of atoms specified as Wyckoff positions along
19 with their parameters. Depending on the space group symmetry only certain
20 parameters of the resulting instance will be settable! A cubic lattice for
21 example allows only to set its 'a' lattice parameter but none of the other unit
22 cell shape parameters.
23 """
24
25 import copy
26 import numbers
27 from collections import OrderedDict
28 from math import cos, radians, sin, sqrt
29
30 import numpy
31
32 from .. import math, utilities
33 from ..exception import InputError
34 from . import elements
35 from .atom import Atom
36 from .wyckpos import wp
37
38
39 class RangeDict(dict):
40 def __getitem__(self, item):
41 if type(item) != range:
42 for key in self:
43 if item in key:
44 return self[key]
45 else:
46 return super().__getitem__(item)
47
48
49 # space group number to symmetry and number of parameters dictionary
50 sgrp_sym = RangeDict({range(1, 3): ('triclinic', 6),
51 range(3, 16): ('monoclinic', 4),
52 range(16, 75): ('orthorhombic', 3),
53 range(75, 143): ('tetragonal', 2),
54 range(143, 168): ('trigonal', 2),
55 range(168, 195): ('hexagonal', 2),
56 range(195, 231): ('cubic', 1)})
57
58 sgrp_name = {'1': 'P1', '2': 'P-1', '3': 'P2', '4': 'P21', '5': 'C2',
59 '6': 'Pm', '7': 'Pc', '8': 'Cm', '9': 'Cc', '10': 'P2/m',
60 '11': 'P21/m', '12': 'C2/m', '13': 'P2/c', '14': 'P21/c',
61 '15': 'C2/c', '16': 'P222', '17': 'P2221', '18': 'P21212',
62 '19': 'P212121', '20': 'C2221', '21': 'C222', '22': 'F222',
63 '23': 'I222', '24': 'I212121', '25': 'Pmm2', '26': 'Pmc21',
64 '27': 'Pcc2', '28': 'Pma2', '29': 'Pca21', '30': 'Pnc2',
65 '31': 'Pmn21', '32': 'Pba2', '33': 'Pna21', '34': 'Pnn2',
66 '35': 'Cmm2', '36': 'Cmc21', '37': 'Ccc2', '38': 'Amm2',
67 '39': 'Aem2', '40': 'Ama2', '41': 'Aea2', '42': 'Fmm2',
68 '43': 'Fdd2', '44': 'Imm2', '45': 'Iba2', '46': 'Ima2',
69 '47': 'Pmmm', '48': 'Pnnn', '49': 'Pccm', '50': 'Pban',
70 '51': 'Pmma', '52': 'Pnna', '53': 'Pmna', '54': 'Pcca',
71 '55': 'Pbam', '56': 'Pccn', '57': 'Pbcm', '58': 'Pnnm',
72 '59': 'Pmmn', '60': 'Pbcn', '61': 'Pbca', '62': 'Pnma',
73 '63': 'Cmcm', '64': 'Cmce', '65': 'Cmmm', '66': 'Cccm',
74 '67': 'Cmme', '68': 'Ccce', '69': 'Fmmm', '70': 'Fddd',
75 '71': 'Immm', '72': 'Ibam', '73': 'Ibca', '74': 'Imma',
76 '75': 'P4', '76': 'P41', '77': 'P42', '78': 'P43',
77 '79': 'I4', '80': 'I41', '81': 'P-4', '82': 'I-4',
78 '83': 'P4/m', '84': 'P42/m', '85': 'P4/n', '86': 'P42/n',
79 '87': 'I4/m', '88': 'I41/a', '89': 'P422', '90': 'P4212',
80 '91': 'P4122', '92': 'P41212', '93': 'P4222', '94': 'P42212',
81 '95': 'P4322', '96': 'P43212', '97': 'I422', '98': 'I4122',
82 '99': 'P4mm', '100': 'P4bm', '101': 'P42cm', '102': 'P42nm',
83 '103': 'P4cc', '104': 'P4nc', '105': 'P42mc', '106': 'P42bc',
84 '107': 'I4mm', '108': 'I4cm', '109': 'I41md', '110': 'I41cd',
85 '111': 'P-42m', '112': 'P-42c', '113': 'P-421m',
86 '114': 'P-421c', '115': 'P-4m2', '116': 'P-4c2',
87 '117': 'P-4b2', '118': 'P-4n2', '119': 'I-4m2',
88 '120': 'I-4c2', '121': 'I-42m', '122': 'I-42d',
89 '123': 'P4/mmm', '124': 'P4/mcc', '125': 'P4/nbm',
90 '126': 'P4/nnc', '127': 'P4/mbm', '128': 'P4/mnc',
91 '129': 'P4/nmm', '130': 'P4/ncc', '131': 'P42/mmc',
92 '132': 'P42/mcm', '133': 'P42/nbc', '134': 'P42/nnm',
93 '135': 'P42/mbc', '136': 'P42/mnm', '137': 'P42/nmc',
94 '138': 'P42/ncm', '139': 'I4/mmm', '140': 'I4/mcm',
95 '141': 'I41/amd', '142': 'I41/acd', '143': 'P3',
96 '144': 'P31', '145': 'P32', '146': 'R3', '147': 'P-3',
97 '148': 'R-3', '149': 'P312', '150': 'P321', '151': 'P3112',
98 '152': 'P3121', '153': 'P3212', '154': 'P3221', '155': 'R32',
99 '156': 'P3m1', '157': 'P31m', '158': 'P3c1', '159': 'P31c',
100 '160': 'R3m', '161': 'R3c', '162': 'P-31m', '163': 'P-31c',
101 '164': 'P-3m1', '165': 'P-3c1', '166': 'R-3m', '167': 'R-3c',
102 '168': 'P6', '169': 'P61', '170': 'P65', '171': 'P62',
103 '172': 'P64', '173': 'P63', '174': 'P-6', '175': 'P6/m',
104 '176': 'P63/m', '177': 'P622', '178': 'P6122',
105 '179': 'P6522', '180': 'P6222', '181': 'P6422',
106 '182': 'P6322', '183': 'P6mm', '184': 'P6cc', '185': 'P63cm',
107 '186': 'P63mc', '187': 'P-6m2', '188': 'P-6c2',
108 '189': 'P-62m', '190': 'P-62c', '191': 'P6/mmm',
109 '192': 'P6/mcc', '193': 'P63/mcm', '194': 'P63/mmc',
110 '195': 'P23', '196': 'F23', '197': 'I23', '198': 'P213',
111 '199': 'I213', '200': 'Pm-3', '201': 'Pn-3', '202': 'Fm-3',
112 '203': 'Fd-3', '204': 'Im-3', '205': 'Pa-3', '206': 'Ia-3',
113 '207': 'P432', '208': 'P4232', '209': 'F432',
114 '210': 'F4132', '211': 'I432', '212': 'P4332',
115 '213': 'P4132', '214': 'I4132', '215': 'P-43m',
116 '216': 'F-43m', '217': 'I-43m', '218': 'P-43n',
117 '219': 'F-43c', '220': 'I-43d', '221': 'Pm-3m',
118 '222': 'Pn-3n', '223': 'Pm-3n', '224': 'Pn-3m',
119 '225': 'Fm-3m', '226': 'Fm-3c', '227': 'Fd-3m',
120 '228': 'Fd-3c', '229': 'Im-3m', '230': 'Ia-3d'}
121
122 sgrp_params = {'cubic:1': (('a', ), ('a', 'a', 'a', 90, 90, 90)),
123 'cubic:2': (('a', ), ('a', 'a', 'a', 90, 90, 90)),
124 'cubic': (('a', ), ('a', 'a', 'a', 90, 90, 90)),
125 'hexagonal': (('a', 'c'), ('a', 'a', 'c', 90, 90, 120)),
126 'trigonal:R': (('a', 'alpha'), ('a', 'a', 'a', 'alpha',
127 'alpha', 'alpha')),
128 'trigonal:H': (('a', 'c'), ('a', 'a', 'c', 90, 90, 120)),
129 'trigonal': (('a', 'c'), ('a', 'a', 'c', 90, 90, 120)),
130 'tetragonal:1': (('a', 'c'), ('a', 'a', 'c', 90, 90, 90)),
131 'tetragonal:2': (('a', 'c'), ('a', 'a', 'c', 90, 90, 90)),
132 'tetragonal': (('a', 'c'), ('a', 'a', 'c', 90, 90, 90)),
133 'orthorhombic:1': (('a', 'b', 'c'),
134 ('a', 'b', 'c', 90, 90, 90)),
135 'orthorhombic:2': (('a', 'b', 'c'),
136 ('a', 'b', 'c', 90, 90, 90)),
137 'orthorhombic': (('a', 'b', 'c'),
138 ('a', 'b', 'c', 90, 90, 90)),
139 'monoclinic:b': (('a', 'b', 'c', 'beta'),
140 ('a', 'b', 'c', 90, 'beta', 90)),
141 'monoclinic:c': (('a', 'b', 'c', 'gamma'),
142 ('a', 'b', 'c', 90, 90, 'gamma')),
143 'monoclinic': (('a', 'b', 'c', 'beta'),
144 ('a', 'b', 'c', 90, 'beta', 90)),
145 'triclinic': (('a', 'b', 'c', 'alpha', 'beta', 'gamma'),
146 ('a', 'b', 'c', 'alpha', 'beta', 'gamma'))}
147
148
149 def get_possible_sgrp_suf(sgrp_nr):
150 """
151 determine possible space group suffix. Multiple suffixes might be possible
152 for one space group due to different origin choice, unique axis, or choice
153 of the unit cell shape.
154
155 Parameters
156 ----------
157 sgrp_nr : int
158 space group number
159
160 Returns
161 -------
162 str or list
163 either an empty string or a list of possible valid suffix strings
164 """
165 sgrp_suf = ''
166 if sgrp_nr in [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]:
167 sgrp_suf = [':b', ':c']
168 elif sgrp_nr in [48, 50, 59, 68, 70, 85, 86, 88, 125, 126,
169 129, 130, 133, 134, 137, 138, 141, 142,
170 201, 203, 222, 224, 227, 228]:
171 sgrp_suf = [':1', ':2']
172 elif sgrp_nr in [146, 148, 155, 160, 161, 166, 167]:
173 sgrp_suf = [':H', ':R']
174 return sgrp_suf
175
176
177 def get_default_sgrp_suf(sgrp_nr):
178 """
179 determine default space group suffix
180 """
181 possibilities = get_possible_sgrp_suf(sgrp_nr)
182 if possibilities:
183 return possibilities[0]
184 else:
185 return ''
186
187
188 class WyckoffBase(list):
189
190 """
191 The WyckoffBase class implements a container for a set of Wyckoff positions
192 that form the base of a crystal lattice. An instance of this class can be
193 treated as a simple container object.
194 """
195
196 def __init__(self, *args, **kwargs):
197 list.__init__(self, *args, **kwargs)
198
199 @staticmethod
200 def _checkatom(atom):
201 if isinstance(atom, str):
202 atom = getattr(elements, atom)
203 elif not isinstance(atom, Atom):
204 raise TypeError("atom must be an instance of class "
205 "xrayutilities.materials.Atom")
206 return atom
207
208 @staticmethod
209 def _checkpos(pos):
210 if isinstance(pos, str):
211 pos = (pos, None)
212 elif isinstance(pos, (tuple, list)):
213 if len(pos) == 1:
214 pos = (pos[0], None)
215 elif len(pos) == 2:
216 if isinstance(pos[1], numbers.Number):
217 pos = (pos[0], (pos[1], ))
218 elif pos[1] is None:
219 pos = (pos[0], None)
220 else:
221 pos = (pos[0], tuple(pos[1]))
222 else:
223 if isinstance(pos[1], numbers.Number):
224 pos = (pos[0], tuple(pos[1:]))
225 return pos
226
227 def append(self, atom, pos, occ=1.0, b=0.):
228 """
229 add new Atom to the lattice base
230
231 Parameters
232 ----------
233 atom : Atom
234 object to be added
235 pos : tuple or str
236 Wyckoff position of the atom, along with its parameters.
237 Examples: ('2i', (0.1, 0.2, 0.3)), or '1a'
238 occ : float, optional
239 occupancy (default=1.0)
240 b : float, optional
241 b-factor of the atom used as exp(-b*q**2/(4*pi)**2) to reduce the
242 intensity of this atom (only used in case of temp=0 in
243 StructureFactor and chi calculation)
244 """
245 atom = self._checkatom(atom)
246 pos = self._checkpos(pos)
247 list.append(self, (atom, pos, occ, b))
248
249 def __setitem__(self, key, data):
250 (atom, pos, occ, b) = data
251 atom = self._checkatom(atom)
252 pos = self._checkpos(pos)
253 list.__setitem__(self, key, (atom, pos, float(occ), float(b)))
254
255 def __copy__(self):
256 """
257 since we use a custom 'append' method we need to overwrite copy
258 """
259 cls = self.__class__
260 new = cls.__new__(cls)
261 for item in self:
262 new.append(*item)
263 return new
264
265 def __deepcopy__(self, memo):
266 cls = self.__class__
267 new = cls.__new__(cls)
268 memo[id(self)] = new
269 for item in self:
270 citem = copy.deepcopy(item, memo)
271 new.append(*citem)
272 return new
273
274 def __str__(self):
275 ostr = ''
276 for i, (atom, p, occ, b) in enumerate(self):
277 ostr += '%d: %s %s ' % (i, str(atom), p[0])
278 if p[1] is not None:
279 ostr += ' '.join(['%.4f' % v for v in p[1]])
280 ostr += ' occ=%5.3f b=%5.3f\n' % (occ, b)
281 return ostr
282
283 def __contains__(self, item):
284 """
285 check if this list contains already the same element, at the same
286 position, and with the same Debye Waller factor. The occupancy is not
287 checked intentionally.
288
289 Parameters
290 ----------
291 item : tuple or list
292 WyckoffBase entry to check if its present in this list
293
294 Returns
295 -------
296 bool
297 """
298 for atom, p, occ, b in self:
299 if (atom == item[0] and self.pos_eq(p, item[1]) and
300 numpy.isclose(b, item[3], atol=1e-4)):
301 return True
302 return False
303
304 @staticmethod
305 def entry_eq(e1, e2):
306 """
307 compare two entries including all its properties to be equal
308
309 Parameters
310 ----------
311 e1, e2: tuple
312 tuples with length 4 containing the entries of WyckoffBase which
313 should be compared
314 """
315 if (e1[0] == e2[0] and WyckoffBase.pos_eq(e1[1], e2[1]) and
316 numpy.all(numpy.isclose(e1[2:], e2[2:], atol=1e-4))):
317 return True
318 return False
319
320 @staticmethod
321 def pos_eq(pos1, pos2):
322 """
323 compare Wyckoff positions
324
325 Parameters
326 ----------
327 pos1, pos2: tuple
328 tuples with Wyckoff label and optional parameters
329 """
330 if pos1[0] != pos2[0]:
331 return False
332 if pos1[1] == pos2[1]:
333 return True
334 else:
335 for f1, f2 in zip(pos1[1], pos2[1]):
336 if not numpy.isclose(f1 % 1, f2 % 1, atol=1e-5):
337 return False
338 return True
339
340 def index(self, item):
341 """
342 return the index of the atom (same element, position, and Debye Waller
343 factor). The occupancy is not checked intentionally. If the item is not
344 present a ValueError is raised.
345
346 Parameters
347 ----------
348 item : tuple or list
349 WyckoffBase entry
350
351 Returns
352 -------
353 int
354 """
355 for i, (atom, p, occ, b) in enumerate(self):
356 if (atom == item[0] and self.pos_eq(p, item[1]) and
357 numpy.isclose(b, item[3], atol=1e-4)):
358 return i
359 raise ValueError("%s is not in list" % str(item))
360
361
362 class SGLattice(object):
363 """
364 lattice object created from the space group number and corresponding unit
365 cell parameters. atoms in the unit cell are specified by their Wyckoff
366 position and their free parameters.
367
368 this replaces the deprecated Lattice class
369 """
370
371 def __init__(self, sgrp, *args, **kwargs):
372 """
373 initialize class with space group number and atom list
374
375 Parameters
376 ----------
377 sgrp : int or str
378 Space group number
379 *args : float
380 space group parameters. depending on the space group number this
381 are 1 (cubic) to 6 (triclinic) parameters.
382 cubic : a (lattice parameter).
383 hexagonal : a, c.
384 trigonal : a, c.
385 tetragonal : a, c.
386 orthorhombic : a, b, c.
387 monoclinic : a, b, c, beta (in degree).
388 triclinic : a, b, c, alpha, beta, gamma (in degree).
389 atoms : list, optional
390 list of elements either as Element object or string with the
391 element name. If you specify atoms you have to also give the same
392 number of Wyckoff positions
393 pos : list, optional
394 list of the atoms Wyckoff positions along with its parameters. If a
395 position has no free parameter the parameters can be omitted.
396 Example: [('2i', (0.1, 0.2, 0.3)), '1a']
397 occ : list, optional
398 site occupation for the atoms. This is optional and defaults to 1
399 if not given.
400 b : list, optional
401 b-factor of the atom used as exp(-b*q**2/(4*pi)**2) to reduce the
402 intensity of this atom (only used in case of temp=0 in
403 StructureFactor and chi calculation)
404 """
405 valid_kwargs = {'atoms': 'list of elements',
406 'pos': 'list of Wyckoff positions',
407 'occ': 'site occupations',
408 'b': 'Debye Waller exponents'}
409 utilities.check_kwargs(kwargs, valid_kwargs, self.__class__.__name__)
410 self.space_group = str(sgrp)
411 self.space_group_nr = int(self.space_group.split(':')[0])
412 try:
413 self.space_group_suf = ':' + self.space_group.split(':')[1]
414 except IndexError:
415 self.space_group_suf = get_default_sgrp_suf(self.space_group_nr)
416
417 if self.space_group_suf != '':
418 self.space_group = str(self.space_group_nr) + self.space_group_suf
419 self.name = sgrp_name[str(self.space_group_nr)] + self.space_group_suf
420 self.crystal_system, nargs = sgrp_sym[self.space_group_nr]
421 self.crystal_system += self.space_group_suf
422 if len(args) != nargs:
423 raise ValueError('XU: number of parameters (%d) does not match the'
424 ' crystal symmetry (%s:%d)'
425 % (len(args), self.crystal_system, nargs))
426 self.free_parameters = OrderedDict()
427 for a, par in zip(args, sgrp_params[self.crystal_system][0]):
428 self.free_parameters[par] = a
429
430 self._parameters = OrderedDict()
431 for i, p in enumerate(('a', 'b', 'c', 'alpha', 'beta', 'gamma')):
432 key = sgrp_params[self.crystal_system][1][i]
433 if isinstance(key, str):
434 self._parameters[p] = self.free_parameters[key]
435 else:
436 self._parameters[p] = key
437
438 # set atom positions in the lattice base
439 self._wbase = WyckoffBase()
440 atoms = kwargs.get('atoms', None)
441 wps = kwargs.get('pos', None)
442 if atoms:
443 occs = kwargs.get('occ', [1.0, ] * len(atoms))
444 bs = kwargs.get('b', [0.0, ] * len(atoms))
445 for at, wpos, o, b in zip(atoms, wps, occs, bs):
446 self._wbase.append(at, wpos, o, b)
447 self.nsites = len(self._wbase)
448
449 # define lattice vectors
450 self._ai = numpy.zeros((3, 3))
451 self._bi = numpy.empty((3, 3))
452 a, b, c, alpha, beta, gamma = self._parameters.values()
453 ra = radians(alpha)
454 self._paramhelp = [cos(ra), cos(radians(beta)),
455 cos(radians(gamma)), sin(ra), 0]
456 self._setlat()
457
458 def base(self):
459 """
460 generator of atomic position within the unit cell.
461 """
462 if not self._wbase:
463 return
464 sgwp = wp[str(self.space_group)]
465 for (atom, w, occ, b) in self._wbase:
466 x, y, z = None, None, None
467 parint, poslist = sgwp[w[0]]
468 i = 0
469 if parint & 1:
470 try:
471 x = w[1][i]
472 except TypeError:
473 print('XU.materials: Wyckoff position %s of %s needs '
474 'parameters (%d) -> wrong material definition'
475 % (w[0], str(self.space_group), parint))
476 raise
477 i += 1
478 if parint & 2:
479 try:
480 y = w[1][i]
481 except TypeError:
482 print('XU.materials: Wyckoff position %s of %s needs '
483 'parameters (%d) -> wrong material definition'
484 % (w[0], str(self.space_group), parint))
485 raise
486 i += 1
487 if parint & 4:
488 try:
489 z = w[1][i]
490 except TypeError:
491 print('XU.materials: Wyckoff position %s of %s needs '
492 'parameters (%d) -> wrong material definition'
493 % (w[0], str(self.space_group), parint))
494 raise
495 i += 1
496 if w[1]:
497 if i != len(w[1]):
498 raise TypeError('XU.materials: too many parameters for '
499 'Wyckoff position')
500
501 for p in poslist:
502 pos = eval(p, {'x': x, 'y': y, 'z': z})
503 pos = numpy.asarray(pos)
504 pos -= pos // 1
505 yield atom, numpy.asarray(pos), occ, b
506
507 def _setlat(self):
508 a, b, c, alpha, beta, gamma = self._parameters.values()
509 ca, cb, cg, sa, vh = self._paramhelp
510 vh = sqrt(1 - ca**2-cb**2-cg**2 + 2*ca*cb*cg)
511 self._paramhelp[4] = vh
512 self._ai[0, 0] = a * vh / sa
513 self._ai[0, 1] = a * (cg-cb*ca) / sa
514 self._ai[0, 2] = a * cb
515 self._ai[1, 1] = b * sa
516 self._ai[1, 2] = b * ca
517 self._ai[2, 2] = c
518 self.transform = math.Transform(self._ai.T)
519 self._setb()
520
521 def _setb(self):
522 V = self.UnitCellVolume()
523 p = 2. * numpy.pi / V
524 math.VecCross(p*self._ai[1, :], self._ai[2, :], out=self._bi[0, :])
525 math.VecCross(p*self._ai[2, :], self._ai[0, :], out=self._bi[1, :])
526 math.VecCross(p*self._ai[0, :], self._ai[1, :], out=self._bi[2, :])
527 self.qtransform = math.Transform(self._bi.T)
528
529 def _set_params_from_sym(self):
530 for i, p in enumerate(('a', 'b', 'c', 'alpha', 'beta', 'gamma')):
531 key = sgrp_params[self.crystal_system][1][i]
532 if isinstance(key, str):
533 if p not in self.free_parameters:
534 self._parameters[p] = self.free_parameters[key]
535
536 @property
537 def a(self):
538 return self._parameters['a']
539
540 @a.setter
541 def a(self, value):
542 if 'a' not in self.free_parameters:
543 raise RuntimeError("a can not be set, its not a free parameter!")
544 self._parameters['a'] = value
545 self.free_parameters['a'] = value
546 self._set_params_from_sym()
547 self._setlat()
548
549 @property
550 def b(self):
551 return self._parameters['b']
552
553 @b.setter
554 def b(self, value):
555 if 'b' not in self.free_parameters:
556 raise RuntimeError("b can not be set, its not a free parameter!")
557 self._parameters['b'] = value
558 self.free_parameters['b'] = value
559 self._set_params_from_sym()
560 self._setlat()
561
562 @property
563 def c(self):
564 return self._parameters['c']
565
566 @c.setter
567 def c(self, value):
568 if 'c' not in self.free_parameters:
569 raise RuntimeError("c can not be set, its not a free parameter!")
570 self._parameters['c'] = value
571 self.free_parameters['c'] = value
572 self._set_params_from_sym()
573 self._setlat()
574
575 @property
576 def alpha(self):
577 return self._parameters['alpha']
578
579 @alpha.setter
580 def alpha(self, value):
581 if 'alpha' not in self.free_parameters:
582 raise RuntimeError("alpha can not be set for this space group!")
583 self._parameters['alpha'] = value
584 self.free_parameters['alpha'] = value
585 self._set_params_from_sym()
586 ra = radians(value)
587 self._paramhelp[0] = cos(ra)
588 self._paramhelp[3] = sin(ra)
589 self._setlat()
590
591 @property
592 def beta(self):
593 return self._parameters['beta']
594
595 @beta.setter
596 def beta(self, value):
597 if 'beta' not in self.free_parameters:
598 raise RuntimeError("beta can not be set for this space group!")
599 self._parameters['beta'] = value
600 self.free_parameters['beta'] = value
601 self._set_params_from_sym()
602 self._paramhelp[1] = cos(radians(value))
603 self._setlat()
604
605 @property
606 def gamma(self):
607 return self._parameters['gamma']
608
609 @gamma.setter
610 def gamma(self, value):
611 if 'gamma' not in self.free_parameters:
612 raise RuntimeError("gamma can not be set for this space group!")
613 self._parameters['gamma'] = value
614 self.free_parameters['gamma'] = value
615 self._set_params_from_sym()
616 self._paramhelp[2] = cos(radians(value))
617 self._setlat()
618
619 def __eq__(self, other):
620 """
621 compare another SGLattice instance to decide if both are equal.
622 To be equal they have to use the same space group, have equal lattice
623 paramters and contain equal atoms in their base.
624 """
625 if self.space_group != other.space_group:
626 return False
627 # compare lattice parameters
628 for prop in self.free_parameters:
629 if getattr(self, prop) != getattr(other, prop):
630 return False
631 # compare atoms in base
632 for e in self._wbase:
633 if e not in other._wbase:
634 return False
635 idx = other._wbase.index(e)
636 if not WyckoffBase.entry_eq(e, other._wbase[idx]):
637 return False
638 return True
639
640 def GetPoint(self, *args):
641 """
642 determine lattice points with indices given in the argument
643
644 Examples
645 --------
646 >>> xu.materials.Si.lattice.GetPoint(0, 0, 4)
647 array([ 0. , 0. , 21.72416])
648
649 or
650
651 >>> xu.materials.Si.lattice.GetPoint((1, 1, 1))
652 array([ 5.43104, 5.43104, 5.43104])
653 """
654 if len(args) == 1:
655 args = args[0]
656 return self.transform(args)
657
658 def GetQ(self, *args):
659 """
660 determine the reciprocal lattice points with indices given in the
661 argument
662 """
663 if len(args) == 1:
664 args = args[0]
665 return self.qtransform(args)
666
667 def GetHKL(self, *args):
668 """
669 determine the Miller indices of the given reciprocal lattice points
670 """
671 if len(args) == 1:
672 args = args[0]
673 return self.qtransform.inverse(args)
674
675 def UnitCellVolume(self):
676 """
677 function to calculate the unit cell volume of a lattice (angstrom^3)
678 """
679 a, b, c, alpha, beta, gamma = self._parameters.values()
680 return a * b * c * self._paramhelp[4]
681
682 def ApplyStrain(self, eps):
683 """
684 Applies a certain strain on a lattice. The result is a change
685 in the base vectors. The full strain matrix (3x3) needs to be given.
686
687 Note:
688 Here you specify the strain and not the stress -> NO elastic
689 response of the material will be considered!
690
691 Note:
692 Although the symmetry of the crystal can be lowered by this
693 operation the spacegroup remains unchanged! The 'free_parameters'
694 attribute is, however, updated to mimic the possible reduction of
695 the symmetry.
696
697 Parameters
698 ----------
699 eps : array-like
700 a 3x3 matrix with all strain components
701 """
702 if isinstance(eps, (list, tuple)):
703 eps = numpy.asarray(eps, dtype=numpy.double)
704 if eps.shape != (3, 3):
705 raise InputError("ApplyStrain needs a 3x3 matrix "
706 "with strain values")
707
708 ai = self._ai + numpy.dot(eps, self._ai.T).T
709 self._parameters['a'] = math.VecNorm(ai[0, :])
710 self._parameters['b'] = math.VecNorm(ai[1, :])
711 self._parameters['c'] = math.VecNorm(ai[2, :])
712 self._parameters['alpha'] = math.VecAngle(ai[1, :], ai[2, :], deg=True)
713 self._parameters['beta'] = math.VecAngle(ai[0, :], ai[2, :], deg=True)
714 self._parameters['gamma'] = math.VecAngle(ai[0, :], ai[1, :], deg=True)
715 # update helper parameters
716 ra = radians(self._parameters['alpha'])
717 self._paramhelp[0] = cos(ra)
718 self._paramhelp[1] = cos(radians(self._parameters['beta']))
719 self._paramhelp[2] = cos(radians(self._parameters['gamma']))
720 self._paramhelp[3] = sin(ra)
721 # set new transformations
722 self._setlat()
723 # update free_parameters
724 for p, v in self.free_parameters.items():
725 self.free_parameters[p] = self._parameters[p]
726 # artificially reduce symmetry if needed
727 for i, p in enumerate(('a', 'b', 'c', 'alpha', 'beta', 'gamma')):
728 key = sgrp_params[self.crystal_system][1][i]
729 if isinstance(key, str):
730 if self._parameters[p] != self.free_parameters[key]:
731 self.free_parameters[p] = self._parameters[p]
732 else:
733 if self._parameters[p] != key:
734 self.free_parameters[p] = self._parameters[p]
735
736 def isequivalent(self, hkl1, hkl2, equalq=False):
737 """
738 primitive way of determining if hkl1 and hkl2 are two
739 crystallographical equivalent pairs of Miller indices
740
741 Parameters
742 ----------
743 hkl1, hkl2 : list
744 Miller indices to be checked for equivalence
745 equalq : bool
746 If False the length of the two q-vactors will be compared. If True
747 it is assumed that the length of the q-vectors of hkl1 and hkl2 is
748 equal!
749
750 Returns
751 -------
752 bool
753 """
754 def leftShift(tup, n):
755 try:
756 n = n % len(tup)
757 except ZeroDivisionError:
758 return tuple()
759 return tup[n:] + tup[0:n]
760
761 def ispermutation(t1, t2):
762 """returns true if t2 is an even permutation of t1"""
763 if t1 == t2 or t1 == leftShift(t2, 1) or t1 == leftShift(t2, 2):
764 return True
765 else:
766 return False
767
768 def checkequal(hkl1, hkl2):
769 if self.crystal_system.startswith('cubic'):
770 if self.space_group_nr < 207:
771 if ispermutation(tuple(numpy.abs(hkl1)),
772 tuple(numpy.abs(hkl2))):
773 return True
774 else:
775 return False
776 else:
777 khl1 = (hkl1[1], hkl1[0], hkl1[2])
778 if (ispermutation(tuple(numpy.abs(hkl1)),
779 tuple(numpy.abs(hkl2))) or
780 ispermutation(tuple(numpy.abs(khl1)),
781 tuple(numpy.abs(hkl2)))):
782 return True
783 else:
784 return False
785 elif self.crystal_system.startswith('hexagonal'):
786 hki1 = (hkl1[0], hkl1[1], -hkl1[0] - hkl1[1])
787 hki2 = (hkl2[0], hkl2[1], -hkl2[0] - hkl2[1])
788 if (abs(hkl1[2]) == abs(hkl2[2]) and
789 sum(numpy.abs(hki1)) == sum(numpy.abs(hki2))):
790 return True
791 else:
792 return False
793 elif self.crystal_system.startswith('trigonal:R'):
794 khl1 = (hkl1[1], hkl1[0], hkl1[2])
795 if (ispermutation(hkl1, hkl2) or ispermutation(khl1, hkl2)):
796 return True
797 else:
798 return False
799 elif self.crystal_system.startswith('trigonal'):
800 hki1 = (hkl1[0], hkl1[1], -hkl1[0] - hkl1[1])
801 hki2 = (hkl2[0], hkl2[1], -hkl2[0] - hkl2[1])
802 khi2 = (hkl2[1], hkl2[0], -hkl2[0] - hkl2[1])
803 if ((hkl1[2] == hkl2[2] and ispermutation(hki1, hki2)) or
804 (hkl1[2] == -hkl2[2] and ispermutation(hki1, khi2))):
805 return True
806 else:
807 return False
808 elif self.crystal_system.startswith('tetragonal'):
809 # this neglects that in some tetragonal materials hkl = khl
810 hk1 = hkl1[:2]
811 hk2 = hkl2[:2]
812 if (abs(hkl1[2]) == abs(hkl2[2]) and
813 (hk1 == hk2 or hk1 == (-hk2[1], hk2[0]) or
814 hk1 == (-hk2[0], -hk2[1]) or
815 hk1 == (hk2[1], -hk2[0]))):
816 return True
817 else:
818 return False
819 elif self.crystal_system.startswith('orthorhombic'):
820 if numpy.all(numpy.abs(hkl1) == numpy.abs(hkl2)):
821 return True
822 else:
823 return False
824 elif self.crystal_system.startswith('monoclinic:c'):
825 hk1 = (hkl1[0], hkl1[1])
826 hk2 = (hkl2[0], hkl2[1])
827 if (abs(hkl1[2]) == abs(hkl2[2]) and
828 (hk1 == hk2 or hk1 == (-hk2[0], -hk2[1]))):
829 return True
830 else:
831 return False
832 elif self.crystal_system.startswith('monoclinic'):
833 hl1 = (hkl1[0], hkl1[2])
834 hl2 = (hkl2[0], hkl2[2])
835 if (abs(hkl1[1]) == abs(hkl2[1]) and
836 (hl1 == hl2 or hl1 == (-hl2[0], -hl2[1]))):
837 return True
838 else:
839 return False
840 elif self.crystal_system.startswith('triclinic'):
841 if (hkl1[0] == -hkl2[0] and hkl1[1] == -hkl2[1] and
842 hkl1[2] == -hkl2[2]):
843 return True
844 else:
845 return False
846
847 if not equalq:
848 if not numpy.isclose(math.VecNorm(self.GetQ(hkl1)),
849 math.VecNorm(self.GetQ(hkl2))):
850 return False
851 return checkequal(tuple(hkl1), tuple(hkl2))
852
853 def __str__(self):
854 ostr = "{sg} {cs} {n}: a = {a:.4f}, b = {b:.4f} c= {c:.4f}\n" +\
855 "alpha = {alpha:.3f}, beta = {beta:.3f}, gamma = {gamma:.3f}\n"
856 ostr = ostr.format(sg=self.space_group, cs=self.crystal_system,
857 n=self.name, **self._parameters)
858 if self._wbase:
859 ostr += "Lattice base:\n"
860 ostr += str(self._wbase)
861 return ostr
862
863 @classmethod
864 def convert_to_P1(cls, sglat):
865 """
866 create a P1 equivalent of the given SGLattice instance.
867
868 Parameters
869 ----------
870 sglat : SGLattice
871 space group lattice instance to be converted to P1.
872
873 Returns
874 -------
875 SGLattice
876 instance with the same properties as sglat, however in the P1
877 setting.
878 """
879 a, b, c, alpha, beta, gamma = (sglat.a, sglat.b, sglat.c, sglat.alpha,
880 sglat.beta, sglat.gamma)
881 atoms = []
882 pos = []
883 occ = []
884 biso = []
885 for at, p, o, bf in sglat.base():
886 atoms.append(at)
887 pos.append(('1a', p))
888 occ.append(o)
889 biso.append(bf)
890 return cls(1, a, b, c, alpha, beta, gamma, atoms=atoms, pos=pos,
891 occ=occ, b=biso)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 1 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 wp = {'1': {'1a': (7, ('(x, y, z)', ))},
18 '2': {'1a': (0, ('(0, 0, 0)', )),
19 '1b': (0, ('(0, 0, 1/2)', )),
20 '1c': (0, ('(0, 1/2, 0)', )),
21 '1d': (0, ('(1/2, 0, 0)', )),
22 '1e': (0, ('(1/2, 1/2, 0)', )),
23 '1f': (0, ('(1/2, 0, 1/2)', )),
24 '1g': (0, ('(0, 1/2, 1/2)', )),
25 '1h': (0, ('(1/2, 1/2, 1/2)', )),
26 '2i': (7, ('(x, y, z)', '(-x, -y, -z)'))},
27 '3:b': {'1a': (2, ('(0, y, 0)', )),
28 '1b': (2, ('(0, y, 1/2)', )),
29 '1c': (2, ('(1/2, y, 0)', )),
30 '1d': (2, ('(1/2, y, 1/2)', )),
31 '2e': (7, ('(x, y, z)', '(-x, y, -z)'))},
32 '3:c': {'1a': (4, ('(0, 0, z)', )),
33 '1b': (4, ('(1/2, 0, z)', )),
34 '1c': (4, ('(0, 1/2, z)', )),
35 '1d': (4, ('(1/2, 1/2, z)', )),
36 '2e': (7, ('(x, y, z)', '(-x, -y, z)'))},
37 '4:b': {'2a': (7, ('(x, y, z)', '(-x, y+1/2, -z)'))},
38 '4:c': {'2a': (7, ('(x, y, z)', '(-x, -y, z+1/2)'))},
39 '5:b': {'2a': (2, ('(0, y, 0)', '(1/2, y+1/2, 0)')),
40 '2b': (2, ('(0, y, 1/2)', '(1/2, y+1/2, 1/2)')),
41 '4c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, y, -z)',
42 '(-x+1/2, y+1/2, -z)'))},
43 '5:c': {'2a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)')),
44 '2b': (4, ('(1/2, 0, z)', '(1/2, 1/2, z+1/2)')),
45 '4c': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
46 '(-x, -y+1/2, z+1/2)'))},
47 '6:b': {'1a': (5, ('(x, 0, z)', )),
48 '1b': (5, ('(x, 1/2, z)', )),
49 '2c': (7, ('(x, y, z)', '(x, -y, z)'))},
50 '6:c': {'1a': (3, ('(x, y, 0)', )),
51 '1b': (3, ('(x, y, 1/2)', )),
52 '2c': (7, ('(x, y, z)', '(x, y, -z)'))},
53 '7:b': {'2a': (7, ('(x, y, z)', '(x, -y, z+1/2)'))},
54 '7:c': {'2a': (7, ('(x, y, z)', '(x+1/2, y, -z)'))},
55 '8:b': {'2a': (5, ('(x, 0, z)', '(x+1/2, 1/2, z)')),
56 '4b': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(x, -y, z)',
57 '(x+1/2, -y+1/2, z)'))},
58 '8:c': {'2a': (3, ('(x, y, 0)', '(x, y+1/2, 1/2)')),
59 '4b': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(x, y, -z)',
60 '(x, y+1/2, -z+1/2)'))},
61 '9:b': {'4a': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(x, -y, z+1/2)',
62 '(x+1/2, -y+1/2, z+1/2)'))},
63 '9:c': {'4a': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(x+1/2, y, -z)',
64 '(x+1/2, y+1/2, -z+1/2)'))},
65 '10:b': {'1a': (0, ('(0, 0, 0)', )),
66 '1b': (0, ('(0, 1/2, 0)', )),
67 '1c': (0, ('(0, 0, 1/2)', )),
68 '1d': (0, ('(1/2, 0, 0)', )),
69 '1e': (0, ('(1/2, 1/2, 0)', )),
70 '1f': (0, ('(0, 1/2, 1/2)', )),
71 '1g': (0, ('(1/2, 0, 1/2)', )),
72 '1h': (0, ('(1/2, 1/2, 1/2)', )),
73 '2i': (2, ('(0, y, 0)', '(0, -y, 0)')),
74 '2j': (2, ('(1/2, y, 0)', '(1/2, -y, 0)')),
75 '2k': (2, ('(0, y, 1/2)', '(0, -y, 1/2)')),
76 '2l': (2, ('(1/2, y, 1/2)', '(1/2, -y, 1/2)')),
77 '2m': (5, ('(x, 0, z)', '(-x, 0, -z)')),
78 '2n': (5, ('(x, 1/2, z)', '(-x, 1/2, -z)')),
79 '4o': (7, ('(x, y, z)', '(-x, y, -z)', '(-x, -y, -z)',
80 '(x, -y, z)'))},
81 '10:c': {'1a': (0, ('(0, 0, 0)', )),
82 '1b': (0, ('(0, 0, 1/2)', )),
83 '1c': (0, ('(1/2, 0, 0)', )),
84 '1d': (0, ('(0, 1/2, 0)', )),
85 '1e': (0, ('(0, 1/2, 1/2)', )),
86 '1f': (0, ('(1/2, 0, 1/2)', )),
87 '1g': (0, ('(1/2, 1/2, 0)', )),
88 '1h': (0, ('(1/2, 1/2, 1/2)', )),
89 '2i': (4, ('(0, 0, z)', '(0, 0, -z)')),
90 '2j': (4, ('(0, 1/2, z)', '(0, 1/2, -z)')),
91 '2k': (4, ('(1/2, 0, z)', '(1/2, 0, -z)')),
92 '2l': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
93 '2m': (3, ('(x, y, 0)', '(-x, -y, 0)')),
94 '2n': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)')),
95 '4o': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, -y, -z)',
96 '(x, y, -z)'))},
97 '11:b': {'2a': (0, ('(0, 0, 0)', '(0, 1/2, 0)')),
98 '2b': (0, ('(1/2, 0, 0)', '(1/2, 1/2, 0)')),
99 '2c': (0, ('(0, 0, 1/2)', '(0, 1/2, 1/2)')),
100 '2d': (0, ('(1/2, 0, 1/2)', '(1/2, 1/2, 1/2)')),
101 '2e': (5, ('(x, 1/4, z)', '(-x, 3/4, -z)')),
102 '4f': (7, ('(x, y, z)', '(-x, y+1/2, -z)', '(-x, -y, -z)',
103 '(x, -y+1/2, z)'))},
104 '11:c': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
105 '2b': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)')),
106 '2c': (0, ('(1/2, 0, 0)', '(1/2, 0, 1/2)')),
107 '2d': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
108 '2e': (3, ('(x, y, 1/4)', '(-x, -y, 3/4)')),
109 '4f': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-x, -y, -z)',
110 '(x, y, -z+1/2)'))},
111 '12:b': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
112 '2b': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
113 '2c': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
114 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
115 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 0)',
116 '(1/4, 3/4, 0)')),
117 '4f': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
118 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
119 '4g': (2, ('(0, y, 0)', '(1/2, y+1/2, 0)', '(0, -y, 0)',
120 '(1/2, -y+1/2, 0)')),
121 '4h': (2, ('(0, y, 1/2)', '(1/2, y+1/2, 1/2)', '(0, -y, 1/2)',
122 '(1/2, -y+1/2, 1/2)')),
123 '4i': (5, ('(x, 0, z)', '(x+1/2, 1/2, z)', '(-x, 0, -z)',
124 '(-x+1/2, 1/2, -z)')),
125 '8j': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, y, -z)',
126 '(-x+1/2, y+1/2, -z)', '(-x, -y, -z)',
127 '(-x+1/2, -y+1/2, -z)', '(x, -y, z)',
128 '(x+1/2, -y+1/2, z)'))},
129 '12:c': {'2a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)')),
130 '2b': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)')),
131 '2c': (0, ('(1/2, 0, 0)', '(1/2, 1/2, 1/2)')),
132 '2d': (0, ('(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
133 '4e': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(0, 3/4, 1/4)',
134 '(0, 1/4, 3/4)')),
135 '4f': (0, ('(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)',
136 '(1/2, 3/4, 1/4)', '(1/2, 1/4, 3/4)')),
137 '4g': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(0, 0, -z)',
138 '(0, 1/2, -z+1/2)')),
139 '4h': (4, ('(1/2, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 0, -z)',
140 '(1/2, 1/2, -z+1/2)')),
141 '4i': (3, ('(x, y, 0)', '(x, y+1/2, 1/2)', '(-x, -y, 0)',
142 '(-x, -y+1/2, 1/2)')),
143 '8j': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
144 '(-x, -y+1/2, z+1/2)', '(-x, -y, -z)',
145 '(-x, -y+1/2, -z+1/2)', '(x, y, -z)',
146 '(x, y+1/2, -z+1/2)'))},
147 '13:b': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
148 '2b': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
149 '2c': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)')),
150 '2d': (0, ('(1/2, 0, 0)', '(1/2, 0, 1/2)')),
151 '2e': (2, ('(0, y, 1/4)', '(0, -y, 3/4)')),
152 '2f': (2, ('(1/2, y, 1/4)', '(1/2, -y, 3/4)')),
153 '4g': (7, ('(x, y, z)', '(-x, y, -z+1/2)', '(-x, -y, -z)',
154 '(x, -y, z+1/2)'))},
155 '13:c': {'2a': (0, ('(0, 0, 0)', '(1/2, 0, 0)')),
156 '2b': (0, ('(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)')),
157 '2c': (0, ('(0, 0, 1/2)', '(1/2, 0, 1/2)')),
158 '2d': (0, ('(0, 1/2, 0)', '(1/2, 1/2, 0)')),
159 '2e': (4, ('(1/4, 0, z)', '(3/4, 0, -z)')),
160 '2f': (4, ('(1/4, 1/2, z)', '(3/4, 1/2, -z)')),
161 '4g': (7, ('(x, y, z)', '(-x+1/2, -y, z)', '(-x, -y, -z)',
162 '(x+1/2, y, -z)'))},
163 '14:b': {'2a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)')),
164 '2b': (0, ('(1/2, 0, 0)', '(1/2, 1/2, 1/2)')),
165 '2c': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)')),
166 '2d': (0, ('(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
167 '4e': (7, ('(x, y, z)', '(-x, y+1/2, -z+1/2)', '(-x, -y, -z)',
168 '(x, -y+1/2, z+1/2)'))},
169 '14:c': {'2a': (0, ('(0, 0, 0)', '(1/2, 0, 1/2)')),
170 '2b': (0, ('(0, 1/2, 0)', '(1/2, 1/2, 1/2)')),
171 '2c': (0, ('(1/2, 0, 0)', '(0, 0, 1/2)')),
172 '2d': (0, ('(1/2, 1/2, 0)', '(0, 1/2, 1/2)')),
173 '4e': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)', '(-x, -y, -z)',
174 '(x+1/2, y, -z+1/2)'))},
175 '15:b': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)',
176 '(1/2, 1/2, 1/2)')),
177 '4b': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
178 '(1/2, 0, 1/2)')),
179 '4c': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 1/2)',
180 '(1/4, 3/4, 1/2)')),
181 '4d': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
182 '(3/4, 1/4, 0)', '(1/4, 3/4, 0)')),
183 '4e': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 1/4)', '(0, -y, 3/4)',
184 '(1/2, -y+1/2, 3/4)')),
185 '8f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, y, -z+1/2)',
186 '(-x+1/2, y+1/2, -z+1/2)', '(-x, -y, -z)',
187 '(-x+1/2, -y+1/2, -z)', '(x, -y, z+1/2)',
188 '(x+1/2, -y+1/2, z+1/2)'))},
189 '15:c': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 0)',
190 '(1/2, 1/2, 1/2)')),
191 '4b': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 1/2)',
192 '(1/2, 1/2, 0)')),
193 '4c': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 3/4, 1/4)',
194 '(1/2, 1/4, 3/4)')),
195 '4d': (0, ('(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)',
196 '(0, 3/4, 1/4)', '(0, 1/4, 3/4)')),
197 '4e': (4, ('(1/4, 0, z)', '(1/4, 1/2, z+1/2)', '(3/4, 0, -z)',
198 '(3/4, 1/2, -z+1/2)')),
199 '8f': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x+1/2, -y, z)',
200 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, -z)',
201 '(-x, -y+1/2, -z+1/2)', '(x+1/2, y, -z)',
202 '(x+1/2, y+1/2, -z+1/2)'))},
203 '16': {'1a': (0, ('(0, 0, 0)', )),
204 '1b': (0, ('(1/2, 0, 0)', )),
205 '1c': (0, ('(0, 1/2, 0)', )),
206 '1d': (0, ('(0, 0, 1/2)', )),
207 '1e': (0, ('(1/2, 1/2, 0)', )),
208 '1f': (0, ('(1/2, 0, 1/2)', )),
209 '1g': (0, ('(0, 1/2, 1/2)', )),
210 '1h': (0, ('(1/2, 1/2, 1/2)', )),
211 '2i': (1, ('(x, 0, 0)', '(-x, 0, 0)')),
212 '2j': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)')),
213 '2k': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)')),
214 '2l': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)')),
215 '2m': (2, ('(0, y, 0)', '(0, -y, 0)')),
216 '2n': (2, ('(0, y, 1/2)', '(0, -y, 1/2)')),
217 '2o': (2, ('(1/2, y, 0)', '(1/2, -y, 0)')),
218 '2p': (2, ('(1/2, y, 1/2)', '(1/2, -y, 1/2)')),
219 '2q': (4, ('(0, 0, z)', '(0, 0, -z)')),
220 '2r': (4, ('(1/2, 0, z)', '(1/2, 0, -z)')),
221 '2s': (4, ('(0, 1/2, z)', '(0, 1/2, -z)')),
222 '2t': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
223 '4u': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
224 '(x, -y, -z)'))},
225 '17': {'2a': (1, ('(x, 0, 0)', '(-x, 0, 1/2)')),
226 '2b': (1, ('(x, 1/2, 0)', '(-x, 1/2, 1/2)')),
227 '2c': (2, ('(0, y, 1/4)', '(0, -y, 3/4)')),
228 '2d': (2, ('(1/2, y, 1/4)', '(1/2, -y, 3/4)')),
229 '4e': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-x, y, -z+1/2)',
230 '(x, -y, -z)'))},
231 '18': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, -z)')),
232 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
233 '4c': (7, ('(x, y, z)', '(-x, -y, z)', '(-x+1/2, y+1/2, -z)',
234 '(x+1/2, -y+1/2, -z)'))},
235 '19': {'4a': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
236 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)'))},
237 '20': {'4a': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
238 '(-x+1/2, 1/2, 1/2)')),
239 '4b': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 1/4)', '(0, -y, 3/4)',
240 '(1/2, -y+1/2, 3/4)')),
241 '8c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z+1/2)',
242 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z+1/2)',
243 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
244 '(x+1/2, -y+1/2, -z)'))},
245 '21': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
246 '2b': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
247 '2c': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
248 '2d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
249 '4e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 0, 0)',
250 '(-x+1/2, 1/2, 0)')),
251 '4f': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 1/2)',
252 '(-x+1/2, 1/2, 1/2)')),
253 '4g': (2, ('(0, y, 0)', '(1/2, y+1/2, 0)', '(0, -y, 0)',
254 '(1/2, -y+1/2, 0)')),
255 '4h': (2, ('(0, y, 1/2)', '(1/2, y+1/2, 1/2)', '(0, -y, 1/2)',
256 '(1/2, -y+1/2, 1/2)')),
257 '4i': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(0, 0, -z)',
258 '(1/2, 1/2, -z)')),
259 '4j': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
260 '(1/2, 0, -z)')),
261 '4k': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)', '(3/4, 1/4, -z)',
262 '(1/4, 3/4, -z)')),
263 '8l': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z)',
264 '(-x+1/2, -y+1/2, z)', '(-x, y, -z)',
265 '(-x+1/2, y+1/2, -z)', '(x, -y, -z)',
266 '(x+1/2, -y+1/2, -z)'))},
267 '22': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
268 '(1/2, 1/2, 0)')),
269 '4b': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 0)',
270 '(1/2, 1/2, 1/2)')),
271 '4c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
272 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
273 '4d': (0, ('(1/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
274 '(3/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
275 '8e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
276 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
277 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)')),
278 '8f': (2, ('(0, y, 0)', '(0, y+1/2, 1/2)', '(1/2, y, 1/2)',
279 '(1/2, y+1/2, 0)', '(0, -y, 0)', '(0, -y+1/2, 1/2)',
280 '(1/2, -y, 1/2)', '(1/2, -y+1/2, 0)')),
281 '8g': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
282 '(1/2, 1/2, z)', '(0, 0, -z)', '(0, 1/2, -z+1/2)',
283 '(1/2, 0, -z+1/2)', '(1/2, 1/2, -z)')),
284 '8h': (4, ('(1/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
285 '(3/4, 1/4, z+1/2)', '(3/4, 3/4, z)',
286 '(3/4, 1/4, -z)', '(3/4, 3/4, -z+1/2)',
287 '(1/4, 1/4, -z+1/2)', '(1/4, 3/4, -z)')),
288 '8i': (2, ('(1/4, y, 1/4)', '(1/4, y+1/2, 3/4)', '(3/4, y, 3/4)',
289 '(3/4, y+1/2, 1/4)', '(3/4, -y, 1/4)',
290 '(3/4, -y+1/2, 3/4)', '(1/4, -y, 3/4)',
291 '(1/4, -y+1/2, 1/4)')),
292 '8j': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)', '(x+1/2, 1/4, 3/4)',
293 '(x+1/2, 3/4, 1/4)', '(-x, 3/4, 1/4)',
294 '(-x, 1/4, 3/4)', '(-x+1/2, 3/4, 3/4)',
295 '(-x+1/2, 1/4, 1/4)')),
296 '16k': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
297 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
298 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
299 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
300 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
301 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
302 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
303 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)'))},
304 '23': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
305 '2b': (0, ('(1/2, 0, 0)', '(0, 1/2, 1/2)')),
306 '2c': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
307 '2d': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
308 '4e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
309 '(-x+1/2, 1/2, 1/2)')),
310 '4f': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
311 '(-x+1/2, 1/2, 0)')),
312 '4g': (2, ('(0, y, 0)', '(1/2, y+1/2, 1/2)', '(0, -y, 0)',
313 '(1/2, -y+1/2, 1/2)')),
314 '4h': (2, ('(1/2, y, 0)', '(0, y+1/2, 1/2)', '(1/2, -y, 0)',
315 '(0, -y+1/2, 1/2)')),
316 '4i': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
317 '(1/2, 1/2, -z+1/2)')),
318 '4j': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, -z)',
319 '(1/2, 0, -z+1/2)')),
320 '8k': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
321 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
322 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
323 '(x+1/2, -y+1/2, -z+1/2)'))},
324 '24': {'4a': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
325 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)')),
326 '4b': (2, ('(1/4, y, 0)', '(3/4, y+1/2, 1/2)', '(1/4, -y, 1/2)',
327 '(3/4, -y+1/2, 0)')),
328 '4c': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)',
329 '(0, 3/4, -z+1/2)', '(1/2, 1/4, -z)')),
330 '8d': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
331 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
332 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
333 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)'))},
334 '25': {'1a': (4, ('(0, 0, z)', )),
335 '1b': (4, ('(0, 1/2, z)', )),
336 '1c': (4, ('(1/2, 0, z)', )),
337 '1d': (4, ('(1/2, 1/2, z)', )),
338 '2e': (5, ('(x, 0, z)', '(-x, 0, z)')),
339 '2f': (5, ('(x, 1/2, z)', '(-x, 1/2, z)')),
340 '2g': (6, ('(0, y, z)', '(0, -y, z)')),
341 '2h': (6, ('(1/2, y, z)', '(1/2, -y, z)')),
342 '4i': (7, ('(x, y, z)', '(-x, -y, z)', '(x, -y, z)',
343 '(-x, y, z)'))},
344 '26': {'2a': (6, ('(0, y, z)', '(0, -y, z+1/2)')),
345 '2b': (6, ('(1/2, y, z)', '(1/2, -y, z+1/2)')),
346 '4c': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(x, -y, z+1/2)',
347 '(-x, y, z)'))},
348 '27': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
349 '2b': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)')),
350 '2c': (4, ('(1/2, 0, z)', '(1/2, 0, z+1/2)')),
351 '2d': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
352 '4e': (7, ('(x, y, z)', '(-x, -y, z)', '(x, -y, z+1/2)',
353 '(-x, y, z+1/2)'))},
354 '28': {'2a': (4, ('(0, 0, z)', '(1/2, 0, z)')),
355 '2b': (4, ('(0, 1/2, z)', '(1/2, 1/2, z)')),
356 '2c': (6, ('(1/4, y, z)', '(3/4, -y, z)')),
357 '4d': (7, ('(x, y, z)', '(-x, -y, z)', '(x+1/2, -y, z)',
358 '(-x+1/2, y, z)'))},
359 '29': {'4a': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(x+1/2, -y, z)',
360 '(-x+1/2, y, z+1/2)'))},
361 '30': {'2a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)')),
362 '2b': (4, ('(1/2, 0, z)', '(1/2, 1/2, z+1/2)')),
363 '4c': (7, ('(x, y, z)', '(-x, -y, z)', '(x, -y+1/2, z+1/2)',
364 '(-x, y+1/2, z+1/2)'))},
365 '31': {'2a': (6, ('(0, y, z)', '(1/2, -y, z+1/2)')),
366 '4b': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
367 '(x+1/2, -y, z+1/2)', '(-x, y, z)'))},
368 '32': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z)')),
369 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, z)')),
370 '4c': (7, ('(x, y, z)', '(-x, -y, z)', '(x+1/2, -y+1/2, z)',
371 '(-x+1/2, y+1/2, z)'))},
372 '33': {'4a': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
373 '(-x+1/2, y+1/2, z+1/2)'))},
374 '34': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
375 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)')),
376 '4c': (7, ('(x, y, z)', '(-x, -y, z)', '(x+1/2, -y+1/2, z+1/2)',
377 '(-x+1/2, y+1/2, z+1/2)'))},
378 '35': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z)')),
379 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, z)')),
380 '4c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)', '(1/4, 3/4, z)',
381 '(3/4, 1/4, z)')),
382 '4d': (5, ('(x, 0, z)', '(x+1/2, 1/2, z)', '(-x, 0, z)',
383 '(-x+1/2, 1/2, z)')),
384 '4e': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y, z)',
385 '(1/2, -y+1/2, z)')),
386 '8f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z)',
387 '(-x+1/2, -y+1/2, z)', '(x, -y, z)',
388 '(x+1/2, -y+1/2, z)', '(-x, y, z)',
389 '(-x+1/2, y+1/2, z)'))},
390 '36': {'4a': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y, z+1/2)',
391 '(1/2, -y+1/2, z+1/2)')),
392 '8b': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z+1/2)',
393 '(-x+1/2, -y+1/2, z+1/2)', '(x, -y, z+1/2)',
394 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
395 '(-x+1/2, y+1/2, z)'))},
396 '37': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(0, 0, z+1/2)',
397 '(1/2, 1/2, z+1/2)')),
398 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, z+1/2)',
399 '(1/2, 0, z+1/2)')),
400 '4c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)', '(1/4, 3/4, z+1/2)',
401 '(3/4, 1/4, z+1/2)')),
402 '8d': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z)',
403 '(-x+1/2, -y+1/2, z)', '(x, -y, z+1/2)',
404 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z+1/2)',
405 '(-x+1/2, y+1/2, z+1/2)'))},
406 '38': {'2a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)')),
407 '2b': (4, ('(1/2, 0, z)', '(1/2, 1/2, z+1/2)')),
408 '4c': (5, ('(x, 0, z)', '(x, 1/2, z+1/2)', '(-x, 0, z)',
409 '(-x, 1/2, z+1/2)')),
410 '4d': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(0, -y, z)',
411 '(0, -y+1/2, z+1/2)')),
412 '4e': (6, ('(1/2, y, z)', '(1/2, y+1/2, z+1/2)', '(1/2, -y, z)',
413 '(1/2, -y+1/2, z+1/2)')),
414 '8f': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
415 '(-x, -y+1/2, z+1/2)', '(x, -y, z)',
416 '(x, -y+1/2, z+1/2)', '(-x, y, z)',
417 '(-x, y+1/2, z+1/2)'))},
418 '39': {'4a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(0, 1/2, z)',
419 '(0, 0, z+1/2)')),
420 '4b': (4, ('(1/2, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 1/2, z)',
421 '(1/2, 0, z+1/2)')),
422 '4c': (5, ('(x, 1/4, z)', '(x, 3/4, z+1/2)', '(-x, 3/4, z)',
423 '(-x, 1/4, z+1/2)')),
424 '8d': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
425 '(-x, -y+1/2, z+1/2)', '(x, -y+1/2, z)',
426 '(x, -y, z+1/2)', '(-x, y+1/2, z)', '(-x, y, z+1/2)'
427 ))},
428 '40': {'4a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z)',
429 '(1/2, 1/2, z+1/2)')),
430 '4b': (6, ('(1/4, y, z)', '(1/4, y+1/2, z+1/2)', '(3/4, -y, z)',
431 '(3/4, -y+1/2, z+1/2)')),
432 '8c': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
433 '(-x, -y+1/2, z+1/2)', '(x+1/2, -y, z)',
434 '(x+1/2, -y+1/2, z+1/2)', '(-x+1/2, y, z)',
435 '(-x+1/2, y+1/2, z+1/2)'))},
436 '41': {'4a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 1/2, z)',
437 '(1/2, 0, z+1/2)')),
438 '8b': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
439 '(-x, -y+1/2, z+1/2)', '(x+1/2, -y+1/2, z)',
440 '(x+1/2, -y, z+1/2)', '(-x+1/2, y+1/2, z)',
441 '(-x+1/2, y, z+1/2)'))},
442 '42': {'4a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
443 '(1/2, 1/2, z)')),
444 '8b': (4, ('(1/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
445 '(3/4, 1/4, z+1/2)', '(3/4, 3/4, z)', '(1/4, 3/4, z)',
446 '(1/4, 1/4, z+1/2)', '(3/4, 3/4, z+1/2)',
447 '(3/4, 1/4, z)')),
448 '8c': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(1/2, y, z+1/2)',
449 '(1/2, y+1/2, z)', '(0, -y, z)', '(0, -y+1/2, z+1/2)',
450 '(1/2, -y, z+1/2)', '(1/2, -y+1/2, z)')),
451 '8d': (5, ('(x, 0, z)', '(x, 1/2, z+1/2)', '(x+1/2, 0, z+1/2)',
452 '(x+1/2, 1/2, z)', '(-x, 0, z)', '(-x, 1/2, z+1/2)',
453 '(-x+1/2, 0, z+1/2)', '(-x+1/2, 1/2, z)')),
454 '16e': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
455 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
456 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
457 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
458 '(x, -y, z)', '(x, -y+1/2, z+1/2)',
459 '(x+1/2, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
460 '(-x, y, z)', '(-x, y+1/2, z+1/2)',
461 '(-x+1/2, y, z+1/2)', '(-x+1/2, y+1/2, z)'))},
462 '43': {'8a': (4, ('(0, 0, z)', '(1/2, 0, z+1/2)', '(0, 1/2, z+1/2)',
463 '(1/2, 1/2, z)', '(1/4, 1/4, z+1/4)',
464 '(3/4, 1/4, z+3/4)', '(1/4, 3/4, z+3/4)',
465 '(3/4, 3/4, z+1/4)')),
466 '16b': (7, ('(x, y, z)', '(x+1/2, y, z+1/2)',
467 '(x, y+1/2, z+1/2)', '(x+1/2, y+1/2, z)',
468 '(-x, -y, z)', '(-x+1/2, -y, z+1/2)',
469 '(-x, -y+1/2, z+1/2)', '(-x+1/2, -y+1/2, z)',
470 '(x+1/4, -y+1/4, z+1/4)', '(x+3/4, -y+1/4, z+3/4)',
471 '(x+1/4, -y+3/4, z+3/4)', '(x+3/4, -y+3/4, z+1/4)',
472 '(-x+1/4, y+1/4, z+1/4)', '(-x+3/4, y+1/4, z+3/4)',
473 '(-x+1/4, y+3/4, z+3/4)', '(-x+3/4, y+3/4, z+1/4)'))},
474 '44': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
475 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)')),
476 '4c': (5, ('(x, 0, z)', '(x+1/2, 1/2, z+1/2)', '(-x, 0, z)',
477 '(-x+1/2, 1/2, z+1/2)')),
478 '4d': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y, z)',
479 '(1/2, -y+1/2, z+1/2)')),
480 '8e': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
481 '(-x+1/2, -y+1/2, z+1/2)', '(x, -y, z)',
482 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
483 '(-x+1/2, y+1/2, z+1/2)'))},
484 '45': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 1/2, z)',
485 '(0, 0, z+1/2)')),
486 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
487 '(0, 1/2, z+1/2)')),
488 '8c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
489 '(-x+1/2, -y+1/2, z+1/2)', '(x+1/2, -y+1/2, z)',
490 '(x, -y, z+1/2)', '(-x+1/2, y+1/2, z)',
491 '(-x, y, z+1/2)'))},
492 '46': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 0, z)',
493 '(0, 1/2, z+1/2)')),
494 '4b': (6, ('(1/4, y, z)', '(3/4, y+1/2, z+1/2)', '(3/4, -y, z)',
495 '(1/4, -y+1/2, z+1/2)')),
496 '8c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
497 '(-x+1/2, -y+1/2, z+1/2)', '(x+1/2, -y, z)',
498 '(x, -y+1/2, z+1/2)', '(-x+1/2, y, z)',
499 '(-x, y+1/2, z+1/2)'))},
500 '47': {'8A': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
501 '(x, -y, -z)', '(-x, -y, -z)', '(x, y, -z)',
502 '(x, -y, z)', '(-x, y, z)')),
503 '1a': (0, ('(0, 0, 0)', )),
504 '1b': (0, ('(1/2, 0, 0)', )),
505 '1c': (0, ('(0, 0, 1/2)', )),
506 '1d': (0, ('(1/2, 0, 1/2)', )),
507 '1e': (0, ('(0, 1/2, 0)', )),
508 '1f': (0, ('(1/2, 1/2, 0)', )),
509 '1g': (0, ('(0, 1/2, 1/2)', )),
510 '1h': (0, ('(1/2, 1/2, 1/2)', )),
511 '2i': (1, ('(x, 0, 0)', '(-x, 0, 0)')),
512 '2j': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)')),
513 '2k': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)')),
514 '2l': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)')),
515 '2m': (2, ('(0, y, 0)', '(0, -y, 0)')),
516 '2n': (2, ('(0, y, 1/2)', '(0, -y, 1/2)')),
517 '2o': (2, ('(1/2, y, 0)', '(1/2, -y, 0)')),
518 '2p': (2, ('(1/2, y, 1/2)', '(1/2, -y, 1/2)')),
519 '2q': (4, ('(0, 0, z)', '(0, 0, -z)')),
520 '2r': (4, ('(0, 1/2, z)', '(0, 1/2, -z)')),
521 '2s': (4, ('(1/2, 0, z)', '(1/2, 0, -z)')),
522 '2t': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
523 '4u': (6, ('(0, y, z)', '(0, -y, z)', '(0, y, -z)',
524 '(0, -y, -z)')),
525 '4v': (6, ('(1/2, y, z)', '(1/2, -y, z)', '(1/2, y, -z)',
526 '(1/2, -y, -z)')),
527 '4w': (5, ('(x, 0, z)', '(-x, 0, z)', '(-x, 0, -z)',
528 '(x, 0, -z)')),
529 '4x': (5, ('(x, 1/2, z)', '(-x, 1/2, z)', '(-x, 1/2, -z)',
530 '(x, 1/2, -z)')),
531 '4y': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-x, y, 0)',
532 '(x, -y, 0)')),
533 '4z': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)', '(-x, y, 1/2)',
534 '(x, -y, 1/2)'))},
535 '48:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
536 '2b': (0, ('(1/2, 0, 0)', '(0, 1/2, 1/2)')),
537 '2c': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
538 '2d': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
539 '4e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
540 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)')),
541 '4f': (0, ('(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
542 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
543 '4g': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(-x+1/2, 1/2, 1/2)',
544 '(x+1/2, 1/2, 1/2)')),
545 '4h': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(-x+1/2, 1/2, 0)',
546 '(x+1/2, 1/2, 0)')),
547 '4i': (2, ('(0, y, 0)', '(0, -y, 0)', '(1/2, -y+1/2, 1/2)',
548 '(1/2, y+1/2, 1/2)')),
549 '4j': (2, ('(1/2, y, 0)', '(1/2, -y, 0)', '(0, -y+1/2, 1/2)',
550 '(0, y+1/2, 1/2)')),
551 '4k': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
552 '(1/2, 1/2, z+1/2)')),
553 '4l': (4, ('(0, 1/2, z)', '(0, 1/2, -z)', '(1/2, 0, -z+1/2)',
554 '(1/2, 0, z+1/2)')),
555 '8m': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
556 '(x, -y, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
557 '(x+1/2, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, z+1/2)',
558 '(-x+1/2, y+1/2, z+1/2)'))},
559 '48:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
560 '2b': (0, ('(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)')),
561 '2c': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
562 '2d': (0, ('(1/4, 3/4, 1/4)', '(3/4, 1/4, 3/4)')),
563 '4e': (0, ('(1/2, 1/2, 1/2)', '(0, 0, 1/2)', '(0, 1/2, 0)',
564 '(1/2, 0, 0)')),
565 '4f': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
566 '(0, 1/2, 1/2)')),
567 '4g': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
568 '(-x, 3/4, 3/4)', '(x+1/2, 3/4, 3/4)')),
569 '4h': (1, ('(x, 1/4, 3/4)', '(-x+1/2, 1/4, 3/4)',
570 '(-x, 3/4, 1/4)', '(x+1/2, 3/4, 1/4)')),
571 '4i': (2, ('(1/4, y, 1/4)', '(1/4, -y+1/2, 1/4)',
572 '(3/4, -y, 3/4)', '(3/4, y+1/2, 3/4)')),
573 '4j': (2, ('(3/4, y, 1/4)', '(3/4, -y+1/2, 1/4)',
574 '(1/4, -y, 3/4)', '(1/4, y+1/2, 3/4)')),
575 '4k': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, -z+1/2)',
576 '(3/4, 3/4, -z)', '(3/4, 3/4, z+1/2)')),
577 '4l': (4, ('(1/4, 3/4, z)', '(1/4, 3/4, -z+1/2)',
578 '(3/4, 1/4, -z)', '(3/4, 1/4, z+1/2)')),
579 '8m': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
580 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
581 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
582 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)'))},
583 '49': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
584 '2b': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
585 '2c': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)')),
586 '2d': (0, ('(1/2, 0, 0)', '(1/2, 0, 1/2)')),
587 '2e': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
588 '2f': (0, ('(1/2, 0, 1/4)', '(1/2, 0, 3/4)')),
589 '2g': (0, ('(0, 1/2, 1/4)', '(0, 1/2, 3/4)')),
590 '2h': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
591 '4i': (1, ('(x, 0, 1/4)', '(-x, 0, 1/4)', '(-x, 0, 3/4)',
592 '(x, 0, 3/4)')),
593 '4j': (1, ('(x, 1/2, 1/4)', '(-x, 1/2, 1/4)', '(-x, 1/2, 3/4)',
594 '(x, 1/2, 3/4)')),
595 '4k': (2, ('(0, y, 1/4)', '(0, -y, 1/4)', '(0, -y, 3/4)',
596 '(0, y, 3/4)')),
597 '4l': (2, ('(1/2, y, 1/4)', '(1/2, -y, 1/4)', '(1/2, -y, 3/4)',
598 '(1/2, y, 3/4)')),
599 '4m': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
600 '(0, 0, z+1/2)')),
601 '4n': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z+1/2)',
602 '(1/2, 1/2, -z)', '(1/2, 1/2, z+1/2)')),
603 '4o': (4, ('(0, 1/2, z)', '(0, 1/2, -z+1/2)', '(0, 1/2, -z)',
604 '(0, 1/2, z+1/2)')),
605 '4p': (4, ('(1/2, 0, z)', '(1/2, 0, -z+1/2)', '(1/2, 0, -z)',
606 '(1/2, 0, z+1/2)')),
607 '4q': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-x, y, 1/2)',
608 '(x, -y, 1/2)')),
609 '8r': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z+1/2)',
610 '(x, -y, -z+1/2)', '(-x, -y, -z)', '(x, y, -z)',
611 '(x, -y, z+1/2)', '(-x, y, z+1/2)'))},
612 '50:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
613 '2b': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)')),
614 '2c': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
615 '2d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
616 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 0)',
617 '(1/4, 3/4, 0)')),
618 '4f': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
619 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
620 '4g': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(-x+1/2, 1/2, 0)',
621 '(x+1/2, 1/2, 0)')),
622 '4h': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(-x+1/2, 1/2, 1/2)',
623 '(x+1/2, 1/2, 1/2)')),
624 '4i': (2, ('(0, y, 0)', '(0, -y, 0)', '(1/2, -y+1/2, 0)',
625 '(1/2, y+1/2, 0)')),
626 '4j': (2, ('(0, y, 1/2)', '(0, -y, 1/2)', '(1/2, -y+1/2, 1/2)',
627 '(1/2, y+1/2, 1/2)')),
628 '4k': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z)',
629 '(1/2, 1/2, z)')),
630 '4l': (4, ('(0, 1/2, z)', '(0, 1/2, -z)', '(1/2, 0, -z)',
631 '(1/2, 0, z)')),
632 '8m': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
633 '(x, -y, -z)', '(-x+1/2, -y+1/2, -z)',
634 '(x+1/2, y+1/2, -z)', '(x+1/2, -y+1/2, z)',
635 '(-x+1/2, y+1/2, z)'))},
636 '50:2': {'2a': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)')),
637 '2b': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 0)')),
638 '2c': (0, ('(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
639 '2d': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)')),
640 '4e': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
641 '(0, 1/2, 0)')),
642 '4f': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
643 '(0, 1/2, 1/2)')),
644 '4g': (1, ('(x, 1/4, 0)', '(-x+1/2, 1/4, 0)', '(-x, 3/4, 0)',
645 '(x+1/2, 3/4, 0)')),
646 '4h': (1, ('(x, 1/4, 1/2)', '(-x+1/2, 1/4, 1/2)',
647 '(-x, 3/4, 1/2)', '(x+1/2, 3/4, 1/2)')),
648 '4i': (2, ('(1/4, y, 0)', '(1/4, -y+1/2, 0)', '(3/4, -y, 0)',
649 '(3/4, y+1/2, 0)')),
650 '4j': (2, ('(1/4, y, 1/2)', '(1/4, -y+1/2, 1/2)',
651 '(3/4, -y, 1/2)', '(3/4, y+1/2, 1/2)')),
652 '4k': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, -z)', '(3/4, 3/4, -z)',
653 '(3/4, 3/4, z)')),
654 '4l': (4, ('(1/4, 3/4, z)', '(1/4, 3/4, -z)', '(3/4, 1/4, -z)',
655 '(3/4, 1/4, z)')),
656 '8m': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
657 '(-x+1/2, y, -z)', '(x, -y+1/2, -z)',
658 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
659 '(x+1/2, -y, z)', '(-x, y+1/2, z)'))},
660 '51': {'2a': (0, ('(0, 0, 0)', '(1/2, 0, 0)')),
661 '2b': (0, ('(0, 1/2, 0)', '(1/2, 1/2, 0)')),
662 '2c': (0, ('(0, 0, 1/2)', '(1/2, 0, 1/2)')),
663 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)')),
664 '2e': (4, ('(1/4, 0, z)', '(3/4, 0, -z)')),
665 '2f': (4, ('(1/4, 1/2, z)', '(3/4, 1/2, -z)')),
666 '4g': (2, ('(0, y, 0)', '(1/2, -y, 0)', '(0, -y, 0)',
667 '(1/2, y, 0)')),
668 '4h': (2, ('(0, y, 1/2)', '(1/2, -y, 1/2)', '(0, -y, 1/2)',
669 '(1/2, y, 1/2)')),
670 '4i': (5, ('(x, 0, z)', '(-x+1/2, 0, z)', '(-x, 0, -z)',
671 '(x+1/2, 0, -z)')),
672 '4j': (5, ('(x, 1/2, z)', '(-x+1/2, 1/2, z)', '(-x, 1/2, -z)',
673 '(x+1/2, 1/2, -z)')),
674 '4k': (6, ('(1/4, y, z)', '(1/4, -y, z)', '(3/4, y, -z)',
675 '(3/4, -y, -z)')),
676 '8l': (7, ('(x, y, z)', '(-x+1/2, -y, z)', '(-x, y, -z)',
677 '(x+1/2, -y, -z)', '(-x, -y, -z)', '(x+1/2, y, -z)',
678 '(x, -y, z)', '(-x+1/2, y, z)'))},
679 '52': {'4a': (0, ('(0, 0, 0)', '(1/2, 0, 0)', '(1/2, 1/2, 1/2)',
680 '(0, 1/2, 1/2)')),
681 '4b': (0, ('(0, 0, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
682 '(0, 1/2, 0)')),
683 '4c': (4, ('(1/4, 0, z)', '(1/4, 1/2, -z+1/2)', '(3/4, 0, -z)',
684 '(3/4, 1/2, z+1/2)')),
685 '4d': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 3/4, 1/4)',
686 '(-x, 3/4, 3/4)', '(x+1/2, 1/4, 3/4)')),
687 '8e': (7, ('(x, y, z)', '(-x+1/2, -y, z)',
688 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y+1/2, -z+1/2)',
689 '(-x, -y, -z)', '(x+1/2, y, -z)',
690 '(x+1/2, -y+1/2, z+1/2)', '(-x, y+1/2, z+1/2)'))},
691 '53': {'2a': (0, ('(0, 0, 0)', '(1/2, 0, 1/2)')),
692 '2b': (0, ('(1/2, 0, 0)', '(0, 0, 1/2)')),
693 '2c': (0, ('(1/2, 1/2, 0)', '(0, 1/2, 1/2)')),
694 '2d': (0, ('(0, 1/2, 0)', '(1/2, 1/2, 1/2)')),
695 '4e': (1, ('(x, 0, 0)', '(-x+1/2, 0, 1/2)', '(-x, 0, 0)',
696 '(x+1/2, 0, 1/2)')),
697 '4f': (1, ('(x, 1/2, 0)', '(-x+1/2, 1/2, 1/2)', '(-x, 1/2, 0)',
698 '(x+1/2, 1/2, 1/2)')),
699 '4g': (2, ('(1/4, y, 1/4)', '(1/4, -y, 3/4)', '(3/4, -y, 3/4)',
700 '(3/4, y, 1/4)')),
701 '4h': (6, ('(0, y, z)', '(1/2, -y, z+1/2)', '(1/2, y, -z+1/2)',
702 '(0, -y, -z)')),
703 '8i': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
704 '(-x+1/2, y, -z+1/2)', '(x, -y, -z)', '(-x, -y, -z)',
705 '(x+1/2, y, -z+1/2)', '(x+1/2, -y, z+1/2)',
706 '(-x, y, z)'))},
707 '54': {'4a': (0, ('(0, 0, 0)', '(1/2, 0, 0)', '(0, 0, 1/2)',
708 '(1/2, 0, 1/2)')),
709 '4b': (0, ('(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
710 '(1/2, 1/2, 1/2)')),
711 '4c': (2, ('(0, y, 1/4)', '(1/2, -y, 1/4)', '(0, -y, 3/4)',
712 '(1/2, y, 3/4)')),
713 '4d': (4, ('(1/4, 0, z)', '(3/4, 0, -z+1/2)', '(3/4, 0, -z)',
714 '(1/4, 0, z+1/2)')),
715 '4e': (4, ('(1/4, 1/2, z)', '(3/4, 1/2, -z+1/2)',
716 '(3/4, 1/2, -z)', '(1/4, 1/2, z+1/2)')),
717 '8f': (7, ('(x, y, z)', '(-x+1/2, -y, z)', '(-x, y, -z+1/2)',
718 '(x+1/2, -y, -z+1/2)', '(-x, -y, -z)',
719 '(x+1/2, y, -z)', '(x, -y, z+1/2)',
720 '(-x+1/2, y, z+1/2)'))},
721 '55': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
722 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
723 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
724 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
725 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, -z)', '(0, 0, -z)',
726 '(1/2, 1/2, z)')),
727 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(0, 1/2, -z)',
728 '(1/2, 0, z)')),
729 '4g': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-x+1/2, y+1/2, 0)',
730 '(x+1/2, -y+1/2, 0)')),
731 '4h': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)',
732 '(-x+1/2, y+1/2, 1/2)', '(x+1/2, -y+1/2, 1/2)')),
733 '8i': (7, ('(x, y, z)', '(-x, -y, z)', '(-x+1/2, y+1/2, -z)',
734 '(x+1/2, -y+1/2, -z)', '(-x, -y, -z)', '(x, y, -z)',
735 '(x+1/2, -y+1/2, z)', '(-x+1/2, y+1/2, z)'))},
736 '56': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
737 '(1/2, 0, 1/2)')),
738 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(0, 1/2, 0)',
739 '(1/2, 0, 0)')),
740 '4c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, -z+1/2)',
741 '(3/4, 3/4, -z)', '(1/4, 1/4, z+1/2)')),
742 '4d': (4, ('(1/4, 3/4, z)', '(3/4, 1/4, -z+1/2)',
743 '(3/4, 1/4, -z)', '(1/4, 3/4, z+1/2)')),
744 '8e': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
745 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y, -z+1/2)',
746 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
747 '(x, -y+1/2, z+1/2)', '(-x+1/2, y, z+1/2)'))},
748 '57': {'4a': (0, ('(0, 0, 0)', '(0, 0, 1/2)', '(0, 1/2, 1/2)',
749 '(0, 1/2, 0)')),
750 '4b': (0, ('(1/2, 0, 0)', '(1/2, 0, 1/2)', '(1/2, 1/2, 1/2)',
751 '(1/2, 1/2, 0)')),
752 '4c': (1, ('(x, 1/4, 0)', '(-x, 3/4, 1/2)', '(-x, 3/4, 0)',
753 '(x, 1/4, 1/2)')),
754 '4d': (3, ('(x, y, 1/4)', '(-x, -y, 3/4)', '(-x, y+1/2, 1/4)',
755 '(x, -y+1/2, 3/4)')),
756 '8e': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-x, y+1/2, -z+1/2)',
757 '(x, -y+1/2, -z)', '(-x, -y, -z)', '(x, y, -z+1/2)',
758 '(x, -y+1/2, z+1/2)', '(-x, y+1/2, z)'))},
759 '58': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
760 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
761 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
762 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)')),
763 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, -z+1/2)', '(0, 0, -z)',
764 '(1/2, 1/2, z+1/2)')),
765 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, -z+1/2)', '(0, 1/2, -z)',
766 '(1/2, 0, z+1/2)')),
767 '4g': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-x+1/2, y+1/2, 1/2)',
768 '(x+1/2, -y+1/2, 1/2)')),
769 '8h': (7, ('(x, y, z)', '(-x, -y, z)', '(-x+1/2, y+1/2, -z+1/2)',
770 '(x+1/2, -y+1/2, -z+1/2)', '(-x, -y, -z)',
771 '(x, y, -z)', '(x+1/2, -y+1/2, z+1/2)',
772 '(-x+1/2, y+1/2, z+1/2)'))},
773 '59:1': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, -z)')),
774 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
775 '4c': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(1/4, 3/4, 0)',
776 '(3/4, 1/4, 0)')),
777 '4d': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
778 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
779 '4e': (6, ('(0, y, z)', '(0, -y, z)', '(1/2, y+1/2, -z)',
780 '(1/2, -y+1/2, -z)')),
781 '4f': (5, ('(x, 0, z)', '(-x, 0, z)', '(-x+1/2, 1/2, -z)',
782 '(x+1/2, 1/2, -z)')),
783 '8g': (7, ('(x, y, z)', '(-x, -y, z)', '(-x+1/2, y+1/2, -z)',
784 '(x+1/2, -y+1/2, -z)', '(-x+1/2, -y+1/2, -z)',
785 '(x+1/2, y+1/2, -z)', '(x, -y, z)', '(-x, y, z)'))},
786 '59:2': {'2a': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, -z)')),
787 '2b': (4, ('(1/4, 3/4, z)', '(3/4, 1/4, -z)')),
788 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 0)',
789 '(1/2, 0, 0)')),
790 '4d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/2)',
791 '(1/2, 0, 1/2)')),
792 '4e': (6, ('(1/4, y, z)', '(1/4, -y+1/2, z)',
793 '(3/4, y+1/2, -z)', '(3/4, -y, -z)')),
794 '4f': (5, ('(x, 1/4, z)', '(-x+1/2, 1/4, z)', '(-x, 3/4, -z)',
795 '(x+1/2, 3/4, -z)')),
796 '8g': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
797 '(-x, y+1/2, -z)', '(x+1/2, -y, -z)',
798 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
799 '(x, -y+1/2, z)', '(-x+1/2, y, z)'))},
800 '60': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 0, 1/2)',
801 '(1/2, 1/2, 0)')),
802 '4b': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)',
803 '(1/2, 0, 0)')),
804 '4c': (2, ('(0, y, 1/4)', '(1/2, -y+1/2, 3/4)', '(0, -y, 3/4)',
805 '(1/2, y+1/2, 1/4)')),
806 '8d': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z+1/2)',
807 '(-x, y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
808 '(-x, -y, -z)', '(x+1/2, y+1/2, -z+1/2)',
809 '(x, -y, z+1/2)', '(-x+1/2, y+1/2, z)'))},
810 '61': {'4a': (0, ('(0, 0, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)',
811 '(1/2, 1/2, 0)')),
812 '4b': (0, ('(0, 0, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
813 '(1/2, 1/2, 1/2)')),
814 '8c': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
815 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)',
816 '(-x, -y, -z)', '(x+1/2, y, -z+1/2)',
817 '(x, -y+1/2, z+1/2)', '(-x+1/2, y+1/2, z)'))},
818 '62': {'4a': (0, ('(0, 0, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 0)',
819 '(1/2, 1/2, 1/2)')),
820 '4b': (0, ('(0, 0, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
821 '(1/2, 1/2, 0)')),
822 '4c': (5, ('(x, 1/4, z)', '(-x+1/2, 3/4, z+1/2)',
823 '(-x, 3/4, -z)', '(x+1/2, 1/4, -z+1/2)')),
824 '8d': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)', '(-x, y+1/2, -z)',
825 '(x+1/2, -y+1/2, -z+1/2)', '(-x, -y, -z)',
826 '(x+1/2, y, -z+1/2)', '(x, -y+1/2, z)',
827 '(-x+1/2, y+1/2, z+1/2)'))},
828 '63': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)',
829 '(1/2, 1/2, 1/2)')),
830 '4b': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
831 '(1/2, 0, 1/2)')),
832 '4c': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 1/4)', '(0, -y, 3/4)',
833 '(1/2, -y+1/2, 3/4)')),
834 '8d': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 3/4, 1/2)',
835 '(1/4, 1/4, 1/2)', '(3/4, 1/4, 1/2)',
836 '(1/4, 3/4, 1/2)', '(1/4, 3/4, 0)', '(3/4, 1/4, 0)')),
837 '8e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
838 '(-x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
839 '(-x+1/2, 1/2, 0)', '(x, 0, 1/2)',
840 '(x+1/2, 1/2, 1/2)')),
841 '8f': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y, z+1/2)',
842 '(1/2, -y+1/2, z+1/2)', '(0, y, -z+1/2)',
843 '(1/2, y+1/2, -z+1/2)', '(0, -y, -z)',
844 '(1/2, -y+1/2, -z)')),
845 '8g': (3, ('(x, y, 1/4)', '(x+1/2, y+1/2, 1/4)', '(-x, -y, 3/4)',
846 '(-x+1/2, -y+1/2, 3/4)', '(-x, y, 1/4)',
847 '(-x+1/2, y+1/2, 1/4)', '(x, -y, 3/4)',
848 '(x+1/2, -y+1/2, 3/4)')),
849 '16h': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z+1/2)',
850 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z+1/2)',
851 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
852 '(x+1/2, -y+1/2, -z)', '(-x, -y, -z)',
853 '(-x+1/2, -y+1/2, -z)', '(x, y, -z+1/2)',
854 '(x+1/2, y+1/2, -z+1/2)', '(x, -y, z+1/2)',
855 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
856 '(-x+1/2, y+1/2, z)'))},
857 '64': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
858 '(1/2, 0, 1/2)')),
859 '4b': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 1/2)',
860 '(0, 0, 1/2)')),
861 '8c': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 1/2)',
862 '(1/4, 3/4, 1/2)', '(3/4, 3/4, 1/2)',
863 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)', '(3/4, 1/4, 0)')),
864 '8d': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 1/2, 1/2)',
865 '(-x+1/2, 0, 1/2)', '(-x, 0, 0)', '(-x+1/2, 1/2, 0)',
866 '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)')),
867 '8e': (2, ('(1/4, y, 1/4)', '(3/4, y+1/2, 1/4)',
868 '(3/4, -y+1/2, 3/4)', '(1/4, -y, 3/4)',
869 '(3/4, -y, 3/4)', '(1/4, -y+1/2, 3/4)',
870 '(1/4, y+1/2, 1/4)', '(3/4, y, 1/4)')),
871 '8f': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y+1/2, z+1/2)',
872 '(1/2, -y, z+1/2)', '(0, y+1/2, -z+1/2)',
873 '(1/2, y, -z+1/2)', '(0, -y, -z)',
874 '(1/2, -y+1/2, -z)')),
875 '16g': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)',
876 '(-x, -y+1/2, z+1/2)', '(-x+1/2, -y, z+1/2)',
877 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z+1/2)',
878 '(x, -y, -z)', '(x+1/2, -y+1/2, -z)', '(-x, -y, -z)',
879 '(-x+1/2, -y+1/2, -z)', '(x, y+1/2, -z+1/2)',
880 '(x+1/2, y, -z+1/2)', '(x, -y+1/2, z+1/2)',
881 '(x+1/2, -y, z+1/2)', '(-x, y, z)',
882 '(-x+1/2, y+1/2, z)'))},
883 '65': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
884 '2b': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)')),
885 '2c': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
886 '2d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
887 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 0)',
888 '(1/4, 3/4, 0)')),
889 '4f': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
890 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
891 '4g': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 0, 0)',
892 '(-x+1/2, 1/2, 0)')),
893 '4h': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 1/2)',
894 '(-x+1/2, 1/2, 1/2)')),
895 '4i': (2, ('(0, y, 0)', '(1/2, y+1/2, 0)', '(0, -y, 0)',
896 '(1/2, -y+1/2, 0)')),
897 '4j': (2, ('(0, y, 1/2)', '(1/2, y+1/2, 1/2)', '(0, -y, 1/2)',
898 '(1/2, -y+1/2, 1/2)')),
899 '4k': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(0, 0, -z)',
900 '(1/2, 1/2, -z)')),
901 '4l': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
902 '(1/2, 0, -z)')),
903 '8m': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)', '(3/4, 1/4, -z)',
904 '(1/4, 3/4, -z)', '(3/4, 3/4, -z)', '(1/4, 1/4, -z)',
905 '(1/4, 3/4, z)', '(3/4, 1/4, z)')),
906 '8n': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y, z)',
907 '(1/2, -y+1/2, z)', '(0, y, -z)', '(1/2, y+1/2, -z)',
908 '(0, -y, -z)', '(1/2, -y+1/2, -z)')),
909 '8o': (5, ('(x, 0, z)', '(x+1/2, 1/2, z)', '(-x, 0, z)',
910 '(-x+1/2, 1/2, z)', '(-x, 0, -z)',
911 '(-x+1/2, 1/2, -z)', '(x, 0, -z)', '(x+1/2, 1/2, -z)'
912 )),
913 '8p': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 0)', '(-x, -y, 0)',
914 '(-x+1/2, -y+1/2, 0)', '(-x, y, 0)',
915 '(-x+1/2, y+1/2, 0)', '(x, -y, 0)',
916 '(x+1/2, -y+1/2, 0)')),
917 '8q': (3, ('(x, y, 1/2)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 1/2)',
918 '(-x+1/2, -y+1/2, 1/2)', '(-x, y, 1/2)',
919 '(-x+1/2, y+1/2, 1/2)', '(x, -y, 1/2)',
920 '(x+1/2, -y+1/2, 1/2)')),
921 '16r': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z)',
922 '(-x+1/2, -y+1/2, z)', '(-x, y, -z)',
923 '(-x+1/2, y+1/2, -z)', '(x, -y, -z)',
924 '(x+1/2, -y+1/2, -z)', '(-x, -y, -z)',
925 '(-x+1/2, -y+1/2, -z)', '(x, y, -z)',
926 '(x+1/2, y+1/2, -z)', '(x, -y, z)',
927 '(x+1/2, -y+1/2, z)', '(-x, y, z)',
928 '(-x+1/2, y+1/2, z)'))},
929 '66': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 1/4)', '(0, 0, 3/4)',
930 '(1/2, 1/2, 3/4)')),
931 '4b': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 1/4)', '(0, 1/2, 3/4)',
932 '(1/2, 0, 3/4)')),
933 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)',
934 '(1/2, 1/2, 1/2)')),
935 '4d': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
936 '(1/2, 0, 1/2)')),
937 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 1/2)',
938 '(1/4, 3/4, 1/2)')),
939 '4f': (0, ('(1/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
940 '(1/4, 1/4, 1/2)')),
941 '8g': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 1/4)', '(-x, 0, 1/4)',
942 '(-x+1/2, 1/2, 1/4)', '(-x, 0, 3/4)',
943 '(-x+1/2, 1/2, 3/4)', '(x, 0, 3/4)',
944 '(x+1/2, 1/2, 3/4)')),
945 '8h': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 1/4)', '(0, -y, 1/4)',
946 '(1/2, -y+1/2, 1/4)', '(0, -y, 3/4)',
947 '(1/2, -y+1/2, 3/4)', '(0, y, 3/4)',
948 '(1/2, y+1/2, 3/4)')),
949 '8i': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(0, 0, -z+1/2)',
950 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)', '(1/2, 1/2, -z)',
951 '(0, 0, z+1/2)', '(1/2, 1/2, z+1/2)')),
952 '8j': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z+1/2)',
953 '(1/2, 0, -z+1/2)', '(0, 1/2, -z)', '(1/2, 0, -z)',
954 '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)')),
955 '8k': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)',
956 '(3/4, 1/4, -z+1/2)', '(1/4, 3/4, -z+1/2)',
957 '(3/4, 3/4, -z)', '(1/4, 1/4, -z)',
958 '(1/4, 3/4, z+1/2)', '(3/4, 1/4, z+1/2)')),
959 '8l': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 0)', '(-x, -y, 0)',
960 '(-x+1/2, -y+1/2, 0)', '(-x, y, 1/2)',
961 '(-x+1/2, y+1/2, 1/2)', '(x, -y, 1/2)',
962 '(x+1/2, -y+1/2, 1/2)')),
963 '16m': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z)',
964 '(-x+1/2, -y+1/2, z)', '(-x, y, -z+1/2)',
965 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z+1/2)',
966 '(x+1/2, -y+1/2, -z+1/2)', '(-x, -y, -z)',
967 '(-x+1/2, -y+1/2, -z)', '(x, y, -z)',
968 '(x+1/2, y+1/2, -z)', '(x, -y, z+1/2)',
969 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z+1/2)',
970 '(-x+1/2, y+1/2, z+1/2)'))},
971 '67': {'4a': (0, ('(1/4, 0, 0)', '(3/4, 1/2, 0)', '(3/4, 0, 0)',
972 '(1/4, 1/2, 0)')),
973 '4b': (0, ('(1/4, 0, 1/2)', '(3/4, 1/2, 1/2)', '(3/4, 0, 1/2)',
974 '(1/4, 1/2, 1/2)')),
975 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 0)',
976 '(1/2, 0, 0)')),
977 '4d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/2)',
978 '(1/2, 0, 1/2)')),
979 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 0)',
980 '(1/4, 3/4, 0)')),
981 '4f': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
982 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
983 '4g': (4, ('(0, 1/4, z)', '(1/2, 3/4, z)', '(0, 3/4, -z)',
984 '(1/2, 1/4, -z)')),
985 '8h': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 1/2, 0)',
986 '(-x+1/2, 0, 0)', '(-x, 0, 0)', '(-x+1/2, 1/2, 0)',
987 '(x, 1/2, 0)', '(x+1/2, 0, 0)')),
988 '8i': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 1/2)', '(-x, 1/2, 1/2)',
989 '(-x+1/2, 0, 1/2)', '(-x, 0, 1/2)',
990 '(-x+1/2, 1/2, 1/2)', '(x, 1/2, 1/2)',
991 '(x+1/2, 0, 1/2)')),
992 '8j': (2, ('(1/4, y, 0)', '(3/4, y+1/2, 0)', '(3/4, -y+1/2, 0)',
993 '(1/4, -y, 0)', '(3/4, -y, 0)', '(1/4, -y+1/2, 0)',
994 '(1/4, y+1/2, 0)', '(3/4, y, 0)')),
995 '8k': (2, ('(1/4, y, 1/2)', '(3/4, y+1/2, 1/2)',
996 '(3/4, -y+1/2, 1/2)', '(1/4, -y, 1/2)',
997 '(3/4, -y, 1/2)', '(1/4, -y+1/2, 1/2)',
998 '(1/4, y+1/2, 1/2)', '(3/4, y, 1/2)')),
999 '8l': (4, ('(1/4, 0, z)', '(3/4, 1/2, z)', '(3/4, 1/2, -z)',
1000 '(1/4, 0, -z)', '(3/4, 0, -z)', '(1/4, 1/2, -z)',
1001 '(1/4, 1/2, z)', '(3/4, 0, z)')),
1002 '8m': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y+1/2, z)',
1003 '(1/2, -y, z)', '(0, y+1/2, -z)', '(1/2, y, -z)',
1004 '(0, -y, -z)', '(1/2, -y+1/2, -z)')),
1005 '8n': (5, ('(x, 1/4, z)', '(x+1/2, 3/4, z)', '(-x, 1/4, z)',
1006 '(-x+1/2, 3/4, z)', '(-x, 3/4, -z)',
1007 '(-x+1/2, 1/4, -z)', '(x, 3/4, -z)',
1008 '(x+1/2, 1/4, -z)')),
1009 '16o': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y+1/2, z)',
1010 '(-x+1/2, -y, z)', '(-x, y+1/2, -z)',
1011 '(-x+1/2, y, -z)', '(x, -y, -z)',
1012 '(x+1/2, -y+1/2, -z)', '(-x, -y, -z)',
1013 '(-x+1/2, -y+1/2, -z)', '(x, y+1/2, -z)',
1014 '(x+1/2, y, -z)', '(x, -y+1/2, z)', '(x+1/2, -y, z)',
1015 '(-x, y, z)', '(-x+1/2, y+1/2, z)'))},
1016 '68:1': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
1017 '(1/2, 0, 1/2)')),
1018 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(0, 1/2, 0)',
1019 '(1/2, 0, 0)')),
1020 '8c': (0, ('(1/4, 0, 1/4)', '(3/4, 1/2, 1/4)',
1021 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)', '(3/4, 0, 3/4)',
1022 '(1/4, 1/2, 3/4)', '(3/4, 1/2, 3/4)',
1023 '(1/4, 0, 3/4)')),
1024 '8d': (0, ('(0, 1/4, 1/4)', '(1/2, 3/4, 1/4)',
1025 '(1/2, 1/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
1026 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
1027 '(0, 3/4, 3/4)')),
1028 '8e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x+1/2, 1/2, 0)',
1029 '(-x, 0, 0)', '(-x, 1/2, 1/2)', '(-x+1/2, 0, 1/2)',
1030 '(x+1/2, 0, 1/2)', '(x, 1/2, 1/2)')),
1031 '8f': (2, ('(0, y, 0)', '(1/2, y+1/2, 0)', '(1/2, -y+1/2, 0)',
1032 '(0, -y, 0)', '(0, -y+1/2, 1/2)', '(1/2, -y, 1/2)',
1033 '(1/2, y, 1/2)', '(0, y+1/2, 1/2)')),
1034 '8g': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(0, 0, -z)',
1035 '(1/2, 1/2, -z)', '(0, 1/2, -z+1/2)',
1036 '(1/2, 0, -z+1/2)', '(0, 1/2, z+1/2)',
1037 '(1/2, 0, z+1/2)')),
1038 '8h': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)', '(3/4, 1/4, -z)',
1039 '(1/4, 3/4, -z)', '(3/4, 1/4, -z+1/2)',
1040 '(1/4, 3/4, -z+1/2)', '(1/4, 1/4, z+1/2)',
1041 '(3/4, 3/4, z+1/2)')),
1042 '16i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)',
1043 '(-x+1/2, -y+1/2, z)', '(-x, -y, z)',
1044 '(-x, y, -z)', '(-x+1/2, y+1/2, -z)',
1045 '(x+1/2, -y+1/2, -z)', '(x, -y, -z)',
1046 '(-x, -y+1/2, -z+1/2)', '(-x+1/2, -y, -z+1/2)',
1047 '(x+1/2, y, -z+1/2)', '(x, y+1/2, -z+1/2)',
1048 '(x, -y+1/2, z+1/2)', '(x+1/2, -y, z+1/2)',
1049 '(-x+1/2, y, z+1/2)', '(-x, y+1/2, z+1/2)'))},
1050 '68:2': {'4a': (0, ('(0, 1/4, 1/4)', '(1/2, 3/4, 1/4)', '(0, 3/4, 3/4)',
1051 '(1/2, 1/4, 3/4)')),
1052 '4b': (0, ('(0, 1/4, 3/4)', '(1/2, 3/4, 3/4)', '(0, 3/4, 1/4)',
1053 '(1/2, 1/4, 1/4)')),
1054 '8c': (0, ('(1/4, 3/4, 0)', '(3/4, 1/4, 0)', '(1/4, 1/4, 0)',
1055 '(3/4, 3/4, 0)', '(3/4, 3/4, 1/2)',
1056 '(1/4, 1/4, 1/2)', '(3/4, 1/4, 1/2)',
1057 '(1/4, 3/4, 1/2)')),
1058 '8d': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
1059 '(0, 1/2, 0)', '(0, 0, 1/2)', '(1/2, 1/2, 1/2)',
1060 '(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
1061 '8e': (1, ('(x, 1/4, 1/4)', '(x+1/2, 3/4, 1/4)',
1062 '(-x+1/2, 3/4, 1/4)', '(-x, 1/4, 1/4)',
1063 '(-x, 3/4, 3/4)', '(-x+1/2, 1/4, 3/4)',
1064 '(x+1/2, 1/4, 3/4)', '(x, 3/4, 3/4)')),
1065 '8f': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 1/4)',
1066 '(1/2, -y, 1/4)', '(0, -y+1/2, 1/4)',
1067 '(0, -y, 3/4)', '(1/2, -y+1/2, 3/4)',
1068 '(1/2, y, 3/4)', '(0, y+1/2, 3/4)')),
1069 '8g': (4, ('(0, 1/4, z)', '(1/2, 3/4, z)', '(0, 1/4, -z+1/2)',
1070 '(1/2, 3/4, -z+1/2)', '(0, 3/4, -z)',
1071 '(1/2, 1/4, -z)', '(0, 3/4, z+1/2)',
1072 '(1/2, 1/4, z+1/2)')),
1073 '8h': (4, ('(1/4, 0, z)', '(3/4, 1/2, z)', '(3/4, 0, -z+1/2)',
1074 '(1/4, 1/2, -z+1/2)', '(3/4, 0, -z)',
1075 '(1/4, 1/2, -z)', '(1/4, 0, z+1/2)',
1076 '(3/4, 1/2, z+1/2)')),
1077 '16i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)',
1078 '(-x+1/2, -y, z)', '(-x, -y+1/2, z)',
1079 '(-x, y, -z+1/2)', '(-x+1/2, y+1/2, -z+1/2)',
1080 '(x+1/2, -y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
1081 '(-x, -y, -z)', '(-x+1/2, -y+1/2, -z)',
1082 '(x+1/2, y, -z)', '(x, y+1/2, -z)',
1083 '(x, -y, z+1/2)', '(x+1/2, -y+1/2, z+1/2)',
1084 '(-x+1/2, y, z+1/2)', '(-x, y+1/2, z+1/2)'))},
1085 '69': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
1086 '(1/2, 1/2, 0)')),
1087 '4b': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 0)',
1088 '(1/2, 1/2, 1/2)')),
1089 '8c': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
1090 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
1091 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)')),
1092 '8d': (0, ('(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
1093 '(3/4, 1/2, 1/4)', '(3/4, 0, 1/4)', '(3/4, 1/2, 3/4)',
1094 '(1/4, 0, 3/4)', '(1/4, 1/2, 1/4)')),
1095 '8e': (0, ('(1/4, 1/4, 0)', '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
1096 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
1097 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
1098 '8f': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
1099 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
1100 '(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
1101 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
1102 '8g': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
1103 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
1104 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)')),
1105 '8h': (2, ('(0, y, 0)', '(0, y+1/2, 1/2)', '(1/2, y, 1/2)',
1106 '(1/2, y+1/2, 0)', '(0, -y, 0)', '(0, -y+1/2, 1/2)',
1107 '(1/2, -y, 1/2)', '(1/2, -y+1/2, 0)')),
1108 '8i': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
1109 '(1/2, 1/2, z)', '(0, 0, -z)', '(0, 1/2, -z+1/2)',
1110 '(1/2, 0, -z+1/2)', '(1/2, 1/2, -z)')),
1111 '16j': (4, ('(1/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
1112 '(3/4, 1/4, z+1/2)', '(3/4, 3/4, z)',
1113 '(3/4, 1/4, -z)', '(3/4, 3/4, -z+1/2)',
1114 '(1/4, 1/4, -z+1/2)', '(1/4, 3/4, -z)',
1115 '(3/4, 3/4, -z)', '(3/4, 1/4, -z+1/2)',
1116 '(1/4, 3/4, -z+1/2)', '(1/4, 1/4, -z)',
1117 '(1/4, 3/4, z)', '(1/4, 1/4, z+1/2)',
1118 '(3/4, 3/4, z+1/2)', '(3/4, 1/4, z)')),
1119 '16k': (2, ('(1/4, y, 1/4)', '(1/4, y+1/2, 3/4)',
1120 '(3/4, y, 3/4)', '(3/4, y+1/2, 1/4)',
1121 '(3/4, -y, 1/4)', '(3/4, -y+1/2, 3/4)',
1122 '(1/4, -y, 3/4)', '(1/4, -y+1/2, 1/4)',
1123 '(3/4, -y, 3/4)', '(3/4, -y+1/2, 1/4)',
1124 '(1/4, -y, 1/4)', '(1/4, -y+1/2, 3/4)',
1125 '(1/4, y, 3/4)', '(1/4, y+1/2, 1/4)',
1126 '(3/4, y, 1/4)', '(3/4, y+1/2, 3/4)')),
1127 '16l': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
1128 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
1129 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
1130 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
1131 '(-x, 3/4, 3/4)', '(-x, 1/4, 1/4)',
1132 '(-x+1/2, 3/4, 1/4)', '(-x+1/2, 1/4, 3/4)',
1133 '(x, 1/4, 3/4)', '(x, 3/4, 1/4)',
1134 '(x+1/2, 1/4, 1/4)', '(x+1/2, 3/4, 3/4)')),
1135 '16m': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(1/2, y, z+1/2)',
1136 '(1/2, y+1/2, z)', '(0, -y, z)',
1137 '(0, -y+1/2, z+1/2)', '(1/2, -y, z+1/2)',
1138 '(1/2, -y+1/2, z)', '(0, y, -z)',
1139 '(0, y+1/2, -z+1/2)', '(1/2, y, -z+1/2)',
1140 '(1/2, y+1/2, -z)', '(0, -y, -z)',
1141 '(0, -y+1/2, -z+1/2)', '(1/2, -y, -z+1/2)',
1142 '(1/2, -y+1/2, -z)')),
1143 '16n': (5, ('(x, 0, z)', '(x, 1/2, z+1/2)', '(x+1/2, 0, z+1/2)',
1144 '(x+1/2, 1/2, z)', '(-x, 0, z)', '(-x, 1/2, z+1/2)',
1145 '(-x+1/2, 0, z+1/2)', '(-x+1/2, 1/2, z)',
1146 '(-x, 0, -z)', '(-x, 1/2, -z+1/2)',
1147 '(-x+1/2, 0, -z+1/2)', '(-x+1/2, 1/2, -z)',
1148 '(x, 0, -z)', '(x, 1/2, -z+1/2)',
1149 '(x+1/2, 0, -z+1/2)', '(x+1/2, 1/2, -z)')),
1150 '16o': (3, ('(x, y, 0)', '(x, y+1/2, 1/2)', '(x+1/2, y, 1/2)',
1151 '(x+1/2, y+1/2, 0)', '(-x, -y, 0)',
1152 '(-x, -y+1/2, 1/2)', '(-x+1/2, -y, 1/2)',
1153 '(-x+1/2, -y+1/2, 0)', '(-x, y, 0)',
1154 '(-x, y+1/2, 1/2)', '(-x+1/2, y, 1/2)',
1155 '(-x+1/2, y+1/2, 0)', '(x, -y, 0)',
1156 '(x, -y+1/2, 1/2)', '(x+1/2, -y, 1/2)',
1157 '(x+1/2, -y+1/2, 0)')),
1158 '32p': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
1159 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
1160 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
1161 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
1162 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
1163 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
1164 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
1165 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
1166 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
1167 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
1168 '(x, y, -z)', '(x, y+1/2, -z+1/2)',
1169 '(x+1/2, y, -z+1/2)', '(x+1/2, y+1/2, -z)',
1170 '(x, -y, z)', '(x, -y+1/2, z+1/2)',
1171 '(x+1/2, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
1172 '(-x, y, z)', '(-x, y+1/2, z+1/2)',
1173 '(-x+1/2, y, z+1/2)', '(-x+1/2, y+1/2, z)'))},
1174 '70:1': {'8a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
1175 '(1/2, 1/2, 0)', '(1/4, 1/4, 1/4)',
1176 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
1177 '(3/4, 3/4, 1/4)')),
1178 '8b': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 0)',
1179 '(1/2, 1/2, 1/2)', '(1/4, 1/4, 3/4)',
1180 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)',
1181 '(3/4, 3/4, 3/4)')),
1182 '16c': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
1183 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
1184 '(7/8, 7/8, 1/8)', '(7/8, 3/8, 5/8)',
1185 '(3/8, 7/8, 5/8)', '(3/8, 3/8, 1/8)',
1186 '(7/8, 1/8, 7/8)', '(7/8, 5/8, 3/8)',
1187 '(3/8, 1/8, 3/8)', '(3/8, 5/8, 7/8)',
1188 '(1/8, 7/8, 7/8)', '(1/8, 3/8, 3/8)',
1189 '(5/8, 7/8, 3/8)', '(5/8, 3/8, 7/8)')),
1190 '16d': (0, ('(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
1191 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
1192 '(3/8, 3/8, 5/8)', '(3/8, 7/8, 1/8)',
1193 '(7/8, 3/8, 1/8)', '(7/8, 7/8, 5/8)',
1194 '(3/8, 5/8, 3/8)', '(3/8, 1/8, 7/8)',
1195 '(7/8, 5/8, 7/8)', '(7/8, 1/8, 3/8)',
1196 '(5/8, 3/8, 3/8)', '(5/8, 7/8, 7/8)',
1197 '(1/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)')),
1198 '16e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
1199 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
1200 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)',
1201 '(-x+1/4, 1/4, 1/4)', '(-x+1/4, 3/4, 3/4)',
1202 '(-x+3/4, 1/4, 3/4)', '(-x+3/4, 3/4, 1/4)',
1203 '(x+1/4, 1/4, 1/4)', '(x+1/4, 3/4, 3/4)',
1204 '(x+3/4, 1/4, 3/4)', '(x+3/4, 3/4, 1/4)')),
1205 '16f': (2, ('(0, y, 0)', '(0, y+1/2, 1/2)', '(1/2, y, 1/2)',
1206 '(1/2, y+1/2, 0)', '(0, -y, 0)',
1207 '(0, -y+1/2, 1/2)', '(1/2, -y, 1/2)',
1208 '(1/2, -y+1/2, 0)', '(1/4, -y+1/4, 1/4)',
1209 '(1/4, -y+3/4, 3/4)', '(3/4, -y+1/4, 3/4)',
1210 '(3/4, -y+3/4, 1/4)', '(1/4, y+1/4, 1/4)',
1211 '(1/4, y+3/4, 3/4)', '(3/4, y+1/4, 3/4)',
1212 '(3/4, y+3/4, 1/4)')),
1213 '16g': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
1214 '(1/2, 1/2, z)', '(0, 0, -z)', '(0, 1/2, -z+1/2)',
1215 '(1/2, 0, -z+1/2)', '(1/2, 1/2, -z)',
1216 '(1/4, 1/4, -z+1/4)', '(1/4, 3/4, -z+3/4)',
1217 '(3/4, 1/4, -z+3/4)', '(3/4, 3/4, -z+1/4)',
1218 '(1/4, 1/4, z+1/4)', '(1/4, 3/4, z+3/4)',
1219 '(3/4, 1/4, z+3/4)', '(3/4, 3/4, z+1/4)')),
1220 '32h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
1221 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
1222 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
1223 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
1224 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
1225 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
1226 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
1227 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
1228 '(-x+1/4, -y+1/4, -z+1/4)',
1229 '(-x+1/4, -y+3/4, -z+3/4)',
1230 '(-x+3/4, -y+1/4, -z+3/4)',
1231 '(-x+3/4, -y+3/4, -z+1/4)',
1232 '(x+1/4, y+1/4, -z+1/4)', '(x+1/4, y+3/4, -z+3/4)',
1233 '(x+3/4, y+1/4, -z+3/4)', '(x+3/4, y+3/4, -z+1/4)',
1234 '(x+1/4, -y+1/4, z+1/4)', '(x+1/4, -y+3/4, z+3/4)',
1235 '(x+3/4, -y+1/4, z+3/4)', '(x+3/4, -y+3/4, z+1/4)',
1236 '(-x+1/4, y+1/4, z+1/4)', '(-x+1/4, y+3/4, z+3/4)',
1237 '(-x+3/4, y+1/4, z+3/4)', '(-x+3/4, y+3/4, z+1/4)'
1238 ))},
1239 '70:2': {'8a': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
1240 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
1241 '(7/8, 7/8, 7/8)', '(7/8, 3/8, 3/8)',
1242 '(3/8, 7/8, 3/8)', '(3/8, 3/8, 7/8)')),
1243 '8b': (0, ('(1/8, 1/8, 5/8)', '(1/8, 5/8, 1/8)',
1244 '(5/8, 1/8, 1/8)', '(5/8, 5/8, 5/8)',
1245 '(7/8, 7/8, 3/8)', '(7/8, 3/8, 7/8)',
1246 '(3/8, 7/8, 7/8)', '(3/8, 3/8, 3/8)')),
1247 '16c': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
1248 '(1/2, 1/2, 0)', '(3/4, 3/4, 0)',
1249 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)',
1250 '(1/4, 1/4, 0)', '(3/4, 0, 3/4)',
1251 '(3/4, 1/2, 1/4)', '(1/4, 0, 1/4)',
1252 '(1/4, 1/2, 3/4)', '(0, 3/4, 3/4)',
1253 '(0, 1/4, 1/4)', '(1/2, 3/4, 1/4)',
1254 '(1/2, 1/4, 3/4)')),
1255 '16d': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
1256 '(0, 0, 1/2)', '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)',
1257 '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
1258 '(1/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
1259 '(3/4, 1/2, 3/4)', '(3/4, 0, 1/4)',
1260 '(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)',
1261 '(0, 1/4, 3/4)', '(0, 3/4, 1/4)')),
1262 '16e': (1, ('(x, 1/8, 1/8)', '(x, 5/8, 5/8)',
1263 '(x+1/2, 1/8, 5/8)', '(x+1/2, 5/8, 1/8)',
1264 '(-x+3/4, 5/8, 1/8)', '(-x+3/4, 1/8, 5/8)',
1265 '(-x+1/4, 5/8, 5/8)', '(-x+1/4, 1/8, 1/8)',
1266 '(-x, 7/8, 7/8)', '(-x, 3/8, 3/8)',
1267 '(-x+1/2, 7/8, 3/8)', '(-x+1/2, 3/8, 7/8)',
1268 '(x+1/4, 3/8, 7/8)', '(x+1/4, 7/8, 3/8)',
1269 '(x+3/4, 3/8, 3/8)', '(x+3/4, 7/8, 7/8)')),
1270 '16f': (2, ('(1/8, y, 1/8)', '(1/8, y+1/2, 5/8)',
1271 '(5/8, y, 5/8)', '(5/8, y+1/2, 1/8)',
1272 '(5/8, -y+3/4, 1/8)', '(5/8, -y+1/4, 5/8)',
1273 '(1/8, -y+3/4, 5/8)', '(1/8, -y+1/4, 1/8)',
1274 '(7/8, -y, 7/8)', '(7/8, -y+1/2, 3/8)',
1275 '(3/8, -y, 3/8)', '(3/8, -y+1/2, 7/8)',
1276 '(3/8, y+1/4, 7/8)', '(3/8, y+3/4, 3/8)',
1277 '(7/8, y+1/4, 3/8)', '(7/8, y+3/4, 7/8)')),
1278 '16g': (4, ('(1/8, 1/8, z)', '(1/8, 5/8, z+1/2)',
1279 '(5/8, 1/8, z+1/2)', '(5/8, 5/8, z)',
1280 '(5/8, 1/8, -z+3/4)', '(5/8, 5/8, -z+1/4)',
1281 '(1/8, 1/8, -z+1/4)', '(1/8, 5/8, -z+3/4)',
1282 '(7/8, 7/8, -z)', '(7/8, 3/8, -z+1/2)',
1283 '(3/8, 7/8, -z+1/2)', '(3/8, 3/8, -z)',
1284 '(3/8, 7/8, z+1/4)', '(3/8, 3/8, z+3/4)',
1285 '(7/8, 7/8, z+3/4)', '(7/8, 3/8, z+1/4)')),
1286 '32h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
1287 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
1288 '(-x+3/4, -y+3/4, z)', '(-x+3/4, -y+1/4, z+1/2)',
1289 '(-x+1/4, -y+3/4, z+1/2)', '(-x+1/4, -y+1/4, z)',
1290 '(-x+3/4, y, -z+3/4)', '(-x+3/4, y+1/2, -z+1/4)',
1291 '(-x+1/4, y, -z+1/4)', '(-x+1/4, y+1/2, -z+3/4)',
1292 '(x, -y+3/4, -z+3/4)', '(x, -y+1/4, -z+1/4)',
1293 '(x+1/2, -y+3/4, -z+1/4)',
1294 '(x+1/2, -y+1/4, -z+3/4)', '(-x, -y, -z)',
1295 '(-x, -y+1/2, -z+1/2)', '(-x+1/2, -y, -z+1/2)',
1296 '(-x+1/2, -y+1/2, -z)', '(x+1/4, y+1/4, -z)',
1297 '(x+1/4, y+3/4, -z+1/2)', '(x+3/4, y+1/4, -z+1/2)',
1298 '(x+3/4, y+3/4, -z)', '(x+1/4, -y, z+1/4)',
1299 '(x+1/4, -y+1/2, z+3/4)', '(x+3/4, -y, z+3/4)',
1300 '(x+3/4, -y+1/2, z+1/4)', '(-x, y+1/4, z+1/4)',
1301 '(-x, y+3/4, z+3/4)', '(-x+1/2, y+1/4, z+3/4)',
1302 '(-x+1/2, y+3/4, z+1/4)'))},
1303 '71': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1304 '2b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)')),
1305 '2c': (0, ('(1/2, 1/2, 0)', '(0, 0, 1/2)')),
1306 '2d': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 0)')),
1307 '4e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
1308 '(-x+1/2, 1/2, 1/2)')),
1309 '4f': (1, ('(x, 1/2, 0)', '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
1310 '(-x+1/2, 0, 1/2)')),
1311 '4g': (2, ('(0, y, 0)', '(1/2, y+1/2, 1/2)', '(0, -y, 0)',
1312 '(1/2, -y+1/2, 1/2)')),
1313 '4h': (2, ('(0, y, 1/2)', '(1/2, y+1/2, 0)', '(0, -y, 1/2)',
1314 '(1/2, -y+1/2, 0)')),
1315 '4i': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
1316 '(1/2, 1/2, -z+1/2)')),
1317 '4j': (4, ('(1/2, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, -z)',
1318 '(0, 1/2, -z+1/2)')),
1319 '8k': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
1320 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
1321 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
1322 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
1323 '8l': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y, z)',
1324 '(1/2, -y+1/2, z+1/2)', '(0, y, -z)',
1325 '(1/2, y+1/2, -z+1/2)', '(0, -y, -z)',
1326 '(1/2, -y+1/2, -z+1/2)')),
1327 '8m': (5, ('(x, 0, z)', '(x+1/2, 1/2, z+1/2)', '(-x, 0, z)',
1328 '(-x+1/2, 1/2, z+1/2)', '(-x, 0, -z)',
1329 '(-x+1/2, 1/2, -z+1/2)', '(x, 0, -z)',
1330 '(x+1/2, 1/2, -z+1/2)')),
1331 '8n': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 0)',
1332 '(-x+1/2, -y+1/2, 1/2)', '(-x, y, 0)',
1333 '(-x+1/2, y+1/2, 1/2)', '(x, -y, 0)',
1334 '(x+1/2, -y+1/2, 1/2)')),
1335 '16o': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1336 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
1337 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
1338 '(x+1/2, -y+1/2, -z+1/2)', '(-x, -y, -z)',
1339 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
1340 '(x+1/2, y+1/2, -z+1/2)', '(x, -y, z)',
1341 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
1342 '(-x+1/2, y+1/2, z+1/2)'))},
1343 '72': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)', '(0, 0, 3/4)',
1344 '(1/2, 1/2, 1/4)')),
1345 '4b': (0, ('(1/2, 0, 1/4)', '(0, 1/2, 3/4)', '(1/2, 0, 3/4)',
1346 '(0, 1/2, 1/4)')),
1347 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 1/2, 0)',
1348 '(0, 0, 1/2)')),
1349 '4d': (0, ('(1/2, 0, 0)', '(0, 1/2, 1/2)', '(0, 1/2, 0)',
1350 '(1/2, 0, 1/2)')),
1351 '8e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
1352 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
1353 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
1354 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)')),
1355 '8f': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)', '(-x, 0, 1/4)',
1356 '(-x+1/2, 1/2, 3/4)', '(-x, 0, 3/4)',
1357 '(-x+1/2, 1/2, 1/4)', '(x, 0, 3/4)',
1358 '(x+1/2, 1/2, 1/4)')),
1359 '8g': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 3/4)', '(0, -y, 1/4)',
1360 '(1/2, -y+1/2, 3/4)', '(0, -y, 3/4)',
1361 '(1/2, -y+1/2, 1/4)', '(0, y, 3/4)',
1362 '(1/2, y+1/2, 1/4)')),
1363 '8h': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 1/2, -z)',
1364 '(0, 0, -z+1/2)', '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
1365 '(1/2, 1/2, z)', '(0, 0, z+1/2)')),
1366 '8i': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
1367 '(0, 1/2, -z+1/2)', '(0, 1/2, -z)',
1368 '(1/2, 0, -z+1/2)', '(1/2, 0, z)', '(0, 1/2, z+1/2)'
1369 )),
1370 '8j': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 0)',
1371 '(-x+1/2, -y+1/2, 1/2)', '(-x+1/2, y+1/2, 0)',
1372 '(-x, y, 1/2)', '(x+1/2, -y+1/2, 0)', '(x, -y, 1/2)'
1373 )),
1374 '16k': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1375 '(-x+1/2, -y+1/2, z+1/2)', '(-x+1/2, y+1/2, -z)',
1376 '(-x, y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
1377 '(x, -y, -z+1/2)', '(-x, -y, -z)',
1378 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
1379 '(x+1/2, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, z)',
1380 '(x, -y, z+1/2)', '(-x+1/2, y+1/2, z)',
1381 '(-x, y, z+1/2)'))},
1382 '73': {'8a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
1383 '(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 0)',
1384 '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
1385 '8b': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
1386 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
1387 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
1388 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)')),
1389 '8c': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
1390 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(-x, 0, 3/4)',
1391 '(-x+1/2, 1/2, 1/4)', '(x+1/2, 0, 1/4)',
1392 '(x, 1/2, 3/4)')),
1393 '8d': (2, ('(1/4, y, 0)', '(3/4, y+1/2, 1/2)', '(1/4, -y, 1/2)',
1394 '(3/4, -y+1/2, 0)', '(3/4, -y, 0)',
1395 '(1/4, -y+1/2, 1/2)', '(3/4, y, 1/2)',
1396 '(1/4, y+1/2, 0)')),
1397 '8e': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)',
1398 '(0, 3/4, -z+1/2)', '(1/2, 1/4, -z)', '(0, 3/4, -z)',
1399 '(1/2, 1/4, -z+1/2)', '(0, 1/4, z+1/2)',
1400 '(1/2, 3/4, z)')),
1401 '16f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1402 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
1403 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
1404 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
1405 '(-x, -y, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
1406 '(x+1/2, y, -z+1/2)', '(x, y+1/2, -z)',
1407 '(x, -y+1/2, z+1/2)', '(x+1/2, -y, z)',
1408 '(-x+1/2, y+1/2, z)', '(-x, y, z+1/2)'))},
1409 '74': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 1/2, 0)',
1410 '(1/2, 0, 1/2)')),
1411 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
1412 '(1/2, 0, 0)')),
1413 '4c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
1414 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)')),
1415 '4d': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
1416 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)')),
1417 '4e': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)', '(0, 3/4, -z)',
1418 '(1/2, 1/4, -z+1/2)')),
1419 '8f': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 1/2, 0)',
1420 '(-x+1/2, 0, 1/2)', '(-x, 0, 0)',
1421 '(-x+1/2, 1/2, 1/2)', '(x, 1/2, 0)',
1422 '(x+1/2, 0, 1/2)')),
1423 '8g': (2, ('(1/4, y, 1/4)', '(3/4, y+1/2, 3/4)',
1424 '(3/4, -y+1/2, 1/4)', '(1/4, -y, 3/4)',
1425 '(3/4, -y, 3/4)', '(1/4, -y+1/2, 1/4)',
1426 '(1/4, y+1/2, 3/4)', '(3/4, y, 1/4)')),
1427 '8h': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y+1/2, z)',
1428 '(1/2, -y, z+1/2)', '(0, y+1/2, -z)',
1429 '(1/2, y, -z+1/2)', '(0, -y, -z)',
1430 '(1/2, -y+1/2, -z+1/2)')),
1431 '8i': (5, ('(x, 1/4, z)', '(x+1/2, 3/4, z+1/2)', '(-x, 1/4, z)',
1432 '(-x+1/2, 3/4, z+1/2)', '(-x, 3/4, -z)',
1433 '(-x+1/2, 1/4, -z+1/2)', '(x, 3/4, -z)',
1434 '(x+1/2, 1/4, -z+1/2)')),
1435 '16j': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1436 '(-x, -y+1/2, z)', '(-x+1/2, -y, z+1/2)',
1437 '(-x, y+1/2, -z)', '(-x+1/2, y, -z+1/2)',
1438 '(x, -y, -z)', '(x+1/2, -y+1/2, -z+1/2)',
1439 '(-x, -y, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
1440 '(x, y+1/2, -z)', '(x+1/2, y, -z+1/2)',
1441 '(x, -y+1/2, z)', '(x+1/2, -y, z+1/2)', '(-x, y, z)',
1442 '(-x+1/2, y+1/2, z+1/2)'))},
1443 '75': {'1a': (4, ('(0, 0, z)', )),
1444 '1b': (4, ('(1/2, 1/2, z)', )),
1445 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, z)')),
1446 '4d': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1447 '(y, -x, z)'))},
1448 '76': {'4a': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-y, x, z+1/4)',
1449 '(y, -x, z+3/4)'))},
1450 '77': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
1451 '2b': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
1452 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)')),
1453 '4d': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1454 '(y, -x, z+1/2)'))},
1455 '78': {'4a': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-y, x, z+3/4)',
1456 '(y, -x, z+1/4)'))},
1457 '79': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
1458 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
1459 '(0, 1/2, z+1/2)')),
1460 '8c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1461 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
1462 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
1463 '(y+1/2, -x+1/2, z+1/2)'))},
1464 '80': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 1/2, z+1/4)',
1465 '(1/2, 0, z+3/4)')),
1466 '8b': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1467 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
1468 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
1469 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)'))},
1470 '81': {'1a': (0, ('(0, 0, 0)', )),
1471 '1b': (0, ('(0, 0, 1/2)', )),
1472 '1c': (0, ('(1/2, 1/2, 0)', )),
1473 '1d': (0, ('(1/2, 1/2, 1/2)', )),
1474 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
1475 '2f': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
1476 '2g': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
1477 '4h': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
1478 '(-y, x, -z)'))},
1479 '82': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1480 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
1481 '2c': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)')),
1482 '2d': (0, ('(0, 1/2, 3/4)', '(1/2, 0, 1/4)')),
1483 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
1484 '(1/2, 1/2, -z+1/2)')),
1485 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
1486 '(0, 1/2, -z+1/2)')),
1487 '8g': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1488 '(-x+1/2, -y+1/2, z+1/2)', '(y, -x, -z)',
1489 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
1490 '(-y+1/2, x+1/2, -z+1/2)'))},
1491 '83': {'1a': (0, ('(0, 0, 0)', )),
1492 '1b': (0, ('(0, 0, 1/2)', )),
1493 '1c': (0, ('(1/2, 1/2, 0)', )),
1494 '1d': (0, ('(1/2, 1/2, 1/2)', )),
1495 '2e': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
1496 '2f': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
1497 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
1498 '2h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
1499 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
1500 '(1/2, 0, -z)')),
1501 '4j': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 0)',
1502 '(y, -x, 0)')),
1503 '4k': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)', '(-y, x, 1/2)',
1504 '(y, -x, 1/2)')),
1505 '8l': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1506 '(y, -x, z)', '(-x, -y, -z)', '(x, y, -z)',
1507 '(y, -x, -z)', '(-y, x, -z)'))},
1508 '84': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
1509 '2b': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
1510 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
1511 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)')),
1512 '2e': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
1513 '2f': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
1514 '4g': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
1515 '(0, 0, -z+1/2)')),
1516 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)',
1517 '(1/2, 1/2, -z)', '(1/2, 1/2, -z+1/2)')),
1518 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, -z)',
1519 '(1/2, 0, -z+1/2)')),
1520 '4j': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 1/2)',
1521 '(y, -x, 1/2)')),
1522 '8k': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1523 '(y, -x, z+1/2)', '(-x, -y, -z)', '(x, y, -z)',
1524 '(y, -x, -z+1/2)', '(-y, x, -z+1/2)'))},
1525 '85:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
1526 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
1527 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
1528 '4d': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(1/4, 3/4, 0)',
1529 '(3/4, 1/4, 0)')),
1530 '4e': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
1531 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
1532 '4f': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(1/2, 1/2, -z)',
1533 '(0, 0, -z)')),
1534 '8g': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z)',
1535 '(y+1/2, -x+1/2, z)', '(-x+1/2, -y+1/2, -z)',
1536 '(x+1/2, y+1/2, -z)', '(y, -x, -z)', '(-y, x, -z)'
1537 ))},
1538 '85:2': {'2a': (0, ('(1/4, 3/4, 0)', '(3/4, 1/4, 0)')),
1539 '2b': (0, ('(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
1540 '2c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, -z)')),
1541 '4d': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
1542 '(0, 1/2, 0)')),
1543 '4e': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
1544 '(0, 1/2, 1/2)')),
1545 '4f': (4, ('(1/4, 3/4, z)', '(3/4, 1/4, z)', '(3/4, 1/4, -z)',
1546 '(1/4, 3/4, -z)')),
1547 '8g': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
1548 '(-y+1/2, x, z)', '(y, -x+1/2, z)', '(-x, -y, -z)',
1549 '(x+1/2, y+1/2, -z)', '(y+1/2, -x, -z)',
1550 '(-y, x+1/2, -z)'))},
1551 '86:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1552 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
1553 '4c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
1554 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)')),
1555 '4d': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)',
1556 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
1557 '4e': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)',
1558 '(1/2, 0, -z+1/2)', '(1/2, 0, -z)')),
1559 '4f': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
1560 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)')),
1561 '8g': (7, ('(x, y, z)', '(-x, -y, z)',
1562 '(-y+1/2, x+1/2, z+1/2)', '(y+1/2, -x+1/2, z+1/2)',
1563 '(-x+1/2, -y+1/2, -z+1/2)',
1564 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
1565 '(-y, x, -z)'))},
1566 '86:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
1567 '2b': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
1568 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
1569 '(1/2, 0, 1/2)')),
1570 '4d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(0, 1/2, 0)',
1571 '(1/2, 0, 0)')),
1572 '4e': (4, ('(3/4, 1/4, z)', '(3/4, 1/4, z+1/2)',
1573 '(1/4, 3/4, -z)', '(1/4, 3/4, -z+1/2)')),
1574 '4f': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z+1/2)',
1575 '(3/4, 3/4, -z)', '(1/4, 1/4, -z+1/2)')),
1576 '8g': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
1577 '(-y, x+1/2, z+1/2)', '(y+1/2, -x, z+1/2)',
1578 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
1579 '(y, -x+1/2, -z+1/2)', '(-y+1/2, x, -z+1/2)'))},
1580 '87': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1581 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
1582 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
1583 '(0, 1/2, 1/2)')),
1584 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(1/2, 0, 1/4)',
1585 '(0, 1/2, 3/4)')),
1586 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
1587 '(1/2, 1/2, -z+1/2)')),
1588 '8f': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
1589 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
1590 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
1591 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 3/4)')),
1592 '8g': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
1593 '(0, 1/2, z+1/2)', '(0, 1/2, -z)', '(1/2, 0, -z+1/2)',
1594 '(1/2, 0, -z)', '(0, 1/2, -z+1/2)')),
1595 '8h': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 0)',
1596 '(-x+1/2, -y+1/2, 1/2)', '(-y, x, 0)',
1597 '(-y+1/2, x+1/2, 1/2)', '(y, -x, 0)',
1598 '(y+1/2, -x+1/2, 1/2)')),
1599 '16i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1600 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
1601 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
1602 '(y+1/2, -x+1/2, z+1/2)', '(-x, -y, -z)',
1603 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
1604 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
1605 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
1606 '(-y+1/2, x+1/2, -z+1/2)'))},
1607 '88:1': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/4)',
1608 '(1/2, 0, 3/4)')),
1609 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(0, 1/2, 3/4)',
1610 '(1/2, 0, 1/4)')),
1611 '8c': (0, ('(0, 1/4, 1/8)', '(1/2, 3/4, 5/8)',
1612 '(1/2, 1/4, 5/8)', '(0, 3/4, 1/8)',
1613 '(3/4, 1/2, 3/8)', '(1/4, 0, 7/8)', '(3/4, 0, 7/8)',
1614 '(1/4, 1/2, 3/8)')),
1615 '8d': (0, ('(0, 1/4, 5/8)', '(1/2, 3/4, 1/8)',
1616 '(1/2, 1/4, 1/8)', '(0, 3/4, 5/8)',
1617 '(3/4, 1/2, 7/8)', '(1/4, 0, 3/8)', '(3/4, 0, 3/8)',
1618 '(1/4, 1/2, 7/8)')),
1619 '8e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 1/2, z+1/4)',
1620 '(1/2, 0, z+3/4)', '(0, 1/2, -z+1/4)',
1621 '(1/2, 0, -z+3/4)', '(0, 0, -z)',
1622 '(1/2, 1/2, -z+1/2)')),
1623 '16f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1624 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
1625 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
1626 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
1627 '(-x, -y+1/2, -z+1/4)', '(-x+1/2, -y, -z+3/4)',
1628 '(x+1/2, y, -z+3/4)', '(x, y+1/2, -z+1/4)',
1629 '(y, -x, -z)', '(y+1/2, -x+1/2, -z+1/2)',
1630 '(-y+1/2, x+1/2, -z+1/2)', '(-y, x, -z)'))},
1631 '88:2': {'4a': (0, ('(0, 1/4, 1/8)', '(1/2, 3/4, 5/8)',
1632 '(1/2, 1/4, 3/8)', '(0, 3/4, 7/8)')),
1633 '4b': (0, ('(0, 1/4, 5/8)', '(1/2, 3/4, 1/8)',
1634 '(1/2, 1/4, 7/8)', '(0, 3/4, 3/8)')),
1635 '8c': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
1636 '(0, 1/2, 0)', '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
1637 '(3/4, 3/4, 3/4)', '(1/4, 1/4, 1/4)')),
1638 '8d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
1639 '(0, 1/2, 1/2)', '(3/4, 1/4, 3/4)',
1640 '(1/4, 3/4, 1/4)', '(3/4, 3/4, 1/4)',
1641 '(1/4, 1/4, 3/4)')),
1642 '8e': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)',
1643 '(1/2, 1/4, z+1/4)', '(0, 3/4, z+3/4)',
1644 '(0, 3/4, -z)', '(1/2, 1/4, -z+1/2)',
1645 '(1/2, 3/4, -z+3/4)', '(0, 1/4, -z+1/4)')),
1646 '16f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1647 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
1648 '(-y+3/4, x+1/4, z+1/4)', '(-y+1/4, x+3/4, z+3/4)',
1649 '(y+3/4, -x+3/4, z+3/4)', '(y+1/4, -x+1/4, z+1/4)',
1650 '(-x, -y, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
1651 '(x+1/2, y, -z+1/2)', '(x, y+1/2, -z)',
1652 '(y+1/4, -x+3/4, -z+3/4)',
1653 '(y+3/4, -x+1/4, -z+1/4)',
1654 '(-y+1/4, x+1/4, -z+1/4)',
1655 '(-y+3/4, x+3/4, -z+3/4)'))},
1656 '89': {'1a': (0, ('(0, 0, 0)', )),
1657 '1b': (0, ('(0, 0, 1/2)', )),
1658 '1c': (0, ('(1/2, 1/2, 0)', )),
1659 '1d': (0, ('(1/2, 1/2, 1/2)', )),
1660 '2e': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)')),
1661 '2f': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
1662 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
1663 '2h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
1664 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
1665 '(1/2, 0, -z)')),
1666 '4j': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x, x, 0)',
1667 '(x, -x, 0)')),
1668 '4k': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(-x, x, 1/2)',
1669 '(x, -x, 1/2)')),
1670 '4l': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)'
1671 )),
1672 '4m': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
1673 '(1/2, -x, 1/2)')),
1674 '4n': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 1/2)',
1675 '(0, -x, 1/2)')),
1676 '4o': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(1/2, x, 0)',
1677 '(1/2, -x, 0)')),
1678 '8p': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1679 '(y, -x, z)', '(-x, y, -z)', '(x, -y, -z)',
1680 '(y, x, -z)', '(-y, -x, -z)'))},
1681 '90': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
1682 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
1683 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
1684 '4d': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(1/2, 1/2, -z)',
1685 '(0, 0, -z)')),
1686 '4e': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x+1/2, x+1/2, 0)',
1687 '(x+1/2, -x+1/2, 0)')),
1688 '4f': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)',
1689 '(-x+1/2, x+1/2, 1/2)', '(x+1/2, -x+1/2, 1/2)')),
1690 '8g': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z)',
1691 '(y+1/2, -x+1/2, z)', '(-x+1/2, y+1/2, -z)',
1692 '(x+1/2, -y+1/2, -z)', '(y, x, -z)', '(-y, -x, -z)'))},
1693 '91': {'4a': (2, ('(0, y, 0)', '(0, -y, 1/2)', '(-y, 0, 1/4)',
1694 '(y, 0, 3/4)')),
1695 '4b': (2, ('(1/2, y, 0)', '(1/2, -y, 1/2)', '(-y, 1/2, 1/4)',
1696 '(y, 1/2, 3/4)')),
1697 '4c': (1, ('(x, x, 3/8)', '(-x, -x, 7/8)', '(-x, x, 5/8)',
1698 '(x, -x, 1/8)')),
1699 '8d': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-y, x, z+1/4)',
1700 '(y, -x, z+3/4)', '(-x, y, -z)', '(x, -y, -z+1/2)',
1701 '(y, x, -z+3/4)', '(-y, -x, -z+1/4)'))},
1702 '92': {'4a': (1, ('(x, x, 0)', '(-x, -x, 1/2)', '(-x+1/2, x+1/2, 1/4)',
1703 '(x+1/2, -x+1/2, 3/4)')),
1704 '8b': (7, ('(x, y, z)', '(-x, -y, z+1/2)',
1705 '(-y+1/2, x+1/2, z+1/4)', '(y+1/2, -x+1/2, z+3/4)',
1706 '(-x+1/2, y+1/2, -z+1/4)', '(x+1/2, -y+1/2, -z+3/4)',
1707 '(y, x, -z)', '(-y, -x, -z+1/2)'))},
1708 '93': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
1709 '2b': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
1710 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
1711 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)')),
1712 '2e': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
1713 '2f': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
1714 '4g': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
1715 '(0, 0, -z+1/2)')),
1716 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)',
1717 '(1/2, 1/2, -z)', '(1/2, 1/2, -z+1/2)')),
1718 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, -z)',
1719 '(1/2, 0, -z+1/2)')),
1720 '4j': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 1/2)',
1721 '(0, -x, 1/2)')),
1722 '4k': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 0)',
1723 '(1/2, -x, 0)')),
1724 '4l': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 0)',
1725 '(0, -x, 0)')),
1726 '4m': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(1/2, x, 1/2)',
1727 '(1/2, -x, 1/2)')),
1728 '4n': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)', '(-x, x, 3/4)',
1729 '(x, -x, 3/4)')),
1730 '4o': (1, ('(x, x, 3/4)', '(-x, -x, 3/4)', '(-x, x, 1/4)',
1731 '(x, -x, 1/4)')),
1732 '8p': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1733 '(y, -x, z+1/2)', '(-x, y, -z)', '(x, -y, -z)',
1734 '(y, x, -z+1/2)', '(-y, -x, -z+1/2)'))},
1735 '94': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1736 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
1737 '4c': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
1738 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)')),
1739 '4d': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)', '(1/2, 0, -z+1/2)',
1740 '(1/2, 0, -z)')),
1741 '4e': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x+1/2, x+1/2, 1/2)',
1742 '(x+1/2, -x+1/2, 1/2)')),
1743 '4f': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(-x+1/2, x+1/2, 0)',
1744 '(x+1/2, -x+1/2, 0)')),
1745 '8g': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z+1/2)',
1746 '(y+1/2, -x+1/2, z+1/2)', '(-x+1/2, y+1/2, -z+1/2)',
1747 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z)',
1748 '(-y, -x, -z)'))},
1749 '95': {'4a': (2, ('(0, y, 0)', '(0, -y, 1/2)', '(-y, 0, 3/4)',
1750 '(y, 0, 1/4)')),
1751 '4b': (2, ('(1/2, y, 0)', '(1/2, -y, 1/2)', '(-y, 1/2, 3/4)',
1752 '(y, 1/2, 1/4)')),
1753 '4c': (1, ('(x, x, 5/8)', '(-x, -x, 1/8)', '(-x, x, 3/8)',
1754 '(x, -x, 7/8)')),
1755 '8d': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-y, x, z+3/4)',
1756 '(y, -x, z+1/4)', '(-x, y, -z)', '(x, -y, -z+1/2)',
1757 '(y, x, -z+1/4)', '(-y, -x, -z+3/4)'))},
1758 '96': {'4a': (1, ('(x, x, 0)', '(-x, -x, 1/2)', '(-x+1/2, x+1/2, 3/4)',
1759 '(x+1/2, -x+1/2, 1/4)')),
1760 '8b': (7, ('(x, y, z)', '(-x, -y, z+1/2)',
1761 '(-y+1/2, x+1/2, z+3/4)', '(y+1/2, -x+1/2, z+1/4)',
1762 '(-x+1/2, y+1/2, -z+3/4)', '(x+1/2, -y+1/2, -z+1/4)',
1763 '(y, x, -z)', '(-y, -x, -z+1/2)'))},
1764 '97': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1765 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
1766 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
1767 '(0, 1/2, 1/2)')),
1768 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(1/2, 0, 1/4)',
1769 '(0, 1/2, 3/4)')),
1770 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
1771 '(1/2, 1/2, -z+1/2)')),
1772 '8f': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
1773 '(0, 1/2, z+1/2)', '(0, 1/2, -z)', '(1/2, 0, -z+1/2)',
1774 '(1/2, 0, -z)', '(0, 1/2, -z+1/2)')),
1775 '8g': (1, ('(x, x, 0)', '(x+1/2, x+1/2, 1/2)', '(-x, -x, 0)',
1776 '(-x+1/2, -x+1/2, 1/2)', '(-x, x, 0)',
1777 '(-x+1/2, x+1/2, 1/2)', '(x, -x, 0)',
1778 '(x+1/2, -x+1/2, 1/2)')),
1779 '8h': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
1780 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
1781 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
1782 '(1/2, -x+1/2, 1/2)')),
1783 '8i': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
1784 '(-x+1/2, 1/2, 0)', '(0, x, 1/2)', '(1/2, x+1/2, 0)',
1785 '(0, -x, 1/2)', '(1/2, -x+1/2, 0)')),
1786 '8j': (1, ('(x, x+1/2, 1/4)', '(x+1/2, x, 3/4)',
1787 '(-x, -x+1/2, 1/4)', '(-x+1/2, -x, 3/4)',
1788 '(-x+1/2, x, 1/4)', '(-x, x+1/2, 3/4)',
1789 '(x+1/2, -x, 1/4)', '(x, -x+1/2, 3/4)')),
1790 '16k': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1791 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
1792 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
1793 '(y+1/2, -x+1/2, z+1/2)', '(-x, y, -z)',
1794 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
1795 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z)',
1796 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z)',
1797 '(-y+1/2, -x+1/2, -z+1/2)'))},
1798 '98': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/4)',
1799 '(1/2, 0, 3/4)')),
1800 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(0, 1/2, 3/4)',
1801 '(1/2, 0, 1/4)')),
1802 '8c': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 1/2, z+1/4)',
1803 '(1/2, 0, z+3/4)', '(1/2, 0, -z+3/4)',
1804 '(0, 1/2, -z+1/4)', '(1/2, 1/2, -z+1/2)',
1805 '(0, 0, -z)')),
1806 '8d': (1, ('(x, x, 0)', '(x+1/2, x+1/2, 1/2)',
1807 '(-x+1/2, -x+1/2, 1/2)', '(-x, -x, 0)',
1808 '(-x, x+1/2, 1/4)', '(-x+1/2, x, 3/4)',
1809 '(x+1/2, -x, 3/4)', '(x, -x+1/2, 1/4)')),
1810 '8e': (1, ('(-x, x, 0)', '(-x+1/2, x+1/2, 1/2)',
1811 '(x+1/2, -x+1/2, 1/2)', '(x, -x, 0)',
1812 '(-x, -x+1/2, 1/4)', '(-x+1/2, -x, 3/4)',
1813 '(x+1/2, x, 3/4)', '(x, x+1/2, 1/4)')),
1814 '8f': (1, ('(x, 1/4, 1/8)', '(x+1/2, 3/4, 5/8)',
1815 '(-x+1/2, 1/4, 5/8)', '(-x, 3/4, 1/8)',
1816 '(3/4, x+1/2, 3/8)', '(1/4, x, 7/8)',
1817 '(3/4, -x, 7/8)', '(1/4, -x+1/2, 3/8)')),
1818 '16g': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1819 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
1820 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
1821 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
1822 '(-x+1/2, y, -z+3/4)', '(-x, y+1/2, -z+1/4)',
1823 '(x, -y+1/2, -z+1/4)', '(x+1/2, -y, -z+3/4)',
1824 '(y+1/2, x+1/2, -z+1/2)', '(y, x, -z)',
1825 '(-y, -x, -z)', '(-y+1/2, -x+1/2, -z+1/2)'))},
1826 '99': {'1a': (4, ('(0, 0, z)', )),
1827 '1b': (4, ('(1/2, 1/2, z)', )),
1828 '2c': (4, ('(1/2, 0, z)', '(0, 1/2, z)')),
1829 '4d': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, z)',
1830 '(x, -x, z)')),
1831 '4e': (5, ('(x, 0, z)', '(-x, 0, z)', '(0, x, z)', '(0, -x, z)'
1832 )),
1833 '4f': (5, ('(x, 1/2, z)', '(-x, 1/2, z)', '(1/2, x, z)',
1834 '(1/2, -x, z)')),
1835 '8g': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1836 '(y, -x, z)', '(x, -y, z)', '(-x, y, z)',
1837 '(-y, -x, z)', '(y, x, z)'))},
1838 '100': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z)')),
1839 '2b': (4, ('(1/2, 0, z)', '(0, 1/2, z)')),
1840 '4c': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)', '(-x+1/2, x, z)',
1841 '(x+1/2, -x, z)')),
1842 '8d': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1843 '(y, -x, z)', '(x+1/2, -y+1/2, z)',
1844 '(-x+1/2, y+1/2, z)', '(-y+1/2, -x+1/2, z)',
1845 '(y+1/2, x+1/2, z)'))},
1846 '101': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
1847 '2b': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
1848 '4c': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, z+1/2)',
1849 '(1/2, 0, z)')),
1850 '4d': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, z+1/2)',
1851 '(x, -x, z+1/2)')),
1852 '8e': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1853 '(y, -x, z+1/2)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
1854 '(-y, -x, z)', '(y, x, z)'))},
1855 '102': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
1856 '4b': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
1857 '(1/2, 0, z)')),
1858 '4c': (5, ('(x, x, z)', '(-x, -x, z)', '(-x+1/2, x+1/2, z+1/2)',
1859 '(x+1/2, -x+1/2, z+1/2)')),
1860 '8d': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z+1/2)',
1861 '(y+1/2, -x+1/2, z+1/2)', '(x+1/2, -y+1/2, z+1/2)',
1862 '(-x+1/2, y+1/2, z+1/2)', '(-y, -x, z)', '(y, x, z)'
1863 ))},
1864 '103': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
1865 '2b': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
1866 '4c': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, z+1/2)',
1867 '(1/2, 0, z+1/2)')),
1868 '8d': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1869 '(y, -x, z)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
1870 '(-y, -x, z+1/2)', '(y, x, z+1/2)'))},
1871 '104': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
1872 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(1/2, 0, z+1/2)',
1873 '(0, 1/2, z+1/2)')),
1874 '8c': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1875 '(y, -x, z)', '(x+1/2, -y+1/2, z+1/2)',
1876 '(-x+1/2, y+1/2, z+1/2)', '(-y+1/2, -x+1/2, z+1/2)',
1877 '(y+1/2, x+1/2, z+1/2)'))},
1878 '105': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
1879 '2b': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
1880 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)')),
1881 '4d': (5, ('(x, 0, z)', '(-x, 0, z)', '(0, x, z+1/2)',
1882 '(0, -x, z+1/2)')),
1883 '4e': (5, ('(x, 1/2, z)', '(-x, 1/2, z)', '(1/2, x, z+1/2)',
1884 '(1/2, -x, z+1/2)')),
1885 '8f': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1886 '(y, -x, z+1/2)', '(x, -y, z)', '(-x, y, z)',
1887 '(-y, -x, z+1/2)', '(y, x, z+1/2)'))},
1888 '106': {'4a': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(1/2, 1/2, z)',
1889 '(1/2, 1/2, z+1/2)')),
1890 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
1891 '(0, 1/2, z+1/2)')),
1892 '8c': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1893 '(y, -x, z+1/2)', '(x+1/2, -y+1/2, z)',
1894 '(-x+1/2, y+1/2, z)', '(-y+1/2, -x+1/2, z+1/2)',
1895 '(y+1/2, x+1/2, z+1/2)'))},
1896 '107': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
1897 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
1898 '(0, 1/2, z+1/2)')),
1899 '8c': (5, ('(x, x, z)', '(x+1/2, x+1/2, z+1/2)', '(-x, -x, z)',
1900 '(-x+1/2, -x+1/2, z+1/2)', '(-x, x, z)',
1901 '(-x+1/2, x+1/2, z+1/2)', '(x, -x, z)',
1902 '(x+1/2, -x+1/2, z+1/2)')),
1903 '8d': (5, ('(x, 0, z)', '(x+1/2, 1/2, z+1/2)', '(-x, 0, z)',
1904 '(-x+1/2, 1/2, z+1/2)', '(0, x, z)',
1905 '(1/2, x+1/2, z+1/2)', '(0, -x, z)',
1906 '(1/2, -x+1/2, z+1/2)')),
1907 '16e': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1908 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
1909 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
1910 '(y+1/2, -x+1/2, z+1/2)', '(x, -y, z)',
1911 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
1912 '(-x+1/2, y+1/2, z+1/2)', '(-y, -x, z)',
1913 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z)',
1914 '(y+1/2, x+1/2, z+1/2)'))},
1915 '108': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, z+1/2)',
1916 '(1/2, 1/2, z)')),
1917 '4b': (4, ('(1/2, 0, z)', '(0, 1/2, z+1/2)', '(0, 1/2, z)',
1918 '(1/2, 0, z+1/2)')),
1919 '8c': (5, ('(x, x+1/2, z)', '(x+1/2, x, z+1/2)',
1920 '(-x, -x+1/2, z)', '(-x+1/2, -x, z+1/2)',
1921 '(-x+1/2, x, z)', '(-x, x+1/2, z+1/2)',
1922 '(x+1/2, -x, z)', '(x, -x+1/2, z+1/2)')),
1923 '16d': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1924 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
1925 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
1926 '(y+1/2, -x+1/2, z+1/2)', '(x, -y, z+1/2)',
1927 '(x+1/2, -y+1/2, z)', '(-x, y, z+1/2)',
1928 '(-x+1/2, y+1/2, z)', '(-y, -x, z+1/2)',
1929 '(-y+1/2, -x+1/2, z)', '(y, x, z+1/2)',
1930 '(y+1/2, x+1/2, z)'))},
1931 '109': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 1/2, z+1/4)',
1932 '(1/2, 0, z+3/4)')),
1933 '8b': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)',
1934 '(1/2, -y+1/2, z+1/2)', '(0, -y, z)',
1935 '(-y, 1/2, z+1/4)', '(-y+1/2, 0, z+3/4)',
1936 '(y+1/2, 0, z+3/4)', '(y, 1/2, z+1/4)')),
1937 '16c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1938 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
1939 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
1940 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
1941 '(x, -y, z)', '(x+1/2, -y+1/2, z+1/2)',
1942 '(-x+1/2, y+1/2, z+1/2)', '(-x, y, z)',
1943 '(-y, -x+1/2, z+1/4)', '(-y+1/2, -x, z+3/4)',
1944 '(y+1/2, x, z+3/4)', '(y, x+1/2, z+1/4)'))},
1945 '110': {'8a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 1/2, z+1/4)',
1946 '(1/2, 0, z+3/4)', '(0, 0, z+1/2)', '(1/2, 1/2, z)',
1947 '(0, 1/2, z+3/4)', '(1/2, 0, z+1/4)')),
1948 '16b': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1949 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
1950 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
1951 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
1952 '(x, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
1953 '(-x+1/2, y+1/2, z)', '(-x, y, z+1/2)',
1954 '(-y, -x+1/2, z+3/4)', '(-y+1/2, -x, z+1/4)',
1955 '(y+1/2, x, z+1/4)', '(y, x+1/2, z+3/4)'))},
1956 '111': {'1a': (0, ('(0, 0, 0)', )),
1957 '1b': (0, ('(1/2, 1/2, 1/2)', )),
1958 '1c': (0, ('(0, 0, 1/2)', )),
1959 '1d': (0, ('(1/2, 1/2, 0)', )),
1960 '2e': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)')),
1961 '2f': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
1962 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
1963 '2h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
1964 '4i': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, -x, 0)', '(0, x, 0)'
1965 )),
1966 '4j': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, -x, 1/2)',
1967 '(1/2, x, 1/2)')),
1968 '4k': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, -x, 1/2)',
1969 '(0, x, 1/2)')),
1970 '4l': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(1/2, -x, 0)',
1971 '(1/2, x, 0)')),
1972 '4m': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(0, 1/2, -z)',
1973 '(1/2, 0, z)')),
1974 '4n': (5, ('(x, x, z)', '(-x, -x, z)', '(x, -x, -z)',
1975 '(-x, x, -z)')),
1976 '8o': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
1977 '(-y, x, -z)', '(-x, y, -z)', '(x, -y, -z)',
1978 '(-y, -x, z)', '(y, x, z)'))},
1979 '112': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
1980 '2b': (0, ('(1/2, 0, 1/4)', '(0, 1/2, 3/4)')),
1981 '2c': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
1982 '2d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)')),
1983 '2e': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
1984 '2f': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
1985 '4g': (1, ('(x, 0, 1/4)', '(-x, 0, 1/4)', '(0, -x, 3/4)',
1986 '(0, x, 3/4)')),
1987 '4h': (2, ('(1/2, y, 1/4)', '(1/2, -y, 1/4)', '(y, 1/2, 3/4)',
1988 '(-y, 1/2, 3/4)')),
1989 '4i': (1, ('(x, 1/2, 1/4)', '(-x, 1/2, 1/4)', '(1/2, -x, 3/4)',
1990 '(1/2, x, 3/4)')),
1991 '4j': (2, ('(0, y, 1/4)', '(0, -y, 1/4)', '(y, 0, 3/4)',
1992 '(-y, 0, 3/4)')),
1993 '4k': (4, ('(0, 0, z)', '(0, 0, -z)', '(0, 0, -z+1/2)',
1994 '(0, 0, z+1/2)')),
1995 '4l': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)',
1996 '(1/2, 1/2, -z+1/2)', '(1/2, 1/2, z+1/2)')),
1997 '4m': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(0, 1/2, -z+1/2)',
1998 '(1/2, 0, z+1/2)')),
1999 '8n': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2000 '(-y, x, -z)', '(-x, y, -z+1/2)', '(x, -y, -z+1/2)',
2001 '(-y, -x, z+1/2)', '(y, x, z+1/2)'))},
2002 '113': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
2003 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2004 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
2005 '4d': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z)',
2006 '(1/2, 1/2, z)')),
2007 '4e': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)',
2008 '(x+1/2, -x, -z)', '(-x+1/2, x, -z)')),
2009 '8f': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2010 '(-y, x, -z)', '(-x+1/2, y+1/2, -z)',
2011 '(x+1/2, -y+1/2, -z)', '(-y+1/2, -x+1/2, z)',
2012 '(y+1/2, x+1/2, z)'))},
2013 '114': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2014 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2015 '4c': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
2016 '(1/2, 1/2, z+1/2)')),
2017 '4d': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(1/2, 0, -z+1/2)',
2018 '(0, 1/2, z+1/2)')),
2019 '8e': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2020 '(-y, x, -z)', '(-x+1/2, y+1/2, -z+1/2)',
2021 '(x+1/2, -y+1/2, -z+1/2)', '(-y+1/2, -x+1/2, z+1/2)',
2022 '(y+1/2, x+1/2, z+1/2)'))},
2023 '115': {'1a': (0, ('(0, 0, 0)', )),
2024 '1b': (0, ('(1/2, 1/2, 0)', )),
2025 '1c': (0, ('(1/2, 1/2, 1/2)', )),
2026 '1d': (0, ('(0, 0, 1/2)', )),
2027 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
2028 '2f': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
2029 '2g': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
2030 '4h': (1, ('(x, x, 0)', '(-x, -x, 0)', '(x, -x, 0)',
2031 '(-x, x, 0)')),
2032 '4i': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(x, -x, 1/2)',
2033 '(-x, x, 1/2)')),
2034 '4j': (5, ('(x, 0, z)', '(-x, 0, z)', '(0, -x, -z)',
2035 '(0, x, -z)')),
2036 '4k': (5, ('(x, 1/2, z)', '(-x, 1/2, z)', '(1/2, -x, -z)',
2037 '(1/2, x, -z)')),
2038 '8l': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2039 '(-y, x, -z)', '(x, -y, z)', '(-x, y, z)',
2040 '(y, x, -z)', '(-y, -x, -z)'))},
2041 '116': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
2042 '2b': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
2043 '2c': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
2044 '2d': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
2045 '4e': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)', '(x, -x, 3/4)',
2046 '(-x, x, 3/4)')),
2047 '4f': (1, ('(x, x, 3/4)', '(-x, -x, 3/4)', '(x, -x, 1/4)',
2048 '(-x, x, 1/4)')),
2049 '4g': (4, ('(0, 0, z)', '(0, 0, -z)', '(0, 0, z+1/2)',
2050 '(0, 0, -z+1/2)')),
2051 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)',
2052 '(1/2, 1/2, z+1/2)', '(1/2, 1/2, -z+1/2)')),
2053 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(0, 1/2, z+1/2)',
2054 '(1/2, 0, -z+1/2)')),
2055 '8j': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2056 '(-y, x, -z)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
2057 '(y, x, -z+1/2)', '(-y, -x, -z+1/2)'))},
2058 '117': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
2059 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2060 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
2061 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
2062 '4e': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, z)',
2063 '(1/2, 1/2, -z)')),
2064 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(1/2, 0, z)',
2065 '(0, 1/2, -z)')),
2066 '4g': (1, ('(x, x+1/2, 0)', '(-x, -x+1/2, 0)', '(x+1/2, -x, 0)',
2067 '(-x+1/2, x, 0)')),
2068 '4h': (1, ('(x, x+1/2, 1/2)', '(-x, -x+1/2, 1/2)',
2069 '(x+1/2, -x, 1/2)', '(-x+1/2, x, 1/2)')),
2070 '8i': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2071 '(-y, x, -z)', '(x+1/2, -y+1/2, z)',
2072 '(-x+1/2, y+1/2, z)', '(y+1/2, x+1/2, -z)',
2073 '(-y+1/2, -x+1/2, -z)'))},
2074 '118': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2075 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2076 '2c': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)')),
2077 '2d': (0, ('(0, 1/2, 3/4)', '(1/2, 0, 1/4)')),
2078 '4e': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, z+1/2)',
2079 '(1/2, 1/2, -z+1/2)')),
2080 '4f': (1, ('(x, -x+1/2, 1/4)', '(-x, x+1/2, 1/4)',
2081 '(-x+1/2, -x, 3/4)', '(x+1/2, x, 3/4)')),
2082 '4g': (1, ('(x, x+1/2, 1/4)', '(-x, -x+1/2, 1/4)',
2083 '(x+1/2, -x, 3/4)', '(-x+1/2, x, 3/4)')),
2084 '4h': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(1/2, 0, z+1/2)',
2085 '(0, 1/2, -z+1/2)')),
2086 '8i': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2087 '(-y, x, -z)', '(x+1/2, -y+1/2, z+1/2)',
2088 '(-x+1/2, y+1/2, z+1/2)', '(y+1/2, x+1/2, -z+1/2)',
2089 '(-y+1/2, -x+1/2, -z+1/2)'))},
2090 '119': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2091 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2092 '2c': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)')),
2093 '2d': (0, ('(0, 1/2, 3/4)', '(1/2, 0, 1/4)')),
2094 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
2095 '(1/2, 1/2, -z+1/2)')),
2096 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
2097 '(0, 1/2, -z+1/2)')),
2098 '8g': (1, ('(x, x, 0)', '(x+1/2, x+1/2, 1/2)', '(-x, -x, 0)',
2099 '(-x+1/2, -x+1/2, 1/2)', '(x, -x, 0)',
2100 '(x+1/2, -x+1/2, 1/2)', '(-x, x, 0)',
2101 '(-x+1/2, x+1/2, 1/2)')),
2102 '8h': (1, ('(x, x+1/2, 1/4)', '(x+1/2, x, 3/4)',
2103 '(-x, -x+1/2, 1/4)', '(-x+1/2, -x, 3/4)',
2104 '(x+1/2, -x, 3/4)', '(x, -x+1/2, 1/4)',
2105 '(-x+1/2, x, 3/4)', '(-x, x+1/2, 1/4)')),
2106 '8i': (5, ('(x, 0, z)', '(x+1/2, 1/2, z+1/2)', '(-x, 0, z)',
2107 '(-x+1/2, 1/2, z+1/2)', '(0, -x, -z)',
2108 '(1/2, -x+1/2, -z+1/2)', '(0, x, -z)',
2109 '(1/2, x+1/2, -z+1/2)')),
2110 '16j': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
2111 '(-x+1/2, -y+1/2, z+1/2)', '(y, -x, -z)',
2112 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
2113 '(-y+1/2, x+1/2, -z+1/2)', '(x, -y, z)',
2114 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
2115 '(-x+1/2, y+1/2, z+1/2)', '(y, x, -z)',
2116 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z)',
2117 '(-y+1/2, -x+1/2, -z+1/2)'))},
2118 '120': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)', '(0, 0, 3/4)',
2119 '(1/2, 1/2, 1/4)')),
2120 '4b': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 0, 1/2)',
2121 '(1/2, 1/2, 0)')),
2122 '4c': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(0, 1/2, 3/4)',
2123 '(1/2, 0, 1/4)')),
2124 '4d': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
2125 '(0, 1/2, 1/2)')),
2126 '8e': (1, ('(x, x, 1/4)', '(x+1/2, x+1/2, 3/4)',
2127 '(-x, -x, 1/4)', '(-x+1/2, -x+1/2, 3/4)',
2128 '(x, -x, 3/4)', '(x+1/2, -x+1/2, 1/4)',
2129 '(-x, x, 3/4)', '(-x+1/2, x+1/2, 1/4)')),
2130 '8f': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
2131 '(1/2, 1/2, -z+1/2)', '(0, 0, z+1/2)',
2132 '(1/2, 1/2, z)', '(0, 0, -z+1/2)', '(1/2, 1/2, -z)'
2133 )),
2134 '8g': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
2135 '(0, 1/2, -z+1/2)', '(0, 1/2, z+1/2)', '(1/2, 0, z)',
2136 '(1/2, 0, -z+1/2)', '(0, 1/2, -z)')),
2137 '8h': (1, ('(x, x+1/2, 0)', '(x+1/2, x, 1/2)',
2138 '(-x, -x+1/2, 0)', '(-x+1/2, -x, 1/2)',
2139 '(x+1/2, -x, 0)', '(x, -x+1/2, 1/2)',
2140 '(-x+1/2, x, 0)', '(-x, x+1/2, 1/2)')),
2141 '16i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
2142 '(-x+1/2, -y+1/2, z+1/2)', '(y, -x, -z)',
2143 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
2144 '(-y+1/2, x+1/2, -z+1/2)', '(x, -y, z+1/2)',
2145 '(x+1/2, -y+1/2, z)', '(-x, y, z+1/2)',
2146 '(-x+1/2, y+1/2, z)', '(y, x, -z+1/2)',
2147 '(y+1/2, x+1/2, -z)', '(-y, -x, -z+1/2)',
2148 '(-y+1/2, -x+1/2, -z)'))},
2149 '121': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2150 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2151 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
2152 '(0, 1/2, 1/2)')),
2153 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(0, 1/2, 3/4)',
2154 '(1/2, 0, 1/4)')),
2155 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
2156 '(1/2, 1/2, -z+1/2)')),
2157 '8f': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
2158 '(-x+1/2, 1/2, 1/2)', '(0, -x, 0)',
2159 '(1/2, -x+1/2, 1/2)', '(0, x, 0)',
2160 '(1/2, x+1/2, 1/2)')),
2161 '8g': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
2162 '(-x+1/2, 1/2, 0)', '(0, -x, 1/2)',
2163 '(1/2, -x+1/2, 0)', '(0, x, 1/2)', '(1/2, x+1/2, 0)'
2164 )),
2165 '8h': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
2166 '(0, 1/2, -z+1/2)', '(0, 1/2, -z)',
2167 '(1/2, 0, -z+1/2)', '(1/2, 0, z)', '(0, 1/2, z+1/2)'
2168 )),
2169 '8i': (5, ('(x, x, z)', '(x+1/2, x+1/2, z+1/2)', '(-x, -x, z)',
2170 '(-x+1/2, -x+1/2, z+1/2)', '(x, -x, -z)',
2171 '(x+1/2, -x+1/2, -z+1/2)', '(-x, x, -z)',
2172 '(-x+1/2, x+1/2, -z+1/2)')),
2173 '16j': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
2174 '(-x+1/2, -y+1/2, z+1/2)', '(y, -x, -z)',
2175 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
2176 '(-y+1/2, x+1/2, -z+1/2)', '(-x, y, -z)',
2177 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
2178 '(x+1/2, -y+1/2, -z+1/2)', '(-y, -x, z)',
2179 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z)',
2180 '(y+1/2, x+1/2, z+1/2)'))},
2181 '122': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 3/4)',
2182 '(0, 1/2, 1/4)')),
2183 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(1/2, 0, 1/4)',
2184 '(0, 1/2, 3/4)')),
2185 '8c': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
2186 '(1/2, 1/2, -z+1/2)', '(1/2, 0, -z+3/4)',
2187 '(0, 1/2, -z+1/4)', '(1/2, 0, z+3/4)',
2188 '(0, 1/2, z+1/4)')),
2189 '8d': (1, ('(x, 1/4, 1/8)', '(x+1/2, 3/4, 5/8)',
2190 '(-x, 3/4, 1/8)', '(-x+1/2, 1/4, 5/8)',
2191 '(1/4, -x, 7/8)', '(3/4, -x+1/2, 3/8)',
2192 '(3/4, x, 7/8)', '(1/4, x+1/2, 3/8)')),
2193 '16e': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
2194 '(-x+1/2, -y+1/2, z+1/2)', '(y, -x, -z)',
2195 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
2196 '(-y+1/2, x+1/2, -z+1/2)', '(-x+1/2, y, -z+3/4)',
2197 '(-x, y+1/2, -z+1/4)', '(x+1/2, -y, -z+3/4)',
2198 '(x, -y+1/2, -z+1/4)', '(-y+1/2, -x, z+3/4)',
2199 '(-y, -x+1/2, z+1/4)', '(y+1/2, x, z+3/4)',
2200 '(y, x+1/2, z+1/4)'))},
2201 '123': {'1a': (0, ('(0, 0, 0)', )),
2202 '1b': (0, ('(0, 0, 1/2)', )),
2203 '1c': (0, ('(1/2, 1/2, 0)', )),
2204 '1d': (0, ('(1/2, 1/2, 1/2)', )),
2205 '2e': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
2206 '2f': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
2207 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
2208 '2h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
2209 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
2210 '(1/2, 0, -z)')),
2211 '4j': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x, x, 0)',
2212 '(x, -x, 0)')),
2213 '4k': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(-x, x, 1/2)',
2214 '(x, -x, 1/2)')),
2215 '4l': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)'
2216 )),
2217 '4m': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 1/2)',
2218 '(0, -x, 1/2)')),
2219 '4n': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(1/2, x, 0)',
2220 '(1/2, -x, 0)')),
2221 '4o': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
2222 '(1/2, -x, 1/2)')),
2223 '8p': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 0)',
2224 '(y, -x, 0)', '(-x, y, 0)', '(x, -y, 0)',
2225 '(y, x, 0)', '(-y, -x, 0)')),
2226 '8q': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)', '(-y, x, 1/2)',
2227 '(y, -x, 1/2)', '(-x, y, 1/2)', '(x, -y, 1/2)',
2228 '(y, x, 1/2)', '(-y, -x, 1/2)')),
2229 '8r': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, z)',
2230 '(x, -x, z)', '(-x, x, -z)', '(x, -x, -z)',
2231 '(x, x, -z)', '(-x, -x, -z)')),
2232 '8s': (5, ('(x, 0, z)', '(-x, 0, z)', '(0, x, z)', '(0, -x, z)',
2233 '(-x, 0, -z)', '(x, 0, -z)', '(0, x, -z)',
2234 '(0, -x, -z)')),
2235 '8t': (5, ('(x, 1/2, z)', '(-x, 1/2, z)', '(1/2, x, z)',
2236 '(1/2, -x, z)', '(-x, 1/2, -z)', '(x, 1/2, -z)',
2237 '(1/2, x, -z)', '(1/2, -x, -z)')),
2238 '16u': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2239 '(y, -x, z)', '(-x, y, -z)', '(x, -y, -z)',
2240 '(y, x, -z)', '(-y, -x, -z)', '(-x, -y, -z)',
2241 '(x, y, -z)', '(y, -x, -z)', '(-y, x, -z)',
2242 '(x, -y, z)', '(-x, y, z)', '(-y, -x, z)',
2243 '(y, x, z)'))},
2244 '124': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
2245 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
2246 '2c': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
2247 '2d': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
2248 '4e': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
2249 '(1/2, 0, 1/2)')),
2250 '4f': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 1/4)', '(0, 1/2, 3/4)',
2251 '(1/2, 0, 3/4)')),
2252 '4g': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
2253 '(0, 0, z+1/2)')),
2254 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z+1/2)',
2255 '(1/2, 1/2, -z)', '(1/2, 1/2, z+1/2)')),
2256 '8i': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z+1/2)',
2257 '(1/2, 0, -z+1/2)', '(0, 1/2, -z)', '(1/2, 0, -z)',
2258 '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)')),
2259 '8j': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)', '(-x, x, 1/4)',
2260 '(x, -x, 1/4)', '(-x, -x, 3/4)', '(x, x, 3/4)',
2261 '(x, -x, 3/4)', '(-x, x, 3/4)')),
2262 '8k': (1, ('(x, 0, 1/4)', '(-x, 0, 1/4)', '(0, x, 1/4)',
2263 '(0, -x, 1/4)', '(-x, 0, 3/4)', '(x, 0, 3/4)',
2264 '(0, -x, 3/4)', '(0, x, 3/4)')),
2265 '8l': (1, ('(x, 1/2, 1/4)', '(-x, 1/2, 1/4)', '(1/2, x, 1/4)',
2266 '(1/2, -x, 1/4)', '(-x, 1/2, 3/4)', '(x, 1/2, 3/4)',
2267 '(1/2, -x, 3/4)', '(1/2, x, 3/4)')),
2268 '8m': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 0)',
2269 '(y, -x, 0)', '(-x, y, 1/2)', '(x, -y, 1/2)',
2270 '(y, x, 1/2)', '(-y, -x, 1/2)')),
2271 '16n': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2272 '(y, -x, z)', '(-x, y, -z+1/2)', '(x, -y, -z+1/2)',
2273 '(y, x, -z+1/2)', '(-y, -x, -z+1/2)',
2274 '(-x, -y, -z)', '(x, y, -z)', '(y, -x, -z)',
2275 '(-y, x, -z)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
2276 '(-y, -x, z+1/2)', '(y, x, z+1/2)'))},
2277 '125:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
2278 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2279 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
2280 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
2281 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 0)',
2282 '(1/4, 3/4, 0)')),
2283 '4f': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
2284 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
2285 '4g': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z)',
2286 '(1/2, 1/2, z)')),
2287 '4h': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
2288 '(1/2, 0, -z)')),
2289 '8i': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x, x, 0)',
2290 '(x, -x, 0)', '(-x+1/2, -x+1/2, 0)',
2291 '(x+1/2, x+1/2, 0)', '(x+1/2, -x+1/2, 0)',
2292 '(-x+1/2, x+1/2, 0)')),
2293 '8j': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(-x, x, 1/2)',
2294 '(x, -x, 1/2)', '(-x+1/2, -x+1/2, 1/2)',
2295 '(x+1/2, x+1/2, 1/2)', '(x+1/2, -x+1/2, 1/2)',
2296 '(-x+1/2, x+1/2, 1/2)')),
2297 '8k': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
2298 '(0, -x, 0)', '(-x+1/2, 1/2, 0)',
2299 '(x+1/2, 1/2, 0)', '(1/2, -x+1/2, 0)',
2300 '(1/2, x+1/2, 0)')),
2301 '8l': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 1/2)',
2302 '(0, -x, 1/2)', '(-x+1/2, 1/2, 1/2)',
2303 '(x+1/2, 1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
2304 '(1/2, x+1/2, 1/2)')),
2305 '8m': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)',
2306 '(-x+1/2, x, z)', '(x+1/2, -x, z)',
2307 '(-x, x+1/2, -z)', '(x, -x+1/2, -z)',
2308 '(x+1/2, x, -z)', '(-x+1/2, -x, -z)')),
2309 '16n': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2310 '(y, -x, z)', '(-x, y, -z)', '(x, -y, -z)',
2311 '(y, x, -z)', '(-y, -x, -z)',
2312 '(-x+1/2, -y+1/2, -z)', '(x+1/2, y+1/2, -z)',
2313 '(y+1/2, -x+1/2, -z)', '(-y+1/2, x+1/2, -z)',
2314 '(x+1/2, -y+1/2, z)', '(-x+1/2, y+1/2, z)',
2315 '(-y+1/2, -x+1/2, z)', '(y+1/2, x+1/2, z)'))},
2316 '125:2': {'2a': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)')),
2317 '2b': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)')),
2318 '2c': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 0)')),
2319 '2d': (0, ('(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
2320 '4e': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
2321 '(0, 1/2, 0)')),
2322 '4f': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
2323 '(0, 1/2, 1/2)')),
2324 '4g': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, -z)',
2325 '(3/4, 3/4, -z)', '(3/4, 3/4, z)')),
2326 '4h': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z)', '(3/4, 1/4, -z)',
2327 '(1/4, 3/4, -z)')),
2328 '8i': (1, ('(x, x, 0)', '(-x+1/2, -x+1/2, 0)',
2329 '(-x+1/2, x, 0)', '(x, -x+1/2, 0)', '(-x, -x, 0)',
2330 '(x+1/2, x+1/2, 0)', '(x+1/2, -x, 0)',
2331 '(-x, x+1/2, 0)')),
2332 '8j': (1, ('(x, x, 1/2)', '(-x+1/2, -x+1/2, 1/2)',
2333 '(-x+1/2, x, 1/2)', '(x, -x+1/2, 1/2)',
2334 '(-x, -x, 1/2)', '(x+1/2, x+1/2, 1/2)',
2335 '(x+1/2, -x, 1/2)', '(-x, x+1/2, 1/2)')),
2336 '8k': (1, ('(x, 1/4, 0)', '(-x+1/2, 1/4, 0)', '(1/4, x, 0)',
2337 '(1/4, -x+1/2, 0)', '(-x, 3/4, 0)',
2338 '(x+1/2, 3/4, 0)', '(3/4, -x, 0)',
2339 '(3/4, x+1/2, 0)')),
2340 '8l': (1, ('(x, 1/4, 1/2)', '(-x+1/2, 1/4, 1/2)',
2341 '(1/4, x, 1/2)', '(1/4, -x+1/2, 1/2)',
2342 '(-x, 3/4, 1/2)', '(x+1/2, 3/4, 1/2)',
2343 '(3/4, -x, 1/2)', '(3/4, x+1/2, 1/2)')),
2344 '8m': (5, ('(x, -x, z)', '(-x+1/2, x+1/2, z)',
2345 '(x+1/2, x, z)', '(-x, -x+1/2, z)',
2346 '(-x+1/2, -x, -z)', '(x, x+1/2, -z)',
2347 '(-x, x, -z)', '(x+1/2, -x+1/2, -z)')),
2348 '16n': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2349 '(-y+1/2, x, z)', '(y, -x+1/2, z)',
2350 '(-x+1/2, y, -z)', '(x, -y+1/2, -z)',
2351 '(y, x, -z)', '(-y+1/2, -x+1/2, -z)',
2352 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2353 '(y+1/2, -x, -z)', '(-y, x+1/2, -z)',
2354 '(x+1/2, -y, z)', '(-x, y+1/2, z)', '(-y, -x, z)',
2355 '(y+1/2, x+1/2, z)'))},
2356 '126:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2357 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2358 '4c': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 1/2, 1/2)',
2359 '(1/2, 0, 1/2)')),
2360 '4d': (0, ('(1/2, 0, 1/4)', '(0, 1/2, 1/4)', '(1/2, 0, 3/4)',
2361 '(0, 1/2, 3/4)')),
2362 '4e': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
2363 '(1/2, 1/2, z+1/2)')),
2364 '8f': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
2365 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
2366 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)',
2367 '(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)')),
2368 '8g': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 0, -z)',
2369 '(0, 1/2, -z)', '(0, 1/2, -z+1/2)',
2370 '(1/2, 0, -z+1/2)', '(0, 1/2, z+1/2)',
2371 '(1/2, 0, z+1/2)')),
2372 '8h': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x, x, 0)',
2373 '(x, -x, 0)', '(-x+1/2, -x+1/2, 1/2)',
2374 '(x+1/2, x+1/2, 1/2)', '(x+1/2, -x+1/2, 1/2)',
2375 '(-x+1/2, x+1/2, 1/2)')),
2376 '8i': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
2377 '(0, -x, 0)', '(-x+1/2, 1/2, 1/2)',
2378 '(x+1/2, 1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
2379 '(1/2, x+1/2, 1/2)')),
2380 '8j': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 1/2)',
2381 '(0, -x, 1/2)', '(-x+1/2, 1/2, 0)',
2382 '(x+1/2, 1/2, 0)', '(1/2, -x+1/2, 0)',
2383 '(1/2, x+1/2, 0)')),
2384 '16k': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2385 '(y, -x, z)', '(-x, y, -z)', '(x, -y, -z)',
2386 '(y, x, -z)', '(-y, -x, -z)',
2387 '(-x+1/2, -y+1/2, -z+1/2)',
2388 '(x+1/2, y+1/2, -z+1/2)',
2389 '(y+1/2, -x+1/2, -z+1/2)',
2390 '(-y+1/2, x+1/2, -z+1/2)',
2391 '(x+1/2, -y+1/2, z+1/2)',
2392 '(-x+1/2, y+1/2, z+1/2)',
2393 '(-y+1/2, -x+1/2, z+1/2)',
2394 '(y+1/2, x+1/2, z+1/2)'))},
2395 '126:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
2396 '2b': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
2397 '4c': (0, ('(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
2398 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)')),
2399 '4d': (0, ('(1/4, 3/4, 0)', '(3/4, 1/4, 0)',
2400 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
2401 '4e': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, -z+1/2)',
2402 '(3/4, 3/4, -z)', '(3/4, 3/4, z+1/2)')),
2403 '8f': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
2404 '(0, 1/2, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)',
2405 '(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2406 '8g': (4, ('(1/4, 3/4, z)', '(3/4, 1/4, z)',
2407 '(1/4, 3/4, -z+1/2)', '(3/4, 1/4, -z+1/2)',
2408 '(3/4, 1/4, -z)', '(1/4, 3/4, -z)',
2409 '(3/4, 1/4, z+1/2)', '(1/4, 3/4, z+1/2)')),
2410 '8h': (1, ('(x, x, 1/4)', '(-x+1/2, -x+1/2, 1/4)',
2411 '(-x+1/2, x, 1/4)', '(x, -x+1/2, 1/4)',
2412 '(-x, -x, 3/4)', '(x+1/2, x+1/2, 3/4)',
2413 '(x+1/2, -x, 3/4)', '(-x, x+1/2, 3/4)')),
2414 '8i': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
2415 '(1/4, x, 1/4)', '(1/4, -x+1/2, 1/4)',
2416 '(-x, 3/4, 3/4)', '(x+1/2, 3/4, 3/4)',
2417 '(3/4, -x, 3/4)', '(3/4, x+1/2, 3/4)')),
2418 '8j': (1, ('(x, 3/4, 1/4)', '(-x+1/2, 3/4, 1/4)',
2419 '(3/4, x, 1/4)', '(3/4, -x+1/2, 1/4)',
2420 '(-x, 1/4, 3/4)', '(x+1/2, 1/4, 3/4)',
2421 '(1/4, -x, 3/4)', '(1/4, x+1/2, 3/4)')),
2422 '16k': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2423 '(-y+1/2, x, z)', '(y, -x+1/2, z)',
2424 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
2425 '(y, x, -z+1/2)', '(-y+1/2, -x+1/2, -z+1/2)',
2426 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2427 '(y+1/2, -x, -z)', '(-y, x+1/2, -z)',
2428 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)',
2429 '(-y, -x, z+1/2)', '(y+1/2, x+1/2, z+1/2)'))},
2430 '127': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
2431 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2432 '2c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
2433 '2d': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
2434 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, -z)', '(0, 0, -z)',
2435 '(1/2, 1/2, z)')),
2436 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(1/2, 0, -z)',
2437 '(0, 1/2, -z)')),
2438 '4g': (1, ('(x, x+1/2, 0)', '(-x, -x+1/2, 0)', '(-x+1/2, x, 0)',
2439 '(x+1/2, -x, 0)')),
2440 '4h': (1, ('(x, x+1/2, 1/2)', '(-x, -x+1/2, 1/2)',
2441 '(-x+1/2, x, 1/2)', '(x+1/2, -x, 1/2)')),
2442 '8i': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 0)',
2443 '(y, -x, 0)', '(-x+1/2, y+1/2, 0)',
2444 '(x+1/2, -y+1/2, 0)', '(y+1/2, x+1/2, 0)',
2445 '(-y+1/2, -x+1/2, 0)')),
2446 '8j': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)', '(-y, x, 1/2)',
2447 '(y, -x, 1/2)', '(-x+1/2, y+1/2, 1/2)',
2448 '(x+1/2, -y+1/2, 1/2)', '(y+1/2, x+1/2, 1/2)',
2449 '(-y+1/2, -x+1/2, 1/2)')),
2450 '8k': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)', '(-x+1/2, x, z)',
2451 '(x+1/2, -x, z)', '(-x+1/2, x, -z)',
2452 '(x+1/2, -x, -z)', '(x, x+1/2, -z)',
2453 '(-x, -x+1/2, -z)')),
2454 '16l': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2455 '(y, -x, z)', '(-x+1/2, y+1/2, -z)',
2456 '(x+1/2, -y+1/2, -z)', '(y+1/2, x+1/2, -z)',
2457 '(-y+1/2, -x+1/2, -z)', '(-x, -y, -z)',
2458 '(x, y, -z)', '(y, -x, -z)', '(-y, x, -z)',
2459 '(x+1/2, -y+1/2, z)', '(-x+1/2, y+1/2, z)',
2460 '(-y+1/2, -x+1/2, z)', '(y+1/2, x+1/2, z)'))},
2461 '128': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2462 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2463 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
2464 '(0, 1/2, 1/2)')),
2465 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 1/4)', '(0, 1/2, 3/4)',
2466 '(1/2, 0, 3/4)')),
2467 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, -z+1/2)', '(0, 0, -z)',
2468 '(1/2, 1/2, z+1/2)')),
2469 '8f': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(1/2, 0, -z+1/2)',
2470 '(0, 1/2, -z+1/2)', '(0, 1/2, -z)', '(1/2, 0, -z)',
2471 '(1/2, 0, z+1/2)', '(0, 1/2, z+1/2)')),
2472 '8g': (1, ('(x, x+1/2, 1/4)', '(-x, -x+1/2, 1/4)',
2473 '(-x+1/2, x, 1/4)', '(x+1/2, -x, 1/4)',
2474 '(-x, -x+1/2, 3/4)', '(x, x+1/2, 3/4)',
2475 '(x+1/2, -x, 3/4)', '(-x+1/2, x, 3/4)')),
2476 '8h': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 0)',
2477 '(y, -x, 0)', '(-x+1/2, y+1/2, 1/2)',
2478 '(x+1/2, -y+1/2, 1/2)', '(y+1/2, x+1/2, 1/2)',
2479 '(-y+1/2, -x+1/2, 1/2)')),
2480 '16i': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2481 '(y, -x, z)', '(-x+1/2, y+1/2, -z+1/2)',
2482 '(x+1/2, -y+1/2, -z+1/2)', '(y+1/2, x+1/2, -z+1/2)',
2483 '(-y+1/2, -x+1/2, -z+1/2)', '(-x, -y, -z)',
2484 '(x, y, -z)', '(y, -x, -z)', '(-y, x, -z)',
2485 '(x+1/2, -y+1/2, z+1/2)', '(-x+1/2, y+1/2, z+1/2)',
2486 '(-y+1/2, -x+1/2, z+1/2)', '(y+1/2, x+1/2, z+1/2)'
2487 ))},
2488 '129:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
2489 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2490 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
2491 '4d': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(1/4, 3/4, 0)',
2492 '(3/4, 1/4, 0)')),
2493 '4e': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
2494 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
2495 '4f': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(1/2, 1/2, -z)',
2496 '(0, 0, -z)')),
2497 '8g': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x+1/2, x+1/2, 0)',
2498 '(x+1/2, -x+1/2, 0)', '(-x+1/2, -x+1/2, 0)',
2499 '(x+1/2, x+1/2, 0)', '(x, -x, 0)', '(-x, x, 0)')),
2500 '8h': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)',
2501 '(-x+1/2, x+1/2, 1/2)', '(x+1/2, -x+1/2, 1/2)',
2502 '(-x+1/2, -x+1/2, 1/2)', '(x+1/2, x+1/2, 1/2)',
2503 '(x, -x, 1/2)', '(-x, x, 1/2)')),
2504 '8i': (6, ('(0, y, z)', '(0, -y, z)', '(-y+1/2, 1/2, z)',
2505 '(y+1/2, 1/2, z)', '(1/2, y+1/2, -z)',
2506 '(1/2, -y+1/2, -z)', '(y, 0, -z)', '(-y, 0, -z)')),
2507 '8j': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)',
2508 '(-x, x+1/2, z)', '(x, -x+1/2, z)',
2509 '(-x+1/2, x, -z)', '(x+1/2, -x, -z)',
2510 '(x+1/2, x, -z)', '(-x+1/2, -x, -z)')),
2511 '16k': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z)',
2512 '(y+1/2, -x+1/2, z)', '(-x+1/2, y+1/2, -z)',
2513 '(x+1/2, -y+1/2, -z)', '(y, x, -z)',
2514 '(-y, -x, -z)', '(-x+1/2, -y+1/2, -z)',
2515 '(x+1/2, y+1/2, -z)', '(y, -x, -z)',
2516 '(-y, x, -z)', '(x, -y, z)', '(-x, y, z)',
2517 '(-y+1/2, -x+1/2, z)', '(y+1/2, x+1/2, z)'))},
2518 '129:2': {'2a': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 0)')),
2519 '2b': (0, ('(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
2520 '2c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, -z)')),
2521 '4d': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
2522 '(0, 1/2, 0)')),
2523 '4e': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
2524 '(0, 1/2, 1/2)')),
2525 '4f': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z)', '(1/4, 3/4, -z)',
2526 '(3/4, 1/4, -z)')),
2527 '8g': (1, ('(x, -x, 0)', '(-x+1/2, x+1/2, 0)',
2528 '(x+1/2, x, 0)', '(-x, -x+1/2, 0)', '(-x, x, 0)',
2529 '(x+1/2, -x+1/2, 0)', '(-x+1/2, -x, 0)',
2530 '(x, x+1/2, 0)')),
2531 '8h': (1, ('(x, -x, 1/2)', '(-x+1/2, x+1/2, 1/2)',
2532 '(x+1/2, x, 1/2)', '(-x, -x+1/2, 1/2)',
2533 '(-x, x, 1/2)', '(x+1/2, -x+1/2, 1/2)',
2534 '(-x+1/2, -x, 1/2)', '(x, x+1/2, 1/2)')),
2535 '8i': (6, ('(1/4, y, z)', '(1/4, -y+1/2, z)',
2536 '(-y+1/2, 1/4, z)', '(y, 1/4, z)',
2537 '(3/4, y+1/2, -z)', '(3/4, -y, -z)',
2538 '(y+1/2, 3/4, -z)', '(-y, 3/4, -z)')),
2539 '8j': (5, ('(x, x, z)', '(-x+1/2, -x+1/2, z)',
2540 '(-x+1/2, x, z)', '(x, -x+1/2, z)',
2541 '(-x, x+1/2, -z)', '(x+1/2, -x, -z)',
2542 '(x+1/2, x+1/2, -z)', '(-x, -x, -z)')),
2543 '16k': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2544 '(-y+1/2, x, z)', '(y, -x+1/2, z)',
2545 '(-x, y+1/2, -z)', '(x+1/2, -y, -z)',
2546 '(y+1/2, x+1/2, -z)', '(-y, -x, -z)',
2547 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2548 '(y+1/2, -x, -z)', '(-y, x+1/2, -z)',
2549 '(x, -y+1/2, z)', '(-x+1/2, y, z)',
2550 '(-y+1/2, -x+1/2, z)', '(y, x, z)'))},
2551 '130:1': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 1/4)',
2552 '(1/2, 1/2, 3/4)', '(0, 0, 3/4)')),
2553 '4b': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)',
2554 '(0, 0, 1/2)')),
2555 '4c': (4, ('(0, 1/2, z)', '(1/2, 0, -z+1/2)', '(1/2, 0, -z)',
2556 '(0, 1/2, z+1/2)')),
2557 '8d': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(1/4, 3/4, 0)',
2558 '(3/4, 1/4, 0)', '(1/4, 3/4, 1/2)',
2559 '(3/4, 1/4, 1/2)', '(1/4, 1/4, 1/2)',
2560 '(3/4, 3/4, 1/2)')),
2561 '8e': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(1/2, 1/2, -z+1/2)',
2562 '(0, 0, -z+1/2)', '(1/2, 1/2, -z)', '(0, 0, -z)',
2563 '(0, 0, z+1/2)', '(1/2, 1/2, z+1/2)')),
2564 '8f': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)',
2565 '(-x+1/2, x+1/2, 1/4)', '(x+1/2, -x+1/2, 1/4)',
2566 '(-x+1/2, -x+1/2, 3/4)', '(x+1/2, x+1/2, 3/4)',
2567 '(x, -x, 3/4)', '(-x, x, 3/4)')),
2568 '16g': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z)',
2569 '(y+1/2, -x+1/2, z)', '(-x+1/2, y+1/2, -z+1/2)',
2570 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z+1/2)',
2571 '(-y, -x, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
2572 '(x+1/2, y+1/2, -z)', '(y, -x, -z)',
2573 '(-y, x, -z)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
2574 '(-y+1/2, -x+1/2, z+1/2)',
2575 '(y+1/2, x+1/2, z+1/2)'))},
2576 '130:2': {'4a': (0, ('(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
2577 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)')),
2578 '4b': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 0)',
2579 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
2580 '4c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, -z+1/2)',
2581 '(3/4, 3/4, -z)', '(1/4, 1/4, z+1/2)')),
2582 '8d': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
2583 '(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
2584 '(1/2, 1/2, 1/2)', '(0, 0, 1/2)')),
2585 '8e': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z)',
2586 '(1/4, 3/4, -z+1/2)', '(3/4, 1/4, -z+1/2)',
2587 '(1/4, 3/4, -z)', '(3/4, 1/4, -z)',
2588 '(3/4, 1/4, z+1/2)', '(1/4, 3/4, z+1/2)')),
2589 '8f': (1, ('(x, -x, 1/4)', '(-x+1/2, x+1/2, 1/4)',
2590 '(x+1/2, x, 1/4)', '(-x, -x+1/2, 1/4)',
2591 '(-x, x, 3/4)', '(x+1/2, -x+1/2, 3/4)',
2592 '(-x+1/2, -x, 3/4)', '(x, x+1/2, 3/4)')),
2593 '16g': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2594 '(-y+1/2, x, z)', '(y, -x+1/2, z)',
2595 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y, -z+1/2)',
2596 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z+1/2)',
2597 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2598 '(y+1/2, -x, -z)', '(-y, x+1/2, -z)',
2599 '(x, -y+1/2, z+1/2)', '(-x+1/2, y, z+1/2)',
2600 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z+1/2)'))},
2601 '131': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
2602 '2b': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
2603 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
2604 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)')),
2605 '2e': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
2606 '2f': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
2607 '4g': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
2608 '(0, 0, -z+1/2)')),
2609 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)',
2610 '(1/2, 1/2, -z)', '(1/2, 1/2, -z+1/2)')),
2611 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, -z)',
2612 '(1/2, 0, -z+1/2)')),
2613 '4j': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 1/2)',
2614 '(0, -x, 1/2)')),
2615 '4k': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 0)',
2616 '(1/2, -x, 0)')),
2617 '4l': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 0)',
2618 '(0, -x, 0)')),
2619 '4m': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(1/2, x, 1/2)',
2620 '(1/2, -x, 1/2)')),
2621 '8n': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)', '(-x, x, 3/4)',
2622 '(x, -x, 3/4)', '(-x, -x, 3/4)', '(x, x, 3/4)',
2623 '(x, -x, 1/4)', '(-x, x, 1/4)')),
2624 '8o': (6, ('(0, y, z)', '(0, -y, z)', '(-y, 0, z+1/2)',
2625 '(y, 0, z+1/2)', '(0, y, -z)', '(0, -y, -z)',
2626 '(y, 0, -z+1/2)', '(-y, 0, -z+1/2)')),
2627 '8p': (6, ('(1/2, y, z)', '(1/2, -y, z)', '(-y, 1/2, z+1/2)',
2628 '(y, 1/2, z+1/2)', '(1/2, y, -z)', '(1/2, -y, -z)',
2629 '(y, 1/2, -z+1/2)', '(-y, 1/2, -z+1/2)')),
2630 '8q': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 1/2)',
2631 '(y, -x, 1/2)', '(-x, y, 0)', '(x, -y, 0)',
2632 '(y, x, 1/2)', '(-y, -x, 1/2)')),
2633 '16r': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
2634 '(y, -x, z+1/2)', '(-x, y, -z)', '(x, -y, -z)',
2635 '(y, x, -z+1/2)', '(-y, -x, -z+1/2)',
2636 '(-x, -y, -z)', '(x, y, -z)', '(y, -x, -z+1/2)',
2637 '(-y, x, -z+1/2)', '(x, -y, z)', '(-x, y, z)',
2638 '(-y, -x, z+1/2)', '(y, x, z+1/2)'))},
2639 '132': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
2640 '2b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
2641 '2c': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
2642 '2d': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
2643 '4e': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(0, 1/2, 3/4)',
2644 '(1/2, 0, 1/4)')),
2645 '4f': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)',
2646 '(1/2, 0, 0)')),
2647 '4g': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z+1/2)',
2648 '(0, 0, -z)')),
2649 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)',
2650 '(1/2, 1/2, -z+1/2)', '(1/2, 1/2, -z)')),
2651 '4i': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x, x, 1/2)',
2652 '(x, -x, 1/2)')),
2653 '4j': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(-x, x, 0)',
2654 '(x, -x, 0)')),
2655 '8k': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, -z+1/2)',
2656 '(1/2, 0, -z)', '(0, 1/2, -z)', '(1/2, 0, -z+1/2)',
2657 '(0, 1/2, z+1/2)', '(1/2, 0, z)')),
2658 '8l': (1, ('(x, 0, 1/4)', '(-x, 0, 1/4)', '(0, x, 3/4)',
2659 '(0, -x, 3/4)', '(-x, 0, 3/4)', '(x, 0, 3/4)',
2660 '(0, -x, 1/4)', '(0, x, 1/4)')),
2661 '8m': (1, ('(x, 1/2, 1/4)', '(-x, 1/2, 1/4)', '(1/2, x, 3/4)',
2662 '(1/2, -x, 3/4)', '(-x, 1/2, 3/4)', '(x, 1/2, 3/4)',
2663 '(1/2, -x, 1/4)', '(1/2, x, 1/4)')),
2664 '8n': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 1/2)',
2665 '(y, -x, 1/2)', '(-x, y, 1/2)', '(x, -y, 1/2)',
2666 '(y, x, 0)', '(-y, -x, 0)')),
2667 '8o': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, z+1/2)',
2668 '(x, -x, z+1/2)', '(-x, x, -z+1/2)',
2669 '(x, -x, -z+1/2)', '(x, x, -z)', '(-x, -x, -z)')),
2670 '16p': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
2671 '(y, -x, z+1/2)', '(-x, y, -z+1/2)',
2672 '(x, -y, -z+1/2)', '(y, x, -z)', '(-y, -x, -z)',
2673 '(-x, -y, -z)', '(x, y, -z)', '(y, -x, -z+1/2)',
2674 '(-y, x, -z+1/2)', '(x, -y, z+1/2)',
2675 '(-x, y, z+1/2)', '(-y, -x, z)', '(y, x, z)'))},
2676 '133:1': {'4a': (0, ('(0, 1/2, 1/4)', '(0, 1/2, 3/4)', '(1/2, 0, 1/4)',
2677 '(1/2, 0, 3/4)')),
2678 '4b': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)',
2679 '(1/2, 1/2, 1/4)', '(0, 0, 3/4)')),
2680 '4c': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
2681 '(1/2, 0, 0)')),
2682 '4d': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 0, 1/2)',
2683 '(1/2, 1/2, 0)')),
2684 '8e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
2685 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
2686 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
2687 '(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)')),
2688 '8f': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)',
2689 '(0, 1/2, -z+1/2)', '(0, 1/2, -z)',
2690 '(1/2, 0, -z+1/2)', '(1/2, 0, -z)', '(1/2, 0, z)',
2691 '(1/2, 0, z+1/2)')),
2692 '8g': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z+1/2)',
2693 '(1/2, 1/2, -z)', '(1/2, 1/2, -z+1/2)',
2694 '(0, 0, -z)', '(1/2, 1/2, z)', '(0, 0, z+1/2)')),
2695 '8h': (1, ('(x, 0, 1/4)', '(-x, 0, 1/4)', '(1/2, x+1/2, 3/4)',
2696 '(1/2, -x+1/2, 3/4)', '(-x+1/2, 1/2, 1/4)',
2697 '(x+1/2, 1/2, 1/4)', '(0, -x, 3/4)', '(0, x, 3/4)'
2698 )),
2699 '8i': (1, ('(x, 0, 3/4)', '(-x, 0, 3/4)', '(1/2, x+1/2, 1/4)',
2700 '(1/2, -x+1/2, 1/4)', '(-x+1/2, 1/2, 3/4)',
2701 '(x+1/2, 1/2, 3/4)', '(0, -x, 1/4)', '(0, x, 1/4)'
2702 )),
2703 '8j': (1, ('(x, x+1/2, 0)', '(-x, -x+1/2, 0)',
2704 '(-x, x+1/2, 1/2)', '(x, -x+1/2, 1/2)',
2705 '(-x+1/2, -x, 1/2)', '(x+1/2, x, 1/2)',
2706 '(x+1/2, -x, 0)', '(-x+1/2, x, 0)')),
2707 '16k': (7, ('(x, y, z)', '(-x, -y, z)',
2708 '(-y+1/2, x+1/2, z+1/2)',
2709 '(y+1/2, -x+1/2, z+1/2)', '(-x, y, -z+1/2)',
2710 '(x, -y, -z+1/2)', '(y+1/2, x+1/2, -z)',
2711 '(-y+1/2, -x+1/2, -z)',
2712 '(-x+1/2, -y+1/2, -z+1/2)',
2713 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
2714 '(-y, x, -z)', '(x+1/2, -y+1/2, z)',
2715 '(-x+1/2, y+1/2, z)', '(-y, -x, z+1/2)',
2716 '(y, x, z+1/2)'))},
2717 '133:2': {'4a': (0, ('(1/4, 1/4, 0)', '(1/4, 1/4, 1/2)',
2718 '(3/4, 3/4, 0)', '(3/4, 3/4, 1/2)')),
2719 '4b': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 1/2)',
2720 '(1/4, 3/4, 0)', '(3/4, 1/4, 1/2)')),
2721 '4c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 1/4, 3/4)',
2722 '(3/4, 3/4, 3/4)', '(3/4, 3/4, 1/4)')),
2723 '4d': (0, ('(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
2724 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)')),
2725 '8e': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
2726 '(0, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
2727 '(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2728 '8f': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, z+1/2)',
2729 '(1/4, 1/4, -z)', '(1/4, 1/4, -z+1/2)',
2730 '(3/4, 3/4, -z)', '(3/4, 3/4, -z+1/2)',
2731 '(3/4, 3/4, z)', '(3/4, 3/4, z+1/2)')),
2732 '8g': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
2733 '(3/4, 1/4, -z)', '(1/4, 3/4, -z+1/2)',
2734 '(1/4, 3/4, -z)', '(3/4, 1/4, -z+1/2)',
2735 '(1/4, 3/4, z)', '(3/4, 1/4, z+1/2)')),
2736 '8h': (1, ('(x, 1/4, 0)', '(-x+1/2, 1/4, 0)', '(1/4, x, 1/2)',
2737 '(1/4, -x+1/2, 1/2)', '(-x, 3/4, 0)',
2738 '(x+1/2, 3/4, 0)', '(3/4, -x, 1/2)',
2739 '(3/4, x+1/2, 1/2)')),
2740 '8i': (1, ('(x, 1/4, 1/2)', '(-x+1/2, 1/4, 1/2)',
2741 '(1/4, x, 0)', '(1/4, -x+1/2, 0)',
2742 '(-x, 3/4, 1/2)', '(x+1/2, 3/4, 1/2)',
2743 '(3/4, -x, 0)', '(3/4, x+1/2, 0)')),
2744 '8j': (1, ('(x, x, 1/4)', '(-x+1/2, -x+1/2, 1/4)',
2745 '(-x+1/2, x, 3/4)', '(x, -x+1/2, 3/4)',
2746 '(-x, -x, 3/4)', '(x+1/2, x+1/2, 3/4)',
2747 '(x+1/2, -x, 1/4)', '(-x, x+1/2, 1/4)')),
2748 '16k': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2749 '(-y+1/2, x, z+1/2)', '(y, -x+1/2, z+1/2)',
2750 '(-x+1/2, y, -z)', '(x, -y+1/2, -z)',
2751 '(y, x, -z+1/2)', '(-y+1/2, -x+1/2, -z+1/2)',
2752 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2753 '(y+1/2, -x, -z+1/2)', '(-y, x+1/2, -z+1/2)',
2754 '(x+1/2, -y, z)', '(-x, y+1/2, z)',
2755 '(-y, -x, z+1/2)', '(y+1/2, x+1/2, z+1/2)'))},
2756 '134:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2757 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2758 '4c': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
2759 '(1/2, 0, 0)')),
2760 '4d': (0, ('(0, 1/2, 1/4)', '(0, 1/2, 3/4)', '(1/2, 0, 1/4)',
2761 '(1/2, 0, 3/4)')),
2762 '4e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
2763 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)')),
2764 '4f': (0, ('(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
2765 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)')),
2766 '4g': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
2767 '(1/2, 1/2, -z+1/2)')),
2768 '8h': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)', '(0, 1/2, -z)',
2769 '(0, 1/2, -z+1/2)', '(1/2, 0, -z+1/2)',
2770 '(1/2, 0, -z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)')),
2771 '8i': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(1/2, x+1/2, 1/2)',
2772 '(1/2, -x+1/2, 1/2)', '(-x+1/2, 1/2, 1/2)',
2773 '(x+1/2, 1/2, 1/2)', '(0, -x, 0)', '(0, x, 0)')),
2774 '8j': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x+1/2, 0)',
2775 '(1/2, -x+1/2, 0)', '(-x+1/2, 1/2, 0)',
2776 '(x+1/2, 1/2, 0)', '(0, -x, 1/2)', '(0, x, 1/2)')),
2777 '8k': (1, ('(x, x+1/2, 1/4)', '(-x, -x+1/2, 1/4)',
2778 '(-x, x+1/2, 3/4)', '(x, -x+1/2, 3/4)',
2779 '(-x+1/2, -x, 1/4)', '(x+1/2, x, 1/4)',
2780 '(x+1/2, -x, 3/4)', '(-x+1/2, x, 3/4)')),
2781 '8l': (1, ('(x, x+1/2, 3/4)', '(-x, -x+1/2, 3/4)',
2782 '(-x, x+1/2, 1/4)', '(x, -x+1/2, 1/4)',
2783 '(-x+1/2, -x, 3/4)', '(x+1/2, x, 3/4)',
2784 '(x+1/2, -x, 1/4)', '(-x+1/2, x, 1/4)')),
2785 '8m': (5, ('(x, x, z)', '(-x, -x, z)',
2786 '(-x+1/2, x+1/2, z+1/2)', '(x+1/2, -x+1/2, z+1/2)',
2787 '(-x, x, -z)', '(x, -x, -z)',
2788 '(x+1/2, x+1/2, -z+1/2)',
2789 '(-x+1/2, -x+1/2, -z+1/2)')),
2790 '16n': (7, ('(x, y, z)', '(-x, -y, z)',
2791 '(-y+1/2, x+1/2, z+1/2)',
2792 '(y+1/2, -x+1/2, z+1/2)', '(-x, y, -z)',
2793 '(x, -y, -z)', '(y+1/2, x+1/2, -z+1/2)',
2794 '(-y+1/2, -x+1/2, -z+1/2)',
2795 '(-x+1/2, -y+1/2, -z+1/2)',
2796 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
2797 '(-y, x, -z)', '(x+1/2, -y+1/2, z+1/2)',
2798 '(-x+1/2, y+1/2, z+1/2)', '(-y, -x, z)',
2799 '(y, x, z)'))},
2800 '134:2': {'2a': (0, ('(1/4, 3/4, 1/4)', '(3/4, 1/4, 3/4)')),
2801 '2b': (0, ('(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)')),
2802 '4c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 1/4, 3/4)',
2803 '(3/4, 3/4, 3/4)', '(3/4, 3/4, 1/4)')),
2804 '4d': (0, ('(1/4, 1/4, 0)', '(1/4, 1/4, 1/2)',
2805 '(3/4, 3/4, 0)', '(3/4, 3/4, 1/2)')),
2806 '4e': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 0)',
2807 '(0, 1/2, 0)')),
2808 '4f': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
2809 '(0, 1/2, 1/2)')),
2810 '4g': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
2811 '(3/4, 1/4, -z+1/2)', '(1/4, 3/4, -z)')),
2812 '8h': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, z+1/2)',
2813 '(1/4, 1/4, -z+1/2)', '(1/4, 1/4, -z)',
2814 '(3/4, 3/4, -z)', '(3/4, 3/4, -z+1/2)',
2815 '(3/4, 3/4, z+1/2)', '(3/4, 3/4, z)')),
2816 '8i': (1, ('(x, 1/4, 3/4)', '(-x+1/2, 1/4, 3/4)',
2817 '(1/4, x, 1/4)', '(1/4, -x+1/2, 1/4)',
2818 '(-x, 3/4, 1/4)', '(x+1/2, 3/4, 1/4)',
2819 '(3/4, -x, 3/4)', '(3/4, x+1/2, 3/4)')),
2820 '8j': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
2821 '(1/4, x, 3/4)', '(1/4, -x+1/2, 3/4)',
2822 '(-x, 3/4, 3/4)', '(x+1/2, 3/4, 3/4)',
2823 '(3/4, -x, 1/4)', '(3/4, x+1/2, 1/4)')),
2824 '8k': (1, ('(x, x, 0)', '(-x+1/2, -x+1/2, 0)',
2825 '(-x+1/2, x, 1/2)', '(x, -x+1/2, 1/2)',
2826 '(-x, -x, 0)', '(x+1/2, x+1/2, 0)',
2827 '(x+1/2, -x, 1/2)', '(-x, x+1/2, 1/2)')),
2828 '8l': (1, ('(x, x, 1/2)', '(-x+1/2, -x+1/2, 1/2)',
2829 '(-x+1/2, x, 0)', '(x, -x+1/2, 0)',
2830 '(-x, -x, 1/2)', '(x+1/2, x+1/2, 1/2)',
2831 '(x+1/2, -x, 0)', '(-x, x+1/2, 0)')),
2832 '8m': (5, ('(x, -x, z)', '(-x+1/2, x+1/2, z)',
2833 '(x+1/2, x, z+1/2)', '(-x, -x+1/2, z+1/2)',
2834 '(-x+1/2, -x, -z+1/2)', '(x, x+1/2, -z+1/2)',
2835 '(-x, x, -z)', '(x+1/2, -x+1/2, -z)')),
2836 '16n': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2837 '(-y+1/2, x, z+1/2)', '(y, -x+1/2, z+1/2)',
2838 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
2839 '(y, x, -z)', '(-y+1/2, -x+1/2, -z)',
2840 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2841 '(y+1/2, -x, -z+1/2)', '(-y, x+1/2, -z+1/2)',
2842 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)',
2843 '(-y, -x, z)', '(y+1/2, x+1/2, z)'))},
2844 '135': {'4a': (0, ('(0, 0, 0)', '(0, 0, 1/2)', '(1/2, 1/2, 0)',
2845 '(1/2, 1/2, 1/2)')),
2846 '4b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)', '(1/2, 1/2, 3/4)',
2847 '(1/2, 1/2, 1/4)')),
2848 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
2849 '(0, 1/2, 1/2)')),
2850 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(0, 1/2, 3/4)',
2851 '(1/2, 0, 1/4)')),
2852 '8e': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(1/2, 1/2, -z)',
2853 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)', '(0, 0, -z+1/2)',
2854 '(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
2855 '8f': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
2856 '(0, 1/2, -z+1/2)', '(0, 1/2, -z)',
2857 '(1/2, 0, -z+1/2)', '(1/2, 0, z)', '(0, 1/2, z+1/2)'
2858 )),
2859 '8g': (1, ('(x, x+1/2, 1/4)', '(-x, -x+1/2, 1/4)',
2860 '(-x+1/2, x, 3/4)', '(x+1/2, -x, 3/4)',
2861 '(-x, -x+1/2, 3/4)', '(x, x+1/2, 3/4)',
2862 '(x+1/2, -x, 1/4)', '(-x+1/2, x, 1/4)')),
2863 '8h': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 1/2)',
2864 '(y, -x, 1/2)', '(-x+1/2, y+1/2, 0)',
2865 '(x+1/2, -y+1/2, 0)', '(y+1/2, x+1/2, 1/2)',
2866 '(-y+1/2, -x+1/2, 1/2)')),
2867 '16i': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
2868 '(y, -x, z+1/2)', '(-x+1/2, y+1/2, -z)',
2869 '(x+1/2, -y+1/2, -z)', '(y+1/2, x+1/2, -z+1/2)',
2870 '(-y+1/2, -x+1/2, -z+1/2)', '(-x, -y, -z)',
2871 '(x, y, -z)', '(y, -x, -z+1/2)', '(-y, x, -z+1/2)',
2872 '(x+1/2, -y+1/2, z)', '(-x+1/2, y+1/2, z)',
2873 '(-y+1/2, -x+1/2, z+1/2)', '(y+1/2, x+1/2, z+1/2)'
2874 ))},
2875 '136': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2876 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2877 '4c': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
2878 '(1/2, 0, 0)')),
2879 '4d': (0, ('(0, 1/2, 1/4)', '(0, 1/2, 3/4)', '(1/2, 0, 1/4)',
2880 '(1/2, 0, 3/4)')),
2881 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
2882 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)')),
2883 '4f': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x+1/2, x+1/2, 1/2)',
2884 '(x+1/2, -x+1/2, 1/2)')),
2885 '4g': (1, ('(x, -x, 0)', '(-x, x, 0)', '(x+1/2, x+1/2, 1/2)',
2886 '(-x+1/2, -x+1/2, 1/2)')),
2887 '8h': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)', '(1/2, 0, -z+1/2)',
2888 '(1/2, 0, -z)', '(0, 1/2, -z)', '(0, 1/2, -z+1/2)',
2889 '(1/2, 0, z+1/2)', '(1/2, 0, z)')),
2890 '8i': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y+1/2, x+1/2, 1/2)',
2891 '(y+1/2, -x+1/2, 1/2)', '(-x+1/2, y+1/2, 1/2)',
2892 '(x+1/2, -y+1/2, 1/2)', '(y, x, 0)', '(-y, -x, 0)')),
2893 '8j': (5, ('(x, x, z)', '(-x, -x, z)', '(-x+1/2, x+1/2, z+1/2)',
2894 '(x+1/2, -x+1/2, z+1/2)', '(-x+1/2, x+1/2, -z+1/2)',
2895 '(x+1/2, -x+1/2, -z+1/2)', '(x, x, -z)',
2896 '(-x, -x, -z)')),
2897 '16k': (7, ('(x, y, z)', '(-x, -y, z)',
2898 '(-y+1/2, x+1/2, z+1/2)', '(y+1/2, -x+1/2, z+1/2)',
2899 '(-x+1/2, y+1/2, -z+1/2)',
2900 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z)',
2901 '(-y, -x, -z)', '(-x, -y, -z)', '(x, y, -z)',
2902 '(y+1/2, -x+1/2, -z+1/2)',
2903 '(-y+1/2, x+1/2, -z+1/2)', '(x+1/2, -y+1/2, z+1/2)',
2904 '(-x+1/2, y+1/2, z+1/2)', '(-y, -x, z)',
2905 '(y, x, z)'))},
2906 '137:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2907 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2908 '4c': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
2909 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)')),
2910 '4d': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)',
2911 '(1/2, 0, -z+1/2)', '(1/2, 0, -z)')),
2912 '8e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
2913 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
2914 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)',
2915 '(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)')),
2916 '8f': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x+1/2, x+1/2, 1/2)',
2917 '(x+1/2, -x+1/2, 1/2)', '(-x+1/2, -x+1/2, 1/2)',
2918 '(x+1/2, x+1/2, 1/2)', '(x, -x, 0)', '(-x, x, 0)'
2919 )),
2920 '8g': (6, ('(0, y, z)', '(0, -y, z)', '(-y+1/2, 1/2, z+1/2)',
2921 '(y+1/2, 1/2, z+1/2)', '(1/2, y+1/2, -z+1/2)',
2922 '(1/2, -y+1/2, -z+1/2)', '(y, 0, -z)',
2923 '(-y, 0, -z)')),
2924 '16h': (7, ('(x, y, z)', '(-x, -y, z)',
2925 '(-y+1/2, x+1/2, z+1/2)',
2926 '(y+1/2, -x+1/2, z+1/2)',
2927 '(-x+1/2, y+1/2, -z+1/2)',
2928 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z)',
2929 '(-y, -x, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
2930 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
2931 '(-y, x, -z)', '(x, -y, z)', '(-x, y, z)',
2932 '(-y+1/2, -x+1/2, z+1/2)',
2933 '(y+1/2, x+1/2, z+1/2)'))},
2934 '137:2': {'2a': (0, ('(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)')),
2935 '2b': (0, ('(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)')),
2936 '4c': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
2937 '(1/4, 3/4, -z)', '(3/4, 1/4, -z+1/2)')),
2938 '4d': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, z+1/2)',
2939 '(3/4, 3/4, -z)', '(3/4, 3/4, -z+1/2)')),
2940 '8e': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
2941 '(0, 1/2, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 0)',
2942 '(1/2, 1/2, 1/2)', '(0, 0, 1/2)')),
2943 '8f': (1, ('(x, -x, 1/4)', '(-x+1/2, x+1/2, 1/4)',
2944 '(x+1/2, x, 3/4)', '(-x, -x+1/2, 3/4)',
2945 '(-x, x, 3/4)', '(x+1/2, -x+1/2, 3/4)',
2946 '(-x+1/2, -x, 1/4)', '(x, x+1/2, 1/4)')),
2947 '8g': (6, ('(1/4, y, z)', '(1/4, -y+1/2, z)',
2948 '(-y+1/2, 1/4, z+1/2)', '(y, 1/4, z+1/2)',
2949 '(3/4, y+1/2, -z)', '(3/4, -y, -z)',
2950 '(y+1/2, 3/4, -z+1/2)', '(-y, 3/4, -z+1/2)')),
2951 '16h': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2952 '(-y+1/2, x, z+1/2)', '(y, -x+1/2, z+1/2)',
2953 '(-x, y+1/2, -z)', '(x+1/2, -y, -z)',
2954 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z+1/2)',
2955 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2956 '(y+1/2, -x, -z+1/2)', '(-y, x+1/2, -z+1/2)',
2957 '(x, -y+1/2, z)', '(-x+1/2, y, z)',
2958 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z+1/2)'))},
2959 '138:1': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)',
2960 '(1/2, 1/2, 1/4)', '(0, 0, 3/4)')),
2961 '4b': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 1/2, 0)',
2962 '(0, 0, 1/2)')),
2963 '4c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
2964 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)')),
2965 '4d': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)',
2966 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
2967 '4e': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)', '(1/2, 0, -z)',
2968 '(1/2, 0, -z+1/2)')),
2969 '8f': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 1/2, -z)',
2970 '(0, 0, -z+1/2)', '(1/2, 1/2, -z+1/2)',
2971 '(0, 0, -z)', '(0, 0, z+1/2)', '(1/2, 1/2, z)')),
2972 '8g': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)',
2973 '(-x+1/2, x+1/2, 3/4)', '(x+1/2, -x+1/2, 3/4)',
2974 '(-x+1/2, -x+1/2, 1/4)', '(x+1/2, x+1/2, 1/4)',
2975 '(x, -x, 3/4)', '(-x, x, 3/4)')),
2976 '8h': (1, ('(x, x, 3/4)', '(-x, -x, 3/4)',
2977 '(-x+1/2, x+1/2, 1/4)', '(x+1/2, -x+1/2, 1/4)',
2978 '(-x+1/2, -x+1/2, 3/4)', '(x+1/2, x+1/2, 3/4)',
2979 '(x, -x, 1/4)', '(-x, x, 1/4)')),
2980 '8i': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)',
2981 '(-x, x+1/2, z+1/2)', '(x, -x+1/2, z+1/2)',
2982 '(-x+1/2, x, -z)', '(x+1/2, -x, -z)',
2983 '(x+1/2, x, -z+1/2)', '(-x+1/2, -x, -z+1/2)')),
2984 '16j': (7, ('(x, y, z)', '(-x, -y, z)',
2985 '(-y+1/2, x+1/2, z+1/2)',
2986 '(y+1/2, -x+1/2, z+1/2)', '(-x+1/2, y+1/2, -z)',
2987 '(x+1/2, -y+1/2, -z)', '(y, x, -z+1/2)',
2988 '(-y, -x, -z+1/2)', '(-x+1/2, -y+1/2, -z+1/2)',
2989 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
2990 '(-y, x, -z)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
2991 '(-y+1/2, -x+1/2, z)', '(y+1/2, x+1/2, z)'))},
2992 '138:2': {'4a': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 1/2)',
2993 '(1/4, 3/4, 0)', '(3/4, 1/4, 1/2)')),
2994 '4b': (0, ('(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
2995 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
2996 '4c': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 0)',
2997 '(0, 1/2, 0)')),
2998 '4d': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
2999 '(0, 1/2, 1/2)')),
3000 '4e': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, z+1/2)',
3001 '(3/4, 3/4, -z+1/2)', '(3/4, 3/4, -z)')),
3002 '8f': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
3003 '(1/4, 3/4, -z+1/2)', '(3/4, 1/4, -z)',
3004 '(1/4, 3/4, -z)', '(3/4, 1/4, -z+1/2)',
3005 '(3/4, 1/4, z+1/2)', '(1/4, 3/4, z)')),
3006 '8g': (1, ('(x, -x, 1/2)', '(-x+1/2, x+1/2, 1/2)',
3007 '(x+1/2, x, 0)', '(-x, -x+1/2, 0)', '(-x, x, 1/2)',
3008 '(x+1/2, -x+1/2, 1/2)', '(-x+1/2, -x, 0)',
3009 '(x, x+1/2, 0)')),
3010 '8h': (1, ('(x, -x, 0)', '(-x+1/2, x+1/2, 0)',
3011 '(x+1/2, x, 1/2)', '(-x, -x+1/2, 1/2)',
3012 '(-x, x, 0)', '(x+1/2, -x+1/2, 0)',
3013 '(-x+1/2, -x, 1/2)', '(x, x+1/2, 1/2)')),
3014 '8i': (5, ('(x, x, z)', '(-x+1/2, -x+1/2, z)',
3015 '(-x+1/2, x, z+1/2)', '(x, -x+1/2, z+1/2)',
3016 '(-x, x+1/2, -z+1/2)', '(x+1/2, -x, -z+1/2)',
3017 '(x+1/2, x+1/2, -z)', '(-x, -x, -z)')),
3018 '16j': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
3019 '(-y+1/2, x, z+1/2)', '(y, -x+1/2, z+1/2)',
3020 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y, -z+1/2)',
3021 '(y+1/2, x+1/2, -z)', '(-y, -x, -z)',
3022 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
3023 '(y+1/2, -x, -z+1/2)', '(-y, x+1/2, -z+1/2)',
3024 '(x, -y+1/2, z+1/2)', '(-x+1/2, y, z+1/2)',
3025 '(-y+1/2, -x+1/2, z)', '(y, x, z)'))},
3026 '139': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
3027 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
3028 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
3029 '(0, 1/2, 1/2)')),
3030 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(1/2, 0, 1/4)',
3031 '(0, 1/2, 3/4)')),
3032 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
3033 '(1/2, 1/2, -z+1/2)')),
3034 '8f': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
3035 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
3036 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
3037 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 3/4)')),
3038 '8g': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
3039 '(0, 1/2, z+1/2)', '(0, 1/2, -z)',
3040 '(1/2, 0, -z+1/2)', '(1/2, 0, -z)',
3041 '(0, 1/2, -z+1/2)')),
3042 '8h': (1, ('(x, x, 0)', '(x+1/2, x+1/2, 1/2)', '(-x, -x, 0)',
3043 '(-x+1/2, -x+1/2, 1/2)', '(-x, x, 0)',
3044 '(-x+1/2, x+1/2, 1/2)', '(x, -x, 0)',
3045 '(x+1/2, -x+1/2, 1/2)')),
3046 '8i': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
3047 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
3048 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
3049 '(1/2, -x+1/2, 1/2)')),
3050 '8j': (1, ('(x, 1/2, 0)', '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
3051 '(-x+1/2, 0, 1/2)', '(1/2, x, 0)', '(0, x+1/2, 1/2)',
3052 '(1/2, -x, 0)', '(0, -x+1/2, 1/2)')),
3053 '16k': (1, ('(x, x+1/2, 1/4)', '(x+1/2, x, 3/4)',
3054 '(-x, -x+1/2, 1/4)', '(-x+1/2, -x, 3/4)',
3055 '(-x+1/2, x, 1/4)', '(-x, x+1/2, 3/4)',
3056 '(x+1/2, -x, 1/4)', '(x, -x+1/2, 3/4)',
3057 '(-x, -x+1/2, 3/4)', '(-x+1/2, -x, 1/4)',
3058 '(x, x+1/2, 3/4)', '(x+1/2, x, 1/4)',
3059 '(x+1/2, -x, 3/4)', '(x, -x+1/2, 1/4)',
3060 '(-x+1/2, x, 3/4)', '(-x, x+1/2, 1/4)')),
3061 '16l': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 0)',
3062 '(-x+1/2, -y+1/2, 1/2)', '(-y, x, 0)',
3063 '(-y+1/2, x+1/2, 1/2)', '(y, -x, 0)',
3064 '(y+1/2, -x+1/2, 1/2)', '(-x, y, 0)',
3065 '(-x+1/2, y+1/2, 1/2)', '(x, -y, 0)',
3066 '(x+1/2, -y+1/2, 1/2)', '(y, x, 0)',
3067 '(y+1/2, x+1/2, 1/2)', '(-y, -x, 0)',
3068 '(-y+1/2, -x+1/2, 1/2)')),
3069 '16m': (5, ('(x, x, z)', '(x+1/2, x+1/2, z+1/2)', '(-x, -x, z)',
3070 '(-x+1/2, -x+1/2, z+1/2)', '(-x, x, z)',
3071 '(-x+1/2, x+1/2, z+1/2)', '(x, -x, z)',
3072 '(x+1/2, -x+1/2, z+1/2)', '(-x, x, -z)',
3073 '(-x+1/2, x+1/2, -z+1/2)', '(x, -x, -z)',
3074 '(x+1/2, -x+1/2, -z+1/2)', '(x, x, -z)',
3075 '(x+1/2, x+1/2, -z+1/2)', '(-x, -x, -z)',
3076 '(-x+1/2, -x+1/2, -z+1/2)')),
3077 '16n': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y, z)',
3078 '(1/2, -y+1/2, z+1/2)', '(-y, 0, z)',
3079 '(-y+1/2, 1/2, z+1/2)', '(y, 0, z)',
3080 '(y+1/2, 1/2, z+1/2)', '(0, y, -z)',
3081 '(1/2, y+1/2, -z+1/2)', '(0, -y, -z)',
3082 '(1/2, -y+1/2, -z+1/2)', '(y, 0, -z)',
3083 '(y+1/2, 1/2, -z+1/2)', '(-y, 0, -z)',
3084 '(-y+1/2, 1/2, -z+1/2)')),
3085 '32o': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
3086 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
3087 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
3088 '(y+1/2, -x+1/2, z+1/2)', '(-x, y, -z)',
3089 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
3090 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z)',
3091 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z)',
3092 '(-y+1/2, -x+1/2, -z+1/2)', '(-x, -y, -z)',
3093 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
3094 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
3095 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
3096 '(-y+1/2, x+1/2, -z+1/2)', '(x, -y, z)',
3097 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
3098 '(-x+1/2, y+1/2, z+1/2)', '(-y, -x, z)',
3099 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z)',
3100 '(y+1/2, x+1/2, z+1/2)'))},
3101 '140': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)', '(0, 0, 3/4)',
3102 '(1/2, 1/2, 1/4)')),
3103 '4b': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(1/2, 0, 1/4)',
3104 '(0, 1/2, 3/4)')),
3105 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 0, 1/2)',
3106 '(1/2, 1/2, 0)')),
3107 '4d': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
3108 '(0, 1/2, 1/2)')),
3109 '8e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
3110 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
3111 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
3112 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 3/4)')),
3113 '8f': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z+1/2)',
3114 '(1/2, 1/2, -z)', '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
3115 '(0, 0, z+1/2)', '(1/2, 1/2, z)')),
3116 '8g': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
3117 '(0, 1/2, z+1/2)', '(0, 1/2, -z+1/2)',
3118 '(1/2, 0, -z)', '(1/2, 0, -z+1/2)', '(0, 1/2, -z)')),
3119 '8h': (1, ('(x, x+1/2, 0)', '(x+1/2, x, 1/2)',
3120 '(-x, -x+1/2, 0)', '(-x+1/2, -x, 1/2)',
3121 '(-x+1/2, x, 0)', '(-x, x+1/2, 1/2)',
3122 '(x+1/2, -x, 0)', '(x, -x+1/2, 1/2)')),
3123 '16i': (1, ('(x, x, 1/4)', '(x+1/2, x+1/2, 3/4)',
3124 '(-x, -x, 1/4)', '(-x+1/2, -x+1/2, 3/4)',
3125 '(-x, x, 1/4)', '(-x+1/2, x+1/2, 3/4)',
3126 '(x, -x, 1/4)', '(x+1/2, -x+1/2, 3/4)',
3127 '(-x, -x, 3/4)', '(-x+1/2, -x+1/2, 1/4)',
3128 '(x, x, 3/4)', '(x+1/2, x+1/2, 1/4)',
3129 '(x, -x, 3/4)', '(x+1/2, -x+1/2, 1/4)',
3130 '(-x, x, 3/4)', '(-x+1/2, x+1/2, 1/4)')),
3131 '16j': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)', '(-x, 0, 1/4)',
3132 '(-x+1/2, 1/2, 3/4)', '(0, x, 1/4)',
3133 '(1/2, x+1/2, 3/4)', '(0, -x, 1/4)',
3134 '(1/2, -x+1/2, 3/4)', '(-x, 0, 3/4)',
3135 '(-x+1/2, 1/2, 1/4)', '(x, 0, 3/4)',
3136 '(x+1/2, 1/2, 1/4)', '(0, -x, 3/4)',
3137 '(1/2, -x+1/2, 1/4)', '(0, x, 3/4)',
3138 '(1/2, x+1/2, 1/4)')),
3139 '16k': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 0)',
3140 '(-x+1/2, -y+1/2, 1/2)', '(-y, x, 0)',
3141 '(-y+1/2, x+1/2, 1/2)', '(y, -x, 0)',
3142 '(y+1/2, -x+1/2, 1/2)', '(-x, y, 1/2)',
3143 '(-x+1/2, y+1/2, 0)', '(x, -y, 1/2)',
3144 '(x+1/2, -y+1/2, 0)', '(y, x, 1/2)',
3145 '(y+1/2, x+1/2, 0)', '(-y, -x, 1/2)',
3146 '(-y+1/2, -x+1/2, 0)')),
3147 '16l': (5, ('(x, x+1/2, z)', '(x+1/2, x, z+1/2)',
3148 '(-x, -x+1/2, z)', '(-x+1/2, -x, z+1/2)',
3149 '(-x+1/2, x, z)', '(-x, x+1/2, z+1/2)',
3150 '(x+1/2, -x, z)', '(x, -x+1/2, z+1/2)',
3151 '(-x, x+1/2, -z+1/2)', '(-x+1/2, x, -z)',
3152 '(x, -x+1/2, -z+1/2)', '(x+1/2, -x, -z)',
3153 '(x+1/2, x, -z+1/2)', '(x, x+1/2, -z)',
3154 '(-x+1/2, -x, -z+1/2)', '(-x, -x+1/2, -z)')),
3155 '32m': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
3156 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
3157 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
3158 '(y+1/2, -x+1/2, z+1/2)', '(-x, y, -z+1/2)',
3159 '(-x+1/2, y+1/2, -z)', '(x, -y, -z+1/2)',
3160 '(x+1/2, -y+1/2, -z)', '(y, x, -z+1/2)',
3161 '(y+1/2, x+1/2, -z)', '(-y, -x, -z+1/2)',
3162 '(-y+1/2, -x+1/2, -z)', '(-x, -y, -z)',
3163 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
3164 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
3165 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
3166 '(-y+1/2, x+1/2, -z+1/2)', '(x, -y, z+1/2)',
3167 '(x+1/2, -y+1/2, z)', '(-x, y, z+1/2)',
3168 '(-x+1/2, y+1/2, z)', '(-y, -x, z+1/2)',
3169 '(-y+1/2, -x+1/2, z)', '(y, x, z+1/2)',
3170 '(y+1/2, x+1/2, z)'))},
3171 '141:1': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/4)',
3172 '(1/2, 0, 3/4)')),
3173 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(0, 1/2, 3/4)',
3174 '(1/2, 0, 1/4)')),
3175 '8c': (0, ('(0, 1/4, 1/8)', '(1/2, 3/4, 5/8)',
3176 '(1/2, 1/4, 5/8)', '(0, 3/4, 1/8)',
3177 '(3/4, 1/2, 3/8)', '(1/4, 0, 7/8)',
3178 '(3/4, 0, 7/8)', '(1/4, 1/2, 3/8)')),
3179 '8d': (0, ('(0, 1/4, 5/8)', '(1/2, 3/4, 1/8)',
3180 '(1/2, 1/4, 1/8)', '(0, 3/4, 5/8)',
3181 '(3/4, 1/2, 7/8)', '(1/4, 0, 3/8)',
3182 '(3/4, 0, 3/8)', '(1/4, 1/2, 7/8)')),
3183 '8e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
3184 '(0, 1/2, z+1/4)', '(1/2, 0, z+3/4)',
3185 '(1/2, 0, -z+3/4)', '(0, 1/2, -z+1/4)',
3186 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)')),
3187 '16f': (1, ('(x, 1/4, 1/8)', '(x+1/2, 3/4, 5/8)',
3188 '(-x+1/2, 1/4, 5/8)', '(-x, 3/4, 1/8)',
3189 '(3/4, x+1/2, 3/8)', '(1/4, x, 7/8)',
3190 '(3/4, -x, 7/8)', '(1/4, -x+1/2, 3/8)',
3191 '(-x, 1/4, 1/8)', '(-x+1/2, 3/4, 5/8)',
3192 '(x+1/2, 1/4, 5/8)', '(x, 3/4, 1/8)',
3193 '(1/4, -x, 7/8)', '(3/4, -x+1/2, 3/8)',
3194 '(1/4, x+1/2, 3/8)', '(3/4, x, 7/8)')),
3195 '16g': (1, ('(x, x, 0)', '(x+1/2, x+1/2, 1/2)',
3196 '(-x+1/2, -x+1/2, 1/2)', '(-x, -x, 0)',
3197 '(-x, x+1/2, 1/4)', '(-x+1/2, x, 3/4)',
3198 '(x+1/2, -x, 3/4)', '(x, -x+1/2, 1/4)',
3199 '(-x, -x+1/2, 1/4)', '(-x+1/2, -x, 3/4)',
3200 '(x+1/2, x, 3/4)', '(x, x+1/2, 1/4)',
3201 '(x, -x, 0)', '(x+1/2, -x+1/2, 1/2)',
3202 '(-x+1/2, x+1/2, 1/2)', '(-x, x, 0)')),
3203 '16h': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)',
3204 '(1/2, -y+1/2, z+1/2)', '(0, -y, z)',
3205 '(-y, 1/2, z+1/4)', '(-y+1/2, 0, z+3/4)',
3206 '(y+1/2, 0, z+3/4)', '(y, 1/2, z+1/4)',
3207 '(1/2, y, -z+3/4)', '(0, y+1/2, -z+1/4)',
3208 '(0, -y+1/2, -z+1/4)', '(1/2, -y, -z+3/4)',
3209 '(y+1/2, 1/2, -z+1/2)', '(y, 0, -z)',
3210 '(-y, 0, -z)', '(-y+1/2, 1/2, -z+1/2)')),
3211 '32i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
3212 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
3213 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
3214 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
3215 '(-x+1/2, y, -z+3/4)', '(-x, y+1/2, -z+1/4)',
3216 '(x, -y+1/2, -z+1/4)', '(x+1/2, -y, -z+3/4)',
3217 '(y+1/2, x+1/2, -z+1/2)', '(y, x, -z)',
3218 '(-y, -x, -z)', '(-y+1/2, -x+1/2, -z+1/2)',
3219 '(-x, -y+1/2, -z+1/4)', '(-x+1/2, -y, -z+3/4)',
3220 '(x+1/2, y, -z+3/4)', '(x, y+1/2, -z+1/4)',
3221 '(y, -x, -z)', '(y+1/2, -x+1/2, -z+1/2)',
3222 '(-y+1/2, x+1/2, -z+1/2)', '(-y, x, -z)',
3223 '(x+1/2, -y+1/2, z+1/2)', '(x, -y, z)',
3224 '(-x, y, z)', '(-x+1/2, y+1/2, z+1/2)',
3225 '(-y+1/2, -x, z+3/4)', '(-y, -x+1/2, z+1/4)',
3226 '(y, x+1/2, z+1/4)', '(y+1/2, x, z+3/4)'))},
3227 '141:2': {'4a': (0, ('(0, 3/4, 1/8)', '(1/2, 1/4, 5/8)',
3228 '(1/2, 3/4, 3/8)', '(0, 1/4, 7/8)')),
3229 '4b': (0, ('(0, 1/4, 3/8)', '(1/2, 3/4, 7/8)',
3230 '(0, 3/4, 5/8)', '(1/2, 1/4, 1/8)')),
3231 '8c': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
3232 '(0, 1/2, 0)', '(1/4, 3/4, 1/4)',
3233 '(3/4, 1/4, 3/4)', '(1/4, 1/4, 3/4)',
3234 '(3/4, 3/4, 1/4)')),
3235 '8d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
3236 '(0, 1/2, 1/2)', '(1/4, 3/4, 3/4)',
3237 '(3/4, 1/4, 1/4)', '(1/4, 1/4, 1/4)',
3238 '(3/4, 3/4, 3/4)')),
3239 '8e': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)',
3240 '(0, 3/4, z+1/4)', '(1/2, 1/4, z+3/4)',
3241 '(1/2, 1/4, -z+1/2)', '(0, 3/4, -z)',
3242 '(1/2, 3/4, -z+1/4)', '(0, 1/4, -z+3/4)')),
3243 '16f': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)',
3244 '(-x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
3245 '(1/4, x+3/4, 1/4)', '(3/4, x+1/4, 3/4)',
3246 '(1/4, -x+1/4, 3/4)', '(3/4, -x+3/4, 1/4)',
3247 '(-x, 0, 0)', '(-x+1/2, 1/2, 1/2)',
3248 '(x+1/2, 0, 1/2)', '(x, 1/2, 0)',
3249 '(3/4, -x+1/4, 3/4)', '(1/4, -x+3/4, 1/4)',
3250 '(3/4, x+3/4, 1/4)', '(1/4, x+1/4, 3/4)')),
3251 '16g': (1, ('(x, x+1/4, 7/8)', '(x+1/2, x+3/4, 3/8)',
3252 '(-x+1/2, -x+3/4, 3/8)', '(-x, -x+1/4, 7/8)',
3253 '(-x, x+3/4, 1/8)', '(-x+1/2, x+1/4, 5/8)',
3254 '(x+1/2, -x+1/4, 5/8)', '(x, -x+3/4, 1/8)',
3255 '(-x, -x+3/4, 1/8)', '(-x+1/2, -x+1/4, 5/8)',
3256 '(x+1/2, x+1/4, 5/8)', '(x, x+3/4, 1/8)',
3257 '(x, -x+1/4, 7/8)', '(x+1/2, -x+3/4, 3/8)',
3258 '(-x+1/2, x+3/4, 3/8)', '(-x, x+1/4, 7/8)')),
3259 '16h': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)',
3260 '(1/2, -y, z+1/2)', '(0, -y+1/2, z)',
3261 '(-y+1/4, 3/4, z+1/4)', '(-y+3/4, 1/4, z+3/4)',
3262 '(y+1/4, 1/4, z+3/4)', '(y+3/4, 3/4, z+1/4)',
3263 '(1/2, y, -z+1/2)', '(0, y+1/2, -z)',
3264 '(0, -y, -z)', '(1/2, -y+1/2, -z+1/2)',
3265 '(y+1/4, 3/4, -z+1/4)', '(y+3/4, 1/4, -z+3/4)',
3266 '(-y+1/4, 1/4, -z+3/4)', '(-y+3/4, 3/4, -z+1/4)'
3267 )),
3268 '32i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
3269 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
3270 '(-y+1/4, x+3/4, z+1/4)',
3271 '(-y+3/4, x+1/4, z+3/4)',
3272 '(y+1/4, -x+1/4, z+3/4)',
3273 '(y+3/4, -x+3/4, z+1/4)', '(-x+1/2, y, -z+1/2)',
3274 '(-x, y+1/2, -z)', '(x, -y, -z)',
3275 '(x+1/2, -y+1/2, -z+1/2)',
3276 '(y+1/4, x+3/4, -z+1/4)',
3277 '(y+3/4, x+1/4, -z+3/4)',
3278 '(-y+1/4, -x+1/4, -z+3/4)',
3279 '(-y+3/4, -x+3/4, -z+1/4)', '(-x, -y, -z)',
3280 '(-x+1/2, -y+1/2, -z+1/2)', '(x+1/2, y, -z+1/2)',
3281 '(x, y+1/2, -z)', '(y+3/4, -x+1/4, -z+3/4)',
3282 '(y+1/4, -x+3/4, -z+1/4)',
3283 '(-y+3/4, x+3/4, -z+1/4)',
3284 '(-y+1/4, x+1/4, -z+3/4)', '(x+1/2, -y, z+1/2)',
3285 '(x, -y+1/2, z)', '(-x, y, z)',
3286 '(-x+1/2, y+1/2, z+1/2)',
3287 '(-y+3/4, -x+1/4, z+3/4)',
3288 '(-y+1/4, -x+3/4, z+1/4)',
3289 '(y+3/4, x+3/4, z+1/4)', '(y+1/4, x+1/4, z+3/4)'
3290 ))},
3291 '142:1': {'8a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/4)',
3292 '(1/2, 0, 3/4)', '(1/2, 0, 1/4)', '(0, 1/2, 3/4)',
3293 '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
3294 '8b': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)', '(0, 1/2, 1/2)',
3295 '(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 0, 1/2)',
3296 '(0, 0, 3/4)', '(1/2, 1/2, 1/4)')),
3297 '16c': (0, ('(0, 1/4, 1/8)', '(1/2, 3/4, 5/8)',
3298 '(1/2, 1/4, 5/8)', '(0, 3/4, 1/8)',
3299 '(3/4, 1/2, 3/8)', '(1/4, 0, 7/8)',
3300 '(3/4, 0, 7/8)', '(1/4, 1/2, 3/8)',
3301 '(1/2, 1/4, 1/8)', '(0, 3/4, 5/8)',
3302 '(0, 1/4, 5/8)', '(1/2, 3/4, 1/8)',
3303 '(3/4, 1/2, 7/8)', '(1/4, 0, 3/8)',
3304 '(3/4, 0, 3/8)', '(1/4, 1/2, 7/8)')),
3305 '16d': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
3306 '(0, 1/2, z+1/4)', '(1/2, 0, z+3/4)',
3307 '(1/2, 0, -z+1/4)', '(0, 1/2, -z+3/4)',
3308 '(1/2, 1/2, -z)', '(0, 0, -z+1/2)',
3309 '(0, 1/2, -z+1/4)', '(1/2, 0, -z+3/4)',
3310 '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
3311 '(1/2, 1/2, z)', '(0, 0, z+1/2)',
3312 '(1/2, 0, z+1/4)', '(0, 1/2, z+3/4)')),
3313 '16e': (2, ('(1/4, y, 1/8)', '(3/4, y+1/2, 5/8)',
3314 '(1/4, -y+1/2, 5/8)', '(3/4, -y, 1/8)',
3315 '(-y, 3/4, 3/8)', '(-y+1/2, 1/4, 7/8)',
3316 '(y+1/2, 3/4, 7/8)', '(y, 1/4, 3/8)',
3317 '(3/4, -y+1/2, 1/8)', '(1/4, -y, 5/8)',
3318 '(3/4, y, 5/8)', '(1/4, y+1/2, 1/8)',
3319 '(y, 3/4, 7/8)', '(y+1/2, 1/4, 3/8)',
3320 '(-y+1/2, 3/4, 3/8)', '(-y, 1/4, 7/8)')),
3321 '16f': (1, ('(x, x, 1/4)', '(x+1/2, x+1/2, 3/4)',
3322 '(-x+1/2, -x+1/2, 3/4)', '(-x, -x, 1/4)',
3323 '(-x, x+1/2, 1/2)', '(-x+1/2, x, 0)',
3324 '(x+1/2, -x, 0)', '(x, -x+1/2, 1/2)',
3325 '(-x, -x+1/2, 0)', '(-x+1/2, -x, 1/2)',
3326 '(x+1/2, x, 1/2)', '(x, x+1/2, 0)',
3327 '(x, -x, 3/4)', '(x+1/2, -x+1/2, 1/4)',
3328 '(-x+1/2, x+1/2, 1/4)', '(-x, x, 3/4)')),
3329 '32g': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
3330 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
3331 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
3332 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
3333 '(-x+1/2, y, -z+1/4)', '(-x, y+1/2, -z+3/4)',
3334 '(x, -y+1/2, -z+3/4)', '(x+1/2, -y, -z+1/4)',
3335 '(y+1/2, x+1/2, -z)', '(y, x, -z+1/2)',
3336 '(-y, -x, -z+1/2)', '(-y+1/2, -x+1/2, -z)',
3337 '(-x, -y+1/2, -z+1/4)', '(-x+1/2, -y, -z+3/4)',
3338 '(x+1/2, y, -z+3/4)', '(x, y+1/2, -z+1/4)',
3339 '(y, -x, -z)', '(y+1/2, -x+1/2, -z+1/2)',
3340 '(-y+1/2, x+1/2, -z+1/2)', '(-y, x, -z)',
3341 '(x+1/2, -y+1/2, z)', '(x, -y, z+1/2)',
3342 '(-x, y, z+1/2)', '(-x+1/2, y+1/2, z)',
3343 '(-y+1/2, -x, z+1/4)', '(-y, -x+1/2, z+3/4)',
3344 '(y, x+1/2, z+3/4)', '(y+1/2, x, z+1/4)'))},
3345 '142:2': {'8a': (0, ('(0, 1/4, 3/8)', '(1/2, 3/4, 7/8)',
3346 '(0, 3/4, 5/8)', '(1/2, 1/4, 1/8)',
3347 '(1/2, 1/4, 5/8)', '(0, 3/4, 1/8)',
3348 '(1/2, 3/4, 3/8)', '(0, 1/4, 7/8)')),
3349 '8b': (0, ('(0, 1/4, 1/8)', '(1/2, 3/4, 5/8)',
3350 '(0, 3/4, 3/8)', '(1/2, 1/4, 7/8)',
3351 '(0, 3/4, 7/8)', '(1/2, 1/4, 3/8)',
3352 '(0, 1/4, 5/8)', '(1/2, 3/4, 1/8)')),
3353 '16c': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
3354 '(0, 1/2, 0)', '(1/4, 3/4, 1/4)',
3355 '(3/4, 1/4, 3/4)', '(1/4, 1/4, 3/4)',
3356 '(3/4, 3/4, 1/4)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
3357 '(0, 0, 1/2)', '(1/2, 1/2, 0)', '(1/4, 3/4, 3/4)',
3358 '(3/4, 1/4, 1/4)', '(1/4, 1/4, 1/4)',
3359 '(3/4, 3/4, 3/4)')),
3360 '16d': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)',
3361 '(0, 3/4, z+1/4)', '(1/2, 1/4, z+3/4)',
3362 '(1/2, 1/4, -z)', '(0, 3/4, -z+1/2)',
3363 '(1/2, 3/4, -z+3/4)', '(0, 1/4, -z+1/4)',
3364 '(0, 3/4, -z)', '(1/2, 1/4, -z+1/2)',
3365 '(0, 1/4, -z+3/4)', '(1/2, 3/4, -z+1/4)',
3366 '(1/2, 3/4, z)', '(0, 1/4, z+1/2)',
3367 '(1/2, 1/4, z+1/4)', '(0, 3/4, z+3/4)')),
3368 '16e': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
3369 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)',
3370 '(1/4, x+3/4, 1/2)', '(3/4, x+1/4, 0)',
3371 '(1/4, -x+1/4, 0)', '(3/4, -x+3/4, 1/2)',
3372 '(-x, 0, 3/4)', '(-x+1/2, 1/2, 1/4)',
3373 '(x+1/2, 0, 1/4)', '(x, 1/2, 3/4)',
3374 '(3/4, -x+1/4, 1/2)', '(1/4, -x+3/4, 0)',
3375 '(3/4, x+3/4, 0)', '(1/4, x+1/4, 1/2)')),
3376 '16f': (1, ('(x, x+1/4, 1/8)', '(x+1/2, x+3/4, 5/8)',
3377 '(-x+1/2, -x+3/4, 5/8)', '(-x, -x+1/4, 1/8)',
3378 '(-x, x+3/4, 3/8)', '(-x+1/2, x+1/4, 7/8)',
3379 '(x+1/2, -x+1/4, 7/8)', '(x, -x+3/4, 3/8)',
3380 '(-x, -x+3/4, 7/8)', '(-x+1/2, -x+1/4, 3/8)',
3381 '(x+1/2, x+1/4, 3/8)', '(x, x+3/4, 7/8)',
3382 '(x, -x+1/4, 5/8)', '(x+1/2, -x+3/4, 1/8)',
3383 '(-x+1/2, x+3/4, 1/8)', '(-x, x+1/4, 5/8)')),
3384 '32g': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
3385 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
3386 '(-y+1/4, x+3/4, z+1/4)',
3387 '(-y+3/4, x+1/4, z+3/4)',
3388 '(y+1/4, -x+1/4, z+3/4)',
3389 '(y+3/4, -x+3/4, z+1/4)', '(-x+1/2, y, -z)',
3390 '(-x, y+1/2, -z+1/2)', '(x, -y, -z+1/2)',
3391 '(x+1/2, -y+1/2, -z)', '(y+1/4, x+3/4, -z+3/4)',
3392 '(y+3/4, x+1/4, -z+1/4)',
3393 '(-y+1/4, -x+1/4, -z+1/4)',
3394 '(-y+3/4, -x+3/4, -z+3/4)', '(-x, -y, -z)',
3395 '(-x+1/2, -y+1/2, -z+1/2)', '(x+1/2, y, -z+1/2)',
3396 '(x, y+1/2, -z)', '(y+3/4, -x+1/4, -z+3/4)',
3397 '(y+1/4, -x+3/4, -z+1/4)',
3398 '(-y+3/4, x+3/4, -z+1/4)',
3399 '(-y+1/4, x+1/4, -z+3/4)', '(x+1/2, -y, z)',
3400 '(x, -y+1/2, z+1/2)', '(-x, y, z+1/2)',
3401 '(-x+1/2, y+1/2, z)', '(-y+3/4, -x+1/4, z+1/4)',
3402 '(-y+1/4, -x+3/4, z+3/4)',
3403 '(y+3/4, x+3/4, z+3/4)', '(y+1/4, x+1/4, z+1/4)'
3404 ))},
3405 '143': {'1a': (4, ('(0, 0, z)', )),
3406 '1b': (4, ('(1/3, 2/3, z)', )),
3407 '1c': (4, ('(2/3, 1/3, z)', )),
3408 '3d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)'))},
3409 '144': {'3a': (7, ('(x, y, z)', '(-y, x-y, z+1/3)', '(-x+y, -x, z+2/3)'
3410 ))},
3411 '145': {'3a': (7, ('(x, y, z)', '(-y, x-y, z+2/3)', '(-x+y, -x, z+1/3)'
3412 ))},
3413 '146:H': {'3a': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3414 '(1/3, 2/3, z+2/3)')),
3415 '9b': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3416 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3417 '(-y+2/3, x-y+1/3, z+1/3)',
3418 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3419 '(-x+y+2/3, -x+1/3, z+1/3)',
3420 '(-x+y+1/3, -x+2/3, z+2/3)'))},
3421 '146:R': {'1a': (1, ('(x, x, x)', )),
3422 '3b': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)'))},
3423 '147': {'1a': (0, ('(0, 0, 0)', )),
3424 '1b': (0, ('(0, 0, 1/2)', )),
3425 '2c': (4, ('(0, 0, z)', '(0, 0, -z)')),
3426 '2d': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, -z)')),
3427 '3e': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
3428 '3f': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3429 )),
3430 '6g': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3431 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)'))},
3432 '148:H': {'3a': (0, ('(0, 0, 0)', '(2/3, 1/3, 1/3)', '(1/3, 2/3, 2/3)'
3433 )),
3434 '3b': (0, ('(0, 0, 1/2)', '(2/3, 1/3, 5/6)',
3435 '(1/3, 2/3, 1/6)')),
3436 '6c': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3437 '(1/3, 2/3, z+2/3)', '(0, 0, -z)',
3438 '(2/3, 1/3, -z+1/3)', '(1/3, 2/3, -z+2/3)')),
3439 '9d': (0, ('(1/2, 0, 1/2)', '(1/6, 1/3, 5/6)',
3440 '(5/6, 2/3, 1/6)', '(0, 1/2, 1/2)',
3441 '(2/3, 5/6, 5/6)', '(1/3, 1/6, 1/6)',
3442 '(1/2, 1/2, 1/2)', '(1/6, 5/6, 5/6)',
3443 '(5/6, 1/6, 1/6)')),
3444 '9e': (0, ('(1/2, 0, 0)', '(1/6, 1/3, 1/3)',
3445 '(5/6, 2/3, 2/3)', '(0, 1/2, 0)',
3446 '(2/3, 5/6, 1/3)', '(1/3, 1/6, 2/3)',
3447 '(1/2, 1/2, 0)', '(1/6, 5/6, 1/3)',
3448 '(5/6, 1/6, 2/3)')),
3449 '18f': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3450 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3451 '(-y+2/3, x-y+1/3, z+1/3)',
3452 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3453 '(-x+y+2/3, -x+1/3, z+1/3)',
3454 '(-x+y+1/3, -x+2/3, z+2/3)', '(-x, -y, -z)',
3455 '(-x+2/3, -y+1/3, -z+1/3)',
3456 '(-x+1/3, -y+2/3, -z+2/3)', '(y, -x+y, -z)',
3457 '(y+2/3, -x+y+1/3, -z+1/3)',
3458 '(y+1/3, -x+y+2/3, -z+2/3)', '(x-y, x, -z)',
3459 '(x-y+2/3, x+1/3, -z+1/3)',
3460 '(x-y+1/3, x+2/3, -z+2/3)'))},
3461 '148:R': {'1a': (0, ('(0, 0, 0)', )),
3462 '1b': (0, ('(1/2, 1/2, 1/2)', )),
3463 '2c': (1, ('(x, x, x)', '(-x, -x, -x)')),
3464 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
3465 '3e': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)'
3466 )),
3467 '6f': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)',
3468 '(-x, -y, -z)', '(-z, -x, -y)', '(-y, -z, -x)'))},
3469 '149': {'1a': (0, ('(0, 0, 0)', )),
3470 '1b': (0, ('(0, 0, 1/2)', )),
3471 '1c': (0, ('(1/3, 2/3, 0)', )),
3472 '1d': (0, ('(1/3, 2/3, 1/2)', )),
3473 '1e': (0, ('(2/3, 1/3, 0)', )),
3474 '1f': (0, ('(2/3, 1/3, 1/2)', )),
3475 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
3476 '2h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z)')),
3477 '2i': (4, ('(2/3, 1/3, z)', '(2/3, 1/3, -z)')),
3478 '3j': (1, ('(x, -x, 0)', '(x, 2*x, 0)', '(-2*x, -x, 0)')),
3479 '3k': (1, ('(x, -x, 1/2)', '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)')),
3480 '6l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3481 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)'))},
3482 '150': {'1a': (0, ('(0, 0, 0)', )),
3483 '1b': (0, ('(0, 0, 1/2)', )),
3484 '2c': (4, ('(0, 0, z)', '(0, 0, -z)')),
3485 '2d': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, -z)')),
3486 '3e': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)')),
3487 '3f': (1, ('(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)')),
3488 '6g': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3489 '(y, x, -z)', '(x-y, -y, -z)', '(-x, -x+y, -z)'))},
3490 '151': {'3a': (1, ('(x, -x, 1/3)', '(x, 2*x, 2/3)', '(-2*x, -x, 0)')),
3491 '3b': (1, ('(x, -x, 5/6)', '(x, 2*x, 1/6)', '(-2*x, -x, 1/2)')),
3492 '6c': (7, ('(x, y, z)', '(-y, x-y, z+1/3)', '(-x+y, -x, z+2/3)',
3493 '(-y, -x, -z+2/3)', '(-x+y, y, -z+1/3)',
3494 '(x, x-y, -z)'))},
3495 '152': {'3a': (1, ('(x, 0, 1/3)', '(0, x, 2/3)', '(-x, -x, 0)')),
3496 '3b': (1, ('(x, 0, 5/6)', '(0, x, 1/6)', '(-x, -x, 1/2)')),
3497 '6c': (7, ('(x, y, z)', '(-y, x-y, z+1/3)', '(-x+y, -x, z+2/3)',
3498 '(y, x, -z)', '(x-y, -y, -z+2/3)',
3499 '(-x, -x+y, -z+1/3)'))},
3500 '153': {'3a': (1, ('(x, -x, 2/3)', '(x, 2*x, 1/3)', '(-2*x, -x, 0)')),
3501 '3b': (1, ('(x, -x, 1/6)', '(x, 2*x, 5/6)', '(-2*x, -x, 1/2)')),
3502 '6c': (7, ('(x, y, z)', '(-y, x-y, z+2/3)', '(-x+y, -x, z+1/3)',
3503 '(-y, -x, -z+1/3)', '(-x+y, y, -z+2/3)',
3504 '(x, x-y, -z)'))},
3505 '154': {'3a': (1, ('(x, 0, 2/3)', '(0, x, 1/3)', '(-x, -x, 0)')),
3506 '3b': (1, ('(x, 0, 1/6)', '(0, x, 5/6)', '(-x, -x, 1/2)')),
3507 '6c': (7, ('(x, y, z)', '(-y, x-y, z+2/3)', '(-x+y, -x, z+1/3)',
3508 '(y, x, -z)', '(x-y, -y, -z+1/3)',
3509 '(-x, -x+y, -z+2/3)'))},
3510 '155:H': {'3a': (0, ('(0, 0, 0)', '(2/3, 1/3, 1/3)', '(1/3, 2/3, 2/3)'
3511 )),
3512 '3b': (0, ('(0, 0, 1/2)', '(2/3, 1/3, 5/6)',
3513 '(1/3, 2/3, 1/6)')),
3514 '6c': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3515 '(1/3, 2/3, z+2/3)', '(0, 0, -z)',
3516 '(2/3, 1/3, -z+1/3)', '(1/3, 2/3, -z+2/3)')),
3517 '9d': (1, ('(x, 0, 0)', '(x+2/3, 1/3, 1/3)',
3518 '(x+1/3, 2/3, 2/3)', '(0, x, 0)',
3519 '(2/3, x+1/3, 1/3)', '(1/3, x+2/3, 2/3)',
3520 '(-x, -x, 0)', '(-x+2/3, -x+1/3, 1/3)',
3521 '(-x+1/3, -x+2/3, 2/3)')),
3522 '9e': (1, ('(x, 0, 1/2)', '(x+2/3, 1/3, 5/6)',
3523 '(x+1/3, 2/3, 1/6)', '(0, x, 1/2)',
3524 '(2/3, x+1/3, 5/6)', '(1/3, x+2/3, 1/6)',
3525 '(-x, -x, 1/2)', '(-x+2/3, -x+1/3, 5/6)',
3526 '(-x+1/3, -x+2/3, 1/6)')),
3527 '18f': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3528 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3529 '(-y+2/3, x-y+1/3, z+1/3)',
3530 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3531 '(-x+y+2/3, -x+1/3, z+1/3)',
3532 '(-x+y+1/3, -x+2/3, z+2/3)', '(y, x, -z)',
3533 '(y+2/3, x+1/3, -z+1/3)',
3534 '(y+1/3, x+2/3, -z+2/3)', '(x-y, -y, -z)',
3535 '(x-y+2/3, -y+1/3, -z+1/3)',
3536 '(x-y+1/3, -y+2/3, -z+2/3)', '(-x, -x+y, -z)',
3537 '(-x+2/3, -x+y+1/3, -z+1/3)',
3538 '(-x+1/3, -x+y+2/3, -z+2/3)'))},
3539 '155:R': {'1a': (0, ('(0, 0, 0)', )),
3540 '1b': (0, ('(1/2, 1/2, 1/2)', )),
3541 '2c': (1, ('(x, x, x)', '(-x, -x, -x)')),
3542 '3d': (2, ('(0, y, -y)', '(-y, 0, y)', '(y, -y, 0)')),
3543 '3e': (2, ('(1/2, y, -y)', '(-y, 1/2, y)', '(y, -y, 1/2)')),
3544 '6f': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)',
3545 '(-z, -y, -x)', '(-y, -x, -z)', '(-x, -z, -y)'))},
3546 '156': {'1a': (4, ('(0, 0, z)', )),
3547 '1b': (4, ('(1/3, 2/3, z)', )),
3548 '1c': (4, ('(2/3, 1/3, z)', )),
3549 '3d': (5, ('(x, -x, z)', '(x, 2*x, z)', '(-2*x, -x, z)')),
3550 '6e': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3551 '(-y, -x, z)', '(-x+y, y, z)', '(x, x-y, z)'))},
3552 '157': {'1a': (4, ('(0, 0, z)', )),
3553 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)')),
3554 '3c': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)')),
3555 '6d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3556 '(y, x, z)', '(x-y, -y, z)', '(-x, -x+y, z)'))},
3557 '158': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
3558 '2b': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, z+1/2)')),
3559 '2c': (4, ('(2/3, 1/3, z)', '(2/3, 1/3, z+1/2)')),
3560 '6d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3561 '(-y, -x, z+1/2)', '(-x+y, y, z+1/2)',
3562 '(x, x-y, z+1/2)'))},
3563 '159': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
3564 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)')),
3565 '6c': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3566 '(y, x, z+1/2)', '(x-y, -y, z+1/2)',
3567 '(-x, -x+y, z+1/2)'))},
3568 '160:H': {'3a': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3569 '(1/3, 2/3, z+2/3)')),
3570 '9b': (5, ('(x, -x, z)', '(x+2/3, -x+1/3, z+1/3)',
3571 '(x+1/3, -x+2/3, z+2/3)', '(x, 2*x, z)',
3572 '(x+2/3, 2*x+1/3, z+1/3)',
3573 '(x+1/3, 2*x+2/3, z+2/3)', '(-2*x, -x, z)',
3574 '(-2*x+2/3, -x+1/3, z+1/3)',
3575 '(-2*x+1/3, -x+2/3, z+2/3)')),
3576 '18c': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3577 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3578 '(-y+2/3, x-y+1/3, z+1/3)',
3579 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3580 '(-x+y+2/3, -x+1/3, z+1/3)',
3581 '(-x+y+1/3, -x+2/3, z+2/3)', '(-y, -x, z)',
3582 '(-y+2/3, -x+1/3, z+1/3)',
3583 '(-y+1/3, -x+2/3, z+2/3)', '(-x+y, y, z)',
3584 '(-x+y+2/3, y+1/3, z+1/3)',
3585 '(-x+y+1/3, y+2/3, z+2/3)', '(x, x-y, z)',
3586 '(x+2/3, x-y+1/3, z+1/3)',
3587 '(x+1/3, x-y+2/3, z+2/3)'))},
3588 '160:R': {'1a': (1, ('(x, x, x)', )),
3589 '3b': (5, ('(x, x, z)', '(z, x, x)', '(x, z, x)')),
3590 '6c': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)', '(z, y, x)',
3591 '(y, x, z)', '(x, z, y)'))},
3592 '161:H': {'6a': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3593 '(1/3, 2/3, z+2/3)', '(0, 0, z+1/2)',
3594 '(2/3, 1/3, z+5/6)', '(1/3, 2/3, z+1/6)')),
3595 '18b': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3596 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3597 '(-y+2/3, x-y+1/3, z+1/3)',
3598 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3599 '(-x+y+2/3, -x+1/3, z+1/3)',
3600 '(-x+y+1/3, -x+2/3, z+2/3)', '(-y, -x, z+1/2)',
3601 '(-y+2/3, -x+1/3, z+5/6)',
3602 '(-y+1/3, -x+2/3, z+1/6)', '(-x+y, y, z+1/2)',
3603 '(-x+y+2/3, y+1/3, z+5/6)',
3604 '(-x+y+1/3, y+2/3, z+1/6)', '(x, x-y, z+1/2)',
3605 '(x+2/3, x-y+1/3, z+5/6)',
3606 '(x+1/3, x-y+2/3, z+1/6)'))},
3607 '161:R': {'2a': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)')),
3608 '6b': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)',
3609 '(z+1/2, y+1/2, x+1/2)', '(y+1/2, x+1/2, z+1/2)',
3610 '(x+1/2, z+1/2, y+1/2)'))},
3611 '162': {'1a': (0, ('(0, 0, 0)', )),
3612 '1b': (0, ('(0, 0, 1/2)', )),
3613 '2c': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)')),
3614 '2d': (0, ('(1/3, 2/3, 1/2)', '(2/3, 1/3, 1/2)')),
3615 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
3616 '3f': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
3617 '3g': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3618 )),
3619 '4h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z)', '(2/3, 1/3, -z)',
3620 '(2/3, 1/3, z)')),
3621 '6i': (1, ('(x, -x, 0)', '(x, 2*x, 0)', '(-2*x, -x, 0)',
3622 '(-x, x, 0)', '(-x, -2*x, 0)', '(2*x, x, 0)')),
3623 '6j': (1, ('(x, -x, 1/2)', '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)',
3624 '(-x, x, 1/2)', '(-x, -2*x, 1/2)', '(2*x, x, 1/2)')),
3625 '6k': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
3626 '(0, -x, -z)', '(-x, 0, -z)', '(x, x, -z)')),
3627 '12l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3628 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)',
3629 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
3630 '(y, x, z)', '(x-y, -y, z)', '(-x, -x+y, z)'))},
3631 '163': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
3632 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
3633 '2c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)')),
3634 '2d': (0, ('(2/3, 1/3, 1/4)', '(1/3, 2/3, 3/4)')),
3635 '4e': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
3636 '(0, 0, z+1/2)')),
3637 '4f': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z+1/2)',
3638 '(2/3, 1/3, -z)', '(2/3, 1/3, z+1/2)')),
3639 '6g': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
3640 '(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 1/2)'
3641 )),
3642 '6h': (1, ('(x, -x, 1/4)', '(x, 2*x, 1/4)', '(-2*x, -x, 1/4)',
3643 '(-x, x, 3/4)', '(-x, -2*x, 3/4)', '(2*x, x, 3/4)')),
3644 '12i': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3645 '(-y, -x, -z+1/2)', '(-x+y, y, -z+1/2)',
3646 '(x, x-y, -z+1/2)', '(-x, -y, -z)', '(y, -x+y, -z)',
3647 '(x-y, x, -z)', '(y, x, z+1/2)', '(x-y, -y, z+1/2)',
3648 '(-x, -x+y, z+1/2)'))},
3649 '164': {'1a': (0, ('(0, 0, 0)', )),
3650 '1b': (0, ('(0, 0, 1/2)', )),
3651 '2c': (4, ('(0, 0, z)', '(0, 0, -z)')),
3652 '2d': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, -z)')),
3653 '3e': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
3654 '3f': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3655 )),
3656 '6g': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
3657 '(-x, 0, 0)', '(0, -x, 0)', '(x, x, 0)')),
3658 '6h': (1, ('(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)',
3659 '(-x, 0, 1/2)', '(0, -x, 1/2)', '(x, x, 1/2)')),
3660 '6i': (5, ('(x, -x, z)', '(x, 2*x, z)', '(-2*x, -x, z)',
3661 '(-x, x, -z)', '(2*x, x, -z)', '(-x, -2*x, -z)')),
3662 '12j': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3663 '(y, x, -z)', '(x-y, -y, -z)', '(-x, -x+y, -z)',
3664 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
3665 '(-y, -x, z)', '(-x+y, y, z)', '(x, x-y, z)'))},
3666 '165': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
3667 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
3668 '4c': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
3669 '(0, 0, z+1/2)')),
3670 '4d': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, -z+1/2)',
3671 '(2/3, 1/3, -z)', '(1/3, 2/3, z+1/2)')),
3672 '6e': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
3673 '(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 1/2)'
3674 )),
3675 '6f': (1, ('(x, 0, 1/4)', '(0, x, 1/4)', '(-x, -x, 1/4)',
3676 '(-x, 0, 3/4)', '(0, -x, 3/4)', '(x, x, 3/4)')),
3677 '12g': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3678 '(y, x, -z+1/2)', '(x-y, -y, -z+1/2)',
3679 '(-x, -x+y, -z+1/2)', '(-x, -y, -z)',
3680 '(y, -x+y, -z)', '(x-y, x, -z)', '(-y, -x, z+1/2)',
3681 '(-x+y, y, z+1/2)', '(x, x-y, z+1/2)'))},
3682 '166:H': {'3a': (0, ('(0, 0, 0)', '(2/3, 1/3, 1/3)', '(1/3, 2/3, 2/3)'
3683 )),
3684 '3b': (0, ('(0, 0, 1/2)', '(2/3, 1/3, 5/6)',
3685 '(1/3, 2/3, 1/6)')),
3686 '6c': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3687 '(1/3, 2/3, z+2/3)', '(0, 0, -z)',
3688 '(2/3, 1/3, -z+1/3)', '(1/3, 2/3, -z+2/3)')),
3689 '9d': (0, ('(1/2, 0, 1/2)', '(1/6, 1/3, 5/6)',
3690 '(5/6, 2/3, 1/6)', '(0, 1/2, 1/2)',
3691 '(2/3, 5/6, 5/6)', '(1/3, 1/6, 1/6)',
3692 '(1/2, 1/2, 1/2)', '(1/6, 5/6, 5/6)',
3693 '(5/6, 1/6, 1/6)')),
3694 '9e': (0, ('(1/2, 0, 0)', '(1/6, 1/3, 1/3)',
3695 '(5/6, 2/3, 2/3)', '(0, 1/2, 0)',
3696 '(2/3, 5/6, 1/3)', '(1/3, 1/6, 2/3)',
3697 '(1/2, 1/2, 0)', '(1/6, 5/6, 1/3)',
3698 '(5/6, 1/6, 2/3)')),
3699 '18f': (1, ('(x, 0, 0)', '(x+2/3, 1/3, 1/3)',
3700 '(x+1/3, 2/3, 2/3)', '(0, x, 0)',
3701 '(2/3, x+1/3, 1/3)', '(1/3, x+2/3, 2/3)',
3702 '(-x, -x, 0)', '(-x+2/3, -x+1/3, 1/3)',
3703 '(-x+1/3, -x+2/3, 2/3)', '(-x, 0, 0)',
3704 '(-x+2/3, 1/3, 1/3)', '(-x+1/3, 2/3, 2/3)',
3705 '(0, -x, 0)', '(2/3, -x+1/3, 1/3)',
3706 '(1/3, -x+2/3, 2/3)', '(x, x, 0)',
3707 '(x+2/3, x+1/3, 1/3)', '(x+1/3, x+2/3, 2/3)')),
3708 '18g': (1, ('(x, 0, 1/2)', '(x+2/3, 1/3, 5/6)',
3709 '(x+1/3, 2/3, 1/6)', '(0, x, 1/2)',
3710 '(2/3, x+1/3, 5/6)', '(1/3, x+2/3, 1/6)',
3711 '(-x, -x, 1/2)', '(-x+2/3, -x+1/3, 5/6)',
3712 '(-x+1/3, -x+2/3, 1/6)', '(-x, 0, 1/2)',
3713 '(-x+2/3, 1/3, 5/6)', '(-x+1/3, 2/3, 1/6)',
3714 '(0, -x, 1/2)', '(2/3, -x+1/3, 5/6)',
3715 '(1/3, -x+2/3, 1/6)', '(x, x, 1/2)',
3716 '(x+2/3, x+1/3, 5/6)', '(x+1/3, x+2/3, 1/6)')),
3717 '18h': (5, ('(x, -x, z)', '(x+2/3, -x+1/3, z+1/3)',
3718 '(x+1/3, -x+2/3, z+2/3)', '(x, 2*x, z)',
3719 '(x+2/3, 2*x+1/3, z+1/3)',
3720 '(x+1/3, 2*x+2/3, z+2/3)', '(-2*x, -x, z)',
3721 '(-2*x+2/3, -x+1/3, z+1/3)',
3722 '(-2*x+1/3, -x+2/3, z+2/3)', '(-x, x, -z)',
3723 '(-x+2/3, x+1/3, -z+1/3)',
3724 '(-x+1/3, x+2/3, -z+2/3)', '(2*x, x, -z)',
3725 '(2*x+2/3, x+1/3, -z+1/3)',
3726 '(2*x+1/3, x+2/3, -z+2/3)', '(-x, -2*x, -z)',
3727 '(-x+2/3, -2*x+1/3, -z+1/3)',
3728 '(-x+1/3, -2*x+2/3, -z+2/3)')),
3729 '36i': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3730 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3731 '(-y+2/3, x-y+1/3, z+1/3)',
3732 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3733 '(-x+y+2/3, -x+1/3, z+1/3)',
3734 '(-x+y+1/3, -x+2/3, z+2/3)', '(y, x, -z)',
3735 '(y+2/3, x+1/3, -z+1/3)',
3736 '(y+1/3, x+2/3, -z+2/3)', '(x-y, -y, -z)',
3737 '(x-y+2/3, -y+1/3, -z+1/3)',
3738 '(x-y+1/3, -y+2/3, -z+2/3)', '(-x, -x+y, -z)',
3739 '(-x+2/3, -x+y+1/3, -z+1/3)',
3740 '(-x+1/3, -x+y+2/3, -z+2/3)', '(-x, -y, -z)',
3741 '(-x+2/3, -y+1/3, -z+1/3)',
3742 '(-x+1/3, -y+2/3, -z+2/3)', '(y, -x+y, -z)',
3743 '(y+2/3, -x+y+1/3, -z+1/3)',
3744 '(y+1/3, -x+y+2/3, -z+2/3)', '(x-y, x, -z)',
3745 '(x-y+2/3, x+1/3, -z+1/3)',
3746 '(x-y+1/3, x+2/3, -z+2/3)', '(-y, -x, z)',
3747 '(-y+2/3, -x+1/3, z+1/3)',
3748 '(-y+1/3, -x+2/3, z+2/3)', '(-x+y, y, z)',
3749 '(-x+y+2/3, y+1/3, z+1/3)',
3750 '(-x+y+1/3, y+2/3, z+2/3)', '(x, x-y, z)',
3751 '(x+2/3, x-y+1/3, z+1/3)',
3752 '(x+1/3, x-y+2/3, z+2/3)'))},
3753 '166:R': {'1a': (0, ('(0, 0, 0)', )),
3754 '1b': (0, ('(1/2, 1/2, 1/2)', )),
3755 '2c': (1, ('(x, x, x)', '(-x, -x, -x)')),
3756 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
3757 '3e': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)'
3758 )),
3759 '6f': (1, ('(x, -x, 0)', '(0, x, -x)', '(-x, 0, x)',
3760 '(-x, x, 0)', '(0, -x, x)', '(x, 0, -x)')),
3761 '6g': (1, ('(x, -x, 1/2)', '(1/2, x, -x)', '(-x, 1/2, x)',
3762 '(-x, x, 1/2)', '(1/2, -x, x)', '(x, 1/2, -x)')),
3763 '6h': (5, ('(x, x, z)', '(z, x, x)', '(x, z, x)',
3764 '(-z, -x, -x)', '(-x, -x, -z)', '(-x, -z, -x)')),
3765 '12i': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)',
3766 '(-z, -y, -x)', '(-y, -x, -z)', '(-x, -z, -y)',
3767 '(-x, -y, -z)', '(-z, -x, -y)', '(-y, -z, -x)',
3768 '(z, y, x)', '(y, x, z)', '(x, z, y)'))},
3769 '167:H': {'6a': (0, ('(0, 0, 1/4)', '(2/3, 1/3, 7/12)',
3770 '(1/3, 2/3, 11/12)', '(0, 0, 3/4)',
3771 '(2/3, 1/3, 1/12)', '(1/3, 2/3, 5/12)')),
3772 '6b': (0, ('(0, 0, 0)', '(2/3, 1/3, 1/3)', '(1/3, 2/3, 2/3)',
3773 '(0, 0, 1/2)', '(2/3, 1/3, 5/6)',
3774 '(1/3, 2/3, 1/6)')),
3775 '12c': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3776 '(1/3, 2/3, z+2/3)', '(0, 0, -z+1/2)',
3777 '(2/3, 1/3, -z+5/6)', '(1/3, 2/3, -z+1/6)',
3778 '(0, 0, -z)', '(2/3, 1/3, -z+1/3)',
3779 '(1/3, 2/3, -z+2/3)', '(0, 0, z+1/2)',
3780 '(2/3, 1/3, z+5/6)', '(1/3, 2/3, z+1/6)')),
3781 '18d': (0, ('(1/2, 0, 0)', '(1/6, 1/3, 1/3)',
3782 '(5/6, 2/3, 2/3)', '(0, 1/2, 0)',
3783 '(2/3, 5/6, 1/3)', '(1/3, 1/6, 2/3)',
3784 '(1/2, 1/2, 0)', '(1/6, 5/6, 1/3)',
3785 '(5/6, 1/6, 2/3)', '(0, 1/2, 1/2)',
3786 '(2/3, 5/6, 5/6)', '(1/3, 1/6, 1/6)',
3787 '(1/2, 0, 1/2)', '(1/6, 1/3, 5/6)',
3788 '(5/6, 2/3, 1/6)', '(1/2, 1/2, 1/2)',
3789 '(1/6, 5/6, 5/6)', '(5/6, 1/6, 1/6)')),
3790 '18e': (1, ('(x, 0, 1/4)', '(x+2/3, 1/3, 7/12)',
3791 '(x+1/3, 2/3, 11/12)', '(0, x, 1/4)',
3792 '(2/3, x+1/3, 7/12)', '(1/3, x+2/3, 11/12)',
3793 '(-x, -x, 1/4)', '(-x+2/3, -x+1/3, 7/12)',
3794 '(-x+1/3, -x+2/3, 11/12)', '(-x, 0, 3/4)',
3795 '(-x+2/3, 1/3, 1/12)', '(-x+1/3, 2/3, 5/12)',
3796 '(0, -x, 3/4)', '(2/3, -x+1/3, 1/12)',
3797 '(1/3, -x+2/3, 5/12)', '(x, x, 3/4)',
3798 '(x+2/3, x+1/3, 1/12)', '(x+1/3, x+2/3, 5/12)')),
3799 '36f': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3800 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3801 '(-y+2/3, x-y+1/3, z+1/3)',
3802 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3803 '(-x+y+2/3, -x+1/3, z+1/3)',
3804 '(-x+y+1/3, -x+2/3, z+2/3)', '(y, x, -z+1/2)',
3805 '(y+2/3, x+1/3, -z+5/6)',
3806 '(y+1/3, x+2/3, -z+1/6)', '(x-y, -y, -z+1/2)',
3807 '(x-y+2/3, -y+1/3, -z+5/6)',
3808 '(x-y+1/3, -y+2/3, -z+1/6)', '(-x, -x+y, -z+1/2)',
3809 '(-x+2/3, -x+y+1/3, -z+5/6)',
3810 '(-x+1/3, -x+y+2/3, -z+1/6)', '(-x, -y, -z)',
3811 '(-x+2/3, -y+1/3, -z+1/3)',
3812 '(-x+1/3, -y+2/3, -z+2/3)', '(y, -x+y, -z)',
3813 '(y+2/3, -x+y+1/3, -z+1/3)',
3814 '(y+1/3, -x+y+2/3, -z+2/3)', '(x-y, x, -z)',
3815 '(x-y+2/3, x+1/3, -z+1/3)',
3816 '(x-y+1/3, x+2/3, -z+2/3)', '(-y, -x, z+1/2)',
3817 '(-y+2/3, -x+1/3, z+5/6)',
3818 '(-y+1/3, -x+2/3, z+1/6)', '(-x+y, y, z+1/2)',
3819 '(-x+y+2/3, y+1/3, z+5/6)',
3820 '(-x+y+1/3, y+2/3, z+1/6)', '(x, x-y, z+1/2)',
3821 '(x+2/3, x-y+1/3, z+5/6)',
3822 '(x+1/3, x-y+2/3, z+1/6)'))},
3823 '167:R': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
3824 '2b': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
3825 '4c': (1, ('(x, x, x)', '(-x+1/2, -x+1/2, -x+1/2)',
3826 '(-x, -x, -x)', '(x+1/2, x+1/2, x+1/2)')),
3827 '6d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)',
3828 '(1/2, 1/2, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)'
3829 )),
3830 '6e': (1, ('(x, -x+1/2, 1/4)', '(1/4, x, -x+1/2)',
3831 '(-x+1/2, 1/4, x)', '(-x, x+1/2, 3/4)',
3832 '(3/4, -x, x+1/2)', '(x+1/2, 3/4, -x)')),
3833 '12f': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)',
3834 '(-z+1/2, -y+1/2, -x+1/2)',
3835 '(-y+1/2, -x+1/2, -z+1/2)',
3836 '(-x+1/2, -z+1/2, -y+1/2)', '(-x, -y, -z)',
3837 '(-z, -x, -y)', '(-y, -z, -x)',
3838 '(z+1/2, y+1/2, x+1/2)', '(y+1/2, x+1/2, z+1/2)',
3839 '(x+1/2, z+1/2, y+1/2)'))},
3840 '168': {'1a': (4, ('(0, 0, z)', )),
3841 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)')),
3842 '3c': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)')),
3843 '6d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3844 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)'))},
3845 '169': {'6a': (7, ('(x, y, z)', '(-y, x-y, z+1/3)', '(-x+y, -x, z+2/3)',
3846 '(-x, -y, z+1/2)', '(y, -x+y, z+5/6)',
3847 '(x-y, x, z+1/6)'))},
3848 '170': {'6a': (7, ('(x, y, z)', '(-y, x-y, z+2/3)', '(-x+y, -x, z+1/3)',
3849 '(-x, -y, z+1/2)', '(y, -x+y, z+1/6)',
3850 '(x-y, x, z+5/6)'))},
3851 '171': {'3a': (4, ('(0, 0, z)', '(0, 0, z+2/3)', '(0, 0, z+1/3)')),
3852 '3b': (4, ('(1/2, 1/2, z)', '(1/2, 0, z+2/3)',
3853 '(0, 1/2, z+1/3)')),
3854 '6c': (7, ('(x, y, z)', '(-y, x-y, z+2/3)', '(-x+y, -x, z+1/3)',
3855 '(-x, -y, z)', '(y, -x+y, z+2/3)', '(x-y, x, z+1/3)'
3856 ))},
3857 '172': {'3a': (4, ('(0, 0, z)', '(0, 0, z+1/3)', '(0, 0, z+2/3)')),
3858 '3b': (4, ('(1/2, 1/2, z)', '(1/2, 0, z+1/3)',
3859 '(0, 1/2, z+2/3)')),
3860 '6c': (7, ('(x, y, z)', '(-y, x-y, z+1/3)', '(-x+y, -x, z+2/3)',
3861 '(-x, -y, z)', '(y, -x+y, z+1/3)', '(x-y, x, z+2/3)'
3862 ))},
3863 '173': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
3864 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)')),
3865 '6c': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3866 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
3867 '(x-y, x, z+1/2)'))},
3868 '174': {'1a': (0, ('(0, 0, 0)', )),
3869 '1b': (0, ('(0, 0, 1/2)', )),
3870 '1c': (0, ('(1/3, 2/3, 0)', )),
3871 '1d': (0, ('(1/3, 2/3, 1/2)', )),
3872 '1e': (0, ('(2/3, 1/3, 0)', )),
3873 '1f': (0, ('(2/3, 1/3, 1/2)', )),
3874 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
3875 '2h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z)')),
3876 '2i': (4, ('(2/3, 1/3, z)', '(2/3, 1/3, -z)')),
3877 '3j': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)')),
3878 '3k': (3, ('(x, y, 1/2)', '(-y, x-y, 1/2)', '(-x+y, -x, 1/2)')),
3879 '6l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3880 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)'))},
3881 '175': {'1a': (0, ('(0, 0, 0)', )),
3882 '1b': (0, ('(0, 0, 1/2)', )),
3883 '2c': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)')),
3884 '2d': (0, ('(1/3, 2/3, 1/2)', '(2/3, 1/3, 1/2)')),
3885 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
3886 '3f': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
3887 '3g': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3888 )),
3889 '4h': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)', '(2/3, 1/3, -z)',
3890 '(1/3, 2/3, -z)')),
3891 '6i': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)',
3892 '(1/2, 0, -z)', '(0, 1/2, -z)', '(1/2, 1/2, -z)')),
3893 '6j': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)',
3894 '(-x, -y, 0)', '(y, -x+y, 0)', '(x-y, x, 0)')),
3895 '6k': (3, ('(x, y, 1/2)', '(-y, x-y, 1/2)', '(-x+y, -x, 1/2)',
3896 '(-x, -y, 1/2)', '(y, -x+y, 1/2)', '(x-y, x, 1/2)')),
3897 '12l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3898 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
3899 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
3900 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)'))},
3901 '176': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
3902 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
3903 '2c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)')),
3904 '2d': (0, ('(2/3, 1/3, 1/4)', '(1/3, 2/3, 3/4)')),
3905 '4e': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
3906 '(0, 0, -z+1/2)')),
3907 '4f': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)',
3908 '(2/3, 1/3, -z)', '(1/3, 2/3, -z+1/2)')),
3909 '6g': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
3910 '(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3911 )),
3912 '6h': (3, ('(x, y, 1/4)', '(-y, x-y, 1/4)', '(-x+y, -x, 1/4)',
3913 '(-x, -y, 3/4)', '(y, -x+y, 3/4)', '(x-y, x, 3/4)')),
3914 '12i': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3915 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
3916 '(x-y, x, z+1/2)', '(-x, -y, -z)', '(y, -x+y, -z)',
3917 '(x-y, x, -z)', '(x, y, -z+1/2)',
3918 '(-y, x-y, -z+1/2)', '(-x+y, -x, -z+1/2)'))},
3919 '177': {'1a': (0, ('(0, 0, 0)', )),
3920 '1b': (0, ('(0, 0, 1/2)', )),
3921 '2c': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)')),
3922 '2d': (0, ('(1/3, 2/3, 1/2)', '(2/3, 1/3, 1/2)')),
3923 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
3924 '3f': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
3925 '3g': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3926 )),
3927 '4h': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)', '(2/3, 1/3, -z)',
3928 '(1/3, 2/3, -z)')),
3929 '6i': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)',
3930 '(0, 1/2, -z)', '(1/2, 0, -z)', '(1/2, 1/2, -z)')),
3931 '6j': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
3932 '(-x, 0, 0)', '(0, -x, 0)', '(x, x, 0)')),
3933 '6k': (1, ('(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)',
3934 '(-x, 0, 1/2)', '(0, -x, 1/2)', '(x, x, 1/2)')),
3935 '6l': (1, ('(x, -x, 0)', '(x, 2*x, 0)', '(-2*x, -x, 0)',
3936 '(-x, x, 0)', '(-x, -2*x, 0)', '(2*x, x, 0)')),
3937 '6m': (1, ('(x, -x, 1/2)', '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)',
3938 '(-x, x, 1/2)', '(-x, -2*x, 1/2)', '(2*x, x, 1/2)')),
3939 '12n': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3940 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
3941 '(y, x, -z)', '(x-y, -y, -z)', '(-x, -x+y, -z)',
3942 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)'))},
3943 '178': {'6a': (1, ('(x, 0, 0)', '(0, x, 1/3)', '(-x, -x, 2/3)',
3944 '(-x, 0, 1/2)', '(0, -x, 5/6)', '(x, x, 1/6)')),
3945 '6b': (1, ('(x, 2*x, 1/4)', '(-2*x, -x, 7/12)',
3946 '(x, -x, 11/12)', '(-x, -2*x, 3/4)',
3947 '(2*x, x, 1/12)', '(-x, x, 5/12)')),
3948 '12c': (7, ('(x, y, z)', '(-y, x-y, z+1/3)',
3949 '(-x+y, -x, z+2/3)', '(-x, -y, z+1/2)',
3950 '(y, -x+y, z+5/6)', '(x-y, x, z+1/6)',
3951 '(y, x, -z+1/3)', '(x-y, -y, -z)',
3952 '(-x, -x+y, -z+2/3)', '(-y, -x, -z+5/6)',
3953 '(-x+y, y, -z+1/2)', '(x, x-y, -z+1/6)'))},
3954 '179': {'6a': (1, ('(x, 0, 0)', '(0, x, 2/3)', '(-x, -x, 1/3)',
3955 '(-x, 0, 1/2)', '(0, -x, 1/6)', '(x, x, 5/6)')),
3956 '6b': (1, ('(x, 2*x, 3/4)', '(-2*x, -x, 5/12)', '(x, -x, 1/12)',
3957 '(-x, -2*x, 1/4)', '(2*x, x, 11/12)',
3958 '(-x, x, 7/12)')),
3959 '12c': (7, ('(x, y, z)', '(-y, x-y, z+2/3)',
3960 '(-x+y, -x, z+1/3)', '(-x, -y, z+1/2)',
3961 '(y, -x+y, z+1/6)', '(x-y, x, z+5/6)',
3962 '(y, x, -z+2/3)', '(x-y, -y, -z)',
3963 '(-x, -x+y, -z+1/3)', '(-y, -x, -z+1/6)',
3964 '(-x+y, y, -z+1/2)', '(x, x-y, -z+5/6)'))},
3965 '180': {'3a': (0, ('(0, 0, 0)', '(0, 0, 2/3)', '(0, 0, 1/3)')),
3966 '3b': (0, ('(0, 0, 1/2)', '(0, 0, 1/6)', '(0, 0, 5/6)')),
3967 '3c': (0, ('(1/2, 0, 0)', '(0, 1/2, 2/3)', '(1/2, 1/2, 1/3)')),
3968 '3d': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/6)', '(1/2, 1/2, 5/6)'
3969 )),
3970 '6e': (4, ('(0, 0, z)', '(0, 0, z+2/3)', '(0, 0, z+1/3)',
3971 '(0, 0, -z+2/3)', '(0, 0, -z)', '(0, 0, -z+1/3)')),
3972 '6f': (4, ('(1/2, 0, z)', '(0, 1/2, z+2/3)',
3973 '(1/2, 1/2, z+1/3)', '(0, 1/2, -z+2/3)',
3974 '(1/2, 0, -z)', '(1/2, 1/2, -z+1/3)')),
3975 '6g': (1, ('(x, 0, 0)', '(0, x, 2/3)', '(-x, -x, 1/3)',
3976 '(-x, 0, 0)', '(0, -x, 2/3)', '(x, x, 1/3)')),
3977 '6h': (1, ('(x, 0, 1/2)', '(0, x, 1/6)', '(-x, -x, 5/6)',
3978 '(-x, 0, 1/2)', '(0, -x, 1/6)', '(x, x, 5/6)')),
3979 '6i': (1, ('(x, 2*x, 0)', '(-2*x, -x, 2/3)', '(x, -x, 1/3)',
3980 '(-x, -2*x, 0)', '(2*x, x, 2/3)', '(-x, x, 1/3)')),
3981 '6j': (1, ('(x, 2*x, 1/2)', '(-2*x, -x, 1/6)', '(x, -x, 5/6)',
3982 '(-x, -2*x, 1/2)', '(2*x, x, 1/6)', '(-x, x, 5/6)')),
3983 '12k': (7, ('(x, y, z)', '(-y, x-y, z+2/3)',
3984 '(-x+y, -x, z+1/3)', '(-x, -y, z)',
3985 '(y, -x+y, z+2/3)', '(x-y, x, z+1/3)',
3986 '(y, x, -z+2/3)', '(x-y, -y, -z)',
3987 '(-x, -x+y, -z+1/3)', '(-y, -x, -z+2/3)',
3988 '(-x+y, y, -z)', '(x, x-y, -z+1/3)'))},
3989 '181': {'3a': (0, ('(0, 0, 0)', '(0, 0, 1/3)', '(0, 0, 2/3)')),
3990 '3b': (0, ('(0, 0, 1/2)', '(0, 0, 5/6)', '(0, 0, 1/6)')),
3991 '3c': (0, ('(1/2, 0, 0)', '(0, 1/2, 1/3)', '(1/2, 1/2, 2/3)')),
3992 '3d': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 5/6)', '(1/2, 1/2, 1/6)'
3993 )),
3994 '6e': (4, ('(0, 0, z)', '(0, 0, z+1/3)', '(0, 0, z+2/3)',
3995 '(0, 0, -z+1/3)', '(0, 0, -z)', '(0, 0, -z+2/3)')),
3996 '6f': (4, ('(1/2, 0, z)', '(0, 1/2, z+1/3)',
3997 '(1/2, 1/2, z+2/3)', '(0, 1/2, -z+1/3)',
3998 '(1/2, 0, -z)', '(1/2, 1/2, -z+2/3)')),
3999 '6g': (1, ('(x, 0, 0)', '(0, x, 1/3)', '(-x, -x, 2/3)',
4000 '(-x, 0, 0)', '(0, -x, 1/3)', '(x, x, 2/3)')),
4001 '6h': (1, ('(x, 0, 1/2)', '(0, x, 5/6)', '(-x, -x, 1/6)',
4002 '(-x, 0, 1/2)', '(0, -x, 5/6)', '(x, x, 1/6)')),
4003 '6i': (1, ('(x, 2*x, 0)', '(-2*x, -x, 1/3)', '(x, -x, 2/3)',
4004 '(-x, -2*x, 0)', '(2*x, x, 1/3)', '(-x, x, 2/3)')),
4005 '6j': (1, ('(x, 2*x, 1/2)', '(-2*x, -x, 5/6)', '(x, -x, 1/6)',
4006 '(-x, -2*x, 1/2)', '(2*x, x, 5/6)', '(-x, x, 1/6)')),
4007 '12k': (7, ('(x, y, z)', '(-y, x-y, z+1/3)',
4008 '(-x+y, -x, z+2/3)', '(-x, -y, z)',
4009 '(y, -x+y, z+1/3)', '(x-y, x, z+2/3)',
4010 '(y, x, -z+1/3)', '(x-y, -y, -z)',
4011 '(-x, -x+y, -z+2/3)', '(-y, -x, -z+1/3)',
4012 '(-x+y, y, -z)', '(x, x-y, -z+2/3)'))},
4013 '182': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4014 '2b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4015 '2c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)')),
4016 '2d': (0, ('(1/3, 2/3, 3/4)', '(2/3, 1/3, 1/4)')),
4017 '4e': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
4018 '(0, 0, -z+1/2)')),
4019 '4f': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)',
4020 '(2/3, 1/3, -z)', '(1/3, 2/3, -z+1/2)')),
4021 '6g': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
4022 '(-x, 0, 1/2)', '(0, -x, 1/2)', '(x, x, 1/2)')),
4023 '6h': (1, ('(x, 2*x, 1/4)', '(-2*x, -x, 1/4)', '(x, -x, 1/4)',
4024 '(-x, -2*x, 3/4)', '(2*x, x, 3/4)', '(-x, x, 3/4)')),
4025 '12i': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4026 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
4027 '(x-y, x, z+1/2)', '(y, x, -z)', '(x-y, -y, -z)',
4028 '(-x, -x+y, -z)', '(-y, -x, -z+1/2)',
4029 '(-x+y, y, -z+1/2)', '(x, x-y, -z+1/2)'))},
4030 '183': {'1a': (4, ('(0, 0, z)', )),
4031 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)')),
4032 '3c': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)')),
4033 '6d': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
4034 '(-x, 0, z)', '(0, -x, z)', '(x, x, z)')),
4035 '6e': (5, ('(x, -x, z)', '(x, 2*x, z)', '(-2*x, -x, z)',
4036 '(-x, x, z)', '(-x, -2*x, z)', '(2*x, x, z)')),
4037 '12f': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4038 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
4039 '(-y, -x, z)', '(-x+y, y, z)', '(x, x-y, z)',
4040 '(y, x, z)', '(x-y, -y, z)', '(-x, -x+y, z)'))},
4041 '184': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
4042 '4b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)',
4043 '(1/3, 2/3, z+1/2)', '(2/3, 1/3, z+1/2)')),
4044 '6c': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)',
4045 '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
4046 '(1/2, 1/2, z+1/2)')),
4047 '12d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4048 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
4049 '(-y, -x, z+1/2)', '(-x+y, y, z+1/2)',
4050 '(x, x-y, z+1/2)', '(y, x, z+1/2)',
4051 '(x-y, -y, z+1/2)', '(-x, -x+y, z+1/2)'))},
4052 '185': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
4053 '4b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)',
4054 '(1/3, 2/3, z+1/2)', '(2/3, 1/3, z)')),
4055 '6c': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
4056 '(-x, 0, z+1/2)', '(0, -x, z+1/2)', '(x, x, z+1/2)'
4057 )),
4058 '12d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4059 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
4060 '(x-y, x, z+1/2)', '(-y, -x, z+1/2)',
4061 '(-x+y, y, z+1/2)', '(x, x-y, z+1/2)', '(y, x, z)',
4062 '(x-y, -y, z)', '(-x, -x+y, z)'))},
4063 '186': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
4064 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)')),
4065 '6c': (5, ('(x, -x, z)', '(x, 2*x, z)', '(-2*x, -x, z)',
4066 '(-x, x, z+1/2)', '(-x, -2*x, z+1/2)',
4067 '(2*x, x, z+1/2)')),
4068 '12d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4069 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
4070 '(x-y, x, z+1/2)', '(-y, -x, z)', '(-x+y, y, z)',
4071 '(x, x-y, z)', '(y, x, z+1/2)', '(x-y, -y, z+1/2)',
4072 '(-x, -x+y, z+1/2)'))},
4073 '187': {'1a': (0, ('(0, 0, 0)', )),
4074 '1b': (0, ('(0, 0, 1/2)', )),
4075 '1c': (0, ('(1/3, 2/3, 0)', )),
4076 '1d': (0, ('(1/3, 2/3, 1/2)', )),
4077 '1e': (0, ('(2/3, 1/3, 0)', )),
4078 '1f': (0, ('(2/3, 1/3, 1/2)', )),
4079 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
4080 '2h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z)')),
4081 '2i': (4, ('(2/3, 1/3, z)', '(2/3, 1/3, -z)')),
4082 '3j': (1, ('(x, -x, 0)', '(x, 2*x, 0)', '(-2*x, -x, 0)')),
4083 '3k': (1, ('(x, -x, 1/2)', '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)')),
4084 '6l': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)',
4085 '(-y, -x, 0)', '(-x+y, y, 0)', '(x, x-y, 0)')),
4086 '6m': (3, ('(x, y, 1/2)', '(-y, x-y, 1/2)', '(-x+y, -x, 1/2)',
4087 '(-y, -x, 1/2)', '(-x+y, y, 1/2)', '(x, x-y, 1/2)')),
4088 '6n': (5, ('(x, -x, z)', '(x, 2*x, z)', '(-2*x, -x, z)',
4089 '(x, -x, -z)', '(x, 2*x, -z)', '(-2*x, -x, -z)')),
4090 '12o': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4091 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)',
4092 '(-y, -x, z)', '(-x+y, y, z)', '(x, x-y, z)',
4093 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)'))},
4094 '188': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4095 '2b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4096 '2c': (0, ('(1/3, 2/3, 0)', '(1/3, 2/3, 1/2)')),
4097 '2d': (0, ('(1/3, 2/3, 1/4)', '(1/3, 2/3, 3/4)')),
4098 '2e': (0, ('(2/3, 1/3, 0)', '(2/3, 1/3, 1/2)')),
4099 '2f': (0, ('(2/3, 1/3, 1/4)', '(2/3, 1/3, 3/4)')),
4100 '4g': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, z+1/2)',
4101 '(0, 0, -z)')),
4102 '4h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z+1/2)',
4103 '(1/3, 2/3, z+1/2)', '(1/3, 2/3, -z)')),
4104 '4i': (4, ('(2/3, 1/3, z)', '(2/3, 1/3, -z+1/2)',
4105 '(2/3, 1/3, z+1/2)', '(2/3, 1/3, -z)')),
4106 '6j': (1, ('(x, -x, 0)', '(x, 2*x, 0)', '(-2*x, -x, 0)',
4107 '(x, -x, 1/2)', '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)')),
4108 '6k': (3, ('(x, y, 1/4)', '(-y, x-y, 1/4)', '(-x+y, -x, 1/4)',
4109 '(-y, -x, 3/4)', '(-x+y, y, 3/4)', '(x, x-y, 3/4)')),
4110 '12l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4111 '(x, y, -z+1/2)', '(-y, x-y, -z+1/2)',
4112 '(-x+y, -x, -z+1/2)', '(-y, -x, z+1/2)',
4113 '(-x+y, y, z+1/2)', '(x, x-y, z+1/2)',
4114 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)'))},
4115 '189': {'1a': (0, ('(0, 0, 0)', )),
4116 '1b': (0, ('(0, 0, 1/2)', )),
4117 '2c': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)')),
4118 '2d': (0, ('(1/3, 2/3, 1/2)', '(2/3, 1/3, 1/2)')),
4119 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
4120 '3f': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)')),
4121 '3g': (1, ('(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)')),
4122 '4h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z)', '(2/3, 1/3, -z)',
4123 '(2/3, 1/3, z)')),
4124 '6i': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
4125 '(x, 0, -z)', '(0, x, -z)', '(-x, -x, -z)')),
4126 '6j': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)',
4127 '(y, x, 0)', '(x-y, -y, 0)', '(-x, -x+y, 0)')),
4128 '6k': (3, ('(x, y, 1/2)', '(-y, x-y, 1/2)', '(-x+y, -x, 1/2)',
4129 '(y, x, 1/2)', '(x-y, -y, 1/2)', '(-x, -x+y, 1/2)')),
4130 '12l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4131 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)',
4132 '(y, x, -z)', '(x-y, -y, -z)', '(-x, -x+y, -z)',
4133 '(y, x, z)', '(x-y, -y, z)', '(-x, -x+y, z)'))},
4134 '190': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4135 '2b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4136 '2c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)')),
4137 '2d': (0, ('(2/3, 1/3, 1/4)', '(1/3, 2/3, 3/4)')),
4138 '4e': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
4139 '(0, 0, z+1/2)')),
4140 '4f': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z+1/2)',
4141 '(2/3, 1/3, -z)', '(2/3, 1/3, z+1/2)')),
4142 '6g': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
4143 '(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)')),
4144 '6h': (3, ('(x, y, 1/4)', '(-y, x-y, 1/4)', '(-x+y, -x, 1/4)',
4145 '(y, x, 3/4)', '(x-y, -y, 3/4)', '(-x, -x+y, 3/4)')),
4146 '12i': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4147 '(x, y, -z+1/2)', '(-y, x-y, -z+1/2)',
4148 '(-x+y, -x, -z+1/2)', '(y, x, -z)', '(x-y, -y, -z)',
4149 '(-x, -x+y, -z)', '(y, x, z+1/2)',
4150 '(x-y, -y, z+1/2)', '(-x, -x+y, z+1/2)'))},
4151 '191': {'1a': (0, ('(0, 0, 0)', )),
4152 '1b': (0, ('(0, 0, 1/2)', )),
4153 '2c': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)')),
4154 '2d': (0, ('(1/3, 2/3, 1/2)', '(2/3, 1/3, 1/2)')),
4155 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
4156 '3f': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
4157 '3g': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
4158 )),
4159 '4h': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)', '(2/3, 1/3, -z)',
4160 '(1/3, 2/3, -z)')),
4161 '6i': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)',
4162 '(0, 1/2, -z)', '(1/2, 0, -z)', '(1/2, 1/2, -z)')),
4163 '6j': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
4164 '(-x, 0, 0)', '(0, -x, 0)', '(x, x, 0)')),
4165 '6k': (1, ('(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)',
4166 '(-x, 0, 1/2)', '(0, -x, 1/2)', '(x, x, 1/2)')),
4167 '6l': (1, ('(x, 2*x, 0)', '(-2*x, -x, 0)', '(x, -x, 0)',
4168 '(-x, -2*x, 0)', '(2*x, x, 0)', '(-x, x, 0)')),
4169 '6m': (1, ('(x, 2*x, 1/2)', '(-2*x, -x, 1/2)', '(x, -x, 1/2)',
4170 '(-x, -2*x, 1/2)', '(2*x, x, 1/2)', '(-x, x, 1/2)')),
4171 '12n': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
4172 '(-x, 0, z)', '(0, -x, z)', '(x, x, z)',
4173 '(0, x, -z)', '(x, 0, -z)', '(-x, -x, -z)',
4174 '(0, -x, -z)', '(-x, 0, -z)', '(x, x, -z)')),
4175 '12o': (5, ('(x, 2*x, z)', '(-2*x, -x, z)', '(x, -x, z)',
4176 '(-x, -2*x, z)', '(2*x, x, z)', '(-x, x, z)',
4177 '(2*x, x, -z)', '(-x, -2*x, -z)', '(-x, x, -z)',
4178 '(-2*x, -x, -z)', '(x, 2*x, -z)', '(x, -x, -z)')),
4179 '12p': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)',
4180 '(-x, -y, 0)', '(y, -x+y, 0)', '(x-y, x, 0)',
4181 '(y, x, 0)', '(x-y, -y, 0)', '(-x, -x+y, 0)',
4182 '(-y, -x, 0)', '(-x+y, y, 0)', '(x, x-y, 0)')),
4183 '12q': (3, ('(x, y, 1/2)', '(-y, x-y, 1/2)', '(-x+y, -x, 1/2)',
4184 '(-x, -y, 1/2)', '(y, -x+y, 1/2)', '(x-y, x, 1/2)',
4185 '(y, x, 1/2)', '(x-y, -y, 1/2)', '(-x, -x+y, 1/2)',
4186 '(-y, -x, 1/2)', '(-x+y, y, 1/2)', '(x, x-y, 1/2)'
4187 )),
4188 '24r': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4189 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
4190 '(y, x, -z)', '(x-y, -y, -z)', '(-x, -x+y, -z)',
4191 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)',
4192 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
4193 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)',
4194 '(-y, -x, z)', '(-x+y, y, z)', '(x, x-y, z)',
4195 '(y, x, z)', '(x-y, -y, z)', '(-x, -x+y, z)'))},
4196 '192': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4197 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4198 '4c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 1/4)',
4199 '(2/3, 1/3, 3/4)', '(1/3, 2/3, 3/4)')),
4200 '4d': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)', '(2/3, 1/3, 1/2)',
4201 '(1/3, 2/3, 1/2)')),
4202 '4e': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
4203 '(0, 0, z+1/2)')),
4204 '6f': (0, ('(1/2, 0, 1/4)', '(0, 1/2, 1/4)', '(1/2, 1/2, 1/4)',
4205 '(1/2, 0, 3/4)', '(0, 1/2, 3/4)', '(1/2, 1/2, 3/4)'
4206 )),
4207 '6g': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
4208 '(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 1/2)'
4209 )),
4210 '8h': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)',
4211 '(2/3, 1/3, -z+1/2)', '(1/3, 2/3, -z+1/2)',
4212 '(2/3, 1/3, -z)', '(1/3, 2/3, -z)',
4213 '(1/3, 2/3, z+1/2)', '(2/3, 1/3, z+1/2)')),
4214 '12i': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)',
4215 '(0, 1/2, -z+1/2)', '(1/2, 0, -z+1/2)',
4216 '(1/2, 1/2, -z+1/2)', '(1/2, 0, -z)',
4217 '(0, 1/2, -z)', '(1/2, 1/2, -z)', '(0, 1/2, z+1/2)',
4218 '(1/2, 0, z+1/2)', '(1/2, 1/2, z+1/2)')),
4219 '12j': (1, ('(x, 0, 1/4)', '(0, x, 1/4)', '(-x, -x, 1/4)',
4220 '(-x, 0, 1/4)', '(0, -x, 1/4)', '(x, x, 1/4)',
4221 '(-x, 0, 3/4)', '(0, -x, 3/4)', '(x, x, 3/4)',
4222 '(x, 0, 3/4)', '(0, x, 3/4)', '(-x, -x, 3/4)')),
4223 '12k': (1, ('(x, 2*x, 1/4)', '(-2*x, -x, 1/4)', '(x, -x, 1/4)',
4224 '(-x, -2*x, 1/4)', '(2*x, x, 1/4)', '(-x, x, 1/4)',
4225 '(-x, -2*x, 3/4)', '(2*x, x, 3/4)', '(-x, x, 3/4)',
4226 '(x, 2*x, 3/4)', '(-2*x, -x, 3/4)', '(x, -x, 3/4)'
4227 )),
4228 '12l': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)',
4229 '(-x, -y, 0)', '(y, -x+y, 0)', '(x-y, x, 0)',
4230 '(y, x, 1/2)', '(x-y, -y, 1/2)', '(-x, -x+y, 1/2)',
4231 '(-y, -x, 1/2)', '(-x+y, y, 1/2)', '(x, x-y, 1/2)'
4232 )),
4233 '24m': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4234 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
4235 '(y, x, -z+1/2)', '(x-y, -y, -z+1/2)',
4236 '(-x, -x+y, -z+1/2)', '(-y, -x, -z+1/2)',
4237 '(-x+y, y, -z+1/2)', '(x, x-y, -z+1/2)',
4238 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
4239 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)',
4240 '(-y, -x, z+1/2)', '(-x+y, y, z+1/2)',
4241 '(x, x-y, z+1/2)', '(y, x, z+1/2)',
4242 '(x-y, -y, z+1/2)', '(-x, -x+y, z+1/2)'))},
4243 '193': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4244 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4245 '4c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)',
4246 '(2/3, 1/3, 1/4)', '(1/3, 2/3, 3/4)')),
4247 '4d': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 1/2)', '(2/3, 1/3, 0)',
4248 '(1/3, 2/3, 1/2)')),
4249 '4e': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z+1/2)',
4250 '(0, 0, -z)')),
4251 '6f': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
4252 '(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
4253 )),
4254 '6g': (1, ('(x, 0, 1/4)', '(0, x, 1/4)', '(-x, -x, 1/4)',
4255 '(-x, 0, 3/4)', '(0, -x, 3/4)', '(x, x, 3/4)')),
4256 '8h': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)',
4257 '(2/3, 1/3, -z+1/2)', '(1/3, 2/3, -z)',
4258 '(2/3, 1/3, -z)', '(1/3, 2/3, -z+1/2)',
4259 '(1/3, 2/3, z+1/2)', '(2/3, 1/3, z)')),
4260 '12i': (1, ('(x, 2*x, 0)', '(-2*x, -x, 0)', '(x, -x, 0)',
4261 '(-x, -2*x, 1/2)', '(2*x, x, 1/2)', '(-x, x, 1/2)',
4262 '(-x, -2*x, 0)', '(2*x, x, 0)', '(-x, x, 0)',
4263 '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)', '(x, -x, 1/2)'
4264 )),
4265 '12j': (3, ('(x, y, 1/4)', '(-y, x-y, 1/4)', '(-x+y, -x, 1/4)',
4266 '(-x, -y, 3/4)', '(y, -x+y, 3/4)', '(x-y, x, 3/4)',
4267 '(y, x, 1/4)', '(x-y, -y, 1/4)', '(-x, -x+y, 1/4)',
4268 '(-y, -x, 3/4)', '(-x+y, y, 3/4)', '(x, x-y, 3/4)'
4269 )),
4270 '12k': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
4271 '(-x, 0, z+1/2)', '(0, -x, z+1/2)', '(x, x, z+1/2)',
4272 '(0, x, -z+1/2)', '(x, 0, -z+1/2)',
4273 '(-x, -x, -z+1/2)', '(0, -x, -z)', '(-x, 0, -z)',
4274 '(x, x, -z)')),
4275 '24l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4276 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
4277 '(x-y, x, z+1/2)', '(y, x, -z+1/2)',
4278 '(x-y, -y, -z+1/2)', '(-x, -x+y, -z+1/2)',
4279 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)',
4280 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
4281 '(x, y, -z+1/2)', '(-y, x-y, -z+1/2)',
4282 '(-x+y, -x, -z+1/2)', '(-y, -x, z+1/2)',
4283 '(-x+y, y, z+1/2)', '(x, x-y, z+1/2)', '(y, x, z)',
4284 '(x-y, -y, z)', '(-x, -x+y, z)'))},
4285 '194': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4286 '2b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4287 '2c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)')),
4288 '2d': (0, ('(1/3, 2/3, 3/4)', '(2/3, 1/3, 1/4)')),
4289 '4e': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
4290 '(0, 0, -z+1/2)')),
4291 '4f': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)',
4292 '(2/3, 1/3, -z)', '(1/3, 2/3, -z+1/2)')),
4293 '6g': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
4294 '(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
4295 )),
4296 '6h': (1, ('(x, 2*x, 1/4)', '(-2*x, -x, 1/4)', '(x, -x, 1/4)',
4297 '(-x, -2*x, 3/4)', '(2*x, x, 3/4)', '(-x, x, 3/4)')),
4298 '12i': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
4299 '(-x, 0, 1/2)', '(0, -x, 1/2)', '(x, x, 1/2)',
4300 '(-x, 0, 0)', '(0, -x, 0)', '(x, x, 0)',
4301 '(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)')),
4302 '12j': (3, ('(x, y, 1/4)', '(-y, x-y, 1/4)', '(-x+y, -x, 1/4)',
4303 '(-x, -y, 3/4)', '(y, -x+y, 3/4)', '(x-y, x, 3/4)',
4304 '(y, x, 3/4)', '(x-y, -y, 3/4)', '(-x, -x+y, 3/4)',
4305 '(-y, -x, 1/4)', '(-x+y, y, 1/4)', '(x, x-y, 1/4)'
4306 )),
4307 '12k': (5, ('(x, 2*x, z)', '(-2*x, -x, z)', '(x, -x, z)',
4308 '(-x, -2*x, z+1/2)', '(2*x, x, z+1/2)',
4309 '(-x, x, z+1/2)', '(2*x, x, -z)', '(-x, -2*x, -z)',
4310 '(-x, x, -z)', '(-2*x, -x, -z+1/2)',
4311 '(x, 2*x, -z+1/2)', '(x, -x, -z+1/2)')),
4312 '24l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4313 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
4314 '(x-y, x, z+1/2)', '(y, x, -z)', '(x-y, -y, -z)',
4315 '(-x, -x+y, -z)', '(-y, -x, -z+1/2)',
4316 '(-x+y, y, -z+1/2)', '(x, x-y, -z+1/2)',
4317 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
4318 '(x, y, -z+1/2)', '(-y, x-y, -z+1/2)',
4319 '(-x+y, -x, -z+1/2)', '(-y, -x, z)', '(-x+y, y, z)',
4320 '(x, x-y, z)', '(y, x, z+1/2)', '(x-y, -y, z+1/2)',
4321 '(-x, -x+y, z+1/2)'))},
4322 '195': {'1a': (0, ('(0, 0, 0)', )),
4323 '1b': (0, ('(1/2, 1/2, 1/2)', )),
4324 '3c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
4325 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
4326 '4e': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
4327 '(x, -x, -x)')),
4328 '6f': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)',
4329 '(0, 0, x)', '(0, 0, -x)')),
4330 '6g': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
4331 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)')),
4332 '6h': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
4333 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)')),
4334 '6i': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
4335 '(1/2, -x, 1/2)', '(1/2, 1/2, x)', '(1/2, 1/2, -x)'
4336 )),
4337 '12j': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
4338 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
4339 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
4340 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)'))},
4341 '196': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
4342 '(1/2, 1/2, 0)')),
4343 '4b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
4344 '(0, 0, 1/2)')),
4345 '4c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
4346 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
4347 '4d': (0, ('(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
4348 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
4349 '16e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
4350 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
4351 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
4352 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
4353 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
4354 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
4355 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
4356 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)')),
4357 '24f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
4358 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
4359 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
4360 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
4361 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
4362 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
4363 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
4364 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
4365 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)')),
4366 '24g': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
4367 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
4368 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
4369 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
4370 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
4371 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
4372 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
4373 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
4374 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
4375 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
4376 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
4377 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)')),
4378 '48h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
4379 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
4380 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
4381 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
4382 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
4383 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
4384 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
4385 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
4386 '(z, x, y)', '(z, x+1/2, y+1/2)',
4387 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
4388 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
4389 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
4390 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
4391 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
4392 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
4393 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
4394 '(y, z, x)', '(y, z+1/2, x+1/2)',
4395 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
4396 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
4397 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
4398 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
4399 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
4400 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
4401 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)'))},
4402 '197': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
4403 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
4404 '(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
4405 '8c': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)', '(-x, -x, x)',
4406 '(-x+1/2, -x+1/2, x+1/2)', '(-x, x, -x)',
4407 '(-x+1/2, x+1/2, -x+1/2)', '(x, -x, -x)',
4408 '(x+1/2, -x+1/2, -x+1/2)')),
4409 '12d': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
4410 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
4411 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
4412 '(1/2, -x+1/2, 1/2)', '(0, 0, x)',
4413 '(1/2, 1/2, x+1/2)', '(0, 0, -x)',
4414 '(1/2, 1/2, -x+1/2)')),
4415 '12e': (1, ('(x, 1/2, 0)', '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
4416 '(-x+1/2, 0, 1/2)', '(0, x, 1/2)',
4417 '(1/2, x+1/2, 0)', '(0, -x, 1/2)',
4418 '(1/2, -x+1/2, 0)', '(1/2, 0, x)',
4419 '(0, 1/2, x+1/2)', '(1/2, 0, -x)',
4420 '(0, 1/2, -x+1/2)')),
4421 '24f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
4422 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
4423 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
4424 '(x+1/2, -y+1/2, -z+1/2)', '(z, x, y)',
4425 '(z+1/2, x+1/2, y+1/2)', '(z, -x, -y)',
4426 '(z+1/2, -x+1/2, -y+1/2)', '(-z, -x, y)',
4427 '(-z+1/2, -x+1/2, y+1/2)', '(-z, x, -y)',
4428 '(-z+1/2, x+1/2, -y+1/2)', '(y, z, x)',
4429 '(y+1/2, z+1/2, x+1/2)', '(-y, z, -x)',
4430 '(-y+1/2, z+1/2, -x+1/2)', '(y, -z, -x)',
4431 '(y+1/2, -z+1/2, -x+1/2)', '(-y, -z, x)',
4432 '(-y+1/2, -z+1/2, x+1/2)'))},
4433 '198': {'4a': (1, ('(x, x, x)', '(-x+1/2, -x, x+1/2)',
4434 '(-x, x+1/2, -x+1/2)', '(x+1/2, -x+1/2, -x)')),
4435 '12b': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
4436 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)',
4437 '(z, x, y)', '(z+1/2, -x+1/2, -y)',
4438 '(-z+1/2, -x, y+1/2)', '(-z, x+1/2, -y+1/2)',
4439 '(y, z, x)', '(-y, z+1/2, -x+1/2)',
4440 '(y+1/2, -z+1/2, -x)', '(-y+1/2, -z, x+1/2)'))},
4441 '199': {'8a': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)',
4442 '(-x+1/2, -x, x+1/2)', '(-x, -x+1/2, x)',
4443 '(-x, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
4444 '(x+1/2, -x+1/2, -x)', '(x, -x, -x+1/2)')),
4445 '12b': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
4446 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(1/4, x, 0)',
4447 '(3/4, x+1/2, 1/2)', '(3/4, -x+1/2, 0)',
4448 '(1/4, -x, 1/2)', '(0, 1/4, x)',
4449 '(1/2, 3/4, x+1/2)', '(0, 3/4, -x+1/2)',
4450 '(1/2, 1/4, -x)')),
4451 '24c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
4452 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
4453 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
4454 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
4455 '(z, x, y)', '(z+1/2, x+1/2, y+1/2)',
4456 '(z+1/2, -x+1/2, -y)', '(z, -x, -y+1/2)',
4457 '(-z+1/2, -x, y+1/2)', '(-z, -x+1/2, y)',
4458 '(-z, x+1/2, -y+1/2)', '(-z+1/2, x, -y)',
4459 '(y, z, x)', '(y+1/2, z+1/2, x+1/2)',
4460 '(-y, z+1/2, -x+1/2)', '(-y+1/2, z, -x)',
4461 '(y+1/2, -z+1/2, -x)', '(y, -z, -x+1/2)',
4462 '(-y+1/2, -z, x+1/2)', '(-y, -z+1/2, x)'))},
4463 '200': {'1a': (0, ('(0, 0, 0)', )),
4464 '1b': (0, ('(1/2, 1/2, 1/2)', )),
4465 '3c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
4466 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
4467 '6e': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)',
4468 '(0, 0, x)', '(0, 0, -x)')),
4469 '6f': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
4470 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)')),
4471 '6g': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
4472 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)')),
4473 '6h': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
4474 '(1/2, -x, 1/2)', '(1/2, 1/2, x)', '(1/2, 1/2, -x)'
4475 )),
4476 '8i': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
4477 '(x, -x, -x)', '(-x, -x, -x)', '(x, x, -x)',
4478 '(x, -x, x)', '(-x, x, x)')),
4479 '12j': (6, ('(0, y, z)', '(0, -y, z)', '(0, y, -z)',
4480 '(0, -y, -z)', '(z, 0, y)', '(z, 0, -y)',
4481 '(-z, 0, y)', '(-z, 0, -y)', '(y, z, 0)',
4482 '(-y, z, 0)', '(y, -z, 0)', '(-y, -z, 0)')),
4483 '12k': (6, ('(1/2, y, z)', '(1/2, -y, z)', '(1/2, y, -z)',
4484 '(1/2, -y, -z)', '(z, 1/2, y)', '(z, 1/2, -y)',
4485 '(-z, 1/2, y)', '(-z, 1/2, -y)', '(y, z, 1/2)',
4486 '(-y, z, 1/2)', '(y, -z, 1/2)', '(-y, -z, 1/2)')),
4487 '24l': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
4488 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
4489 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
4490 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
4491 '(-x, -y, -z)', '(x, y, -z)', '(x, -y, z)',
4492 '(-x, y, z)', '(-z, -x, -y)', '(-z, x, y)',
4493 '(z, x, -y)', '(z, -x, y)', '(-y, -z, -x)',
4494 '(y, -z, x)', '(-y, z, x)', '(y, z, -x)'))},
4495 '201:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
4496 '4b': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
4497 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)')),
4498 '4c': (0, ('(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
4499 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
4500 '6d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
4501 '(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
4502 '8e': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
4503 '(x, -x, -x)', '(-x+1/2, -x+1/2, -x+1/2)',
4504 '(x+1/2, x+1/2, -x+1/2)', '(x+1/2, -x+1/2, x+1/2)',
4505 '(-x+1/2, x+1/2, x+1/2)')),
4506 '12f': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
4507 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
4508 '(-x+1/2, 1/2, 1/2)', '(x+1/2, 1/2, 1/2)',
4509 '(1/2, -x+1/2, 1/2)', '(1/2, x+1/2, 1/2)',
4510 '(1/2, 1/2, -x+1/2)', '(1/2, 1/2, x+1/2)')),
4511 '12g': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
4512 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
4513 '(-x+1/2, 0, 1/2)', '(x+1/2, 0, 1/2)',
4514 '(1/2, -x+1/2, 0)', '(1/2, x+1/2, 0)',
4515 '(0, 1/2, -x+1/2)', '(0, 1/2, x+1/2)')),
4516 '24h': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
4517 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
4518 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
4519 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
4520 '(-x+1/2, -y+1/2, -z+1/2)',
4521 '(x+1/2, y+1/2, -z+1/2)',
4522 '(x+1/2, -y+1/2, z+1/2)',
4523 '(-x+1/2, y+1/2, z+1/2)',
4524 '(-z+1/2, -x+1/2, -y+1/2)',
4525 '(-z+1/2, x+1/2, y+1/2)',
4526 '(z+1/2, x+1/2, -y+1/2)',
4527 '(z+1/2, -x+1/2, y+1/2)',
4528 '(-y+1/2, -z+1/2, -x+1/2)',
4529 '(y+1/2, -z+1/2, x+1/2)',
4530 '(-y+1/2, z+1/2, x+1/2)',
4531 '(y+1/2, z+1/2, -x+1/2)'))},
4532 '201:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
4533 '4b': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
4534 '(0, 1/2, 1/2)')),
4535 '4c': (0, ('(1/2, 1/2, 1/2)', '(0, 0, 1/2)', '(0, 1/2, 0)',
4536 '(1/2, 0, 0)')),
4537 '6d': (0, ('(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
4538 '(3/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)',
4539 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
4540 '8e': (1, ('(x, x, x)', '(-x+1/2, -x+1/2, x)',
4541 '(-x+1/2, x, -x+1/2)', '(x, -x+1/2, -x+1/2)',
4542 '(-x, -x, -x)', '(x+1/2, x+1/2, -x)',
4543 '(x+1/2, -x, x+1/2)', '(-x, x+1/2, x+1/2)')),
4544 '12f': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
4545 '(1/4, x, 1/4)', '(1/4, -x+1/2, 1/4)',
4546 '(1/4, 1/4, x)', '(1/4, 1/4, -x+1/2)',
4547 '(-x, 3/4, 3/4)', '(x+1/2, 3/4, 3/4)',
4548 '(3/4, -x, 3/4)', '(3/4, x+1/2, 3/4)',
4549 '(3/4, 3/4, -x)', '(3/4, 3/4, x+1/2)')),
4550 '12g': (1, ('(x, 3/4, 1/4)', '(-x+1/2, 3/4, 1/4)',
4551 '(1/4, x, 3/4)', '(1/4, -x+1/2, 3/4)',
4552 '(3/4, 1/4, x)', '(3/4, 1/4, -x+1/2)',
4553 '(-x, 1/4, 3/4)', '(x+1/2, 1/4, 3/4)',
4554 '(3/4, -x, 1/4)', '(3/4, x+1/2, 1/4)',
4555 '(1/4, 3/4, -x)', '(1/4, 3/4, x+1/2)')),
4556 '24h': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
4557 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
4558 '(z, x, y)', '(z, -x+1/2, -y+1/2)',
4559 '(-z+1/2, -x+1/2, y)', '(-z+1/2, x, -y+1/2)',
4560 '(y, z, x)', '(-y+1/2, z, -x+1/2)',
4561 '(y, -z+1/2, -x+1/2)', '(-y+1/2, -z+1/2, x)',
4562 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
4563 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)',
4564 '(-z, -x, -y)', '(-z, x+1/2, y+1/2)',
4565 '(z+1/2, x+1/2, -y)', '(z+1/2, -x, y+1/2)',
4566 '(-y, -z, -x)', '(y+1/2, -z, x+1/2)',
4567 '(-y, z+1/2, x+1/2)', '(y+1/2, z+1/2, -x)'))},
4568 '202': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
4569 '(1/2, 1/2, 0)')),
4570 '4b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
4571 '(0, 0, 1/2)')),
4572 '8c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
4573 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
4574 '(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
4575 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
4576 '24d': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
4577 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
4578 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)',
4579 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
4580 '(3/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
4581 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
4582 '(3/4, 1/2, 3/4)', '(1/4, 1/4, 0)',
4583 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
4584 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
4585 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
4586 '24e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
4587 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
4588 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
4589 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
4590 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
4591 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
4592 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
4593 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
4594 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)')),
4595 '32f': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
4596 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
4597 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
4598 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
4599 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
4600 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
4601 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
4602 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
4603 '(-x, -x, -x)', '(-x, -x+1/2, -x+1/2)',
4604 '(-x+1/2, -x, -x+1/2)', '(-x+1/2, -x+1/2, -x)',
4605 '(x, x, -x)', '(x, x+1/2, -x+1/2)',
4606 '(x+1/2, x, -x+1/2)', '(x+1/2, x+1/2, -x)',
4607 '(x, -x, x)', '(x, -x+1/2, x+1/2)',
4608 '(x+1/2, -x, x+1/2)', '(x+1/2, -x+1/2, x)',
4609 '(-x, x, x)', '(-x, x+1/2, x+1/2)',
4610 '(-x+1/2, x, x+1/2)', '(-x+1/2, x+1/2, x)')),
4611 '48g': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
4612 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
4613 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
4614 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
4615 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
4616 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
4617 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
4618 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
4619 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
4620 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
4621 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
4622 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)',
4623 '(-x, 3/4, 3/4)', '(-x, 1/4, 1/4)',
4624 '(-x+1/2, 3/4, 1/4)', '(-x+1/2, 1/4, 3/4)',
4625 '(x, 1/4, 3/4)', '(x, 3/4, 1/4)',
4626 '(x+1/2, 1/4, 1/4)', '(x+1/2, 3/4, 3/4)',
4627 '(3/4, -x, 3/4)', '(3/4, -x+1/2, 1/4)',
4628 '(1/4, -x, 1/4)', '(1/4, -x+1/2, 3/4)',
4629 '(3/4, x, 1/4)', '(3/4, x+1/2, 3/4)',
4630 '(1/4, x, 3/4)', '(1/4, x+1/2, 1/4)',
4631 '(3/4, 3/4, -x)', '(3/4, 1/4, -x+1/2)',
4632 '(1/4, 3/4, -x+1/2)', '(1/4, 1/4, -x)',
4633 '(1/4, 3/4, x)', '(1/4, 1/4, x+1/2)',
4634 '(3/4, 3/4, x+1/2)', '(3/4, 1/4, x)')),
4635 '48h': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(1/2, y, z+1/2)',
4636 '(1/2, y+1/2, z)', '(0, -y, z)',
4637 '(0, -y+1/2, z+1/2)', '(1/2, -y, z+1/2)',
4638 '(1/2, -y+1/2, z)', '(0, y, -z)',
4639 '(0, y+1/2, -z+1/2)', '(1/2, y, -z+1/2)',
4640 '(1/2, y+1/2, -z)', '(0, -y, -z)',
4641 '(0, -y+1/2, -z+1/2)', '(1/2, -y, -z+1/2)',
4642 '(1/2, -y+1/2, -z)', '(z, 0, y)', '(z, 1/2, y+1/2)',
4643 '(z+1/2, 0, y+1/2)', '(z+1/2, 1/2, y)',
4644 '(z, 0, -y)', '(z, 1/2, -y+1/2)',
4645 '(z+1/2, 0, -y+1/2)', '(z+1/2, 1/2, -y)',
4646 '(-z, 0, y)', '(-z, 1/2, y+1/2)',
4647 '(-z+1/2, 0, y+1/2)', '(-z+1/2, 1/2, y)',
4648 '(-z, 0, -y)', '(-z, 1/2, -y+1/2)',
4649 '(-z+1/2, 0, -y+1/2)', '(-z+1/2, 1/2, -y)',
4650 '(y, z, 0)', '(y, z+1/2, 1/2)', '(y+1/2, z, 1/2)',
4651 '(y+1/2, z+1/2, 0)', '(-y, z, 0)',
4652 '(-y, z+1/2, 1/2)', '(-y+1/2, z, 1/2)',
4653 '(-y+1/2, z+1/2, 0)', '(y, -z, 0)',
4654 '(y, -z+1/2, 1/2)', '(y+1/2, -z, 1/2)',
4655 '(y+1/2, -z+1/2, 0)', '(-y, -z, 0)',
4656 '(-y, -z+1/2, 1/2)', '(-y+1/2, -z, 1/2)',
4657 '(-y+1/2, -z+1/2, 0)')),
4658 '96i': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
4659 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
4660 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
4661 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
4662 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
4663 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
4664 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
4665 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
4666 '(z, x, y)', '(z, x+1/2, y+1/2)',
4667 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
4668 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
4669 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
4670 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
4671 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
4672 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
4673 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
4674 '(y, z, x)', '(y, z+1/2, x+1/2)',
4675 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
4676 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
4677 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
4678 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
4679 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
4680 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
4681 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
4682 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
4683 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
4684 '(x, y, -z)', '(x, y+1/2, -z+1/2)',
4685 '(x+1/2, y, -z+1/2)', '(x+1/2, y+1/2, -z)',
4686 '(x, -y, z)', '(x, -y+1/2, z+1/2)',
4687 '(x+1/2, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
4688 '(-x, y, z)', '(-x, y+1/2, z+1/2)',
4689 '(-x+1/2, y, z+1/2)', '(-x+1/2, y+1/2, z)',
4690 '(-z, -x, -y)', '(-z, -x+1/2, -y+1/2)',
4691 '(-z+1/2, -x, -y+1/2)', '(-z+1/2, -x+1/2, -y)',
4692 '(-z, x, y)', '(-z, x+1/2, y+1/2)',
4693 '(-z+1/2, x, y+1/2)', '(-z+1/2, x+1/2, y)',
4694 '(z, x, -y)', '(z, x+1/2, -y+1/2)',
4695 '(z+1/2, x, -y+1/2)', '(z+1/2, x+1/2, -y)',
4696 '(z, -x, y)', '(z, -x+1/2, y+1/2)',
4697 '(z+1/2, -x, y+1/2)', '(z+1/2, -x+1/2, y)',
4698 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
4699 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
4700 '(y, -z, x)', '(y, -z+1/2, x+1/2)',
4701 '(y+1/2, -z, x+1/2)', '(y+1/2, -z+1/2, x)',
4702 '(-y, z, x)', '(-y, z+1/2, x+1/2)',
4703 '(-y+1/2, z, x+1/2)', '(-y+1/2, z+1/2, x)',
4704 '(y, z, -x)', '(y, z+1/2, -x+1/2)',
4705 '(y+1/2, z, -x+1/2)', '(y+1/2, z+1/2, -x)'))},
4706 '203:1': {'8a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
4707 '(1/2, 1/2, 0)', '(1/4, 1/4, 1/4)',
4708 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
4709 '(3/4, 3/4, 1/4)')),
4710 '8b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
4711 '(0, 0, 1/2)', '(3/4, 3/4, 3/4)',
4712 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
4713 '(1/4, 1/4, 3/4)')),
4714 '16c': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
4715 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
4716 '(7/8, 7/8, 1/8)', '(7/8, 3/8, 5/8)',
4717 '(3/8, 7/8, 5/8)', '(3/8, 3/8, 1/8)',
4718 '(7/8, 1/8, 7/8)', '(7/8, 5/8, 3/8)',
4719 '(3/8, 1/8, 3/8)', '(3/8, 5/8, 7/8)',
4720 '(1/8, 7/8, 7/8)', '(1/8, 3/8, 3/8)',
4721 '(5/8, 7/8, 3/8)', '(5/8, 3/8, 7/8)')),
4722 '16d': (0, ('(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
4723 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
4724 '(3/8, 3/8, 5/8)', '(3/8, 7/8, 1/8)',
4725 '(7/8, 3/8, 1/8)', '(7/8, 7/8, 5/8)',
4726 '(3/8, 5/8, 3/8)', '(3/8, 1/8, 7/8)',
4727 '(7/8, 5/8, 7/8)', '(7/8, 1/8, 3/8)',
4728 '(5/8, 3/8, 3/8)', '(5/8, 7/8, 7/8)',
4729 '(1/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)')),
4730 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
4731 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
4732 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
4733 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
4734 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
4735 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
4736 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
4737 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
4738 '(-x+1/4, -x+1/4, -x+1/4)',
4739 '(-x+1/4, -x+3/4, -x+3/4)',
4740 '(-x+3/4, -x+1/4, -x+3/4)',
4741 '(-x+3/4, -x+3/4, -x+1/4)',
4742 '(x+1/4, x+1/4, -x+1/4)',
4743 '(x+1/4, x+3/4, -x+3/4)',
4744 '(x+3/4, x+1/4, -x+3/4)',
4745 '(x+3/4, x+3/4, -x+1/4)',
4746 '(x+1/4, -x+1/4, x+1/4)',
4747 '(x+1/4, -x+3/4, x+3/4)',
4748 '(x+3/4, -x+1/4, x+3/4)',
4749 '(x+3/4, -x+3/4, x+1/4)',
4750 '(-x+1/4, x+1/4, x+1/4)',
4751 '(-x+1/4, x+3/4, x+3/4)',
4752 '(-x+3/4, x+1/4, x+3/4)',
4753 '(-x+3/4, x+3/4, x+1/4)')),
4754 '48f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
4755 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
4756 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)',
4757 '(0, x, 0)', '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
4758 '(1/2, x+1/2, 0)', '(0, -x, 0)',
4759 '(0, -x+1/2, 1/2)', '(1/2, -x, 1/2)',
4760 '(1/2, -x+1/2, 0)', '(0, 0, x)',
4761 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
4762 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
4763 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)',
4764 '(-x+1/4, 1/4, 1/4)', '(-x+1/4, 3/4, 3/4)',
4765 '(-x+3/4, 1/4, 3/4)', '(-x+3/4, 3/4, 1/4)',
4766 '(x+1/4, 1/4, 1/4)', '(x+1/4, 3/4, 3/4)',
4767 '(x+3/4, 1/4, 3/4)', '(x+3/4, 3/4, 1/4)',
4768 '(1/4, -x+1/4, 1/4)', '(1/4, -x+3/4, 3/4)',
4769 '(3/4, -x+1/4, 3/4)', '(3/4, -x+3/4, 1/4)',
4770 '(1/4, x+1/4, 1/4)', '(1/4, x+3/4, 3/4)',
4771 '(3/4, x+1/4, 3/4)', '(3/4, x+3/4, 1/4)',
4772 '(1/4, 1/4, -x+1/4)', '(1/4, 3/4, -x+3/4)',
4773 '(3/4, 1/4, -x+3/4)', '(3/4, 3/4, -x+1/4)',
4774 '(1/4, 1/4, x+1/4)', '(1/4, 3/4, x+3/4)',
4775 '(3/4, 1/4, x+3/4)', '(3/4, 3/4, x+1/4)')),
4776 '96g': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
4777 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
4778 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
4779 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
4780 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
4781 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
4782 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
4783 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
4784 '(z, x, y)', '(z, x+1/2, y+1/2)',
4785 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
4786 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
4787 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
4788 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
4789 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
4790 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
4791 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
4792 '(y, z, x)', '(y, z+1/2, x+1/2)',
4793 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
4794 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
4795 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
4796 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
4797 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
4798 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
4799 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
4800 '(-x+1/4, -y+1/4, -z+1/4)',
4801 '(-x+1/4, -y+3/4, -z+3/4)',
4802 '(-x+3/4, -y+1/4, -z+3/4)',
4803 '(-x+3/4, -y+3/4, -z+1/4)',
4804 '(x+1/4, y+1/4, -z+1/4)',
4805 '(x+1/4, y+3/4, -z+3/4)',
4806 '(x+3/4, y+1/4, -z+3/4)',
4807 '(x+3/4, y+3/4, -z+1/4)',
4808 '(x+1/4, -y+1/4, z+1/4)',
4809 '(x+1/4, -y+3/4, z+3/4)',
4810 '(x+3/4, -y+1/4, z+3/4)',
4811 '(x+3/4, -y+3/4, z+1/4)',
4812 '(-x+1/4, y+1/4, z+1/4)',
4813 '(-x+1/4, y+3/4, z+3/4)',
4814 '(-x+3/4, y+1/4, z+3/4)',
4815 '(-x+3/4, y+3/4, z+1/4)',
4816 '(-z+1/4, -x+1/4, -y+1/4)',
4817 '(-z+1/4, -x+3/4, -y+3/4)',
4818 '(-z+3/4, -x+1/4, -y+3/4)',
4819 '(-z+3/4, -x+3/4, -y+1/4)',
4820 '(-z+1/4, x+1/4, y+1/4)',
4821 '(-z+1/4, x+3/4, y+3/4)',
4822 '(-z+3/4, x+1/4, y+3/4)',
4823 '(-z+3/4, x+3/4, y+1/4)',
4824 '(z+1/4, x+1/4, -y+1/4)',
4825 '(z+1/4, x+3/4, -y+3/4)',
4826 '(z+3/4, x+1/4, -y+3/4)',
4827 '(z+3/4, x+3/4, -y+1/4)',
4828 '(z+1/4, -x+1/4, y+1/4)',
4829 '(z+1/4, -x+3/4, y+3/4)',
4830 '(z+3/4, -x+1/4, y+3/4)',
4831 '(z+3/4, -x+3/4, y+1/4)',
4832 '(-y+1/4, -z+1/4, -x+1/4)',
4833 '(-y+1/4, -z+3/4, -x+3/4)',
4834 '(-y+3/4, -z+1/4, -x+3/4)',
4835 '(-y+3/4, -z+3/4, -x+1/4)',
4836 '(y+1/4, -z+1/4, x+1/4)',
4837 '(y+1/4, -z+3/4, x+3/4)',
4838 '(y+3/4, -z+1/4, x+3/4)',
4839 '(y+3/4, -z+3/4, x+1/4)',
4840 '(-y+1/4, z+1/4, x+1/4)',
4841 '(-y+1/4, z+3/4, x+3/4)',
4842 '(-y+3/4, z+1/4, x+3/4)',
4843 '(-y+3/4, z+3/4, x+1/4)',
4844 '(y+1/4, z+1/4, -x+1/4)',
4845 '(y+1/4, z+3/4, -x+3/4)',
4846 '(y+3/4, z+1/4, -x+3/4)',
4847 '(y+3/4, z+3/4, -x+1/4)'))},
4848 '203:2': {'8a': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
4849 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
4850 '(7/8, 7/8, 7/8)', '(7/8, 3/8, 3/8)',
4851 '(3/8, 7/8, 3/8)', '(3/8, 3/8, 7/8)')),
4852 '8b': (0, ('(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
4853 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
4854 '(3/8, 3/8, 3/8)', '(3/8, 7/8, 7/8)',
4855 '(7/8, 3/8, 7/8)', '(7/8, 7/8, 3/8)')),
4856 '16c': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
4857 '(1/2, 1/2, 0)', '(3/4, 3/4, 0)',
4858 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)',
4859 '(1/4, 1/4, 0)', '(3/4, 0, 3/4)',
4860 '(3/4, 1/2, 1/4)', '(1/4, 0, 1/4)',
4861 '(1/4, 1/2, 3/4)', '(0, 3/4, 3/4)',
4862 '(0, 1/4, 1/4)', '(1/2, 3/4, 1/4)',
4863 '(1/2, 1/4, 3/4)')),
4864 '16d': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
4865 '(0, 0, 1/2)', '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)',
4866 '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
4867 '(1/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
4868 '(3/4, 1/2, 3/4)', '(3/4, 0, 1/4)',
4869 '(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)',
4870 '(0, 1/4, 3/4)', '(0, 3/4, 1/4)')),
4871 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
4872 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
4873 '(-x+3/4, -x+3/4, x)', '(-x+3/4, -x+1/4, x+1/2)',
4874 '(-x+1/4, -x+3/4, x+1/2)', '(-x+1/4, -x+1/4, x)',
4875 '(-x+3/4, x, -x+3/4)', '(-x+3/4, x+1/2, -x+1/4)',
4876 '(-x+1/4, x, -x+1/4)', '(-x+1/4, x+1/2, -x+3/4)',
4877 '(x, -x+3/4, -x+3/4)', '(x, -x+1/4, -x+1/4)',
4878 '(x+1/2, -x+3/4, -x+1/4)',
4879 '(x+1/2, -x+1/4, -x+3/4)', '(-x, -x, -x)',
4880 '(-x, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x+1/2)',
4881 '(-x+1/2, -x+1/2, -x)', '(x+1/4, x+1/4, -x)',
4882 '(x+1/4, x+3/4, -x+1/2)',
4883 '(x+3/4, x+1/4, -x+1/2)', '(x+3/4, x+3/4, -x)',
4884 '(x+1/4, -x, x+1/4)', '(x+1/4, -x+1/2, x+3/4)',
4885 '(x+3/4, -x, x+3/4)', '(x+3/4, -x+1/2, x+1/4)',
4886 '(-x, x+1/4, x+1/4)', '(-x, x+3/4, x+3/4)',
4887 '(-x+1/2, x+1/4, x+3/4)',
4888 '(-x+1/2, x+3/4, x+1/4)')),
4889 '48f': (1, ('(x, 1/8, 1/8)', '(x, 5/8, 5/8)',
4890 '(x+1/2, 1/8, 5/8)', '(x+1/2, 5/8, 1/8)',
4891 '(-x+3/4, 5/8, 1/8)', '(-x+3/4, 1/8, 5/8)',
4892 '(-x+1/4, 5/8, 5/8)', '(-x+1/4, 1/8, 1/8)',
4893 '(1/8, x, 1/8)', '(1/8, x+1/2, 5/8)',
4894 '(5/8, x, 5/8)', '(5/8, x+1/2, 1/8)',
4895 '(1/8, -x+3/4, 5/8)', '(1/8, -x+1/4, 1/8)',
4896 '(5/8, -x+3/4, 1/8)', '(5/8, -x+1/4, 5/8)',
4897 '(1/8, 1/8, x)', '(1/8, 5/8, x+1/2)',
4898 '(5/8, 1/8, x+1/2)', '(5/8, 5/8, x)',
4899 '(5/8, 1/8, -x+3/4)', '(5/8, 5/8, -x+1/4)',
4900 '(1/8, 1/8, -x+1/4)', '(1/8, 5/8, -x+3/4)',
4901 '(-x, 7/8, 7/8)', '(-x, 3/8, 3/8)',
4902 '(-x+1/2, 7/8, 3/8)', '(-x+1/2, 3/8, 7/8)',
4903 '(x+1/4, 3/8, 7/8)', '(x+1/4, 7/8, 3/8)',
4904 '(x+3/4, 3/8, 3/8)', '(x+3/4, 7/8, 7/8)',
4905 '(7/8, -x, 7/8)', '(7/8, -x+1/2, 3/8)',
4906 '(3/8, -x, 3/8)', '(3/8, -x+1/2, 7/8)',
4907 '(7/8, x+1/4, 3/8)', '(7/8, x+3/4, 7/8)',
4908 '(3/8, x+1/4, 7/8)', '(3/8, x+3/4, 3/8)',
4909 '(7/8, 7/8, -x)', '(7/8, 3/8, -x+1/2)',
4910 '(3/8, 7/8, -x+1/2)', '(3/8, 3/8, -x)',
4911 '(3/8, 7/8, x+1/4)', '(3/8, 3/8, x+3/4)',
4912 '(7/8, 7/8, x+3/4)', '(7/8, 3/8, x+1/4)')),
4913 '96g': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
4914 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
4915 '(-x+3/4, -y+3/4, z)', '(-x+3/4, -y+1/4, z+1/2)',
4916 '(-x+1/4, -y+3/4, z+1/2)', '(-x+1/4, -y+1/4, z)',
4917 '(-x+3/4, y, -z+3/4)', '(-x+3/4, y+1/2, -z+1/4)',
4918 '(-x+1/4, y, -z+1/4)', '(-x+1/4, y+1/2, -z+3/4)',
4919 '(x, -y+3/4, -z+3/4)', '(x, -y+1/4, -z+1/4)',
4920 '(x+1/2, -y+3/4, -z+1/4)',
4921 '(x+1/2, -y+1/4, -z+3/4)', '(z, x, y)',
4922 '(z, x+1/2, y+1/2)', '(z+1/2, x, y+1/2)',
4923 '(z+1/2, x+1/2, y)', '(z, -x+3/4, -y+3/4)',
4924 '(z, -x+1/4, -y+1/4)', '(z+1/2, -x+3/4, -y+1/4)',
4925 '(z+1/2, -x+1/4, -y+3/4)', '(-z+3/4, -x+3/4, y)',
4926 '(-z+3/4, -x+1/4, y+1/2)',
4927 '(-z+1/4, -x+3/4, y+1/2)', '(-z+1/4, -x+1/4, y)',
4928 '(-z+3/4, x, -y+3/4)', '(-z+3/4, x+1/2, -y+1/4)',
4929 '(-z+1/4, x, -y+1/4)', '(-z+1/4, x+1/2, -y+3/4)',
4930 '(y, z, x)', '(y, z+1/2, x+1/2)',
4931 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
4932 '(-y+3/4, z, -x+3/4)', '(-y+3/4, z+1/2, -x+1/4)',
4933 '(-y+1/4, z, -x+1/4)', '(-y+1/4, z+1/2, -x+3/4)',
4934 '(y, -z+3/4, -x+3/4)', '(y, -z+1/4, -x+1/4)',
4935 '(y+1/2, -z+3/4, -x+1/4)',
4936 '(y+1/2, -z+1/4, -x+3/4)', '(-y+3/4, -z+3/4, x)',
4937 '(-y+3/4, -z+1/4, x+1/2)',
4938 '(-y+1/4, -z+3/4, x+1/2)', '(-y+1/4, -z+1/4, x)',
4939 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
4940 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
4941 '(x+1/4, y+1/4, -z)', '(x+1/4, y+3/4, -z+1/2)',
4942 '(x+3/4, y+1/4, -z+1/2)', '(x+3/4, y+3/4, -z)',
4943 '(x+1/4, -y, z+1/4)', '(x+1/4, -y+1/2, z+3/4)',
4944 '(x+3/4, -y, z+3/4)', '(x+3/4, -y+1/2, z+1/4)',
4945 '(-x, y+1/4, z+1/4)', '(-x, y+3/4, z+3/4)',
4946 '(-x+1/2, y+1/4, z+3/4)',
4947 '(-x+1/2, y+3/4, z+1/4)', '(-z, -x, -y)',
4948 '(-z, -x+1/2, -y+1/2)', '(-z+1/2, -x, -y+1/2)',
4949 '(-z+1/2, -x+1/2, -y)', '(-z, x+1/4, y+1/4)',
4950 '(-z, x+3/4, y+3/4)', '(-z+1/2, x+1/4, y+3/4)',
4951 '(-z+1/2, x+3/4, y+1/4)', '(z+1/4, x+1/4, -y)',
4952 '(z+1/4, x+3/4, -y+1/2)',
4953 '(z+3/4, x+1/4, -y+1/2)', '(z+3/4, x+3/4, -y)',
4954 '(z+1/4, -x, y+1/4)', '(z+1/4, -x+1/2, y+3/4)',
4955 '(z+3/4, -x, y+3/4)', '(z+3/4, -x+1/2, y+1/4)',
4956 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
4957 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
4958 '(y+1/4, -z, x+1/4)', '(y+1/4, -z+1/2, x+3/4)',
4959 '(y+3/4, -z, x+3/4)', '(y+3/4, -z+1/2, x+1/4)',
4960 '(-y, z+1/4, x+1/4)', '(-y, z+3/4, x+3/4)',
4961 '(-y+1/2, z+1/4, x+3/4)',
4962 '(-y+1/2, z+3/4, x+1/4)', '(y+1/4, z+1/4, -x)',
4963 '(y+1/4, z+3/4, -x+1/2)',
4964 '(y+3/4, z+1/4, -x+1/2)', '(y+3/4, z+3/4, -x)'))},
4965 '204': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
4966 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
4967 '(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
4968 '8c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
4969 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
4970 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
4971 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
4972 '12d': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
4973 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
4974 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
4975 '(1/2, -x+1/2, 1/2)', '(0, 0, x)',
4976 '(1/2, 1/2, x+1/2)', '(0, 0, -x)',
4977 '(1/2, 1/2, -x+1/2)')),
4978 '12e': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
4979 '(-x+1/2, 1/2, 0)', '(1/2, x, 0)',
4980 '(0, x+1/2, 1/2)', '(1/2, -x, 0)',
4981 '(0, -x+1/2, 1/2)', '(0, 1/2, x)',
4982 '(1/2, 0, x+1/2)', '(0, 1/2, -x)',
4983 '(1/2, 0, -x+1/2)')),
4984 '16f': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)', '(-x, -x, x)',
4985 '(-x+1/2, -x+1/2, x+1/2)', '(-x, x, -x)',
4986 '(-x+1/2, x+1/2, -x+1/2)', '(x, -x, -x)',
4987 '(x+1/2, -x+1/2, -x+1/2)', '(-x, -x, -x)',
4988 '(-x+1/2, -x+1/2, -x+1/2)', '(x, x, -x)',
4989 '(x+1/2, x+1/2, -x+1/2)', '(x, -x, x)',
4990 '(x+1/2, -x+1/2, x+1/2)', '(-x, x, x)',
4991 '(-x+1/2, x+1/2, x+1/2)')),
4992 '24g': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y, z)',
4993 '(1/2, -y+1/2, z+1/2)', '(0, y, -z)',
4994 '(1/2, y+1/2, -z+1/2)', '(0, -y, -z)',
4995 '(1/2, -y+1/2, -z+1/2)', '(z, 0, y)',
4996 '(z+1/2, 1/2, y+1/2)', '(z, 0, -y)',
4997 '(z+1/2, 1/2, -y+1/2)', '(-z, 0, y)',
4998 '(-z+1/2, 1/2, y+1/2)', '(-z, 0, -y)',
4999 '(-z+1/2, 1/2, -y+1/2)', '(y, z, 0)',
5000 '(y+1/2, z+1/2, 1/2)', '(-y, z, 0)',
5001 '(-y+1/2, z+1/2, 1/2)', '(y, -z, 0)',
5002 '(y+1/2, -z+1/2, 1/2)', '(-y, -z, 0)',
5003 '(-y+1/2, -z+1/2, 1/2)')),
5004 '48h': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
5005 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
5006 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
5007 '(x+1/2, -y+1/2, -z+1/2)', '(z, x, y)',
5008 '(z+1/2, x+1/2, y+1/2)', '(z, -x, -y)',
5009 '(z+1/2, -x+1/2, -y+1/2)', '(-z, -x, y)',
5010 '(-z+1/2, -x+1/2, y+1/2)', '(-z, x, -y)',
5011 '(-z+1/2, x+1/2, -y+1/2)', '(y, z, x)',
5012 '(y+1/2, z+1/2, x+1/2)', '(-y, z, -x)',
5013 '(-y+1/2, z+1/2, -x+1/2)', '(y, -z, -x)',
5014 '(y+1/2, -z+1/2, -x+1/2)', '(-y, -z, x)',
5015 '(-y+1/2, -z+1/2, x+1/2)', '(-x, -y, -z)',
5016 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
5017 '(x+1/2, y+1/2, -z+1/2)', '(x, -y, z)',
5018 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
5019 '(-x+1/2, y+1/2, z+1/2)', '(-z, -x, -y)',
5020 '(-z+1/2, -x+1/2, -y+1/2)', '(-z, x, y)',
5021 '(-z+1/2, x+1/2, y+1/2)', '(z, x, -y)',
5022 '(z+1/2, x+1/2, -y+1/2)', '(z, -x, y)',
5023 '(z+1/2, -x+1/2, y+1/2)', '(-y, -z, -x)',
5024 '(-y+1/2, -z+1/2, -x+1/2)', '(y, -z, x)',
5025 '(y+1/2, -z+1/2, x+1/2)', '(-y, z, x)',
5026 '(-y+1/2, z+1/2, x+1/2)', '(y, z, -x)',
5027 '(y+1/2, z+1/2, -x+1/2)'))},
5028 '205': {'4a': (0, ('(0, 0, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)',
5029 '(1/2, 1/2, 0)')),
5030 '4b': (0, ('(1/2, 1/2, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 0)',
5031 '(0, 0, 1/2)')),
5032 '8c': (1, ('(x, x, x)', '(-x+1/2, -x, x+1/2)',
5033 '(-x, x+1/2, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5034 '(-x, -x, -x)', '(x+1/2, x, -x+1/2)',
5035 '(x, -x+1/2, x+1/2)', '(-x+1/2, x+1/2, x)')),
5036 '24d': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
5037 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5038 '(z, x, y)', '(z+1/2, -x+1/2, -y)',
5039 '(-z+1/2, -x, y+1/2)', '(-z, x+1/2, -y+1/2)',
5040 '(y, z, x)', '(-y, z+1/2, -x+1/2)',
5041 '(y+1/2, -z+1/2, -x)', '(-y+1/2, -z, x+1/2)',
5042 '(-x, -y, -z)', '(x+1/2, y, -z+1/2)',
5043 '(x, -y+1/2, z+1/2)', '(-x+1/2, y+1/2, z)',
5044 '(-z, -x, -y)', '(-z+1/2, x+1/2, y)',
5045 '(z+1/2, x, -y+1/2)', '(z, -x+1/2, y+1/2)',
5046 '(-y, -z, -x)', '(y, -z+1/2, x+1/2)',
5047 '(-y+1/2, z+1/2, x)', '(y+1/2, z, -x+1/2)'))},
5048 '206': {'8a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
5049 '(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 0)',
5050 '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
5051 '8b': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
5052 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
5053 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
5054 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)')),
5055 '16c': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)',
5056 '(-x+1/2, -x, x+1/2)', '(-x, -x+1/2, x)',
5057 '(-x, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
5058 '(x+1/2, -x+1/2, -x)', '(x, -x, -x+1/2)',
5059 '(-x, -x, -x)', '(-x+1/2, -x+1/2, -x+1/2)',
5060 '(x+1/2, x, -x+1/2)', '(x, x+1/2, -x)',
5061 '(x, -x+1/2, x+1/2)', '(x+1/2, -x, x)',
5062 '(-x+1/2, x+1/2, x)', '(-x, x, x+1/2)')),
5063 '24d': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
5064 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(1/4, x, 0)',
5065 '(3/4, x+1/2, 1/2)', '(3/4, -x+1/2, 0)',
5066 '(1/4, -x, 1/2)', '(0, 1/4, x)',
5067 '(1/2, 3/4, x+1/2)', '(0, 3/4, -x+1/2)',
5068 '(1/2, 1/4, -x)', '(-x, 0, 3/4)',
5069 '(-x+1/2, 1/2, 1/4)', '(x+1/2, 0, 1/4)',
5070 '(x, 1/2, 3/4)', '(3/4, -x, 0)',
5071 '(1/4, -x+1/2, 1/2)', '(1/4, x+1/2, 0)',
5072 '(3/4, x, 1/2)', '(0, 3/4, -x)',
5073 '(1/2, 1/4, -x+1/2)', '(0, 1/4, x+1/2)',
5074 '(1/2, 3/4, x)')),
5075 '48e': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
5076 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
5077 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
5078 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
5079 '(z, x, y)', '(z+1/2, x+1/2, y+1/2)',
5080 '(z+1/2, -x+1/2, -y)', '(z, -x, -y+1/2)',
5081 '(-z+1/2, -x, y+1/2)', '(-z, -x+1/2, y)',
5082 '(-z, x+1/2, -y+1/2)', '(-z+1/2, x, -y)',
5083 '(y, z, x)', '(y+1/2, z+1/2, x+1/2)',
5084 '(-y, z+1/2, -x+1/2)', '(-y+1/2, z, -x)',
5085 '(y+1/2, -z+1/2, -x)', '(y, -z, -x+1/2)',
5086 '(-y+1/2, -z, x+1/2)', '(-y, -z+1/2, x)',
5087 '(-x, -y, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
5088 '(x+1/2, y, -z+1/2)', '(x, y+1/2, -z)',
5089 '(x, -y+1/2, z+1/2)', '(x+1/2, -y, z)',
5090 '(-x+1/2, y+1/2, z)', '(-x, y, z+1/2)',
5091 '(-z, -x, -y)', '(-z+1/2, -x+1/2, -y+1/2)',
5092 '(-z+1/2, x+1/2, y)', '(-z, x, y+1/2)',
5093 '(z+1/2, x, -y+1/2)', '(z, x+1/2, -y)',
5094 '(z, -x+1/2, y+1/2)', '(z+1/2, -x, y)',
5095 '(-y, -z, -x)', '(-y+1/2, -z+1/2, -x+1/2)',
5096 '(y, -z+1/2, x+1/2)', '(y+1/2, -z, x)',
5097 '(-y+1/2, z+1/2, x)', '(-y, z, x+1/2)',
5098 '(y+1/2, z, -x+1/2)', '(y, z+1/2, -x)'))},
5099 '207': {'1a': (0, ('(0, 0, 0)', )),
5100 '1b': (0, ('(1/2, 1/2, 1/2)', )),
5101 '3c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
5102 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
5103 '6e': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)',
5104 '(0, 0, x)', '(0, 0, -x)')),
5105 '6f': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
5106 '(1/2, -x, 1/2)', '(1/2, 1/2, x)', '(1/2, 1/2, -x)'
5107 )),
5108 '8g': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
5109 '(x, -x, -x)', '(x, x, -x)', '(-x, -x, -x)',
5110 '(x, -x, x)', '(-x, x, x)')),
5111 '12h': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
5112 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
5113 '(1/2, x, 0)', '(1/2, -x, 0)', '(x, 0, 1/2)',
5114 '(-x, 0, 1/2)', '(0, 1/2, -x)', '(0, 1/2, x)')),
5115 '12i': (2, ('(0, y, y)', '(0, -y, y)', '(0, y, -y)',
5116 '(0, -y, -y)', '(y, 0, y)', '(y, 0, -y)',
5117 '(-y, 0, y)', '(-y, 0, -y)', '(y, y, 0)',
5118 '(-y, y, 0)', '(y, -y, 0)', '(-y, -y, 0)')),
5119 '12j': (2, ('(1/2, y, y)', '(1/2, -y, y)', '(1/2, y, -y)',
5120 '(1/2, -y, -y)', '(y, 1/2, y)', '(y, 1/2, -y)',
5121 '(-y, 1/2, y)', '(-y, 1/2, -y)', '(y, y, 1/2)',
5122 '(-y, y, 1/2)', '(y, -y, 1/2)', '(-y, -y, 1/2)')),
5123 '24k': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
5124 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
5125 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
5126 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
5127 '(y, x, -z)', '(-y, -x, -z)', '(y, -x, z)',
5128 '(-y, x, z)', '(x, z, -y)', '(-x, z, y)',
5129 '(-x, -z, -y)', '(x, -z, y)', '(z, y, -x)',
5130 '(z, -y, x)', '(-z, y, x)', '(-z, -y, -x)'))},
5131 '208': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
5132 '4b': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
5133 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)')),
5134 '4c': (0, ('(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
5135 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
5136 '6d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
5137 '(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 0, 1/2)')),
5138 '6e': (0, ('(1/4, 0, 1/2)', '(3/4, 0, 1/2)', '(1/2, 1/4, 0)',
5139 '(1/2, 3/4, 0)', '(0, 1/2, 1/4)', '(0, 1/2, 3/4)')),
5140 '6f': (0, ('(1/4, 1/2, 0)', '(3/4, 1/2, 0)', '(0, 1/4, 1/2)',
5141 '(0, 3/4, 1/2)', '(1/2, 0, 1/4)', '(1/2, 0, 3/4)')),
5142 '8g': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
5143 '(x, -x, -x)', '(x+1/2, x+1/2, -x+1/2)',
5144 '(-x+1/2, -x+1/2, -x+1/2)', '(x+1/2, -x+1/2, x+1/2)',
5145 '(-x+1/2, x+1/2, x+1/2)')),
5146 '12h': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
5147 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
5148 '(1/2, x+1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
5149 '(x+1/2, 1/2, 1/2)', '(-x+1/2, 1/2, 1/2)',
5150 '(1/2, 1/2, -x+1/2)', '(1/2, 1/2, x+1/2)')),
5151 '12i': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
5152 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)',
5153 '(1/2, x+1/2, 0)', '(1/2, -x+1/2, 0)',
5154 '(x+1/2, 0, 1/2)', '(-x+1/2, 0, 1/2)',
5155 '(0, 1/2, -x+1/2)', '(0, 1/2, x+1/2)')),
5156 '12j': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
5157 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
5158 '(0, x+1/2, 1/2)', '(0, -x+1/2, 1/2)',
5159 '(x+1/2, 1/2, 0)', '(-x+1/2, 1/2, 0)',
5160 '(1/2, 0, -x+1/2)', '(1/2, 0, x+1/2)')),
5161 '12k': (2, ('(1/4, y, -y+1/2)', '(3/4, -y, -y+1/2)',
5162 '(3/4, y, y+1/2)', '(1/4, -y, y+1/2)',
5163 '(-y+1/2, 1/4, y)', '(-y+1/2, 3/4, -y)',
5164 '(y+1/2, 3/4, y)', '(y+1/2, 1/4, -y)',
5165 '(y, -y+1/2, 1/4)', '(-y, -y+1/2, 3/4)',
5166 '(y, y+1/2, 3/4)', '(-y, y+1/2, 1/4)')),
5167 '12l': (2, ('(1/4, y, y+1/2)', '(3/4, -y, y+1/2)',
5168 '(3/4, y, -y+1/2)', '(1/4, -y, -y+1/2)',
5169 '(y+1/2, 1/4, y)', '(y+1/2, 3/4, -y)',
5170 '(-y+1/2, 3/4, y)', '(-y+1/2, 1/4, -y)',
5171 '(y, y+1/2, 1/4)', '(-y, y+1/2, 3/4)',
5172 '(y, -y+1/2, 3/4)', '(-y, -y+1/2, 1/4)')),
5173 '24m': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
5174 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
5175 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
5176 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
5177 '(y+1/2, x+1/2, -z+1/2)',
5178 '(-y+1/2, -x+1/2, -z+1/2)',
5179 '(y+1/2, -x+1/2, z+1/2)', '(-y+1/2, x+1/2, z+1/2)',
5180 '(x+1/2, z+1/2, -y+1/2)', '(-x+1/2, z+1/2, y+1/2)',
5181 '(-x+1/2, -z+1/2, -y+1/2)',
5182 '(x+1/2, -z+1/2, y+1/2)', '(z+1/2, y+1/2, -x+1/2)',
5183 '(z+1/2, -y+1/2, x+1/2)', '(-z+1/2, y+1/2, x+1/2)',
5184 '(-z+1/2, -y+1/2, -x+1/2)'))},
5185 '209': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
5186 '(1/2, 1/2, 0)')),
5187 '4b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
5188 '(0, 0, 1/2)')),
5189 '8c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
5190 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
5191 '(1/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
5192 '(3/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
5193 '24d': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
5194 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
5195 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)',
5196 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
5197 '(3/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
5198 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
5199 '(3/4, 1/2, 3/4)', '(1/4, 1/4, 0)',
5200 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
5201 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
5202 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
5203 '24e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
5204 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
5205 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
5206 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
5207 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
5208 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
5209 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
5210 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
5211 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)')),
5212 '32f': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
5213 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
5214 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
5215 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
5216 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
5217 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
5218 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
5219 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5220 '(x, x, -x)', '(x, x+1/2, -x+1/2)',
5221 '(x+1/2, x, -x+1/2)', '(x+1/2, x+1/2, -x)',
5222 '(-x, -x, -x)', '(-x, -x+1/2, -x+1/2)',
5223 '(-x+1/2, -x, -x+1/2)', '(-x+1/2, -x+1/2, -x)',
5224 '(x, -x, x)', '(x, -x+1/2, x+1/2)',
5225 '(x+1/2, -x, x+1/2)', '(x+1/2, -x+1/2, x)',
5226 '(-x, x, x)', '(-x, x+1/2, x+1/2)',
5227 '(-x+1/2, x, x+1/2)', '(-x+1/2, x+1/2, x)')),
5228 '48g': (2, ('(0, y, y)', '(0, y+1/2, y+1/2)', '(1/2, y, y+1/2)',
5229 '(1/2, y+1/2, y)', '(0, -y, y)',
5230 '(0, -y+1/2, y+1/2)', '(1/2, -y, y+1/2)',
5231 '(1/2, -y+1/2, y)', '(0, y, -y)',
5232 '(0, y+1/2, -y+1/2)', '(1/2, y, -y+1/2)',
5233 '(1/2, y+1/2, -y)', '(0, -y, -y)',
5234 '(0, -y+1/2, -y+1/2)', '(1/2, -y, -y+1/2)',
5235 '(1/2, -y+1/2, -y)', '(y, 0, y)', '(y, 1/2, y+1/2)',
5236 '(y+1/2, 0, y+1/2)', '(y+1/2, 1/2, y)',
5237 '(y, 0, -y)', '(y, 1/2, -y+1/2)',
5238 '(y+1/2, 0, -y+1/2)', '(y+1/2, 1/2, -y)',
5239 '(-y, 0, y)', '(-y, 1/2, y+1/2)',
5240 '(-y+1/2, 0, y+1/2)', '(-y+1/2, 1/2, y)',
5241 '(-y, 0, -y)', '(-y, 1/2, -y+1/2)',
5242 '(-y+1/2, 0, -y+1/2)', '(-y+1/2, 1/2, -y)',
5243 '(y, y, 0)', '(y, y+1/2, 1/2)', '(y+1/2, y, 1/2)',
5244 '(y+1/2, y+1/2, 0)', '(-y, y, 0)',
5245 '(-y, y+1/2, 1/2)', '(-y+1/2, y, 1/2)',
5246 '(-y+1/2, y+1/2, 0)', '(y, -y, 0)',
5247 '(y, -y+1/2, 1/2)', '(y+1/2, -y, 1/2)',
5248 '(y+1/2, -y+1/2, 0)', '(-y, -y, 0)',
5249 '(-y, -y+1/2, 1/2)', '(-y+1/2, -y, 1/2)',
5250 '(-y+1/2, -y+1/2, 0)')),
5251 '48h': (2, ('(1/2, y, y)', '(1/2, y+1/2, y+1/2)',
5252 '(0, y, y+1/2)', '(0, y+1/2, y)', '(1/2, -y, y)',
5253 '(1/2, -y+1/2, y+1/2)', '(0, -y, y+1/2)',
5254 '(0, -y+1/2, y)', '(1/2, y, -y)',
5255 '(1/2, y+1/2, -y+1/2)', '(0, y, -y+1/2)',
5256 '(0, y+1/2, -y)', '(1/2, -y, -y)',
5257 '(1/2, -y+1/2, -y+1/2)', '(0, -y, -y+1/2)',
5258 '(0, -y+1/2, -y)', '(y, 1/2, y)', '(y, 0, y+1/2)',
5259 '(y+1/2, 1/2, y+1/2)', '(y+1/2, 0, y)',
5260 '(y, 1/2, -y)', '(y, 0, -y+1/2)',
5261 '(y+1/2, 1/2, -y+1/2)', '(y+1/2, 0, -y)',
5262 '(-y, 1/2, y)', '(-y, 0, y+1/2)',
5263 '(-y+1/2, 1/2, y+1/2)', '(-y+1/2, 0, y)',
5264 '(-y, 1/2, -y)', '(-y, 0, -y+1/2)',
5265 '(-y+1/2, 1/2, -y+1/2)', '(-y+1/2, 0, -y)',
5266 '(y, y, 1/2)', '(y, y+1/2, 0)', '(y+1/2, y, 0)',
5267 '(y+1/2, y+1/2, 1/2)', '(-y, y, 1/2)',
5268 '(-y, y+1/2, 0)', '(-y+1/2, y, 0)',
5269 '(-y+1/2, y+1/2, 1/2)', '(y, -y, 1/2)',
5270 '(y, -y+1/2, 0)', '(y+1/2, -y, 0)',
5271 '(y+1/2, -y+1/2, 1/2)', '(-y, -y, 1/2)',
5272 '(-y, -y+1/2, 0)', '(-y+1/2, -y, 0)',
5273 '(-y+1/2, -y+1/2, 1/2)')),
5274 '48i': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
5275 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
5276 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
5277 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
5278 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
5279 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
5280 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
5281 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
5282 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
5283 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
5284 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
5285 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)',
5286 '(1/4, x, 3/4)', '(1/4, x+1/2, 1/4)',
5287 '(3/4, x, 1/4)', '(3/4, x+1/2, 3/4)',
5288 '(3/4, -x, 3/4)', '(3/4, -x+1/2, 1/4)',
5289 '(1/4, -x, 1/4)', '(1/4, -x+1/2, 3/4)',
5290 '(x, 1/4, 3/4)', '(x, 3/4, 1/4)',
5291 '(x+1/2, 1/4, 1/4)', '(x+1/2, 3/4, 3/4)',
5292 '(-x, 1/4, 1/4)', '(-x, 3/4, 3/4)',
5293 '(-x+1/2, 1/4, 3/4)', '(-x+1/2, 3/4, 1/4)',
5294 '(1/4, 1/4, -x)', '(1/4, 3/4, -x+1/2)',
5295 '(3/4, 1/4, -x+1/2)', '(3/4, 3/4, -x)',
5296 '(1/4, 3/4, x)', '(1/4, 1/4, x+1/2)',
5297 '(3/4, 3/4, x+1/2)', '(3/4, 1/4, x)')),
5298 '96j': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
5299 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
5300 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
5301 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
5302 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
5303 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
5304 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
5305 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5306 '(z, x, y)', '(z, x+1/2, y+1/2)',
5307 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
5308 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
5309 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
5310 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
5311 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
5312 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
5313 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
5314 '(y, z, x)', '(y, z+1/2, x+1/2)',
5315 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
5316 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
5317 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
5318 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
5319 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
5320 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
5321 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
5322 '(y, x, -z)', '(y, x+1/2, -z+1/2)',
5323 '(y+1/2, x, -z+1/2)', '(y+1/2, x+1/2, -z)',
5324 '(-y, -x, -z)', '(-y, -x+1/2, -z+1/2)',
5325 '(-y+1/2, -x, -z+1/2)', '(-y+1/2, -x+1/2, -z)',
5326 '(y, -x, z)', '(y, -x+1/2, z+1/2)',
5327 '(y+1/2, -x, z+1/2)', '(y+1/2, -x+1/2, z)',
5328 '(-y, x, z)', '(-y, x+1/2, z+1/2)',
5329 '(-y+1/2, x, z+1/2)', '(-y+1/2, x+1/2, z)',
5330 '(x, z, -y)', '(x, z+1/2, -y+1/2)',
5331 '(x+1/2, z, -y+1/2)', '(x+1/2, z+1/2, -y)',
5332 '(-x, z, y)', '(-x, z+1/2, y+1/2)',
5333 '(-x+1/2, z, y+1/2)', '(-x+1/2, z+1/2, y)',
5334 '(-x, -z, -y)', '(-x, -z+1/2, -y+1/2)',
5335 '(-x+1/2, -z, -y+1/2)', '(-x+1/2, -z+1/2, -y)',
5336 '(x, -z, y)', '(x, -z+1/2, y+1/2)',
5337 '(x+1/2, -z, y+1/2)', '(x+1/2, -z+1/2, y)',
5338 '(z, y, -x)', '(z, y+1/2, -x+1/2)',
5339 '(z+1/2, y, -x+1/2)', '(z+1/2, y+1/2, -x)',
5340 '(z, -y, x)', '(z, -y+1/2, x+1/2)',
5341 '(z+1/2, -y, x+1/2)', '(z+1/2, -y+1/2, x)',
5342 '(-z, y, x)', '(-z, y+1/2, x+1/2)',
5343 '(-z+1/2, y, x+1/2)', '(-z+1/2, y+1/2, x)',
5344 '(-z, -y, -x)', '(-z, -y+1/2, -x+1/2)',
5345 '(-z+1/2, -y, -x+1/2)', '(-z+1/2, -y+1/2, -x)'))},
5346 '210': {'8a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
5347 '(1/2, 1/2, 0)', '(3/4, 1/4, 3/4)',
5348 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 1/4)',
5349 '(1/4, 3/4, 3/4)')),
5350 '8b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
5351 '(0, 0, 1/2)', '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
5352 '(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
5353 '16c': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
5354 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
5355 '(7/8, 3/8, 5/8)', '(7/8, 7/8, 1/8)',
5356 '(3/8, 3/8, 1/8)', '(3/8, 7/8, 5/8)',
5357 '(3/8, 5/8, 7/8)', '(3/8, 1/8, 3/8)',
5358 '(7/8, 5/8, 3/8)', '(7/8, 1/8, 7/8)',
5359 '(5/8, 7/8, 3/8)', '(5/8, 3/8, 7/8)',
5360 '(1/8, 7/8, 7/8)', '(1/8, 3/8, 3/8)')),
5361 '16d': (0, ('(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
5362 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
5363 '(3/8, 7/8, 1/8)', '(3/8, 3/8, 5/8)',
5364 '(7/8, 7/8, 5/8)', '(7/8, 3/8, 1/8)',
5365 '(7/8, 1/8, 3/8)', '(7/8, 5/8, 7/8)',
5366 '(3/8, 1/8, 7/8)', '(3/8, 5/8, 3/8)',
5367 '(1/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)',
5368 '(5/8, 3/8, 3/8)', '(5/8, 7/8, 7/8)')),
5369 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
5370 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
5371 '(-x, -x+1/2, x+1/2)', '(-x, -x, x)',
5372 '(-x+1/2, -x+1/2, x)', '(-x+1/2, -x, x+1/2)',
5373 '(-x+1/2, x+1/2, -x)', '(-x+1/2, x, -x+1/2)',
5374 '(-x, x+1/2, -x+1/2)', '(-x, x, -x)',
5375 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5376 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
5377 '(x+3/4, x+1/4, -x+3/4)', '(x+3/4, x+3/4, -x+1/4)',
5378 '(x+1/4, x+1/4, -x+1/4)', '(x+1/4, x+3/4, -x+3/4)',
5379 '(-x+1/4, -x+1/4, -x+1/4)',
5380 '(-x+1/4, -x+3/4, -x+3/4)',
5381 '(-x+3/4, -x+1/4, -x+3/4)',
5382 '(-x+3/4, -x+3/4, -x+1/4)',
5383 '(x+1/4, -x+3/4, x+3/4)', '(x+1/4, -x+1/4, x+1/4)',
5384 '(x+3/4, -x+3/4, x+1/4)', '(x+3/4, -x+1/4, x+3/4)',
5385 '(-x+3/4, x+3/4, x+1/4)', '(-x+3/4, x+1/4, x+3/4)',
5386 '(-x+1/4, x+3/4, x+3/4)', '(-x+1/4, x+1/4, x+1/4)'
5387 )),
5388 '48f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
5389 '(x+1/2, 1/2, 0)', '(-x, 1/2, 1/2)', '(-x, 0, 0)',
5390 '(-x+1/2, 1/2, 0)', '(-x+1/2, 0, 1/2)', '(0, x, 0)',
5391 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
5392 '(1/2, x+1/2, 0)', '(1/2, -x, 1/2)',
5393 '(1/2, -x+1/2, 0)', '(0, -x, 0)',
5394 '(0, -x+1/2, 1/2)', '(0, 0, x)', '(0, 1/2, x+1/2)',
5395 '(1/2, 0, x+1/2)', '(1/2, 1/2, x)',
5396 '(1/2, 1/2, -x)', '(1/2, 0, -x+1/2)',
5397 '(0, 1/2, -x+1/2)', '(0, 0, -x)',
5398 '(3/4, x+1/4, 3/4)', '(3/4, x+3/4, 1/4)',
5399 '(1/4, x+1/4, 1/4)', '(1/4, x+3/4, 3/4)',
5400 '(1/4, -x+1/4, 1/4)', '(1/4, -x+3/4, 3/4)',
5401 '(3/4, -x+1/4, 3/4)', '(3/4, -x+3/4, 1/4)',
5402 '(x+3/4, 1/4, 3/4)', '(x+3/4, 3/4, 1/4)',
5403 '(x+1/4, 1/4, 1/4)', '(x+1/4, 3/4, 3/4)',
5404 '(-x+3/4, 3/4, 1/4)', '(-x+3/4, 1/4, 3/4)',
5405 '(-x+1/4, 3/4, 3/4)', '(-x+1/4, 1/4, 1/4)',
5406 '(3/4, 1/4, -x+3/4)', '(3/4, 3/4, -x+1/4)',
5407 '(1/4, 1/4, -x+1/4)', '(1/4, 3/4, -x+3/4)',
5408 '(1/4, 3/4, x+3/4)', '(1/4, 1/4, x+1/4)',
5409 '(3/4, 3/4, x+1/4)', '(3/4, 1/4, x+3/4)')),
5410 '48g': (2, ('(1/8, y, -y+1/4)', '(1/8, y+1/2, -y+3/4)',
5411 '(5/8, y, -y+3/4)', '(5/8, y+1/2, -y+1/4)',
5412 '(7/8, -y+1/2, -y+3/4)', '(7/8, -y, -y+1/4)',
5413 '(3/8, -y+1/2, -y+1/4)', '(3/8, -y, -y+3/4)',
5414 '(3/8, y+1/2, y+3/4)', '(3/8, y, y+1/4)',
5415 '(7/8, y+1/2, y+1/4)', '(7/8, y, y+3/4)',
5416 '(5/8, -y, y+1/4)', '(5/8, -y+1/2, y+3/4)',
5417 '(1/8, -y, y+3/4)', '(1/8, -y+1/2, y+1/4)',
5418 '(-y+1/4, 1/8, y)', '(-y+1/4, 5/8, y+1/2)',
5419 '(-y+3/4, 1/8, y+1/2)', '(-y+3/4, 5/8, y)',
5420 '(-y+3/4, 7/8, -y+1/2)', '(-y+3/4, 3/8, -y)',
5421 '(-y+1/4, 7/8, -y)', '(-y+1/4, 3/8, -y+1/2)',
5422 '(y+3/4, 3/8, y+1/2)', '(y+3/4, 7/8, y)',
5423 '(y+1/4, 3/8, y)', '(y+1/4, 7/8, y+1/2)',
5424 '(y+1/4, 5/8, -y)', '(y+1/4, 1/8, -y+1/2)',
5425 '(y+3/4, 5/8, -y+1/2)', '(y+3/4, 1/8, -y)',
5426 '(y, -y+1/4, 1/8)', '(y, -y+3/4, 5/8)',
5427 '(y+1/2, -y+1/4, 5/8)', '(y+1/2, -y+3/4, 1/8)',
5428 '(-y+1/2, -y+3/4, 7/8)', '(-y+1/2, -y+1/4, 3/8)',
5429 '(-y, -y+3/4, 3/8)', '(-y, -y+1/4, 7/8)',
5430 '(y+1/2, y+3/4, 3/8)', '(y+1/2, y+1/4, 7/8)',
5431 '(y, y+3/4, 7/8)', '(y, y+1/4, 3/8)',
5432 '(-y, y+1/4, 5/8)', '(-y, y+3/4, 1/8)',
5433 '(-y+1/2, y+1/4, 1/8)', '(-y+1/2, y+3/4, 5/8)')),
5434 '96h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
5435 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
5436 '(-x, -y+1/2, z+1/2)', '(-x, -y, z)',
5437 '(-x+1/2, -y+1/2, z)', '(-x+1/2, -y, z+1/2)',
5438 '(-x+1/2, y+1/2, -z)', '(-x+1/2, y, -z+1/2)',
5439 '(-x, y+1/2, -z+1/2)', '(-x, y, -z)',
5440 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5441 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)', '(z, x, y)',
5442 '(z, x+1/2, y+1/2)', '(z+1/2, x, y+1/2)',
5443 '(z+1/2, x+1/2, y)', '(z+1/2, -x, -y+1/2)',
5444 '(z+1/2, -x+1/2, -y)', '(z, -x, -y)',
5445 '(z, -x+1/2, -y+1/2)', '(-z, -x+1/2, y+1/2)',
5446 '(-z, -x, y)', '(-z+1/2, -x+1/2, y)',
5447 '(-z+1/2, -x, y+1/2)', '(-z+1/2, x+1/2, -y)',
5448 '(-z+1/2, x, -y+1/2)', '(-z, x+1/2, -y+1/2)',
5449 '(-z, x, -y)', '(y, z, x)', '(y, z+1/2, x+1/2)',
5450 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
5451 '(-y+1/2, z+1/2, -x)', '(-y+1/2, z, -x+1/2)',
5452 '(-y, z+1/2, -x+1/2)', '(-y, z, -x)',
5453 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
5454 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
5455 '(-y, -z+1/2, x+1/2)', '(-y, -z, x)',
5456 '(-y+1/2, -z+1/2, x)', '(-y+1/2, -z, x+1/2)',
5457 '(y+3/4, x+1/4, -z+3/4)', '(y+3/4, x+3/4, -z+1/4)',
5458 '(y+1/4, x+1/4, -z+1/4)', '(y+1/4, x+3/4, -z+3/4)',
5459 '(-y+1/4, -x+1/4, -z+1/4)',
5460 '(-y+1/4, -x+3/4, -z+3/4)',
5461 '(-y+3/4, -x+1/4, -z+3/4)',
5462 '(-y+3/4, -x+3/4, -z+1/4)',
5463 '(y+1/4, -x+3/4, z+3/4)', '(y+1/4, -x+1/4, z+1/4)',
5464 '(y+3/4, -x+3/4, z+1/4)', '(y+3/4, -x+1/4, z+3/4)',
5465 '(-y+3/4, x+3/4, z+1/4)', '(-y+3/4, x+1/4, z+3/4)',
5466 '(-y+1/4, x+3/4, z+3/4)', '(-y+1/4, x+1/4, z+1/4)',
5467 '(x+3/4, z+1/4, -y+3/4)', '(x+3/4, z+3/4, -y+1/4)',
5468 '(x+1/4, z+1/4, -y+1/4)', '(x+1/4, z+3/4, -y+3/4)',
5469 '(-x+3/4, z+3/4, y+1/4)', '(-x+3/4, z+1/4, y+3/4)',
5470 '(-x+1/4, z+3/4, y+3/4)', '(-x+1/4, z+1/4, y+1/4)',
5471 '(-x+1/4, -z+1/4, -y+1/4)',
5472 '(-x+1/4, -z+3/4, -y+3/4)',
5473 '(-x+3/4, -z+1/4, -y+3/4)',
5474 '(-x+3/4, -z+3/4, -y+1/4)',
5475 '(x+1/4, -z+3/4, y+3/4)', '(x+1/4, -z+1/4, y+1/4)',
5476 '(x+3/4, -z+3/4, y+1/4)', '(x+3/4, -z+1/4, y+3/4)',
5477 '(z+3/4, y+1/4, -x+3/4)', '(z+3/4, y+3/4, -x+1/4)',
5478 '(z+1/4, y+1/4, -x+1/4)', '(z+1/4, y+3/4, -x+3/4)',
5479 '(z+1/4, -y+3/4, x+3/4)', '(z+1/4, -y+1/4, x+1/4)',
5480 '(z+3/4, -y+3/4, x+1/4)', '(z+3/4, -y+1/4, x+3/4)',
5481 '(-z+3/4, y+3/4, x+1/4)', '(-z+3/4, y+1/4, x+3/4)',
5482 '(-z+1/4, y+3/4, x+3/4)', '(-z+1/4, y+1/4, x+1/4)',
5483 '(-z+1/4, -y+1/4, -x+1/4)',
5484 '(-z+1/4, -y+3/4, -x+3/4)',
5485 '(-z+3/4, -y+1/4, -x+3/4)',
5486 '(-z+3/4, -y+3/4, -x+1/4)'))},
5487 '211': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
5488 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
5489 '(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
5490 '8c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
5491 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
5492 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
5493 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
5494 '12d': (0, ('(1/4, 1/2, 0)', '(3/4, 0, 1/2)', '(3/4, 1/2, 0)',
5495 '(1/4, 0, 1/2)', '(0, 1/4, 1/2)', '(1/2, 3/4, 0)',
5496 '(0, 3/4, 1/2)', '(1/2, 1/4, 0)', '(1/2, 0, 1/4)',
5497 '(0, 1/2, 3/4)', '(1/2, 0, 3/4)', '(0, 1/2, 1/4)')),
5498 '12e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
5499 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
5500 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
5501 '(1/2, -x+1/2, 1/2)', '(0, 0, x)',
5502 '(1/2, 1/2, x+1/2)', '(0, 0, -x)',
5503 '(1/2, 1/2, -x+1/2)')),
5504 '16f': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)', '(-x, -x, x)',
5505 '(-x+1/2, -x+1/2, x+1/2)', '(-x, x, -x)',
5506 '(-x+1/2, x+1/2, -x+1/2)', '(x, -x, -x)',
5507 '(x+1/2, -x+1/2, -x+1/2)', '(x, x, -x)',
5508 '(x+1/2, x+1/2, -x+1/2)', '(-x, -x, -x)',
5509 '(-x+1/2, -x+1/2, -x+1/2)', '(x, -x, x)',
5510 '(x+1/2, -x+1/2, x+1/2)', '(-x, x, x)',
5511 '(-x+1/2, x+1/2, x+1/2)')),
5512 '24g': (1, ('(x, 1/2, 0)', '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
5513 '(-x+1/2, 0, 1/2)', '(0, x, 1/2)',
5514 '(1/2, x+1/2, 0)', '(0, -x, 1/2)',
5515 '(1/2, -x+1/2, 0)', '(1/2, 0, x)',
5516 '(0, 1/2, x+1/2)', '(1/2, 0, -x)',
5517 '(0, 1/2, -x+1/2)', '(1/2, x, 0)',
5518 '(0, x+1/2, 1/2)', '(1/2, -x, 0)',
5519 '(0, -x+1/2, 1/2)', '(x, 0, 1/2)',
5520 '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
5521 '(-x+1/2, 1/2, 0)', '(0, 1/2, -x)',
5522 '(1/2, 0, -x+1/2)', '(0, 1/2, x)',
5523 '(1/2, 0, x+1/2)')),
5524 '24h': (2, ('(0, y, y)', '(1/2, y+1/2, y+1/2)', '(0, -y, y)',
5525 '(1/2, -y+1/2, y+1/2)', '(0, y, -y)',
5526 '(1/2, y+1/2, -y+1/2)', '(0, -y, -y)',
5527 '(1/2, -y+1/2, -y+1/2)', '(y, 0, y)',
5528 '(y+1/2, 1/2, y+1/2)', '(y, 0, -y)',
5529 '(y+1/2, 1/2, -y+1/2)', '(-y, 0, y)',
5530 '(-y+1/2, 1/2, y+1/2)', '(-y, 0, -y)',
5531 '(-y+1/2, 1/2, -y+1/2)', '(y, y, 0)',
5532 '(y+1/2, y+1/2, 1/2)', '(-y, y, 0)',
5533 '(-y+1/2, y+1/2, 1/2)', '(y, -y, 0)',
5534 '(y+1/2, -y+1/2, 1/2)', '(-y, -y, 0)',
5535 '(-y+1/2, -y+1/2, 1/2)')),
5536 '24i': (2, ('(1/4, y, -y+1/2)', '(3/4, y+1/2, -y)',
5537 '(3/4, -y, -y+1/2)', '(1/4, -y+1/2, -y)',
5538 '(3/4, y, y+1/2)', '(1/4, y+1/2, y)',
5539 '(1/4, -y, y+1/2)', '(3/4, -y+1/2, y)',
5540 '(-y+1/2, 1/4, y)', '(-y, 3/4, y+1/2)',
5541 '(-y+1/2, 3/4, -y)', '(-y, 1/4, -y+1/2)',
5542 '(y+1/2, 3/4, y)', '(y, 1/4, y+1/2)',
5543 '(y+1/2, 1/4, -y)', '(y, 3/4, -y+1/2)',
5544 '(y, -y+1/2, 1/4)', '(y+1/2, -y, 3/4)',
5545 '(-y, -y+1/2, 3/4)', '(-y+1/2, -y, 1/4)',
5546 '(y, y+1/2, 3/4)', '(y+1/2, y, 1/4)',
5547 '(-y, y+1/2, 1/4)', '(-y+1/2, y, 3/4)')),
5548 '48j': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
5549 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
5550 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
5551 '(x+1/2, -y+1/2, -z+1/2)', '(z, x, y)',
5552 '(z+1/2, x+1/2, y+1/2)', '(z, -x, -y)',
5553 '(z+1/2, -x+1/2, -y+1/2)', '(-z, -x, y)',
5554 '(-z+1/2, -x+1/2, y+1/2)', '(-z, x, -y)',
5555 '(-z+1/2, x+1/2, -y+1/2)', '(y, z, x)',
5556 '(y+1/2, z+1/2, x+1/2)', '(-y, z, -x)',
5557 '(-y+1/2, z+1/2, -x+1/2)', '(y, -z, -x)',
5558 '(y+1/2, -z+1/2, -x+1/2)', '(-y, -z, x)',
5559 '(-y+1/2, -z+1/2, x+1/2)', '(y, x, -z)',
5560 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z)',
5561 '(-y+1/2, -x+1/2, -z+1/2)', '(y, -x, z)',
5562 '(y+1/2, -x+1/2, z+1/2)', '(-y, x, z)',
5563 '(-y+1/2, x+1/2, z+1/2)', '(x, z, -y)',
5564 '(x+1/2, z+1/2, -y+1/2)', '(-x, z, y)',
5565 '(-x+1/2, z+1/2, y+1/2)', '(-x, -z, -y)',
5566 '(-x+1/2, -z+1/2, -y+1/2)', '(x, -z, y)',
5567 '(x+1/2, -z+1/2, y+1/2)', '(z, y, -x)',
5568 '(z+1/2, y+1/2, -x+1/2)', '(z, -y, x)',
5569 '(z+1/2, -y+1/2, x+1/2)', '(-z, y, x)',
5570 '(-z+1/2, y+1/2, x+1/2)', '(-z, -y, -x)',
5571 '(-z+1/2, -y+1/2, -x+1/2)'))},
5572 '212': {'4a': (0, ('(1/8, 1/8, 1/8)', '(3/8, 7/8, 5/8)',
5573 '(7/8, 5/8, 3/8)', '(5/8, 3/8, 7/8)')),
5574 '4b': (0, ('(5/8, 5/8, 5/8)', '(7/8, 3/8, 1/8)',
5575 '(3/8, 1/8, 7/8)', '(1/8, 7/8, 3/8)')),
5576 '8c': (1, ('(x, x, x)', '(-x+1/2, -x, x+1/2)',
5577 '(-x, x+1/2, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5578 '(x+1/4, x+3/4, -x+3/4)', '(-x+1/4, -x+1/4, -x+1/4)',
5579 '(x+3/4, -x+3/4, x+1/4)', '(-x+3/4, x+1/4, x+3/4)')),
5580 '12d': (2, ('(1/8, y, -y+1/4)', '(3/8, -y, -y+3/4)',
5581 '(7/8, y+1/2, y+1/4)', '(5/8, -y+1/2, y+3/4)',
5582 '(-y+1/4, 1/8, y)', '(-y+3/4, 3/8, -y)',
5583 '(y+1/4, 7/8, y+1/2)', '(y+3/4, 5/8, -y+1/2)',
5584 '(y, -y+1/4, 1/8)', '(-y, -y+3/4, 3/8)',
5585 '(y+1/2, y+1/4, 7/8)', '(-y+1/2, y+3/4, 5/8)')),
5586 '24e': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
5587 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5588 '(z, x, y)', '(z+1/2, -x+1/2, -y)',
5589 '(-z+1/2, -x, y+1/2)', '(-z, x+1/2, -y+1/2)',
5590 '(y, z, x)', '(-y, z+1/2, -x+1/2)',
5591 '(y+1/2, -z+1/2, -x)', '(-y+1/2, -z, x+1/2)',
5592 '(y+1/4, x+3/4, -z+3/4)',
5593 '(-y+1/4, -x+1/4, -z+1/4)',
5594 '(y+3/4, -x+3/4, z+1/4)', '(-y+3/4, x+1/4, z+3/4)',
5595 '(x+1/4, z+3/4, -y+3/4)', '(-x+3/4, z+1/4, y+3/4)',
5596 '(-x+1/4, -z+1/4, -y+1/4)',
5597 '(x+3/4, -z+3/4, y+1/4)', '(z+1/4, y+3/4, -x+3/4)',
5598 '(z+3/4, -y+3/4, x+1/4)', '(-z+3/4, y+1/4, x+3/4)',
5599 '(-z+1/4, -y+1/4, -x+1/4)'))},
5600 '213': {'4a': (0, ('(3/8, 3/8, 3/8)', '(1/8, 5/8, 7/8)',
5601 '(5/8, 7/8, 1/8)', '(7/8, 1/8, 5/8)')),
5602 '4b': (0, ('(7/8, 7/8, 7/8)', '(5/8, 1/8, 3/8)',
5603 '(1/8, 3/8, 5/8)', '(3/8, 5/8, 1/8)')),
5604 '8c': (1, ('(x, x, x)', '(-x+1/2, -x, x+1/2)',
5605 '(-x, x+1/2, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5606 '(x+3/4, x+1/4, -x+1/4)', '(-x+3/4, -x+3/4, -x+3/4)',
5607 '(x+1/4, -x+1/4, x+3/4)', '(-x+1/4, x+3/4, x+1/4)')),
5608 '12d': (2, ('(1/8, y, y+1/4)', '(3/8, -y, y+3/4)',
5609 '(7/8, y+1/2, -y+1/4)', '(5/8, -y+1/2, -y+3/4)',
5610 '(y+1/4, 1/8, y)', '(y+3/4, 3/8, -y)',
5611 '(-y+1/4, 7/8, y+1/2)', '(-y+3/4, 5/8, -y+1/2)',
5612 '(y, y+1/4, 1/8)', '(-y, y+3/4, 3/8)',
5613 '(y+1/2, -y+1/4, 7/8)', '(-y+1/2, -y+3/4, 5/8)')),
5614 '24e': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
5615 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5616 '(z, x, y)', '(z+1/2, -x+1/2, -y)',
5617 '(-z+1/2, -x, y+1/2)', '(-z, x+1/2, -y+1/2)',
5618 '(y, z, x)', '(-y, z+1/2, -x+1/2)',
5619 '(y+1/2, -z+1/2, -x)', '(-y+1/2, -z, x+1/2)',
5620 '(y+3/4, x+1/4, -z+1/4)',
5621 '(-y+3/4, -x+3/4, -z+3/4)',
5622 '(y+1/4, -x+1/4, z+3/4)', '(-y+1/4, x+3/4, z+1/4)',
5623 '(x+3/4, z+1/4, -y+1/4)', '(-x+1/4, z+3/4, y+1/4)',
5624 '(-x+3/4, -z+3/4, -y+3/4)',
5625 '(x+1/4, -z+1/4, y+3/4)', '(z+3/4, y+1/4, -x+1/4)',
5626 '(z+1/4, -y+1/4, x+3/4)', '(-z+1/4, y+3/4, x+1/4)',
5627 '(-z+3/4, -y+3/4, -x+3/4)'))},
5628 '214': {'8a': (0, ('(1/8, 1/8, 1/8)', '(5/8, 5/8, 5/8)',
5629 '(3/8, 7/8, 5/8)', '(7/8, 3/8, 1/8)',
5630 '(7/8, 5/8, 3/8)', '(3/8, 1/8, 7/8)',
5631 '(5/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)')),
5632 '8b': (0, ('(7/8, 7/8, 7/8)', '(3/8, 3/8, 3/8)',
5633 '(5/8, 1/8, 3/8)', '(1/8, 5/8, 7/8)',
5634 '(1/8, 3/8, 5/8)', '(5/8, 7/8, 1/8)',
5635 '(3/8, 5/8, 1/8)', '(7/8, 1/8, 5/8)')),
5636 '12c': (0, ('(1/8, 0, 1/4)', '(5/8, 1/2, 3/4)', '(3/8, 0, 3/4)',
5637 '(7/8, 1/2, 1/4)', '(1/4, 1/8, 0)',
5638 '(3/4, 5/8, 1/2)', '(3/4, 3/8, 0)',
5639 '(1/4, 7/8, 1/2)', '(0, 1/4, 1/8)',
5640 '(1/2, 3/4, 5/8)', '(0, 3/4, 3/8)',
5641 '(1/2, 1/4, 7/8)')),
5642 '12d': (0, ('(5/8, 0, 1/4)', '(1/8, 1/2, 3/4)', '(7/8, 0, 3/4)',
5643 '(3/8, 1/2, 1/4)', '(1/4, 5/8, 0)',
5644 '(3/4, 1/8, 1/2)', '(3/4, 7/8, 0)',
5645 '(1/4, 3/8, 1/2)', '(0, 1/4, 5/8)',
5646 '(1/2, 3/4, 1/8)', '(0, 3/4, 7/8)',
5647 '(1/2, 1/4, 3/8)')),
5648 '16e': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)',
5649 '(-x+1/2, -x, x+1/2)', '(-x, -x+1/2, x)',
5650 '(-x, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
5651 '(x+1/2, -x+1/2, -x)', '(x, -x, -x+1/2)',
5652 '(x+3/4, x+1/4, -x+1/4)', '(x+1/4, x+3/4, -x+3/4)',
5653 '(-x+3/4, -x+3/4, -x+3/4)',
5654 '(-x+1/4, -x+1/4, -x+1/4)',
5655 '(x+1/4, -x+1/4, x+3/4)', '(x+3/4, -x+3/4, x+1/4)',
5656 '(-x+1/4, x+3/4, x+1/4)', '(-x+3/4, x+1/4, x+3/4)'
5657 )),
5658 '24f': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
5659 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(1/4, x, 0)',
5660 '(3/4, x+1/2, 1/2)', '(3/4, -x+1/2, 0)',
5661 '(1/4, -x, 1/2)', '(0, 1/4, x)',
5662 '(1/2, 3/4, x+1/2)', '(0, 3/4, -x+1/2)',
5663 '(1/2, 1/4, -x)', '(3/4, x+1/4, 0)',
5664 '(1/4, x+3/4, 1/2)', '(3/4, -x+3/4, 1/2)',
5665 '(1/4, -x+1/4, 0)', '(x+3/4, 1/2, 1/4)',
5666 '(x+1/4, 0, 3/4)', '(-x+1/4, 0, 1/4)',
5667 '(-x+3/4, 1/2, 3/4)', '(0, 1/4, -x+1/4)',
5668 '(1/2, 3/4, -x+3/4)', '(1/2, 1/4, x+3/4)',
5669 '(0, 3/4, x+1/4)')),
5670 '24g': (2, ('(1/8, y, y+1/4)', '(5/8, y+1/2, y+3/4)',
5671 '(3/8, -y, y+3/4)', '(7/8, -y+1/2, y+1/4)',
5672 '(7/8, y+1/2, -y+1/4)', '(3/8, y, -y+3/4)',
5673 '(5/8, -y+1/2, -y+3/4)', '(1/8, -y, -y+1/4)',
5674 '(y+1/4, 1/8, y)', '(y+3/4, 5/8, y+1/2)',
5675 '(y+3/4, 3/8, -y)', '(y+1/4, 7/8, -y+1/2)',
5676 '(-y+1/4, 7/8, y+1/2)', '(-y+3/4, 3/8, y)',
5677 '(-y+3/4, 5/8, -y+1/2)', '(-y+1/4, 1/8, -y)',
5678 '(y, y+1/4, 1/8)', '(y+1/2, y+3/4, 5/8)',
5679 '(-y, y+3/4, 3/8)', '(-y+1/2, y+1/4, 7/8)',
5680 '(y+1/2, -y+1/4, 7/8)', '(y, -y+3/4, 3/8)',
5681 '(-y+1/2, -y+3/4, 5/8)', '(-y, -y+1/4, 1/8)')),
5682 '24h': (2, ('(1/8, y, -y+1/4)', '(5/8, y+1/2, -y+3/4)',
5683 '(3/8, -y, -y+3/4)', '(7/8, -y+1/2, -y+1/4)',
5684 '(7/8, y+1/2, y+1/4)', '(3/8, y, y+3/4)',
5685 '(5/8, -y+1/2, y+3/4)', '(1/8, -y, y+1/4)',
5686 '(-y+1/4, 1/8, y)', '(-y+3/4, 5/8, y+1/2)',
5687 '(-y+3/4, 3/8, -y)', '(-y+1/4, 7/8, -y+1/2)',
5688 '(y+1/4, 7/8, y+1/2)', '(y+3/4, 3/8, y)',
5689 '(y+3/4, 5/8, -y+1/2)', '(y+1/4, 1/8, -y)',
5690 '(y, -y+1/4, 1/8)', '(y+1/2, -y+3/4, 5/8)',
5691 '(-y, -y+3/4, 3/8)', '(-y+1/2, -y+1/4, 7/8)',
5692 '(y+1/2, y+1/4, 7/8)', '(y, y+3/4, 3/8)',
5693 '(-y+1/2, y+3/4, 5/8)', '(-y, y+1/4, 1/8)')),
5694 '48i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
5695 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
5696 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
5697 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
5698 '(z, x, y)', '(z+1/2, x+1/2, y+1/2)',
5699 '(z+1/2, -x+1/2, -y)', '(z, -x, -y+1/2)',
5700 '(-z+1/2, -x, y+1/2)', '(-z, -x+1/2, y)',
5701 '(-z, x+1/2, -y+1/2)', '(-z+1/2, x, -y)',
5702 '(y, z, x)', '(y+1/2, z+1/2, x+1/2)',
5703 '(-y, z+1/2, -x+1/2)', '(-y+1/2, z, -x)',
5704 '(y+1/2, -z+1/2, -x)', '(y, -z, -x+1/2)',
5705 '(-y+1/2, -z, x+1/2)', '(-y, -z+1/2, x)',
5706 '(y+3/4, x+1/4, -z+1/4)', '(y+1/4, x+3/4, -z+3/4)',
5707 '(-y+3/4, -x+3/4, -z+3/4)',
5708 '(-y+1/4, -x+1/4, -z+1/4)',
5709 '(y+1/4, -x+1/4, z+3/4)', '(y+3/4, -x+3/4, z+1/4)',
5710 '(-y+1/4, x+3/4, z+1/4)', '(-y+3/4, x+1/4, z+3/4)',
5711 '(x+3/4, z+1/4, -y+1/4)', '(x+1/4, z+3/4, -y+3/4)',
5712 '(-x+1/4, z+3/4, y+1/4)', '(-x+3/4, z+1/4, y+3/4)',
5713 '(-x+3/4, -z+3/4, -y+3/4)',
5714 '(-x+1/4, -z+1/4, -y+1/4)',
5715 '(x+1/4, -z+1/4, y+3/4)', '(x+3/4, -z+3/4, y+1/4)',
5716 '(z+3/4, y+1/4, -x+1/4)', '(z+1/4, y+3/4, -x+3/4)',
5717 '(z+1/4, -y+1/4, x+3/4)', '(z+3/4, -y+3/4, x+1/4)',
5718 '(-z+1/4, y+3/4, x+1/4)', '(-z+3/4, y+1/4, x+3/4)',
5719 '(-z+3/4, -y+3/4, -x+3/4)',
5720 '(-z+1/4, -y+1/4, -x+1/4)'))},
5721 '215': {'1a': (0, ('(0, 0, 0)', )),
5722 '1b': (0, ('(1/2, 1/2, 1/2)', )),
5723 '3c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
5724 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
5725 '4e': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
5726 '(x, -x, -x)')),
5727 '6f': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)',
5728 '(0, 0, x)', '(0, 0, -x)')),
5729 '6g': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
5730 '(1/2, -x, 1/2)', '(1/2, 1/2, x)', '(1/2, 1/2, -x)'
5731 )),
5732 '12h': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
5733 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
5734 '(1/2, x, 0)', '(1/2, -x, 0)', '(x, 0, 1/2)',
5735 '(-x, 0, 1/2)', '(0, 1/2, x)', '(0, 1/2, -x)')),
5736 '12i': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, -z)',
5737 '(x, -x, -z)', '(z, x, x)', '(z, -x, -x)',
5738 '(-z, -x, x)', '(-z, x, -x)', '(x, z, x)',
5739 '(-x, z, -x)', '(x, -z, -x)', '(-x, -z, x)')),
5740 '24j': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
5741 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
5742 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
5743 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
5744 '(y, x, z)', '(-y, -x, z)', '(y, -x, -z)',
5745 '(-y, x, -z)', '(x, z, y)', '(-x, z, -y)',
5746 '(-x, -z, y)', '(x, -z, -y)', '(z, y, x)',
5747 '(z, -y, -x)', '(-z, y, -x)', '(-z, -y, x)'))},
5748 '216': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
5749 '(1/2, 1/2, 0)')),
5750 '4b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
5751 '(0, 0, 1/2)')),
5752 '4c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
5753 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
5754 '4d': (0, ('(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
5755 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
5756 '16e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
5757 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
5758 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
5759 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
5760 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
5761 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
5762 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
5763 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)')),
5764 '24f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
5765 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
5766 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
5767 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
5768 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
5769 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
5770 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
5771 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
5772 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)')),
5773 '24g': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
5774 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
5775 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
5776 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
5777 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
5778 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
5779 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
5780 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
5781 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
5782 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
5783 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
5784 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)')),
5785 '48h': (5, ('(x, x, z)', '(x, x+1/2, z+1/2)',
5786 '(x+1/2, x, z+1/2)', '(x+1/2, x+1/2, z)',
5787 '(-x, -x, z)', '(-x, -x+1/2, z+1/2)',
5788 '(-x+1/2, -x, z+1/2)', '(-x+1/2, -x+1/2, z)',
5789 '(-x, x, -z)', '(-x, x+1/2, -z+1/2)',
5790 '(-x+1/2, x, -z+1/2)', '(-x+1/2, x+1/2, -z)',
5791 '(x, -x, -z)', '(x, -x+1/2, -z+1/2)',
5792 '(x+1/2, -x, -z+1/2)', '(x+1/2, -x+1/2, -z)',
5793 '(z, x, x)', '(z, x+1/2, x+1/2)',
5794 '(z+1/2, x, x+1/2)', '(z+1/2, x+1/2, x)',
5795 '(z, -x, -x)', '(z, -x+1/2, -x+1/2)',
5796 '(z+1/2, -x, -x+1/2)', '(z+1/2, -x+1/2, -x)',
5797 '(-z, -x, x)', '(-z, -x+1/2, x+1/2)',
5798 '(-z+1/2, -x, x+1/2)', '(-z+1/2, -x+1/2, x)',
5799 '(-z, x, -x)', '(-z, x+1/2, -x+1/2)',
5800 '(-z+1/2, x, -x+1/2)', '(-z+1/2, x+1/2, -x)',
5801 '(x, z, x)', '(x, z+1/2, x+1/2)',
5802 '(x+1/2, z, x+1/2)', '(x+1/2, z+1/2, x)',
5803 '(-x, z, -x)', '(-x, z+1/2, -x+1/2)',
5804 '(-x+1/2, z, -x+1/2)', '(-x+1/2, z+1/2, -x)',
5805 '(x, -z, -x)', '(x, -z+1/2, -x+1/2)',
5806 '(x+1/2, -z, -x+1/2)', '(x+1/2, -z+1/2, -x)',
5807 '(-x, -z, x)', '(-x, -z+1/2, x+1/2)',
5808 '(-x+1/2, -z, x+1/2)', '(-x+1/2, -z+1/2, x)')),
5809 '96i': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
5810 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
5811 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
5812 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
5813 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
5814 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
5815 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
5816 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5817 '(z, x, y)', '(z, x+1/2, y+1/2)',
5818 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
5819 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
5820 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
5821 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
5822 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
5823 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
5824 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
5825 '(y, z, x)', '(y, z+1/2, x+1/2)',
5826 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
5827 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
5828 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
5829 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
5830 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
5831 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
5832 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
5833 '(y, x, z)', '(y, x+1/2, z+1/2)',
5834 '(y+1/2, x, z+1/2)', '(y+1/2, x+1/2, z)',
5835 '(-y, -x, z)', '(-y, -x+1/2, z+1/2)',
5836 '(-y+1/2, -x, z+1/2)', '(-y+1/2, -x+1/2, z)',
5837 '(y, -x, -z)', '(y, -x+1/2, -z+1/2)',
5838 '(y+1/2, -x, -z+1/2)', '(y+1/2, -x+1/2, -z)',
5839 '(-y, x, -z)', '(-y, x+1/2, -z+1/2)',
5840 '(-y+1/2, x, -z+1/2)', '(-y+1/2, x+1/2, -z)',
5841 '(x, z, y)', '(x, z+1/2, y+1/2)',
5842 '(x+1/2, z, y+1/2)', '(x+1/2, z+1/2, y)',
5843 '(-x, z, -y)', '(-x, z+1/2, -y+1/2)',
5844 '(-x+1/2, z, -y+1/2)', '(-x+1/2, z+1/2, -y)',
5845 '(-x, -z, y)', '(-x, -z+1/2, y+1/2)',
5846 '(-x+1/2, -z, y+1/2)', '(-x+1/2, -z+1/2, y)',
5847 '(x, -z, -y)', '(x, -z+1/2, -y+1/2)',
5848 '(x+1/2, -z, -y+1/2)', '(x+1/2, -z+1/2, -y)',
5849 '(z, y, x)', '(z, y+1/2, x+1/2)',
5850 '(z+1/2, y, x+1/2)', '(z+1/2, y+1/2, x)',
5851 '(z, -y, -x)', '(z, -y+1/2, -x+1/2)',
5852 '(z+1/2, -y, -x+1/2)', '(z+1/2, -y+1/2, -x)',
5853 '(-z, y, -x)', '(-z, y+1/2, -x+1/2)',
5854 '(-z+1/2, y, -x+1/2)', '(-z+1/2, y+1/2, -x)',
5855 '(-z, -y, x)', '(-z, -y+1/2, x+1/2)',
5856 '(-z+1/2, -y, x+1/2)', '(-z+1/2, -y+1/2, x)'))},
5857 '217': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
5858 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
5859 '(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
5860 '8c': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)', '(-x, -x, x)',
5861 '(-x+1/2, -x+1/2, x+1/2)', '(-x, x, -x)',
5862 '(-x+1/2, x+1/2, -x+1/2)', '(x, -x, -x)',
5863 '(x+1/2, -x+1/2, -x+1/2)')),
5864 '12d': (0, ('(1/4, 1/2, 0)', '(3/4, 0, 1/2)', '(3/4, 1/2, 0)',
5865 '(1/4, 0, 1/2)', '(0, 1/4, 1/2)', '(1/2, 3/4, 0)',
5866 '(0, 3/4, 1/2)', '(1/2, 1/4, 0)', '(1/2, 0, 1/4)',
5867 '(0, 1/2, 3/4)', '(1/2, 0, 3/4)', '(0, 1/2, 1/4)')),
5868 '12e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
5869 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
5870 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
5871 '(1/2, -x+1/2, 1/2)', '(0, 0, x)',
5872 '(1/2, 1/2, x+1/2)', '(0, 0, -x)',
5873 '(1/2, 1/2, -x+1/2)')),
5874 '24f': (1, ('(x, 1/2, 0)', '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
5875 '(-x+1/2, 0, 1/2)', '(0, x, 1/2)',
5876 '(1/2, x+1/2, 0)', '(0, -x, 1/2)',
5877 '(1/2, -x+1/2, 0)', '(1/2, 0, x)',
5878 '(0, 1/2, x+1/2)', '(1/2, 0, -x)',
5879 '(0, 1/2, -x+1/2)', '(1/2, x, 0)',
5880 '(0, x+1/2, 1/2)', '(1/2, -x, 0)',
5881 '(0, -x+1/2, 1/2)', '(x, 0, 1/2)',
5882 '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
5883 '(-x+1/2, 1/2, 0)', '(0, 1/2, x)',
5884 '(1/2, 0, x+1/2)', '(0, 1/2, -x)',
5885 '(1/2, 0, -x+1/2)')),
5886 '24g': (5, ('(x, x, z)', '(x+1/2, x+1/2, z+1/2)', '(-x, -x, z)',
5887 '(-x+1/2, -x+1/2, z+1/2)', '(-x, x, -z)',
5888 '(-x+1/2, x+1/2, -z+1/2)', '(x, -x, -z)',
5889 '(x+1/2, -x+1/2, -z+1/2)', '(z, x, x)',
5890 '(z+1/2, x+1/2, x+1/2)', '(z, -x, -x)',
5891 '(z+1/2, -x+1/2, -x+1/2)', '(-z, -x, x)',
5892 '(-z+1/2, -x+1/2, x+1/2)', '(-z, x, -x)',
5893 '(-z+1/2, x+1/2, -x+1/2)', '(x, z, x)',
5894 '(x+1/2, z+1/2, x+1/2)', '(-x, z, -x)',
5895 '(-x+1/2, z+1/2, -x+1/2)', '(x, -z, -x)',
5896 '(x+1/2, -z+1/2, -x+1/2)', '(-x, -z, x)',
5897 '(-x+1/2, -z+1/2, x+1/2)')),
5898 '48h': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
5899 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
5900 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
5901 '(x+1/2, -y+1/2, -z+1/2)', '(z, x, y)',
5902 '(z+1/2, x+1/2, y+1/2)', '(z, -x, -y)',
5903 '(z+1/2, -x+1/2, -y+1/2)', '(-z, -x, y)',
5904 '(-z+1/2, -x+1/2, y+1/2)', '(-z, x, -y)',
5905 '(-z+1/2, x+1/2, -y+1/2)', '(y, z, x)',
5906 '(y+1/2, z+1/2, x+1/2)', '(-y, z, -x)',
5907 '(-y+1/2, z+1/2, -x+1/2)', '(y, -z, -x)',
5908 '(y+1/2, -z+1/2, -x+1/2)', '(-y, -z, x)',
5909 '(-y+1/2, -z+1/2, x+1/2)', '(y, x, z)',
5910 '(y+1/2, x+1/2, z+1/2)', '(-y, -x, z)',
5911 '(-y+1/2, -x+1/2, z+1/2)', '(y, -x, -z)',
5912 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
5913 '(-y+1/2, x+1/2, -z+1/2)', '(x, z, y)',
5914 '(x+1/2, z+1/2, y+1/2)', '(-x, z, -y)',
5915 '(-x+1/2, z+1/2, -y+1/2)', '(-x, -z, y)',
5916 '(-x+1/2, -z+1/2, y+1/2)', '(x, -z, -y)',
5917 '(x+1/2, -z+1/2, -y+1/2)', '(z, y, x)',
5918 '(z+1/2, y+1/2, x+1/2)', '(z, -y, -x)',
5919 '(z+1/2, -y+1/2, -x+1/2)', '(-z, y, -x)',
5920 '(-z+1/2, y+1/2, -x+1/2)', '(-z, -y, x)',
5921 '(-z+1/2, -y+1/2, x+1/2)'))},
5922 '218': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
5923 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
5924 '(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 0, 1/2)')),
5925 '6c': (0, ('(1/4, 1/2, 0)', '(3/4, 1/2, 0)', '(0, 1/4, 1/2)',
5926 '(0, 3/4, 1/2)', '(1/2, 0, 1/4)', '(1/2, 0, 3/4)')),
5927 '6d': (0, ('(1/4, 0, 1/2)', '(3/4, 0, 1/2)', '(1/2, 1/4, 0)',
5928 '(1/2, 3/4, 0)', '(0, 1/2, 1/4)', '(0, 1/2, 3/4)')),
5929 '8e': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
5930 '(x, -x, -x)', '(x+1/2, x+1/2, x+1/2)',
5931 '(-x+1/2, -x+1/2, x+1/2)', '(x+1/2, -x+1/2, -x+1/2)',
5932 '(-x+1/2, x+1/2, -x+1/2)')),
5933 '12f': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
5934 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
5935 '(1/2, x+1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
5936 '(x+1/2, 1/2, 1/2)', '(-x+1/2, 1/2, 1/2)',
5937 '(1/2, 1/2, x+1/2)', '(1/2, 1/2, -x+1/2)')),
5938 '12g': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
5939 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
5940 '(0, x+1/2, 1/2)', '(0, -x+1/2, 1/2)',
5941 '(x+1/2, 1/2, 0)', '(-x+1/2, 1/2, 0)',
5942 '(1/2, 0, x+1/2)', '(1/2, 0, -x+1/2)')),
5943 '12h': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
5944 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)',
5945 '(1/2, x+1/2, 0)', '(1/2, -x+1/2, 0)',
5946 '(x+1/2, 0, 1/2)', '(-x+1/2, 0, 1/2)',
5947 '(0, 1/2, x+1/2)', '(0, 1/2, -x+1/2)')),
5948 '24i': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
5949 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
5950 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
5951 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
5952 '(y+1/2, x+1/2, z+1/2)', '(-y+1/2, -x+1/2, z+1/2)',
5953 '(y+1/2, -x+1/2, -z+1/2)',
5954 '(-y+1/2, x+1/2, -z+1/2)', '(x+1/2, z+1/2, y+1/2)',
5955 '(-x+1/2, z+1/2, -y+1/2)',
5956 '(-x+1/2, -z+1/2, y+1/2)',
5957 '(x+1/2, -z+1/2, -y+1/2)', '(z+1/2, y+1/2, x+1/2)',
5958 '(z+1/2, -y+1/2, -x+1/2)',
5959 '(-z+1/2, y+1/2, -x+1/2)',
5960 '(-z+1/2, -y+1/2, x+1/2)'))},
5961 '219': {'8a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
5962 '(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 0)',
5963 '(0, 1/2, 0)', '(0, 0, 1/2)')),
5964 '8b': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
5965 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
5966 '(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
5967 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
5968 '24c': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
5969 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
5970 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)',
5971 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
5972 '(3/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
5973 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
5974 '(3/4, 1/2, 3/4)', '(1/4, 1/4, 0)',
5975 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
5976 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
5977 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
5978 '24d': (0, ('(1/4, 0, 0)', '(1/4, 1/2, 1/2)', '(3/4, 0, 1/2)',
5979 '(3/4, 1/2, 0)', '(3/4, 0, 0)', '(3/4, 1/2, 1/2)',
5980 '(1/4, 0, 1/2)', '(1/4, 1/2, 0)', '(0, 1/4, 0)',
5981 '(0, 3/4, 1/2)', '(1/2, 1/4, 1/2)', '(1/2, 3/4, 0)',
5982 '(0, 3/4, 0)', '(0, 1/4, 1/2)', '(1/2, 3/4, 1/2)',
5983 '(1/2, 1/4, 0)', '(0, 0, 1/4)', '(0, 1/2, 3/4)',
5984 '(1/2, 0, 3/4)', '(1/2, 1/2, 1/4)', '(0, 0, 3/4)',
5985 '(0, 1/2, 1/4)', '(1/2, 0, 1/4)', '(1/2, 1/2, 3/4)'
5986 )),
5987 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
5988 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
5989 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
5990 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
5991 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
5992 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
5993 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
5994 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5995 '(x+1/2, x+1/2, x+1/2)', '(x+1/2, x, x)',
5996 '(x, x+1/2, x)', '(x, x, x+1/2)',
5997 '(-x+1/2, -x+1/2, x+1/2)', '(-x+1/2, -x, x)',
5998 '(-x, -x+1/2, x)', '(-x, -x, x+1/2)',
5999 '(x+1/2, -x+1/2, -x+1/2)', '(x+1/2, -x, -x)',
6000 '(x, -x+1/2, -x)', '(x, -x, -x+1/2)',
6001 '(-x+1/2, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
6002 '(-x, x+1/2, -x)', '(-x, x, -x+1/2)')),
6003 '48f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
6004 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
6005 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
6006 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
6007 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
6008 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
6009 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
6010 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
6011 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)',
6012 '(1/2, x+1/2, 1/2)', '(1/2, x, 0)', '(0, x+1/2, 0)',
6013 '(0, x, 1/2)', '(1/2, -x+1/2, 1/2)', '(1/2, -x, 0)',
6014 '(0, -x+1/2, 0)', '(0, -x, 1/2)',
6015 '(x+1/2, 1/2, 1/2)', '(x+1/2, 0, 0)', '(x, 1/2, 0)',
6016 '(x, 0, 1/2)', '(-x+1/2, 1/2, 1/2)',
6017 '(-x+1/2, 0, 0)', '(-x, 1/2, 0)', '(-x, 0, 1/2)',
6018 '(1/2, 1/2, x+1/2)', '(1/2, 0, x)', '(0, 1/2, x)',
6019 '(0, 0, x+1/2)', '(1/2, 1/2, -x+1/2)',
6020 '(1/2, 0, -x)', '(0, 1/2, -x)', '(0, 0, -x+1/2)')),
6021 '48g': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
6022 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
6023 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
6024 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
6025 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
6026 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
6027 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
6028 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
6029 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
6030 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
6031 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
6032 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)',
6033 '(3/4, x+1/2, 3/4)', '(3/4, x, 1/4)',
6034 '(1/4, x+1/2, 1/4)', '(1/4, x, 3/4)',
6035 '(1/4, -x+1/2, 3/4)', '(1/4, -x, 1/4)',
6036 '(3/4, -x+1/2, 1/4)', '(3/4, -x, 3/4)',
6037 '(x+1/2, 3/4, 3/4)', '(x+1/2, 1/4, 1/4)',
6038 '(x, 3/4, 1/4)', '(x, 1/4, 3/4)',
6039 '(-x+1/2, 3/4, 1/4)', '(-x+1/2, 1/4, 3/4)',
6040 '(-x, 3/4, 3/4)', '(-x, 1/4, 1/4)',
6041 '(3/4, 3/4, x+1/2)', '(3/4, 1/4, x)',
6042 '(1/4, 3/4, x)', '(1/4, 1/4, x+1/2)',
6043 '(3/4, 1/4, -x+1/2)', '(3/4, 3/4, -x)',
6044 '(1/4, 1/4, -x)', '(1/4, 3/4, -x+1/2)')),
6045 '96h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
6046 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
6047 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
6048 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
6049 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
6050 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
6051 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
6052 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
6053 '(z, x, y)', '(z, x+1/2, y+1/2)',
6054 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
6055 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
6056 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
6057 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
6058 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
6059 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
6060 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
6061 '(y, z, x)', '(y, z+1/2, x+1/2)',
6062 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
6063 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
6064 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
6065 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
6066 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
6067 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
6068 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
6069 '(y+1/2, x+1/2, z+1/2)', '(y+1/2, x, z)',
6070 '(y, x+1/2, z)', '(y, x, z+1/2)',
6071 '(-y+1/2, -x+1/2, z+1/2)', '(-y+1/2, -x, z)',
6072 '(-y, -x+1/2, z)', '(-y, -x, z+1/2)',
6073 '(y+1/2, -x+1/2, -z+1/2)', '(y+1/2, -x, -z)',
6074 '(y, -x+1/2, -z)', '(y, -x, -z+1/2)',
6075 '(-y+1/2, x+1/2, -z+1/2)', '(-y+1/2, x, -z)',
6076 '(-y, x+1/2, -z)', '(-y, x, -z+1/2)',
6077 '(x+1/2, z+1/2, y+1/2)', '(x+1/2, z, y)',
6078 '(x, z+1/2, y)', '(x, z, y+1/2)',
6079 '(-x+1/2, z+1/2, -y+1/2)', '(-x+1/2, z, -y)',
6080 '(-x, z+1/2, -y)', '(-x, z, -y+1/2)',
6081 '(-x+1/2, -z+1/2, y+1/2)', '(-x+1/2, -z, y)',
6082 '(-x, -z+1/2, y)', '(-x, -z, y+1/2)',
6083 '(x+1/2, -z+1/2, -y+1/2)', '(x+1/2, -z, -y)',
6084 '(x, -z+1/2, -y)', '(x, -z, -y+1/2)',
6085 '(z+1/2, y+1/2, x+1/2)', '(z+1/2, y, x)',
6086 '(z, y+1/2, x)', '(z, y, x+1/2)',
6087 '(z+1/2, -y+1/2, -x+1/2)', '(z+1/2, -y, -x)',
6088 '(z, -y+1/2, -x)', '(z, -y, -x+1/2)',
6089 '(-z+1/2, y+1/2, -x+1/2)', '(-z+1/2, y, -x)',
6090 '(-z, y+1/2, -x)', '(-z, y, -x+1/2)',
6091 '(-z+1/2, -y+1/2, x+1/2)', '(-z+1/2, -y, x)',
6092 '(-z, -y+1/2, x)', '(-z, -y, x+1/2)'))},
6093 '220': {'12a': (0, ('(3/8, 0, 1/4)', '(7/8, 1/2, 3/4)', '(1/8, 0, 3/4)',
6094 '(5/8, 1/2, 1/4)', '(1/4, 3/8, 0)',
6095 '(3/4, 7/8, 1/2)', '(3/4, 1/8, 0)',
6096 '(1/4, 5/8, 1/2)', '(0, 1/4, 3/8)',
6097 '(1/2, 3/4, 7/8)', '(0, 3/4, 1/8)',
6098 '(1/2, 1/4, 5/8)')),
6099 '12b': (0, ('(7/8, 0, 1/4)', '(3/8, 1/2, 3/4)', '(5/8, 0, 3/4)',
6100 '(1/8, 1/2, 1/4)', '(1/4, 7/8, 0)',
6101 '(3/4, 3/8, 1/2)', '(3/4, 5/8, 0)',
6102 '(1/4, 1/8, 1/2)', '(0, 1/4, 7/8)',
6103 '(1/2, 3/4, 3/8)', '(0, 3/4, 5/8)',
6104 '(1/2, 1/4, 1/8)')),
6105 '16c': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)',
6106 '(-x+1/2, -x, x+1/2)', '(-x, -x+1/2, x)',
6107 '(-x, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
6108 '(x+1/2, -x+1/2, -x)', '(x, -x, -x+1/2)',
6109 '(x+1/4, x+1/4, x+1/4)', '(x+3/4, x+3/4, x+3/4)',
6110 '(-x+1/4, -x+3/4, x+3/4)',
6111 '(-x+3/4, -x+1/4, x+1/4)',
6112 '(x+3/4, -x+1/4, -x+3/4)',
6113 '(x+1/4, -x+3/4, -x+1/4)',
6114 '(-x+3/4, x+3/4, -x+1/4)',
6115 '(-x+1/4, x+1/4, -x+3/4)')),
6116 '24d': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
6117 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(1/4, x, 0)',
6118 '(3/4, x+1/2, 1/2)', '(3/4, -x+1/2, 0)',
6119 '(1/4, -x, 1/2)', '(0, 1/4, x)',
6120 '(1/2, 3/4, x+1/2)', '(0, 3/4, -x+1/2)',
6121 '(1/2, 1/4, -x)', '(1/4, x+1/4, 1/2)',
6122 '(3/4, x+3/4, 0)', '(1/4, -x+3/4, 0)',
6123 '(3/4, -x+1/4, 1/2)', '(x+1/4, 1/2, 1/4)',
6124 '(x+3/4, 0, 3/4)', '(-x+3/4, 0, 1/4)',
6125 '(-x+1/4, 1/2, 3/4)', '(1/2, 1/4, x+1/4)',
6126 '(0, 3/4, x+3/4)', '(0, 1/4, -x+3/4)',
6127 '(1/2, 3/4, -x+1/4)')),
6128 '48e': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
6129 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
6130 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
6131 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
6132 '(z, x, y)', '(z+1/2, x+1/2, y+1/2)',
6133 '(z+1/2, -x+1/2, -y)', '(z, -x, -y+1/2)',
6134 '(-z+1/2, -x, y+1/2)', '(-z, -x+1/2, y)',
6135 '(-z, x+1/2, -y+1/2)', '(-z+1/2, x, -y)',
6136 '(y, z, x)', '(y+1/2, z+1/2, x+1/2)',
6137 '(-y, z+1/2, -x+1/2)', '(-y+1/2, z, -x)',
6138 '(y+1/2, -z+1/2, -x)', '(y, -z, -x+1/2)',
6139 '(-y+1/2, -z, x+1/2)', '(-y, -z+1/2, x)',
6140 '(y+1/4, x+1/4, z+1/4)', '(y+3/4, x+3/4, z+3/4)',
6141 '(-y+1/4, -x+3/4, z+3/4)',
6142 '(-y+3/4, -x+1/4, z+1/4)',
6143 '(y+3/4, -x+1/4, -z+3/4)',
6144 '(y+1/4, -x+3/4, -z+1/4)',
6145 '(-y+3/4, x+3/4, -z+1/4)',
6146 '(-y+1/4, x+1/4, -z+3/4)', '(x+1/4, z+1/4, y+1/4)',
6147 '(x+3/4, z+3/4, y+3/4)', '(-x+3/4, z+3/4, -y+1/4)',
6148 '(-x+1/4, z+1/4, -y+3/4)',
6149 '(-x+1/4, -z+3/4, y+3/4)',
6150 '(-x+3/4, -z+1/4, y+1/4)',
6151 '(x+3/4, -z+1/4, -y+3/4)',
6152 '(x+1/4, -z+3/4, -y+1/4)', '(z+1/4, y+1/4, x+1/4)',
6153 '(z+3/4, y+3/4, x+3/4)', '(z+3/4, -y+1/4, -x+3/4)',
6154 '(z+1/4, -y+3/4, -x+1/4)',
6155 '(-z+3/4, y+3/4, -x+1/4)',
6156 '(-z+1/4, y+1/4, -x+3/4)',
6157 '(-z+1/4, -y+3/4, x+3/4)',
6158 '(-z+3/4, -y+1/4, x+1/4)'))},
6159 '221': {'1a': (0, ('(0, 0, 0)', )),
6160 '1b': (0, ('(1/2, 1/2, 1/2)', )),
6161 '3c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
6162 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
6163 '6e': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)',
6164 '(0, 0, x)', '(0, 0, -x)')),
6165 '6f': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
6166 '(1/2, -x, 1/2)', '(1/2, 1/2, x)', '(1/2, 1/2, -x)'
6167 )),
6168 '8g': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
6169 '(x, -x, -x)', '(x, x, -x)', '(-x, -x, -x)',
6170 '(x, -x, x)', '(-x, x, x)')),
6171 '12h': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
6172 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
6173 '(1/2, x, 0)', '(1/2, -x, 0)', '(x, 0, 1/2)',
6174 '(-x, 0, 1/2)', '(0, 1/2, -x)', '(0, 1/2, x)')),
6175 '12i': (2, ('(0, y, y)', '(0, -y, y)', '(0, y, -y)',
6176 '(0, -y, -y)', '(y, 0, y)', '(y, 0, -y)',
6177 '(-y, 0, y)', '(-y, 0, -y)', '(y, y, 0)',
6178 '(-y, y, 0)', '(y, -y, 0)', '(-y, -y, 0)')),
6179 '12j': (2, ('(1/2, y, y)', '(1/2, -y, y)', '(1/2, y, -y)',
6180 '(1/2, -y, -y)', '(y, 1/2, y)', '(y, 1/2, -y)',
6181 '(-y, 1/2, y)', '(-y, 1/2, -y)', '(y, y, 1/2)',
6182 '(-y, y, 1/2)', '(y, -y, 1/2)', '(-y, -y, 1/2)')),
6183 '24k': (6, ('(0, y, z)', '(0, -y, z)', '(0, y, -z)',
6184 '(0, -y, -z)', '(z, 0, y)', '(z, 0, -y)',
6185 '(-z, 0, y)', '(-z, 0, -y)', '(y, z, 0)',
6186 '(-y, z, 0)', '(y, -z, 0)', '(-y, -z, 0)',
6187 '(y, 0, -z)', '(-y, 0, -z)', '(y, 0, z)',
6188 '(-y, 0, z)', '(0, z, -y)', '(0, z, y)',
6189 '(0, -z, -y)', '(0, -z, y)', '(z, y, 0)',
6190 '(z, -y, 0)', '(-z, y, 0)', '(-z, -y, 0)')),
6191 '24l': (6, ('(1/2, y, z)', '(1/2, -y, z)', '(1/2, y, -z)',
6192 '(1/2, -y, -z)', '(z, 1/2, y)', '(z, 1/2, -y)',
6193 '(-z, 1/2, y)', '(-z, 1/2, -y)', '(y, z, 1/2)',
6194 '(-y, z, 1/2)', '(y, -z, 1/2)', '(-y, -z, 1/2)',
6195 '(y, 1/2, -z)', '(-y, 1/2, -z)', '(y, 1/2, z)',
6196 '(-y, 1/2, z)', '(1/2, z, -y)', '(1/2, z, y)',
6197 '(1/2, -z, -y)', '(1/2, -z, y)', '(z, y, 1/2)',
6198 '(z, -y, 1/2)', '(-z, y, 1/2)', '(-z, -y, 1/2)')),
6199 '24m': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, -z)',
6200 '(x, -x, -z)', '(z, x, x)', '(z, -x, -x)',
6201 '(-z, -x, x)', '(-z, x, -x)', '(x, z, x)',
6202 '(-x, z, -x)', '(x, -z, -x)', '(-x, -z, x)',
6203 '(x, x, -z)', '(-x, -x, -z)', '(x, -x, z)',
6204 '(-x, x, z)', '(x, z, -x)', '(-x, z, x)',
6205 '(-x, -z, -x)', '(x, -z, x)', '(z, x, -x)',
6206 '(z, -x, x)', '(-z, x, x)', '(-z, -x, -x)')),
6207 '48n': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
6208 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
6209 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
6210 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
6211 '(y, x, -z)', '(-y, -x, -z)', '(y, -x, z)',
6212 '(-y, x, z)', '(x, z, -y)', '(-x, z, y)',
6213 '(-x, -z, -y)', '(x, -z, y)', '(z, y, -x)',
6214 '(z, -y, x)', '(-z, y, x)', '(-z, -y, -x)',
6215 '(-x, -y, -z)', '(x, y, -z)', '(x, -y, z)',
6216 '(-x, y, z)', '(-z, -x, -y)', '(-z, x, y)',
6217 '(z, x, -y)', '(z, -x, y)', '(-y, -z, -x)',
6218 '(y, -z, x)', '(-y, z, x)', '(y, z, -x)',
6219 '(-y, -x, z)', '(y, x, z)', '(-y, x, -z)',
6220 '(y, -x, -z)', '(-x, -z, y)', '(x, -z, -y)',
6221 '(x, z, y)', '(-x, z, -y)', '(-z, -y, x)',
6222 '(-z, y, -x)', '(z, -y, -x)', '(z, y, x)'))},
6223 '222:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
6224 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
6225 '(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
6226 '8c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
6227 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)',
6228 '(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)',
6229 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
6230 '12d': (0, ('(1/4, 0, 1/2)', '(3/4, 0, 1/2)', '(1/2, 1/4, 0)',
6231 '(1/2, 3/4, 0)', '(0, 1/2, 1/4)', '(0, 1/2, 3/4)',
6232 '(0, 1/4, 1/2)', '(0, 3/4, 1/2)', '(1/4, 1/2, 0)',
6233 '(3/4, 1/2, 0)', '(1/2, 0, 3/4)', '(1/2, 0, 1/4)'
6234 )),
6235 '12e': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
6236 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
6237 '(-x+1/2, 1/2, 1/2)', '(x+1/2, 1/2, 1/2)',
6238 '(1/2, -x+1/2, 1/2)', '(1/2, x+1/2, 1/2)',
6239 '(1/2, 1/2, -x+1/2)', '(1/2, 1/2, x+1/2)')),
6240 '16f': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
6241 '(x, -x, -x)', '(x, x, -x)', '(-x, -x, -x)',
6242 '(x, -x, x)', '(-x, x, x)',
6243 '(-x+1/2, -x+1/2, -x+1/2)',
6244 '(x+1/2, x+1/2, -x+1/2)',
6245 '(x+1/2, -x+1/2, x+1/2)',
6246 '(-x+1/2, x+1/2, x+1/2)',
6247 '(-x+1/2, -x+1/2, x+1/2)',
6248 '(x+1/2, x+1/2, x+1/2)',
6249 '(-x+1/2, x+1/2, -x+1/2)',
6250 '(x+1/2, -x+1/2, -x+1/2)')),
6251 '24g': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
6252 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)',
6253 '(0, x, 1/2)', '(0, -x, 1/2)', '(x, 1/2, 0)',
6254 '(-x, 1/2, 0)', '(1/2, 0, -x)', '(1/2, 0, x)',
6255 '(-x+1/2, 1/2, 0)', '(x+1/2, 1/2, 0)',
6256 '(0, -x+1/2, 1/2)', '(0, x+1/2, 1/2)',
6257 '(1/2, 0, -x+1/2)', '(1/2, 0, x+1/2)',
6258 '(1/2, -x+1/2, 0)', '(1/2, x+1/2, 0)',
6259 '(-x+1/2, 0, 1/2)', '(x+1/2, 0, 1/2)',
6260 '(0, 1/2, x+1/2)', '(0, 1/2, -x+1/2)')),
6261 '24h': (2, ('(0, y, y)', '(0, -y, y)', '(0, y, -y)',
6262 '(0, -y, -y)', '(y, 0, y)', '(y, 0, -y)',
6263 '(-y, 0, y)', '(-y, 0, -y)', '(y, y, 0)',
6264 '(-y, y, 0)', '(y, -y, 0)', '(-y, -y, 0)',
6265 '(1/2, -y+1/2, -y+1/2)', '(1/2, y+1/2, -y+1/2)',
6266 '(1/2, -y+1/2, y+1/2)', '(1/2, y+1/2, y+1/2)',
6267 '(-y+1/2, 1/2, -y+1/2)', '(-y+1/2, 1/2, y+1/2)',
6268 '(y+1/2, 1/2, -y+1/2)', '(y+1/2, 1/2, y+1/2)',
6269 '(-y+1/2, -y+1/2, 1/2)', '(y+1/2, -y+1/2, 1/2)',
6270 '(-y+1/2, y+1/2, 1/2)', '(y+1/2, y+1/2, 1/2)')),
6271 '48i': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
6272 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
6273 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
6274 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
6275 '(y, x, -z)', '(-y, -x, -z)', '(y, -x, z)',
6276 '(-y, x, z)', '(x, z, -y)', '(-x, z, y)',
6277 '(-x, -z, -y)', '(x, -z, y)', '(z, y, -x)',
6278 '(z, -y, x)', '(-z, y, x)', '(-z, -y, -x)',
6279 '(-x+1/2, -y+1/2, -z+1/2)',
6280 '(x+1/2, y+1/2, -z+1/2)',
6281 '(x+1/2, -y+1/2, z+1/2)',
6282 '(-x+1/2, y+1/2, z+1/2)',
6283 '(-z+1/2, -x+1/2, -y+1/2)',
6284 '(-z+1/2, x+1/2, y+1/2)',
6285 '(z+1/2, x+1/2, -y+1/2)',
6286 '(z+1/2, -x+1/2, y+1/2)',
6287 '(-y+1/2, -z+1/2, -x+1/2)',
6288 '(y+1/2, -z+1/2, x+1/2)',
6289 '(-y+1/2, z+1/2, x+1/2)',
6290 '(y+1/2, z+1/2, -x+1/2)',
6291 '(-y+1/2, -x+1/2, z+1/2)',
6292 '(y+1/2, x+1/2, z+1/2)',
6293 '(-y+1/2, x+1/2, -z+1/2)',
6294 '(y+1/2, -x+1/2, -z+1/2)',
6295 '(-x+1/2, -z+1/2, y+1/2)',
6296 '(x+1/2, -z+1/2, -y+1/2)',
6297 '(x+1/2, z+1/2, y+1/2)',
6298 '(-x+1/2, z+1/2, -y+1/2)',
6299 '(-z+1/2, -y+1/2, x+1/2)',
6300 '(-z+1/2, y+1/2, -x+1/2)',
6301 '(z+1/2, -y+1/2, -x+1/2)',
6302 '(z+1/2, y+1/2, x+1/2)'))},
6303 '222:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
6304 '6b': (0, ('(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
6305 '(1/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)',
6306 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
6307 '8c': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
6308 '(0, 1/2, 1/2)', '(0, 0, 1/2)', '(1/2, 1/2, 1/2)',
6309 '(0, 1/2, 0)', '(1/2, 0, 0)')),
6310 '12d': (0, ('(0, 3/4, 1/4)', '(1/2, 3/4, 1/4)',
6311 '(1/4, 0, 3/4)', '(1/4, 1/2, 3/4)',
6312 '(3/4, 1/4, 0)', '(3/4, 1/4, 1/2)',
6313 '(3/4, 0, 1/4)', '(3/4, 1/2, 1/4)',
6314 '(0, 1/4, 3/4)', '(1/2, 1/4, 3/4)',
6315 '(1/4, 3/4, 1/2)', '(1/4, 3/4, 0)')),
6316 '12e': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
6317 '(1/4, x, 1/4)', '(1/4, -x+1/2, 1/4)',
6318 '(1/4, 1/4, x)', '(1/4, 1/4, -x+1/2)',
6319 '(-x, 3/4, 3/4)', '(x+1/2, 3/4, 3/4)',
6320 '(3/4, -x, 3/4)', '(3/4, x+1/2, 3/4)',
6321 '(3/4, 3/4, -x)', '(3/4, 3/4, x+1/2)')),
6322 '16f': (1, ('(x, x, x)', '(-x+1/2, -x+1/2, x)',
6323 '(-x+1/2, x, -x+1/2)', '(x, -x+1/2, -x+1/2)',
6324 '(x, x, -x+1/2)', '(-x+1/2, -x+1/2, -x+1/2)',
6325 '(x, -x+1/2, x)', '(-x+1/2, x, x)',
6326 '(-x, -x, -x)', '(x+1/2, x+1/2, -x)',
6327 '(x+1/2, -x, x+1/2)', '(-x, x+1/2, x+1/2)',
6328 '(-x, -x, x+1/2)', '(x+1/2, x+1/2, x+1/2)',
6329 '(-x, x+1/2, -x)', '(x+1/2, -x, -x)')),
6330 '24g': (1, ('(x, 3/4, 1/4)', '(-x+1/2, 3/4, 1/4)',
6331 '(1/4, x, 3/4)', '(1/4, -x+1/2, 3/4)',
6332 '(3/4, 1/4, x)', '(3/4, 1/4, -x+1/2)',
6333 '(3/4, x, 1/4)', '(3/4, -x+1/2, 1/4)',
6334 '(x, 1/4, 3/4)', '(-x+1/2, 1/4, 3/4)',
6335 '(1/4, 3/4, -x+1/2)', '(1/4, 3/4, x)',
6336 '(-x, 1/4, 3/4)', '(x+1/2, 1/4, 3/4)',
6337 '(3/4, -x, 1/4)', '(3/4, x+1/2, 1/4)',
6338 '(1/4, 3/4, -x)', '(1/4, 3/4, x+1/2)',
6339 '(1/4, -x, 3/4)', '(1/4, x+1/2, 3/4)',
6340 '(-x, 3/4, 1/4)', '(x+1/2, 3/4, 1/4)',
6341 '(3/4, 1/4, x+1/2)', '(3/4, 1/4, -x)')),
6342 '24h': (2, ('(1/4, y, y)', '(1/4, -y+1/2, y)',
6343 '(1/4, y, -y+1/2)', '(1/4, -y+1/2, -y+1/2)',
6344 '(y, 1/4, y)', '(y, 1/4, -y+1/2)',
6345 '(-y+1/2, 1/4, y)', '(-y+1/2, 1/4, -y+1/2)',
6346 '(y, y, 1/4)', '(-y+1/2, y, 1/4)',
6347 '(y, -y+1/2, 1/4)', '(-y+1/2, -y+1/2, 1/4)',
6348 '(3/4, -y, -y)', '(3/4, y+1/2, -y)',
6349 '(3/4, -y, y+1/2)', '(3/4, y+1/2, y+1/2)',
6350 '(-y, 3/4, -y)', '(-y, 3/4, y+1/2)',
6351 '(y+1/2, 3/4, -y)', '(y+1/2, 3/4, y+1/2)',
6352 '(-y, -y, 3/4)', '(y+1/2, -y, 3/4)',
6353 '(-y, y+1/2, 3/4)', '(y+1/2, y+1/2, 3/4)')),
6354 '48i': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
6355 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
6356 '(z, x, y)', '(z, -x+1/2, -y+1/2)',
6357 '(-z+1/2, -x+1/2, y)', '(-z+1/2, x, -y+1/2)',
6358 '(y, z, x)', '(-y+1/2, z, -x+1/2)',
6359 '(y, -z+1/2, -x+1/2)', '(-y+1/2, -z+1/2, x)',
6360 '(y, x, -z+1/2)', '(-y+1/2, -x+1/2, -z+1/2)',
6361 '(y, -x+1/2, z)', '(-y+1/2, x, z)',
6362 '(x, z, -y+1/2)', '(-x+1/2, z, y)',
6363 '(-x+1/2, -z+1/2, -y+1/2)', '(x, -z+1/2, y)',
6364 '(z, y, -x+1/2)', '(z, -y+1/2, x)',
6365 '(-z+1/2, y, x)', '(-z+1/2, -y+1/2, -x+1/2)',
6366 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
6367 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)',
6368 '(-z, -x, -y)', '(-z, x+1/2, y+1/2)',
6369 '(z+1/2, x+1/2, -y)', '(z+1/2, -x, y+1/2)',
6370 '(-y, -z, -x)', '(y+1/2, -z, x+1/2)',
6371 '(-y, z+1/2, x+1/2)', '(y+1/2, z+1/2, -x)',
6372 '(-y, -x, z+1/2)', '(y+1/2, x+1/2, z+1/2)',
6373 '(-y, x+1/2, -z)', '(y+1/2, -x, -z)',
6374 '(-x, -z, y+1/2)', '(x+1/2, -z, -y)',
6375 '(x+1/2, z+1/2, y+1/2)', '(-x, z+1/2, -y)',
6376 '(-z, -y, x+1/2)', '(-z, y+1/2, -x)',
6377 '(z+1/2, -y, -x)', '(z+1/2, y+1/2, x+1/2)'))},
6378 '223': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
6379 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
6380 '(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 0, 1/2)')),
6381 '6c': (0, ('(1/4, 0, 1/2)', '(3/4, 0, 1/2)', '(1/2, 1/4, 0)',
6382 '(1/2, 3/4, 0)', '(0, 1/2, 1/4)', '(0, 1/2, 3/4)')),
6383 '6d': (0, ('(1/4, 1/2, 0)', '(3/4, 1/2, 0)', '(0, 1/4, 1/2)',
6384 '(0, 3/4, 1/2)', '(1/2, 0, 1/4)', '(1/2, 0, 3/4)')),
6385 '8e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
6386 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)',
6387 '(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
6388 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
6389 '12f': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
6390 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
6391 '(1/2, x+1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
6392 '(x+1/2, 1/2, 1/2)', '(-x+1/2, 1/2, 1/2)',
6393 '(1/2, 1/2, -x+1/2)', '(1/2, 1/2, x+1/2)')),
6394 '12g': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
6395 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)',
6396 '(1/2, x+1/2, 0)', '(1/2, -x+1/2, 0)',
6397 '(x+1/2, 0, 1/2)', '(-x+1/2, 0, 1/2)',
6398 '(0, 1/2, -x+1/2)', '(0, 1/2, x+1/2)')),
6399 '12h': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
6400 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
6401 '(0, x+1/2, 1/2)', '(0, -x+1/2, 1/2)',
6402 '(x+1/2, 1/2, 0)', '(-x+1/2, 1/2, 0)',
6403 '(1/2, 0, -x+1/2)', '(1/2, 0, x+1/2)')),
6404 '16i': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
6405 '(x, -x, -x)', '(x+1/2, x+1/2, -x+1/2)',
6406 '(-x+1/2, -x+1/2, -x+1/2)',
6407 '(x+1/2, -x+1/2, x+1/2)', '(-x+1/2, x+1/2, x+1/2)',
6408 '(-x, -x, -x)', '(x, x, -x)', '(x, -x, x)',
6409 '(-x, x, x)', '(-x+1/2, -x+1/2, x+1/2)',
6410 '(x+1/2, x+1/2, x+1/2)', '(-x+1/2, x+1/2, -x+1/2)',
6411 '(x+1/2, -x+1/2, -x+1/2)')),
6412 '24j': (2, ('(1/4, y, y+1/2)', '(3/4, -y, y+1/2)',
6413 '(3/4, y, -y+1/2)', '(1/4, -y, -y+1/2)',
6414 '(y+1/2, 1/4, y)', '(y+1/2, 3/4, -y)',
6415 '(-y+1/2, 3/4, y)', '(-y+1/2, 1/4, -y)',
6416 '(y, y+1/2, 1/4)', '(-y, y+1/2, 3/4)',
6417 '(y, -y+1/2, 3/4)', '(-y, -y+1/2, 1/4)',
6418 '(3/4, -y, -y+1/2)', '(1/4, y, -y+1/2)',
6419 '(1/4, -y, y+1/2)', '(3/4, y, y+1/2)',
6420 '(-y+1/2, 3/4, -y)', '(-y+1/2, 1/4, y)',
6421 '(y+1/2, 1/4, -y)', '(y+1/2, 3/4, y)',
6422 '(-y, -y+1/2, 3/4)', '(y, -y+1/2, 1/4)',
6423 '(-y, y+1/2, 1/4)', '(y, y+1/2, 3/4)')),
6424 '24k': (6, ('(0, y, z)', '(0, -y, z)', '(0, y, -z)',
6425 '(0, -y, -z)', '(z, 0, y)', '(z, 0, -y)',
6426 '(-z, 0, y)', '(-z, 0, -y)', '(y, z, 0)',
6427 '(-y, z, 0)', '(y, -z, 0)', '(-y, -z, 0)',
6428 '(y+1/2, 1/2, -z+1/2)', '(-y+1/2, 1/2, -z+1/2)',
6429 '(y+1/2, 1/2, z+1/2)', '(-y+1/2, 1/2, z+1/2)',
6430 '(1/2, z+1/2, -y+1/2)', '(1/2, z+1/2, y+1/2)',
6431 '(1/2, -z+1/2, -y+1/2)', '(1/2, -z+1/2, y+1/2)',
6432 '(z+1/2, y+1/2, 1/2)', '(z+1/2, -y+1/2, 1/2)',
6433 '(-z+1/2, y+1/2, 1/2)', '(-z+1/2, -y+1/2, 1/2)')),
6434 '48l': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
6435 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
6436 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
6437 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
6438 '(y+1/2, x+1/2, -z+1/2)',
6439 '(-y+1/2, -x+1/2, -z+1/2)',
6440 '(y+1/2, -x+1/2, z+1/2)', '(-y+1/2, x+1/2, z+1/2)',
6441 '(x+1/2, z+1/2, -y+1/2)', '(-x+1/2, z+1/2, y+1/2)',
6442 '(-x+1/2, -z+1/2, -y+1/2)',
6443 '(x+1/2, -z+1/2, y+1/2)', '(z+1/2, y+1/2, -x+1/2)',
6444 '(z+1/2, -y+1/2, x+1/2)', '(-z+1/2, y+1/2, x+1/2)',
6445 '(-z+1/2, -y+1/2, -x+1/2)', '(-x, -y, -z)',
6446 '(x, y, -z)', '(x, -y, z)', '(-x, y, z)',
6447 '(-z, -x, -y)', '(-z, x, y)', '(z, x, -y)',
6448 '(z, -x, y)', '(-y, -z, -x)', '(y, -z, x)',
6449 '(-y, z, x)', '(y, z, -x)',
6450 '(-y+1/2, -x+1/2, z+1/2)', '(y+1/2, x+1/2, z+1/2)',
6451 '(-y+1/2, x+1/2, -z+1/2)',
6452 '(y+1/2, -x+1/2, -z+1/2)',
6453 '(-x+1/2, -z+1/2, y+1/2)',
6454 '(x+1/2, -z+1/2, -y+1/2)', '(x+1/2, z+1/2, y+1/2)',
6455 '(-x+1/2, z+1/2, -y+1/2)',
6456 '(-z+1/2, -y+1/2, x+1/2)',
6457 '(-z+1/2, y+1/2, -x+1/2)',
6458 '(z+1/2, -y+1/2, -x+1/2)', '(z+1/2, y+1/2, x+1/2)'
6459 ))},
6460 '224:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
6461 '4b': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
6462 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)')),
6463 '4c': (0, ('(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
6464 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
6465 '6d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
6466 '(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 0, 1/2)')),
6467 '8e': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
6468 '(x, -x, -x)', '(x+1/2, x+1/2, -x+1/2)',
6469 '(-x+1/2, -x+1/2, -x+1/2)',
6470 '(x+1/2, -x+1/2, x+1/2)', '(-x+1/2, x+1/2, x+1/2)'
6471 )),
6472 '12f': (0, ('(1/4, 0, 1/2)', '(3/4, 0, 1/2)', '(1/2, 1/4, 0)',
6473 '(1/2, 3/4, 0)', '(0, 1/2, 1/4)', '(0, 1/2, 3/4)',
6474 '(1/4, 1/2, 0)', '(3/4, 1/2, 0)', '(0, 1/4, 1/2)',
6475 '(0, 3/4, 1/2)', '(1/2, 0, 1/4)', '(1/2, 0, 3/4)'
6476 )),
6477 '12g': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
6478 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
6479 '(1/2, x+1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
6480 '(x+1/2, 1/2, 1/2)', '(-x+1/2, 1/2, 1/2)',
6481 '(1/2, 1/2, -x+1/2)', '(1/2, 1/2, x+1/2)')),
6482 '24h': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
6483 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)',
6484 '(1/2, x+1/2, 0)', '(1/2, -x+1/2, 0)',
6485 '(x+1/2, 0, 1/2)', '(-x+1/2, 0, 1/2)',
6486 '(0, 1/2, -x+1/2)', '(0, 1/2, x+1/2)',
6487 '(-x+1/2, 1/2, 0)', '(x+1/2, 1/2, 0)',
6488 '(0, -x+1/2, 1/2)', '(0, x+1/2, 1/2)',
6489 '(1/2, 0, -x+1/2)', '(1/2, 0, x+1/2)',
6490 '(0, -x, 1/2)', '(0, x, 1/2)', '(-x, 1/2, 0)',
6491 '(x, 1/2, 0)', '(1/2, 0, x)', '(1/2, 0, -x)')),
6492 '24i': (2, ('(1/4, y, -y+1/2)', '(3/4, -y, -y+1/2)',
6493 '(3/4, y, y+1/2)', '(1/4, -y, y+1/2)',
6494 '(-y+1/2, 1/4, y)', '(-y+1/2, 3/4, -y)',
6495 '(y+1/2, 3/4, y)', '(y+1/2, 1/4, -y)',
6496 '(y, -y+1/2, 1/4)', '(-y, -y+1/2, 3/4)',
6497 '(y, y+1/2, 3/4)', '(-y, y+1/2, 1/4)',
6498 '(1/4, -y+1/2, y)', '(3/4, y+1/2, y)',
6499 '(3/4, -y+1/2, -y)', '(1/4, y+1/2, -y)',
6500 '(y, 1/4, -y+1/2)', '(y, 3/4, y+1/2)',
6501 '(-y, 3/4, -y+1/2)', '(-y, 1/4, y+1/2)',
6502 '(-y+1/2, y, 1/4)', '(y+1/2, y, 3/4)',
6503 '(-y+1/2, -y, 3/4)', '(y+1/2, -y, 1/4)')),
6504 '24j': (2, ('(1/4, y, y+1/2)', '(3/4, -y, y+1/2)',
6505 '(3/4, y, -y+1/2)', '(1/4, -y, -y+1/2)',
6506 '(y+1/2, 1/4, y)', '(y+1/2, 3/4, -y)',
6507 '(-y+1/2, 3/4, y)', '(-y+1/2, 1/4, -y)',
6508 '(y, y+1/2, 1/4)', '(-y, y+1/2, 3/4)',
6509 '(y, -y+1/2, 3/4)', '(-y, -y+1/2, 1/4)',
6510 '(1/4, -y+1/2, -y)', '(3/4, y+1/2, -y)',
6511 '(3/4, -y+1/2, y)', '(1/4, y+1/2, y)',
6512 '(-y, 1/4, -y+1/2)', '(-y, 3/4, y+1/2)',
6513 '(y, 3/4, -y+1/2)', '(y, 1/4, y+1/2)',
6514 '(-y+1/2, -y, 1/4)', '(y+1/2, -y, 3/4)',
6515 '(-y+1/2, y, 3/4)', '(y+1/2, y, 1/4)')),
6516 '24k': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, -z)',
6517 '(x, -x, -z)', '(z, x, x)', '(z, -x, -x)',
6518 '(-z, -x, x)', '(-z, x, -x)', '(x, z, x)',
6519 '(-x, z, -x)', '(x, -z, -x)', '(-x, -z, x)',
6520 '(x+1/2, x+1/2, -z+1/2)',
6521 '(-x+1/2, -x+1/2, -z+1/2)',
6522 '(x+1/2, -x+1/2, z+1/2)',
6523 '(-x+1/2, x+1/2, z+1/2)',
6524 '(x+1/2, z+1/2, -x+1/2)',
6525 '(-x+1/2, z+1/2, x+1/2)',
6526 '(-x+1/2, -z+1/2, -x+1/2)',
6527 '(x+1/2, -z+1/2, x+1/2)',
6528 '(z+1/2, x+1/2, -x+1/2)',
6529 '(z+1/2, -x+1/2, x+1/2)',
6530 '(-z+1/2, x+1/2, x+1/2)',
6531 '(-z+1/2, -x+1/2, -x+1/2)')),
6532 '48l': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
6533 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
6534 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
6535 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
6536 '(y+1/2, x+1/2, -z+1/2)',
6537 '(-y+1/2, -x+1/2, -z+1/2)',
6538 '(y+1/2, -x+1/2, z+1/2)',
6539 '(-y+1/2, x+1/2, z+1/2)',
6540 '(x+1/2, z+1/2, -y+1/2)',
6541 '(-x+1/2, z+1/2, y+1/2)',
6542 '(-x+1/2, -z+1/2, -y+1/2)',
6543 '(x+1/2, -z+1/2, y+1/2)',
6544 '(z+1/2, y+1/2, -x+1/2)',
6545 '(z+1/2, -y+1/2, x+1/2)',
6546 '(-z+1/2, y+1/2, x+1/2)',
6547 '(-z+1/2, -y+1/2, -x+1/2)',
6548 '(-x+1/2, -y+1/2, -z+1/2)',
6549 '(x+1/2, y+1/2, -z+1/2)',
6550 '(x+1/2, -y+1/2, z+1/2)',
6551 '(-x+1/2, y+1/2, z+1/2)',
6552 '(-z+1/2, -x+1/2, -y+1/2)',
6553 '(-z+1/2, x+1/2, y+1/2)',
6554 '(z+1/2, x+1/2, -y+1/2)',
6555 '(z+1/2, -x+1/2, y+1/2)',
6556 '(-y+1/2, -z+1/2, -x+1/2)',
6557 '(y+1/2, -z+1/2, x+1/2)',
6558 '(-y+1/2, z+1/2, x+1/2)',
6559 '(y+1/2, z+1/2, -x+1/2)', '(-y, -x, z)',
6560 '(y, x, z)', '(-y, x, -z)', '(y, -x, -z)',
6561 '(-x, -z, y)', '(x, -z, -y)', '(x, z, y)',
6562 '(-x, z, -y)', '(-z, -y, x)', '(-z, y, -x)',
6563 '(z, -y, -x)', '(z, y, x)'))},
6564 '224:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
6565 '4b': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
6566 '(0, 1/2, 1/2)')),
6567 '4c': (0, ('(1/2, 1/2, 1/2)', '(0, 0, 1/2)', '(0, 1/2, 0)',
6568 '(1/2, 0, 0)')),
6569 '6d': (0, ('(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
6570 '(3/4, 3/4, 1/4)', '(1/4, 3/4, 1/4)',
6571 '(3/4, 1/4, 1/4)', '(1/4, 1/4, 3/4)')),
6572 '8e': (1, ('(x, x, x)', '(-x+1/2, -x+1/2, x)',
6573 '(-x+1/2, x, -x+1/2)', '(x, -x+1/2, -x+1/2)',
6574 '(x+1/2, x+1/2, -x)', '(-x, -x, -x)',
6575 '(x+1/2, -x, x+1/2)', '(-x, x+1/2, x+1/2)')),
6576 '12f': (0, ('(1/2, 1/4, 3/4)', '(0, 1/4, 3/4)',
6577 '(3/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
6578 '(1/4, 3/4, 1/2)', '(1/4, 3/4, 0)',
6579 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)',
6580 '(1/4, 1/2, 3/4)', '(1/4, 0, 3/4)',
6581 '(3/4, 1/4, 1/2)', '(3/4, 1/4, 0)')),
6582 '12g': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
6583 '(1/4, x, 1/4)', '(1/4, -x+1/2, 1/4)',
6584 '(1/4, 1/4, x)', '(1/4, 1/4, -x+1/2)',
6585 '(3/4, x+1/2, 3/4)', '(3/4, -x, 3/4)',
6586 '(x+1/2, 3/4, 3/4)', '(-x, 3/4, 3/4)',
6587 '(3/4, 3/4, -x)', '(3/4, 3/4, x+1/2)')),
6588 '24h': (1, ('(x, 1/4, 3/4)', '(-x+1/2, 1/4, 3/4)',
6589 '(3/4, x, 1/4)', '(3/4, -x+1/2, 1/4)',
6590 '(1/4, 3/4, x)', '(1/4, 3/4, -x+1/2)',
6591 '(3/4, x+1/2, 1/4)', '(3/4, -x, 1/4)',
6592 '(x+1/2, 1/4, 3/4)', '(-x, 1/4, 3/4)',
6593 '(1/4, 3/4, -x)', '(1/4, 3/4, x+1/2)',
6594 '(-x, 3/4, 1/4)', '(x+1/2, 3/4, 1/4)',
6595 '(1/4, -x, 3/4)', '(1/4, x+1/2, 3/4)',
6596 '(3/4, 1/4, -x)', '(3/4, 1/4, x+1/2)',
6597 '(1/4, -x+1/2, 3/4)', '(1/4, x, 3/4)',
6598 '(-x+1/2, 3/4, 1/4)', '(x, 3/4, 1/4)',
6599 '(3/4, 1/4, x)', '(3/4, 1/4, -x+1/2)')),
6600 '24i': (2, ('(1/2, y, y+1/2)', '(0, -y+1/2, y+1/2)',
6601 '(0, y, -y)', '(1/2, -y+1/2, -y)',
6602 '(y+1/2, 1/2, y)', '(y+1/2, 0, -y+1/2)',
6603 '(-y, 0, y)', '(-y, 1/2, -y+1/2)',
6604 '(y, y+1/2, 1/2)', '(-y+1/2, y+1/2, 0)',
6605 '(y, -y, 0)', '(-y+1/2, -y, 1/2)',
6606 '(1/2, -y, -y+1/2)', '(0, y+1/2, -y+1/2)',
6607 '(0, -y, y)', '(1/2, y+1/2, y)',
6608 '(-y+1/2, 1/2, -y)', '(-y+1/2, 0, y+1/2)',
6609 '(y, 0, -y)', '(y, 1/2, y+1/2)',
6610 '(-y, -y+1/2, 1/2)', '(y+1/2, -y+1/2, 0)',
6611 '(-y, y, 0)', '(y+1/2, y, 1/2)')),
6612 '24j': (2, ('(1/2, y, -y)', '(0, -y+1/2, -y)',
6613 '(0, y, y+1/2)', '(1/2, -y+1/2, y+1/2)',
6614 '(-y, 1/2, y)', '(-y, 0, -y+1/2)',
6615 '(y+1/2, 0, y)', '(y+1/2, 1/2, -y+1/2)',
6616 '(y, -y, 1/2)', '(-y+1/2, -y, 0)',
6617 '(y, y+1/2, 0)', '(-y+1/2, y+1/2, 1/2)',
6618 '(1/2, -y, y)', '(0, y+1/2, y)',
6619 '(0, -y, -y+1/2)', '(1/2, y+1/2, -y+1/2)',
6620 '(y, 1/2, -y)', '(y, 0, y+1/2)',
6621 '(-y+1/2, 0, -y)', '(-y+1/2, 1/2, y+1/2)',
6622 '(-y, y, 1/2)', '(y+1/2, y, 0)',
6623 '(-y, -y+1/2, 0)', '(y+1/2, -y+1/2, 1/2)')),
6624 '24k': (5, ('(x, x, z)', '(-x+1/2, -x+1/2, z)',
6625 '(-x+1/2, x, -z+1/2)', '(x, -x+1/2, -z+1/2)',
6626 '(z, x, x)', '(z, -x+1/2, -x+1/2)',
6627 '(-z+1/2, -x+1/2, x)', '(-z+1/2, x, -x+1/2)',
6628 '(x, z, x)', '(-x+1/2, z, -x+1/2)',
6629 '(x, -z+1/2, -x+1/2)', '(-x+1/2, -z+1/2, x)',
6630 '(x+1/2, x+1/2, -z)', '(-x, -x, -z)',
6631 '(x+1/2, -x, z+1/2)', '(-x, x+1/2, z+1/2)',
6632 '(x+1/2, z+1/2, -x)', '(-x, z+1/2, x+1/2)',
6633 '(-x, -z, -x)', '(x+1/2, -z, x+1/2)',
6634 '(z+1/2, x+1/2, -x)', '(z+1/2, -x, x+1/2)',
6635 '(-z, x+1/2, x+1/2)', '(-z, -x, -x)')),
6636 '48l': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
6637 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
6638 '(z, x, y)', '(z, -x+1/2, -y+1/2)',
6639 '(-z+1/2, -x+1/2, y)', '(-z+1/2, x, -y+1/2)',
6640 '(y, z, x)', '(-y+1/2, z, -x+1/2)',
6641 '(y, -z+1/2, -x+1/2)', '(-y+1/2, -z+1/2, x)',
6642 '(y+1/2, x+1/2, -z)', '(-y, -x, -z)',
6643 '(y+1/2, -x, z+1/2)', '(-y, x+1/2, z+1/2)',
6644 '(x+1/2, z+1/2, -y)', '(-x, z+1/2, y+1/2)',
6645 '(-x, -z, -y)', '(x+1/2, -z, y+1/2)',
6646 '(z+1/2, y+1/2, -x)', '(z+1/2, -y, x+1/2)',
6647 '(-z, y+1/2, x+1/2)', '(-z, -y, -x)',
6648 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
6649 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)',
6650 '(-z, -x, -y)', '(-z, x+1/2, y+1/2)',
6651 '(z+1/2, x+1/2, -y)', '(z+1/2, -x, y+1/2)',
6652 '(-y, -z, -x)', '(y+1/2, -z, x+1/2)',
6653 '(-y, z+1/2, x+1/2)', '(y+1/2, z+1/2, -x)',
6654 '(-y+1/2, -x+1/2, z)', '(y, x, z)',
6655 '(-y+1/2, x, -z+1/2)', '(y, -x+1/2, -z+1/2)',
6656 '(-x+1/2, -z+1/2, y)', '(x, -z+1/2, -y+1/2)',
6657 '(x, z, y)', '(-x+1/2, z, -y+1/2)',
6658 '(-z+1/2, -y+1/2, x)', '(-z+1/2, y, -x+1/2)',
6659 '(z, -y+1/2, -x+1/2)', '(z, y, x)'))},
6660 '225': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
6661 '(1/2, 1/2, 0)')),
6662 '4b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
6663 '(0, 0, 1/2)')),
6664 '8c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
6665 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
6666 '(1/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
6667 '(3/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
6668 '24d': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
6669 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
6670 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)',
6671 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
6672 '(3/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
6673 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
6674 '(3/4, 1/2, 3/4)', '(1/4, 1/4, 0)',
6675 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
6676 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
6677 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
6678 '24e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
6679 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
6680 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
6681 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
6682 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
6683 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
6684 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
6685 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
6686 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)')),
6687 '32f': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
6688 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
6689 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
6690 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
6691 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
6692 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
6693 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
6694 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
6695 '(x, x, -x)', '(x, x+1/2, -x+1/2)',
6696 '(x+1/2, x, -x+1/2)', '(x+1/2, x+1/2, -x)',
6697 '(-x, -x, -x)', '(-x, -x+1/2, -x+1/2)',
6698 '(-x+1/2, -x, -x+1/2)', '(-x+1/2, -x+1/2, -x)',
6699 '(x, -x, x)', '(x, -x+1/2, x+1/2)',
6700 '(x+1/2, -x, x+1/2)', '(x+1/2, -x+1/2, x)',
6701 '(-x, x, x)', '(-x, x+1/2, x+1/2)',
6702 '(-x+1/2, x, x+1/2)', '(-x+1/2, x+1/2, x)')),
6703 '48g': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
6704 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
6705 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
6706 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
6707 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
6708 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
6709 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
6710 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
6711 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
6712 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
6713 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
6714 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)',
6715 '(1/4, x, 3/4)', '(1/4, x+1/2, 1/4)',
6716 '(3/4, x, 1/4)', '(3/4, x+1/2, 3/4)',
6717 '(3/4, -x, 3/4)', '(3/4, -x+1/2, 1/4)',
6718 '(1/4, -x, 1/4)', '(1/4, -x+1/2, 3/4)',
6719 '(x, 1/4, 3/4)', '(x, 3/4, 1/4)',
6720 '(x+1/2, 1/4, 1/4)', '(x+1/2, 3/4, 3/4)',
6721 '(-x, 1/4, 1/4)', '(-x, 3/4, 3/4)',
6722 '(-x+1/2, 1/4, 3/4)', '(-x+1/2, 3/4, 1/4)',
6723 '(1/4, 1/4, -x)', '(1/4, 3/4, -x+1/2)',
6724 '(3/4, 1/4, -x+1/2)', '(3/4, 3/4, -x)',
6725 '(1/4, 3/4, x)', '(1/4, 1/4, x+1/2)',
6726 '(3/4, 3/4, x+1/2)', '(3/4, 1/4, x)')),
6727 '48h': (2, ('(0, y, y)', '(0, y+1/2, y+1/2)', '(1/2, y, y+1/2)',
6728 '(1/2, y+1/2, y)', '(0, -y, y)',
6729 '(0, -y+1/2, y+1/2)', '(1/2, -y, y+1/2)',
6730 '(1/2, -y+1/2, y)', '(0, y, -y)',
6731 '(0, y+1/2, -y+1/2)', '(1/2, y, -y+1/2)',
6732 '(1/2, y+1/2, -y)', '(0, -y, -y)',
6733 '(0, -y+1/2, -y+1/2)', '(1/2, -y, -y+1/2)',
6734 '(1/2, -y+1/2, -y)', '(y, 0, y)', '(y, 1/2, y+1/2)',
6735 '(y+1/2, 0, y+1/2)', '(y+1/2, 1/2, y)',
6736 '(y, 0, -y)', '(y, 1/2, -y+1/2)',
6737 '(y+1/2, 0, -y+1/2)', '(y+1/2, 1/2, -y)',
6738 '(-y, 0, y)', '(-y, 1/2, y+1/2)',
6739 '(-y+1/2, 0, y+1/2)', '(-y+1/2, 1/2, y)',
6740 '(-y, 0, -y)', '(-y, 1/2, -y+1/2)',
6741 '(-y+1/2, 0, -y+1/2)', '(-y+1/2, 1/2, -y)',
6742 '(y, y, 0)', '(y, y+1/2, 1/2)', '(y+1/2, y, 1/2)',
6743 '(y+1/2, y+1/2, 0)', '(-y, y, 0)',
6744 '(-y, y+1/2, 1/2)', '(-y+1/2, y, 1/2)',
6745 '(-y+1/2, y+1/2, 0)', '(y, -y, 0)',
6746 '(y, -y+1/2, 1/2)', '(y+1/2, -y, 1/2)',
6747 '(y+1/2, -y+1/2, 0)', '(-y, -y, 0)',
6748 '(-y, -y+1/2, 1/2)', '(-y+1/2, -y, 1/2)',
6749 '(-y+1/2, -y+1/2, 0)')),
6750 '48i': (2, ('(1/2, y, y)', '(1/2, y+1/2, y+1/2)',
6751 '(0, y, y+1/2)', '(0, y+1/2, y)', '(1/2, -y, y)',
6752 '(1/2, -y+1/2, y+1/2)', '(0, -y, y+1/2)',
6753 '(0, -y+1/2, y)', '(1/2, y, -y)',
6754 '(1/2, y+1/2, -y+1/2)', '(0, y, -y+1/2)',
6755 '(0, y+1/2, -y)', '(1/2, -y, -y)',
6756 '(1/2, -y+1/2, -y+1/2)', '(0, -y, -y+1/2)',
6757 '(0, -y+1/2, -y)', '(y, 1/2, y)', '(y, 0, y+1/2)',
6758 '(y+1/2, 1/2, y+1/2)', '(y+1/2, 0, y)',
6759 '(y, 1/2, -y)', '(y, 0, -y+1/2)',
6760 '(y+1/2, 1/2, -y+1/2)', '(y+1/2, 0, -y)',
6761 '(-y, 1/2, y)', '(-y, 0, y+1/2)',
6762 '(-y+1/2, 1/2, y+1/2)', '(-y+1/2, 0, y)',
6763 '(-y, 1/2, -y)', '(-y, 0, -y+1/2)',
6764 '(-y+1/2, 1/2, -y+1/2)', '(-y+1/2, 0, -y)',
6765 '(y, y, 1/2)', '(y, y+1/2, 0)', '(y+1/2, y, 0)',
6766 '(y+1/2, y+1/2, 1/2)', '(-y, y, 1/2)',
6767 '(-y, y+1/2, 0)', '(-y+1/2, y, 0)',
6768 '(-y+1/2, y+1/2, 1/2)', '(y, -y, 1/2)',
6769 '(y, -y+1/2, 0)', '(y+1/2, -y, 0)',
6770 '(y+1/2, -y+1/2, 1/2)', '(-y, -y, 1/2)',
6771 '(-y, -y+1/2, 0)', '(-y+1/2, -y, 0)',
6772 '(-y+1/2, -y+1/2, 1/2)')),
6773 '96j': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(1/2, y, z+1/2)',
6774 '(1/2, y+1/2, z)', '(0, -y, z)',
6775 '(0, -y+1/2, z+1/2)', '(1/2, -y, z+1/2)',
6776 '(1/2, -y+1/2, z)', '(0, y, -z)',
6777 '(0, y+1/2, -z+1/2)', '(1/2, y, -z+1/2)',
6778 '(1/2, y+1/2, -z)', '(0, -y, -z)',
6779 '(0, -y+1/2, -z+1/2)', '(1/2, -y, -z+1/2)',
6780 '(1/2, -y+1/2, -z)', '(z, 0, y)', '(z, 1/2, y+1/2)',
6781 '(z+1/2, 0, y+1/2)', '(z+1/2, 1/2, y)',
6782 '(z, 0, -y)', '(z, 1/2, -y+1/2)',
6783 '(z+1/2, 0, -y+1/2)', '(z+1/2, 1/2, -y)',
6784 '(-z, 0, y)', '(-z, 1/2, y+1/2)',
6785 '(-z+1/2, 0, y+1/2)', '(-z+1/2, 1/2, y)',
6786 '(-z, 0, -y)', '(-z, 1/2, -y+1/2)',
6787 '(-z+1/2, 0, -y+1/2)', '(-z+1/2, 1/2, -y)',
6788 '(y, z, 0)', '(y, z+1/2, 1/2)', '(y+1/2, z, 1/2)',
6789 '(y+1/2, z+1/2, 0)', '(-y, z, 0)',
6790 '(-y, z+1/2, 1/2)', '(-y+1/2, z, 1/2)',
6791 '(-y+1/2, z+1/2, 0)', '(y, -z, 0)',
6792 '(y, -z+1/2, 1/2)', '(y+1/2, -z, 1/2)',
6793 '(y+1/2, -z+1/2, 0)', '(-y, -z, 0)',
6794 '(-y, -z+1/2, 1/2)', '(-y+1/2, -z, 1/2)',
6795 '(-y+1/2, -z+1/2, 0)', '(y, 0, -z)',
6796 '(y, 1/2, -z+1/2)', '(y+1/2, 0, -z+1/2)',
6797 '(y+1/2, 1/2, -z)', '(-y, 0, -z)',
6798 '(-y, 1/2, -z+1/2)', '(-y+1/2, 0, -z+1/2)',
6799 '(-y+1/2, 1/2, -z)', '(y, 0, z)', '(y, 1/2, z+1/2)',
6800 '(y+1/2, 0, z+1/2)', '(y+1/2, 1/2, z)',
6801 '(-y, 0, z)', '(-y, 1/2, z+1/2)',
6802 '(-y+1/2, 0, z+1/2)', '(-y+1/2, 1/2, z)',
6803 '(0, z, -y)', '(0, z+1/2, -y+1/2)',
6804 '(1/2, z, -y+1/2)', '(1/2, z+1/2, -y)', '(0, z, y)',
6805 '(0, z+1/2, y+1/2)', '(1/2, z, y+1/2)',
6806 '(1/2, z+1/2, y)', '(0, -z, -y)',
6807 '(0, -z+1/2, -y+1/2)', '(1/2, -z, -y+1/2)',
6808 '(1/2, -z+1/2, -y)', '(0, -z, y)',
6809 '(0, -z+1/2, y+1/2)', '(1/2, -z, y+1/2)',
6810 '(1/2, -z+1/2, y)', '(z, y, 0)', '(z, y+1/2, 1/2)',
6811 '(z+1/2, y, 1/2)', '(z+1/2, y+1/2, 0)',
6812 '(z, -y, 0)', '(z, -y+1/2, 1/2)',
6813 '(z+1/2, -y, 1/2)', '(z+1/2, -y+1/2, 0)',
6814 '(-z, y, 0)', '(-z, y+1/2, 1/2)',
6815 '(-z+1/2, y, 1/2)', '(-z+1/2, y+1/2, 0)',
6816 '(-z, -y, 0)', '(-z, -y+1/2, 1/2)',
6817 '(-z+1/2, -y, 1/2)', '(-z+1/2, -y+1/2, 0)')),
6818 '96k': (5, ('(x, x, z)', '(x, x+1/2, z+1/2)',
6819 '(x+1/2, x, z+1/2)', '(x+1/2, x+1/2, z)',
6820 '(-x, -x, z)', '(-x, -x+1/2, z+1/2)',
6821 '(-x+1/2, -x, z+1/2)', '(-x+1/2, -x+1/2, z)',
6822 '(-x, x, -z)', '(-x, x+1/2, -z+1/2)',
6823 '(-x+1/2, x, -z+1/2)', '(-x+1/2, x+1/2, -z)',
6824 '(x, -x, -z)', '(x, -x+1/2, -z+1/2)',
6825 '(x+1/2, -x, -z+1/2)', '(x+1/2, -x+1/2, -z)',
6826 '(z, x, x)', '(z, x+1/2, x+1/2)',
6827 '(z+1/2, x, x+1/2)', '(z+1/2, x+1/2, x)',
6828 '(z, -x, -x)', '(z, -x+1/2, -x+1/2)',
6829 '(z+1/2, -x, -x+1/2)', '(z+1/2, -x+1/2, -x)',
6830 '(-z, -x, x)', '(-z, -x+1/2, x+1/2)',
6831 '(-z+1/2, -x, x+1/2)', '(-z+1/2, -x+1/2, x)',
6832 '(-z, x, -x)', '(-z, x+1/2, -x+1/2)',
6833 '(-z+1/2, x, -x+1/2)', '(-z+1/2, x+1/2, -x)',
6834 '(x, z, x)', '(x, z+1/2, x+1/2)',
6835 '(x+1/2, z, x+1/2)', '(x+1/2, z+1/2, x)',
6836 '(-x, z, -x)', '(-x, z+1/2, -x+1/2)',
6837 '(-x+1/2, z, -x+1/2)', '(-x+1/2, z+1/2, -x)',
6838 '(x, -z, -x)', '(x, -z+1/2, -x+1/2)',
6839 '(x+1/2, -z, -x+1/2)', '(x+1/2, -z+1/2, -x)',
6840 '(-x, -z, x)', '(-x, -z+1/2, x+1/2)',
6841 '(-x+1/2, -z, x+1/2)', '(-x+1/2, -z+1/2, x)',
6842 '(x, x, -z)', '(x, x+1/2, -z+1/2)',
6843 '(x+1/2, x, -z+1/2)', '(x+1/2, x+1/2, -z)',
6844 '(-x, -x, -z)', '(-x, -x+1/2, -z+1/2)',
6845 '(-x+1/2, -x, -z+1/2)', '(-x+1/2, -x+1/2, -z)',
6846 '(x, -x, z)', '(x, -x+1/2, z+1/2)',
6847 '(x+1/2, -x, z+1/2)', '(x+1/2, -x+1/2, z)',
6848 '(-x, x, z)', '(-x, x+1/2, z+1/2)',
6849 '(-x+1/2, x, z+1/2)', '(-x+1/2, x+1/2, z)',
6850 '(x, z, -x)', '(x, z+1/2, -x+1/2)',
6851 '(x+1/2, z, -x+1/2)', '(x+1/2, z+1/2, -x)',
6852 '(-x, z, x)', '(-x, z+1/2, x+1/2)',
6853 '(-x+1/2, z, x+1/2)', '(-x+1/2, z+1/2, x)',
6854 '(-x, -z, -x)', '(-x, -z+1/2, -x+1/2)',
6855 '(-x+1/2, -z, -x+1/2)', '(-x+1/2, -z+1/2, -x)',
6856 '(x, -z, x)', '(x, -z+1/2, x+1/2)',
6857 '(x+1/2, -z, x+1/2)', '(x+1/2, -z+1/2, x)',
6858 '(z, x, -x)', '(z, x+1/2, -x+1/2)',
6859 '(z+1/2, x, -x+1/2)', '(z+1/2, x+1/2, -x)',
6860 '(z, -x, x)', '(z, -x+1/2, x+1/2)',
6861 '(z+1/2, -x, x+1/2)', '(z+1/2, -x+1/2, x)',
6862 '(-z, x, x)', '(-z, x+1/2, x+1/2)',
6863 '(-z+1/2, x, x+1/2)', '(-z+1/2, x+1/2, x)',
6864 '(-z, -x, -x)', '(-z, -x+1/2, -x+1/2)',
6865 '(-z+1/2, -x, -x+1/2)', '(-z+1/2, -x+1/2, -x)')),
6866 '192l': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
6867 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
6868 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
6869 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
6870 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
6871 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
6872 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
6873 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
6874 '(z, x, y)', '(z, x+1/2, y+1/2)',
6875 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
6876 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
6877 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
6878 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
6879 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
6880 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
6881 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
6882 '(y, z, x)', '(y, z+1/2, x+1/2)',
6883 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
6884 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
6885 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
6886 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
6887 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
6888 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
6889 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
6890 '(y, x, -z)', '(y, x+1/2, -z+1/2)',
6891 '(y+1/2, x, -z+1/2)', '(y+1/2, x+1/2, -z)',
6892 '(-y, -x, -z)', '(-y, -x+1/2, -z+1/2)',
6893 '(-y+1/2, -x, -z+1/2)', '(-y+1/2, -x+1/2, -z)',
6894 '(y, -x, z)', '(y, -x+1/2, z+1/2)',
6895 '(y+1/2, -x, z+1/2)', '(y+1/2, -x+1/2, z)',
6896 '(-y, x, z)', '(-y, x+1/2, z+1/2)',
6897 '(-y+1/2, x, z+1/2)', '(-y+1/2, x+1/2, z)',
6898 '(x, z, -y)', '(x, z+1/2, -y+1/2)',
6899 '(x+1/2, z, -y+1/2)', '(x+1/2, z+1/2, -y)',
6900 '(-x, z, y)', '(-x, z+1/2, y+1/2)',
6901 '(-x+1/2, z, y+1/2)', '(-x+1/2, z+1/2, y)',
6902 '(-x, -z, -y)', '(-x, -z+1/2, -y+1/2)',
6903 '(-x+1/2, -z, -y+1/2)', '(-x+1/2, -z+1/2, -y)',
6904 '(x, -z, y)', '(x, -z+1/2, y+1/2)',
6905 '(x+1/2, -z, y+1/2)', '(x+1/2, -z+1/2, y)',
6906 '(z, y, -x)', '(z, y+1/2, -x+1/2)',
6907 '(z+1/2, y, -x+1/2)', '(z+1/2, y+1/2, -x)',
6908 '(z, -y, x)', '(z, -y+1/2, x+1/2)',
6909 '(z+1/2, -y, x+1/2)', '(z+1/2, -y+1/2, x)',
6910 '(-z, y, x)', '(-z, y+1/2, x+1/2)',
6911 '(-z+1/2, y, x+1/2)', '(-z+1/2, y+1/2, x)',
6912 '(-z, -y, -x)', '(-z, -y+1/2, -x+1/2)',
6913 '(-z+1/2, -y, -x+1/2)', '(-z+1/2, -y+1/2, -x)',
6914 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
6915 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
6916 '(x, y, -z)', '(x, y+1/2, -z+1/2)',
6917 '(x+1/2, y, -z+1/2)', '(x+1/2, y+1/2, -z)',
6918 '(x, -y, z)', '(x, -y+1/2, z+1/2)',
6919 '(x+1/2, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
6920 '(-x, y, z)', '(-x, y+1/2, z+1/2)',
6921 '(-x+1/2, y, z+1/2)', '(-x+1/2, y+1/2, z)',
6922 '(-z, -x, -y)', '(-z, -x+1/2, -y+1/2)',
6923 '(-z+1/2, -x, -y+1/2)', '(-z+1/2, -x+1/2, -y)',
6924 '(-z, x, y)', '(-z, x+1/2, y+1/2)',
6925 '(-z+1/2, x, y+1/2)', '(-z+1/2, x+1/2, y)',
6926 '(z, x, -y)', '(z, x+1/2, -y+1/2)',
6927 '(z+1/2, x, -y+1/2)', '(z+1/2, x+1/2, -y)',
6928 '(z, -x, y)', '(z, -x+1/2, y+1/2)',
6929 '(z+1/2, -x, y+1/2)', '(z+1/2, -x+1/2, y)',
6930 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
6931 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
6932 '(y, -z, x)', '(y, -z+1/2, x+1/2)',
6933 '(y+1/2, -z, x+1/2)', '(y+1/2, -z+1/2, x)',
6934 '(-y, z, x)', '(-y, z+1/2, x+1/2)',
6935 '(-y+1/2, z, x+1/2)', '(-y+1/2, z+1/2, x)',
6936 '(y, z, -x)', '(y, z+1/2, -x+1/2)',
6937 '(y+1/2, z, -x+1/2)', '(y+1/2, z+1/2, -x)',
6938 '(-y, -x, z)', '(-y, -x+1/2, z+1/2)',
6939 '(-y+1/2, -x, z+1/2)', '(-y+1/2, -x+1/2, z)',
6940 '(y, x, z)', '(y, x+1/2, z+1/2)',
6941 '(y+1/2, x, z+1/2)', '(y+1/2, x+1/2, z)',
6942 '(-y, x, -z)', '(-y, x+1/2, -z+1/2)',
6943 '(-y+1/2, x, -z+1/2)', '(-y+1/2, x+1/2, -z)',
6944 '(y, -x, -z)', '(y, -x+1/2, -z+1/2)',
6945 '(y+1/2, -x, -z+1/2)', '(y+1/2, -x+1/2, -z)',
6946 '(-x, -z, y)', '(-x, -z+1/2, y+1/2)',
6947 '(-x+1/2, -z, y+1/2)', '(-x+1/2, -z+1/2, y)',
6948 '(x, -z, -y)', '(x, -z+1/2, -y+1/2)',
6949 '(x+1/2, -z, -y+1/2)', '(x+1/2, -z+1/2, -y)',
6950 '(x, z, y)', '(x, z+1/2, y+1/2)',
6951 '(x+1/2, z, y+1/2)', '(x+1/2, z+1/2, y)',
6952 '(-x, z, -y)', '(-x, z+1/2, -y+1/2)',
6953 '(-x+1/2, z, -y+1/2)', '(-x+1/2, z+1/2, -y)',
6954 '(-z, -y, x)', '(-z, -y+1/2, x+1/2)',
6955 '(-z+1/2, -y, x+1/2)', '(-z+1/2, -y+1/2, x)',
6956 '(-z, y, -x)', '(-z, y+1/2, -x+1/2)',
6957 '(-z+1/2, y, -x+1/2)', '(-z+1/2, y+1/2, -x)',
6958 '(z, -y, -x)', '(z, -y+1/2, -x+1/2)',
6959 '(z+1/2, -y, -x+1/2)', '(z+1/2, -y+1/2, -x)',
6960 '(z, y, x)', '(z, y+1/2, x+1/2)',
6961 '(z+1/2, y, x+1/2)', '(z+1/2, y+1/2, x)'))},
6962 '226': {'8a': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
6963 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
6964 '(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
6965 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
6966 '8b': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
6967 '(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 0)',
6968 '(0, 1/2, 0)', '(0, 0, 1/2)')),
6969 '24c': (0, ('(1/4, 0, 0)', '(1/4, 1/2, 1/2)', '(3/4, 0, 1/2)',
6970 '(3/4, 1/2, 0)', '(3/4, 0, 0)', '(3/4, 1/2, 1/2)',
6971 '(1/4, 0, 1/2)', '(1/4, 1/2, 0)', '(0, 1/4, 0)',
6972 '(0, 3/4, 1/2)', '(1/2, 1/4, 1/2)', '(1/2, 3/4, 0)',
6973 '(0, 3/4, 0)', '(0, 1/4, 1/2)', '(1/2, 3/4, 1/2)',
6974 '(1/2, 1/4, 0)', '(0, 0, 1/4)', '(0, 1/2, 3/4)',
6975 '(1/2, 0, 3/4)', '(1/2, 1/2, 1/4)', '(0, 0, 3/4)',
6976 '(0, 1/2, 1/4)', '(1/2, 0, 1/4)', '(1/2, 1/2, 3/4)'
6977 )),
6978 '24d': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
6979 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
6980 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)',
6981 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
6982 '(3/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
6983 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
6984 '(3/4, 1/2, 3/4)', '(1/4, 1/4, 0)',
6985 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
6986 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
6987 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
6988 '48e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
6989 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
6990 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
6991 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
6992 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
6993 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
6994 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
6995 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
6996 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)',
6997 '(1/2, x+1/2, 1/2)', '(1/2, x, 0)', '(0, x+1/2, 0)',
6998 '(0, x, 1/2)', '(1/2, -x+1/2, 1/2)', '(1/2, -x, 0)',
6999 '(0, -x+1/2, 0)', '(0, -x, 1/2)',
7000 '(x+1/2, 1/2, 1/2)', '(x+1/2, 0, 0)', '(x, 1/2, 0)',
7001 '(x, 0, 1/2)', '(-x+1/2, 1/2, 1/2)',
7002 '(-x+1/2, 0, 0)', '(-x, 1/2, 0)', '(-x, 0, 1/2)',
7003 '(1/2, 1/2, -x+1/2)', '(1/2, 0, -x)',
7004 '(0, 1/2, -x)', '(0, 0, -x+1/2)',
7005 '(1/2, 1/2, x+1/2)', '(1/2, 0, x)', '(0, 1/2, x)',
7006 '(0, 0, x+1/2)')),
7007 '48f': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
7008 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
7009 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
7010 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
7011 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
7012 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
7013 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
7014 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
7015 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
7016 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
7017 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
7018 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)',
7019 '(-x, 3/4, 3/4)', '(-x, 1/4, 1/4)',
7020 '(-x+1/2, 3/4, 1/4)', '(-x+1/2, 1/4, 3/4)',
7021 '(x, 1/4, 3/4)', '(x, 3/4, 1/4)',
7022 '(x+1/2, 1/4, 1/4)', '(x+1/2, 3/4, 3/4)',
7023 '(3/4, -x, 3/4)', '(3/4, -x+1/2, 1/4)',
7024 '(1/4, -x, 1/4)', '(1/4, -x+1/2, 3/4)',
7025 '(3/4, x, 1/4)', '(3/4, x+1/2, 3/4)',
7026 '(1/4, x, 3/4)', '(1/4, x+1/2, 1/4)',
7027 '(3/4, 3/4, -x)', '(3/4, 1/4, -x+1/2)',
7028 '(1/4, 3/4, -x+1/2)', '(1/4, 1/4, -x)',
7029 '(1/4, 3/4, x)', '(1/4, 1/4, x+1/2)',
7030 '(3/4, 3/4, x+1/2)', '(3/4, 1/4, x)')),
7031 '64g': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
7032 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
7033 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
7034 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
7035 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
7036 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
7037 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
7038 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
7039 '(x+1/2, x+1/2, -x+1/2)', '(x+1/2, x, -x)',
7040 '(x, x+1/2, -x)', '(x, x, -x+1/2)',
7041 '(-x+1/2, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x)',
7042 '(-x, -x+1/2, -x)', '(-x, -x, -x+1/2)',
7043 '(x+1/2, -x+1/2, x+1/2)', '(x+1/2, -x, x)',
7044 '(x, -x+1/2, x)', '(x, -x, x+1/2)',
7045 '(-x+1/2, x+1/2, x+1/2)', '(-x+1/2, x, x)',
7046 '(-x, x+1/2, x)', '(-x, x, x+1/2)', '(-x, -x, -x)',
7047 '(-x, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x+1/2)',
7048 '(-x+1/2, -x+1/2, -x)', '(x, x, -x)',
7049 '(x, x+1/2, -x+1/2)', '(x+1/2, x, -x+1/2)',
7050 '(x+1/2, x+1/2, -x)', '(x, -x, x)',
7051 '(x, -x+1/2, x+1/2)', '(x+1/2, -x, x+1/2)',
7052 '(x+1/2, -x+1/2, x)', '(-x, x, x)',
7053 '(-x, x+1/2, x+1/2)', '(-x+1/2, x, x+1/2)',
7054 '(-x+1/2, x+1/2, x)', '(-x+1/2, -x+1/2, x+1/2)',
7055 '(-x+1/2, -x, x)', '(-x, -x+1/2, x)',
7056 '(-x, -x, x+1/2)', '(x+1/2, x+1/2, x+1/2)',
7057 '(x+1/2, x, x)', '(x, x+1/2, x)', '(x, x, x+1/2)',
7058 '(-x+1/2, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
7059 '(-x, x+1/2, -x)', '(-x, x, -x+1/2)',
7060 '(x+1/2, -x+1/2, -x+1/2)', '(x+1/2, -x, -x)',
7061 '(x, -x+1/2, -x)', '(x, -x, -x+1/2)')),
7062 '96h': (2, ('(1/4, y, y)', '(1/4, y+1/2, y+1/2)',
7063 '(3/4, y, y+1/2)', '(3/4, y+1/2, y)',
7064 '(3/4, -y, y)', '(3/4, -y+1/2, y+1/2)',
7065 '(1/4, -y, y+1/2)', '(1/4, -y+1/2, y)',
7066 '(3/4, y, -y)', '(3/4, y+1/2, -y+1/2)',
7067 '(1/4, y, -y+1/2)', '(1/4, y+1/2, -y)',
7068 '(1/4, -y, -y)', '(1/4, -y+1/2, -y+1/2)',
7069 '(3/4, -y, -y+1/2)', '(3/4, -y+1/2, -y)',
7070 '(y, 1/4, y)', '(y, 3/4, y+1/2)',
7071 '(y+1/2, 1/4, y+1/2)', '(y+1/2, 3/4, y)',
7072 '(y, 3/4, -y)', '(y, 1/4, -y+1/2)',
7073 '(y+1/2, 3/4, -y+1/2)', '(y+1/2, 1/4, -y)',
7074 '(-y, 3/4, y)', '(-y, 1/4, y+1/2)',
7075 '(-y+1/2, 3/4, y+1/2)', '(-y+1/2, 1/4, y)',
7076 '(-y, 1/4, -y)', '(-y, 3/4, -y+1/2)',
7077 '(-y+1/2, 1/4, -y+1/2)', '(-y+1/2, 3/4, -y)',
7078 '(y, y, 1/4)', '(y, y+1/2, 3/4)', '(y+1/2, y, 3/4)',
7079 '(y+1/2, y+1/2, 1/4)', '(-y, y, 3/4)',
7080 '(-y, y+1/2, 1/4)', '(-y+1/2, y, 1/4)',
7081 '(-y+1/2, y+1/2, 3/4)', '(y, -y, 3/4)',
7082 '(y, -y+1/2, 1/4)', '(y+1/2, -y, 1/4)',
7083 '(y+1/2, -y+1/2, 3/4)', '(-y, -y, 1/4)',
7084 '(-y, -y+1/2, 3/4)', '(-y+1/2, -y, 3/4)',
7085 '(-y+1/2, -y+1/2, 1/4)', '(3/4, -y, -y)',
7086 '(3/4, -y+1/2, -y+1/2)', '(1/4, -y, -y+1/2)',
7087 '(1/4, -y+1/2, -y)', '(1/4, y, -y)',
7088 '(1/4, y+1/2, -y+1/2)', '(3/4, y, -y+1/2)',
7089 '(3/4, y+1/2, -y)', '(1/4, -y, y)',
7090 '(1/4, -y+1/2, y+1/2)', '(3/4, -y, y+1/2)',
7091 '(3/4, -y+1/2, y)', '(3/4, y, y)',
7092 '(3/4, y+1/2, y+1/2)', '(1/4, y, y+1/2)',
7093 '(1/4, y+1/2, y)', '(-y, 3/4, -y)',
7094 '(-y, 1/4, -y+1/2)', '(-y+1/2, 3/4, -y+1/2)',
7095 '(-y+1/2, 1/4, -y)', '(-y, 1/4, y)',
7096 '(-y, 3/4, y+1/2)', '(-y+1/2, 1/4, y+1/2)',
7097 '(-y+1/2, 3/4, y)', '(y, 1/4, -y)',
7098 '(y, 3/4, -y+1/2)', '(y+1/2, 1/4, -y+1/2)',
7099 '(y+1/2, 3/4, -y)', '(y, 3/4, y)',
7100 '(y, 1/4, y+1/2)', '(y+1/2, 3/4, y+1/2)',
7101 '(y+1/2, 1/4, y)', '(-y, -y, 3/4)',
7102 '(-y, -y+1/2, 1/4)', '(-y+1/2, -y, 1/4)',
7103 '(-y+1/2, -y+1/2, 3/4)', '(y, -y, 1/4)',
7104 '(y, -y+1/2, 3/4)', '(y+1/2, -y, 3/4)',
7105 '(y+1/2, -y+1/2, 1/4)', '(-y, y, 1/4)',
7106 '(-y, y+1/2, 3/4)', '(-y+1/2, y, 3/4)',
7107 '(-y+1/2, y+1/2, 1/4)', '(y, y, 3/4)',
7108 '(y, y+1/2, 1/4)', '(y+1/2, y, 1/4)',
7109 '(y+1/2, y+1/2, 3/4)')),
7110 '96i': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(1/2, y, z+1/2)',
7111 '(1/2, y+1/2, z)', '(0, -y, z)',
7112 '(0, -y+1/2, z+1/2)', '(1/2, -y, z+1/2)',
7113 '(1/2, -y+1/2, z)', '(0, y, -z)',
7114 '(0, y+1/2, -z+1/2)', '(1/2, y, -z+1/2)',
7115 '(1/2, y+1/2, -z)', '(0, -y, -z)',
7116 '(0, -y+1/2, -z+1/2)', '(1/2, -y, -z+1/2)',
7117 '(1/2, -y+1/2, -z)', '(z, 0, y)', '(z, 1/2, y+1/2)',
7118 '(z+1/2, 0, y+1/2)', '(z+1/2, 1/2, y)',
7119 '(z, 0, -y)', '(z, 1/2, -y+1/2)',
7120 '(z+1/2, 0, -y+1/2)', '(z+1/2, 1/2, -y)',
7121 '(-z, 0, y)', '(-z, 1/2, y+1/2)',
7122 '(-z+1/2, 0, y+1/2)', '(-z+1/2, 1/2, y)',
7123 '(-z, 0, -y)', '(-z, 1/2, -y+1/2)',
7124 '(-z+1/2, 0, -y+1/2)', '(-z+1/2, 1/2, -y)',
7125 '(y, z, 0)', '(y, z+1/2, 1/2)', '(y+1/2, z, 1/2)',
7126 '(y+1/2, z+1/2, 0)', '(-y, z, 0)',
7127 '(-y, z+1/2, 1/2)', '(-y+1/2, z, 1/2)',
7128 '(-y+1/2, z+1/2, 0)', '(y, -z, 0)',
7129 '(y, -z+1/2, 1/2)', '(y+1/2, -z, 1/2)',
7130 '(y+1/2, -z+1/2, 0)', '(-y, -z, 0)',
7131 '(-y, -z+1/2, 1/2)', '(-y+1/2, -z, 1/2)',
7132 '(-y+1/2, -z+1/2, 0)', '(y+1/2, 1/2, -z+1/2)',
7133 '(y+1/2, 0, -z)', '(y, 1/2, -z)', '(y, 0, -z+1/2)',
7134 '(-y+1/2, 1/2, -z+1/2)', '(-y+1/2, 0, -z)',
7135 '(-y, 1/2, -z)', '(-y, 0, -z+1/2)',
7136 '(y+1/2, 1/2, z+1/2)', '(y+1/2, 0, z)',
7137 '(y, 1/2, z)', '(y, 0, z+1/2)',
7138 '(-y+1/2, 1/2, z+1/2)', '(-y+1/2, 0, z)',
7139 '(-y, 1/2, z)', '(-y, 0, z+1/2)',
7140 '(1/2, z+1/2, -y+1/2)', '(1/2, z, -y)',
7141 '(0, z+1/2, -y)', '(0, z, -y+1/2)',
7142 '(1/2, z+1/2, y+1/2)', '(1/2, z, y)',
7143 '(0, z+1/2, y)', '(0, z, y+1/2)',
7144 '(1/2, -z+1/2, -y+1/2)', '(1/2, -z, -y)',
7145 '(0, -z+1/2, -y)', '(0, -z, -y+1/2)',
7146 '(1/2, -z+1/2, y+1/2)', '(1/2, -z, y)',
7147 '(0, -z+1/2, y)', '(0, -z, y+1/2)',
7148 '(z+1/2, y+1/2, 1/2)', '(z+1/2, y, 0)',
7149 '(z, y+1/2, 0)', '(z, y, 1/2)',
7150 '(z+1/2, -y+1/2, 1/2)', '(z+1/2, -y, 0)',
7151 '(z, -y+1/2, 0)', '(z, -y, 1/2)',
7152 '(-z+1/2, y+1/2, 1/2)', '(-z+1/2, y, 0)',
7153 '(-z, y+1/2, 0)', '(-z, y, 1/2)',
7154 '(-z+1/2, -y+1/2, 1/2)', '(-z+1/2, -y, 0)',
7155 '(-z, -y+1/2, 0)', '(-z, -y, 1/2)')),
7156 '192j': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
7157 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
7158 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
7159 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
7160 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
7161 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
7162 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
7163 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
7164 '(z, x, y)', '(z, x+1/2, y+1/2)',
7165 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
7166 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
7167 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
7168 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
7169 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
7170 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
7171 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
7172 '(y, z, x)', '(y, z+1/2, x+1/2)',
7173 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
7174 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
7175 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
7176 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
7177 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
7178 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
7179 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
7180 '(y+1/2, x+1/2, -z+1/2)', '(y+1/2, x, -z)',
7181 '(y, x+1/2, -z)', '(y, x, -z+1/2)',
7182 '(-y+1/2, -x+1/2, -z+1/2)', '(-y+1/2, -x, -z)',
7183 '(-y, -x+1/2, -z)', '(-y, -x, -z+1/2)',
7184 '(y+1/2, -x+1/2, z+1/2)', '(y+1/2, -x, z)',
7185 '(y, -x+1/2, z)', '(y, -x, z+1/2)',
7186 '(-y+1/2, x+1/2, z+1/2)', '(-y+1/2, x, z)',
7187 '(-y, x+1/2, z)', '(-y, x, z+1/2)',
7188 '(x+1/2, z+1/2, -y+1/2)', '(x+1/2, z, -y)',
7189 '(x, z+1/2, -y)', '(x, z, -y+1/2)',
7190 '(-x+1/2, z+1/2, y+1/2)', '(-x+1/2, z, y)',
7191 '(-x, z+1/2, y)', '(-x, z, y+1/2)',
7192 '(-x+1/2, -z+1/2, -y+1/2)', '(-x+1/2, -z, -y)',
7193 '(-x, -z+1/2, -y)', '(-x, -z, -y+1/2)',
7194 '(x+1/2, -z+1/2, y+1/2)', '(x+1/2, -z, y)',
7195 '(x, -z+1/2, y)', '(x, -z, y+1/2)',
7196 '(z+1/2, y+1/2, -x+1/2)', '(z+1/2, y, -x)',
7197 '(z, y+1/2, -x)', '(z, y, -x+1/2)',
7198 '(z+1/2, -y+1/2, x+1/2)', '(z+1/2, -y, x)',
7199 '(z, -y+1/2, x)', '(z, -y, x+1/2)',
7200 '(-z+1/2, y+1/2, x+1/2)', '(-z+1/2, y, x)',
7201 '(-z, y+1/2, x)', '(-z, y, x+1/2)',
7202 '(-z+1/2, -y+1/2, -x+1/2)', '(-z+1/2, -y, -x)',
7203 '(-z, -y+1/2, -x)', '(-z, -y, -x+1/2)',
7204 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
7205 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
7206 '(x, y, -z)', '(x, y+1/2, -z+1/2)',
7207 '(x+1/2, y, -z+1/2)', '(x+1/2, y+1/2, -z)',
7208 '(x, -y, z)', '(x, -y+1/2, z+1/2)',
7209 '(x+1/2, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
7210 '(-x, y, z)', '(-x, y+1/2, z+1/2)',
7211 '(-x+1/2, y, z+1/2)', '(-x+1/2, y+1/2, z)',
7212 '(-z, -x, -y)', '(-z, -x+1/2, -y+1/2)',
7213 '(-z+1/2, -x, -y+1/2)', '(-z+1/2, -x+1/2, -y)',
7214 '(-z, x, y)', '(-z, x+1/2, y+1/2)',
7215 '(-z+1/2, x, y+1/2)', '(-z+1/2, x+1/2, y)',
7216 '(z, x, -y)', '(z, x+1/2, -y+1/2)',
7217 '(z+1/2, x, -y+1/2)', '(z+1/2, x+1/2, -y)',
7218 '(z, -x, y)', '(z, -x+1/2, y+1/2)',
7219 '(z+1/2, -x, y+1/2)', '(z+1/2, -x+1/2, y)',
7220 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
7221 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
7222 '(y, -z, x)', '(y, -z+1/2, x+1/2)',
7223 '(y+1/2, -z, x+1/2)', '(y+1/2, -z+1/2, x)',
7224 '(-y, z, x)', '(-y, z+1/2, x+1/2)',
7225 '(-y+1/2, z, x+1/2)', '(-y+1/2, z+1/2, x)',
7226 '(y, z, -x)', '(y, z+1/2, -x+1/2)',
7227 '(y+1/2, z, -x+1/2)', '(y+1/2, z+1/2, -x)',
7228 '(-y+1/2, -x+1/2, z+1/2)', '(-y+1/2, -x, z)',
7229 '(-y, -x+1/2, z)', '(-y, -x, z+1/2)',
7230 '(y+1/2, x+1/2, z+1/2)', '(y+1/2, x, z)',
7231 '(y, x+1/2, z)', '(y, x, z+1/2)',
7232 '(-y+1/2, x+1/2, -z+1/2)', '(-y+1/2, x, -z)',
7233 '(-y, x+1/2, -z)', '(-y, x, -z+1/2)',
7234 '(y+1/2, -x+1/2, -z+1/2)', '(y+1/2, -x, -z)',
7235 '(y, -x+1/2, -z)', '(y, -x, -z+1/2)',
7236 '(-x+1/2, -z+1/2, y+1/2)', '(-x+1/2, -z, y)',
7237 '(-x, -z+1/2, y)', '(-x, -z, y+1/2)',
7238 '(x+1/2, -z+1/2, -y+1/2)', '(x+1/2, -z, -y)',
7239 '(x, -z+1/2, -y)', '(x, -z, -y+1/2)',
7240 '(x+1/2, z+1/2, y+1/2)', '(x+1/2, z, y)',
7241 '(x, z+1/2, y)', '(x, z, y+1/2)',
7242 '(-x+1/2, z+1/2, -y+1/2)', '(-x+1/2, z, -y)',
7243 '(-x, z+1/2, -y)', '(-x, z, -y+1/2)',
7244 '(-z+1/2, -y+1/2, x+1/2)', '(-z+1/2, -y, x)',
7245 '(-z, -y+1/2, x)', '(-z, -y, x+1/2)',
7246 '(-z+1/2, y+1/2, -x+1/2)', '(-z+1/2, y, -x)',
7247 '(-z, y+1/2, -x)', '(-z, y, -x+1/2)',
7248 '(z+1/2, -y+1/2, -x+1/2)', '(z+1/2, -y, -x)',
7249 '(z, -y+1/2, -x)', '(z, -y, -x+1/2)',
7250 '(z+1/2, y+1/2, x+1/2)', '(z+1/2, y, x)',
7251 '(z, y+1/2, x)', '(z, y, x+1/2)'))},
7252 '227:1': {'8a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
7253 '(1/2, 1/2, 0)', '(3/4, 1/4, 3/4)',
7254 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 1/4)',
7255 '(1/4, 3/4, 3/4)')),
7256 '8b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
7257 '(0, 0, 1/2)', '(1/4, 3/4, 1/4)',
7258 '(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)',
7259 '(3/4, 1/4, 1/4)')),
7260 '16c': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
7261 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
7262 '(7/8, 3/8, 5/8)', '(7/8, 7/8, 1/8)',
7263 '(3/8, 3/8, 1/8)', '(3/8, 7/8, 5/8)',
7264 '(3/8, 5/8, 7/8)', '(3/8, 1/8, 3/8)',
7265 '(7/8, 5/8, 3/8)', '(7/8, 1/8, 7/8)',
7266 '(5/8, 7/8, 3/8)', '(5/8, 3/8, 7/8)',
7267 '(1/8, 7/8, 7/8)', '(1/8, 3/8, 3/8)')),
7268 '16d': (0, ('(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
7269 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
7270 '(3/8, 7/8, 1/8)', '(3/8, 3/8, 5/8)',
7271 '(7/8, 7/8, 5/8)', '(7/8, 3/8, 1/8)',
7272 '(7/8, 1/8, 3/8)', '(7/8, 5/8, 7/8)',
7273 '(3/8, 1/8, 7/8)', '(3/8, 5/8, 3/8)',
7274 '(1/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)',
7275 '(5/8, 3/8, 3/8)', '(5/8, 7/8, 7/8)')),
7276 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
7277 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
7278 '(-x, -x+1/2, x+1/2)', '(-x, -x, x)',
7279 '(-x+1/2, -x+1/2, x)', '(-x+1/2, -x, x+1/2)',
7280 '(-x+1/2, x+1/2, -x)', '(-x+1/2, x, -x+1/2)',
7281 '(-x, x+1/2, -x+1/2)', '(-x, x, -x)',
7282 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
7283 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
7284 '(x+3/4, x+1/4, -x+3/4)',
7285 '(x+3/4, x+3/4, -x+1/4)',
7286 '(x+1/4, x+1/4, -x+1/4)',
7287 '(x+1/4, x+3/4, -x+3/4)',
7288 '(-x+1/4, -x+1/4, -x+1/4)',
7289 '(-x+1/4, -x+3/4, -x+3/4)',
7290 '(-x+3/4, -x+1/4, -x+3/4)',
7291 '(-x+3/4, -x+3/4, -x+1/4)',
7292 '(x+1/4, -x+3/4, x+3/4)',
7293 '(x+1/4, -x+1/4, x+1/4)',
7294 '(x+3/4, -x+3/4, x+1/4)',
7295 '(x+3/4, -x+1/4, x+3/4)',
7296 '(-x+3/4, x+3/4, x+1/4)',
7297 '(-x+3/4, x+1/4, x+3/4)',
7298 '(-x+1/4, x+3/4, x+3/4)',
7299 '(-x+1/4, x+1/4, x+1/4)')),
7300 '48f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
7301 '(x+1/2, 1/2, 0)', '(-x, 1/2, 1/2)', '(-x, 0, 0)',
7302 '(-x+1/2, 1/2, 0)', '(-x+1/2, 0, 1/2)',
7303 '(0, x, 0)', '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
7304 '(1/2, x+1/2, 0)', '(1/2, -x, 1/2)',
7305 '(1/2, -x+1/2, 0)', '(0, -x, 0)',
7306 '(0, -x+1/2, 1/2)', '(0, 0, x)',
7307 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
7308 '(1/2, 1/2, x)', '(1/2, 1/2, -x)',
7309 '(1/2, 0, -x+1/2)', '(0, 1/2, -x+1/2)',
7310 '(0, 0, -x)', '(3/4, x+1/4, 3/4)',
7311 '(3/4, x+3/4, 1/4)', '(1/4, x+1/4, 1/4)',
7312 '(1/4, x+3/4, 3/4)', '(1/4, -x+1/4, 1/4)',
7313 '(1/4, -x+3/4, 3/4)', '(3/4, -x+1/4, 3/4)',
7314 '(3/4, -x+3/4, 1/4)', '(x+3/4, 1/4, 3/4)',
7315 '(x+3/4, 3/4, 1/4)', '(x+1/4, 1/4, 1/4)',
7316 '(x+1/4, 3/4, 3/4)', '(-x+3/4, 3/4, 1/4)',
7317 '(-x+3/4, 1/4, 3/4)', '(-x+1/4, 3/4, 3/4)',
7318 '(-x+1/4, 1/4, 1/4)', '(3/4, 1/4, -x+3/4)',
7319 '(3/4, 3/4, -x+1/4)', '(1/4, 1/4, -x+1/4)',
7320 '(1/4, 3/4, -x+3/4)', '(1/4, 3/4, x+3/4)',
7321 '(1/4, 1/4, x+1/4)', '(3/4, 3/4, x+1/4)',
7322 '(3/4, 1/4, x+3/4)')),
7323 '96g': (5, ('(x, x, z)', '(x, x+1/2, z+1/2)',
7324 '(x+1/2, x, z+1/2)', '(x+1/2, x+1/2, z)',
7325 '(-x, -x+1/2, z+1/2)', '(-x, -x, z)',
7326 '(-x+1/2, -x+1/2, z)', '(-x+1/2, -x, z+1/2)',
7327 '(-x+1/2, x+1/2, -z)', '(-x+1/2, x, -z+1/2)',
7328 '(-x, x+1/2, -z+1/2)', '(-x, x, -z)',
7329 '(x+1/2, -x, -z+1/2)', '(x+1/2, -x+1/2, -z)',
7330 '(x, -x, -z)', '(x, -x+1/2, -z+1/2)', '(z, x, x)',
7331 '(z, x+1/2, x+1/2)', '(z+1/2, x, x+1/2)',
7332 '(z+1/2, x+1/2, x)', '(z+1/2, -x, -x+1/2)',
7333 '(z+1/2, -x+1/2, -x)', '(z, -x, -x)',
7334 '(z, -x+1/2, -x+1/2)', '(-z, -x+1/2, x+1/2)',
7335 '(-z, -x, x)', '(-z+1/2, -x+1/2, x)',
7336 '(-z+1/2, -x, x+1/2)', '(-z+1/2, x+1/2, -x)',
7337 '(-z+1/2, x, -x+1/2)', '(-z, x+1/2, -x+1/2)',
7338 '(-z, x, -x)', '(x, z, x)', '(x, z+1/2, x+1/2)',
7339 '(x+1/2, z, x+1/2)', '(x+1/2, z+1/2, x)',
7340 '(-x+1/2, z+1/2, -x)', '(-x+1/2, z, -x+1/2)',
7341 '(-x, z+1/2, -x+1/2)', '(-x, z, -x)',
7342 '(x+1/2, -z, -x+1/2)', '(x+1/2, -z+1/2, -x)',
7343 '(x, -z, -x)', '(x, -z+1/2, -x+1/2)',
7344 '(-x, -z+1/2, x+1/2)', '(-x, -z, x)',
7345 '(-x+1/2, -z+1/2, x)', '(-x+1/2, -z, x+1/2)',
7346 '(x+3/4, x+1/4, -z+3/4)',
7347 '(x+3/4, x+3/4, -z+1/4)',
7348 '(x+1/4, x+1/4, -z+1/4)',
7349 '(x+1/4, x+3/4, -z+3/4)',
7350 '(-x+1/4, -x+1/4, -z+1/4)',
7351 '(-x+1/4, -x+3/4, -z+3/4)',
7352 '(-x+3/4, -x+1/4, -z+3/4)',
7353 '(-x+3/4, -x+3/4, -z+1/4)',
7354 '(x+1/4, -x+3/4, z+3/4)',
7355 '(x+1/4, -x+1/4, z+1/4)',
7356 '(x+3/4, -x+3/4, z+1/4)',
7357 '(x+3/4, -x+1/4, z+3/4)',
7358 '(-x+3/4, x+3/4, z+1/4)',
7359 '(-x+3/4, x+1/4, z+3/4)',
7360 '(-x+1/4, x+3/4, z+3/4)',
7361 '(-x+1/4, x+1/4, z+1/4)',
7362 '(x+3/4, z+1/4, -x+3/4)',
7363 '(x+3/4, z+3/4, -x+1/4)',
7364 '(x+1/4, z+1/4, -x+1/4)',
7365 '(x+1/4, z+3/4, -x+3/4)',
7366 '(-x+3/4, z+3/4, x+1/4)',
7367 '(-x+3/4, z+1/4, x+3/4)',
7368 '(-x+1/4, z+3/4, x+3/4)',
7369 '(-x+1/4, z+1/4, x+1/4)',
7370 '(-x+1/4, -z+1/4, -x+1/4)',
7371 '(-x+1/4, -z+3/4, -x+3/4)',
7372 '(-x+3/4, -z+1/4, -x+3/4)',
7373 '(-x+3/4, -z+3/4, -x+1/4)',
7374 '(x+1/4, -z+3/4, x+3/4)',
7375 '(x+1/4, -z+1/4, x+1/4)',
7376 '(x+3/4, -z+3/4, x+1/4)',
7377 '(x+3/4, -z+1/4, x+3/4)',
7378 '(z+3/4, x+1/4, -x+3/4)',
7379 '(z+3/4, x+3/4, -x+1/4)',
7380 '(z+1/4, x+1/4, -x+1/4)',
7381 '(z+1/4, x+3/4, -x+3/4)',
7382 '(z+1/4, -x+3/4, x+3/4)',
7383 '(z+1/4, -x+1/4, x+1/4)',
7384 '(z+3/4, -x+3/4, x+1/4)',
7385 '(z+3/4, -x+1/4, x+3/4)',
7386 '(-z+3/4, x+3/4, x+1/4)',
7387 '(-z+3/4, x+1/4, x+3/4)',
7388 '(-z+1/4, x+3/4, x+3/4)',
7389 '(-z+1/4, x+1/4, x+1/4)',
7390 '(-z+1/4, -x+1/4, -x+1/4)',
7391 '(-z+1/4, -x+3/4, -x+3/4)',
7392 '(-z+3/4, -x+1/4, -x+3/4)',
7393 '(-z+3/4, -x+3/4, -x+1/4)')),
7394 '96h': (2, ('(1/8, y, -y+1/4)', '(1/8, y+1/2, -y+3/4)',
7395 '(5/8, y, -y+3/4)', '(5/8, y+1/2, -y+1/4)',
7396 '(7/8, -y+1/2, -y+3/4)', '(7/8, -y, -y+1/4)',
7397 '(3/8, -y+1/2, -y+1/4)', '(3/8, -y, -y+3/4)',
7398 '(3/8, y+1/2, y+3/4)', '(3/8, y, y+1/4)',
7399 '(7/8, y+1/2, y+1/4)', '(7/8, y, y+3/4)',
7400 '(5/8, -y, y+1/4)', '(5/8, -y+1/2, y+3/4)',
7401 '(1/8, -y, y+3/4)', '(1/8, -y+1/2, y+1/4)',
7402 '(-y+1/4, 1/8, y)', '(-y+1/4, 5/8, y+1/2)',
7403 '(-y+3/4, 1/8, y+1/2)', '(-y+3/4, 5/8, y)',
7404 '(-y+3/4, 7/8, -y+1/2)', '(-y+3/4, 3/8, -y)',
7405 '(-y+1/4, 7/8, -y)', '(-y+1/4, 3/8, -y+1/2)',
7406 '(y+3/4, 3/8, y+1/2)', '(y+3/4, 7/8, y)',
7407 '(y+1/4, 3/8, y)', '(y+1/4, 7/8, y+1/2)',
7408 '(y+1/4, 5/8, -y)', '(y+1/4, 1/8, -y+1/2)',
7409 '(y+3/4, 5/8, -y+1/2)', '(y+3/4, 1/8, -y)',
7410 '(y, -y+1/4, 1/8)', '(y, -y+3/4, 5/8)',
7411 '(y+1/2, -y+1/4, 5/8)', '(y+1/2, -y+3/4, 1/8)',
7412 '(-y+1/2, -y+3/4, 7/8)', '(-y+1/2, -y+1/4, 3/8)',
7413 '(-y, -y+3/4, 3/8)', '(-y, -y+1/4, 7/8)',
7414 '(y+1/2, y+3/4, 3/8)', '(y+1/2, y+1/4, 7/8)',
7415 '(y, y+3/4, 7/8)', '(y, y+1/4, 3/8)',
7416 '(-y, y+1/4, 5/8)', '(-y, y+3/4, 1/8)',
7417 '(-y+1/2, y+1/4, 1/8)', '(-y+1/2, y+3/4, 5/8)',
7418 '(1/8, -y+1/4, y)', '(1/8, -y+3/4, y+1/2)',
7419 '(5/8, -y+1/4, y+1/2)', '(5/8, -y+3/4, y)',
7420 '(3/8, y+3/4, y+1/2)', '(3/8, y+1/4, y)',
7421 '(7/8, y+3/4, y)', '(7/8, y+1/4, y+1/2)',
7422 '(7/8, -y+3/4, -y+1/2)', '(7/8, -y+1/4, -y)',
7423 '(3/8, -y+3/4, -y)', '(3/8, -y+1/4, -y+1/2)',
7424 '(5/8, y+1/4, -y)', '(5/8, y+3/4, -y+1/2)',
7425 '(1/8, y+1/4, -y+1/2)', '(1/8, y+3/4, -y)',
7426 '(y, 1/8, -y+1/4)', '(y, 5/8, -y+3/4)',
7427 '(y+1/2, 1/8, -y+3/4)', '(y+1/2, 5/8, -y+1/4)',
7428 '(y+1/2, 3/8, y+3/4)', '(y+1/2, 7/8, y+1/4)',
7429 '(y, 3/8, y+1/4)', '(y, 7/8, y+3/4)',
7430 '(-y+1/2, 7/8, -y+3/4)', '(-y+1/2, 3/8, -y+1/4)',
7431 '(-y, 7/8, -y+1/4)', '(-y, 3/8, -y+3/4)',
7432 '(-y, 5/8, y+1/4)', '(-y, 1/8, y+3/4)',
7433 '(-y+1/2, 5/8, y+3/4)', '(-y+1/2, 1/8, y+1/4)',
7434 '(-y+1/4, y, 1/8)', '(-y+1/4, y+1/2, 5/8)',
7435 '(-y+3/4, y, 5/8)', '(-y+3/4, y+1/2, 1/8)',
7436 '(y+3/4, y+1/2, 3/8)', '(y+3/4, y, 7/8)',
7437 '(y+1/4, y+1/2, 7/8)', '(y+1/4, y, 3/8)',
7438 '(-y+3/4, -y+1/2, 7/8)', '(-y+3/4, -y, 3/8)',
7439 '(-y+1/4, -y+1/2, 3/8)', '(-y+1/4, -y, 7/8)',
7440 '(y+1/4, -y, 5/8)', '(y+1/4, -y+1/2, 1/8)',
7441 '(y+3/4, -y, 1/8)', '(y+3/4, -y+1/2, 5/8)')),
7442 '192i': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
7443 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
7444 '(-x, -y+1/2, z+1/2)', '(-x, -y, z)',
7445 '(-x+1/2, -y+1/2, z)', '(-x+1/2, -y, z+1/2)',
7446 '(-x+1/2, y+1/2, -z)', '(-x+1/2, y, -z+1/2)',
7447 '(-x, y+1/2, -z+1/2)', '(-x, y, -z)',
7448 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
7449 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
7450 '(z, x, y)', '(z, x+1/2, y+1/2)',
7451 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
7452 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
7453 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
7454 '(-z, -x+1/2, y+1/2)', '(-z, -x, y)',
7455 '(-z+1/2, -x+1/2, y)', '(-z+1/2, -x, y+1/2)',
7456 '(-z+1/2, x+1/2, -y)', '(-z+1/2, x, -y+1/2)',
7457 '(-z, x+1/2, -y+1/2)', '(-z, x, -y)',
7458 '(y, z, x)', '(y, z+1/2, x+1/2)',
7459 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
7460 '(-y+1/2, z+1/2, -x)', '(-y+1/2, z, -x+1/2)',
7461 '(-y, z+1/2, -x+1/2)', '(-y, z, -x)',
7462 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
7463 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
7464 '(-y, -z+1/2, x+1/2)', '(-y, -z, x)',
7465 '(-y+1/2, -z+1/2, x)', '(-y+1/2, -z, x+1/2)',
7466 '(y+3/4, x+1/4, -z+3/4)',
7467 '(y+3/4, x+3/4, -z+1/4)',
7468 '(y+1/4, x+1/4, -z+1/4)',
7469 '(y+1/4, x+3/4, -z+3/4)',
7470 '(-y+1/4, -x+1/4, -z+1/4)',
7471 '(-y+1/4, -x+3/4, -z+3/4)',
7472 '(-y+3/4, -x+1/4, -z+3/4)',
7473 '(-y+3/4, -x+3/4, -z+1/4)',
7474 '(y+1/4, -x+3/4, z+3/4)',
7475 '(y+1/4, -x+1/4, z+1/4)',
7476 '(y+3/4, -x+3/4, z+1/4)',
7477 '(y+3/4, -x+1/4, z+3/4)',
7478 '(-y+3/4, x+3/4, z+1/4)',
7479 '(-y+3/4, x+1/4, z+3/4)',
7480 '(-y+1/4, x+3/4, z+3/4)',
7481 '(-y+1/4, x+1/4, z+1/4)',
7482 '(x+3/4, z+1/4, -y+3/4)',
7483 '(x+3/4, z+3/4, -y+1/4)',
7484 '(x+1/4, z+1/4, -y+1/4)',
7485 '(x+1/4, z+3/4, -y+3/4)',
7486 '(-x+3/4, z+3/4, y+1/4)',
7487 '(-x+3/4, z+1/4, y+3/4)',
7488 '(-x+1/4, z+3/4, y+3/4)',
7489 '(-x+1/4, z+1/4, y+1/4)',
7490 '(-x+1/4, -z+1/4, -y+1/4)',
7491 '(-x+1/4, -z+3/4, -y+3/4)',
7492 '(-x+3/4, -z+1/4, -y+3/4)',
7493 '(-x+3/4, -z+3/4, -y+1/4)',
7494 '(x+1/4, -z+3/4, y+3/4)',
7495 '(x+1/4, -z+1/4, y+1/4)',
7496 '(x+3/4, -z+3/4, y+1/4)',
7497 '(x+3/4, -z+1/4, y+3/4)',
7498 '(z+3/4, y+1/4, -x+3/4)',
7499 '(z+3/4, y+3/4, -x+1/4)',
7500 '(z+1/4, y+1/4, -x+1/4)',
7501 '(z+1/4, y+3/4, -x+3/4)',
7502 '(z+1/4, -y+3/4, x+3/4)',
7503 '(z+1/4, -y+1/4, x+1/4)',
7504 '(z+3/4, -y+3/4, x+1/4)',
7505 '(z+3/4, -y+1/4, x+3/4)',
7506 '(-z+3/4, y+3/4, x+1/4)',
7507 '(-z+3/4, y+1/4, x+3/4)',
7508 '(-z+1/4, y+3/4, x+3/4)',
7509 '(-z+1/4, y+1/4, x+1/4)',
7510 '(-z+1/4, -y+1/4, -x+1/4)',
7511 '(-z+1/4, -y+3/4, -x+3/4)',
7512 '(-z+3/4, -y+1/4, -x+3/4)',
7513 '(-z+3/4, -y+3/4, -x+1/4)',
7514 '(-x+1/4, -y+1/4, -z+1/4)',
7515 '(-x+1/4, -y+3/4, -z+3/4)',
7516 '(-x+3/4, -y+1/4, -z+3/4)',
7517 '(-x+3/4, -y+3/4, -z+1/4)',
7518 '(x+1/4, y+3/4, -z+3/4)',
7519 '(x+1/4, y+1/4, -z+1/4)',
7520 '(x+3/4, y+3/4, -z+1/4)',
7521 '(x+3/4, y+1/4, -z+3/4)',
7522 '(x+3/4, -y+3/4, z+1/4)',
7523 '(x+3/4, -y+1/4, z+3/4)',
7524 '(x+1/4, -y+3/4, z+3/4)',
7525 '(x+1/4, -y+1/4, z+1/4)',
7526 '(-x+3/4, y+1/4, z+3/4)',
7527 '(-x+3/4, y+3/4, z+1/4)',
7528 '(-x+1/4, y+1/4, z+1/4)',
7529 '(-x+1/4, y+3/4, z+3/4)',
7530 '(-z+1/4, -x+1/4, -y+1/4)',
7531 '(-z+1/4, -x+3/4, -y+3/4)',
7532 '(-z+3/4, -x+1/4, -y+3/4)',
7533 '(-z+3/4, -x+3/4, -y+1/4)',
7534 '(-z+3/4, x+1/4, y+3/4)',
7535 '(-z+3/4, x+3/4, y+1/4)',
7536 '(-z+1/4, x+1/4, y+1/4)',
7537 '(-z+1/4, x+3/4, y+3/4)',
7538 '(z+1/4, x+3/4, -y+3/4)',
7539 '(z+1/4, x+1/4, -y+1/4)',
7540 '(z+3/4, x+3/4, -y+1/4)',
7541 '(z+3/4, x+1/4, -y+3/4)',
7542 '(z+3/4, -x+3/4, y+1/4)',
7543 '(z+3/4, -x+1/4, y+3/4)',
7544 '(z+1/4, -x+3/4, y+3/4)',
7545 '(z+1/4, -x+1/4, y+1/4)',
7546 '(-y+1/4, -z+1/4, -x+1/4)',
7547 '(-y+1/4, -z+3/4, -x+3/4)',
7548 '(-y+3/4, -z+1/4, -x+3/4)',
7549 '(-y+3/4, -z+3/4, -x+1/4)',
7550 '(y+3/4, -z+3/4, x+1/4)',
7551 '(y+3/4, -z+1/4, x+3/4)',
7552 '(y+1/4, -z+3/4, x+3/4)',
7553 '(y+1/4, -z+1/4, x+1/4)',
7554 '(-y+3/4, z+1/4, x+3/4)',
7555 '(-y+3/4, z+3/4, x+1/4)',
7556 '(-y+1/4, z+1/4, x+1/4)',
7557 '(-y+1/4, z+3/4, x+3/4)',
7558 '(y+1/4, z+3/4, -x+3/4)',
7559 '(y+1/4, z+1/4, -x+1/4)',
7560 '(y+3/4, z+3/4, -x+1/4)',
7561 '(y+3/4, z+1/4, -x+3/4)', '(-y+1/2, -x, z+1/2)',
7562 '(-y+1/2, -x+1/2, z)', '(-y, -x, z)',
7563 '(-y, -x+1/2, z+1/2)', '(y, x, z)',
7564 '(y, x+1/2, z+1/2)', '(y+1/2, x, z+1/2)',
7565 '(y+1/2, x+1/2, z)', '(-y, x+1/2, -z+1/2)',
7566 '(-y, x, -z)', '(-y+1/2, x+1/2, -z)',
7567 '(-y+1/2, x, -z+1/2)', '(y+1/2, -x+1/2, -z)',
7568 '(y+1/2, -x, -z+1/2)', '(y, -x+1/2, -z+1/2)',
7569 '(y, -x, -z)', '(-x+1/2, -z, y+1/2)',
7570 '(-x+1/2, -z+1/2, y)', '(-x, -z, y)',
7571 '(-x, -z+1/2, y+1/2)', '(x+1/2, -z+1/2, -y)',
7572 '(x+1/2, -z, -y+1/2)', '(x, -z+1/2, -y+1/2)',
7573 '(x, -z, -y)', '(x, z, y)', '(x, z+1/2, y+1/2)',
7574 '(x+1/2, z, y+1/2)', '(x+1/2, z+1/2, y)',
7575 '(-x, z+1/2, -y+1/2)', '(-x, z, -y)',
7576 '(-x+1/2, z+1/2, -y)', '(-x+1/2, z, -y+1/2)',
7577 '(-z+1/2, -y, x+1/2)', '(-z+1/2, -y+1/2, x)',
7578 '(-z, -y, x)', '(-z, -y+1/2, x+1/2)',
7579 '(-z, y+1/2, -x+1/2)', '(-z, y, -x)',
7580 '(-z+1/2, y+1/2, -x)', '(-z+1/2, y, -x+1/2)',
7581 '(z+1/2, -y+1/2, -x)', '(z+1/2, -y, -x+1/2)',
7582 '(z, -y+1/2, -x+1/2)', '(z, -y, -x)',
7583 '(z, y, x)', '(z, y+1/2, x+1/2)',
7584 '(z+1/2, y, x+1/2)', '(z+1/2, y+1/2, x)'))},
7585 '227:2': {'8a': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
7586 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
7587 '(7/8, 3/8, 3/8)', '(7/8, 7/8, 7/8)',
7588 '(3/8, 3/8, 7/8)', '(3/8, 7/8, 3/8)')),
7589 '8b': (0, ('(3/8, 3/8, 3/8)', '(3/8, 7/8, 7/8)',
7590 '(7/8, 3/8, 7/8)', '(7/8, 7/8, 3/8)',
7591 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
7592 '(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)')),
7593 '16c': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
7594 '(1/2, 1/2, 0)', '(3/4, 1/4, 1/2)',
7595 '(3/4, 3/4, 0)', '(1/4, 1/4, 0)',
7596 '(1/4, 3/4, 1/2)', '(1/4, 1/2, 3/4)',
7597 '(1/4, 0, 1/4)', '(3/4, 1/2, 1/4)',
7598 '(3/4, 0, 3/4)', '(1/2, 3/4, 1/4)',
7599 '(1/2, 1/4, 3/4)', '(0, 3/4, 3/4)',
7600 '(0, 1/4, 1/4)')),
7601 '16d': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
7602 '(0, 0, 1/2)', '(1/4, 3/4, 0)', '(1/4, 1/4, 1/2)',
7603 '(3/4, 3/4, 1/2)', '(3/4, 1/4, 0)',
7604 '(3/4, 0, 1/4)', '(3/4, 1/2, 3/4)',
7605 '(1/4, 0, 3/4)', '(1/4, 1/2, 1/4)',
7606 '(0, 1/4, 3/4)', '(0, 3/4, 1/4)',
7607 '(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)')),
7608 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
7609 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
7610 '(-x+3/4, -x+1/4, x+1/2)', '(-x+3/4, -x+3/4, x)',
7611 '(-x+1/4, -x+1/4, x)', '(-x+1/4, -x+3/4, x+1/2)',
7612 '(-x+1/4, x+1/2, -x+3/4)', '(-x+1/4, x, -x+1/4)',
7613 '(-x+3/4, x+1/2, -x+1/4)', '(-x+3/4, x, -x+3/4)',
7614 '(x+1/2, -x+3/4, -x+1/4)',
7615 '(x+1/2, -x+1/4, -x+3/4)', '(x, -x+3/4, -x+3/4)',
7616 '(x, -x+1/4, -x+1/4)', '(x+3/4, x+1/4, -x+1/2)',
7617 '(x+3/4, x+3/4, -x)', '(x+1/4, x+1/4, -x)',
7618 '(x+1/4, x+3/4, -x+1/2)', '(-x, -x, -x)',
7619 '(-x, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x+1/2)',
7620 '(-x+1/2, -x+1/2, -x)', '(x+1/4, -x+1/2, x+3/4)',
7621 '(x+1/4, -x, x+1/4)', '(x+3/4, -x+1/2, x+1/4)',
7622 '(x+3/4, -x, x+3/4)', '(-x+1/2, x+3/4, x+1/4)',
7623 '(-x+1/2, x+1/4, x+3/4)', '(-x, x+3/4, x+3/4)',
7624 '(-x, x+1/4, x+1/4)')),
7625 '48f': (1, ('(x, 1/8, 1/8)', '(x, 5/8, 5/8)',
7626 '(x+1/2, 1/8, 5/8)', '(x+1/2, 5/8, 1/8)',
7627 '(-x+3/4, 1/8, 5/8)', '(-x+3/4, 5/8, 1/8)',
7628 '(-x+1/4, 1/8, 1/8)', '(-x+1/4, 5/8, 5/8)',
7629 '(1/8, x, 1/8)', '(1/8, x+1/2, 5/8)',
7630 '(5/8, x, 5/8)', '(5/8, x+1/2, 1/8)',
7631 '(5/8, -x+3/4, 1/8)', '(5/8, -x+1/4, 5/8)',
7632 '(1/8, -x+3/4, 5/8)', '(1/8, -x+1/4, 1/8)',
7633 '(1/8, 1/8, x)', '(1/8, 5/8, x+1/2)',
7634 '(5/8, 1/8, x+1/2)', '(5/8, 5/8, x)',
7635 '(1/8, 5/8, -x+3/4)', '(1/8, 1/8, -x+1/4)',
7636 '(5/8, 5/8, -x+1/4)', '(5/8, 1/8, -x+3/4)',
7637 '(7/8, x+1/4, 3/8)', '(7/8, x+3/4, 7/8)',
7638 '(3/8, x+1/4, 7/8)', '(3/8, x+3/4, 3/8)',
7639 '(7/8, -x, 7/8)', '(7/8, -x+1/2, 3/8)',
7640 '(3/8, -x, 3/8)', '(3/8, -x+1/2, 7/8)',
7641 '(x+3/4, 3/8, 3/8)', '(x+3/4, 7/8, 7/8)',
7642 '(x+1/4, 3/8, 7/8)', '(x+1/4, 7/8, 3/8)',
7643 '(-x+1/2, 7/8, 3/8)', '(-x+1/2, 3/8, 7/8)',
7644 '(-x, 7/8, 7/8)', '(-x, 3/8, 3/8)',
7645 '(7/8, 3/8, -x+1/2)', '(7/8, 7/8, -x)',
7646 '(3/8, 3/8, -x)', '(3/8, 7/8, -x+1/2)',
7647 '(3/8, 3/8, x+3/4)', '(3/8, 7/8, x+1/4)',
7648 '(7/8, 3/8, x+1/4)', '(7/8, 7/8, x+3/4)')),
7649 '96g': (5, ('(x, x, z)', '(x, x+1/2, z+1/2)',
7650 '(x+1/2, x, z+1/2)', '(x+1/2, x+1/2, z)',
7651 '(-x+3/4, -x+1/4, z+1/2)', '(-x+3/4, -x+3/4, z)',
7652 '(-x+1/4, -x+1/4, z)', '(-x+1/4, -x+3/4, z+1/2)',
7653 '(-x+1/4, x+1/2, -z+3/4)', '(-x+1/4, x, -z+1/4)',
7654 '(-x+3/4, x+1/2, -z+1/4)', '(-x+3/4, x, -z+3/4)',
7655 '(x+1/2, -x+3/4, -z+1/4)',
7656 '(x+1/2, -x+1/4, -z+3/4)', '(x, -x+3/4, -z+3/4)',
7657 '(x, -x+1/4, -z+1/4)', '(z, x, x)',
7658 '(z, x+1/2, x+1/2)', '(z+1/2, x, x+1/2)',
7659 '(z+1/2, x+1/2, x)', '(z+1/2, -x+3/4, -x+1/4)',
7660 '(z+1/2, -x+1/4, -x+3/4)', '(z, -x+3/4, -x+3/4)',
7661 '(z, -x+1/4, -x+1/4)', '(-z+3/4, -x+1/4, x+1/2)',
7662 '(-z+3/4, -x+3/4, x)', '(-z+1/4, -x+1/4, x)',
7663 '(-z+1/4, -x+3/4, x+1/2)',
7664 '(-z+1/4, x+1/2, -x+3/4)', '(-z+1/4, x, -x+1/4)',
7665 '(-z+3/4, x+1/2, -x+1/4)', '(-z+3/4, x, -x+3/4)',
7666 '(x, z, x)', '(x, z+1/2, x+1/2)',
7667 '(x+1/2, z, x+1/2)', '(x+1/2, z+1/2, x)',
7668 '(-x+1/4, z+1/2, -x+3/4)', '(-x+1/4, z, -x+1/4)',
7669 '(-x+3/4, z+1/2, -x+1/4)', '(-x+3/4, z, -x+3/4)',
7670 '(x+1/2, -z+3/4, -x+1/4)',
7671 '(x+1/2, -z+1/4, -x+3/4)', '(x, -z+3/4, -x+3/4)',
7672 '(x, -z+1/4, -x+1/4)', '(-x+3/4, -z+1/4, x+1/2)',
7673 '(-x+3/4, -z+3/4, x)', '(-x+1/4, -z+1/4, x)',
7674 '(-x+1/4, -z+3/4, x+1/2)',
7675 '(x+3/4, x+1/4, -z+1/2)', '(x+3/4, x+3/4, -z)',
7676 '(x+1/4, x+1/4, -z)', '(x+1/4, x+3/4, -z+1/2)',
7677 '(-x, -x, -z)', '(-x, -x+1/2, -z+1/2)',
7678 '(-x+1/2, -x, -z+1/2)', '(-x+1/2, -x+1/2, -z)',
7679 '(x+1/4, -x+1/2, z+3/4)', '(x+1/4, -x, z+1/4)',
7680 '(x+3/4, -x+1/2, z+1/4)', '(x+3/4, -x, z+3/4)',
7681 '(-x+1/2, x+3/4, z+1/4)',
7682 '(-x+1/2, x+1/4, z+3/4)', '(-x, x+3/4, z+3/4)',
7683 '(-x, x+1/4, z+1/4)', '(x+3/4, z+1/4, -x+1/2)',
7684 '(x+3/4, z+3/4, -x)', '(x+1/4, z+1/4, -x)',
7685 '(x+1/4, z+3/4, -x+1/2)',
7686 '(-x+1/2, z+3/4, x+1/4)',
7687 '(-x+1/2, z+1/4, x+3/4)', '(-x, z+3/4, x+3/4)',
7688 '(-x, z+1/4, x+1/4)', '(-x, -z, -x)',
7689 '(-x, -z+1/2, -x+1/2)', '(-x+1/2, -z, -x+1/2)',
7690 '(-x+1/2, -z+1/2, -x)', '(x+1/4, -z+1/2, x+3/4)',
7691 '(x+1/4, -z, x+1/4)', '(x+3/4, -z+1/2, x+1/4)',
7692 '(x+3/4, -z, x+3/4)', '(z+3/4, x+1/4, -x+1/2)',
7693 '(z+3/4, x+3/4, -x)', '(z+1/4, x+1/4, -x)',
7694 '(z+1/4, x+3/4, -x+1/2)',
7695 '(z+1/4, -x+1/2, x+3/4)', '(z+1/4, -x, x+1/4)',
7696 '(z+3/4, -x+1/2, x+1/4)', '(z+3/4, -x, x+3/4)',
7697 '(-z+1/2, x+3/4, x+1/4)',
7698 '(-z+1/2, x+1/4, x+3/4)', '(-z, x+3/4, x+3/4)',
7699 '(-z, x+1/4, x+1/4)', '(-z, -x, -x)',
7700 '(-z, -x+1/2, -x+1/2)', '(-z+1/2, -x, -x+1/2)',
7701 '(-z+1/2, -x+1/2, -x)')),
7702 '96h': (2, ('(0, y, -y)', '(0, y+1/2, -y+1/2)',
7703 '(1/2, y, -y+1/2)', '(1/2, y+1/2, -y)',
7704 '(3/4, -y+1/4, -y+1/2)', '(3/4, -y+3/4, -y)',
7705 '(1/4, -y+1/4, -y)', '(1/4, -y+3/4, -y+1/2)',
7706 '(1/4, y+1/2, y+3/4)', '(1/4, y, y+1/4)',
7707 '(3/4, y+1/2, y+1/4)', '(3/4, y, y+3/4)',
7708 '(1/2, -y+3/4, y+1/4)', '(1/2, -y+1/4, y+3/4)',
7709 '(0, -y+3/4, y+3/4)', '(0, -y+1/4, y+1/4)',
7710 '(-y, 0, y)', '(-y, 1/2, y+1/2)',
7711 '(-y+1/2, 0, y+1/2)', '(-y+1/2, 1/2, y)',
7712 '(-y+1/2, 3/4, -y+1/4)', '(-y+1/2, 1/4, -y+3/4)',
7713 '(-y, 3/4, -y+3/4)', '(-y, 1/4, -y+1/4)',
7714 '(y+3/4, 1/4, y+1/2)', '(y+3/4, 3/4, y)',
7715 '(y+1/4, 1/4, y)', '(y+1/4, 3/4, y+1/2)',
7716 '(y+1/4, 1/2, -y+3/4)', '(y+1/4, 0, -y+1/4)',
7717 '(y+3/4, 1/2, -y+1/4)', '(y+3/4, 0, -y+3/4)',
7718 '(y, -y, 0)', '(y, -y+1/2, 1/2)',
7719 '(y+1/2, -y, 1/2)', '(y+1/2, -y+1/2, 0)',
7720 '(-y+1/4, -y+1/2, 3/4)', '(-y+1/4, -y, 1/4)',
7721 '(-y+3/4, -y+1/2, 1/4)', '(-y+3/4, -y, 3/4)',
7722 '(y+1/2, y+3/4, 1/4)', '(y+1/2, y+1/4, 3/4)',
7723 '(y, y+3/4, 3/4)', '(y, y+1/4, 1/4)',
7724 '(-y+3/4, y+1/4, 1/2)', '(-y+3/4, y+3/4, 0)',
7725 '(-y+1/4, y+1/4, 0)', '(-y+1/4, y+3/4, 1/2)',
7726 '(0, -y, y)', '(0, -y+1/2, y+1/2)',
7727 '(1/2, -y, y+1/2)', '(1/2, -y+1/2, y)',
7728 '(1/4, y+3/4, y+1/2)', '(1/4, y+1/4, y)',
7729 '(3/4, y+3/4, y)', '(3/4, y+1/4, y+1/2)',
7730 '(3/4, -y+1/2, -y+1/4)', '(3/4, -y, -y+3/4)',
7731 '(1/4, -y+1/2, -y+3/4)', '(1/4, -y, -y+1/4)',
7732 '(1/2, y+1/4, -y+3/4)', '(1/2, y+3/4, -y+1/4)',
7733 '(0, y+1/4, -y+1/4)', '(0, y+3/4, -y+3/4)',
7734 '(y, 0, -y)', '(y, 1/2, -y+1/2)',
7735 '(y+1/2, 0, -y+1/2)', '(y+1/2, 1/2, -y)',
7736 '(y+1/2, 1/4, y+3/4)', '(y+1/2, 3/4, y+1/4)',
7737 '(y, 1/4, y+1/4)', '(y, 3/4, y+3/4)',
7738 '(-y+1/4, 3/4, -y+1/2)', '(-y+1/4, 1/4, -y)',
7739 '(-y+3/4, 3/4, -y)', '(-y+3/4, 1/4, -y+1/2)',
7740 '(-y+3/4, 1/2, y+1/4)', '(-y+3/4, 0, y+3/4)',
7741 '(-y+1/4, 1/2, y+3/4)', '(-y+1/4, 0, y+1/4)',
7742 '(-y, y, 0)', '(-y, y+1/2, 1/2)',
7743 '(-y+1/2, y, 1/2)', '(-y+1/2, y+1/2, 0)',
7744 '(y+3/4, y+1/2, 1/4)', '(y+3/4, y, 3/4)',
7745 '(y+1/4, y+1/2, 3/4)', '(y+1/4, y, 1/4)',
7746 '(-y+1/2, -y+1/4, 3/4)', '(-y+1/2, -y+3/4, 1/4)',
7747 '(-y, -y+1/4, 1/4)', '(-y, -y+3/4, 3/4)',
7748 '(y+1/4, -y+3/4, 1/2)', '(y+1/4, -y+1/4, 0)',
7749 '(y+3/4, -y+3/4, 0)', '(y+3/4, -y+1/4, 1/2)')),
7750 '192i': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
7751 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
7752 '(-x+3/4, -y+1/4, z+1/2)', '(-x+3/4, -y+3/4, z)',
7753 '(-x+1/4, -y+1/4, z)', '(-x+1/4, -y+3/4, z+1/2)',
7754 '(-x+1/4, y+1/2, -z+3/4)', '(-x+1/4, y, -z+1/4)',
7755 '(-x+3/4, y+1/2, -z+1/4)', '(-x+3/4, y, -z+3/4)',
7756 '(x+1/2, -y+3/4, -z+1/4)',
7757 '(x+1/2, -y+1/4, -z+3/4)', '(x, -y+3/4, -z+3/4)',
7758 '(x, -y+1/4, -z+1/4)', '(z, x, y)',
7759 '(z, x+1/2, y+1/2)', '(z+1/2, x, y+1/2)',
7760 '(z+1/2, x+1/2, y)', '(z+1/2, -x+3/4, -y+1/4)',
7761 '(z+1/2, -x+1/4, -y+3/4)', '(z, -x+3/4, -y+3/4)',
7762 '(z, -x+1/4, -y+1/4)', '(-z+3/4, -x+1/4, y+1/2)',
7763 '(-z+3/4, -x+3/4, y)', '(-z+1/4, -x+1/4, y)',
7764 '(-z+1/4, -x+3/4, y+1/2)',
7765 '(-z+1/4, x+1/2, -y+3/4)', '(-z+1/4, x, -y+1/4)',
7766 '(-z+3/4, x+1/2, -y+1/4)', '(-z+3/4, x, -y+3/4)',
7767 '(y, z, x)', '(y, z+1/2, x+1/2)',
7768 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
7769 '(-y+1/4, z+1/2, -x+3/4)', '(-y+1/4, z, -x+1/4)',
7770 '(-y+3/4, z+1/2, -x+1/4)', '(-y+3/4, z, -x+3/4)',
7771 '(y+1/2, -z+3/4, -x+1/4)',
7772 '(y+1/2, -z+1/4, -x+3/4)', '(y, -z+3/4, -x+3/4)',
7773 '(y, -z+1/4, -x+1/4)', '(-y+3/4, -z+1/4, x+1/2)',
7774 '(-y+3/4, -z+3/4, x)', '(-y+1/4, -z+1/4, x)',
7775 '(-y+1/4, -z+3/4, x+1/2)',
7776 '(y+3/4, x+1/4, -z+1/2)', '(y+3/4, x+3/4, -z)',
7777 '(y+1/4, x+1/4, -z)', '(y+1/4, x+3/4, -z+1/2)',
7778 '(-y, -x, -z)', '(-y, -x+1/2, -z+1/2)',
7779 '(-y+1/2, -x, -z+1/2)', '(-y+1/2, -x+1/2, -z)',
7780 '(y+1/4, -x+1/2, z+3/4)', '(y+1/4, -x, z+1/4)',
7781 '(y+3/4, -x+1/2, z+1/4)', '(y+3/4, -x, z+3/4)',
7782 '(-y+1/2, x+3/4, z+1/4)',
7783 '(-y+1/2, x+1/4, z+3/4)', '(-y, x+3/4, z+3/4)',
7784 '(-y, x+1/4, z+1/4)', '(x+3/4, z+1/4, -y+1/2)',
7785 '(x+3/4, z+3/4, -y)', '(x+1/4, z+1/4, -y)',
7786 '(x+1/4, z+3/4, -y+1/2)',
7787 '(-x+1/2, z+3/4, y+1/4)',
7788 '(-x+1/2, z+1/4, y+3/4)', '(-x, z+3/4, y+3/4)',
7789 '(-x, z+1/4, y+1/4)', '(-x, -z, -y)',
7790 '(-x, -z+1/2, -y+1/2)', '(-x+1/2, -z, -y+1/2)',
7791 '(-x+1/2, -z+1/2, -y)', '(x+1/4, -z+1/2, y+3/4)',
7792 '(x+1/4, -z, y+1/4)', '(x+3/4, -z+1/2, y+1/4)',
7793 '(x+3/4, -z, y+3/4)', '(z+3/4, y+1/4, -x+1/2)',
7794 '(z+3/4, y+3/4, -x)', '(z+1/4, y+1/4, -x)',
7795 '(z+1/4, y+3/4, -x+1/2)',
7796 '(z+1/4, -y+1/2, x+3/4)', '(z+1/4, -y, x+1/4)',
7797 '(z+3/4, -y+1/2, x+1/4)', '(z+3/4, -y, x+3/4)',
7798 '(-z+1/2, y+3/4, x+1/4)',
7799 '(-z+1/2, y+1/4, x+3/4)', '(-z, y+3/4, x+3/4)',
7800 '(-z, y+1/4, x+1/4)', '(-z, -y, -x)',
7801 '(-z, -y+1/2, -x+1/2)', '(-z+1/2, -y, -x+1/2)',
7802 '(-z+1/2, -y+1/2, -x)', '(-x, -y, -z)',
7803 '(-x, -y+1/2, -z+1/2)', '(-x+1/2, -y, -z+1/2)',
7804 '(-x+1/2, -y+1/2, -z)', '(x+1/4, y+3/4, -z+1/2)',
7805 '(x+1/4, y+1/4, -z)', '(x+3/4, y+3/4, -z)',
7806 '(x+3/4, y+1/4, -z+1/2)',
7807 '(x+3/4, -y+1/2, z+1/4)', '(x+3/4, -y, z+3/4)',
7808 '(x+1/4, -y+1/2, z+3/4)', '(x+1/4, -y, z+1/4)',
7809 '(-x+1/2, y+1/4, z+3/4)',
7810 '(-x+1/2, y+3/4, z+1/4)', '(-x, y+1/4, z+1/4)',
7811 '(-x, y+3/4, z+3/4)', '(-z, -x, -y)',
7812 '(-z, -x+1/2, -y+1/2)', '(-z+1/2, -x, -y+1/2)',
7813 '(-z+1/2, -x+1/2, -y)', '(-z+1/2, x+1/4, y+3/4)',
7814 '(-z+1/2, x+3/4, y+1/4)', '(-z, x+1/4, y+1/4)',
7815 '(-z, x+3/4, y+3/4)', '(z+1/4, x+3/4, -y+1/2)',
7816 '(z+1/4, x+1/4, -y)', '(z+3/4, x+3/4, -y)',
7817 '(z+3/4, x+1/4, -y+1/2)',
7818 '(z+3/4, -x+1/2, y+1/4)', '(z+3/4, -x, y+3/4)',
7819 '(z+1/4, -x+1/2, y+3/4)', '(z+1/4, -x, y+1/4)',
7820 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
7821 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
7822 '(y+3/4, -z+1/2, x+1/4)', '(y+3/4, -z, x+3/4)',
7823 '(y+1/4, -z+1/2, x+3/4)', '(y+1/4, -z, x+1/4)',
7824 '(-y+1/2, z+1/4, x+3/4)',
7825 '(-y+1/2, z+3/4, x+1/4)', '(-y, z+1/4, x+1/4)',
7826 '(-y, z+3/4, x+3/4)', '(y+1/4, z+3/4, -x+1/2)',
7827 '(y+1/4, z+1/4, -x)', '(y+3/4, z+3/4, -x)',
7828 '(y+3/4, z+1/4, -x+1/2)',
7829 '(-y+1/4, -x+3/4, z+1/2)', '(-y+1/4, -x+1/4, z)',
7830 '(-y+3/4, -x+3/4, z)', '(-y+3/4, -x+1/4, z+1/2)',
7831 '(y, x, z)', '(y, x+1/2, z+1/2)',
7832 '(y+1/2, x, z+1/2)', '(y+1/2, x+1/2, z)',
7833 '(-y+3/4, x+1/2, -z+1/4)', '(-y+3/4, x, -z+3/4)',
7834 '(-y+1/4, x+1/2, -z+3/4)', '(-y+1/4, x, -z+1/4)',
7835 '(y+1/2, -x+1/4, -z+3/4)',
7836 '(y+1/2, -x+3/4, -z+1/4)', '(y, -x+1/4, -z+1/4)',
7837 '(y, -x+3/4, -z+3/4)', '(-x+1/4, -z+3/4, y+1/2)',
7838 '(-x+1/4, -z+1/4, y)', '(-x+3/4, -z+3/4, y)',
7839 '(-x+3/4, -z+1/4, y+1/2)',
7840 '(x+1/2, -z+1/4, -y+3/4)',
7841 '(x+1/2, -z+3/4, -y+1/4)', '(x, -z+1/4, -y+1/4)',
7842 '(x, -z+3/4, -y+3/4)', '(x, z, y)',
7843 '(x, z+1/2, y+1/2)', '(x+1/2, z, y+1/2)',
7844 '(x+1/2, z+1/2, y)', '(-x+3/4, z+1/2, -y+1/4)',
7845 '(-x+3/4, z, -y+3/4)', '(-x+1/4, z+1/2, -y+3/4)',
7846 '(-x+1/4, z, -y+1/4)', '(-z+1/4, -y+3/4, x+1/2)',
7847 '(-z+1/4, -y+1/4, x)', '(-z+3/4, -y+3/4, x)',
7848 '(-z+3/4, -y+1/4, x+1/2)',
7849 '(-z+3/4, y+1/2, -x+1/4)', '(-z+3/4, y, -x+3/4)',
7850 '(-z+1/4, y+1/2, -x+3/4)', '(-z+1/4, y, -x+1/4)',
7851 '(z+1/2, -y+1/4, -x+3/4)',
7852 '(z+1/2, -y+3/4, -x+1/4)', '(z, -y+1/4, -x+1/4)',
7853 '(z, -y+3/4, -x+3/4)', '(z, y, x)',
7854 '(z, y+1/2, x+1/2)', '(z+1/2, y, x+1/2)',
7855 '(z+1/2, y+1/2, x)'))},
7856 '228:1': {'16a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
7857 '(1/2, 1/2, 0)', '(3/4, 1/4, 3/4)',
7858 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 1/4)',
7859 '(1/4, 3/4, 3/4)', '(3/4, 3/4, 3/4)',
7860 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
7861 '(1/4, 1/4, 3/4)', '(0, 1/2, 0)', '(0, 0, 1/2)',
7862 '(1/2, 1/2, 1/2)', '(1/2, 0, 0)')),
7863 '32b': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
7864 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
7865 '(7/8, 3/8, 5/8)', '(7/8, 7/8, 1/8)',
7866 '(3/8, 3/8, 1/8)', '(3/8, 7/8, 5/8)',
7867 '(3/8, 5/8, 7/8)', '(3/8, 1/8, 3/8)',
7868 '(7/8, 5/8, 3/8)', '(7/8, 1/8, 7/8)',
7869 '(5/8, 7/8, 3/8)', '(5/8, 3/8, 7/8)',
7870 '(1/8, 7/8, 7/8)', '(1/8, 3/8, 3/8)',
7871 '(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
7872 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
7873 '(7/8, 3/8, 1/8)', '(7/8, 7/8, 5/8)',
7874 '(3/8, 3/8, 5/8)', '(3/8, 7/8, 1/8)',
7875 '(3/8, 1/8, 7/8)', '(3/8, 5/8, 3/8)',
7876 '(7/8, 1/8, 3/8)', '(7/8, 5/8, 7/8)',
7877 '(1/8, 7/8, 3/8)', '(1/8, 3/8, 7/8)',
7878 '(5/8, 7/8, 7/8)', '(5/8, 3/8, 3/8)')),
7879 '32c': (0, ('(3/8, 3/8, 3/8)', '(3/8, 7/8, 7/8)',
7880 '(7/8, 3/8, 7/8)', '(7/8, 7/8, 3/8)',
7881 '(5/8, 1/8, 7/8)', '(5/8, 5/8, 3/8)',
7882 '(1/8, 1/8, 3/8)', '(1/8, 5/8, 7/8)',
7883 '(1/8, 7/8, 5/8)', '(1/8, 3/8, 1/8)',
7884 '(5/8, 7/8, 1/8)', '(5/8, 3/8, 5/8)',
7885 '(7/8, 5/8, 1/8)', '(7/8, 1/8, 5/8)',
7886 '(3/8, 5/8, 5/8)', '(3/8, 1/8, 1/8)',
7887 '(1/8, 5/8, 3/8)', '(1/8, 1/8, 7/8)',
7888 '(5/8, 5/8, 7/8)', '(5/8, 1/8, 3/8)',
7889 '(7/8, 7/8, 7/8)', '(7/8, 3/8, 3/8)',
7890 '(3/8, 7/8, 3/8)', '(3/8, 3/8, 7/8)',
7891 '(5/8, 3/8, 1/8)', '(5/8, 7/8, 5/8)',
7892 '(1/8, 3/8, 5/8)', '(1/8, 7/8, 1/8)',
7893 '(3/8, 1/8, 5/8)', '(3/8, 5/8, 1/8)',
7894 '(7/8, 1/8, 1/8)', '(7/8, 5/8, 5/8)')),
7895 '48d': (0, ('(1/4, 0, 0)', '(1/4, 1/2, 1/2)', '(3/4, 0, 1/2)',
7896 '(3/4, 1/2, 0)', '(3/4, 1/2, 1/2)', '(3/4, 0, 0)',
7897 '(1/4, 1/2, 0)', '(1/4, 0, 1/2)', '(0, 1/4, 0)',
7898 '(0, 3/4, 1/2)', '(1/2, 1/4, 1/2)',
7899 '(1/2, 3/4, 0)', '(1/2, 3/4, 1/2)',
7900 '(1/2, 1/4, 0)', '(0, 3/4, 0)', '(0, 1/4, 1/2)',
7901 '(0, 0, 1/4)', '(0, 1/2, 3/4)', '(1/2, 0, 3/4)',
7902 '(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)',
7903 '(1/2, 0, 1/4)', '(0, 1/2, 1/4)', '(0, 0, 3/4)',
7904 '(3/4, 1/2, 3/4)', '(3/4, 0, 1/4)',
7905 '(1/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
7906 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)',
7907 '(3/4, 0, 3/4)', '(3/4, 1/2, 1/4)',
7908 '(0, 1/4, 3/4)', '(0, 3/4, 1/4)',
7909 '(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)',
7910 '(1/2, 3/4, 1/4)', '(1/2, 1/4, 3/4)',
7911 '(0, 3/4, 3/4)', '(0, 1/4, 1/4)',
7912 '(3/4, 1/4, 1/2)', '(3/4, 3/4, 0)',
7913 '(1/4, 1/4, 0)', '(1/4, 3/4, 1/2)',
7914 '(1/4, 3/4, 0)', '(1/4, 1/4, 1/2)',
7915 '(3/4, 3/4, 1/2)', '(3/4, 1/4, 0)')),
7916 '64e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
7917 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
7918 '(-x, -x+1/2, x+1/2)', '(-x, -x, x)',
7919 '(-x+1/2, -x+1/2, x)', '(-x+1/2, -x, x+1/2)',
7920 '(-x+1/2, x+1/2, -x)', '(-x+1/2, x, -x+1/2)',
7921 '(-x, x+1/2, -x+1/2)', '(-x, x, -x)',
7922 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
7923 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
7924 '(x+3/4, x+1/4, -x+3/4)',
7925 '(x+3/4, x+3/4, -x+1/4)',
7926 '(x+1/4, x+1/4, -x+1/4)',
7927 '(x+1/4, x+3/4, -x+3/4)',
7928 '(-x+1/4, -x+1/4, -x+1/4)',
7929 '(-x+1/4, -x+3/4, -x+3/4)',
7930 '(-x+3/4, -x+1/4, -x+3/4)',
7931 '(-x+3/4, -x+3/4, -x+1/4)',
7932 '(x+1/4, -x+3/4, x+3/4)',
7933 '(x+1/4, -x+1/4, x+1/4)',
7934 '(x+3/4, -x+3/4, x+1/4)',
7935 '(x+3/4, -x+1/4, x+3/4)',
7936 '(-x+3/4, x+3/4, x+1/4)',
7937 '(-x+3/4, x+1/4, x+3/4)',
7938 '(-x+1/4, x+3/4, x+3/4)',
7939 '(-x+1/4, x+1/4, x+1/4)',
7940 '(-x+3/4, -x+3/4, -x+3/4)',
7941 '(-x+3/4, -x+1/4, -x+1/4)',
7942 '(-x+1/4, -x+3/4, -x+1/4)',
7943 '(-x+1/4, -x+1/4, -x+3/4)',
7944 '(x+3/4, x+1/4, -x+1/4)',
7945 '(x+3/4, x+3/4, -x+3/4)',
7946 '(x+1/4, x+1/4, -x+3/4)',
7947 '(x+1/4, x+3/4, -x+1/4)',
7948 '(x+1/4, -x+1/4, x+3/4)',
7949 '(x+1/4, -x+3/4, x+1/4)',
7950 '(x+3/4, -x+1/4, x+1/4)',
7951 '(x+3/4, -x+3/4, x+3/4)',
7952 '(-x+1/4, x+3/4, x+1/4)',
7953 '(-x+1/4, x+1/4, x+3/4)',
7954 '(-x+3/4, x+3/4, x+3/4)',
7955 '(-x+3/4, x+1/4, x+1/4)', '(-x, -x+1/2, x)',
7956 '(-x, -x, x+1/2)', '(-x+1/2, -x+1/2, x+1/2)',
7957 '(-x+1/2, -x, x)', '(x+1/2, x+1/2, x+1/2)',
7958 '(x+1/2, x, x)', '(x, x+1/2, x)', '(x, x, x+1/2)',
7959 '(-x+1/2, x, -x)', '(-x+1/2, x+1/2, -x+1/2)',
7960 '(-x, x, -x+1/2)', '(-x, x+1/2, -x)',
7961 '(x, -x, -x+1/2)', '(x, -x+1/2, -x)',
7962 '(x+1/2, -x, -x)', '(x+1/2, -x+1/2, -x+1/2)')),
7963 '96f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
7964 '(x+1/2, 1/2, 0)', '(-x, 1/2, 1/2)', '(-x, 0, 0)',
7965 '(-x+1/2, 1/2, 0)', '(-x+1/2, 0, 1/2)',
7966 '(0, x, 0)', '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
7967 '(1/2, x+1/2, 0)', '(1/2, -x, 1/2)',
7968 '(1/2, -x+1/2, 0)', '(0, -x, 0)',
7969 '(0, -x+1/2, 1/2)', '(0, 0, x)',
7970 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
7971 '(1/2, 1/2, x)', '(1/2, 1/2, -x)',
7972 '(1/2, 0, -x+1/2)', '(0, 1/2, -x+1/2)',
7973 '(0, 0, -x)', '(3/4, x+1/4, 3/4)',
7974 '(3/4, x+3/4, 1/4)', '(1/4, x+1/4, 1/4)',
7975 '(1/4, x+3/4, 3/4)', '(1/4, -x+1/4, 1/4)',
7976 '(1/4, -x+3/4, 3/4)', '(3/4, -x+1/4, 3/4)',
7977 '(3/4, -x+3/4, 1/4)', '(x+3/4, 1/4, 3/4)',
7978 '(x+3/4, 3/4, 1/4)', '(x+1/4, 1/4, 1/4)',
7979 '(x+1/4, 3/4, 3/4)', '(-x+3/4, 3/4, 1/4)',
7980 '(-x+3/4, 1/4, 3/4)', '(-x+1/4, 3/4, 3/4)',
7981 '(-x+1/4, 1/4, 1/4)', '(3/4, 1/4, -x+3/4)',
7982 '(3/4, 3/4, -x+1/4)', '(1/4, 1/4, -x+1/4)',
7983 '(1/4, 3/4, -x+3/4)', '(1/4, 3/4, x+3/4)',
7984 '(1/4, 1/4, x+1/4)', '(3/4, 3/4, x+1/4)',
7985 '(3/4, 1/4, x+3/4)', '(-x+3/4, 3/4, 3/4)',
7986 '(-x+3/4, 1/4, 1/4)', '(-x+1/4, 3/4, 1/4)',
7987 '(-x+1/4, 1/4, 3/4)', '(x+3/4, 1/4, 1/4)',
7988 '(x+3/4, 3/4, 3/4)', '(x+1/4, 1/4, 3/4)',
7989 '(x+1/4, 3/4, 1/4)', '(3/4, -x+3/4, 3/4)',
7990 '(3/4, -x+1/4, 1/4)', '(1/4, -x+3/4, 1/4)',
7991 '(1/4, -x+1/4, 3/4)', '(1/4, x+3/4, 1/4)',
7992 '(1/4, x+1/4, 3/4)', '(3/4, x+3/4, 3/4)',
7993 '(3/4, x+1/4, 1/4)', '(3/4, 3/4, -x+3/4)',
7994 '(3/4, 1/4, -x+1/4)', '(1/4, 3/4, -x+1/4)',
7995 '(1/4, 1/4, -x+3/4)', '(1/4, 1/4, x+3/4)',
7996 '(1/4, 3/4, x+1/4)', '(3/4, 1/4, x+1/4)',
7997 '(3/4, 3/4, x+3/4)', '(0, -x+1/2, 0)',
7998 '(0, -x, 1/2)', '(1/2, -x+1/2, 1/2)',
7999 '(1/2, -x, 0)', '(1/2, x+1/2, 1/2)',
8000 '(1/2, x, 0)', '(0, x+1/2, 0)', '(0, x, 1/2)',
8001 '(-x, 1/2, 0)', '(-x, 0, 1/2)',
8002 '(-x+1/2, 1/2, 1/2)', '(-x+1/2, 0, 0)',
8003 '(x, 0, 1/2)', '(x, 1/2, 0)', '(x+1/2, 0, 0)',
8004 '(x+1/2, 1/2, 1/2)', '(0, 1/2, x)',
8005 '(0, 0, x+1/2)', '(1/2, 1/2, x+1/2)',
8006 '(1/2, 0, x)', '(1/2, 0, -x)',
8007 '(1/2, 1/2, -x+1/2)', '(0, 0, -x+1/2)',
8008 '(0, 1/2, -x)')),
8009 '96g': (2, ('(1/8, y, -y+1/4)', '(1/8, y+1/2, -y+3/4)',
8010 '(5/8, y, -y+3/4)', '(5/8, y+1/2, -y+1/4)',
8011 '(7/8, -y+1/2, -y+3/4)', '(7/8, -y, -y+1/4)',
8012 '(3/8, -y+1/2, -y+1/4)', '(3/8, -y, -y+3/4)',
8013 '(3/8, y+1/2, y+3/4)', '(3/8, y, y+1/4)',
8014 '(7/8, y+1/2, y+1/4)', '(7/8, y, y+3/4)',
8015 '(5/8, -y, y+1/4)', '(5/8, -y+1/2, y+3/4)',
8016 '(1/8, -y, y+3/4)', '(1/8, -y+1/2, y+1/4)',
8017 '(-y+1/4, 1/8, y)', '(-y+1/4, 5/8, y+1/2)',
8018 '(-y+3/4, 1/8, y+1/2)', '(-y+3/4, 5/8, y)',
8019 '(-y+3/4, 7/8, -y+1/2)', '(-y+3/4, 3/8, -y)',
8020 '(-y+1/4, 7/8, -y)', '(-y+1/4, 3/8, -y+1/2)',
8021 '(y+3/4, 3/8, y+1/2)', '(y+3/4, 7/8, y)',
8022 '(y+1/4, 3/8, y)', '(y+1/4, 7/8, y+1/2)',
8023 '(y+1/4, 5/8, -y)', '(y+1/4, 1/8, -y+1/2)',
8024 '(y+3/4, 5/8, -y+1/2)', '(y+3/4, 1/8, -y)',
8025 '(y, -y+1/4, 1/8)', '(y, -y+3/4, 5/8)',
8026 '(y+1/2, -y+1/4, 5/8)', '(y+1/2, -y+3/4, 1/8)',
8027 '(-y+1/2, -y+3/4, 7/8)', '(-y+1/2, -y+1/4, 3/8)',
8028 '(-y, -y+3/4, 3/8)', '(-y, -y+1/4, 7/8)',
8029 '(y+1/2, y+3/4, 3/8)', '(y+1/2, y+1/4, 7/8)',
8030 '(y, y+3/4, 7/8)', '(y, y+1/4, 3/8)',
8031 '(-y, y+1/4, 5/8)', '(-y, y+3/4, 1/8)',
8032 '(-y+1/2, y+1/4, 1/8)', '(-y+1/2, y+3/4, 5/8)',
8033 '(5/8, -y+3/4, y+1/2)', '(5/8, -y+1/4, y)',
8034 '(1/8, -y+3/4, y)', '(1/8, -y+1/4, y+1/2)',
8035 '(7/8, y+1/4, y)', '(7/8, y+3/4, y+1/2)',
8036 '(3/8, y+1/4, y+1/2)', '(3/8, y+3/4, y)',
8037 '(3/8, -y+1/4, -y)', '(3/8, -y+3/4, -y+1/2)',
8038 '(7/8, -y+1/4, -y+1/2)', '(7/8, -y+3/4, -y)',
8039 '(1/8, y+3/4, -y+1/2)', '(1/8, y+1/4, -y)',
8040 '(5/8, y+3/4, -y)', '(5/8, y+1/4, -y+1/2)',
8041 '(y+1/2, 5/8, -y+3/4)', '(y+1/2, 1/8, -y+1/4)',
8042 '(y, 5/8, -y+1/4)', '(y, 1/8, -y+3/4)',
8043 '(y, 7/8, y+1/4)', '(y, 3/8, y+3/4)',
8044 '(y+1/2, 7/8, y+3/4)', '(y+1/2, 3/8, y+1/4)',
8045 '(-y, 3/8, -y+1/4)', '(-y, 7/8, -y+3/4)',
8046 '(-y+1/2, 3/8, -y+3/4)', '(-y+1/2, 7/8, -y+1/4)',
8047 '(-y+1/2, 1/8, y+3/4)', '(-y+1/2, 5/8, y+1/4)',
8048 '(-y, 1/8, y+1/4)', '(-y, 5/8, y+3/4)',
8049 '(-y+3/4, y+1/2, 5/8)', '(-y+3/4, y, 1/8)',
8050 '(-y+1/4, y+1/2, 1/8)', '(-y+1/4, y, 5/8)',
8051 '(y+1/4, y, 7/8)', '(y+1/4, y+1/2, 3/8)',
8052 '(y+3/4, y, 3/8)', '(y+3/4, y+1/2, 7/8)',
8053 '(-y+1/4, -y, 3/8)', '(-y+1/4, -y+1/2, 7/8)',
8054 '(-y+3/4, -y, 7/8)', '(-y+3/4, -y+1/2, 3/8)',
8055 '(y+3/4, -y+1/2, 1/8)', '(y+3/4, -y, 5/8)',
8056 '(y+1/4, -y+1/2, 5/8)', '(y+1/4, -y, 1/8)')),
8057 '192h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
8058 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
8059 '(-x, -y+1/2, z+1/2)', '(-x, -y, z)',
8060 '(-x+1/2, -y+1/2, z)', '(-x+1/2, -y, z+1/2)',
8061 '(-x+1/2, y+1/2, -z)', '(-x+1/2, y, -z+1/2)',
8062 '(-x, y+1/2, -z+1/2)', '(-x, y, -z)',
8063 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
8064 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
8065 '(z, x, y)', '(z, x+1/2, y+1/2)',
8066 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
8067 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
8068 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
8069 '(-z, -x+1/2, y+1/2)', '(-z, -x, y)',
8070 '(-z+1/2, -x+1/2, y)', '(-z+1/2, -x, y+1/2)',
8071 '(-z+1/2, x+1/2, -y)', '(-z+1/2, x, -y+1/2)',
8072 '(-z, x+1/2, -y+1/2)', '(-z, x, -y)',
8073 '(y, z, x)', '(y, z+1/2, x+1/2)',
8074 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
8075 '(-y+1/2, z+1/2, -x)', '(-y+1/2, z, -x+1/2)',
8076 '(-y, z+1/2, -x+1/2)', '(-y, z, -x)',
8077 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
8078 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
8079 '(-y, -z+1/2, x+1/2)', '(-y, -z, x)',
8080 '(-y+1/2, -z+1/2, x)', '(-y+1/2, -z, x+1/2)',
8081 '(y+3/4, x+1/4, -z+3/4)',
8082 '(y+3/4, x+3/4, -z+1/4)',
8083 '(y+1/4, x+1/4, -z+1/4)',
8084 '(y+1/4, x+3/4, -z+3/4)',
8085 '(-y+1/4, -x+1/4, -z+1/4)',
8086 '(-y+1/4, -x+3/4, -z+3/4)',
8087 '(-y+3/4, -x+1/4, -z+3/4)',
8088 '(-y+3/4, -x+3/4, -z+1/4)',
8089 '(y+1/4, -x+3/4, z+3/4)',
8090 '(y+1/4, -x+1/4, z+1/4)',
8091 '(y+3/4, -x+3/4, z+1/4)',
8092 '(y+3/4, -x+1/4, z+3/4)',
8093 '(-y+3/4, x+3/4, z+1/4)',
8094 '(-y+3/4, x+1/4, z+3/4)',
8095 '(-y+1/4, x+3/4, z+3/4)',
8096 '(-y+1/4, x+1/4, z+1/4)',
8097 '(x+3/4, z+1/4, -y+3/4)',
8098 '(x+3/4, z+3/4, -y+1/4)',
8099 '(x+1/4, z+1/4, -y+1/4)',
8100 '(x+1/4, z+3/4, -y+3/4)',
8101 '(-x+3/4, z+3/4, y+1/4)',
8102 '(-x+3/4, z+1/4, y+3/4)',
8103 '(-x+1/4, z+3/4, y+3/4)',
8104 '(-x+1/4, z+1/4, y+1/4)',
8105 '(-x+1/4, -z+1/4, -y+1/4)',
8106 '(-x+1/4, -z+3/4, -y+3/4)',
8107 '(-x+3/4, -z+1/4, -y+3/4)',
8108 '(-x+3/4, -z+3/4, -y+1/4)',
8109 '(x+1/4, -z+3/4, y+3/4)',
8110 '(x+1/4, -z+1/4, y+1/4)',
8111 '(x+3/4, -z+3/4, y+1/4)',
8112 '(x+3/4, -z+1/4, y+3/4)',
8113 '(z+3/4, y+1/4, -x+3/4)',
8114 '(z+3/4, y+3/4, -x+1/4)',
8115 '(z+1/4, y+1/4, -x+1/4)',
8116 '(z+1/4, y+3/4, -x+3/4)',
8117 '(z+1/4, -y+3/4, x+3/4)',
8118 '(z+1/4, -y+1/4, x+1/4)',
8119 '(z+3/4, -y+3/4, x+1/4)',
8120 '(z+3/4, -y+1/4, x+3/4)',
8121 '(-z+3/4, y+3/4, x+1/4)',
8122 '(-z+3/4, y+1/4, x+3/4)',
8123 '(-z+1/4, y+3/4, x+3/4)',
8124 '(-z+1/4, y+1/4, x+1/4)',
8125 '(-z+1/4, -y+1/4, -x+1/4)',
8126 '(-z+1/4, -y+3/4, -x+3/4)',
8127 '(-z+3/4, -y+1/4, -x+3/4)',
8128 '(-z+3/4, -y+3/4, -x+1/4)',
8129 '(-x+3/4, -y+3/4, -z+3/4)',
8130 '(-x+3/4, -y+1/4, -z+1/4)',
8131 '(-x+1/4, -y+3/4, -z+1/4)',
8132 '(-x+1/4, -y+1/4, -z+3/4)',
8133 '(x+3/4, y+1/4, -z+1/4)',
8134 '(x+3/4, y+3/4, -z+3/4)',
8135 '(x+1/4, y+1/4, -z+3/4)',
8136 '(x+1/4, y+3/4, -z+1/4)',
8137 '(x+1/4, -y+1/4, z+3/4)',
8138 '(x+1/4, -y+3/4, z+1/4)',
8139 '(x+3/4, -y+1/4, z+1/4)',
8140 '(x+3/4, -y+3/4, z+3/4)',
8141 '(-x+1/4, y+3/4, z+1/4)',
8142 '(-x+1/4, y+1/4, z+3/4)',
8143 '(-x+3/4, y+3/4, z+3/4)',
8144 '(-x+3/4, y+1/4, z+1/4)',
8145 '(-z+3/4, -x+3/4, -y+3/4)',
8146 '(-z+3/4, -x+1/4, -y+1/4)',
8147 '(-z+1/4, -x+3/4, -y+1/4)',
8148 '(-z+1/4, -x+1/4, -y+3/4)',
8149 '(-z+1/4, x+3/4, y+1/4)',
8150 '(-z+1/4, x+1/4, y+3/4)',
8151 '(-z+3/4, x+3/4, y+3/4)',
8152 '(-z+3/4, x+1/4, y+1/4)',
8153 '(z+3/4, x+1/4, -y+1/4)',
8154 '(z+3/4, x+3/4, -y+3/4)',
8155 '(z+1/4, x+1/4, -y+3/4)',
8156 '(z+1/4, x+3/4, -y+1/4)',
8157 '(z+1/4, -x+1/4, y+3/4)',
8158 '(z+1/4, -x+3/4, y+1/4)',
8159 '(z+3/4, -x+1/4, y+1/4)',
8160 '(z+3/4, -x+3/4, y+3/4)',
8161 '(-y+3/4, -z+3/4, -x+3/4)',
8162 '(-y+3/4, -z+1/4, -x+1/4)',
8163 '(-y+1/4, -z+3/4, -x+1/4)',
8164 '(-y+1/4, -z+1/4, -x+3/4)',
8165 '(y+1/4, -z+1/4, x+3/4)',
8166 '(y+1/4, -z+3/4, x+1/4)',
8167 '(y+3/4, -z+1/4, x+1/4)',
8168 '(y+3/4, -z+3/4, x+3/4)',
8169 '(-y+1/4, z+3/4, x+1/4)',
8170 '(-y+1/4, z+1/4, x+3/4)',
8171 '(-y+3/4, z+3/4, x+3/4)',
8172 '(-y+3/4, z+1/4, x+1/4)',
8173 '(y+3/4, z+1/4, -x+1/4)',
8174 '(y+3/4, z+3/4, -x+3/4)',
8175 '(y+1/4, z+1/4, -x+3/4)',
8176 '(y+1/4, z+3/4, -x+1/4)', '(-y, -x+1/2, z)',
8177 '(-y, -x, z+1/2)', '(-y+1/2, -x+1/2, z+1/2)',
8178 '(-y+1/2, -x, z)', '(y+1/2, x+1/2, z+1/2)',
8179 '(y+1/2, x, z)', '(y, x+1/2, z)',
8180 '(y, x, z+1/2)', '(-y+1/2, x, -z)',
8181 '(-y+1/2, x+1/2, -z+1/2)', '(-y, x, -z+1/2)',
8182 '(-y, x+1/2, -z)', '(y, -x, -z+1/2)',
8183 '(y, -x+1/2, -z)', '(y+1/2, -x, -z)',
8184 '(y+1/2, -x+1/2, -z+1/2)', '(-x, -z+1/2, y)',
8185 '(-x, -z, y+1/2)', '(-x+1/2, -z+1/2, y+1/2)',
8186 '(-x+1/2, -z, y)', '(x, -z, -y+1/2)',
8187 '(x, -z+1/2, -y)', '(x+1/2, -z, -y)',
8188 '(x+1/2, -z+1/2, -y+1/2)',
8189 '(x+1/2, z+1/2, y+1/2)', '(x+1/2, z, y)',
8190 '(x, z+1/2, y)', '(x, z, y+1/2)',
8191 '(-x+1/2, z, -y)', '(-x+1/2, z+1/2, -y+1/2)',
8192 '(-x, z, -y+1/2)', '(-x, z+1/2, -y)',
8193 '(-z, -y+1/2, x)', '(-z, -y, x+1/2)',
8194 '(-z+1/2, -y+1/2, x+1/2)', '(-z+1/2, -y, x)',
8195 '(-z+1/2, y, -x)', '(-z+1/2, y+1/2, -x+1/2)',
8196 '(-z, y, -x+1/2)', '(-z, y+1/2, -x)',
8197 '(z, -y, -x+1/2)', '(z, -y+1/2, -x)',
8198 '(z+1/2, -y, -x)', '(z+1/2, -y+1/2, -x+1/2)',
8199 '(z+1/2, y+1/2, x+1/2)', '(z+1/2, y, x)',
8200 '(z, y+1/2, x)', '(z, y, x+1/2)'))},
8201 '228:2': {'16a': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
8202 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
8203 '(7/8, 3/8, 7/8)', '(7/8, 7/8, 3/8)',
8204 '(3/8, 3/8, 3/8)', '(3/8, 7/8, 7/8)',
8205 '(7/8, 7/8, 7/8)', '(7/8, 3/8, 3/8)',
8206 '(3/8, 7/8, 3/8)', '(3/8, 3/8, 7/8)',
8207 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
8208 '(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)')),
8209 '32b': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
8210 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
8211 '(0, 1/2, 3/4)', '(0, 0, 1/4)', '(1/2, 1/2, 1/4)',
8212 '(1/2, 0, 3/4)', '(1/2, 3/4, 0)',
8213 '(1/2, 1/4, 1/2)', '(0, 3/4, 1/2)', '(0, 1/4, 0)',
8214 '(3/4, 0, 1/2)', '(3/4, 1/2, 0)', '(1/4, 0, 0)',
8215 '(1/4, 1/2, 1/2)', '(3/4, 3/4, 3/4)',
8216 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
8217 '(1/4, 1/4, 3/4)', '(0, 1/2, 1/4)', '(0, 0, 3/4)',
8218 '(1/2, 1/2, 3/4)', '(1/2, 0, 1/4)',
8219 '(1/2, 1/4, 0)', '(1/2, 3/4, 1/2)',
8220 '(0, 1/4, 1/2)', '(0, 3/4, 0)', '(1/4, 0, 1/2)',
8221 '(1/4, 1/2, 0)', '(3/4, 0, 0)', '(3/4, 1/2, 1/2)'
8222 )),
8223 '32c': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
8224 '(1/2, 1/2, 0)', '(1/4, 3/4, 1/2)',
8225 '(1/4, 1/4, 0)', '(3/4, 3/4, 0)',
8226 '(3/4, 1/4, 1/2)', '(3/4, 1/2, 1/4)',
8227 '(3/4, 0, 3/4)', '(1/4, 1/2, 3/4)',
8228 '(1/4, 0, 1/4)', '(1/2, 1/4, 3/4)',
8229 '(1/2, 3/4, 1/4)', '(0, 1/4, 1/4)',
8230 '(0, 3/4, 3/4)', '(3/4, 1/4, 0)',
8231 '(3/4, 3/4, 1/2)', '(1/4, 1/4, 1/2)',
8232 '(1/4, 3/4, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 0)',
8233 '(0, 1/2, 0)', '(0, 0, 1/2)', '(1/4, 0, 3/4)',
8234 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
8235 '(3/4, 1/2, 3/4)', '(0, 3/4, 1/4)',
8236 '(0, 1/4, 3/4)', '(1/2, 3/4, 3/4)',
8237 '(1/2, 1/4, 1/4)')),
8238 '48d': (0, ('(7/8, 1/8, 1/8)', '(7/8, 5/8, 5/8)',
8239 '(3/8, 1/8, 5/8)', '(3/8, 5/8, 1/8)',
8240 '(3/8, 5/8, 5/8)', '(3/8, 1/8, 1/8)',
8241 '(7/8, 5/8, 1/8)', '(7/8, 1/8, 5/8)',
8242 '(1/8, 7/8, 1/8)', '(1/8, 3/8, 5/8)',
8243 '(5/8, 7/8, 5/8)', '(5/8, 3/8, 1/8)',
8244 '(5/8, 3/8, 5/8)', '(5/8, 7/8, 1/8)',
8245 '(1/8, 3/8, 1/8)', '(1/8, 7/8, 5/8)',
8246 '(1/8, 1/8, 7/8)', '(1/8, 5/8, 3/8)',
8247 '(5/8, 1/8, 3/8)', '(5/8, 5/8, 7/8)',
8248 '(5/8, 5/8, 3/8)', '(5/8, 1/8, 7/8)',
8249 '(1/8, 5/8, 7/8)', '(1/8, 1/8, 3/8)',
8250 '(7/8, 1/8, 7/8)', '(7/8, 5/8, 3/8)',
8251 '(3/8, 1/8, 3/8)', '(3/8, 5/8, 7/8)',
8252 '(3/8, 5/8, 3/8)', '(3/8, 1/8, 7/8)',
8253 '(7/8, 5/8, 7/8)', '(7/8, 1/8, 3/8)',
8254 '(5/8, 3/8, 7/8)', '(5/8, 7/8, 3/8)',
8255 '(1/8, 3/8, 3/8)', '(1/8, 7/8, 7/8)',
8256 '(1/8, 7/8, 3/8)', '(1/8, 3/8, 7/8)',
8257 '(5/8, 7/8, 7/8)', '(5/8, 3/8, 3/8)',
8258 '(7/8, 3/8, 1/8)', '(7/8, 7/8, 5/8)',
8259 '(3/8, 3/8, 5/8)', '(3/8, 7/8, 1/8)',
8260 '(3/8, 7/8, 5/8)', '(3/8, 3/8, 1/8)',
8261 '(7/8, 7/8, 1/8)', '(7/8, 3/8, 5/8)')),
8262 '64e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
8263 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
8264 '(-x+1/4, -x+3/4, x+1/2)', '(-x+1/4, -x+1/4, x)',
8265 '(-x+3/4, -x+3/4, x)', '(-x+3/4, -x+1/4, x+1/2)',
8266 '(-x+3/4, x+1/2, -x+1/4)', '(-x+3/4, x, -x+3/4)',
8267 '(-x+1/4, x+1/2, -x+3/4)', '(-x+1/4, x, -x+1/4)',
8268 '(x+1/2, -x+1/4, -x+3/4)',
8269 '(x+1/2, -x+3/4, -x+1/4)', '(x, -x+1/4, -x+1/4)',
8270 '(x, -x+3/4, -x+3/4)', '(x+3/4, x+1/4, -x)',
8271 '(x+3/4, x+3/4, -x+1/2)',
8272 '(x+1/4, x+1/4, -x+1/2)', '(x+1/4, x+3/4, -x)',
8273 '(-x+1/2, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x)',
8274 '(-x, -x+1/2, -x)', '(-x, -x, -x+1/2)',
8275 '(x+1/4, -x, x+3/4)', '(x+1/4, -x+1/2, x+1/4)',
8276 '(x+3/4, -x, x+1/4)', '(x+3/4, -x+1/2, x+3/4)',
8277 '(-x, x+3/4, x+1/4)', '(-x, x+1/4, x+3/4)',
8278 '(-x+1/2, x+3/4, x+3/4)',
8279 '(-x+1/2, x+1/4, x+1/4)', '(-x, -x, -x)',
8280 '(-x, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x+1/2)',
8281 '(-x+1/2, -x+1/2, -x)', '(x+3/4, x+1/4, -x+1/2)',
8282 '(x+3/4, x+3/4, -x)', '(x+1/4, x+1/4, -x)',
8283 '(x+1/4, x+3/4, -x+1/2)',
8284 '(x+1/4, -x+1/2, x+3/4)', '(x+1/4, -x, x+1/4)',
8285 '(x+3/4, -x+1/2, x+1/4)', '(x+3/4, -x, x+3/4)',
8286 '(-x+1/2, x+3/4, x+1/4)',
8287 '(-x+1/2, x+1/4, x+3/4)', '(-x, x+3/4, x+3/4)',
8288 '(-x, x+1/4, x+1/4)', '(-x+1/4, -x+3/4, x)',
8289 '(-x+1/4, -x+1/4, x+1/2)',
8290 '(-x+3/4, -x+3/4, x+1/2)', '(-x+3/4, -x+1/4, x)',
8291 '(x+1/2, x+1/2, x+1/2)', '(x+1/2, x, x)',
8292 '(x, x+1/2, x)', '(x, x, x+1/2)',
8293 '(-x+3/4, x, -x+1/4)', '(-x+3/4, x+1/2, -x+3/4)',
8294 '(-x+1/4, x, -x+3/4)', '(-x+1/4, x+1/2, -x+1/4)',
8295 '(x, -x+1/4, -x+3/4)', '(x, -x+3/4, -x+1/4)',
8296 '(x+1/2, -x+1/4, -x+1/4)',
8297 '(x+1/2, -x+3/4, -x+3/4)')),
8298 '96f': (1, ('(x, 1/8, 1/8)', '(x, 5/8, 5/8)',
8299 '(x+1/2, 1/8, 5/8)', '(x+1/2, 5/8, 1/8)',
8300 '(-x+1/4, 5/8, 5/8)', '(-x+1/4, 1/8, 1/8)',
8301 '(-x+3/4, 5/8, 1/8)', '(-x+3/4, 1/8, 5/8)',
8302 '(1/8, x, 1/8)', '(1/8, x+1/2, 5/8)',
8303 '(5/8, x, 5/8)', '(5/8, x+1/2, 1/8)',
8304 '(5/8, -x+1/4, 5/8)', '(5/8, -x+3/4, 1/8)',
8305 '(1/8, -x+1/4, 1/8)', '(1/8, -x+3/4, 5/8)',
8306 '(1/8, 1/8, x)', '(1/8, 5/8, x+1/2)',
8307 '(5/8, 1/8, x+1/2)', '(5/8, 5/8, x)',
8308 '(5/8, 5/8, -x+1/4)', '(5/8, 1/8, -x+3/4)',
8309 '(1/8, 5/8, -x+3/4)', '(1/8, 1/8, -x+1/4)',
8310 '(7/8, x+1/4, 7/8)', '(7/8, x+3/4, 3/8)',
8311 '(3/8, x+1/4, 3/8)', '(3/8, x+3/4, 7/8)',
8312 '(3/8, -x+1/2, 3/8)', '(3/8, -x, 7/8)',
8313 '(7/8, -x+1/2, 7/8)', '(7/8, -x, 3/8)',
8314 '(x+3/4, 3/8, 7/8)', '(x+3/4, 7/8, 3/8)',
8315 '(x+1/4, 3/8, 3/8)', '(x+1/4, 7/8, 7/8)',
8316 '(-x, 7/8, 3/8)', '(-x, 3/8, 7/8)',
8317 '(-x+1/2, 7/8, 7/8)', '(-x+1/2, 3/8, 3/8)',
8318 '(7/8, 3/8, -x)', '(7/8, 7/8, -x+1/2)',
8319 '(3/8, 3/8, -x+1/2)', '(3/8, 7/8, -x)',
8320 '(3/8, 7/8, x+3/4)', '(3/8, 3/8, x+1/4)',
8321 '(7/8, 7/8, x+1/4)', '(7/8, 3/8, x+3/4)',
8322 '(-x, 7/8, 7/8)', '(-x, 3/8, 3/8)',
8323 '(-x+1/2, 7/8, 3/8)', '(-x+1/2, 3/8, 7/8)',
8324 '(x+3/4, 3/8, 3/8)', '(x+3/4, 7/8, 7/8)',
8325 '(x+1/4, 3/8, 7/8)', '(x+1/4, 7/8, 3/8)',
8326 '(7/8, -x, 7/8)', '(7/8, -x+1/2, 3/8)',
8327 '(3/8, -x, 3/8)', '(3/8, -x+1/2, 7/8)',
8328 '(3/8, x+3/4, 3/8)', '(3/8, x+1/4, 7/8)',
8329 '(7/8, x+3/4, 7/8)', '(7/8, x+1/4, 3/8)',
8330 '(7/8, 7/8, -x)', '(7/8, 3/8, -x+1/2)',
8331 '(3/8, 7/8, -x+1/2)', '(3/8, 3/8, -x)',
8332 '(3/8, 3/8, x+3/4)', '(3/8, 7/8, x+1/4)',
8333 '(7/8, 3/8, x+1/4)', '(7/8, 7/8, x+3/4)',
8334 '(1/8, -x+3/4, 1/8)', '(1/8, -x+1/4, 5/8)',
8335 '(5/8, -x+3/4, 5/8)', '(5/8, -x+1/4, 1/8)',
8336 '(5/8, x+1/2, 5/8)', '(5/8, x, 1/8)',
8337 '(1/8, x+1/2, 1/8)', '(1/8, x, 5/8)',
8338 '(-x+1/4, 5/8, 1/8)', '(-x+1/4, 1/8, 5/8)',
8339 '(-x+3/4, 5/8, 5/8)', '(-x+3/4, 1/8, 1/8)',
8340 '(x, 1/8, 5/8)', '(x, 5/8, 1/8)',
8341 '(x+1/2, 1/8, 1/8)', '(x+1/2, 5/8, 5/8)',
8342 '(1/8, 5/8, x)', '(1/8, 1/8, x+1/2)',
8343 '(5/8, 5/8, x+1/2)', '(5/8, 1/8, x)',
8344 '(5/8, 1/8, -x+1/4)', '(5/8, 5/8, -x+3/4)',
8345 '(1/8, 1/8, -x+3/4)', '(1/8, 5/8, -x+1/4)')),
8346 '96g': (2, ('(1/4, y, -y)', '(1/4, y+1/2, -y+1/2)',
8347 '(3/4, y, -y+1/2)', '(3/4, y+1/2, -y)',
8348 '(0, -y+3/4, -y+1/2)', '(0, -y+1/4, -y)',
8349 '(1/2, -y+3/4, -y)', '(1/2, -y+1/4, -y+1/2)',
8350 '(1/2, y+1/2, y+1/4)', '(1/2, y, y+3/4)',
8351 '(0, y+1/2, y+3/4)', '(0, y, y+1/4)',
8352 '(3/4, -y+1/4, y+3/4)', '(3/4, -y+3/4, y+1/4)',
8353 '(1/4, -y+1/4, y+1/4)', '(1/4, -y+3/4, y+3/4)',
8354 '(-y, 1/4, y)', '(-y, 3/4, y+1/2)',
8355 '(-y+1/2, 1/4, y+1/2)', '(-y+1/2, 3/4, y)',
8356 '(-y+1/2, 0, -y+3/4)', '(-y+1/2, 1/2, -y+1/4)',
8357 '(-y, 0, -y+1/4)', '(-y, 1/2, -y+3/4)',
8358 '(y+1/4, 1/2, y+1/2)', '(y+1/4, 0, y)',
8359 '(y+3/4, 1/2, y)', '(y+3/4, 0, y+1/2)',
8360 '(y+3/4, 3/4, -y+1/4)', '(y+3/4, 1/4, -y+3/4)',
8361 '(y+1/4, 3/4, -y+3/4)', '(y+1/4, 1/4, -y+1/4)',
8362 '(y, -y, 1/4)', '(y, -y+1/2, 3/4)',
8363 '(y+1/2, -y, 3/4)', '(y+1/2, -y+1/2, 1/4)',
8364 '(-y+3/4, -y+1/2, 0)', '(-y+3/4, -y, 1/2)',
8365 '(-y+1/4, -y+1/2, 1/2)', '(-y+1/4, -y, 0)',
8366 '(y+1/2, y+1/4, 1/2)', '(y+1/2, y+3/4, 0)',
8367 '(y, y+1/4, 0)', '(y, y+3/4, 1/2)',
8368 '(-y+1/4, y+3/4, 3/4)', '(-y+1/4, y+1/4, 1/4)',
8369 '(-y+3/4, y+3/4, 1/4)', '(-y+3/4, y+1/4, 3/4)',
8370 '(3/4, -y, y)', '(3/4, -y+1/2, y+1/2)',
8371 '(1/4, -y, y+1/2)', '(1/4, -y+1/2, y)',
8372 '(0, y+1/4, y+1/2)', '(0, y+3/4, y)',
8373 '(1/2, y+1/4, y)', '(1/2, y+3/4, y+1/2)',
8374 '(1/2, -y+1/2, -y+3/4)', '(1/2, -y, -y+1/4)',
8375 '(0, -y+1/2, -y+1/4)', '(0, -y, -y+3/4)',
8376 '(1/4, y+3/4, -y+1/4)', '(1/4, y+1/4, -y+3/4)',
8377 '(3/4, y+3/4, -y+3/4)', '(3/4, y+1/4, -y+1/4)',
8378 '(y, 3/4, -y)', '(y, 1/4, -y+1/2)',
8379 '(y+1/2, 3/4, -y+1/2)', '(y+1/2, 1/4, -y)',
8380 '(y+1/2, 0, y+1/4)', '(y+1/2, 1/2, y+3/4)',
8381 '(y, 0, y+3/4)', '(y, 1/2, y+1/4)',
8382 '(-y+3/4, 1/2, -y+1/2)', '(-y+3/4, 0, -y)',
8383 '(-y+1/4, 1/2, -y)', '(-y+1/4, 0, -y+1/2)',
8384 '(-y+1/4, 1/4, y+3/4)', '(-y+1/4, 3/4, y+1/4)',
8385 '(-y+3/4, 1/4, y+1/4)', '(-y+3/4, 3/4, y+3/4)',
8386 '(-y, y, 3/4)', '(-y, y+1/2, 1/4)',
8387 '(-y+1/2, y, 1/4)', '(-y+1/2, y+1/2, 3/4)',
8388 '(y+1/4, y+1/2, 0)', '(y+1/4, y, 1/2)',
8389 '(y+3/4, y+1/2, 1/2)', '(y+3/4, y, 0)',
8390 '(-y+1/2, -y+3/4, 1/2)', '(-y+1/2, -y+1/4, 0)',
8391 '(-y, -y+3/4, 0)', '(-y, -y+1/4, 1/2)',
8392 '(y+3/4, -y+1/4, 1/4)', '(y+3/4, -y+3/4, 3/4)',
8393 '(y+1/4, -y+1/4, 3/4)', '(y+1/4, -y+3/4, 1/4)')),
8394 '192h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
8395 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
8396 '(-x+1/4, -y+3/4, z+1/2)', '(-x+1/4, -y+1/4, z)',
8397 '(-x+3/4, -y+3/4, z)', '(-x+3/4, -y+1/4, z+1/2)',
8398 '(-x+3/4, y+1/2, -z+1/4)', '(-x+3/4, y, -z+3/4)',
8399 '(-x+1/4, y+1/2, -z+3/4)', '(-x+1/4, y, -z+1/4)',
8400 '(x+1/2, -y+1/4, -z+3/4)',
8401 '(x+1/2, -y+3/4, -z+1/4)', '(x, -y+1/4, -z+1/4)',
8402 '(x, -y+3/4, -z+3/4)', '(z, x, y)',
8403 '(z, x+1/2, y+1/2)', '(z+1/2, x, y+1/2)',
8404 '(z+1/2, x+1/2, y)', '(z+1/2, -x+1/4, -y+3/4)',
8405 '(z+1/2, -x+3/4, -y+1/4)', '(z, -x+1/4, -y+1/4)',
8406 '(z, -x+3/4, -y+3/4)', '(-z+1/4, -x+3/4, y+1/2)',
8407 '(-z+1/4, -x+1/4, y)', '(-z+3/4, -x+3/4, y)',
8408 '(-z+3/4, -x+1/4, y+1/2)',
8409 '(-z+3/4, x+1/2, -y+1/4)', '(-z+3/4, x, -y+3/4)',
8410 '(-z+1/4, x+1/2, -y+3/4)', '(-z+1/4, x, -y+1/4)',
8411 '(y, z, x)', '(y, z+1/2, x+1/2)',
8412 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
8413 '(-y+3/4, z+1/2, -x+1/4)', '(-y+3/4, z, -x+3/4)',
8414 '(-y+1/4, z+1/2, -x+3/4)', '(-y+1/4, z, -x+1/4)',
8415 '(y+1/2, -z+1/4, -x+3/4)',
8416 '(y+1/2, -z+3/4, -x+1/4)', '(y, -z+1/4, -x+1/4)',
8417 '(y, -z+3/4, -x+3/4)', '(-y+1/4, -z+3/4, x+1/2)',
8418 '(-y+1/4, -z+1/4, x)', '(-y+3/4, -z+3/4, x)',
8419 '(-y+3/4, -z+1/4, x+1/2)', '(y+3/4, x+1/4, -z)',
8420 '(y+3/4, x+3/4, -z+1/2)',
8421 '(y+1/4, x+1/4, -z+1/2)', '(y+1/4, x+3/4, -z)',
8422 '(-y+1/2, -x+1/2, -z+1/2)', '(-y+1/2, -x, -z)',
8423 '(-y, -x+1/2, -z)', '(-y, -x, -z+1/2)',
8424 '(y+1/4, -x, z+3/4)', '(y+1/4, -x+1/2, z+1/4)',
8425 '(y+3/4, -x, z+1/4)', '(y+3/4, -x+1/2, z+3/4)',
8426 '(-y, x+3/4, z+1/4)', '(-y, x+1/4, z+3/4)',
8427 '(-y+1/2, x+3/4, z+3/4)',
8428 '(-y+1/2, x+1/4, z+1/4)', '(x+3/4, z+1/4, -y)',
8429 '(x+3/4, z+3/4, -y+1/2)',
8430 '(x+1/4, z+1/4, -y+1/2)', '(x+1/4, z+3/4, -y)',
8431 '(-x, z+3/4, y+1/4)', '(-x, z+1/4, y+3/4)',
8432 '(-x+1/2, z+3/4, y+3/4)',
8433 '(-x+1/2, z+1/4, y+1/4)',
8434 '(-x+1/2, -z+1/2, -y+1/2)', '(-x+1/2, -z, -y)',
8435 '(-x, -z+1/2, -y)', '(-x, -z, -y+1/2)',
8436 '(x+1/4, -z, y+3/4)', '(x+1/4, -z+1/2, y+1/4)',
8437 '(x+3/4, -z, y+1/4)', '(x+3/4, -z+1/2, y+3/4)',
8438 '(z+3/4, y+1/4, -x)', '(z+3/4, y+3/4, -x+1/2)',
8439 '(z+1/4, y+1/4, -x+1/2)', '(z+1/4, y+3/4, -x)',
8440 '(z+1/4, -y, x+3/4)', '(z+1/4, -y+1/2, x+1/4)',
8441 '(z+3/4, -y, x+1/4)', '(z+3/4, -y+1/2, x+3/4)',
8442 '(-z, y+3/4, x+1/4)', '(-z, y+1/4, x+3/4)',
8443 '(-z+1/2, y+3/4, x+3/4)',
8444 '(-z+1/2, y+1/4, x+1/4)',
8445 '(-z+1/2, -y+1/2, -x+1/2)', '(-z+1/2, -y, -x)',
8446 '(-z, -y+1/2, -x)', '(-z, -y, -x+1/2)',
8447 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
8448 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
8449 '(x+3/4, y+1/4, -z+1/2)', '(x+3/4, y+3/4, -z)',
8450 '(x+1/4, y+1/4, -z)', '(x+1/4, y+3/4, -z+1/2)',
8451 '(x+1/4, -y+1/2, z+3/4)', '(x+1/4, -y, z+1/4)',
8452 '(x+3/4, -y+1/2, z+1/4)', '(x+3/4, -y, z+3/4)',
8453 '(-x+1/2, y+3/4, z+1/4)',
8454 '(-x+1/2, y+1/4, z+3/4)', '(-x, y+3/4, z+3/4)',
8455 '(-x, y+1/4, z+1/4)', '(-z, -x, -y)',
8456 '(-z, -x+1/2, -y+1/2)', '(-z+1/2, -x, -y+1/2)',
8457 '(-z+1/2, -x+1/2, -y)', '(-z+1/2, x+3/4, y+1/4)',
8458 '(-z+1/2, x+1/4, y+3/4)', '(-z, x+3/4, y+3/4)',
8459 '(-z, x+1/4, y+1/4)', '(z+3/4, x+1/4, -y+1/2)',
8460 '(z+3/4, x+3/4, -y)', '(z+1/4, x+1/4, -y)',
8461 '(z+1/4, x+3/4, -y+1/2)',
8462 '(z+1/4, -x+1/2, y+3/4)', '(z+1/4, -x, y+1/4)',
8463 '(z+3/4, -x+1/2, y+1/4)', '(z+3/4, -x, y+3/4)',
8464 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
8465 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
8466 '(y+1/4, -z+1/2, x+3/4)', '(y+1/4, -z, x+1/4)',
8467 '(y+3/4, -z+1/2, x+1/4)', '(y+3/4, -z, x+3/4)',
8468 '(-y+1/2, z+3/4, x+1/4)',
8469 '(-y+1/2, z+1/4, x+3/4)', '(-y, z+3/4, x+3/4)',
8470 '(-y, z+1/4, x+1/4)', '(y+3/4, z+1/4, -x+1/2)',
8471 '(y+3/4, z+3/4, -x)', '(y+1/4, z+1/4, -x)',
8472 '(y+1/4, z+3/4, -x+1/2)', '(-y+1/4, -x+3/4, z)',
8473 '(-y+1/4, -x+1/4, z+1/2)',
8474 '(-y+3/4, -x+3/4, z+1/2)', '(-y+3/4, -x+1/4, z)',
8475 '(y+1/2, x+1/2, z+1/2)', '(y+1/2, x, z)',
8476 '(y, x+1/2, z)', '(y, x, z+1/2)',
8477 '(-y+3/4, x, -z+1/4)', '(-y+3/4, x+1/2, -z+3/4)',
8478 '(-y+1/4, x, -z+3/4)', '(-y+1/4, x+1/2, -z+1/4)',
8479 '(y, -x+1/4, -z+3/4)', '(y, -x+3/4, -z+1/4)',
8480 '(y+1/2, -x+1/4, -z+1/4)',
8481 '(y+1/2, -x+3/4, -z+3/4)', '(-x+1/4, -z+3/4, y)',
8482 '(-x+1/4, -z+1/4, y+1/2)',
8483 '(-x+3/4, -z+3/4, y+1/2)', '(-x+3/4, -z+1/4, y)',
8484 '(x, -z+1/4, -y+3/4)', '(x, -z+3/4, -y+1/4)',
8485 '(x+1/2, -z+1/4, -y+1/4)',
8486 '(x+1/2, -z+3/4, -y+3/4)',
8487 '(x+1/2, z+1/2, y+1/2)', '(x+1/2, z, y)',
8488 '(x, z+1/2, y)', '(x, z, y+1/2)',
8489 '(-x+3/4, z, -y+1/4)', '(-x+3/4, z+1/2, -y+3/4)',
8490 '(-x+1/4, z, -y+3/4)', '(-x+1/4, z+1/2, -y+1/4)',
8491 '(-z+1/4, -y+3/4, x)', '(-z+1/4, -y+1/4, x+1/2)',
8492 '(-z+3/4, -y+3/4, x+1/2)', '(-z+3/4, -y+1/4, x)',
8493 '(-z+3/4, y, -x+1/4)', '(-z+3/4, y+1/2, -x+3/4)',
8494 '(-z+1/4, y, -x+3/4)', '(-z+1/4, y+1/2, -x+1/4)',
8495 '(z, -y+1/4, -x+3/4)', '(z, -y+3/4, -x+1/4)',
8496 '(z+1/2, -y+1/4, -x+1/4)',
8497 '(z+1/2, -y+3/4, -x+3/4)',
8498 '(z+1/2, y+1/2, x+1/2)', '(z+1/2, y, x)',
8499 '(z, y+1/2, x)', '(z, y, x+1/2)'))},
8500 '229': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
8501 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
8502 '(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
8503 '8c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
8504 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
8505 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
8506 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
8507 '12d': (0, ('(1/4, 0, 1/2)', '(3/4, 1/2, 0)', '(3/4, 0, 1/2)',
8508 '(1/4, 1/2, 0)', '(1/2, 1/4, 0)', '(0, 3/4, 1/2)',
8509 '(1/2, 3/4, 0)', '(0, 1/4, 1/2)', '(0, 1/2, 1/4)',
8510 '(1/2, 0, 3/4)', '(0, 1/2, 3/4)', '(1/2, 0, 1/4)')),
8511 '12e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
8512 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
8513 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
8514 '(1/2, -x+1/2, 1/2)', '(0, 0, x)',
8515 '(1/2, 1/2, x+1/2)', '(0, 0, -x)',
8516 '(1/2, 1/2, -x+1/2)')),
8517 '16f': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)', '(-x, -x, x)',
8518 '(-x+1/2, -x+1/2, x+1/2)', '(-x, x, -x)',
8519 '(-x+1/2, x+1/2, -x+1/2)', '(x, -x, -x)',
8520 '(x+1/2, -x+1/2, -x+1/2)', '(x, x, -x)',
8521 '(x+1/2, x+1/2, -x+1/2)', '(-x, -x, -x)',
8522 '(-x+1/2, -x+1/2, -x+1/2)', '(x, -x, x)',
8523 '(x+1/2, -x+1/2, x+1/2)', '(-x, x, x)',
8524 '(-x+1/2, x+1/2, x+1/2)')),
8525 '24g': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
8526 '(-x+1/2, 1/2, 0)', '(1/2, x, 0)',
8527 '(0, x+1/2, 1/2)', '(1/2, -x, 0)',
8528 '(0, -x+1/2, 1/2)', '(0, 1/2, x)',
8529 '(1/2, 0, x+1/2)', '(0, 1/2, -x)',
8530 '(1/2, 0, -x+1/2)', '(0, x, 1/2)',
8531 '(1/2, x+1/2, 0)', '(0, -x, 1/2)',
8532 '(1/2, -x+1/2, 0)', '(x, 1/2, 0)',
8533 '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
8534 '(-x+1/2, 0, 1/2)', '(1/2, 0, -x)',
8535 '(0, 1/2, -x+1/2)', '(1/2, 0, x)',
8536 '(0, 1/2, x+1/2)')),
8537 '24h': (2, ('(0, y, y)', '(1/2, y+1/2, y+1/2)', '(0, -y, y)',
8538 '(1/2, -y+1/2, y+1/2)', '(0, y, -y)',
8539 '(1/2, y+1/2, -y+1/2)', '(0, -y, -y)',
8540 '(1/2, -y+1/2, -y+1/2)', '(y, 0, y)',
8541 '(y+1/2, 1/2, y+1/2)', '(y, 0, -y)',
8542 '(y+1/2, 1/2, -y+1/2)', '(-y, 0, y)',
8543 '(-y+1/2, 1/2, y+1/2)', '(-y, 0, -y)',
8544 '(-y+1/2, 1/2, -y+1/2)', '(y, y, 0)',
8545 '(y+1/2, y+1/2, 1/2)', '(-y, y, 0)',
8546 '(-y+1/2, y+1/2, 1/2)', '(y, -y, 0)',
8547 '(y+1/2, -y+1/2, 1/2)', '(-y, -y, 0)',
8548 '(-y+1/2, -y+1/2, 1/2)')),
8549 '48i': (2, ('(1/4, y, -y+1/2)', '(3/4, y+1/2, -y)',
8550 '(3/4, -y, -y+1/2)', '(1/4, -y+1/2, -y)',
8551 '(3/4, y, y+1/2)', '(1/4, y+1/2, y)',
8552 '(1/4, -y, y+1/2)', '(3/4, -y+1/2, y)',
8553 '(-y+1/2, 1/4, y)', '(-y, 3/4, y+1/2)',
8554 '(-y+1/2, 3/4, -y)', '(-y, 1/4, -y+1/2)',
8555 '(y+1/2, 3/4, y)', '(y, 1/4, y+1/2)',
8556 '(y+1/2, 1/4, -y)', '(y, 3/4, -y+1/2)',
8557 '(y, -y+1/2, 1/4)', '(y+1/2, -y, 3/4)',
8558 '(-y, -y+1/2, 3/4)', '(-y+1/2, -y, 1/4)',
8559 '(y, y+1/2, 3/4)', '(y+1/2, y, 1/4)',
8560 '(-y, y+1/2, 1/4)', '(-y+1/2, y, 3/4)',
8561 '(3/4, -y, y+1/2)', '(1/4, -y+1/2, y)',
8562 '(1/4, y, y+1/2)', '(3/4, y+1/2, y)',
8563 '(1/4, -y, -y+1/2)', '(3/4, -y+1/2, -y)',
8564 '(3/4, y, -y+1/2)', '(1/4, y+1/2, -y)',
8565 '(y+1/2, 3/4, -y)', '(y, 1/4, -y+1/2)',
8566 '(y+1/2, 1/4, y)', '(y, 3/4, y+1/2)',
8567 '(-y+1/2, 1/4, -y)', '(-y, 3/4, -y+1/2)',
8568 '(-y+1/2, 3/4, y)', '(-y, 1/4, y+1/2)',
8569 '(-y, y+1/2, 3/4)', '(-y+1/2, y, 1/4)',
8570 '(y, y+1/2, 1/4)', '(y+1/2, y, 3/4)',
8571 '(-y, -y+1/2, 1/4)', '(-y+1/2, -y, 3/4)',
8572 '(y, -y+1/2, 3/4)', '(y+1/2, -y, 1/4)')),
8573 '48j': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y, z)',
8574 '(1/2, -y+1/2, z+1/2)', '(0, y, -z)',
8575 '(1/2, y+1/2, -z+1/2)', '(0, -y, -z)',
8576 '(1/2, -y+1/2, -z+1/2)', '(z, 0, y)',
8577 '(z+1/2, 1/2, y+1/2)', '(z, 0, -y)',
8578 '(z+1/2, 1/2, -y+1/2)', '(-z, 0, y)',
8579 '(-z+1/2, 1/2, y+1/2)', '(-z, 0, -y)',
8580 '(-z+1/2, 1/2, -y+1/2)', '(y, z, 0)',
8581 '(y+1/2, z+1/2, 1/2)', '(-y, z, 0)',
8582 '(-y+1/2, z+1/2, 1/2)', '(y, -z, 0)',
8583 '(y+1/2, -z+1/2, 1/2)', '(-y, -z, 0)',
8584 '(-y+1/2, -z+1/2, 1/2)', '(y, 0, -z)',
8585 '(y+1/2, 1/2, -z+1/2)', '(-y, 0, -z)',
8586 '(-y+1/2, 1/2, -z+1/2)', '(y, 0, z)',
8587 '(y+1/2, 1/2, z+1/2)', '(-y, 0, z)',
8588 '(-y+1/2, 1/2, z+1/2)', '(0, z, -y)',
8589 '(1/2, z+1/2, -y+1/2)', '(0, z, y)',
8590 '(1/2, z+1/2, y+1/2)', '(0, -z, -y)',
8591 '(1/2, -z+1/2, -y+1/2)', '(0, -z, y)',
8592 '(1/2, -z+1/2, y+1/2)', '(z, y, 0)',
8593 '(z+1/2, y+1/2, 1/2)', '(z, -y, 0)',
8594 '(z+1/2, -y+1/2, 1/2)', '(-z, y, 0)',
8595 '(-z+1/2, y+1/2, 1/2)', '(-z, -y, 0)',
8596 '(-z+1/2, -y+1/2, 1/2)')),
8597 '48k': (5, ('(x, x, z)', '(x+1/2, x+1/2, z+1/2)', '(-x, -x, z)',
8598 '(-x+1/2, -x+1/2, z+1/2)', '(-x, x, -z)',
8599 '(-x+1/2, x+1/2, -z+1/2)', '(x, -x, -z)',
8600 '(x+1/2, -x+1/2, -z+1/2)', '(z, x, x)',
8601 '(z+1/2, x+1/2, x+1/2)', '(z, -x, -x)',
8602 '(z+1/2, -x+1/2, -x+1/2)', '(-z, -x, x)',
8603 '(-z+1/2, -x+1/2, x+1/2)', '(-z, x, -x)',
8604 '(-z+1/2, x+1/2, -x+1/2)', '(x, z, x)',
8605 '(x+1/2, z+1/2, x+1/2)', '(-x, z, -x)',
8606 '(-x+1/2, z+1/2, -x+1/2)', '(x, -z, -x)',
8607 '(x+1/2, -z+1/2, -x+1/2)', '(-x, -z, x)',
8608 '(-x+1/2, -z+1/2, x+1/2)', '(x, x, -z)',
8609 '(x+1/2, x+1/2, -z+1/2)', '(-x, -x, -z)',
8610 '(-x+1/2, -x+1/2, -z+1/2)', '(x, -x, z)',
8611 '(x+1/2, -x+1/2, z+1/2)', '(-x, x, z)',
8612 '(-x+1/2, x+1/2, z+1/2)', '(x, z, -x)',
8613 '(x+1/2, z+1/2, -x+1/2)', '(-x, z, x)',
8614 '(-x+1/2, z+1/2, x+1/2)', '(-x, -z, -x)',
8615 '(-x+1/2, -z+1/2, -x+1/2)', '(x, -z, x)',
8616 '(x+1/2, -z+1/2, x+1/2)', '(z, x, -x)',
8617 '(z+1/2, x+1/2, -x+1/2)', '(z, -x, x)',
8618 '(z+1/2, -x+1/2, x+1/2)', '(-z, x, x)',
8619 '(-z+1/2, x+1/2, x+1/2)', '(-z, -x, -x)',
8620 '(-z+1/2, -x+1/2, -x+1/2)')),
8621 '96l': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
8622 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
8623 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
8624 '(x+1/2, -y+1/2, -z+1/2)', '(z, x, y)',
8625 '(z+1/2, x+1/2, y+1/2)', '(z, -x, -y)',
8626 '(z+1/2, -x+1/2, -y+1/2)', '(-z, -x, y)',
8627 '(-z+1/2, -x+1/2, y+1/2)', '(-z, x, -y)',
8628 '(-z+1/2, x+1/2, -y+1/2)', '(y, z, x)',
8629 '(y+1/2, z+1/2, x+1/2)', '(-y, z, -x)',
8630 '(-y+1/2, z+1/2, -x+1/2)', '(y, -z, -x)',
8631 '(y+1/2, -z+1/2, -x+1/2)', '(-y, -z, x)',
8632 '(-y+1/2, -z+1/2, x+1/2)', '(y, x, -z)',
8633 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z)',
8634 '(-y+1/2, -x+1/2, -z+1/2)', '(y, -x, z)',
8635 '(y+1/2, -x+1/2, z+1/2)', '(-y, x, z)',
8636 '(-y+1/2, x+1/2, z+1/2)', '(x, z, -y)',
8637 '(x+1/2, z+1/2, -y+1/2)', '(-x, z, y)',
8638 '(-x+1/2, z+1/2, y+1/2)', '(-x, -z, -y)',
8639 '(-x+1/2, -z+1/2, -y+1/2)', '(x, -z, y)',
8640 '(x+1/2, -z+1/2, y+1/2)', '(z, y, -x)',
8641 '(z+1/2, y+1/2, -x+1/2)', '(z, -y, x)',
8642 '(z+1/2, -y+1/2, x+1/2)', '(-z, y, x)',
8643 '(-z+1/2, y+1/2, x+1/2)', '(-z, -y, -x)',
8644 '(-z+1/2, -y+1/2, -x+1/2)', '(-x, -y, -z)',
8645 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
8646 '(x+1/2, y+1/2, -z+1/2)', '(x, -y, z)',
8647 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
8648 '(-x+1/2, y+1/2, z+1/2)', '(-z, -x, -y)',
8649 '(-z+1/2, -x+1/2, -y+1/2)', '(-z, x, y)',
8650 '(-z+1/2, x+1/2, y+1/2)', '(z, x, -y)',
8651 '(z+1/2, x+1/2, -y+1/2)', '(z, -x, y)',
8652 '(z+1/2, -x+1/2, y+1/2)', '(-y, -z, -x)',
8653 '(-y+1/2, -z+1/2, -x+1/2)', '(y, -z, x)',
8654 '(y+1/2, -z+1/2, x+1/2)', '(-y, z, x)',
8655 '(-y+1/2, z+1/2, x+1/2)', '(y, z, -x)',
8656 '(y+1/2, z+1/2, -x+1/2)', '(-y, -x, z)',
8657 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z)',
8658 '(y+1/2, x+1/2, z+1/2)', '(-y, x, -z)',
8659 '(-y+1/2, x+1/2, -z+1/2)', '(y, -x, -z)',
8660 '(y+1/2, -x+1/2, -z+1/2)', '(-x, -z, y)',
8661 '(-x+1/2, -z+1/2, y+1/2)', '(x, -z, -y)',
8662 '(x+1/2, -z+1/2, -y+1/2)', '(x, z, y)',
8663 '(x+1/2, z+1/2, y+1/2)', '(-x, z, -y)',
8664 '(-x+1/2, z+1/2, -y+1/2)', '(-z, -y, x)',
8665 '(-z+1/2, -y+1/2, x+1/2)', '(-z, y, -x)',
8666 '(-z+1/2, y+1/2, -x+1/2)', '(z, -y, -x)',
8667 '(z+1/2, -y+1/2, -x+1/2)', '(z, y, x)',
8668 '(z+1/2, y+1/2, x+1/2)'))},
8669 '230': {'16a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
8670 '(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 0)',
8671 '(1/2, 1/2, 0)', '(0, 0, 1/2)', '(3/4, 1/4, 1/4)',
8672 '(1/4, 3/4, 3/4)', '(3/4, 3/4, 3/4)',
8673 '(1/4, 1/4, 1/4)', '(1/4, 1/4, 3/4)',
8674 '(3/4, 3/4, 1/4)', '(1/4, 3/4, 1/4)',
8675 '(3/4, 1/4, 3/4)')),
8676 '16b': (0, ('(1/8, 1/8, 1/8)', '(5/8, 5/8, 5/8)',
8677 '(3/8, 7/8, 5/8)', '(7/8, 3/8, 1/8)',
8678 '(7/8, 5/8, 3/8)', '(3/8, 1/8, 7/8)',
8679 '(5/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)',
8680 '(7/8, 7/8, 7/8)', '(3/8, 3/8, 3/8)',
8681 '(5/8, 1/8, 3/8)', '(1/8, 5/8, 7/8)',
8682 '(1/8, 3/8, 5/8)', '(5/8, 7/8, 1/8)',
8683 '(3/8, 5/8, 1/8)', '(7/8, 1/8, 5/8)')),
8684 '24c': (0, ('(1/8, 0, 1/4)', '(5/8, 1/2, 3/4)', '(3/8, 0, 3/4)',
8685 '(7/8, 1/2, 1/4)', '(1/4, 1/8, 0)',
8686 '(3/4, 5/8, 1/2)', '(3/4, 3/8, 0)',
8687 '(1/4, 7/8, 1/2)', '(0, 1/4, 1/8)',
8688 '(1/2, 3/4, 5/8)', '(0, 3/4, 3/8)',
8689 '(1/2, 1/4, 7/8)', '(7/8, 0, 3/4)',
8690 '(3/8, 1/2, 1/4)', '(5/8, 0, 1/4)',
8691 '(1/8, 1/2, 3/4)', '(3/4, 7/8, 0)',
8692 '(1/4, 3/8, 1/2)', '(1/4, 5/8, 0)',
8693 '(3/4, 1/8, 1/2)', '(0, 3/4, 7/8)',
8694 '(1/2, 1/4, 3/8)', '(0, 1/4, 5/8)',
8695 '(1/2, 3/4, 1/8)')),
8696 '24d': (0, ('(3/8, 0, 1/4)', '(7/8, 1/2, 3/4)', '(1/8, 0, 3/4)',
8697 '(5/8, 1/2, 1/4)', '(1/4, 3/8, 0)',
8698 '(3/4, 7/8, 1/2)', '(3/4, 1/8, 0)',
8699 '(1/4, 5/8, 1/2)', '(0, 1/4, 3/8)',
8700 '(1/2, 3/4, 7/8)', '(0, 3/4, 1/8)',
8701 '(1/2, 1/4, 5/8)', '(3/4, 5/8, 0)',
8702 '(1/4, 1/8, 1/2)', '(3/4, 3/8, 1/2)',
8703 '(1/4, 7/8, 0)', '(1/8, 1/2, 1/4)', '(5/8, 0, 3/4)',
8704 '(7/8, 0, 1/4)', '(3/8, 1/2, 3/4)', '(0, 1/4, 7/8)',
8705 '(1/2, 3/4, 3/8)', '(1/2, 1/4, 1/8)',
8706 '(0, 3/4, 5/8)')),
8707 '32e': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)',
8708 '(-x+1/2, -x, x+1/2)', '(-x, -x+1/2, x)',
8709 '(-x, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
8710 '(x+1/2, -x+1/2, -x)', '(x, -x, -x+1/2)',
8711 '(x+3/4, x+1/4, -x+1/4)', '(x+1/4, x+3/4, -x+3/4)',
8712 '(-x+3/4, -x+3/4, -x+3/4)',
8713 '(-x+1/4, -x+1/4, -x+1/4)',
8714 '(x+1/4, -x+1/4, x+3/4)', '(x+3/4, -x+3/4, x+1/4)',
8715 '(-x+1/4, x+3/4, x+1/4)', '(-x+3/4, x+1/4, x+3/4)',
8716 '(-x, -x, -x)', '(-x+1/2, -x+1/2, -x+1/2)',
8717 '(x+1/2, x, -x+1/2)', '(x, x+1/2, -x)',
8718 '(x, -x+1/2, x+1/2)', '(x+1/2, -x, x)',
8719 '(-x+1/2, x+1/2, x)', '(-x, x, x+1/2)',
8720 '(-x+1/4, -x+3/4, x+3/4)',
8721 '(-x+3/4, -x+1/4, x+1/4)', '(x+1/4, x+1/4, x+1/4)',
8722 '(x+3/4, x+3/4, x+3/4)', '(-x+3/4, x+3/4, -x+1/4)',
8723 '(-x+1/4, x+1/4, -x+3/4)',
8724 '(x+3/4, -x+1/4, -x+3/4)',
8725 '(x+1/4, -x+3/4, -x+1/4)')),
8726 '48f': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
8727 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(1/4, x, 0)',
8728 '(3/4, x+1/2, 1/2)', '(3/4, -x+1/2, 0)',
8729 '(1/4, -x, 1/2)', '(0, 1/4, x)',
8730 '(1/2, 3/4, x+1/2)', '(0, 3/4, -x+1/2)',
8731 '(1/2, 1/4, -x)', '(3/4, x+1/4, 0)',
8732 '(1/4, x+3/4, 1/2)', '(3/4, -x+3/4, 1/2)',
8733 '(1/4, -x+1/4, 0)', '(x+3/4, 1/2, 1/4)',
8734 '(x+1/4, 0, 3/4)', '(-x+1/4, 0, 1/4)',
8735 '(-x+3/4, 1/2, 3/4)', '(0, 1/4, -x+1/4)',
8736 '(1/2, 3/4, -x+3/4)', '(1/2, 1/4, x+3/4)',
8737 '(0, 3/4, x+1/4)', '(-x, 0, 3/4)',
8738 '(-x+1/2, 1/2, 1/4)', '(x+1/2, 0, 1/4)',
8739 '(x, 1/2, 3/4)', '(3/4, -x, 0)',
8740 '(1/4, -x+1/2, 1/2)', '(1/4, x+1/2, 0)',
8741 '(3/4, x, 1/2)', '(0, 3/4, -x)',
8742 '(1/2, 1/4, -x+1/2)', '(0, 1/4, x+1/2)',
8743 '(1/2, 3/4, x)', '(1/4, -x+3/4, 0)',
8744 '(3/4, -x+1/4, 1/2)', '(1/4, x+1/4, 1/2)',
8745 '(3/4, x+3/4, 0)', '(-x+1/4, 1/2, 3/4)',
8746 '(-x+3/4, 0, 1/4)', '(x+3/4, 0, 3/4)',
8747 '(x+1/4, 1/2, 1/4)', '(0, 3/4, x+3/4)',
8748 '(1/2, 1/4, x+1/4)', '(1/2, 3/4, -x+1/4)',
8749 '(0, 1/4, -x+3/4)')),
8750 '48g': (2, ('(1/8, y, -y+1/4)', '(5/8, y+1/2, -y+3/4)',
8751 '(3/8, -y, -y+3/4)', '(7/8, -y+1/2, -y+1/4)',
8752 '(7/8, y+1/2, y+1/4)', '(3/8, y, y+3/4)',
8753 '(5/8, -y+1/2, y+3/4)', '(1/8, -y, y+1/4)',
8754 '(-y+1/4, 1/8, y)', '(-y+3/4, 5/8, y+1/2)',
8755 '(-y+3/4, 3/8, -y)', '(-y+1/4, 7/8, -y+1/2)',
8756 '(y+1/4, 7/8, y+1/2)', '(y+3/4, 3/8, y)',
8757 '(y+3/4, 5/8, -y+1/2)', '(y+1/4, 1/8, -y)',
8758 '(y, -y+1/4, 1/8)', '(y+1/2, -y+3/4, 5/8)',
8759 '(-y, -y+3/4, 3/8)', '(-y+1/2, -y+1/4, 7/8)',
8760 '(y+1/2, y+1/4, 7/8)', '(y, y+3/4, 3/8)',
8761 '(-y+1/2, y+3/4, 5/8)', '(-y, y+1/4, 1/8)',
8762 '(7/8, -y, y+3/4)', '(3/8, -y+1/2, y+1/4)',
8763 '(5/8, y, y+1/4)', '(1/8, y+1/2, y+3/4)',
8764 '(1/8, -y+1/2, -y+3/4)', '(5/8, -y, -y+1/4)',
8765 '(3/8, y+1/2, -y+1/4)', '(7/8, y, -y+3/4)',
8766 '(y+3/4, 7/8, -y)', '(y+1/4, 3/8, -y+1/2)',
8767 '(y+1/4, 5/8, y)', '(y+3/4, 1/8, y+1/2)',
8768 '(-y+3/4, 1/8, -y+1/2)', '(-y+1/4, 5/8, -y)',
8769 '(-y+1/4, 3/8, y+1/2)', '(-y+3/4, 7/8, y)',
8770 '(-y, y+3/4, 7/8)', '(-y+1/2, y+1/4, 3/8)',
8771 '(y, y+1/4, 5/8)', '(y+1/2, y+3/4, 1/8)',
8772 '(-y+1/2, -y+3/4, 1/8)', '(-y, -y+1/4, 5/8)',
8773 '(y+1/2, -y+1/4, 3/8)', '(y, -y+3/4, 7/8)')),
8774 '96h': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
8775 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
8776 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
8777 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
8778 '(z, x, y)', '(z+1/2, x+1/2, y+1/2)',
8779 '(z+1/2, -x+1/2, -y)', '(z, -x, -y+1/2)',
8780 '(-z+1/2, -x, y+1/2)', '(-z, -x+1/2, y)',
8781 '(-z, x+1/2, -y+1/2)', '(-z+1/2, x, -y)',
8782 '(y, z, x)', '(y+1/2, z+1/2, x+1/2)',
8783 '(-y, z+1/2, -x+1/2)', '(-y+1/2, z, -x)',
8784 '(y+1/2, -z+1/2, -x)', '(y, -z, -x+1/2)',
8785 '(-y+1/2, -z, x+1/2)', '(-y, -z+1/2, x)',
8786 '(y+3/4, x+1/4, -z+1/4)', '(y+1/4, x+3/4, -z+3/4)',
8787 '(-y+3/4, -x+3/4, -z+3/4)',
8788 '(-y+1/4, -x+1/4, -z+1/4)',
8789 '(y+1/4, -x+1/4, z+3/4)', '(y+3/4, -x+3/4, z+1/4)',
8790 '(-y+1/4, x+3/4, z+1/4)', '(-y+3/4, x+1/4, z+3/4)',
8791 '(x+3/4, z+1/4, -y+1/4)', '(x+1/4, z+3/4, -y+3/4)',
8792 '(-x+1/4, z+3/4, y+1/4)', '(-x+3/4, z+1/4, y+3/4)',
8793 '(-x+3/4, -z+3/4, -y+3/4)',
8794 '(-x+1/4, -z+1/4, -y+1/4)',
8795 '(x+1/4, -z+1/4, y+3/4)', '(x+3/4, -z+3/4, y+1/4)',
8796 '(z+3/4, y+1/4, -x+1/4)', '(z+1/4, y+3/4, -x+3/4)',
8797 '(z+1/4, -y+1/4, x+3/4)', '(z+3/4, -y+3/4, x+1/4)',
8798 '(-z+1/4, y+3/4, x+1/4)', '(-z+3/4, y+1/4, x+3/4)',
8799 '(-z+3/4, -y+3/4, -x+3/4)',
8800 '(-z+1/4, -y+1/4, -x+1/4)', '(-x, -y, -z)',
8801 '(-x+1/2, -y+1/2, -z+1/2)', '(x+1/2, y, -z+1/2)',
8802 '(x, y+1/2, -z)', '(x, -y+1/2, z+1/2)',
8803 '(x+1/2, -y, z)', '(-x+1/2, y+1/2, z)',
8804 '(-x, y, z+1/2)', '(-z, -x, -y)',
8805 '(-z+1/2, -x+1/2, -y+1/2)', '(-z+1/2, x+1/2, y)',
8806 '(-z, x, y+1/2)', '(z+1/2, x, -y+1/2)',
8807 '(z, x+1/2, -y)', '(z, -x+1/2, y+1/2)',
8808 '(z+1/2, -x, y)', '(-y, -z, -x)',
8809 '(-y+1/2, -z+1/2, -x+1/2)', '(y, -z+1/2, x+1/2)',
8810 '(y+1/2, -z, x)', '(-y+1/2, z+1/2, x)',
8811 '(-y, z, x+1/2)', '(y+1/2, z, -x+1/2)',
8812 '(y, z+1/2, -x)', '(-y+1/4, -x+3/4, z+3/4)',
8813 '(-y+3/4, -x+1/4, z+1/4)', '(y+1/4, x+1/4, z+1/4)',
8814 '(y+3/4, x+3/4, z+3/4)', '(-y+3/4, x+3/4, -z+1/4)',
8815 '(-y+1/4, x+1/4, -z+3/4)',
8816 '(y+3/4, -x+1/4, -z+3/4)',
8817 '(y+1/4, -x+3/4, -z+1/4)',
8818 '(-x+1/4, -z+3/4, y+3/4)',
8819 '(-x+3/4, -z+1/4, y+1/4)',
8820 '(x+3/4, -z+1/4, -y+3/4)',
8821 '(x+1/4, -z+3/4, -y+1/4)', '(x+1/4, z+1/4, y+1/4)',
8822 '(x+3/4, z+3/4, y+3/4)', '(-x+3/4, z+3/4, -y+1/4)',
8823 '(-x+1/4, z+1/4, -y+3/4)',
8824 '(-z+1/4, -y+3/4, x+3/4)',
8825 '(-z+3/4, -y+1/4, x+1/4)',
8826 '(-z+3/4, y+3/4, -x+1/4)',
8827 '(-z+1/4, y+1/4, -x+3/4)',
8828 '(z+3/4, -y+1/4, -x+3/4)',
8829 '(z+1/4, -y+3/4, -x+1/4)', '(z+1/4, y+1/4, x+1/4)',
8830 '(z+3/4, y+3/4, x+3/4)'))},
8831 }
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2010-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 from .algebra import solve_quartic
19 from .fit import (fit_peak2d, gauss_fit, linregress, multGaussFit,
20 multGaussPlot, multPeakFit, multPeakPlot, peak_fit)
21 from .functions import (Debye1, Gauss1d, Gauss1d_der_p, Gauss1d_der_x,
22 Gauss1dArea, Gauss2d, Gauss2dArea, Gauss3d, Lorentz1d,
23 Lorentz1d_der_p, Lorentz1d_der_x, Lorentz1dArea,
24 Lorentz2d, NormGauss1d, NormLorentz1d, PseudoVoigt1d,
25 PseudoVoigt1dArea, PseudoVoigt1dasym,
26 PseudoVoigt1dasym2, PseudoVoigt2d, TwoGauss2d,
27 heaviside, kill_spike, multPeak1d, multPeak2d, smooth)
28 from .misc import center_of_mass, fwhm_exp, gcd
29 from .transforms import (ArbRotation, AxisToZ, AxisToZ_keepXY,
30 CoordinateTransform, Transform, XRotation, YRotation,
31 ZRotation, rotarb)
32 from .vector import (VecAngle, VecCross, VecDot, VecNorm, VecUnit, distance,
33 getSyntax, getVector)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module providing analytic algebraic functions not implemented in scipy or any
19 other dependency of xrayutilities. In particular the analytic solution of a
20 quartic equation which is needed for the solution of the dynamic scattering
21 equations.
22 """
23
24 import numpy
25
26
27 def solve_quartic(a4, a3, a2, a1, a0):
28 """
29 analytic solution [1]_ of the general quartic equation. The solved equation
30 takes the form :math:`a_4 z^4 + a_3 z^3 + a_2 z^2 + a_1 z + a_0`
31
32 Returns
33 -------
34 tuple
35 tuple of the four (complex) solutions of aboves equation.
36
37 References
38 ----------
39 .. [1] http://mathworld.wolfram.com/QuarticEquation.html
40 """
41 a4 = numpy.asarray(a4)
42 a3 = numpy.asarray(a3)
43 a2 = numpy.asarray(a2)
44 a1 = numpy.asarray(a1)
45 a0 = numpy.asarray(a0)
46
47 t01 = 1. / a4
48 t02 = a3**2
49 t04 = t01 * t01
50 t05 = t04 * t01
51 t09 = a1 * t01
52 t10 = (a3 * t02 * t05) / 8
53 t11 = (a3 * a2 * t04) / 2
54 t03 = t09 + t10 - t11
55 t06 = a2 * t01
56 t19 = (3 * t02 * t04) / 8
57 t07 = t06 - t19
58 t08 = t07**2
59 t12 = t03**2
60 t13 = a0 * t01
61 t14 = t05 * t01
62 t15 = t02**2
63 t16 = (a2 * t02 * t05) / 16
64 t20 = (3 * t14 * t15) / 256
65 t21 = (a3 * a1 * t04) / 4
66 t17 = t13 + t16 - t20 - t21
67 t18 = t17**2
68 t22 = t12 / 2.
69 t23 = (t07 * t08) / 27
70 t24 = 3.**(1./2)
71 t25 = t12**2
72 t26 = 27 * t25
73 t27 = t08**2
74 t28 = 4 * t07 * t08 * t12
75 t29 = 128 * t08 * t18
76 t35 = 256 * t17 * t18
77 t36 = 16 * t17 * t27
78 t37 = 144 * t07 * t12 * t17
79 t30 = t26 + t28 + t29 - t35 - t36 - t37
80 t31 = t30.astype(numpy.complex)**(1./2)
81 t32 = (t24 * t31) / 18
82 t34 = (4 * t07 * t17) / 3
83 t33 = t22 + t23 + t32 - t34
84 t38 = 1 / t33.astype(numpy.complex)**(1./6)
85 t39 = t33**(1./3)
86 t40 = 12 * a0 * t01
87 t41 = t33**(2./3)
88 t42 = 9 * t41
89 t43 = (3 * a2 * t02 * t05) / 4
90 t46 = 6 * t07 * t39
91 t47 = (9 * t14 * t15) / 64
92 t48 = 3 * a3 * a1 * t04
93 t44 = t08 + t40 + t42 + t43 - t46 - t47 - t48
94 t45 = t44.astype(numpy.complex)**(1./2)
95 t49 = 6.**(1./2)
96 t50 = 27 * t12
97 t51 = 2 * t07 * t08
98 t52 = 3 * t24 * t31
99 t62 = 72 * t07 * t17
100 t53 = t50 + t51 + t52 - t62
101 t54 = t53.astype(numpy.complex)**(1./2)
102 t55 = 3 * t03 * t49 * t54
103 t59 = t08 * t45
104 t60 = 12 * t17 * t45
105 t61 = 9 * t41 * t45
106 t63 = 12 * t07 * t39 * t45
107 t56 = t55 - t59 - t60 - t61 - t63
108 t57 = t56.astype(numpy.complex)**(1./2)
109 t58 = 1. / t44.astype(numpy.complex)**(1./4)
110 t64 = (t38 * t45) / 6
111 t65 = - t55 - t59 - t60 - t61 - t63
112 t66 = t65.astype(numpy.complex)**(1./2)
113
114 return (-(a3 * t01) / 4 - (t38 * t45) / 6 - (t38 * t57 * t58) / 6,
115 (t38 * t57 * t58) / 6 - (t38 * t45) / 6 - (a3 * t01) / 4,
116 t64 - (a3 * t01) / 4 - (t38 * t58 * t66) / 6,
117 t64 - (a3 * t01) / 4 + (t38 * t58 * t66) / 6)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2012-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16 """
17 module with a function wrapper to scipy.optimize.leastsq
18 for fitting of a 2D function to a peak or a 1D Gauss fit with
19 the odr package
20 """
21
22 import time
23
24 import numpy
25 import scipy.optimize as optimize
26 from scipy.odr import odrpack as odr
27
28 from .. import config, utilities
29 from ..exception import InputError
30 from .functions import (Gauss1d, Gauss1d_der_p, Gauss1d_der_x, Lorentz1d,
31 Lorentz1d_der_p, Lorentz1d_der_x, PseudoVoigt1d,
32 PseudoVoigt1d_der_p, PseudoVoigt1d_der_x,
33 PseudoVoigt1dasym, PseudoVoigt1dasym2)
34 from .misc import center_of_mass, fwhm_exp
35
36
37 def linregress(x, y):
38 """
39 fast linregress to avoid usage of scipy.stats which is slow!
40 NaN values in y are ignored by this function.
41
42 Parameters
43 ----------
44 x, y : array-like
45 data coordinates and values
46
47 Returns
48 -------
49 p : tuple
50 parameters of the linear fit (slope, offset)
51 rsq: float
52 R^2 value
53
54 Examples
55 --------
56 >>> (k, d), R2 = xu.math.linregress(x, y)
57 """
58 mask = numpy.logical_not(numpy.isnan(y))
59 lx, ly = (x[mask], y[mask])
60 if numpy.all(numpy.isclose(lx-lx[0], numpy.zeros_like(lx))):
61 return (0, numpy.mean(ly)), 0
62 else:
63 p = numpy.polyfit(lx, ly, 1)
64
65 # calculation of r-squared
66 f = numpy.polyval(p, lx)
67 fbar = numpy.sum(ly) / len(ly)
68 ssreg = numpy.sum((f-fbar)**2)
69 sstot = numpy.sum((ly - fbar)**2)
70 rsq = ssreg / sstot
71
72 return p, rsq
73
74
75 def peak_fit(xdata, ydata, iparams=[], peaktype='Gauss', maxit=300,
76 background='constant', plot=False, func_out=False, debug=False):
77 """
78 fit function using odr-pack wrapper in scipy similar to
79 https://github.com/tiagopereira/python_tips/wiki/Scipy%3A-curve-fitting
80 for Gauss, Lorentz or Pseudovoigt-functions
81
82 Parameters
83 ----------
84 xdata : array_like
85 x-coordinates of the data to be fitted
86 ydata : array_like
87 y-coordinates of the data which should be fit
88
89 iparams : list, optional
90 initial paramters, determined automatically if not specified
91 peaktype : {'Gauss', 'Lorentz', 'PseudoVoigt', 'PseudoVoigtAsym', 'PseudoVoigtAsym2'}, optional
92 type of peak to fit
93 maxit : int, optional
94 maximal iteration number of the fit
95 background : {'constant', 'linear'}, optional
96 type of background function
97 plot : bool or str, optional
98 flag to ask for a plot to visually judge the fit. If plot is a string
99 it will be used as figure name, which makes reusing the figures easier.
100 func_out : bool, optional
101 returns the fitted function, which takes the independent variables as
102 only argument (f(x))
103
104 Returns
105 -------
106 params : list
107 the parameters as defined in function `Gauss1d/Lorentz1d/PseudoVoigt1d/
108 PseudoVoigt1dasym`. In the case of linear background one more parameter
109 is included!
110 sd_params : list
111 For every parameter the corresponding errors are returned.
112 itlim : bool
113 flag to tell if the iteration limit was reached, should be False
114 fitfunc : function, optional
115 the function used in the fit can be returned (see func_out).
116 """
117 if plot:
118 plot, plt = utilities.import_matplotlib_pyplot('XU.math.peak_fit')
119
120 gfunc, gfunc_dx, gfunc_dp = _getfit_func(peaktype, background)
121
122 # determine initial parameters
123 _check_iparams(iparams, peaktype, background)
124 if not any(iparams):
125 iparams = _guess_iparams(xdata, ydata, peaktype, background)
126 if config.VERBOSITY >= config.DEBUG:
127 print("XU.math.peak_fit: iparams: %s" % str(tuple(iparams)))
128
129 # set up odr fitting
130 peak = odr.Model(gfunc, fjacd=gfunc_dx, fjacb=gfunc_dp)
131
132 sy = numpy.sqrt(ydata)
133 sy[sy == 0] = 1
134 mydata = odr.RealData(xdata, ydata, sy=sy)
135
136 myodr = odr.ODR(mydata, peak, beta0=iparams, maxit=maxit)
137 myodr.set_job(fit_type=2) # use least-square fit
138
139 fit = myodr.run()
140 if config.VERBOSITY >= config.DEBUG:
141 print('XU.math.peak_fit:')
142 fit.pprint() # prints final message from odrpack
143
144 fparam = fit.beta
145 etaidx = []
146 if peaktype in ('PseudoVoigt', 'PseudoVoigtAsym'):
147 if background == 'linear':
148 etaidx = [-2, ]
149 else:
150 etaidx = [-1, ]
151 elif peaktype == 'PseudoVoigtAsym2':
152 etaidx = [5, 6]
153 for e in etaidx:
154 fparam[e] = 0 if fparam[e] < 0 else fparam[e]
155 fparam[e] = 1 if fparam[e] > 1 else fparam[e]
156
157 itlim = False
158 if fit.stopreason[0] == 'Iteration limit reached':
159 itlim = True
160 if config.VERBOSITY >= config.INFO_LOW:
161 print("XU.math.peak_fit: Iteration limit reached, "
162 "do not trust the result!")
163
164 if plot:
165 if isinstance(plot, str):
166 plt.figure(plot)
167 else:
168 plt.figure('XU:peak_fit')
169 plt.plot(xdata, ydata, 'ko', label='data', mew=2)
170 if debug:
171 plt.plot(xdata, gfunc(iparams, xdata), '-', color='0.5',
172 label='estimate')
173 plt.plot(xdata, gfunc(fparam, xdata), 'r-',
174 label='%s-fit' % peaktype)
175 plt.legend()
176
177 if func_out:
178 return fparam, fit.sd_beta, itlim, lambda x: gfunc(fparam, x)
179 else:
180 return fparam, fit.sd_beta, itlim
181
182
183 def _getfit_func(peaktype, background):
184 """
185 internal function to prepare the model functions and derivatives for the
186 peak_fit function.
187
188 Parameters
189 ----------
190 peaktype : {'Gauss', 'Lorentz', 'PseudoVoigt', 'PseudoVoigtAsym', 'PseudoVoigtAsym2'}
191 type of peak function
192 background : {'constant', 'linear'}
193 type of background function
194
195 Returns
196 -------
197 f, f_dx, f_dp : functions
198 fit function, function of derivative regarding `x`, and functions of
199 derivatives regarding the parameters
200 """
201 gfunc_dx = None
202 gfunc_dp = None
203 if peaktype == 'Gauss':
204 f = Gauss1d
205 fdx = Gauss1d_der_x
206 fdp = Gauss1d_der_p
207 elif peaktype == 'Lorentz':
208 f = Lorentz1d
209 fdx = Lorentz1d_der_x
210 fdp = Lorentz1d_der_p
211 elif peaktype == 'PseudoVoigt':
212 f = PseudoVoigt1d
213 fdx = PseudoVoigt1d_der_x
214 fdp = PseudoVoigt1d_der_p
215 elif peaktype == 'PseudoVoigtAsym':
216 if background == 'linear':
217 def gfunc(param, x):
218 return PseudoVoigt1dasym(x, *param) + x * param[-1]
219 else:
220 def gfunc(param, x):
221 return PseudoVoigt1dasym(x, *param)
222 elif peaktype == 'PseudoVoigtAsym2':
223 if background == 'linear':
224 def gfunc(param, x):
225 return PseudoVoigt1dasym2(x, *param) + x * param[-1]
226 else:
227 def gfunc(param, x):
228 return PseudoVoigt1dasym2(x, *param)
229 else:
230 raise InputError("keyword argument peaktype takes invalid value!")
231
232 if peaktype in ('Gauss', 'Lorentz', 'PseudoVoigt'):
233 if background == 'linear':
234 def gfunc(param, x):
235 return f(x, *param) + x * param[-1]
236
237 def gfunc_dx(param, x):
238 return fdx(x, *param) + param[-1]
239
240 def gfunc_dp(param, x):
241 return numpy.vstack((fdp(x, *param), x))
242 else:
243 def gfunc(param, x):
244 return f(x, *param)
245
246 def gfunc_dx(param, x):
247 return fdx(x, *param)
248
249 def gfunc_dp(param, x):
250 return fdp(x, *param)
251 return gfunc, gfunc_dx, gfunc_dp
252
253
254 def _check_iparams(iparams, peaktype, background):
255 """
256 internal function to check if the length of the supplied initial
257 parameters is correct given the other settings of the peak_fit function.
258 An InputError is raised in case of wrong shape or value.
259
260 Parameters
261 ----------
262 iparams : list
263 initial paramters for the fit
264 peaktype : {'Gauss', 'Lorentz', 'PseudoVoigt', 'PseudoVoigtAsym', 'PseudoVoigtAsym2'}
265 type of peak to fit
266 background : {'constant', 'linear'}
267 type of background
268
269 """
270 if not any(iparams):
271 return
272 else:
273 ptypes = {('Gauss', 'constant'): 4, ('Lorentz', 'constant'): 4,
274 ('Gauss', 'linear'): 5, ('Lorentz', 'linear'): 5,
275 ('PseudoVoigt', 'constant'): 5, ('PseudoVoigt', 'linear'): 6,
276 ('PseudoVoigtAsym', 'constant'): 6,
277 ('PseudoVoigtAsym', 'linear'): 7,
278 ('PseudoVoigtAsym2', 'constant'): 7,
279 ('PseudoVoigtAsym2', 'linear'): 8}
280 if not all(numpy.isreal(iparams)):
281 raise InputError("XU.math.peak_fit: all initial parameters need to"
282 "be real!")
283 elif (peaktype, background) in ptypes:
284 nparams = ptypes[(peaktype, background)]
285 if len(iparams) != nparams:
286 raise InputError("XU.math.peak_fit: %d initial parameters "
287 "are needed for %s-peak with %s background."
288 % (nparams, peaktype, background))
289 else:
290 raise InputError("XU.math.peak_fit: invalid peak (%s) or "
291 "background (%s)" % (peaktype, background))
292
293
294 def _guess_iparams(xdata, ydata, peaktype, background):
295 """
296 internal function to automatically esitmate peak parameters from the data,
297 considering also the background type.
298
299 Parameters
300 ----------
301 xdata : array-like
302 x-coordinates of the data to be fitted
303 ydata : array-like
304 y-coordinates of the data which should be fit
305 peaktype : {'Gauss', 'Lorentz', 'PseudoVoigt', 'PseudoVoigtAsym', 'PseudoVoigtAsym2'}
306 type of peak to fit
307 background : {'constant', 'linear'}
308 type of background, either
309
310 Returns
311 -------
312 list of initial parameters estimated from the data
313 """
314 ld = numpy.empty(len(ydata))
315 # estimate peak position
316 ipos, ld, back, slope = center_of_mass(xdata, ydata, background,
317 full_output=True)
318 maxpos = xdata[numpy.argmax(ld)]
319 avx = numpy.average(xdata)
320 if numpy.abs(ipos - avx) < numpy.abs(maxpos-avx):
321 ipos = maxpos # use the estimate which is further from the center
322
323 # estimate peak width
324 sigma1 = numpy.sqrt(numpy.sum(numpy.abs((xdata - ipos) ** 2 * ld)) /
325 numpy.abs(numpy.sum(ld)))
326 sigma2 = fwhm_exp(xdata, ld)/(2 * numpy.sqrt(2 * numpy.log(2)))
327 sigma = sigma1 if sigma1 < sigma2 else sigma2
328
329 # build initial parameters
330 iparams = [ipos, sigma, numpy.max(ld), back]
331 if peaktype in ['Lorentz', 'PseudoVoigt']:
332 iparams[1] *= 2 * numpy.sqrt(2 * numpy.log(2))
333 if peaktype in ['PseudoVoigtAsym', 'PseudoVoigtAsym2']:
334 iparams.insert(1, iparams[1])
335 if peaktype in ['PseudoVoigt', 'PseudoVoigtAsym']:
336 # set ETA parameter to be between Gauss and Lorentz shape
337 iparams.append(0.5)
338 if peaktype == 'PseudoVoigtAsym2':
339 iparams.append(0.5)
340 iparams.append(0.5)
341 if background == 'linear':
342 iparams.append(slope)
343 return iparams
344
345
346 def gauss_fit(xdata, ydata, iparams=[], maxit=300):
347 """
348 Gauss fit function using odr-pack wrapper in scipy similar to
349 https://github.com/tiagopereira/python_tips/wiki/Scipy%3A-curve-fitting
350
351 Parameters
352 ----------
353 xdata : array-like
354 x-coordinates of the data to be fitted
355 ydata : array-like
356 y-coordinates of the data which should be fit
357 iparams: list, optional
358 initial paramters for the fit, determined automatically if not given
359 maxit : int, optional
360 maximal iteration number of the fit
361
362 Returns
363 -------
364 params : list
365 the parameters as defined in function ``Gauss1d(x, *param)``
366 sd_params : list
367 For every parameter the corresponding errors are returned.
368 itlim : bool
369 flag to tell if the iteration limit was reached, should be False
370 """
371 return peak_fit(xdata, ydata, iparams=iparams,
372 peaktype='Gauss', maxit=maxit)
373
374
375 def fit_peak2d(x, y, data, start, drange, fit_function, maxfev=2000):
376 """
377 fit a two dimensional function to a two dimensional data set
378 e.g. a reciprocal space map
379
380 Parameters
381 ----------
382 x, y : array-like
383 data coordinates (do NOT need to be regularly spaced)
384 data : array-like
385 data set used for fitting (e.g. intensity at the data coords)
386 start : list
387 set of starting parameters for the fit used as first parameter of
388 function fit_function
389 drange : list
390 limits for the data ranges used in the fitting algorithm, e.g. it is
391 clever to use only a small region around the peak which should be
392 fitted: [xmin, xmax, ymin, ymax]
393 fit_function : callable
394 function which should be fitted, must be of form accept the parameters
395 ``fit_function (x, y, *params) -> ndarray``
396
397 Returns
398 -------
399 fitparam : list
400 fitted parameters
401 cov : array-like
402 covariance matrix
403 """
404 s = time.time()
405 if config.VERBOSITY >= config.INFO_ALL:
406 print("XU.math.fit: Fitting started... ", end='')
407
408 start = numpy.array(start)
409 lx = x.flatten()
410 ly = y.flatten()
411 mask = (lx > drange[0]) * (lx < drange[1]) * \
412 (ly > drange[2]) * (ly < drange[3])
413 ly = ly[mask]
414 lx = lx[mask]
415 ldata = data.flatten()[mask]
416
417 def errfunc(p, x, z, data):
418 return fit_function(x, z, *p) - data
419
420 p, cov, infodict, errmsg, success = optimize.leastsq(
421 errfunc, start, args=(lx, ly, ldata), full_output=1, maxfev=maxfev)
422
423 s = time.time() - s
424 if config.VERBOSITY >= config.INFO_ALL:
425 print("finished in %8.2f sec, (data length used %d)" % (s, ldata.size))
426 print("XU.math.fit: %s" % errmsg)
427
428 # calculate correct variance covariance matrix
429 if cov is not None:
430 s_sq = (errfunc(p, lx, ly, ldata) ** 2).sum() / \
431 (len(ldata) - len(start))
432 pcov = cov * s_sq
433 else:
434 pcov = numpy.zeros((len(start), len(start)))
435
436 if success not in [1, 2, 3, 4]:
437 print("XU.math.fit: Could not obtain fit!")
438 return p, pcov
439
440
441 def multGaussFit(*args, **kwargs):
442 """
443 convenience function to keep API stable
444 see multPeakFit for documentation
445 """
446 kwargs['peaktype'] = 'Gaussian'
447 return multPeakFit(*args, **kwargs)
448
449
450 def multPeakFit(x, data, peakpos, peakwidth, dranges=None,
451 peaktype='Gaussian'):
452 """
453 function to fit multiple Gaussian/Lorentzian peaks with linear background
454 to a set of data
455
456 Parameters
457 ----------
458 x : array-like
459 x-coordinate of the data
460 data : array-like
461 data array with same length as `x`
462 peakpos : list
463 initial parameters for the peak positions
464 peakwidth : list
465 initial values for the peak width
466 dranges : list of tuples
467 list of tuples with (min, max) value of the data ranges to use. does
468 not need to have the same number of entries as peakpos
469 peaktype : {'Gaussian', 'Lorentzian'}
470 type of peaks to be used
471
472 Returns
473 -------
474 pos : list
475 peak positions derived by the fit
476 sigma : list
477 peak width derived by the fit
478 amp : list
479 amplitudes of the peaks derived by the fit
480 background : array-like
481 background values at positions `x`
482 """
483 if peaktype == 'Gaussian':
484 pfunc = Gauss1d
485 pfunc_derx = Gauss1d_der_x
486 elif peaktype == 'Lorentzian':
487 pfunc = Lorentz1d
488 pfunc_derx = Lorentz1d_der_x
489 else:
490 raise ValueError('wrong value for parameter peaktype was given')
491
492 def deriv_x(p, x):
493 """
494 function to calculate the derivative of the signal of multiple peaks
495 and background w.r.t. the x-coordinate
496
497 Parameters
498 ----------
499 p : list
500 parameters, for every peak there needs to be position, sigma,
501 amplitude and at the end two values for the linear background
502 function (b0, b1)
503 x : array-like
504 x-coordinate
505 """
506 derx = numpy.zeros(x.size)
507
508 # sum up peak functions contributions
509 for i in range(len(p) // 3):
510 ldx = pfunc_derx(x, p[3 * i], p[3 * i + 1], p[3 * i + 2], 0)
511 derx += ldx
512
513 # background contribution
514 k = p[-2]
515 b = numpy.ones(x.size) * k
516
517 return derx + b
518
519 def deriv_p(p, x):
520 """
521 function to calculate the derivative of the signal of multiple peaks
522 and background w.r.t. the parameters
523
524 Parameters
525 ----------
526 p : list
527 parameters, for every peak there needs to be position, sigma,
528 amplitude and at the end two values for the linear background
529 function (b0, b1)
530 x : array-like
531 x-coordinate
532
533 returns derivative w.r.t. all the parameters with shape (len(p),x.size)
534 """
535
536 derp = numpy.empty(0)
537 # peak functions contributions
538 for i in range(len(p) // 3):
539 lp = (p[3 * i], p[3 * i + 1], p[3 * i + 2], 0)
540 if peaktype == 'Gaussian':
541 derp = numpy.append(derp, -2 * (lp[0] - x) * pfunc(x, *lp))
542 derp = numpy.append(
543 derp, (lp[0] - x) ** 2 / (2 * lp[1] ** 3) * pfunc(x, *lp))
544 derp = numpy.append(derp, pfunc(x, *lp) / lp[2])
545 else: # Lorentzian
546 derp = numpy.append(derp, 4 * (x - lp[0]) * lp[2] / lp[1] /
547 (1 + (2 * (x - lp[0]) / lp[1]) ** 2) ** 2)
548 derp = numpy.append(derp, 4 * (lp[0] - x) * lp[2] /
549 lp[1] ** 2 / (1 + (2 * (x - lp[0]) /
550 lp[1]) ** 2) ** 2)
551 derp = numpy.append(derp,
552 1 / (1 + (2 * (x - p[0]) / p[1]) ** 2))
553
554 # background contributions
555 derp = numpy.append(derp, x)
556 derp = numpy.append(derp, numpy.ones(x.size))
557
558 # reshape output
559 derp.shape = (len(p),) + x.shape
560 return derp
561
562 def fsignal(p, x):
563 """
564 function to calculate the signal of multiple peaks and background
565
566 Parameters
567 ----------
568 p : list
569 list of parameters, for every peak there needs to be position,
570 sigma, amplitude and at the end two values for the linear
571 background function (k, d)
572 x : array-like
573 x-coordinate
574 """
575 f = numpy.zeros(x.size)
576
577 # sum up peak functions
578 for i in range(len(p) // 3):
579 lf = pfunc(x, p[3 * i], p[3 * i + 1], p[3 * i + 2], 0)
580 f += lf
581
582 # background
583 k = p[-2]
584 d = p[-1]
585 b = numpy.polyval((k, d), x)
586
587 return f + b
588
589 ##########################
590 # create local data set (extract data ranges)
591 if dranges:
592 mask = numpy.array([False] * x.size)
593 for i in range(len(dranges)):
594 lrange = dranges[i]
595 lmask = numpy.logical_and(x > lrange[0], x < lrange[1])
596 mask = numpy.logical_or(mask, lmask)
597 lx = x[mask]
598 ldata = data[mask]
599 else:
600 lx = x
601 ldata = data
602
603 # create initial parameter list
604 p = []
605
606 # background
607 k, d = numpy.polyfit(lx, ldata, 1)
608
609 # peak parameters
610 for i in range(len(peakpos)):
611 amp = ldata[(lx - peakpos[i]) >= 0][0] - \
612 numpy.polyval((k, d), lx)[(lx - peakpos[i]) >= 0][0]
613 p += [peakpos[i], peakwidth[i], amp]
614
615 # background parameters
616 p += [k, d]
617
618 if(config.VERBOSITY >= config.DEBUG):
619 print("XU.math.multGaussFit: intial parameters")
620 print(p)
621
622 ##########################
623 # fit with odrpack
624 model = odr.Model(fsignal, fjacd=deriv_x, fjacb=deriv_p)
625 odata = odr.RealData(lx, ldata)
626 my_odr = odr.ODR(odata, model, beta0=p)
627 # fit type 2 for least squares
628 my_odr.set_job(fit_type=2)
629 fit = my_odr.run()
630
631 if(config.VERBOSITY >= config.DEBUG):
632 print("XU.math.multPeakFit: fitted parameters")
633 print(fit.beta)
634 try:
635 if fit.stopreason[0] not in ['Sum of squares convergence']:
636 print("XU.math.multPeakFit: fit NOT converged (%s)"
637 % fit.stopreason[0])
638 return None, None, None, None
639 except IndexError:
640 print("XU.math.multPeakFit: fit most probably NOT converged (%s)"
641 % str(fit.stopreason))
642 return None, None, None, None
643 # prepare return values
644 fpos = fit.beta[:-2:3]
645 fwidth = numpy.abs(fit.beta[1:-2:3])
646 famp = fit.beta[2::3]
647 background = numpy.polyval((fit.beta[-2], fit.beta[-1]), x)
648
649 return fpos, fwidth, famp, background
650
651
652 def multGaussPlot(*args, **kwargs):
653 """
654 convenience function to keep API stable
655 see multPeakPlot for documentation
656 """
657 kwargs['peaktype'] = 'Gaussian'
658 return multPeakPlot(*args, **kwargs)
659
660
661 def multPeakPlot(x, fpos, fwidth, famp, background, dranges=None,
662 peaktype='Gaussian', fig="xu_plot", ax=None, fact=1.):
663 """
664 function to plot multiple Gaussian/Lorentz peaks with background values
665 given by an array
666
667 Parameters
668 ----------
669 x : array-like
670 x-coordinate of the data
671 fpos : list
672 positions of the peaks
673 fwidth : list
674 width of the peaks
675 famp : list
676 amplitudes of the peaks
677 background : array-like
678 background values, same shape as `x`
679 dranges : list of tuples
680 list of (min, max) values of the data ranges to use. does not need to
681 have the same number of entries as fpos
682 peaktype : {'Gaussian', 'Lorentzian'}
683 type of peaks to be used
684 fig : int, str, or None
685 matplotlib figure number or name
686 ax : matplotlib.Axes
687 matplotlib axes as alternative to the figure name
688 fact : float
689 factor to use as multiplicator in the plot
690 """
691 success, plt = utilities.import_matplotlib_pyplot('XU.math.multPeakPlot')
692 if not success:
693 return
694
695 if fig:
696 plt.figure(fig)
697 if ax:
698 plt.sca(ax)
699 # plot single peaks
700 if dranges:
701 mask = numpy.array([False] * x.size)
702 for i in range(len(dranges)):
703 lrange = dranges[i]
704 lmask = numpy.logical_and(x > lrange[0], x < lrange[1])
705 mask = numpy.logical_or(mask, lmask)
706 lx = x[mask]
707 lb = background[mask]
708 else:
709 lx = x
710 lb = background
711
712 f = numpy.zeros(lx.size)
713 for i in range(len(fpos)):
714 if peaktype == 'Gaussian':
715 lf = Gauss1d(lx, fpos[i], fwidth[i], famp[i], 0)
716 elif peaktype == 'Lorentzian':
717 lf = Lorentz1d(lx, fpos[i], fwidth[i], famp[i], 0)
718 else:
719 raise ValueError('wrong value for parameter peaktype was given')
720 f += lf
721 plt.plot(lx, (lf + lb) * fact, 'k:')
722
723 # plot summed signal
724 plt.plot(lx, (f + lb) * fact, 'r-', lw=1.5)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2012-2014 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module with several common function needed in xray data analysis
19 """
20
21 import copy
22 import numbers
23
24 import numpy
25 import scipy.integrate
26
27 from .. import config
28
29
30 def smooth(x, n):
31 """
32 function to smooth an array of data by averaging N adjacent data points
33
34 Parameters
35 ----------
36 x : array-like
37 1D data array
38 n : int
39 number of data points to average
40
41 Returns
42 -------
43 xsmooth: array-like
44 smoothed array with same length as x
45 """
46 if x.ndim != 1:
47 raise ValueError("smooth only accepts 1 dimension arrays.")
48 if x.size < n:
49 raise ValueError("Input vector needs to be bigger than n.")
50 if n < 2:
51 return x
52 # avoid boundary effects by adding mirrored signal at the boundaries
53 s = numpy.r_[x[n - 1:0:-1], x, x[-1:-n - 1:-1]]
54 w = numpy.ones(n, 'd')
55 y = numpy.convolve(w / w.sum(), s, mode='same')
56 return y[n:-n + 1]
57
58
59 def kill_spike(data, threshold=2., offset=None):
60 """
61 function to smooth **single** data points which differ from the average of
62 the neighboring data points by more than the threshold factor or more than
63 the offset value. Such spikes will be replaced by the mean value of the
64 next neighbors.
65
66 .. warning:: Use this function carefully not to manipulate your data!
67
68 Parameters
69 ----------
70 data : array-like
71 1d numpy array with experimental data
72 threshold : float or None
73 threshold factor to identify outlier data points. If None it will be
74 ignored.
75 offset : None or float
76 offset value to identify outlier data points. If None it will be
77 ignored.
78
79 Returns
80 -------
81 array-like
82 1d data-array with spikes removed
83 """
84
85 dataout = data.copy()
86
87 mean = (data[:-2] + data[2:]) / 2.
88 mask = numpy.zeros_like(data[1:-1], dtype=numpy.bool)
89 if threshold:
90 mask = numpy.logical_or(
91 mask, numpy.logical_or(data[1:-1] * threshold < mean,
92 data[1:-1] / threshold > mean))
93 if offset:
94 mask = numpy.logical_or(
95 mask, numpy.logical_or(data[1:-1] + offset < mean,
96 data[1:-1] - offset > mean))
97 # ensure that only single value are corrected and neighboring are ignored
98 for i in range(1, len(mask) - 1):
99 if mask[i - 1] and mask[i] and mask[i + 1]:
100 mask[i - 1] = False
101 mask[i + 1] = False
102
103 dataout[1:-1][mask] = mean[mask]
104
105 return dataout
106
107
108 def Gauss1d(x, *p):
109 """
110 function to calculate a general one dimensional Gaussian
111
112 Parameters
113 ----------
114 x : array-like
115 coordinate(s) where the function should be evaluated
116 p : list
117 list of parameters of the Gaussian [XCEN, SIGMA, AMP, BACKGROUND]
118 for information: SIGMA = FWHM / (2*sqrt(2*log(2)))
119
120 Returns
121 -------
122 array-like
123 the value of the Gaussian described by the parameters p at position x
124
125 Examples
126 --------
127 Calling with a list of parameters needs a call looking as shown below
128 (note the '*') or explicit listing of the parameters
129
130 >>> Gauss1d(x,*p)
131
132 >>> Gauss1d(numpy.linspace(0, 10, 100), 5, 1, 1e3, 0)
133 """
134 g = p[3] + p[2] * numpy.exp(-((p[0] - x) / p[1]) ** 2 / 2.)
135 return g
136
137
138 def NormGauss1d(x, *p):
139 """
140 function to calculate a normalized one dimensional Gaussian
141
142 Parameters
143 ----------
144 x : array-like
145 coordinate(s) where the function should be evaluated
146 p : list
147 list of parameters of the Gaussian [XCEN, SIGMA];
148 for information: SIGMA = FWHM / (2*sqrt(2*log(2)))
149
150 Returns
151 -------
152 array-like
153 the value of the normalized Gaussian described by the parameters p at
154 position x
155 """
156 g = numpy.exp(-((p[0] - x) / p[1]) ** 2 / 2.)
157 a = numpy.sqrt(2 * numpy.pi) * p[1]
158 return g / a
159
160
161 def Gauss1d_der_x(x, *p):
162 """
163 function to calculate the derivative of a Gaussian with respect to x
164
165 for parameter description see Gauss1d
166 """
167
168 lp = numpy.copy(p)
169 lp[3] = 0
170 return 2 * (p[0] - x) * Gauss1d(x, *lp)
171
172
173 def Gauss1d_der_p(x, *p):
174 """
175 function to calculate the derivative of a Gaussian with respect the
176 parameters p
177
178 for parameter description see Gauss1d
179 """
180 lp = numpy.copy(p)
181 lp[3] = 0
182 r = numpy.vstack((-2 * (p[0] - x) * Gauss1d(x, *lp),
183 (p[0] - x) ** 2 / (2 * p[1] ** 3) * Gauss1d(x, *lp),
184 Gauss1d(x, *lp) / p[2],
185 numpy.ones(x.shape, dtype=numpy.float)))
186
187 return r
188
189
190 def Gauss2d(x, y, *p):
191 """
192 function to calculate a general two dimensional Gaussian
193
194 Parameters
195 ----------
196 x, y : array-like
197 coordinate(s) where the function should be evaluated
198 p : list
199 list of parameters of the Gauss-function
200 [XCEN, YCEN, SIGMAX, SIGMAY, AMP, BACKGROUND, ANGLE];
201 SIGMA = FWHM / (2*sqrt(2*log(2)));
202 ANGLE = rotation of the X, Y direction of the Gaussian in radians
203
204 Returns
205 -------
206 array-like
207 the value of the Gaussian described by the parameters p at
208 position (x, y)
209 """
210
211 rcen_x = p[0] * numpy.cos(p[6]) - p[1] * numpy.sin(p[6])
212 rcen_y = p[0] * numpy.sin(p[6]) + p[1] * numpy.cos(p[6])
213 xp = x * numpy.cos(p[6]) - y * numpy.sin(p[6])
214 yp = x * numpy.sin(p[6]) + y * numpy.cos(p[6])
215
216 g = p[5] + p[4] * numpy.exp(-(((rcen_x - xp) / p[2]) ** 2 +
217 ((rcen_y - yp) / p[3]) ** 2) / 2.)
218 return g
219
220
221 def Gauss3d(x, y, z, *p):
222 """
223 function to calculate a general three dimensional Gaussian
224
225 Parameters
226 ----------
227 x, y, z : array-like
228 coordinate(s) where the function should be evaluated
229 p : list
230 list of parameters of the Gauss-function
231 [XCEN, YCEN, ZCEN, SIGMAX, SIGMAY, SIGMAZ, AMP, BACKGROUND];
232
233 SIGMA = FWHM / (2*sqrt(2*log(2)))
234
235 Returns
236 -------
237 array-like
238 the value of the Gaussian described by the parameters p at
239 positions (x, y, z)
240 """
241
242 g = p[7] + p[6] * numpy.exp(-(((x - p[0]) / p[3]) ** 2 +
243 ((y - p[1]) / p[4]) ** 2 +
244 ((z - p[2]) / p[5]) ** 2) / 2.)
245 return g
246
247
248 def TwoGauss2d(x, y, *p):
249 """
250 function to calculate two general two dimensional Gaussians
251
252 Parameters
253 ----------
254 x, y : array-like
255 coordinate(s) where the function should be evaluated
256 p : list
257 list of parameters of the Gauss-function
258 [XCEN1, YCEN1, SIGMAX1, SIGMAY1, AMP1, ANGLE1, XCEN2, YCEN2, SIGMAX2,
259 SIGMAY2, AMP2, ANGLE2, BACKGROUND];
260 SIGMA = FWHM / (2*sqrt(2*log(2)))
261 ANGLE = rotation of the X, Y direction of the Gaussian in radians
262
263 Returns
264 -------
265 array-like
266 the value of the Gaussian described by the parameters p
267 at position (x, y)
268 """
269
270 p = list(p)
271 p1 = p[0:5] + [p[12], ] + [p[5], ]
272 p2 = p[6:11] + [p[12], ] + [p[11], ]
273
274 g = Gauss2d(x, y, *p1) + Gauss2d(x, y, *p2)
275
276 return g
277
278
279 def Lorentz1d(x, *p):
280 """
281 function to calculate a general one dimensional Lorentzian
282
283 Parameters
284 ----------
285 x : array-like
286 coordinate(s) where the function should be evaluated
287 p : list
288 list of parameters of the Lorentz-function
289 [XCEN, FWHM, AMP, BACKGROUND]
290
291 Returns
292 -------
293 array-like
294 the value of the Lorentian described by the parameters p
295 at position (x, y)
296
297 """
298 g = p[3] + p[2] / (1 + (2 * (x - p[0]) / p[1]) ** 2)
299 return g
300
301
302 def Lorentz1d_der_x(x, *p):
303 """
304 function to calculate the derivative of a Gaussian with respect to x
305
306 for parameter description see Lorentz1d
307 """
308
309 return 4 * (p[0] - x) * p[2] / p[1] / \
310 (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2
311
312
313 def Lorentz1d_der_p(x, *p):
314 """
315 function to calculate the derivative of a Gaussian with respect the
316 parameters p
317
318 for parameter description see Lorentz1d
319 """
320 r = numpy.vstack((
321 4 * (x - p[0]) * p[2] / p[1] / (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2,
322 4 * (p[0] - x) * p[2] / p[1] ** 2 /
323 (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2,
324 1 / (1 + (2 * (x - p[0]) / p[1]) ** 2),
325 numpy.ones(x.shape, dtype=numpy.float)))
326 return r
327
328
329 def NormLorentz1d(x, *p):
330 """
331 function to calculate a normalized one dimensional Lorentzian
332
333 Parameters
334 ----------
335 x : array-like
336 coordinate(s) where the function should be evaluated
337 p : list
338 list of parameters of the Lorentzian [XCEN, FWHM]
339
340 Returns
341 -------
342 array-like
343 the value of the normalized Lorentzian described by the parameters p
344 at position x
345 """
346 g = 1.0 / (1 + (2 * (x - p[0]) / p[1]) ** 2)
347 a = numpy.pi / (2. / (p[1]))
348 return g / a
349
350
351 def Lorentz2d(x, y, *p):
352 """
353 function to calculate a general two dimensional Lorentzian
354
355 Parameters
356 ----------
357 x, y : array-like
358 coordinate(s) where the function should be evaluated
359 p : list
360 list of parameters of the Lorentz-function
361 [XCEN, YCEN, FWHMX, FWHMY, AMP, BACKGROUND, ANGLE];
362 ANGLE = rotation of the X, Y direction of the Lorentzian in radians
363
364 Returns
365 -------
366 array-like
367 the value of the Lorentian described by the parameters p
368 at position (x, y)
369 """
370 rcen_x = p[0] * numpy.cos(p[6]) - p[1] * numpy.sin(p[6])
371 rcen_y = p[0] * numpy.sin(p[6]) + p[1] * numpy.cos(p[6])
372 xp = x * numpy.cos(p[6]) - y * numpy.sin(p[6])
373 yp = x * numpy.sin(p[6]) + y * numpy.cos(p[6])
374
375 g = p[5] + p[4] / (1 + (2 * (rcen_x - xp) / p[2]) **
376 2 + (2 * (rcen_y - yp) / p[3]) ** 2)
377 return g
378
379
380 def PseudoVoigt1d(x, *p):
381 """
382 function to calculate a pseudo Voigt function as linear combination of a
383 Gauss and Lorentz peak
384
385 Parameters
386 ----------
387 x : array-like
388 coordinate(s) where the function should be evaluated
389 p : list
390 list of parameters of the pseudo Voigt-function
391 [XCEN, FWHM, AMP, BACKGROUND, ETA];
392 ETA: 0 ...1 0 means pure Gauss and 1 means pure Lorentz
393
394 Returns
395 -------
396 array-like
397 the value of the PseudoVoigt described by the parameters p
398 at position `x`
399 """
400 if p[4] > 1.0:
401 pv = 1.0
402 elif p[4] < 0.:
403 pv = 0.0
404 else:
405 pv = p[4]
406
407 sigma = p[1] / (2 * numpy.sqrt(2 * numpy.log(2)))
408 f = p[3] + pv * Lorentz1d(x, p[0], p[1], p[2], 0) + \
409 (1 - pv) * Gauss1d(x, p[0], sigma, p[2], 0)
410 return f
411
412
413 def PseudoVoigt1d_der_x(x, *p):
414 """
415 function to calculate the derivative of a PseudoVoigt with respect to `x`
416
417 for parameter description see PseudoVoigt1d
418 """
419 if p[4] > 1.0:
420 pv = 1.0
421 elif p[4] < 0.:
422 pv = 0.0
423 else:
424 pv = p[4]
425
426 lp = numpy.copy(p)
427 lp[3] = 0
428 lp[1] = p[1] / (2 * numpy.sqrt(2 * numpy.log(2)))
429
430 gdx = 2 * (p[0] - x) * Gauss1d(x, *lp)
431 ldx = 4 * (p[0] - x) * p[2] / p[1] / \
432 (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2
433 return pv * ldx + (1 - pv) * gdx
434
435
436 def PseudoVoigt1d_der_p(x, *p):
437 """
438 function to calculate the derivative of a PseudoVoigt with respect the
439 parameters `p`
440
441 for parameter description see PseudoVoigt1d
442 """
443
444 if p[4] > 1.0:
445 pv = 1.0
446 elif p[4] < 0.:
447 pv = 0.0
448 else:
449 pv = p[4]
450
451 lpg = numpy.copy(p) # local parameters for gaussian
452 lpg[3] = 0
453 lpl = numpy.copy(lpg) # local parameters for lorentzian
454 lpg[1] = p[1] / (2 * numpy.sqrt(2 * numpy.log(2)))
455
456 rl = numpy.vstack((
457 4 * (x - p[0]) * p[2] / p[1] / (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2,
458 4 * (p[0] - x) * p[2] / p[1] ** 2 /
459 (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2,
460 1 / (1 + (2 * (x - p[0]) / p[1]) ** 2)))
461
462 rg = numpy.vstack((-2 * (lpg[0] - x) * Gauss1d(x, *lpg),
463 (lpg[0] - x) ** 2 /
464 (2 * lpg[1] ** 3) * Gauss1d(x, *lpg),
465 Gauss1d(x, *lpg) / lpg[2]))
466
467 r = pv * rl + (1 - pv) * rg
468 r = numpy.vstack((r,
469 numpy.ones(x.shape),
470 Lorentz1d(x, *lpl) - Gauss1d(x, *lpg)))
471 return r
472
473
474 def PseudoVoigt1dasym(x, *p):
475 """
476 function to calculate an asymmetric pseudo Voigt function as linear
477 combination of asymmetric Gauss and Lorentz peak
478
479 Parameters
480 ----------
481 x : array-like
482 coordinate(s) where the function should be evaluated
483 p : list
484 list of parameters of the pseudo Voigt-function
485 [XCEN, FWHMLEFT, FWHMRIGHT, AMP, BACKGROUND, ETA];
486 ETA: 0 ...1 0 means pure Gauss and 1 means pure Lorentz
487
488 Returns
489 -------
490 array-like
491 the value of the PseudoVoigt described by the parameters p
492 at position `x`
493 """
494 lp = copy.copy(list(p))
495 lp.insert(6, p[5])
496 return PseudoVoigt1dasym2(x, *lp)
497
498
499 def PseudoVoigt1dasym2(x, *p):
500 """
501 function to calculate an asymmetric pseudo Voigt function as linear
502 combination of asymmetric Gauss and Lorentz peak
503
504 Parameters
505 ----------
506 x : naddray
507 coordinate(s) where the function should be evaluated
508 p : list
509 list of parameters of the pseudo Voigt-function
510 [XCEN, FWHMLEFT, FWHMRIGHT, AMP, BACKGROUND, ETALEFT, ETARIGHT];
511 ETA: 0 ...1 0 means pure Gauss and 1 means pure Lorentz
512
513 Returns
514 -------
515 array-like
516 the value of the PseudoVoigt described by the parameters p
517 at position `x`
518 """
519 pvl = p[5] if p[5] < 1.0 else 1.0
520 pvl = pvl if p[5] > 0.0 else 0.0
521 pvr = p[6] if p[6] < 1.0 else 1.0
522 pvr = pvr if p[6] > 0.0 else 0.0
523
524 sigmal = p[1] / (2 * numpy.sqrt(2 * numpy.log(2)))
525 sigmar = p[2] / (2 * numpy.sqrt(2 * numpy.log(2)))
526
527 if isinstance(x, numbers.Number):
528 if x < p[0]:
529 f = p[4] + pvl * Lorentz1d(x, p[0], p[1], p[3], 0) + \
530 (1 - pvl) * Gauss1d(x, p[0], sigmal, p[3], 0)
531 else:
532 f = p[4] + pvr * Lorentz1d(x, p[0], p[2], p[3], 0) + \
533 (1 - pvr) * Gauss1d(x, p[0], sigmar, p[3], 0)
534 else:
535 lx = numpy.asarray(x)
536 f = numpy.zeros(lx.shape)
537 f[lx < p[0]] = (p[4] + pvl *
538 Lorentz1d(lx[x < p[0]], p[0], p[1], p[3], 0) +
539 (1 - pvl) *
540 Gauss1d(lx[x < p[0]], p[0], sigmal, p[3], 0))
541 f[lx >= p[0]] = (p[4] + pvr *
542 Lorentz1d(lx[x >= p[0]], p[0], p[2], p[3], 0) +
543 (1 - pvr) *
544 Gauss1d(lx[x >= p[0]], p[0], sigmar, p[3], 0))
545
546 return f
547
548
549 def PseudoVoigt2d(x, y, *p):
550 """
551 function to calculate a pseudo Voigt function as linear combination of a
552 Gauss and Lorentz peak in two dimensions
553
554 Parameters
555 ----------
556 x, y : array-like
557 coordinate(s) where the function should be evaluated
558 p : list
559 list of parameters of the pseudo Voigt-function
560 [XCEN, YCEN, FWHMX, FWHMY, AMP, BACKGROUND, ANGLE, ETA];
561 ETA: 0 ...1 0 means pure Gauss and 1 means pure Lorentz
562
563 Returns
564 -------
565 array-like
566 the value of the PseudoVoigt described by the parameters `p`
567 at position `(x, y)`
568 """
569 if p[7] > 1.0:
570 pv = 1.0
571 elif p[7] < 0.:
572 pv = 0.0
573 else:
574 pv = p[7]
575 sigmax = p[2] / (2 * numpy.sqrt(2 * numpy.log(2)))
576 sigmay = p[3] / (2 * numpy.sqrt(2 * numpy.log(2)))
577 f = p[5] + pv * Lorentz2d(x, y, p[0], p[1], p[2], p[3], p[4], 0, p[6]) + \
578 (1 - pv) * Gauss2d(x, y, p[0], p[1], sigmax, sigmay, p[4], 0, p[6])
579 return f
580
581
582 def Gauss1dArea(*p):
583 """
584 function to calculate the area of a Gauss function with neglected
585 background
586
587 Parameters
588 ----------
589 p : list
590 list of parameters of the Gauss-function [XCEN, SIGMA, AMP, BACKGROUND]
591
592 Returns
593 -------
594 float
595 the area of the Gaussian described by the parameters `p`
596 """
597 f = p[2] * numpy.sqrt(2 * numpy.pi) * p[1]
598 return f
599
600
601 def Gauss2dArea(*p):
602 """
603 function to calculate the area of a 2D Gauss function with neglected
604 background
605
606 Parameters
607 ----------
608 p : list
609 list of parameters of the Gauss-function
610 [XCEN, YCEN, SIGMAX, SIGMAY, AMP, ANGLE, BACKGROUND]
611
612 Returns
613 -------
614 float
615 the area of the Gaussian described by the parameters `p`
616 """
617 f = p[4] * numpy.sqrt(2 * numpy.pi) ** 2 * p[2] * p[3]
618 return f
619
620
621 def Lorentz1dArea(*p):
622 """
623 function to calculate the area of a Lorentz function with neglected
624 background
625
626 Parameters
627 ----------
628 p : list
629 list of parameters of the Lorentz-function
630 [XCEN, FWHM, AMP, BACKGROUND]
631
632 Returns
633 -------
634 float
635 the area of the Lorentzian described by the parameters `p`
636 """
637 f = p[2] * numpy.pi / (2. / (p[1]))
638 return f
639
640
641 def PseudoVoigt1dArea(*p):
642 """
643 function to calculate the area of a pseudo Voigt function with neglected
644 background
645
646 Parameters
647 ----------
648 p : list
649 list of parameters of the Lorentz-function
650 [XCEN, FWHM, AMP, BACKGROUND, ETA];
651 ETA: 0 ...1 0 means pure Gauss and 1 means pure Lorentz
652
653 Returns
654 -------
655 float
656 the area of the PseudoVoigt described by the parameters `p`
657 """
658 sigma = p[1] / (2 * numpy.sqrt(2 * numpy.log(2)))
659 f = p[4] * Lorentz1dArea(*p) + (1. - p[4]) * \
660 Gauss1dArea(p[0], sigma, p[2], p[3])
661 return f
662
663
664 def Debye1(x):
665 r"""
666 function to calculate the first Debye function [1]_ as needed
667 for the calculation of the thermal Debye-Waller-factor
668 by numerical integration
669
670 .. math:: D_1(x) = (1/x) \int_0^x t/(\exp(t)-1) dt
671
672 Parameters
673 ----------
674 x : float
675 argument of the Debye function
676
677 Returns
678 -------
679 float
680 D1(x) float value of the Debye function
681
682 References
683 ----------
684 .. [1] http://en.wikipedia.org/wiki/Debye_function
685 """
686
687 def __int_kernel(t):
688 """
689 integration kernel for the numeric integration
690 """
691 y = t / (numpy.exp(t) - 1)
692 return y
693
694 if x > 0.:
695 integral = scipy.integrate.quad(__int_kernel, 0, x)
696 d1 = (1 / float(x)) * integral[0]
697 else:
698 integral = (0, 0)
699 d1 = 1.
700
701 if (config.VERBOSITY >= config.DEBUG):
702 print(
703 "XU.math.Debye1: Debye integral value/error estimate: %g %g" %
704 (integral[0], integral[1]))
705
706 return d1
707
708
709 def multPeak1d(x, *args):
710 """
711 function to calculate the sum of multiple peaks in 1D.
712 the peaks can be of different type and a background function (polynom)
713 can also be included.
714
715 Parameters
716 ----------
717 x : array-like
718 coordinate where the function should be evaluated
719 args : list
720 list of peak/function types and parameters for every function type two
721 arguments need to be given first the type of function as string with
722 possible values 'g': Gaussian, 'l': Lorentzian, 'v': PseudoVoigt, 'a':
723 asym. PseudoVoigt, 'p': polynom the second type of arguments is the
724 tuple/list of parameters of the respective function. See documentation
725 of math.Gauss1d, math.Lorentz1d, math.PseudoVoigt1d,
726 math.PseudoVoigt1dasym, and numpy.polyval for details of the different
727 function types.
728
729 Returns
730 -------
731 array-like
732 value of the sum of functions at position `x`
733 """
734 if len(args) % 2 != 0:
735 raise ValueError('number of arguments must be even!')
736
737 if numpy.isscalar(x):
738 f = 0
739 else:
740 lx = numpy.array(x)
741 f = numpy.zeros(lx.shape)
742
743 for i in range(int(len(args) / 2)):
744 ftype = str.lower(args[2 * i])
745 fparam = args[2 * i + 1]
746 if ftype == 'g':
747 f += Gauss1d(x, *fparam)
748 elif ftype == 'l':
749 f += Lorentz1d(x, *fparam)
750 elif ftype == 'v':
751 f += PseudoVoigt1d(x, *fparam)
752 elif ftype == 'a':
753 f += PseudoVoigt1dasym(x, *fparam)
754 elif ftype == 'p':
755 if isinstance(fparam, (tuple, list, numpy.ndarray)):
756 f += numpy.polyval(fparam, x)
757 else:
758 f += numpy.polyval((fparam,), x)
759 else:
760 raise ValueError('invalid function type given!')
761
762 return f
763
764
765 def multPeak2d(x, y, *args):
766 """
767 function to calculate the sum of multiple peaks in 2D.
768 the peaks can be of different type and a background function (polynom)
769 can also be included.
770
771 Parameters
772 ----------
773 x, y : array-like
774 coordinates where the function should be evaluated
775 args : list
776 list of peak/function types and parameters for every function type two
777 arguments need to be given first the type of function as string with
778 possible values 'g': Gaussian, 'l': Lorentzian, 'v': PseudoVoigt, 'c':
779 constant the second type of arguments is the tuple/list of parameters
780 of the respective function. See documentation of math.Gauss2d,
781 math.Lorentz2d, math.PseudoVoigt2d for details of the different
782 function types. The constant accepts a single float which will be
783 added to the data
784
785 Returns
786 -------
787 array-like
788 value of the sum of functions at position `(x, y)`
789 """
790 if len(args) % 2 != 0:
791 raise ValueError('number of arguments must be even!')
792
793 if numpy.isscalar(x):
794 f = 0
795 else:
796 lx = numpy.array(x)
797 ly = numpy.array(y)
798 f = numpy.zeros(lx.shape)
799
800 for i in range(int(len(args) / 2)):
801 ftype = str.lower(args[2 * i])
802 fparam = args[2 * i + 1]
803 if ftype == 'g':
804 f += Gauss2d(lx, ly, *fparam)
805 elif ftype == 'l':
806 f += Lorentz2d(lx, ly, *fparam)
807 elif ftype == 'v':
808 f += PseudoVoigt2d(lx, ly, *fparam)
809 elif ftype == 'c':
810 f += fparam
811 else:
812 raise ValueError('invalid function type given!')
813
814 return f
815
816
817 def heaviside(x):
818 """
819 Heaviside step function for numpy arrays
820
821 Parameters
822 ----------
823 x: scalar or array-like
824 argument of the step function
825
826 Returns
827 -------
828 int or array-like
829 Heaviside step function evaluated for all values of `x` with datatype
830 integer
831 """
832 return (numpy.sign(x)/2. + 0.5).astype(numpy.int8)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import fractions
18 import math
19 import sys
20
21 import numpy
22
23 from .. import config
24
25
26 def center_of_mass(pos, data, background='none', full_output=False):
27 """
28 function to determine the center of mass of an array
29
30 Parameters
31 ----------
32 pos : array-like
33 position of the data points
34 data : array-like
35 data values
36 background : {'none', 'constant', 'linear'}
37 type of background, either 'none', 'constant' or 'linear'
38 full_output : bool
39 return background cleaned data and background-parameters
40
41 Returns
42 -------
43 float
44 center of mass position
45 """
46 # subtract background
47 slope = 0
48 back = 0
49 if background == 'linear':
50 dx = float(pos[-1] - pos[0])
51 if abs(dx) > 0:
52 slope = (float(data[-1]) - float(data[0])) / dx
53 back = (data[0] - slope * pos[0] +
54 data[-1] - slope * pos[-1]) / 2.0
55 ld = data - (slope * pos + back)
56 elif background == 'constant':
57 back = numpy.median(data)
58 ld = data - numpy.min(data)
59 else:
60 ld = data
61
62 ipos = numpy.sum(pos * ld) / numpy.sum(ld)
63 if full_output:
64 return ipos, ld, back, slope
65 else:
66 return ipos
67
68
69 def fwhm_exp(pos, data):
70 """
71 function to determine the full width at half maximum value of experimental
72 data. Please check the obtained value visually (noise influences the
73 result)
74
75 Parameters
76 ----------
77 pos : array-like
78 position of the data points
79 data : array-like
80 data values
81
82 Returns
83 -------
84 float
85 fwhm value
86 """
87
88 m = data.max()
89 p0 = numpy.argmax(data)
90 datal = data[:p0+1]
91 datar = data[p0:]
92
93 # determine left side half value position
94 try:
95 pls = pos[:p0+1][datal < m / 2.][-1]
96 pll = pos[:p0+1][datal > m / 2.][0]
97 ds = data[pos == pls][0]
98 dl = data[pos == pll][0]
99 pl = pls + (pll - pls) * (m / 2. - ds) / (dl - ds)
100 except IndexError:
101 if config.VERBOSITY >= config.INFO_LOW:
102 print("XU.math.fwhm_exp: warning: left side half value could"
103 " not be determined -> returns 2*hwhm")
104 pl = None
105
106 # determine right side half value position
107 try:
108 prs = pos[p0:][datar < m / 2.][0]
109 prl = pos[p0:][datar > m / 2.][-1]
110 ds = data[pos == prs][0]
111 dl = data[pos == prl][0]
112 pr = prs + (prl - prs) * (m / 2. - ds) / (dl - ds)
113 except IndexError:
114 if config.VERBOSITY >= config.INFO_LOW:
115 print("XU.math.fwhm_exp: warning: right side half value could"
116 " not be determined -> returns 2*hwhm")
117 pr = None
118
119 if pl is None:
120 return numpy.abs(pr - p0)*2
121 elif pr is None:
122 return numpy.abs(pl - p0)*2
123 else:
124 return numpy.abs(pr - pl)
125
126
127 def gcd(lst):
128 """
129 greatest common divisor function using library functions
130
131 Parameters
132 ----------
133 lst: array-like
134 array of integer values for which the greatest common divisor should be
135 determined
136
137 Returns
138 -------
139 gcd: int
140 """
141 if numpy.version.version >= '1.15.0':
142 return numpy.gcd.reduce(lst)
143 elif sys.version_info >= (3, 5):
144 gcdfunc = numpy.frompyfunc(math.gcd, 2, 1)
145 else:
146 gcdfunc = numpy.frompyfunc(fractions.gcd, 2, 1)
147 return numpy.ufunc.reduce(gcdfunc, lst)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 import numpy
19
20 from .. import config
21 from . import vector
22
23
24 class Transform(object):
25
26 def __init__(self, matrix):
27 self.matrix = matrix
28 self.imatrix = None
29
30 def inverse(self, args, rank=1):
31 """
32 performs inverse transformation a vector, matrix or tensor of rank 4
33
34 Parameters
35 ----------
36 args : list or array-like
37 object to transform, list or numpy array of shape (..., n)
38 (..., n, n), (..., n, n, n, n) where n is the size of the
39 transformation matrix.
40 rank : int
41 rank of the supplied object. allowed values are 1, 2, and 4
42 """
43 if self.imatrix is None:
44 try:
45 self.imatrix = numpy.linalg.inv(self.matrix)
46 except numpy.linalg.LinAlgError:
47 raise Exception("XU.math.Transform: matrix cannot be inverted"
48 " - seems to be singular")
49
50 it = Transform(self.imatrix)
51 return it(args, rank)
52
53 def __call__(self, args, rank=1):
54 """
55 transforms a vector, matrix or tensor of rank 4
56 (e.g. elasticity tensor)
57
58 Parameters
59 ----------
60 args : list or array-like
61 object to transform, list or numpy array of shape (..., n)
62 (..., n, n), (..., n, n, n, n) where n is the size of the
63 transformation matrix.
64 rank : int
65 rank of the supplied object. allowed values are 1, 2, and 4
66 """
67
68 m = self.matrix
69 if rank == 1: # argument is a vector
70 # out_i = m_ij * args_j
71 out = numpy.einsum('ij,...j', m, args)
72 elif rank == 2: # argument is a matrix
73 # out_ij = m_ik * m_jl * args_kl
74 out = numpy.einsum('ik, jl,...kl', m, m, args)
75 elif rank == 4:
76 # cp_ijkl = m_in * m_jo * m_kp * m_lq * args_nopq
77 out = numpy.einsum('in, jo, kp, lq,...nopq', m, m, m, m, args)
78
79 return out
80
81 def __str__(self):
82 ostr = "Transformation matrix:\n"
83 ostr += str(self.matrix)
84 return ostr
85
86
87 class CoordinateTransform(Transform):
88
89 """
90 Create a Transformation object which transforms a point into a new
91 coordinate frame. The new frame is determined by the three vectors
92 v1/norm(v1), v2/norm(v2) and v3/norm(v3), which need to be orthogonal!
93 """
94
95 def __init__(self, v1, v2, v3):
96 """
97 initialization routine for Coordinate transformation
98
99 Parameters
100 ----------
101 v1, v2, v3 : list, tuple or array-like
102 new base vectors
103
104 Returns
105 -------
106 Transform
107 An instance of a Transform class
108 """
109 e1 = vector._checkvec(v1)
110 e2 = vector._checkvec(v2)
111 e3 = vector._checkvec(v3)
112
113 # normalize base vectors
114 e1 = e1 / numpy.linalg.norm(e1)
115 e2 = e2 / numpy.linalg.norm(e2)
116 e3 = e3 / numpy.linalg.norm(e3)
117
118 # check that the vectors are orthogonal
119 t1 = numpy.abs(numpy.dot(e1, e2))
120 t2 = numpy.abs(numpy.dot(e1, e3))
121 t3 = numpy.abs(numpy.dot(e2, e3))
122 if t1 > config.EPSILON or t2 > config.EPSILON or t3 > config.EPSILON:
123 raise ValueError("given basis vectors need to be orthogonal!")
124
125 if config.VERBOSITY >= config.INFO_ALL:
126 print("XU.math.CoordinateTransform: new basis set: \n"
127 " x: (%5.2f %5.2f %5.2f) \n"
128 " y: (%5.2f %5.2f %5.2f) \n"
129 " z: (%5.2f %5.2f %5.2f)"
130 % (e1[0], e1[1], e1[2], e2[0], e2[1],
131 e2[2], e3[0], e3[1], e3[2]))
132
133 # assemble the transformation matrix
134 m = numpy.array([e1, e2, e3])
135
136 Transform.__init__(self, m)
137
138
139 class AxisToZ(CoordinateTransform):
140
141 """
142 Creates a coordinate transformation to move a certain axis to the z-axis.
143 The rotation is done along the great circle. The x-axis of the new
144 coordinate frame is created to be normal to the new and original z-axis.
145 The new y-axis is create in order to obtain a right handed coordinate
146 system.
147 """
148
149 def __init__(self, newzaxis):
150 """
151 initialize the CoordinateTransformation to move a certain axis to the
152 z-axis
153
154 Parameters
155 ----------
156 newzaxis : list or array-like
157 new z-axis
158 """
159 newz = vector._checkvec(newzaxis)
160
161 if vector.VecAngle([0, 0, 1], newz) < config.EPSILON:
162 newx = [1, 0, 0]
163 newy = [0, 1, 0]
164 newz = [0, 0, 1]
165 elif vector.VecAngle([0, 0, 1], -newz) < config.EPSILON:
166 newx = [-1, 0, 0]
167 newy = [0, 1, 0]
168 newz = [0, 0, -1]
169 else:
170 newx = numpy.cross(newz, [0, 0, 1])
171 newy = numpy.cross(newz, newx)
172
173 CoordinateTransform.__init__(self, newx, newy, newz)
174
175
176 class AxisToZ_keepXY(CoordinateTransform):
177
178 """
179 Creates a coordinate transformation to move a certain axis to the z-axis.
180 The rotation is done along the great circle. The x-axis/y-axis of the new
181 coordinate frame is created to be similar to the old x and y directions.
182 This variant of AxisToZ assumes that the new Z-axis has its main component
183 along the Z-direction
184 """
185
186 def __init__(self, newzaxis):
187 """
188 initialize the CoordinateTransformation to move a certain axis to the
189 z-axis
190
191 Parameters
192 ----------
193 newzaxis : list or array-like
194 new z-axis
195 """
196 newz = vector._checkvec(newzaxis)
197
198 if vector.VecAngle([0, 0, 1], newz) < config.EPSILON:
199 newx = [1, 0, 0]
200 newy = [0, 1, 0]
201 newz = [0, 0, 1]
202 elif vector.VecAngle([0, 0, 1], -newz) < config.EPSILON:
203 newx = [-1, 0, 0]
204 newy = [0, 1, 0]
205 newz = [0, 0, -1]
206 else:
207 newx = numpy.cross(newz, [0, 0, 1])
208 newy = numpy.cross(newz, newx)
209 # rotate newx and newy to be similar to old directions
210 ang = numpy.degrees(numpy.arctan2(newz[0], newz[1]))
211 newx = rotarb(newx, newz, ang)
212 newy = rotarb(newy, newz, ang)
213
214 CoordinateTransform.__init__(self, newx, newy, newz)
215
216
217 def _sincos(alpha, deg):
218 if deg:
219 a = numpy.radians(alpha)
220 else:
221 a = alpha
222 return numpy.sin(a), numpy.cos(a)
223
224
225 def XRotation(alpha, deg=True):
226 """
227 Returns a transform that represents a rotation about the x-axis
228 by an angle alpha. If deg=True the angle is assumed to be in
229 degree, otherwise the function expects radiants.
230 """
231 sina, cosa = _sincos(alpha, deg)
232 m = numpy.array(
233 [[1, 0, 0], [0, cosa, -sina], [0, sina, cosa]], dtype=numpy.double)
234 return Transform(m)
235
236
237 def YRotation(alpha, deg=True):
238 """
239 Returns a transform that represents a rotation about the y-axis
240 by an angle alpha. If deg=True the angle is assumed to be in
241 degree, otherwise the function expects radiants.
242 """
243 sina, cosa = _sincos(alpha, deg)
244 m = numpy.array(
245 [[cosa, 0, sina], [0, 1, 0], [-sina, 0, cosa]], dtype=numpy.double)
246 return Transform(m)
247
248
249 def ZRotation(alpha, deg=True):
250 """
251 Returns a transform that represents a rotation about the z-axis
252 by an angle alpha. If deg=True the angle is assumed to be in
253 degree, otherwise the function expects radiants.
254 """
255 sina, cosa = _sincos(alpha, deg)
256 m = numpy.array(
257 [[cosa, -sina, 0], [sina, cosa, 0], [0, 0, 1]], dtype=numpy.double)
258 return Transform(m)
259
260
261 # helper scripts for rotations around arbitrary axis
262 def tensorprod(vec1, vec2):
263 """
264 function implements an elementwise multiplication of two vectors
265 """
266 return vec1[:, numpy.newaxis] * numpy.ones((3, 3)) * vec2[numpy.newaxis, :]
267
268
269 def mycross(vec, mat):
270 """
271 function implements the cross-product of a vector with each column of a
272 matrix
273 """
274 out = numpy.zeros((3, 3))
275 for i in range(3):
276 out[:, i] = numpy.cross(vec, mat[:, i])
277 return out
278
279
280 def ArbRotation(axis, alpha, deg=True):
281 """
282 Returns a transform that represents a rotation around an arbitrary axis by
283 the angle alpha. positive rotation is anti-clockwise when looking from
284 positive end of axis vector
285
286 Parameters
287 ----------
288 axis : list or array-like
289 rotation axis
290 alpha : float
291 rotation angle in degree (deg=True) or in rad (deg=False)
292 deg : bool
293 determines the input format of ang (default: True)
294
295 Returns
296 -------
297 Transform
298 """
299 axis = vector._checkvec(axis)
300 e = axis / numpy.linalg.norm(axis)
301 if deg:
302 rad = numpy.radians(alpha)
303 else:
304 rad = alpha
305 get = tensorprod(e, e)
306 rot = get + numpy.cos(rad) * (numpy.identity(3) - get) + \
307 numpy.sin(rad) * mycross(e, numpy.identity(3))
308 return Transform(rot)
309
310
311 def rotarb(vec, axis, ang, deg=True):
312 """
313 function implements the rotation around an arbitrary axis by an angle ang
314 positive rotation is anti-clockwise when looking from positive end of axis
315 vector
316
317 Parameters
318 ----------
319 vec : list or array-like
320 vector to rotate
321 axis : list or array-like
322 rotation axis
323 ang : float
324 rotation angle in degree (deg=True) or in rad (deg=False)
325 deg : bool
326 determines the input format of ang (default: True)
327
328 Returns
329 -------
330 rotvec : rotated vector as numpy.array
331
332 Examples
333 --------
334 >>> rotarb([1, 0, 0],[0, 0, 1], 90)
335 array([ 6.12323400e-17, 1.00000000e+00, 0.00000000e+00])
336 """
337 return ArbRotation(axis, ang, deg)(vec)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2010-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 module with vector operations for vectors of size 3,
20 since for so short vectors numpy does not give the best performance explicit
21 implementation of the equations is performed together with error checking to
22 ensure vectors of length 3.
23 """
24
25 import math
26 import re
27
28 import numpy
29
30 from .. import config
31 from ..exception import InputError
32
33 circleSyntax = re.compile("[xyz][+-]")
34
35
36 def _checkvec(v):
37 if isinstance(v, (list, tuple, numpy.ndarray)):
38 vtmp = numpy.asarray(v, dtype=numpy.double)
39 else:
40 raise TypeError("Vector must be a list, tuple or numpy array")
41 return vtmp
42
43
44 def VecNorm(v):
45 """
46 Calculate the norm of a vector.
47
48 Parameters
49 ----------
50 v : list or array-like
51 input vector(s), either one vector or an array of vectors with shape
52 (n, 3)
53
54 Returns
55 -------
56 float or ndarray
57 vector norm, either a single float or shape (n, )
58 """
59 if isinstance(v, numpy.ndarray):
60 if len(v.shape) >= 2:
61 return numpy.linalg.norm(v, axis=-1)
62 if len(v) != 3:
63 raise ValueError("Vector must be of length 3, but has length %d!"
64 % len(v))
65 return math.sqrt(v[0]**2 + v[1]**2 + v[2]**2)
66
67
68 def VecUnit(v):
69 """
70 Calculate the unit vector of v.
71
72 Parameters
73 ----------
74 v : list or array-like
75 input vector(s), either one vector or an array of vectors with shape
76 (n, 3)
77
78 Returns
79 -------
80 ndarray
81 unit vector of `v`, either shape (3, ) or (n, 3)
82 """
83 vtmp = _checkvec(v)
84 if len(vtmp.shape) == 1:
85 return vtmp / VecNorm(vtmp)
86 else:
87 return vtmp / VecNorm(vtmp)[..., numpy.newaxis]
88
89
90 def VecDot(v1, v2):
91 """
92 Calculate the vector dot product.
93
94 Parameters
95 ----------
96 v1, v2 : list or array-like
97 input vector(s), either one vector or an array of vectors with shape
98 (n, 3)
99
100 Returns
101 -------
102 float or ndarray
103 innter product of the vectors, either a single float or (n, )
104 """
105 if isinstance(v1, numpy.ndarray):
106 if len(v1.shape) >= 2:
107 return numpy.einsum('...i, ...i', v1, v2)
108 if len(v1) != 3 or len(v2) != 3:
109 raise ValueError("Vectors must be of size 3! (len(v1)=%d len(v2)=%d)"
110 % (len(v1), len(v2)))
111
112 return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]
113
114
115 def VecCross(v1, v2, out=None):
116 """
117 Calculate the vector cross product.
118
119 Parameters
120 ----------
121 v1, v2 : list or array-like
122 input vector(s), either one vector or an array of vectors with shape
123 (n, 3)
124 out : list or array-like, optional
125 output vector
126
127 Returns
128 -------
129 ndarray
130 cross product either of shape (3, ) or (n, 3)
131 """
132 if isinstance(v1, numpy.ndarray):
133 if len(v1.shape) >= 2 or len(v2.shape) >= 2:
134 return numpy.cross(v1, v2)
135 if len(v1) != 3 or len(v2) != 3:
136 raise ValueError("Vectors must be of size 3! (len(v1)=%d len(v2)=%d)"
137 % (len(v1), len(v2)))
138 if out is None:
139 out = numpy.empty(3)
140 out[0] = v1[1] * v2[2] - v1[2] * v2[1]
141 out[1] = v1[2] * v2[0] - v1[0] * v2[2]
142 out[2] = v1[0] * v2[1] - v1[1] * v2[0]
143 return out
144
145
146 def VecAngle(v1, v2, deg=False):
147 """
148 calculate the angle between two vectors. The following
149 formula is used
150 v1.v2 = norm(v1)*norm(v2)*cos(alpha)
151
152 alpha = acos((v1.v2)/(norm(v1)*norm(v2)))
153
154 Parameters
155 ----------
156 v1, v2 : list or array-like
157 input vector(s), either one vector or an array of vectors with shape
158 (n, 3)
159 deg: bool
160 True: return result in degree, False: in radiants
161
162 Returns
163 -------
164 float or ndarray
165 the angle included by the two vectors `v1` and `v2`, either a single
166 float or an array with shape (n, )
167 """
168 u1 = VecNorm(v1)
169 u2 = VecNorm(v2)
170
171 if isinstance(u1, numpy.ndarray) or isinstance(u2, numpy.ndarray):
172 s = VecDot(v1, v2) / u1 / u2
173 s[s > 1.0] = 1.0
174 alpha = numpy.arccos(s)
175 if deg:
176 alpha = numpy.degrees(alpha)
177 else:
178 alpha = math.acos(min(1., VecDot(v1, v2) / u1 / u2))
179 if deg:
180 alpha = math.degrees(alpha)
181
182 return alpha
183
184
185 def distance(x, y, z, point, vec):
186 """
187 calculate the distance between the point (x, y, z) and the line defined by
188 the point and vector vec
189
190 Parameters
191 ----------
192 x : float or ndarray
193 x coordinate(s) of the point(s)
194 y : float or ndarray
195 y coordinate(s) of the point(s)
196 z : float or ndarray
197 z coordinate(s) of the point(s)
198 point : tuple, list or ndarray
199 3D point on the line to which the distance should be calculated
200 vec : tuple, list or ndarray
201 3D vector defining the propergation direction of the line
202 """
203 coords = numpy.vstack((numpy.ravel(x) - point[0],
204 numpy.ravel(y) - point[1],
205 numpy.ravel(z) - point[2])).T
206 ret = VecNorm(VecCross(coords, numpy.asarray(vec)))/VecNorm(vec)
207 if isinstance(x, numpy.ndarray):
208 ret = ret.reshape(x.shape)
209 else:
210 ret = ret[0]
211 return ret
212
213
214 def getVector(string):
215 """
216 returns unit vector along a rotation axis given in the syntax
217 'x+' 'z-' or equivalents
218
219 Parameters
220 ----------
221 string: str
222 vector string following the synthax [xyz][+-]
223
224 Returns
225 -------
226 ndarray
227 vector along the given direction
228 """
229
230 if len(string) != 2:
231 raise InputError("wrong length of string for conversion given")
232 if not circleSyntax.search(string):
233 raise InputError("getVector: incorrect string syntax (%s)" % string)
234
235 if string[0] == 'x':
236 v = [1., 0, 0]
237 elif string[0] == 'y':
238 v = [0, 1., 0]
239 elif string[0] == 'z':
240 v = [0, 0, 1.]
241 else:
242 raise InputError("wrong first character of string given "
243 "(needs to be one of x, y, z)")
244
245 if string[1] == '+':
246 v = numpy.asarray(v) * (+1)
247 elif string[1] == '-':
248 v = numpy.asarray(v) * (-1)
249 else:
250 raise InputError("wrong second character of string given "
251 "(needs to be + or -)")
252
253 return v
254
255
256 def getSyntax(vec):
257 """
258 returns vector direction in the syntax
259 'x+' 'z-' or equivalents
260 therefore works only for principle vectors of the coordinate system
261 like e.g. [1, 0, 0] or [0, 2, 0]
262
263 Parameters
264 ----------
265 vec : list or array-like
266 vector of length 3
267
268 Returns
269 -------
270 str
271 vector string following the synthax [xyz][+-]
272 """
273 v = _checkvec(vec)
274 if len(v) != 3:
275 raise InputError("no valid 3D vector given")
276
277 x = [1, 0, 0]
278 y = [0, 1, 0]
279 z = [0, 0, 1]
280
281 if VecNorm(numpy.cross(numpy.cross(x, y), v)) <= config.EPSILON:
282 if v[2] >= 0:
283 string = 'z+'
284 else:
285 string = 'z-'
286 elif VecNorm(numpy.cross(numpy.cross(x, z), v)) <= config.EPSILON:
287 if v[1] >= 0:
288 string = 'y+'
289 else:
290 string = 'y-'
291 elif VecNorm(numpy.cross(numpy.cross(y, z), v)) <= config.EPSILON:
292 if v[0] >= 0:
293 string = 'x+'
294 else:
295 string = 'x-'
296 else:
297 raise InputError("no valid 3D vector given")
298
299 return string
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
16 """
17 Defines new matplotlib Sqrt scale which further allows for negative values by
18 using the sign of the original value as sign of the plotted value.
19 """
20
21 import math
22
23 import matplotlib
24 import numpy
25 from matplotlib import scale as mscale
26 from matplotlib import ticker as mticker
27 from matplotlib import transforms as mtransforms
28 from pkg_resources import parse_version
29
30
31 class SqrtAllowNegScale(mscale.ScaleBase):
32 """
33 Scales data using a sqrt-function, however, allowing also negative values.
34
35 The scale function:
36 sign(y) * sqrt(abs(y))
37
38 The inverse scale function:
39 sign(y) * y**2
40 """
41 name = 'sqrt'
42
43 def __init__(self, axis, **kwargs):
44 """
45 Any keyword arguments passed to ``set_xscale`` and
46 ``set_yscale`` will be passed along to the scale's
47 constructor.
48 """
49 if parse_version(matplotlib.__version__) < parse_version('3.1.0'):
50 super().__init__(**kwargs)
51 else:
52 super().__init__(axis, **kwargs)
53
54 def set_default_locators_and_formatters(self, axis):
55 axis.set_major_locator(SqrtTickLocator())
56
57 def limit_range_for_scale(self, vmin, vmax, minpos):
58 """
59 Override to limit the bounds of the axis to the domain of the
60 transform. In the case of Mercator, the bounds should be
61 limited to the threshold that was passed in. Unlike the
62 autoscaling provided by the tick locators, this range limiting
63 will always be adhered to, whether the axis range is set
64 manually, determined automatically or changed through panning
65 and zooming.
66 """
67 return vmin, vmax
68
69 def get_transform(self):
70 return self.SqrtTransform()
71
72 class SqrtTransform(mtransforms.Transform):
73 input_dims = 1
74 output_dims = 1
75 is_separable = True
76
77 def transform_non_affine(self, a):
78 """
79 This transform takes an Nx1 ``numpy`` array and returns a
80 transformed copy.
81 """
82 return numpy.sign(a) * numpy.sqrt(numpy.abs(a))
83
84 def inverted(self):
85 """
86 return the inverse transform for this transform.
87 """
88 return SqrtAllowNegScale.InvertedSqrtTransform()
89
90 class InvertedSqrtTransform(mtransforms.Transform):
91 input_dims = 1
92 output_dims = 1
93 is_separable = True
94
95 def transform_non_affine(self, a):
96 return numpy.sign(a) * a**2
97
98 def inverted(self):
99 return SqrtAllowNegScale.SqrtTransform()
100
101
102 class SqrtTickLocator(mticker.Locator):
103 def __init__(self, nbins=7, symmetric=True):
104 if parse_version(matplotlib.__version__) < parse_version('3.1.0'):
105 self._base = mticker.Base(1.0)
106 else:
107 self._base = mticker._Edge_integer(1.0, 0)
108 self.set_params(nbins, symmetric)
109
110 def set_params(self, nbins, symmetric):
111 """Set parameters within this locator."""
112 self._nbins = nbins
113 self._symmetric = symmetric
114
115 def __call__(self):
116 'Return the locations of the ticks'
117 vmin, vmax = self.axis.get_view_interval()
118 return self.tick_values(vmin, vmax)
119
120 def tick_values(self, vmin, vmax):
121 if vmax < vmin:
122 vmin, vmax = vmax, vmin
123 tmin = math.copysign(math.sqrt(abs(vmin)), vmin)
124 tmax = math.copysign(math.sqrt(abs(vmax)), vmax)
125 delta = (tmax - tmin) / self._nbins
126 locs = numpy.arange(tmin, tmax, self._base.ge(delta))
127 if self._symmetric and numpy.sign(tmin) != numpy.sign(tmax):
128 locs -= locs[numpy.argmin(numpy.abs(locs))]
129 locs = numpy.sign(locs) * locs**2
130 return self.raise_if_exceeds(locs)
131
132 def view_limits(self, dmin, dmax):
133 """
134 Set the view limits to the nearest multiples of base that
135 contain the data
136 """
137 vmin = self._base.le(math.copysign(math.sqrt(abs(dmin)), dmin))
138 vmax = self._base.ge(math.sqrt(dmax))
139 if vmin == vmax:
140 vmin -= 1
141 vmax += 1
142
143 return mtransforms.nonsingular(math.copysign(vmin**2, vmin), vmax**2)
144
145
146 # register new scale to matplotlib
147 mscale.register_scale(SqrtAllowNegScale)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2011 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module to provide functions that perform block averaging
19 of intensity arrays to reduce the amount of data (mainly
20 for PSD and CCD measurements
21
22 and
23
24 provide functions for normalizing intensities for
25
26 * count time
27 * absorber (user-defined function)
28 * monitor
29 * flatfield correction
30 """
31
32 import numpy
33
34 from . import config, cxrayutilities, math, utilities
35 from .exception import InputError
36
37
38 def blockAverage1D(data, Nav):
39 """
40 perform block average for 1D array/list of Scalar values
41 all data are used. at the end of the array a smaller cell
42 may be used by the averaging algorithm
43
44 Parameters
45 ----------
46 data : array-like
47 data which should be contracted (length N)
48 Nav : int
49 number of values which should be averaged
50
51 Returns
52 -------
53 ndarray
54 block averaged numpy array of data type numpy.double
55 (length ceil(N/Nav))
56 """
57
58 if not isinstance(data, (numpy.ndarray, list)):
59 raise TypeError("first argument data must be of type list or "
60 "numpy.ndarray")
61
62 data = numpy.array(data, dtype=numpy.double)
63 block_av = cxrayutilities.block_average1d(data, Nav)
64
65 return block_av
66
67
68 def blockAverage2D(data2d, Nav1, Nav2, **kwargs):
69 """
70 perform a block average for 2D array of Scalar values
71 all data are used therefore the margin cells may differ in size
72
73 Parameters
74 ----------
75 data2d : ndarray
76 array of 2D data shape (N, M)
77 Nav1, Nav2 : int
78 a field of (Nav1 x Nav2) values is contracted
79
80 kwargs : dict, optional
81 optional keyword argument
82 roi : tuple or list, optional
83 region of interest for the 2D array. e.g. [20, 980, 40, 960],
84 reduces M, and M!
85
86 Returns
87 -------
88 ndarray
89 block averaged numpy array with type numpy.double with shape
90 (ceil(N/Nav1), ceil(M/Nav2))
91 """
92
93 if not isinstance(data2d, (numpy.ndarray)):
94 raise TypeError("first argument data2d must be of type numpy.ndarray")
95
96 roi = kwargs.get('roi', [0, data2d.shape[0], 0, data2d.shape[1]])
97 data = numpy.array(data2d[roi[0]:roi[1], roi[2]:roi[3]],
98 dtype=numpy.double)
99
100 if config.VERBOSITY >= config.DEBUG:
101 N, M = (roi[1] - roi[0], roi[3] - roi[2])
102 print("xu.normalize.blockAverage2D: roi: %s" % (str(roi)))
103 print("xu.normalize.blockAverage2D: Nav1, 2: %d,%d" % (Nav1, Nav2))
104 print("xu.normalize.blockAverage2D: number of points: (%d,%d)"
105 % (numpy.ceil(N / float(Nav1)), numpy.ceil(M / float(Nav2))))
106
107 block_av = cxrayutilities.block_average2d(data, Nav1, Nav2,
108 config.NTHREADS)
109
110 return block_av
111
112
113 def blockAveragePSD(psddata, Nav, **kwargs):
114 """
115 perform a block average for serveral PSD spectra
116 all data are used therefore the last cell used for
117 averaging may differ in size
118
119 Parameters
120 ----------
121 psddata : ndarray
122 array of 2D data shape (Nspectra, Nchannels)
123 Nav : int
124 number of channels which should be averaged
125
126 kwargs : dict, optional
127 optional keyword argument
128 roi : tuple or list
129 region of interest for the 2D array. e.g. [20, 980] Nchannels = 980-20
130
131 Returns
132 -------
133 ndarray
134 block averaged psd spectra as numpy array with type numpy.double of
135 shape (Nspectra , ceil(Nchannels/Nav))
136 """
137
138 if not isinstance(psddata, (numpy.ndarray)):
139 raise TypeError("first argument psddata must be of type numpy.ndarray")
140
141 roi = kwargs.get('roi', [0, psddata.shape[1]])
142 data = numpy.array(psddata[:, roi[0]:roi[1]], dtype=numpy.double)
143
144 block_av = cxrayutilities.block_average_PSD(data, Nav, config.NTHREADS)
145
146 return block_av
147
148
149 def blockAverageCCD(data3d, Nav1, Nav2, **kwargs):
150 """
151 perform a block average for 2D frames inside a 3D array.
152 all data are used therefore the margin cells may differ in size
153
154 Parameters
155 ----------
156 data3d : ndarray
157 array of 3D data shape (Nframes, N, M)
158 Nav1, Nav2 : int
159 a field of (Nav1 x Nav2) values is contracted
160
161 kwargs : dict, optional
162 optional keyword argument
163 roi : tuple or list, optional
164 region of interest for the 2D array. e.g. [20, 980, 40, 960],
165 reduces M, and M!
166
167 Returns
168 -------
169 ndarray
170 block averaged numpy array with type numpy.double with shape
171 (Nframes, ceil(N/Nav1), ceil(M/Nav2))
172 """
173
174 if not isinstance(data3d, (numpy.ndarray)):
175 raise TypeError("first argument data3d must be of type numpy.ndarray")
176
177 roi = kwargs.get('roi', [0, data3d.shape[1], 0, data3d.shape[2]])
178 data = numpy.array(data3d[:, roi[0]:roi[1], roi[2]:roi[3]],
179 dtype=numpy.double)
180
181 if config.VERBOSITY >= config.DEBUG:
182 N, M = (roi[1] - roi[0], roi[3] - roi[2])
183 print("xu.normalize.blockAverageCCD: roi: %s" % (str(roi)))
184 print("xu.normalize.blockAverageCCD: Nav1, 2: %d,%d" % (Nav1, Nav2))
185 print("xu.normalize.blockAverageCCD: number of points: (%d,%d)"
186 % (numpy.ceil(N / float(Nav1)), numpy.ceil(M / float(Nav2))))
187
188 block_av = cxrayutilities.block_average_CCD(data, Nav1, Nav2,
189 config.NTHREADS)
190
191 return block_av
192
193 # #####################################
194 # # Intensity correction class ##
195 # #####################################
196
197
198 class IntensityNormalizer(object):
199
200 """
201 generic class for correction of intensity (point detector, or MCA,
202 single CCD frames) for count time and absorber factors
203 the class must be supplied with a absorber correction function
204 and works with data structures provided by xrayutilities.io classes or the
205 corresponding objects from hdf5 files
206 """
207
208 def __init__(self, det='', **keyargs):
209 """
210 initialization of the corrector class
211
212 Parameters
213 ----------
214 det : str
215 detector field name of the data structure
216
217 mon : str, optional
218 monitor field name
219 time: float or str, optional
220 count time field name or count time as float
221 av_mon : float, optional
222 average monitor value (default: data[mon].mean())
223 smoothmon : int
224 number of monitor values used to get a smooth monitor signal
225 absfun : callable, optional
226 absorber correction function to be used as in
227 ``absorber_corrected_intensity = data[det]*absfun(data)``
228 flatfield : ndarray
229 flatfield of the detector; shape must be the same as data[det], and
230 is only applied for MCA detectors
231 darkfield : ndarray
232 darkfield of the detector; shape must be the same as data[det], and
233 is only applied for MCA detectors
234
235 Examples
236 --------
237 >>> detcorr = IntensityNormalizer("MCA", time="Seconds",
238 >>> absfun=lambda d: d["PSDCORR"]/d["PSD"].astype(numpy.float))
239 """
240 valid_kwargs = {'mon': 'monitor field name',
241 'time': 'count time field/value',
242 'smoothmon': 'number of monitor values to average',
243 'av_mon': 'average monitor value',
244 'absfun': 'absorber correction function',
245 'flatfield': 'detector flatfield',
246 'darkfield': 'detector darkfield'}
247 utilities.check_kwargs(keyargs, valid_kwargs,
248 self.__class__.__name__)
249
250 # check input arguments
251 self._setdet(det)
252
253 self._setmon(keyargs.get('mon', None))
254 self._settime(keyargs.get('time', None))
255 self._setavmon(keyargs.get('av_mon', None))
256 self._setabsfun(keyargs.get('absfun', None))
257 self._setflatfield(keyargs.get('flatfield', None))
258 self._setdarkfield(keyargs.get('darkfield', None))
259 self.smoothmon = keyargs.get('smoothmon', 1)
260
261 def _getdet(self):
262 """
263 det property handler
264
265 returns the detector field name
266 """
267 return self._det
268
269 def _setdet(self, det):
270 """
271 det property handler
272
273 sets the detector field name
274 """
275 if isinstance(det, str):
276 self._det = det
277 else:
278 self._det = None
279 raise TypeError("argument det must be of type str")
280
281 def _gettime(self):
282 """
283 time property handler
284
285 returns the count time or the field name of the count time
286 or None if time is not set
287 """
288 return self._time
289
290 def _settime(self, time):
291 """
292 time property handler
293
294 sets the count time field or value
295 """
296 if isinstance(time, str):
297 self._time = time
298 elif isinstance(time, (float, int)):
299 self._time = float(time)
300 elif isinstance(time, type(None)):
301 self._time = None
302 else:
303 raise TypeError("argument time must be of type str, float or None")
304
305 def _getmon(self):
306 """
307 mon property handler
308
309 returns the monitor field name or None if not set
310 """
311 return self._mon
312
313 def _setmon(self, mon):
314 """
315 mon property handler
316
317 sets the monitor field name
318 """
319 if isinstance(mon, str):
320 self._mon = mon
321 elif isinstance(mon, type(None)):
322 self._mon = None
323 else:
324 raise TypeError("argument mon must be of type str")
325
326 def _getavmon(self):
327 """
328 av_mon property handler
329
330 returns the value of the average monitor or None
331 if average is calculated from the monitor field
332 """
333 return self._avmon
334
335 def _setavmon(self, avmon):
336 """
337 avmon property handler
338
339 sets the average monitor field name
340 """
341 if isinstance(avmon, (float, int)):
342 self._avmon = float(avmon)
343 elif isinstance(avmon, type(None)):
344 self._avmon = None
345 else:
346 raise TypeError("argument avmon must be of type float or None")
347
348 def _getabsfun(self):
349 """
350 absfun property handler
351
352 returns the costum correction function or None
353 """
354 return self._absfun
355
356 def _setabsfun(self, absfun):
357 """
358 absfun property handler
359
360 sets the costum correction function
361 """
362 if hasattr(absfun, '__call__'):
363 self._absfun = absfun
364 if self._absfun.__code__.co_argcount != 1:
365 raise TypeError("argument absfun must be a function with one "
366 "argument (data object)")
367 elif isinstance(absfun, type(None)):
368 self._absfun = None
369 else:
370 raise TypeError("argument absfun must be of type function or None")
371
372 def _getflatfield(self):
373 """
374 flatfield property handler
375
376 returns the current set flatfield of the detector
377 or None if not set
378 """
379 return self._flatfield
380
381 def _setflatfield(self, flatf):
382 """
383 flatfield property handler
384
385 sets the flatfield of the detector
386 """
387 if isinstance(flatf, (list, tuple, numpy.ndarray)):
388 self._flatfield = numpy.array(flatf, dtype=numpy.float)
389 self._flatfieldav = numpy.mean(self._flatfield[
390 self._flatfield.nonzero()])
391 self._flatfield[self.flatfield < 1.e-5] = 1.0
392 elif isinstance(flatf, type(None)):
393 self._flatfield = None
394 else:
395 raise TypeError("argument flatfield must be of type list, tuple, "
396 "numpy.ndarray or None")
397
398 def _getdarkfield(self):
399 """
400 flatfield property handler
401
402 returns the current set darkfield of the detector
403 or None if not set
404 """
405 return self._darkfield
406
407 def _setdarkfield(self, darkf):
408 """
409 flatfield property handler
410
411 sets the darkfield of the detector
412 """
413 if isinstance(darkf, (list, tuple, numpy.ndarray)):
414 self._darkfield = numpy.array(darkf, dtype=numpy.float)
415 self._darkfieldav = numpy.mean(self._darkfield)
416 elif isinstance(darkf, type(None)):
417 self._darkfield = None
418 else:
419 raise TypeError("argument flatfield must be of type list, tuple, "
420 "numpy.ndarray or None")
421
422 det = property(_getdet, _setdet)
423 time = property(_gettime, _settime)
424 mon = property(_getmon, _setmon)
425 avmon = property(_getavmon, _setavmon)
426 absfun = property(_getabsfun, _setabsfun)
427 flatfield = property(_getflatfield, _setflatfield)
428 darkfield = property(_getdarkfield, _setdarkfield)
429
430 def __call__(self, data, ccd=None):
431 """
432 apply the correction method which was initialized to the measured data
433
434 Parameters
435 ----------
436 data : numpy.recarray
437 data object from xrayutilities.io classes
438 ccd : ndarray, optional
439 optionally CCD data can be given as separate ndarray of shape
440 (len(data), n1, n2), where n1, n2 is the shape of the CCD image.
441
442 Returns
443 -------
444 corrint : ndarray
445 corrected intensity as numpy.ndarray of the same shape as data[det]
446 (or ccd.shape)
447 """
448 if numpy.any(ccd):
449 rawdata = ccd
450 else:
451 rawdata = data[self._det]
452
453 corrint = numpy.zeros(rawdata.shape, dtype=numpy.float)
454
455 # set needed variables
456 # monitor intensity
457 if self._mon:
458 if self.smoothmon == 1:
459 mon = data[self._mon]
460 else:
461 mon = math.smooth(data[self._mon], self.smoothmon)
462 else:
463 mon = 1.
464 # count time
465 if isinstance(self._time, str):
466 time = data[self._time]
467 elif isinstance(self._time, float):
468 time = self._time
469 else:
470 time = 1.
471 # average monitor counts
472 if self._avmon:
473 avmon = self._avmon
474 else:
475 avmon = numpy.mean(mon)
476 # absorber correction function
477 if self._absfun:
478 abscorr = self._absfun(data)
479 else:
480 abscorr = 1.
481
482 c = abscorr * avmon / (mon * time)
483 # correct the correction factor if it was evaluated to an incorrect
484 # value
485 if isinstance(c, numpy.ndarray):
486 c[numpy.isnan(c)] = 1.0
487 c[numpy.isinf(c)] = 1.0
488 c[c == 0] = 1.0
489 else:
490 if numpy.isnan(c) or numpy.isinf(c) or c == 0:
491 c = 1.0
492
493 if len(rawdata.shape) == 1:
494 corrint = rawdata * c
495 elif len(rawdata.shape) == 2 and isinstance(c, numpy.ndarray):
496 # 1D detector c.shape[0] should be rawdata.shape[0]
497 if self._darkfield is not None:
498 if self._darkfield.shape[0] != rawdata.shape[1]:
499 raise InputError("data[det] second dimension must have "
500 "the same length as darkfield")
501
502 if isinstance(time, numpy.ndarray):
503 # darkfield correction
504 corrint = (rawdata -
505 self._darkfield[numpy.newaxis, :] *
506 time[:, numpy.newaxis])
507 elif isinstance(time, float):
508 # darkfield correction
509 corrint = (rawdata -
510 self._darkfield[numpy.newaxis, :] * time)
511 else:
512 print("XU.normalize.IntensityNormalizer: check "
513 "initialization and your input")
514 return None
515 corrint[corrint < 0.] = 0.
516
517 else:
518 corrint = rawdata
519
520 corrint = corrint * c[:, numpy.newaxis]
521
522 if self._flatfield is not None:
523 if self._flatfield.shape[0] != rawdata.shape[1]:
524 raise InputError("data[det] second dimension must have "
525 "the same length as flatfield")
526 # flatfield correction
527 corrint = (corrint / self._flatfield[numpy.newaxis, :] *
528 self._flatfieldav)
529
530 elif len(rawdata.shape) == 2 and isinstance(c, numpy.float):
531 # single 2D detector frame
532 corrint = rawdata * c
533
534 elif len(rawdata.shape) == 3:
535 # darkfield and flatfield correction is still missing!
536 corrint = rawdata * c[:, numpy.newaxis, numpy.newaxis]
537
538 else:
539 raise InputError("data[det] must be an array of dimension one "
540 "or two or three")
541
542 return corrint
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2015-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 Module provides functions to convert a q-vector from reciprocal space to
19 angular space. a simple implementation uses scipy optimize routines to perform
20 a fit for a arbitrary goniometer.
21
22 The user is, however, expected to use the bounds variable to put restrictions
23 to the number of free angles to obtain reproducible results. In general only 3
24 angles are needed to fit an arbitrary q-vector (2 sample + 1 detector angles or
25 1 sample + 2 detector). More complicated restrictions can be implemented using
26 the lmfit package. (done upon request!)
27
28 The function is based on a fitting routine. For a specific goniometer also
29 analytic expressions from literature can be used as they are implemented in the
30 predefined experimental classes HXRD, NonCOP, and GID.
31 """
32
33 import numbers
34
35 import numpy
36 import scipy.optimize
37
38 from . import config, math
39 from .exception import InputError
40
41
42 def _makebounds(boundsin):
43 """
44 generate proper bounds for scipy.optimize.minimize function
45 from a list/tuple of more convenient bounds.
46
47 Parameters
48 ----------
49 boundsin : list or tuple or array-like
50 bounds, or fixed values. the number of entries needs to be equal to the
51 number of angle in the goniometer given to the q2ang_general function
52 example input for four gonimeter angles: ((0, 90), 0, (0, 180), (0,
53 90))
54
55 Returns
56 -------
57 tuple
58 bounds to be handed over to the scipy.minimize routine. The function
59 will expand fixed values to two equal bounds
60 """
61 boundsout = []
62 for b in boundsin:
63 if isinstance(b, (tuple, list, numpy.ndarray)):
64 if len(b) == 2:
65 boundsout.append((b[0], b[1]))
66 elif len(b) == 1:
67 boundsout.append((b[0], b[0]))
68 else:
69 raise InputError('bound values must have two or one elements')
70 elif isinstance(b, numbers.Number):
71 boundsout.append((b, b)) # variable fixed
72 elif b is None:
73 boundsout.append((None, None)) # no bound
74 else:
75 raise InputError('bound value is of invalid type (%s)' % type(b))
76
77 return tuple(boundsout)
78
79
80 def _errornorm_q2ang(angles, qvec, hxrd, U=numpy.identity(3)):
81 """
82 function to determine the offset in the qposition calculated from
83 a set of experimental angles and the given vector
84
85 Parameters
86 ----------
87 angles : iterable
88 iterable object with angles of the goniometer
89 qvec : list or tuple or array-like
90 vector with three q-coordinates
91 hxrd : Experiment
92 experiment class to be used for the q calculation
93 U : array-like, optional
94 orientation matrix
95
96 Returns
97 -------
98 error : float
99 q-space error between the current fit-guess and the user-specified
100 position
101 """
102
103 qcalc = hxrd.Ang2Q.point(*angles, UB=U)
104 dq = numpy.linalg.norm(qcalc - qvec)
105 return dq
106
107
108 def exitAngleConst(angles, alphaf, hxrd):
109 """
110 helper function for an pseudo-angle constraint for the Q2AngFit-routine.
111
112 Parameters
113 ----------
114 angles : iterable
115 fit parameters of Q2AngFit
116 alphaf : float
117 the exit angle which should be fixed
118 hxrd : Experiment
119 the Experiment object to use for qconversion
120 """
121 qconv = hxrd._A2QConversion
122 # calc kf
123 detangles = [a for a in angles[-len(qconv.detectorAxis):]]
124 kf = qconv.getDetectorPos(*detangles)
125 if numpy.linalg.norm(kf) == 0:
126 af = 0
127 else:
128 ndirlab = qconv.transformSample2Lab(hxrd.Transform(hxrd.ndir), *angles)
129 af = 90 - math.VecAngle(kf, ndirlab, deg=True) - alphaf
130 return af
131
132
133 def Q2AngFit(qvec, expclass, bounds=None, ormat=numpy.identity(3),
134 startvalues=None, constraints=()):
135 """
136 Functions to convert a q-vector from reciprocal space to angular space.
137 This implementation uses scipy optimize routines to perform a fit for a
138 goniometer with arbitrary number of goniometer angles.
139
140 The user *must* use the bounds variable to put
141 restrictions to the number of free angles to obtain reproducible results.
142 In general only 3 angles are needed to fit an arbitrary q-vector (2 sample
143 + 1 detector angles or 1 sample + 2 detector).
144
145 Parameters
146 ----------
147 qvec : tuple or list or array-like
148 q-vector for which the angular positions should be calculated
149 expclass : Experiment
150 experimental class used to define the goniometer for which the angles
151 should be calculated.
152
153 bounds : tuple or list
154 bounds of the goniometer angles. The number of bounds must correspond
155 to the number of goniometer angles in the expclass. Angles can also be
156 fixed by supplying only one value for a particular angle. e.g.: ((low,
157 up), fix, (low2, up2), (low3, up3))
158 ormat : array-like
159 orientation matrix of the sample to be used in the conversion
160 startvalues : array-like
161 start values for the fit, which can significantly speed up the
162 conversion. The number of values must correspond to the number of
163 angles in the goniometer of the expclass
164 constraints : tuple
165 sequence of constraint dictionaries. This allows applying arbitrary
166 (e.g. pseudo-angle) contraints by supplying according constraint
167 functions. (see scipy.optimize.minimize). The supplied function will be
168 called with the arguments (angles, qvec, Experiment, U).
169
170 Returns
171 -------
172 fittedangles : list
173 list of fitted goniometer angles
174 qerror : float
175 error in reciprocal space
176 errcode : int
177 error-code of the scipy minimize function. for a successful fit the
178 error code should be <=2
179 """
180
181 # check input parameters
182 if len(qvec) != 3:
183 raise ValueError("XU.Q2AngFit: length of given q-vector is not 3 "
184 "-> invalid")
185 lqvec = numpy.asarray(qvec)
186
187 qconv = expclass._A2QConversion
188 nangles = len(qconv.sampleAxis) + len(qconv.detectorAxis)
189
190 # generate starting position for optimization
191 if startvalues is None:
192 start = numpy.zeros(nangles)
193 else:
194 start = startvalues
195
196 # check bounds
197 if bounds is None:
198 bounds = numpy.zeros(2 * nangles) - 180.
199 bounds[::2] = 180.
200 bounds.shape = (nangles, 2)
201 elif len(bounds) != nangles:
202 raise ValueError("XU.Q2AngFit: number of specified bounds invalid")
203
204 # perform optimization
205 res = scipy.optimize.minimize(_errornorm_q2ang, start,
206 args=(lqvec, expclass, ormat),
207 method='SLSQP', bounds=_makebounds(bounds),
208 constraints=constraints,
209 options={'maxiter': 1000,
210 'eps': config.EPSILON,
211 'ftol': config.EPSILON})
212
213 x, errcode, qerror = (res.x, res.status, res.fun)
214 if qerror >= 1e-7:
215 if config.VERBOSITY >= config.DEBUG:
216 print("XU.Q2AngFit: info: need second run")
217 # make a second run
218 res = scipy.optimize.minimize(_errornorm_q2ang, res.x,
219 args=(lqvec, expclass, ormat),
220 method='SLSQP',
221 bounds=_makebounds(bounds),
222 constraints=constraints,
223 options={'maxiter': 1000,
224 'eps': config.EPSILON,
225 'ftol': config.EPSILON})
226 x, errcode, qerror = (res.x, res.status, res.fun)
227
228 if ((config.VERBOSITY >= config.DEBUG) or (qerror > 10*config.EPSILON and
229 config.VERBOSITY >=
230 config.INFO_LOW)):
231 print("XU.Q2AngFit: q-error=%.4g with error-code %d (%s)"
232 % (qerror, errcode, res.message))
233
234 return x, qerror, errcode
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16 """
17 simulation subpackage of xrayutilities.
18
19 This package provides possibilities to simulate X-ray diffraction and
20 reflectivity curves of thin film samples. It could be extended for more
21 general use in future if there is demand for that.
22
23 In addition it provides a fitting routine for reflectivity data which is based
24 on lmfit.
25 """
26
27 from .darwin_theory import (DarwinModel, DarwinModelAlGaAs001,
28 DarwinModelAlloy, DarwinModelGaInAs001,
29 DarwinModelSiGe001, GradedBuffer)
30 from .fit import FitModel, fit_xrr
31 from .helpers import coplanar_alphai, get_qz
32 from .models import (DiffuseReflectivityModel, DynamicalModel,
33 DynamicalReflectivityModel, KinematicalModel,
34 KinematicalMultiBeamModel, LayerModel, Model,
35 SimpleDynamicalCoplanarModel, SpecularReflectivityModel)
36 from .mosaicity import mosaic_analytic
37 from .powder import FP_profile, PowderDiffraction
38 from .powdermodel import PowderModel, Rietveld_error_metrics, plot_powder
39 from .smaterials import (CrystalStack, GradedLayerStack, Layer, LayerStack,
40 MaterialList, Powder, PowderList,
41 PseudomorphicStack001, PseudomorphicStack111,
42 SMaterial)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16 import abc
17 import collections.abc
18 import copy
19 import warnings
20
21 import numpy
22 from scipy.constants import physical_constants
23 from scipy.misc import derivative
24
25 from .. import materials, utilities
26 from ..math import heaviside
27 from .models import LayerModel
28
29
30 def getit(it, key):
31 """
32 generator to obtain items from nested iterable
33 """
34 for elem in it:
35 if isinstance(elem, collections.abc.Iterable):
36 if key in elem:
37 yield elem[key]
38 else:
39 for subelem in getit(elem, key):
40 yield subelem
41
42
43 def getfirst(iterable, key):
44 """
45 helper function to obtain the first item in a nested iterable
46 """
47 return next(getit(iterable, key))
48
49
50 def GradedBuffer(xfrom, xto, nsteps, thickness, relaxation=1):
51 """
52 create a multistep graded composition buffer.
53
54 Parameters
55 ----------
56 xfrom : float
57 begin of the composition gradient
58 xto : float
59 end of the composition gradient
60 nsteps : int
61 number of steps of the gradient
62 thickness : float
63 total thickness of the Buffer in A
64 relaxation : float
65 relaxation of the buffer
66
67 Returns
68 -------
69 list
70 layer list needed for the Darwin model simulation
71 """
72 subthickness = thickness/nsteps
73 gradedx = numpy.linspace(xfrom, xto, nsteps)
74 layerlist = []
75 for x in gradedx:
76 layerlist.append({'t': subthickness, 'x': x, 'r': relaxation})
77 return layerlist
78
79
80 class DarwinModel(LayerModel):
81 """
82 model class inmplementing the basics of the Darwin theory for layers
83 materials. This class is not fully functional and should be used to derive
84 working models for particular material systems.
85
86 To make the class functional the user needs to implement the
87 init_structurefactors() and _calc_mono() methods
88 """
89 ncalls = 0
90
91 def __init__(self, qz, qx=0, qy=0, **kwargs):
92 """
93 constructor of the model class. The arguments consist of basic
94 parameters which are needed to prepare the calculation of the model.
95
96 Parameters
97 ----------
98 qz : array-like
99 momentum transfer values for the calculation
100 qx, qy : float, optional
101 inplane momentum transfer (not implemented!)
102 I0 : float, optional
103 the primary beam intensity
104 background : float, optional
105 the background added to the simulation
106 resolution_width : float, optional
107 width of the resolution function (deg)
108 polarization : {'S', 'P', 'both'}
109 polarization of the x-ray beam. If set to 'both' also Cmono, the
110 polarization factor of the monochromator should be set
111 experiment : Experiment, optional
112 experiment class containing geometry and energy of the experiment.
113 Cmono : float, optional
114 polarization factor of the monochromator
115 energy : float or str, optional
116 x-ray energy in eV
117 """
118 self.polarization = kwargs.pop('polarization', 'S')
119 exp = kwargs.pop('experiment', None)
120 self.Cmono = kwargs.pop('Cmono', 1)
121 super().__init__(exp, **kwargs)
122
123 self.npoints = len(qz)
124 self.qz = numpy.asarray(qz)
125 self.qinp = (qx, qy)
126 if self.qinp != (0, 0):
127 raise NotImplementedError('asymmetric CTR simulation is not yet '
128 'supported -> approach the authors')
129 self.init_structurefactors()
130 # initialize coplanar geometry
131 k = self.exp.k0
132 qv = numpy.asarray([(qx, qy, q) for q in self.qz])
133 Q = numpy.linalg.norm(qv, axis=1)
134 theta = numpy.arcsin(Q / (2 * k))
135 domega = numpy.arctan2(numpy.sqrt(qx**2 + qy**2), self.qz)
136 self.alphai, self.alphaf = (theta + domega, theta - domega)
137 # polarization factor
138 self.C = {'S': numpy.ones(len(self.qz)),
139 'P': numpy.abs(numpy.cos(self.alphai + self.alphaf))}
140
141 def init_structurefactors(self):
142 """
143 calculates the needed atomic structure factors
144 """
145 pass
146
147 def _calc_mono(self, pdict, pol):
148 """
149 calculate the reflection and transmission coefficients of monolayer
150
151 Parameters
152 ----------
153 pdict : dict
154 property dictionary, contains all the properties for the structure
155 factor calculation
156 pol : {'S', 'P'}
157 polarization of the x-rays; sigma or pi
158
159 Returns
160 -------
161 r, rbar, t : float or array-like
162 reflection, backside reflection, and tranmission coefficients
163 """
164 pass
165
166 def _calc_double(self, ra, rabar, ta, rb, rbbar, tb, d):
167 """
168 calculate reflection coefficients for the double layer from the
169 reflection values of the single layers
170
171 Parameters
172 ----------
173 ra, rabar, ta : float or array-like
174 reflection, backside reflection, and transmission coefficients of
175 layer A
176 rb, rbbar, tb : float or array-like
177 same for layer B
178 d : float
179 distance between the layers
180
181 Returns
182 -------
183 r, rbar, t : float or array-like
184 reflection, backside reflection, and tranmission coefficients
185 """
186 self.ncalls += 1
187 e = numpy.exp(-1j*self.qz*d)
188 eh = numpy.exp(-1j*self.qz*d/2)
189 denom = (1-rabar*rb*e)
190 rab = ra + rb*(ta*ta*e)/denom
191 rabbar = rbbar + rabar*(tb*tb*e)/(1-rbbar*ra*e)
192 tab = ta*tb*eh/denom
193 return rab, rabbar, tab
194
195 def simulate(self, ml):
196 """
197 main simulation function for the Darwin model. will calculate the
198 reflected intensity
199
200 Parameters
201 ----------
202 ml : iterable
203 monolayer sequence of the sample. This should be created with the
204 function make_monolayer(). see its documentation for details
205 """
206 self.ncalls = 0
207 Ih = {'S': numpy.zeros(len(self.qz)), 'P': numpy.zeros(len(self.qz))}
208 geomfact = heaviside(self.alphai) * heaviside(self.alphaf)
209 for pol in self.get_polarizations():
210 r, rbar, t = (numpy.zeros(self.npoints, dtype=numpy.complex),
211 numpy.zeros(self.npoints, dtype=numpy.complex),
212 numpy.ones(self.npoints, dtype=numpy.complex))
213 for nrep, subml in ml:
214 r, rbar, t = self._recur_sim(nrep, subml, r, rbar, t, pol)
215 self.r, self.rbar, self.t = r, rbar, t
216 Ih[pol] = numpy.abs(self.r)**2 * geomfact
217
218 ret = self.join_polarizations(Ih['S'], Ih['P'])
219 return self._create_return(self.qz, numpy.sqrt(ret))
220
221 def _recur_sim(self, nrep, ml, r, rbar, t, pol):
222 """
223 recursive simulation function for the calculation of the reflected,
224 backside reflected and transmitted wave factors (internal)
225
226 Parameters
227 ----------
228 ml : iterable
229 monolayer sequence of the calculation block. This should be created
230 with the function make_monolayer(). see its documentation for
231 details
232 r : float or array-like
233 reflection factor of the upper layers (needed for the recursion)
234 rbar : float or array-like
235 back-side reflection factor of the upper layers (needed for the
236 recursion)
237 t : float or array-like
238 transmission factor of the upper layers (needed for the recursion)
239 pol : {'S', 'P'}
240 polarization of the x-rays
241
242 Returns
243 -------
244 r, rbar, t : float or array-like
245 reflection and transmission of the full stack
246 """
247 if isinstance(ml, list):
248 rm, rmbar, tm = (None, None, None)
249 for nsub, subml in ml:
250 rm, rmbar, tm = self._recur_sim(nsub, subml, rm,
251 rmbar, tm, pol)
252 d = getfirst(ml, 'd')
253 else:
254 rm, rmbar, tm = self._calc_mono(ml, pol)
255 d = ml['d']
256
257 Nmax = int(numpy.log(nrep) / numpy.log(2)) + 1
258 for i in range(Nmax):
259 if r is None:
260 r, rbar, t = rm, rmbar, tm
261 elif (nrep // (2**i)) % 2 == 1:
262 r, rbar, t = self._calc_double(r, rbar, t, rm, rmbar, tm, d)
263 rm, rmbar, tm = self._calc_double(rm, rmbar, tm, rm, rmbar, tm, d)
264
265 return r, rbar, t
266
267
268 class DarwinModelAlloy(DarwinModel, utilities.ABC):
269 """
270 extension of the DarwinModel for an binary alloy system were one parameter
271 is used to determine the chemical composition
272
273 To make the class functional the user needs to implement the
274 get_dperp_apar() method and define the substrate lattice parameter (asub).
275 See the DarwinModelSiGe001 class for an implementation example.
276 """
277 @abc.abstractmethod
278 def get_dperp_apar(self, x, apar, r=1):
279 """
280 calculate inplane lattice parameter and the out of plane lattice plane
281 spacing (of the atomic planes!) from composition and relaxation.
282
283 Parameters
284 ----------
285 x : float
286 chemical composition parameter
287 apar : float
288 inplane lattice parameter of the material below the current layer
289 (onto which the present layer is strained to). This value also
290 served as a reference for the relaxation parameter.
291 r : float
292 relaxation parameter. 1=relaxed, 0=pseudomorphic
293
294 Returns
295 -------
296 dperp : float
297 apar : float
298 """
299 raise NotImplementedError("abstract method needs to be overwritten")
300
301 def make_monolayers(self, s):
302 """
303 create monolayer sequence from layer list
304
305 Parameters
306 ----------
307 s : list
308 layer model. list of layer dictionaries including possibility to
309 form superlattices. As an example 5 repetitions of a
310 Si(10nm)/Ge(15nm) superlattice on Si would like like:
311
312 >>> s = [(5, [{'t': 100, 'x': 0, 'r': 0},
313 >>> {'t': 150, 'x': 1, 'r': 0}]),
314 >>> {'t': 3500000, 'x': 0, 'r': 0}]
315
316 the dictionaries must contain 't': thickness in A, 'x': chemical
317 composition, and either 'r': relaxation or 'ai': inplane lattice
318 parameter.
319 Future implementations for asymmetric peaks might include layer
320 type 'l' (not yet implemented). Already now any additional property
321 in the dictionary will be handed on to the returned monolayer list.
322 asub : float
323 inplane lattice parameter of the substrate
324
325 Returns
326 -------
327 list
328 monolayer list in a format understood by the simulate and
329 xGe_profile methods
330 """
331 ml = []
332 ai = self.asub
333 for subl in copy.copy(s):
334 ml, ai = self._recur_makeml(subl, ml, ai)
335 return ml
336
337 def _recur_makeml(self, s, ml, apar):
338 """
339 recursive creation of a monolayer structure (internal)
340
341 Parameters
342 ----------
343 s : list
344 layer model. list of layer dictionaries
345 ml : list
346 list of layers below what should be added (needed for recursion)
347 apar : float
348 inplane lattice parameter of the current surface
349
350 Returns
351 -------
352 list
353 monolayer list in a format understood by the simulate and
354 prop_profile methods
355 """
356
357 if isinstance(s, tuple):
358 nrep, sd = s
359 if isinstance(sd, dict):
360 sd = [sd, ]
361 if any([r > 0 for r in getit(sd, 'r')]): # if relaxation
362 for i in range(nrep):
363 for subsd in sd:
364 ml, apar = self._recur_makeml(subsd, ml, apar=apar)
365 else: # no relaxation in substructure
366 subl = []
367 for subsd in sd:
368 subl, apar = self._recur_makeml(subsd, subl, apar=apar)
369 ml.insert(0, (nrep, subl))
370 elif isinstance(s, dict):
371 x = s.pop('x')
372 if callable(x): # composition profile in layer
373 t = 0
374 T = s.pop('t')
375 if 'r' in s:
376 if s['r'] > 0:
377 warnings.warn(
378 """relaxation for composition gradient may yield
379 weird lattice parameter variation! Consider
380 supplying the inplane lattice parameter 'ai'
381 directly!""")
382 while t < T:
383 if 'r' in s:
384 r = abs(derivative(x, t, dx=1.4, n=1))*s['r']
385 dperp, apar = self.get_dperp_apar(x(t), apar, r)
386 else:
387 dperp, apar = self.get_dperp_apar(x(t), s['ai'])
388 t += dperp
389 d = copy.copy(s)
390 d.pop('r')
391 d.update({'d': dperp, 'x': x(t), 'ai': apar})
392 ml.insert(0, (1, d))
393 else: # constant composition layer
394 if 'r' in s:
395 dperp, apar = self.get_dperp_apar(x, apar, s.pop('r'))
396 else:
397 dperp, apar = self.get_dperp_apar(x, s.pop('ai'))
398 nmono = int(numpy.ceil(s['t']/dperp))
399 s.update({'d': dperp, 'x': x, 'ai': apar})
400 ml.insert(0, (nmono, s))
401 else:
402 raise Exception('wrong type (%s) of sublayer, must be tuple or'
403 ' dict' % (type(s)))
404 return ml, apar
405
406 def prop_profile(self, ml, prop):
407 """
408 calculate the profile of chemical composition or inplane lattice
409 spacing from a monolayer list. One value for each monolayer in the
410 sample is returned.
411
412 Parameters
413 ----------
414 ml : list
415 monolayer list created by make_monolayer()
416 prop : str
417 name of the property which should be evaluated. Use 'x' for the
418 chemical composition and 'ai' for the inplane lattice parameter.
419
420 Returns
421 -------
422 zm : ndarray
423 z-position, z-0 is the surface
424 propx : ndarray
425 value of the property prop for every monolayer
426 """
427
428 def startinterval(start, inter, N):
429 return numpy.arange(start, start+inter*(N+0.5), inter)
430
431 def _recur_prop(nrep, ml, zp, propx, propn):
432 if isinstance(ml, list):
433 lzp, lprop = ([], [])
434 for nreps, subml in ml:
435 lzp, lprop = _recur_prop(nreps, subml, lzp, lprop, propn)
436 else:
437 lzp = -ml['d']
438 lprop = ml[propn]
439
440 Nmax = int(numpy.log(nrep) / numpy.log(2)) + 1
441 for i in range(Nmax):
442 if (nrep // (2**i)) % 2 == 1:
443 try:
444 curzp = zp[-1]
445 except IndexError:
446 curzp = 0.0
447 zp = numpy.append(zp, lzp+curzp)
448 propx = numpy.append(propx, lprop)
449 try:
450 curlzp = lzp[-1]
451 except IndexError:
452 curlzp = lzp
453 lzp = numpy.append(lzp, lzp+curlzp)
454 lprop = numpy.append(lprop, lprop)
455 return zp, propx
456
457 zm = []
458 propx = []
459 for nrep, subml in ml:
460 zm, propx = _recur_prop(nrep, subml, zm, propx, prop)
461 return zm, propx
462
463
464 class DarwinModelSiGe001(DarwinModelAlloy):
465 """
466 model class implementing the Darwin theory of diffraction for SiGe layers.
467 The model is based on separation of the sample structure into building
468 blocks of atomic planes from which a multibeam dynamical model is
469 calculated.
470 """
471 Si = materials.Si
472 Ge = materials.Ge
473 eSi = materials.elements.Si
474 eGe = materials.elements.Ge
475 aSi = materials.Si.a1[0]
476 asub = aSi # needed for the make_monolayer function
477 re = physical_constants['classical electron radius'][0] * 1e10
478
479 @classmethod
480 def abulk(cls, x):
481 """
482 calculate the bulk (relaxed) lattice parameter of the alloy
483 """
484 return cls.aSi + (0.2 * x + 0.027 * x ** 2)
485
486 @staticmethod
487 def poisson_ratio(x):
488 """
489 calculate the Poisson ratio of the alloy
490 """
491 return 2 * (63.9-15.6*x) / (165.8-37.3*x) # according to IOFFE
492
493 @classmethod
494 def get_dperp_apar(cls, x, apar, r=1):
495 """
496 calculate inplane lattice parameter and the out of plane lattice plane
497 spacing (of the atomic planes!) from composition and relaxation
498
499 Parameters
500 ----------
501 x : float
502 chemical composition parameter
503 apar : float
504 inplane lattice parameter of the material below the current layer
505 (onto which the present layer is strained to). This value also
506 served as a reference for the relaxation parameter.
507 r : float, optional
508 relaxation parameter. 1=relaxed, 0=pseudomorphic
509
510 Returns
511 -------
512 dperp : float
513 perpendicular d-spacing
514 apar : float
515 inplane lattice parameter
516 """
517 abulk = cls.abulk(x)
518 aparl = apar + (abulk - apar) * r
519 dperp = abulk*(1+cls.poisson_ratio(x)*(1-aparl/abulk))/4.
520 return dperp, aparl
521
522 def init_structurefactors(self, temp=300):
523 """
524 calculates the needed atomic structure factors
525
526 Parameters
527 ----------
528 temp : float, optional
529 temperature used for the Debye model
530 """
531 en = self.exp.energy
532 q = numpy.sqrt(self.qinp[0]**2 + self.qinp[1]**2 + self.qz**2)
533 self.fSi = self.eSi.f(q, en) * self.Si._debyewallerfactor(temp, q)
534 self.fGe = self.eGe.f(q, en) * self.Ge._debyewallerfactor(temp, q)
535 self.fSi0 = self.eSi.f(0, en)
536 self.fGe0 = self.eGe.f(0, en)
537
538 def _calc_mono(self, pdict, pol):
539 """
540 calculate the reflection and transmission coefficients of monolayer
541
542 Parameters
543 ----------
544 pdict : dict
545 property dictionary, contains the layer properties:
546 'x': Ge-content of the layer (0: Si, 1: Ge);
547 'l': index of the layer in the unit cell (0, 1, 2, 3). important
548 for asymmetric peaks only!
549 pol : {'S', 'P'}
550 polarization of the x-rays
551
552 Returns
553 -------
554 r, rbar, t : float or array-like
555 reflection, backside reflection, and tranmission coefficients
556 """
557 ainp = pdict.get('ai')
558 xGe = pdict.get('x')
559 # pre-factor for reflection: contains footprint correction
560 gamma = 4*numpy.pi*self.re/(self.qz*ainp**2)
561 # ltype = pdict.get('l', 0)
562 # if ltype == 0: # for asymmetric peaks (not yet implemented)
563 # p1, p2 = (0, 0), (0.5, 0.5)
564 # elif ltype == 1:
565 # p1, p2 = (0.25, 0.25), (0.75, 0.75)
566 # elif ltype == 2:
567 # p1, p2 = (0.5, 0.), (0., 0.5)
568 # elif ltype == 3:
569 # p1, p2 = (0.75, 0.25), (0.25, 0.75)
570
571 r = -1j*gamma * self.C[pol] * (self.fSi+(self.fGe-self.fSi)*xGe) * 2
572 # * (exp(1j*(h*p1[0]+k*p1[1])) + exp(1j*(h*p1[0]+k*p1[1])))
573 t = 1 + 1j*gamma * (self.fSi0+(self.fGe0-self.fSi0)*xGe) * 2
574 return r, numpy.copy(r), t
575
576
577 class DarwinModelGaInAs001(DarwinModelAlloy):
578 """
579 Darwin theory of diffraction for Ga_{1-x} In_x As layers.
580 The model is based on separation of the sample structure into building
581 blocks of atomic planes from which a multibeam dynamical model is
582 calculated.
583 """
584 GaAs = materials.GaAs
585 InAs = materials.InAs
586 eGa = materials.elements.Ga
587 eIn = materials.elements.In
588 eAs = materials.elements.As
589 aGaAs = materials.GaAs.a1[0]
590 asub = aGaAs # needed for the make_monolayer function
591 re = physical_constants['classical electron radius'][0] * 1e10
592
593 @classmethod
594 def abulk(cls, x):
595 """
596 calculate the bulk (relaxed) lattice parameter of the Ga_{1-x}In_{x}As
597 alloy
598 """
599 return cls.aGaAs + 0.40505*x
600
601 @staticmethod
602 def poisson_ratio(x):
603 """
604 calculate the Poisson ratio of the alloy
605 """
606 return 2 * (4.54 + 0.8*x) / (8.34 + 3.56*x) # according to IOFFE
607
608 @classmethod
609 def get_dperp_apar(cls, x, apar, r=1):
610 """
611 calculate inplane lattice parameter and the out of plane lattice plane
612 spacing (of the atomic planes!) from composition and relaxation
613
614 Parameters
615 ----------
616 x : float
617 chemical composition parameter
618 apar : float
619 inplane lattice parameter of the material below the current layer
620 (onto which the present layer is strained to). This value also
621 served as a reference for the relaxation parameter.
622 r : float
623 relaxation parameter. 1=relaxed, 0=pseudomorphic
624
625 Returns
626 -------
627 dperp : float
628 perpendicular d-spacing
629 apar : float
630 inplane lattice parameter
631 """
632 abulk = cls.abulk(x)
633 aparl = apar + (abulk - apar) * r
634 dperp = abulk*(1+cls.poisson_ratio(x)*(1-aparl/abulk))/4.
635 return dperp, aparl
636
637 def init_structurefactors(self, temp=300):
638 """
639 calculates the needed atomic structure factors
640
641 Parameters
642 ----------
643 temp : float, optional
644 temperature used for the Debye model
645 """
646 en = self.exp.energy
647 q = numpy.sqrt(self.qinp[0]**2 + self.qinp[1]**2 + self.qz**2)
648 fAs = self.eAs.f(q, en)
649 self.fGaAs = (self.eGa.f(q, en) + fAs) \
650 * self.GaAs._debyewallerfactor(temp, q)
651 self.fInAs = (self.eIn.f(q, en) + fAs) \
652 * self.InAs._debyewallerfactor(temp, q)
653 self.fGaAs0 = self.eGa.f(0, en) + self.eAs.f(0, en)
654 self.fInAs0 = self.eIn.f(0, en) + self.eAs.f(0, en)
655
656 def _calc_mono(self, pdict, pol):
657 """
658 calculate the reflection and transmission coefficients of monolayer
659
660 Parameters
661 ----------
662 pdict : dict
663 property dictionary, contains the layer properties:
664 'x': In-content of the layer (0: GaAs, 1: InAs)
665 pol : {'S', 'P'}
666 polarization of the x-rays
667
668 Returns
669 -------
670 r, rbar, t : float or array-like
671 reflection, backside reflection, and tranmission coefficients
672 """
673 ainp = pdict.get('ai')
674 xInAs = pdict.get('x')
675 # pre-factor for reflection: contains footprint correction
676 gamma = 4*numpy.pi * self.re/(self.qz*ainp**2)
677 r = -1j*gamma*self.C[pol]*(self.fGaAs+(self.fInAs-self.fGaAs)*xInAs)
678 t = 1 + 1j*gamma * (self.fGaAs0+(self.fInAs0-self.fGaAs0)*xInAs)
679 return r, numpy.copy(r), t
680
681
682 class DarwinModelAlGaAs001(DarwinModelAlloy):
683 """
684 Darwin theory of diffraction for Al_x Ga_{1-x} As layers.
685 The model is based on separation of the sample structure into building
686 blocks of atomic planes from which a multibeam dynamical model is
687 calculated.
688 """
689 GaAs = materials.GaAs
690 AlAs = materials.AlAs
691 eGa = materials.elements.Ga
692 eAl = materials.elements.Al
693 eAs = materials.elements.As
694 aGaAs = materials.GaAs.a1[0]
695 asub = aGaAs # needed for the make_monolayer function
696 re = physical_constants['classical electron radius'][0] * 1e10
697
698 @classmethod
699 def abulk(cls, x):
700 """
701 calculate the bulk (relaxed) lattice parameter of the Al_{x}Ga_{1-x}As
702 alloy
703 """
704 return cls.aGaAs + 0.0078*x
705
706 @staticmethod
707 def poisson_ratio(x):
708 """
709 calculate the Poisson ratio of the alloy
710 """
711 return 2 * (5.38+0.32*x) / (11.88+0.14*x) # according to IOFFE
712
713 @classmethod
714 def get_dperp_apar(cls, x, apar, r=1):
715 """
716 calculate inplane lattice parameter and the out of plane lattice plane
717 spacing (of the atomic planes!) from composition and relaxation
718
719 Parameters
720 ----------
721 x : float
722 chemical composition parameter
723 apar : float
724 inplane lattice parameter of the material below the current layer
725 (onto which the present layer is strained to). This value also
726 served as a reference for the relaxation parameter.
727 r : float
728 relaxation parameter. 1=relaxed, 0=pseudomorphic
729
730 Returns
731 -------
732 dperp : float
733 perpendicular d-spacing
734 apar : float
735 inplane lattice parameter
736 """
737 abulk = cls.abulk(x)
738 aparl = apar + (abulk - apar) * r
739 dperp = abulk*(1+cls.poisson_ratio(x)*(1-aparl/abulk))/4.
740 return dperp, aparl
741
742 def init_structurefactors(self, temp=300):
743 """
744 calculates the needed atomic structure factors
745
746 Parameters
747 ----------
748 temp : float, optional
749 temperature used for the Debye model
750 """
751 en = self.exp.energy
752 q = numpy.sqrt(self.qinp[0]**2 + self.qinp[1]**2 + self.qz**2)
753 fAs = self.eAs.f(q, en)
754 self.fGaAs = (self.eGa.f(q, en) + fAs) \
755 * self.GaAs._debyewallerfactor(temp, q)
756 self.fAlAs = (self.eAl.f(q, en) + fAs) \
757 * self.AlAs._debyewallerfactor(temp, q)
758 self.fGaAs0 = self.eGa.f(0, en) + self.eAs.f(0, en)
759 self.fAlAs0 = self.eAl.f(0, en) + self.eAs.f(0, en)
760
761 def _calc_mono(self, pdict, pol):
762 """
763 calculate the reflection and transmission coefficients of monolayer
764
765 Parameters
766 ----------
767 pdict : dict
768 property dictionary, contains the layer properties:
769 'x': Al-content of the layer (0: GaAs, 1: AlAs)
770 pol : {'S', 'P'}
771 polarization of the x-rays
772
773 Returns
774 -------
775 r, rbar, t : float or array-like
776 reflection, backside reflection, and tranmission coefficients
777 """
778 ainp = pdict.get('ai')
779 xAlAs = pdict.get('x')
780 # pre-factor for reflection: contains footprint correction
781 gamma = 4*numpy.pi * self.re/(self.qz*ainp**2)
782 r = -1j*gamma*self.C[pol]*(self.fGaAs+(self.fAlAs-self.fGaAs)*xAlAs)
783 t = 1 + 1j*gamma * (self.fGaAs0+(self.fAlAs0-self.fGaAs0)*xAlAs)
784 return r, numpy.copy(r), t
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import warnings
18
19 import numpy
20
21 from .. import config, utilities
22 from ..exception import InputError
23 from . import models
24
25
26 def fit_xrr(reflmod, params, ai, data=None, eps=None, xmin=-numpy.inf,
27 xmax=numpy.inf, plot=False, verbose=False, elog=True, maxfev=500):
28 """
29 optimize function for a Reflectivity Model using lmfit. The fitting
30 parameters must be specified as instance of lmfits Parameters class.
31
32 Parameters
33 ----------
34 reflmod : SpecularReflectivityModel
35 preconfigured model used for the fitting
36 params : lmfit.Parameters
37 instance of lmfits Parameters class. For every layer the parameters
38 '{}_thickness', '{}_roughness', '{}_density', with '{}' representing
39 the layer name are supported. In addition the setup parameters:
40
41 - 'I0' primary beam intensity
42 - 'background' background added to the simulation
43 - 'sample_width' size of the sample along the beam
44 - 'beam_width' width of the beam in the same units
45 - 'resolution_width' width of the resolution function in deg
46 - 'shift' experimental shift of the incidence angle array
47
48 ai : array-like
49 array of incidence angles for the calculation
50 data : array-like
51 experimental data which should be fitted
52 eps : array-like, optional
53 error bar of the data
54 xmin : float, optional
55 minimum value of ai which should be used. a mask is generated to cut
56 away other data
57 xmax : float, optional
58 maximum value of ai which should be used. a mask is generated to cut
59 away other data
60 plot : bool, optional
61 flag to decide wheter an plot should be created showing the fit's
62 progress. If plot is a string it will be used as figure name, which
63 makes reusing the figures easier.
64 verbose : bool, optional
65 flag to tell if the variation of the fitting error should be output
66 during the fit.
67 elog : bool, optional
68 logarithmic error during the fit
69 maxfev : int, optional
70 maximum number of function evaluations during the leastsq optimization
71
72 Returns
73 -------
74 res : lmfit.MinimizerResult
75 object from lmfit, which contains the fitted parameters in res.params
76 (see res.params.pretty_print) or try lmfit.report_fit(res)
77 """
78 warnings.warn("deprecated function -> change to FitModel",
79 DeprecationWarning)
80 lmfit = utilities.import_lmfit('XU.simpack')
81
82 if plot:
83 plot, plt = utilities.import_matplotlib_pyplot('XU.simpack')
84
85 mask = numpy.logical_and(ai > xmin, ai < xmax)
86 # check Parameters
87 lstack = reflmod.lstack
88 if not isinstance(params, lmfit.Parameters):
89 raise TypeError('params argument must be of type lmfit.Parameters')
90 for lname, l in zip(lstack.namelist, lstack):
91 pname = '{}_thickness'.format(lname)
92 if pname not in params:
93 raise InputError('XU.simpack.fit_xrr: Parameter %s not defined.'
94 % pname)
95 pname = '{}_roughness'.format(lname)
96 if pname not in params:
97 params.add(pname, value=l.roughness, vary=False)
98 if config.VERBOSITY >= config.INFO_LOW:
99 print("XU.simpack.fit_xrr: adding fixed parameter %s to model"
100 % pname)
101 pname = '{}_density'.format(lname)
102 if pname not in params:
103 params.add(pname, value=l.density, vary=False)
104 if config.VERBOSITY >= config.INFO_LOW:
105 print("XU.simpack.fit_xrr: adding fixed parameter %s to model"
106 % pname)
107
108 # residual function
109 def xrr_residual(pars, ai, reflmod, **kwargs):
110 """
111 residual function for lmfit Minimizer routine
112
113 Parameters
114 ----------
115 pars : lmfit.Parameters
116 fit parameters
117 ai : array-like
118 array of incidence angles
119 reflmod : SpecularReflectivityModel
120 reflectivity model object
121 data : array-like, optional
122 experimental data of same shape as ai (default: None)
123 eps : array-like
124 experimental error bars of same shape as ai (default: None)
125 """
126 data = kwargs.get('data', None)
127 eps = kwargs.get('eps', None)
128 pvals = pars.valuesdict()
129 # update reflmod global parameters:
130 reflmod.I0 = pvals.get('I0', reflmod.I0)
131 reflmod.background = pvals.get('background', reflmod.background)
132 reflmod.sample_width = pvals.get('sample_width', reflmod.sample_width)
133 reflmod.beam_width = pvals.get('beam_width', reflmod.beam_width)
134 reflmod.resolution_width = pvals.get('resolution_width',
135 reflmod.resolution_width)
136 shift = pvals.get('shift', 0)
137 # update layer properties
138 for lname, l in zip(reflmod.lstack.namelist, reflmod.lstack):
139 l.thickness = pvals['{}_thickness'.format(lname)]
140 l.roughness = pvals['{}_roughness'.format(lname)]
141 l.density = pvals['{}_density'.format(lname)]
142 # run simulation
143 model = reflmod.simulate(ai - shift)
144 if data is None:
145 return model
146 if kwargs['elog']:
147 logmodel = numpy.log10(model)
148 logdata = numpy.log10(data)
149 mask = numpy.logical_and(numpy.isfinite(logmodel),
150 numpy.isfinite(logdata))
151 return logmodel[mask] - logdata[mask]
152 if eps is None:
153 return (model - data)
154 return (model - data)/eps
155
156 # plot of initial values
157 if plot:
158 plt.ion()
159 if isinstance(plot, str):
160 plt.figure(plot)
161 else:
162 plt.figure('XU:fit_xrr')
163 plt.clf()
164 ax = plt.subplot(111)
165 ax.set_yscale("log", nonposy='clip')
166 if data is not None:
167 if eps is not None:
168 eline = plt.errorbar(ai, data, yerr=eps, ecolor='0.3',
169 fmt='ko', errorevery=int(ai.size/80),
170 label='data')[0]
171 else:
172 eline, = plt.semilogy(ai, data, 'ko', label='data')
173 if verbose:
174 init, = plt.semilogy(ai,
175 xrr_residual(params, ai, reflmod, data=None),
176 '-', color='0.5', label='initial')
177 if eline:
178 zord = eline.zorder+2
179 else:
180 zord = 1
181 fline, = plt.semilogy(
182 ai[mask], xrr_residual(params, ai[mask], reflmod, data=None),
183 'r-', lw=2, label='fit', zorder=zord)
184 plt.legend()
185 plt.xlabel('incidence angle (deg)')
186 plt.ylabel('Intensity (arb. u.)')
187 plt.show()
188 else:
189 fline = None
190
191 # create and run minimizer/minimization
192 if eps is None:
193 eps = numpy.ones(ai.shape)
194
195 def cb_func(params, niter, resid, ai, reflmod, **kwargs):
196 if kwargs.get('verbose', False):
197 print('{:04d} {:12.3e}'.format(niter, numpy.sum(resid**2)))
198 if kwargs.get('plot', False) and niter % 20 == 0:
199 fl = kwargs['fline']
200 plt.sca(ax)
201 fl.set_ydata(xrr_residual(params, ai, reflmod, data=None))
202 plt.draw()
203 plt.pause(0.001) # enable better mpl backend compatibility
204
205 minimizer = lmfit.Minimizer(
206 xrr_residual, params, fcn_args=(ai[mask], reflmod),
207 fcn_kws={'data': data[mask], 'eps': eps[mask], 'fline': fline,
208 'verbose': verbose, 'plot': plot, 'elog': elog},
209 iter_cb=cb_func, maxfev=maxfev)
210 res = minimizer.minimize()
211
212 # final update of plot
213 if plot:
214 plt.sca(ax)
215 plt.semilogy(ai, xrr_residual(res.params, ai, reflmod, data=None),
216 'g-', lw=1, label='fit', zorder=fline.zorder-1)
217 cb_func(res.params, 0, res.residual, ai[mask], reflmod, fline=fline,
218 plot=True)
219
220 return res
221
222
223 class FitModel(object):
224 """
225 Wrapper for the lmfit Model class working for instances of LayerModel
226
227 Typically this means that after initialization of `FitModel` you want to
228 use make_params to get a `lmfit.Parameters` list which one customizes for
229 fitting.
230
231 Later on you can call `fit` and `eval` methods with those parameter list.
232 """
233 def __init__(self, lmodel, verbose=False, plot=False, elog=True, **kwargs):
234 """
235 initialization of a FitModel which uses lmfit for the actual fitting,
236 and generates an according lmfit.Model internally for the given
237 pre-configured LayerModel, or subclasses thereof which includes models
238 for reflectivity, kinematic and dynamic diffraction.
239
240 Parameters
241 ----------
242 lmodel : LayerModel
243 pre-configured instance of LayerModel or any subclass
244 verbose : bool, optional
245 flag to enable verbose printing during fitting
246 plot : bool or str, optional
247 flag to decide wheter an plot should be created showing the fit's
248 progress. If plot is a string it will be used as figure name, which
249 makes reusing the figures easier.
250 elog : bool, optional
251 flag to enable a logarithmic error metric between model and data.
252 Since the dynamic range of data is often large its often benefitial
253 to keep this enabled.
254 kwargs : dict, optional
255 additional keyword arguments are forwarded to the `simulate` method
256 of `lmodel`
257 """
258 self.verbose = verbose
259 self.plot = plot
260 self.elog = elog
261 lmfit = utilities.import_lmfit('XU.simpack')
262
263 assert isinstance(lmodel, models.LayerModel)
264 self.lmodel = lmodel
265 # generate dynamic function for model evalution
266 funcstr = "def func(x, "
267 # add LayerModel parameters
268 for p in self.lmodel.fit_paramnames:
269 funcstr += "{}, ".format(p)
270 # add LayerStack parameters
271 for l in self.lmodel.lstack:
272 for param in self.lmodel.lstack_params:
273 funcstr += '{}_{}, '.format(l.name, param)
274 if self.lmodel.lstack_structural_params:
275 for param in l._structural_params:
276 funcstr += '{}_{}, '.format(l.name, param)
277 funcstr += "lmodel=self.lmodel, **kwargs):\n"
278 # define modelfunc content
279 for p in self.lmodel.fit_paramnames:
280 funcstr += " setattr(lmodel, '{}', {})\n".format(p, p)
281 for i, l in enumerate(self.lmodel.lstack):
282 for param in self.lmodel.lstack_params:
283 varname = '{}_{}'.format(l.name, param)
284 cmd = " setattr(lmodel.lstack[{}], '{}', {})\n"
285 funcstr += cmd.format(i, param, varname)
286 if self.lmodel.lstack_structural_params:
287 for param in l._structural_params:
288 varname = '{}_{}'.format(l.name, param)
289 cmd = " setattr(lmodel.lstack[{}], '{}', {})\n"
290 funcstr += cmd.format(i, param, varname)
291 # perform actual model calculation
292 funcstr += " return lmodel.simulate(x, **kwargs)"
293
294 namespace = {'self': self}
295 exec(funcstr, {'lmodel': self.lmodel}, namespace)
296 self.func = namespace['func']
297 self.emetricfunc = numpy.log10 if self.elog else lambda x: x
298
299 def _residual(params, data, weights, **kwargs):
300 """
301 Return the residual. This is a (simplified, only real values)
302 reimplementation of the lmfit.Model._residual function which adds
303 the possibility of a logarithmic error metric.
304
305 Default residual: (data-model)*weights.
306 """
307 scale = self.emetricfunc
308 model = scale(self.eval(params, **kwargs))
309 sdata = scale(data)
310 mask = numpy.logical_and(numpy.isfinite(model),
311 numpy.isfinite(sdata))
312 diff = model[mask] - sdata[mask]
313 if weights is not None and scale(1) == 1:
314 diff *= weights
315 return numpy.asarray(diff).ravel()
316
317 self.lmm = lmfit.Model(self.func,
318 name=self.lmodel.__class__.__name__, **kwargs)
319 self.lmm._residual = _residual
320 for method in ('set_param_hint', 'print_param_hints', 'eval',
321 'make_params'):
322 setattr(self, method, getattr(self.lmm, method))
323 # set default parameter hints
324 self._default_hints()
325 self.set_fit_limits()
326
327 def set_fit_limits(self, xmin=-numpy.inf, xmax=numpy.inf, mask=None):
328 """
329 set fit limits. If mask is given it must have the same size as the
330 `data` and `x` variables given to fit. If mask is None it will be
331 generated from xmin and xmax.
332
333 Parameters
334 ----------
335 xmin : float, optional
336 minimum value of x-values to include in the fit
337 xmax : float, optional
338 maximum value of x-values to include in the fit
339 mask : boolean array, optional
340 mask to be used for the data given to the fit
341 """
342 self.mask = mask
343 self.xmin = xmin
344 self.xmax = xmax
345
346 def fit(self, data, params, x, weights=None, fit_kws=None, **kwargs):
347 """
348 wrapper around lmfit.Model.fit which enables plotting during the
349 fitting
350
351 Parameters
352 ----------
353 data : ndarray
354 experimental values
355 params : lmfit.Parameters
356 list of parameters for the fit, use make_params for generation
357 x : ndarray
358 independent variable (incidence angle or q-position depending on
359 the model)
360 weights : ndarray, optional
361 values of weights for the fit, same size as data
362 fit_kws : dict, optional
363 Options to pass to the minimizer being used
364 kwargs : dict, optional
365 keyword arguments which are passed to lmfit.Model.fit
366
367 Returns
368 -------
369 lmfit.ModelResult
370 """
371 class FitPlot(object):
372 def __init__(self, figname, logscale):
373 self.figname = figname
374 self.logscale = logscale
375 if not self.figname:
376 self.plot = False
377 else:
378 f, plt = utilities.import_matplotlib_pyplot('XU.simpack')
379 self.plt = plt
380 self.plot = f
381
382 def plot_init(self, x, data, weights, model, mask, verbose):
383 if not self.plot:
384 return
385 self.plt.ion()
386 if isinstance(self.figname, str):
387 self.fig = self.plt.figure(self.figname)
388 else:
389 self.fig = self.plt.figure('XU:FitModel')
390 self.plt.clf()
391 self.ax = self.plt.subplot(111)
392
393 if weights is not None:
394 eline = self.ax.errorbar(
395 x, data, yerr=1/weights, ecolor='0.3', fmt='ko',
396 errorevery=int(x.size/80), label='data')[0]
397 else:
398 eline, = self.ax.plot(x, data, 'ko', label='data')
399 if verbose:
400 init, = self.ax.plot(
401 x, model, '-', color='0.5', label='initial')
402 if eline:
403 self.zord = eline.zorder+2
404 else:
405 self.zord = 1
406 if self.logscale:
407 self.ax.set_yscale("log", nonposy='clip')
408 self.fline = None
409
410 def showplot(self, xlab=self.lmodel.xlabelstr,
411 ylab='Intensity (arb. u.)'):
412 if not self.plot:
413 return
414 self.plt.xlabel(xlab)
415 self.plt.ylabel(ylab)
416 self.plt.legend()
417 self.fig.set_tight_layout(True)
418 self.plt.show()
419
420 def updatemodelline(self, x, newmodel):
421 if not self.plot:
422 return
423 try:
424 self.plt.sca(self.ax)
425 except ValueError:
426 return
427 if self.fline is None:
428 self.fline, = self.ax.plot(
429 x, newmodel, 'r-', lw=2, label='fit', zorder=self.zord)
430 else:
431 self.fline.set_data(x, newmodel)
432 canvas = self.fig.canvas # see plt.draw function (avoid show!)
433 canvas.draw_idle()
434 canvas.start_event_loop(0.001)
435
436 def addfullmodelline(self, x, y):
437 if not self.plot:
438 return
439 self.ax.plot(x, y, 'g-', lw=1, label='full model',
440 zorder=self.zord-1)
441
442 if self.mask:
443 mask = self.mask
444 else:
445 mask = numpy.logical_and(x >= self.xmin, x <= self.xmax)
446 mweights = weights
447 if mweights is not None:
448 mweights = weights[mask]
449
450 # create initial plot
451 self.fitplot = FitPlot(self.plot, self.elog)
452 initmodel = self.eval(params, x=x, **kwargs)
453 self.fitplot.plot_init(x, data, weights, initmodel, mask, self.verbose)
454 self.fitplot.showplot()
455
456 # create callback function
457 def cb_func(params, niter, resid, *args, **kwargs):
458 if self.verbose:
459 print('{:04d} {:12.3e}'.format(niter, numpy.sum(resid**2)))
460 if self.fitplot.plot and niter % 20 == 0:
461 self.fitplot.updatemodelline(kwargs['x'],
462 self.eval(params, **kwargs))
463
464 # perform fitting
465 res = self.lmm.fit(data[mask], params, x=x[mask], weights=mweights,
466 fit_kws=fit_kws, iter_cb=cb_func, **kwargs)
467
468 # final update of plot
469 if self.fitplot.plot:
470 try:
471 self.fitplot.plt.sca(self.fitplot.ax)
472 except ValueError:
473 self.fitplot.plot_init(x, data, weights, initmodel, mask,
474 self.verbose)
475 fittedmodel = self.eval(res.params, x=x, **kwargs)
476 self.fitplot.addfullmodelline(x, fittedmodel)
477 self.fitplot.updatemodelline(x[mask], fittedmodel[mask])
478 self.fitplot.showplot()
479
480 return res
481
482 def _default_hints(self):
483 """
484 set useful hints for parameters all LayerModels have
485 """
486 # general parameters
487 for pn in self.lmodel.fit_paramnames:
488 self.set_param_hint(pn, value=getattr(self.lmodel, pn), vary=False)
489 for pn in ('I0', 'background'):
490 self.set_param_hint(pn, vary=True, min=0)
491 self.set_param_hint('resolution_width', min=0, vary=False)
492 self.set_param_hint('energy', min=1000, vary=False)
493
494 # parameters of the layerstack
495 for l in self.lmodel.lstack:
496 for param in self.lmodel.lstack_params:
497 varname = '{}_{}'.format(l.name, param)
498 self.set_param_hint(varname, value=getattr(l, param), min=0)
499 if param == 'density':
500 self.set_param_hint(varname, max=1.5*l.material.density)
501 if param == 'thickness':
502 self.set_param_hint(varname, max=2*l.thickness)
503 if param == 'roughness':
504 self.set_param_hint(varname, max=50)
505 if self.lmodel.lstack_structural_params:
506 for param in l._structural_params:
507 varname = '{}_{}'.format(l.name, param)
508 self.set_param_hint(varname, value=getattr(l, param),
509 vary=False)
510 if 'occupation' in param:
511 self.set_param_hint(varname, min=0, max=1)
512 if 'biso' in param:
513 self.set_param_hint(varname, min=0, max=5)
514 if self.lmodel.lstack[0].thickness == numpy.inf:
515 varname = '{}_{}'.format(self.lmodel.lstack[0].name, 'thickness')
516 self.set_param_hint(varname, vary=False)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import numpy
18
19 from .. import config, utilities
20
21
22 def coplanar_alphai(qx, qz, en='config'):
23 """
24 calculate coplanar incidence angle from knowledge of the qx and qz
25 coordinates
26
27 Parameters
28 ----------
29 qx : array-like
30 inplane momentum transfer component
31 qz : array-like
32 out of plane momentum transfer component
33 en : float or str, optional
34 x-ray energy (eV). By default the value from the config is used.
35
36 Returns
37 -------
38 alphai : array-like
39 the incidence angle in degree. points in the Laue zone are set to
40 'nan'.
41 """
42 if isinstance(en, str) and en == 'config':
43 en = utilities.energy(config.ENERGY)
44 k = 2 * numpy.pi / utilities.en2lam(en)
45 th = numpy.arcsin(numpy.sqrt(qx**2 + qz**2) / (2 * k))
46 ai = numpy.arctan2(qx, qz) + th
47 if isinstance(ai, numpy.ndarray): # remove positions in Laue zone
48 ai[qz < numpy.sqrt(2 * qx * k - qx**2)] = numpy.nan
49 else:
50 if qz < numpy.sqrt(2 * qx * k - qx**2):
51 ai = numpy.nan
52 return numpy.degrees(ai)
53
54
55 def get_qz(qx, alphai, en='config'):
56 """
57 calculate the qz position from the qx position and the incidence angle for
58 a coplanar diffraction geometry
59
60 Parameters
61 ----------
62 qx : array-like
63 inplane momentum transfer component
64 alphai : array-like
65 incidence angle (deg)
66 en : float or str, optional
67 x-ray energy (eV). By default the value from the config is used.
68
69 Returns
70 -------
71 array-like
72 the qz position for the given incidence angle
73 """
74 if isinstance(en, str) and en == 'config':
75 en = utilities.energy(config.ENERGY)
76 k = 2 * numpy.pi / utilities.en2lam(en)
77 ai = numpy.radians(alphai)
78 return numpy.sqrt(k**2 - (qx + k * numpy.cos(ai))**2) + k * numpy.sin(ai)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import abc
18 import copy
19 import math as pymath
20
21 import numpy
22 import scipy.constants as constants
23 import scipy.integrate as integrate
24 import scipy.interpolate as interpolate
25 from scipy.special import erf, j0
26
27 from .. import config, utilities
28 from ..exception import InputError
29 from ..experiment import Experiment
30 from ..math import NormGauss1d, NormLorentz1d, heaviside, solve_quartic
31 from .smaterials import Layer, LayerStack
32
33
34 def startdelta(start, delta, num):
35 end = start + delta * (num - 1)
36 return numpy.linspace(start, end, int(num))
37
38
39 class Model(object):
40 """
41 generic model class from which further models can be derived from
42 """
43
44 def __init__(self, experiment, **kwargs):
45 """
46 constructor for a generical simulation model.
47 currently only the experiment class describing the diffraction geometry
48 is stored in the base class
49
50 Parameters
51 ----------
52 experiment : Experiment
53 class describing the diffraction geometry, energy and wavelength of
54 the model
55 resolution_width : float, optional
56 defines the width of the resolution
57 I0 : float, optional
58 the primary beam flux/intensity
59 background : float, optional
60 the background added to the simulation
61 energy : float or str
62 the experimental energy in eV
63 resolution_type : {'Gauss', 'Lorentz'}, optional
64 type of resolution function, default: Gauss
65 """
66 local_fit_params = {'resolution_width': 'width of the resolution',
67 'I0': 'primary beam intensity',
68 'background': 'background intensity',
69 'energy': 'x-ray energy in eV'}
70 if not hasattr(self, 'fit_paramnames'):
71 self.fit_paramnames = []
72 self.fit_paramnames += local_fit_params
73 valid_kwargs = copy.copy(local_fit_params)
74 valid_kwargs['resolution_type'] = 'resolution function typ'
75 utilities.check_kwargs(kwargs, valid_kwargs,
76 self.__class__.__name__)
77
78 if experiment:
79 self.exp = experiment
80 else:
81 self.exp = Experiment([1, 0, 0], [0, 0, 1])
82 self.resolution_width = kwargs.get('resolution_width', 0)
83 self.resolution_type = kwargs.get('resolution_type', 'Gauss')
84 self.I0 = kwargs.get('I0', 1)
85 self.background = kwargs.get('background', 0)
86 if 'energy' in kwargs:
87 self.energy = kwargs['energy']
88
89 @property
90 def energy(self):
91 return self.exp.energy
92
93 @energy.setter
94 def energy(self, en):
95 self.exp.energy = en
96
97 def convolute_resolution(self, x, y):
98 """
99 convolve simulation result with a resolution function
100
101 Parameters
102 ----------
103 x : array-like
104 x-values of the simulation, units of x also decide about the unit
105 of the resolution_width parameter
106 y : array-like
107 y-values of the simulation
108
109 Returns
110 -------
111 array-like
112 convoluted y-data with same shape as y
113 """
114 if self.resolution_width == 0:
115 return y
116 else:
117 dx = numpy.mean(numpy.gradient(x))
118 nres = int(20 * numpy.abs(self.resolution_width / dx))
119 xres = startdelta(-10*self.resolution_width, dx, nres + 1)
120 # the following works only exactly for equally spaced data points
121 if self.resolution_type == 'Gauss':
122 fres = NormGauss1d
123 else:
124 fres = NormLorentz1d
125 resf = fres(xres, numpy.mean(xres), self.resolution_width)
126 resf /= numpy.sum(resf) # proper normalization for discrete conv.
127 # pad y to avoid edge effects
128 interp = interpolate.InterpolatedUnivariateSpline(x, y, k=1)
129 nextmin = numpy.ceil(nres/2.)
130 nextpos = numpy.floor(nres/2.)
131 xext = numpy.concatenate(
132 (startdelta(x[0]-dx, -dx, nextmin)[-1::-1],
133 x,
134 startdelta(x[-1]+dx, dx, nextpos)))
135 ypad = numpy.asarray(interp(xext))
136 return numpy.convolve(ypad, resf, mode='valid')
137
138 def scale_simulation(self, y):
139 """
140 scale simulation result with primary beam flux/intensity and add a
141 background.
142
143 Parameters
144 ----------
145 y : array-like
146 y-values of the simulation
147
148 Returns
149 -------
150 array-like
151 scaled y-values
152 """
153 return y * self.I0 + self.background
154
155
156 class LayerModel(Model, utilities.ABC):
157 """
158 generic model class from which further thin film models can be derived from
159 """
160
161 def __init__(self, *args, **kwargs):
162 """
163 constructor for a thin film model. The arguments consist of a
164 LayerStack or individual Layer(s). Optional parameters are specified
165 in the keyword arguments.
166
167 Parameters
168 ----------
169 *args : LayerStack or Layers
170 either one LayerStack or several Layer objects can be given
171 **kwargs : dict
172 optional parameters for the simulation. ones not listed below are
173 forwarded to the superclass.
174 experiment : Experiment, optional
175 class containing geometry and energy of the experiment.
176 surface_hkl : list or tuple, optional
177 Miller indices of the surface (default: (0, 0, 1))
178 """
179 exp = kwargs.pop('experiment', None)
180 super().__init__(exp, **kwargs)
181 self.lstack_params = []
182 self.lstack_structural_params = False
183 self.xlabelstr = 'x (1)'
184 if len(args) == 1:
185 if isinstance(args[0], Layer):
186 self.lstack = LayerStack('Stack for %s'
187 % self.__class__.__name__, *args)
188 else:
189 self.lstack = args[0]
190 else:
191 self.lstack = LayerStack('Stack for %s' % self.__class__.__name__,
192 *args)
193
194 @abc.abstractmethod
195 def simulate(self):
196 """
197 abstract method that every implementation of a LayerModel has to
198 override.
199 """
200 pass
201
202 def _create_return(self, x, E, ai=None, af=None, Ir=None,
203 rettype='intensity'):
204 """
205 function to create the return value of a simulation. by default only
206 the diffracted intensity is returned. However, optionally also the
207 incidence and exit angle as well as the reflected intensity can be
208 returned.
209
210 Parameters
211 ----------
212 x : array-like
213 independent coordinate value for the convolution with the
214 resolution function
215 E : array-like
216 electric field amplitude (complex)
217 ai, af : array-like, optional
218 incidence and exit angle of the XRD beam (in radians)
219 Ir : array-like, optional
220 reflected intensity
221 rettype : {'intensity', 'field', 'all'}, optional
222 type of the return value. 'intensity' (default): returns the
223 diffracted beam flux convoluted with the resolution function;
224 'field': returns the electric field (complex) without convolution
225 with the resolution function, 'all': returns the electric field,
226 ai, af (both in degree), and the reflected intensity.
227
228 Returns
229 -------
230 return value depends on value of rettype.
231 """
232 if rettype == 'intensity':
233 ret = self.scale_simulation(
234 self.convolute_resolution(x, numpy.abs(E)**2))
235 elif rettype == 'field':
236 ret = E
237 elif rettype == 'all':
238 ret = (E, numpy.degrees(ai), numpy.degrees(af), Ir)
239 return ret
240
241 def get_polarizations(self):
242 """
243 return list of polarizations which should be calculated
244 """
245 if self.polarization == 'both':
246 return ('S', 'P')
247 else:
248 return (self.polarization,)
249
250 def join_polarizations(self, Is, Ip):
251 """
252 method to calculate the total diffracted intensity from the intensities
253 of S and P-polarization.
254 """
255 if self.polarization == 'both':
256 ret = (Is + self.Cmono * Ip) / (1 + self.Cmono)
257 else:
258 if self.polarization == 'S':
259 ret = Is
260 else:
261 ret = Ip
262 return ret
263
264
265 class KinematicalModel(LayerModel):
266 """
267 Kinematical diffraction model for specular and off-specular qz-scans. The
268 model calculates the kinematical contribution of one (hkl) Bragg peak,
269 however considers the variation of the structure factor for different 'q'.
270 The surface geometry is specified using the Experiment-object given to the
271 constructor.
272 """
273
274 def __init__(self, *args, **kwargs):
275 """
276 constructor for a kinematic thin film model. The arguments consist of a
277 LayerStack or individual Layer(s). Optional parameters are specified in
278 the keyword arguments.
279
280 Parameters
281 ----------
282 *args : LayerStack or Layers
283 either one LayerStack or several Layer objects can be given
284 **kwargs : dict
285 optional parameters; also see LayerModel/Model.
286 experiment : Experiment
287 Experiment class containing geometry and energy of the experiment.
288 """
289 super().__init__(*args, **kwargs)
290 self.lstack_params += ['thickness', ]
291 self.lstack_structural_params = True
292 self.xlabelstr = r'momentum transfer $Q_z$ ($\mathrm{\AA^{-1}}$)'
293 # precalc optical properties
294 self._init_en = 0
295 self.init_chi0()
296
297 def init_chi0(self):
298 """
299 calculates the needed optical parameters for the simulation. If any of
300 the materials/layers is changing its properties this function needs to
301 be called again before another correct simulation is made. (Changes of
302 thickness does NOT require this!)
303 """
304 if self._init_en != self.energy: # recalc properties if energy changed
305 self.chi0 = numpy.asarray([l.material.chi0(en=self.energy)
306 for l in self.lstack])
307 self._init_en = self.energy
308
309 def _prepare_kincalculation(self, qz, hkl):
310 """
311 prepare kinematic calculation by calculating some helper values
312 """
313 rel = constants.physical_constants['classical electron radius'][0]
314 rel *= 1e10
315 k = self.exp.k0
316
317 # determine q-inplane
318 t = self.exp._transform
319 ql0 = t(self.lstack[0].material.Q(*hkl))
320 qinp = numpy.sqrt(ql0[0]**2 + ql0[1]**2)
321
322 # calculate needed angles
323 qv = numpy.asarray([t.inverse((ql0[0], ql0[1], q)) for q in qz])
324 Q = numpy.linalg.norm(qv, axis=1)
325 theta = numpy.arcsin(Q / (2 * k))
326 domega = numpy.arctan2(qinp, qz)
327 alphai, alphaf = (theta + domega, theta - domega)
328 # calculate structure factors
329 f = numpy.empty((len(self.lstack), len(qz)), dtype=numpy.complex)
330 fhkl = numpy.empty(len(self.lstack), dtype=numpy.complex)
331 for i, l in enumerate(self.lstack):
332 m = l.material
333 fhkl[i] = m.StructureFactor(m.Q(*hkl), en=self.energy) /\
334 m.lattice.UnitCellVolume()
335 f[i, :] = m.StructureFactorForQ(qv, en0=self.energy) /\
336 m.lattice.UnitCellVolume()
337
338 E = numpy.zeros(len(qz), dtype=numpy.complex)
339 return rel, alphai, alphaf, f, fhkl, E, t
340
341 def _get_qz(self, qz, alphai, alphaf, chi0, absorption, refraction):
342 k = self.exp.k0
343 q = qz.astype(numpy.complex)
344 if absorption and not refraction:
345 q += 1j * k * numpy.imag(chi0) / \
346 numpy.sin((alphai + alphaf) / 2)
347 if refraction:
348 q = k * (numpy.sqrt(numpy.sin(alphai)**2 + chi0) +
349 numpy.sqrt(numpy.sin(alphaf)**2 + chi0))
350 return q
351
352 def simulate(self, qz, hkl, absorption=False, refraction=False,
353 rettype='intensity'):
354 """
355 performs the actual kinematical diffraction calculation on the Qz
356 positions specified considering the contribution from a single Bragg
357 peak.
358
359 Parameters
360 ----------
361 qz : array-like
362 simulation positions along qz
363 hkl : list or tuple
364 Miller indices of the Bragg peak whos truncation rod should be
365 calculated
366 absorption : bool, optional
367 flag to tell if absorption correction should be used
368 refraction : bool, optional
369 flag to tell if basic refraction correction should be performed. If
370 refraction is True absorption correction is also included
371 independent of the absorption flag.
372 rettype : {'intensity', 'field', 'all'}
373 type of the return value. 'intensity' (default): returns the
374 diffracted beam flux convoluted with the resolution function;
375 'field': returns the electric field (complex) without convolution
376 with the resolution function, 'all': returns the electric field,
377 ai, af (both in degree), and the reflected intensity.
378
379 Returns
380 -------
381 array-like
382 return value depends on the setting of `rettype`, by default only
383 the calculate intensity is returned
384 """
385 self.init_chi0()
386 rel, ai, af, f, fhkl, E, t = self._prepare_kincalculation(qz, hkl)
387 # calculate interface positions
388 z = numpy.zeros(len(self.lstack))
389 for i, l in enumerate(self.lstack[-1:0:-1]):
390 z[-i-2] = z[-i-1] - l.thickness
391
392 # perform kinematical calculation
393 for i, l in enumerate(self.lstack):
394 q = self._get_qz(qz, ai, af, self.chi0[i], absorption, refraction)
395 q -= t(l.material.Q(*hkl))[-1]
396
397 if l.thickness == numpy.inf:
398 E += fhkl[i] * numpy.exp(-1j * z[i] * q) / (1j * q)
399 else:
400 E += - fhkl[i] * numpy.exp(-1j * q * z[i]) * \
401 (1 - numpy.exp(1j * q * l.thickness)) / (1j * q)
402
403 wf = numpy.sqrt(heaviside(ai) * heaviside(af) * rel**2 /
404 (numpy.sin(ai) * numpy.sin(af))) * E
405 return self._create_return(qz, wf, ai, af, rettype=rettype)
406
407
408 class KinematicalMultiBeamModel(KinematicalModel):
409 """
410 Kinematical diffraction model for specular and off-specular qz-scans. The
411 model calculates the kinematical contribution of several Bragg peaks on
412 the truncation rod and considers the variation of the structure factor.
413 In order to use a analytical description for the kinematic diffraction
414 signal all layer thicknesses are changed to a multiple of the respective
415 lattice parameter along qz. Therefore this description only works for (001)
416 surfaces.
417 """
418
419 def __init__(self, *args, **kwargs):
420 """
421 constructor for a kinematic thin film model. The arguments consist of a
422 LayerStack or individual Layer(s). Optional parameters are specified in
423 the keyword arguments.
424
425 Parameters
426 ----------
427 *args : LayerStack or Layers
428 either one LayerStack or several Layer objects can be given
429 **kwargs : dict
430 optional parameters. see also LayerModel/Model.
431 experiment : Experiment
432 Experiment class containing geometry and energy of the experiment.
433 surface_hkl : list or tuple
434 Miller indices of the surface (default: (0, 0, 1))
435 """
436 self.surface_hkl = kwargs.pop('surface_hkl', (0, 0, 1))
437 super().__init__(*args, **kwargs)
438
439 def simulate(self, qz, hkl, absorption=False, refraction=True,
440 rettype='intensity'):
441 """
442 performs the actual kinematical diffraction calculation on the Qz
443 positions specified considering the contribution from a full
444 truncation rod
445
446 Parameters
447 ----------
448 qz : array-like
449 simulation positions along qz
450 hkl : list or tuple
451 Miller indices of the Bragg peak whos truncation rod should be
452 calculated
453 absorption : bool, optional
454 flag to tell if absorption correction should be used
455 refraction : bool, optional,
456 flag to tell if basic refraction correction should be performed. If
457 refraction is True absorption correction is also included
458 independent of the absorption flag.
459 rettype : {'intensity', 'field', 'all'}
460 type of the return value. 'intensity' (default): returns the
461 diffracted beam flux convoluted with the resolution function;
462 'field': returns the electric field (complex) without convolution
463 with the resolution function, 'all': returns the electric field,
464 ai, af (both in degree), and the reflected intensity.
465
466 Returns
467 -------
468 array-like
469 return value depends on the setting of `rettype`, by default only
470 the calculate intensity is returned
471 """
472 self.init_chi0()
473 rel, ai, af, f, fhkl, E, t = self._prepare_kincalculation(qz, hkl)
474
475 # calculate interface positions for integer unit-cell thickness
476 z = numpy.zeros(len(self.lstack))
477 for i, l in enumerate(self.lstack[-1:0:-1]):
478 lat = l.material.lattice
479 a3 = t(lat.GetPoint(*self.surface_hkl))[-1]
480 n3 = l.thickness // a3
481 z[-i-2] = z[-i-1] - a3 * n3
482 if config.VERBOSITY >= config.INFO_LOW and \
483 numpy.abs(l.thickness/a3 - n3) > 0.01:
484 print('XU.KinematicMultiBeamModel: %s thickness changed from'
485 ' %.2fA to %.2fA (%d UCs)' % (l.name, l.thickness,
486 a3 * n3, n3))
487
488 # perform kinematical calculation
489 for i, l in enumerate(self.lstack):
490 q = self._get_qz(qz, ai, af, self.chi0[i], absorption, refraction)
491 lat = l.material.lattice
492 a3 = t(lat.GetPoint(*self.surface_hkl))[-1]
493
494 if l.thickness == numpy.inf:
495 E += f[i, :] * a3 * numpy.exp(-1j * z[i] * q) /\
496 (1 - numpy.exp(1j * q * a3))
497 else:
498 n3 = l.thickness // a3
499 E += f[i, :] * a3 * numpy.exp(-1j * z[i] * q) * \
500 (1 - numpy.exp(1j * q * a3 * n3)) /\
501 (1 - numpy.exp(1j * q * a3))
502
503 wf = numpy.sqrt(heaviside(ai) * heaviside(af) * rel**2 /
504 (numpy.sin(ai) * numpy.sin(af))) * E
505 return self._create_return(qz, wf, ai, af, rettype=rettype)
506
507
508 class SimpleDynamicalCoplanarModel(KinematicalModel):
509 """
510 Dynamical diffraction model for specular and off-specular qz-scans.
511 Calculation of the flux of reflected and diffracted waves for general
512 asymmetric coplanar diffraction from an arbitrary pseudomorphic multilayer
513 is performed by a simplified 2-beam theory (2 tiepoints, S and P
514 polarizations)
515
516 No restrictions are made for the surface orientation.
517
518 The first layer in the model is always assumed to be the semiinfinite
519 substrate indepentent of its given thickness
520
521 Note:
522 This model should not be used in real life scenarios since the made
523 approximations severely fail for distances far from the reference
524 position.
525 """
526
527 def __init__(self, *args, **kwargs):
528 """
529 constructor for a diffraction model. The arguments consist of a
530 LayerStack or individual Layer(s). Optional parameters are specified
531 in the keyword arguments.
532
533 Parameters
534 ----------
535 *args : LayerStack or Layers
536 either one LayerStack or several Layer objects can be given
537 **kwargs: dict
538 optional parameters for the simulation
539 I0 : float, optional
540 the primary beam intensity
541 background : float, optional
542 the background added to the simulation
543 resolution_width : float, optional
544 the width of the resolution (deg)
545 polarization: {'S', 'P', 'both'}
546 polarization of the x-ray beam. If set to 'both' also Cmono, the
547 polarization factor of the monochromator should be set
548 Cmono : float, optional
549 polarization factor of the monochromator
550 energy : float or str
551 the experimental energy in eV
552 experiment : Experiment
553 Experiment class containing geometry of the sample; surface
554 orientation!
555 """
556 if not hasattr(self, 'fit_paramnames'):
557 self.fit_paramnames = []
558 self.fit_paramnames += ['Cmono', ]
559 self.polarization = kwargs.pop('polarization', 'S')
560 self.Cmono = kwargs.pop('Cmono', 1)
561 super().__init__(*args, **kwargs)
562 self.xlabelstr = 'incidence angle (deg)'
563 self.hkl = None
564 self.chih = None
565 self.chimh = None
566
567 def set_hkl(self, *hkl):
568 """
569 To speed up future calculations of the same Bragg peak optical
570 parameters can be pre-calculated using this function.
571
572 Parameters
573 ----------
574 hkl : list or tuple
575 Miller indices of the Bragg peak for the calculation
576 """
577 if hkl != (None, ):
578 if len(hkl) < 3:
579 hkl = hkl[0]
580 if len(hkl) < 3:
581 raise InputError("need 3 Miller indices")
582 newhkl = numpy.asarray(hkl)
583 else:
584 newhkl = self.hkl
585
586 if self.energy != self._init_en or numpy.any(newhkl != self.hkl):
587 self.hkl = newhkl
588 self._init_en = self.energy
589
590 # calculate chih
591 self.chih = {'S': [], 'P': []}
592 self.chimh = {'S': [], 'P': []}
593 for l in self.lstack:
594 q = l.material.Q(self.hkl)
595 thetaB = numpy.arcsin(numpy.linalg.norm(q) / 2 / self.exp.k0)
596 ch = l.material.chih(q, en=self.energy, polarization='S')
597 self.chih['S'].append(-ch[0] + 1j*ch[1])
598 self.chih['P'].append((-ch[0] + 1j*ch[1]) *
599 numpy.abs(numpy.cos(2*thetaB)))
600 if not getattr(l, 'inversion_sym', False):
601 ch = l.material.chih(-q, en=self.energy, polarization='S')
602 self.chimh['S'].append(-ch[0] + 1j*ch[1])
603 self.chimh['P'].append((-ch[0] + 1j*ch[1]) *
604 numpy.abs(numpy.cos(2*thetaB)))
605
606 for pol in ('S', 'P'):
607 self.chih[pol] = numpy.asarray(self.chih[pol])
608 self.chimh[pol] = numpy.asarray(self.chimh[pol])
609
610 def _prepare_dyncalculation(self, geometry):
611 """
612 prepare dynamical calculation by calculating some helper values
613 """
614 t = self.exp._transform
615 ql0 = t(self.lstack[0].material.Q(*self.hkl))
616 hx = numpy.sqrt(ql0[0]**2 + ql0[1]**2)
617 if geometry == 'lo_hi':
618 hx = -hx
619
620 # calculate vertical diffraction vector components and strain
621 hz = numpy.zeros(len(self.lstack))
622 for i, l in enumerate(self.lstack):
623 hz[i] = t(l.material.Q(*self.hkl))[2]
624 return t, hx, hz
625
626 def simulate(self, alphai, hkl=None, geometry='hi_lo', idxref=1):
627 """
628 performs the actual diffraction calculation for the specified
629 incidence angles.
630
631 Parameters
632 ----------
633 alphai : array-like
634 vector of incidence angles (deg)
635 hkl : list or tuple, optional
636 Miller indices of the diffraction vector (preferable use set_hkl
637 method to speed up repeated calculations of the same peak!)
638 geometry : {'hi_lo', 'lo_hi'}, optional
639 'hi_lo' for grazing exit (default) and 'lo_hi' for grazing
640 incidence
641 idxref : int, optional
642 index of the reference layer. In order to get accurate peak
643 position of the film peak you want this to be the index of the film
644 peak (default: 1). For the substrate use 0.
645
646 Returns
647 -------
648 array-like
649 vector of intensities of the diffracted signal
650 """
651 self.set_hkl(hkl)
652
653 # return values
654 Ih = {'S': numpy.zeros(len(alphai)), 'P': numpy.zeros(len(alphai))}
655
656 t, hx, hz = self._prepare_dyncalculation(geometry)
657 epsilon = (hz[idxref] - hz) / hz
658
659 k = self.exp.k0
660 thetaB = numpy.arcsin(numpy.sqrt(hx**2 + hz[idxref]**2) / 2 / k)
661 # asymmetry angle
662 asym = numpy.arctan2(hx, hz[idxref])
663 gamma0 = numpy.sin(asym + thetaB)
664 gammah = numpy.sin(asym - thetaB)
665
666 # deviation of the incident beam from the kinematical maximum
667 eta = numpy.radians(alphai) - thetaB - asym
668
669 for pol in self.get_polarizations():
670 x = numpy.zeros(len(alphai), dtype=numpy.complex)
671 for i, l in enumerate(self.lstack):
672 beta = (2 * eta * numpy.sin(2 * thetaB) +
673 self.chi0[i] * (1 - gammah / gamma0) -
674 2 * gammah * (gamma0 - gammah) * epsilon[i])
675 y = beta / 2 / numpy.sqrt(self.chih[pol][i] *
676 self.chimh[pol][i]) /\
677 numpy.sqrt(numpy.abs(gammah) / gamma0)
678 c1 = -numpy.sqrt(self.chih[pol][i] / self.chih[pol][i] *
679 gamma0 / numpy.abs(gammah)) *\
680 (y + numpy.sqrt(y**2 - 1))
681 c2 = -numpy.sqrt(self.chih[pol][i] / self.chimh[pol][i] *
682 gamma0 / numpy.abs(gammah)) *\
683 (y - numpy.sqrt(y**2 - 1))
684 kz2mkz1 = k * numpy.sqrt(self.chih[pol][i] *
685 self.chimh[pol][i] / gamma0 /
686 numpy.abs(gammah)) *\
687 numpy.sqrt(y**2 - 1)
688 if i == 0: # substrate
689 pp = numpy.abs(gammah) / gamma0 * numpy.abs(c1)**2
690 m = pp < 1
691 x[m] = c1[m]
692 m = pp >= 1
693 x[m] = c2[m]
694 else: # layers
695 cphi = numpy.exp(1j * kz2mkz1 * l.thickness)
696 x = (c1 * c2 * (cphi - 1) + xs * (c1 - cphi * c2)) /\
697 (cphi * c1 - c2 + xs * (1 - cphi))
698 xs = x
699 Ih[pol] = numpy.abs(x)**2 * numpy.abs(gammah) / gamma0
700
701 ret = self.join_polarizations(Ih['S'], Ih['P'])
702 return self.scale_simulation(self.convolute_resolution(alphai, ret))
703
704
705 class DynamicalModel(SimpleDynamicalCoplanarModel):
706 """
707 Dynamical diffraction model for specular and off-specular qz-scans.
708 Calculation of the flux of reflected and diffracted waves for general
709 asymmetric coplanar diffraction from an arbitrary pseudomorphic multilayer
710 is performed by a generalized 2-beam theory (4 tiepoints, S and P
711 polarizations)
712
713 The first layer in the model is always assumed to be the semiinfinite
714 substrate indepentent of its given thickness
715 """
716
717 def simulate(self, alphai, hkl=None, geometry='hi_lo',
718 rettype='intensity'):
719 """
720 performs the actual diffraction calculation for the specified
721 incidence angles and uses an analytic solution for the quartic
722 dispersion equation
723
724 Parameters
725 ----------
726 alphai : array-like
727 vector of incidence angles (deg)
728 hkl : list or tuple, optional
729 Miller indices of the diffraction vector (preferable use set_hkl
730 method to speed up repeated calculations of the same peak!)
731 geometry : {'hi_lo', 'lo_hi'}, optional
732 'hi_lo' for grazing exit (default) and 'lo_hi' for grazing
733 incidence
734 rettype : {'intensity', 'field', 'all'}, optional
735 type of the return value. 'intensity' (default): returns the
736 diffracted beam flux convoluted with the resolution function;
737 'field': returns the electric field (complex) without convolution
738 with the resolution function, 'all': returns the electric field,
739 ai, af (both in degree), and the reflected intensity.
740
741 Returns
742 -------
743 array-like
744 vector of intensities of the diffracted signal, possibly changed
745 return value due the rettype setting!
746 """
747 if len(self.get_polarizations()) > 1 and rettype != "intensity":
748 raise ValueError('XU:DynamicalModel: return type (%s) not '
749 'supported with multiple polarizations!')
750 rettype = 'intensity'
751 self.set_hkl(hkl)
752
753 # return values
754 Ih = {'S': numpy.zeros(len(alphai)), 'P': numpy.zeros(len(alphai))}
755 Ir = {'S': numpy.zeros(len(alphai)), 'P': numpy.zeros(len(alphai))}
756
757 t, hx, hz = self._prepare_dyncalculation(geometry)
758
759 k = self.exp.k0
760 kc = k * numpy.sqrt(1 + self.chi0)
761 ai = numpy.radians(alphai)
762 Kix = k * numpy.cos(ai)
763 Kiz = -k * numpy.sin(ai)
764 Khz = numpy.sqrt(k**2 - (Kix + hx)**2)
765 pp = Khz / k
766 mask = numpy.logical_and(pp > 0, pp < 1)
767 ah = numpy.zeros(len(ai)) # exit angles
768 ah[mask] = numpy.arcsin(pp[mask])
769
770 nal = len(ai)
771 for pol in self.get_polarizations():
772 if pol == 'S':
773 CC = numpy.ones(nal)
774 else:
775 CC = abs(numpy.cos(ai+ah))
776 pom = k**4 * self.chih['S'] * self.chimh['S']
777 if config.VERBOSITY >= config.INFO_ALL:
778 print('XU.DynamicalModel: calc. %s-polarization...' % (pol))
779
780 M = numpy.zeros((nal, 4, 4), dtype=numpy.complex)
781 for j in range(4):
782 M[:, j, j] = numpy.ones(nal)
783
784 for i, l in enumerate(self.lstack[-1::-1]):
785 jL = len(self.lstack) - 1 - i
786 A4 = numpy.ones(nal)
787 A3 = 2 * hz[jL] * numpy.ones(nal)
788 A2 = (Kix + hx)**2 + hz[jL]**2 + Kix**2 - 2 * kc[jL]**2
789 A1 = 2 * hz[jL] * (Kix**2 - kc[jL]**2)
790 A0 = (Kix**2 - kc[jL]**2) *\
791 ((Kix + hx)**2 + hz[jL]**2 - kc[jL]**2) - pom[jL] * CC**2
792 X = solve_quartic(A4, A3, A2, A1, A0)
793 X = numpy.asarray(X).T
794
795 kz = numpy.zeros((nal, 4), dtype=numpy.complex)
796 kz[:, :2] = X[numpy.imag(X) <= 0].reshape(nal, 2)
797 kz[:, 2:] = X[numpy.imag(X) > 0].reshape(nal, 2)
798
799 P = numpy.zeros((nal, 4, 4), dtype=numpy.complex)
800 phi = numpy.zeros((nal, 4, 4), dtype=numpy.complex)
801 c = ((Kix**2)[:, numpy.newaxis] + kz**2 - kc[jL]**2) / k**2 /\
802 self.chimh['S'][jL] / CC[:, numpy.newaxis]
803 if jL > 0:
804 for j in range(4):
805 phi[:, j, j] = numpy.exp(1j * kz[:, j] * l.thickness)
806 else:
807 phi = numpy.tile(numpy.identity(4), (nal, 1, 1))
808 P[:, 0, :] = numpy.ones((nal, 4))
809 P[:, 1, :] = c
810 P[:, 2, :] = kz
811 P[:, 3, :] = c * (kz + hz[jL])
812
813 if i == 0:
814 R = numpy.copy(P)
815 else:
816 temp = numpy.linalg.inv(Ps)
817 try:
818 R = numpy.matmul(temp, P)
819 except AttributeError:
820 R = numpy.einsum('...ij,...jk', temp, P)
821 try:
822 M = numpy.matmul(numpy.matmul(M, R), phi)
823 except AttributeError:
824 M = numpy.einsum('...ij,...jk',
825 numpy.einsum('...ij,...jk', M, R), phi)
826 Ps = numpy.copy(P)
827
828 B = numpy.zeros((nal, 4, 4), dtype=numpy.complex)
829 B[..., :2] = M[..., :2]
830 B[:, 0, 2] = -numpy.ones(nal)
831 B[:, 1, 3] = -numpy.ones(nal)
832 B[:, 2, 2] = Kiz
833 B[:, 3, 3] = -Khz
834 C = numpy.zeros((nal, 4))
835 C[:, 0] = numpy.ones(nal)
836 C[:, 2] = Kiz
837
838 E = numpy.einsum('...ij,...j', numpy.linalg.inv(B), C)
839 Ir[pol] = numpy.abs(E[:, 2])**2 # reflected intensity
840 Ih[pol] = numpy.abs(E[:, 3])**2 * numpy.abs(Khz / Kiz) * mask
841
842 if len(self.get_polarizations()) > 1 and rettype == "intensity":
843 ret = numpy.sqrt(self.join_polarizations(Ih['S'], Ih['P']))
844 else:
845 ret = E[:, 3] * numpy.sqrt(numpy.abs(Khz / Kiz) * mask)
846
847 return self._create_return(alphai, ret, ai, ah, Ir, rettype=rettype)
848
849
850 class SpecularReflectivityModel(LayerModel):
851 """
852 model for specular reflectivity calculations
853 """
854
855 def __init__(self, *args, **kwargs):
856 """
857 constructor for a reflectivity model. The arguments consist of a
858 LayerStack or individual Layer(s). Optional parameters are specified
859 in the keyword arguments.
860
861 Parameters
862 ----------
863 args : LayerStack or Layers
864 either one LayerStack or several Layer objects can be given
865 kwargs: dict
866 optional parameters for the simulation; supported are:
867 I0 : float, optional
868 the primary beam intensity
869 background : float, optional
870 the background added to the simulation
871 sample_width : float, optional
872 width of the sample along the beam
873 beam_width : float, optional
874 beam width in the same units as the sample width
875 offset : float, optional
876 angular offset of the incidence angle (deg)
877 resolution_width : float, optional
878 width of the resolution (deg)
879 energy : float or str
880 x-ray energy in eV
881 """
882 if not hasattr(self, 'fit_paramnames'):
883 self.fit_paramnames = []
884 self.fit_paramnames += ['sample_width', 'beam_width', 'offset']
885 self.sample_width = kwargs.pop('sample_width', numpy.inf)
886 self.beam_width = kwargs.pop('beam_width', 0)
887 self.offset = kwargs.pop('offset', 0)
888 super().__init__(*args, **kwargs)
889 self.lstack_params += ['thickness', 'roughness', 'density']
890 self.xlabelstr = 'incidence angle (deg)'
891 # precalc optical properties
892 self._init_en = 0
893 self.init_cd()
894
895 def init_cd(self):
896 """
897 calculates the needed optical parameters for the simulation. If any of
898 the materials/layers is changing its properties this function needs to
899 be called again before another correct simulation is made. (Changes of
900 thickness and roughness do NOT require this!)
901 """
902 if self._init_en != self.energy:
903 self.cd = numpy.asarray([-l.material.chi0(en=self.energy)/2
904 for l in self.lstack])
905 self._init_en = self.energy
906
907 def simulate(self, alphai):
908 """
909 performs the actual reflectivity calculation for the specified
910 incidence angles
911
912 Parameters
913 ----------
914 alphai : array-like
915 vector of incidence angles
916
917 Returns
918 -------
919 array-like
920 vector of intensities of the reflectivity signal
921 """
922 self.init_cd()
923 ns, np = (len(self.lstack), len(alphai))
924 lai = alphai - self.offset
925 # get layer properties
926 t = numpy.asarray([l.thickness for l in self.lstack])
927 sig = numpy.asarray([l.roughness for l in self.lstack])
928 rho = numpy.asarray([l.density/l.material.density
929 for l in self.lstack])
930 cd = self.cd
931
932 sai = numpy.sin(numpy.radians(lai))
933
934 if self.beam_width > 0:
935 shape = self.sample_width * sai / self.beam_width
936 shape[shape > 1] = 1
937 else:
938 shape = numpy.ones(np)
939
940 ETs = numpy.ones(np, dtype=numpy.complex)
941 ERs = numpy.zeros(np, dtype=numpy.complex)
942 ks = -self.exp.k0 * numpy.sqrt(sai**2 - 2 * cd[0] * rho[0])
943
944 for i in range(ns):
945 if i < ns-1:
946 k = -self.exp.k0 * numpy.sqrt(sai**2 - 2 * cd[i+1] * rho[i+1])
947 phi = numpy.exp(1j * k * t[i+1])
948 else:
949 k = -self.exp.k0 * sai
950 phi = numpy.ones(np)
951 r = (k - ks) / (k + ks) * numpy.exp(-2 * sig[i]**2 * k * ks)
952 ET = phi * (ETs + r * ERs)
953 ER = (r * ETs + ERs) / phi
954 ETs = ET
955 ERs = ER
956 ks = k
957
958 R = shape * abs(ER / ET)**2
959 return self.scale_simulation(self.convolute_resolution(lai, R))
960
961 def densityprofile(self, nz, plot=False):
962 """
963 calculates the electron density of the layerstack from the thickness
964 and roughness of the individual layers
965
966 Parameters
967 ----------
968 nz : int
969 number of values on which the profile should be calculated
970 plot : bool, optional
971 flag to tell if a plot of the profile should be created
972
973 Returns
974 -------
975 z : array-like
976 z-coordinates, z = 0 corresponds to the surface
977 eprof : array-like
978 electron profile
979 """
980 if plot:
981 try:
982 from matplotlib import pyplot as plt
983 except ImportError:
984 plot = False
985 if config.VERBOSITY >= config.INFO_LOW:
986 print("XU.simpack: Warning: plot "
987 "functionality not available")
988
989 rel = constants.physical_constants['classical electron radius'][0]
990 rel *= 1e10
991 nl = len(self.lstack)
992
993 # get layer properties
994 t = numpy.asarray([l.thickness for l in self.lstack])
995 sig = numpy.asarray([l.roughness for l in self.lstack])
996 rho = numpy.asarray([l.density/l.material.density
997 for l in self.lstack])
998 delta = numpy.real(self.cd)
999
1000 totT = numpy.sum(t[1:])
1001 zmin = -totT - 10 * sig[0]
1002 zmax = 5 * sig[-1]
1003
1004 z = numpy.linspace(zmin, zmax, nz)
1005 pre_factor = 2 * numpy.pi / self.exp.wavelength**2 / rel * 1e24
1006
1007 # generate delta-rho values and interface positions
1008 zz = numpy.zeros(nl)
1009 dr = numpy.zeros(nl)
1010 dr[-1] = delta[-1] * rho[-1] * pre_factor
1011 for i in range(nl-1, 0, -1):
1012 zz[i-1] = zz[i] - t[i]
1013 dr[i-1] = delta[i-1] * rho[i-1] * pre_factor
1014
1015 # calculate profile from contribution of all interfaces
1016 prof = numpy.zeros(nz)
1017 w = numpy.zeros((nl, nz))
1018 for i in range(nl):
1019 s = (1 + erf((z - zz[i]) / sig[i] / numpy.sqrt(2))) / 2
1020 mask = s < (1 - 1e-10)
1021 w[i, mask] = s[mask] / (1 - s[mask])
1022 mask = numpy.logical_not(mask)
1023 w[i, mask] = 1e10
1024
1025 c = numpy.ones((nl, nz))
1026 for i in range(1, nl):
1027 c[i, :] = w[i-1, :] * c[i-1, :]
1028 c0 = w[-1, :] * c[-1, :]
1029 norm = numpy.sum(c, axis=0) + c0
1030
1031 for i in range(nl):
1032 c[i] /= norm
1033
1034 for j in range(nz):
1035 prof[j] = numpy.sum(dr * c[:, j])
1036
1037 if plot:
1038 plt.figure('XU:density_profile', figsize=(5, 3))
1039 plt.plot(z, prof, 'k-', lw=2, label='electron density')
1040 plt.xlabel(r'z ($\mathrm{\AA}$)')
1041 plt.ylabel(r'electron density (e$^-$ cm$^{-3}$)')
1042 plt.tight_layout()
1043
1044 return z, prof
1045
1046
1047 class DynamicalReflectivityModel(SpecularReflectivityModel):
1048 """
1049 model for Dynamical Specular Reflectivity Simulations.
1050 It uses the transfer Matrix methods as given in chapter 3
1051 "Daillant, J., & Gibaud, A. (2008). X-ray and Neutron Reflectivity"
1052 """
1053 def __init__(self, *args, **kwargs):
1054 """
1055 constructor for a reflectivity model. The arguments consist of a
1056 LayerStack or individual Layer(s). Optional parameters are specified
1057 in the keyword arguments.
1058
1059 Parameters
1060 ----------
1061 args : LayerStack or Layers
1062 either one LayerStack or several Layer objects can be given
1063 kwargs: dict
1064 optional parameters for the simulation; supported are:
1065 I0 : float, optional
1066 the primary beam intensity
1067 background : float, optional
1068 the background added to the simulation
1069 sample_width : float, optional
1070 width of the sample along the beam
1071 beam_width : float, optional
1072 beam width in the same units as the sample width
1073 resolution_width : float, optional
1074 width of the resolution (deg)
1075 energy : float or str
1076 x-ray energy in eV
1077 polarization: ['P', 'S']
1078 x-ray polarization
1079 """
1080 self.polarization = kwargs.pop('polarization', 'P')
1081 super().__init__(*args, **kwargs)
1082 self._init_en_opt = 0
1083 self._setOpticalConstants()
1084
1085 def _setOpticalConstants(self):
1086 if self._init_en_opt != self.energy:
1087 self.n_indices = numpy.asarray(
1088 [l.material.idx_refraction(en=self.energy)
1089 for l in self.lstack])
1090 # append n = 1 for vacuum
1091 self.n_indices = numpy.append(self.n_indices, 1)[::-1]
1092 self._init_en_opt = self.energy
1093
1094 def _getTransferMatrices(self, alphai):
1095 """
1096 Calculation of Refraction and Translation Matrices per angle per layer.
1097 """
1098 # Set heights for each layer
1099 heights = numpy.asarray([l.thickness for l in self.lstack[1:]])
1100 heights = numpy.cumsum(heights)[::-1]
1101 heights = numpy.insert(heights, 0, 0.) # first interface is at z=0
1102
1103 # set K-vector in each layer
1104 kz_angles = -self.exp.k0 * numpy.sqrt(numpy.asarray(
1105 [n**2 - numpy.cos(numpy.radians(alphai))**2
1106 for n in self.n_indices]).T)
1107
1108 # set Roughness for each layer
1109 roughness = numpy.asarray([l.roughness for l in self.lstack[1:]])[::-1]
1110 roughness = numpy.insert(roughness, 0, 0.) # first interface is at z=0
1111
1112 # Roughness is approximated by a Gaussian Statistics model modification
1113 # of the transfer matrix elements using Groce-Nevot factors (GNF).
1114
1115 GNF_factor_P = numpy.asarray(
1116 [[numpy.exp(-(kz_next - kz)**2 * (rough**2) / 2)
1117 for (kz, kz_next, rough) in zip(kz_1angle, kz_1angle[1:],
1118 roughness[1:])]
1119 for kz_1angle in kz_angles])
1120
1121 GNF_factor_M = numpy.asarray(
1122 [[numpy.exp(-(kz_next + kz) ** 2 * (rough ** 2) / 2)
1123 for (kz, kz_next, rough) in zip(kz_1angle, kz_1angle[1:],
1124 roughness[1:])]
1125 for kz_1angle in kz_angles])
1126
1127 if self.polarization == 'S':
1128 p_factor_angles = numpy.asarray(
1129 [[(kz + kz_next) / (2 * kz)
1130 for kz, kz_next in zip(kz_1angle, kz_1angle[1:])]
1131 for kz_1angle in kz_angles])
1132
1133 m_factor_angles = numpy.asarray(
1134 [[(kz - kz_next) / (2 * kz)
1135 for kz, kz_next in zip(kz_1angle, kz_1angle[1:])]
1136 for kz_1angle in kz_angles])
1137 else:
1138 p_factor_angles = numpy.asarray(
1139 [[(n_next**2*kz + n**2*kz_next) / (2*n_next**2*kz)
1140 for (kz, kz_next, n, n_next) in zip(kz_1angle, kz_1angle[1:],
1141 self.n_indices,
1142 self.n_indices[1:])]
1143 for kz_1angle in kz_angles])
1144 m_factor_angles = numpy.asarray(
1145 [[(n_next**2*kz - n**2*kz_next) / (2*n_next**2*kz)
1146 for (kz, kz_next, n, n_next) in zip(kz_1angle, kz_1angle[1:],
1147 self.n_indices,
1148 self.n_indices[1:])]
1149 for kz_1angle in kz_angles])
1150
1151 # Translation Matrices dim = (angle, layer, 2, 2)
1152 T_matrices = numpy.asarray(
1153 [[([numpy.exp(-1.j*kz*height), 0], [0, numpy.exp(1.j*kz*height)])
1154 for kz, height in zip(kz_1angle, heights)]
1155 for kz_1angle in kz_angles])
1156
1157 R_matrices = numpy.asarray(
1158 [[([p, m], [m, p]) for p, m in zip(P_fact, M_fact)]
1159 for (P_fact, M_fact) in zip(p_factor_angles, m_factor_angles)])
1160
1161 for R_mat, GNF_P, GNF_M in zip(R_matrices, GNF_factor_P, GNF_factor_M):
1162 R_mat[0, 0] = R_mat[0, 0] * GNF_P
1163 R_mat[0, 1] = R_mat[0, 1] * GNF_M
1164 R_mat[1, 0] = R_mat[1, 0] * GNF_M
1165 R_mat[1, 1] = R_mat[1, 1] * GNF_P
1166
1167 return T_matrices, R_matrices
1168
1169 def simulate(self, alphai):
1170 """
1171 Simulates the Dynamical Reflectivity as a function of angle of
1172 incidence
1173
1174 Parameters
1175 ----------
1176 alphai : array-like
1177 vector of incidence angles
1178
1179 Returns
1180 -------
1181 reflectivity: array-like
1182 vector of intensities of the reflectivity signal
1183 transmitivity: array-like
1184 vector of intensities of the transmitted signal
1185 """
1186 self._setOpticalConstants()
1187 lai = alphai - self.offset
1188 # Get Refraction and Translation Matrices for each angle of incidence
1189 if lai[0] < 1.e-5:
1190 lai[0] = 1.e-5 # cutoff
1191
1192 T_matrices, R_matrices = self._getTransferMatrices(lai)
1193
1194 # Calculate the Transfer Matrix
1195 M_angles = numpy.zeros((lai.size, 2, 2), dtype=numpy.complex128)
1196 for (angle, R), T in zip(enumerate(R_matrices), T_matrices):
1197 pairwiseRT = [numpy.dot(t, r) for r, t in zip(R, T)]
1198 M = numpy.identity(2, dtype=numpy.complex128)
1199 for pair in pairwiseRT:
1200 M = numpy.dot(M, pair)
1201 M_angles[angle] = M
1202
1203 # Reflectance and Transmittance
1204 R = numpy.array([numpy.abs((M[0, 1] / M[1, 1]))**2 for M in M_angles])
1205 T = numpy.array([numpy.abs((1. / M[1, 1]))**2 for M in M_angles])
1206
1207 return R, T
1208
1209 def scanEnergy(self, energies, angle):
1210 # TODO: this is quite inefficient, too many calls to internal functions
1211 # TODO: DO not return normalized refelctivity
1212 """
1213 Simulates the Dynamical Reflectivity as a function of photon energy at
1214 fixed angle.
1215
1216 Parameters
1217 ----------
1218 energies: np.ndarray or list
1219 photon energies (in eV).
1220 angle : float
1221 fixed incidence angle
1222
1223 Returns
1224 -------
1225 reflectivity: array-like
1226 vector of intensities of the reflectivity signal
1227 transmitivity: array-like
1228 vector of intensities of the transmitted signal
1229 """
1230 R_energies, T_energies = numpy.array([]), numpy.array([])
1231 for energy in energies:
1232 self.energy = energy
1233 self._setOpticalConstants()
1234 T_matrices, R_matrices = self._getTransferMatrices([angle, 0])
1235 T_matrix = T_matrices[0]
1236 R_matrix = R_matrices[0]
1237 pairwiseRT = [numpy.dot(t, r) for r, t in zip(R_matrix, T_matrix)]
1238 M = numpy.identity(2, dtype=numpy.complex128)
1239 for pair in pairwiseRT:
1240 M = numpy.dot(M, pair)
1241 R = numpy.abs(M[0, 1] / M[1, 1]) ** 2
1242 T = numpy.abs(1. / M[1, 1]) ** 2
1243 R_energies = numpy.append(R_energies, R)
1244 T_energies = numpy.append(T_energies, T)
1245 return R_energies, T_energies
1246
1247
1248 class ResonantReflectivityModel(SpecularReflectivityModel):
1249 """
1250 model for specular reflectivity calculations
1251 CURRENTLY UNDER DEVELOPEMENT! DO NOT USE!
1252 """
1253 def __init__(self, *args, **kwargs):
1254 """
1255 constructor for a reflectivity model. The arguments consist of a
1256 LayerStack or individual Layer(s). Optional parameters are specified
1257 in the keyword arguments.
1258
1259 Parameters
1260 ----------
1261 args : LayerStack or Layers
1262 either one LayerStack or several Layer objects can be given
1263 kwargs: dict
1264 optional parameters for the simulation; supported are:
1265 I0 : float, optional
1266 the primary beam intensity
1267 background : float, optional
1268 the background added to the simulation
1269 sample_width : float, optional
1270 width of the sample along the beam
1271 beam_width : float, optional
1272 beam width in the same units as the sample width
1273 resolution_width : float, optional
1274 width of the resolution (deg)
1275 energy : float or str
1276 x-ray energy in eV
1277 polarization: ['P', 'S']
1278 x-ray polarization
1279 """
1280 self.polarization = kwargs.pop('polarization', 'S')
1281 super().__init__(*args, **kwargs)
1282
1283 def simulate(self, alphai):
1284 """
1285 performs the actual reflectivity calculation for the specified
1286 incidence angles
1287
1288 Parameters
1289 ----------
1290 alphai : array-like
1291 vector of incidence angles
1292
1293 Returns
1294 -------
1295 array-like
1296 vector of intensities of the reflectivity signal
1297 """
1298 self.init_cd()
1299 ns, np = (len(self.lstack), len(alphai))
1300 lai = alphai - self.offset
1301
1302 # get layer properties
1303 t = numpy.asarray([l.thickness for l in self.lstack])
1304 sig = numpy.asarray([l.roughness for l in self.lstack])
1305 rho = numpy.asarray([l.density/l.material.density
1306 for l in self.lstack])
1307 cd = self.cd
1308 qzvec = 4 * numpy.pi * numpy.sin(numpy.radians(lai)) /\
1309 utilities.en2lam(self.energy)
1310 qvec = numpy.array([[0., 0., qz] for qz in qzvec])
1311 chihP = numpy.array([[l.material.chih(q, en=self.energy,
1312 polarization=self.polarization)
1313 for q in qvec]
1314 for l in self.lstack])
1315
1316 if self.polarization in ['S', 'P']:
1317 cd = cd + chihP
1318 else:
1319 cd = cd
1320
1321 sai = numpy.sin(numpy.radians(lai))
1322
1323 if self.beam_width > 0:
1324 shape = self.sample_width * sai / self.beam_width
1325 shape[shape > 1] = 1
1326 else:
1327 shape = numpy.ones(np)
1328
1329 ETs = numpy.ones(np, dtype=numpy.complex)
1330 ERs = numpy.zeros(np, dtype=numpy.complex)
1331 ks = -self.exp.k0 * numpy.sqrt(sai**2 - 2 * cd[0] * rho[0])
1332
1333 for i in range(ns):
1334 if i < ns-1:
1335 k = -self.exp.k0 * numpy.sqrt(sai**2 - 2 * cd[i+1] * rho[i+1])
1336 phi = numpy.exp(1j * k * t[i+1])
1337 else:
1338 k = -self.exp.k0 * sai
1339 phi = numpy.ones(np)
1340 r = (k - ks) / (k + ks) * numpy.exp(-2 * sig[i]**2 * k * ks)
1341 ET = phi * (ETs + r * ERs)
1342 ER = (r * ETs + ERs) / phi
1343 ETs = ET
1344 ERs = ER
1345 ks = k
1346
1347 R = shape * abs(ER / ET)**2
1348 return self.scale_simulation(self.convolute_resolution(lai, R))
1349
1350
1351 class DiffuseReflectivityModel(SpecularReflectivityModel):
1352 """
1353 model for diffuse reflectivity calculations
1354
1355 The 'simulate' method calculates the diffuse reflectivity on the specular
1356 rod in coplanar geometry in analogy to the SpecularReflectivityModel.
1357
1358 The 'simulate_map' method calculates the diffuse reflectivity for a 2D set
1359 of Q-positions. This method can also calculate the intensity for other
1360 geometries, like GISAXS with constant incidence angle or a quasi
1361 omega/2theta scan in GISAXS geometry.
1362 """
1363
1364 def __init__(self, *args, **kwargs):
1365 """
1366 constructor for a reflectivity model. The arguments consist of a
1367 LayerStack or individual Layer(s). Optional parameters are specified
1368 in the keyword arguments.
1369
1370 Parameters
1371 ----------
1372 args : LayerStack or Layers
1373 either one LayerStack or several Layer objects can be given
1374 kwargs : dict
1375 optional parameters for the simulation; supported are:
1376 I0 : float, optional
1377 the primary beam intensity
1378 background : float, optional
1379 the background added to the simulation
1380 sample_width : float, optional
1381 width of the sample along the beam
1382 beam_width : float, optional
1383 beam width in the same units as the sample width
1384 resolution_width : float, optional
1385 defines the width of the resolution (deg)
1386 energy : float, optional
1387 sets the experimental energy (eV)
1388 H : float, optional
1389 Hurst factor defining the fractal dimension of the roughness (0..1,
1390 very slow for H != 1 or H != 0.5), default: 1
1391 vert_correl : float, optional
1392 vertical correlation length in (Angstrom), 0 means full replication
1393 vert_nu : float, optional
1394 exponent in the vertical correlation function
1395 method : int, optional
1396 1..simple DWBA (default), 2..full DWBA (slower)
1397 vert_int : int, optional
1398 0..no integration over the vertical divergence, 1..with integration
1399 over the vertical divergence
1400 qL_zero : float, optional
1401 value of inplane q-coordinate which can be considered 0, using
1402 method 2 it is important to avoid exact 0 and this value will be
1403 used instead
1404 """
1405 if not hasattr(self, 'fit_paramnames'):
1406 self.fit_paramnames = []
1407 self.fit_paramnames += ['H', 'vert_correl', 'vert_nu']
1408 self.H = kwargs.pop('H', 1)
1409 self.vert_correl = kwargs.pop('vert_correl', 0)
1410 self.vert_nu = kwargs.pop('vert_nu', 0)
1411 self.method = kwargs.pop('method', 1)
1412 self.vert = kwargs.pop('vert_int', 0)
1413 self.qL_zero = kwargs.pop('qL_zero', 5e-5)
1414 super().__init__(*args, **kwargs)
1415 self.lstack_params += ['lat_correl', ]
1416
1417 def _get_layer_prop(self):
1418 """
1419 helper function to obtain layer properties needed for all types of
1420 simulations
1421 """
1422 nl = len(self.lstack)
1423 self.init_cd()
1424
1425 t = numpy.asarray([float(l.thickness) for l in self.lstack[nl:0:-1]])
1426 sig = [float(l.roughness) for l in self.lstack[nl::-1]]
1427 rho = [l.density/l.material.density for l in self.lstack[nl::-1]]
1428 delta = self.cd * numpy.asarray(rho)
1429 xiL = [float(l.lat_correl) for l in self.lstack[nl::-1]]
1430
1431 return t, sig, rho, delta, xiL
1432
1433 def simulate(self, alphai):
1434 """
1435 performs the actual diffuse reflectivity calculation for the specified
1436 incidence angles. This method always uses the coplanar geometry
1437 independent of the one set during the initialization.
1438
1439 Parameters
1440 ----------
1441 alphai : array-like
1442 vector of incidence angles
1443
1444 Returns
1445 -------
1446 array-like
1447 vector of intensities of the reflectivity signal
1448 """
1449 lai = alphai - self.offset
1450 # get layer properties
1451 t, sig, rho, delta, xiL = self._get_layer_prop()
1452
1453 deltaA = numpy.sum(delta[:-1]*t)/numpy.sum(t)
1454 lam = utilities.en2lam(self.energy)
1455 if self.method == 2:
1456 qL = [-abs(self.qL_zero), abs(self.qL_zero)]
1457 else:
1458 qL = [0, ]
1459 qz = 4 * numpy.pi / lam * numpy.sin(numpy.radians(lai))
1460 R = self._xrrdiffv2(lam, delta, t, sig, xiL, self.H, self.vert_correl,
1461 self.vert_nu, None, qL, qz, self.sample_width,
1462 self.beam_width, 1e-4, 1000, deltaA, self.method,
1463 1, self.vert)
1464 R = R.mean(axis=0)
1465 return self.scale_simulation(self.convolute_resolution(lai, R))
1466
1467 def simulate_map(self, qL, qz):
1468 """
1469 performs diffuse reflectivity calculation for the rectangular grid of
1470 reciprocal space positions define by qL and qz. This method uses the
1471 method and geometry set during the initialization of the class.
1472
1473 Parameters
1474 ----------
1475 qL : array-like
1476 lateral coordinate in reciprocal space (vector with NqL components)
1477 qz : array-like
1478 vertical coordinate in reciprocal space (vector with Nqz
1479 components)
1480
1481 Returns
1482 -------
1483 array-like
1484 matrix of intensities of the reflectivity signal, with shape
1485 (len(qL), len(qz))
1486 """
1487 # get layer properties
1488 t, sig, rho, delta, xiL = self._get_layer_prop()
1489
1490 deltaA = numpy.sum(delta[:-1]*t)/numpy.sum(t)
1491 lam = utilities.en2lam(self.energy)
1492 localqL = numpy.copy(qL)
1493 if self.method == 2:
1494 localqL[qL == 0] = self.qL_zero
1495
1496 R = self._xrrdiffv2(lam, delta, t, sig, xiL, self.H, self.vert_correl,
1497 self.vert_nu, None, localqL, qz, self.sample_width,
1498 self.beam_width, 1e-4, 1000, deltaA, self.method,
1499 1, self.vert)
1500 return self.scale_simulation(R)
1501
1502 def _xrrdiffv2(self, lam, delta, thick, sigma, xiL, H, xiV, nu, alphai, qL,
1503 qz, samplewidth, beamwidth, eps, nmax, deltaA, method, scan,
1504 vert):
1505 """
1506 simulation of diffuse reflectivity from a rough multilayer. Exact or
1507 simplified DWBA, fractal roughness model, Ming model of the vertical
1508 correlation, various scattering geometries
1509
1510 The used incidence and exit angles are stored in _smap_alphai,
1511 _smap_alphaf
1512
1513 Parameters
1514 ----------
1515 lam : float
1516 x-ray wavelength in Angstrom
1517 delta : list or array-like
1518 vector with the 1-n values (N+1 components, 1st component..layer at
1519 the free surface, last component..substrate)
1520 thick : list or array-like
1521 vector with thicknesses (N components)
1522 sigma : list or array-like
1523 vector with rms roughnesses (N+1 components)
1524 xiL : list or array-like
1525 vector with lateral correlation lengths (N+1 components)
1526 H : float
1527 Hurst factor (scalar)
1528 xiV : float
1529 vertical correlation: 0..full replication, > 0 and nu > 0.. see
1530 below, > 0 and nu = 0..vertical correlation length
1531 nu : float
1532 exponent in the vertical correlation function
1533 exp(-abs(z_m-z_n)*(qL/max(qL))**nu/xiV)
1534 alphai : float
1535 incidence angle (scalar for scan=2, ignored for scan=1, 3)
1536 qL : array-like
1537 lateral coordinate in reciprocal space (vector with NqL components)
1538 qz : array-like
1539 vertical coordinate in reciprocal space (vector with Nqz
1540 components)
1541 samplewidth : float
1542 width of the irradiated sample area (scalar), =0..the irradiated
1543 are is assumed constant
1544 beamwidth : float
1545 width of the primary beam
1546 eps : float
1547 small number
1548 nmax : int
1549 max number of terms in the Taylor series of the lateral correlation
1550 function
1551 deltaA : complex
1552 effective value of 1-n in simple DWBA (ignored for method=2)
1553 method : int
1554 1..simple DWBA, 2..full DWBA
1555 scan : int
1556 1..standard coplanar geometry, 2..standard GISAXS geometry with
1557 constant incidence angle, =3..quasi omega/2theta scan in GISAXS
1558 geometry (incidence and central-exit angles are equal)
1559 vert : int
1560 0..no integration over the vertical divergence, 1..with integration
1561 over the vertical divergence
1562
1563 Returns
1564 -------
1565 diffint : array-like
1566 diffuse reflectivity intensity matrix
1567 """
1568 # worker function definitions
1569 def coherent(alphai, K, delta, thick, N, NqL, Nqz):
1570 """
1571 calculate coherent reflection/transmission signal of a multilayer
1572
1573 Parameters
1574 ----------
1575 alphai : array-like
1576 matrix of incidence angles in radians (NqL x Nqz components)
1577 K : float
1578 x-ray wave-vector (2*pi/lambda)
1579 delta : array-like
1580 vector with the 1-n values (N+1 components, 1st
1581 component..layer at the free surface, last
1582 component..substrate)
1583 thick : array-like
1584 vector with thicknesses (N components)
1585 N : int
1586 number layers in the stack
1587 NqL : int
1588 number of lateral q-points to calculate
1589 Nqz : int
1590 number of vertical q-points to calculate
1591
1592 Returns
1593 -------
1594 T, R, R0, k0, kz : array-like
1595 transmission, reflection, surface reflection, z-component of
1596 k-vector, z-component of k-vector in the material.
1597 """
1598 k0 = -K * numpy.sin(alphai)
1599 kz = numpy.zeros((N+1, NqL, Nqz), dtype=numpy.complex)
1600 T = numpy.zeros((N+1, NqL, Nqz), dtype=numpy.complex)
1601 R = numpy.zeros((N+1, NqL, Nqz), dtype=numpy.complex)
1602 for jn in range(N+1):
1603 kz[jn, ...] = -K * numpy.sqrt(numpy.sin(alphai)**2 -
1604 2 * delta[jn])
1605
1606 T[N, ...] = numpy.ones((NqL, Nqz), dtype=numpy.complex)
1607 kzs = kz[N, ...] # kz in substrate
1608 for jn in range(N-1, -1, -1):
1609 kzn = kz[jn, ...]
1610 tF = 2 * kzn / (kzn + kzs)
1611 rF = (kzn - kzs) / (kzn + kzs)
1612 phi = numpy.exp(1j * kzn * thick[jn])
1613 T[jn, ...] = phi / tF * (T[jn+1, ...] + rF * R[jn+1, ...])
1614 R[jn, ...] = 1 / phi / tF * (rF * T[jn+1, ...] + R[jn+1, ...])
1615 kzs = numpy.copy(kzn)
1616
1617 tF = 2 * k0 / (k0 + kzn)
1618 rF = (k0 - kzn) / (k0 + kzn)
1619 T0 = 1 / tF * (T[0, ...] + rF * R[0, ...])
1620 R0 = 1 / tF * (rF * T[0, ...] + R[0, ...])
1621
1622 T /= T0
1623 R /= T0
1624 R0 /= T0
1625
1626 return T, R, R0, k0, kz
1627
1628 def correl(a, b, L, H, eps, nmax, vert, K, NqL, Nqz, isurf):
1629 """
1630 correlation function
1631
1632 Parameters
1633 ----------
1634 a : array-like
1635 lateral correlation parameter
1636 b : array-like
1637 vertical correlation parameter
1638 L : float
1639 lateral correlation length
1640 H : float
1641 Hurst factor (scalar)
1642 eps : float
1643 small number (decides integration cut-off), typical 1e-3
1644 nmax : int
1645 max number of terms in the Taylor series of the lateral
1646 correlation function
1647 vert : int
1648 flag to tell decide if integration over vertical divergence is
1649 used: 0..no integration, 1..with integration
1650 K : float
1651 length of the x-ray wave-vector (2*pi/lambda)
1652 NqL : int
1653 number of lateral q-points to calculate
1654 Nqz : int
1655 number of vertical q-points to calculate
1656 isurf : array-like
1657 array with NqL, Nqz flags to tell if there is a positive
1658 incidence and exit angle
1659
1660 Returns
1661 -------
1662 psi : array-like
1663 correlation function
1664 """
1665 psi = numpy.zeros((NqL, Nqz), dtype=numpy.complex)
1666 if H == 0.5 or H == 1:
1667 dpsi = numpy.zeros_like(psi, dtype=numpy.complex)
1668 m = isurf > 0
1669 n = 1
1670 s = numpy.copy(b)
1671 errm = numpy.inf
1672 if H == 1 and vert == 0:
1673 def f(a, n):
1674 return numpy.exp(-a**2/4/n) / 2 / n**2
1675 elif H == 0.5 and vert == 0:
1676 def f(a, n):
1677 return 1. / (n**2 + a**2)**(3/2.)
1678 elif H == 1 and vert == 1:
1679 def f(a, n):
1680 return numpy.sqrt(numpy.pi/n**3) * numpy.exp(-a**2/4/n)
1681 elif H == 0.5 and vert == 1:
1682 def f(a, n):
1683 return 2. / (n**2 + a**2)
1684
1685 while errm > eps and n < nmax:
1686 dpsi[m] = s[m] * f(a[m], n)
1687 if n > 1:
1688 errm = abs(numpy.max(dpsi[m] / psi[m]))
1689 psi[m] += dpsi[m]
1690 s[m] *= b[m]/float(n)
1691 n += 1
1692 else:
1693 if vert == 0:
1694 kern = kernel
1695 else:
1696 kern = kernelvert
1697 for jL in range(NqL):
1698 for jz in range(Nqz):
1699 if isurf[jL, jz] == 1:
1700 xmax = (-numpy.log(eps / b[jL, jz]))**(1/(2*H))
1701 psi[jL, jz] = cquad(kern, 0.0, numpy.real(xmax),
1702 epsrel=eps, epsabs=0,
1703 limit=nmax, args=(a[jL, jz],
1704 b[jL, jz], H))
1705
1706 if vert == 0:
1707 psi *= 2 * numpy.pi * L ** 2
1708 else:
1709 psi *= 2 * numpy.pi * L / K
1710 return psi
1711
1712 def kernelvert(x, a, b, H):
1713 """
1714 integration kernel with vertical integration
1715
1716 Parameters
1717 ----------
1718 x : float or array-like
1719 independent parameter of the function
1720 a : float
1721 lateral correlation parameter
1722 b : complex
1723 vertical correlation parameter
1724 H : float
1725 Hurst factor (scalar)
1726
1727 Returns
1728 -------
1729 float or arraylike
1730 """
1731 w = numpy.exp(b * numpy.exp(-x**(2*H))) - 1
1732 F = 2 * numpy.cos(a*x) * w
1733 return F
1734
1735 def kernel(x, a, b, H):
1736 """
1737 integration kernel without vertical integration
1738
1739 Parameters
1740 ----------
1741 x : float or array-like
1742 independent parameter of the function
1743 a : float
1744 lateral correlation parameter
1745 b : complex
1746 vertical correlation parameter
1747 H : float
1748 Hurst factor (scalar)
1749
1750 Returns
1751 -------
1752 float or arraylike
1753 """
1754 w = numpy.exp(b * numpy.exp(-x**(2*H))) - 1
1755 F = x * j0(a*x) * w
1756 return F
1757
1758 def cquad(func, a, b, **kwargs):
1759 """
1760 complex quadrature by spliting real and imaginary part using scipy
1761 """
1762 def real_func(*args):
1763 return numpy.real(func(*args))
1764
1765 def imag_func(*args):
1766 return numpy.imag(func(*args))
1767 real_integral = integrate.quad(real_func, a, b, **kwargs)
1768 imag_integral = integrate.quad(imag_func, a, b, **kwargs)
1769 return (real_integral[0] + 1j*imag_integral[0])
1770
1771 # begin of _xrrdiffv2
1772 K = 2 * numpy.pi / lam
1773
1774 N = len(thick)
1775 NqL = len(qL)
1776 Nqz = len(qz)
1777
1778 QZ, QL = numpy.meshgrid(qz, qL)
1779
1780 # scan types:
1781 if scan == 1: # coplanar geometry
1782 Q = numpy.sqrt(QL**2 + QZ**2)
1783 QP = numpy.abs(QL)
1784 th = numpy.arcsin(Q / 2 / K)
1785 om = numpy.arctan2(QL, QZ)
1786 ALPHAI = th + om
1787 ALPHAF = th - om
1788 elif scan == 2: # GISAXS geometry with constant incidence angle
1789 ALPHAI = numpy.radians(alphai) * numpy.ones((NqL, Nqz))
1790 ALPHAF = numpy.arcsin(QZ / K - numpy.sin(numpy.radians(alphai)))
1791 PHI = numpy.arcsin(QL / K / numpy.cos(ALPHAF))
1792 QP = K * numpy.sqrt(numpy.cos(ALPHAF)**2 + numpy.cos(ALPHAI)**2 -
1793 2*numpy.cos(ALPHAF) * numpy.cos(ALPHAI) *
1794 numpy.cos(PHI))
1795 elif scan == 3: # with quasi omega/2theta scan in GISAXS geometry
1796 ALPHAI = numpy.arcsin(QZ * (K - numpy.sqrt(K**2 - QL**2)) / QL**2)
1797 ALPHAF = numpy.arcsin(QZ / K - numpy.sin(ALPHAI))
1798 PHI = numpy.arcsin(QL / K / numpy.cos(ALPHAF))
1799 QP = K * numpy.sqrt(numpy.cos(ALPHAF)**2 + numpy.cos(ALPHAI)**2 -
1800 2*numpy.cos(ALPHAF) * numpy.cos(ALPHAI) *
1801 numpy.cos(PHI))
1802 else:
1803 raise ValueError("Invalid value of parameter 'scan'")
1804
1805 # removing the values under the horizon
1806 isurf = heaviside(ALPHAI) * heaviside(ALPHAF)
1807
1808 # non-disturbed states:
1809 if method == 1:
1810 k01 = -K * numpy.sin(ALPHAI)
1811 kz1 = -K * numpy.sqrt(numpy.sin(ALPHAI)**2 - 2*deltaA)
1812 k02 = -K * numpy.sin(ALPHAF)
1813 kz2 = -K * numpy.sqrt(numpy.sin(ALPHAF)**2 - 2*deltaA)
1814 T1 = 2 * k01 / (k01 + kz1)
1815 T2 = 2 * k02 / (k02 + kz2)
1816 R01 = (k01 - kz1) / (k01 + kz1)
1817 R02 = (k02 - kz2) / (k02+kz2)
1818 R1 = numpy.zeros((NqL, Nqz), dtype=numpy.complex)
1819 R2 = numpy.copy(R1)
1820 nproc = 1
1821 else: # method == 2
1822 T1, R1, R01, k01, kz1 = coherent(ALPHAI, K, delta, thick, N,
1823 NqL, Nqz)
1824 T2, R2, R02, k02, kz2 = coherent(ALPHAF, K, delta, thick, N,
1825 NqL, Nqz)
1826 nproc = 4
1827
1828 # sample surface
1829 if beamwidth > 0:
1830 S = samplewidth * numpy.sin(ALPHAI) / beamwidth
1831 S[S > 1] = 1
1832 else:
1833 S = 1
1834
1835 # z-coordinates
1836 z = numpy.zeros(N+1)
1837 for jn in range(1, N+1):
1838 z[jn] = z[jn-1] - thick[jn-1]
1839
1840 # calculation of the deltas
1841 delt = numpy.zeros(N+1, dtype=numpy.complex)
1842 for jn in range(N+1):
1843 if jn == 0:
1844 delt[jn] = delta[jn]
1845 if jn > 0:
1846 delt[jn] = delta[jn] - delta[jn-1]
1847
1848 # double sum over interfaces
1849 result = numpy.zeros((NqL, Nqz))
1850 for jn in range(N+1):
1851 # if method == 1 and (H == 1 or H == 0.5):
1852 # print(jn)
1853 if nu != 0 or xiV == 0:
1854 jmdol = 1
1855 else:
1856 jmdol = numpy.argmin(numpy.abs(z - (z[jn] -
1857 xiV * numpy.log(eps))))
1858
1859 for ja in range(nproc):
1860 if method == 1:
1861 Qn = -kz1 - kz2
1862 An = T1 * T2 * numpy.exp(-1j*Qn*z[jn])
1863 else: # method == 2
1864 if ja == 0:
1865 An = T1[jn, ...] * T2[jn, ...]
1866 Qn = -kz1[jn, ...] - kz2[jn, ...]
1867 elif ja == 1:
1868 An = T1[jn, ...] * R2[jn, ...]
1869 Qn = -kz1[jn, ...] + kz2[jn, ...]
1870 elif ja == 2:
1871 An = R1[jn, ...] * T2[jn, ...]
1872 Qn = kz1[jn, ...] - kz2[jn, ...]
1873 elif ja == 3:
1874 An = R1[jn, ...] * R2[jn, ...]
1875 Qn = kz1[jn, ...] + kz2[jn, ...]
1876 for jm in range(jmdol, jn+1):
1877 if jm == jn:
1878 weight = 1
1879 else:
1880 weight = 2
1881 # if method == 1 and (H != 0.5 and H != 1) and ja==1:
1882 # print(jn, jm)
1883 # vertical correlation function:
1884 if xiV > 0:
1885 CV = numpy.exp(-abs(z[jn] - z[jm]) *
1886 (QP/numpy.max(QP))**nu / xiV)
1887 else:
1888 CV = 1
1889 # effective values of sigma and lateral correl. length:
1890 try:
1891 LP = ((float(xiL[jn])**(-2*H) +
1892 float(xiL[jm])**(-2*H)) / 2) ** (-1 / 2 / H)
1893 except ZeroDivisionError:
1894 LP = 0
1895 sig = pymath.sqrt(sigma[jn] * sigma[jm])
1896 for jb in range(nproc):
1897 if method == 1:
1898 Qm = -kz1 - kz2
1899 Am = T1 * T2 * numpy.exp(-1j * Qm * z[jm])
1900 else: # method == 2
1901 # if H != 0.5 or H != 1:
1902 # print(ja, jb, jn, jm)
1903 if jb == 0:
1904 Am = T1[jm, ...] * T2[jm, ...]
1905 Qm = -kz1[jm, ...] - kz2[jm, ...]
1906 elif jb == 1:
1907 Am = T1[jm, ...] * R2[jm, ...]
1908 Qm = -kz1[jm, ...] + kz2[jm, ...]
1909 elif jb == 2:
1910 Am = R1[jm, ...] * T2[jm, ...]
1911 Qm = kz1[jm, ...] - kz2[jm, ...]
1912 elif jb == 3:
1913 Am = R1[jm, ...] * R2[jm, ...]
1914 Qm = +kz1[jm, ...] + kz2[jm, ...]
1915 # lateral correlation function:
1916 Psi = correl(QP*LP, Qn*numpy.conj(Qm)*sig**2, LP, H,
1917 eps, nmax, vert, K, NqL, Nqz, isurf)
1918 result += numpy.real(CV * delt[jn] *
1919 numpy.exp(-Qn**2 *
1920 sigma[jn]**2/2) /
1921 Qn * An *
1922 numpy.conj(delt[jm] *
1923 numpy.exp(-Qm**2*sigma[jm]**2/2) /
1924 Qm * Am) * Psi) * weight
1925
1926 result[isurf == 0] = 0
1927 self._smap_R01 = R01 * isurf
1928 self._smap_R02 = R02 * isurf
1929 self._smap_alphai = numpy.degrees(ALPHAI*isurf)
1930 self._smap_alphaf = numpy.degrees(ALPHAF*isurf)
1931 return result * S * K**4 / (16*numpy.pi**2)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import math
18
19 import numpy
20
21
22 def mosaic_analytic(qx, qz, RL, RV, Delta, hx, hz, shape):
23 """
24 simulation of the coplanar reciprocal space map of a single mosaic layer
25 using a simple analytic approximation
26
27 Parameters
28 ----------
29 qx : array-like
30 vector of the qx values (offset from the Bragg peak)
31 qz : array-like
32 vector of the qz values (offset from the Bragg peak)
33 RL : float
34 lateral block radius in Angstrom
35 RV : float
36 vertical block radius in Angstrom
37 Delta : float
38 root mean square misorientation of the grains in degree
39 hx : float
40 lateral component of the diffraction vector
41 hz : float
42 vertical component of the diffraction vector
43 shape: float
44 shape factor (1..Gaussian)
45
46 Returns
47 -------
48 array-like
49 2D array with calculated intensities
50 """
51 QX, QZ = numpy.meshgrid(qx, qz)
52 QX = QX.T
53 QZ = QZ.T
54 DD = numpy.radians(Delta)
55 tmp = 6 + DD**2 * ((hz*RL)**2 + (hx*RV)**2)
56 F = ((DD*RL*RV)**2*(QZ*hz + QX*hx)**2+6*((RL*QX)**2+(RV*QZ)**2)) / 4 / tmp
57 return math.pi * math.sqrt(6) * RL * RV /\
58 math.sqrt(tmp) * numpy.exp(-F**shape)
0 # This file is part of xrayutilities.
1 #
2 # xrayutilies is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License and the additonal notes below for
11 # more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, see <http://www.gnu.org/licenses/>.
15 #
16 # Copyright (C) 2016-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
17 # Copyright (C) 2015-2017 Marcus H. Mendenhall <marcus.mendenhall@nist.gov>
18
19 # FP_profile was derived from http://dx.doi.org/10.6028/jres.120.014.c
20
21 # Original copyright notice:
22 # @author Marcus H. Mendenhall (marcus.mendenhall@nist.gov)
23 # @date March, 2015
24 # The "Fundamental Parameters Python Code" ("software") is provided by the
25 # National Institute of Standards and Technology (NIST), an agency of the
26 # United States Department of Commerce, as a public service. This software is
27 # to be used for non-commercial research purposes only and is expressly
28 # provided "AS IS." Use of this software is subject to your acceptance of these
29 # terms and conditions.
30 #
31 # NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED, IN FACT OR ARISING BY
32 # OPERATION OF LAW, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
33 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA
34 # ACCURACY. NIST NEITHER REPRESENTS NOR WARRANTS THAT THE OPERATION OF THE
35 # SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT ANY DEFECTS WILL BE
36 # CORRECTED. NIST DOES NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE
37 # USE OF THE SOFTWARE OR THE RESULTS THEREOF, INCLUDING BUT NOT LIMITED TO THE
38 # CORRECTNESS, ACCURACY, RELIABILITY, OR USEFULNESS OF THE SOFTWARE.
39 #
40 # NIST SHALL NOT BE LIABLE AND YOU HEREBY RELEASE NIST FROM LIABILITY FOR ANY
41 # INDIRECT, CONSEQUENTIAL, SPECIAL, OR INCIDENTAL DAMAGES (INCLUDING DAMAGES
42 # FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS
43 # INFORMATION, AND THE LIKE), WHETHER ARISING IN TORT, CONTRACT, OR
44 # OTHERWISE, ARISING FROM OR RELATING TO THE SOFTWARE (OR THE USE OF OR
45 # INABILITY TO USE THIS SOFTWARE), EVEN IF NIST HAS BEEN ADVISED OF THE
46 # POSSIBILITY OF SUCH DAMAGES.
47 #
48 # Software developed by NIST employees is not subject to copyright protection
49 # within the United States. By using this software, creating derivative works
50 # or by incorporating this software into another product, you agree that you
51 # will use the software only for non-commercial research purposes and will
52 # indemnify and hold harmless the United States Government for any and all
53 # damages or liabilities that arise out of any use by you.
54 #
55 # changelog:
56 #
57 # 15 August, 2018 -- MHM
58 # fixed apparent error in flip of eps0 across twotheta=90 around line 681
59 #
60
61 """
62 This module contains the core definitions for the XRD Fundamental Parameneters
63 Model (FPA) computation in Python. The main computational class is FP_profile,
64 which stores cached information to allow it to efficiently recompute profiles
65 when parameters have been modified. For the user an Powder class is available
66 which can calculate a complete powder pattern of a crystalline material.
67
68 The diffractometer line profile functions are calculated by methods from Cheary
69 & Coelho 1998 and Mullen & Cline paper and 'R' package. Accumulate all
70 convolutions in Fourier space, for efficiency, except for axial divergence,
71 which needs to be weighted in real space for I3 integral.
72
73 More details about the applied algorithms can be found in the paper by
74 M. H. Mendelhall et al., `Journal of Research of NIST 120, 223 (2015)
75 <http://dx.doi.org/10.6028/jres.120.014>`_ to which you should also refer for a
76 careful definition of all the parameters
77 """
78
79 # Known bugs/problems:
80 # in the axial convolver the parameters slit_length_source can not be equal to
81 # slit_length_target!
82
83 # in this file SI units (m) are used for wavelengths, while by default Angstrom
84 # are used in the remaining of the package
85
86 import atexit
87 import copy
88 import math
89 import multiprocessing
90 import numbers
91 import queue
92 import sys
93 import threading
94 import time
95 import warnings
96 from math import cos, pi, sin, sqrt, tan
97 from multiprocessing.managers import BaseManager
98
99 import numpy
100 from numpy import abs as nabs
101 from numpy import arcsin as nasin
102 from numpy import asarray
103 from numpy import cos as ncos
104 from numpy import sin as nsin
105 from scipy.special import sici # for the sine and cosine integral
106
107 # package internal imports
108 from .. import config, materials, utilities
109 from ..experiment import PowderExperiment
110 from ..math import VecNorm
111 from .smaterials import Powder
112
113 # figure out which FFT package we have, and import it
114 try:
115 from pyfftw.interfaces import numpy_fft, cache
116 # recorded variant of real fft that we will use
117 best_rfft = numpy_fft.rfft
118 # recorded variant of inverse real fft that we will use
119 best_irfft = numpy_fft.irfft
120 cache.enable()
121 cache.set_keepalive_time(1.0)
122 except ImportError:
123 best_rfft = numpy.fft.rfft
124 best_irfft = numpy.fft.irfft
125
126 # create a table of nice factorizations for the FFT package
127 #
128 # this is built once, and shared by all instances
129 # fftw can handle a variety of transform factorizations
130 # numpy fft is not too efficient for things other than a power of two,
131 # although my own measurements says it really does fine. For now, leave
132 # all factors available
133 ft_factors = [
134 2*2**i*3**j*5**k for i in range(20) for j in range(10) for k in range(8)
135 if 2*2**i*3**j*5**k <= 1000000
136 ]
137
138 ft_factors.sort()
139 ft_factors = numpy.array(ft_factors, numpy.int)
140
141 # used for debugging moments from FP_profile.axial_helper().
142 moment_list = []
143 # if this is *True*, compute and save moment errors
144 collect_moment_errors = False
145
146
147 class profile_data(object):
148 """
149 a skeleton class which makes a combined dict and namespace interface for
150 easy pickling and data passing
151 """
152
153 def __init__(self, **kwargs):
154 """
155 initialize the class
156
157 Parameters
158 ----------
159 kwargs : dict
160 keyword=value list to pre-populate the class
161 """
162 mydict = {}
163 mydict.update(kwargs)
164 for k, v in mydict.items():
165 setattr(self, k, v)
166 # a dictionary which shadows our attributes.
167 self.dictionary = mydict
168
169 def add_symbol(self, **kwargs):
170 """
171 add new symbols to both the attributes and dictionary for the class
172
173 Parameters
174 ----------
175 kwargs : dict
176 keyword=value pairs
177 """
178 self.dictionary.update(kwargs)
179 for k, v in kwargs.items():
180 setattr(self, k, v)
181
182
183 class FP_profile:
184 """
185 the main fundamental parameters class, which handles a single reflection.
186 This class is designed to be highly extensible by inheriting new
187 convolvers. When it is initialized, it scans its namespace for specially
188 formatted names, which can come from mixin classes. If it finds a function
189 name of the form conv_xxx, it will call this funtion to create a
190 convolver. If it finds a name of the form info_xxx it will associate the
191 dictionary with that convolver, which can be used in UI generation, for
192 example. The class, as it stands, does nothing significant with it. If it
193 finds str_xxx, it will use that function to format a printout of the
194 current state of the convolver conv_xxx, to allow improved report
195 generation for convolvers.
196
197 When it is asked to generate a profile, it calls all known convolvers.
198 Each convolver returns the Fourier transform of its convolvution. The
199 transforms are multiplied together, inverse transformed, and after fixing
200 the periodicity issue, subsampled, smoothed and returned.
201
202 If a convolver returns *None*, it is not multipled into the product.
203
204 Parameters
205 ----------
206 max_history_length : int
207 the number of histories to cache (default=5); can be overridden if
208 memory is an issue.
209 length_scale_m : float
210 length_scale_m sets scaling for nice printing of parameters. if the
211 units are in mm everywhere, set it to 0.001, e.g. convolvers which
212 implement their own str_xxx method may use this to format their
213 results, especially if 'natural' units are not meters. Typical is
214 wavelengths and lattices in nm or angstroms, for example.
215 """
216 max_history_length = 5 # max number of histories for each convolver
217 length_scale_m = 1.0
218 # class attribute to tell if convolvers in this class
219 # contain anisotropic convolvers
220 isotropic = True
221
222 def __init__(self, anglemode,
223 gaussian_smoother_bins_sigma=1.0,
224 oversampling=10):
225 """
226 initialize the instance
227
228 Parameters
229 ----------
230 anglemode : {'d', 'twotheta'}
231 if setup will be in terms of a d-spacing, otherwise 'twotheta' if
232 setup will be at a fixed 2theta value.
233 gaussian_smoother_bins_sigma : float
234 the number of bins for post-smoothing of data. 1.0 is good. *None*
235 means no final smoothing step.
236 oversampling : int
237 the number of bins internally which will get computed for each bin
238 the the final result.
239 """
240 if anglemode not in ("d", "twotheta"):
241 raise Exception(
242 "invalid angle mode %s, must be 'd' or 'twotheta'" % anglemode)
243 # set to either 'd' for d-spacing based position, or 'twotheta' for
244 # angle-based position
245 self.anglemode = anglemode
246 # sigma, in units of bins, for the final smoother.
247 self.gaussian_smoother_bins_sigma = gaussian_smoother_bins_sigma
248 # the number of internal bins computed for each bin in the final
249 # output. 5-10 is usually plenty.
250 self.oversampling = oversampling
251 # List of our convolvers, found by introspection of names beginning
252 # with 'conv_'
253 self.convolvers = convolvers = [
254 x for x in dir(self) if x.startswith("conv_")]
255 # A dictionary which will store all the parameters local to each
256 # convolution
257 self.param_dicts = dict([(c, {}) for c in convolvers])
258 # add global parameters, associated with no specific convolver
259 # A dictionary of bound functions to call to compute convolutions
260 self.convolver_funcs = dict(
261 [(x, getattr(self, x)) for x in convolvers])
262 # If *True*, print cache hit information
263 self.debug_cache = False
264 # keep a record of things we don't keep when pickled
265 self._clean_on_pickle = set()
266
267 @classmethod
268 def isequivalent(cls, hkl1, hkl2, crystalsystem):
269 """
270 function to determine if according to the convolvers included in this
271 class two sets of Miller indices are equivalent. This function is only
272 called when the class attribute 'isotropic' is False.
273
274 Parameters
275 ----------
276 hkl1, hkl2 : list or tuple
277 Miller indices to be checked for equivalence
278 crystalsystem : str
279 symmetry class of the material which is considered
280
281 Returns
282 -------
283 bool
284 """
285 return True
286
287 def get_function_name(self):
288 """
289 return the name of the function that called this. Useful for convolvers
290 to identify themselves
291
292 Returns
293 ----------
294 str
295 name of calling function
296 """
297 return sys._getframe(1).f_code.co_name
298
299 def add_buffer(self, b):
300 """
301 add a numpy array to the list of objects that can be thrown away on
302 pickling.
303
304 Parameters
305 ----------
306 b : array-like
307 the buffer to add to the list
308
309 Returns
310 -------
311 b : array-like
312 return the same buffer, to make nesting easy.
313 """
314 self._clean_on_pickle.add(id(b))
315 return b
316
317 def set_window(self, twotheta_window_center_deg,
318 twotheta_window_fullwidth_deg, twotheta_output_points):
319 """
320 move the compute window to a new location and compute grids, without
321 resetting all parameters. Clears convolution history and sets up many
322 arrays.
323
324 Parameters
325 ----------
326 twotheta_window_center_deg : float
327 the center position of the middle bin of the window, in degrees
328 twotheta_window_fullwidth_deg : float
329 the full width of the window, in degrees
330 twotheta_output_points : int
331 the number of bins in the final output
332 """
333 # the saved width of the window, in degrees
334 self.twotheta_window_fullwidth_deg = twotheta_window_fullwidth_deg
335 # the saved center of the window, in degrees
336 self.twotheta_window_center_deg = twotheta_window_center_deg
337 # the width of the window, in radians
338 window_fullwidth = math.radians(twotheta_window_fullwidth_deg)
339 self.window_fullwidth = window_fullwidth
340 # the center of the window, in radians
341 twotheta = math.radians(twotheta_window_center_deg)
342 self.twotheta_window_center = twotheta
343 # the number of points to compute in the final results
344 self.twotheta_output_points = twotheta_output_points
345 # the number of points in Fourier space to compute
346 nn = self.oversampling * twotheta_output_points // 2 + 1
347 self.n_omega_points = nn
348 # build all the arrays
349 b = self.add_buffer # shortcut
350
351 # a real-format scratch buffer
352 self._rb1 = b(numpy.zeros(nn, numpy.float))
353 # a real-format scratch buffer
354 self._rb2 = b(numpy.zeros(nn, numpy.float))
355 # a real-format scratch buffer
356 self._rb3 = b(numpy.zeros(nn, numpy.float))
357 # a complex-format scratch buffer
358 self._cb1 = b(numpy.zeros(nn, numpy.complex))
359 # a scratch buffer used by the axial helper
360 self._f0buf = b(numpy.zeros(self.oversampling *
361 twotheta_output_points, numpy.float))
362 # a scratch buffer used for axial divergence
363 self._epsb2 = b(numpy.zeros(self.oversampling *
364 twotheta_output_points, numpy.float))
365 # the I2+ buffer
366 self._I2p = b(numpy.zeros(self.oversampling *
367 twotheta_output_points, numpy.float))
368 # the I2- buffer
369 self._I2m = b(numpy.zeros(self.oversampling *
370 twotheta_output_points, numpy.float))
371 # another buffer used for axial divergence
372 self._axial = b(numpy.zeros(self.oversampling *
373 twotheta_output_points, numpy.float))
374 # the largest frequency in Fourier space
375 omega_max = self.n_omega_points * 2 * pi / window_fullwidth
376 # build the x grid and the complex array that is the convolver
377 # omega is in inverse radians in twotheta space (!)
378 # i.e. if a transform is written
379 # I(ds) = integral(A(L) exp(2 pi i L ds) dL
380 # where L is a real-space length, and s=2 sin(twotheta/2)/lambda
381 # then ds=2*pi*omega*cos(twotheta/2)/lambda (double check this!)
382 # The grid in Fourier space, in inverse radians
383 self.omega_vals = b(numpy.linspace(
384 0, omega_max, self.n_omega_points, endpoint=True))
385 # The grid in Fourier space, in inverse degrees
386 self.omega_inv_deg = b(numpy.radians(self.omega_vals))
387 # The grid in real space, in radians, with full oversampling
388 self.twothetasamples = b(numpy.linspace(
389 twotheta - window_fullwidth/2.0, twotheta + window_fullwidth/2.0,
390 self.twotheta_output_points * self.oversampling, endpoint=False))
391 # The grid in real space, in degrees, with full oversampling
392 self.twothetasamples_deg = b(numpy.linspace(
393 twotheta_window_center_deg - twotheta_window_fullwidth_deg/2.0,
394 twotheta_window_center_deg + twotheta_window_fullwidth_deg/2.0,
395 self.twotheta_output_points * self.oversampling, endpoint=False))
396 # Offsets around the center of the window, in radians
397 self.epsilon = b(self.twothetasamples - twotheta)
398
399 # A dictionary in which we collect recent state for each convolution.
400 # whenever the window gets reset, all of these get cleared
401 self.convolution_history = dict([(x, []) for x in self.convolvers])
402
403 # A dictionary of Lorentz widths, used for de-periodizing the final
404 # result.
405 self.lor_widths = {}
406
407 def get_good_bin_count(self, count):
408 """
409 find a bin count close to what we need, which works well for Fourier
410 transforms.
411
412 Parameters
413 ----------
414 count : int
415 a number of bins.
416
417 Returns
418 -------
419 int
420 a bin count somewhat larger than *count* which is efficient for FFT
421 """
422 return ft_factors[ft_factors.searchsorted(count)]
423
424 def set_optimized_window(self, twotheta_window_center_deg,
425 twotheta_approx_window_fullwidth_deg,
426 twotheta_exact_bin_spacing_deg):
427 """
428 pick a bin count which factors cleanly for FFT, and adjust the window
429 width to preserve the exact center and bin spacing
430
431 Parameters
432 ----------
433 twotheta_window_center_deg : float
434 exact position of center bin, in degrees
435 twotheta_approx_window_fullwidth_deg: float
436 approximate desired width
437 twotheta_exact_bin_spacing_deg: float
438 the exact bin spacing to use
439 """
440 bins = self.get_good_bin_count(
441 int(1 + twotheta_approx_window_fullwidth_deg /
442 twotheta_exact_bin_spacing_deg))
443 window_actwidth = twotheta_exact_bin_spacing_deg * bins
444 self.set_window(twotheta_window_center_deg=twotheta_window_center_deg,
445 twotheta_window_fullwidth_deg=window_actwidth,
446 twotheta_output_points=bins)
447
448 def set_parameters(self, convolver="global", **kwargs):
449 """
450 update the dictionary of parameters associated with the given convolver
451
452 Parameters
453 ----------
454 convolver : str
455 the name of the convolver. name 'global', e.g., attaches to
456 function 'conv_global'
457 kwargs : dict
458 keyword-value pairs to update the convolvers dictionary.
459 """
460 self.param_dicts["conv_" + convolver].update(kwargs)
461
462 def get_conv(self, name, key, format=numpy.float):
463 """
464 get a cached, pre-computed convolver associated with the given
465 parameters, or a newly zeroed convolver if the cache doesn't contain
466 it. Recycles old cache entries.
467
468 This takes advantage of the mutability of arrays. When the contents of
469 the array are changed by the convolver, the cached copy is implicitly
470 updated, so that the next time this is called with the same parameters,
471 it will return the previous array.
472
473 Parameters
474 ----------
475 name : str
476 the name of the convolver to seek
477 key : object
478 any hashable object which identifies the parameters for the
479 computation
480 format : numpy.dtype, optional
481 the type of the array to create, if one is not found.
482
483 Returns
484 -------
485 bool
486 flag, which is *True* if valid data were found, or *False* if the
487 returned array is zero, and *array*, which must be computed by the
488 convolver if *flag* was *False*.
489 """
490
491 # previous computed values as a list
492 history = self.convolution_history[name]
493 for idx, (k, b) in enumerate(history):
494 if k == key:
495 # move to front to mark recently used
496 history.insert(0, history.pop(idx))
497 if self.debug_cache:
498 print(name, True, file=sys.stderr)
499 return True, b # True says we got a buffer with valid data
500 if len(history) == self.max_history_length:
501 buf = history.pop(-1)[1] # re-use oldest buffer
502 buf[:] = 0
503 else:
504 buf = numpy.zeros(self.n_omega_points, format)
505 history.insert(0, (key, buf))
506 if self.debug_cache:
507 print(name, False, file=sys.stderr)
508 return False, buf # False says buffer is empty, need to recompute
509
510 def get_convolver_information(self):
511 """
512 return a list of convolvers, and what we know about them. function
513 scans for functions named conv_xxx, and associated info_xxx entries.
514
515 Returns
516 -------
517 list
518 list of (convolver_xxx, info_xxx) pairs
519 """
520 info_list = []
521 for k, f in self.convolver_funcs.items():
522 info = getattr(self, "info_" + k[5:], {})
523 info["docstring"] = f.__doc__
524 info_list.append((k, info))
525
526 return info_list
527
528 # A dictionary of default parameters for the global namespace,
529 # used to seed a GUI which can harvest this for names, descriptions, and
530 # initial values
531 info_global = dict(
532 group_name="Global parameters",
533 help="this should be help information",
534 param_info=dict(
535 twotheta0_deg=("Bragg center of peak (degrees)", 30.0),
536 d=("d spacing (m)", 4.00e-10),
537 dominant_wavelength=(
538 "wavelength of most intense line (m)", 1.5e-10)
539 )
540 )
541
542 def __str__(self):
543 """
544 return a nicely formatted report describing the current state of this
545 class. this looks for an str_xxx function associated with each conv_xxx
546 name. If it is found, that function if called to get the state of
547 conv_xxx. Otherwise, this simply formats the dictionary of parameters
548 for the convolver, and uses that.
549
550 Returns
551 -------
552 str
553 string of formatted information
554 """
555 keys = list(self.convolver_funcs)
556 keys.sort() # always return info in the same order
557 # global is always first, anyways!
558 keys.insert(0, keys.pop(keys.index('conv_global')))
559 strings = ["", "***convolver id 0x%08x:" % id(self)]
560 for k in keys:
561 strfn = "str_" + k[5:]
562 if hasattr(self, strfn):
563 strings.append(getattr(self, strfn)())
564 else:
565 dd = self.param_dicts["conv_" + k[5:]]
566 if dd:
567 strings.append(k[5:] + ": " + str(dd))
568 return '\n'.join(strings)
569
570 def str_global(self):
571 """
572 returns a string representation for the global context.
573
574 Returns
575 -------
576 str
577 report on global parameters.
578 """
579 # in case it's not initialized
580 self.param_dicts["conv_global"].setdefault("d", 0)
581 return "global: peak center=%(twotheta0_deg).4f, d=%(d).8g, eq. "\
582 "div=%(equatorial_divergence_deg).3f" \
583 % self.param_dicts["conv_global"]
584
585 def conv_global(self):
586 """
587 a dummy convolver to hold global variables and information.
588 the global context isn't really a convolver, returning *None* means
589 ignore result
590
591 Returns
592 -------
593 *None*
594 always returns None
595 """
596 return None
597
598 def axial_helper(self, outerbound, innerbound, epsvals, destination,
599 peakpos=0, y0=0, k=0):
600 """
601 the function F0 from the paper. compute k/sqrt(peakpos-x)+y0 nonzero
602 between outer & inner (inner is closer to peak) or k/sqrt(x-peakpos)+y0
603 if reversed (i.e. if outer > peak) fully evaluated on a specified eps
604 grid, and stuff into destination
605
606 Parameters
607 ----------
608 outerbound : float
609 the edge of the function farthest from the singularity, referenced
610 to epsvals
611 innerbound : float
612 the edge closest to the singularity, referenced to epsvals
613 epsvals : array-like
614 the array of two-theta values or offsets
615 destination : array-like
616 an array into which final results are summed. modified in place!
617 peakpos : float
618 the position of the singularity, referenced to epsvals.
619 y0 : float
620 the constant offset
621 k : float
622 the scale factor
623
624 Returns
625 -------
626 lower_index, upper_index : int
627 python style bounds for region of `destination` which has been
628 modified.
629 """
630 if k == 0:
631 # nothing to do, point at the middle
632 return len(epsvals) // 2, len(epsvals) // 2 + 1
633
634 # bin width for normalizer so sum(result*dx)=exact integral
635 dx = epsvals[1] - epsvals[0]
636 # flag for whether tail is to the left or right.
637 flip = outerbound > peakpos
638
639 delta1 = abs(innerbound - peakpos)
640 delta2 = abs(outerbound - peakpos)
641
642 # this is the analytic area the function must have,
643 # integral(1/sqrt(eps0-eps)) from lower to upper
644 exactintegral = 2 * k * (sqrt(delta2) - sqrt(delta1))
645 exactintegral += y0 * (delta2 - delta1)
646 # exactintegral=max(0, exactintegral) # can never be < 0, beta out of
647 # range.
648 exactintegral *= 1 / dx # normalize so sum is right
649 # compute the exact centroid we need for this
650 if abs(delta2-delta1) < 1e-12:
651 exact_moment1 = 0
652 else:
653 exact_moment1 = ( # simplified from Mathematica FortranForm
654 (4*k*(delta2**1.5-delta1**1.5) + 3*y0*(delta2**2-delta1**2)) /
655 (6.*(2*k*(sqrt(delta2)-sqrt(delta1)) + y0*(delta2-delta1)))
656 )
657 if not flip:
658 exact_moment1 = -exact_moment1
659 exact_moment1 += peakpos
660
661 # note: because of the way the search is done, this produces a result
662 # with a bias of 1/2 channel to the left of where it should be.
663 # this is fixed by shifting all the parameters up 1/2 channel
664 outerbound += dx / 2
665 innerbound += dx / 2
666 peakpos += dx / 2 # fix 1/2 channel average bias from search
667 # note: searchsorted(side="left") always returns the position of the
668 # bin to the right of the match, or exact bin
669 idx0, idx1 = epsvals.searchsorted(
670 (outerbound, innerbound), side='left')
671
672 # peak has been squeezed out, nothing to do
673 if abs(outerbound - innerbound) < (2*dx) or abs(idx1 - idx0) < 2:
674 # preserve the exact centroid: requires summing into two channels
675 # for a peak this narrow, no attempt to preserve the width.
676 # note that x1 (1-f1) + (x1+dx) f1 = mu has solution
677 # (mu - x1) / dx = f1 thus, we want to sum into a channel that has
678 # x1<mu by less than dx, and the one to its right
679 # pick left edge and make sure we are past it
680 idx0 = min(idx0, idx1) - 1
681 while exact_moment1 - epsvals[idx0] > dx:
682 # normally only one step max, but make it a loop in case of
683 # corner case
684 idx0 += 1
685 f1 = (exact_moment1 - epsvals[idx0]) / dx
686 res = (exactintegral * (1 - f1), exactintegral * f1)
687 destination[idx0:idx0 + 2] += res
688 if collect_moment_errors:
689 centroid2 = (res * epsvals[idx0:idx0 + 2]).sum() / sum(res)
690 moment_list.append((centroid2 - exact_moment1) / dx)
691 return [idx0, idx0 + 2] # return collapsed bounds
692
693 if not flip:
694 if epsvals[idx0] != outerbound:
695 idx0 = max(idx0 - 1, 0)
696 idx1 = min(idx1 + 1, len(epsvals))
697 sign = 1
698 deps = self._f0buf[idx0:idx1]
699 deps[:] = peakpos
700 deps -= epsvals[idx0:idx1]
701 deps[-1] = peakpos - min(innerbound, peakpos)
702 deps[0] = peakpos - outerbound
703 else:
704 idx0, idx1 = idx1, idx0
705 if epsvals[idx0] != innerbound:
706 idx0 = max(idx0 - 1, 0)
707 idx1 = min(idx1 + 1, len(epsvals))
708 sign = -1
709 deps = self._f0buf[idx0:idx1]
710 deps[:] = epsvals[idx0:idx1]
711 deps -= peakpos
712 deps[0] = max(innerbound, peakpos) - peakpos
713 deps[-1] = outerbound - peakpos
714
715 dx0 = abs(deps[1] - deps[0])
716 dx1 = abs(deps[-1] - deps[-2])
717
718 # make the numerics accurate: compute average on each bin, which is
719 # integral of 1/sqrt = 2*sqrt, then difference integral
720 # do it in place, return value is actually deps too
721 intg = numpy.sqrt(deps, deps)
722 intg *= 2 * k * sign
723
724 # do difference in place, running forward to avoid self-trampling
725 intg[:-1] -= intg[1:]
726 intg[1:-2] += y0 * dx # add constant
727 # handle narrowed bins on ends carefully
728 intg[0] += y0 * dx0
729 intg[-2] += y0 * dx1
730
731 # intensities are never less than zero!
732 if min(intg[:-1]) < -1e-10 * max(intg[:-1]):
733 print("bad parameters:", (5 * "%10.4f") %
734 (peakpos, innerbound, outerbound, k, y0))
735 print(len(intg), intg[:-1])
736 raise ValueError("Bad axial helper parameters")
737
738 # now, make sure the underlying area is the exactly correct
739 # integral, without bumps due to discretizing the grid.
740 intg *= (exactintegral / (intg[:-1].sum()))
741
742 destination[idx0:idx1 - 1] += intg[:-1]
743
744 # This is purely for debugging. If collect_moment_errors is *True*,
745 # compute exact vs. approximate moments.
746 if collect_moment_errors:
747 centroid2 = (intg[:-1] * epsvals[idx0:idx1 - 1]
748 ).sum() / intg[:-1].sum()
749 moment_list.append((centroid2 - exact_moment1) / dx)
750
751 return [idx0, idx1 - 1] # useful info for peak position
752
753 def full_axdiv_I2(self, Lx=None, Ls=None, Lr=None, R=None, twotheta=None,
754 beta=None, epsvals=None):
755 """
756 return the *I2* function
757
758 Parameters
759 ----------
760 Lx : float
761 length of the xray filament
762 Ls : float
763 length of the sample
764 Lr : float
765 length of the receiver slit
766 R : float
767 diffractometer length, assumed symmetrical
768 twotheta : float
769 angle, in radians, of the center of the computation
770 beta : float
771 offset angle
772 epsvals : array-like
773 array of offsets from center of computation, in radians
774
775 Returns
776 -------
777 epsvals : array-like
778 array of offsets from center of computation, in radians
779 idxmin, idxmax : int
780 the full python-style bounds of the non-zero region of `I2p`
781 and `I2m`
782 I2p, I2m : array-like
783 I2+ and I2- from the paper, the contributions to the intensity
784 """
785 beta1 = (Ls - Lx) / (2 * R) # Ch&Co after eq. 15abcd
786 beta2 = (Ls + Lx) / (2 * R) # Ch&Co after eq. 15abcd, corrected by KM
787
788 eps0 = beta * beta * tan(twotheta) / 2 # after eq. 26 in Ch&Co
789
790 if -beta2 <= beta < beta1:
791 z0p = Lx / 2 + beta * R * (1 + 1 / cos(twotheta))
792 elif beta1 <= beta <= beta2:
793 z0p = Ls / 2 + beta * R / cos(twotheta)
794
795 if -beta2 <= beta <= -beta1:
796 z0m = -1 * Ls / 2 + beta * R / cos(twotheta)
797 elif -beta1 < beta <= beta2:
798 z0m = -1 * Lx / 2 + beta * R * (1 + 1 / cos(twotheta))
799
800 epsscale = tan(pi / 2 - twotheta) / (2 * R * R) # =cotan(twotheta)...
801
802 # Ch&Co 18a&18b, KM sign correction
803 eps1p = (eps0 - epsscale * ((Lr / 2) - z0p)**2)
804 eps2p = (eps0 - epsscale * ((Lr / 2) - z0m)**2)
805 # reversed eps2m and eps1m per KM R
806 eps2m = (eps0 - epsscale * ((Lr / 2) + z0p)**2)
807 eps1m = (eps0 - epsscale * ((Lr / 2) + z0m)**2) # flip all epsilons
808
809 if twotheta > pi/2:
810 # this set of inversions from KM 'R' code, simplified here
811 eps1p, eps2p, eps1m, eps2m = eps1m, eps2m, eps1p, eps2p
812
813 # identify ranges per Ch&Co 4.2.2 and table 1 and select parameters
814 # note table 1 is full of typos, but the minimized
815 # tests from 4.2.2 with redundancies removed seem fine.
816 if Lr > z0p - z0m:
817 if z0p <= Lr/2 and z0m > -1*Lr/2: # beam entirely within slit
818 rng = 1
819 ea = eps1p
820 eb = eps2p
821 ec = eps1m
822 ed = eps2m
823 elif (z0p > Lr/2 and z0m < Lr/2) or \
824 (z0m < -1*Lr/2 and z0p > -1*Lr/2):
825 rng = 2
826 ea = eps2p
827 eb = eps1p
828 ec = eps1m
829 ed = eps2m
830 else:
831 rng = 3
832 ea = eps2p
833 eb = eps1p
834 ec = eps1m
835 ed = eps2m
836 else:
837 # beam hanging off both ends of slit, peak centered
838 if z0m < -1*Lr/2 and z0p > Lr/2:
839 rng = 1
840 ea = eps1m
841 eb = eps2p
842 ec = eps1p
843 ed = eps2m
844 # one edge of beam within slit
845 elif (-1*Lr/2 < z0m < Lr/2 and z0p > Lr/2) or \
846 (-1*Lr/2 < z0p < Lr/2 and z0m < -1*Lr/2):
847 rng = 2
848 ea = eps2p
849 eb = eps1m
850 ec = eps1p
851 ed = eps2m
852 else:
853 rng = 3
854 ea = eps2p
855 eb = eps1m
856 ec = eps1p
857 ed = eps2m
858
859 # now, evaluate function on bounds in table 1 based on ranges
860 # note: because of a sign convention in epsilon, the bounds all get
861 # switched
862
863 # define them in our namespace so they inherit ea, eb, ec, ed, etc.
864 def F1(dst, lower, upper, eea, eeb):
865 return self.axial_helper(destination=dst,
866 innerbound=upper, outerbound=lower,
867 epsvals=epsvals, peakpos=eps0,
868 k=sqrt(abs(eps0-eeb))-sqrt(abs(eps0-eea)),
869 y0=0)
870
871 def F2(dst, lower, upper, eea):
872 return self.axial_helper(destination=dst,
873 innerbound=upper, outerbound=lower,
874 epsvals=epsvals, peakpos=eps0,
875 k=sqrt(abs(eps0 - eea)), y0=-1)
876
877 def F3(dst, lower, upper, eea):
878 return self.axial_helper(destination=dst,
879 innerbound=upper, outerbound=lower,
880 epsvals=epsvals, peakpos=eps0,
881 k=sqrt(abs(eps0 - eea)), y0=+1)
882
883 def F4(dst, lower, upper, eea):
884 # just like F2 but k and y0 negated
885 return self.axial_helper(destination=dst,
886 innerbound=upper, outerbound=lower,
887 epsvals=epsvals, peakpos=eps0,
888 k=-sqrt(abs(eps0 - eea)), y0=+1)
889
890 I2p = self._I2p
891 I2p[:] = 0
892 I2m = self._I2m
893 I2m[:] = 0
894
895 indices = []
896 if rng == 1:
897 indices += F1(dst=I2p, lower=ea, upper=eps0, eea=ea, eeb=eb)
898 indices += F2(dst=I2p, lower=eb, upper=ea, eea=eb)
899 indices += F1(dst=I2m, lower=ec, upper=eps0, eea=ec, eeb=ed)
900 indices += F2(dst=I2m, lower=ed, upper=ec, eea=ed)
901 elif rng == 2:
902 indices += F2(dst=I2p, lower=ea, upper=eps0, eea=ea)
903 indices += F3(dst=I2m, lower=eb, upper=eps0, eea=ea)
904 indices += F1(dst=I2m, lower=ec, upper=eb, eea=ec, eeb=ed)
905 indices += F2(dst=I2m, lower=ed, upper=ec, eea=ed)
906 elif rng == 3:
907 indices += F4(dst=I2m, lower=eb, upper=ea, eea=ea)
908 indices += F1(dst=I2m, lower=ec, upper=eb, eea=ec, eeb=ed)
909 indices += F2(dst=I2m, lower=ed, upper=ec, eea=ed)
910
911 idxmin = min(indices)
912 idxmax = max(indices)
913
914 return epsvals, idxmin, idxmax, I2p, I2m
915
916 def full_axdiv_I3(self, Lx=None, Ls=None, Lr=None, R=None,
917 twotheta=None, epsvals=None, sollerIdeg=None,
918 sollerDdeg=None, nsteps=10, axDiv=""):
919 """
920 carry out the integral of *I2* over *beta* and the Soller slits.
921
922 Parameters
923 ----------
924 Lx : float
925 length of the xray filament
926 Ls : float
927 length of the sample
928 Lr : float
929 length of the receiver slit
930 R : float
931 the (assumed symmetrical) diffractometer radius
932 twotheta : float
933 angle, in radians, of the center of the computation
934 epsvals : array-like
935 array of offsets from center of computation, in radians
936 sollerIdeg : float
937 the full-width (both sides) cutoff angle of the incident Soller
938 slit
939 sollerDdeg : float
940 the full-width (both sides) cutoff angle of the detector Soller
941 slit
942 nsteps : int
943 the number of subdivisions for the integral
944 axDiv : str
945 not used
946
947 Returns
948 -------
949 array-like
950 the accumulated integral, a copy of a persistent buffer *_axial*
951 """
952 beta2 = (Ls + Lx) / (2 * R) # Ch&Co after eq. 15abcd, corrected by KM
953
954 if sollerIdeg is not None:
955 solIrad = math.radians(sollerIdeg) / 2
956
957 def solIfunc(x):
958 return numpy.clip(1.0 - abs(x / solIrad), 0, 1)
959 beta2 = min(beta2, solIrad) # no point going beyond Soller
960 else:
961 def solIfunc(x):
962 return numpy.ones_like(x)
963 if sollerDdeg is not None:
964 solDrad = math.radians(sollerDdeg) / 2
965
966 def solDfunc(x):
967 return numpy.clip(1.0 - abs(x / solDrad), 0, 1)
968 else:
969 def solDfunc(x):
970 return numpy.ones_like(x)
971
972 accum = self._axial
973 accum[:] = 0
974
975 if twotheta > pi / 2:
976 tth1 = pi - twotheta
977 else:
978 tth1 = twotheta
979
980 for iidx in range(nsteps):
981 beta = beta2 * iidx / float(nsteps)
982
983 eps, idxmin, idxmax, I2p, I2m = self.full_axdiv_I2(
984 Lx=Lx, Lr=Lr, Ls=Ls, beta=beta, R=R,
985 twotheta=twotheta, epsvals=epsvals)
986
987 # after eq. 26 in Ch&Co
988 eps0 = beta * beta * tan(twotheta) / 2
989
990 gamma0 = beta / cos(tth1)
991 deps = self._f0buf[idxmin:idxmax]
992 deps[:] = eps0
993 deps -= epsvals[idxmin:idxmax]
994 deps *= 2 * tan(twotheta)
995 # check two channels on each end for negative argument.
996 deps[-1] = max(deps[-1], 0)
997 deps[0] = max(deps[0], 0)
998 if len(deps) >= 2:
999 deps[-2] = max(deps[-2], 0)
1000 deps[1] = max(deps[1], 0)
1001
1002 gamarg = numpy.sqrt(deps, deps) # do sqrt in place for speed
1003 # still need to convert these to in-place
1004 gamp = gamma0 + gamarg
1005 gamm = gamma0 - gamarg
1006
1007 if iidx == 0 or iidx == nsteps - 1:
1008 weight = 1.0 # trapezoidal rule weighting
1009 else:
1010 weight = 2.0
1011
1012 # sum into the accumulator only channels which can be non-zero
1013 # do scaling in-place to save a lot of slow array copying
1014 I2p[idxmin:idxmax] *= solDfunc(gamp)
1015 I2p[idxmin:idxmax] *= (weight * solIfunc(beta))
1016 accum[idxmin:idxmax] += I2p[idxmin:idxmax]
1017 I2m[idxmin:idxmax] *= solDfunc(gamm)
1018 I2m[idxmin:idxmax] *= (weight * solIfunc(beta))
1019 accum[idxmin:idxmax] += I2m[idxmin:idxmax]
1020
1021 # keep this normalized
1022 K = 2 * R * R * abs(tan(twotheta))
1023 accum *= K
1024
1025 return accum
1026
1027 def conv_axial(self):
1028 """
1029 compute the Fourier transform of the axial divergence component
1030
1031 Returns
1032 -------
1033 array-like
1034 the transform buffer, or *None* if this is being ignored
1035 """
1036 me = self.get_function_name() # the name of the convolver, as a string
1037 if self.param_dicts[me].get("axDiv", None) is None:
1038 return None
1039 kwargs = {}
1040 kwargs.update(self.param_dicts[me]) # get all of our parameters
1041 kwargs.update(self.param_dicts["conv_global"])
1042 if "equatorial_divergence_deg" in kwargs:
1043 del kwargs["equatorial_divergence_deg"] # not used
1044
1045 flag, axfn = self.get_conv(me, kwargs, numpy.complex)
1046 if flag:
1047 return axfn # already up to date if first return is True
1048
1049 xx = type("data", (), kwargs)
1050 # no axial divergence, transform of delta fn
1051 if xx.axDiv != "full" or xx.twotheta0_deg == 90.0:
1052 axfn[:] = 1
1053 return axfn
1054 else:
1055 axbuf = self.full_axdiv_I3(
1056 nsteps=xx.n_integral_points,
1057 epsvals=self.epsilon,
1058 Lx=xx.slit_length_source,
1059 Lr=xx.slit_length_target,
1060 Ls=xx.length_sample,
1061 sollerIdeg=xx.angI_deg,
1062 sollerDdeg=xx.angD_deg,
1063 R=xx.diffractometer_radius,
1064 twotheta=xx.twotheta0
1065 )
1066 axfn[:] = best_rfft(axbuf)
1067
1068 return axfn
1069
1070 def conv_tube_tails(self):
1071 """
1072 compute the Fourier transform of the rectangular tube tails function
1073
1074 Returns
1075 -------
1076 array-like
1077 the transform buffer, or *None* if this is being ignored
1078 """
1079 me = self.get_function_name() # the name of the convolver, as a string
1080 kwargs = {}
1081 kwargs.update(self.param_dicts[me]) # get all of our parameters
1082 if not kwargs:
1083 return None # no convolver
1084 # we also need the diffractometer radius from the global space
1085 kwargs["diffractometer_radius"] = self.param_dicts[
1086 "conv_global"]["diffractometer_radius"]
1087 flag, tailfn = self.get_conv(me, kwargs, numpy.complex)
1088 if flag:
1089 return tailfn # already up to date
1090
1091 # tube_tails is (main width, left width, right width, intensity),
1092 # so peak is raw peak + tophat centered at (left width+ right width)
1093 # with area intensity*(right width-left width)/main_width
1094 # check this normalization!
1095 # note: this widths are as defined by Topas... really I think it should
1096 # be
1097 # x/(2*diffractometer_radius) since the detector is 2R from the source,
1098 # but since this is just a fit parameter, we'll defin it as does Topas
1099 xx = type("data", (), kwargs) # allow dotted notation
1100
1101 tail_eps = (xx.tail_right - xx.tail_left) / xx.diffractometer_radius
1102 main_eps = xx.main_width / xx.diffractometer_radius
1103 tail_center = (xx.tail_right + xx.tail_left) / \
1104 xx.diffractometer_radius / 2.0
1105 tail_area = xx.tail_intens * \
1106 (xx.tail_right - xx.tail_left) / xx.main_width
1107
1108 cb1 = self._cb1
1109 rb1 = self._rb1
1110
1111 cb1.real = 0
1112 cb1.imag = self.omega_vals
1113 cb1.imag *= tail_center # sign is consistent with Topas definition
1114 numpy.exp(cb1, tailfn) # shifted center, computed into tailfn
1115
1116 rb1[:] = self.omega_vals
1117 rb1 *= (tail_eps / 2 / pi)
1118 rb1 = numpy.sinc(rb1)
1119 tailfn *= rb1
1120 tailfn *= tail_area # normalize
1121
1122 rb1[:] = self.omega_vals
1123 rb1 *= (main_eps / 2 / pi)
1124 rb1 = numpy.sinc(rb1)
1125 tailfn += rb1 # add central peak
1126 return tailfn
1127
1128 def general_tophat(self, name="", width=None):
1129 """
1130 a utility to compute a transformed tophat function and save it in a
1131 convolver buffer
1132
1133 Parameters
1134 ----------
1135 name : str
1136 the name of the convolver cache buffer to update
1137 width : float
1138 the width in 2-theta space of the tophat
1139
1140 Returns
1141 -------
1142 array-like
1143 the updated convolver buffer, or *None* if the width was *None*
1144 """
1145 if width is None:
1146 return # no convolver
1147 flag, conv = self.get_conv(name, width, numpy.float)
1148 if flag:
1149 return conv # already up to date
1150 rb1 = self._rb1
1151 rb1[:] = self.omega_vals
1152 rb1 *= (width / 2 / pi)
1153 conv[:] = numpy.sinc(rb1)
1154 return conv
1155
1156 # A dictionary of default parameters for conv_emissions,
1157 # used to seed a GUI which can harvest this for names, descriptions, and
1158 # initial values
1159 info_emission = dict(
1160 group_name="Incident beam and crystal size",
1161 help="this should be help information",
1162 param_info=dict(
1163 emiss_wavelengths=("wavelengths (m)", (1.58e-10,)),
1164 emiss_intensities=("relative intensities", (1.00,)),
1165 emiss_lor_widths=("Lorenztian emission fwhm (m)", (1e-13,)),
1166 emiss_gauss_widths=("Gaussian emissions fwhm (m)", (1e-13,)),
1167 crystallite_size_gauss=(
1168 "Gaussian crystallite size fwhm (m)", 1e-6),
1169 crystallite_size_lor=(
1170 "Lorentzian crystallite size fwhm (m)", 1e-6),
1171 )
1172 )
1173
1174 def str_emission(self):
1175 """
1176 format the emission spectrum and crystal size information
1177
1178 Returns
1179 -------
1180 str
1181 the formatted information
1182 """
1183 dd = self.param_dicts["conv_emission"]
1184 if not dd:
1185 return "No emission spectrum"
1186 dd.setdefault("crystallite_size_lor", 1e10)
1187 dd.setdefault("crystallite_size_gauss", 1e10)
1188 dd.setdefault("strain_lor", 0)
1189 dd.setdefault("strain_gauss", 0)
1190 xx = type("data", (), dd)
1191 spect = numpy.array((
1192 xx.emiss_wavelengths, xx.emiss_intensities,
1193 xx.emiss_lor_widths, xx.emiss_gauss_widths))
1194 # convert to Angstroms, like Topas
1195 spect[0] *= 1e10 * self.length_scale_m
1196 spect[2] *= 1e13 * self.length_scale_m # milli-Angstroms
1197 spect[3] *= 1e13 * self.length_scale_m # milli-Angstroms
1198 nm = 1e9 * self.length_scale_m
1199 items = ["emission and broadening:"]
1200 items.append("spectrum=\n" + str(spect.transpose()))
1201 items.append("crystallite_size_lor (nm): %.5g" %
1202 (xx.crystallite_size_lor * nm))
1203 items.append("crystallite_size_gauss (nm): %.5g" %
1204 (xx.crystallite_size_gauss * nm))
1205 items.append("strain_lor: %.5g" % xx.strain_lor)
1206 items.append("strain_gauss: %.5g" % xx.strain_gauss)
1207 return '\n'.join(items)
1208
1209 def conv_emission(self):
1210 """
1211 compute the emission spectrum and (for convenience) the particle size
1212 widths
1213
1214 Returns
1215 -------
1216 array-like
1217 the convolver for the emission and particle sizes
1218
1219 Note:
1220 the particle size and strain stuff here is just to be consistent
1221 with *Topas* and to be vaguely efficient about the computation,
1222 since all of these have the same general shape.
1223 """
1224 me = self.get_function_name() # the name of the convolver, as a string
1225 kwargs = {}
1226 kwargs.update(self.param_dicts[me]) # get all of our parameters
1227 kwargs.update(self.param_dicts["conv_global"])
1228 # if the crystallite size and strain parameters are not set, set them
1229 # to values that make their corrections disappear
1230 kwargs.setdefault("crystallite_size_lor", 1e10)
1231 kwargs.setdefault("crystallite_size_gauss", 1e10)
1232 kwargs.setdefault("strain_lor", 0)
1233 kwargs.setdefault("strain_gauss", 0)
1234
1235 # convert arrays to lists for key checking
1236 key = {}
1237 key.update(kwargs)
1238 for k, v in key.items():
1239 if hasattr(v, 'tolist'):
1240 key[k] = v.tolist()
1241
1242 flag, emiss = self.get_conv(me, key, numpy.complex)
1243 if flag:
1244 return emiss # already up to date
1245
1246 xx = type("data", (), kwargs) # make it dot-notation accessible
1247
1248 epsilon0s = (2 * nasin(asarray(xx.emiss_wavelengths)/(2.0*xx.d)) -
1249 xx.twotheta0)
1250 theta = xx.twotheta0 / 2
1251 # Emission profile FWHM + crystallite broadening (scale factors are
1252 # Topas choice!) (Lorentzian)
1253 # note: the strain broadenings in Topas are expressed in degrees
1254 # 2theta, must convert to radians(theta) with pi/360
1255 widths = (
1256 (asarray(xx.emiss_lor_widths) / asarray(xx.emiss_wavelengths)) *
1257 tan(theta) + math.radians(xx.strain_lor) / 2 * tan(theta) +
1258 (asarray(xx.emiss_wavelengths) /
1259 (2*xx.crystallite_size_lor*cos(theta)))
1260 )
1261 # save weighted average width for future reference in periodicity fixer
1262 self.lor_widths[me] = sum(
1263 widths * xx.emiss_intensities) / sum(xx.emiss_intensities)
1264 # gaussian bits add in quadrature
1265 gfwhm2s = (
1266 ((2*asarray(xx.emiss_gauss_widths)/asarray(xx.emiss_wavelengths)) *
1267 tan(theta))**2 +
1268 (math.radians(xx.strain_gauss) / 2 * tan(theta))**2 +
1269 (asarray(xx.emiss_wavelengths) /
1270 (xx.crystallite_size_gauss*cos(theta)))**2
1271 )
1272
1273 # note that the Fourier transform of a lorentzian with FWHM 2a
1274 # is exp(-abs(a omega))
1275 # now, the line profiles in Fourier space have to have phases
1276 # carefully handled to put the lines in the right places.
1277 # note that the transform of f(x+dx)=exp(i omega dx) f~(x)
1278 omega_vals = self.omega_vals
1279 for wid, gfwhm2, eps, intens in zip(widths, gfwhm2s, epsilon0s,
1280 xx.emiss_intensities):
1281 xvals = numpy.clip(omega_vals * (-wid), -100, 0)
1282 sig2 = gfwhm2 / (8 * math.log(2.0)) # convert fwhm**2 to sigma**2
1283 gxv = numpy.clip((sig2 / -2.0) * omega_vals * omega_vals, -100, 0)
1284 emiss += numpy.exp(xvals + gxv + complex(0, -eps) *
1285 omega_vals) * intens
1286 return emiss
1287
1288 def conv_flat_specimen(self):
1289 """
1290 compute the convolver for the flat-specimen correction
1291
1292 Returns
1293 -------
1294 array-like
1295 the convolver
1296 """
1297 me = self.get_function_name() # the name of the convolver, as a string
1298 equatorial_divergence_deg = self.param_dicts[
1299 "conv_global"].get("equatorial_divergence_deg", None)
1300 if not equatorial_divergence_deg:
1301 return None
1302 twotheta0 = self.param_dicts["conv_global"]["twotheta0"]
1303 key = (twotheta0, equatorial_divergence_deg)
1304 flag, conv = self.get_conv(me, key, numpy.complex)
1305 if flag:
1306 return conv # already up to date
1307
1308 # Flat-specimen error, from Cheary, Coelho & Cline 2004 NIST eq. 9 & 10
1309 # compute epsm in radians from eq. divergence in degrees
1310 # to make it easy to use the axial_helper to compute the function
1311 epsm = math.radians(equatorial_divergence_deg)**2 /\
1312 tan(twotheta0/2.0) / 2.0
1313 eqdiv = self._epsb2
1314 eqdiv[:] = 0
1315 dtwoth = (self.twothetasamples[1] - self.twothetasamples[0])
1316 idx0, idx1 = self.axial_helper(destination=eqdiv,
1317 outerbound=-epsm,
1318 innerbound=0,
1319 epsvals=self.epsilon,
1320 peakpos=0, k=dtwoth/(2.0*sqrt(epsm)))
1321
1322 conv[:] = best_rfft(eqdiv)
1323 conv[1::2] *= -1 # flip center
1324 return conv
1325
1326 def conv_absorption(self):
1327 """
1328 compute the sample transparency correction, including the
1329 finite-thickness version
1330
1331 Returns
1332 -------
1333 array-like
1334 the convolver
1335 """
1336 me = self.get_function_name() # the name of the convolver, as a string
1337 kwargs = {}
1338 kwargs.update(self.param_dicts[me]) # get all of our parameters
1339 if not kwargs:
1340 return None
1341 kwargs["twotheta0"] = self.param_dicts["conv_global"]["twotheta0"]
1342 kwargs["diffractometer_radius"] = self.param_dicts[
1343 "conv_global"]["diffractometer_radius"]
1344
1345 flag, conv = self.get_conv(me, kwargs, numpy.complex)
1346 if flag:
1347 return conv # already up to date
1348 xx = type("data", (), kwargs) # make it dot-notation accessible
1349
1350 # absorption, from Cheary, Coelho & Cline 2004 NIST eq. 12,
1351 # EXCEPT delta = 1/(2*mu*R) instead of 2/(mu*R)
1352 # from Mathematica, unnormalized transform is
1353 # (1-exp(epsmin*(i w + 1/delta)))/(i w + 1/delta)
1354 delta = sin(xx.twotheta0) / (2 * xx.absorption_coefficient *
1355 xx.diffractometer_radius)
1356 # arg=(1/delta)+complex(0,-1)*omega_vals
1357 cb = self._cb1
1358 cb.imag = self.omega_vals
1359 cb.imag *= -1
1360 cb.real = 1 / delta
1361 numpy.reciprocal(cb, conv) # limit for thick samples=1/(delta*arg)
1362 conv *= 1.0 / delta # normalize
1363 # rest of transform of function with cutoff
1364 if kwargs.get("sample_thickness", None) is not None:
1365 epsmin = -2.0 * xx.sample_thickness * \
1366 cos(xx.twotheta0 / 2.0) / xx.diffractometer_radius
1367 cb *= epsmin
1368 numpy.expm1(cb, cb)
1369 cb *= -1
1370 conv *= cb
1371 return conv
1372
1373 def conv_displacement(self):
1374 """
1375 compute the peak shift due to sample displacement and the *2theta* zero
1376 offset
1377
1378 Returns
1379 -------
1380 array-like
1381 the convolver
1382 """
1383 me = self.get_function_name() # the name of the convolver, as a string
1384 kwargs = self.param_dicts[me]
1385 twotheta0 = self.param_dicts["conv_global"]["twotheta0"]
1386 diffractometer_radius = self.param_dicts[
1387 "conv_global"]["diffractometer_radius"]
1388 specimen_displacement = kwargs.get("specimen_displacement", 0.0)
1389 if specimen_displacement is None:
1390 specimen_displacement = 0.0
1391 zero_error_deg = kwargs.get("zero_error_deg", 0.0)
1392 if zero_error_deg is None:
1393 zero_error_deg = 0.0
1394
1395 flag, conv = self.get_conv(me,
1396 (twotheta0, diffractometer_radius,
1397 specimen_displacement, zero_error_deg),
1398 numpy.complex)
1399 if flag:
1400 return conv # already up to date
1401
1402 delta = -2 * cos(twotheta0 / 2.0) * \
1403 specimen_displacement / diffractometer_radius
1404 conv.real = 0
1405 conv.imag = self.omega_vals
1406 # convolver *= numpy.exp(complex(0, -delta-zero_error_deg*pi/180.0) *
1407 # omega_vals)
1408 conv.imag *= (-delta - math.radians(zero_error_deg) -
1409 (twotheta0 - self.twotheta_window_center))
1410 numpy.exp(conv, conv)
1411 return conv
1412
1413 def conv_receiver_slit(self):
1414 """
1415 compute the rectangular convolution for the receiver slit or SiPSD
1416 pixel size
1417
1418 Returns
1419 -------
1420 array-like
1421 the convolver
1422 """
1423 me = self.get_function_name() # the name of the convolver, as a string
1424 # The receiver slit convolution is a top-hat of angular half-width
1425 # a=(slit_width/2)/diffractometer_radius
1426 # which has Fourier transform of sin(a omega)/(a omega)
1427 # NOTE! numpy's sinc(x) is sin(pi x)/(pi x), not sin(x)/x
1428 if self.param_dicts[me].get("slit_width", None) is None:
1429 return None
1430
1431 epsr = (self.param_dicts["conv_receiver_slit"]["slit_width"] /
1432 self.param_dicts["conv_global"]["diffractometer_radius"])
1433 return self.general_tophat(me, epsr)
1434
1435 def conv_si_psd(self):
1436 """
1437 compute the convolver for the integral of defocusing of the face of an
1438 Si PSD
1439
1440 Returns
1441 -------
1442 array-like
1443 the convolver
1444 """
1445 # omega offset defocussing from Cheary, Coelho & Cline 2004 eq. 15
1446 # expressed in terms of a Si PSD looking at channels with vertical
1447 # offset from the center between psd_window_lower_offset and
1448 # psd_window_upper_offset do this last, because we may ultimately take
1449 # a list of bounds, and return a list of convolutions, for efficiency
1450 me = self.get_function_name() # the name of the convolver, as a string
1451 kwargs = {}
1452 kwargs.update(self.param_dicts[me]) # get all of our parameters
1453 if not kwargs:
1454 return None
1455 kwargs.update(self.param_dicts["conv_global"])
1456
1457 flag, conv = self.get_conv(me, kwargs, numpy.float)
1458 if flag:
1459 return conv # already up to date
1460
1461 xx = type("data", (), kwargs)
1462
1463 if not xx.equatorial_divergence_deg or not xx.si_psd_window_bounds:
1464 # if either of these is zero or None, convolution is trivial
1465 conv[:] = 1
1466 return conv
1467
1468 psd_lower_window_pos, psd_upper_window_pos = xx.si_psd_window_bounds
1469 dthl = psd_lower_window_pos / xx.diffractometer_radius
1470 dthu = psd_upper_window_pos / xx.diffractometer_radius
1471 alpha = math.radians(xx.equatorial_divergence_deg)
1472 argscale = alpha / (2.0 * tan(xx.twotheta0 / 2))
1473 # WARNING si(x)=integral(sin(x)/x), not integral(sin(pi x)/(pi x))
1474 # i.e. they sinc function is not consistent with the si function
1475 # whence the missing pi in the denominator of argscale
1476 rb1 = self._rb1
1477 rb2 = self._rb2
1478 rb3 = self._rb3
1479 rb1[:] = self.omega_vals
1480 rb1 *= argscale * dthu
1481 sici(rb1, conv, rb3) # gets both sine and cosine integral, si in conv
1482 if dthl: # no need to do this if the lower bound is 0
1483 rb1[:] = self.omega_vals
1484 rb1 *= argscale * dthl
1485 sici(rb1, rb2, rb3) # gets sine and cosine integral, si in rb2
1486 conv -= rb2
1487 conv[1:] /= self.omega_vals[1:]
1488 conv *= 1 / argscale
1489 conv[0] = dthu - dthl # fix 0/0 with proper area
1490 return conv
1491
1492 def conv_smoother(self):
1493 """
1494 compute the convolver to smooth the final result with a Gaussian before
1495 downsampling.
1496
1497 Returns
1498 -------
1499 array-like
1500 the convolver
1501 """
1502 # create a smoother for output result, independent of real physics, if
1503 # wanted
1504 me = self.get_function_name() # the name of the convolver, as a string
1505 if not self.gaussian_smoother_bins_sigma:
1506 return # no smoothing
1507 flag, buf = self.get_conv(me, self.gaussian_smoother_bins_sigma,
1508 format=numpy.float)
1509 if flag:
1510 return buf # already computed
1511 buf[:] = self.omega_vals
1512 buf *= (self.gaussian_smoother_bins_sigma * (
1513 self.twothetasamples[1] - self.twothetasamples[0]))
1514 buf *= buf
1515 buf *= -0.5
1516 numpy.exp(buf, buf)
1517 return buf
1518
1519 def compute_line_profile(self, convolver_names=None,
1520 compute_derivative=False,
1521 return_convolver=False):
1522 """
1523 execute all the convolutions; if convolver_names is None, use
1524 everything we have, otherwise, use named convolutions.
1525
1526 Parameters
1527 ----------
1528 convolver_names: list
1529 a list of convolvers to select. If *None*, use all found
1530 convolvers.
1531 compute_derivative: bool
1532 if *True*, also return d/dx(function) for peak position fitting
1533
1534 Returns
1535 -------
1536 object
1537 a profile_data object with much information about the peak
1538 """
1539
1540 # create a function which is the Fourier transform of the
1541 # combined convolutions of all the factors
1542
1543 # ="d" if we are using 'd' space, "twotheta" if using twotheta
1544 anglemode = self.anglemode
1545
1546 # the rough center of the spectrum, used for things which need it.
1547 # Copied from global convolver.
1548 self.dominant_wavelength = dominant_wavelength = self.param_dicts[
1549 "conv_global"].get("dominant_wavelength", None)
1550
1551 if anglemode == "twotheta":
1552 twotheta0_deg = self.param_dicts["conv_global"]["twotheta0_deg"]
1553 twotheta0 = math.radians(twotheta0_deg)
1554 d = dominant_wavelength / (2 * sin(twotheta0 / 2.0))
1555 else:
1556 d = self.param_dicts["conv_global"]["d"]
1557 twotheta0 = 2 * math.asin(dominant_wavelength / (2.0 * d))
1558 twotheta0_deg = math.degrees(twotheta0)
1559
1560 # set these in global namespace
1561 self.set_parameters(d=d, twotheta0=twotheta0,
1562 twotheta0_deg=twotheta0_deg)
1563
1564 if convolver_names is None:
1565 convolver_names = self.convolver_funcs.keys() # get all names
1566
1567 # run through the name list, and call the convolver to harvest its
1568 # result
1569 conv_list = [self.convolver_funcs[x]() for x in convolver_names]
1570
1571 # now, multiply everything together
1572 convolver = self._cb1 # get a complex scratch buffer
1573 convolver[:] = 1 # initialize
1574 for c in conv_list: # accumulate product
1575 if c is not None:
1576 convolver *= c
1577
1578 if convolver[1].real > 0: # recenter peak!
1579 convolver[1::2] *= -1
1580
1581 peak = best_irfft(convolver)
1582
1583 # now, use the trick from Mendenhall JQSRT Voigt paper to remove
1584 # periodic function correction
1585 # JQSRT 105 number 3 July 2007 p. 519 eq. 7
1586 # total lor widths, created by the various colvolvers
1587 correction_width = 2 * sum(self.lor_widths.values())
1588
1589 d2p = 2.0 * pi / self.window_fullwidth
1590 alpha = correction_width / 2.0 # be consistent with convolver
1591 mu = (peak * self.twothetasamples).sum() / peak.sum() # centroid
1592 dx = self.twothetasamples - mu
1593 eps_corr1 = (math.sinh(d2p * alpha) / self.window_fullwidth) / \
1594 (math.cosh(d2p * alpha) - ncos(d2p * dx))
1595 eps_corr2 = (alpha / pi) / (dx * dx + alpha * alpha)
1596 corr = (convolver[0].real / numpy.sum(eps_corr2)) * \
1597 (eps_corr1 - eps_corr2)
1598 peak -= corr
1599
1600 peak *= self.window_fullwidth / \
1601 (self.twotheta_output_points / self.oversampling) # scale to area
1602
1603 if compute_derivative:
1604 # this is useful
1605 convolver *= self.omega_vals
1606 convolver *= complex(0, 1)
1607 deriv = best_irfft(convolver)
1608 deriv *= self.window_fullwidth / \
1609 (self.twotheta_output_points / self.oversampling)
1610 deriv = deriv[::self.oversampling]
1611 else:
1612 deriv = None
1613
1614 result = profile_data(twotheta0_deg=math.degrees(twotheta0),
1615 twotheta=self.twothetasamples[
1616 ::self.oversampling],
1617 omega_inv_deg=self.omega_inv_deg[
1618 :self.twotheta_output_points // 2 + 1],
1619 twotheta_deg=self.twothetasamples_deg[
1620 ::self.oversampling],
1621 peak=peak[::self.oversampling],
1622 derivative=deriv
1623 )
1624
1625 if return_convolver:
1626 result.add_symbol(
1627 convolver=convolver[:self.twotheta_output_points//2+1])
1628
1629 return result
1630
1631 def self_clean(self):
1632 """
1633 do some cleanup to make us more compact;
1634 Instance can no longer be used after doing this, but can be pickled.
1635 """
1636 clean = self._clean_on_pickle
1637 pd = dict()
1638 pd.update(self.__dict__)
1639 for thing in pd:
1640 x = getattr(self, thing)
1641 if id(x) in clean:
1642 delattr(self, thing)
1643 # delete extra attributes cautiously, in case we have already been
1644 # cleaned
1645 for k in ('convolver_funcs', 'convolvers',
1646 'factors', 'convolution_history'):
1647 if pd.pop(k, None) is not None:
1648 delattr(self, k)
1649
1650 def __getstate__(self):
1651 """
1652 return information for pickling. Removes transient data from cache of
1653 shadow copy so resulting object is fairly compact. This does not
1654 affect the state of the actual instance.
1655
1656 Returns
1657 -------
1658 dict
1659 dictionary of sufficient information to reanimate instance.
1660 """
1661 # do some cleanup on state before we get pickled
1662 # (even if main class not cleaned)
1663 clean = self._clean_on_pickle
1664 pd = dict()
1665 pd.update(self.__dict__)
1666 for thing in pd:
1667 x = getattr(self, thing)
1668 if id(x) in clean:
1669 del pd[thing]
1670 # delete extra attributes cautiously, in case we have already been
1671 # cleaned
1672 for k in ('convolver_funcs', 'convolvers',
1673 'factors', 'convolution_history'):
1674 pd.pop(k, None)
1675 return pd
1676
1677 def __setstate__(self, setdict):
1678 """
1679 reconstruct class from pickled information
1680 This rebuilds the class instance so it is ready to use on unpickling.
1681
1682 Parameters
1683 ----------
1684 self : FP_Profile
1685 an empty class instance
1686 setdict : dict
1687 dictionary from FP_profile.__getstate__()
1688 """
1689 self.__init__(anglemode=setdict["anglemode"],
1690 gaussian_smoother_bins_sigma=setdict[
1691 "gaussian_smoother_bins_sigma"],
1692 oversampling=setdict["oversampling"]
1693 )
1694 for k, v in setdict.items():
1695 setattr(self, k, v)
1696 try:
1697 s = self
1698 self.set_window(
1699 twotheta_window_center_deg=s.twotheta_window_center_deg,
1700 twotheta_window_fullwidth_deg=s.twotheta_window_fullwidth_deg,
1701 twotheta_output_points=s.twotheta_output_points
1702 )
1703 # override clearing of this by set_window
1704 self.lor_widths = setdict["lor_widths"]
1705 except AttributeError:
1706 pass
1707
1708
1709 class convolver_handler(object):
1710 """
1711 manage the convolvers of on process
1712 """
1713
1714 def __init__(self):
1715 self.convolvers = []
1716
1717 def add_convolver(self, convolver):
1718 self.convolvers.append(convolver)
1719
1720 def update_parameters(self, parameters):
1721 for idx, c in enumerate(self.convolvers):
1722 for k, v in parameters.items():
1723 if k == 'classoptions':
1724 continue
1725 c.set_parameters(convolver=k, **v)
1726
1727 def set_windows(self, centers, npoints, flag, width):
1728 for c, cen, np, f in zip(self.convolvers, centers, npoints, flag):
1729 if f:
1730 c.set_window(twotheta_output_points=np,
1731 twotheta_window_center_deg=cen,
1732 twotheta_window_fullwidth_deg=width)
1733
1734 def calc(self, run, ttpeaks):
1735 """
1736 calculate profile function for selected convolvers
1737
1738 Parameters
1739 ----------
1740 run : list
1741 list of flags of length of convolvers to tell which convolver needs
1742 to be run
1743 ttpeaks : array-like
1744 peak positions for the convolvers
1745
1746 Returns
1747 -------
1748 list
1749 list of profile_data result objects
1750 """
1751 results = []
1752 for c, flag, tt in zip(self.convolvers, run, ttpeaks):
1753 if flag:
1754 c.set_parameters(twotheta0_deg=tt)
1755 res = c.compute_line_profile()
1756 del res.twotheta, res.omega_inv_deg
1757 del res.dictionary, res.derivative
1758 results.append(res)
1759 else:
1760 results.append(None)
1761 return results
1762
1763
1764 def chunkify(lst, n):
1765 return [lst[i::n] for i in range(n)]
1766
1767
1768 class manager(BaseManager):
1769 pass
1770
1771
1772 class PowderDiffraction(PowderExperiment):
1773
1774 """
1775 Experimental class for powder diffraction. This class calculates the
1776 structure factors of powder diffraction lines and uses instances of
1777 FP_profile to perform the convolution with experimental resolution function
1778 calculated by the fundamental parameters approach. This class used
1779 multiprocessing to speed up calculation. Set config.NTHREADS=1 to restrict
1780 this to one worker process.
1781 """
1782
1783 _valid_init_kwargs = copy.copy(PowderExperiment._valid_init_kwargs)
1784 _valid_init_kwargs['tt_cutoff'] = '2Theta cut off value in degree'
1785 _valid_init_kwargs['fpclass'] = 'FP_profile derived class'
1786 _valid_init_kwargs['fpsettings'] = 'settings dictionaries'
1787 _valid_init_kwargs['enable_simulation'] = 'flag to enable simulation mode'
1788 del _valid_init_kwargs['sampleor']
1789
1790 def __init__(self, mat, **kwargs):
1791 """
1792 the class is initialized with a xrayutilities.materials.Crystal
1793 instance and calculates the powder intensity and peak positions of the
1794 Crystal up to an angle of tt_cutoff. Results are stored in
1795
1796 data .... array with intensities
1797 ang ..... Bragg angles of the peaks (Theta!)
1798 qpos .... reciprocal space position of intensities
1799
1800 If `enable_simulation` is set to True the `Convolve` and `Calculate`
1801 methods can be used to calculated a powder pattern. Upon such
1802 initialization it is strongly recommend to call the `close` method to
1803 clean up the multiprocessing threads when no further calculations will
1804 be performed.
1805
1806 Parameters
1807 ----------
1808 mat : Crystal or Powder
1809 material for the powder calculation
1810 kwargs : dict
1811 optional keyword arguments same as for the Experiment base class
1812 and:
1813 tt_cutoff : float, optional
1814 Powder peaks are calculated up to an scattering angle of tt_cutoff
1815 (deg)
1816 enable_simulation: False, optional
1817 enables the initialization of the convolvers. Call `close()` after
1818 you are finished with your instance!
1819 fpclass : FP_profile
1820 FP_profile derived class with possible convolver mixins.
1821 (default: FP_profile)
1822 fpsettings : dict
1823 settings dictionaries for the convolvers. Default settings are
1824 loaded from the config file.
1825 """
1826 utilities.check_kwargs(kwargs, self._valid_init_kwargs,
1827 self.__class__.__name__)
1828 if isinstance(mat, materials.Crystal):
1829 self.mat = Powder(mat, 1)
1830 elif isinstance(mat, Powder):
1831 self.mat = mat
1832 else:
1833 raise TypeError("mat must be an instance of class "
1834 "xrayutilities.materials.Crystal or "
1835 "xrayutilities.simpack.Powder")
1836
1837 self._tt_cutoff = kwargs.pop('tt_cutoff', 180)
1838 self.fpclass = kwargs.pop('fpclass', FP_profile)
1839 self.settings = self.load_settings_from_config(
1840 kwargs.pop('fpsettings', {}))
1841 self._enable_sim = kwargs.pop('enable_simulation', False)
1842
1843 PowderExperiment.__init__(self, **kwargs)
1844
1845 # number of significant digits, needed to identify equal floats
1846 self.digits = 5
1847
1848 # determine if convolvers are isotropic
1849 self.isotropic = self.fpclass.isotropic
1850
1851 # calculate powder lines position and intensities
1852 self.init_powder_lines(self._tt_cutoff)
1853
1854 # initialize FP_profile instances (add field to the data dictionary)
1855 for h in self.data:
1856 self.data[h]['conv'] = self._init_fpprofile(self.fpclass)
1857 self.update_settings(self.settings)
1858 self.set_sample_parameters()
1859
1860 # set some other class properties
1861 self.__tt = None
1862 self.__ww = None
1863
1864 # initialize multiprocessing
1865 if self._enable_sim:
1866 self._init_multiprocessing()
1867 # set wavelength from class constructor
1868 if 'wl' in kwargs:
1869 self._set_wavelength_pd(kwargs['wl'])
1870 if 'en' in kwargs:
1871 self._set_energy_pd(kwargs['en'])
1872
1873 def _init_multiprocessing(self):
1874 """
1875 initialize multiprocessing for powder pattern calculation
1876 """
1877 # The structure of the multiprocessing code is as follows:
1878 # There are nproc "manager"s which handle the actual convolver code.
1879 # Additionally there are 4 daemon threads which listen for work to be
1880 # distributed to the managers.
1881 np = config.NTHREADS
1882 self.nproc = np if np != 0 else multiprocessing.cpu_count()
1883 self.chunks = chunkify(list(self.data), self.nproc)
1884 self.next_proc = len(self.data) % self.nproc
1885 manager.register("conv", convolver_handler)
1886 self.managers = [manager() for idx in range(self.nproc)]
1887 self.conv_handlers = []
1888 self.threads = []
1889 self.output_queue = queue.Queue()
1890 for idx, mg in enumerate(self.managers):
1891 mg.start()
1892 m = mg.conv()
1893 for h in self.chunks[idx]:
1894 m.add_convolver(self.data[h]['conv'])
1895 self.conv_handlers.append(m)
1896 self.threads.append((
1897 threading.Thread(target=self._send_work, args=(idx, )),
1898 queue.Queue(), self.output_queue))
1899 self._running = True
1900 for th, q1, q2 in self.threads:
1901 th.daemon = True
1902 th.start()
1903 atexit.register(self.__stop__)
1904
1905 def close(self):
1906 self.__stop__()
1907
1908 def __stop__(self):
1909 self._running = False
1910 try: # try/except needed only for python2 compatibility
1911 # end daemon threads which distribute the work load
1912 for th, q1, q2 in self.threads:
1913 q1.put(None)
1914 th.join()
1915 delattr(self, 'threads')
1916 # end managers which handle the convolvers
1917 delattr(self, 'conv_handlers')
1918 for m in self.managers:
1919 m.shutdown()
1920 delattr(self, 'managers')
1921 except AttributeError:
1922 pass
1923 atexit.unregister(self.__stop__)
1924
1925 def load_settings_from_config(self, settings):
1926 """
1927 load parameters from the config and update these settings with the
1928 options from the settings parameter
1929 """
1930 params = dict()
1931 for k in config.POWDER:
1932 params[k] = dict()
1933 params[k].update(config.POWDER[k])
1934 if k in settings:
1935 params[k].update(settings[k])
1936 for k in settings:
1937 if k not in config.POWDER:
1938 params[k] = dict()
1939 params[k].update(settings[k])
1940 return params
1941
1942 def _init_fpprofile(self, fpclass):
1943 """
1944 configure the default parameters of the FP_profile class and return an
1945 instance with these settings
1946
1947 Parameters
1948 ----------
1949 fpclass : FP_profile
1950 class with possible mixins which implement more convolvers
1951
1952 Returns
1953 -------
1954 fpclass
1955 instance of fpclass
1956 """
1957 classparams = dict()
1958 classparams.update(self.settings['classoptions'])
1959 classparams.pop('window_width')
1960 p = fpclass(**classparams)
1961 p.debug_cache = False
1962 return p
1963
1964 def _set_wavelength_pd(self, wl):
1965 PowderExperiment._set_wavelength(self, wl)
1966 s = {'emission': {'emiss_wavelengths': self.wavelength*1e-10}}
1967 self.update_settings(s)
1968
1969 def _set_energy_pd(self, energy):
1970 PowderExperiment._set_energy(self, energy)
1971 s = {'emission': {'emiss_wavelengths': self.wavelength*1e-10}}
1972 self.update_settings(s)
1973
1974 energy = property(PowderExperiment._get_energy, _set_energy_pd)
1975 wavelength = property(PowderExperiment._get_wavelength, _set_wavelength_pd)
1976
1977 def set_wavelength_from_params(self):
1978 """
1979 sets the wavelenth in the base class from the settings dictionary of
1980 the FP_profile classes and also set it in the 'global' part of the
1981 parameters
1982 """
1983 if 'emission' in self.settings:
1984 pem = self.settings['emission']
1985 if 'emiss_wavelengths' in pem:
1986 wl = pem['emiss_wavelengths'][0]
1987 self.settings['global']['dominant_wavelength'] = wl
1988 for h, d in self.data.items():
1989 fp = d['conv']
1990 fp.set_parameters(convolver='global',
1991 **self.settings['global'])
1992 # set wavelength in base class
1993 PowderExperiment._set_wavelength(self, wl*1e10)
1994
1995 def set_sample_parameters(self):
1996 """
1997 load sample parameters from the Powder class and use them in all
1998 FP_profile instances of this object
1999 """
2000 samplesettings = {}
2001 for prop, default in zip(('crystallite_size_lor',
2002 'crystallite_size_gauss',
2003 'strain_lor', 'strain_gauss'),
2004 (1e10, 1e10, 0, 0)):
2005 samplesettings[prop] = getattr(self.mat, prop, default)
2006
2007 self.settings['emission'].update(samplesettings)
2008 for h, d in self.data.items():
2009 fp = d['conv']
2010 fp.set_parameters(convolver='emission', **samplesettings)
2011
2012 def update_settings(self, newsettings={}):
2013 """
2014 update settings of all instances of FP_profile
2015
2016 Parameters
2017 ----------
2018 newsettings : dict
2019 dictionary with new settings. It has to include one subdictionary
2020 for every convolver which should have its settings changed.
2021 """
2022 if 'global' in newsettings:
2023 if 'dominant_wavelength' in newsettings['global']:
2024 print('PowderDiffraction: dominant wavelength is a read only'
2025 'setting \n -> use emission: emiss_wavelength instead')
2026 if 'emission' in newsettings:
2027 nem = newsettings['emission']
2028 for k in ('emiss_wavelengths', 'emiss_intensities',
2029 'emiss_gauss_widths', 'emiss_lor_widths'):
2030 if k in nem:
2031 if isinstance(nem[k], numbers.Number):
2032 nem[k] = (nem[k], )
2033 for k in newsettings:
2034 if k == 'classoptions':
2035 continue
2036 for h, d in self.data.items():
2037 fp = d['conv']
2038 fp.set_parameters(convolver=k, **newsettings[k])
2039 if k not in self.settings:
2040 self.settings[k] = dict()
2041 self.settings[k].update(newsettings[k])
2042 self.set_wavelength_from_params()
2043
2044 @property
2045 def twotheta(self):
2046 return self.__tt
2047
2048 @twotheta.setter
2049 def twotheta(self, tt):
2050 oldtt = self.__tt
2051 self.__tt = tt
2052 if oldtt is None:
2053 self.set_window()
2054 elif len(oldtt) != len(self.__tt):
2055 self.set_window(force=True)
2056 elif not numpy.all(numpy.equal(oldtt, self.__tt)):
2057 self.set_window(force=True)
2058
2059 @property
2060 def window_width(self):
2061 return self.__ww
2062
2063 @window_width.setter
2064 def window_width(self, ww):
2065 oldww = self.__ww
2066 if ww == 'config':
2067 self.__ww = config.POWDER['classoptions']['window_width']
2068 else:
2069 self.__ww = ww
2070 if oldww != self.__ww:
2071 self.set_window(force=True)
2072
2073 def set_window(self, force=False):
2074 """
2075 sets the calcultion window for all convolvers
2076 """
2077 ww = self.window_width
2078 tt = self.twotheta
2079 if not ww or tt is None: # not all necessary information is set up
2080 return
2081 npoints = dict()
2082 nset = dict()
2083 for h, d in self.data.items():
2084 ttpeak = 2 * d['ang']
2085 if ttpeak - ww/2 > tt.max() or ttpeak + ww/2 < tt.min():
2086 continue
2087 idx = numpy.argwhere(numpy.logical_and(tt > ttpeak - ww/2,
2088 tt < ttpeak + ww/2))
2089 try:
2090 np = int(math.ceil(len(idx) / (tt[idx[-1]]-tt[idx[0]]) * ww))
2091 except OverflowError:
2092 np = 1
2093 npoints[h] = np
2094 if hasattr(d['conv'], 'twotheta_window_center_deg'):
2095 fptt = d['conv'].twotheta_window_center_deg
2096 if abs(ttpeak-fptt) / ww < 0.25 and not force:
2097 continue
2098 else:
2099 nset[h] = True
2100 else:
2101 nset[h] = True
2102 # set window in local instances
2103 d['conv'].set_window(twotheta_output_points=np,
2104 twotheta_window_center_deg=ttpeak,
2105 twotheta_window_fullwidth_deg=ww)
2106 # set multiprocessing instances
2107 for chunk, handler in zip(self.chunks, self.conv_handlers):
2108 handler.set_windows([2 * self.data[h]['ang'] for h in chunk],
2109 [npoints.get(h, 0) for h in chunk],
2110 [nset.get(h, False) for h in chunk], ww)
2111
2112 def _send_work(self, idx):
2113 """
2114 a threaded block which watches for data and runs computation
2115 """
2116 th, input, output = self.threads[idx]
2117 while self._running:
2118 try:
2119 settings, run, ttpeaks = input.get(True)
2120 except TypeError:
2121 break
2122 handler = self.conv_handlers[idx]
2123 handler.update_parameters(settings)
2124 results = handler.calc(run, ttpeaks)
2125 output.put((idx, results)) # put results on output queue
2126 self._running = False
2127
2128 def structure_factors(self, tt_cutoff):
2129 """
2130 determine structure factors/reflection strength of all Bragg peaks up
2131 to tt_cutoff
2132
2133 Parameters
2134 ----------
2135 tt_cutoff : float
2136 upper cutoff value of 2theta until which the reflection strength
2137 are calculated
2138
2139 Returns
2140 -------
2141 ndarray
2142 numpy array with field for 'hkl' (Miller indices of the peaks), 'q'
2143 (q-position), and 'r' (reflection strength) of the Bragg peaks
2144 """
2145 mat = self.mat.material
2146 # calculate maximal Bragg indices
2147 hma = int(math.ceil(VecNorm(mat.a1) * self.k0 / pi *
2148 sin(math.radians(tt_cutoff / 2.))))
2149 hmi = -hma
2150 kma = int(math.ceil(VecNorm(mat.a2) * self.k0 / pi *
2151 sin(math.radians(tt_cutoff / 2.))))
2152 kmi = -kma
2153 lma = int(math.ceil(VecNorm(mat.a3) * self.k0 / pi *
2154 sin(math.radians(tt_cutoff / 2.))))
2155 lmi = -lma
2156
2157 if config.VERBOSITY >= config.INFO_ALL:
2158 print("XU.Powder.PowderIntensity: tt_cutoff; (hmax, kmax, lmax): "
2159 "%6.2f (%d,%d,%d)" % (tt_cutoff, hma, kma, lma))
2160
2161 # calculate structure factors
2162 qmax = 2 * self.k0 * sin(math.radians(tt_cutoff/2))
2163 hkl = numpy.mgrid[hma:hmi-1:-1,
2164 kma:kmi-1:-1,
2165 lma:lmi-1:-1].reshape(3, -1).T
2166 q = mat.Q(hkl)
2167 qnorm = numpy.linalg.norm(q, axis=1)
2168 m = qnorm <= qmax
2169
2170 data = numpy.zeros(numpy.sum(m), dtype=[('q', numpy.double),
2171 ('r', numpy.double),
2172 ('hkl', numpy.ndarray)])
2173 data['q'] = qnorm[m]
2174 data['r'] = nabs(mat.StructureFactorForQ(q[m], self.energy)) ** 2
2175 data['hkl'] = list(hkl[m])
2176
2177 return data
2178
2179 def merge_lines(self, data):
2180 """
2181 if calculation if isotropic lines at the same q-position can be merged
2182 to one line to reduce the calculational effort
2183
2184 Parameters
2185 ----------
2186 data : ndarray
2187 numpy field array with values of 'hkl' (Miller indices of the
2188 peaks), 'q' (q-position), and 'r' (reflection strength) as produced
2189 by the structure_factors method
2190
2191 Returns
2192 -------
2193 hkl, q, ang, r : array-like
2194 Miller indices, q-position, diffraction angle (Theta), and
2195 reflection strength of the material
2196 """
2197 data = data[numpy.argsort(data['q'], kind='mergesort')]
2198 qpos = []
2199 refstrength = []
2200 hkl = []
2201
2202 def add_lines(q, ref, chkl):
2203 for R, m in zip(ref, chkl):
2204 qpos.append(q)
2205 refstrength.append(R)
2206 hkl.append(m)
2207
2208 currq = -1
2209 curref = []
2210 currhkl = []
2211 for r in data:
2212 if abs(r[0] - currq) > config.EPSILON:
2213 add_lines(currq, curref, currhkl)
2214 currq = r[0]
2215 curref = [r[1], ]
2216 currhkl = [r[2], ]
2217 else:
2218 if self.isotropic:
2219 curref[-1] += r[1]
2220 else:
2221 # merge lines which are equal according to the crystal
2222 # and convolver symmetries
2223 added = False
2224 for i, m in enumerate(currhkl):
2225 if self.mat.material.lattice.isequivalent(
2226 m, r[2], equalq=True):
2227 if self.fpclass.isequivalent(
2228 m, r[2],
2229 self.mat.material.lattice.crystal_system):
2230 curref[i] += r[1]
2231 added = True
2232 if not added:
2233 curref.append(r[1])
2234 currhkl.append(r[2])
2235 # add remaining lines
2236 add_lines(currq, curref, currhkl)
2237
2238 qpos = numpy.array(qpos[1:], dtype=numpy.double)
2239 ang = self.Q2Ang(qpos)
2240 refstrength = numpy.array(refstrength[1:], dtype=numpy.double)
2241 hkl = hkl[1:]
2242 return hkl, qpos, ang, refstrength
2243
2244 def correction_factor(self, ang):
2245 """
2246 calculate the correction factor for the diffracted intensities. This
2247 contains the polarization effects and the Lorentz factor
2248
2249 Parameters
2250 ----------
2251 ang : aray-like
2252 theta diffraction angles for which the correction should be
2253 calculated
2254
2255 Returns
2256 -------
2257 f : array-like
2258 array of the same shape as ang containing the correction factors
2259 """
2260 # correct data for polarization and lorentzfactor and unit cell volume
2261 # see L.S. Zevin : Quantitative X-Ray Diffractometry
2262 # page 18ff
2263 polarization_factor = (1 +
2264 ncos(numpy.radians(2 * ang)) ** 2) / 2
2265 lorentz_factor = 1. / (nsin(numpy.radians(ang)) ** 2 *
2266 ncos(numpy.radians(ang)))
2267 unitcellvol = self.mat.material.lattice.UnitCellVolume()
2268 return polarization_factor * lorentz_factor / unitcellvol ** 2
2269
2270 def init_powder_lines(self, tt_cutoff):
2271 """
2272 calculates the powder intensity and positions up to an angle of
2273 tt_cutoff (deg) and stores the result in the data dictionary whose
2274 structure is as follows:
2275
2276 The data dictionary has one entry per line with a unique identifier
2277 as key of the entry. The entries themself are dictionaries which
2278 have the following entries:
2279
2280 * hkl : (h, k, l), Miller indices of the Bragg peak
2281 * r : reflection strength of the line
2282 * ang : Bragg angle of the peak (theta = 2theta/2!)
2283 * qpos : reciprocal space position
2284 """
2285
2286 tmp_data = self.structure_factors(tt_cutoff)
2287 hkl, qpos, ang, rs = self.merge_lines(tmp_data)
2288 corrfact = self.correction_factor(ang)
2289 rs *= corrfact
2290 ids = [tuple(idx) for idx in hkl]
2291 self.data = dict()
2292 for i, q, a, r in zip(ids, qpos, ang, rs):
2293 active = True if r/rs.max() > config.EPSILON else False
2294 self.data[i] = {'qpos': q, 'ang': a, 'r': r,
2295 'active': active}
2296
2297 def update_powder_lines(self, tt_cutoff):
2298 """
2299 calculates the powder intensity and positions up to an angle of
2300 tt_cutoff (deg) and updates the values in:
2301
2302 * ids: list of unique identifiers of the powder line
2303 * data: array with intensities
2304 * ang: bragg angles of the peaks (theta=2theta/2!)
2305 * qpos: reciprocal space position of intensities
2306 """
2307 tmp_data = self.structure_factors(tt_cutoff)
2308 hkl, qpos, ang, rs = self.merge_lines(tmp_data)
2309 corrfact = self.correction_factor(ang)
2310 rs *= corrfact
2311 ids = [tuple(idx) for idx in hkl]
2312 for h, q, a, r in zip(ids, qpos, ang, rs):
2313 active = True if r/rs.max() > config.EPSILON else False
2314 if h in self.data:
2315 self.data[h]['qpos'] = q
2316 self.data[h]['ang'] = a
2317 self.data[h]['r'] = r
2318 self.data[h]['active'] = active
2319 else:
2320 # new peak needs a new convolver
2321 fp = self._init_fpprofile(self.fpclass)
2322 for k, v in self.settings.items():
2323 if k == 'classoptions':
2324 continue
2325 fp.set_parameters(convolver=k, **v)
2326 self.data[h] = {'qpos': q, 'ang': a, 'r': r,
2327 'conv': fp, 'active': active}
2328 if self._enable_sim:
2329 self.conv_handlers[self.next_proc].add_convolver(fp)
2330 self.chunks[self.next_proc].append(h)
2331 self.next_proc = (self.next_proc + 1) % self.nproc
2332 for h in self.data:
2333 if h not in ids:
2334 # make entry inactive
2335 self.data[h]['active'] = False
2336
2337 def Convolve(self, twotheta, window_width='config', mode='multi'):
2338 """
2339 convolute the powder lines with the resolution function and map them
2340 onto the twotheta positions. This calculates the powder pattern
2341 excluding any background contribution
2342
2343 Parameters
2344 ----------
2345 twotheta : array-like
2346 two theta values at which the powder pattern should be calculated.
2347 window_width : float, optional
2348 width of the calculation window of a single peak
2349 mode : {'multi, 'local'}, optional
2350 multiprocessing mode, either 'multi' to use multiple processes or
2351 'local' to restrict the calculation to a single process
2352
2353 Note:
2354 Bragg peaks are only included up to tt_cutoff set in the class
2355 constructor!
2356
2357 Returns
2358 -------
2359 output intensity values for the twotheta values given in the input
2360 """
2361 if self._enable_sim:
2362 t_start = time.time()
2363
2364 out = numpy.zeros_like(twotheta)
2365 tt = self.twotheta = twotheta
2366 self.window_width = window_width
2367 ww = self.window_width
2368
2369 # check if twotheta range extends above tt_cutoff
2370 if tt.max() > self._tt_cutoff:
2371 warnings.warn('twotheta range is larger then tt_cutoff. '
2372 'Possibly Bragg peaks in the convolution range '
2373 'are not considered!')
2374
2375 if mode == 'local':
2376 for h, d in self.data.items():
2377 if not d['active']:
2378 continue
2379 ttpeak = 2 * d['ang']
2380 # check if peak is in data range to be calculated
2381 if ttpeak - ww/2 > tt.max() or ttpeak + ww/2 < tt.min():
2382 continue
2383 idx = numpy.argwhere(numpy.logical_and(tt > ttpeak - ww/2,
2384 tt < ttpeak + ww/2))
2385 d['conv'].set_parameters(twotheta0_deg=ttpeak)
2386 result = d['conv'].compute_line_profile()
2387 out[idx] += numpy.interp(tt[idx], result.twotheta_deg,
2388 result.peak*d['r'], left=0,
2389 right=0)
2390 else:
2391 # prepare multiprocess calculation
2392 for idx, chunk in enumerate(self.chunks):
2393 run = []
2394 ttpeaks = []
2395 for h in chunk:
2396 ttpeak = 2 * self.data[h]['ang']
2397 ttpeaks.append(ttpeak)
2398 if (ttpeak - ww/2 > tt.max() or
2399 ttpeak + ww/2 < tt.min()):
2400 run.append(False)
2401 else:
2402 run.append(True)
2403 if not self.data[h]['active']:
2404 run[-1] = False
2405 # start calculation in other processes
2406 self.threads[idx][1].put((self.settings, run, ttpeaks))
2407 gotit = set(range(self.nproc))
2408 while gotit:
2409 # receive ready calculations
2410 idx, res = self.output_queue.get(True)
2411 chunk = self.chunks[idx]
2412 for h, r in zip(chunk, res):
2413 if r is None:
2414 continue
2415 else:
2416 ttpeak = 2 * self.data[h]['ang']
2417 mask = numpy.argwhere(
2418 numpy.logical_and(tt > ttpeak - ww/2,
2419 tt < ttpeak + ww/2))
2420
2421 out[mask] += numpy.interp(tt[mask], r.twotheta_deg,
2422 r.peak*self.data[h]['r'],
2423 left=0, right=0)
2424 gotit.discard(idx) # got that result, don't expect more
2425
2426 if config.VERBOSITY >= config.INFO_ALL:
2427 print("XU.Powder.Convolute: exec time=", time.time() - t_start)
2428 return out
2429 else:
2430 print("XU.Powder: not initialized for calculation -> exiting!")
2431 return None
2432
2433 def Calculate(self, twotheta, **kwargs):
2434 """
2435 calculate the powder diffraction pattern including convolution with the
2436 resolution function and map them onto the twotheta positions. This also
2437 performs the calculation of the peak intensities from the internal
2438 material object
2439
2440 Parameters
2441 ----------
2442 twotheta : array-like
2443 two theta values at which the powder pattern should be calculated.
2444 kwargs : dict
2445 additional keyword arguments are passed to the Convolve function
2446
2447 Returns
2448 -------
2449 array-like
2450 output intensity values for the twotheta values given in the input
2451
2452 Notes
2453 -----
2454 Bragg peaks are only included up to tt_cutoff set in the class
2455 constructor!
2456 """
2457 if self._enable_sim:
2458 self.set_sample_parameters()
2459 self.update_powder_lines(self._tt_cutoff)
2460 self.set_window()
2461 return self.Convolve(twotheta, **kwargs)
2462 else:
2463 print("XU.Powder: not initialized for calculation -> exiting!")
2464 return None
2465
2466 def __str__(self):
2467 """
2468 Prints out available information about the material and reflections
2469 """
2470 ostr = "\nPowder diffraction object \n"
2471 ostr += "-------------------------\n"
2472 ostr += self.mat.__repr__() + "\n"
2473 ostr += "Lattice:\n" + self.mat.material.lattice.__str__()
2474 rmax = 0
2475 for d in self.data.values():
2476 if d['r'] > rmax:
2477 rmax = d['r']
2478 ostr += "\nReflections: \n"
2479 ostr += "--------------\n"
2480 ostr += (" h k l | tth | |Q| |"
2481 "Int | Int (%)\n")
2482 ostr += (" ------------------------------------"
2483 "---------------------------\n")
2484 for h, d in sorted(self.data.items(), key=lambda t: t[1]['ang']):
2485 if d['active']:
2486 ostr += ("%15s %8.4f %8.3f %10.2f %10.2f\n"
2487 % (h.__str__(), 2 * d['ang'],
2488 d['qpos'], d['r'], d['r'] / rmax * 100.))
2489 ostr += "Settings: " + str(self.settings)
2490 return ostr
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import numbers
18 from math import sqrt
19
20 import numpy
21 from scipy import interpolate
22
23 from .. import utilities
24 from .powder import PowderDiffraction
25 from .smaterials import PowderList
26
27
28 class PowderModel(object):
29 """
30 Class to help with powder calculations for multiple materials. For basic
31 calculations the Powder class together with the Fundamental parameters
32 approach is used.
33 """
34
35 def __init__(self, *args, **kwargs):
36 """
37 constructor for a powder model. The arguments consist of a PowderList
38 or individual Powder(s). Optional parameters are specified in the
39 keyword arguments.
40
41 Note:
42 After the end-of-use it is advisable to call the `close()` method to
43 cleanup the multiprocessing calculation!
44
45 Parameters
46 ----------
47 args : PowderList or Powders
48 either one PowderList or several Powder objects can be given
49 kwargs : dict
50 optional parameters for the simulation. supported are:
51 fpclass : FP_profile, optional
52 derived class with possible convolver mixins. (default:
53 FP_profile)
54 fpsettings : dict
55 settings dictionaries for the convolvers. Default settings are
56 loaded from the config file.
57 I0 : float, optional
58 scaling factor for the simulation result
59
60 In particular interesting in fpsettings might be:
61 {'displacement': {'specimen_displacement': z-displacement of the sample
62 from the rotation center
63 'zero_error_deg': zero error of the 2theta angle}
64 'absorption': {'sample_thickness': sample thickness (m),
65 'absorption_coefficient': sample's absorption (m^-1)}
66 'axial': {'length_sample': the length of the sample in the axial
67 direction (m)}
68 }
69 """
70 if len(args) == 1 and isinstance(args[0], PowderList):
71 self.materials = args[0]
72 else:
73 self.materials = PowderList('%s List' % self.__class__.__name__,
74 *args)
75 self.I0 = kwargs.pop('I0', 1.0)
76 self.pdiff = []
77 kwargs['enable_simulation'] = True
78 for mat in self.materials:
79 self.pdiff.append(PowderDiffraction(mat, **kwargs))
80
81 # default background
82 self._bckg_type = 'polynomial'
83 self._bckg_pol = [0, ]
84
85 def set_parameters(self, params):
86 """
87 set simulation parameters of all subobjects
88
89 Parameters
90 ----------
91 params : dict
92 settings dictionaries for the convolvers.
93 """
94 # set parameters for each convolver
95 for pd in self.pdiff:
96 pd.update_settings(params)
97
98 def set_lmfit_parameters(self, lmparams):
99 """
100 function to update the settings of this class during an least squares
101 fit
102
103 Parameters
104 ----------
105 lmparams : lmfit.Parameters
106 lmfit Parameters list of sample and instrument parameters
107 """
108 pv = lmparams.valuesdict()
109 settings = dict()
110 h = list(self.pdiff[0].data)[0]
111 fp = self.pdiff[0].data[h]['conv'].convolvers
112 for conv in fp:
113 name = conv[5:]
114 settings[name] = dict()
115
116 self.I0 = pv.pop('primary_beam_intensity', 1)
117 set_splbkg = False
118 spliney = {}
119 for p in pv:
120 if p.startswith('phase_'): # sample phase parameters
121 midx = 0
122 for i, name in enumerate(self.materials.namelist):
123 if p.find(name) > 0:
124 midx = i
125 name = self.materials.namelist[midx]
126 attrname = p[p.find(name) + len(name) + 1:]
127 setattr(self.materials[midx], attrname, pv[p])
128 elif p.startswith('background_coeff'):
129 self._bckg_pol[int(p.split('_')[-1])] = pv[p]
130 elif p.startswith('background_spl_coeff'):
131 set_splbkg = True
132 spliney[int(p.split('_')[-1])] = pv[p]
133 else: # instrument parameters
134 for k in settings:
135 if p.startswith(k):
136 slist = p[len(k) + 1:].split('_')
137 if len(slist) > 2 and slist[-2] == 'item':
138 name = '_'.join(slist[:-2])
139 if slist[-1] == '0':
140 settings[k][name] = []
141 settings[k][name].append(pv[p])
142 else:
143 name = p[len(k) + 1:]
144 settings[k][name] = pv[p]
145 break
146 if set_splbkg:
147 self._bckg_spline = interpolate.InterpolatedUnivariateSpline(
148 self._bckg_spline._data[0],
149 [spliney[k] for k in sorted(spliney)], ext=0)
150 self.set_parameters(settings)
151
152 def create_fitparameters(self):
153 """
154 function to create a fit model with all instrument and sample
155 parameters.
156
157 Returns
158 -------
159 lmfit.Parameters
160 """
161 lmfit = utilities.import_lmfit('XU.PowderModel')
162
163 params = lmfit.Parameters()
164 # sample phase parameters
165 for mat, name in zip(self.materials, self.materials.namelist):
166 for k in mat.__dict__:
167 attr = getattr(mat, k)
168 if isinstance(attr, numbers.Number):
169 params.add('_'.join(('phase', name, k)), value=attr,
170 vary=False)
171
172 # instrument parameters
173 settings = self.pdiff[0].settings
174 for pg in settings:
175 for p in settings[pg]:
176 val = settings[pg][p]
177 if p == 'dominant_wavelength' and pg == 'global':
178 # wavelength must be fit using emission_emiss_wavelength
179 continue
180 if isinstance(val, numbers.Number):
181 params.add('_'.join((pg, p)), value=val, vary=False)
182 elif isinstance(val, (numpy.ndarray, tuple, list)):
183 for j, item in enumerate(val):
184 params.add('_'.join((pg, p, 'item_%d' % j)),
185 value=item, vary=False)
186
187 # other global parameters
188 params.add('primary_beam_intensity', value=self.I0, vary=False)
189 if self._bckg_type == 'polynomial':
190 for i, coeff in enumerate(self._bckg_pol):
191 params.add('background_coeff_%d' % i, value=coeff, vary=False)
192 elif self._bckg_type == 'spline':
193 for i, coeff in enumerate(self._bckg_spline._data[1]):
194 params.add('background_spl_coeff_%d' % i, value=coeff,
195 vary=False)
196 return params
197
198 def set_background(self, btype, **kwargs):
199 """
200 define background as spline or polynomial function
201
202 Parameters
203 ----------
204 btype : {polynomial', 'spline'}
205 background type; Depending on this
206 value the expected keyword arguments differ.
207 kwargs : dict
208 optional keyword arguments
209 x : array-like, optional
210 x-values (twotheta) of the background points (if btype='spline')
211 y : array-like, optional
212 intensity values of the background (if btype='spline')
213 p : array-like, optional
214 polynomial coefficients from the highest degree to the constant
215 term. len of p decides about the degree of the polynomial (if
216 btype='polynomial')
217 """
218 if btype == 'spline':
219 self._bckg_spline = interpolate.InterpolatedUnivariateSpline(
220 kwargs.get('x'), kwargs.get('y'), ext=0)
221 elif btype == 'polynomial':
222 self._bckg_pol = list(kwargs.get('p'))
223 else:
224 raise ValueError("btype must be either 'spline' or 'polynomial'")
225 self._bckg_type = btype
226
227 def simulate(self, twotheta, **kwargs):
228 """
229 calculate the powder diffraction pattern of all materials and sum the
230 results based on the relative volume of the materials.
231
232 Parameters
233 ----------
234 twotheta : array-like
235 positions at which the powder pattern should be evaluated
236 kwargs : dict
237 optional keyword arguments
238 background : array-like
239 an array of background values (same shape as twotheta) if no
240 background is given then the background is calculated as previously
241 set by the set_background function or is 0.
242
243
244 further keyword arguments are passed to the Convolve function of of the
245 PowderDiffraction objects
246
247 Returns
248 -------
249 array-like
250 summed powder diffraction intensity of all materials present in the
251 model
252 """
253 inte = numpy.zeros_like(twotheta)
254 background = kwargs.pop('background', None)
255 if background is None:
256 if self._bckg_type == 'spline':
257 background = self._bckg_spline(twotheta)
258 else:
259 background = numpy.polyval(self._bckg_pol, twotheta)
260 totalvol = sum(pd.mat.volume for pd in self.pdiff)
261 for pd in self.pdiff:
262 inte += pd.Calculate(twotheta, **kwargs) * pd.mat.volume / totalvol
263 return self.I0 * inte + background
264
265 def fit(self, params, twotheta, data, std=None, maxfev=200):
266 """
267 make least squares fit with parameters supplied by the user
268
269 Parameters
270 ----------
271 params : lmfit.Parameters
272 object with all parameters set as intended by the user
273 twotheta : array-like
274 angular values for the fit
275 data : array-like
276 experimental intensities for the fit
277 std : array-like
278 standard deviation of the experimental data. if 'None' the sqrt of
279 the data will be used
280 maxfev: int
281 maximal number of simulations during the least squares refinement
282
283 Returns
284 -------
285 lmfit.MinimizerResult
286 """
287 lmfit = utilities.import_lmfit('XU.PowderModel')
288
289 def residual(pars, tt, data, weight):
290 """
291 residual function for lmfit Minimizer routine
292
293 Parameters
294 ----------
295 pars : lmfit.Parameters
296 fit Parameters
297 tt : array-like
298 array of twotheta angles
299 data : array-like
300 experimental data, same shape as tt
301 eps : array-like
302 experimental error bars, shape as tt
303 """
304 # set parameters in this instance
305 self.set_lmfit_parameters(pars)
306
307 # run simulation
308 model = self.simulate(tt)
309 return (model - data) * weight
310
311 if std is None:
312 weight = numpy.reciprocal(numpy.sqrt(data))
313 else:
314 weight = numpy.reciprocal(std)
315 weight[numpy.isinf(weight)] = 1
316 self.minimizer = lmfit.Minimizer(residual, params,
317 fcn_args=(twotheta, data, weight))
318 fitres = self.minimizer.minimize(maxfev=maxfev)
319 self.set_lmfit_parameters(fitres.params)
320 return fitres
321
322 def close(self):
323 for pd in self.pdiff:
324 pd.close()
325
326 def __str__(self):
327 """
328 string representation of the PowderModel
329 """
330 ostr = "PowderModel {\n"
331 ostr += "I0: %f\n" % self.I0
332 ostr += str(self.materials)
333 ostr += "}"
334 return ostr
335
336
337 def Rietveld_error_metrics(exp, sim, weight=None, std=None,
338 Nvar=0, disp=False):
339 """
340 calculates common error metrics for Rietveld refinement.
341
342 Parameters
343 ----------
344 exp : array-like
345 experimental datapoints
346 sim : array-like
347 simulated data
348 weight : array-like, optional
349 weight factor in the least squares sum. If it is None the weight is
350 estimated from the counting statistics of 'exp'
351 std : array-like, optional
352 standard deviation of the experimental data. alternative way of
353 specifying the weight factor. when both are given weight overwrites
354 std!
355 Nvar : int, optional
356 number of variables in the refinement
357 disp : bool, optional
358 flag to tell if a line with the calculated values should be printed.
359
360 Returns
361 -------
362 M, Rp, Rwp, Rwpexp, chi2: float
363 """
364 if weight is None and std is None:
365 weight = numpy.reciprocal(exp)
366 elif weight is None:
367 weight = numpy.reciprocal(std**2)
368 weight[numpy.isinf(weight)] = 1
369 M = numpy.sum((exp - sim)**2 * weight)
370 Rp = numpy.sum(numpy.abs(exp - sim))/numpy.sum(exp)
371 Rwp = sqrt(M / numpy.sum(weight * exp**2))
372 chi2 = M / (len(exp) - Nvar)
373 Rwpexp = Rwp / sqrt(chi2)
374 if disp:
375 print('Rp=%.4f Rwp=%.4f Rwpexp=%.4f chi2=%.4f'
376 % (Rp, Rwp, Rwpexp, chi2))
377 return M, Rp, Rwp, Rwpexp, chi2
378
379
380 def plot_powder(twotheta, exp, sim, mask=None, scale='sqrt', fig='XU:powder',
381 show_diff=True, show_legend=True, labelexp='experiment',
382 labelsim='simulate', formatexp='k-.', formatsim='r-'):
383 """
384 Convenience function to plot the comparison between experimental and
385 simulated powder diffraction data
386
387 Parameters
388 ----------
389 twotheta : array-like
390 angle values used for the x-axis of the plot (deg)
391 exp : array-like
392 experimental data (same shape as twotheta). If None only the simulation
393 and no difference will be plotted
394 sim : array-like
395 simulated data
396 mask : array-like, optional
397 mask to reduce the twotheta values to the be used as x-coordinates of
398 sim
399 scale : {'linear', 'sqrt', 'log'}, optional
400 string specifying the scale of the y-axis.
401 fig : str or int, optional
402 matplotlib figure name (figure will be cleared!)
403 show_diff : bool, optional
404 flag to specify if a difference curve should be shown
405 show_legend: bool, optional
406 flag to specify if a legend should be shown
407 """
408 plot, plt = utilities.import_matplotlib_pyplot('XU.simpack')
409 if not plot:
410 return
411
412 plt.figure(fig, figsize=(10, 7))
413 plt.clf()
414 ax = plt.subplot(111)
415 lines = []
416 if exp is not None:
417 lines.append(ax.plot(twotheta, exp, formatexp, label=labelexp)[0])
418 if mask is None:
419 mask = numpy.ones_like(twotheta, dtype=numpy.bool)
420 lines.append(ax.plot(twotheta[mask], sim, formatsim, label=labelsim)[0])
421
422 if show_diff:
423 # plot error between simulation and experiment
424 if exp is not None:
425 lines.append(ax.plot(twotheta[mask], exp[mask]-sim, '.-',
426 color='0.5', label='difference')[0])
427
428 plt.xlabel('2Theta (deg)')
429 plt.ylabel('Intensity')
430 plt.figlegend(lines, [l.get_label() for l in lines], loc='upper right',
431 frameon=True)
432 ax.set_yscale(scale)
433 plt.tight_layout()
434 return lines
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2015-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import collections.abc
18 import copy
19 import numbers
20
21 import numpy
22
23 from .. import utilities
24 from ..materials import Crystal, PseudomorphicMaterial
25 from ..math import CoordinateTransform, Transform
26
27
28 def _multiply(a, b):
29 """
30 implement multiplication of SMaterial and MaterialList with integer
31 """
32 if not isinstance(b, int):
33 raise TypeError("unsupported operand type(s) for *: "
34 "'%s' and '%s'" % (type(a), type(b)))
35 if b < 1:
36 raise ValueError("multiplication factor needs to be positive!")
37 m = MaterialList('%d * (%s)' % (b, a.name), a)
38 for i in range(b-1):
39 m.append(copy.deepcopy(a))
40 return m
41
42
43 class SMaterial(object):
44 """
45 Simulation Material. Extends the xrayutilities Materials by properties
46 needed for simulations
47 """
48
49 def __init__(self, material, **kwargs):
50 """
51 initialize a simulation material by specifiying its Material and
52 optional other properties
53
54 Parameters
55 ----------
56 material : Material (Crystal, or Amorphous)
57 Material object containing optical/crystal properties of for the
58 simulation; a deepcopy is used internally.
59 kwargs : dict
60 optional properties of the material needed for the simulation
61 """
62 self.name = utilities.makeNaturalName(material.name, check=True)
63 self.material = copy.deepcopy(material)
64 for kw in kwargs:
65 setattr(self, kw, kwargs[kw])
66
67 @property
68 def material(self):
69 return self._material
70
71 @material.setter
72 def material(self, material):
73 self._material = material
74 if isinstance(material, Crystal):
75 self._structural_params = []
76 # make lattice parameters attributes
77 for param, value in material.lattice.free_parameters.items():
78 self._structural_params.append(param)
79 setattr(self, param, value)
80 # make attributes from atom positions
81 for i, wp in enumerate(material.lattice._wbase):
82 if wp[1][1] is not None:
83 for j, p in enumerate(wp[1][1]):
84 name = '_'.join(('at%d' % i, wp[0].name,
85 wp[1][0], str(j), 'pos'))
86 self._structural_params.append(name)
87 setattr(self, name, p)
88 # make attributes from atom occupations
89 for i, wp in enumerate(material.lattice._wbase):
90 name = '_'.join(('at%d' % i, wp[0].name,
91 wp[1][0], 'occupation'))
92 self._structural_params.append(name)
93 setattr(self, name, wp[2])
94 # make attributes from Debye waller exponents
95 for i, wp in enumerate(material.lattice._wbase):
96 name = '_'.join(('at%d' % i, wp[0].name, wp[1][0], 'biso'))
97 self._structural_params.append(name)
98 setattr(self, name, wp[3])
99
100 def __setattr__(self, name, value):
101 object.__setattr__(self, name, value)
102 if hasattr(self, 'material'):
103 if isinstance(self.material, Crystal):
104 if name in self.material.lattice.free_parameters:
105 setattr(self.material.lattice, name, value)
106 if name.startswith('at'):
107 nsplit = name.split('_')
108 idx = int(nsplit[0][2:])
109 wp = self.material.lattice._wbase[idx]
110 # wyckoff position parameter
111 if nsplit[-1] == 'pos':
112 pidx = int(nsplit[-2])
113 wyckpos = (wp[1][0], list(wp[1][1]))
114 wyckpos[1][pidx] = value
115 self.material.lattice._wbase[idx] = (wp[0], wyckpos,
116 wp[2], wp[3])
117 # site occupation
118 if nsplit[-1] == 'occupation':
119 self.material.lattice._wbase[idx] = (wp[0], wp[1],
120 value, wp[3])
121 # site DW exponent
122 if nsplit[-1] == 'biso':
123 self.material.lattice._wbase[idx] = (wp[0], wp[1],
124 wp[2], value)
125
126 def __radd__(self, other):
127 return MaterialList('%s + %s' % (other.name, self.name), other, self)
128
129 def __add__(self, other):
130 return MaterialList('%s + %s' % (self.name, other.name), self, other)
131
132 def __mul__(self, other):
133 return _multiply(self, other)
134
135 __rmul__ = __mul__
136
137 def __repr__(self):
138 s = '{cls}-{name} ('.format(name=self.material.name,
139 cls=self.__class__.__name__)
140 for k in self.__dict__:
141 if k not in ('material', 'name'):
142 v = getattr(self, k)
143 if isinstance(v, numbers.Number):
144 s += '{key}: {value:.5g}, '.format(key=k, value=v)
145 else:
146 s += '{key}: {value}, '.format(key=k, value=v)
147 return s + ')'
148
149
150 class MaterialList(collections.abc.MutableSequence):
151 """
152 class representing the basics of a list of materials for simulations within
153 xrayutilities. It extends the built in list type.
154 """
155
156 def __init__(self, name, *args):
157 if not isinstance(name, str):
158 raise TypeError("'name' argument must be a string")
159 self.name = name
160 self.list = list()
161 self.namelist = list()
162 self.extend(list(args))
163
164 def check(self, v):
165 if not isinstance(v, SMaterial):
166 raise TypeError('%s can only contain SMaterial as entries!'
167 % self.__class__.__name__)
168
169 def _set_unique_name(self, v):
170 if v.name in self.namelist:
171 splitname = v.name.split('_')
172 if len(splitname) > 1:
173 try:
174 num = int(splitname[-1])
175 basename = '_'.join(splitname[:-1])
176 except ValueError:
177 num = 1
178 basename = v.name
179 else:
180 num = 1
181 basename = v.name
182 name = '{name}_{num:d}'.format(name=basename, num=num)
183 while name in self.namelist:
184 num += 1
185 name = '{name}_{num:d}'.format(name=basename, num=num)
186 v.name = name
187 return v.name
188
189 def __len__(self): return len(self.list)
190
191 def __getitem__(self, i): return self.list[i]
192
193 def __delitem__(self, i): del self.list[i]
194
195 def __setitem__(self, i, v):
196 self.check(v)
197 self.namelist[i] = self._set_unique_name(v)
198 self.list[i] = v
199
200 def insert(self, i, v):
201 if isinstance(v, MaterialList):
202 vs = v
203 else:
204 vs = [v, ]
205 for j, val in enumerate(vs):
206 self.check(val)
207 self.namelist.insert(i+j, self._set_unique_name(val))
208 self.list.insert(i+j, val)
209
210 def __radd__(self, other):
211 ml = MaterialList('%s + %s' % (other.name, self.name))
212 ml.append(other)
213 ml.append(self)
214 return ml
215
216 def __add__(self, other):
217 ml = MaterialList('%s + %s' % (self.name, other.name))
218 ml.append(self)
219 ml.append(other)
220 return ml
221
222 def __mul__(self, other):
223 return _multiply(self, other)
224
225 __rmul__ = __mul__
226
227 def __str__(self):
228 layer = ',\n '.join([str(entry) for entry in self.list])
229 s = '{name} [\n {layer}\n]'.format(name=self.name, layer=layer)
230 return s
231
232 def __repr__(self):
233 return self.name
234
235
236 class Layer(SMaterial):
237 """
238 Object describing part of a thin film sample. The properties of a layer
239 are :
240
241 Attributes
242 ----------
243 material : Material (Crystal or Amorhous)
244 an xrayutilties material describing optical and crystal properties of
245 the thin film
246 thickness : float
247 film thickness in Angstrom
248 """
249
250 _valid_init_kwargs = {'roughness': 'root mean square roughness',
251 'density': 'density in kg/m^3',
252 'relaxation': 'degree of relaxation',
253 'lat_correl': 'lateral correlation length'}
254
255 def __init__(self, material, thickness, **kwargs):
256 """
257 constructor for the material saving its properties
258
259 Parameters
260 ----------
261 material : Material (Crystal or Amorhous)
262 an xrayutilties material describing optical and crystal properties
263 of the thin film
264 thickness : float
265 film thickness in Angstrom
266 kwargs : dict
267 optional keyword arguments with further layer properties.
268 roughness : float, optional
269 root mean square roughness of the top interface in Angstrom
270 density : float, optional
271 density of the material in kg/m^3; If not specified the density of
272 the material will be used.
273 relaxation : float, optional
274 the degree of relaxation in case of crystalline thin films
275 lat_correl : float, optional
276 the lateral correlation length for diffuse reflectivity
277 calculations
278 """
279 utilities.check_kwargs(kwargs, self._valid_init_kwargs,
280 self.__class__.__name__)
281 kwargs['thickness'] = thickness
282 super().__init__(material, **kwargs)
283
284 def __getattr__(self, name):
285 """
286 return default values for properties if they were not set
287 """
288 if name == "density":
289 return self.material.density
290 elif name == "roughness":
291 return 0
292 elif name == "lat_correl":
293 return numpy.inf
294 elif name == "relaxation":
295 return 1
296 else:
297 return super().__getattribute__(name)
298
299
300 class LayerStack(MaterialList):
301 """
302 extends the built in list type to enable building a stack of Layer by
303 various methods.
304 """
305
306 def check(self, v):
307 if not isinstance(v, Layer):
308 raise TypeError('LayerStack can only contain Layer as entries!')
309
310
311 class CrystalStack(LayerStack):
312 """
313 extends the built in list type to enable building a stack of crystalline
314 Layers by various methods.
315 """
316
317 def check(self, v):
318 super().check(v)
319 if not isinstance(v.material, Crystal):
320 raise TypeError('CrystalStack can only contain crystalline Layers'
321 ' as entries!')
322
323
324 class GradedLayerStack(CrystalStack):
325 """
326 generates a sequence of layers with a gradient in chemical composition
327 """
328
329 def __init__(self, alloy, xfrom, xto, nsteps, thickness, **kwargs):
330 """
331 constructor for a graded buffer of the material 'alloy' with chemical
332 composition from 'xfrom' to 'xto' with 'nsteps' number of sublayers.
333 The total thickness of the graded buffer is 'thickness'
334
335 Parameters
336 ----------
337 alloy : function
338 Alloy function which allows to create a material with chemical
339 composition 'x' by alloy(x)
340 xfrom, xto : float
341 chemical composition from the bottom to top
342 nsteps : int
343 number of sublayers in the graded buffer
344 thickness : float
345 total thickness of the graded stack
346 """
347 nfrom = alloy(xfrom).name
348 nto = alloy(xto).name
349 super().__init__('(' + nfrom + '-' + nto + ')')
350 for x in numpy.linspace(xfrom, xto, nsteps):
351 layer = Layer(alloy(x), thickness/nsteps, **kwargs)
352 self.append(layer)
353
354
355 class PseudomorphicStack001(CrystalStack):
356 """
357 generate a sequence of pseudomorphic crystalline Layers. Surface
358 orientation is assumed to be 001 and materials must be cubic/tetragonal.
359 """
360 trans = Transform(numpy.identity(3))
361
362 def make_epitaxial(self, i):
363 layer = self.list[i]
364 if i == 0:
365 return layer
366 psub = self.list[i-1].material
367 mpseudo = PseudomorphicMaterial(psub, layer.material, layer.relaxation,
368 trans=self.trans)
369 self.list[i].material = mpseudo
370
371 def __delitem__(self, i):
372 del self.list[i]
373 for j in range(i, len(self)):
374 self.make_epitaxial(j)
375
376 def __setitem__(self, i, v):
377 self.check(v)
378 self.namelist[i] = self._set_unique_name(v)
379 self.list[i] = v
380 for j in range(i, len(self)):
381 self.make_epitaxial(j)
382
383 def insert(self, i, v):
384 if isinstance(v, MaterialList):
385 vs = v
386 else:
387 vs = [v, ]
388 for j, val in enumerate(vs):
389 self.check(val)
390 self.namelist.insert(i+j, self._set_unique_name(val))
391 self.list.insert(i+j, copy.copy(val))
392 for k in range(i+j, len(self)):
393 self.make_epitaxial(k)
394
395
396 class PseudomorphicStack111(PseudomorphicStack001):
397 """
398 generate a sequence of pseudomorphic crystalline Layers. Surface
399 orientation is assumed to be 111 and materials must be cubic.
400 """
401 trans = CoordinateTransform((1, -1, 0), (1, 1, -2), (1, 1, 1))
402
403
404 class Powder(SMaterial):
405 """
406 Object describing part of a powder sample. The properties of a powder
407 are:
408
409 Attributes
410 ----------
411 material : Crystal
412 an xrayutilties material (Crystal) describing optical and crystal
413 properties of the powder
414 volume : float
415 powder's volume (in pseudo units, since only the relative volume enters
416 the calculation)
417
418 crystallite_size_lor : float, optional
419 Lorentzian crystallite size fwhm (m)
420 crystallite_size_gauss : float, optional
421 Gaussian crystallite size fwhm (m)
422 strain_lor : float, optional
423 extra peak width proportional to tan(theta)
424 strain_gauss : float, optional
425 extra peak width proportional to tan(theta)
426 """
427
428 _valid_init_kwargs = {'crystallite_size_lor': 'Lorentzian cryst. size',
429 'crystallite_size_gauss': 'Gaussian cryst. size',
430 'strain_lor': 'microstrain broadening',
431 'strain_gauss': 'microstrain broadening'}
432
433 def __init__(self, material, volume, **kwargs):
434 """
435 constructor for the material saving its properties
436
437 Parameters
438 ----------
439 material : Crystal
440 an xrayutilties material (Crystal) describing optical and crystal
441 properties of the powder
442 volume : float
443 powder's volume (in pseudo units, since only the relative volume
444 enters the calculation)
445 kwargs : dict
446 optional keyword arguments with further powder properties.
447 crystallite_size_lor : float, optional
448 Lorentzian crystallite size fwhm (m)
449 crystallite_size_gauss : float, optional
450 Gaussian crystallite size fwhm (m)
451 strain_lor, strain_gauss : float, optional
452 extra peak width proportional to tan(theta);
453 typically interpreted as microstrain broadening
454 """
455 utilities.check_kwargs(kwargs, self._valid_init_kwargs,
456 self.__class__.__name__)
457 kwargs['volume'] = volume
458 super().__init__(material, **kwargs)
459
460
461 class PowderList(MaterialList):
462 """
463 extends the built in list type to enable building a list of Powder
464 by various methods.
465 """
466
467 def check(self, v):
468 if not isinstance(v, Powder):
469 raise TypeError('PowderList can only contain Powder as entries!')
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2011 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 xrayutilities utilities contains a conglomeration of useful functions
19 which do not fit into one of the other files
20 """
21
22 import numpy
23
24 from . import config
25 from .utilities_noconf import *
26
27
28 def import_matplotlib_pyplot(funcname='XU'):
29 """
30 lazy import function of matplotlib.pyplot
31
32 Parameters
33 ----------
34 funcname : str
35 identification string of the calling function
36
37 Returns
38 -------
39 flag : bool
40 the flag is True if the loading was successful and False otherwise.
41 pyplot
42 On success pyplot is the matplotlib.pyplot package.
43 """
44 try:
45 from matplotlib import pyplot as plt
46 from .mpl_helper import SqrtAllowNegScale
47 return True, plt
48 except ImportError:
49 if config.VERBOSITY >= config.INFO_LOW:
50 print("%s: Warning: plot functionality not available" % funcname)
51 return False, None
52
53
54 def import_lmfit(funcname='XU'):
55 """
56 lazy import function for lmfit
57 """
58 try:
59 import lmfit
60 return lmfit
61 except ImportError:
62 raise ImportError("%s: Fitting of models needs the lmfit package "
63 "(https://pypi.python.org/pypi/lmfit)" % funcname)
64
65
66 def maplog(inte, dynlow="config", dynhigh="config"):
67 """
68 clips values smaller and larger as the given bounds and returns the log10
69 of the input array. The bounds are given as exponent with base 10 with
70 respect to the maximum in the input array. The function is implemented in
71 analogy to J. Stangl's matlab implementation.
72
73 Parameters
74 ----------
75 inte : ndarray
76 numpy.array, values to be cut in range
77 dynlow : float, optional
78 10^(-dynlow) will be the minimum cut off
79 dynhigh : float, optional
80 10^(-dynhigh) will be the maximum cut off
81
82 Returns
83 -------
84 ndarray
85 numpy.array of the same shape as inte, where values smaller/larger than
86 10^(-dynlow, dynhigh) were replaced by 10^(-dynlow, dynhigh)
87
88 Examples
89 --------
90 >>> lint = maplog(int, 5, 2)
91 """
92 if dynlow == "config":
93 dynlow = config.DYNLOW
94 if dynhigh == "config":
95 dynhigh = config.DYNHIGH
96
97 if inte.max() <= 0.0:
98 raise ValueError("XU.maplog: only negativ or zero values given. "
99 "Log is not defined!")
100 ma = inte.max() * 10 ** (-1*dynhigh) # upper bound
101 mi = inte.max() * 10 ** (-1*dynlow) # lower bound
102
103 return numpy.log10(numpy.minimum(numpy.maximum(inte, mi), ma))
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 xrayutilities utilities contains a conglomeration of useful functions
19 this part of utilities does not need the config class
20 """
21
22 import abc
23 import numbers
24 import os.path
25 import re
26
27 import numpy
28 import scipy.constants
29
30 from .exception import InputError
31
32 try: # works in Python >3.4
33 ABC = abc.ABC
34 except AttributeError: # Python 2.7
35 ABC = abc.ABCMeta('ABC', (object, ), {'__slots__': ()})
36
37
38 __all__ = ['ABC', 'check_kwargs', 'clear_bit', 'en2lam', 'energies', 'energy',
39 'exchange_filepath', 'exchange_path', 'is_valid_variable_name',
40 'lam2en', 'makeNaturalName', 'set_bit', 'wavelength']
41
42 energies = {
43 'CuKa1': 8047.82310,
44 'CuKa2': 8027.9117,
45 'CuKa12': 8041.18,
46 'CuKb': 8905.337,
47 'MoKa1': 17479.374,
48 'CoKa1': 6930.32,
49 'CoKa2': 6915.30}
50 # wavelength values from International Tables of Crystallography:
51 # Vol C, 2nd Ed. page 203
52 # CuKa1: 1.54059292(45) the value in bracket is the uncertainty
53 # CuKa2: 1.5444140(19)
54 # CuKa12: mixture 2:1 a1 and a2
55 # CuKb: 1.392246(14)
56 # MoKa1: 0.70931713(41)
57 # Xray data booklet:
58 # CoKa1
59 # CoKa2
60
61
62 def set_bit(f, offset):
63 """
64 sets the bit at an offset
65 """
66 mask = 1 << offset
67 return(f | mask)
68
69
70 def clear_bit(f, offset):
71 """
72 clears the bet at an offset
73 """
74 mask = ~(1 << offset)
75 return(f & mask)
76
77
78 def lam2en(inp):
79 """
80 converts the input wavelength in Angstrom to an energy in eV
81
82 Parameters
83 ----------
84 inp : float or str
85 wavelength in Angstrom
86
87 Returns
88 -------
89 float
90 energy in eV
91
92 Examples
93 --------
94 >>> energy = lam2en(1.5406)
95 """
96 # E(eV) = h*c/(e * lambda(A)) *1e10
97 inp = wavelength(inp)
98 c = scipy.constants
99 out = c.h * c.speed_of_light / (c.e * inp) * 1e10
100 return out
101
102
103 def en2lam(inp):
104 """
105 converts the input energy in eV to a wavelength in Angstrom
106
107 Parameters
108 ----------
109 inp : float or str
110 energy in eV
111
112 Returns
113 -------
114 float
115 wavlength in Angstrom
116
117 Examples
118 --------
119 >>> wavelength = en2lam(8048)
120 """
121 # lambda(A) = h*c/(e * E(eV)) *1e10
122 inp = energy(inp)
123 c = scipy.constants
124 out = c.h * c.speed_of_light / (c.e * inp) * 1e10
125 return out
126
127
128 def energy(en):
129 """
130 convert common energy names to energies in eV
131
132 so far this works with CuKa1, CuKa2, CuKa12, CuKb, MoKa1
133
134 Parameters
135 ----------
136 en : float, array-like or str
137 energy either as scalar or array with value in eV, which will be
138 returned unchanged; or string with name of emission line
139
140 Returns
141 -------
142 float or array-like
143 energy in eV
144 """
145
146 if isinstance(en, numbers.Number):
147 return numpy.double(en)
148 elif isinstance(en, (numpy.ndarray, list, tuple)):
149 return numpy.asarray(en)
150 elif isinstance(en, str):
151 return energies[en]
152 else:
153 raise InputError("wrong type for argument en")
154
155
156 def wavelength(wl):
157 """
158 convert common energy names to energies in eV
159
160 so far this works with CuKa1, CuKa2, CuKa12, CuKb, MoKa1
161
162 Parameters
163 ----------
164 wl : float, array-like or str
165 wavelength; If scalar or array the wavelength in Angstrom will be
166 returned unchanged, string with emission name is converted to
167 wavelength
168
169 Returns
170 -------
171 float or array-like
172 wavelength in Angstrom
173 """
174
175 if isinstance(wl, numbers.Number):
176 return numpy.double(wl)
177 elif isinstance(wl, (numpy.ndarray, list, tuple)):
178 return numpy.asarray(wl)
179 elif isinstance(wl, str):
180 return en2lam(energies[wl])
181 else:
182 raise InputError("wrong type for argument wavelength")
183
184
185 def exchange_path(orig, new, keep=0, replace=None):
186 """
187 function to exchange the root of a path with the option of keeping the
188 inner directory structure. This for example includes such a conversion
189 /dir_a/subdir/images/sample -> /home/user/data/images/sample
190 where the two innermost directory names are kept (keep=2), or equally
191 the three outer most are replaced (replace=3). One can either give keep,
192 or replace, with replace taking preference if both are given. Note that
193 replace=1 on Linux/Unix replaces only the root for absolute paths.
194
195 Parameters
196 ----------
197 orig : str
198 original path which should be replaced by the new path
199 new : str
200 new path which should be used instead
201 keep : int, optional
202 number of inner most directory names which should be kept the same in
203 the output (default = 0)
204 replace : int, optional
205 number of outer most directory names which should be replaced in the
206 output (default = None)
207
208 Returns
209 -------
210 str
211 directory path string
212
213 Examples
214 --------
215 >>> exchange_path('/dir_a/subdir/img/sam', '/home/user/data', keep=2)
216 '/home/user/data/img/sam'
217 """
218 subdirs = []
219 o = orig
220 if replace is None:
221 for i in range(keep):
222 o, s = os.path.split(o)
223 subdirs.append(s)
224 out = new
225 subdirs.reverse()
226 for s in subdirs:
227 out = os.path.join(out, s)
228 else:
229 while True:
230 o, s = os.path.split(o)
231 if not s:
232 subdirs.append(o)
233 break
234 elif not o:
235 subdirs.append(s)
236 break
237 else:
238 subdirs.append(s)
239 subdirs.reverse()
240 out = new
241 for s in subdirs[replace:]:
242 out = os.path.join(out, s)
243 return out
244
245
246 def exchange_filepath(orig, new, keep=0, replace=None):
247 """
248 function to exchange the root of a filename with the option of keeping the
249 inner directory structure. This for example includes such a conversion
250 /dir_a/subdir/sample/file.txt -> /home/user/data/sample/file.txt
251 where the innermost directory name is kept (keep=1), or equally
252 the three outer most are replaced (replace=3). One can either give keep,
253 or replace, with replace taking preference if both are given. Note that
254 replace=1 on Linux/Unix replaces only the root for absolute paths.
255
256 Parameters
257 ----------
258 orig : str
259 original filename which should have its data root replaced
260 new : str
261 new path which should be used instead
262 keep : int, optional
263 number of inner most directory names which should be kept the same in
264 the output (default = 0)
265 replace : int, optional
266 number of outer most directory names which should be replaced in the
267 output (default = None)
268
269 Returns
270 -------
271 str
272 filename string
273
274 Examples
275 --------
276 >>> exchange_filepath('/dir_a/subdir/sam/file.txt', '/data', 1)
277 '/data/sam/file.txt'
278 """
279 if new:
280 if replace is None:
281 return exchange_path(orig, new, keep+1)
282 else:
283 return exchange_path(orig, new, replace=replace)
284 else:
285 return orig
286
287
288 def makeNaturalName(name, check=False):
289 ret = re.sub('[^0-9a-zA-Z]', '_', name.strip())
290 isvalid = is_valid_variable_name(ret)
291 if not check or isvalid:
292 return ret
293 elif not isvalid:
294 raise ValueError("'{}' is not valid variable name".format(ret))
295
296
297 def is_valid_variable_name(name):
298 return name.isidentifier()
299
300
301 def check_kwargs(kwargs, valid_kwargs, identifier):
302 """
303 Raises an TypeError if kwargs included a key which is not in valid_kwargs.
304
305 Parameters
306 ----------
307 kwargs : dict
308 keyword arguments dictionary
309 valid_kwargs : dict
310 dictionary with valid keyword arguments and their description
311 identifier : str
312 string to identifier the caller of this function
313 """
314 desc = ', '.join(["'%s': %s" % (k, d) for k, d in valid_kwargs.items()])
315 for k in kwargs:
316 if k not in valid_kwargs:
317 raise TypeError("%s: unknown keyword argument ('%s') given; "
318 "allowed are %s" % (identifier, k, desc))
0 # XRAYUTILITIES global default configuration
1 # default values for some properties of xrayutilities may be set
2 # the syntax follows the one of the ConfigParser Python module,
3 # which is similar to .ini files
4
5 # begin of xrayutilities configuration
6 [xrayutilities]
7
8 # verbosity level of information and debugging outputs
9 # 0: no output
10 # 1: very import notes for users
11 # 2: less import notes for users (e.g. intermediate results)
12 # 3: debuging output (e.g. print everything, which could be interesing)
13 # levels can be changed in the config file as well
14 verbosity = 1
15
16 # verbosity level borders
17 info_low = 1
18 info_all = 2
19 debug = 3
20
21 # default wavelength in Angstrom
22 wavelength = CuKa1
23
24 # default energy in eV
25 # if energy is given wavelength settings will be ignored
26 # energy = CuKa1
27
28 # number of threads to use in parallel sections of the code
29 nthreads = 0
30 # 0: the maximum number of available threads will be used
31 # (as returned by omp_get_max_threads())
32 # n: n-threads will be used
33
34 # maplog dynlow
35 # at 10^(-dynlow) will be the minimum cut off of the maplog routine
36 dynlow = 6
37 # maplog dyn high
38 # at 10^(-dynhigh) will be the maximum cut off of the maplog routine
39 dynhigh = 0
40
41 # boundary to neglect things in error checks (example the scalar product,
42 # of to vectors, which are supposed to be orthogonal)
43 epsilon = 1e-8
44
45 dbname = elements.db
46
47 # kappa-geometry specifications
48 # direction specifies the direction into which the rotation axis of the
49 # kappa circle is tilted at zero positions of all the gradles
50 # e.g. look at http://en.wikipedia.org/wiki/File:Kappa_goniometer_animation.ogg
51 # assume the following coordinate system and zero angles at the beginning of
52 # the movie: x downstream, y backwards/away from the view, z upwards
53 # the rotation axis of kappa rotation is in the "zy" plane; ~60degree tilted
54 # from z. note that the use of zy to specify that the 60degree are measured
55 # from the z-direction rotation is positive towards y direction
56 kappa_plane = zy
57 kappa_angle = -60
58
59 [powder]
60 # anglemode 'd' is currently not supported by most of the code in xrayutilities
61 anglemode = twotheta
62 oversampling = 4
63 gaussian_smoother_bins_sigma = 1.0
64 window_width = 20
65
66 [powder.global]
67 diffractometer_radius = 300e-3
68 equatorial_divergence_deg = 0.5
69
70 [powder.emission]
71 emiss_wavelengths = ('CuKa1', 'CuKa2')
72 emiss_intensities = (1.0, 0.5)
73 emiss_gauss_widths = (3e-14, 3e-14)
74 emiss_lor_widths = (3e-14, 3e-14)
75
76 [powder.axial]
77 axDiv = full
78 slit_length_source = 8.001e-3
79 slit_length_target = 8e-3
80 length_sample = 10e-3
81 angI_deg = 2.5
82 angD_deg = 2.5
83 n_integral_points = 10
84
85 [powder.absorption]
86 # absorption coefficient in m^{-1}
87 absorption_coefficient = 1e5
88
89 [powder.si_psd]
90 # bounds of solid state detector bounds: e.g. (0, 32e-3)
91 si_psd_window_bounds = None
92
93 [powder.receiver_slit]
94 slit_width = 55e-6
95
96 [powder.tube_tails]
97 main_width = 200e-6
98 tail_left = -1e-3
99 tail_right = 1e-3
100 tail_intens = 0.001
0 Metadata-Version: 2.1
1 Name: xrayutilities
2 Version: 1.6.0
3 Summary: package for x-ray diffraction data evaluation
4 Home-page: http://xrayutilities.sourceforge.net
5 Author: Eugen Wintersberger, Dominik Kriegner
6 Author-email: eugen.wintersberger@desy.de, dominik.kriegner@gmail.com
7 Maintainer: Dominik Kriegner
8 Maintainer-email: dominik.kriegner@gmail.com
9 License: GPLv2
10 Description: xrayutilities
11 =============
12
13 [![Build
14 Status Travis CI](https://travis-ci.com/dkriegner/xrayutilities.svg?branch=master)](https://travis-ci.com/dkriegner/xrayutilities)
15 [![Build Status AppVeyor](https://ci.appveyor.com/api/projects/status/t8cb5jj0atklxay3/branch/master?svg=true)](https://ci.appveyor.com/project/dkriegner/xrayutilities)
16
17
18 xrayutilities is a collection of scripts used to analyze and simulate x-ray
19 diffraction data. It consists of a Python package and several routines coded
20 in C. For analysis the package is especially useful for the reciprocal space
21 conversion of diffraction data taken with linear and area detectors. For
22 simulations code for X-ray reflectivity, kinematical and dynamical diffraction
23 simulation of crystal truncation rods as well as fundamental parameters powder
24 diffraction is included.
25
26
27 Copyright (C) 2009-2020 Dominik Kriegner <dominik.kriegner@gmail.com>
28
29 Copyright (C) 2009-2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
30
31
32 Mailing list and issue tracker
33 ------------------------------
34
35 To get in touch with us or report an issue please use the mailing list
36 (https://sourceforge.net/p/xrayutilities/mailman/xrayutilities-users/) or the
37 Github issue tracker (https://github.com/dkriegner/xrayutilities/issues). When
38 you want to follow announcements of major changes or new releases its
39 recommended to [sign up for the mailing
40 list](https://sourceforge.net/projects/xrayutilities/lists/xrayutilities-users)
41
42
43 Contents
44 --------
45
46 * *examples*: directory with example scripts and configurations
47 * *xrayutilities*: directory with the sources for the Python package
48 * *tests*: directory with the unittest scripts
49 * *setup.py*: distutils install script used for the package installation
50 * *xrayutilities.pdf*: pdf-file with documentation of the package
51
52
53 Installation (pip)
54 ==================
55 Using the python package manager pip you can install xrayutilities by executing
56
57 pip install xrayutilities
58
59 or for a user installation (without admin access) use
60
61 pip install --user xrayutilities
62
63 If installation using above's command fails due to missing OpenMP libraries, use
64
65 pip install --global-option="--without-openmp" xrayutilities
66
67
68 Installation (source)
69 =====================
70 Installing xrayutilities from source is an easy process done by executing
71
72 python setup.py install
73
74 or
75
76 python setup.py install --prefix=<install_path>
77
78 in the source folder of xrayutilities on the command line/terminal. The first
79 command installs in the systems default directories, whereas in the second
80 command you can manually specify the installation path.
81
82 By default the setup.py script tries to use OpenMP. If you do not want to use
83 OpenMP or do not have it available use the *--without-openmp* option for the
84 installation:
85
86 python setup.py --without-openmp install --prefix=<install_path>
87
88 Requirements
89 ------------
90 The following requirements are needed for installing and using *xrayutilities*:
91
92 - Python (>= 3.3, for Python 2.7 support use version up to 1.5.x)
93 - h5py
94 - scipy (version >= 0.13.0)
95 - numpy (version >= 1.9)
96 - setuptools (to provide the pkg_resources module)
97 - lmfit (optional)
98 - matplotlib (optional)
99
100 When building from source you also might need:
101
102 - C-compiler (preferential with OpenMP support)
103 - python dev headers
104 - unittest2 (optional - only if you want to run the tests)
105 - matplotlib (optional - only if running the tests/example scripts)
106 - sphinx (optional - only when you want to build the documentation)
107 - numpydoc (optional - only when you want to build the documentation)
108 - rst2pdf (optional - only when you want to build the documentation)
109
110 refer to your operating system documentation to find out how to install
111 those packages. On Microsoft Windows refer to the Documentation for the
112 easiest way of the installation (Anaconda, Python(x,y), or WinPython).
113
114 Python-2.7 and Python-3.X compatibility
115 =======================================
116
117 The current development is for Python-3.X only. xrayutilities up to version
118 1.5.x can be used with Python-2.7 as well.
119
120 The Python package configuration
121 ================================
122
123 The following steps should only be necessary for user local installation to
124 ensure the Python module is found by the Python interpreter:
125 In this case the module is installed under
126 *<prefix>/lib[64]/python?.?/site-packages* on Unix systems and
127 *<prefix>/Lib/site-packages* on Windows systems.
128
129 If you have installed the Python package in a directory unknown to your local
130 Python distribution, you have to tell Python where to look for the Package.
131 There are several ways how to do this:
132
133 - add the directory where the package is installed to your
134 *PYTHONPATH* environment variable.
135
136 - add the path to sys.path in the *.pythonrc* file placed in your home
137 directory
138
139 import sys
140 sys.path.append("path to the xrayutilities package")
141
142 - simply apply the previous method in every script where you want to
143 use the xrayutilities package before importing the package
144
145 import sys
146 sys.path.append("path to the xrayutilities package")
147 import xrayutilities
148
149 Obtaining the source code
150 =========================
151
152 The sources are hosted on sourceforge in git repository.
153 Use
154
155 git clone https://github.com/dkriegner/xrayutilities.git
156
157 to clone the git repository. If you would like to have commit rights
158 contact one of the administrators.
159
160 Update
161 ======
162
163 if you already installed xrayutilities you can update it by navigating into
164 its source folder and obtain the new sources by ::
165
166 git pull
167
168 or download the new tarball from sourceforge
169 (http://sf.net/projects/xrayutilities) if any code changed during the update you
170 need to reinstall the Python package. To determine the path in which
171 xrayutilities where installed previously use
172
173 python -c "import xrayutilities as xu; print xu.__file__"
174 /usr/local/lib64/python2.7/site-packages/xrayutilities/__init__.pyc
175
176 if the output is e.g.: */usr/local/lib64/python2.7/site-packages/xrayutilities/__init__.py*
177 you previously installed xrayutilities in */usr/local*, which should be used
178 again as install path. Use ::
179
180 python setup.py install --prefix=<path to install directory>
181
182 to install the updated package.
183
184
185 Documentation
186 =============
187
188 Documentation for xrayutilities is found in the *xrayutilities.pdf* file or on the
189 webpage http://xrayutilities.sourceforge.io
190
191 The API-documentation can also be browsed by
192
193 pydoc -p PORT
194
195 in any web-browser, after the installation is finished.
196
197 Platform: UNKNOWN
198 Classifier: Programming Language :: C
199 Classifier: Programming Language :: Python :: 3.3
200 Classifier: Programming Language :: Python :: 3.4
201 Classifier: Programming Language :: Python :: 3.5
202 Classifier: Programming Language :: Python :: 3.6
203 Classifier: Programming Language :: Python :: 3.7
204 Classifier: Topic :: Scientific/Engineering :: Physics
205 Classifier: Intended Audience :: Science/Research
206 Classifier: Development Status :: 5 - Production/Stable
207 Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
208 Requires-Python: ~=3.3
209 Description-Content-Type: text/markdown
210 Provides-Extra: plot
211 Provides-Extra: fit
212 Provides-Extra: lzma
0 CHANGES.txt
1 CONTRIBUTING.md
2 LICENSE.txt
3 MANIFEST.in
4 README.md
5 setup.py
6 xrayutilities.pdf
7 doc/README_sf.rst
8 doc/webpage.patch
9 doc/source/conf.py
10 doc/source/cxrayutilities.rst
11 doc/source/example_xu_ccd_parameter.py
12 doc/source/example_xu_ccd_parameter_hkl.py
13 doc/source/example_xu_linear_detector_parameters.py
14 doc/source/example_xu_read_spec_easy.py
15 doc/source/examples.rst
16 doc/source/index.rst
17 doc/source/modules.rst
18 doc/source/simulations.rst
19 doc/source/xrayutilities.analysis.rst
20 doc/source/xrayutilities.io.rst
21 doc/source/xrayutilities.materials.rst
22 doc/source/xrayutilities.math.rst
23 doc/source/xrayutilities.rst
24 doc/source/xrayutilities.simpack.rst
25 doc/source/_static/favicon.ico
26 doc/source/pics/fit_xrd.svg
27 doc/source/pics/line_cut_intdir.png
28 doc/source/pics/line_cut_radial.png
29 doc/source/pics/reciprocal_space_plane.png
30 doc/source/pics/rsm_xrdml.png
31 doc/source/pics/show_unitcell.png
32 doc/source/pics/xray-logo.png
33 doc/source/pics/xrd_algaas004.svg
34 doc/source/pics/xrd_sige004.svg
35 doc/source/pics/xrr_densityprofile.svg
36 doc/source/pics/xrr_diffuse.png
37 doc/source/pics/xrr_fitting.svg
38 doc/source/pics/xu_usage.svg
39 doc/source/pics/xu_usage_inkscape.svg
40 doc/source/pics/xu_usage_planning.svg
41 doc/source/pics/xu_usage_planning_inkscape.svg
42 examples/simpack_powdermodel.py
43 examples/simpack_xrd_AlGaAs.py
44 examples/simpack_xrd_Darwin_AlGaAs.py
45 examples/simpack_xrd_InAs_fitting.py
46 examples/simpack_xrd_SiGe.py
47 examples/simpack_xrd_SiGe111.py
48 examples/simpack_xrd_SiGe_asymmmetric.py
49 examples/simpack_xrd_SiGe_superlattice.py
50 examples/simpack_xrr_SiO2_Ru_CoFe_IrMn_Al2O3.py
51 examples/simpack_xrr_diffuse.py
52 examples/simpack_xrr_matrixmethod.py
53 examples/xrayutilities_angular2hkl_conversion.py
54 examples/xrayutilities_ccd_parameter.py
55 examples/xrayutilities_components_of_the_structure_factor.py
56 examples/xrayutilities_define_material.py
57 examples/xrayutilities_energy_dependent_structure_factor.py
58 examples/xrayutilities_example_plot_3D_ESRF_ID01.py
59 examples/xrayutilities_experiment_Powder_example_Iron.py
60 examples/xrayutilities_experiment_angle_calculation.py
61 examples/xrayutilities_experiment_kappa.py
62 examples/xrayutilities_export_data2vtk.py
63 examples/xrayutilities_fuzzygridding.py
64 examples/xrayutilities_hotpixelkill_variant.py
65 examples/xrayutilities_id01_functions.py
66 examples/xrayutilities_io_cif_parser.py
67 examples/xrayutilities_io_cif_parser_bi2te3.py
68 examples/xrayutilities_io_pdcif_plot.py
69 examples/xrayutilities_kmap_example_ESRF.py
70 examples/xrayutilities_linear_detector_parameters.py
71 examples/xrayutilities_materials_Alloy_contentcalc.py
72 examples/xrayutilities_math_fitting.py
73 examples/xrayutilities_orientation_matrix.py
74 examples/xrayutilities_peak_angles_beamtime.py
75 examples/xrayutilities_polefigure.py
76 examples/xrayutilities_q2ang_general.py
77 examples/xrayutilities_read_panalytical.py
78 examples/xrayutilities_read_seifert.py
79 examples/xrayutilities_read_spec.py
80 examples/xrayutilities_reflection_strength.py
81 examples/xrayutilities_show_reciprocal_space_plane.py
82 examples/xrayutilities_user.conf
83 examples/data/BaF2.cif
84 examples/data/Calcite.cif
85 examples/data/LaB6_d500_si_psd.xye.bz2
86 examples/data/README.txt
87 examples/data/Silicon.cif
88 examples/data/bi2te3.cif
89 examples/data/inas_layer_radial_002_004.ras.bz2
90 examples/data/polefig_Ge113.xrdml.bz2
91 examples/data/rsm_1.xrdml.bz2
92 examples/data/rsm_2.xrdml.bz2
93 examples/data/rsm_3.xrdml.bz2
94 examples/data/rsm_4.xrdml.bz2
95 examples/data/rsm_5.xrdml.bz2
96 examples/data/test.spec.bz2
97 examples/data/xrr_data.txt
98 lib/xrayutilities/VERSION
99 lib/xrayutilities/__init__.py
100 lib/xrayutilities/config.py
101 lib/xrayutilities/exception.py
102 lib/xrayutilities/experiment.py
103 lib/xrayutilities/gridder.py
104 lib/xrayutilities/gridder2d.py
105 lib/xrayutilities/gridder3d.py
106 lib/xrayutilities/mpl_helper.py
107 lib/xrayutilities/normalize.py
108 lib/xrayutilities/q2ang_fit.py
109 lib/xrayutilities/utilities.py
110 lib/xrayutilities/utilities_noconf.py
111 lib/xrayutilities/xrayutilities_default.conf
112 lib/xrayutilities.egg-info/PKG-INFO
113 lib/xrayutilities.egg-info/SOURCES.txt
114 lib/xrayutilities.egg-info/dependency_links.txt
115 lib/xrayutilities.egg-info/requires.txt
116 lib/xrayutilities.egg-info/top_level.txt
117 lib/xrayutilities/analysis/__init__.py
118 lib/xrayutilities/analysis/line_cuts.py
119 lib/xrayutilities/analysis/misc.py
120 lib/xrayutilities/analysis/sample_align.py
121 lib/xrayutilities/io/__init__.py
122 lib/xrayutilities/io/cbf.py
123 lib/xrayutilities/io/desy_tty08.py
124 lib/xrayutilities/io/edf.py
125 lib/xrayutilities/io/fastscan.py
126 lib/xrayutilities/io/filedir.py
127 lib/xrayutilities/io/helper.py
128 lib/xrayutilities/io/ill_numor.py
129 lib/xrayutilities/io/imagereader.py
130 lib/xrayutilities/io/panalytical_xml.py
131 lib/xrayutilities/io/pdcif.py
132 lib/xrayutilities/io/rigaku_ras.py
133 lib/xrayutilities/io/rotanode_alignment.py
134 lib/xrayutilities/io/seifert.py
135 lib/xrayutilities/io/spec.py
136 lib/xrayutilities/io/spectra.py
137 lib/xrayutilities/materials/__init__.py
138 lib/xrayutilities/materials/_create_database.py
139 lib/xrayutilities/materials/atom.py
140 lib/xrayutilities/materials/cif.py
141 lib/xrayutilities/materials/database.py
142 lib/xrayutilities/materials/elements.py
143 lib/xrayutilities/materials/heuslerlib.py
144 lib/xrayutilities/materials/material.py
145 lib/xrayutilities/materials/plot.py
146 lib/xrayutilities/materials/predefined_materials.py
147 lib/xrayutilities/materials/spacegrouplattice.py
148 lib/xrayutilities/materials/wyckpos.py
149 lib/xrayutilities/materials/data/README.txt
150 lib/xrayutilities/materials/data/atomic_radius.dat
151 lib/xrayutilities/materials/data/colors.dat
152 lib/xrayutilities/materials/data/f0_InterTables.dat.xz
153 lib/xrayutilities/materials/data/f0_xop.dat.xz
154 lib/xrayutilities/materials/data/f1f2_Henke.dat.xz
155 lib/xrayutilities/materials/data/f1f2_asf_Kissel.dat.xz
156 lib/xrayutilities/materials/data/nist_atom.dat
157 lib/xrayutilities/math/__init__.py
158 lib/xrayutilities/math/algebra.py
159 lib/xrayutilities/math/fit.py
160 lib/xrayutilities/math/functions.py
161 lib/xrayutilities/math/misc.py
162 lib/xrayutilities/math/transforms.py
163 lib/xrayutilities/math/vector.py
164 lib/xrayutilities/simpack/__init__.py
165 lib/xrayutilities/simpack/darwin_theory.py
166 lib/xrayutilities/simpack/fit.py
167 lib/xrayutilities/simpack/helpers.py
168 lib/xrayutilities/simpack/models.py
169 lib/xrayutilities/simpack/mosaicity.py
170 lib/xrayutilities/simpack/powder.py
171 lib/xrayutilities/simpack/powdermodel.py
172 lib/xrayutilities/simpack/smaterials.py
173 src/block_average.c
174 src/cxrayutilities.c
175 src/file_io.c
176 src/gridder.h
177 src/gridder1d.c
178 src/gridder2d.c
179 src/gridder3d.c
180 src/gridder_utils.c
181 src/gridder_utils.h
182 src/qconversion.c
183 src/qconversion.h
184 src/xrayutilities.h
185 tests/README.txt
186 tests/__init__.py
187 tests/test_HXRD.py
188 tests/test_NonCOP.py
189 tests/test_alloy_content_calc.py
190 tests/test_amorphous.py
191 tests/test_analysis_linecuts.py
192 tests/test_area_calib.py
193 tests/test_blockaverage.py
194 tests/test_ccd_normalizer.py
195 tests/test_examples.py
196 tests/test_functions.py
197 tests/test_fuzzygridder1d.py
198 tests/test_fuzzygridder2d.py
199 tests/test_fuzzygridder3d.py
200 tests/test_getang.py
201 tests/test_gridder1d.py
202 tests/test_gridder2d.py
203 tests/test_gridder2dlist.py
204 tests/test_gridder3d.py
205 tests/test_io_cbf.py
206 tests/test_io_edf.py
207 tests/test_io_esg.py
208 tests/test_io_fastscan.py
209 tests/test_io_fio.py
210 tests/test_io_nja.py
211 tests/test_io_nja_map.py
212 tests/test_io_nja_tsk.py
213 tests/test_io_numor.py
214 tests/test_io_pdcif.py
215 tests/test_io_perkinelmer.py
216 tests/test_io_pilatus.py
217 tests/test_io_rigaku.py
218 tests/test_io_roperccd.py
219 tests/test_io_spec.py
220 tests/test_io_specalignmentlog.py
221 tests/test_io_speclog.py
222 tests/test_io_specsardana.py
223 tests/test_io_tty.py
224 tests/test_io_xrdml.py
225 tests/test_linear_calib.py
226 tests/test_maplog.py
227 tests/test_materials.py
228 tests/test_materials_cif.py
229 tests/test_materials_cifexport.py
230 tests/test_materials_database.py
231 tests/test_math_peak_fit.py
232 tests/test_math_solve_quartic.py
233 tests/test_math_vector.py
234 tests/test_miscut_calc.py
235 tests/test_npygridder1d.py
236 tests/test_optical_properties.py
237 tests/test_pseudomorphic.py
238 tests/test_q2angfit.py
239 tests/test_qconversion.py
240 tests/test_qconversion_area.py
241 tests/test_qconversion_linear.py
242 tests/test_qconversion_trans.py
243 tests/test_simpack_dynamicalmodel.py
244 tests/test_simpack_kinematicalmodel.py
245 tests/test_simpack_powdermodel.py
246 tests/test_simpack_xrrdiffuse.py
247 tests/test_simpack_xrrspecular.py
248 tests/test_structure_factor.py
249 tests/test_transforms.py
0 numpy>=1.9.2
1 scipy>=0.11.0
2 h5py
3 setuptools
4
5 [fit]
6 lmfit
7
8 [lzma]
9 lzma
10
11 [plot]
12 matplotlib
+0
-136
release.txt less more
0
1 This document describes the process of making a new release of xrayutilities.
2 It is therefore not relevant for users, but for developers and packagers only.
3
4 Note: This procedure is intended for use on a Unix operating system.
5
6 CHECK EVERYTHING
7 ================
8
9 Is everything running fine? perform the tests and run the examples
10
11 # change the version in VERSION
12 # update copyright notice in doc/source/conf.py
13 pycodestyle xrayutilities
14 # allowed output is:
15 # xrayutilities/materials/elements.py:36:1: E741 ambiguous variable name 'O'
16 # xrayutilities/materials/elements.py:139:1: E741 ambiguous variable name 'I'
17 # xrayutilities/math/fit.py:100:80: E501 line too long (100 > 79 characters)
18 # xrayutilities/math/fit.py:199:80: E501 line too long (90 > 79 characters)
19 # xrayutilities/math/fit.py:273:80: E501 line too long (90 > 79 characters)
20 # xrayutilities/math/fit.py:314:80: E501 line too long (90 > 79 characters)
21 python2 setup.py test
22 python3 setup.py test
23 # optionally check the coverage
24 # also see https://coverage.readthedocs.io/en/latest/subprocess.html
25 export COVERAGE_PROCESS_START=.coveragerc
26 coverage run --parallel-mode --source xrayutilities setup.py test
27 coverage combine --append
28 coverage html
29
30 run the examples
31
32 UPDATE DOCUMENTATION
33 ====================
34
35 to build the documentation from scratch first one needs to rebuild the API
36 documentation sources (which is done from the installed module, so make sure
37 you have the latest version installed!)
38
39 sphinx-apidoc -f -o doc/source xrayutilities
40
41 In the root directory of the package execute the following to rebuild the
42 documentation pdf. You will need sphinx, numpydoc and rst2pdf.
43
44 python setup.py build build_doc -b pdf
45 cd build/sphinx/pdf; pdftk xrayutilities.pdf output ../../../xrayutilities.pdf ; cd ../../..
46
47 Or generate a texinfo file using
48
49 python setup.py build_doc -b texinfo
50 cd build/sphinx/texinfo; make
51
52 PACKAGING
53 =========
54
55 create a tarball for redistribution of xrayutilities without the use of git
56
57 python setup.py sdist
58
59 creates a tarball in the directory dist, which contains everything needed for
60 the installation of xrayutilities
61
62 This tarball should be uploaded to sourceforge and PyPI.
63
64 To build a executable installer for Microsoft windows run
65
66 python setup.py bdist_wininst
67
68 on a windows machine.
69 If not done previously you need to build the extenstion modules using
70
71 python setup.py build -c <compiler_name>
72
73 The compiler name can be omitted if the default is working.
74
75 For up to date Python installations you should use Visual Studio 2015.
76 To perform the installation or build a installer package perform the
77 following steps:
78 * open an administrator shell (or winpython command prompt)
79 * (optional) execute: 'set DISTUTILS_USE_SDK=1'
80 * (optional) execute: 'setenv /x64 /release' (use /x86 for a 32-bit build)
81 * compile with: 'python setup.py build'. one might need to use
82 '--without-openmp' option for building.
83 * build binaries with 'python setup.py bdist_wininst bdist_wheel'
84 * install package locally 'python setup.py install'
85 * use 'python setup.py test' to test your installation
86
87 GIT tagging
88 -----------
89
90 tag the version in the GIT repository and publish the version tag to Github
91
92 git tag -a vX -m "version X"
93 git push origin vX
94
95 UPDATE WEBPAGE
96 ==============
97
98 rebuild the html documents and upload them to the sourceforge webserver
99 to have the correct style the style file needs to patched
100
101 python setup.py build_doc -b html
102 patch -p0 < doc/webpage.patch
103 # in case its needed, update the patch with
104 # diff -Naur build/sphinx/html/index.html.orig build/sphinx/html/index.html > doc/webpage.patch
105
106 to upload new web-documentation connect to the sourceforge server via:
107
108 sftp://USERNAME@web.sourceforge.net
109 /home/project-web/xrayutilities/htdocs
110
111 files on sourceforge can be moved/archived using
112
113 sftp://USERNAME@frs.sourceforge.net/home/frs/project/x/xr/xrayutilities
114
115 however, upon such a move the download statistics are lost.
116
117 UPDATE PyPI PACKAGE
118 ===================
119
120 Upload new version to the Python package index by
121
122 twine upload dist/xrayutilities-VERSION.tar.gz
123
124 On a windows machine run
125
126 twine upload --config-file PATH_TO_pypirc dist/*
127
128 or download the artifacts from AppVeyor and publish them to PyPI using twine
129
130 Github release and mailing list anouncement
131 ===========================================
132
133 Finally announce the new release on Github (which will also upload the new
134 version to Sourceforge) and drop a mail to the user mailinglist:
135 xrayutilities-users@lists.sourceforge.net
1717
1818 import glob
1919 import os.path
20 import subprocess
2021 import sys
22 import tempfile
23 from distutils.command.build_py import build_py
2124 from distutils.command.install import INSTALL_SCHEMES
22 from distutils.errors import DistutilsArgError
25 from distutils.errors import DistutilsArgError, CompileError
2326 from distutils.fancy_getopt import FancyGetopt
2427
2528 import numpy
2629 from setuptools import Extension, find_packages, setup
2730 from setuptools.command.build_ext import build_ext
31
2832
2933 cliopts = []
3034 cliopts.append(("without-openmp", None, "build without OpenMP support"))
4549 except DistutilsArgError:
4650 pass
4751
48 # set default flags
52 # get options from command line
4953 without_openmp = False
50
5154 for opts, values in options.get_option_order():
5255 if opts == "without-openmp":
5356 without_openmp = True
5457
55 copt = {'msvc': [],
56 'mingw32': ['-std=c99'],
57 'unix': ['-std=c99']}
58 lopt = {'mingw32': [],
59 'unix': []}
60
61 user_macros = []
62 if not without_openmp:
63 user_macros = [('__OPENMP__', None)]
64 copt["msvc"].append('/openmp')
65 copt["mingw32"].append("-fopenmp")
66 copt["unix"].append("-fopenmp")
67 lopt["mingw32"].append("-fopenmp")
68 lopt["unix"].append("-lgomp")
58
59 def has_flag(compiler, flagname, output_dir=None):
60 # see https://bugs.python.org/issue26689
61 """Return a boolean indicating whether a flag name is supported on
62 the specified compiler.
63 """
64 with tempfile.NamedTemporaryFile('w', suffix='.c', delete=False) as f:
65 f.write('int main (int argc, char **argv) { return 0; }')
66 f.close()
67 try:
68 obj = compiler.compile([f.name], output_dir=output_dir,
69 extra_postargs=[flagname])
70 os.remove(*obj)
71 except CompileError:
72 return False
73 finally:
74 os.remove(f.name)
75 return True
6976
7077
7178 class build_ext_subclass(build_ext):
79
7280 def build_extensions(self):
7381 c = self.compiler.compiler_type
74 # set custom compiler options
75 if c in list(copt.keys()):
76 for e in self.extensions:
77 e.extra_compile_args = copt[c]
78 if c in list(lopt.keys()):
79 for e in self.extensions:
80 e.extra_link_args = lopt[c]
81 build_ext.build_extensions(self)
82
83
84 cmdclass = {'build_ext': build_ext_subclass}
82 # set standard compiler options
83 copt = {'mingw32': ['-std=c99'],
84 'unix': ['-std=c99']}
85
86 if c in copt:
87 for flag in copt[c]:
88 if has_flag(self.compiler, flag, self.build_temp):
89 for e in self.extensions:
90 e.extra_compile_args.append(flag)
91
92 # set openMP compiler options
93 if not without_openmp:
94 openmpflags = {'msvc': ('/openmp', None),
95 'mingw32': ('-fopenmp', '-fopenmp'),
96 'unix': ('-fopenmp', '-lgomp')}
97 if c in openmpflags:
98 flag, lib = openmpflags[c]
99 if has_flag(self.compiler, flag, self.build_temp):
100 for e in self.extensions:
101 e.extra_compile_args.append(flag)
102 if lib is not None:
103 e.extra_link_args.append(lib)
104 e.define_macros .append(('__OPENMP__', None))
105
106 super().build_extensions()
107
108
109 class build_with_database(build_py):
110 def build_database(self):
111 dbfilename = os.path.join(self.build_lib, 'xrayutilities',
112 'materials', 'data', 'elements.db')
113 cmd = [sys.executable,
114 os.path.join('lib', 'xrayutilities', 'materials',
115 '_create_database.py'),
116 dbfilename]
117 self.mkpath(os.path.dirname(dbfilename))
118 print('building database: {}'.format(dbfilename))
119 try:
120 if sys.version_info >= (3, 5):
121 subprocess.run(cmd, stderr=subprocess.PIPE,
122 stdout=subprocess.PIPE, check=True)
123 else:
124 subprocess.check_output(cmd)
125 except subprocess.CalledProcessError as cpe:
126 sys.stdout.buffer.write(cpe.stdout)
127 sys.stdout.buffer.write(cpe.stderr)
128 raise
129
130 def run(self):
131 super().run()
132 self.build_database()
133
134
135 cmdclass = {'build_py': build_with_database,
136 'build_ext': build_ext_subclass}
85137
86138 with open('README.md') as f:
87139 long_description = f.read()
88140
89 extmodul = Extension(
90 'xrayutilities.cxrayutilities',
91 sources=glob.glob(os.path.join('xrayutilities', 'src', '*.c')),
92 define_macros=user_macros
93 )
94
95 with open('VERSION') as version_file:
141 extmodul = Extension('xrayutilities.cxrayutilities',
142 sources=glob.glob(os.path.join('src', '*.c')))
143
144 with open('lib/xrayutilities/VERSION') as version_file:
96145 version = version_file.read().strip()
97146
98147 try:
117166 sys.path.pop(0)
118167
119168 cmdclass['build_doc'] = build_doc
169
120170 except ImportError:
121171 pass
122172
127177 description="package for x-ray diffraction data evaluation",
128178 classifiers=[
129179 "Programming Language :: C",
130 "Programming Language :: Python :: 2.7",
131 "Programming Language :: Python :: 3.2",
132180 "Programming Language :: Python :: 3.3",
133181 "Programming Language :: Python :: 3.4",
134182 "Programming Language :: Python :: 3.5",
141189 "(GPLv2+)"
142190 ],
143191 long_description=long_description,
192 long_description_content_type="text/markdown",
144193 author_email="eugen.wintersberger@desy.de, dominik.kriegner@gmail.com",
145194 maintainer="Dominik Kriegner",
146195 maintainer_email="dominik.kriegner@gmail.com",
147 packages=find_packages(exclude=['tests']),
196 package_dir={'': 'lib'},
197 packages=find_packages('lib'),
148198 package_data={
149 "xrayutilities": ["*.conf"],
150 "xrayutilities.materials": [
151 os.path.join("data", "*.db"),
152 os.path.join("data", "*.cif")
153 ]
199 "xrayutilities": ["VERSION", "*.conf"],
200 "xrayutilities.materials": [os.path.join("data", "*")]
154201 },
155 data_files=[('xrayutilities', ['VERSION'])],
202 python_requires='~=3.3',
203 setup_requires=['numpy', 'scipy', 'h5py'],
156204 install_requires=['numpy>=1.9.2', 'scipy>=0.11.0', 'h5py', 'setuptools'],
157205 extras_require={
158206 'plot': ["matplotlib"],
164212 cmdclass=cmdclass,
165213 url="http://xrayutilities.sourceforge.net",
166214 license="GPLv2",
167 test_suite="unittest2.collector",
168215 script_args=args
169216 )
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2010-2011, 2013 Dominik Kriegner <dominik.kriegner@gmail.com>
17 */
18
19 #include "xrayutilities.h"
20
21 PyObject* block_average1d(PyObject *self, PyObject *args) {
22 /* block average for one-dimensional double array
23 *
24 * Parameters
25 * ----------
26 * input: input array of datatype double
27 * Nav: number of items to average
28 *
29 * Returns
30 * -------
31 * block_av: block averaged output array
32 * size = ceil(N/Nav)
33 *
34 */
35
36 int i, j, Nav, N;
37 PyArrayObject *input = NULL, *outarr = NULL;
38 double *cin, *cout;
39 double buf;
40 npy_intp nout;
41
42 /* Python argument conversion code */
43 if (!PyArg_ParseTuple(args, "O!i", &PyArray_Type, &input, &Nav)) {
44 return NULL;
45 }
46
47 PYARRAY_CHECK(input, 1, NPY_DOUBLE, "input must be a 1D double array!");
48 N = (int) PyArray_SIZE(input);
49 cin = (double *) PyArray_DATA(input);
50
51 /* create output ndarray */
52 nout = ((int) ceil(N / (float) Nav));
53 outarr = (PyArrayObject *) PyArray_SimpleNew(1, &nout, NPY_DOUBLE);
54 cout = (double *) PyArray_DATA(outarr);
55
56 /* c-code following is performing the block averaging */
57 for (i = 0; i < N; i = i + Nav) {
58 buf = 0;
59 /* perform one block average (j-i serves as counter
60 -> last bin is therefore correct) */
61 for (j = i; j < i + Nav && j < N; ++j) {
62 buf += cin[j];
63 }
64 /* save average to output array */
65 cout[i / Nav] = buf / (float) (j - i);
66 }
67
68 /* clean up */
69 Py_DECREF(input);
70
71 /* return output array */
72 return PyArray_Return(outarr);
73 }
74
75 PyObject* block_average2d(PyObject *self, PyObject *args) {
76 /* 2D block average for one CCD frame
77 *
78 * Parameters
79 * ----------
80 * ccd: input array/CCD frame
81 * size = (Nch2, Nch1)
82 * Nch1 is the fast varying index
83 * Nav1, 2: number of channels to average in each dimension
84 * in total a block of Nav1 x Nav2 is averaged
85 * nthreads: number of threads to use in parallel section
86 *
87 * Returns
88 * -------
89 * block_av: block averaged output array
90 * size = (ceil(Nch2/Nav2) , ceil(Nch1/Nav1))
91 *
92 */
93
94 int i = 0, j = 0, k = 0, l = 0; /* loop indices */
95 int Nch1, Nch2; /* number of values in input array */
96 int Nav1, Nav2; /* number of items to average */
97 unsigned int nthreads; /* number of threads to use */
98 PyArrayObject *input = NULL, *outarr = NULL;
99 double *cin, *cout;
100 double buf;
101 npy_intp nout[2];
102
103 /* Python argument conversion code */
104 if (!PyArg_ParseTuple(args, "O!iiI", &PyArray_Type, &input, &Nav2,
105 &Nav1, &nthreads)) {
106 return NULL;
107 }
108
109 PYARRAY_CHECK(input, 2, NPY_DOUBLE, "input must be a 2D double array!");
110 Nch2 = (int) PyArray_DIMS(input)[0];
111 Nch1 = (int) PyArray_DIMS(input)[1];
112 cin = (double *) PyArray_DATA(input);
113
114 /* create output ndarray */
115 nout[0] = ((int) ceil(Nch2 / (float) Nav2));
116 nout[1] = ((int) ceil(Nch1 / (float) Nav1));
117 outarr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
118 cout = (double *) PyArray_DATA(outarr);
119
120 #ifdef __OPENMP__
121 /* set openmp thread numbers dynamically */
122 OMPSETNUMTHREADS(nthreads);
123 #endif
124
125 #pragma omp parallel for default(shared) \
126 private(i, j, k, l, buf) schedule(static)
127 for (i = 0; i < Nch2; i = i + Nav2) {
128 for (j = 0; j < Nch1; j = j + Nav1) {
129 buf = 0.;
130 for (k = 0; k < Nav2 && (i + k) < Nch2; ++k) {
131 for (l = 0; l < Nav1 && (j + l) < Nch1; ++l) {
132 buf += cin[(i + k) * Nch1 + (j + l)];
133 }
134 }
135 cout[(i / Nav2) * nout[1] + j / Nav1] = buf / (float)(k * l);
136 }
137 }
138
139 /* clean up */
140 Py_DECREF(input);
141
142 return PyArray_Return(outarr);
143 }
144
145 PyObject* block_average_PSD(PyObject *self, PyObject *args) {
146 /* block average for a bunch of PSD spectra
147 *
148 * Parameters
149 * ----------
150 * psd: input array of PSD values
151 * size = (Nspec, Nch) (in)
152 * Nav: number of channels to average
153 * nthreads: number of threads to use in parallel section
154 *
155 * Returns
156 * -------
157 * block_av: block averaged output array
158 * size = (Nspec , ceil(Nch/Nav)) (out)
159 */
160 int i, j, k; /* loop indices */
161 int Nspec, Nch; /* number of items in input */
162 int Nav; /* number of items to average */
163 unsigned int nthreads; /* number of threads to use */
164 PyArrayObject *input = NULL, *outarr = NULL;
165 double *cin, *cout;
166 double buf;
167 npy_intp nout[2];
168
169 /* Python argument conversion code */
170 if (!PyArg_ParseTuple(args, "O!iI", &PyArray_Type,
171 &input, &Nav, &nthreads)) {
172 return NULL;
173 }
174 PYARRAY_CHECK(input, 2, NPY_DOUBLE, "input must be a 2D double array!");
175 Nspec = (int) PyArray_DIMS(input)[0];
176 Nch = (int) PyArray_DIMS(input)[1];
177 cin = (double *) PyArray_DATA(input);
178
179 /* create output ndarray */
180 nout[0] = Nspec;
181 nout[1] = ((int) ceil(Nch / (float) Nav));
182 outarr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
183 cout = (double *) PyArray_DATA(outarr);
184
185 #ifdef __OPENMP__
186 /* set openmp thread numbers dynamically */
187 OMPSETNUMTHREADS(nthreads);
188 #endif
189
190 /* c-code following is performing the block averaging */
191 #pragma omp parallel for default(shared) private(i, j, k, buf) \
192 schedule(static)
193 for (i = 0; i < Nspec; ++i) {
194 for (j = 0; j < Nch; j = j + Nav) {
195 buf = 0;
196 /* perform one block average
197 (j-i serves as counter -> last bin is therefore correct) */
198 for (k = j; k < j + Nav && k < Nch; ++k) {
199 buf += cin[i * Nch + k];
200 }
201 /* save average to output array */
202 cout[j / Nav + i * nout[1]] = buf / (float) (k - j);
203 }
204 }
205
206 /* clean up */
207 Py_DECREF(input);
208
209 /* return output array */
210 return PyArray_Return(outarr);
211 }
212
213 PyObject* block_average_CCD(PyObject *self, PyObject *args) {
214 /* 2D block average for a series of CCD frames
215 *
216 * Parameters
217 * ----------
218 * ccd: input array/CCD frames
219 * size = (Nframes, Nch2, Nch1)
220 * Nch1 is the fast varying index
221 * Nav1, 2: number of channels to average in each dimension
222 * in total a block of Nav1 x Nav2 is averaged
223 * nthreads: number of threads to use in parallel section
224 *
225 * Returns
226 * -------
227 * block_av: block averaged output array
228 * size = (Nframes, ceil(Nch2/Nav2) , ceil(Nch1/Nav1))
229 *
230 */
231
232 int i = 0, j = 0, k = 0, l = 0, n = 0; /* loop indices */
233 int Nframes, Nch1, Nch2; /* number of values in input array */
234 int Nav1, Nav2; /* number of items to average */
235 unsigned int nthreads; /* number of threads to use */
236 PyArrayObject *input = NULL, *outarr = NULL;
237 double *cin, *cout;
238 double buf;
239 npy_intp nout[3];
240
241 /* Python argument conversion code */
242 if (!PyArg_ParseTuple(args, "O!iiI", &PyArray_Type, &input, &Nav2,
243 &Nav1, &nthreads)) {
244 return NULL;
245 }
246
247 PYARRAY_CHECK(input, 3, NPY_DOUBLE, "input must be a 3D double array!");
248 Nframes = (int) PyArray_DIMS(input)[0];
249 Nch2 = (int) PyArray_DIMS(input)[1];
250 Nch1 = (int) PyArray_DIMS(input)[2];
251 cin = (double *) PyArray_DATA(input);
252
253 /* create output ndarray */
254 nout[0] = Nframes;
255 nout[1] = ((int) ceil(Nch2 / (float) Nav2));
256 nout[2] = ((int) ceil(Nch1 / (float) Nav1));
257 outarr = (PyArrayObject *) PyArray_SimpleNew(3, nout, NPY_DOUBLE);
258 cout = (double *) PyArray_DATA(outarr);
259
260 #ifdef __OPENMP__
261 /* set openmp thread numbers dynamically */
262 OMPSETNUMTHREADS(nthreads);
263 #endif
264
265 #pragma omp parallel for default(shared) \
266 private(i, j, k, l, n, buf) schedule(static)
267 for (n = 0; n < Nframes; n++) {
268 for (i = 0; i < Nch2; i = i + Nav2) {
269 for (j = 0; j < Nch1; j = j + Nav1) {
270 buf = 0.;
271 for (k = 0; k < Nav2 && (i + k) < Nch2; ++k) {
272 for (l = 0; l < Nav1 && (j + l) < Nch1; ++l) {
273 buf += cin[n* Nch1 * Nch2 + (i + k) * Nch1 + (j + l)];
274 }
275 }
276 cout[n * nout[1] * nout[2] + (i / Nav2) * nout[1] + j / Nav1] = buf / (float)(k * l);
277 }
278 }
279 }
280
281 /* clean up */
282 Py_DECREF(input);
283
284 return PyArray_Return(outarr);
285 }
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
17 * Copyright (C) 2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
18 *
19 */
20 #include <Python.h>
21
22 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
23 #define PY_ARRAY_UNIQUE_SYMBOL XU_UNIQUE_SYMBOL
24 #include <numpy/arrayobject.h>
25
26
27 /* functions from block_average.c */
28 extern PyObject* block_average1d(PyObject *self, PyObject *args);
29 extern PyObject* block_average2d(PyObject *self, PyObject *args);
30 extern PyObject* block_average_PSD(PyObject *self, PyObject *args);
31 extern PyObject* block_average_CCD(PyObject *self, PyObject *args);
32
33 /* functions from gridder1d.c */
34 extern PyObject* pygridder1d(PyObject *self, PyObject *args);
35 extern PyObject* pyfuzzygridder1d(PyObject *self, PyObject *args);
36
37 /* functions from gridder2d.c */
38 extern PyObject* pygridder2d(PyObject *self, PyObject *args);
39 extern PyObject* pyfuzzygridder2d(PyObject *self, PyObject *args);
40
41 /* function from gridder3d.c */
42 extern PyObject* pygridder3d(PyObject *self, PyObject *args);
43 extern PyObject* pyfuzzygridder3d(PyObject *self, PyObject *args);
44
45 /* functions from qconversion.c */
46 extern PyObject* py_ang2q_conversion(PyObject *self, PyObject *args);
47 extern PyObject* py_ang2q_conversion_linear(PyObject *self, PyObject *args);
48 extern PyObject* py_ang2q_conversion_area(PyObject *self, PyObject *args);
49 extern PyObject* ang2q_conversion_area_pixel(PyObject *self, PyObject *args);
50 extern PyObject* ang2q_conversion_area_pixel2(PyObject *self, PyObject *args);
51
52 extern PyObject* ang2q_detpos(PyObject *self, PyObject *args);
53 extern PyObject* ang2q_detpos_linear(PyObject *self, PyObject *args);
54 extern PyObject* ang2q_detpos_area(PyObject *self, PyObject *args);
55
56 /* functions from file_io.c */
57 extern PyObject* cbfread(PyObject *self, PyObject *args);
58
59 static PyMethodDef XRU_Methods[] = {
60 {"block_average1d", (PyCFunction)block_average1d, METH_VARARGS,
61 "block average for one-dimensional numpy double array\n\n"
62 "Parameters \n"
63 "----------\n"
64 " input: input array of datatype double \n"
65 " Nav: number of items to average \n\n"
66 "Returns\n"
67 "-------\n"
68 " block_av: block averaged output array\n"
69 " size = ceil(N / Nav) \n"
70 },
71 {"block_average2d", block_average2d, METH_VARARGS,
72 "2D block average for one CCD frame\n\n"
73 "Parameters\n"
74 "----------\n"
75 " ccd: input array/CCD frame\n"
76 " size = (Nch2, Nch1) \n"
77 " Nch1 is the fast varying index\n"
78 " Nav1, 2: number of channels to average in each dimension\n"
79 " in total a block of Nav1 x Nav2 is averaged\n"
80 "\n"
81 "Returns\n"
82 "-------\n"
83 " block_av: block averaged output array\n"
84 " size = (ceil(Nch2 / Nav2) , ceil(Nch1 / Nav1))\n"
85 },
86 {"block_average_PSD", block_average_PSD, METH_VARARGS,
87 "1D block average for a bunch of PSD spectra in a 2D array\n"
88 "\n"
89 "Parameters\n"
90 "----------\n"
91 " psd: input array of PSD values\n"
92 " size = (Nspec, Nch) (in)\n"
93 " Nav: number of channels to average\n"
94 "\n"
95 "Returns\n"
96 "-------\n"
97 " block_av: block averaged output array\n"
98 " size = (Nspec , ceil(Nch/Nav)) (out)\n"
99 },
100 {"block_average_CCD", block_average_CCD, METH_VARARGS,
101 "2D block average for a series of CCD frames\n\n"
102 "Parameters\n"
103 "----------\n"
104 " ccd: input array/CCD frames\n"
105 " size = (Nframes, Nch2, Nch1) \n"
106 " Nch1 is the fast varying index\n"
107 " Nav1, 2: number of channels to average in each dimension\n"
108 " in total a block of Nav1 x Nav2 is averaged\n"
109 "\n"
110 "Returns\n"
111 "-------\n"
112 " block_av: block averaged output array\n"
113 " size = (Nframes, ceil(Nch2 / Nav2) , ceil(Nch1 / Nav1))\n"
114 },
115 {"gridder1d", pygridder1d, METH_VARARGS,
116 "Function performs 1D gridding on 1D input data. \n\n"
117 "Parameters\n"
118 "----------\n"
119 " x ...... input x-values (1D numpy array - float64)\n"
120 " data ... input data (1D numpy array - float64)\n"
121 " nx ..... number of grid points in x-direction\n"
122 " xmin ... minimum x-value of the grid\n"
123 " xmax ... maximum x-value of the grid\n"
124 " out .... output data\n"
125 " norm ... normalization array\n"
126 " flags .. flags to specify behavior\n"
127 },
128 {"fuzzygridder1d", pyfuzzygridder1d, METH_VARARGS,
129 "Function performs fuzzy 1D gridding on 1D input data. \n\n"
130 "Parameters\n"
131 "----------\n"
132 " x ...... input x-values (1D numpy array - float64)\n"
133 " data ... input data (1D numpy array - float64)\n"
134 " nx ..... number of grid points in x-direction\n"
135 " xmin ... minimum x-value of the grid\n"
136 " xmax ... maximum x-value of the grid\n"
137 " out .... output data\n"
138 " norm ... normalization array\n"
139 " fuzzywidth .. fuzzy-size of the input data\n"
140 " flags .. flags to specify behavior\n"
141 },
142 {"gridder2d", pygridder2d, METH_VARARGS,
143 "Function performs 2D gridding on 1D input data. \n\n"
144 "Parameters\n"
145 "----------\n"
146 " x ...... input x-values (1D numpy array - float64)\n"
147 " y ...... input y-values (1D numpy array - float64)\n"
148 " data ... input data (1D numpy array - float64)\n"
149 " nx ..... number of grid points in x-direction\n"
150 " ny ..... number of grid points in y-direction\n"
151 " xmin ... minimum x-value of the grid\n"
152 " xmax ... maximum x-value of the grid\n"
153 " ymin ... minimum y-value of the grid\n"
154 " ymax ... minimum y-value of the grid\n"
155 " out .... output data\n"
156 " norm ... normalization array\n"
157 " flags .. flags to specify behavior\n"
158 },
159 {"fuzzygridder2d", pyfuzzygridder2d, METH_VARARGS,
160 "Function performs 2D fuzzy gridding on 1D input data. \n\n"
161 "Parameters\n"
162 "----------\n"
163 " x ...... input x-values (1D numpy array - float64)\n"
164 " y ...... input y-values (1D numpy array - float64)\n"
165 " data ... input data (1D numpy array - float64)\n"
166 " nx ..... number of grid points in x-direction\n"
167 " ny ..... number of grid points in y-direction\n"
168 " xmin ... minimum x-value of the grid\n"
169 " xmax ... maximum x-value of the grid\n"
170 " ymin ... minimum y-value of the grid\n"
171 " ymax ... minimum y-value of the grid\n"
172 " out .... output data\n"
173 " norm ... normalization array\n"
174 " wx ..... fuzzy width of data points in x-direction\n"
175 " wy ..... fuzzy width of data points in y-direction\n"
176 " flags .. flags to specify behavior\n"
177 },
178 {"gridder3d", pygridder3d, METH_VARARGS,
179 "Function performs 2D gridding on 1D input data. \n\n"
180 "Parameters\n"
181 "----------\n"
182 " x ...... input x-values (1D numpy array - float64)\n"
183 " y ...... input y-values (1D numpy array - float64)\n"
184 " z ...... input z-values (1D numpy array - float64)\n"
185 " data ... input data (1D numpy array - float64)\n"
186 " nx ..... number of grid points in x-direction\n"
187 " ny ..... number of grid points in y-direction\n"
188 " nz ..... number of grid points in z-direction\n"
189 " xmin ... minimum x-value of the grid\n"
190 " xmax ... maximum x-value of the grid\n"
191 " ymin ... minimum y-value of the grid\n"
192 " ymax ... maximum y-value of the grid\n"
193 " zmin ... minimum z-value of the grid\n"
194 " zmax ... maximum z-value of the grid\n"
195 " out .... output data\n"
196 " norm ... normalization array\n"
197 " flags .. flags to specify behavior\n"
198 },
199 {"fuzzygridder3d", pyfuzzygridder3d, METH_VARARGS,
200 "Function performs 3D fuzzy gridding on 1D input data. \n\n"
201 "Parameters\n"
202 "----------\n"
203 " x ...... input x-values (1D numpy array - float64)\n"
204 " y ...... input y-values (1D numpy array - float64)\n"
205 " z ...... input z-values (1D numpy array - float64)\n"
206 " data ... input data (1D numpy array - float64)\n"
207 " nx ..... number of grid points in x-direction\n"
208 " ny ..... number of grid points in y-direction\n"
209 " nz ..... number of grid points in z-direction\n"
210 " xmin ... minimum x-value of the grid\n"
211 " xmax ... maximum x-value of the grid\n"
212 " ymin ... minimum y-value of the grid\n"
213 " ymax ... maximum y-value of the grid\n"
214 " zmin ... minimum z-value of the grid\n"
215 " zmax ... maximum z-value of the grid\n"
216 " out .... output data\n"
217 " norm ... normalization array\n"
218 " wx ..... fuzzy width of data points in x-direction\n"
219 " wy ..... fuzzy width of data points in y-direction\n"
220 " wz ..... fuzzy width of data points in z-direction\n"
221 " flags .. flags to specify behavior\n"
222 },
223 {"ang2q_conversion", py_ang2q_conversion, METH_VARARGS,
224 "conversion of Npoints of goniometer positions to reciprocal space\n"
225 "for a setup with point detector\n"
226 "\n"
227 "Parameters\n"
228 "----------\n"
229 " sampleAngles .... angular positions of the sample goniometer\n"
230 " (Npoints, Ns)\n"
231 " detectorAngles .. angular positions of the detector goniometer\n"
232 " (Npoints, Nd)\n"
233 " ri .............. direction of primary beam (length irrelevant)\n"
234 " (angles zero)\n"
235 " sampleAxis ...... string with sample axis directions\n"
236 " detectorAxis .... string with detector axis directions\n"
237 " kappadir ........ rotation axis of a possible kappa circle\n"
238 " UB .............. orientation matrix and reciprocal space conversion\n"
239 " of investigated crystal (3, 3)\n"
240 " sampledis ....... sample displacement vector in relative units of\n"
241 " the detector distance\n"
242 " lambda .......... wavelength of the used x-rays (Angstreom)\n"
243 " nthreads ........ number of threads to use in parallel section of\n"
244 " the code\n"
245 " flags ........... integer flags to select sub-function\n"
246 "\n"
247 "Returns\n"
248 "-------\n"
249 " qpos .......... momentum transfer (Npoints, 3)\n"
250 },
251 {"ang2q_conversion_linear", py_ang2q_conversion_linear, METH_VARARGS,
252 "conversion of Npoints of goniometer positions to reciprocal space\n"
253 "for a linear detector with a given pixel size mounted along one of\n"
254 "the coordinate axis\n"
255 "\n"
256 "Parameters\n"
257 "----------\n"
258 " sampleAngles .... angular positions of the goniometer (Npoints, Ns)\n"
259 " detectorAngles .. angular positions of the detector goniometer\n"
260 " (Npoints, Nd)\n"
261 " rcch ............ direction + distance of center channel (angles\n"
262 " zero)\n"
263 " sampleAxis ...... string with sample axis directions\n"
264 " detectorAxis .... string with detector axis directions\n"
265 " kappadir ........ rotation axis of a possible kappa circle\n"
266 " cch ............. center channel of the detector\n"
267 " dpixel .......... width of one pixel, same unit as distance rcch\n"
268 " roi ............. region of interest of the detector\n"
269 " dir ............. direction of the detector, e.g.: 'x+'\n"
270 " tilt ............ tilt of the detector direction from dir\n"
271 " UB .............. orientation matrix and reciprocal space conversion\n"
272 " of investigated crystal (9)\n"
273 " sampledis ....... sample displacement vector in same unit as the\n"
274 " detector distance\n"
275 " lambda .......... wavelength of the used x-rays in Angstroem\n"
276 " nthreads ........ number of threads to use in parallel section of the\n"
277 " code\n"
278 " flags ........... integer flags to select sub-function\n"
279 "\n"
280 "Returns\n"
281 "-------\n"
282 " qpos ............ momentum transfer (Npoints * Nch, 3)\n"
283 " \n"
284 },
285 {"ang2q_conversion_area", py_ang2q_conversion_area, METH_VARARGS,
286 "conversion of Npoints of goniometer positions to reciprocal space\n"
287 "for an area detector with a given pixel size mounted along one of\n"
288 "the coordinate axis\n"
289 "\n"
290 "Parameters\n"
291 "----------\n"
292 " sampleAngles .... angular positions of the sample goniometer\n"
293 " (Npoints, Ns)\n"
294 " detectorAngles .. angular positions of the detector goniometer\n"
295 " (Npoints, Nd)\n"
296 " rcch ............ direction + distance of center pixel (angles zero)\n"
297 " sampleAxis ...... string with sample axis directions\n"
298 " detectorAxis .... string with detector axis directions\n"
299 " kappadir ...... rotation axis of a possible kappa circle\n"
300 " cch1 ............ center channel of the detector\n"
301 " cch2 ............ center channel of the detector\n"
302 " dpixel1 ......... width of one pixel in first direction, same unit\n"
303 " as distance rcch\n"
304 " dpixel2 ......... width of one pixel in second direction, same unit\n"
305 " as distance rcch\n"
306 " roi ............. region of interest for the area detector\n"
307 " [dir1min, dir1max, dir2min, dir2max]\n"
308 " dir1 ............ first direction of the detector, e.g.: 'x+'\n"
309 " dir2 ............ second direction of the detector, e.g.: 'z+'\n"
310 " tiltazimuth ..... azimuth of the tilt\n"
311 " tilt ............ tilt of the detector plane (rotation around axis\n"
312 " normal to the direction given by the tiltazimuth\n"
313 " UB .............. orientation matrix and reciprocal space conversion\n"
314 " of investigated crystal (3, 3)\n"
315 " sampledis ....... sample displacement vector in same unit as the\n"
316 " detector distance\n"
317 " lambda .......... wavelength of the used x-rays \n"
318 " nthreads ........ number of threads to use in parallel section of\n"
319 " the code\n"
320 " flags ........... integer flags to select sub-function\n"
321 "\n"
322 "Returns\n"
323 "-------\n"
324 " qpos ............ momentum transfer (Npoints * Npix1 * Npix2, 3)\n"
325 "\n"
326 },
327 {"ang2q_conversion_area_pixel", ang2q_conversion_area_pixel, METH_VARARGS,
328 "conversion of Npoints of detector positions to Q\n"
329 "for an area detector with a given pixel size mounted along one of\n"
330 "the coordinate axis. This function only calculates the q-position for\n"
331 "the pairs of pixel numbers (n1, n2) given in the input and should\n"
332 "therefore be used only for detector calibration purposes.\n"
333 "\n"
334 "Parameters\n"
335 "----------\n"
336 " detectorAngles .. angular positions of the detector goniometer\n"
337 " (Npoints, Nd)\n"
338 " n1 .............. detector pixel numbers dim1 (Npoints)\n"
339 " n2 .............. detector pixel numbers dim2 (Npoints)\n"
340 " rcch ............ direction + distance of center pixel (angles zero)\n"
341 " detectorAxis .... string with detector axis directions\n"
342 " cch1 ............ center channel of the detector\n"
343 " cch2 ............ center channel of the detector\n"
344 " dpixel1 ......... width of one pixel in first direction, same unit as\n"
345 " distance rcch\n"
346 " dpixel2 ......... width of one pixel in second direction, same unit\n"
347 " as distance rcch\n"
348 " dir1 ............ first direction of the detector, e.g.: 'x+'\n"
349 " dir2 ............ second direction of the detector, e.g.: 'z+'\n"
350 " tiltazimuth ..... azimuth of the tilt\n"
351 " tilt ............ tilt of the detector plane (rotation around axis\n"
352 " normal to the direction given by the tiltazimuth\n"
353 " lambda .......... wavelength of the used x-rays\n"
354 " nthreads ........ number of threads to use in parallel section of the\n"
355 " code\n"
356 "\n"
357 "Returns\n"
358 "-------\n"
359 " qpos ............ momentum transfer (Npoints, 3)\n"
360 },
361 {"ang2q_conversion_area_pixel2",
362 ang2q_conversion_area_pixel2, METH_VARARGS,
363 "conversion of Npoints of detector positions to Q.\n"
364 "for an area detector with a given pixel size mounted along one of\n"
365 "the coordinate axis. This function only calculates the q-position for\n"
366 "the pairs of pixel numbers (n1, n2) given in the input and should\n"
367 "therefore be used only for detector calibration purposes.\n"
368 "\n"
369 "This variant of this function also takes a sample orientation matrix\n"
370 "as well as the sample goniometer as input to allow for a simultaneous\n"
371 "fit of reference samples orientation\n"
372 "\n"
373 "Parameters\n"
374 "----------\n"
375 " sampleAngles .... angular positions of the sample goniometer\n"
376 " (Npoints, Ns)\n"
377 " detectorAngles .. angular positions of the detector goniometer\n"
378 " (Npoints, Nd)\n"
379 " n1 .............. detector pixel numbers dim1 (Npoints)\n"
380 " n2 .............. detector pixel numbers dim2 (Npoints)\n"
381 " rcch ............ direction + distance of center pixel (angles zero)\n"
382 " sampleAxis ...... string with sample axis directions\n"
383 " detectorAxis .... string with detector axis directions\n"
384 " cch1 ............ center channel of the detector\n"
385 " cch2 ............ center channel of the detector\n"
386 " dpixel1 ......... width of one pixel in first direction, same unit as\n"
387 " distance rcch\n"
388 " dpixel2 ......... width of one pixel in second direction, same unit\n"
389 " as distance rcch \n"
390 " dir1 ............ first direction of the detector, e.g.: 'x+'\n"
391 " dir2 ............ second direction of the detector, e.g.: 'z+'\n"
392 " tiltazimuth ..... azimuth of the tilt\n"
393 " tilt ............ tilt of the detector plane (rotation around axis\n"
394 " normal to the direction given by the tiltazimuth\n"
395 " UB .............. orientation matrix and reciprocal space conversion\n"
396 " of investigated crystal (3, 3)\n"
397 " lambda .......... wavelength of the used x-rays\n"
398 " nthreads ........ number of threads to use in parallel section of the\n"
399 " code\n"
400 "\n"
401 "Returns\n"
402 "-------\n"
403 " qpos ............ momentum transfer (Npoints, 3)\n"
404 },
405 {"ang2q_detpos", ang2q_detpos, METH_VARARGS,
406 "conversion of Npoints of detector arm angles to real space position\n"
407 "for a setup with point detector\n"
408 "\n"
409 "Parameters\n"
410 "----------\n"
411 " detectorAngles .. angular positions of the detector goniometer\n"
412 " (Npoints, Nd)\n"
413 " ri .............. direction and distance of detector at zero angles\n"
414 " detectorAxis .... string with detector axis directions\n"
415 " nthreads ........ number of threads to use in parallel section of\n"
416 " the code\n"
417 "\n"
418 "Returns\n"
419 "-------\n"
420 " dpos .......... real space detector position (Npoints, 3)\n"
421 },
422 {"ang2q_detpos_linear", ang2q_detpos_linear, METH_VARARGS,
423 "conversion of Npoints of detector arm angles to real space\n"
424 "position for a linear detector\n"
425 "\n"
426 "Parameters\n"
427 "----------\n"
428 " detectorAngles .. angular positions of the detector goniometer\n"
429 " (Npoints, Nd)\n"
430 " rcch ............ direction + distance of center channel\n"
431 " (angles zero)\n"
432 " detectorAxis .... string with detector axis directions\n"
433 " cch ............. center channel of the detector\n"
434 " dpixel .......... width of one pixel, same unit as distance rcch\n"
435 " roi ............. region of interest of the detector\n"
436 " dir ............. direction of the detector, e.g.: 'x+'\n"
437 " tilt ............ tilt of the detector direction from dir\n"
438 " nthreads ........ number of threads to use in parallel section of\n"
439 " the code\n"
440 "\n"
441 "Returns\n"
442 "-------\n"
443 " dpos ............ real space detector position (Npoints * Nch, 3)\n"
444 " \n"
445 },
446 {"ang2q_detpos_area", ang2q_detpos_area, METH_VARARGS,
447 "conversion of Npoints of detector arm angles to reciprocal space\n"
448 "position for an area detector with a given pixel size\n"
449 "\n"
450 "Parameters\n"
451 "----------\n"
452 " detectorAngles .. angular positions of the detector goniometer\n"
453 " (Npoints, Nd)\n"
454 " rcch ............ direction + distance of center pixel (angles zero)\n"
455 " detectorAxis .... string with detector axis directions\n"
456 " cch1 ............ center channel of the detector\n"
457 " cch2 ............ center channel of the detector\n"
458 " dpixel1 ......... width of one pixel in first direction, same unit\n"
459 " as distance rcch\n"
460 " dpixel2 ......... width of one pixel in second direction, same unit\n"
461 " as distance rcch\n"
462 " roi ............. region of interest for the area detector\n"
463 " [dir1min, dir1max, dir2min, dir2max]\n"
464 " dir1 ............ first direction of the detector, e.g.: 'x+'\n"
465 " dir2 ............ second direction of the detector, e.g.: 'z+'\n"
466 " tiltazimuth ..... azimuth of the tilt\n"
467 " tilt ............ tilt of the detector plane (rotation around axis\n"
468 " normal to the direction given by the tiltazimuth\n"
469 " nthreads ........ number of threads to use in parallel section of\n"
470 " the code\n"
471 "\n"
472 "Returns\n"
473 "-------\n"
474 " dpos ............ real space detector position\n"
475 " (Npoints * Npix1 * Npix2, 3)\n"
476 "\n"
477 },
478 {"cbfread", cbfread, METH_VARARGS,
479 "parser for cbf data arrays from Pilatus detector images\n\n"
480 " Parameters\n"
481 " ----------\n"
482 " data: data stream (character array)\n"
483 " nx, ny: number of entries of the two dimensional image\n\n"
484 " Returns\n"
485 " -------\n"
486 " the parsed data values as float ndarray\n"
487 },
488 {NULL, NULL, 0, NULL} /* Sentinel */
489 };
490
491 #if PY_MAJOR_VERSION >= 3
492 static struct PyModuleDef moduledef = {
493 PyModuleDef_HEAD_INIT,
494 "cxrayutilities", /* m_name */
495 "Python C extension including performance critical parts\n"
496 "of xrayutilities (gridder, qconversion, block-averageing)\n", /* m_doc */
497 -1, /* m_size */
498 XRU_Methods, /* m_methods */
499 NULL, /* m_reload */
500 NULL, /* m_traverse */
501 NULL, /* m_clear */
502 NULL, /* m_free */
503 };
504 #endif
505
506 PyMODINIT_FUNC
507 #if PY_MAJOR_VERSION >= 3
508 PyInit_cxrayutilities(void)
509 #else
510 initcxrayutilities(void)
511 #endif
512 {
513 PyObject *m;
514
515 #if PY_MAJOR_VERSION >= 3
516 m = PyModule_Create(&moduledef);
517 #else
518 m = Py_InitModule3("cxrayutilities", XRU_Methods,
519 "Python C extension including performance critical parts\n"
520 "of xrayutilities (gridder, qconversion, block-averageing)\n");
521 #endif
522
523 import_array();
524
525 #if PY_MAJOR_VERSION >= 3
526 return m;
527 #endif
528 }
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013 Dominik Kriegner <dominik.kriegner@gmail.com>
17 */
18
19 #include "xrayutilities.h"
20
21 #include <stdio.h>
22
23 PyObject* cbfread(PyObject *self, PyObject *args) {
24 /* parser for cbf data arrays from Pilatus detector images
25 *
26 * Parameters
27 * ----------
28 * data: data stream (character array)
29 * nx, ny: number of entries of the two dimensional image
30 *
31 * Returns
32 * -------
33 * the parsed data values as float ndarray
34 */
35
36 unsigned int i, start = 0, nx, ny;
37 Py_ssize_t len;
38 unsigned int parsed = 0;
39 PyArrayObject *outarr = NULL;
40 unsigned char *cin;
41 float *cout;
42 npy_intp nout;
43
44 int cur = 0;
45 int diff = 0;
46 unsigned int np = 0;
47
48 union {
49 const unsigned char* uint8;
50 const unsigned short* uint16;
51 const unsigned int* uint32;
52 const char* int8;
53 const short* int16;
54 const int* int32;
55 } parser;
56
57 /* Python argument conversion code */
58 if (!PyArg_ParseTuple(args, "s#ii", &cin, &len, &nx, &ny)) {
59 return NULL;
60 }
61 /* debug output
62 printf("stream length: %d\n", len);
63 printf("entries: %d %d\n", nx, ny); */
64
65 /* create output ndarray */
66 nout = nx * ny;
67 outarr = (PyArrayObject *) PyArray_SimpleNew(1, &nout, NPY_FLOAT);
68 cout = (float *) PyArray_DATA(outarr);
69
70 i = 0;
71 while (i < (long) len - 10) { /* find the start of the array */
72 if ((cin[i] == 0x0c) && (cin[i + 1] == 0x1a) &&
73 (cin[i + 2] == 0x04) && (cin[i + 3] == 0xd5)) {
74 start = i + 4;
75 i = (long) len + 10;
76 }
77 i++;
78 }
79 if (i == (long) len - 10) {
80 PyErr_SetString(PyExc_ValueError,
81 "start of data in stream not found!");
82 return NULL;
83 }
84
85 /* next while loop was taken from pilatus code and adapted by O. Seeck
86 * and D. Kriegner */
87 parser.uint8 = (const unsigned char*) cin + start;
88
89 while (parsed < ((long) len - start)) {
90 if (*parser.uint8 != 0x80) {
91 diff = (int) *parser.int8;
92 parser.int8++;
93 parsed += 1;
94 }
95 else {
96 parser.uint8++;
97 parsed += 1;
98 if (*parser.uint16 != 0x8000) {
99 diff = (int) *parser.int16;
100 parser.int16++;
101 parsed += 2;
102 }
103 else {
104 parser.uint16++;
105 parsed += 2;
106 diff = (int) *parser.int32;
107 parser.int32++;
108 parsed += 4;
109 }
110 }
111 cur += diff;
112 *cout++ = (float) cur;
113 np++;
114 /* check if we already have all data (file might be longer) */
115 if (np == nout) {
116 /* printf("all data read (%d,%d)\n", np, parsed); */
117 break;
118 }
119 }
120
121 /* return output array */
122 return PyArray_Return(outarr);
123 }
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
17 * Copyright (C) 2009-2010 Dominik Kriegner <dominik.kriegner@gmail.com>
18 */
19
20 /***** xrayutilities/gridder
21 * NAME
22 * gridder - module with gridder functions
23 * PURPOSE
24 *
25 * AUTHOR
26 * Eugen Wintersberger
27 ****/
28
29 #pragma once
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <Python.h>
34
35 /* define flags for the gridder functions */
36 #define NO_DATA_INIT 1
37 #define NO_NORMALIZATION 4
38 #define VERBOSE 16
39
40 /*!
41 \brief python interface function
42
43 Python interface function for fuzzygridder1d. This function is virtually doing
44 all the Python related stuff to run fuzzygridder1d function.
45 \param self reference to the module
46 \param args function arguments
47 \return return value of the function
48 */
49 PyObject* pyfuzzygridder1d(PyObject *self, PyObject *args);
50
51 /*---------------------------------------------------------------------------*/
52 /*!
53 \brief 1D single threaded fuzzy gridder
54
55 \param x input x-values
56 \param data input data
57 \param n number of input points
58 \param nx number of steps in x-direction
59 \param xmin minimm along x-direction
60 \param xmax maximum along x-direction
61 \param odata output data
62 \param norm normalization data
63 \param fuzzywidth width of the data for the fuzzy assignment to bins
64 \param flags control falgs
65 */
66 int fuzzygridder1d(double *x, double *data, unsigned int n,
67 unsigned int nx, double xmin, double xmax,
68 double *odata, double *norm, double fuzzywidth, int flags);
69
70 /*!
71 \brief python interface function
72
73 Python interface function for gridder1d. This function is virtually doing all
74 the Python related stuff to run gridder1d function.
75 \param self reference to the module
76 \param args function arguments
77 \return return value of the function
78 */
79 PyObject* pygridder1d(PyObject *self, PyObject *args);
80
81 /*---------------------------------------------------------------------------*/
82 /*!
83 \brief 1D single threaded gridder
84
85 \param x input x-values
86 \param data input data
87 \param n number of input points
88 \param nx number of steps in x-direction
89 \param xmin minimm along x-direction
90 \param xmax maximum along x-direction
91 \param odata output data
92 \param norm normalization data
93 \param flags control falgs
94 */
95 int gridder1d(double *x, double *data, unsigned int n,
96 unsigned int nx, double xmin, double xmax,
97 double *odata, double *norm, int flags);
98
99 /*!
100 \brief python interface function
101
102 Python interface function for gridder2d. This function is virtually doing all
103 the Python related stuff to run gridder2d function.
104 \param self reference to the module
105 \param args function arguments
106 \return return value of the function
107 */
108 PyObject* pygridder2d(PyObject *self, PyObject *args);
109
110 /*---------------------------------------------------------------------------*/
111 /*!
112 \brief 2D single threaded gridder
113
114 \param x input x-values
115 \param y input y-values
116 \param data input data
117 \param n number of input points
118 \param nx number of steps in x-direction
119 \param ny number of steps in y-direction
120 \param xmin minimm along x-direction
121 \param xmax maximum along x-direction
122 \param ymin minimum along y-direction
123 \param ymax maximum along y-direction
124 \param odata output data
125 \param norm normalization data
126 \param flags control falgs
127 */
128 int gridder2d(double *x, double *y, double *data, unsigned int n,
129 unsigned int nx, unsigned int ny,
130 double xmin, double xmax,
131 double ymin, double ymax,
132 double *odata, double *norm, int flags);
133
134 /*!
135 \brief python interface function
136
137 Python interface function for fuzzygridder2d. This function is virtually doing
138 all the Python related stuff to run the fuzzygridder2d function.
139 \param self reference to the module
140 \param args function arguments
141 \return return value of the function
142 */
143 PyObject* pyfuzzygridder2d(PyObject *self, PyObject *args);
144
145 /*---------------------------------------------------------------------------*/
146 /*!
147 \brief 2D single threaded fuzzy gridder
148
149 \param x input x-values
150 \param y input y-values
151 \param data input data
152 \param n number of input points
153 \param nx number of steps in x-direction
154 \param ny number of steps in y-direction
155 \param xmin minimm along x-direction
156 \param xmax maximum along x-direction
157 \param ymin minimum along y-direction
158 \param ymax maximum along y-direction
159 \param odata output data
160 \param norm normalization data
161 \param wx fuzzy size of data along x-direction
162 \param wy fuzzy size of data along y-direction
163 \param flags control falgs
164 */
165 int fuzzygridder2d(double *x, double *y, double *data, unsigned int n,
166 unsigned int nx, unsigned int ny,
167 double xmin, double xmax,
168 double ymin, double ymax,
169 double *odata, double *norm,
170 double wx, double wy, int flags);
171
172 /*---------------------------------------------------------------------------*/
173 /*!
174 \brief 3D gridder python interface function
175
176 Python interface function for gridder3d. This function is virtually doing all
177 the Python related stuff to run gridder3d function.
178 \param self reference to the module
179 \param args function arguments
180 \return return value of the function
181 */
182 PyObject* pyfuzzygridder3d(PyObject *self, PyObject *args);
183
184 /*---------------------------------------------------------------------------*/
185 /*!
186 \brief single threaded 3d gridder
187
188 Gridder code rebinning scatterd data onto a regular grid in 3 dimensions.
189
190 \param x pointer to x-coordinates of input data
191 \param y pointer to y-coordinates of input data
192 \param z pointer to z-coordinates of input data
193 \param data pointer to input data
194 \param n number of input points
195 \param nx number of grid points along the x-direction
196 \param ny number of grid points along the y-direction
197 \param nz number of grid points along the z-direction
198 \param xmin minimum value of x-axis on the grid
199 \param xmax maximum value of x-axis on the grid
200 \param ymin minimum value of y-axis on the grid
201 \param ymax maximum value of y-axis on the grid
202 \param zmin minimum value of z-axis on the grid
203 \param zmax maximum value of z-axis on the grid
204 \param odata pointer to grid data (output data)
205 \param norm pointer to optional normalization from previous run
206 \param wx fuzzy width parameter in x-direction
207 \param wy fuzzy width parameter in y-direction
208 \param wz fuzzy width parameter in z-direction
209 \param flags gridder flags
210 */
211 int fuzzygridder3d(double *x, double *y, double *z, double *data,
212 unsigned int n, unsigned int nx, unsigned int ny,
213 unsigned int nz, double xmin, double xmax, double ymin,
214 double ymax, double zmin, double zmax, double *odata,
215 double *norm, double wx, double wy, double wz, int flags);
216
217 /*---------------------------------------------------------------------------*/
218 /*!
219 \brief 3D gridder python interface function
220
221 Python interface function for gridder3d. This function is virtually doing all
222 the Python related stuff to run gridder3d function.
223 \param self reference to the module
224 \param args function arguments
225 \return return value of the function
226 */
227 PyObject* pygridder3d(PyObject *self, PyObject *args);
228
229 /*---------------------------------------------------------------------------*/
230 /*!
231 \brief single threaded 3d gridder
232
233 Gridder code rebinning scatterd data onto a regular grid in 3 dimensions.
234
235 \param x pointer to x-coordinates of input data
236 \param y pointer to y-coordinates of input data
237 \param z pointer to z-coordinates of input data
238 \param data pointer to input data
239 \param n number of input points
240 \param nx number of grid points along the x-direction
241 \param ny number of grid points along the y-direction
242 \param nz number of grid points along the z-direction
243 \param xmin minimum value of x-axis on the grid
244 \param xmax maximum value of x-axis on the grid
245 \param ymin minimum value of y-axis on the grid
246 \param ymax maximum value of y-axis on the grid
247 \param zmin minimum value of z-axis on the grid
248 \param zmax maximum value of z-axis on the grid
249 \param odata pointer to grid data (output data)
250 \param norm pointer to optional normalization from previous run
251 \param flags gridder flags
252 */
253 int gridder3d(double *x, double *y, double *z, double *data, unsigned int n,
254 unsigned int nx, unsigned int ny, unsigned int nz,
255 double xmin, double xmax, double ymin, double ymax,
256 double zmin, double zmax,
257 double *odata, double *norm, int flags);
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2014 Dominik Kriegner <dominik.kriegner@gmail.com>
17 *
18 ******************************************************************************
19 *
20 * created: Sep 16, 2014
21 * author: Dominik Kriegner
22 */
23
24 #include "gridder.h"
25 #include "gridder_utils.h"
26
27 PyObject* pyfuzzygridder1d(PyObject *self, PyObject *args)
28 {
29 PyArrayObject *py_x = NULL, *py_data = NULL,
30 *py_output = NULL, *py_norm = NULL;
31
32 double *x = NULL, *data = NULL, *odata = NULL, *norm = NULL;
33 double xmin, xmax, fuzzywidth;
34 unsigned int nx;
35 int flags;
36 int n, result;
37
38 if (!PyArg_ParseTuple(args, "O!O!IddO!|O!di",
39 &PyArray_Type, &py_x,
40 &PyArray_Type, &py_data,
41 &nx, &xmin, &xmax,
42 &PyArray_Type, &py_output,
43 &PyArray_Type, &py_norm,
44 &fuzzywidth, &flags))
45 return NULL;
46
47 /* have to check input variables */
48 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
49 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
50 "input data must be a 1D double array!");
51 PYARRAY_CHECK(py_output, 1, NPY_DOUBLE,
52 "ouput data must be a 1D double array!");
53 if (py_norm != NULL)
54 PYARRAY_CHECK(py_norm, 1, NPY_DOUBLE,
55 "norm data must be a 1D double array!");
56
57 /* get data */
58 x = (double *) PyArray_DATA(py_x);
59 data = (double *) PyArray_DATA(py_data);
60 odata = (double *) PyArray_DATA(py_output);
61 if (py_norm != NULL) {
62 norm = (double *) PyArray_DATA(py_norm);
63 }
64
65 /* get the total number of points */
66 n = (int) PyArray_SIZE(py_x);
67
68 /* call the actual gridder routine */
69 result = fuzzygridder1d(x, data, n, nx, xmin, xmax, odata,
70 norm, fuzzywidth, flags);
71
72 /* clean up */
73 Py_DECREF(py_x);
74 Py_DECREF(py_data);
75 Py_DECREF(py_output);
76 if (py_norm != NULL) {
77 Py_DECREF(py_norm);
78 }
79
80 return Py_BuildValue("i", &result);
81 }
82
83 /*---------------------------------------------------------------------------*/
84 int fuzzygridder1d(double *x, double *data, unsigned int n,
85 unsigned int nx,
86 double xmin, double xmax,
87 double *odata, double *norm, double fuzzywidth, int flags)
88 {
89 double *gnorm;
90 unsigned int offset1, offset2;
91 unsigned int noutofbounds = 0; /* counter for out of bounds points */
92
93 double dx = delta(xmin, xmax, nx);
94 double fraction, dwidth; /* fuzzy fraction and data width */
95
96 unsigned int i, j; /* loop indices */
97
98 /* initialize data if requested */
99 if (!(flags & NO_DATA_INIT)) set_array(odata, nx, 0.);
100
101 /* check if normalization array is passed */
102 if (norm == NULL) {
103 gnorm = malloc(sizeof(double) * nx);
104 if (gnorm == NULL) {
105 fprintf(stderr, "XU.FuzzyGridder1D(c): Cannot allocate memory for "
106 "normalization buffer!\n");
107 return -1;
108 }
109 /* initialize memory for norm */
110 set_array(gnorm, nx, 0.);
111 }
112 else {
113 if (flags & VERBOSE) {
114 fprintf(stdout, "XU.FuzzyGridder1D(c): use user provided buffer "
115 "for normalization data\n");
116 }
117 gnorm = norm;
118 }
119
120 /* the master loop over all data points */
121 dwidth = fuzzywidth / dx;
122 if (flags & VERBOSE) {
123 fprintf(stdout, "XU.FuzzyGridder1D(c): fuzzyness: %f %f\n",
124 fuzzywidth, dwidth);
125 }
126 for (i = 0; i < n; i++) {
127 /* if data point is nan ignore it */
128 if (!isnan(data[i])) {
129 /* if the x value is outside the grid boundaries continue with
130 * the next point */
131 if ((x[i] < (xmin - fuzzywidth/2.)) || (x[i] > xmax + fuzzywidth/2.)) {
132 noutofbounds++;
133 continue;
134 }
135 /* compute the linear offset and distribute the data to the bins */
136 if ((x[i] - fuzzywidth / 2.) <= xmin) {
137 offset1 = 0;
138 }
139 else {
140 offset1 = gindex(x[i] - fuzzywidth / 2., xmin, dx);
141 }
142 offset2 = gindex(x[i] + fuzzywidth / 2., xmin, dx);
143 offset2 = offset2 < nx ? offset2 : nx - 1;
144 for(j = offset1; j <= offset2; j++) {
145 if (offset1 == offset2) {
146 fraction = 1.;
147 }
148 else if (j == offset1) {
149 fraction = (j + 1 - (x[i] - fuzzywidth / 2. - xmin + dx / 2.) / dx) / dwidth;
150 }
151 else if (j == offset2) {
152 fraction = ((x[i] + fuzzywidth / 2. - xmin + dx / 2.) / dx - j) / dwidth;
153 }
154 else {
155 fraction = 1 / dwidth;
156 }
157 odata[j] += data[i]*fraction;
158 gnorm[j] += fraction;
159 }
160 }
161 }
162
163 /* perform normalization */
164 if (!(flags & NO_NORMALIZATION)) {
165 if (flags & VERBOSE) {
166 fprintf(stdout, "XU.FuzzyGridder1D(c): perform normalization\n");
167 }
168
169 for (i = 0; i < nx; i++) {
170 if (gnorm[i] > 1.e-16) {
171 odata[i] = odata[i] / gnorm[i];
172 }
173 }
174 }
175
176 /* free the norm buffer if it has been locally allocated */
177 if (norm == NULL) free(gnorm);
178
179 /* warn the user in case more than half the data points where out
180 * of the gridding area */
181 if (noutofbounds > n / 2) {
182 fprintf(stdout, "XU.FuzzyGridder1D(c): more than half of the "
183 "datapoints out of the data range, consider regridding"
184 " with extended range!\n");
185 }
186 else if (flags & VERBOSE) {
187 fprintf(stdout, "XU.FuzzyGridder1D(c): %d datapoints out of the data "
188 "range!\n", noutofbounds);
189 }
190
191 return 0;
192 }
193
194 /*---------------------------------------------------------------------------*/
195 PyObject* pygridder1d(PyObject *self, PyObject *args)
196 {
197 PyArrayObject *py_x = NULL, *py_data = NULL,
198 *py_output = NULL, *py_norm = NULL;
199
200 double *x = NULL, *data = NULL, *odata = NULL, *norm = NULL;
201 double xmin, xmax;
202 unsigned int nx;
203 int flags;
204 int n, result;
205
206 if (!PyArg_ParseTuple(args, "O!O!IddO!|O!i",
207 &PyArray_Type, &py_x,
208 &PyArray_Type, &py_data,
209 &nx, &xmin, &xmax,
210 &PyArray_Type, &py_output,
211 &PyArray_Type, &py_norm,
212 &flags))
213 return NULL;
214
215 /* have to check input variables */
216 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
217 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
218 "input data must be a 1D double array!");
219 PYARRAY_CHECK(py_output, 1, NPY_DOUBLE,
220 "ouput data must be a 1D double array!");
221 if (py_norm != NULL)
222 PYARRAY_CHECK(py_norm, 1, NPY_DOUBLE,
223 "norm data must be a 1D double array!");
224
225 /* get data */
226 x = (double *) PyArray_DATA(py_x);
227 data = (double *) PyArray_DATA(py_data);
228 odata = (double *) PyArray_DATA(py_output);
229 if (py_norm != NULL) {
230 norm = (double *) PyArray_DATA(py_norm);
231 }
232
233 /* get the total number of points */
234 n = (int) PyArray_SIZE(py_x);
235
236 /* call the actual gridder routine */
237 result = gridder1d(x, data, n, nx, xmin, xmax, odata, norm, flags);
238
239 /* clean up */
240 Py_DECREF(py_x);
241 Py_DECREF(py_data);
242 Py_DECREF(py_output);
243 if (py_norm != NULL) {
244 Py_DECREF(py_norm);
245 }
246
247 return Py_BuildValue("i", &result);
248 }
249
250 /*---------------------------------------------------------------------------*/
251 int gridder1d(double *x, double *data, unsigned int n,
252 unsigned int nx,
253 double xmin, double xmax,
254 double *odata, double *norm, int flags)
255 {
256 double *gnorm;
257 unsigned int offset;
258 unsigned int noutofbounds = 0; /* counter for out of bounds points */
259
260 double dx = delta(xmin, xmax, nx);
261
262 unsigned int i; /* loop index */
263
264 /* initialize data if requested */
265 if (!(flags & NO_DATA_INIT)) set_array(odata, nx, 0.);
266
267 /* check if normalization array is passed */
268 if (norm == NULL) {
269 gnorm = malloc(sizeof(double) * nx);
270 if (gnorm == NULL) {
271 fprintf(stderr, "XU.Gridder1D(c): Cannot allocate memory for "
272 "normalization buffer!\n");
273 return -1;
274 }
275 /* initialize memory for norm */
276 set_array(gnorm, nx, 0.);
277 }
278 else {
279 if (flags & VERBOSE) {
280 fprintf(stdout, "XU.Gridder1D(c): use user provided buffer for "
281 "normalization data\n");
282 }
283 gnorm = norm;
284 }
285
286 /* the master loop over all data points */
287 for (i = 0; i < n; i++) {
288 /* if data point is nan ignore it */
289 if (!isnan(data[i])) {
290 /* if the x value is outside the grid boundaries continue with
291 * the next point */
292 if ((x[i] < xmin) || (x[i] > xmax)) {
293 noutofbounds++;
294 continue;
295 }
296 /* compute the linear offset and set the data */
297 offset = gindex(x[i], xmin, dx);
298
299 odata[offset] += data[i];
300 gnorm[offset] += 1.;
301 }
302 }
303
304 /* perform normalization */
305 if (!(flags & NO_NORMALIZATION)) {
306 if (flags & VERBOSE) {
307 fprintf(stdout, "XU.Gridder1D(c): perform normalization ...\n");
308 }
309
310 for (i = 0; i < nx; i++) {
311 if (gnorm[i] > 1.e-16) {
312 odata[i] = odata[i] / gnorm[i];
313 }
314 }
315 }
316
317 /* free the norm buffer if it has been locally allocated */
318 if (norm == NULL) free(gnorm);
319
320 /* warn the user in case more than half the data points where out
321 * of the gridding area */
322 if (noutofbounds > n / 2) {
323 fprintf(stdout, "XU.Gridder1D(c): more than half of the datapoints "
324 "out of the data range, consider regridding with "
325 "extended range!\n");
326 }
327
328 return 0;
329 }
330
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
17 * Copyright (C) 2013, 2015 Dominik Kriegner <dominik.kriegner@gmail.com>
18 *
19 ******************************************************************************
20 *
21 * created: Jun 8, 2013
22 * author: Eugen Wintersberger
23 */
24
25 #include "gridder.h"
26 #include "gridder_utils.h"
27
28
29 PyObject* pyfuzzygridder2d(PyObject *self, PyObject *args)
30 {
31 PyArrayObject *py_x = NULL, *py_y = NULL, *py_data = NULL,
32 *py_output = NULL, *py_norm = NULL;
33
34 double *x = NULL, *y = NULL, *data = NULL, *odata = NULL, *norm = NULL;
35 double xmin, xmax, ymin, ymax, wx, wy;
36 unsigned int nx, ny;
37 int flags;
38 int n, result;
39
40 if (!PyArg_ParseTuple(args, "O!O!O!IIddddO!|O!ddi",
41 &PyArray_Type, &py_x,
42 &PyArray_Type, &py_y,
43 &PyArray_Type, &py_data,
44 &nx, &ny, &xmin, &xmax, &ymin, &ymax,
45 &PyArray_Type, &py_output,
46 &PyArray_Type, &py_norm,
47 &wx, &wy, &flags)) {
48 return NULL;
49 }
50
51 /* have to check input variables */
52 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
53 PYARRAY_CHECK(py_y, 1, NPY_DOUBLE, "y-axis must be a 1D double array!");
54 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
55 "input data must be a 1D double array!");
56 PYARRAY_CHECK(py_output, 2, NPY_DOUBLE,
57 "ouput data must be a 2D double array!");
58 if (py_norm != NULL) {
59 PYARRAY_CHECK(py_norm, 2, NPY_DOUBLE,
60 "norm data must be a 2D double array!");
61 }
62
63 /* get data */
64 x = (double *) PyArray_DATA(py_x);
65 y = (double *) PyArray_DATA(py_y);
66 data = (double *) PyArray_DATA(py_data);
67 odata = (double *) PyArray_DATA(py_output);
68 if (py_norm != NULL) {
69 norm = (double *) PyArray_DATA(py_norm);
70 }
71
72 /* get the total number of points */
73 n = (int) PyArray_SIZE(py_x);
74
75 /* call the actual gridder routine */
76 result = fuzzygridder2d(x, y, data, n, nx, ny, xmin, xmax, ymin, ymax, odata,
77 norm, wx, wy, flags);
78
79 /* clean up */
80 Py_DECREF(py_x);
81 Py_DECREF(py_y);
82 Py_DECREF(py_data);
83 Py_DECREF(py_output);
84 if (py_norm != NULL) {
85 Py_DECREF(py_norm);
86 }
87
88 return Py_BuildValue("i", &result);
89 }
90
91 /*--------------------------------------------------------------------------*/
92 int fuzzygridder2d(double *x, double *y, double *data, unsigned int n,
93 unsigned int nx, unsigned int ny,
94 double xmin, double xmax, double ymin, double ymax,
95 double *odata, double *norm, double wx, double wy,
96 int flags)
97 {
98 double *gnorm;
99 unsigned int offset, offsetx1, offsetx2, offsety1, offsety2;
100 unsigned int ntot = nx * ny; /* total number of points on the grid */
101 unsigned int noutofbounds = 0; /* number of points out of bounds */
102
103 double fractionx, fractiony, dwx, dwy; /* variables for the fuzzy part */
104 double dx = delta(xmin, xmax, nx);
105 double dy = delta(ymin, ymax, ny);
106
107 unsigned int i, j, k; /* loop indices */
108
109 /* initialize data if requested */
110 if (!(flags & NO_DATA_INIT)) {
111 set_array(odata, ntot, 0.);
112 }
113
114 /* check if normalization array is passed */
115 if (norm == NULL) {
116 gnorm = malloc(sizeof(double) * (nx * ny));
117 if (gnorm == NULL) {
118 fprintf(stderr, "XU.FuzzyGridder2D(c): Cannot allocate memory for"
119 " normalization buffer!\n");
120 return -1;
121 }
122 /* initialize memory for norm */
123 set_array(gnorm, nx * ny, 0.);
124 }
125 else {
126 if (flags & VERBOSE) {
127 fprintf(stdout, "XU.FuzzyGridder2D(c): use user provided buffer "
128 "for normalization data\n");
129 }
130 gnorm = norm;
131 }
132
133 /* calculate the fuzzy spread in number of bin sizes */
134 dwx = wx / dx;
135 dwy = wy / dy;
136 if (flags & VERBOSE) {
137 fprintf(stdout, "XU.FuzzyGridder2D(c): fuzzyness: %f %f %f %f\n",
138 wx, wy, dwx, dwy);
139 }
140 /* the master loop over all data points */
141 for (i = 0; i < n; i++) {
142 /* if data point is nan ignore it */
143 if (!isnan(data[i])) {
144 /* if the x and y values are outside the grids boundaries
145 * continue with the next point */
146 if ((x[i] < xmin) || (x[i] > xmax)) {
147 noutofbounds++;
148 continue;
149 }
150 if ((y[i] < ymin) || (y[i] > ymax)) {
151 noutofbounds++;
152 continue;
153 }
154 /* compute the linear offset and distribute the data to the bins */
155 if ((x[i] - wx / 2.) <= xmin) {
156 offsetx1 = 0;
157 }
158 else {
159 offsetx1 = gindex(x[i] - wx / 2., xmin, dx);
160 }
161 offsetx2 = gindex(x[i] + wx / 2., xmin, dx);
162 offsetx2 = offsetx2 < nx ? offsetx2 : nx - 1;
163 if ((y[i] - wy / 2.) <= ymin) {
164 offsety1 = 0;
165 }
166 else {
167 offsety1 = gindex(y[i] - wy / 2., ymin, dy);
168 }
169 offsety2 = gindex(y[i] + wy / 2., ymin, dy);
170 offsety2 = offsety2 < ny ? offsety2 : ny - 1;
171
172 for(j = offsetx1; j <= offsetx2; j++) {
173 if (offsetx1 == offsetx2) {
174 fractionx = 1.;
175 }
176 else if (j == offsetx1) {
177 fractionx = (j + 1 - (x[i] - wx / 2. - xmin + dx / 2.) / dx) / dwx;
178 }
179 else if (j == offsetx2) {
180 fractionx = ((x[i] + wx / 2. - xmin + dx / 2.) / dx - j) / dwx;
181 }
182 else {
183 fractionx = 1 / dwx;
184 }
185
186 for(k = offsety1; k <= offsety2; k++) {
187 if (offsety1 == offsety2) {
188 fractiony = 1.;
189 }
190 else if (k == offsety1) {
191 fractiony = (k + 1 - (y[i] - wy / 2. - ymin + dy / 2.) / dy) / dwy;
192 }
193 else if (k == offsety2) {
194 fractiony = ((y[i] + wy / 2. - ymin + dy / 2.) / dy - k) / dwy;
195 }
196 else {
197 fractiony = 1 / dwy;
198 }
199
200 offset = j * ny + k;
201 odata[offset] += data[i]*fractionx*fractiony;
202 gnorm[offset] += fractionx*fractiony;
203 }
204 }
205 }
206 }
207
208 /* perform normalization */
209 if (!(flags & NO_NORMALIZATION)) {
210 if (flags & VERBOSE)
211 fprintf(stdout, "XU.FuzzyGridder2D(c): perform normalization\n");
212
213 for (i = 0; i < nx * ny; i++) {
214 if (gnorm[i] > 1.e-16) {
215 odata[i] = odata[i] / gnorm[i];
216 }
217 }
218 }
219
220 /* free the norm buffer if it has been locally allocated */
221 if (norm == NULL) {
222 free(gnorm);
223 }
224
225 /* warn the user in case more than half the data points where out
226 * of the gridding area */
227 if (noutofbounds > n / 2) {
228 fprintf(stdout,"XU.FuzzyGridder2D(c): more than half of the datapoints"
229 " out of the data range, consider regridding with"
230 " extended range!\n");
231 }
232
233 return 0;
234 }
235
236
237 /*---------------------------------------------------------------------------*/
238 PyObject* pygridder2d(PyObject *self, PyObject *args)
239 {
240 PyArrayObject *py_x = NULL, *py_y = NULL, *py_data = NULL,
241 *py_output = NULL, *py_norm = NULL;
242
243 double *x = NULL, *y = NULL, *data = NULL, *odata = NULL, *norm = NULL;
244 double xmin, xmax, ymin, ymax;
245 unsigned int nx, ny;
246 int flags;
247 int n, result;
248
249 if (!PyArg_ParseTuple(args, "O!O!O!IIddddO!|O!i",
250 &PyArray_Type, &py_x,
251 &PyArray_Type, &py_y,
252 &PyArray_Type, &py_data,
253 &nx, &ny, &xmin, &xmax, &ymin, &ymax,
254 &PyArray_Type, &py_output,
255 &PyArray_Type, &py_norm,
256 &flags)) {
257 return NULL;
258 }
259
260 /* have to check input variables */
261 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
262 PYARRAY_CHECK(py_y, 1, NPY_DOUBLE, "y-axis must be a 1D double array!");
263 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
264 "input data must be a 1D double array!");
265 PYARRAY_CHECK(py_output, 2, NPY_DOUBLE,
266 "ouput data must be a 2D double array!");
267 if (py_norm != NULL) {
268 PYARRAY_CHECK(py_norm, 2, NPY_DOUBLE,
269 "norm data must be a 2D double array!");
270 }
271
272 /* get data */
273 x = (double *) PyArray_DATA(py_x);
274 y = (double *) PyArray_DATA(py_y);
275 data = (double *) PyArray_DATA(py_data);
276 odata = (double *) PyArray_DATA(py_output);
277 if (py_norm != NULL) {
278 norm = (double *) PyArray_DATA(py_norm);
279 }
280
281 /* get the total number of points */
282 n = (int) PyArray_SIZE(py_x);
283
284 /* call the actual gridder routine */
285 result = gridder2d(x, y, data, n, nx, ny, xmin, xmax, ymin, ymax, odata,
286 norm, flags);
287
288 /* clean up */
289 Py_DECREF(py_x);
290 Py_DECREF(py_y);
291 Py_DECREF(py_data);
292 Py_DECREF(py_output);
293 if (py_norm != NULL) {
294 Py_DECREF(py_norm);
295 }
296
297 return Py_BuildValue("i", &result);
298 }
299
300 /*--------------------------------------------------------------------------*/
301 int gridder2d(double *x, double *y, double *data, unsigned int n,
302 unsigned int nx, unsigned int ny,
303 double xmin, double xmax, double ymin, double ymax,
304 double *odata, double *norm, int flags)
305 {
306 double *gnorm;
307 unsigned int offset;
308 unsigned int ntot = nx * ny; /* total number of points on the grid */
309 unsigned int noutofbounds = 0; /* number of points out of bounds */
310
311 double dx = delta(xmin, xmax, nx);
312 double dy = delta(ymin, ymax, ny);
313
314 unsigned int i; /* loop index */
315
316 /* initialize data if requested */
317 if (!(flags & NO_DATA_INIT)) {
318 set_array(odata, ntot, 0.);
319 }
320
321 /* check if normalization array is passed */
322 if (norm == NULL) {
323 gnorm = malloc(sizeof(double) * (nx * ny));
324 if (gnorm == NULL) {
325 fprintf(stderr, "XU.Gridder2D(c): Cannot allocate memory for "
326 "normalization buffer!\n");
327 return -1;
328 }
329 /* initialize memory for norm */
330 set_array(gnorm, nx * ny, 0.);
331 }
332 else {
333 if (flags & VERBOSE) {
334 fprintf(stdout, "XU.Gridder2D(c): use user provided buffer for "
335 "normalization data\n");
336 }
337 gnorm = norm;
338 }
339
340 /* the master loop over all data points */
341 for (i = 0; i < n; i++) {
342 /* if data point is nan ignore it */
343 if (!isnan(data[i])) {
344 /* if the x and y values are outside the grids boundaries
345 * continue with the next point */
346 if ((x[i] < xmin) || (x[i] > xmax)) {
347 noutofbounds++;
348 continue;
349 }
350 if ((y[i] < ymin) || (y[i] > ymax)) {
351 noutofbounds++;
352 continue;
353 }
354 /* compute the linear offset and set the data */
355 offset = gindex(x[i], xmin, dx) * ny + gindex(y[i], ymin, dy);
356
357 odata[offset] += data[i];
358 gnorm[offset] += 1.;
359 }
360 }
361
362 /* perform normalization */
363 if (!(flags & NO_NORMALIZATION)) {
364 if (flags & VERBOSE)
365 fprintf(stdout, "XU.Gridder2D(c): perform normalization ...\n");
366
367 for (i = 0; i < nx * ny; i++) {
368 if (gnorm[i] > 1.e-16) {
369 odata[i] = odata[i] / gnorm[i];
370 }
371 }
372 }
373
374 /* free the norm buffer if it has been locally allocated */
375 if (norm == NULL) {
376 free(gnorm);
377 }
378
379 /* warn the user in case more than half the data points where out
380 * of the gridding area */
381 if (noutofbounds > n / 2) {
382 fprintf(stdout,"XU.Gridder2D(c): more than half of the datapoints out "
383 "of the data range, consider regridding with extended "
384 "range!\n");
385 }
386
387 return 0;
388 }
389
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
17 * Copyright (C) 2013, 2015 Dominik Kriegner <dominik.kriegner@gmail.com>
18 *
19 ******************************************************************************
20 *
21 * created: Jun 21, 2013
22 * author: Eugen Wintersberger
23 */
24
25 #include "gridder.h"
26 #include "gridder_utils.h"
27
28 PyObject* pyfuzzygridder3d(PyObject *self, PyObject *args)
29 {
30 PyArrayObject *py_x = NULL, *py_y = NULL, *py_z = NULL, *py_data = NULL,
31 *py_output = NULL, *py_norm = NULL;
32
33 double *x = NULL, *y = NULL, *z = NULL, *data = NULL, *odata = NULL,
34 *norm = NULL;
35 double xmin, xmax, ymin, ymax, zmin, zmax, wx, wy, wz;
36 unsigned int nx, ny, nz;
37 int flags;
38 int n, result;
39
40 if (!PyArg_ParseTuple(args, "O!O!O!O!IIIddddddO!|O!dddi",
41 &PyArray_Type, &py_x,
42 &PyArray_Type, &py_y,
43 &PyArray_Type, &py_z,
44 &PyArray_Type, &py_data,
45 &nx, &ny, &nz,
46 &xmin, &xmax, &ymin, &ymax, &zmin, &zmax,
47 &PyArray_Type, &py_output,
48 &PyArray_Type, &py_norm,
49 &wx, &wy, &wz, &flags)) {
50 return NULL;
51 }
52
53 /* check input variables */
54 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
55 PYARRAY_CHECK(py_y, 1, NPY_DOUBLE, "y-axis must be a 1D double array!");
56 PYARRAY_CHECK(py_z, 1, NPY_DOUBLE, "z-axis must be a 1D double array!");
57 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
58 "input data must be a 1D double array!");
59 PYARRAY_CHECK(py_output, 3, NPY_DOUBLE,
60 "ouput data must be a 2D double array!");
61 if (py_norm != NULL) {
62 PYARRAY_CHECK(py_norm, 3, NPY_DOUBLE,
63 "norm data must be a 2D double array!");
64 }
65
66 /* get data */
67 x = (double *) PyArray_DATA(py_x);
68 y = (double *) PyArray_DATA(py_y);
69 z = (double *) PyArray_DATA(py_z);
70 data = (double *) PyArray_DATA(py_data);
71 odata = (double *) PyArray_DATA(py_output);
72 if (py_norm != NULL) {
73 norm = (double *) PyArray_DATA(py_norm);
74 }
75
76 /* get the total number of points */
77 n = (int) PyArray_SIZE(py_x);
78
79 /* call the actual gridder routine */
80 result = fuzzygridder3d(x, y, z, data, n, nx, ny, nz,
81 xmin, xmax, ymin, ymax, zmin, zmax, odata, norm,
82 wx, wy, wz, flags);
83
84 /* clean up */
85 Py_DECREF(py_x);
86 Py_DECREF(py_y);
87 Py_DECREF(py_z);
88 Py_DECREF(py_data);
89 Py_DECREF(py_output);
90 if (py_norm != NULL) {
91 Py_DECREF(py_norm);
92 }
93
94 return Py_BuildValue("i", &result);
95 }
96
97 /*---------------------------------------------------------------------------*/
98 int fuzzygridder3d(double *x, double *y, double *z, double *data,
99 unsigned int n, unsigned int nx, unsigned int ny,
100 unsigned int nz, double xmin, double xmax, double ymin,
101 double ymax, double zmin, double zmax,
102 double *odata, double *norm,
103 double wx, double wy, double wz, int flags)
104 {
105 double *gnorm; /* pointer to normalization data */
106 unsigned int offset, offsetx1, offsetx2, offsety1, offsety2, offsetz1, offsetz2;
107 unsigned int ntot = nx * ny * nz; /* total number of points on the grid */
108 unsigned int i, j, k, l; /* loop indeces variables */
109 unsigned int noutofbounds = 0; /* number of points out of bounds */
110
111 double fractionx, fractiony, fractionz, dwx, dwy, dwz; /* variables for
112 the fuzziness */
113 /* compute step width for the grid */
114 double dx = delta(xmin, xmax, nx);
115 double dy = delta(ymin, ymax, ny);
116 double dz = delta(zmin, zmax, nz);
117
118 /* initialize data if requested */
119 if (!(flags & NO_DATA_INIT)) {
120 set_array(odata, ntot, 0.);
121 }
122
123 /* check if normalization array is passed */
124 if (norm == NULL) {
125 gnorm = malloc(sizeof(double) * ntot);
126 if (gnorm == NULL) {
127 fprintf(stderr, "XU.FuzzyGridder3D(c): Cannot allocate memory for "
128 "normalization buffer!\n");
129 return -1;
130 }
131 /* initialize memory for norm */
132 set_array(gnorm, ntot, 0.);
133 }
134 else {
135 gnorm = norm;
136 }
137
138 /* calculate the fuzzy spread in number of bin sizes */
139 dwx = wx / dx;
140 dwy = wy / dy;
141 dwz = wz / dz;
142 if (flags & VERBOSE) {
143 fprintf(stdout, "XU.FuzzyGridder3D(c): fuzzyness: %f %f %f %f %f %f\n",
144 wx, wy, wz, dwx, dwy, dwz);
145 }
146 /* the master loop over all data points */
147 for (i = 0; i < n; i++) {
148 if (!isnan(data[i])) {
149 /* check if the current point is within the bounds of the grid */
150 if ((x[i] < xmin) || (x[i] > xmax)) {
151 noutofbounds++;
152 continue;
153 }
154 if ((y[i] < ymin) || (y[i] > ymax)) {
155 noutofbounds++;
156 continue;
157 }
158 if ((z[i] < zmin) || (z[i] > zmax)) {
159 noutofbounds++;
160 continue;
161 }
162
163 /* compute the offset value of the current input point on the
164 * grid array */
165 /* compute the linear offset and distribute the data to the bins */
166 if ((x[i] - wx / 2.) <= xmin) {
167 offsetx1 = 0;
168 }
169 else {
170 offsetx1 = gindex(x[i] - wx / 2., xmin, dx);
171 }
172 offsetx2 = gindex(x[i] + wx / 2., xmin, dx);
173 offsetx2 = offsetx2 < nx ? offsetx2 : nx - 1;
174 if ((y[i] - wy / 2.) <= ymin) {
175 offsety1 = 0;
176 }
177 else {
178 offsety1 = gindex(y[i] - wy / 2., ymin, dy);
179 }
180 offsety2 = gindex(y[i] + wy / 2., ymin, dy);
181 offsety2 = offsety2 < ny ? offsety2 : ny - 1;
182 if ((z[i] - wz / 2.) <= zmin) {
183 offsetz1 = 0;
184 }
185 else {
186 offsetz1 = gindex(z[i] - wz / 2., zmin, dz);
187 }
188 offsetz2 = gindex(z[i] + wz / 2., zmin, dz);
189 offsetz2 = offsetz2 < nz ? offsetz2 : nz - 1;
190
191 for(j = offsetx1; j <= offsetx2; j++) {
192 if (offsetx1 == offsetx2) {
193 fractionx = 1.;
194 }
195 else if (j == offsetx1) {
196 fractionx = (j + 1 - (x[i] - wx / 2. - xmin + dx / 2.) / dx) / dwx;
197 }
198 else if (j == offsetx2) {
199 fractionx = ((x[i] + wx / 2. - xmin + dx / 2.) / dx - j) / dwx;
200 }
201 else {
202 fractionx = 1 / dwx;
203 }
204
205 for(k = offsety1; k <= offsety2; k++) {
206 if (offsety1 == offsety2) {
207 fractiony = 1.;
208 }
209 else if (k == offsety1) {
210 fractiony = (k + 1 - (y[i] - wy / 2. - ymin + dy / 2.) / dy) / dwy;
211 }
212 else if (k == offsety2) {
213 fractiony = ((y[i] + wy / 2. - ymin + dy / 2.) / dy - k) / dwy;
214 }
215 else {
216 fractiony = 1 / dwy;
217 }
218 for(l = offsetz1; l <= offsetz2; l++) {
219 if (offsetz1 == offsetz2) {
220 fractionz = 1.;
221 }
222 else if (l == offsetz1) {
223 fractionz = (l + 1 - (z[i] - wz / 2. - zmin + dz / 2.) / dz) / dwz;
224 }
225 else if (l == offsetz2) {
226 fractionz = ((z[i] + wz / 2. - zmin + dz / 2.) / dz - l) / dwz;
227 }
228 else {
229 fractionz = 1 / dwz;
230 }
231
232 offset = j * ny * nz + k * nz + l;
233 odata[offset] += data[i]*fractionx*fractiony*fractionz;
234 gnorm[offset] += fractionx*fractiony*fractionz;
235 }
236 }
237 }
238 }
239 }
240
241 /* perform normalization */
242 if (!(flags & NO_NORMALIZATION)) {
243 for (i = 0; i < ntot; i++) {
244 if (gnorm[i] > 1.e-16) {
245 odata[i] = odata[i] / gnorm[i];
246 }
247 }
248 }
249
250 /* free the norm buffer if it has been locally allocated */
251 if (norm == NULL) {
252 free(gnorm);
253 }
254
255 /* warn the user in case more than half the data points where out
256 * of the gridding area */
257 if (noutofbounds > n / 2) {
258 fprintf(stdout, "XU.FuzzyGridder3D(c): more than half of the "
259 "datapoints out of the data range, consider regridding with "
260 "extended range!\n");
261 }
262
263 return 0;
264 }
265
266
267 /*---------------------------------------------------------------------------*/
268 PyObject* pygridder3d(PyObject *self, PyObject *args)
269 {
270 PyArrayObject *py_x = NULL, *py_y = NULL, *py_z = NULL, *py_data = NULL,
271 *py_output = NULL, *py_norm = NULL;
272
273 double *x = NULL, *y = NULL, *z = NULL, *data = NULL, *odata = NULL,
274 *norm = NULL;
275 double xmin, xmax, ymin, ymax, zmin, zmax;
276 unsigned int nx, ny, nz;
277 int flags;
278 int n, result;
279
280 if (!PyArg_ParseTuple(args, "O!O!O!O!IIIddddddO!|O!i",
281 &PyArray_Type, &py_x,
282 &PyArray_Type, &py_y,
283 &PyArray_Type, &py_z,
284 &PyArray_Type, &py_data,
285 &nx, &ny, &nz,
286 &xmin, &xmax, &ymin, &ymax, &zmin, &zmax,
287 &PyArray_Type, &py_output,
288 &PyArray_Type, &py_norm,
289 &flags)) {
290 return NULL;
291 }
292
293 /* check input variables */
294 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
295 PYARRAY_CHECK(py_y, 1, NPY_DOUBLE, "y-axis must be a 1D double array!");
296 PYARRAY_CHECK(py_z, 1, NPY_DOUBLE, "z-axis must be a 1D double array!");
297 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
298 "input data must be a 1D double array!");
299 PYARRAY_CHECK(py_output, 3, NPY_DOUBLE,
300 "ouput data must be a 2D double array!");
301 if (py_norm != NULL) {
302 PYARRAY_CHECK(py_norm, 3, NPY_DOUBLE,
303 "norm data must be a 2D double array!");
304 }
305
306 /* get data */
307 x = (double *) PyArray_DATA(py_x);
308 y = (double *) PyArray_DATA(py_y);
309 z = (double *) PyArray_DATA(py_z);
310 data = (double *) PyArray_DATA(py_data);
311 odata = (double *) PyArray_DATA(py_output);
312 if (py_norm != NULL) {
313 norm = (double *) PyArray_DATA(py_norm);
314 }
315
316 /* get the total number of points */
317 n = (int) PyArray_SIZE(py_x);
318
319 /* call the actual gridder routine */
320 result = gridder3d(x, y, z, data, n, nx, ny, nz,
321 xmin, xmax, ymin, ymax, zmin, zmax, odata, norm, flags);
322
323 /* clean up */
324 Py_DECREF(py_x);
325 Py_DECREF(py_y);
326 Py_DECREF(py_z);
327 Py_DECREF(py_data);
328 Py_DECREF(py_output);
329 if (py_norm != NULL) {
330 Py_DECREF(py_norm);
331 }
332
333 return Py_BuildValue("i", &result);
334 }
335
336 /*---------------------------------------------------------------------------*/
337 int gridder3d(double *x, double *y, double *z, double *data, unsigned int n,
338 unsigned int nx, unsigned int ny, unsigned int nz,
339 double xmin, double xmax, double ymin, double ymax,
340 double zmin, double zmax,
341 double *odata, double *norm, int flags)
342 {
343 double *gnorm; /* pointer to normalization data */
344 unsigned int offset; /* linear offset for the grid data */
345 unsigned int ntot = nx * ny * nz; /* total number of points on the grid */
346 unsigned int i; /* loop index variable */
347 unsigned int noutofbounds = 0; /* number of points out of bounds */
348
349 /* compute step width for the grid */
350 double dx = delta(xmin, xmax, nx);
351 double dy = delta(ymin, ymax, ny);
352 double dz = delta(zmin, zmax, nz);
353
354 /* initialize data if requested */
355 if (!(flags & NO_DATA_INIT)) {
356 set_array(odata, ntot, 0.);
357 }
358
359 /* check if normalization array is passed */
360 if (norm == NULL) {
361 gnorm = malloc(sizeof(double) * ntot);
362 if (gnorm == NULL) {
363 fprintf(stderr, "XU.Gridder3D(c): Cannot allocate memory for "
364 "normalization buffer!\n");
365 return -1;
366 }
367 /* initialize memory for norm */
368 set_array(gnorm, ntot, 0.);
369 }
370 else {
371 gnorm = norm;
372 }
373
374 /* the master loop over all data points */
375 for (i = 0; i < n; i++) {
376 if (!isnan(data[i])) {
377 /* check if the current point is within the bounds of the grid */
378 if ((x[i] < xmin) || (x[i] > xmax)) {
379 noutofbounds++;
380 continue;
381 }
382 if ((y[i] < ymin) || (y[i] > ymax)) {
383 noutofbounds++;
384 continue;
385 }
386 if ((z[i] < zmin) || (z[i] > zmax)) {
387 noutofbounds++;
388 continue;
389 }
390
391 /* compute the offset value of the current input point on the
392 * grid array */
393 offset = gindex(x[i], xmin, dx) * ny * nz +
394 gindex(y[i], ymin, dy) * nz +
395 gindex(z[i], zmin, dz);
396
397 odata[offset] += data[i];
398 gnorm[offset] += 1.;
399 }
400 }
401
402 /* perform normalization */
403 if (!(flags & NO_NORMALIZATION)) {
404 for (i = 0; i < ntot; i++) {
405 if (gnorm[i] > 1.e-16) {
406 odata[i] = odata[i] / gnorm[i];
407 }
408 }
409 }
410
411 /* free the norm buffer if it has been locally allocated */
412 if (norm == NULL) {
413 free(gnorm);
414 }
415
416 /* warn the user in case more than half the data points where out
417 * of the gridding area */
418 if (noutofbounds > n / 2) {
419 fprintf(stdout, "XU.Gridder3D(c): more than half of the datapoints "
420 "out of the data range, consider regridding with extended "
421 "range!\n");
422 }
423
424 return 0;
425 }
426
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
17 * Copyright (C) 2013 Dominik Kriegner <dominik.kriegner@gmail.com>
18 *
19 ******************************************************************************
20 *
21 * created: Jun 8, 2013
22 * author: Eugen Wintersberger
23 */
24
25 #include "gridder_utils.h"
26
27 /*---------------------------------------------------------------------------*/
28 double get_min(double *a, unsigned int n)
29 {
30 double m = a[0];
31 unsigned int i;
32
33 for (i = 0; i < n; i++) {
34 if (m < a[i]) {
35 m = a[i];
36 }
37 }
38
39 return m;
40 }
41
42 /*---------------------------------------------------------------------------*/
43 double get_max(double *a, unsigned int n)
44 {
45 double m = a[0];
46 unsigned int i;
47
48 for (i = 0; i < n; i++) {
49 if (m > a[i]) {
50 m = a[i];
51 }
52 }
53
54 return m;
55 }
56
57 /*---------------------------------------------------------------------------*/
58 void set_array(double *a, unsigned int n, double value)
59 {
60 unsigned int i;
61
62 for (i = 0; i < n; ++i) {
63 a[i] = value;
64 }
65 }
66
67 /*---------------------------------------------------------------------------*/
68 double delta(double min, double max, unsigned int n)
69 {
70 return fabs(max - min) / (double) (n - 1);
71 }
72
73 /*---------------------------------------------------------------------------*/
74 unsigned int gindex(double x, double min, double d)
75 {
76 return (unsigned int) rint((x - min) / d);
77 }
78
79 /*---------------------------------------------------------------------------*/
80 #ifdef _WIN32
81 double rint(double x)
82 {
83 return x < 0.0 ? ceil(x - 0.5) : floor(x + 0.5);
84 }
85 #endif
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
17 * Copyright (C) 2013 Dominik Kriegner <dominik.kriegner@gmail.com>
18 *
19 ******************************************************************************
20 *
21 * created: Jun 8, 2013
22 * author: Eugen Wintersberger
23 */
24 #pragma once
25
26 #include <stdlib.h>
27 #include <stdio.h>
28
29 #include "xrayutilities.h"
30
31 #ifdef _WIN32
32 double rint(double x);
33 #endif
34
35 /*!
36 \brief find minimum
37
38 Finds the minimum in an array.
39 \param a input data
40 \param n number of elements
41 \return minimum value
42 */
43 double get_min(double *a, unsigned int n);
44
45 /*---------------------------------------------------------------------------*/
46 /*!
47 \brief find maximum
48
49 Finds the maximum value in an array.
50 \param a input data
51 \param n number of elements
52 \return return maximum value
53 */
54 double get_max(double *a, unsigned int n);
55
56 /*---------------------------------------------------------------------------*/
57 /*!
58 \brief set array values
59
60 Set all elements of an array to the same values.
61 \param a input array
62 \param n number of points
63 \param value the new element values
64 */
65 void set_array(double *a, unsigned int n, double value);
66
67 /*---------------------------------------------------------------------------*/
68 /*!
69 \brief compute step width
70
71 Computes the stepwidth of a grid.
72 \param min minimum value
73 \param max maximum value
74 \param n number of steps
75 \return step width
76 */
77 double delta(double min, double max, unsigned int n);
78
79 /*---------------------------------------------------------------------------*/
80 /*!
81 \brief compute grid index
82
83 */
84 unsigned int gindex(double x, double min, double d);
85
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2010-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
17 */
18
19 /* ######################################
20 * conversion of angular coordinates
21 * to reciprocal space
22 * using general algorithms to work
23 * for different types of geometries
24 * and detectors
25 * ######################################*/
26
27
28 #include "qconversion.h"
29 #include <ctype.h>
30 #include <math.h>
31
32
33 /* ###################################
34 * matrix vector operations for
35 * 3x3 matrices and vectors of length
36 * 3
37 * ################################### */
38
39 INLINE void ident(double *m) {
40 m[0] = 1.; m[1] = 0.; m[2] = 0.;
41 m[3] = 0.; m[4] = 1.; m[5] = 0.;
42 m[6] = 0.; m[7] = 0.; m[8] = 1.;
43 }
44
45 INLINE void sumvec(double *RESTRICT v1, double *RESTRICT v2) {
46 unsigned int i;
47 for (i = 0; i < 3; ++i) {
48 v1[i] += v2[i];
49 }
50 }
51
52 INLINE void diffvec(double *RESTRICT v1, double *RESTRICT v2) {
53 unsigned int i;
54 for (i = 0; i < 3; ++i) {
55 v1[i] -= v2[i];
56 }
57 }
58
59 INLINE double norm(double *v) {
60 double n = 0;
61 unsigned int i;
62 for (i = 0; i < 3; ++i) {
63 n += v[i] * v[i];
64 }
65 return sqrt(n);
66 }
67
68 INLINE void normalize(double *v) {
69 double n = norm(v);
70 unsigned int i;
71 for (i = 0; i < 3; ++i) {
72 v[i] /= n;
73 }
74 }
75
76 INLINE void veccopy(double *RESTRICT v1, double *RESTRICT v2) {
77 unsigned int i;
78 for (i = 0; i < 3; ++i) {
79 v1[i] = v2[i];
80 }
81 }
82
83 INLINE void vecmul(double *RESTRICT r, double a) {
84 unsigned int i;
85 for (i = 0; i < 3; ++i) {
86 r[i] *= a;
87 }
88 }
89
90 INLINE void cross(double *RESTRICT v1, double *RESTRICT v2,
91 double *RESTRICT r) {
92 r[0] = v1[1] * v2[2] - v1[2] * v2[1];
93 r[1] = -v1[0] * v2[2] + v1[2] * v2[0];
94 r[2] = v1[0] * v2[1] - v1[1] * v2[0];
95 }
96
97 INLINE void vecmatcross(double *RESTRICT v, double *RESTRICT m,
98 double *RESTRICT mr) {
99 unsigned int i;
100 for (i = 0; i < 9; i = i + 3) {
101 mr[0 + i] = v[1] * m[2 + i] - v[2] * m[1 + i];
102 mr[1 + i] = -v[0] * m[2 + i] + v[2] * m[0 + i];
103 mr[2 + i] = v[0] * m[1 + i] - v[1] * m[0 + i];
104 }
105 }
106
107 INLINE void matmulc(double *RESTRICT m, double c) {
108 unsigned int i;
109 for (i = 0; i < 9; i = i + 1) {
110 m[i] *= c;
111 }
112 }
113
114 INLINE void matvec(double *RESTRICT m, double *RESTRICT v,
115 double *RESTRICT r) {
116 r[0] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2];
117 r[1] = m[3] * v[0] + m[4] * v[1] + m[5] * v[2];
118 r[2] = m[6] * v[0] + m[7] * v[1] + m[8] * v[2];
119 }
120
121 INLINE void matmul(double *RESTRICT m1, double *RESTRICT m2) {
122 double a, b, c;
123 unsigned int i;
124 for (i = 0; i < 9; i = i + 3) {
125 a = m1[i] * m2[0] + m1[i + 1] * m2[3] + m1[i + 2] * m2[6];
126 b = m1[i] * m2[1] + m1[i + 1] * m2[4] + m1[i + 2] * m2[7];
127 c = m1[i] * m2[2] + m1[i + 1] * m2[5] + m1[i + 2] * m2[8];
128 m1[i] = a;
129 m1[i + 1] = b;
130 m1[i + 2] = c;
131 }
132 }
133
134 INLINE void tensorprod(double *RESTRICT v1, double *RESTRICT v2,
135 double *RESTRICT m) {
136 unsigned int i, j;
137 for (i = 0; i < 3; i = i + 1) {
138 for (j = 0; j < 3; j = j + 1) {
139 m[i * 3 + j] = v1[i] * v2[j];
140 }
141 }
142 }
143
144 INLINE void summat(double *RESTRICT m1, double *RESTRICT m2) {
145 unsigned int i;
146 for (i = 0; i < 9; ++i) {
147 m1[i] += m2[i];
148 }
149 }
150
151 INLINE void diffmat(double *RESTRICT m1, double *RESTRICT m2) {
152 unsigned int i;
153 for (i = 0; i < 9; ++i) {
154 m1[i] -= m2[i];
155 }
156 }
157
158 INLINE void inversemat(double *RESTRICT m, double *RESTRICT i) {
159 double det;
160 double h1, h2, h3, h4, h5, h6;
161 unsigned int j;
162
163 h1 = m[4] * m[8]; /* m11*m22 */
164 h2 = m[5] * m[6]; /* m12*m20 */
165 h3 = m[3] * m[7]; /* m10*m21 */
166 h4 = m[4] * m[6]; /* m11*m20 */
167 h5 = m[3] * m[8]; /* m10*m22 */
168 h6 = m[5] * m[7]; /* m12*m21 */
169 det = m[0] * h1 + m[1] * h2 + m[2] * h3 - \
170 m[2] * h4 - m[1] * h5 - m[0] * h6;
171
172 i[0] = (h1 - h6);
173 i[1] = (m[2] * m[7] - m[1] * m[8]);
174 i[2] = (m[1] * m[5] - m[2] * m[4]);
175 i[3] = (h2 - h5);
176 i[4] = (m[0] * m[8] - m[2] * m[6]);
177 i[5] = (m[2] * m[3] - m[0] * m[5]);
178 i[6] = (h3 - h4);
179 i[7] = (m[1] * m[6] - m[0] * m[7]);
180 i[8] = (m[0] * m[4] - m[1] * m[3]);
181
182 for (j = 0; j < 9; ++j) {
183 i[j] /= det;
184 }
185 }
186
187 INLINE double determinant(double *RESTRICT m) {
188 double h1, h2, h3, h4, h5, h6;
189 double det = 0;
190
191 h1 = m[4] * m[8]; /* m11*m22 */
192 h2 = m[5] * m[6]; /* m12*m20 */
193 h3 = m[3] * m[7]; /* m10*m21 */
194 h4 = m[4] * m[6]; /* m11*m20 */
195 h5 = m[3] * m[8]; /* m10*m22 */
196 h6 = m[5] * m[7]; /* m12*m21 */
197
198 det = m[0] * h1 + m[1] * h2 + m[2] * h3 - \
199 m[2] * h4 - m[1] * h5 - m[0] * h6;
200 return det;
201 }
202
203
204 /*##############################################
205 # functions which implement rotation matrices
206 # for all coordinate axes and rotation senses
207 #
208 # the routines expect angles in radians
209 # for conversion from degrees to radians
210 # the functions and2rad and rad2ang are
211 # supplied
212 ################################################*/
213
214 INLINE void rotation_xp(double a, double *mat){
215 double sa = sin(a), ca = cos(a);
216 mat[0] = 1.; mat[1] = 0.; mat[2] = 0.;
217 mat[3] = 0.; mat[4] = ca; mat[5] = -sa;
218 mat[6] = 0.; mat[7] = sa; mat[8] = ca;
219 }
220
221 INLINE void apply_xp(double a, double *vec){
222 double mat[9], vtemp[3];
223 rotation_xp(a, mat);
224 veccopy(vtemp, vec);
225 matvec(mat, vtemp, vec);
226 }
227
228 INLINE void rotation_xm(double a, double *mat){
229 double sa = sin(a), ca = cos(a);
230 mat[0] = 1.; mat[1] = 0.; mat[2] = 0.;
231 mat[3] = 0.; mat[4] = ca; mat[5] = sa;
232 mat[6] = 0.; mat[7] = -sa; mat[8] = ca;
233 }
234
235 INLINE void apply_xm(double a, double *vec){
236 double mat[9], vtemp[3];
237 rotation_xm(a, mat);
238 veccopy(vtemp, vec);
239 matvec(mat, vtemp, vec);
240 }
241
242 INLINE void rotation_yp(double a, double *mat){
243 double sa = sin(a), ca = cos(a);
244 mat[0] = ca; mat[1] = 0.; mat[2] = sa;
245 mat[3] = 0.; mat[4] = 1.; mat[5] = 0.;
246 mat[6] = -sa; mat[7] = 0.; mat[8] = ca;
247 }
248
249 INLINE void apply_yp(double a, double *vec){
250 double mat[9], vtemp[3];
251 rotation_yp(a, mat);
252 veccopy(vtemp, vec);
253 matvec(mat, vtemp, vec);
254 }
255
256 INLINE void rotation_ym(double a, double *mat){
257 double sa = sin(a), ca = cos(a);
258 mat[0] = ca; mat[1] = 0.; mat[2] = -sa;
259 mat[3] = 0.; mat[4] = 1.; mat[5] = 0.;
260 mat[6] = sa; mat[7] = 0.; mat[8] = ca;
261 }
262
263 INLINE void apply_ym(double a, double *vec){
264 double mat[9], vtemp[3];
265 rotation_ym(a, mat);
266 veccopy(vtemp, vec);
267 matvec(mat, vtemp, vec);
268 }
269
270 INLINE void rotation_zp(double a, double *mat){
271 double sa = sin(a), ca = cos(a);
272 mat[0] = ca; mat[1] = -sa; mat[2] = 0.;
273 mat[3] = sa; mat[4] = ca; mat[5] = 0.;
274 mat[6] = 0.; mat[7] = 0.; mat[8] = 1.;
275 }
276
277 INLINE void apply_zp(double a, double *vec){
278 double mat[9], vtemp[3];
279 rotation_zp(a, mat);
280 veccopy(vtemp, vec);
281 matvec(mat, vtemp, vec);
282 }
283
284 INLINE void rotation_zm(double a, double *mat){
285 double sa = sin(a), ca = cos(a);
286 mat[0] = ca; mat[1] = sa; mat[2] = 0.;
287 mat[3] = -sa; mat[4] = ca; mat[5] = 0.;
288 mat[6] = 0.; mat[7] = 0.; mat[8] = 1.;
289 }
290
291 INLINE void apply_zm(double a, double *vec){
292 double mat[9], vtemp[3];
293 rotation_zm(a, mat);
294 veccopy(vtemp, vec);
295 matvec(mat, vtemp, vec);
296 }
297
298 INLINE void rotation_kappa(double a, double *mat){
299 double e[3];
300 e[0] = mat[0]; e[1] = mat[1]; e[2] = mat[2];
301 rotation_arb(a, e, mat);
302 }
303
304 INLINE void rotation_arb(double a, double *RESTRICT e, double *RESTRICT mat) {
305 double sa = sin(a), ca = cos(a);
306 double mtemp[9], mtemp2[9];
307
308 /* e must be normalized */
309
310 /* ca*(ident(3) - vec(e) o vec(e))*/
311 ident(mat);
312 tensorprod(e, e, mtemp);
313 diffmat(mat, mtemp);
314 matmulc(mat, ca);
315
316 /* tensorprod(vec(e), vec(e)) */
317 summat(mat, mtemp);
318
319 /* sa*(vec(e) cross ident(3)) */
320 ident(mtemp2);
321 vecmatcross(e, mtemp2, mtemp);
322 matmulc(mtemp, sa);
323 summat(mat, mtemp);
324 }
325
326 INLINE void apply_tx(double x, double *vec){
327 vec[0] += x;
328 }
329
330 INLINE void apply_ty(double y, double *vec){
331 vec[1] += y;
332 }
333
334 INLINE void apply_tz(double z, double *vec){
335 vec[2] += z;
336 }
337
338 /* #######################################
339 * debug helper functions
340 * #######################################*/
341 int print_matrix(double *m) {
342 unsigned int i;
343 for (i = 0; i < 9; i += 3) {
344 printf("%8.5g %8.5g %8.5g\n", m[i], m[i + 1], m[i + 2]);
345 }
346 printf("\n");
347 return 0;
348 }
349
350 int print_vector(double *m) {
351 printf("\n%8.5g %8.5g %8.5g\n", m[0], m[1], m[2]);
352 return 0;
353 }
354
355 /* #######################################
356 * conversion helper functions
357 * #######################################*/
358
359 int determine_detector_pixel(double *rpixel, char *dir, double dpixel,
360 double *r_i, double tilt) {
361 /* determine the direction of a linear detector or one of the directions of
362 * an area detector. the function returns the vector containing the
363 * distance from one to the next pixel a tilt of the detector axis with
364 * respect to the coordinate axis can be considered as well! rotation of
365 * pixel direction around the crossproduct of primary beam and detector
366 * axis. this is mainly usefull for linear detectors, since the tilt of
367 * area detectors is handled different. */
368
369 double tiltaxis[3], tiltmat[9];
370 unsigned int i;
371
372 for (i = 0; i < 3; ++i) {
373 rpixel[i] = 0.;
374 }
375
376 switch (tolower(dir[0])) {
377 case 'x':
378 switch (dir[1]) {
379 case '+':
380 rpixel[0] = dpixel;
381 break;
382 case '-':
383 rpixel[0] = -dpixel;
384 break;
385 default:
386 PyErr_SetString(PyExc_ValueError,
387 "XU.Qconversion(c): detector determination: no valid "
388 "direction sign given");
389 return 1;
390 }
391 break;
392 case 'y':
393 switch (dir[1]) {
394 case '+':
395 rpixel[1] = dpixel;
396 break;
397 case '-':
398 rpixel[1] = -dpixel;
399 break;
400 default:
401 PyErr_SetString(PyExc_ValueError,
402 "XU.Qconversion(c): detector determination: no valid "
403 "direction sign given");
404 return 1;
405 }
406 break;
407 case 'z':
408 switch (dir[1]) {
409 case '+':
410 rpixel[2] = dpixel;
411 break;
412 case '-':
413 rpixel[2] = -dpixel;
414 break;
415 default:
416 PyErr_SetString(PyExc_ValueError,
417 "XU.Qconversion(c): detector determination: no valid "
418 "direction sign given");
419 return 1;
420 }
421 break;
422 default:
423 PyErr_SetString(PyExc_ValueError,
424 "XU.Qconversion(c): detector determination: no valid "
425 "direction direction given");
426 return 2;
427 }
428
429 /* include possible tilt of detector axis with respect to its direction */
430 cross(r_i, rpixel, tiltaxis);
431 normalize(tiltaxis);
432 /* check if there is a problem with the tiltaxis */
433 for (i = 0; i < 3; ++i) {
434 if (isnan(tiltaxis[i])) {
435 memset(tiltaxis, 0, sizeof(tiltaxis));
436 }
437 }
438 /* create needed rotation matrix */
439 rotation_arb(tilt, tiltaxis, tiltmat);
440 /* rotate rpixel */
441 matvec(tiltmat, rpixel, tiltaxis);
442 veccopy(rpixel, tiltaxis);
443 return 0;
444 }
445
446 int determine_axes_directions(fp_rot *fp_circles, char *stringAxis,
447 unsigned int n) {
448 /* feed the function pointer array with the correct
449 * rotation matrix generating functions
450 * */
451 unsigned int i;
452
453 for (i = 0; i < n; ++i) {
454 switch (tolower(stringAxis[2 * i])) {
455 case 'x':
456 switch (stringAxis[2 * i + 1]) {
457 case '+':
458 fp_circles[i] = &rotation_xp;
459 break;
460 case '-':
461 fp_circles[i] = &rotation_xm;
462 break;
463 default:
464 PyErr_SetString(PyExc_ValueError,
465 "XU.Qconversion(c): axis determination: no valid "
466 "rotation sense given");
467 return 1;
468 }
469 break;
470 case 'y':
471 switch (stringAxis[2 * i + 1]) {
472 case '+':
473 fp_circles[i] = &rotation_yp;
474 break;
475 case '-':
476 fp_circles[i] = &rotation_ym;
477 break;
478 default:
479 PyErr_SetString(PyExc_ValueError,
480 "XU.Qconversion(c): axis determination: no valid "
481 "rotation sense given");
482 return 1;
483 }
484 break;
485 case 'z':
486 switch(stringAxis[2 * i + 1]) {
487 case '+':
488 fp_circles[i] = &rotation_zp;
489 break;
490 case '-':
491 fp_circles[i] = &rotation_zm;
492 break;
493 default:
494 PyErr_SetString(PyExc_ValueError,
495 "XU.Qconversion(c): axis determination: no valid "
496 "rotation sense given");
497 return 1;
498 }
499 break;
500 case 'k':
501 fp_circles[i] = &rotation_kappa;
502 break;
503 default:
504 PyErr_SetString(PyExc_ValueError,
505 "XU.Qconversion(c): axis determination: no valid axis "
506 "direction given");
507 return 2;
508 }
509 }
510
511 return 0;
512 }
513
514 int determine_axes_directions_apply(fp_rot *fp_circles, char *stringAxis,
515 unsigned int n) {
516 /* feed the function pointer array with the correct
517 * rotation/translation applying functions
518 * */
519 unsigned int i;
520
521 for (i = 0; i < n; ++i) {
522 switch (tolower(stringAxis[2 * i])) {
523 case 'x':
524 switch (stringAxis[2 * i + 1]) {
525 case '+':
526 fp_circles[i] = &apply_xp;
527 break;
528 case '-':
529 fp_circles[i] = &apply_xm;
530 break;
531 default:
532 PyErr_SetString(PyExc_ValueError,
533 "XU.Qconversion(c): axis determination: no valid "
534 "rotation sense given");
535 return 1;
536 }
537 break;
538 case 'y':
539 switch (stringAxis[2 * i + 1]) {
540 case '+':
541 fp_circles[i] = &apply_yp;
542 break;
543 case '-':
544 fp_circles[i] = &apply_ym;
545 break;
546 default:
547 PyErr_SetString(PyExc_ValueError,
548 "XU.Qconversion(c): axis determination: no valid "
549 "rotation sense given");
550 return 1;
551 }
552 break;
553 case 'z':
554 switch(stringAxis[2 * i + 1]) {
555 case '+':
556 fp_circles[i] = &apply_zp;
557 break;
558 case '-':
559 fp_circles[i] = &apply_zm;
560 break;
561 default:
562 PyErr_SetString(PyExc_ValueError,
563 "XU.Qconversion(c): axis determination: no valid "
564 "rotation sense given");
565 return 1;
566 }
567 break;
568 case 't':
569 switch(stringAxis[2 * i + 1]) {
570 case 'x':
571 fp_circles[i] = &apply_tx;
572 break;
573 case 'y':
574 fp_circles[i] = &apply_ty;
575 break;
576 case 'z':
577 fp_circles[i] = &apply_tz;
578 break;
579 default:
580 PyErr_SetString(PyExc_ValueError,
581 "XU.Qconversion(c): axis determination: no valid "
582 "translation given");
583 return 1;
584 }
585 break;
586 default:
587 PyErr_SetString(PyExc_ValueError,
588 "XU.Qconversion(c): axis determination: no valid axis "
589 "direction given");
590 return 2;
591 }
592 }
593
594 return 0;
595 }
596
597 int tilt_detector_axis(double tiltazimuth, double tilt,
598 double *RESTRICT rpixel1, double *RESTRICT rpixel2) {
599 /* rotate detector pixel vectors of a 2D detector according to tilt and
600 * tiltazimuth */
601 double rtemp[3], rtemp2[3]; /* buffer vectors */
602 double mtemp[9]; /* rotation matrix buffer */
603
604 veccopy(rtemp, rpixel1);
605 normalize(rtemp);
606 vecmul(rtemp, cos(tiltazimuth + M_PI / 2.));
607
608 veccopy(rtemp2, rpixel2);
609 normalize(rtemp2);
610 vecmul(rtemp2, sin(tiltazimuth + M_PI / 2.));
611
612 sumvec(rtemp, rtemp2); /* tiltaxis (rotation axis) now stored in rtemp */
613 rotation_arb(tilt, rtemp, mtemp); /* rotation matrix now in mtemp */
614
615 /* rotate detector pixel directions */
616 veccopy(rtemp, rpixel1);
617 matvec(mtemp, rtemp, rpixel1);
618 veccopy(rtemp, rpixel2);
619 matvec(mtemp, rtemp, rpixel2);
620
621 return 0;
622 }
623
624 /***********************************************
625 * QConversion functions for point detector *
626 ***********************************************/
627
628 PyObject* py_ang2q_conversion(PyObject *self, PyObject *args)
629 /* conversion of Npoints of goniometer positions to reciprocal space
630 * for a setup with point detector. This is the python wrapper function
631 * which should be called by the user. It offers one common interface to
632 * the outside although internally several performance optimized variants
633 * are called.
634 *
635 * Parameters
636 * ----------
637 * sampleAngles .. angular positions of the sample goniometer
638 * (Npoints, Ns)
639 * detectorAngles. angular positions of the detector goniometer
640 * (Npoints, Nd)
641 * ri ............ direction of primary beam (length of detector distance)
642 * (angles zero)
643 * sampleAxis .... string with sample axis directions
644 * detectorAxis .. string with detector axis directions
645 * kappadir ...... rotation axis of a possible kappa circle
646 * UB ............ orientation matrix and reciprocal space
647 * conversion of the investigated crystal (3, 3)
648 * sampledis ..... sample displacement vector in relative units of
649 * the detector distance
650 * lambda ........ wavelength of the used x-rays as array (Npoints,)
651 * in units of Angstreom
652 * nthreads ...... number of threads to use in parallel section of
653 * the code
654 * flags ......... integer with flags: (1: has_translations;
655 * 4: has_sampledis;
656 * 16: verbose)
657 *
658 * Returns
659 * -------
660 * qpos .......... momentum transfer (Npoints, 3)
661 *
662 * */
663 {
664 int Ns, Nd; /* number of sample and detector circles */
665 int Npoints; /* number of angular positions */
666 int r; /* for return value checking */
667 unsigned int nthreads; /* number of threads to use */
668 char *sampleAxis, *detectorAxis; /* str with sample and detector axis */
669 double *sampleAngles,*detectorAngles, *ri, *kappadir, *sampledis,
670 *UB, *qpos, *lambda; /* c-arrays for further usage */
671 int flags;
672 npy_intp nout[2];
673
674 /* numpy arrays */
675 PyArrayObject *sampleAnglesArr = NULL, *detectorAnglesArr = NULL,
676 *riArr = NULL, *kappadirArr = NULL, *sampledisArr = NULL,
677 *UBArr = NULL, *qposArr = NULL, *lambdaArr = NULL;
678
679 /* Python argument conversion code */
680 if (!PyArg_ParseTuple(args, "O!O!O!ssO!O!O!O!Ii",
681 &PyArray_Type, &sampleAnglesArr,
682 &PyArray_Type, &detectorAnglesArr,
683 &PyArray_Type, &riArr,
684 &sampleAxis, &detectorAxis,
685 &PyArray_Type, &kappadirArr,
686 &PyArray_Type, &UBArr,
687 &PyArray_Type, &sampledisArr,
688 &PyArray_Type, &lambdaArr, &nthreads, &flags)) {
689 return NULL;
690 }
691
692 /* check Python array dimensions and types */
693 PYARRAY_CHECK(sampleAnglesArr, 2, NPY_DOUBLE,
694 "sampleAngles must be a 2D double array");
695 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
696 "detectorAngles must be a 2D double array");
697 PYARRAY_CHECK(lambdaArr, 1, NPY_DOUBLE,
698 "wavelength must be a 1D double array");
699 PYARRAY_CHECK(riArr, 1, NPY_DOUBLE,
700 "r_i must be a 1D double array");
701 if (PyArray_SIZE(riArr) != 3) {
702 PyErr_SetString(PyExc_ValueError, "r_i needs to be of length 3");
703 return NULL;
704 }
705 PYARRAY_CHECK(sampledisArr, 1, NPY_DOUBLE,
706 "sampledis must be a 1D double array");
707 if (PyArray_SIZE(sampledisArr) != 3) {
708 PyErr_SetString(PyExc_ValueError,"sampledis needs to be of length 3");
709 return NULL;
710 }
711 PYARRAY_CHECK(kappadirArr, 1, NPY_DOUBLE,
712 "kappa_dir must be a 1D double array");
713 if (PyArray_SIZE(kappadirArr) != 3) {
714 PyErr_SetString(PyExc_ValueError, "kappa_dir needs to be of length 3");
715 return NULL;
716 }
717 PYARRAY_CHECK(UBArr, 2, NPY_DOUBLE, "UB must be a 2D double array");
718 if (PyArray_DIMS(UBArr)[0] != 3 || PyArray_DIMS(UBArr)[1] != 3) {
719 PyErr_SetString(PyExc_ValueError, "UB must be of shape (3, 3)");
720 return NULL;
721 }
722
723 Npoints = (int) PyArray_DIMS(sampleAnglesArr)[0];
724 Ns = (int) PyArray_DIMS(sampleAnglesArr)[1];
725 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
726 if (PyArray_DIMS(detectorAnglesArr)[0] != Npoints) {
727 PyErr_SetString(PyExc_ValueError,
728 "detectorAngles and sampleAngles must have same first dimension");
729 return NULL;
730 }
731 if (PyArray_SIZE(lambdaArr) != Npoints) {
732 PyErr_SetString(PyExc_ValueError,
733 "size of wavelength array need to fit with angle arrays");
734 return NULL;
735 }
736
737 sampleAngles = (double *) PyArray_DATA(sampleAnglesArr);
738 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
739 lambda = (double *) PyArray_DATA(lambdaArr);
740 ri = (double *) PyArray_DATA(riArr);
741 sampledis = (double *) PyArray_DATA(sampledisArr);
742 kappadir = (double *) PyArray_DATA(kappadirArr);
743 UB = (double *) PyArray_DATA(UBArr);
744
745 /* create output ndarray */
746 nout[0] = Npoints;
747 nout[1] = 3;
748 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
749 qpos = (double *) PyArray_DATA(qposArr);
750
751 #ifdef __OPENMP__
752 /* set openmp thread numbers dynamically */
753 OMPSETNUMTHREADS(nthreads);
754 #endif
755
756 /* call worker function */
757 if (flags & HAS_SAMPLEDIS) {
758 if (flags & HAS_TRANSLATIONS) {
759 r = ang2q_conversion_sdtrans(
760 sampleAngles, detectorAngles, ri,
761 sampleAxis, detectorAxis, kappadir, UB,
762 sampledis, lambda, Npoints, Ns, Nd, flags, qpos);
763 }
764 else {
765 r = ang2q_conversion_sd(
766 sampleAngles, detectorAngles, ri,
767 sampleAxis, detectorAxis, kappadir, UB,
768 sampledis, lambda, Npoints, Ns, Nd, flags, qpos);
769 }
770 }
771 else {
772 if (flags & HAS_TRANSLATIONS) {
773 r = ang2q_conversion_trans(
774 sampleAngles, detectorAngles, ri,
775 sampleAxis, detectorAxis, kappadir, UB,
776 lambda, Npoints, Ns, Nd, flags, qpos);
777 }
778 else {
779 r = ang2q_conversion(
780 sampleAngles, detectorAngles, ri,
781 sampleAxis, detectorAxis, kappadir, UB, lambda,
782 Npoints, Ns, Nd, flags, qpos);
783 }
784 }
785
786 /* clean up */
787 Py_DECREF(sampleAnglesArr);
788 Py_DECREF(detectorAnglesArr);
789 Py_DECREF(riArr);
790 Py_DECREF(kappadirArr);
791 Py_DECREF(UBArr);
792 Py_DECREF(sampledisArr);
793 Py_DECREF(lambdaArr);
794 if (r != 0) {
795 return NULL;
796 }
797
798 /* return output array */
799 return PyArray_Return(qposArr);
800 }
801
802
803 int ang2q_conversion(double *sampleAngles, double *detectorAngles,
804 double *ri, char *sampleAxis, char *detectorAxis,
805 double *kappadir, double *UB, double *lambda,
806 int Npoints, int Ns, int Nd, int flags,
807 double *qpos)
808 /* conversion of Npoints of goniometer positions to reciprocal space
809 * for a setup with point detector
810 *
811 * Parameters
812 * ----------
813 * sampleAngles .. angular positions of the sample goniometer
814 * (Npoints, Ns)
815 * detectorAngles. angular positions of the detector goniometer
816 * (Npoints, Nd)
817 * ri ............ direction of primary beam (length irrelevant)
818 * (angles zero)
819 * sampleAxis .... string with sample axis directions
820 * detectorAxis .. string with detector axis directions
821 * kappadir ...... rotation axis of a possible kappa circle
822 * UB ............ orientation matrix and reciprocal space conversion of
823 * investigated crystal (3, 3)
824 * lambda ........ wavelength of the used x-rays as array (Npoints,)
825 * in units of Angstreom
826 * Npoints ....... number of points to calculate
827 * Ns ............ number of sample axes
828 * Nd ............ number of detector axes
829 * flags ......... general flags integer (verbosity)
830 * qpos .......... momentum transfer (Npoints, 3) (OUTPUT array)
831 *
832 * */
833 {
834 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
835 double local_ri[3], ki[3]; /* copy of primary beam direction */
836 int i, j; /* needed indices */
837 /* arrays with function pointers to rotation matrix functions */
838 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
839 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
840
841 /* determine axes directions */
842 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
843 return -1;
844 }
845 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
846 return -1;
847 }
848
849 /* give ri correct length */
850 veccopy(local_ri, ri);
851 normalize(local_ri);
852
853 /* calculate rotation matices and perform rotations */
854 #pragma omp parallel for default(shared) \
855 private(i, j, ki, mtemp, mtemp2, ms, md) \
856 schedule(static)
857 for (i = 0; i < Npoints; ++i) {
858 /* determine sample rotations */
859 ident(mtemp);
860 for (j = 0; j < Ns; ++j) {
861 /* load kappa direction into matrix
862 * (just needed for kappa goniometer) */
863 mtemp2[0] = kappadir[0];
864 mtemp2[1] = kappadir[1];
865 mtemp2[2] = kappadir[2];
866 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
867 matmul(mtemp, mtemp2);
868 }
869 /* apply rotation of orientation matrix */
870 matmul(mtemp, UB);
871 /* determine inverse matrix */
872 inversemat(mtemp, ms);
873
874 /* determine detector rotations */
875 ident(md);
876 for (j = 0; j < Nd; ++j) {
877 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
878 matmul(md, mtemp);
879 }
880 ident(mtemp);
881 diffmat(md, mtemp);
882
883 matmul(ms, md);
884 /* ms contains now the rotation matrix to determine
885 * the momentum transfer.
886 * calculate the momentum transfer */
887 veccopy(ki, local_ri); /* ki is now normalized ri */
888 vecmul(ki, M_2PI / lambda[i]); /* scales k_i */
889 matvec(ms, ki, &qpos[3 * i]);
890 }
891 return 0;
892 }
893
894
895 int ang2q_conversion_sd(
896 double *sampleAngles, double *detectorAngles, double *ri,
897 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
898 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
899 int flags, double *qpos)
900 /* conversion of Npoints of goniometer positions to reciprocal space
901 * for a setup with point detector including the effect of a sample
902 * displacement error.
903 *
904 * Parameters
905 * ----------
906 * sampleAngles .. angular positions of the sample goniometer
907 * (Npoints, Ns)
908 * detectorAngles. angular positions of the detector goniometer
909 * (Npoints, Nd)
910 * ri ............ direction of primary beam (length of detector distance)
911 * (angles zero)
912 * sampleAxis .... string with sample axis directions
913 * detectorAxis .. string with detector axis directions
914 * kappadir ...... rotation axis of a possible kappa circle
915 * UB ............ orientation matrix and reciprocal space
916 * conversion of the investigated crystal (3, 3)
917 * sampledis ..... sample displacement vector in relative units of
918 * the detector distance
919 * lambda ........ wavelength of the used x-rays as array (Npoints,)
920 * in units of Angstreom
921 * Npoints ....... number of points to calculate
922 * Ns ............ number of sample axes
923 * Nd ............ number of detector axes
924 * flags ......... general flags integer (verbosity)
925 * qpos .......... momentum transfer (Npoints, 3) (OUTPUT array)
926 *
927 * */
928 {
929 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
930 double local_ri[3]; /* copy of primary beam direction */
931 int i, j; /* needed indices */
932 /* arrays with function pointers to rotation matrix functions */
933 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
934 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
935
936 /* determine axes directions */
937 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
938 return -1;
939 }
940 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
941 return -1;
942 }
943
944 /* give ri correct length */
945 veccopy(local_ri, ri);
946 normalize(local_ri);
947
948 /* calculate rotation matices and perform rotations */
949 #pragma omp parallel for default(shared) \
950 private(i, j, mtemp, mtemp2, ms, md) \
951 schedule(static)
952 for (i = 0; i < Npoints; ++i) {
953 /* determine sample rotations */
954 ident(mtemp);
955 for (j = 0; j < Ns; ++j) {
956 /* load kappa direction into matrix
957 * (just needed for kappa goniometer) */
958 mtemp2[0] = kappadir[0];
959 mtemp2[1] = kappadir[1];
960 mtemp2[2] = kappadir[2];
961 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
962 matmul(mtemp, mtemp2);
963 }
964 /* apply rotation of orientation matrix */
965 matmul(mtemp, UB);
966 /* determine inverse matrix */
967 inversemat(mtemp, ms);
968
969 /* determine detector rotations */
970 ident(md);
971 for (j = 0; j < Nd; ++j) {
972 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
973 matmul(md, mtemp);
974 }
975
976 /* consider sample displacement in kf
977 * kf = |k| * (\mat D . \hat ri - \vec rs)/||...|| */
978 matvec(md, ri, mtemp);
979 diffvec(mtemp, sampledis);
980 normalize(mtemp);
981 diffvec(mtemp, local_ri); /* ki/|k| - kf/|k| */
982 vecmul(mtemp, M_2PI / lambda[i]); /* defines k_f */
983 /* mtemp now contains the momentum transfer which will be
984 * transformed to the sample q-coordinate system.
985 * calculate the momentum transfer */
986 matvec(ms, mtemp, &qpos[3 * i]);
987 }
988 return 0;
989 }
990
991
992 int ang2q_conversion_trans(
993 double *sampleAngles, double *detectorAngles, double *ri,
994 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
995 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos)
996 /* conversion of Npoints of goniometer positions to reciprocal space
997 * for a setup with point detector and detector translations
998 *
999 * Parameters
1000 * ----------
1001 * sampleAngles .. angular positions of the sample goniometer
1002 * (Npoints, Ns)
1003 * detectorAngles. angular positions of the detector goniometer
1004 * (Npoints, Nd)
1005 * ri ............ direction of primary beam (length irrelevant)
1006 * (angles zero)
1007 * sampleAxis .... string with sample axis directions
1008 * detectorAxis .. string with detector axis directions
1009 * kappadir ...... rotation axis of a possible kappa circle
1010 * UB ............ orientation matrix and reciprocal space conversion of
1011 * investigated crystal (3, 3)
1012 * lambda ........ wavelength of the used x-rays as array (Npoints,)
1013 * in units of Angstreom
1014 * Npoints ....... number of points to calculate
1015 * Ns ............ number of sample axes
1016 * Nd ............ number of detector axes
1017 * flags ......... general flags integer (verbosity)
1018 * qpos .......... momentum transfer (Npoints, 3) (OUTPUT array)
1019 *
1020 * */
1021 {
1022 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
1023 double local_ri[3], rd[3]; /* copy of primary beam direction */
1024 int i, j; /* needed indices */
1025 /* arrays with function pointers to rotation matrix functions */
1026 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1027 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1028
1029 /* determine axes directions */
1030 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1031 return -1;
1032 }
1033 if (determine_axes_directions_apply(detectorRotation,
1034 detectorAxis, Nd) != 0) {
1035 return -1;
1036 }
1037
1038 /* give ri correct length */
1039 veccopy(local_ri, ri);
1040 normalize(local_ri);
1041
1042 /* calculate rotation matices and perform rotations */
1043 #pragma omp parallel for default(shared) \
1044 private(i, j, mtemp, mtemp2, ms, rd) \
1045 schedule(static)
1046 for (i = 0; i < Npoints; ++i) {
1047 /* determine sample rotations */
1048 ident(mtemp);
1049 for (j = 0; j < Ns; ++j) {
1050 /* load kappa direction into matrix
1051 * (just needed for kappa goniometer) */
1052 mtemp2[0] = kappadir[0];
1053 mtemp2[1] = kappadir[1];
1054 mtemp2[2] = kappadir[2];
1055 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1056 matmul(mtemp, mtemp2);
1057 }
1058 /* apply rotation of orientation matrix */
1059 matmul(mtemp, UB);
1060 /* determine inverse matrix */
1061 inversemat(mtemp, ms);
1062
1063 /* determine detector rotations */
1064 veccopy(rd, ri);
1065 for (j = Nd - 1; j >= 0; --j) {
1066 detectorRotation[j](detectorAngles[Nd * i + j], rd);
1067 }
1068 normalize(rd);
1069 diffvec(rd, local_ri);
1070
1071 /* ms contains now the rotation matrix to determine
1072 * the momentum transfer.
1073 * calculate the momentum transfer */
1074 vecmul(rd, M_2PI / lambda[i]); /* scales by k */
1075 matvec(ms, rd, &qpos[3 * i]);
1076 }
1077 return 0;
1078 }
1079
1080
1081 int ang2q_conversion_sdtrans(
1082 double *sampleAngles, double *detectorAngles, double *ri,
1083 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
1084 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
1085 int flags, double *qpos)
1086 /* conversion of Npoints of goniometer positions to reciprocal space
1087 * for a setup with point detector including the effect of a sample
1088 * displacement error and detector translations
1089 *
1090 * Parameters
1091 * ----------
1092 * sampleAngles .. angular positions of the sample goniometer
1093 * (Npoints, Ns)
1094 * detectorAngles. angular positions of the detector goniometer
1095 * (Npoints, Nd)
1096 * ri ............ direction of primary beam (length of detector distance)
1097 * (angles zero)
1098 * sampleAxis .... string with sample axis directions
1099 * detectorAxis .. string with detector axis directions
1100 * kappadir ...... rotation axis of a possible kappa circle
1101 * UB ............ orientation matrix and reciprocal space
1102 * conversion of the investigated crystal (3, 3)
1103 * sampledis ..... sample displacement vector in relative units of
1104 * the detector distance
1105 * lambda ........ wavelength of the used x-rays as array (Npoints,)
1106 * in units of Angstreom
1107 * Npoints ....... number of points to calculate
1108 * Ns ............ number of sample axes
1109 * Nd ............ number of detector axes
1110 * flags ......... general flags integer (verbosity)
1111 * qpos .......... momentum transfer (Npoints, 3) (OUTPUT array)
1112 *
1113 * */
1114 {
1115 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
1116 double local_ri[3], rd[3]; /* copy of primary beam direction */
1117 int i, j; /* needed indices */
1118 /* arrays with function pointers to rotation matrix functions */
1119 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1120 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1121
1122 /* determine axes directions */
1123 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1124 return -1;
1125 }
1126 if (determine_axes_directions_apply(detectorRotation,
1127 detectorAxis, Nd) != 0) {
1128 return -1;
1129 }
1130
1131 /* give ri correct length */
1132 veccopy(local_ri, ri);
1133 normalize(local_ri);
1134
1135 /* calculate rotation matices and perform rotations */
1136 #pragma omp parallel for default(shared) \
1137 private(i, j, mtemp, mtemp2, ms, rd) \
1138 schedule(static)
1139 for (i = 0; i < Npoints; ++i) {
1140 /* determine sample rotations */
1141 ident(mtemp);
1142 for (j = 0; j < Ns; ++j) {
1143 /* load kappa direction into matrix
1144 * (just needed for kappa goniometer) */
1145 mtemp2[0] = kappadir[0];
1146 mtemp2[1] = kappadir[1];
1147 mtemp2[2] = kappadir[2];
1148 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1149 matmul(mtemp, mtemp2);
1150 }
1151 /* apply rotation of orientation matrix */
1152 matmul(mtemp, UB);
1153 /* determine inverse matrix */
1154 inversemat(mtemp, ms);
1155
1156 /* determine detector rotations */
1157 for (j = Nd - 1; j >= 0; --j) {
1158 detectorRotation[j](detectorAngles[Nd * i + j], rd);
1159 }
1160 /* consider sample displacement in kf */
1161 diffvec(rd, sampledis);
1162 normalize(rd);
1163
1164 diffvec(rd, local_ri); /* ki/|k| - kf/|k| */
1165 vecmul(rd, M_2PI / lambda[i]); /* defines k_f */
1166 /* rd now contains the momentum transfer which will be
1167 * transformed to the sample q-coordinate system.
1168 * calculate the momentum transfer */
1169 matvec(ms, rd, &qpos[3 * i]);
1170 }
1171 return 0;
1172 }
1173
1174
1175 /***********************************************
1176 * QConversion functions for linear detector *
1177 ***********************************************/
1178
1179 PyObject* py_ang2q_conversion_linear(PyObject *self, PyObject *args)
1180 /* conversion of Npoints of goniometer positions to reciprocal space
1181 * for a linear detector with a given pixel size mounted along one of the
1182 * coordinate axis. This is the python wrapper function which should be
1183 * called by the user. It offers one common interface to the outside
1184 * although internally several performance optimized variants are called.
1185 *
1186 * Parameters
1187 * ----------
1188 * sampleAngles .... angular positions of the goniometer (Npoints, Ns)
1189 * detectorAngles .. angular positions of the detector goniometer
1190 * (Npoints, Nd)
1191 * rcch ............ direction + distance of center channel (angles zero)
1192 * sampleAxis ...... string with sample axis directions
1193 * detectorAxis .... string with detector axis directions
1194 * kappadir ........ rotation axis of a possible kappa circle
1195 * cch ............. center channel of the detector
1196 * dpixel .......... width of one pixel, same unit as distance rcch
1197 * roi ............. region of interest of the detector
1198 * dir ............. direction of the detector, e.g.: "x+"
1199 * tilt ............ tilt of the detector direction from dir
1200 * UB .............. orientation matrix and reciprocal space conversion
1201 * of investigated crystal (3, 3)
1202 * sampledis ....... sample displacement vector, same units as the
1203 * detector distance
1204 * lambda .......... wavelength of the used x-rays in Angstroem (Npoints,)
1205 * nthreads ........ number of threads to use in parallel section of
1206 * the code
1207 * flags ........... integer with flags: (1: has_translations;
1208 * 4: has_sampledis;
1209 * 16: verbose)
1210 *
1211 * Returns
1212 * -------
1213 * qpos ............ momentum transfer (Npoints * Nch, 3)
1214 * */
1215 {
1216 int Ns, Nd; /* number of sample and detector circles */
1217 int Npoints; /* number of angular positions */
1218 int Nch; /* number of channels in region of interest */
1219 int r; /* return value checking */
1220 int flags; /* flags to select behavior of the function */
1221 unsigned int nthreads; /* number of threads to use */
1222 double cch, dpixel, tilt; /* wavelength and detector parameters */
1223 char *sampleAxis, *detectorAxis, *dir; /* string with sample and
1224 * detector axis, and
1225 * detector direction */
1226 double *sampleAngles, *detectorAngles, *rcch, *kappadir, *sampledis,
1227 *UB, *qpos, *lambda; /* c-arrays for further usage */
1228 int *roi; /* region of interest integer array */
1229 npy_intp nout[2];
1230
1231 PyArrayObject *sampleAnglesArr = NULL, *detectorAnglesArr = NULL,
1232 *rcchArr = NULL, *kappadirArr = NULL, *roiArr = NULL,
1233 *sampledisArr = NULL, *UBArr = NULL, *qposArr = NULL,
1234 *lambdaArr = NULL;
1235
1236 /* Python argument conversion code */
1237 if (!PyArg_ParseTuple(args, "O!O!O!ssO!ddO!sdO!O!O!Ii",
1238 &PyArray_Type, &sampleAnglesArr,
1239 &PyArray_Type, &detectorAnglesArr,
1240 &PyArray_Type, &rcchArr,
1241 &sampleAxis, &detectorAxis,
1242 &PyArray_Type, &kappadirArr,
1243 &cch, &dpixel, &PyArray_Type, &roiArr,
1244 &dir, &tilt,
1245 &PyArray_Type, &UBArr,
1246 &PyArray_Type, &sampledisArr,
1247 &PyArray_Type, &lambdaArr, &nthreads, &flags)) {
1248 return NULL;
1249 }
1250
1251 /* check Python array dimensions and types */
1252 PYARRAY_CHECK(sampleAnglesArr, 2, NPY_DOUBLE,
1253 "sampleAngles must be a 2D double array");
1254 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
1255 "detectorAngles must be a 2D double array");
1256 PYARRAY_CHECK(lambdaArr, 1, NPY_DOUBLE,
1257 "wavelength must be a 1D double array");
1258 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE,
1259 "rcch must be a 1D double array");
1260 if (PyArray_SIZE(rcchArr) != 3) {
1261 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
1262 return NULL;
1263 }
1264
1265 PYARRAY_CHECK(sampledisArr, 1, NPY_DOUBLE,
1266 "sampledis must be a 1D double array");
1267 if (PyArray_SIZE(sampledisArr) != 3) {
1268 PyErr_SetString(PyExc_ValueError, "sampledis needs to be of length 3");
1269 return NULL;
1270 }
1271
1272 PYARRAY_CHECK(kappadirArr, 1, NPY_DOUBLE,
1273 "kappa_dir must be a 1D double array");
1274 if (PyArray_SIZE(kappadirArr) != 3) {
1275 PyErr_SetString(PyExc_ValueError, "kappa_dir needs to be of length 3");
1276 return NULL;
1277 }
1278 PYARRAY_CHECK(UBArr, 2, NPY_DOUBLE, "UB must be a 2D double array");
1279 if (PyArray_DIMS(UBArr)[0] != 3 || PyArray_DIMS(UBArr)[1] != 3) {
1280 PyErr_SetString(PyExc_ValueError, "UB must be of shape (3, 3)");
1281 return NULL;
1282 }
1283 PYARRAY_CHECK(roiArr, 1, NPY_INT32, "roi must be a 1D int array");
1284 if (PyArray_SIZE(roiArr) != 2) {
1285 PyErr_SetString(PyExc_ValueError, "roi must be of length 2");
1286 return NULL;
1287 }
1288
1289 Npoints = (int) PyArray_DIMS(sampleAnglesArr)[0];
1290 Ns = (int) PyArray_DIMS(sampleAnglesArr)[1];
1291 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
1292 if (PyArray_DIMS(detectorAnglesArr)[0] != Npoints) {
1293 PyErr_SetString(PyExc_ValueError,
1294 "detectorAngles and sampleAngles must have same first dimension");
1295 return NULL;
1296 }
1297 if (PyArray_SIZE(lambdaArr) != Npoints) {
1298 PyErr_SetString(PyExc_ValueError,
1299 "size of wavelength array need to fit with angle arrays");
1300 return NULL;
1301 }
1302
1303 sampleAngles = (double *) PyArray_DATA(sampleAnglesArr);
1304 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
1305 lambda = (double *) PyArray_DATA(lambdaArr);
1306 rcch = (double *) PyArray_DATA(rcchArr);
1307 kappadir = (double *) PyArray_DATA(kappadirArr);
1308 UB = (double *) PyArray_DATA(UBArr);
1309 sampledis = (double *) PyArray_DATA(sampledisArr);
1310 roi = (int *) PyArray_DATA(roiArr);
1311
1312 /* derived values from input parameters */
1313 Nch = roi[1] - roi[0]; /* number of channels */
1314
1315 /* create output ndarray */
1316 nout[0] = Npoints * Nch;
1317 nout[1] = 3;
1318 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
1319 qpos = (double *) PyArray_DATA(qposArr);
1320
1321 #ifdef __OPENMP__
1322 /* set openmp thread numbers dynamically */
1323 OMPSETNUMTHREADS(nthreads);
1324 #endif
1325
1326 /* call worker function */
1327 if (flags & HAS_SAMPLEDIS) {
1328 if (flags & HAS_TRANSLATIONS) {
1329 r = ang2q_conversion_linear_sdtrans(
1330 sampleAngles, detectorAngles, rcch, sampleAxis,
1331 detectorAxis, kappadir, cch, dpixel, roi, dir, tilt,
1332 UB, sampledis, lambda, Npoints, Ns, Nd, Nch, flags, qpos);
1333 }
1334 else {
1335 r = ang2q_conversion_linear_sd(
1336 sampleAngles, detectorAngles, rcch, sampleAxis,
1337 detectorAxis, kappadir, cch, dpixel, roi, dir, tilt,
1338 UB, sampledis, lambda, Npoints, Ns, Nd, Nch, flags, qpos);
1339 }
1340 }
1341 else {
1342 if (flags & HAS_TRANSLATIONS) {
1343 r = ang2q_conversion_linear_trans(
1344 sampleAngles, detectorAngles, rcch, sampleAxis,
1345 detectorAxis, kappadir, cch, dpixel, roi, dir, tilt,
1346 UB, lambda, Npoints, Ns, Nd, Nch, flags, qpos);
1347 }
1348 else {
1349 r = ang2q_conversion_linear(
1350 sampleAngles, detectorAngles, rcch, sampleAxis,
1351 detectorAxis, kappadir, cch, dpixel, roi, dir, tilt,
1352 UB, lambda, Npoints, Ns, Nd, Nch, flags, qpos);
1353 }
1354 }
1355
1356 /* clean up */
1357 Py_DECREF(sampleAnglesArr);
1358 Py_DECREF(detectorAnglesArr);
1359 Py_DECREF(rcchArr);
1360 Py_DECREF(kappadirArr);
1361 Py_DECREF(roiArr);
1362 Py_DECREF(UBArr);
1363 Py_DECREF(sampledisArr);
1364 Py_DECREF(lambdaArr);
1365 if (r != 0) {
1366 return NULL;
1367 }
1368
1369 /* return output array */
1370 return PyArray_Return(qposArr);
1371 }
1372
1373
1374 int ang2q_conversion_linear(
1375 double *sampleAngles, double *detectorAngles, double *rcch,
1376 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
1377 double dpixel, int *roi, char *dir, double tilt, double *UB,
1378 double *lambda, int Npoints, int Ns, int Nd, int Nch,
1379 int flags, double *qpos)
1380 /* conversion of Npoints of goniometer positions to reciprocal space
1381 * for a linear detector with a given pixel size mounted along one of the
1382 * coordinate axis. This is the python wrapper function which should be
1383 * called by the user. It offers one common interface to the outside
1384 * although internally several performance optimized variants are called.
1385 *
1386 * Parameters
1387 * ----------
1388 * sampleAngles .... angular positions of the goniometer (Npoints, Ns)
1389 * detectorAngles .. angular positions of the detector goniometer
1390 * (Npoints, Nd)
1391 * rcch ............ direction + distance of center channel (angles zero)
1392 * sampleAxis ...... string with sample axis directions
1393 * detectorAxis .... string with detector axis directions
1394 * kappadir ........ rotation axis of a possible kappa circle
1395 * cch ............. center channel of the detector
1396 * dpixel .......... width of one pixel, same unit as distance rcch
1397 * roi ............. region of interest of the detector
1398 * dir ............. direction of the detector, e.g.: "x+"
1399 * tilt ............ tilt of the detector direction from dir
1400 * UB .............. orientation matrix and reciprocal space conversion
1401 * of investigated crystal (3, 3)
1402 * lambda .......... wavelength of the used x-rays in Angstroem (Npoints,)
1403 * Npoints ......... number of points to calculate
1404 * Ns .............. number of sample axes
1405 * Nd .............. number of detector axes
1406 * Nch ............. number of channels
1407 * flags ........... general flags integer (verbosity)
1408 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
1409 *
1410 * */
1411 {
1412 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
1413 double rd[3], rpixel[3], rcchp[3]; /* detector position */
1414 double r_i[3], rtemp[3]; /* center channel direction */
1415 int i, j, k; /* needed indices */
1416 double f; /* f = M_2PI / lambda */
1417 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1418 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1419
1420 /* determine axes directions */
1421 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1422 return -1;
1423 }
1424 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
1425 return -1;
1426 }
1427
1428 veccopy(r_i, rcch);
1429 normalize(r_i);
1430 /* determine detector pixel vector */
1431 if (determine_detector_pixel(rpixel, dir, dpixel, r_i, tilt) != 0) {
1432 return -1;
1433 }
1434 for (k = 0; k < 3; ++k) {
1435 rcchp[k] = rpixel[k] * cch;
1436 }
1437
1438 /* calculate rotation matices and perform rotations */
1439 #pragma omp parallel for default(shared) \
1440 private(i, j, k, f, mtemp, mtemp2, ms, md, rd, rtemp) \
1441 schedule(static)
1442 for (i = 0; i < Npoints; ++i) {
1443 /* length of k */
1444 f = M_2PI / lambda[i];
1445 /* determine sample rotations */
1446 ident(mtemp);
1447 for (j = 0; j < Ns; ++j) {
1448 /* load kappa direction into matrix
1449 * (just needed for kappa goniometer) */
1450 mtemp2[0] = kappadir[0];
1451 mtemp2[1] = kappadir[1];
1452 mtemp2[2] = kappadir[2];
1453 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1454 matmul(mtemp, mtemp2);
1455 }
1456 /* apply rotation of orientation matrix */
1457 matmul(mtemp, UB);
1458 /* determine inverse matrix */
1459 inversemat(mtemp, ms);
1460
1461 /* determine detector rotations */
1462 ident(md);
1463 for (j = 0; j < Nd; ++j) {
1464 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
1465 matmul(md, mtemp);
1466 }
1467
1468 /* ms contains now the inverse rotation matrix for the sample circles
1469 * md contains the detector rotation matrix
1470 * calculate the momentum transfer for each detector pixel */
1471 for (j = roi[0]; j < roi[1]; ++j) {
1472 for (k = 0; k < 3; ++k) {
1473 rd[k] = j * rpixel[k] - rcchp[k];
1474 }
1475 sumvec(rd, rcch);
1476 normalize(rd);
1477 /* rd contains detector pixel direction,
1478 * r_i contains primary beam direction */
1479 matvec(md, rd, rtemp);
1480 diffvec(rtemp, r_i);
1481 vecmul(rtemp, f);
1482 /* determine momentum transfer */
1483 matvec(ms, rtemp, &qpos[3 * (i * Nch + j - roi[0])]);
1484 }
1485 }
1486 return 0;
1487 }
1488
1489
1490 int ang2q_conversion_linear_sd(
1491 double *sampleAngles, double *detectorAngles, double *rcch,
1492 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
1493 double dpixel, int *roi, char *dir, double tilt, double *UB,
1494 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
1495 int Nch, int flags, double *qpos)
1496 /* conversion of Npoints of goniometer positions to reciprocal space
1497 * for a linear detector with a given pixel size mounted along one of
1498 * the coordinate axis. this variant also considers the effect of a sample
1499 * displacement.
1500 *
1501 * Parameters
1502 * ----------
1503 * sampleAngles .... angular positions of the goniometer (Npoints, Ns)
1504 * detectorAngles .. angular positions of the detector goniometer
1505 * (Npoints, Nd)
1506 * rcch ............ direction + distance of center channel (angles zero)
1507 * sampleAxis ...... string with sample axis directions
1508 * detectorAxis .... string with detector axis directions
1509 * kappadir ........ rotation axis of a possible kappa circle
1510 * cch ............. center channel of the detector
1511 * dpixel .......... width of one pixel, same unit as distance rcch
1512 * roi ............. region of interest of the detector
1513 * dir ............. direction of the detector, e.g.: "x+"
1514 * tilt ............ tilt of the detector direction from dir
1515 * UB .............. orientation matrix and reciprocal space conversion
1516 * of investigated crystal (3, 3)
1517 * sampledis ....... sample displacement vector, same units as the
1518 * detector distance
1519 * lambda .......... wavelength of the used x-rays in Angstroem (Npoints,)
1520 * Npoints ......... number of points to calculate
1521 * Ns .............. number of sample axes
1522 * Nd .............. number of detector axes
1523 * Nch ............. number of channels
1524 * flags ........... general flags integer (verbosity)
1525 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
1526 *
1527 * */
1528 {
1529 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
1530 double rd[3], rpixel[3], rcchp[3]; /* detector position */
1531 double r_i[3], rtemp[3]; /* center channel direction */
1532 int i, j, k; /* needed indices */
1533 double f; /* wavelength parameters */
1534 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1535 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1536
1537 /* determine axes directions */
1538 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1539 return -1;
1540 }
1541 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
1542 return -1;
1543 }
1544
1545 veccopy(r_i, rcch);
1546 normalize(r_i);
1547 /* determine detector pixel vector */
1548 if (determine_detector_pixel(rpixel, dir, dpixel, r_i, tilt) != 0) {
1549 return -1;
1550 }
1551 for (k = 0; k < 3; ++k) {
1552 rcchp[k] = rpixel[k] * cch;
1553 }
1554
1555 /* calculate rotation matices and perform rotations */
1556 #pragma omp parallel for default(shared) \
1557 private(i, j, k, f, mtemp, mtemp2, ms, md, rd, rtemp) \
1558 schedule(static)
1559 for (i = 0; i < Npoints; ++i) {
1560 /* length of k */
1561 f = M_2PI / lambda[i];
1562 /* determine sample rotations */
1563 ident(mtemp);
1564 for (j = 0; j < Ns; ++j) {
1565 /* load kappa direction into matrix
1566 * (just needed for kappa goniometer) */
1567 mtemp2[0] = kappadir[0];
1568 mtemp2[1] = kappadir[1];
1569 mtemp2[2] = kappadir[2];
1570 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1571 matmul(mtemp, mtemp2);
1572 }
1573 /* apply rotation of orientation matrix */
1574 matmul(mtemp, UB);
1575 /* determine inverse matrix */
1576 inversemat(mtemp, ms);
1577
1578 /* determine detector rotations */
1579 ident(md);
1580 for (j = 0; j < Nd; ++j) {
1581 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
1582 matmul(md, mtemp);
1583 }
1584
1585 /* ms contains now the inverse rotation matrix for the sample circles
1586 * md contains the detector rotation matrix
1587 * calculate the momentum transfer for each detector pixel */
1588 for (j = roi[0]; j < roi[1]; ++j) {
1589 for (k = 0; k < 3; ++k) {
1590 rd[k] = j * rpixel[k] - rcchp[k];
1591 }
1592 sumvec(rd, rcch);
1593 matvec(md, rd, rtemp);
1594 /* consider sample displacement vector */
1595 diffvec(rtemp, sampledis);
1596 normalize(rtemp);
1597 /* continue with normal conversion */
1598 /* rtemp contains detector pixel direction,
1599 * r_i contains primary beam direction */
1600 diffvec(rtemp, r_i);
1601 vecmul(rtemp, f);
1602 /* determine momentum transfer */
1603 matvec(ms, rtemp, &qpos[3 * (i * Nch + j - roi[0])]);
1604 }
1605 }
1606 return 0;
1607 }
1608
1609
1610 int ang2q_conversion_linear_trans(
1611 double *sampleAngles, double *detectorAngles, double *rcch,
1612 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
1613 double dpixel, int *roi, char *dir, double tilt, double *UB,
1614 double *lambda, int Npoints, int Ns, int Nd, int Nch,
1615 int flags, double *qpos)
1616 /* conversion of Npoints of goniometer positions to reciprocal space
1617 * for a linear detector with a given pixel size mounted along one of
1618 * the coordinate axis, and translation motors on the detector arm
1619 *
1620 * Parameters
1621 * ----------
1622 * sampleAngles .... angular positions of the goniometer (Npoints, Ns)
1623 * detectorAngles .. angular positions of the detector goniometer
1624 * (Npoints, Nd)
1625 * rcch ............ direction + distance of center channel (angles zero)
1626 * sampleAxis ...... string with sample axis directions
1627 * detectorAxis .... string with detector axis directions
1628 * kappadir ........ rotation axis of a possible kappa circle
1629 * cch ............. center channel of the detector
1630 * dpixel .......... width of one pixel, same unit as distance rcch
1631 * roi ............. region of interest of the detector
1632 * dir ............. direction of the detector, e.g.: "x+"
1633 * tilt ............ tilt of the detector direction from dir
1634 * UB .............. orientation matrix and reciprocal space conversion
1635 * of investigated crystal (3, 3)
1636 * lambda .......... wavelength of the used x-rays in Angstroem (Npoints,)
1637 * Npoints ......... number of points to calculate
1638 * Ns .............. number of sample axes
1639 * Nd .............. number of detector axes
1640 * Nch ............. number of channels
1641 * flags ........... general flags integer (verbosity)
1642 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array))
1643 *
1644 * */
1645 {
1646 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
1647 double rd[3], rpixel[3], rcchp[3]; /* detector position */
1648 double r_i[3]; /* center channel direction */
1649 int i, j, k; /* needed indices */
1650 double f; /* f = M_2PI / lambda */
1651 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1652 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1653
1654 /* determine axes directions */
1655 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1656 return -1;
1657 }
1658 if (determine_axes_directions_apply(detectorRotation,
1659 detectorAxis, Nd) != 0) {
1660 return -1;
1661 }
1662
1663 veccopy(r_i, rcch);
1664 normalize(r_i);
1665 /* determine detector pixel vector */
1666 if (determine_detector_pixel(rpixel, dir, dpixel, r_i, tilt) != 0) {
1667 return -1;
1668 }
1669 for (k = 0; k < 3; ++k) {
1670 rcchp[k] = rpixel[k] * cch;
1671 }
1672
1673 /* calculate rotation matices and perform rotations */
1674 #pragma omp parallel for default(shared) \
1675 private(i, j, k, f, mtemp, mtemp2, ms, rd) \
1676 schedule(static)
1677 for (i = 0; i < Npoints; ++i) {
1678 /* length of k */
1679 f = M_2PI / lambda[i];
1680 /* determine sample rotations */
1681 ident(mtemp);
1682 for (j = 0; j < Ns; ++j) {
1683 /* load kappa direction into matrix
1684 * (just needed for kappa goniometer) */
1685 mtemp2[0] = kappadir[0];
1686 mtemp2[1] = kappadir[1];
1687 mtemp2[2] = kappadir[2];
1688 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1689 matmul(mtemp, mtemp2);
1690 }
1691 /* apply rotation of orientation matrix */
1692 matmul(mtemp, UB);
1693 /* determine inverse matrix */
1694 inversemat(mtemp, ms);
1695
1696 /* ms contains now the inverse rotation matrix for the sample circles
1697 * calculate the momentum transfer for each detector pixel */
1698 for (j = roi[0]; j < roi[1]; ++j) {
1699 for (k = 0; k < 3; ++k) {
1700 rd[k] = j * rpixel[k] - rcchp[k];
1701 }
1702 sumvec(rd, rcch);
1703 /* determine detector rotations */
1704 for (k = Nd - 1; k >= 0; --k) {
1705 detectorRotation[k](detectorAngles[Nd * i + k], rd);
1706 }
1707
1708 normalize(rd);
1709 /* rd contains detector pixel direction,
1710 * r_i contains primary beam direction */
1711 diffvec(rd, r_i);
1712 vecmul(rd, f);
1713 /* determine momentum transfer */
1714 matvec(ms, rd, &qpos[3 * (i * Nch + j - roi[0])]);
1715 }
1716 }
1717 return 0;
1718 }
1719
1720 int ang2q_conversion_linear_sdtrans(
1721 double *sampleAngles, double *detectorAngles, double *rcch,
1722 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
1723 double dpixel, int *roi, char *dir, double tilt, double *UB,
1724 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
1725 int Nch, int flags, double *qpos)
1726 /* conversion of Npoints of goniometer positions to reciprocal space
1727 * for a linear detector with a given pixel size mounted along one of
1728 * the coordinate axis. this variant also considers the effect of a sample
1729 * displacement and can consider detector translation-axis.
1730 *
1731 * Parameters
1732 * ----------
1733 * sampleAngles .... angular positions of the goniometer (Npoints, Ns)
1734 * detectorAngles .. angular positions of the detector goniometer
1735 * (Npoints, Nd)
1736 * rcch ............ direction + distance of center channel (angles zero)
1737 * sampleAxis ...... string with sample axis directions
1738 * detectorAxis .... string with detector axis directions
1739 * kappadir ........ rotation axis of a possible kappa circle
1740 * cch ............. center channel of the detector
1741 * dpixel .......... width of one pixel, same unit as distance rcch
1742 * roi ............. region of interest of the detector
1743 * dir ............. direction of the detector, e.g.: "x+"
1744 * tilt ............ tilt of the detector direction from dir
1745 * UB .............. orientation matrix and reciprocal space conversion
1746 * of investigated crystal (3, 3)
1747 * sampledis ....... sample displacement vector, same units as the
1748 * detector distance
1749 * lambda .......... wavelength of the used x-rays in Angstroem (Npoints,)
1750 * Npoints ......... number of points to calculate
1751 * Ns .............. number of sample axes
1752 * Nd .............. number of detector axes
1753 * Nch ............. number of channels
1754 * flags ........... general flags integer (verbosity)
1755 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array))
1756 *
1757 * */
1758 {
1759 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
1760 double rd[3], rpixel[3], rcchp[3]; /* detector position */
1761 double r_i[3]; /* center channel direction */
1762 int i, j, k; /* needed indices */
1763 double f; /* wavelength parameter */
1764 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1765 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1766
1767 /* determine axes directions */
1768 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1769 return -1;
1770 }
1771 if (determine_axes_directions_apply(detectorRotation,
1772 detectorAxis, Nd) != 0) {
1773 return -1;
1774 }
1775
1776 veccopy(r_i, rcch);
1777 normalize(r_i);
1778 /* determine detector pixel vector */
1779 if (determine_detector_pixel(rpixel, dir, dpixel, r_i, tilt) != 0) {
1780 return -1;
1781 }
1782 for (k = 0; k < 3; ++k) {
1783 rcchp[k] = rpixel[k] * cch;
1784 }
1785
1786 /* calculate rotation matices and perform rotations */
1787 #pragma omp parallel for default(shared) \
1788 private(i, j, k, f, mtemp, mtemp2, ms, rd) \
1789 schedule(static)
1790 for (i = 0; i < Npoints; ++i) {
1791 /* length of k */
1792 f = M_2PI / lambda[i];
1793 /* determine sample rotations */
1794 ident(mtemp);
1795 for (j = 0; j < Ns; ++j) {
1796 /* load kappa direction into matrix
1797 * (just needed for kappa goniometer) */
1798 mtemp2[0] = kappadir[0];
1799 mtemp2[1] = kappadir[1];
1800 mtemp2[2] = kappadir[2];
1801 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1802 matmul(mtemp, mtemp2);
1803 }
1804 /* apply rotation of orientation matrix */
1805 matmul(mtemp, UB);
1806 /* determine inverse matrix */
1807 inversemat(mtemp, ms);
1808
1809 /* ms contains now the inverse rotation matrix for the sample circles
1810 * calculate the momentum transfer for each detector pixel */
1811 for (j = roi[0]; j < roi[1]; ++j) {
1812 for (k = 0; k < 3; ++k) {
1813 rd[k] = j * rpixel[k] - rcchp[k];
1814 }
1815 sumvec(rd, rcch);
1816 /* apply detector rotations/translations, starting with the
1817 * inner most */
1818 for (k = Nd - 1; k >= 0; --k) {
1819 detectorRotation[k](detectorAngles[Nd * i + k], rd);
1820 }
1821 /* consider sample displacement vector */
1822 diffvec(rd, sampledis);
1823 normalize(rd);
1824 /* continue with normal conversion */
1825 /* rd contains detector pixel direction,
1826 * r_i contains primary beam direction */
1827 diffvec(rd, r_i);
1828 vecmul(rd, f);
1829 /* determine momentum transfer */
1830 matvec(ms, rd, &qpos[3 * (i * Nch + j - roi[0])]);
1831 }
1832 }
1833 return 0;
1834 }
1835
1836 /***********************************************
1837 * QConversion functions for area detectors *
1838 ***********************************************/
1839
1840 PyObject* py_ang2q_conversion_area(PyObject *self, PyObject *args)
1841 /* conversion of Npoints of goniometer positions to reciprocal space for an
1842 * area detector with a given pixel size mounted along one of the coordinate
1843 * axis. This is the python wrapper function which should be called by the
1844 * user. It offers one common interface to the outside although internally
1845 * several performance optimized variants are called.
1846 *
1847 * Parameters
1848 * ----------
1849 * sampleAngles .... angular positions of the sample goniometer
1850 * (Npoints, Ns)
1851 * detectorAngles .. angular positions of the detector goniometer
1852 * (Npoints, Nd)
1853 * rcch ............ direction + distance of center pixel (angles zero)
1854 * sampleAxis ...... string with sample axis directions
1855 * detectorAxis .... string with detector axis directions
1856 * kappadir ........ rotation axis of a possible kappa circle
1857 * cch1 ............ center channel of the detector
1858 * cch2 ............ center channel of the detector
1859 * dpixel1 ......... width of one pixel in first direction, same unit as
1860 * distance rcch
1861 * dpixel2 ......... width of one pixel in second direction, same unit as
1862 * distance rcch
1863 * roi ............. region of interest for the area detector
1864 * [dir1min, dir1max, dir2min, dir2max]
1865 * dir1 ............ first direction of the detector, e.g.: "x+"
1866 * dir2 ............ second direction of the detector, e.g.: "z+"
1867 * tiltazimuth ..... azimuth of the tilt
1868 * tilt ............ tilt of the detector plane (rotation around axis
1869 * normal to the direction given by the tiltazimuth
1870 * UB .............. orientation matrix and reciprocal space conversion
1871 * of investigated crystal (3, 3)
1872 * sampledis ....... sample displacement vector, same units as the
1873 * detector distance
1874 * lambda .......... wavelength of the used x-rays (Npoints,)
1875 * nthreads ........ number of threads to use in parallelization
1876 * flags ........... integer with flags: (1: has_translations;
1877 * 4: has_sampledis;
1878 * 16: verbose)
1879 *
1880 * Returns
1881 * -------
1882 * qpos ............ momentum transfer (Npoints * Npix1 * Npix2, 3)
1883 * */
1884 {
1885 int Ns, Nd; /* number of sample and detector circles */
1886 int Npoints; /* number of angular positions */
1887 int r; /* return value checking */
1888 int flags; /* flags to select behavior of the function */
1889 unsigned int nthreads; /* number threads for OpenMP */
1890 double cch1, cch2, dpixel1, dpixel2, tilt, tiltazimuth;
1891 /* string with sample and detector axis, and detector direction */
1892 char *sampleAxis, *detectorAxis, *dir1, *dir2;
1893 double *sampleAngles,*detectorAngles, *rcch, *kappadir, *UB, *sampledis,
1894 *qpos, *lambda; /* c-arrays for further usage */
1895 int *roi; /* region of interest integer array */
1896 npy_intp nout[2];
1897 /* numpy arrays */
1898 PyArrayObject *sampleAnglesArr = NULL, *detectorAnglesArr = NULL,
1899 *rcchArr = NULL, *kappadirArr = NULL, *roiArr = NULL,
1900 *sampledisArr = NULL, *UBArr = NULL, *qposArr = NULL,
1901 *lambdaArr = NULL;
1902
1903 /* Python argument conversion code */
1904 if (!PyArg_ParseTuple(args, "O!O!O!ssO!ddddO!ssddO!O!O!Ii",
1905 &PyArray_Type, &sampleAnglesArr,
1906 &PyArray_Type, &detectorAnglesArr,
1907 &PyArray_Type, &rcchArr,
1908 &sampleAxis, &detectorAxis,
1909 &PyArray_Type, &kappadirArr,
1910 &cch1, &cch2, &dpixel1, &dpixel2,
1911 &PyArray_Type, &roiArr,
1912 &dir1, &dir2, &tiltazimuth, &tilt,
1913 &PyArray_Type, &UBArr,
1914 &PyArray_Type, &sampledisArr,
1915 &PyArray_Type, &lambdaArr, &nthreads, &flags)) {
1916 return NULL;
1917 }
1918
1919 /* check Python array dimensions and types */
1920 PYARRAY_CHECK(sampleAnglesArr, 2, NPY_DOUBLE,
1921 "sampleAngles must be a 2D double array");
1922 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
1923 "detectorAngles must be a 2D double array");
1924 PYARRAY_CHECK(lambdaArr, 1, NPY_DOUBLE,
1925 "wavelength must be a 1D double array");
1926 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE, "rcch must be a 1D double array");
1927 if (PyArray_SIZE(rcchArr) != 3) {
1928 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
1929 return NULL;
1930 }
1931 PYARRAY_CHECK(kappadirArr, 1, NPY_DOUBLE,
1932 "kappa_dir must be a 1D double array");
1933 if (PyArray_SIZE(kappadirArr) != 3) {
1934 PyErr_SetString(PyExc_ValueError, "kappa_dir needs to be of length 3");
1935 return NULL;
1936 }
1937 PYARRAY_CHECK(UBArr, 2, NPY_DOUBLE, "UB must be a 2D double array");
1938 if (PyArray_DIMS(UBArr)[0] != 3 || PyArray_DIMS(UBArr)[1] != 3) {
1939 PyErr_SetString(PyExc_ValueError, "UB must be of shape (3, 3)");
1940 return NULL;
1941 }
1942 PYARRAY_CHECK(roiArr, 1, NPY_INT32, "roi must be a 1D int array");
1943 if (PyArray_SIZE(roiArr) != 4) {
1944 PyErr_SetString(PyExc_ValueError, "roi must be of length 4");
1945 return NULL;
1946 }
1947 PYARRAY_CHECK(sampledisArr, 1, NPY_DOUBLE,
1948 "sampledis must be a 1D double array");
1949 if (PyArray_SIZE(sampledisArr) != 3) {
1950 PyErr_SetString(PyExc_ValueError, "sampledis needs to be of length 3");
1951 return NULL;
1952 }
1953
1954 Npoints = (int) PyArray_DIMS(sampleAnglesArr)[0];
1955 Ns = (int) PyArray_DIMS(sampleAnglesArr)[1];
1956 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
1957 if (PyArray_DIMS(detectorAnglesArr)[0] != Npoints) {
1958 PyErr_SetString(PyExc_ValueError,
1959 "detectorAngles and sampleAngles must have same first dimension");
1960 return NULL;
1961 }
1962 if (PyArray_SIZE(lambdaArr) != Npoints) {
1963 PyErr_SetString(PyExc_ValueError,
1964 "size of wavelength array need to fit with angle arrays");
1965 return NULL;
1966 }
1967
1968 sampleAngles = (double *) PyArray_DATA(sampleAnglesArr);
1969 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
1970 lambda = (double *) PyArray_DATA(lambdaArr);
1971 rcch = (double *) PyArray_DATA(rcchArr);
1972 kappadir = (double *) PyArray_DATA(kappadirArr);
1973 UB = (double *) PyArray_DATA(UBArr);
1974 roi = (int *) PyArray_DATA(roiArr);
1975 sampledis = (double *) PyArray_DATA(sampledisArr);
1976
1977 /* create output ndarray */
1978 nout[0] = Npoints * (roi[1] - roi[0]) * (roi[3] - roi[2]);
1979 nout[1] = 3;
1980 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
1981 qpos = (double *) PyArray_DATA(qposArr);
1982
1983 #ifdef __OPENMP__
1984 /* set openmp thread numbers dynamically */
1985 OMPSETNUMTHREADS(nthreads);
1986 #endif
1987
1988 /* call worker function */
1989 if (flags & HAS_SAMPLEDIS) {
1990 if (flags & HAS_TRANSLATIONS) {
1991 r = ang2q_conversion_area_sdtrans(
1992 sampleAngles, detectorAngles, rcch, sampleAxis,
1993 detectorAxis, kappadir, cch1, cch2, dpixel1, dpixel2, roi,
1994 dir1, dir2, tiltazimuth, tilt, UB, sampledis, lambda,
1995 Npoints, Ns, Nd, flags, qpos);
1996 }
1997 else {
1998 r = ang2q_conversion_area_sd(
1999 sampleAngles, detectorAngles, rcch, sampleAxis,
2000 detectorAxis, kappadir, cch1, cch2, dpixel1, dpixel2, roi,
2001 dir1, dir2, tiltazimuth, tilt, UB, sampledis, lambda,
2002 Npoints, Ns, Nd, flags, qpos);
2003 }
2004 }
2005 else {
2006 if (flags & HAS_TRANSLATIONS) {
2007 r = ang2q_conversion_area_trans(
2008 sampleAngles, detectorAngles, rcch, sampleAxis,
2009 detectorAxis, kappadir, cch1, cch2, dpixel1, dpixel2, roi,
2010 dir1, dir2, tiltazimuth, tilt, UB, lambda, Npoints, Ns, Nd,
2011 flags, qpos);
2012 }
2013 else {
2014 r = ang2q_conversion_area(
2015 sampleAngles, detectorAngles, rcch, sampleAxis,
2016 detectorAxis, kappadir, cch1, cch2, dpixel1, dpixel2, roi,
2017 dir1, dir2, tiltazimuth, tilt, UB, lambda, Npoints, Ns, Nd,
2018 flags, qpos);
2019 }
2020 }
2021
2022 /* clean up */
2023 Py_DECREF(sampleAnglesArr);
2024 Py_DECREF(detectorAnglesArr);
2025 Py_DECREF(rcchArr);
2026 Py_DECREF(kappadirArr);
2027 Py_DECREF(roiArr);
2028 Py_DECREF(UBArr);
2029 Py_DECREF(sampledisArr);
2030 Py_DECREF(lambdaArr);
2031 if (r != 0) {
2032 return NULL;
2033 }
2034
2035 /* return output array */
2036 return PyArray_Return(qposArr);
2037 }
2038
2039
2040 int ang2q_conversion_area(
2041 double *sampleAngles, double *detectorAngles, double *rcch,
2042 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
2043 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
2044 char *dir2, double tiltazimuth, double tilt, double *UB,
2045 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos)
2046 /* conversion of Npoints of goniometer positions to reciprocal space
2047 * for an area detector with a given pixel size mounted along one of
2048 * the coordinate axis
2049 *
2050 * Parameters
2051 * ----------
2052 * sampleAngles .... angular positions of the sample goniometer
2053 * (Npoints, Ns)
2054 * detectorAngles .. angular positions of the detector goniometer
2055 * (Npoints, Nd)
2056 * rcch ............ direction + distance of center pixel (angles zero)
2057 * sampleAxis ...... string with sample axis directions
2058 * detectorAxis .... string with detector axis directions
2059 * kappadir ........ rotation axis of a possible kappa circle
2060 * cch1 ............ center channel of the detector
2061 * cch2 ............ center channel of the detector
2062 * dpixel1 ......... width of one pixel in first direction, same unit as
2063 * distance rcch
2064 * dpixel2 ......... width of one pixel in second direction, same unit as
2065 * distance rcch
2066 * roi ............. region of interest for the area detector
2067 * [dir1min, dir1max, dir2min, dir2max]
2068 * dir1 ............ first direction of the detector, e.g.: "x+"
2069 * dir2 ............ second direction of the detector, e.g.: "z+"
2070 * tiltazimuth ..... azimuth of the tilt
2071 * tilt ............ tilt of the detector plane (rotation around axis
2072 * normal to the direction
2073 * given by the tiltazimuth
2074 * UB .............. orientation matrix and reciprocal space conversion
2075 * of the investigated crystal (3, 3)
2076 * lambda .......... wavelength of the used x-rays (Npoints,)
2077 * Npoints ......... number of points to calculate
2078 * Ns .............. number of sample axes
2079 * Nd .............. number of detector axes
2080 * flags ........... general flags integer (verbosity)
2081 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
2082 *
2083 * */
2084 {
2085 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
2086 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2087 double r_i[3], rtemp[3]; /* r_i: center channel direction */
2088 int i, j, j1, j2, k; /* loop indices */
2089 int idxh1, idxh2; /* temporary index helper */
2090 double f; /* f = M_2PI / lambda and detector parameters */
2091 /* string with sample and detector axis, and detector direction */
2092 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
2093 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
2094
2095 /* calculate some index shortcuts */
2096 idxh1 = (roi[1] - roi[0]) * (roi[3] - roi[2]);
2097 idxh2 = roi[3] - roi[2];
2098
2099 /* determine axes directions */
2100 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
2101 return -1;
2102 }
2103 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
2104 return -1;
2105 }
2106
2107 veccopy(r_i, rcch);
2108 normalize(r_i);
2109
2110 /* determine detector pixel vector */
2111 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2112 return -1;
2113 }
2114 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2115 return -1;
2116 }
2117
2118 /* rotate detector pixel vectors according to tilt */
2119 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2120
2121 /* calculate center channel position in detector plane */
2122 for (k = 0; k < 3; ++k) {
2123 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2124 }
2125
2126 /* calculate rotation matices and perform rotations */
2127 #pragma omp parallel for default(shared) \
2128 private(i, j, j1, j2, k, f, mtemp, mtemp2, ms, md, rd, rtemp) \
2129 schedule(static)
2130 for (i = 0; i < Npoints; ++i) {
2131 f = M_2PI / lambda[i];
2132 /* determine sample rotations */
2133 ident(mtemp);
2134 for (j = 0; j < Ns; ++j) {
2135 /* load kappa direction into matrix
2136 * (just needed for kappa goniometer) */
2137 mtemp2[0] = kappadir[0];
2138 mtemp2[1] = kappadir[1];
2139 mtemp2[2] = kappadir[2];
2140 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
2141 matmul(mtemp, mtemp2);
2142 }
2143 /* apply rotation of orientation matrix */
2144 matmul(mtemp, UB);
2145 /* determine inverse matrix */
2146 inversemat(mtemp, ms);
2147
2148 /* determine detector rotations */
2149 ident(md);
2150 for (j = 0; j < Nd; ++j) {
2151 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
2152 matmul(md, mtemp);
2153 }
2154
2155 /* ms contains now the inverse rotation matrix for the sample circles
2156 * md contains the detector rotation matrix
2157 * calculate the momentum transfer for each detector pixel */
2158 for (j1 = roi[0]; j1 < roi[1]; ++j1) {
2159 for (j2 = roi[2]; j2 < roi[3]; ++j2) {
2160 for (k = 0; k < 3; ++k) {
2161 rd[k] = j1 * rpixel1[k] + j2 * rpixel2[k] - rcchp[k];
2162 }
2163 sumvec(rd, rcch);
2164 normalize(rd);
2165 /* rd contains detector pixel direction,
2166 * r_i contains primary beam direction */
2167 matvec(md, rd, rtemp);
2168 diffvec(rtemp, r_i);
2169 vecmul(rtemp, f);
2170 /* determine momentum transfer */
2171 matvec(ms, rtemp,
2172 &qpos[3 * (i * idxh1 + idxh2 * (j1 - roi[0]) +
2173 (j2 - roi[2]))]);
2174 }
2175 }
2176 }
2177 return 0;
2178 }
2179
2180
2181 int ang2q_conversion_area_sd(
2182 double *sampleAngles, double *detectorAngles, double *rcch,
2183 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
2184 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
2185 char *dir2, double tiltazimuth, double tilt, double *UB,
2186 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
2187 int flags, double *qpos)
2188 /* conversion of Npoints of goniometer positions to reciprocal space
2189 * for an area detector with a given pixel size mounted along one of
2190 * the coordinate axis. this variant also considers the effect of a
2191 * sample displacement error.
2192 *
2193 * Parameters
2194 * ----------
2195 * sampleAngles .... angular positions of the sample goniometer
2196 * (Npoints, Ns)
2197 * detectorAngles .. angular positions of the detector goniometer
2198 * (Npoints, Nd)
2199 * rcch ............ direction + distance of center pixel (angles zero)
2200 * sampleAxis ...... string with sample axis directions
2201 * detectorAxis .... string with detector axis directions
2202 * kappadir ........ rotation axis of a possible kappa circle
2203 * cch1 ............ center channel of the detector
2204 * cch2 ............ center channel of the detector
2205 * dpixel1 ......... width of one pixel in first direction, same unit as
2206 * distance rcch
2207 * dpixel2 ......... width of one pixel in second direction, same unit as
2208 * distance rcch
2209 * roi ............. region of interest for the area detector
2210 * [dir1min, dir1max, dir2min, dir2max]
2211 * dir1 ............ first direction of the detector, e.g.: "x+"
2212 * dir2 ............ second direction of the detector, e.g.: "z+"
2213 * tiltazimuth ..... azimuth of the tilt
2214 * tilt ............ tilt of the detector plane (rotation around axis
2215 * normal to the direction given by the tiltazimuth
2216 * UB .............. orientation matrix and reciprocal space conversion
2217 * of investigated crystal (3, 3)
2218 * sampledis ....... sample displacement vector, same units as the
2219 * detector distance
2220 * lambda .......... wavelength of the used x-rays (Npoints,)
2221 * Npoints ......... number of points to calculate
2222 * Ns .............. number of sample axes
2223 * Nd .............. number of detector axes
2224 * flags ........... general flags integer (verbosity)
2225 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
2226 *
2227 * */
2228 {
2229 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
2230 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2231 double r_i[3], rtemp[3]; /* r_i: center channel direction */
2232 int i, j, j1, j2, k; /* loop indices */
2233 int idxh1, idxh2; /* temporary index helper */
2234 double f; /* f = M_2PI / lambda and detector parameters */
2235 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
2236 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
2237
2238 /* calculate some index shortcuts */
2239 idxh1 = (roi[1] - roi[0]) * (roi[3] - roi[2]);
2240 idxh2 = roi[3] - roi[2];
2241
2242 /* determine axes directions */
2243 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
2244 return -1;
2245 }
2246 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
2247 return -1;
2248 }
2249
2250 veccopy(r_i, rcch);
2251 normalize(r_i);
2252
2253 /* determine detector pixel vector */
2254 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2255 return -1;
2256 }
2257 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2258 return -1;
2259 }
2260
2261 /* rotate detector pixel vectors according to tilt */
2262 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2263
2264 /* calculate center channel position in detector plane */
2265 for (k = 0; k < 3; ++k) {
2266 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2267 }
2268
2269 /* calculate rotation matices and perform rotations */
2270 #pragma omp parallel for default(shared) \
2271 private(i, j, j1, j2, k, f, mtemp, mtemp2, ms, md, rd, rtemp) \
2272 schedule(static)
2273 for (i = 0; i < Npoints; ++i) {
2274 /* length of k */
2275 f = M_2PI / lambda[i];
2276 /* determine sample rotations */
2277 ident(mtemp);
2278 for (j = 0; j < Ns; ++j) {
2279 /* load kappa direction into matrix
2280 * (just needed for kappa goniometer) */
2281 mtemp2[0] = kappadir[0];
2282 mtemp2[1] = kappadir[1];
2283 mtemp2[2] = kappadir[2];
2284 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
2285 matmul(mtemp, mtemp2);
2286 }
2287 /* apply rotation of orientation matrix */
2288 matmul(mtemp, UB);
2289 /* determine inverse matrix */
2290 inversemat(mtemp, ms);
2291
2292 /* determine detector rotations */
2293 ident(md);
2294 for (j = 0; j < Nd; ++j) {
2295 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
2296 matmul(md, mtemp);
2297 }
2298
2299 /* ms contains now the inverse rotation matrix for the sample circles
2300 * md contains the detector rotation matrix
2301 * calculate the momentum transfer for each detector pixel */
2302 for (j1 = roi[0]; j1 < roi[1]; ++j1) {
2303 for (j2 = roi[2]; j2 < roi[3]; ++j2) {
2304 for (k = 0; k < 3; ++k) {
2305 rd[k] = j1 * rpixel1[k] + j2 * rpixel2[k] - rcchp[k];
2306 }
2307 sumvec(rd, rcch);
2308 matvec(md, rd, rtemp);
2309 /* consider the effect of the sample displacement */
2310 diffvec(rtemp, sampledis);
2311 normalize(rtemp);
2312 /* rd contains detector pixel direction,
2313 * r_i contains primary beam direction */
2314 diffvec(rtemp, r_i);
2315 vecmul(rtemp, f);
2316 /* determine momentum transfer */
2317 matvec(ms, rtemp,
2318 &qpos[3 * (i * idxh1 + idxh2 * (j1 - roi[0]) +
2319 (j2 - roi[2]))]);
2320 }
2321 }
2322 }
2323 return 0;
2324 }
2325
2326
2327 int ang2q_conversion_area_trans(
2328 double *sampleAngles, double *detectorAngles, double *rcch,
2329 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
2330 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
2331 char *dir2, double tiltazimuth, double tilt, double *UB,
2332 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos)
2333 /* conversion of Npoints of goniometer positions to reciprocal space
2334 * for an area detector with a given pixel size mounted along one of
2335 * the coordinate axis including translation axis on the detector arm
2336 *
2337 * Parameters
2338 * ----------
2339 * sampleAngles .... angular positions of the sample goniometer
2340 * (Npoints, Ns)
2341 * detectorAngles .. angular positions of the detector goniometer
2342 * (Npoints, Nd)
2343 * rcch ............ direction + distance of center pixel (angles zero)
2344 * sampleAxis ...... string with sample axis directions
2345 * detectorAxis .... string with detector axis directions
2346 * kappadir ........ rotation axis of a possible kappa circle
2347 * cch1 ............ center channel of the detector
2348 * cch2 ............ center channel of the detector
2349 * dpixel1 ......... width of one pixel in first direction, same unit as
2350 * distance rcch
2351 * dpixel2 ......... width of one pixel in second direction, same unit as
2352 * distance rcch
2353 * roi ............. region of interest for the area detector
2354 * [dir1min, dir1max, dir2min, dir2max]
2355 * dir1 ............ first direction of the detector, e.g.: "x+"
2356 * dir2 ............ second direction of the detector, e.g.: "z+"
2357 * tiltazimuth ..... azimuth of the tilt
2358 * tilt ............ tilt of the detector plane (rotation around axis
2359 * normal to the direction
2360 * given by the tiltazimuth
2361 * UB .............. orientation matrix and reciprocal space conversion
2362 * of the investigated crystal (3, 3)
2363 * lambda .......... wavelength of the used x-rays (Npoints,)
2364 * Npoints ......... number of points to calculate
2365 * Ns .............. number of sample axes
2366 * Nd .............. number of detector axes
2367 * flags ........... general flags integer (verbosity)
2368 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
2369 *
2370 * */
2371 {
2372 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
2373 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2374 double r_i[3]; /* r_i: center channel direction */
2375 int i, j, j1, j2, k; /* loop indices */
2376 int idxh1, idxh2; /* temporary index helper */
2377 double f; /* f = M_2PI / lambda and detector parameters */
2378 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
2379 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
2380
2381 /* calculate some index shortcuts */
2382 idxh1 = (roi[1] - roi[0]) * (roi[3] - roi[2]);
2383 idxh2 = roi[3] - roi[2];
2384
2385 /* determine axes directions */
2386 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
2387 return -1;
2388 }
2389 if (determine_axes_directions_apply(detectorRotation,
2390 detectorAxis, Nd) != 0) {
2391 return -1;
2392 }
2393
2394 veccopy(r_i, rcch);
2395 normalize(r_i);
2396
2397 /* determine detector pixel vector */
2398 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2399 return -1;
2400 }
2401 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2402 return -1;
2403 }
2404
2405 /* rotate detector pixel vectors according to tilt */
2406 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2407
2408 /* calculate center channel position in detector plane */
2409 for (k = 0; k < 3; ++k) {
2410 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2411 }
2412
2413 /* calculate rotation matices and perform rotations */
2414 #pragma omp parallel for default(shared) \
2415 private(i, j, j1, j2, k, f, mtemp, mtemp2, ms, rd) \
2416 schedule(static)
2417 for (i = 0; i < Npoints; ++i) {
2418 f = M_2PI / lambda[i];
2419 /* determine sample rotations */
2420 ident(mtemp);
2421 for (j = 0; j < Ns; ++j) {
2422 /* load kappa direction into matrix
2423 * (just needed for kappa goniometer) */
2424 mtemp2[0] = kappadir[0];
2425 mtemp2[1] = kappadir[1];
2426 mtemp2[2] = kappadir[2];
2427 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
2428 matmul(mtemp, mtemp2);
2429 }
2430 /* apply rotation of orientation matrix */
2431 matmul(mtemp, UB);
2432 /* determine inverse matrix */
2433 inversemat(mtemp, ms);
2434
2435 /* ms contains now the inverse rotation matrix for the sample circles
2436 * detector rotations/translations need to be applied separately for
2437 * every pixel */
2438 for (j1 = roi[0]; j1 < roi[1]; ++j1) {
2439 for (j2 = roi[2]; j2 < roi[3]; ++j2) {
2440 for (k = 0; k < 3; ++k) {
2441 rd[k] = j1 * rpixel1[k] + j2 * rpixel2[k] - rcchp[k];
2442 }
2443 sumvec(rd, rcch);
2444 /* apply detector rotations/translations, starting with the
2445 * inner most */
2446 for (j = Nd - 1; j >= 0; --j) {
2447 detectorRotation[j](detectorAngles[Nd * i + j], rd);
2448 }
2449
2450 normalize(rd);
2451 /* rd contains detector pixel direction,
2452 * r_i contains primary beam direction */
2453 diffvec(rd, r_i);
2454 vecmul(rd, f);
2455 /* determine momentum transfer */
2456 matvec(ms, rd,
2457 &qpos[3 * (i * idxh1 + idxh2 * (j1 - roi[0]) +
2458 (j2 - roi[2]))]);
2459 }
2460 }
2461 }
2462 return 0;
2463 }
2464
2465
2466 int ang2q_conversion_area_sdtrans(
2467 double *sampleAngles, double *detectorAngles, double *rcch,
2468 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
2469 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
2470 char *dir2, double tiltazimuth, double tilt, double *UB,
2471 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
2472 int flags, double *qpos)
2473 /* conversion of Npoints of goniometer positions to reciprocal space
2474 * for an area detector with a given pixel size mounted along one of
2475 * the coordinate axis including translation axis on the detector arm
2476 * and considering a sample displacement
2477 *
2478 * Parameters
2479 * ----------
2480 * sampleAngles .... angular positions of the sample goniometer
2481 * (Npoints, Ns)
2482 * detectorAngles .. angular positions of the detector goniometer
2483 * (Npoints, Nd)
2484 * rcch ............ direction + distance of center pixel (angles zero)
2485 * sampleAxis ...... string with sample axis directions
2486 * detectorAxis .... string with detector axis directions
2487 * kappadir ........ rotation axis of a possible kappa circle
2488 * cch1 ............ center channel of the detector
2489 * cch2 ............ center channel of the detector
2490 * dpixel1 ......... width of one pixel in first direction, same unit as
2491 * distance rcch
2492 * dpixel2 ......... width of one pixel in second direction, same unit as
2493 * distance rcch
2494 * roi ............. region of interest for the area detector
2495 * [dir1min, dir1max, dir2min, dir2max]
2496 * dir1 ............ first direction of the detector, e.g.: "x+"
2497 * dir2 ............ second direction of the detector, e.g.: "z+"
2498 * tiltazimuth ..... azimuth of the tilt
2499 * tilt ............ tilt of the detector plane (rotation around axis
2500 * normal to the direction
2501 * given by the tiltazimuth
2502 * UB .............. orientation matrix and reciprocal space conversion
2503 * of the investigated crystal (3, 3)
2504 * sampledis ....... sample displacement vector, same units as the
2505 * detector distance
2506 * lambda .......... wavelength of the used x-rays (Npoints,)
2507 * Npoints ......... number of points to calculate
2508 * Ns .............. number of sample axes
2509 * Nd .............. number of detector axes
2510 * flags ........... general flags integer (verbosity)
2511 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
2512 *
2513 * */
2514 {
2515 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
2516 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2517 double r_i[3]; /* r_i: center channel direction */
2518 int i, j, j1, j2, k; /* loop indices */
2519 int idxh1, idxh2; /* temporary index helper */
2520 double f; /* f = M_2PI / lambda and detector parameters */
2521 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
2522 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
2523
2524 /* calculate some index shortcuts */
2525 idxh1 = (roi[1] - roi[0]) * (roi[3] - roi[2]);
2526 idxh2 = roi[3] - roi[2];
2527
2528 /* determine axes directions */
2529 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
2530 return -1;
2531 }
2532 if (determine_axes_directions_apply(detectorRotation,
2533 detectorAxis, Nd) != 0) {
2534 return -1;
2535 }
2536
2537 veccopy(r_i, rcch);
2538 normalize(r_i);
2539
2540 /* determine detector pixel vector */
2541 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2542 return -1;
2543 }
2544 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2545 return -1;
2546 }
2547
2548 /* rotate detector pixel vectors according to tilt */
2549 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2550
2551 /* calculate center channel position in detector plane */
2552 for (k = 0; k < 3; ++k) {
2553 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2554 }
2555
2556 /* calculate rotation matices and perform rotations */
2557 #pragma omp parallel for default(shared) \
2558 private(i, j, j1, j2, k, f, mtemp, mtemp2, ms, rd) \
2559 schedule(static)
2560 for (i = 0; i < Npoints; ++i) {
2561 f = M_2PI / lambda[i];
2562 /* determine sample rotations */
2563 ident(mtemp);
2564 for (j = 0; j < Ns; ++j) {
2565 /* load kappa direction into matrix
2566 * (just needed for kappa goniometer) */
2567 mtemp2[0] = kappadir[0];
2568 mtemp2[1] = kappadir[1];
2569 mtemp2[2] = kappadir[2];
2570 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
2571 matmul(mtemp, mtemp2);
2572 }
2573 /* apply rotation of orientation matrix */
2574 matmul(mtemp, UB);
2575 /* determine inverse matrix */
2576 inversemat(mtemp, ms);
2577
2578 /* ms contains now the inverse rotation matrix for the sample circles
2579 * detector rotations/translations need to be applied separately for
2580 * every pixel */
2581 for (j1 = roi[0]; j1 < roi[1]; ++j1) {
2582 for (j2 = roi[2]; j2 < roi[3]; ++j2) {
2583 for (k = 0; k < 3; ++k) {
2584 rd[k] = j1 * rpixel1[k] + j2 * rpixel2[k] - rcchp[k];
2585 }
2586 sumvec(rd, rcch);
2587 /* apply detector rotations/translations, starting with the
2588 * inner most */
2589 for (j = Nd - 1; j >= 0; --j) {
2590 detectorRotation[j](detectorAngles[Nd * i + j], rd);
2591 }
2592 /* consider the effect of the sample displacement */
2593 diffvec(rd, sampledis);
2594 normalize(rd);
2595 /* rd contains detector pixel direction,
2596 * r_i contains primary beam direction */
2597 diffvec(rd, r_i);
2598 vecmul(rd, f);
2599 /* determine momentum transfer */
2600 matvec(ms, rd,
2601 &qpos[3 * (i * idxh1 + idxh2 * (j1 - roi[0]) +
2602 (j2 - roi[2]))]);
2603 }
2604 }
2605 }
2606 return 0;
2607 }
2608
2609
2610 PyObject* ang2q_conversion_area_pixel(PyObject *self, PyObject *args)
2611 /* conversion of Npoints of detector positions to Q for an area detector
2612 * with a given pixel size mounted along one of the coordinate axis. This
2613 * function only calculates the q-position for the pairs of pixel numbers
2614 * (n1, n2) given in the input and should therefore be used only for
2615 * detector calibration purposes.
2616 *
2617 * Parameters
2618 * ----------
2619 * detectorAngles .. angular positions of the detector goniometer
2620 * (Npoints, Nd)
2621 * n1 .............. detector pixel numbers dim1 (Npoints)
2622 * n2 .............. detector pixel numbers dim2 (Npoints)
2623 * rcch ............ direction + distance of center pixel (angles zero)
2624 * detectorAxis .... string with detector axis directions
2625 * cch1 ............ center channel of the detector
2626 * cch2 ............ center channel of the detector
2627 * dpixel1 ......... width of one pixel in first direction, same unit as
2628 * distance rcch
2629 * dpixel2 ......... width of one pixel in second direction, same unit as
2630 * distance rcch
2631 * dir1 ............ first direction of the detector, e.g.: "x+"
2632 * dir2 ............ second direction of the detector, e.g.: "z+"
2633 * tiltazimuth ..... azimuth of the tilt
2634 * tilt ............ tilt of the detector plane (rotation around axis
2635 * normal to the direction given by the tiltazimuth
2636 * lambda .......... wavelength of the used x-rays
2637 * nthreads ........ number of threads to use in parallel section of
2638 * the code
2639 *
2640 * Returns
2641 * -------
2642 * qpos ............ momentum transfer (Npoints, 3)
2643 * */
2644 {
2645 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2646 double r_i[3]; /* r_i: center channel direction */
2647 int i, j, k; /* loop indices */
2648 int Nd; /* number of detector circles */
2649 int Npoints; /* number of angular positions */
2650 unsigned int nthreads; /* number of threads to use */
2651 /* x-ray wavelength, f = M_2PI / lambda and detector parameters */
2652 double f, lambda, cch1, cch2, dpixel1, dpixel2, tilt, tiltazimuth;
2653 char *detectorAxis, *dir1, *dir2; /* string with detector axis,
2654 * and detector direction */
2655 double *detectorAngles, *n1, *n2, *rcch, *qpos; /* c-arrays */
2656 fp_rot *detectorRotation;
2657 npy_intp nout[2];
2658
2659 PyArrayObject *detectorAnglesArr = NULL, *n1Arr = NULL, *n2Arr = NULL,
2660 *rcchArr = NULL, *qposArr = NULL; /* numpy arrays */
2661
2662 /* Python argument conversion code */
2663 if (!PyArg_ParseTuple(args, "O!O!O!O!sddddssdddI",
2664 &PyArray_Type, &detectorAnglesArr,
2665 &PyArray_Type, &n1Arr,
2666 &PyArray_Type, &n2Arr,
2667 &PyArray_Type, &rcchArr,
2668 &detectorAxis, &cch1, &cch2, &dpixel1, &dpixel2,
2669 &dir1, &dir2, &tiltazimuth, &tilt,
2670 &lambda, &nthreads)) {
2671 return NULL;
2672 }
2673
2674 /* check Python array dimensions and types */
2675 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
2676 "detectorAngles must be a 2D double array");
2677 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE,
2678 "rcch must be a 1D double array");
2679 if (PyArray_SIZE(rcchArr) != 3) {
2680 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
2681 return NULL;
2682 }
2683 PYARRAY_CHECK(n1Arr, 1, NPY_DOUBLE, "n1 must be a 1D double array");
2684 PYARRAY_CHECK(n2Arr, 1, NPY_DOUBLE, "n2 must be a 1D double array");
2685
2686 Npoints = (int) PyArray_DIMS(detectorAnglesArr)[0];
2687 if (PyArray_SIZE(n1Arr) != Npoints || PyArray_SIZE(n2Arr) != Npoints) {
2688 PyErr_SetString(PyExc_ValueError, "n1, n2 must be of length Npoints");
2689 return NULL;
2690 }
2691 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
2692
2693 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
2694 rcch = (double *) PyArray_DATA(rcchArr);
2695 n1 = (double *) PyArray_DATA(n1Arr);
2696 n2 = (double *) PyArray_DATA(n2Arr);
2697
2698 /* derived values from input parameters */
2699 f = M_2PI / lambda;
2700
2701 /* create output ndarray */
2702 nout[0] = Npoints;
2703 nout[1] = 3;
2704 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
2705 qpos = (double *) PyArray_DATA(qposArr);
2706
2707 #ifdef __OPENMP__
2708 /* set openmp thread numbers dynamically */
2709 OMPSETNUMTHREADS(nthreads);
2710 #endif
2711
2712 /* arrays with function pointers to rotation matrix functions */
2713 detectorRotation = (fp_rot*) malloc(Nd * sizeof(fp_rot));
2714
2715 /* determine axes directions */
2716 if (determine_axes_directions_apply(detectorRotation,
2717 detectorAxis, Nd) != 0) {
2718 return NULL;
2719 }
2720
2721 veccopy(r_i, rcch);
2722 normalize(r_i);
2723
2724 /* determine detector pixel vector */
2725 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2726 return NULL;
2727 }
2728 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2729 return NULL;
2730 }
2731
2732 /* rotate detector pixel vectors according to tilt */
2733 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2734
2735 /* calculate center channel position in detector plane */
2736 for (k = 0; k < 3; ++k) {
2737 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2738 }
2739
2740 /* calculate rotation matices and perform rotations */
2741 #pragma omp parallel for default(shared) \
2742 private(i, j, k, rd) \
2743 schedule(static)
2744 for (i = 0; i < Npoints; ++i) {
2745 /* calculate momentum transfer for the detector pixel n1[i], n2[i] */
2746 for (k = 0; k < 3; ++k) {
2747 rd[k] = n1[i] * rpixel1[k] + n2[i] * rpixel2[k] - rcchp[k];
2748 }
2749 sumvec(rd, rcch);
2750 /* apply detector rotations/translations */
2751 for (j = 0; j < Nd; ++j) {
2752 detectorRotation[j](detectorAngles[Nd * i + j], rd);
2753 }
2754 normalize(rd);
2755 /* rd contains detector pixel direction,
2756 * r_i contains primary beam direction */
2757 diffvec(rd, r_i);
2758 vecmul(rd, f);
2759 /* save momentum transfer to output */
2760 veccopy(&qpos[3 * i], rd);
2761 }
2762
2763 /* clean up */
2764 Py_DECREF(detectorAnglesArr);
2765 Py_DECREF(n1Arr);
2766 Py_DECREF(n2Arr);
2767 Py_DECREF(rcchArr);
2768
2769 /* return output array */
2770 return PyArray_Return(qposArr);
2771 }
2772
2773
2774 PyObject* ang2q_conversion_area_pixel2(PyObject *self, PyObject *args)
2775 /* conversion of Npoints of detector positions to Q
2776 * for an area detector with a given pixel size mounted along one of
2777 * the coordinate axis. This function only calculates the q-position for the
2778 * pairs of pixel numbers (n1, n2) given in the input and should therefore
2779 * be used only for detector calibration purposes.
2780 *
2781 * This variant of this function also takes a sample orientation matrix as
2782 * well as the sample goniometer as input to allow for a simultaneous fit
2783 * of reference samples orientation
2784 *
2785 * Interface:
2786 * sampleAngles .... angular positions of the sample goniometer
2787 * (Npoints, Ns)
2788 * detectorAngles .. angular positions of the detector goniometer
2789 * (Npoints, Nd)
2790 * n1 .............. detector pixel numbers dim1 (Npoints)
2791 * n2 .............. detector pixel numbers dim2 (Npoints)
2792 * rcch ............ direction + distance of center pixel (angles zero)
2793 * sampleAxis ...... string with sample axis directions
2794 * detectorAxis .... string with detector axis directions
2795 * cch1 ............ center channel of the detector
2796 * cch2 ............ center channel of the detector
2797 * dpixel1 ......... width of one pixel in first direction, same unit as
2798 * distance rcch
2799 * dpixel2 ......... width of one pixel in second direction, same unit as
2800 * distance rcch
2801 * dir1 ............ first direction of the detector, e.g.: "x+"
2802 * dir2 ............ second direction of the detector, e.g.: "z+"
2803 * tiltazimuth ..... azimuth of the tilt
2804 * tilt ............ tilt of the detector plane (rotation around axis
2805 * normal to the direction given by the tiltazimuth
2806 * UB .............. orientation matrix and reciprocal space conversion
2807 * of the investigated crystal (3, 3)
2808 * lambda .......... wavelength of the used x-rays
2809 * nthreads ........ number of threads to use in parallel section of the
2810 * code
2811 *
2812 * Returns
2813 * -------
2814 * qpos ............ momentum transfer (Npoints, 3)
2815 * */
2816 {
2817 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
2818 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2819 double r_i[3]; /* r_i: center channel direction */
2820 int i, j, k; /* loop indices */
2821 int Ns, Nd; /* number of sample / detector circles */
2822 int Npoints; /* number of angular positions */
2823 unsigned int nthreads; /* number of threads to use */
2824 /* x-ray wavelength, f = M_2PI / lambda and detector parameters */
2825 double f, lambda, cch1, cch2, dpixel1, dpixel2, tilt, tiltazimuth;
2826 /* string with sample and detector axis, and detector direction */
2827 char *sampleAxis, *detectorAxis, *dir1, *dir2;
2828 /* c-arrays */
2829 double *sampleAngles, *detectorAngles, *n1, *n2, *rcch, *UB, *qpos;
2830 fp_rot *sampleRotation;
2831 fp_rot *detectorRotation;
2832 npy_intp nout[2];
2833
2834 PyArrayObject *sampleAnglesArr = NULL, *detectorAnglesArr = NULL,
2835 *n1Arr = NULL, *n2Arr = NULL, *rcchArr = NULL,
2836 *UBArr = NULL, *qposArr = NULL; /* numpy arrays */
2837
2838 /* Python argument conversion code */
2839 if (!PyArg_ParseTuple(args, "O!O!O!O!O!ssddddssddO!dI",
2840 &PyArray_Type, &sampleAnglesArr,
2841 &PyArray_Type, &detectorAnglesArr,
2842 &PyArray_Type, &n1Arr, &PyArray_Type, &n2Arr,
2843 &PyArray_Type, &rcchArr,
2844 &sampleAxis, &detectorAxis, &cch1, &cch2,
2845 &dpixel1, &dpixel2, &dir1, &dir2, &tiltazimuth,
2846 &tilt, &PyArray_Type, &UBArr,
2847 &lambda, &nthreads)) {
2848 return NULL;
2849 }
2850
2851 /* check Python array dimensions and types */
2852 PYARRAY_CHECK(sampleAnglesArr, 2, NPY_DOUBLE,
2853 "sampleAngles must be a 2D double array");
2854 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
2855 "detectorAngles must be a 2D double array");
2856 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE,
2857 "rcch must be a 1D double array");
2858 if (PyArray_SIZE(rcchArr) != 3) {
2859 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
2860 return NULL;
2861 }
2862 PYARRAY_CHECK(UBArr, 2, NPY_DOUBLE, "UB must be a 2D double array");
2863 if (PyArray_DIMS(UBArr)[0] != 3 || PyArray_DIMS(UBArr)[1] != 3) {
2864 PyErr_SetString(PyExc_ValueError, "UB must be of shape (3, 3)");
2865 return NULL;
2866 }
2867 PYARRAY_CHECK(n1Arr, 1, NPY_DOUBLE, "n1 must be a 1D double array");
2868 PYARRAY_CHECK(n2Arr, 1, NPY_DOUBLE, "n2 must be a 1D double array");
2869
2870 Npoints = (int) PyArray_DIMS(detectorAnglesArr)[0];
2871 if (PyArray_SIZE(n1Arr) != Npoints || PyArray_SIZE(n2Arr) != Npoints) {
2872 PyErr_SetString(PyExc_ValueError, "n1, n2 must be of length Npoints");
2873 return NULL;
2874 }
2875 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
2876 Ns = (int) PyArray_DIMS(sampleAnglesArr)[1];
2877 if (PyArray_DIMS(sampleAnglesArr)[0] != Npoints) {
2878 PyErr_SetString(PyExc_ValueError,
2879 "detectorAngles and sampleAngles must have same first dimension");
2880 return NULL;
2881 }
2882
2883 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
2884 sampleAngles = (double *) PyArray_DATA(sampleAnglesArr);
2885 rcch = (double *) PyArray_DATA(rcchArr);
2886 UB = (double *) PyArray_DATA(UBArr);
2887 n1 = (double *) PyArray_DATA(n1Arr);
2888 n2 = (double *) PyArray_DATA(n2Arr);
2889
2890 /* derived values from input parameters */
2891 f = M_2PI / lambda;
2892
2893 /* create output ndarray */
2894 nout[0] = Npoints;
2895 nout[1] = 3;
2896 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
2897 qpos = (double *) PyArray_DATA(qposArr);
2898
2899 #ifdef __OPENMP__
2900 /* set openmp thread numbers dynamically */
2901 OMPSETNUMTHREADS(nthreads);
2902 #endif
2903
2904 /* arrays with function pointers to rotation matrix functions */
2905 sampleRotation = (fp_rot*) malloc(Ns * sizeof(fp_rot));
2906 detectorRotation = (fp_rot*) malloc(Nd * sizeof(fp_rot));
2907
2908
2909 /* determine axes directions */
2910 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
2911 return NULL;
2912 }
2913 if (determine_axes_directions_apply(detectorRotation,
2914 detectorAxis, Nd) != 0) {
2915 return NULL;
2916 }
2917
2918 veccopy(r_i, rcch);
2919 normalize(r_i);
2920
2921 /* determine detector pixel vector */
2922 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2923 return NULL;
2924 }
2925 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2926 return NULL;
2927 }
2928
2929 /* rotate detector pixel vectors according to tilt */
2930 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2931
2932 /* calculate center channel position in detector plane */
2933 for (k = 0; k < 3; ++k) {
2934 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2935 }
2936
2937 /* calculate rotation matices and perform rotations */
2938 #pragma omp parallel for default(shared) \
2939 private(i, j, k, mtemp, mtemp2, ms, rd) \
2940 schedule(static)
2941 for (i = 0; i < Npoints; ++i) {
2942 /* determine sample rotations */
2943 ident(mtemp);
2944 for (j = 0; j < Ns; ++j) {
2945 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
2946 matmul(mtemp, mtemp2);
2947 }
2948 /* apply rotation of orientation matrix */
2949 matmul(mtemp, UB);
2950 /* determine inverse matrix */
2951 inversemat(mtemp, ms);
2952
2953 /* ms contains now the inverse rotation matrix for the sample circles
2954 * calculate the momentum transfer for a certain detector pixel */
2955 for (k = 0; k < 3; ++k) {
2956 rd[k] = n1[i] * rpixel1[k] + n2[i] * rpixel2[k] - rcchp[k];
2957 }
2958 sumvec(rd, rcch);
2959 /* apply detector rotations/translations */
2960 for (j = 0; j < Nd; ++j) {
2961 detectorRotation[j](detectorAngles[Nd * i + j], rd);
2962 }
2963 normalize(rd);
2964 /* rd contains detector pixel direction,
2965 * r_i contains primary beam direction */
2966 diffvec(rd, r_i);
2967 vecmul(rd, f);
2968 /* determine momentum transfer */
2969 matvec(ms, rd, &qpos[3 * i]);
2970 }
2971
2972 /* clean up */
2973 Py_DECREF(detectorAnglesArr);
2974 Py_DECREF(n1Arr);
2975 Py_DECREF(n2Arr);
2976 Py_DECREF(rcchArr);
2977 Py_DECREF(sampleAnglesArr);
2978 Py_DECREF(UBArr);
2979
2980 /* return output array */
2981 return PyArray_Return(qposArr);
2982 }
2983
2984
2985 /* #################################################
2986 * detector position functions (incl. translations)
2987 * #################################################*/
2988
2989 PyObject* ang2q_detpos(PyObject *self, PyObject *args)
2990 /* conversion of Npoints of detector angles positions to vectorial position
2991 * of the detector in real space for a setup with point detector and
2992 * possible detector translations
2993 *
2994 * Parameters
2995 * ----------
2996 * detectorAngles. angular positions of the detector goniometer
2997 * (Npoints, Nd)
2998 * ri ............ direction of primary beam (length specifies distance
2999 * of the detector)
3000 * detectorAxis .. string with detector axis directions
3001 * nthreads ...... number of threads to use in parallel section of the
3002 * code
3003 *
3004 * Returns
3005 * -------
3006 * dpos .......... real space detector position (Npoints, 3)
3007 *
3008 * */
3009 {
3010 double rd[3]; /* local detector direction */
3011 int i, j; /* needed indices */
3012 int Nd; /* number of detector circles */
3013 int Npoints; /* number of angular positions */
3014 unsigned int nthreads; /* number of threads to use */
3015 char *detectorAxis; /* str with sample and detector axis */
3016 /* c-array pointers for further usage */
3017 double *detectorAngles, *ri, *qpos;
3018 npy_intp nout[2];
3019 /* arrays with function pointers to rotation matrix functions */
3020 fp_rot *detectorRotation;
3021
3022 /* numpy arrays */
3023 PyArrayObject *detectorAnglesArr = NULL, *riArr = NULL, *qposArr = NULL;
3024
3025 /* Python argument conversion code */
3026 if (!PyArg_ParseTuple(args, "O!O!sI",
3027 &PyArray_Type, &detectorAnglesArr,
3028 &PyArray_Type, &riArr,
3029 &detectorAxis,
3030 &nthreads)) {
3031 return NULL;
3032 }
3033
3034 /* check Python array dimensions and types */
3035 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
3036 "detectorAngles must be a 2D double array");
3037 PYARRAY_CHECK(riArr, 1, NPY_DOUBLE,
3038 "r_i must be a 1D double array");
3039 if (PyArray_SIZE(riArr) != 3) {
3040 PyErr_SetString(PyExc_ValueError, "r_i needs to be of length 3");
3041 return NULL;
3042 }
3043
3044 Npoints = (int) PyArray_DIMS(detectorAnglesArr)[0];
3045 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
3046
3047 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
3048 ri = (double *) PyArray_DATA(riArr);
3049
3050 /* create output ndarray */
3051 nout[0] = Npoints;
3052 nout[1] = 3;
3053 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
3054 qpos = (double *) PyArray_DATA(qposArr);
3055
3056 #ifdef __OPENMP__
3057 /* set openmp thread numbers dynamically */
3058 OMPSETNUMTHREADS(nthreads);
3059 #endif
3060
3061 /* arrays with function pointers to rotation matrix functions */
3062 detectorRotation = (fp_rot*) malloc(Nd * sizeof(fp_rot));
3063
3064 /* determine axes directions */
3065 if (determine_axes_directions_apply(detectorRotation,
3066 detectorAxis, Nd) != 0) {
3067 return NULL;
3068 }
3069
3070 /* calculate rotation matices and perform rotations */
3071 #pragma omp parallel for default(shared) \
3072 private(i, j, rd) schedule(static)
3073 for (i = 0; i < Npoints; ++i) {
3074 /* determine detector rotations */
3075 veccopy(rd, ri);
3076 for (j = Nd - 1; j >= 0; --j) {
3077 detectorRotation[j](detectorAngles[Nd * i + j], rd);
3078 }
3079 veccopy(&qpos[3 * i], rd);
3080 }
3081
3082 /* clean up */
3083 Py_DECREF(detectorAnglesArr);
3084 Py_DECREF(riArr);
3085
3086 /* return output array */
3087 return PyArray_Return(qposArr);
3088 }
3089
3090
3091 PyObject* ang2q_detpos_linear(PyObject *self, PyObject *args)
3092 /* conversion of Npoints of detector angles to real space detector
3093 * positions for a linear detector with a given pixel size mounted
3094 * along one of the coordinate axis, and translation motors on the
3095 * detector arm
3096 *
3097 * Parameters
3098 * ----------
3099 * detectorAngles .. angular positions of the detector goniometer
3100 * (Npoints, Nd)
3101 * rcch ............ direction + distance of center channel (angles zero)
3102 * detectorAxis .... string with detector axis directions
3103 * cch ............. center channel of the detector
3104 * dpixel .......... width of one pixel, same unit as distance rcch
3105 * roi ............. region of interest of the detector
3106 * dir ............. direction of the detector, e.g.: "x+"
3107 * tilt ............ tilt of the detector direction from dir
3108 * nthreads ........ number of threads to use in parallel section of the
3109 * code
3110 *
3111 * Returns
3112 * -------
3113 * dpos ............ real space detector position (Npoints * Nch, 3)
3114 * */
3115 {
3116 double rd[3], rpixel[3], rcchp[3]; /* detector position */
3117 int i, j, k; /* needed indices */
3118 int Nd; /* number of detector circles */
3119 int Npoints; /* number of angular positions */
3120 int Nch; /* number of channels in region of interest */
3121 unsigned int nthreads; /* number of threads to use */
3122 double cch, dpixel, tilt; /* detector parameters */
3123 char *detectorAxis, *dir; /* string with detector axis, and detector
3124 * direction */
3125 double *detectorAngles, *rcch, *qpos;
3126 int *roi; /* region of interest integer array */
3127 npy_intp nout[2];
3128 fp_rot *detectorRotation;
3129
3130 /* numpy arrays */
3131 PyArrayObject *detectorAnglesArr = NULL, *rcchArr = NULL,
3132 *roiArr = NULL, *qposArr = NULL;
3133
3134 /* Python argument conversion code */
3135 if (!PyArg_ParseTuple(args, "O!O!sddO!sdI",
3136 &PyArray_Type, &detectorAnglesArr,
3137 &PyArray_Type, &rcchArr,
3138 &detectorAxis,
3139 &cch, &dpixel, &PyArray_Type, &roiArr,
3140 &dir, &tilt, &nthreads)) {
3141 return NULL;
3142 }
3143
3144 /* check Python array dimensions and types */
3145 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
3146 "detectorAngles must be a 2D double array");
3147 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE,
3148 "rcch must be a 1D double array");
3149 if (PyArray_SIZE(rcchArr) != 3) {
3150 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
3151 return NULL;
3152 }
3153 PYARRAY_CHECK(roiArr, 1, NPY_INT32, "roi must be a 1D int array");
3154 if (PyArray_SIZE(roiArr) != 2) {
3155 PyErr_SetString(PyExc_ValueError, "roi must be of length 2");
3156 return NULL;
3157 }
3158
3159 Npoints = (int) PyArray_DIMS(detectorAnglesArr)[0];
3160 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
3161
3162 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
3163 rcch = (double *) PyArray_DATA(rcchArr);
3164 roi = (int *) PyArray_DATA(roiArr);
3165
3166 /* derived values from input parameters */
3167 Nch = roi[1] - roi[0]; /* number of channels */
3168
3169 /* create output ndarray */
3170 nout[0] = Npoints * Nch;
3171 nout[1] = 3;
3172 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
3173 qpos = (double *) PyArray_DATA(qposArr);
3174
3175 #ifdef __OPENMP__
3176 /* set openmp thread numbers dynamically */
3177 OMPSETNUMTHREADS(nthreads);
3178 #endif
3179
3180 /* arrays with function pointers to rotation matrix functions */
3181 detectorRotation = (fp_rot*) malloc(Nd * sizeof(fp_rot));
3182
3183 /* determine axes directions */
3184 if (determine_axes_directions_apply(detectorRotation,
3185 detectorAxis, Nd) != 0) {
3186 return NULL;
3187 }
3188
3189 /* determine detector pixel vector */
3190 if (determine_detector_pixel(rpixel, dir, dpixel, rcch, tilt) != 0) {
3191 return NULL;
3192 }
3193 for (k = 0; k < 3; ++k) {
3194 rcchp[k] = rpixel[k] * cch;
3195 }
3196
3197 /* calculate rotation matices and perform rotations */
3198 #pragma omp parallel for default(shared) \
3199 private(i, j, k, rd) schedule(static)
3200 for (i = 0; i < Npoints; ++i) {
3201 for (j = roi[0]; j < roi[1]; ++j) {
3202 for (k = 0; k < 3; ++k) {
3203 rd[k] = j * rpixel[k] - rcchp[k];
3204 }
3205 sumvec(rd, rcch);
3206 /* determine detector rotations */
3207 for (k = Nd - 1; k >= 0; --k) {
3208 detectorRotation[k](detectorAngles[Nd * i + k], rd);
3209 }
3210
3211 veccopy(&qpos[3 * (i * Nch + j - roi[0])], rd);
3212 }
3213 }
3214
3215 /* clean up */
3216 Py_DECREF(detectorAnglesArr);
3217 Py_DECREF(rcchArr);
3218 Py_DECREF(roiArr);
3219
3220 /* return output array */
3221 return PyArray_Return(qposArr);
3222 }
3223
3224
3225 PyObject* ang2q_detpos_area(PyObject *self, PyObject *args)
3226 /* conversion of Npoints of detector arm angles to real space position
3227 * of the detector for an area detector with a given pixel size
3228 * mounted along one of the coordinate axis including translation axis
3229 * on the detector arm
3230 *
3231 * Parameters
3232 * ----------
3233 * detectorAngles .. angular positions of the detector goniometer
3234 * (Npoints, Nd)
3235 * rcch ............ direction + distance of center pixel (angles zero)
3236 * detectorAxis .... string with detector axis directions
3237 * cch1 ............ center channel of the detector
3238 * cch2 ............ center channel of the detector
3239 * dpixel1 ......... width of one pixel in first direction, same unit as
3240 * distance rcch
3241 * dpixel2 ......... width of one pixel in second direction, same unit as
3242 * distance rcch
3243 * roi ............. region of interest for the area detector
3244 * [dir1min, dir1max, dir2min, dir2max]
3245 * dir1 ............ first direction of the detector, e.g.: "x+"
3246 * dir2 ............ second direction of the detector, e.g.: "z+"
3247 * tiltazimuth ..... azimuth of the tilt
3248 * tilt ............ tilt of the detector plane (rotation around axis
3249 * normal to the direction
3250 * given by the tiltazimuth
3251 * nthreads ........ number of threads to use in parallelization
3252 *
3253 * Returns
3254 * -------
3255 * dpos ............ detector position vector (Npoints * Npix1 * Npix2, 3)
3256 * */
3257 {
3258 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
3259 int i, j, j1, j2, k; /* loop indices */
3260 int idxh1, idxh2; /* temporary index helper */
3261 int Nd; /* number of sample and detector circles */
3262 int Npoints; /* number of angular positions */
3263 unsigned int nthreads; /* number threads for OpenMP */
3264 /* detector parameters */
3265 double cch1, cch2, dpixel1, dpixel2, tilt, tiltazimuth;
3266 /* string with detector axis, and detector direction */
3267 char *detectorAxis, *dir1, *dir2;
3268 double *detectorAngles, *rcch, *qpos;
3269 int *roi; /* region of interest integer array */
3270 fp_rot *detectorRotation;
3271 npy_intp nout[2];
3272
3273 /* numpy arrays */
3274 PyArrayObject *detectorAnglesArr = NULL, *rcchArr = NULL,
3275 *roiArr = NULL, *qposArr = NULL;
3276
3277 /* Python argument conversion code */
3278 if (!PyArg_ParseTuple(args, "O!O!sddddO!ssddI",
3279 &PyArray_Type, &detectorAnglesArr,
3280 &PyArray_Type, &rcchArr,
3281 &detectorAxis,
3282 &cch1, &cch2, &dpixel1, &dpixel2,
3283 &PyArray_Type, &roiArr,
3284 &dir1, &dir2, &tiltazimuth, &tilt,
3285 &nthreads)) {
3286 return NULL;
3287 }
3288
3289 /* check Python array dimensions and types */
3290 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
3291 "detectorAngles must be a 2D double array");
3292 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE,
3293 "rcch must be a 1D double array");
3294 if (PyArray_SIZE(rcchArr) != 3) {
3295 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
3296 return NULL;
3297 }
3298 PYARRAY_CHECK(roiArr, 1, NPY_INT32, "roi must be a 1D int array");
3299 if (PyArray_SIZE(roiArr) != 4) {
3300 PyErr_SetString(PyExc_ValueError, "roi must be of length 4");
3301 return NULL;
3302 }
3303
3304 Npoints = (int) PyArray_DIMS(detectorAnglesArr)[0];
3305 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
3306
3307 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
3308 rcch = (double *) PyArray_DATA(rcchArr);
3309 roi = (int *) PyArray_DATA(roiArr);
3310
3311 /* calculate some index shortcuts */
3312 idxh1 = (roi[1] - roi[0]) * (roi[3] - roi[2]);
3313 idxh2 = roi[3] - roi[2];
3314
3315 /* create output ndarray */
3316 nout[0] = Npoints * idxh1;
3317 nout[1] = 3;
3318 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
3319 qpos = (double *) PyArray_DATA(qposArr);
3320
3321 #ifdef __OPENMP__
3322 /* set openmp thread numbers dynamically */
3323 OMPSETNUMTHREADS(nthreads);
3324 #endif
3325
3326 /* arrays with function pointers to rotation matrix functions */
3327 detectorRotation = (fp_rot*) malloc(Nd * sizeof(fp_rot));
3328
3329 /* determine axes directions */
3330 if (determine_axes_directions_apply(detectorRotation,
3331 detectorAxis, Nd) != 0) {
3332 return NULL;
3333 }
3334
3335 /* determine detector pixel vector */
3336 if (determine_detector_pixel(rpixel1, dir1, dpixel1, rcch, 0.) != 0) {
3337 return NULL;
3338 }
3339 if (determine_detector_pixel(rpixel2, dir2, dpixel2, rcch, 0.) != 0) {
3340 return NULL;
3341 }
3342
3343 /* rotate detector pixel vectors according to tilt */
3344 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
3345
3346 /* calculate center channel position in detector plane */
3347 for (k = 0; k < 3; ++k) {
3348 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
3349 }
3350
3351 /* calculate rotation matices and perform rotations */
3352 #pragma omp parallel for default(shared) \
3353 private(i, j, j1, j2, k, rd) schedule(static)
3354 for (i = 0; i < Npoints; ++i) {
3355 for (j1 = roi[0]; j1 < roi[1]; ++j1) {
3356 for (j2 = roi[2]; j2 < roi[3]; ++j2) {
3357 for (k = 0; k < 3; ++k) {
3358 rd[k] = j1 * rpixel1[k] + j2 * rpixel2[k] - rcchp[k];
3359 }
3360 sumvec(rd, rcch);
3361 /* apply detector rotations/translations, starting with the
3362 * inner most */
3363 for (j = Nd - 1; j >= 0; --j) {
3364 detectorRotation[j](detectorAngles[Nd * i + j], rd);
3365 }
3366
3367 veccopy(&qpos[3 * (i * idxh1 + idxh2 * (j1 - roi[0]) +
3368 (j2 - roi[2]))], rd);
3369 }
3370 }
3371 }
3372
3373 /* clean up */
3374 Py_DECREF(detectorAnglesArr);
3375 Py_DECREF(rcchArr);
3376 Py_DECREF(roiArr);
3377
3378 /* return output array */
3379 return PyArray_Return(qposArr);
3380 }
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
17 */
18 #pragma once
19
20 #include "xrayutilities.h"
21
22 #define cdeg2rad (M_PI / 180.)
23 #define crad2deg (180. / M_PI)
24
25 #define deg2rad(ang) (ang * cdeg2rad)
26 #define rad2deg(rad) (rad * crad2deg)
27
28 /* define flags for the qconversion functions */
29 #define HAS_TRANSLATIONS 1
30 #define HAS_SAMPLEDIS 4
31 #define VERBOSE 16
32
33 /* ###################################
34 * matrix vector operations for
35 * 3x3 matrices and vectors of length
36 * 3
37 * ################################### */
38
39 INLINE void ident(double *m);
40
41 INLINE void sumvec(double *RESTRICT v1, double *RESTRICT v2);
42
43 INLINE void diffvec(double *RESTRICT v1, double *RESTRICT v2);
44
45 INLINE double norm(double *v);
46
47 INLINE void normalize(double *v);
48
49 INLINE void veccopy(double *RESTRICT v1, double *RESTRICT v2);
50
51 INLINE void vecmul(double *RESTRICT r, double a);
52
53 INLINE void cross(double *RESTRICT v1, double *RESTRICT v2,
54 double *RESTRICT r);
55
56 INLINE void vecmatcross(double *RESTRICT v, double *RESTRICT m,
57 double *RESTRICT mr);
58
59 INLINE void matmul(double *RESTRICT m1, double *RESTRICT m2);
60
61 INLINE void matmulc(double *RESTRICT m, double c);
62
63 INLINE void matvec(double *RESTRICT m, double *RESTRICT v,
64 double *RESTRICT r);
65
66 INLINE void tensorprod(double *RESTRICT v1, double *RESTRICT v2,
67 double *RESTRICT m);
68
69 INLINE void summat(double *RESTRICT m1, double *RESTRICT m2);
70
71 INLINE void diffmat(double *RESTRICT m1, double *RESTRICT m2);
72
73 INLINE void inversemat(double *RESTRICT m, double *RESTRICT i);
74
75 INLINE double determinant(double *RESTRICT m);
76
77
78 /*##############################################
79 # functions which implement rotation matrices
80 # for all coordinate axes and rotation senses
81 #
82 # the routines expect angles in radians
83 # for conversion from degrees to radians
84 # the functions and2rad and rad2ang are
85 # supplied
86 ################################################*/
87
88 typedef void (*fp_rot)(double, double *);
89
90 INLINE void rotation_xp(double a, double *mat);
91 INLINE void rotation_yp(double a, double *mat);
92 INLINE void rotation_zp(double a, double *mat);
93
94 INLINE void rotation_xm(double a, double *mat);
95 INLINE void rotation_ym(double a, double *mat);
96 INLINE void rotation_zm(double a, double *mat);
97
98 INLINE void rotation_kappa(double a, double *mat);
99
100 INLINE void rotation_arb(double a, double *RESTRICT e, double *RESTRICT mat);
101
102 INLINE void apply_xp(double a, double *vec);
103 INLINE void apply_yp(double a, double *vec);
104 INLINE void apply_zp(double a, double *vec);
105
106 INLINE void apply_xm(double a, double *vec);
107 INLINE void apply_ym(double a, double *vec);
108 INLINE void apply_zm(double a, double *vec);
109
110 INLINE void apply_tx(double x, double *vec);
111 INLINE void apply_ty(double y, double *vec);
112 INLINE void apply_tz(double z, double *vec);
113
114 /*##############################################
115 # functions needed for reciprocal space converions
116 ################################################*/
117
118 int determine_axes_directions(fp_rot *fp_circles, char *stringAxis,
119 unsigned int n);
120
121 int determine_axes_directions_apply(fp_rot *fp_circles, char *stringAxis,
122 unsigned int n);
123
124 int determine_detector_pixel(double *rpixel, char *dir, double dpixel,
125 double *r_i, double tilt);
126
127 int tilt_detector_axis(double tiltazimuth, double tilt,
128 double *RESTRICT rpixel1, double *RESTRICT rpixel2);
129
130 int print_matrix(double *m);
131 int print_vector(double *m);
132
133 /*################################################
134 # reciprocal space converions worker functions
135 # point detector
136 ##################################################*/
137
138 int ang2q_conversion(
139 double *sampleAngles, double *detectorAngles,
140 double *ri, char *sampleAxis, char *detectorAxis,
141 double *kappadir, double *UB, double *lambda,
142 int Npoints, int Ns, int Nd, int flags, double *qpos);
143
144 int ang2q_conversion_sd(
145 double *sampleAngles, double *detectorAngles, double *ri,
146 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
147 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
148 int flags, double *qpos);
149
150 int ang2q_conversion_trans(
151 double *sampleAngles, double *detectorAngles, double *ri,
152 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
153 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos);
154
155 int ang2q_conversion_sdtrans(
156 double *sampleAngles, double *detectorAngles, double *ri,
157 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
158 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
159 int flags, double *qpos);
160
161 /*################################################
162 # reciprocal space converions worker functions
163 # linear detector
164 ##################################################*/
165
166 int ang2q_conversion_linear(
167 double *sampleAngles, double *detectorAngles, double *rcch,
168 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
169 double dpixel, int *roi, char *dir, double tilt, double *UB,
170 double *lambda, int Npoints, int Ns, int Nd, int Nch,
171 int flags, double *qpos);
172
173 int ang2q_conversion_linear_sd(
174 double *sampleAngles, double *detectorAngles, double *rcch,
175 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
176 double dpixel, int *roi, char *dir, double tilt, double *UB,
177 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
178 int Nch, int flags, double *qpos);
179
180 int ang2q_conversion_linear_trans(
181 double *sampleAngles, double *detectorAngles, double *rcch,
182 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
183 double dpixel, int *roi, char *dir, double tilt, double *UB,
184 double *lambda, int Npoints, int Ns, int Nd, int Nch,
185 int flags, double *qpos);
186
187 int ang2q_conversion_linear_sdtrans(double *sampleAngles, double *detectorAngles, double *rcch,
188 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
189 double dpixel, int *roi, char *dir, double tilt, double *UB,
190 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
191 int Nch, int flags, double *qpos);
192
193 /*################################################
194 # reciprocal space converions worker functions
195 # area detector
196 ##################################################*/
197
198 int ang2q_conversion_area(
199 double *sampleAngles, double *detectorAngles, double *rcch,
200 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
201 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
202 char *dir2, double tiltazimuth, double tilt, double *UB,
203 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos);
204
205 int ang2q_conversion_area_sd(
206 double *sampleAngles, double *detectorAngles, double *rcch,
207 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
208 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
209 char *dir2, double tiltazimuth, double tilt, double *UB,
210 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
211 int flags, double *qpos);
212
213 int ang2q_conversion_area_trans(
214 double *sampleAngles, double *detectorAngles, double *rcch,
215 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
216 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
217 char *dir2, double tiltazimuth, double tilt, double *UB,
218 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos);
219
220 int ang2q_conversion_area_sdtrans(
221 double *sampleAngles, double *detectorAngles, double *rcch,
222 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
223 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
224 char *dir2, double tiltazimuth, double tilt, double *UB,
225 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
226 int flags, double *qpos);
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2014 Eugen Wintersberger <eugen.wintersberger@gmail.com>
17 */
18 #pragma once
19
20 #define PY_SSIZE_T_CLEAN
21 #include <Python.h>
22 #include <math.h>
23
24 /*****************************************************************************
25 * NUMPY specific macros and header files
26 ****************************************************************************/
27 /*
28 * need to make some definitions before loading the arrayobject.h
29 * header file
30 */
31 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
32 #define PY_ARRAY_UNIQUE_SYMBOL XU_UNIQUE_SYMBOL
33 #define NO_IMPORT_ARRAY
34 #include <numpy/arrayobject.h>
35
36 /*
37 * set numpy API specific macros
38 */
39 #if NPY_FEATURE_VERSION < 0x00000007
40 #define NPY_ARRAY_ALIGNED NPY_ALIGNED
41 #define NPY_ARRAY_C_CONTIGUOUS NPY_C_CONTIGUOUS
42 #endif
43
44 /*
45 * define a macro to check a numpy array. This should mabye go into a
46 * function in future.
47 */
48 #define PYARRAY_CHECK(array, dims, type, msg) \
49 array = (PyArrayObject *) PyArray_FROM_OTF((PyObject *) array, \
50 type, \
51 NPY_ARRAY_C_CONTIGUOUS | \
52 NPY_ARRAY_ALIGNED); \
53 if (PyArray_NDIM(array) != dims || \
54 PyArray_TYPE(array) != type) {\
55 PyErr_SetString(PyExc_ValueError, msg); \
56 return NULL; \
57 }
58
59
60 /*****************************************************************************
61 * Windows build related macros
62 ****************************************************************************/
63 /* 'extern inline' seems to work only on newer version of gcc (>4.6 tested)
64 * gcc 4.1 seems to need this empty, i am not sure if there is a speed gain
65 * by inlining since the calls to those functions are anyhow built dynamically
66 * for compatibility keep this empty unless you can test with several compilers
67 */
68 #define INLINE
69 #ifdef _WIN32
70 #define RESTRICT
71 #else
72 #define RESTRICT restrict
73 #endif
74
75
76 /*
77 * some stuff we need for the Windows build
78 */
79 #ifdef _WIN32
80 #ifndef __MINGW32__
81 #include <float.h>
82 #define isnan _isnan
83 #endif
84
85 #endif
86
87 /*****************************************************************************
88 * general purpose macros
89 ****************************************************************************/
90 /*
91 * if M_PI is not set we do this here
92 */
93 #ifndef M_PI
94 # define M_PI 3.14159265358979323846
95 #endif
96 #define M_2PI (2 * M_PI)
97
98
99 /*****************************************************************************
100 * OpenMP related macros
101 ****************************************************************************/
102 /*
103 * include OpenMP header is required
104 */
105 #ifdef __OPENMP__
106 #include <omp.h>
107 #endif
108
109 #define OMPSETNUMTHREADS(nth) \
110 if (nth == 0) omp_set_num_threads(omp_get_max_threads());\
111 else omp_set_num_threads(nth);
44
55 python setup.py test
66
7 or
7 or
88
99 python -m unittest discover
1010
1919
2020 To run specific tests (file parsers) additional test data files are needed.
2121 If those files are not present the tests are skipped. Because of their size
22 they are shipped seperately and can be downloaded at
22 they are shipped seperately and can be downloaded at
2323 https://sourceforge.net/projects/xrayutilities/files/
2424
2525 The latest 'testdata' tarball has to be unpacked and placed in the
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import os
18 import subprocess
19 import sys
20 import tempfile
21 import unittest
22
23
24 scriptdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..',
25 'examples')
26 scriptfiles = [
27 'simpack_powdermodel.py',
28 'simpack_xrd_AlGaAs.py',
29 'simpack_xrd_Darwin_AlGaAs.py',
30 'simpack_xrd_InAs_fitting.py',
31 'simpack_xrd_SiGe111.py',
32 'simpack_xrd_SiGe_asymmmetric.py',
33 'simpack_xrd_SiGe.py',
34 'simpack_xrd_SiGe_superlattice.py',
35 'simpack_xrr_diffuse.py',
36 'simpack_xrr_matrixmethod.py',
37 'simpack_xrr_SiO2_Ru_CoFe_IrMn_Al2O3.py',
38 'xrayutilities_angular2hkl_conversion.py',
39 # 'xrayutilities_ccd_parameter.py', # data file not included
40 'xrayutilities_components_of_the_structure_factor.py',
41 'xrayutilities_define_material.py',
42 'xrayutilities_energy_dependent_structure_factor.py',
43 # 'xrayutilities_example_plot_3D_ESRF_ID01.py', # data file not included
44 'xrayutilities_experiment_angle_calculation.py',
45 'xrayutilities_experiment_kappa.py',
46 'xrayutilities_experiment_Powder_example_Iron.py',
47 # 'xrayutilities_export_data2vtk.py', # needs vtk + data
48 'xrayutilities_fuzzygridding.py',
49 'xrayutilities_hotpixelkill_variant.py',
50 'xrayutilities_id01_functions.py',
51 'xrayutilities_io_cif_parser_bi2te3.py',
52 'xrayutilities_io_cif_parser.py',
53 # 'xrayutilities_io_pdcif_plot.py', # data file not included
54 # 'xrayutilities_kmap_example_ESRF.py', # data file not included
55 # 'xrayutilities_linear_detector_parameters.py', # data file not included
56 'xrayutilities_materials_Alloy_contentcalc.py',
57 'xrayutilities_math_fitting.py',
58 'xrayutilities_orientation_matrix.py',
59 'xrayutilities_peak_angles_beamtime.py',
60 # 'xrayutilities_polefigure.py', # basemap needed
61 'xrayutilities_q2ang_general.py',
62 'xrayutilities_read_panalytical.py',
63 # 'xrayutilities_read_seifert.py', # data file not included
64 'xrayutilities_read_spec.py',
65 'xrayutilities_reflection_strength.py',
66 'xrayutilities_show_reciprocal_space_plane.py',
67 ]
68
69
70 class TestExampleScriptsMeta(type):
71 def __new__(mcs, name, bases, dict):
72 def test_generator(scriptname):
73 def test(self):
74 with tempfile.TemporaryFile(mode='w') as fid:
75 env = os.environ.copy()
76 env['MPLBACKEND'] = 'agg'
77 cmd = [sys.executable, scriptname]
78 subprocess.run(cmd, env=env, cwd=scriptdir, stdout=fid,
79 check=True)
80 return test
81
82 for sf in scriptfiles:
83 test_name = 'test_%s' % os.path.splitext(sf)[0]
84 test = test_generator(sf)
85 dict[test_name] = test
86 return type.__new__(mcs, name, bases, dict)
87
88
89 @unittest.skipIf(sys.version_info < (3, 5),
90 "needs subprocess.run -> python 3.5 or newer")
91 class TestExampleScripts(unittest.TestCase, metaclass=TestExampleScriptsMeta):
92 pass
93
94
95 if __name__ == '__main__':
96 unittest.main()
5555 def test_savehdf5(self):
5656 with tempfile.NamedTemporaryFile(mode='w') as fid:
5757 self.cbffile.Save2HDF5(fid.name)
58 with h5py.File(fid.name) as h5f:
58 with h5py.File(fid.name, 'r') as h5f:
5959 h5d = h5f[list(h5f.keys())[0]]
6060 h5d = numpy.asarray(h5d)
6161 self.assertTrue(numpy.all(h5d == self.data))
6464 with tempfile.NamedTemporaryFile(mode='w') as fid:
6565 ed = xu.io.CBFDirectory(datadir, 'cbf')
6666 ed.Save2HDF5(fid.name)
67 with h5py.File(fid.name) as h5f:
67 with h5py.File(fid.name, 'r') as h5f:
6868 h5g = h5f[os.path.split(datadir)[-1]]
6969 h5d = h5g[list(h5g.keys())[0]]
7070 h5d = numpy.asarray(h5d)
5555 def test_savehdf5(self):
5656 with tempfile.NamedTemporaryFile(mode='w') as fid:
5757 self.edffile.Save2HDF5(fid.name)
58 with h5py.File(fid.name) as h5f:
58 with h5py.File(fid.name, 'r') as h5f:
5959 h5d = h5f[list(h5f.keys())[0]]
6060 h5d = numpy.asarray(h5d)
6161 self.assertTrue(numpy.all(h5d == self.data))
6464 with tempfile.NamedTemporaryFile(mode='w') as fid:
6565 ed = xu.io.EDFDirectory(datadir, 'edf.gz')
6666 ed.Save2HDF5(fid.name)
67 with h5py.File(fid.name) as h5f:
67 with h5py.File(fid.name, 'r') as h5f:
6868 h5g = h5f[os.path.split(datadir)[-1]]
6969 h5d = h5g[list(h5g.keys())[0]]
7070 h5d = numpy.asarray(h5d)
1212 # You should have received a copy of the GNU General Public License
1313 # along with this program; if not, see <http://www.gnu.org/licenses/>.
1414 #
15 # Copyright (C) 2015-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
15 # Copyright (C) 2015-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
1616
1717 import math
18 import os
19 import subprocess
20 import sys
21 import tempfile
2218 import unittest
23
24 import numpy
2519
2620 import xrayutilities as xu
2721
3024 @classmethod
3125 def setUpClass(cls):
3226 cls.el = xu.materials.elements.dummy
33 # test create database
34 if sys.version_info >= (3, 3):
35 with tempfile.NamedTemporaryFile(delete=False) as fid:
36 cls.dbfilename = fid.name
37 cmd = [sys.executable,
38 os.path.join(xu.__path__[0],
39 'materials/_create_database.py'),
40 cls.dbfilename,
41 os.path.join(os.path.dirname(__file__),
42 '../xrayutilities/materials/data')]
43 r = subprocess.call(cmd)
44 if r != 0:
45 raise Exception('Database creation failed!')
46 cls.db = xu.materials.DataBase(cls.dbfilename)
47 cls.db.Open()
48 cls.db.SetMaterial(cls.el.name)
49
50 @classmethod
51 def tearDownClass(cls):
52 if sys.version_info >= (3, 3):
53 try:
54 cls.db.Close()
55 os.remove(cls.dbfilename)
56 except OSError:
57 pass
5827
5928 def test_db_f0(self):
6029 f0 = self.el.f0(0)
61 self.assertAlmostEqual(f0, 1.0, places=10)
62
63 @unittest.skipIf(sys.version_info < (3, 3),
64 "needs the lzma module -> python 3.3 or newer")
65 def test_owndb_f0(self):
66 f0 = self.db.GetF0(0)
6730 self.assertAlmostEqual(f0, 1.0, places=10)
6831
6932 def test_db_f1_neg(self):
7033 f1 = self.el.f1(-1)
7134 self.assertTrue(math.isnan(f1))
7235
73 @unittest.skipIf(sys.version_info < (3, 3),
74 "needs the lzma module -> python 3.3 or newer")
75 def test_owndb_f1_neg(self):
76 f1 = self.db.GetF1(-1)
77 self.assertTrue(math.isnan(f1))
78
7936 def test_db_f1(self):
8037 f1 = self.el.f1(1000)
81 self.assertAlmostEqual(f1, 0.0, places=10)
82
83 @unittest.skipIf(sys.version_info < (3, 3),
84 "needs the lzma module -> python 3.3 or newer")
85 def test_owndb_f1(self):
86 f1 = self.db.GetF1(1000)
8738 self.assertAlmostEqual(f1, 0.0, places=10)
8839
8940 def test_db_f2_neg(self):
9041 f2 = self.el.f2(-1)
9142 self.assertTrue(math.isnan(f2))
9243
93 @unittest.skipIf(sys.version_info < (3, 3),
94 "needs the lzma module -> python 3.3 or newer")
95 def test_owndb_f2_neg(self):
96 f2 = self.db.GetF2(-1)
97 self.assertTrue(math.isnan(f2))
98
9944 def test_db_f2(self):
10045 f2 = self.el.f2(1000)
101 self.assertAlmostEqual(f2, 0.0, places=10)
102
103 @unittest.skipIf(sys.version_info < (3, 3),
104 "needs the lzma module -> python 3.3 or newer")
105 def test_owndb_f2(self):
106 f2 = self.db.GetF2(1000)
10746 self.assertAlmostEqual(f2, 0.0, places=10)
10847
10948
1313 # along with this program; if not, see <http://www.gnu.org/licenses/>.
1414 #
1515 # Copyright (C) 2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 from __future__ import print_function
1816
1917 import unittest
2018
+0
-45
xrayutilities/__init__.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 xrayutilities is a Python package for assisting with x-ray diffraction
20 experiments. Its the python package included in *xrayutilities*.
21
22 It helps with planning experiments as well as analyzing the data.
23
24 Authors:
25 Dominik Kriegner <dominik.kriegner@gmail.com> and
26 Eugen Wintersberger <eugen.wintersberger@desy.de>
27 """
28 import pkg_resources
29
30 # load configuration
31 from . import analysis, config, io, materials, math, simpack
32 from .experiment import (GID, GISAXS, HXRD, Experiment, FourC, NonCOP,
33 PowderExperiment, QConversion)
34 from .gridder import FuzzyGridder1D, Gridder1D, npyGridder1D
35 from .gridder2d import FuzzyGridder2D, Gridder2D, Gridder2DList
36 from .gridder3d import FuzzyGridder3D, Gridder3D
37 from .normalize import (IntensityNormalizer, blockAverage1D, blockAverage2D,
38 blockAveragePSD)
39 from .q2ang_fit import Q2AngFit
40 from .utilities import (clear_bit, en2lam, energy, lam2en, makeNaturalName,
41 maplog, set_bit, wavelength)
42
43 # load package version
44 __version__ = pkg_resources.get_distribution("xrayutilities").version
+0
-33
xrayutilities/analysis/__init__.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2011-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 xrayutilities.analysis is a package for assisting with the analysis of
19 x-ray diffraction data, mainly reciprocal space maps
20
21 Routines for obtaining line cuts from gridded reciprocal space maps are
22 offered, with the ability to integrate the intensity perpendicular to the
23 line cut direction.
24 """
25
26 from .line_cuts import (get_arbitrary_line, get_omega_scan, get_qx_scan,
27 get_qy_scan, get_qz_scan, get_radial_scan,
28 get_ttheta_scan)
29 from .misc import coplanar_intensity, getangles, getunitvector
30 from .sample_align import (area_detector_calib, area_detector_calib_hkl,
31 fit_bragg_peak, linear_detector_calib, miscut_calc,
32 psd_chdeg, psd_refl_align)
+0
-598
xrayutilities/analysis/line_cuts.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2018-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import numpy
18
19 from .. import config, math
20 from ..experiment import HXRD
21 from ..gridder import FuzzyGridder1D
22
23
24 def _get_cut(pos_along, pos_perp, intensity, dis, npoints):
25 """
26 obtain a line cut from 2D data using a FuzzyGridder1D to do the hard work.
27 Data points with value of pos_perp smaller than `dis` will be considered in
28 the line cut
29
30 Parameters
31 ----------
32 pos_along : array-like
33 position along the cut which should be taken
34 pos_perp : array-like
35 distance from the line cut axis. only data points with distance < `dis`
36 will be considered
37 intensity : array-like
38 data points, `pos_along`, `pos_perp`, and `intensity` must have the
39 same shape
40 dis : float
41 maximum distance to be allowed for contributing data points
42 npoints : int
43 number of points in the output data
44
45 Returns
46 -------
47 x : ndarray
48 gridded position along the cut axis
49 d : ndarray
50 gridded data values for every position `x` along the cut line
51 mask: ndarray
52 mask which is 1 for every used data point and 0 for the rest
53 """
54 pos_a = pos_along.ravel()
55 pos_p = pos_perp.ravel()
56 ma = numpy.abs(pos_p) < dis
57 g1d = FuzzyGridder1D(npoints)
58 width = (numpy.max(pos_a[ma]) - numpy.min(pos_a[ma])) / float(npoints)
59 g1d(pos_a[ma], intensity.ravel()[ma], width=width)
60 return g1d.xaxis, g1d.data, ma.astype(numpy.int8).reshape(intensity.shape)
61
62
63 def get_qz_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
64 r"""
65 extracts a qz scan from reciprocal space map data with integration along
66 either, the perpendicular plane in q-space, omega (sample rocking angle) or
67 2theta direction. For the integration in angular space (omega, or 2theta)
68 the coplanar diffraction geometry with qy and qz as diffraction plane is
69 assumed. This is consistent with the coplanar geometry implemented in the
70 HXRD-experiment class.
71
72 This function works for 2D and 3D input data in the same way!
73
74 Parameters
75 ----------
76 qpos : list of array-like objects
77 arrays of y, z (list with two components) or x, y, z (list with three
78 components) momentum transfers
79 intensity : array-like
80 2D or 3D array of reciprocal space intensity with shape equal to the
81 qpos entries
82 cutpos : float or tuple/list
83 x/y-position at which the line scan should be extracted. this must be a
84 float for 2D data and a tuple with two values for 3D data
85 npoints : int
86 number of points in the output data
87 intrange : float
88 integration range in along `intdir`, either in 1/\AA (`q`) or degree
89 ('omega', or '2theta'). data will be integrated from
90 `-intrange/2 .. +intrange/2`
91
92 intdir : {'q', 'omega', '2theta'}, optional
93 integration direction: 'q': perpendicular Q-plane (default), 'omega':
94 sample rocking angle, or '2theta': scattering angle.
95 wl : float or str, optional
96 wavelength used to determine angular integration positions
97
98 Note:
99 For 3D data the angular integration directions although applicable for
100 any set of data only makes sense when the data are aligned into the
101 y/z-plane.
102
103 Returns
104 -------
105 qz, qzint : ndarray
106 qz scan coordinates and intensities
107 used_mask : ndarray
108 mask of used data, shape is the same as the input intensity: True for
109 points which contributed, False for all others
110
111 Examples
112 --------
113 >>> qzcut, qzcut_int, mask = get_qz_scan([qy, qz], inten, 3.0, 200,
114 intrange=0.3)
115 """
116 intdir = kwargs.get('intdir', 'q')
117 lam = kwargs.get('wl', config.WAVELENGTH)
118 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
119
120 # make all data 3D
121 if len(qpos) == 2:
122 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
123 lcut = [0, cutpos]
124 else:
125 lqpos = qpos
126 lcut = cutpos
127
128 # make line cuts with selected integration direction
129 if intdir == 'q':
130 qperp = numpy.sqrt((lqpos[0]-lcut[0])**2 + (lqpos[1]-lcut[1])**2)
131 ret = _get_cut(lqpos[2], qperp, intensity, intrange/2., npoints)
132 elif intdir == 'omega':
133 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='realTilt')
134 q = 4 * numpy.pi / lam * numpy.sin(numpy.radians(tt/2))
135 ocut = numpy.degrees(numpy.arcsin(lcut[1]/q)) + tt / 2
136 qzpos = q * numpy.cos(numpy.radians(ocut - tt/2))
137 ret = _get_cut(qzpos, om-ocut, intensity, intrange/2., npoints)
138 elif intdir == '2theta':
139 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='realTilt')
140 q = 4 * numpy.pi / lam * numpy.sin(numpy.radians(tt/2))
141 ttcut = 2 * (om - numpy.degrees(numpy.arcsin(lcut[1]/q)))
142 qzpos = 4 * numpy.pi / lam * numpy.sin(numpy.radians(ttcut/2)) *\
143 numpy.cos(numpy.radians(om - ttcut/2))
144 ret = _get_cut(qzpos, tt-ttcut, intensity, intrange/2., npoints)
145
146 return ret
147
148
149 def get_qy_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
150 r"""
151 extracts a qy scan from reciprocal space map data with integration along
152 either, the perpendicular plane in q-space, omega (sample rocking angle) or
153 2theta direction. For the integration in angular space (omega, or 2theta)
154 the coplanar diffraction geometry with qy and qz as diffraction plane is
155 assumed. This is consistent with the coplanar geometry implemented in the
156 HXRD-experiment class.
157
158 This function works for 2D and 3D input data in the same way!
159
160 Parameters
161 ----------
162 qpos : list of array-like objects
163 arrays of y, z (list with two components) or x, y, z (list with three
164 components) momentum transfers
165 intensity : array-like
166 2D or 3D array of reciprocal space intensity with shape equal to the
167 qpos entries
168 cutpos : float or tuple/list
169 x/z-position at which the line scan should be extracted. this must be a
170 float for 2D data (z-position) and a tuple with two values for 3D data
171 npoints : int
172 number of points in the output data
173 intrange : float
174 integration range in along `intdir`, either in 1/\AA (`q`) or degree
175 ('omega', or '2theta'). data will be integrated from
176 `-intrange .. +intrange`
177
178 intdir : {'q', 'omega', '2theta'}, optional
179 integration direction: 'q': perpendicular Q-plane (default), 'omega':
180 sample rocking angle, or '2theta': scattering angle.
181 wl : float or str, optional
182 wavelength used to determine angular integration positions
183
184 Note:
185 For 3D data the angular integration directions although applicable for
186 any set of data only makes sense when the data are aligned into the
187 y/z-plane.
188
189 Returns
190 -------
191 qy, qyint : ndarray
192 qy scan coordinates and intensities
193 used_mask : ndarray
194 mask of used data, shape is the same as the input intensity: True for
195 points which contributed, False for all others
196
197 Examples
198 --------
199 >>> qycut, qycut_int, mask = get_qy_scan([qy, qz], inten, 5.0, 250,
200 intrange=0.02, intdir='2theta')
201 """
202 intdir = kwargs.get('intdir', 'q')
203 lam = kwargs.get('wl', config.WAVELENGTH)
204 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
205
206 # make all data 3D
207 if len(qpos) == 2:
208 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
209 lcut = [0, cutpos]
210 else:
211 lqpos = qpos
212 lcut = cutpos
213
214 # make line cuts with selected integration direction
215 if intdir == 'q':
216 qperp = numpy.sqrt((lqpos[0]-lcut[0])**2 + (lqpos[2]-lcut[1])**2)
217 ret = _get_cut(lqpos[1], qperp, intensity, intrange/2., npoints)
218 elif intdir == 'omega':
219 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='real')
220 q = 4 * numpy.pi / lam * numpy.sin(numpy.radians(tt/2))
221 ocut = tt / 2 + (numpy.sign(lqpos[1].ravel()) *
222 numpy.degrees(numpy.arccos(lcut[1]/q)))
223 qypos = q * numpy.sin(numpy.radians(ocut - tt/2))
224 ret = _get_cut(qypos, om-ocut, intensity, intrange/2., npoints)
225 elif intdir == '2theta':
226 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='real')
227 ttcut = (om - numpy.degrees(numpy.arcsin(numpy.sin(numpy.radians(om)) -
228 lcut[1]*lam/(2*numpy.pi)))) % 360
229 ttcut2 = (om +
230 numpy.degrees(numpy.arcsin(numpy.sin(numpy.radians(om)) -
231 lcut[1]*lam/(2*numpy.pi))) +
232 180) % 360
233 mask = numpy.abs(tt - om) > 90
234 ttcut[mask] = ttcut2[mask]
235 q = 4 * numpy.pi / lam * numpy.sin(numpy.radians(ttcut/2))
236 qypos = q * numpy.sin(numpy.radians(om - ttcut/2))
237 ret = _get_cut(qypos, tt-ttcut, intensity, intrange/2., npoints)
238
239 return ret
240
241
242 def get_qx_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
243 r"""
244 extracts a qx scan from 3D reciprocal space map data with integration along
245 either, the perpendicular plane in q-space, omega (sample rocking angle) or
246 2theta direction. For the integration in angular space (omega, or 2theta)
247 the coplanar diffraction geometry with qy and qz as diffraction plane is
248 assumed. This is consistent with the coplanar geometry implemented in the
249 HXRD-experiment class.
250
251 Parameters
252 ----------
253 qpos : list of array-like objects
254 arrays of x, y, z (list with three components) momentum transfers
255 intensity : array-like
256 3D array of reciprocal space intensity with shape equal to the
257 qpos entries
258 cutpos : tuple/list
259 y/z-position at which the line scan should be extracted. this must be
260 and a tuple/list with the qy, qz cut position
261 npoints : int
262 number of points in the output data
263 intrange : float
264 integration range in along `intdir`, either in 1/\AA (`q`) or degree
265 ('omega', or '2theta'). data will be integrated from
266 `-intrange .. +intrange`
267
268 intdir : {'q', 'omega', '2theta'}, optional
269 integration direction: 'q': perpendicular Q-plane (default), 'omega':
270 sample rocking angle, or '2theta': scattering angle.
271 wl : float or str, optional
272 wavelength used to determine angular integration positions
273
274 Note:
275 The angular integration directions although applicable for
276 any set of data only makes sense when the data are aligned into the
277 y/z-plane.
278
279 Returns
280 -------
281 qx, qxint : ndarray
282 qx scan coordinates and intensities
283 used_mask : ndarray
284 mask of used data, shape is the same as the input intensity: True for
285 points which contributed, False for all others
286
287 Examples
288 --------
289 >>> qxcut, qxcut_int, mask = get_qx_scan([qx, qy, qz], inten, [0, 2.0],
290 250, intrange=0.01)
291 """
292 intdir = kwargs.get('intdir', 'q')
293 lam = kwargs.get('wl', config.WAVELENGTH)
294 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
295
296 # make line cuts with selected integration direction
297 if intdir == 'q':
298 qperp = numpy.sqrt((qpos[1]-cutpos[0])**2 + (qpos[2]-cutpos[1])**2)
299 ret = _get_cut(qpos[0], qperp, intensity, intrange/2., npoints)
300 elif intdir == 'omega':
301 # needs testing
302 om, chi, phi, tt = hxrd.Q2Ang(*qpos, trans=False, geometry='realTilt')
303 ocut, dmy, dmy, ttcut = hxrd.Q2Ang(qpos[0], cutpos[0], cutpos[1],
304 trans=False, geometry='realTilt')
305 ret = _get_cut(qpos[0], om-ocut, intensity, intrange/2., npoints)
306 elif intdir == '2theta':
307 # needs testing
308 om, chi, phi, tt = hxrd.Q2Ang(*qpos, trans=False, geometry='realTilt')
309 ocut, dmy, dmy, ttcut = hxrd.Q2Ang(qpos[0], cutpos[0], cutpos[1],
310 trans=False, geometry='realTilt')
311 ret = _get_cut(qpos[0], tt-ttcut, intensity, intrange/2., npoints)
312
313 return ret
314
315
316 def get_omega_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
317 """
318 extracts an omega scan from reciprocal space map data with integration
319 along either the 2theta, or radial (omega-2theta) direction. The coplanar
320 diffraction geometry with qy and qz as diffraction plane is assumed. This
321 is consistent with the coplanar geometry implemented in the HXRD-experiment
322 class.
323
324 This function works for 2D and 3D input data in the same way!
325
326 Parameters
327 ----------
328 qpos : list of array-like objects
329 arrays of y, z (list with two components) or x, y, z (list with three
330 components) momentum transfers
331 intensity : array-like
332 2D or 3D array of reciprocal space intensity with shape equal to the
333 qpos entries
334 cutpos : tuple or list
335 y/z-position or x/y/z-position at which the line scan should be
336 extracted. this must be have two entries for 2D data (z-position) and a
337 three for 3D data
338 npoints : int
339 number of points in the output data
340 intrange : float
341 integration range in along `intdir` in degree. data will be integrated
342 from `-intrange .. +intrange`
343
344 intdir : {'2theta', 'radial'}, optional
345 integration direction: '2theta': scattering angle (default), or
346 'radial': omega-2theta direction.
347 wl : float or str, optional
348 wavelength used to determine angular integration positions
349
350 Note:
351 Although applicable for any set of data, the extraction only makes
352 sense when the data are aligned into the y/z-plane.
353
354 Returns
355 -------
356 om, omint : ndarray
357 omega scan coordinates and intensities
358 used_mask : ndarray
359 mask of used data, shape is the same as the input intensity: True for
360 points which contributed, False for all others
361
362 Examples
363 --------
364 >>> omcut, omcut_int, mask = get_omega_scan([qy, qz], inten, [2.0, 5.0],
365 250, intrange=0.1)
366 """
367 intdir = kwargs.get('intdir', '2theta')
368 lam = kwargs.get('wl', config.WAVELENGTH)
369 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
370
371 # make all data 3D
372 if len(qpos) == 2:
373 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
374 lcut = [0, cutpos[0], cutpos[1]]
375 else:
376 lqpos = qpos
377 lcut = cutpos
378
379 # make line cuts with selected integration direction
380 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='realTilt')
381 ocut, dmy, dmy, ttcut = hxrd.Q2Ang(*lcut, trans=False, geometry='realTilt')
382 if intdir == '2theta':
383 ret = _get_cut(om, tt-ttcut, intensity, intrange/2., npoints)
384 elif intdir == 'radial':
385 ret = _get_cut(om-(tt-ttcut)/2, tt-ttcut, intensity, intrange/2.,
386 npoints)
387
388 return ret
389
390
391 def get_radial_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
392 """
393 extracts a radial scan from reciprocal space map data with integration
394 along either the omega or 2theta direction. The coplanar
395 diffraction geometry with qy and qz as diffraction plane is assumed. This
396 is consistent with the coplanar geometry implemented in the HXRD-experiment
397 class.
398
399 This function works for 2D and 3D input data in the same way!
400
401 Parameters
402 ----------
403 qpos : list of array-like objects
404 arrays of y, z (list with two components) or x, y, z (list with three
405 components) momentum transfers
406 intensity : array-like
407 2D or 3D array of reciprocal space intensity with shape equal to the
408 qpos entries
409 cutpos : tuple or list
410 y/z-position or x/y/z-position at which the line scan should be
411 extracted. this must be have two entries for 2D data (z-position) and a
412 three for 3D data
413 npoints : int
414 number of points in the output data
415 intrange : float
416 integration range in along `intdir` in degree. data will be integrated
417 from `-intrange .. +intrange`
418
419 intdir : {'omega', '2theta'}, optional
420 integration direction: 'omega': sample rocking angle (default),
421 '2theta': scattering angle
422 wl : float or str, optional
423 wavelength used to determine angular integration positions
424
425 Note:
426 Although applicable for any set of data, the extraction only makes
427 sense when the data are aligned into the y/z-plane.
428
429 Returns
430 -------
431 tt, omttint : ndarray
432 omega-2theta scan coordinates (2theta values) and intensities
433 used_mask : ndarray
434 mask of used data, shape is the same as the input intensity: True for
435 points which contributed, False for all others
436
437 Examples
438 --------
439 >>> ttcut, omtt_int, mask = get_radial_scan([qy, qz], inten, [2.0, 5.0],
440 250, intrange=0.1)
441 """
442 intdir = kwargs.get('intdir', 'omega')
443 lam = kwargs.get('wl', config.WAVELENGTH)
444 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
445
446 # make all data 3D
447 if len(qpos) == 2:
448 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
449 lcut = [0, cutpos[0], cutpos[1]]
450 else:
451 lqpos = qpos
452 lcut = cutpos
453
454 # make line cuts with selected integration direction
455 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='realTilt')
456 ocut, dmy, dmy, ttcut = hxrd.Q2Ang(*lcut, trans=False, geometry='realTilt')
457 if intdir == 'omega':
458 ret = _get_cut(tt, om-((tt-ttcut)/2+ocut), intensity, intrange/2.,
459 npoints)
460 elif intdir == '2theta':
461 offcut = ttcut/2 - ocut
462 ret = _get_cut(tt-2*(tt/2-om-offcut), 2*(tt/2-om-offcut), intensity,
463 intrange/2., npoints)
464
465 return ret
466
467
468 def get_ttheta_scan(qpos, intensity, cutpos, npoints, intrange, **kwargs):
469 """
470 extracts a 2theta scan from reciprocal space map data with integration
471 along either the omega or radial direction. The coplanar
472 diffraction geometry with qy and qz as diffraction plane is assumed. This
473 is consistent with the coplanar geometry implemented in the HXRD-experiment
474 class.
475
476 This function works for 2D and 3D input data in the same way!
477
478 Parameters
479 ----------
480 qpos : list of array-like objects
481 arrays of y, z (list with two components) or x, y, z (list with three
482 components) momentum transfers
483 intensity : array-like
484 2D or 3D array of reciprocal space intensity with shape equal to the
485 qpos entries
486 cutpos : tuple or list
487 y/z-position or x/y/z-position at which the line scan should be
488 extracted. this must be have two entries for 2D data (z-position) and a
489 three for 3D data
490 npoints : int
491 number of points in the output data
492 intrange : float
493 integration range in along `intdir` in degree. data will be integrated
494 from `-intrange .. +intrange`
495
496 intdir : {'omega', 'radial'}, optional
497 integration direction: 'omega': sample rocking angle (default),
498 'radial': omega-2theta direction
499 wl : float or str, optional
500 wavelength used to determine angular integration positions
501
502 Note:
503 Although applicable for any set of data, the extraction only makes
504 sense when the data are aligned into the y/z-plane.
505
506 Returns
507 -------
508 tt, ttint : ndarray
509 2theta scan coordinates and intensities
510 used_mask : ndarray
511 mask of used data, shape is the same as the input intensity: True for
512 points which contributed, False for all others
513
514 Examples
515 --------
516 >>> ttcut, tt_int, mask = get_ttheta_scan([qy, qz], inten, [2.0, 5.0],
517 250, intrange=0.1)
518 """
519 intdir = kwargs.get('intdir', 'omega')
520 lam = kwargs.get('wl', config.WAVELENGTH)
521 hxrd = HXRD([1, 0, 0], [0, 0, 1], wl=lam)
522
523 # make all data 3D
524 if len(qpos) == 2:
525 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
526 lcut = [0, cutpos[0], cutpos[1]]
527 else:
528 lqpos = qpos
529 lcut = cutpos
530
531 # make line cuts with selected integration direction
532 om, chi, phi, tt = hxrd.Q2Ang(*lqpos, trans=False, geometry='realTilt')
533 ocut, dmy, dmy, ttcut = hxrd.Q2Ang(*lcut, trans=False, geometry='realTilt')
534 if intdir == 'omega':
535 ret = _get_cut(tt, om-ocut, intensity, intrange/2., npoints)
536 elif intdir == 'radial':
537 ret = _get_cut(tt-2*(om-ocut), 2*(om-ocut), intensity,
538 intrange/2., npoints)
539
540 return ret
541
542
543 def get_arbitrary_line(qpos, intensity, point, vec, npoints, intrange):
544 """
545 extracts a line scan from reciprocal space map data along an arbitrary line
546 defined by the point 'point' and propergation vector 'vec'. Integration of
547 the data is performed in a cylindrical volume along the line.
548 This function works for 2D and 3D input data!
549
550 Parameters
551 ----------
552 qpos : list of array-like objects
553 arrays of x, y (list with two components) or x, y, z (list with three
554 components) momentum transfers
555 intensity : array-like
556 2D or 3D array of reciprocal space intensity with shape equal to the
557 qpos entries
558 point : tuple, list or array-like
559 point on the extraction line (2 or 3 coordinates)
560 vec : tuple, list or array-like
561 propergation vector of the extraction line (2 or 3 coordinates)
562 npoints : int
563 number of points in the output data
564 intrange : float
565 radius of the cylindrical integration volume around the extraction line
566
567 Returns
568 -------
569 qpos, qint : ndarray
570 line scan coordinates and intensities
571 used_mask : ndarray
572 mask of used data, shape is the same as the input intensity: True for
573 points which contributed, False for all others
574
575 Examples
576 --------
577 >>> qcut, qint, mask = get_arbitrary_line([qx, qy, qz], inten,
578 (1.1, 2.2, 0.0),
579 (1, 1, 1), 200, 0.1)
580 """
581 # make all data 3D
582 if len(qpos) == 2:
583 lqpos = [numpy.zeros_like(qpos[0]), qpos[0], qpos[1]]
584 lpoint = [0, point[0], point[1]]
585 lvec = [0, vec[0], vec[1]]
586 else:
587 lqpos = qpos
588 lpoint = point
589 lvec = vec
590
591 # make line cut
592 lqpos = numpy.reshape(numpy.asarray([lqpos[0].ravel(), lqpos[1].ravel(),
593 lqpos[2].ravel()]).T, (-1, 3))
594 qalong = math.VecDot(lqpos, math.VecUnit(lvec))
595 qdistance = math.distance(lqpos[:, 0], lqpos[:, 1], lqpos[:, 2], lpoint,
596 lvec)
597 return _get_cut(qalong, qdistance, intensity, intrange, npoints)
+0
-172
xrayutilities/analysis/misc.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2012-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 miscellaneous functions helpful in the analysis and experiment
19 """
20
21 import numpy
22
23 from .. import config, math
24
25
26 def getangles(peak, sur, inp):
27 """
28 calculates the chi and phi angles for a given peak
29
30 Parameters
31 ----------
32 peak : list or array-like
33 hkl for the peak of interest
34 sur : list or array-like
35 hkl of the surface
36 inp : list or array-like
37 inplane reference peak or direction
38
39 Returns
40 -------
41 list
42 [chi, phi] for the given peak on surface sur with inplane direction inp
43 as reference
44
45 Examples
46 --------
47 To get the angles for the -224 peak on a 111 surface type
48
49 >>> [chi, phi] = getangles([-2, 2, 4], [1, 1, 1], [2, 2, 4])
50 """
51
52 # transform input to numpy.arrays
53 peak = numpy.array(peak)
54 sur = numpy.array(sur)
55 inp = numpy.array(inp)
56
57 peak = peak / numpy.linalg.norm(peak)
58 sur = sur / numpy.linalg.norm(sur)
59 inp = inp / numpy.linalg.norm(inp)
60
61 # calculate reference inplane direction
62 inplane = numpy.cross(numpy.cross(sur, inp), sur)
63 inplane = inplane / numpy.linalg.norm(inplane)
64 if config.VERBOSITY >= config.INFO_ALL:
65 print("XU.analyis.getangles: reference inplane direction: ", inplane)
66
67 # calculate inplane direction of peak
68 pinp = numpy.cross(numpy.cross(sur, peak), sur)
69 pinp = pinp / numpy.linalg.norm(pinp)
70 if(numpy.linalg.norm(numpy.cross(sur, peak)) <= config.EPSILON):
71 pinp = inplane
72 if config.VERBOSITY >= config.INFO_ALL:
73 print("XU.analyis.getangles: peaks inplane direction: ", pinp)
74
75 # calculate angles
76 r2d = 180. / numpy.pi
77 chi = numpy.arccos(numpy.dot(sur, peak)) * r2d
78 if(numpy.dot(sur, peak) >= 1. - config.EPSILON):
79 chi = 0.
80 phi = 0.
81 elif(numpy.dot(sur, peak) <= -1. + config.EPSILON):
82 chi = 180.
83 phi = 0.
84 elif(numpy.dot(sur, numpy.cross(inplane, pinp)) <= config.EPSILON):
85 if numpy.dot(pinp, inplane) >= 1.0:
86 phi = 0.
87 elif numpy.dot(pinp, inplane) <= -1.0:
88 phi = 180.
89 else:
90 phi = numpy.sign(numpy.dot(sur, numpy.cross(inplane, pinp))) *\
91 numpy.arccos(numpy.dot(pinp, inplane)) * r2d
92 else:
93 phi = numpy.sign(numpy.dot(sur, numpy.cross(inplane, pinp))) * \
94 numpy.arccos(numpy.dot(pinp, inplane)) * r2d
95 phi = phi - round(phi / 360.) * 360
96
97 return (chi, phi)
98
99
100 def getunitvector(chi, phi, ndir=(0, 0, 1), idir=(1, 0, 0)):
101 """
102 return unit vector determined by spherical angles and definition of the
103 polar axis and inplane reference direction (phi=0)
104
105 Parameters
106 ----------
107 chi, phi : float
108 spherical angles (polar and azimuthal) in degree
109 ndir : tuple, list or array-like
110 polar/z-axis (determines chi=0)
111 idir : tuple, list or array-like
112 azimuthal axis (determines phi=0)
113 """
114 chi_axis = numpy.cross(ndir, idir)
115 v = math.rotarb(ndir, chi_axis, chi)
116 v = math.rotarb(v, ndir, phi)
117 return v / numpy.linalg.norm(v)
118
119
120 def coplanar_intensity(mat, exp, hkl, thickness, thMono, sample_width=10,
121 beam_width=1):
122 """
123 Calculates the expected intensity of a Bragg peak from an epitaxial thin
124 film measured in coplanar geometry (integration over omega and 2theta in
125 angular space!)
126
127 Parameters
128 ----------
129 mat : Crystal
130 Crystal instance for structure factor calculation
131 exp : Experiment
132 Experimental(HXRD) class for the angle calculation
133 hkl : list, tuple or array-like
134 Miller indices of the peak to calculate
135 thickness : float
136 film thickness in nm
137 thMono : float
138 Bragg angle of the monochromator (deg)
139 sample_width : float, optional
140 width of the sample along the beam
141 beam_width : float, optional
142 width of the beam in the same units as the sample size
143
144 Returns
145 -------
146 float
147 intensity of the peak
148 """
149 # angle calculation for geometrical factors
150 om, chi, phi, tt = exp.Q2Ang(mat.Q(hkl))
151
152 # structure factor calculation
153 r = abs(mat.StructureFactor(mat.Q(hkl)))**2
154
155 # polarization factor
156 Cmono = numpy.cos(2 * numpy.radians(thMono))
157 P = (1 + Cmono * numpy.cos(numpy.radians(tt))**2) / ((1 + Cmono))
158 # Lorentz factor to be used when integrating in angular space
159 L = 1 / numpy.sin(numpy.radians(tt))
160 # shape factor: changing illumination with the incidence angle
161 shapef = beam_width / (numpy.sin(numpy.radians(om)) * sample_width)
162 if shapef > 1:
163 shapef = 1
164
165 # absorption correction
166 mu = 1 / (mat.absorption_length() * 1e3)
167 mu_eff = mu * (abs(1 / numpy.sin(numpy.radians(om))) +
168 abs(1 / numpy.sin(numpy.radians(tt - om))))
169 Nblocks = (1 - numpy.exp(-mu_eff * thickness)) / mu_eff
170
171 return r * P * L * shapef * Nblocks
+0
-2278
xrayutilities/analysis/sample_align.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2011-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 functions to help with experimental alignment during experiments, especially
19 for experiments with linear and area detectors
20 """
21
22 import glob
23 import math
24 import numbers
25 import re
26 import time
27
28 import numpy
29 import scipy.optimize as optimize
30 from numpy import cos, degrees, radians, sin, tan
31 from scipy.ndimage.measurements import center_of_mass
32 from scipy.odr import models
33 from scipy.odr import odrpack as odr
34
35 from .. import config, cxrayutilities
36 from .. import math as xumath
37 from .. import utilities
38 from ..exception import InputError
39 from ..math import fwhm_exp
40
41 # regular expression to check goniometer circle syntax
42 circleSyntax = re.compile("[xyz][+-]")
43
44 #################################################
45 # channel per degree calculation
46 #################################################
47
48
49 def psd_chdeg(angles, channels, stdev=None, usetilt=True, plot=True,
50 datap="kx", modelline="r--", modeltilt="b-", fignum=None,
51 mlabel="fit", mtiltlabel="fit w/tilt", dlabel="data",
52 figtitle=True):
53 """
54 function to determine the channels per degree using a linear
55 fit of the function nchannel = center_ch+chdeg*tan(angles)
56 or the equivalent including a detector tilt
57
58 Parameters
59 ----------
60 angles : array-like
61 detector angles for which the position of the beam was measured
62 channels : array-like
63 detector channels where the beam was found
64
65 stdev : array-like, optional
66 standard deviation of the beam position
67 plot : bool, optional
68 flag to specify if a visualization of the fit should be done
69 usetilt : bool, optional
70 whether to use model considering a detector tilt, i.e. deviation angle
71 of the pixel direction from orthogonal to the primary beam
72 (default: True)
73
74 Other Parameters
75 ----------------
76 datap : str, optional
77 plot format of data points
78 modelline : str, optional
79 plot format of modelline
80 modeltilt : str, optional
81 plot format of modeltilt
82 fignum : int or str, optional
83 figure number to use for the plot
84 mlabel : str
85 label of the model w/o tilt to be used in the plot
86 mtiltlabel : str
87 label of the model with tilt to be used in the plot
88 dlabel : str
89 label of the data line to be used in the plot
90 figtitle : bool
91 flag to tell if the figure title should show the fit parameters
92
93 Returns
94 -------
95 pixelwidth : float
96 the width of one detector channel @ 1m distance, which is negative in
97 case the hit channel number decreases upon an increase of the detector
98 angle.
99 centerch : float
100 center channel of the detector
101 tilt : float
102 tilt of the detector from perpendicular to the beam (will be zero in
103 case of usetilt=False)
104
105 Note:
106 L/pixelwidth*pi/180 = channel/degree for large detector distance with the
107 sample detector disctance L
108 """
109
110 if stdev is None:
111 stdevu = numpy.ones(len(channels))
112 else:
113 stdevu = stdev
114
115 # define detector model and other functions needed for the tilt
116 def straight_tilt(p, x):
117 """
118 model for straight-linear detectors including tilt
119
120 Parameters
121 ----------
122 p : list
123 [L/w_pix*pi/180 ~= channel/degree, center_channel, detector_tilt]
124 with L sample detector disctance, and w_pix the width of one
125 detector channel
126 x : array-like
127 independent variable of the model: detector angle (degree)
128 """
129 rad = radians(x)
130 r = math.degrees(p[0]) * sin(rad) / \
131 cos(rad - math.radians(p[2])) + p[1]
132 return r
133
134 def straight_tilt_der_x(p, x):
135 """
136 derivative of straight-linear detector model with respect to the angle
137 for parameter description see straigt_tilt
138 """
139 rad = radians(x)
140 p2 = math.radians(p[2])
141 r = math.degrees(p[0]) * \
142 (cos(rad) / cos(rad - p2) +
143 sin(rad) / cos(rad - p2) ** 2 * sin(rad - p2))
144 return r
145
146 def straight_tilt_der_p(p, x):
147 """
148 derivative of straight-linear detector model with respect to the
149 paramters for parameter description see straigt_tilt
150 """
151 rad = radians(x)
152 p2 = math.radians(p[2])
153 r = numpy.concatenate([degrees(sin(rad)) / cos(rad - p2),
154 numpy.ones(x.shape, dtype=numpy.float),
155 - math.degrees(p[0]) * sin(rad) /
156 cos(rad - p2) ** 2 * sin(rad - p2)])
157 r.shape = (3,) + x.shape
158 return r
159
160 # fit linear
161 model = models.unilinear
162 data = odr.RealData(angles, channels, sy=stdevu)
163 my_odr = odr.ODR(data, model)
164 # fit type 2 for least squares
165 my_odr.set_job(fit_type=2)
166 fitlin = my_odr.run()
167
168 # fit linear with tangens angle
169 model = models.unilinear
170 data = odr.RealData(degrees(tan(radians(angles))),
171 channels, sy=stdevu)
172 my_odr = odr.ODR(data, model)
173 # fit type 2 for least squares
174 my_odr.set_job(fit_type=2)
175 fittan = my_odr.run()
176
177 if usetilt:
178 # fit tilted straight detector model
179 model = odr.Model(straight_tilt, fjacd=straight_tilt_der_x,
180 fjacb=straight_tilt_der_p)
181 data = odr.RealData(angles, channels, sy=stdevu)
182 my_odr = odr.ODR(data, model, beta0=[fittan.beta[0],
183 fittan.beta[1], 0])
184 # fit type 2 for least squares
185 my_odr.set_job(fit_type=2)
186 fittilt = my_odr.run()
187
188 if plot:
189 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.psd_chdeg')
190
191 if plot:
192 markersize = 6.0
193 markeredgewidth = 1.5
194 linewidth = 2.0
195 if fignum is None:
196 plt.figure()
197 else:
198 plt.figure(fignum)
199 # first plot to show linear model
200 ax1 = plt.subplot(211)
201 angr = angles.max() - angles.min()
202 angp = numpy.linspace(angles.min() - angr * 0.1,
203 angles.max() + angr * .1, 1000)
204 if modelline:
205 plt.plot(angp, models._unilin(fittan.beta,
206 degrees(tan(radians(angp)))),
207 modelline, label=mlabel, lw=linewidth)
208 plt.plot(angp, models._unilin(fitlin.beta, angp), 'k-', label='')
209 if usetilt:
210 plt.plot(angp, straight_tilt(fittilt.beta, angp),
211 modeltilt, label=mtiltlabel, lw=linewidth)
212 if stdev is None:
213 plt.plot(angles, channels, datap, ms=markersize,
214 mew=markeredgewidth, mec=datap[0],
215 mfc='none', label=dlabel)
216 else:
217 plt.errorbar(angles, channels, fmt=datap, yerr=stdevu,
218 ms=markersize, mew=markeredgewidth, mec=datap[0],
219 mfc='none', label=dlabel, ecolor='0.5')
220 plt.grid(True)
221 leg = plt.legend(numpoints=1)
222 leg.get_frame().set_alpha(0.8)
223
224 plt.ylabel("channel number")
225
226 # lower plot to show deviations from linear model
227 ax2 = plt.subplot(212, sharex=ax1)
228 if modelline:
229 plt.plot(angp, models._unilin(fittan.beta,
230 degrees(tan(radians(angp)))) -
231 models._unilin(fitlin.beta, angp),
232 modelline, label=mlabel, lw=linewidth)
233 if usetilt:
234 plt.plot(angp, straight_tilt(fittilt.beta, angp) -
235 models._unilin(fitlin.beta, angp),
236 modeltilt, label=mtiltlabel, lw=linewidth)
237 if stdev is None:
238 plt.plot(angles, channels - models._unilin(fitlin.beta, angles),
239 datap, ms=markersize, mew=markeredgewidth, mec=datap[0],
240 mfc='none', label=dlabel)
241 else:
242 plt.errorbar(angles,
243 channels - models._unilin(fitlin.beta, angles),
244 fmt=datap, yerr=stdevu, ms=markersize,
245 mew=markeredgewidth, mec=datap[0], mfc='none',
246 label=dlabel, ecolor='0.5')
247 plt.xlabel("detector angle (deg)")
248 plt.ylabel("ch. num. - linear trend")
249 plt.grid(True)
250 plt.hlines(0, angp.min(), angp.max())
251
252 if figtitle:
253 if usetilt:
254 plt.suptitle(
255 "L/w*pi/180: %8.2f; center channel: %8.2f; tilt: %5.2fdeg"
256 % (fittilt.beta[0], fittilt.beta[1], fittilt.beta[2]))
257 else:
258 plt.suptitle("L/w*pi/180: %8.2f; center channel: %8.2f"
259 % (fittan.beta[0], fittan.beta[1]))
260
261 if usetilt:
262 fit = fittilt
263 else:
264 fit = fittan
265
266 if config.VERBOSITY >= config.INFO_LOW:
267 if usetilt:
268 print("XU.analysis.psd_chdeg: channelwidth@1m / center channel /"
269 " tilt: %8.4e / %8.2f / %6.3fdeg"
270 % (abs(1 / math.degrees(fit.beta[0])),
271 fit.beta[1], fit.beta[2]))
272 print("XU.analysis.psd_chdeg: error of channelwidth / "
273 "center channel / tilt: %8.4e / %8.3f / %6.3fdeg"
274 % (math.radians(fit.sd_beta[0] / fit.beta[0] ** 2),
275 fit.sd_beta[1], fit.sd_beta[2]))
276 else:
277 print("XU.analysis.psd_chdeg: channelwidth@1m / center channel: "
278 "%8.4e / %8.2f"
279 % (1 / math.degrees(fit.beta[0]), fit.beta[1]))
280 print("XU.analysis.psd_chdeg: error of channelwidth / "
281 "center channel: %8.4e / %8.3f"
282 % (math.radians(fit.sd_beta[0] / fit.beta[0] ** 2),
283 fit.sd_beta[1]))
284
285 if usetilt:
286 return (1. / math.degrees(fit.beta[0]), fit.beta[1], fit.beta[2])
287 else:
288 return (1. / math.degrees(fit.beta[0]), fit.beta[1], 0.)
289
290
291 #################################################
292 # channel per degree calculation from scan with
293 # linear detector (determined maximum by peak_fit)
294 #################################################
295 def linear_detector_calib(angle, mca_spectra, **keyargs):
296 """
297 function to calibrate the detector distance/channel per degrees
298 for a straight linear detector mounted on a detector arm
299
300 Parameters
301 ----------
302 angle : array-like
303 array of angles in degree of measured detector spectra
304 mca_spectra : array-like
305 corresponding detector spectra (shape: (len(angle), Nchannels)
306
307 r_i : str, optional
308 primary beam direction as vector [xyz][+-]; default: 'y+'
309 detaxis : str, optional
310 detector arm rotation axis [xyz][+-]; default: 'x+'
311
312 Other parameters
313 ----------------
314 plot : bool
315 flag to specify if a visualization of the fit should be done
316 usetilt : bool
317 whether to use model considering a detector tilt, i.e. deviation angle
318 of the pixel direction from orthogonal to the primary beam
319 (default: True)
320
321 Returns
322 -------
323 pixelwidth : float
324 width of the pixel at one meter distance, pixelwidth is negative in
325 case the hit channel number decreases upon an increase of the detector
326 angle
327 center_channel : float
328 central channel of the detector
329 detector_tilt : float, optional
330 if usetilt=True the fitted tilt of the detector is also returned
331
332 Note:
333 L/pixelwidth*pi/180 ~= channel/degree, with the sample detector
334 distance L
335
336 The function also prints out how a linear detector can be initialized using
337 the results obtained from this calibration. Carefully check the results
338
339 See Also
340 --------
341 psd_chdeg : low level function with more configurable options
342 """
343
344 if "detaxis" in keyargs:
345 detrotaxis = keyargs["detaxis"]
346 keyargs.pop("detaxis")
347 else: # use default
348 detrotaxis = 'x+'
349 if "r_i" in keyargs:
350 r_i = keyargs["r_i"]
351 keyargs.pop("r_i")
352 else: # use default
353 r_i = 'y+'
354
355 # max intensity per spectrum
356 mca_int = mca_spectra.sum(axis=1)
357 mca_avg = numpy.average(mca_int)
358 mca_rowmax = numpy.max(mca_int)
359 mca_std = numpy.std(mca_int)
360
361 # determine positions
362 pos = []
363 posstd = []
364 ang = []
365 nignored = 0
366 for i in range(len(mca_spectra)):
367 row = mca_spectra[i, :]
368 row_int = row.sum()
369 if ((abs(row_int - mca_avg) > 3 * mca_std) or
370 (row_int - mca_rowmax * 0.7 < 0)):
371 if config.VERBOSITY >= config.DEBUG:
372 print("XU.analysis.linear_detector_calib: spectrum #%d "
373 "out of intensity range -> ignored" % i)
374 nignored += 1
375 continue
376
377 maxp = numpy.argmax(row)
378 fwhm = fwhm_exp(numpy.arange(row.size), row)
379 N = int(7 * numpy.ceil(fwhm)) // 2 * 2
380
381 # fit beam position
382 # determine maximal usable length of array around peak position
383 Nuse = min(maxp + N // 2, len(row) - 1) - max(maxp - N // 2, 0)
384 param, perr, itlim = xumath.peak_fit(
385 numpy.arange(Nuse),
386 row[max(maxp - N // 2, 0):min(maxp + N // 2, len(row) - 1)],
387 peaktype='PseudoVoigt')
388 if param[0] > 0 and param[0] < Nuse and perr[0] < Nuse/2.:
389 param[0] += max(maxp - N // 2, 0)
390 pos.append(param[0])
391 posstd.append(perr[0])
392 ang.append(angle[i])
393
394 ang = numpy.array(ang)
395 pos = numpy.array(pos)
396 posstd = numpy.array(posstd)
397 if config.VERBOSITY >= config.INFO_ALL:
398 print("XU.analysis.linear_detector_calib: using %d out of %d given "
399 "spectra." % (len(ang), len(angle)))
400 if config.VERBOSITY >= config.DEBUG:
401 print("XU.analysis.linear_detector_calib: determined peak positions:")
402 print(zip(pos, posstd))
403
404 detparam = psd_chdeg(ang, pos, stdev=posstd, **keyargs)
405 if numpy.sign(detparam[0]) > 0:
406 sign = '-'
407 else:
408 sign = '+'
409
410 detaxis = ' '
411 detd = numpy.cross(xumath.getVector(detrotaxis), xumath.getVector(r_i))
412 argm = numpy.abs(detd).argmax()
413
414 def flipsign(char, val):
415 if numpy.sign(val) < 0:
416 if char == '+':
417 return '-'
418 else:
419 return '+'
420 else:
421 return char
422
423 if argm == 0:
424 detaxis = 'x' + flipsign(sign, detd[argm])
425 elif argm == 1:
426 detaxis = 'y' + flipsign(sign, detd[argm])
427 elif argm == 2:
428 detaxis = 'z' + flipsign(sign, detd[argm])
429
430 if config.VERBOSITY >= config.INFO_LOW:
431 print("XU.analysis.linear_detector_calib:\n\tused/total spectra: %d/%d"
432 % (mca_spectra.shape[0] - nignored, mca_spectra.shape[0]))
433 print("\tdetector rotation axis (given by user/default input): %s"
434 % detrotaxis)
435 if len(detparam) == 3:
436 tilt = detparam[2]
437 else:
438 tilt = 0
439 print("\tdetector initialization with: init_linear('%s', %.2f, %d"
440 ", pixelwidth=%.4e, distance=1., tilt=%.2f)"
441 % (detaxis, abs(detparam[1]), mca_spectra.shape[1],
442 abs(detparam[0]), tilt))
443
444 return detparam
445
446
447 ######################################################
448 # detector parameter calculation from scan with
449 # area detector (determine maximum by center of mass)
450 ######################################################
451 def area_detector_calib(angle1, angle2, ccdimages, detaxis, r_i, plot=True,
452 cut_off=0.7, start=(None, None, 1, 0, 0, 0, 0),
453 fix=(False, False, True, False, False, False, False),
454 fig=None, wl=None, plotlog=False, nwindow=50,
455 debug=False):
456 """
457 function to calibrate the detector parameters of an area detector
458 it determines the detector tilt possible rotations and offsets in the
459 detector arm angles
460
461 Parameters
462 ----------
463 angle1 : array-like
464 outer detector arm angle
465 angle2 : array-like
466 inner detector arm angle
467 ccdimages : array-like
468 images of the ccd taken at the angles given above
469 detaxis : list of str
470 detector arm rotation axis; default: ['z+', 'y-']
471 r_i : str
472 primary beam direction [xyz][+-]; default 'x+'
473
474 plot : bool, optional
475 flag to determine if results and intermediate results should be
476 plotted; default: True
477 cut_off : float, optional
478 cut off intensity to decide if image is used for the determination or
479 not; default: 0.7 = 70%
480 start : tuple, optional
481 sequence of start values of the fit for parameters, which can not be
482 estimated automatically or might want to be fixed. These are: pwidth1,
483 pwidth2, distance, tiltazimuth, tilt, detector_rotation,
484 outerangle_offset. By default (None, None, 1, 0, 0, 0, 0) is used.
485 fix : tuple of bool
486 fix parameters of start (default: (False, False, True, False, False,
487 False, False)) It is strongly recommended to either fix the distance or
488 the pwidth1, 2 values.
489 fig : Figure, optional
490 matplotlib figure used for plotting the error default: None (creates
491 own figure)
492 wl : float or str
493 wavelength of the experiment in Angstrom (default: config.WAVELENGTH)
494 value does not really matter here but does affect the scaling of the
495 error
496 plotlog : bool
497 flag to specify if the created error plot should be on log-scale
498 nwindow : int
499 window size for determination of the center of mass position after the
500 center of mass of every full image is determined, the center of mass is
501 determined again using a window of size nwindow in order to reduce the
502 effect of hot pixels.
503 debug : bool
504 flag to specify that you want to see verbose output and saving of
505 images to show if the CEN determination works
506 """
507
508 if plot:
509 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.area_'
510 'detector_calib')
511
512 if wl is None:
513 wl = config.WAVELENGTH
514 else:
515 wl = utilities.wavelength(wl)
516
517 for i in [0, 1]:
518 if fix[i] and not numpy.isscalar(start[i]):
519 raise ValueError("XU.analysis.area_detector_calib: start value for"
520 " pwidth%d must be given if it should be fixed "
521 "during the fit" % (i+1))
522
523 t0 = time.time()
524 Npoints = len(angle1)
525 if debug:
526 print("number of given images: %d" % Npoints)
527
528 # determine center of mass position from detector images
529 # also use only images with an intensity larger than 70% of the average
530 # intensity
531 n1 = numpy.zeros(0, dtype=numpy.double)
532 n2 = n1
533 ang1 = n1
534 ang2 = n1
535
536 avg = 0
537 for i in range(Npoints):
538 avg += numpy.sum(ccdimages[i])
539 avg /= float(Npoints)
540 (N1, N2) = ccdimages[0].shape
541
542 if debug:
543 print("average intensity per image: %.1f" % avg)
544
545 for i in range(Npoints):
546 if debug and i == 0:
547 print("angle1, angle2, cen1, cen2")
548 img = ccdimages[i]
549 if numpy.sum(img) > cut_off * avg:
550 cen1, cen2 = _peak_position(img, nwindow, plot=debug and plot)
551 n1 = numpy.append(n1, cen1)
552 n2 = numpy.append(n2, cen2)
553 ang1 = numpy.append(ang1, angle1[i])
554 ang2 = numpy.append(ang2, angle2[i])
555 if debug:
556 print("%8.3f %8.3f \t%.2f %.2f" % (angle1[i], angle2[i],
557 cen1, cen2))
558 Nused = len(ang1)
559
560 if debug:
561 print("Nused / Npoints: %d / %d" % (Nused, Npoints))
562
563 # determine detector directions
564 detdir1, detdir2 = _determine_detdir(
565 ang1 - start[6], ang2, n1, n2, detaxis, r_i)
566
567 if debug:
568 print("determined detector directions:[%s, %s]" % (detdir1, detdir2))
569
570 epslist = []
571 paramlist = []
572 epsmin = numpy.inf
573 fitmin = None
574
575 print("tiltaz tilt detrot offset: error (relative) (fittime)")
576 print("------------------------------------------------------------")
577 # find optimal detector rotation (however keep other parameters free)
578 detrot = start[5]
579 if not fix[5]:
580 for detrotstart in numpy.linspace(start[5] - 1, start[5] + 1, 40):
581 start = start[:5] + (detrotstart,) + (start[6],)
582 eps, param, fit = _area_detector_calib_fit(
583 ang1, ang2, n1, n2, detaxis, r_i, detdir1, detdir2,
584 start=start, fix=fix, full_output=True, wl=wl, debug=debug)
585 epslist.append(eps)
586 paramlist.append(param)
587 if epslist[-1] < epsmin:
588 epsmin = epslist[-1]
589 parammin = param
590 fitmin = fit
591 detrot = param[7]
592 if debug:
593 print(eps, param)
594
595 Ntiltaz = 1 if fix[3] else 5
596 Ntilt = 1 if fix[4] else 6
597 Noffset = 1 if fix[6] else 100
598 if fix[6]:
599 Ntilt = Ntilt * 8 if not fix[4] else Ntilt
600 Ntiltaz = Ntiltaz * 7 if not fix[3] else Ntiltaz
601
602 startparam = start[:5] + (detrot,) + (start[6],)
603 if debug:
604 print("start params: %s" % str(startparam))
605
606 Ntot = Ntiltaz * Ntilt * Noffset
607 ict = 0
608 for tiltazimuth in numpy.linspace(startparam[3] if fix[3] else 0,
609 360, Ntiltaz, endpoint=False):
610 for tilt in numpy.linspace(
611 startparam[4] if fix[4] else max(0, startparam[4]-2),
612 max(0, startparam[4]-2)+4, Ntilt):
613 for offset in numpy.linspace(
614 startparam[6] if fix[6] else - 3 + startparam[6],
615 3 + startparam[6], Noffset):
616 t1 = time.time()
617 start = (startparam[0], startparam[1], startparam[2],
618 tiltazimuth, tilt, startparam[5], offset)
619 eps, param, fit = _area_detector_calib_fit(
620 ang1, ang2, n1, n2, detaxis, r_i, detdir1, detdir2,
621 start=start, fix=fix, full_output=True, wl=wl)
622 epslist.append(eps)
623 paramlist.append(param)
624 t2 = time.time()
625 print("%d/%d\t%6.1f %6.2f %8.3f %8.3f: %10.4e (%4.2f) "
626 "(%5.2fsec)" % ((ict, Ntot) + start[3:] +
627 (epslist[-1],
628 epslist[-1] / epsmin, t2 - t1)))
629 ict += 1
630
631 if epslist[-1] < epsmin:
632 print("************************")
633 print("new global minimum found")
634 epsmin = epslist[-1]
635 parammin = param
636 fitmin = fit
637 print("new best parameters: %.2f %.2f %10.4e %10.4e %8.4f "
638 "%.1f %.2f %.3f %.3f" % parammin)
639 print("************************\n")
640
641 (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
642 outerangle_offset) = parammin
643
644 if plot:
645 if fig:
646 plt.figure(fig.number)
647 else:
648 plt.figure("CCD Calib fit")
649 plt.clf()
650 nparams = numpy.array(paramlist)
651 neps = numpy.array(epslist)
652 labels = (
653 'cch1 (1)',
654 'cch2 (1)',
655 r'pwidth1 ($\mu$m)',
656 r'pwidth2 ($\mu$m)',
657 'distance (m)',
658 'tiltazimuth (deg)',
659 'tilt (deg)',
660 'detrot (deg)',
661 'outerangle offset (deg)')
662 xscale = (1., 1., 1.e6, 1.e6, 1., 1., 1., 1., 1.)
663 for p in range(9):
664 ax = plt.subplot(3, 3, p + 1)
665 if plotlog:
666 plt.semilogy(nparams[:, p] * xscale[p], neps, 'k.')
667 else:
668 plt.scatter(nparams[:, p] * xscale[p], neps, c=nparams[:, -1],
669 s=10, marker='o', cmap=plt.cm.gnuplot,
670 edgecolor='none')
671 plt.xlabel(labels[p])
672 if plotlog:
673 plt.semilogy(parammin[p] * xscale[p], epsmin, 'ko', ms=8,
674 mew=2.5, mec='k', mfc='w')
675 else:
676 plt.plot(parammin[p] * xscale[p], epsmin, 'ko', ms=8, mew=2.5,
677 mec='k', mfc='w')
678 plt.ylim(epsmin * 0.7, epsmin * 2.)
679 plt.locator_params(nbins=4, axis='x')
680 if p > 1:
681 if fix[p-2]:
682 ax.set_facecolor('0.85')
683 plt.tight_layout()
684
685 if config.VERBOSITY >= config.INFO_LOW:
686 print("total time needed for fit: %.2fsec" % (time.time() - t0))
687 print("fitted parameters: epsilon: %10.4e (%d,%s) "
688 % (epsmin, fitmin.info, repr(fitmin.stopreason)))
689 print("param: (cch1, cch2, pwidth1, pwidth2, tiltazimuth, tilt, "
690 "detrot, outerangle_offset)")
691 print("param: %.2f %.2f %10.4e %10.4e %.4f %.1f %.2f %.3f %.3f"
692 % (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt,
693 detrot, outerangle_offset))
694
695 if config.VERBOSITY > 0:
696 print("please check the resulting data (consider setting plot=True)")
697 print("detector rotation axis / primary beam direction "
698 "(given by user): %s / %s" % (repr(detaxis), r_i))
699 print("detector pixel directions / distance: %s %s / %g"
700 % (detdir1, detdir2, 1.))
701 print("\tdetector initialization with: init_area('%s', '%s', "
702 "cch1=%.2f, cch2=%.2f, Nch1=%d, Nch2=%d, pwidth1=%.4e, "
703 "pwidth2=%.4e, distance=%.5f, detrot=%.3f, tiltazimuth=%.1f, "
704 "tilt=%.3f)" % (detdir1, detdir2, cch1, cch2, N1, N2, pwidth1,
705 pwidth2, distance, detrot, tiltazimuth, tilt))
706 print("AND ALWAYS USE an (additional) OFFSET of %.4fdeg in the "
707 "OUTER DETECTOR ANGLE!" % (outerangle_offset))
708
709 return (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth,
710 tilt, detrot, outerangle_offset), eps
711
712
713 def _peak_position(img, nwindow, plot=False):
714 """
715 function to determine the peak position on the detector using the center of
716 mass (COM)
717
718 Parameters
719 ----------
720 img : array-like
721 detector image data as 2D array
722 nwindow : int
723 to avoid influence of hot pixels far away from the peak position the
724 center of mass approach is repeated with a window around the COM of the
725 full image. COM of the size (nwindow, nwindow) is returned
726 plot : bool, optional
727 the result of the of the determination can be saved as a plot
728 """
729 nw = nwindow // 2
730 [cen1r, cen2r] = center_of_mass(img)
731 for i in range(11): # refine center of mass multiple times
732 [cen1, cen2] = center_of_mass(
733 img[max(int(cen1r) - nw, 0):
734 min(int(cen1r) + nw, img.shape[0]),
735 max(int(cen2r) - nw, 0):
736 min(int(cen2r) + nw, img.shape[1])])
737 cen1 += max(int(cen1r) - nw, 0)
738 cen2 += max(int(cen2r) - nw, 0)
739 if numpy.linalg.norm((cen1 - cen1r, cen2 - cen2r)) > 3:
740 cen1r, cen2r = (cen1, cen2)
741 else:
742 break
743 if i == 10 and config.VERBOSITY >= config.INFO_LOW:
744 print("XU.analysis._peak_position: Warning: peak position "
745 "determination not converged, consider debug mode!")
746 if plot:
747 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis._peak_'
748 'position')
749 if plot:
750 plt.figure("_ccd")
751 plt.imshow(utilities.maplog(img), origin='low')
752 plt.plot(cen2, cen1, "wo", mfc='none')
753 plt.axis([cen2 - nw, cen2 + nw, cen1 - nw, cen1 + nw])
754 plt.colorbar()
755 fnr = len(glob.glob('xu_calib_ccd_img*.png'))
756 plt.savefig("xu_calib_ccd_img%d.png" % (fnr + 1))
757 plt.close("_ccd")
758
759 return cen1, cen2
760
761
762 def _determine_detdir(ang1, ang2, n1, n2, detaxis, r_i):
763 """
764 determines detector pixel direction from correlation analysis of linear
765 fits to the observed pixel numbers of the primary beam.
766 """
767 # center channel and detector pixel direction and pixel size
768 (s1, i1), r1 = xumath.linregress(ang1, n1)
769 (s2, i2), r2 = xumath.linregress(ang1, n2)
770 (s3, i3), r3 = xumath.linregress(ang2, n1)
771 (s4, i4), r4 = xumath.linregress(ang2, n2)
772
773 # determine detector directions
774 s = ord('x') + ord('y') + ord('z')
775 c1 = ord(detaxis[0][0]) + ord(r_i[0])
776 c2 = ord(detaxis[1][0]) + ord(r_i[0])
777 sign1 = numpy.sign(numpy.sum(numpy.cross(xumath.getVector(detaxis[0]),
778 xumath.getVector(r_i))))
779 sign2 = numpy.sign(numpy.sum(numpy.cross(xumath.getVector(detaxis[1]),
780 xumath.getVector(r_i))))
781 if ((r1 + r2 > r3 + r4) and r1 > r2) or ((r1 + r2 < r3 + r4) and r3 < r4):
782 detdir1 = chr(s - c1)
783 detdir2 = chr(s - c2)
784 if numpy.sign(s1) > 0:
785 if sign1 > 0:
786 detdir1 += '-'
787 else:
788 detdir1 += '+'
789 else:
790 if sign1 > 0:
791 detdir1 += '+'
792 else:
793 detdir1 += '-'
794
795 if numpy.sign(s4) > 0:
796 if sign2 > 0:
797 detdir2 += '-'
798 else:
799 detdir2 += '+'
800 else:
801 if sign2 > 0:
802 detdir2 += '+'
803 else:
804 detdir2 += '-'
805 else:
806 detdir1 = chr(s - c2)
807 detdir2 = chr(s - c1)
808 if numpy.sign(s3) > 0:
809 if sign2 > 0:
810 detdir1 += '-'
811 else:
812 detdir1 += '+'
813 else:
814 if sign2 > 0:
815 detdir1 += '+'
816 else:
817 detdir1 += '-'
818
819 if numpy.sign(s2) > 0:
820 if sign1 > 0:
821 detdir2 += '-'
822 else:
823 detdir2 += '+'
824 else:
825 if sign1 > 0:
826 detdir2 += '+'
827 else:
828 detdir2 += '-'
829
830 return detdir1, detdir2
831
832
833 def _area_detector_calib_fit(ang1, ang2, n1, n2, detaxis, r_i, detdir1,
834 detdir2, start=(None, None, 1, 0, 0, 0, 0),
835 fix=(False, False, True, False, False, False,
836 False),
837 full_output=False, wl=1., debug=False):
838 """
839 INTERNAL FUNCTION
840 function to calibrate the detector parameters of an area detector
841 it determines the detector tilt possible rotations and offsets in the
842 detector arm angles
843
844 parameters
845 ----------
846 angle1 : array-like
847 outer detector arm angle
848 angle2 : array-like
849 inner detector arm angle
850 n1, n2 : int
851 pixel number at which the primary beam was observed
852 detaxis : list of str
853 detector arm rotation axis; default: ['z+', 'y-']
854 r_i : str
855 primary beam direction [xyz][+-]; default 'x+'
856 detdir1, detdir2 : str
857 detector pixel directions of first and second pixel coordinates;
858 e.g. 'y+'
859
860 start : tuple, optional
861 sequence of start values of the fit for parameters, which can not be
862 estimated automatically or might want to be fixed. These are: pwidth1,
863 pwidth2, distance, tiltazimuth, tilt, detector_rotation,
864 outerangle_offset. By default (None, None, 1, 0, 0, 0, 0) is used.
865 fix : tuple of bool
866 fix parameters of start (default: (False, False, True, False, False,
867 False, False)) It is strongly recommended to either fix the distance or
868 the pwidth1, 2 values.
869 fig : Figure, optional
870 matplotlib figure used for plotting the error default: None (creates
871 own figure)
872 full_output : bool
873 flag to tell if to return fit object with final parameters and detector
874 directions
875 wl : float or str
876 wavelength of the experiment in Angstrom (default: 1)
877 value does not really matter here but does affect the scaling of the
878 error
879 debug : bool
880 flag to tell if you want to see debug output of the script (switch this
881 to true only if you can handle it :))
882
883 Returns
884 -------
885 float
886 final epsilon of the fit
887 param : list, optional
888 if full_output: fit parameters
889 fit : object, optional
890 if full_output: fit object
891 """
892
893 def areapixel(params, detectorDir1, detectorDir2, r_i, detectorAxis,
894 *args, **kwargs):
895 """
896 angular to momentum space conversion for pixels of an area detector the
897 center pixel is in direction of self.r_i when detector angles are zero
898
899 the detector geometry must be given to the routine
900
901 Parameters
902 ----------
903 params : list
904 parameters of the detector calibration model
905 (cch1, cch2, pwidth1, pwidth2, tiltazimuth, tilt, detrot)
906
907 - cch1, cch2: center pixel, in direction of self.r_i at zero
908 detectorAngles;
909 - pwidth1, pwidth2: width of one pixel (same unit as distance);
910 - distance: distance of center pixel from center of rotation;
911 - tiltazimuth: direction of the tilt vector in the detector plane
912 (in degree);
913 - tilt: tilt of the detector plane around an axis normal to the
914 direction given by the tiltazimuth;
915 - detrot: detector rotation around the primary beam direction as
916 given by r_i;
917
918 detectorDir1 : str
919 direction of the detector (along the pixel direction 1); e.g. 'z+'
920 means higher pixel numbers at larger z positions
921 detectorDir2 : str
922 direction of the detector (along the pixel direction 2); e.g. 'x+'
923 r_i : str
924 primary beam direction e.g. 'x+'
925 detectorAxis : list or tuple
926 detector circles e.g. ['z+', 'y-'] would mean a detector arm with a
927 two rotations
928 *args : array-like
929 detector angles and channel numbers;
930 *dAngles* as numpy array, lists or Scalars in total
931 len(detectorAxis) must be given starting with the outer most
932 circle. All arguments must have the same shape or length.
933 *channel numbers* n1 and n2 where the primary beam hits the
934 detector with same length as the detector values
935
936 delta : list, optional
937 giving delta angles to correct the given ones for misalignment
938 delta must be an numpy array or list of len(*dAngles) used angles
939 are than *args - delta
940 wl : float or str, optional
941 x-ray wavelength in angstroem (default: 1 (since it does not matter
942 here))
943 deg : bool, optional
944 flag to tell if angles are passed as degree (default: True)
945
946 Returns
947 -------
948 ndarray
949 reciprocal space position of detector pixels n1, n2 in a
950 numpy.ndarray of shape ( len(args) , 3 )
951 """
952
953 # check detector circle argument
954 if isinstance(detectorAxis, (str, list, tuple)):
955 if isinstance(detectorAxis, str):
956 dAxis = list([detectorAxis])
957 else:
958 dAxis = list(detectorAxis)
959 for circ in dAxis:
960 if not isinstance(circ, str) or len(circ) != 2:
961 raise InputError("QConversionPixel: incorrect detector "
962 "circle type or syntax (%s)" % repr(circ))
963 if not circleSyntax.search(circ):
964 raise InputError("QConversionPixel: incorrect detector "
965 "circle syntax (%s)" % circ)
966 else:
967 raise TypeError("Qconversion error: invalid type for detectorAxis,"
968 " must be str, list or tuple")
969 # add detector rotation around primary beam
970 dAxis += [r_i]
971 _detectorAxis_str = ''
972 for circ in dAxis:
973 _detectorAxis_str += circ
974
975 Nd = len(dAxis)
976 Nargs = Nd + 2 - 1
977
978 # check detectorDir
979 if not isinstance(detectorDir1, str) or len(detectorDir1) != 2:
980 raise InputError("QConversionPixel: incorrect detector direction1 "
981 "type or syntax (%s)" % repr(detectorDir1))
982 if not circleSyntax.search(detectorDir1):
983 raise InputError("QConversionPixel: incorrect detector direction1 "
984 "syntax (%s)" % detectorDir1)
985 _area_detdir1 = detectorDir1
986 if not isinstance(detectorDir2, str) or len(detectorDir2) != 2:
987 raise InputError("QConversionPixel: incorrect detector direction2 "
988 "type or syntax (%s)" % repr(detectorDir2))
989 if not circleSyntax.search(detectorDir2):
990 raise InputError("QConversionPixel: incorrect detector direction2 "
991 "syntax (%s)" % detectorDir2)
992 _area_detdir2 = detectorDir2
993
994 # parse parameter arguments
995 _area_cch1 = float(params[0])
996 _area_cch2 = float(params[1])
997 _area_pwidth1 = float(params[2])
998 _area_pwidth2 = float(params[3])
999 _area_distance = float(params[4])
1000 _area_tiltazimuth = math.radians(params[5])
1001 _area_tilt = math.radians(params[6])
1002 _area_rot = float(params[7])
1003 _area_ri = xumath.getVector(r_i) * _area_distance
1004
1005 # kwargs
1006 wl = utilities.wavelength(kwargs.get('wl', 1.))
1007 deg = kwargs.get('deg', True)
1008
1009 delta = numpy.asarray(kwargs.get('delta', numpy.zeros(Nd)),
1010 dtype=numpy.double)
1011 if delta.size != Nd - 1:
1012 raise InputError("QConversionPixel: keyword argument delta "
1013 "does not have an appropriate shape")
1014
1015 # prepare angular arrays from *args
1016 # need one sample angle and one detector angle array
1017 if len(args) != Nargs:
1018 raise InputError("QConversionPixel: wrong amount (%d) of arguments"
1019 " given, number of arguments should be %d"
1020 % (len(args), Nargs))
1021
1022 try:
1023 Npoints = len(args[0])
1024 except (TypeError, IndexError):
1025 Npoints = 1
1026
1027 dAngles = numpy.array((), dtype=numpy.double)
1028 for i in range(Nd - 1):
1029 arg = args[i]
1030 if not isinstance(arg, (numbers.Number, tuple,
1031 list, numpy.ndarray)):
1032 raise TypeError("QConversionPixel: invalid type for one of "
1033 "the detector coordinates, must be scalar, "
1034 "list or array")
1035 elif isinstance(arg, numbers.Number):
1036 arg = numpy.array([arg], dtype=numpy.double)
1037 elif isinstance(arg, list):
1038 arg = numpy.array(arg, dtype=numpy.double)
1039 arg = arg - delta[i]
1040 dAngles = numpy.concatenate((dAngles, arg))
1041 # add detector rotation around primary beam
1042 dAngles = numpy.concatenate((dAngles,
1043 numpy.ones(arg.shape,
1044 dtype=numpy.double) *
1045 _area_rot))
1046
1047 # read channel numbers
1048 n1 = numpy.array((), dtype=numpy.double)
1049 n2 = numpy.array((), dtype=numpy.double)
1050
1051 arg = args[Nd - 1]
1052 if not isinstance(arg, (numbers.Number, tuple, list, numpy.ndarray)):
1053 raise TypeError("QConversionPixel: invalid type for one of the "
1054 "detector coordinates, must be scalar, list or "
1055 "array")
1056 elif isinstance(arg, numbers.Number):
1057 arg = numpy.array([arg], dtype=numpy.double)
1058 elif isinstance(arg, list):
1059 arg = numpy.array(arg, dtype=numpy.double)
1060 n1 = arg
1061
1062 arg = args[Nd]
1063 if not isinstance(arg, (numbers.Number, tuple, list, numpy.ndarray)):
1064 raise TypeError("QConversionPixel: invalid type for one of the "
1065 "detector coordinates, must be scalar, list or "
1066 "array")
1067 elif isinstance(arg, numbers.Number):
1068 arg = numpy.array([arg], dtype=numpy.double)
1069 elif isinstance(arg, list):
1070 arg = numpy.array(arg, dtype=numpy.double)
1071 n2 = arg
1072
1073 dAngles.shape = (Nd, Npoints)
1074 dAngles = dAngles.transpose()
1075
1076 if deg:
1077 dAngles = radians(dAngles)
1078
1079 qpos = cxrayutilities.ang2q_conversion_area_pixel(
1080 dAngles, n1, n2, _area_ri, _detectorAxis_str,
1081 _area_cch1, _area_cch2, _area_pwidth1, _area_pwidth2,
1082 _area_detdir1, _area_detdir2, _area_tiltazimuth, _area_tilt,
1083 wl, config.NTHREADS)
1084
1085 return qpos[:, 0], qpos[:, 1], qpos[:, 2]
1086
1087 def afunc(param, x, detectorDir1, detectorDir2, r_i, detectorAxis, wl):
1088 """
1089 function for fitting the detector parameters
1090 basically this is a wrapper for the areapixel function
1091
1092 parameters
1093 ----------
1094 param : list
1095 fit parameters (cch1, cch2, pwidth1, pwidth2, distance,
1096 tiltazimuth, tilt, detrot, outerangle_offset)
1097 x : array-like
1098 independent variables (angle1, angle2, n1, n2) with
1099 shape (4, Npoints)
1100 detectorDir1 : str
1101 direction of the detector (along the pixel direction 1); e.g. 'z+'
1102 means higher pixel numbers at larger z positions
1103 detectorDir2 : str
1104 direction of the detector (along the pixel direction 2); e.g. 'x+'
1105 r_i : str
1106 primary beam direction e.g. 'x+'
1107 detectorAxis : list or tuple
1108 detector circles e.g. ['z+', 'y-'] would mean a detector arm with a
1109 two rotations
1110 wl : float or str
1111 wavelength of the experiment in Angstroem
1112
1113 Returns
1114 -------
1115 ndarray
1116 reciprocal space position of detector pixels n1, n2 in a
1117 numpy.ndarray of shape (3, x.shape[1])
1118 """
1119
1120 angle1 = x[0, :]
1121 angle2 = x[1, :]
1122 n1 = x[2, :]
1123 n2 = x[3, :]
1124
1125 # use only positive tilt
1126 param[6] = abs(param[6])
1127
1128 (qx, qy, qz) = areapixel(
1129 param[:-1], detectorDir1, detectorDir2, r_i, detectorAxis,
1130 angle1, angle2, n1, n2, delta=[param[-1], 0.], wl=wl)
1131
1132 return qx ** 2 + qy ** 2 + qz ** 2
1133
1134 Npoints = len(ang1)
1135
1136 # guess initial parameters
1137 # center channel and detector pixel direction and pixel size
1138 (s1, i1), r1 = xumath.linregress(ang1 - start[6], n1)
1139 (s2, i2), r2 = xumath.linregress(ang1 - start[6], n2)
1140 (s3, i3), r3 = xumath.linregress(ang2, n1)
1141 (s4, i4), r4 = xumath.linregress(ang2, n2)
1142
1143 distance = start[2]
1144 if ((r1 + r2 > r3 + r4) and r1 > r2) or ((r1 + r2 < r3 + r4) and r3 < r4):
1145 cch1 = i1
1146 cch2 = i4
1147 pwidth1 = 2 * distance / numpy.abs(s1) * math.tan(math.radians(0.5))
1148 pwidth2 = 2 * distance / numpy.abs(s4) * math.tan(math.radians(0.5))
1149 else:
1150 cch1 = i3
1151 cch2 = i2
1152 pwidth1 = 2 * distance / numpy.abs(s3) * math.tan(math.radians(0.5))
1153 pwidth2 = 2 * distance / numpy.abs(s2) * math.tan(math.radians(0.5))
1154 if numpy.isscalar(start[0]):
1155 pwidth1 = start[0]
1156 if numpy.isscalar(start[1]):
1157 pwidth2 = start[1]
1158 if numpy.isscalar(start[0]) or numpy.isscalar(start[1]):
1159 # find biggest correlation and recalculate distance
1160 idxmax = numpy.argmax((r1, r2, r3, r4))
1161 s = (s1, s2, s3, s4)[idxmax]
1162 distance = abs(s) / math.tan(math.radians(0.5)) / 2
1163 distance *= pwidth1 if idxmax < 2 else pwidth2
1164 tilt = abs(start[4])
1165 tiltazimuth = start[3]
1166 detrot = start[5]
1167 outerangle_offset = start[6]
1168 # parameters for the fitting
1169 param = (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt,
1170 detrot, outerangle_offset)
1171 if debug:
1172 print("initial parameters: ")
1173 print("primary beam / detector pixel directions / distance: "
1174 "%s / %s %s / %e" % (r_i, detdir1, detdir2, distance))
1175 print("param: (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, "
1176 "tilt, detrot, outerangle_offset)")
1177 print("param: %.2f %.2f %10.4e %10.4e %.3f %.1f %.2f %.3f %.3f"
1178 % param)
1179
1180 # set data
1181 x = numpy.empty((4, Npoints), dtype=numpy.double)
1182 x[0, :] = ang1
1183 x[1, :] = ang2
1184 x[2, :] = n1
1185 x[3, :] = n2
1186 data = odr.Data(x, y=1)
1187 # define model for fitting
1188 model = odr.Model(afunc, extra_args=(detdir1, detdir2, r_i, detaxis, wl),
1189 implicit=True)
1190 # check if parameters need to be fixed
1191 ifixb = ()
1192 for i in range(len(fix)):
1193 ifixb += (int(not fix[i]),)
1194
1195 my_odr = odr.ODR(data, model, beta0=param, ifixb=(1, 1) + ifixb,
1196 ifixx=(0, 0, 0, 0),
1197 stpb=(0.4, 0.4, pwidth1/50., pwidth2/50.,
1198 distance/1000, 2, 0.125, 0.01, 0.01),
1199 sclb=(1/abs(cch1), 1/abs(cch2),
1200 1/pwidth1, 1/pwidth2, 1/distance, 1/90.,
1201 1/0.2, 1/0.2, 1/0.2),
1202 maxit=200, ndigit=12, sstol=1e-11, partol=1e-11)
1203 if debug:
1204 my_odr.set_iprint(final=1)
1205 my_odr.set_iprint(iter=2)
1206
1207 fit = my_odr.run()
1208
1209 (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
1210 outerangle_offset) = fit.beta
1211 # fix things in parameters
1212 tiltazimuth = tiltazimuth % 360.
1213 tilt = abs(tilt)
1214
1215 final_q = afunc([cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt,
1216 detrot, outerangle_offset],
1217 x, detdir1, detdir2, r_i, detaxis, wl)
1218 final_error = numpy.mean(final_q)
1219
1220 if debug:
1221 print("fitted parameters: (%e, %d, %s) " % (final_error, fit.info,
1222 repr(fit.stopreason)))
1223 print("primary beam / detector pixel directions / distance: "
1224 "%s / %s %s / %e" % (r_i, detdir1, detdir2, distance))
1225 print("param: (cch1, cch2, pwidth1, pwidth2, disance, tiltazimuth, "
1226 "tilt, detrot, outerangle_offset)")
1227 print("param: %.2f %.2f %10.4e %10.4e %.3f %.1f %.2f %.3f %.3f"
1228 % (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt,
1229 detrot, outerangle_offset))
1230
1231 if full_output:
1232 return final_error, (cch1, cch2, pwidth1, pwidth2, distance,
1233 tiltazimuth, tilt, detrot, outerangle_offset), fit
1234 else:
1235 return final_error
1236
1237
1238 # #####################################################
1239 # detector parameter calculation from scan with
1240 # area detector (determine maximum by center of mass)
1241 # #####################################################
1242 def area_detector_calib_hkl(sampleang, angle1, angle2, ccdimages, hkls,
1243 experiment, material, detaxis, r_i, plot=True,
1244 cut_off=0.7,
1245 start=(None, None, 1, 0, 0, 0, 0, 0, 0, 'config'),
1246 fix=(False, False, True, False, False, False,
1247 False, False, False, False),
1248 fig=None, plotlog=False, nwindow=50, debug=False):
1249 """
1250 function to calibrate the detector parameters of an area detector
1251 it determines the detector tilt possible rotations and offsets in the
1252 detector arm angles
1253
1254 in this variant not only scans through the primary beam but also scans at a
1255 set of symmetric reflections can be used for the detector parameter
1256 determination. for this not only the detector parameters but in addition
1257 the sample orientation and wavelength need to be fit. Both images from the
1258 primary beam hkl = (0, 0, 0) and from a symmetric reflection
1259 hkl = (h, k, l) need to be given for a successful run.
1260
1261 Parameters
1262 ----------
1263 sampleang : array-like
1264 sample rocking angle (needed to align the reflections (same rotation
1265 direction as inner detector rotation)) other sample angle are not
1266 allowed to be changed during the scans
1267 angle1 : array-like
1268 outer detector arm angle
1269 angle2 : array-like
1270 inner detector arm angle
1271 ccdimages : array-like
1272 images of the ccd taken at the angles given above
1273 hkls : list or array-like
1274 hkl values for every image
1275 experiment : Experiment
1276 Experiment class object needed to get the UB matrix for the hkl peak
1277 treatment
1278 material : Crystal
1279 material used as reference crystal
1280 detaxis : list of str
1281 detector arm rotation axis; default: ['z+', 'y-']
1282 r_i : str
1283 primary beam direction [xyz][+-]; default 'x+'
1284
1285 plot : bool, optional
1286 flag to determine if results and intermediate results should be
1287 plotted; default: True
1288 cut_off : float, optional
1289 cut off intensity to decide if image is used for the determination or
1290 not; default: 0.7 = 70%
1291 start : tuple, optional
1292 sequence of start values of the fit for parameters, which can not be
1293 estimated automatically or might want to be fixed. These are: pwidth1,
1294 pwidth2, distance, tiltazimuth, tilt, detector_rotation,
1295 outerangle_offset, sampletilt, sampletiltazimuth, wavelength. By
1296 default (None, None, 1, 0, 0, 0, 0, 0, 0, 'config').
1297 fix : tuple of bool
1298 fix parameters of start (default: (False, False, True, False, False,
1299 False, False, False, False, False)) It is strongly recommended to
1300 either fix the distance or the pwidth1, 2 values.
1301 fig : Figure, optional
1302 matplotlib figure used for plotting the error default: None (creates
1303 own figure)
1304 plotlog : bool
1305 flag to specify if the created error plot should be on log-scale
1306 nwindow : int
1307 window size for determination of the center of mass position after the
1308 center of mass of every full image is determined, the center of mass is
1309 determined again using a window of size nwindow in order to reduce the
1310 effect of hot pixels.
1311 debug : bool
1312 flag to specify that you want to see verbose output and saving of
1313 images to show if the CEN determination works
1314 """
1315 if plot:
1316 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.area_'
1317 'detector_calib_hkl')
1318
1319 if start[-1] == 'config':
1320 start[-1] = config.WAVELENGTH
1321 elif isinstance(start[-1], str):
1322 start[-1] = utilities.wavelength(start[-1])
1323
1324 t0 = time.time()
1325 Npoints = len(angle1)
1326 if debug:
1327 print("number of given images: %d" % Npoints)
1328
1329 # determine center of mass position from detector images also use only
1330 # images with an intensity larger than cut_off of the average intensity.
1331 # the image selection is only performed for images in the primary beam
1332 n1 = numpy.zeros(0, dtype=numpy.double)
1333 n2 = n1
1334 ang1 = n1
1335 ang2 = n1
1336 sang = n1
1337 usedhkls = []
1338
1339 avg = 0
1340 imgpbcnt = 0
1341 for i in range(Npoints):
1342 if (numpy.all(hkls[i] == (0, 0, 0))):
1343 avg += numpy.sum(ccdimages[i])
1344 imgpbcnt += 1
1345
1346 if imgpbcnt > 0:
1347 avg /= float(imgpbcnt)
1348 else:
1349 raise ValueError("XU.analyis.area_detector_calib_hkl: no required "
1350 "images in the primary beam given!")
1351 (N1, N2) = ccdimages[0].shape
1352
1353 if debug:
1354 print("average intensity per image in the primary beam: %.1f" % avg)
1355
1356 for i in range(Npoints):
1357 if debug and i == 0:
1358 print("angle1, angle2, cen1, cen2")
1359 img = ccdimages[i]
1360 if ((numpy.sum(img) > cut_off * avg) or
1361 (numpy.all(hkls[i] != (0, 0, 0)))):
1362 cen1, cen2 = _peak_position(img, nwindow, plot=debug and plot)
1363
1364 n1 = numpy.append(n1, cen1)
1365 n2 = numpy.append(n2, cen2)
1366 ang1 = numpy.append(ang1, angle1[i])
1367 ang2 = numpy.append(ang2, angle2[i])
1368 sang = numpy.append(sang, sampleang[i])
1369 usedhkls.append(hkls[i])
1370 if debug:
1371 print("%8.3f %8.3f \t%.2f %.2f" % (angle1[i], angle2[i],
1372 cen1, cen2))
1373
1374 Nused = len(ang1)
1375 usedhkls = numpy.array(usedhkls)
1376
1377 if debug:
1378 print("Nused / Npoints: %d / %d" % (Nused, Npoints))
1379
1380 # guess initial parameters
1381 n10 = numpy.zeros(0, dtype=numpy.double)
1382 n20 = n10
1383 ang10 = n10
1384 ang20 = n10
1385 for i in range(Nused):
1386 if numpy.all(usedhkls[i] == (0, 0, 0)):
1387 n10 = numpy.append(n10, n1[i])
1388 n20 = numpy.append(n20, n2[i])
1389 ang10 = numpy.append(ang10, ang1[i])
1390 ang20 = numpy.append(ang20, ang2[i])
1391
1392 detdir1, detdir2 = _determine_detdir(ang10 - start[3], ang20, n10, n20,
1393 detaxis, r_i)
1394
1395 epslist = []
1396 paramlist = []
1397 epsmin = numpy.inf
1398 fitmin = None
1399
1400 print("tiltaz tilt detrot offset sampletilt+azimuth wavelength: "
1401 "error (relative) (fittime)")
1402 print("------------------------------------------------------------")
1403 # find optimal detector rotation (however keep other parameters free)
1404 detrot = start[5]
1405 if not fix[5]:
1406 for detrotstart in numpy.linspace(start[5] - 1, start[5] + 1, 20):
1407 start = start[:5] + (detrotstart,) + start[6:]
1408 eps, param, fit = _area_detector_calib_fit2(
1409 sang, ang1, ang2, n1, n2, usedhkls, experiment, material,
1410 detaxis, r_i, detdir1, detdir2, start=start, fix=fix,
1411 full_output=True)
1412 epslist.append(eps)
1413 paramlist.append(param)
1414 if epslist[-1] < epsmin:
1415 epsmin = epslist[-1]
1416 parammin = param
1417 fitmin = fit
1418 detrot = param[7]
1419 if debug:
1420 print("single fit")
1421 print(param)
1422
1423 Ntiltaz = 1 if fix[3] else 5
1424 Ntilt = 1 if fix[4] else 6
1425 Noffset = 1 if fix[6] else 100
1426 if fix[6]:
1427 Ntilt = Ntilt * 8 if not fix[4] else Ntilt
1428 Ntiltaz = Ntiltaz * 7 if not fix[3] else Ntiltaz
1429
1430 startparam = start[:5] + (detrot,) + start[6:]
1431
1432 Ntot = Ntiltaz * Ntilt * Noffset
1433 ict = 0
1434 for tiltazimuth in numpy.linspace(startparam[3] if fix[3] else 0, 360,
1435 Ntiltaz, endpoint=False):
1436 for tilt in numpy.linspace(startparam[4] if fix[4] else 0, 4, Ntilt):
1437 for offset in numpy.linspace(
1438 startparam[6] if fix[6] else - 3 + startparam[6],
1439 3 + startparam[6], Noffset):
1440 t1 = time.time()
1441 start = (start[:3] + (tiltazimuth, tilt, detrot, offset) +
1442 start[7:])
1443 eps, param, fit = _area_detector_calib_fit2(
1444 sang, ang1, ang2, n1, n2, usedhkls, experiment, material,
1445 detaxis, r_i, detdir1, detdir2, start=start, fix=fix,
1446 full_output=True)
1447 epslist.append(eps)
1448 paramlist.append(param)
1449 t2 = time.time()
1450 print("%d/%d\t%6.1f %6.2f %8.3f %8.3f %8.3f %7.2f %8.4f: "
1451 "%10.4e (%4.2f) (%5.2fsec)"
1452 % ((ict, Ntot) + start[3:] +
1453 (epslist[-1], epslist[-1] / epsmin, t2 - t1)))
1454 ict += 1
1455
1456 if epslist[-1] < epsmin:
1457 print("************************")
1458 print("new global minimum found")
1459 epsmin = epslist[-1]
1460 parammin = param
1461 fitmin = fit
1462 print("new best parameters: %.2f %.2f %10.4e %10.4e %8.4f "
1463 "%.1f %.2f %.3f %.3f %.3f %.2f %.4f" % parammin)
1464 print("************************\n")
1465
1466 (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
1467 outerangle_offset, stilt, stazimuth, wavelength) = parammin
1468
1469 if plot:
1470 if fig:
1471 plt.figure(fig.number)
1472 else:
1473 figlabel = "CCD Calib fit %d"
1474 i = 1
1475 while figlabel % i in plt.get_figlabels():
1476 i += 1
1477 plt.figure(figlabel % i)
1478 nparams = numpy.array(paramlist)
1479 neps = numpy.array(epslist)
1480 labels = (
1481 'cch1 (1)',
1482 'cch2 (1)',
1483 r'pwidth1 ($\mu$m@1m)',
1484 r'pwidth2 ($\mu$m@1m)',
1485 'distance (m)',
1486 'tiltazimuth (deg)',
1487 'tilt (deg)',
1488 'detrot (deg)',
1489 'outerangle offset (deg)',
1490 'sample tilt (deg)',
1491 'st azimuth (deg)',
1492 'wavelength (AA)')
1493 xscale = (1., 1., 1.e6, 1.e6, 1., 1., 1., 1., 1., 1., 1., 1.)
1494 for p in range(12):
1495 ax = plt.subplot(3, 4, p + 1)
1496 if plotlog:
1497 plt.semilogy(nparams[:, p] * xscale[p], neps, 'k.')
1498 else:
1499 plt.scatter(nparams[:, p] * xscale[p], neps, c=nparams[:, -1],
1500 s=10, marker='o', cmap=plt.cm.gnuplot,
1501 edgecolor='none')
1502 plt.xlabel(labels[p])
1503 if plotlog:
1504 plt.semilogy(parammin[p] * xscale[p], epsmin, 'ko',
1505 ms=8, mew=2.5, mec='k', mfc='w')
1506 else:
1507 plt.plot(parammin[p] * xscale[p], epsmin, 'ko',
1508 ms=8, mew=2.5, mec='k', mfc='w')
1509 plt.ylim(epsmin * 0.7, epsmin * 2.)
1510 plt.locator_params(nbins=4, axis='x')
1511 if p > 1:
1512 if fix[p-2]:
1513 ax.set_facecolor('0.85')
1514 plt.tight_layout()
1515
1516 if config.VERBOSITY >= config.INFO_LOW:
1517 print("total time needed for fit: %.2fsec" % (time.time() - t0))
1518 print("fitted parameters: epsilon: %10.4e (%d,%s) "
1519 % (epsmin, fitmin.info, repr(fitmin.stopreason)))
1520 print("param: (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, "
1521 "tilt, detrot, outerangle_offset, sampletilt, stazimuth, "
1522 "wavelength)")
1523 print("param: %.2f %.2f %10.4e %10.4e %.4f %.1f %.2f %.3f %.3f %.3f "
1524 "%.2f %.4f" % (cch1, cch2, pwidth1, pwidth2, distance,
1525 tiltazimuth, tilt, detrot, outerangle_offset,
1526 stilt, stazimuth, wavelength))
1527
1528 if config.VERBOSITY > 0:
1529 print("please check the resulting data (consider setting plot=True)")
1530 print("detector rotation axis / primary beam direction (given by user)"
1531 ": %s / %s" % (repr(detaxis), r_i))
1532 print("detector pixel directions / distance: %s %s / %e"
1533 % (detdir1, detdir2, distance))
1534 print("\tdetector initialization with: init_area('%s', '%s', "
1535 "cch1=%.2f, cch2=%.2f, Nch1=%d, Nch2=%d, pwidth1=%.4e, "
1536 "pwidth2=%.4e, distance=%.5f, detrot=%.3f, tiltazimuth=%.1f, "
1537 "tilt=%.3f)" % (detdir1, detdir2, cch1, cch2, N1, N2, pwidth1,
1538 pwidth2, distance, detrot, tiltazimuth, tilt))
1539 print("AND ALWAYS USE an (additional) OFFSET of %.4fdeg in the "
1540 "OUTER DETECTOR ANGLE!" % (outerangle_offset))
1541
1542 return (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth,
1543 tilt, detrot, outerangle_offset, stilt, stazimuth, wavelength), eps
1544
1545
1546 def _area_detector_calib_fit2(sang, ang1, ang2, n1, n2, hkls, experiment,
1547 material, detaxis, r_i, detdir1, detdir2,
1548 start=(None, None, 1, 0, 0, 0, 0, 0, 0, 1.0),
1549 fix=(False, False, True, False, False, False,
1550 False, False, False, False),
1551 full_output=False, debug=False):
1552 """
1553 INTERNAL FUNCTION
1554 function to calibrate the detector parameters of an area detector
1555 it determines the detector tilt possible rotations and offsets in the
1556 detector arm angles
1557
1558 Parameters
1559 ----------
1560 sang : array-like
1561 sample rocking angle (rotation direction of inner detector rotation)
1562 angle1 : array-like
1563 outer detector arm angle
1564 angle2 : array-like
1565 inner detector arm angle
1566 n1, n2 : array-like
1567 pixel number at which the beam was observed
1568 hkls : tuple or list
1569 Miller indices of the reflection were the images were taken (use
1570 (0, 0, 0)) for primary beam
1571 experiment : Experiment
1572 Experiment class object needed to get the UB matrix needed for the hkl
1573 peak treatment
1574 material : Crystal
1575 material used as reference crystal
1576 detaxis : str
1577 detector arm rotation axis default: ['z+', 'y-']
1578 detdir1, detdir2 : str
1579 detector pixel directions of first and second pixel coordinates;
1580 e.g. 'y+'
1581 r_i : str
1582 primary beam direction [xyz][+-] default 'x+'
1583
1584 start : tuple or list, optional
1585 sequence of start values of the fit for parameters, which can not be
1586 estimated automatically. these are: pwidth1, pwidth2, distance,
1587 tiltazimuth, tilt, detector_rotation, outerangle_offset, sampletilt,
1588 sampletiltazimuth, wavelength.
1589 By default: (None, None, 1, 0, 0, 0, 0, 0, 0, 1.0)
1590 fix : tuple or list, optional
1591 fix parameters of start
1592 full_output : bool, optional
1593 flag to tell if to return fit object with final parameters and detector
1594 directions
1595 debug : bool, optional
1596 flag to tell if you want to see debug output of the script (switch this
1597 to true only if you can handle it :))
1598
1599 Returns
1600 -------
1601 float
1602 final epsilon of the fit
1603 param : list, optional
1604 if full_output: fit parameters
1605 fit : object, optional
1606 if full_output: fit object
1607 """
1608
1609 def areapixel2(params, detectorDir1, detectorDir2, r_i, detectorAxis,
1610 *args, **kwargs):
1611 """
1612 angular to momentum space conversion for pixels of an area detector the
1613 center pixel is in direction of self.r_i when detector angles are zero
1614
1615 the detector geometry must be given to the routine
1616
1617 Parameters
1618 ----------
1619 params : list or tuple
1620 parameters of the detector calibration model
1621 (cch1, cch2, pwidth1, pwidth2, tiltazimuth, tilt, detrot):
1622 cch1, 2: center pixel, in direction of r_i at zero detectorAngles;
1623 pwidth1, 2: width of one pixel (same unit as distance);
1624 distance: distance of center pixel from center of rotation;
1625 tiltazimuth: direction of the tilt vector in the detector plane (in
1626 degree);
1627 tilt: tilt of the detector plane around an axis normal to the
1628 direction given by the tiltazimuth;
1629 detrot: detector rotation around the primary beam direction as
1630 given by r_i
1631 detectorDir1 : str
1632 direction of the detector (along the pixel direction 1); e.g. 'z+'
1633 means higher pixel numbers at larger z positions
1634 detectorDir2 : str
1635 direction of the detector (along the pixel direction 2); e.g. 'x+'
1636 r_i : str
1637 primary beam direction e.g. 'x+'
1638 detectorAxis : list or tuple
1639 detector circles; e.g. ['z+', 'y-'] would mean a detector arm with
1640 a two rotations
1641 *args : array-like
1642 sample, detector angles and channel numbers;
1643 *sAngle* sample rocking angle as numpy array, lists or Scalars.
1644 *dAngles* as numpy array, lists or Scalars. In total
1645 len(detectorAxis) values must be given starting with the outer most
1646 circle. All arguments must have the same shape or length.
1647 *channel numbers* `n1` and `n2` where the primary beam hits the
1648 detector with the same length as the detector values
1649
1650 delta : list of array-like, optional
1651 giving delta angles to correct the given ones for misalignment.
1652 delta must be an numpy array or list of len(*dAngles). used angles
1653 are than *args - delta
1654 UB : array-like, optional
1655 orientation matrix of the sample
1656 wl : float or str, optional
1657 x-ray wavelength in angstroem
1658 deg : bool, optional
1659 flag to tell if angles are passed as degree (default: True)
1660
1661 Returns
1662 -------
1663 ndarray
1664 reciprocal space position of detector pixels n1, n2 in a
1665 numpy.ndarray of shape (len(args) , 3)
1666 """
1667
1668 # check detector circle argument
1669 if isinstance(detectorAxis, str):
1670 dAxis = list([detectorAxis])
1671 else:
1672 dAxis = list(detectorAxis)
1673
1674 _sampleAxis_str = dAxis[-1]
1675 # add detector rotation around primary beam
1676 dAxis += [r_i]
1677 _detectorAxis_str = ''
1678 for circ in dAxis:
1679 _detectorAxis_str += circ
1680
1681 Nd = len(dAxis)
1682 Nargs = Nd + 2 - 1 + 1
1683
1684 # check detectorDir
1685 _area_detdir1 = detectorDir1
1686 _area_detdir2 = detectorDir2
1687
1688 # parse parameter arguments
1689 _area_cch1 = float(params[0])
1690 _area_cch2 = float(params[1])
1691 _area_pwidth1 = float(params[2])
1692 _area_pwidth2 = float(params[3])
1693 _area_distance = float(params[4])
1694 _area_tiltazimuth = math.radians(params[5])
1695 _area_tilt = math.radians(params[6])
1696 _area_rot = float(params[7])
1697 _area_ri = xumath.getVector(r_i) * _area_distance
1698
1699 # kwargs
1700 wl = utilities.wavelength(kwargs.get('wl', 1.))
1701 deg = kwargs.get('deg', True)
1702 UB = kwargs.get('UB', numpy.identity(3))
1703
1704 delta = numpy.asarray(kwargs.get('delta', numpy.zeros(Nd)),
1705 dtype=numpy.double)
1706 if delta.size != Nd - 1 + 1:
1707 raise InputError("QConversionPixel: keyword argument delta "
1708 "does not have an appropriate shape")
1709
1710 # prepare angular arrays from *args
1711 # need one sample angle and one detector angle array
1712 if len(args) != Nargs:
1713 raise InputError("QConversionPixel: wrong amount (%d) of "
1714 "arguments given, number of arguments should be "
1715 "%d" % (len(args), Nargs))
1716
1717 try:
1718 Npoints = len(args[0])
1719 except (TypeError, IndexError):
1720 Npoints = 1
1721
1722 sAngles = numpy.array((), dtype=numpy.double)
1723 for i in range(1):
1724 arg = args[i]
1725 if isinstance(arg, numbers.Number):
1726 arg = numpy.array([arg], dtype=numpy.double)
1727 elif isinstance(arg, list):
1728 arg = numpy.array(arg, dtype=numpy.double)
1729 arg = arg - delta[i]
1730 sAngles = numpy.concatenate((sAngles, arg))
1731
1732 dAngles = numpy.array((), dtype=numpy.double)
1733 for i in range(1, Nd):
1734 arg = args[i]
1735 if isinstance(arg, numbers.Number):
1736 arg = numpy.array([arg], dtype=numpy.double)
1737 elif isinstance(arg, list):
1738 arg = numpy.array(arg, dtype=numpy.double)
1739 arg = arg - delta[i]
1740 dAngles = numpy.concatenate((dAngles, arg))
1741 # add detector rotation around primary beam
1742 dAngles = numpy.concatenate((
1743 dAngles,
1744 numpy.ones(arg.shape, dtype=numpy.double) * _area_rot))
1745
1746 # read channel numbers
1747 n1 = numpy.array((), dtype=numpy.double)
1748 n2 = numpy.array((), dtype=numpy.double)
1749
1750 arg = args[Nd]
1751 if isinstance(arg, numbers.Number):
1752 arg = numpy.array([arg], dtype=numpy.double)
1753 elif isinstance(arg, list):
1754 arg = numpy.array(arg, dtype=numpy.double)
1755 n1 = arg
1756
1757 arg = args[Nd + 1]
1758 if isinstance(arg, numbers.Number):
1759 arg = numpy.array([arg], dtype=numpy.double)
1760 elif isinstance(arg, list):
1761 arg = numpy.array(arg, dtype=numpy.double)
1762 n2 = arg
1763
1764 sAngles.shape = (1, Npoints)
1765 sAngles = sAngles.transpose()
1766 dAngles.shape = (Nd, Npoints)
1767 dAngles = dAngles.transpose()
1768
1769 if deg:
1770 sAngles = radians(sAngles)
1771 dAngles = radians(dAngles)
1772
1773 qpos = cxrayutilities.ang2q_conversion_area_pixel2(
1774 sAngles, dAngles, n1, n2, _area_ri, _sampleAxis_str,
1775 _detectorAxis_str, _area_cch1, _area_cch2, _area_pwidth1,
1776 _area_pwidth2, _area_detdir1, _area_detdir2, _area_tiltazimuth,
1777 _area_tilt, UB, wl, config.NTHREADS)
1778
1779 return qpos[:, 0], qpos[:, 1], qpos[:, 2]
1780
1781 def afunc(param, x, detectorDir1, detectorDir2, r_i, detectorAxis):
1782 """
1783 function for fitting the detector parameters
1784 basically this is a wrapper for the areapixel function
1785
1786 parameters
1787 ----------
1788 param : list or tuple
1789 fit parameters (cch1, cch2, pwidth1, pwidth2, distance,
1790 tiltazimuth, tilt, detrot, outerangle_offset, sampletilt,
1791 sampletiltazimuth, wavelength)
1792 x : array-like
1793 independent variables; contains (sang, angle1, angle2, n1, n2,
1794 hkls) with shape (8, Npoints)
1795 detectorDir1 : str
1796 direction of the detector (along the pixel direction 1); e.g. 'z+'
1797 means higher pixel numbers at larger z positions
1798 detectorDir2 : str
1799 direction of the detector (along the pixel direction 2); e.g. 'x+'
1800 r_i : str
1801 primary beam direction e.g. 'x+'
1802 detectorAxis : list or tuple
1803 detector circles; e.g. ['z+', 'y-'] would mean a detector arm with
1804 a two rotations
1805
1806 Returns
1807 -------
1808 ndarray
1809 reciprocal space position of detector pixels n1, n2 in a
1810 numpy.ndarray of shape (3, x.shape[1])
1811 """
1812
1813 sang = x[0, :]
1814 angle1 = x[1, :]
1815 angle2 = x[2, :]
1816 n1 = x[3, :]
1817 n2 = x[4, :]
1818 H = x[5, :]
1819 K = x[6, :]
1820 L = x[7, :]
1821
1822 # use only positive tilt and sample tilt
1823 param[6] = abs(param[6])
1824 param[9] = abs(param[9])
1825 wl = param[11]
1826 cp = math.cos(math.radians(param[10]))
1827 sp = math.sin(math.radians(param[10]))
1828 cc = math.cos(math.radians(param[9]))
1829 sc = math.sin(math.radians(param[9]))
1830
1831 # UB matrix due to tilt at symmetric peak
1832 U1 = numpy.array(((cp * cc, -sp, cp * sc),
1833 (sp * cc, cp, sp * sc),
1834 (-sc, 0, cc)), dtype=numpy.double)
1835 U2 = experiment._transform.matrix
1836 U = numpy.dot(U1, U2)
1837 B = material.B
1838 ubmat = numpy.dot(U, B)
1839
1840 (qx, qy, qz) = areapixel2(
1841 param[:-4], detectorDir1, detectorDir2, r_i, detectorAxis,
1842 sang, angle1, angle2, n1, n2, delta=[0, param[8], 0.],
1843 distance=1., UB=ubmat, wl=wl)
1844
1845 return (qx - H) ** 2 + (qy - K) ** 2 + (qz - L) ** 2
1846
1847 Npoints = len(ang1)
1848
1849 # guess initial parameters
1850 n10 = numpy.zeros(0, dtype=numpy.double)
1851 n20 = n10
1852 ang10 = n10
1853 ang20 = n10
1854 n1s = numpy.zeros(0, dtype=numpy.double)
1855 n2s = n1s
1856 ang1s = n1s
1857 ang2s = n1s
1858 sangs = n1s
1859
1860 for i in range(Npoints):
1861 if numpy.all(hkls[i] == (0, 0, 0)):
1862 n10 = numpy.append(n10, n1[i])
1863 n20 = numpy.append(n20, n2[i])
1864 ang10 = numpy.append(ang10, ang1[i])
1865 ang20 = numpy.append(ang20, ang2[i])
1866 else:
1867 n1s = numpy.append(n1s, n1[i])
1868 n2s = numpy.append(n2s, n2[i])
1869 ang1s = numpy.append(ang1s, ang1[i])
1870 ang2s = numpy.append(ang2s, ang2[i])
1871 sangs = numpy.append(sangs, sang[i])
1872
1873 # center channel and detector pixel direction and pixel size
1874 (s1, i1), r1 = xumath.linregress(ang10 - start[3], n10)
1875 (s2, i2), r2 = xumath.linregress(ang10 - start[3], n20)
1876 (s3, i3), r3 = xumath.linregress(ang20, n10)
1877 (s4, i4), r4 = xumath.linregress(ang20, n20)
1878
1879 distance = start[2]
1880 if ((r1 + r2 > r3 + r4) and r1 > r2) or ((r1 + r2 < r3 + r4) and r3 < r4):
1881 cch1 = i1
1882 cch2 = i4
1883 pwidth1 = 2 * distance / numpy.abs(s1) * math.tan(math.radians(0.5))
1884 pwidth2 = 2 * distance / numpy.abs(s4) * math.tan(math.radians(0.5))
1885 else:
1886 cch1 = i3
1887 cch2 = i2
1888 pwidth1 = 2 * distance / numpy.abs(s3) * math.tan(math.radians(0.5))
1889 pwidth2 = 2 * distance / numpy.abs(s2) * math.tan(math.radians(0.5))
1890 if numpy.isscalar(start[0]):
1891 pwidth1 = start[0]
1892 if numpy.isscalar(start[1]):
1893 pwidth2 = start[1]
1894 if numpy.isscalar(start[0]) or numpy.isscalar(start[1]):
1895 # find biggest correlation and recalculate distance
1896 idxmax = numpy.argmax((r1, r2, r3, r4))
1897 s = (s1, s2, s3, s4)[idxmax]
1898 distance = abs(s) / math.tan(math.radians(0.5)) / 2
1899 distance *= pwidth1 if idxmax < 2 else pwidth2
1900 tilt = abs(start[4])
1901 tiltazimuth = start[3]
1902 detrot = start[5]
1903 outerangle_offset = start[6]
1904 sampletilt = start[7]
1905 stazimuth = start[8]
1906 wavelength = start[9]
1907 # parameters for the fitting
1908 param = (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
1909 outerangle_offset, sampletilt, stazimuth, wavelength)
1910
1911 # determine better start values for sample tilt and azimuth
1912 (qx, qy, qz) = areapixel2(
1913 param[:-4], detdir1, detdir2, r_i, detaxis, sangs, ang1s, ang2s,
1914 n1s, n2s, delta=[0, param[8], 0.], wl=wavelength)
1915 if debug:
1916 print("average qx: %.3f(%.3f)" % (numpy.average(qx), numpy.std(qx)))
1917 print("average qy: %.3f(%.3f)" % (numpy.average(qy), numpy.std(qy)))
1918 print("average qz: %.3f(%.3f)" % (numpy.average(qz), numpy.std(qz)))
1919
1920 qvecav = (numpy.average(qx), numpy.average(qy), numpy.average(qz))
1921 sampletilt = xumath.VecAngle(experiment.Transform(experiment.ndir),
1922 qvecav, deg=True)
1923 stazimuth = -xumath.VecAngle(
1924 experiment.Transform(experiment.idir),
1925 qvecav - xumath.VecDot(experiment.Transform(experiment.ndir), qvecav) *
1926 experiment.Transform(experiment.ndir), deg=True)
1927 param = (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
1928 outerangle_offset, sampletilt, stazimuth, wavelength)
1929
1930 if debug:
1931 print("initial parameters: ")
1932 print("primary beam / detector pixel directions / distance: %s / "
1933 "%s %s / %e" % (r_i, detdir1, detdir2, distance))
1934 print("param: (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, "
1935 "tilt, detrot, outerangle_offset, sampletilt, stazimuth, "
1936 "wavelength)")
1937 print("param: %.2f %.2f %10.4e %10.4e %.3f %.1f %.2f %.3f %.3f %.3f "
1938 "%.2f %.4f" % param)
1939
1940 # set data
1941 x = numpy.empty((8, Npoints), dtype=numpy.double)
1942 x[0, :] = sang
1943 x[1, :] = ang1
1944 x[2, :] = ang2
1945 x[3, :] = n1
1946 x[4, :] = n2
1947 x[5, :] = hkls[:, 0]
1948 x[6, :] = hkls[:, 1]
1949 x[7, :] = hkls[:, 2]
1950
1951 data = odr.Data(x, y=1)
1952 # define model for fitting
1953 model = odr.Model(afunc, extra_args=(detdir1, detdir2, r_i, detaxis),
1954 implicit=True)
1955 # check if parameters need to be fixed
1956 ifixb = ()
1957 for i in range(len(fix)):
1958 ifixb += (int(not fix[i]),)
1959
1960 my_odr = odr.ODR(data, model, beta0=param, ifixb=(1, 1) + ifixb,
1961 ifixx=(0, 0, 0, 0, 0, 0, 0, 0),
1962 stpb=(0.4, 0.4, pwidth1/50., pwidth2/50., distance/1000,
1963 2, 0.125, 0.01, 0.01, 0.01, 1., 0.0001),
1964 sclb=(1/abs(cch1), 1/abs(cch2), 1/pwidth1,
1965 1/pwidth2, 1/distance, 1/90., 1/0.2, 1/0.2, 1/0.2,
1966 1/0.1, 1/90., 1.),
1967 maxit=200, ndigit=12, sstol=1e-11, partol=1e-11)
1968 if debug:
1969 my_odr.set_iprint(final=1)
1970 my_odr.set_iprint(iter=2)
1971
1972 fit = my_odr.run()
1973
1974 (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt, detrot,
1975 outerangle_offset, sampletilt, stazimuth, wavelength) = fit.beta
1976 # fix things in parameters
1977 tiltazimuth = tiltazimuth % 360.
1978 stazimuth = stazimuth % 360.
1979 tilt = abs(tilt)
1980 sampletilt = abs(sampletilt)
1981
1982 final_q = afunc([cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, tilt,
1983 detrot, outerangle_offset, sampletilt, stazimuth,
1984 wavelength],
1985 x, detdir1, detdir2, r_i, detaxis)
1986 final_error = numpy.mean(final_q)
1987
1988 if debug:
1989 print("fitted parameters: (%e, %d, %s) " % (final_error, fit.info,
1990 repr(fit.stopreason)))
1991 print("primary beam / detector pixel directions / distance: %s / %s "
1992 "%s / %e" % (r_i, detdir1, detdir2, distance))
1993 print("param: (cch1, cch2, pwidth1, pwidth2, distance, tiltazimuth, "
1994 "tilt, detrot, outerangle_offset, sampletilt, sampletiltazimuth,"
1995 " wavelength)")
1996 print("param: %.2f %.2f %10.4e %10.4e %.3f %.1f %.2f %.3f %.3f %.3f "
1997 "%.2f %.4f" % (cch1, cch2, pwidth1, pwidth2, distance,
1998 tiltazimuth, tilt, detrot, outerangle_offset,
1999 sampletilt, stazimuth, wavelength))
2000
2001 if full_output:
2002 return final_error, (cch1, cch2, pwidth1, pwidth2, distance,
2003 tiltazimuth, tilt, detrot, outerangle_offset,
2004 sampletilt, stazimuth, wavelength), fit
2005 else:
2006 return final_error
2007
2008
2009 #################################################
2010 def psd_refl_align(primarybeam, angles, channels, plot=True):
2011 """
2012 function which calculates the angle at which the sample
2013 is parallel to the beam from various angles and detector channels
2014 from the reflected beam. The function can be used during the half
2015 beam alignment with a linear detector.
2016
2017 Parameters
2018 ----------
2019 primarybeam : int
2020 primary beam channel number
2021 angles : list or array-like
2022 incidence angles
2023 channels : list or array-like
2024 corresponding detector channels
2025 plot : bool, optional
2026 flag to specify if a visualization of the fit is wanted default : True
2027
2028 Returns
2029 -------
2030 float
2031 angle at which the sample is parallel to the beam
2032
2033 Examples
2034 --------
2035 >>> psd_refl_align(500, [0, 0.1, 0.2, 0.3], [550, 600, 640, 700])
2036 """
2037 if plot:
2038 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.psd_refl_'
2039 'align')
2040
2041 p, rsq = xumath.linregress(numpy.asarray(channels), numpy.asarray(angles))
2042 zeropos = numpy.polyval(p, primarybeam)
2043
2044 if plot:
2045 xmin = min(min(channels), primarybeam)
2046 xmax = max(max(channels), primarybeam)
2047 ymin = min(min(angles), zeropos)
2048 ymax = max(max(angles), zeropos)
2049 # open new figure for the plot
2050 plt.figure()
2051 plt.plot(channels, angles, 'kx', ms=8., mew=2.)
2052 plt.plot([xmin - (xmax - xmin) * 0.1, xmax + (xmax - xmin) * 0.1],
2053 numpy.polyval(p,
2054 [xmin - (xmax - xmin) * 0.1,
2055 xmax + (xmax - xmin) * 0.1]),
2056 'g-',
2057 linewidth=1.5)
2058 ax = plt.gca()
2059 plt.grid()
2060 ax.set_xlim(xmin - (xmax - xmin) * 0.15, xmax + (xmax - xmin) * 0.15)
2061 ax.set_ylim(ymin - (ymax - ymin) * 0.15, ymax + (ymax - ymin) * 0.15)
2062 plt.vlines(primarybeam,
2063 ymin - (ymax - ymin) * 0.1,
2064 ymax + (ymax - ymin) * 0.1,
2065 linewidth=1.5)
2066 plt.xlabel("PSD Channel")
2067 plt.ylabel("sample angle")
2068 plt.tight_layout()
2069
2070 if config.VERBOSITY >= config.INFO_LOW:
2071 print("XU.analysis.psd_refl_align: sample is parallel to beam at "
2072 "goniometer angle %8.4f (R^2=%6.4f)" % (zeropos, rsq))
2073 return zeropos
2074
2075
2076 #################################################
2077 # miscut calculation from alignment in 2 and
2078 # more azimuths
2079 #################################################
2080 def miscut_calc(phi, aomega, zeros=None, omega0=None, plot=True):
2081 """
2082 function to calculate the miscut direction and miscut angle of a sample
2083 by fitting a sinusoidal function to the variation of the aligned
2084 omega values of more than two reflections.
2085 The function can also be used to fit reflectivity alignment values
2086 in various azimuths.
2087
2088 Parameters
2089 ----------
2090 phi : list, tuple or array-like
2091 azimuths in which the reflection was aligned (deg)
2092 aomega : list, tuple or array-like
2093 aligned omega values (deg)
2094 zeros : list, tuple or array-like, optional
2095 angles at which surface is parallel to the beam (deg). For the analysis
2096 the angles (aomega - zeros) are used.
2097 omega0 : float, optional
2098 if specified the nominal value of the reflection is not included as fit
2099 parameter, but is fixed to the specified value. This value is MANDATORY
2100 if ONLY TWO AZIMUTHs are given.
2101 plot : bool, optional
2102 flag to specify if a visualization of the fit is wanted.
2103 default: True
2104
2105 Returns
2106 -------
2107 omega0 : float
2108 the omega value of the reflection should be close to the nominal one
2109 phi0 : float
2110 the azimuth in which the primary beam looks upstairs
2111 miscut : float
2112 amplitude of the sinusoidal variation == miscut angle
2113 """
2114 if plot:
2115 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.miscut_'
2116 'calc')
2117
2118 if zeros is not None:
2119 om = (numpy.array(aomega) - numpy.array(zeros))
2120 else:
2121 om = numpy.array(aomega)
2122
2123 a = numpy.array(phi)
2124
2125 if omega0 is None:
2126 # first guess for the parameters
2127 # omega0, phi0, miscut
2128 p0 = (om.mean(), a[om.argmax()], om.max() - om.min())
2129
2130 def fitfunc(p, phi):
2131 return abs(p[2]) * \
2132 cos(radians(phi - (p[1] % 360.))) + p[0]
2133
2134 else:
2135 # first guess for the parameters
2136 p0 = (a[om.argmax()], om.max() - om.min()) # omega0, phi0, miscut
2137
2138 def fitfunc(p, phi):
2139 return abs(p[1]) * \
2140 cos(radians(phi - (p[0] % 360.))) + omega0
2141
2142 def errfunc(p, phi, om):
2143 return fitfunc(p, phi) - om
2144
2145 p1, success = optimize.leastsq(errfunc, p0, args=(a, om), maxfev=10000)
2146 if config.VERBOSITY >= config.INFO_ALL:
2147 print("xu.analysis.miscut_calc: leastsq optimization return value: "
2148 "%d" % success)
2149
2150 if plot:
2151 plt.figure()
2152 plt.plot(a, om, 'kx', mew=2, ms=8)
2153 linx = numpy.linspace(a.min() - 45, a.min() + 360 - 45, num=1000)
2154 plt.plot(linx, fitfunc(p1, linx), 'g-', linewidth=1.5)
2155 plt.grid()
2156 plt.xlabel("azimuth")
2157 plt.ylabel("aligned sample angle")
2158
2159 if omega0 is None:
2160 ret = [p1[0], p1[1] % 360., abs(p1[2])]
2161 else:
2162 ret = [omega0] + [p1[0] % 360., abs(p1[1])]
2163
2164 if config.VERBOSITY >= config.INFO_LOW:
2165 print("xu.analysis.miscut_calc: \n"
2166 "\t fitted reflection angle: %8.3f \n"
2167 "\t looking upstairs at phi: %8.2f \n"
2168 "\t miscut angle: %8.3f \n" % (ret[0], ret[1], ret[2]))
2169
2170 return ret
2171
2172
2173 #################################################
2174 # correct substrate Bragg peak position in
2175 # reciprocal space maps
2176 #################################################
2177 def fit_bragg_peak(om, tt, psd, omalign, ttalign, exphxrd, frange=(0.03, 0.03),
2178 peaktype='Gauss', plot=True):
2179 r"""
2180 helper function to determine the Bragg peak position in a reciprocal
2181 space map used to obtain the position needed for correction of the data.
2182 the determination is done by fitting a two dimensional Gaussian
2183 (xrayutilities.math.Gauss2d) or Lorentzian
2184 (xrayutilities.math.Lorentz2d)
2185
2186 PLEASE ALWAYS CHECK THE RESULT CAREFULLY!
2187
2188 Parameters
2189 ----------
2190 om, tt : array-like
2191 angular coordinates of the measurement either with size of psd or of
2192 psd.shape[0]
2193 psd : array-like
2194 intensity values needed for fitting
2195 omalign : float
2196 aligned omega value, used as first guess in the fit
2197 ttalign : float
2198 aligned two theta values used as first guess in the fit these values
2199 are also used to set the range for the fit: the peak should be within
2200 +/-frange\AA^{-1} of those values
2201 exphxrd : Experiment
2202 experiment class used for the conversion between angular and reciprocal
2203 space.
2204 frange : tuple of float, optional
2205 data range used for the fit in both directions (see above for details
2206 default:(0.03, 0.03) unit: \AA^{-1})
2207 peaktype : {'Gauss', 'Lorentz'}
2208 peak type to fit
2209 plot : bool, optional
2210 if True (default) function will plot the result of the fit in
2211 comparison with the measurement.
2212
2213 Returns
2214 -------
2215 omfit, ttfit : float
2216 fitted angular values
2217 params : list
2218 fit parameters (of the Gaussian/Lorentzian)
2219 covariance : ndarray
2220 covariance matrix of the fit parameters
2221 """
2222 if plot:
2223 plot, plt = utilities.import_matplotlib_pyplot('XU.analysis.fit_bragg'
2224 '_peak')
2225
2226 if peaktype == 'Gauss':
2227 func = xumath.Gauss2d
2228 elif peaktype == 'Lorentz':
2229 func = xumath.Lorentz2d
2230 else:
2231 raise InputError("peaktype must be either 'Gauss' or 'Lorentz'")
2232
2233 if om.size != psd.size:
2234 [qx, qy, qz] = exphxrd.Ang2Q.linear(om, tt)
2235 else:
2236 [qx, qy, qz] = exphxrd.Ang2Q(om, tt)
2237 [qxsub, qysub, qzsub] = exphxrd.Ang2Q(omalign, ttalign)
2238 params = [qysub, qzsub, 0.001, 0.001, psd.max(), 0, 0.]
2239 drange = [qysub - frange[0], qysub + frange[0], qzsub - frange[1],
2240 qzsub + frange[1]]
2241 params, covariance = xumath.fit_peak2d(
2242 qy.flatten(), qz.flatten(), psd.flatten(), params, drange,
2243 func, maxfev=10000)
2244 # correct params
2245 params[6] = params[6] % (numpy.pi)
2246 if params[5] < 0:
2247 params[5] = 0
2248
2249 [omfit, dummy, dummy, ttfit] = exphxrd.Q2Ang((0, params[0], params[1]),
2250 trans=False, geometry="real")
2251 if config.VERBOSITY >= config.INFO_LOW:
2252 print("XU.analysis.fit_bragg_peak:fitted peak angles: \n\tom =%8.4f\n"
2253 "\ttt =%8.4f" % (omfit, ttfit))
2254
2255 if plot:
2256 plt.figure()
2257 plt.clf()
2258 from ..gridder2d import Gridder2D
2259 gridder = Gridder2D(50, 50)
2260 mask = (qy.flatten() > drange[0]) * (qy.flatten() < drange[1]) * \
2261 (qz.flatten() > drange[2]) * (qz.flatten() < drange[3])
2262 gridder(qy.flatten()[mask], qz.flatten()[mask], psd.flatten()[mask])
2263 # calculate intensity which should be plotted
2264 INT = utilities.maplog(gridder.data.transpose(), 4, 0)
2265 QXm = gridder.xmatrix
2266 QZm = gridder.ymatrix
2267 cl = plt.contour(gridder.xaxis, gridder.yaxis,
2268 utilities.maplog(func(QXm, QZm, *params), 4, 0).T,
2269 8, colors='k', linestyles='solid')
2270 cf = plt.contourf(gridder.xaxis, gridder.yaxis, INT, 35)
2271 cf.collections[0].set_label('data')
2272 cl.collections[0].set_label('fit')
2273 # plt.legend() # for some reason not working?
2274 plt.colorbar(extend='min')
2275 plt.title("plot shows only coarse data! fit used raw data!")
2276
2277 return omfit, ttfit, params, covariance
+0
-172
xrayutilities/config.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module to parse xrayutilities user-specific config file
19 the parsed values are provide as global constants for the use
20 in other parts of xrayutilities. The config file with the default constants
21 is found in the python installation path of xrayutilities. It is however not
22 recommended to change things there, instead the user-specific config file
23 ~/.xrayutilities.conf or the local xrayutilities.conf file should be used.
24 """
25
26 import os.path
27 from ast import literal_eval
28
29 import numpy
30
31 from . import __path__, utilities_noconf
32
33 try: # Python-3
34 import configparser
35 except ImportError: # Python-2
36 import ConfigParser as configparser
37
38
39 # so far parsed config variables are
40 #
41 # wavelength
42 # energy
43 # verbosity
44 # nthreads
45 # dynlow
46 # dynhigh
47 # epsilon
48 # database filename for atomic structure factors
49 # kappa_plane and kappa_angle
50
51 xuParser = configparser.ConfigParser()
52 xuParser.optionxform = str
53
54
55 def trytomake(obj, key, typefunc):
56 try:
57 obj[key] = typefunc(obj[key])
58 except KeyError:
59 pass
60
61
62 # read global default values for configuration variables
63 with open(os.path.join(__path__[0], "xrayutilities_default.conf")) as conffile:
64 xuParser.readfp(conffile)
65
66 # read user configuration and local configuration if available
67 cfiles = xuParser.read([
68 os.path.expanduser(os.path.join("~", ".xrayutilities.conf")),
69 "xrayutilities.conf"])
70
71 # set global variables according to configuration
72 sect = "xrayutilities"
73 INFO_LOW = xuParser.getint(sect, "info_low")
74 INFO_ALL = xuParser.getint(sect, "info_all")
75 DEBUG = xuParser.getint(sect, "debug")
76
77 VERBOSITY = xuParser.getint(sect, "verbosity")
78 try:
79 WAVELENGTH = xuParser.getfloat(sect, "wavelength")
80 except ValueError:
81 WAVELENGTH = xuParser.get(sect, "wavelength")
82 except configparser.NoOptionError:
83 pass
84
85 try:
86 ENERGY = xuParser.getfloat(sect, "energy")
87 except ValueError:
88 ENERGY = xuParser.get(sect, "energy")
89 except configparser.NoOptionError:
90 ENERGY = utilities_noconf.lam2en(utilities_noconf.wavelength(WAVELENGTH))
91 WAVELENGTH = utilities_noconf.en2lam(utilities_noconf.energy(ENERGY))
92
93 # number of threads in parallel section of c-code
94 NTHREADS = xuParser.getint(sect, "nthreads")
95
96 # default parameters for the maplog function
97 DYNLOW = xuParser.getfloat(sect, "dynlow")
98 DYNHIGH = xuParser.getfloat(sect, "dynhigh")
99
100 # small number needed for error checks
101 EPSILON = xuParser.getfloat(sect, "epsilon")
102
103 # name of the database with atomic scattering factors
104 DBNAME = xuParser.get(sect, "dbname")
105
106 # kappa goniometer specific config parameters
107 KAPPA_PLANE = xuParser.get(sect, "kappa_plane")
108 KAPPA_ANGLE = xuParser.getfloat(sect, "kappa_angle")
109
110 # parser Powder profile related variables
111 POWDER = dict()
112
113 subsec = 'classoptions'
114 POWDER[subsec] = dict(xuParser.items("powder"))
115 for k in ('oversampling',):
116 trytomake(POWDER[subsec], k, int)
117 for k in ('gaussian_smoother_bins_sigma', 'window_width'):
118 trytomake(POWDER[subsec], k, float)
119
120 subsec = 'global'
121 POWDER[subsec] = dict(xuParser.items("powder.global"))
122 for k in ('diffractometer_radius', 'equatorial_divergence_deg'):
123 trytomake(POWDER[subsec], k, float)
124
125 subsec = 'emission'
126 POWDER[subsec] = dict(xuParser.items("powder.emission"))
127 for k in ('crystallite_size_gauss', 'crystallite_size_lor',
128 'strain_lor', 'strain_gauss'):
129 trytomake(POWDER[subsec], k, float)
130 for k in ('emiss_wavelengths', 'emiss_intensities',
131 'emiss_gauss_widths', 'emiss_lor_widths'):
132 trytomake(POWDER[subsec], k, literal_eval)
133 if 'emiss_wavelengths' in POWDER[subsec]:
134 POWDER[subsec]['emiss_wavelengths'] = tuple(
135 utilities_noconf.wavelength(wl) * 1e-10
136 for wl in POWDER[subsec]['emiss_wavelengths'])
137
138 subsec = 'axial'
139 POWDER[subsec] = dict(xuParser.items("powder.axial"))
140 for k in ('n_integral_points',):
141 trytomake(POWDER[subsec], k, int)
142 for k in ('slit_length_source', 'slit_length_target', 'length_sample',
143 'angI_deg', 'angD_deg'):
144 trytomake(POWDER[subsec], k, float)
145
146 subsec = 'absorption'
147 POWDER[subsec] = dict(xuParser.items("powder.absorption"))
148 for k in ('absorption_coefficient', 'sample_thickness'):
149 trytomake(POWDER[subsec], k, float)
150
151 subsec = 'si_psd'
152 POWDER[subsec] = dict(xuParser.items("powder.si_psd"))
153 for k in ('si_psd_window_bounds',):
154 trytomake(POWDER[subsec], k, literal_eval)
155
156 subsec = 'receiver_slit'
157 POWDER[subsec] = dict(xuParser.items("powder.receiver_slit"))
158 for k in ('slit_width', ):
159 trytomake(POWDER[subsec], k, float)
160
161 subsec = 'tube_tails'
162 POWDER[subsec] = dict(xuParser.items("powder.tube_tails"))
163 for k in ('main_width', 'tail_left', 'tail_right', 'tail_intens'):
164 trytomake(POWDER[subsec], k, float)
165
166 if VERBOSITY >= DEBUG:
167 print("XU.config: xrayutilities configuration files: %s" % repr(cfiles))
168 print("xrayutilities configuration:")
169 for (name, value) in xuParser.items("xrayutilities"):
170 print("%s: %s" % (name, value))
171 print("---")
+0
-58
xrayutilities/exception.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 xrayutilities derives its own exceptions which are raised
19 upon wrong input when calling one of xrayutilities functions.
20 none of the pre-defined exceptions is made for that purpose.
21 """
22
23 # other used Exception should mainly be the python built-in exceptions
24 #
25 # * TypeError
26 # Raised when an operation or function is applied to an object of
27 # inappropriate type
28 #
29 # * ValueError
30 # Raised when a operation or function receives an argument that
31 # has the right type but an inappropriate value
32 #
33 # * UserWarning
34 # Base class for warnings generated by user code
35
36
37 class InputError(Exception):
38
39 """
40 Exception raised for errors in the input.
41 Either wrong datatype not handled by TypeError or missing mandatory
42 keyword argument (Note that the obligation to give keyword arguments
43 might depend on the value of the arguments itself)
44
45 Parameters
46 ----------
47 expr : str
48 input expression in which the error occurred
49 msg : str
50 explanation of the error
51 """
52
53 def __init__(self, msg):
54 self.msg = msg
55
56 def __str__(self):
57 return self.msg
+0
-2489
xrayutilities/experiment.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17 # Copyright (C) 2012 Tanja Etzelstorfer <tanja.etzelstorfer@jku.at>
18
19 """
20 module helping with planning and analyzing experiments.
21 various classes are provided for describing experimental geometries,
22 calculationof angular coordinates of Bragg reflections, conversion of angular
23 coordinates to Q-space and determination of powder diffraction peak positions.
24
25 The strength of the module is the versatile QConversion module which can be
26 configured to describe almost any goniometer geometry.
27 """
28
29 import copy
30 import numbers
31 import re
32 import warnings
33
34 import numpy
35 from numpy.linalg import norm
36
37 # package internal imports
38 from . import config, cxrayutilities, math, utilities
39 from .exception import InputError
40
41 # python 2to3 compatibility
42 try:
43 basestring
44 except NameError:
45 basestring = str
46
47 # regular expression to check goniometer circle syntax
48 directionSyntax = re.compile("[xyz][+-]")
49 circleSyntaxDetector = re.compile("([xyz][+-])|(t[xyz])")
50 circleSyntaxSample = re.compile("[xyzk][+-]")
51
52
53 class QConversion(object):
54
55 """
56 Class for the conversion of angular coordinates to momentum space for
57 arbitrary goniometer geometries and X-ray energy. Both angular scans
58 (where some goniometer angles change during data acquisition) and energy
59 scans (where the energy is varied during acquisition) as well as mixed
60 cases can be treated.
61
62 the class is configured with the initialization and does provide three
63 distinct routines for conversion to momentum space for
64
65 * point detector: point(...) or __call__()
66 * linear detector: linear(...)
67 * area detector: area(...)
68
69 linear() and area() can only be used after the init_linear()
70 or init_area() routines were called
71 """
72
73 _valid_init_kwargs = {'en': 'x-ray energy',
74 'wl': 'x-ray wavelength',
75 'UB': 'orientation/orthonormalization matrix'}
76 _valid_call_kwargs = {'delta': 'angle offsets',
77 'wl': 'x-ray wavelength',
78 'en': 'x-ray energy',
79 'UB': 'orientation/orthonormalization matrix',
80 'deg': 'True if angles are in degrees',
81 'sampledis': 'sample displacement vector'}
82 _valid_linear_kwargs = {'Nav': 'number of channels for block-average',
83 'roi': 'region of interest'}
84
85 def __init__(self, sampleAxis, detectorAxis, r_i, **kwargs):
86 """
87 initialize QConversion object.
88 This means the rotation axis of the sample and detector circles
89 need to be given: starting with the outer most circle.
90
91 Parameters
92 ----------
93 sampleAxis : list or tuple
94 sample circles, e.g. ['x+', 'z+'] would mean two sample circles
95 whereas the outer one turns righthanded around the x axis and the
96 inner one turns righthanded around z.
97
98 detectorAxis : list or tuple
99 detector circles e.g. ['x-'] would mean a detector arm with a
100 single motor turning lefthanded around the x-axis.
101
102 r_i : array-like
103 vector giving the direction of the primary beam (length is relevant
104 only if translations are involved)
105
106 kwargs : dict, optional
107 optional keyword arguments
108 wl : float or str, optional
109 wavelength of the x-rays in Angstroem
110 en : float or str, optional
111 energy of the x-rays in electronvolt
112 UB : array-like, optional
113 matrix for conversion from (hkl) coordinates to Q of sample used to
114 determine not Q but (hkl) (default: identity matrix)
115 """
116
117 utilities.check_kwargs(kwargs, self._valid_init_kwargs,
118 self.__class__.__name__)
119
120 # initialize some needed variables
121 self._kappa_dir = numpy.array((numpy.nan, numpy.nan, numpy.nan))
122
123 # set/check sample and detector axis geometry
124 self._set_sampleAxis(sampleAxis)
125 self._set_detectorAxis(detectorAxis)
126
127 # r_i: primary beam direction
128 if isinstance(r_i, (list, tuple, numpy.ndarray)):
129 self.r_i = numpy.array(r_i, dtype=numpy.double)
130 if self.r_i.size != 3:
131 print("XU.QConversion: warning invalid primary beam "
132 "direction given -> using [0, 1, 0]")
133 self.r_i = numpy.array([0, 1, 0], dtype=numpy.double)
134 else:
135 raise TypeError("QConversion: invalid type of primary beam "
136 "direction r_i, must be tuple, list or "
137 "numpy.ndarray")
138
139 # kwargs
140 self._set_wavelength(kwargs.get("wl", config.WAVELENGTH))
141 if "en" in kwargs:
142 self._set_energy(kwargs["en"])
143
144 self._set_UB(kwargs.get('UB', numpy.identity(3)))
145
146 self._linear_init = False
147 self._area_init = False
148 self._area_detrotaxis_set = False
149
150 def _set_energy(self, energy):
151 self._en = utilities.energy(energy)
152 self._wl = utilities.en2lam(self._en)
153
154 def _set_wavelength(self, wl):
155 self._wl = utilities.wavelength(wl)
156 self._en = utilities.lam2en(self._wl)
157
158 def _get_energy(self):
159 return self._en
160
161 def _get_wavelength(self):
162 return self._wl
163
164 def _set_sampleAxis(self, sampleAxis):
165 """
166 property handler for _sampleAxis
167 checks if a syntactically correct list of sample circles is given
168
169 Parameters
170 ----------
171 sampleAxis : list or tuple
172 sample circles, e.g. ['x+', 'z+']
173 """
174
175 if isinstance(sampleAxis, (basestring, list, tuple)):
176 if isinstance(sampleAxis, basestring):
177 sAxis = list([sampleAxis])
178 else:
179 sAxis = list(sampleAxis)
180 for circ in sAxis:
181 if not isinstance(circ, basestring) or len(circ) != 2:
182 raise InputError("QConversion: incorrect sample circle "
183 "type or syntax (%s)" % repr(circ))
184 if not circleSyntaxSample.search(circ):
185 raise InputError("QConversion: incorrect sample circle "
186 "syntax (%s)" % circ)
187 if circ[0] == 'k': # determine kappa rotation axis
188 # determine reference direction
189 if config.KAPPA_PLANE[0] == 'x':
190 self._kappa_dir = numpy.array((1., 0, 0))
191 # turn reference direction
192 if config.KAPPA_PLANE[1] == 'y':
193 self._kappa_dir = math.ZRotation(
194 config.KAPPA_ANGLE)(self._kappa_dir)
195 elif config.KAPPA_PLANE[1] == 'z':
196 self._kappa_dir = math.YRotation(
197 -1 * config.KAPPA_ANGLE)(self._kappa_dir)
198 else:
199 raise TypeError("Qconverision init: invalid "
200 "kappa_plane in config!")
201 elif config.KAPPA_PLANE[0] == 'y':
202 self._kappa_dir = numpy.array((0, 1., 0))
203 # turn reference direction
204 if config.KAPPA_PLANE[1] == 'z':
205 self._kappa_dir = math.XRotation(
206 config.KAPPA_ANGLE)(self._kappa_dir)
207 elif config.KAPPA_PLANE[1] == 'x':
208 self._kappa_dir = math.ZRotation(
209 -1 * config.KAPPA_ANGLE)(self._kappa_dir)
210 else:
211 raise TypeError("Qconverision init: invalid "
212 "kappa_plane in config!")
213 elif config.KAPPA_PLANE[0] == 'z':
214 self._kappa_dir = numpy.array((0, 0, 1.))
215 # turn reference direction
216 if config.KAPPA_PLANE[1] == 'x':
217 self._kappa_dir = math.YRotation(
218 config.KAPPA_ANGLE)(self._kappa_dir)
219 elif config.KAPPA_PLANE[1] == 'y':
220 self._kappa_dir = math.XRotation(
221 -1 * config.KAPPA_ANGLE)(self._kappa_dir)
222 else:
223 raise TypeError("Qconverision init: invalid "
224 "kappa_plane in config!")
225 else:
226 raise TypeError("Qconverision init: invalid "
227 "kappa_plane in config!")
228
229 # rotation sense
230 if circ[1] == '-':
231 self._kappa_dir *= -1
232
233 if config.VERBOSITY >= config.DEBUG:
234 print("XU.QConversion: kappa_dir: (%5.3f %5.3f %5.3f)"
235 % tuple(self._kappa_dir))
236
237 else:
238 raise TypeError("Qconversion error: invalid type for sampleAxis, "
239 "must be str, list, or tuple")
240 self._sampleAxis = sAxis
241 self._sampleAxis_str = ''
242 for circ in self._sampleAxis:
243 self._sampleAxis_str += circ
244
245 def _get_sampleAxis(self):
246 """
247 property handler for _sampleAxis
248
249 Returns
250 -------
251 list
252 sample axes following the syntax /[xyzk][+-]/
253 """
254 return self._sampleAxis
255
256 def _set_detectorAxis(self, detectorAxis, detrot=False):
257 """
258 property handler for _detectorAxis_
259 checks if a syntactically correct list of detector circles is given
260
261 Parameters
262 ----------
263 detectorAxis : list or tuple
264 detector circles, e.g. ['x+']
265 detrot : bool, optional
266 flag to tell that the detector rotation is going to be added (used
267 internally to avoid double adding of detector rotation axis)
268 """
269 has_translations = False
270 if isinstance(detectorAxis, (basestring, list, tuple)):
271 if isinstance(detectorAxis, basestring):
272 dAxis = list([detectorAxis])
273 else:
274 dAxis = list(detectorAxis)
275 for circ in dAxis:
276 if not isinstance(circ, basestring) or len(circ) != 2:
277 raise InputError("QConversion: incorrect detector circle "
278 "type or syntax (%s)" % repr(circ))
279 if not circleSyntaxDetector.search(circ):
280 raise InputError("QConversion: incorrect detector circle "
281 "syntax (%s)" % circ)
282 if circ[0] == 't':
283 has_translations = True
284 else:
285 raise TypeError("Qconversion error: invalid type for "
286 "detectorAxis, must be str, list or tuple")
287 self._detectorAxis = dAxis
288 self._detectorAxis_str = ''
289 self._has_translations = has_translations
290 for circ in self._detectorAxis:
291 self._detectorAxis_str += circ
292 if detrot:
293 self._area_detrotaxis_set = True
294 else:
295 self._area_init = False
296
297 def _get_detectorAxis(self):
298 """
299 property handler for _detectorAxis
300
301 Returns
302 -------
303 list of detector axis following the syntax /[xyz][+-]/
304 """
305 return self._detectorAxis
306
307 def _get_UB(self):
308 return self._UB
309
310 def _set_UB(self, UB):
311 """
312 set the orientation matrix used in the Qconversion
313 needs to be (3, 3) matrix
314 """
315 tmp = numpy.array(UB)
316 if tmp.shape != (3, 3) and tmp.size != 9:
317 raise InputError("QConversion: incorrect shape of UB matrix "
318 "(shape: %s)" % str(tmp.shape))
319 else:
320 self._UB = tmp.reshape((3, 3))
321
322 energy = property(_get_energy, _set_energy)
323 wavelength = property(_get_wavelength, _set_wavelength)
324 sampleAxis = property(_get_sampleAxis, _set_sampleAxis)
325 detectorAxis = property(_get_detectorAxis, _set_detectorAxis)
326 UB = property(_get_UB, _set_UB)
327
328 def __str__(self):
329 pstr = 'QConversion geometry \n'
330 pstr += '---------------------------\n'
331 pstr += 'sample geometry(%d): ' % len(self._sampleAxis) + \
332 self._sampleAxis_str + '\n'
333 if self._sampleAxis_str.find('k') != -1:
334 pstr += ('kappa rotation axis (%5.3f %5.3f %5.3f)\n'
335 % tuple(self._kappa_dir))
336 pstr += 'detector geometry(%d): ' % len(self._detectorAxis) + \
337 self._detectorAxis_str + '\n'
338 pstr += ('primary beam direction: (%5.2f %5.2f %5.2f) \n'
339 % (self.r_i[0], self.r_i[1], self.r_i[2]))
340
341 if self._linear_init:
342 pstr += '\n linear detector initialized:\n'
343 pstr += 'linear detector mount direction: ' + \
344 self._linear_detdir + '\n'
345 pstr += ('number of channels/center channel: %d/%d\n'
346 % (self._linear_Nch, self._linear_cch))
347 pstr += ('distance to center of rotation/pixel width: '
348 '%10.4g/%10.4g\n'
349 % (self._linear_distance, self._linear_pixwidth))
350 chpdeg = 2 * self._linear_distance / \
351 self._linear_pixwidth * numpy.tan(numpy.radians(0.5))
352 pstr += 'corresponds to channel per degree: %8.2f\n' % (chpdeg)
353 if self._area_init:
354 pstr += '\n area detector initialized:\n'
355 pstr += 'area detector mount directions: %s/%s\n' % (
356 self._area_detdir1, self._area_detdir2)
357 pstr += ('number of channels/center channels: (%d,%d) / (%d,%d)\n'
358 % (self._area_Nch1, self._area_Nch2,
359 self._area_cch1, self._area_cch2))
360 pstr += ('distance to center of rotation/pixel width: '
361 '%10.4g/ (%10.4g,%10.4g) \n'
362 % (self._area_distance, self._area_pwidth1,
363 self._area_pwidth2))
364 chpdeg1 = 2 * self._area_distance / \
365 self._area_pwidth1 * numpy.tan(numpy.radians(0.5))
366 chpdeg2 = 2 * self._area_distance / \
367 self._area_pwidth2 * numpy.tan(numpy.radians(0.5))
368 pstr += 'corresponds to channel per degree: (%8.2f,%8.2f)\n' % (
369 chpdeg1, chpdeg2)
370
371 return pstr
372
373 def _checkInput(self, *args):
374 """
375 helper function to check that the arguments given to the QConversion
376 routines have the correct shape. It determines the number of points in
377 the input from the longest array/list given and checks that only inputs
378 combatible with this length are given
379
380 Parameters
381 ----------
382 args : list
383 arguments from the QConversion routine (sample and detector angles)
384
385 Returns
386 -------
387 Npoints : int
388 integer to tell the number of points given
389 """
390 np = 1
391 for a in args:
392 # optain size of input
393 if isinstance(a, numpy.ndarray):
394 anp = a.size
395 elif isinstance(a, (list, tuple)):
396 anp = len(a)
397 elif isinstance(a, numbers.Number):
398 anp = 1
399 else:
400 raise TypeError('QConversion: Input argument #%d has an '
401 'invalid type.' % args.index(a))
402 # check if the input field is valid
403 if anp > 1 and np == 1:
404 np = anp
405 elif anp > 1 and np != anp:
406 raise InputError('QConversion: Several input-arrays arguments '
407 'with different shape are an invalid input!')
408
409 return np
410
411 def _reshapeInput(self, npoints, delta, circles, *args, **kwargs):
412 """
413 helper function to reshape the input of arguments to
414 (len(args), npoints) The input arguments must be either scalars or are
415 of length npoints.
416
417 Parameters
418 ----------
419 npoints : int
420 length of the input arrays
421 delta : list or array-like
422 value to substract from the input arguments as array with len(args)
423 circles : list
424 list of circle description to decide if degree/radians conversion
425 is needed
426 args : list
427 input arrays and scalars
428 kwargs : dict, optional
429 optional keyword argument to tell if values of rotation axis should
430 be converted to radiants (name= 'deg', default=True)
431
432 Returns
433 -------
434 inarr : ndarray
435 array of shape (len(args), npoints) with the input arguments
436 retshape : tuple
437 shape of return values
438 """
439
440 inarr = numpy.empty((len(args), npoints), dtype=numpy.double)
441 retshape = (npoints,) # default value
442 deg2rad = kwargs.get('deg', True)
443
444 for i in range(len(args)):
445 arg = args[i]
446 if not isinstance(
447 arg,
448 (numbers.Number,
449 list,
450 tuple,
451 numpy.ndarray)):
452 raise TypeError("QConversion: invalid type for one of the "
453 "sample coordinates, must be scalar, list or "
454 "array")
455 elif isinstance(arg, numbers.Number):
456 arg = numpy.ones(npoints, dtype=numpy.double) * arg
457 elif isinstance(arg, (list, tuple)):
458 arg = numpy.array(arg, dtype=numpy.double)
459 else: # determine return value shape
460 retshape = arg.shape
461 arg = arg - delta[i]
462 if deg2rad and circleSyntaxSample.search(circles[i]):
463 inarr[i, :] = numpy.radians(numpy.ravel(arg))
464 else:
465 inarr[i, :] = numpy.ravel(arg)
466
467 return inarr, retshape
468
469 def _parse_common_kwargs(self, **kwargs):
470 """
471 parse common keyword arguments to QConversion calls
472
473 Parameters
474 ----------
475 delta : list or array-like, optional
476 delta angles to correct the given ones for misalignment.
477 delta must be an numpy array or list of len(*args). used angles are
478 than ``*args - delta``
479 UB : array-like, optional
480 matrix for conversion from (hkl) coordinates to Q of sample used to
481 determine not Q but (hkl) (default: self.UB)
482 wl : float or str, optional
483 x-ray wavelength in angstroem (default: self._wl)
484 en : float, optional
485 x-ray energy in eV (default is converted self._wl). both wavelength
486 and energy can also be an array which enables the QConversion for
487 energy scans. Note that the `en` keyword overrules the `wl`
488 keyword!
489 deg : bool, optional
490 flag to tell if angles are passed as degree (default: True)
491 sampledis : tuple or list or array-like
492 sample displacement vector in relative units of the detector
493 distance (default: (0, 0, 0))
494 """
495 flags = 0
496 if self._has_translations:
497 flags = utilities.set_bit(flags, 0)
498
499 Ns = len(self.sampleAxis)
500 Nd = len(self.detectorAxis)
501 if self._area_detrotaxis_set:
502 Nd -= 1 # do not consider detector rotation for point detector
503 Ncirc = Ns + Nd
504
505 # kwargs
506 wl = utilities.wavelength(kwargs.get('wl', self._wl))
507 if 'en' in kwargs:
508 wl = utilities.lam2en(utilities.energy(kwargs['en']))
509
510 deg = kwargs.get('deg', True)
511
512 delta = numpy.asarray(kwargs.get('delta', numpy.zeros(Ncirc)),
513 dtype=numpy.double)
514 if delta.size != Ncirc:
515 raise InputError("QConversion: keyword argument delta does "
516 "not have an appropriate shape")
517
518 UB = numpy.asarray(kwargs.get('UB', self.UB))
519
520 sd = numpy.asarray(kwargs.get('sampledis', [0, 0, 0]))
521 if 'sampledis' in kwargs:
522 flags = utilities.set_bit(flags, 2)
523
524 return Ns, Nd, Ncirc, wl, deg, delta, UB, sd, flags
525
526 def __call__(self, *args, **kwargs):
527 """
528 wrapper function for point(...)
529 """
530 return self.point(*args, **kwargs)
531
532 def point(self, *args, **kwargs):
533 """
534 angular to momentum space conversion for a point detector
535 located in direction of self.r_i when detector angles are zero
536
537 Parameters
538 ----------
539 args : ndarray, list or Scalars
540 sample and detector angles; in total `len(self.sampleAxis) +
541 len(detectorAxis)` must be given, always starting with the outer
542 most circle. all arguments must have the same shape or length but
543 can be mixed with Scalars (i.e. if an angle is always the same it
544 can be given only once instead of an array)
545
546 - sAngles :
547 sample circle angles, number of arguments must correspond to
548 len(self.sampleAxis)
549 - dAngles :
550 detector circle angles, number of arguments must correspond to
551 len(self.detectorAxis)
552
553 kwargs : dict, optional
554 optional keyword arguments
555 delta : list or array-like, optional
556 delta angles to correct the given ones for misalignment.
557 delta must be an numpy array or list of ``len(*args)``. used angles
558 are then ``*args - delta``
559 UB : array-like, optional
560 matrix for conversion from (hkl) coordinates to Q of sample used to
561 determine not Q but (hkl) (default: self.UB)
562 wl : float or str, optional
563 x-ray wavelength in angstroem (default: self._wl)
564 en : float, optional
565 x-ray energy in eV (default is converted self._wl). both wavelength
566 and energy can also be an array which enables the QConversion for
567 energy scans. Note that the `en` keyword overrules the `wl`
568 keyword!
569 deg : bool, optional
570 flag to tell if angles are passed as degree (default: True)
571 sampledis : tuple or list or array-like
572 sample displacement vector in relative units of the detector
573 distance (default: (0, 0, 0))
574
575 Returns
576 -------
577 ndarray
578 reciprocal space positions as numpy.ndarray with shape ``(N , 3)``
579 where `N` corresponds to the number of points given in the input
580 """
581
582 utilities.check_kwargs(kwargs, self._valid_call_kwargs, 'Ang2Q/point')
583
584 Ns, Nd, Ncirc, wl, deg, delta, UB, sd, flags = \
585 self._parse_common_kwargs(**kwargs)
586
587 # prepare angular arrays from *args
588 # need one sample angle and one detector angle array
589 if len(args) != Ncirc:
590 raise InputError("QConversion: wrong amount (%d) of arguments "
591 "given, number of arguments should be %d"
592 % (len(args), Ncirc))
593
594 # determine the number of points
595 a = args + (wl,)
596 Npoints = self._checkInput(*a)
597
598 # reshape/recast input arguments for sample and detector angles
599 sAngles, retshape = self._reshapeInput(Npoints, delta[:Ns],
600 self.sampleAxis, *args[:Ns],
601 deg=deg)
602 dAngles = self._reshapeInput(Npoints, delta[Ns:],
603 self.detectorAxis, *args[Ns:],
604 deg=deg)[0]
605 wl = numpy.ravel(self._reshapeInput(Npoints, (0, ), 'a',
606 wl, deg=False)[0])
607
608 sAngles = sAngles.transpose()
609 dAngles = dAngles.transpose()
610
611 sAxis = self._sampleAxis_str
612 dAxis = self._detectorAxis_str
613
614 if self._area_detrotaxis_set:
615 # do not consider detector rotation for point detector
616 dAxis = self._detectorAxis_str[:-2]
617 else:
618 dAxis = self._detectorAxis_str
619
620 if config.VERBOSITY >= config.DEBUG:
621 print("XU.QConversion: Ns, Nd: %d %d" % (Ns, Nd))
622 print("XU.QConversion: sAngles / dAngles %s / %s"
623 % (str(sAngles), str(dAngles)))
624
625 qpos = cxrayutilities.ang2q_conversion(
626 sAngles, dAngles, self.r_i, sAxis, dAxis,
627 self._kappa_dir, UB, sd, wl, config.NTHREADS, flags)
628
629 if Npoints == 1:
630 return (qpos[0, 0], qpos[0, 1], qpos[0, 2])
631 else:
632 return numpy.reshape(qpos[:, 0], retshape), \
633 numpy.reshape(qpos[:, 1], retshape), \
634 numpy.reshape(qpos[:, 2], retshape)
635
636 def init_linear(self, detectorDir, cch, Nchannel, distance=None,
637 pixelwidth=None, chpdeg=None, tilt=0, **kwargs):
638 """
639 initialization routine for linear detectors
640 detector direction as well as distance and pixel size or
641 channels per degree must be given.
642
643 Parameters
644 ----------
645 detectorDir : str
646 direction of the detector (along the pixel array); e.g. 'z+'
647 cch : float
648 center channel, in direction of self.r_i at zero detectorAngles
649 Nchannel : int
650 total number of detector channels
651 distance : float, optional
652 distance of center channel from center of rotation
653 pixelwidth : float, optional
654 width of one pixel (same unit as distance)
655 chpdeg : float, optional
656 channels per degree (only absolute value is relevant) sign
657 determined through detectorDir
658 tilt : float, optional
659 tilt of the detector axis from the detectorDir (in degree)
660 kwargs: dict, optional
661 optional keyword arguments
662 Nav : int, optional
663 number of channels to average to reduce data size (default: 1)
664 roi : tuple or list
665 region of interest for the detector pixels; e.g. [100, 900]
666
667 Note:
668 Either distance and pixelwidth or chpdeg must be given !!
669
670 Note:
671 the channel numbers run from 0 .. Nchannel-1
672
673 """
674
675 utilities.check_kwargs(kwargs, self._valid_linear_kwargs,
676 'init_linear')
677
678 # detectorDir
679 if not isinstance(detectorDir, basestring) or len(detectorDir) != 2:
680 raise InputError("QConversion: incorrect detector direction type "
681 "or syntax (%s)" % repr(detectorDir))
682 if not directionSyntax.search(detectorDir):
683 raise InputError("QConversion: incorrect detector direction "
684 "syntax (%s)" % detectorDir)
685 self._linear_detdir = detectorDir
686
687 self._linear_Nch = int(Nchannel)
688 self._linear_cch = float(cch)
689 self._linear_tilt = numpy.radians(tilt)
690
691 if distance is not None and pixelwidth is not None:
692 self._linear_distance = float(distance)
693 self._linear_pixwidth = float(pixelwidth)
694 elif chpdeg is not None:
695 self._linear_distance = 1.0
696 self._linear_pixwidth = 2 * self._linear_distance / \
697 numpy.abs(float(chpdeg)) * numpy.tan(numpy.radians(0.5))
698 else:
699 # not all needed values were given
700 raise InputError("QConversion: not all mandatory arguments were "
701 "given -> read API doc, need distance and "
702 "pixelwidth or chpdeg")
703
704 # kwargs
705 self._linear_roi = kwargs.get('roi', [0, self._linear_Nch])
706 self._linear_nav = kwargs.get('Nav', 1)
707
708 # rescale r_i
709 self.r_i = math.VecUnit(self.r_i) * self._linear_distance
710
711 self._linear_init = True
712
713 def _get_detparam_linear(self, oroi, nav):
714 """
715 initialize linear detector geometry for C subroutines. This function
716 considers the Nav and roi options.
717 """
718 cch = self._linear_cch / float(nav)
719 pwidth = self._linear_pixwidth * nav
720 roi = numpy.array(oroi)
721 roi[0] = numpy.floor(oroi[0] / float(nav))
722 roi[1] = numpy.ceil((oroi[1] - oroi[0]) / float(nav)) + roi[0]
723 roi = roi.astype(numpy.int32)
724 return cch, pwidth, roi
725
726 def linear(self, *args, **kwargs):
727 """
728 angular to momentum space conversion for a linear detector
729 the cch of the detector must be in direction of self.r_i when
730 detector angles are zero
731
732 the detector geometry must be initialized by the init_linear(...)
733 routine
734
735 Parameters
736 ----------
737 args : ndarray, list or Scalars
738 sample and detector angles; in total `len(self.sampleAxis) +
739 len(detectorAxis)` must be given, always starting with the outer
740 most circle. all arguments must have the same shape or length but
741 can be mixed with Scalars (i.e. if an angle is always the same it
742 can be given only once instead of an array)
743
744 - sAngles :
745 sample circle angles, number of arguments must correspond to
746 len(self.sampleAxis)
747 - dAngles :
748 detector circle angles, number of arguments must correspond to
749 len(self.detectorAxis)
750
751 kwargs : dict, optional
752 optional keyword arguments
753 delta : list or array-like, optional
754 delta angles to correct the given ones for misalignment.
755 delta must be an numpy array or list of ``len(*args)``. used angles
756 are then ``*args - delta``
757 UB : array-like, optional
758 matrix for conversion from (hkl) coordinates to Q of sample used to
759 determine not Q but (hkl) (default: self.UB)
760 Nav : int, optional
761 number of channels to average to reduce data size (default:
762 self._linear_nav)
763 roi : list or tuple, optional
764 region of interest for the detector pixels; e.g. [100, 900]
765 (default: self._linear_roi)
766 wl : float or str, optional
767 x-ray wavelength in angstroem (default: self._wl)
768 en : float, optional
769 x-ray energy in eV (default is converted self._wl). both wavelength
770 and energy can also be an array which enables the QConversion for
771 energy scans. Note that the `en` keyword overrules the `wl`
772 keyword!
773 deg : bool, optional
774 flag to tell if angles are passed as degree (default: True)
775 sampledis : tuple or list or array-like
776 sample displacement vector in relative units of the detector
777 distance (default: (0, 0, 0))
778
779 Returns
780 -------
781 reciprocal space position of all detector pixels in a numpy.ndarray of
782 shape ( (*)*(self._linear_roi[1]-self._linear_roi[0]+1) , 3 )
783 """
784
785 if not self._linear_init:
786 raise Exception("QConversion: linear detector not initialized -> "
787 "call Ang2Q.init_linear(...)")
788
789 valid_kwargs = copy.copy(self._valid_call_kwargs)
790 valid_kwargs.update(self._valid_linear_kwargs)
791 utilities.check_kwargs(kwargs, valid_kwargs, 'Ang2Q/linear')
792
793 Ns, Nd, Ncirc, wl, deg, delta, UB, sd, flags = \
794 self._parse_common_kwargs(**kwargs)
795
796 # extra keyword arguments
797 nav = kwargs.get('Nav', self._linear_nav)
798 oroi = kwargs.get('roi', self._linear_roi)
799
800 # prepare angular arrays from *args
801 # need one sample angle and one detector angle array
802 if len(args) != Ncirc:
803 raise InputError("QConversion: wrong amount (%d) of arguments "
804 "given, number of arguments should be %d"
805 % (len(args), Ncirc))
806
807 # determine the number of points
808 a = args + (wl,)
809 Npoints = self._checkInput(*a)
810
811 # reshape/recast input arguments for sample and detector angles
812 sAngles, retshape = self._reshapeInput(Npoints, delta[:Ns],
813 self.sampleAxis, *args[:Ns],
814 deg=deg)
815 dAngles = self._reshapeInput(Npoints, delta[Ns:],
816 self.detectorAxis, *args[Ns:],
817 deg=deg)[0]
818 wl = numpy.ravel(self._reshapeInput(Npoints, (0, ), 'a',
819 wl, deg=False)[0])
820
821 sAngles = sAngles.transpose()
822 dAngles = dAngles.transpose()
823
824 cch, pwidth, roi = self._get_detparam_linear(oroi, nav)
825 sAxis = self._sampleAxis_str
826 dAxis = self._detectorAxis_str
827
828 qpos = cxrayutilities.ang2q_conversion_linear(
829 sAngles, dAngles, self.r_i, sAxis, dAxis, self._kappa_dir,
830 cch, pwidth, roi, self._linear_detdir, self._linear_tilt,
831 UB, sd, wl, config.NTHREADS, flags)
832
833 # reshape output
834 if Npoints == 1:
835 qpos.shape = (Npoints * (roi[1] - roi[0]), 3)
836 return qpos[:, 0], qpos[:, 1], qpos[:, 2]
837 else:
838 qpos.shape = (Npoints, (roi[1] - roi[0]), 3)
839 return qpos[:, :, 0], qpos[:, :, 1], qpos[:, :, 2]
840
841 def init_area(self, detectorDir1, detectorDir2, cch1, cch2, Nch1, Nch2,
842 distance=None, pwidth1=None, pwidth2=None, chpdeg1=None,
843 chpdeg2=None, detrot=0, tiltazimuth=0, tilt=0, **kwargs):
844 """
845 initialization routine for area detectors
846 detector direction as well as distance and pixel size or
847 channels per degree must be given. Two separate pixel sizes and
848 channels per degree for the two orthogonal directions can be given
849
850 Parameters
851 ----------
852 detectorDir1 : str
853 direction of the detector (along the pixel direction 1); e.g. 'z+'
854 means higher pixel numbers at larger z positions
855 detectorDir2 : str
856 direction of the detector (along the pixel direction 2); e.g. 'x+'
857 cch1, cch2 : float
858 center pixel, in direction of self.r_i at zero detectorAngles
859 Nch1, Nch2 : int
860 number of detector pixels along direction 1, 2
861 distance : float, optional
862 distance of center pixel from center of rotation
863 pwidth1, pwidth2 : float, optional
864 width of one pixel (same unit as distance)
865 chpdeg1, chpdeg2 : float, optional
866 channels per degree (only absolute value is relevant) sign
867 determined through `detectorDir1, detectorDir2`
868 detrot : float, optional
869 angle of the detector rotation around primary beam direction (used
870 to correct misalignments)
871 tiltazimuth : float, optional
872 direction of the tilt vector in the detector plane (in degree)
873 tilt : float, optional
874 tilt of the detector plane around an axis normal to the direction
875 given by the tiltazimuth
876
877 kwargs : dict, optional
878 optional keyword arguments
879 Nav : tuple or list, optional
880 number of channels to average to reduce data size (default: [1, 1])
881 roi : tuple or list, optional
882 region of interest for the detector pixels; e.g.
883 [100, 900, 200, 800]
884
885 Note:
886 Either distance and pwidth1, pwidth2 or chpdeg1, chpdeg2 must
887 be given !!
888
889 Note:
890 the channel numbers run from 0 .. NchX-1
891 """
892
893 utilities.check_kwargs(kwargs, self._valid_linear_kwargs, 'init_area')
894
895 # detectorDir
896 if not isinstance(detectorDir1, basestring) or len(detectorDir1) != 2:
897 raise InputError("QConversion: incorrect detector direction1 type "
898 "or syntax (%s)" % repr(detectorDir1))
899 if not directionSyntax.search(detectorDir1):
900 raise InputError("QConversion: incorrect detector direction1 "
901 "syntax (%s)" % detectorDir1)
902 self._area_detdir1 = detectorDir1
903 if not isinstance(detectorDir2, basestring) or len(detectorDir2) != 2:
904 raise InputError("QConversion: incorrect detector direction2 type "
905 "or syntax (%s)" % repr(detectorDir2))
906 if not directionSyntax.search(detectorDir2):
907 raise InputError("QConversion: incorrect detector direction2 "
908 "syntax (%s)" % detectorDir2)
909 self._area_detdir2 = detectorDir2
910
911 # other none keyword arguments
912 self._area_Nch1 = int(Nch1)
913 self._area_Nch2 = int(Nch2)
914 self._area_cch1 = int(cch1)
915 self._area_cch2 = int(cch2)
916
917 # if detector rotation is present add new motor to consider it in
918 # conversion
919 self._area_detrot = numpy.radians(detrot)
920 if self._area_detrot != 0.:
921 if self._area_detrotaxis_set:
922 self._set_detectorAxis(
923 self._get_detectorAxis()[:-1] + [math.getSyntax(self.r_i)],
924 detrot=True)
925 else:
926 self._set_detectorAxis(
927 self._get_detectorAxis() + [math.getSyntax(self.r_i)],
928 detrot=True)
929
930 self._area_tiltazimuth = numpy.radians(tiltazimuth)
931 self._area_tilt = numpy.radians(tilt)
932
933 # mandatory keyword arguments
934 if (distance is not None and pwidth1 is not None and
935 pwidth2 is not None):
936 self._area_distance = float(distance)
937 self._area_pwidth1 = float(pwidth1)
938 self._area_pwidth2 = float(pwidth2)
939 elif chpdeg1 is not None and chpdeg2 is not None:
940 self._area_distance = 1.0
941 self._area_pwidth1 = 2 * self._area_distance / \
942 numpy.abs(float(chpdeg1)) * numpy.tan(numpy.radians(0.5))
943 self._area_pwidth2 = 2 * self._area_distance / \
944 numpy.abs(float(chpdeg2)) * numpy.tan(numpy.radians(0.5))
945 else:
946 # not all needed values were given
947 raise InputError("Qconversion error: not all mandatory arguments "
948 "were given -> read API doc")
949
950 # kwargs
951 self._area_roi = kwargs.get('roi', [0, self._area_Nch1,
952 0, self._area_Nch2])
953 self._area_nav = kwargs.get('Nav', [1, 1])
954
955 # rescale r_i
956 self.r_i = math.VecUnit(self.r_i) * self._area_distance
957
958 self._area_init = True
959
960 def _get_detparam_area(self, oroi, nav):
961 """
962 initialize CCD geomtry for C subroutines. This function considers the
963 Nav and roi options.
964 """
965 cch1 = self._area_cch1 / float(nav[0])
966 cch2 = self._area_cch2 / float(nav[1])
967 pwidth1 = self._area_pwidth1 * nav[0]
968 pwidth2 = self._area_pwidth2 * nav[1]
969 roi = numpy.array(oroi)
970 roi[0] = numpy.floor(oroi[0] / float(nav[0]))
971 roi[1] = numpy.ceil((oroi[1] - oroi[0]) / float(nav[0])) + roi[0]
972 roi[2] = numpy.floor(oroi[2] / float(nav[1]))
973 roi[3] = numpy.ceil((oroi[3] - oroi[2]) / float(nav[1])) + roi[2]
974 roi = roi.astype(numpy.int32)
975 return cch1, cch2, pwidth1, pwidth2, roi
976
977 def area(self, *args, **kwargs):
978 """
979 angular to momentum space conversion for a area detector
980 the center pixel defined by the init_area routine must be
981 in direction of self.r_i when detector angles are zero
982
983 the detector geometry must be initialized by the init_area(...) routine
984
985 Parameters
986 ----------
987 args : ndarray, list or Scalars
988 sample and detector angles; in total `len(self.sampleAxis) +
989 len(detectorAxis)` must be given, always starting with the outer
990 most circle. all arguments must have the same shape or length but
991 can be mixed with Scalars (i.e. if an angle is always the same it
992 can be given only once instead of an array)
993
994 - sAngles :
995 sample circle angles, number of arguments must correspond to
996 len(self.sampleAxis)
997 - dAngles :
998 detector circle angles, number of arguments must correspond to
999 len(self.detectorAxis)
1000
1001 kwargs : dict, optional
1002 optional keyword arguments
1003 delta : list or array-like, optional
1004 delta angles to correct the given ones for misalignment.
1005 delta must be an numpy array or list of ``len(*args)``. used angles
1006 are then ``*args - delta``
1007 UB : array-like, optional
1008 matrix for conversion from (hkl) coordinates to Q of sample used to
1009 determine not Q but (hkl) (default: self.UB)
1010 Nav : tuple or list, optional
1011 number of channels to average to reduce data size e.g. [2, 2]
1012 (default: self._area_nav)
1013 roi : list or tuple, optional
1014 region of interest for the detector pixels; e.g.
1015 [100, 900, 200, 800] (default: self._area_roi)
1016 wl : float or str, optional
1017 x-ray wavelength in angstroem (default: self._wl)
1018 en : float, optional
1019 x-ray energy in eV (default is converted self._wl). both wavelength
1020 and energy can also be an array which enables the QConversion for
1021 energy scans. Note that the `en` keyword overrules the `wl`
1022 keyword!
1023 deg : bool, optional
1024 flag to tell if angles are passed as degree (default: True)
1025 sampledis : tuple or list or array-like
1026 sample displacement vector in relative units of the detector
1027 distance (default: (0, 0, 0))
1028
1029
1030 Returns
1031 -------
1032 reciprocal space position of all detector pixels in a numpy.ndarray of
1033 shape ((*)*(self._area_roi[1] - self._area_roi[0]+1) *
1034 (self._area_roi[3] - self._area_roi[2] + 1) , 3) were detectorDir1 is
1035 the fastest varing
1036 """
1037
1038 if not self._area_init:
1039 raise Exception("QConversion: area detector not initialized -> "
1040 "call Ang2Q.init_area(...)")
1041
1042 valid_kwargs = copy.copy(self._valid_call_kwargs)
1043 valid_kwargs.update(self._valid_linear_kwargs)
1044 utilities.check_kwargs(kwargs, valid_kwargs, 'Ang2Q/area')
1045
1046 Ns, Nd, Ncirc, wl, deg, delta, UB, sd, flags = \
1047 self._parse_common_kwargs(**kwargs)
1048
1049 # extra keyword arguments
1050 nav = kwargs.get('Nav', self._area_nav)
1051 oroi = kwargs.get('roi', self._area_roi)
1052
1053 # prepare angular arrays from *args
1054 # need one sample angle and one detector angle array
1055 if len(args) != Ncirc:
1056 raise InputError("QConversion: wrong amount (%d) of arguments "
1057 "given, number of arguments should be %d"
1058 % (len(args), Ncirc))
1059
1060 # determine the number of points
1061 a = args + (wl,)
1062 Npoints = self._checkInput(*a)
1063
1064 # reshape/recast input arguments for sample and detector angles
1065 sAngles, retshape = self._reshapeInput(Npoints, delta[:Ns],
1066 self.sampleAxis, *args[:Ns],
1067 deg=deg)
1068 wl = numpy.ravel(self._reshapeInput(Npoints, (0, ), 'a',
1069 wl, deg=False)[0])
1070
1071 if self._area_detrotaxis_set:
1072 Nd = Nd + 1
1073 if deg:
1074 a = args[Ns:] + (numpy.degrees(self._area_detrot),)
1075 else:
1076 a = args[Ns:] + (self._area_detrot,)
1077 dAngles = self._reshapeInput(
1078 Npoints, numpy.append(delta[Ns:], 0),
1079 self.detectorAxis, *a, deg=deg)[0]
1080 else:
1081 dAngles = self._reshapeInput(Npoints, delta[Ns:],
1082 self.detectorAxis, *args[Ns:],
1083 deg=deg)[0]
1084
1085 sAngles = sAngles.transpose()
1086 dAngles = dAngles.transpose()
1087
1088 cch1, cch2, pwidth1, pwidth2, roi = self._get_detparam_area(oroi, nav)
1089
1090 if config.VERBOSITY >= config.DEBUG:
1091 print("QConversion.area: roi, number of points per frame: %s, %d"
1092 % (str(roi), (roi[1] - roi[0]) * (roi[3] - roi[2])))
1093 print("QConversion.area: cch1, cch2: %5.2f %5.2f" % (cch1, cch2))
1094
1095 sAxis = self._sampleAxis_str
1096 dAxis = self._detectorAxis_str
1097
1098 qpos = cxrayutilities.ang2q_conversion_area(
1099 sAngles, dAngles, self.r_i, sAxis, dAxis, self._kappa_dir,
1100 cch1, cch2, pwidth1, pwidth2, roi, self._area_detdir1,
1101 self._area_detdir2, self._area_tiltazimuth, self._area_tilt,
1102 UB, sd, wl, config.NTHREADS, flags)
1103
1104 # reshape output
1105 if Npoints == 1:
1106 qpos.shape = ((roi[1] - roi[0]), (roi[3] - roi[2]), 3)
1107 return qpos[:, :, 0], qpos[:, :, 1], qpos[:, :, 2]
1108 else:
1109 qpos.shape = (Npoints, (roi[1] - roi[0]), (roi[3] - roi[2]), 3)
1110 return qpos[:, :, :, 0], qpos[:, :, :, 1], qpos[:, :, :, 2]
1111
1112 def transformSample2Lab(self, vector, *args):
1113 """
1114 transforms a vector from the sample coordinate frame to the laboratory
1115 coordinate system by applying the sample rotations from inner to outer
1116 circle.
1117
1118 Parameters
1119 ----------
1120 vector : sequence, list or numpy array
1121 vector to transform
1122 args : list
1123 goniometer angles (sample angles or full goniometer angles can be
1124 given. If more angles than the sample circles are given they will
1125 be ignored)
1126
1127 Returns
1128 -------
1129 ndarray
1130 rotated vector as numpy.array
1131 """
1132 rotvec = vector
1133 for i in range(len(self.sampleAxis)-1, -1, -1):
1134 a = args[i]
1135 axis = self.sampleAxis[i]
1136 rota = math.getVector(axis)
1137 rotvec = math.rotarb(rotvec, rota, a)
1138 return rotvec
1139
1140 def getDetectorPos(self, *args, **kwargs):
1141 """
1142 obtains the detector position vector by applying the detector arm
1143 rotations.
1144
1145 Parameters
1146 ----------
1147 args : list
1148 detector angles. Only detector arm angles as described by the
1149 detectorAxis attribute must be given.
1150 kwargs : dict, optional
1151 optional keyword arguments
1152 dim : int, optional
1153 dimension of the detector for which the position should be
1154 determined
1155 roi : tuple or list, optional
1156 region of interest for the detector pixels; (default:
1157 self._area_roi/self._linear_roi)
1158 Nav : tuple or list, optional
1159 number of channels to average to reduce data size; (default:
1160 self._area_nav/self._linear_nav)
1161 deg : bool, optional
1162 flag to tell if angles are passed as degree (default: True)
1163
1164 Returns
1165 -------
1166 ndarray
1167 numpy array of length 3 with vector components of the detector
1168 direction. The length of the vector is k.
1169 """
1170
1171 valid_kwargs = copy.copy(self._valid_linear_kwargs)
1172 valid_kwargs['dim'] = 'dimensionality of the detector'
1173 valid_kwargs['deg'] = 'True if angles are in degrees'
1174 utilities.check_kwargs(kwargs, valid_kwargs, 'get_detector_pos')
1175
1176 dim = kwargs.get('dim', 0)
1177
1178 if dim == 1 and not self._linear_init:
1179 raise Exception("QConversion: linear detector not initialized -> "
1180 "call Ang2Q.init_linear(...)")
1181 elif dim == 2 and not self._area_init:
1182 raise Exception("QConversion: area detector not initialized -> "
1183 "call Ang2Q.init_area(...)")
1184
1185 Ns = len(self.sampleAxis)
1186 Nd = len(self.detectorAxis)
1187 if self._area_detrotaxis_set:
1188 Nd = Nd - 1
1189 Ncirc = Ns + Nd
1190
1191 # kwargs
1192 deg = kwargs.get('deg', True)
1193
1194 if 'roi' in kwargs:
1195 oroi = kwargs['roi']
1196 else:
1197 if dim == 1:
1198 oroi = self._linear_roi
1199 elif dim == 2:
1200 oroi = self._area_roi
1201
1202 if 'Nav' in kwargs:
1203 nav = kwargs['Nav']
1204 else:
1205 if dim == 1:
1206 nav = self._linear_nav
1207 elif dim == 2:
1208 nav = self._area_nav
1209
1210 # prepare angular arrays from *args
1211 # need one sample angle and one detector angle array
1212 if len(args) != Nd:
1213 raise InputError("QConversion: wrong amount (%d) of arguments "
1214 "given, number of arguments should be %d"
1215 % (len(args), Nd))
1216
1217 # determine the number of points and reshape input arguments
1218 Npoints = self._checkInput(*args)
1219
1220 if dim == 2 and self._area_detrotaxis_set:
1221 Nd = Nd + 1
1222 if deg:
1223 a = args + (numpy.degrees(self._area_detrot),)
1224 else:
1225 a = args + (self._area_detrot,)
1226 dAngles, retshape = self._reshapeInput(
1227 Npoints, numpy.append(numpy.zeros(Nd), 0),
1228 self.detectorAxis, *a, deg=deg)
1229 else:
1230 dAngles, retshape = self._reshapeInput(Npoints, numpy.zeros(Nd),
1231 self.detectorAxis, *args,
1232 deg=deg)
1233
1234 dAngles = dAngles.transpose()
1235
1236 if dim == 2:
1237 cch1, cch2, pwidth1, pwidth2, roi = self._get_detparam_area(oroi,
1238 nav)
1239 elif dim == 1:
1240 cch, pwidth, roi = self._get_detparam_linear(oroi, nav)
1241
1242 dAxis = self._detectorAxis_str
1243
1244 if dim == 2:
1245 cfunc = cxrayutilities.ang2q_detpos_area
1246 dpos = cfunc(dAngles, self.r_i, dAxis, cch1, cch2, pwidth1,
1247 pwidth2, roi, self._area_detdir1, self._area_detdir2,
1248 self._area_tiltazimuth, self._area_tilt,
1249 config.NTHREADS)
1250
1251 # reshape output
1252 if Npoints == 1:
1253 dpos.shape = ((roi[1] - roi[0]), (roi[3] - roi[2]), 3)
1254 return dpos[:, :, 0], dpos[:, :, 1], dpos[:, :, 2]
1255 else:
1256 dpos.shape = (Npoints, (roi[1] - roi[0]), (roi[3] - roi[2]), 3)
1257 return dpos[:, :, :, 0], dpos[:, :, :, 1], dpos[:, :, :, 2]
1258
1259 elif dim == 1:
1260 cfunc = cxrayutilities.ang2q_detpos_linear
1261 dpos = cfunc(dAngles, self.r_i, dAxis, cch, pwidth, roi,
1262 self._linear_detdir, self._linear_tilt,
1263 config.NTHREADS)
1264
1265 # reshape output
1266 if Npoints == 1:
1267 dpos.shape = (Npoints * (roi[1] - roi[0]), 3)
1268 return dpos[:, 0], dpos[:, 1], dpos[:, 2]
1269 else:
1270 dpos.shape = (Npoints, (roi[1] - roi[0]), 3)
1271 return dpos[:, :, 0], dpos[:, :, 1], dpos[:, :, 2]
1272
1273 else:
1274 cfunc = cxrayutilities.ang2q_detpos
1275 dpos = cfunc(dAngles, self.r_i, dAxis, config.NTHREADS)
1276
1277 if Npoints == 1:
1278 return (dpos[0, 0], dpos[0, 1], dpos[0, 2])
1279 else:
1280 return numpy.reshape(dpos[:, 0], retshape), \
1281 numpy.reshape(dpos[:, 1], retshape), \
1282 numpy.reshape(dpos[:, 2], retshape)
1283
1284 def getDetectorDistance(self, *args, **kwargs):
1285 """
1286 obtains the detector distance by applying the detector arm movements.
1287 This is especially interesting for the case of 1 or 2D detectors to
1288 perform certain geometric corrections.
1289
1290 Parameters
1291 ----------
1292 args : list
1293 detector angles. Only detector arm angles as described by the
1294 detectorAxis attribute must be given.
1295 kwargs : dict, optional
1296 optional keyword arguments
1297 dim : int, optional
1298 dimension of the detector for which the position should be
1299 determined
1300 roi : tuple or list, optional
1301 region of interest for the detector pixels; (default:
1302 self._area_roi/self._linear_roi)
1303 Nav : tuple or list, optional
1304 number of channels to average to reduce data size; (default:
1305 self._area_nav/self._linear_nav)
1306 deg : bool, optional
1307 flag to tell if angles are passed as degree (default: True)
1308
1309 Returns
1310 -------
1311 ndarray
1312 numpy array with the detector distance
1313 """
1314 x, y, z = self.getDetectorPos(*args, **kwargs)
1315 return numpy.sqrt(x**2 + y**2 + z**2)
1316
1317
1318 class Experiment(object):
1319
1320 """
1321 base class for describing experiments
1322 users should use the derived classes: HXRD, GID, PowderExperiment
1323 """
1324
1325 _valid_init_kwargs = {'en': 'x-ray energy',
1326 'wl': 'x-ray wavelength',
1327 'qconv': 'reciprocal space conversion',
1328 'sampleor': 'sample orientation'}
1329
1330 def __init__(self, ipdir, ndir, **keyargs):
1331 """
1332 initialization of an Experiment class needs the sample orientation
1333 given by the samples surface normal and an second not colinear
1334 direction specifying the inplane reference direction in the crystal
1335 coordinate system. The orientation of the surface normal in the lab
1336 coordinate system can also be given or is automatically determined by
1337 the goniometer type (see argument sampleor).
1338
1339 Parameters
1340 ----------
1341 ipdir : list or tuple or array-like
1342 inplane reference direction (ipdir points into the primary beam
1343 direction at zero angles)
1344 ndir : list or tuple or array-like
1345 surface normal of your sample (ndir points in a direction
1346 perpendicular to the primary beam and the innermost detector
1347 rotation axis)
1348
1349 keyargs : dict, optional
1350 optional keyword arguments
1351 qconv : QConversion, optional
1352 QConversion object to use for the Ang2Q conversion
1353 sampleor : {'det', 'sam', '[xyz][+-]'}, optional
1354 sample orientation specifies the orientation of the sample surface
1355 with respect to the coordinate system in which the goniometer
1356 rotations are given. You can use the [xyz][+-] synthax to specify
1357 the nominal surface orientation (when all goniometer angles are
1358 zero). In addition two special values 'det' and 'sam' are
1359 available, which will let the code determine the orientation from
1360 either the inner most detector or sample rotation. Default is
1361 'det'.
1362 wl : float or str
1363 wavelength of the x-rays in Angstroem (default: 1.5406A)
1364 en : float or str
1365 energy of the x-rays in eV (default: 8048eV == 1.5406A ).
1366 the en keyword overrules the wl keyword
1367
1368 Note:
1369 The qconv argument does not change the Q2Ang function's
1370 behavior. See Q2AngFit function in case you want to calculate
1371 for arbitrary goniometers with some restrictions.
1372 """
1373
1374 utilities.check_kwargs(keyargs, self._valid_init_kwargs,
1375 self.__class__.__name__)
1376
1377 if isinstance(ipdir, (list, tuple, numpy.ndarray)):
1378 self.idir = math.VecUnit(ipdir)
1379 else:
1380 raise TypeError("Inplane direction must be list or numpy array")
1381
1382 if isinstance(ndir, (list, tuple, numpy.ndarray)):
1383 self.ndir = math.VecUnit(ndir)
1384 else:
1385 raise TypeError("normal direction must be list or numpy array")
1386
1387 # test the given direction to be not parallel and warn if not
1388 # perpendicular
1389 if(norm(numpy.cross(self.idir, self.ndir)) < config.EPSILON):
1390 raise InputError("given inplane direction is parallel to normal "
1391 "direction, they must be linear independent!")
1392 if(numpy.abs(numpy.dot(self.idir, self.ndir)) > config.EPSILON):
1393 self.idir = numpy.cross(
1394 numpy.cross(self.ndir, self.idir),
1395 self.ndir)
1396 self.idir = self.idir / norm(self.idir)
1397 warnings.warn("Experiment: given inplane direction is not "
1398 "perpendicular to normal direction\n -> Experiment "
1399 "class uses the following direction with the same "
1400 "azimuth:\n %s" % (' '.join(map(
1401 str, numpy.round(self.idir, 3)))))
1402
1403 # initialize Ang2Q conversion
1404 self._A2QConversion = keyargs.get(
1405 'qconv', QConversion('x+', 'x+', [0, 1, 0]))
1406 self.Ang2Q = self._A2QConversion
1407
1408 self._sampleor = keyargs.get('sampleor', 'det')
1409
1410 # set the coordinate transform for the azimuth used in the experiment
1411 self.scatplane = math.VecUnit(numpy.cross(self.idir, self.ndir))
1412 self._set_transform(self.scatplane, self.idir,
1413 self.ndir, self._sampleor)
1414
1415 # calculate the energy from the wavelength
1416 self._set_wavelength(keyargs.get('wl', config.WAVELENGTH))
1417 if "en" in keyargs:
1418 self._set_energy(keyargs["en"])
1419
1420 def __str__(self):
1421
1422 ostr = "scattering plane normal: (%f %f %f)\n" % (self.scatplane[0],
1423 self.scatplane[1],
1424 self.scatplane[2])
1425 ostr += "inplane azimuth: (%f %f %f)\n" % (self.idir[0],
1426 self.idir[1],
1427 self.idir[2])
1428 ostr += "second refercence direction: (%f %f %f)\n" % (self.ndir[0],
1429 self.ndir[1],
1430 self.ndir[2])
1431 ostr += "energy: %f (eV)\n" % self._en
1432 ostr += "wavelength: %f (Anstrom)\n" % (self._wl)
1433 ostr += self._A2QConversion.__str__()
1434
1435 return ostr
1436
1437 def _set_transform(self, v1, v2, v3, sampleor='det'):
1438 """
1439 set new transformation of the coordinate system to use in the
1440 experimental class.
1441
1442 The sampleor variable determines the sample surface orientation with
1443 respect to the coordinate system in which the goniometer rotations are
1444 given. You can use the [xyz][+-] synthax to specify the nominal surface
1445 orientation (when all goniometer angles are zero). In addition two
1446 special values 'det' and 'sam' are available, which will let the code
1447 determine the orientation from either the inner most detector or sample
1448 rotation. 'det' means the surface is in the plane spanned by the inner
1449 most detector rotation (rotation around primary beam is ignored) and
1450 perpendicular to the primary beam. 'sam' means the surface orientation
1451 is along the innermost sample circles rotation direction (in this case
1452 this should be the azimuth motor to yield the expected results).
1453 Default is 'det'.
1454
1455 Restrictions: the given direction can not be along the primary beam.
1456 If one needs that case, let the maintainer know. Currently this case is
1457 caught and a different axis is automatically used as z-axis.
1458 """
1459 # turn idir to Y and ndir to Z
1460 self._t1 = math.CoordinateTransform(v1, v2, v3)
1461
1462 if sampleor == 'det':
1463 yi = self._A2QConversion.r_i
1464 idc = self._A2QConversion.detectorAxis[-1]
1465 xi = math.getVector(idc)
1466 if norm(numpy.cross(xi, yi)) < config.EPSILON:
1467 # this is the case when a detector rotation around the primary
1468 # beam direction is installed
1469 idc = self._A2QConversion.detectorAxis[-2]
1470 xi = math.getVector(idc)
1471 zi = math.VecUnit(numpy.cross(xi, yi))
1472 elif sampleor == 'sam':
1473 yi = self._A2QConversion.r_i
1474 isc = self._A2QConversion.sampleAxis[-1]
1475 zi = numpy.abs(math.getVector(isc))
1476 if numpy.all(numpy.abs(yi) == numpy.abs(zi)):
1477 zi = numpy.roll(zi, 1)
1478 if config.VERBOSITY >= config.INFO_LOW:
1479 print("XU.Experiment: Warning, sample orientation "
1480 "convention failed. Using (%.3f %.3f %.3f) "
1481 "as internal z-axis" % (zi[0], zi[1], zi[2]))
1482 xi = math.VecUnit(numpy.cross(yi, zi))
1483 else:
1484 yi = self._A2QConversion.r_i
1485 try:
1486 zi = math.getVector(sampleor)
1487 except InputError:
1488 raise InputError('invalid value of sample orientation, use '
1489 'either [xyz][+-] syntax or det/sam!')
1490 if numpy.all(numpy.abs(yi) == numpy.abs(zi)):
1491 zi = numpy.roll(zi, 1)
1492 if config.VERBOSITY >= config.INFO_LOW:
1493 print("XU.Experiment: Warning, sample orientation "
1494 "convention failed. Using (%.3f %.3f %.3f) "
1495 "as internal z-axis" % (zi[0], zi[1], zi[2]))
1496 xi = math.VecUnit(numpy.cross(yi, zi))
1497 # turn r_i to Y and Z defined by detector rotation plane
1498 self._t2 = math.CoordinateTransform(xi, yi, zi)
1499
1500 self._transform = math.Transform(
1501 numpy.dot(numpy.linalg.inv(self._t2.matrix), self._t1.matrix))
1502
1503 def _set_energy(self, energy):
1504 self._en = utilities.energy(energy)
1505 self._wl = utilities.en2lam(self._en)
1506 self.k0 = numpy.pi * 2. / self._wl
1507 self._A2QConversion.wavelength = self._wl
1508
1509 def _set_wavelength(self, wl):
1510 self._wl = utilities.wavelength(wl)
1511 self._en = utilities.lam2en(self._wl)
1512 self.k0 = numpy.pi * 2. / self._wl
1513 self._A2QConversion.wavelength = self._wl
1514
1515 def _get_energy(self):
1516 return self._en
1517
1518 def _get_wavelength(self):
1519 return self._wl
1520
1521 energy = property(_get_energy, _set_energy)
1522 wavelength = property(_get_wavelength, _set_wavelength)
1523
1524 def _set_inplane_direction(self, dir):
1525 self.idir = math.VecUnit(dir)
1526 v1 = numpy.cross(self.ndir, self.idir)
1527 self._set_transform(v1, self.idir, self.ndir, self._sampleor)
1528
1529 def _get_inplane_direction(self):
1530 return self.idir
1531
1532 def _set_normal_direction(self, dir):
1533 self.ndir = math.VecUnit(dir)
1534 v1 = numpy.cross(self.ndir, self.idir)
1535 self._set_transform(v1, self.idir, self.ndir, self._sampleor)
1536
1537 def _get_normal_direction(self):
1538 return self.ndir
1539
1540 def Q2Ang(self, qvec):
1541 pass
1542
1543 def Ang2HKL(self, *args, **kwargs):
1544 """
1545 angular to (h, k, l) space conversion.
1546 It will set the UB argument to Ang2Q and pass all other parameters
1547 unchanged. See Ang2Q for description of the rest of the arguments.
1548
1549 Parameters
1550 ----------
1551 args : list
1552 arguments forwarded to Ang2Q
1553 kwargs : dict, optional
1554 optional keyword arguments
1555 B : array-like, optional
1556 reciprocal space conversion matrix of a Crystal. You can specify
1557 the matrix B (default identiy matrix) shape needs to be (3, 3)
1558 mat : Crystal, optional
1559 Crystal object to use to obtain a B matrix (e.g. xu.materials.Si)
1560 can be used as alternative to the B keyword argument B is favored
1561 in case both are given
1562 U : array-like, optional
1563 orientation matrix U can be given. If none is given the orientation
1564 defined in the Experiment class is used.
1565 dettype : {'point', 'linear', 'area'}, optional
1566 detector type: decides which routine of Ang2Q to call. default
1567 'point'
1568 delta : ndarray, list or tuple, optional
1569 giving delta angles to correct the given ones for misalignment.
1570 delta must be an numpy array or list of length 2. used angles are
1571 than ``(om, tt) - delta``
1572 wl : float or str, optional
1573 x-ray wavelength in angstroem (default: self._wl)
1574 en : float or str, optional
1575 x-ray energy in eV (default: converted self._wl)
1576 deg : bool, optional
1577 flag to tell if angles are passed as degree (default: True)
1578 sampledis : tuple, list or array-like, optional
1579 sample displacement vector in relative units of the detector
1580 distance (default: (0, 0, 0))
1581
1582 Returns
1583 -------
1584 ndarray
1585 H K L coordinates as numpy.ndarray with shape `(N , 3)` where `N`
1586 corresponds to the number of points given in the input (args)
1587 """
1588
1589 valid_kwargs = {'B': 'orthonormalization matrix',
1590 'U': 'orientation matrix',
1591 'mat': 'material object',
1592 'dettype': 'string with detector type'}
1593 valid_kwargs.update(QConversion._valid_call_kwargs)
1594 del valid_kwargs['UB']
1595 utilities.check_kwargs(kwargs, valid_kwargs, 'Ang2HKL')
1596
1597 if "B" in kwargs:
1598 B = numpy.array(kwargs['B'])
1599 kwargs.pop("B")
1600 elif "mat" in kwargs:
1601 mat = kwargs['mat']
1602 B = mat.B
1603 kwargs.pop("mat")
1604 else:
1605 B = numpy.identity(3)
1606
1607 if "U" in kwargs:
1608 U = numpy.array(kwargs['U'])
1609 kwargs.pop("U")
1610 else:
1611 U = self._transform.matrix
1612
1613 kwargs['UB'] = numpy.dot(U, B)
1614
1615 if "dettype" in kwargs:
1616 typ = kwargs['dettype']
1617 if typ not in ('point', 'linear', 'area'):
1618 raise InputError("wrong dettype given: needs to be one of "
1619 "'point', 'linear', 'area'")
1620 kwargs.pop("dettype")
1621 else:
1622 typ = 'point'
1623
1624 if typ == 'linear':
1625 return self.Ang2Q.linear(*args, **kwargs)
1626 elif typ == 'area':
1627 return self.Ang2Q.area(*args, **kwargs)
1628 else:
1629 return self.Ang2Q(*args, **kwargs)
1630
1631 def Transform(self, v):
1632 """
1633 transforms a vector, matrix or tensor of rank 4 (e.g. elasticity
1634 tensor) to the coordinate frame of the Experiment class. This is for
1635 example necessary before any Q2Ang-conversion can be performed.
1636
1637 Parameters
1638 ----------
1639 v : object to transform, list or numpy array of shape
1640 (n,) (n, n), (n, n, n, n) where n is the rank of the
1641 transformation matrix
1642
1643 Returns
1644 -------
1645 transformed object of the same shape as v
1646 """
1647 return self._transform(v)
1648
1649 def TiltAngle(self, q, deg=True):
1650 """
1651 TiltAngle(q, deg=True):
1652 Return the angle between a q-space position and the surface normal.
1653
1654 Parameters
1655 ----------
1656 q : list or numpy array with the reciprocal space position
1657
1658 optional keyword arguments:
1659 deg : True/False whether the return value should be in degree or
1660 radians (default: True)
1661 """
1662
1663 if isinstance(q, list):
1664 qt = numpy.array(q, dtype=numpy.double)
1665 elif isinstance(q, numpy.ndarray):
1666 qt = q
1667 else:
1668 raise TypeError("q-space position must be list or numpy array")
1669
1670 return math.VecAngle(self.ndir, qt, deg)
1671
1672 def _prepare_qvec(self, Q):
1673 """
1674 check and reshape input to have the same q array for all possible types
1675 of input
1676 """
1677 if len(Q) < 3:
1678 Q = Q[0]
1679 if len(Q) < 3:
1680 raise InputError("need 3 q-space vector components")
1681
1682 if isinstance(Q, (list, tuple, numpy.ndarray)):
1683 q = numpy.asarray(Q, dtype=numpy.double)
1684 else:
1685 raise TypeError("Q vector must be a list, tuple or numpy array")
1686
1687 if len(q.shape) != 2:
1688 q = q.reshape(3, -1)
1689 return q.T
1690
1691
1692 class HXRD(Experiment):
1693
1694 """
1695 class describing high angle x-ray diffraction experiments
1696 the class helps with calculating the angles of Bragg reflections
1697 as well as helps with analyzing measured data
1698
1699 the class describes a two circle (omega, twotheta) goniometer to
1700 help with coplanar x-ray diffraction experiments. Nevertheless 3D data
1701 can be treated with the use of linear and area detectors.
1702 see help self.Ang2Q
1703 """
1704
1705 def __init__(self, idir, ndir, geometry='hi_lo', **keyargs):
1706 """
1707 initialization routine for the HXRD Experiment class
1708
1709 Parameters
1710 ----------
1711 idir, ndir, keyargs :
1712 same as for the Experiment base class -> please look at the
1713 docstring of Experiment.__init__ for more details
1714 geometry : {'hi_lo', 'lo_hi', 'real'}, optional
1715 determines the scattering geometry :
1716
1717 - 'hi_lo' (default) high incidence-low exit
1718 - 'lo_hi' low incidence - high exit
1719 - 'real' general geometry - q-coordinates determine
1720 high or low incidence
1721
1722 """
1723 if "qconv" not in keyargs:
1724 keyargs['qconv'] = QConversion('x+', 'x+', [0, 1, 0])
1725
1726 if geometry in ["hi_lo", "lo_hi", "real"]:
1727 self.geometry = geometry
1728 else:
1729 raise InputError("HXRD: invalid value for the geometry "
1730 "argument given")
1731
1732 Experiment.__init__(self, idir, ndir, **keyargs)
1733
1734 if config.VERBOSITY >= config.DEBUG:
1735 print(
1736 "XU.HXRD.__init__: \nEnergy: %s \nGeometry: %s \n%s---" %
1737 (self._en, self.geometry, str(
1738 self.Ang2Q)))
1739
1740 def Ang2Q(self, om, tt, **kwargs):
1741 """
1742 angular to momentum space conversion for a point detector. Also see
1743 help HXRD.Ang2Q for procedures which treat line and area detectors
1744
1745 Parameters
1746 ----------
1747 om, tt : float or array-like
1748 sample and detector angles as numpy array, lists or Scalars must be
1749 given. All arguments must have the same shape or length. However,
1750 if one angle is always the same its enough to give one scalar
1751 value.
1752
1753 kwargs : dict, optional
1754 optional keyword arguments
1755 delta : list or array-like
1756 giving delta angles to correct the given ones for misalignment.
1757 delta must be an numpy array or list of length 2. Used angles are
1758 than om, tt - delta
1759 UB : array-like
1760 matrix for conversion from (hkl) coordinates to Q of sample used to
1761 determine not Q but (hkl) (default: identity matrix)
1762 wl : float or str, optional
1763 x-ray wavelength in angstroem (default: self._wl)
1764 deg : bool, optional
1765 flag to tell if angles are passed as degree (default: True)
1766
1767 Returns
1768 -------
1769 ndarray
1770 reciprocal space positions as numpy.ndarray with shape `(N , 3)`
1771 where `N` corresponds to the number of points given in the input
1772 """
1773 # dummy function to have some documentation string available
1774 # the real function is generated dynamically in the __init__ routine
1775 pass
1776
1777 def Q2Ang(self, *Q, **keyargs):
1778 """
1779 Convert a reciprocal space vector Q to COPLANAR scattering angles. The
1780 keyword argument trans determines whether Q should be transformed to
1781 the experimental coordinate frame or not. The coplanar scattering
1782 angles correspond to a goniometer with sample rotations
1783 ['x+', 'y+', 'z-'] and detector rotation 'x+' and primary beam along y.
1784 This is a standard four circle diffractometer.
1785
1786 Note:
1787 The behavior of this function is unchanged if the goniometer
1788 definition is changed!
1789
1790 Parameters
1791 ----------
1792 Q : list, tuple or array-like
1793 array of shape (3) with q-space vector components or 3
1794 separate lists with qx, qy, qz
1795
1796 trans : bool, optional
1797 apply coordinate transformation on Q (default True)
1798 deg : book, optional
1799 (default True) determines if the angles are returned in radians or
1800 degrees
1801 geometry : {'hi_lo', 'lo_hi', 'real', 'realTilt'}, optional
1802 determines the scattering geometry (default: self.geometry):
1803
1804 - 'hi_lo' high incidence and low exit
1805 - 'lo_hi' low incidence and high exit
1806 - 'real' general geometry with angles determined by
1807 q-coordinates (azimuth); this and upper geometries
1808 return [omega, 0, phi, twotheta]
1809 - 'realTilt' general geometry with angles determined by
1810 q-coordinates (tilt); returns [omega, chi, phi, twotheta]
1811
1812 refrac : bool, optional
1813 determines if refraction is taken into account;
1814 if True then also a material must be given (default: False)
1815 mat : Crystal
1816 Crystal object; needed to obtain its optical properties for
1817 refraction correction, otherwise not used
1818 full_output : bool, optional
1819 determines if additional output is given to determine scattering
1820 angles more accurately in case refraction is set to True. default:
1821 False
1822 fi, fd : tuple or list
1823 if refraction correction is applied one can optionally specify the
1824 facet through which the beam enters (fi) and exits (fd) fi, fd must
1825 be the surface normal vectors (not transformed & not necessarily
1826 normalized). If omitted the normal direction of the experiment is
1827 used.
1828
1829 Returns
1830 -------
1831 ndarray
1832 **full_output=False**: a numpy array of shape (4) with four
1833 scattering angles which are [omega, chi, phi, twotheta];
1834
1835 - omega : incidence angle with respect to surface
1836 - chi : sample tilt for the case of non-coplanar geometry
1837 - phi : sample azimuth with respect to inplane reference
1838 direction
1839 - twotheta : scattering angle/detector angle
1840
1841 **full_output=True**: a numpy array of shape (6) with five angles
1842 which are [omega, chi, phi, twotheta, psi_i, psi_d]
1843
1844 - psi_i : offset of the incidence beam from the scattering plane
1845 due to refraction
1846 - pdi_d : offset ot the diffracted beam from the scattering plane
1847 due to refraction
1848 """
1849
1850 valid_kwargs = {'trans': 'flag, perform coordinate transformation',
1851 'deg': 'flag, return degrees',
1852 'geometry': 'geometry string',
1853 'refrac': 'flag',
1854 'mat': 'Crystal instance',
1855 'fi': 'incidence facet',
1856 'fd': 'exit facet',
1857 'full_output': 'see docstring for details'}
1858 utilities.check_kwargs(keyargs, valid_kwargs, 'Q2Ang')
1859
1860 q = self._prepare_qvec(Q)
1861
1862 # parse keyword arguments
1863 geom = keyargs.get('geometry', self.geometry)
1864 if geom not in ["hi_lo", "lo_hi", "real", "realTilt"]:
1865 raise InputError("HXRD: invalid value for the geometry argument "
1866 "given\n valid entries are: hi_lo, lo_hi, real, "
1867 "realTilt")
1868 trans = keyargs.get('trans', True)
1869 deg = keyargs.get('deg', True)
1870 mat = keyargs.get('mat', None) # material for optical properties
1871
1872 refrac = keyargs.get('refrac', False)
1873 if refrac and mat is None: # check if material is available
1874 raise InputError("keyword argument 'mat' must be set when "
1875 "'refrac' is set to True!")
1876
1877 foutp = keyargs.get('full_output', False)
1878 fi = keyargs.get('fi', self.ndir) # incidence facet
1879 fi = math.VecUnit(self.Transform(fi))
1880
1881 fd = keyargs.get('fd', self.ndir) # exit facet
1882 fd = math.VecUnit(self.Transform(fd))
1883
1884 # set parameters for the calculation
1885 z = self.Transform(self.ndir) # z
1886 y = self.Transform(self.idir) # y
1887 x = self.Transform(self.scatplane) # x
1888 if refrac:
1889 n = numpy.real(
1890 mat.idx_refraction(
1891 self.energy)) # index of refraction
1892 k = self.k0 * n
1893 else:
1894 k = self.k0
1895
1896 # start calculation for each given Q-point
1897 if foutp:
1898 angle = numpy.zeros((6, q.shape[0]))
1899 else:
1900 angle = numpy.zeros((4, q.shape[0]))
1901
1902 if trans:
1903 q = self.Transform(q)
1904
1905 if config.VERBOSITY >= config.DEBUG:
1906 print("XU.HXRD.Q2Ang: q= %s" % repr(q))
1907
1908 qa = math.VecNorm(q)
1909 tth = 2. * numpy.arcsin(qa / 2. / k)
1910
1911 # calculation of the sample azimuth phi (scattering plane
1912 # spanned by qvec[1] and qvec[2] directions)
1913
1914 chi = -numpy.arctan2(math.VecDot(q, x), math.VecDot(q, z))
1915 if numpy.any(numpy.abs(math.VecDot(q, z)) < config.EPSILON):
1916 if config.VERBOSITY >= config.INFO_LOW:
1917 print("XU.HXRD: some position is perpendicular to ndir-"
1918 "reference direction (might be inplane or "
1919 "unreachable)")
1920
1921 if geom == 'hi_lo':
1922 # +: high incidence geometry
1923 om = tth / 2. + math.VecAngle(q, z)
1924 phi = -numpy.arctan2(math.VecDot(q, x), math.VecDot(q, y))
1925 elif geom == 'lo_hi':
1926 # -: low incidence geometry
1927 om = tth / 2. - math.VecAngle(q, z)
1928 phi = -numpy.arctan2(-1 * math.VecDot(q, x),
1929 -1 * math.VecDot(q, y))
1930 elif geom == 'real':
1931 phi = -numpy.arctan2(math.VecDot(q, x), math.VecDot(q, y))
1932 sign = numpy.ones(q.shape[0])
1933 m = numpy.abs(phi) > numpy.pi / 2.
1934 phi = -numpy.arctan2(math.VecDot(q, x), math.VecDot(q, y))
1935 sign[m] = -1
1936 phi[m] = -numpy.arctan2(-1 * math.VecDot(q[m], x),
1937 -1 * math.VecDot(q[m], y))
1938 om = tth / 2 + sign * math.VecAngle(q, z)
1939 elif geom == 'realTilt':
1940 phi = 0.
1941 om = tth / 2 + numpy.arctan2(
1942 math.VecDot(q, y),
1943 numpy.sqrt(math.VecDot(q, z) ** 2 + math.VecDot(q, x) ** 2))
1944
1945 # refraction correction at incidence and exit facet
1946 psi_i = numpy.zeros_like(tth)
1947 psi_d = numpy.zeros_like(tth) # if refrac is false and full_output
1948 if refrac:
1949 beta = tth - om
1950
1951 ki = k * (numpy.cos(om)[:, numpy.newaxis] * y[numpy.newaxis, :] -
1952 numpy.sin(om)[:, numpy.newaxis] * z[numpy.newaxis, :])
1953 kd = k * (numpy.cos(beta)[:, numpy.newaxis] * y[numpy.newaxis, :] +
1954 numpy.sin(beta)[:, numpy.newaxis] * z[numpy.newaxis, :])
1955
1956 # refraction at incidence facet
1957 m = math.VecDot(ki, fi) > 0
1958 if numpy.any(m):
1959 print("XU.HXRD: Warning, incidence facet not hit by "
1960 "primary beam for all positions! check your input!")
1961 om[m] = numpy.nan
1962 tth[m] = numpy.nan
1963 mnot = numpy.logical_not(m)
1964 cosbi = numpy.abs(math.VecDot(ki, fi) / math.VecNorm(ki))
1965 cosb0 = numpy.sqrt(1 - n ** 2 * (1 - cosbi ** 2))
1966
1967 ki0 = self.k0 * (n * math.VecUnit(ki) -
1968 (numpy.sign(math.VecDot(ki, fi)) *
1969 (n * cosbi - cosb0))[:, numpy.newaxis] *
1970 fi[numpy.newaxis, :])
1971 om[mnot] = math.VecAngle(ki0, y)
1972 psi_i[mnot] = numpy.arcsin(math.VecDot(ki0, x) / self.k0)
1973 if config.VERBOSITY >= config.DEBUG:
1974 print("XU.HXRD.Q2Ang: ki, ki0 = %s %s"
1975 % (repr(ki), repr(ki0)))
1976
1977 # refraction at exit facet
1978 m = math.VecDot(kd, fd) < 0
1979 if numpy.any(m):
1980 print("XU.HXRD: Warning, exit facet not hit by "
1981 "diffracted beam! check your input!")
1982 om[m] = numpy.nan
1983 tth[m] = numpy.nan
1984
1985 cosbd = numpy.abs(math.VecDot(kd, fd) / math.VecNorm(kd))
1986 cosb0 = numpy.sqrt(1 - n ** 2 * (1 - cosbd ** 2))
1987
1988 kd0 = self.k0 * (n * math.VecUnit(kd) -
1989 (numpy.sign(math.VecDot(kd, fd)) *
1990 (n * cosbd - cosb0))[:, numpy.newaxis] *
1991 fd[numpy.newaxis, :])
1992 tth[mnot] = math.VecAngle(ki0, kd0)
1993 psi_d[mnot] = numpy.arcsin(numpy.dot(kd0, x) / self.k0)
1994 if config.VERBOSITY >= config.DEBUG:
1995 print("XU.HXRD.Q2Ang: kd, kd0 = %s %s"
1996 % (repr(kd), repr(kd0)))
1997
1998 if geom == 'realTilt':
1999 angle[0, :] = om
2000 angle[1, :] = chi
2001 angle[3, :] = tth
2002 else:
2003 angle[0, :] = om
2004 angle[2, :] = phi
2005 angle[3, :] = tth
2006 if foutp:
2007 angle[4, :] = psi_i
2008 angle[5, :] = psi_d
2009
2010 if q.shape[0] == 1:
2011 angle = angle.flatten()
2012 if config.VERBOSITY >= config.INFO_ALL:
2013 print("XU.HXRD.Q2Ang: om, chi, phi, tth,[psi_i, psi_d] = %s"
2014 % repr(angle))
2015
2016 if deg:
2017 return numpy.degrees(angle)
2018 else:
2019 return angle
2020
2021
2022 class FourC(HXRD):
2023
2024 """
2025 class describing high angle x-ray diffraction experiments
2026 the class helps with calculating the angles of Bragg reflections
2027 as well as helps with analyzing measured data
2028
2029 the class describes a four circle (omega, chi, phi, twotheta) goniometer to
2030 help with coplanar x-ray diffraction experiments. Nevertheless 3D data can
2031 be treated with the use of linear and area detectors. see help self.Ang2Q
2032 """
2033
2034 def __init__(self, idir, ndir, **keyargs):
2035 """
2036 initialization routine for the FourC Experiment class
2037
2038 Parameters
2039 ----------
2040 idir, ndir, keyargs :
2041 same as for the Experiment base class -> please look at the
2042 docstring of Experiment.__init__ for more details
2043 geometry : {'hi_lo', 'lo_hi', 'real'}, optional
2044 determines the scattering geometry :
2045
2046 - 'hi_lo' (default) high incidence-low exit
2047 - 'lo_hi' low incidence - high exit
2048 - 'real' general geometry - q-coordinates determine
2049 high or low incidence
2050
2051 """
2052 if "qconv" not in keyargs:
2053 # 3S+1D goniometer (standard four-circle goniometer,
2054 # omega, chi, phi, theta)
2055 keyargs['qconv'] = QConversion(['x+', 'y+', 'z-'],
2056 'x+', [0, 1, 0])
2057
2058 HXRD.__init__(self, idir, ndir, **keyargs)
2059
2060
2061 class NonCOP(Experiment):
2062
2063 """
2064 class describing high angle x-ray diffraction experiments. The class helps
2065 with calculating the angles of Bragg reflections as well as helps with
2066 analyzing measured data for NON-COPLANAR measurements, where the tilt is
2067 used to align asymmetric peaks, like in the case of a polefigure
2068 measurement.
2069
2070 The class describes a four circle (omega, chi, phi, twotheta) goniometer to
2071 help with x-ray diffraction experiments. Linear and area detectors can be
2072 treated as described in "help self.Ang2Q"
2073 """
2074
2075 def __init__(self, idir, ndir, **keyargs):
2076 """
2077 initialization routine for the NonCOP Experiment class
2078
2079 Parameters
2080 ----------
2081 idir, ndir, keyargs :
2082 same as for the Experiment base class
2083 """
2084 if "qconv" not in keyargs:
2085 # 3S+1D goniometer (standard four-circle goniometer,
2086 # omega, chi, phi, theta)
2087 keyargs['qconv'] = QConversion(['x+', 'y+', 'z-'],
2088 'x+', [0, 1, 0])
2089
2090 Experiment.__init__(self, idir, ndir, **keyargs)
2091
2092 def Ang2Q(self, om, chi, phi, tt, **kwargs):
2093 """
2094 angular to momentum space conversion for a point detector. Also see
2095 help NonCOP.Ang2Q for procedures which treat line and area detectors
2096
2097 Parameters
2098 ----------
2099 om, chi, phi, tt : float or array-like
2100 sample and detector angles as numpy array, lists or Scalars must be
2101 given. All arguments must have the same shape or length. However,
2102 if one angle is always the same its enough to give one scalar
2103 value.
2104
2105 kwargs : dict, optional
2106 optional keyword arguments
2107 delta : list, tuple or array-like, optional
2108 giving delta angles to correct the given ones for misalignment
2109 delta must be an numpy array or list of length 4. Used angles are
2110 than om, chi, phi, tt - delta
2111 UB : array-like, optional
2112 matrix for conversion from (hkl) coordinates to Q of sample used to
2113 determine not Q but (hkl) (default: identity matrix)
2114 wl : float or str, optional
2115 x-ray wavelength in angstroem (default: self._wl)
2116 deg : bool, optional
2117 flag to tell if angles are passed as degree (default: True)
2118
2119 Returns
2120 -------
2121 ndarray
2122 reciprocal space positions as numpy.ndarray with shape `(N , 3)`
2123 where `N` corresponds to the number of points given in the input
2124 """
2125 # dummy function to have some documentation string available
2126 # the real function is generated dynamically in the __init__ routine
2127 pass
2128
2129 def Q2Ang(self, *Q, **keyargs):
2130 """
2131 Convert a reciprocal space vector Q to NON-COPLANAR scattering angles.
2132 The keyword argument trans determines whether Q should be transformed
2133 to the experimental coordinate frame or not.
2134
2135 Note:
2136 The behavior of this function is unchanged if the goniometer
2137 definition is changed!
2138
2139 Parameters
2140 ----------
2141 Q : list, tuple or array-like
2142 array of shape (3) with q-space vector components or 3
2143 separate lists with qx, qy, qz
2144
2145 trans : bool, optional
2146 apply coordinate transformation on Q (default True)
2147 deg : book, optional
2148 (default True) determines if the angles are returned in radians or
2149 degrees
2150
2151 Returns
2152 -------
2153 ndarray
2154 a numpy array of shape (4) with four scattering
2155 angles which are [omega, chi, phi, twotheta];
2156
2157 - omega : incidence angle with respect to surface
2158 - chi : sample tilt for the case of non-coplanar geometry
2159 - phi : sample azimuth with respect to inplane reference
2160 direction
2161 - twotheta : scattering angle/detector angle
2162 """
2163
2164 valid_kwargs = {'trans': 'coordinate transformation flag',
2165 'deg': 'degree-flag'}
2166 utilities.check_kwargs(keyargs, valid_kwargs, 'Q2Ang')
2167
2168 q = self._prepare_qvec(Q)
2169
2170 trans = keyargs.get('trans', True)
2171 deg = keyargs.get('deg', True)
2172
2173 angle = numpy.zeros((4, q.shape[0]))
2174 # set parameters for the calculation
2175 z = self.Transform(self.ndir) # z
2176 y = self.Transform(self.idir) # y
2177 x = self.Transform(self.scatplane) # x
2178
2179 if trans:
2180 q = self.Transform(q)
2181
2182 if config.VERBOSITY >= config.DEBUG:
2183 print("XU.NonCop.Q2Ang: q= %s" % repr(q))
2184
2185 qa = math.VecNorm(q)
2186 tth = 2. * numpy.arcsin(qa / 2. / self.k0)
2187 om = tth / 2.
2188
2189 # calculation of the sample azimuth
2190 # the sign depends on the phi movement direction
2191 phi = -1 * numpy.arctan2(
2192 math.VecDot(q, x),
2193 math.VecDot(q, y)) - numpy.pi / 2.
2194
2195 chi = (math.VecAngle(q, z))
2196
2197 angle[0, :] = om
2198 angle[1, :] = chi
2199 angle[2, :] = phi
2200 angle[3, :] = tth
2201
2202 if q.shape[0] == 1:
2203 angle = angle.flatten()
2204 if config.VERBOSITY >= config.INFO_ALL:
2205 print("XU.HXRD.Q2Ang: [om, chi, phi, tth] = %s" % repr(angle))
2206
2207 if deg:
2208 return numpy.degrees(angle)
2209 else:
2210 return angle
2211
2212
2213 class GID(Experiment):
2214
2215 """
2216 class describing grazing incidence x-ray diffraction experiments
2217 the class helps with calculating the angles of Bragg reflections
2218 as well as it helps with analyzing measured data
2219
2220 the class describes a four circle (alpha_i, azimuth, twotheta, beta)
2221 goniometer to help with GID experiments at the ROTATING ANODE.
2222 3D data can be treated with the use of linear and area detectors.
2223 see help self.Ang2Q
2224
2225 Using this class the default sample surface orientation is determined by
2226 the inner most sample rotation (which is usually the azimuth motor).
2227 """
2228
2229 def __init__(self, idir, ndir, **keyargs):
2230 """
2231 initialization routine for the GID Experiment class
2232
2233 - ``idir`` defines the inplane reference direction (idir points into
2234 the PB direction at zero angles)
2235 - ``ndir`` defines the surface normal of your sample (ndir points
2236 along the innermost sample rotation axis)
2237
2238 Parameters
2239 ----------
2240 idir, ndir, keyargs :
2241 same as for the Experiment base class
2242 """
2243 if 'sampleor' not in keyargs:
2244 keyargs['sampleor'] = 'sam'
2245
2246 if "qconv" not in keyargs:
2247 # 2S+2D goniometer
2248 keyargs['qconv'] = QConversion(['z-', 'x+'], ['x+', 'z-'],
2249 [0, 1, 0])
2250
2251 Experiment.__init__(self, idir, ndir, **keyargs)
2252
2253 def Q2Ang(self, Q, trans=True, deg=True, **kwargs):
2254 """
2255 calculate the GID angles needed in the experiment
2256 the inplane reference direction defines the direction were
2257 the reference direction is parallel to the primary beam
2258 (i.e. lattice planes perpendicular to the beam)
2259
2260 Note:
2261 The behavior of this function is unchanged if the goniometer
2262 definition is changed!
2263
2264 Parameters
2265 ----------
2266 Q : list, tuple or array-like
2267 array of shape (3) with q-space vector components or 3
2268 separate lists with qx, qy, qz
2269
2270 trans : bool, optional
2271 apply coordinate transformation on Q (default True)
2272 deg : book, optional
2273 (default True) determines if the angles are returned in radians or
2274 degrees
2275
2276 Returns
2277 -------
2278 ndarray
2279 a numpy array of shape (4) with four GID scattering
2280 angles which are [alpha_i, azimuth, twotheta, beta];
2281
2282 - alpha_i : incidence angle to surface (at the moment always 0)
2283 - azimuth : sample rotation with respect to the inplane
2284 reference direction
2285 - twotheta : scattering angle
2286 - beta : exit angle from surface (at the moment always 0)
2287
2288 """
2289
2290 valid_kwargs = {'trans': 'coordinate transformation flag',
2291 'deg': 'degree-flag'}
2292 utilities.check_kwargs(keyargs, valid_kwargs, 'Q2Ang')
2293
2294 if isinstance(Q, list):
2295 q = numpy.array(Q, dtype=numpy.double)
2296 elif isinstance(Q, numpy.ndarray):
2297 q = Q
2298 else:
2299 raise TypeError("Q vector must be a list or numpy array")
2300
2301 if trans:
2302 q = self.Transform(q)
2303
2304 if config.VERBOSITY >= config.INFO_ALL:
2305 print("XU.GID.Q2Ang: q = %s" % repr(q))
2306
2307 # set parameters for the calculation
2308 z = self.Transform(self.ndir) # z
2309 y = self.Transform(self.idir) # y
2310 x = self.Transform(self.scatplane) # x
2311
2312 # check if reflection is inplane
2313 if numpy.abs(math.VecDot(q, z)) >= 0.001:
2314 raise InputError("Reflection not reachable in GID geometry (Q: %s)"
2315 % str(q))
2316
2317 # calculate angle to inplane reference direction
2318 aref = numpy.arctan2(math.VecDot(q, x), math.VecDot(q, y))
2319
2320 # calculate scattering angle
2321 qa = math.VecNorm(q)
2322 tth = 2. * numpy.arcsin(qa / 2. / self.k0)
2323 azimuth = numpy.pi / 2 + aref + tth / 2.
2324
2325 if deg:
2326 ang = [0, numpy.degrees(azimuth), numpy.degrees(tth), 0]
2327 else:
2328 ang = [0, azimuth, tth, 0]
2329
2330 if config.VERBOSITY >= config.INFO_ALL:
2331 print("XU.GID.Q2Ang: [ai, azimuth, tth, beta] = %s \n difference "
2332 "to inplane reference which is %5.2f" % (str(ang), aref))
2333
2334 return ang
2335
2336 def Ang2Q(self, ai, phi, tt, beta, **kwargs):
2337 """
2338 angular to momentum space conversion for a point detector. Also see
2339 help GID.Ang2Q for procedures which treat line and area detectors
2340
2341 Parameters
2342 ----------
2343 ai, phi, tt, beta : float or array-like
2344 sample and detector angles as numpy array, lists or Scalars must be
2345 given. All arguments must have the same shape or length. However,
2346 if one angle is always the same its enough to give one scalar
2347 value.
2348
2349 kwargs : dict, optional
2350 optional keyword arguments
2351 delta : list, tuple or array-like, optional
2352 giving delta angles to correct the given ones for misalignment
2353 delta must be an numpy array or list of length 4. Used angles are
2354 then ``ai, phi, tt, beta - delta``
2355 UB : array-like, optional
2356 matrix for conversion from (hkl) coordinates to Q of sample used to
2357 determine not Q but (hkl) (default: identity matrix)
2358 wl : float or str, optional
2359 x-ray wavelength in angstroem (default: self._wl)
2360 deg : bool, optional
2361 flag to tell if angles are passed as degree (default: True)
2362
2363 Returns
2364 -------
2365 ndarray
2366 reciprocal space positions as numpy.ndarray with shape `(N , 3)`
2367 where `N` corresponds to the number of points given in the input
2368 """
2369 # dummy function to have some documentation string available
2370 # the real function is generated dynamically in the __init__ routine
2371 pass
2372
2373
2374 class GISAXS(Experiment):
2375
2376 """
2377 class describing grazing incidence x-ray diffraction experiments
2378 the class helps with calculating the angles of Bragg reflections
2379 as well as it helps with analyzing measured data
2380
2381 the class describes a three circle (alpha_i, twotheta, beta)
2382 goniometer to help with GISAXS experiments at the ROTATING ANODE.
2383 3D data can be treated with the use of linear and area detectors.
2384 see help self.Ang2Q
2385 """
2386
2387 def __init__(self, idir, ndir, **keyargs):
2388 """
2389 initialization routine for the GISAXS Experiment class
2390
2391 ``idir`` defines the inplane reference direction (idir points into the
2392 PB direction at zero angles)
2393
2394 Parameters
2395 ----------
2396 idir, ndir, keyargs :
2397 same as for the Experiment base class
2398 """
2399 if "qconv" not in keyargs:
2400 # 1S+2D goniometer
2401 keyargs['qconv'] = QConversion(['x+'], ['x+', 'z-'],
2402 [0, 1, 0])
2403
2404 Experiment.__init__(self, idir, ndir, **keyargs)
2405
2406 def Q2Ang(self, Q, trans=True, deg=True, **kwargs):
2407 pass
2408
2409 def Ang2Q(self, ai, tt, beta, **kwargs):
2410 """
2411 angular to momentum space conversion for a point detector. Also see
2412 help GISAXS.Ang2Q for procedures which treat line and area detectors
2413
2414 Parameters
2415 ----------
2416 ai, tt, beta : float or array-like
2417 sample and detector angles as numpy array, lists or Scalars must be
2418 given. all arguments must have the same shape or length. Howevver,
2419 if one angle is always the same its enough to give one scalar
2420 value.
2421
2422 kwargs : dict, optional
2423 optional keyword arguments
2424 delta : list, tuple or array-like, optional
2425 giving delta angles to correct the given ones for misalignment
2426 delta must be an numpy array or list of length 3. Used angles are
2427 then ``ai, tt, beta - delta``
2428 UB : array-like, optional
2429 matrix for conversion from (hkl) coordinates to Q of sample used to
2430 determine not Q but (hkl) (default: identity matrix)
2431 wl : float or str, optional
2432 x-ray wavelength in angstroem (default: self._wl)
2433 deg : bool, optional
2434 flag to tell if angles are passed as degree (default: True)
2435
2436 Returns
2437 -------
2438 ndarray
2439 reciprocal space positions as numpy.ndarray with shape `(N , 3)`
2440 where `N` corresponds to the number of points given in the input
2441 """
2442 # dummy function to have some documentation string available
2443 # the real function is generated dynamically in the __init__ routine
2444 pass
2445
2446
2447 class PowderExperiment(Experiment):
2448 """
2449 Experimental class for powder diffraction which helps to convert theta
2450 angles to momentum transfer space
2451 """
2452
2453 def __init__(self, **kwargs):
2454 """
2455 class constructor which takes the same keyword arguments as the
2456 Experiment class
2457
2458 Parameters
2459 ----------
2460 kwargs : dict, optional
2461 keyword arguments same as for the Experiment base class
2462 """
2463 Experiment.__init__(self, [0, 1, 0], [0, 0, 1], **kwargs)
2464 self.Ang2Q = self._Ang2Q
2465
2466 def _Ang2Q(self, th, deg=True):
2467 """
2468 Converts theta angles to reciprocal space positions
2469 returns the absolute value of momentum transfer
2470 """
2471 if deg:
2472 lth = numpy.radians(th)
2473 else:
2474 lth = th
2475
2476 qpos = 2 * self.k0 * numpy.sin(lth)
2477 return qpos
2478
2479 def Q2Ang(self, qpos, deg=True):
2480 """
2481 Converts reciprocal space values to theta angles
2482 """
2483 th = numpy.arcsin(qpos / (2 * self.k0))
2484
2485 if deg:
2486 th = numpy.degrees(th)
2487
2488 return th
+0
-367
xrayutilities/gridder.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010, 2013
16 # Eugen Wintersberger <eugen.wintersberger@desy.de>
17 # Copyright (C) 2009 Mario Keplinger <mario.keplinger@jku.at>
18 # Copyright (C) 2009-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
19
20 import abc
21
22 import numpy
23
24 from . import config, cxrayutilities, utilities
25 from .exception import InputError
26
27
28 def delta(min_value, max_value, n):
29 """
30 Compute the stepsize along an axis of a grid.
31
32 Parameters
33 ----------
34 min_value : axis minimum value
35 max_value : axis maximum value
36 n : number of steps
37 """
38 if n != 1:
39 return (float(max_value) - float(min_value)) / float(n - 1)
40 else:
41 return numpy.inf
42
43
44 def axis(min_value, max_value, n):
45 """
46 Compute the a grid axis.
47
48 Parameters
49 ----------
50 min_value : float
51 axis minimum value
52 max_value : float
53 axis maximum value
54 n : int
55 number of steps
56 """
57
58 if n != 1:
59 d = delta(min_value, max_value, n)
60 a = min_value + d * numpy.arange(0, n)
61 else:
62 a = (min_value + max_value) / 2.
63
64 return a
65
66
67 def ones(*args):
68 """
69 Compute ones for matrix generation. The shape is determined by the number
70 of input arguments.
71 """
72 return numpy.ones(args, dtype=numpy.double)
73
74
75 class Gridder(utilities.ABC):
76 """
77 Basis class for gridders in xrayutilities. A gridder is a function mapping
78 irregular spaced data onto a regular grid by binning the data into equally
79 sized elements.
80
81 There are different ways of defining the regular grid of a Gridder. In
82 xrayutilities the data bins extend beyond the data range in the input data,
83 but the given position being the center of these bins, extends from the
84 minimum to the maximum of the data! The main motivation for this was to
85 create a Gridder, which when feeded with N equidistant data points and
86 gridded with N bins would not change the data position (not the case with
87 numpy.histogramm functions!). Of course this leads to the fact that for
88 homogeneous point density the first and last bin in any direction are not
89 filled as the other bins.
90
91 A different definition is used by numpy histogram functions where the bins
92 extend only to the end of the data range. (see numpy histogram,
93 histrogram2d, ...)
94 """
95
96 def __init__(self):
97 """
98 Constructor defining default properties of any Gridder class
99 """
100
101 self.flags = 0
102 # by default every call to gridder will start a new gridding
103 self.keep_data = False
104 self.normalize = True
105 # flag to allow for sequential gridding with fixed data range
106 self.fixed_range = False
107
108 # no data initialization necessary in c-code
109 self.flags = utilities.set_bit(self.flags, 0)
110
111 if not hasattr(self, '_gdata'):
112 self._gdata = numpy.empty(0)
113 if not hasattr(self, '_gnorm'):
114 self._gnorm = numpy.empty(0)
115
116 if config.VERBOSITY >= config.INFO_ALL:
117 self.flags = utilities.set_bit(self.flags, 3)
118
119 @abc.abstractmethod
120 def __call__(self):
121 """
122 abstract call method which every implementation of a Gridder has to
123 override
124 """
125 pass
126
127 def Normalize(self, bool):
128 """
129 set or unset the normalization flag. Normalization needs to be done to
130 obtain proper gridding but may want to be disabled in certain cases
131 when sequential gridding is performed
132 """
133 if bool not in [False, True]:
134 raise TypeError("Normalize flag must be a boolan value "
135 "(True/False)!")
136 self.normalize = bool
137 if bool:
138 self.flags = utilities.clear_bit(self.flags, 2)
139 else:
140 self.flags = utilities.set_bit(self.flags, 2)
141
142 def KeepData(self, bool):
143 if bool not in [False, True]:
144 raise TypeError("Keep Data flag must be a boolan value"
145 "(True/False)!")
146
147 self.keep_data = bool
148
149 def __get_data(self):
150 """
151 return gridded data (performs normalization if switched on)
152 """
153 if self.normalize:
154 tmp = numpy.copy(self._gdata)
155 mask = (self._gnorm != 0)
156 tmp[mask] /= self._gnorm[mask].astype(numpy.double)
157 return tmp
158 else:
159 return self._gdata.copy()
160
161 data = property(__get_data)
162
163 def _prepare_array(self, a):
164 """
165 prepare array for passing to c-code
166 """
167 if isinstance(a, (list, tuple, numpy.float, numpy.int)):
168 a = numpy.asarray(a)
169 return a.reshape(a.size)
170
171 def Clear(self):
172 """
173 Clear so far gridded data to reuse this instance of the Gridder
174 """
175 self._gdata[...] = 0
176 self._gnorm[...] = 0
177
178
179 class Gridder1D(Gridder):
180
181 def __init__(self, nx):
182 Gridder.__init__(self)
183 if nx <= 0:
184 raise InputError('nx must be a positiv integer!')
185
186 self.nx = nx
187 self.xmin = 0
188 self.xmax = 0
189 self._gdata = numpy.zeros(nx, dtype=numpy.double)
190 self._gnorm = numpy.zeros(nx, dtype=numpy.double)
191
192 def savetxt(self, filename, header=''):
193 """
194 save gridded data to a txt file with two columns. The first column is
195 the data coordinate and the second the corresponding data value
196
197 Parameters
198 ----------
199 filename : str
200 output filename
201 header : str, optional
202 optional header for the data file.
203 """
204 numpy.savetxt(filename, numpy.vstack((self.xaxis, self.data)).T,
205 header=header, fmt='%.6g %.4g')
206
207 def __get_xaxis(self):
208 """
209 Returns the xaxis of the gridder
210 the returned values correspond to the center of the data bins used by
211 the gridding algorithm
212 """
213 return axis(self.xmin, self.xmax, self.nx)
214
215 xaxis = property(__get_xaxis)
216
217 def dataRange(self, min, max, fixed=True):
218 """
219 define minimum and maximum data range, usually this is deduced
220 from the given data automatically, however, for sequential
221 gridding it is useful to set this before the first call of the
222 gridder. data outside the range are simply ignored
223
224 Parameters
225 ----------
226 min : float
227 minimum value of the gridding range
228 max : float
229 maximum value of the gridding range
230 fixed : bool, optional
231 flag to turn fixed range gridding on (True (default)) or off
232 (False)
233 """
234 self.fixed_range = fixed
235 self.xmin = min
236 self.xmax = max
237
238 def _checktransinput(self, x, data):
239 """
240 common checks and reshape commands for the input data. This function
241 checks the data type and shape of the input data.
242 """
243 if not self.keep_data:
244 self.Clear()
245
246 x = self._prepare_array(x)
247 data = self._prepare_array(data)
248
249 if x.size != data.size:
250 raise InputError("XU.%s: size of given datasets (x, data)"
251 " is not equal!" % self.__class__.__name__)
252
253 if not self.fixed_range:
254 # assume that with setting keep_data the user wants to call the
255 # gridder more often and obtain a reasonable result
256 self.dataRange(x.min(), x.max(), self.keep_data)
257
258 return x, data
259
260 def __call__(self, x, data):
261 """
262 Perform gridding on a set of data. After running the gridder
263 the 'data' object in the class is holding the gridded data.
264
265 Parameters
266 ----------
267 x : ndarray
268 numpy array of arbitrary shape with x positions
269 data : ndarray
270 numpy array of arbitrary shape with data values
271 """
272 x, data = self._checktransinput(x, data)
273 # remove normalize flag for C-code, normalization is always performed
274 # in python
275 flags = utilities.set_bit(self.flags, 2)
276 cxrayutilities.gridder1d(x, data, self.nx, self.xmin, self.xmax,
277 self._gdata, self._gnorm, flags)
278
279
280 class FuzzyGridder1D(Gridder1D):
281 """
282 An 1D binning class considering every data point to have a finite width.
283 If necessary one data point will be split fractionally over different
284 data bins. This is numerically more effort but represents better the
285 typical case of a experimental data, which do not represent a mathematical
286 point but have a finite width (e.g. X-ray data from a 1D detector).
287 """
288
289 def __call__(self, x, data, width=None):
290 """
291 Perform gridding on a set of data. After running the gridder
292 the 'data' object in the class is holding the gridded data.
293
294 Parameters
295 ----------
296 x : ndarray
297 numpy array of arbitrary shape with x positions
298 data : ndarray
299 numpy array of arbitrary shape with data values
300 width : float, optional
301 width of one data point. If not given half the bin size will be
302 used.
303 """
304 x, data = self._checktransinput(x, data)
305
306 if not width:
307 width = delta(self.xmin, self.xmax, self.nx) / 2.
308
309 # remove normalize flag for C-code, normalization is always performed
310 # in python
311 flags = utilities.set_bit(self.flags, 2)
312 cxrayutilities.fuzzygridder1d(x, data, self.nx, self.xmin, self.xmax,
313 self._gdata, self._gnorm, width, flags)
314
315
316 class npyGridder1D(Gridder1D):
317
318 def __get_xaxis(self):
319 """
320 Returns the xaxis of the gridder
321 the returned values correspond to the center of the data bins used by
322 the numpy.histogram function
323 """
324 # no -1 here to be consistent with numpy.histogram
325 dx = (float(self.xmax - self.xmin)) / float(self.nx)
326 ax = self.xmin + dx * numpy.arange(0, self.nx) + dx / 2.
327 return ax
328
329 xaxis = property(__get_xaxis)
330
331 def __call__(self, x, data):
332 """
333 Perform gridding on a set of data. After running the gridder
334 the 'data' object in the class is holding the gridded data.
335
336 Parameters
337 ----------
338 x : ndarray
339 numpy array of arbitrary shape with x positions
340 data : ndarray
341 numpy array of arbitrary shape with data values
342 """
343
344 x, data = self._checktransinput(x, data)
345
346 # use only non-NaN data values
347 mask = numpy.invert(numpy.isnan(data))
348 ldata = data[mask]
349 lx = x[mask]
350
351 if not self.fixed_range:
352 # assume that with setting keep_data the user wants to call the
353 # gridder more often and obtain a reasonable result
354 self.dataRange(lx.min(), lx.max(), self.keep_data)
355
356 # grid the data using numpy histogram
357 tmpgdata, bins = numpy.histogram(lx, weights=ldata, bins=self.nx,
358 range=(self.xmin, self.xmax))
359 tmpgnorm, bins = numpy.histogram(lx, bins=self.nx,
360 range=(self.xmin, self.xmax))
361 if self.keep_data:
362 self._gnorm += tmpgnorm
363 self._gdata += tmpgdata
364 else:
365 self._gnorm = tmpgnorm
366 self._gdata = tmpgdata
+0
-297
xrayutilities/gridder2d.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010, 2013
16 # Eugen Wintersberger <eugen.wintersberger@desy.de>
17 # Copyright (C) 2009 Mario Keplinger <mario.keplinger@jku.at>
18 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
19
20 import numpy
21
22 from . import cxrayutilities, exception, utilities
23 from .gridder import Gridder, axis, delta, ones
24
25
26 class Gridder2D(Gridder):
27
28 def __init__(self, nx, ny):
29 Gridder.__init__(self)
30
31 # check input
32 if nx <= 0 or ny <= 0:
33 raise exception.InputError('Neither nx nor ny can be smaller'
34 'than 1!')
35
36 self.xmin = None
37 self.ymin = None
38 self.xmax = None
39 self.ymax = None
40
41 self.nx = nx
42 self.ny = ny
43
44 self._allocate_memory()
45
46 def _allocate_memory(self):
47 """
48 Class method to allocate memory for the gridder based on the nx, ny
49 class attributes.
50 """
51
52 self._gdata = numpy.zeros((self.nx, self.ny), dtype=numpy.double)
53 self._gnorm = numpy.zeros((self.nx, self.ny), dtype=numpy.double)
54
55 def savetxt(self, filename, header=''):
56 """
57 save gridded data to a txt file with two columns. The first two columns
58 are the data coordinates and the last one the corresponding data
59 value.
60
61 Parameters
62 ----------
63 filename : str
64 output filename
65 header : str, optional
66 optional header for the data file.
67 """
68 numpy.savetxt(filename, numpy.vstack((self.xmatrix.flat,
69 self.ymatrix.flat,
70 self.data.flat)).T,
71 header=header, fmt='%.6g %.6g %.4g')
72
73 def SetResolution(self, nx, ny):
74 """
75 Reset the resolution of the gridder. In this case the original data
76 stored in the object will be deleted.
77
78 Parameters
79 ----------
80 nx : int
81 number of points in x-direction
82 ny : int
83 number of points in y-direction
84 """
85 self.nx = nx
86 self.ny = ny
87
88 self._allocate_memory()
89
90 def __get_xaxis(self):
91 return axis(self.xmin, self.xmax, self.nx)
92
93 def __get_yaxis(self):
94 return axis(self.ymin, self.ymax, self.ny)
95
96 def __get_xmatrix(self):
97 return ones(self.nx, self.ny) * self.xaxis[:, numpy.newaxis]
98
99 def __get_ymatrix(self):
100 return ones(self.nx, self.ny) * self.yaxis[numpy.newaxis, :]
101
102 yaxis = property(__get_yaxis)
103 xaxis = property(__get_xaxis)
104 xmatrix = property(__get_xmatrix)
105 ymatrix = property(__get_ymatrix)
106
107 def dataRange(self, xmin, xmax, ymin, ymax, fixed=True):
108 """
109 define minimum and maximum data range, usually this is deduced
110 from the given data automatically, however, for sequential
111 gridding it is useful to set this before the first call of the
112 gridder. data outside the range are simply ignored
113
114 Parameters
115 ----------
116 xmin, ymin : float
117 minimum value of the gridding range in x, y
118 xmax, ymax : float
119 maximum value of the gridding range in x, y
120 fixed : bool, optional
121 flag to turn fixed range gridding on (True (default)) or off
122 (False)
123 """
124 self.fixed_range = fixed
125 self.xmin = xmin
126 self.xmax = xmax
127 self.ymin = ymin
128 self.ymax = ymax
129
130 def _checktransinput(self, x, y, data):
131 """
132 common checks and reshape commands for the input data. This function
133 checks the data type and shape of the input data.
134 """
135 if not self.keep_data:
136 self.Clear()
137
138 x = self._prepare_array(x)
139 y = self._prepare_array(y)
140 data = self._prepare_array(data)
141
142 if x.size != y.size or y.size != data.size:
143 raise exception.InputError("XU.%s: size of given datasets "
144 "(x, y, data) is not equal!"
145 % self.__class__.__name__)
146
147 if not self.fixed_range:
148 # assume that with setting keep_data the user wants to call the
149 # gridder more often and obtain a reasonable result
150 self.dataRange(x.min(), x.max(), y.min(), y.max(), self.keep_data)
151
152 return x, y, data
153
154 def __call__(self, x, y, data):
155 """
156 Perform gridding on a set of data. After running the gridder
157 the 'data' object in the class is holding the gridded data.
158
159 Parameters
160 ----------
161 x : ndarray
162 numpy array of arbitrary shape with x positions
163 y : ndarray
164 numpy array of arbitrary shape with y positions
165 data : ndarray
166 numpy array of arbitrary shape with data values
167 """
168 x, y, data = self._checktransinput(x, y, data)
169 # remove normalize flag for C-code
170 flags = utilities.set_bit(self.flags, 2)
171 cxrayutilities.gridder2d(x, y, data, self.nx, self.ny,
172 self.xmin, self.xmax,
173 self.ymin, self.ymax,
174 self._gdata, self._gnorm, flags)
175
176
177 class FuzzyGridder2D(Gridder2D):
178 """
179 An 2D binning class considering every data point to have a finite area.
180 If necessary one data point will be split fractionally over different
181 data bins. This is numerically more effort but represents better the
182 typical case of a experimental data, which do not represent a mathematical
183 point but have a finite size (e.g. X-ray data from a 2D detector or
184 reciprocal space maps measured with point/linear detector).
185
186 Currently only a rectangular area can be considered during the gridding.
187 """
188
189 def __call__(self, x, y, data, **kwargs):
190 """
191 Perform gridding on a set of data. After running the gridder
192 the 'data' object in the class is holding the gridded data.
193
194 Parameters
195 ----------
196 x : ndarray
197 numpy array of arbitrary shape with x positions
198 y : ndarray
199 numpy array of arbitrary shape with y positions
200 data : ndarray
201 numpy array of arbitrary shape with data values
202 width : float or tuple or list, optional
203 width of one data point. If not given half the bin size will be
204 used. The width can be given as scalar if it is equal for both data
205 dimensions, or as sequence of length 2.
206 """
207
208 valid_kwargs = {'width': 'specifiying fuzzy data size'}
209 utilities.check_kwargs(kwargs, valid_kwargs,
210 self.__class__.__name__)
211
212 x, y, data = self._checktransinput(x, y, data)
213
214 if 'width' in kwargs:
215 try:
216 length = len(kwargs['width'])
217 except TypeError:
218 length = 1
219 if length == 2:
220 wx, wy = kwargs['width']
221 else:
222 wx = kwargs['width']
223 wy = wx
224 else:
225 wx = delta(self.xmin, self.xmax, self.nx) / 2.
226 wy = delta(self.ymin, self.ymax, self.ny) / 2.
227 # remove normalize flag for C-code
228 flags = utilities.set_bit(self.flags, 2)
229 cxrayutilities.fuzzygridder2d(x, y, data, self.nx, self.ny,
230 self.xmin, self.xmax,
231 self.ymin, self.ymax,
232 self._gdata, self._gnorm, wx, wy, flags)
233
234
235 class Gridder2DList(Gridder2D):
236
237 """
238 special version of a 2D gridder which performs no actual averaging of the
239 data in one grid/bin but just collects the data-objects belonging to one
240 bin for further treatment by the user
241 """
242
243 def _allocate_memory(self):
244 """
245 Class method to allocate memory for the gridder based on the nx, ny
246 class attributes.
247 """
248
249 self._gdata = numpy.empty((self.nx, self.ny), dtype=list)
250 for i in range(self.nx):
251 for j in range(self.ny):
252 self._gdata[i, j] = []
253 self._gnorm = numpy.zeros((self.nx, self.ny), dtype=numpy.int)
254
255 def Clear(self):
256 self._allocate_memory()
257
258 def __get_data(self):
259 """
260 return gridded data, in this special version no normalization is
261 defined!
262 """
263 return self._gdata.copy()
264
265 data = property(__get_data)
266
267 def __call__(self, x, y, data):
268 """
269 Perform gridding on a set of data. After running the gridder the 'data'
270 object in the class is holding the lists of data-objects belonging to
271 one bin/grid-point.
272
273 Parameters
274 ----------
275 x : ndarray
276 numpy array of arbitrary shape with x positions
277 y : ndarray
278 numpy array of arbitrary shape with y positions
279 data : ndarray, list or tuple
280 data of same length as x, y but of arbitrary type
281 """
282
283 x, y, data = self._checktransinput(x, y, data)
284
285 # perform gridding this should be moved to native code if possible
286 def gindex(x, min, delt):
287 return numpy.round((x - min) / delt).astype(numpy.int)
288
289 xdelta = delta(self.xmin, self.xmax, self.nx)
290 ydelta = delta(self.ymin, self.ymax, self.ny)
291
292 for i in range(len(x)):
293 xidx = gindex(x[i], self.xmin, xdelta)
294 yidx = gindex(y[i], self.ymin, ydelta)
295 self._gdata[xidx, yidx].append(data[i])
296 self._gnorm[xidx, yidx] += 1
+0
-237
xrayutilities/gridder3d.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010, 2013
16 # Eugen Wintersberger <eugen.wintersberger@desy.de>
17 # Copyright (C) 2009 Mario Keplinger <mario.keplinger@jku.at>
18 # Copyright (C) 2009-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
19
20 import numpy
21
22 from . import cxrayutilities, exception, utilities
23 from .gridder import Gridder, axis, delta, ones
24
25
26 class Gridder3D(Gridder):
27
28 def __init__(self, nx, ny, nz):
29 Gridder.__init__(self)
30
31 # check input
32 if nx <= 0 or ny <= 0 or nz <= 0:
33 raise exception.InputError('None of nx, ny and nz can be smaller '
34 'than 1!')
35
36 self.xmin = 0
37 self.xmax = 0
38 self.ymin = 0
39 self.ymax = 0
40 self.zmin = 0
41 self.zmax = 0
42
43 self.nx = nx
44 self.nz = nz
45 self.ny = ny
46
47 self._allocate_memory()
48
49 def _allocate_memory(self):
50 """
51 Class method to allocate memory for the gridder based on the nx, ny
52 class attributes.
53 """
54 self._gdata = numpy.zeros((self.nx, self.ny, self.nz),
55 dtype=numpy.double)
56 self._gnorm = numpy.zeros((self.nx, self.ny, self.nz),
57 dtype=numpy.double)
58
59 def SetResolution(self, nx, ny, nz):
60 self.nx = nx
61 self.ny = ny
62 self.nz = nz
63
64 self._allocate_memory()
65
66 def __get_xaxis(self):
67 return axis(self.xmin, self.xmax, self.nx)
68
69 def __get_yaxis(self):
70 return axis(self.ymin, self.ymax, self.ny)
71
72 def __get_zaxis(self):
73 return axis(self.zmin, self.zmax, self.nz)
74
75 def __get_xmatrix(self):
76 return ones(self.nx, self.ny, self.nz) *\
77 self.xaxis[:, numpy.newaxis, numpy.newaxis]
78
79 def __get_ymatrix(self):
80 return ones(self.nx, self.ny, self.nz) *\
81 self.yaxis[numpy.newaxis, :, numpy.newaxis]
82
83 def __get_zmatrix(self):
84 return ones(self.nx, self.ny, self.nz) *\
85 self.zaxis[numpy.newaxis, numpy.newaxis, :]
86
87 zaxis = property(__get_zaxis)
88 zmatrix = property(__get_zmatrix)
89 xaxis = property(__get_xaxis)
90 xmatrix = property(__get_xmatrix)
91 yaxis = property(__get_yaxis)
92 ymatrix = property(__get_ymatrix)
93
94 def dataRange(self, xmin, xmax, ymin, ymax, zmin, zmax, fixed=True):
95 """
96 define minimum and maximum data range, usually this is deduced
97 from the given data automatically, however, for sequential
98 gridding it is useful to set this before the first call of the
99 gridder. data outside the range are simply ignored
100
101 Parameters
102 ----------
103 xmin, ymin, zmin : float
104 minimum value of the gridding range in x, y, z
105 xmax, ymax, zmax : float
106 maximum value of the gridding range in x, y, z
107 fixed : bool, optional
108 flag to turn fixed range gridding on (True (default)) or off
109 (False)
110 """
111 self.fixed_range = fixed
112 self.xmin = xmin
113 self.xmax = xmax
114 self.ymin = ymin
115 self.ymax = ymax
116 self.zmin = zmin
117 self.zmax = zmax
118
119 def _checktransinput(self, x, y, z, data):
120 """
121 common checks and reshape commands for the input data. This function
122 checks the data type and shape of the input data.
123 """
124 if not self.keep_data:
125 self.Clear()
126
127 x = self._prepare_array(x)
128 y = self._prepare_array(y)
129 z = self._prepare_array(z)
130 data = self._prepare_array(data)
131
132 if x.size != y.size or y.size != z.size or z.size != data.size:
133 raise exception.InputError("XU.%s: size of given datasets "
134 "(x, y, z, data) is not equal!"
135 % self.__class__.__name__)
136
137 if not self.fixed_range:
138 # assume that with setting keep_data the user wants to call the
139 # gridder more often and obtain a reasonable result
140 self.dataRange(x.min(), x.max(),
141 y.min(), y.max(),
142 z.min(), z.max(),
143 self.keep_data)
144
145 return x, y, z, data
146
147 def __call__(self, x, y, z, data):
148 """
149 Perform gridding on a set of data. After running the gridder
150 the 'data' object in the class is holding the gridded data.
151
152 Parameters
153 ----------
154 x : ndarray
155 numpy array of arbitrary shape with x positions
156 y : ndarray
157 numpy array of arbitrary shape with y positions
158 z : ndarray
159 numpy array fo arbitrary shape with z positions
160 data : ndarray
161 numpy array of arbitrary shape with data values
162 """
163
164 x, y, z, data = self._checktransinput(x, y, z, data)
165
166 # remove normalize flag for C-code
167 flags = utilities.set_bit(self.flags, 2)
168 cxrayutilities.gridder3d(x, y, z, data, self.nx, self.ny, self.nz,
169 self.xmin, self.xmax,
170 self.ymin, self.ymax,
171 self.zmin, self.zmax,
172 self._gdata, self._gnorm, flags)
173
174
175 class FuzzyGridder3D(Gridder3D):
176 """
177 An 3D binning class considering every data point to have a finite volume.
178 If necessary one data point will be split fractionally over different
179 data bins. This is numerically more effort but represents better the
180 typical case of a experimental data, which do not represent a mathematical
181 point but have a finite size.
182
183 Currently only a quader can be considered as volume during the gridding.
184 """
185
186 def __call__(self, x, y, z, data, **kwargs):
187 """
188 Perform gridding on a set of data. After running the gridder
189 the 'data' object in the class is holding the gridded data.
190
191 Parameters
192 ----------
193 x : ndarray
194 numpy array of arbitrary shape with x positions
195 y : ndarray
196 numpy array of arbitrary shape with y positions
197 z : ndarray
198 numpy array fo arbitrary shape with z positions
199 data : ndarray
200 numpy array of arbitrary shape with data values
201 width : float, tuple or list, optional
202 width of one data point. If not given half the bin size will be
203 used. The width can be given as scalar if it is equal for all three
204 dimensions, or as sequence of length 3.
205 """
206
207 valid_kwargs = {'width': 'specifiying fuzzy data size'}
208 utilities.check_kwargs(kwargs, valid_kwargs,
209 self.__class__.__name__)
210
211 x, y, z, data = self._checktransinput(x, y, z, data)
212
213 if 'width' in kwargs:
214 try:
215 length = len(kwargs['width'])
216 except TypeError:
217 length = 1
218 if length == 3:
219 wx, wy, wz = kwargs['width']
220 else:
221 wx = kwargs['width']
222 wy = wx
223 wz = wx
224 else:
225 wx = delta(self.xmin, self.xmax, self.nx) / 2.
226 wy = delta(self.ymin, self.ymax, self.ny) / 2.
227 wz = delta(self.zmin, self.zmax, self.nz) / 2.
228
229 # remove normalize flag for C-code
230 flags = utilities.set_bit(self.flags, 2)
231 cxrayutilities.fuzzygridder3d(x, y, z, data, self.nx, self.ny, self.nz,
232 self.xmin, self.xmax,
233 self.ymin, self.ymax,
234 self.zmin, self.zmax,
235 self._gdata, self._gnorm,
236 wx, wy, wz, flags)
+0
-34
xrayutilities/io/__init__.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 from .cbf import CBFDirectory, CBFFile
19 from .desy_tty08 import gettty08_scan, tty08File
20 from .edf import EDFDirectory, EDFFile
21 from .fastscan import FastScan, FastScanCCD, FastScanSeries
22 from .helper import xu_h5open, xu_open
23 from .ill_numor import numor_scan, numorFile
24 from .imagereader import (ImageReader, PerkinElmer, Pilatus100K, RoperCCD,
25 TIFFRead, get_tiff)
26 from .panalytical_xml import XRDMLFile, getxrdml_map, getxrdml_scan
27 from .pdcif import pdCIF, pdESG
28 from .rigaku_ras import RASFile, RASScan, getras_scan
29 # parser for the alignment log file of the rotating anode
30 from .rotanode_alignment import RA_Alignment
31 from .seifert import SeifertMultiScan, SeifertScan, getSeifert_map
32 from .spec import SPECFile, SPECLog, SPECScan, geth5_scan, getspec_scan
33 from .spectra import SPECTRAFile, geth5_spectra_map
+0
-156
xrayutilities/io/cbf.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2013, 2015 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 # module for handling files stored in the CBF data format
18
19 import os.path
20 import re
21
22 import numpy
23
24 from .. import config, cxrayutilities, utilities
25 from .filedir import FileDirectory
26 from .helper import xu_h5open, xu_open
27
28 cbf_name_start_num = re.compile(r"^\d")
29
30
31 class CBFFile(object):
32
33 def __init__(self, fname, nxkey="X-Binary-Size-Fastest-Dimension",
34 nykey="X-Binary-Size-Second-Dimension",
35 dtkey="DataType", path=None):
36 """
37 CBF detector image parser
38
39 Parameters
40 ----------
41 fname : str
42 name of the CBF file of type .cbf or .cbf.gz
43 nxkey : str, optional
44 name of the header key that holds the number of points in
45 x-direction
46 nykey : str, optional
47 name of the header key that holds the number of points in
48 y-direction
49 dtkey : str, optional
50 name of the header key that holds the datatype for the binary data
51 path : str, optional
52 path to the CBF file
53 """
54
55 self.filename = fname
56 if path:
57 self.full_filename = os.path.join(path, fname)
58 else:
59 self.full_filename = self.filename
60
61 # evaluate keyword arguments
62 self.nxkey = nxkey
63 self.nykey = nykey
64 self.dtkey = dtkey
65
66 # create attributes for holding data
67 self.data = None
68 self.ReadData()
69
70 def ReadData(self):
71 """
72 Read the CCD data into the .data object
73 this function is called by the initialization
74 """
75 with xu_open(self.full_filename, 'rb') as fid:
76 tmp = numpy.fromfile(file=fid, dtype="u1").tostring()
77 tmp2 = tmp.decode('ascii', 'ignore')
78 # read header information
79 pos = tmp2.index(self.nxkey + ':') + len(self.nxkey + ':')
80 self.xdim = int(tmp2[pos:pos + 6].strip())
81 pos = tmp2.index(self.nykey + ':') + len(self.nykey + ':')
82 self.ydim = int(tmp2[pos:pos + 6].strip())
83
84 self.data = cxrayutilities.cbfread(tmp, self.xdim, self.ydim)
85 self.data.shape = (self.ydim, self.xdim)
86
87 def Save2HDF5(self, h5f, group="/", comp=True):
88 """
89 Saves the data stored in the EDF file in a HDF5 file as a HDF5 array.
90 By default the data is stored in the root group of the HDF5 file - this
91 can be changed by passing the name of a target group or a path to the
92 target group via the "group" keyword argument.
93
94 Parameters
95 ----------
96 h5f : file-handle or str
97 a HDF5 file object or name
98 group : str, optional
99 group where to store the data (default to the root of the file)
100 comp : bool, optional
101 activate compression - true by default
102 """
103 with xu_h5open(h5f, 'a') as h5:
104 if isinstance(group, str):
105 g = h5.get(group)
106 else:
107 g = group
108
109 # create the array name
110 name = os.path.split(self.filename)[-1]
111 name = os.path.splitext(name)[0]
112 # perform a second time for case of .cbf.gz files
113 name = os.path.splitext(name)[0]
114 name = utilities.makeNaturalName(name)
115 if cbf_name_start_num.match(name):
116 name = "ccd_" + name
117 if config.VERBOSITY >= config.INFO_ALL:
118 print("xu.io.CBFFile: HDF5 group name: %s" % name)
119
120 # create the array description
121 desc = "CBF CCD data from file %s " % (self.filename)
122
123 # create the dataset for the array
124 kwds = {'fletcher32': True}
125 if comp:
126 kwds['compression'] = 'gzip'
127
128 try:
129 ca = g.create_dataset(name, data=self.data, **kwds)
130 except ValueError:
131 del g[name]
132 ca = g.create_dataset(name, data=self.data, **kwds)
133
134 ca.attrs['TITLE'] = desc
135
136
137 class CBFDirectory(FileDirectory):
138
139 """
140 Parses a directory for CBF files, which can be stored to a HDF5 file for
141 further usage
142 """
143
144 def __init__(self, datapath, ext="cbf", **keyargs):
145 """
146 Parameters
147 ----------
148 datapath : str
149 directory of the CBF files
150 ext : str, optional
151 extension of the ccd files in the datapath (default: "cbf")
152 keyargs : dict, optional
153 further keyword arguments are passed to CBFFile
154 """
155 super(CBFDirectory, self).__init__(datapath, ext, CBFFile, **keyargs)
+0
-218
xrayutilities/io/desy_tty08.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2013-2014 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17
18 """
19 class for reading data + header information from tty08 data files
20
21 tty08 is a system used at beamline P08 at Hasylab Hamburg and creates simple
22 ASCII files to save the data. Information is easily read from the multicolumn
23 data file. the functions below enable also to parse the information of the
24 header
25 """
26
27 import glob
28 import os.path
29 import re
30
31 import numpy
32 import numpy.lib.recfunctions
33
34 from ..exception import InputError
35 # relative imports from xrayutilities
36 from .helper import xu_open
37
38 re_columns = re.compile(r"/\*H")
39 re_command = re.compile(r"^/\*C command")
40 re_comment = re.compile(r"^/\*")
41 re_date = re.compile(r"^/\*D date")
42 re_epoch = re.compile(r"^/\*T epoch")
43 re_initmopo = re.compile(r"^/\*M")
44
45
46 class tty08File(object):
47
48 """
49 Represents a tty08 data file. The file is read during the
50 Constructor call. This class should work for data stored at
51 beamline P08 using the tty08 acquisition system.
52
53 Parameters
54 ----------
55 filename : str
56 tty08-filename
57 mcadir : str, optional
58 directory name of MCA files
59 """
60
61 def __init__(self, filename, path=None, mcadir=None):
62 self.filename = filename
63 if path is None:
64 self.full_filename = self.filename
65 else:
66 self.full_filename = os.path.join(path, self.filename)
67
68 self.Read()
69
70 if mcadir is not None:
71 self.mca_directory = mcadir
72 self.mca_files = sorted(glob.glob(
73 os.path.join(self.mca_directory, '*')))
74
75 if self.mca_files:
76 self.ReadMCA()
77
78 def ReadMCA(self):
79 self.mca = numpy.empty((len(self.mca_files),
80 numpy.loadtxt(self.mca_files[0]).shape[0]),
81 dtype=numpy.float)
82 for i in range(len(self.mca_files)):
83 mcadata = numpy.loadtxt(self.mca_files[i])
84
85 self.mca[i, :] = mcadata[:, 1]
86
87 if i == 0:
88 if len(mcadata.shape) == 2:
89 self.mca_channels = mcadata[:, 0]
90 else:
91 self.mca_channels = numpy.arange(0, mcadata.shape[0])
92
93 mcatemp = self.mca.view([('MCA',
94 (self.mca.dtype, self.mca.shape[1]))])
95 self.data = numpy.lib.recfunctions.merge_arrays([self.data, mcatemp],
96 flatten=True)
97
98 def Read(self):
99 """
100 Read the data from the file
101 """
102
103 with xu_open(self.full_filename) as fid:
104 # read header
105 self.init_mopo = {}
106 for line in fid:
107 line = line.decode('ascii')
108
109 if re_command.match(line):
110 m = line.split(':')
111 self.scan_command = m[1].strip()
112 if re_date.match(line):
113 m = line.split(':', 1)
114 self.scan_date = m[1].strip()
115 if re_epoch.match(line):
116 m = line.split(':', 1)
117 self.epoch = float(m[1])
118 if re_initmopo.match(line):
119 m = line[3:]
120 m = m.split(';')
121 for e in m:
122 e = e.split('=')
123 self.init_mopo[e[0].strip()] = float(e[1])
124
125 if re_columns.match(line):
126 self.columns = tuple(line.split()[1:])
127 # here all necessary information is read and we can start
128 # reading the data
129 break
130 self.data = numpy.loadtxt(fid, comments="/")
131
132 self.data = numpy.rec.fromrecords(self.data, names=self.columns)
133
134
135 def gettty08_scan(scanname, scannumbers, *args, **keyargs):
136 """
137 function to obtain the angular cooridinates as well as intensity values
138 saved in TTY08 datafiles. Especially usefull for reciprocal space map
139 measurements, and to combine date from several scans
140
141 further more it is possible to obtain even more positions from
142 the data file if more than two string arguments with its names are given
143
144 Parameters
145 ----------
146 scanname : str
147 name of the scans, for multiple scans this needs to be a template
148 string
149 scannumbers : int, tuple or list
150 number of the scans of the reciprocal space map
151
152 args : str, optional
153 names of the motors. to read reciprocal space maps measured in coplanar
154 diffraction give:
155
156 - `omname`: the name of the omega motor (or its equivalent)
157 - `ttname`: the name of the two theta motor (or its equivalent)
158
159 keyargs : dict, optional
160 keyword arguments are passed on to tty08File
161
162 Returns
163 -------
164 [ang1, ang2, ...] : list, optional
165 angular positions of the center channel of the position sensitive
166 detector (numpy.ndarray 1D), omitted if no `args` are given
167 MAP : ndarray
168 All the data values as stored in the data file (includes the
169 intensities e.g. MAP['MCA']).
170
171 Examples
172 --------
173 >>> [om, tt], MAP = xu.io.gettty08_scan('text%05d.dat', 36, 'omega',
174 >>> 'gamma')
175 """
176
177 if isinstance(scannumbers, (list, tuple)):
178 scanlist = scannumbers
179 else:
180 scanlist = list([scannumbers])
181
182 angles = dict.fromkeys(args)
183 for key in angles.keys():
184 if not isinstance(key, str):
185 raise InputError("*arg values need to be strings with motornames")
186 angles[key] = numpy.zeros(0)
187 buf = numpy.zeros(0)
188 MAP = numpy.zeros(0)
189
190 for nr in scanlist:
191 scan = tty08File(scanname % nr, **keyargs)
192 sdata = scan.data
193 if MAP.dtype == numpy.float64:
194 MAP.dtype = sdata.dtype
195 # append scan data to MAP, where all data are stored
196 MAP = numpy.append(MAP, sdata)
197 # check type of scan
198 for i in range(len(args)):
199 motname = args[i]
200 scanlength = len(sdata)
201 try:
202 buf = sdata[motname]
203 except ValueError:
204 buf = scan.init_mopo[motname] * numpy.ones(scanlength)
205 angles[motname] = numpy.concatenate((angles[motname], buf))
206
207 retval = []
208 for motname in args:
209 # create return values in correct order
210 retval.append(angles[motname])
211
212 if not args:
213 return MAP
214 elif len(args) == 1:
215 return retval[0], MAP
216 else:
217 return retval, MAP
+0
-388
xrayutilities/io/edf.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2010-2012,2014-2015
17 # Dominik Kriegner <dominik.kriegner@gmail.com>
18 # Copyright (C) 2012 Tanja Etzelstorfer <tanja.etzelstorfer@jku.at>
19
20 # module for handling files stored in the EDF data format developed by the ESRF
21
22 import os.path
23 import re
24 import struct
25
26 import numpy
27
28 from .. import config, utilities
29 from .filedir import FileDirectory
30 from .helper import xu_h5open, xu_open
31
32 edf_kv_split = re.compile(r"\s*=\s*") # key value sepeartor for header data
33 edf_eokv = re.compile(r";") # end of line for a header
34 # regular expressions for several ASCII representations of numbers
35 edf_integer_value = re.compile(r"\d+")
36 edf_float_value = re.compile(r"[+-]*\d+\.*\d*")
37 edf_float_e_value = re.compile(r"[+-]*\d+\.\d*e[+-]*\d*")
38 edf_name_start_num = re.compile(r"^\d")
39
40 # dictionary mapping EDF data type keywords onto struct data types
41 DataTypeDict = {"SignedByte": "b",
42 "SignedShort": "h",
43 "SignedInteger": "i",
44 "SignedLong": "i",
45 "FloatValue": "f",
46 "DoubleValue": "d",
47 "UnsignedByte": "B",
48 "UnsignedShort": "H",
49 "UnsignedInt": "I",
50 "UnsignedLong": "L"}
51 # SignedLong is only 4byte, on my 64bit machine using SignedLong:"l" caused
52 # troubles
53 # UnsignedLong is only 4byte, on my 64bit machine using UnsignedLong:"L"
54 # caused troubles ("I" works)
55
56
57 class EDFFile(object):
58
59 def __init__(self, fname, nxkey="Dim_1", nykey="Dim_2",
60 dtkey="DataType", path="", header=True, keep_open=False):
61 """
62 Parameters
63 ----------
64 fname : str
65 name of the EDF file of type .edf or .edf.gz
66
67 nxkey : str, optional
68 name of the header key that holds the number of points in
69 x-direction
70 nykey : str, optional
71 name of the header key that holds the number of points in
72 y-direction
73 dtkey : str, optional
74 name of the header key that holds the datatype for the binary data
75 path : str, optional
76 path to the EDF file
77 header : bool, optional
78 has header (default true)
79 keep_open : bool, optional
80 if True the file handle is kept open between multiple calls which
81 can cause significant speed-ups
82 """
83
84 self.filename = fname
85 self.full_filename = os.path.join(path, fname)
86
87 # evaluate keyword arguments
88 self.nxkey = nxkey
89 self.nykey = nykey
90 self.dtkey = dtkey
91 self.headerflag = header
92
93 # create attributes for holding data
94 self._data = {}
95 self._headers = []
96 self._data_offsets = []
97 self._data_read = False
98 self._dimx = []
99 self._dimy = []
100 self._byte_order = []
101 self._fmt_str = []
102 self._dtype = []
103
104 self.Parse()
105 if keep_open:
106 self.fid = xu_open(self.full_filename, 'rb')
107 else:
108 self.fid = None
109
110 self.nimages = len(self._data_offsets)
111 self.header = self._headers[0]
112
113 def Parse(self):
114 """
115 Parse file to find the number of entries and read the respective
116 header information
117 """
118 header = {}
119 offset = 0
120
121 with xu_open(self.full_filename, 'rb') as fid:
122 if config.VERBOSITY >= config.INFO_ALL:
123 print("XU.io.EDFFile.Parse: file: %s" % self.full_filename)
124
125 if self.headerflag:
126 while True: # until end of file
127 hdr_flag = False
128 ml_value_flag = False # marks a multiline header
129 byte_order = ""
130 for line in fid: # until end of header
131 linelength = len(line)
132 offset += linelength
133 line = line.decode('ascii', 'ignore')
134 if config.VERBOSITY >= config.DEBUG:
135 print(line)
136 if line == "":
137 break
138 # remove leading and trailing whitespace symbols
139 line = line.strip()
140
141 if line == "{" and not hdr_flag:
142 # start with header
143 hdr_flag = True
144 header = {}
145 continue
146
147 if hdr_flag:
148 # stop reading when the end of the header
149 # is reached
150 if line == "}":
151 # place offset reading here - here we get the
152 # real starting position of the binary data!!
153 break
154
155 # continue if the line has no content
156 if line == "":
157 continue
158
159 # split key and value of the header entry
160 if not ml_value_flag:
161 try:
162 key, value = edf_kv_split.split(line, 1)
163 except ValueError:
164 print("XU.io.EDFFile.Parse: "
165 "line: %s" % line)
166
167 key = key.strip()
168 value = value.strip()
169
170 # if the value extends over multiple lines set
171 # the multiline value flag
172 if value[-1] != ";":
173 ml_value_flag = True
174 else:
175 value = value[:-1]
176 value = value.strip()
177 header[key] = value
178 else:
179 value = value + line
180 if value[-1] == ";":
181 ml_value_flag = False
182
183 value = value[:-1]
184 value = value.strip()
185 header[key] = value
186 else:
187 break
188 # append header to class variables
189 self._byte_order.append(header["ByteOrder"])
190 self._fmt_str.append(DataTypeDict[header[self.dtkey]])
191 self._dimx.append(int(header[self.nxkey]))
192 self._dimy.append(int(header[self.nykey]))
193 self._dtype.append(header[self.dtkey])
194
195 self._headers.append(header)
196 self._data_offsets.append(offset)
197 # jump over data block
198 tot_nofp = self._dimx[-1] * self._dimy[-1]
199 dsize = tot_nofp * struct.calcsize(self._fmt_str[-1])
200 fid.seek(offset + dsize, 0)
201 offset += dsize
202
203 else: # in case of no header also save one set of defaults
204 self._byte_order.append('LowByteFirst')
205 self._fmt_str.append(DataTypeDict['UnsignedShort'])
206 self._dimx.append(516)
207 self._dimy.append(516)
208 self._dtype.append('UnsignedShort')
209 self._headers.append(header)
210 self._data_offsets.append(offset)
211
212 # try to parse motor positions and counters from last found header
213 # into separate dictionary
214 if 'motor_mne' in header.keys():
215 tkeys = header['motor_mne'].split()
216 try:
217 tval = numpy.array(header['motor_pos'].split(),
218 dtype=numpy.double)
219 self.motors = dict(zip(tkeys, tval))
220 except ValueError:
221 print("XU.io.EDFFile.ReadData: Warning: header conversion "
222 "of motor positions failed")
223
224 if 'counter_mne' in header.keys():
225 tkeys = header['counter_mne'].split()
226 try:
227 tval = numpy.array(header['counter_pos'].split(),
228 dtype=numpy.double)
229 self.counters = dict(zip(tkeys, tval))
230 except ValueError:
231 print("XU.io.EDFFile.ReadData: Warning: header conversion "
232 "of counter values failed")
233
234 def ReadData(self, nimg=0):
235 """
236 Read the CCD data of the specified image and return the data
237 this function is called automatically when the 'data' property is
238 accessed, but can also be called manually when only a certain image
239 from the file is needed.
240
241 Parameters
242 ----------
243 nimg : int, optional
244 number of the image which should be read (starts with 0)
245 """
246 if self.fid:
247 binfid = self.fid
248 # move to the data section - jump over the header
249 binfid.seek(self._data_offsets[nimg], 0)
250 # read the data
251 tot_nofp = self._dimx[nimg] * self._dimy[nimg]
252 fmt_str = self._fmt_str[nimg]
253 bindata = binfid.read(tot_nofp * struct.calcsize(fmt_str))
254 else:
255 with xu_open(self.full_filename, 'rb') as binfid:
256 # move to the data section - jump over the header
257 binfid.seek(self._data_offsets[nimg], 0)
258 # read the data
259 tot_nofp = self._dimx[nimg] * self._dimy[nimg]
260 fmt_str = self._fmt_str[nimg]
261 bindata = binfid.read(tot_nofp * struct.calcsize(fmt_str))
262 if config.VERBOSITY >= config.DEBUG:
263 print("XU.io.EDFFile: read binary data: nofp: %d len: %d"
264 % (tot_nofp, len(bindata)))
265 print("XU.io.EDFFile: format: %s" % fmt_str)
266
267 try:
268 data = numpy.frombuffer(bindata, count=tot_nofp, dtype=fmt_str)
269 except ValueError:
270 if fmt_str == 'L':
271 fmt_str = 'I'
272 try:
273 data = numpy.frombuffer(bindata, count=tot_nofp,
274 dtype=fmt_str)
275 except ValueError:
276 raise IOError("XU.io.EDFFile: data format (%s) has "
277 "different byte-length, from amount of data "
278 "one expects %d bytes per entry"
279 % (fmt_str, len(bindata) / tot_nofp))
280 else:
281 raise IOError("XU.io.EDFFile: data format (%s) has different "
282 "byte-length, from amount of data one expects "
283 "%d bytes per entry"
284 % (fmt_str, len(bindata) / tot_nofp))
285
286 data.shape = (self._dimy[nimg], self._dimx[nimg])
287
288 if self._byte_order[nimg] != "LowByteFirst": # data = data.byteswap()
289 print("XU.io.EDFFile.ReadData: check byte order - "
290 "not low byte first")
291
292 return data
293
294 @property
295 def data(self):
296 if not self._data_read:
297 for i in range(self.nimages):
298 self._data[i] = self.ReadData(i)
299 self._data_read = True
300 if self.nimages == 1:
301 return self._data[0]
302 else:
303 return self._data
304
305 def Save2HDF5(self, h5f, group="/", comp=True):
306 """
307 Saves the data stored in the EDF file in a HDF5 file as a HDF5 array.
308 By default the data is stored in the root group of the HDF5 file - this
309 can be changed by passing the name of a target group or a path to the
310 target group via the "group" keyword argument.
311
312 Parameters
313 ----------
314 h5f : file-handle or str
315 a HDF5 file object or name
316 group : str, optional
317 group where to store the data (default to the root of the file)
318 comp : bool, optional
319 activate compression - true by default
320 """
321 with xu_h5open(h5f, 'a') as h5:
322 if isinstance(group, str):
323 if group == '/':
324 g = h5
325 else:
326 if group in h5:
327 del h5[group]
328 g = h5.create_group(group)
329 else:
330 g = group
331
332 # create the array name
333 ca_name = os.path.split(self.filename)[-1]
334 ca_name = os.path.splitext(ca_name)[0]
335 # perform a second time for case of .edf.gz files
336 ca_name = os.path.splitext(ca_name)[0]
337 ca_name = utilities.makeNaturalName(ca_name)
338 if edf_name_start_num.match(ca_name):
339 ca_name = "ccd_" + ca_name
340 if config.VERBOSITY >= config.INFO_ALL:
341 print(ca_name)
342
343 # create the array description
344 ca_desc = "EDF CCD data from file %s " % (self.filename)
345 kwds = {'fletcher32': True}
346 if comp:
347 kwds['compression'] = 'gzip'
348
349 if self.nimages != 1:
350 ca_name += '_{n:04d}'
351
352 for n in range(self.nimages):
353 d = self.ReadData(n)
354 name = ca_name.format(n=n)
355 try:
356 ca = g.create_dataset(name, data=d, **kwds)
357 except ValueError:
358 del g[name]
359 ca = g.create_dataset(name, data=d, **kwds)
360
361 ca.attrs['TITLE'] = ca_desc
362
363 # finally we have to append the attributes
364 for k in self.header.keys():
365 ca.attrs[utilities.makeNaturalName(k)] = self.header[k]
366
367
368 class EDFDirectory(FileDirectory):
369
370 """
371 Parses a directory for EDF files, which can be stored to a HDF5 file for
372 further usage
373 """
374
375 def __init__(self, datapath, ext="edf", **keyargs):
376 """
377
378 Parameters
379 ----------
380 datapath : str
381 directory of the EDF file
382 ext : str, optional
383 extension of the ccd files in the datapath (default: "edf")
384 keyargs : dict, optional
385 further keyword arguments are passed to EDFFile
386 """
387 super(EDFDirectory, self).__init__(datapath, ext, EDFFile, **keyargs)
+0
-1275
xrayutilities/io/fastscan.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2014 Raphael Grifone <raphael.grifone@esrf.fr>
16 # Copyright (C) 2014-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 modules to help with the analysis of FastScan data acquired at the ESRF.
20 FastScan data are X-ray data (various detectors possible) acquired during
21 scanning the sample in real space with a Piezo Scanner. The same functions
22 might be used to analze traditional SPEC mesh scans.
23
24 The module provides three core classes:
25
26 * FastScan
27 * FastScanCCD
28 * FastScanSeries
29
30 where the first two are able to parse single mesh/FastScans when one is
31 interested in data of a single channel detector or are detector and the last
32 one is able to parse full series of such mesh scans with either type of
33 detector
34
35 see examples/xrayutilities_kmap_ESRF.py for an example script
36 """
37
38 import glob
39 import os.path
40 import re
41
42 import h5py
43 import numpy
44
45 from .. import config, utilities
46 from ..gridder import delta
47 from ..gridder2d import Gridder2D, Gridder2DList
48 from ..gridder3d import Gridder3D
49 from ..normalize import blockAverage2D
50 from .edf import EDFFile
51 from .spec import SPECFile
52
53 # python 2to3 compatibility
54 try:
55 basestring
56 except NameError:
57 basestring = str
58
59
60 class FastScan(object):
61
62 """
63 class to help parsing and treating fast scan data. FastScan is the
64 aquisition of X-ray data while scanning the sample with piezo stages in
65 real space. It's is available at several beamlines at the ESRF synchrotron
66 light-source.
67 """
68
69 def __init__(self, filename, scannr,
70 xmotor='adcX', ymotor='adcY', path=""):
71 """
72 Constructor routine for the FastScan object. It initializes the object
73 and parses the spec-scan for the needed data which are saved in
74 properties of the FastScan object.
75
76 Parameters
77 ----------
78 filename : str
79 file name of the fast scan spec file
80 scannr : int
81 scannr of the to be parsed fast scan
82 xmotor : str, optional
83 motor name of the x-motor (default: 'adcX' (ID01))
84 ymotor : str, optional
85 motor name of the y-motor (default: 'adcY' (ID01))
86 path : str, optional
87 optional path of the FastScan spec file
88 """
89 self.scannr = scannr
90 self.xmotor = xmotor
91 self.ymotor = ymotor
92
93 if isinstance(filename, SPECFile):
94 self.specfile = filename
95 self.filename = self.specfile.filename
96 self.full_filename = self.specfile.full_filename
97 self.specscan = getattr(self.specfile, 'scan%d' % self.scannr)
98 else:
99 self.filename = filename
100 self.full_filename = os.path.join(path, filename)
101 self.filename = os.path.basename(self.full_filename)
102 self.specscan = None
103
104 # read the scan
105 self.parse()
106
107 def parse(self):
108 """
109 parse the specfile for the scan number specified in the constructor and
110 store the needed informations in the object properties
111 """
112
113 # parse the file
114 if not self.specscan:
115 self.specfile = SPECFile(self.full_filename)
116 self.specscan = getattr(self.specfile, 'scan%d' % self.scannr)
117 self.specscan.ReadData()
118
119 self.xvalues = self.specscan.data[self.xmotor]
120 self.yvalues = self.specscan.data[self.ymotor]
121
122 self.data = self.specscan.data
123
124 def motorposition(self, motorname):
125 """
126 read the position of motor with name given by motorname from the data
127 file. In case the motor is included in the data columns the returned
128 object is an array with all the values from the file (although retrace
129 clean is respected if already performed). In the case the motor is not
130 moved during the scan only one value is returned.
131
132 Parameters
133 ----------
134 motorname : str
135 name of the motor for which the position is wanted
136
137 Returns
138 -------
139 ndarray
140 motor position(s) of motor with name motorname during the scan
141 """
142 if self.specscan:
143 # try reading value from data
144 try:
145 return self.data[motorname]
146 except ValueError:
147 try:
148 return self.specscan.init_motor_pos['INIT_MOPO_%s'
149 % motorname]
150 except KeyError:
151 raise ValueError("given motorname '%s' not found in the "
152 "Spec-data" % motorname)
153 else:
154 return None
155
156 def retrace_clean(self):
157 """
158 function to clean the data of the scan from retrace artifacts created
159 by the zig-zag scanning motion of the piezo actuators the function
160 cleans the xvalues, yvalues and data attribute of the FastScan object.
161 """
162
163 # set window to determin the slope
164 window = [-1, 0, 1]
165 # calc the slope of x_motor movement using a window for better acuracy
166 slope = numpy.convolve(self.xvalues, window, mode='same') / \
167 numpy.convolve(numpy.arange(len(self.xvalues)), window, 'same')
168 # select where slope is above the slope mean value
169 # this can be modified if data points are missing of the retrace does
170 # not clean all points
171 mask = numpy.where(slope > slope.mean())
172
173 # reduce data size by cutting out retrace
174 self.xvalues = self.xvalues[mask]
175 self.yvalues = self.yvalues[mask]
176 self.data = self.data[mask]
177
178 def grid2D(self, nx, ny, **kwargs):
179 """
180 function to grid the counter data and return the gridded X, Y and
181 Intensity values.
182
183 Parameters
184 ----------
185 nx, ny : int
186 number of bins in x, and y direction
187 counter : str, optional
188 name of the counter to use for gridding (default: 'mpx4int' (ID01))
189 gridrange : tuple, optional
190 range for the gridder: format: ((xmin, xmax), (ymin, ymax))
191
192 Returns
193 -------
194 Gridder2D
195 Gridder2D object with X, Y, data on regular x, y-grid
196 """
197 self.counter = kwargs.get('counter', 'mpx4int')
198 gridrange = kwargs.get('gridrange', None)
199
200 # define gridder
201 g2d = Gridder2D(nx, ny)
202 if gridrange:
203 g2d.dataRange(gridrange[0][0], gridrange[0][1],
204 gridrange[1][0], gridrange[1][1])
205
206 # check if counter is in data fields
207 try:
208 inte = self.data[self.counter]
209 except ValueError:
210 raise ValueError("field named '%s' not found in data parsed from "
211 "scan #%d in file %s"
212 % (self.counter, self.scannr, self.filename))
213
214 # grid data
215 g2d(self.xvalues, self.yvalues, self.data[self.counter])
216
217 # return gridded data
218 return g2d
219
220
221 class FastScanCCD(FastScan):
222
223 """
224 class to help parsing and treating fast scan data including CCD frames.
225 FastScan is the aquisition of X-ray data while scanning the sample with
226 piezo stages in real space. It's is available at several beamlines at the
227 ESRF synchrotron light-source. During such fast scan at every grid point
228 CCD frames are recorded and need to be analyzed
229 """
230
231 def __init__(self, *args, **kwargs):
232 """
233 Parameters
234 ----------
235 imagefiletype : str, optional
236 image file extension, either 'edf' / 'edf.gz' (default) or 'h5'
237
238 other parameters are passed on to FastScanCCD
239 """
240 self.imagefiletype = kwargs.pop('imagefiletype', 'edf')
241 self.imgfile = None
242 self.nimages = None
243 super(FastScanCCD, self).__init__(*args, **kwargs)
244
245 def _getCCDnumbers(self, ccdnr):
246 """
247 internal function to return the ccd frame numbers from the data object
248 or take them from the argument.
249 """
250 if isinstance(ccdnr, basestring):
251 # check if counter is in data fields
252 try:
253 ccdnumbers = self.data[ccdnr]
254 except ValueError:
255 raise ValueError("field named '%s' not found in data parsed "
256 "from scan #%d in file %s"
257 % (ccdnr, self.scannr, self.filename))
258 elif isinstance(ccdnr, (list, tuple, numpy.ndarray)):
259 ccdnumbers = ccdnr
260 else:
261 raise ValueError("xu.FastScanCCD: wrong data type for "
262 "argument 'ccdnr'")
263 return ccdnumbers
264
265 def _gridCCDnumbers(self, nx, ny, ccdnr, gridrange=None):
266 """
267 internal function to grid the CCD frame number to produce a list of
268 ccd-files per bin needed for the further treatment
269
270 Parameters
271 ----------
272 nx, ny : int
273 number of bins in x, and y direction
274 ccdnr : str or array-like
275 array with ccd file numbers of length length(FastScanCCD.data) OR a
276 string with the data column name for the file ccd-numbers
277
278 gridrange : tuple, optional
279 range for the gridder: format: ((xmin, xmax), (ymin, ymax))
280
281 Returns
282 -------
283 gridder-object
284 regular x, y-grid as well as 4-dimensional data object
285 """
286 g2l = Gridder2DList(nx, ny)
287 if gridrange:
288 g2l.dataRange(gridrange[0][0], gridrange[0][1],
289 gridrange[1][0], gridrange[1][1])
290
291 ccdnumbers = self._getCCDnumbers(ccdnr)
292 # assign ccd frames to grid
293 g2l(self.xvalues, self.yvalues, ccdnumbers)
294
295 return g2l
296
297 def _read_image(self, filename, imgindex, nav, roi, filterfunc):
298 """
299 helper function to obtain one frame from an EDF/HDF5 file
300
301 Parameters
302 ----------
303 filename : str
304 EDF file name
305 imgindex : int
306 index of frame inside the given EDF file
307 nav : tuple or list
308 number of detector pixel which will be averaged together (reduces
309 the date size)
310 roi : tuple
311 region of interest on the 2D detector. should be a list of lower
312 and upper bounds of detector channels for the two pixel directions
313 (default: None)
314 filterfunc : callable
315 function applied to the CCD-frames before any processing. this
316 function should take a single argument which is the ccddata which
317 need to be returned with the same shape! e.g. remove hot pixels,
318 flat/darkfield correction
319
320 Returns
321 -------
322 ndarray
323 numpy 2D array with the detector frame
324 """
325 if roi is None:
326 kwdict = {}
327 else:
328 kwdict = {'roi': roi}
329 if 'edf' in self.imagefiletype:
330 if not self.imgfile:
331 self.imgfile = EDFFile(filename, keep_open=True)
332 else:
333 if self.imgfile.filename != filename:
334 self.imgfile = EDFFile(filename, keep_open=True)
335 ccdfilt = self.imgfile.ReadData(imgindex)
336 else:
337 fileroot = os.path.splitext(os.path.splitext(filename)[0])[0]
338 if not self.imgfile:
339 self.imgfile = h5py.File(fileroot + '.h5', 'r')
340 ccdfilt = self.imgfile.get(
341 os.path.split(fileroot)[-1] + '_%04d' % imgindex).value
342 if filterfunc:
343 ccdfilt = filterfunc(ccdfilt)
344 if roi is None and nav[0] == 1 and nav[1] == 1:
345 return ccdfilt
346 else:
347 return blockAverage2D(ccdfilt, nav[0], nav[1], **kwdict)
348
349 def _get_image_number(self, imgnum, imgoffset, fileoffset, ccdfiletmp):
350 """
351 function to obtain the image and file number. The logic for obtain this
352 is likely to change between beamtimes.
353
354 Parameters
355 ----------
356 imgnum : int
357 running image number from the data file
358 imgoffset : int
359 offset in the image number
360 fileoffset : int
361 offset in the file number
362 ccdfiletmp : str
363 ccd file template string
364 """
365 if 'edf' in self.imagefiletype:
366 if not self.imgfile:
367 self.imgfile = EDFFile(ccdfiletmp % fileoffset, keep_open=True)
368 if self.nimages is None:
369 self.nimages = self.imgfile.nimages
370 else:
371 fileroot = os.path.splitext(os.path.splitext(ccdfiletmp
372 % fileoffset)[0])[0]
373 if not self.imgfile:
374 self.imgfile = h5py.File(fileroot + '.h5', 'r')
375 if self.nimages is None:
376 self.nimages = len(self.imgfile.items())
377 filenumber = int((imgnum - imgoffset) // self.nimages + fileoffset)
378 imgindex = int((imgnum - imgoffset) % self.nimages)
379 return imgindex, filenumber
380
381 def getccdFileTemplate(self, specscan, datadir=None, keepdir=0,
382 replacedir=None):
383 """
384 function to extract the CCD file template string from the comment
385 in the SPEC-file scan-header.
386
387 Parameters
388 ----------
389 specscan : SpecScan
390 spec-scan object from which header the CCD directory should be
391 extracted
392 datadir : str, optional
393 the CCD filenames are usually parsed from the scan object. With
394 this option the directory used for the data can be overwritten.
395 Specify the datadir as simple string. Alternatively the innermost
396 directory structure can be automatically taken from the specfile.
397 If this is needed specify the number of directories which should be
398 kept using the keepdir option.
399 keepdir : int, optional
400 number of directories which should be taken from the specscan.
401 (default: 0)
402 replacedir : int, optional
403 number of outer most directory names which should be replaced in
404 the output (default = None). One can either give keepdir, or
405 replacedir, with replace taking preference if both are given.
406
407 Returns
408 -------
409 fmtstr : str
410 format string for the CCD file name using one number to build the
411 real file name
412 filenr : int
413 starting file number
414 """
415 hline = specscan.getheader_element('C imageFile')
416 re_ccdfiles = re.compile(r'dir\[([a-zA-Z0-9_.%/]*)\] '
417 r'prefix\[([a-zA-Z0-9_.%/]*)\] '
418 r'idxFmt\[([a-zA-Z0-9_.%/]*)\] '
419 r'nextNr\[([0-9]*)\] '
420 r'suffix\[([a-zA-Z0-9_.%/]*)\]')
421 m = re_ccdfiles.match(hline)
422 if m:
423 path, prefix, idxFmt, num, suffix = m.groups()
424 else:
425 ValueError('spec-scan does not contain images or the '
426 'corresponding header line is not detected correctly')
427 ccdtmp = os.path.join(path, prefix + idxFmt + suffix)
428 r = utilities.exchange_filepath(ccdtmp, datadir, keepdir, replacedir)
429 return r, int(num)
430
431 def getCCD(self, ccdnr, roi=None, datadir=None, keepdir=0,
432 replacedir=None, nav=[1, 1], filterfunc=None):
433 """
434 function to read the ccd files and return the raw X, Y and DATA values.
435 DATA represents a 3D object with first dimension representing the data
436 point index and the remaining two dimensions representing detector
437 channels
438
439 Parameters
440 ----------
441 ccdnr : array-like or str
442 array with ccd file numbers of length length(FastScanCCD.data) OR a
443 string with the data column name for the file ccd-numbers
444
445 roi : tuple, optional
446 region of interest on the 2D detector. should be a list of lower
447 and upper bounds of detector channels for the two pixel directions
448 (default: None)
449 datadir : str, optional
450 the CCD filenames are usually parsed from the SPEC file. With this
451 option the directory used for the data can be overwritten. Specify
452 the datadir as simple string. Alternatively the innermost
453 directory structure can be automatically taken from the specfile.
454 If this is needed specify the number of directories which should be
455 kept using the keepdir option.
456 keepdir : int, optional
457 number of directories which should be taken from the SPEC file.
458 (default: 0)
459 replacedir : int, optional
460 number of outer most directory names which should be replaced in
461 the output (default = None). One can either give keepdir, or
462 replacedir, with replace taking preference if both are given.
463 nav : tuple or list, optional
464 number of detector pixel which will be averaged together (reduces
465 the date size)
466 filterfunc : callable
467 function applied to the CCD-frames before any processing. this
468 function should take a single argument which is the ccddata which
469 need to be returned with the same shape! e.g. remove hot pixels,
470 flat/darkfield correction
471
472 Returns
473 -------
474 X, Y : ndarray
475 x, y-array (1D)
476 DATA : ndarray
477 3-dimensional data object
478 """
479 ccdnumbers = self._getCCDnumbers(ccdnr)
480
481 ccdtemplate, nextNr = self.getccdFileTemplate(
482 self.specscan, datadir, keepdir=keepdir, replacedir=replacedir)
483
484 # read ccd shape from first image
485 filename = ccdtemplate % nextNr
486 ccdshape = self._read_image(filename, 0, nav, roi, filterfunc).shape
487 ccddata = numpy.empty((self.xvalues.size, ccdshape[0], ccdshape[1]))
488 if config.VERBOSITY >= config.INFO_ALL:
489 print('XU.io.FastScanCCD: allocated ccddata array with %d bytes'
490 % ccddata.nbytes)
491
492 # go through the ccd-frames
493 for i, imgnum in enumerate(ccdnumbers):
494 # read ccd-frames
495 imgindex, filenumber = self._get_image_number(imgnum, nextNr,
496 nextNr, ccdtemplate)
497 filename = ccdtemplate % filenumber
498 ccd = self._read_image(filename, imgindex, nav, roi, filterfunc)
499 ccddata[i, :, :] = ccd
500 return self.xvalues, self.yvalues, ccddata
501
502 def processCCD(self, ccdnr, roi, datadir=None, keepdir=0,
503 replacedir=None, filterfunc=None):
504 """
505 function to read a region of interest (ROI) from the ccd files and
506 return the raw X, Y and intensity from ROI.
507
508 Parameters
509 ----------
510 ccdnr : array-like or str
511 array with ccd file numbers of length length(FastScanCCD.data) OR a
512 string with the data column name for the file ccd-numbers
513 roi : tuple or list
514 region of interest on the 2D detector. Either a list of lower and
515 upper bounds of detector channels for the two pixel directions as
516 tuple or a list of mask arrays
517 datadir : str, optional
518 the CCD filenames are usually parsed from the SPEC file. With this
519 option the directory used for the data can be overwritten. Specify
520 the datadir as simple string. Alternatively the innermost
521 directory structure can be automatically taken from the specfile.
522 If this is needed specify the number of directories which should be
523 kept using the keepdir option.
524 keepdir : int, optional
525 number of directories which should be taken from the SPEC file.
526 (default: 0)
527 replacedir : int, optional
528 number of outer most directory names which should be replaced in
529 the output (default = None). One can either give keepdir, or
530 replacedir, with replace taking preference if both are given.
531 filterfunc : callable, optional
532 function applied to the CCD-frames before any processing. this
533 function should take a single argument which is the ccddata which
534 need to be returned with the same shape! e.g. remove hot pixels,
535 flat/darkfield correction
536
537 Returns
538 -------
539 X, Y, DATA : ndarray
540 x, y-array (1D) as well as 1-dimensional data object
541 """
542 ccdnumbers = self._getCCDnumbers(ccdnr)
543
544 ccdtemplate, nextNr = self.getccdFileTemplate(
545 self.specscan, datadir, keepdir=keepdir, replacedir=replacedir)
546
547 if isinstance(roi, list):
548 lmask = roi
549 lroi = None
550 else:
551 lmask = [numpy.ones((roi[1]-roi[0], roi[3]-roi[2])), ]
552 lroi = roi
553 ccdroi = numpy.empty((len(lmask), self.xvalues.size))
554
555 # go through the ccd-frames
556 for i, imgnum in enumerate(ccdnumbers):
557 # read ccd-frames
558 imgindex, filenumber = self._get_image_number(imgnum, nextNr,
559 nextNr, ccdtemplate)
560 filename = ccdtemplate % filenumber
561 ccd = self._read_image(filename, imgindex, [1, 1], lroi,
562 filterfunc)
563 for j, m in enumerate(lmask):
564 ccdroi[j, i] = numpy.sum(ccd[m])
565 if len(lmask) == 1:
566 return self.xvalues, self.yvalues, ccdroi[0]
567 else:
568 return self.xvalues, self.yvalues, ccdroi
569
570 def gridCCD(self, nx, ny, ccdnr, roi=None, datadir=None, keepdir=0,
571 replacedir=None, nav=[1, 1], gridrange=None, filterfunc=None):
572 """
573 function to grid the internal data and ccd files and return the gridded
574 X, Y and DATA values. DATA represents a 4D object with first two
575 dimensions representing X, Y and the remaining two dimensions
576 representing detector channels
577
578 Parameters
579 ----------
580 nx, ny : int
581 number of bins in x, and y direction
582 ccdnr : array-like or str
583 array with ccd file numbers of length length(FastScanCCD.data) OR a
584 string with the data column name for the file ccd-numbers
585
586 roi : tuple, optional
587 region of interest on the 2D detector. should be a list of lower
588 and upper bounds of detector channels for the two pixel directions
589 (default: None)
590 datadir : str, optional
591 the CCD filenames are usually parsed from the SPEC file. With this
592 option the directory used for the data can be overwritten. Specify
593 the datadir as simple string. Alternatively the innermost
594 directory structure can be automatically taken from the specfile.
595 If this is needed specify the number of directories which should be
596 kept using the keepdir option.
597 keepdir : int, optional
598 number of directories which should be taken from the SPEC file.
599 (default: 0)
600 replacedir : int, optional
601 number of outer most directory names which should be replaced in
602 the output (default = None). One can either give keepdir, or
603 replacedir, with replace taking preference if both are given.
604 nav : tuple or list, optional
605 number of detector pixel which will be averaged together (reduces
606 the date size)
607 gridrange : tuple
608 range for the gridder: format: ((xmin, xmax), (ymin, ymax))
609 filterfunc : callable
610 function applied to the CCD-frames before any processing. this
611 function should take a single argument which is the ccddata which
612 need to be returned with the same shape! e.g. remove hot pixels,
613 flat/darkfield correction
614
615 Returns
616 -------
617 X, Y: ndarray
618 regular x, y-grid
619 DATA : ndarray
620 4-dimensional data object
621 """
622
623 g2l = self._gridCCDnumbers(nx, ny, ccdnr, gridrange=gridrange)
624 gdata = g2l.data
625
626 ccdtemplate, nextNr = self.getccdFileTemplate(
627 self.specscan, datadir, keepdir=keepdir, replacedir=replacedir)
628
629 # read ccd shape from first image
630 filename = ccdtemplate % nextNr
631 ccdshape = self._read_image(filename, 0, nav, roi, filterfunc).shape
632 ccddata = numpy.empty((self.xvalues.size, ccdshape[0], ccdshape[1]))
633 if config.VERBOSITY >= config.INFO_ALL:
634 print('XU.io.FastScanCCD: allocated ccddata array with %d bytes'
635 % ccddata.nbytes)
636
637 # go through the gridded data and average the ccd-frames
638 for i in range(gdata.shape[0]):
639 for j in range(gdata.shape[1]):
640 if not gdata[i, j]:
641 continue
642 else:
643 framecount = 0
644 # read ccd-frames and average them
645 for imgnum in gdata[i, j]:
646 imgindex, filenumber = self._get_image_number(
647 imgnum, nextNr, nextNr, ccdtemplate)
648 filename = ccdtemplate % filenumber
649 ccd = self._read_image(filename, imgindex, nav,
650 roi, filterfunc)
651 ccddata[i, j, ...] += ccd
652 framecount += 1
653 ccddata[i, j, ...] /= float(framecount)
654
655 return g2l.xmatrix, g2l.ymatrix, ccddata
656
657
658 class FastScanSeries(object):
659
660 """
661 class to help parsing and treating a series of fast scan data including CCD
662 frames. FastScan is the aquisition of X-ray data while scanning the sample
663 with piezo stages in real space. It's is available at several beamlines at
664 the ESRF synchrotron light-source. During such fast scan at every grid
665 point CCD frames are recorded and need to be analyzed.
666
667 For the series of FastScans we assume that they are measured at different
668 goniometer angles and therefore transform the data to reciprocal space.
669 """
670
671 def __init__(self, filenames, scannrs, nx, ny, *args, **kwargs):
672 """
673 Constructor routine for the FastScanSeries object. It initializes the
674 object and creates a list of FastScanCCD objects. Importantly it also
675 expects the motor names of the angles needed for reciprocal space
676 conversion.
677
678 Parameters
679 ----------
680 filenames : list or str
681 file names of the fast scan spec files, in case of more than one
682 filename supply a list of names and also a list of scan numbers for
683 the different files in the 'scannrs' argument
684 scannrs : list
685 scannrs of the to be parsed fast scans. in case of one specfile
686 this is a list of numbers (e.g. [1, 2, 3]). when multiple filenames
687 are given supply a separate list for every file (e.g. [[1, 2,
688 3],[2, 4]])
689 nx, ny : int
690 grid-points for the real space grid
691 args : str
692 motor names for the Reciprocal space conversion. The order needs be
693 as required by the ``QConversion.area()`` function.
694 xmotor : str, optional
695 motor name of the x-motor (default: 'adcX' (ID01))
696 ymotor : str, optional
697 motor name of the y-motor (default: 'adcY' (ID01))
698 ccdnr : str, optional
699 name of the ccd-number data column (default: 'imgnr' (ID01))
700 counter : str, optional
701 name of a defined counter (roi) in the spec file (default:
702 'mpx4int' (ID01))
703 path : str, optional
704 path of the FastScan spec file (default: '')
705 """
706
707 if 'ccdnr' in kwargs:
708 self.ccdnr = kwargs['ccdnr']
709 kwargs.pop("ccdnr")
710 else:
711 self.ccdnr = 'imgnr'
712
713 if 'counter' in kwargs:
714 self.counter = kwargs['counter']
715 kwargs.pop("counter")
716 else:
717 self.counter = 'mpx4int'
718
719 if 'path' in kwargs:
720 self.path = kwargs['path']
721 kwargs.pop("path")
722 else:
723 self.path = ''
724
725 self.fastscans = []
726 self.nx = nx
727 self.ny = ny
728 self.motor_pos = None
729
730 self.gonio_motors = []
731 # save motor names
732 for arg in args:
733 if not isinstance(arg, basestring):
734 raise ValueError("one of the motor name arguments is not of "
735 "type 'str' but %s" % str(type(arg)))
736 self.gonio_motors.append(arg)
737
738 # create list of FastScans
739 if isinstance(filenames, basestring):
740 filenames = [filenames]
741 scannrs = [scannrs]
742 if isinstance(filenames, (tuple, list)):
743 for fname in filenames:
744 full_filename = os.path.join(self.path, fname)
745 specfile = SPECFile(full_filename)
746 for snrs in scannrs[filenames.index(fname)]:
747 self.fastscans.append(FastScanCCD(specfile,
748 snrs, **kwargs))
749 else:
750 raise ValueError("argument 'filenames' is not of "
751 "appropriate type!")
752
753 self._init_minmax()
754 for fs in self.fastscans:
755 self._update_minmax(fs)
756
757 def _init_minmax(self):
758 self.gridded = False
759 self.xmin = numpy.min(self.fastscans[0].xvalues)
760 self.ymin = numpy.min(self.fastscans[0].yvalues)
761 self.xmax = numpy.max(self.fastscans[0].xvalues)
762 self.ymax = numpy.max(self.fastscans[0].yvalues)
763
764 def _update_minmax(self, fs):
765 if numpy.max(fs.xvalues) > self.xmax:
766 self.xmax = numpy.max(fs.xvalues)
767 if numpy.max(fs.yvalues) > self.ymax:
768 self.ymax = numpy.max(fs.yvalues)
769 if numpy.min(fs.xvalues) < self.xmin:
770 self.xmin = numpy.min(fs.xvalues)
771 if numpy.min(fs.yvalues) < self.ymin:
772 self.ymin = numpy.min(fs.yvalues)
773
774 def retrace_clean(self):
775 """
776 perform retrace clean for every FastScan in the series
777 """
778 self._init_minmax()
779
780 for fs in self.fastscans:
781 fs.retrace_clean()
782 self._update_minmax(fs)
783
784 def align(self, deltax, deltay):
785 """
786 Since a sample drift or shift due to rotation often occurs between
787 different FastScans it should be corrected before combining them. Since
788 determining such a shift is not straight-forward in general the user
789 needs to supply the routine with the shifts in order correct the
790 x, y-values for the different FastScans. Such a routine could for
791 example use the integrated CCD intensities and determine the shift
792 using a cross-convolution.
793
794 Parameters
795 ----------
796 deltax, deltay : list
797 list of shifts in x/y-direction for every FastScan in the data
798 structure
799 """
800 self._init_minmax()
801 for fs in self.fastscans:
802 i = self.fastscans.index(fs)
803 fs.xvalues += deltax[i]
804 fs.yvalues += deltay[i]
805 self._update_minmax(fs)
806
807 def read_motors(self):
808 """
809 read motor values from the series of fast scans
810 """
811 self.motor_pos = numpy.zeros((len(self.fastscans),
812 len(self.gonio_motors)))
813 for i in range(len(self.fastscans)):
814 fs = self.fastscans[i]
815 for j in range(len(self.gonio_motors)):
816 mname = self.gonio_motors[j]
817 self.motor_pos[i, j] = fs.motorposition(mname)
818
819 def get_average_RSM(self, qnx, qny, qnz, qconv, datadir=None, keepdir=0,
820 replacedir=None, roi=None, nav=(1, 1),
821 filterfunc=None):
822 """
823 function to return the reciprocal space map data averaged over all x, y
824 positions from a series of FastScan measurements. It necessary to give
825 the QConversion-object to be used for the reciprocal space conversion.
826 The QConversion-object is expected to have the 'area' conversion
827 routines configured properly. This function needs to read all detector
828 images, so be prepared to lean back for a moment!
829
830 Parameters
831 ----------
832 qnx, qny, qnz : int
833 number of points used for the 3D Gridder
834 qconv : QConversion
835 QConversion-object to be used for the conversion of the CCD-data to
836 reciprocal space
837
838 roi : tuple, optional
839 region of interest on the 2D detector. should be a list of lower
840 and upper bounds of detector channels for the two pixel directions
841 (default: None)
842 nav : tuple or list, optional
843 number of detector pixel which will be averaged together (reduces
844 the date size)
845 filterfunc : callable, optional
846 function applied to the CCD-frames before any processing. this
847 function should take a single argument which is the ccddata which
848 need to be returned with the same shape! e.g. remove hot pixels,
849 flat/darkfield correction
850 datadir : str, optional
851 the CCD filenames are usually parsed from the SPEC file. With this
852 option the directory used for the data can be overwritten. Specify
853 the datadir as simple string. Alternatively the innermost
854 directory structure can be automatically taken from the specfile.
855 If this is needed specify the number of directories which should be
856 kept/replaced using the keepdir/replacedir option.
857 keepdir : int, optional
858 number of directories which should be taken from the SPEC file.
859 (default: 0)
860 replacedir : int, optional
861 number of outer most directory names which should be replaced in
862 the output (default = None). One can either give keepdir, or
863 replacedir, with replace taking preference if both are given.
864
865 Returns
866 -------
867 Gridder3D
868 gridded reciprocal space map
869 """
870 if self.motor_pos is None:
871 self.read_motors()
872
873 # determine q-coordinates
874 kwargs = {'Nav': nav}
875 if roi:
876 kwargs['roi'] = roi
877 qx, qy, qz = qconv.area(*self.motor_pos.T, **kwargs)
878
879 # define gridder with fixed optimized q-range
880 g3d = Gridder3D(qnx, qny, qnz)
881 g3d.keep_data = True
882 g3d.dataRange(qx.min(), qx.max(), qy.min(),
883 qy.max(), qz.min(), qz.max(), fixed=True)
884
885 # start parsing the images and grid the data frame by frame
886 for fsidx, fsccd in enumerate(self.fastscans):
887 ccdtemplate, nextNr = fsccd.getccdFileTemplate(
888 fsccd.specscan, datadir, keepdir=keepdir,
889 replacedir=replacedir)
890
891 ccdnumbers = fsccd._getCCDnumbers(self.ccdnr)
892 ccdav = numpy.zeros_like(qx[fsidx, ...])
893
894 # go through the ccdframes
895 for i, imgnum in enumerate(ccdnumbers):
896 # read ccdframes
897 imgindex, filenumber = fsccd._get_image_number(
898 imgnum, nextNr, nextNr, ccdtemplate)
899 filename = ccdtemplate % filenumber
900 ccd = fsccd._read_image(filename, imgindex, nav, roi,
901 filterfunc)
902 ccdav += ccd
903 g3d(qx[fsidx, ...], qy[fsidx, ...], qz[fsidx, ...], ccdav)
904
905 return g3d
906
907 def get_sxrd_for_qrange(self, qrange, qconv, datadir=None, keepdir=0,
908 replacedir=None, roi=None, nav=(1, 1),
909 filterfunc=None):
910 """
911 function to return the real space data averaged over a certain q-range
912 from a series of FastScan measurements. It necessary to give the
913 QConversion-object to be used for the reciprocal space conversion. The
914 QConversion-object is expected to have the 'area' conversion routines
915 configured properly.
916
917 Note:
918 This function assumes that all FastScans were performed in the same
919 real space positions, no gridding or aligning is performed!
920
921 Parameters
922 ----------
923 qrange : list or tuple
924 q-limits defining a box in reciprocal space. six values are
925 needed: [minx, maxx, miny, ..., maxz]
926 qconv : QConversion
927 QConversion object to be used for the conversion of the CCD-data to
928 reciprocal space
929
930 roi : tuple, optional
931 region of interest on the 2D detector. should be a list of lower
932 and upper bounds of detector channels for the two pixel directions
933 (default: None)
934 nav : tuple or list, optional
935 number of detector pixel which will be averaged together (reduces
936 the date size)
937 filterfunc : callable, optional
938 function applied to the CCD-frames before any processing. this
939 function should take a single argument which is the ccddata which
940 need to be returned with the same shape! e.g. remove hot pixels,
941 flat/darkfield correction
942 datadir : str, optional
943 the CCD filenames are usually parsed from the SPEC file. With this
944 option the directory used for the data can be overwritten. Specify
945 the datadir as simple string. Alternatively the innermost
946 directory structure can be automatically taken from the specfile.
947 If this is needed specify the number of directories which should be
948 kept/replaced using the keepdir/replacedir option.
949 keepdir : int, optional
950 number of directories which should be taken from the SPEC file.
951 (default: 0)
952 replacedir : int, optional
953 number of outer most directory names which should be replaced in
954 the output (default = None). One can either give keepdir, or
955 replacedir, with replace taking preference if both are given.
956
957 Returns
958 -------
959 xvalues, yvalues, data : ndarray
960 x, y, and data values
961 """
962 if self.motor_pos is None:
963 self.read_motors()
964
965 # determine q-coordinates
966 kwargs = {'Nav': nav}
967 if roi:
968 kwargs['roi'] = roi
969 qx, qy, qz = qconv.area(*self.motor_pos.T, **kwargs)
970 output = numpy.zeros_like(self.fastscans[0].xvalues)
971
972 # parse the images only if some q coordinates fall into the ROI
973 for fsidx, fsccd in enumerate(self.fastscans):
974 mask = numpy.logical_and(numpy.logical_and(
975 numpy.logical_and(qx[fsidx] > qrange[0],
976 qx[fsidx] < qrange[1]),
977 numpy.logical_and(qy[fsidx] > qrange[2],
978 qy[fsidx] < qrange[3])),
979 numpy.logical_and(qz[fsidx] > qrange[4],
980 qz[fsidx] < qrange[5]))
981
982 if numpy.any(mask):
983 ccdtemplate, nextNr = fsccd.getccdFileTemplate(
984 fsccd.specscan, datadir, keepdir=keepdir,
985 replacedir=replacedir)
986
987 ccdnumbers = fsccd._getCCDnumbers(self.ccdnr)
988
989 # go through the ccdframes
990 for i, imgnum in enumerate(ccdnumbers):
991 # read ccdframes
992 imgindex, filenumber = fsccd._get_image_number(
993 imgnum, nextNr, nextNr, ccdtemplate)
994 filename = ccdtemplate % filenumber
995 ccd = fsccd._read_image(filename, imgindex, nav, roi,
996 filterfunc)
997 output[i] += numpy.sum(ccd[mask])
998
999 return fsccd.xvalues, fsccd.yvalues, output
1000
1001 def getCCDFrames(self, posx, posy, typ='real'):
1002 """
1003 function to determine the list of ccd-frame numbers for a specific real
1004 space position. The real space position must be within the data limits
1005 of the FastScanSeries otherwise an ValueError is thrown
1006
1007 Parameters
1008 ----------
1009 posx : float
1010 real space x-position or index in x direction
1011 posy : float
1012 real space y-position or index in y direction
1013
1014 typ : {'real', 'index'}, optional
1015 type of coordinates. specifies if the position is specified as real
1016 space coordinate or as index. (default: 'real')
1017
1018 Returns
1019 -------
1020 list
1021 ``[[motorpos1, ccdnrs1], [motorpos2, ccdnrs2], ...]`` where
1022 motorposN is from the N-ths FastScan in the series and ccdnrsN is
1023 the list of according CCD-frames
1024 """
1025
1026 # determine grid point for position x, y
1027 if typ == 'real':
1028 # grid point calculation
1029 def gindex(x, min, delt):
1030 return numpy.round((x - min) / delt)
1031
1032 xdelta = delta(self.xmin, self.xmax, self.nx)
1033 ydelta = delta(self.ymin, self.ymax, self.ny)
1034 xidx = gindex(posx, self.xmin, xdelta)
1035 yidx = gindex(posy, self.ymin, ydelta)
1036 elif typ == 'index':
1037 xidx = posx
1038 yidx = posy
1039 else:
1040 raise ValueError("given value of 'typ' is invalid.")
1041
1042 if xidx >= self.nx or xidx < 0:
1043 raise ValueError("specified x-position is out of the data range")
1044 if yidx > self.ny or yidx < 0:
1045 raise ValueError("specified y-position is out of the data range")
1046
1047 # read motor values and perform gridding for all subscans
1048 if not self.gridded:
1049 self.read_motors()
1050 self.glist = []
1051 for fs in self.fastscans:
1052 g2l = fs._gridCCDnumbers(
1053 self.nx, self.ny, self.ccdnr,
1054 gridrange=((self.xmin, self.xmax), (self.ymin, self.ymax)))
1055 self.glist.append(g2l) # contains the ccdnumbers in g2l.data
1056
1057 self.gridded = True
1058
1059 # return the ccdnumbers and goniometer angles for this position
1060 ret = []
1061 for i in range(len(self.glist)):
1062 motorpos = self.motor_pos[i]
1063 ccdnrs = self.glist[i].data[xidx, yidx]
1064 ret.append([motorpos, ccdnrs])
1065
1066 return ret
1067
1068 def rawRSM(self, posx, posy, qconv, roi=None, nav=[1, 1], typ='real',
1069 datadir=None, keepdir=0, replacedir=None, filterfunc=None,
1070 **kwargs):
1071 """
1072 function to return the reciprocal space map data at a certain
1073 x, y-position from a series of FastScan measurements. It necessary to
1074 give the QConversion-object to be used for the reciprocal space
1075 conversion. The QConversion-object is expected to have the 'area'
1076 conversion routines configured properly.
1077
1078 Parameters
1079 ----------
1080 posx : float
1081 real space x-position or index in x direction
1082 posy : float
1083 real space y-position or index in y direction
1084 qconv : QConversion
1085 QConversion-object to be used for the conversion of the CCD-data to
1086 reciprocal space
1087
1088 roi : tuple, optional
1089 region of interest on the 2D detector. should be a list of lower
1090 and upper bounds of detector channels for the two pixel directions
1091 (default: None)
1092 nav : tuple or list, optional
1093 number of detector pixel which will be averaged together (reduces
1094 the date size)
1095 typ : {'real', 'index'}, optional
1096 type of coordinates. specifies if the position is specified as real
1097 space coordinate or as index. (default: 'real')
1098 filterfunc : callable, optional
1099 function applied to the CCD-frames before any processing. this
1100 function should take a single argument which is the ccddata which
1101 need to be returned with the same shape! e.g. remove hot pixels,
1102 flat/darkfield correction
1103 UB : array-like, optional
1104 sample orientation matrix
1105 datadir : str, optional
1106 the CCD filenames are usually parsed from the SPEC file. With this
1107 option the directory used for the data can be overwritten. Specify
1108 the datadir as simple string. Alternatively the innermost
1109 directory structure can be automatically taken from the specfile.
1110 If this is needed specify the number of directories which should be
1111 kept using the keepdir option.
1112 keepdir : int, optional
1113 number of directories which should be taken from the SPEC file.
1114 (default: 0)
1115 replacedir : int, optional
1116 number of outer most directory names which should be replaced in
1117 the output (default = None). One can either give keepdir, or
1118 replacedir, with replace taking preference if both are given.
1119
1120 Returns
1121 -------
1122 qx, qy, qz : ndarray
1123 reciprocal space positions of the reciprocal space map
1124 ccddata : ndarray
1125 raw data of the reciprocal space map
1126 valuelist : ndarray
1127 valuelist containing the ccdframe numbers and corresponding motor
1128 positions
1129 """
1130 U = kwargs.get('UB', numpy.identity(3))
1131
1132 # get CCDframe numbers and motor values
1133 valuelist = self.getCCDFrames(posx, posy, typ)
1134 # load ccd frames and convert to reciprocal space
1135 fsccd = self.fastscans[0]
1136 ccdtemplate, nextNr = fsccd.getccdFileTemplate(
1137 fsccd.specscan, datadir, keepdir=keepdir, replacedir=replacedir)
1138
1139 # read ccd shape from first image
1140 imgindex, filenumber = fsccd._get_image_number(
1141 valuelist[0][1], nextNr, nextNr, ccdtemplate)
1142 filename = ccdtemplate % filenumber
1143 ccdshape = fsccd._read_image(filename, imgindex, nav, roi,
1144 filterfunc).shape
1145 ccddata = numpy.zeros((len(self.fastscans), ccdshape[0], ccdshape[1]))
1146 motors = []
1147
1148 for i in range(len(self.gonio_motors)):
1149 motors.append(numpy.zeros(0))
1150 # go through the gridded data and average the ccdframes
1151 for i in range(len(self.fastscans)):
1152 imotors, ccdnrs = valuelist[i]
1153 fsccd = self.fastscans[i]
1154 # append motor positions
1155 for j in range(len(self.gonio_motors)):
1156 motors[j] = numpy.append(motors[j], imotors[j])
1157 # read CCD
1158 if not ccdnrs:
1159 continue
1160 else:
1161 ccdtemplate, nextNr = fsccd.getccdFileTemplate(fsccd.specscan)
1162 framecount = 0
1163 # read ccd-frames and average them
1164 for imgnum in ccdnrs:
1165 imgindex, filenumber = fsccd._get_image_number(
1166 imgnum, nextNr, nextNr, ccdtemplate)
1167 filename = ccdtemplate % filenumber
1168 ccd = fsccd._read_image(filename, imgindex, nav, roi,
1169 filterfunc)
1170 ccddata[i, ...] += ccd
1171 framecount += 1
1172 ccddata[i, ...] /= float(framecount)
1173
1174 qx, qy, qz = qconv.area(*motors, roi=roi, Nav=nav, UB=U)
1175 return qx, qy, qz, ccddata, valuelist
1176
1177 def gridRSM(self, posx, posy, qnx, qny, qnz, qconv, roi=None, nav=[1, 1],
1178 typ='real', filterfunc=None, **kwargs):
1179 """
1180 function to calculate the reciprocal space map at a certain
1181 x, y-position from a series of FastScan measurements it is necessary to
1182 specify the number of grid-oints for the reciprocal space map and the
1183 QConversion-object to be used for the reciprocal space conversion. The
1184 QConversion-object is expected to have the 'area' conversion routines
1185 configured properly.
1186
1187 Parameters
1188 ----------
1189 posx : float
1190 real space x-position or index in x direction
1191 posy : float
1192 real space y-position or index in y direction
1193 qnx, qny, qnz : int
1194 number of points in the Qx, Qy, Qz direction of the gridded
1195 reciprocal space map
1196 qconv : QConversion
1197 QConversion-object to be used for the conversion of the CCD-data to
1198 reciprocal space
1199 roi : tuple, optional
1200 region of interest on the 2D detector. should be a list of lower
1201 and upper bounds of detector channels for the two pixel directions
1202 (default: None)
1203 nav : tuple or list, optional
1204 number of detector pixel which will be averaged together (reduces
1205 the date size)
1206 typ : {'real', 'index'}, optional
1207 type of coordinates. specifies if the position is specified as real
1208 space coordinate or as index. (default: 'real')
1209 filterfunc : callable, optional
1210 function applied to the CCD-frames before any processing. this
1211 function should take a single argument which is the ccddata which
1212 need to be returned with the same shape! e.g. remove hot pixels,
1213 flat/darkfield correction
1214 UB : ndarray
1215 sample orientation matrix
1216
1217 Returns
1218 -------
1219 Gridder3D
1220 object with gridded reciprocal space map
1221 """
1222 qx, qy, qz, ccddata, vallist = self.rawRSM(
1223 posx, posy, qconv, roi=roi, nav=nav,
1224 typ=typ, filterfunc=filterfunc, **kwargs)
1225 # perform 3D gridding and return the data or gridder
1226 g = Gridder3D(qnx, qny, qnz)
1227 g(qx, qy, qz, ccddata)
1228 return g
1229
1230 def grid2Dall(self, nx, ny, **kwargs):
1231 """
1232 function to grid the counter data and return the gridded X, Y and
1233 Intensity values from all the FastScanSeries.
1234
1235 Parameters
1236 ----------
1237 nx, ny : int
1238 number of bins in x, and y direction
1239
1240 counter : str, optional
1241 name of the counter to use for gridding (default: 'mpx4int' (ID01))
1242 gridrange : tuple, optional
1243 range for the gridder: format: ((xmin, xmax), (ymin, ymax))
1244
1245 Returns
1246 -------
1247 Gridder2D
1248 object with X, Y, data on regular x, y-grid
1249 """
1250 counter = kwargs.get('counter', 'mpx4int')
1251 gridrange = kwargs.get('gridrange', ((self.xmin, self.xmax),
1252 (self.ymin, self.ymax)))
1253
1254 # define gridder
1255 g2d = Gridder2D(nx, ny)
1256 if gridrange:
1257 g2d.dataRange(gridrange[0][0], gridrange[0][1],
1258 gridrange[1][0], gridrange[1][1])
1259 g2d.KeepData(True)
1260
1261 for fs in self.fastscans:
1262 # check if counter is in data fields
1263 try:
1264 inte = fs.data[counter]
1265 except ValueError:
1266 raise ValueError("field named '%s' not found in data parsed "
1267 "from scan #%d in file %s"
1268 % (counter, fs.scannr, fs.filename))
1269
1270 # grid data
1271 g2d(fs.xvalues, fs.yvalues, fs.data[counter])
1272
1273 # return gridded data
1274 return g2d
+0
-100
xrayutilities/io/filedir.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import glob
18 import os.path
19
20 from .. import config
21 from .helper import xu_h5open
22
23
24 class FileDirectory(object):
25
26 """
27 Parses a directory for files, which can be stored to a HDF5 file for
28 further usage. The file parser is given to the constructor and must provide
29 a Save2HDF5 method.
30 """
31
32 def __init__(self, datapath, ext, parser, **keyargs):
33 """
34
35 Parameters
36 ----------
37 datapath : str
38 directory of the files
39 ext : str
40 extension of the files in the datapath
41 parser : class
42 Parser class for the data files.
43 keyargs : dict
44 further keyword arguments are passed to the constructor of the
45 parser
46 """
47
48 self.datapath = os.path.normpath(datapath)
49 self.extension = ext
50 self.parser = parser
51
52 # create list of files to read
53 self.files = glob.glob(os.path.join(
54 self.datapath, '*.%s' % (self.extension)))
55
56 if not self.files:
57 print("XU.io.FileDirectory: no file found in %s" % (self.datapath))
58 return
59
60 if config.VERBOSITY >= config.INFO_ALL:
61 print("XU.io.FileDirectory: %d files found in %s"
62 % (len(self.files), self.datapath))
63
64 self.init_keyargs = keyargs
65
66 def Save2HDF5(self, h5f, group="", comp=True):
67 """
68 Saves the data stored in the found files in the specified directory in
69 a HDF5 file as a HDF5 arrays in a subgroup. By default the data is
70 stored in a group given by the foldername - this can be changed by
71 passing the name of a target group or a path to the target group via
72 the "group" keyword argument.
73
74 Parameters
75 ----------
76 h5f : file-handle or str
77 a HDF5 file object or name
78 group : str, optional
79 group where to store the data (defaults to pathname if group is
80 empty string)
81 comp : bool, optional
82 activate compression - true by default
83 """
84 with xu_h5open(h5f, 'a') as h5:
85 if isinstance(group, str):
86 if group == "":
87 group = os.path.split(self.datapath)[1]
88 g = h5.get(group)
89 if not g:
90 g = h5.create_group(group)
91 else:
92 g = group
93
94 for infile in self.files:
95 # read EDFFile and save to hdf5
96 filename = os.path.split(infile)[1]
97 e = self.parser(filename, path=self.datapath,
98 **self.init_keyargs)
99 e.Save2HDF5(h5, group=g, comp=comp)
+0
-129
xrayutilities/io/helper.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2013-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17
18 """
19 convenience functions to open files for various data file reader
20
21 these functions should be used in new parsers since they transparently allow to
22 open gzipped and bzipped files
23 """
24
25 import bz2
26 import gzip
27 import sys
28
29 import h5py
30
31 from ..exception import InputError
32 from .. import config
33
34 # python 2to3 compatibility
35 try:
36 basestring
37 except NameError:
38 basestring = str
39
40
41 def xu_open(filename, mode='rb'):
42 """
43 function to open a file no matter if zipped or not. Files with extension
44 '.gz', '.bz2', and '.xz' are assumed to be compressed and transparently
45 opened to read like usual files.
46
47 Parameters
48 ----------
49 filename : str
50 filename of the file to open (full including path)
51 mode : str, optional
52 mode in which the file should be opened
53
54 Returns
55 -------
56 file-handle
57 handle of the opened file
58
59 Raises
60 ------
61 IOError
62 If the file does not exist an IOError is raised by the open routine,
63 which is not caught within the function
64 """
65 if config.VERBOSITY >= config.INFO_ALL:
66 print("XU:io: opening file %s" % filename)
67 if filename.endswith('.gz'):
68 fid = gzip.open(filename, mode)
69 elif filename.endswith('.bz2'):
70 fid = bz2.BZ2File(filename, mode)
71 elif filename.endswith('.xz'):
72 if sys.version_info >= (3, 3):
73 import lzma
74 fid = lzma.open(filename, mode)
75 else:
76 try:
77 import contextlib
78 import lzma
79 fid = contextlib.closing(lzma.LZMAFile(filename, mode))
80 except ImportError:
81 raise TypeError("File compression type not supported! Install "
82 "pyliblzma or switch to Python >3.3")
83 else:
84 fid = open(filename, mode)
85
86 return fid
87
88
89 class xu_h5open(object):
90 """
91 helper object to decide if a HDF5 file has to be opened/closed when
92 using with a 'with' statement.
93 """
94
95 def __init__(self, f, mode='r'):
96 """
97 Parameters
98 ----------
99 f : str
100 filename or h5py.File instance
101 mode : str, optional
102 mode in which the file should be opened. ignored in case a file
103 handle is passed as f
104 """
105 self.closeFile = True
106 self.fid = None
107 self.mode = mode
108 if isinstance(f, h5py.File):
109 self.fid = f
110 self.closeFile = False
111 self.filename = f.filename
112 elif isinstance(f, basestring):
113 self.filename = f
114 else:
115 raise InputError("f argument of wrong type was passed, "
116 "should be string or filename")
117
118 def __enter__(self):
119 if self.fid:
120 if not self.fid.id.valid:
121 self.fid = h5py.File(self.filename, self.mode)
122 else:
123 self.fid = h5py.File(self.filename, self.mode)
124 return self.fid
125
126 def __exit__(self, type, value, traceback):
127 if self.closeFile:
128 self.fid.close()
+0
-254
xrayutilities/io/ill_numor.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module for reading ILL data files (station D23): numor files
19 """
20
21 import collections
22 import os.path
23 import re
24
25 import numpy
26
27 from ..exception import InputError
28 # relative imports from xrayutilities
29 from .helper import xu_open
30
31 re_comment = re.compile(r"^A+$")
32 re_basicinfo = re.compile(r"^R+$")
33 re_values = re.compile(r"^F+$")
34 re_spectrum = re.compile(r"^S+$")
35 re_header = re.compile(r"^I+$")
36
37
38 class numorFile(object):
39 """
40 Represents a ILL data file (numor). The file is read during the Constructor
41 call. This class should work for created at station D23 using the mad
42 acquisition system.
43
44 Parameters
45 ----------
46 filename : str
47 a string with the name of the data file
48 """
49
50 columns = {0: ('detector', 'monitor', 'time', 'gamma', 'omega', 'psi'),
51 1: ('detector', 'monitor', 'time', 'gamma'),
52 2: ('detector', 'monitor', 'time', 'omega'),
53 5: ('detector', 'monitor', 'time', 'psi')}
54
55 def __init__(self, filename, path=None):
56 """
57 constructor for the data file parser
58
59 Parameters
60 ----------
61 filename : str
62 a string with the name of the data file
63 path : str, optional
64 directory of the data file
65 """
66 self.filename = filename
67 if path is None:
68 self.full_filename = self.filename
69 else:
70 self.full_filename = os.path.join(path, self.filename)
71
72 self.Read()
73
74 def getline(self, fid):
75 return fid.readline().decode('ascii')
76
77 def ssplit(self, string):
78 """
79 multispace split. splits string at two or more spaces after stripping
80 it.
81 """
82 return re.split(r'\s\s+', string.strip())
83
84 def Read(self):
85 """
86 Read the data from the file
87 """
88 with xu_open(self.full_filename) as fid:
89 self.filesize = os.stat(self.full_filename).st_size
90 # read header
91 self.init_mopo = {}
92 self.comments = []
93 self.header = {}
94 self._data = []
95 while fid.tell() < self.filesize:
96 line = self.getline(fid)
97
98 if re_comment.match(line):
99 # read AAAA sections
100 line = self.getline(fid)
101 desc = []
102 for j in range(int(line.split()[1])):
103 desc += self.ssplit(self.getline(fid))
104 comval = self.ssplit(self.getline(fid))
105 self.comments.append((desc, comval))
106
107 if re_basicinfo.match(line):
108 # read RRRR section
109 info = self.ssplit(self.getline(fid))
110 self.dataversion = int(info[2])
111 self.runnumber = int(info[0])
112
113 if int(info[1]) > 0:
114 headerdesc = ''
115 for j in range(int(info[1])):
116 headerdesc += self.getline(fid) + '\n'
117 self.comments.append((['Fileheader'], [headerdesc]))
118
119 if re_header.match(line):
120 # read IIII section: integer header values
121 info = self.ssplit(self.getline(fid))
122 names = []
123 values = []
124
125 for j in range(int(info[1])):
126 names += self.getline(fid).split()
127 values = numpy.fromfile(fid, dtype=int,
128 count=int(info[0]), sep=' ')
129 self.header = {k: v for k, v in zip(names, values)}
130
131 if re_values.match(line):
132 # read FFFF section: initial motor positions
133 info = self.ssplit(self.getline(fid))
134 names = []
135 values = []
136
137 for j in range(int(info[1])):
138 names += self.ssplit(self.getline(fid))
139 values = numpy.fromfile(fid, dtype=float,
140 count=int(info[0]), sep=' ')
141 self.init_mopo = {k: v for k, v in zip(names, values)}
142
143 if re_spectrum.match(line):
144 # read SSSS section: initial motor positions
145 info = self.ssplit(self.getline(fid))
146 nspectrum = int(info[0])
147 self.nspectra = int(info[2])
148
149 if re_values.match(self.getline(fid)):
150 # read FFFF section: subspectrum data
151 nval = int(self.getline(fid))
152 # check if nval is multiple of npdone
153 if nval % self.header['npdone'] != 0:
154 raise InputError("File corrupted, wrong number of "
155 "data values (%d) found." % nval)
156
157 self._data.append(numpy.fromfile(fid, dtype=float,
158 count=nval, sep=' '))
159
160 if int(info[1]) == 0:
161 break
162 # make data columns accessible by names
163 data = numpy.reshape(self._data[0],
164 (self.header['npdone'],
165 nval // self.header['npdone']))
166 self.data = numpy.rec.fromrecords(
167 data, names=self.columns[self.header['manip']])
168
169 def __str__(self):
170 ostr = 'Numor: %d (%s)\n' % (self.runnumber, self.filename)
171 ostr += 'Comments: %s\n' % " ".join(
172 s for c in self.comments for s in c[1])
173 ostr += 'Npoints/Ndone: %(nkmes)d/%(npdone)d\n' % (self.header)
174 ostr += 'Nspectra: %d\n' % self.nspectra
175 ostr += 'Ncolumns: %s' % self.data.shape[1]
176 return ostr
177
178
179 def numor_scan(scannumbers, *args, **kwargs):
180 """
181 function to obtain the angular cooridinates as well as intensity values
182 saved in numor datafiles. Especially useful for combining several scans
183 into one data object.
184
185 Parameters
186 ----------
187 scannumbers : int or str or iterable
188 number of the numors, or list of numbers. This will be transformed to a
189 string and used as a filename
190 args : str, optional
191 names of the motors e.g.: 'omega', 'gamma'
192 kwargs : dict
193 keyword arguments are passed on to numorFile. e.g. 'path' for the files
194 directory
195
196 Returns
197 -------
198 [ang1, ang2, ...] : list
199 angular positions list, omitted if no args are given
200 data : ndarray
201 all the data values.
202
203 Examples
204 --------
205 >>> [om, gam], data = xu.io.numor_scan(414363, 'omega', 'gamma')
206 """
207
208 if isinstance(scannumbers, (str, int)):
209 scanlist = list([scannumbers])
210 elif isinstance(scannumbers, collections.Iterable):
211 scanlist = scannumbers
212 else:
213 raise TypeError('scannumbers is of invalid type (%s)'
214 % type(scannumbers))
215
216 angles = dict.fromkeys(args)
217 for key in angles.keys():
218 if not isinstance(key, str):
219 raise InputError("*arg values need to be strings with motornames")
220 angles[key] = numpy.zeros(0)
221 buf = numpy.zeros(0)
222 MAP = numpy.zeros(0)
223
224 for nr in scanlist:
225 scan = numorFile(str(nr), **kwargs)
226 sdata = scan.data
227 if MAP.dtype == numpy.float64:
228 MAP.dtype = sdata.dtype
229 # append scan data to MAP, where all data are stored
230 MAP = numpy.append(MAP, sdata)
231 # check type of scan
232 for i in range(len(args)):
233 motname = args[i]
234 scanlength = len(sdata)
235 try:
236 buf = sdata[motname]
237 except ValueError:
238 mv = [v for k, v in scan.init_mopo.items()
239 if motname in k][0]
240 buf = mv * numpy.ones(scanlength)
241 angles[motname] = numpy.concatenate((angles[motname], buf))
242
243 retval = []
244 for motname in args:
245 # create return values in correct order
246 retval.append(angles[motname])
247
248 if not args:
249 return MAP
250 elif len(args) == 1:
251 return retval[0], MAP
252 else:
253 return retval, MAP
+0
-452
xrayutilities/io/imagereader.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2012-2015 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import os.path
18 import time
19
20 import numpy
21
22 from .. import config
23 from ..exception import InputError
24 # relative imports from xrayutilities
25 from .helper import xu_open
26
27
28 class ImageReader(object):
29
30 """
31 parse CCD frames in the form of tiffs or binary data (``*.bin``)
32 to numpy arrays. ignore the header since it seems to contain
33 no useful data
34
35 The routine was tested so far with
36
37 1. RoperScientific files with 4096x4096 pixels created at Hasylab Hamburg,
38 which save an 16bit integer per point.
39 2. Perkin Elmer images created at Hasylab Hamburg with 2048x2048 pixels.
40 """
41
42 def __init__(self, nop1, nop2, hdrlen=0, flatfield=None, darkfield=None,
43 dtype=numpy.int16, byte_swap=False):
44 """
45 initialize the ImageReader reader, which includes setting the dimension
46 of the images as well as defining the data used for flat- and darkfield
47 correction!
48
49 Parameters
50 ----------
51 nop1, nop2 : int
52 number of pixels in the first and second dimension of the image
53 hdrlen : int, optional
54 length of the file header which should be ignored
55 flatfield : str or ndarray, optional
56 filename or data for flatfield correction. supported file types
57 include (.bin/.tif (also compressed .xz or .gz) and .npy files).
58 otherwise a 2D numpy array should be given
59 darkfield : str or ndarray, optional
60 filename or data for darkfield correction. same types as for flat
61 field are supported.
62 dtype : numpy.dtype, optional
63 datatype of the stored values (default: numpy.int16)
64 byte_swap : bool, optional
65 flag which determines bytes are swapped after reading
66 """
67
68 # save number of pixels per image
69 self.nop1 = nop1
70 self.nop2 = nop2
71 self.dtype = dtype
72 self.hdrlen = hdrlen
73 self.byteswap = byte_swap
74
75 # read flatfield
76 if flatfield:
77 if isinstance(flatfield, str):
78 if os.path.splitext(flatfield)[1] == '.npy':
79 self.flatfield = numpy.load(flatfield)
80 elif os.path.splitext(flatfield)[1] in \
81 ['.gz', '.xz', '.bin', '.tif']:
82 # read without flatc and darkc
83 self.flatfield = self.readImage(flatfield)
84 else:
85 raise InputError("Error: unknown filename for "
86 "flatfield correction!")
87 elif isinstance(flatfield, numpy.ndarray):
88 self.flatfield = flatfield
89 else:
90 raise InputError("Error: unsupported type for "
91 "flatfield correction!")
92
93 # read darkfield
94 if darkfield:
95 if isinstance(darkfield, str):
96 if os.path.splitext(darkfield)[1] == '.npy':
97 self.darkfield = numpy.load(darkfield)
98 elif os.path.splitext(darkfield)[1] in \
99 ['.gz', '.xz', '.bin', '.tif']:
100 # read without flatc and darkc
101 self.darkfield = self.readImage(darkfield)
102 else:
103 raise InputError("Error: unknown filename for "
104 "darkfield correction!")
105 elif isinstance(darkfield, numpy.ndarray):
106 self.darkfield = darkfield
107 else:
108 raise InputError("Error: unsupported type for "
109 "darkfield correction!")
110
111 if flatfield:
112 self.flatc = True
113 if config.VERBOSITY >= config.INFO_ALL:
114 print("XU.io.ImageReader: flatfield correction enabled")
115 else:
116 self.flatc = False
117 if darkfield:
118 self.darkc = True
119 if config.VERBOSITY >= config.INFO_ALL:
120 print("XU.io.ImageReader: darkfield correction enabled")
121 else:
122 self.darkc = False
123
124 def readImage(self, filename, path=None):
125 """
126 read image file
127 and correct for dark- and flatfield in case the necessary data are
128 available.
129
130 returned data = ((image data)-(darkfield))/flatfield*average(flatfield)
131
132 Parameters
133 ----------
134 filename : str
135 filename of the image to be read. so far only single filenames are
136 supported. The data might be compressed. supported extensions:
137 .tif, .bin and .bin.xz
138 path : str, optional
139 path of the data files
140 """
141 if path:
142 full_filename = os.path.join(path, filename)
143 else:
144 full_filename = filename
145
146 if config.VERBOSITY >= config.INFO_ALL:
147 print("XU.io.ImageReader.readImage: file %s" % (full_filename))
148 t1 = time.time()
149
150 with xu_open(full_filename) as fh:
151 # jump over header
152 fh.seek(self.hdrlen)
153 # read image
154 rlen = numpy.dtype(self.dtype).itemsize * self.nop1 * self.nop2
155 img = numpy.frombuffer(fh.read(rlen), dtype=self.dtype)
156 if self.byteswap:
157 img = img.byteswap()
158 img.shape = (self.nop1, self.nop2) # reshape the data
159 # darkfield correction
160 if self.darkc:
161 img = (img - self.darkfield).astype(numpy.float32)
162 # flatfield correction
163 if self.flatc:
164 img = img.astype(numpy.float32) / self.flatfield
165
166 if config.VERBOSITY >= config.INFO_ALL:
167 t2 = time.time()
168 print("XU.io.ImageReader.readImage: parsing time %8.3f"
169 % (t2 - t1))
170
171 return img
172
173
174 dlen = {'char': 1,
175 'byte': 1,
176 'word': 2,
177 'dword': 4,
178 'rational': 8, # not implemented correctly
179 'float': 4,
180 'double': 8}
181
182 dtypes = {1: 'byte',
183 2: 'char',
184 3: 'word',
185 4: 'dword',
186 5: 'rational', # not implemented correctly
187 6: 'byte',
188 7: 'byte',
189 8: 'word',
190 9: 'dword',
191 10: 'rational', # not implemented correctly
192 11: 'float',
193 12: 'double'}
194
195 nptyp = {1: numpy.byte,
196 2: numpy.char,
197 3: numpy.uint16,
198 4: numpy.uint32,
199 5: numpy.uint32,
200 6: numpy.int8,
201 7: numpy.byte,
202 8: numpy.int16,
203 9: numpy.int32,
204 10: numpy.int32,
205 11: numpy.float32,
206 12: numpy.float64}
207
208 tiffdtype = {1: {8: numpy.uint8, 16: numpy.uint16, 32: numpy.uint32},
209 2: {8: numpy.int8, 16: numpy.int16, 32: numpy.int32},
210 3: {16: numpy.float16, 32: numpy.float32}}
211
212 tifftags = {256: 'ImageWidth', # width
213 257: 'ImageLength', # height
214 258: 'BitsPerSample',
215 259: 'Compression',
216 262: 'PhotometricInterpretation',
217 272: 'Model',
218 273: 'StripOffsets',
219 282: 'XResolution',
220 283: 'YResolution',
221 305: 'Software',
222 339: 'SampleFormat'}
223
224
225 class TIFFRead(ImageReader):
226 """
227 class to Parse a TIFF file including extraction of information from the
228 file header in order to determine the image size and data type
229
230 The data stored in the image are available in the 'data' property.
231 """
232
233 def __init__(self, filename, path=None):
234 """
235 initialization of the class which will prepare the parser and parse
236 the files content into class properties
237
238 Parameters
239 ----------
240 filename : str
241 file name of the TIFF-like image file
242 path : str, optional
243 path of the data file
244 """
245 if path:
246 full_filename = os.path.join(path, filename)
247 else:
248 full_filename = filename
249
250 with xu_open(full_filename, 'rb') as fh:
251 self.byteorder = fh.read(2*dlen['char'])
252 self.version = numpy.frombuffer(fh.read(dlen['word']),
253 dtype=numpy.uint16)[0]
254 if self.byteorder not in (b'II', b'MM') or self.version != 42:
255 raise TypeError("Not a TIFF file (%s)" % filename)
256 if self.byteorder != b'II':
257 raise NotImplementedError("The 'MM' byte order is not yet "
258 "implemented, please file a bug!")
259
260 fh.seek(4)
261 self.ifdoffset = numpy.frombuffer(fh.read(dlen['dword']),
262 dtype=numpy.uint32)[0]
263 fh.seek(self.ifdoffset)
264
265 self.ntags = numpy.frombuffer(fh.read(dlen['word']),
266 dtype=numpy.uint16)[0]
267
268 self._parseImgTags(fh, self.ntags)
269
270 fh.seek(self.ifdoffset + 2 + 12 * self.ntags)
271 nextimgoffset = numpy.frombuffer(fh.read(dlen['dword']),
272 dtype=numpy.uint32)[0]
273 if nextimgoffset != 0:
274 raise NotImplementedError("Multiple images per file are not "
275 "supported, please file a bug!")
276
277 # check if image type is supported
278 if self.imgtags.get('Compression', 1) != 1:
279 raise NotImplementedError("Compression is not supported, "
280 "please file a bug report!")
281 if self.imgtags.get('PhotometricInterpretation', 0) not in (0, 1):
282 raise NotImplementedError("RGB and colormap is not supported")
283
284 sf = self.imgtags.get('SampleFormat', 1)
285 bs = self.imgtags.get('BitsPerSample', 1)
286 if isinstance(self.imgtags['StripOffsets'], numpy.ndarray):
287 hdrlen = self.imgtags['StripOffsets'][0]
288 else:
289 hdrlen = self.imgtags['StripOffsets']
290 ImageReader.__init__(self,
291 self.imgtags['ImageLength'],
292 self.imgtags['ImageWidth'],
293 hdrlen=hdrlen,
294 dtype=tiffdtype[sf][bs],
295 byte_swap=False)
296
297 self.data = self.readImage(filename, path)
298
299 def _parseImgTags(self, fh, ntags):
300 """
301 parse TIFF image tags from Image File Directory header
302
303 Parameters
304 ----------
305 fh : file-handle
306 file handle of the TIFF file
307 ntags : int
308 number of tags in the Image File Directory
309 """
310
311 self.imgtags = {}
312 for i in range(ntags):
313 ftag = numpy.frombuffer(fh.read(dlen['word']),
314 dtype=numpy.uint16)[0]
315 ftype = numpy.frombuffer(fh.read(dlen['word']),
316 dtype=numpy.uint16)[0]
317 flength = numpy.frombuffer(fh.read(dlen['dword']),
318 dtype=numpy.uint32)[0]
319 fdoffset = numpy.frombuffer(fh.read(dlen['dword']),
320 dtype=numpy.uint32)[0]
321
322 pos = fh.tell()
323 if flength*dlen[dtypes[ftype]] <= 4:
324 fdoffset = pos - dlen['dword']
325 fh.seek(fdoffset)
326 if ftype == 2:
327 fdata = fh.read(flength * dlen[dtypes[ftype]]).decode("ASCII")
328 fdata = fdata.rstrip('\0')
329 else:
330 rlen = flength * dlen[dtypes[ftype]]
331 fdata = numpy.frombuffer(fh.read(rlen), dtype=nptyp[ftype])
332 if flength == 1:
333 fdata = fdata[0]
334 fh.seek(pos)
335
336 # add field to tags
337 self.imgtags[tifftags.get(ftag, ftag)] = fdata
338
339
340 class PerkinElmer(ImageReader):
341 """
342 parse PerkinElmer CCD frames (``*.tif``) to numpy arrays
343 Ignore the header since it seems to contain no useful data
344
345 The routine was tested only for files with 2048x2048 pixel images
346 created at Hasylab Hamburg which save an 32bit float per point.
347 """
348
349 def __init__(self, **keyargs):
350 """
351 initialize the PerkinElmer reader, which includes setting the dimension
352 of the images as well as defining the data used for flat- and darkfield
353 correction!
354
355 Parameters
356 ----------
357 flatfield : str or ndarray, optional
358 filename or data for flatfield correction. supported file types
359 include (.bin .bin.xz and .npy files). otherwise a 2D numpy
360 array should be given
361 darkfield : str or ndarray, optional
362 filename or data for darkfield correction. same types as for flat
363 field are supported.
364 """
365
366 ImageReader.__init__(self, 2048, 2048, hdrlen=8, dtype=numpy.float32,
367 byte_swap=False, **keyargs)
368
369
370 class RoperCCD(ImageReader):
371
372 """
373 parse RoperScientific CCD frames (``*.bin``) to numpy arrays
374 Ignore the header since it seems to contain no useful data
375
376 The routine was tested only for files with 4096x4096 pixel images
377 created at Hasylab Hamburg which save an 16bit integer per point.
378 """
379
380 def __init__(self, **keyargs):
381 """
382 initialize the RoperCCD reader, which includes setting the dimension of
383 the images as well as defining the data used for flat- and darkfield
384 correction!
385
386 Parameters
387 ----------
388 flatfield : str or ndarray, optional
389 filename or data for flatfield correction. supported file types
390 include (.bin .bin.xz and .npy files). otherwise a 2D numpy
391 array should be given
392 darkfield : str or ndarray, optional
393 filename or data for darkfield correction. same types as for flat
394 field are supported.
395 """
396
397 ImageReader.__init__(self, 4096, 4096, hdrlen=216, dtype=numpy.int16,
398 byte_swap=False, **keyargs)
399
400
401 class Pilatus100K(ImageReader):
402 """
403 parse Dectris Pilatus 100k frames (``*.tiff``) to numpy arrays
404 Ignore the header since it seems to contain no useful data
405 """
406
407 def __init__(self, **keyargs):
408 """
409 initialize the Piulatus100k reader, which includes setting the
410 dimension of the images as well as defining the data used for flat- and
411 darkfield correction!
412
413 Parameters
414 ----------
415 flatfield : str or ndarray, optional
416 filename or data for flatfield correction. supported file types
417 include (.bin .bin.xz and .npy files). otherwise a 2D numpy
418 array should be given
419 darkfield : str or ndarray, optional
420 filename or data for darkfield correction. same types as for flat
421 field are supported.
422 """
423
424 ImageReader.__init__(self, 195, 487, hdrlen=4096, dtype=numpy.int32,
425 byte_swap=False, **keyargs)
426
427
428 def get_tiff(filename, path=None):
429 """
430 read tiff image file and return the data
431
432 Parameters
433 ----------
434 filename : str
435 filename of the image to be read. so far only single filenames are
436 supported. The data might be compressed.
437 path : str, optional
438 path of the data file
439 """
440
441 if config.VERBOSITY >= config.INFO_ALL:
442 print("XU.io.get_tiff: file %s" % (filename))
443 t1 = time.time()
444
445 t = TIFFRead(filename, path=path)
446
447 if config.VERBOSITY >= config.INFO_ALL:
448 t2 = time.time()
449 print("XU.io.get_tiff: parsing time %8.3f" % (t2 - t1))
450
451 return t.data
+0
-443
xrayutilities/io/panalytical_xml.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 Panalytical XML (www.XRDML.com) data file parser
19
20 based on the native python xml.dom.minidom module.
21 want to keep the number of dependancies as small as possible
22 """
23
24 import os.path
25 import warnings
26 from xml.etree import cElementTree as ElementTree
27
28 import numpy
29
30 from .. import config
31 from .helper import xu_open
32
33
34 class XRDMLMeasurement(object):
35
36 """
37 class to handle scans in a XRDML datafile
38 """
39
40 def __init__(self, measurement, namespace=''):
41 """
42 initialization routine for a XRDML measurement which parses are all
43 scans within this measurement.
44 """
45
46 self.namespace = namespace
47 # get scans in <xrdMeasurement>
48 slist = measurement.findall(self.namespace + "scan")
49
50 self.hkl = (numpy.nan, numpy.nan, numpy.nan)
51 self.material = ""
52 self.ddict = {}
53 for field in ["countTime", "detector", "counts",
54 "beamAttenuationFactors", "hkl"]:
55 self.ddict[field] = []
56 is_scalar = 0
57
58 # loop over all scan entries - scan points
59 for s in slist:
60 # check if scan is complete
61 scanstatus = s.get("status")
62 if scanstatus in ("Aborted", "Not finished") and len(slist) > 1:
63 if config.VERBOSITY >= config.INFO_LOW:
64 print("XU.io.XRDMLFile: subscan has been aborted "
65 "(part of the data unavailable)!")
66 else:
67 self.scanmotname = s.get("scanAxis")
68 reflection = s.find(self.namespace + "reflection")
69 if reflection:
70 m = reflection.find(self.namespace + "material")
71 if m:
72 self.material = m.text
73 hkl = reflection.find(self.namespace + "hkl")
74 if hkl:
75 hkl_h = int(hkl.find(self.namespace + "h").text)
76 hkl_k = int(hkl.find(self.namespace + "k").text)
77 hkl_l = int(hkl.find(self.namespace + "l").text)
78 self.hkl = (hkl_h, hkl_k, hkl_l)
79 points = s.find(self.namespace + "dataPoints")
80
81 # add count time to output data
82 countTime = points.find(self.namespace +
83 "commonCountingTime").text
84 self.ddict["countTime"].append(float(countTime))
85
86 # check for intensities first to get number of points in scan
87 int_elem = points.find(self.namespace + "intensities")
88 if int_elem is not None:
89 data = int_elem.text
90 hascounts = False
91 else:
92 ct_elem = points.find(self.namespace + "counts")
93 ct_npy = numpy.fromstring(ct_elem.text, sep=" ")
94 self.ddict["counts"].append(ct_npy.tolist())
95 data = ct_elem.text
96 hascounts = True
97 # count time normalization; output is counts/sec
98 ct_rate = numpy.fromstring(data, sep=" ") / float(countTime)
99 nofpoints = ct_rate.size
100 # if present read beamAttenuationFactors
101 # they are already corrected in the data file, but may be
102 # interesting
103 attfact = points.find(self.namespace +
104 "beamAttenuationFactors")
105 if attfact is not None:
106 atten = numpy.fromstring(attfact.text, sep=" ")
107 atten_list = atten.tolist()
108 self.ddict["beamAttenuationFactors"].append(atten_list)
109 hasatten = True
110 else:
111 hasatten = False
112
113 if hascounts and hasatten:
114 self.ddict["detector"].append((ct_rate*atten).tolist())
115 else:
116 self.ddict["detector"].append(ct_rate.tolist())
117
118 # read the axes position
119 pos = points.findall(self.namespace + "positions")
120 for p in pos:
121 # read axis name and unit
122 aname = p.get("axis")
123 aunit = p.get("unit")
124
125 # read axis data
126 listp = p.findall(self.namespace + "listPositions")
127 s = p.findall(self.namespace + "startPosition")
128 e = p.findall(self.namespace + "endPosition")
129 if listp: # listPositions
130 listp = listp[0]
131 data_list = numpy.fromstring(listp.text, sep=" ")
132 data_list = data_list.tolist()
133 elif s: # start endPosition
134 data_list = numpy.linspace(
135 float(s[0].text), float(e[0].text),
136 nofpoints).tolist()
137 else: # commonPosition
138 c = p.find(self.namespace + "commonPosition")
139 data_list = numpy.fromstring(c.text, sep=" ")
140 data_list = data_list.tolist()
141 is_scalar = 1
142
143 # have to append the data to the data dictionary in case
144 # the scan is complete!
145 if aname not in self.ddict:
146 self.ddict[aname] = []
147 if not is_scalar:
148 self.ddict[aname].append(data_list)
149 else:
150 self.ddict[aname].append(data_list[0])
151 is_scalar = 0
152
153 # finally all scan data needs to be converted to numpy arrays
154 for k in self.ddict.keys():
155 self.ddict[k] = numpy.array(self.ddict[k])
156
157 # flatten output if only one scan was present
158 if len(slist) == 1:
159 for k in self.ddict.keys():
160 self.ddict[k] = numpy.ravel(self.ddict[k])
161
162 # save scanmot-values and detector counts in special arrays
163 if self.scanmotname in ['2Theta-Omega', 'Gonio']:
164 self.scanmot = self.ddict['2Theta']
165 elif self.scanmotname == 'Omega-2Theta':
166 self.scanmot = self.ddict['Omega']
167 elif self.scanmotname in self.ddict.keys():
168 self.scanmot = self.ddict[self.scanmotname]
169 else:
170 warnings.warn('XU.io: unknown scan motor name in XRDML-File')
171 self.int = self.ddict['detector']
172
173 def __getitem__(self, key):
174 return self.ddict[key]
175
176 def __str__(self):
177 ostr = "XRDML Measurement\n"
178 if self.material:
179 ostr += "Material: '%s'; hkl: %s\n" % (self.material,
180 str(self.hkl))
181 for k in self.ddict.keys():
182 ostr += "%s with %s points\n" % (k, str(self.ddict[k].shape))
183
184 return ostr
185
186
187 class XRDMLFile(object):
188
189 """
190 class to handle XRDML data files. The class is supplied with a file
191 name and uses the XRDMLScan class to parse the xrdMeasurement in the
192 file
193 """
194
195 def __init__(self, fname, path=""):
196 """
197 initialization routine supplied with a filename
198 the file is automatically parsed and the data are available
199 in the "scan" object. If more <xrdMeasurement> tags are present, which
200 should not be the case, their data is present in the "scans" object.
201
202 Parameters
203 ----------
204 fname : str
205 filename of the XRDML file
206 path : str, optional
207 path to the XRDML file
208 """
209 self.full_filename = os.path.join(path, fname)
210 self.filename = os.path.basename(self.full_filename)
211 with xu_open(self.full_filename) as fid:
212 d = ElementTree.parse(fid)
213 root = d.getroot()
214 try:
215 namespace = root.tag[:root.tag.index('}')+1]
216 except ValueError:
217 namespace = ''
218
219 slist = root.findall(namespace+"xrdMeasurement")
220
221 # determine the number of scans in the file
222 self.nscans = len(slist)
223 self.scans = []
224 for s in slist:
225 self.scans.append(XRDMLMeasurement(s, namespace))
226
227 if self.nscans == 1:
228 self.scan = self.scans[0]
229
230 def __str__(self):
231 ostr = "XRDML File: %s\n" % self.filename
232 for s in self.scans:
233 ostr += s.__str__()
234
235 return ostr
236
237
238 def getxrdml_map(filetemplate, scannrs=None, path=".", roi=None):
239 """
240 parses multiple XRDML file and concatenates the results for parsing the
241 xrayutilities.io.XRDMLFile class is used. The function can be used for
242 parsing maps measured with the PIXCel 1D detector (and in limited way also
243 for data acquired with a point detector -> see getxrdml_scan instead).
244
245 Parameters
246 ----------
247 filetemplate : str
248 template string for the file names, can contain a %d which is replaced
249 by the scan number or be a list of filenames
250 scannrs : int or list, optional
251 scan number(s)
252 path : str, optional
253 common path to the filenames
254 roi : tuple, optional
255 region of interest for the PIXCel detector, for other measurements this
256 is not useful!
257
258 Returns
259 -------
260 om, tt, psd : ndarray
261 motor positions and data as flattened numpy arrays
262
263 Examples
264 --------
265 >>> om, tt, psd = xrayutilities.io.getxrdml_map("samplename_%d.xrdml",
266 >>> [1, 2], path="./data")
267 """
268 def getOmPixcel(omraw, ttraw):
269 """
270 function to reshape the Omega values into a form needed for
271 further treatment with xrayutilities
272 """
273 return (omraw[:, numpy.newaxis] * numpy.ones(ttraw.shape)).flatten()
274
275 # read raw data and convert to reciprocal space
276 om = numpy.zeros(0)
277 tt = numpy.zeros(0)
278 psd = numpy.zeros(0)
279 # create scan names
280 if scannrs is None:
281 files = [filetemplate]
282 else:
283 files = list()
284 if not getattr(scannrs, '__iter__', False):
285 scannrs = [scannrs]
286 for nr in scannrs:
287 files.append(filetemplate % nr)
288
289 # parse files
290 for f in files:
291 d = XRDMLFile(os.path.join(path, f))
292 s = d.scan
293 if len(s['detector'].shape) == 1:
294 raise TypeError("XU.getxrdml_map: This function can only be used "
295 "to parse reciprocal space map files")
296
297 if roi is None:
298 roi = [0, s['detector'].shape[1]]
299 if s['Omega'].size < s['2Theta'].size:
300 om = numpy.concatenate(
301 (om, getOmPixcel(s['Omega'], s['2Theta'][:, roi[0]:roi[1]])))
302 tt = numpy.concatenate(
303 (tt, s['2Theta'][:, roi[0]:roi[1]].flatten()))
304 elif s['Omega'].size > s['2Theta'].size:
305 om = numpy.concatenate((om, s['Omega'].flatten()))
306 tt = numpy.concatenate((
307 tt,
308 numpy.ravel(s['2Theta'][:, numpy.newaxis] *
309 numpy.ones(s['Omega'].shape))))
310 else:
311 om = numpy.concatenate((om, s['Omega'].flatten()))
312 tt = numpy.concatenate(
313 (tt, s['2Theta'][:, roi[0]:roi[1]].flatten()))
314 psd = numpy.concatenate(
315 (psd, s['detector'][:, roi[0]:roi[1]].flatten()))
316
317 return om, tt, psd
318
319
320 def getxrdml_scan(filetemplate, *motors, **kwargs):
321 """
322 parses multiple XRDML file and concatenates the results for parsing the
323 xrayutilities.io.XRDMLFile class is used. The function can be used for
324 parsing arbitrary scans and will return the the motor values of the scan
325 motor and additionally the positions of the motors given by in the
326 ``*motors`` argument
327
328 Parameters
329 ----------
330 filetemplate : str
331 template string for the file names, can contain a %d which is replaced
332 by the scan number or be a list of filenames given by the scannrs
333 keyword argument
334
335 motors : str
336 motor names to return: e.g.: 'Omega', '2Theta', ... one can also use
337 abbreviations:
338
339 - 'Omega' = 'om' = 'o'
340 - '2Theta' = 'tt' = 't'
341 - 'Chi' = 'c'
342 - 'Phi' = 'p'
343
344 scannrs : int or list, optional
345 scan number(s)
346 path : str, optional
347 common path to the filenames
348
349 Returns
350 -------
351 scanmot, mot1, mot2,..., detectorint : ndarray
352 motor positions and data as flattened numpy arrays
353
354 Examples
355 --------
356 >>> scanmot, om, tt, inte = xrayutilities.io.getxrdml_scan(
357 >>> "samplename_1.xrdml", 'om', 'tt', path="./data")
358 """
359 flatten = True
360 # parse keyword arguments
361 path = kwargs.get('path', '.')
362 scannrs = kwargs.get('scannrs', None)
363
364 validmotors = ['Omega', '2Theta', 'Psi', 'Chi', 'Phi', 'Z', 'X', 'Y']
365 validmotorslow = [mot.lower() for mot in validmotors]
366 # create correct motor names from input values
367 motnames = []
368 for mot in motors:
369 if mot.lower() in validmotorslow:
370 motnames.append(validmotors[validmotorslow.index(mot.lower())])
371 elif mot.lower() in ['phi', 'p']:
372 motnames.append('Phi')
373 elif mot.lower() in ['chi', 'c']:
374 motnames.append('Chi')
375 elif mot.lower() in ['psi']:
376 motnames.append('Psi')
377 elif mot.lower() in ['tt', 't']:
378 motnames.append('2Theta')
379 elif mot.lower() in ['om', 'o']:
380 motnames.append('Omega')
381 else:
382 raise ValueError("XU: invalid motor name given")
383
384 motvals = numpy.empty((len(motnames) + 1, 0))
385 detvals = numpy.empty(0)
386 # create scan names
387 if scannrs is None:
388 if isinstance(filetemplate, list):
389 files = filetemplate
390 else:
391 files = [filetemplate]
392 else:
393 files = list()
394 if not numpy.iterable(scannrs):
395 scannrs = [scannrs]
396 for nr in scannrs:
397 files.append(filetemplate % nr)
398
399 # parse files
400 if len(files) == 1:
401 flatten = False
402 for f in files:
403 d = XRDMLFile(os.path.join(path, f))
404 s = d.scan
405 detshape = s['detector'].shape
406 detsize = s['detector'].size
407
408 if len(detshape) == 2:
409 angles = numpy.ravel(s.scanmot)
410 angles.shape = (1, angles.size)
411 for mot in motnames:
412 if s[mot].shape != detshape:
413 angles = numpy.vstack((
414 angles,
415 numpy.ravel(s[mot][:, numpy.newaxis] *
416 numpy.ones(detshape))))
417 else:
418 angles = numpy.vstack((angles, numpy.ravel(s[mot])))
419 motvals = numpy.concatenate((motvals, angles), axis=1)
420 dval = numpy.ravel(s['detector'])
421 detvals = numpy.concatenate((detvals, dval))
422 if not flatten:
423 detvals.shape = detshape
424 motvals.shape = (len(motnames) + 1, detshape[0], detshape[1])
425 else:
426 detvals = numpy.concatenate((detvals, s['detector']))
427 angles = s.scanmot
428 angles.shape = (1, angles.size)
429 for mot in motnames:
430 try:
431 angles = numpy.vstack((angles, s[mot]))
432 except ValueError: # motor is not array
433 angles = numpy.vstack(
434 (angles, s[mot] * numpy.ones(detshape)))
435 motvals = numpy.concatenate((motvals, angles), axis=1)
436
437 # make return value
438 ret = []
439 for i in range(motvals.shape[0]):
440 ret.append(motvals[i, ...])
441 ret.append(detvals)
442 return ret
+0
-333
xrayutilities/io/pdcif.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2014 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import copy
18 import re
19 import shlex
20
21 import numpy
22
23 from .. import config
24 from . import xu_open
25
26 re_label = re.compile(r'^\s*_')
27 re_default = re.compile(r'^\s*_('
28 'pd_meas_counts_total|'
29 'pd_meas_intensity_total|'
30 'pd_proc_intensity_total|'
31 'pd_proc_intensity_net|'
32 'pd_calc_intensity_total|'
33 'pd_calc_intensity_net)')
34 re_loop = re.compile(r'^\s*loop_')
35 re_nop = re.compile(r'^\s*_(pd_meas_number_of_points|pd_meas_detector_id)')
36 re_multiline = re.compile(r';')
37
38
39 def remove_comments(line, sep='#'):
40 for s in sep:
41 line = line.split(s)[0]
42 return line
43
44
45 class pdCIF(object):
46
47 """
48 the class implements a primitive parser for pdCIF-like files. It reads
49 every entry and collects the information in the header attribute. The first
50 loop containing one of the intensity fields is assumed to be the data the
51 user is interested in and is transfered to the data array which is stored
52 as numpy record array the columns can be accessed by name
53
54 intensity fields:
55
56 - `_pd_meas_counts_total`
57 - `_pd_meas_intensity_total`
58 - `_pd_proc_intensity_total`
59 - `_pd_proc_intensity_net`
60 - `_pd_calc_intensity_total`
61 - `_pd_calc_intensity_net`
62
63 alternatively the data column name can be given as argument to the
64 constructor
65 """
66
67 def __init__(self, filename, datacolumn=None):
68 """
69 contructor of the pdCIF class
70
71 Parameters
72 ----------
73 filename : str
74 filename of the file to be parsed
75 datacolumn : str, optional
76 name of data column to identify the data loop (default =None; means
77 that a list of default names is used)
78 """
79 self.filename = filename
80 self.datacolumn = datacolumn
81 self.header = {}
82 self.data = None
83
84 self.Parse()
85
86 def Parse(self):
87 """
88 parser of the pdCIF file. the method reads the data from the file and
89 fills the data and header attributes with content
90 """
91 with xu_open(self.filename) as fh:
92 self._parse_single(fh)
93
94 def _parse_single(self, fh, breakAfterData=False):
95 """
96 internal routine to parse a single loop of the pdCIF file
97
98 Parameters
99 ----------
100 fh : file-handle
101 breakAfterData : bool, optional
102 allowing to stop the parsing after data loop was found
103 (default:False)
104 """
105 loopStart = False
106 dataLoop = False
107 dataDone = False
108 loopheader = []
109 numOfEntries = -1
110 multiline = None
111
112 while True:
113 line = fh.readline().decode('ascii')
114 if not line:
115 break
116
117 line = remove_comments(line)
118 if re_loop.match(line):
119 loopStart = True
120 remainingline = re.sub('loop_', '', line).strip()
121 if re_label.match(remainingline):
122 if ((self.datacolumn is None and re_default.match(line)) or
123 line.strip() == self.datacolumn):
124 dataLoop = True
125 loopheader.append(remainingline)
126 continue
127
128 if multiline:
129 multiline += line
130 if re_multiline.match(line): # end of multiline
131 val = multiline
132 self.header[label] = val
133 multiline = None
134 continue
135
136 if re_label.match(line) and not loopStart:
137 # parse header
138 split = line.split(None, 1)
139 label = split[0].strip()
140 try:
141 val = split[1].strip()
142 self.header[label] = val
143 # convert data format of header line
144 if re_nop.match(line):
145 numOfEntries = int(val)
146 try:
147 self.header[label] = float(val)
148 except ValueError:
149 self.header[label] = val
150 except IndexError:
151 # try if multiline
152 line2 = fh.readline().decode('ascii')
153 if re_multiline.match(line2):
154 multiline = line2
155 else: # single value must be in second line
156 self.header[label] = line2
157
158 elif re_label.match(line) and loopStart:
159 # read loop entries
160 if ((self.datacolumn is None and re_default.match(line)) or
161 line.strip() == self.datacolumn):
162 dataLoop = True
163 loopheader.append(line.strip())
164
165 elif loopStart:
166 fh.seek(fh.tell() - len(line))
167 if numOfEntries != -1 and dataLoop and not dataDone:
168 self.data = self._parse_loop_numpy(fh, loopheader,
169 numOfEntries)
170 dataDone = True
171 if breakAfterData:
172 break
173 elif dataLoop and not dataDone:
174 self._parse_loop(fh, loopheader)
175 length = len(self.header[loopheader[0]])
176 dtypes = [(str(entry), type(self.header[entry][0]))
177 for entry in loopheader]
178 for i in range(len(dtypes)):
179 if dtypes[i][1] is str:
180 dtypes[i] = (str(dtypes[i][0]), numpy.str_, 64)
181 self.data = numpy.zeros(length, dtype=dtypes)
182 for entry in loopheader:
183 self.data[entry] = self.header.pop(entry)
184 dataDone = True
185 if breakAfterData:
186 break
187 else:
188 try:
189 self._parse_loop(fh, loopheader)
190 except ValueError:
191 if config.VERBOSITY >= config.INFO_LOW:
192 print('XU.io.pdCIF: unable to handle loop at %d'
193 % fh.tell())
194 dataLoop = False
195 loopStart = False
196 loopheader = []
197 numOfEntries = -1
198
199 def _parse_loop_numpy(self, filehandle, fields, nentry):
200 """
201 function to parse a loop using numpy routines
202
203 Parameters
204 ----------
205 filehandle : file-handle
206 filehandle object to use as data source
207 fields : iterable
208 field names in the loop
209 nentry : int
210 number of entries in the loop
211
212 Returns
213 -------
214 data : ndarray
215 data read from the file as numpy record array
216 """
217 tmp = numpy.fromfile(filehandle, count=nentry * len(fields), sep=' ')
218 data = numpy.rec.fromarrays(tmp.reshape((-1, len(fields))).T,
219 names=fields)
220 return data
221
222 def _parse_loop(self, filehandle, fields):
223 """
224 function to parse a loop using python loops routines. the fields are
225 added to the fileheader dictionary
226
227 Parameters
228 ----------
229 filehandle : file-handle
230 filehandle object to use as data source
231 fields : iterable
232 field names in the loop
233
234 """
235 fh = filehandle
236
237 for f in fields:
238 self.header[f] = []
239 while True:
240 line = fh.readline().decode('ascii')
241 if not line:
242 break
243
244 if re_label.match(line) or line.strip() == '':
245 fh.seek(fh.tell() - len(line))
246 break
247 row = shlex.split(line, comments=True)
248 for i in range(len(fields)):
249 try:
250 self.header[fields[i]].append(float(row[i]))
251 except ValueError:
252 self.header[fields[i]].append(row[i])
253 except IndexError: # maybe multiline field
254 line2 = fh.readline().decode('ascii')
255 line2 = remove_comments(line2)
256 if re_multiline.match(line2):
257 multiline = line2
258 while True:
259 line = fh.readline().decode('ascii')
260 line = remove_comments(line)
261 if not line:
262 fh.seek(fh.tell() - len(line))
263 break
264 if re_multiline.match(line) and line.strip()[1:]:
265 multiline += line
266 else:
267 self.header[fields[i]].append(multiline)
268 break
269 else:
270 fh.seek(fh.tell() - len(line2))
271 raise ValueError('a column is missing for label %s '
272 'in a loop' % fields[i])
273
274
275 class pdESG(pdCIF):
276
277 """
278 class for parsing multiple pdCIF loops in one file.
279 This includes especially ``*.esg`` files which are supposed to
280 consist of multiple loops of pdCIF data with equal length.
281
282 Upon parsing the class tries to combine the data of these different
283 scans into a single data matrix -> same shape of subscan data is assumed
284 """
285
286 def __init__(self, filename, datacolumn=None):
287 self.filename = filename
288 self.datacolumn = datacolumn
289 self.fileheader = {}
290 self.header = {}
291 self.data = None
292
293 self.Parse()
294
295 def Parse(self):
296 """
297 parser of the pdCIF file. the method reads the data from the file and
298 fills the data and header attributes with content
299 """
300 with xu_open(self.filename) as fh:
301 # parse first header and loop
302 self._parse_single(fh, breakAfterData=True)
303 self.fileheader = copy.deepcopy(self.header)
304 self.header = {}
305 fdata = self.data
306 datasize = self.data.size
307 nscan = 1
308 tell = 0
309 while True: # try to parse all scans
310 tell = fh.tell()
311 self._parse_single(fh, breakAfterData=True)
312 if tell == fh.tell():
313 break
314 # copy changing data from header
315 for key in self.header:
316 if key in self.fileheader:
317 if not isinstance(self.fileheader[key], list):
318 self.fileheader[key] = [self.fileheader[key], ]
319 self.fileheader[key].append(self.header[key])
320 else:
321 self.fileheader[key] = self.header[key]
322
323 fdata = numpy.append(fdata, self.data)
324 nscan += 1
325
326 # convert data for output to user
327 for key in self.fileheader:
328 if isinstance(self.fileheader[key], list):
329 self.fileheader[key] = numpy.array(self.fileheader[key])
330 self.data = numpy.empty(fdata.shape)
331 self.data[...] = fdata[...]
332 self.data.shape = (nscan, datasize)
+0
-284
xrayutilities/io/rigaku_ras.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2015-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17
18 """
19 class for reading data + header information from Rigaku RAS (3-column ASCII)
20 files
21
22 Such datafiles are generated by the Smartlab Guidance software from Rigaku.
23 """
24
25 import os.path
26 import re
27 from itertools import islice
28
29 import numpy
30 import numpy.lib.recfunctions
31
32 from .. import config
33 from ..exception import InputError
34 # relative imports from xrayutilities
35 from .helper import xu_open
36
37 re_measstart = re.compile(r"^\*RAS_DATA_START")
38 re_measend = re.compile(r"^\*RAS_DATA_END")
39 re_headerstart = re.compile(r"^\*RAS_HEADER_START")
40 re_headerend = re.compile(r"^\*RAS_HEADER_END")
41 re_datastart = re.compile(r"^\*RAS_INT_START")
42 re_dataend = re.compile(r"^\*RAS_INT_END")
43 re_scanaxis = re.compile(r"^\*MEAS_SCAN_AXIS_X_INTERNAL")
44 re_intstart = re.compile(r"^\*RAS_INT_START")
45 re_datestart = re.compile(r"^\*MEAS_SCAN_START_TIME")
46 re_datestop = re.compile(r"^\*MEAS_SCAN_END_TIME")
47 re_initmoponame = re.compile(r"^\*MEAS_COND_AXIS_NAME_INTERNAL")
48 re_initmopovalue = re.compile(r"^\*MEAS_COND_AXIS_POSITION")
49 re_datacount = re.compile(r"^\*MEAS_DATA_COUNT")
50 re_measspeed = re.compile(r"^\*MEAS_SCAN_SPEED ")
51 re_measstep = re.compile(r"^\*MEAS_SCAN_STEP ")
52
53
54 class RASFile(object):
55
56 """
57 Represents a RAS data file. The file is read during the
58 constructor call
59
60 Parameters
61 ----------
62 filename : str
63 name of the ras-file
64 path : str, optional
65 path to the data file
66 """
67
68 def __init__(self, filename, path=None):
69 self.filename = filename
70 if path is None:
71 self.full_filename = self.filename
72 else:
73 self.full_filename = os.path.join(path, self.filename)
74
75 self.scans = []
76 self.Read()
77
78 def Read(self):
79 """
80 Read the data from the file
81 """
82 with xu_open(self.full_filename) as fid:
83 while True:
84 t = fid.tell()
85 line = fid.readline()
86 line = line.decode('ascii', 'ignore')
87 if config.VERBOSITY >= config.DEBUG:
88 print("XU.io.RASFile: %d: '%s'" % (t, line))
89 if re_measstart.match(line):
90 continue
91 elif re_headerstart.match(line):
92 s = RASScan(self.full_filename, t)
93 self.scans.append(s)
94 fid.seek(s.fidend) # set handle to after scan
95 elif re_measend.match(line) or line in (None, ''):
96 break
97 else:
98 continue
99 if len(self.scans) > 0:
100 self.scan = self.scans[0]
101
102
103 class RASScan(object):
104
105 """
106 Represents a single Scan portion of a RAS data file. The scan is parsed
107 during the constructor call
108
109 Parameters
110 ----------
111 filename : str
112 file name of the data file
113 pos : int
114 seek position of the 'RAS_HEADER_START' line
115 """
116
117 def __init__(self, filename, pos):
118 self.filename = filename
119 self.fidpos = pos
120 self.fidend = pos
121 with xu_open(self.filename) as self.fid:
122 self.fid.seek(self.fidpos)
123 self._parse_header()
124 self._parse_data()
125 self.fidend = self.fid.tell()
126
127 def _parse_header(self):
128 """
129 Read the data from the file
130 """
131 # read header
132 self.header = []
133 keys = {}
134 position = {}
135 offset = self.fid.tell()
136 for line in self.fid:
137 offset += len(line)
138 line = line.decode('ascii', 'ignore')
139 self.header.append(line)
140 if config.VERBOSITY >= config.DEBUG:
141 print("XU.io.RASScan: %d: '%s'" % (offset, line))
142
143 if re_datestart.match(line):
144 m = line.split(' ', 1)[-1].strip()
145 self.scan_start = m.strip('"')
146 elif re_datestop.match(line):
147 m = line.split(' ', 1)[-1].strip()
148 self.scan_stop = m.strip('"')
149 elif re_initmoponame.match(line):
150 idx = int(line.split('-', 1)[-1].split()[0])
151 moname = line.split(' ', 1)[-1].strip().strip('"')
152 keys[idx] = moname
153 elif re_initmopovalue.match(line):
154 idx = int(line.split('-', 1)[-1].split()[0])
155 mopos = line.split(' ', 1)[-1].strip().strip('"')
156 try:
157 mopos = float(mopos)
158 except ValueError:
159 pass
160 position[idx] = mopos
161 elif re_scanaxis.match(line):
162 self.scan_axis = line.split(' ', 1)[-1].strip().strip('"')
163 elif re_datacount.match(line):
164 length = line.split(' ', 1)[-1].strip().strip('"')
165 self.length = int(float(length))
166 elif re_measspeed.match(line):
167 speed = line.split(' ', 1)[-1].strip().strip('"')
168 self.meas_speed = float(speed)
169 elif re_measstep.match(line):
170 step = line.split(' ', 1)[-1].strip().strip('"')
171 self.meas_step = float(step)
172 elif re_headerend.match(line):
173 break
174
175 # generate header dictionary
176 self.init_mopo = {}
177 for k in keys:
178 self.init_mopo[keys[k]] = position[k]
179 self.fid.seek(offset)
180
181 def _parse_data(self):
182 line = self.fid.readline().decode('ascii', 'ignore')
183 offset = self.fid.tell()
184 if re_datastart.match(line):
185 lines = islice(self.fid, self.length)
186 self.data = numpy.genfromtxt(lines)
187 self.data = numpy.rec.fromrecords(self.data,
188 names=[self.scan_axis,
189 'int',
190 'att'])
191 self.fid.seek(offset)
192 lines = islice(self.fid, self.length)
193 dlength = numpy.sum([len(line) for line in lines])
194 if config.VERBOSITY >= config.DEBUG:
195 print("XU.io.RASScan: offset %d; data-length %d"
196 % (offset, dlength))
197 self.fid.seek(offset + dlength)
198 else:
199 raise IOError('File handle at wrong position to read data!')
200
201
202 def getras_scan(scanname, scannumbers, *args, **kwargs):
203 """
204 function to obtain the angular cooridinates as well as intensity values
205 saved in RAS datafiles. Especially useful for reciprocal space map
206 measurements, and to combine date from several scans
207
208 further more it is possible to obtain even more positions from
209 the data file if more than two string arguments with its names are given
210
211 Parameters
212 ----------
213 scanname : str
214 name of the scans, for multiple scans this needs to be a template
215 string
216 scannumbers : int, tuple or list
217 number of the scans of the reciprocal space map
218 args : str, optional
219 names of the motors. to read reciprocal space maps measured in coplanar
220 diffraction give:
221
222 - omname: name of the omega motor (or its equivalent)
223 - ttname: name of the two theta motor (or its equivalent)
224
225 kwargs : dict
226 keyword arguments forwarded to RASFile function
227
228 Returns
229 -------
230 [ang1, ang2, ...] : list
231 angular positions are extracted from the respective scan header, or
232 motor positions during the scan. this is omitted if no `args` are given
233 rasdata : ndarray
234 the data values (includes the intensities e.g. rasdata['int']).
235
236 Examples
237 --------
238 >>> [om, tt], MAP = xu.io.getras_scan('text%05d.ras', 36, 'Omega',
239 >>> 'TwoTheta')
240 """
241
242 if isinstance(scannumbers, (list, tuple)):
243 scanlist = scannumbers
244 else:
245 scanlist = list([scannumbers])
246
247 angles = dict.fromkeys(args)
248 for key in angles.keys():
249 if not isinstance(key, str):
250 raise InputError("*arg values need to be strings with motornames")
251 angles[key] = numpy.zeros(0)
252 buf = numpy.zeros(0)
253 MAP = numpy.zeros(0)
254
255 for nr in scanlist:
256 rasfile = RASFile(scanname % nr, **kwargs)
257 for scan in rasfile.scans:
258 sdata = scan.data
259 if MAP.dtype == numpy.float64:
260 MAP.dtype = sdata.dtype
261 # append scan data to MAP, where all data are stored
262 MAP = numpy.append(MAP, sdata)
263 # check type of scan
264 for i in range(len(args)):
265 motname = args[i]
266 scanlength = len(sdata)
267 try:
268 buf = sdata[motname]
269 except ValueError:
270 buf = scan.init_mopo[motname] * numpy.ones(scanlength)
271 angles[motname] = numpy.concatenate((angles[motname], buf))
272
273 retval = []
274 for motname in args:
275 # create return values in correct order
276 retval.append(angles[motname])
277
278 if not args:
279 return MAP
280 elif len(args) == 1:
281 return retval[0], MAP
282 else:
283 return retval, MAP
+0
-236
xrayutilities/io/rotanode_alignment.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 parser for the alignment log file of the rotating anode
19 """
20
21 import re
22
23 import numpy
24
25 from .. import config, utilities
26 from .helper import xu_open
27
28 LOG_comment = re.compile(r"^#C")
29 LOG_peakname = re.compile(r"^#P")
30 LOG_motorname = re.compile(r"^#M")
31 LOG_datetime = re.compile(r"^#D")
32 LOG_tagline = re.compile(r"^#")
33 # denotes a numeric value
34 LOG_num_value = re.compile(r"[+-]*\d*\.*\d*e*[+-]*\d+")
35
36
37 class RA_Alignment(object):
38
39 """
40 class to parse the data file created by the alignment routine
41 (tpalign) at the rotating anode spec installation
42
43 this routine does an iterative alignment procedure and saves the
44 center of mass values were it moves after each scan. It iterates
45 between two different peaks and iteratively aligns at each peak between
46 two different motors (om/chi at symmetric peaks, om/phi at asymmetric
47 peaks)
48 """
49
50 def __init__(self, filename):
51 """
52 initialization function to initialize the objects variables and
53 opens the file
54
55 Parameters
56 ----------
57 filename : str
58 filename of the alignment log file
59 """
60
61 self.filename = filename
62 try:
63 self.fid = xu_open(self.filename)
64 except OSError:
65 self.fid = None
66 raise IOError("error opening alignment log file %s"
67 % self.filename)
68
69 self.peaks = []
70 self.alignnames = []
71 self.motorpos = []
72 self.intensities = []
73 self.iterations = []
74
75 self.Parse()
76
77 def Parse(self):
78 """
79 parser to read the alignment log and obtain the aligned values
80 at every iteration.
81 """
82
83 currentpeakname = None
84 currentmotname = None
85 opencommenttag = False
86 dataline = False
87 iteration = 0
88
89 if self.fid is None:
90 raise Exception("RA_Alignment: file was not opened by "
91 "initialization!")
92
93 for line in self.fid.readlines():
94 # for loop to read every line in the file
95 line = line.decode('ascii')
96
97 # check for new tag in the current line
98 if LOG_tagline.match(line):
99 opencommenttag = False
100
101 if LOG_comment.match(line):
102 # comment line or block starts
103 opencommenttag = True
104 continue
105
106 elif LOG_datetime.match(line):
107 # data is so far ignored
108 continue
109
110 elif LOG_peakname.match(line):
111 # line with peak name found
112 pname = LOG_peakname.sub("", line)
113 pname = pname.strip()
114 # check if we found a new peakname
115 try:
116 self.peaks.index(pname)
117 except ValueError:
118 self.peaks.append(pname)
119 currentpeakname = pname # set current peak name
120 iteration += 1 # increment iteration counter
121
122 elif LOG_motorname.match(line):
123 # line with motorname is found
124 motname = LOG_motorname.sub("", line)
125 motname = motname.strip()
126 # check if a peakname is already set
127 if currentpeakname is None:
128 if config.VERBOSITY >= config.INFO_LOW:
129 print("RA_Alignment: Warning: a peakname should "
130 "be given before a motor data line")
131 currentpeakname = "somepeak"
132 currentmotname = currentpeakname + "_" + motname
133 # check if we found a new peak/motor name combination
134 try:
135 self.alignnames.index(currentmotname)
136 except ValueError:
137 # new peak/motor combination
138 self.alignnames.append(currentmotname)
139 # create necessary data structures
140 self.motorpos.append([])
141 self.intensities.append([])
142 self.iterations.append([])
143 # next line contains motor position and intensity
144 dataline = True
145 elif opencommenttag:
146 # ignore line because it is part of a comment block
147 continue
148
149 elif dataline:
150 # dataline with motorposition and intensity is found
151 line_list = LOG_num_value.findall(line)
152 idx = self.alignnames.index(currentmotname)
153 self.motorpos[idx].append(float(line_list[0]))
154 self.intensities[idx].append(float(line_list[1]))
155 self.iterations[idx].append(iteration)
156 dataline = False
157
158 # convert data to numpy array and combine position and intensity
159 self.data = []
160 for i, k in enumerate(self.keys()):
161 self.data.append(numpy.array((self.motorpos[i],
162 self.intensities[i],
163 self.iterations[i])))
164
165 def __str__(self):
166 """
167 returns a string describing the content of the alignment file
168 """
169 ostr = ""
170 ostr += "Peaknames: " + repr(self.peaks) + "\n"
171 ostr += "aligned values: " + repr(self.alignnames)
172 return ostr
173
174 def __del__(self):
175 try:
176 self.fid.close()
177 except AttributeError:
178 pass
179
180 def keys(self):
181 """
182 returns a list of keys for which aligned values were parsed
183 """
184 return self.alignnames
185
186 def get(self, key):
187 return self.__getitem__(key)
188
189 def __getitem__(self, key):
190 """
191 returns the values to the corresponding key
192 """
193 if key in self.alignnames:
194 i = self.alignnames.index(key)
195 return self.data[i]
196 else:
197 raise KeyError("RA_Alignment: unknown key given!")
198
199 def plot(self, pname):
200 """
201 function to plot the alignment history for a given peak
202
203 Parameters
204 ----------
205 pname : str
206 peakname for which the alignment should be plotted
207 """
208 flag, plt = utilities.import_matplotlib_pyplot('XU.io.RA_ALignment')
209 if not flag:
210 return
211
212 if pname not in self.peaks:
213 print("RA_Alignment.plot: error peakname not found!")
214 return
215
216 # get number aligned axis for the current peak
217 axnames = []
218 for k in self.keys():
219 if k.find(pname) >= 0:
220 axnames.append(k)
221
222 fig, ax = plt.subplots(nrows=len(axnames), sharex=True)
223
224 for an, axis in zip(axnames, ax):
225 d = self.get(an)
226 plt.sca(axis)
227 plt.plot(d[2], d[0], 'k.-')
228 plt.ylabel(re.sub(pname + "_", "", an))
229 twax = axis.twinx()
230 plt.plot(d[2], d[1], 'r.-')
231 plt.ylabel("Int (cps)", color='r')
232 plt.grid()
233
234 plt.xlabel("Peak iteration number")
235 plt.suptitle(pname)
+0
-341
xrayutilities/io/seifert.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2013 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 a set of routines to convert Seifert ASCII files to HDF5
20 in fact there exist two posibilities how the data is stored (depending on the
21 use detector):
22
23 1. as a simple line scan (using the point detector)
24 2. as a map using the PSD
25
26 In the first case the data ist stored
27 """
28
29 import itertools
30 import os.path
31 import re
32
33 import numpy
34
35 from .. import config
36 from .helper import xu_open
37
38 # define some regular expressions
39 nscans_re = re.compile(r"^&NumScans=\d+")
40 scan_data_re = re.compile(r"^\#Values\d+")
41 scan_partab_re = re.compile(r"^\#ScanTableParameter")
42 novalues_re = re.compile(r"^&NoValues=\d+")
43 scanaxis_re = re.compile(r"^&ScanAxis=.*")
44
45 # some constant regular expressions
46 re_measparams = re.compile(r"#MeasParameter")
47 re_rsmparams = re.compile(r"#RsmParameter")
48 re_data = re.compile(r"#Values")
49 re_keyvalue = re.compile(r"&\S+=\S")
50 re_invalidkeyword = re.compile(r"^\d+\S")
51 re_multiblank = re.compile(r"\s+")
52 re_position = re.compile(r"&Pos=[+-]*\d*\.\d*")
53 re_start = re.compile(r"&Start=[+-]*\d*\.\d*")
54 re_end = re.compile(r"&End=[+-]*\d*\.\d*")
55 re_step = re.compile(r"&Step=[+-]*\d*\.\d*")
56 re_time = re.compile(r"&Time=\d*.\d*")
57 re_stepscan = re.compile(r"^&Start")
58 re_dataline = re.compile(r"^[+-]*\d*\.\d*")
59 re_absorber = re.compile(r"^&Axis=A6")
60
61
62 def repair_key(key):
63 """
64 Repair a key string in the sense that the string is changed in a way that
65 it can be used as a valid Python identifier. For that purpose all blanks
66 within the string will be replaced by _ and leading numbers get an
67 preceeding _.
68 """
69
70 if re_invalidkeyword.match(key):
71 key = "_" + key
72
73 # now replace all blanks
74 key = key.replace(" ", "_")
75
76 return key
77
78
79 class SeifertHeader(object):
80 """
81 helper class to represent a Seifert (NJA) scan file header
82 """
83
84 def __init__(self):
85 pass
86
87 def __str__(self):
88 ostr = ""
89 for k in self.__dict__.keys():
90 value = self.__getattribute__(k)
91 if isinstance(value, float):
92 ostr += k + " = %f\n" % value
93 else:
94 ostr += k + " = %s\n" % value
95
96 return ostr
97
98
99 class SeifertMultiScan(object):
100 """
101 Class to parse a Seifert (NJA) multiscan file
102 """
103
104 def __init__(self, filename, m_scan, m2, path=""):
105 """
106 Parse data from a multiscan Seifert file.
107
108 Parameters
109 ----------
110 filename : str
111 name of the NJA file
112 m_scan : str
113 name of the scan axis
114 m2 : str
115 name of the second moving motor
116 path : str, optional
117 path to the datafile
118 """
119 self.Filename = os.path.join(path, filename)
120
121 self.nscans = 0 # total number of scans
122 self.npscan = 0 # number of points per scan
123 self.ctime = 0 # counting time
124 self.re_m2 = re.compile(r"^&Axis=%s\s+&Task=Drive" % m2)
125 self.re_sm = re.compile(r"^&ScanAxis=%s" % m_scan)
126 self.scan_motor_name = m_scan
127 self.sec_motor_name = m2
128
129 self.m2_pos = []
130 self.sm_pos = []
131 self.data = []
132 self.n_sm_pos = 0
133
134 with xu_open(self.Filename) as self.fid:
135 if config.VERBOSITY >= config.INFO_LOW:
136 print("XU.io.SeifertScan: parsing file: %s" % self.Filename)
137 self.parse()
138
139 def parse(self):
140 self.data = []
141 m2_tmppos = None
142 self.sm_pos = []
143 self.m2_pos = []
144
145 # flag to check if all header information was parsed
146 header_complete = False
147
148 for line in self.fid:
149 lb = line.decode('ascii').strip()
150
151 # the first thing needed is the number of scans in the file (in
152 # file header)
153 if nscans_re.match(lb):
154 t = lb.split("=")[1]
155 self.nscans = int(t)
156
157 if self.re_m2.match(lb):
158 t = re_position.findall(lb)[0]
159 t = t.split("=")[1]
160 m2_tmppos = float(t)
161
162 if novalues_re.match(lb):
163 t = lb.split("=")[1]
164 self.n_sm_pos = int(t)
165 header_complete = True
166
167 if header_complete:
168 # append motor positions of second motor
169 self.m2_pos.append([[m2_tmppos] * self.n_sm_pos])
170
171 # reset header flag
172 header_complete = False
173 # read data lines (number of lines determined by number of
174 # values)
175 datalines = itertools.islice(self.fid, self.n_sm_pos)
176 t = numpy.loadtxt(datalines)
177 self.data.append(t[:, 1])
178 self.sm_pos.append(t[:, 0])
179
180 # after reading all the data
181 self.m2_pos = numpy.array(self.m2_pos, dtype=numpy.double)
182 self.sm_pos = numpy.array(self.sm_pos, dtype=numpy.double)
183 self.data = numpy.array(self.data, dtype=numpy.double)
184
185 self.data.shape = (self.nscans, self.n_sm_pos)
186 self.m2_pos.shape = (self.nscans, self.n_sm_pos)
187 self.sm_pos.shape = (self.nscans, self.n_sm_pos)
188
189
190 class SeifertScan(object):
191 """
192 Class to parse a single Seifert (NJA) scan file
193 """
194
195 def __init__(self, filename, path=""):
196 """
197 Constructor for a SeifertScan object.
198
199 Parameters
200 ----------
201 filename : str
202 a string with the name of the file to read
203 path : str, optional
204 path to the datafile
205 """
206 self.Filename = os.path.join(path, filename)
207
208 self.hdr = SeifertHeader()
209 self.data = []
210 self.axispos = {}
211
212 with xu_open(self.Filename) as self.fid:
213 if config.VERBOSITY >= config.INFO_LOW:
214 print("XU.io.SeifertScan: parsing file: %s" % self.Filename)
215 self.parse()
216
217 if self.hdr.NumScans != 1:
218 self.data.shape = (int(self.data.shape[0] / self.hdr.NoValues),
219 int(self.hdr.NoValues), 2)
220
221 def parse(self):
222 if config.VERBOSITY >= config.INFO_ALL:
223 print("XU.io.SeifertScan.parse: starting the parser")
224 self.data = []
225 for line in self.fid:
226 lb = line.decode('ascii')
227 # remove leading and trailing whitespace and newline characeters
228 lb = lb.strip()
229
230 # every line is broken into its content
231 llist = re_multiblank.split(lb)
232 tmplist = []
233 axes = ""
234 for e in llist:
235 # if the entry is a key value pair
236 if re_keyvalue.match(e):
237 (key, value) = e.split("=")
238 # remove leading & from the key
239 key = key[1:]
240 # have to manage malformed key names that cannot be used as
241 # Python identifiers (leading numbers or blanks inside the
242 # name)
243 key = repair_key(key)
244
245 # try to convert the values to float numbers
246 # leave them as strings if this is not possible
247 try:
248 value = float(value)
249 except ValueError:
250 pass
251
252 if key == "Axis":
253 axes = value
254 if value not in self.axispos:
255 self.axispos[value] = []
256 elif key == "Pos":
257 self.axispos[axes] += [value, ]
258
259 self.hdr.__setattr__(key, value)
260 else:
261 try:
262 tmplist.append(float(e))
263 except ValueError:
264 pass
265
266 if tmplist != []:
267 self.data.append(tmplist)
268
269 # in the end we convert the data list to a numeric array
270 self.data = numpy.array(self.data, dtype=numpy.float)
271 for key in self.axispos:
272 self.axispos[key] = numpy.array(self.axispos[key])
273
274
275 def getSeifert_map(filetemplate, scannrs=None, path=".", scantype="map",
276 Nchannels=1280):
277 """
278 parses multiple Seifert ``*.nja`` files and concatenates the results. for
279 parsing the xrayutilities.io.SeifertMultiScan class is used. The function
280 can be used for parsing maps measured with the Meteor1D and point detector.
281
282 Parameters
283 ----------
284 filetemplate : str
285 template string for the file names, can contain a %d which is replaced
286 by the scan number or be a list of filenames
287 scannrs : int or list, optional
288 scan number(s)
289 path : str, optional
290 common path to the filenames
291 scantype : {'map', 'tsk'}, optional
292 type of datafile: can be either 'map' (reciprocal space map measured
293 with a regular Seifert job (default)) or 'tsk' (MCA spectra measured
294 using the TaskInterpreter)
295 Nchannels : int, optional
296 number of channels of the MCA (needed for 'tsk' measurements)
297
298 Returns
299 -------
300 om, tt, psd : ndarray
301 positions and data as flattened numpy arrays
302
303 Examples
304 --------
305 >>> om, tt, psd = xrayutilities.io.getSeifert_map("samplename_%d.xrdml",
306 >>> [1, 2], path="./data")
307 """
308 # read raw data and convert to reciprocal space
309 om = numpy.zeros(0)
310 tt = numpy.zeros(0)
311 if scantype == "map":
312 psd = numpy.zeros(0)
313 else:
314 psd = numpy.zeros((0, Nchannels))
315 # create scan names
316 if scannrs is None:
317 files = [filetemplate]
318 else:
319 files = list()
320 if not getattr(scannrs, '__iter__', False):
321 scannrs = [scannrs]
322 for nr in scannrs:
323 files.append(filetemplate % nr)
324
325 # parse files
326 for f in files:
327 if scantype == "map":
328 d = SeifertMultiScan(os.path.join(path, f), 'T', 'O')
329
330 om = numpy.concatenate((om, d.m2_pos.flatten()))
331 tt = numpy.concatenate((tt, d.sm_pos.flatten()))
332 psd = numpy.concatenate((psd, d.data.flatten()))
333 else: # scantype == "tsk":
334 d = SeifertScan(os.path.join(path, f))
335
336 om = numpy.concatenate((om, d.axispos['O'].flatten()))
337 tt = numpy.concatenate((tt, d.axispos['T'].flatten()))
338 psd = numpy.concatenate((psd, d.data[:, :, 1]))
339
340 return om, tt, psd
+0
-1190
xrayutilities/io/spec.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 a class for observing a SPEC data file
20
21 Motivation:
22
23 SPEC files can become quite large. Therefore, subsequently reading
24 the entire file to extract a single scan is a quite cumbersome procedure.
25 This module is a proof of concept code to write a file observer starting
26 a reread of the file starting from a stored offset (last known scan position)
27 """
28
29 import os.path
30 import re
31
32 import numpy
33
34 from .. import config, utilities
35 from ..exception import InputError
36 # relative imports from xrayutilities
37 from .helper import xu_h5open, xu_open
38
39 # define some uesfull regular expressions
40 SPEC_time_format = re.compile(r"\d\d:\d\d:\d\d")
41 SPEC_multi_blank = re.compile(r"\s+")
42 SPEC_multi_blank2 = re.compile(r"\s\s+")
43 # denotes a numeric value
44 SPEC_int_value = re.compile(r"[+-]?\d+")
45 SPEC_num_value = re.compile(
46 r"([+-]?\d*\.*\d*[eE]*[+-]*\d+|[+-]?[Ii][Nn][Ff]|[Nn][Aa][Nn])")
47 SPEC_dataline = re.compile(r"^[+-]*\d.*")
48
49 SPEC_scan = re.compile(r"^#S")
50 SPEC_initmoponames = re.compile(r"#O\d+")
51 SPEC_initmopopos = re.compile(r"#P\d+")
52 SPEC_datetime = re.compile(r"^#D")
53 SPEC_exptime = re.compile(r"^#T")
54 SPEC_nofcols = re.compile(r"^#N")
55 SPEC_colnames = re.compile(r"^#L")
56 SPEC_MCAFormat = re.compile(r"^#@MCA")
57 SPEC_MCAChannels = re.compile(r"^#@CHANN")
58 SPEC_headerline = re.compile(r"^#")
59 SPEC_scanbroken = re.compile(r"#C[a-zA-Z0-9: .]*Scan aborted")
60 SPEC_scanresumed = re.compile(r"#C[a-zA-Z0-9: .]*Scan resumed")
61 SPEC_commentline = re.compile(r"#C")
62 SPEC_newheader = re.compile(r"^#E")
63 SPEC_errorbm20 = re.compile(r"^MI:")
64 scan_status_flags = ["OK", "NODATA", "ABORTED", "CORRUPTED"]
65
66
67 class SPECScan(object):
68 """
69 Represents a single SPEC scan. This class is usually not called by the
70 user directly but used via the SPECFile class.
71 """
72
73 def __init__(self, name, scannr, command, date, time, itime, colnames,
74 hoffset, doffset, fname, imopnames, imopvalues, scan_status):
75 """
76 Constructor for the SPECScan class.
77
78 Parameters
79 ----------
80 name : str
81 name of the scan
82 scannr : int
83 Number of the scan in the specfile
84 command : str
85 command used to write the scan
86 date : str
87 starting date of the scan
88 time : str
89 starting time of the scan
90 itime : int
91 integration time
92 colnames : list
93 list of names of the data columns
94 hoffset : int
95 file byte offset to the header of the scan
96 doffset : int
97 file byte offset to the data section of the scan
98 fname : str
99 file name of the SPEC file the scan belongs to
100 imopnames : list of str
101 motor names for the initial motor positions array
102 imopvalues : list
103 intial motor positions array
104 scan_status : {'OK', 'NODATA', 'CORRUPTED', 'ABORTED'}
105 scan status as string
106 """
107 self.name = name # name of the scan
108 self.nr = scannr # number of the scan
109 self.command = command # command used to record the data
110 self.date = date # date the command has been sent
111 self.time = time # time the command has been sent
112 self.colnames = colnames # list with column names
113 self.hoffset = hoffset # file offset where the header data starts
114 self.doffset = doffset # file offset where the data section starts
115 self.fname = fname # full file name of the file holding the data
116 # flag to force resave to hdf5 file in Save2HDF5()
117 self.ischanged = True
118 self.header = []
119 self.fid = None
120
121 if scan_status in scan_status_flags:
122 self.scan_status = scan_status
123 else:
124 self.scan_status = "CORRUPTED"
125 if config.VERBOSITY >= config.INFO_ALL:
126 print("XU.io.spec.SPECScan: unknown scan status flag - "
127 "set to CORRUPTED")
128
129 # setup the initial motor positions dictionary - set the motor names
130 # dictionary holding the initial motor positions
131 self.init_motor_pos = {}
132 if len(imopnames) == len(imopvalues):
133 for i in range(len(imopnames)):
134 natmotname = utilities.makeNaturalName(imopnames[i])
135 self.init_motor_pos[
136 "INIT_MOPO_" + natmotname] = float(imopvalues[i])
137 else:
138 print("XU.io.spec.SPECScan: Warning: incorrect number of "
139 "initial motor positions in scan %03d" % (self.nr))
140 if config.VERBOSITY >= config.INFO_ALL:
141 print(imopnames)
142 print(imopvalues)
143 # ASSUME ORDER DID NOT CHANGE!! (which might be wrong)
144 # in fact this is sign for a broken spec file
145 # number of initial motor positions should not change without new
146 # file header!
147 for i in range(min(len(imopnames), len(imopvalues))):
148 natmotname = utilities.makeNaturalName(imopnames[i])
149 self.init_motor_pos[
150 "INIT_MOPO_" + natmotname] = float(imopvalues[i])
151 # read the rest of the positions into dummy INIT_MOPO__NONAME__%03d
152 for i in range(len(imopnames), len(imopvalues)):
153 self.init_motor_pos["INIT_MOPO___NONAME__%03d" % (i)] = \
154 float(imopvalues[i])
155
156 # some additional attributes for the MCA data
157 # False if scan contains no MCA data, True otherwise
158 self.has_mca = False
159 self.mca_column_format = 0 # number of columns used to save MCA data
160 self.mca_channels = 0 # number of channels stored from the MCA
161 self.mca_nof_lines = 0 # number of lines used to store MCA data
162 self.mca_start_channel = 0 # first channel of the MCA that is stored
163 self.mca_stop_channel = 0 # last channel of the MCA that is stored
164
165 # a numpy record array holding the data - this is set using by the
166 # ReadData method.
167 self.data = None
168
169 # check for duplicate values in column names
170 for i in range(len(self.colnames)):
171 name = self.colnames[i]
172 cnt = self.colnames.count(name)
173 if cnt > 1:
174 # have multiple entries
175 cnt = 1
176 for j in range(self.colnames.index(name) + 1,
177 len(self.colnames)):
178 if self.colnames[j] == name:
179 self.colnames[j] = name + "_%i" % cnt
180 cnt += 1
181
182 def SetMCAParams(self, mca_column_format, mca_channels,
183 mca_start, mca_stop):
184 """
185 Set the parameters used to save the MCA data to the file. This method
186 calculates the number of lines used to store the MCA data from the
187 number of columns and the
188
189 Parameters
190 ----------
191 mca_column_format : int
192 number of columns used to save the data
193 mca_channels : int
194 number of MCA channels stored
195 mca_start : int
196 first channel that is stored
197 mca_stop : int
198 last channel that is stored
199 """
200 self.has_mca = True
201 self.mca_column_format = mca_column_format
202 self.mca_channels = mca_channels
203 self.mca_start_channel = mca_start
204 self.mca_stop_channel = mca_stop
205
206 # calculate the number of lines per data point for the mca
207 self.mca_nof_lines = int(mca_channels / mca_column_format)
208 if mca_channels % mca_column_format != 0:
209 # some additional values have to be read
210 self.mca_nof_lines = self.mca_nof_lines + 1
211
212 if config.VERBOSITY >= config.DEBUG:
213 print("XU.io.SPECScan.SetMCAParams: number of channels: %d"
214 % self.mca_channels)
215 print("XU.io.SPECScan.SetMCAParams: number of columns: %d"
216 % self.mca_column_format)
217 print("XU.io.SPECScan.SetMCAParams: number of lines to read "
218 "for MCA: %d" % self.mca_nof_lines)
219
220 def __str__(self):
221 # build a proper string to print the scan information
222 str_rep = "|%4i|" % (self.nr)
223 str_rep = str_rep + "%50s|%10s|%10s|" % (self.command, self.time,
224 self.date)
225
226 if self.has_mca:
227 str_rep = str_rep + "MCA: %5i" % (self.mca_channels)
228
229 str_rep = str_rep + "\n"
230 return str_rep
231
232 def ClearData(self):
233 """
234 Delete the data stored in a scan after it is no longer
235 used.
236 """
237
238 self.__delattr__("data")
239 self.data = None
240
241 def ReadData(self):
242 """
243 Set the data attribute of the scan class.
244 """
245
246 if self.scan_status == "NODATA":
247 if config.VERBOSITY >= config.INFO_LOW:
248 print("XU.io.SPECScan.ReadData: %s has been aborted - "
249 "no data available!" % self.name)
250 self.data = None
251 return None
252
253 if not self.has_mca:
254 if config.VERBOSITY >= config.INFO_ALL:
255 print("XU.io.SPECScan.ReadData: scan %d contains no MCA data"
256 % self.nr)
257
258 with xu_open(self.fname) as self.fid:
259 # read header lines
260 self.fid.seek(self.hoffset, 0)
261 self.header = []
262 while self.fid.tell() < self.doffset:
263 line = self.fid.readline().decode('ascii', 'ignore')
264 self.header.append(line.strip())
265
266 self.fid.seek(self.doffset, 0)
267
268 # create dictionary to hold the data
269 if self.has_mca:
270 type_desc = {"names": self.colnames + ["MCA"],
271 "formats": len(self.colnames) * [numpy.float32] +
272 [(numpy.uint32, self.mca_channels)]}
273 else:
274 type_desc = {"names": self.colnames,
275 "formats": len(self.colnames) * [numpy.float32]}
276
277 if config.VERBOSITY >= config.DEBUG:
278 print("xu.io.SPECScan.ReadData: type descriptor: %s"
279 % (repr(type_desc)))
280
281 record_list = [] # from this list the record array while be built
282
283 mca_counter = 0
284 scan_aborted_flag = False
285
286 for line in self.fid:
287 line = line.decode('ascii', 'ignore')
288 line = line.strip()
289 if not line:
290 continue
291
292 # check if scan is broken
293 if (SPEC_scanbroken.findall(line) != [] or
294 scan_aborted_flag):
295 # need to check next line(s) to know if scan is resumed
296 # read until end of comment block or end of file
297 if not scan_aborted_flag:
298 scan_aborted_flag = True
299 self.scan_status = "ABORTED"
300 if config.VERBOSITY >= config.INFO_ALL:
301 print("XU.io.SPECScan.ReadData: %s aborted"
302 % self.name)
303 continue
304 elif SPEC_scanresumed.match(line):
305 self.scan_status = "OK"
306 scan_aborted_flag = False
307 if config.VERBOSITY >= config.INFO_ALL:
308 print("XU.io.SPECScan.ReadData: %s resumed"
309 % self.name)
310 continue
311 elif SPEC_commentline.match(line):
312 continue
313 elif SPEC_errorbm20.match(line):
314 print(line)
315 continue
316 else:
317 break
318
319 if SPEC_headerline.match(line) or \
320 SPEC_commentline.match(line):
321 if SPEC_scanresumed.match(line):
322 continue
323 elif SPEC_commentline.match(line):
324 continue
325 else:
326 break
327
328 if mca_counter == 0:
329 # the line is a scalar data line
330 line_list = SPEC_num_value.findall(line)
331 if config.VERBOSITY >= config.DEBUG:
332 print("XU.io.SPECScan.ReadData: %s" % line)
333 print("XU.io.SPECScan.ReadData: read scalar values %s"
334 % repr(line_list))
335 # convert strings to numbers
336 line_list = map(float, line_list)
337
338 # increment the MCA counter if MCA data is stored
339 if self.has_mca:
340 mca_counter = mca_counter + 1
341 # create a temporary list for the mca data
342 mca_tmp_list = []
343 else:
344 record_list.append(tuple(line_list))
345 else:
346 # reading MCA spectrum
347 mca_tmp_list += map(int, SPEC_int_value.findall(line))
348
349 # increment MCA counter
350 mca_counter = mca_counter + 1
351 # if mca_counter exceeds the number of lines used to store
352 # MCA data: append everything to the record list
353 if mca_counter > self.mca_nof_lines:
354 record_list.append(tuple(list(line_list) +
355 [mca_tmp_list]))
356 mca_counter = 0
357
358 # convert the data to numpy arrays
359 ncol = len(record_list[0])
360 if config.VERBOSITY >= config.INFO_LOW:
361 print("XU.io.SPECScan.ReadData: %s: %d %d %d"
362 % (self.name, len(record_list), ncol,
363 len(type_desc["names"])))
364 if ncol == len(type_desc["names"]):
365 try:
366 self.data = numpy.rec.fromrecords(record_list,
367 dtype=type_desc)
368 except ValueError:
369 self.scan_status = 'NODATA'
370 print("XU.io.SPECScan.ReadData: %s exception while "
371 "parsing data" % self.name)
372 else:
373 self.scan_status = 'NODATA'
374
375 def plot(self, *args, **keyargs):
376 """
377 Plot scan data to a matplotlib figure. If newfig=True a new
378 figure instance will be created. If logy=True (default is False)
379 the y-axis will be plotted with a logarithmic scale.
380
381 Parameters
382 ----------
383 args : list
384 arguments for the plot: first argument is the name of x-value
385 column the following pairs of arguments are the y-value names and
386 plot styles allowed are 3, 5, 7,... number of arguments
387 keyargs : dict, optional
388 newfig : bool, optional
389 if True a new figure instance will be created otherwise an existing
390 one will be used
391 logy : bool, optional
392 if True a semilogy plot will be done
393 """
394 flag, plt = utilities.import_matplotlib_pyplot('XU.io.SPECScan')
395 if not flag:
396 return
397
398 newfig = keyargs.get('newfig', True)
399 logy = keyargs.get('logy', False)
400
401 try:
402 xname = args[0]
403 xdata = self.data[xname]
404 except ValueError:
405 raise InputError("name of the x-axis is invalid!")
406
407 alist = args[1:]
408 leglist = []
409
410 if len(alist) % 2 != 0:
411 raise InputError("wrong number of yname/style arguments!")
412
413 if newfig:
414 plt.figure()
415 plt.subplots_adjust(left=0.08, right=0.95)
416
417 for i in range(0, len(alist), 2):
418 yname = alist[i]
419 ystyle = alist[i + 1]
420 try:
421 ydata = self.data[yname]
422 except ValueError:
423 raise InputError("no column with name %s exists!" % yname)
424 continue
425 if logy:
426 plt.semilogy(xdata, ydata, ystyle)
427 else:
428 plt.plot(xdata, ydata, ystyle)
429
430 leglist.append(yname)
431
432 plt.xlabel("%s" % xname)
433 plt.legend(leglist)
434 plt.title("scan %i %s\n%s %s"
435 % (self.nr, self.command, self.date, self.time))
436 # need to adjust axis limits properly
437 lim = plt.axis()
438 plt.axis([xdata.min(), xdata.max(), lim[2], lim[3]])
439
440 def Save2HDF5(self, h5f, group="/", title="", optattrs={}, comp=True):
441 """
442 Save a SPEC scan to an HDF5 file. The method creates a group with the
443 name of the scan and stores the data there as a table object with name
444 "data". By default the scan group is created under the root group of
445 the HDF5 file. The title of the scan group is ususally the scan
446 command. Metadata of the scan are stored as attributes to the scan
447 group. Additional custom attributes to the scan group can be passed as
448 a dictionary via the optattrs keyword argument.
449
450 Parameters
451 ----------
452 h5f : file-handle or str
453 a HDF5 file object or its filename
454
455 group : str, optional
456 name or group object of the HDF5 group where to store the data
457 title : str, optional
458 a string with the title for the data, defaults to the name of scan
459 if empty
460 optattrs : dict, optional
461 a dictionary with optional attributes to store for the data
462 comp : bool, optional
463 activate compression - true by default
464 """
465
466 with xu_h5open(h5f, 'a') as h5:
467 # check if data object has been already written
468 if self.data is None:
469 raise InputError("XU.io.SPECScan.Save2HDF5: No data has been"
470 "read so far - call ReadData method of the "
471 "scan")
472 return None
473
474 # parse keyword arguments:
475 if isinstance(group, str):
476 rootgroup = h5.get(group)
477 else:
478 rootgroup = group
479
480 if title != "":
481 group_title = title
482 else:
483 group_title = self.name
484 group_title = group_title.replace(".", "_")
485
486 # create the dataset and fill it
487 copy_count = 0
488 if self.ischanged and group_title in rootgroup:
489 del rootgroup[group_title]
490 raw_grp_title = group_title
491 # if the group already exists the name must be changed and
492 # another will be made to create the group.
493 while group_title in rootgroup:
494 group_title = raw_grp_title + "_%i" % (copy_count)
495 copy_count = copy_count + 1
496 g = rootgroup.create_group(group_title)
497
498 kwds = {'fletcher32': True}
499 if comp:
500 kwds['compression'] = 'gzip'
501
502 dset = g.create_dataset("data", data=self.data, **kwds)
503
504 # write attribute data for the scan
505 g.attrs['ScanNumber'] = numpy.uint(self.nr)
506 g.attrs['Command'] = self.command
507 g.attrs['Date'] = self.date
508 g.attrs['Time'] = self.time
509 g.attrs['scan_status'] = self.scan_status
510
511 # write the initial motor positions as attributes
512 for k in self.init_motor_pos.keys():
513 g.attrs[k] = numpy.float(self.init_motor_pos[k])
514
515 # if scan contains MCA data write also MCA parameters
516 g.attrs['has_mca'] = self.has_mca
517 g.attrs['mca_start_channel'] = numpy.uint(self.mca_start_channel)
518 g.attrs['mca_stop_channel'] = numpy.uint(self.mca_stop_channel)
519 g.attrs['mca_nof_channels'] = numpy.uint(self.mca_channels)
520
521 for k in optattrs:
522 g.attrs[k] = optattrs[k]
523
524 h5.flush()
525
526 def getheader_element(self, key, firstonly=True):
527 """
528 return the value-string of the first appearance of this SPECScan's
529 header element, or a list of all values if firstonly=False
530
531 Parameters
532 ----------
533 specscan : SPECScan
534 key : str
535 name of the key to return; e.g. 'UMONO' or 'D'
536 firstonly : bool, optional
537 flag to specify if all instances or only the first one should be
538 returned
539
540 Returns
541 -------
542 valuestring : str
543 header value (if firstonly=True)
544 [str1, str2, ...] : list
545 header values (if firstonly=False)
546 """
547 if not self.header:
548 self.ReadData()
549 re_key = re.compile(r'^#%s (.*)' % key)
550 ret = []
551 for line in self.header:
552 m = re_key.match(line)
553 if m:
554 if firstonly:
555 ret = m.groups()[0]
556 break
557 else:
558 ret.append(m.groups()[0])
559 return ret
560
561
562 class SPECFile(object):
563
564 """
565 This class represents a single SPEC file. The class provides
566 methodes for updateing an already opened file which makes it particular
567 interesting for interactive use.
568 """
569
570 def __init__(self, filename, path=""):
571 """
572 SPECFile init routine
573
574 Parameters
575 ----------
576 filename : str
577 filename of the spec file
578 path : str, optional
579 path to the specfile
580 """
581 self.full_filename = os.path.join(path, filename)
582 self.filename = os.path.basename(self.full_filename)
583
584 # list holding scan objects
585 self.scan_list = []
586 self.fid = None
587 self.last_offset = 0
588
589 # initially parse the file
590 self.init_motor_names_fh = [] # this list will hold the names of the
591 # motors saved in initial motor positions given in the file header
592 self.init_motor_names_sh = [] # this list will hold the names of the
593 # motors saved in initial motor positions given in the scan header
594 self.init_motor_names = [] # this list will hold the names of the
595 # motors saved in initial motor positions from either the file or
596 # scan header
597
598 self.Parse()
599
600 def __getitem__(self, index):
601 """
602 function to return the n-th scan in the spec-file. be aware that
603 numbering starts at 0! If scans are missing the relation between the
604 given number and the "number" of the returned scan might be not
605 trivial.
606
607 See also
608 --------
609 scanI
610 attributes of the SPECFile object, where 'I' is the scan number
611 """
612 return self.scan_list[index]
613
614 def __getattr__(self, name):
615 """
616 return scanX objects where X stands for the scan number in the SPECFile
617 which for this purpose is assumed to be unique. (otherwise the first
618 instance of scan number X is returned)
619 """
620 if name.startswith("scan"):
621 index = name[4:]
622
623 try:
624 scannr = int(index)
625 except ValueError:
626 raise AttributeError("scannumber needs to be convertable to "
627 "integer")
628
629 # try to find the scan in the list of scans
630 s = None
631 for scan in self.scan_list:
632 if scan.nr == scannr:
633 s = scan
634 break
635
636 if s is not None:
637 return s
638 else:
639 raise AttributeError("requested scan-number not found")
640 else:
641 raise AttributeError("SPECFile has no attribute '%s'" % name)
642
643 def __len__(self):
644 return self.scan_list.__len__()
645
646 def __str__(self):
647 ostr = ""
648 for i in range(len(self.scan_list)):
649 ostr = ostr + "%5i" % (i)
650 ostr = ostr + self.scan_list[i].__str__()
651
652 return ostr
653
654 def Save2HDF5(self, h5f, comp=True, optattrs={}):
655 """
656 Save the entire file in an HDF5 file. For that purpose a group is set
657 up in the root group of the file with the name of the file without
658 extension and leading path. If the method is called after an previous
659 update only the scans not written to the file meanwhile are saved.
660
661 Parameters
662 ----------
663 h5f : file-handle or str
664 a HDF5 file object or its filename
665 comp : bool, optional
666 activate compression - true by default
667 """
668 with xu_h5open(h5f, 'a') as h5:
669 groupname = os.path.splitext(os.path.splitext(self.filename)[0])[0]
670 try:
671 g = h5.create_group(groupname)
672 except ValueError:
673 g = h5.get(groupname)
674
675 g.attrs['TITLE'] = "Data of SPEC - File %s" % (self.filename)
676 for k in optattrs:
677 g.attrs[k] = optattrs[k]
678 for s in self.scan_list:
679 if (((s.name not in g) or s.ischanged) and
680 s.scan_status != "NODATA"):
681 s.ReadData()
682 if s.data is not None:
683 s.Save2HDF5(h5, group=g, comp=comp)
684 s.ClearData()
685 s.ischanged = False
686
687 def Update(self):
688 """
689 reread the file and add newly added files. The parsing starts at the
690 data offset of the last scan gathered during the last parsing run.
691 """
692
693 # reparse the SPEC file
694 if config.VERBOSITY >= config.INFO_LOW:
695 print("XU.io.SPECFile.Update: reparsing file for new scans ...")
696 # mark last found scan as not saved to force reread
697 idx = len(self.scan_list)
698 if idx > 0:
699 lastscan = self.scan_list[idx - 1]
700 lastscan.ischanged = True
701 self.Parse()
702
703 def Parse(self):
704 """
705 Parses the file from the starting at last_offset and adding found scans
706 to the scan list.
707 """
708 with xu_open(self.full_filename) as self.fid:
709 # move to the last read position in the file
710 self.fid.seek(self.last_offset, 0)
711 scan_started = False
712 scan_has_mca = False
713 # list with the motors from whome the initial
714 # position is stored.
715 init_motor_values = []
716
717 if config.VERBOSITY >= config.DEBUG:
718 print('XU.io.SPECFile: start parsing')
719
720 for line in self.fid:
721 linelength = len(line)
722 line = line.decode('ascii', 'ignore')
723 if config.VERBOSITY >= config.DEBUG:
724 print('parsing line: %s' % line)
725
726 # remove trailing and leading blanks from the read line
727 line = line.strip()
728
729 # fill the list with the initial motor names in the header
730 if SPEC_newheader.match(line):
731 self.init_motor_names_fh = []
732
733 elif SPEC_initmoponames.match(line) and not scan_started:
734 if config.VERBOSITY >= config.DEBUG:
735 print("XU.io.SPECFile.Parse: found initial motor "
736 "names in file header")
737 line = SPEC_initmoponames.sub("", line)
738 line = line.strip()
739 self.init_motor_names_fh = self.init_motor_names_fh + \
740 SPEC_multi_blank2.split(line)
741
742 # if the line marks the beginning of a new scan
743 elif SPEC_scan.match(line) and not scan_started:
744 if config.VERBOSITY >= config.DEBUG:
745 print("XU.io.SPECFile.Parse: found scan")
746 line_list = SPEC_multi_blank.split(line)
747 scannr = int(line_list[1])
748 scancmd = "".join(" " + x + " " for x in line_list[2:])
749 scan_started = True
750 scan_has_mca = False
751 scan_header_offset = self.last_offset
752 scan_status = "OK"
753 # define some necessary variables which could be missing in
754 # the scan header
755 itime = numpy.nan
756 time = ''
757 if config.VERBOSITY >= config.INFO_ALL:
758 print("XU.io.SPECFile.Parse: processing scan nr. %d "
759 "..." % scannr)
760 # set the init_motor_names to the ones found in
761 # the file header
762 self.init_motor_names_sh = []
763 self.init_motor_names = self.init_motor_names_fh
764
765 # if the line contains the date and time information
766 elif SPEC_datetime.match(line) and scan_started:
767 if config.VERBOSITY >= config.DEBUG:
768 print("XU.io.SPECFile.Parse: found date and time")
769 # fetch the time from the line data
770 time = SPEC_time_format.findall(line)[0]
771 line = SPEC_time_format.sub("", line)
772 line = SPEC_datetime.sub("", line)
773 date = SPEC_multi_blank.sub(" ", line).strip()
774
775 # if the line contains the integration time
776 elif SPEC_exptime.match(line) and scan_started:
777 if config.VERBOSITY >= config.DEBUG:
778 print("XU.io.SPECFile.Parse: found exposure time")
779 itime = float(SPEC_num_value.findall(line)[0])
780 # read the initial motor names in the scan header if present
781 elif SPEC_initmoponames.match(line) and scan_started:
782 if config.VERBOSITY >= config.DEBUG:
783 print("XU.io.SPECFile.Parse: found initial motor "
784 "names in scan header")
785 line = SPEC_initmoponames.sub("", line)
786 line = line.strip()
787 self.init_motor_names_sh = self.init_motor_names_sh + \
788 SPEC_multi_blank2.split(line)
789 self.init_motor_names = self.init_motor_names_sh
790 # read the initial motor positions
791 elif SPEC_initmopopos.match(line) and scan_started:
792 if config.VERBOSITY >= config.DEBUG:
793 print("XU.io.SPECFile.Parse: found initial motor "
794 "positions")
795 line = SPEC_initmopopos.sub("", line)
796 line = line.strip()
797 line_list = SPEC_multi_blank.split(line)
798 # sometimes initial motor position are simply empty and
799 # this should not lead to an error
800 try:
801 for value in line_list:
802 init_motor_values.append(float(value))
803 except ValueError:
804 pass
805
806 # if the line contains the number of colunmns
807 elif SPEC_nofcols.match(line) and scan_started:
808 if config.VERBOSITY >= config.DEBUG:
809 print("XU.io.SPECFile.Parse: found number of columns")
810 line = SPEC_nofcols.sub("", line)
811 line = line.strip()
812 nofcols = int(line)
813
814 # if the line contains the column names
815 elif SPEC_colnames.match(line) and scan_started:
816 if config.VERBOSITY >= config.DEBUG:
817 print("XU.io.SPECFile.Parse: found column names")
818 line = SPEC_colnames.sub("", line)
819 line = line.strip()
820 col_names = SPEC_multi_blank.split(line)
821
822 # this is a fix in the case that blanks are allowed in
823 # motor and detector names (only a single balanks is
824 # supported meanwhile)
825 if len(col_names) > nofcols:
826 col_names = SPEC_multi_blank2.split(line)
827
828 elif SPEC_MCAFormat.match(line) and scan_started:
829 mca_col_number = int(SPEC_num_value.findall(
830 line)[0])
831 scan_has_mca = True
832
833 elif SPEC_MCAChannels.match(line) and scan_started:
834 line_list = SPEC_num_value.findall(line)
835 mca_channels = int(line_list[0])
836 mca_start = int(line_list[1])
837 mca_stop = int(line_list[2])
838
839 elif (SPEC_scanbroken.findall(line) != [] and
840 scan_started):
841 # this is the case when a scan is broken and no data has
842 # been written, but nevertheless a comment is in the file
843 # that tells us that the scan was aborted
844 scan_data_offset = self.last_offset
845 s = SPECScan("scan_%i" % (scannr), scannr, scancmd,
846 date, time, itime, col_names,
847 scan_header_offset, scan_data_offset,
848 self.full_filename, self.init_motor_names,
849 init_motor_values, "NODATA")
850
851 self.scan_list.append(s)
852
853 # reset control flags
854 scan_started = False
855 scan_has_mca = False
856 # reset initial motor positions flag
857 init_motor_values = []
858
859 elif SPEC_dataline.match(line) and scan_started:
860 # this is now the real end of the header block. at this
861 # point we know that there is enough information about the
862 # scan
863
864 # save the data offset
865 scan_data_offset = self.last_offset
866
867 # create an SPECFile scan object and add it to the scan
868 # list the name of the group consists of the prefix scan
869 # and the number of the scan in the file - this shoule make
870 # it easier to find scans in the HDF5 file.
871 s = SPECScan("scan_%i" % (scannr), scannr, scancmd, date,
872 time, itime, col_names, scan_header_offset,
873 scan_data_offset, self.full_filename,
874 self.init_motor_names, init_motor_values,
875 scan_status)
876 if scan_has_mca:
877 s.SetMCAParams(mca_col_number, mca_channels, mca_start,
878 mca_stop)
879
880 self.scan_list.append(s)
881
882 # reset control flags
883 scan_started = False
884 scan_has_mca = False
885 # reset initial motor positions flag
886 init_motor_values = []
887
888 elif SPEC_scan.match(line) and scan_started:
889 # this should only be the case when there are two
890 # consecutive file headers in the data file without any
891 # data or abort notice of the first scan; first store
892 # current scan as aborted then start new scan parsing
893 s = SPECScan("scan_%i" % (scannr), scannr, scancmd,
894 date, time, itime, col_names,
895 scan_header_offset, None,
896 self.full_filename, self.init_motor_names,
897 init_motor_values, "NODATA")
898 self.scan_list.append(s)
899
900 # reset control flags
901 scan_started = False
902 scan_has_mca = False
903 # reset initial motor positions flag
904 init_motor_values = []
905
906 # start parsing of new scan
907 if config.VERBOSITY >= config.DEBUG:
908 print("XU.io.SPECFile.Parse: found scan "
909 "(after aborted scan)")
910 line_list = SPEC_multi_blank.split(line)
911 scannr = int(line_list[1])
912 scancmd = "".join(" " + x + " " for x in line_list[2:])
913 scan_started = True
914 scan_has_mca = False
915 scan_header_offset = self.last_offset
916 scan_status = "OK"
917 self.init_motor_names_sh = []
918 self.init_motor_names = self.init_motor_names_fh
919
920 # store the position of the file pointer
921 self.last_offset += linelength
922
923 # if reading of the file is finished store the data offset of the
924 # last scan as the last offset for the next parsing run of the file
925 self.last_offset = self.scan_list[-1].doffset
926
927
928 class SPECCmdLine(object):
929
930 def __init__(self, n, prompt, cmdl, out=""):
931 self.linenumber = n
932 self.prompt = prompt
933 self.command = cmdl
934 self.out = out
935
936 def __str__(self):
937 ostr = "%i.%s> %s" % (self.linenumber, self.prompt, self.command)
938 return ostr
939
940
941 class SPECLog(object):
942 """
943 class to parse a SPEC log file to find the command history
944 """
945
946 def __init__(self, filename, prompt, path=""):
947 """
948 init routine for a class to read a SPEC log file
949
950 Parameters
951 ----------
952 filename : str
953 SPEC log file name
954 prompt : str
955 SPEC command prompt (e.g. 'PSIC' or 'SPEC')
956 path : str, optional
957 directory where the SPEC log can be found
958 """
959 self.filename = filename
960 self.full_filename = os.path.join(path, self.filename)
961
962 self.prompt = prompt
963 self.prompt_re = re.compile(r"%s>" % self.prompt)
964
965 self.cmdl_list = []
966 self.line_counter = 0
967 self.Parse()
968
969 def Parse(self):
970 with xu_open(self.full_filename, 'r') as fid:
971 for line in fid:
972 line = line.decode('ascii', 'ignore')
973 self.line_counter += 1
974
975 line = line.strip()
976 if self.prompt_re.findall(line):
977 [line, cmd] = self.prompt_re.split(line)
978 self.cmdl_list.append(SPECCmdLine(int(float(line)),
979 self.prompt, cmd))
980
981 def __getitem__(self, index):
982 """
983 function to return the n-th cmd in the spec-log.
984 """
985 return self.cmdl_list[index]
986
987 def __str__(self):
988 ostr = "%s with %d lines\n" % (self.filename, self.line_counter)
989
990 for cmd in self.cmdl_list:
991 ostr = ostr + cmd.__str__() + "\n"
992
993 return ostr
994
995
996 def geth5_scan(h5f, scans, *args, **kwargs):
997 """
998 function to obtain the angular cooridinates as well as intensity values
999 saved in an HDF5 file, which was created from a spec file by the Save2HDF5
1000 method. Especially useful for reciprocal space map measurements.
1001
1002 further more it is possible to obtain even more positions from
1003 the data file if more than two string arguments with its names are given
1004
1005 Parameters
1006 ----------
1007 h5f : file-handle or str
1008 file object of a HDF5 file opened using h5py or its filename
1009 scans : int, tuple or list
1010 number of the scans of the reciprocal space map
1011 args : str, optional
1012 names of the motors. to read reciprocal space maps measured in coplanar
1013 diffraction give:
1014
1015 - omname: name of the omega motor (or its equivalent)
1016 - ttname: name of the two theta motor (or its equivalent)
1017
1018 kwargs : dict, optional
1019 samplename: str, optional
1020 string with the hdf5-group containing the scan data if ommited the
1021 first child node of h5f.root will be used
1022 rettype: {'list', 'numpy'}, optional
1023 how to return motor positions. by default a list of arrays is returned.
1024 when rettype == 'numpy' a record array will be returned.
1025
1026 Returns
1027 -------
1028 [ang1, ang2, ...] : list
1029 angular positions of the center channel of the position sensitive
1030 detector (numpy.ndarray 1D), this list is omitted if no `args` are
1031 given
1032 MAP : ndarray
1033 the data values as stored in the data file (includes the intensities
1034 e.g. MAP['MCA']).
1035
1036 Examples
1037 --------
1038 >>> [om, tt], MAP = xu.io.geth5_scan(h5file, 36, 'omega', 'gamma')
1039 """
1040
1041 with xu_h5open(h5f) as h5:
1042 gname = kwargs.get("samplename", list(h5.keys())[0])
1043 h5g = h5.get(gname)
1044
1045 if numpy.iterable(scans):
1046 scanlist = scans
1047 else:
1048 scanlist = list([scans])
1049
1050 angles = dict.fromkeys(args)
1051 for key in angles.keys():
1052 if not isinstance(key, str):
1053 raise InputError("*arg values need to be strings with "
1054 "motornames")
1055 angles[key] = numpy.zeros(0)
1056 buf = numpy.zeros(0)
1057 MAP = numpy.zeros(0)
1058
1059 for nr in scanlist:
1060 h5scan = h5g.get("scan_%d" % nr)
1061 sdata = numpy.asarray(h5scan.get('data'))
1062 if MAP.dtype == numpy.float64:
1063 MAP.dtype = sdata.dtype
1064 # append scan data to MAP, where all data are stored
1065 MAP = numpy.append(MAP, sdata)
1066 # check type of scan
1067 notscanmotors = []
1068 for i in range(len(args)):
1069 motname = args[i]
1070 try:
1071 buf = sdata[motname]
1072 scanshape = buf.shape
1073 angles[motname] = numpy.concatenate((angles[motname], buf))
1074 except ValueError:
1075 notscanmotors.append(i)
1076 if len(notscanmotors) == len(args):
1077 scanshape = len(sdata)
1078 for i in notscanmotors:
1079 motname = args[i]
1080 natmotname = utilities.makeNaturalName(motname)
1081 buf = numpy.ones(scanshape) * \
1082 h5scan.attrs["INIT_MOPO_%s" % natmotname]
1083 angles[motname] = numpy.concatenate((angles[motname], buf))
1084
1085 # create return values in correct order
1086 def create_retval():
1087 retval = []
1088 for motname in args:
1089 retval.append(angles[motname])
1090 return retval
1091
1092 rettype = kwargs.get('rettype', 'list')
1093 if rettype == 'numpy':
1094 retval = numpy.core.records.fromarrays([angles[m] for m in args],
1095 names=args)
1096 else:
1097 retval = create_retval()
1098
1099 if not args:
1100 return MAP
1101 else:
1102 return retval, MAP
1103
1104
1105 def getspec_scan(specf, scans, *args, **kwargs):
1106 """
1107 function to obtain the angular cooridinates as well as intensity values
1108 saved in a SPECFile. Especially useful to combine the data from multiple
1109 scans.
1110
1111 further more it is possible to obtain even more positions from
1112 the data file if more than two string arguments with its names are given
1113
1114 Parameters
1115 ----------
1116 specf : SPECFile
1117 file object
1118 scans : int, tuple or list
1119 number of the scans
1120 args : str
1121 names of the motors and counters
1122 rettype : {'list', 'numpy'}, optional
1123 how to return motor positions. by default a list of arrays is returned.
1124 when rettype == 'numpy' a record array will be returned.
1125
1126 Returns
1127 -------
1128 [ang1, ang2, ...] : list
1129 coordinates and counters from the SPEC file
1130
1131 Examples
1132 --------
1133 >>> [om, tt, cnt2] = xu.io.getspec_scan(s, 36, 'omega', 'gamma',
1134 >>> 'Counter2')
1135 """
1136 if not args:
1137 return
1138
1139 if numpy.iterable(scans):
1140 scanlist = scans
1141 else:
1142 scanlist = list([scans])
1143
1144 angles = dict.fromkeys(args)
1145 for key in angles.keys():
1146 if not isinstance(key, str):
1147 raise InputError("*arg values need to be strings with "
1148 "motornames")
1149 angles[key] = numpy.zeros(0)
1150 buf = numpy.zeros(0)
1151
1152 for nr in scanlist:
1153 sscan = specf.__getattr__("scan%d" % nr)
1154 sscan.ReadData()
1155 sdata = sscan.data
1156 # check type of scan
1157 notscanmotors = []
1158 for i in range(len(args)):
1159 motname = args[i]
1160 try:
1161 buf = sdata[motname]
1162 scanshape = buf.shape
1163 angles[motname] = numpy.concatenate((angles[motname], buf))
1164 except ValueError:
1165 notscanmotors.append(i)
1166 if len(notscanmotors) == len(args):
1167 scanshape = len(sdata)
1168 for i in notscanmotors:
1169 motname = args[i]
1170 buf = (numpy.ones(scanshape) *
1171 sscan.init_motor_pos["INIT_MOPO_%s"
1172 % utilities.makeNaturalName(motname)])
1173 angles[motname] = numpy.concatenate((angles[motname], buf))
1174
1175 # create return values in correct order
1176 def create_retval():
1177 retval = []
1178 for motname in args:
1179 retval.append(angles[motname])
1180 return retval
1181
1182 rettype = kwargs.get('rettype', 'list')
1183 if rettype == 'numpy':
1184 retval = numpy.core.records.fromarrays([angles[m] for m in args],
1185 names=args)
1186 else:
1187 retval = create_retval()
1188
1189 return retval
+0
-610
xrayutilities/io/spectra.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2010 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2015 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 module to handle spectra data
20 """
21
22 import glob
23 import re
24
25 import numpy
26 import numpy.lib.recfunctions
27 from numpy import rec
28
29 from .. import config
30 from .helper import xu_h5open
31
32 re_wspaces = re.compile(r"\s+")
33 re_colname = re.compile(r"^Col")
34
35 re_comment_section = re.compile(r"^%c")
36 re_parameter_section = re.compile(r"^%p")
37 re_data_section = re.compile(r"^%d")
38 re_end_section = re.compile(r"^!")
39 re_unit = re.compile(r"\[.+\]")
40 re_obracket = re.compile(r"\[")
41 re_cbracket = re.compile(r"\]")
42 re_underscore = re.compile(r"_")
43 re_column = re.compile(r"^Col")
44 re_col_name = re.compile(r"\d+\s+.+\s*\[")
45 re_col_index = re.compile(r"\d+\s+")
46 re_col_type = re.compile(r"\[.+\]")
47 re_num = re.compile(r"[0-9]")
48
49 dtype_map = {"FLOAT": "f4",
50 "DOUBLE": "f8"}
51
52
53 class SPECTRAFileComments(dict):
54 """
55 Class that describes the comments in the header of a SPECTRA file.
56 The different comments are accessible via the comment keys.
57 """
58
59 def __init__(self):
60 pass
61
62 def __getattr__(self, name):
63 if name in self:
64 return self[name]
65
66
67 class SPECTRAFileParameters(dict):
68
69 def __init__(self):
70 pass
71
72 def __getattr__(self, name):
73 if name in self:
74 return self[name]
75
76 def __str__(self):
77 ostr = ""
78 n = len(self.keys())
79 lmax_key = 0
80 lmax_item = 0
81 strlist = []
82
83 # find the length of the longest key
84 for k in self.keys():
85 if len(k) > lmax_key:
86 lmax_key = len(k)
87
88 i = self[k]
89 if not isinstance(i, str):
90 # if the item is not a string it must be converted
91 i = "%f" % i
92
93 if len(i) > lmax_item:
94 lmax_item = len(i)
95
96 # define the format string for a single key-value pair
97 kvfmt = "|%%-%is = %%-%is" % (lmax_key, lmax_item)
98
99 nc = 3
100 nres = len(self.keys()) % nc
101 nrow = (len(self.keys()) - nres) / nc
102
103 cnt = 0
104 ostr += (3 * (lmax_key + lmax_item + 4) + 1) * "-" + "\n"
105 ostr += "|Parameters:" + (3 * (lmax_key + lmax_item)) * " " + "|\n"
106 ostr += (3 * (lmax_key + lmax_item + 4) + 1) * "-" + "\n"
107 for key in self.keys():
108 value = self[key]
109 if not isinstance(value, str):
110 value = "%f" % value
111
112 ostr += kvfmt % (key, value)
113 cnt += 1
114 if cnt == 3:
115 ostr += "|\n"
116 cnt = 0
117
118 if cnt != 0:
119 ostr += "|\n"
120 ostr += (3 * (lmax_key + lmax_item + 4) + 1) * "-" + "\n"
121
122 return ostr
123
124
125 class SPECTRAFileDataColumn(object):
126
127 def __init__(self, index, name, unit, type):
128 self.index = int(index)
129 self.name = name
130 self.unit = unit
131 self.type = type
132
133 def __str__(self):
134 ostr = "%i %s %s %s" % (self.index, self.name, self.unit, self.type)
135 return ostr
136
137
138 class SPECTRAFileData(object):
139
140 def __init__(self):
141 self.collist = []
142 self.data = None
143
144 def append(self, col):
145 self.collist.append(col)
146
147 def __getitem__(self, key):
148 try:
149 return self.data[key]
150 except IndexError:
151 print("XU.io.specta.SPECTRAFileData: data contains no column "
152 "named: %s!" % key)
153
154 def __str__(self):
155 ostr = ""
156
157 # determine the maximum lenght of every column string
158 lmax = 0
159 for c in self.collist:
160 if len(c.__str__()) > lmax:
161 lmax = len(c.__str__())
162
163 lmax += 3
164
165 # want to print in three columns
166 nc = 3
167 nres = len(self.collist) % nc
168 nrows = (len(self.collist) - nres) / nc
169
170 fmtstr = "| %%-%is| %%-%is| %%-%is|\n" % (lmax, lmax, lmax)
171
172 ostr += (3 * lmax + 7) * "-" + "\n"
173 ostr += "|Column names:" + (3 * lmax - 8) * " " + "|\n"
174 ostr += (3 * lmax + 7) * "-" + "\n"
175 # full output rows
176 for i in range(nrows):
177 c1 = self.collist[i * nc + 0]
178 c2 = self.collist[i * nc + 1]
179 c3 = self.collist[i * nc + 2]
180 ostr += fmtstr % (c1.__str__(), c2.__str__(), c3.__str__())
181
182 # residual output row
183 c = ['', '', '']
184 for j in range(nres):
185 c[j] = self.collist[-nres + j]
186
187 ostr += fmtstr % (c[0].__str__(), c[1].__str__(), c[2].__str__())
188
189 ostr += (3 * lmax + 7) * "-" + "\n"
190 return ostr
191
192
193 class SPECTRAFile(object):
194
195 """
196 Represents a SPECTRA data file. The file is read during the
197 Constructor call. This class should work for data stored at
198 beamlines P08 and BW2 at HASYLAB.
199
200 Parameters
201 ----------
202 filename : str
203 a string with the name of the SPECTRA file
204
205 mcatmp : str, optional
206 template for the MCA files
207 mcastart, mcastop : int, optional
208 start and stop index for the MCA files, if not given, the class tries
209 to determine the start and stop index automatically.
210 """
211
212 def __init__(self, filename, mcatmp=None, mcastart=None, mcastop=None):
213 self.filename = filename
214 self.comments = SPECTRAFileComments()
215 self.params = SPECTRAFileParameters()
216 self.data = SPECTRAFileData()
217 self.mca = None
218 self.mca_channels = None
219
220 self.Read() # reads the .fio data file
221
222 if mcatmp is not None:
223 self.mca_file_template = mcatmp
224
225 if mcastart is not None and mcastop is not None:
226 self.mca_start_index = mcastart
227 self.mca_stop_index = mcastop
228 else:
229 # try to determine the number of MCA spectra automatically
230 spat = self.mca_file_template.replace("%i", "*")
231 lst = glob.glob(spat)
232 self.mca_start_index = 1
233 self.mca_stop_index = 0
234 if lst:
235 self.mca_stop_index = self.data.data.size # len(l)
236
237 if self.mca_stop_index != 0:
238 self.ReadMCA()
239
240 def Save2HDF5(self, h5file, name, group="/", mcaname="MCA"):
241 """
242 Saves the scan to an HDF5 file. The scan is saved to a
243 seperate group of name "name". h5file is either a string
244 for the file name or a HDF5 file object.
245 If the mca attribute is not None mca data will be stored to an
246 chunked array of with name mcaname.
247
248 Parameters
249 ----------
250 h5file : file-handle or str
251 HDF5 file object or name
252 name : str
253 name of the group where to store the data
254
255 group : str, optional
256 root group where to store the data
257 mcaname : str, optional
258 Name of the MCA in the HDF5 file
259
260 Returns
261 -------
262 bool or None
263 The method returns None in the case of everything went fine, True
264 otherwise.
265 """
266 with xu_h5open(h5file, 'w') as h5:
267 # create the group where to store the data
268 try:
269 g = h5.create_group(group + '/' + name)
270 except ValueError:
271 print("XU.io.spectra.Save2HDF5: cannot create group %s for "
272 "writing data!" % name)
273 return True
274
275 # start with saving scan comments
276 for k in self.comments.keys():
277 try:
278 g.attrs[k] = self.comments[k]
279 except IndexError:
280 print("XU.io.spectra.Save2HDF5: cannot save file comment "
281 "%s = %s to group %s!" % (k, self.comments[k], name))
282
283 # save scan parameters
284 for k in self.params.keys():
285 try:
286 g.attrs[k] = self.params[k]
287 except IndexError:
288 print("XU.io.spectra.Save2HDF5: cannot save file parametes"
289 " %s to group %s!" % (k, name))
290
291 # ----------finally we need to save the data -------------------
292 kwds = {'fletcher32': True, 'compression': 'gzip'}
293
294 try:
295 dset = g.create_dataset("data", data=self.data.data, **kwds)
296 except (RuntimeError, ValueError):
297 print("XU.io.spectra.Save2HDF5: cannot create table for "
298 "storing scan data!")
299 return True
300
301 # if there is MCA data - store this
302 if self.mca is not None:
303 try:
304 c = g.create_dataset(mcaname, data=self.mca, **kwds)
305 except (RuntimeError, ValueError):
306 print("XU.io.spectra.Save2HDF5: cannot create carray %s "
307 "for MCA data!" % mcaname)
308 return True
309
310 # set MCA specific attributes
311 c.attrs["channels"] = self.mca_channels
312 c.attrs["nchannels"] = self.mca_channels.shape[0]
313
314 h5.flush()
315
316 return None
317
318 def ReadMCA(self):
319 dlist = []
320 for i in range(self.mca_start_index, self.mca_stop_index + 1):
321 fname = self.mca_file_template % i
322 data = numpy.loadtxt(fname)
323
324 if i == self.mca_start_index:
325 if len(data.shape) == 2:
326 self.mca_channels = data[:, 0]
327 else:
328 self.mca_channels = numpy.arange(0, data.shape[0])
329
330 if len(data.shape) == 2:
331 dlist.append(data[:, 1].tolist())
332 else:
333 dlist.append(data.tolist())
334
335 self.mca = numpy.array(dlist, dtype=float)
336
337 def __str__(self):
338 ostr = self.params.__str__()
339 ostr += self.data.__str__()
340
341 return ostr
342
343 def Read(self):
344 """
345 Read the data from the file.
346 """
347
348 def addkeyval(lst, k, v):
349 """
350 add new key to a list. if key already exists a number will be
351 appended to the key name
352
353 Parameters
354 ----------
355 lst : list
356 k : str
357 key
358 v : object
359 value
360 """
361 kcnt = 0
362 key = k
363 while key in lst:
364 key = k + "_%i" % (kcnt + 1)
365 kcnt += 1
366 lst[key] = v
367
368 col_names = []
369 col_units = []
370 col_types = []
371 rec_list = []
372 with open(self.filename, 'rb') as fid:
373 for line in fid:
374 line = line.decode('utf8', 'ignore')
375 line = line.strip()
376
377 # read the next line if the line starts with a "!"
378 if re_end_section.match(line):
379 continue
380
381 # select the which section to read
382 if re_comment_section.match(line):
383 read_mode = 1
384 continue
385
386 if re_parameter_section.match(line):
387 read_mode = 2
388 continue
389
390 if re_data_section.match(line):
391 read_mode = 3
392 continue
393
394 # here we decide how to proceed with the data
395 if read_mode == 1:
396 # read the file comments
397 try:
398 (key, value) = line.split("=")
399 except ValueError:
400 # avoid annoying output
401 if config.VERBOSITY >= config.INFO_ALL:
402 print("XU.io.SPECTRAFile.Read: cannot interpret "
403 "the comment string: %s" % (line))
404 continue
405
406 key = key.strip()
407 # remove whitespaces to be conform with natural naming
408 key = key.replace(' ', '')
409 key = key.replace(':', '_')
410 # remove possible number at first position
411 if re_num.findall(key[0]) != []:
412 key = "_" + key
413 value = value.strip()
414 if config.VERBOSITY >= config.DEBUG:
415 print("XU.io.SPECTRAFile.Read: comment: k, v: %s, %s"
416 % (key, value))
417
418 try:
419 value = float(value)
420 except ValueError:
421 pass
422
423 # need to handle the case, that a key may appear several
424 # times in the list
425 addkeyval(self.comments, key, value)
426
427 elif read_mode == 2:
428 # read scan parameters
429 try:
430 (key, value) = line.split("=")
431 except ValueError:
432 print("XU.io.SPECTRAFile.Read: cannot interpret the "
433 "parameter string: %s" % (line))
434
435 key = key.strip()
436 # remove whitespaces to be conform with natural naming
437 key = key.replace(' ', '')
438 key = key.replace(':', '_')
439 # remove possible number at first position
440 if re_num.findall(key[0]) != []:
441 key = "_" + key
442 value = value.strip()
443 if config.VERBOSITY >= config.DEBUG:
444 print("XU.io.SPECTRAFile.Read: parameter: k, v: %s, %s"
445 % (key, value))
446
447 try:
448 value = float(value)
449 except ValueError:
450 # if the conversion of the parameter to float
451 # fails it will be saved as a string
452 pass
453
454 # need to handle the case, that a key may appear several
455 # times in the list
456 addkeyval(self.params, key, value)
457
458 elif read_mode == 3:
459 if re_column.match(line):
460 try:
461 unit = re_unit.findall(line)[0]
462 except IndexError:
463 unit = "NONE"
464
465 try:
466 sline = re_obracket.split(line)
467 if len(sline) == 1:
468 raise IndexError
469 lval = sline[0]
470 rval = re_cbracket.split(line)[-1]
471 dtype = rval.strip()
472 lv = re_wspaces.split(lval)
473 index = int(lv[1])
474 name = "".join(lv[2:])
475 name = name.replace(':', '_')
476 except IndexError:
477 lv = re_wspaces.split(line)
478 index = int(lv[1])
479 dtype = lv[-1]
480 name = "".join(lv[2:-1])
481 name = name.replace(':', '_')
482
483 # store column definition
484 self.data.append(
485 SPECTRAFileDataColumn(index, name, unit, dtype))
486
487 if name in col_names:
488 name += "%s_1" % name
489 col_names.append("%s" % name)
490 col_types.append("%s" % (dtype_map[dtype]))
491
492 else:
493 # read data
494 dlist = re_wspaces.split(line)
495 for i in range(len(dlist)):
496 dlist[i] = float(dlist[i])
497
498 rec_list.append(tuple(dlist))
499
500 if config.VERBOSITY >= config.DEBUG:
501 print("XU.io.SPECTRAFile.Read: data columns: name, type: %s, %s"
502 % (col_names, col_types))
503 if rec_list:
504 self.data.data = rec.fromrecords(rec_list, formats=col_types,
505 names=col_names)
506 else:
507 self.data.data = None
508
509
510 def geth5_spectra_map(h5file, scans, *args, **kwargs):
511 """
512 function to obtain the omega and twotheta as well as intensity values
513 for a reciprocal space map saved in an HDF5 file, which was created
514 from a spectra file by the Save2HDF5 method.
515
516 further more it is possible to obtain even more positions from
517 the data file if more than two string arguments with its names are given
518
519 Parameters
520 ----------
521 h5f : file-handle or str
522 file object of a HDF5 file opened using h5py
523 scans : int, tuple or list
524 number of the scans of the reciprocal space map
525 args: str, optional
526 arbitrary number of motor names
527
528 - omname: name of the omega motor (or its equivalent)
529 - ttname: name of the two theta motor (or its equivalent)
530
531 kwargs : dict, optional
532 mca : str, optional
533 name of the mca data (if available) otherwise None (default: "MCA")
534 samplename : str, optional
535 string with the hdf5-group containing the scan data if omitted the
536 first child node of h5f.root will be used to determine the sample name
537
538 Returns
539 -------
540 [ang1, ang2, ...] : list
541 angular positions of the center channel of the position
542 sensitive detector (numpy.ndarray 1D). one entry for every
543 `args`-argument given to the function
544 MAP : ndarray
545 the data values as stored in the data file (includes the intensities
546 e.g. MAP['MCA']).
547 """
548
549 with xu_h5open(h5file) as h5:
550 mca = kwargs.get('mca', 'MCA')
551
552 if "samplename" in kwargs:
553 basename = kwargs["samplename"]
554 else:
555 nodename = list(h5.keys())[0]
556 basenlist = re_underscore.split(nodename)
557 basename = "_".join(basenlist[:-1])
558 if config.VERBOSITY >= config.DEBUG:
559 print("XU.io.spectra.geth5_spectra_map: using \'%s\' as "
560 "basename" % (basename))
561
562 if isinstance(scans, (list, tuple)):
563 scanlist = scans
564 else:
565 scanlist = list([scans])
566
567 angles = dict.fromkeys(args)
568 for key in angles.keys():
569 angles[key] = numpy.zeros(0)
570 buf = numpy.zeros(0)
571 MAP = numpy.zeros(0)
572
573 for nr in scanlist:
574 h5scan = h5.get(basename + "_%05d" % nr)
575 sdata = h5scan.get('data')
576 if mca:
577 mcanode = h5.get(basename + "_%05d/%s" % (nr, mca))
578 mcadata = numpy.asarray(mcanode)
579
580 # append scan data to MAP, where all data are stored
581 mcatemp = mcadata.view([(mca, (mcadata.dtype, mcadata.shape[1]))])
582 sdtmp = numpy.lib.recfunctions.merge_arrays([sdata, mcatemp],
583 flatten=True)
584 if MAP.dtype == numpy.float64:
585 MAP.dtype = sdtmp.dtype
586 MAP = numpy.append(MAP, sdtmp)
587
588 # check type of scan
589 notscanmotors = []
590 for i in range(len(args)):
591 motname = args[i]
592 try:
593 buf = sdata[motname]
594 scanshape = buf.shape
595 angles[motname] = numpy.concatenate((angles[motname], buf))
596 except ValueError:
597 notscanmotors.append(i)
598 for i in notscanmotors:
599 motname = args[i]
600 buf = numpy.ones(scanshape) * \
601 h5scan.attrs.get("%s" % motname)
602 angles[motname] = numpy.concatenate((angles[motname], buf))
603
604 retval = []
605 for motname in args:
606 # create return values in correct order
607 retval.append(angles[motname])
608
609 return retval, MAP
+0
-31
xrayutilities/materials/__init__.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2010-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 from . import elements
19 from .atom import Atom
20 from .cif import CIFFile, cifexport
21 from .database import (DataBase, add_f0_from_intertab, add_f0_from_xop,
22 add_f1f2_from_ascii_file, add_f1f2_from_henkedb,
23 add_f1f2_from_kissel, add_mass_from_NIST,
24 init_material_db)
25 from .material import (Alloy, Amorphous, Crystal, CubicAlloy,
26 CubicElasticTensor, HexagonalElasticTensor, Material,
27 PseudomorphicMaterial, WZTensorFromCub)
28 from .plot import show_reciprocal_space_plane
29 from .predefined_materials import *
30 from .spacegrouplattice import SGLattice
+0
-94
xrayutilities/materials/_create_database.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2012-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16 """
17 script to create the HDF5 database from the raw data of XOP
18 his file is only needed for administration and also used during the unit tests
19
20 Optionally it accepts two command line arguments. The first one is the filename
21 of the database. If the filename has no path or a relative path the data-folder
22 given as second argument will be used as root of the output file.
23
24 The second optional argument is the directory of the source data file used to
25 generate the database. If no path is given the 'data' sub-folder of the scripts
26 directory is used. The script therefore works if called from the extracted
27 tarball directory but not after installation. After installation the respective
28 data path must be specified. The script can be run with 0, one or two command
29 line arguments as described above.
30
31 See tests/test_materials_database for an example.
32 """
33
34 import lzma
35 import os.path
36 import sys
37
38 # local import
39 from database import (DataBase, add_color_from_JMOL, add_f0_from_intertab,
40 add_f1f2_from_kissel, add_mass_from_NIST,
41 add_radius_from_WIKI, init_material_db)
42
43 verbose = False
44
45 if len(sys.argv) > 1:
46 fname = sys.argv[1]
47 else:
48 fname = 'elements.db'
49 if len(sys.argv) > 2:
50 dataroot = sys.argv[2]
51 else:
52 dataroot = os.path.join(os.path.dirname(__file__), 'data')
53
54 fullfilename = os.path.join(dataroot, fname)
55
56 dbf = DataBase(fullfilename)
57 dbf.Create('elementdata',
58 'Database with elemental data from XOP and Kissel databases')
59
60 init_material_db(dbf)
61
62 # add a dummy element, this is useful not only for testing and should be
63 # kept in future! It can be used for structure factor calculation tests, and
64 # shows how the a database entry can be generated manually
65 dbf.SetMaterial('dummy')
66 dbf.SetF0([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) # atomic structure factors
67 dbf.SetF1F2((0, 1e5), (0, 0), (0, 0)) # zero dispersion correction
68
69 add_mass_from_NIST(dbf, os.path.join(dataroot, 'nist_atom.dat'), verbose)
70 add_color_from_JMOL(dbf, os.path.join(dataroot, 'colors.dat'), verbose)
71 add_radius_from_WIKI(dbf, os.path.join(dataroot, 'atomic_radius.dat'), verbose)
72
73 # add F0(Q) for every element
74 # with lzma.open(os.path.join('data', 'f0_xop.dat.xz'), 'r') as xop:
75 # add_f0_from_xop(dbf, xop, verbose)
76 with lzma.open(os.path.join(dataroot, 'f0_InterTables.dat.xz'), 'r') as itf:
77 add_f0_from_intertab(dbf, itf, verbose)
78
79 # add F1 and F2 from database
80 with lzma.open(os.path.join(dataroot, 'f1f2_asf_Kissel.dat.xz'), 'r') as kf:
81 add_f1f2_from_kissel(dbf, kf, verbose)
82 # with lzma.open(os.path.join(dataroot, 'f1f2_Henke.dat'), 'r') as hf:
83 # add_f1f2_from_henkedb(dbf, hf, verbose)
84
85 # Also its possible to add custom data from different databases; e.g.
86 # created by Hepaestus (http://bruceravel.github.io/demeter/). This is also
87 # possible for specific elements only, therefore extract the data from
88 # Hephaestus or any other source producing ASCII files with three columns
89 # (energy (eV), f1, f2). To import such data use:
90 # add_f1f2_from_ascii_file(dbf, os.path.join(dataroot, 'Ga.f1f2'), 'Ga',
91 # verbose)
92
93 dbf.Close()
+0
-197
xrayutilities/materials/atom.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module containing the Atom class which handles the database access for atomic
19 scattering factors and the atomic mass.
20 """
21 import hashlib
22 import os.path
23 import re
24
25 import numpy
26
27 from .. import config, utilities
28 from . import __path__, database
29
30 # python 2to3 compatibility
31 try:
32 basestring
33 except NameError:
34 basestring = str
35
36
37 _db = database.DataBase(os.path.join(__path__[0], "data", config.DBNAME))
38 _db.Open()
39
40
41 def get_key(*args):
42 """
43 generate a hash key for several possible types of arguments
44 """
45 tup = []
46 for a in args:
47 if isinstance(a, numpy.ndarray):
48 tup.append(hashlib.md5(a).digest())
49 elif isinstance(a, list):
50 tup.append(hash(tuple(a)))
51 else:
52 tup.append(hash(a))
53 return hash(tuple(tup))
54
55
56 class Atom(object):
57 max_cache_length = 1000
58
59 def __init__(self, name, num):
60 self.name = name
61 self.ostate = re.sub('[A-Za-z]', '', name)
62 for r, o in zip(('dot', 'p', 'm'), ('.', '+', '-')):
63 self.ostate = self.ostate.replace(o, r)
64
65 self.basename = re.sub('[^A-Za-z]', '', name)
66 self.num = num
67 self.__weight = None
68 self.__color = None
69 self.__radius = numpy.nan
70 self._dbcache = dict([(prop, []) for prop in ('f0', 'f1', 'f2', 'f')])
71
72 def __key__(self):
73 """ key function to return the elements number """
74 return self.num
75
76 def __lt__(self, other_el):
77 """ make elements sortable by their key """
78 return self.__key__() < other_el.__key__()
79
80 @property
81 def weight(self):
82 if not self.__weight:
83 _db.SetMaterial(self.basename)
84 self.__weight = _db.weight
85 return self.__weight
86
87 @property
88 def color(self):
89 if self.__color is None:
90 _db.SetMaterial(self.basename)
91 self.__color = _db.color
92 return self.__color
93
94 @property
95 def radius(self):
96 if self.__radius is numpy.nan:
97 _db.SetMaterial(self.basename)
98 self.__radius = _db.radius
99 return self.__radius
100
101 def get_cache(self, prop, key):
102 """
103 check if a cached value exists to speed up repeated database requests
104
105 Returns
106 -------
107 bool
108 True then result contains the cached otherwise False and result is
109 None
110 result : database value
111 """
112 history = self._dbcache[prop]
113 for idx, (k, result) in enumerate(history):
114 if k == key:
115 history.insert(0, history.pop(idx)) # move to front
116 return True, result
117 return False, None
118
119 def set_cache(self, prop, key, result):
120 """
121 set result to be cached to speed up future calls
122 """
123 history = self._dbcache[prop]
124 if len(history) == self.max_cache_length:
125 history.pop(-1)
126 history.insert(0, (key, result))
127
128 def f0(self, q):
129 key = get_key(q)
130 f, res = self.get_cache('f0', key)
131 if f:
132 return res
133 _db.SetMaterial(self.basename)
134 res = _db.GetF0(q, self.ostate)
135 self.set_cache('f0', key, res)
136 return res
137
138 def f1(self, en='config'):
139 key = get_key(en)
140 f, res = self.get_cache('f1', key)
141 if f:
142 return res
143 if isinstance(en, basestring) and en == 'config':
144 en = utilities.energy(config.ENERGY)
145
146 _db.SetMaterial(self.basename)
147 res = _db.GetF1(utilities.energy(en))
148 self.set_cache('f1', key, res)
149 return res
150
151 def f2(self, en='config'):
152 key = get_key(en)
153 f, res = self.get_cache('f2', key)
154 if f:
155 return res
156 if isinstance(en, basestring) and en == 'config':
157 en = utilities.energy(config.ENERGY)
158
159 _db.SetMaterial(self.basename)
160 res = _db.GetF2(utilities.energy(en))
161 self.set_cache('f2', key, res)
162 return res
163
164 def f(self, q, en='config'):
165 """
166 function to calculate the atomic structure factor F
167
168 Parameters
169 ----------
170 q : float, array-like
171 momentum transfer
172 en : float or str, optional
173 energy for which F should be calculated, if omitted the value from
174 the xrayutilities configuration is used
175
176 Returns
177 -------
178 float or array-like
179 value(s) of the atomic structure factor
180 """
181 key = get_key(q, en)
182 f, res = self.get_cache('f', key)
183 if f:
184 return res
185
186 res = self.f0(q) + self.f1(en) + 1.j * self.f2(en)
187 self.set_cache('f2', key, res)
188 return res
189
190 def __str__(self):
191 ostr = self.name
192 ostr += " (%2d)" % self.num
193 return ostr
194
195 def __repr__(self):
196 return self.__str__()
+0
-776
xrayutilities/materials/cif.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16 from __future__ import division
17
18 import copy
19 import io
20 import itertools
21 import operator
22 import os
23 import re
24 import shlex
25 import warnings
26
27 import numpy
28 import scipy.optimize
29
30 from .. import config
31 from . import elements
32 from . import spacegrouplattice as sgl
33 from . import wyckpos
34
35 # python 2to3 compatibility
36 try:
37 basestring
38 except NameError:
39 basestring = str
40
41 re_data = re.compile(r"^data_", re.IGNORECASE)
42 re_loop = re.compile(r"^loop_", re.IGNORECASE)
43 re_symop = re.compile(r"^\s*("
44 "_space_group_symop_operation_xyz|"
45 "_symmetry_equiv_pos_as_xyz)", re.IGNORECASE)
46 re_name = re.compile(r"^\s*_chemical_formula_sum", re.IGNORECASE)
47 re_atom = re.compile(r"^\s*_atom_site_label\s*$", re.IGNORECASE)
48 re_atomtyp = re.compile(r"^\s*_atom_site_type_symbol\s*$", re.IGNORECASE)
49 re_atomx = re.compile(r"^\s*_atom_site_fract_x", re.IGNORECASE)
50 re_atomy = re.compile(r"^\s*_atom_site_fract_y", re.IGNORECASE)
51 re_atomz = re.compile(r"^\s*_atom_site_fract_z", re.IGNORECASE)
52 re_uiso = re.compile(r"^\s*_atom_site_U_iso_or_equiv", re.IGNORECASE)
53 re_biso = re.compile(r"^\s*_atom_site_B_iso_or_equiv", re.IGNORECASE)
54 re_atomocc = re.compile(r"^\s*_atom_site_occupancy", re.IGNORECASE)
55 re_labelline = re.compile(r"^\s*_")
56 re_emptyline = re.compile(r"^\s*$")
57 re_quote = re.compile(r"'")
58 re_spacegroupnr = re.compile(r"^\s*(_space_group_IT_number|"
59 "_symmetry_Int_Tables_number)", re.IGNORECASE)
60 re_spacegroupname = re.compile(r"^\s*(_symmetry_space_group_name_H-M|"
61 "_space_group_name_H-M_alt)",
62 re.IGNORECASE)
63 re_spacegroupsetting = re.compile(r"^\s*_symmetry_cell_setting", re.IGNORECASE)
64 re_cell_a = re.compile(r"^\s*_cell_length_a", re.IGNORECASE)
65 re_cell_b = re.compile(r"^\s*_cell_length_b", re.IGNORECASE)
66 re_cell_c = re.compile(r"^\s*_cell_length_c", re.IGNORECASE)
67 re_cell_alpha = re.compile(r"^\s*_cell_angle_alpha", re.IGNORECASE)
68 re_cell_beta = re.compile(r"^\s*_cell_angle_beta", re.IGNORECASE)
69 re_cell_gamma = re.compile(r"^\s*_cell_angle_gamma", re.IGNORECASE)
70 re_comment = re.compile(r"^\s*#")
71
72
73 def testwp(parint, wp, cifpos, digits):
74 """
75 test if a Wyckoff position can describe the given position from a CIF file
76
77 Parameters
78 ----------
79 parint : int
80 telling which Parameters the given Wyckoff position has
81 wp : str or tuple
82 expression of the Wyckoff position
83 cifpos : list, or tuple or array-like
84 (x, y, z) position of the atom in the CIF file
85 digits : int
86 number of digits for which for a comparison of floating point numbers
87 will be rounded to
88
89 Returns
90 -------
91 foundflag : bool
92 flag to tell if the positions match
93 pars : array-like or None
94 parameters associated with the position or None if no parameters are
95 needed
96 """
97 def check_numbers_match(p1, p2, digits):
98 p1 = p1 - numpy.round(p1, digits) // 1
99 p2 = p2 - numpy.round(p2, digits) // 1
100 if numpy.round(p1, digits) == numpy.round(p2, digits):
101 return True
102 else:
103 return False
104
105 def get_pardict(parint, x):
106 i = 0
107 pardict = {}
108 if parint & 1:
109 pardict['x'] = x[i]
110 i += 1
111 if parint & 2:
112 pardict['y'] = x[i]
113 i += 1
114 if parint & 4:
115 pardict['z'] = x[i]
116 return pardict
117
118 wyckp = wp.strip('()').split(',')
119 # test agreement in positions witout variables
120 match = numpy.asarray([False, False, False])
121 variables = []
122 for i in range(3):
123 v = re.findall(r'[xyz]', wyckp[i])
124 if v == []:
125 pos = eval(wyckp[i])
126 match[i] = check_numbers_match(pos, cifpos[i], digits)
127 if not match[i]:
128 return False, None
129 else:
130 variables += v
131
132 if numpy.all(match):
133 return True, None
134
135 # check if with proper choice of the variables a correspondence of the
136 # positions can be obtained
137 def fmin(x, parint, wyckp, cifpos):
138 evalexp = []
139 cifp = []
140 for i in range(3):
141 if not match[i]:
142 evalexp.append(wyckp[i])
143 cifp.append(cifpos[i])
144 pardict = get_pardict(parint, x)
145 wpos = [eval(e, pardict) for e in evalexp]
146 return numpy.linalg.norm(numpy.asarray(wpos)-numpy.asarray(cifp))
147
148 x0 = []
149 if 'x' in variables:
150 x0.append(cifpos[0])
151 if 'y' in variables:
152 x0.append(cifpos[1])
153 if 'z' in variables:
154 x0.append(cifpos[2])
155
156 opt = scipy.optimize.minimize(fmin, x0, args=(parint, wyckp, cifpos))
157 pardict = get_pardict(parint, opt.x)
158 for i in range(3):
159 if not match[i]:
160 pos = eval(wyckp[i], pardict)
161 match[i] = check_numbers_match(pos, cifpos[i], digits)
162 if numpy.all(match):
163 return True, opt.x
164 else:
165 return False, None
166
167
168 class CIFFile(object):
169 """
170 class for parsing CIF (Crystallographic Information File) files. The class
171 aims to provide an additional way of creating material classes instead of
172 manual entering of the information the lattice constants and unit cell
173 structure are parsed from the CIF file.
174
175 If multiple datasets are present in the CIF file this class will attempt to
176 parse all of them into the the data dictionary. By default all methods
177 access the first data set found in the file.
178 """
179 def __init__(self, filestr, digits=3):
180 """
181 initialization of the CIFFile class
182
183 Parameters
184 ----------
185 filestr : str, bytes
186 CIF filename or string representation of the CIF file
187 digits : int, optional
188 number of digits to check if position is unique
189 """
190 self.digits = digits
191
192 if os.path.isfile(filestr):
193 self.filename = filestr
194 try:
195 self.fid = open(self.filename, "rb")
196 except OSError:
197 raise IOError("cannot open CIF file %s" % self.filename)
198 else:
199 if filestr.count('\n') == 0:
200 print('XU.material.CIFFile: "filestr" contains only one line '
201 'but a file with that name does not exist! Continuing '
202 'with the assumption this one line string is the '
203 'content of a CIF file!')
204 self.filename = '__from_str__'
205 if isinstance(filestr, bytes):
206 self.fid = io.BytesIO(filestr)
207 else:
208 self.fid = io.BytesIO(bytes(filestr.encode('ascii')))
209
210 if config.VERBOSITY >= config.INFO_ALL:
211 print('XU.material: parsing CIF file %s' % self.filename)
212 self._default_dataset = None
213 self.data = {}
214 self.Parse()
215
216 def __del__(self):
217 """
218 class destructor which closes open files
219 """
220 if self.fid is not None:
221 self.fid.close()
222
223 def Parse(self):
224 """
225 function to parse a CIF file. The function reads all the included data
226 sets and adds them to the data dictionary.
227
228 """
229 fidpos = self.fid.tell()
230 while True:
231 line = self.fid.readline()
232 if not line:
233 break
234 fidpos = self.fid.tell()
235 line = line.decode('ascii', 'ignore')
236 m = re_data.match(line)
237 if m:
238 self.fid.seek(fidpos)
239 name = line[m.end():].strip()
240 self.data[name] = CIFDataset(self.fid, name, self.digits)
241 if self.data[name].has_atoms and not self._default_dataset:
242 self._default_dataset = name
243
244 def SGLattice(self, dataset=None, use_p1=False):
245 """
246 create a SGLattice object with the structure from the CIF dataset
247
248 Parameters
249 ----------
250 dataset : str, optional
251 name of the dataset to use. if None the default one will be used.
252 use_p1 : bool, optional
253 force the use of P1 symmetry, default False
254 """
255 if not dataset:
256 dataset = self._default_dataset
257 return self.data[dataset].SGLattice(use_p1=use_p1)
258
259 def __str__(self):
260 """
261 returns a string with positions and names of the atoms for all datasets
262 """
263 ostr = ""
264 ostr += "CIF-File: %s\n" % self.filename
265 for ds in self.data:
266 ostr += "\nDataset: %s" % ds
267 if ds == self._default_dataset:
268 ostr += " (default)"
269 ostr += "\n"
270 ostr += str(self.data[ds])
271 return ostr
272
273
274 class CIFDataset(object):
275 """
276 class for parsing CIF (Crystallographic Information File) files. The class
277 aims to provide an additional way of creating material classes instead of
278 manual entering of the information the lattice constants and unit cell
279 structure are parsed from the CIF file
280 """
281
282 def __init__(self, fid, name, digits):
283 """
284 initialization of the CIFDataset class. This class parses one data
285 block.
286
287 Parameters
288 ----------
289 fid : filehandle
290 file handle set to the beginning of the data block to be parsed
291 name : str
292 identifier string of the dataset
293 digits : int
294 number of digits to check if position is unique
295 """
296 self.name = name
297 self.digits = digits
298 self.has_atoms = False
299
300 if config.VERBOSITY >= config.INFO_ALL:
301 print('XU.material: parsing cif dataset %s' % self.name)
302 self.Parse(fid)
303 self.SymStruct()
304
305 def Parse(self, fid):
306 """
307 function to parse a CIF data set. The function reads the
308 space group symmetry operations and the basic atom positions
309 as well as the lattice constants and unit cell angles
310 """
311
312 self.symops = []
313 self.atoms = []
314 self.lattice_const = numpy.zeros(3, dtype=numpy.double)
315 self.lattice_angles = numpy.zeros(3, dtype=numpy.double)
316
317 loop_start = False
318 symop_loop = False
319 atom_loop = False
320
321 def get_element(cifstring):
322 el = re.sub(r"['\"]", r"", cifstring)
323 if '+' in el or '-' in el:
324 for r, o in zip(('dot', 'p', 'm'), ('.', '+', '-')):
325 # move special character to last position
326 el = el.replace(o, r)
327 else:
328 el = re.sub(r"([0-9])", r"", el)
329 el = re.sub(r"\(\w*\)", r"", el)
330 try:
331 element = getattr(elements, el)
332 except AttributeError: # el not found, typ. due to oxidation state
333 f = re.search('[0-9]', el)
334 if not f and el == '?':
335 element = elements.dummy
336 else:
337 elname = el[:f.start()]
338 if hasattr(elements, elname):
339 # here one might want to find a closer alternative than
340 # the neutral atom, but the effect this has should be
341 # minimal, currently simply the neutral atom is used
342 if config.VERBOSITY >= config.INFO_LOW:
343 print('XU.material: element %s used instead of %s'
344 % (elname, cifstring))
345 element = getattr(elements, elname)
346 else:
347 raise ValueError('XU.material: element (%s) could not'
348 ' be found' % (cifstring))
349 return element
350
351 def floatconv(string):
352 """
353 helper function to convert string with possible error
354 given in brackets to float
355 """
356 try:
357 f = float(re.sub(r"\(.+\)", r"", string))
358 except ValueError:
359 f = numpy.nan
360 return f
361
362 def intconv(string):
363 """
364 helper function to convert string to integer
365 """
366 try:
367 i = int(string)
368 except ValueError:
369 i = None
370 return i
371
372 fidpos = fid.tell()
373 for line in fid.readlines():
374 linelen = len(line)
375 line = line.decode('ascii', 'ignore')
376 if config.VERBOSITY >= config.DEBUG:
377 print(line)
378 print(fid.tell(), fidpos)
379
380 if re_data.match(line):
381 fid.seek(fidpos)
382 break
383 fidpos += linelen
384
385 # ignore comment lines
386 if re_comment.match(line):
387 continue
388
389 if re_loop.match(line): # start of loop
390 if config.VERBOSITY >= config.DEBUG:
391 print('XU.material: loop start found')
392 loop_start = True
393 loop_labels = []
394 symop_loop = False
395 atom_loop = False
396 ax_idx = None
397 ay_idx = None
398 az_idx = None
399 uiso_idx = None
400 biso_idx = None
401 occ_idx = None
402 elif re_labelline.match(line):
403 if re_cell_a.match(line):
404 self.lattice_const[0] = floatconv(line.split()[1])
405 elif re_cell_b.match(line):
406 self.lattice_const[1] = floatconv(line.split()[1])
407 elif re_cell_c.match(line):
408 self.lattice_const[2] = floatconv(line.split()[1])
409 elif re_cell_alpha.match(line):
410 self.lattice_angles[0] = floatconv(line.split()[1])
411 elif re_cell_beta.match(line):
412 self.lattice_angles[1] = floatconv(line.split()[1])
413 elif re_cell_gamma.match(line):
414 self.lattice_angles[2] = floatconv(line.split()[1])
415 elif re_spacegroupnr.match(line):
416 i = intconv(line.split()[1])
417 if i:
418 self.sgrp_nr = i
419 elif re_spacegroupname.match(line):
420 self.sgrp_name = ''.join(line.split()[1:]).strip("'")
421 elif re_spacegroupsetting.match(line):
422 i = intconv(line.split()[1])
423 if i:
424 self.sgrp_setting = i
425 elif re_name.match(line):
426 try:
427 self.name = shlex.split(line)[1]
428 except IndexError:
429 self.name = None
430 if loop_start:
431 loop_labels.append(line.strip())
432 if re_symop.match(line): # start of symmetry op. loop
433 if config.VERBOSITY >= config.DEBUG:
434 print('XU.material: symop-loop identified')
435 symop_loop = True
436 symop_idx = len(loop_labels) - 1
437 elif re_atom.match(line) or re_atomtyp.match(line):
438 # start of atom position loop
439 if config.VERBOSITY >= config.DEBUG:
440 print('XU.material: atom position-loop identified')
441 atom_loop = True
442 if re_atomtyp.match(line):
443 alab_idx = len(loop_labels) - 1
444 elif not list(filter(re_atomtyp.match, loop_labels)):
445 # ensure precedence of atom_site_type_symbol
446 alab_idx = len(loop_labels) - 1
447 elif re_atomx.match(line):
448 ax_idx = len(loop_labels) - 1
449 if config.VERBOSITY >= config.DEBUG:
450 print('XU.material: atom position x: col%d'
451 % ax_idx)
452 elif re_atomy.match(line):
453 ay_idx = len(loop_labels) - 1
454 elif re_atomz.match(line):
455 az_idx = len(loop_labels) - 1
456 elif re_uiso.match(line):
457 uiso_idx = len(loop_labels) - 1
458 elif re_biso.match(line):
459 biso_idx = len(loop_labels) - 1
460 elif re_atomocc.match(line):
461 occ_idx = len(loop_labels) - 1
462
463 elif re_emptyline.match(line):
464 loop_start = False
465 symop_loop = False
466 atom_loop = False
467 continue
468 elif symop_loop: # symmetry operation entry
469 loop_start = False
470 entry = shlex.split(line)[symop_idx]
471 if re_quote.match(entry):
472 opstr = entry
473 else:
474 opstr = "'" + entry + "'"
475 opstr = re.sub(r"^'", r"(", opstr)
476 opstr = re.sub(r"'$", r")", opstr)
477 # add a comma to a fraction to avoid int division problems
478 opstr = re.sub(r"/([1-9])", r"/\1.", opstr)
479 self.symops.append(opstr)
480 elif atom_loop: # atom label and position
481 loop_start = False
482 asplit = line.split()
483 try:
484 atom = get_element(asplit[alab_idx])
485 apos = (floatconv(asplit[ax_idx]),
486 floatconv(asplit[ay_idx]),
487 floatconv(asplit[az_idx]))
488 occ = floatconv(asplit[occ_idx]) if occ_idx else 1
489 if numpy.isnan(occ):
490 occ = 1
491 uiso = floatconv(asplit[uiso_idx]) if uiso_idx else 0
492 biso = floatconv(asplit[biso_idx]) if biso_idx else 0
493 if numpy.isnan(uiso):
494 uiso = 0
495 if numpy.isnan(biso):
496 biso = 0
497 if biso == 0:
498 biso = 8 * numpy.pi**2 * uiso
499 self.atoms.append((atom, apos, occ, biso))
500 except IndexError:
501 if config.VERBOSITY >= config.INFO_LOW:
502 print('XU.material: could not parse atom line: "%s"'
503 % line.strip())
504 if self.atoms:
505 self.has_atoms = True
506
507 def SymStruct(self):
508 """
509 function to obtain the list of different atom positions in the unit
510 cell for the different types of atoms and determine the space group
511 number and origin choice if available. The data are obtained from the
512 data parsed from the CIF file.
513 """
514
515 def rem_white(string):
516 return string.replace(' ', '')
517
518 if hasattr(self, 'sgrp_name'):
519 # determine spacegroup
520 cifsgn = rem_white(self.sgrp_name).split(':')[0]
521 for nr, name in sgl.sgrp_name.items():
522 if cifsgn == rem_white(name):
523 self.sgrp_nr = int(nr)
524 if not hasattr(self, 'sgrp_nr'):
525 # try ignoring the minuses
526 for nr, name in sgl.sgrp_name.items():
527 if cifsgn == rem_white(name.replace('-', '')):
528 self.sgrp_nr = int(nr)
529 if len(self.sgrp_name.split(':')) > 1:
530 self.sgrp_suf = ':' + self.sgrp_name.split(':')[1]
531 elif hasattr(self, 'sgrp_setting'):
532 self.sgrp_suf = ':%d' % self.sgrp_setting
533
534 if not hasattr(self, 'sgrp_suf'):
535 if hasattr(self, 'sgrp_nr'):
536 self.sgrp_suf = sgl.get_possible_sgrp_suf(self.sgrp_nr)
537 else:
538 self.sgrp_suf = ''
539 if isinstance(self.sgrp_suf, basestring):
540 suffixes = [self.sgrp_suf, ]
541 else:
542 suffixes = copy.copy(self.sgrp_suf)
543 for sgrp_suf in suffixes:
544 self.sgrp_suf = sgrp_suf
545 if hasattr(self, 'sgrp_nr'):
546 self.sgrp = str(self.sgrp_nr) + self.sgrp_suf
547 allwyckp = wyckpos.wp[self.sgrp]
548 if config.VERBOSITY >= config.INFO_ALL:
549 print('XU.material: attempting space group %s' % self.sgrp)
550
551 # determine all unique positions for definition of a P1 space group
552 symops = self.symops
553 if not symops and hasattr(self, 'sgrp') and self.atoms:
554 label = sorted(allwyckp.keys(), key=lambda s: int(s[:-1]))
555 symops = allwyckp[label[-1]][1]
556 if config.VERBOSITY >= config.INFO_ALL:
557 print('XU.material: no symmetry operations in CIF-Dataset '
558 'using built in general positions.')
559 self.unique_positions = []
560 for el, (x, y, z), occ, biso in self.atoms:
561 unique_pos = []
562 for symop in symops:
563 pos = eval(symop, {'x': x, 'y': y, 'z': z})
564 pos = numpy.asarray(pos)
565 # check that position is within unit cell
566 pos = pos - numpy.round(pos, self.digits) // 1
567 # check if position is unique
568 unique = True
569 for upos in unique_pos:
570 if (numpy.round(upos, self.digits) ==
571 numpy.round(pos, self.digits)).all():
572 unique = False
573 if unique:
574 unique_pos.append(pos)
575 self.unique_positions.append((el, unique_pos, occ, biso))
576
577 # determine Wyckoff positions and free parameters of unit cell
578 if hasattr(self, 'sgrp'):
579 self.wp = []
580 self.occ = []
581 self.elements = []
582 self.biso = []
583 keys = list(allwyckp.keys())
584 wpn = [int(re.sub(r"([a-zA-Z])", r"", k)) for k in keys]
585 for i, (el, (x, y, z), occ, biso) in enumerate(self.atoms):
586 # candidate positions from number of unique atoms
587 natoms = len(self.unique_positions[i][1])
588 wpcand = []
589 for j, n in enumerate(wpn):
590 if n == natoms:
591 wpcand.append((keys[j], allwyckp[keys[j]]))
592 for j, (k, wp) in enumerate(
593 sorted(wpcand, key=operator.itemgetter(1))):
594 parint, poslist = wp
595 for positem in poslist:
596 foundwp, xyz = testwp(parint, positem,
597 (x, y, z), self.digits)
598 if foundwp:
599 if xyz is None:
600 self.wp.append(k)
601 else:
602 self.wp.append((k, list(xyz)))
603 self.elements.append(el)
604 self.occ.append(occ)
605 self.biso.append(biso)
606 break
607 if foundwp:
608 break
609 if config.VERBOSITY >= config.INFO_ALL:
610 print('XU.material: %d of %d Wyckoff positions identified'
611 % (len(self.wp), len(self.atoms)))
612 if len(self.wp) < len(self.atoms):
613 print('XU.material: space group %s seems not to fit'
614 % self.sgrp)
615
616 # free unit cell parameters
617 self.crystal_system, nargs = sgl.sgrp_sym[self.sgrp_nr]
618 self.crystal_system += self.sgrp_suf
619 self.uc_params = []
620 p2i = {'a': 0, 'b': 1, 'c': 2,
621 'alpha': 0, 'beta': 1, 'gamma': 2}
622 for pname in sgl.sgrp_params[self.crystal_system][0]:
623 if pname in ('a', 'b', 'c'):
624 self.uc_params.append(self.lattice_const[p2i[pname]])
625 if pname in ('alpha', 'beta', 'gamma'):
626 self.uc_params.append(self.lattice_angles[p2i[pname]])
627
628 if len(self.wp) == len(self.atoms):
629 if config.VERBOSITY >= config.INFO_ALL:
630 print('XU.material: identified space group as %s'
631 % self.sgrp)
632 break
633
634 def SGLattice(self, use_p1=False):
635 """
636 create a SGLattice object with the structure from the CIF file
637 """
638 if not use_p1:
639 if hasattr(self, 'sgrp'):
640 if len(self.wp) == len(self.atoms):
641 return sgl.SGLattice(self.sgrp, *self.uc_params,
642 atoms=self.elements, pos=self.wp,
643 occ=self.occ, b=self.biso)
644 else:
645 if config.VERBOSITY >= config.INFO_LOW:
646 print('XU.material: Wyckoff positions missing, '
647 'using P1')
648 else:
649 if config.VERBOSITY >= config.INFO_LOW:
650 print('XU.material: space-group detection failed, '
651 'using P1')
652
653 atoms = []
654 pos = []
655 occ = []
656 biso = []
657 for element, positions, o, b in self.unique_positions:
658 for p in positions:
659 atoms.append(element)
660 pos.append(('1a', p))
661 occ.append(o)
662 biso.append(b)
663
664 return sgl.SGLattice(1, *itertools.chain(self.lattice_const,
665 self.lattice_angles),
666 atoms=atoms, pos=pos, occ=occ, b=biso)
667
668 def __str__(self):
669 """
670 returns a string with positions and names of the atoms
671 """
672 ostr = ""
673 ostr += "unit cell structure:"
674 if hasattr(self, 'sgrp'):
675 ostr += " %s %s %s\n" % (self.sgrp, self.crystal_system,
676 getattr(self, 'sgrp_name', ''))
677 else:
678 ostr += "\n"
679 ostr += "a: %8.4f b: %8.4f c: %8.4f\n" % tuple(self.lattice_const)
680 ostr += "alpha: %6.2f beta: %6.2f gamma: %6.2f\n" % tuple(
681 self.lattice_angles)
682 if self.unique_positions:
683 ostr += "Unique atom positions in unit cell\n"
684 for atom in self.unique_positions:
685 ostr += atom[0].name + " (%d): \n" % atom[0].num
686 for pos in atom[1]:
687 ostr += str(numpy.round(pos, self.digits)) + "\n"
688 return ostr
689
690
691 def cifexport(filename, mat):
692 """
693 function to export a Crystal instance to CIF file. This in particular
694 includes the atomic coordinates, however, ignores for example the elastic
695 parameters.
696 """
697
698 def unique_label(basename, names):
699 num = 1
700 name = '{name}{num:d}'.format(name=basename, num=num)
701 while name in names:
702 num += 1
703 name = '{name}{num:d}'.format(name=basename, num=num)
704 return name
705
706 general = """data_global
707 _chemical_formula_sum '{chemsum}'
708 _cell_length_a {a:.5f}
709 _cell_length_b {b:.5f}
710 _cell_length_c {c:.5f}
711 _cell_angle_alpha {alpha:.4f}
712 _cell_angle_beta {beta:.4f}
713 _cell_angle_gamma {gamma:.4f}
714 _cell_volume {vol:.3f}
715 _space_group_crystal_system {csystem}
716 _space_group_IT_number {sgrpnr}
717 _space_group_name_H-M_alt '{hmsymb}'
718 """
719
720 csystem = mat.lattice.crystal_system
721 if len(mat.lattice.space_group_suf) > 0:
722 csystem = csystem[:-len(mat.lattice.space_group_suf)]
723
724 ctxt = general.format(chemsum=mat.chemical_composition(with_spaces=True),
725 a=mat.a, b=mat.b, c=mat.c,
726 alpha=mat.alpha, beta=mat.beta, gamma=mat.gamma,
727 vol=mat.lattice.UnitCellVolume(),
728 csystem=csystem,
729 sgrpnr=mat.lattice.space_group_nr,
730 hmsymb=mat.lattice.name)
731
732 sgrpsuf = mat.lattice.space_group_suf[1:]
733 if sgrpsuf:
734 ctxt += '_symmetry_cell_setting {suf}\n'.format(suf=sgrpsuf)
735
736 symloop = """
737 loop_
738 _space_group_symop_operation_xyz
739 """
740
741 gplabel = sorted(wyckpos.wp[mat.lattice.space_group].keys(),
742 key=lambda s: int(s[:-1]))[-1]
743 gp = wyckpos.wp[mat.lattice.space_group][gplabel]
744
745 for pos in gp[1]:
746 symloop += "'" + pos.strip('()') + "'\n"
747
748 atomloop = """
749 loop_
750 _atom_site_label
751 _atom_site_type_symbol
752 _atom_site_symmetry_multiplicity
753 _atom_site_Wyckoff_symbol
754 _atom_site_fract_x
755 _atom_site_fract_y
756 _atom_site_fract_z
757 _atom_site_occupancy
758 _atom_site_B_iso_or_equiv
759 """
760 nidx = 0
761 allatoms = list(mat.lattice.base())
762 names = []
763 for at, pos, occ, b in mat.lattice._wbase:
764 wm, wl, dummy = re.split('([a-z])', pos[0])
765 nsite = int(wm)
766 x, y, z = allatoms[nidx][1]
767 names.append(unique_label(at.name, names))
768 atomloop += '%s %s %d %c %.5f %.5f %.5f %.4f %.4f\n' % (
769 names[-1], at.name, nsite, wl, x, y, z, occ, b)
770 nidx += nsite
771
772 with open(filename, 'w') as f:
773 f.write(ctxt)
774 f.write(symloop)
775 f.write(atomloop)
+0
-14
xrayutilities/materials/data/README.txt less more
0 Notes about origin/copyright of files:
1
2 f[01]*.dat.xz: database files from XOP/DABAX project [1]
3
4 nist_atom.dat: included from NIST Physical Reference Database [2]
5
6 colors.dat: colors for atoms as found in Jmol [3]
7
8 atomic_radius.dat: atomic radii from Wikipedia [4]
9
10 [1] http://ftp.esrf.eu/pub/scisoft/xop2.3/
11 [2] http://www.nist.gov/pml/data/comp.cfm
12 [3] http://jmol.sourceforge.net/jscolors/
13 [4] https://en.wikipedia.org/wiki/Atomic_radii_of_the_elements_(data_page)
+0
-118
xrayutilities/materials/data/atomic_radius.dat less more
0 1,H,hydrogen,25
1 2,He,helium,120
2 3,Li,lithium,145
3 4,Be,beryllium,105
4 5,B,boron,85
5 6,C,carbon,70
6 7,N,nitrogen,65
7 8,O,oxygen,60
8 9,F,fluorine,50
9 10,Ne,neon,160
10 11,Na,sodium,180
11 12,Mg,magnesium,150
12 13,Al,aluminium,125
13 14,Si,silicon,110
14 15,P,phosphorus,100
15 16,S,sulfur,100
16 17,Cl,chlorine,100
17 18,Ar,argon,71
18 19,K,potassium,220
19 20,Ca,calcium,180
20 21,Sc,scandium,160
21 22,Ti,titanium,140
22 23,V,vanadium,135
23 24,Cr,chromium,140
24 25,Mn,manganese,140
25 26,Fe,iron,140
26 27,Co,cobalt,135
27 28,Ni,nickel,135
28 29,Cu,copper,135
29 30,Zn,zinc,135
30 31,Ga,gallium,130
31 32,Ge,germanium,125
32 33,As,arsenic,115
33 34,Se,selenium,115
34 35,Br,bromine,115
35 36,Kr,krypton,nan
36 37,Rb,rubidium,235
37 38,Sr,strontium,200
38 39,Y,yttrium,180
39 40,Zr,zirconium,155
40 41,Nb,niobium,145
41 42,Mo,molybdenum,145
42 43,Tc,technetium,135
43 44,Ru,ruthenium,130
44 45,Rh,rhodium,135
45 46,Pd,palladium,140
46 47,Ag,silver,160
47 48,Cd,cadmium,155
48 49,In,indium,155
49 50,Sn,tin,145
50 51,Sb,antimony,145
51 52,Te,tellurium,140
52 53,I,iodine,140
53 54,Xe,xenon,nan
54 55,Cs,caesium,260
55 56,Ba,barium,215
56 57,La,lanthanum,195
57 58,Ce,cerium,185
58 59,Pr,praseodymium,185
59 60,Nd,neodymium,185
60 61,Pm,promethium,185
61 62,Sm,samarium,185
62 63,Eu,europium,185
63 64,Gd,gadolinium,180
64 65,Tb,terbium,175
65 66,Dy,dysprosium,175
66 67,Ho,holmium,175
67 68,Er,erbium,175
68 69,Tm,thulium,175
69 70,Yb,ytterbium,175
70 71,Lu,lutetium,175
71 72,Hf,hafnium,155
72 73,Ta,tantalum,145
73 74,W,tungsten,135
74 75,Re,rhenium,135
75 76,Os,osmium,130
76 77,Ir,iridium,135
77 78,Pt,platinum,135
78 79,Au,gold,135
79 80,Hg,mercury,150
80 81,Tl,thallium,190
81 82,Pb,lead,180
82 83,Bi,bismuth,160
83 84,Po,polonium,190
84 85,At,astatine,nan
85 86,Rn,radon,nan
86 87,Fr,francium,nan
87 88,Ra,radium,215
88 89,Ac,actinium,195
89 90,Th,thorium,180
90 91,Pa,protactinium,180
91 92,U,uranium,175
92 93,Np,neptunium,175
93 94,Pu,plutonium,175
94 95,Am,americium,175
95 96,Cm,curium,nan
96 97,Bk,berkelium,nan
97 98,Cf,californium,nan
98 99,Es,einsteinium,nan
99 100,Fm,fermium,nan
100 101,Md,mendelevium,nan
101 102,No,nobelium,nan
102 103,Lr,lawrencium,nan
103 104,Rf,rutherfordium,nan
104 105,Db,dubnium,nan
105 106,Sg,seaborgium,nan
106 107,Bh,bohrium,nan
107 108,Hs,hassium,nan
108 109,Mt,meitnerium,nan
109 110,Ds,darmstadtium,nan
110 111,Rg,roentgenium,nan
111 112,Cn,copernicium,nan
112 113,Nh,nihonium,nan
113 114,Fl,flerovium,nan
114 115,Mc,moscovium,nan
115 116,Lv,livermorium,nan
116 117,Ts,tennessine,nan
117 118,Og,oganesson,nan
+0
-109
xrayutilities/materials/data/colors.dat less more
0 1 H [255,255,255]
1 2 He [217,255,255]
2 3 Li [204,128,255]
3 4 Be [194,255,0]
4 5 B [255,181,181]
5 6 C [144,144,144]
6 7 N [48,80,248]
7 8 O [255,13,13]
8 9 F [144,224,80]
9 10 Ne [179,227,245]
10 11 Na [171,92,242]
11 12 Mg [138,255,0]
12 13 Al [191,166,166]
13 14 Si [240,200,160]
14 15 P [255,128,0]
15 16 S [255,255,48]
16 17 Cl [31,240,31]
17 18 Ar [128,209,227]
18 19 K [143,64,212]
19 20 Ca [61,255,0]
20 21 Sc [230,230,230]
21 22 Ti [191,194,199]
22 23 V [166,166,171]
23 24 Cr [138,153,199]
24 25 Mn [156,122,199]
25 26 Fe [224,102,51]
26 27 Co [240,144,160]
27 28 Ni [80,208,80]
28 29 Cu [200,128,51]
29 30 Zn [125,128,176]
30 31 Ga [194,143,143]
31 32 Ge [102,143,143]
32 33 As [189,128,227]
33 34 Se [255,161,0]
34 35 Br [166,41,41]
35 36 Kr [92,184,209]
36 37 Rb [112,46,176]
37 38 Sr [0,255,0]
38 39 Y [148,255,255]
39 40 Zr [148,224,224]
40 41 Nb [115,194,201]
41 42 Mo [84,181,181]
42 43 Tc [59,158,158]
43 44 Ru [36,143,143]
44 45 Rh [10,125,140]
45 46 Pd [0,105,133]
46 47 Ag [192,192,192]
47 48 Cd [255,217,143]
48 49 In [166,117,115]
49 50 Sn [102,128,128]
50 51 Sb [158,99,181]
51 52 Te [212,122,0]
52 53 I [148,0,148]
53 54 Xe [66,158,176]
54 55 Cs [87,23,143]
55 56 Ba [0,201,0]
56 57 La [112,212,255]
57 58 Ce [255,255,199]
58 59 Pr [217,255,199]
59 60 Nd [199,255,199]
60 61 Pm [163,255,199]
61 62 Sm [143,255,199]
62 63 Eu [97,255,199]
63 64 Gd [69,255,199]
64 65 Tb [48,255,199]
65 66 Dy [31,255,199]
66 67 Ho [0,255,156]
67 68 Er [0,230,117]
68 69 Tm [0,212,82]
69 70 Yb [0,191,56]
70 71 Lu [0,171,36]
71 72 Hf [77,194,255]
72 73 Ta [77,166,255]
73 74 W [33,148,214]
74 75 Re [38,125,171]
75 76 Os [38,102,150]
76 77 Ir [23,84,135]
77 78 Pt [208,208,224]
78 79 Au [255,209,35]
79 80 Hg [184,184,208]
80 81 Tl [166,84,77]
81 82 Pb [87,89,97]
82 83 Bi [158,79,181]
83 84 Po [171,92,0]
84 85 At [117,79,69]
85 86 Rn [66,130,150]
86 87 Fr [66,0,102]
87 88 Ra [0,125,0]
88 89 Ac [112,171,250]
89 90 Th [0,186,255]
90 91 Pa [0,161,255]
91 92 U [0,143,255]
92 93 Np [0,128,255]
93 94 Pu [0,107,255]
94 95 Am [84,92,242]
95 96 Cm [120,92,227]
96 97 Bk [138,79,227]
97 98 Cf [161,54,212]
98 99 Es [179,31,212]
99 100 Fm [179,31,186]
100 101 Md [179,13,166]
101 102 No [189,13,135]
102 103 Lr [199,0,102]
103 104 Rf [204,0,89]
104 105 Db [209,0,79]
105 106 Sg [217,0,69]
106 107 Bh [224,0,56]
107 108 Hs [230,0,46]
108 109 Mt [235,0,38]
xrayutilities/materials/data/elements.db less more
Binary diff not shown
xrayutilities/materials/data/f0_InterTables.dat.xz less more
Binary diff not shown
xrayutilities/materials/data/f0_xop.dat.xz less more
Binary diff not shown
xrayutilities/materials/data/f1f2_Henke.dat.xz less more
Binary diff not shown
xrayutilities/materials/data/f1f2_asf_Kissel.dat.xz less more
Binary diff not shown
+0
-2840
xrayutilities/materials/data/nist_atom.dat less more
0 # This file is part of xrayutilities.
1 #
2 # data included from
3 # NIST > Physical Reference Data > Atomic Weights
4 # see http://www.nist.gov/pml/data/comp.cfm
5 # use settings:
6 # Linearized ASCII Output
7 # Most common isotopes
8
9 Atomic Number = 1
10 Atomic Symbol = H
11 Mass Number = 1
12 Relative Atomic Mass = 1.00782503223(9)
13 Isotopic Composition = 0.999885(70)
14 Standard Atomic Weight = [1.00784,1.00811]
15 Notes = m
16
17 Atomic Number = 1
18 Atomic Symbol = D
19 Mass Number = 2
20 Relative Atomic Mass = 2.01410177812(12)
21 Isotopic Composition = 0.000115(70)
22 Standard Atomic Weight = [1.00784,1.00811]
23 Notes = m
24
25 Atomic Number = 1
26 Atomic Symbol = T
27 Mass Number = 3
28 Relative Atomic Mass = 3.0160492779(24)
29 Isotopic Composition =
30 Standard Atomic Weight = [1.00784,1.00811]
31 Notes = m
32
33 Atomic Number = 2
34 Atomic Symbol = He
35 Mass Number = 3
36 Relative Atomic Mass = 3.0160293201(25)
37 Isotopic Composition = 0.00000134(3)
38 Standard Atomic Weight = 4.002602(2)
39 Notes = g,r
40
41 Atomic Number = 2
42 Atomic Symbol = He
43 Mass Number = 4
44 Relative Atomic Mass = 4.00260325413(6)
45 Isotopic Composition = 0.99999866(3)
46 Standard Atomic Weight = 4.002602(2)
47 Notes = g,r
48
49 Atomic Number = 3
50 Atomic Symbol = Li
51 Mass Number = 6
52 Relative Atomic Mass = 6.0151228874(16)
53 Isotopic Composition = 0.0759(4)
54 Standard Atomic Weight = [6.938,6.997]
55 Notes = m
56
57 Atomic Number = 3
58 Atomic Symbol = Li
59 Mass Number = 7
60 Relative Atomic Mass = 7.0160034366(45)
61 Isotopic Composition = 0.9241(4)
62 Standard Atomic Weight = [6.938,6.997]
63 Notes = m
64
65 Atomic Number = 4
66 Atomic Symbol = Be
67 Mass Number = 9
68 Relative Atomic Mass = 9.012183065(82)
69 Isotopic Composition = 1
70 Standard Atomic Weight = 9.0121831(5)
71 Notes =
72
73 Atomic Number = 5
74 Atomic Symbol = B
75 Mass Number = 10
76 Relative Atomic Mass = 10.01293695(41)
77 Isotopic Composition = 0.199(7)
78 Standard Atomic Weight = [10.806,10.821]
79 Notes = m
80
81 Atomic Number = 5
82 Atomic Symbol = B
83 Mass Number = 11
84 Relative Atomic Mass = 11.00930536(45)
85 Isotopic Composition = 0.801(7)
86 Standard Atomic Weight = [10.806,10.821]
87 Notes = m
88
89 Atomic Number = 6
90 Atomic Symbol = C
91 Mass Number = 12
92 Relative Atomic Mass = 12.0000000(00)
93 Isotopic Composition = 0.9893(8)
94 Standard Atomic Weight = [12.0096,12.0116]
95 Notes =
96
97 Atomic Number = 6
98 Atomic Symbol = C
99 Mass Number = 13
100 Relative Atomic Mass = 13.00335483507(23)
101 Isotopic Composition = 0.0107(8)
102 Standard Atomic Weight = [12.0096,12.0116]
103 Notes =
104
105 Atomic Number = 6
106 Atomic Symbol = C
107 Mass Number = 14
108 Relative Atomic Mass = 14.0032419884(40)
109 Isotopic Composition =
110 Standard Atomic Weight = [12.0096,12.0116]
111 Notes =
112
113 Atomic Number = 7
114 Atomic Symbol = N
115 Mass Number = 14
116 Relative Atomic Mass = 14.00307400443(20)
117 Isotopic Composition = 0.99636(20)
118 Standard Atomic Weight = [14.00643,14.00728]
119 Notes =
120
121 Atomic Number = 7
122 Atomic Symbol = N
123 Mass Number = 15
124 Relative Atomic Mass = 15.00010889888(64)
125 Isotopic Composition = 0.00364(20)
126 Standard Atomic Weight = [14.00643,14.00728]
127 Notes =
128
129 Atomic Number = 8
130 Atomic Symbol = O
131 Mass Number = 16
132 Relative Atomic Mass = 15.99491461957(17)
133 Isotopic Composition = 0.99757(16)
134 Standard Atomic Weight = [15.99903,15.99977]
135 Notes =
136
137 Atomic Number = 8
138 Atomic Symbol = O
139 Mass Number = 17
140 Relative Atomic Mass = 16.99913175650(69)
141 Isotopic Composition = 0.00038(1)
142 Standard Atomic Weight = [15.99903,15.99977]
143 Notes =
144
145 Atomic Number = 8
146 Atomic Symbol = O
147 Mass Number = 18
148 Relative Atomic Mass = 17.99915961286(76)
149 Isotopic Composition = 0.00205(14)
150 Standard Atomic Weight = [15.99903,15.99977]
151 Notes =
152
153 Atomic Number = 9
154 Atomic Symbol = F
155 Mass Number = 19
156 Relative Atomic Mass = 18.99840316273(92)
157 Isotopic Composition = 1
158 Standard Atomic Weight = 18.998403163(6)
159 Notes =
160
161 Atomic Number = 10
162 Atomic Symbol = Ne
163 Mass Number = 20
164 Relative Atomic Mass = 19.9924401762(17)
165 Isotopic Composition = 0.9048(3)
166 Standard Atomic Weight = 20.1797(6)
167 Notes = g,m
168
169 Atomic Number = 10
170 Atomic Symbol = Ne
171 Mass Number = 21
172 Relative Atomic Mass = 20.993846685(41)
173 Isotopic Composition = 0.0027(1)
174 Standard Atomic Weight = 20.1797(6)
175 Notes = g,m
176
177 Atomic Number = 10
178 Atomic Symbol = Ne
179 Mass Number = 22
180 Relative Atomic Mass = 21.991385114(18)
181 Isotopic Composition = 0.0925(3)
182 Standard Atomic Weight = 20.1797(6)
183 Notes = g,m
184
185 Atomic Number = 11
186 Atomic Symbol = Na
187 Mass Number = 23
188 Relative Atomic Mass = 22.9897692820(19)
189 Isotopic Composition = 1
190 Standard Atomic Weight = 22.98976928(2)
191 Notes =
192
193 Atomic Number = 12
194 Atomic Symbol = Mg
195 Mass Number = 24
196 Relative Atomic Mass = 23.985041697(14)
197 Isotopic Composition = 0.7899(4)
198 Standard Atomic Weight = [24.304,24.307]
199 Notes =
200
201 Atomic Number = 12
202 Atomic Symbol = Mg
203 Mass Number = 25
204 Relative Atomic Mass = 24.985836976(50)
205 Isotopic Composition = 0.1000(1)
206 Standard Atomic Weight = [24.304,24.307]
207 Notes =
208
209 Atomic Number = 12
210 Atomic Symbol = Mg
211 Mass Number = 26
212 Relative Atomic Mass = 25.982592968(31)
213 Isotopic Composition = 0.1101(3)
214 Standard Atomic Weight = [24.304,24.307]
215 Notes =
216
217 Atomic Number = 13
218 Atomic Symbol = Al
219 Mass Number = 27
220 Relative Atomic Mass = 26.98153853(11)
221 Isotopic Composition = 1
222 Standard Atomic Weight = 26.9815385(7)
223 Notes =
224
225 Atomic Number = 14
226 Atomic Symbol = Si
227 Mass Number = 28
228 Relative Atomic Mass = 27.97692653465(44)
229 Isotopic Composition = 0.92223(19)
230 Standard Atomic Weight = [28.084,28.086]
231 Notes =
232
233 Atomic Number = 14
234 Atomic Symbol = Si
235 Mass Number = 29
236 Relative Atomic Mass = 28.97649466490(52)
237 Isotopic Composition = 0.04685(8)
238 Standard Atomic Weight = [28.084,28.086]
239 Notes =
240
241 Atomic Number = 14
242 Atomic Symbol = Si
243 Mass Number = 30
244 Relative Atomic Mass = 29.973770136(23)
245 Isotopic Composition = 0.03092(11)
246 Standard Atomic Weight = [28.084,28.086]
247 Notes =
248
249 Atomic Number = 15
250 Atomic Symbol = P
251 Mass Number = 31
252 Relative Atomic Mass = 30.97376199842(70)
253 Isotopic Composition = 1
254 Standard Atomic Weight = 30.973761998(5)
255 Notes =
256
257 Atomic Number = 16
258 Atomic Symbol = S
259 Mass Number = 32
260 Relative Atomic Mass = 31.9720711744(14)
261 Isotopic Composition = 0.9499(26)
262 Standard Atomic Weight = [32.059,32.076]
263 Notes =
264
265 Atomic Number = 16
266 Atomic Symbol = S
267 Mass Number = 33
268 Relative Atomic Mass = 32.9714589098(15)
269 Isotopic Composition = 0.0075(2)
270 Standard Atomic Weight = [32.059,32.076]
271 Notes =
272
273 Atomic Number = 16
274 Atomic Symbol = S
275 Mass Number = 34
276 Relative Atomic Mass = 33.967867004(47)
277 Isotopic Composition = 0.0425(24)
278 Standard Atomic Weight = [32.059,32.076]
279 Notes =
280
281 Atomic Number = 16
282 Atomic Symbol = S
283 Mass Number = 36
284 Relative Atomic Mass = 35.96708071(20)
285 Isotopic Composition = 0.0001(1)
286 Standard Atomic Weight = [32.059,32.076]
287 Notes =
288
289 Atomic Number = 17
290 Atomic Symbol = Cl
291 Mass Number = 35
292 Relative Atomic Mass = 34.968852682(37)
293 Isotopic Composition = 0.7576(10)
294 Standard Atomic Weight = [35.446,35.457]
295 Notes = m
296
297 Atomic Number = 17
298 Atomic Symbol = Cl
299 Mass Number = 37
300 Relative Atomic Mass = 36.965902602(55)
301 Isotopic Composition = 0.2424(10)
302 Standard Atomic Weight = [35.446,35.457]
303 Notes = m
304
305 Atomic Number = 18
306 Atomic Symbol = Ar
307 Mass Number = 36
308 Relative Atomic Mass = 35.967545105(28)
309 Isotopic Composition = 0.003336(21)
310 Standard Atomic Weight = 39.948(1)
311 Notes = g,r
312
313 Atomic Number = 18
314 Atomic Symbol = Ar
315 Mass Number = 38
316 Relative Atomic Mass = 37.96273211(21)
317 Isotopic Composition = 0.000629(7)
318 Standard Atomic Weight = 39.948(1)
319 Notes = g,r
320
321 Atomic Number = 18
322 Atomic Symbol = Ar
323 Mass Number = 40
324 Relative Atomic Mass = 39.9623831237(24)
325 Isotopic Composition = 0.996035(25)
326 Standard Atomic Weight = 39.948(1)
327 Notes = g,r
328
329 Atomic Number = 19
330 Atomic Symbol = K
331 Mass Number = 39
332 Relative Atomic Mass = 38.9637064864(49)
333 Isotopic Composition = 0.932581(44)
334 Standard Atomic Weight = 39.0983(1)
335 Notes =
336
337 Atomic Number = 19
338 Atomic Symbol = K
339 Mass Number = 40
340 Relative Atomic Mass = 39.963998166(60)
341 Isotopic Composition = 0.000117(1)
342 Standard Atomic Weight = 39.0983(1)
343 Notes =
344
345 Atomic Number = 19
346 Atomic Symbol = K
347 Mass Number = 41
348 Relative Atomic Mass = 40.9618252579(41)
349 Isotopic Composition = 0.067302(44)
350 Standard Atomic Weight = 39.0983(1)
351 Notes =
352
353 Atomic Number = 20
354 Atomic Symbol = Ca
355 Mass Number = 40
356 Relative Atomic Mass = 39.962590863(22)
357 Isotopic Composition = 0.96941(156)
358 Standard Atomic Weight = 40.078(4)
359 Notes = g
360
361 Atomic Number = 20
362 Atomic Symbol = Ca
363 Mass Number = 42
364 Relative Atomic Mass = 41.95861783(16)
365 Isotopic Composition = 0.00647(23)
366 Standard Atomic Weight = 40.078(4)
367 Notes = g
368
369 Atomic Number = 20
370 Atomic Symbol = Ca
371 Mass Number = 43
372 Relative Atomic Mass = 42.95876644(24)
373 Isotopic Composition = 0.00135(10)
374 Standard Atomic Weight = 40.078(4)
375 Notes = g
376
377 Atomic Number = 20
378 Atomic Symbol = Ca
379 Mass Number = 44
380 Relative Atomic Mass = 43.95548156(35)
381 Isotopic Composition = 0.02086(110)
382 Standard Atomic Weight = 40.078(4)
383 Notes = g
384
385 Atomic Number = 20
386 Atomic Symbol = Ca
387 Mass Number = 46
388 Relative Atomic Mass = 45.9536890(24)
389 Isotopic Composition = 0.00004(3)
390 Standard Atomic Weight = 40.078(4)
391 Notes = g
392
393 Atomic Number = 20
394 Atomic Symbol = Ca
395 Mass Number = 48
396 Relative Atomic Mass = 47.95252276(13)
397 Isotopic Composition = 0.00187(21)
398 Standard Atomic Weight = 40.078(4)
399 Notes = g
400
401 Atomic Number = 21
402 Atomic Symbol = Sc
403 Mass Number = 45
404 Relative Atomic Mass = 44.95590828(77)
405 Isotopic Composition = 1
406 Standard Atomic Weight = 44.955908(5)
407 Notes =
408
409 Atomic Number = 22
410 Atomic Symbol = Ti
411 Mass Number = 46
412 Relative Atomic Mass = 45.95262772(35)
413 Isotopic Composition = 0.0825(3)
414 Standard Atomic Weight = 47.867(1)
415 Notes =
416
417 Atomic Number = 22
418 Atomic Symbol = Ti
419 Mass Number = 47
420 Relative Atomic Mass = 46.95175879(38)
421 Isotopic Composition = 0.0744(2)
422 Standard Atomic Weight = 47.867(1)
423 Notes =
424
425 Atomic Number = 22
426 Atomic Symbol = Ti
427 Mass Number = 48
428 Relative Atomic Mass = 47.94794198(38)
429 Isotopic Composition = 0.7372(3)
430 Standard Atomic Weight = 47.867(1)
431 Notes =
432
433 Atomic Number = 22
434 Atomic Symbol = Ti
435 Mass Number = 49
436 Relative Atomic Mass = 48.94786568(39)
437 Isotopic Composition = 0.0541(2)
438 Standard Atomic Weight = 47.867(1)
439 Notes =
440
441 Atomic Number = 22
442 Atomic Symbol = Ti
443 Mass Number = 50
444 Relative Atomic Mass = 49.94478689(39)
445 Isotopic Composition = 0.0518(2)
446 Standard Atomic Weight = 47.867(1)
447 Notes =
448
449 Atomic Number = 23
450 Atomic Symbol = V
451 Mass Number = 50
452 Relative Atomic Mass = 49.94715601(95)
453 Isotopic Composition = 0.00250(4)
454 Standard Atomic Weight = 50.9415(1)
455 Notes =
456
457 Atomic Number = 23
458 Atomic Symbol = V
459 Mass Number = 51
460 Relative Atomic Mass = 50.94395704(94)
461 Isotopic Composition = 0.99750(4)
462 Standard Atomic Weight = 50.9415(1)
463 Notes =
464
465 Atomic Number = 24
466 Atomic Symbol = Cr
467 Mass Number = 50
468 Relative Atomic Mass = 49.94604183(94)
469 Isotopic Composition = 0.04345(13)
470 Standard Atomic Weight = 51.9961(6)
471 Notes =
472
473 Atomic Number = 24
474 Atomic Symbol = Cr
475 Mass Number = 52
476 Relative Atomic Mass = 51.94050623(63)
477 Isotopic Composition = 0.83789(18)
478 Standard Atomic Weight = 51.9961(6)
479 Notes =
480
481 Atomic Number = 24
482 Atomic Symbol = Cr
483 Mass Number = 53
484 Relative Atomic Mass = 52.94064815(62)
485 Isotopic Composition = 0.09501(17)
486 Standard Atomic Weight = 51.9961(6)
487 Notes =
488
489 Atomic Number = 24
490 Atomic Symbol = Cr
491 Mass Number = 54
492 Relative Atomic Mass = 53.93887916(61)
493 Isotopic Composition = 0.02365(7)
494 Standard Atomic Weight = 51.9961(6)
495 Notes =
496
497 Atomic Number = 25
498 Atomic Symbol = Mn
499 Mass Number = 55
500 Relative Atomic Mass = 54.93804391(48)
501 Isotopic Composition = 1
502 Standard Atomic Weight = 54.938044(3)
503 Notes =
504
505 Atomic Number = 26
506 Atomic Symbol = Fe
507 Mass Number = 54
508 Relative Atomic Mass = 53.93960899(53)
509 Isotopic Composition = 0.05845(35)
510 Standard Atomic Weight = 55.845(2)
511 Notes =
512
513 Atomic Number = 26
514 Atomic Symbol = Fe
515 Mass Number = 56
516 Relative Atomic Mass = 55.93493633(49)
517 Isotopic Composition = 0.91754(36)
518 Standard Atomic Weight = 55.845(2)
519 Notes =
520
521 Atomic Number = 26
522 Atomic Symbol = Fe
523 Mass Number = 57
524 Relative Atomic Mass = 56.93539284(49)
525 Isotopic Composition = 0.02119(10)
526 Standard Atomic Weight = 55.845(2)
527 Notes =
528
529 Atomic Number = 26
530 Atomic Symbol = Fe
531 Mass Number = 58
532 Relative Atomic Mass = 57.93327443(53)
533 Isotopic Composition = 0.00282(4)
534 Standard Atomic Weight = 55.845(2)
535 Notes =
536
537 Atomic Number = 27
538 Atomic Symbol = Co
539 Mass Number = 59
540 Relative Atomic Mass = 58.93319429(56)
541 Isotopic Composition = 1
542 Standard Atomic Weight = 58.933194(4)
543 Notes =
544
545 Atomic Number = 28
546 Atomic Symbol = Ni
547 Mass Number = 58
548 Relative Atomic Mass = 57.93534241(52)
549 Isotopic Composition = 0.68077(19)
550 Standard Atomic Weight = 58.6934(4)
551 Notes = r
552
553 Atomic Number = 28
554 Atomic Symbol = Ni
555 Mass Number = 60
556 Relative Atomic Mass = 59.93078588(52)
557 Isotopic Composition = 0.26223(15)
558 Standard Atomic Weight = 58.6934(4)
559 Notes = r
560
561 Atomic Number = 28
562 Atomic Symbol = Ni
563 Mass Number = 61
564 Relative Atomic Mass = 60.93105557(52)
565 Isotopic Composition = 0.011399(13)
566 Standard Atomic Weight = 58.6934(4)
567 Notes = r
568
569 Atomic Number = 28
570 Atomic Symbol = Ni
571 Mass Number = 62
572 Relative Atomic Mass = 61.92834537(55)
573 Isotopic Composition = 0.036346(40)
574 Standard Atomic Weight = 58.6934(4)
575 Notes = r
576
577 Atomic Number = 28
578 Atomic Symbol = Ni
579 Mass Number = 64
580 Relative Atomic Mass = 63.92796682(58)
581 Isotopic Composition = 0.009255(19)
582 Standard Atomic Weight = 58.6934(4)
583 Notes = r
584
585 Atomic Number = 29
586 Atomic Symbol = Cu
587 Mass Number = 63
588 Relative Atomic Mass = 62.92959772(56)
589 Isotopic Composition = 0.6915(15)
590 Standard Atomic Weight = 63.546(3)
591 Notes = r
592
593 Atomic Number = 29
594 Atomic Symbol = Cu
595 Mass Number = 65
596 Relative Atomic Mass = 64.92778970(71)
597 Isotopic Composition = 0.3085(15)
598 Standard Atomic Weight = 63.546(3)
599 Notes = r
600
601 Atomic Number = 30
602 Atomic Symbol = Zn
603 Mass Number = 64
604 Relative Atomic Mass = 63.92914201(71)
605 Isotopic Composition = 0.4917(75)
606 Standard Atomic Weight = 65.38(2)
607 Notes = r
608
609 Atomic Number = 30
610 Atomic Symbol = Zn
611 Mass Number = 66
612 Relative Atomic Mass = 65.92603381(94)
613 Isotopic Composition = 0.2773(98)
614 Standard Atomic Weight = 65.38(2)
615 Notes = r
616
617 Atomic Number = 30
618 Atomic Symbol = Zn
619 Mass Number = 67
620 Relative Atomic Mass = 66.92712775(96)
621 Isotopic Composition = 0.0404(16)
622 Standard Atomic Weight = 65.38(2)
623 Notes = r
624
625 Atomic Number = 30
626 Atomic Symbol = Zn
627 Mass Number = 68
628 Relative Atomic Mass = 67.92484455(98)
629 Isotopic Composition = 0.1845(63)
630 Standard Atomic Weight = 65.38(2)
631 Notes = r
632
633 Atomic Number = 30
634 Atomic Symbol = Zn
635 Mass Number = 70
636 Relative Atomic Mass = 69.9253192(21)
637 Isotopic Composition = 0.0061(10)
638 Standard Atomic Weight = 65.38(2)
639 Notes = r
640
641 Atomic Number = 31
642 Atomic Symbol = Ga
643 Mass Number = 69
644 Relative Atomic Mass = 68.9255735(13)
645 Isotopic Composition = 0.60108(9)
646 Standard Atomic Weight = 69.723(1)
647 Notes =
648
649 Atomic Number = 31
650 Atomic Symbol = Ga
651 Mass Number = 71
652 Relative Atomic Mass = 70.92470258(87)
653 Isotopic Composition = 0.39892(9)
654 Standard Atomic Weight = 69.723(1)
655 Notes =
656
657 Atomic Number = 32
658 Atomic Symbol = Ge
659 Mass Number = 70
660 Relative Atomic Mass = 69.92424875(90)
661 Isotopic Composition = 0.2057(27)
662 Standard Atomic Weight = 72.630(8)
663 Notes =
664
665 Atomic Number = 32
666 Atomic Symbol = Ge
667 Mass Number = 72
668 Relative Atomic Mass = 71.922075826(81)
669 Isotopic Composition = 0.2745(32)
670 Standard Atomic Weight = 72.630(8)
671 Notes =
672
673 Atomic Number = 32
674 Atomic Symbol = Ge
675 Mass Number = 73
676 Relative Atomic Mass = 72.923458956(61)
677 Isotopic Composition = 0.0775(12)
678 Standard Atomic Weight = 72.630(8)
679 Notes =
680
681 Atomic Number = 32
682 Atomic Symbol = Ge
683 Mass Number = 74
684 Relative Atomic Mass = 73.921177761(13)
685 Isotopic Composition = 0.3650(20)
686 Standard Atomic Weight = 72.630(8)
687 Notes =
688
689 Atomic Number = 32
690 Atomic Symbol = Ge
691 Mass Number = 76
692 Relative Atomic Mass = 75.921402726(19)
693 Isotopic Composition = 0.0773(12)
694 Standard Atomic Weight = 72.630(8)
695 Notes =
696
697 Atomic Number = 33
698 Atomic Symbol = As
699 Mass Number = 75
700 Relative Atomic Mass = 74.92159457(95)
701 Isotopic Composition = 1
702 Standard Atomic Weight = 74.921595(6)
703 Notes =
704
705 Atomic Number = 34
706 Atomic Symbol = Se
707 Mass Number = 74
708 Relative Atomic Mass = 73.922475934(15)
709 Isotopic Composition = 0.0089(4)
710 Standard Atomic Weight = 78.971(8)
711 Notes = r
712
713 Atomic Number = 34
714 Atomic Symbol = Se
715 Mass Number = 76
716 Relative Atomic Mass = 75.919213704(17)
717 Isotopic Composition = 0.0937(29)
718 Standard Atomic Weight = 78.971(8)
719 Notes = r
720
721 Atomic Number = 34
722 Atomic Symbol = Se
723 Mass Number = 77
724 Relative Atomic Mass = 76.919914154(67)
725 Isotopic Composition = 0.0763(16)
726 Standard Atomic Weight = 78.971(8)
727 Notes = r
728
729 Atomic Number = 34
730 Atomic Symbol = Se
731 Mass Number = 78
732 Relative Atomic Mass = 77.91730928(20)
733 Isotopic Composition = 0.2377(28)
734 Standard Atomic Weight = 78.971(8)
735 Notes = r
736
737 Atomic Number = 34
738 Atomic Symbol = Se
739 Mass Number = 80
740 Relative Atomic Mass = 79.9165218(13)
741 Isotopic Composition = 0.4961(41)
742 Standard Atomic Weight = 78.971(8)
743 Notes = r
744
745 Atomic Number = 34
746 Atomic Symbol = Se
747 Mass Number = 82
748 Relative Atomic Mass = 81.9166995(15)
749 Isotopic Composition = 0.0873(22)
750 Standard Atomic Weight = 78.971(8)
751 Notes = r
752
753 Atomic Number = 35
754 Atomic Symbol = Br
755 Mass Number = 79
756 Relative Atomic Mass = 78.9183376(14)
757 Isotopic Composition = 0.5069(7)
758 Standard Atomic Weight = [79.901,79.907]
759 Notes =
760
761 Atomic Number = 35
762 Atomic Symbol = Br
763 Mass Number = 81
764 Relative Atomic Mass = 80.9162897(14)
765 Isotopic Composition = 0.4931(7)
766 Standard Atomic Weight = [79.901,79.907]
767 Notes =
768
769 Atomic Number = 36
770 Atomic Symbol = Kr
771 Mass Number = 78
772 Relative Atomic Mass = 77.92036494(76)
773 Isotopic Composition = 0.00355(3)
774 Standard Atomic Weight = 83.798(2)
775 Notes = g,m
776
777 Atomic Number = 36
778 Atomic Symbol = Kr
779 Mass Number = 80
780 Relative Atomic Mass = 79.91637808(75)
781 Isotopic Composition = 0.02286(10)
782 Standard Atomic Weight = 83.798(2)
783 Notes = g,m
784
785 Atomic Number = 36
786 Atomic Symbol = Kr
787 Mass Number = 82
788 Relative Atomic Mass = 81.91348273(94)
789 Isotopic Composition = 0.11593(31)
790 Standard Atomic Weight = 83.798(2)
791 Notes = g,m
792
793 Atomic Number = 36
794 Atomic Symbol = Kr
795 Mass Number = 83
796 Relative Atomic Mass = 82.91412716(32)
797 Isotopic Composition = 0.11500(19)
798 Standard Atomic Weight = 83.798(2)
799 Notes = g,m
800
801 Atomic Number = 36
802 Atomic Symbol = Kr
803 Mass Number = 84
804 Relative Atomic Mass = 83.9114977282(44)
805 Isotopic Composition = 0.56987(15)
806 Standard Atomic Weight = 83.798(2)
807 Notes = g,m
808
809 Atomic Number = 36
810 Atomic Symbol = Kr
811 Mass Number = 86
812 Relative Atomic Mass = 85.9106106269(41)
813 Isotopic Composition = 0.17279(41)
814 Standard Atomic Weight = 83.798(2)
815 Notes = g,m
816
817 Atomic Number = 37
818 Atomic Symbol = Rb
819 Mass Number = 85
820 Relative Atomic Mass = 84.9117897379(54)
821 Isotopic Composition = 0.7217(2)
822 Standard Atomic Weight = 85.4678(3)
823 Notes = g
824
825 Atomic Number = 37
826 Atomic Symbol = Rb
827 Mass Number = 87
828 Relative Atomic Mass = 86.9091805310(60)
829 Isotopic Composition = 0.2783(2)
830 Standard Atomic Weight = 85.4678(3)
831 Notes = g
832
833 Atomic Number = 38
834 Atomic Symbol = Sr
835 Mass Number = 84
836 Relative Atomic Mass = 83.9134191(13)
837 Isotopic Composition = 0.0056(1)
838 Standard Atomic Weight = 87.62(1)
839 Notes = g,r
840
841 Atomic Number = 38
842 Atomic Symbol = Sr
843 Mass Number = 86
844 Relative Atomic Mass = 85.9092606(12)
845 Isotopic Composition = 0.0986(1)
846 Standard Atomic Weight = 87.62(1)
847 Notes = g,r
848
849 Atomic Number = 38
850 Atomic Symbol = Sr
851 Mass Number = 87
852 Relative Atomic Mass = 86.9088775(12)
853 Isotopic Composition = 0.0700(1)
854 Standard Atomic Weight = 87.62(1)
855 Notes = g,r
856
857 Atomic Number = 38
858 Atomic Symbol = Sr
859 Mass Number = 88
860 Relative Atomic Mass = 87.9056125(12)
861 Isotopic Composition = 0.8258(1)
862 Standard Atomic Weight = 87.62(1)
863 Notes = g,r
864
865 Atomic Number = 39
866 Atomic Symbol = Y
867 Mass Number = 89
868 Relative Atomic Mass = 88.9058403(24)
869 Isotopic Composition = 1
870 Standard Atomic Weight = 88.90584(2)
871 Notes =
872
873 Atomic Number = 40
874 Atomic Symbol = Zr
875 Mass Number = 90
876 Relative Atomic Mass = 89.9046977(20)
877 Isotopic Composition = 0.5145(40)
878 Standard Atomic Weight = 91.224(2)
879 Notes = g
880
881 Atomic Number = 40
882 Atomic Symbol = Zr
883 Mass Number = 91
884 Relative Atomic Mass = 90.9056396(20)
885 Isotopic Composition = 0.1122(5)
886 Standard Atomic Weight = 91.224(2)
887 Notes = g
888
889 Atomic Number = 40
890 Atomic Symbol = Zr
891 Mass Number = 92
892 Relative Atomic Mass = 91.9050347(20)
893 Isotopic Composition = 0.1715(8)
894 Standard Atomic Weight = 91.224(2)
895 Notes = g
896
897 Atomic Number = 40
898 Atomic Symbol = Zr
899 Mass Number = 94
900 Relative Atomic Mass = 93.9063108(20)
901 Isotopic Composition = 0.1738(28)
902 Standard Atomic Weight = 91.224(2)
903 Notes = g
904
905 Atomic Number = 40
906 Atomic Symbol = Zr
907 Mass Number = 96
908 Relative Atomic Mass = 95.9082714(21)
909 Isotopic Composition = 0.0280(9)
910 Standard Atomic Weight = 91.224(2)
911 Notes = g
912
913 Atomic Number = 41
914 Atomic Symbol = Nb
915 Mass Number = 93
916 Relative Atomic Mass = 92.9063730(20)
917 Isotopic Composition = 1
918 Standard Atomic Weight = 92.90637(2)
919 Notes =
920
921 Atomic Number = 42
922 Atomic Symbol = Mo
923 Mass Number = 92
924 Relative Atomic Mass = 91.90680796(84)
925 Isotopic Composition = 0.1453(30)
926 Standard Atomic Weight = 95.95(1)
927 Notes = g
928
929 Atomic Number = 42
930 Atomic Symbol = Mo
931 Mass Number = 94
932 Relative Atomic Mass = 93.90508490(48)
933 Isotopic Composition = 0.0915(9)
934 Standard Atomic Weight = 95.95(1)
935 Notes = g
936
937 Atomic Number = 42
938 Atomic Symbol = Mo
939 Mass Number = 95
940 Relative Atomic Mass = 94.90583877(47)
941 Isotopic Composition = 0.1584(11)
942 Standard Atomic Weight = 95.95(1)
943 Notes = g
944
945 Atomic Number = 42
946 Atomic Symbol = Mo
947 Mass Number = 96
948 Relative Atomic Mass = 95.90467612(47)
949 Isotopic Composition = 0.1667(15)
950 Standard Atomic Weight = 95.95(1)
951 Notes = g
952
953 Atomic Number = 42
954 Atomic Symbol = Mo
955 Mass Number = 97
956 Relative Atomic Mass = 96.90601812(49)
957 Isotopic Composition = 0.0960(14)
958 Standard Atomic Weight = 95.95(1)
959 Notes = g
960
961 Atomic Number = 42
962 Atomic Symbol = Mo
963 Mass Number = 98
964 Relative Atomic Mass = 97.90540482(49)
965 Isotopic Composition = 0.2439(37)
966 Standard Atomic Weight = 95.95(1)
967 Notes = g
968
969 Atomic Number = 42
970 Atomic Symbol = Mo
971 Mass Number = 100
972 Relative Atomic Mass = 99.9074718(11)
973 Isotopic Composition = 0.0982(31)
974 Standard Atomic Weight = 95.95(1)
975 Notes = g
976
977 Atomic Number = 43
978 Atomic Symbol = Tc
979 Mass Number = 97
980 Relative Atomic Mass = 96.9063667(40)
981 Isotopic Composition =
982 Standard Atomic Weight = [98]
983 Notes =
984
985 Atomic Number = 43
986 Atomic Symbol = Tc
987 Mass Number = 98
988 Relative Atomic Mass = 97.9072124(36)
989 Isotopic Composition =
990 Standard Atomic Weight = [98]
991 Notes =
992
993 Atomic Number = 43
994 Atomic Symbol = Tc
995 Mass Number = 99
996 Relative Atomic Mass = 98.9062508(10)
997 Isotopic Composition =
998 Standard Atomic Weight = [98]
999 Notes =
1000
1001 Atomic Number = 44
1002 Atomic Symbol = Ru
1003 Mass Number = 96
1004 Relative Atomic Mass = 95.90759025(49)
1005 Isotopic Composition = 0.0554(14)
1006 Standard Atomic Weight = 101.07(2)
1007 Notes = g
1008
1009 Atomic Number = 44
1010 Atomic Symbol = Ru
1011 Mass Number = 98
1012 Relative Atomic Mass = 97.9052868(69)
1013 Isotopic Composition = 0.0187(3)
1014 Standard Atomic Weight = 101.07(2)
1015 Notes = g
1016
1017 Atomic Number = 44
1018 Atomic Symbol = Ru
1019 Mass Number = 99
1020 Relative Atomic Mass = 98.9059341(11)
1021 Isotopic Composition = 0.1276(14)
1022 Standard Atomic Weight = 101.07(2)
1023 Notes = g
1024
1025 Atomic Number = 44
1026 Atomic Symbol = Ru
1027 Mass Number = 100
1028 Relative Atomic Mass = 99.9042143(11)
1029 Isotopic Composition = 0.1260(7)
1030 Standard Atomic Weight = 101.07(2)
1031 Notes = g
1032
1033 Atomic Number = 44
1034 Atomic Symbol = Ru
1035 Mass Number = 101
1036 Relative Atomic Mass = 100.9055769(12)
1037 Isotopic Composition = 0.1706(2)
1038 Standard Atomic Weight = 101.07(2)
1039 Notes = g
1040
1041 Atomic Number = 44
1042 Atomic Symbol = Ru
1043 Mass Number = 102
1044 Relative Atomic Mass = 101.9043441(12)
1045 Isotopic Composition = 0.3155(14)
1046 Standard Atomic Weight = 101.07(2)
1047 Notes = g
1048
1049 Atomic Number = 44
1050 Atomic Symbol = Ru
1051 Mass Number = 104
1052 Relative Atomic Mass = 103.9054275(28)
1053 Isotopic Composition = 0.1862(27)
1054 Standard Atomic Weight = 101.07(2)
1055 Notes = g
1056
1057 Atomic Number = 45
1058 Atomic Symbol = Rh
1059 Mass Number = 103
1060 Relative Atomic Mass = 102.9054980(26)
1061 Isotopic Composition = 1
1062 Standard Atomic Weight = 102.90550(2)
1063 Notes =
1064
1065 Atomic Number = 46
1066 Atomic Symbol = Pd
1067 Mass Number = 102
1068 Relative Atomic Mass = 101.9056022(28)
1069 Isotopic Composition = 0.0102(1)
1070 Standard Atomic Weight = 106.42(1)
1071 Notes = g
1072
1073 Atomic Number = 46
1074 Atomic Symbol = Pd
1075 Mass Number = 104
1076 Relative Atomic Mass = 103.9040305(14)
1077 Isotopic Composition = 0.1114(8)
1078 Standard Atomic Weight = 106.42(1)
1079 Notes = g
1080
1081 Atomic Number = 46
1082 Atomic Symbol = Pd
1083 Mass Number = 105
1084 Relative Atomic Mass = 104.9050796(12)
1085 Isotopic Composition = 0.2233(8)
1086 Standard Atomic Weight = 106.42(1)
1087 Notes = g
1088
1089 Atomic Number = 46
1090 Atomic Symbol = Pd
1091 Mass Number = 106
1092 Relative Atomic Mass = 105.9034804(12)
1093 Isotopic Composition = 0.2733(3)
1094 Standard Atomic Weight = 106.42(1)
1095 Notes = g
1096
1097 Atomic Number = 46
1098 Atomic Symbol = Pd
1099 Mass Number = 108
1100 Relative Atomic Mass = 107.9038916(12)
1101 Isotopic Composition = 0.2646(9)
1102 Standard Atomic Weight = 106.42(1)
1103 Notes = g
1104
1105 Atomic Number = 46
1106 Atomic Symbol = Pd
1107 Mass Number = 110
1108 Relative Atomic Mass = 109.90517220(75)
1109 Isotopic Composition = 0.1172(9)
1110 Standard Atomic Weight = 106.42(1)
1111 Notes = g
1112
1113 Atomic Number = 47
1114 Atomic Symbol = Ag
1115 Mass Number = 107
1116 Relative Atomic Mass = 106.9050916(26)
1117 Isotopic Composition = 0.51839(8)
1118 Standard Atomic Weight = 107.8682(2)
1119 Notes = g
1120
1121 Atomic Number = 47
1122 Atomic Symbol = Ag
1123 Mass Number = 109
1124 Relative Atomic Mass = 108.9047553(14)
1125 Isotopic Composition = 0.48161(8)
1126 Standard Atomic Weight = 107.8682(2)
1127 Notes = g
1128
1129 Atomic Number = 48
1130 Atomic Symbol = Cd
1131 Mass Number = 106
1132 Relative Atomic Mass = 105.9064599(12)
1133 Isotopic Composition = 0.0125(6)
1134 Standard Atomic Weight = 112.414(4)
1135 Notes = g
1136
1137 Atomic Number = 48
1138 Atomic Symbol = Cd
1139 Mass Number = 108
1140 Relative Atomic Mass = 107.9041834(12)
1141 Isotopic Composition = 0.0089(3)
1142 Standard Atomic Weight = 112.414(4)
1143 Notes = g
1144
1145 Atomic Number = 48
1146 Atomic Symbol = Cd
1147 Mass Number = 110
1148 Relative Atomic Mass = 109.90300661(61)
1149 Isotopic Composition = 0.1249(18)
1150 Standard Atomic Weight = 112.414(4)
1151 Notes = g
1152
1153 Atomic Number = 48
1154 Atomic Symbol = Cd
1155 Mass Number = 111
1156 Relative Atomic Mass = 110.90418287(61)
1157 Isotopic Composition = 0.1280(12)
1158 Standard Atomic Weight = 112.414(4)
1159 Notes = g
1160
1161 Atomic Number = 48
1162 Atomic Symbol = Cd
1163 Mass Number = 112
1164 Relative Atomic Mass = 111.90276287(60)
1165 Isotopic Composition = 0.2413(21)
1166 Standard Atomic Weight = 112.414(4)
1167 Notes = g
1168
1169 Atomic Number = 48
1170 Atomic Symbol = Cd
1171 Mass Number = 113
1172 Relative Atomic Mass = 112.90440813(45)
1173 Isotopic Composition = 0.1222(12)
1174 Standard Atomic Weight = 112.414(4)
1175 Notes = g
1176
1177 Atomic Number = 48
1178 Atomic Symbol = Cd
1179 Mass Number = 114
1180 Relative Atomic Mass = 113.90336509(43)
1181 Isotopic Composition = 0.2873(42)
1182 Standard Atomic Weight = 112.414(4)
1183 Notes = g
1184
1185 Atomic Number = 48
1186 Atomic Symbol = Cd
1187 Mass Number = 116
1188 Relative Atomic Mass = 115.90476315(17)
1189 Isotopic Composition = 0.0749(18)
1190 Standard Atomic Weight = 112.414(4)
1191 Notes = g
1192
1193 Atomic Number = 49
1194 Atomic Symbol = In
1195 Mass Number = 113
1196 Relative Atomic Mass = 112.90406184(91)
1197 Isotopic Composition = 0.0429(5)
1198 Standard Atomic Weight = 114.818(1)
1199 Notes =
1200
1201 Atomic Number = 49
1202 Atomic Symbol = In
1203 Mass Number = 115
1204 Relative Atomic Mass = 114.903878776(12)
1205 Isotopic Composition = 0.9571(5)
1206 Standard Atomic Weight = 114.818(1)
1207 Notes =
1208
1209 Atomic Number = 50
1210 Atomic Symbol = Sn
1211 Mass Number = 112
1212 Relative Atomic Mass = 111.90482387(61)
1213 Isotopic Composition = 0.0097(1)
1214 Standard Atomic Weight = 118.710(7)
1215 Notes = g
1216
1217 Atomic Number = 50
1218 Atomic Symbol = Sn
1219 Mass Number = 114
1220 Relative Atomic Mass = 113.9027827(10)
1221 Isotopic Composition = 0.0066(1)
1222 Standard Atomic Weight = 118.710(7)
1223 Notes = g
1224
1225 Atomic Number = 50
1226 Atomic Symbol = Sn
1227 Mass Number = 115
1228 Relative Atomic Mass = 114.903344699(16)
1229 Isotopic Composition = 0.0034(1)
1230 Standard Atomic Weight = 118.710(7)
1231 Notes = g
1232
1233 Atomic Number = 50
1234 Atomic Symbol = Sn
1235 Mass Number = 116
1236 Relative Atomic Mass = 115.90174280(10)
1237 Isotopic Composition = 0.1454(9)
1238 Standard Atomic Weight = 118.710(7)
1239 Notes = g
1240
1241 Atomic Number = 50
1242 Atomic Symbol = Sn
1243 Mass Number = 117
1244 Relative Atomic Mass = 116.90295398(52)
1245 Isotopic Composition = 0.0768(7)
1246 Standard Atomic Weight = 118.710(7)
1247 Notes = g
1248
1249 Atomic Number = 50
1250 Atomic Symbol = Sn
1251 Mass Number = 118
1252 Relative Atomic Mass = 117.90160657(54)
1253 Isotopic Composition = 0.2422(9)
1254 Standard Atomic Weight = 118.710(7)
1255 Notes = g
1256
1257 Atomic Number = 50
1258 Atomic Symbol = Sn
1259 Mass Number = 119
1260 Relative Atomic Mass = 118.90331117(78)
1261 Isotopic Composition = 0.0859(4)
1262 Standard Atomic Weight = 118.710(7)
1263 Notes = g
1264
1265 Atomic Number = 50
1266 Atomic Symbol = Sn
1267 Mass Number = 120
1268 Relative Atomic Mass = 119.90220163(97)
1269 Isotopic Composition = 0.3258(9)
1270 Standard Atomic Weight = 118.710(7)
1271 Notes = g
1272
1273 Atomic Number = 50
1274 Atomic Symbol = Sn
1275 Mass Number = 122
1276 Relative Atomic Mass = 121.9034438(26)
1277 Isotopic Composition = 0.0463(3)
1278 Standard Atomic Weight = 118.710(7)
1279 Notes = g
1280
1281 Atomic Number = 50
1282 Atomic Symbol = Sn
1283 Mass Number = 124
1284 Relative Atomic Mass = 123.9052766(11)
1285 Isotopic Composition = 0.0579(5)
1286 Standard Atomic Weight = 118.710(7)
1287 Notes = g
1288
1289 Atomic Number = 51
1290 Atomic Symbol = Sb
1291 Mass Number = 121
1292 Relative Atomic Mass = 120.9038120(30)
1293 Isotopic Composition = 0.5721(5)
1294 Standard Atomic Weight = 121.760(1)
1295 Notes = g
1296
1297 Atomic Number = 51
1298 Atomic Symbol = Sb
1299 Mass Number = 123
1300 Relative Atomic Mass = 122.9042132(23)
1301 Isotopic Composition = 0.4279(5)
1302 Standard Atomic Weight = 121.760(1)
1303 Notes = g
1304
1305 Atomic Number = 52
1306 Atomic Symbol = Te
1307 Mass Number = 120
1308 Relative Atomic Mass = 119.9040593(33)
1309 Isotopic Composition = 0.0009(1)
1310 Standard Atomic Weight = 127.60(3)
1311 Notes = g
1312
1313 Atomic Number = 52
1314 Atomic Symbol = Te
1315 Mass Number = 122
1316 Relative Atomic Mass = 121.9030435(16)
1317 Isotopic Composition = 0.0255(12)
1318 Standard Atomic Weight = 127.60(3)
1319 Notes = g
1320
1321 Atomic Number = 52
1322 Atomic Symbol = Te
1323 Mass Number = 123
1324 Relative Atomic Mass = 122.9042698(16)
1325 Isotopic Composition = 0.0089(3)
1326 Standard Atomic Weight = 127.60(3)
1327 Notes = g
1328
1329 Atomic Number = 52
1330 Atomic Symbol = Te
1331 Mass Number = 124
1332 Relative Atomic Mass = 123.9028171(16)
1333 Isotopic Composition = 0.0474(14)
1334 Standard Atomic Weight = 127.60(3)
1335 Notes = g
1336
1337 Atomic Number = 52
1338 Atomic Symbol = Te
1339 Mass Number = 125
1340 Relative Atomic Mass = 124.9044299(16)
1341 Isotopic Composition = 0.0707(15)
1342 Standard Atomic Weight = 127.60(3)
1343 Notes = g
1344
1345 Atomic Number = 52
1346 Atomic Symbol = Te
1347 Mass Number = 126
1348 Relative Atomic Mass = 125.9033109(16)
1349 Isotopic Composition = 0.1884(25)
1350 Standard Atomic Weight = 127.60(3)
1351 Notes = g
1352
1353 Atomic Number = 52
1354 Atomic Symbol = Te
1355 Mass Number = 128
1356 Relative Atomic Mass = 127.90446128(93)
1357 Isotopic Composition = 0.3174(8)
1358 Standard Atomic Weight = 127.60(3)
1359 Notes = g
1360
1361 Atomic Number = 52
1362 Atomic Symbol = Te
1363 Mass Number = 130
1364 Relative Atomic Mass = 129.906222748(12)
1365 Isotopic Composition = 0.3408(62)
1366 Standard Atomic Weight = 127.60(3)
1367 Notes = g
1368
1369 Atomic Number = 53
1370 Atomic Symbol = I
1371 Mass Number = 127
1372 Relative Atomic Mass = 126.9044719(39)
1373 Isotopic Composition = 1
1374 Standard Atomic Weight = 126.90447(3)
1375 Notes =
1376
1377 Atomic Number = 54
1378 Atomic Symbol = Xe
1379 Mass Number = 124
1380 Relative Atomic Mass = 123.9058920(19)
1381 Isotopic Composition = 0.000952(3)
1382 Standard Atomic Weight = 131.293(6)
1383 Notes = g,m
1384
1385 Atomic Number = 54
1386 Atomic Symbol = Xe
1387 Mass Number = 126
1388 Relative Atomic Mass = 125.9042983(38)
1389 Isotopic Composition = 0.000890(2)
1390 Standard Atomic Weight = 131.293(6)
1391 Notes = g,m
1392
1393 Atomic Number = 54
1394 Atomic Symbol = Xe
1395 Mass Number = 128
1396 Relative Atomic Mass = 127.9035310(11)
1397 Isotopic Composition = 0.019102(8)
1398 Standard Atomic Weight = 131.293(6)
1399 Notes = g,m
1400
1401 Atomic Number = 54
1402 Atomic Symbol = Xe
1403 Mass Number = 129
1404 Relative Atomic Mass = 128.9047808611(60)
1405 Isotopic Composition = 0.264006(82)
1406 Standard Atomic Weight = 131.293(6)
1407 Notes = g,m
1408
1409 Atomic Number = 54
1410 Atomic Symbol = Xe
1411 Mass Number = 130
1412 Relative Atomic Mass = 129.903509349(10)
1413 Isotopic Composition = 0.040710(13)
1414 Standard Atomic Weight = 131.293(6)
1415 Notes = g,m
1416
1417 Atomic Number = 54
1418 Atomic Symbol = Xe
1419 Mass Number = 131
1420 Relative Atomic Mass = 130.90508406(24)
1421 Isotopic Composition = 0.212324(30)
1422 Standard Atomic Weight = 131.293(6)
1423 Notes = g,m
1424
1425 Atomic Number = 54
1426 Atomic Symbol = Xe
1427 Mass Number = 132
1428 Relative Atomic Mass = 131.9041550856(56)
1429 Isotopic Composition = 0.269086(33)
1430 Standard Atomic Weight = 131.293(6)
1431 Notes = g,m
1432
1433 Atomic Number = 54
1434 Atomic Symbol = Xe
1435 Mass Number = 134
1436 Relative Atomic Mass = 133.90539466(90)
1437 Isotopic Composition = 0.104357(21)
1438 Standard Atomic Weight = 131.293(6)
1439 Notes = g,m
1440
1441 Atomic Number = 54
1442 Atomic Symbol = Xe
1443 Mass Number = 136
1444 Relative Atomic Mass = 135.907214484(11)
1445 Isotopic Composition = 0.088573(44)
1446 Standard Atomic Weight = 131.293(6)
1447 Notes = g,m
1448
1449 Atomic Number = 55
1450 Atomic Symbol = Cs
1451 Mass Number = 133
1452 Relative Atomic Mass = 132.9054519610(80)
1453 Isotopic Composition = 1
1454 Standard Atomic Weight = 132.90545196(6)
1455 Notes =
1456
1457 Atomic Number = 56
1458 Atomic Symbol = Ba
1459 Mass Number = 130
1460 Relative Atomic Mass = 129.9063207(28)
1461 Isotopic Composition = 0.00106(1)
1462 Standard Atomic Weight = 137.327(7)
1463 Notes =
1464
1465 Atomic Number = 56
1466 Atomic Symbol = Ba
1467 Mass Number = 132
1468 Relative Atomic Mass = 131.9050611(11)
1469 Isotopic Composition = 0.00101(1)
1470 Standard Atomic Weight = 137.327(7)
1471 Notes =
1472
1473 Atomic Number = 56
1474 Atomic Symbol = Ba
1475 Mass Number = 134
1476 Relative Atomic Mass = 133.90450818(30)
1477 Isotopic Composition = 0.02417(18)
1478 Standard Atomic Weight = 137.327(7)
1479 Notes =
1480
1481 Atomic Number = 56
1482 Atomic Symbol = Ba
1483 Mass Number = 135
1484 Relative Atomic Mass = 134.90568838(29)
1485 Isotopic Composition = 0.06592(12)
1486 Standard Atomic Weight = 137.327(7)
1487 Notes =
1488
1489 Atomic Number = 56
1490 Atomic Symbol = Ba
1491 Mass Number = 136
1492 Relative Atomic Mass = 135.90457573(29)
1493 Isotopic Composition = 0.07854(24)
1494 Standard Atomic Weight = 137.327(7)
1495 Notes =
1496
1497 Atomic Number = 56
1498 Atomic Symbol = Ba
1499 Mass Number = 137
1500 Relative Atomic Mass = 136.90582714(30)
1501 Isotopic Composition = 0.11232(24)
1502 Standard Atomic Weight = 137.327(7)
1503 Notes =
1504
1505 Atomic Number = 56
1506 Atomic Symbol = Ba
1507 Mass Number = 138
1508 Relative Atomic Mass = 137.90524700(31)
1509 Isotopic Composition = 0.71698(42)
1510 Standard Atomic Weight = 137.327(7)
1511 Notes =
1512
1513 Atomic Number = 57
1514 Atomic Symbol = La
1515 Mass Number = 138
1516 Relative Atomic Mass = 137.9071149(37)
1517 Isotopic Composition = 0.0008881(71)
1518 Standard Atomic Weight = 138.90547(7)
1519 Notes = g
1520
1521 Atomic Number = 57
1522 Atomic Symbol = La
1523 Mass Number = 139
1524 Relative Atomic Mass = 138.9063563(24)
1525 Isotopic Composition = 0.9991119(71)
1526 Standard Atomic Weight = 138.90547(7)
1527 Notes = g
1528
1529 Atomic Number = 58
1530 Atomic Symbol = Ce
1531 Mass Number = 136
1532 Relative Atomic Mass = 135.90712921(41)
1533 Isotopic Composition = 0.00185(2)
1534 Standard Atomic Weight = 140.116(1)
1535 Notes = g
1536
1537 Atomic Number = 58
1538 Atomic Symbol = Ce
1539 Mass Number = 138
1540 Relative Atomic Mass = 137.905991(11)
1541 Isotopic Composition = 0.00251(2)
1542 Standard Atomic Weight = 140.116(1)
1543 Notes = g
1544
1545 Atomic Number = 58
1546 Atomic Symbol = Ce
1547 Mass Number = 140
1548 Relative Atomic Mass = 139.9054431(23)
1549 Isotopic Composition = 0.88450(51)
1550 Standard Atomic Weight = 140.116(1)
1551 Notes = g
1552
1553 Atomic Number = 58
1554 Atomic Symbol = Ce
1555 Mass Number = 142
1556 Relative Atomic Mass = 141.9092504(29)
1557 Isotopic Composition = 0.11114(51)
1558 Standard Atomic Weight = 140.116(1)
1559 Notes = g
1560
1561 Atomic Number = 59
1562 Atomic Symbol = Pr
1563 Mass Number = 141
1564 Relative Atomic Mass = 140.9076576(23)
1565 Isotopic Composition = 1
1566 Standard Atomic Weight = 140.90766(2)
1567 Notes =
1568
1569 Atomic Number = 60
1570 Atomic Symbol = Nd
1571 Mass Number = 142
1572 Relative Atomic Mass = 141.9077290(20)
1573 Isotopic Composition = 0.27152(40)
1574 Standard Atomic Weight = 144.242(3)
1575 Notes = g
1576
1577 Atomic Number = 60
1578 Atomic Symbol = Nd
1579 Mass Number = 143
1580 Relative Atomic Mass = 142.9098200(20)
1581 Isotopic Composition = 0.12174(26)
1582 Standard Atomic Weight = 144.242(3)
1583 Notes = g
1584
1585 Atomic Number = 60
1586 Atomic Symbol = Nd
1587 Mass Number = 144
1588 Relative Atomic Mass = 143.9100930(20)
1589 Isotopic Composition = 0.23798(19)
1590 Standard Atomic Weight = 144.242(3)
1591 Notes = g
1592
1593 Atomic Number = 60
1594 Atomic Symbol = Nd
1595 Mass Number = 145
1596 Relative Atomic Mass = 144.9125793(20)
1597 Isotopic Composition = 0.08293(12)
1598 Standard Atomic Weight = 144.242(3)
1599 Notes = g
1600
1601 Atomic Number = 60
1602 Atomic Symbol = Nd
1603 Mass Number = 146
1604 Relative Atomic Mass = 145.9131226(20)
1605 Isotopic Composition = 0.17189(32)
1606 Standard Atomic Weight = 144.242(3)
1607 Notes = g
1608
1609 Atomic Number = 60
1610 Atomic Symbol = Nd
1611 Mass Number = 148
1612 Relative Atomic Mass = 147.9168993(26)
1613 Isotopic Composition = 0.05756(21)
1614 Standard Atomic Weight = 144.242(3)
1615 Notes = g
1616
1617 Atomic Number = 60
1618 Atomic Symbol = Nd
1619 Mass Number = 150
1620 Relative Atomic Mass = 149.9209022(18)
1621 Isotopic Composition = 0.05638(28)
1622 Standard Atomic Weight = 144.242(3)
1623 Notes = g
1624
1625 Atomic Number = 61
1626 Atomic Symbol = Pm
1627 Mass Number = 145
1628 Relative Atomic Mass = 144.9127559(33)
1629 Isotopic Composition =
1630 Standard Atomic Weight = [145]
1631 Notes =
1632
1633 Atomic Number = 61
1634 Atomic Symbol = Pm
1635 Mass Number = 147
1636 Relative Atomic Mass = 146.9151450(19)
1637 Isotopic Composition =
1638 Standard Atomic Weight = [145]
1639 Notes =
1640
1641 Atomic Number = 62
1642 Atomic Symbol = Sm
1643 Mass Number = 144
1644 Relative Atomic Mass = 143.9120065(21)
1645 Isotopic Composition = 0.0307(7)
1646 Standard Atomic Weight = 150.36(2)
1647 Notes = g
1648
1649 Atomic Number = 62
1650 Atomic Symbol = Sm
1651 Mass Number = 147
1652 Relative Atomic Mass = 146.9149044(19)
1653 Isotopic Composition = 0.1499(18)
1654 Standard Atomic Weight = 150.36(2)
1655 Notes = g
1656
1657 Atomic Number = 62
1658 Atomic Symbol = Sm
1659 Mass Number = 148
1660 Relative Atomic Mass = 147.9148292(19)
1661 Isotopic Composition = 0.1124(10)
1662 Standard Atomic Weight = 150.36(2)
1663 Notes = g
1664
1665 Atomic Number = 62
1666 Atomic Symbol = Sm
1667 Mass Number = 149
1668 Relative Atomic Mass = 148.9171921(18)
1669 Isotopic Composition = 0.1382(7)
1670 Standard Atomic Weight = 150.36(2)
1671 Notes = g
1672
1673 Atomic Number = 62
1674 Atomic Symbol = Sm
1675 Mass Number = 150
1676 Relative Atomic Mass = 149.9172829(18)
1677 Isotopic Composition = 0.0738(1)
1678 Standard Atomic Weight = 150.36(2)
1679 Notes = g
1680
1681 Atomic Number = 62
1682 Atomic Symbol = Sm
1683 Mass Number = 152
1684 Relative Atomic Mass = 151.9197397(18)
1685 Isotopic Composition = 0.2675(16)
1686 Standard Atomic Weight = 150.36(2)
1687 Notes = g
1688
1689 Atomic Number = 62
1690 Atomic Symbol = Sm
1691 Mass Number = 154
1692 Relative Atomic Mass = 153.9222169(20)
1693 Isotopic Composition = 0.2275(29)
1694 Standard Atomic Weight = 150.36(2)
1695 Notes = g
1696
1697 Atomic Number = 63
1698 Atomic Symbol = Eu
1699 Mass Number = 151
1700 Relative Atomic Mass = 150.9198578(18)
1701 Isotopic Composition = 0.4781(6)
1702 Standard Atomic Weight = 151.964(1)
1703 Notes = g
1704
1705 Atomic Number = 63
1706 Atomic Symbol = Eu
1707 Mass Number = 153
1708 Relative Atomic Mass = 152.9212380(18)
1709 Isotopic Composition = 0.5219(6)
1710 Standard Atomic Weight = 151.964(1)
1711 Notes = g
1712
1713 Atomic Number = 64
1714 Atomic Symbol = Gd
1715 Mass Number = 152
1716 Relative Atomic Mass = 151.9197995(18)
1717 Isotopic Composition = 0.0020(1)
1718 Standard Atomic Weight = 157.25(3)
1719 Notes = g
1720
1721 Atomic Number = 64
1722 Atomic Symbol = Gd
1723 Mass Number = 154
1724 Relative Atomic Mass = 153.9208741(17)
1725 Isotopic Composition = 0.0218(3)
1726 Standard Atomic Weight = 157.25(3)
1727 Notes = g
1728
1729 Atomic Number = 64
1730 Atomic Symbol = Gd
1731 Mass Number = 155
1732 Relative Atomic Mass = 154.9226305(17)
1733 Isotopic Composition = 0.1480(12)
1734 Standard Atomic Weight = 157.25(3)
1735 Notes = g
1736
1737 Atomic Number = 64
1738 Atomic Symbol = Gd
1739 Mass Number = 156
1740 Relative Atomic Mass = 155.9221312(17)
1741 Isotopic Composition = 0.2047(9)
1742 Standard Atomic Weight = 157.25(3)
1743 Notes = g
1744
1745 Atomic Number = 64
1746 Atomic Symbol = Gd
1747 Mass Number = 157
1748 Relative Atomic Mass = 156.9239686(17)
1749 Isotopic Composition = 0.1565(2)
1750 Standard Atomic Weight = 157.25(3)
1751 Notes = g
1752
1753 Atomic Number = 64
1754 Atomic Symbol = Gd
1755 Mass Number = 158
1756 Relative Atomic Mass = 157.9241123(17)
1757 Isotopic Composition = 0.2484(7)
1758 Standard Atomic Weight = 157.25(3)
1759 Notes = g
1760
1761 Atomic Number = 64
1762 Atomic Symbol = Gd
1763 Mass Number = 160
1764 Relative Atomic Mass = 159.9270624(18)
1765 Isotopic Composition = 0.2186(19)
1766 Standard Atomic Weight = 157.25(3)
1767 Notes = g
1768
1769 Atomic Number = 65
1770 Atomic Symbol = Tb
1771 Mass Number = 159
1772 Relative Atomic Mass = 158.9253547(19)
1773 Isotopic Composition = 1
1774 Standard Atomic Weight = 158.92535(2)
1775 Notes =
1776
1777 Atomic Number = 66
1778 Atomic Symbol = Dy
1779 Mass Number = 156
1780 Relative Atomic Mass = 155.9242847(17)
1781 Isotopic Composition = 0.00056(3)
1782 Standard Atomic Weight = 162.500(1)
1783 Notes = g
1784
1785 Atomic Number = 66
1786 Atomic Symbol = Dy
1787 Mass Number = 158
1788 Relative Atomic Mass = 157.9244159(31)
1789 Isotopic Composition = 0.00095(3)
1790 Standard Atomic Weight = 162.500(1)
1791 Notes = g
1792
1793 Atomic Number = 66
1794 Atomic Symbol = Dy
1795 Mass Number = 160
1796 Relative Atomic Mass = 159.9252046(20)
1797 Isotopic Composition = 0.02329(18)
1798 Standard Atomic Weight = 162.500(1)
1799 Notes = g
1800
1801 Atomic Number = 66
1802 Atomic Symbol = Dy
1803 Mass Number = 161
1804 Relative Atomic Mass = 160.9269405(20)
1805 Isotopic Composition = 0.18889(42)
1806 Standard Atomic Weight = 162.500(1)
1807 Notes = g
1808
1809 Atomic Number = 66
1810 Atomic Symbol = Dy
1811 Mass Number = 162
1812 Relative Atomic Mass = 161.9268056(20)
1813 Isotopic Composition = 0.25475(36)
1814 Standard Atomic Weight = 162.500(1)
1815 Notes = g
1816
1817 Atomic Number = 66
1818 Atomic Symbol = Dy
1819 Mass Number = 163
1820 Relative Atomic Mass = 162.9287383(20)
1821 Isotopic Composition = 0.24896(42)
1822 Standard Atomic Weight = 162.500(1)
1823 Notes = g
1824
1825 Atomic Number = 66
1826 Atomic Symbol = Dy
1827 Mass Number = 164
1828 Relative Atomic Mass = 163.9291819(20)
1829 Isotopic Composition = 0.28260(54)
1830 Standard Atomic Weight = 162.500(1)
1831 Notes = g
1832
1833 Atomic Number = 67
1834 Atomic Symbol = Ho
1835 Mass Number = 165
1836 Relative Atomic Mass = 164.9303288(21)
1837 Isotopic Composition = 1
1838 Standard Atomic Weight = 164.93033(2)
1839 Notes =
1840
1841 Atomic Number = 68
1842 Atomic Symbol = Er
1843 Mass Number = 162
1844 Relative Atomic Mass = 161.9287884(20)
1845 Isotopic Composition = 0.00139(5)
1846 Standard Atomic Weight = 167.259(3)
1847 Notes = g
1848
1849 Atomic Number = 68
1850 Atomic Symbol = Er
1851 Mass Number = 164
1852 Relative Atomic Mass = 163.9292088(20)
1853 Isotopic Composition = 0.01601(3)
1854 Standard Atomic Weight = 167.259(3)
1855 Notes = g
1856
1857 Atomic Number = 68
1858 Atomic Symbol = Er
1859 Mass Number = 166
1860 Relative Atomic Mass = 165.9302995(22)
1861 Isotopic Composition = 0.33503(36)
1862 Standard Atomic Weight = 167.259(3)
1863 Notes = g
1864
1865 Atomic Number = 68
1866 Atomic Symbol = Er
1867 Mass Number = 167
1868 Relative Atomic Mass = 166.9320546(22)
1869 Isotopic Composition = 0.22869(9)
1870 Standard Atomic Weight = 167.259(3)
1871 Notes = g
1872
1873 Atomic Number = 68
1874 Atomic Symbol = Er
1875 Mass Number = 168
1876 Relative Atomic Mass = 167.9323767(22)
1877 Isotopic Composition = 0.26978(18)
1878 Standard Atomic Weight = 167.259(3)
1879 Notes = g
1880
1881 Atomic Number = 68
1882 Atomic Symbol = Er
1883 Mass Number = 170
1884 Relative Atomic Mass = 169.9354702(26)
1885 Isotopic Composition = 0.14910(36)
1886 Standard Atomic Weight = 167.259(3)
1887 Notes = g
1888
1889 Atomic Number = 69
1890 Atomic Symbol = Tm
1891 Mass Number = 169
1892 Relative Atomic Mass = 168.9342179(22)
1893 Isotopic Composition = 1
1894 Standard Atomic Weight = 168.93422(2)
1895 Notes =
1896
1897 Atomic Number = 70
1898 Atomic Symbol = Yb
1899 Mass Number = 168
1900 Relative Atomic Mass = 167.9338896(22)
1901 Isotopic Composition = 0.00123(3)
1902 Standard Atomic Weight = 173.054(5)
1903 Notes = g
1904
1905 Atomic Number = 70
1906 Atomic Symbol = Yb
1907 Mass Number = 170
1908 Relative Atomic Mass = 169.9347664(22)
1909 Isotopic Composition = 0.02982(39)
1910 Standard Atomic Weight = 173.054(5)
1911 Notes = g
1912
1913 Atomic Number = 70
1914 Atomic Symbol = Yb
1915 Mass Number = 171
1916 Relative Atomic Mass = 170.9363302(22)
1917 Isotopic Composition = 0.1409(14)
1918 Standard Atomic Weight = 173.054(5)
1919 Notes = g
1920
1921 Atomic Number = 70
1922 Atomic Symbol = Yb
1923 Mass Number = 172
1924 Relative Atomic Mass = 171.9363859(22)
1925 Isotopic Composition = 0.2168(13)
1926 Standard Atomic Weight = 173.054(5)
1927 Notes = g
1928
1929 Atomic Number = 70
1930 Atomic Symbol = Yb
1931 Mass Number = 173
1932 Relative Atomic Mass = 172.9382151(22)
1933 Isotopic Composition = 0.16103(63)
1934 Standard Atomic Weight = 173.054(5)
1935 Notes = g
1936
1937 Atomic Number = 70
1938 Atomic Symbol = Yb
1939 Mass Number = 174
1940 Relative Atomic Mass = 173.9388664(22)
1941 Isotopic Composition = 0.32026(80)
1942 Standard Atomic Weight = 173.054(5)
1943 Notes = g
1944
1945 Atomic Number = 70
1946 Atomic Symbol = Yb
1947 Mass Number = 176
1948 Relative Atomic Mass = 175.9425764(24)
1949 Isotopic Composition = 0.12996(83)
1950 Standard Atomic Weight = 173.054(5)
1951 Notes = g
1952
1953 Atomic Number = 71
1954 Atomic Symbol = Lu
1955 Mass Number = 175
1956 Relative Atomic Mass = 174.9407752(20)
1957 Isotopic Composition = 0.97401(13)
1958 Standard Atomic Weight = 174.9668(1)
1959 Notes = g
1960
1961 Atomic Number = 71
1962 Atomic Symbol = Lu
1963 Mass Number = 176
1964 Relative Atomic Mass = 175.9426897(20)
1965 Isotopic Composition = 0.02599(13)
1966 Standard Atomic Weight = 174.9668(1)
1967 Notes = g
1968
1969 Atomic Number = 72
1970 Atomic Symbol = Hf
1971 Mass Number = 174
1972 Relative Atomic Mass = 173.9400461(28)
1973 Isotopic Composition = 0.0016(1)
1974 Standard Atomic Weight = 178.49(2)
1975 Notes =
1976
1977 Atomic Number = 72
1978 Atomic Symbol = Hf
1979 Mass Number = 176
1980 Relative Atomic Mass = 175.9414076(22)
1981 Isotopic Composition = 0.0526(7)
1982 Standard Atomic Weight = 178.49(2)
1983 Notes =
1984
1985 Atomic Number = 72
1986 Atomic Symbol = Hf
1987 Mass Number = 177
1988 Relative Atomic Mass = 176.9432277(20)
1989 Isotopic Composition = 0.1860(9)
1990 Standard Atomic Weight = 178.49(2)
1991 Notes =
1992
1993 Atomic Number = 72
1994 Atomic Symbol = Hf
1995 Mass Number = 178
1996 Relative Atomic Mass = 177.9437058(20)
1997 Isotopic Composition = 0.2728(7)
1998 Standard Atomic Weight = 178.49(2)
1999 Notes =
2000
2001 Atomic Number = 72
2002 Atomic Symbol = Hf
2003 Mass Number = 179
2004 Relative Atomic Mass = 178.9458232(20)
2005 Isotopic Composition = 0.1362(2)
2006 Standard Atomic Weight = 178.49(2)
2007 Notes =
2008
2009 Atomic Number = 72
2010 Atomic Symbol = Hf
2011 Mass Number = 180
2012 Relative Atomic Mass = 179.9465570(20)
2013 Isotopic Composition = 0.3508(16)
2014 Standard Atomic Weight = 178.49(2)
2015 Notes =
2016
2017 Atomic Number = 73
2018 Atomic Symbol = Ta
2019 Mass Number = 180
2020 Relative Atomic Mass = 179.9474648(24)
2021 Isotopic Composition = 0.0001201(32)
2022 Standard Atomic Weight = 180.94788(2)
2023 Notes =
2024
2025 Atomic Number = 73
2026 Atomic Symbol = Ta
2027 Mass Number = 181
2028 Relative Atomic Mass = 180.9479958(20)
2029 Isotopic Composition = 0.9998799(32)
2030 Standard Atomic Weight = 180.94788(2)
2031 Notes =
2032
2033 Atomic Number = 74
2034 Atomic Symbol = W
2035 Mass Number = 180
2036 Relative Atomic Mass = 179.9467108(20)
2037 Isotopic Composition = 0.0012(1)
2038 Standard Atomic Weight = 183.84(1)
2039 Notes =
2040
2041 Atomic Number = 74
2042 Atomic Symbol = W
2043 Mass Number = 182
2044 Relative Atomic Mass = 181.94820394(91)
2045 Isotopic Composition = 0.2650(16)
2046 Standard Atomic Weight = 183.84(1)
2047 Notes =
2048
2049 Atomic Number = 74
2050 Atomic Symbol = W
2051 Mass Number = 183
2052 Relative Atomic Mass = 182.95022275(90)
2053 Isotopic Composition = 0.1431(4)
2054 Standard Atomic Weight = 183.84(1)
2055 Notes =
2056
2057 Atomic Number = 74
2058 Atomic Symbol = W
2059 Mass Number = 184
2060 Relative Atomic Mass = 183.95093092(94)
2061 Isotopic Composition = 0.3064(2)
2062 Standard Atomic Weight = 183.84(1)
2063 Notes =
2064
2065 Atomic Number = 74
2066 Atomic Symbol = W
2067 Mass Number = 186
2068 Relative Atomic Mass = 185.9543628(17)
2069 Isotopic Composition = 0.2843(19)
2070 Standard Atomic Weight = 183.84(1)
2071 Notes =
2072
2073 Atomic Number = 75
2074 Atomic Symbol = Re
2075 Mass Number = 185
2076 Relative Atomic Mass = 184.9529545(13)
2077 Isotopic Composition = 0.3740(2)
2078 Standard Atomic Weight = 186.207(1)
2079 Notes =
2080
2081 Atomic Number = 75
2082 Atomic Symbol = Re
2083 Mass Number = 187
2084 Relative Atomic Mass = 186.9557501(16)
2085 Isotopic Composition = 0.6260(2)
2086 Standard Atomic Weight = 186.207(1)
2087 Notes =
2088
2089 Atomic Number = 76
2090 Atomic Symbol = Os
2091 Mass Number = 184
2092 Relative Atomic Mass = 183.9524885(14)
2093 Isotopic Composition = 0.0002(1)
2094 Standard Atomic Weight = 190.23(3)
2095 Notes = g
2096
2097 Atomic Number = 76
2098 Atomic Symbol = Os
2099 Mass Number = 186
2100 Relative Atomic Mass = 185.9538350(16)
2101 Isotopic Composition = 0.0159(3)
2102 Standard Atomic Weight = 190.23(3)
2103 Notes = g
2104
2105 Atomic Number = 76
2106 Atomic Symbol = Os
2107 Mass Number = 187
2108 Relative Atomic Mass = 186.9557474(16)
2109 Isotopic Composition = 0.0196(2)
2110 Standard Atomic Weight = 190.23(3)
2111 Notes = g
2112
2113 Atomic Number = 76
2114 Atomic Symbol = Os
2115 Mass Number = 188
2116 Relative Atomic Mass = 187.9558352(16)
2117 Isotopic Composition = 0.1324(8)
2118 Standard Atomic Weight = 190.23(3)
2119 Notes = g
2120
2121 Atomic Number = 76
2122 Atomic Symbol = Os
2123 Mass Number = 189
2124 Relative Atomic Mass = 188.9581442(17)
2125 Isotopic Composition = 0.1615(5)
2126 Standard Atomic Weight = 190.23(3)
2127 Notes = g
2128
2129 Atomic Number = 76
2130 Atomic Symbol = Os
2131 Mass Number = 190
2132 Relative Atomic Mass = 189.9584437(17)
2133 Isotopic Composition = 0.2626(2)
2134 Standard Atomic Weight = 190.23(3)
2135 Notes = g
2136
2137 Atomic Number = 76
2138 Atomic Symbol = Os
2139 Mass Number = 192
2140 Relative Atomic Mass = 191.9614770(29)
2141 Isotopic Composition = 0.4078(19)
2142 Standard Atomic Weight = 190.23(3)
2143 Notes = g
2144
2145 Atomic Number = 77
2146 Atomic Symbol = Ir
2147 Mass Number = 191
2148 Relative Atomic Mass = 190.9605893(21)
2149 Isotopic Composition = 0.373(2)
2150 Standard Atomic Weight = 192.217(3)
2151 Notes =
2152
2153 Atomic Number = 77
2154 Atomic Symbol = Ir
2155 Mass Number = 193
2156 Relative Atomic Mass = 192.9629216(21)
2157 Isotopic Composition = 0.627(2)
2158 Standard Atomic Weight = 192.217(3)
2159 Notes =
2160
2161 Atomic Number = 78
2162 Atomic Symbol = Pt
2163 Mass Number = 190
2164 Relative Atomic Mass = 189.9599297(63)
2165 Isotopic Composition = 0.00012(2)
2166 Standard Atomic Weight = 195.084(9)
2167 Notes =
2168
2169 Atomic Number = 78
2170 Atomic Symbol = Pt
2171 Mass Number = 192
2172 Relative Atomic Mass = 191.9610387(32)
2173 Isotopic Composition = 0.00782(24)
2174 Standard Atomic Weight = 195.084(9)
2175 Notes =
2176
2177 Atomic Number = 78
2178 Atomic Symbol = Pt
2179 Mass Number = 194
2180 Relative Atomic Mass = 193.9626809(10)
2181 Isotopic Composition = 0.3286(40)
2182 Standard Atomic Weight = 195.084(9)
2183 Notes =
2184
2185 Atomic Number = 78
2186 Atomic Symbol = Pt
2187 Mass Number = 195
2188 Relative Atomic Mass = 194.9647917(10)
2189 Isotopic Composition = 0.3378(24)
2190 Standard Atomic Weight = 195.084(9)
2191 Notes =
2192
2193 Atomic Number = 78
2194 Atomic Symbol = Pt
2195 Mass Number = 196
2196 Relative Atomic Mass = 195.96495209(99)
2197 Isotopic Composition = 0.2521(34)
2198 Standard Atomic Weight = 195.084(9)
2199 Notes =
2200
2201 Atomic Number = 78
2202 Atomic Symbol = Pt
2203 Mass Number = 198
2204 Relative Atomic Mass = 197.9678949(23)
2205 Isotopic Composition = 0.07356(130)
2206 Standard Atomic Weight = 195.084(9)
2207 Notes =
2208
2209 Atomic Number = 79
2210 Atomic Symbol = Au
2211 Mass Number = 197
2212 Relative Atomic Mass = 196.96656879(71)
2213 Isotopic Composition = 1
2214 Standard Atomic Weight = 196.966569(5)
2215 Notes =
2216
2217 Atomic Number = 80
2218 Atomic Symbol = Hg
2219 Mass Number = 196
2220 Relative Atomic Mass = 195.9658326(32)
2221 Isotopic Composition = 0.0015(1)
2222 Standard Atomic Weight = 200.592(3)
2223 Notes =
2224
2225 Atomic Number = 80
2226 Atomic Symbol = Hg
2227 Mass Number = 198
2228 Relative Atomic Mass = 197.96676860(52)
2229 Isotopic Composition = 0.0997(20)
2230 Standard Atomic Weight = 200.592(3)
2231 Notes =
2232
2233 Atomic Number = 80
2234 Atomic Symbol = Hg
2235 Mass Number = 199
2236 Relative Atomic Mass = 198.96828064(46)
2237 Isotopic Composition = 0.1687(22)
2238 Standard Atomic Weight = 200.592(3)
2239 Notes =
2240
2241 Atomic Number = 80
2242 Atomic Symbol = Hg
2243 Mass Number = 200
2244 Relative Atomic Mass = 199.96832659(47)
2245 Isotopic Composition = 0.2310(19)
2246 Standard Atomic Weight = 200.592(3)
2247 Notes =
2248
2249 Atomic Number = 80
2250 Atomic Symbol = Hg
2251 Mass Number = 201
2252 Relative Atomic Mass = 200.97030284(69)
2253 Isotopic Composition = 0.1318(9)
2254 Standard Atomic Weight = 200.592(3)
2255 Notes =
2256
2257 Atomic Number = 80
2258 Atomic Symbol = Hg
2259 Mass Number = 202
2260 Relative Atomic Mass = 201.97064340(69)
2261 Isotopic Composition = 0.2986(26)
2262 Standard Atomic Weight = 200.592(3)
2263 Notes =
2264
2265 Atomic Number = 80
2266 Atomic Symbol = Hg
2267 Mass Number = 204
2268 Relative Atomic Mass = 203.97349398(53)
2269 Isotopic Composition = 0.0687(15)
2270 Standard Atomic Weight = 200.592(3)
2271 Notes =
2272
2273 Atomic Number = 81
2274 Atomic Symbol = Tl
2275 Mass Number = 203
2276 Relative Atomic Mass = 202.9723446(14)
2277 Isotopic Composition = 0.2952(1)
2278 Standard Atomic Weight = [204.382,204.385]
2279 Notes =
2280
2281 Atomic Number = 81
2282 Atomic Symbol = Tl
2283 Mass Number = 205
2284 Relative Atomic Mass = 204.9744278(14)
2285 Isotopic Composition = 0.7048(1)
2286 Standard Atomic Weight = [204.382,204.385]
2287 Notes =
2288
2289 Atomic Number = 82
2290 Atomic Symbol = Pb
2291 Mass Number = 204
2292 Relative Atomic Mass = 203.9730440(13)
2293 Isotopic Composition = 0.014(1)
2294 Standard Atomic Weight = 207.2(1)
2295 Notes = g,r
2296
2297 Atomic Number = 82
2298 Atomic Symbol = Pb
2299 Mass Number = 206
2300 Relative Atomic Mass = 205.9744657(13)
2301 Isotopic Composition = 0.241(1)
2302 Standard Atomic Weight = 207.2(1)
2303 Notes = g,r
2304
2305 Atomic Number = 82
2306 Atomic Symbol = Pb
2307 Mass Number = 207
2308 Relative Atomic Mass = 206.9758973(13)
2309 Isotopic Composition = 0.221(1)
2310 Standard Atomic Weight = 207.2(1)
2311 Notes = g,r
2312
2313 Atomic Number = 82
2314 Atomic Symbol = Pb
2315 Mass Number = 208
2316 Relative Atomic Mass = 207.9766525(13)
2317 Isotopic Composition = 0.524(1)
2318 Standard Atomic Weight = 207.2(1)
2319 Notes = g,r
2320
2321 Atomic Number = 83
2322 Atomic Symbol = Bi
2323 Mass Number = 209
2324 Relative Atomic Mass = 208.9803991(16)
2325 Isotopic Composition = 1
2326 Standard Atomic Weight = 208.98040(1)
2327 Notes =
2328
2329 Atomic Number = 84
2330 Atomic Symbol = Po
2331 Mass Number = 209
2332 Relative Atomic Mass = 208.9824308(20)
2333 Isotopic Composition =
2334 Standard Atomic Weight = [209]
2335 Notes =
2336
2337 Atomic Number = 84
2338 Atomic Symbol = Po
2339 Mass Number = 210
2340 Relative Atomic Mass = 209.9828741(13)
2341 Isotopic Composition =
2342 Standard Atomic Weight = [209]
2343 Notes =
2344
2345 Atomic Number = 85
2346 Atomic Symbol = At
2347 Mass Number = 210
2348 Relative Atomic Mass = 209.9871479(83)
2349 Isotopic Composition =
2350 Standard Atomic Weight = [210]
2351 Notes =
2352
2353 Atomic Number = 85
2354 Atomic Symbol = At
2355 Mass Number = 211
2356 Relative Atomic Mass = 210.9874966(30)
2357 Isotopic Composition =
2358 Standard Atomic Weight = [210]
2359 Notes =
2360
2361 Atomic Number = 86
2362 Atomic Symbol = Rn
2363 Mass Number = 211
2364 Relative Atomic Mass = 210.9906011(73)
2365 Isotopic Composition =
2366 Standard Atomic Weight = [222]
2367 Notes =
2368
2369 Atomic Number = 86
2370 Atomic Symbol = Rn
2371 Mass Number = 220
2372 Relative Atomic Mass = 220.0113941(23)
2373 Isotopic Composition =
2374 Standard Atomic Weight = [222]
2375 Notes =
2376
2377 Atomic Number = 86
2378 Atomic Symbol = Rn
2379 Mass Number = 222
2380 Relative Atomic Mass = 222.0175782(25)
2381 Isotopic Composition =
2382 Standard Atomic Weight = [222]
2383 Notes =
2384
2385 Atomic Number = 87
2386 Atomic Symbol = Fr
2387 Mass Number = 223
2388 Relative Atomic Mass = 223.0197360(25)
2389 Isotopic Composition =
2390 Standard Atomic Weight = [223]
2391 Notes =
2392
2393 Atomic Number = 88
2394 Atomic Symbol = Ra
2395 Mass Number = 223
2396 Relative Atomic Mass = 223.0185023(27)
2397 Isotopic Composition =
2398 Standard Atomic Weight = [226]
2399 Notes =
2400
2401 Atomic Number = 88
2402 Atomic Symbol = Ra
2403 Mass Number = 224
2404 Relative Atomic Mass = 224.0202120(23)
2405 Isotopic Composition =
2406 Standard Atomic Weight = [226]
2407 Notes =
2408
2409 Atomic Number = 88
2410 Atomic Symbol = Ra
2411 Mass Number = 226
2412 Relative Atomic Mass = 226.0254103(25)
2413 Isotopic Composition =
2414 Standard Atomic Weight = [226]
2415 Notes =
2416
2417 Atomic Number = 88
2418 Atomic Symbol = Ra
2419 Mass Number = 228
2420 Relative Atomic Mass = 228.0310707(26)
2421 Isotopic Composition =
2422 Standard Atomic Weight = [226]
2423 Notes =
2424
2425 Atomic Number = 89
2426 Atomic Symbol = Ac
2427 Mass Number = 227
2428 Relative Atomic Mass = 227.0277523(25)
2429 Isotopic Composition =
2430 Standard Atomic Weight = [227]
2431 Notes =
2432
2433 Atomic Number = 90
2434 Atomic Symbol = Th
2435 Mass Number = 230
2436 Relative Atomic Mass = 230.0331341(19)
2437 Isotopic Composition =
2438 Standard Atomic Weight = 232.0377(4)
2439 Notes = g
2440
2441 Atomic Number = 90
2442 Atomic Symbol = Th
2443 Mass Number = 232
2444 Relative Atomic Mass = 232.0380558(21)
2445 Isotopic Composition = 1
2446 Standard Atomic Weight = 232.0377(4)
2447 Notes = g
2448
2449 Atomic Number = 91
2450 Atomic Symbol = Pa
2451 Mass Number = 231
2452 Relative Atomic Mass = 231.0358842(24)
2453 Isotopic Composition = 1
2454 Standard Atomic Weight = 231.03588(2)
2455 Notes =
2456
2457 Atomic Number = 92
2458 Atomic Symbol = U
2459 Mass Number = 233
2460 Relative Atomic Mass = 233.0396355(29)
2461 Isotopic Composition =
2462 Standard Atomic Weight = 238.02891(3)
2463 Notes = g,m
2464
2465 Atomic Number = 92
2466 Atomic Symbol = U
2467 Mass Number = 234
2468 Relative Atomic Mass = 234.0409523(19)
2469 Isotopic Composition = 0.000054(5)
2470 Standard Atomic Weight = 238.02891(3)
2471 Notes = g,m
2472
2473 Atomic Number = 92
2474 Atomic Symbol = U
2475 Mass Number = 235
2476 Relative Atomic Mass = 235.0439301(19)
2477 Isotopic Composition = 0.007204(6)
2478 Standard Atomic Weight = 238.02891(3)
2479 Notes = g,m
2480
2481 Atomic Number = 92
2482 Atomic Symbol = U
2483 Mass Number = 236
2484 Relative Atomic Mass = 236.0455682(19)
2485 Isotopic Composition =
2486 Standard Atomic Weight = 238.02891(3)
2487 Notes = g,m
2488
2489 Atomic Number = 92
2490 Atomic Symbol = U
2491 Mass Number = 238
2492 Relative Atomic Mass = 238.0507884(20)
2493 Isotopic Composition = 0.992742(10)
2494 Standard Atomic Weight = 238.02891(3)
2495 Notes = g,m
2496
2497 Atomic Number = 93
2498 Atomic Symbol = Np
2499 Mass Number = 236
2500 Relative Atomic Mass = 236.046570(54)
2501 Isotopic Composition =
2502 Standard Atomic Weight = [237]
2503 Notes =
2504
2505 Atomic Number = 93
2506 Atomic Symbol = Np
2507 Mass Number = 237
2508 Relative Atomic Mass = 237.0481736(19)
2509 Isotopic Composition =
2510 Standard Atomic Weight = [237]
2511 Notes =
2512
2513 Atomic Number = 94
2514 Atomic Symbol = Pu
2515 Mass Number = 238
2516 Relative Atomic Mass = 238.0495601(19)
2517 Isotopic Composition =
2518 Standard Atomic Weight = [244]
2519 Notes =
2520
2521 Atomic Number = 94
2522 Atomic Symbol = Pu
2523 Mass Number = 239
2524 Relative Atomic Mass = 239.0521636(19)
2525 Isotopic Composition =
2526 Standard Atomic Weight = [244]
2527 Notes =
2528
2529 Atomic Number = 94
2530 Atomic Symbol = Pu
2531 Mass Number = 240
2532 Relative Atomic Mass = 240.0538138(19)
2533 Isotopic Composition =
2534 Standard Atomic Weight = [244]
2535 Notes =
2536
2537 Atomic Number = 94
2538 Atomic Symbol = Pu
2539 Mass Number = 241
2540 Relative Atomic Mass = 241.0568517(19)
2541 Isotopic Composition =
2542 Standard Atomic Weight = [244]
2543 Notes =
2544
2545 Atomic Number = 94
2546 Atomic Symbol = Pu
2547 Mass Number = 242
2548 Relative Atomic Mass = 242.0587428(20)
2549 Isotopic Composition =
2550 Standard Atomic Weight = [244]
2551 Notes =
2552
2553 Atomic Number = 94
2554 Atomic Symbol = Pu
2555 Mass Number = 244
2556 Relative Atomic Mass = 244.0642053(56)
2557 Isotopic Composition =
2558 Standard Atomic Weight = [244]
2559 Notes =
2560
2561 Atomic Number = 95
2562 Atomic Symbol = Am
2563 Mass Number = 241
2564 Relative Atomic Mass = 241.0568293(19)
2565 Isotopic Composition =
2566 Standard Atomic Weight =
2567 Notes =
2568
2569 Atomic Number = 95
2570 Atomic Symbol = Am
2571 Mass Number = 243
2572 Relative Atomic Mass = 243.0613813(24)
2573 Isotopic Composition =
2574 Standard Atomic Weight =
2575 Notes =
2576
2577 Atomic Number = 96
2578 Atomic Symbol = Cm
2579 Mass Number = 243
2580 Relative Atomic Mass = 243.0613893(22)
2581 Isotopic Composition =
2582 Standard Atomic Weight =
2583 Notes =
2584
2585 Atomic Number = 96
2586 Atomic Symbol = Cm
2587 Mass Number = 244
2588 Relative Atomic Mass = 244.0627528(19)
2589 Isotopic Composition =
2590 Standard Atomic Weight =
2591 Notes =
2592
2593 Atomic Number = 96
2594 Atomic Symbol = Cm
2595 Mass Number = 245
2596 Relative Atomic Mass = 245.0654915(22)
2597 Isotopic Composition =
2598 Standard Atomic Weight =
2599 Notes =
2600
2601 Atomic Number = 96
2602 Atomic Symbol = Cm
2603 Mass Number = 246
2604 Relative Atomic Mass = 246.0672238(22)
2605 Isotopic Composition =
2606 Standard Atomic Weight =
2607 Notes =
2608
2609 Atomic Number = 96
2610 Atomic Symbol = Cm
2611 Mass Number = 247
2612 Relative Atomic Mass = 247.0703541(47)
2613 Isotopic Composition =
2614 Standard Atomic Weight =
2615 Notes =
2616
2617 Atomic Number = 96
2618 Atomic Symbol = Cm
2619 Mass Number = 248
2620 Relative Atomic Mass = 248.0723499(56)
2621 Isotopic Composition =
2622 Standard Atomic Weight =
2623 Notes =
2624
2625 Atomic Number = 97
2626 Atomic Symbol = Bk
2627 Mass Number = 247
2628 Relative Atomic Mass = 247.0703073(59)
2629 Isotopic Composition =
2630 Standard Atomic Weight =
2631 Notes =
2632
2633 Atomic Number = 97
2634 Atomic Symbol = Bk
2635 Mass Number = 249
2636 Relative Atomic Mass = 249.0749877(27)
2637 Isotopic Composition =
2638 Standard Atomic Weight =
2639 Notes =
2640
2641 Atomic Number = 98
2642 Atomic Symbol = Cf
2643 Mass Number = 249
2644 Relative Atomic Mass = 249.0748539(23)
2645 Isotopic Composition =
2646 Standard Atomic Weight =
2647 Notes =
2648
2649 Atomic Number = 98
2650 Atomic Symbol = Cf
2651 Mass Number = 250
2652 Relative Atomic Mass = 250.0764062(22)
2653 Isotopic Composition =
2654 Standard Atomic Weight =
2655 Notes =
2656
2657 Atomic Number = 98
2658 Atomic Symbol = Cf
2659 Mass Number = 251
2660 Relative Atomic Mass = 251.0795886(48)
2661 Isotopic Composition =
2662 Standard Atomic Weight =
2663 Notes =
2664
2665 Atomic Number = 98
2666 Atomic Symbol = Cf
2667 Mass Number = 252
2668 Relative Atomic Mass = 252.0816272(56)
2669 Isotopic Composition =
2670 Standard Atomic Weight =
2671 Notes =
2672
2673 Atomic Number = 99
2674 Atomic Symbol = Es
2675 Mass Number = 252
2676 Relative Atomic Mass = 252.082980(54)
2677 Isotopic Composition =
2678 Standard Atomic Weight =
2679 Notes =
2680
2681 Atomic Number = 100
2682 Atomic Symbol = Fm
2683 Mass Number = 257
2684 Relative Atomic Mass = 257.0951061(69)
2685 Isotopic Composition =
2686 Standard Atomic Weight =
2687 Notes =
2688
2689 Atomic Number = 101
2690 Atomic Symbol = Md
2691 Mass Number = 258
2692 Relative Atomic Mass = 258.0984315(50)
2693 Isotopic Composition =
2694 Standard Atomic Weight =
2695 Notes =
2696
2697 Atomic Number = 101
2698 Atomic Symbol = Md
2699 Mass Number = 260
2700 Relative Atomic Mass = 260.10365(34#)
2701 Isotopic Composition =
2702 Standard Atomic Weight =
2703 Notes =
2704
2705 Atomic Number = 102
2706 Atomic Symbol = No
2707 Mass Number = 259
2708 Relative Atomic Mass = 259.10103(11#)
2709 Isotopic Composition =
2710 Standard Atomic Weight =
2711 Notes =
2712
2713 Atomic Number = 103
2714 Atomic Symbol = Lr
2715 Mass Number = 262
2716 Relative Atomic Mass = 262.10961(22#)
2717 Isotopic Composition =
2718 Standard Atomic Weight =
2719 Notes =
2720
2721 Atomic Number = 104
2722 Atomic Symbol = Rf
2723 Mass Number = 267
2724 Relative Atomic Mass = 267.12179(62#)
2725 Isotopic Composition =
2726 Standard Atomic Weight =
2727 Notes =
2728
2729 Atomic Number = 105
2730 Atomic Symbol = Db
2731 Mass Number = 268
2732 Relative Atomic Mass = 268.12567(57#)
2733 Isotopic Composition =
2734 Standard Atomic Weight =
2735 Notes =
2736
2737 Atomic Number = 106
2738 Atomic Symbol = Sg
2739 Mass Number = 271
2740 Relative Atomic Mass = 271.13393(63#)
2741 Isotopic Composition =
2742 Standard Atomic Weight =
2743 Notes =
2744
2745 Atomic Number = 107
2746 Atomic Symbol = Bh
2747 Mass Number = 272
2748 Relative Atomic Mass = 272.13826(58#)
2749 Isotopic Composition =
2750 Standard Atomic Weight =
2751 Notes =
2752
2753 Atomic Number = 108
2754 Atomic Symbol = Hs
2755 Mass Number = 270
2756 Relative Atomic Mass = 270.13429(27#)
2757 Isotopic Composition =
2758 Standard Atomic Weight =
2759 Notes =
2760
2761 Atomic Number = 109
2762 Atomic Symbol = Mt
2763 Mass Number = 276
2764 Relative Atomic Mass = 276.15159(59#)
2765 Isotopic Composition =
2766 Standard Atomic Weight =
2767 Notes =
2768
2769 Atomic Number = 110
2770 Atomic Symbol = Ds
2771 Mass Number = 281
2772 Relative Atomic Mass = 281.16451(59#)
2773 Isotopic Composition =
2774 Standard Atomic Weight =
2775 Notes =
2776
2777 Atomic Number = 111
2778 Atomic Symbol = Rg
2779 Mass Number = 280
2780 Relative Atomic Mass = 280.16514(61#)
2781 Isotopic Composition =
2782 Standard Atomic Weight =
2783 Notes =
2784
2785 Atomic Number = 112
2786 Atomic Symbol = Cn
2787 Mass Number = 285
2788 Relative Atomic Mass = 285.17712(60#)
2789 Isotopic Composition =
2790 Standard Atomic Weight =
2791 Notes =
2792
2793 Atomic Number = 113
2794 Atomic Symbol = Nh
2795 Mass Number = 284
2796 Relative Atomic Mass = 284.17873(62#)
2797 Isotopic Composition =
2798 Standard Atomic Weight =
2799 Notes =
2800
2801 Atomic Number = 114
2802 Atomic Symbol = Fl
2803 Mass Number = 289
2804 Relative Atomic Mass = 289.19042(60#)
2805 Isotopic Composition =
2806 Standard Atomic Weight =
2807 Notes =
2808
2809 Atomic Number = 115
2810 Atomic Symbol = Mc
2811 Mass Number = 288
2812 Relative Atomic Mass = 288.19274(62#)
2813 Isotopic Composition =
2814 Standard Atomic Weight =
2815 Notes =
2816
2817 Atomic Number = 116
2818 Atomic Symbol = Lv
2819 Mass Number = 293
2820 Relative Atomic Mass = 293.20449(60#)
2821 Isotopic Composition =
2822 Standard Atomic Weight =
2823 Notes =
2824
2825 Atomic Number = 117
2826 Atomic Symbol = Ts
2827 Mass Number = 292
2828 Relative Atomic Mass = 292.20746(75#)
2829 Isotopic Composition =
2830 Standard Atomic Weight =
2831 Notes =
2832
2833 Atomic Number = 118
2834 Atomic Symbol = Og
2835 Mass Number = 294
2836 Relative Atomic Mass = 294.21392(71#)
2837 Isotopic Composition =
2838 Standard Atomic Weight =
2839 Notes =
+0
-795
xrayutilities/materials/database.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 module to handle the access to the optical parameters database
20 """
21
22 import re
23
24 import h5py
25 import numpy
26 import scipy.constants
27
28
29 class DataBase(object):
30
31 def __init__(self, fname):
32 self.fname = fname
33 self.h5file = None # HDF5 file object holding the database
34 self.h5group = None # Group pointing to the actual element
35 self.f0_params = None
36 self.f1_en = None
37 self.f1 = None
38 self.f2_en = None
39 self.f2 = None
40 self.weight = None
41 self.color = None
42 self.radius = numpy.nan
43 self.matname = None
44
45 def Create(self, dbname, dbdesc):
46 """
47 Creates a new database. If the database file already exists
48 its content is delete.
49
50 Parameters
51 ----------
52 dbname : str
53 name of the database
54 dbdesc : str
55 a short description of the database
56 """
57 if self.h5file is not None:
58 print("database already opened - "
59 "close first to create new database")
60 return None
61
62 # tryp to open the database file
63 try:
64 self.h5file = h5py.File(self.fname, 'w')
65 except OSError:
66 print('cannot create database file %s!' % (self.fname))
67 return None
68
69 # set attributes to the root group with database name and
70 # description
71 self.h5file.attrs['DBName'] = dbname
72 self.h5file.attrs['DBDesc'] = dbdesc
73
74 def Open(self, mode='r'):
75 """
76 Open an existing database file.
77 """
78 if self.h5file is not None:
79 print('database already opened - '
80 'close first to open new database!')
81 return
82
83 try:
84 self.h5file = h5py.File(self.fname, mode)
85 except OSError:
86 print("cannot open database file %s!" % (self.fname))
87
88 def Close(self):
89 """
90 Close an opend database file.
91 """
92 if self.h5file is None:
93 print("no database file opened!")
94 return
95
96 self.h5file.close()
97 self.h5file = None
98
99 def CreateMaterial(self, name, description):
100 """
101 This method creates a new material. If the material group already
102 exists the procedure is aborted.
103
104 Parameters
105 ----------
106 name : str
107 name of the material
108 description : str
109 description of the material
110 """
111 if self.h5file is None:
112 print("no database file opened!")
113 return
114
115 if name in self.h5file:
116 # if the material node already exists a warning message is printed
117 print("material node already exists")
118 else:
119 g = self.h5file.create_group(name)
120 g.attrs['name'] = description
121
122 def SetWeight(self, weight):
123 """
124 Save weight of the element as float
125
126 Parameters
127 ----------
128 weight : float
129 atomic standard weight of the element
130 """
131 if not isinstance(weight, float):
132 raise TypeError("weight parameter must be a float!")
133
134 self.h5group.attrs['atomic_standard_weight'] = weight
135 self.h5file.flush()
136
137 def SetColor(self, color):
138 """
139 Save color of the element for visualization
140
141 Parameters
142 ----------
143 color : tuple, str
144 matplotlib color for the element
145 """
146 if not isinstance(color, (tuple, str)):
147 raise TypeError("color parameter must be a tuple or str!")
148
149 self.h5group.attrs['color'] = color
150 self.h5file.flush()
151
152 def SetRadius(self, radius):
153 """
154 Save atomic radius for visualization
155
156 Parameters
157 ----------
158 radius: float
159 atomic radius in Angstrom
160 """
161 if not isinstance(radius, float):
162 raise TypeError("radius parameter must be a float!")
163
164 self.h5group.attrs['atomic_radius'] = radius
165 self.h5file.flush()
166
167 def SetF0(self, parameters, subset='default'):
168 """
169 Save f0 fit parameters for the set material. The fit parameters
170 are stored in the following order:
171 c, a1, b1,......., a4, b4
172
173 Parameters
174 ----------
175 parameters : list or array-like
176 fit parameters
177 subset : str, optional
178 name the f0 dataset
179 """
180 if isinstance(parameters, list):
181 p = numpy.array(parameters, dtype=numpy.float32)
182 elif isinstance(parameters, numpy.ndarray):
183 p = parameters.astype(numpy.float32)
184 else:
185 raise TypeError("f0 fit parameters must be a "
186 "list or a numpy array!")
187
188 if not subset:
189 subset = 'default'
190
191 try:
192 del self.h5group['f0/%s' % subset]
193 except KeyError:
194 pass
195
196 self.h5group.create_dataset('f0/%s' % subset, data=p)
197 self.h5file.flush()
198
199 def SetF1F2(self, en, f1, f2):
200 """
201 Set f1, f2 values for the active material.
202
203 Parameters
204 ----------
205 en : list or array-like
206 energy in (eV)
207 f1 : list or array-like
208 f1 values
209 f2 : list or array-like
210 f2 values
211 """
212 if isinstance(en, (list, tuple)):
213 end = numpy.array(en, dtype=numpy.float32)
214 elif isinstance(en, numpy.ndarray):
215 end = en.astype(numpy.float32)
216 else:
217 raise TypeError("energy values must be a list or a numpy array!")
218
219 if isinstance(f1, (list, tuple)):
220 f1d = numpy.array(f1, dtype=numpy.float32)
221 elif isinstance(f1, numpy.ndarray):
222 f1d = f1.astype(numpy.float32)
223 else:
224 raise TypeError("f1 values must be a list or a numpy array!")
225
226 if isinstance(f2, (list, tuple)):
227 f2d = numpy.array(f2, dtype=numpy.float32)
228 elif isinstance(f2, numpy.ndarray):
229 f2d = f2.astype(numpy.float32)
230 else:
231 raise TypeError("f2 values must be a list or a numpy array!")
232
233 try:
234 del self.h5group['en_f12']
235 except KeyError:
236 pass
237
238 try:
239 del self.h5group['f1']
240 except KeyError:
241 pass
242
243 try:
244 del self.h5group['f2']
245 except KeyError:
246 pass
247
248 self.h5group.create_dataset('en_f12', data=end)
249 self.h5group.create_dataset('f1', data=f1d)
250 self.h5group.create_dataset('f2', data=f2d)
251 self.h5file.flush()
252
253 def SetMaterial(self, name):
254 """
255 Set a particular material in the database as the actual material. All
256 operations like setting and getting optical constants are done for this
257 particular material.
258
259 Parameters
260 ----------
261 name : str
262 name of the material
263 """
264 if self.matname == name:
265 return
266 try:
267 self.h5group = self.h5file[name]
268 except KeyError:
269 print("XU.materials.database: material '%s' not existing!" % name)
270
271 try:
272 self.f0_params = self.h5group['f0']
273 except KeyError:
274 self.f0_params = None
275 try:
276 self.f1_en = self.h5group['en_f12']
277 self.f1 = self.h5group['f1']
278 except KeyError:
279 self.f1_en = None
280 self.f1 = None
281 try:
282 self.f2_en = self.h5group['en_f12']
283 self.f2 = self.h5group['f2']
284 except KeyError:
285 self.f2_en = None
286 self.f2 = None
287 try:
288 self.weight = self.h5group.attrs['atomic_standard_weight']
289 except KeyError:
290 self.weight = None
291 try:
292 self.radius = self.h5group.attrs['atomic_radius']
293 except KeyError:
294 self.radius = numpy.nan
295 try:
296 self.color = self.h5group.attrs['color']
297 except KeyError:
298 self.color = None
299 self.matname = name
300
301 def GetF0(self, q, dset='default'):
302 """
303 Obtain the f0 scattering factor component for a particular
304 momentum transfer q.
305
306 Parameters
307 ----------
308 q : float or array-like
309 momentum transfer
310 dset : str, optional
311 specifies which dataset (different oxidation states)
312 should be used
313 """
314 # get parameters from file
315 if not dset:
316 dset = 'default'
317 f0_params = self.f0_params[dset]
318 # calculate f0
319 if isinstance(q, (numpy.ndarray, list, tuple)):
320 ql = numpy.asarray(q)
321 f0 = f0_params[0] * numpy.ones(ql.shape)
322 else:
323 ql = q
324 f0 = f0_params[0]
325 k = ql / (4. * numpy.pi)
326
327 for i in range(1, len(f0_params) - 1, 2):
328 a = f0_params[i]
329 b = f0_params[i + 1]
330 f0 += a * numpy.exp(-b * k ** 2)
331
332 return f0
333
334 def GetF1(self, en):
335 """
336 Return the second, energy dependent, real part of the scattering
337 factor for a certain energy en.
338
339 Parameters
340 ----------
341 en : float or array-like
342 energy
343 """
344 if1 = numpy.interp(en, self.f1_en, self.f1,
345 left=numpy.nan, right=numpy.nan)
346
347 return if1
348
349 def GetF2(self, en):
350 """
351 Return the imaginary part of the scattering
352 factor for a certain energy en.
353
354 Parameters
355 ----------
356 en : float or array-like
357 energy
358 """
359 if2 = numpy.interp(en, self.f2_en, self.f2,
360 left=numpy.nan, right=numpy.nan)
361
362 return if2
363
364
365 def init_material_db(db):
366 db.CreateMaterial("dummy", "Dummy atom")
367 db.CreateMaterial("H", "Hydrogen")
368 db.CreateMaterial("D", "Deuterium")
369 db.CreateMaterial("T", "Tritium")
370 db.CreateMaterial("He", "Helium")
371 db.CreateMaterial("Li", "Lithium")
372 db.CreateMaterial("Be", "Berylium")
373 db.CreateMaterial("B", "Bor")
374 db.CreateMaterial("C", "Carbon")
375 db.CreateMaterial("N", "Nitrogen")
376 db.CreateMaterial("O", "Oxygen")
377 db.CreateMaterial("F", "Flourine")
378 db.CreateMaterial("Ne", "Neon")
379 db.CreateMaterial("Na", "Sodium")
380 db.CreateMaterial("Mg", "Magnesium")
381 db.CreateMaterial("Al", "Aluminium")
382 db.CreateMaterial("Si", "Silicon")
383 db.CreateMaterial("P", "Phosphorus")
384 db.CreateMaterial("S", "Sulfur")
385 db.CreateMaterial("Cl", "Chlorine")
386 db.CreateMaterial("Ar", "Argon")
387 db.CreateMaterial("K", "Potassium")
388 db.CreateMaterial("Ca", "Calcium")
389 db.CreateMaterial("Sc", "Scandium")
390 db.CreateMaterial("Ti", "Titanium")
391 db.CreateMaterial("V", "Vanadium")
392 db.CreateMaterial("Cr", "Chromium")
393 db.CreateMaterial("Mn", "Manganese")
394 db.CreateMaterial("Fe", "Iron")
395 db.CreateMaterial("Co", "Cobalt")
396 db.CreateMaterial("Ni", "Nickel")
397 db.CreateMaterial("Cu", "Copper")
398 db.CreateMaterial("Zn", "Zinc")
399 db.CreateMaterial("Ga", "Gallium")
400 db.CreateMaterial("Ge", "Germanium")
401 db.CreateMaterial("As", "Arsenic")
402 db.CreateMaterial("Se", "Selenium")
403 db.CreateMaterial("Br", "Bromine")
404 db.CreateMaterial("Kr", "Krypton")
405 db.CreateMaterial("Rb", "Rubidium")
406 db.CreateMaterial("Sr", "Strontium")
407 db.CreateMaterial("Y", "Yttrium")
408 db.CreateMaterial("Zr", "Zirconium")
409 db.CreateMaterial("Nb", "Niobium")
410 db.CreateMaterial("Mo", "Molybdenum")
411 db.CreateMaterial("Tc", "Technetium")
412 db.CreateMaterial("Ru", "Ruthenium")
413 db.CreateMaterial("Rh", "Rhodium")
414 db.CreateMaterial("Pd", "Palladium")
415 db.CreateMaterial("Ag", "Silver")
416 db.CreateMaterial("Cd", "Cadmium")
417 db.CreateMaterial("In", "Indium")
418 db.CreateMaterial("Sn", "Tin")
419 db.CreateMaterial("Sb", "Antimony")
420 db.CreateMaterial("Te", "Tellurium")
421 db.CreateMaterial("I", "Iodine")
422 db.CreateMaterial("Xe", "Xenon")
423 db.CreateMaterial("Cs", "Caesium")
424 db.CreateMaterial("Ba", "Barium")
425 db.CreateMaterial("La", "Lanthanum")
426 db.CreateMaterial("Ce", "Cerium")
427 db.CreateMaterial("Pr", "Praseordymium")
428 db.CreateMaterial("Nd", "Neodymium")
429 db.CreateMaterial("Pm", "Promethium")
430 db.CreateMaterial("Sm", "Samarium")
431 db.CreateMaterial("Eu", "Europium")
432 db.CreateMaterial("Gd", "Gadolinium")
433 db.CreateMaterial("Tb", "Terbium")
434 db.CreateMaterial("Dy", "Dysprosium")
435 db.CreateMaterial("Ho", "Holmium")
436 db.CreateMaterial("Er", "Erbium")
437 db.CreateMaterial("Tm", "Thulium")
438 db.CreateMaterial("Yb", "Ytterbium")
439 db.CreateMaterial("Lu", "Lutetium")
440 db.CreateMaterial("Hf", "Hafnium")
441 db.CreateMaterial("Ta", "Tantalum")
442 db.CreateMaterial("W", "Tungsten")
443 db.CreateMaterial("Re", "Rhenium")
444 db.CreateMaterial("Os", "Osmium")
445 db.CreateMaterial("Ir", "Iridium")
446 db.CreateMaterial("Pt", "Platinum")
447 db.CreateMaterial("Au", "Gold")
448 db.CreateMaterial("Hg", "Mercury")
449 db.CreateMaterial("Tl", "Thallium")
450 db.CreateMaterial("Pb", "Lead")
451 db.CreateMaterial("Bi", "Bismuth")
452 db.CreateMaterial("Po", "Polonium")
453 db.CreateMaterial("At", "Astatine")
454 db.CreateMaterial("Rn", "Radon")
455 db.CreateMaterial("Fr", "Fancium")
456 db.CreateMaterial("Ra", "Radium")
457 db.CreateMaterial("Ac", "Actinium")
458 db.CreateMaterial("Th", "Thorium")
459 db.CreateMaterial("Pa", "Protactinium")
460 db.CreateMaterial("U", "Urianium")
461 db.CreateMaterial("Np", "Neptunium")
462 db.CreateMaterial("Pu", "Plutonium")
463 db.CreateMaterial("Am", "Americium")
464 db.CreateMaterial("Cm", "Curium")
465 db.CreateMaterial("Bk", "Berkelium")
466 db.CreateMaterial("Cf", "Californium")
467 db.CreateMaterial("Es", "Einsteinium")
468 db.CreateMaterial("Fm", "Fermium")
469 db.CreateMaterial("Md", "Mendelevium")
470 db.CreateMaterial("No", "Nobelium")
471 db.CreateMaterial("Lr", "Lawrencium")
472 db.CreateMaterial("Rf", "Rutherfordium")
473 db.CreateMaterial("Db", "Dubnium")
474 db.CreateMaterial("Sg", "Seaborgium")
475 db.CreateMaterial("Bh", "Bohrium")
476 db.CreateMaterial("Hs", "Hassium")
477 db.CreateMaterial("Mt", "Meitnerium")
478 db.CreateMaterial("Ds", "Darmstadtium")
479 db.CreateMaterial("Rg", "Roentgenium")
480 db.CreateMaterial("Cn", "Copernicium")
481 db.CreateMaterial("Nh", "Nihonium")
482 db.CreateMaterial("Fl", "Flerovium")
483 db.CreateMaterial("Mc", "Moscovium")
484 db.CreateMaterial("Lv", "Livermorium")
485 db.CreateMaterial("Ts", "Tennessine")
486 db.CreateMaterial("Og", "Oganesson")
487
488
489 # functions to read database files
490 def add_f0_from_intertab(db, itf, verbose=False):
491 """
492 Read f0 data from International Tables of Crystallography and add
493 it to the database.
494 """
495 # some regular expressions
496 elementstr = re.compile(r"^#S")
497 multiblank = re.compile(r"\s+")
498 while True:
499 lb = itf.readline().decode("utf-8")
500 if lb == "":
501 break
502 lb = lb.strip()
503
504 if elementstr.match(lb):
505 # found new element
506 lb = multiblank.split(lb)
507
508 # determine oxidation state and element name
509 elemstate = re.sub('[A-Za-z]', '', lb[2])
510 for r, o in zip(('dot', 'p', 'm'), ('.', '+', '-')):
511 elemstate = elemstate.replace(o, r)
512 if elemstate == 'p2': # fix wrong name in the source file
513 elemstate = '2p'
514 ename = re.sub('[^A-Za-z]', '', lb[2])
515
516 if verbose:
517 print("{pyname} = Atom('{name}', {num})".format(
518 pyname=ename+elemstate, name=lb[2], num=lb[1]))
519 db.SetMaterial(ename)
520 # make two dummy reads
521 for i in range(2):
522 itf.readline()
523 # read fit parameters
524 lb = itf.readline().decode("utf-8")
525 lb = lb.strip()
526 lb = multiblank.split(lb)
527 a1 = float(lb[0])
528 a2 = float(lb[1])
529 a3 = float(lb[2])
530 a4 = float(lb[3])
531 c = float(lb[4])
532 b1 = float(lb[5])
533 b2 = float(lb[6])
534 b3 = float(lb[7])
535 b4 = float(lb[8])
536 db.SetF0([c, a1, b1, a2, b2, a3, b3, a4, b4], subset=elemstate)
537
538
539 def add_f0_from_xop(db, xop, verbose=False):
540 """
541 Read f0 data from f0_xop.dat and add
542 it to the database.
543 """
544 # some regular expressions
545 elementstr = re.compile(r"^#S")
546 multiblank = re.compile(r"\s+")
547 invalidelem = re.compile(r"[^A-Za-z]")
548
549 while True:
550 lb = xop.readline().decode("utf-8")
551 if lb == "":
552 break
553 lb = lb.strip()
554
555 if elementstr.match(lb):
556 # found new element
557 lb = multiblank.split(lb)
558 # determine oxidation state and element name
559 elemstate = re.sub('[A-Za-z]', '', lb[2])
560 for r, o in zip(('dot', 'p', 'm'), ('.', '+', '-')):
561 elemstate = elemstate.replace(o, r)
562 ename = re.sub('[^A-Za-z]', '', lb[2])
563
564 if verbose:
565 print("{pyname} = Atom('{name}', {num})".format(
566 pyname=ename+elemstate, name=lb[2], num=lb[1]))
567 db.SetMaterial(ename)
568
569 # make nine dummy reads
570 for i in range(9):
571 xop.readline()
572 # read fit parameters
573 lb = xop.readline().decode("utf-8")
574 lb = lb.strip()
575 lb = multiblank.split(lb)
576 a1 = float(lb[0])
577 a2 = float(lb[1])
578 a3 = float(lb[2])
579 a4 = float(lb[3])
580 a5 = float(lb[4])
581 c = float(lb[5])
582 b1 = float(lb[6])
583 b2 = float(lb[7])
584 b3 = float(lb[8])
585 b4 = float(lb[9])
586 b5 = float(lb[10])
587 db.SetF0([c, a1, b1, a2, b2, a3, b3, a4, b4, a5, b5])
588
589
590 def add_f1f2_from_henkedb(db, hf, verbose=False):
591 """
592 Read f1 and f2 data from Henke database and add
593 it to the database.
594 """
595 # some regular expressions
596 elementstr = re.compile(r"^#S")
597 multiblank = re.compile(r"\s+")
598 invalidelem = re.compile(r"[^A-Za-z]")
599
600 while True:
601 lb = hf.readline().decode("utf-8")
602 if lb == "":
603 break
604 lb = lb.strip()
605
606 if elementstr.match(lb):
607 # found new element
608 lb = multiblank.split(lb)
609 enum = lb[1]
610 ename = lb[2]
611 # check if this is not some funny isotope
612
613 if invalidelem.findall(ename) == []:
614 if verbose:
615 print("set element %s" % ename)
616 db.SetMaterial(ename)
617 # make one dummy read
618 for i in range(5):
619 hf.readline()
620
621 # read data
622 en_list = []
623 f1_list = []
624 f2_list = []
625 while True:
626 lb = hf.readline().decode("utf-8")
627 lb = lb.strip()
628 lb = multiblank.split(lb)
629 en = float(lb[0])
630 # to account for wrong f1 definition in Henke db
631 f1 = float(lb[1]) - float(enum)
632 f2 = float(lb[2])
633 en_list.append(en)
634 f1_list.append(f1)
635 f2_list.append(f2)
636 if en == 30000.:
637 db.SetF1F2(en_list, f1_list, f2_list)
638 break
639
640
641 def add_f1f2_from_kissel(db, kf, verbose=False):
642 """
643 Read f1 and f2 data from Henke database and add
644 it to the database.
645 """
646 # some regular expressions
647 elementstr = re.compile(r"^#S")
648 multiblank = re.compile(r"\s+")
649 invalidelem = re.compile(r"[^A-Za-z]")
650
651 while True:
652 lb = kf.readline().decode("utf-8")
653 if lb == "":
654 break
655 lb = lb.strip()
656
657 if elementstr.match(lb):
658 # found new element
659 lb = multiblank.split(lb)
660 enum = lb[1]
661 ename = lb[2]
662 # check if this is not some funny isotope
663
664 if invalidelem.findall(ename) == []:
665 if verbose:
666 print("set element %s" % ename)
667 db.SetMaterial(ename)
668 # make 28 dummy reads
669 for i in range(28):
670 kf.readline()
671
672 # read data
673 en_list = []
674 f1_list = []
675 f2_list = []
676 while True:
677 lb = kf.readline().decode("utf-8")
678 lb = lb.strip()
679 lb = multiblank.split(lb)
680 en = float(lb[0]) * 1000 # convert energy
681 # to account for wrong f1 definition in Henke db
682 f1 = float(lb[4]) - float(enum)
683 f2 = float(lb[5])
684 en_list.append(en)
685 f1_list.append(f1)
686 f2_list.append(f2)
687 if en == 10000000.:
688 db.SetF1F2(en_list, f1_list, f2_list)
689 break
690
691
692 def add_f1f2_from_ascii_file(db, asciifile, element, verbose=False):
693 """
694 Read f1 and f2 data for specific element from ASCII file (3 columns) and
695 save it to the database.
696 """
697
698 # parse the f1f2 file
699 try:
700 af = numpy.loadtxt(asciifile)
701 except OSError:
702 print("cannot open f1f2 database file")
703 return None
704 db.SetMaterial(element)
705
706 en = af[:, 0]
707 f1 = af[:, 1]
708 f2 = af[:, 2]
709 db.SetF1F2(en, f1, f2)
710
711
712 def add_mass_from_NIST(db, nistfile, verbose=False):
713 """
714 Read atoms standard mass and save it to the database.
715 The mass of the natural isotope mixture is taken from the NIST data!
716 """
717 # some regular expressions
718 commentline = re.compile(r"^#")
719 isotope = re.compile(r"^Atomic Number =")
720 standardw = re.compile(r"^Standard Atomic Weight")
721 relativew = re.compile(r"^Relative Atomic Mass")
722 number = re.compile(r"[0-9.]+")
723 multiblank = re.compile(r"\s+")
724
725 # parse the nist file
726 with open(nistfile, "r") as nf:
727 while True:
728 lb = nf.readline()
729 if lb == "":
730 break
731 lb = lb.strip()
732
733 if isotope.match(lb):
734 # found new element
735 lb = multiblank.split(lb)
736 enum = int(lb[-1])
737 lb = nf.readline()
738 lb = lb.strip()
739 lb = multiblank.split(lb)
740 ename = lb[-1]
741
742 if verbose:
743 print("set element %s" % ename)
744 db.SetMaterial(ename)
745
746 # read data
747 while True:
748 lb = nf.readline()
749 lb = lb.strip()
750 if relativew.match(lb):
751 lb = multiblank.split(lb)
752 # extract fallback weight
753 w = float(number.findall(lb[-1])[0])
754 db.SetWeight(w * scipy.constants.atomic_mass)
755 elif standardw.match(lb):
756 lb = multiblank.split(lb)
757 # extract average weight
758 try:
759 w = float(number.findall(lb[-1])[0])
760 db.SetWeight(w * scipy.constants.atomic_mass)
761 except IndexError:
762 pass
763 break
764
765
766 def add_color_from_JMOL(db, cfile, verbose=False):
767 """
768 Read color from JMOL color table and save it to the database.
769 """
770 with open(cfile, "r") as f:
771 for line in f.readlines():
772 s = line.split()
773 ename = s[1]
774 color = [float(num)/255. for num in s[2].strip('[]').split(',')]
775 color = tuple(color)
776 if verbose:
777 print("set element %s" % ename)
778 db.SetMaterial(ename)
779 db.SetColor(color)
780
781
782 def add_radius_from_WIKI(db, dfile, verbose=False):
783 """
784 Read radius from Wikipedia radius table and save it to the database.
785 """
786 with open(dfile, "r") as f:
787 for line in f.readlines():
788 s = line.split(',')
789 ename = s[1]
790 radius = float(s[3]) / 100.
791 if verbose:
792 print("set element %s" % ename)
793 db.SetMaterial(ename)
794 db.SetRadius(radius)
+0
-257
xrayutilities/materials/elements.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2010-2015 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 from .atom import Atom
19
20 dummy = Atom('dummy', 0)
21 H = Atom('H', 1)
22 Hdot = Atom('H.', 1)
23 H1m = Atom('H1-', 1)
24 D = Atom('D', 1)
25 T = Atom('T', 1)
26 He = Atom('He', 2)
27 Li = Atom('Li', 3)
28 Li1p = Atom('Li1+', 3)
29 Be = Atom('Be', 4)
30 Be2p = Atom('Be2+', 4)
31 B = Atom('B', 5)
32 C = Atom('C', 6)
33 Cdot = Atom('C.', 6)
34 N = Atom('N', 7)
35 O = Atom('O', 8)
36 O1m = Atom('O1-', 8)
37 O2mdot = Atom('O2-.', 8)
38 O2m = Atom('O2-.', 8)
39 F = Atom('F', 9)
40 F1m = Atom('F1-', 9)
41 Ne = Atom('Ne', 10)
42 Na = Atom('Na', 11)
43 Na1p = Atom('Na1+', 11)
44 Mg = Atom('Mg', 12)
45 Mg2p = Atom('Mg2+', 12)
46 Al = Atom('Al', 13)
47 Al3p = Atom('Al3+', 13)
48 Si = Atom('Si', 14)
49 Sidot = Atom('Si.', 14)
50 Si4p = Atom('Si4+', 14)
51 P = Atom('P', 15)
52 S = Atom('S', 16)
53 Cl = Atom('Cl', 17)
54 Cl1m = Atom('Cl1-', 17)
55 Ar = Atom('Ar', 18)
56 K = Atom('K', 19)
57 K1p = Atom('K1+', 19)
58 Ca = Atom('Ca', 20)
59 Ca2p = Atom('Ca2+', 20)
60 Sc = Atom('Sc', 21)
61 Sc3p = Atom('Sc3+', 21)
62 Ti = Atom('Ti', 22)
63 Ti2p = Atom('Ti2+', 22)
64 Ti3p = Atom('Ti3+', 22)
65 Ti4p = Atom('Ti4+', 22)
66 V = Atom('V', 23)
67 V2p = Atom('V2+', 23)
68 V3p = Atom('V3+', 23)
69 V5p = Atom('V5+', 23)
70 Cr = Atom('Cr', 24)
71 Cr2p = Atom('Cr2+', 24)
72 Cr3p = Atom('Cr3+', 24)
73 Mn = Atom('Mn', 25)
74 Mn2p = Atom('Mn2+', 25)
75 Mn3p = Atom('Mn3+', 25)
76 Mn4p = Atom('Mn4+', 25)
77 Fe = Atom('Fe', 26)
78 Fe2p = Atom('Fe2+', 26)
79 Fe3p = Atom('Fe3+', 26)
80 Co = Atom('Co', 27)
81 Co2p = Atom('Co2+', 27)
82 Co3p = Atom('Co3+', 27)
83 Ni = Atom('Ni', 28)
84 Ni2p = Atom('Ni2+', 28)
85 Ni3p = Atom('Ni3+', 28)
86 Cu = Atom('Cu', 29)
87 Cu1p = Atom('Cu1+', 29)
88 Cu2p = Atom('Cu2+', 29)
89 Zn = Atom('Zn', 30)
90 Zn2p = Atom('Zn2+', 30)
91 Ga = Atom('Ga', 31)
92 Ga3p = Atom('Ga3+', 31)
93 Ge = Atom('Ge', 32)
94 Ge4p = Atom('Ge4+', 32)
95 As = Atom('As', 33)
96 Se = Atom('Se', 34)
97 Br = Atom('Br', 35)
98 Br1m = Atom('Br1-', 35)
99 Kr = Atom('Kr', 36)
100 Rb = Atom('Rb', 37)
101 Rb1p = Atom('Rb1+', 37)
102 Sr = Atom('Sr', 38)
103 Sr2p = Atom('Sr2+', 38)
104 Y = Atom('Y', 39)
105 Y3p = Atom('Y3+', 39)
106 Zr = Atom('Zr', 40)
107 Zr4p = Atom('Zr4+', 40)
108 Nb = Atom('Nb', 41)
109 Nb3p = Atom('Nb3+', 41)
110 Nb5p = Atom('Nb5+', 41)
111 Mo = Atom('Mo', 42)
112 Mo3p = Atom('Mo3+', 42)
113 Mo5p = Atom('Mo5+', 42)
114 Mo6p = Atom('Mo6+', 42)
115 Tc = Atom('Tc', 43)
116 Ru = Atom('Ru', 44)
117 Ru3p = Atom('Ru3+', 44)
118 Ru4p = Atom('Ru4+', 44)
119 Rh = Atom('Rh', 45)
120 Rh3p = Atom('Rh3+', 45)
121 Rh4p = Atom('Rh4+', 45)
122 Pd = Atom('Pd', 46)
123 Pd2p = Atom('Pd2+', 46)
124 Pd4p = Atom('Pd4+', 46)
125 Ag = Atom('Ag', 47)
126 Ag1p = Atom('Ag1+', 47)
127 Ag2p = Atom('Ag2+', 47)
128 Cd = Atom('Cd', 48)
129 Cd2p = Atom('Cd2+', 48)
130 In = Atom('In', 49)
131 In3p = Atom('In3+', 49)
132 Sn = Atom('Sn', 50)
133 Sn2p = Atom('Sn2+', 50)
134 Sn4p = Atom('Sn4+', 50)
135 Sb = Atom('Sb', 51)
136 Sb3p = Atom('Sb3+', 51)
137 Sb5p = Atom('Sb5+', 51)
138 Te = Atom('Te', 52)
139 I = Atom('I', 53)
140 I1m = Atom('I1-', 53)
141 Xe = Atom('Xe', 54)
142 Cs = Atom('Cs', 55)
143 Cs1p = Atom('Cs1+', 55)
144 Ba = Atom('Ba', 56)
145 Ba2p = Atom('Ba2+', 56)
146 La = Atom('La', 57)
147 La3p = Atom('La3+', 57)
148 Ce = Atom('Ce', 58)
149 Ce3p = Atom('Ce3+', 58)
150 Ce4p = Atom('Ce4+', 58)
151 Pr = Atom('Pr', 59)
152 Pr3p = Atom('Pr3+', 59)
153 Pr4p = Atom('Pr4+', 59)
154 Nd = Atom('Nd', 60)
155 Nd3p = Atom('Nd3+', 60)
156 Pm = Atom('Pm', 61)
157 Pm3p = Atom('Pm3+', 61)
158 Sm = Atom('Sm', 62)
159 Sm3p = Atom('Sm3+', 62)
160 Eu = Atom('Eu', 63)
161 Eu2p = Atom('Eu2+', 63)
162 Eu3p = Atom('Eu3+', 63)
163 Gd = Atom('Gd', 64)
164 Gd3p = Atom('Gd3+', 64)
165 Tb = Atom('Tb', 65)
166 Tb3p = Atom('Tb3+', 65)
167 Dy = Atom('Dy', 66)
168 Dy3p = Atom('Dy3+', 66)
169 Ho = Atom('Ho', 67)
170 Ho3p = Atom('Ho3+', 67)
171 Er = Atom('Er', 68)
172 Er3p = Atom('Er3+', 68)
173 Tm = Atom('Tm', 69)
174 Tm3p = Atom('Tm3+', 69)
175 Yb = Atom('Yb', 70)
176 Yb2p = Atom('Yb2+', 70)
177 Yb3p = Atom('Yb3+', 70)
178 Lu = Atom('Lu', 71)
179 Lu3p = Atom('Lu3+', 71)
180 Hf = Atom('Hf', 72)
181 Hf4p = Atom('Hf4+', 72)
182 Ta = Atom('Ta', 73)
183 Ta5p = Atom('Ta5+', 73)
184 W = Atom('W', 74)
185 W6p = Atom('W6+', 74)
186 Re = Atom('Re', 75)
187 Os = Atom('Os', 76)
188 Os4p = Atom('Os4+', 76)
189 Ir = Atom('Ir', 77)
190 Ir3p = Atom('Ir3+', 77)
191 Ir4p = Atom('Ir4+', 77)
192 Pt = Atom('Pt', 78)
193 Pt2p = Atom('Pt2+', 78)
194 Pt4p = Atom('Pt4+', 78)
195 Au = Atom('Au', 79)
196 Au1p = Atom('Au1+', 79)
197 Au3p = Atom('Au3+', 79)
198 Hg = Atom('Hg', 80)
199 Hg1p = Atom('Hg1+', 80)
200 Hg2p = Atom('Hg2+', 80)
201 Tl = Atom('Tl', 81)
202 Tl1p = Atom('Tl1+', 81)
203 Tl3p = Atom('Tl3+', 81)
204 Pb = Atom('Pb', 82)
205 Pb2p = Atom('Pb2+', 82)
206 Pb4p = Atom('Pb4+', 82)
207 Bi = Atom('Bi', 83)
208 Bi3p = Atom('Bi3+', 83)
209 Bi5p = Atom('Bi5+', 83)
210 Po = Atom('Po', 84)
211 At = Atom('At', 85)
212 Rn = Atom('Rn', 86)
213 Fr = Atom('Fr', 87)
214 Ra = Atom('Ra', 88)
215 Ra2p = Atom('Ra2+', 88)
216 Ac = Atom('Ac', 89)
217 Ac3p = Atom('Ac3+', 89)
218 Th = Atom('Th', 90)
219 Th4p = Atom('Th4+', 90)
220 Pa = Atom('Pa', 91)
221 U = Atom('U', 92)
222 U3p = Atom('U3+', 92)
223 U4p = Atom('U4+', 92)
224 U6p = Atom('U6+', 92)
225 Np = Atom('Np', 93)
226 Np3p = Atom('Np3+', 93)
227 Np4p = Atom('Np4+', 93)
228 Np6p = Atom('Np6+', 93)
229 Pu = Atom('Pu', 94)
230 Pu3p = Atom('Pu3+', 94)
231 Pu4p = Atom('Pu4+', 94)
232 Pu6p = Atom('Pu6+', 94)
233 Am = Atom('Am', 95)
234 Cm = Atom('Cm', 96)
235 Bk = Atom('Bk', 97)
236 Cf = Atom('Cf', 98)
237 Es = Atom("Es", 99)
238 Fm = Atom("Fm", 100)
239 Md = Atom("Md", 101)
240 No = Atom("No", 102)
241 Lr = Atom("Lr", 103)
242 Rf = Atom("Rf", 104)
243 Db = Atom("Db", 105)
244 Sg = Atom("Sg", 106)
245 Bh = Atom("Bh", 107)
246 Hs = Atom("Hs", 108)
247 Mt = Atom("Mt", 109)
248 Ds = Atom("Ds", 110)
249 Rg = Atom("Rg", 111)
250 Cn = Atom("Cn", 112)
251 Uut = Atom("Uut", 113)
252 Uuq = Atom("Uuq", 114)
253 Uup = Atom("Uup", 115)
254 Uuh = Atom("Uuh", 116)
255 Uus = Atom("Uus", 117)
256 Uuo = Atom("Uuo", 118)
+0
-283
xrayutilities/materials/heuslerlib.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 implement convenience functions to define Heusler materials.
19 """
20
21 from . import elements
22 from .material import Crystal
23 from .spacegrouplattice import SGLattice
24
25
26 def _check_elements(*elem):
27 ret = []
28 for el in elem:
29 if isinstance(el, str):
30 ret.append(getattr(elements, el))
31 else:
32 ret.append(el)
33 return ret
34
35
36 def FullHeuslerCubic225(X, Y, Z, a, biso=[0, 0, 0], occ=[1, 1, 1]):
37 """
38 Full Heusler structure with formula X2YZ.
39 Strukturberichte symbol L2_1; space group Fm-3m (225)
40
41 Parameters
42 ----------
43 X, Y, Z : str or Element
44 elements
45 a : float
46 cubic lattice parameter in Angstroem
47 biso : list of floats, optional
48 Debye Waller factors for X, Y, Z elements
49 occ : list of floats, optional
50 occupation numbers for the elements X, Y, Z
51
52 Returns
53 -------
54 Crystal
55 Crystal describing the Heusler material
56 """
57 x, y, z = _check_elements(X, Y, Z)
58 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
59 SGLattice(225, a, atoms=[x, y, z], pos=['8c', '4a', '4b'],
60 b=biso, occ=occ))
61
62
63 def FullHeuslerCubic225_B2(X, Y, Z, a, b2dis, biso=[0, 0, 0], occ=[1, 1, 1]):
64 """
65 Full Heusler structure with formula X2YZ.
66 Strukturberichte symbol L2_1; space group Fm-3m (225) with B2-type (CsCl)
67 disorder
68
69 Parameters
70 ----------
71 X, Y, Z : str or Element
72 elements
73 a : float
74 cubic lattice parameter in Angstroem
75 b2dis : float
76 amount of B2-type disorder (0: fully ordered, 1: fully disordered)
77 biso : list of floats, optional
78 Debye Waller factors for X, Y, Z elements
79 occ : list of floats, optional
80 occupation numbers for the elements X, Y, Z
81
82 Returns
83 -------
84 Crystal
85 Crystal describing the Heusler material
86 """
87 x, y, z = _check_elements(X, Y, Z)
88 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
89 SGLattice(225, a,
90 atoms=[x, y, z, y, z],
91 pos=['8c', '4a', '4b', '4b', '4a'],
92 occ=[1*occ[0], (1-b2dis/2.)*occ[1],
93 (1-b2dis/2.)*occ[2], b2dis/2.*occ[1],
94 b2dis/2.*occ[2]],
95 b=biso + [biso[1], biso[2]]))
96
97
98 def FullHeuslerCubic225_A2(X, Y, Z, a, a2dis, biso=[0, 0, 0], occ=[1, 1, 1]):
99 """
100 Full Heusler structure with formula X2YZ.
101 Strukturberichte symbol L2_1; space group Fm-3m (225) with A2-type (W)
102 disorder
103
104 Parameters
105 ----------
106 X, Y, Z : str or Element
107 elements
108 a : float
109 cubic lattice parameter in Angstroem
110 a2dis : float
111 amount of A2-type disorder (0: fully ordered, 1: fully disordered)
112 biso : list of floats, optional
113 Debye Waller factors for X, Y, Z elements
114 occ : list of floats, optional
115 occupation numbers for the elements X, Y, Z
116
117 Returns
118 -------
119 Crystal
120 Crystal describing the Heusler material
121 """
122 x, y, z = _check_elements(X, Y, Z)
123 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
124 SGLattice(225, a,
125 atoms=[x, x, x, y, y, y, z, z, z],
126 pos=['8c', '4a', '4b',
127 '8c', '4a', '4b',
128 '8c', '4a', '4b'],
129 occ=[(1-a2dis/2.)*occ[0], a2dis/2.*occ[0],
130 a2dis/2.*occ[0], a2dis/4.*occ[1],
131 (1-a2dis*3./4.)*occ[1], a2dis/4.*occ[1],
132 a2dis/4.*occ[2], a2dis/4.*occ[2],
133 (1-a2dis*3./4.)*occ[2]],
134 b=[biso[0], ]*3 + [biso[1], ]*3 + [biso[2], ]*3))
135
136
137 def FullHeuslerCubic225_DO3(X, Y, Z, a, do3disxy, do3disxz, biso=[0, 0, 0],
138 occ=[1, 1, 1]):
139 """
140 Full Heusler structure with formula X2YZ.
141 Strukturberichte symbol L2_1; space group Fm-3m (225) with DO_3-type (BiF3)
142 disorder, either between atoms X <-> Y or X <-> Z.
143
144 Parameters
145 ----------
146 X, Y, Z : str or Element
147 elements
148 a : float
149 cubic lattice parameter in Angstroem
150 do3disxy : float
151 amount of DO_3-type disorder between X and Y atoms (0: fully ordered,
152 1: fully disordered)
153 do3disxz : float
154 amount of DO_3-type disorder between X and Z atoms (0: fully ordered,
155 1: fully disordered)
156 biso : list of floats, optional
157 Debye Waller factors for X, Y, Z elements
158 occ : list of floats, optional
159 occupation numbers for the elements X, Y, Z
160
161 Returns
162 -------
163 Crystal
164 Crystal describing the Heusler material
165 """
166 x, y, z = _check_elements(X, Y, Z)
167 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
168 SGLattice(225, a,
169 atoms=[x, y, z,
170 x, y,
171 x, z],
172 pos=['8c', '4a', '4b',
173 '4a', '8c',
174 '4b', '8c'],
175 occ=[(1-do3disxy/3.-do3disxz/3.)*occ[0],
176 (1-do3disxy*2/3.)*occ[1],
177 (1-do3disxz*2/3.)*occ[2],
178 do3disxy*2/3.*occ[0], do3disxy*1/3.*occ[1],
179 do3disxz*2/3.*occ[0], do3disxz*1/3.*occ[2]],
180 b=biso + [biso[0], biso[1]] + [biso[0], biso[2]]))
181
182
183 def InverseHeuslerCubic216(X, Y, Z, a, biso=[0, 0, 0], occ=[1, 1, 1]):
184 """
185 Full Heusler structure with formula (XY)X'Z structure;
186 space group F-43m (216)
187
188 Parameters
189 ----------
190 X, Y, Z : str or Element
191 elements
192 a : float
193 cubic lattice parameter in Angstroem
194
195 Returns
196 -------
197 Crystal
198 Crystal describing the Heusler material
199 """
200 x, y, z = _check_elements(X, Y, Z)
201 return Crystal('(%s%s)%s\'%s' % (x.basename, y.basename,
202 x.basename, z.basename),
203 SGLattice(216, a, atoms=[x, x, y, z],
204 pos=['4a', '4d', '4b', '4c'],
205 b=[biso[0], ] + biso,
206 occ=[occ[0], ] + occ))
207
208
209 def HeuslerTetragonal139(X, Y, Z, a, c, biso=[0, 0, 0], occ=[1, 1, 1]):
210 """
211 Tetragonal Heusler structure with formula X2YZ
212 space group I4/mmm (139)
213
214 Parameters
215 ----------
216 X, Y, Z : str or Element
217 elements
218 a, c : float
219 tetragonal lattice parameters in Angstroem
220
221 Returns
222 -------
223 Crystal
224 Crystal describing the Heusler material
225 """
226 x, y, z = _check_elements(X, Y, Z)
227 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
228 SGLattice(139, a, c,
229 atoms=[x, y, z],
230 pos=['4d', '2b', '2a'],
231 b=biso, occ=occ))
232
233
234 def HeuslerTetragonal119(X, Y, Z, a, c, biso=[0, 0, 0], occ=[1, 1, 1]):
235 """
236 Tetragonal Heusler structure with formula X2YZ
237 space group I-4m2 (119)
238
239 Parameters
240 ----------
241 X, Y, Z : str or Element
242 elements
243 a, c : float
244 tetragonal lattice parameters in Angstroem
245
246 Returns
247 -------
248 Crystal
249 Crystal describing the Heusler material
250 """
251 x, y, z = _check_elements(X, Y, Z)
252 return Crystal('%s2%s%s' % (x.basename, y.basename, z.basename),
253 SGLattice(119, a, c,
254 atoms=[x, x, y, z],
255 pos=['2b', '2c', '2d', '2a'],
256 b=[biso[0], ] + biso,
257 occ=[occ[0], ] + occ))
258
259
260 def HeuslerHexagonal194(X, Y, Z, a, c, biso=[0, 0, 0], occ=[1, 1, 1]):
261 """
262 Hexagonal Heusler structure with formula XYZ
263 space group P63/mmc (194)
264
265 Parameters
266 ----------
267 X, Y, Z : str or Element
268 elements
269 a, c : float
270 hexagonal lattice parameters in Angstroem
271
272 Returns
273 -------
274 Crystal
275 Crystal describing the Heusler material
276 """
277 x, y, z = _check_elements(X, Y, Z)
278 return Crystal('%s%s%s' % (x.basename, y.basename, z.basename),
279 SGLattice(194, a, c,
280 atoms=[x, y, z],
281 pos=['2a', '2c', '2d'],
282 b=biso, occ=occ))
+0
-1923
xrayutilities/materials/material.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17 # Copyright (C) 2012 Tanja Etzelstorfer <tanja.etzelstorfer@jku.at>
18
19 """
20 Classes decribing materials. Materials are devided with respect to their
21 crystalline state in either Amorphous or Crystal types. While for most
22 materials their crystalline state is defined few materials are also included as
23 amorphous which can be useful for calculation of their optical properties.
24 """
25 import abc
26 import copy
27 import numbers
28 import operator
29 import re
30 import warnings
31 from math import ceil, copysign
32
33 import numpy
34 import scipy.optimize
35
36 from .. import config, math, utilities
37 from ..exception import InputError
38 from ..math import VecCross, VecDot, VecNorm
39 from . import cif, elements
40 from .atom import Atom
41 from .spacegrouplattice import WyckoffBase
42
43 # python 2to3 compatibility
44 try:
45 basestring
46 except NameError:
47 basestring = str
48
49 numpy.seterr(divide='ignore', invalid='ignore')
50
51 map_ijkl2ij = {"00": 0, "11": 1, "22": 2,
52 "12": 3, "20": 4, "01": 5,
53 "21": 6, "02": 7, "10": 8}
54 map_ij2ijkl = {"0": [0, 0], "1": [1, 1], "2": [2, 2],
55 "3": [1, 2], "4": [2, 0], "5": [0, 1],
56 "6": [2, 1], "7": [0, 2], "8": [1, 0]}
57
58
59 def index_map_ijkl2ij(i, j):
60 return map_ijkl2ij["%i%i" % (i, j)]
61
62
63 def index_map_ij2ijkl(ij):
64 return map_ij2ijkl["%i" % ij]
65
66
67 def Cij2Cijkl(cij):
68 """
69 Converts the elastic constants matrix (tensor of rank 2) to
70 the full rank 4 cijkl tensor.
71
72 Parameters
73 ----------
74 cij : array-like
75 (6, 6) cij matrix
76
77 Returns
78 -------
79 cijkl ndarray
80 (3, 3, 3, 3) cijkl tensor as numpy array
81 """
82
83 # first have to build a 9x9 matrix from the 6x6 one
84 m = numpy.zeros((9, 9), dtype=numpy.double)
85 m[0:6, 0:6] = cij[:, :]
86 m[6:9, 0:6] = cij[3:6, :]
87 m[0:6, 6:9] = cij[:, 3:6]
88 m[6:9, 6:9] = cij[3:6, 3:6]
89
90 # now create the full tensor
91 cijkl = numpy.empty((3, 3, 3, 3), dtype=numpy.double)
92
93 for i in range(0, 3):
94 for j in range(0, 3):
95 for k in range(0, 3):
96 for l in range(0, 3):
97 mi = index_map_ijkl2ij(i, j)
98 mj = index_map_ijkl2ij(k, l)
99 cijkl[i, j, k, l] = m[mi, mj]
100 return cijkl
101
102
103 def Cijkl2Cij(cijkl):
104 """
105 Converts the full rank 4 tensor of the elastic constants to
106 the (6, 6) matrix of elastic constants.
107
108 Parameters
109 ----------
110 cijkl ndarray
111 (3, 3, 3, 3) cijkl tensor as numpy array
112
113 Returns
114 -------
115 cij : array-like
116 (6, 6) cij matrix
117 """
118
119 cij = numpy.empty((6, 6), dtype=numpy.double)
120
121 for i in range(6):
122 for j in range(6):
123 ij = index_map_ij2ijkl(i)
124 kl = index_map_ij2ijkl(j)
125 cij[i, j] = cijkl[ij[0], ij[1], kl[0], kl[1]]
126
127 return cij
128
129
130 class Material(utilities.ABC):
131 """
132 base class for all Materials. common properties of amorphous and
133 crystalline materials are described by this class from which Amorphous and
134 Crystal are derived from.
135 """
136
137 def __init__(self, name, cij=None):
138 if cij is None:
139 self.cij = numpy.zeros((6, 6), dtype=numpy.double)
140 self.cijkl = numpy.zeros((3, 3, 3, 3), dtype=numpy.double)
141 elif isinstance(cij, (tuple, list, numpy.ndarray)):
142 self.cij = numpy.asarray(cij, dtype=numpy.double)
143 self.cijkl = Cij2Cijkl(self.cij)
144 else:
145 raise TypeError("Elastic constants must be a list or numpy array!")
146
147 self.name = name
148 self.transform = None
149 self._density = None
150
151 def __getattr__(self, name):
152 if name.startswith("c"):
153 index = name[1:]
154 if len(index) > 2:
155 raise AttributeError("Cij indices must be between 1 and 6")
156
157 i = int(index[0])
158 j = int(index[1])
159
160 if i > 6 or i < 1 or j > 6 or j < 1:
161 raise AttributeError("Cij indices must be between 1 and 6")
162
163 if callable(self.transform):
164 cij = Cijkl2Cij(self.transform(Cij2Cijkl(self.cij)))
165 else:
166 cij = self.cij
167
168 return cij[i - 1, j - 1]
169 else:
170 object.__getattribute__(self, name)
171
172 def _getmu(self):
173 return self.cij[3, 3]
174
175 def _getlam(self):
176 return self.cij[0, 1]
177
178 def _getnu(self):
179 return self.lam / 2. / (self.mu + self.lam)
180
181 def _getdensity(self):
182 return self._density
183
184 density = property(_getdensity)
185 mu = property(_getmu)
186 lam = property(_getlam)
187 nu = property(_getnu)
188
189 @abc.abstractmethod
190 def delta(self, en='config'):
191 """
192 abstract method which every implementation of a Material has to
193 override
194 """
195 pass
196
197 @abc.abstractmethod
198 def ibeta(self, en='config'):
199 """
200 abstract method which every implementation of a Material has to
201 override
202 """
203 pass
204
205 def chi0(self, en='config'):
206 """
207 calculates the complex chi_0 values often needed in simulations.
208 They are closely related to delta and beta
209 (n = 1 + chi_r0/2 + i*chi_i0/2 vs. n = 1 - delta + i*beta)
210 """
211 return (-2 * self.delta(en) + 2j * self.ibeta(en))
212
213 def idx_refraction(self, en="config"):
214 """
215 function to calculate the complex index of refraction of a material
216 in the x-ray range
217
218 Parameters
219 ----------
220 en : energy of the x-rays, if omitted the value from the
221 xrayutilities configuration is used
222
223 Returns
224 -------
225 n (complex)
226 """
227 n = 1. - self.delta(en) + 1.j * self.ibeta(en)
228 return n
229
230 def critical_angle(self, en='config', deg=True):
231 """
232 calculate critical angle for total external reflection
233
234 Parameters
235 ----------
236 en : float or str, optional
237 energy of the x-rays in eV, if omitted the value from the
238 xrayutilities configuration is used
239 deg : bool, optional
240 return angle in degree if True otherwise radians (default:True)
241
242 Returns
243 -------
244 float
245 Angle of total external reflection
246 """
247 rn = 1. - self.delta(en)
248
249 alphac = numpy.arccos(rn)
250 if deg:
251 alphac = numpy.degrees(alphac)
252
253 return alphac
254
255 def absorption_length(self, en='config'):
256 """
257 wavelength dependent x-ray absorption length defined as
258 mu = lambda/(2*pi*2*beta) with lambda and beta as the x-ray
259 wavelength and complex part of the refractive index respectively.
260
261 Parameters
262 ----------
263 en : float or str, optional
264 energy of the x-rays in eV
265
266 Returns
267 -------
268 float
269 the absorption length in um
270 """
271 if isinstance(en, basestring) and en == 'config':
272 en = utilities.energy(config.ENERGY)
273 return utilities.en2lam(en) / (2 * numpy.pi * self.ibeta(en) * 2) / 1e4
274
275 def __str__(self):
276 ostr = "%s: %s\n" % (self.__class__.__name__, self.name)
277 if numpy.any(self.cij):
278 ostr += "Elastic tensor (6x6):\n"
279 d = numpy.get_printoptions()
280 numpy.set_printoptions(precision=2, linewidth=78, suppress=False)
281 ostr += str(self.cij) + '\n'
282 numpy.set_printoptions(d)
283
284 return ostr
285
286
287 class Amorphous(Material):
288 """
289 amorphous materials are described by this class
290 """
291
292 def __init__(self, name, density, atoms=None, cij=None):
293 """
294 constructor of an amorphous material. The amorphous material is
295 described by its density and atom composition.
296
297 Parameters
298 ----------
299 name : str
300 name of the material. To allow automatic parsing of the chemical
301 elements use the abbreviation of the chemical element from the
302 periodic table. To specify alloys, use e.g. 'Ir0.2Mn0.8' or 'H2O'.
303 density : float
304 mass density in kg/m^3
305 atoms : list, optional
306 list of atoms together with their fractional content. When the
307 name is a simply chemical formula then this can be None. To
308 specify more complicated materials use [('Ir', 0.2), ('Mn', 0.8),
309 ...]. Instead of the elements as string you can also use an Atom
310 object. If the contents to not add up to 1 they will be corrected
311 without notice.
312 cij : array-like, optional
313 elasticity matrix
314 """
315 super(Amorphous, self).__init__(name, cij)
316 self._density = density
317 self.base = list()
318 if atoms is None:
319 comp = Amorphous.parseChemForm(name)
320 if config.VERBOSITY >= config.DEBUG:
321 print("XU.materials.Amorphous: using '%s' as chemical formula"
322 % ''.join(['%s%.2f ' % (e.name, c) for e, c in comp]))
323 for (e, c) in comp:
324 self.base.append((e, c))
325 else:
326 frsum = numpy.sum([at[1] for at in atoms])
327 for at, fr in atoms:
328 if not isinstance(at, Atom):
329 a = getattr(elements, at)
330 else:
331 a = at
332 self.base.append((a, fr/frsum))
333
334 @staticmethod
335 def parseChemForm(cstring):
336 """
337 Parse a string containing a simple chemical formula and transform it to
338 a list of elements together with their relative atomic fraction. e.g.
339 'H2O' -> [(H, 2/3), (O, 1/3)], where H and O are the Element objects of
340 Hydrogen and Oxygen. Note that every chemical element needs to start
341 with a capital letter! Complicated formulas containing bracket are not
342 supported!
343
344 Parameters
345 ----------
346 cstring : str
347 string containing the chemical fomula
348
349 Returns
350 -------
351 list of tuples
352 chemical element and atomic fraction
353 """
354 if re.findall(r'[\(\)]', cstring):
355 raise ValueError('unsupported chemical formula (%s) given.'
356 % cstring)
357 elems = re.findall('[A-Z][^A-Z]*', cstring)
358 r = re.compile(r"([a-zA-Z]+)([0-9\.]+)")
359 ret = []
360 csum = 0
361 for e in elems:
362 if r.match(e):
363 elstr, cont = r.match(e).groups()
364 cont = float(cont)
365 else:
366 elstr, cont = (e, 1.0)
367 ret.append((elstr, cont))
368 csum += cont
369 for i, r in enumerate(ret):
370 ret[i] = (getattr(elements, r[0]), r[1]/csum)
371 return ret
372
373 def _get_f(self, q, en):
374 """
375 optimized method to calculate the atomic scattering factor for all
376 atoms in the unit cell by calling the database only as much as needed.
377
378 Parameters
379 ----------
380 q : float or array-like
381 momentum transfer for which the atomic scattering factor should be
382 calculated
383 en : float or str
384 x-ray energy (eV)
385
386 Returns
387 -------
388 list
389 atomic scattering factors for every atom in the unit cell
390 """
391 f = {}
392 for at, occ in self.base:
393 if at.num not in f:
394 f[at.num] = at.f(q, en)
395 return [f[a.num] for a, o in self.base]
396
397 def delta(self, en='config'):
398 """
399 function to calculate the real part of the deviation of the
400 refractive index from 1 (n=1-delta+i*beta)
401
402 Parameters
403 ----------
404 en : float, array-like or str, optional
405 energy of the x-rays in eV
406
407 Returns
408 -------
409 float or array-like
410 """
411 re = scipy.constants.physical_constants['classical electron radius'][0]
412 re *= 1e10
413 if isinstance(en, basestring) and en == 'config':
414 en = utilities.energy(config.ENERGY)
415
416 lam = utilities.en2lam(en)
417 delta = 0.
418 m = 0.
419 f = self._get_f(0., en)
420 for (at, occ), fa in zip(self.base, f):
421 delta += numpy.real(fa) * occ
422 m += at.weight * occ
423
424 delta *= re / (2 * numpy.pi) * lam ** 2 / (m / self.density) * 1e-30
425 return delta
426
427 def ibeta(self, en='config'):
428 """
429 function to calculate the imaginary part of the deviation
430 of the refractive index from 1 (n=1-delta+i*beta)
431
432 Parameters
433 ----------
434 en : float, array-like or str, optional
435 energy of the x-rays in eV
436
437 Returns
438 -------
439 float or array-like
440 """
441 re = scipy.constants.physical_constants['classical electron radius'][0]
442 re *= 1e10
443 if isinstance(en, basestring) and en == 'config':
444 en = utilities.energy(config.ENERGY)
445
446 lam = utilities.en2lam(en)
447 beta = 0.
448 m = 0.
449 f = self._get_f(0., en)
450 for (at, occ), fa in zip(self.base, f):
451 beta += numpy.imag(fa) * occ
452 m += at.weight * occ
453
454 beta *= re / (2 * numpy.pi) * lam ** 2 / (m / self.density) * 1e-30
455 return beta
456
457 def chi0(self, en='config'):
458 """
459 calculates the complex chi_0 values often needed in simulations.
460 They are closely related to delta and beta
461 (n = 1 + chi_r0/2 + i*chi_i0/2 vs. n = 1 - delta + i*beta)
462 """
463 re = scipy.constants.physical_constants['classical electron radius'][0]
464 re *= 1e10
465 if isinstance(en, basestring) and en == 'config':
466 en = utilities.energy(config.ENERGY)
467
468 lam = utilities.en2lam(en)
469 beta = 0.
470 delta = 0.
471 m = 0.
472 f = self._get_f(0., en)
473 for (at, occ), f0 in zip(self.base, f):
474 beta += numpy.imag(f0) * occ
475 delta += numpy.real(f0) * occ
476 m += at.weight * occ
477
478 beta *= re / (2 * numpy.pi) * lam ** 2 / (m / self.density) * 1e-30
479 delta *= re / (2 * numpy.pi) * lam ** 2 / (m / self.density) * 1e-30
480 return (-2 * delta + 2j * beta)
481
482 def __str__(self):
483 ostr = super(Amorphous, self).__str__()
484 ostr += "density: %.2f\n" % self.density
485 if self.base:
486 ostr += "atoms: "
487 for at, o in self.base:
488 ostr += "(%s, %.3f) " % (at.name, o)
489 ostr += "\n"
490
491 return ostr
492
493
494 class Crystal(Material):
495 """
496 Crystalline materials are described by this class
497 """
498
499 def __init__(self, name, lat, cij=None, thetaDebye=None):
500 super(Crystal, self).__init__(name, cij)
501
502 self.lattice = lat
503 if isinstance(thetaDebye, numbers.Number):
504 self.thetaDebye = float(thetaDebye)
505 else:
506 self.thetaDebye = thetaDebye
507
508 @classmethod
509 def fromCIF(cls, ciffilestr):
510 """
511 Create a Crystal from a CIF file. The default data-set from the cif
512 file will be used to create the Crystal.
513
514 Parameters
515 ----------
516 ciffilestr : str, bytes
517 filename of the CIF file or string representation of the CIF file
518
519 Returns
520 -------
521 Crystal
522 """
523 cf = cif.CIFFile(ciffilestr)
524 lat = cf.SGLattice()
525 return cls(cf.data[cf._default_dataset].name, lat)
526
527 def loadLatticefromCIF(self, ciffilestr):
528 """
529 load the unit cell data (lattice) from the CIF file. Other material
530 properties stay unchanged.
531
532 Parameters
533 ----------
534 ciffilestr : str, bytes
535 filename of the CIF file or string representation of the CIF file
536 """
537 cf = cif.CIFFile(ciffilestr)
538 self.lattice = cf.SGLattice()
539
540 def toCIF(self, ciffilename):
541 """
542 Export the Crystal to a CIF file.
543
544 Parameters
545 ----------
546 ciffilename : str
547 filename of the CIF file
548 """
549 cif.cifexport(ciffilename, self)
550
551 @property
552 def a(self):
553 return self.lattice.a
554
555 @property
556 def b(self):
557 return self.lattice.b
558
559 @property
560 def c(self):
561 return self.lattice.c
562
563 @property
564 def alpha(self):
565 return self.lattice.alpha
566
567 @property
568 def beta(self):
569 return self.lattice.beta
570
571 @property
572 def gamma(self):
573 return self.lattice.gamma
574
575 @property
576 def a1(self):
577 return self.lattice._ai[0, :]
578
579 @property
580 def a2(self):
581 return self.lattice._ai[1, :]
582
583 @property
584 def a3(self):
585 return self.lattice._ai[2, :]
586
587 @property
588 def B(self):
589 return self.lattice.qtransform.matrix
590
591 def __eq__(self, other):
592 """
593 compare if another Crystal instance is equal to the current one.
594 Currently this considers only the lattice to be equal. Additional
595 parameters like thetaDebye and the eleastic parameters are ignored.
596
597 Parameters
598 ----------
599 other: Crystal
600 another instance of Crystal to compare
601 """
602 return self.lattice == other.lattice
603
604 def Q(self, *hkl):
605 """
606 Return the Q-space position for a certain material.
607
608 Parameters
609 ----------
610 hkl : list or array-like
611 Miller indices (or Q(h, k, l) is also possible)
612
613 """
614 return self.lattice.GetQ(*hkl)
615
616 def HKL(self, *q):
617 """
618 Return the HKL-coordinates for a certain Q-space position.
619
620 Parameters
621 ----------
622 q : list or array-like
623 Q-position. its also possible to use HKL(qx, qy, qz).
624
625 """
626 return self.lattice.GetHKL(*q)
627
628 def chemical_composition(self, natoms=None, with_spaces=False, ndigits=2):
629 """
630 determine chemical composition from occupancy of atomic positions.
631
632 Parameters
633 ----------
634 mat : Crystal
635 instance of Crystal
636 natoms : int, optional
637 number of atoms to normalize the formula, if None some automatic
638 normalization is attempted using the greatest common divisor of the
639 number of atoms per unit cell. If the number of atoms of any
640 element is fractional natoms=1 is used.
641 with_spaces : bool, optional
642 add spaces between the different entries in the output string for
643 CIF combatibility
644 ndigits : int, optional
645 number of digits to which floating point numbers are rounded to
646
647 Returns
648 -------
649 str
650 representation of the chemical composition
651 """
652 elem = {}
653 for a in self.lattice.base():
654 e = a[0].name
655 occ = a[2]
656 if e in elem:
657 elem[e] += occ
658 else:
659 elem[e] = occ
660 natom = sum([elem[e] for e in elem])
661 isint = True
662 for e in elem:
663 if not float(elem[e]).is_integer():
664 isint = False
665 # determine number of atoms
666 if not natoms:
667 if isint:
668 gcd = math.gcd([int(elem[e]) for e in elem])
669 natoms = natom/gcd
670 else:
671 natoms = 1
672
673 # generate output strig
674 cstr = ''
675 fmtstr = '%d' if isint else '%%.%df' % ndigits
676 for e in elem:
677 n = elem[e] / float(natom) * natoms
678 cstr += e
679 if n != 1:
680 cstr += fmtstr % n
681 cstr += ' ' if with_spaces else ''
682 return cstr.strip()
683
684 def environment(self, *pos, **kwargs):
685 """
686 Returns a list of neighboring atoms for a given position within the the
687 unit cell.
688
689 Parameters
690 ----------
691 pos : list or array-like
692 fractional coordinate in the unit cell
693 maxdist : float
694 maximum distance wanted in the list of neighbors (default: 7)
695
696 Returns
697 -------
698 list of tuples
699 (distance, atomType, multiplicity) giving distance sorted list of
700 atoms
701 """
702
703 valid_kwargs = {'maxdist': 'maximum distance needed in the output'}
704 utilities.check_kwargs(kwargs, valid_kwargs, 'Crystal.environment')
705 maxdist = kwargs.get('maxdist', 7)
706
707 if len(pos) < 3:
708 pos = pos[0]
709 if len(pos) < 3:
710 raise InputError("need 3 coordinates of the "
711 "reference position")
712
713 refpos = self.a1 * \
714 pos[0] + self.a2 * pos[1] + self.a3 * pos[2]
715
716 lst = []
717 Na = 2 * int(ceil(maxdist / math.VecNorm(self.a1)))
718 Nb = 2 * int(ceil(maxdist / math.VecNorm(self.a2)))
719 Nc = 2 * int(ceil(maxdist / math.VecNorm(self.a3)))
720 if self.lattice.nsites > 0:
721 for a, p, o, b in self.lattice.base():
722 ucpos = (self.a1 * p[0] + self.a2 * p[1] + self.a3 * p[2])
723 for i in range(-Na, Na + 1):
724 for j in range(-Nb, Nb + 1):
725 for k in range(-Nc, Nc + 1):
726 atpos = ucpos + (self.a1 * i + self.a2 * j +
727 self.a3 * k)
728 distance = math.VecNorm(atpos - refpos)
729 if distance <= maxdist:
730 lst.append((distance, a, o))
731 else:
732 for i in range(-Na, Na + 1):
733 for j in range(-Nb, Nb + 1):
734 for k in range(-Nc, Nc + 1):
735 atpos = (self.a1 * i + self.a2 * j + self.a3 * k)
736 distance = math.VecNorm(atpos - refpos)
737 if distance <= maxdist:
738 lst.append((distance, '__dummy__', 1.))
739
740 # sort
741 lst.sort(key=operator.itemgetter(1))
742 lst.sort(key=operator.itemgetter(0))
743 rl = []
744 if len(lst) >= 1:
745 mult = lst[0][2]
746 else:
747 return rl
748 for i in range(1, len(lst)):
749 if (abs(lst[i - 1][0] - lst[i][0]) < config.EPSILON and
750 lst[i - 1][1] == lst[i][1]):
751 mult += lst[i - 1][2] # add occupancy
752 else:
753 rl.append((lst[i - 1][0], lst[i - 1][1], mult))
754 mult = lst[i][2]
755 rl.append((lst[-1][0], lst[-1][1], mult))
756
757 return rl
758
759 def planeDistance(self, *hkl):
760 """
761 determines the lattice plane spacing for the planes specified by (hkl)
762
763 Parameters
764 ----------
765 h, k, l : list, tuple or floats
766 Miller indices of the lattice planes given either as list, tuple or
767 seperate arguments
768
769 Returns
770 -------
771 float
772 the lattice plane spacing
773
774 Examples
775 --------
776 >>> xu.materials.Si.planeDistance(0, 0, 4)
777 1.3577600000000001
778
779 or
780
781 >>> xu.materials.Si.planeDistance((1, 1, 1))
782 3.1356124059796255
783 """
784 if len(hkl) < 3:
785 hkl = hkl[0]
786 if len(hkl) < 3:
787 raise InputError("need 3 indices for the lattice point")
788
789 return 2 * numpy.pi / math.VecNorm(self.Q(hkl))
790
791 def _getdensity(self):
792 """
793 calculates the mass density of an material from the mass of the atoms
794 in the unit cell.
795
796 Returns
797 -------
798 float
799 mass density in kg/m^3
800 """
801 m = 0.
802 for at, pos, occ, b in self.lattice.base():
803 m += at.weight * occ
804
805 return m / self.lattice.UnitCellVolume() * 1e30
806
807 density = property(_getdensity)
808
809 def _get_f(self, q, en):
810 """
811 optimized method to calculate the atomic scattering factor for all
812 atoms in the unit cell by calling the database only as much as needed.
813
814 Parameters
815 ----------
816 q : float or array-like
817 momentum transfer for which the atomic scattering factor should be
818 calculated
819 en : float or str
820 x-ray energy (eV)
821
822 Returns
823 -------
824 list
825 atomic scattering factors for every atom in the unit cell
826 """
827 f = {}
828 if self.lattice.nsites > 0:
829 for at, pos, occ, b in self.lattice.base():
830 if at.num not in f:
831 f[at.num] = at.f(q, en)
832 return [f[a.num] for a, p, o, b in self.lattice.base()]
833 else:
834 return None
835
836 def _get_lamen(self, en):
837 if isinstance(en, basestring) and en == 'config':
838 en = utilities.energy(config.ENERGY)
839 lam = utilities.en2lam(en)
840 return lam, en
841
842 def delta(self, en='config'):
843 """
844 function to calculate the real part of the deviation of the
845 refractive index from 1 (n=1-delta+i*beta)
846
847 Parameters
848 ----------
849 en : float or str, optional
850 x-ray energy eV, if omitted the value from the xrayutilities
851 configuration is used
852
853 Returns
854 -------
855 float
856 """
857 re = scipy.constants.physical_constants['classical electron radius'][0]
858 re *= 1e10
859
860 lam, en = self._get_lamen(en)
861 delta = 0.
862 f = self._get_f(0, en)
863 for (at, pos, occ, b), fa in zip(self.lattice.base(), f):
864 delta += numpy.real(fa) * occ
865
866 delta *= re / (2 * numpy.pi) * lam ** 2 / \
867 self.lattice.UnitCellVolume()
868 return delta
869
870 def ibeta(self, en='config'):
871 """
872 function to calculate the imaginary part of the deviation
873 of the refractive index from 1 (n=1-delta+i*beta)
874
875 Parameters
876 ----------
877 en : float or str, optional
878 x-ray energy eV, if omitted the value from the xrayutilities
879 configuration is used
880
881 Returns
882 -------
883 float
884 """
885 re = scipy.constants.physical_constants['classical electron radius'][0]
886 re *= 1e10
887
888 lam, en = self._get_lamen(en)
889 beta = 0.
890 f = self._get_f(0, en)
891 for (at, pos, occ, b), fa in zip(self.lattice.base(), f):
892 beta += numpy.imag(fa) * occ
893
894 beta *= re / (2 * numpy.pi) * lam ** 2 / self.lattice.UnitCellVolume()
895 return beta
896
897 def chi0(self, en='config'):
898 """
899 calculates the complex chi_0 values often needed in simulations.
900 They are closely related to delta and beta
901 (n = 1 + chi_r0/2 + i*chi_i0/2 vs. n = 1 - delta + i*beta)
902 """
903 re = scipy.constants.physical_constants['classical electron radius'][0]
904 re *= 1e10
905
906 lam, en = self._get_lamen(en)
907 beta = 0.
908 delta = 0.
909 if self.lattice.nsites > 0:
910 f = self._get_f(0, en)
911 for (at, pos, occ, b), f0 in zip(self.lattice.base(), f):
912 beta += numpy.imag(f0) * occ
913 delta += numpy.real(f0) * occ
914
915 v = self.lattice.UnitCellVolume()
916 beta *= re / (2 * numpy.pi) * lam ** 2 / v
917 delta *= re / (2 * numpy.pi) * lam ** 2 / v
918 return (-2 * delta + 2j * beta)
919
920 def _debyewallerfactor(self, temp, qnorm):
921 """
922 Calculate the Debye Waller temperature factor according to the Debye
923 temperature
924
925 Parameters
926 ----------
927 temp : float
928 actual temperature (K)
929 qnorm : float or array-like
930 norm of the q-vector(s) for which the factor should be calculated
931
932 Returns
933 -------
934 float or array-like
935 the Debye Waller factor(s) with the same shape as qnorm
936 """
937 if temp != 0 and self.thetaDebye:
938 # W(q) = 3/2* hbar^2*q^2/(m*kB*tD) * (D1(tD/T)/(tD/T) + 1/4)
939 # DWF = exp(-W(q)) consistent with Vaclav H. and several books
940 hbar = scipy.constants.hbar
941 kb = scipy.constants.Boltzmann
942 x = self.thetaDebye / float(temp)
943 m = 0.
944 im = 0
945 for a, p, o, b in self.lattice.base():
946 m += a.weight
947 im += 1
948 m = m / float(im)
949 exponentf = 3 / 2. * hbar ** 2 * 1.0e20 / \
950 (m * kb * self.thetaDebye) * (math.Debye1(x) / x + 0.25)
951 if config.VERBOSITY >= config.DEBUG:
952 print("XU.materials.Crystal: DWF = exp(-W*q**2) W= %g"
953 % exponentf)
954 dwf = numpy.exp(-exponentf * qnorm ** 2)
955 else:
956 dwf = 1.0
957 return dwf
958
959 def chih(self, q, en='config', temp=0, polarization='S'):
960 """
961 calculates the complex polarizability of a material for a certain
962 momentum transfer and energy
963
964 Parameters
965 ----------
966 q : list, tuple or array-like
967 momentum transfer vector in (1/A)
968 en : float or str, optional
969 x-ray energy eV, if omitted the value from the xrayutilities
970 configuration is used
971 temp : float, optional
972 temperature used for Debye-Waller-factor calculation
973 polarization : {'S', 'P'}, optional
974 sigma or pi polarization
975
976 Returns
977 -------
978 tuple
979 (abs(chih_real), abs(chih_imag)) complex polarizability
980 """
981
982 if isinstance(q, (list, tuple)):
983 q = numpy.array(q, dtype=numpy.double)
984 elif isinstance(q, numpy.ndarray):
985 pass
986 else:
987 raise TypeError("q must be a list or numpy array!")
988 qnorm = math.VecNorm(q)
989
990 if isinstance(en, basestring) and en == 'config':
991 en = utilities.energy(config.ENERGY)
992
993 if polarization not in ('S', 'P'):
994 raise ValueError("polarization must be 'S':sigma or 'P': pi!")
995
996 if self.lattice.nsites == 0:
997 return (0, 0)
998
999 dwf = self._debyewallerfactor(temp, qnorm)
1000
1001 sr = 0. + 0.j
1002 si = 0. + 0.j
1003 # a: atom, p: position, o: occupancy, b: temperature-factor
1004 f = self._get_f(qnorm, en)
1005 for (a, p, o, b), F in zip(self.lattice.base(), f):
1006 r = self.lattice.GetPoint(p)
1007 if temp == 0:
1008 dwf = numpy.exp(-b * qnorm ** 2 / (4 * numpy.pi) ** 2)
1009 fr = numpy.real(F) * o
1010 fi = numpy.imag(F) * o
1011 sr += fr * numpy.exp(-1.j * math.VecDot(q, r)) * dwf
1012 si += fi * numpy.exp(-1.j * math.VecDot(q, r)) * dwf
1013
1014 # classical electron radius
1015 c = scipy.constants
1016 r_e = 1 / (4 * numpy.pi * c.epsilon_0) * c.e ** 2 / \
1017 (c.electron_mass * c.speed_of_light ** 2) * 1e10
1018 lam = utilities.en2lam(en)
1019
1020 fact = -lam ** 2 * r_e / (numpy.pi * self.lattice.UnitCellVolume())
1021 rchi = numpy.abs(fact * sr)
1022 ichi = numpy.abs(fact * si)
1023 if polarization == 'P':
1024 theta = numpy.arcsin(qnorm * utilities.en2lam(en) / (4*numpy.pi))
1025 rchi *= numpy.cos(2 * theta)
1026 ichi *= numpy.cos(2 * theta)
1027
1028 return rchi, ichi
1029
1030 def dTheta(self, Q, en='config'):
1031 """
1032 function to calculate the refractive peak shift
1033
1034 Parameters
1035 ----------
1036 Q : list, tuple or array-like
1037 momentum transfer vector (1/A)
1038 en : float or str, optional
1039 x-ray energy eV, if omitted the value from the xrayutilities
1040 configuration is used
1041
1042 Returns
1043 -------
1044 float
1045 peak shift in degree
1046 """
1047
1048 if isinstance(en, basestring) and en == 'config':
1049 en = utilities.energy(config.ENERGY)
1050 lam = utilities.en2lam(en)
1051 dth = numpy.degrees(
1052 2 * self.delta(en) / numpy.sin(2 * numpy.arcsin(
1053 lam * VecNorm(Q) / (4 * numpy.pi))))
1054 return dth
1055
1056 def __str__(self):
1057 ostr = super(Crystal, self).__str__()
1058 ostr += "Lattice:\n"
1059 ostr += str(self.lattice)
1060 return ostr
1061
1062 def StructureFactor(self, q, en='config', temp=0):
1063 """
1064 calculates the structure factor of a material
1065 for a certain momentum transfer and energy
1066 at a certain temperature of the material
1067
1068 Parameters
1069 ----------
1070 q : list, tuple or array-like
1071 vectorial momentum transfer
1072 en : float or str, optional
1073 x-ray energy eV, if omitted the value from the xrayutilities
1074 configuration is used
1075 temp : float
1076 temperature used for Debye-Waller-factor calculation
1077
1078 Returns
1079 -------
1080 complex
1081 the complex structure factor
1082 """
1083
1084 if isinstance(q, (list, tuple)):
1085 q = numpy.array(q, dtype=numpy.double)
1086 elif isinstance(q, numpy.ndarray):
1087 pass
1088 else:
1089 raise TypeError("q must be a list or numpy array!")
1090
1091 if isinstance(en, basestring) and en == 'config':
1092 en = utilities.energy(config.ENERGY)
1093
1094 if self.lattice.nsites == 0:
1095 return 1.
1096
1097 qnorm = math.VecNorm(q)
1098 dwf = self._debyewallerfactor(temp, qnorm)
1099
1100 s = 0. + 0.j
1101 f = self._get_f(qnorm, en)
1102 # a: atom, p: position, o: occupancy, b: temperature-factor
1103 for (a, p, o, b), fq in zip(self.lattice.base(), f):
1104 r = self.lattice.GetPoint(p)
1105 if temp == 0:
1106 dwf = numpy.exp(-b * qnorm ** 2 /
1107 (4 * numpy.pi) ** 2)
1108 s += fq * o * numpy.exp(-1.j * math.VecDot(q, r)) * dwf
1109
1110 return s
1111
1112 def StructureFactorForEnergy(self, q0, en, temp=0):
1113 """
1114 calculates the structure factor of a material
1115 for a certain momentum transfer and a bunch of energies
1116
1117 Parameters
1118 ----------
1119 q0 : list, tuple or array-like
1120 vectorial momentum transfer
1121 en : list, tuple or array-like
1122 energy values in eV
1123 temp : float
1124 temperature used for Debye-Waller-factor calculation
1125
1126 Returns
1127 -------
1128 array-like
1129 complex valued structure factor array
1130 """
1131 if isinstance(q0, (list, tuple)):
1132 q = numpy.array(q0, dtype=numpy.double)
1133 elif isinstance(q0, numpy.ndarray):
1134 q = q0
1135 else:
1136 raise TypeError("q must be a list or numpy array!")
1137 qnorm = math.VecNorm(q)
1138
1139 if isinstance(en, (list, tuple)):
1140 en = numpy.array(en, dtype=numpy.double)
1141 elif isinstance(en, numpy.ndarray):
1142 pass
1143 else:
1144 raise TypeError("Energy data must be provided as a list "
1145 "or numpy array!")
1146
1147 if self.lattice.nsites == 0:
1148 return numpy.ones(len(en))
1149
1150 dwf = self._debyewallerfactor(temp, qnorm)
1151
1152 s = 0. + 0.j
1153 f = self._get_f(qnorm, en)
1154 # a: atom, p: position, o: occupancy, b: temperature-factor
1155 for (a, p, o, b), fq in zip(self.lattice.base(), f):
1156 if temp == 0:
1157 dwf = numpy.exp(-b * qnorm ** 2 / (4 * numpy.pi) ** 2)
1158 r = self.lattice.GetPoint(p)
1159 s += fq * o * dwf * numpy.exp(-1.j * math.VecDot(q, r))
1160
1161 return s
1162
1163 def StructureFactorForQ(self, q, en0='config', temp=0):
1164 """
1165 calculates the structure factor of a material
1166 for a bunch of momentum transfers and a certain energy
1167
1168 Parameters
1169 ----------
1170 q : list of vectors or array-like
1171 vectorial momentum transfers; list of vectores (list, tuple or
1172 array) of length 3
1173 e.g.: (Si.Q(0, 0, 4), Si.Q(0, 0, 4.1),...) or
1174 numpy.array([Si.Q(0, 0, 4), Si.Q(0, 0, 4.1)])
1175 en0 : float or str, optional
1176 x-ray energy eV, if omitted the value from the xrayutilities
1177 configuration is used
1178 temp : float
1179 temperature used for Debye-Waller-factor calculation
1180
1181 Returns
1182 -------
1183 array-like
1184 complex valued structure factor array
1185 """
1186 if isinstance(q, (list, tuple, numpy.ndarray)):
1187 q = numpy.asarray(q, dtype=numpy.double)
1188 else:
1189 raise TypeError("q must be a list or numpy array!")
1190 if len(q.shape) != 2:
1191 raise ValueError("q does not have the correct shape (shape = %s)"
1192 % str(q.shape))
1193 qnorm = numpy.linalg.norm(q, axis=1)
1194
1195 if isinstance(en0, basestring) and en0 == 'config':
1196 en0 = utilities.energy(config.ENERGY)
1197
1198 if self.lattice.nsites == 0:
1199 return numpy.ones(len(q))
1200
1201 dwf = self._debyewallerfactor(temp, qnorm)
1202
1203 s = 0. + 0.j
1204 f = self._get_f(qnorm, en0)
1205 # a: atom, p: position, o: occupancy, b: temperature-factor
1206 for (a, p, o, b), fq in zip(self.lattice.base(), f):
1207 if temp == 0:
1208 dwf = numpy.exp(-b * qnorm ** 2 / (4 * numpy.pi) ** 2)
1209
1210 r = self.lattice.GetPoint(p)
1211 s += fq * o * numpy.exp(-1.j * numpy.dot(q, r)) * dwf
1212
1213 return s
1214
1215 def ApplyStrain(self, strain):
1216 """
1217 Applies a certain strain on the lattice of the material. The result is
1218 a change in the base vectors of the real space as well as reciprocal
1219 space lattice. The full strain matrix (3x3) needs to be given.
1220
1221 Note:
1222 NO elastic response of the material will be considered!
1223 """
1224 # let strain act on the unit cell vectors
1225 self.lattice.ApplyStrain(strain)
1226
1227 def GetMismatch(self, mat):
1228 """
1229 Calculate the mismatch strain between the material and a second
1230 material
1231 """
1232 raise NotImplementedError("XU.material.GetMismatch: "
1233 "not implemented yet")
1234
1235 def distances(self):
1236 """
1237 function to obtain distances of atoms in the crystal up to the unit
1238 cell size (largest value of a, b, c is the cut-off)
1239
1240 returns a list of tuples with distance d and number of occurence n
1241 [(d1, n1), (d2, n2),...]
1242
1243 Note:
1244 if the base of the material is empty the list will be empty
1245 """
1246
1247 if self.lattice.nsites == 0:
1248 return []
1249
1250 cutoff = numpy.max((self.lattice.a, self.lattice.b, self.lattice.c))
1251
1252 tmp_data = []
1253
1254 for at1 in self.lattice.base():
1255 for at2 in self.lattice.base():
1256 dis = math.VecNorm(self.lattice.GetPoint(at1[1] - at2[1]))
1257 dis2 = math.VecNorm(self.lattice.GetPoint(
1258 at1[1] - at2[1] + numpy.array((1, 0, 0))))
1259 dis3 = math.VecNorm(self.lattice.GetPoint(
1260 at1[1] - at2[1] + numpy.array((0, 1, 0))))
1261 dis4 = math.VecNorm(self.lattice.GetPoint(
1262 at1[1] - at2[1] + numpy.array((0, 0, 1))))
1263 dis5 = math.VecNorm(self.lattice.GetPoint(
1264 at1[1] - at2[1] + numpy.array((-1, 0, 0))))
1265 dis6 = math.VecNorm(self.lattice.GetPoint(
1266 at1[1] - at2[1] + numpy.array((0, -1, 0))))
1267 dis7 = math.VecNorm(self.lattice.GetPoint(
1268 at1[1] - at2[1] + numpy.array((0, 0, -1))))
1269 distances = sorted([dis, dis2, dis3, dis4, dis5, dis6, dis7])
1270
1271 for dis in distances:
1272 if dis < cutoff:
1273 tmp_data.append(dis)
1274
1275 # sort the list and compress equal entries
1276 tmp_data.sort()
1277
1278 self._distances = [0]
1279 self._dis_hist = [0]
1280 for dis in tmp_data:
1281 if numpy.round(dis - self._distances[-1],
1282 int(abs(numpy.log10(config.EPSILON)))) == 0:
1283 self._dis_hist[-1] += 1
1284 else:
1285 self._distances.append(dis)
1286 self._dis_hist.append(1)
1287
1288 # create return value
1289 ret = []
1290 for i in range(len(self._distances)):
1291 ret.append((self._distances[i], self._dis_hist[i]))
1292
1293 return ret
1294
1295 def show_unitcell(self, fig=None, subplot=111, scale=0.6, complexity=11,
1296 linewidth=2):
1297 """
1298 primitive visualization of the unit cell using matplotlibs basic 3D
1299 functionality -> expect rendering inaccuracies!
1300
1301 Note:
1302 For more precise visualization export to CIF and use a proper
1303 crystal structure viewer.
1304
1305 Parameters
1306 ----------
1307 fig : matplotlib Figure or None, optional
1308 subplot : int or list, optional
1309 subplot to use for the visualization. This argument of fowarded to
1310 the first argument of matplotlibs `add_subplot` function
1311 scale : float, optional
1312 scale the size of the atoms by this additional factor. By default
1313 the size of the atoms corresponds to 60% of their atomic radius.
1314 complexity : int, optional
1315 number of steps to approximate the atoms as spheres. higher values
1316 cause significant slower plotting.
1317 linewidth : float, optional
1318 line thickness of the unit cell outline
1319 """
1320 plot, plt = utilities.import_matplotlib_pyplot('XU.materials')
1321 try:
1322 import mpl_toolkits.mplot3d
1323 except ImportError:
1324 plot = False
1325
1326 if not plot:
1327 print('matplotlib (including mplot3d) needed for show_unitcell()')
1328 return
1329
1330 if fig is None:
1331 fig = plt.figure()
1332 ax = fig.add_subplot(subplot, projection='3d')
1333
1334 phi, theta = numpy.mgrid[0:numpy.pi:1j*complexity,
1335 0:2*numpy.pi:1j*complexity]
1336
1337 for a, pos, occ, b in self.lattice.base():
1338 r = a.radius * scale
1339 for i in range(-1, 2):
1340 for j in range(-1, 2):
1341 for k in range(-1, 2):
1342 atpos = (pos + [i, j, k])
1343 inunitcell = True
1344 for l in range(3):
1345 if (atpos[l] < -config.EPSILON or
1346 atpos[l] > 1+config.EPSILON):
1347 inunitcell = False
1348 if inunitcell:
1349 vecpos = atpos[0]*self.a1 + atpos[1]*self.a2 +\
1350 atpos[2]*self.a3
1351 x = r*numpy.sin(phi)*numpy.cos(theta) + vecpos[0]
1352 y = r*numpy.sin(phi)*numpy.sin(theta) + vecpos[1]
1353 z = r*numpy.cos(phi) + vecpos[2]
1354 ax.plot_surface(x, y, z, rstride=1, cstride=1,
1355 color=a.color, alpha=occ,
1356 linewidth=0)
1357
1358 # plot unit cell outlines
1359 ax.plot([0, self.a1[0]], [0, self.a1[1]], [0, self.a1[2]], color='k',
1360 lw=linewidth)
1361 ax.plot([0, self.a2[0]], [0, self.a2[1]], [0, self.a2[2]], color='k',
1362 lw=linewidth)
1363 ax.plot([0, self.a3[0]], [0, self.a3[1]], [0, self.a3[2]], color='k',
1364 lw=linewidth)
1365 ax.plot([self.a1[0], self.a1[0]+self.a2[0]],
1366 [self.a1[1], self.a1[1]+self.a2[1]],
1367 [self.a1[2], self.a1[2]+self.a2[2]], color='k', lw=linewidth)
1368 ax.plot([self.a1[0], self.a1[0]+self.a3[0]],
1369 [self.a1[1], self.a1[1]+self.a3[1]],
1370 [self.a1[2], self.a1[2]+self.a3[2]], color='k', lw=linewidth)
1371 ax.plot([self.a2[0], self.a1[0]+self.a2[0]],
1372 [self.a2[1], self.a1[1]+self.a2[1]],
1373 [self.a2[2], self.a1[2]+self.a2[2]], color='k', lw=linewidth)
1374 ax.plot([self.a2[0], self.a2[0]+self.a3[0]],
1375 [self.a2[1], self.a2[1]+self.a3[1]],
1376 [self.a2[2], self.a2[2]+self.a3[2]], color='k', lw=linewidth)
1377 ax.plot([self.a3[0], self.a1[0]+self.a3[0]],
1378 [self.a3[1], self.a1[1]+self.a3[1]],
1379 [self.a3[2], self.a1[2]+self.a3[2]], color='k', lw=linewidth)
1380 ax.plot([self.a3[0], self.a2[0]+self.a3[0]],
1381 [self.a3[1], self.a2[1]+self.a3[1]],
1382 [self.a3[2], self.a2[2]+self.a3[2]], color='k', lw=linewidth)
1383 ax.plot([self.a1[0]+self.a2[0], self.a1[0]+self.a2[0]+self.a3[0]],
1384 [self.a1[1]+self.a2[1], self.a1[1]+self.a2[1]+self.a3[1]],
1385 [self.a1[2]+self.a2[2], self.a1[2]+self.a2[2]+self.a3[2]],
1386 color='k', lw=linewidth)
1387 ax.plot([self.a1[0]+self.a3[0], self.a1[0]+self.a2[0]+self.a3[0]],
1388 [self.a1[1]+self.a3[1], self.a1[1]+self.a2[1]+self.a3[1]],
1389 [self.a1[2]+self.a3[2], self.a1[2]+self.a2[2]+self.a3[2]],
1390 color='k', lw=linewidth)
1391 ax.plot([self.a2[0]+self.a3[0], self.a1[0]+self.a2[0]+self.a3[0]],
1392 [self.a2[1]+self.a3[1], self.a1[1]+self.a2[1]+self.a3[1]],
1393 [self.a2[2]+self.a3[2], self.a1[2]+self.a2[2]+self.a3[2]],
1394 color='k', lw=linewidth)
1395
1396 ax.set_aspect("equal")
1397 plt.tight_layout()
1398
1399
1400 def CubicElasticTensor(c11, c12, c44):
1401 """
1402 Assemble the 6x6 matrix of elastic constants for a cubic material from the
1403 three independent components of a cubic crystal
1404
1405 Parameters
1406 ----------
1407 c11, c12, c44 : float
1408 independent components of the elastic tensor of cubic materials
1409
1410 Returns
1411 -------
1412 cij : ndarray
1413 6x6 matrix with elastic constants
1414 """
1415 m = numpy.zeros((6, 6), dtype=numpy.double)
1416 m[0, 0] = c11
1417 m[1, 1] = c11
1418 m[2, 2] = c11
1419 m[3, 3] = c44
1420 m[4, 4] = c44
1421 m[5, 5] = c44
1422 m[0, 1] = m[0, 2] = c12
1423 m[1, 0] = m[1, 2] = c12
1424 m[2, 0] = m[2, 1] = c12
1425
1426 return m
1427
1428
1429 def HexagonalElasticTensor(c11, c12, c13, c33, c44):
1430 """
1431 Assemble the 6x6 matrix of elastic constants for a hexagonal material from
1432 the five independent components of a hexagonal crystal
1433
1434 Parameters
1435 ----------
1436 c11, c12, c13, c33, c44 : float
1437 independent components of the elastic tensor of a hexagonal material
1438
1439 Returns
1440 -------
1441 cij : ndarray
1442 6x6 matrix with elastic constants
1443 """
1444 m = numpy.zeros((6, 6), dtype=numpy.double)
1445 m[0, 0] = m[1, 1] = c11
1446 m[2, 2] = c33
1447 m[3, 3] = m[4, 4] = c44
1448 m[5, 5] = 0.5 * (c11 - c12)
1449 m[0, 1] = m[1, 0] = c12
1450 m[0, 2] = m[1, 2] = m[2, 0] = m[2, 1] = c13
1451
1452 return m
1453
1454
1455 def WZTensorFromCub(c11ZB, c12ZB, c44ZB):
1456 """
1457 Determines the hexagonal elastic tensor from the values of the cubic
1458 elastic tensor under the assumptions presented in Phys. Rev. B 6, 4546
1459 (1972), which are valid for the WZ <-> ZB polymorphs.
1460
1461 Parameters
1462 ----------
1463 c11, c12, c44 : float
1464 independent components of the elastic tensor of cubic materials
1465
1466 Returns
1467 -------
1468 cij : ndarray
1469 6x6 matrix with elastic constants
1470
1471 Implementation according to a patch submitted by Julian Stangl
1472 """
1473 # matrix conversions: cubic (111) to hexagonal (001) direction
1474 P = (1 / 6.) * numpy.array([[3, 3, 6],
1475 [2, 4, 8],
1476 [1, 5, -2],
1477 [2, 4, -4],
1478 [2, -2, 2],
1479 [1, -1, 4]])
1480 Q = (1 / (3 * numpy.sqrt(2))) * numpy.array([1, -1, -2])
1481
1482 cZBvec = numpy.array([c11ZB, c12ZB, c44ZB])
1483 cWZvec_BAR = numpy.dot(P, cZBvec)
1484 delta = numpy.dot(Q, cZBvec)
1485 D = numpy.array([delta**2 / cWZvec_BAR[2], 0, -delta**2 / cWZvec_BAR[2],
1486 0, delta**2 / cWZvec_BAR[0], delta**2 / cWZvec_BAR[2]])
1487 cWZvec = cWZvec_BAR - D.T
1488
1489 return HexagonalElasticTensor(cWZvec[0], cWZvec[2], cWZvec[3],
1490 cWZvec[1], cWZvec[4])
1491
1492
1493 class Alloy(Crystal):
1494 """
1495 alloys two materials from the same crystal system. If the materials have
1496 the same space group the Wyckoff positions within the unit cell will also
1497 reflect the alloying.
1498 """
1499
1500 def __init__(self, matA, matB, x):
1501 self.check_compatibility(matA, matB)
1502 lat = copy.deepcopy(matA.lattice)
1503 super(Alloy, self).__init__("None", lat, matA.cij)
1504 self.matA = matA
1505 self.matB = matB
1506 self._setxb(x)
1507
1508 @staticmethod
1509 def check_compatibility(matA, matB):
1510 csA = matA.lattice.crystal_system.split(':')[0]
1511 csB = matB.lattice.crystal_system.split(':')[0]
1512 if csA != csB:
1513 raise InputError("Crystal systems of the two materials are "
1514 "incompatible!")
1515
1516 @staticmethod
1517 def lattice_const_AB(latA, latB, x, name=''):
1518 """
1519 method to calculated the interpolation of lattice parameters and unit
1520 cell angles of the Alloy. By default linear interpolation between the
1521 value of material A and B is performed.
1522
1523 Parameters
1524 ----------
1525 latA, latB : float or vector
1526 property (lattice parameter/angle) of material A and B. A property
1527 can be a scalar or vector.
1528 x : float
1529 fraction of material B in the alloy.
1530 name : str, optional
1531 label of the property which is interpolated. Can be 'a', 'b', 'c',
1532 'alpha', 'beta', or 'gamma'.
1533 """
1534 return (latB - latA) * x + latA
1535
1536 def _getxb(self):
1537 return self._xb
1538
1539 def _setxb(self, x):
1540 self._xb = x
1541 self.name = ("%s(%2.2f)%s(%2.2f)"
1542 % (self.matA.name, 1-x, self.matB.name, x))
1543 # modify the free parameters of the lattice
1544 for k in self.lattice.free_parameters:
1545 setattr(self.lattice, k,
1546 self.lattice_const_AB(getattr(self.matA, k),
1547 getattr(self.matB, k), x, name=k))
1548 # set elastic constants
1549 self.cij = (self.matB.cij - self.matA.cij) * x + self.matA.cij
1550 self.cijkl = (self.matB.cijkl - self.matA.cijkl) * x + self.matA.cijkl
1551 # alloying in unit cell
1552 if self.matA.lattice.space_group == self.matB.lattice.space_group:
1553 self.lattice._wbase = WyckoffBase()
1554 for a, wp, o, b in self.matA.lattice._wbase:
1555 self.lattice._wbase.append(a, wp, occ=o*(1-x), b=b)
1556 for a, wp, o, b in self.matB.lattice._wbase:
1557 if (a, wp, o, b) in self.lattice._wbase:
1558 idx = self.lattice._wbase.index((a, wp, o, b))
1559 occ = self.lattice._wbase[idx][2]
1560 self.lattice._wbase[idx] = (a, wp, occ+o*x, b)
1561 else:
1562 self.lattice._wbase.append(a, wp, occ=o*x, b=b)
1563
1564 x = property(_getxb, _setxb)
1565
1566 def _checkfinitenumber(self, arg, name=""):
1567 if isinstance(arg, numbers.Number) and numpy.isfinite(arg):
1568 return float(arg)
1569 else:
1570 raise TypeError("argument (%s) must be a scalar!" % name)
1571
1572 def _checkarray(self, arg, name=""):
1573 if isinstance(arg, (list, tuple, numpy.ndarray)):
1574 return numpy.asarray(arg, dtype=numpy.double)
1575 else:
1576 raise TypeError("argument (%s) must be of type "
1577 "list, tuple or numpy.ndarray" % name)
1578
1579 def _definehelpers(self, hkl, cijA, cijB):
1580 """
1581 define helper functions for solving the content from reciprocal space
1582 positions
1583 """
1584 def a1(x):
1585 return self.lattice_const_AB(self.matA.a1, self.matB.a1,
1586 x, name='a')
1587
1588 def a2(x):
1589 return self.lattice_const_AB(self.matA.a2, self.matB.a2,
1590 x, name='b')
1591
1592 def a3(x):
1593 return self.lattice_const_AB(self.matA.a3, self.matB.a3,
1594 x, name='c')
1595
1596 def V(x):
1597 return numpy.dot(a3(x), numpy.cross(a1(x), a2(x)))
1598
1599 def b1(x):
1600 return 2 * numpy.pi / V(x) * numpy.cross(a2(x), a3(x))
1601
1602 def b2(x):
1603 return 2 * numpy.pi / V(x) * numpy.cross(a3(x), a1(x))
1604
1605 def b3(x):
1606 return 2 * numpy.pi / V(x) * numpy.cross(a1(x), a2(x))
1607
1608 def qhklx(x):
1609 return hkl[0] * b1(x) + hkl[1] * b2(x) + hkl[2] * b3(x)
1610
1611 def frac(x):
1612 return ((cijB[0, 2] + cijB[1, 2] - (cijA[0, 2] + cijA[1, 2])) * x +
1613 (cijA[0, 2] + cijA[1, 2])) / \
1614 ((cijB[2, 2] - cijA[2, 2]) * x + cijA[2, 2])
1615
1616 return a1, a2, a3, V, b1, b2, b3, qhklx, frac
1617
1618 def RelaxationTriangle(self, hkl, sub, exp):
1619 """
1620 function which returns the relaxation triangle for a
1621 Alloy of given composition. Reciprocal space coordinates are
1622 calculated using the user-supplied experimental class
1623
1624 Parameters
1625 ----------
1626 hkl : list or array-like
1627 Miller Indices
1628 sub : Crystal, or float
1629 substrate material or lattice constant
1630 exp : Experiment
1631 object from which the Transformation object and ndir are needed
1632
1633 Returns
1634 -------
1635 qy, qz : float
1636 reciprocal space coordinates of the corners of the relaxation
1637 triangle
1638
1639 """
1640 hkl = self._checkarray(hkl, "hkl")
1641 trans = exp._transform
1642 ndir = exp.ndir / VecNorm(exp.ndir)
1643
1644 if isinstance(sub, Crystal):
1645 asub = sub.lattice.a
1646 elif isinstance(sub, float):
1647 asub = sub
1648 else:
1649 raise TypeError("Second argument (sub) must be of type float or "
1650 "an instance of xrayutilities.materials.Crystal")
1651
1652 # test if inplane direction of hkl is the same as the one for the
1653 # experiment otherwise warn the user
1654 hklinplane = VecCross(VecCross(exp.ndir, hkl), exp.ndir)
1655 if (VecNorm(VecCross(hklinplane, exp.idir)) > config.EPSILON):
1656 warnings.warn("Alloy: given hkl differs from the geometry of the "
1657 "Experiment instance in the azimuthal direction")
1658
1659 # transform elastic constants to correct coordinate frame
1660 cijA = Cijkl2Cij(trans(self.matA.cijkl, rank=4))
1661 cijB = Cijkl2Cij(trans(self.matB.cijkl, rank=4))
1662
1663 a1, a2, a3, V, b1, b2, b3, qhklx, frac = self._definehelpers(hkl,
1664 cijA,
1665 cijB)
1666
1667 qr_i = trans(qhklx(self.x))[1]
1668 qr_p = trans(qhklx(self.x))[2]
1669 qs_i = copysign(2*numpy.pi/asub * VecNorm(VecCross(ndir, hkl)), qr_i)
1670 qs_p = 2*numpy.pi/asub * abs(VecDot(ndir, hkl))
1671
1672 # calculate pseudomorphic points for A and B
1673 def abulk(x):
1674 return math.VecNorm(a1(x))
1675
1676 def aperp(x):
1677 return abulk(self.x) * (1 + frac(x) * (1 - asub / abulk(self.x)))
1678
1679 qp_i = copysign(2*numpy.pi/asub * VecNorm(VecCross(ndir, hkl)), qr_i)
1680 qp_p = 2*numpy.pi/aperp(self.x) * abs(VecDot(ndir, hkl))
1681
1682 # assembly return values
1683 qy = numpy.array([qr_i, qp_i, qs_i, qr_i], dtype=numpy.double)
1684 qz = numpy.array([qr_p, qp_p, qs_p, qr_p], dtype=numpy.double)
1685
1686 return qy, qz
1687
1688
1689 class CubicAlloy(Alloy):
1690
1691 def __init__(self, matA, matB, x):
1692 # here one could check if material is really cubic!!
1693 Alloy.__init__(self, matA, matB, x)
1694
1695 def ContentBsym(self, q_perp, hkl, inpr, asub, relax):
1696 """
1697 function that determines the content of B
1698 in the alloy from the reciprocal space position
1699 of a symetric peak. As an additional input the substrates
1700 lattice parameter and the degree of relaxation must be given
1701
1702 Parameters
1703 ----------
1704 q_perp : float
1705 perpendicular peak position of the reflection hkl of the alloy in
1706 reciprocal space
1707 hkl : list
1708 Miller indices of the measured symmetric reflection (also defines
1709 the surface normal
1710 inpr : list
1711 Miller indices of a Bragg peak defining the inplane reference
1712 direction
1713 asub : float
1714 substrate lattice parameter
1715 relax : float
1716 degree of relaxation (needed to obtain the content from symmetric
1717 reciprocal space position)
1718
1719 Returns
1720 -------
1721 content : float
1722 the content of B in the alloy determined from the input variables
1723
1724 """
1725
1726 # check input parameters
1727 q_perp = self._checkfinitenumber(q_perp, "q_perp")
1728 hkl = self._checkarray(hkl, "hkl")
1729 inpr = self._checkarray(inpr, "inpr")
1730 asub = self._checkfinitenumber(asub, "asub")
1731 relax = self._checkfinitenumber(relax, "relax")
1732
1733 # calculate lattice constants from reciprocal space positions
1734 n = self.Q(hkl) / VecNorm(self.Q(hkl))
1735 q_hkl = self.Q(hkl)
1736 # the following line is not generally true! only cubic materials
1737 aperp = 2 * numpy.pi / q_perp * abs(VecDot(n, hkl))
1738
1739 # transform the elastic tensors to a coordinate frame attached to the
1740 # surface normal
1741 inp1 = VecCross(n, inpr) / VecNorm(VecCross(n, inpr))
1742 inp2 = VecCross(n, inp1)
1743 trans = math.CoordinateTransform(inp1, inp2, n)
1744
1745 if config.VERBOSITY >= config.DEBUG:
1746 print("XU.materials.Alloy.ContentB: inp1/inp2: ", inp1, inp2)
1747 cijA = Cijkl2Cij(trans(self.matA.cijkl, rank=4))
1748 cijB = Cijkl2Cij(trans(self.matB.cijkl, rank=4))
1749
1750 a1, a2, a3, V, b1, b2, b3, qhklx, frac = self._definehelpers(hkl,
1751 cijA,
1752 cijB)
1753
1754 # the following line is not generally true! only cubic materials
1755 def abulk_perp(x):
1756 return abs(2 * numpy.pi / numpy.inner(qhklx(x), n) *
1757 numpy.inner(n, hkl))
1758
1759 # can we use abulk_perp here? for cubic materials this should work?!
1760 def ainp(x):
1761 return asub + relax * (abulk_perp(x) - asub)
1762
1763 if config.VERBOSITY >= config.DEBUG:
1764 print("XU.materials.Alloy.ContentB: abulk_perp: %8.5g"
1765 % (abulk_perp(0.)))
1766
1767 def equation(x):
1768 return ((aperp - abulk_perp(x)) +
1769 (ainp(x) - abulk_perp(x)) * frac(x))
1770
1771 x = scipy.optimize.brentq(equation, -0.1, 1.1)
1772
1773 return x
1774
1775 def ContentBasym(self, q_inp, q_perp, hkl, sur):
1776 """
1777 function that determines the content of B
1778 in the alloy from the reciprocal space position
1779 of an asymmetric peak.
1780
1781 Parameters
1782 ----------
1783 q_inp : float
1784 inplane peak position of reflection hkl of the alloy in reciprocal
1785 space
1786 q_perp : float
1787 perpendicular peak position of the reflection hkl of the alloy in
1788 reciprocal space
1789 hkl : list
1790 Miller indices of the measured asymmetric reflection
1791 sur : list
1792 Miller indices of the surface (determines the perpendicular
1793 direction)
1794
1795 Returns
1796 -------
1797 content : float
1798 content of B in the alloy determined from the input variables
1799 list
1800 [a_inplane a_perp, a_bulk_perp(x), eps_inplane, eps_perp];
1801 lattice parameters calculated from the reciprocal space positions
1802 as well as the strain (eps) of the layer
1803 """
1804
1805 # check input parameters
1806 q_inp = self._checkfinitenumber(q_inp, "q_inp")
1807 q_perp = self._checkfinitenumber(q_perp, "q_perp")
1808 hkl = self._checkarray(hkl, "hkl")
1809 sur = self._checkarray(sur, "sur")
1810
1811 # check if reflection is asymmetric
1812 if math.VecNorm(math.VecCross(self.Q(hkl), self.Q(sur))) < 1.e-8:
1813 raise InputError("Miller indices of a symmetric reflection were"
1814 "given where an asymmetric reflection is needed")
1815
1816 # calculate lattice constants from reciprocal space positions
1817 n = self.Q(sur) / VecNorm(self.Q(sur))
1818 q_hkl = self.Q(hkl)
1819 # the following two lines are not generally true! only cubic materials
1820 ainp = 2 * numpy.pi / abs(q_inp) * VecNorm(VecCross(n, hkl))
1821 aperp = 2 * numpy.pi / abs(q_perp) * abs(VecDot(n, hkl))
1822
1823 # transform the elastic tensors to a coordinate frame attached to the
1824 # surface normal
1825 inp1 = VecCross(n, q_hkl) / VecNorm(VecCross(n, q_hkl))
1826 inp2 = VecCross(n, inp1)
1827 trans = math.CoordinateTransform(inp1, inp2, n)
1828
1829 cijA = Cijkl2Cij(trans(self.matA.cijkl, rank=4))
1830 cijB = Cijkl2Cij(trans(self.matB.cijkl, rank=4))
1831
1832 a1, a2, a3, V, b1, b2, b3, qhklx, frac = self._definehelpers(hkl,
1833 cijA,
1834 cijB)
1835
1836 # the following two lines are not generally true! only cubic materials
1837 def abulk_inp(x):
1838 return abs(2 * numpy.pi / numpy.inner(qhklx(x), inp2) *
1839 VecNorm(VecCross(n, hkl)))
1840
1841 def abulk_perp(x):
1842 return abs(2 * numpy.pi / numpy.inner(qhklx(x), n) *
1843 numpy.inner(n, hkl))
1844
1845 if config.VERBOSITY >= config.DEBUG:
1846 print("XU.materials.Alloy.ContentB: abulk_inp/perp: %8.5g %8.5g"
1847 % (abulk_inp(0.), abulk_perp(0.)))
1848
1849 def equation(x):
1850 return ((aperp - abulk_perp(x)) +
1851 (ainp - abulk_inp(x)) * frac(x))
1852
1853 x = scipy.optimize.brentq(equation, -0.1, 1.1)
1854
1855 eps_inplane = (ainp - abulk_perp(x)) / abulk_perp(x)
1856 eps_perp = (aperp - abulk_perp(x)) / abulk_perp(x)
1857
1858 return x, [ainp, aperp, abulk_perp(x), eps_inplane, eps_perp]
1859
1860
1861 def PseudomorphicMaterial(sub, layer, relaxation=0, trans=None):
1862 """
1863 This function returns a material whos lattice is pseudomorphic on a
1864 particular substrate material. The two materials must have similar unit
1865 cell definitions for the algorithm to work correctly, i.e. it does not work
1866 for combiniations of materials with different lattice symmetry. It is also
1867 crucial that the layer object includes values for the elastic tensor.
1868
1869 Parameters
1870 ----------
1871 sub : Crystal
1872 substrate material
1873 layer : Crystal
1874 bulk material of the layer, including its elasticity tensor
1875 relaxation : float, optional
1876 degree of relaxation 0: pseudomorphic, 1: relaxed (default: 0)
1877 trans : Tranform
1878 Transformation which transforms lattice directions into a surface
1879 orientated coordinate frame (x, y inplane, z out of plane). If None a
1880 (001) surface geometry of a cubic material is assumed.
1881
1882 Returns
1883 -------
1884 An instance of Crystal holding the new pseudomorphically
1885 strained material.
1886
1887 Raises
1888 ------
1889 InputError
1890 If the layer material has no elastic parameters
1891 """
1892 def get_inplane(lat):
1893 """determine inplane lattice parameter"""
1894 return (math.VecNorm(lat.GetPoint(trans.inverse((1, 0, 0)))) +
1895 math.VecNorm(lat.GetPoint(trans.inverse((0, 1, 0))))) / 2.
1896
1897 if not trans:
1898 trans = math.Transform(numpy.identity(3))
1899
1900 if numpy.all(layer.cijkl == 0):
1901 raise InputError("'layer' argument needs elastic parameters")
1902
1903 slat = sub.lattice
1904 llat = layer.lattice
1905 # calculate the strain
1906 asub = get_inplane(sub.lattice)
1907 abulk = get_inplane(layer.lattice)
1908 apar = asub + (abulk - asub) * relaxation
1909 epar = (apar - abulk) / abulk
1910 cT = trans(layer.cijkl, rank=4)
1911
1912 eperp = -epar * (cT[1, 1, 2, 2] + cT[2, 2, 0, 0]) / (cT[2, 2, 2, 2])
1913 eps = trans.inverse(numpy.diag((epar, epar, eperp)), rank=2)
1914 if config.VERBOSITY >= config.INFO_ALL:
1915 print("XU.materials.PseudomorphicMaterial: applying strain (inplane, "
1916 "perpendicular): %.4g %.4g" % (epar, eperp))
1917
1918 # create the pseudomorphic material
1919 pmlatt = copy.deepcopy(layer.lattice)
1920 pmat = Crystal(layer.name, pmlatt, layer.cij)
1921 pmat.ApplyStrain(eps)
1922 return pmat
+0
-242
xrayutilities/materials/plot.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import math
18 from math import pi
19
20 import numpy
21
22 from .. import config, utilities
23 from ..math import VecNorm
24
25
26 def show_reciprocal_space_plane(
27 mat, exp, ttmax=None, maxqout=0.01, scalef=100, ax=None, color=None,
28 show_Laue=True, show_legend=True, projection='perpendicular',
29 label=None):
30 """
31 show a plot of the coplanar diffraction plane with peak positions for the
32 respective material. the size of the spots is scaled with the strength of
33 the structure factor
34
35 Parameters
36 ----------
37 mat: Crystal
38 instance of Crystal for structure factor calculations
39 exp: Experiment
40 instance of Experiment (likely HXRD, or FourC). defines the inplane and
41 out of plane direction as well as the sample azimuth
42 ttmax: float, optional
43 maximal 2Theta angle to consider, by default 180deg
44 maxqout: float, optional
45 maximal out of plane q for plotted Bragg peaks as fraction of exp.k0
46 scalef: float, optional
47 scale factor for the marker size
48 ax: matplotlib.Axes, optional
49 matplotlib Axes to use for the plot, useful if multiple materials
50 should be plotted in one plot
51 color: matplotlib color, optional
52 show_Laue: bool, optional
53 flag to indicate if the Laue zones should be indicated
54 show_legend: bool, optional
55 flag to indiate if a legend should be shown
56 projection: 'perpendicular', 'polar', optional
57 type of projection for Bragg peaks which do not fall into the
58 diffraction plane. 'perpendicular' (default) uses only the inplane
59 component in the scattering plane, whereas 'polar' uses the vectorial
60 absolute value of the two inplane components. See also the 'maxqout'
61 option.
62 label: None or str, optional
63 label to be used for the legend. If 'None' the name of the material
64 will be used.
65
66 Returns
67 -------
68 Axes, plot_handle
69 """
70 def get_peaks(mat, exp, ttmax):
71 """
72 Parameters
73 ----------
74 mat: Crystal
75 instance of Crystal for structure factor calculations
76 exp: Experiment
77 instance of Experiment (likely HXRD, or FourC)
78 tt_cutoff: float
79 maximal 2Theta angle to consider, by default 180deg
80
81 Returns
82 -------
83 ndarray
84 data array with columns for 'q', 'qvec', 'hkl', 'r' for the Bragg
85 peaks
86 """
87 # calculate maximal Bragg indices
88 hma = int(math.ceil(VecNorm(mat.a1) * exp.k0 / pi *
89 math.sin(math.radians(ttmax / 2.))))
90 hmi = -hma
91 kma = int(math.ceil(VecNorm(mat.a2) * exp.k0 / pi *
92 math.sin(math.radians(ttmax / 2.))))
93 kmi = -kma
94 lma = int(math.ceil(VecNorm(mat.a3) * exp.k0 / pi *
95 math.sin(math.radians(ttmax / 2.))))
96 lmi = -lma
97
98 # calculate structure factors
99 qmax = 2 * exp.k0 * math.sin(math.radians(ttmax/2.))
100 hkl = numpy.mgrid[hma:hmi-1:-1,
101 kma:kmi-1:-1,
102 lma:lmi-1:-1].reshape(3, -1).T
103 q = mat.Q(hkl)
104 qnorm = VecNorm(q)
105 m = qnorm < qmax
106
107 data = numpy.zeros(numpy.sum(m), dtype=[('q', numpy.double),
108 ('qvec', numpy.ndarray),
109 ('r', numpy.double),
110 ('hkl', numpy.ndarray)])
111 data['q'] = qnorm[m]
112 data['qvec'] = list(exp.Transform(q[m]))
113 rref = abs(mat.StructureFactor((0, 0, 0), exp.energy)) ** 2
114 data['r'] = numpy.abs(mat.StructureFactorForQ(q[m], exp.energy)) ** 2
115 data['r'] /= rref
116 data['hkl'] = list(hkl[m])
117
118 return data
119
120 plot, plt = utilities.import_matplotlib_pyplot('XU.materials')
121
122 if not plot:
123 print('matplotlib needed for show_reciprocal_space_plane')
124 return
125
126 if ttmax is None:
127 ttmax = 180
128
129 d = get_peaks(mat, exp, ttmax)
130 k0 = exp.k0
131
132 if not ax:
133 fig = plt.figure(figsize=(9, 5))
134 ax = plt.subplot(111)
135 else:
136 fig = ax.get_figure()
137 plt.sca(ax)
138
139 plt.axis('scaled')
140 ax.set_autoscaley_on(False)
141 ax.set_autoscalex_on(False)
142 plt.xlim(-2.05*k0, 2.05*k0)
143 plt.ylim(-0.05*k0, 2.05*k0)
144
145 if show_Laue:
146 c = plt.matplotlib.patches.Circle((0, 0), 2*k0, facecolor='#FF9180',
147 edgecolor='none')
148 ax.add_patch(c)
149 qmax = 2 * k0 * math.sin(math.radians(ttmax/2.))
150 c = plt.matplotlib.patches.Circle((0, 0), qmax, facecolor='#FFFFFF',
151 edgecolor='none')
152 ax.add_patch(c)
153
154 c = plt.matplotlib.patches.Circle((0, 0), 2*k0, facecolor='none',
155 edgecolor='0.5')
156 ax.add_patch(c)
157 c = plt.matplotlib.patches.Circle((k0, 0), k0, facecolor='none',
158 edgecolor='0.5')
159 ax.add_patch(c)
160 c = plt.matplotlib.patches.Circle((-k0, 0), k0, facecolor='none',
161 edgecolor='0.5')
162 ax.add_patch(c)
163 plt.hlines(0, -2*k0, 2*k0, color='0.5', lw=0.5)
164 plt.vlines(0, -2*k0, 2*k0, color='0.5', lw=0.5)
165
166 # generate mask for plotting
167 m = numpy.zeros_like(d, dtype=numpy.bool)
168 for i, (q, r) in enumerate(zip(d['qvec'], d['r'])):
169 if (abs(q[0]) < maxqout*k0 and r > config.EPSILON):
170 m[i] = True
171
172 x = numpy.empty_like(d['r'][m])
173 y = numpy.empty_like(d['r'][m])
174 s = numpy.empty_like(d['r'][m])
175 for i, (qv, r) in enumerate(zip(d['qvec'][m], d['r'][m])):
176 if projection == 'perpendicular':
177 x[i] = qv[1]
178 else:
179 x[i] = numpy.sign(qv[1])*numpy.sqrt(qv[0]**2 + qv[1]**2)
180 y[i] = qv[2]
181 s[i] = r*scalef
182 label = label if label else mat.name
183 h = plt.scatter(x, y, s=s, zorder=2, label=label)
184 if color:
185 h.set_color(color)
186
187 plt.xlabel(r'$Q$ inplane ($\mathrm{\AA^{-1}}$)')
188 plt.ylabel(r'$Q$ out of plane ($\mathrm{\AA^{-1}}$)')
189
190 if show_legend:
191 if len(fig.legends) == 1:
192 fig.legends[0].remove()
193 fig.legend(*ax.get_legend_handles_labels(), loc='upper right')
194 plt.tight_layout()
195
196 annot = ax.annotate("", xy=(0, 0), xytext=(20, 20),
197 textcoords="offset points",
198 bbox=dict(boxstyle="round", fc="w"),
199 arrowprops=dict(arrowstyle="->"))
200 annot.set_visible(False)
201
202 def update_annot(ind):
203 pos = h.get_offsets()[ind["ind"][0]]
204 annot.xy = pos
205 text = "{}\n{}".format(mat.name,
206 str(d['hkl'][m][ind['ind'][0]]))
207 annot.set_text(text)
208 annot.get_bbox_patch().set_facecolor(h.get_facecolor()[0])
209 annot.get_bbox_patch().set_alpha(0.2)
210
211 def hover(event):
212 vis = annot.get_visible()
213 if event.inaxes == ax:
214 cont, ind = h.contains(event)
215 if cont:
216 update_annot(ind)
217 annot.set_visible(True)
218 fig.canvas.draw_idle()
219 else:
220 if vis:
221 annot.set_visible(False)
222 fig.canvas.draw_idle()
223
224 def click(event):
225 if event.inaxes == ax:
226 cont, ind = h.contains(event)
227 if cont:
228 i = ind['ind'][0]
229 popts = numpy.get_printoptions()
230 numpy.set_printoptions(precision=4, suppress=True)
231 angles = exp.Q2Ang(d['qvec'][m][ind['ind'][0]], trans=False,
232 geometry='real')
233 text = "{}\nhkl: {}\nangles: {}".format(
234 mat.name, str(d['hkl'][m][ind['ind'][0]]), str(angles))
235 numpy.set_printoptions(**popts)
236 print(text)
237
238 fig.canvas.mpl_connect("motion_notify_event", hover)
239 fig.canvas.mpl_connect("button_press_event", click)
240
241 return ax, h
+0
-275
xrayutilities/materials/predefined_materials.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2013-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import numpy
18
19 from . import __path__
20 from . import elements as e
21 from .heuslerlib import *
22 from .material import (Crystal, CubicAlloy, CubicElasticTensor,
23 HexagonalElasticTensor, WZTensorFromCub)
24 from .spacegrouplattice import SGLattice
25
26 # some predefined materials
27 # PLEASE use N/m^2 as unit for cij for newly entered material ( 1 dyn/cm^2 =
28 # 0.1 N/m^2 = 0.1 GPa)
29 # Use Kelvin as unit for the Debye temperature
30 # ref http://www.semiconductors.co.uk/propiviv5431.htm
31 C = Crystal("C", SGLattice('227:1', 3.5668, atoms=[e.C, ], pos=['8a', ]),
32 CubicElasticTensor(1.0764e12, 0.125e12, 0.577e12))
33 C_HOPG = Crystal('HOPG', SGLattice(194, 2.4612, 6.7079,
34 atoms=[e.C, e.C], pos=['2b', '2c']))
35 Si = Crystal("Si", SGLattice('227:1', 5.43104, atoms=[e.Si, ], pos=['8a', ]),
36 CubicElasticTensor(165.77e+9, 63.93e+9, 79.62e+9), thetaDebye=640)
37 Ge = Crystal("Ge", SGLattice('227:1', 5.65785, atoms=[e.Ge, ], pos=['8a', ]),
38 CubicElasticTensor(128.5e+9, 48.3e+9, 66.8e+9), thetaDebye=374)
39 InAs = Crystal("InAs", SGLattice(216, 6.0583, atoms=[e.In, e.As],
40 pos=['4a', '4c']),
41 CubicElasticTensor(8.34e+10, 4.54e+10, 3.95e+10),
42 thetaDebye=280)
43 InP = Crystal("InP", SGLattice(216, 5.8687, atoms=[e.In, e.P],
44 pos=['4a', '4c']),
45 CubicElasticTensor(10.11e+10, 5.61e+10, 4.56e+10),
46 thetaDebye=425)
47 InSb = Crystal("InSb", SGLattice(216, 6.47937, atoms=[e.In, e.Sb],
48 pos=['4a', '4c']),
49 CubicElasticTensor(6.66e+10, 3.65e+10, 3.02e+10),
50 thetaDebye=160)
51 GaP = Crystal("GaP", SGLattice(216, 5.4505, atoms=[e.Ga, e.P],
52 pos=['4a', '4c']),
53 CubicElasticTensor(14.05e+10, 6.20e+10, 7.03e+10),
54 thetaDebye=445)
55 GaAs = Crystal("GaAs", SGLattice(216, 5.65325, atoms=[e.Ga, e.As],
56 pos=['4a', '4c']),
57 CubicElasticTensor(11.9e+10, 5.34e+10, 5.96e+10),
58 thetaDebye=360)
59 AlAs = Crystal("AlAs", SGLattice(216, 5.6611, atoms=[e.Al, e.As],
60 pos=['4a', '4c']),
61 CubicElasticTensor(12.02e+10, 5.70e+10, 5.99e+10),
62 thetaDebye=446)
63 GaSb = Crystal("GaSb", SGLattice(216, 6.09593, atoms=[e.Ga, e.Sb],
64 pos=['4a', '4c']),
65 CubicElasticTensor(8.83e+10, 4.02e+10, 4.32e+10),
66 thetaDebye=266)
67 # from Cryst. Growth Des. 15, 4795-4803 (2015)
68 GaAsWZ = Crystal("GaAs(WZ)",
69 SGLattice(186, 3.9845, 6.5701, atoms=[e.Ga, e.As],
70 pos=[('2b', 0), ('2b', 3/8.)]),
71 WZTensorFromCub(11.9e+10, 5.34e+10, 5.96e+10))
72 GaAs4H = Crystal("GaAs(4H)",
73 SGLattice(186, 3.9900, 13.0964,
74 atoms=[e.Ga, e.Ga, e.As, e.As],
75 pos=[('2a', 0), ('2b', 1/4.),
76 ('2a', 3/16.), ('2b', 7/16.)]))
77 # from Phys. Rev. B 88, 115315 (2013)
78 GaPWZ = Crystal("GaP(WZ)",
79 SGLattice(186, 3.8419, 6.3353, atoms=[e.Ga, e.P],
80 pos=[('2b', 0), ('2b', 0.37385)]),
81 WZTensorFromCub(14.05e+10, 6.20e+10, 7.03e+10))
82 # from Nanotechnology 22 425704 (2011)
83 InPWZ = Crystal("InP(WZ)",
84 SGLattice(186, 4.1423, 6.8013, atoms=[e.In, e.P],
85 pos=[('2b', 0), ('2b', 3/8.)]),
86 WZTensorFromCub(10.11e+10, 5.61e+10, 4.56e+10))
87 # from Nano Lett., 2011, 11 (4), pp 1483-1489
88 InAsWZ = Crystal("InAs(WZ)",
89 SGLattice(186, 4.2742, 7.0250, atoms=[e.In, e.As],
90 pos=[('2b', 0), ('2b', 3/8.)]),
91 WZTensorFromCub(8.34e+10, 4.54e+10, 3.95e+10))
92 InAs4H = Crystal("InAs(4H)",
93 SGLattice(186, 4.2780, 14.0171,
94 atoms=[e.In, e.In, e.As, e.As],
95 pos=[('2a', 0), ('2b', 1/4.),
96 ('2a', 3/16.), ('2b', 7/16.)]))
97 InSbWZ = Crystal("InSb(WZ)",
98 SGLattice(186, 4.5712, 7.5221, atoms=[e.In, e.Sb],
99 pos=[('2b', 0), ('2b', 3/8.)]),
100 WZTensorFromCub(6.66e+10, 3.65e+10, 3.02e+10))
101 InSb4H = Crystal("InSb(4H)",
102 SGLattice(186, 4.5753, 15.0057,
103 atoms=[e.In, e.In, e.Sb, e.Sb],
104 pos=[('2a', 0), ('2b', 1/4.),
105 ('2a', 3/16.), ('2b', 7/16.)]))
106
107 # ? Unit of elastic constants for CdTe, PbTe, PbSe ?
108 PbTe = Crystal("PbTe",
109 SGLattice(225, 6.464, atoms=[e.Pb, e.Te], pos=['4a', '4b']),
110 CubicElasticTensor(93.6, 7.7, 13.4))
111 PbSe = Crystal("PbSe",
112 SGLattice(225, 6.128, atoms=[e.Pb, e.Se], pos=['4a', '4b']),
113 CubicElasticTensor(123.7, 19.3, 15.9))
114 CdTe = Crystal("CdTe", SGLattice(216, 6.482, atoms=[e.Cd, e.Te],
115 pos=['4a', '4c']),
116 CubicElasticTensor(53.5, 36.7, 19.9))
117 CdSe = Crystal("CdSe", SGLattice(186, 4.5712, 7.5221, atoms=[e.In, e.Sb],
118 pos=[('2b', 0), ('2b', 3/8.)]),
119 HexagonalElasticTensor(7.490e10, 4.609e10, 3.926e10,
120 8.451e10, 1.315e10))
121 CdSe_ZB = Crystal("CdSe(ZB)", SGLattice(216, 6.052, atoms=[e.Cd, e.Se],
122 pos=['4a', '4c']))
123 HgSe = Crystal("HgSe", SGLattice(216, 6.085, atoms=[e.Hg, e.Se],
124 pos=['4a', '4c']),
125 CubicElasticTensor(6.1e10, 4.4e10, 2.2e10))
126 NaCl = Crystal("NaCl",
127 SGLattice(225, 5.6402, atoms=[e.Na, e.Cl], pos=['4a', '4b']))
128 MgO = Crystal("MgO",
129 SGLattice(225, 4.212, atoms=[e.Mg, e.O], pos=['4a', '4b']))
130 GaN = Crystal("GaN",
131 SGLattice(186, 3.189, 5.186, atoms=[e.Ga, e.N],
132 pos=[('2b', 0), ('2b', 3/8.)]),
133 HexagonalElasticTensor(390.e9, 145.e9, 106.e9, 398.e9, 105.e9),
134 thetaDebye=600)
135 BaF2 = Crystal("BaF2", SGLattice(225, 6.2001, atoms=[e.Ba, e.F],
136 pos=['4a', '8c']))
137 SrF2 = Crystal("SrF2", SGLattice(225, 5.8007, atoms=[e.Sr, e.F],
138 pos=['4a', '8c']))
139 CaF2 = Crystal("CaF2", SGLattice(225, 5.4631, atoms=[e.Ca, e.F],
140 pos=['4a', '8c']))
141 MnO = Crystal("MnO", SGLattice(225, 4.444, atoms=[e.Mn, e.O],
142 pos=['4a', '4b']))
143 MnTe = Crystal("MnTe", SGLattice(186, 4.1429, 6.7031, atoms=[e.Mn, e.Te],
144 pos=[('2a', 0), ('2b', 0.25)]))
145 GeTe = Crystal("GeTe",
146 SGLattice('160:R', 5.996, 88.18, atoms=[e.Ge, e.Ge, e.Te, e.Te],
147 pos=[('1a', -0.237), ('3b', (0.5-0.237, -0.237)),
148 ('1a', 0.237), ('3b', (0.5+0.237, +0.237))]))
149 SnTe = Crystal("SnTe",
150 SGLattice(225, 6.3268, atoms=[e.Sn, e.Te], pos=['4a', '4b']))
151 Al = Crystal("Al", SGLattice(225, 4.04958, atoms=[e.Al, ], pos=['4a', ]))
152 Au = Crystal("Au", SGLattice(225, 4.0782, atoms=[e.Au, ], pos=['4a', ]))
153 Fe = Crystal("Fe", SGLattice(229, 2.8665, atoms=[e.Fe, ], pos=['2a', ]))
154 Cr = Crystal("Cr", SGLattice(229, 2.910, atoms=[e.Cr, ], pos=['2a', ]))
155 Co = Crystal("Co", SGLattice(194, 2.5071, 4.0695, atoms=[e.Co, ],
156 pos=['2c', ]))
157 Ti = Crystal("Ti", SGLattice(194, 2.9508, 4.6855, atoms=[e.Ti, ],
158 pos=['2c', ]))
159 Mo = Crystal("Mo", SGLattice(229, 3.147, atoms=[e.Mo, ], pos=['2a', ]))
160 Ru = Crystal("Ru", SGLattice(194, 2.7059, 4.2815, atoms=[e.Ru, ],
161 pos=['2c', ]))
162 Rh = Crystal("Rh", SGLattice(225, 3.8034, atoms=[e.Rh, ], pos=['4a', ]))
163 V = Crystal("V", SGLattice(229, 3.024, atoms=[e.V, ], pos=['2a', ]))
164 Ta = Crystal("Ta", SGLattice(229, 3.306, atoms=[e.Ta, ], pos=['2a', ]))
165 Nb = Crystal("Nb", SGLattice(229, 3.3004, atoms=[e.Nb, ], pos=['2a', ]))
166 Pt = Crystal("Pt", SGLattice(225, 3.9242, atoms=[e.Pt, ], pos=['4a', ]))
167 Ag2Se = Crystal("Ag2Se", SGLattice(19, 4.333, 7.062, 7.764,
168 atoms=[e.Ag, e.Ag, e.Se],
169 pos=[('4a', (0.107, 0.369, 0.456)),
170 ('4a', (0.728, 0.029, 0.361)),
171 ('4a', (0.358, 0.235, 0.149))]))
172 TiO2 = Crystal("TiO2", SGLattice(136, 4.59, 2.96, atoms=[e.Ti, e.O],
173 pos=['2a', ('4f', 0.30479)]))
174 MnO2 = Crystal("MnO2", SGLattice(136, 4.40, 2.87, atoms=[e.Mn, e.O],
175 pos=['2a', ('4f', 0.30479)]))
176 VO2_Rutile = Crystal("VO2", SGLattice(136, 4.55, 2.88, atoms=[e.V, e.O],
177 pos=['2a', ('4f', 0.305)]))
178 VO2_Baddeleyite = Crystal("VO2", SGLattice(14, 5.75, 5.42, 5.38, 122.6,
179 atoms=[e.V, e.O, e.O],
180 pos=[('4e', (0.242, 0.975, 0.025)),
181 ('4e', (0.1, 0.21, 0.20)),
182 ('4e', (0.39, 0.69, 0.29))]))
183 SiO2 = Crystal("SiO2", SGLattice(154, 4.916, 5.4054, atoms=[e.Si, e.O],
184 pos=[('3a', 0.46970),
185 ('6c', (0.41350, 0.26690,
186 0.11910+2/3.))]))
187 In = Crystal("In", SGLattice(139, 3.2523, 4.9461, atoms=[e.In, ],
188 pos=['2a', ]))
189 Sb = Crystal("Sb", SGLattice('166:H', 4.307, 11.273, atoms=[e.Sb, ],
190 pos=[('6c', 0.23349), ]))
191 Sn = Crystal("Sn", SGLattice('141:1', 5.8197, 3.17488, atoms=[e.Sn, ],
192 pos=['4a', ]))
193 Ag = Crystal("Ag", SGLattice(225, 4.0853, atoms=[e.Ag, ], pos=['4a', ]))
194 SnAlpha = Crystal("Sn-alpha", SGLattice('227:1', 6.4912, atoms=[e.Sn, ],
195 pos=['8a', ]))
196 Cu = Crystal("Cu", SGLattice(225, 3.61496, atoms=[e.Cu, ], pos=['4a', ]))
197 CaTiO3 = Crystal("CaTiO3", SGLattice(221, 3.795, atoms=[e.Ca, e.Ti, e.O],
198 pos=['1a', '1b', '3c']))
199 SrTiO3 = Crystal("SrTiO3", SGLattice(221, 3.905, atoms=[e.Sr, e.Ti, e.O],
200 pos=['1a', '1b', '3c']))
201 BaTiO3 = Crystal("BaTiO3", SGLattice(99, 3.992, 4.036,
202 atoms=[e.Ba, e.Ti, e.O, e.O],
203 pos=[('1a', 1.000), ('1b', 0.5274),
204 ('1b', 0.9993), ('2c', 0.5125)]))
205 # BiFeO3 = Crystal("BiFeO3", SGLattice())
206 # BiFeO3 = Crystal(
207 # "BiFeO3",
208 # lattice.PerovskiteTypeRhombohedral(elements.Bi, elements.Fe, elements.O,
209 # 3.965, 89.3))
210 FeO = Crystal("FeO", SGLattice(225, 4.332, atoms=[e.Fe, e.O],
211 pos=['4a', '4b']))
212 CoO = Crystal("CoO", SGLattice(225, 4.214, atoms=[e.Co, e.O],
213 pos=['4a', '4b']))
214 Fe3O4 = Crystal("Fe3O4", SGLattice('227:2', 8.3958, atoms=[e.Fe, e.Fe, e.O],
215 pos=['8a', '16d', ('32e', 0.255)]))
216 Co3O4 = Crystal("Co3O4", SGLattice('227:2', 8.0821, atoms=[e.Co, e.Co, e.O],
217 pos=['8a', '16d', ('32e', 0.255)]))
218 FeRh = Crystal("FeRh", SGLattice(221, 2.993, atoms=[e.Fe, e.Rh],
219 pos=['1a', '1b']))
220 Ir20Mn80 = Crystal("Ir20Mn80", SGLattice(225, 3.780, atoms=[e.Ir, e.Mn],
221 pos=['4a', '4a'], occ=[0.2, 0.8]))
222 CoFe = Crystal("CoFe", SGLattice(221, 2.8508, atoms=[e.Co, e.Fe],
223 pos=['1a', '1b']))
224 CoGa = Crystal("CoGa", SGLattice(221, 2.883, atoms=[e.Co, e.Ga],
225 pos=['1a', '1b']))
226 LaB6 = Crystal("LaB6", SGLattice(221, 4.15692, atoms=[e.La, e.B],
227 pos=['1a', ('6f', 0.19750)]))
228 Al2O3 = Crystal("Al2O3", SGLattice('167:H', 4.7602, 12.9933,
229 atoms=[e.Al, e.O],
230 pos=[('12c', 0.35216), ('18e', 0.30624)]))
231 CuMnAs = Crystal("CuMnAs", SGLattice('129:2', 3.8200, 6.3180,
232 atoms=[e.Cu, e.Mn, e.As],
233 pos=['2b', ('2c', -0.8300),
234 ('2c', -0.2347)],
235 occ=[1, 0.86, 0.96],
236 b=[3.5531, 1.8950, 1.2633]))
237 Mn3Ge_cub = Crystal("Mn3Ge (cub)", SGLattice('221', 3.8019,
238 atoms=[e.Mn, e.Ge],
239 pos=['3c', '1a']))
240 Mn3Ge = Crystal("Mn3Ge (hex)", SGLattice('194', 5.34, 4.31,
241 atoms=[e.Mn, e.Ge],
242 pos=[('6h', 1/6.), '2d']))
243 Pt3Cr = Crystal("Pt3Cr", SGLattice('221', 3.876,
244 atoms=[e.Pt, e.Cr], pos=['3c', '1a']))
245 TiN = Crystal("TiN",
246 SGLattice(225, 4.235, atoms=[e.Ti, e.N], pos=['4a', '4b']))
247
248
249 # Alloys with special properties
250 class SiGe(CubicAlloy):
251
252 def __init__(self, x):
253 """
254 Si_{1-x} Ge_x cubic compound
255 """
256 super(SiGe, self).__init__(Si, Ge, x)
257
258 @staticmethod
259 def lattice_const_AB(latA, latB, x, **kwargs):
260 """
261 method to calculate the lattice parameter of the SiGe alloy with
262 composition Si_{1-x}Ge_x
263 """
264 return latA + (0.2 * x + 0.027 * x ** 2) * \
265 (latB - latA) / numpy.linalg.norm(latB - latA)
266
267
268 class AlGaAs(CubicAlloy):
269
270 def __init__(self, x):
271 """
272 Al_{1-x} Ga_x As cubic compound
273 """
274 super(AlGaAs, self).__init__(AlAs, GaAs, x)
+0
-903
xrayutilities/materials/spacegrouplattice.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2017-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16 """
17 module handling crystal lattice structures. A SGLattice consists of a space
18 group number and the position of atoms specified as Wyckoff positions along
19 with their parameters. Depending on the space group symmetry only certain
20 parameters of the resulting instance will be settable! A cubic lattice for
21 example allows only to set its 'a' lattice parameter but none of the other unit
22 cell shape parameters.
23 """
24 from __future__ import division
25
26 import copy
27 import numbers
28 import sys
29 from collections import OrderedDict
30 from math import cos, radians, sin, sqrt
31
32 import numpy
33
34 from .. import math, utilities
35 from ..exception import InputError
36 from . import elements
37 from .atom import Atom
38 from .wyckpos import wp
39
40 if sys.version_info < (3, 0):
41 range = xrange
42
43 # python 2to3 compatibility
44 try:
45 basestring
46 except NameError:
47 basestring = str
48
49
50 class RangeDict(dict):
51 def __getitem__(self, item):
52 if type(item) != range:
53 for key in self:
54 if item in key:
55 return self[key]
56 else:
57 return super(RangeDict, self).__getitem__(item)
58
59
60 # space group number to symmetry and number of parameters dictionary
61 sgrp_sym = RangeDict({range(1, 3): ('triclinic', 6),
62 range(3, 16): ('monoclinic', 4),
63 range(16, 75): ('orthorhombic', 3),
64 range(75, 143): ('tetragonal', 2),
65 range(143, 168): ('trigonal', 2),
66 range(168, 195): ('hexagonal', 2),
67 range(195, 231): ('cubic', 1)})
68
69 sgrp_name = {'1': 'P1', '2': 'P-1', '3': 'P2', '4': 'P21', '5': 'C2',
70 '6': 'Pm', '7': 'Pc', '8': 'Cm', '9': 'Cc', '10': 'P2/m',
71 '11': 'P21/m', '12': 'C2/m', '13': 'P2/c', '14': 'P21/c',
72 '15': 'C2/c', '16': 'P222', '17': 'P2221', '18': 'P21212',
73 '19': 'P212121', '20': 'C2221', '21': 'C222', '22': 'F222',
74 '23': 'I222', '24': 'I212121', '25': 'Pmm2', '26': 'Pmc21',
75 '27': 'Pcc2', '28': 'Pma2', '29': 'Pca21', '30': 'Pnc2',
76 '31': 'Pmn21', '32': 'Pba2', '33': 'Pna21', '34': 'Pnn2',
77 '35': 'Cmm2', '36': 'Cmc21', '37': 'Ccc2', '38': 'Amm2',
78 '39': 'Aem2', '40': 'Ama2', '41': 'Aea2', '42': 'Fmm2',
79 '43': 'Fdd2', '44': 'Imm2', '45': 'Iba2', '46': 'Ima2',
80 '47': 'Pmmm', '48': 'Pnnn', '49': 'Pccm', '50': 'Pban',
81 '51': 'Pmma', '52': 'Pnna', '53': 'Pmna', '54': 'Pcca',
82 '55': 'Pbam', '56': 'Pccn', '57': 'Pbcm', '58': 'Pnnm',
83 '59': 'Pmmn', '60': 'Pbcn', '61': 'Pbca', '62': 'Pnma',
84 '63': 'Cmcm', '64': 'Cmce', '65': 'Cmmm', '66': 'Cccm',
85 '67': 'Cmme', '68': 'Ccce', '69': 'Fmmm', '70': 'Fddd',
86 '71': 'Immm', '72': 'Ibam', '73': 'Ibca', '74': 'Imma',
87 '75': 'P4', '76': 'P41', '77': 'P42', '78': 'P43',
88 '79': 'I4', '80': 'I41', '81': 'P-4', '82': 'I-4',
89 '83': 'P4/m', '84': 'P42/m', '85': 'P4/n', '86': 'P42/n',
90 '87': 'I4/m', '88': 'I41/a', '89': 'P422', '90': 'P4212',
91 '91': 'P4122', '92': 'P41212', '93': 'P4222', '94': 'P42212',
92 '95': 'P4322', '96': 'P43212', '97': 'I422', '98': 'I4122',
93 '99': 'P4mm', '100': 'P4bm', '101': 'P42cm', '102': 'P42nm',
94 '103': 'P4cc', '104': 'P4nc', '105': 'P42mc', '106': 'P42bc',
95 '107': 'I4mm', '108': 'I4cm', '109': 'I41md', '110': 'I41cd',
96 '111': 'P-42m', '112': 'P-42c', '113': 'P-421m',
97 '114': 'P-421c', '115': 'P-4m2', '116': 'P-4c2',
98 '117': 'P-4b2', '118': 'P-4n2', '119': 'I-4m2',
99 '120': 'I-4c2', '121': 'I-42m', '122': 'I-42d',
100 '123': 'P4/mmm', '124': 'P4/mcc', '125': 'P4/nbm',
101 '126': 'P4/nnc', '127': 'P4/mbm', '128': 'P4/mnc',
102 '129': 'P4/nmm', '130': 'P4/ncc', '131': 'P42/mmc',
103 '132': 'P42/mcm', '133': 'P42/nbc', '134': 'P42/nnm',
104 '135': 'P42/mbc', '136': 'P42/mnm', '137': 'P42/nmc',
105 '138': 'P42/ncm', '139': 'I4/mmm', '140': 'I4/mcm',
106 '141': 'I41/amd', '142': 'I41/acd', '143': 'P3',
107 '144': 'P31', '145': 'P32', '146': 'R3', '147': 'P-3',
108 '148': 'R-3', '149': 'P312', '150': 'P321', '151': 'P3112',
109 '152': 'P3121', '153': 'P3212', '154': 'P3221', '155': 'R32',
110 '156': 'P3m1', '157': 'P31m', '158': 'P3c1', '159': 'P31c',
111 '160': 'R3m', '161': 'R3c', '162': 'P-31m', '163': 'P-31c',
112 '164': 'P-3m1', '165': 'P-3c1', '166': 'R-3m', '167': 'R-3c',
113 '168': 'P6', '169': 'P61', '170': 'P65', '171': 'P62',
114 '172': 'P64', '173': 'P63', '174': 'P-6', '175': 'P6/m',
115 '176': 'P63/m', '177': 'P622', '178': 'P6122',
116 '179': 'P6522', '180': 'P6222', '181': 'P6422',
117 '182': 'P6322', '183': 'P6mm', '184': 'P6cc', '185': 'P63cm',
118 '186': 'P63mc', '187': 'P-6m2', '188': 'P-6c2',
119 '189': 'P-62m', '190': 'P-62c', '191': 'P6/mmm',
120 '192': 'P6/mcc', '193': 'P63/mcm', '194': 'P63/mmc',
121 '195': 'P23', '196': 'F23', '197': 'I23', '198': 'P213',
122 '199': 'I213', '200': 'Pm-3', '201': 'Pn-3', '202': 'Fm-3',
123 '203': 'Fd-3', '204': 'Im-3', '205': 'Pa-3', '206': 'Ia-3',
124 '207': 'P432', '208': 'P4232', '209': 'F432',
125 '210': 'F4132', '211': 'I432', '212': 'P4332',
126 '213': 'P4132', '214': 'I4132', '215': 'P-43m',
127 '216': 'F-43m', '217': 'I-43m', '218': 'P-43n',
128 '219': 'F-43c', '220': 'I-43d', '221': 'Pm-3m',
129 '222': 'Pn-3n', '223': 'Pm-3n', '224': 'Pn-3m',
130 '225': 'Fm-3m', '226': 'Fm-3c', '227': 'Fd-3m',
131 '228': 'Fd-3c', '229': 'Im-3m', '230': 'Ia-3d'}
132
133 sgrp_params = {'cubic:1': (('a', ), ('a', 'a', 'a', 90, 90, 90)),
134 'cubic:2': (('a', ), ('a', 'a', 'a', 90, 90, 90)),
135 'cubic': (('a', ), ('a', 'a', 'a', 90, 90, 90)),
136 'hexagonal': (('a', 'c'), ('a', 'a', 'c', 90, 90, 120)),
137 'trigonal:R': (('a', 'alpha'), ('a', 'a', 'a', 'alpha',
138 'alpha', 'alpha')),
139 'trigonal:H': (('a', 'c'), ('a', 'a', 'c', 90, 90, 120)),
140 'trigonal': (('a', 'c'), ('a', 'a', 'c', 90, 90, 120)),
141 'tetragonal:1': (('a', 'c'), ('a', 'a', 'c', 90, 90, 90)),
142 'tetragonal:2': (('a', 'c'), ('a', 'a', 'c', 90, 90, 90)),
143 'tetragonal': (('a', 'c'), ('a', 'a', 'c', 90, 90, 90)),
144 'orthorhombic:1': (('a', 'b', 'c'),
145 ('a', 'b', 'c', 90, 90, 90)),
146 'orthorhombic:2': (('a', 'b', 'c'),
147 ('a', 'b', 'c', 90, 90, 90)),
148 'orthorhombic': (('a', 'b', 'c'),
149 ('a', 'b', 'c', 90, 90, 90)),
150 'monoclinic:b': (('a', 'b', 'c', 'beta'),
151 ('a', 'b', 'c', 90, 'beta', 90)),
152 'monoclinic:c': (('a', 'b', 'c', 'gamma'),
153 ('a', 'b', 'c', 90, 90, 'gamma')),
154 'monoclinic': (('a', 'b', 'c', 'beta'),
155 ('a', 'b', 'c', 90, 'beta', 90)),
156 'triclinic': (('a', 'b', 'c', 'alpha', 'beta', 'gamma'),
157 ('a', 'b', 'c', 'alpha', 'beta', 'gamma'))}
158
159
160 def get_possible_sgrp_suf(sgrp_nr):
161 """
162 determine possible space group suffix. Multiple suffixes might be possible
163 for one space group due to different origin choice, unique axis, or choice
164 of the unit cell shape.
165
166 Parameters
167 ----------
168 sgrp_nr : int
169 space group number
170
171 Returns
172 -------
173 str or list
174 either an empty string or a list of possible valid suffix strings
175 """
176 sgrp_suf = ''
177 if sgrp_nr in [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]:
178 sgrp_suf = [':b', ':c']
179 elif sgrp_nr in [48, 50, 59, 68, 70, 85, 86, 88, 125, 126,
180 129, 130, 133, 134, 137, 138, 141, 142,
181 201, 203, 222, 224, 227, 228]:
182 sgrp_suf = [':1', ':2']
183 elif sgrp_nr in [146, 148, 155, 160, 161, 166, 167]:
184 sgrp_suf = [':H', ':R']
185 return sgrp_suf
186
187
188 def get_default_sgrp_suf(sgrp_nr):
189 """
190 determine default space group suffix
191 """
192 possibilities = get_possible_sgrp_suf(sgrp_nr)
193 if possibilities:
194 return possibilities[0]
195 else:
196 return ''
197
198
199 class WyckoffBase(list):
200
201 """
202 The WyckoffBase class implements a container for a set of Wyckoff positions
203 that form the base of a crystal lattice. An instance of this class can be
204 treated as a simple container object.
205 """
206
207 def __init__(self, *args, **kwargs):
208 list.__init__(self, *args, **kwargs)
209
210 @staticmethod
211 def _checkatom(atom):
212 if isinstance(atom, basestring):
213 atom = getattr(elements, atom)
214 elif not isinstance(atom, Atom):
215 raise TypeError("atom must be an instance of class "
216 "xrayutilities.materials.Atom")
217 return atom
218
219 @staticmethod
220 def _checkpos(pos):
221 if isinstance(pos, basestring):
222 pos = (pos, None)
223 elif isinstance(pos, (tuple, list)):
224 if len(pos) == 1:
225 pos = (pos[0], None)
226 elif len(pos) == 2:
227 if isinstance(pos[1], numbers.Number):
228 pos = (pos[0], (pos[1], ))
229 elif pos[1] is None:
230 pos = (pos[0], None)
231 else:
232 pos = (pos[0], tuple(pos[1]))
233 else:
234 if isinstance(pos[1], numbers.Number):
235 pos = (pos[0], tuple(pos[1:]))
236 return pos
237
238 def append(self, atom, pos, occ=1.0, b=0.):
239 """
240 add new Atom to the lattice base
241
242 Parameters
243 ----------
244 atom : Atom
245 object to be added
246 pos : tuple or str
247 Wyckoff position of the atom, along with its parameters.
248 Examples: ('2i', (0.1, 0.2, 0.3)), or '1a'
249 occ : float, optional
250 occupancy (default=1.0)
251 b : float, optional
252 b-factor of the atom used as exp(-b*q**2/(4*pi)**2) to reduce the
253 intensity of this atom (only used in case of temp=0 in
254 StructureFactor and chi calculation)
255 """
256 atom = self._checkatom(atom)
257 pos = self._checkpos(pos)
258 list.append(self, (atom, pos, occ, b))
259
260 def __setitem__(self, key, data):
261 (atom, pos, occ, b) = data
262 atom = self._checkatom(atom)
263 pos = self._checkpos(pos)
264 list.__setitem__(self, key, (atom, pos, float(occ), float(b)))
265
266 def __copy__(self):
267 """
268 since we use a custom 'append' method we need to overwrite copy
269 """
270 cls = self.__class__
271 new = cls.__new__(cls)
272 for item in self:
273 new.append(*item)
274 return new
275
276 def __deepcopy__(self, memo):
277 cls = self.__class__
278 new = cls.__new__(cls)
279 memo[id(self)] = new
280 for item in self:
281 citem = copy.deepcopy(item, memo)
282 new.append(*citem)
283 return new
284
285 def __str__(self):
286 ostr = ''
287 for i, (atom, p, occ, b) in enumerate(self):
288 ostr += '%d: %s %s ' % (i, str(atom), p[0])
289 if p[1] is not None:
290 ostr += ' '.join(['%.4f' % v for v in p[1]])
291 ostr += ' occ=%5.3f b=%5.3f\n' % (occ, b)
292 return ostr
293
294 def __contains__(self, item):
295 """
296 check if this list contains already the same element, at the same
297 position, and with the same Debye Waller factor. The occupancy is not
298 checked intentionally.
299
300 Parameters
301 ----------
302 item : tuple or list
303 WyckoffBase entry to check if its present in this list
304
305 Returns
306 -------
307 bool
308 """
309 for atom, p, occ, b in self:
310 if (atom == item[0] and self.pos_eq(p, item[1]) and
311 numpy.isclose(b, item[3], atol=1e-4)):
312 return True
313 return False
314
315 @staticmethod
316 def entry_eq(e1, e2):
317 """
318 compare two entries including all its properties to be equal
319
320 Parameters
321 ----------
322 e1, e2: tuple
323 tuples with length 4 containing the entries of WyckoffBase which
324 should be compared
325 """
326 if (e1[0] == e2[0] and WyckoffBase.pos_eq(e1[1], e2[1]) and
327 numpy.all(numpy.isclose(e1[2:], e2[2:], atol=1e-4))):
328 return True
329 return False
330
331 @staticmethod
332 def pos_eq(pos1, pos2):
333 """
334 compare Wyckoff positions
335
336 Parameters
337 ----------
338 pos1, pos2: tuple
339 tuples with Wyckoff label and optional parameters
340 """
341 if pos1[0] != pos2[0]:
342 return False
343 if pos1[1] == pos2[1]:
344 return True
345 else:
346 for f1, f2 in zip(pos1[1], pos2[1]):
347 if not numpy.isclose(f1 % 1, f2 % 1, atol=1e-5):
348 return False
349 return True
350
351 def index(self, item):
352 """
353 return the index of the atom (same element, position, and Debye Waller
354 factor). The occupancy is not checked intentionally. If the item is not
355 present a ValueError is raised.
356
357 Parameters
358 ----------
359 item : tuple or list
360 WyckoffBase entry
361
362 Returns
363 -------
364 int
365 """
366 for i, (atom, p, occ, b) in enumerate(self):
367 if (atom == item[0] and self.pos_eq(p, item[1]) and
368 numpy.isclose(b, item[3], atol=1e-4)):
369 return i
370 raise ValueError("%s is not in list" % str(item))
371
372
373 class SGLattice(object):
374 """
375 lattice object created from the space group number and corresponding unit
376 cell parameters. atoms in the unit cell are specified by their Wyckoff
377 position and their free parameters.
378
379 this replaces the deprecated Lattice class
380 """
381
382 def __init__(self, sgrp, *args, **kwargs):
383 """
384 initialize class with space group number and atom list
385
386 Parameters
387 ----------
388 sgrp : int or str
389 Space group number
390 *args : float
391 space group parameters. depending on the space group number this
392 are 1 (cubic) to 6 (triclinic) parameters.
393 cubic : a (lattice parameter).
394 hexagonal : a, c.
395 trigonal : a, c.
396 tetragonal : a, c.
397 orthorhombic : a, b, c.
398 monoclinic : a, b, c, beta (in degree).
399 triclinic : a, b, c, alpha, beta, gamma (in degree).
400 atoms : list, optional
401 list of elements either as Element object or string with the
402 element name. If you specify atoms you have to also give the same
403 number of Wyckoff positions
404 pos : list, optional
405 list of the atoms Wyckoff positions along with its parameters. If a
406 position has no free parameter the parameters can be omitted.
407 Example: [('2i', (0.1, 0.2, 0.3)), '1a']
408 occ : list, optional
409 site occupation for the atoms. This is optional and defaults to 1
410 if not given.
411 b : list, optional
412 b-factor of the atom used as exp(-b*q**2/(4*pi)**2) to reduce the
413 intensity of this atom (only used in case of temp=0 in
414 StructureFactor and chi calculation)
415 """
416 valid_kwargs = {'atoms': 'list of elements',
417 'pos': 'list of Wyckoff positions',
418 'occ': 'site occupations',
419 'b': 'Debye Waller exponents'}
420 utilities.check_kwargs(kwargs, valid_kwargs, self.__class__.__name__)
421 self.space_group = str(sgrp)
422 self.space_group_nr = int(self.space_group.split(':')[0])
423 try:
424 self.space_group_suf = ':' + self.space_group.split(':')[1]
425 except IndexError:
426 self.space_group_suf = get_default_sgrp_suf(self.space_group_nr)
427
428 if self.space_group_suf != '':
429 self.space_group = str(self.space_group_nr) + self.space_group_suf
430 self.name = sgrp_name[str(self.space_group_nr)] + self.space_group_suf
431 self.crystal_system, nargs = sgrp_sym[self.space_group_nr]
432 self.crystal_system += self.space_group_suf
433 if len(args) != nargs:
434 raise ValueError('XU: number of parameters (%d) does not match the'
435 ' crystal symmetry (%s:%d)'
436 % (len(args), self.crystal_system, nargs))
437 self.free_parameters = OrderedDict()
438 for a, par in zip(args, sgrp_params[self.crystal_system][0]):
439 self.free_parameters[par] = a
440
441 self._parameters = OrderedDict()
442 for i, p in enumerate(('a', 'b', 'c', 'alpha', 'beta', 'gamma')):
443 key = sgrp_params[self.crystal_system][1][i]
444 if isinstance(key, basestring):
445 self._parameters[p] = self.free_parameters[key]
446 else:
447 self._parameters[p] = key
448
449 # set atom positions in the lattice base
450 self._wbase = WyckoffBase()
451 atoms = kwargs.get('atoms', None)
452 wps = kwargs.get('pos', None)
453 if atoms:
454 occs = kwargs.get('occ', [1.0, ] * len(atoms))
455 bs = kwargs.get('b', [0.0, ] * len(atoms))
456 for at, wpos, o, b in zip(atoms, wps, occs, bs):
457 self._wbase.append(at, wpos, o, b)
458 self.nsites = len(self._wbase)
459
460 # define lattice vectors
461 self._ai = numpy.zeros((3, 3))
462 self._bi = numpy.empty((3, 3))
463 a, b, c, alpha, beta, gamma = self._parameters.values()
464 ra = radians(alpha)
465 self._paramhelp = [cos(ra), cos(radians(beta)),
466 cos(radians(gamma)), sin(ra), 0]
467 self._setlat()
468
469 def base(self):
470 """
471 generator of atomic position within the unit cell.
472 """
473 if not self._wbase:
474 return
475 sgwp = wp[str(self.space_group)]
476 for (atom, w, occ, b) in self._wbase:
477 x, y, z = None, None, None
478 parint, poslist = sgwp[w[0]]
479 i = 0
480 if parint & 1:
481 try:
482 x = w[1][i]
483 except TypeError:
484 print('XU.materials: Wyckoff position %s of %s needs '
485 'parameters (%d) -> wrong material definition'
486 % (w[0], str(self.space_group), parint))
487 raise
488 i += 1
489 if parint & 2:
490 try:
491 y = w[1][i]
492 except TypeError:
493 print('XU.materials: Wyckoff position %s of %s needs '
494 'parameters (%d) -> wrong material definition'
495 % (w[0], str(self.space_group), parint))
496 raise
497 i += 1
498 if parint & 4:
499 try:
500 z = w[1][i]
501 except TypeError:
502 print('XU.materials: Wyckoff position %s of %s needs '
503 'parameters (%d) -> wrong material definition'
504 % (w[0], str(self.space_group), parint))
505 raise
506 i += 1
507 if w[1]:
508 if i != len(w[1]):
509 raise TypeError('XU.materials: too many parameters for '
510 'Wyckoff position')
511
512 for p in poslist:
513 pos = eval(p, {'x': x, 'y': y, 'z': z})
514 pos = numpy.asarray(pos)
515 pos -= pos // 1
516 yield atom, numpy.asarray(pos), occ, b
517
518 def _setlat(self):
519 a, b, c, alpha, beta, gamma = self._parameters.values()
520 ca, cb, cg, sa, vh = self._paramhelp
521 vh = sqrt(1 - ca**2-cb**2-cg**2 + 2*ca*cb*cg)
522 self._paramhelp[4] = vh
523 self._ai[0, 0] = a * vh / sa
524 self._ai[0, 1] = a * (cg-cb*ca) / sa
525 self._ai[0, 2] = a * cb
526 self._ai[1, 1] = b * sa
527 self._ai[1, 2] = b * ca
528 self._ai[2, 2] = c
529 self.transform = math.Transform(self._ai.T)
530 self._setb()
531
532 def _setb(self):
533 V = self.UnitCellVolume()
534 p = 2. * numpy.pi / V
535 math.VecCross(p*self._ai[1, :], self._ai[2, :], out=self._bi[0, :])
536 math.VecCross(p*self._ai[2, :], self._ai[0, :], out=self._bi[1, :])
537 math.VecCross(p*self._ai[0, :], self._ai[1, :], out=self._bi[2, :])
538 self.qtransform = math.Transform(self._bi.T)
539
540 def _set_params_from_sym(self):
541 for i, p in enumerate(('a', 'b', 'c', 'alpha', 'beta', 'gamma')):
542 key = sgrp_params[self.crystal_system][1][i]
543 if isinstance(key, basestring):
544 if p not in self.free_parameters:
545 self._parameters[p] = self.free_parameters[key]
546
547 @property
548 def a(self):
549 return self._parameters['a']
550
551 @a.setter
552 def a(self, value):
553 if 'a' not in self.free_parameters:
554 raise RuntimeError("a can not be set, its not a free parameter!")
555 self._parameters['a'] = value
556 self.free_parameters['a'] = value
557 self._set_params_from_sym()
558 self._setlat()
559
560 @property
561 def b(self):
562 return self._parameters['b']
563
564 @b.setter
565 def b(self, value):
566 if 'b' not in self.free_parameters:
567 raise RuntimeError("b can not be set, its not a free parameter!")
568 self._parameters['b'] = value
569 self.free_parameters['b'] = value
570 self._set_params_from_sym()
571 self._setlat()
572
573 @property
574 def c(self):
575 return self._parameters['c']
576
577 @c.setter
578 def c(self, value):
579 if 'c' not in self.free_parameters:
580 raise RuntimeError("c can not be set, its not a free parameter!")
581 self._parameters['c'] = value
582 self.free_parameters['c'] = value
583 self._set_params_from_sym()
584 self._setlat()
585
586 @property
587 def alpha(self):
588 return self._parameters['alpha']
589
590 @alpha.setter
591 def alpha(self, value):
592 if 'alpha' not in self.free_parameters:
593 raise RuntimeError("alpha can not be set for this space group!")
594 self._parameters['alpha'] = value
595 self.free_parameters['alpha'] = value
596 self._set_params_from_sym()
597 ra = radians(value)
598 self._paramhelp[0] = cos(ra)
599 self._paramhelp[3] = sin(ra)
600 self._setlat()
601
602 @property
603 def beta(self):
604 return self._parameters['beta']
605
606 @beta.setter
607 def beta(self, value):
608 if 'beta' not in self.free_parameters:
609 raise RuntimeError("beta can not be set for this space group!")
610 self._parameters['beta'] = value
611 self.free_parameters['beta'] = value
612 self._set_params_from_sym()
613 self._paramhelp[1] = cos(radians(value))
614 self._setlat()
615
616 @property
617 def gamma(self):
618 return self._parameters['gamma']
619
620 @gamma.setter
621 def gamma(self, value):
622 if 'gamma' not in self.free_parameters:
623 raise RuntimeError("gamma can not be set for this space group!")
624 self._parameters['gamma'] = value
625 self.free_parameters['gamma'] = value
626 self._set_params_from_sym()
627 self._paramhelp[2] = cos(radians(value))
628 self._setlat()
629
630 def __eq__(self, other):
631 """
632 compare another SGLattice instance to decide if both are equal.
633 To be equal they have to use the same space group, have equal lattice
634 paramters and contain equal atoms in their base.
635 """
636 if self.space_group != other.space_group:
637 return False
638 # compare lattice parameters
639 for prop in self.free_parameters:
640 if getattr(self, prop) != getattr(other, prop):
641 return False
642 # compare atoms in base
643 for e in self._wbase:
644 if e not in other._wbase:
645 return False
646 idx = other._wbase.index(e)
647 if not WyckoffBase.entry_eq(e, other._wbase[idx]):
648 return False
649 return True
650
651 def GetPoint(self, *args):
652 """
653 determine lattice points with indices given in the argument
654
655 Examples
656 --------
657 >>> xu.materials.Si.lattice.GetPoint(0, 0, 4)
658 array([ 0. , 0. , 21.72416])
659
660 or
661
662 >>> xu.materials.Si.lattice.GetPoint((1, 1, 1))
663 array([ 5.43104, 5.43104, 5.43104])
664 """
665 if len(args) == 1:
666 args = args[0]
667 return self.transform(args)
668
669 def GetQ(self, *args):
670 """
671 determine the reciprocal lattice points with indices given in the
672 argument
673 """
674 if len(args) == 1:
675 args = args[0]
676 return self.qtransform(args)
677
678 def GetHKL(self, *args):
679 """
680 determine the Miller indices of the given reciprocal lattice points
681 """
682 if len(args) == 1:
683 args = args[0]
684 return self.qtransform.inverse(args)
685
686 def UnitCellVolume(self):
687 """
688 function to calculate the unit cell volume of a lattice (angstrom^3)
689 """
690 a, b, c, alpha, beta, gamma = self._parameters.values()
691 return a * b * c * self._paramhelp[4]
692
693 def ApplyStrain(self, eps):
694 """
695 Applies a certain strain on a lattice. The result is a change
696 in the base vectors. The full strain matrix (3x3) needs to be given.
697
698 Note:
699 Here you specify the strain and not the stress -> NO elastic
700 response of the material will be considered!
701
702 Note:
703 Although the symmetry of the crystal can be lowered by this
704 operation the spacegroup remains unchanged! The 'free_parameters'
705 attribute is, however, updated to mimic the possible reduction of
706 the symmetry.
707
708 Parameters
709 ----------
710 eps : array-like
711 a 3x3 matrix with all strain components
712 """
713 if isinstance(eps, (list, tuple)):
714 eps = numpy.asarray(eps, dtype=numpy.double)
715 if eps.shape != (3, 3):
716 raise InputError("ApplyStrain needs a 3x3 matrix "
717 "with strain values")
718
719 ai = self._ai + numpy.dot(eps, self._ai.T).T
720 self._parameters['a'] = math.VecNorm(ai[0, :])
721 self._parameters['b'] = math.VecNorm(ai[1, :])
722 self._parameters['c'] = math.VecNorm(ai[2, :])
723 self._parameters['alpha'] = math.VecAngle(ai[1, :], ai[2, :], deg=True)
724 self._parameters['beta'] = math.VecAngle(ai[0, :], ai[2, :], deg=True)
725 self._parameters['gamma'] = math.VecAngle(ai[0, :], ai[1, :], deg=True)
726 # update helper parameters
727 ra = radians(self._parameters['alpha'])
728 self._paramhelp[0] = cos(ra)
729 self._paramhelp[1] = cos(radians(self._parameters['beta']))
730 self._paramhelp[2] = cos(radians(self._parameters['gamma']))
731 self._paramhelp[3] = sin(ra)
732 # set new transformations
733 self._setlat()
734 # update free_parameters
735 for p, v in self.free_parameters.items():
736 self.free_parameters[p] = self._parameters[p]
737 # artificially reduce symmetry if needed
738 for i, p in enumerate(('a', 'b', 'c', 'alpha', 'beta', 'gamma')):
739 key = sgrp_params[self.crystal_system][1][i]
740 if isinstance(key, basestring):
741 if self._parameters[p] != self.free_parameters[key]:
742 self.free_parameters[p] = self._parameters[p]
743 else:
744 if self._parameters[p] != key:
745 self.free_parameters[p] = self._parameters[p]
746
747 def isequivalent(self, hkl1, hkl2, equalq=False):
748 """
749 primitive way of determining if hkl1 and hkl2 are two
750 crystallographical equivalent pairs of Miller indices
751
752 Parameters
753 ----------
754 hkl1, hkl2 : list
755 Miller indices to be checked for equivalence
756 equalq : bool
757 If False the length of the two q-vactors will be compared. If True
758 it is assumed that the length of the q-vectors of hkl1 and hkl2 is
759 equal!
760
761 Returns
762 -------
763 bool
764 """
765 def leftShift(tup, n):
766 try:
767 n = n % len(tup)
768 except ZeroDivisionError:
769 return tuple()
770 return tup[n:] + tup[0:n]
771
772 def ispermutation(t1, t2):
773 """returns true if t2 is an even permutation of t1"""
774 if t1 == t2 or t1 == leftShift(t2, 1) or t1 == leftShift(t2, 2):
775 return True
776 else:
777 return False
778
779 def checkequal(hkl1, hkl2):
780 if self.crystal_system.startswith('cubic'):
781 if self.space_group_nr < 207:
782 if ispermutation(tuple(numpy.abs(hkl1)),
783 tuple(numpy.abs(hkl2))):
784 return True
785 else:
786 return False
787 else:
788 khl1 = (hkl1[1], hkl1[0], hkl1[2])
789 if (ispermutation(tuple(numpy.abs(hkl1)),
790 tuple(numpy.abs(hkl2))) or
791 ispermutation(tuple(numpy.abs(khl1)),
792 tuple(numpy.abs(hkl2)))):
793 return True
794 else:
795 return False
796 elif self.crystal_system.startswith('hexagonal'):
797 hki1 = (hkl1[0], hkl1[1], -hkl1[0] - hkl1[1])
798 hki2 = (hkl2[0], hkl2[1], -hkl2[0] - hkl2[1])
799 if (abs(hkl1[2]) == abs(hkl2[2]) and
800 sum(numpy.abs(hki1)) == sum(numpy.abs(hki2))):
801 return True
802 else:
803 return False
804 elif self.crystal_system.startswith('trigonal:R'):
805 khl1 = (hkl1[1], hkl1[0], hkl1[2])
806 if (ispermutation(hkl1, hkl2) or ispermutation(khl1, hkl2)):
807 return True
808 else:
809 return False
810 elif self.crystal_system.startswith('trigonal'):
811 hki1 = (hkl1[0], hkl1[1], -hkl1[0] - hkl1[1])
812 hki2 = (hkl2[0], hkl2[1], -hkl2[0] - hkl2[1])
813 khi2 = (hkl2[1], hkl2[0], -hkl2[0] - hkl2[1])
814 if ((hkl1[2] == hkl2[2] and ispermutation(hki1, hki2)) or
815 (hkl1[2] == -hkl2[2] and ispermutation(hki1, khi2))):
816 return True
817 else:
818 return False
819 elif self.crystal_system.startswith('tetragonal'):
820 # this neglects that in some tetragonal materials hkl = khl
821 hk1 = hkl1[:2]
822 hk2 = hkl2[:2]
823 if (abs(hkl1[2]) == abs(hkl2[2]) and
824 (hk1 == hk2 or hk1 == (-hk2[1], hk2[0]) or
825 hk1 == (-hk2[0], -hk2[1]) or
826 hk1 == (hk2[1], -hk2[0]))):
827 return True
828 else:
829 return False
830 elif self.crystal_system.startswith('orthorhombic'):
831 if numpy.all(numpy.abs(hkl1) == numpy.abs(hkl2)):
832 return True
833 else:
834 return False
835 elif self.crystal_system.startswith('monoclinic:c'):
836 hk1 = (hkl1[0], hkl1[1])
837 hk2 = (hkl2[0], hkl2[1])
838 if (abs(hkl1[2]) == abs(hkl2[2]) and
839 (hk1 == hk2 or hk1 == (-hk2[0], -hk2[1]))):
840 return True
841 else:
842 return False
843 elif self.crystal_system.startswith('monoclinic'):
844 hl1 = (hkl1[0], hkl1[2])
845 hl2 = (hkl2[0], hkl2[2])
846 if (abs(hkl1[1]) == abs(hkl2[1]) and
847 (hl1 == hl2 or hl1 == (-hl2[0], -hl2[1]))):
848 return True
849 else:
850 return False
851 elif self.crystal_system.startswith('triclinic'):
852 if (hkl1[0] == -hkl2[0] and hkl1[1] == -hkl2[1] and
853 hkl1[2] == -hkl2[2]):
854 return True
855 else:
856 return False
857
858 if not equalq:
859 if not numpy.isclose(math.VecNorm(self.GetQ(hkl1)),
860 math.VecNorm(self.GetQ(hkl2))):
861 return False
862 return checkequal(tuple(hkl1), tuple(hkl2))
863
864 def __str__(self):
865 ostr = "{sg} {cs} {n}: a = {a:.4f}, b = {b:.4f} c= {c:.4f}\n" +\
866 "alpha = {alpha:.3f}, beta = {beta:.3f}, gamma = {gamma:.3f}\n"
867 ostr = ostr.format(sg=self.space_group, cs=self.crystal_system,
868 n=self.name, **self._parameters)
869 if self._wbase:
870 ostr += "Lattice base:\n"
871 ostr += str(self._wbase)
872 return ostr
873
874 @classmethod
875 def convert_to_P1(cls, sglat):
876 """
877 create a P1 equivalent of the given SGLattice instance.
878
879 Parameters
880 ----------
881 sglat : SGLattice
882 space group lattice instance to be converted to P1.
883
884 Returns
885 -------
886 SGLattice
887 instance with the same properties as sglat, however in the P1
888 setting.
889 """
890 a, b, c, alpha, beta, gamma = (sglat.a, sglat.b, sglat.c, sglat.alpha,
891 sglat.beta, sglat.gamma)
892 atoms = []
893 pos = []
894 occ = []
895 biso = []
896 for at, p, o, bf in sglat.base():
897 atoms.append(at)
898 pos.append(('1a', p))
899 occ.append(o)
900 biso.append(bf)
901 return cls(1, a, b, c, alpha, beta, gamma, atoms=atoms, pos=pos,
902 occ=occ, b=biso)
+0
-8832
xrayutilities/materials/wyckpos.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 1 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 wp = {'1': {'1a': (7, ('(x, y, z)', ))},
18 '2': {'1a': (0, ('(0, 0, 0)', )),
19 '1b': (0, ('(0, 0, 1/2)', )),
20 '1c': (0, ('(0, 1/2, 0)', )),
21 '1d': (0, ('(1/2, 0, 0)', )),
22 '1e': (0, ('(1/2, 1/2, 0)', )),
23 '1f': (0, ('(1/2, 0, 1/2)', )),
24 '1g': (0, ('(0, 1/2, 1/2)', )),
25 '1h': (0, ('(1/2, 1/2, 1/2)', )),
26 '2i': (7, ('(x, y, z)', '(-x, -y, -z)'))},
27 '3:b': {'1a': (2, ('(0, y, 0)', )),
28 '1b': (2, ('(0, y, 1/2)', )),
29 '1c': (2, ('(1/2, y, 0)', )),
30 '1d': (2, ('(1/2, y, 1/2)', )),
31 '2e': (7, ('(x, y, z)', '(-x, y, -z)'))},
32 '3:c': {'1a': (4, ('(0, 0, z)', )),
33 '1b': (4, ('(1/2, 0, z)', )),
34 '1c': (4, ('(0, 1/2, z)', )),
35 '1d': (4, ('(1/2, 1/2, z)', )),
36 '2e': (7, ('(x, y, z)', '(-x, -y, z)'))},
37 '4:b': {'2a': (7, ('(x, y, z)', '(-x, y+1/2, -z)'))},
38 '4:c': {'2a': (7, ('(x, y, z)', '(-x, -y, z+1/2)'))},
39 '5:b': {'2a': (2, ('(0, y, 0)', '(1/2, y+1/2, 0)')),
40 '2b': (2, ('(0, y, 1/2)', '(1/2, y+1/2, 1/2)')),
41 '4c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, y, -z)',
42 '(-x+1/2, y+1/2, -z)'))},
43 '5:c': {'2a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)')),
44 '2b': (4, ('(1/2, 0, z)', '(1/2, 1/2, z+1/2)')),
45 '4c': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
46 '(-x, -y+1/2, z+1/2)'))},
47 '6:b': {'1a': (5, ('(x, 0, z)', )),
48 '1b': (5, ('(x, 1/2, z)', )),
49 '2c': (7, ('(x, y, z)', '(x, -y, z)'))},
50 '6:c': {'1a': (3, ('(x, y, 0)', )),
51 '1b': (3, ('(x, y, 1/2)', )),
52 '2c': (7, ('(x, y, z)', '(x, y, -z)'))},
53 '7:b': {'2a': (7, ('(x, y, z)', '(x, -y, z+1/2)'))},
54 '7:c': {'2a': (7, ('(x, y, z)', '(x+1/2, y, -z)'))},
55 '8:b': {'2a': (5, ('(x, 0, z)', '(x+1/2, 1/2, z)')),
56 '4b': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(x, -y, z)',
57 '(x+1/2, -y+1/2, z)'))},
58 '8:c': {'2a': (3, ('(x, y, 0)', '(x, y+1/2, 1/2)')),
59 '4b': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(x, y, -z)',
60 '(x, y+1/2, -z+1/2)'))},
61 '9:b': {'4a': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(x, -y, z+1/2)',
62 '(x+1/2, -y+1/2, z+1/2)'))},
63 '9:c': {'4a': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(x+1/2, y, -z)',
64 '(x+1/2, y+1/2, -z+1/2)'))},
65 '10:b': {'1a': (0, ('(0, 0, 0)', )),
66 '1b': (0, ('(0, 1/2, 0)', )),
67 '1c': (0, ('(0, 0, 1/2)', )),
68 '1d': (0, ('(1/2, 0, 0)', )),
69 '1e': (0, ('(1/2, 1/2, 0)', )),
70 '1f': (0, ('(0, 1/2, 1/2)', )),
71 '1g': (0, ('(1/2, 0, 1/2)', )),
72 '1h': (0, ('(1/2, 1/2, 1/2)', )),
73 '2i': (2, ('(0, y, 0)', '(0, -y, 0)')),
74 '2j': (2, ('(1/2, y, 0)', '(1/2, -y, 0)')),
75 '2k': (2, ('(0, y, 1/2)', '(0, -y, 1/2)')),
76 '2l': (2, ('(1/2, y, 1/2)', '(1/2, -y, 1/2)')),
77 '2m': (5, ('(x, 0, z)', '(-x, 0, -z)')),
78 '2n': (5, ('(x, 1/2, z)', '(-x, 1/2, -z)')),
79 '4o': (7, ('(x, y, z)', '(-x, y, -z)', '(-x, -y, -z)',
80 '(x, -y, z)'))},
81 '10:c': {'1a': (0, ('(0, 0, 0)', )),
82 '1b': (0, ('(0, 0, 1/2)', )),
83 '1c': (0, ('(1/2, 0, 0)', )),
84 '1d': (0, ('(0, 1/2, 0)', )),
85 '1e': (0, ('(0, 1/2, 1/2)', )),
86 '1f': (0, ('(1/2, 0, 1/2)', )),
87 '1g': (0, ('(1/2, 1/2, 0)', )),
88 '1h': (0, ('(1/2, 1/2, 1/2)', )),
89 '2i': (4, ('(0, 0, z)', '(0, 0, -z)')),
90 '2j': (4, ('(0, 1/2, z)', '(0, 1/2, -z)')),
91 '2k': (4, ('(1/2, 0, z)', '(1/2, 0, -z)')),
92 '2l': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
93 '2m': (3, ('(x, y, 0)', '(-x, -y, 0)')),
94 '2n': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)')),
95 '4o': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, -y, -z)',
96 '(x, y, -z)'))},
97 '11:b': {'2a': (0, ('(0, 0, 0)', '(0, 1/2, 0)')),
98 '2b': (0, ('(1/2, 0, 0)', '(1/2, 1/2, 0)')),
99 '2c': (0, ('(0, 0, 1/2)', '(0, 1/2, 1/2)')),
100 '2d': (0, ('(1/2, 0, 1/2)', '(1/2, 1/2, 1/2)')),
101 '2e': (5, ('(x, 1/4, z)', '(-x, 3/4, -z)')),
102 '4f': (7, ('(x, y, z)', '(-x, y+1/2, -z)', '(-x, -y, -z)',
103 '(x, -y+1/2, z)'))},
104 '11:c': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
105 '2b': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)')),
106 '2c': (0, ('(1/2, 0, 0)', '(1/2, 0, 1/2)')),
107 '2d': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
108 '2e': (3, ('(x, y, 1/4)', '(-x, -y, 3/4)')),
109 '4f': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-x, -y, -z)',
110 '(x, y, -z+1/2)'))},
111 '12:b': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
112 '2b': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
113 '2c': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
114 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
115 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 0)',
116 '(1/4, 3/4, 0)')),
117 '4f': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
118 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
119 '4g': (2, ('(0, y, 0)', '(1/2, y+1/2, 0)', '(0, -y, 0)',
120 '(1/2, -y+1/2, 0)')),
121 '4h': (2, ('(0, y, 1/2)', '(1/2, y+1/2, 1/2)', '(0, -y, 1/2)',
122 '(1/2, -y+1/2, 1/2)')),
123 '4i': (5, ('(x, 0, z)', '(x+1/2, 1/2, z)', '(-x, 0, -z)',
124 '(-x+1/2, 1/2, -z)')),
125 '8j': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, y, -z)',
126 '(-x+1/2, y+1/2, -z)', '(-x, -y, -z)',
127 '(-x+1/2, -y+1/2, -z)', '(x, -y, z)',
128 '(x+1/2, -y+1/2, z)'))},
129 '12:c': {'2a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)')),
130 '2b': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)')),
131 '2c': (0, ('(1/2, 0, 0)', '(1/2, 1/2, 1/2)')),
132 '2d': (0, ('(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
133 '4e': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(0, 3/4, 1/4)',
134 '(0, 1/4, 3/4)')),
135 '4f': (0, ('(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)',
136 '(1/2, 3/4, 1/4)', '(1/2, 1/4, 3/4)')),
137 '4g': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(0, 0, -z)',
138 '(0, 1/2, -z+1/2)')),
139 '4h': (4, ('(1/2, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 0, -z)',
140 '(1/2, 1/2, -z+1/2)')),
141 '4i': (3, ('(x, y, 0)', '(x, y+1/2, 1/2)', '(-x, -y, 0)',
142 '(-x, -y+1/2, 1/2)')),
143 '8j': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
144 '(-x, -y+1/2, z+1/2)', '(-x, -y, -z)',
145 '(-x, -y+1/2, -z+1/2)', '(x, y, -z)',
146 '(x, y+1/2, -z+1/2)'))},
147 '13:b': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
148 '2b': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
149 '2c': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)')),
150 '2d': (0, ('(1/2, 0, 0)', '(1/2, 0, 1/2)')),
151 '2e': (2, ('(0, y, 1/4)', '(0, -y, 3/4)')),
152 '2f': (2, ('(1/2, y, 1/4)', '(1/2, -y, 3/4)')),
153 '4g': (7, ('(x, y, z)', '(-x, y, -z+1/2)', '(-x, -y, -z)',
154 '(x, -y, z+1/2)'))},
155 '13:c': {'2a': (0, ('(0, 0, 0)', '(1/2, 0, 0)')),
156 '2b': (0, ('(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)')),
157 '2c': (0, ('(0, 0, 1/2)', '(1/2, 0, 1/2)')),
158 '2d': (0, ('(0, 1/2, 0)', '(1/2, 1/2, 0)')),
159 '2e': (4, ('(1/4, 0, z)', '(3/4, 0, -z)')),
160 '2f': (4, ('(1/4, 1/2, z)', '(3/4, 1/2, -z)')),
161 '4g': (7, ('(x, y, z)', '(-x+1/2, -y, z)', '(-x, -y, -z)',
162 '(x+1/2, y, -z)'))},
163 '14:b': {'2a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)')),
164 '2b': (0, ('(1/2, 0, 0)', '(1/2, 1/2, 1/2)')),
165 '2c': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)')),
166 '2d': (0, ('(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
167 '4e': (7, ('(x, y, z)', '(-x, y+1/2, -z+1/2)', '(-x, -y, -z)',
168 '(x, -y+1/2, z+1/2)'))},
169 '14:c': {'2a': (0, ('(0, 0, 0)', '(1/2, 0, 1/2)')),
170 '2b': (0, ('(0, 1/2, 0)', '(1/2, 1/2, 1/2)')),
171 '2c': (0, ('(1/2, 0, 0)', '(0, 0, 1/2)')),
172 '2d': (0, ('(1/2, 1/2, 0)', '(0, 1/2, 1/2)')),
173 '4e': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)', '(-x, -y, -z)',
174 '(x+1/2, y, -z+1/2)'))},
175 '15:b': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)',
176 '(1/2, 1/2, 1/2)')),
177 '4b': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
178 '(1/2, 0, 1/2)')),
179 '4c': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 1/2)',
180 '(1/4, 3/4, 1/2)')),
181 '4d': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
182 '(3/4, 1/4, 0)', '(1/4, 3/4, 0)')),
183 '4e': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 1/4)', '(0, -y, 3/4)',
184 '(1/2, -y+1/2, 3/4)')),
185 '8f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, y, -z+1/2)',
186 '(-x+1/2, y+1/2, -z+1/2)', '(-x, -y, -z)',
187 '(-x+1/2, -y+1/2, -z)', '(x, -y, z+1/2)',
188 '(x+1/2, -y+1/2, z+1/2)'))},
189 '15:c': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 0)',
190 '(1/2, 1/2, 1/2)')),
191 '4b': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 1/2)',
192 '(1/2, 1/2, 0)')),
193 '4c': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 3/4, 1/4)',
194 '(1/2, 1/4, 3/4)')),
195 '4d': (0, ('(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)',
196 '(0, 3/4, 1/4)', '(0, 1/4, 3/4)')),
197 '4e': (4, ('(1/4, 0, z)', '(1/4, 1/2, z+1/2)', '(3/4, 0, -z)',
198 '(3/4, 1/2, -z+1/2)')),
199 '8f': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x+1/2, -y, z)',
200 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, -z)',
201 '(-x, -y+1/2, -z+1/2)', '(x+1/2, y, -z)',
202 '(x+1/2, y+1/2, -z+1/2)'))},
203 '16': {'1a': (0, ('(0, 0, 0)', )),
204 '1b': (0, ('(1/2, 0, 0)', )),
205 '1c': (0, ('(0, 1/2, 0)', )),
206 '1d': (0, ('(0, 0, 1/2)', )),
207 '1e': (0, ('(1/2, 1/2, 0)', )),
208 '1f': (0, ('(1/2, 0, 1/2)', )),
209 '1g': (0, ('(0, 1/2, 1/2)', )),
210 '1h': (0, ('(1/2, 1/2, 1/2)', )),
211 '2i': (1, ('(x, 0, 0)', '(-x, 0, 0)')),
212 '2j': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)')),
213 '2k': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)')),
214 '2l': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)')),
215 '2m': (2, ('(0, y, 0)', '(0, -y, 0)')),
216 '2n': (2, ('(0, y, 1/2)', '(0, -y, 1/2)')),
217 '2o': (2, ('(1/2, y, 0)', '(1/2, -y, 0)')),
218 '2p': (2, ('(1/2, y, 1/2)', '(1/2, -y, 1/2)')),
219 '2q': (4, ('(0, 0, z)', '(0, 0, -z)')),
220 '2r': (4, ('(1/2, 0, z)', '(1/2, 0, -z)')),
221 '2s': (4, ('(0, 1/2, z)', '(0, 1/2, -z)')),
222 '2t': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
223 '4u': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
224 '(x, -y, -z)'))},
225 '17': {'2a': (1, ('(x, 0, 0)', '(-x, 0, 1/2)')),
226 '2b': (1, ('(x, 1/2, 0)', '(-x, 1/2, 1/2)')),
227 '2c': (2, ('(0, y, 1/4)', '(0, -y, 3/4)')),
228 '2d': (2, ('(1/2, y, 1/4)', '(1/2, -y, 3/4)')),
229 '4e': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-x, y, -z+1/2)',
230 '(x, -y, -z)'))},
231 '18': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, -z)')),
232 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
233 '4c': (7, ('(x, y, z)', '(-x, -y, z)', '(-x+1/2, y+1/2, -z)',
234 '(x+1/2, -y+1/2, -z)'))},
235 '19': {'4a': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
236 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)'))},
237 '20': {'4a': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
238 '(-x+1/2, 1/2, 1/2)')),
239 '4b': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 1/4)', '(0, -y, 3/4)',
240 '(1/2, -y+1/2, 3/4)')),
241 '8c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z+1/2)',
242 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z+1/2)',
243 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
244 '(x+1/2, -y+1/2, -z)'))},
245 '21': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
246 '2b': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
247 '2c': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
248 '2d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
249 '4e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 0, 0)',
250 '(-x+1/2, 1/2, 0)')),
251 '4f': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 1/2)',
252 '(-x+1/2, 1/2, 1/2)')),
253 '4g': (2, ('(0, y, 0)', '(1/2, y+1/2, 0)', '(0, -y, 0)',
254 '(1/2, -y+1/2, 0)')),
255 '4h': (2, ('(0, y, 1/2)', '(1/2, y+1/2, 1/2)', '(0, -y, 1/2)',
256 '(1/2, -y+1/2, 1/2)')),
257 '4i': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(0, 0, -z)',
258 '(1/2, 1/2, -z)')),
259 '4j': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
260 '(1/2, 0, -z)')),
261 '4k': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)', '(3/4, 1/4, -z)',
262 '(1/4, 3/4, -z)')),
263 '8l': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z)',
264 '(-x+1/2, -y+1/2, z)', '(-x, y, -z)',
265 '(-x+1/2, y+1/2, -z)', '(x, -y, -z)',
266 '(x+1/2, -y+1/2, -z)'))},
267 '22': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
268 '(1/2, 1/2, 0)')),
269 '4b': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 0)',
270 '(1/2, 1/2, 1/2)')),
271 '4c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
272 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
273 '4d': (0, ('(1/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
274 '(3/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
275 '8e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
276 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
277 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)')),
278 '8f': (2, ('(0, y, 0)', '(0, y+1/2, 1/2)', '(1/2, y, 1/2)',
279 '(1/2, y+1/2, 0)', '(0, -y, 0)', '(0, -y+1/2, 1/2)',
280 '(1/2, -y, 1/2)', '(1/2, -y+1/2, 0)')),
281 '8g': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
282 '(1/2, 1/2, z)', '(0, 0, -z)', '(0, 1/2, -z+1/2)',
283 '(1/2, 0, -z+1/2)', '(1/2, 1/2, -z)')),
284 '8h': (4, ('(1/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
285 '(3/4, 1/4, z+1/2)', '(3/4, 3/4, z)',
286 '(3/4, 1/4, -z)', '(3/4, 3/4, -z+1/2)',
287 '(1/4, 1/4, -z+1/2)', '(1/4, 3/4, -z)')),
288 '8i': (2, ('(1/4, y, 1/4)', '(1/4, y+1/2, 3/4)', '(3/4, y, 3/4)',
289 '(3/4, y+1/2, 1/4)', '(3/4, -y, 1/4)',
290 '(3/4, -y+1/2, 3/4)', '(1/4, -y, 3/4)',
291 '(1/4, -y+1/2, 1/4)')),
292 '8j': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)', '(x+1/2, 1/4, 3/4)',
293 '(x+1/2, 3/4, 1/4)', '(-x, 3/4, 1/4)',
294 '(-x, 1/4, 3/4)', '(-x+1/2, 3/4, 3/4)',
295 '(-x+1/2, 1/4, 1/4)')),
296 '16k': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
297 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
298 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
299 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
300 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
301 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
302 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
303 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)'))},
304 '23': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
305 '2b': (0, ('(1/2, 0, 0)', '(0, 1/2, 1/2)')),
306 '2c': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
307 '2d': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
308 '4e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
309 '(-x+1/2, 1/2, 1/2)')),
310 '4f': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
311 '(-x+1/2, 1/2, 0)')),
312 '4g': (2, ('(0, y, 0)', '(1/2, y+1/2, 1/2)', '(0, -y, 0)',
313 '(1/2, -y+1/2, 1/2)')),
314 '4h': (2, ('(1/2, y, 0)', '(0, y+1/2, 1/2)', '(1/2, -y, 0)',
315 '(0, -y+1/2, 1/2)')),
316 '4i': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
317 '(1/2, 1/2, -z+1/2)')),
318 '4j': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, -z)',
319 '(1/2, 0, -z+1/2)')),
320 '8k': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
321 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
322 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
323 '(x+1/2, -y+1/2, -z+1/2)'))},
324 '24': {'4a': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
325 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)')),
326 '4b': (2, ('(1/4, y, 0)', '(3/4, y+1/2, 1/2)', '(1/4, -y, 1/2)',
327 '(3/4, -y+1/2, 0)')),
328 '4c': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)',
329 '(0, 3/4, -z+1/2)', '(1/2, 1/4, -z)')),
330 '8d': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
331 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
332 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
333 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)'))},
334 '25': {'1a': (4, ('(0, 0, z)', )),
335 '1b': (4, ('(0, 1/2, z)', )),
336 '1c': (4, ('(1/2, 0, z)', )),
337 '1d': (4, ('(1/2, 1/2, z)', )),
338 '2e': (5, ('(x, 0, z)', '(-x, 0, z)')),
339 '2f': (5, ('(x, 1/2, z)', '(-x, 1/2, z)')),
340 '2g': (6, ('(0, y, z)', '(0, -y, z)')),
341 '2h': (6, ('(1/2, y, z)', '(1/2, -y, z)')),
342 '4i': (7, ('(x, y, z)', '(-x, -y, z)', '(x, -y, z)',
343 '(-x, y, z)'))},
344 '26': {'2a': (6, ('(0, y, z)', '(0, -y, z+1/2)')),
345 '2b': (6, ('(1/2, y, z)', '(1/2, -y, z+1/2)')),
346 '4c': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(x, -y, z+1/2)',
347 '(-x, y, z)'))},
348 '27': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
349 '2b': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)')),
350 '2c': (4, ('(1/2, 0, z)', '(1/2, 0, z+1/2)')),
351 '2d': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
352 '4e': (7, ('(x, y, z)', '(-x, -y, z)', '(x, -y, z+1/2)',
353 '(-x, y, z+1/2)'))},
354 '28': {'2a': (4, ('(0, 0, z)', '(1/2, 0, z)')),
355 '2b': (4, ('(0, 1/2, z)', '(1/2, 1/2, z)')),
356 '2c': (6, ('(1/4, y, z)', '(3/4, -y, z)')),
357 '4d': (7, ('(x, y, z)', '(-x, -y, z)', '(x+1/2, -y, z)',
358 '(-x+1/2, y, z)'))},
359 '29': {'4a': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(x+1/2, -y, z)',
360 '(-x+1/2, y, z+1/2)'))},
361 '30': {'2a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)')),
362 '2b': (4, ('(1/2, 0, z)', '(1/2, 1/2, z+1/2)')),
363 '4c': (7, ('(x, y, z)', '(-x, -y, z)', '(x, -y+1/2, z+1/2)',
364 '(-x, y+1/2, z+1/2)'))},
365 '31': {'2a': (6, ('(0, y, z)', '(1/2, -y, z+1/2)')),
366 '4b': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
367 '(x+1/2, -y, z+1/2)', '(-x, y, z)'))},
368 '32': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z)')),
369 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, z)')),
370 '4c': (7, ('(x, y, z)', '(-x, -y, z)', '(x+1/2, -y+1/2, z)',
371 '(-x+1/2, y+1/2, z)'))},
372 '33': {'4a': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
373 '(-x+1/2, y+1/2, z+1/2)'))},
374 '34': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
375 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)')),
376 '4c': (7, ('(x, y, z)', '(-x, -y, z)', '(x+1/2, -y+1/2, z+1/2)',
377 '(-x+1/2, y+1/2, z+1/2)'))},
378 '35': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z)')),
379 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, z)')),
380 '4c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)', '(1/4, 3/4, z)',
381 '(3/4, 1/4, z)')),
382 '4d': (5, ('(x, 0, z)', '(x+1/2, 1/2, z)', '(-x, 0, z)',
383 '(-x+1/2, 1/2, z)')),
384 '4e': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y, z)',
385 '(1/2, -y+1/2, z)')),
386 '8f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z)',
387 '(-x+1/2, -y+1/2, z)', '(x, -y, z)',
388 '(x+1/2, -y+1/2, z)', '(-x, y, z)',
389 '(-x+1/2, y+1/2, z)'))},
390 '36': {'4a': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y, z+1/2)',
391 '(1/2, -y+1/2, z+1/2)')),
392 '8b': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z+1/2)',
393 '(-x+1/2, -y+1/2, z+1/2)', '(x, -y, z+1/2)',
394 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
395 '(-x+1/2, y+1/2, z)'))},
396 '37': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(0, 0, z+1/2)',
397 '(1/2, 1/2, z+1/2)')),
398 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, z+1/2)',
399 '(1/2, 0, z+1/2)')),
400 '4c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)', '(1/4, 3/4, z+1/2)',
401 '(3/4, 1/4, z+1/2)')),
402 '8d': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z)',
403 '(-x+1/2, -y+1/2, z)', '(x, -y, z+1/2)',
404 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z+1/2)',
405 '(-x+1/2, y+1/2, z+1/2)'))},
406 '38': {'2a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)')),
407 '2b': (4, ('(1/2, 0, z)', '(1/2, 1/2, z+1/2)')),
408 '4c': (5, ('(x, 0, z)', '(x, 1/2, z+1/2)', '(-x, 0, z)',
409 '(-x, 1/2, z+1/2)')),
410 '4d': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(0, -y, z)',
411 '(0, -y+1/2, z+1/2)')),
412 '4e': (6, ('(1/2, y, z)', '(1/2, y+1/2, z+1/2)', '(1/2, -y, z)',
413 '(1/2, -y+1/2, z+1/2)')),
414 '8f': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
415 '(-x, -y+1/2, z+1/2)', '(x, -y, z)',
416 '(x, -y+1/2, z+1/2)', '(-x, y, z)',
417 '(-x, y+1/2, z+1/2)'))},
418 '39': {'4a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(0, 1/2, z)',
419 '(0, 0, z+1/2)')),
420 '4b': (4, ('(1/2, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 1/2, z)',
421 '(1/2, 0, z+1/2)')),
422 '4c': (5, ('(x, 1/4, z)', '(x, 3/4, z+1/2)', '(-x, 3/4, z)',
423 '(-x, 1/4, z+1/2)')),
424 '8d': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
425 '(-x, -y+1/2, z+1/2)', '(x, -y+1/2, z)',
426 '(x, -y, z+1/2)', '(-x, y+1/2, z)', '(-x, y, z+1/2)'
427 ))},
428 '40': {'4a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z)',
429 '(1/2, 1/2, z+1/2)')),
430 '4b': (6, ('(1/4, y, z)', '(1/4, y+1/2, z+1/2)', '(3/4, -y, z)',
431 '(3/4, -y+1/2, z+1/2)')),
432 '8c': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
433 '(-x, -y+1/2, z+1/2)', '(x+1/2, -y, z)',
434 '(x+1/2, -y+1/2, z+1/2)', '(-x+1/2, y, z)',
435 '(-x+1/2, y+1/2, z+1/2)'))},
436 '41': {'4a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 1/2, z)',
437 '(1/2, 0, z+1/2)')),
438 '8b': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)', '(-x, -y, z)',
439 '(-x, -y+1/2, z+1/2)', '(x+1/2, -y+1/2, z)',
440 '(x+1/2, -y, z+1/2)', '(-x+1/2, y+1/2, z)',
441 '(-x+1/2, y, z+1/2)'))},
442 '42': {'4a': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
443 '(1/2, 1/2, z)')),
444 '8b': (4, ('(1/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
445 '(3/4, 1/4, z+1/2)', '(3/4, 3/4, z)', '(1/4, 3/4, z)',
446 '(1/4, 1/4, z+1/2)', '(3/4, 3/4, z+1/2)',
447 '(3/4, 1/4, z)')),
448 '8c': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(1/2, y, z+1/2)',
449 '(1/2, y+1/2, z)', '(0, -y, z)', '(0, -y+1/2, z+1/2)',
450 '(1/2, -y, z+1/2)', '(1/2, -y+1/2, z)')),
451 '8d': (5, ('(x, 0, z)', '(x, 1/2, z+1/2)', '(x+1/2, 0, z+1/2)',
452 '(x+1/2, 1/2, z)', '(-x, 0, z)', '(-x, 1/2, z+1/2)',
453 '(-x+1/2, 0, z+1/2)', '(-x+1/2, 1/2, z)')),
454 '16e': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
455 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
456 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
457 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
458 '(x, -y, z)', '(x, -y+1/2, z+1/2)',
459 '(x+1/2, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
460 '(-x, y, z)', '(-x, y+1/2, z+1/2)',
461 '(-x+1/2, y, z+1/2)', '(-x+1/2, y+1/2, z)'))},
462 '43': {'8a': (4, ('(0, 0, z)', '(1/2, 0, z+1/2)', '(0, 1/2, z+1/2)',
463 '(1/2, 1/2, z)', '(1/4, 1/4, z+1/4)',
464 '(3/4, 1/4, z+3/4)', '(1/4, 3/4, z+3/4)',
465 '(3/4, 3/4, z+1/4)')),
466 '16b': (7, ('(x, y, z)', '(x+1/2, y, z+1/2)',
467 '(x, y+1/2, z+1/2)', '(x+1/2, y+1/2, z)',
468 '(-x, -y, z)', '(-x+1/2, -y, z+1/2)',
469 '(-x, -y+1/2, z+1/2)', '(-x+1/2, -y+1/2, z)',
470 '(x+1/4, -y+1/4, z+1/4)', '(x+3/4, -y+1/4, z+3/4)',
471 '(x+1/4, -y+3/4, z+3/4)', '(x+3/4, -y+3/4, z+1/4)',
472 '(-x+1/4, y+1/4, z+1/4)', '(-x+3/4, y+1/4, z+3/4)',
473 '(-x+1/4, y+3/4, z+3/4)', '(-x+3/4, y+3/4, z+1/4)'))},
474 '44': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
475 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)')),
476 '4c': (5, ('(x, 0, z)', '(x+1/2, 1/2, z+1/2)', '(-x, 0, z)',
477 '(-x+1/2, 1/2, z+1/2)')),
478 '4d': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y, z)',
479 '(1/2, -y+1/2, z+1/2)')),
480 '8e': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
481 '(-x+1/2, -y+1/2, z+1/2)', '(x, -y, z)',
482 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
483 '(-x+1/2, y+1/2, z+1/2)'))},
484 '45': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 1/2, z)',
485 '(0, 0, z+1/2)')),
486 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
487 '(0, 1/2, z+1/2)')),
488 '8c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
489 '(-x+1/2, -y+1/2, z+1/2)', '(x+1/2, -y+1/2, z)',
490 '(x, -y, z+1/2)', '(-x+1/2, y+1/2, z)',
491 '(-x, y, z+1/2)'))},
492 '46': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 0, z)',
493 '(0, 1/2, z+1/2)')),
494 '4b': (6, ('(1/4, y, z)', '(3/4, y+1/2, z+1/2)', '(3/4, -y, z)',
495 '(1/4, -y+1/2, z+1/2)')),
496 '8c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
497 '(-x+1/2, -y+1/2, z+1/2)', '(x+1/2, -y, z)',
498 '(x, -y+1/2, z+1/2)', '(-x+1/2, y, z)',
499 '(-x, y+1/2, z+1/2)'))},
500 '47': {'8A': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
501 '(x, -y, -z)', '(-x, -y, -z)', '(x, y, -z)',
502 '(x, -y, z)', '(-x, y, z)')),
503 '1a': (0, ('(0, 0, 0)', )),
504 '1b': (0, ('(1/2, 0, 0)', )),
505 '1c': (0, ('(0, 0, 1/2)', )),
506 '1d': (0, ('(1/2, 0, 1/2)', )),
507 '1e': (0, ('(0, 1/2, 0)', )),
508 '1f': (0, ('(1/2, 1/2, 0)', )),
509 '1g': (0, ('(0, 1/2, 1/2)', )),
510 '1h': (0, ('(1/2, 1/2, 1/2)', )),
511 '2i': (1, ('(x, 0, 0)', '(-x, 0, 0)')),
512 '2j': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)')),
513 '2k': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)')),
514 '2l': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)')),
515 '2m': (2, ('(0, y, 0)', '(0, -y, 0)')),
516 '2n': (2, ('(0, y, 1/2)', '(0, -y, 1/2)')),
517 '2o': (2, ('(1/2, y, 0)', '(1/2, -y, 0)')),
518 '2p': (2, ('(1/2, y, 1/2)', '(1/2, -y, 1/2)')),
519 '2q': (4, ('(0, 0, z)', '(0, 0, -z)')),
520 '2r': (4, ('(0, 1/2, z)', '(0, 1/2, -z)')),
521 '2s': (4, ('(1/2, 0, z)', '(1/2, 0, -z)')),
522 '2t': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
523 '4u': (6, ('(0, y, z)', '(0, -y, z)', '(0, y, -z)',
524 '(0, -y, -z)')),
525 '4v': (6, ('(1/2, y, z)', '(1/2, -y, z)', '(1/2, y, -z)',
526 '(1/2, -y, -z)')),
527 '4w': (5, ('(x, 0, z)', '(-x, 0, z)', '(-x, 0, -z)',
528 '(x, 0, -z)')),
529 '4x': (5, ('(x, 1/2, z)', '(-x, 1/2, z)', '(-x, 1/2, -z)',
530 '(x, 1/2, -z)')),
531 '4y': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-x, y, 0)',
532 '(x, -y, 0)')),
533 '4z': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)', '(-x, y, 1/2)',
534 '(x, -y, 1/2)'))},
535 '48:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
536 '2b': (0, ('(1/2, 0, 0)', '(0, 1/2, 1/2)')),
537 '2c': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
538 '2d': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
539 '4e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
540 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)')),
541 '4f': (0, ('(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
542 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
543 '4g': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(-x+1/2, 1/2, 1/2)',
544 '(x+1/2, 1/2, 1/2)')),
545 '4h': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(-x+1/2, 1/2, 0)',
546 '(x+1/2, 1/2, 0)')),
547 '4i': (2, ('(0, y, 0)', '(0, -y, 0)', '(1/2, -y+1/2, 1/2)',
548 '(1/2, y+1/2, 1/2)')),
549 '4j': (2, ('(1/2, y, 0)', '(1/2, -y, 0)', '(0, -y+1/2, 1/2)',
550 '(0, y+1/2, 1/2)')),
551 '4k': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
552 '(1/2, 1/2, z+1/2)')),
553 '4l': (4, ('(0, 1/2, z)', '(0, 1/2, -z)', '(1/2, 0, -z+1/2)',
554 '(1/2, 0, z+1/2)')),
555 '8m': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
556 '(x, -y, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
557 '(x+1/2, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, z+1/2)',
558 '(-x+1/2, y+1/2, z+1/2)'))},
559 '48:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
560 '2b': (0, ('(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)')),
561 '2c': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
562 '2d': (0, ('(1/4, 3/4, 1/4)', '(3/4, 1/4, 3/4)')),
563 '4e': (0, ('(1/2, 1/2, 1/2)', '(0, 0, 1/2)', '(0, 1/2, 0)',
564 '(1/2, 0, 0)')),
565 '4f': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
566 '(0, 1/2, 1/2)')),
567 '4g': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
568 '(-x, 3/4, 3/4)', '(x+1/2, 3/4, 3/4)')),
569 '4h': (1, ('(x, 1/4, 3/4)', '(-x+1/2, 1/4, 3/4)',
570 '(-x, 3/4, 1/4)', '(x+1/2, 3/4, 1/4)')),
571 '4i': (2, ('(1/4, y, 1/4)', '(1/4, -y+1/2, 1/4)',
572 '(3/4, -y, 3/4)', '(3/4, y+1/2, 3/4)')),
573 '4j': (2, ('(3/4, y, 1/4)', '(3/4, -y+1/2, 1/4)',
574 '(1/4, -y, 3/4)', '(1/4, y+1/2, 3/4)')),
575 '4k': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, -z+1/2)',
576 '(3/4, 3/4, -z)', '(3/4, 3/4, z+1/2)')),
577 '4l': (4, ('(1/4, 3/4, z)', '(1/4, 3/4, -z+1/2)',
578 '(3/4, 1/4, -z)', '(3/4, 1/4, z+1/2)')),
579 '8m': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
580 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
581 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
582 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)'))},
583 '49': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
584 '2b': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
585 '2c': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)')),
586 '2d': (0, ('(1/2, 0, 0)', '(1/2, 0, 1/2)')),
587 '2e': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
588 '2f': (0, ('(1/2, 0, 1/4)', '(1/2, 0, 3/4)')),
589 '2g': (0, ('(0, 1/2, 1/4)', '(0, 1/2, 3/4)')),
590 '2h': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
591 '4i': (1, ('(x, 0, 1/4)', '(-x, 0, 1/4)', '(-x, 0, 3/4)',
592 '(x, 0, 3/4)')),
593 '4j': (1, ('(x, 1/2, 1/4)', '(-x, 1/2, 1/4)', '(-x, 1/2, 3/4)',
594 '(x, 1/2, 3/4)')),
595 '4k': (2, ('(0, y, 1/4)', '(0, -y, 1/4)', '(0, -y, 3/4)',
596 '(0, y, 3/4)')),
597 '4l': (2, ('(1/2, y, 1/4)', '(1/2, -y, 1/4)', '(1/2, -y, 3/4)',
598 '(1/2, y, 3/4)')),
599 '4m': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
600 '(0, 0, z+1/2)')),
601 '4n': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z+1/2)',
602 '(1/2, 1/2, -z)', '(1/2, 1/2, z+1/2)')),
603 '4o': (4, ('(0, 1/2, z)', '(0, 1/2, -z+1/2)', '(0, 1/2, -z)',
604 '(0, 1/2, z+1/2)')),
605 '4p': (4, ('(1/2, 0, z)', '(1/2, 0, -z+1/2)', '(1/2, 0, -z)',
606 '(1/2, 0, z+1/2)')),
607 '4q': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-x, y, 1/2)',
608 '(x, -y, 1/2)')),
609 '8r': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z+1/2)',
610 '(x, -y, -z+1/2)', '(-x, -y, -z)', '(x, y, -z)',
611 '(x, -y, z+1/2)', '(-x, y, z+1/2)'))},
612 '50:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
613 '2b': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)')),
614 '2c': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
615 '2d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
616 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 0)',
617 '(1/4, 3/4, 0)')),
618 '4f': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
619 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
620 '4g': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(-x+1/2, 1/2, 0)',
621 '(x+1/2, 1/2, 0)')),
622 '4h': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(-x+1/2, 1/2, 1/2)',
623 '(x+1/2, 1/2, 1/2)')),
624 '4i': (2, ('(0, y, 0)', '(0, -y, 0)', '(1/2, -y+1/2, 0)',
625 '(1/2, y+1/2, 0)')),
626 '4j': (2, ('(0, y, 1/2)', '(0, -y, 1/2)', '(1/2, -y+1/2, 1/2)',
627 '(1/2, y+1/2, 1/2)')),
628 '4k': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z)',
629 '(1/2, 1/2, z)')),
630 '4l': (4, ('(0, 1/2, z)', '(0, 1/2, -z)', '(1/2, 0, -z)',
631 '(1/2, 0, z)')),
632 '8m': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
633 '(x, -y, -z)', '(-x+1/2, -y+1/2, -z)',
634 '(x+1/2, y+1/2, -z)', '(x+1/2, -y+1/2, z)',
635 '(-x+1/2, y+1/2, z)'))},
636 '50:2': {'2a': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)')),
637 '2b': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 0)')),
638 '2c': (0, ('(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
639 '2d': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)')),
640 '4e': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
641 '(0, 1/2, 0)')),
642 '4f': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
643 '(0, 1/2, 1/2)')),
644 '4g': (1, ('(x, 1/4, 0)', '(-x+1/2, 1/4, 0)', '(-x, 3/4, 0)',
645 '(x+1/2, 3/4, 0)')),
646 '4h': (1, ('(x, 1/4, 1/2)', '(-x+1/2, 1/4, 1/2)',
647 '(-x, 3/4, 1/2)', '(x+1/2, 3/4, 1/2)')),
648 '4i': (2, ('(1/4, y, 0)', '(1/4, -y+1/2, 0)', '(3/4, -y, 0)',
649 '(3/4, y+1/2, 0)')),
650 '4j': (2, ('(1/4, y, 1/2)', '(1/4, -y+1/2, 1/2)',
651 '(3/4, -y, 1/2)', '(3/4, y+1/2, 1/2)')),
652 '4k': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, -z)', '(3/4, 3/4, -z)',
653 '(3/4, 3/4, z)')),
654 '4l': (4, ('(1/4, 3/4, z)', '(1/4, 3/4, -z)', '(3/4, 1/4, -z)',
655 '(3/4, 1/4, z)')),
656 '8m': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
657 '(-x+1/2, y, -z)', '(x, -y+1/2, -z)',
658 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
659 '(x+1/2, -y, z)', '(-x, y+1/2, z)'))},
660 '51': {'2a': (0, ('(0, 0, 0)', '(1/2, 0, 0)')),
661 '2b': (0, ('(0, 1/2, 0)', '(1/2, 1/2, 0)')),
662 '2c': (0, ('(0, 0, 1/2)', '(1/2, 0, 1/2)')),
663 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)')),
664 '2e': (4, ('(1/4, 0, z)', '(3/4, 0, -z)')),
665 '2f': (4, ('(1/4, 1/2, z)', '(3/4, 1/2, -z)')),
666 '4g': (2, ('(0, y, 0)', '(1/2, -y, 0)', '(0, -y, 0)',
667 '(1/2, y, 0)')),
668 '4h': (2, ('(0, y, 1/2)', '(1/2, -y, 1/2)', '(0, -y, 1/2)',
669 '(1/2, y, 1/2)')),
670 '4i': (5, ('(x, 0, z)', '(-x+1/2, 0, z)', '(-x, 0, -z)',
671 '(x+1/2, 0, -z)')),
672 '4j': (5, ('(x, 1/2, z)', '(-x+1/2, 1/2, z)', '(-x, 1/2, -z)',
673 '(x+1/2, 1/2, -z)')),
674 '4k': (6, ('(1/4, y, z)', '(1/4, -y, z)', '(3/4, y, -z)',
675 '(3/4, -y, -z)')),
676 '8l': (7, ('(x, y, z)', '(-x+1/2, -y, z)', '(-x, y, -z)',
677 '(x+1/2, -y, -z)', '(-x, -y, -z)', '(x+1/2, y, -z)',
678 '(x, -y, z)', '(-x+1/2, y, z)'))},
679 '52': {'4a': (0, ('(0, 0, 0)', '(1/2, 0, 0)', '(1/2, 1/2, 1/2)',
680 '(0, 1/2, 1/2)')),
681 '4b': (0, ('(0, 0, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
682 '(0, 1/2, 0)')),
683 '4c': (4, ('(1/4, 0, z)', '(1/4, 1/2, -z+1/2)', '(3/4, 0, -z)',
684 '(3/4, 1/2, z+1/2)')),
685 '4d': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 3/4, 1/4)',
686 '(-x, 3/4, 3/4)', '(x+1/2, 1/4, 3/4)')),
687 '8e': (7, ('(x, y, z)', '(-x+1/2, -y, z)',
688 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y+1/2, -z+1/2)',
689 '(-x, -y, -z)', '(x+1/2, y, -z)',
690 '(x+1/2, -y+1/2, z+1/2)', '(-x, y+1/2, z+1/2)'))},
691 '53': {'2a': (0, ('(0, 0, 0)', '(1/2, 0, 1/2)')),
692 '2b': (0, ('(1/2, 0, 0)', '(0, 0, 1/2)')),
693 '2c': (0, ('(1/2, 1/2, 0)', '(0, 1/2, 1/2)')),
694 '2d': (0, ('(0, 1/2, 0)', '(1/2, 1/2, 1/2)')),
695 '4e': (1, ('(x, 0, 0)', '(-x+1/2, 0, 1/2)', '(-x, 0, 0)',
696 '(x+1/2, 0, 1/2)')),
697 '4f': (1, ('(x, 1/2, 0)', '(-x+1/2, 1/2, 1/2)', '(-x, 1/2, 0)',
698 '(x+1/2, 1/2, 1/2)')),
699 '4g': (2, ('(1/4, y, 1/4)', '(1/4, -y, 3/4)', '(3/4, -y, 3/4)',
700 '(3/4, y, 1/4)')),
701 '4h': (6, ('(0, y, z)', '(1/2, -y, z+1/2)', '(1/2, y, -z+1/2)',
702 '(0, -y, -z)')),
703 '8i': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
704 '(-x+1/2, y, -z+1/2)', '(x, -y, -z)', '(-x, -y, -z)',
705 '(x+1/2, y, -z+1/2)', '(x+1/2, -y, z+1/2)',
706 '(-x, y, z)'))},
707 '54': {'4a': (0, ('(0, 0, 0)', '(1/2, 0, 0)', '(0, 0, 1/2)',
708 '(1/2, 0, 1/2)')),
709 '4b': (0, ('(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
710 '(1/2, 1/2, 1/2)')),
711 '4c': (2, ('(0, y, 1/4)', '(1/2, -y, 1/4)', '(0, -y, 3/4)',
712 '(1/2, y, 3/4)')),
713 '4d': (4, ('(1/4, 0, z)', '(3/4, 0, -z+1/2)', '(3/4, 0, -z)',
714 '(1/4, 0, z+1/2)')),
715 '4e': (4, ('(1/4, 1/2, z)', '(3/4, 1/2, -z+1/2)',
716 '(3/4, 1/2, -z)', '(1/4, 1/2, z+1/2)')),
717 '8f': (7, ('(x, y, z)', '(-x+1/2, -y, z)', '(-x, y, -z+1/2)',
718 '(x+1/2, -y, -z+1/2)', '(-x, -y, -z)',
719 '(x+1/2, y, -z)', '(x, -y, z+1/2)',
720 '(-x+1/2, y, z+1/2)'))},
721 '55': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
722 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
723 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
724 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
725 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, -z)', '(0, 0, -z)',
726 '(1/2, 1/2, z)')),
727 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(0, 1/2, -z)',
728 '(1/2, 0, z)')),
729 '4g': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-x+1/2, y+1/2, 0)',
730 '(x+1/2, -y+1/2, 0)')),
731 '4h': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)',
732 '(-x+1/2, y+1/2, 1/2)', '(x+1/2, -y+1/2, 1/2)')),
733 '8i': (7, ('(x, y, z)', '(-x, -y, z)', '(-x+1/2, y+1/2, -z)',
734 '(x+1/2, -y+1/2, -z)', '(-x, -y, -z)', '(x, y, -z)',
735 '(x+1/2, -y+1/2, z)', '(-x+1/2, y+1/2, z)'))},
736 '56': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
737 '(1/2, 0, 1/2)')),
738 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(0, 1/2, 0)',
739 '(1/2, 0, 0)')),
740 '4c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, -z+1/2)',
741 '(3/4, 3/4, -z)', '(1/4, 1/4, z+1/2)')),
742 '4d': (4, ('(1/4, 3/4, z)', '(3/4, 1/4, -z+1/2)',
743 '(3/4, 1/4, -z)', '(1/4, 3/4, z+1/2)')),
744 '8e': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
745 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y, -z+1/2)',
746 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
747 '(x, -y+1/2, z+1/2)', '(-x+1/2, y, z+1/2)'))},
748 '57': {'4a': (0, ('(0, 0, 0)', '(0, 0, 1/2)', '(0, 1/2, 1/2)',
749 '(0, 1/2, 0)')),
750 '4b': (0, ('(1/2, 0, 0)', '(1/2, 0, 1/2)', '(1/2, 1/2, 1/2)',
751 '(1/2, 1/2, 0)')),
752 '4c': (1, ('(x, 1/4, 0)', '(-x, 3/4, 1/2)', '(-x, 3/4, 0)',
753 '(x, 1/4, 1/2)')),
754 '4d': (3, ('(x, y, 1/4)', '(-x, -y, 3/4)', '(-x, y+1/2, 1/4)',
755 '(x, -y+1/2, 3/4)')),
756 '8e': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-x, y+1/2, -z+1/2)',
757 '(x, -y+1/2, -z)', '(-x, -y, -z)', '(x, y, -z+1/2)',
758 '(x, -y+1/2, z+1/2)', '(-x, y+1/2, z)'))},
759 '58': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
760 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
761 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
762 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)')),
763 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, -z+1/2)', '(0, 0, -z)',
764 '(1/2, 1/2, z+1/2)')),
765 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, -z+1/2)', '(0, 1/2, -z)',
766 '(1/2, 0, z+1/2)')),
767 '4g': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-x+1/2, y+1/2, 1/2)',
768 '(x+1/2, -y+1/2, 1/2)')),
769 '8h': (7, ('(x, y, z)', '(-x, -y, z)', '(-x+1/2, y+1/2, -z+1/2)',
770 '(x+1/2, -y+1/2, -z+1/2)', '(-x, -y, -z)',
771 '(x, y, -z)', '(x+1/2, -y+1/2, z+1/2)',
772 '(-x+1/2, y+1/2, z+1/2)'))},
773 '59:1': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, -z)')),
774 '2b': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
775 '4c': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(1/4, 3/4, 0)',
776 '(3/4, 1/4, 0)')),
777 '4d': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
778 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
779 '4e': (6, ('(0, y, z)', '(0, -y, z)', '(1/2, y+1/2, -z)',
780 '(1/2, -y+1/2, -z)')),
781 '4f': (5, ('(x, 0, z)', '(-x, 0, z)', '(-x+1/2, 1/2, -z)',
782 '(x+1/2, 1/2, -z)')),
783 '8g': (7, ('(x, y, z)', '(-x, -y, z)', '(-x+1/2, y+1/2, -z)',
784 '(x+1/2, -y+1/2, -z)', '(-x+1/2, -y+1/2, -z)',
785 '(x+1/2, y+1/2, -z)', '(x, -y, z)', '(-x, y, z)'))},
786 '59:2': {'2a': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, -z)')),
787 '2b': (4, ('(1/4, 3/4, z)', '(3/4, 1/4, -z)')),
788 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 0)',
789 '(1/2, 0, 0)')),
790 '4d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/2)',
791 '(1/2, 0, 1/2)')),
792 '4e': (6, ('(1/4, y, z)', '(1/4, -y+1/2, z)',
793 '(3/4, y+1/2, -z)', '(3/4, -y, -z)')),
794 '4f': (5, ('(x, 1/4, z)', '(-x+1/2, 1/4, z)', '(-x, 3/4, -z)',
795 '(x+1/2, 3/4, -z)')),
796 '8g': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
797 '(-x, y+1/2, -z)', '(x+1/2, -y, -z)',
798 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
799 '(x, -y+1/2, z)', '(-x+1/2, y, z)'))},
800 '60': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 0, 1/2)',
801 '(1/2, 1/2, 0)')),
802 '4b': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)',
803 '(1/2, 0, 0)')),
804 '4c': (2, ('(0, y, 1/4)', '(1/2, -y+1/2, 3/4)', '(0, -y, 3/4)',
805 '(1/2, y+1/2, 1/4)')),
806 '8d': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z+1/2)',
807 '(-x, y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
808 '(-x, -y, -z)', '(x+1/2, y+1/2, -z+1/2)',
809 '(x, -y, z+1/2)', '(-x+1/2, y+1/2, z)'))},
810 '61': {'4a': (0, ('(0, 0, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)',
811 '(1/2, 1/2, 0)')),
812 '4b': (0, ('(0, 0, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
813 '(1/2, 1/2, 1/2)')),
814 '8c': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
815 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)',
816 '(-x, -y, -z)', '(x+1/2, y, -z+1/2)',
817 '(x, -y+1/2, z+1/2)', '(-x+1/2, y+1/2, z)'))},
818 '62': {'4a': (0, ('(0, 0, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 0)',
819 '(1/2, 1/2, 1/2)')),
820 '4b': (0, ('(0, 0, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
821 '(1/2, 1/2, 0)')),
822 '4c': (5, ('(x, 1/4, z)', '(-x+1/2, 3/4, z+1/2)',
823 '(-x, 3/4, -z)', '(x+1/2, 1/4, -z+1/2)')),
824 '8d': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)', '(-x, y+1/2, -z)',
825 '(x+1/2, -y+1/2, -z+1/2)', '(-x, -y, -z)',
826 '(x+1/2, y, -z+1/2)', '(x, -y+1/2, z)',
827 '(-x+1/2, y+1/2, z+1/2)'))},
828 '63': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)',
829 '(1/2, 1/2, 1/2)')),
830 '4b': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
831 '(1/2, 0, 1/2)')),
832 '4c': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 1/4)', '(0, -y, 3/4)',
833 '(1/2, -y+1/2, 3/4)')),
834 '8d': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 3/4, 1/2)',
835 '(1/4, 1/4, 1/2)', '(3/4, 1/4, 1/2)',
836 '(1/4, 3/4, 1/2)', '(1/4, 3/4, 0)', '(3/4, 1/4, 0)')),
837 '8e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
838 '(-x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
839 '(-x+1/2, 1/2, 0)', '(x, 0, 1/2)',
840 '(x+1/2, 1/2, 1/2)')),
841 '8f': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y, z+1/2)',
842 '(1/2, -y+1/2, z+1/2)', '(0, y, -z+1/2)',
843 '(1/2, y+1/2, -z+1/2)', '(0, -y, -z)',
844 '(1/2, -y+1/2, -z)')),
845 '8g': (3, ('(x, y, 1/4)', '(x+1/2, y+1/2, 1/4)', '(-x, -y, 3/4)',
846 '(-x+1/2, -y+1/2, 3/4)', '(-x, y, 1/4)',
847 '(-x+1/2, y+1/2, 1/4)', '(x, -y, 3/4)',
848 '(x+1/2, -y+1/2, 3/4)')),
849 '16h': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z+1/2)',
850 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z+1/2)',
851 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
852 '(x+1/2, -y+1/2, -z)', '(-x, -y, -z)',
853 '(-x+1/2, -y+1/2, -z)', '(x, y, -z+1/2)',
854 '(x+1/2, y+1/2, -z+1/2)', '(x, -y, z+1/2)',
855 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
856 '(-x+1/2, y+1/2, z)'))},
857 '64': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
858 '(1/2, 0, 1/2)')),
859 '4b': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 1/2)',
860 '(0, 0, 1/2)')),
861 '8c': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 1/2)',
862 '(1/4, 3/4, 1/2)', '(3/4, 3/4, 1/2)',
863 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)', '(3/4, 1/4, 0)')),
864 '8d': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 1/2, 1/2)',
865 '(-x+1/2, 0, 1/2)', '(-x, 0, 0)', '(-x+1/2, 1/2, 0)',
866 '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)')),
867 '8e': (2, ('(1/4, y, 1/4)', '(3/4, y+1/2, 1/4)',
868 '(3/4, -y+1/2, 3/4)', '(1/4, -y, 3/4)',
869 '(3/4, -y, 3/4)', '(1/4, -y+1/2, 3/4)',
870 '(1/4, y+1/2, 1/4)', '(3/4, y, 1/4)')),
871 '8f': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y+1/2, z+1/2)',
872 '(1/2, -y, z+1/2)', '(0, y+1/2, -z+1/2)',
873 '(1/2, y, -z+1/2)', '(0, -y, -z)',
874 '(1/2, -y+1/2, -z)')),
875 '16g': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)',
876 '(-x, -y+1/2, z+1/2)', '(-x+1/2, -y, z+1/2)',
877 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z+1/2)',
878 '(x, -y, -z)', '(x+1/2, -y+1/2, -z)', '(-x, -y, -z)',
879 '(-x+1/2, -y+1/2, -z)', '(x, y+1/2, -z+1/2)',
880 '(x+1/2, y, -z+1/2)', '(x, -y+1/2, z+1/2)',
881 '(x+1/2, -y, z+1/2)', '(-x, y, z)',
882 '(-x+1/2, y+1/2, z)'))},
883 '65': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
884 '2b': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)')),
885 '2c': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
886 '2d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
887 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 0)',
888 '(1/4, 3/4, 0)')),
889 '4f': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
890 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
891 '4g': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 0, 0)',
892 '(-x+1/2, 1/2, 0)')),
893 '4h': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 1/2)',
894 '(-x+1/2, 1/2, 1/2)')),
895 '4i': (2, ('(0, y, 0)', '(1/2, y+1/2, 0)', '(0, -y, 0)',
896 '(1/2, -y+1/2, 0)')),
897 '4j': (2, ('(0, y, 1/2)', '(1/2, y+1/2, 1/2)', '(0, -y, 1/2)',
898 '(1/2, -y+1/2, 1/2)')),
899 '4k': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(0, 0, -z)',
900 '(1/2, 1/2, -z)')),
901 '4l': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
902 '(1/2, 0, -z)')),
903 '8m': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)', '(3/4, 1/4, -z)',
904 '(1/4, 3/4, -z)', '(3/4, 3/4, -z)', '(1/4, 1/4, -z)',
905 '(1/4, 3/4, z)', '(3/4, 1/4, z)')),
906 '8n': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y, z)',
907 '(1/2, -y+1/2, z)', '(0, y, -z)', '(1/2, y+1/2, -z)',
908 '(0, -y, -z)', '(1/2, -y+1/2, -z)')),
909 '8o': (5, ('(x, 0, z)', '(x+1/2, 1/2, z)', '(-x, 0, z)',
910 '(-x+1/2, 1/2, z)', '(-x, 0, -z)',
911 '(-x+1/2, 1/2, -z)', '(x, 0, -z)', '(x+1/2, 1/2, -z)'
912 )),
913 '8p': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 0)', '(-x, -y, 0)',
914 '(-x+1/2, -y+1/2, 0)', '(-x, y, 0)',
915 '(-x+1/2, y+1/2, 0)', '(x, -y, 0)',
916 '(x+1/2, -y+1/2, 0)')),
917 '8q': (3, ('(x, y, 1/2)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 1/2)',
918 '(-x+1/2, -y+1/2, 1/2)', '(-x, y, 1/2)',
919 '(-x+1/2, y+1/2, 1/2)', '(x, -y, 1/2)',
920 '(x+1/2, -y+1/2, 1/2)')),
921 '16r': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z)',
922 '(-x+1/2, -y+1/2, z)', '(-x, y, -z)',
923 '(-x+1/2, y+1/2, -z)', '(x, -y, -z)',
924 '(x+1/2, -y+1/2, -z)', '(-x, -y, -z)',
925 '(-x+1/2, -y+1/2, -z)', '(x, y, -z)',
926 '(x+1/2, y+1/2, -z)', '(x, -y, z)',
927 '(x+1/2, -y+1/2, z)', '(-x, y, z)',
928 '(-x+1/2, y+1/2, z)'))},
929 '66': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 1/4)', '(0, 0, 3/4)',
930 '(1/2, 1/2, 3/4)')),
931 '4b': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 1/4)', '(0, 1/2, 3/4)',
932 '(1/2, 0, 3/4)')),
933 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)',
934 '(1/2, 1/2, 1/2)')),
935 '4d': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
936 '(1/2, 0, 1/2)')),
937 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 1/2)',
938 '(1/4, 3/4, 1/2)')),
939 '4f': (0, ('(1/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
940 '(1/4, 1/4, 1/2)')),
941 '8g': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 1/4)', '(-x, 0, 1/4)',
942 '(-x+1/2, 1/2, 1/4)', '(-x, 0, 3/4)',
943 '(-x+1/2, 1/2, 3/4)', '(x, 0, 3/4)',
944 '(x+1/2, 1/2, 3/4)')),
945 '8h': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 1/4)', '(0, -y, 1/4)',
946 '(1/2, -y+1/2, 1/4)', '(0, -y, 3/4)',
947 '(1/2, -y+1/2, 3/4)', '(0, y, 3/4)',
948 '(1/2, y+1/2, 3/4)')),
949 '8i': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(0, 0, -z+1/2)',
950 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)', '(1/2, 1/2, -z)',
951 '(0, 0, z+1/2)', '(1/2, 1/2, z+1/2)')),
952 '8j': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z+1/2)',
953 '(1/2, 0, -z+1/2)', '(0, 1/2, -z)', '(1/2, 0, -z)',
954 '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)')),
955 '8k': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)',
956 '(3/4, 1/4, -z+1/2)', '(1/4, 3/4, -z+1/2)',
957 '(3/4, 3/4, -z)', '(1/4, 1/4, -z)',
958 '(1/4, 3/4, z+1/2)', '(3/4, 1/4, z+1/2)')),
959 '8l': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 0)', '(-x, -y, 0)',
960 '(-x+1/2, -y+1/2, 0)', '(-x, y, 1/2)',
961 '(-x+1/2, y+1/2, 1/2)', '(x, -y, 1/2)',
962 '(x+1/2, -y+1/2, 1/2)')),
963 '16m': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y, z)',
964 '(-x+1/2, -y+1/2, z)', '(-x, y, -z+1/2)',
965 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z+1/2)',
966 '(x+1/2, -y+1/2, -z+1/2)', '(-x, -y, -z)',
967 '(-x+1/2, -y+1/2, -z)', '(x, y, -z)',
968 '(x+1/2, y+1/2, -z)', '(x, -y, z+1/2)',
969 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z+1/2)',
970 '(-x+1/2, y+1/2, z+1/2)'))},
971 '67': {'4a': (0, ('(1/4, 0, 0)', '(3/4, 1/2, 0)', '(3/4, 0, 0)',
972 '(1/4, 1/2, 0)')),
973 '4b': (0, ('(1/4, 0, 1/2)', '(3/4, 1/2, 1/2)', '(3/4, 0, 1/2)',
974 '(1/4, 1/2, 1/2)')),
975 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 0)',
976 '(1/2, 0, 0)')),
977 '4d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/2)',
978 '(1/2, 0, 1/2)')),
979 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 0)',
980 '(1/4, 3/4, 0)')),
981 '4f': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
982 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
983 '4g': (4, ('(0, 1/4, z)', '(1/2, 3/4, z)', '(0, 3/4, -z)',
984 '(1/2, 1/4, -z)')),
985 '8h': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x, 1/2, 0)',
986 '(-x+1/2, 0, 0)', '(-x, 0, 0)', '(-x+1/2, 1/2, 0)',
987 '(x, 1/2, 0)', '(x+1/2, 0, 0)')),
988 '8i': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 1/2)', '(-x, 1/2, 1/2)',
989 '(-x+1/2, 0, 1/2)', '(-x, 0, 1/2)',
990 '(-x+1/2, 1/2, 1/2)', '(x, 1/2, 1/2)',
991 '(x+1/2, 0, 1/2)')),
992 '8j': (2, ('(1/4, y, 0)', '(3/4, y+1/2, 0)', '(3/4, -y+1/2, 0)',
993 '(1/4, -y, 0)', '(3/4, -y, 0)', '(1/4, -y+1/2, 0)',
994 '(1/4, y+1/2, 0)', '(3/4, y, 0)')),
995 '8k': (2, ('(1/4, y, 1/2)', '(3/4, y+1/2, 1/2)',
996 '(3/4, -y+1/2, 1/2)', '(1/4, -y, 1/2)',
997 '(3/4, -y, 1/2)', '(1/4, -y+1/2, 1/2)',
998 '(1/4, y+1/2, 1/2)', '(3/4, y, 1/2)')),
999 '8l': (4, ('(1/4, 0, z)', '(3/4, 1/2, z)', '(3/4, 1/2, -z)',
1000 '(1/4, 0, -z)', '(3/4, 0, -z)', '(1/4, 1/2, -z)',
1001 '(1/4, 1/2, z)', '(3/4, 0, z)')),
1002 '8m': (6, ('(0, y, z)', '(1/2, y+1/2, z)', '(0, -y+1/2, z)',
1003 '(1/2, -y, z)', '(0, y+1/2, -z)', '(1/2, y, -z)',
1004 '(0, -y, -z)', '(1/2, -y+1/2, -z)')),
1005 '8n': (5, ('(x, 1/4, z)', '(x+1/2, 3/4, z)', '(-x, 1/4, z)',
1006 '(-x+1/2, 3/4, z)', '(-x, 3/4, -z)',
1007 '(-x+1/2, 1/4, -z)', '(x, 3/4, -z)',
1008 '(x+1/2, 1/4, -z)')),
1009 '16o': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)', '(-x, -y+1/2, z)',
1010 '(-x+1/2, -y, z)', '(-x, y+1/2, -z)',
1011 '(-x+1/2, y, -z)', '(x, -y, -z)',
1012 '(x+1/2, -y+1/2, -z)', '(-x, -y, -z)',
1013 '(-x+1/2, -y+1/2, -z)', '(x, y+1/2, -z)',
1014 '(x+1/2, y, -z)', '(x, -y+1/2, z)', '(x+1/2, -y, z)',
1015 '(-x, y, z)', '(-x+1/2, y+1/2, z)'))},
1016 '68:1': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
1017 '(1/2, 0, 1/2)')),
1018 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(0, 1/2, 0)',
1019 '(1/2, 0, 0)')),
1020 '8c': (0, ('(1/4, 0, 1/4)', '(3/4, 1/2, 1/4)',
1021 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)', '(3/4, 0, 3/4)',
1022 '(1/4, 1/2, 3/4)', '(3/4, 1/2, 3/4)',
1023 '(1/4, 0, 3/4)')),
1024 '8d': (0, ('(0, 1/4, 1/4)', '(1/2, 3/4, 1/4)',
1025 '(1/2, 1/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
1026 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
1027 '(0, 3/4, 3/4)')),
1028 '8e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 0)', '(-x+1/2, 1/2, 0)',
1029 '(-x, 0, 0)', '(-x, 1/2, 1/2)', '(-x+1/2, 0, 1/2)',
1030 '(x+1/2, 0, 1/2)', '(x, 1/2, 1/2)')),
1031 '8f': (2, ('(0, y, 0)', '(1/2, y+1/2, 0)', '(1/2, -y+1/2, 0)',
1032 '(0, -y, 0)', '(0, -y+1/2, 1/2)', '(1/2, -y, 1/2)',
1033 '(1/2, y, 1/2)', '(0, y+1/2, 1/2)')),
1034 '8g': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(0, 0, -z)',
1035 '(1/2, 1/2, -z)', '(0, 1/2, -z+1/2)',
1036 '(1/2, 0, -z+1/2)', '(0, 1/2, z+1/2)',
1037 '(1/2, 0, z+1/2)')),
1038 '8h': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z)', '(3/4, 1/4, -z)',
1039 '(1/4, 3/4, -z)', '(3/4, 1/4, -z+1/2)',
1040 '(1/4, 3/4, -z+1/2)', '(1/4, 1/4, z+1/2)',
1041 '(3/4, 3/4, z+1/2)')),
1042 '16i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)',
1043 '(-x+1/2, -y+1/2, z)', '(-x, -y, z)',
1044 '(-x, y, -z)', '(-x+1/2, y+1/2, -z)',
1045 '(x+1/2, -y+1/2, -z)', '(x, -y, -z)',
1046 '(-x, -y+1/2, -z+1/2)', '(-x+1/2, -y, -z+1/2)',
1047 '(x+1/2, y, -z+1/2)', '(x, y+1/2, -z+1/2)',
1048 '(x, -y+1/2, z+1/2)', '(x+1/2, -y, z+1/2)',
1049 '(-x+1/2, y, z+1/2)', '(-x, y+1/2, z+1/2)'))},
1050 '68:2': {'4a': (0, ('(0, 1/4, 1/4)', '(1/2, 3/4, 1/4)', '(0, 3/4, 3/4)',
1051 '(1/2, 1/4, 3/4)')),
1052 '4b': (0, ('(0, 1/4, 3/4)', '(1/2, 3/4, 3/4)', '(0, 3/4, 1/4)',
1053 '(1/2, 1/4, 1/4)')),
1054 '8c': (0, ('(1/4, 3/4, 0)', '(3/4, 1/4, 0)', '(1/4, 1/4, 0)',
1055 '(3/4, 3/4, 0)', '(3/4, 3/4, 1/2)',
1056 '(1/4, 1/4, 1/2)', '(3/4, 1/4, 1/2)',
1057 '(1/4, 3/4, 1/2)')),
1058 '8d': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
1059 '(0, 1/2, 0)', '(0, 0, 1/2)', '(1/2, 1/2, 1/2)',
1060 '(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
1061 '8e': (1, ('(x, 1/4, 1/4)', '(x+1/2, 3/4, 1/4)',
1062 '(-x+1/2, 3/4, 1/4)', '(-x, 1/4, 1/4)',
1063 '(-x, 3/4, 3/4)', '(-x+1/2, 1/4, 3/4)',
1064 '(x+1/2, 1/4, 3/4)', '(x, 3/4, 3/4)')),
1065 '8f': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 1/4)',
1066 '(1/2, -y, 1/4)', '(0, -y+1/2, 1/4)',
1067 '(0, -y, 3/4)', '(1/2, -y+1/2, 3/4)',
1068 '(1/2, y, 3/4)', '(0, y+1/2, 3/4)')),
1069 '8g': (4, ('(0, 1/4, z)', '(1/2, 3/4, z)', '(0, 1/4, -z+1/2)',
1070 '(1/2, 3/4, -z+1/2)', '(0, 3/4, -z)',
1071 '(1/2, 1/4, -z)', '(0, 3/4, z+1/2)',
1072 '(1/2, 1/4, z+1/2)')),
1073 '8h': (4, ('(1/4, 0, z)', '(3/4, 1/2, z)', '(3/4, 0, -z+1/2)',
1074 '(1/4, 1/2, -z+1/2)', '(3/4, 0, -z)',
1075 '(1/4, 1/2, -z)', '(1/4, 0, z+1/2)',
1076 '(3/4, 1/2, z+1/2)')),
1077 '16i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z)',
1078 '(-x+1/2, -y, z)', '(-x, -y+1/2, z)',
1079 '(-x, y, -z+1/2)', '(-x+1/2, y+1/2, -z+1/2)',
1080 '(x+1/2, -y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
1081 '(-x, -y, -z)', '(-x+1/2, -y+1/2, -z)',
1082 '(x+1/2, y, -z)', '(x, y+1/2, -z)',
1083 '(x, -y, z+1/2)', '(x+1/2, -y+1/2, z+1/2)',
1084 '(-x+1/2, y, z+1/2)', '(-x, y+1/2, z+1/2)'))},
1085 '69': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
1086 '(1/2, 1/2, 0)')),
1087 '4b': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 0)',
1088 '(1/2, 1/2, 1/2)')),
1089 '8c': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
1090 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
1091 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)')),
1092 '8d': (0, ('(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
1093 '(3/4, 1/2, 1/4)', '(3/4, 0, 1/4)', '(3/4, 1/2, 3/4)',
1094 '(1/4, 0, 3/4)', '(1/4, 1/2, 1/4)')),
1095 '8e': (0, ('(1/4, 1/4, 0)', '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
1096 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
1097 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
1098 '8f': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
1099 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
1100 '(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
1101 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
1102 '8g': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
1103 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
1104 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)')),
1105 '8h': (2, ('(0, y, 0)', '(0, y+1/2, 1/2)', '(1/2, y, 1/2)',
1106 '(1/2, y+1/2, 0)', '(0, -y, 0)', '(0, -y+1/2, 1/2)',
1107 '(1/2, -y, 1/2)', '(1/2, -y+1/2, 0)')),
1108 '8i': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
1109 '(1/2, 1/2, z)', '(0, 0, -z)', '(0, 1/2, -z+1/2)',
1110 '(1/2, 0, -z+1/2)', '(1/2, 1/2, -z)')),
1111 '16j': (4, ('(1/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
1112 '(3/4, 1/4, z+1/2)', '(3/4, 3/4, z)',
1113 '(3/4, 1/4, -z)', '(3/4, 3/4, -z+1/2)',
1114 '(1/4, 1/4, -z+1/2)', '(1/4, 3/4, -z)',
1115 '(3/4, 3/4, -z)', '(3/4, 1/4, -z+1/2)',
1116 '(1/4, 3/4, -z+1/2)', '(1/4, 1/4, -z)',
1117 '(1/4, 3/4, z)', '(1/4, 1/4, z+1/2)',
1118 '(3/4, 3/4, z+1/2)', '(3/4, 1/4, z)')),
1119 '16k': (2, ('(1/4, y, 1/4)', '(1/4, y+1/2, 3/4)',
1120 '(3/4, y, 3/4)', '(3/4, y+1/2, 1/4)',
1121 '(3/4, -y, 1/4)', '(3/4, -y+1/2, 3/4)',
1122 '(1/4, -y, 3/4)', '(1/4, -y+1/2, 1/4)',
1123 '(3/4, -y, 3/4)', '(3/4, -y+1/2, 1/4)',
1124 '(1/4, -y, 1/4)', '(1/4, -y+1/2, 3/4)',
1125 '(1/4, y, 3/4)', '(1/4, y+1/2, 1/4)',
1126 '(3/4, y, 1/4)', '(3/4, y+1/2, 3/4)')),
1127 '16l': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
1128 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
1129 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
1130 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
1131 '(-x, 3/4, 3/4)', '(-x, 1/4, 1/4)',
1132 '(-x+1/2, 3/4, 1/4)', '(-x+1/2, 1/4, 3/4)',
1133 '(x, 1/4, 3/4)', '(x, 3/4, 1/4)',
1134 '(x+1/2, 1/4, 1/4)', '(x+1/2, 3/4, 3/4)')),
1135 '16m': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(1/2, y, z+1/2)',
1136 '(1/2, y+1/2, z)', '(0, -y, z)',
1137 '(0, -y+1/2, z+1/2)', '(1/2, -y, z+1/2)',
1138 '(1/2, -y+1/2, z)', '(0, y, -z)',
1139 '(0, y+1/2, -z+1/2)', '(1/2, y, -z+1/2)',
1140 '(1/2, y+1/2, -z)', '(0, -y, -z)',
1141 '(0, -y+1/2, -z+1/2)', '(1/2, -y, -z+1/2)',
1142 '(1/2, -y+1/2, -z)')),
1143 '16n': (5, ('(x, 0, z)', '(x, 1/2, z+1/2)', '(x+1/2, 0, z+1/2)',
1144 '(x+1/2, 1/2, z)', '(-x, 0, z)', '(-x, 1/2, z+1/2)',
1145 '(-x+1/2, 0, z+1/2)', '(-x+1/2, 1/2, z)',
1146 '(-x, 0, -z)', '(-x, 1/2, -z+1/2)',
1147 '(-x+1/2, 0, -z+1/2)', '(-x+1/2, 1/2, -z)',
1148 '(x, 0, -z)', '(x, 1/2, -z+1/2)',
1149 '(x+1/2, 0, -z+1/2)', '(x+1/2, 1/2, -z)')),
1150 '16o': (3, ('(x, y, 0)', '(x, y+1/2, 1/2)', '(x+1/2, y, 1/2)',
1151 '(x+1/2, y+1/2, 0)', '(-x, -y, 0)',
1152 '(-x, -y+1/2, 1/2)', '(-x+1/2, -y, 1/2)',
1153 '(-x+1/2, -y+1/2, 0)', '(-x, y, 0)',
1154 '(-x, y+1/2, 1/2)', '(-x+1/2, y, 1/2)',
1155 '(-x+1/2, y+1/2, 0)', '(x, -y, 0)',
1156 '(x, -y+1/2, 1/2)', '(x+1/2, -y, 1/2)',
1157 '(x+1/2, -y+1/2, 0)')),
1158 '32p': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
1159 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
1160 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
1161 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
1162 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
1163 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
1164 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
1165 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
1166 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
1167 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
1168 '(x, y, -z)', '(x, y+1/2, -z+1/2)',
1169 '(x+1/2, y, -z+1/2)', '(x+1/2, y+1/2, -z)',
1170 '(x, -y, z)', '(x, -y+1/2, z+1/2)',
1171 '(x+1/2, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
1172 '(-x, y, z)', '(-x, y+1/2, z+1/2)',
1173 '(-x+1/2, y, z+1/2)', '(-x+1/2, y+1/2, z)'))},
1174 '70:1': {'8a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
1175 '(1/2, 1/2, 0)', '(1/4, 1/4, 1/4)',
1176 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
1177 '(3/4, 3/4, 1/4)')),
1178 '8b': (0, ('(0, 0, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 0)',
1179 '(1/2, 1/2, 1/2)', '(1/4, 1/4, 3/4)',
1180 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)',
1181 '(3/4, 3/4, 3/4)')),
1182 '16c': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
1183 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
1184 '(7/8, 7/8, 1/8)', '(7/8, 3/8, 5/8)',
1185 '(3/8, 7/8, 5/8)', '(3/8, 3/8, 1/8)',
1186 '(7/8, 1/8, 7/8)', '(7/8, 5/8, 3/8)',
1187 '(3/8, 1/8, 3/8)', '(3/8, 5/8, 7/8)',
1188 '(1/8, 7/8, 7/8)', '(1/8, 3/8, 3/8)',
1189 '(5/8, 7/8, 3/8)', '(5/8, 3/8, 7/8)')),
1190 '16d': (0, ('(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
1191 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
1192 '(3/8, 3/8, 5/8)', '(3/8, 7/8, 1/8)',
1193 '(7/8, 3/8, 1/8)', '(7/8, 7/8, 5/8)',
1194 '(3/8, 5/8, 3/8)', '(3/8, 1/8, 7/8)',
1195 '(7/8, 5/8, 7/8)', '(7/8, 1/8, 3/8)',
1196 '(5/8, 3/8, 3/8)', '(5/8, 7/8, 7/8)',
1197 '(1/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)')),
1198 '16e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
1199 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
1200 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)',
1201 '(-x+1/4, 1/4, 1/4)', '(-x+1/4, 3/4, 3/4)',
1202 '(-x+3/4, 1/4, 3/4)', '(-x+3/4, 3/4, 1/4)',
1203 '(x+1/4, 1/4, 1/4)', '(x+1/4, 3/4, 3/4)',
1204 '(x+3/4, 1/4, 3/4)', '(x+3/4, 3/4, 1/4)')),
1205 '16f': (2, ('(0, y, 0)', '(0, y+1/2, 1/2)', '(1/2, y, 1/2)',
1206 '(1/2, y+1/2, 0)', '(0, -y, 0)',
1207 '(0, -y+1/2, 1/2)', '(1/2, -y, 1/2)',
1208 '(1/2, -y+1/2, 0)', '(1/4, -y+1/4, 1/4)',
1209 '(1/4, -y+3/4, 3/4)', '(3/4, -y+1/4, 3/4)',
1210 '(3/4, -y+3/4, 1/4)', '(1/4, y+1/4, 1/4)',
1211 '(1/4, y+3/4, 3/4)', '(3/4, y+1/4, 3/4)',
1212 '(3/4, y+3/4, 1/4)')),
1213 '16g': (4, ('(0, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
1214 '(1/2, 1/2, z)', '(0, 0, -z)', '(0, 1/2, -z+1/2)',
1215 '(1/2, 0, -z+1/2)', '(1/2, 1/2, -z)',
1216 '(1/4, 1/4, -z+1/4)', '(1/4, 3/4, -z+3/4)',
1217 '(3/4, 1/4, -z+3/4)', '(3/4, 3/4, -z+1/4)',
1218 '(1/4, 1/4, z+1/4)', '(1/4, 3/4, z+3/4)',
1219 '(3/4, 1/4, z+3/4)', '(3/4, 3/4, z+1/4)')),
1220 '32h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
1221 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
1222 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
1223 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
1224 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
1225 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
1226 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
1227 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
1228 '(-x+1/4, -y+1/4, -z+1/4)',
1229 '(-x+1/4, -y+3/4, -z+3/4)',
1230 '(-x+3/4, -y+1/4, -z+3/4)',
1231 '(-x+3/4, -y+3/4, -z+1/4)',
1232 '(x+1/4, y+1/4, -z+1/4)', '(x+1/4, y+3/4, -z+3/4)',
1233 '(x+3/4, y+1/4, -z+3/4)', '(x+3/4, y+3/4, -z+1/4)',
1234 '(x+1/4, -y+1/4, z+1/4)', '(x+1/4, -y+3/4, z+3/4)',
1235 '(x+3/4, -y+1/4, z+3/4)', '(x+3/4, -y+3/4, z+1/4)',
1236 '(-x+1/4, y+1/4, z+1/4)', '(-x+1/4, y+3/4, z+3/4)',
1237 '(-x+3/4, y+1/4, z+3/4)', '(-x+3/4, y+3/4, z+1/4)'
1238 ))},
1239 '70:2': {'8a': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
1240 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
1241 '(7/8, 7/8, 7/8)', '(7/8, 3/8, 3/8)',
1242 '(3/8, 7/8, 3/8)', '(3/8, 3/8, 7/8)')),
1243 '8b': (0, ('(1/8, 1/8, 5/8)', '(1/8, 5/8, 1/8)',
1244 '(5/8, 1/8, 1/8)', '(5/8, 5/8, 5/8)',
1245 '(7/8, 7/8, 3/8)', '(7/8, 3/8, 7/8)',
1246 '(3/8, 7/8, 7/8)', '(3/8, 3/8, 3/8)')),
1247 '16c': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
1248 '(1/2, 1/2, 0)', '(3/4, 3/4, 0)',
1249 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)',
1250 '(1/4, 1/4, 0)', '(3/4, 0, 3/4)',
1251 '(3/4, 1/2, 1/4)', '(1/4, 0, 1/4)',
1252 '(1/4, 1/2, 3/4)', '(0, 3/4, 3/4)',
1253 '(0, 1/4, 1/4)', '(1/2, 3/4, 1/4)',
1254 '(1/2, 1/4, 3/4)')),
1255 '16d': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
1256 '(0, 0, 1/2)', '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)',
1257 '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
1258 '(1/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
1259 '(3/4, 1/2, 3/4)', '(3/4, 0, 1/4)',
1260 '(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)',
1261 '(0, 1/4, 3/4)', '(0, 3/4, 1/4)')),
1262 '16e': (1, ('(x, 1/8, 1/8)', '(x, 5/8, 5/8)',
1263 '(x+1/2, 1/8, 5/8)', '(x+1/2, 5/8, 1/8)',
1264 '(-x+3/4, 5/8, 1/8)', '(-x+3/4, 1/8, 5/8)',
1265 '(-x+1/4, 5/8, 5/8)', '(-x+1/4, 1/8, 1/8)',
1266 '(-x, 7/8, 7/8)', '(-x, 3/8, 3/8)',
1267 '(-x+1/2, 7/8, 3/8)', '(-x+1/2, 3/8, 7/8)',
1268 '(x+1/4, 3/8, 7/8)', '(x+1/4, 7/8, 3/8)',
1269 '(x+3/4, 3/8, 3/8)', '(x+3/4, 7/8, 7/8)')),
1270 '16f': (2, ('(1/8, y, 1/8)', '(1/8, y+1/2, 5/8)',
1271 '(5/8, y, 5/8)', '(5/8, y+1/2, 1/8)',
1272 '(5/8, -y+3/4, 1/8)', '(5/8, -y+1/4, 5/8)',
1273 '(1/8, -y+3/4, 5/8)', '(1/8, -y+1/4, 1/8)',
1274 '(7/8, -y, 7/8)', '(7/8, -y+1/2, 3/8)',
1275 '(3/8, -y, 3/8)', '(3/8, -y+1/2, 7/8)',
1276 '(3/8, y+1/4, 7/8)', '(3/8, y+3/4, 3/8)',
1277 '(7/8, y+1/4, 3/8)', '(7/8, y+3/4, 7/8)')),
1278 '16g': (4, ('(1/8, 1/8, z)', '(1/8, 5/8, z+1/2)',
1279 '(5/8, 1/8, z+1/2)', '(5/8, 5/8, z)',
1280 '(5/8, 1/8, -z+3/4)', '(5/8, 5/8, -z+1/4)',
1281 '(1/8, 1/8, -z+1/4)', '(1/8, 5/8, -z+3/4)',
1282 '(7/8, 7/8, -z)', '(7/8, 3/8, -z+1/2)',
1283 '(3/8, 7/8, -z+1/2)', '(3/8, 3/8, -z)',
1284 '(3/8, 7/8, z+1/4)', '(3/8, 3/8, z+3/4)',
1285 '(7/8, 7/8, z+3/4)', '(7/8, 3/8, z+1/4)')),
1286 '32h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
1287 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
1288 '(-x+3/4, -y+3/4, z)', '(-x+3/4, -y+1/4, z+1/2)',
1289 '(-x+1/4, -y+3/4, z+1/2)', '(-x+1/4, -y+1/4, z)',
1290 '(-x+3/4, y, -z+3/4)', '(-x+3/4, y+1/2, -z+1/4)',
1291 '(-x+1/4, y, -z+1/4)', '(-x+1/4, y+1/2, -z+3/4)',
1292 '(x, -y+3/4, -z+3/4)', '(x, -y+1/4, -z+1/4)',
1293 '(x+1/2, -y+3/4, -z+1/4)',
1294 '(x+1/2, -y+1/4, -z+3/4)', '(-x, -y, -z)',
1295 '(-x, -y+1/2, -z+1/2)', '(-x+1/2, -y, -z+1/2)',
1296 '(-x+1/2, -y+1/2, -z)', '(x+1/4, y+1/4, -z)',
1297 '(x+1/4, y+3/4, -z+1/2)', '(x+3/4, y+1/4, -z+1/2)',
1298 '(x+3/4, y+3/4, -z)', '(x+1/4, -y, z+1/4)',
1299 '(x+1/4, -y+1/2, z+3/4)', '(x+3/4, -y, z+3/4)',
1300 '(x+3/4, -y+1/2, z+1/4)', '(-x, y+1/4, z+1/4)',
1301 '(-x, y+3/4, z+3/4)', '(-x+1/2, y+1/4, z+3/4)',
1302 '(-x+1/2, y+3/4, z+1/4)'))},
1303 '71': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1304 '2b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)')),
1305 '2c': (0, ('(1/2, 1/2, 0)', '(0, 0, 1/2)')),
1306 '2d': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 0)')),
1307 '4e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
1308 '(-x+1/2, 1/2, 1/2)')),
1309 '4f': (1, ('(x, 1/2, 0)', '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
1310 '(-x+1/2, 0, 1/2)')),
1311 '4g': (2, ('(0, y, 0)', '(1/2, y+1/2, 1/2)', '(0, -y, 0)',
1312 '(1/2, -y+1/2, 1/2)')),
1313 '4h': (2, ('(0, y, 1/2)', '(1/2, y+1/2, 0)', '(0, -y, 1/2)',
1314 '(1/2, -y+1/2, 0)')),
1315 '4i': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
1316 '(1/2, 1/2, -z+1/2)')),
1317 '4j': (4, ('(1/2, 0, z)', '(0, 1/2, z+1/2)', '(1/2, 0, -z)',
1318 '(0, 1/2, -z+1/2)')),
1319 '8k': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
1320 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
1321 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
1322 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
1323 '8l': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y, z)',
1324 '(1/2, -y+1/2, z+1/2)', '(0, y, -z)',
1325 '(1/2, y+1/2, -z+1/2)', '(0, -y, -z)',
1326 '(1/2, -y+1/2, -z+1/2)')),
1327 '8m': (5, ('(x, 0, z)', '(x+1/2, 1/2, z+1/2)', '(-x, 0, z)',
1328 '(-x+1/2, 1/2, z+1/2)', '(-x, 0, -z)',
1329 '(-x+1/2, 1/2, -z+1/2)', '(x, 0, -z)',
1330 '(x+1/2, 1/2, -z+1/2)')),
1331 '8n': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 0)',
1332 '(-x+1/2, -y+1/2, 1/2)', '(-x, y, 0)',
1333 '(-x+1/2, y+1/2, 1/2)', '(x, -y, 0)',
1334 '(x+1/2, -y+1/2, 1/2)')),
1335 '16o': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1336 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
1337 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
1338 '(x+1/2, -y+1/2, -z+1/2)', '(-x, -y, -z)',
1339 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
1340 '(x+1/2, y+1/2, -z+1/2)', '(x, -y, z)',
1341 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
1342 '(-x+1/2, y+1/2, z+1/2)'))},
1343 '72': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)', '(0, 0, 3/4)',
1344 '(1/2, 1/2, 1/4)')),
1345 '4b': (0, ('(1/2, 0, 1/4)', '(0, 1/2, 3/4)', '(1/2, 0, 3/4)',
1346 '(0, 1/2, 1/4)')),
1347 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 1/2, 0)',
1348 '(0, 0, 1/2)')),
1349 '4d': (0, ('(1/2, 0, 0)', '(0, 1/2, 1/2)', '(0, 1/2, 0)',
1350 '(1/2, 0, 1/2)')),
1351 '8e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
1352 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
1353 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
1354 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)')),
1355 '8f': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)', '(-x, 0, 1/4)',
1356 '(-x+1/2, 1/2, 3/4)', '(-x, 0, 3/4)',
1357 '(-x+1/2, 1/2, 1/4)', '(x, 0, 3/4)',
1358 '(x+1/2, 1/2, 1/4)')),
1359 '8g': (2, ('(0, y, 1/4)', '(1/2, y+1/2, 3/4)', '(0, -y, 1/4)',
1360 '(1/2, -y+1/2, 3/4)', '(0, -y, 3/4)',
1361 '(1/2, -y+1/2, 1/4)', '(0, y, 3/4)',
1362 '(1/2, y+1/2, 1/4)')),
1363 '8h': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 1/2, -z)',
1364 '(0, 0, -z+1/2)', '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
1365 '(1/2, 1/2, z)', '(0, 0, z+1/2)')),
1366 '8i': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
1367 '(0, 1/2, -z+1/2)', '(0, 1/2, -z)',
1368 '(1/2, 0, -z+1/2)', '(1/2, 0, z)', '(0, 1/2, z+1/2)'
1369 )),
1370 '8j': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 0)',
1371 '(-x+1/2, -y+1/2, 1/2)', '(-x+1/2, y+1/2, 0)',
1372 '(-x, y, 1/2)', '(x+1/2, -y+1/2, 0)', '(x, -y, 1/2)'
1373 )),
1374 '16k': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1375 '(-x+1/2, -y+1/2, z+1/2)', '(-x+1/2, y+1/2, -z)',
1376 '(-x, y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
1377 '(x, -y, -z+1/2)', '(-x, -y, -z)',
1378 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
1379 '(x+1/2, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, z)',
1380 '(x, -y, z+1/2)', '(-x+1/2, y+1/2, z)',
1381 '(-x, y, z+1/2)'))},
1382 '73': {'8a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
1383 '(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 0)',
1384 '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
1385 '8b': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
1386 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
1387 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
1388 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)')),
1389 '8c': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
1390 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(-x, 0, 3/4)',
1391 '(-x+1/2, 1/2, 1/4)', '(x+1/2, 0, 1/4)',
1392 '(x, 1/2, 3/4)')),
1393 '8d': (2, ('(1/4, y, 0)', '(3/4, y+1/2, 1/2)', '(1/4, -y, 1/2)',
1394 '(3/4, -y+1/2, 0)', '(3/4, -y, 0)',
1395 '(1/4, -y+1/2, 1/2)', '(3/4, y, 1/2)',
1396 '(1/4, y+1/2, 0)')),
1397 '8e': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)',
1398 '(0, 3/4, -z+1/2)', '(1/2, 1/4, -z)', '(0, 3/4, -z)',
1399 '(1/2, 1/4, -z+1/2)', '(0, 1/4, z+1/2)',
1400 '(1/2, 3/4, z)')),
1401 '16f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1402 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
1403 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
1404 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
1405 '(-x, -y, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
1406 '(x+1/2, y, -z+1/2)', '(x, y+1/2, -z)',
1407 '(x, -y+1/2, z+1/2)', '(x+1/2, -y, z)',
1408 '(-x+1/2, y+1/2, z)', '(-x, y, z+1/2)'))},
1409 '74': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 1/2, 0)',
1410 '(1/2, 0, 1/2)')),
1411 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
1412 '(1/2, 0, 0)')),
1413 '4c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
1414 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)')),
1415 '4d': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
1416 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)')),
1417 '4e': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)', '(0, 3/4, -z)',
1418 '(1/2, 1/4, -z+1/2)')),
1419 '8f': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 1/2, 0)',
1420 '(-x+1/2, 0, 1/2)', '(-x, 0, 0)',
1421 '(-x+1/2, 1/2, 1/2)', '(x, 1/2, 0)',
1422 '(x+1/2, 0, 1/2)')),
1423 '8g': (2, ('(1/4, y, 1/4)', '(3/4, y+1/2, 3/4)',
1424 '(3/4, -y+1/2, 1/4)', '(1/4, -y, 3/4)',
1425 '(3/4, -y, 3/4)', '(1/4, -y+1/2, 1/4)',
1426 '(1/4, y+1/2, 3/4)', '(3/4, y, 1/4)')),
1427 '8h': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y+1/2, z)',
1428 '(1/2, -y, z+1/2)', '(0, y+1/2, -z)',
1429 '(1/2, y, -z+1/2)', '(0, -y, -z)',
1430 '(1/2, -y+1/2, -z+1/2)')),
1431 '8i': (5, ('(x, 1/4, z)', '(x+1/2, 3/4, z+1/2)', '(-x, 1/4, z)',
1432 '(-x+1/2, 3/4, z+1/2)', '(-x, 3/4, -z)',
1433 '(-x+1/2, 1/4, -z+1/2)', '(x, 3/4, -z)',
1434 '(x+1/2, 1/4, -z+1/2)')),
1435 '16j': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1436 '(-x, -y+1/2, z)', '(-x+1/2, -y, z+1/2)',
1437 '(-x, y+1/2, -z)', '(-x+1/2, y, -z+1/2)',
1438 '(x, -y, -z)', '(x+1/2, -y+1/2, -z+1/2)',
1439 '(-x, -y, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
1440 '(x, y+1/2, -z)', '(x+1/2, y, -z+1/2)',
1441 '(x, -y+1/2, z)', '(x+1/2, -y, z+1/2)', '(-x, y, z)',
1442 '(-x+1/2, y+1/2, z+1/2)'))},
1443 '75': {'1a': (4, ('(0, 0, z)', )),
1444 '1b': (4, ('(1/2, 1/2, z)', )),
1445 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, z)')),
1446 '4d': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1447 '(y, -x, z)'))},
1448 '76': {'4a': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-y, x, z+1/4)',
1449 '(y, -x, z+3/4)'))},
1450 '77': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
1451 '2b': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
1452 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)')),
1453 '4d': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1454 '(y, -x, z+1/2)'))},
1455 '78': {'4a': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-y, x, z+3/4)',
1456 '(y, -x, z+1/4)'))},
1457 '79': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
1458 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
1459 '(0, 1/2, z+1/2)')),
1460 '8c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1461 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
1462 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
1463 '(y+1/2, -x+1/2, z+1/2)'))},
1464 '80': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 1/2, z+1/4)',
1465 '(1/2, 0, z+3/4)')),
1466 '8b': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1467 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
1468 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
1469 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)'))},
1470 '81': {'1a': (0, ('(0, 0, 0)', )),
1471 '1b': (0, ('(0, 0, 1/2)', )),
1472 '1c': (0, ('(1/2, 1/2, 0)', )),
1473 '1d': (0, ('(1/2, 1/2, 1/2)', )),
1474 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
1475 '2f': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
1476 '2g': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
1477 '4h': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
1478 '(-y, x, -z)'))},
1479 '82': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1480 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
1481 '2c': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)')),
1482 '2d': (0, ('(0, 1/2, 3/4)', '(1/2, 0, 1/4)')),
1483 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
1484 '(1/2, 1/2, -z+1/2)')),
1485 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
1486 '(0, 1/2, -z+1/2)')),
1487 '8g': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1488 '(-x+1/2, -y+1/2, z+1/2)', '(y, -x, -z)',
1489 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
1490 '(-y+1/2, x+1/2, -z+1/2)'))},
1491 '83': {'1a': (0, ('(0, 0, 0)', )),
1492 '1b': (0, ('(0, 0, 1/2)', )),
1493 '1c': (0, ('(1/2, 1/2, 0)', )),
1494 '1d': (0, ('(1/2, 1/2, 1/2)', )),
1495 '2e': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
1496 '2f': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
1497 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
1498 '2h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
1499 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
1500 '(1/2, 0, -z)')),
1501 '4j': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 0)',
1502 '(y, -x, 0)')),
1503 '4k': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)', '(-y, x, 1/2)',
1504 '(y, -x, 1/2)')),
1505 '8l': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1506 '(y, -x, z)', '(-x, -y, -z)', '(x, y, -z)',
1507 '(y, -x, -z)', '(-y, x, -z)'))},
1508 '84': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
1509 '2b': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
1510 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
1511 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)')),
1512 '2e': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
1513 '2f': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
1514 '4g': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
1515 '(0, 0, -z+1/2)')),
1516 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)',
1517 '(1/2, 1/2, -z)', '(1/2, 1/2, -z+1/2)')),
1518 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, -z)',
1519 '(1/2, 0, -z+1/2)')),
1520 '4j': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 1/2)',
1521 '(y, -x, 1/2)')),
1522 '8k': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1523 '(y, -x, z+1/2)', '(-x, -y, -z)', '(x, y, -z)',
1524 '(y, -x, -z+1/2)', '(-y, x, -z+1/2)'))},
1525 '85:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
1526 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
1527 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
1528 '4d': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(1/4, 3/4, 0)',
1529 '(3/4, 1/4, 0)')),
1530 '4e': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
1531 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
1532 '4f': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(1/2, 1/2, -z)',
1533 '(0, 0, -z)')),
1534 '8g': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z)',
1535 '(y+1/2, -x+1/2, z)', '(-x+1/2, -y+1/2, -z)',
1536 '(x+1/2, y+1/2, -z)', '(y, -x, -z)', '(-y, x, -z)'
1537 ))},
1538 '85:2': {'2a': (0, ('(1/4, 3/4, 0)', '(3/4, 1/4, 0)')),
1539 '2b': (0, ('(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
1540 '2c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, -z)')),
1541 '4d': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
1542 '(0, 1/2, 0)')),
1543 '4e': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
1544 '(0, 1/2, 1/2)')),
1545 '4f': (4, ('(1/4, 3/4, z)', '(3/4, 1/4, z)', '(3/4, 1/4, -z)',
1546 '(1/4, 3/4, -z)')),
1547 '8g': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
1548 '(-y+1/2, x, z)', '(y, -x+1/2, z)', '(-x, -y, -z)',
1549 '(x+1/2, y+1/2, -z)', '(y+1/2, -x, -z)',
1550 '(-y, x+1/2, -z)'))},
1551 '86:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1552 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
1553 '4c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
1554 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)')),
1555 '4d': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)',
1556 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
1557 '4e': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)',
1558 '(1/2, 0, -z+1/2)', '(1/2, 0, -z)')),
1559 '4f': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
1560 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)')),
1561 '8g': (7, ('(x, y, z)', '(-x, -y, z)',
1562 '(-y+1/2, x+1/2, z+1/2)', '(y+1/2, -x+1/2, z+1/2)',
1563 '(-x+1/2, -y+1/2, -z+1/2)',
1564 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
1565 '(-y, x, -z)'))},
1566 '86:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
1567 '2b': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
1568 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(0, 1/2, 1/2)',
1569 '(1/2, 0, 1/2)')),
1570 '4d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(0, 1/2, 0)',
1571 '(1/2, 0, 0)')),
1572 '4e': (4, ('(3/4, 1/4, z)', '(3/4, 1/4, z+1/2)',
1573 '(1/4, 3/4, -z)', '(1/4, 3/4, -z+1/2)')),
1574 '4f': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, z+1/2)',
1575 '(3/4, 3/4, -z)', '(1/4, 1/4, -z+1/2)')),
1576 '8g': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
1577 '(-y, x+1/2, z+1/2)', '(y+1/2, -x, z+1/2)',
1578 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
1579 '(y, -x+1/2, -z+1/2)', '(-y+1/2, x, -z+1/2)'))},
1580 '87': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1581 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
1582 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
1583 '(0, 1/2, 1/2)')),
1584 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(1/2, 0, 1/4)',
1585 '(0, 1/2, 3/4)')),
1586 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
1587 '(1/2, 1/2, -z+1/2)')),
1588 '8f': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
1589 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
1590 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
1591 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 3/4)')),
1592 '8g': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
1593 '(0, 1/2, z+1/2)', '(0, 1/2, -z)', '(1/2, 0, -z+1/2)',
1594 '(1/2, 0, -z)', '(0, 1/2, -z+1/2)')),
1595 '8h': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 0)',
1596 '(-x+1/2, -y+1/2, 1/2)', '(-y, x, 0)',
1597 '(-y+1/2, x+1/2, 1/2)', '(y, -x, 0)',
1598 '(y+1/2, -x+1/2, 1/2)')),
1599 '16i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1600 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
1601 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
1602 '(y+1/2, -x+1/2, z+1/2)', '(-x, -y, -z)',
1603 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
1604 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
1605 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
1606 '(-y+1/2, x+1/2, -z+1/2)'))},
1607 '88:1': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/4)',
1608 '(1/2, 0, 3/4)')),
1609 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(0, 1/2, 3/4)',
1610 '(1/2, 0, 1/4)')),
1611 '8c': (0, ('(0, 1/4, 1/8)', '(1/2, 3/4, 5/8)',
1612 '(1/2, 1/4, 5/8)', '(0, 3/4, 1/8)',
1613 '(3/4, 1/2, 3/8)', '(1/4, 0, 7/8)', '(3/4, 0, 7/8)',
1614 '(1/4, 1/2, 3/8)')),
1615 '8d': (0, ('(0, 1/4, 5/8)', '(1/2, 3/4, 1/8)',
1616 '(1/2, 1/4, 1/8)', '(0, 3/4, 5/8)',
1617 '(3/4, 1/2, 7/8)', '(1/4, 0, 3/8)', '(3/4, 0, 3/8)',
1618 '(1/4, 1/2, 7/8)')),
1619 '8e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 1/2, z+1/4)',
1620 '(1/2, 0, z+3/4)', '(0, 1/2, -z+1/4)',
1621 '(1/2, 0, -z+3/4)', '(0, 0, -z)',
1622 '(1/2, 1/2, -z+1/2)')),
1623 '16f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1624 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
1625 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
1626 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
1627 '(-x, -y+1/2, -z+1/4)', '(-x+1/2, -y, -z+3/4)',
1628 '(x+1/2, y, -z+3/4)', '(x, y+1/2, -z+1/4)',
1629 '(y, -x, -z)', '(y+1/2, -x+1/2, -z+1/2)',
1630 '(-y+1/2, x+1/2, -z+1/2)', '(-y, x, -z)'))},
1631 '88:2': {'4a': (0, ('(0, 1/4, 1/8)', '(1/2, 3/4, 5/8)',
1632 '(1/2, 1/4, 3/8)', '(0, 3/4, 7/8)')),
1633 '4b': (0, ('(0, 1/4, 5/8)', '(1/2, 3/4, 1/8)',
1634 '(1/2, 1/4, 7/8)', '(0, 3/4, 3/8)')),
1635 '8c': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
1636 '(0, 1/2, 0)', '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
1637 '(3/4, 3/4, 3/4)', '(1/4, 1/4, 1/4)')),
1638 '8d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
1639 '(0, 1/2, 1/2)', '(3/4, 1/4, 3/4)',
1640 '(1/4, 3/4, 1/4)', '(3/4, 3/4, 1/4)',
1641 '(1/4, 1/4, 3/4)')),
1642 '8e': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)',
1643 '(1/2, 1/4, z+1/4)', '(0, 3/4, z+3/4)',
1644 '(0, 3/4, -z)', '(1/2, 1/4, -z+1/2)',
1645 '(1/2, 3/4, -z+3/4)', '(0, 1/4, -z+1/4)')),
1646 '16f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1647 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
1648 '(-y+3/4, x+1/4, z+1/4)', '(-y+1/4, x+3/4, z+3/4)',
1649 '(y+3/4, -x+3/4, z+3/4)', '(y+1/4, -x+1/4, z+1/4)',
1650 '(-x, -y, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
1651 '(x+1/2, y, -z+1/2)', '(x, y+1/2, -z)',
1652 '(y+1/4, -x+3/4, -z+3/4)',
1653 '(y+3/4, -x+1/4, -z+1/4)',
1654 '(-y+1/4, x+1/4, -z+1/4)',
1655 '(-y+3/4, x+3/4, -z+3/4)'))},
1656 '89': {'1a': (0, ('(0, 0, 0)', )),
1657 '1b': (0, ('(0, 0, 1/2)', )),
1658 '1c': (0, ('(1/2, 1/2, 0)', )),
1659 '1d': (0, ('(1/2, 1/2, 1/2)', )),
1660 '2e': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)')),
1661 '2f': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
1662 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
1663 '2h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
1664 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
1665 '(1/2, 0, -z)')),
1666 '4j': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x, x, 0)',
1667 '(x, -x, 0)')),
1668 '4k': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(-x, x, 1/2)',
1669 '(x, -x, 1/2)')),
1670 '4l': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)'
1671 )),
1672 '4m': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
1673 '(1/2, -x, 1/2)')),
1674 '4n': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 1/2)',
1675 '(0, -x, 1/2)')),
1676 '4o': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(1/2, x, 0)',
1677 '(1/2, -x, 0)')),
1678 '8p': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1679 '(y, -x, z)', '(-x, y, -z)', '(x, -y, -z)',
1680 '(y, x, -z)', '(-y, -x, -z)'))},
1681 '90': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
1682 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
1683 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
1684 '4d': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(1/2, 1/2, -z)',
1685 '(0, 0, -z)')),
1686 '4e': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x+1/2, x+1/2, 0)',
1687 '(x+1/2, -x+1/2, 0)')),
1688 '4f': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)',
1689 '(-x+1/2, x+1/2, 1/2)', '(x+1/2, -x+1/2, 1/2)')),
1690 '8g': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z)',
1691 '(y+1/2, -x+1/2, z)', '(-x+1/2, y+1/2, -z)',
1692 '(x+1/2, -y+1/2, -z)', '(y, x, -z)', '(-y, -x, -z)'))},
1693 '91': {'4a': (2, ('(0, y, 0)', '(0, -y, 1/2)', '(-y, 0, 1/4)',
1694 '(y, 0, 3/4)')),
1695 '4b': (2, ('(1/2, y, 0)', '(1/2, -y, 1/2)', '(-y, 1/2, 1/4)',
1696 '(y, 1/2, 3/4)')),
1697 '4c': (1, ('(x, x, 3/8)', '(-x, -x, 7/8)', '(-x, x, 5/8)',
1698 '(x, -x, 1/8)')),
1699 '8d': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-y, x, z+1/4)',
1700 '(y, -x, z+3/4)', '(-x, y, -z)', '(x, -y, -z+1/2)',
1701 '(y, x, -z+3/4)', '(-y, -x, -z+1/4)'))},
1702 '92': {'4a': (1, ('(x, x, 0)', '(-x, -x, 1/2)', '(-x+1/2, x+1/2, 1/4)',
1703 '(x+1/2, -x+1/2, 3/4)')),
1704 '8b': (7, ('(x, y, z)', '(-x, -y, z+1/2)',
1705 '(-y+1/2, x+1/2, z+1/4)', '(y+1/2, -x+1/2, z+3/4)',
1706 '(-x+1/2, y+1/2, -z+1/4)', '(x+1/2, -y+1/2, -z+3/4)',
1707 '(y, x, -z)', '(-y, -x, -z+1/2)'))},
1708 '93': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
1709 '2b': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
1710 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
1711 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)')),
1712 '2e': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
1713 '2f': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
1714 '4g': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
1715 '(0, 0, -z+1/2)')),
1716 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)',
1717 '(1/2, 1/2, -z)', '(1/2, 1/2, -z+1/2)')),
1718 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, -z)',
1719 '(1/2, 0, -z+1/2)')),
1720 '4j': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 1/2)',
1721 '(0, -x, 1/2)')),
1722 '4k': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 0)',
1723 '(1/2, -x, 0)')),
1724 '4l': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 0)',
1725 '(0, -x, 0)')),
1726 '4m': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(1/2, x, 1/2)',
1727 '(1/2, -x, 1/2)')),
1728 '4n': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)', '(-x, x, 3/4)',
1729 '(x, -x, 3/4)')),
1730 '4o': (1, ('(x, x, 3/4)', '(-x, -x, 3/4)', '(-x, x, 1/4)',
1731 '(x, -x, 1/4)')),
1732 '8p': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1733 '(y, -x, z+1/2)', '(-x, y, -z)', '(x, -y, -z)',
1734 '(y, x, -z+1/2)', '(-y, -x, -z+1/2)'))},
1735 '94': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1736 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
1737 '4c': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
1738 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)')),
1739 '4d': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)', '(1/2, 0, -z+1/2)',
1740 '(1/2, 0, -z)')),
1741 '4e': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x+1/2, x+1/2, 1/2)',
1742 '(x+1/2, -x+1/2, 1/2)')),
1743 '4f': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(-x+1/2, x+1/2, 0)',
1744 '(x+1/2, -x+1/2, 0)')),
1745 '8g': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z+1/2)',
1746 '(y+1/2, -x+1/2, z+1/2)', '(-x+1/2, y+1/2, -z+1/2)',
1747 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z)',
1748 '(-y, -x, -z)'))},
1749 '95': {'4a': (2, ('(0, y, 0)', '(0, -y, 1/2)', '(-y, 0, 3/4)',
1750 '(y, 0, 1/4)')),
1751 '4b': (2, ('(1/2, y, 0)', '(1/2, -y, 1/2)', '(-y, 1/2, 3/4)',
1752 '(y, 1/2, 1/4)')),
1753 '4c': (1, ('(x, x, 5/8)', '(-x, -x, 1/8)', '(-x, x, 3/8)',
1754 '(x, -x, 7/8)')),
1755 '8d': (7, ('(x, y, z)', '(-x, -y, z+1/2)', '(-y, x, z+3/4)',
1756 '(y, -x, z+1/4)', '(-x, y, -z)', '(x, -y, -z+1/2)',
1757 '(y, x, -z+1/4)', '(-y, -x, -z+3/4)'))},
1758 '96': {'4a': (1, ('(x, x, 0)', '(-x, -x, 1/2)', '(-x+1/2, x+1/2, 3/4)',
1759 '(x+1/2, -x+1/2, 1/4)')),
1760 '8b': (7, ('(x, y, z)', '(-x, -y, z+1/2)',
1761 '(-y+1/2, x+1/2, z+3/4)', '(y+1/2, -x+1/2, z+1/4)',
1762 '(-x+1/2, y+1/2, -z+3/4)', '(x+1/2, -y+1/2, -z+1/4)',
1763 '(y, x, -z)', '(-y, -x, -z+1/2)'))},
1764 '97': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
1765 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
1766 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
1767 '(0, 1/2, 1/2)')),
1768 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(1/2, 0, 1/4)',
1769 '(0, 1/2, 3/4)')),
1770 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
1771 '(1/2, 1/2, -z+1/2)')),
1772 '8f': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
1773 '(0, 1/2, z+1/2)', '(0, 1/2, -z)', '(1/2, 0, -z+1/2)',
1774 '(1/2, 0, -z)', '(0, 1/2, -z+1/2)')),
1775 '8g': (1, ('(x, x, 0)', '(x+1/2, x+1/2, 1/2)', '(-x, -x, 0)',
1776 '(-x+1/2, -x+1/2, 1/2)', '(-x, x, 0)',
1777 '(-x+1/2, x+1/2, 1/2)', '(x, -x, 0)',
1778 '(x+1/2, -x+1/2, 1/2)')),
1779 '8h': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
1780 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
1781 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
1782 '(1/2, -x+1/2, 1/2)')),
1783 '8i': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
1784 '(-x+1/2, 1/2, 0)', '(0, x, 1/2)', '(1/2, x+1/2, 0)',
1785 '(0, -x, 1/2)', '(1/2, -x+1/2, 0)')),
1786 '8j': (1, ('(x, x+1/2, 1/4)', '(x+1/2, x, 3/4)',
1787 '(-x, -x+1/2, 1/4)', '(-x+1/2, -x, 3/4)',
1788 '(-x+1/2, x, 1/4)', '(-x, x+1/2, 3/4)',
1789 '(x+1/2, -x, 1/4)', '(x, -x+1/2, 3/4)')),
1790 '16k': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1791 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
1792 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
1793 '(y+1/2, -x+1/2, z+1/2)', '(-x, y, -z)',
1794 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
1795 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z)',
1796 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z)',
1797 '(-y+1/2, -x+1/2, -z+1/2)'))},
1798 '98': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/4)',
1799 '(1/2, 0, 3/4)')),
1800 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(0, 1/2, 3/4)',
1801 '(1/2, 0, 1/4)')),
1802 '8c': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 1/2, z+1/4)',
1803 '(1/2, 0, z+3/4)', '(1/2, 0, -z+3/4)',
1804 '(0, 1/2, -z+1/4)', '(1/2, 1/2, -z+1/2)',
1805 '(0, 0, -z)')),
1806 '8d': (1, ('(x, x, 0)', '(x+1/2, x+1/2, 1/2)',
1807 '(-x+1/2, -x+1/2, 1/2)', '(-x, -x, 0)',
1808 '(-x, x+1/2, 1/4)', '(-x+1/2, x, 3/4)',
1809 '(x+1/2, -x, 3/4)', '(x, -x+1/2, 1/4)')),
1810 '8e': (1, ('(-x, x, 0)', '(-x+1/2, x+1/2, 1/2)',
1811 '(x+1/2, -x+1/2, 1/2)', '(x, -x, 0)',
1812 '(-x, -x+1/2, 1/4)', '(-x+1/2, -x, 3/4)',
1813 '(x+1/2, x, 3/4)', '(x, x+1/2, 1/4)')),
1814 '8f': (1, ('(x, 1/4, 1/8)', '(x+1/2, 3/4, 5/8)',
1815 '(-x+1/2, 1/4, 5/8)', '(-x, 3/4, 1/8)',
1816 '(3/4, x+1/2, 3/8)', '(1/4, x, 7/8)',
1817 '(3/4, -x, 7/8)', '(1/4, -x+1/2, 3/8)')),
1818 '16g': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1819 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
1820 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
1821 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
1822 '(-x+1/2, y, -z+3/4)', '(-x, y+1/2, -z+1/4)',
1823 '(x, -y+1/2, -z+1/4)', '(x+1/2, -y, -z+3/4)',
1824 '(y+1/2, x+1/2, -z+1/2)', '(y, x, -z)',
1825 '(-y, -x, -z)', '(-y+1/2, -x+1/2, -z+1/2)'))},
1826 '99': {'1a': (4, ('(0, 0, z)', )),
1827 '1b': (4, ('(1/2, 1/2, z)', )),
1828 '2c': (4, ('(1/2, 0, z)', '(0, 1/2, z)')),
1829 '4d': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, z)',
1830 '(x, -x, z)')),
1831 '4e': (5, ('(x, 0, z)', '(-x, 0, z)', '(0, x, z)', '(0, -x, z)'
1832 )),
1833 '4f': (5, ('(x, 1/2, z)', '(-x, 1/2, z)', '(1/2, x, z)',
1834 '(1/2, -x, z)')),
1835 '8g': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1836 '(y, -x, z)', '(x, -y, z)', '(-x, y, z)',
1837 '(-y, -x, z)', '(y, x, z)'))},
1838 '100': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z)')),
1839 '2b': (4, ('(1/2, 0, z)', '(0, 1/2, z)')),
1840 '4c': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)', '(-x+1/2, x, z)',
1841 '(x+1/2, -x, z)')),
1842 '8d': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1843 '(y, -x, z)', '(x+1/2, -y+1/2, z)',
1844 '(-x+1/2, y+1/2, z)', '(-y+1/2, -x+1/2, z)',
1845 '(y+1/2, x+1/2, z)'))},
1846 '101': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
1847 '2b': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
1848 '4c': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, z+1/2)',
1849 '(1/2, 0, z)')),
1850 '4d': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, z+1/2)',
1851 '(x, -x, z+1/2)')),
1852 '8e': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1853 '(y, -x, z+1/2)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
1854 '(-y, -x, z)', '(y, x, z)'))},
1855 '102': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
1856 '4b': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
1857 '(1/2, 0, z)')),
1858 '4c': (5, ('(x, x, z)', '(-x, -x, z)', '(-x+1/2, x+1/2, z+1/2)',
1859 '(x+1/2, -x+1/2, z+1/2)')),
1860 '8d': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z+1/2)',
1861 '(y+1/2, -x+1/2, z+1/2)', '(x+1/2, -y+1/2, z+1/2)',
1862 '(-x+1/2, y+1/2, z+1/2)', '(-y, -x, z)', '(y, x, z)'
1863 ))},
1864 '103': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
1865 '2b': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
1866 '4c': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, z+1/2)',
1867 '(1/2, 0, z+1/2)')),
1868 '8d': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1869 '(y, -x, z)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
1870 '(-y, -x, z+1/2)', '(y, x, z+1/2)'))},
1871 '104': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
1872 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(1/2, 0, z+1/2)',
1873 '(0, 1/2, z+1/2)')),
1874 '8c': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
1875 '(y, -x, z)', '(x+1/2, -y+1/2, z+1/2)',
1876 '(-x+1/2, y+1/2, z+1/2)', '(-y+1/2, -x+1/2, z+1/2)',
1877 '(y+1/2, x+1/2, z+1/2)'))},
1878 '105': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
1879 '2b': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
1880 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)')),
1881 '4d': (5, ('(x, 0, z)', '(-x, 0, z)', '(0, x, z+1/2)',
1882 '(0, -x, z+1/2)')),
1883 '4e': (5, ('(x, 1/2, z)', '(-x, 1/2, z)', '(1/2, x, z+1/2)',
1884 '(1/2, -x, z+1/2)')),
1885 '8f': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1886 '(y, -x, z+1/2)', '(x, -y, z)', '(-x, y, z)',
1887 '(-y, -x, z+1/2)', '(y, x, z+1/2)'))},
1888 '106': {'4a': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(1/2, 1/2, z)',
1889 '(1/2, 1/2, z+1/2)')),
1890 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
1891 '(0, 1/2, z+1/2)')),
1892 '8c': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
1893 '(y, -x, z+1/2)', '(x+1/2, -y+1/2, z)',
1894 '(-x+1/2, y+1/2, z)', '(-y+1/2, -x+1/2, z+1/2)',
1895 '(y+1/2, x+1/2, z+1/2)'))},
1896 '107': {'2a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)')),
1897 '4b': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
1898 '(0, 1/2, z+1/2)')),
1899 '8c': (5, ('(x, x, z)', '(x+1/2, x+1/2, z+1/2)', '(-x, -x, z)',
1900 '(-x+1/2, -x+1/2, z+1/2)', '(-x, x, z)',
1901 '(-x+1/2, x+1/2, z+1/2)', '(x, -x, z)',
1902 '(x+1/2, -x+1/2, z+1/2)')),
1903 '8d': (5, ('(x, 0, z)', '(x+1/2, 1/2, z+1/2)', '(-x, 0, z)',
1904 '(-x+1/2, 1/2, z+1/2)', '(0, x, z)',
1905 '(1/2, x+1/2, z+1/2)', '(0, -x, z)',
1906 '(1/2, -x+1/2, z+1/2)')),
1907 '16e': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1908 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
1909 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
1910 '(y+1/2, -x+1/2, z+1/2)', '(x, -y, z)',
1911 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
1912 '(-x+1/2, y+1/2, z+1/2)', '(-y, -x, z)',
1913 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z)',
1914 '(y+1/2, x+1/2, z+1/2)'))},
1915 '108': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, z+1/2)',
1916 '(1/2, 1/2, z)')),
1917 '4b': (4, ('(1/2, 0, z)', '(0, 1/2, z+1/2)', '(0, 1/2, z)',
1918 '(1/2, 0, z+1/2)')),
1919 '8c': (5, ('(x, x+1/2, z)', '(x+1/2, x, z+1/2)',
1920 '(-x, -x+1/2, z)', '(-x+1/2, -x, z+1/2)',
1921 '(-x+1/2, x, z)', '(-x, x+1/2, z+1/2)',
1922 '(x+1/2, -x, z)', '(x, -x+1/2, z+1/2)')),
1923 '16d': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
1924 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
1925 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
1926 '(y+1/2, -x+1/2, z+1/2)', '(x, -y, z+1/2)',
1927 '(x+1/2, -y+1/2, z)', '(-x, y, z+1/2)',
1928 '(-x+1/2, y+1/2, z)', '(-y, -x, z+1/2)',
1929 '(-y+1/2, -x+1/2, z)', '(y, x, z+1/2)',
1930 '(y+1/2, x+1/2, z)'))},
1931 '109': {'4a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 1/2, z+1/4)',
1932 '(1/2, 0, z+3/4)')),
1933 '8b': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)',
1934 '(1/2, -y+1/2, z+1/2)', '(0, -y, z)',
1935 '(-y, 1/2, z+1/4)', '(-y+1/2, 0, z+3/4)',
1936 '(y+1/2, 0, z+3/4)', '(y, 1/2, z+1/4)')),
1937 '16c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1938 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
1939 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
1940 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
1941 '(x, -y, z)', '(x+1/2, -y+1/2, z+1/2)',
1942 '(-x+1/2, y+1/2, z+1/2)', '(-x, y, z)',
1943 '(-y, -x+1/2, z+1/4)', '(-y+1/2, -x, z+3/4)',
1944 '(y+1/2, x, z+3/4)', '(y, x+1/2, z+1/4)'))},
1945 '110': {'8a': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 1/2, z+1/4)',
1946 '(1/2, 0, z+3/4)', '(0, 0, z+1/2)', '(1/2, 1/2, z)',
1947 '(0, 1/2, z+3/4)', '(1/2, 0, z+1/4)')),
1948 '16b': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
1949 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
1950 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
1951 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
1952 '(x, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
1953 '(-x+1/2, y+1/2, z)', '(-x, y, z+1/2)',
1954 '(-y, -x+1/2, z+3/4)', '(-y+1/2, -x, z+1/4)',
1955 '(y+1/2, x, z+1/4)', '(y, x+1/2, z+3/4)'))},
1956 '111': {'1a': (0, ('(0, 0, 0)', )),
1957 '1b': (0, ('(1/2, 1/2, 1/2)', )),
1958 '1c': (0, ('(0, 0, 1/2)', )),
1959 '1d': (0, ('(1/2, 1/2, 0)', )),
1960 '2e': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)')),
1961 '2f': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)')),
1962 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
1963 '2h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
1964 '4i': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, -x, 0)', '(0, x, 0)'
1965 )),
1966 '4j': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, -x, 1/2)',
1967 '(1/2, x, 1/2)')),
1968 '4k': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, -x, 1/2)',
1969 '(0, x, 1/2)')),
1970 '4l': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(1/2, -x, 0)',
1971 '(1/2, x, 0)')),
1972 '4m': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(0, 1/2, -z)',
1973 '(1/2, 0, z)')),
1974 '4n': (5, ('(x, x, z)', '(-x, -x, z)', '(x, -x, -z)',
1975 '(-x, x, -z)')),
1976 '8o': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
1977 '(-y, x, -z)', '(-x, y, -z)', '(x, -y, -z)',
1978 '(-y, -x, z)', '(y, x, z)'))},
1979 '112': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
1980 '2b': (0, ('(1/2, 0, 1/4)', '(0, 1/2, 3/4)')),
1981 '2c': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
1982 '2d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)')),
1983 '2e': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
1984 '2f': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
1985 '4g': (1, ('(x, 0, 1/4)', '(-x, 0, 1/4)', '(0, -x, 3/4)',
1986 '(0, x, 3/4)')),
1987 '4h': (2, ('(1/2, y, 1/4)', '(1/2, -y, 1/4)', '(y, 1/2, 3/4)',
1988 '(-y, 1/2, 3/4)')),
1989 '4i': (1, ('(x, 1/2, 1/4)', '(-x, 1/2, 1/4)', '(1/2, -x, 3/4)',
1990 '(1/2, x, 3/4)')),
1991 '4j': (2, ('(0, y, 1/4)', '(0, -y, 1/4)', '(y, 0, 3/4)',
1992 '(-y, 0, 3/4)')),
1993 '4k': (4, ('(0, 0, z)', '(0, 0, -z)', '(0, 0, -z+1/2)',
1994 '(0, 0, z+1/2)')),
1995 '4l': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)',
1996 '(1/2, 1/2, -z+1/2)', '(1/2, 1/2, z+1/2)')),
1997 '4m': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(0, 1/2, -z+1/2)',
1998 '(1/2, 0, z+1/2)')),
1999 '8n': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2000 '(-y, x, -z)', '(-x, y, -z+1/2)', '(x, -y, -z+1/2)',
2001 '(-y, -x, z+1/2)', '(y, x, z+1/2)'))},
2002 '113': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
2003 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2004 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
2005 '4d': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z)',
2006 '(1/2, 1/2, z)')),
2007 '4e': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)',
2008 '(x+1/2, -x, -z)', '(-x+1/2, x, -z)')),
2009 '8f': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2010 '(-y, x, -z)', '(-x+1/2, y+1/2, -z)',
2011 '(x+1/2, -y+1/2, -z)', '(-y+1/2, -x+1/2, z)',
2012 '(y+1/2, x+1/2, z)'))},
2013 '114': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2014 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2015 '4c': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
2016 '(1/2, 1/2, z+1/2)')),
2017 '4d': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(1/2, 0, -z+1/2)',
2018 '(0, 1/2, z+1/2)')),
2019 '8e': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2020 '(-y, x, -z)', '(-x+1/2, y+1/2, -z+1/2)',
2021 '(x+1/2, -y+1/2, -z+1/2)', '(-y+1/2, -x+1/2, z+1/2)',
2022 '(y+1/2, x+1/2, z+1/2)'))},
2023 '115': {'1a': (0, ('(0, 0, 0)', )),
2024 '1b': (0, ('(1/2, 1/2, 0)', )),
2025 '1c': (0, ('(1/2, 1/2, 1/2)', )),
2026 '1d': (0, ('(0, 0, 1/2)', )),
2027 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
2028 '2f': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
2029 '2g': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
2030 '4h': (1, ('(x, x, 0)', '(-x, -x, 0)', '(x, -x, 0)',
2031 '(-x, x, 0)')),
2032 '4i': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(x, -x, 1/2)',
2033 '(-x, x, 1/2)')),
2034 '4j': (5, ('(x, 0, z)', '(-x, 0, z)', '(0, -x, -z)',
2035 '(0, x, -z)')),
2036 '4k': (5, ('(x, 1/2, z)', '(-x, 1/2, z)', '(1/2, -x, -z)',
2037 '(1/2, x, -z)')),
2038 '8l': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2039 '(-y, x, -z)', '(x, -y, z)', '(-x, y, z)',
2040 '(y, x, -z)', '(-y, -x, -z)'))},
2041 '116': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
2042 '2b': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
2043 '2c': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
2044 '2d': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
2045 '4e': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)', '(x, -x, 3/4)',
2046 '(-x, x, 3/4)')),
2047 '4f': (1, ('(x, x, 3/4)', '(-x, -x, 3/4)', '(x, -x, 1/4)',
2048 '(-x, x, 1/4)')),
2049 '4g': (4, ('(0, 0, z)', '(0, 0, -z)', '(0, 0, z+1/2)',
2050 '(0, 0, -z+1/2)')),
2051 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)',
2052 '(1/2, 1/2, z+1/2)', '(1/2, 1/2, -z+1/2)')),
2053 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(0, 1/2, z+1/2)',
2054 '(1/2, 0, -z+1/2)')),
2055 '8j': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2056 '(-y, x, -z)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
2057 '(y, x, -z+1/2)', '(-y, -x, -z+1/2)'))},
2058 '117': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
2059 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2060 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
2061 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
2062 '4e': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, z)',
2063 '(1/2, 1/2, -z)')),
2064 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(1/2, 0, z)',
2065 '(0, 1/2, -z)')),
2066 '4g': (1, ('(x, x+1/2, 0)', '(-x, -x+1/2, 0)', '(x+1/2, -x, 0)',
2067 '(-x+1/2, x, 0)')),
2068 '4h': (1, ('(x, x+1/2, 1/2)', '(-x, -x+1/2, 1/2)',
2069 '(x+1/2, -x, 1/2)', '(-x+1/2, x, 1/2)')),
2070 '8i': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2071 '(-y, x, -z)', '(x+1/2, -y+1/2, z)',
2072 '(-x+1/2, y+1/2, z)', '(y+1/2, x+1/2, -z)',
2073 '(-y+1/2, -x+1/2, -z)'))},
2074 '118': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2075 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2076 '2c': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)')),
2077 '2d': (0, ('(0, 1/2, 3/4)', '(1/2, 0, 1/4)')),
2078 '4e': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, z+1/2)',
2079 '(1/2, 1/2, -z+1/2)')),
2080 '4f': (1, ('(x, -x+1/2, 1/4)', '(-x, x+1/2, 1/4)',
2081 '(-x+1/2, -x, 3/4)', '(x+1/2, x, 3/4)')),
2082 '4g': (1, ('(x, x+1/2, 1/4)', '(-x, -x+1/2, 1/4)',
2083 '(x+1/2, -x, 3/4)', '(-x+1/2, x, 3/4)')),
2084 '4h': (4, ('(0, 1/2, z)', '(1/2, 0, -z)', '(1/2, 0, z+1/2)',
2085 '(0, 1/2, -z+1/2)')),
2086 '8i': (7, ('(x, y, z)', '(-x, -y, z)', '(y, -x, -z)',
2087 '(-y, x, -z)', '(x+1/2, -y+1/2, z+1/2)',
2088 '(-x+1/2, y+1/2, z+1/2)', '(y+1/2, x+1/2, -z+1/2)',
2089 '(-y+1/2, -x+1/2, -z+1/2)'))},
2090 '119': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2091 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2092 '2c': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)')),
2093 '2d': (0, ('(0, 1/2, 3/4)', '(1/2, 0, 1/4)')),
2094 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
2095 '(1/2, 1/2, -z+1/2)')),
2096 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
2097 '(0, 1/2, -z+1/2)')),
2098 '8g': (1, ('(x, x, 0)', '(x+1/2, x+1/2, 1/2)', '(-x, -x, 0)',
2099 '(-x+1/2, -x+1/2, 1/2)', '(x, -x, 0)',
2100 '(x+1/2, -x+1/2, 1/2)', '(-x, x, 0)',
2101 '(-x+1/2, x+1/2, 1/2)')),
2102 '8h': (1, ('(x, x+1/2, 1/4)', '(x+1/2, x, 3/4)',
2103 '(-x, -x+1/2, 1/4)', '(-x+1/2, -x, 3/4)',
2104 '(x+1/2, -x, 3/4)', '(x, -x+1/2, 1/4)',
2105 '(-x+1/2, x, 3/4)', '(-x, x+1/2, 1/4)')),
2106 '8i': (5, ('(x, 0, z)', '(x+1/2, 1/2, z+1/2)', '(-x, 0, z)',
2107 '(-x+1/2, 1/2, z+1/2)', '(0, -x, -z)',
2108 '(1/2, -x+1/2, -z+1/2)', '(0, x, -z)',
2109 '(1/2, x+1/2, -z+1/2)')),
2110 '16j': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
2111 '(-x+1/2, -y+1/2, z+1/2)', '(y, -x, -z)',
2112 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
2113 '(-y+1/2, x+1/2, -z+1/2)', '(x, -y, z)',
2114 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
2115 '(-x+1/2, y+1/2, z+1/2)', '(y, x, -z)',
2116 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z)',
2117 '(-y+1/2, -x+1/2, -z+1/2)'))},
2118 '120': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)', '(0, 0, 3/4)',
2119 '(1/2, 1/2, 1/4)')),
2120 '4b': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 0, 1/2)',
2121 '(1/2, 1/2, 0)')),
2122 '4c': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(0, 1/2, 3/4)',
2123 '(1/2, 0, 1/4)')),
2124 '4d': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
2125 '(0, 1/2, 1/2)')),
2126 '8e': (1, ('(x, x, 1/4)', '(x+1/2, x+1/2, 3/4)',
2127 '(-x, -x, 1/4)', '(-x+1/2, -x+1/2, 3/4)',
2128 '(x, -x, 3/4)', '(x+1/2, -x+1/2, 1/4)',
2129 '(-x, x, 3/4)', '(-x+1/2, x+1/2, 1/4)')),
2130 '8f': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
2131 '(1/2, 1/2, -z+1/2)', '(0, 0, z+1/2)',
2132 '(1/2, 1/2, z)', '(0, 0, -z+1/2)', '(1/2, 1/2, -z)'
2133 )),
2134 '8g': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
2135 '(0, 1/2, -z+1/2)', '(0, 1/2, z+1/2)', '(1/2, 0, z)',
2136 '(1/2, 0, -z+1/2)', '(0, 1/2, -z)')),
2137 '8h': (1, ('(x, x+1/2, 0)', '(x+1/2, x, 1/2)',
2138 '(-x, -x+1/2, 0)', '(-x+1/2, -x, 1/2)',
2139 '(x+1/2, -x, 0)', '(x, -x+1/2, 1/2)',
2140 '(-x+1/2, x, 0)', '(-x, x+1/2, 1/2)')),
2141 '16i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
2142 '(-x+1/2, -y+1/2, z+1/2)', '(y, -x, -z)',
2143 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
2144 '(-y+1/2, x+1/2, -z+1/2)', '(x, -y, z+1/2)',
2145 '(x+1/2, -y+1/2, z)', '(-x, y, z+1/2)',
2146 '(-x+1/2, y+1/2, z)', '(y, x, -z+1/2)',
2147 '(y+1/2, x+1/2, -z)', '(-y, -x, -z+1/2)',
2148 '(-y+1/2, -x+1/2, -z)'))},
2149 '121': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2150 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2151 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
2152 '(0, 1/2, 1/2)')),
2153 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(0, 1/2, 3/4)',
2154 '(1/2, 0, 1/4)')),
2155 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
2156 '(1/2, 1/2, -z+1/2)')),
2157 '8f': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
2158 '(-x+1/2, 1/2, 1/2)', '(0, -x, 0)',
2159 '(1/2, -x+1/2, 1/2)', '(0, x, 0)',
2160 '(1/2, x+1/2, 1/2)')),
2161 '8g': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
2162 '(-x+1/2, 1/2, 0)', '(0, -x, 1/2)',
2163 '(1/2, -x+1/2, 0)', '(0, x, 1/2)', '(1/2, x+1/2, 0)'
2164 )),
2165 '8h': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
2166 '(0, 1/2, -z+1/2)', '(0, 1/2, -z)',
2167 '(1/2, 0, -z+1/2)', '(1/2, 0, z)', '(0, 1/2, z+1/2)'
2168 )),
2169 '8i': (5, ('(x, x, z)', '(x+1/2, x+1/2, z+1/2)', '(-x, -x, z)',
2170 '(-x+1/2, -x+1/2, z+1/2)', '(x, -x, -z)',
2171 '(x+1/2, -x+1/2, -z+1/2)', '(-x, x, -z)',
2172 '(-x+1/2, x+1/2, -z+1/2)')),
2173 '16j': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
2174 '(-x+1/2, -y+1/2, z+1/2)', '(y, -x, -z)',
2175 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
2176 '(-y+1/2, x+1/2, -z+1/2)', '(-x, y, -z)',
2177 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
2178 '(x+1/2, -y+1/2, -z+1/2)', '(-y, -x, z)',
2179 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z)',
2180 '(y+1/2, x+1/2, z+1/2)'))},
2181 '122': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 3/4)',
2182 '(0, 1/2, 1/4)')),
2183 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(1/2, 0, 1/4)',
2184 '(0, 1/2, 3/4)')),
2185 '8c': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
2186 '(1/2, 1/2, -z+1/2)', '(1/2, 0, -z+3/4)',
2187 '(0, 1/2, -z+1/4)', '(1/2, 0, z+3/4)',
2188 '(0, 1/2, z+1/4)')),
2189 '8d': (1, ('(x, 1/4, 1/8)', '(x+1/2, 3/4, 5/8)',
2190 '(-x, 3/4, 1/8)', '(-x+1/2, 1/4, 5/8)',
2191 '(1/4, -x, 7/8)', '(3/4, -x+1/2, 3/8)',
2192 '(3/4, x, 7/8)', '(1/4, x+1/2, 3/8)')),
2193 '16e': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
2194 '(-x+1/2, -y+1/2, z+1/2)', '(y, -x, -z)',
2195 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
2196 '(-y+1/2, x+1/2, -z+1/2)', '(-x+1/2, y, -z+3/4)',
2197 '(-x, y+1/2, -z+1/4)', '(x+1/2, -y, -z+3/4)',
2198 '(x, -y+1/2, -z+1/4)', '(-y+1/2, -x, z+3/4)',
2199 '(-y, -x+1/2, z+1/4)', '(y+1/2, x, z+3/4)',
2200 '(y, x+1/2, z+1/4)'))},
2201 '123': {'1a': (0, ('(0, 0, 0)', )),
2202 '1b': (0, ('(0, 0, 1/2)', )),
2203 '1c': (0, ('(1/2, 1/2, 0)', )),
2204 '1d': (0, ('(1/2, 1/2, 1/2)', )),
2205 '2e': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
2206 '2f': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
2207 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
2208 '2h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z)')),
2209 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
2210 '(1/2, 0, -z)')),
2211 '4j': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x, x, 0)',
2212 '(x, -x, 0)')),
2213 '4k': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(-x, x, 1/2)',
2214 '(x, -x, 1/2)')),
2215 '4l': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)'
2216 )),
2217 '4m': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 1/2)',
2218 '(0, -x, 1/2)')),
2219 '4n': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(1/2, x, 0)',
2220 '(1/2, -x, 0)')),
2221 '4o': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
2222 '(1/2, -x, 1/2)')),
2223 '8p': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 0)',
2224 '(y, -x, 0)', '(-x, y, 0)', '(x, -y, 0)',
2225 '(y, x, 0)', '(-y, -x, 0)')),
2226 '8q': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)', '(-y, x, 1/2)',
2227 '(y, -x, 1/2)', '(-x, y, 1/2)', '(x, -y, 1/2)',
2228 '(y, x, 1/2)', '(-y, -x, 1/2)')),
2229 '8r': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, z)',
2230 '(x, -x, z)', '(-x, x, -z)', '(x, -x, -z)',
2231 '(x, x, -z)', '(-x, -x, -z)')),
2232 '8s': (5, ('(x, 0, z)', '(-x, 0, z)', '(0, x, z)', '(0, -x, z)',
2233 '(-x, 0, -z)', '(x, 0, -z)', '(0, x, -z)',
2234 '(0, -x, -z)')),
2235 '8t': (5, ('(x, 1/2, z)', '(-x, 1/2, z)', '(1/2, x, z)',
2236 '(1/2, -x, z)', '(-x, 1/2, -z)', '(x, 1/2, -z)',
2237 '(1/2, x, -z)', '(1/2, -x, -z)')),
2238 '16u': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2239 '(y, -x, z)', '(-x, y, -z)', '(x, -y, -z)',
2240 '(y, x, -z)', '(-y, -x, -z)', '(-x, -y, -z)',
2241 '(x, y, -z)', '(y, -x, -z)', '(-y, x, -z)',
2242 '(x, -y, z)', '(-x, y, z)', '(-y, -x, z)',
2243 '(y, x, z)'))},
2244 '124': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
2245 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
2246 '2c': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
2247 '2d': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
2248 '4e': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
2249 '(1/2, 0, 1/2)')),
2250 '4f': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 1/4)', '(0, 1/2, 3/4)',
2251 '(1/2, 0, 3/4)')),
2252 '4g': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
2253 '(0, 0, z+1/2)')),
2254 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, -z+1/2)',
2255 '(1/2, 1/2, -z)', '(1/2, 1/2, z+1/2)')),
2256 '8i': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z+1/2)',
2257 '(1/2, 0, -z+1/2)', '(0, 1/2, -z)', '(1/2, 0, -z)',
2258 '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)')),
2259 '8j': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)', '(-x, x, 1/4)',
2260 '(x, -x, 1/4)', '(-x, -x, 3/4)', '(x, x, 3/4)',
2261 '(x, -x, 3/4)', '(-x, x, 3/4)')),
2262 '8k': (1, ('(x, 0, 1/4)', '(-x, 0, 1/4)', '(0, x, 1/4)',
2263 '(0, -x, 1/4)', '(-x, 0, 3/4)', '(x, 0, 3/4)',
2264 '(0, -x, 3/4)', '(0, x, 3/4)')),
2265 '8l': (1, ('(x, 1/2, 1/4)', '(-x, 1/2, 1/4)', '(1/2, x, 1/4)',
2266 '(1/2, -x, 1/4)', '(-x, 1/2, 3/4)', '(x, 1/2, 3/4)',
2267 '(1/2, -x, 3/4)', '(1/2, x, 3/4)')),
2268 '8m': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 0)',
2269 '(y, -x, 0)', '(-x, y, 1/2)', '(x, -y, 1/2)',
2270 '(y, x, 1/2)', '(-y, -x, 1/2)')),
2271 '16n': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2272 '(y, -x, z)', '(-x, y, -z+1/2)', '(x, -y, -z+1/2)',
2273 '(y, x, -z+1/2)', '(-y, -x, -z+1/2)',
2274 '(-x, -y, -z)', '(x, y, -z)', '(y, -x, -z)',
2275 '(-y, x, -z)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
2276 '(-y, -x, z+1/2)', '(y, x, z+1/2)'))},
2277 '125:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
2278 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2279 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
2280 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
2281 '4e': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(3/4, 1/4, 0)',
2282 '(1/4, 3/4, 0)')),
2283 '4f': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
2284 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
2285 '4g': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z)',
2286 '(1/2, 1/2, z)')),
2287 '4h': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(0, 1/2, -z)',
2288 '(1/2, 0, -z)')),
2289 '8i': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x, x, 0)',
2290 '(x, -x, 0)', '(-x+1/2, -x+1/2, 0)',
2291 '(x+1/2, x+1/2, 0)', '(x+1/2, -x+1/2, 0)',
2292 '(-x+1/2, x+1/2, 0)')),
2293 '8j': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(-x, x, 1/2)',
2294 '(x, -x, 1/2)', '(-x+1/2, -x+1/2, 1/2)',
2295 '(x+1/2, x+1/2, 1/2)', '(x+1/2, -x+1/2, 1/2)',
2296 '(-x+1/2, x+1/2, 1/2)')),
2297 '8k': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
2298 '(0, -x, 0)', '(-x+1/2, 1/2, 0)',
2299 '(x+1/2, 1/2, 0)', '(1/2, -x+1/2, 0)',
2300 '(1/2, x+1/2, 0)')),
2301 '8l': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 1/2)',
2302 '(0, -x, 1/2)', '(-x+1/2, 1/2, 1/2)',
2303 '(x+1/2, 1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
2304 '(1/2, x+1/2, 1/2)')),
2305 '8m': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)',
2306 '(-x+1/2, x, z)', '(x+1/2, -x, z)',
2307 '(-x, x+1/2, -z)', '(x, -x+1/2, -z)',
2308 '(x+1/2, x, -z)', '(-x+1/2, -x, -z)')),
2309 '16n': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2310 '(y, -x, z)', '(-x, y, -z)', '(x, -y, -z)',
2311 '(y, x, -z)', '(-y, -x, -z)',
2312 '(-x+1/2, -y+1/2, -z)', '(x+1/2, y+1/2, -z)',
2313 '(y+1/2, -x+1/2, -z)', '(-y+1/2, x+1/2, -z)',
2314 '(x+1/2, -y+1/2, z)', '(-x+1/2, y+1/2, z)',
2315 '(-y+1/2, -x+1/2, z)', '(y+1/2, x+1/2, z)'))},
2316 '125:2': {'2a': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)')),
2317 '2b': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)')),
2318 '2c': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 0)')),
2319 '2d': (0, ('(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
2320 '4e': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
2321 '(0, 1/2, 0)')),
2322 '4f': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
2323 '(0, 1/2, 1/2)')),
2324 '4g': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, -z)',
2325 '(3/4, 3/4, -z)', '(3/4, 3/4, z)')),
2326 '4h': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z)', '(3/4, 1/4, -z)',
2327 '(1/4, 3/4, -z)')),
2328 '8i': (1, ('(x, x, 0)', '(-x+1/2, -x+1/2, 0)',
2329 '(-x+1/2, x, 0)', '(x, -x+1/2, 0)', '(-x, -x, 0)',
2330 '(x+1/2, x+1/2, 0)', '(x+1/2, -x, 0)',
2331 '(-x, x+1/2, 0)')),
2332 '8j': (1, ('(x, x, 1/2)', '(-x+1/2, -x+1/2, 1/2)',
2333 '(-x+1/2, x, 1/2)', '(x, -x+1/2, 1/2)',
2334 '(-x, -x, 1/2)', '(x+1/2, x+1/2, 1/2)',
2335 '(x+1/2, -x, 1/2)', '(-x, x+1/2, 1/2)')),
2336 '8k': (1, ('(x, 1/4, 0)', '(-x+1/2, 1/4, 0)', '(1/4, x, 0)',
2337 '(1/4, -x+1/2, 0)', '(-x, 3/4, 0)',
2338 '(x+1/2, 3/4, 0)', '(3/4, -x, 0)',
2339 '(3/4, x+1/2, 0)')),
2340 '8l': (1, ('(x, 1/4, 1/2)', '(-x+1/2, 1/4, 1/2)',
2341 '(1/4, x, 1/2)', '(1/4, -x+1/2, 1/2)',
2342 '(-x, 3/4, 1/2)', '(x+1/2, 3/4, 1/2)',
2343 '(3/4, -x, 1/2)', '(3/4, x+1/2, 1/2)')),
2344 '8m': (5, ('(x, -x, z)', '(-x+1/2, x+1/2, z)',
2345 '(x+1/2, x, z)', '(-x, -x+1/2, z)',
2346 '(-x+1/2, -x, -z)', '(x, x+1/2, -z)',
2347 '(-x, x, -z)', '(x+1/2, -x+1/2, -z)')),
2348 '16n': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2349 '(-y+1/2, x, z)', '(y, -x+1/2, z)',
2350 '(-x+1/2, y, -z)', '(x, -y+1/2, -z)',
2351 '(y, x, -z)', '(-y+1/2, -x+1/2, -z)',
2352 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2353 '(y+1/2, -x, -z)', '(-y, x+1/2, -z)',
2354 '(x+1/2, -y, z)', '(-x, y+1/2, z)', '(-y, -x, z)',
2355 '(y+1/2, x+1/2, z)'))},
2356 '126:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2357 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2358 '4c': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 1/2, 1/2)',
2359 '(1/2, 0, 1/2)')),
2360 '4d': (0, ('(1/2, 0, 1/4)', '(0, 1/2, 1/4)', '(1/2, 0, 3/4)',
2361 '(0, 1/2, 3/4)')),
2362 '4e': (4, ('(0, 0, z)', '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
2363 '(1/2, 1/2, z+1/2)')),
2364 '8f': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
2365 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
2366 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)',
2367 '(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)')),
2368 '8g': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 0, -z)',
2369 '(0, 1/2, -z)', '(0, 1/2, -z+1/2)',
2370 '(1/2, 0, -z+1/2)', '(0, 1/2, z+1/2)',
2371 '(1/2, 0, z+1/2)')),
2372 '8h': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x, x, 0)',
2373 '(x, -x, 0)', '(-x+1/2, -x+1/2, 1/2)',
2374 '(x+1/2, x+1/2, 1/2)', '(x+1/2, -x+1/2, 1/2)',
2375 '(-x+1/2, x+1/2, 1/2)')),
2376 '8i': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
2377 '(0, -x, 0)', '(-x+1/2, 1/2, 1/2)',
2378 '(x+1/2, 1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
2379 '(1/2, x+1/2, 1/2)')),
2380 '8j': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 1/2)',
2381 '(0, -x, 1/2)', '(-x+1/2, 1/2, 0)',
2382 '(x+1/2, 1/2, 0)', '(1/2, -x+1/2, 0)',
2383 '(1/2, x+1/2, 0)')),
2384 '16k': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2385 '(y, -x, z)', '(-x, y, -z)', '(x, -y, -z)',
2386 '(y, x, -z)', '(-y, -x, -z)',
2387 '(-x+1/2, -y+1/2, -z+1/2)',
2388 '(x+1/2, y+1/2, -z+1/2)',
2389 '(y+1/2, -x+1/2, -z+1/2)',
2390 '(-y+1/2, x+1/2, -z+1/2)',
2391 '(x+1/2, -y+1/2, z+1/2)',
2392 '(-x+1/2, y+1/2, z+1/2)',
2393 '(-y+1/2, -x+1/2, z+1/2)',
2394 '(y+1/2, x+1/2, z+1/2)'))},
2395 '126:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
2396 '2b': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
2397 '4c': (0, ('(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
2398 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)')),
2399 '4d': (0, ('(1/4, 3/4, 0)', '(3/4, 1/4, 0)',
2400 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
2401 '4e': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, -z+1/2)',
2402 '(3/4, 3/4, -z)', '(3/4, 3/4, z+1/2)')),
2403 '8f': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
2404 '(0, 1/2, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)',
2405 '(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2406 '8g': (4, ('(1/4, 3/4, z)', '(3/4, 1/4, z)',
2407 '(1/4, 3/4, -z+1/2)', '(3/4, 1/4, -z+1/2)',
2408 '(3/4, 1/4, -z)', '(1/4, 3/4, -z)',
2409 '(3/4, 1/4, z+1/2)', '(1/4, 3/4, z+1/2)')),
2410 '8h': (1, ('(x, x, 1/4)', '(-x+1/2, -x+1/2, 1/4)',
2411 '(-x+1/2, x, 1/4)', '(x, -x+1/2, 1/4)',
2412 '(-x, -x, 3/4)', '(x+1/2, x+1/2, 3/4)',
2413 '(x+1/2, -x, 3/4)', '(-x, x+1/2, 3/4)')),
2414 '8i': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
2415 '(1/4, x, 1/4)', '(1/4, -x+1/2, 1/4)',
2416 '(-x, 3/4, 3/4)', '(x+1/2, 3/4, 3/4)',
2417 '(3/4, -x, 3/4)', '(3/4, x+1/2, 3/4)')),
2418 '8j': (1, ('(x, 3/4, 1/4)', '(-x+1/2, 3/4, 1/4)',
2419 '(3/4, x, 1/4)', '(3/4, -x+1/2, 1/4)',
2420 '(-x, 1/4, 3/4)', '(x+1/2, 1/4, 3/4)',
2421 '(1/4, -x, 3/4)', '(1/4, x+1/2, 3/4)')),
2422 '16k': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2423 '(-y+1/2, x, z)', '(y, -x+1/2, z)',
2424 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
2425 '(y, x, -z+1/2)', '(-y+1/2, -x+1/2, -z+1/2)',
2426 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2427 '(y+1/2, -x, -z)', '(-y, x+1/2, -z)',
2428 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)',
2429 '(-y, -x, z+1/2)', '(y+1/2, x+1/2, z+1/2)'))},
2430 '127': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
2431 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2432 '2c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)')),
2433 '2d': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)')),
2434 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, -z)', '(0, 0, -z)',
2435 '(1/2, 1/2, z)')),
2436 '4f': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(1/2, 0, -z)',
2437 '(0, 1/2, -z)')),
2438 '4g': (1, ('(x, x+1/2, 0)', '(-x, -x+1/2, 0)', '(-x+1/2, x, 0)',
2439 '(x+1/2, -x, 0)')),
2440 '4h': (1, ('(x, x+1/2, 1/2)', '(-x, -x+1/2, 1/2)',
2441 '(-x+1/2, x, 1/2)', '(x+1/2, -x, 1/2)')),
2442 '8i': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 0)',
2443 '(y, -x, 0)', '(-x+1/2, y+1/2, 0)',
2444 '(x+1/2, -y+1/2, 0)', '(y+1/2, x+1/2, 0)',
2445 '(-y+1/2, -x+1/2, 0)')),
2446 '8j': (3, ('(x, y, 1/2)', '(-x, -y, 1/2)', '(-y, x, 1/2)',
2447 '(y, -x, 1/2)', '(-x+1/2, y+1/2, 1/2)',
2448 '(x+1/2, -y+1/2, 1/2)', '(y+1/2, x+1/2, 1/2)',
2449 '(-y+1/2, -x+1/2, 1/2)')),
2450 '8k': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)', '(-x+1/2, x, z)',
2451 '(x+1/2, -x, z)', '(-x+1/2, x, -z)',
2452 '(x+1/2, -x, -z)', '(x, x+1/2, -z)',
2453 '(-x, -x+1/2, -z)')),
2454 '16l': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2455 '(y, -x, z)', '(-x+1/2, y+1/2, -z)',
2456 '(x+1/2, -y+1/2, -z)', '(y+1/2, x+1/2, -z)',
2457 '(-y+1/2, -x+1/2, -z)', '(-x, -y, -z)',
2458 '(x, y, -z)', '(y, -x, -z)', '(-y, x, -z)',
2459 '(x+1/2, -y+1/2, z)', '(-x+1/2, y+1/2, z)',
2460 '(-y+1/2, -x+1/2, z)', '(y+1/2, x+1/2, z)'))},
2461 '128': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2462 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2463 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
2464 '(0, 1/2, 1/2)')),
2465 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 1/4)', '(0, 1/2, 3/4)',
2466 '(1/2, 0, 3/4)')),
2467 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, -z+1/2)', '(0, 0, -z)',
2468 '(1/2, 1/2, z+1/2)')),
2469 '8f': (4, ('(0, 1/2, z)', '(1/2, 0, z)', '(1/2, 0, -z+1/2)',
2470 '(0, 1/2, -z+1/2)', '(0, 1/2, -z)', '(1/2, 0, -z)',
2471 '(1/2, 0, z+1/2)', '(0, 1/2, z+1/2)')),
2472 '8g': (1, ('(x, x+1/2, 1/4)', '(-x, -x+1/2, 1/4)',
2473 '(-x+1/2, x, 1/4)', '(x+1/2, -x, 1/4)',
2474 '(-x, -x+1/2, 3/4)', '(x, x+1/2, 3/4)',
2475 '(x+1/2, -x, 3/4)', '(-x+1/2, x, 3/4)')),
2476 '8h': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 0)',
2477 '(y, -x, 0)', '(-x+1/2, y+1/2, 1/2)',
2478 '(x+1/2, -y+1/2, 1/2)', '(y+1/2, x+1/2, 1/2)',
2479 '(-y+1/2, -x+1/2, 1/2)')),
2480 '16i': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z)',
2481 '(y, -x, z)', '(-x+1/2, y+1/2, -z+1/2)',
2482 '(x+1/2, -y+1/2, -z+1/2)', '(y+1/2, x+1/2, -z+1/2)',
2483 '(-y+1/2, -x+1/2, -z+1/2)', '(-x, -y, -z)',
2484 '(x, y, -z)', '(y, -x, -z)', '(-y, x, -z)',
2485 '(x+1/2, -y+1/2, z+1/2)', '(-x+1/2, y+1/2, z+1/2)',
2486 '(-y+1/2, -x+1/2, z+1/2)', '(y+1/2, x+1/2, z+1/2)'
2487 ))},
2488 '129:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)')),
2489 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2490 '2c': (4, ('(0, 1/2, z)', '(1/2, 0, -z)')),
2491 '4d': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(1/4, 3/4, 0)',
2492 '(3/4, 1/4, 0)')),
2493 '4e': (0, ('(1/4, 1/4, 1/2)', '(3/4, 3/4, 1/2)',
2494 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
2495 '4f': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(1/2, 1/2, -z)',
2496 '(0, 0, -z)')),
2497 '8g': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x+1/2, x+1/2, 0)',
2498 '(x+1/2, -x+1/2, 0)', '(-x+1/2, -x+1/2, 0)',
2499 '(x+1/2, x+1/2, 0)', '(x, -x, 0)', '(-x, x, 0)')),
2500 '8h': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)',
2501 '(-x+1/2, x+1/2, 1/2)', '(x+1/2, -x+1/2, 1/2)',
2502 '(-x+1/2, -x+1/2, 1/2)', '(x+1/2, x+1/2, 1/2)',
2503 '(x, -x, 1/2)', '(-x, x, 1/2)')),
2504 '8i': (6, ('(0, y, z)', '(0, -y, z)', '(-y+1/2, 1/2, z)',
2505 '(y+1/2, 1/2, z)', '(1/2, y+1/2, -z)',
2506 '(1/2, -y+1/2, -z)', '(y, 0, -z)', '(-y, 0, -z)')),
2507 '8j': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)',
2508 '(-x, x+1/2, z)', '(x, -x+1/2, z)',
2509 '(-x+1/2, x, -z)', '(x+1/2, -x, -z)',
2510 '(x+1/2, x, -z)', '(-x+1/2, -x, -z)')),
2511 '16k': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z)',
2512 '(y+1/2, -x+1/2, z)', '(-x+1/2, y+1/2, -z)',
2513 '(x+1/2, -y+1/2, -z)', '(y, x, -z)',
2514 '(-y, -x, -z)', '(-x+1/2, -y+1/2, -z)',
2515 '(x+1/2, y+1/2, -z)', '(y, -x, -z)',
2516 '(-y, x, -z)', '(x, -y, z)', '(-x, y, z)',
2517 '(-y+1/2, -x+1/2, z)', '(y+1/2, x+1/2, z)'))},
2518 '129:2': {'2a': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 0)')),
2519 '2b': (0, ('(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)')),
2520 '2c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, -z)')),
2521 '4d': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
2522 '(0, 1/2, 0)')),
2523 '4e': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
2524 '(0, 1/2, 1/2)')),
2525 '4f': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z)', '(1/4, 3/4, -z)',
2526 '(3/4, 1/4, -z)')),
2527 '8g': (1, ('(x, -x, 0)', '(-x+1/2, x+1/2, 0)',
2528 '(x+1/2, x, 0)', '(-x, -x+1/2, 0)', '(-x, x, 0)',
2529 '(x+1/2, -x+1/2, 0)', '(-x+1/2, -x, 0)',
2530 '(x, x+1/2, 0)')),
2531 '8h': (1, ('(x, -x, 1/2)', '(-x+1/2, x+1/2, 1/2)',
2532 '(x+1/2, x, 1/2)', '(-x, -x+1/2, 1/2)',
2533 '(-x, x, 1/2)', '(x+1/2, -x+1/2, 1/2)',
2534 '(-x+1/2, -x, 1/2)', '(x, x+1/2, 1/2)')),
2535 '8i': (6, ('(1/4, y, z)', '(1/4, -y+1/2, z)',
2536 '(-y+1/2, 1/4, z)', '(y, 1/4, z)',
2537 '(3/4, y+1/2, -z)', '(3/4, -y, -z)',
2538 '(y+1/2, 3/4, -z)', '(-y, 3/4, -z)')),
2539 '8j': (5, ('(x, x, z)', '(-x+1/2, -x+1/2, z)',
2540 '(-x+1/2, x, z)', '(x, -x+1/2, z)',
2541 '(-x, x+1/2, -z)', '(x+1/2, -x, -z)',
2542 '(x+1/2, x+1/2, -z)', '(-x, -x, -z)')),
2543 '16k': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2544 '(-y+1/2, x, z)', '(y, -x+1/2, z)',
2545 '(-x, y+1/2, -z)', '(x+1/2, -y, -z)',
2546 '(y+1/2, x+1/2, -z)', '(-y, -x, -z)',
2547 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2548 '(y+1/2, -x, -z)', '(-y, x+1/2, -z)',
2549 '(x, -y+1/2, z)', '(-x+1/2, y, z)',
2550 '(-y+1/2, -x+1/2, z)', '(y, x, z)'))},
2551 '130:1': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 1/4)',
2552 '(1/2, 1/2, 3/4)', '(0, 0, 3/4)')),
2553 '4b': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)',
2554 '(0, 0, 1/2)')),
2555 '4c': (4, ('(0, 1/2, z)', '(1/2, 0, -z+1/2)', '(1/2, 0, -z)',
2556 '(0, 1/2, z+1/2)')),
2557 '8d': (0, ('(1/4, 1/4, 0)', '(3/4, 3/4, 0)', '(1/4, 3/4, 0)',
2558 '(3/4, 1/4, 0)', '(1/4, 3/4, 1/2)',
2559 '(3/4, 1/4, 1/2)', '(1/4, 1/4, 1/2)',
2560 '(3/4, 3/4, 1/2)')),
2561 '8e': (4, ('(0, 0, z)', '(1/2, 1/2, z)', '(1/2, 1/2, -z+1/2)',
2562 '(0, 0, -z+1/2)', '(1/2, 1/2, -z)', '(0, 0, -z)',
2563 '(0, 0, z+1/2)', '(1/2, 1/2, z+1/2)')),
2564 '8f': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)',
2565 '(-x+1/2, x+1/2, 1/4)', '(x+1/2, -x+1/2, 1/4)',
2566 '(-x+1/2, -x+1/2, 3/4)', '(x+1/2, x+1/2, 3/4)',
2567 '(x, -x, 3/4)', '(-x, x, 3/4)')),
2568 '16g': (7, ('(x, y, z)', '(-x, -y, z)', '(-y+1/2, x+1/2, z)',
2569 '(y+1/2, -x+1/2, z)', '(-x+1/2, y+1/2, -z+1/2)',
2570 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z+1/2)',
2571 '(-y, -x, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
2572 '(x+1/2, y+1/2, -z)', '(y, -x, -z)',
2573 '(-y, x, -z)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
2574 '(-y+1/2, -x+1/2, z+1/2)',
2575 '(y+1/2, x+1/2, z+1/2)'))},
2576 '130:2': {'4a': (0, ('(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
2577 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)')),
2578 '4b': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 0)',
2579 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)')),
2580 '4c': (4, ('(1/4, 1/4, z)', '(3/4, 3/4, -z+1/2)',
2581 '(3/4, 3/4, -z)', '(1/4, 1/4, z+1/2)')),
2582 '8d': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
2583 '(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
2584 '(1/2, 1/2, 1/2)', '(0, 0, 1/2)')),
2585 '8e': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z)',
2586 '(1/4, 3/4, -z+1/2)', '(3/4, 1/4, -z+1/2)',
2587 '(1/4, 3/4, -z)', '(3/4, 1/4, -z)',
2588 '(3/4, 1/4, z+1/2)', '(1/4, 3/4, z+1/2)')),
2589 '8f': (1, ('(x, -x, 1/4)', '(-x+1/2, x+1/2, 1/4)',
2590 '(x+1/2, x, 1/4)', '(-x, -x+1/2, 1/4)',
2591 '(-x, x, 3/4)', '(x+1/2, -x+1/2, 3/4)',
2592 '(-x+1/2, -x, 3/4)', '(x, x+1/2, 3/4)')),
2593 '16g': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2594 '(-y+1/2, x, z)', '(y, -x+1/2, z)',
2595 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y, -z+1/2)',
2596 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z+1/2)',
2597 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2598 '(y+1/2, -x, -z)', '(-y, x+1/2, -z)',
2599 '(x, -y+1/2, z+1/2)', '(-x+1/2, y, z+1/2)',
2600 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z+1/2)'))},
2601 '131': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
2602 '2b': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
2603 '2c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)')),
2604 '2d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)')),
2605 '2e': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
2606 '2f': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
2607 '4g': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
2608 '(0, 0, -z+1/2)')),
2609 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)',
2610 '(1/2, 1/2, -z)', '(1/2, 1/2, -z+1/2)')),
2611 '4i': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, -z)',
2612 '(1/2, 0, -z+1/2)')),
2613 '4j': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 1/2)',
2614 '(0, -x, 1/2)')),
2615 '4k': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 0)',
2616 '(1/2, -x, 0)')),
2617 '4l': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(0, x, 0)',
2618 '(0, -x, 0)')),
2619 '4m': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(1/2, x, 1/2)',
2620 '(1/2, -x, 1/2)')),
2621 '8n': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)', '(-x, x, 3/4)',
2622 '(x, -x, 3/4)', '(-x, -x, 3/4)', '(x, x, 3/4)',
2623 '(x, -x, 1/4)', '(-x, x, 1/4)')),
2624 '8o': (6, ('(0, y, z)', '(0, -y, z)', '(-y, 0, z+1/2)',
2625 '(y, 0, z+1/2)', '(0, y, -z)', '(0, -y, -z)',
2626 '(y, 0, -z+1/2)', '(-y, 0, -z+1/2)')),
2627 '8p': (6, ('(1/2, y, z)', '(1/2, -y, z)', '(-y, 1/2, z+1/2)',
2628 '(y, 1/2, z+1/2)', '(1/2, y, -z)', '(1/2, -y, -z)',
2629 '(y, 1/2, -z+1/2)', '(-y, 1/2, -z+1/2)')),
2630 '8q': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 1/2)',
2631 '(y, -x, 1/2)', '(-x, y, 0)', '(x, -y, 0)',
2632 '(y, x, 1/2)', '(-y, -x, 1/2)')),
2633 '16r': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
2634 '(y, -x, z+1/2)', '(-x, y, -z)', '(x, -y, -z)',
2635 '(y, x, -z+1/2)', '(-y, -x, -z+1/2)',
2636 '(-x, -y, -z)', '(x, y, -z)', '(y, -x, -z+1/2)',
2637 '(-y, x, -z+1/2)', '(x, -y, z)', '(-x, y, z)',
2638 '(-y, -x, z+1/2)', '(y, x, z+1/2)'))},
2639 '132': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
2640 '2b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
2641 '2c': (0, ('(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)')),
2642 '2d': (0, ('(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)')),
2643 '4e': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(0, 1/2, 3/4)',
2644 '(1/2, 0, 1/4)')),
2645 '4f': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)',
2646 '(1/2, 0, 0)')),
2647 '4g': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z+1/2)',
2648 '(0, 0, -z)')),
2649 '4h': (4, ('(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)',
2650 '(1/2, 1/2, -z+1/2)', '(1/2, 1/2, -z)')),
2651 '4i': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x, x, 1/2)',
2652 '(x, -x, 1/2)')),
2653 '4j': (1, ('(x, x, 1/2)', '(-x, -x, 1/2)', '(-x, x, 0)',
2654 '(x, -x, 0)')),
2655 '8k': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(0, 1/2, -z+1/2)',
2656 '(1/2, 0, -z)', '(0, 1/2, -z)', '(1/2, 0, -z+1/2)',
2657 '(0, 1/2, z+1/2)', '(1/2, 0, z)')),
2658 '8l': (1, ('(x, 0, 1/4)', '(-x, 0, 1/4)', '(0, x, 3/4)',
2659 '(0, -x, 3/4)', '(-x, 0, 3/4)', '(x, 0, 3/4)',
2660 '(0, -x, 1/4)', '(0, x, 1/4)')),
2661 '8m': (1, ('(x, 1/2, 1/4)', '(-x, 1/2, 1/4)', '(1/2, x, 3/4)',
2662 '(1/2, -x, 3/4)', '(-x, 1/2, 3/4)', '(x, 1/2, 3/4)',
2663 '(1/2, -x, 1/4)', '(1/2, x, 1/4)')),
2664 '8n': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 1/2)',
2665 '(y, -x, 1/2)', '(-x, y, 1/2)', '(x, -y, 1/2)',
2666 '(y, x, 0)', '(-y, -x, 0)')),
2667 '8o': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, z+1/2)',
2668 '(x, -x, z+1/2)', '(-x, x, -z+1/2)',
2669 '(x, -x, -z+1/2)', '(x, x, -z)', '(-x, -x, -z)')),
2670 '16p': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
2671 '(y, -x, z+1/2)', '(-x, y, -z+1/2)',
2672 '(x, -y, -z+1/2)', '(y, x, -z)', '(-y, -x, -z)',
2673 '(-x, -y, -z)', '(x, y, -z)', '(y, -x, -z+1/2)',
2674 '(-y, x, -z+1/2)', '(x, -y, z+1/2)',
2675 '(-x, y, z+1/2)', '(-y, -x, z)', '(y, x, z)'))},
2676 '133:1': {'4a': (0, ('(0, 1/2, 1/4)', '(0, 1/2, 3/4)', '(1/2, 0, 1/4)',
2677 '(1/2, 0, 3/4)')),
2678 '4b': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)',
2679 '(1/2, 1/2, 1/4)', '(0, 0, 3/4)')),
2680 '4c': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
2681 '(1/2, 0, 0)')),
2682 '4d': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 0, 1/2)',
2683 '(1/2, 1/2, 0)')),
2684 '8e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
2685 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
2686 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
2687 '(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)')),
2688 '8f': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)',
2689 '(0, 1/2, -z+1/2)', '(0, 1/2, -z)',
2690 '(1/2, 0, -z+1/2)', '(1/2, 0, -z)', '(1/2, 0, z)',
2691 '(1/2, 0, z+1/2)')),
2692 '8g': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z+1/2)',
2693 '(1/2, 1/2, -z)', '(1/2, 1/2, -z+1/2)',
2694 '(0, 0, -z)', '(1/2, 1/2, z)', '(0, 0, z+1/2)')),
2695 '8h': (1, ('(x, 0, 1/4)', '(-x, 0, 1/4)', '(1/2, x+1/2, 3/4)',
2696 '(1/2, -x+1/2, 3/4)', '(-x+1/2, 1/2, 1/4)',
2697 '(x+1/2, 1/2, 1/4)', '(0, -x, 3/4)', '(0, x, 3/4)'
2698 )),
2699 '8i': (1, ('(x, 0, 3/4)', '(-x, 0, 3/4)', '(1/2, x+1/2, 1/4)',
2700 '(1/2, -x+1/2, 1/4)', '(-x+1/2, 1/2, 3/4)',
2701 '(x+1/2, 1/2, 3/4)', '(0, -x, 1/4)', '(0, x, 1/4)'
2702 )),
2703 '8j': (1, ('(x, x+1/2, 0)', '(-x, -x+1/2, 0)',
2704 '(-x, x+1/2, 1/2)', '(x, -x+1/2, 1/2)',
2705 '(-x+1/2, -x, 1/2)', '(x+1/2, x, 1/2)',
2706 '(x+1/2, -x, 0)', '(-x+1/2, x, 0)')),
2707 '16k': (7, ('(x, y, z)', '(-x, -y, z)',
2708 '(-y+1/2, x+1/2, z+1/2)',
2709 '(y+1/2, -x+1/2, z+1/2)', '(-x, y, -z+1/2)',
2710 '(x, -y, -z+1/2)', '(y+1/2, x+1/2, -z)',
2711 '(-y+1/2, -x+1/2, -z)',
2712 '(-x+1/2, -y+1/2, -z+1/2)',
2713 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
2714 '(-y, x, -z)', '(x+1/2, -y+1/2, z)',
2715 '(-x+1/2, y+1/2, z)', '(-y, -x, z+1/2)',
2716 '(y, x, z+1/2)'))},
2717 '133:2': {'4a': (0, ('(1/4, 1/4, 0)', '(1/4, 1/4, 1/2)',
2718 '(3/4, 3/4, 0)', '(3/4, 3/4, 1/2)')),
2719 '4b': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 1/2)',
2720 '(1/4, 3/4, 0)', '(3/4, 1/4, 1/2)')),
2721 '4c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 1/4, 3/4)',
2722 '(3/4, 3/4, 3/4)', '(3/4, 3/4, 1/4)')),
2723 '4d': (0, ('(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
2724 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)')),
2725 '8e': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
2726 '(0, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
2727 '(0, 0, 1/2)', '(1/2, 1/2, 1/2)')),
2728 '8f': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, z+1/2)',
2729 '(1/4, 1/4, -z)', '(1/4, 1/4, -z+1/2)',
2730 '(3/4, 3/4, -z)', '(3/4, 3/4, -z+1/2)',
2731 '(3/4, 3/4, z)', '(3/4, 3/4, z+1/2)')),
2732 '8g': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
2733 '(3/4, 1/4, -z)', '(1/4, 3/4, -z+1/2)',
2734 '(1/4, 3/4, -z)', '(3/4, 1/4, -z+1/2)',
2735 '(1/4, 3/4, z)', '(3/4, 1/4, z+1/2)')),
2736 '8h': (1, ('(x, 1/4, 0)', '(-x+1/2, 1/4, 0)', '(1/4, x, 1/2)',
2737 '(1/4, -x+1/2, 1/2)', '(-x, 3/4, 0)',
2738 '(x+1/2, 3/4, 0)', '(3/4, -x, 1/2)',
2739 '(3/4, x+1/2, 1/2)')),
2740 '8i': (1, ('(x, 1/4, 1/2)', '(-x+1/2, 1/4, 1/2)',
2741 '(1/4, x, 0)', '(1/4, -x+1/2, 0)',
2742 '(-x, 3/4, 1/2)', '(x+1/2, 3/4, 1/2)',
2743 '(3/4, -x, 0)', '(3/4, x+1/2, 0)')),
2744 '8j': (1, ('(x, x, 1/4)', '(-x+1/2, -x+1/2, 1/4)',
2745 '(-x+1/2, x, 3/4)', '(x, -x+1/2, 3/4)',
2746 '(-x, -x, 3/4)', '(x+1/2, x+1/2, 3/4)',
2747 '(x+1/2, -x, 1/4)', '(-x, x+1/2, 1/4)')),
2748 '16k': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2749 '(-y+1/2, x, z+1/2)', '(y, -x+1/2, z+1/2)',
2750 '(-x+1/2, y, -z)', '(x, -y+1/2, -z)',
2751 '(y, x, -z+1/2)', '(-y+1/2, -x+1/2, -z+1/2)',
2752 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2753 '(y+1/2, -x, -z+1/2)', '(-y, x+1/2, -z+1/2)',
2754 '(x+1/2, -y, z)', '(-x, y+1/2, z)',
2755 '(-y, -x, z+1/2)', '(y+1/2, x+1/2, z+1/2)'))},
2756 '134:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2757 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2758 '4c': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
2759 '(1/2, 0, 0)')),
2760 '4d': (0, ('(0, 1/2, 1/4)', '(0, 1/2, 3/4)', '(1/2, 0, 1/4)',
2761 '(1/2, 0, 3/4)')),
2762 '4e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
2763 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)')),
2764 '4f': (0, ('(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
2765 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)')),
2766 '4g': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
2767 '(1/2, 1/2, -z+1/2)')),
2768 '8h': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)', '(0, 1/2, -z)',
2769 '(0, 1/2, -z+1/2)', '(1/2, 0, -z+1/2)',
2770 '(1/2, 0, -z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)')),
2771 '8i': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(1/2, x+1/2, 1/2)',
2772 '(1/2, -x+1/2, 1/2)', '(-x+1/2, 1/2, 1/2)',
2773 '(x+1/2, 1/2, 1/2)', '(0, -x, 0)', '(0, x, 0)')),
2774 '8j': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x+1/2, 0)',
2775 '(1/2, -x+1/2, 0)', '(-x+1/2, 1/2, 0)',
2776 '(x+1/2, 1/2, 0)', '(0, -x, 1/2)', '(0, x, 1/2)')),
2777 '8k': (1, ('(x, x+1/2, 1/4)', '(-x, -x+1/2, 1/4)',
2778 '(-x, x+1/2, 3/4)', '(x, -x+1/2, 3/4)',
2779 '(-x+1/2, -x, 1/4)', '(x+1/2, x, 1/4)',
2780 '(x+1/2, -x, 3/4)', '(-x+1/2, x, 3/4)')),
2781 '8l': (1, ('(x, x+1/2, 3/4)', '(-x, -x+1/2, 3/4)',
2782 '(-x, x+1/2, 1/4)', '(x, -x+1/2, 1/4)',
2783 '(-x+1/2, -x, 3/4)', '(x+1/2, x, 3/4)',
2784 '(x+1/2, -x, 1/4)', '(-x+1/2, x, 1/4)')),
2785 '8m': (5, ('(x, x, z)', '(-x, -x, z)',
2786 '(-x+1/2, x+1/2, z+1/2)', '(x+1/2, -x+1/2, z+1/2)',
2787 '(-x, x, -z)', '(x, -x, -z)',
2788 '(x+1/2, x+1/2, -z+1/2)',
2789 '(-x+1/2, -x+1/2, -z+1/2)')),
2790 '16n': (7, ('(x, y, z)', '(-x, -y, z)',
2791 '(-y+1/2, x+1/2, z+1/2)',
2792 '(y+1/2, -x+1/2, z+1/2)', '(-x, y, -z)',
2793 '(x, -y, -z)', '(y+1/2, x+1/2, -z+1/2)',
2794 '(-y+1/2, -x+1/2, -z+1/2)',
2795 '(-x+1/2, -y+1/2, -z+1/2)',
2796 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
2797 '(-y, x, -z)', '(x+1/2, -y+1/2, z+1/2)',
2798 '(-x+1/2, y+1/2, z+1/2)', '(-y, -x, z)',
2799 '(y, x, z)'))},
2800 '134:2': {'2a': (0, ('(1/4, 3/4, 1/4)', '(3/4, 1/4, 3/4)')),
2801 '2b': (0, ('(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)')),
2802 '4c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 1/4, 3/4)',
2803 '(3/4, 3/4, 3/4)', '(3/4, 3/4, 1/4)')),
2804 '4d': (0, ('(1/4, 1/4, 0)', '(1/4, 1/4, 1/2)',
2805 '(3/4, 3/4, 0)', '(3/4, 3/4, 1/2)')),
2806 '4e': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 0)',
2807 '(0, 1/2, 0)')),
2808 '4f': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
2809 '(0, 1/2, 1/2)')),
2810 '4g': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
2811 '(3/4, 1/4, -z+1/2)', '(1/4, 3/4, -z)')),
2812 '8h': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, z+1/2)',
2813 '(1/4, 1/4, -z+1/2)', '(1/4, 1/4, -z)',
2814 '(3/4, 3/4, -z)', '(3/4, 3/4, -z+1/2)',
2815 '(3/4, 3/4, z+1/2)', '(3/4, 3/4, z)')),
2816 '8i': (1, ('(x, 1/4, 3/4)', '(-x+1/2, 1/4, 3/4)',
2817 '(1/4, x, 1/4)', '(1/4, -x+1/2, 1/4)',
2818 '(-x, 3/4, 1/4)', '(x+1/2, 3/4, 1/4)',
2819 '(3/4, -x, 3/4)', '(3/4, x+1/2, 3/4)')),
2820 '8j': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
2821 '(1/4, x, 3/4)', '(1/4, -x+1/2, 3/4)',
2822 '(-x, 3/4, 3/4)', '(x+1/2, 3/4, 3/4)',
2823 '(3/4, -x, 1/4)', '(3/4, x+1/2, 1/4)')),
2824 '8k': (1, ('(x, x, 0)', '(-x+1/2, -x+1/2, 0)',
2825 '(-x+1/2, x, 1/2)', '(x, -x+1/2, 1/2)',
2826 '(-x, -x, 0)', '(x+1/2, x+1/2, 0)',
2827 '(x+1/2, -x, 1/2)', '(-x, x+1/2, 1/2)')),
2828 '8l': (1, ('(x, x, 1/2)', '(-x+1/2, -x+1/2, 1/2)',
2829 '(-x+1/2, x, 0)', '(x, -x+1/2, 0)',
2830 '(-x, -x, 1/2)', '(x+1/2, x+1/2, 1/2)',
2831 '(x+1/2, -x, 0)', '(-x, x+1/2, 0)')),
2832 '8m': (5, ('(x, -x, z)', '(-x+1/2, x+1/2, z)',
2833 '(x+1/2, x, z+1/2)', '(-x, -x+1/2, z+1/2)',
2834 '(-x+1/2, -x, -z+1/2)', '(x, x+1/2, -z+1/2)',
2835 '(-x, x, -z)', '(x+1/2, -x+1/2, -z)')),
2836 '16n': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2837 '(-y+1/2, x, z+1/2)', '(y, -x+1/2, z+1/2)',
2838 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
2839 '(y, x, -z)', '(-y+1/2, -x+1/2, -z)',
2840 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2841 '(y+1/2, -x, -z+1/2)', '(-y, x+1/2, -z+1/2)',
2842 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)',
2843 '(-y, -x, z)', '(y+1/2, x+1/2, z)'))},
2844 '135': {'4a': (0, ('(0, 0, 0)', '(0, 0, 1/2)', '(1/2, 1/2, 0)',
2845 '(1/2, 1/2, 1/2)')),
2846 '4b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)', '(1/2, 1/2, 3/4)',
2847 '(1/2, 1/2, 1/4)')),
2848 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
2849 '(0, 1/2, 1/2)')),
2850 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(0, 1/2, 3/4)',
2851 '(1/2, 0, 1/4)')),
2852 '8e': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(1/2, 1/2, -z)',
2853 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)', '(0, 0, -z+1/2)',
2854 '(1/2, 1/2, z)', '(1/2, 1/2, z+1/2)')),
2855 '8f': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, -z)',
2856 '(0, 1/2, -z+1/2)', '(0, 1/2, -z)',
2857 '(1/2, 0, -z+1/2)', '(1/2, 0, z)', '(0, 1/2, z+1/2)'
2858 )),
2859 '8g': (1, ('(x, x+1/2, 1/4)', '(-x, -x+1/2, 1/4)',
2860 '(-x+1/2, x, 3/4)', '(x+1/2, -x, 3/4)',
2861 '(-x, -x+1/2, 3/4)', '(x, x+1/2, 3/4)',
2862 '(x+1/2, -x, 1/4)', '(-x+1/2, x, 1/4)')),
2863 '8h': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y, x, 1/2)',
2864 '(y, -x, 1/2)', '(-x+1/2, y+1/2, 0)',
2865 '(x+1/2, -y+1/2, 0)', '(y+1/2, x+1/2, 1/2)',
2866 '(-y+1/2, -x+1/2, 1/2)')),
2867 '16i': (7, ('(x, y, z)', '(-x, -y, z)', '(-y, x, z+1/2)',
2868 '(y, -x, z+1/2)', '(-x+1/2, y+1/2, -z)',
2869 '(x+1/2, -y+1/2, -z)', '(y+1/2, x+1/2, -z+1/2)',
2870 '(-y+1/2, -x+1/2, -z+1/2)', '(-x, -y, -z)',
2871 '(x, y, -z)', '(y, -x, -z+1/2)', '(-y, x, -z+1/2)',
2872 '(x+1/2, -y+1/2, z)', '(-x+1/2, y+1/2, z)',
2873 '(-y+1/2, -x+1/2, z+1/2)', '(y+1/2, x+1/2, z+1/2)'
2874 ))},
2875 '136': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2876 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2877 '4c': (0, ('(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
2878 '(1/2, 0, 0)')),
2879 '4d': (0, ('(0, 1/2, 1/4)', '(0, 1/2, 3/4)', '(1/2, 0, 1/4)',
2880 '(1/2, 0, 3/4)')),
2881 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
2882 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)')),
2883 '4f': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x+1/2, x+1/2, 1/2)',
2884 '(x+1/2, -x+1/2, 1/2)')),
2885 '4g': (1, ('(x, -x, 0)', '(-x, x, 0)', '(x+1/2, x+1/2, 1/2)',
2886 '(-x+1/2, -x+1/2, 1/2)')),
2887 '8h': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)', '(1/2, 0, -z+1/2)',
2888 '(1/2, 0, -z)', '(0, 1/2, -z)', '(0, 1/2, -z+1/2)',
2889 '(1/2, 0, z+1/2)', '(1/2, 0, z)')),
2890 '8i': (3, ('(x, y, 0)', '(-x, -y, 0)', '(-y+1/2, x+1/2, 1/2)',
2891 '(y+1/2, -x+1/2, 1/2)', '(-x+1/2, y+1/2, 1/2)',
2892 '(x+1/2, -y+1/2, 1/2)', '(y, x, 0)', '(-y, -x, 0)')),
2893 '8j': (5, ('(x, x, z)', '(-x, -x, z)', '(-x+1/2, x+1/2, z+1/2)',
2894 '(x+1/2, -x+1/2, z+1/2)', '(-x+1/2, x+1/2, -z+1/2)',
2895 '(x+1/2, -x+1/2, -z+1/2)', '(x, x, -z)',
2896 '(-x, -x, -z)')),
2897 '16k': (7, ('(x, y, z)', '(-x, -y, z)',
2898 '(-y+1/2, x+1/2, z+1/2)', '(y+1/2, -x+1/2, z+1/2)',
2899 '(-x+1/2, y+1/2, -z+1/2)',
2900 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z)',
2901 '(-y, -x, -z)', '(-x, -y, -z)', '(x, y, -z)',
2902 '(y+1/2, -x+1/2, -z+1/2)',
2903 '(-y+1/2, x+1/2, -z+1/2)', '(x+1/2, -y+1/2, z+1/2)',
2904 '(-x+1/2, y+1/2, z+1/2)', '(-y, -x, z)',
2905 '(y, x, z)'))},
2906 '137:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
2907 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
2908 '4c': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
2909 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)')),
2910 '4d': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)',
2911 '(1/2, 0, -z+1/2)', '(1/2, 0, -z)')),
2912 '8e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
2913 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
2914 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)',
2915 '(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)')),
2916 '8f': (1, ('(x, x, 0)', '(-x, -x, 0)', '(-x+1/2, x+1/2, 1/2)',
2917 '(x+1/2, -x+1/2, 1/2)', '(-x+1/2, -x+1/2, 1/2)',
2918 '(x+1/2, x+1/2, 1/2)', '(x, -x, 0)', '(-x, x, 0)'
2919 )),
2920 '8g': (6, ('(0, y, z)', '(0, -y, z)', '(-y+1/2, 1/2, z+1/2)',
2921 '(y+1/2, 1/2, z+1/2)', '(1/2, y+1/2, -z+1/2)',
2922 '(1/2, -y+1/2, -z+1/2)', '(y, 0, -z)',
2923 '(-y, 0, -z)')),
2924 '16h': (7, ('(x, y, z)', '(-x, -y, z)',
2925 '(-y+1/2, x+1/2, z+1/2)',
2926 '(y+1/2, -x+1/2, z+1/2)',
2927 '(-x+1/2, y+1/2, -z+1/2)',
2928 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z)',
2929 '(-y, -x, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
2930 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
2931 '(-y, x, -z)', '(x, -y, z)', '(-x, y, z)',
2932 '(-y+1/2, -x+1/2, z+1/2)',
2933 '(y+1/2, x+1/2, z+1/2)'))},
2934 '137:2': {'2a': (0, ('(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)')),
2935 '2b': (0, ('(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)')),
2936 '4c': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
2937 '(1/4, 3/4, -z)', '(3/4, 1/4, -z+1/2)')),
2938 '4d': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, z+1/2)',
2939 '(3/4, 3/4, -z)', '(3/4, 3/4, -z+1/2)')),
2940 '8e': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
2941 '(0, 1/2, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 0)',
2942 '(1/2, 1/2, 1/2)', '(0, 0, 1/2)')),
2943 '8f': (1, ('(x, -x, 1/4)', '(-x+1/2, x+1/2, 1/4)',
2944 '(x+1/2, x, 3/4)', '(-x, -x+1/2, 3/4)',
2945 '(-x, x, 3/4)', '(x+1/2, -x+1/2, 3/4)',
2946 '(-x+1/2, -x, 1/4)', '(x, x+1/2, 1/4)')),
2947 '8g': (6, ('(1/4, y, z)', '(1/4, -y+1/2, z)',
2948 '(-y+1/2, 1/4, z+1/2)', '(y, 1/4, z+1/2)',
2949 '(3/4, y+1/2, -z)', '(3/4, -y, -z)',
2950 '(y+1/2, 3/4, -z+1/2)', '(-y, 3/4, -z+1/2)')),
2951 '16h': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
2952 '(-y+1/2, x, z+1/2)', '(y, -x+1/2, z+1/2)',
2953 '(-x, y+1/2, -z)', '(x+1/2, -y, -z)',
2954 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z+1/2)',
2955 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
2956 '(y+1/2, -x, -z+1/2)', '(-y, x+1/2, -z+1/2)',
2957 '(x, -y+1/2, z)', '(-x+1/2, y, z)',
2958 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z+1/2)'))},
2959 '138:1': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)',
2960 '(1/2, 1/2, 1/4)', '(0, 0, 3/4)')),
2961 '4b': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 1/2, 0)',
2962 '(0, 0, 1/2)')),
2963 '4c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
2964 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)')),
2965 '4d': (0, ('(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)',
2966 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
2967 '4e': (4, ('(0, 1/2, z)', '(0, 1/2, z+1/2)', '(1/2, 0, -z)',
2968 '(1/2, 0, -z+1/2)')),
2969 '8f': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(1/2, 1/2, -z)',
2970 '(0, 0, -z+1/2)', '(1/2, 1/2, -z+1/2)',
2971 '(0, 0, -z)', '(0, 0, z+1/2)', '(1/2, 1/2, z)')),
2972 '8g': (1, ('(x, x, 1/4)', '(-x, -x, 1/4)',
2973 '(-x+1/2, x+1/2, 3/4)', '(x+1/2, -x+1/2, 3/4)',
2974 '(-x+1/2, -x+1/2, 1/4)', '(x+1/2, x+1/2, 1/4)',
2975 '(x, -x, 3/4)', '(-x, x, 3/4)')),
2976 '8h': (1, ('(x, x, 3/4)', '(-x, -x, 3/4)',
2977 '(-x+1/2, x+1/2, 1/4)', '(x+1/2, -x+1/2, 1/4)',
2978 '(-x+1/2, -x+1/2, 3/4)', '(x+1/2, x+1/2, 3/4)',
2979 '(x, -x, 1/4)', '(-x, x, 1/4)')),
2980 '8i': (5, ('(x, x+1/2, z)', '(-x, -x+1/2, z)',
2981 '(-x, x+1/2, z+1/2)', '(x, -x+1/2, z+1/2)',
2982 '(-x+1/2, x, -z)', '(x+1/2, -x, -z)',
2983 '(x+1/2, x, -z+1/2)', '(-x+1/2, -x, -z+1/2)')),
2984 '16j': (7, ('(x, y, z)', '(-x, -y, z)',
2985 '(-y+1/2, x+1/2, z+1/2)',
2986 '(y+1/2, -x+1/2, z+1/2)', '(-x+1/2, y+1/2, -z)',
2987 '(x+1/2, -y+1/2, -z)', '(y, x, -z+1/2)',
2988 '(-y, -x, -z+1/2)', '(-x+1/2, -y+1/2, -z+1/2)',
2989 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
2990 '(-y, x, -z)', '(x, -y, z+1/2)', '(-x, y, z+1/2)',
2991 '(-y+1/2, -x+1/2, z)', '(y+1/2, x+1/2, z)'))},
2992 '138:2': {'4a': (0, ('(3/4, 1/4, 0)', '(1/4, 3/4, 1/2)',
2993 '(1/4, 3/4, 0)', '(3/4, 1/4, 1/2)')),
2994 '4b': (0, ('(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
2995 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
2996 '4c': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 1/2)', '(1/2, 0, 0)',
2997 '(0, 1/2, 0)')),
2998 '4d': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
2999 '(0, 1/2, 1/2)')),
3000 '4e': (4, ('(1/4, 1/4, z)', '(1/4, 1/4, z+1/2)',
3001 '(3/4, 3/4, -z+1/2)', '(3/4, 3/4, -z)')),
3002 '8f': (4, ('(3/4, 1/4, z)', '(1/4, 3/4, z+1/2)',
3003 '(1/4, 3/4, -z+1/2)', '(3/4, 1/4, -z)',
3004 '(1/4, 3/4, -z)', '(3/4, 1/4, -z+1/2)',
3005 '(3/4, 1/4, z+1/2)', '(1/4, 3/4, z)')),
3006 '8g': (1, ('(x, -x, 1/2)', '(-x+1/2, x+1/2, 1/2)',
3007 '(x+1/2, x, 0)', '(-x, -x+1/2, 0)', '(-x, x, 1/2)',
3008 '(x+1/2, -x+1/2, 1/2)', '(-x+1/2, -x, 0)',
3009 '(x, x+1/2, 0)')),
3010 '8h': (1, ('(x, -x, 0)', '(-x+1/2, x+1/2, 0)',
3011 '(x+1/2, x, 1/2)', '(-x, -x+1/2, 1/2)',
3012 '(-x, x, 0)', '(x+1/2, -x+1/2, 0)',
3013 '(-x+1/2, -x, 1/2)', '(x, x+1/2, 1/2)')),
3014 '8i': (5, ('(x, x, z)', '(-x+1/2, -x+1/2, z)',
3015 '(-x+1/2, x, z+1/2)', '(x, -x+1/2, z+1/2)',
3016 '(-x, x+1/2, -z+1/2)', '(x+1/2, -x, -z+1/2)',
3017 '(x+1/2, x+1/2, -z)', '(-x, -x, -z)')),
3018 '16j': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
3019 '(-y+1/2, x, z+1/2)', '(y, -x+1/2, z+1/2)',
3020 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y, -z+1/2)',
3021 '(y+1/2, x+1/2, -z)', '(-y, -x, -z)',
3022 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
3023 '(y+1/2, -x, -z+1/2)', '(-y, x+1/2, -z+1/2)',
3024 '(x, -y+1/2, z+1/2)', '(-x+1/2, y, z+1/2)',
3025 '(-y+1/2, -x+1/2, z)', '(y, x, z)'))},
3026 '139': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
3027 '2b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)')),
3028 '4c': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
3029 '(0, 1/2, 1/2)')),
3030 '4d': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(1/2, 0, 1/4)',
3031 '(0, 1/2, 3/4)')),
3032 '4e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z)',
3033 '(1/2, 1/2, -z+1/2)')),
3034 '8f': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
3035 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
3036 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
3037 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 3/4)')),
3038 '8g': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
3039 '(0, 1/2, z+1/2)', '(0, 1/2, -z)',
3040 '(1/2, 0, -z+1/2)', '(1/2, 0, -z)',
3041 '(0, 1/2, -z+1/2)')),
3042 '8h': (1, ('(x, x, 0)', '(x+1/2, x+1/2, 1/2)', '(-x, -x, 0)',
3043 '(-x+1/2, -x+1/2, 1/2)', '(-x, x, 0)',
3044 '(-x+1/2, x+1/2, 1/2)', '(x, -x, 0)',
3045 '(x+1/2, -x+1/2, 1/2)')),
3046 '8i': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
3047 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
3048 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
3049 '(1/2, -x+1/2, 1/2)')),
3050 '8j': (1, ('(x, 1/2, 0)', '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
3051 '(-x+1/2, 0, 1/2)', '(1/2, x, 0)', '(0, x+1/2, 1/2)',
3052 '(1/2, -x, 0)', '(0, -x+1/2, 1/2)')),
3053 '16k': (1, ('(x, x+1/2, 1/4)', '(x+1/2, x, 3/4)',
3054 '(-x, -x+1/2, 1/4)', '(-x+1/2, -x, 3/4)',
3055 '(-x+1/2, x, 1/4)', '(-x, x+1/2, 3/4)',
3056 '(x+1/2, -x, 1/4)', '(x, -x+1/2, 3/4)',
3057 '(-x, -x+1/2, 3/4)', '(-x+1/2, -x, 1/4)',
3058 '(x, x+1/2, 3/4)', '(x+1/2, x, 1/4)',
3059 '(x+1/2, -x, 3/4)', '(x, -x+1/2, 1/4)',
3060 '(-x+1/2, x, 3/4)', '(-x, x+1/2, 1/4)')),
3061 '16l': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 0)',
3062 '(-x+1/2, -y+1/2, 1/2)', '(-y, x, 0)',
3063 '(-y+1/2, x+1/2, 1/2)', '(y, -x, 0)',
3064 '(y+1/2, -x+1/2, 1/2)', '(-x, y, 0)',
3065 '(-x+1/2, y+1/2, 1/2)', '(x, -y, 0)',
3066 '(x+1/2, -y+1/2, 1/2)', '(y, x, 0)',
3067 '(y+1/2, x+1/2, 1/2)', '(-y, -x, 0)',
3068 '(-y+1/2, -x+1/2, 1/2)')),
3069 '16m': (5, ('(x, x, z)', '(x+1/2, x+1/2, z+1/2)', '(-x, -x, z)',
3070 '(-x+1/2, -x+1/2, z+1/2)', '(-x, x, z)',
3071 '(-x+1/2, x+1/2, z+1/2)', '(x, -x, z)',
3072 '(x+1/2, -x+1/2, z+1/2)', '(-x, x, -z)',
3073 '(-x+1/2, x+1/2, -z+1/2)', '(x, -x, -z)',
3074 '(x+1/2, -x+1/2, -z+1/2)', '(x, x, -z)',
3075 '(x+1/2, x+1/2, -z+1/2)', '(-x, -x, -z)',
3076 '(-x+1/2, -x+1/2, -z+1/2)')),
3077 '16n': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y, z)',
3078 '(1/2, -y+1/2, z+1/2)', '(-y, 0, z)',
3079 '(-y+1/2, 1/2, z+1/2)', '(y, 0, z)',
3080 '(y+1/2, 1/2, z+1/2)', '(0, y, -z)',
3081 '(1/2, y+1/2, -z+1/2)', '(0, -y, -z)',
3082 '(1/2, -y+1/2, -z+1/2)', '(y, 0, -z)',
3083 '(y+1/2, 1/2, -z+1/2)', '(-y, 0, -z)',
3084 '(-y+1/2, 1/2, -z+1/2)')),
3085 '32o': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
3086 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
3087 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
3088 '(y+1/2, -x+1/2, z+1/2)', '(-x, y, -z)',
3089 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
3090 '(x+1/2, -y+1/2, -z+1/2)', '(y, x, -z)',
3091 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z)',
3092 '(-y+1/2, -x+1/2, -z+1/2)', '(-x, -y, -z)',
3093 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
3094 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
3095 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
3096 '(-y+1/2, x+1/2, -z+1/2)', '(x, -y, z)',
3097 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
3098 '(-x+1/2, y+1/2, z+1/2)', '(-y, -x, z)',
3099 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z)',
3100 '(y+1/2, x+1/2, z+1/2)'))},
3101 '140': {'4a': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)', '(0, 0, 3/4)',
3102 '(1/2, 1/2, 1/4)')),
3103 '4b': (0, ('(0, 1/2, 1/4)', '(1/2, 0, 3/4)', '(1/2, 0, 1/4)',
3104 '(0, 1/2, 3/4)')),
3105 '4c': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 0, 1/2)',
3106 '(1/2, 1/2, 0)')),
3107 '4d': (0, ('(0, 1/2, 0)', '(1/2, 0, 1/2)', '(1/2, 0, 0)',
3108 '(0, 1/2, 1/2)')),
3109 '8e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
3110 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
3111 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
3112 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 3/4)')),
3113 '8f': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)', '(0, 0, -z+1/2)',
3114 '(1/2, 1/2, -z)', '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
3115 '(0, 0, z+1/2)', '(1/2, 1/2, z)')),
3116 '8g': (4, ('(0, 1/2, z)', '(1/2, 0, z+1/2)', '(1/2, 0, z)',
3117 '(0, 1/2, z+1/2)', '(0, 1/2, -z+1/2)',
3118 '(1/2, 0, -z)', '(1/2, 0, -z+1/2)', '(0, 1/2, -z)')),
3119 '8h': (1, ('(x, x+1/2, 0)', '(x+1/2, x, 1/2)',
3120 '(-x, -x+1/2, 0)', '(-x+1/2, -x, 1/2)',
3121 '(-x+1/2, x, 0)', '(-x, x+1/2, 1/2)',
3122 '(x+1/2, -x, 0)', '(x, -x+1/2, 1/2)')),
3123 '16i': (1, ('(x, x, 1/4)', '(x+1/2, x+1/2, 3/4)',
3124 '(-x, -x, 1/4)', '(-x+1/2, -x+1/2, 3/4)',
3125 '(-x, x, 1/4)', '(-x+1/2, x+1/2, 3/4)',
3126 '(x, -x, 1/4)', '(x+1/2, -x+1/2, 3/4)',
3127 '(-x, -x, 3/4)', '(-x+1/2, -x+1/2, 1/4)',
3128 '(x, x, 3/4)', '(x+1/2, x+1/2, 1/4)',
3129 '(x, -x, 3/4)', '(x+1/2, -x+1/2, 1/4)',
3130 '(-x, x, 3/4)', '(-x+1/2, x+1/2, 1/4)')),
3131 '16j': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)', '(-x, 0, 1/4)',
3132 '(-x+1/2, 1/2, 3/4)', '(0, x, 1/4)',
3133 '(1/2, x+1/2, 3/4)', '(0, -x, 1/4)',
3134 '(1/2, -x+1/2, 3/4)', '(-x, 0, 3/4)',
3135 '(-x+1/2, 1/2, 1/4)', '(x, 0, 3/4)',
3136 '(x+1/2, 1/2, 1/4)', '(0, -x, 3/4)',
3137 '(1/2, -x+1/2, 1/4)', '(0, x, 3/4)',
3138 '(1/2, x+1/2, 1/4)')),
3139 '16k': (3, ('(x, y, 0)', '(x+1/2, y+1/2, 1/2)', '(-x, -y, 0)',
3140 '(-x+1/2, -y+1/2, 1/2)', '(-y, x, 0)',
3141 '(-y+1/2, x+1/2, 1/2)', '(y, -x, 0)',
3142 '(y+1/2, -x+1/2, 1/2)', '(-x, y, 1/2)',
3143 '(-x+1/2, y+1/2, 0)', '(x, -y, 1/2)',
3144 '(x+1/2, -y+1/2, 0)', '(y, x, 1/2)',
3145 '(y+1/2, x+1/2, 0)', '(-y, -x, 1/2)',
3146 '(-y+1/2, -x+1/2, 0)')),
3147 '16l': (5, ('(x, x+1/2, z)', '(x+1/2, x, z+1/2)',
3148 '(-x, -x+1/2, z)', '(-x+1/2, -x, z+1/2)',
3149 '(-x+1/2, x, z)', '(-x, x+1/2, z+1/2)',
3150 '(x+1/2, -x, z)', '(x, -x+1/2, z+1/2)',
3151 '(-x, x+1/2, -z+1/2)', '(-x+1/2, x, -z)',
3152 '(x, -x+1/2, -z+1/2)', '(x+1/2, -x, -z)',
3153 '(x+1/2, x, -z+1/2)', '(x, x+1/2, -z)',
3154 '(-x+1/2, -x, -z+1/2)', '(-x, -x+1/2, -z)')),
3155 '32m': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
3156 '(-x+1/2, -y+1/2, z+1/2)', '(-y, x, z)',
3157 '(-y+1/2, x+1/2, z+1/2)', '(y, -x, z)',
3158 '(y+1/2, -x+1/2, z+1/2)', '(-x, y, -z+1/2)',
3159 '(-x+1/2, y+1/2, -z)', '(x, -y, -z+1/2)',
3160 '(x+1/2, -y+1/2, -z)', '(y, x, -z+1/2)',
3161 '(y+1/2, x+1/2, -z)', '(-y, -x, -z+1/2)',
3162 '(-y+1/2, -x+1/2, -z)', '(-x, -y, -z)',
3163 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
3164 '(x+1/2, y+1/2, -z+1/2)', '(y, -x, -z)',
3165 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
3166 '(-y+1/2, x+1/2, -z+1/2)', '(x, -y, z+1/2)',
3167 '(x+1/2, -y+1/2, z)', '(-x, y, z+1/2)',
3168 '(-x+1/2, y+1/2, z)', '(-y, -x, z+1/2)',
3169 '(-y+1/2, -x+1/2, z)', '(y, x, z+1/2)',
3170 '(y+1/2, x+1/2, z)'))},
3171 '141:1': {'4a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/4)',
3172 '(1/2, 0, 3/4)')),
3173 '4b': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(0, 1/2, 3/4)',
3174 '(1/2, 0, 1/4)')),
3175 '8c': (0, ('(0, 1/4, 1/8)', '(1/2, 3/4, 5/8)',
3176 '(1/2, 1/4, 5/8)', '(0, 3/4, 1/8)',
3177 '(3/4, 1/2, 3/8)', '(1/4, 0, 7/8)',
3178 '(3/4, 0, 7/8)', '(1/4, 1/2, 3/8)')),
3179 '8d': (0, ('(0, 1/4, 5/8)', '(1/2, 3/4, 1/8)',
3180 '(1/2, 1/4, 1/8)', '(0, 3/4, 5/8)',
3181 '(3/4, 1/2, 7/8)', '(1/4, 0, 3/8)',
3182 '(3/4, 0, 3/8)', '(1/4, 1/2, 7/8)')),
3183 '8e': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
3184 '(0, 1/2, z+1/4)', '(1/2, 0, z+3/4)',
3185 '(1/2, 0, -z+3/4)', '(0, 1/2, -z+1/4)',
3186 '(1/2, 1/2, -z+1/2)', '(0, 0, -z)')),
3187 '16f': (1, ('(x, 1/4, 1/8)', '(x+1/2, 3/4, 5/8)',
3188 '(-x+1/2, 1/4, 5/8)', '(-x, 3/4, 1/8)',
3189 '(3/4, x+1/2, 3/8)', '(1/4, x, 7/8)',
3190 '(3/4, -x, 7/8)', '(1/4, -x+1/2, 3/8)',
3191 '(-x, 1/4, 1/8)', '(-x+1/2, 3/4, 5/8)',
3192 '(x+1/2, 1/4, 5/8)', '(x, 3/4, 1/8)',
3193 '(1/4, -x, 7/8)', '(3/4, -x+1/2, 3/8)',
3194 '(1/4, x+1/2, 3/8)', '(3/4, x, 7/8)')),
3195 '16g': (1, ('(x, x, 0)', '(x+1/2, x+1/2, 1/2)',
3196 '(-x+1/2, -x+1/2, 1/2)', '(-x, -x, 0)',
3197 '(-x, x+1/2, 1/4)', '(-x+1/2, x, 3/4)',
3198 '(x+1/2, -x, 3/4)', '(x, -x+1/2, 1/4)',
3199 '(-x, -x+1/2, 1/4)', '(-x+1/2, -x, 3/4)',
3200 '(x+1/2, x, 3/4)', '(x, x+1/2, 1/4)',
3201 '(x, -x, 0)', '(x+1/2, -x+1/2, 1/2)',
3202 '(-x+1/2, x+1/2, 1/2)', '(-x, x, 0)')),
3203 '16h': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)',
3204 '(1/2, -y+1/2, z+1/2)', '(0, -y, z)',
3205 '(-y, 1/2, z+1/4)', '(-y+1/2, 0, z+3/4)',
3206 '(y+1/2, 0, z+3/4)', '(y, 1/2, z+1/4)',
3207 '(1/2, y, -z+3/4)', '(0, y+1/2, -z+1/4)',
3208 '(0, -y+1/2, -z+1/4)', '(1/2, -y, -z+3/4)',
3209 '(y+1/2, 1/2, -z+1/2)', '(y, 0, -z)',
3210 '(-y, 0, -z)', '(-y+1/2, 1/2, -z+1/2)')),
3211 '32i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
3212 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
3213 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
3214 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
3215 '(-x+1/2, y, -z+3/4)', '(-x, y+1/2, -z+1/4)',
3216 '(x, -y+1/2, -z+1/4)', '(x+1/2, -y, -z+3/4)',
3217 '(y+1/2, x+1/2, -z+1/2)', '(y, x, -z)',
3218 '(-y, -x, -z)', '(-y+1/2, -x+1/2, -z+1/2)',
3219 '(-x, -y+1/2, -z+1/4)', '(-x+1/2, -y, -z+3/4)',
3220 '(x+1/2, y, -z+3/4)', '(x, y+1/2, -z+1/4)',
3221 '(y, -x, -z)', '(y+1/2, -x+1/2, -z+1/2)',
3222 '(-y+1/2, x+1/2, -z+1/2)', '(-y, x, -z)',
3223 '(x+1/2, -y+1/2, z+1/2)', '(x, -y, z)',
3224 '(-x, y, z)', '(-x+1/2, y+1/2, z+1/2)',
3225 '(-y+1/2, -x, z+3/4)', '(-y, -x+1/2, z+1/4)',
3226 '(y, x+1/2, z+1/4)', '(y+1/2, x, z+3/4)'))},
3227 '141:2': {'4a': (0, ('(0, 3/4, 1/8)', '(1/2, 1/4, 5/8)',
3228 '(1/2, 3/4, 3/8)', '(0, 1/4, 7/8)')),
3229 '4b': (0, ('(0, 1/4, 3/8)', '(1/2, 3/4, 7/8)',
3230 '(0, 3/4, 5/8)', '(1/2, 1/4, 1/8)')),
3231 '8c': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
3232 '(0, 1/2, 0)', '(1/4, 3/4, 1/4)',
3233 '(3/4, 1/4, 3/4)', '(1/4, 1/4, 3/4)',
3234 '(3/4, 3/4, 1/4)')),
3235 '8d': (0, ('(0, 0, 1/2)', '(1/2, 1/2, 0)', '(1/2, 0, 0)',
3236 '(0, 1/2, 1/2)', '(1/4, 3/4, 3/4)',
3237 '(3/4, 1/4, 1/4)', '(1/4, 1/4, 1/4)',
3238 '(3/4, 3/4, 3/4)')),
3239 '8e': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)',
3240 '(0, 3/4, z+1/4)', '(1/2, 1/4, z+3/4)',
3241 '(1/2, 1/4, -z+1/2)', '(0, 3/4, -z)',
3242 '(1/2, 3/4, -z+1/4)', '(0, 1/4, -z+3/4)')),
3243 '16f': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)',
3244 '(-x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
3245 '(1/4, x+3/4, 1/4)', '(3/4, x+1/4, 3/4)',
3246 '(1/4, -x+1/4, 3/4)', '(3/4, -x+3/4, 1/4)',
3247 '(-x, 0, 0)', '(-x+1/2, 1/2, 1/2)',
3248 '(x+1/2, 0, 1/2)', '(x, 1/2, 0)',
3249 '(3/4, -x+1/4, 3/4)', '(1/4, -x+3/4, 1/4)',
3250 '(3/4, x+3/4, 1/4)', '(1/4, x+1/4, 3/4)')),
3251 '16g': (1, ('(x, x+1/4, 7/8)', '(x+1/2, x+3/4, 3/8)',
3252 '(-x+1/2, -x+3/4, 3/8)', '(-x, -x+1/4, 7/8)',
3253 '(-x, x+3/4, 1/8)', '(-x+1/2, x+1/4, 5/8)',
3254 '(x+1/2, -x+1/4, 5/8)', '(x, -x+3/4, 1/8)',
3255 '(-x, -x+3/4, 1/8)', '(-x+1/2, -x+1/4, 5/8)',
3256 '(x+1/2, x+1/4, 5/8)', '(x, x+3/4, 1/8)',
3257 '(x, -x+1/4, 7/8)', '(x+1/2, -x+3/4, 3/8)',
3258 '(-x+1/2, x+3/4, 3/8)', '(-x, x+1/4, 7/8)')),
3259 '16h': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)',
3260 '(1/2, -y, z+1/2)', '(0, -y+1/2, z)',
3261 '(-y+1/4, 3/4, z+1/4)', '(-y+3/4, 1/4, z+3/4)',
3262 '(y+1/4, 1/4, z+3/4)', '(y+3/4, 3/4, z+1/4)',
3263 '(1/2, y, -z+1/2)', '(0, y+1/2, -z)',
3264 '(0, -y, -z)', '(1/2, -y+1/2, -z+1/2)',
3265 '(y+1/4, 3/4, -z+1/4)', '(y+3/4, 1/4, -z+3/4)',
3266 '(-y+1/4, 1/4, -z+3/4)', '(-y+3/4, 3/4, -z+1/4)'
3267 )),
3268 '32i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
3269 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
3270 '(-y+1/4, x+3/4, z+1/4)',
3271 '(-y+3/4, x+1/4, z+3/4)',
3272 '(y+1/4, -x+1/4, z+3/4)',
3273 '(y+3/4, -x+3/4, z+1/4)', '(-x+1/2, y, -z+1/2)',
3274 '(-x, y+1/2, -z)', '(x, -y, -z)',
3275 '(x+1/2, -y+1/2, -z+1/2)',
3276 '(y+1/4, x+3/4, -z+1/4)',
3277 '(y+3/4, x+1/4, -z+3/4)',
3278 '(-y+1/4, -x+1/4, -z+3/4)',
3279 '(-y+3/4, -x+3/4, -z+1/4)', '(-x, -y, -z)',
3280 '(-x+1/2, -y+1/2, -z+1/2)', '(x+1/2, y, -z+1/2)',
3281 '(x, y+1/2, -z)', '(y+3/4, -x+1/4, -z+3/4)',
3282 '(y+1/4, -x+3/4, -z+1/4)',
3283 '(-y+3/4, x+3/4, -z+1/4)',
3284 '(-y+1/4, x+1/4, -z+3/4)', '(x+1/2, -y, z+1/2)',
3285 '(x, -y+1/2, z)', '(-x, y, z)',
3286 '(-x+1/2, y+1/2, z+1/2)',
3287 '(-y+3/4, -x+1/4, z+3/4)',
3288 '(-y+1/4, -x+3/4, z+1/4)',
3289 '(y+3/4, x+3/4, z+1/4)', '(y+1/4, x+1/4, z+3/4)'
3290 ))},
3291 '142:1': {'8a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(0, 1/2, 1/4)',
3292 '(1/2, 0, 3/4)', '(1/2, 0, 1/4)', '(0, 1/2, 3/4)',
3293 '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
3294 '8b': (0, ('(0, 0, 1/4)', '(1/2, 1/2, 3/4)', '(0, 1/2, 1/2)',
3295 '(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 0, 1/2)',
3296 '(0, 0, 3/4)', '(1/2, 1/2, 1/4)')),
3297 '16c': (0, ('(0, 1/4, 1/8)', '(1/2, 3/4, 5/8)',
3298 '(1/2, 1/4, 5/8)', '(0, 3/4, 1/8)',
3299 '(3/4, 1/2, 3/8)', '(1/4, 0, 7/8)',
3300 '(3/4, 0, 7/8)', '(1/4, 1/2, 3/8)',
3301 '(1/2, 1/4, 1/8)', '(0, 3/4, 5/8)',
3302 '(0, 1/4, 5/8)', '(1/2, 3/4, 1/8)',
3303 '(3/4, 1/2, 7/8)', '(1/4, 0, 3/8)',
3304 '(3/4, 0, 3/8)', '(1/4, 1/2, 7/8)')),
3305 '16d': (4, ('(0, 0, z)', '(1/2, 1/2, z+1/2)',
3306 '(0, 1/2, z+1/4)', '(1/2, 0, z+3/4)',
3307 '(1/2, 0, -z+1/4)', '(0, 1/2, -z+3/4)',
3308 '(1/2, 1/2, -z)', '(0, 0, -z+1/2)',
3309 '(0, 1/2, -z+1/4)', '(1/2, 0, -z+3/4)',
3310 '(0, 0, -z)', '(1/2, 1/2, -z+1/2)',
3311 '(1/2, 1/2, z)', '(0, 0, z+1/2)',
3312 '(1/2, 0, z+1/4)', '(0, 1/2, z+3/4)')),
3313 '16e': (2, ('(1/4, y, 1/8)', '(3/4, y+1/2, 5/8)',
3314 '(1/4, -y+1/2, 5/8)', '(3/4, -y, 1/8)',
3315 '(-y, 3/4, 3/8)', '(-y+1/2, 1/4, 7/8)',
3316 '(y+1/2, 3/4, 7/8)', '(y, 1/4, 3/8)',
3317 '(3/4, -y+1/2, 1/8)', '(1/4, -y, 5/8)',
3318 '(3/4, y, 5/8)', '(1/4, y+1/2, 1/8)',
3319 '(y, 3/4, 7/8)', '(y+1/2, 1/4, 3/8)',
3320 '(-y+1/2, 3/4, 3/8)', '(-y, 1/4, 7/8)')),
3321 '16f': (1, ('(x, x, 1/4)', '(x+1/2, x+1/2, 3/4)',
3322 '(-x+1/2, -x+1/2, 3/4)', '(-x, -x, 1/4)',
3323 '(-x, x+1/2, 1/2)', '(-x+1/2, x, 0)',
3324 '(x+1/2, -x, 0)', '(x, -x+1/2, 1/2)',
3325 '(-x, -x+1/2, 0)', '(-x+1/2, -x, 1/2)',
3326 '(x+1/2, x, 1/2)', '(x, x+1/2, 0)',
3327 '(x, -x, 3/4)', '(x+1/2, -x+1/2, 1/4)',
3328 '(-x+1/2, x+1/2, 1/4)', '(-x, x, 3/4)')),
3329 '32g': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
3330 '(-x+1/2, -y+1/2, z+1/2)', '(-x, -y, z)',
3331 '(-y, x+1/2, z+1/4)', '(-y+1/2, x, z+3/4)',
3332 '(y+1/2, -x, z+3/4)', '(y, -x+1/2, z+1/4)',
3333 '(-x+1/2, y, -z+1/4)', '(-x, y+1/2, -z+3/4)',
3334 '(x, -y+1/2, -z+3/4)', '(x+1/2, -y, -z+1/4)',
3335 '(y+1/2, x+1/2, -z)', '(y, x, -z+1/2)',
3336 '(-y, -x, -z+1/2)', '(-y+1/2, -x+1/2, -z)',
3337 '(-x, -y+1/2, -z+1/4)', '(-x+1/2, -y, -z+3/4)',
3338 '(x+1/2, y, -z+3/4)', '(x, y+1/2, -z+1/4)',
3339 '(y, -x, -z)', '(y+1/2, -x+1/2, -z+1/2)',
3340 '(-y+1/2, x+1/2, -z+1/2)', '(-y, x, -z)',
3341 '(x+1/2, -y+1/2, z)', '(x, -y, z+1/2)',
3342 '(-x, y, z+1/2)', '(-x+1/2, y+1/2, z)',
3343 '(-y+1/2, -x, z+1/4)', '(-y, -x+1/2, z+3/4)',
3344 '(y, x+1/2, z+3/4)', '(y+1/2, x, z+1/4)'))},
3345 '142:2': {'8a': (0, ('(0, 1/4, 3/8)', '(1/2, 3/4, 7/8)',
3346 '(0, 3/4, 5/8)', '(1/2, 1/4, 1/8)',
3347 '(1/2, 1/4, 5/8)', '(0, 3/4, 1/8)',
3348 '(1/2, 3/4, 3/8)', '(0, 1/4, 7/8)')),
3349 '8b': (0, ('(0, 1/4, 1/8)', '(1/2, 3/4, 5/8)',
3350 '(0, 3/4, 3/8)', '(1/2, 1/4, 7/8)',
3351 '(0, 3/4, 7/8)', '(1/2, 1/4, 3/8)',
3352 '(0, 1/4, 5/8)', '(1/2, 3/4, 1/8)')),
3353 '16c': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
3354 '(0, 1/2, 0)', '(1/4, 3/4, 1/4)',
3355 '(3/4, 1/4, 3/4)', '(1/4, 1/4, 3/4)',
3356 '(3/4, 3/4, 1/4)', '(1/2, 0, 0)', '(0, 1/2, 1/2)',
3357 '(0, 0, 1/2)', '(1/2, 1/2, 0)', '(1/4, 3/4, 3/4)',
3358 '(3/4, 1/4, 1/4)', '(1/4, 1/4, 1/4)',
3359 '(3/4, 3/4, 3/4)')),
3360 '16d': (4, ('(0, 1/4, z)', '(1/2, 3/4, z+1/2)',
3361 '(0, 3/4, z+1/4)', '(1/2, 1/4, z+3/4)',
3362 '(1/2, 1/4, -z)', '(0, 3/4, -z+1/2)',
3363 '(1/2, 3/4, -z+3/4)', '(0, 1/4, -z+1/4)',
3364 '(0, 3/4, -z)', '(1/2, 1/4, -z+1/2)',
3365 '(0, 1/4, -z+3/4)', '(1/2, 3/4, -z+1/4)',
3366 '(1/2, 3/4, z)', '(0, 1/4, z+1/2)',
3367 '(1/2, 1/4, z+1/4)', '(0, 3/4, z+3/4)')),
3368 '16e': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
3369 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)',
3370 '(1/4, x+3/4, 1/2)', '(3/4, x+1/4, 0)',
3371 '(1/4, -x+1/4, 0)', '(3/4, -x+3/4, 1/2)',
3372 '(-x, 0, 3/4)', '(-x+1/2, 1/2, 1/4)',
3373 '(x+1/2, 0, 1/4)', '(x, 1/2, 3/4)',
3374 '(3/4, -x+1/4, 1/2)', '(1/4, -x+3/4, 0)',
3375 '(3/4, x+3/4, 0)', '(1/4, x+1/4, 1/2)')),
3376 '16f': (1, ('(x, x+1/4, 1/8)', '(x+1/2, x+3/4, 5/8)',
3377 '(-x+1/2, -x+3/4, 5/8)', '(-x, -x+1/4, 1/8)',
3378 '(-x, x+3/4, 3/8)', '(-x+1/2, x+1/4, 7/8)',
3379 '(x+1/2, -x+1/4, 7/8)', '(x, -x+3/4, 3/8)',
3380 '(-x, -x+3/4, 7/8)', '(-x+1/2, -x+1/4, 3/8)',
3381 '(x+1/2, x+1/4, 3/8)', '(x, x+3/4, 7/8)',
3382 '(x, -x+1/4, 5/8)', '(x+1/2, -x+3/4, 1/8)',
3383 '(-x+1/2, x+3/4, 1/8)', '(-x, x+1/4, 5/8)')),
3384 '32g': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
3385 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
3386 '(-y+1/4, x+3/4, z+1/4)',
3387 '(-y+3/4, x+1/4, z+3/4)',
3388 '(y+1/4, -x+1/4, z+3/4)',
3389 '(y+3/4, -x+3/4, z+1/4)', '(-x+1/2, y, -z)',
3390 '(-x, y+1/2, -z+1/2)', '(x, -y, -z+1/2)',
3391 '(x+1/2, -y+1/2, -z)', '(y+1/4, x+3/4, -z+3/4)',
3392 '(y+3/4, x+1/4, -z+1/4)',
3393 '(-y+1/4, -x+1/4, -z+1/4)',
3394 '(-y+3/4, -x+3/4, -z+3/4)', '(-x, -y, -z)',
3395 '(-x+1/2, -y+1/2, -z+1/2)', '(x+1/2, y, -z+1/2)',
3396 '(x, y+1/2, -z)', '(y+3/4, -x+1/4, -z+3/4)',
3397 '(y+1/4, -x+3/4, -z+1/4)',
3398 '(-y+3/4, x+3/4, -z+1/4)',
3399 '(-y+1/4, x+1/4, -z+3/4)', '(x+1/2, -y, z)',
3400 '(x, -y+1/2, z+1/2)', '(-x, y, z+1/2)',
3401 '(-x+1/2, y+1/2, z)', '(-y+3/4, -x+1/4, z+1/4)',
3402 '(-y+1/4, -x+3/4, z+3/4)',
3403 '(y+3/4, x+3/4, z+3/4)', '(y+1/4, x+1/4, z+1/4)'
3404 ))},
3405 '143': {'1a': (4, ('(0, 0, z)', )),
3406 '1b': (4, ('(1/3, 2/3, z)', )),
3407 '1c': (4, ('(2/3, 1/3, z)', )),
3408 '3d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)'))},
3409 '144': {'3a': (7, ('(x, y, z)', '(-y, x-y, z+1/3)', '(-x+y, -x, z+2/3)'
3410 ))},
3411 '145': {'3a': (7, ('(x, y, z)', '(-y, x-y, z+2/3)', '(-x+y, -x, z+1/3)'
3412 ))},
3413 '146:H': {'3a': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3414 '(1/3, 2/3, z+2/3)')),
3415 '9b': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3416 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3417 '(-y+2/3, x-y+1/3, z+1/3)',
3418 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3419 '(-x+y+2/3, -x+1/3, z+1/3)',
3420 '(-x+y+1/3, -x+2/3, z+2/3)'))},
3421 '146:R': {'1a': (1, ('(x, x, x)', )),
3422 '3b': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)'))},
3423 '147': {'1a': (0, ('(0, 0, 0)', )),
3424 '1b': (0, ('(0, 0, 1/2)', )),
3425 '2c': (4, ('(0, 0, z)', '(0, 0, -z)')),
3426 '2d': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, -z)')),
3427 '3e': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
3428 '3f': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3429 )),
3430 '6g': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3431 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)'))},
3432 '148:H': {'3a': (0, ('(0, 0, 0)', '(2/3, 1/3, 1/3)', '(1/3, 2/3, 2/3)'
3433 )),
3434 '3b': (0, ('(0, 0, 1/2)', '(2/3, 1/3, 5/6)',
3435 '(1/3, 2/3, 1/6)')),
3436 '6c': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3437 '(1/3, 2/3, z+2/3)', '(0, 0, -z)',
3438 '(2/3, 1/3, -z+1/3)', '(1/3, 2/3, -z+2/3)')),
3439 '9d': (0, ('(1/2, 0, 1/2)', '(1/6, 1/3, 5/6)',
3440 '(5/6, 2/3, 1/6)', '(0, 1/2, 1/2)',
3441 '(2/3, 5/6, 5/6)', '(1/3, 1/6, 1/6)',
3442 '(1/2, 1/2, 1/2)', '(1/6, 5/6, 5/6)',
3443 '(5/6, 1/6, 1/6)')),
3444 '9e': (0, ('(1/2, 0, 0)', '(1/6, 1/3, 1/3)',
3445 '(5/6, 2/3, 2/3)', '(0, 1/2, 0)',
3446 '(2/3, 5/6, 1/3)', '(1/3, 1/6, 2/3)',
3447 '(1/2, 1/2, 0)', '(1/6, 5/6, 1/3)',
3448 '(5/6, 1/6, 2/3)')),
3449 '18f': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3450 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3451 '(-y+2/3, x-y+1/3, z+1/3)',
3452 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3453 '(-x+y+2/3, -x+1/3, z+1/3)',
3454 '(-x+y+1/3, -x+2/3, z+2/3)', '(-x, -y, -z)',
3455 '(-x+2/3, -y+1/3, -z+1/3)',
3456 '(-x+1/3, -y+2/3, -z+2/3)', '(y, -x+y, -z)',
3457 '(y+2/3, -x+y+1/3, -z+1/3)',
3458 '(y+1/3, -x+y+2/3, -z+2/3)', '(x-y, x, -z)',
3459 '(x-y+2/3, x+1/3, -z+1/3)',
3460 '(x-y+1/3, x+2/3, -z+2/3)'))},
3461 '148:R': {'1a': (0, ('(0, 0, 0)', )),
3462 '1b': (0, ('(1/2, 1/2, 1/2)', )),
3463 '2c': (1, ('(x, x, x)', '(-x, -x, -x)')),
3464 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
3465 '3e': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)'
3466 )),
3467 '6f': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)',
3468 '(-x, -y, -z)', '(-z, -x, -y)', '(-y, -z, -x)'))},
3469 '149': {'1a': (0, ('(0, 0, 0)', )),
3470 '1b': (0, ('(0, 0, 1/2)', )),
3471 '1c': (0, ('(1/3, 2/3, 0)', )),
3472 '1d': (0, ('(1/3, 2/3, 1/2)', )),
3473 '1e': (0, ('(2/3, 1/3, 0)', )),
3474 '1f': (0, ('(2/3, 1/3, 1/2)', )),
3475 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
3476 '2h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z)')),
3477 '2i': (4, ('(2/3, 1/3, z)', '(2/3, 1/3, -z)')),
3478 '3j': (1, ('(x, -x, 0)', '(x, 2*x, 0)', '(-2*x, -x, 0)')),
3479 '3k': (1, ('(x, -x, 1/2)', '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)')),
3480 '6l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3481 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)'))},
3482 '150': {'1a': (0, ('(0, 0, 0)', )),
3483 '1b': (0, ('(0, 0, 1/2)', )),
3484 '2c': (4, ('(0, 0, z)', '(0, 0, -z)')),
3485 '2d': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, -z)')),
3486 '3e': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)')),
3487 '3f': (1, ('(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)')),
3488 '6g': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3489 '(y, x, -z)', '(x-y, -y, -z)', '(-x, -x+y, -z)'))},
3490 '151': {'3a': (1, ('(x, -x, 1/3)', '(x, 2*x, 2/3)', '(-2*x, -x, 0)')),
3491 '3b': (1, ('(x, -x, 5/6)', '(x, 2*x, 1/6)', '(-2*x, -x, 1/2)')),
3492 '6c': (7, ('(x, y, z)', '(-y, x-y, z+1/3)', '(-x+y, -x, z+2/3)',
3493 '(-y, -x, -z+2/3)', '(-x+y, y, -z+1/3)',
3494 '(x, x-y, -z)'))},
3495 '152': {'3a': (1, ('(x, 0, 1/3)', '(0, x, 2/3)', '(-x, -x, 0)')),
3496 '3b': (1, ('(x, 0, 5/6)', '(0, x, 1/6)', '(-x, -x, 1/2)')),
3497 '6c': (7, ('(x, y, z)', '(-y, x-y, z+1/3)', '(-x+y, -x, z+2/3)',
3498 '(y, x, -z)', '(x-y, -y, -z+2/3)',
3499 '(-x, -x+y, -z+1/3)'))},
3500 '153': {'3a': (1, ('(x, -x, 2/3)', '(x, 2*x, 1/3)', '(-2*x, -x, 0)')),
3501 '3b': (1, ('(x, -x, 1/6)', '(x, 2*x, 5/6)', '(-2*x, -x, 1/2)')),
3502 '6c': (7, ('(x, y, z)', '(-y, x-y, z+2/3)', '(-x+y, -x, z+1/3)',
3503 '(-y, -x, -z+1/3)', '(-x+y, y, -z+2/3)',
3504 '(x, x-y, -z)'))},
3505 '154': {'3a': (1, ('(x, 0, 2/3)', '(0, x, 1/3)', '(-x, -x, 0)')),
3506 '3b': (1, ('(x, 0, 1/6)', '(0, x, 5/6)', '(-x, -x, 1/2)')),
3507 '6c': (7, ('(x, y, z)', '(-y, x-y, z+2/3)', '(-x+y, -x, z+1/3)',
3508 '(y, x, -z)', '(x-y, -y, -z+1/3)',
3509 '(-x, -x+y, -z+2/3)'))},
3510 '155:H': {'3a': (0, ('(0, 0, 0)', '(2/3, 1/3, 1/3)', '(1/3, 2/3, 2/3)'
3511 )),
3512 '3b': (0, ('(0, 0, 1/2)', '(2/3, 1/3, 5/6)',
3513 '(1/3, 2/3, 1/6)')),
3514 '6c': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3515 '(1/3, 2/3, z+2/3)', '(0, 0, -z)',
3516 '(2/3, 1/3, -z+1/3)', '(1/3, 2/3, -z+2/3)')),
3517 '9d': (1, ('(x, 0, 0)', '(x+2/3, 1/3, 1/3)',
3518 '(x+1/3, 2/3, 2/3)', '(0, x, 0)',
3519 '(2/3, x+1/3, 1/3)', '(1/3, x+2/3, 2/3)',
3520 '(-x, -x, 0)', '(-x+2/3, -x+1/3, 1/3)',
3521 '(-x+1/3, -x+2/3, 2/3)')),
3522 '9e': (1, ('(x, 0, 1/2)', '(x+2/3, 1/3, 5/6)',
3523 '(x+1/3, 2/3, 1/6)', '(0, x, 1/2)',
3524 '(2/3, x+1/3, 5/6)', '(1/3, x+2/3, 1/6)',
3525 '(-x, -x, 1/2)', '(-x+2/3, -x+1/3, 5/6)',
3526 '(-x+1/3, -x+2/3, 1/6)')),
3527 '18f': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3528 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3529 '(-y+2/3, x-y+1/3, z+1/3)',
3530 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3531 '(-x+y+2/3, -x+1/3, z+1/3)',
3532 '(-x+y+1/3, -x+2/3, z+2/3)', '(y, x, -z)',
3533 '(y+2/3, x+1/3, -z+1/3)',
3534 '(y+1/3, x+2/3, -z+2/3)', '(x-y, -y, -z)',
3535 '(x-y+2/3, -y+1/3, -z+1/3)',
3536 '(x-y+1/3, -y+2/3, -z+2/3)', '(-x, -x+y, -z)',
3537 '(-x+2/3, -x+y+1/3, -z+1/3)',
3538 '(-x+1/3, -x+y+2/3, -z+2/3)'))},
3539 '155:R': {'1a': (0, ('(0, 0, 0)', )),
3540 '1b': (0, ('(1/2, 1/2, 1/2)', )),
3541 '2c': (1, ('(x, x, x)', '(-x, -x, -x)')),
3542 '3d': (2, ('(0, y, -y)', '(-y, 0, y)', '(y, -y, 0)')),
3543 '3e': (2, ('(1/2, y, -y)', '(-y, 1/2, y)', '(y, -y, 1/2)')),
3544 '6f': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)',
3545 '(-z, -y, -x)', '(-y, -x, -z)', '(-x, -z, -y)'))},
3546 '156': {'1a': (4, ('(0, 0, z)', )),
3547 '1b': (4, ('(1/3, 2/3, z)', )),
3548 '1c': (4, ('(2/3, 1/3, z)', )),
3549 '3d': (5, ('(x, -x, z)', '(x, 2*x, z)', '(-2*x, -x, z)')),
3550 '6e': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3551 '(-y, -x, z)', '(-x+y, y, z)', '(x, x-y, z)'))},
3552 '157': {'1a': (4, ('(0, 0, z)', )),
3553 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)')),
3554 '3c': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)')),
3555 '6d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3556 '(y, x, z)', '(x-y, -y, z)', '(-x, -x+y, z)'))},
3557 '158': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
3558 '2b': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, z+1/2)')),
3559 '2c': (4, ('(2/3, 1/3, z)', '(2/3, 1/3, z+1/2)')),
3560 '6d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3561 '(-y, -x, z+1/2)', '(-x+y, y, z+1/2)',
3562 '(x, x-y, z+1/2)'))},
3563 '159': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
3564 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)')),
3565 '6c': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3566 '(y, x, z+1/2)', '(x-y, -y, z+1/2)',
3567 '(-x, -x+y, z+1/2)'))},
3568 '160:H': {'3a': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3569 '(1/3, 2/3, z+2/3)')),
3570 '9b': (5, ('(x, -x, z)', '(x+2/3, -x+1/3, z+1/3)',
3571 '(x+1/3, -x+2/3, z+2/3)', '(x, 2*x, z)',
3572 '(x+2/3, 2*x+1/3, z+1/3)',
3573 '(x+1/3, 2*x+2/3, z+2/3)', '(-2*x, -x, z)',
3574 '(-2*x+2/3, -x+1/3, z+1/3)',
3575 '(-2*x+1/3, -x+2/3, z+2/3)')),
3576 '18c': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3577 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3578 '(-y+2/3, x-y+1/3, z+1/3)',
3579 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3580 '(-x+y+2/3, -x+1/3, z+1/3)',
3581 '(-x+y+1/3, -x+2/3, z+2/3)', '(-y, -x, z)',
3582 '(-y+2/3, -x+1/3, z+1/3)',
3583 '(-y+1/3, -x+2/3, z+2/3)', '(-x+y, y, z)',
3584 '(-x+y+2/3, y+1/3, z+1/3)',
3585 '(-x+y+1/3, y+2/3, z+2/3)', '(x, x-y, z)',
3586 '(x+2/3, x-y+1/3, z+1/3)',
3587 '(x+1/3, x-y+2/3, z+2/3)'))},
3588 '160:R': {'1a': (1, ('(x, x, x)', )),
3589 '3b': (5, ('(x, x, z)', '(z, x, x)', '(x, z, x)')),
3590 '6c': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)', '(z, y, x)',
3591 '(y, x, z)', '(x, z, y)'))},
3592 '161:H': {'6a': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3593 '(1/3, 2/3, z+2/3)', '(0, 0, z+1/2)',
3594 '(2/3, 1/3, z+5/6)', '(1/3, 2/3, z+1/6)')),
3595 '18b': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3596 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3597 '(-y+2/3, x-y+1/3, z+1/3)',
3598 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3599 '(-x+y+2/3, -x+1/3, z+1/3)',
3600 '(-x+y+1/3, -x+2/3, z+2/3)', '(-y, -x, z+1/2)',
3601 '(-y+2/3, -x+1/3, z+5/6)',
3602 '(-y+1/3, -x+2/3, z+1/6)', '(-x+y, y, z+1/2)',
3603 '(-x+y+2/3, y+1/3, z+5/6)',
3604 '(-x+y+1/3, y+2/3, z+1/6)', '(x, x-y, z+1/2)',
3605 '(x+2/3, x-y+1/3, z+5/6)',
3606 '(x+1/3, x-y+2/3, z+1/6)'))},
3607 '161:R': {'2a': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)')),
3608 '6b': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)',
3609 '(z+1/2, y+1/2, x+1/2)', '(y+1/2, x+1/2, z+1/2)',
3610 '(x+1/2, z+1/2, y+1/2)'))},
3611 '162': {'1a': (0, ('(0, 0, 0)', )),
3612 '1b': (0, ('(0, 0, 1/2)', )),
3613 '2c': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)')),
3614 '2d': (0, ('(1/3, 2/3, 1/2)', '(2/3, 1/3, 1/2)')),
3615 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
3616 '3f': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
3617 '3g': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3618 )),
3619 '4h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z)', '(2/3, 1/3, -z)',
3620 '(2/3, 1/3, z)')),
3621 '6i': (1, ('(x, -x, 0)', '(x, 2*x, 0)', '(-2*x, -x, 0)',
3622 '(-x, x, 0)', '(-x, -2*x, 0)', '(2*x, x, 0)')),
3623 '6j': (1, ('(x, -x, 1/2)', '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)',
3624 '(-x, x, 1/2)', '(-x, -2*x, 1/2)', '(2*x, x, 1/2)')),
3625 '6k': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
3626 '(0, -x, -z)', '(-x, 0, -z)', '(x, x, -z)')),
3627 '12l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3628 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)',
3629 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
3630 '(y, x, z)', '(x-y, -y, z)', '(-x, -x+y, z)'))},
3631 '163': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
3632 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
3633 '2c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)')),
3634 '2d': (0, ('(2/3, 1/3, 1/4)', '(1/3, 2/3, 3/4)')),
3635 '4e': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
3636 '(0, 0, z+1/2)')),
3637 '4f': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z+1/2)',
3638 '(2/3, 1/3, -z)', '(2/3, 1/3, z+1/2)')),
3639 '6g': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
3640 '(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 1/2)'
3641 )),
3642 '6h': (1, ('(x, -x, 1/4)', '(x, 2*x, 1/4)', '(-2*x, -x, 1/4)',
3643 '(-x, x, 3/4)', '(-x, -2*x, 3/4)', '(2*x, x, 3/4)')),
3644 '12i': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3645 '(-y, -x, -z+1/2)', '(-x+y, y, -z+1/2)',
3646 '(x, x-y, -z+1/2)', '(-x, -y, -z)', '(y, -x+y, -z)',
3647 '(x-y, x, -z)', '(y, x, z+1/2)', '(x-y, -y, z+1/2)',
3648 '(-x, -x+y, z+1/2)'))},
3649 '164': {'1a': (0, ('(0, 0, 0)', )),
3650 '1b': (0, ('(0, 0, 1/2)', )),
3651 '2c': (4, ('(0, 0, z)', '(0, 0, -z)')),
3652 '2d': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, -z)')),
3653 '3e': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
3654 '3f': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3655 )),
3656 '6g': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
3657 '(-x, 0, 0)', '(0, -x, 0)', '(x, x, 0)')),
3658 '6h': (1, ('(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)',
3659 '(-x, 0, 1/2)', '(0, -x, 1/2)', '(x, x, 1/2)')),
3660 '6i': (5, ('(x, -x, z)', '(x, 2*x, z)', '(-2*x, -x, z)',
3661 '(-x, x, -z)', '(2*x, x, -z)', '(-x, -2*x, -z)')),
3662 '12j': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3663 '(y, x, -z)', '(x-y, -y, -z)', '(-x, -x+y, -z)',
3664 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
3665 '(-y, -x, z)', '(-x+y, y, z)', '(x, x-y, z)'))},
3666 '165': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
3667 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
3668 '4c': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
3669 '(0, 0, z+1/2)')),
3670 '4d': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, -z+1/2)',
3671 '(2/3, 1/3, -z)', '(1/3, 2/3, z+1/2)')),
3672 '6e': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
3673 '(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 1/2)'
3674 )),
3675 '6f': (1, ('(x, 0, 1/4)', '(0, x, 1/4)', '(-x, -x, 1/4)',
3676 '(-x, 0, 3/4)', '(0, -x, 3/4)', '(x, x, 3/4)')),
3677 '12g': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3678 '(y, x, -z+1/2)', '(x-y, -y, -z+1/2)',
3679 '(-x, -x+y, -z+1/2)', '(-x, -y, -z)',
3680 '(y, -x+y, -z)', '(x-y, x, -z)', '(-y, -x, z+1/2)',
3681 '(-x+y, y, z+1/2)', '(x, x-y, z+1/2)'))},
3682 '166:H': {'3a': (0, ('(0, 0, 0)', '(2/3, 1/3, 1/3)', '(1/3, 2/3, 2/3)'
3683 )),
3684 '3b': (0, ('(0, 0, 1/2)', '(2/3, 1/3, 5/6)',
3685 '(1/3, 2/3, 1/6)')),
3686 '6c': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3687 '(1/3, 2/3, z+2/3)', '(0, 0, -z)',
3688 '(2/3, 1/3, -z+1/3)', '(1/3, 2/3, -z+2/3)')),
3689 '9d': (0, ('(1/2, 0, 1/2)', '(1/6, 1/3, 5/6)',
3690 '(5/6, 2/3, 1/6)', '(0, 1/2, 1/2)',
3691 '(2/3, 5/6, 5/6)', '(1/3, 1/6, 1/6)',
3692 '(1/2, 1/2, 1/2)', '(1/6, 5/6, 5/6)',
3693 '(5/6, 1/6, 1/6)')),
3694 '9e': (0, ('(1/2, 0, 0)', '(1/6, 1/3, 1/3)',
3695 '(5/6, 2/3, 2/3)', '(0, 1/2, 0)',
3696 '(2/3, 5/6, 1/3)', '(1/3, 1/6, 2/3)',
3697 '(1/2, 1/2, 0)', '(1/6, 5/6, 1/3)',
3698 '(5/6, 1/6, 2/3)')),
3699 '18f': (1, ('(x, 0, 0)', '(x+2/3, 1/3, 1/3)',
3700 '(x+1/3, 2/3, 2/3)', '(0, x, 0)',
3701 '(2/3, x+1/3, 1/3)', '(1/3, x+2/3, 2/3)',
3702 '(-x, -x, 0)', '(-x+2/3, -x+1/3, 1/3)',
3703 '(-x+1/3, -x+2/3, 2/3)', '(-x, 0, 0)',
3704 '(-x+2/3, 1/3, 1/3)', '(-x+1/3, 2/3, 2/3)',
3705 '(0, -x, 0)', '(2/3, -x+1/3, 1/3)',
3706 '(1/3, -x+2/3, 2/3)', '(x, x, 0)',
3707 '(x+2/3, x+1/3, 1/3)', '(x+1/3, x+2/3, 2/3)')),
3708 '18g': (1, ('(x, 0, 1/2)', '(x+2/3, 1/3, 5/6)',
3709 '(x+1/3, 2/3, 1/6)', '(0, x, 1/2)',
3710 '(2/3, x+1/3, 5/6)', '(1/3, x+2/3, 1/6)',
3711 '(-x, -x, 1/2)', '(-x+2/3, -x+1/3, 5/6)',
3712 '(-x+1/3, -x+2/3, 1/6)', '(-x, 0, 1/2)',
3713 '(-x+2/3, 1/3, 5/6)', '(-x+1/3, 2/3, 1/6)',
3714 '(0, -x, 1/2)', '(2/3, -x+1/3, 5/6)',
3715 '(1/3, -x+2/3, 1/6)', '(x, x, 1/2)',
3716 '(x+2/3, x+1/3, 5/6)', '(x+1/3, x+2/3, 1/6)')),
3717 '18h': (5, ('(x, -x, z)', '(x+2/3, -x+1/3, z+1/3)',
3718 '(x+1/3, -x+2/3, z+2/3)', '(x, 2*x, z)',
3719 '(x+2/3, 2*x+1/3, z+1/3)',
3720 '(x+1/3, 2*x+2/3, z+2/3)', '(-2*x, -x, z)',
3721 '(-2*x+2/3, -x+1/3, z+1/3)',
3722 '(-2*x+1/3, -x+2/3, z+2/3)', '(-x, x, -z)',
3723 '(-x+2/3, x+1/3, -z+1/3)',
3724 '(-x+1/3, x+2/3, -z+2/3)', '(2*x, x, -z)',
3725 '(2*x+2/3, x+1/3, -z+1/3)',
3726 '(2*x+1/3, x+2/3, -z+2/3)', '(-x, -2*x, -z)',
3727 '(-x+2/3, -2*x+1/3, -z+1/3)',
3728 '(-x+1/3, -2*x+2/3, -z+2/3)')),
3729 '36i': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3730 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3731 '(-y+2/3, x-y+1/3, z+1/3)',
3732 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3733 '(-x+y+2/3, -x+1/3, z+1/3)',
3734 '(-x+y+1/3, -x+2/3, z+2/3)', '(y, x, -z)',
3735 '(y+2/3, x+1/3, -z+1/3)',
3736 '(y+1/3, x+2/3, -z+2/3)', '(x-y, -y, -z)',
3737 '(x-y+2/3, -y+1/3, -z+1/3)',
3738 '(x-y+1/3, -y+2/3, -z+2/3)', '(-x, -x+y, -z)',
3739 '(-x+2/3, -x+y+1/3, -z+1/3)',
3740 '(-x+1/3, -x+y+2/3, -z+2/3)', '(-x, -y, -z)',
3741 '(-x+2/3, -y+1/3, -z+1/3)',
3742 '(-x+1/3, -y+2/3, -z+2/3)', '(y, -x+y, -z)',
3743 '(y+2/3, -x+y+1/3, -z+1/3)',
3744 '(y+1/3, -x+y+2/3, -z+2/3)', '(x-y, x, -z)',
3745 '(x-y+2/3, x+1/3, -z+1/3)',
3746 '(x-y+1/3, x+2/3, -z+2/3)', '(-y, -x, z)',
3747 '(-y+2/3, -x+1/3, z+1/3)',
3748 '(-y+1/3, -x+2/3, z+2/3)', '(-x+y, y, z)',
3749 '(-x+y+2/3, y+1/3, z+1/3)',
3750 '(-x+y+1/3, y+2/3, z+2/3)', '(x, x-y, z)',
3751 '(x+2/3, x-y+1/3, z+1/3)',
3752 '(x+1/3, x-y+2/3, z+2/3)'))},
3753 '166:R': {'1a': (0, ('(0, 0, 0)', )),
3754 '1b': (0, ('(1/2, 1/2, 1/2)', )),
3755 '2c': (1, ('(x, x, x)', '(-x, -x, -x)')),
3756 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
3757 '3e': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)'
3758 )),
3759 '6f': (1, ('(x, -x, 0)', '(0, x, -x)', '(-x, 0, x)',
3760 '(-x, x, 0)', '(0, -x, x)', '(x, 0, -x)')),
3761 '6g': (1, ('(x, -x, 1/2)', '(1/2, x, -x)', '(-x, 1/2, x)',
3762 '(-x, x, 1/2)', '(1/2, -x, x)', '(x, 1/2, -x)')),
3763 '6h': (5, ('(x, x, z)', '(z, x, x)', '(x, z, x)',
3764 '(-z, -x, -x)', '(-x, -x, -z)', '(-x, -z, -x)')),
3765 '12i': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)',
3766 '(-z, -y, -x)', '(-y, -x, -z)', '(-x, -z, -y)',
3767 '(-x, -y, -z)', '(-z, -x, -y)', '(-y, -z, -x)',
3768 '(z, y, x)', '(y, x, z)', '(x, z, y)'))},
3769 '167:H': {'6a': (0, ('(0, 0, 1/4)', '(2/3, 1/3, 7/12)',
3770 '(1/3, 2/3, 11/12)', '(0, 0, 3/4)',
3771 '(2/3, 1/3, 1/12)', '(1/3, 2/3, 5/12)')),
3772 '6b': (0, ('(0, 0, 0)', '(2/3, 1/3, 1/3)', '(1/3, 2/3, 2/3)',
3773 '(0, 0, 1/2)', '(2/3, 1/3, 5/6)',
3774 '(1/3, 2/3, 1/6)')),
3775 '12c': (4, ('(0, 0, z)', '(2/3, 1/3, z+1/3)',
3776 '(1/3, 2/3, z+2/3)', '(0, 0, -z+1/2)',
3777 '(2/3, 1/3, -z+5/6)', '(1/3, 2/3, -z+1/6)',
3778 '(0, 0, -z)', '(2/3, 1/3, -z+1/3)',
3779 '(1/3, 2/3, -z+2/3)', '(0, 0, z+1/2)',
3780 '(2/3, 1/3, z+5/6)', '(1/3, 2/3, z+1/6)')),
3781 '18d': (0, ('(1/2, 0, 0)', '(1/6, 1/3, 1/3)',
3782 '(5/6, 2/3, 2/3)', '(0, 1/2, 0)',
3783 '(2/3, 5/6, 1/3)', '(1/3, 1/6, 2/3)',
3784 '(1/2, 1/2, 0)', '(1/6, 5/6, 1/3)',
3785 '(5/6, 1/6, 2/3)', '(0, 1/2, 1/2)',
3786 '(2/3, 5/6, 5/6)', '(1/3, 1/6, 1/6)',
3787 '(1/2, 0, 1/2)', '(1/6, 1/3, 5/6)',
3788 '(5/6, 2/3, 1/6)', '(1/2, 1/2, 1/2)',
3789 '(1/6, 5/6, 5/6)', '(5/6, 1/6, 1/6)')),
3790 '18e': (1, ('(x, 0, 1/4)', '(x+2/3, 1/3, 7/12)',
3791 '(x+1/3, 2/3, 11/12)', '(0, x, 1/4)',
3792 '(2/3, x+1/3, 7/12)', '(1/3, x+2/3, 11/12)',
3793 '(-x, -x, 1/4)', '(-x+2/3, -x+1/3, 7/12)',
3794 '(-x+1/3, -x+2/3, 11/12)', '(-x, 0, 3/4)',
3795 '(-x+2/3, 1/3, 1/12)', '(-x+1/3, 2/3, 5/12)',
3796 '(0, -x, 3/4)', '(2/3, -x+1/3, 1/12)',
3797 '(1/3, -x+2/3, 5/12)', '(x, x, 3/4)',
3798 '(x+2/3, x+1/3, 1/12)', '(x+1/3, x+2/3, 5/12)')),
3799 '36f': (7, ('(x, y, z)', '(x+2/3, y+1/3, z+1/3)',
3800 '(x+1/3, y+2/3, z+2/3)', '(-y, x-y, z)',
3801 '(-y+2/3, x-y+1/3, z+1/3)',
3802 '(-y+1/3, x-y+2/3, z+2/3)', '(-x+y, -x, z)',
3803 '(-x+y+2/3, -x+1/3, z+1/3)',
3804 '(-x+y+1/3, -x+2/3, z+2/3)', '(y, x, -z+1/2)',
3805 '(y+2/3, x+1/3, -z+5/6)',
3806 '(y+1/3, x+2/3, -z+1/6)', '(x-y, -y, -z+1/2)',
3807 '(x-y+2/3, -y+1/3, -z+5/6)',
3808 '(x-y+1/3, -y+2/3, -z+1/6)', '(-x, -x+y, -z+1/2)',
3809 '(-x+2/3, -x+y+1/3, -z+5/6)',
3810 '(-x+1/3, -x+y+2/3, -z+1/6)', '(-x, -y, -z)',
3811 '(-x+2/3, -y+1/3, -z+1/3)',
3812 '(-x+1/3, -y+2/3, -z+2/3)', '(y, -x+y, -z)',
3813 '(y+2/3, -x+y+1/3, -z+1/3)',
3814 '(y+1/3, -x+y+2/3, -z+2/3)', '(x-y, x, -z)',
3815 '(x-y+2/3, x+1/3, -z+1/3)',
3816 '(x-y+1/3, x+2/3, -z+2/3)', '(-y, -x, z+1/2)',
3817 '(-y+2/3, -x+1/3, z+5/6)',
3818 '(-y+1/3, -x+2/3, z+1/6)', '(-x+y, y, z+1/2)',
3819 '(-x+y+2/3, y+1/3, z+5/6)',
3820 '(-x+y+1/3, y+2/3, z+1/6)', '(x, x-y, z+1/2)',
3821 '(x+2/3, x-y+1/3, z+5/6)',
3822 '(x+1/3, x-y+2/3, z+1/6)'))},
3823 '167:R': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
3824 '2b': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
3825 '4c': (1, ('(x, x, x)', '(-x+1/2, -x+1/2, -x+1/2)',
3826 '(-x, -x, -x)', '(x+1/2, x+1/2, x+1/2)')),
3827 '6d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)',
3828 '(1/2, 1/2, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)'
3829 )),
3830 '6e': (1, ('(x, -x+1/2, 1/4)', '(1/4, x, -x+1/2)',
3831 '(-x+1/2, 1/4, x)', '(-x, x+1/2, 3/4)',
3832 '(3/4, -x, x+1/2)', '(x+1/2, 3/4, -x)')),
3833 '12f': (7, ('(x, y, z)', '(z, x, y)', '(y, z, x)',
3834 '(-z+1/2, -y+1/2, -x+1/2)',
3835 '(-y+1/2, -x+1/2, -z+1/2)',
3836 '(-x+1/2, -z+1/2, -y+1/2)', '(-x, -y, -z)',
3837 '(-z, -x, -y)', '(-y, -z, -x)',
3838 '(z+1/2, y+1/2, x+1/2)', '(y+1/2, x+1/2, z+1/2)',
3839 '(x+1/2, z+1/2, y+1/2)'))},
3840 '168': {'1a': (4, ('(0, 0, z)', )),
3841 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)')),
3842 '3c': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)')),
3843 '6d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3844 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)'))},
3845 '169': {'6a': (7, ('(x, y, z)', '(-y, x-y, z+1/3)', '(-x+y, -x, z+2/3)',
3846 '(-x, -y, z+1/2)', '(y, -x+y, z+5/6)',
3847 '(x-y, x, z+1/6)'))},
3848 '170': {'6a': (7, ('(x, y, z)', '(-y, x-y, z+2/3)', '(-x+y, -x, z+1/3)',
3849 '(-x, -y, z+1/2)', '(y, -x+y, z+1/6)',
3850 '(x-y, x, z+5/6)'))},
3851 '171': {'3a': (4, ('(0, 0, z)', '(0, 0, z+2/3)', '(0, 0, z+1/3)')),
3852 '3b': (4, ('(1/2, 1/2, z)', '(1/2, 0, z+2/3)',
3853 '(0, 1/2, z+1/3)')),
3854 '6c': (7, ('(x, y, z)', '(-y, x-y, z+2/3)', '(-x+y, -x, z+1/3)',
3855 '(-x, -y, z)', '(y, -x+y, z+2/3)', '(x-y, x, z+1/3)'
3856 ))},
3857 '172': {'3a': (4, ('(0, 0, z)', '(0, 0, z+1/3)', '(0, 0, z+2/3)')),
3858 '3b': (4, ('(1/2, 1/2, z)', '(1/2, 0, z+1/3)',
3859 '(0, 1/2, z+2/3)')),
3860 '6c': (7, ('(x, y, z)', '(-y, x-y, z+1/3)', '(-x+y, -x, z+2/3)',
3861 '(-x, -y, z)', '(y, -x+y, z+1/3)', '(x-y, x, z+2/3)'
3862 ))},
3863 '173': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
3864 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)')),
3865 '6c': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3866 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
3867 '(x-y, x, z+1/2)'))},
3868 '174': {'1a': (0, ('(0, 0, 0)', )),
3869 '1b': (0, ('(0, 0, 1/2)', )),
3870 '1c': (0, ('(1/3, 2/3, 0)', )),
3871 '1d': (0, ('(1/3, 2/3, 1/2)', )),
3872 '1e': (0, ('(2/3, 1/3, 0)', )),
3873 '1f': (0, ('(2/3, 1/3, 1/2)', )),
3874 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
3875 '2h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z)')),
3876 '2i': (4, ('(2/3, 1/3, z)', '(2/3, 1/3, -z)')),
3877 '3j': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)')),
3878 '3k': (3, ('(x, y, 1/2)', '(-y, x-y, 1/2)', '(-x+y, -x, 1/2)')),
3879 '6l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3880 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)'))},
3881 '175': {'1a': (0, ('(0, 0, 0)', )),
3882 '1b': (0, ('(0, 0, 1/2)', )),
3883 '2c': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)')),
3884 '2d': (0, ('(1/3, 2/3, 1/2)', '(2/3, 1/3, 1/2)')),
3885 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
3886 '3f': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
3887 '3g': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3888 )),
3889 '4h': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)', '(2/3, 1/3, -z)',
3890 '(1/3, 2/3, -z)')),
3891 '6i': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)',
3892 '(1/2, 0, -z)', '(0, 1/2, -z)', '(1/2, 1/2, -z)')),
3893 '6j': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)',
3894 '(-x, -y, 0)', '(y, -x+y, 0)', '(x-y, x, 0)')),
3895 '6k': (3, ('(x, y, 1/2)', '(-y, x-y, 1/2)', '(-x+y, -x, 1/2)',
3896 '(-x, -y, 1/2)', '(y, -x+y, 1/2)', '(x-y, x, 1/2)')),
3897 '12l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3898 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
3899 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
3900 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)'))},
3901 '176': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
3902 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
3903 '2c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)')),
3904 '2d': (0, ('(2/3, 1/3, 1/4)', '(1/3, 2/3, 3/4)')),
3905 '4e': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
3906 '(0, 0, -z+1/2)')),
3907 '4f': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)',
3908 '(2/3, 1/3, -z)', '(1/3, 2/3, -z+1/2)')),
3909 '6g': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
3910 '(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3911 )),
3912 '6h': (3, ('(x, y, 1/4)', '(-y, x-y, 1/4)', '(-x+y, -x, 1/4)',
3913 '(-x, -y, 3/4)', '(y, -x+y, 3/4)', '(x-y, x, 3/4)')),
3914 '12i': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3915 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
3916 '(x-y, x, z+1/2)', '(-x, -y, -z)', '(y, -x+y, -z)',
3917 '(x-y, x, -z)', '(x, y, -z+1/2)',
3918 '(-y, x-y, -z+1/2)', '(-x+y, -x, -z+1/2)'))},
3919 '177': {'1a': (0, ('(0, 0, 0)', )),
3920 '1b': (0, ('(0, 0, 1/2)', )),
3921 '2c': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)')),
3922 '2d': (0, ('(1/3, 2/3, 1/2)', '(2/3, 1/3, 1/2)')),
3923 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
3924 '3f': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
3925 '3g': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
3926 )),
3927 '4h': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)', '(2/3, 1/3, -z)',
3928 '(1/3, 2/3, -z)')),
3929 '6i': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)',
3930 '(0, 1/2, -z)', '(1/2, 0, -z)', '(1/2, 1/2, -z)')),
3931 '6j': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
3932 '(-x, 0, 0)', '(0, -x, 0)', '(x, x, 0)')),
3933 '6k': (1, ('(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)',
3934 '(-x, 0, 1/2)', '(0, -x, 1/2)', '(x, x, 1/2)')),
3935 '6l': (1, ('(x, -x, 0)', '(x, 2*x, 0)', '(-2*x, -x, 0)',
3936 '(-x, x, 0)', '(-x, -2*x, 0)', '(2*x, x, 0)')),
3937 '6m': (1, ('(x, -x, 1/2)', '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)',
3938 '(-x, x, 1/2)', '(-x, -2*x, 1/2)', '(2*x, x, 1/2)')),
3939 '12n': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
3940 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
3941 '(y, x, -z)', '(x-y, -y, -z)', '(-x, -x+y, -z)',
3942 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)'))},
3943 '178': {'6a': (1, ('(x, 0, 0)', '(0, x, 1/3)', '(-x, -x, 2/3)',
3944 '(-x, 0, 1/2)', '(0, -x, 5/6)', '(x, x, 1/6)')),
3945 '6b': (1, ('(x, 2*x, 1/4)', '(-2*x, -x, 7/12)',
3946 '(x, -x, 11/12)', '(-x, -2*x, 3/4)',
3947 '(2*x, x, 1/12)', '(-x, x, 5/12)')),
3948 '12c': (7, ('(x, y, z)', '(-y, x-y, z+1/3)',
3949 '(-x+y, -x, z+2/3)', '(-x, -y, z+1/2)',
3950 '(y, -x+y, z+5/6)', '(x-y, x, z+1/6)',
3951 '(y, x, -z+1/3)', '(x-y, -y, -z)',
3952 '(-x, -x+y, -z+2/3)', '(-y, -x, -z+5/6)',
3953 '(-x+y, y, -z+1/2)', '(x, x-y, -z+1/6)'))},
3954 '179': {'6a': (1, ('(x, 0, 0)', '(0, x, 2/3)', '(-x, -x, 1/3)',
3955 '(-x, 0, 1/2)', '(0, -x, 1/6)', '(x, x, 5/6)')),
3956 '6b': (1, ('(x, 2*x, 3/4)', '(-2*x, -x, 5/12)', '(x, -x, 1/12)',
3957 '(-x, -2*x, 1/4)', '(2*x, x, 11/12)',
3958 '(-x, x, 7/12)')),
3959 '12c': (7, ('(x, y, z)', '(-y, x-y, z+2/3)',
3960 '(-x+y, -x, z+1/3)', '(-x, -y, z+1/2)',
3961 '(y, -x+y, z+1/6)', '(x-y, x, z+5/6)',
3962 '(y, x, -z+2/3)', '(x-y, -y, -z)',
3963 '(-x, -x+y, -z+1/3)', '(-y, -x, -z+1/6)',
3964 '(-x+y, y, -z+1/2)', '(x, x-y, -z+5/6)'))},
3965 '180': {'3a': (0, ('(0, 0, 0)', '(0, 0, 2/3)', '(0, 0, 1/3)')),
3966 '3b': (0, ('(0, 0, 1/2)', '(0, 0, 1/6)', '(0, 0, 5/6)')),
3967 '3c': (0, ('(1/2, 0, 0)', '(0, 1/2, 2/3)', '(1/2, 1/2, 1/3)')),
3968 '3d': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/6)', '(1/2, 1/2, 5/6)'
3969 )),
3970 '6e': (4, ('(0, 0, z)', '(0, 0, z+2/3)', '(0, 0, z+1/3)',
3971 '(0, 0, -z+2/3)', '(0, 0, -z)', '(0, 0, -z+1/3)')),
3972 '6f': (4, ('(1/2, 0, z)', '(0, 1/2, z+2/3)',
3973 '(1/2, 1/2, z+1/3)', '(0, 1/2, -z+2/3)',
3974 '(1/2, 0, -z)', '(1/2, 1/2, -z+1/3)')),
3975 '6g': (1, ('(x, 0, 0)', '(0, x, 2/3)', '(-x, -x, 1/3)',
3976 '(-x, 0, 0)', '(0, -x, 2/3)', '(x, x, 1/3)')),
3977 '6h': (1, ('(x, 0, 1/2)', '(0, x, 1/6)', '(-x, -x, 5/6)',
3978 '(-x, 0, 1/2)', '(0, -x, 1/6)', '(x, x, 5/6)')),
3979 '6i': (1, ('(x, 2*x, 0)', '(-2*x, -x, 2/3)', '(x, -x, 1/3)',
3980 '(-x, -2*x, 0)', '(2*x, x, 2/3)', '(-x, x, 1/3)')),
3981 '6j': (1, ('(x, 2*x, 1/2)', '(-2*x, -x, 1/6)', '(x, -x, 5/6)',
3982 '(-x, -2*x, 1/2)', '(2*x, x, 1/6)', '(-x, x, 5/6)')),
3983 '12k': (7, ('(x, y, z)', '(-y, x-y, z+2/3)',
3984 '(-x+y, -x, z+1/3)', '(-x, -y, z)',
3985 '(y, -x+y, z+2/3)', '(x-y, x, z+1/3)',
3986 '(y, x, -z+2/3)', '(x-y, -y, -z)',
3987 '(-x, -x+y, -z+1/3)', '(-y, -x, -z+2/3)',
3988 '(-x+y, y, -z)', '(x, x-y, -z+1/3)'))},
3989 '181': {'3a': (0, ('(0, 0, 0)', '(0, 0, 1/3)', '(0, 0, 2/3)')),
3990 '3b': (0, ('(0, 0, 1/2)', '(0, 0, 5/6)', '(0, 0, 1/6)')),
3991 '3c': (0, ('(1/2, 0, 0)', '(0, 1/2, 1/3)', '(1/2, 1/2, 2/3)')),
3992 '3d': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 5/6)', '(1/2, 1/2, 1/6)'
3993 )),
3994 '6e': (4, ('(0, 0, z)', '(0, 0, z+1/3)', '(0, 0, z+2/3)',
3995 '(0, 0, -z+1/3)', '(0, 0, -z)', '(0, 0, -z+2/3)')),
3996 '6f': (4, ('(1/2, 0, z)', '(0, 1/2, z+1/3)',
3997 '(1/2, 1/2, z+2/3)', '(0, 1/2, -z+1/3)',
3998 '(1/2, 0, -z)', '(1/2, 1/2, -z+2/3)')),
3999 '6g': (1, ('(x, 0, 0)', '(0, x, 1/3)', '(-x, -x, 2/3)',
4000 '(-x, 0, 0)', '(0, -x, 1/3)', '(x, x, 2/3)')),
4001 '6h': (1, ('(x, 0, 1/2)', '(0, x, 5/6)', '(-x, -x, 1/6)',
4002 '(-x, 0, 1/2)', '(0, -x, 5/6)', '(x, x, 1/6)')),
4003 '6i': (1, ('(x, 2*x, 0)', '(-2*x, -x, 1/3)', '(x, -x, 2/3)',
4004 '(-x, -2*x, 0)', '(2*x, x, 1/3)', '(-x, x, 2/3)')),
4005 '6j': (1, ('(x, 2*x, 1/2)', '(-2*x, -x, 5/6)', '(x, -x, 1/6)',
4006 '(-x, -2*x, 1/2)', '(2*x, x, 5/6)', '(-x, x, 1/6)')),
4007 '12k': (7, ('(x, y, z)', '(-y, x-y, z+1/3)',
4008 '(-x+y, -x, z+2/3)', '(-x, -y, z)',
4009 '(y, -x+y, z+1/3)', '(x-y, x, z+2/3)',
4010 '(y, x, -z+1/3)', '(x-y, -y, -z)',
4011 '(-x, -x+y, -z+2/3)', '(-y, -x, -z+1/3)',
4012 '(-x+y, y, -z)', '(x, x-y, -z+2/3)'))},
4013 '182': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4014 '2b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4015 '2c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)')),
4016 '2d': (0, ('(1/3, 2/3, 3/4)', '(2/3, 1/3, 1/4)')),
4017 '4e': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
4018 '(0, 0, -z+1/2)')),
4019 '4f': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)',
4020 '(2/3, 1/3, -z)', '(1/3, 2/3, -z+1/2)')),
4021 '6g': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
4022 '(-x, 0, 1/2)', '(0, -x, 1/2)', '(x, x, 1/2)')),
4023 '6h': (1, ('(x, 2*x, 1/4)', '(-2*x, -x, 1/4)', '(x, -x, 1/4)',
4024 '(-x, -2*x, 3/4)', '(2*x, x, 3/4)', '(-x, x, 3/4)')),
4025 '12i': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4026 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
4027 '(x-y, x, z+1/2)', '(y, x, -z)', '(x-y, -y, -z)',
4028 '(-x, -x+y, -z)', '(-y, -x, -z+1/2)',
4029 '(-x+y, y, -z+1/2)', '(x, x-y, -z+1/2)'))},
4030 '183': {'1a': (4, ('(0, 0, z)', )),
4031 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)')),
4032 '3c': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)')),
4033 '6d': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
4034 '(-x, 0, z)', '(0, -x, z)', '(x, x, z)')),
4035 '6e': (5, ('(x, -x, z)', '(x, 2*x, z)', '(-2*x, -x, z)',
4036 '(-x, x, z)', '(-x, -2*x, z)', '(2*x, x, z)')),
4037 '12f': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4038 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
4039 '(-y, -x, z)', '(-x+y, y, z)', '(x, x-y, z)',
4040 '(y, x, z)', '(x-y, -y, z)', '(-x, -x+y, z)'))},
4041 '184': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
4042 '4b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)',
4043 '(1/3, 2/3, z+1/2)', '(2/3, 1/3, z+1/2)')),
4044 '6c': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)',
4045 '(0, 1/2, z+1/2)', '(1/2, 0, z+1/2)',
4046 '(1/2, 1/2, z+1/2)')),
4047 '12d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4048 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
4049 '(-y, -x, z+1/2)', '(-x+y, y, z+1/2)',
4050 '(x, x-y, z+1/2)', '(y, x, z+1/2)',
4051 '(x-y, -y, z+1/2)', '(-x, -x+y, z+1/2)'))},
4052 '185': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
4053 '4b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)',
4054 '(1/3, 2/3, z+1/2)', '(2/3, 1/3, z)')),
4055 '6c': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
4056 '(-x, 0, z+1/2)', '(0, -x, z+1/2)', '(x, x, z+1/2)'
4057 )),
4058 '12d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4059 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
4060 '(x-y, x, z+1/2)', '(-y, -x, z+1/2)',
4061 '(-x+y, y, z+1/2)', '(x, x-y, z+1/2)', '(y, x, z)',
4062 '(x-y, -y, z)', '(-x, -x+y, z)'))},
4063 '186': {'2a': (4, ('(0, 0, z)', '(0, 0, z+1/2)')),
4064 '2b': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)')),
4065 '6c': (5, ('(x, -x, z)', '(x, 2*x, z)', '(-2*x, -x, z)',
4066 '(-x, x, z+1/2)', '(-x, -2*x, z+1/2)',
4067 '(2*x, x, z+1/2)')),
4068 '12d': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4069 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
4070 '(x-y, x, z+1/2)', '(-y, -x, z)', '(-x+y, y, z)',
4071 '(x, x-y, z)', '(y, x, z+1/2)', '(x-y, -y, z+1/2)',
4072 '(-x, -x+y, z+1/2)'))},
4073 '187': {'1a': (0, ('(0, 0, 0)', )),
4074 '1b': (0, ('(0, 0, 1/2)', )),
4075 '1c': (0, ('(1/3, 2/3, 0)', )),
4076 '1d': (0, ('(1/3, 2/3, 1/2)', )),
4077 '1e': (0, ('(2/3, 1/3, 0)', )),
4078 '1f': (0, ('(2/3, 1/3, 1/2)', )),
4079 '2g': (4, ('(0, 0, z)', '(0, 0, -z)')),
4080 '2h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z)')),
4081 '2i': (4, ('(2/3, 1/3, z)', '(2/3, 1/3, -z)')),
4082 '3j': (1, ('(x, -x, 0)', '(x, 2*x, 0)', '(-2*x, -x, 0)')),
4083 '3k': (1, ('(x, -x, 1/2)', '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)')),
4084 '6l': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)',
4085 '(-y, -x, 0)', '(-x+y, y, 0)', '(x, x-y, 0)')),
4086 '6m': (3, ('(x, y, 1/2)', '(-y, x-y, 1/2)', '(-x+y, -x, 1/2)',
4087 '(-y, -x, 1/2)', '(-x+y, y, 1/2)', '(x, x-y, 1/2)')),
4088 '6n': (5, ('(x, -x, z)', '(x, 2*x, z)', '(-2*x, -x, z)',
4089 '(x, -x, -z)', '(x, 2*x, -z)', '(-2*x, -x, -z)')),
4090 '12o': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4091 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)',
4092 '(-y, -x, z)', '(-x+y, y, z)', '(x, x-y, z)',
4093 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)'))},
4094 '188': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4095 '2b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4096 '2c': (0, ('(1/3, 2/3, 0)', '(1/3, 2/3, 1/2)')),
4097 '2d': (0, ('(1/3, 2/3, 1/4)', '(1/3, 2/3, 3/4)')),
4098 '2e': (0, ('(2/3, 1/3, 0)', '(2/3, 1/3, 1/2)')),
4099 '2f': (0, ('(2/3, 1/3, 1/4)', '(2/3, 1/3, 3/4)')),
4100 '4g': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, z+1/2)',
4101 '(0, 0, -z)')),
4102 '4h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z+1/2)',
4103 '(1/3, 2/3, z+1/2)', '(1/3, 2/3, -z)')),
4104 '4i': (4, ('(2/3, 1/3, z)', '(2/3, 1/3, -z+1/2)',
4105 '(2/3, 1/3, z+1/2)', '(2/3, 1/3, -z)')),
4106 '6j': (1, ('(x, -x, 0)', '(x, 2*x, 0)', '(-2*x, -x, 0)',
4107 '(x, -x, 1/2)', '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)')),
4108 '6k': (3, ('(x, y, 1/4)', '(-y, x-y, 1/4)', '(-x+y, -x, 1/4)',
4109 '(-y, -x, 3/4)', '(-x+y, y, 3/4)', '(x, x-y, 3/4)')),
4110 '12l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4111 '(x, y, -z+1/2)', '(-y, x-y, -z+1/2)',
4112 '(-x+y, -x, -z+1/2)', '(-y, -x, z+1/2)',
4113 '(-x+y, y, z+1/2)', '(x, x-y, z+1/2)',
4114 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)'))},
4115 '189': {'1a': (0, ('(0, 0, 0)', )),
4116 '1b': (0, ('(0, 0, 1/2)', )),
4117 '2c': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)')),
4118 '2d': (0, ('(1/3, 2/3, 1/2)', '(2/3, 1/3, 1/2)')),
4119 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
4120 '3f': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)')),
4121 '3g': (1, ('(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)')),
4122 '4h': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z)', '(2/3, 1/3, -z)',
4123 '(2/3, 1/3, z)')),
4124 '6i': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
4125 '(x, 0, -z)', '(0, x, -z)', '(-x, -x, -z)')),
4126 '6j': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)',
4127 '(y, x, 0)', '(x-y, -y, 0)', '(-x, -x+y, 0)')),
4128 '6k': (3, ('(x, y, 1/2)', '(-y, x-y, 1/2)', '(-x+y, -x, 1/2)',
4129 '(y, x, 1/2)', '(x-y, -y, 1/2)', '(-x, -x+y, 1/2)')),
4130 '12l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4131 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)',
4132 '(y, x, -z)', '(x-y, -y, -z)', '(-x, -x+y, -z)',
4133 '(y, x, z)', '(x-y, -y, z)', '(-x, -x+y, z)'))},
4134 '190': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4135 '2b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4136 '2c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)')),
4137 '2d': (0, ('(2/3, 1/3, 1/4)', '(1/3, 2/3, 3/4)')),
4138 '4e': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
4139 '(0, 0, z+1/2)')),
4140 '4f': (4, ('(1/3, 2/3, z)', '(1/3, 2/3, -z+1/2)',
4141 '(2/3, 1/3, -z)', '(2/3, 1/3, z+1/2)')),
4142 '6g': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
4143 '(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)')),
4144 '6h': (3, ('(x, y, 1/4)', '(-y, x-y, 1/4)', '(-x+y, -x, 1/4)',
4145 '(y, x, 3/4)', '(x-y, -y, 3/4)', '(-x, -x+y, 3/4)')),
4146 '12i': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4147 '(x, y, -z+1/2)', '(-y, x-y, -z+1/2)',
4148 '(-x+y, -x, -z+1/2)', '(y, x, -z)', '(x-y, -y, -z)',
4149 '(-x, -x+y, -z)', '(y, x, z+1/2)',
4150 '(x-y, -y, z+1/2)', '(-x, -x+y, z+1/2)'))},
4151 '191': {'1a': (0, ('(0, 0, 0)', )),
4152 '1b': (0, ('(0, 0, 1/2)', )),
4153 '2c': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)')),
4154 '2d': (0, ('(1/3, 2/3, 1/2)', '(2/3, 1/3, 1/2)')),
4155 '2e': (4, ('(0, 0, z)', '(0, 0, -z)')),
4156 '3f': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)')),
4157 '3g': (0, ('(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
4158 )),
4159 '4h': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)', '(2/3, 1/3, -z)',
4160 '(1/3, 2/3, -z)')),
4161 '6i': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)',
4162 '(0, 1/2, -z)', '(1/2, 0, -z)', '(1/2, 1/2, -z)')),
4163 '6j': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
4164 '(-x, 0, 0)', '(0, -x, 0)', '(x, x, 0)')),
4165 '6k': (1, ('(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)',
4166 '(-x, 0, 1/2)', '(0, -x, 1/2)', '(x, x, 1/2)')),
4167 '6l': (1, ('(x, 2*x, 0)', '(-2*x, -x, 0)', '(x, -x, 0)',
4168 '(-x, -2*x, 0)', '(2*x, x, 0)', '(-x, x, 0)')),
4169 '6m': (1, ('(x, 2*x, 1/2)', '(-2*x, -x, 1/2)', '(x, -x, 1/2)',
4170 '(-x, -2*x, 1/2)', '(2*x, x, 1/2)', '(-x, x, 1/2)')),
4171 '12n': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
4172 '(-x, 0, z)', '(0, -x, z)', '(x, x, z)',
4173 '(0, x, -z)', '(x, 0, -z)', '(-x, -x, -z)',
4174 '(0, -x, -z)', '(-x, 0, -z)', '(x, x, -z)')),
4175 '12o': (5, ('(x, 2*x, z)', '(-2*x, -x, z)', '(x, -x, z)',
4176 '(-x, -2*x, z)', '(2*x, x, z)', '(-x, x, z)',
4177 '(2*x, x, -z)', '(-x, -2*x, -z)', '(-x, x, -z)',
4178 '(-2*x, -x, -z)', '(x, 2*x, -z)', '(x, -x, -z)')),
4179 '12p': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)',
4180 '(-x, -y, 0)', '(y, -x+y, 0)', '(x-y, x, 0)',
4181 '(y, x, 0)', '(x-y, -y, 0)', '(-x, -x+y, 0)',
4182 '(-y, -x, 0)', '(-x+y, y, 0)', '(x, x-y, 0)')),
4183 '12q': (3, ('(x, y, 1/2)', '(-y, x-y, 1/2)', '(-x+y, -x, 1/2)',
4184 '(-x, -y, 1/2)', '(y, -x+y, 1/2)', '(x-y, x, 1/2)',
4185 '(y, x, 1/2)', '(x-y, -y, 1/2)', '(-x, -x+y, 1/2)',
4186 '(-y, -x, 1/2)', '(-x+y, y, 1/2)', '(x, x-y, 1/2)'
4187 )),
4188 '24r': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4189 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
4190 '(y, x, -z)', '(x-y, -y, -z)', '(-x, -x+y, -z)',
4191 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)',
4192 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
4193 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)',
4194 '(-y, -x, z)', '(-x+y, y, z)', '(x, x-y, z)',
4195 '(y, x, z)', '(x-y, -y, z)', '(-x, -x+y, z)'))},
4196 '192': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4197 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4198 '4c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 1/4)',
4199 '(2/3, 1/3, 3/4)', '(1/3, 2/3, 3/4)')),
4200 '4d': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 0)', '(2/3, 1/3, 1/2)',
4201 '(1/3, 2/3, 1/2)')),
4202 '4e': (4, ('(0, 0, z)', '(0, 0, -z+1/2)', '(0, 0, -z)',
4203 '(0, 0, z+1/2)')),
4204 '6f': (0, ('(1/2, 0, 1/4)', '(0, 1/2, 1/4)', '(1/2, 1/2, 1/4)',
4205 '(1/2, 0, 3/4)', '(0, 1/2, 3/4)', '(1/2, 1/2, 3/4)'
4206 )),
4207 '6g': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
4208 '(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 1/2)'
4209 )),
4210 '8h': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z)',
4211 '(2/3, 1/3, -z+1/2)', '(1/3, 2/3, -z+1/2)',
4212 '(2/3, 1/3, -z)', '(1/3, 2/3, -z)',
4213 '(1/3, 2/3, z+1/2)', '(2/3, 1/3, z+1/2)')),
4214 '12i': (4, ('(1/2, 0, z)', '(0, 1/2, z)', '(1/2, 1/2, z)',
4215 '(0, 1/2, -z+1/2)', '(1/2, 0, -z+1/2)',
4216 '(1/2, 1/2, -z+1/2)', '(1/2, 0, -z)',
4217 '(0, 1/2, -z)', '(1/2, 1/2, -z)', '(0, 1/2, z+1/2)',
4218 '(1/2, 0, z+1/2)', '(1/2, 1/2, z+1/2)')),
4219 '12j': (1, ('(x, 0, 1/4)', '(0, x, 1/4)', '(-x, -x, 1/4)',
4220 '(-x, 0, 1/4)', '(0, -x, 1/4)', '(x, x, 1/4)',
4221 '(-x, 0, 3/4)', '(0, -x, 3/4)', '(x, x, 3/4)',
4222 '(x, 0, 3/4)', '(0, x, 3/4)', '(-x, -x, 3/4)')),
4223 '12k': (1, ('(x, 2*x, 1/4)', '(-2*x, -x, 1/4)', '(x, -x, 1/4)',
4224 '(-x, -2*x, 1/4)', '(2*x, x, 1/4)', '(-x, x, 1/4)',
4225 '(-x, -2*x, 3/4)', '(2*x, x, 3/4)', '(-x, x, 3/4)',
4226 '(x, 2*x, 3/4)', '(-2*x, -x, 3/4)', '(x, -x, 3/4)'
4227 )),
4228 '12l': (3, ('(x, y, 0)', '(-y, x-y, 0)', '(-x+y, -x, 0)',
4229 '(-x, -y, 0)', '(y, -x+y, 0)', '(x-y, x, 0)',
4230 '(y, x, 1/2)', '(x-y, -y, 1/2)', '(-x, -x+y, 1/2)',
4231 '(-y, -x, 1/2)', '(-x+y, y, 1/2)', '(x, x-y, 1/2)'
4232 )),
4233 '24m': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4234 '(-x, -y, z)', '(y, -x+y, z)', '(x-y, x, z)',
4235 '(y, x, -z+1/2)', '(x-y, -y, -z+1/2)',
4236 '(-x, -x+y, -z+1/2)', '(-y, -x, -z+1/2)',
4237 '(-x+y, y, -z+1/2)', '(x, x-y, -z+1/2)',
4238 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
4239 '(x, y, -z)', '(-y, x-y, -z)', '(-x+y, -x, -z)',
4240 '(-y, -x, z+1/2)', '(-x+y, y, z+1/2)',
4241 '(x, x-y, z+1/2)', '(y, x, z+1/2)',
4242 '(x-y, -y, z+1/2)', '(-x, -x+y, z+1/2)'))},
4243 '193': {'2a': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4244 '2b': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4245 '4c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)',
4246 '(2/3, 1/3, 1/4)', '(1/3, 2/3, 3/4)')),
4247 '4d': (0, ('(1/3, 2/3, 0)', '(2/3, 1/3, 1/2)', '(2/3, 1/3, 0)',
4248 '(1/3, 2/3, 1/2)')),
4249 '4e': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z+1/2)',
4250 '(0, 0, -z)')),
4251 '6f': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
4252 '(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
4253 )),
4254 '6g': (1, ('(x, 0, 1/4)', '(0, x, 1/4)', '(-x, -x, 1/4)',
4255 '(-x, 0, 3/4)', '(0, -x, 3/4)', '(x, x, 3/4)')),
4256 '8h': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)',
4257 '(2/3, 1/3, -z+1/2)', '(1/3, 2/3, -z)',
4258 '(2/3, 1/3, -z)', '(1/3, 2/3, -z+1/2)',
4259 '(1/3, 2/3, z+1/2)', '(2/3, 1/3, z)')),
4260 '12i': (1, ('(x, 2*x, 0)', '(-2*x, -x, 0)', '(x, -x, 0)',
4261 '(-x, -2*x, 1/2)', '(2*x, x, 1/2)', '(-x, x, 1/2)',
4262 '(-x, -2*x, 0)', '(2*x, x, 0)', '(-x, x, 0)',
4263 '(x, 2*x, 1/2)', '(-2*x, -x, 1/2)', '(x, -x, 1/2)'
4264 )),
4265 '12j': (3, ('(x, y, 1/4)', '(-y, x-y, 1/4)', '(-x+y, -x, 1/4)',
4266 '(-x, -y, 3/4)', '(y, -x+y, 3/4)', '(x-y, x, 3/4)',
4267 '(y, x, 1/4)', '(x-y, -y, 1/4)', '(-x, -x+y, 1/4)',
4268 '(-y, -x, 3/4)', '(-x+y, y, 3/4)', '(x, x-y, 3/4)'
4269 )),
4270 '12k': (5, ('(x, 0, z)', '(0, x, z)', '(-x, -x, z)',
4271 '(-x, 0, z+1/2)', '(0, -x, z+1/2)', '(x, x, z+1/2)',
4272 '(0, x, -z+1/2)', '(x, 0, -z+1/2)',
4273 '(-x, -x, -z+1/2)', '(0, -x, -z)', '(-x, 0, -z)',
4274 '(x, x, -z)')),
4275 '24l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4276 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
4277 '(x-y, x, z+1/2)', '(y, x, -z+1/2)',
4278 '(x-y, -y, -z+1/2)', '(-x, -x+y, -z+1/2)',
4279 '(-y, -x, -z)', '(-x+y, y, -z)', '(x, x-y, -z)',
4280 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
4281 '(x, y, -z+1/2)', '(-y, x-y, -z+1/2)',
4282 '(-x+y, -x, -z+1/2)', '(-y, -x, z+1/2)',
4283 '(-x+y, y, z+1/2)', '(x, x-y, z+1/2)', '(y, x, z)',
4284 '(x-y, -y, z)', '(-x, -x+y, z)'))},
4285 '194': {'2a': (0, ('(0, 0, 0)', '(0, 0, 1/2)')),
4286 '2b': (0, ('(0, 0, 1/4)', '(0, 0, 3/4)')),
4287 '2c': (0, ('(1/3, 2/3, 1/4)', '(2/3, 1/3, 3/4)')),
4288 '2d': (0, ('(1/3, 2/3, 3/4)', '(2/3, 1/3, 1/4)')),
4289 '4e': (4, ('(0, 0, z)', '(0, 0, z+1/2)', '(0, 0, -z)',
4290 '(0, 0, -z+1/2)')),
4291 '4f': (4, ('(1/3, 2/3, z)', '(2/3, 1/3, z+1/2)',
4292 '(2/3, 1/3, -z)', '(1/3, 2/3, -z+1/2)')),
4293 '6g': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(1/2, 1/2, 0)',
4294 '(1/2, 0, 1/2)', '(0, 1/2, 1/2)', '(1/2, 1/2, 1/2)'
4295 )),
4296 '6h': (1, ('(x, 2*x, 1/4)', '(-2*x, -x, 1/4)', '(x, -x, 1/4)',
4297 '(-x, -2*x, 3/4)', '(2*x, x, 3/4)', '(-x, x, 3/4)')),
4298 '12i': (1, ('(x, 0, 0)', '(0, x, 0)', '(-x, -x, 0)',
4299 '(-x, 0, 1/2)', '(0, -x, 1/2)', '(x, x, 1/2)',
4300 '(-x, 0, 0)', '(0, -x, 0)', '(x, x, 0)',
4301 '(x, 0, 1/2)', '(0, x, 1/2)', '(-x, -x, 1/2)')),
4302 '12j': (3, ('(x, y, 1/4)', '(-y, x-y, 1/4)', '(-x+y, -x, 1/4)',
4303 '(-x, -y, 3/4)', '(y, -x+y, 3/4)', '(x-y, x, 3/4)',
4304 '(y, x, 3/4)', '(x-y, -y, 3/4)', '(-x, -x+y, 3/4)',
4305 '(-y, -x, 1/4)', '(-x+y, y, 1/4)', '(x, x-y, 1/4)'
4306 )),
4307 '12k': (5, ('(x, 2*x, z)', '(-2*x, -x, z)', '(x, -x, z)',
4308 '(-x, -2*x, z+1/2)', '(2*x, x, z+1/2)',
4309 '(-x, x, z+1/2)', '(2*x, x, -z)', '(-x, -2*x, -z)',
4310 '(-x, x, -z)', '(-2*x, -x, -z+1/2)',
4311 '(x, 2*x, -z+1/2)', '(x, -x, -z+1/2)')),
4312 '24l': (7, ('(x, y, z)', '(-y, x-y, z)', '(-x+y, -x, z)',
4313 '(-x, -y, z+1/2)', '(y, -x+y, z+1/2)',
4314 '(x-y, x, z+1/2)', '(y, x, -z)', '(x-y, -y, -z)',
4315 '(-x, -x+y, -z)', '(-y, -x, -z+1/2)',
4316 '(-x+y, y, -z+1/2)', '(x, x-y, -z+1/2)',
4317 '(-x, -y, -z)', '(y, -x+y, -z)', '(x-y, x, -z)',
4318 '(x, y, -z+1/2)', '(-y, x-y, -z+1/2)',
4319 '(-x+y, -x, -z+1/2)', '(-y, -x, z)', '(-x+y, y, z)',
4320 '(x, x-y, z)', '(y, x, z+1/2)', '(x-y, -y, z+1/2)',
4321 '(-x, -x+y, z+1/2)'))},
4322 '195': {'1a': (0, ('(0, 0, 0)', )),
4323 '1b': (0, ('(1/2, 1/2, 1/2)', )),
4324 '3c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
4325 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
4326 '4e': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
4327 '(x, -x, -x)')),
4328 '6f': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)',
4329 '(0, 0, x)', '(0, 0, -x)')),
4330 '6g': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
4331 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)')),
4332 '6h': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
4333 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)')),
4334 '6i': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
4335 '(1/2, -x, 1/2)', '(1/2, 1/2, x)', '(1/2, 1/2, -x)'
4336 )),
4337 '12j': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
4338 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
4339 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
4340 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)'))},
4341 '196': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
4342 '(1/2, 1/2, 0)')),
4343 '4b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
4344 '(0, 0, 1/2)')),
4345 '4c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
4346 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
4347 '4d': (0, ('(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
4348 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
4349 '16e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
4350 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
4351 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
4352 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
4353 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
4354 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
4355 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
4356 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)')),
4357 '24f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
4358 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
4359 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
4360 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
4361 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
4362 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
4363 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
4364 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
4365 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)')),
4366 '24g': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
4367 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
4368 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
4369 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
4370 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
4371 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
4372 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
4373 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
4374 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
4375 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
4376 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
4377 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)')),
4378 '48h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
4379 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
4380 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
4381 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
4382 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
4383 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
4384 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
4385 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
4386 '(z, x, y)', '(z, x+1/2, y+1/2)',
4387 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
4388 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
4389 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
4390 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
4391 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
4392 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
4393 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
4394 '(y, z, x)', '(y, z+1/2, x+1/2)',
4395 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
4396 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
4397 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
4398 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
4399 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
4400 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
4401 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)'))},
4402 '197': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
4403 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
4404 '(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
4405 '8c': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)', '(-x, -x, x)',
4406 '(-x+1/2, -x+1/2, x+1/2)', '(-x, x, -x)',
4407 '(-x+1/2, x+1/2, -x+1/2)', '(x, -x, -x)',
4408 '(x+1/2, -x+1/2, -x+1/2)')),
4409 '12d': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
4410 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
4411 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
4412 '(1/2, -x+1/2, 1/2)', '(0, 0, x)',
4413 '(1/2, 1/2, x+1/2)', '(0, 0, -x)',
4414 '(1/2, 1/2, -x+1/2)')),
4415 '12e': (1, ('(x, 1/2, 0)', '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
4416 '(-x+1/2, 0, 1/2)', '(0, x, 1/2)',
4417 '(1/2, x+1/2, 0)', '(0, -x, 1/2)',
4418 '(1/2, -x+1/2, 0)', '(1/2, 0, x)',
4419 '(0, 1/2, x+1/2)', '(1/2, 0, -x)',
4420 '(0, 1/2, -x+1/2)')),
4421 '24f': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
4422 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
4423 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
4424 '(x+1/2, -y+1/2, -z+1/2)', '(z, x, y)',
4425 '(z+1/2, x+1/2, y+1/2)', '(z, -x, -y)',
4426 '(z+1/2, -x+1/2, -y+1/2)', '(-z, -x, y)',
4427 '(-z+1/2, -x+1/2, y+1/2)', '(-z, x, -y)',
4428 '(-z+1/2, x+1/2, -y+1/2)', '(y, z, x)',
4429 '(y+1/2, z+1/2, x+1/2)', '(-y, z, -x)',
4430 '(-y+1/2, z+1/2, -x+1/2)', '(y, -z, -x)',
4431 '(y+1/2, -z+1/2, -x+1/2)', '(-y, -z, x)',
4432 '(-y+1/2, -z+1/2, x+1/2)'))},
4433 '198': {'4a': (1, ('(x, x, x)', '(-x+1/2, -x, x+1/2)',
4434 '(-x, x+1/2, -x+1/2)', '(x+1/2, -x+1/2, -x)')),
4435 '12b': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
4436 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)',
4437 '(z, x, y)', '(z+1/2, -x+1/2, -y)',
4438 '(-z+1/2, -x, y+1/2)', '(-z, x+1/2, -y+1/2)',
4439 '(y, z, x)', '(-y, z+1/2, -x+1/2)',
4440 '(y+1/2, -z+1/2, -x)', '(-y+1/2, -z, x+1/2)'))},
4441 '199': {'8a': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)',
4442 '(-x+1/2, -x, x+1/2)', '(-x, -x+1/2, x)',
4443 '(-x, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
4444 '(x+1/2, -x+1/2, -x)', '(x, -x, -x+1/2)')),
4445 '12b': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
4446 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(1/4, x, 0)',
4447 '(3/4, x+1/2, 1/2)', '(3/4, -x+1/2, 0)',
4448 '(1/4, -x, 1/2)', '(0, 1/4, x)',
4449 '(1/2, 3/4, x+1/2)', '(0, 3/4, -x+1/2)',
4450 '(1/2, 1/4, -x)')),
4451 '24c': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
4452 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
4453 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
4454 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
4455 '(z, x, y)', '(z+1/2, x+1/2, y+1/2)',
4456 '(z+1/2, -x+1/2, -y)', '(z, -x, -y+1/2)',
4457 '(-z+1/2, -x, y+1/2)', '(-z, -x+1/2, y)',
4458 '(-z, x+1/2, -y+1/2)', '(-z+1/2, x, -y)',
4459 '(y, z, x)', '(y+1/2, z+1/2, x+1/2)',
4460 '(-y, z+1/2, -x+1/2)', '(-y+1/2, z, -x)',
4461 '(y+1/2, -z+1/2, -x)', '(y, -z, -x+1/2)',
4462 '(-y+1/2, -z, x+1/2)', '(-y, -z+1/2, x)'))},
4463 '200': {'1a': (0, ('(0, 0, 0)', )),
4464 '1b': (0, ('(1/2, 1/2, 1/2)', )),
4465 '3c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
4466 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
4467 '6e': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)',
4468 '(0, 0, x)', '(0, 0, -x)')),
4469 '6f': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
4470 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)')),
4471 '6g': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
4472 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)')),
4473 '6h': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
4474 '(1/2, -x, 1/2)', '(1/2, 1/2, x)', '(1/2, 1/2, -x)'
4475 )),
4476 '8i': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
4477 '(x, -x, -x)', '(-x, -x, -x)', '(x, x, -x)',
4478 '(x, -x, x)', '(-x, x, x)')),
4479 '12j': (6, ('(0, y, z)', '(0, -y, z)', '(0, y, -z)',
4480 '(0, -y, -z)', '(z, 0, y)', '(z, 0, -y)',
4481 '(-z, 0, y)', '(-z, 0, -y)', '(y, z, 0)',
4482 '(-y, z, 0)', '(y, -z, 0)', '(-y, -z, 0)')),
4483 '12k': (6, ('(1/2, y, z)', '(1/2, -y, z)', '(1/2, y, -z)',
4484 '(1/2, -y, -z)', '(z, 1/2, y)', '(z, 1/2, -y)',
4485 '(-z, 1/2, y)', '(-z, 1/2, -y)', '(y, z, 1/2)',
4486 '(-y, z, 1/2)', '(y, -z, 1/2)', '(-y, -z, 1/2)')),
4487 '24l': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
4488 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
4489 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
4490 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
4491 '(-x, -y, -z)', '(x, y, -z)', '(x, -y, z)',
4492 '(-x, y, z)', '(-z, -x, -y)', '(-z, x, y)',
4493 '(z, x, -y)', '(z, -x, y)', '(-y, -z, -x)',
4494 '(y, -z, x)', '(-y, z, x)', '(y, z, -x)'))},
4495 '201:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
4496 '4b': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
4497 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)')),
4498 '4c': (0, ('(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
4499 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
4500 '6d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
4501 '(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
4502 '8e': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
4503 '(x, -x, -x)', '(-x+1/2, -x+1/2, -x+1/2)',
4504 '(x+1/2, x+1/2, -x+1/2)', '(x+1/2, -x+1/2, x+1/2)',
4505 '(-x+1/2, x+1/2, x+1/2)')),
4506 '12f': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
4507 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
4508 '(-x+1/2, 1/2, 1/2)', '(x+1/2, 1/2, 1/2)',
4509 '(1/2, -x+1/2, 1/2)', '(1/2, x+1/2, 1/2)',
4510 '(1/2, 1/2, -x+1/2)', '(1/2, 1/2, x+1/2)')),
4511 '12g': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
4512 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
4513 '(-x+1/2, 0, 1/2)', '(x+1/2, 0, 1/2)',
4514 '(1/2, -x+1/2, 0)', '(1/2, x+1/2, 0)',
4515 '(0, 1/2, -x+1/2)', '(0, 1/2, x+1/2)')),
4516 '24h': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
4517 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
4518 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
4519 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
4520 '(-x+1/2, -y+1/2, -z+1/2)',
4521 '(x+1/2, y+1/2, -z+1/2)',
4522 '(x+1/2, -y+1/2, z+1/2)',
4523 '(-x+1/2, y+1/2, z+1/2)',
4524 '(-z+1/2, -x+1/2, -y+1/2)',
4525 '(-z+1/2, x+1/2, y+1/2)',
4526 '(z+1/2, x+1/2, -y+1/2)',
4527 '(z+1/2, -x+1/2, y+1/2)',
4528 '(-y+1/2, -z+1/2, -x+1/2)',
4529 '(y+1/2, -z+1/2, x+1/2)',
4530 '(-y+1/2, z+1/2, x+1/2)',
4531 '(y+1/2, z+1/2, -x+1/2)'))},
4532 '201:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
4533 '4b': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
4534 '(0, 1/2, 1/2)')),
4535 '4c': (0, ('(1/2, 1/2, 1/2)', '(0, 0, 1/2)', '(0, 1/2, 0)',
4536 '(1/2, 0, 0)')),
4537 '6d': (0, ('(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
4538 '(3/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)',
4539 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
4540 '8e': (1, ('(x, x, x)', '(-x+1/2, -x+1/2, x)',
4541 '(-x+1/2, x, -x+1/2)', '(x, -x+1/2, -x+1/2)',
4542 '(-x, -x, -x)', '(x+1/2, x+1/2, -x)',
4543 '(x+1/2, -x, x+1/2)', '(-x, x+1/2, x+1/2)')),
4544 '12f': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
4545 '(1/4, x, 1/4)', '(1/4, -x+1/2, 1/4)',
4546 '(1/4, 1/4, x)', '(1/4, 1/4, -x+1/2)',
4547 '(-x, 3/4, 3/4)', '(x+1/2, 3/4, 3/4)',
4548 '(3/4, -x, 3/4)', '(3/4, x+1/2, 3/4)',
4549 '(3/4, 3/4, -x)', '(3/4, 3/4, x+1/2)')),
4550 '12g': (1, ('(x, 3/4, 1/4)', '(-x+1/2, 3/4, 1/4)',
4551 '(1/4, x, 3/4)', '(1/4, -x+1/2, 3/4)',
4552 '(3/4, 1/4, x)', '(3/4, 1/4, -x+1/2)',
4553 '(-x, 1/4, 3/4)', '(x+1/2, 1/4, 3/4)',
4554 '(3/4, -x, 1/4)', '(3/4, x+1/2, 1/4)',
4555 '(1/4, 3/4, -x)', '(1/4, 3/4, x+1/2)')),
4556 '24h': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
4557 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
4558 '(z, x, y)', '(z, -x+1/2, -y+1/2)',
4559 '(-z+1/2, -x+1/2, y)', '(-z+1/2, x, -y+1/2)',
4560 '(y, z, x)', '(-y+1/2, z, -x+1/2)',
4561 '(y, -z+1/2, -x+1/2)', '(-y+1/2, -z+1/2, x)',
4562 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
4563 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)',
4564 '(-z, -x, -y)', '(-z, x+1/2, y+1/2)',
4565 '(z+1/2, x+1/2, -y)', '(z+1/2, -x, y+1/2)',
4566 '(-y, -z, -x)', '(y+1/2, -z, x+1/2)',
4567 '(-y, z+1/2, x+1/2)', '(y+1/2, z+1/2, -x)'))},
4568 '202': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
4569 '(1/2, 1/2, 0)')),
4570 '4b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
4571 '(0, 0, 1/2)')),
4572 '8c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
4573 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
4574 '(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
4575 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
4576 '24d': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
4577 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
4578 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)',
4579 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
4580 '(3/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
4581 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
4582 '(3/4, 1/2, 3/4)', '(1/4, 1/4, 0)',
4583 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
4584 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
4585 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
4586 '24e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
4587 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
4588 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
4589 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
4590 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
4591 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
4592 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
4593 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
4594 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)')),
4595 '32f': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
4596 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
4597 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
4598 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
4599 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
4600 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
4601 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
4602 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
4603 '(-x, -x, -x)', '(-x, -x+1/2, -x+1/2)',
4604 '(-x+1/2, -x, -x+1/2)', '(-x+1/2, -x+1/2, -x)',
4605 '(x, x, -x)', '(x, x+1/2, -x+1/2)',
4606 '(x+1/2, x, -x+1/2)', '(x+1/2, x+1/2, -x)',
4607 '(x, -x, x)', '(x, -x+1/2, x+1/2)',
4608 '(x+1/2, -x, x+1/2)', '(x+1/2, -x+1/2, x)',
4609 '(-x, x, x)', '(-x, x+1/2, x+1/2)',
4610 '(-x+1/2, x, x+1/2)', '(-x+1/2, x+1/2, x)')),
4611 '48g': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
4612 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
4613 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
4614 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
4615 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
4616 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
4617 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
4618 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
4619 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
4620 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
4621 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
4622 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)',
4623 '(-x, 3/4, 3/4)', '(-x, 1/4, 1/4)',
4624 '(-x+1/2, 3/4, 1/4)', '(-x+1/2, 1/4, 3/4)',
4625 '(x, 1/4, 3/4)', '(x, 3/4, 1/4)',
4626 '(x+1/2, 1/4, 1/4)', '(x+1/2, 3/4, 3/4)',
4627 '(3/4, -x, 3/4)', '(3/4, -x+1/2, 1/4)',
4628 '(1/4, -x, 1/4)', '(1/4, -x+1/2, 3/4)',
4629 '(3/4, x, 1/4)', '(3/4, x+1/2, 3/4)',
4630 '(1/4, x, 3/4)', '(1/4, x+1/2, 1/4)',
4631 '(3/4, 3/4, -x)', '(3/4, 1/4, -x+1/2)',
4632 '(1/4, 3/4, -x+1/2)', '(1/4, 1/4, -x)',
4633 '(1/4, 3/4, x)', '(1/4, 1/4, x+1/2)',
4634 '(3/4, 3/4, x+1/2)', '(3/4, 1/4, x)')),
4635 '48h': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(1/2, y, z+1/2)',
4636 '(1/2, y+1/2, z)', '(0, -y, z)',
4637 '(0, -y+1/2, z+1/2)', '(1/2, -y, z+1/2)',
4638 '(1/2, -y+1/2, z)', '(0, y, -z)',
4639 '(0, y+1/2, -z+1/2)', '(1/2, y, -z+1/2)',
4640 '(1/2, y+1/2, -z)', '(0, -y, -z)',
4641 '(0, -y+1/2, -z+1/2)', '(1/2, -y, -z+1/2)',
4642 '(1/2, -y+1/2, -z)', '(z, 0, y)', '(z, 1/2, y+1/2)',
4643 '(z+1/2, 0, y+1/2)', '(z+1/2, 1/2, y)',
4644 '(z, 0, -y)', '(z, 1/2, -y+1/2)',
4645 '(z+1/2, 0, -y+1/2)', '(z+1/2, 1/2, -y)',
4646 '(-z, 0, y)', '(-z, 1/2, y+1/2)',
4647 '(-z+1/2, 0, y+1/2)', '(-z+1/2, 1/2, y)',
4648 '(-z, 0, -y)', '(-z, 1/2, -y+1/2)',
4649 '(-z+1/2, 0, -y+1/2)', '(-z+1/2, 1/2, -y)',
4650 '(y, z, 0)', '(y, z+1/2, 1/2)', '(y+1/2, z, 1/2)',
4651 '(y+1/2, z+1/2, 0)', '(-y, z, 0)',
4652 '(-y, z+1/2, 1/2)', '(-y+1/2, z, 1/2)',
4653 '(-y+1/2, z+1/2, 0)', '(y, -z, 0)',
4654 '(y, -z+1/2, 1/2)', '(y+1/2, -z, 1/2)',
4655 '(y+1/2, -z+1/2, 0)', '(-y, -z, 0)',
4656 '(-y, -z+1/2, 1/2)', '(-y+1/2, -z, 1/2)',
4657 '(-y+1/2, -z+1/2, 0)')),
4658 '96i': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
4659 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
4660 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
4661 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
4662 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
4663 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
4664 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
4665 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
4666 '(z, x, y)', '(z, x+1/2, y+1/2)',
4667 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
4668 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
4669 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
4670 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
4671 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
4672 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
4673 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
4674 '(y, z, x)', '(y, z+1/2, x+1/2)',
4675 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
4676 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
4677 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
4678 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
4679 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
4680 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
4681 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
4682 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
4683 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
4684 '(x, y, -z)', '(x, y+1/2, -z+1/2)',
4685 '(x+1/2, y, -z+1/2)', '(x+1/2, y+1/2, -z)',
4686 '(x, -y, z)', '(x, -y+1/2, z+1/2)',
4687 '(x+1/2, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
4688 '(-x, y, z)', '(-x, y+1/2, z+1/2)',
4689 '(-x+1/2, y, z+1/2)', '(-x+1/2, y+1/2, z)',
4690 '(-z, -x, -y)', '(-z, -x+1/2, -y+1/2)',
4691 '(-z+1/2, -x, -y+1/2)', '(-z+1/2, -x+1/2, -y)',
4692 '(-z, x, y)', '(-z, x+1/2, y+1/2)',
4693 '(-z+1/2, x, y+1/2)', '(-z+1/2, x+1/2, y)',
4694 '(z, x, -y)', '(z, x+1/2, -y+1/2)',
4695 '(z+1/2, x, -y+1/2)', '(z+1/2, x+1/2, -y)',
4696 '(z, -x, y)', '(z, -x+1/2, y+1/2)',
4697 '(z+1/2, -x, y+1/2)', '(z+1/2, -x+1/2, y)',
4698 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
4699 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
4700 '(y, -z, x)', '(y, -z+1/2, x+1/2)',
4701 '(y+1/2, -z, x+1/2)', '(y+1/2, -z+1/2, x)',
4702 '(-y, z, x)', '(-y, z+1/2, x+1/2)',
4703 '(-y+1/2, z, x+1/2)', '(-y+1/2, z+1/2, x)',
4704 '(y, z, -x)', '(y, z+1/2, -x+1/2)',
4705 '(y+1/2, z, -x+1/2)', '(y+1/2, z+1/2, -x)'))},
4706 '203:1': {'8a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
4707 '(1/2, 1/2, 0)', '(1/4, 1/4, 1/4)',
4708 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
4709 '(3/4, 3/4, 1/4)')),
4710 '8b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
4711 '(0, 0, 1/2)', '(3/4, 3/4, 3/4)',
4712 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
4713 '(1/4, 1/4, 3/4)')),
4714 '16c': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
4715 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
4716 '(7/8, 7/8, 1/8)', '(7/8, 3/8, 5/8)',
4717 '(3/8, 7/8, 5/8)', '(3/8, 3/8, 1/8)',
4718 '(7/8, 1/8, 7/8)', '(7/8, 5/8, 3/8)',
4719 '(3/8, 1/8, 3/8)', '(3/8, 5/8, 7/8)',
4720 '(1/8, 7/8, 7/8)', '(1/8, 3/8, 3/8)',
4721 '(5/8, 7/8, 3/8)', '(5/8, 3/8, 7/8)')),
4722 '16d': (0, ('(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
4723 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
4724 '(3/8, 3/8, 5/8)', '(3/8, 7/8, 1/8)',
4725 '(7/8, 3/8, 1/8)', '(7/8, 7/8, 5/8)',
4726 '(3/8, 5/8, 3/8)', '(3/8, 1/8, 7/8)',
4727 '(7/8, 5/8, 7/8)', '(7/8, 1/8, 3/8)',
4728 '(5/8, 3/8, 3/8)', '(5/8, 7/8, 7/8)',
4729 '(1/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)')),
4730 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
4731 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
4732 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
4733 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
4734 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
4735 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
4736 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
4737 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
4738 '(-x+1/4, -x+1/4, -x+1/4)',
4739 '(-x+1/4, -x+3/4, -x+3/4)',
4740 '(-x+3/4, -x+1/4, -x+3/4)',
4741 '(-x+3/4, -x+3/4, -x+1/4)',
4742 '(x+1/4, x+1/4, -x+1/4)',
4743 '(x+1/4, x+3/4, -x+3/4)',
4744 '(x+3/4, x+1/4, -x+3/4)',
4745 '(x+3/4, x+3/4, -x+1/4)',
4746 '(x+1/4, -x+1/4, x+1/4)',
4747 '(x+1/4, -x+3/4, x+3/4)',
4748 '(x+3/4, -x+1/4, x+3/4)',
4749 '(x+3/4, -x+3/4, x+1/4)',
4750 '(-x+1/4, x+1/4, x+1/4)',
4751 '(-x+1/4, x+3/4, x+3/4)',
4752 '(-x+3/4, x+1/4, x+3/4)',
4753 '(-x+3/4, x+3/4, x+1/4)')),
4754 '48f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
4755 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
4756 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)',
4757 '(0, x, 0)', '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
4758 '(1/2, x+1/2, 0)', '(0, -x, 0)',
4759 '(0, -x+1/2, 1/2)', '(1/2, -x, 1/2)',
4760 '(1/2, -x+1/2, 0)', '(0, 0, x)',
4761 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
4762 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
4763 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)',
4764 '(-x+1/4, 1/4, 1/4)', '(-x+1/4, 3/4, 3/4)',
4765 '(-x+3/4, 1/4, 3/4)', '(-x+3/4, 3/4, 1/4)',
4766 '(x+1/4, 1/4, 1/4)', '(x+1/4, 3/4, 3/4)',
4767 '(x+3/4, 1/4, 3/4)', '(x+3/4, 3/4, 1/4)',
4768 '(1/4, -x+1/4, 1/4)', '(1/4, -x+3/4, 3/4)',
4769 '(3/4, -x+1/4, 3/4)', '(3/4, -x+3/4, 1/4)',
4770 '(1/4, x+1/4, 1/4)', '(1/4, x+3/4, 3/4)',
4771 '(3/4, x+1/4, 3/4)', '(3/4, x+3/4, 1/4)',
4772 '(1/4, 1/4, -x+1/4)', '(1/4, 3/4, -x+3/4)',
4773 '(3/4, 1/4, -x+3/4)', '(3/4, 3/4, -x+1/4)',
4774 '(1/4, 1/4, x+1/4)', '(1/4, 3/4, x+3/4)',
4775 '(3/4, 1/4, x+3/4)', '(3/4, 3/4, x+1/4)')),
4776 '96g': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
4777 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
4778 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
4779 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
4780 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
4781 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
4782 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
4783 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
4784 '(z, x, y)', '(z, x+1/2, y+1/2)',
4785 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
4786 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
4787 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
4788 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
4789 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
4790 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
4791 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
4792 '(y, z, x)', '(y, z+1/2, x+1/2)',
4793 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
4794 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
4795 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
4796 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
4797 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
4798 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
4799 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
4800 '(-x+1/4, -y+1/4, -z+1/4)',
4801 '(-x+1/4, -y+3/4, -z+3/4)',
4802 '(-x+3/4, -y+1/4, -z+3/4)',
4803 '(-x+3/4, -y+3/4, -z+1/4)',
4804 '(x+1/4, y+1/4, -z+1/4)',
4805 '(x+1/4, y+3/4, -z+3/4)',
4806 '(x+3/4, y+1/4, -z+3/4)',
4807 '(x+3/4, y+3/4, -z+1/4)',
4808 '(x+1/4, -y+1/4, z+1/4)',
4809 '(x+1/4, -y+3/4, z+3/4)',
4810 '(x+3/4, -y+1/4, z+3/4)',
4811 '(x+3/4, -y+3/4, z+1/4)',
4812 '(-x+1/4, y+1/4, z+1/4)',
4813 '(-x+1/4, y+3/4, z+3/4)',
4814 '(-x+3/4, y+1/4, z+3/4)',
4815 '(-x+3/4, y+3/4, z+1/4)',
4816 '(-z+1/4, -x+1/4, -y+1/4)',
4817 '(-z+1/4, -x+3/4, -y+3/4)',
4818 '(-z+3/4, -x+1/4, -y+3/4)',
4819 '(-z+3/4, -x+3/4, -y+1/4)',
4820 '(-z+1/4, x+1/4, y+1/4)',
4821 '(-z+1/4, x+3/4, y+3/4)',
4822 '(-z+3/4, x+1/4, y+3/4)',
4823 '(-z+3/4, x+3/4, y+1/4)',
4824 '(z+1/4, x+1/4, -y+1/4)',
4825 '(z+1/4, x+3/4, -y+3/4)',
4826 '(z+3/4, x+1/4, -y+3/4)',
4827 '(z+3/4, x+3/4, -y+1/4)',
4828 '(z+1/4, -x+1/4, y+1/4)',
4829 '(z+1/4, -x+3/4, y+3/4)',
4830 '(z+3/4, -x+1/4, y+3/4)',
4831 '(z+3/4, -x+3/4, y+1/4)',
4832 '(-y+1/4, -z+1/4, -x+1/4)',
4833 '(-y+1/4, -z+3/4, -x+3/4)',
4834 '(-y+3/4, -z+1/4, -x+3/4)',
4835 '(-y+3/4, -z+3/4, -x+1/4)',
4836 '(y+1/4, -z+1/4, x+1/4)',
4837 '(y+1/4, -z+3/4, x+3/4)',
4838 '(y+3/4, -z+1/4, x+3/4)',
4839 '(y+3/4, -z+3/4, x+1/4)',
4840 '(-y+1/4, z+1/4, x+1/4)',
4841 '(-y+1/4, z+3/4, x+3/4)',
4842 '(-y+3/4, z+1/4, x+3/4)',
4843 '(-y+3/4, z+3/4, x+1/4)',
4844 '(y+1/4, z+1/4, -x+1/4)',
4845 '(y+1/4, z+3/4, -x+3/4)',
4846 '(y+3/4, z+1/4, -x+3/4)',
4847 '(y+3/4, z+3/4, -x+1/4)'))},
4848 '203:2': {'8a': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
4849 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
4850 '(7/8, 7/8, 7/8)', '(7/8, 3/8, 3/8)',
4851 '(3/8, 7/8, 3/8)', '(3/8, 3/8, 7/8)')),
4852 '8b': (0, ('(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
4853 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
4854 '(3/8, 3/8, 3/8)', '(3/8, 7/8, 7/8)',
4855 '(7/8, 3/8, 7/8)', '(7/8, 7/8, 3/8)')),
4856 '16c': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
4857 '(1/2, 1/2, 0)', '(3/4, 3/4, 0)',
4858 '(3/4, 1/4, 1/2)', '(1/4, 3/4, 1/2)',
4859 '(1/4, 1/4, 0)', '(3/4, 0, 3/4)',
4860 '(3/4, 1/2, 1/4)', '(1/4, 0, 1/4)',
4861 '(1/4, 1/2, 3/4)', '(0, 3/4, 3/4)',
4862 '(0, 1/4, 1/4)', '(1/2, 3/4, 1/4)',
4863 '(1/2, 1/4, 3/4)')),
4864 '16d': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
4865 '(0, 0, 1/2)', '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)',
4866 '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
4867 '(1/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
4868 '(3/4, 1/2, 3/4)', '(3/4, 0, 1/4)',
4869 '(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)',
4870 '(0, 1/4, 3/4)', '(0, 3/4, 1/4)')),
4871 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
4872 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
4873 '(-x+3/4, -x+3/4, x)', '(-x+3/4, -x+1/4, x+1/2)',
4874 '(-x+1/4, -x+3/4, x+1/2)', '(-x+1/4, -x+1/4, x)',
4875 '(-x+3/4, x, -x+3/4)', '(-x+3/4, x+1/2, -x+1/4)',
4876 '(-x+1/4, x, -x+1/4)', '(-x+1/4, x+1/2, -x+3/4)',
4877 '(x, -x+3/4, -x+3/4)', '(x, -x+1/4, -x+1/4)',
4878 '(x+1/2, -x+3/4, -x+1/4)',
4879 '(x+1/2, -x+1/4, -x+3/4)', '(-x, -x, -x)',
4880 '(-x, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x+1/2)',
4881 '(-x+1/2, -x+1/2, -x)', '(x+1/4, x+1/4, -x)',
4882 '(x+1/4, x+3/4, -x+1/2)',
4883 '(x+3/4, x+1/4, -x+1/2)', '(x+3/4, x+3/4, -x)',
4884 '(x+1/4, -x, x+1/4)', '(x+1/4, -x+1/2, x+3/4)',
4885 '(x+3/4, -x, x+3/4)', '(x+3/4, -x+1/2, x+1/4)',
4886 '(-x, x+1/4, x+1/4)', '(-x, x+3/4, x+3/4)',
4887 '(-x+1/2, x+1/4, x+3/4)',
4888 '(-x+1/2, x+3/4, x+1/4)')),
4889 '48f': (1, ('(x, 1/8, 1/8)', '(x, 5/8, 5/8)',
4890 '(x+1/2, 1/8, 5/8)', '(x+1/2, 5/8, 1/8)',
4891 '(-x+3/4, 5/8, 1/8)', '(-x+3/4, 1/8, 5/8)',
4892 '(-x+1/4, 5/8, 5/8)', '(-x+1/4, 1/8, 1/8)',
4893 '(1/8, x, 1/8)', '(1/8, x+1/2, 5/8)',
4894 '(5/8, x, 5/8)', '(5/8, x+1/2, 1/8)',
4895 '(1/8, -x+3/4, 5/8)', '(1/8, -x+1/4, 1/8)',
4896 '(5/8, -x+3/4, 1/8)', '(5/8, -x+1/4, 5/8)',
4897 '(1/8, 1/8, x)', '(1/8, 5/8, x+1/2)',
4898 '(5/8, 1/8, x+1/2)', '(5/8, 5/8, x)',
4899 '(5/8, 1/8, -x+3/4)', '(5/8, 5/8, -x+1/4)',
4900 '(1/8, 1/8, -x+1/4)', '(1/8, 5/8, -x+3/4)',
4901 '(-x, 7/8, 7/8)', '(-x, 3/8, 3/8)',
4902 '(-x+1/2, 7/8, 3/8)', '(-x+1/2, 3/8, 7/8)',
4903 '(x+1/4, 3/8, 7/8)', '(x+1/4, 7/8, 3/8)',
4904 '(x+3/4, 3/8, 3/8)', '(x+3/4, 7/8, 7/8)',
4905 '(7/8, -x, 7/8)', '(7/8, -x+1/2, 3/8)',
4906 '(3/8, -x, 3/8)', '(3/8, -x+1/2, 7/8)',
4907 '(7/8, x+1/4, 3/8)', '(7/8, x+3/4, 7/8)',
4908 '(3/8, x+1/4, 7/8)', '(3/8, x+3/4, 3/8)',
4909 '(7/8, 7/8, -x)', '(7/8, 3/8, -x+1/2)',
4910 '(3/8, 7/8, -x+1/2)', '(3/8, 3/8, -x)',
4911 '(3/8, 7/8, x+1/4)', '(3/8, 3/8, x+3/4)',
4912 '(7/8, 7/8, x+3/4)', '(7/8, 3/8, x+1/4)')),
4913 '96g': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
4914 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
4915 '(-x+3/4, -y+3/4, z)', '(-x+3/4, -y+1/4, z+1/2)',
4916 '(-x+1/4, -y+3/4, z+1/2)', '(-x+1/4, -y+1/4, z)',
4917 '(-x+3/4, y, -z+3/4)', '(-x+3/4, y+1/2, -z+1/4)',
4918 '(-x+1/4, y, -z+1/4)', '(-x+1/4, y+1/2, -z+3/4)',
4919 '(x, -y+3/4, -z+3/4)', '(x, -y+1/4, -z+1/4)',
4920 '(x+1/2, -y+3/4, -z+1/4)',
4921 '(x+1/2, -y+1/4, -z+3/4)', '(z, x, y)',
4922 '(z, x+1/2, y+1/2)', '(z+1/2, x, y+1/2)',
4923 '(z+1/2, x+1/2, y)', '(z, -x+3/4, -y+3/4)',
4924 '(z, -x+1/4, -y+1/4)', '(z+1/2, -x+3/4, -y+1/4)',
4925 '(z+1/2, -x+1/4, -y+3/4)', '(-z+3/4, -x+3/4, y)',
4926 '(-z+3/4, -x+1/4, y+1/2)',
4927 '(-z+1/4, -x+3/4, y+1/2)', '(-z+1/4, -x+1/4, y)',
4928 '(-z+3/4, x, -y+3/4)', '(-z+3/4, x+1/2, -y+1/4)',
4929 '(-z+1/4, x, -y+1/4)', '(-z+1/4, x+1/2, -y+3/4)',
4930 '(y, z, x)', '(y, z+1/2, x+1/2)',
4931 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
4932 '(-y+3/4, z, -x+3/4)', '(-y+3/4, z+1/2, -x+1/4)',
4933 '(-y+1/4, z, -x+1/4)', '(-y+1/4, z+1/2, -x+3/4)',
4934 '(y, -z+3/4, -x+3/4)', '(y, -z+1/4, -x+1/4)',
4935 '(y+1/2, -z+3/4, -x+1/4)',
4936 '(y+1/2, -z+1/4, -x+3/4)', '(-y+3/4, -z+3/4, x)',
4937 '(-y+3/4, -z+1/4, x+1/2)',
4938 '(-y+1/4, -z+3/4, x+1/2)', '(-y+1/4, -z+1/4, x)',
4939 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
4940 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
4941 '(x+1/4, y+1/4, -z)', '(x+1/4, y+3/4, -z+1/2)',
4942 '(x+3/4, y+1/4, -z+1/2)', '(x+3/4, y+3/4, -z)',
4943 '(x+1/4, -y, z+1/4)', '(x+1/4, -y+1/2, z+3/4)',
4944 '(x+3/4, -y, z+3/4)', '(x+3/4, -y+1/2, z+1/4)',
4945 '(-x, y+1/4, z+1/4)', '(-x, y+3/4, z+3/4)',
4946 '(-x+1/2, y+1/4, z+3/4)',
4947 '(-x+1/2, y+3/4, z+1/4)', '(-z, -x, -y)',
4948 '(-z, -x+1/2, -y+1/2)', '(-z+1/2, -x, -y+1/2)',
4949 '(-z+1/2, -x+1/2, -y)', '(-z, x+1/4, y+1/4)',
4950 '(-z, x+3/4, y+3/4)', '(-z+1/2, x+1/4, y+3/4)',
4951 '(-z+1/2, x+3/4, y+1/4)', '(z+1/4, x+1/4, -y)',
4952 '(z+1/4, x+3/4, -y+1/2)',
4953 '(z+3/4, x+1/4, -y+1/2)', '(z+3/4, x+3/4, -y)',
4954 '(z+1/4, -x, y+1/4)', '(z+1/4, -x+1/2, y+3/4)',
4955 '(z+3/4, -x, y+3/4)', '(z+3/4, -x+1/2, y+1/4)',
4956 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
4957 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
4958 '(y+1/4, -z, x+1/4)', '(y+1/4, -z+1/2, x+3/4)',
4959 '(y+3/4, -z, x+3/4)', '(y+3/4, -z+1/2, x+1/4)',
4960 '(-y, z+1/4, x+1/4)', '(-y, z+3/4, x+3/4)',
4961 '(-y+1/2, z+1/4, x+3/4)',
4962 '(-y+1/2, z+3/4, x+1/4)', '(y+1/4, z+1/4, -x)',
4963 '(y+1/4, z+3/4, -x+1/2)',
4964 '(y+3/4, z+1/4, -x+1/2)', '(y+3/4, z+3/4, -x)'))},
4965 '204': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
4966 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
4967 '(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
4968 '8c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
4969 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
4970 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
4971 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
4972 '12d': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
4973 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
4974 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
4975 '(1/2, -x+1/2, 1/2)', '(0, 0, x)',
4976 '(1/2, 1/2, x+1/2)', '(0, 0, -x)',
4977 '(1/2, 1/2, -x+1/2)')),
4978 '12e': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
4979 '(-x+1/2, 1/2, 0)', '(1/2, x, 0)',
4980 '(0, x+1/2, 1/2)', '(1/2, -x, 0)',
4981 '(0, -x+1/2, 1/2)', '(0, 1/2, x)',
4982 '(1/2, 0, x+1/2)', '(0, 1/2, -x)',
4983 '(1/2, 0, -x+1/2)')),
4984 '16f': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)', '(-x, -x, x)',
4985 '(-x+1/2, -x+1/2, x+1/2)', '(-x, x, -x)',
4986 '(-x+1/2, x+1/2, -x+1/2)', '(x, -x, -x)',
4987 '(x+1/2, -x+1/2, -x+1/2)', '(-x, -x, -x)',
4988 '(-x+1/2, -x+1/2, -x+1/2)', '(x, x, -x)',
4989 '(x+1/2, x+1/2, -x+1/2)', '(x, -x, x)',
4990 '(x+1/2, -x+1/2, x+1/2)', '(-x, x, x)',
4991 '(-x+1/2, x+1/2, x+1/2)')),
4992 '24g': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y, z)',
4993 '(1/2, -y+1/2, z+1/2)', '(0, y, -z)',
4994 '(1/2, y+1/2, -z+1/2)', '(0, -y, -z)',
4995 '(1/2, -y+1/2, -z+1/2)', '(z, 0, y)',
4996 '(z+1/2, 1/2, y+1/2)', '(z, 0, -y)',
4997 '(z+1/2, 1/2, -y+1/2)', '(-z, 0, y)',
4998 '(-z+1/2, 1/2, y+1/2)', '(-z, 0, -y)',
4999 '(-z+1/2, 1/2, -y+1/2)', '(y, z, 0)',
5000 '(y+1/2, z+1/2, 1/2)', '(-y, z, 0)',
5001 '(-y+1/2, z+1/2, 1/2)', '(y, -z, 0)',
5002 '(y+1/2, -z+1/2, 1/2)', '(-y, -z, 0)',
5003 '(-y+1/2, -z+1/2, 1/2)')),
5004 '48h': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
5005 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
5006 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
5007 '(x+1/2, -y+1/2, -z+1/2)', '(z, x, y)',
5008 '(z+1/2, x+1/2, y+1/2)', '(z, -x, -y)',
5009 '(z+1/2, -x+1/2, -y+1/2)', '(-z, -x, y)',
5010 '(-z+1/2, -x+1/2, y+1/2)', '(-z, x, -y)',
5011 '(-z+1/2, x+1/2, -y+1/2)', '(y, z, x)',
5012 '(y+1/2, z+1/2, x+1/2)', '(-y, z, -x)',
5013 '(-y+1/2, z+1/2, -x+1/2)', '(y, -z, -x)',
5014 '(y+1/2, -z+1/2, -x+1/2)', '(-y, -z, x)',
5015 '(-y+1/2, -z+1/2, x+1/2)', '(-x, -y, -z)',
5016 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
5017 '(x+1/2, y+1/2, -z+1/2)', '(x, -y, z)',
5018 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
5019 '(-x+1/2, y+1/2, z+1/2)', '(-z, -x, -y)',
5020 '(-z+1/2, -x+1/2, -y+1/2)', '(-z, x, y)',
5021 '(-z+1/2, x+1/2, y+1/2)', '(z, x, -y)',
5022 '(z+1/2, x+1/2, -y+1/2)', '(z, -x, y)',
5023 '(z+1/2, -x+1/2, y+1/2)', '(-y, -z, -x)',
5024 '(-y+1/2, -z+1/2, -x+1/2)', '(y, -z, x)',
5025 '(y+1/2, -z+1/2, x+1/2)', '(-y, z, x)',
5026 '(-y+1/2, z+1/2, x+1/2)', '(y, z, -x)',
5027 '(y+1/2, z+1/2, -x+1/2)'))},
5028 '205': {'4a': (0, ('(0, 0, 0)', '(1/2, 0, 1/2)', '(0, 1/2, 1/2)',
5029 '(1/2, 1/2, 0)')),
5030 '4b': (0, ('(1/2, 1/2, 1/2)', '(0, 1/2, 0)', '(1/2, 0, 0)',
5031 '(0, 0, 1/2)')),
5032 '8c': (1, ('(x, x, x)', '(-x+1/2, -x, x+1/2)',
5033 '(-x, x+1/2, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5034 '(-x, -x, -x)', '(x+1/2, x, -x+1/2)',
5035 '(x, -x+1/2, x+1/2)', '(-x+1/2, x+1/2, x)')),
5036 '24d': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
5037 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5038 '(z, x, y)', '(z+1/2, -x+1/2, -y)',
5039 '(-z+1/2, -x, y+1/2)', '(-z, x+1/2, -y+1/2)',
5040 '(y, z, x)', '(-y, z+1/2, -x+1/2)',
5041 '(y+1/2, -z+1/2, -x)', '(-y+1/2, -z, x+1/2)',
5042 '(-x, -y, -z)', '(x+1/2, y, -z+1/2)',
5043 '(x, -y+1/2, z+1/2)', '(-x+1/2, y+1/2, z)',
5044 '(-z, -x, -y)', '(-z+1/2, x+1/2, y)',
5045 '(z+1/2, x, -y+1/2)', '(z, -x+1/2, y+1/2)',
5046 '(-y, -z, -x)', '(y, -z+1/2, x+1/2)',
5047 '(-y+1/2, z+1/2, x)', '(y+1/2, z, -x+1/2)'))},
5048 '206': {'8a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
5049 '(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 0)',
5050 '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
5051 '8b': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
5052 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
5053 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
5054 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)')),
5055 '16c': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)',
5056 '(-x+1/2, -x, x+1/2)', '(-x, -x+1/2, x)',
5057 '(-x, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
5058 '(x+1/2, -x+1/2, -x)', '(x, -x, -x+1/2)',
5059 '(-x, -x, -x)', '(-x+1/2, -x+1/2, -x+1/2)',
5060 '(x+1/2, x, -x+1/2)', '(x, x+1/2, -x)',
5061 '(x, -x+1/2, x+1/2)', '(x+1/2, -x, x)',
5062 '(-x+1/2, x+1/2, x)', '(-x, x, x+1/2)')),
5063 '24d': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
5064 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(1/4, x, 0)',
5065 '(3/4, x+1/2, 1/2)', '(3/4, -x+1/2, 0)',
5066 '(1/4, -x, 1/2)', '(0, 1/4, x)',
5067 '(1/2, 3/4, x+1/2)', '(0, 3/4, -x+1/2)',
5068 '(1/2, 1/4, -x)', '(-x, 0, 3/4)',
5069 '(-x+1/2, 1/2, 1/4)', '(x+1/2, 0, 1/4)',
5070 '(x, 1/2, 3/4)', '(3/4, -x, 0)',
5071 '(1/4, -x+1/2, 1/2)', '(1/4, x+1/2, 0)',
5072 '(3/4, x, 1/2)', '(0, 3/4, -x)',
5073 '(1/2, 1/4, -x+1/2)', '(0, 1/4, x+1/2)',
5074 '(1/2, 3/4, x)')),
5075 '48e': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
5076 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
5077 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
5078 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
5079 '(z, x, y)', '(z+1/2, x+1/2, y+1/2)',
5080 '(z+1/2, -x+1/2, -y)', '(z, -x, -y+1/2)',
5081 '(-z+1/2, -x, y+1/2)', '(-z, -x+1/2, y)',
5082 '(-z, x+1/2, -y+1/2)', '(-z+1/2, x, -y)',
5083 '(y, z, x)', '(y+1/2, z+1/2, x+1/2)',
5084 '(-y, z+1/2, -x+1/2)', '(-y+1/2, z, -x)',
5085 '(y+1/2, -z+1/2, -x)', '(y, -z, -x+1/2)',
5086 '(-y+1/2, -z, x+1/2)', '(-y, -z+1/2, x)',
5087 '(-x, -y, -z)', '(-x+1/2, -y+1/2, -z+1/2)',
5088 '(x+1/2, y, -z+1/2)', '(x, y+1/2, -z)',
5089 '(x, -y+1/2, z+1/2)', '(x+1/2, -y, z)',
5090 '(-x+1/2, y+1/2, z)', '(-x, y, z+1/2)',
5091 '(-z, -x, -y)', '(-z+1/2, -x+1/2, -y+1/2)',
5092 '(-z+1/2, x+1/2, y)', '(-z, x, y+1/2)',
5093 '(z+1/2, x, -y+1/2)', '(z, x+1/2, -y)',
5094 '(z, -x+1/2, y+1/2)', '(z+1/2, -x, y)',
5095 '(-y, -z, -x)', '(-y+1/2, -z+1/2, -x+1/2)',
5096 '(y, -z+1/2, x+1/2)', '(y+1/2, -z, x)',
5097 '(-y+1/2, z+1/2, x)', '(-y, z, x+1/2)',
5098 '(y+1/2, z, -x+1/2)', '(y, z+1/2, -x)'))},
5099 '207': {'1a': (0, ('(0, 0, 0)', )),
5100 '1b': (0, ('(1/2, 1/2, 1/2)', )),
5101 '3c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
5102 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
5103 '6e': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)',
5104 '(0, 0, x)', '(0, 0, -x)')),
5105 '6f': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
5106 '(1/2, -x, 1/2)', '(1/2, 1/2, x)', '(1/2, 1/2, -x)'
5107 )),
5108 '8g': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
5109 '(x, -x, -x)', '(x, x, -x)', '(-x, -x, -x)',
5110 '(x, -x, x)', '(-x, x, x)')),
5111 '12h': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
5112 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
5113 '(1/2, x, 0)', '(1/2, -x, 0)', '(x, 0, 1/2)',
5114 '(-x, 0, 1/2)', '(0, 1/2, -x)', '(0, 1/2, x)')),
5115 '12i': (2, ('(0, y, y)', '(0, -y, y)', '(0, y, -y)',
5116 '(0, -y, -y)', '(y, 0, y)', '(y, 0, -y)',
5117 '(-y, 0, y)', '(-y, 0, -y)', '(y, y, 0)',
5118 '(-y, y, 0)', '(y, -y, 0)', '(-y, -y, 0)')),
5119 '12j': (2, ('(1/2, y, y)', '(1/2, -y, y)', '(1/2, y, -y)',
5120 '(1/2, -y, -y)', '(y, 1/2, y)', '(y, 1/2, -y)',
5121 '(-y, 1/2, y)', '(-y, 1/2, -y)', '(y, y, 1/2)',
5122 '(-y, y, 1/2)', '(y, -y, 1/2)', '(-y, -y, 1/2)')),
5123 '24k': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
5124 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
5125 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
5126 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
5127 '(y, x, -z)', '(-y, -x, -z)', '(y, -x, z)',
5128 '(-y, x, z)', '(x, z, -y)', '(-x, z, y)',
5129 '(-x, -z, -y)', '(x, -z, y)', '(z, y, -x)',
5130 '(z, -y, x)', '(-z, y, x)', '(-z, -y, -x)'))},
5131 '208': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
5132 '4b': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
5133 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)')),
5134 '4c': (0, ('(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
5135 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
5136 '6d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
5137 '(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 0, 1/2)')),
5138 '6e': (0, ('(1/4, 0, 1/2)', '(3/4, 0, 1/2)', '(1/2, 1/4, 0)',
5139 '(1/2, 3/4, 0)', '(0, 1/2, 1/4)', '(0, 1/2, 3/4)')),
5140 '6f': (0, ('(1/4, 1/2, 0)', '(3/4, 1/2, 0)', '(0, 1/4, 1/2)',
5141 '(0, 3/4, 1/2)', '(1/2, 0, 1/4)', '(1/2, 0, 3/4)')),
5142 '8g': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
5143 '(x, -x, -x)', '(x+1/2, x+1/2, -x+1/2)',
5144 '(-x+1/2, -x+1/2, -x+1/2)', '(x+1/2, -x+1/2, x+1/2)',
5145 '(-x+1/2, x+1/2, x+1/2)')),
5146 '12h': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
5147 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
5148 '(1/2, x+1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
5149 '(x+1/2, 1/2, 1/2)', '(-x+1/2, 1/2, 1/2)',
5150 '(1/2, 1/2, -x+1/2)', '(1/2, 1/2, x+1/2)')),
5151 '12i': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
5152 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)',
5153 '(1/2, x+1/2, 0)', '(1/2, -x+1/2, 0)',
5154 '(x+1/2, 0, 1/2)', '(-x+1/2, 0, 1/2)',
5155 '(0, 1/2, -x+1/2)', '(0, 1/2, x+1/2)')),
5156 '12j': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
5157 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
5158 '(0, x+1/2, 1/2)', '(0, -x+1/2, 1/2)',
5159 '(x+1/2, 1/2, 0)', '(-x+1/2, 1/2, 0)',
5160 '(1/2, 0, -x+1/2)', '(1/2, 0, x+1/2)')),
5161 '12k': (2, ('(1/4, y, -y+1/2)', '(3/4, -y, -y+1/2)',
5162 '(3/4, y, y+1/2)', '(1/4, -y, y+1/2)',
5163 '(-y+1/2, 1/4, y)', '(-y+1/2, 3/4, -y)',
5164 '(y+1/2, 3/4, y)', '(y+1/2, 1/4, -y)',
5165 '(y, -y+1/2, 1/4)', '(-y, -y+1/2, 3/4)',
5166 '(y, y+1/2, 3/4)', '(-y, y+1/2, 1/4)')),
5167 '12l': (2, ('(1/4, y, y+1/2)', '(3/4, -y, y+1/2)',
5168 '(3/4, y, -y+1/2)', '(1/4, -y, -y+1/2)',
5169 '(y+1/2, 1/4, y)', '(y+1/2, 3/4, -y)',
5170 '(-y+1/2, 3/4, y)', '(-y+1/2, 1/4, -y)',
5171 '(y, y+1/2, 1/4)', '(-y, y+1/2, 3/4)',
5172 '(y, -y+1/2, 3/4)', '(-y, -y+1/2, 1/4)')),
5173 '24m': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
5174 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
5175 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
5176 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
5177 '(y+1/2, x+1/2, -z+1/2)',
5178 '(-y+1/2, -x+1/2, -z+1/2)',
5179 '(y+1/2, -x+1/2, z+1/2)', '(-y+1/2, x+1/2, z+1/2)',
5180 '(x+1/2, z+1/2, -y+1/2)', '(-x+1/2, z+1/2, y+1/2)',
5181 '(-x+1/2, -z+1/2, -y+1/2)',
5182 '(x+1/2, -z+1/2, y+1/2)', '(z+1/2, y+1/2, -x+1/2)',
5183 '(z+1/2, -y+1/2, x+1/2)', '(-z+1/2, y+1/2, x+1/2)',
5184 '(-z+1/2, -y+1/2, -x+1/2)'))},
5185 '209': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
5186 '(1/2, 1/2, 0)')),
5187 '4b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
5188 '(0, 0, 1/2)')),
5189 '8c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
5190 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
5191 '(1/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
5192 '(3/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
5193 '24d': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
5194 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
5195 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)',
5196 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
5197 '(3/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
5198 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
5199 '(3/4, 1/2, 3/4)', '(1/4, 1/4, 0)',
5200 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
5201 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
5202 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
5203 '24e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
5204 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
5205 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
5206 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
5207 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
5208 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
5209 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
5210 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
5211 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)')),
5212 '32f': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
5213 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
5214 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
5215 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
5216 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
5217 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
5218 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
5219 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5220 '(x, x, -x)', '(x, x+1/2, -x+1/2)',
5221 '(x+1/2, x, -x+1/2)', '(x+1/2, x+1/2, -x)',
5222 '(-x, -x, -x)', '(-x, -x+1/2, -x+1/2)',
5223 '(-x+1/2, -x, -x+1/2)', '(-x+1/2, -x+1/2, -x)',
5224 '(x, -x, x)', '(x, -x+1/2, x+1/2)',
5225 '(x+1/2, -x, x+1/2)', '(x+1/2, -x+1/2, x)',
5226 '(-x, x, x)', '(-x, x+1/2, x+1/2)',
5227 '(-x+1/2, x, x+1/2)', '(-x+1/2, x+1/2, x)')),
5228 '48g': (2, ('(0, y, y)', '(0, y+1/2, y+1/2)', '(1/2, y, y+1/2)',
5229 '(1/2, y+1/2, y)', '(0, -y, y)',
5230 '(0, -y+1/2, y+1/2)', '(1/2, -y, y+1/2)',
5231 '(1/2, -y+1/2, y)', '(0, y, -y)',
5232 '(0, y+1/2, -y+1/2)', '(1/2, y, -y+1/2)',
5233 '(1/2, y+1/2, -y)', '(0, -y, -y)',
5234 '(0, -y+1/2, -y+1/2)', '(1/2, -y, -y+1/2)',
5235 '(1/2, -y+1/2, -y)', '(y, 0, y)', '(y, 1/2, y+1/2)',
5236 '(y+1/2, 0, y+1/2)', '(y+1/2, 1/2, y)',
5237 '(y, 0, -y)', '(y, 1/2, -y+1/2)',
5238 '(y+1/2, 0, -y+1/2)', '(y+1/2, 1/2, -y)',
5239 '(-y, 0, y)', '(-y, 1/2, y+1/2)',
5240 '(-y+1/2, 0, y+1/2)', '(-y+1/2, 1/2, y)',
5241 '(-y, 0, -y)', '(-y, 1/2, -y+1/2)',
5242 '(-y+1/2, 0, -y+1/2)', '(-y+1/2, 1/2, -y)',
5243 '(y, y, 0)', '(y, y+1/2, 1/2)', '(y+1/2, y, 1/2)',
5244 '(y+1/2, y+1/2, 0)', '(-y, y, 0)',
5245 '(-y, y+1/2, 1/2)', '(-y+1/2, y, 1/2)',
5246 '(-y+1/2, y+1/2, 0)', '(y, -y, 0)',
5247 '(y, -y+1/2, 1/2)', '(y+1/2, -y, 1/2)',
5248 '(y+1/2, -y+1/2, 0)', '(-y, -y, 0)',
5249 '(-y, -y+1/2, 1/2)', '(-y+1/2, -y, 1/2)',
5250 '(-y+1/2, -y+1/2, 0)')),
5251 '48h': (2, ('(1/2, y, y)', '(1/2, y+1/2, y+1/2)',
5252 '(0, y, y+1/2)', '(0, y+1/2, y)', '(1/2, -y, y)',
5253 '(1/2, -y+1/2, y+1/2)', '(0, -y, y+1/2)',
5254 '(0, -y+1/2, y)', '(1/2, y, -y)',
5255 '(1/2, y+1/2, -y+1/2)', '(0, y, -y+1/2)',
5256 '(0, y+1/2, -y)', '(1/2, -y, -y)',
5257 '(1/2, -y+1/2, -y+1/2)', '(0, -y, -y+1/2)',
5258 '(0, -y+1/2, -y)', '(y, 1/2, y)', '(y, 0, y+1/2)',
5259 '(y+1/2, 1/2, y+1/2)', '(y+1/2, 0, y)',
5260 '(y, 1/2, -y)', '(y, 0, -y+1/2)',
5261 '(y+1/2, 1/2, -y+1/2)', '(y+1/2, 0, -y)',
5262 '(-y, 1/2, y)', '(-y, 0, y+1/2)',
5263 '(-y+1/2, 1/2, y+1/2)', '(-y+1/2, 0, y)',
5264 '(-y, 1/2, -y)', '(-y, 0, -y+1/2)',
5265 '(-y+1/2, 1/2, -y+1/2)', '(-y+1/2, 0, -y)',
5266 '(y, y, 1/2)', '(y, y+1/2, 0)', '(y+1/2, y, 0)',
5267 '(y+1/2, y+1/2, 1/2)', '(-y, y, 1/2)',
5268 '(-y, y+1/2, 0)', '(-y+1/2, y, 0)',
5269 '(-y+1/2, y+1/2, 1/2)', '(y, -y, 1/2)',
5270 '(y, -y+1/2, 0)', '(y+1/2, -y, 0)',
5271 '(y+1/2, -y+1/2, 1/2)', '(-y, -y, 1/2)',
5272 '(-y, -y+1/2, 0)', '(-y+1/2, -y, 0)',
5273 '(-y+1/2, -y+1/2, 1/2)')),
5274 '48i': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
5275 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
5276 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
5277 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
5278 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
5279 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
5280 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
5281 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
5282 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
5283 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
5284 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
5285 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)',
5286 '(1/4, x, 3/4)', '(1/4, x+1/2, 1/4)',
5287 '(3/4, x, 1/4)', '(3/4, x+1/2, 3/4)',
5288 '(3/4, -x, 3/4)', '(3/4, -x+1/2, 1/4)',
5289 '(1/4, -x, 1/4)', '(1/4, -x+1/2, 3/4)',
5290 '(x, 1/4, 3/4)', '(x, 3/4, 1/4)',
5291 '(x+1/2, 1/4, 1/4)', '(x+1/2, 3/4, 3/4)',
5292 '(-x, 1/4, 1/4)', '(-x, 3/4, 3/4)',
5293 '(-x+1/2, 1/4, 3/4)', '(-x+1/2, 3/4, 1/4)',
5294 '(1/4, 1/4, -x)', '(1/4, 3/4, -x+1/2)',
5295 '(3/4, 1/4, -x+1/2)', '(3/4, 3/4, -x)',
5296 '(1/4, 3/4, x)', '(1/4, 1/4, x+1/2)',
5297 '(3/4, 3/4, x+1/2)', '(3/4, 1/4, x)')),
5298 '96j': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
5299 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
5300 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
5301 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
5302 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
5303 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
5304 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
5305 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5306 '(z, x, y)', '(z, x+1/2, y+1/2)',
5307 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
5308 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
5309 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
5310 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
5311 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
5312 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
5313 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
5314 '(y, z, x)', '(y, z+1/2, x+1/2)',
5315 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
5316 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
5317 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
5318 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
5319 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
5320 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
5321 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
5322 '(y, x, -z)', '(y, x+1/2, -z+1/2)',
5323 '(y+1/2, x, -z+1/2)', '(y+1/2, x+1/2, -z)',
5324 '(-y, -x, -z)', '(-y, -x+1/2, -z+1/2)',
5325 '(-y+1/2, -x, -z+1/2)', '(-y+1/2, -x+1/2, -z)',
5326 '(y, -x, z)', '(y, -x+1/2, z+1/2)',
5327 '(y+1/2, -x, z+1/2)', '(y+1/2, -x+1/2, z)',
5328 '(-y, x, z)', '(-y, x+1/2, z+1/2)',
5329 '(-y+1/2, x, z+1/2)', '(-y+1/2, x+1/2, z)',
5330 '(x, z, -y)', '(x, z+1/2, -y+1/2)',
5331 '(x+1/2, z, -y+1/2)', '(x+1/2, z+1/2, -y)',
5332 '(-x, z, y)', '(-x, z+1/2, y+1/2)',
5333 '(-x+1/2, z, y+1/2)', '(-x+1/2, z+1/2, y)',
5334 '(-x, -z, -y)', '(-x, -z+1/2, -y+1/2)',
5335 '(-x+1/2, -z, -y+1/2)', '(-x+1/2, -z+1/2, -y)',
5336 '(x, -z, y)', '(x, -z+1/2, y+1/2)',
5337 '(x+1/2, -z, y+1/2)', '(x+1/2, -z+1/2, y)',
5338 '(z, y, -x)', '(z, y+1/2, -x+1/2)',
5339 '(z+1/2, y, -x+1/2)', '(z+1/2, y+1/2, -x)',
5340 '(z, -y, x)', '(z, -y+1/2, x+1/2)',
5341 '(z+1/2, -y, x+1/2)', '(z+1/2, -y+1/2, x)',
5342 '(-z, y, x)', '(-z, y+1/2, x+1/2)',
5343 '(-z+1/2, y, x+1/2)', '(-z+1/2, y+1/2, x)',
5344 '(-z, -y, -x)', '(-z, -y+1/2, -x+1/2)',
5345 '(-z+1/2, -y, -x+1/2)', '(-z+1/2, -y+1/2, -x)'))},
5346 '210': {'8a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
5347 '(1/2, 1/2, 0)', '(3/4, 1/4, 3/4)',
5348 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 1/4)',
5349 '(1/4, 3/4, 3/4)')),
5350 '8b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
5351 '(0, 0, 1/2)', '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
5352 '(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
5353 '16c': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
5354 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
5355 '(7/8, 3/8, 5/8)', '(7/8, 7/8, 1/8)',
5356 '(3/8, 3/8, 1/8)', '(3/8, 7/8, 5/8)',
5357 '(3/8, 5/8, 7/8)', '(3/8, 1/8, 3/8)',
5358 '(7/8, 5/8, 3/8)', '(7/8, 1/8, 7/8)',
5359 '(5/8, 7/8, 3/8)', '(5/8, 3/8, 7/8)',
5360 '(1/8, 7/8, 7/8)', '(1/8, 3/8, 3/8)')),
5361 '16d': (0, ('(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
5362 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
5363 '(3/8, 7/8, 1/8)', '(3/8, 3/8, 5/8)',
5364 '(7/8, 7/8, 5/8)', '(7/8, 3/8, 1/8)',
5365 '(7/8, 1/8, 3/8)', '(7/8, 5/8, 7/8)',
5366 '(3/8, 1/8, 7/8)', '(3/8, 5/8, 3/8)',
5367 '(1/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)',
5368 '(5/8, 3/8, 3/8)', '(5/8, 7/8, 7/8)')),
5369 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
5370 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
5371 '(-x, -x+1/2, x+1/2)', '(-x, -x, x)',
5372 '(-x+1/2, -x+1/2, x)', '(-x+1/2, -x, x+1/2)',
5373 '(-x+1/2, x+1/2, -x)', '(-x+1/2, x, -x+1/2)',
5374 '(-x, x+1/2, -x+1/2)', '(-x, x, -x)',
5375 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5376 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
5377 '(x+3/4, x+1/4, -x+3/4)', '(x+3/4, x+3/4, -x+1/4)',
5378 '(x+1/4, x+1/4, -x+1/4)', '(x+1/4, x+3/4, -x+3/4)',
5379 '(-x+1/4, -x+1/4, -x+1/4)',
5380 '(-x+1/4, -x+3/4, -x+3/4)',
5381 '(-x+3/4, -x+1/4, -x+3/4)',
5382 '(-x+3/4, -x+3/4, -x+1/4)',
5383 '(x+1/4, -x+3/4, x+3/4)', '(x+1/4, -x+1/4, x+1/4)',
5384 '(x+3/4, -x+3/4, x+1/4)', '(x+3/4, -x+1/4, x+3/4)',
5385 '(-x+3/4, x+3/4, x+1/4)', '(-x+3/4, x+1/4, x+3/4)',
5386 '(-x+1/4, x+3/4, x+3/4)', '(-x+1/4, x+1/4, x+1/4)'
5387 )),
5388 '48f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
5389 '(x+1/2, 1/2, 0)', '(-x, 1/2, 1/2)', '(-x, 0, 0)',
5390 '(-x+1/2, 1/2, 0)', '(-x+1/2, 0, 1/2)', '(0, x, 0)',
5391 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
5392 '(1/2, x+1/2, 0)', '(1/2, -x, 1/2)',
5393 '(1/2, -x+1/2, 0)', '(0, -x, 0)',
5394 '(0, -x+1/2, 1/2)', '(0, 0, x)', '(0, 1/2, x+1/2)',
5395 '(1/2, 0, x+1/2)', '(1/2, 1/2, x)',
5396 '(1/2, 1/2, -x)', '(1/2, 0, -x+1/2)',
5397 '(0, 1/2, -x+1/2)', '(0, 0, -x)',
5398 '(3/4, x+1/4, 3/4)', '(3/4, x+3/4, 1/4)',
5399 '(1/4, x+1/4, 1/4)', '(1/4, x+3/4, 3/4)',
5400 '(1/4, -x+1/4, 1/4)', '(1/4, -x+3/4, 3/4)',
5401 '(3/4, -x+1/4, 3/4)', '(3/4, -x+3/4, 1/4)',
5402 '(x+3/4, 1/4, 3/4)', '(x+3/4, 3/4, 1/4)',
5403 '(x+1/4, 1/4, 1/4)', '(x+1/4, 3/4, 3/4)',
5404 '(-x+3/4, 3/4, 1/4)', '(-x+3/4, 1/4, 3/4)',
5405 '(-x+1/4, 3/4, 3/4)', '(-x+1/4, 1/4, 1/4)',
5406 '(3/4, 1/4, -x+3/4)', '(3/4, 3/4, -x+1/4)',
5407 '(1/4, 1/4, -x+1/4)', '(1/4, 3/4, -x+3/4)',
5408 '(1/4, 3/4, x+3/4)', '(1/4, 1/4, x+1/4)',
5409 '(3/4, 3/4, x+1/4)', '(3/4, 1/4, x+3/4)')),
5410 '48g': (2, ('(1/8, y, -y+1/4)', '(1/8, y+1/2, -y+3/4)',
5411 '(5/8, y, -y+3/4)', '(5/8, y+1/2, -y+1/4)',
5412 '(7/8, -y+1/2, -y+3/4)', '(7/8, -y, -y+1/4)',
5413 '(3/8, -y+1/2, -y+1/4)', '(3/8, -y, -y+3/4)',
5414 '(3/8, y+1/2, y+3/4)', '(3/8, y, y+1/4)',
5415 '(7/8, y+1/2, y+1/4)', '(7/8, y, y+3/4)',
5416 '(5/8, -y, y+1/4)', '(5/8, -y+1/2, y+3/4)',
5417 '(1/8, -y, y+3/4)', '(1/8, -y+1/2, y+1/4)',
5418 '(-y+1/4, 1/8, y)', '(-y+1/4, 5/8, y+1/2)',
5419 '(-y+3/4, 1/8, y+1/2)', '(-y+3/4, 5/8, y)',
5420 '(-y+3/4, 7/8, -y+1/2)', '(-y+3/4, 3/8, -y)',
5421 '(-y+1/4, 7/8, -y)', '(-y+1/4, 3/8, -y+1/2)',
5422 '(y+3/4, 3/8, y+1/2)', '(y+3/4, 7/8, y)',
5423 '(y+1/4, 3/8, y)', '(y+1/4, 7/8, y+1/2)',
5424 '(y+1/4, 5/8, -y)', '(y+1/4, 1/8, -y+1/2)',
5425 '(y+3/4, 5/8, -y+1/2)', '(y+3/4, 1/8, -y)',
5426 '(y, -y+1/4, 1/8)', '(y, -y+3/4, 5/8)',
5427 '(y+1/2, -y+1/4, 5/8)', '(y+1/2, -y+3/4, 1/8)',
5428 '(-y+1/2, -y+3/4, 7/8)', '(-y+1/2, -y+1/4, 3/8)',
5429 '(-y, -y+3/4, 3/8)', '(-y, -y+1/4, 7/8)',
5430 '(y+1/2, y+3/4, 3/8)', '(y+1/2, y+1/4, 7/8)',
5431 '(y, y+3/4, 7/8)', '(y, y+1/4, 3/8)',
5432 '(-y, y+1/4, 5/8)', '(-y, y+3/4, 1/8)',
5433 '(-y+1/2, y+1/4, 1/8)', '(-y+1/2, y+3/4, 5/8)')),
5434 '96h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
5435 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
5436 '(-x, -y+1/2, z+1/2)', '(-x, -y, z)',
5437 '(-x+1/2, -y+1/2, z)', '(-x+1/2, -y, z+1/2)',
5438 '(-x+1/2, y+1/2, -z)', '(-x+1/2, y, -z+1/2)',
5439 '(-x, y+1/2, -z+1/2)', '(-x, y, -z)',
5440 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5441 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)', '(z, x, y)',
5442 '(z, x+1/2, y+1/2)', '(z+1/2, x, y+1/2)',
5443 '(z+1/2, x+1/2, y)', '(z+1/2, -x, -y+1/2)',
5444 '(z+1/2, -x+1/2, -y)', '(z, -x, -y)',
5445 '(z, -x+1/2, -y+1/2)', '(-z, -x+1/2, y+1/2)',
5446 '(-z, -x, y)', '(-z+1/2, -x+1/2, y)',
5447 '(-z+1/2, -x, y+1/2)', '(-z+1/2, x+1/2, -y)',
5448 '(-z+1/2, x, -y+1/2)', '(-z, x+1/2, -y+1/2)',
5449 '(-z, x, -y)', '(y, z, x)', '(y, z+1/2, x+1/2)',
5450 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
5451 '(-y+1/2, z+1/2, -x)', '(-y+1/2, z, -x+1/2)',
5452 '(-y, z+1/2, -x+1/2)', '(-y, z, -x)',
5453 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
5454 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
5455 '(-y, -z+1/2, x+1/2)', '(-y, -z, x)',
5456 '(-y+1/2, -z+1/2, x)', '(-y+1/2, -z, x+1/2)',
5457 '(y+3/4, x+1/4, -z+3/4)', '(y+3/4, x+3/4, -z+1/4)',
5458 '(y+1/4, x+1/4, -z+1/4)', '(y+1/4, x+3/4, -z+3/4)',
5459 '(-y+1/4, -x+1/4, -z+1/4)',
5460 '(-y+1/4, -x+3/4, -z+3/4)',
5461 '(-y+3/4, -x+1/4, -z+3/4)',
5462 '(-y+3/4, -x+3/4, -z+1/4)',
5463 '(y+1/4, -x+3/4, z+3/4)', '(y+1/4, -x+1/4, z+1/4)',
5464 '(y+3/4, -x+3/4, z+1/4)', '(y+3/4, -x+1/4, z+3/4)',
5465 '(-y+3/4, x+3/4, z+1/4)', '(-y+3/4, x+1/4, z+3/4)',
5466 '(-y+1/4, x+3/4, z+3/4)', '(-y+1/4, x+1/4, z+1/4)',
5467 '(x+3/4, z+1/4, -y+3/4)', '(x+3/4, z+3/4, -y+1/4)',
5468 '(x+1/4, z+1/4, -y+1/4)', '(x+1/4, z+3/4, -y+3/4)',
5469 '(-x+3/4, z+3/4, y+1/4)', '(-x+3/4, z+1/4, y+3/4)',
5470 '(-x+1/4, z+3/4, y+3/4)', '(-x+1/4, z+1/4, y+1/4)',
5471 '(-x+1/4, -z+1/4, -y+1/4)',
5472 '(-x+1/4, -z+3/4, -y+3/4)',
5473 '(-x+3/4, -z+1/4, -y+3/4)',
5474 '(-x+3/4, -z+3/4, -y+1/4)',
5475 '(x+1/4, -z+3/4, y+3/4)', '(x+1/4, -z+1/4, y+1/4)',
5476 '(x+3/4, -z+3/4, y+1/4)', '(x+3/4, -z+1/4, y+3/4)',
5477 '(z+3/4, y+1/4, -x+3/4)', '(z+3/4, y+3/4, -x+1/4)',
5478 '(z+1/4, y+1/4, -x+1/4)', '(z+1/4, y+3/4, -x+3/4)',
5479 '(z+1/4, -y+3/4, x+3/4)', '(z+1/4, -y+1/4, x+1/4)',
5480 '(z+3/4, -y+3/4, x+1/4)', '(z+3/4, -y+1/4, x+3/4)',
5481 '(-z+3/4, y+3/4, x+1/4)', '(-z+3/4, y+1/4, x+3/4)',
5482 '(-z+1/4, y+3/4, x+3/4)', '(-z+1/4, y+1/4, x+1/4)',
5483 '(-z+1/4, -y+1/4, -x+1/4)',
5484 '(-z+1/4, -y+3/4, -x+3/4)',
5485 '(-z+3/4, -y+1/4, -x+3/4)',
5486 '(-z+3/4, -y+3/4, -x+1/4)'))},
5487 '211': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
5488 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
5489 '(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
5490 '8c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
5491 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
5492 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
5493 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
5494 '12d': (0, ('(1/4, 1/2, 0)', '(3/4, 0, 1/2)', '(3/4, 1/2, 0)',
5495 '(1/4, 0, 1/2)', '(0, 1/4, 1/2)', '(1/2, 3/4, 0)',
5496 '(0, 3/4, 1/2)', '(1/2, 1/4, 0)', '(1/2, 0, 1/4)',
5497 '(0, 1/2, 3/4)', '(1/2, 0, 3/4)', '(0, 1/2, 1/4)')),
5498 '12e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
5499 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
5500 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
5501 '(1/2, -x+1/2, 1/2)', '(0, 0, x)',
5502 '(1/2, 1/2, x+1/2)', '(0, 0, -x)',
5503 '(1/2, 1/2, -x+1/2)')),
5504 '16f': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)', '(-x, -x, x)',
5505 '(-x+1/2, -x+1/2, x+1/2)', '(-x, x, -x)',
5506 '(-x+1/2, x+1/2, -x+1/2)', '(x, -x, -x)',
5507 '(x+1/2, -x+1/2, -x+1/2)', '(x, x, -x)',
5508 '(x+1/2, x+1/2, -x+1/2)', '(-x, -x, -x)',
5509 '(-x+1/2, -x+1/2, -x+1/2)', '(x, -x, x)',
5510 '(x+1/2, -x+1/2, x+1/2)', '(-x, x, x)',
5511 '(-x+1/2, x+1/2, x+1/2)')),
5512 '24g': (1, ('(x, 1/2, 0)', '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
5513 '(-x+1/2, 0, 1/2)', '(0, x, 1/2)',
5514 '(1/2, x+1/2, 0)', '(0, -x, 1/2)',
5515 '(1/2, -x+1/2, 0)', '(1/2, 0, x)',
5516 '(0, 1/2, x+1/2)', '(1/2, 0, -x)',
5517 '(0, 1/2, -x+1/2)', '(1/2, x, 0)',
5518 '(0, x+1/2, 1/2)', '(1/2, -x, 0)',
5519 '(0, -x+1/2, 1/2)', '(x, 0, 1/2)',
5520 '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
5521 '(-x+1/2, 1/2, 0)', '(0, 1/2, -x)',
5522 '(1/2, 0, -x+1/2)', '(0, 1/2, x)',
5523 '(1/2, 0, x+1/2)')),
5524 '24h': (2, ('(0, y, y)', '(1/2, y+1/2, y+1/2)', '(0, -y, y)',
5525 '(1/2, -y+1/2, y+1/2)', '(0, y, -y)',
5526 '(1/2, y+1/2, -y+1/2)', '(0, -y, -y)',
5527 '(1/2, -y+1/2, -y+1/2)', '(y, 0, y)',
5528 '(y+1/2, 1/2, y+1/2)', '(y, 0, -y)',
5529 '(y+1/2, 1/2, -y+1/2)', '(-y, 0, y)',
5530 '(-y+1/2, 1/2, y+1/2)', '(-y, 0, -y)',
5531 '(-y+1/2, 1/2, -y+1/2)', '(y, y, 0)',
5532 '(y+1/2, y+1/2, 1/2)', '(-y, y, 0)',
5533 '(-y+1/2, y+1/2, 1/2)', '(y, -y, 0)',
5534 '(y+1/2, -y+1/2, 1/2)', '(-y, -y, 0)',
5535 '(-y+1/2, -y+1/2, 1/2)')),
5536 '24i': (2, ('(1/4, y, -y+1/2)', '(3/4, y+1/2, -y)',
5537 '(3/4, -y, -y+1/2)', '(1/4, -y+1/2, -y)',
5538 '(3/4, y, y+1/2)', '(1/4, y+1/2, y)',
5539 '(1/4, -y, y+1/2)', '(3/4, -y+1/2, y)',
5540 '(-y+1/2, 1/4, y)', '(-y, 3/4, y+1/2)',
5541 '(-y+1/2, 3/4, -y)', '(-y, 1/4, -y+1/2)',
5542 '(y+1/2, 3/4, y)', '(y, 1/4, y+1/2)',
5543 '(y+1/2, 1/4, -y)', '(y, 3/4, -y+1/2)',
5544 '(y, -y+1/2, 1/4)', '(y+1/2, -y, 3/4)',
5545 '(-y, -y+1/2, 3/4)', '(-y+1/2, -y, 1/4)',
5546 '(y, y+1/2, 3/4)', '(y+1/2, y, 1/4)',
5547 '(-y, y+1/2, 1/4)', '(-y+1/2, y, 3/4)')),
5548 '48j': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
5549 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
5550 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
5551 '(x+1/2, -y+1/2, -z+1/2)', '(z, x, y)',
5552 '(z+1/2, x+1/2, y+1/2)', '(z, -x, -y)',
5553 '(z+1/2, -x+1/2, -y+1/2)', '(-z, -x, y)',
5554 '(-z+1/2, -x+1/2, y+1/2)', '(-z, x, -y)',
5555 '(-z+1/2, x+1/2, -y+1/2)', '(y, z, x)',
5556 '(y+1/2, z+1/2, x+1/2)', '(-y, z, -x)',
5557 '(-y+1/2, z+1/2, -x+1/2)', '(y, -z, -x)',
5558 '(y+1/2, -z+1/2, -x+1/2)', '(-y, -z, x)',
5559 '(-y+1/2, -z+1/2, x+1/2)', '(y, x, -z)',
5560 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z)',
5561 '(-y+1/2, -x+1/2, -z+1/2)', '(y, -x, z)',
5562 '(y+1/2, -x+1/2, z+1/2)', '(-y, x, z)',
5563 '(-y+1/2, x+1/2, z+1/2)', '(x, z, -y)',
5564 '(x+1/2, z+1/2, -y+1/2)', '(-x, z, y)',
5565 '(-x+1/2, z+1/2, y+1/2)', '(-x, -z, -y)',
5566 '(-x+1/2, -z+1/2, -y+1/2)', '(x, -z, y)',
5567 '(x+1/2, -z+1/2, y+1/2)', '(z, y, -x)',
5568 '(z+1/2, y+1/2, -x+1/2)', '(z, -y, x)',
5569 '(z+1/2, -y+1/2, x+1/2)', '(-z, y, x)',
5570 '(-z+1/2, y+1/2, x+1/2)', '(-z, -y, -x)',
5571 '(-z+1/2, -y+1/2, -x+1/2)'))},
5572 '212': {'4a': (0, ('(1/8, 1/8, 1/8)', '(3/8, 7/8, 5/8)',
5573 '(7/8, 5/8, 3/8)', '(5/8, 3/8, 7/8)')),
5574 '4b': (0, ('(5/8, 5/8, 5/8)', '(7/8, 3/8, 1/8)',
5575 '(3/8, 1/8, 7/8)', '(1/8, 7/8, 3/8)')),
5576 '8c': (1, ('(x, x, x)', '(-x+1/2, -x, x+1/2)',
5577 '(-x, x+1/2, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5578 '(x+1/4, x+3/4, -x+3/4)', '(-x+1/4, -x+1/4, -x+1/4)',
5579 '(x+3/4, -x+3/4, x+1/4)', '(-x+3/4, x+1/4, x+3/4)')),
5580 '12d': (2, ('(1/8, y, -y+1/4)', '(3/8, -y, -y+3/4)',
5581 '(7/8, y+1/2, y+1/4)', '(5/8, -y+1/2, y+3/4)',
5582 '(-y+1/4, 1/8, y)', '(-y+3/4, 3/8, -y)',
5583 '(y+1/4, 7/8, y+1/2)', '(y+3/4, 5/8, -y+1/2)',
5584 '(y, -y+1/4, 1/8)', '(-y, -y+3/4, 3/8)',
5585 '(y+1/2, y+1/4, 7/8)', '(-y+1/2, y+3/4, 5/8)')),
5586 '24e': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
5587 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5588 '(z, x, y)', '(z+1/2, -x+1/2, -y)',
5589 '(-z+1/2, -x, y+1/2)', '(-z, x+1/2, -y+1/2)',
5590 '(y, z, x)', '(-y, z+1/2, -x+1/2)',
5591 '(y+1/2, -z+1/2, -x)', '(-y+1/2, -z, x+1/2)',
5592 '(y+1/4, x+3/4, -z+3/4)',
5593 '(-y+1/4, -x+1/4, -z+1/4)',
5594 '(y+3/4, -x+3/4, z+1/4)', '(-y+3/4, x+1/4, z+3/4)',
5595 '(x+1/4, z+3/4, -y+3/4)', '(-x+3/4, z+1/4, y+3/4)',
5596 '(-x+1/4, -z+1/4, -y+1/4)',
5597 '(x+3/4, -z+3/4, y+1/4)', '(z+1/4, y+3/4, -x+3/4)',
5598 '(z+3/4, -y+3/4, x+1/4)', '(-z+3/4, y+1/4, x+3/4)',
5599 '(-z+1/4, -y+1/4, -x+1/4)'))},
5600 '213': {'4a': (0, ('(3/8, 3/8, 3/8)', '(1/8, 5/8, 7/8)',
5601 '(5/8, 7/8, 1/8)', '(7/8, 1/8, 5/8)')),
5602 '4b': (0, ('(7/8, 7/8, 7/8)', '(5/8, 1/8, 3/8)',
5603 '(1/8, 3/8, 5/8)', '(3/8, 5/8, 1/8)')),
5604 '8c': (1, ('(x, x, x)', '(-x+1/2, -x, x+1/2)',
5605 '(-x, x+1/2, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5606 '(x+3/4, x+1/4, -x+1/4)', '(-x+3/4, -x+3/4, -x+3/4)',
5607 '(x+1/4, -x+1/4, x+3/4)', '(-x+1/4, x+3/4, x+1/4)')),
5608 '12d': (2, ('(1/8, y, y+1/4)', '(3/8, -y, y+3/4)',
5609 '(7/8, y+1/2, -y+1/4)', '(5/8, -y+1/2, -y+3/4)',
5610 '(y+1/4, 1/8, y)', '(y+3/4, 3/8, -y)',
5611 '(-y+1/4, 7/8, y+1/2)', '(-y+3/4, 5/8, -y+1/2)',
5612 '(y, y+1/4, 1/8)', '(-y, y+3/4, 3/8)',
5613 '(y+1/2, -y+1/4, 7/8)', '(-y+1/2, -y+3/4, 5/8)')),
5614 '24e': (7, ('(x, y, z)', '(-x+1/2, -y, z+1/2)',
5615 '(-x, y+1/2, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5616 '(z, x, y)', '(z+1/2, -x+1/2, -y)',
5617 '(-z+1/2, -x, y+1/2)', '(-z, x+1/2, -y+1/2)',
5618 '(y, z, x)', '(-y, z+1/2, -x+1/2)',
5619 '(y+1/2, -z+1/2, -x)', '(-y+1/2, -z, x+1/2)',
5620 '(y+3/4, x+1/4, -z+1/4)',
5621 '(-y+3/4, -x+3/4, -z+3/4)',
5622 '(y+1/4, -x+1/4, z+3/4)', '(-y+1/4, x+3/4, z+1/4)',
5623 '(x+3/4, z+1/4, -y+1/4)', '(-x+1/4, z+3/4, y+1/4)',
5624 '(-x+3/4, -z+3/4, -y+3/4)',
5625 '(x+1/4, -z+1/4, y+3/4)', '(z+3/4, y+1/4, -x+1/4)',
5626 '(z+1/4, -y+1/4, x+3/4)', '(-z+1/4, y+3/4, x+1/4)',
5627 '(-z+3/4, -y+3/4, -x+3/4)'))},
5628 '214': {'8a': (0, ('(1/8, 1/8, 1/8)', '(5/8, 5/8, 5/8)',
5629 '(3/8, 7/8, 5/8)', '(7/8, 3/8, 1/8)',
5630 '(7/8, 5/8, 3/8)', '(3/8, 1/8, 7/8)',
5631 '(5/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)')),
5632 '8b': (0, ('(7/8, 7/8, 7/8)', '(3/8, 3/8, 3/8)',
5633 '(5/8, 1/8, 3/8)', '(1/8, 5/8, 7/8)',
5634 '(1/8, 3/8, 5/8)', '(5/8, 7/8, 1/8)',
5635 '(3/8, 5/8, 1/8)', '(7/8, 1/8, 5/8)')),
5636 '12c': (0, ('(1/8, 0, 1/4)', '(5/8, 1/2, 3/4)', '(3/8, 0, 3/4)',
5637 '(7/8, 1/2, 1/4)', '(1/4, 1/8, 0)',
5638 '(3/4, 5/8, 1/2)', '(3/4, 3/8, 0)',
5639 '(1/4, 7/8, 1/2)', '(0, 1/4, 1/8)',
5640 '(1/2, 3/4, 5/8)', '(0, 3/4, 3/8)',
5641 '(1/2, 1/4, 7/8)')),
5642 '12d': (0, ('(5/8, 0, 1/4)', '(1/8, 1/2, 3/4)', '(7/8, 0, 3/4)',
5643 '(3/8, 1/2, 1/4)', '(1/4, 5/8, 0)',
5644 '(3/4, 1/8, 1/2)', '(3/4, 7/8, 0)',
5645 '(1/4, 3/8, 1/2)', '(0, 1/4, 5/8)',
5646 '(1/2, 3/4, 1/8)', '(0, 3/4, 7/8)',
5647 '(1/2, 1/4, 3/8)')),
5648 '16e': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)',
5649 '(-x+1/2, -x, x+1/2)', '(-x, -x+1/2, x)',
5650 '(-x, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
5651 '(x+1/2, -x+1/2, -x)', '(x, -x, -x+1/2)',
5652 '(x+3/4, x+1/4, -x+1/4)', '(x+1/4, x+3/4, -x+3/4)',
5653 '(-x+3/4, -x+3/4, -x+3/4)',
5654 '(-x+1/4, -x+1/4, -x+1/4)',
5655 '(x+1/4, -x+1/4, x+3/4)', '(x+3/4, -x+3/4, x+1/4)',
5656 '(-x+1/4, x+3/4, x+1/4)', '(-x+3/4, x+1/4, x+3/4)'
5657 )),
5658 '24f': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
5659 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(1/4, x, 0)',
5660 '(3/4, x+1/2, 1/2)', '(3/4, -x+1/2, 0)',
5661 '(1/4, -x, 1/2)', '(0, 1/4, x)',
5662 '(1/2, 3/4, x+1/2)', '(0, 3/4, -x+1/2)',
5663 '(1/2, 1/4, -x)', '(3/4, x+1/4, 0)',
5664 '(1/4, x+3/4, 1/2)', '(3/4, -x+3/4, 1/2)',
5665 '(1/4, -x+1/4, 0)', '(x+3/4, 1/2, 1/4)',
5666 '(x+1/4, 0, 3/4)', '(-x+1/4, 0, 1/4)',
5667 '(-x+3/4, 1/2, 3/4)', '(0, 1/4, -x+1/4)',
5668 '(1/2, 3/4, -x+3/4)', '(1/2, 1/4, x+3/4)',
5669 '(0, 3/4, x+1/4)')),
5670 '24g': (2, ('(1/8, y, y+1/4)', '(5/8, y+1/2, y+3/4)',
5671 '(3/8, -y, y+3/4)', '(7/8, -y+1/2, y+1/4)',
5672 '(7/8, y+1/2, -y+1/4)', '(3/8, y, -y+3/4)',
5673 '(5/8, -y+1/2, -y+3/4)', '(1/8, -y, -y+1/4)',
5674 '(y+1/4, 1/8, y)', '(y+3/4, 5/8, y+1/2)',
5675 '(y+3/4, 3/8, -y)', '(y+1/4, 7/8, -y+1/2)',
5676 '(-y+1/4, 7/8, y+1/2)', '(-y+3/4, 3/8, y)',
5677 '(-y+3/4, 5/8, -y+1/2)', '(-y+1/4, 1/8, -y)',
5678 '(y, y+1/4, 1/8)', '(y+1/2, y+3/4, 5/8)',
5679 '(-y, y+3/4, 3/8)', '(-y+1/2, y+1/4, 7/8)',
5680 '(y+1/2, -y+1/4, 7/8)', '(y, -y+3/4, 3/8)',
5681 '(-y+1/2, -y+3/4, 5/8)', '(-y, -y+1/4, 1/8)')),
5682 '24h': (2, ('(1/8, y, -y+1/4)', '(5/8, y+1/2, -y+3/4)',
5683 '(3/8, -y, -y+3/4)', '(7/8, -y+1/2, -y+1/4)',
5684 '(7/8, y+1/2, y+1/4)', '(3/8, y, y+3/4)',
5685 '(5/8, -y+1/2, y+3/4)', '(1/8, -y, y+1/4)',
5686 '(-y+1/4, 1/8, y)', '(-y+3/4, 5/8, y+1/2)',
5687 '(-y+3/4, 3/8, -y)', '(-y+1/4, 7/8, -y+1/2)',
5688 '(y+1/4, 7/8, y+1/2)', '(y+3/4, 3/8, y)',
5689 '(y+3/4, 5/8, -y+1/2)', '(y+1/4, 1/8, -y)',
5690 '(y, -y+1/4, 1/8)', '(y+1/2, -y+3/4, 5/8)',
5691 '(-y, -y+3/4, 3/8)', '(-y+1/2, -y+1/4, 7/8)',
5692 '(y+1/2, y+1/4, 7/8)', '(y, y+3/4, 3/8)',
5693 '(-y+1/2, y+3/4, 5/8)', '(-y, y+1/4, 1/8)')),
5694 '48i': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
5695 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
5696 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
5697 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
5698 '(z, x, y)', '(z+1/2, x+1/2, y+1/2)',
5699 '(z+1/2, -x+1/2, -y)', '(z, -x, -y+1/2)',
5700 '(-z+1/2, -x, y+1/2)', '(-z, -x+1/2, y)',
5701 '(-z, x+1/2, -y+1/2)', '(-z+1/2, x, -y)',
5702 '(y, z, x)', '(y+1/2, z+1/2, x+1/2)',
5703 '(-y, z+1/2, -x+1/2)', '(-y+1/2, z, -x)',
5704 '(y+1/2, -z+1/2, -x)', '(y, -z, -x+1/2)',
5705 '(-y+1/2, -z, x+1/2)', '(-y, -z+1/2, x)',
5706 '(y+3/4, x+1/4, -z+1/4)', '(y+1/4, x+3/4, -z+3/4)',
5707 '(-y+3/4, -x+3/4, -z+3/4)',
5708 '(-y+1/4, -x+1/4, -z+1/4)',
5709 '(y+1/4, -x+1/4, z+3/4)', '(y+3/4, -x+3/4, z+1/4)',
5710 '(-y+1/4, x+3/4, z+1/4)', '(-y+3/4, x+1/4, z+3/4)',
5711 '(x+3/4, z+1/4, -y+1/4)', '(x+1/4, z+3/4, -y+3/4)',
5712 '(-x+1/4, z+3/4, y+1/4)', '(-x+3/4, z+1/4, y+3/4)',
5713 '(-x+3/4, -z+3/4, -y+3/4)',
5714 '(-x+1/4, -z+1/4, -y+1/4)',
5715 '(x+1/4, -z+1/4, y+3/4)', '(x+3/4, -z+3/4, y+1/4)',
5716 '(z+3/4, y+1/4, -x+1/4)', '(z+1/4, y+3/4, -x+3/4)',
5717 '(z+1/4, -y+1/4, x+3/4)', '(z+3/4, -y+3/4, x+1/4)',
5718 '(-z+1/4, y+3/4, x+1/4)', '(-z+3/4, y+1/4, x+3/4)',
5719 '(-z+3/4, -y+3/4, -x+3/4)',
5720 '(-z+1/4, -y+1/4, -x+1/4)'))},
5721 '215': {'1a': (0, ('(0, 0, 0)', )),
5722 '1b': (0, ('(1/2, 1/2, 1/2)', )),
5723 '3c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
5724 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
5725 '4e': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
5726 '(x, -x, -x)')),
5727 '6f': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)',
5728 '(0, 0, x)', '(0, 0, -x)')),
5729 '6g': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
5730 '(1/2, -x, 1/2)', '(1/2, 1/2, x)', '(1/2, 1/2, -x)'
5731 )),
5732 '12h': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
5733 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
5734 '(1/2, x, 0)', '(1/2, -x, 0)', '(x, 0, 1/2)',
5735 '(-x, 0, 1/2)', '(0, 1/2, x)', '(0, 1/2, -x)')),
5736 '12i': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, -z)',
5737 '(x, -x, -z)', '(z, x, x)', '(z, -x, -x)',
5738 '(-z, -x, x)', '(-z, x, -x)', '(x, z, x)',
5739 '(-x, z, -x)', '(x, -z, -x)', '(-x, -z, x)')),
5740 '24j': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
5741 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
5742 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
5743 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
5744 '(y, x, z)', '(-y, -x, z)', '(y, -x, -z)',
5745 '(-y, x, -z)', '(x, z, y)', '(-x, z, -y)',
5746 '(-x, -z, y)', '(x, -z, -y)', '(z, y, x)',
5747 '(z, -y, -x)', '(-z, y, -x)', '(-z, -y, x)'))},
5748 '216': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
5749 '(1/2, 1/2, 0)')),
5750 '4b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
5751 '(0, 0, 1/2)')),
5752 '4c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
5753 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
5754 '4d': (0, ('(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
5755 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
5756 '16e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
5757 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
5758 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
5759 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
5760 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
5761 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
5762 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
5763 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)')),
5764 '24f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
5765 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
5766 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
5767 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
5768 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
5769 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
5770 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
5771 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
5772 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)')),
5773 '24g': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
5774 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
5775 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
5776 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
5777 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
5778 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
5779 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
5780 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
5781 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
5782 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
5783 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
5784 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)')),
5785 '48h': (5, ('(x, x, z)', '(x, x+1/2, z+1/2)',
5786 '(x+1/2, x, z+1/2)', '(x+1/2, x+1/2, z)',
5787 '(-x, -x, z)', '(-x, -x+1/2, z+1/2)',
5788 '(-x+1/2, -x, z+1/2)', '(-x+1/2, -x+1/2, z)',
5789 '(-x, x, -z)', '(-x, x+1/2, -z+1/2)',
5790 '(-x+1/2, x, -z+1/2)', '(-x+1/2, x+1/2, -z)',
5791 '(x, -x, -z)', '(x, -x+1/2, -z+1/2)',
5792 '(x+1/2, -x, -z+1/2)', '(x+1/2, -x+1/2, -z)',
5793 '(z, x, x)', '(z, x+1/2, x+1/2)',
5794 '(z+1/2, x, x+1/2)', '(z+1/2, x+1/2, x)',
5795 '(z, -x, -x)', '(z, -x+1/2, -x+1/2)',
5796 '(z+1/2, -x, -x+1/2)', '(z+1/2, -x+1/2, -x)',
5797 '(-z, -x, x)', '(-z, -x+1/2, x+1/2)',
5798 '(-z+1/2, -x, x+1/2)', '(-z+1/2, -x+1/2, x)',
5799 '(-z, x, -x)', '(-z, x+1/2, -x+1/2)',
5800 '(-z+1/2, x, -x+1/2)', '(-z+1/2, x+1/2, -x)',
5801 '(x, z, x)', '(x, z+1/2, x+1/2)',
5802 '(x+1/2, z, x+1/2)', '(x+1/2, z+1/2, x)',
5803 '(-x, z, -x)', '(-x, z+1/2, -x+1/2)',
5804 '(-x+1/2, z, -x+1/2)', '(-x+1/2, z+1/2, -x)',
5805 '(x, -z, -x)', '(x, -z+1/2, -x+1/2)',
5806 '(x+1/2, -z, -x+1/2)', '(x+1/2, -z+1/2, -x)',
5807 '(-x, -z, x)', '(-x, -z+1/2, x+1/2)',
5808 '(-x+1/2, -z, x+1/2)', '(-x+1/2, -z+1/2, x)')),
5809 '96i': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
5810 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
5811 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
5812 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
5813 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
5814 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
5815 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
5816 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
5817 '(z, x, y)', '(z, x+1/2, y+1/2)',
5818 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
5819 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
5820 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
5821 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
5822 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
5823 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
5824 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
5825 '(y, z, x)', '(y, z+1/2, x+1/2)',
5826 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
5827 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
5828 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
5829 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
5830 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
5831 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
5832 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
5833 '(y, x, z)', '(y, x+1/2, z+1/2)',
5834 '(y+1/2, x, z+1/2)', '(y+1/2, x+1/2, z)',
5835 '(-y, -x, z)', '(-y, -x+1/2, z+1/2)',
5836 '(-y+1/2, -x, z+1/2)', '(-y+1/2, -x+1/2, z)',
5837 '(y, -x, -z)', '(y, -x+1/2, -z+1/2)',
5838 '(y+1/2, -x, -z+1/2)', '(y+1/2, -x+1/2, -z)',
5839 '(-y, x, -z)', '(-y, x+1/2, -z+1/2)',
5840 '(-y+1/2, x, -z+1/2)', '(-y+1/2, x+1/2, -z)',
5841 '(x, z, y)', '(x, z+1/2, y+1/2)',
5842 '(x+1/2, z, y+1/2)', '(x+1/2, z+1/2, y)',
5843 '(-x, z, -y)', '(-x, z+1/2, -y+1/2)',
5844 '(-x+1/2, z, -y+1/2)', '(-x+1/2, z+1/2, -y)',
5845 '(-x, -z, y)', '(-x, -z+1/2, y+1/2)',
5846 '(-x+1/2, -z, y+1/2)', '(-x+1/2, -z+1/2, y)',
5847 '(x, -z, -y)', '(x, -z+1/2, -y+1/2)',
5848 '(x+1/2, -z, -y+1/2)', '(x+1/2, -z+1/2, -y)',
5849 '(z, y, x)', '(z, y+1/2, x+1/2)',
5850 '(z+1/2, y, x+1/2)', '(z+1/2, y+1/2, x)',
5851 '(z, -y, -x)', '(z, -y+1/2, -x+1/2)',
5852 '(z+1/2, -y, -x+1/2)', '(z+1/2, -y+1/2, -x)',
5853 '(-z, y, -x)', '(-z, y+1/2, -x+1/2)',
5854 '(-z+1/2, y, -x+1/2)', '(-z+1/2, y+1/2, -x)',
5855 '(-z, -y, x)', '(-z, -y+1/2, x+1/2)',
5856 '(-z+1/2, -y, x+1/2)', '(-z+1/2, -y+1/2, x)'))},
5857 '217': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
5858 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
5859 '(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
5860 '8c': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)', '(-x, -x, x)',
5861 '(-x+1/2, -x+1/2, x+1/2)', '(-x, x, -x)',
5862 '(-x+1/2, x+1/2, -x+1/2)', '(x, -x, -x)',
5863 '(x+1/2, -x+1/2, -x+1/2)')),
5864 '12d': (0, ('(1/4, 1/2, 0)', '(3/4, 0, 1/2)', '(3/4, 1/2, 0)',
5865 '(1/4, 0, 1/2)', '(0, 1/4, 1/2)', '(1/2, 3/4, 0)',
5866 '(0, 3/4, 1/2)', '(1/2, 1/4, 0)', '(1/2, 0, 1/4)',
5867 '(0, 1/2, 3/4)', '(1/2, 0, 3/4)', '(0, 1/2, 1/4)')),
5868 '12e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
5869 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
5870 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
5871 '(1/2, -x+1/2, 1/2)', '(0, 0, x)',
5872 '(1/2, 1/2, x+1/2)', '(0, 0, -x)',
5873 '(1/2, 1/2, -x+1/2)')),
5874 '24f': (1, ('(x, 1/2, 0)', '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
5875 '(-x+1/2, 0, 1/2)', '(0, x, 1/2)',
5876 '(1/2, x+1/2, 0)', '(0, -x, 1/2)',
5877 '(1/2, -x+1/2, 0)', '(1/2, 0, x)',
5878 '(0, 1/2, x+1/2)', '(1/2, 0, -x)',
5879 '(0, 1/2, -x+1/2)', '(1/2, x, 0)',
5880 '(0, x+1/2, 1/2)', '(1/2, -x, 0)',
5881 '(0, -x+1/2, 1/2)', '(x, 0, 1/2)',
5882 '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
5883 '(-x+1/2, 1/2, 0)', '(0, 1/2, x)',
5884 '(1/2, 0, x+1/2)', '(0, 1/2, -x)',
5885 '(1/2, 0, -x+1/2)')),
5886 '24g': (5, ('(x, x, z)', '(x+1/2, x+1/2, z+1/2)', '(-x, -x, z)',
5887 '(-x+1/2, -x+1/2, z+1/2)', '(-x, x, -z)',
5888 '(-x+1/2, x+1/2, -z+1/2)', '(x, -x, -z)',
5889 '(x+1/2, -x+1/2, -z+1/2)', '(z, x, x)',
5890 '(z+1/2, x+1/2, x+1/2)', '(z, -x, -x)',
5891 '(z+1/2, -x+1/2, -x+1/2)', '(-z, -x, x)',
5892 '(-z+1/2, -x+1/2, x+1/2)', '(-z, x, -x)',
5893 '(-z+1/2, x+1/2, -x+1/2)', '(x, z, x)',
5894 '(x+1/2, z+1/2, x+1/2)', '(-x, z, -x)',
5895 '(-x+1/2, z+1/2, -x+1/2)', '(x, -z, -x)',
5896 '(x+1/2, -z+1/2, -x+1/2)', '(-x, -z, x)',
5897 '(-x+1/2, -z+1/2, x+1/2)')),
5898 '48h': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
5899 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
5900 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
5901 '(x+1/2, -y+1/2, -z+1/2)', '(z, x, y)',
5902 '(z+1/2, x+1/2, y+1/2)', '(z, -x, -y)',
5903 '(z+1/2, -x+1/2, -y+1/2)', '(-z, -x, y)',
5904 '(-z+1/2, -x+1/2, y+1/2)', '(-z, x, -y)',
5905 '(-z+1/2, x+1/2, -y+1/2)', '(y, z, x)',
5906 '(y+1/2, z+1/2, x+1/2)', '(-y, z, -x)',
5907 '(-y+1/2, z+1/2, -x+1/2)', '(y, -z, -x)',
5908 '(y+1/2, -z+1/2, -x+1/2)', '(-y, -z, x)',
5909 '(-y+1/2, -z+1/2, x+1/2)', '(y, x, z)',
5910 '(y+1/2, x+1/2, z+1/2)', '(-y, -x, z)',
5911 '(-y+1/2, -x+1/2, z+1/2)', '(y, -x, -z)',
5912 '(y+1/2, -x+1/2, -z+1/2)', '(-y, x, -z)',
5913 '(-y+1/2, x+1/2, -z+1/2)', '(x, z, y)',
5914 '(x+1/2, z+1/2, y+1/2)', '(-x, z, -y)',
5915 '(-x+1/2, z+1/2, -y+1/2)', '(-x, -z, y)',
5916 '(-x+1/2, -z+1/2, y+1/2)', '(x, -z, -y)',
5917 '(x+1/2, -z+1/2, -y+1/2)', '(z, y, x)',
5918 '(z+1/2, y+1/2, x+1/2)', '(z, -y, -x)',
5919 '(z+1/2, -y+1/2, -x+1/2)', '(-z, y, -x)',
5920 '(-z+1/2, y+1/2, -x+1/2)', '(-z, -y, x)',
5921 '(-z+1/2, -y+1/2, x+1/2)'))},
5922 '218': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
5923 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
5924 '(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 0, 1/2)')),
5925 '6c': (0, ('(1/4, 1/2, 0)', '(3/4, 1/2, 0)', '(0, 1/4, 1/2)',
5926 '(0, 3/4, 1/2)', '(1/2, 0, 1/4)', '(1/2, 0, 3/4)')),
5927 '6d': (0, ('(1/4, 0, 1/2)', '(3/4, 0, 1/2)', '(1/2, 1/4, 0)',
5928 '(1/2, 3/4, 0)', '(0, 1/2, 1/4)', '(0, 1/2, 3/4)')),
5929 '8e': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
5930 '(x, -x, -x)', '(x+1/2, x+1/2, x+1/2)',
5931 '(-x+1/2, -x+1/2, x+1/2)', '(x+1/2, -x+1/2, -x+1/2)',
5932 '(-x+1/2, x+1/2, -x+1/2)')),
5933 '12f': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
5934 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
5935 '(1/2, x+1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
5936 '(x+1/2, 1/2, 1/2)', '(-x+1/2, 1/2, 1/2)',
5937 '(1/2, 1/2, x+1/2)', '(1/2, 1/2, -x+1/2)')),
5938 '12g': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
5939 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
5940 '(0, x+1/2, 1/2)', '(0, -x+1/2, 1/2)',
5941 '(x+1/2, 1/2, 0)', '(-x+1/2, 1/2, 0)',
5942 '(1/2, 0, x+1/2)', '(1/2, 0, -x+1/2)')),
5943 '12h': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
5944 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)',
5945 '(1/2, x+1/2, 0)', '(1/2, -x+1/2, 0)',
5946 '(x+1/2, 0, 1/2)', '(-x+1/2, 0, 1/2)',
5947 '(0, 1/2, x+1/2)', '(0, 1/2, -x+1/2)')),
5948 '24i': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
5949 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
5950 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
5951 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
5952 '(y+1/2, x+1/2, z+1/2)', '(-y+1/2, -x+1/2, z+1/2)',
5953 '(y+1/2, -x+1/2, -z+1/2)',
5954 '(-y+1/2, x+1/2, -z+1/2)', '(x+1/2, z+1/2, y+1/2)',
5955 '(-x+1/2, z+1/2, -y+1/2)',
5956 '(-x+1/2, -z+1/2, y+1/2)',
5957 '(x+1/2, -z+1/2, -y+1/2)', '(z+1/2, y+1/2, x+1/2)',
5958 '(z+1/2, -y+1/2, -x+1/2)',
5959 '(-z+1/2, y+1/2, -x+1/2)',
5960 '(-z+1/2, -y+1/2, x+1/2)'))},
5961 '219': {'8a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
5962 '(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 0)',
5963 '(0, 1/2, 0)', '(0, 0, 1/2)')),
5964 '8b': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
5965 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
5966 '(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
5967 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
5968 '24c': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
5969 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
5970 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)',
5971 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
5972 '(3/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
5973 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
5974 '(3/4, 1/2, 3/4)', '(1/4, 1/4, 0)',
5975 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
5976 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
5977 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
5978 '24d': (0, ('(1/4, 0, 0)', '(1/4, 1/2, 1/2)', '(3/4, 0, 1/2)',
5979 '(3/4, 1/2, 0)', '(3/4, 0, 0)', '(3/4, 1/2, 1/2)',
5980 '(1/4, 0, 1/2)', '(1/4, 1/2, 0)', '(0, 1/4, 0)',
5981 '(0, 3/4, 1/2)', '(1/2, 1/4, 1/2)', '(1/2, 3/4, 0)',
5982 '(0, 3/4, 0)', '(0, 1/4, 1/2)', '(1/2, 3/4, 1/2)',
5983 '(1/2, 1/4, 0)', '(0, 0, 1/4)', '(0, 1/2, 3/4)',
5984 '(1/2, 0, 3/4)', '(1/2, 1/2, 1/4)', '(0, 0, 3/4)',
5985 '(0, 1/2, 1/4)', '(1/2, 0, 1/4)', '(1/2, 1/2, 3/4)'
5986 )),
5987 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
5988 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
5989 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
5990 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
5991 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
5992 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
5993 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
5994 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
5995 '(x+1/2, x+1/2, x+1/2)', '(x+1/2, x, x)',
5996 '(x, x+1/2, x)', '(x, x, x+1/2)',
5997 '(-x+1/2, -x+1/2, x+1/2)', '(-x+1/2, -x, x)',
5998 '(-x, -x+1/2, x)', '(-x, -x, x+1/2)',
5999 '(x+1/2, -x+1/2, -x+1/2)', '(x+1/2, -x, -x)',
6000 '(x, -x+1/2, -x)', '(x, -x, -x+1/2)',
6001 '(-x+1/2, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
6002 '(-x, x+1/2, -x)', '(-x, x, -x+1/2)')),
6003 '48f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
6004 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
6005 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
6006 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
6007 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
6008 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
6009 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
6010 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
6011 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)',
6012 '(1/2, x+1/2, 1/2)', '(1/2, x, 0)', '(0, x+1/2, 0)',
6013 '(0, x, 1/2)', '(1/2, -x+1/2, 1/2)', '(1/2, -x, 0)',
6014 '(0, -x+1/2, 0)', '(0, -x, 1/2)',
6015 '(x+1/2, 1/2, 1/2)', '(x+1/2, 0, 0)', '(x, 1/2, 0)',
6016 '(x, 0, 1/2)', '(-x+1/2, 1/2, 1/2)',
6017 '(-x+1/2, 0, 0)', '(-x, 1/2, 0)', '(-x, 0, 1/2)',
6018 '(1/2, 1/2, x+1/2)', '(1/2, 0, x)', '(0, 1/2, x)',
6019 '(0, 0, x+1/2)', '(1/2, 1/2, -x+1/2)',
6020 '(1/2, 0, -x)', '(0, 1/2, -x)', '(0, 0, -x+1/2)')),
6021 '48g': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
6022 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
6023 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
6024 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
6025 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
6026 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
6027 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
6028 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
6029 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
6030 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
6031 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
6032 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)',
6033 '(3/4, x+1/2, 3/4)', '(3/4, x, 1/4)',
6034 '(1/4, x+1/2, 1/4)', '(1/4, x, 3/4)',
6035 '(1/4, -x+1/2, 3/4)', '(1/4, -x, 1/4)',
6036 '(3/4, -x+1/2, 1/4)', '(3/4, -x, 3/4)',
6037 '(x+1/2, 3/4, 3/4)', '(x+1/2, 1/4, 1/4)',
6038 '(x, 3/4, 1/4)', '(x, 1/4, 3/4)',
6039 '(-x+1/2, 3/4, 1/4)', '(-x+1/2, 1/4, 3/4)',
6040 '(-x, 3/4, 3/4)', '(-x, 1/4, 1/4)',
6041 '(3/4, 3/4, x+1/2)', '(3/4, 1/4, x)',
6042 '(1/4, 3/4, x)', '(1/4, 1/4, x+1/2)',
6043 '(3/4, 1/4, -x+1/2)', '(3/4, 3/4, -x)',
6044 '(1/4, 1/4, -x)', '(1/4, 3/4, -x+1/2)')),
6045 '96h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
6046 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
6047 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
6048 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
6049 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
6050 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
6051 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
6052 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
6053 '(z, x, y)', '(z, x+1/2, y+1/2)',
6054 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
6055 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
6056 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
6057 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
6058 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
6059 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
6060 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
6061 '(y, z, x)', '(y, z+1/2, x+1/2)',
6062 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
6063 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
6064 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
6065 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
6066 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
6067 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
6068 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
6069 '(y+1/2, x+1/2, z+1/2)', '(y+1/2, x, z)',
6070 '(y, x+1/2, z)', '(y, x, z+1/2)',
6071 '(-y+1/2, -x+1/2, z+1/2)', '(-y+1/2, -x, z)',
6072 '(-y, -x+1/2, z)', '(-y, -x, z+1/2)',
6073 '(y+1/2, -x+1/2, -z+1/2)', '(y+1/2, -x, -z)',
6074 '(y, -x+1/2, -z)', '(y, -x, -z+1/2)',
6075 '(-y+1/2, x+1/2, -z+1/2)', '(-y+1/2, x, -z)',
6076 '(-y, x+1/2, -z)', '(-y, x, -z+1/2)',
6077 '(x+1/2, z+1/2, y+1/2)', '(x+1/2, z, y)',
6078 '(x, z+1/2, y)', '(x, z, y+1/2)',
6079 '(-x+1/2, z+1/2, -y+1/2)', '(-x+1/2, z, -y)',
6080 '(-x, z+1/2, -y)', '(-x, z, -y+1/2)',
6081 '(-x+1/2, -z+1/2, y+1/2)', '(-x+1/2, -z, y)',
6082 '(-x, -z+1/2, y)', '(-x, -z, y+1/2)',
6083 '(x+1/2, -z+1/2, -y+1/2)', '(x+1/2, -z, -y)',
6084 '(x, -z+1/2, -y)', '(x, -z, -y+1/2)',
6085 '(z+1/2, y+1/2, x+1/2)', '(z+1/2, y, x)',
6086 '(z, y+1/2, x)', '(z, y, x+1/2)',
6087 '(z+1/2, -y+1/2, -x+1/2)', '(z+1/2, -y, -x)',
6088 '(z, -y+1/2, -x)', '(z, -y, -x+1/2)',
6089 '(-z+1/2, y+1/2, -x+1/2)', '(-z+1/2, y, -x)',
6090 '(-z, y+1/2, -x)', '(-z, y, -x+1/2)',
6091 '(-z+1/2, -y+1/2, x+1/2)', '(-z+1/2, -y, x)',
6092 '(-z, -y+1/2, x)', '(-z, -y, x+1/2)'))},
6093 '220': {'12a': (0, ('(3/8, 0, 1/4)', '(7/8, 1/2, 3/4)', '(1/8, 0, 3/4)',
6094 '(5/8, 1/2, 1/4)', '(1/4, 3/8, 0)',
6095 '(3/4, 7/8, 1/2)', '(3/4, 1/8, 0)',
6096 '(1/4, 5/8, 1/2)', '(0, 1/4, 3/8)',
6097 '(1/2, 3/4, 7/8)', '(0, 3/4, 1/8)',
6098 '(1/2, 1/4, 5/8)')),
6099 '12b': (0, ('(7/8, 0, 1/4)', '(3/8, 1/2, 3/4)', '(5/8, 0, 3/4)',
6100 '(1/8, 1/2, 1/4)', '(1/4, 7/8, 0)',
6101 '(3/4, 3/8, 1/2)', '(3/4, 5/8, 0)',
6102 '(1/4, 1/8, 1/2)', '(0, 1/4, 7/8)',
6103 '(1/2, 3/4, 3/8)', '(0, 3/4, 5/8)',
6104 '(1/2, 1/4, 1/8)')),
6105 '16c': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)',
6106 '(-x+1/2, -x, x+1/2)', '(-x, -x+1/2, x)',
6107 '(-x, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
6108 '(x+1/2, -x+1/2, -x)', '(x, -x, -x+1/2)',
6109 '(x+1/4, x+1/4, x+1/4)', '(x+3/4, x+3/4, x+3/4)',
6110 '(-x+1/4, -x+3/4, x+3/4)',
6111 '(-x+3/4, -x+1/4, x+1/4)',
6112 '(x+3/4, -x+1/4, -x+3/4)',
6113 '(x+1/4, -x+3/4, -x+1/4)',
6114 '(-x+3/4, x+3/4, -x+1/4)',
6115 '(-x+1/4, x+1/4, -x+3/4)')),
6116 '24d': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
6117 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(1/4, x, 0)',
6118 '(3/4, x+1/2, 1/2)', '(3/4, -x+1/2, 0)',
6119 '(1/4, -x, 1/2)', '(0, 1/4, x)',
6120 '(1/2, 3/4, x+1/2)', '(0, 3/4, -x+1/2)',
6121 '(1/2, 1/4, -x)', '(1/4, x+1/4, 1/2)',
6122 '(3/4, x+3/4, 0)', '(1/4, -x+3/4, 0)',
6123 '(3/4, -x+1/4, 1/2)', '(x+1/4, 1/2, 1/4)',
6124 '(x+3/4, 0, 3/4)', '(-x+3/4, 0, 1/4)',
6125 '(-x+1/4, 1/2, 3/4)', '(1/2, 1/4, x+1/4)',
6126 '(0, 3/4, x+3/4)', '(0, 1/4, -x+3/4)',
6127 '(1/2, 3/4, -x+1/4)')),
6128 '48e': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
6129 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
6130 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
6131 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
6132 '(z, x, y)', '(z+1/2, x+1/2, y+1/2)',
6133 '(z+1/2, -x+1/2, -y)', '(z, -x, -y+1/2)',
6134 '(-z+1/2, -x, y+1/2)', '(-z, -x+1/2, y)',
6135 '(-z, x+1/2, -y+1/2)', '(-z+1/2, x, -y)',
6136 '(y, z, x)', '(y+1/2, z+1/2, x+1/2)',
6137 '(-y, z+1/2, -x+1/2)', '(-y+1/2, z, -x)',
6138 '(y+1/2, -z+1/2, -x)', '(y, -z, -x+1/2)',
6139 '(-y+1/2, -z, x+1/2)', '(-y, -z+1/2, x)',
6140 '(y+1/4, x+1/4, z+1/4)', '(y+3/4, x+3/4, z+3/4)',
6141 '(-y+1/4, -x+3/4, z+3/4)',
6142 '(-y+3/4, -x+1/4, z+1/4)',
6143 '(y+3/4, -x+1/4, -z+3/4)',
6144 '(y+1/4, -x+3/4, -z+1/4)',
6145 '(-y+3/4, x+3/4, -z+1/4)',
6146 '(-y+1/4, x+1/4, -z+3/4)', '(x+1/4, z+1/4, y+1/4)',
6147 '(x+3/4, z+3/4, y+3/4)', '(-x+3/4, z+3/4, -y+1/4)',
6148 '(-x+1/4, z+1/4, -y+3/4)',
6149 '(-x+1/4, -z+3/4, y+3/4)',
6150 '(-x+3/4, -z+1/4, y+1/4)',
6151 '(x+3/4, -z+1/4, -y+3/4)',
6152 '(x+1/4, -z+3/4, -y+1/4)', '(z+1/4, y+1/4, x+1/4)',
6153 '(z+3/4, y+3/4, x+3/4)', '(z+3/4, -y+1/4, -x+3/4)',
6154 '(z+1/4, -y+3/4, -x+1/4)',
6155 '(-z+3/4, y+3/4, -x+1/4)',
6156 '(-z+1/4, y+1/4, -x+3/4)',
6157 '(-z+1/4, -y+3/4, x+3/4)',
6158 '(-z+3/4, -y+1/4, x+1/4)'))},
6159 '221': {'1a': (0, ('(0, 0, 0)', )),
6160 '1b': (0, ('(1/2, 1/2, 1/2)', )),
6161 '3c': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)')),
6162 '3d': (0, ('(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
6163 '6e': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)', '(0, -x, 0)',
6164 '(0, 0, x)', '(0, 0, -x)')),
6165 '6f': (1, ('(x, 1/2, 1/2)', '(-x, 1/2, 1/2)', '(1/2, x, 1/2)',
6166 '(1/2, -x, 1/2)', '(1/2, 1/2, x)', '(1/2, 1/2, -x)'
6167 )),
6168 '8g': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
6169 '(x, -x, -x)', '(x, x, -x)', '(-x, -x, -x)',
6170 '(x, -x, x)', '(-x, x, x)')),
6171 '12h': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
6172 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
6173 '(1/2, x, 0)', '(1/2, -x, 0)', '(x, 0, 1/2)',
6174 '(-x, 0, 1/2)', '(0, 1/2, -x)', '(0, 1/2, x)')),
6175 '12i': (2, ('(0, y, y)', '(0, -y, y)', '(0, y, -y)',
6176 '(0, -y, -y)', '(y, 0, y)', '(y, 0, -y)',
6177 '(-y, 0, y)', '(-y, 0, -y)', '(y, y, 0)',
6178 '(-y, y, 0)', '(y, -y, 0)', '(-y, -y, 0)')),
6179 '12j': (2, ('(1/2, y, y)', '(1/2, -y, y)', '(1/2, y, -y)',
6180 '(1/2, -y, -y)', '(y, 1/2, y)', '(y, 1/2, -y)',
6181 '(-y, 1/2, y)', '(-y, 1/2, -y)', '(y, y, 1/2)',
6182 '(-y, y, 1/2)', '(y, -y, 1/2)', '(-y, -y, 1/2)')),
6183 '24k': (6, ('(0, y, z)', '(0, -y, z)', '(0, y, -z)',
6184 '(0, -y, -z)', '(z, 0, y)', '(z, 0, -y)',
6185 '(-z, 0, y)', '(-z, 0, -y)', '(y, z, 0)',
6186 '(-y, z, 0)', '(y, -z, 0)', '(-y, -z, 0)',
6187 '(y, 0, -z)', '(-y, 0, -z)', '(y, 0, z)',
6188 '(-y, 0, z)', '(0, z, -y)', '(0, z, y)',
6189 '(0, -z, -y)', '(0, -z, y)', '(z, y, 0)',
6190 '(z, -y, 0)', '(-z, y, 0)', '(-z, -y, 0)')),
6191 '24l': (6, ('(1/2, y, z)', '(1/2, -y, z)', '(1/2, y, -z)',
6192 '(1/2, -y, -z)', '(z, 1/2, y)', '(z, 1/2, -y)',
6193 '(-z, 1/2, y)', '(-z, 1/2, -y)', '(y, z, 1/2)',
6194 '(-y, z, 1/2)', '(y, -z, 1/2)', '(-y, -z, 1/2)',
6195 '(y, 1/2, -z)', '(-y, 1/2, -z)', '(y, 1/2, z)',
6196 '(-y, 1/2, z)', '(1/2, z, -y)', '(1/2, z, y)',
6197 '(1/2, -z, -y)', '(1/2, -z, y)', '(z, y, 1/2)',
6198 '(z, -y, 1/2)', '(-z, y, 1/2)', '(-z, -y, 1/2)')),
6199 '24m': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, -z)',
6200 '(x, -x, -z)', '(z, x, x)', '(z, -x, -x)',
6201 '(-z, -x, x)', '(-z, x, -x)', '(x, z, x)',
6202 '(-x, z, -x)', '(x, -z, -x)', '(-x, -z, x)',
6203 '(x, x, -z)', '(-x, -x, -z)', '(x, -x, z)',
6204 '(-x, x, z)', '(x, z, -x)', '(-x, z, x)',
6205 '(-x, -z, -x)', '(x, -z, x)', '(z, x, -x)',
6206 '(z, -x, x)', '(-z, x, x)', '(-z, -x, -x)')),
6207 '48n': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
6208 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
6209 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
6210 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
6211 '(y, x, -z)', '(-y, -x, -z)', '(y, -x, z)',
6212 '(-y, x, z)', '(x, z, -y)', '(-x, z, y)',
6213 '(-x, -z, -y)', '(x, -z, y)', '(z, y, -x)',
6214 '(z, -y, x)', '(-z, y, x)', '(-z, -y, -x)',
6215 '(-x, -y, -z)', '(x, y, -z)', '(x, -y, z)',
6216 '(-x, y, z)', '(-z, -x, -y)', '(-z, x, y)',
6217 '(z, x, -y)', '(z, -x, y)', '(-y, -z, -x)',
6218 '(y, -z, x)', '(-y, z, x)', '(y, z, -x)',
6219 '(-y, -x, z)', '(y, x, z)', '(-y, x, -z)',
6220 '(y, -x, -z)', '(-x, -z, y)', '(x, -z, -y)',
6221 '(x, z, y)', '(-x, z, -y)', '(-z, -y, x)',
6222 '(-z, y, -x)', '(z, -y, -x)', '(z, y, x)'))},
6223 '222:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
6224 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
6225 '(1/2, 0, 0)', '(0, 1/2, 0)', '(0, 0, 1/2)')),
6226 '8c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
6227 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)',
6228 '(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)',
6229 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
6230 '12d': (0, ('(1/4, 0, 1/2)', '(3/4, 0, 1/2)', '(1/2, 1/4, 0)',
6231 '(1/2, 3/4, 0)', '(0, 1/2, 1/4)', '(0, 1/2, 3/4)',
6232 '(0, 1/4, 1/2)', '(0, 3/4, 1/2)', '(1/4, 1/2, 0)',
6233 '(3/4, 1/2, 0)', '(1/2, 0, 3/4)', '(1/2, 0, 1/4)'
6234 )),
6235 '12e': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
6236 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
6237 '(-x+1/2, 1/2, 1/2)', '(x+1/2, 1/2, 1/2)',
6238 '(1/2, -x+1/2, 1/2)', '(1/2, x+1/2, 1/2)',
6239 '(1/2, 1/2, -x+1/2)', '(1/2, 1/2, x+1/2)')),
6240 '16f': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
6241 '(x, -x, -x)', '(x, x, -x)', '(-x, -x, -x)',
6242 '(x, -x, x)', '(-x, x, x)',
6243 '(-x+1/2, -x+1/2, -x+1/2)',
6244 '(x+1/2, x+1/2, -x+1/2)',
6245 '(x+1/2, -x+1/2, x+1/2)',
6246 '(-x+1/2, x+1/2, x+1/2)',
6247 '(-x+1/2, -x+1/2, x+1/2)',
6248 '(x+1/2, x+1/2, x+1/2)',
6249 '(-x+1/2, x+1/2, -x+1/2)',
6250 '(x+1/2, -x+1/2, -x+1/2)')),
6251 '24g': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
6252 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)',
6253 '(0, x, 1/2)', '(0, -x, 1/2)', '(x, 1/2, 0)',
6254 '(-x, 1/2, 0)', '(1/2, 0, -x)', '(1/2, 0, x)',
6255 '(-x+1/2, 1/2, 0)', '(x+1/2, 1/2, 0)',
6256 '(0, -x+1/2, 1/2)', '(0, x+1/2, 1/2)',
6257 '(1/2, 0, -x+1/2)', '(1/2, 0, x+1/2)',
6258 '(1/2, -x+1/2, 0)', '(1/2, x+1/2, 0)',
6259 '(-x+1/2, 0, 1/2)', '(x+1/2, 0, 1/2)',
6260 '(0, 1/2, x+1/2)', '(0, 1/2, -x+1/2)')),
6261 '24h': (2, ('(0, y, y)', '(0, -y, y)', '(0, y, -y)',
6262 '(0, -y, -y)', '(y, 0, y)', '(y, 0, -y)',
6263 '(-y, 0, y)', '(-y, 0, -y)', '(y, y, 0)',
6264 '(-y, y, 0)', '(y, -y, 0)', '(-y, -y, 0)',
6265 '(1/2, -y+1/2, -y+1/2)', '(1/2, y+1/2, -y+1/2)',
6266 '(1/2, -y+1/2, y+1/2)', '(1/2, y+1/2, y+1/2)',
6267 '(-y+1/2, 1/2, -y+1/2)', '(-y+1/2, 1/2, y+1/2)',
6268 '(y+1/2, 1/2, -y+1/2)', '(y+1/2, 1/2, y+1/2)',
6269 '(-y+1/2, -y+1/2, 1/2)', '(y+1/2, -y+1/2, 1/2)',
6270 '(-y+1/2, y+1/2, 1/2)', '(y+1/2, y+1/2, 1/2)')),
6271 '48i': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
6272 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
6273 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
6274 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
6275 '(y, x, -z)', '(-y, -x, -z)', '(y, -x, z)',
6276 '(-y, x, z)', '(x, z, -y)', '(-x, z, y)',
6277 '(-x, -z, -y)', '(x, -z, y)', '(z, y, -x)',
6278 '(z, -y, x)', '(-z, y, x)', '(-z, -y, -x)',
6279 '(-x+1/2, -y+1/2, -z+1/2)',
6280 '(x+1/2, y+1/2, -z+1/2)',
6281 '(x+1/2, -y+1/2, z+1/2)',
6282 '(-x+1/2, y+1/2, z+1/2)',
6283 '(-z+1/2, -x+1/2, -y+1/2)',
6284 '(-z+1/2, x+1/2, y+1/2)',
6285 '(z+1/2, x+1/2, -y+1/2)',
6286 '(z+1/2, -x+1/2, y+1/2)',
6287 '(-y+1/2, -z+1/2, -x+1/2)',
6288 '(y+1/2, -z+1/2, x+1/2)',
6289 '(-y+1/2, z+1/2, x+1/2)',
6290 '(y+1/2, z+1/2, -x+1/2)',
6291 '(-y+1/2, -x+1/2, z+1/2)',
6292 '(y+1/2, x+1/2, z+1/2)',
6293 '(-y+1/2, x+1/2, -z+1/2)',
6294 '(y+1/2, -x+1/2, -z+1/2)',
6295 '(-x+1/2, -z+1/2, y+1/2)',
6296 '(x+1/2, -z+1/2, -y+1/2)',
6297 '(x+1/2, z+1/2, y+1/2)',
6298 '(-x+1/2, z+1/2, -y+1/2)',
6299 '(-z+1/2, -y+1/2, x+1/2)',
6300 '(-z+1/2, y+1/2, -x+1/2)',
6301 '(z+1/2, -y+1/2, -x+1/2)',
6302 '(z+1/2, y+1/2, x+1/2)'))},
6303 '222:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
6304 '6b': (0, ('(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
6305 '(1/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)',
6306 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)')),
6307 '8c': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
6308 '(0, 1/2, 1/2)', '(0, 0, 1/2)', '(1/2, 1/2, 1/2)',
6309 '(0, 1/2, 0)', '(1/2, 0, 0)')),
6310 '12d': (0, ('(0, 3/4, 1/4)', '(1/2, 3/4, 1/4)',
6311 '(1/4, 0, 3/4)', '(1/4, 1/2, 3/4)',
6312 '(3/4, 1/4, 0)', '(3/4, 1/4, 1/2)',
6313 '(3/4, 0, 1/4)', '(3/4, 1/2, 1/4)',
6314 '(0, 1/4, 3/4)', '(1/2, 1/4, 3/4)',
6315 '(1/4, 3/4, 1/2)', '(1/4, 3/4, 0)')),
6316 '12e': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
6317 '(1/4, x, 1/4)', '(1/4, -x+1/2, 1/4)',
6318 '(1/4, 1/4, x)', '(1/4, 1/4, -x+1/2)',
6319 '(-x, 3/4, 3/4)', '(x+1/2, 3/4, 3/4)',
6320 '(3/4, -x, 3/4)', '(3/4, x+1/2, 3/4)',
6321 '(3/4, 3/4, -x)', '(3/4, 3/4, x+1/2)')),
6322 '16f': (1, ('(x, x, x)', '(-x+1/2, -x+1/2, x)',
6323 '(-x+1/2, x, -x+1/2)', '(x, -x+1/2, -x+1/2)',
6324 '(x, x, -x+1/2)', '(-x+1/2, -x+1/2, -x+1/2)',
6325 '(x, -x+1/2, x)', '(-x+1/2, x, x)',
6326 '(-x, -x, -x)', '(x+1/2, x+1/2, -x)',
6327 '(x+1/2, -x, x+1/2)', '(-x, x+1/2, x+1/2)',
6328 '(-x, -x, x+1/2)', '(x+1/2, x+1/2, x+1/2)',
6329 '(-x, x+1/2, -x)', '(x+1/2, -x, -x)')),
6330 '24g': (1, ('(x, 3/4, 1/4)', '(-x+1/2, 3/4, 1/4)',
6331 '(1/4, x, 3/4)', '(1/4, -x+1/2, 3/4)',
6332 '(3/4, 1/4, x)', '(3/4, 1/4, -x+1/2)',
6333 '(3/4, x, 1/4)', '(3/4, -x+1/2, 1/4)',
6334 '(x, 1/4, 3/4)', '(-x+1/2, 1/4, 3/4)',
6335 '(1/4, 3/4, -x+1/2)', '(1/4, 3/4, x)',
6336 '(-x, 1/4, 3/4)', '(x+1/2, 1/4, 3/4)',
6337 '(3/4, -x, 1/4)', '(3/4, x+1/2, 1/4)',
6338 '(1/4, 3/4, -x)', '(1/4, 3/4, x+1/2)',
6339 '(1/4, -x, 3/4)', '(1/4, x+1/2, 3/4)',
6340 '(-x, 3/4, 1/4)', '(x+1/2, 3/4, 1/4)',
6341 '(3/4, 1/4, x+1/2)', '(3/4, 1/4, -x)')),
6342 '24h': (2, ('(1/4, y, y)', '(1/4, -y+1/2, y)',
6343 '(1/4, y, -y+1/2)', '(1/4, -y+1/2, -y+1/2)',
6344 '(y, 1/4, y)', '(y, 1/4, -y+1/2)',
6345 '(-y+1/2, 1/4, y)', '(-y+1/2, 1/4, -y+1/2)',
6346 '(y, y, 1/4)', '(-y+1/2, y, 1/4)',
6347 '(y, -y+1/2, 1/4)', '(-y+1/2, -y+1/2, 1/4)',
6348 '(3/4, -y, -y)', '(3/4, y+1/2, -y)',
6349 '(3/4, -y, y+1/2)', '(3/4, y+1/2, y+1/2)',
6350 '(-y, 3/4, -y)', '(-y, 3/4, y+1/2)',
6351 '(y+1/2, 3/4, -y)', '(y+1/2, 3/4, y+1/2)',
6352 '(-y, -y, 3/4)', '(y+1/2, -y, 3/4)',
6353 '(-y, y+1/2, 3/4)', '(y+1/2, y+1/2, 3/4)')),
6354 '48i': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
6355 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
6356 '(z, x, y)', '(z, -x+1/2, -y+1/2)',
6357 '(-z+1/2, -x+1/2, y)', '(-z+1/2, x, -y+1/2)',
6358 '(y, z, x)', '(-y+1/2, z, -x+1/2)',
6359 '(y, -z+1/2, -x+1/2)', '(-y+1/2, -z+1/2, x)',
6360 '(y, x, -z+1/2)', '(-y+1/2, -x+1/2, -z+1/2)',
6361 '(y, -x+1/2, z)', '(-y+1/2, x, z)',
6362 '(x, z, -y+1/2)', '(-x+1/2, z, y)',
6363 '(-x+1/2, -z+1/2, -y+1/2)', '(x, -z+1/2, y)',
6364 '(z, y, -x+1/2)', '(z, -y+1/2, x)',
6365 '(-z+1/2, y, x)', '(-z+1/2, -y+1/2, -x+1/2)',
6366 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
6367 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)',
6368 '(-z, -x, -y)', '(-z, x+1/2, y+1/2)',
6369 '(z+1/2, x+1/2, -y)', '(z+1/2, -x, y+1/2)',
6370 '(-y, -z, -x)', '(y+1/2, -z, x+1/2)',
6371 '(-y, z+1/2, x+1/2)', '(y+1/2, z+1/2, -x)',
6372 '(-y, -x, z+1/2)', '(y+1/2, x+1/2, z+1/2)',
6373 '(-y, x+1/2, -z)', '(y+1/2, -x, -z)',
6374 '(-x, -z, y+1/2)', '(x+1/2, -z, -y)',
6375 '(x+1/2, z+1/2, y+1/2)', '(-x, z+1/2, -y)',
6376 '(-z, -y, x+1/2)', '(-z, y+1/2, -x)',
6377 '(z+1/2, -y, -x)', '(z+1/2, y+1/2, x+1/2)'))},
6378 '223': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
6379 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
6380 '(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 0, 1/2)')),
6381 '6c': (0, ('(1/4, 0, 1/2)', '(3/4, 0, 1/2)', '(1/2, 1/4, 0)',
6382 '(1/2, 3/4, 0)', '(0, 1/2, 1/4)', '(0, 1/2, 3/4)')),
6383 '6d': (0, ('(1/4, 1/2, 0)', '(3/4, 1/2, 0)', '(0, 1/4, 1/2)',
6384 '(0, 3/4, 1/2)', '(1/2, 0, 1/4)', '(1/2, 0, 3/4)')),
6385 '8e': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
6386 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)',
6387 '(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
6388 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
6389 '12f': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
6390 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
6391 '(1/2, x+1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
6392 '(x+1/2, 1/2, 1/2)', '(-x+1/2, 1/2, 1/2)',
6393 '(1/2, 1/2, -x+1/2)', '(1/2, 1/2, x+1/2)')),
6394 '12g': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
6395 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)',
6396 '(1/2, x+1/2, 0)', '(1/2, -x+1/2, 0)',
6397 '(x+1/2, 0, 1/2)', '(-x+1/2, 0, 1/2)',
6398 '(0, 1/2, -x+1/2)', '(0, 1/2, x+1/2)')),
6399 '12h': (1, ('(x, 1/2, 0)', '(-x, 1/2, 0)', '(0, x, 1/2)',
6400 '(0, -x, 1/2)', '(1/2, 0, x)', '(1/2, 0, -x)',
6401 '(0, x+1/2, 1/2)', '(0, -x+1/2, 1/2)',
6402 '(x+1/2, 1/2, 0)', '(-x+1/2, 1/2, 0)',
6403 '(1/2, 0, -x+1/2)', '(1/2, 0, x+1/2)')),
6404 '16i': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
6405 '(x, -x, -x)', '(x+1/2, x+1/2, -x+1/2)',
6406 '(-x+1/2, -x+1/2, -x+1/2)',
6407 '(x+1/2, -x+1/2, x+1/2)', '(-x+1/2, x+1/2, x+1/2)',
6408 '(-x, -x, -x)', '(x, x, -x)', '(x, -x, x)',
6409 '(-x, x, x)', '(-x+1/2, -x+1/2, x+1/2)',
6410 '(x+1/2, x+1/2, x+1/2)', '(-x+1/2, x+1/2, -x+1/2)',
6411 '(x+1/2, -x+1/2, -x+1/2)')),
6412 '24j': (2, ('(1/4, y, y+1/2)', '(3/4, -y, y+1/2)',
6413 '(3/4, y, -y+1/2)', '(1/4, -y, -y+1/2)',
6414 '(y+1/2, 1/4, y)', '(y+1/2, 3/4, -y)',
6415 '(-y+1/2, 3/4, y)', '(-y+1/2, 1/4, -y)',
6416 '(y, y+1/2, 1/4)', '(-y, y+1/2, 3/4)',
6417 '(y, -y+1/2, 3/4)', '(-y, -y+1/2, 1/4)',
6418 '(3/4, -y, -y+1/2)', '(1/4, y, -y+1/2)',
6419 '(1/4, -y, y+1/2)', '(3/4, y, y+1/2)',
6420 '(-y+1/2, 3/4, -y)', '(-y+1/2, 1/4, y)',
6421 '(y+1/2, 1/4, -y)', '(y+1/2, 3/4, y)',
6422 '(-y, -y+1/2, 3/4)', '(y, -y+1/2, 1/4)',
6423 '(-y, y+1/2, 1/4)', '(y, y+1/2, 3/4)')),
6424 '24k': (6, ('(0, y, z)', '(0, -y, z)', '(0, y, -z)',
6425 '(0, -y, -z)', '(z, 0, y)', '(z, 0, -y)',
6426 '(-z, 0, y)', '(-z, 0, -y)', '(y, z, 0)',
6427 '(-y, z, 0)', '(y, -z, 0)', '(-y, -z, 0)',
6428 '(y+1/2, 1/2, -z+1/2)', '(-y+1/2, 1/2, -z+1/2)',
6429 '(y+1/2, 1/2, z+1/2)', '(-y+1/2, 1/2, z+1/2)',
6430 '(1/2, z+1/2, -y+1/2)', '(1/2, z+1/2, y+1/2)',
6431 '(1/2, -z+1/2, -y+1/2)', '(1/2, -z+1/2, y+1/2)',
6432 '(z+1/2, y+1/2, 1/2)', '(z+1/2, -y+1/2, 1/2)',
6433 '(-z+1/2, y+1/2, 1/2)', '(-z+1/2, -y+1/2, 1/2)')),
6434 '48l': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
6435 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
6436 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
6437 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
6438 '(y+1/2, x+1/2, -z+1/2)',
6439 '(-y+1/2, -x+1/2, -z+1/2)',
6440 '(y+1/2, -x+1/2, z+1/2)', '(-y+1/2, x+1/2, z+1/2)',
6441 '(x+1/2, z+1/2, -y+1/2)', '(-x+1/2, z+1/2, y+1/2)',
6442 '(-x+1/2, -z+1/2, -y+1/2)',
6443 '(x+1/2, -z+1/2, y+1/2)', '(z+1/2, y+1/2, -x+1/2)',
6444 '(z+1/2, -y+1/2, x+1/2)', '(-z+1/2, y+1/2, x+1/2)',
6445 '(-z+1/2, -y+1/2, -x+1/2)', '(-x, -y, -z)',
6446 '(x, y, -z)', '(x, -y, z)', '(-x, y, z)',
6447 '(-z, -x, -y)', '(-z, x, y)', '(z, x, -y)',
6448 '(z, -x, y)', '(-y, -z, -x)', '(y, -z, x)',
6449 '(-y, z, x)', '(y, z, -x)',
6450 '(-y+1/2, -x+1/2, z+1/2)', '(y+1/2, x+1/2, z+1/2)',
6451 '(-y+1/2, x+1/2, -z+1/2)',
6452 '(y+1/2, -x+1/2, -z+1/2)',
6453 '(-x+1/2, -z+1/2, y+1/2)',
6454 '(x+1/2, -z+1/2, -y+1/2)', '(x+1/2, z+1/2, y+1/2)',
6455 '(-x+1/2, z+1/2, -y+1/2)',
6456 '(-z+1/2, -y+1/2, x+1/2)',
6457 '(-z+1/2, y+1/2, -x+1/2)',
6458 '(z+1/2, -y+1/2, -x+1/2)', '(z+1/2, y+1/2, x+1/2)'
6459 ))},
6460 '224:1': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
6461 '4b': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 1/4)',
6462 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 3/4)')),
6463 '4c': (0, ('(3/4, 3/4, 3/4)', '(1/4, 1/4, 3/4)',
6464 '(1/4, 3/4, 1/4)', '(3/4, 1/4, 1/4)')),
6465 '6d': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 1/2)', '(1/2, 1/2, 0)',
6466 '(0, 1/2, 0)', '(1/2, 0, 0)', '(0, 0, 1/2)')),
6467 '8e': (1, ('(x, x, x)', '(-x, -x, x)', '(-x, x, -x)',
6468 '(x, -x, -x)', '(x+1/2, x+1/2, -x+1/2)',
6469 '(-x+1/2, -x+1/2, -x+1/2)',
6470 '(x+1/2, -x+1/2, x+1/2)', '(-x+1/2, x+1/2, x+1/2)'
6471 )),
6472 '12f': (0, ('(1/4, 0, 1/2)', '(3/4, 0, 1/2)', '(1/2, 1/4, 0)',
6473 '(1/2, 3/4, 0)', '(0, 1/2, 1/4)', '(0, 1/2, 3/4)',
6474 '(1/4, 1/2, 0)', '(3/4, 1/2, 0)', '(0, 1/4, 1/2)',
6475 '(0, 3/4, 1/2)', '(1/2, 0, 1/4)', '(1/2, 0, 3/4)'
6476 )),
6477 '12g': (1, ('(x, 0, 0)', '(-x, 0, 0)', '(0, x, 0)',
6478 '(0, -x, 0)', '(0, 0, x)', '(0, 0, -x)',
6479 '(1/2, x+1/2, 1/2)', '(1/2, -x+1/2, 1/2)',
6480 '(x+1/2, 1/2, 1/2)', '(-x+1/2, 1/2, 1/2)',
6481 '(1/2, 1/2, -x+1/2)', '(1/2, 1/2, x+1/2)')),
6482 '24h': (1, ('(x, 0, 1/2)', '(-x, 0, 1/2)', '(1/2, x, 0)',
6483 '(1/2, -x, 0)', '(0, 1/2, x)', '(0, 1/2, -x)',
6484 '(1/2, x+1/2, 0)', '(1/2, -x+1/2, 0)',
6485 '(x+1/2, 0, 1/2)', '(-x+1/2, 0, 1/2)',
6486 '(0, 1/2, -x+1/2)', '(0, 1/2, x+1/2)',
6487 '(-x+1/2, 1/2, 0)', '(x+1/2, 1/2, 0)',
6488 '(0, -x+1/2, 1/2)', '(0, x+1/2, 1/2)',
6489 '(1/2, 0, -x+1/2)', '(1/2, 0, x+1/2)',
6490 '(0, -x, 1/2)', '(0, x, 1/2)', '(-x, 1/2, 0)',
6491 '(x, 1/2, 0)', '(1/2, 0, x)', '(1/2, 0, -x)')),
6492 '24i': (2, ('(1/4, y, -y+1/2)', '(3/4, -y, -y+1/2)',
6493 '(3/4, y, y+1/2)', '(1/4, -y, y+1/2)',
6494 '(-y+1/2, 1/4, y)', '(-y+1/2, 3/4, -y)',
6495 '(y+1/2, 3/4, y)', '(y+1/2, 1/4, -y)',
6496 '(y, -y+1/2, 1/4)', '(-y, -y+1/2, 3/4)',
6497 '(y, y+1/2, 3/4)', '(-y, y+1/2, 1/4)',
6498 '(1/4, -y+1/2, y)', '(3/4, y+1/2, y)',
6499 '(3/4, -y+1/2, -y)', '(1/4, y+1/2, -y)',
6500 '(y, 1/4, -y+1/2)', '(y, 3/4, y+1/2)',
6501 '(-y, 3/4, -y+1/2)', '(-y, 1/4, y+1/2)',
6502 '(-y+1/2, y, 1/4)', '(y+1/2, y, 3/4)',
6503 '(-y+1/2, -y, 3/4)', '(y+1/2, -y, 1/4)')),
6504 '24j': (2, ('(1/4, y, y+1/2)', '(3/4, -y, y+1/2)',
6505 '(3/4, y, -y+1/2)', '(1/4, -y, -y+1/2)',
6506 '(y+1/2, 1/4, y)', '(y+1/2, 3/4, -y)',
6507 '(-y+1/2, 3/4, y)', '(-y+1/2, 1/4, -y)',
6508 '(y, y+1/2, 1/4)', '(-y, y+1/2, 3/4)',
6509 '(y, -y+1/2, 3/4)', '(-y, -y+1/2, 1/4)',
6510 '(1/4, -y+1/2, -y)', '(3/4, y+1/2, -y)',
6511 '(3/4, -y+1/2, y)', '(1/4, y+1/2, y)',
6512 '(-y, 1/4, -y+1/2)', '(-y, 3/4, y+1/2)',
6513 '(y, 3/4, -y+1/2)', '(y, 1/4, y+1/2)',
6514 '(-y+1/2, -y, 1/4)', '(y+1/2, -y, 3/4)',
6515 '(-y+1/2, y, 3/4)', '(y+1/2, y, 1/4)')),
6516 '24k': (5, ('(x, x, z)', '(-x, -x, z)', '(-x, x, -z)',
6517 '(x, -x, -z)', '(z, x, x)', '(z, -x, -x)',
6518 '(-z, -x, x)', '(-z, x, -x)', '(x, z, x)',
6519 '(-x, z, -x)', '(x, -z, -x)', '(-x, -z, x)',
6520 '(x+1/2, x+1/2, -z+1/2)',
6521 '(-x+1/2, -x+1/2, -z+1/2)',
6522 '(x+1/2, -x+1/2, z+1/2)',
6523 '(-x+1/2, x+1/2, z+1/2)',
6524 '(x+1/2, z+1/2, -x+1/2)',
6525 '(-x+1/2, z+1/2, x+1/2)',
6526 '(-x+1/2, -z+1/2, -x+1/2)',
6527 '(x+1/2, -z+1/2, x+1/2)',
6528 '(z+1/2, x+1/2, -x+1/2)',
6529 '(z+1/2, -x+1/2, x+1/2)',
6530 '(-z+1/2, x+1/2, x+1/2)',
6531 '(-z+1/2, -x+1/2, -x+1/2)')),
6532 '48l': (7, ('(x, y, z)', '(-x, -y, z)', '(-x, y, -z)',
6533 '(x, -y, -z)', '(z, x, y)', '(z, -x, -y)',
6534 '(-z, -x, y)', '(-z, x, -y)', '(y, z, x)',
6535 '(-y, z, -x)', '(y, -z, -x)', '(-y, -z, x)',
6536 '(y+1/2, x+1/2, -z+1/2)',
6537 '(-y+1/2, -x+1/2, -z+1/2)',
6538 '(y+1/2, -x+1/2, z+1/2)',
6539 '(-y+1/2, x+1/2, z+1/2)',
6540 '(x+1/2, z+1/2, -y+1/2)',
6541 '(-x+1/2, z+1/2, y+1/2)',
6542 '(-x+1/2, -z+1/2, -y+1/2)',
6543 '(x+1/2, -z+1/2, y+1/2)',
6544 '(z+1/2, y+1/2, -x+1/2)',
6545 '(z+1/2, -y+1/2, x+1/2)',
6546 '(-z+1/2, y+1/2, x+1/2)',
6547 '(-z+1/2, -y+1/2, -x+1/2)',
6548 '(-x+1/2, -y+1/2, -z+1/2)',
6549 '(x+1/2, y+1/2, -z+1/2)',
6550 '(x+1/2, -y+1/2, z+1/2)',
6551 '(-x+1/2, y+1/2, z+1/2)',
6552 '(-z+1/2, -x+1/2, -y+1/2)',
6553 '(-z+1/2, x+1/2, y+1/2)',
6554 '(z+1/2, x+1/2, -y+1/2)',
6555 '(z+1/2, -x+1/2, y+1/2)',
6556 '(-y+1/2, -z+1/2, -x+1/2)',
6557 '(y+1/2, -z+1/2, x+1/2)',
6558 '(-y+1/2, z+1/2, x+1/2)',
6559 '(y+1/2, z+1/2, -x+1/2)', '(-y, -x, z)',
6560 '(y, x, z)', '(-y, x, -z)', '(y, -x, -z)',
6561 '(-x, -z, y)', '(x, -z, -y)', '(x, z, y)',
6562 '(-x, z, -y)', '(-z, -y, x)', '(-z, y, -x)',
6563 '(z, -y, -x)', '(z, y, x)'))},
6564 '224:2': {'2a': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
6565 '4b': (0, ('(0, 0, 0)', '(1/2, 1/2, 0)', '(1/2, 0, 1/2)',
6566 '(0, 1/2, 1/2)')),
6567 '4c': (0, ('(1/2, 1/2, 1/2)', '(0, 0, 1/2)', '(0, 1/2, 0)',
6568 '(1/2, 0, 0)')),
6569 '6d': (0, ('(1/4, 3/4, 3/4)', '(3/4, 1/4, 3/4)',
6570 '(3/4, 3/4, 1/4)', '(1/4, 3/4, 1/4)',
6571 '(3/4, 1/4, 1/4)', '(1/4, 1/4, 3/4)')),
6572 '8e': (1, ('(x, x, x)', '(-x+1/2, -x+1/2, x)',
6573 '(-x+1/2, x, -x+1/2)', '(x, -x+1/2, -x+1/2)',
6574 '(x+1/2, x+1/2, -x)', '(-x, -x, -x)',
6575 '(x+1/2, -x, x+1/2)', '(-x, x+1/2, x+1/2)')),
6576 '12f': (0, ('(1/2, 1/4, 3/4)', '(0, 1/4, 3/4)',
6577 '(3/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
6578 '(1/4, 3/4, 1/2)', '(1/4, 3/4, 0)',
6579 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)',
6580 '(1/4, 1/2, 3/4)', '(1/4, 0, 3/4)',
6581 '(3/4, 1/4, 1/2)', '(3/4, 1/4, 0)')),
6582 '12g': (1, ('(x, 1/4, 1/4)', '(-x+1/2, 1/4, 1/4)',
6583 '(1/4, x, 1/4)', '(1/4, -x+1/2, 1/4)',
6584 '(1/4, 1/4, x)', '(1/4, 1/4, -x+1/2)',
6585 '(3/4, x+1/2, 3/4)', '(3/4, -x, 3/4)',
6586 '(x+1/2, 3/4, 3/4)', '(-x, 3/4, 3/4)',
6587 '(3/4, 3/4, -x)', '(3/4, 3/4, x+1/2)')),
6588 '24h': (1, ('(x, 1/4, 3/4)', '(-x+1/2, 1/4, 3/4)',
6589 '(3/4, x, 1/4)', '(3/4, -x+1/2, 1/4)',
6590 '(1/4, 3/4, x)', '(1/4, 3/4, -x+1/2)',
6591 '(3/4, x+1/2, 1/4)', '(3/4, -x, 1/4)',
6592 '(x+1/2, 1/4, 3/4)', '(-x, 1/4, 3/4)',
6593 '(1/4, 3/4, -x)', '(1/4, 3/4, x+1/2)',
6594 '(-x, 3/4, 1/4)', '(x+1/2, 3/4, 1/4)',
6595 '(1/4, -x, 3/4)', '(1/4, x+1/2, 3/4)',
6596 '(3/4, 1/4, -x)', '(3/4, 1/4, x+1/2)',
6597 '(1/4, -x+1/2, 3/4)', '(1/4, x, 3/4)',
6598 '(-x+1/2, 3/4, 1/4)', '(x, 3/4, 1/4)',
6599 '(3/4, 1/4, x)', '(3/4, 1/4, -x+1/2)')),
6600 '24i': (2, ('(1/2, y, y+1/2)', '(0, -y+1/2, y+1/2)',
6601 '(0, y, -y)', '(1/2, -y+1/2, -y)',
6602 '(y+1/2, 1/2, y)', '(y+1/2, 0, -y+1/2)',
6603 '(-y, 0, y)', '(-y, 1/2, -y+1/2)',
6604 '(y, y+1/2, 1/2)', '(-y+1/2, y+1/2, 0)',
6605 '(y, -y, 0)', '(-y+1/2, -y, 1/2)',
6606 '(1/2, -y, -y+1/2)', '(0, y+1/2, -y+1/2)',
6607 '(0, -y, y)', '(1/2, y+1/2, y)',
6608 '(-y+1/2, 1/2, -y)', '(-y+1/2, 0, y+1/2)',
6609 '(y, 0, -y)', '(y, 1/2, y+1/2)',
6610 '(-y, -y+1/2, 1/2)', '(y+1/2, -y+1/2, 0)',
6611 '(-y, y, 0)', '(y+1/2, y, 1/2)')),
6612 '24j': (2, ('(1/2, y, -y)', '(0, -y+1/2, -y)',
6613 '(0, y, y+1/2)', '(1/2, -y+1/2, y+1/2)',
6614 '(-y, 1/2, y)', '(-y, 0, -y+1/2)',
6615 '(y+1/2, 0, y)', '(y+1/2, 1/2, -y+1/2)',
6616 '(y, -y, 1/2)', '(-y+1/2, -y, 0)',
6617 '(y, y+1/2, 0)', '(-y+1/2, y+1/2, 1/2)',
6618 '(1/2, -y, y)', '(0, y+1/2, y)',
6619 '(0, -y, -y+1/2)', '(1/2, y+1/2, -y+1/2)',
6620 '(y, 1/2, -y)', '(y, 0, y+1/2)',
6621 '(-y+1/2, 0, -y)', '(-y+1/2, 1/2, y+1/2)',
6622 '(-y, y, 1/2)', '(y+1/2, y, 0)',
6623 '(-y, -y+1/2, 0)', '(y+1/2, -y+1/2, 1/2)')),
6624 '24k': (5, ('(x, x, z)', '(-x+1/2, -x+1/2, z)',
6625 '(-x+1/2, x, -z+1/2)', '(x, -x+1/2, -z+1/2)',
6626 '(z, x, x)', '(z, -x+1/2, -x+1/2)',
6627 '(-z+1/2, -x+1/2, x)', '(-z+1/2, x, -x+1/2)',
6628 '(x, z, x)', '(-x+1/2, z, -x+1/2)',
6629 '(x, -z+1/2, -x+1/2)', '(-x+1/2, -z+1/2, x)',
6630 '(x+1/2, x+1/2, -z)', '(-x, -x, -z)',
6631 '(x+1/2, -x, z+1/2)', '(-x, x+1/2, z+1/2)',
6632 '(x+1/2, z+1/2, -x)', '(-x, z+1/2, x+1/2)',
6633 '(-x, -z, -x)', '(x+1/2, -z, x+1/2)',
6634 '(z+1/2, x+1/2, -x)', '(z+1/2, -x, x+1/2)',
6635 '(-z, x+1/2, x+1/2)', '(-z, -x, -x)')),
6636 '48l': (7, ('(x, y, z)', '(-x+1/2, -y+1/2, z)',
6637 '(-x+1/2, y, -z+1/2)', '(x, -y+1/2, -z+1/2)',
6638 '(z, x, y)', '(z, -x+1/2, -y+1/2)',
6639 '(-z+1/2, -x+1/2, y)', '(-z+1/2, x, -y+1/2)',
6640 '(y, z, x)', '(-y+1/2, z, -x+1/2)',
6641 '(y, -z+1/2, -x+1/2)', '(-y+1/2, -z+1/2, x)',
6642 '(y+1/2, x+1/2, -z)', '(-y, -x, -z)',
6643 '(y+1/2, -x, z+1/2)', '(-y, x+1/2, z+1/2)',
6644 '(x+1/2, z+1/2, -y)', '(-x, z+1/2, y+1/2)',
6645 '(-x, -z, -y)', '(x+1/2, -z, y+1/2)',
6646 '(z+1/2, y+1/2, -x)', '(z+1/2, -y, x+1/2)',
6647 '(-z, y+1/2, x+1/2)', '(-z, -y, -x)',
6648 '(-x, -y, -z)', '(x+1/2, y+1/2, -z)',
6649 '(x+1/2, -y, z+1/2)', '(-x, y+1/2, z+1/2)',
6650 '(-z, -x, -y)', '(-z, x+1/2, y+1/2)',
6651 '(z+1/2, x+1/2, -y)', '(z+1/2, -x, y+1/2)',
6652 '(-y, -z, -x)', '(y+1/2, -z, x+1/2)',
6653 '(-y, z+1/2, x+1/2)', '(y+1/2, z+1/2, -x)',
6654 '(-y+1/2, -x+1/2, z)', '(y, x, z)',
6655 '(-y+1/2, x, -z+1/2)', '(y, -x+1/2, -z+1/2)',
6656 '(-x+1/2, -z+1/2, y)', '(x, -z+1/2, -y+1/2)',
6657 '(x, z, y)', '(-x+1/2, z, -y+1/2)',
6658 '(-z+1/2, -y+1/2, x)', '(-z+1/2, y, -x+1/2)',
6659 '(z, -y+1/2, -x+1/2)', '(z, y, x)'))},
6660 '225': {'4a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
6661 '(1/2, 1/2, 0)')),
6662 '4b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
6663 '(0, 0, 1/2)')),
6664 '8c': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
6665 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
6666 '(1/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
6667 '(3/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)')),
6668 '24d': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
6669 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
6670 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)',
6671 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
6672 '(3/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
6673 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
6674 '(3/4, 1/2, 3/4)', '(1/4, 1/4, 0)',
6675 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
6676 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
6677 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
6678 '24e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
6679 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
6680 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
6681 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
6682 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
6683 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
6684 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
6685 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
6686 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)')),
6687 '32f': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
6688 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
6689 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
6690 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
6691 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
6692 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
6693 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
6694 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
6695 '(x, x, -x)', '(x, x+1/2, -x+1/2)',
6696 '(x+1/2, x, -x+1/2)', '(x+1/2, x+1/2, -x)',
6697 '(-x, -x, -x)', '(-x, -x+1/2, -x+1/2)',
6698 '(-x+1/2, -x, -x+1/2)', '(-x+1/2, -x+1/2, -x)',
6699 '(x, -x, x)', '(x, -x+1/2, x+1/2)',
6700 '(x+1/2, -x, x+1/2)', '(x+1/2, -x+1/2, x)',
6701 '(-x, x, x)', '(-x, x+1/2, x+1/2)',
6702 '(-x+1/2, x, x+1/2)', '(-x+1/2, x+1/2, x)')),
6703 '48g': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
6704 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
6705 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
6706 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
6707 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
6708 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
6709 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
6710 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
6711 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
6712 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
6713 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
6714 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)',
6715 '(1/4, x, 3/4)', '(1/4, x+1/2, 1/4)',
6716 '(3/4, x, 1/4)', '(3/4, x+1/2, 3/4)',
6717 '(3/4, -x, 3/4)', '(3/4, -x+1/2, 1/4)',
6718 '(1/4, -x, 1/4)', '(1/4, -x+1/2, 3/4)',
6719 '(x, 1/4, 3/4)', '(x, 3/4, 1/4)',
6720 '(x+1/2, 1/4, 1/4)', '(x+1/2, 3/4, 3/4)',
6721 '(-x, 1/4, 1/4)', '(-x, 3/4, 3/4)',
6722 '(-x+1/2, 1/4, 3/4)', '(-x+1/2, 3/4, 1/4)',
6723 '(1/4, 1/4, -x)', '(1/4, 3/4, -x+1/2)',
6724 '(3/4, 1/4, -x+1/2)', '(3/4, 3/4, -x)',
6725 '(1/4, 3/4, x)', '(1/4, 1/4, x+1/2)',
6726 '(3/4, 3/4, x+1/2)', '(3/4, 1/4, x)')),
6727 '48h': (2, ('(0, y, y)', '(0, y+1/2, y+1/2)', '(1/2, y, y+1/2)',
6728 '(1/2, y+1/2, y)', '(0, -y, y)',
6729 '(0, -y+1/2, y+1/2)', '(1/2, -y, y+1/2)',
6730 '(1/2, -y+1/2, y)', '(0, y, -y)',
6731 '(0, y+1/2, -y+1/2)', '(1/2, y, -y+1/2)',
6732 '(1/2, y+1/2, -y)', '(0, -y, -y)',
6733 '(0, -y+1/2, -y+1/2)', '(1/2, -y, -y+1/2)',
6734 '(1/2, -y+1/2, -y)', '(y, 0, y)', '(y, 1/2, y+1/2)',
6735 '(y+1/2, 0, y+1/2)', '(y+1/2, 1/2, y)',
6736 '(y, 0, -y)', '(y, 1/2, -y+1/2)',
6737 '(y+1/2, 0, -y+1/2)', '(y+1/2, 1/2, -y)',
6738 '(-y, 0, y)', '(-y, 1/2, y+1/2)',
6739 '(-y+1/2, 0, y+1/2)', '(-y+1/2, 1/2, y)',
6740 '(-y, 0, -y)', '(-y, 1/2, -y+1/2)',
6741 '(-y+1/2, 0, -y+1/2)', '(-y+1/2, 1/2, -y)',
6742 '(y, y, 0)', '(y, y+1/2, 1/2)', '(y+1/2, y, 1/2)',
6743 '(y+1/2, y+1/2, 0)', '(-y, y, 0)',
6744 '(-y, y+1/2, 1/2)', '(-y+1/2, y, 1/2)',
6745 '(-y+1/2, y+1/2, 0)', '(y, -y, 0)',
6746 '(y, -y+1/2, 1/2)', '(y+1/2, -y, 1/2)',
6747 '(y+1/2, -y+1/2, 0)', '(-y, -y, 0)',
6748 '(-y, -y+1/2, 1/2)', '(-y+1/2, -y, 1/2)',
6749 '(-y+1/2, -y+1/2, 0)')),
6750 '48i': (2, ('(1/2, y, y)', '(1/2, y+1/2, y+1/2)',
6751 '(0, y, y+1/2)', '(0, y+1/2, y)', '(1/2, -y, y)',
6752 '(1/2, -y+1/2, y+1/2)', '(0, -y, y+1/2)',
6753 '(0, -y+1/2, y)', '(1/2, y, -y)',
6754 '(1/2, y+1/2, -y+1/2)', '(0, y, -y+1/2)',
6755 '(0, y+1/2, -y)', '(1/2, -y, -y)',
6756 '(1/2, -y+1/2, -y+1/2)', '(0, -y, -y+1/2)',
6757 '(0, -y+1/2, -y)', '(y, 1/2, y)', '(y, 0, y+1/2)',
6758 '(y+1/2, 1/2, y+1/2)', '(y+1/2, 0, y)',
6759 '(y, 1/2, -y)', '(y, 0, -y+1/2)',
6760 '(y+1/2, 1/2, -y+1/2)', '(y+1/2, 0, -y)',
6761 '(-y, 1/2, y)', '(-y, 0, y+1/2)',
6762 '(-y+1/2, 1/2, y+1/2)', '(-y+1/2, 0, y)',
6763 '(-y, 1/2, -y)', '(-y, 0, -y+1/2)',
6764 '(-y+1/2, 1/2, -y+1/2)', '(-y+1/2, 0, -y)',
6765 '(y, y, 1/2)', '(y, y+1/2, 0)', '(y+1/2, y, 0)',
6766 '(y+1/2, y+1/2, 1/2)', '(-y, y, 1/2)',
6767 '(-y, y+1/2, 0)', '(-y+1/2, y, 0)',
6768 '(-y+1/2, y+1/2, 1/2)', '(y, -y, 1/2)',
6769 '(y, -y+1/2, 0)', '(y+1/2, -y, 0)',
6770 '(y+1/2, -y+1/2, 1/2)', '(-y, -y, 1/2)',
6771 '(-y, -y+1/2, 0)', '(-y+1/2, -y, 0)',
6772 '(-y+1/2, -y+1/2, 1/2)')),
6773 '96j': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(1/2, y, z+1/2)',
6774 '(1/2, y+1/2, z)', '(0, -y, z)',
6775 '(0, -y+1/2, z+1/2)', '(1/2, -y, z+1/2)',
6776 '(1/2, -y+1/2, z)', '(0, y, -z)',
6777 '(0, y+1/2, -z+1/2)', '(1/2, y, -z+1/2)',
6778 '(1/2, y+1/2, -z)', '(0, -y, -z)',
6779 '(0, -y+1/2, -z+1/2)', '(1/2, -y, -z+1/2)',
6780 '(1/2, -y+1/2, -z)', '(z, 0, y)', '(z, 1/2, y+1/2)',
6781 '(z+1/2, 0, y+1/2)', '(z+1/2, 1/2, y)',
6782 '(z, 0, -y)', '(z, 1/2, -y+1/2)',
6783 '(z+1/2, 0, -y+1/2)', '(z+1/2, 1/2, -y)',
6784 '(-z, 0, y)', '(-z, 1/2, y+1/2)',
6785 '(-z+1/2, 0, y+1/2)', '(-z+1/2, 1/2, y)',
6786 '(-z, 0, -y)', '(-z, 1/2, -y+1/2)',
6787 '(-z+1/2, 0, -y+1/2)', '(-z+1/2, 1/2, -y)',
6788 '(y, z, 0)', '(y, z+1/2, 1/2)', '(y+1/2, z, 1/2)',
6789 '(y+1/2, z+1/2, 0)', '(-y, z, 0)',
6790 '(-y, z+1/2, 1/2)', '(-y+1/2, z, 1/2)',
6791 '(-y+1/2, z+1/2, 0)', '(y, -z, 0)',
6792 '(y, -z+1/2, 1/2)', '(y+1/2, -z, 1/2)',
6793 '(y+1/2, -z+1/2, 0)', '(-y, -z, 0)',
6794 '(-y, -z+1/2, 1/2)', '(-y+1/2, -z, 1/2)',
6795 '(-y+1/2, -z+1/2, 0)', '(y, 0, -z)',
6796 '(y, 1/2, -z+1/2)', '(y+1/2, 0, -z+1/2)',
6797 '(y+1/2, 1/2, -z)', '(-y, 0, -z)',
6798 '(-y, 1/2, -z+1/2)', '(-y+1/2, 0, -z+1/2)',
6799 '(-y+1/2, 1/2, -z)', '(y, 0, z)', '(y, 1/2, z+1/2)',
6800 '(y+1/2, 0, z+1/2)', '(y+1/2, 1/2, z)',
6801 '(-y, 0, z)', '(-y, 1/2, z+1/2)',
6802 '(-y+1/2, 0, z+1/2)', '(-y+1/2, 1/2, z)',
6803 '(0, z, -y)', '(0, z+1/2, -y+1/2)',
6804 '(1/2, z, -y+1/2)', '(1/2, z+1/2, -y)', '(0, z, y)',
6805 '(0, z+1/2, y+1/2)', '(1/2, z, y+1/2)',
6806 '(1/2, z+1/2, y)', '(0, -z, -y)',
6807 '(0, -z+1/2, -y+1/2)', '(1/2, -z, -y+1/2)',
6808 '(1/2, -z+1/2, -y)', '(0, -z, y)',
6809 '(0, -z+1/2, y+1/2)', '(1/2, -z, y+1/2)',
6810 '(1/2, -z+1/2, y)', '(z, y, 0)', '(z, y+1/2, 1/2)',
6811 '(z+1/2, y, 1/2)', '(z+1/2, y+1/2, 0)',
6812 '(z, -y, 0)', '(z, -y+1/2, 1/2)',
6813 '(z+1/2, -y, 1/2)', '(z+1/2, -y+1/2, 0)',
6814 '(-z, y, 0)', '(-z, y+1/2, 1/2)',
6815 '(-z+1/2, y, 1/2)', '(-z+1/2, y+1/2, 0)',
6816 '(-z, -y, 0)', '(-z, -y+1/2, 1/2)',
6817 '(-z+1/2, -y, 1/2)', '(-z+1/2, -y+1/2, 0)')),
6818 '96k': (5, ('(x, x, z)', '(x, x+1/2, z+1/2)',
6819 '(x+1/2, x, z+1/2)', '(x+1/2, x+1/2, z)',
6820 '(-x, -x, z)', '(-x, -x+1/2, z+1/2)',
6821 '(-x+1/2, -x, z+1/2)', '(-x+1/2, -x+1/2, z)',
6822 '(-x, x, -z)', '(-x, x+1/2, -z+1/2)',
6823 '(-x+1/2, x, -z+1/2)', '(-x+1/2, x+1/2, -z)',
6824 '(x, -x, -z)', '(x, -x+1/2, -z+1/2)',
6825 '(x+1/2, -x, -z+1/2)', '(x+1/2, -x+1/2, -z)',
6826 '(z, x, x)', '(z, x+1/2, x+1/2)',
6827 '(z+1/2, x, x+1/2)', '(z+1/2, x+1/2, x)',
6828 '(z, -x, -x)', '(z, -x+1/2, -x+1/2)',
6829 '(z+1/2, -x, -x+1/2)', '(z+1/2, -x+1/2, -x)',
6830 '(-z, -x, x)', '(-z, -x+1/2, x+1/2)',
6831 '(-z+1/2, -x, x+1/2)', '(-z+1/2, -x+1/2, x)',
6832 '(-z, x, -x)', '(-z, x+1/2, -x+1/2)',
6833 '(-z+1/2, x, -x+1/2)', '(-z+1/2, x+1/2, -x)',
6834 '(x, z, x)', '(x, z+1/2, x+1/2)',
6835 '(x+1/2, z, x+1/2)', '(x+1/2, z+1/2, x)',
6836 '(-x, z, -x)', '(-x, z+1/2, -x+1/2)',
6837 '(-x+1/2, z, -x+1/2)', '(-x+1/2, z+1/2, -x)',
6838 '(x, -z, -x)', '(x, -z+1/2, -x+1/2)',
6839 '(x+1/2, -z, -x+1/2)', '(x+1/2, -z+1/2, -x)',
6840 '(-x, -z, x)', '(-x, -z+1/2, x+1/2)',
6841 '(-x+1/2, -z, x+1/2)', '(-x+1/2, -z+1/2, x)',
6842 '(x, x, -z)', '(x, x+1/2, -z+1/2)',
6843 '(x+1/2, x, -z+1/2)', '(x+1/2, x+1/2, -z)',
6844 '(-x, -x, -z)', '(-x, -x+1/2, -z+1/2)',
6845 '(-x+1/2, -x, -z+1/2)', '(-x+1/2, -x+1/2, -z)',
6846 '(x, -x, z)', '(x, -x+1/2, z+1/2)',
6847 '(x+1/2, -x, z+1/2)', '(x+1/2, -x+1/2, z)',
6848 '(-x, x, z)', '(-x, x+1/2, z+1/2)',
6849 '(-x+1/2, x, z+1/2)', '(-x+1/2, x+1/2, z)',
6850 '(x, z, -x)', '(x, z+1/2, -x+1/2)',
6851 '(x+1/2, z, -x+1/2)', '(x+1/2, z+1/2, -x)',
6852 '(-x, z, x)', '(-x, z+1/2, x+1/2)',
6853 '(-x+1/2, z, x+1/2)', '(-x+1/2, z+1/2, x)',
6854 '(-x, -z, -x)', '(-x, -z+1/2, -x+1/2)',
6855 '(-x+1/2, -z, -x+1/2)', '(-x+1/2, -z+1/2, -x)',
6856 '(x, -z, x)', '(x, -z+1/2, x+1/2)',
6857 '(x+1/2, -z, x+1/2)', '(x+1/2, -z+1/2, x)',
6858 '(z, x, -x)', '(z, x+1/2, -x+1/2)',
6859 '(z+1/2, x, -x+1/2)', '(z+1/2, x+1/2, -x)',
6860 '(z, -x, x)', '(z, -x+1/2, x+1/2)',
6861 '(z+1/2, -x, x+1/2)', '(z+1/2, -x+1/2, x)',
6862 '(-z, x, x)', '(-z, x+1/2, x+1/2)',
6863 '(-z+1/2, x, x+1/2)', '(-z+1/2, x+1/2, x)',
6864 '(-z, -x, -x)', '(-z, -x+1/2, -x+1/2)',
6865 '(-z+1/2, -x, -x+1/2)', '(-z+1/2, -x+1/2, -x)')),
6866 '192l': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
6867 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
6868 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
6869 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
6870 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
6871 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
6872 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
6873 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
6874 '(z, x, y)', '(z, x+1/2, y+1/2)',
6875 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
6876 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
6877 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
6878 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
6879 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
6880 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
6881 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
6882 '(y, z, x)', '(y, z+1/2, x+1/2)',
6883 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
6884 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
6885 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
6886 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
6887 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
6888 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
6889 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
6890 '(y, x, -z)', '(y, x+1/2, -z+1/2)',
6891 '(y+1/2, x, -z+1/2)', '(y+1/2, x+1/2, -z)',
6892 '(-y, -x, -z)', '(-y, -x+1/2, -z+1/2)',
6893 '(-y+1/2, -x, -z+1/2)', '(-y+1/2, -x+1/2, -z)',
6894 '(y, -x, z)', '(y, -x+1/2, z+1/2)',
6895 '(y+1/2, -x, z+1/2)', '(y+1/2, -x+1/2, z)',
6896 '(-y, x, z)', '(-y, x+1/2, z+1/2)',
6897 '(-y+1/2, x, z+1/2)', '(-y+1/2, x+1/2, z)',
6898 '(x, z, -y)', '(x, z+1/2, -y+1/2)',
6899 '(x+1/2, z, -y+1/2)', '(x+1/2, z+1/2, -y)',
6900 '(-x, z, y)', '(-x, z+1/2, y+1/2)',
6901 '(-x+1/2, z, y+1/2)', '(-x+1/2, z+1/2, y)',
6902 '(-x, -z, -y)', '(-x, -z+1/2, -y+1/2)',
6903 '(-x+1/2, -z, -y+1/2)', '(-x+1/2, -z+1/2, -y)',
6904 '(x, -z, y)', '(x, -z+1/2, y+1/2)',
6905 '(x+1/2, -z, y+1/2)', '(x+1/2, -z+1/2, y)',
6906 '(z, y, -x)', '(z, y+1/2, -x+1/2)',
6907 '(z+1/2, y, -x+1/2)', '(z+1/2, y+1/2, -x)',
6908 '(z, -y, x)', '(z, -y+1/2, x+1/2)',
6909 '(z+1/2, -y, x+1/2)', '(z+1/2, -y+1/2, x)',
6910 '(-z, y, x)', '(-z, y+1/2, x+1/2)',
6911 '(-z+1/2, y, x+1/2)', '(-z+1/2, y+1/2, x)',
6912 '(-z, -y, -x)', '(-z, -y+1/2, -x+1/2)',
6913 '(-z+1/2, -y, -x+1/2)', '(-z+1/2, -y+1/2, -x)',
6914 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
6915 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
6916 '(x, y, -z)', '(x, y+1/2, -z+1/2)',
6917 '(x+1/2, y, -z+1/2)', '(x+1/2, y+1/2, -z)',
6918 '(x, -y, z)', '(x, -y+1/2, z+1/2)',
6919 '(x+1/2, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
6920 '(-x, y, z)', '(-x, y+1/2, z+1/2)',
6921 '(-x+1/2, y, z+1/2)', '(-x+1/2, y+1/2, z)',
6922 '(-z, -x, -y)', '(-z, -x+1/2, -y+1/2)',
6923 '(-z+1/2, -x, -y+1/2)', '(-z+1/2, -x+1/2, -y)',
6924 '(-z, x, y)', '(-z, x+1/2, y+1/2)',
6925 '(-z+1/2, x, y+1/2)', '(-z+1/2, x+1/2, y)',
6926 '(z, x, -y)', '(z, x+1/2, -y+1/2)',
6927 '(z+1/2, x, -y+1/2)', '(z+1/2, x+1/2, -y)',
6928 '(z, -x, y)', '(z, -x+1/2, y+1/2)',
6929 '(z+1/2, -x, y+1/2)', '(z+1/2, -x+1/2, y)',
6930 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
6931 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
6932 '(y, -z, x)', '(y, -z+1/2, x+1/2)',
6933 '(y+1/2, -z, x+1/2)', '(y+1/2, -z+1/2, x)',
6934 '(-y, z, x)', '(-y, z+1/2, x+1/2)',
6935 '(-y+1/2, z, x+1/2)', '(-y+1/2, z+1/2, x)',
6936 '(y, z, -x)', '(y, z+1/2, -x+1/2)',
6937 '(y+1/2, z, -x+1/2)', '(y+1/2, z+1/2, -x)',
6938 '(-y, -x, z)', '(-y, -x+1/2, z+1/2)',
6939 '(-y+1/2, -x, z+1/2)', '(-y+1/2, -x+1/2, z)',
6940 '(y, x, z)', '(y, x+1/2, z+1/2)',
6941 '(y+1/2, x, z+1/2)', '(y+1/2, x+1/2, z)',
6942 '(-y, x, -z)', '(-y, x+1/2, -z+1/2)',
6943 '(-y+1/2, x, -z+1/2)', '(-y+1/2, x+1/2, -z)',
6944 '(y, -x, -z)', '(y, -x+1/2, -z+1/2)',
6945 '(y+1/2, -x, -z+1/2)', '(y+1/2, -x+1/2, -z)',
6946 '(-x, -z, y)', '(-x, -z+1/2, y+1/2)',
6947 '(-x+1/2, -z, y+1/2)', '(-x+1/2, -z+1/2, y)',
6948 '(x, -z, -y)', '(x, -z+1/2, -y+1/2)',
6949 '(x+1/2, -z, -y+1/2)', '(x+1/2, -z+1/2, -y)',
6950 '(x, z, y)', '(x, z+1/2, y+1/2)',
6951 '(x+1/2, z, y+1/2)', '(x+1/2, z+1/2, y)',
6952 '(-x, z, -y)', '(-x, z+1/2, -y+1/2)',
6953 '(-x+1/2, z, -y+1/2)', '(-x+1/2, z+1/2, -y)',
6954 '(-z, -y, x)', '(-z, -y+1/2, x+1/2)',
6955 '(-z+1/2, -y, x+1/2)', '(-z+1/2, -y+1/2, x)',
6956 '(-z, y, -x)', '(-z, y+1/2, -x+1/2)',
6957 '(-z+1/2, y, -x+1/2)', '(-z+1/2, y+1/2, -x)',
6958 '(z, -y, -x)', '(z, -y+1/2, -x+1/2)',
6959 '(z+1/2, -y, -x+1/2)', '(z+1/2, -y+1/2, -x)',
6960 '(z, y, x)', '(z, y+1/2, x+1/2)',
6961 '(z+1/2, y, x+1/2)', '(z+1/2, y+1/2, x)'))},
6962 '226': {'8a': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
6963 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
6964 '(3/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)',
6965 '(1/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)')),
6966 '8b': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
6967 '(1/2, 1/2, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 0)',
6968 '(0, 1/2, 0)', '(0, 0, 1/2)')),
6969 '24c': (0, ('(1/4, 0, 0)', '(1/4, 1/2, 1/2)', '(3/4, 0, 1/2)',
6970 '(3/4, 1/2, 0)', '(3/4, 0, 0)', '(3/4, 1/2, 1/2)',
6971 '(1/4, 0, 1/2)', '(1/4, 1/2, 0)', '(0, 1/4, 0)',
6972 '(0, 3/4, 1/2)', '(1/2, 1/4, 1/2)', '(1/2, 3/4, 0)',
6973 '(0, 3/4, 0)', '(0, 1/4, 1/2)', '(1/2, 3/4, 1/2)',
6974 '(1/2, 1/4, 0)', '(0, 0, 1/4)', '(0, 1/2, 3/4)',
6975 '(1/2, 0, 3/4)', '(1/2, 1/2, 1/4)', '(0, 0, 3/4)',
6976 '(0, 1/2, 1/4)', '(1/2, 0, 1/4)', '(1/2, 1/2, 3/4)'
6977 )),
6978 '24d': (0, ('(0, 1/4, 1/4)', '(0, 3/4, 3/4)', '(1/2, 1/4, 3/4)',
6979 '(1/2, 3/4, 1/4)', '(0, 3/4, 1/4)', '(0, 1/4, 3/4)',
6980 '(1/2, 3/4, 3/4)', '(1/2, 1/4, 1/4)',
6981 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)', '(3/4, 0, 3/4)',
6982 '(3/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
6983 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
6984 '(3/4, 1/2, 3/4)', '(1/4, 1/4, 0)',
6985 '(1/4, 3/4, 1/2)', '(3/4, 1/4, 1/2)',
6986 '(3/4, 3/4, 0)', '(3/4, 1/4, 0)', '(3/4, 3/4, 1/2)',
6987 '(1/4, 1/4, 1/2)', '(1/4, 3/4, 0)')),
6988 '48e': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
6989 '(x+1/2, 1/2, 0)', '(-x, 0, 0)', '(-x, 1/2, 1/2)',
6990 '(-x+1/2, 0, 1/2)', '(-x+1/2, 1/2, 0)', '(0, x, 0)',
6991 '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
6992 '(1/2, x+1/2, 0)', '(0, -x, 0)', '(0, -x+1/2, 1/2)',
6993 '(1/2, -x, 1/2)', '(1/2, -x+1/2, 0)', '(0, 0, x)',
6994 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
6995 '(1/2, 1/2, x)', '(0, 0, -x)', '(0, 1/2, -x+1/2)',
6996 '(1/2, 0, -x+1/2)', '(1/2, 1/2, -x)',
6997 '(1/2, x+1/2, 1/2)', '(1/2, x, 0)', '(0, x+1/2, 0)',
6998 '(0, x, 1/2)', '(1/2, -x+1/2, 1/2)', '(1/2, -x, 0)',
6999 '(0, -x+1/2, 0)', '(0, -x, 1/2)',
7000 '(x+1/2, 1/2, 1/2)', '(x+1/2, 0, 0)', '(x, 1/2, 0)',
7001 '(x, 0, 1/2)', '(-x+1/2, 1/2, 1/2)',
7002 '(-x+1/2, 0, 0)', '(-x, 1/2, 0)', '(-x, 0, 1/2)',
7003 '(1/2, 1/2, -x+1/2)', '(1/2, 0, -x)',
7004 '(0, 1/2, -x)', '(0, 0, -x+1/2)',
7005 '(1/2, 1/2, x+1/2)', '(1/2, 0, x)', '(0, 1/2, x)',
7006 '(0, 0, x+1/2)')),
7007 '48f': (1, ('(x, 1/4, 1/4)', '(x, 3/4, 3/4)',
7008 '(x+1/2, 1/4, 3/4)', '(x+1/2, 3/4, 1/4)',
7009 '(-x, 3/4, 1/4)', '(-x, 1/4, 3/4)',
7010 '(-x+1/2, 3/4, 3/4)', '(-x+1/2, 1/4, 1/4)',
7011 '(1/4, x, 1/4)', '(1/4, x+1/2, 3/4)',
7012 '(3/4, x, 3/4)', '(3/4, x+1/2, 1/4)',
7013 '(1/4, -x, 3/4)', '(1/4, -x+1/2, 1/4)',
7014 '(3/4, -x, 1/4)', '(3/4, -x+1/2, 3/4)',
7015 '(1/4, 1/4, x)', '(1/4, 3/4, x+1/2)',
7016 '(3/4, 1/4, x+1/2)', '(3/4, 3/4, x)',
7017 '(3/4, 1/4, -x)', '(3/4, 3/4, -x+1/2)',
7018 '(1/4, 1/4, -x+1/2)', '(1/4, 3/4, -x)',
7019 '(-x, 3/4, 3/4)', '(-x, 1/4, 1/4)',
7020 '(-x+1/2, 3/4, 1/4)', '(-x+1/2, 1/4, 3/4)',
7021 '(x, 1/4, 3/4)', '(x, 3/4, 1/4)',
7022 '(x+1/2, 1/4, 1/4)', '(x+1/2, 3/4, 3/4)',
7023 '(3/4, -x, 3/4)', '(3/4, -x+1/2, 1/4)',
7024 '(1/4, -x, 1/4)', '(1/4, -x+1/2, 3/4)',
7025 '(3/4, x, 1/4)', '(3/4, x+1/2, 3/4)',
7026 '(1/4, x, 3/4)', '(1/4, x+1/2, 1/4)',
7027 '(3/4, 3/4, -x)', '(3/4, 1/4, -x+1/2)',
7028 '(1/4, 3/4, -x+1/2)', '(1/4, 1/4, -x)',
7029 '(1/4, 3/4, x)', '(1/4, 1/4, x+1/2)',
7030 '(3/4, 3/4, x+1/2)', '(3/4, 1/4, x)')),
7031 '64g': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
7032 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
7033 '(-x, -x, x)', '(-x, -x+1/2, x+1/2)',
7034 '(-x+1/2, -x, x+1/2)', '(-x+1/2, -x+1/2, x)',
7035 '(-x, x, -x)', '(-x, x+1/2, -x+1/2)',
7036 '(-x+1/2, x, -x+1/2)', '(-x+1/2, x+1/2, -x)',
7037 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
7038 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
7039 '(x+1/2, x+1/2, -x+1/2)', '(x+1/2, x, -x)',
7040 '(x, x+1/2, -x)', '(x, x, -x+1/2)',
7041 '(-x+1/2, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x)',
7042 '(-x, -x+1/2, -x)', '(-x, -x, -x+1/2)',
7043 '(x+1/2, -x+1/2, x+1/2)', '(x+1/2, -x, x)',
7044 '(x, -x+1/2, x)', '(x, -x, x+1/2)',
7045 '(-x+1/2, x+1/2, x+1/2)', '(-x+1/2, x, x)',
7046 '(-x, x+1/2, x)', '(-x, x, x+1/2)', '(-x, -x, -x)',
7047 '(-x, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x+1/2)',
7048 '(-x+1/2, -x+1/2, -x)', '(x, x, -x)',
7049 '(x, x+1/2, -x+1/2)', '(x+1/2, x, -x+1/2)',
7050 '(x+1/2, x+1/2, -x)', '(x, -x, x)',
7051 '(x, -x+1/2, x+1/2)', '(x+1/2, -x, x+1/2)',
7052 '(x+1/2, -x+1/2, x)', '(-x, x, x)',
7053 '(-x, x+1/2, x+1/2)', '(-x+1/2, x, x+1/2)',
7054 '(-x+1/2, x+1/2, x)', '(-x+1/2, -x+1/2, x+1/2)',
7055 '(-x+1/2, -x, x)', '(-x, -x+1/2, x)',
7056 '(-x, -x, x+1/2)', '(x+1/2, x+1/2, x+1/2)',
7057 '(x+1/2, x, x)', '(x, x+1/2, x)', '(x, x, x+1/2)',
7058 '(-x+1/2, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
7059 '(-x, x+1/2, -x)', '(-x, x, -x+1/2)',
7060 '(x+1/2, -x+1/2, -x+1/2)', '(x+1/2, -x, -x)',
7061 '(x, -x+1/2, -x)', '(x, -x, -x+1/2)')),
7062 '96h': (2, ('(1/4, y, y)', '(1/4, y+1/2, y+1/2)',
7063 '(3/4, y, y+1/2)', '(3/4, y+1/2, y)',
7064 '(3/4, -y, y)', '(3/4, -y+1/2, y+1/2)',
7065 '(1/4, -y, y+1/2)', '(1/4, -y+1/2, y)',
7066 '(3/4, y, -y)', '(3/4, y+1/2, -y+1/2)',
7067 '(1/4, y, -y+1/2)', '(1/4, y+1/2, -y)',
7068 '(1/4, -y, -y)', '(1/4, -y+1/2, -y+1/2)',
7069 '(3/4, -y, -y+1/2)', '(3/4, -y+1/2, -y)',
7070 '(y, 1/4, y)', '(y, 3/4, y+1/2)',
7071 '(y+1/2, 1/4, y+1/2)', '(y+1/2, 3/4, y)',
7072 '(y, 3/4, -y)', '(y, 1/4, -y+1/2)',
7073 '(y+1/2, 3/4, -y+1/2)', '(y+1/2, 1/4, -y)',
7074 '(-y, 3/4, y)', '(-y, 1/4, y+1/2)',
7075 '(-y+1/2, 3/4, y+1/2)', '(-y+1/2, 1/4, y)',
7076 '(-y, 1/4, -y)', '(-y, 3/4, -y+1/2)',
7077 '(-y+1/2, 1/4, -y+1/2)', '(-y+1/2, 3/4, -y)',
7078 '(y, y, 1/4)', '(y, y+1/2, 3/4)', '(y+1/2, y, 3/4)',
7079 '(y+1/2, y+1/2, 1/4)', '(-y, y, 3/4)',
7080 '(-y, y+1/2, 1/4)', '(-y+1/2, y, 1/4)',
7081 '(-y+1/2, y+1/2, 3/4)', '(y, -y, 3/4)',
7082 '(y, -y+1/2, 1/4)', '(y+1/2, -y, 1/4)',
7083 '(y+1/2, -y+1/2, 3/4)', '(-y, -y, 1/4)',
7084 '(-y, -y+1/2, 3/4)', '(-y+1/2, -y, 3/4)',
7085 '(-y+1/2, -y+1/2, 1/4)', '(3/4, -y, -y)',
7086 '(3/4, -y+1/2, -y+1/2)', '(1/4, -y, -y+1/2)',
7087 '(1/4, -y+1/2, -y)', '(1/4, y, -y)',
7088 '(1/4, y+1/2, -y+1/2)', '(3/4, y, -y+1/2)',
7089 '(3/4, y+1/2, -y)', '(1/4, -y, y)',
7090 '(1/4, -y+1/2, y+1/2)', '(3/4, -y, y+1/2)',
7091 '(3/4, -y+1/2, y)', '(3/4, y, y)',
7092 '(3/4, y+1/2, y+1/2)', '(1/4, y, y+1/2)',
7093 '(1/4, y+1/2, y)', '(-y, 3/4, -y)',
7094 '(-y, 1/4, -y+1/2)', '(-y+1/2, 3/4, -y+1/2)',
7095 '(-y+1/2, 1/4, -y)', '(-y, 1/4, y)',
7096 '(-y, 3/4, y+1/2)', '(-y+1/2, 1/4, y+1/2)',
7097 '(-y+1/2, 3/4, y)', '(y, 1/4, -y)',
7098 '(y, 3/4, -y+1/2)', '(y+1/2, 1/4, -y+1/2)',
7099 '(y+1/2, 3/4, -y)', '(y, 3/4, y)',
7100 '(y, 1/4, y+1/2)', '(y+1/2, 3/4, y+1/2)',
7101 '(y+1/2, 1/4, y)', '(-y, -y, 3/4)',
7102 '(-y, -y+1/2, 1/4)', '(-y+1/2, -y, 1/4)',
7103 '(-y+1/2, -y+1/2, 3/4)', '(y, -y, 1/4)',
7104 '(y, -y+1/2, 3/4)', '(y+1/2, -y, 3/4)',
7105 '(y+1/2, -y+1/2, 1/4)', '(-y, y, 1/4)',
7106 '(-y, y+1/2, 3/4)', '(-y+1/2, y, 3/4)',
7107 '(-y+1/2, y+1/2, 1/4)', '(y, y, 3/4)',
7108 '(y, y+1/2, 1/4)', '(y+1/2, y, 1/4)',
7109 '(y+1/2, y+1/2, 3/4)')),
7110 '96i': (6, ('(0, y, z)', '(0, y+1/2, z+1/2)', '(1/2, y, z+1/2)',
7111 '(1/2, y+1/2, z)', '(0, -y, z)',
7112 '(0, -y+1/2, z+1/2)', '(1/2, -y, z+1/2)',
7113 '(1/2, -y+1/2, z)', '(0, y, -z)',
7114 '(0, y+1/2, -z+1/2)', '(1/2, y, -z+1/2)',
7115 '(1/2, y+1/2, -z)', '(0, -y, -z)',
7116 '(0, -y+1/2, -z+1/2)', '(1/2, -y, -z+1/2)',
7117 '(1/2, -y+1/2, -z)', '(z, 0, y)', '(z, 1/2, y+1/2)',
7118 '(z+1/2, 0, y+1/2)', '(z+1/2, 1/2, y)',
7119 '(z, 0, -y)', '(z, 1/2, -y+1/2)',
7120 '(z+1/2, 0, -y+1/2)', '(z+1/2, 1/2, -y)',
7121 '(-z, 0, y)', '(-z, 1/2, y+1/2)',
7122 '(-z+1/2, 0, y+1/2)', '(-z+1/2, 1/2, y)',
7123 '(-z, 0, -y)', '(-z, 1/2, -y+1/2)',
7124 '(-z+1/2, 0, -y+1/2)', '(-z+1/2, 1/2, -y)',
7125 '(y, z, 0)', '(y, z+1/2, 1/2)', '(y+1/2, z, 1/2)',
7126 '(y+1/2, z+1/2, 0)', '(-y, z, 0)',
7127 '(-y, z+1/2, 1/2)', '(-y+1/2, z, 1/2)',
7128 '(-y+1/2, z+1/2, 0)', '(y, -z, 0)',
7129 '(y, -z+1/2, 1/2)', '(y+1/2, -z, 1/2)',
7130 '(y+1/2, -z+1/2, 0)', '(-y, -z, 0)',
7131 '(-y, -z+1/2, 1/2)', '(-y+1/2, -z, 1/2)',
7132 '(-y+1/2, -z+1/2, 0)', '(y+1/2, 1/2, -z+1/2)',
7133 '(y+1/2, 0, -z)', '(y, 1/2, -z)', '(y, 0, -z+1/2)',
7134 '(-y+1/2, 1/2, -z+1/2)', '(-y+1/2, 0, -z)',
7135 '(-y, 1/2, -z)', '(-y, 0, -z+1/2)',
7136 '(y+1/2, 1/2, z+1/2)', '(y+1/2, 0, z)',
7137 '(y, 1/2, z)', '(y, 0, z+1/2)',
7138 '(-y+1/2, 1/2, z+1/2)', '(-y+1/2, 0, z)',
7139 '(-y, 1/2, z)', '(-y, 0, z+1/2)',
7140 '(1/2, z+1/2, -y+1/2)', '(1/2, z, -y)',
7141 '(0, z+1/2, -y)', '(0, z, -y+1/2)',
7142 '(1/2, z+1/2, y+1/2)', '(1/2, z, y)',
7143 '(0, z+1/2, y)', '(0, z, y+1/2)',
7144 '(1/2, -z+1/2, -y+1/2)', '(1/2, -z, -y)',
7145 '(0, -z+1/2, -y)', '(0, -z, -y+1/2)',
7146 '(1/2, -z+1/2, y+1/2)', '(1/2, -z, y)',
7147 '(0, -z+1/2, y)', '(0, -z, y+1/2)',
7148 '(z+1/2, y+1/2, 1/2)', '(z+1/2, y, 0)',
7149 '(z, y+1/2, 0)', '(z, y, 1/2)',
7150 '(z+1/2, -y+1/2, 1/2)', '(z+1/2, -y, 0)',
7151 '(z, -y+1/2, 0)', '(z, -y, 1/2)',
7152 '(-z+1/2, y+1/2, 1/2)', '(-z+1/2, y, 0)',
7153 '(-z, y+1/2, 0)', '(-z, y, 1/2)',
7154 '(-z+1/2, -y+1/2, 1/2)', '(-z+1/2, -y, 0)',
7155 '(-z, -y+1/2, 0)', '(-z, -y, 1/2)')),
7156 '192j': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
7157 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
7158 '(-x, -y, z)', '(-x, -y+1/2, z+1/2)',
7159 '(-x+1/2, -y, z+1/2)', '(-x+1/2, -y+1/2, z)',
7160 '(-x, y, -z)', '(-x, y+1/2, -z+1/2)',
7161 '(-x+1/2, y, -z+1/2)', '(-x+1/2, y+1/2, -z)',
7162 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
7163 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
7164 '(z, x, y)', '(z, x+1/2, y+1/2)',
7165 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
7166 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
7167 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
7168 '(-z, -x, y)', '(-z, -x+1/2, y+1/2)',
7169 '(-z+1/2, -x, y+1/2)', '(-z+1/2, -x+1/2, y)',
7170 '(-z, x, -y)', '(-z, x+1/2, -y+1/2)',
7171 '(-z+1/2, x, -y+1/2)', '(-z+1/2, x+1/2, -y)',
7172 '(y, z, x)', '(y, z+1/2, x+1/2)',
7173 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
7174 '(-y, z, -x)', '(-y, z+1/2, -x+1/2)',
7175 '(-y+1/2, z, -x+1/2)', '(-y+1/2, z+1/2, -x)',
7176 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
7177 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
7178 '(-y, -z, x)', '(-y, -z+1/2, x+1/2)',
7179 '(-y+1/2, -z, x+1/2)', '(-y+1/2, -z+1/2, x)',
7180 '(y+1/2, x+1/2, -z+1/2)', '(y+1/2, x, -z)',
7181 '(y, x+1/2, -z)', '(y, x, -z+1/2)',
7182 '(-y+1/2, -x+1/2, -z+1/2)', '(-y+1/2, -x, -z)',
7183 '(-y, -x+1/2, -z)', '(-y, -x, -z+1/2)',
7184 '(y+1/2, -x+1/2, z+1/2)', '(y+1/2, -x, z)',
7185 '(y, -x+1/2, z)', '(y, -x, z+1/2)',
7186 '(-y+1/2, x+1/2, z+1/2)', '(-y+1/2, x, z)',
7187 '(-y, x+1/2, z)', '(-y, x, z+1/2)',
7188 '(x+1/2, z+1/2, -y+1/2)', '(x+1/2, z, -y)',
7189 '(x, z+1/2, -y)', '(x, z, -y+1/2)',
7190 '(-x+1/2, z+1/2, y+1/2)', '(-x+1/2, z, y)',
7191 '(-x, z+1/2, y)', '(-x, z, y+1/2)',
7192 '(-x+1/2, -z+1/2, -y+1/2)', '(-x+1/2, -z, -y)',
7193 '(-x, -z+1/2, -y)', '(-x, -z, -y+1/2)',
7194 '(x+1/2, -z+1/2, y+1/2)', '(x+1/2, -z, y)',
7195 '(x, -z+1/2, y)', '(x, -z, y+1/2)',
7196 '(z+1/2, y+1/2, -x+1/2)', '(z+1/2, y, -x)',
7197 '(z, y+1/2, -x)', '(z, y, -x+1/2)',
7198 '(z+1/2, -y+1/2, x+1/2)', '(z+1/2, -y, x)',
7199 '(z, -y+1/2, x)', '(z, -y, x+1/2)',
7200 '(-z+1/2, y+1/2, x+1/2)', '(-z+1/2, y, x)',
7201 '(-z, y+1/2, x)', '(-z, y, x+1/2)',
7202 '(-z+1/2, -y+1/2, -x+1/2)', '(-z+1/2, -y, -x)',
7203 '(-z, -y+1/2, -x)', '(-z, -y, -x+1/2)',
7204 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
7205 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
7206 '(x, y, -z)', '(x, y+1/2, -z+1/2)',
7207 '(x+1/2, y, -z+1/2)', '(x+1/2, y+1/2, -z)',
7208 '(x, -y, z)', '(x, -y+1/2, z+1/2)',
7209 '(x+1/2, -y, z+1/2)', '(x+1/2, -y+1/2, z)',
7210 '(-x, y, z)', '(-x, y+1/2, z+1/2)',
7211 '(-x+1/2, y, z+1/2)', '(-x+1/2, y+1/2, z)',
7212 '(-z, -x, -y)', '(-z, -x+1/2, -y+1/2)',
7213 '(-z+1/2, -x, -y+1/2)', '(-z+1/2, -x+1/2, -y)',
7214 '(-z, x, y)', '(-z, x+1/2, y+1/2)',
7215 '(-z+1/2, x, y+1/2)', '(-z+1/2, x+1/2, y)',
7216 '(z, x, -y)', '(z, x+1/2, -y+1/2)',
7217 '(z+1/2, x, -y+1/2)', '(z+1/2, x+1/2, -y)',
7218 '(z, -x, y)', '(z, -x+1/2, y+1/2)',
7219 '(z+1/2, -x, y+1/2)', '(z+1/2, -x+1/2, y)',
7220 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
7221 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
7222 '(y, -z, x)', '(y, -z+1/2, x+1/2)',
7223 '(y+1/2, -z, x+1/2)', '(y+1/2, -z+1/2, x)',
7224 '(-y, z, x)', '(-y, z+1/2, x+1/2)',
7225 '(-y+1/2, z, x+1/2)', '(-y+1/2, z+1/2, x)',
7226 '(y, z, -x)', '(y, z+1/2, -x+1/2)',
7227 '(y+1/2, z, -x+1/2)', '(y+1/2, z+1/2, -x)',
7228 '(-y+1/2, -x+1/2, z+1/2)', '(-y+1/2, -x, z)',
7229 '(-y, -x+1/2, z)', '(-y, -x, z+1/2)',
7230 '(y+1/2, x+1/2, z+1/2)', '(y+1/2, x, z)',
7231 '(y, x+1/2, z)', '(y, x, z+1/2)',
7232 '(-y+1/2, x+1/2, -z+1/2)', '(-y+1/2, x, -z)',
7233 '(-y, x+1/2, -z)', '(-y, x, -z+1/2)',
7234 '(y+1/2, -x+1/2, -z+1/2)', '(y+1/2, -x, -z)',
7235 '(y, -x+1/2, -z)', '(y, -x, -z+1/2)',
7236 '(-x+1/2, -z+1/2, y+1/2)', '(-x+1/2, -z, y)',
7237 '(-x, -z+1/2, y)', '(-x, -z, y+1/2)',
7238 '(x+1/2, -z+1/2, -y+1/2)', '(x+1/2, -z, -y)',
7239 '(x, -z+1/2, -y)', '(x, -z, -y+1/2)',
7240 '(x+1/2, z+1/2, y+1/2)', '(x+1/2, z, y)',
7241 '(x, z+1/2, y)', '(x, z, y+1/2)',
7242 '(-x+1/2, z+1/2, -y+1/2)', '(-x+1/2, z, -y)',
7243 '(-x, z+1/2, -y)', '(-x, z, -y+1/2)',
7244 '(-z+1/2, -y+1/2, x+1/2)', '(-z+1/2, -y, x)',
7245 '(-z, -y+1/2, x)', '(-z, -y, x+1/2)',
7246 '(-z+1/2, y+1/2, -x+1/2)', '(-z+1/2, y, -x)',
7247 '(-z, y+1/2, -x)', '(-z, y, -x+1/2)',
7248 '(z+1/2, -y+1/2, -x+1/2)', '(z+1/2, -y, -x)',
7249 '(z, -y+1/2, -x)', '(z, -y, -x+1/2)',
7250 '(z+1/2, y+1/2, x+1/2)', '(z+1/2, y, x)',
7251 '(z, y+1/2, x)', '(z, y, x+1/2)'))},
7252 '227:1': {'8a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
7253 '(1/2, 1/2, 0)', '(3/4, 1/4, 3/4)',
7254 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 1/4)',
7255 '(1/4, 3/4, 3/4)')),
7256 '8b': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
7257 '(0, 0, 1/2)', '(1/4, 3/4, 1/4)',
7258 '(1/4, 1/4, 3/4)', '(3/4, 3/4, 3/4)',
7259 '(3/4, 1/4, 1/4)')),
7260 '16c': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
7261 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
7262 '(7/8, 3/8, 5/8)', '(7/8, 7/8, 1/8)',
7263 '(3/8, 3/8, 1/8)', '(3/8, 7/8, 5/8)',
7264 '(3/8, 5/8, 7/8)', '(3/8, 1/8, 3/8)',
7265 '(7/8, 5/8, 3/8)', '(7/8, 1/8, 7/8)',
7266 '(5/8, 7/8, 3/8)', '(5/8, 3/8, 7/8)',
7267 '(1/8, 7/8, 7/8)', '(1/8, 3/8, 3/8)')),
7268 '16d': (0, ('(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
7269 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
7270 '(3/8, 7/8, 1/8)', '(3/8, 3/8, 5/8)',
7271 '(7/8, 7/8, 5/8)', '(7/8, 3/8, 1/8)',
7272 '(7/8, 1/8, 3/8)', '(7/8, 5/8, 7/8)',
7273 '(3/8, 1/8, 7/8)', '(3/8, 5/8, 3/8)',
7274 '(1/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)',
7275 '(5/8, 3/8, 3/8)', '(5/8, 7/8, 7/8)')),
7276 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
7277 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
7278 '(-x, -x+1/2, x+1/2)', '(-x, -x, x)',
7279 '(-x+1/2, -x+1/2, x)', '(-x+1/2, -x, x+1/2)',
7280 '(-x+1/2, x+1/2, -x)', '(-x+1/2, x, -x+1/2)',
7281 '(-x, x+1/2, -x+1/2)', '(-x, x, -x)',
7282 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
7283 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
7284 '(x+3/4, x+1/4, -x+3/4)',
7285 '(x+3/4, x+3/4, -x+1/4)',
7286 '(x+1/4, x+1/4, -x+1/4)',
7287 '(x+1/4, x+3/4, -x+3/4)',
7288 '(-x+1/4, -x+1/4, -x+1/4)',
7289 '(-x+1/4, -x+3/4, -x+3/4)',
7290 '(-x+3/4, -x+1/4, -x+3/4)',
7291 '(-x+3/4, -x+3/4, -x+1/4)',
7292 '(x+1/4, -x+3/4, x+3/4)',
7293 '(x+1/4, -x+1/4, x+1/4)',
7294 '(x+3/4, -x+3/4, x+1/4)',
7295 '(x+3/4, -x+1/4, x+3/4)',
7296 '(-x+3/4, x+3/4, x+1/4)',
7297 '(-x+3/4, x+1/4, x+3/4)',
7298 '(-x+1/4, x+3/4, x+3/4)',
7299 '(-x+1/4, x+1/4, x+1/4)')),
7300 '48f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
7301 '(x+1/2, 1/2, 0)', '(-x, 1/2, 1/2)', '(-x, 0, 0)',
7302 '(-x+1/2, 1/2, 0)', '(-x+1/2, 0, 1/2)',
7303 '(0, x, 0)', '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
7304 '(1/2, x+1/2, 0)', '(1/2, -x, 1/2)',
7305 '(1/2, -x+1/2, 0)', '(0, -x, 0)',
7306 '(0, -x+1/2, 1/2)', '(0, 0, x)',
7307 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
7308 '(1/2, 1/2, x)', '(1/2, 1/2, -x)',
7309 '(1/2, 0, -x+1/2)', '(0, 1/2, -x+1/2)',
7310 '(0, 0, -x)', '(3/4, x+1/4, 3/4)',
7311 '(3/4, x+3/4, 1/4)', '(1/4, x+1/4, 1/4)',
7312 '(1/4, x+3/4, 3/4)', '(1/4, -x+1/4, 1/4)',
7313 '(1/4, -x+3/4, 3/4)', '(3/4, -x+1/4, 3/4)',
7314 '(3/4, -x+3/4, 1/4)', '(x+3/4, 1/4, 3/4)',
7315 '(x+3/4, 3/4, 1/4)', '(x+1/4, 1/4, 1/4)',
7316 '(x+1/4, 3/4, 3/4)', '(-x+3/4, 3/4, 1/4)',
7317 '(-x+3/4, 1/4, 3/4)', '(-x+1/4, 3/4, 3/4)',
7318 '(-x+1/4, 1/4, 1/4)', '(3/4, 1/4, -x+3/4)',
7319 '(3/4, 3/4, -x+1/4)', '(1/4, 1/4, -x+1/4)',
7320 '(1/4, 3/4, -x+3/4)', '(1/4, 3/4, x+3/4)',
7321 '(1/4, 1/4, x+1/4)', '(3/4, 3/4, x+1/4)',
7322 '(3/4, 1/4, x+3/4)')),
7323 '96g': (5, ('(x, x, z)', '(x, x+1/2, z+1/2)',
7324 '(x+1/2, x, z+1/2)', '(x+1/2, x+1/2, z)',
7325 '(-x, -x+1/2, z+1/2)', '(-x, -x, z)',
7326 '(-x+1/2, -x+1/2, z)', '(-x+1/2, -x, z+1/2)',
7327 '(-x+1/2, x+1/2, -z)', '(-x+1/2, x, -z+1/2)',
7328 '(-x, x+1/2, -z+1/2)', '(-x, x, -z)',
7329 '(x+1/2, -x, -z+1/2)', '(x+1/2, -x+1/2, -z)',
7330 '(x, -x, -z)', '(x, -x+1/2, -z+1/2)', '(z, x, x)',
7331 '(z, x+1/2, x+1/2)', '(z+1/2, x, x+1/2)',
7332 '(z+1/2, x+1/2, x)', '(z+1/2, -x, -x+1/2)',
7333 '(z+1/2, -x+1/2, -x)', '(z, -x, -x)',
7334 '(z, -x+1/2, -x+1/2)', '(-z, -x+1/2, x+1/2)',
7335 '(-z, -x, x)', '(-z+1/2, -x+1/2, x)',
7336 '(-z+1/2, -x, x+1/2)', '(-z+1/2, x+1/2, -x)',
7337 '(-z+1/2, x, -x+1/2)', '(-z, x+1/2, -x+1/2)',
7338 '(-z, x, -x)', '(x, z, x)', '(x, z+1/2, x+1/2)',
7339 '(x+1/2, z, x+1/2)', '(x+1/2, z+1/2, x)',
7340 '(-x+1/2, z+1/2, -x)', '(-x+1/2, z, -x+1/2)',
7341 '(-x, z+1/2, -x+1/2)', '(-x, z, -x)',
7342 '(x+1/2, -z, -x+1/2)', '(x+1/2, -z+1/2, -x)',
7343 '(x, -z, -x)', '(x, -z+1/2, -x+1/2)',
7344 '(-x, -z+1/2, x+1/2)', '(-x, -z, x)',
7345 '(-x+1/2, -z+1/2, x)', '(-x+1/2, -z, x+1/2)',
7346 '(x+3/4, x+1/4, -z+3/4)',
7347 '(x+3/4, x+3/4, -z+1/4)',
7348 '(x+1/4, x+1/4, -z+1/4)',
7349 '(x+1/4, x+3/4, -z+3/4)',
7350 '(-x+1/4, -x+1/4, -z+1/4)',
7351 '(-x+1/4, -x+3/4, -z+3/4)',
7352 '(-x+3/4, -x+1/4, -z+3/4)',
7353 '(-x+3/4, -x+3/4, -z+1/4)',
7354 '(x+1/4, -x+3/4, z+3/4)',
7355 '(x+1/4, -x+1/4, z+1/4)',
7356 '(x+3/4, -x+3/4, z+1/4)',
7357 '(x+3/4, -x+1/4, z+3/4)',
7358 '(-x+3/4, x+3/4, z+1/4)',
7359 '(-x+3/4, x+1/4, z+3/4)',
7360 '(-x+1/4, x+3/4, z+3/4)',
7361 '(-x+1/4, x+1/4, z+1/4)',
7362 '(x+3/4, z+1/4, -x+3/4)',
7363 '(x+3/4, z+3/4, -x+1/4)',
7364 '(x+1/4, z+1/4, -x+1/4)',
7365 '(x+1/4, z+3/4, -x+3/4)',
7366 '(-x+3/4, z+3/4, x+1/4)',
7367 '(-x+3/4, z+1/4, x+3/4)',
7368 '(-x+1/4, z+3/4, x+3/4)',
7369 '(-x+1/4, z+1/4, x+1/4)',
7370 '(-x+1/4, -z+1/4, -x+1/4)',
7371 '(-x+1/4, -z+3/4, -x+3/4)',
7372 '(-x+3/4, -z+1/4, -x+3/4)',
7373 '(-x+3/4, -z+3/4, -x+1/4)',
7374 '(x+1/4, -z+3/4, x+3/4)',
7375 '(x+1/4, -z+1/4, x+1/4)',
7376 '(x+3/4, -z+3/4, x+1/4)',
7377 '(x+3/4, -z+1/4, x+3/4)',
7378 '(z+3/4, x+1/4, -x+3/4)',
7379 '(z+3/4, x+3/4, -x+1/4)',
7380 '(z+1/4, x+1/4, -x+1/4)',
7381 '(z+1/4, x+3/4, -x+3/4)',
7382 '(z+1/4, -x+3/4, x+3/4)',
7383 '(z+1/4, -x+1/4, x+1/4)',
7384 '(z+3/4, -x+3/4, x+1/4)',
7385 '(z+3/4, -x+1/4, x+3/4)',
7386 '(-z+3/4, x+3/4, x+1/4)',
7387 '(-z+3/4, x+1/4, x+3/4)',
7388 '(-z+1/4, x+3/4, x+3/4)',
7389 '(-z+1/4, x+1/4, x+1/4)',
7390 '(-z+1/4, -x+1/4, -x+1/4)',
7391 '(-z+1/4, -x+3/4, -x+3/4)',
7392 '(-z+3/4, -x+1/4, -x+3/4)',
7393 '(-z+3/4, -x+3/4, -x+1/4)')),
7394 '96h': (2, ('(1/8, y, -y+1/4)', '(1/8, y+1/2, -y+3/4)',
7395 '(5/8, y, -y+3/4)', '(5/8, y+1/2, -y+1/4)',
7396 '(7/8, -y+1/2, -y+3/4)', '(7/8, -y, -y+1/4)',
7397 '(3/8, -y+1/2, -y+1/4)', '(3/8, -y, -y+3/4)',
7398 '(3/8, y+1/2, y+3/4)', '(3/8, y, y+1/4)',
7399 '(7/8, y+1/2, y+1/4)', '(7/8, y, y+3/4)',
7400 '(5/8, -y, y+1/4)', '(5/8, -y+1/2, y+3/4)',
7401 '(1/8, -y, y+3/4)', '(1/8, -y+1/2, y+1/4)',
7402 '(-y+1/4, 1/8, y)', '(-y+1/4, 5/8, y+1/2)',
7403 '(-y+3/4, 1/8, y+1/2)', '(-y+3/4, 5/8, y)',
7404 '(-y+3/4, 7/8, -y+1/2)', '(-y+3/4, 3/8, -y)',
7405 '(-y+1/4, 7/8, -y)', '(-y+1/4, 3/8, -y+1/2)',
7406 '(y+3/4, 3/8, y+1/2)', '(y+3/4, 7/8, y)',
7407 '(y+1/4, 3/8, y)', '(y+1/4, 7/8, y+1/2)',
7408 '(y+1/4, 5/8, -y)', '(y+1/4, 1/8, -y+1/2)',
7409 '(y+3/4, 5/8, -y+1/2)', '(y+3/4, 1/8, -y)',
7410 '(y, -y+1/4, 1/8)', '(y, -y+3/4, 5/8)',
7411 '(y+1/2, -y+1/4, 5/8)', '(y+1/2, -y+3/4, 1/8)',
7412 '(-y+1/2, -y+3/4, 7/8)', '(-y+1/2, -y+1/4, 3/8)',
7413 '(-y, -y+3/4, 3/8)', '(-y, -y+1/4, 7/8)',
7414 '(y+1/2, y+3/4, 3/8)', '(y+1/2, y+1/4, 7/8)',
7415 '(y, y+3/4, 7/8)', '(y, y+1/4, 3/8)',
7416 '(-y, y+1/4, 5/8)', '(-y, y+3/4, 1/8)',
7417 '(-y+1/2, y+1/4, 1/8)', '(-y+1/2, y+3/4, 5/8)',
7418 '(1/8, -y+1/4, y)', '(1/8, -y+3/4, y+1/2)',
7419 '(5/8, -y+1/4, y+1/2)', '(5/8, -y+3/4, y)',
7420 '(3/8, y+3/4, y+1/2)', '(3/8, y+1/4, y)',
7421 '(7/8, y+3/4, y)', '(7/8, y+1/4, y+1/2)',
7422 '(7/8, -y+3/4, -y+1/2)', '(7/8, -y+1/4, -y)',
7423 '(3/8, -y+3/4, -y)', '(3/8, -y+1/4, -y+1/2)',
7424 '(5/8, y+1/4, -y)', '(5/8, y+3/4, -y+1/2)',
7425 '(1/8, y+1/4, -y+1/2)', '(1/8, y+3/4, -y)',
7426 '(y, 1/8, -y+1/4)', '(y, 5/8, -y+3/4)',
7427 '(y+1/2, 1/8, -y+3/4)', '(y+1/2, 5/8, -y+1/4)',
7428 '(y+1/2, 3/8, y+3/4)', '(y+1/2, 7/8, y+1/4)',
7429 '(y, 3/8, y+1/4)', '(y, 7/8, y+3/4)',
7430 '(-y+1/2, 7/8, -y+3/4)', '(-y+1/2, 3/8, -y+1/4)',
7431 '(-y, 7/8, -y+1/4)', '(-y, 3/8, -y+3/4)',
7432 '(-y, 5/8, y+1/4)', '(-y, 1/8, y+3/4)',
7433 '(-y+1/2, 5/8, y+3/4)', '(-y+1/2, 1/8, y+1/4)',
7434 '(-y+1/4, y, 1/8)', '(-y+1/4, y+1/2, 5/8)',
7435 '(-y+3/4, y, 5/8)', '(-y+3/4, y+1/2, 1/8)',
7436 '(y+3/4, y+1/2, 3/8)', '(y+3/4, y, 7/8)',
7437 '(y+1/4, y+1/2, 7/8)', '(y+1/4, y, 3/8)',
7438 '(-y+3/4, -y+1/2, 7/8)', '(-y+3/4, -y, 3/8)',
7439 '(-y+1/4, -y+1/2, 3/8)', '(-y+1/4, -y, 7/8)',
7440 '(y+1/4, -y, 5/8)', '(y+1/4, -y+1/2, 1/8)',
7441 '(y+3/4, -y, 1/8)', '(y+3/4, -y+1/2, 5/8)')),
7442 '192i': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
7443 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
7444 '(-x, -y+1/2, z+1/2)', '(-x, -y, z)',
7445 '(-x+1/2, -y+1/2, z)', '(-x+1/2, -y, z+1/2)',
7446 '(-x+1/2, y+1/2, -z)', '(-x+1/2, y, -z+1/2)',
7447 '(-x, y+1/2, -z+1/2)', '(-x, y, -z)',
7448 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
7449 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
7450 '(z, x, y)', '(z, x+1/2, y+1/2)',
7451 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
7452 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
7453 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
7454 '(-z, -x+1/2, y+1/2)', '(-z, -x, y)',
7455 '(-z+1/2, -x+1/2, y)', '(-z+1/2, -x, y+1/2)',
7456 '(-z+1/2, x+1/2, -y)', '(-z+1/2, x, -y+1/2)',
7457 '(-z, x+1/2, -y+1/2)', '(-z, x, -y)',
7458 '(y, z, x)', '(y, z+1/2, x+1/2)',
7459 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
7460 '(-y+1/2, z+1/2, -x)', '(-y+1/2, z, -x+1/2)',
7461 '(-y, z+1/2, -x+1/2)', '(-y, z, -x)',
7462 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
7463 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
7464 '(-y, -z+1/2, x+1/2)', '(-y, -z, x)',
7465 '(-y+1/2, -z+1/2, x)', '(-y+1/2, -z, x+1/2)',
7466 '(y+3/4, x+1/4, -z+3/4)',
7467 '(y+3/4, x+3/4, -z+1/4)',
7468 '(y+1/4, x+1/4, -z+1/4)',
7469 '(y+1/4, x+3/4, -z+3/4)',
7470 '(-y+1/4, -x+1/4, -z+1/4)',
7471 '(-y+1/4, -x+3/4, -z+3/4)',
7472 '(-y+3/4, -x+1/4, -z+3/4)',
7473 '(-y+3/4, -x+3/4, -z+1/4)',
7474 '(y+1/4, -x+3/4, z+3/4)',
7475 '(y+1/4, -x+1/4, z+1/4)',
7476 '(y+3/4, -x+3/4, z+1/4)',
7477 '(y+3/4, -x+1/4, z+3/4)',
7478 '(-y+3/4, x+3/4, z+1/4)',
7479 '(-y+3/4, x+1/4, z+3/4)',
7480 '(-y+1/4, x+3/4, z+3/4)',
7481 '(-y+1/4, x+1/4, z+1/4)',
7482 '(x+3/4, z+1/4, -y+3/4)',
7483 '(x+3/4, z+3/4, -y+1/4)',
7484 '(x+1/4, z+1/4, -y+1/4)',
7485 '(x+1/4, z+3/4, -y+3/4)',
7486 '(-x+3/4, z+3/4, y+1/4)',
7487 '(-x+3/4, z+1/4, y+3/4)',
7488 '(-x+1/4, z+3/4, y+3/4)',
7489 '(-x+1/4, z+1/4, y+1/4)',
7490 '(-x+1/4, -z+1/4, -y+1/4)',
7491 '(-x+1/4, -z+3/4, -y+3/4)',
7492 '(-x+3/4, -z+1/4, -y+3/4)',
7493 '(-x+3/4, -z+3/4, -y+1/4)',
7494 '(x+1/4, -z+3/4, y+3/4)',
7495 '(x+1/4, -z+1/4, y+1/4)',
7496 '(x+3/4, -z+3/4, y+1/4)',
7497 '(x+3/4, -z+1/4, y+3/4)',
7498 '(z+3/4, y+1/4, -x+3/4)',
7499 '(z+3/4, y+3/4, -x+1/4)',
7500 '(z+1/4, y+1/4, -x+1/4)',
7501 '(z+1/4, y+3/4, -x+3/4)',
7502 '(z+1/4, -y+3/4, x+3/4)',
7503 '(z+1/4, -y+1/4, x+1/4)',
7504 '(z+3/4, -y+3/4, x+1/4)',
7505 '(z+3/4, -y+1/4, x+3/4)',
7506 '(-z+3/4, y+3/4, x+1/4)',
7507 '(-z+3/4, y+1/4, x+3/4)',
7508 '(-z+1/4, y+3/4, x+3/4)',
7509 '(-z+1/4, y+1/4, x+1/4)',
7510 '(-z+1/4, -y+1/4, -x+1/4)',
7511 '(-z+1/4, -y+3/4, -x+3/4)',
7512 '(-z+3/4, -y+1/4, -x+3/4)',
7513 '(-z+3/4, -y+3/4, -x+1/4)',
7514 '(-x+1/4, -y+1/4, -z+1/4)',
7515 '(-x+1/4, -y+3/4, -z+3/4)',
7516 '(-x+3/4, -y+1/4, -z+3/4)',
7517 '(-x+3/4, -y+3/4, -z+1/4)',
7518 '(x+1/4, y+3/4, -z+3/4)',
7519 '(x+1/4, y+1/4, -z+1/4)',
7520 '(x+3/4, y+3/4, -z+1/4)',
7521 '(x+3/4, y+1/4, -z+3/4)',
7522 '(x+3/4, -y+3/4, z+1/4)',
7523 '(x+3/4, -y+1/4, z+3/4)',
7524 '(x+1/4, -y+3/4, z+3/4)',
7525 '(x+1/4, -y+1/4, z+1/4)',
7526 '(-x+3/4, y+1/4, z+3/4)',
7527 '(-x+3/4, y+3/4, z+1/4)',
7528 '(-x+1/4, y+1/4, z+1/4)',
7529 '(-x+1/4, y+3/4, z+3/4)',
7530 '(-z+1/4, -x+1/4, -y+1/4)',
7531 '(-z+1/4, -x+3/4, -y+3/4)',
7532 '(-z+3/4, -x+1/4, -y+3/4)',
7533 '(-z+3/4, -x+3/4, -y+1/4)',
7534 '(-z+3/4, x+1/4, y+3/4)',
7535 '(-z+3/4, x+3/4, y+1/4)',
7536 '(-z+1/4, x+1/4, y+1/4)',
7537 '(-z+1/4, x+3/4, y+3/4)',
7538 '(z+1/4, x+3/4, -y+3/4)',
7539 '(z+1/4, x+1/4, -y+1/4)',
7540 '(z+3/4, x+3/4, -y+1/4)',
7541 '(z+3/4, x+1/4, -y+3/4)',
7542 '(z+3/4, -x+3/4, y+1/4)',
7543 '(z+3/4, -x+1/4, y+3/4)',
7544 '(z+1/4, -x+3/4, y+3/4)',
7545 '(z+1/4, -x+1/4, y+1/4)',
7546 '(-y+1/4, -z+1/4, -x+1/4)',
7547 '(-y+1/4, -z+3/4, -x+3/4)',
7548 '(-y+3/4, -z+1/4, -x+3/4)',
7549 '(-y+3/4, -z+3/4, -x+1/4)',
7550 '(y+3/4, -z+3/4, x+1/4)',
7551 '(y+3/4, -z+1/4, x+3/4)',
7552 '(y+1/4, -z+3/4, x+3/4)',
7553 '(y+1/4, -z+1/4, x+1/4)',
7554 '(-y+3/4, z+1/4, x+3/4)',
7555 '(-y+3/4, z+3/4, x+1/4)',
7556 '(-y+1/4, z+1/4, x+1/4)',
7557 '(-y+1/4, z+3/4, x+3/4)',
7558 '(y+1/4, z+3/4, -x+3/4)',
7559 '(y+1/4, z+1/4, -x+1/4)',
7560 '(y+3/4, z+3/4, -x+1/4)',
7561 '(y+3/4, z+1/4, -x+3/4)', '(-y+1/2, -x, z+1/2)',
7562 '(-y+1/2, -x+1/2, z)', '(-y, -x, z)',
7563 '(-y, -x+1/2, z+1/2)', '(y, x, z)',
7564 '(y, x+1/2, z+1/2)', '(y+1/2, x, z+1/2)',
7565 '(y+1/2, x+1/2, z)', '(-y, x+1/2, -z+1/2)',
7566 '(-y, x, -z)', '(-y+1/2, x+1/2, -z)',
7567 '(-y+1/2, x, -z+1/2)', '(y+1/2, -x+1/2, -z)',
7568 '(y+1/2, -x, -z+1/2)', '(y, -x+1/2, -z+1/2)',
7569 '(y, -x, -z)', '(-x+1/2, -z, y+1/2)',
7570 '(-x+1/2, -z+1/2, y)', '(-x, -z, y)',
7571 '(-x, -z+1/2, y+1/2)', '(x+1/2, -z+1/2, -y)',
7572 '(x+1/2, -z, -y+1/2)', '(x, -z+1/2, -y+1/2)',
7573 '(x, -z, -y)', '(x, z, y)', '(x, z+1/2, y+1/2)',
7574 '(x+1/2, z, y+1/2)', '(x+1/2, z+1/2, y)',
7575 '(-x, z+1/2, -y+1/2)', '(-x, z, -y)',
7576 '(-x+1/2, z+1/2, -y)', '(-x+1/2, z, -y+1/2)',
7577 '(-z+1/2, -y, x+1/2)', '(-z+1/2, -y+1/2, x)',
7578 '(-z, -y, x)', '(-z, -y+1/2, x+1/2)',
7579 '(-z, y+1/2, -x+1/2)', '(-z, y, -x)',
7580 '(-z+1/2, y+1/2, -x)', '(-z+1/2, y, -x+1/2)',
7581 '(z+1/2, -y+1/2, -x)', '(z+1/2, -y, -x+1/2)',
7582 '(z, -y+1/2, -x+1/2)', '(z, -y, -x)',
7583 '(z, y, x)', '(z, y+1/2, x+1/2)',
7584 '(z+1/2, y, x+1/2)', '(z+1/2, y+1/2, x)'))},
7585 '227:2': {'8a': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
7586 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
7587 '(7/8, 3/8, 3/8)', '(7/8, 7/8, 7/8)',
7588 '(3/8, 3/8, 7/8)', '(3/8, 7/8, 3/8)')),
7589 '8b': (0, ('(3/8, 3/8, 3/8)', '(3/8, 7/8, 7/8)',
7590 '(7/8, 3/8, 7/8)', '(7/8, 7/8, 3/8)',
7591 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
7592 '(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)')),
7593 '16c': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
7594 '(1/2, 1/2, 0)', '(3/4, 1/4, 1/2)',
7595 '(3/4, 3/4, 0)', '(1/4, 1/4, 0)',
7596 '(1/4, 3/4, 1/2)', '(1/4, 1/2, 3/4)',
7597 '(1/4, 0, 1/4)', '(3/4, 1/2, 1/4)',
7598 '(3/4, 0, 3/4)', '(1/2, 3/4, 1/4)',
7599 '(1/2, 1/4, 3/4)', '(0, 3/4, 3/4)',
7600 '(0, 1/4, 1/4)')),
7601 '16d': (0, ('(1/2, 1/2, 1/2)', '(1/2, 0, 0)', '(0, 1/2, 0)',
7602 '(0, 0, 1/2)', '(1/4, 3/4, 0)', '(1/4, 1/4, 1/2)',
7603 '(3/4, 3/4, 1/2)', '(3/4, 1/4, 0)',
7604 '(3/4, 0, 1/4)', '(3/4, 1/2, 3/4)',
7605 '(1/4, 0, 3/4)', '(1/4, 1/2, 1/4)',
7606 '(0, 1/4, 3/4)', '(0, 3/4, 1/4)',
7607 '(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)')),
7608 '32e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
7609 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
7610 '(-x+3/4, -x+1/4, x+1/2)', '(-x+3/4, -x+3/4, x)',
7611 '(-x+1/4, -x+1/4, x)', '(-x+1/4, -x+3/4, x+1/2)',
7612 '(-x+1/4, x+1/2, -x+3/4)', '(-x+1/4, x, -x+1/4)',
7613 '(-x+3/4, x+1/2, -x+1/4)', '(-x+3/4, x, -x+3/4)',
7614 '(x+1/2, -x+3/4, -x+1/4)',
7615 '(x+1/2, -x+1/4, -x+3/4)', '(x, -x+3/4, -x+3/4)',
7616 '(x, -x+1/4, -x+1/4)', '(x+3/4, x+1/4, -x+1/2)',
7617 '(x+3/4, x+3/4, -x)', '(x+1/4, x+1/4, -x)',
7618 '(x+1/4, x+3/4, -x+1/2)', '(-x, -x, -x)',
7619 '(-x, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x+1/2)',
7620 '(-x+1/2, -x+1/2, -x)', '(x+1/4, -x+1/2, x+3/4)',
7621 '(x+1/4, -x, x+1/4)', '(x+3/4, -x+1/2, x+1/4)',
7622 '(x+3/4, -x, x+3/4)', '(-x+1/2, x+3/4, x+1/4)',
7623 '(-x+1/2, x+1/4, x+3/4)', '(-x, x+3/4, x+3/4)',
7624 '(-x, x+1/4, x+1/4)')),
7625 '48f': (1, ('(x, 1/8, 1/8)', '(x, 5/8, 5/8)',
7626 '(x+1/2, 1/8, 5/8)', '(x+1/2, 5/8, 1/8)',
7627 '(-x+3/4, 1/8, 5/8)', '(-x+3/4, 5/8, 1/8)',
7628 '(-x+1/4, 1/8, 1/8)', '(-x+1/4, 5/8, 5/8)',
7629 '(1/8, x, 1/8)', '(1/8, x+1/2, 5/8)',
7630 '(5/8, x, 5/8)', '(5/8, x+1/2, 1/8)',
7631 '(5/8, -x+3/4, 1/8)', '(5/8, -x+1/4, 5/8)',
7632 '(1/8, -x+3/4, 5/8)', '(1/8, -x+1/4, 1/8)',
7633 '(1/8, 1/8, x)', '(1/8, 5/8, x+1/2)',
7634 '(5/8, 1/8, x+1/2)', '(5/8, 5/8, x)',
7635 '(1/8, 5/8, -x+3/4)', '(1/8, 1/8, -x+1/4)',
7636 '(5/8, 5/8, -x+1/4)', '(5/8, 1/8, -x+3/4)',
7637 '(7/8, x+1/4, 3/8)', '(7/8, x+3/4, 7/8)',
7638 '(3/8, x+1/4, 7/8)', '(3/8, x+3/4, 3/8)',
7639 '(7/8, -x, 7/8)', '(7/8, -x+1/2, 3/8)',
7640 '(3/8, -x, 3/8)', '(3/8, -x+1/2, 7/8)',
7641 '(x+3/4, 3/8, 3/8)', '(x+3/4, 7/8, 7/8)',
7642 '(x+1/4, 3/8, 7/8)', '(x+1/4, 7/8, 3/8)',
7643 '(-x+1/2, 7/8, 3/8)', '(-x+1/2, 3/8, 7/8)',
7644 '(-x, 7/8, 7/8)', '(-x, 3/8, 3/8)',
7645 '(7/8, 3/8, -x+1/2)', '(7/8, 7/8, -x)',
7646 '(3/8, 3/8, -x)', '(3/8, 7/8, -x+1/2)',
7647 '(3/8, 3/8, x+3/4)', '(3/8, 7/8, x+1/4)',
7648 '(7/8, 3/8, x+1/4)', '(7/8, 7/8, x+3/4)')),
7649 '96g': (5, ('(x, x, z)', '(x, x+1/2, z+1/2)',
7650 '(x+1/2, x, z+1/2)', '(x+1/2, x+1/2, z)',
7651 '(-x+3/4, -x+1/4, z+1/2)', '(-x+3/4, -x+3/4, z)',
7652 '(-x+1/4, -x+1/4, z)', '(-x+1/4, -x+3/4, z+1/2)',
7653 '(-x+1/4, x+1/2, -z+3/4)', '(-x+1/4, x, -z+1/4)',
7654 '(-x+3/4, x+1/2, -z+1/4)', '(-x+3/4, x, -z+3/4)',
7655 '(x+1/2, -x+3/4, -z+1/4)',
7656 '(x+1/2, -x+1/4, -z+3/4)', '(x, -x+3/4, -z+3/4)',
7657 '(x, -x+1/4, -z+1/4)', '(z, x, x)',
7658 '(z, x+1/2, x+1/2)', '(z+1/2, x, x+1/2)',
7659 '(z+1/2, x+1/2, x)', '(z+1/2, -x+3/4, -x+1/4)',
7660 '(z+1/2, -x+1/4, -x+3/4)', '(z, -x+3/4, -x+3/4)',
7661 '(z, -x+1/4, -x+1/4)', '(-z+3/4, -x+1/4, x+1/2)',
7662 '(-z+3/4, -x+3/4, x)', '(-z+1/4, -x+1/4, x)',
7663 '(-z+1/4, -x+3/4, x+1/2)',
7664 '(-z+1/4, x+1/2, -x+3/4)', '(-z+1/4, x, -x+1/4)',
7665 '(-z+3/4, x+1/2, -x+1/4)', '(-z+3/4, x, -x+3/4)',
7666 '(x, z, x)', '(x, z+1/2, x+1/2)',
7667 '(x+1/2, z, x+1/2)', '(x+1/2, z+1/2, x)',
7668 '(-x+1/4, z+1/2, -x+3/4)', '(-x+1/4, z, -x+1/4)',
7669 '(-x+3/4, z+1/2, -x+1/4)', '(-x+3/4, z, -x+3/4)',
7670 '(x+1/2, -z+3/4, -x+1/4)',
7671 '(x+1/2, -z+1/4, -x+3/4)', '(x, -z+3/4, -x+3/4)',
7672 '(x, -z+1/4, -x+1/4)', '(-x+3/4, -z+1/4, x+1/2)',
7673 '(-x+3/4, -z+3/4, x)', '(-x+1/4, -z+1/4, x)',
7674 '(-x+1/4, -z+3/4, x+1/2)',
7675 '(x+3/4, x+1/4, -z+1/2)', '(x+3/4, x+3/4, -z)',
7676 '(x+1/4, x+1/4, -z)', '(x+1/4, x+3/4, -z+1/2)',
7677 '(-x, -x, -z)', '(-x, -x+1/2, -z+1/2)',
7678 '(-x+1/2, -x, -z+1/2)', '(-x+1/2, -x+1/2, -z)',
7679 '(x+1/4, -x+1/2, z+3/4)', '(x+1/4, -x, z+1/4)',
7680 '(x+3/4, -x+1/2, z+1/4)', '(x+3/4, -x, z+3/4)',
7681 '(-x+1/2, x+3/4, z+1/4)',
7682 '(-x+1/2, x+1/4, z+3/4)', '(-x, x+3/4, z+3/4)',
7683 '(-x, x+1/4, z+1/4)', '(x+3/4, z+1/4, -x+1/2)',
7684 '(x+3/4, z+3/4, -x)', '(x+1/4, z+1/4, -x)',
7685 '(x+1/4, z+3/4, -x+1/2)',
7686 '(-x+1/2, z+3/4, x+1/4)',
7687 '(-x+1/2, z+1/4, x+3/4)', '(-x, z+3/4, x+3/4)',
7688 '(-x, z+1/4, x+1/4)', '(-x, -z, -x)',
7689 '(-x, -z+1/2, -x+1/2)', '(-x+1/2, -z, -x+1/2)',
7690 '(-x+1/2, -z+1/2, -x)', '(x+1/4, -z+1/2, x+3/4)',
7691 '(x+1/4, -z, x+1/4)', '(x+3/4, -z+1/2, x+1/4)',
7692 '(x+3/4, -z, x+3/4)', '(z+3/4, x+1/4, -x+1/2)',
7693 '(z+3/4, x+3/4, -x)', '(z+1/4, x+1/4, -x)',
7694 '(z+1/4, x+3/4, -x+1/2)',
7695 '(z+1/4, -x+1/2, x+3/4)', '(z+1/4, -x, x+1/4)',
7696 '(z+3/4, -x+1/2, x+1/4)', '(z+3/4, -x, x+3/4)',
7697 '(-z+1/2, x+3/4, x+1/4)',
7698 '(-z+1/2, x+1/4, x+3/4)', '(-z, x+3/4, x+3/4)',
7699 '(-z, x+1/4, x+1/4)', '(-z, -x, -x)',
7700 '(-z, -x+1/2, -x+1/2)', '(-z+1/2, -x, -x+1/2)',
7701 '(-z+1/2, -x+1/2, -x)')),
7702 '96h': (2, ('(0, y, -y)', '(0, y+1/2, -y+1/2)',
7703 '(1/2, y, -y+1/2)', '(1/2, y+1/2, -y)',
7704 '(3/4, -y+1/4, -y+1/2)', '(3/4, -y+3/4, -y)',
7705 '(1/4, -y+1/4, -y)', '(1/4, -y+3/4, -y+1/2)',
7706 '(1/4, y+1/2, y+3/4)', '(1/4, y, y+1/4)',
7707 '(3/4, y+1/2, y+1/4)', '(3/4, y, y+3/4)',
7708 '(1/2, -y+3/4, y+1/4)', '(1/2, -y+1/4, y+3/4)',
7709 '(0, -y+3/4, y+3/4)', '(0, -y+1/4, y+1/4)',
7710 '(-y, 0, y)', '(-y, 1/2, y+1/2)',
7711 '(-y+1/2, 0, y+1/2)', '(-y+1/2, 1/2, y)',
7712 '(-y+1/2, 3/4, -y+1/4)', '(-y+1/2, 1/4, -y+3/4)',
7713 '(-y, 3/4, -y+3/4)', '(-y, 1/4, -y+1/4)',
7714 '(y+3/4, 1/4, y+1/2)', '(y+3/4, 3/4, y)',
7715 '(y+1/4, 1/4, y)', '(y+1/4, 3/4, y+1/2)',
7716 '(y+1/4, 1/2, -y+3/4)', '(y+1/4, 0, -y+1/4)',
7717 '(y+3/4, 1/2, -y+1/4)', '(y+3/4, 0, -y+3/4)',
7718 '(y, -y, 0)', '(y, -y+1/2, 1/2)',
7719 '(y+1/2, -y, 1/2)', '(y+1/2, -y+1/2, 0)',
7720 '(-y+1/4, -y+1/2, 3/4)', '(-y+1/4, -y, 1/4)',
7721 '(-y+3/4, -y+1/2, 1/4)', '(-y+3/4, -y, 3/4)',
7722 '(y+1/2, y+3/4, 1/4)', '(y+1/2, y+1/4, 3/4)',
7723 '(y, y+3/4, 3/4)', '(y, y+1/4, 1/4)',
7724 '(-y+3/4, y+1/4, 1/2)', '(-y+3/4, y+3/4, 0)',
7725 '(-y+1/4, y+1/4, 0)', '(-y+1/4, y+3/4, 1/2)',
7726 '(0, -y, y)', '(0, -y+1/2, y+1/2)',
7727 '(1/2, -y, y+1/2)', '(1/2, -y+1/2, y)',
7728 '(1/4, y+3/4, y+1/2)', '(1/4, y+1/4, y)',
7729 '(3/4, y+3/4, y)', '(3/4, y+1/4, y+1/2)',
7730 '(3/4, -y+1/2, -y+1/4)', '(3/4, -y, -y+3/4)',
7731 '(1/4, -y+1/2, -y+3/4)', '(1/4, -y, -y+1/4)',
7732 '(1/2, y+1/4, -y+3/4)', '(1/2, y+3/4, -y+1/4)',
7733 '(0, y+1/4, -y+1/4)', '(0, y+3/4, -y+3/4)',
7734 '(y, 0, -y)', '(y, 1/2, -y+1/2)',
7735 '(y+1/2, 0, -y+1/2)', '(y+1/2, 1/2, -y)',
7736 '(y+1/2, 1/4, y+3/4)', '(y+1/2, 3/4, y+1/4)',
7737 '(y, 1/4, y+1/4)', '(y, 3/4, y+3/4)',
7738 '(-y+1/4, 3/4, -y+1/2)', '(-y+1/4, 1/4, -y)',
7739 '(-y+3/4, 3/4, -y)', '(-y+3/4, 1/4, -y+1/2)',
7740 '(-y+3/4, 1/2, y+1/4)', '(-y+3/4, 0, y+3/4)',
7741 '(-y+1/4, 1/2, y+3/4)', '(-y+1/4, 0, y+1/4)',
7742 '(-y, y, 0)', '(-y, y+1/2, 1/2)',
7743 '(-y+1/2, y, 1/2)', '(-y+1/2, y+1/2, 0)',
7744 '(y+3/4, y+1/2, 1/4)', '(y+3/4, y, 3/4)',
7745 '(y+1/4, y+1/2, 3/4)', '(y+1/4, y, 1/4)',
7746 '(-y+1/2, -y+1/4, 3/4)', '(-y+1/2, -y+3/4, 1/4)',
7747 '(-y, -y+1/4, 1/4)', '(-y, -y+3/4, 3/4)',
7748 '(y+1/4, -y+3/4, 1/2)', '(y+1/4, -y+1/4, 0)',
7749 '(y+3/4, -y+3/4, 0)', '(y+3/4, -y+1/4, 1/2)')),
7750 '192i': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
7751 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
7752 '(-x+3/4, -y+1/4, z+1/2)', '(-x+3/4, -y+3/4, z)',
7753 '(-x+1/4, -y+1/4, z)', '(-x+1/4, -y+3/4, z+1/2)',
7754 '(-x+1/4, y+1/2, -z+3/4)', '(-x+1/4, y, -z+1/4)',
7755 '(-x+3/4, y+1/2, -z+1/4)', '(-x+3/4, y, -z+3/4)',
7756 '(x+1/2, -y+3/4, -z+1/4)',
7757 '(x+1/2, -y+1/4, -z+3/4)', '(x, -y+3/4, -z+3/4)',
7758 '(x, -y+1/4, -z+1/4)', '(z, x, y)',
7759 '(z, x+1/2, y+1/2)', '(z+1/2, x, y+1/2)',
7760 '(z+1/2, x+1/2, y)', '(z+1/2, -x+3/4, -y+1/4)',
7761 '(z+1/2, -x+1/4, -y+3/4)', '(z, -x+3/4, -y+3/4)',
7762 '(z, -x+1/4, -y+1/4)', '(-z+3/4, -x+1/4, y+1/2)',
7763 '(-z+3/4, -x+3/4, y)', '(-z+1/4, -x+1/4, y)',
7764 '(-z+1/4, -x+3/4, y+1/2)',
7765 '(-z+1/4, x+1/2, -y+3/4)', '(-z+1/4, x, -y+1/4)',
7766 '(-z+3/4, x+1/2, -y+1/4)', '(-z+3/4, x, -y+3/4)',
7767 '(y, z, x)', '(y, z+1/2, x+1/2)',
7768 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
7769 '(-y+1/4, z+1/2, -x+3/4)', '(-y+1/4, z, -x+1/4)',
7770 '(-y+3/4, z+1/2, -x+1/4)', '(-y+3/4, z, -x+3/4)',
7771 '(y+1/2, -z+3/4, -x+1/4)',
7772 '(y+1/2, -z+1/4, -x+3/4)', '(y, -z+3/4, -x+3/4)',
7773 '(y, -z+1/4, -x+1/4)', '(-y+3/4, -z+1/4, x+1/2)',
7774 '(-y+3/4, -z+3/4, x)', '(-y+1/4, -z+1/4, x)',
7775 '(-y+1/4, -z+3/4, x+1/2)',
7776 '(y+3/4, x+1/4, -z+1/2)', '(y+3/4, x+3/4, -z)',
7777 '(y+1/4, x+1/4, -z)', '(y+1/4, x+3/4, -z+1/2)',
7778 '(-y, -x, -z)', '(-y, -x+1/2, -z+1/2)',
7779 '(-y+1/2, -x, -z+1/2)', '(-y+1/2, -x+1/2, -z)',
7780 '(y+1/4, -x+1/2, z+3/4)', '(y+1/4, -x, z+1/4)',
7781 '(y+3/4, -x+1/2, z+1/4)', '(y+3/4, -x, z+3/4)',
7782 '(-y+1/2, x+3/4, z+1/4)',
7783 '(-y+1/2, x+1/4, z+3/4)', '(-y, x+3/4, z+3/4)',
7784 '(-y, x+1/4, z+1/4)', '(x+3/4, z+1/4, -y+1/2)',
7785 '(x+3/4, z+3/4, -y)', '(x+1/4, z+1/4, -y)',
7786 '(x+1/4, z+3/4, -y+1/2)',
7787 '(-x+1/2, z+3/4, y+1/4)',
7788 '(-x+1/2, z+1/4, y+3/4)', '(-x, z+3/4, y+3/4)',
7789 '(-x, z+1/4, y+1/4)', '(-x, -z, -y)',
7790 '(-x, -z+1/2, -y+1/2)', '(-x+1/2, -z, -y+1/2)',
7791 '(-x+1/2, -z+1/2, -y)', '(x+1/4, -z+1/2, y+3/4)',
7792 '(x+1/4, -z, y+1/4)', '(x+3/4, -z+1/2, y+1/4)',
7793 '(x+3/4, -z, y+3/4)', '(z+3/4, y+1/4, -x+1/2)',
7794 '(z+3/4, y+3/4, -x)', '(z+1/4, y+1/4, -x)',
7795 '(z+1/4, y+3/4, -x+1/2)',
7796 '(z+1/4, -y+1/2, x+3/4)', '(z+1/4, -y, x+1/4)',
7797 '(z+3/4, -y+1/2, x+1/4)', '(z+3/4, -y, x+3/4)',
7798 '(-z+1/2, y+3/4, x+1/4)',
7799 '(-z+1/2, y+1/4, x+3/4)', '(-z, y+3/4, x+3/4)',
7800 '(-z, y+1/4, x+1/4)', '(-z, -y, -x)',
7801 '(-z, -y+1/2, -x+1/2)', '(-z+1/2, -y, -x+1/2)',
7802 '(-z+1/2, -y+1/2, -x)', '(-x, -y, -z)',
7803 '(-x, -y+1/2, -z+1/2)', '(-x+1/2, -y, -z+1/2)',
7804 '(-x+1/2, -y+1/2, -z)', '(x+1/4, y+3/4, -z+1/2)',
7805 '(x+1/4, y+1/4, -z)', '(x+3/4, y+3/4, -z)',
7806 '(x+3/4, y+1/4, -z+1/2)',
7807 '(x+3/4, -y+1/2, z+1/4)', '(x+3/4, -y, z+3/4)',
7808 '(x+1/4, -y+1/2, z+3/4)', '(x+1/4, -y, z+1/4)',
7809 '(-x+1/2, y+1/4, z+3/4)',
7810 '(-x+1/2, y+3/4, z+1/4)', '(-x, y+1/4, z+1/4)',
7811 '(-x, y+3/4, z+3/4)', '(-z, -x, -y)',
7812 '(-z, -x+1/2, -y+1/2)', '(-z+1/2, -x, -y+1/2)',
7813 '(-z+1/2, -x+1/2, -y)', '(-z+1/2, x+1/4, y+3/4)',
7814 '(-z+1/2, x+3/4, y+1/4)', '(-z, x+1/4, y+1/4)',
7815 '(-z, x+3/4, y+3/4)', '(z+1/4, x+3/4, -y+1/2)',
7816 '(z+1/4, x+1/4, -y)', '(z+3/4, x+3/4, -y)',
7817 '(z+3/4, x+1/4, -y+1/2)',
7818 '(z+3/4, -x+1/2, y+1/4)', '(z+3/4, -x, y+3/4)',
7819 '(z+1/4, -x+1/2, y+3/4)', '(z+1/4, -x, y+1/4)',
7820 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
7821 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
7822 '(y+3/4, -z+1/2, x+1/4)', '(y+3/4, -z, x+3/4)',
7823 '(y+1/4, -z+1/2, x+3/4)', '(y+1/4, -z, x+1/4)',
7824 '(-y+1/2, z+1/4, x+3/4)',
7825 '(-y+1/2, z+3/4, x+1/4)', '(-y, z+1/4, x+1/4)',
7826 '(-y, z+3/4, x+3/4)', '(y+1/4, z+3/4, -x+1/2)',
7827 '(y+1/4, z+1/4, -x)', '(y+3/4, z+3/4, -x)',
7828 '(y+3/4, z+1/4, -x+1/2)',
7829 '(-y+1/4, -x+3/4, z+1/2)', '(-y+1/4, -x+1/4, z)',
7830 '(-y+3/4, -x+3/4, z)', '(-y+3/4, -x+1/4, z+1/2)',
7831 '(y, x, z)', '(y, x+1/2, z+1/2)',
7832 '(y+1/2, x, z+1/2)', '(y+1/2, x+1/2, z)',
7833 '(-y+3/4, x+1/2, -z+1/4)', '(-y+3/4, x, -z+3/4)',
7834 '(-y+1/4, x+1/2, -z+3/4)', '(-y+1/4, x, -z+1/4)',
7835 '(y+1/2, -x+1/4, -z+3/4)',
7836 '(y+1/2, -x+3/4, -z+1/4)', '(y, -x+1/4, -z+1/4)',
7837 '(y, -x+3/4, -z+3/4)', '(-x+1/4, -z+3/4, y+1/2)',
7838 '(-x+1/4, -z+1/4, y)', '(-x+3/4, -z+3/4, y)',
7839 '(-x+3/4, -z+1/4, y+1/2)',
7840 '(x+1/2, -z+1/4, -y+3/4)',
7841 '(x+1/2, -z+3/4, -y+1/4)', '(x, -z+1/4, -y+1/4)',
7842 '(x, -z+3/4, -y+3/4)', '(x, z, y)',
7843 '(x, z+1/2, y+1/2)', '(x+1/2, z, y+1/2)',
7844 '(x+1/2, z+1/2, y)', '(-x+3/4, z+1/2, -y+1/4)',
7845 '(-x+3/4, z, -y+3/4)', '(-x+1/4, z+1/2, -y+3/4)',
7846 '(-x+1/4, z, -y+1/4)', '(-z+1/4, -y+3/4, x+1/2)',
7847 '(-z+1/4, -y+1/4, x)', '(-z+3/4, -y+3/4, x)',
7848 '(-z+3/4, -y+1/4, x+1/2)',
7849 '(-z+3/4, y+1/2, -x+1/4)', '(-z+3/4, y, -x+3/4)',
7850 '(-z+1/4, y+1/2, -x+3/4)', '(-z+1/4, y, -x+1/4)',
7851 '(z+1/2, -y+1/4, -x+3/4)',
7852 '(z+1/2, -y+3/4, -x+1/4)', '(z, -y+1/4, -x+1/4)',
7853 '(z, -y+3/4, -x+3/4)', '(z, y, x)',
7854 '(z, y+1/2, x+1/2)', '(z+1/2, y, x+1/2)',
7855 '(z+1/2, y+1/2, x)'))},
7856 '228:1': {'16a': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
7857 '(1/2, 1/2, 0)', '(3/4, 1/4, 3/4)',
7858 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 1/4)',
7859 '(1/4, 3/4, 3/4)', '(3/4, 3/4, 3/4)',
7860 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
7861 '(1/4, 1/4, 3/4)', '(0, 1/2, 0)', '(0, 0, 1/2)',
7862 '(1/2, 1/2, 1/2)', '(1/2, 0, 0)')),
7863 '32b': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
7864 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
7865 '(7/8, 3/8, 5/8)', '(7/8, 7/8, 1/8)',
7866 '(3/8, 3/8, 1/8)', '(3/8, 7/8, 5/8)',
7867 '(3/8, 5/8, 7/8)', '(3/8, 1/8, 3/8)',
7868 '(7/8, 5/8, 3/8)', '(7/8, 1/8, 7/8)',
7869 '(5/8, 7/8, 3/8)', '(5/8, 3/8, 7/8)',
7870 '(1/8, 7/8, 7/8)', '(1/8, 3/8, 3/8)',
7871 '(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)',
7872 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
7873 '(7/8, 3/8, 1/8)', '(7/8, 7/8, 5/8)',
7874 '(3/8, 3/8, 5/8)', '(3/8, 7/8, 1/8)',
7875 '(3/8, 1/8, 7/8)', '(3/8, 5/8, 3/8)',
7876 '(7/8, 1/8, 3/8)', '(7/8, 5/8, 7/8)',
7877 '(1/8, 7/8, 3/8)', '(1/8, 3/8, 7/8)',
7878 '(5/8, 7/8, 7/8)', '(5/8, 3/8, 3/8)')),
7879 '32c': (0, ('(3/8, 3/8, 3/8)', '(3/8, 7/8, 7/8)',
7880 '(7/8, 3/8, 7/8)', '(7/8, 7/8, 3/8)',
7881 '(5/8, 1/8, 7/8)', '(5/8, 5/8, 3/8)',
7882 '(1/8, 1/8, 3/8)', '(1/8, 5/8, 7/8)',
7883 '(1/8, 7/8, 5/8)', '(1/8, 3/8, 1/8)',
7884 '(5/8, 7/8, 1/8)', '(5/8, 3/8, 5/8)',
7885 '(7/8, 5/8, 1/8)', '(7/8, 1/8, 5/8)',
7886 '(3/8, 5/8, 5/8)', '(3/8, 1/8, 1/8)',
7887 '(1/8, 5/8, 3/8)', '(1/8, 1/8, 7/8)',
7888 '(5/8, 5/8, 7/8)', '(5/8, 1/8, 3/8)',
7889 '(7/8, 7/8, 7/8)', '(7/8, 3/8, 3/8)',
7890 '(3/8, 7/8, 3/8)', '(3/8, 3/8, 7/8)',
7891 '(5/8, 3/8, 1/8)', '(5/8, 7/8, 5/8)',
7892 '(1/8, 3/8, 5/8)', '(1/8, 7/8, 1/8)',
7893 '(3/8, 1/8, 5/8)', '(3/8, 5/8, 1/8)',
7894 '(7/8, 1/8, 1/8)', '(7/8, 5/8, 5/8)')),
7895 '48d': (0, ('(1/4, 0, 0)', '(1/4, 1/2, 1/2)', '(3/4, 0, 1/2)',
7896 '(3/4, 1/2, 0)', '(3/4, 1/2, 1/2)', '(3/4, 0, 0)',
7897 '(1/4, 1/2, 0)', '(1/4, 0, 1/2)', '(0, 1/4, 0)',
7898 '(0, 3/4, 1/2)', '(1/2, 1/4, 1/2)',
7899 '(1/2, 3/4, 0)', '(1/2, 3/4, 1/2)',
7900 '(1/2, 1/4, 0)', '(0, 3/4, 0)', '(0, 1/4, 1/2)',
7901 '(0, 0, 1/4)', '(0, 1/2, 3/4)', '(1/2, 0, 3/4)',
7902 '(1/2, 1/2, 1/4)', '(1/2, 1/2, 3/4)',
7903 '(1/2, 0, 1/4)', '(0, 1/2, 1/4)', '(0, 0, 3/4)',
7904 '(3/4, 1/2, 3/4)', '(3/4, 0, 1/4)',
7905 '(1/4, 1/2, 1/4)', '(1/4, 0, 3/4)',
7906 '(1/4, 0, 1/4)', '(1/4, 1/2, 3/4)',
7907 '(3/4, 0, 3/4)', '(3/4, 1/2, 1/4)',
7908 '(0, 1/4, 3/4)', '(0, 3/4, 1/4)',
7909 '(1/2, 1/4, 1/4)', '(1/2, 3/4, 3/4)',
7910 '(1/2, 3/4, 1/4)', '(1/2, 1/4, 3/4)',
7911 '(0, 3/4, 3/4)', '(0, 1/4, 1/4)',
7912 '(3/4, 1/4, 1/2)', '(3/4, 3/4, 0)',
7913 '(1/4, 1/4, 0)', '(1/4, 3/4, 1/2)',
7914 '(1/4, 3/4, 0)', '(1/4, 1/4, 1/2)',
7915 '(3/4, 3/4, 1/2)', '(3/4, 1/4, 0)')),
7916 '64e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
7917 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
7918 '(-x, -x+1/2, x+1/2)', '(-x, -x, x)',
7919 '(-x+1/2, -x+1/2, x)', '(-x+1/2, -x, x+1/2)',
7920 '(-x+1/2, x+1/2, -x)', '(-x+1/2, x, -x+1/2)',
7921 '(-x, x+1/2, -x+1/2)', '(-x, x, -x)',
7922 '(x+1/2, -x, -x+1/2)', '(x+1/2, -x+1/2, -x)',
7923 '(x, -x, -x)', '(x, -x+1/2, -x+1/2)',
7924 '(x+3/4, x+1/4, -x+3/4)',
7925 '(x+3/4, x+3/4, -x+1/4)',
7926 '(x+1/4, x+1/4, -x+1/4)',
7927 '(x+1/4, x+3/4, -x+3/4)',
7928 '(-x+1/4, -x+1/4, -x+1/4)',
7929 '(-x+1/4, -x+3/4, -x+3/4)',
7930 '(-x+3/4, -x+1/4, -x+3/4)',
7931 '(-x+3/4, -x+3/4, -x+1/4)',
7932 '(x+1/4, -x+3/4, x+3/4)',
7933 '(x+1/4, -x+1/4, x+1/4)',
7934 '(x+3/4, -x+3/4, x+1/4)',
7935 '(x+3/4, -x+1/4, x+3/4)',
7936 '(-x+3/4, x+3/4, x+1/4)',
7937 '(-x+3/4, x+1/4, x+3/4)',
7938 '(-x+1/4, x+3/4, x+3/4)',
7939 '(-x+1/4, x+1/4, x+1/4)',
7940 '(-x+3/4, -x+3/4, -x+3/4)',
7941 '(-x+3/4, -x+1/4, -x+1/4)',
7942 '(-x+1/4, -x+3/4, -x+1/4)',
7943 '(-x+1/4, -x+1/4, -x+3/4)',
7944 '(x+3/4, x+1/4, -x+1/4)',
7945 '(x+3/4, x+3/4, -x+3/4)',
7946 '(x+1/4, x+1/4, -x+3/4)',
7947 '(x+1/4, x+3/4, -x+1/4)',
7948 '(x+1/4, -x+1/4, x+3/4)',
7949 '(x+1/4, -x+3/4, x+1/4)',
7950 '(x+3/4, -x+1/4, x+1/4)',
7951 '(x+3/4, -x+3/4, x+3/4)',
7952 '(-x+1/4, x+3/4, x+1/4)',
7953 '(-x+1/4, x+1/4, x+3/4)',
7954 '(-x+3/4, x+3/4, x+3/4)',
7955 '(-x+3/4, x+1/4, x+1/4)', '(-x, -x+1/2, x)',
7956 '(-x, -x, x+1/2)', '(-x+1/2, -x+1/2, x+1/2)',
7957 '(-x+1/2, -x, x)', '(x+1/2, x+1/2, x+1/2)',
7958 '(x+1/2, x, x)', '(x, x+1/2, x)', '(x, x, x+1/2)',
7959 '(-x+1/2, x, -x)', '(-x+1/2, x+1/2, -x+1/2)',
7960 '(-x, x, -x+1/2)', '(-x, x+1/2, -x)',
7961 '(x, -x, -x+1/2)', '(x, -x+1/2, -x)',
7962 '(x+1/2, -x, -x)', '(x+1/2, -x+1/2, -x+1/2)')),
7963 '96f': (1, ('(x, 0, 0)', '(x, 1/2, 1/2)', '(x+1/2, 0, 1/2)',
7964 '(x+1/2, 1/2, 0)', '(-x, 1/2, 1/2)', '(-x, 0, 0)',
7965 '(-x+1/2, 1/2, 0)', '(-x+1/2, 0, 1/2)',
7966 '(0, x, 0)', '(0, x+1/2, 1/2)', '(1/2, x, 1/2)',
7967 '(1/2, x+1/2, 0)', '(1/2, -x, 1/2)',
7968 '(1/2, -x+1/2, 0)', '(0, -x, 0)',
7969 '(0, -x+1/2, 1/2)', '(0, 0, x)',
7970 '(0, 1/2, x+1/2)', '(1/2, 0, x+1/2)',
7971 '(1/2, 1/2, x)', '(1/2, 1/2, -x)',
7972 '(1/2, 0, -x+1/2)', '(0, 1/2, -x+1/2)',
7973 '(0, 0, -x)', '(3/4, x+1/4, 3/4)',
7974 '(3/4, x+3/4, 1/4)', '(1/4, x+1/4, 1/4)',
7975 '(1/4, x+3/4, 3/4)', '(1/4, -x+1/4, 1/4)',
7976 '(1/4, -x+3/4, 3/4)', '(3/4, -x+1/4, 3/4)',
7977 '(3/4, -x+3/4, 1/4)', '(x+3/4, 1/4, 3/4)',
7978 '(x+3/4, 3/4, 1/4)', '(x+1/4, 1/4, 1/4)',
7979 '(x+1/4, 3/4, 3/4)', '(-x+3/4, 3/4, 1/4)',
7980 '(-x+3/4, 1/4, 3/4)', '(-x+1/4, 3/4, 3/4)',
7981 '(-x+1/4, 1/4, 1/4)', '(3/4, 1/4, -x+3/4)',
7982 '(3/4, 3/4, -x+1/4)', '(1/4, 1/4, -x+1/4)',
7983 '(1/4, 3/4, -x+3/4)', '(1/4, 3/4, x+3/4)',
7984 '(1/4, 1/4, x+1/4)', '(3/4, 3/4, x+1/4)',
7985 '(3/4, 1/4, x+3/4)', '(-x+3/4, 3/4, 3/4)',
7986 '(-x+3/4, 1/4, 1/4)', '(-x+1/4, 3/4, 1/4)',
7987 '(-x+1/4, 1/4, 3/4)', '(x+3/4, 1/4, 1/4)',
7988 '(x+3/4, 3/4, 3/4)', '(x+1/4, 1/4, 3/4)',
7989 '(x+1/4, 3/4, 1/4)', '(3/4, -x+3/4, 3/4)',
7990 '(3/4, -x+1/4, 1/4)', '(1/4, -x+3/4, 1/4)',
7991 '(1/4, -x+1/4, 3/4)', '(1/4, x+3/4, 1/4)',
7992 '(1/4, x+1/4, 3/4)', '(3/4, x+3/4, 3/4)',
7993 '(3/4, x+1/4, 1/4)', '(3/4, 3/4, -x+3/4)',
7994 '(3/4, 1/4, -x+1/4)', '(1/4, 3/4, -x+1/4)',
7995 '(1/4, 1/4, -x+3/4)', '(1/4, 1/4, x+3/4)',
7996 '(1/4, 3/4, x+1/4)', '(3/4, 1/4, x+1/4)',
7997 '(3/4, 3/4, x+3/4)', '(0, -x+1/2, 0)',
7998 '(0, -x, 1/2)', '(1/2, -x+1/2, 1/2)',
7999 '(1/2, -x, 0)', '(1/2, x+1/2, 1/2)',
8000 '(1/2, x, 0)', '(0, x+1/2, 0)', '(0, x, 1/2)',
8001 '(-x, 1/2, 0)', '(-x, 0, 1/2)',
8002 '(-x+1/2, 1/2, 1/2)', '(-x+1/2, 0, 0)',
8003 '(x, 0, 1/2)', '(x, 1/2, 0)', '(x+1/2, 0, 0)',
8004 '(x+1/2, 1/2, 1/2)', '(0, 1/2, x)',
8005 '(0, 0, x+1/2)', '(1/2, 1/2, x+1/2)',
8006 '(1/2, 0, x)', '(1/2, 0, -x)',
8007 '(1/2, 1/2, -x+1/2)', '(0, 0, -x+1/2)',
8008 '(0, 1/2, -x)')),
8009 '96g': (2, ('(1/8, y, -y+1/4)', '(1/8, y+1/2, -y+3/4)',
8010 '(5/8, y, -y+3/4)', '(5/8, y+1/2, -y+1/4)',
8011 '(7/8, -y+1/2, -y+3/4)', '(7/8, -y, -y+1/4)',
8012 '(3/8, -y+1/2, -y+1/4)', '(3/8, -y, -y+3/4)',
8013 '(3/8, y+1/2, y+3/4)', '(3/8, y, y+1/4)',
8014 '(7/8, y+1/2, y+1/4)', '(7/8, y, y+3/4)',
8015 '(5/8, -y, y+1/4)', '(5/8, -y+1/2, y+3/4)',
8016 '(1/8, -y, y+3/4)', '(1/8, -y+1/2, y+1/4)',
8017 '(-y+1/4, 1/8, y)', '(-y+1/4, 5/8, y+1/2)',
8018 '(-y+3/4, 1/8, y+1/2)', '(-y+3/4, 5/8, y)',
8019 '(-y+3/4, 7/8, -y+1/2)', '(-y+3/4, 3/8, -y)',
8020 '(-y+1/4, 7/8, -y)', '(-y+1/4, 3/8, -y+1/2)',
8021 '(y+3/4, 3/8, y+1/2)', '(y+3/4, 7/8, y)',
8022 '(y+1/4, 3/8, y)', '(y+1/4, 7/8, y+1/2)',
8023 '(y+1/4, 5/8, -y)', '(y+1/4, 1/8, -y+1/2)',
8024 '(y+3/4, 5/8, -y+1/2)', '(y+3/4, 1/8, -y)',
8025 '(y, -y+1/4, 1/8)', '(y, -y+3/4, 5/8)',
8026 '(y+1/2, -y+1/4, 5/8)', '(y+1/2, -y+3/4, 1/8)',
8027 '(-y+1/2, -y+3/4, 7/8)', '(-y+1/2, -y+1/4, 3/8)',
8028 '(-y, -y+3/4, 3/8)', '(-y, -y+1/4, 7/8)',
8029 '(y+1/2, y+3/4, 3/8)', '(y+1/2, y+1/4, 7/8)',
8030 '(y, y+3/4, 7/8)', '(y, y+1/4, 3/8)',
8031 '(-y, y+1/4, 5/8)', '(-y, y+3/4, 1/8)',
8032 '(-y+1/2, y+1/4, 1/8)', '(-y+1/2, y+3/4, 5/8)',
8033 '(5/8, -y+3/4, y+1/2)', '(5/8, -y+1/4, y)',
8034 '(1/8, -y+3/4, y)', '(1/8, -y+1/4, y+1/2)',
8035 '(7/8, y+1/4, y)', '(7/8, y+3/4, y+1/2)',
8036 '(3/8, y+1/4, y+1/2)', '(3/8, y+3/4, y)',
8037 '(3/8, -y+1/4, -y)', '(3/8, -y+3/4, -y+1/2)',
8038 '(7/8, -y+1/4, -y+1/2)', '(7/8, -y+3/4, -y)',
8039 '(1/8, y+3/4, -y+1/2)', '(1/8, y+1/4, -y)',
8040 '(5/8, y+3/4, -y)', '(5/8, y+1/4, -y+1/2)',
8041 '(y+1/2, 5/8, -y+3/4)', '(y+1/2, 1/8, -y+1/4)',
8042 '(y, 5/8, -y+1/4)', '(y, 1/8, -y+3/4)',
8043 '(y, 7/8, y+1/4)', '(y, 3/8, y+3/4)',
8044 '(y+1/2, 7/8, y+3/4)', '(y+1/2, 3/8, y+1/4)',
8045 '(-y, 3/8, -y+1/4)', '(-y, 7/8, -y+3/4)',
8046 '(-y+1/2, 3/8, -y+3/4)', '(-y+1/2, 7/8, -y+1/4)',
8047 '(-y+1/2, 1/8, y+3/4)', '(-y+1/2, 5/8, y+1/4)',
8048 '(-y, 1/8, y+1/4)', '(-y, 5/8, y+3/4)',
8049 '(-y+3/4, y+1/2, 5/8)', '(-y+3/4, y, 1/8)',
8050 '(-y+1/4, y+1/2, 1/8)', '(-y+1/4, y, 5/8)',
8051 '(y+1/4, y, 7/8)', '(y+1/4, y+1/2, 3/8)',
8052 '(y+3/4, y, 3/8)', '(y+3/4, y+1/2, 7/8)',
8053 '(-y+1/4, -y, 3/8)', '(-y+1/4, -y+1/2, 7/8)',
8054 '(-y+3/4, -y, 7/8)', '(-y+3/4, -y+1/2, 3/8)',
8055 '(y+3/4, -y+1/2, 1/8)', '(y+3/4, -y, 5/8)',
8056 '(y+1/4, -y+1/2, 5/8)', '(y+1/4, -y, 1/8)')),
8057 '192h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
8058 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
8059 '(-x, -y+1/2, z+1/2)', '(-x, -y, z)',
8060 '(-x+1/2, -y+1/2, z)', '(-x+1/2, -y, z+1/2)',
8061 '(-x+1/2, y+1/2, -z)', '(-x+1/2, y, -z+1/2)',
8062 '(-x, y+1/2, -z+1/2)', '(-x, y, -z)',
8063 '(x+1/2, -y, -z+1/2)', '(x+1/2, -y+1/2, -z)',
8064 '(x, -y, -z)', '(x, -y+1/2, -z+1/2)',
8065 '(z, x, y)', '(z, x+1/2, y+1/2)',
8066 '(z+1/2, x, y+1/2)', '(z+1/2, x+1/2, y)',
8067 '(z+1/2, -x, -y+1/2)', '(z+1/2, -x+1/2, -y)',
8068 '(z, -x, -y)', '(z, -x+1/2, -y+1/2)',
8069 '(-z, -x+1/2, y+1/2)', '(-z, -x, y)',
8070 '(-z+1/2, -x+1/2, y)', '(-z+1/2, -x, y+1/2)',
8071 '(-z+1/2, x+1/2, -y)', '(-z+1/2, x, -y+1/2)',
8072 '(-z, x+1/2, -y+1/2)', '(-z, x, -y)',
8073 '(y, z, x)', '(y, z+1/2, x+1/2)',
8074 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
8075 '(-y+1/2, z+1/2, -x)', '(-y+1/2, z, -x+1/2)',
8076 '(-y, z+1/2, -x+1/2)', '(-y, z, -x)',
8077 '(y+1/2, -z, -x+1/2)', '(y+1/2, -z+1/2, -x)',
8078 '(y, -z, -x)', '(y, -z+1/2, -x+1/2)',
8079 '(-y, -z+1/2, x+1/2)', '(-y, -z, x)',
8080 '(-y+1/2, -z+1/2, x)', '(-y+1/2, -z, x+1/2)',
8081 '(y+3/4, x+1/4, -z+3/4)',
8082 '(y+3/4, x+3/4, -z+1/4)',
8083 '(y+1/4, x+1/4, -z+1/4)',
8084 '(y+1/4, x+3/4, -z+3/4)',
8085 '(-y+1/4, -x+1/4, -z+1/4)',
8086 '(-y+1/4, -x+3/4, -z+3/4)',
8087 '(-y+3/4, -x+1/4, -z+3/4)',
8088 '(-y+3/4, -x+3/4, -z+1/4)',
8089 '(y+1/4, -x+3/4, z+3/4)',
8090 '(y+1/4, -x+1/4, z+1/4)',
8091 '(y+3/4, -x+3/4, z+1/4)',
8092 '(y+3/4, -x+1/4, z+3/4)',
8093 '(-y+3/4, x+3/4, z+1/4)',
8094 '(-y+3/4, x+1/4, z+3/4)',
8095 '(-y+1/4, x+3/4, z+3/4)',
8096 '(-y+1/4, x+1/4, z+1/4)',
8097 '(x+3/4, z+1/4, -y+3/4)',
8098 '(x+3/4, z+3/4, -y+1/4)',
8099 '(x+1/4, z+1/4, -y+1/4)',
8100 '(x+1/4, z+3/4, -y+3/4)',
8101 '(-x+3/4, z+3/4, y+1/4)',
8102 '(-x+3/4, z+1/4, y+3/4)',
8103 '(-x+1/4, z+3/4, y+3/4)',
8104 '(-x+1/4, z+1/4, y+1/4)',
8105 '(-x+1/4, -z+1/4, -y+1/4)',
8106 '(-x+1/4, -z+3/4, -y+3/4)',
8107 '(-x+3/4, -z+1/4, -y+3/4)',
8108 '(-x+3/4, -z+3/4, -y+1/4)',
8109 '(x+1/4, -z+3/4, y+3/4)',
8110 '(x+1/4, -z+1/4, y+1/4)',
8111 '(x+3/4, -z+3/4, y+1/4)',
8112 '(x+3/4, -z+1/4, y+3/4)',
8113 '(z+3/4, y+1/4, -x+3/4)',
8114 '(z+3/4, y+3/4, -x+1/4)',
8115 '(z+1/4, y+1/4, -x+1/4)',
8116 '(z+1/4, y+3/4, -x+3/4)',
8117 '(z+1/4, -y+3/4, x+3/4)',
8118 '(z+1/4, -y+1/4, x+1/4)',
8119 '(z+3/4, -y+3/4, x+1/4)',
8120 '(z+3/4, -y+1/4, x+3/4)',
8121 '(-z+3/4, y+3/4, x+1/4)',
8122 '(-z+3/4, y+1/4, x+3/4)',
8123 '(-z+1/4, y+3/4, x+3/4)',
8124 '(-z+1/4, y+1/4, x+1/4)',
8125 '(-z+1/4, -y+1/4, -x+1/4)',
8126 '(-z+1/4, -y+3/4, -x+3/4)',
8127 '(-z+3/4, -y+1/4, -x+3/4)',
8128 '(-z+3/4, -y+3/4, -x+1/4)',
8129 '(-x+3/4, -y+3/4, -z+3/4)',
8130 '(-x+3/4, -y+1/4, -z+1/4)',
8131 '(-x+1/4, -y+3/4, -z+1/4)',
8132 '(-x+1/4, -y+1/4, -z+3/4)',
8133 '(x+3/4, y+1/4, -z+1/4)',
8134 '(x+3/4, y+3/4, -z+3/4)',
8135 '(x+1/4, y+1/4, -z+3/4)',
8136 '(x+1/4, y+3/4, -z+1/4)',
8137 '(x+1/4, -y+1/4, z+3/4)',
8138 '(x+1/4, -y+3/4, z+1/4)',
8139 '(x+3/4, -y+1/4, z+1/4)',
8140 '(x+3/4, -y+3/4, z+3/4)',
8141 '(-x+1/4, y+3/4, z+1/4)',
8142 '(-x+1/4, y+1/4, z+3/4)',
8143 '(-x+3/4, y+3/4, z+3/4)',
8144 '(-x+3/4, y+1/4, z+1/4)',
8145 '(-z+3/4, -x+3/4, -y+3/4)',
8146 '(-z+3/4, -x+1/4, -y+1/4)',
8147 '(-z+1/4, -x+3/4, -y+1/4)',
8148 '(-z+1/4, -x+1/4, -y+3/4)',
8149 '(-z+1/4, x+3/4, y+1/4)',
8150 '(-z+1/4, x+1/4, y+3/4)',
8151 '(-z+3/4, x+3/4, y+3/4)',
8152 '(-z+3/4, x+1/4, y+1/4)',
8153 '(z+3/4, x+1/4, -y+1/4)',
8154 '(z+3/4, x+3/4, -y+3/4)',
8155 '(z+1/4, x+1/4, -y+3/4)',
8156 '(z+1/4, x+3/4, -y+1/4)',
8157 '(z+1/4, -x+1/4, y+3/4)',
8158 '(z+1/4, -x+3/4, y+1/4)',
8159 '(z+3/4, -x+1/4, y+1/4)',
8160 '(z+3/4, -x+3/4, y+3/4)',
8161 '(-y+3/4, -z+3/4, -x+3/4)',
8162 '(-y+3/4, -z+1/4, -x+1/4)',
8163 '(-y+1/4, -z+3/4, -x+1/4)',
8164 '(-y+1/4, -z+1/4, -x+3/4)',
8165 '(y+1/4, -z+1/4, x+3/4)',
8166 '(y+1/4, -z+3/4, x+1/4)',
8167 '(y+3/4, -z+1/4, x+1/4)',
8168 '(y+3/4, -z+3/4, x+3/4)',
8169 '(-y+1/4, z+3/4, x+1/4)',
8170 '(-y+1/4, z+1/4, x+3/4)',
8171 '(-y+3/4, z+3/4, x+3/4)',
8172 '(-y+3/4, z+1/4, x+1/4)',
8173 '(y+3/4, z+1/4, -x+1/4)',
8174 '(y+3/4, z+3/4, -x+3/4)',
8175 '(y+1/4, z+1/4, -x+3/4)',
8176 '(y+1/4, z+3/4, -x+1/4)', '(-y, -x+1/2, z)',
8177 '(-y, -x, z+1/2)', '(-y+1/2, -x+1/2, z+1/2)',
8178 '(-y+1/2, -x, z)', '(y+1/2, x+1/2, z+1/2)',
8179 '(y+1/2, x, z)', '(y, x+1/2, z)',
8180 '(y, x, z+1/2)', '(-y+1/2, x, -z)',
8181 '(-y+1/2, x+1/2, -z+1/2)', '(-y, x, -z+1/2)',
8182 '(-y, x+1/2, -z)', '(y, -x, -z+1/2)',
8183 '(y, -x+1/2, -z)', '(y+1/2, -x, -z)',
8184 '(y+1/2, -x+1/2, -z+1/2)', '(-x, -z+1/2, y)',
8185 '(-x, -z, y+1/2)', '(-x+1/2, -z+1/2, y+1/2)',
8186 '(-x+1/2, -z, y)', '(x, -z, -y+1/2)',
8187 '(x, -z+1/2, -y)', '(x+1/2, -z, -y)',
8188 '(x+1/2, -z+1/2, -y+1/2)',
8189 '(x+1/2, z+1/2, y+1/2)', '(x+1/2, z, y)',
8190 '(x, z+1/2, y)', '(x, z, y+1/2)',
8191 '(-x+1/2, z, -y)', '(-x+1/2, z+1/2, -y+1/2)',
8192 '(-x, z, -y+1/2)', '(-x, z+1/2, -y)',
8193 '(-z, -y+1/2, x)', '(-z, -y, x+1/2)',
8194 '(-z+1/2, -y+1/2, x+1/2)', '(-z+1/2, -y, x)',
8195 '(-z+1/2, y, -x)', '(-z+1/2, y+1/2, -x+1/2)',
8196 '(-z, y, -x+1/2)', '(-z, y+1/2, -x)',
8197 '(z, -y, -x+1/2)', '(z, -y+1/2, -x)',
8198 '(z+1/2, -y, -x)', '(z+1/2, -y+1/2, -x+1/2)',
8199 '(z+1/2, y+1/2, x+1/2)', '(z+1/2, y, x)',
8200 '(z, y+1/2, x)', '(z, y, x+1/2)'))},
8201 '228:2': {'16a': (0, ('(1/8, 1/8, 1/8)', '(1/8, 5/8, 5/8)',
8202 '(5/8, 1/8, 5/8)', '(5/8, 5/8, 1/8)',
8203 '(7/8, 3/8, 7/8)', '(7/8, 7/8, 3/8)',
8204 '(3/8, 3/8, 3/8)', '(3/8, 7/8, 7/8)',
8205 '(7/8, 7/8, 7/8)', '(7/8, 3/8, 3/8)',
8206 '(3/8, 7/8, 3/8)', '(3/8, 3/8, 7/8)',
8207 '(1/8, 5/8, 1/8)', '(1/8, 1/8, 5/8)',
8208 '(5/8, 5/8, 5/8)', '(5/8, 1/8, 1/8)')),
8209 '32b': (0, ('(1/4, 1/4, 1/4)', '(1/4, 3/4, 3/4)',
8210 '(3/4, 1/4, 3/4)', '(3/4, 3/4, 1/4)',
8211 '(0, 1/2, 3/4)', '(0, 0, 1/4)', '(1/2, 1/2, 1/4)',
8212 '(1/2, 0, 3/4)', '(1/2, 3/4, 0)',
8213 '(1/2, 1/4, 1/2)', '(0, 3/4, 1/2)', '(0, 1/4, 0)',
8214 '(3/4, 0, 1/2)', '(3/4, 1/2, 0)', '(1/4, 0, 0)',
8215 '(1/4, 1/2, 1/2)', '(3/4, 3/4, 3/4)',
8216 '(3/4, 1/4, 1/4)', '(1/4, 3/4, 1/4)',
8217 '(1/4, 1/4, 3/4)', '(0, 1/2, 1/4)', '(0, 0, 3/4)',
8218 '(1/2, 1/2, 3/4)', '(1/2, 0, 1/4)',
8219 '(1/2, 1/4, 0)', '(1/2, 3/4, 1/2)',
8220 '(0, 1/4, 1/2)', '(0, 3/4, 0)', '(1/4, 0, 1/2)',
8221 '(1/4, 1/2, 0)', '(3/4, 0, 0)', '(3/4, 1/2, 1/2)'
8222 )),
8223 '32c': (0, ('(0, 0, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 1/2)',
8224 '(1/2, 1/2, 0)', '(1/4, 3/4, 1/2)',
8225 '(1/4, 1/4, 0)', '(3/4, 3/4, 0)',
8226 '(3/4, 1/4, 1/2)', '(3/4, 1/2, 1/4)',
8227 '(3/4, 0, 3/4)', '(1/4, 1/2, 3/4)',
8228 '(1/4, 0, 1/4)', '(1/2, 1/4, 3/4)',
8229 '(1/2, 3/4, 1/4)', '(0, 1/4, 1/4)',
8230 '(0, 3/4, 3/4)', '(3/4, 1/4, 0)',
8231 '(3/4, 3/4, 1/2)', '(1/4, 1/4, 1/2)',
8232 '(1/4, 3/4, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 0)',
8233 '(0, 1/2, 0)', '(0, 0, 1/2)', '(1/4, 0, 3/4)',
8234 '(1/4, 1/2, 1/4)', '(3/4, 0, 1/4)',
8235 '(3/4, 1/2, 3/4)', '(0, 3/4, 1/4)',
8236 '(0, 1/4, 3/4)', '(1/2, 3/4, 3/4)',
8237 '(1/2, 1/4, 1/4)')),
8238 '48d': (0, ('(7/8, 1/8, 1/8)', '(7/8, 5/8, 5/8)',
8239 '(3/8, 1/8, 5/8)', '(3/8, 5/8, 1/8)',
8240 '(3/8, 5/8, 5/8)', '(3/8, 1/8, 1/8)',
8241 '(7/8, 5/8, 1/8)', '(7/8, 1/8, 5/8)',
8242 '(1/8, 7/8, 1/8)', '(1/8, 3/8, 5/8)',
8243 '(5/8, 7/8, 5/8)', '(5/8, 3/8, 1/8)',
8244 '(5/8, 3/8, 5/8)', '(5/8, 7/8, 1/8)',
8245 '(1/8, 3/8, 1/8)', '(1/8, 7/8, 5/8)',
8246 '(1/8, 1/8, 7/8)', '(1/8, 5/8, 3/8)',
8247 '(5/8, 1/8, 3/8)', '(5/8, 5/8, 7/8)',
8248 '(5/8, 5/8, 3/8)', '(5/8, 1/8, 7/8)',
8249 '(1/8, 5/8, 7/8)', '(1/8, 1/8, 3/8)',
8250 '(7/8, 1/8, 7/8)', '(7/8, 5/8, 3/8)',
8251 '(3/8, 1/8, 3/8)', '(3/8, 5/8, 7/8)',
8252 '(3/8, 5/8, 3/8)', '(3/8, 1/8, 7/8)',
8253 '(7/8, 5/8, 7/8)', '(7/8, 1/8, 3/8)',
8254 '(5/8, 3/8, 7/8)', '(5/8, 7/8, 3/8)',
8255 '(1/8, 3/8, 3/8)', '(1/8, 7/8, 7/8)',
8256 '(1/8, 7/8, 3/8)', '(1/8, 3/8, 7/8)',
8257 '(5/8, 7/8, 7/8)', '(5/8, 3/8, 3/8)',
8258 '(7/8, 3/8, 1/8)', '(7/8, 7/8, 5/8)',
8259 '(3/8, 3/8, 5/8)', '(3/8, 7/8, 1/8)',
8260 '(3/8, 7/8, 5/8)', '(3/8, 3/8, 1/8)',
8261 '(7/8, 7/8, 1/8)', '(7/8, 3/8, 5/8)')),
8262 '64e': (1, ('(x, x, x)', '(x, x+1/2, x+1/2)',
8263 '(x+1/2, x, x+1/2)', '(x+1/2, x+1/2, x)',
8264 '(-x+1/4, -x+3/4, x+1/2)', '(-x+1/4, -x+1/4, x)',
8265 '(-x+3/4, -x+3/4, x)', '(-x+3/4, -x+1/4, x+1/2)',
8266 '(-x+3/4, x+1/2, -x+1/4)', '(-x+3/4, x, -x+3/4)',
8267 '(-x+1/4, x+1/2, -x+3/4)', '(-x+1/4, x, -x+1/4)',
8268 '(x+1/2, -x+1/4, -x+3/4)',
8269 '(x+1/2, -x+3/4, -x+1/4)', '(x, -x+1/4, -x+1/4)',
8270 '(x, -x+3/4, -x+3/4)', '(x+3/4, x+1/4, -x)',
8271 '(x+3/4, x+3/4, -x+1/2)',
8272 '(x+1/4, x+1/4, -x+1/2)', '(x+1/4, x+3/4, -x)',
8273 '(-x+1/2, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x)',
8274 '(-x, -x+1/2, -x)', '(-x, -x, -x+1/2)',
8275 '(x+1/4, -x, x+3/4)', '(x+1/4, -x+1/2, x+1/4)',
8276 '(x+3/4, -x, x+1/4)', '(x+3/4, -x+1/2, x+3/4)',
8277 '(-x, x+3/4, x+1/4)', '(-x, x+1/4, x+3/4)',
8278 '(-x+1/2, x+3/4, x+3/4)',
8279 '(-x+1/2, x+1/4, x+1/4)', '(-x, -x, -x)',
8280 '(-x, -x+1/2, -x+1/2)', '(-x+1/2, -x, -x+1/2)',
8281 '(-x+1/2, -x+1/2, -x)', '(x+3/4, x+1/4, -x+1/2)',
8282 '(x+3/4, x+3/4, -x)', '(x+1/4, x+1/4, -x)',
8283 '(x+1/4, x+3/4, -x+1/2)',
8284 '(x+1/4, -x+1/2, x+3/4)', '(x+1/4, -x, x+1/4)',
8285 '(x+3/4, -x+1/2, x+1/4)', '(x+3/4, -x, x+3/4)',
8286 '(-x+1/2, x+3/4, x+1/4)',
8287 '(-x+1/2, x+1/4, x+3/4)', '(-x, x+3/4, x+3/4)',
8288 '(-x, x+1/4, x+1/4)', '(-x+1/4, -x+3/4, x)',
8289 '(-x+1/4, -x+1/4, x+1/2)',
8290 '(-x+3/4, -x+3/4, x+1/2)', '(-x+3/4, -x+1/4, x)',
8291 '(x+1/2, x+1/2, x+1/2)', '(x+1/2, x, x)',
8292 '(x, x+1/2, x)', '(x, x, x+1/2)',
8293 '(-x+3/4, x, -x+1/4)', '(-x+3/4, x+1/2, -x+3/4)',
8294 '(-x+1/4, x, -x+3/4)', '(-x+1/4, x+1/2, -x+1/4)',
8295 '(x, -x+1/4, -x+3/4)', '(x, -x+3/4, -x+1/4)',
8296 '(x+1/2, -x+1/4, -x+1/4)',
8297 '(x+1/2, -x+3/4, -x+3/4)')),
8298 '96f': (1, ('(x, 1/8, 1/8)', '(x, 5/8, 5/8)',
8299 '(x+1/2, 1/8, 5/8)', '(x+1/2, 5/8, 1/8)',
8300 '(-x+1/4, 5/8, 5/8)', '(-x+1/4, 1/8, 1/8)',
8301 '(-x+3/4, 5/8, 1/8)', '(-x+3/4, 1/8, 5/8)',
8302 '(1/8, x, 1/8)', '(1/8, x+1/2, 5/8)',
8303 '(5/8, x, 5/8)', '(5/8, x+1/2, 1/8)',
8304 '(5/8, -x+1/4, 5/8)', '(5/8, -x+3/4, 1/8)',
8305 '(1/8, -x+1/4, 1/8)', '(1/8, -x+3/4, 5/8)',
8306 '(1/8, 1/8, x)', '(1/8, 5/8, x+1/2)',
8307 '(5/8, 1/8, x+1/2)', '(5/8, 5/8, x)',
8308 '(5/8, 5/8, -x+1/4)', '(5/8, 1/8, -x+3/4)',
8309 '(1/8, 5/8, -x+3/4)', '(1/8, 1/8, -x+1/4)',
8310 '(7/8, x+1/4, 7/8)', '(7/8, x+3/4, 3/8)',
8311 '(3/8, x+1/4, 3/8)', '(3/8, x+3/4, 7/8)',
8312 '(3/8, -x+1/2, 3/8)', '(3/8, -x, 7/8)',
8313 '(7/8, -x+1/2, 7/8)', '(7/8, -x, 3/8)',
8314 '(x+3/4, 3/8, 7/8)', '(x+3/4, 7/8, 3/8)',
8315 '(x+1/4, 3/8, 3/8)', '(x+1/4, 7/8, 7/8)',
8316 '(-x, 7/8, 3/8)', '(-x, 3/8, 7/8)',
8317 '(-x+1/2, 7/8, 7/8)', '(-x+1/2, 3/8, 3/8)',
8318 '(7/8, 3/8, -x)', '(7/8, 7/8, -x+1/2)',
8319 '(3/8, 3/8, -x+1/2)', '(3/8, 7/8, -x)',
8320 '(3/8, 7/8, x+3/4)', '(3/8, 3/8, x+1/4)',
8321 '(7/8, 7/8, x+1/4)', '(7/8, 3/8, x+3/4)',
8322 '(-x, 7/8, 7/8)', '(-x, 3/8, 3/8)',
8323 '(-x+1/2, 7/8, 3/8)', '(-x+1/2, 3/8, 7/8)',
8324 '(x+3/4, 3/8, 3/8)', '(x+3/4, 7/8, 7/8)',
8325 '(x+1/4, 3/8, 7/8)', '(x+1/4, 7/8, 3/8)',
8326 '(7/8, -x, 7/8)', '(7/8, -x+1/2, 3/8)',
8327 '(3/8, -x, 3/8)', '(3/8, -x+1/2, 7/8)',
8328 '(3/8, x+3/4, 3/8)', '(3/8, x+1/4, 7/8)',
8329 '(7/8, x+3/4, 7/8)', '(7/8, x+1/4, 3/8)',
8330 '(7/8, 7/8, -x)', '(7/8, 3/8, -x+1/2)',
8331 '(3/8, 7/8, -x+1/2)', '(3/8, 3/8, -x)',
8332 '(3/8, 3/8, x+3/4)', '(3/8, 7/8, x+1/4)',
8333 '(7/8, 3/8, x+1/4)', '(7/8, 7/8, x+3/4)',
8334 '(1/8, -x+3/4, 1/8)', '(1/8, -x+1/4, 5/8)',
8335 '(5/8, -x+3/4, 5/8)', '(5/8, -x+1/4, 1/8)',
8336 '(5/8, x+1/2, 5/8)', '(5/8, x, 1/8)',
8337 '(1/8, x+1/2, 1/8)', '(1/8, x, 5/8)',
8338 '(-x+1/4, 5/8, 1/8)', '(-x+1/4, 1/8, 5/8)',
8339 '(-x+3/4, 5/8, 5/8)', '(-x+3/4, 1/8, 1/8)',
8340 '(x, 1/8, 5/8)', '(x, 5/8, 1/8)',
8341 '(x+1/2, 1/8, 1/8)', '(x+1/2, 5/8, 5/8)',
8342 '(1/8, 5/8, x)', '(1/8, 1/8, x+1/2)',
8343 '(5/8, 5/8, x+1/2)', '(5/8, 1/8, x)',
8344 '(5/8, 1/8, -x+1/4)', '(5/8, 5/8, -x+3/4)',
8345 '(1/8, 1/8, -x+3/4)', '(1/8, 5/8, -x+1/4)')),
8346 '96g': (2, ('(1/4, y, -y)', '(1/4, y+1/2, -y+1/2)',
8347 '(3/4, y, -y+1/2)', '(3/4, y+1/2, -y)',
8348 '(0, -y+3/4, -y+1/2)', '(0, -y+1/4, -y)',
8349 '(1/2, -y+3/4, -y)', '(1/2, -y+1/4, -y+1/2)',
8350 '(1/2, y+1/2, y+1/4)', '(1/2, y, y+3/4)',
8351 '(0, y+1/2, y+3/4)', '(0, y, y+1/4)',
8352 '(3/4, -y+1/4, y+3/4)', '(3/4, -y+3/4, y+1/4)',
8353 '(1/4, -y+1/4, y+1/4)', '(1/4, -y+3/4, y+3/4)',
8354 '(-y, 1/4, y)', '(-y, 3/4, y+1/2)',
8355 '(-y+1/2, 1/4, y+1/2)', '(-y+1/2, 3/4, y)',
8356 '(-y+1/2, 0, -y+3/4)', '(-y+1/2, 1/2, -y+1/4)',
8357 '(-y, 0, -y+1/4)', '(-y, 1/2, -y+3/4)',
8358 '(y+1/4, 1/2, y+1/2)', '(y+1/4, 0, y)',
8359 '(y+3/4, 1/2, y)', '(y+3/4, 0, y+1/2)',
8360 '(y+3/4, 3/4, -y+1/4)', '(y+3/4, 1/4, -y+3/4)',
8361 '(y+1/4, 3/4, -y+3/4)', '(y+1/4, 1/4, -y+1/4)',
8362 '(y, -y, 1/4)', '(y, -y+1/2, 3/4)',
8363 '(y+1/2, -y, 3/4)', '(y+1/2, -y+1/2, 1/4)',
8364 '(-y+3/4, -y+1/2, 0)', '(-y+3/4, -y, 1/2)',
8365 '(-y+1/4, -y+1/2, 1/2)', '(-y+1/4, -y, 0)',
8366 '(y+1/2, y+1/4, 1/2)', '(y+1/2, y+3/4, 0)',
8367 '(y, y+1/4, 0)', '(y, y+3/4, 1/2)',
8368 '(-y+1/4, y+3/4, 3/4)', '(-y+1/4, y+1/4, 1/4)',
8369 '(-y+3/4, y+3/4, 1/4)', '(-y+3/4, y+1/4, 3/4)',
8370 '(3/4, -y, y)', '(3/4, -y+1/2, y+1/2)',
8371 '(1/4, -y, y+1/2)', '(1/4, -y+1/2, y)',
8372 '(0, y+1/4, y+1/2)', '(0, y+3/4, y)',
8373 '(1/2, y+1/4, y)', '(1/2, y+3/4, y+1/2)',
8374 '(1/2, -y+1/2, -y+3/4)', '(1/2, -y, -y+1/4)',
8375 '(0, -y+1/2, -y+1/4)', '(0, -y, -y+3/4)',
8376 '(1/4, y+3/4, -y+1/4)', '(1/4, y+1/4, -y+3/4)',
8377 '(3/4, y+3/4, -y+3/4)', '(3/4, y+1/4, -y+1/4)',
8378 '(y, 3/4, -y)', '(y, 1/4, -y+1/2)',
8379 '(y+1/2, 3/4, -y+1/2)', '(y+1/2, 1/4, -y)',
8380 '(y+1/2, 0, y+1/4)', '(y+1/2, 1/2, y+3/4)',
8381 '(y, 0, y+3/4)', '(y, 1/2, y+1/4)',
8382 '(-y+3/4, 1/2, -y+1/2)', '(-y+3/4, 0, -y)',
8383 '(-y+1/4, 1/2, -y)', '(-y+1/4, 0, -y+1/2)',
8384 '(-y+1/4, 1/4, y+3/4)', '(-y+1/4, 3/4, y+1/4)',
8385 '(-y+3/4, 1/4, y+1/4)', '(-y+3/4, 3/4, y+3/4)',
8386 '(-y, y, 3/4)', '(-y, y+1/2, 1/4)',
8387 '(-y+1/2, y, 1/4)', '(-y+1/2, y+1/2, 3/4)',
8388 '(y+1/4, y+1/2, 0)', '(y+1/4, y, 1/2)',
8389 '(y+3/4, y+1/2, 1/2)', '(y+3/4, y, 0)',
8390 '(-y+1/2, -y+3/4, 1/2)', '(-y+1/2, -y+1/4, 0)',
8391 '(-y, -y+3/4, 0)', '(-y, -y+1/4, 1/2)',
8392 '(y+3/4, -y+1/4, 1/4)', '(y+3/4, -y+3/4, 3/4)',
8393 '(y+1/4, -y+1/4, 3/4)', '(y+1/4, -y+3/4, 1/4)')),
8394 '192h': (7, ('(x, y, z)', '(x, y+1/2, z+1/2)',
8395 '(x+1/2, y, z+1/2)', '(x+1/2, y+1/2, z)',
8396 '(-x+1/4, -y+3/4, z+1/2)', '(-x+1/4, -y+1/4, z)',
8397 '(-x+3/4, -y+3/4, z)', '(-x+3/4, -y+1/4, z+1/2)',
8398 '(-x+3/4, y+1/2, -z+1/4)', '(-x+3/4, y, -z+3/4)',
8399 '(-x+1/4, y+1/2, -z+3/4)', '(-x+1/4, y, -z+1/4)',
8400 '(x+1/2, -y+1/4, -z+3/4)',
8401 '(x+1/2, -y+3/4, -z+1/4)', '(x, -y+1/4, -z+1/4)',
8402 '(x, -y+3/4, -z+3/4)', '(z, x, y)',
8403 '(z, x+1/2, y+1/2)', '(z+1/2, x, y+1/2)',
8404 '(z+1/2, x+1/2, y)', '(z+1/2, -x+1/4, -y+3/4)',
8405 '(z+1/2, -x+3/4, -y+1/4)', '(z, -x+1/4, -y+1/4)',
8406 '(z, -x+3/4, -y+3/4)', '(-z+1/4, -x+3/4, y+1/2)',
8407 '(-z+1/4, -x+1/4, y)', '(-z+3/4, -x+3/4, y)',
8408 '(-z+3/4, -x+1/4, y+1/2)',
8409 '(-z+3/4, x+1/2, -y+1/4)', '(-z+3/4, x, -y+3/4)',
8410 '(-z+1/4, x+1/2, -y+3/4)', '(-z+1/4, x, -y+1/4)',
8411 '(y, z, x)', '(y, z+1/2, x+1/2)',
8412 '(y+1/2, z, x+1/2)', '(y+1/2, z+1/2, x)',
8413 '(-y+3/4, z+1/2, -x+1/4)', '(-y+3/4, z, -x+3/4)',
8414 '(-y+1/4, z+1/2, -x+3/4)', '(-y+1/4, z, -x+1/4)',
8415 '(y+1/2, -z+1/4, -x+3/4)',
8416 '(y+1/2, -z+3/4, -x+1/4)', '(y, -z+1/4, -x+1/4)',
8417 '(y, -z+3/4, -x+3/4)', '(-y+1/4, -z+3/4, x+1/2)',
8418 '(-y+1/4, -z+1/4, x)', '(-y+3/4, -z+3/4, x)',
8419 '(-y+3/4, -z+1/4, x+1/2)', '(y+3/4, x+1/4, -z)',
8420 '(y+3/4, x+3/4, -z+1/2)',
8421 '(y+1/4, x+1/4, -z+1/2)', '(y+1/4, x+3/4, -z)',
8422 '(-y+1/2, -x+1/2, -z+1/2)', '(-y+1/2, -x, -z)',
8423 '(-y, -x+1/2, -z)', '(-y, -x, -z+1/2)',
8424 '(y+1/4, -x, z+3/4)', '(y+1/4, -x+1/2, z+1/4)',
8425 '(y+3/4, -x, z+1/4)', '(y+3/4, -x+1/2, z+3/4)',
8426 '(-y, x+3/4, z+1/4)', '(-y, x+1/4, z+3/4)',
8427 '(-y+1/2, x+3/4, z+3/4)',
8428 '(-y+1/2, x+1/4, z+1/4)', '(x+3/4, z+1/4, -y)',
8429 '(x+3/4, z+3/4, -y+1/2)',
8430 '(x+1/4, z+1/4, -y+1/2)', '(x+1/4, z+3/4, -y)',
8431 '(-x, z+3/4, y+1/4)', '(-x, z+1/4, y+3/4)',
8432 '(-x+1/2, z+3/4, y+3/4)',
8433 '(-x+1/2, z+1/4, y+1/4)',
8434 '(-x+1/2, -z+1/2, -y+1/2)', '(-x+1/2, -z, -y)',
8435 '(-x, -z+1/2, -y)', '(-x, -z, -y+1/2)',
8436 '(x+1/4, -z, y+3/4)', '(x+1/4, -z+1/2, y+1/4)',
8437 '(x+3/4, -z, y+1/4)', '(x+3/4, -z+1/2, y+3/4)',
8438 '(z+3/4, y+1/4, -x)', '(z+3/4, y+3/4, -x+1/2)',
8439 '(z+1/4, y+1/4, -x+1/2)', '(z+1/4, y+3/4, -x)',
8440 '(z+1/4, -y, x+3/4)', '(z+1/4, -y+1/2, x+1/4)',
8441 '(z+3/4, -y, x+1/4)', '(z+3/4, -y+1/2, x+3/4)',
8442 '(-z, y+3/4, x+1/4)', '(-z, y+1/4, x+3/4)',
8443 '(-z+1/2, y+3/4, x+3/4)',
8444 '(-z+1/2, y+1/4, x+1/4)',
8445 '(-z+1/2, -y+1/2, -x+1/2)', '(-z+1/2, -y, -x)',
8446 '(-z, -y+1/2, -x)', '(-z, -y, -x+1/2)',
8447 '(-x, -y, -z)', '(-x, -y+1/2, -z+1/2)',
8448 '(-x+1/2, -y, -z+1/2)', '(-x+1/2, -y+1/2, -z)',
8449 '(x+3/4, y+1/4, -z+1/2)', '(x+3/4, y+3/4, -z)',
8450 '(x+1/4, y+1/4, -z)', '(x+1/4, y+3/4, -z+1/2)',
8451 '(x+1/4, -y+1/2, z+3/4)', '(x+1/4, -y, z+1/4)',
8452 '(x+3/4, -y+1/2, z+1/4)', '(x+3/4, -y, z+3/4)',
8453 '(-x+1/2, y+3/4, z+1/4)',
8454 '(-x+1/2, y+1/4, z+3/4)', '(-x, y+3/4, z+3/4)',
8455 '(-x, y+1/4, z+1/4)', '(-z, -x, -y)',
8456 '(-z, -x+1/2, -y+1/2)', '(-z+1/2, -x, -y+1/2)',
8457 '(-z+1/2, -x+1/2, -y)', '(-z+1/2, x+3/4, y+1/4)',
8458 '(-z+1/2, x+1/4, y+3/4)', '(-z, x+3/4, y+3/4)',
8459 '(-z, x+1/4, y+1/4)', '(z+3/4, x+1/4, -y+1/2)',
8460 '(z+3/4, x+3/4, -y)', '(z+1/4, x+1/4, -y)',
8461 '(z+1/4, x+3/4, -y+1/2)',
8462 '(z+1/4, -x+1/2, y+3/4)', '(z+1/4, -x, y+1/4)',
8463 '(z+3/4, -x+1/2, y+1/4)', '(z+3/4, -x, y+3/4)',
8464 '(-y, -z, -x)', '(-y, -z+1/2, -x+1/2)',
8465 '(-y+1/2, -z, -x+1/2)', '(-y+1/2, -z+1/2, -x)',
8466 '(y+1/4, -z+1/2, x+3/4)', '(y+1/4, -z, x+1/4)',
8467 '(y+3/4, -z+1/2, x+1/4)', '(y+3/4, -z, x+3/4)',
8468 '(-y+1/2, z+3/4, x+1/4)',
8469 '(-y+1/2, z+1/4, x+3/4)', '(-y, z+3/4, x+3/4)',
8470 '(-y, z+1/4, x+1/4)', '(y+3/4, z+1/4, -x+1/2)',
8471 '(y+3/4, z+3/4, -x)', '(y+1/4, z+1/4, -x)',
8472 '(y+1/4, z+3/4, -x+1/2)', '(-y+1/4, -x+3/4, z)',
8473 '(-y+1/4, -x+1/4, z+1/2)',
8474 '(-y+3/4, -x+3/4, z+1/2)', '(-y+3/4, -x+1/4, z)',
8475 '(y+1/2, x+1/2, z+1/2)', '(y+1/2, x, z)',
8476 '(y, x+1/2, z)', '(y, x, z+1/2)',
8477 '(-y+3/4, x, -z+1/4)', '(-y+3/4, x+1/2, -z+3/4)',
8478 '(-y+1/4, x, -z+3/4)', '(-y+1/4, x+1/2, -z+1/4)',
8479 '(y, -x+1/4, -z+3/4)', '(y, -x+3/4, -z+1/4)',
8480 '(y+1/2, -x+1/4, -z+1/4)',
8481 '(y+1/2, -x+3/4, -z+3/4)', '(-x+1/4, -z+3/4, y)',
8482 '(-x+1/4, -z+1/4, y+1/2)',
8483 '(-x+3/4, -z+3/4, y+1/2)', '(-x+3/4, -z+1/4, y)',
8484 '(x, -z+1/4, -y+3/4)', '(x, -z+3/4, -y+1/4)',
8485 '(x+1/2, -z+1/4, -y+1/4)',
8486 '(x+1/2, -z+3/4, -y+3/4)',
8487 '(x+1/2, z+1/2, y+1/2)', '(x+1/2, z, y)',
8488 '(x, z+1/2, y)', '(x, z, y+1/2)',
8489 '(-x+3/4, z, -y+1/4)', '(-x+3/4, z+1/2, -y+3/4)',
8490 '(-x+1/4, z, -y+3/4)', '(-x+1/4, z+1/2, -y+1/4)',
8491 '(-z+1/4, -y+3/4, x)', '(-z+1/4, -y+1/4, x+1/2)',
8492 '(-z+3/4, -y+3/4, x+1/2)', '(-z+3/4, -y+1/4, x)',
8493 '(-z+3/4, y, -x+1/4)', '(-z+3/4, y+1/2, -x+3/4)',
8494 '(-z+1/4, y, -x+3/4)', '(-z+1/4, y+1/2, -x+1/4)',
8495 '(z, -y+1/4, -x+3/4)', '(z, -y+3/4, -x+1/4)',
8496 '(z+1/2, -y+1/4, -x+1/4)',
8497 '(z+1/2, -y+3/4, -x+3/4)',
8498 '(z+1/2, y+1/2, x+1/2)', '(z+1/2, y, x)',
8499 '(z, y+1/2, x)', '(z, y, x+1/2)'))},
8500 '229': {'2a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)')),
8501 '6b': (0, ('(0, 1/2, 1/2)', '(1/2, 0, 0)', '(1/2, 0, 1/2)',
8502 '(0, 1/2, 0)', '(1/2, 1/2, 0)', '(0, 0, 1/2)')),
8503 '8c': (0, ('(1/4, 1/4, 1/4)', '(3/4, 3/4, 3/4)',
8504 '(3/4, 3/4, 1/4)', '(1/4, 1/4, 3/4)',
8505 '(3/4, 1/4, 3/4)', '(1/4, 3/4, 1/4)',
8506 '(1/4, 3/4, 3/4)', '(3/4, 1/4, 1/4)')),
8507 '12d': (0, ('(1/4, 0, 1/2)', '(3/4, 1/2, 0)', '(3/4, 0, 1/2)',
8508 '(1/4, 1/2, 0)', '(1/2, 1/4, 0)', '(0, 3/4, 1/2)',
8509 '(1/2, 3/4, 0)', '(0, 1/4, 1/2)', '(0, 1/2, 1/4)',
8510 '(1/2, 0, 3/4)', '(0, 1/2, 3/4)', '(1/2, 0, 1/4)')),
8511 '12e': (1, ('(x, 0, 0)', '(x+1/2, 1/2, 1/2)', '(-x, 0, 0)',
8512 '(-x+1/2, 1/2, 1/2)', '(0, x, 0)',
8513 '(1/2, x+1/2, 1/2)', '(0, -x, 0)',
8514 '(1/2, -x+1/2, 1/2)', '(0, 0, x)',
8515 '(1/2, 1/2, x+1/2)', '(0, 0, -x)',
8516 '(1/2, 1/2, -x+1/2)')),
8517 '16f': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)', '(-x, -x, x)',
8518 '(-x+1/2, -x+1/2, x+1/2)', '(-x, x, -x)',
8519 '(-x+1/2, x+1/2, -x+1/2)', '(x, -x, -x)',
8520 '(x+1/2, -x+1/2, -x+1/2)', '(x, x, -x)',
8521 '(x+1/2, x+1/2, -x+1/2)', '(-x, -x, -x)',
8522 '(-x+1/2, -x+1/2, -x+1/2)', '(x, -x, x)',
8523 '(x+1/2, -x+1/2, x+1/2)', '(-x, x, x)',
8524 '(-x+1/2, x+1/2, x+1/2)')),
8525 '24g': (1, ('(x, 0, 1/2)', '(x+1/2, 1/2, 0)', '(-x, 0, 1/2)',
8526 '(-x+1/2, 1/2, 0)', '(1/2, x, 0)',
8527 '(0, x+1/2, 1/2)', '(1/2, -x, 0)',
8528 '(0, -x+1/2, 1/2)', '(0, 1/2, x)',
8529 '(1/2, 0, x+1/2)', '(0, 1/2, -x)',
8530 '(1/2, 0, -x+1/2)', '(0, x, 1/2)',
8531 '(1/2, x+1/2, 0)', '(0, -x, 1/2)',
8532 '(1/2, -x+1/2, 0)', '(x, 1/2, 0)',
8533 '(x+1/2, 0, 1/2)', '(-x, 1/2, 0)',
8534 '(-x+1/2, 0, 1/2)', '(1/2, 0, -x)',
8535 '(0, 1/2, -x+1/2)', '(1/2, 0, x)',
8536 '(0, 1/2, x+1/2)')),
8537 '24h': (2, ('(0, y, y)', '(1/2, y+1/2, y+1/2)', '(0, -y, y)',
8538 '(1/2, -y+1/2, y+1/2)', '(0, y, -y)',
8539 '(1/2, y+1/2, -y+1/2)', '(0, -y, -y)',
8540 '(1/2, -y+1/2, -y+1/2)', '(y, 0, y)',
8541 '(y+1/2, 1/2, y+1/2)', '(y, 0, -y)',
8542 '(y+1/2, 1/2, -y+1/2)', '(-y, 0, y)',
8543 '(-y+1/2, 1/2, y+1/2)', '(-y, 0, -y)',
8544 '(-y+1/2, 1/2, -y+1/2)', '(y, y, 0)',
8545 '(y+1/2, y+1/2, 1/2)', '(-y, y, 0)',
8546 '(-y+1/2, y+1/2, 1/2)', '(y, -y, 0)',
8547 '(y+1/2, -y+1/2, 1/2)', '(-y, -y, 0)',
8548 '(-y+1/2, -y+1/2, 1/2)')),
8549 '48i': (2, ('(1/4, y, -y+1/2)', '(3/4, y+1/2, -y)',
8550 '(3/4, -y, -y+1/2)', '(1/4, -y+1/2, -y)',
8551 '(3/4, y, y+1/2)', '(1/4, y+1/2, y)',
8552 '(1/4, -y, y+1/2)', '(3/4, -y+1/2, y)',
8553 '(-y+1/2, 1/4, y)', '(-y, 3/4, y+1/2)',
8554 '(-y+1/2, 3/4, -y)', '(-y, 1/4, -y+1/2)',
8555 '(y+1/2, 3/4, y)', '(y, 1/4, y+1/2)',
8556 '(y+1/2, 1/4, -y)', '(y, 3/4, -y+1/2)',
8557 '(y, -y+1/2, 1/4)', '(y+1/2, -y, 3/4)',
8558 '(-y, -y+1/2, 3/4)', '(-y+1/2, -y, 1/4)',
8559 '(y, y+1/2, 3/4)', '(y+1/2, y, 1/4)',
8560 '(-y, y+1/2, 1/4)', '(-y+1/2, y, 3/4)',
8561 '(3/4, -y, y+1/2)', '(1/4, -y+1/2, y)',
8562 '(1/4, y, y+1/2)', '(3/4, y+1/2, y)',
8563 '(1/4, -y, -y+1/2)', '(3/4, -y+1/2, -y)',
8564 '(3/4, y, -y+1/2)', '(1/4, y+1/2, -y)',
8565 '(y+1/2, 3/4, -y)', '(y, 1/4, -y+1/2)',
8566 '(y+1/2, 1/4, y)', '(y, 3/4, y+1/2)',
8567 '(-y+1/2, 1/4, -y)', '(-y, 3/4, -y+1/2)',
8568 '(-y+1/2, 3/4, y)', '(-y, 1/4, y+1/2)',
8569 '(-y, y+1/2, 3/4)', '(-y+1/2, y, 1/4)',
8570 '(y, y+1/2, 1/4)', '(y+1/2, y, 3/4)',
8571 '(-y, -y+1/2, 1/4)', '(-y+1/2, -y, 3/4)',
8572 '(y, -y+1/2, 3/4)', '(y+1/2, -y, 1/4)')),
8573 '48j': (6, ('(0, y, z)', '(1/2, y+1/2, z+1/2)', '(0, -y, z)',
8574 '(1/2, -y+1/2, z+1/2)', '(0, y, -z)',
8575 '(1/2, y+1/2, -z+1/2)', '(0, -y, -z)',
8576 '(1/2, -y+1/2, -z+1/2)', '(z, 0, y)',
8577 '(z+1/2, 1/2, y+1/2)', '(z, 0, -y)',
8578 '(z+1/2, 1/2, -y+1/2)', '(-z, 0, y)',
8579 '(-z+1/2, 1/2, y+1/2)', '(-z, 0, -y)',
8580 '(-z+1/2, 1/2, -y+1/2)', '(y, z, 0)',
8581 '(y+1/2, z+1/2, 1/2)', '(-y, z, 0)',
8582 '(-y+1/2, z+1/2, 1/2)', '(y, -z, 0)',
8583 '(y+1/2, -z+1/2, 1/2)', '(-y, -z, 0)',
8584 '(-y+1/2, -z+1/2, 1/2)', '(y, 0, -z)',
8585 '(y+1/2, 1/2, -z+1/2)', '(-y, 0, -z)',
8586 '(-y+1/2, 1/2, -z+1/2)', '(y, 0, z)',
8587 '(y+1/2, 1/2, z+1/2)', '(-y, 0, z)',
8588 '(-y+1/2, 1/2, z+1/2)', '(0, z, -y)',
8589 '(1/2, z+1/2, -y+1/2)', '(0, z, y)',
8590 '(1/2, z+1/2, y+1/2)', '(0, -z, -y)',
8591 '(1/2, -z+1/2, -y+1/2)', '(0, -z, y)',
8592 '(1/2, -z+1/2, y+1/2)', '(z, y, 0)',
8593 '(z+1/2, y+1/2, 1/2)', '(z, -y, 0)',
8594 '(z+1/2, -y+1/2, 1/2)', '(-z, y, 0)',
8595 '(-z+1/2, y+1/2, 1/2)', '(-z, -y, 0)',
8596 '(-z+1/2, -y+1/2, 1/2)')),
8597 '48k': (5, ('(x, x, z)', '(x+1/2, x+1/2, z+1/2)', '(-x, -x, z)',
8598 '(-x+1/2, -x+1/2, z+1/2)', '(-x, x, -z)',
8599 '(-x+1/2, x+1/2, -z+1/2)', '(x, -x, -z)',
8600 '(x+1/2, -x+1/2, -z+1/2)', '(z, x, x)',
8601 '(z+1/2, x+1/2, x+1/2)', '(z, -x, -x)',
8602 '(z+1/2, -x+1/2, -x+1/2)', '(-z, -x, x)',
8603 '(-z+1/2, -x+1/2, x+1/2)', '(-z, x, -x)',
8604 '(-z+1/2, x+1/2, -x+1/2)', '(x, z, x)',
8605 '(x+1/2, z+1/2, x+1/2)', '(-x, z, -x)',
8606 '(-x+1/2, z+1/2, -x+1/2)', '(x, -z, -x)',
8607 '(x+1/2, -z+1/2, -x+1/2)', '(-x, -z, x)',
8608 '(-x+1/2, -z+1/2, x+1/2)', '(x, x, -z)',
8609 '(x+1/2, x+1/2, -z+1/2)', '(-x, -x, -z)',
8610 '(-x+1/2, -x+1/2, -z+1/2)', '(x, -x, z)',
8611 '(x+1/2, -x+1/2, z+1/2)', '(-x, x, z)',
8612 '(-x+1/2, x+1/2, z+1/2)', '(x, z, -x)',
8613 '(x+1/2, z+1/2, -x+1/2)', '(-x, z, x)',
8614 '(-x+1/2, z+1/2, x+1/2)', '(-x, -z, -x)',
8615 '(-x+1/2, -z+1/2, -x+1/2)', '(x, -z, x)',
8616 '(x+1/2, -z+1/2, x+1/2)', '(z, x, -x)',
8617 '(z+1/2, x+1/2, -x+1/2)', '(z, -x, x)',
8618 '(z+1/2, -x+1/2, x+1/2)', '(-z, x, x)',
8619 '(-z+1/2, x+1/2, x+1/2)', '(-z, -x, -x)',
8620 '(-z+1/2, -x+1/2, -x+1/2)')),
8621 '96l': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)', '(-x, -y, z)',
8622 '(-x+1/2, -y+1/2, z+1/2)', '(-x, y, -z)',
8623 '(-x+1/2, y+1/2, -z+1/2)', '(x, -y, -z)',
8624 '(x+1/2, -y+1/2, -z+1/2)', '(z, x, y)',
8625 '(z+1/2, x+1/2, y+1/2)', '(z, -x, -y)',
8626 '(z+1/2, -x+1/2, -y+1/2)', '(-z, -x, y)',
8627 '(-z+1/2, -x+1/2, y+1/2)', '(-z, x, -y)',
8628 '(-z+1/2, x+1/2, -y+1/2)', '(y, z, x)',
8629 '(y+1/2, z+1/2, x+1/2)', '(-y, z, -x)',
8630 '(-y+1/2, z+1/2, -x+1/2)', '(y, -z, -x)',
8631 '(y+1/2, -z+1/2, -x+1/2)', '(-y, -z, x)',
8632 '(-y+1/2, -z+1/2, x+1/2)', '(y, x, -z)',
8633 '(y+1/2, x+1/2, -z+1/2)', '(-y, -x, -z)',
8634 '(-y+1/2, -x+1/2, -z+1/2)', '(y, -x, z)',
8635 '(y+1/2, -x+1/2, z+1/2)', '(-y, x, z)',
8636 '(-y+1/2, x+1/2, z+1/2)', '(x, z, -y)',
8637 '(x+1/2, z+1/2, -y+1/2)', '(-x, z, y)',
8638 '(-x+1/2, z+1/2, y+1/2)', '(-x, -z, -y)',
8639 '(-x+1/2, -z+1/2, -y+1/2)', '(x, -z, y)',
8640 '(x+1/2, -z+1/2, y+1/2)', '(z, y, -x)',
8641 '(z+1/2, y+1/2, -x+1/2)', '(z, -y, x)',
8642 '(z+1/2, -y+1/2, x+1/2)', '(-z, y, x)',
8643 '(-z+1/2, y+1/2, x+1/2)', '(-z, -y, -x)',
8644 '(-z+1/2, -y+1/2, -x+1/2)', '(-x, -y, -z)',
8645 '(-x+1/2, -y+1/2, -z+1/2)', '(x, y, -z)',
8646 '(x+1/2, y+1/2, -z+1/2)', '(x, -y, z)',
8647 '(x+1/2, -y+1/2, z+1/2)', '(-x, y, z)',
8648 '(-x+1/2, y+1/2, z+1/2)', '(-z, -x, -y)',
8649 '(-z+1/2, -x+1/2, -y+1/2)', '(-z, x, y)',
8650 '(-z+1/2, x+1/2, y+1/2)', '(z, x, -y)',
8651 '(z+1/2, x+1/2, -y+1/2)', '(z, -x, y)',
8652 '(z+1/2, -x+1/2, y+1/2)', '(-y, -z, -x)',
8653 '(-y+1/2, -z+1/2, -x+1/2)', '(y, -z, x)',
8654 '(y+1/2, -z+1/2, x+1/2)', '(-y, z, x)',
8655 '(-y+1/2, z+1/2, x+1/2)', '(y, z, -x)',
8656 '(y+1/2, z+1/2, -x+1/2)', '(-y, -x, z)',
8657 '(-y+1/2, -x+1/2, z+1/2)', '(y, x, z)',
8658 '(y+1/2, x+1/2, z+1/2)', '(-y, x, -z)',
8659 '(-y+1/2, x+1/2, -z+1/2)', '(y, -x, -z)',
8660 '(y+1/2, -x+1/2, -z+1/2)', '(-x, -z, y)',
8661 '(-x+1/2, -z+1/2, y+1/2)', '(x, -z, -y)',
8662 '(x+1/2, -z+1/2, -y+1/2)', '(x, z, y)',
8663 '(x+1/2, z+1/2, y+1/2)', '(-x, z, -y)',
8664 '(-x+1/2, z+1/2, -y+1/2)', '(-z, -y, x)',
8665 '(-z+1/2, -y+1/2, x+1/2)', '(-z, y, -x)',
8666 '(-z+1/2, y+1/2, -x+1/2)', '(z, -y, -x)',
8667 '(z+1/2, -y+1/2, -x+1/2)', '(z, y, x)',
8668 '(z+1/2, y+1/2, x+1/2)'))},
8669 '230': {'16a': (0, ('(0, 0, 0)', '(1/2, 1/2, 1/2)', '(1/2, 0, 1/2)',
8670 '(0, 1/2, 0)', '(0, 1/2, 1/2)', '(1/2, 0, 0)',
8671 '(1/2, 1/2, 0)', '(0, 0, 1/2)', '(3/4, 1/4, 1/4)',
8672 '(1/4, 3/4, 3/4)', '(3/4, 3/4, 3/4)',
8673 '(1/4, 1/4, 1/4)', '(1/4, 1/4, 3/4)',
8674 '(3/4, 3/4, 1/4)', '(1/4, 3/4, 1/4)',
8675 '(3/4, 1/4, 3/4)')),
8676 '16b': (0, ('(1/8, 1/8, 1/8)', '(5/8, 5/8, 5/8)',
8677 '(3/8, 7/8, 5/8)', '(7/8, 3/8, 1/8)',
8678 '(7/8, 5/8, 3/8)', '(3/8, 1/8, 7/8)',
8679 '(5/8, 3/8, 7/8)', '(1/8, 7/8, 3/8)',
8680 '(7/8, 7/8, 7/8)', '(3/8, 3/8, 3/8)',
8681 '(5/8, 1/8, 3/8)', '(1/8, 5/8, 7/8)',
8682 '(1/8, 3/8, 5/8)', '(5/8, 7/8, 1/8)',
8683 '(3/8, 5/8, 1/8)', '(7/8, 1/8, 5/8)')),
8684 '24c': (0, ('(1/8, 0, 1/4)', '(5/8, 1/2, 3/4)', '(3/8, 0, 3/4)',
8685 '(7/8, 1/2, 1/4)', '(1/4, 1/8, 0)',
8686 '(3/4, 5/8, 1/2)', '(3/4, 3/8, 0)',
8687 '(1/4, 7/8, 1/2)', '(0, 1/4, 1/8)',
8688 '(1/2, 3/4, 5/8)', '(0, 3/4, 3/8)',
8689 '(1/2, 1/4, 7/8)', '(7/8, 0, 3/4)',
8690 '(3/8, 1/2, 1/4)', '(5/8, 0, 1/4)',
8691 '(1/8, 1/2, 3/4)', '(3/4, 7/8, 0)',
8692 '(1/4, 3/8, 1/2)', '(1/4, 5/8, 0)',
8693 '(3/4, 1/8, 1/2)', '(0, 3/4, 7/8)',
8694 '(1/2, 1/4, 3/8)', '(0, 1/4, 5/8)',
8695 '(1/2, 3/4, 1/8)')),
8696 '24d': (0, ('(3/8, 0, 1/4)', '(7/8, 1/2, 3/4)', '(1/8, 0, 3/4)',
8697 '(5/8, 1/2, 1/4)', '(1/4, 3/8, 0)',
8698 '(3/4, 7/8, 1/2)', '(3/4, 1/8, 0)',
8699 '(1/4, 5/8, 1/2)', '(0, 1/4, 3/8)',
8700 '(1/2, 3/4, 7/8)', '(0, 3/4, 1/8)',
8701 '(1/2, 1/4, 5/8)', '(3/4, 5/8, 0)',
8702 '(1/4, 1/8, 1/2)', '(3/4, 3/8, 1/2)',
8703 '(1/4, 7/8, 0)', '(1/8, 1/2, 1/4)', '(5/8, 0, 3/4)',
8704 '(7/8, 0, 1/4)', '(3/8, 1/2, 3/4)', '(0, 1/4, 7/8)',
8705 '(1/2, 3/4, 3/8)', '(1/2, 1/4, 1/8)',
8706 '(0, 3/4, 5/8)')),
8707 '32e': (1, ('(x, x, x)', '(x+1/2, x+1/2, x+1/2)',
8708 '(-x+1/2, -x, x+1/2)', '(-x, -x+1/2, x)',
8709 '(-x, x+1/2, -x+1/2)', '(-x+1/2, x, -x)',
8710 '(x+1/2, -x+1/2, -x)', '(x, -x, -x+1/2)',
8711 '(x+3/4, x+1/4, -x+1/4)', '(x+1/4, x+3/4, -x+3/4)',
8712 '(-x+3/4, -x+3/4, -x+3/4)',
8713 '(-x+1/4, -x+1/4, -x+1/4)',
8714 '(x+1/4, -x+1/4, x+3/4)', '(x+3/4, -x+3/4, x+1/4)',
8715 '(-x+1/4, x+3/4, x+1/4)', '(-x+3/4, x+1/4, x+3/4)',
8716 '(-x, -x, -x)', '(-x+1/2, -x+1/2, -x+1/2)',
8717 '(x+1/2, x, -x+1/2)', '(x, x+1/2, -x)',
8718 '(x, -x+1/2, x+1/2)', '(x+1/2, -x, x)',
8719 '(-x+1/2, x+1/2, x)', '(-x, x, x+1/2)',
8720 '(-x+1/4, -x+3/4, x+3/4)',
8721 '(-x+3/4, -x+1/4, x+1/4)', '(x+1/4, x+1/4, x+1/4)',
8722 '(x+3/4, x+3/4, x+3/4)', '(-x+3/4, x+3/4, -x+1/4)',
8723 '(-x+1/4, x+1/4, -x+3/4)',
8724 '(x+3/4, -x+1/4, -x+3/4)',
8725 '(x+1/4, -x+3/4, -x+1/4)')),
8726 '48f': (1, ('(x, 0, 1/4)', '(x+1/2, 1/2, 3/4)',
8727 '(-x+1/2, 0, 3/4)', '(-x, 1/2, 1/4)', '(1/4, x, 0)',
8728 '(3/4, x+1/2, 1/2)', '(3/4, -x+1/2, 0)',
8729 '(1/4, -x, 1/2)', '(0, 1/4, x)',
8730 '(1/2, 3/4, x+1/2)', '(0, 3/4, -x+1/2)',
8731 '(1/2, 1/4, -x)', '(3/4, x+1/4, 0)',
8732 '(1/4, x+3/4, 1/2)', '(3/4, -x+3/4, 1/2)',
8733 '(1/4, -x+1/4, 0)', '(x+3/4, 1/2, 1/4)',
8734 '(x+1/4, 0, 3/4)', '(-x+1/4, 0, 1/4)',
8735 '(-x+3/4, 1/2, 3/4)', '(0, 1/4, -x+1/4)',
8736 '(1/2, 3/4, -x+3/4)', '(1/2, 1/4, x+3/4)',
8737 '(0, 3/4, x+1/4)', '(-x, 0, 3/4)',
8738 '(-x+1/2, 1/2, 1/4)', '(x+1/2, 0, 1/4)',
8739 '(x, 1/2, 3/4)', '(3/4, -x, 0)',
8740 '(1/4, -x+1/2, 1/2)', '(1/4, x+1/2, 0)',
8741 '(3/4, x, 1/2)', '(0, 3/4, -x)',
8742 '(1/2, 1/4, -x+1/2)', '(0, 1/4, x+1/2)',
8743 '(1/2, 3/4, x)', '(1/4, -x+3/4, 0)',
8744 '(3/4, -x+1/4, 1/2)', '(1/4, x+1/4, 1/2)',
8745 '(3/4, x+3/4, 0)', '(-x+1/4, 1/2, 3/4)',
8746 '(-x+3/4, 0, 1/4)', '(x+3/4, 0, 3/4)',
8747 '(x+1/4, 1/2, 1/4)', '(0, 3/4, x+3/4)',
8748 '(1/2, 1/4, x+1/4)', '(1/2, 3/4, -x+1/4)',
8749 '(0, 1/4, -x+3/4)')),
8750 '48g': (2, ('(1/8, y, -y+1/4)', '(5/8, y+1/2, -y+3/4)',
8751 '(3/8, -y, -y+3/4)', '(7/8, -y+1/2, -y+1/4)',
8752 '(7/8, y+1/2, y+1/4)', '(3/8, y, y+3/4)',
8753 '(5/8, -y+1/2, y+3/4)', '(1/8, -y, y+1/4)',
8754 '(-y+1/4, 1/8, y)', '(-y+3/4, 5/8, y+1/2)',
8755 '(-y+3/4, 3/8, -y)', '(-y+1/4, 7/8, -y+1/2)',
8756 '(y+1/4, 7/8, y+1/2)', '(y+3/4, 3/8, y)',
8757 '(y+3/4, 5/8, -y+1/2)', '(y+1/4, 1/8, -y)',
8758 '(y, -y+1/4, 1/8)', '(y+1/2, -y+3/4, 5/8)',
8759 '(-y, -y+3/4, 3/8)', '(-y+1/2, -y+1/4, 7/8)',
8760 '(y+1/2, y+1/4, 7/8)', '(y, y+3/4, 3/8)',
8761 '(-y+1/2, y+3/4, 5/8)', '(-y, y+1/4, 1/8)',
8762 '(7/8, -y, y+3/4)', '(3/8, -y+1/2, y+1/4)',
8763 '(5/8, y, y+1/4)', '(1/8, y+1/2, y+3/4)',
8764 '(1/8, -y+1/2, -y+3/4)', '(5/8, -y, -y+1/4)',
8765 '(3/8, y+1/2, -y+1/4)', '(7/8, y, -y+3/4)',
8766 '(y+3/4, 7/8, -y)', '(y+1/4, 3/8, -y+1/2)',
8767 '(y+1/4, 5/8, y)', '(y+3/4, 1/8, y+1/2)',
8768 '(-y+3/4, 1/8, -y+1/2)', '(-y+1/4, 5/8, -y)',
8769 '(-y+1/4, 3/8, y+1/2)', '(-y+3/4, 7/8, y)',
8770 '(-y, y+3/4, 7/8)', '(-y+1/2, y+1/4, 3/8)',
8771 '(y, y+1/4, 5/8)', '(y+1/2, y+3/4, 1/8)',
8772 '(-y+1/2, -y+3/4, 1/8)', '(-y, -y+1/4, 5/8)',
8773 '(y+1/2, -y+1/4, 3/8)', '(y, -y+3/4, 7/8)')),
8774 '96h': (7, ('(x, y, z)', '(x+1/2, y+1/2, z+1/2)',
8775 '(-x+1/2, -y, z+1/2)', '(-x, -y+1/2, z)',
8776 '(-x, y+1/2, -z+1/2)', '(-x+1/2, y, -z)',
8777 '(x+1/2, -y+1/2, -z)', '(x, -y, -z+1/2)',
8778 '(z, x, y)', '(z+1/2, x+1/2, y+1/2)',
8779 '(z+1/2, -x+1/2, -y)', '(z, -x, -y+1/2)',
8780 '(-z+1/2, -x, y+1/2)', '(-z, -x+1/2, y)',
8781 '(-z, x+1/2, -y+1/2)', '(-z+1/2, x, -y)',
8782 '(y, z, x)', '(y+1/2, z+1/2, x+1/2)',
8783 '(-y, z+1/2, -x+1/2)', '(-y+1/2, z, -x)',
8784 '(y+1/2, -z+1/2, -x)', '(y, -z, -x+1/2)',
8785 '(-y+1/2, -z, x+1/2)', '(-y, -z+1/2, x)',
8786 '(y+3/4, x+1/4, -z+1/4)', '(y+1/4, x+3/4, -z+3/4)',
8787 '(-y+3/4, -x+3/4, -z+3/4)',
8788 '(-y+1/4, -x+1/4, -z+1/4)',
8789 '(y+1/4, -x+1/4, z+3/4)', '(y+3/4, -x+3/4, z+1/4)',
8790 '(-y+1/4, x+3/4, z+1/4)', '(-y+3/4, x+1/4, z+3/4)',
8791 '(x+3/4, z+1/4, -y+1/4)', '(x+1/4, z+3/4, -y+3/4)',
8792 '(-x+1/4, z+3/4, y+1/4)', '(-x+3/4, z+1/4, y+3/4)',
8793 '(-x+3/4, -z+3/4, -y+3/4)',
8794 '(-x+1/4, -z+1/4, -y+1/4)',
8795 '(x+1/4, -z+1/4, y+3/4)', '(x+3/4, -z+3/4, y+1/4)',
8796 '(z+3/4, y+1/4, -x+1/4)', '(z+1/4, y+3/4, -x+3/4)',
8797 '(z+1/4, -y+1/4, x+3/4)', '(z+3/4, -y+3/4, x+1/4)',
8798 '(-z+1/4, y+3/4, x+1/4)', '(-z+3/4, y+1/4, x+3/4)',
8799 '(-z+3/4, -y+3/4, -x+3/4)',
8800 '(-z+1/4, -y+1/4, -x+1/4)', '(-x, -y, -z)',
8801 '(-x+1/2, -y+1/2, -z+1/2)', '(x+1/2, y, -z+1/2)',
8802 '(x, y+1/2, -z)', '(x, -y+1/2, z+1/2)',
8803 '(x+1/2, -y, z)', '(-x+1/2, y+1/2, z)',
8804 '(-x, y, z+1/2)', '(-z, -x, -y)',
8805 '(-z+1/2, -x+1/2, -y+1/2)', '(-z+1/2, x+1/2, y)',
8806 '(-z, x, y+1/2)', '(z+1/2, x, -y+1/2)',
8807 '(z, x+1/2, -y)', '(z, -x+1/2, y+1/2)',
8808 '(z+1/2, -x, y)', '(-y, -z, -x)',
8809 '(-y+1/2, -z+1/2, -x+1/2)', '(y, -z+1/2, x+1/2)',
8810 '(y+1/2, -z, x)', '(-y+1/2, z+1/2, x)',
8811 '(-y, z, x+1/2)', '(y+1/2, z, -x+1/2)',
8812 '(y, z+1/2, -x)', '(-y+1/4, -x+3/4, z+3/4)',
8813 '(-y+3/4, -x+1/4, z+1/4)', '(y+1/4, x+1/4, z+1/4)',
8814 '(y+3/4, x+3/4, z+3/4)', '(-y+3/4, x+3/4, -z+1/4)',
8815 '(-y+1/4, x+1/4, -z+3/4)',
8816 '(y+3/4, -x+1/4, -z+3/4)',
8817 '(y+1/4, -x+3/4, -z+1/4)',
8818 '(-x+1/4, -z+3/4, y+3/4)',
8819 '(-x+3/4, -z+1/4, y+1/4)',
8820 '(x+3/4, -z+1/4, -y+3/4)',
8821 '(x+1/4, -z+3/4, -y+1/4)', '(x+1/4, z+1/4, y+1/4)',
8822 '(x+3/4, z+3/4, y+3/4)', '(-x+3/4, z+3/4, -y+1/4)',
8823 '(-x+1/4, z+1/4, -y+3/4)',
8824 '(-z+1/4, -y+3/4, x+3/4)',
8825 '(-z+3/4, -y+1/4, x+1/4)',
8826 '(-z+3/4, y+3/4, -x+1/4)',
8827 '(-z+1/4, y+1/4, -x+3/4)',
8828 '(z+3/4, -y+1/4, -x+3/4)',
8829 '(z+1/4, -y+3/4, -x+1/4)', '(z+1/4, y+1/4, x+1/4)',
8830 '(z+3/4, y+3/4, x+3/4)'))},
8831 }
+0
-34
xrayutilities/math/__init__.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2010-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 from .algebra import solve_quartic
19 from .fit import (fit_peak2d, gauss_fit, linregress, multGaussFit,
20 multGaussPlot, multPeakFit, multPeakPlot, peak_fit)
21 from .functions import (Debye1, Gauss1d, Gauss1d_der_p, Gauss1d_der_x,
22 Gauss1dArea, Gauss2d, Gauss2dArea, Gauss3d, Lorentz1d,
23 Lorentz1d_der_p, Lorentz1d_der_x, Lorentz1dArea,
24 Lorentz2d, NormGauss1d, NormLorentz1d, PseudoVoigt1d,
25 PseudoVoigt1dArea, PseudoVoigt1dasym,
26 PseudoVoigt1dasym2, PseudoVoigt2d, TwoGauss2d,
27 heaviside, kill_spike, multPeak1d, multPeak2d, smooth)
28 from .misc import center_of_mass, fwhm_exp, gcd
29 from .transforms import (ArbRotation, AxisToZ, AxisToZ_keepXY,
30 CoordinateTransform, Transform, XRotation, YRotation,
31 ZRotation, rotarb)
32 from .vector import (VecAngle, VecCross, VecDot, VecNorm, VecUnit, distance,
33 getSyntax, getVector)
+0
-118
xrayutilities/math/algebra.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module providing analytic algebraic functions not implemented in scipy or any
19 other dependency of xrayutilities. In particular the analytic solution of a
20 quartic equation which is needed for the solution of the dynamic scattering
21 equations.
22 """
23
24 import numpy
25
26
27 def solve_quartic(a4, a3, a2, a1, a0):
28 """
29 analytic solution [1]_ of the general quartic equation. The solved equation
30 takes the form :math:`a_4 z^4 + a_3 z^3 + a_2 z^2 + a_1 z + a_0`
31
32 Returns
33 -------
34 tuple
35 tuple of the four (complex) solutions of aboves equation.
36
37 References
38 ----------
39 .. [1] http://mathworld.wolfram.com/QuarticEquation.html
40 """
41 a4 = numpy.asarray(a4)
42 a3 = numpy.asarray(a3)
43 a2 = numpy.asarray(a2)
44 a1 = numpy.asarray(a1)
45 a0 = numpy.asarray(a0)
46
47 t01 = 1. / a4
48 t02 = a3**2
49 t04 = t01 * t01
50 t05 = t04 * t01
51 t09 = a1 * t01
52 t10 = (a3 * t02 * t05) / 8
53 t11 = (a3 * a2 * t04) / 2
54 t03 = t09 + t10 - t11
55 t06 = a2 * t01
56 t19 = (3 * t02 * t04) / 8
57 t07 = t06 - t19
58 t08 = t07**2
59 t12 = t03**2
60 t13 = a0 * t01
61 t14 = t05 * t01
62 t15 = t02**2
63 t16 = (a2 * t02 * t05) / 16
64 t20 = (3 * t14 * t15) / 256
65 t21 = (a3 * a1 * t04) / 4
66 t17 = t13 + t16 - t20 - t21
67 t18 = t17**2
68 t22 = t12 / 2.
69 t23 = (t07 * t08) / 27
70 t24 = 3.**(1./2)
71 t25 = t12**2
72 t26 = 27 * t25
73 t27 = t08**2
74 t28 = 4 * t07 * t08 * t12
75 t29 = 128 * t08 * t18
76 t35 = 256 * t17 * t18
77 t36 = 16 * t17 * t27
78 t37 = 144 * t07 * t12 * t17
79 t30 = t26 + t28 + t29 - t35 - t36 - t37
80 t31 = t30.astype(numpy.complex)**(1./2)
81 t32 = (t24 * t31) / 18
82 t34 = (4 * t07 * t17) / 3
83 t33 = t22 + t23 + t32 - t34
84 t38 = 1 / t33.astype(numpy.complex)**(1./6)
85 t39 = t33**(1./3)
86 t40 = 12 * a0 * t01
87 t41 = t33**(2./3)
88 t42 = 9 * t41
89 t43 = (3 * a2 * t02 * t05) / 4
90 t46 = 6 * t07 * t39
91 t47 = (9 * t14 * t15) / 64
92 t48 = 3 * a3 * a1 * t04
93 t44 = t08 + t40 + t42 + t43 - t46 - t47 - t48
94 t45 = t44.astype(numpy.complex)**(1./2)
95 t49 = 6.**(1./2)
96 t50 = 27 * t12
97 t51 = 2 * t07 * t08
98 t52 = 3 * t24 * t31
99 t62 = 72 * t07 * t17
100 t53 = t50 + t51 + t52 - t62
101 t54 = t53.astype(numpy.complex)**(1./2)
102 t55 = 3 * t03 * t49 * t54
103 t59 = t08 * t45
104 t60 = 12 * t17 * t45
105 t61 = 9 * t41 * t45
106 t63 = 12 * t07 * t39 * t45
107 t56 = t55 - t59 - t60 - t61 - t63
108 t57 = t56.astype(numpy.complex)**(1./2)
109 t58 = 1. / t44.astype(numpy.complex)**(1./4)
110 t64 = (t38 * t45) / 6
111 t65 = - t55 - t59 - t60 - t61 - t63
112 t66 = t65.astype(numpy.complex)**(1./2)
113
114 return (-(a3 * t01) / 4 - (t38 * t45) / 6 - (t38 * t57 * t58) / 6,
115 (t38 * t57 * t58) / 6 - (t38 * t45) / 6 - (a3 * t01) / 4,
116 t64 - (a3 * t01) / 4 - (t38 * t58 * t66) / 6,
117 t64 - (a3 * t01) / 4 + (t38 * t58 * t66) / 6)
+0
-734
xrayutilities/math/fit.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2012-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16 """
17 module with a function wrapper to scipy.optimize.leastsq
18 for fitting of a 2D function to a peak or a 1D Gauss fit with
19 the odr package
20 """
21
22 from __future__ import print_function
23
24 import time
25
26 import numpy
27 import scipy.optimize as optimize
28 from scipy.odr import odrpack as odr
29
30 from .. import config, utilities
31 from ..exception import InputError
32 from .functions import (Gauss1d, Gauss1d_der_p, Gauss1d_der_x, Lorentz1d,
33 Lorentz1d_der_p, Lorentz1d_der_x, PseudoVoigt1d,
34 PseudoVoigt1d_der_p, PseudoVoigt1d_der_x,
35 PseudoVoigt1dasym, PseudoVoigt1dasym2)
36 from .misc import center_of_mass, fwhm_exp
37
38 # python 2to3 compatibility
39 try:
40 basestring
41 except NameError:
42 basestring = str
43
44
45 def linregress(x, y):
46 """
47 fast linregress to avoid usage of scipy.stats which is slow!
48 NaN values in y are ignored by this function.
49
50 Parameters
51 ----------
52 x, y : array-like
53 data coordinates and values
54
55 Returns
56 -------
57 p : tuple
58 parameters of the linear fit (slope, offset)
59 rsq: float
60 R^2 value
61
62 Examples
63 --------
64 >>> (k, d), R2 = xu.math.linregress(x, y)
65 """
66 mask = numpy.logical_not(numpy.isnan(y))
67 lx, ly = (x[mask], y[mask])
68 if numpy.all(numpy.isclose(lx-lx[0], numpy.zeros_like(lx))):
69 return (0, numpy.mean(ly)), 0
70 else:
71 p = numpy.polyfit(lx, ly, 1)
72
73 # calculation of r-squared
74 f = numpy.polyval(p, lx)
75 fbar = numpy.sum(ly) / len(ly)
76 ssreg = numpy.sum((f-fbar)**2)
77 sstot = numpy.sum((ly - fbar)**2)
78 rsq = ssreg / sstot
79
80 return p, rsq
81
82
83 def peak_fit(xdata, ydata, iparams=[], peaktype='Gauss', maxit=300,
84 background='constant', plot=False, func_out=False, debug=False):
85 """
86 fit function using odr-pack wrapper in scipy similar to
87 https://github.com/tiagopereira/python_tips/wiki/Scipy%3A-curve-fitting
88 for Gauss, Lorentz or Pseudovoigt-functions
89
90 Parameters
91 ----------
92 xdata : array_like
93 x-coordinates of the data to be fitted
94 ydata : array_like
95 y-coordinates of the data which should be fit
96
97 iparams : list, optional
98 initial paramters, determined automatically if not specified
99 peaktype : {'Gauss', 'Lorentz', 'PseudoVoigt', 'PseudoVoigtAsym', 'PseudoVoigtAsym2'}, optional
100 type of peak to fit
101 maxit : int, optional
102 maximal iteration number of the fit
103 background : {'constant', 'linear'}, optional
104 type of background function
105 plot : bool or str, optional
106 flag to ask for a plot to visually judge the fit. If plot is a string
107 it will be used as figure name, which makes reusing the figures easier.
108 func_out : bool, optional
109 returns the fitted function, which takes the independent variables as
110 only argument (f(x))
111
112 Returns
113 -------
114 params : list
115 the parameters as defined in function `Gauss1d/Lorentz1d/PseudoVoigt1d/
116 PseudoVoigt1dasym`. In the case of linear background one more parameter
117 is included!
118 sd_params : list
119 For every parameter the corresponding errors are returned.
120 itlim : bool
121 flag to tell if the iteration limit was reached, should be False
122 fitfunc : function, optional
123 the function used in the fit can be returned (see func_out).
124 """
125 if plot:
126 plot, plt = utilities.import_matplotlib_pyplot('XU.math.peak_fit')
127
128 gfunc, gfunc_dx, gfunc_dp = _getfit_func(peaktype, background)
129
130 # determine initial parameters
131 _check_iparams(iparams, peaktype, background)
132 if not any(iparams):
133 iparams = _guess_iparams(xdata, ydata, peaktype, background)
134 if config.VERBOSITY >= config.DEBUG:
135 print("XU.math.peak_fit: iparams: %s" % str(tuple(iparams)))
136
137 # set up odr fitting
138 peak = odr.Model(gfunc, fjacd=gfunc_dx, fjacb=gfunc_dp)
139
140 sy = numpy.sqrt(ydata)
141 sy[sy == 0] = 1
142 mydata = odr.RealData(xdata, ydata, sy=sy)
143
144 myodr = odr.ODR(mydata, peak, beta0=iparams, maxit=maxit)
145 myodr.set_job(fit_type=2) # use least-square fit
146
147 fit = myodr.run()
148 if config.VERBOSITY >= config.DEBUG:
149 print('XU.math.peak_fit:')
150 fit.pprint() # prints final message from odrpack
151
152 fparam = fit.beta
153 etaidx = []
154 if peaktype in ('PseudoVoigt', 'PseudoVoigtAsym'):
155 if background == 'linear':
156 etaidx = [-2, ]
157 else:
158 etaidx = [-1, ]
159 elif peaktype == 'PseudoVoigtAsym2':
160 etaidx = [5, 6]
161 for e in etaidx:
162 fparam[e] = 0 if fparam[e] < 0 else fparam[e]
163 fparam[e] = 1 if fparam[e] > 1 else fparam[e]
164
165 itlim = False
166 if fit.stopreason[0] == 'Iteration limit reached':
167 itlim = True
168 if config.VERBOSITY >= config.INFO_LOW:
169 print("XU.math.peak_fit: Iteration limit reached, "
170 "do not trust the result!")
171
172 if plot:
173 if isinstance(plot, basestring):
174 plt.figure(plot)
175 else:
176 plt.figure('XU:peak_fit')
177 plt.plot(xdata, ydata, 'ko', label='data', mew=2)
178 if debug:
179 plt.plot(xdata, gfunc(iparams, xdata), '-', color='0.5',
180 label='estimate')
181 plt.plot(xdata, gfunc(fparam, xdata), 'r-',
182 label='%s-fit' % peaktype)
183 plt.legend()
184
185 if func_out:
186 return fparam, fit.sd_beta, itlim, lambda x: gfunc(fparam, x)
187 else:
188 return fparam, fit.sd_beta, itlim
189
190
191 def _getfit_func(peaktype, background):
192 """
193 internal function to prepare the model functions and derivatives for the
194 peak_fit function.
195
196 Parameters
197 ----------
198 peaktype : {'Gauss', 'Lorentz', 'PseudoVoigt', 'PseudoVoigtAsym', 'PseudoVoigtAsym2'}
199 type of peak function
200 background : {'constant', 'linear'}
201 type of background function
202
203 Returns
204 -------
205 f, f_dx, f_dp : functions
206 fit function, function of derivative regarding `x`, and functions of
207 derivatives regarding the parameters
208 """
209 gfunc_dx = None
210 gfunc_dp = None
211 if peaktype == 'Gauss':
212 f = Gauss1d
213 fdx = Gauss1d_der_x
214 fdp = Gauss1d_der_p
215 elif peaktype == 'Lorentz':
216 f = Lorentz1d
217 fdx = Lorentz1d_der_x
218 fdp = Lorentz1d_der_p
219 elif peaktype == 'PseudoVoigt':
220 f = PseudoVoigt1d
221 fdx = PseudoVoigt1d_der_x
222 fdp = PseudoVoigt1d_der_p
223 elif peaktype == 'PseudoVoigtAsym':
224 if background == 'linear':
225 def gfunc(param, x):
226 return PseudoVoigt1dasym(x, *param) + x * param[-1]
227 else:
228 def gfunc(param, x):
229 return PseudoVoigt1dasym(x, *param)
230 elif peaktype == 'PseudoVoigtAsym2':
231 if background == 'linear':
232 def gfunc(param, x):
233 return PseudoVoigt1dasym2(x, *param) + x * param[-1]
234 else:
235 def gfunc(param, x):
236 return PseudoVoigt1dasym2(x, *param)
237 else:
238 raise InputError("keyword argument peaktype takes invalid value!")
239
240 if peaktype in ('Gauss', 'Lorentz', 'PseudoVoigt'):
241 if background == 'linear':
242 def gfunc(param, x):
243 return f(x, *param) + x * param[-1]
244
245 def gfunc_dx(param, x):
246 return fdx(x, *param) + param[-1]
247
248 def gfunc_dp(param, x):
249 return numpy.vstack((fdp(x, *param), x))
250 else:
251 def gfunc(param, x):
252 return f(x, *param)
253
254 def gfunc_dx(param, x):
255 return fdx(x, *param)
256
257 def gfunc_dp(param, x):
258 return fdp(x, *param)
259 return gfunc, gfunc_dx, gfunc_dp
260
261
262 def _check_iparams(iparams, peaktype, background):
263 """
264 internal function to check if the length of the supplied initial
265 parameters is correct given the other settings of the peak_fit function.
266 An InputError is raised in case of wrong shape or value.
267
268 Parameters
269 ----------
270 iparams : list
271 initial paramters for the fit
272 peaktype : {'Gauss', 'Lorentz', 'PseudoVoigt', 'PseudoVoigtAsym', 'PseudoVoigtAsym2'}
273 type of peak to fit
274 background : {'constant', 'linear'}
275 type of background
276
277 """
278 if not any(iparams):
279 return
280 else:
281 ptypes = {('Gauss', 'constant'): 4, ('Lorentz', 'constant'): 4,
282 ('Gauss', 'linear'): 5, ('Lorentz', 'linear'): 5,
283 ('PseudoVoigt', 'constant'): 5, ('PseudoVoigt', 'linear'): 6,
284 ('PseudoVoigtAsym', 'constant'): 6,
285 ('PseudoVoigtAsym', 'linear'): 7,
286 ('PseudoVoigtAsym2', 'constant'): 7,
287 ('PseudoVoigtAsym2', 'linear'): 8}
288 if not all(numpy.isreal(iparams)):
289 raise InputError("XU.math.peak_fit: all initial parameters need to"
290 "be real!")
291 elif (peaktype, background) in ptypes:
292 nparams = ptypes[(peaktype, background)]
293 if len(iparams) != nparams:
294 raise InputError("XU.math.peak_fit: %d initial parameters "
295 "are needed for %s-peak with %s background."
296 % (nparams, peaktype, background))
297 else:
298 raise InputError("XU.math.peak_fit: invalid peak (%s) or "
299 "background (%s)" % (peaktype, background))
300
301
302 def _guess_iparams(xdata, ydata, peaktype, background):
303 """
304 internal function to automatically esitmate peak parameters from the data,
305 considering also the background type.
306
307 Parameters
308 ----------
309 xdata : array-like
310 x-coordinates of the data to be fitted
311 ydata : array-like
312 y-coordinates of the data which should be fit
313 peaktype : {'Gauss', 'Lorentz', 'PseudoVoigt', 'PseudoVoigtAsym', 'PseudoVoigtAsym2'}
314 type of peak to fit
315 background : {'constant', 'linear'}
316 type of background, either
317
318 Returns
319 -------
320 list of initial parameters estimated from the data
321 """
322 ld = numpy.empty(len(ydata))
323 # estimate peak position
324 ipos, ld, back, slope = center_of_mass(xdata, ydata, background,
325 full_output=True)
326 maxpos = xdata[numpy.argmax(ld)]
327 avx = numpy.average(xdata)
328 if numpy.abs(ipos - avx) < numpy.abs(maxpos-avx):
329 ipos = maxpos # use the estimate which is further from the center
330
331 # estimate peak width
332 sigma1 = numpy.sqrt(numpy.sum(numpy.abs((xdata - ipos) ** 2 * ld)) /
333 numpy.abs(numpy.sum(ld)))
334 sigma2 = fwhm_exp(xdata, ld)/(2 * numpy.sqrt(2 * numpy.log(2)))
335 sigma = sigma1 if sigma1 < sigma2 else sigma2
336
337 # build initial parameters
338 iparams = [ipos, sigma, numpy.max(ld), back]
339 if peaktype in ['Lorentz', 'PseudoVoigt']:
340 iparams[1] *= 2 * numpy.sqrt(2 * numpy.log(2))
341 if peaktype in ['PseudoVoigtAsym', 'PseudoVoigtAsym2']:
342 iparams.insert(1, iparams[1])
343 if peaktype in ['PseudoVoigt', 'PseudoVoigtAsym']:
344 # set ETA parameter to be between Gauss and Lorentz shape
345 iparams.append(0.5)
346 if peaktype == 'PseudoVoigtAsym2':
347 iparams.append(0.5)
348 iparams.append(0.5)
349 if background == 'linear':
350 iparams.append(slope)
351 return iparams
352
353
354 def gauss_fit(xdata, ydata, iparams=[], maxit=300):
355 """
356 Gauss fit function using odr-pack wrapper in scipy similar to
357 https://github.com/tiagopereira/python_tips/wiki/Scipy%3A-curve-fitting
358
359 Parameters
360 ----------
361 xdata : array-like
362 x-coordinates of the data to be fitted
363 ydata : array-like
364 y-coordinates of the data which should be fit
365 iparams: list, optional
366 initial paramters for the fit, determined automatically if not given
367 maxit : int, optional
368 maximal iteration number of the fit
369
370 Returns
371 -------
372 params : list
373 the parameters as defined in function ``Gauss1d(x, *param)``
374 sd_params : list
375 For every parameter the corresponding errors are returned.
376 itlim : bool
377 flag to tell if the iteration limit was reached, should be False
378 """
379 return peak_fit(xdata, ydata, iparams=iparams,
380 peaktype='Gauss', maxit=maxit)
381
382
383 def fit_peak2d(x, y, data, start, drange, fit_function, maxfev=2000):
384 """
385 fit a two dimensional function to a two dimensional data set
386 e.g. a reciprocal space map
387
388 Parameters
389 ----------
390 x, y : array-like
391 data coordinates (do NOT need to be regularly spaced)
392 data : array-like
393 data set used for fitting (e.g. intensity at the data coords)
394 start : list
395 set of starting parameters for the fit used as first parameter of
396 function fit_function
397 drange : list
398 limits for the data ranges used in the fitting algorithm, e.g. it is
399 clever to use only a small region around the peak which should be
400 fitted: [xmin, xmax, ymin, ymax]
401 fit_function : callable
402 function which should be fitted, must be of form accept the parameters
403 ``fit_function (x, y, *params) -> ndarray``
404
405 Returns
406 -------
407 fitparam : list
408 fitted parameters
409 cov : array-like
410 covariance matrix
411 """
412 s = time.time()
413 if config.VERBOSITY >= config.INFO_ALL:
414 print("XU.math.fit: Fitting started... ", end='')
415
416 start = numpy.array(start)
417 lx = x.flatten()
418 ly = y.flatten()
419 mask = (lx > drange[0]) * (lx < drange[1]) * \
420 (ly > drange[2]) * (ly < drange[3])
421 ly = ly[mask]
422 lx = lx[mask]
423 ldata = data.flatten()[mask]
424
425 def errfunc(p, x, z, data):
426 return fit_function(x, z, *p) - data
427
428 p, cov, infodict, errmsg, success = optimize.leastsq(
429 errfunc, start, args=(lx, ly, ldata), full_output=1, maxfev=maxfev)
430
431 s = time.time() - s
432 if config.VERBOSITY >= config.INFO_ALL:
433 print("finished in %8.2f sec, (data length used %d)" % (s, ldata.size))
434 print("XU.math.fit: %s" % errmsg)
435
436 # calculate correct variance covariance matrix
437 if cov is not None:
438 s_sq = (errfunc(p, lx, ly, ldata) ** 2).sum() / \
439 (len(ldata) - len(start))
440 pcov = cov * s_sq
441 else:
442 pcov = numpy.zeros((len(start), len(start)))
443
444 if success not in [1, 2, 3, 4]:
445 print("XU.math.fit: Could not obtain fit!")
446 return p, pcov
447
448
449 def multGaussFit(*args, **kwargs):
450 """
451 convenience function to keep API stable
452 see multPeakFit for documentation
453 """
454 kwargs['peaktype'] = 'Gaussian'
455 return multPeakFit(*args, **kwargs)
456
457
458 def multPeakFit(x, data, peakpos, peakwidth, dranges=None,
459 peaktype='Gaussian'):
460 """
461 function to fit multiple Gaussian/Lorentzian peaks with linear background
462 to a set of data
463
464 Parameters
465 ----------
466 x : array-like
467 x-coordinate of the data
468 data : array-like
469 data array with same length as `x`
470 peakpos : list
471 initial parameters for the peak positions
472 peakwidth : list
473 initial values for the peak width
474 dranges : list of tuples
475 list of tuples with (min, max) value of the data ranges to use. does
476 not need to have the same number of entries as peakpos
477 peaktype : {'Gaussian', 'Lorentzian'}
478 type of peaks to be used
479
480 Returns
481 -------
482 pos : list
483 peak positions derived by the fit
484 sigma : list
485 peak width derived by the fit
486 amp : list
487 amplitudes of the peaks derived by the fit
488 background : array-like
489 background values at positions `x`
490 """
491 if peaktype == 'Gaussian':
492 pfunc = Gauss1d
493 pfunc_derx = Gauss1d_der_x
494 elif peaktype == 'Lorentzian':
495 pfunc = Lorentz1d
496 pfunc_derx = Lorentz1d_der_x
497 else:
498 raise ValueError('wrong value for parameter peaktype was given')
499
500 def deriv_x(p, x):
501 """
502 function to calculate the derivative of the signal of multiple peaks
503 and background w.r.t. the x-coordinate
504
505 Parameters
506 ----------
507 p : list
508 parameters, for every peak there needs to be position, sigma,
509 amplitude and at the end two values for the linear background
510 function (b0, b1)
511 x : array-like
512 x-coordinate
513 """
514 derx = numpy.zeros(x.size)
515
516 # sum up peak functions contributions
517 for i in range(len(p) // 3):
518 ldx = pfunc_derx(x, p[3 * i], p[3 * i + 1], p[3 * i + 2], 0)
519 derx += ldx
520
521 # background contribution
522 k = p[-2]
523 d = p[-1]
524 b = numpy.ones(x.size) * k
525
526 return derx + b
527
528 def deriv_p(p, x):
529 """
530 function to calculate the derivative of the signal of multiple peaks
531 and background w.r.t. the parameters
532
533 Parameters
534 ----------
535 p : list
536 parameters, for every peak there needs to be position, sigma,
537 amplitude and at the end two values for the linear background
538 function (b0, b1)
539 x : array-like
540 x-coordinate
541
542 returns derivative w.r.t. all the parameters with shape (len(p),x.size)
543 """
544
545 derp = numpy.empty(0)
546 # peak functions contributions
547 for i in range(len(p) // 3):
548 lp = (p[3 * i], p[3 * i + 1], p[3 * i + 2], 0)
549 if peaktype == 'Gaussian':
550 derp = numpy.append(derp, -2 * (lp[0] - x) * pfunc(x, *lp))
551 derp = numpy.append(
552 derp, (lp[0] - x) ** 2 / (2 * lp[1] ** 3) * pfunc(x, *lp))
553 derp = numpy.append(derp, pfunc(x, *lp) / lp[2])
554 else: # Lorentzian
555 derp = numpy.append(derp, 4 * (x - lp[0]) * lp[2] / lp[1] /
556 (1 + (2 * (x - lp[0]) / lp[1]) ** 2) ** 2)
557 derp = numpy.append(derp, 4 * (lp[0] - x) * lp[2] /
558 lp[1] ** 2 / (1 + (2 * (x - lp[0]) /
559 lp[1]) ** 2) ** 2)
560 derp = numpy.append(derp,
561 1 / (1 + (2 * (x - p[0]) / p[1]) ** 2))
562
563 # background contributions
564 derp = numpy.append(derp, x)
565 derp = numpy.append(derp, numpy.ones(x.size))
566
567 # reshape output
568 derp.shape = (len(p),) + x.shape
569 return derp
570
571 def fsignal(p, x):
572 """
573 function to calculate the signal of multiple peaks and background
574
575 Parameters
576 ----------
577 p : list
578 list of parameters, for every peak there needs to be position,
579 sigma, amplitude and at the end two values for the linear
580 background function (k, d)
581 x : array-like
582 x-coordinate
583 """
584 f = numpy.zeros(x.size)
585
586 # sum up peak functions
587 for i in range(len(p) // 3):
588 lf = pfunc(x, p[3 * i], p[3 * i + 1], p[3 * i + 2], 0)
589 f += lf
590
591 # background
592 k = p[-2]
593 d = p[-1]
594 b = numpy.polyval((k, d), x)
595
596 return f + b
597
598 ##########################
599 # create local data set (extract data ranges)
600 if dranges:
601 mask = numpy.array([False] * x.size)
602 for i in range(len(dranges)):
603 lrange = dranges[i]
604 lmask = numpy.logical_and(x > lrange[0], x < lrange[1])
605 mask = numpy.logical_or(mask, lmask)
606 lx = x[mask]
607 ldata = data[mask]
608 else:
609 lx = x
610 ldata = data
611
612 # create initial parameter list
613 p = []
614
615 # background
616 k, d = numpy.polyfit(lx, ldata, 1)
617
618 # peak parameters
619 for i in range(len(peakpos)):
620 amp = ldata[(lx - peakpos[i]) >= 0][0] - \
621 numpy.polyval((k, d), lx)[(lx - peakpos[i]) >= 0][0]
622 p += [peakpos[i], peakwidth[i], amp]
623
624 # background parameters
625 p += [k, d]
626
627 if(config.VERBOSITY >= config.DEBUG):
628 print("XU.math.multGaussFit: intial parameters")
629 print(p)
630
631 ##########################
632 # fit with odrpack
633 model = odr.Model(fsignal, fjacd=deriv_x, fjacb=deriv_p)
634 odata = odr.RealData(lx, ldata)
635 my_odr = odr.ODR(odata, model, beta0=p)
636 # fit type 2 for least squares
637 my_odr.set_job(fit_type=2)
638 fit = my_odr.run()
639
640 if(config.VERBOSITY >= config.DEBUG):
641 print("XU.math.multPeakFit: fitted parameters")
642 print(fit.beta)
643 try:
644 if fit.stopreason[0] not in ['Sum of squares convergence']:
645 print("XU.math.multPeakFit: fit NOT converged (%s)"
646 % fit.stopreason[0])
647 return None, None, None, None
648 except IndexError:
649 print("XU.math.multPeakFit: fit most probably NOT converged (%s)"
650 % str(fit.stopreason))
651 return None, None, None, None
652 # prepare return values
653 fpos = fit.beta[:-2:3]
654 fwidth = numpy.abs(fit.beta[1:-2:3])
655 famp = fit.beta[2::3]
656 background = numpy.polyval((fit.beta[-2], fit.beta[-1]), x)
657
658 return fpos, fwidth, famp, background
659
660
661 def multGaussPlot(*args, **kwargs):
662 """
663 convenience function to keep API stable
664 see multPeakPlot for documentation
665 """
666 kwargs['peaktype'] = 'Gaussian'
667 return multPeakPlot(*args, **kwargs)
668
669
670 def multPeakPlot(x, fpos, fwidth, famp, background, dranges=None,
671 peaktype='Gaussian', fig="xu_plot", ax=None, fact=1.):
672 """
673 function to plot multiple Gaussian/Lorentz peaks with background values
674 given by an array
675
676 Parameters
677 ----------
678 x : array-like
679 x-coordinate of the data
680 fpos : list
681 positions of the peaks
682 fwidth : list
683 width of the peaks
684 famp : list
685 amplitudes of the peaks
686 background : array-like
687 background values, same shape as `x`
688 dranges : list of tuples
689 list of (min, max) values of the data ranges to use. does not need to
690 have the same number of entries as fpos
691 peaktype : {'Gaussian', 'Lorentzian'}
692 type of peaks to be used
693 fig : int, str, or None
694 matplotlib figure number or name
695 ax : matplotlib.Axes
696 matplotlib axes as alternative to the figure name
697 fact : float
698 factor to use as multiplicator in the plot
699 """
700 success, plt = utilities.import_matplotlib_pyplot('XU.math.multPeakPlot')
701 if not success:
702 return
703
704 if fig:
705 plt.figure(fig)
706 if ax:
707 plt.sca(ax)
708 # plot single peaks
709 if dranges:
710 mask = numpy.array([False] * x.size)
711 for i in range(len(dranges)):
712 lrange = dranges[i]
713 lmask = numpy.logical_and(x > lrange[0], x < lrange[1])
714 mask = numpy.logical_or(mask, lmask)
715 lx = x[mask]
716 lb = background[mask]
717 else:
718 lx = x
719 lb = background
720
721 f = numpy.zeros(lx.size)
722 for i in range(len(fpos)):
723 if peaktype == 'Gaussian':
724 lf = Gauss1d(lx, fpos[i], fwidth[i], famp[i], 0)
725 elif peaktype == 'Lorentzian':
726 lf = Lorentz1d(lx, fpos[i], fwidth[i], famp[i], 0)
727 else:
728 raise ValueError('wrong value for parameter peaktype was given')
729 f += lf
730 plt.plot(lx, (lf + lb) * fact, 'k:')
731
732 # plot summed signal
733 plt.plot(lx, (f + lb) * fact, 'r-', lw=1.5)
+0
-835
xrayutilities/math/functions.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2012-2014 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module with several common function needed in xray data analysis
19 """
20
21 import copy
22 import numbers
23
24 import numpy
25 import scipy.integrate
26
27 from .. import config
28
29
30 def smooth(x, n):
31 """
32 function to smooth an array of data by averaging N adjacent data points
33
34 Parameters
35 ----------
36 x : array-like
37 1D data array
38 n : int
39 number of data points to average
40
41 Returns
42 -------
43 xsmooth: array-like
44 smoothed array with same length as x
45 """
46 if x.ndim != 1:
47 raise ValueError("smooth only accepts 1 dimension arrays.")
48 if x.size < n:
49 raise ValueError("Input vector needs to be bigger than n.")
50 if n < 2:
51 return x
52 # avoid boundary effects by adding mirrored signal at the boundaries
53 s = numpy.r_[x[n - 1:0:-1], x, x[-1:-n - 1:-1]]
54 w = numpy.ones(n, 'd')
55 y = numpy.convolve(w / w.sum(), s, mode='same')
56 return y[n:-n + 1]
57
58
59 def kill_spike(data, threshold=2., offset=None):
60 """
61 function to smooth **single** data points which differ from the average of
62 the neighboring data points by more than the threshold factor or more than
63 the offset value. Such spikes will be replaced by the mean value of the
64 next neighbors.
65
66 .. warning:: Use this function carefully not to manipulate your data!
67
68 Parameters
69 ----------
70 data : array-like
71 1d numpy array with experimental data
72 threshold : float or None
73 threshold factor to identify outlier data points. If None it will be
74 ignored.
75 offset : None or float
76 offset value to identify outlier data points. If None it will be
77 ignored.
78
79 Returns
80 -------
81 array-like
82 1d data-array with spikes removed
83 """
84
85 dataout = data.copy()
86
87 mean = (data[:-2] + data[2:]) / 2.
88 mask = numpy.zeros_like(data[1:-1], dtype=numpy.bool)
89 if threshold:
90 mask = numpy.logical_or(
91 mask, numpy.logical_or(data[1:-1] * threshold < mean,
92 data[1:-1] / threshold > mean))
93 if offset:
94 mask = numpy.logical_or(
95 mask, numpy.logical_or(data[1:-1] + offset < mean,
96 data[1:-1] - offset > mean))
97 # ensure that only single value are corrected and neighboring are ignored
98 for i in range(1, len(mask) - 1):
99 if mask[i - 1] and mask[i] and mask[i + 1]:
100 mask[i - 1] = False
101 mask[i + 1] = False
102
103 dataout[1:-1][mask] = mean[mask]
104
105 return dataout
106
107
108 def Gauss1d(x, *p):
109 """
110 function to calculate a general one dimensional Gaussian
111
112 Parameters
113 ----------
114 x : array-like
115 coordinate(s) where the function should be evaluated
116 p : list
117 list of parameters of the Gaussian [XCEN, SIGMA, AMP, BACKGROUND]
118 for information: SIGMA = FWHM / (2*sqrt(2*log(2)))
119
120 Returns
121 -------
122 array-like
123 the value of the Gaussian described by the parameters p at position x
124
125 Examples
126 --------
127 Calling with a list of parameters needs a call looking as shown below
128 (note the '*') or explicit listing of the parameters
129
130 >>> Gauss1d(x,*p)
131
132 >>> Gauss1d(numpy.linspace(0, 10, 100), 5, 1, 1e3, 0)
133 """
134 g = p[3] + p[2] * numpy.exp(-((p[0] - x) / p[1]) ** 2 / 2.)
135 return g
136
137
138 def NormGauss1d(x, *p):
139 """
140 function to calculate a normalized one dimensional Gaussian
141
142 Parameters
143 ----------
144 x : array-like
145 coordinate(s) where the function should be evaluated
146 p : list
147 list of parameters of the Gaussian [XCEN, SIGMA];
148 for information: SIGMA = FWHM / (2*sqrt(2*log(2)))
149
150 Returns
151 -------
152 array-like
153 the value of the normalized Gaussian described by the parameters p at
154 position x
155 """
156 g = numpy.exp(-((p[0] - x) / p[1]) ** 2 / 2.)
157 a = numpy.sqrt(2 * numpy.pi) * p[1]
158 return g / a
159
160
161 def Gauss1d_der_x(x, *p):
162 """
163 function to calculate the derivative of a Gaussian with respect to x
164
165 for parameter description see Gauss1d
166 """
167
168 lp = numpy.copy(p)
169 lp[3] = 0
170 return 2 * (p[0] - x) * Gauss1d(x, *lp)
171
172
173 def Gauss1d_der_p(x, *p):
174 """
175 function to calculate the derivative of a Gaussian with respect the
176 parameters p
177
178 for parameter description see Gauss1d
179 """
180 lp = numpy.copy(p)
181 lp[3] = 0
182 r = numpy.vstack((-2 * (p[0] - x) * Gauss1d(x, *lp),
183 (p[0] - x) ** 2 / (2 * p[1] ** 3) * Gauss1d(x, *lp),
184 Gauss1d(x, *lp) / p[2],
185 numpy.ones(x.shape, dtype=numpy.float)))
186
187 return r
188
189
190 def Gauss2d(x, y, *p):
191 """
192 function to calculate a general two dimensional Gaussian
193
194 Parameters
195 ----------
196 x, y : array-like
197 coordinate(s) where the function should be evaluated
198 p : list
199 list of parameters of the Gauss-function
200 [XCEN, YCEN, SIGMAX, SIGMAY, AMP, BACKGROUND, ANGLE];
201 SIGMA = FWHM / (2*sqrt(2*log(2)));
202 ANGLE = rotation of the X, Y direction of the Gaussian in radians
203
204 Returns
205 -------
206 array-like
207 the value of the Gaussian described by the parameters p at
208 position (x, y)
209 """
210
211 rcen_x = p[0] * numpy.cos(p[6]) - p[1] * numpy.sin(p[6])
212 rcen_y = p[0] * numpy.sin(p[6]) + p[1] * numpy.cos(p[6])
213 xp = x * numpy.cos(p[6]) - y * numpy.sin(p[6])
214 yp = x * numpy.sin(p[6]) + y * numpy.cos(p[6])
215
216 g = p[5] + p[4] * numpy.exp(-(((rcen_x - xp) / p[2]) ** 2 +
217 ((rcen_y - yp) / p[3]) ** 2) / 2.)
218 return g
219
220
221 def Gauss3d(x, y, z, *p):
222 """
223 function to calculate a general three dimensional Gaussian
224
225 Parameters
226 ----------
227 x, y, z : array-like
228 coordinate(s) where the function should be evaluated
229 p : list
230 list of parameters of the Gauss-function
231 [XCEN, YCEN, ZCEN, SIGMAX, SIGMAY, SIGMAZ, AMP, BACKGROUND];
232
233 SIGMA = FWHM / (2*sqrt(2*log(2)))
234
235 Returns
236 -------
237 array-like
238 the value of the Gaussian described by the parameters p at
239 positions (x, y, z)
240 """
241
242 g = p[7] + p[6] * numpy.exp(-(((x - p[0]) / p[3]) ** 2 +
243 ((y - p[1]) / p[4]) ** 2 +
244 ((z - p[2]) / p[5]) ** 2) / 2.)
245 return g
246
247
248 def TwoGauss2d(x, y, *p):
249 """
250 function to calculate two general two dimensional Gaussians
251
252 Parameters
253 ----------
254 x, y : array-like
255 coordinate(s) where the function should be evaluated
256 p : list
257 list of parameters of the Gauss-function
258 [XCEN1, YCEN1, SIGMAX1, SIGMAY1, AMP1, ANGLE1, XCEN2, YCEN2, SIGMAX2,
259 SIGMAY2, AMP2, ANGLE2, BACKGROUND];
260 SIGMA = FWHM / (2*sqrt(2*log(2)))
261 ANGLE = rotation of the X, Y direction of the Gaussian in radians
262
263 Returns
264 -------
265 array-like
266 the value of the Gaussian described by the parameters p
267 at position (x, y)
268 """
269
270 p = list(p)
271 p1 = p[0:5] + [p[12], ] + [p[5], ]
272 p2 = p[6:11] + [p[12], ] + [p[11], ]
273
274 g = Gauss2d(x, y, *p1) + Gauss2d(x, y, *p2)
275
276 return g
277
278
279 def Lorentz1d(x, *p):
280 """
281 function to calculate a general one dimensional Lorentzian
282
283 Parameters
284 ----------
285 x : array-like
286 coordinate(s) where the function should be evaluated
287 p : list
288 list of parameters of the Lorentz-function
289 [XCEN, FWHM, AMP, BACKGROUND]
290
291 Returns
292 -------
293 array-like
294 the value of the Lorentian described by the parameters p
295 at position (x, y)
296
297 """
298 g = p[3] + p[2] / (1 + (2 * (x - p[0]) / p[1]) ** 2)
299 return g
300
301
302 def Lorentz1d_der_x(x, *p):
303 """
304 function to calculate the derivative of a Gaussian with respect to x
305
306 for parameter description see Lorentz1d
307 """
308
309 return 4 * (p[0] - x) * p[2] / p[1] / \
310 (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2
311
312
313 def Lorentz1d_der_p(x, *p):
314 """
315 function to calculate the derivative of a Gaussian with respect the
316 parameters p
317
318 for parameter description see Lorentz1d
319 """
320 r = numpy.vstack((
321 4 * (x - p[0]) * p[2] / p[1] / (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2,
322 4 * (p[0] - x) * p[2] / p[1] ** 2 /
323 (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2,
324 1 / (1 + (2 * (x - p[0]) / p[1]) ** 2),
325 numpy.ones(x.shape, dtype=numpy.float)))
326 return r
327
328
329 def NormLorentz1d(x, *p):
330 """
331 function to calculate a normalized one dimensional Lorentzian
332
333 Parameters
334 ----------
335 x : array-like
336 coordinate(s) where the function should be evaluated
337 p : list
338 list of parameters of the Lorentzian [XCEN, FWHM]
339
340 Returns
341 -------
342 array-like
343 the value of the normalized Lorentzian described by the parameters p
344 at position x
345 """
346 g = 1.0 / (1 + (2 * (x - p[0]) / p[1]) ** 2)
347 a = numpy.pi / (2. / (p[1]))
348 return g / a
349
350
351 def Lorentz2d(x, y, *p):
352 """
353 function to calculate a general two dimensional Lorentzian
354
355 Parameters
356 ----------
357 x, y : array-like
358 coordinate(s) where the function should be evaluated
359 p : list
360 list of parameters of the Lorentz-function
361 [XCEN, YCEN, FWHMX, FWHMY, AMP, BACKGROUND, ANGLE];
362 ANGLE = rotation of the X, Y direction of the Lorentzian in radians
363
364 Returns
365 -------
366 array-like
367 the value of the Lorentian described by the parameters p
368 at position (x, y)
369 """
370 rcen_x = p[0] * numpy.cos(p[6]) - p[1] * numpy.sin(p[6])
371 rcen_y = p[0] * numpy.sin(p[6]) + p[1] * numpy.cos(p[6])
372 xp = x * numpy.cos(p[6]) - y * numpy.sin(p[6])
373 yp = x * numpy.sin(p[6]) + y * numpy.cos(p[6])
374
375 g = p[5] + p[4] / (1 + (2 * (rcen_x - xp) / p[2]) **
376 2 + (2 * (rcen_y - yp) / p[3]) ** 2)
377 return g
378
379
380 def PseudoVoigt1d(x, *p):
381 """
382 function to calculate a pseudo Voigt function as linear combination of a
383 Gauss and Lorentz peak
384
385 Parameters
386 ----------
387 x : array-like
388 coordinate(s) where the function should be evaluated
389 p : list
390 list of parameters of the pseudo Voigt-function
391 [XCEN, FWHM, AMP, BACKGROUND, ETA];
392 ETA: 0 ...1 0 means pure Gauss and 1 means pure Lorentz
393
394 Returns
395 -------
396 array-like
397 the value of the PseudoVoigt described by the parameters p
398 at position `x`
399 """
400 if p[4] > 1.0:
401 pv = 1.0
402 elif p[4] < 0.:
403 pv = 0.0
404 else:
405 pv = p[4]
406
407 sigma = p[1] / (2 * numpy.sqrt(2 * numpy.log(2)))
408 f = p[3] + pv * Lorentz1d(x, p[0], p[1], p[2], 0) + \
409 (1 - pv) * Gauss1d(x, p[0], sigma, p[2], 0)
410 return f
411
412
413 def PseudoVoigt1d_der_x(x, *p):
414 """
415 function to calculate the derivative of a PseudoVoigt with respect to `x`
416
417 for parameter description see PseudoVoigt1d
418 """
419 if p[4] > 1.0:
420 pv = 1.0
421 elif p[4] < 0.:
422 pv = 0.0
423 else:
424 pv = p[4]
425
426 lp = numpy.copy(p)
427 lp[3] = 0
428 lp[1] = p[1] / (2 * numpy.sqrt(2 * numpy.log(2)))
429
430 gdx = 2 * (p[0] - x) * Gauss1d(x, *lp)
431 ldx = 4 * (p[0] - x) * p[2] / p[1] / \
432 (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2
433 return pv * ldx + (1 - pv) * gdx
434
435
436 def PseudoVoigt1d_der_p(x, *p):
437 """
438 function to calculate the derivative of a PseudoVoigt with respect the
439 parameters `p`
440
441 for parameter description see PseudoVoigt1d
442 """
443
444 if p[4] > 1.0:
445 pv = 1.0
446 elif p[4] < 0.:
447 pv = 0.0
448 else:
449 pv = p[4]
450
451 lpg = numpy.copy(p) # local parameters for gaussian
452 lpg[3] = 0
453 lpl = numpy.copy(lpg) # local parameters for lorentzian
454 lpg[1] = p[1] / (2 * numpy.sqrt(2 * numpy.log(2)))
455
456 rl = numpy.vstack((
457 4 * (x - p[0]) * p[2] / p[1] / (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2,
458 4 * (p[0] - x) * p[2] / p[1] ** 2 /
459 (1 + (2 * (x - p[0]) / p[1]) ** 2) ** 2,
460 1 / (1 + (2 * (x - p[0]) / p[1]) ** 2)))
461
462 rg = numpy.vstack((-2 * (lpg[0] - x) * Gauss1d(x, *lpg),
463 (lpg[0] - x) ** 2 /
464 (2 * lpg[1] ** 3) * Gauss1d(x, *lpg),
465 Gauss1d(x, *lpg) / lpg[2]))
466
467 r = pv * rl + (1 - pv) * rg
468 f = Lorentz1d(x, *lpl) + \
469 - Gauss1d(x, *lpg)
470 r = numpy.vstack((r,
471 numpy.ones(x.shape),
472 Lorentz1d(x, *lpl) - Gauss1d(x, *lpg)))
473 return r
474
475
476 def PseudoVoigt1dasym(x, *p):
477 """
478 function to calculate an asymmetric pseudo Voigt function as linear
479 combination of asymmetric Gauss and Lorentz peak
480
481 Parameters
482 ----------
483 x : array-like
484 coordinate(s) where the function should be evaluated
485 p : list
486 list of parameters of the pseudo Voigt-function
487 [XCEN, FWHMLEFT, FWHMRIGHT, AMP, BACKGROUND, ETA];
488 ETA: 0 ...1 0 means pure Gauss and 1 means pure Lorentz
489
490 Returns
491 -------
492 array-like
493 the value of the PseudoVoigt described by the parameters p
494 at position `x`
495 """
496 lp = copy.copy(list(p))
497 lp.insert(6, p[5])
498 return PseudoVoigt1dasym2(x, *lp)
499
500
501 def PseudoVoigt1dasym2(x, *p):
502 """
503 function to calculate an asymmetric pseudo Voigt function as linear
504 combination of asymmetric Gauss and Lorentz peak
505
506 Parameters
507 ----------
508 x : naddray
509 coordinate(s) where the function should be evaluated
510 p : list
511 list of parameters of the pseudo Voigt-function
512 [XCEN, FWHMLEFT, FWHMRIGHT, AMP, BACKGROUND, ETALEFT, ETARIGHT];
513 ETA: 0 ...1 0 means pure Gauss and 1 means pure Lorentz
514
515 Returns
516 -------
517 array-like
518 the value of the PseudoVoigt described by the parameters p
519 at position `x`
520 """
521 pvl = p[5] if p[5] < 1.0 else 1.0
522 pvl = pvl if p[5] > 0.0 else 0.0
523 pvr = p[6] if p[6] < 1.0 else 1.0
524 pvr = pvr if p[6] > 0.0 else 0.0
525
526 sigmal = p[1] / (2 * numpy.sqrt(2 * numpy.log(2)))
527 sigmar = p[2] / (2 * numpy.sqrt(2 * numpy.log(2)))
528
529 if isinstance(x, numbers.Number):
530 if x < p[0]:
531 f = p[4] + pvl * Lorentz1d(x, p[0], p[1], p[3], 0) + \
532 (1 - pvl) * Gauss1d(x, p[0], sigmal, p[3], 0)
533 else:
534 f = p[4] + pvr * Lorentz1d(x, p[0], p[2], p[3], 0) + \
535 (1 - pvr) * Gauss1d(x, p[0], sigmar, p[3], 0)
536 else:
537 lx = numpy.asarray(x)
538 f = numpy.zeros(lx.shape)
539 f[lx < p[0]] = (p[4] + pvl *
540 Lorentz1d(lx[x < p[0]], p[0], p[1], p[3], 0) +
541 (1 - pvl) *
542 Gauss1d(lx[x < p[0]], p[0], sigmal, p[3], 0))
543 f[lx >= p[0]] = (p[4] + pvr *
544 Lorentz1d(lx[x >= p[0]], p[0], p[2], p[3], 0) +
545 (1 - pvr) *
546 Gauss1d(lx[x >= p[0]], p[0], sigmar, p[3], 0))
547
548 return f
549
550
551 def PseudoVoigt2d(x, y, *p):
552 """
553 function to calculate a pseudo Voigt function as linear combination of a
554 Gauss and Lorentz peak in two dimensions
555
556 Parameters
557 ----------
558 x, y : array-like
559 coordinate(s) where the function should be evaluated
560 p : list
561 list of parameters of the pseudo Voigt-function
562 [XCEN, YCEN, FWHMX, FWHMY, AMP, BACKGROUND, ANGLE, ETA];
563 ETA: 0 ...1 0 means pure Gauss and 1 means pure Lorentz
564
565 Returns
566 -------
567 array-like
568 the value of the PseudoVoigt described by the parameters `p`
569 at position `(x, y)`
570 """
571 if p[7] > 1.0:
572 pv = 1.0
573 elif p[7] < 0.:
574 pv = 0.0
575 else:
576 pv = p[7]
577 sigmax = p[2] / (2 * numpy.sqrt(2 * numpy.log(2)))
578 sigmay = p[3] / (2 * numpy.sqrt(2 * numpy.log(2)))
579 f = p[5] + pv * Lorentz2d(x, y, p[0], p[1], p[2], p[3], p[4], 0, p[6]) + \
580 (1 - pv) * Gauss2d(x, y, p[0], p[1], sigmax, sigmay, p[4], 0, p[6])
581 return f
582
583
584 def Gauss1dArea(*p):
585 """
586 function to calculate the area of a Gauss function with neglected
587 background
588
589 Parameters
590 ----------
591 p : list
592 list of parameters of the Gauss-function [XCEN, SIGMA, AMP, BACKGROUND]
593
594 Returns
595 -------
596 float
597 the area of the Gaussian described by the parameters `p`
598 """
599 f = p[2] * numpy.sqrt(2 * numpy.pi) * p[1]
600 return f
601
602
603 def Gauss2dArea(*p):
604 """
605 function to calculate the area of a 2D Gauss function with neglected
606 background
607
608 Parameters
609 ----------
610 p : list
611 list of parameters of the Gauss-function
612 [XCEN, YCEN, SIGMAX, SIGMAY, AMP, ANGLE, BACKGROUND]
613
614 Returns
615 -------
616 float
617 the area of the Gaussian described by the parameters `p`
618 """
619 f = p[4] * numpy.sqrt(2 * numpy.pi) ** 2 * p[2] * p[3]
620 return f
621
622
623 def Lorentz1dArea(*p):
624 """
625 function to calculate the area of a Lorentz function with neglected
626 background
627
628 Parameters
629 ----------
630 p : list
631 list of parameters of the Lorentz-function
632 [XCEN, FWHM, AMP, BACKGROUND]
633
634 Returns
635 -------
636 float
637 the area of the Lorentzian described by the parameters `p`
638 """
639 f = p[2] * numpy.pi / (2. / (p[1]))
640 return f
641
642
643 def PseudoVoigt1dArea(*p):
644 """
645 function to calculate the area of a pseudo Voigt function with neglected
646 background
647
648 Parameters
649 ----------
650 p : list
651 list of parameters of the Lorentz-function
652 [XCEN, FWHM, AMP, BACKGROUND, ETA];
653 ETA: 0 ...1 0 means pure Gauss and 1 means pure Lorentz
654
655 Returns
656 -------
657 float
658 the area of the PseudoVoigt described by the parameters `p`
659 """
660 sigma = p[1] / (2 * numpy.sqrt(2 * numpy.log(2)))
661 f = p[4] * Lorentz1dArea(*p) + (1. - p[4]) * \
662 Gauss1dArea(p[0], sigma, p[2], p[3])
663 return f
664
665
666 def Debye1(x):
667 r"""
668 function to calculate the first Debye function [1]_ as needed
669 for the calculation of the thermal Debye-Waller-factor
670 by numerical integration
671
672 .. math:: D_1(x) = (1/x) \int_0^x t/(\exp(t)-1) dt
673
674 Parameters
675 ----------
676 x : float
677 argument of the Debye function
678
679 Returns
680 -------
681 float
682 D1(x) float value of the Debye function
683
684 References
685 ----------
686 .. [1] http://en.wikipedia.org/wiki/Debye_function
687 """
688
689 def __int_kernel(t):
690 """
691 integration kernel for the numeric integration
692 """
693 y = t / (numpy.exp(t) - 1)
694 return y
695
696 if x > 0.:
697 integral = scipy.integrate.quad(__int_kernel, 0, x)
698 d1 = (1 / float(x)) * integral[0]
699 else:
700 integral = (0, 0)
701 d1 = 1.
702
703 if (config.VERBOSITY >= config.DEBUG):
704 print(
705 "XU.math.Debye1: Debye integral value/error estimate: %g %g" %
706 (integral[0], integral[1]))
707
708 return d1
709
710
711 def multPeak1d(x, *args):
712 """
713 function to calculate the sum of multiple peaks in 1D.
714 the peaks can be of different type and a background function (polynom)
715 can also be included.
716
717 Parameters
718 ----------
719 x : array-like
720 coordinate where the function should be evaluated
721 args : list
722 list of peak/function types and parameters for every function type two
723 arguments need to be given first the type of function as string with
724 possible values 'g': Gaussian, 'l': Lorentzian, 'v': PseudoVoigt, 'a':
725 asym. PseudoVoigt, 'p': polynom the second type of arguments is the
726 tuple/list of parameters of the respective function. See documentation
727 of math.Gauss1d, math.Lorentz1d, math.PseudoVoigt1d,
728 math.PseudoVoigt1dasym, and numpy.polyval for details of the different
729 function types.
730
731 Returns
732 -------
733 array-like
734 value of the sum of functions at position `x`
735 """
736 if len(args) % 2 != 0:
737 raise ValueError('number of arguments must be even!')
738
739 if numpy.isscalar(x):
740 f = 0
741 else:
742 lx = numpy.array(x)
743 f = numpy.zeros(lx.shape)
744
745 for i in range(int(len(args) / 2)):
746 ftype = str.lower(args[2 * i])
747 fparam = args[2 * i + 1]
748 if ftype == 'g':
749 f += Gauss1d(x, *fparam)
750 elif ftype == 'l':
751 f += Lorentz1d(x, *fparam)
752 elif ftype == 'v':
753 f += PseudoVoigt1d(x, *fparam)
754 elif ftype == 'a':
755 f += PseudoVoigt1dasym(x, *fparam)
756 elif ftype == 'p':
757 if isinstance(fparam, (tuple, list, numpy.ndarray)):
758 f += numpy.polyval(fparam, x)
759 else:
760 f += numpy.polyval((fparam,), x)
761 else:
762 raise ValueError('invalid function type given!')
763
764 return f
765
766
767 def multPeak2d(x, y, *args):
768 """
769 function to calculate the sum of multiple peaks in 2D.
770 the peaks can be of different type and a background function (polynom)
771 can also be included.
772
773 Parameters
774 ----------
775 x, y : array-like
776 coordinates where the function should be evaluated
777 args : list
778 list of peak/function types and parameters for every function type two
779 arguments need to be given first the type of function as string with
780 possible values 'g': Gaussian, 'l': Lorentzian, 'v': PseudoVoigt, 'c':
781 constant the second type of arguments is the tuple/list of parameters
782 of the respective function. See documentation of math.Gauss2d,
783 math.Lorentz2d, math.PseudoVoigt2d for details of the different
784 function types. The constant accepts a single float which will be
785 added to the data
786
787 Returns
788 -------
789 array-like
790 value of the sum of functions at position `(x, y)`
791 """
792 if len(args) % 2 != 0:
793 raise ValueError('number of arguments must be even!')
794
795 if numpy.isscalar(x):
796 f = 0
797 else:
798 lx = numpy.array(x)
799 ly = numpy.array(y)
800 f = numpy.zeros(lx.shape)
801
802 for i in range(int(len(args) / 2)):
803 ftype = str.lower(args[2 * i])
804 fparam = args[2 * i + 1]
805 if ftype == 'g':
806 f += Gauss2d(lx, ly, *fparam)
807 elif ftype == 'l':
808 f += Lorentz2d(lx, ly, *fparam)
809 elif ftype == 'v':
810 f += PseudoVoigt2d(lx, ly, *fparam)
811 elif ftype == 'c':
812 f += fparam
813 else:
814 raise ValueError('invalid function type given!')
815
816 return f
817
818
819 def heaviside(x):
820 """
821 Heaviside step function for numpy arrays
822
823 Parameters
824 ----------
825 x: scalar or array-like
826 argument of the step function
827
828 Returns
829 -------
830 int or array-like
831 Heaviside step function evaluated for all values of `x` with datatype
832 integer
833 """
834 return (numpy.sign(x)/2. + 0.5).astype(numpy.int8)
+0
-148
xrayutilities/math/misc.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import fractions
18 import math
19 import sys
20
21 import numpy
22
23 from .. import config
24
25
26 def center_of_mass(pos, data, background='none', full_output=False):
27 """
28 function to determine the center of mass of an array
29
30 Parameters
31 ----------
32 pos : array-like
33 position of the data points
34 data : array-like
35 data values
36 background : {'none', 'constant', 'linear'}
37 type of background, either 'none', 'constant' or 'linear'
38 full_output : bool
39 return background cleaned data and background-parameters
40
41 Returns
42 -------
43 float
44 center of mass position
45 """
46 # subtract background
47 slope = 0
48 back = 0
49 if background == 'linear':
50 dx = float(pos[-1] - pos[0])
51 if abs(dx) > 0:
52 slope = (float(data[-1]) - float(data[0])) / dx
53 back = (data[0] - slope * pos[0] +
54 data[-1] - slope * pos[-1]) / 2.0
55 ld = data - (slope * pos + back)
56 elif background == 'constant':
57 back = numpy.median(data)
58 ld = data - numpy.min(data)
59 else:
60 ld = data
61
62 ipos = numpy.sum(pos * ld) / numpy.sum(ld)
63 if full_output:
64 return ipos, ld, back, slope
65 else:
66 return ipos
67
68
69 def fwhm_exp(pos, data):
70 """
71 function to determine the full width at half maximum value of experimental
72 data. Please check the obtained value visually (noise influences the
73 result)
74
75 Parameters
76 ----------
77 pos : array-like
78 position of the data points
79 data : array-like
80 data values
81
82 Returns
83 -------
84 float
85 fwhm value
86 """
87
88 m = data.max()
89 p0 = numpy.argmax(data)
90 datal = data[:p0+1]
91 datar = data[p0:]
92
93 # determine left side half value position
94 try:
95 pls = pos[:p0+1][datal < m / 2.][-1]
96 pll = pos[:p0+1][datal > m / 2.][0]
97 ds = data[pos == pls][0]
98 dl = data[pos == pll][0]
99 pl = pls + (pll - pls) * (m / 2. - ds) / (dl - ds)
100 except IndexError:
101 if config.VERBOSITY >= config.INFO_LOW:
102 print("XU.math.fwhm_exp: warning: left side half value could"
103 " not be determined -> returns 2*hwhm")
104 pl = None
105
106 # determine right side half value position
107 try:
108 prs = pos[p0:][datar < m / 2.][0]
109 prl = pos[p0:][datar > m / 2.][-1]
110 ds = data[pos == prs][0]
111 dl = data[pos == prl][0]
112 pr = prs + (prl - prs) * (m / 2. - ds) / (dl - ds)
113 except IndexError:
114 if config.VERBOSITY >= config.INFO_LOW:
115 print("XU.math.fwhm_exp: warning: right side half value could"
116 " not be determined -> returns 2*hwhm")
117 pr = None
118
119 if pl is None:
120 return numpy.abs(pr - p0)*2
121 elif pr is None:
122 return numpy.abs(pl - p0)*2
123 else:
124 return numpy.abs(pr - pl)
125
126
127 def gcd(lst):
128 """
129 greatest common divisor function using library functions
130
131 Parameters
132 ----------
133 lst: array-like
134 array of integer values for which the greatest common divisor should be
135 determined
136
137 Returns
138 -------
139 gcd: int
140 """
141 if numpy.version.version >= '1.15.0':
142 return numpy.gcd.reduce(lst)
143 elif sys.version_info >= (3, 5):
144 gcdfunc = numpy.frompyfunc(math.gcd, 2, 1)
145 else:
146 gcdfunc = numpy.frompyfunc(fractions.gcd, 2, 1)
147 return numpy.ufunc.reduce(gcdfunc, lst)
+0
-338
xrayutilities/math/transforms.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2009-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 import numpy
19
20 from .. import config
21 from . import vector
22
23
24 class Transform(object):
25
26 def __init__(self, matrix):
27 self.matrix = matrix
28 self.imatrix = None
29
30 def inverse(self, args, rank=1):
31 """
32 performs inverse transformation a vector, matrix or tensor of rank 4
33
34 Parameters
35 ----------
36 args : list or array-like
37 object to transform, list or numpy array of shape (..., n)
38 (..., n, n), (..., n, n, n, n) where n is the size of the
39 transformation matrix.
40 rank : int
41 rank of the supplied object. allowed values are 1, 2, and 4
42 """
43 if self.imatrix is None:
44 try:
45 self.imatrix = numpy.linalg.inv(self.matrix)
46 except numpy.linalg.LinAlgError:
47 raise Exception("XU.math.Transform: matrix cannot be inverted"
48 " - seems to be singular")
49
50 it = Transform(self.imatrix)
51 return it(args, rank)
52
53 def __call__(self, args, rank=1):
54 """
55 transforms a vector, matrix or tensor of rank 4
56 (e.g. elasticity tensor)
57
58 Parameters
59 ----------
60 args : list or array-like
61 object to transform, list or numpy array of shape (..., n)
62 (..., n, n), (..., n, n, n, n) where n is the size of the
63 transformation matrix.
64 rank : int
65 rank of the supplied object. allowed values are 1, 2, and 4
66 """
67
68 m = self.matrix
69 if rank == 1: # argument is a vector
70 # out_i = m_ij * args_j
71 out = numpy.einsum('ij,...j', m, args)
72 elif rank == 2: # argument is a matrix
73 # out_ij = m_ik * m_jl * args_kl
74 out = numpy.einsum('ik, jl,...kl', m, m, args)
75 elif rank == 4:
76 # cp_ijkl = m_in * m_jo * m_kp * m_lq * args_nopq
77 out = numpy.einsum('in, jo, kp, lq,...nopq', m, m, m, m, args)
78
79 return out
80
81 def __str__(self):
82 ostr = "Transformation matrix:\n"
83 ostr += str(self.matrix)
84 return ostr
85
86
87 class CoordinateTransform(Transform):
88
89 """
90 Create a Transformation object which transforms a point into a new
91 coordinate frame. The new frame is determined by the three vectors
92 v1/norm(v1), v2/norm(v2) and v3/norm(v3), which need to be orthogonal!
93 """
94
95 def __init__(self, v1, v2, v3):
96 """
97 initialization routine for Coordinate transformation
98
99 Parameters
100 ----------
101 v1, v2, v3 : list, tuple or array-like
102 new base vectors
103
104 Returns
105 -------
106 Transform
107 An instance of a Transform class
108 """
109 e1 = vector._checkvec(v1)
110 e2 = vector._checkvec(v2)
111 e3 = vector._checkvec(v3)
112
113 # normalize base vectors
114 e1 = e1 / numpy.linalg.norm(e1)
115 e2 = e2 / numpy.linalg.norm(e2)
116 e3 = e3 / numpy.linalg.norm(e3)
117
118 # check that the vectors are orthogonal
119 t1 = numpy.abs(numpy.dot(e1, e2))
120 t2 = numpy.abs(numpy.dot(e1, e3))
121 t3 = numpy.abs(numpy.dot(e2, e3))
122 if t1 > config.EPSILON or t2 > config.EPSILON or t3 > config.EPSILON:
123 raise ValueError("given basis vectors need to be orthogonal!")
124
125 if config.VERBOSITY >= config.INFO_ALL:
126 print("XU.math.CoordinateTransform: new basis set: \n"
127 " x: (%5.2f %5.2f %5.2f) \n"
128 " y: (%5.2f %5.2f %5.2f) \n"
129 " z: (%5.2f %5.2f %5.2f)"
130 % (e1[0], e1[1], e1[2], e2[0], e2[1],
131 e2[2], e3[0], e3[1], e3[2]))
132
133 # assemble the transformation matrix
134 m = numpy.array([e1, e2, e3])
135
136 Transform.__init__(self, m)
137
138
139 class AxisToZ(CoordinateTransform):
140
141 """
142 Creates a coordinate transformation to move a certain axis to the z-axis.
143 The rotation is done along the great circle. The x-axis of the new
144 coordinate frame is created to be normal to the new and original z-axis.
145 The new y-axis is create in order to obtain a right handed coordinate
146 system.
147 """
148
149 def __init__(self, newzaxis):
150 """
151 initialize the CoordinateTransformation to move a certain axis to the
152 z-axis
153
154 Parameters
155 ----------
156 newzaxis : list or array-like
157 new z-axis
158 """
159 newz = vector._checkvec(newzaxis)
160
161 if vector.VecAngle([0, 0, 1], newz) < config.EPSILON:
162 newx = [1, 0, 0]
163 newy = [0, 1, 0]
164 newz = [0, 0, 1]
165 elif vector.VecAngle([0, 0, 1], -newz) < config.EPSILON:
166 newx = [-1, 0, 0]
167 newy = [0, 1, 0]
168 newz = [0, 0, -1]
169 else:
170 newx = numpy.cross(newz, [0, 0, 1])
171 newy = numpy.cross(newz, newx)
172
173 CoordinateTransform.__init__(self, newx, newy, newz)
174
175
176 class AxisToZ_keepXY(CoordinateTransform):
177
178 """
179 Creates a coordinate transformation to move a certain axis to the z-axis.
180 The rotation is done along the great circle. The x-axis/y-axis of the new
181 coordinate frame is created to be similar to the old x and y directions.
182 This variant of AxisToZ assumes that the new Z-axis has its main component
183 along the Z-direction
184 """
185
186 def __init__(self, newzaxis):
187 """
188 initialize the CoordinateTransformation to move a certain axis to the
189 z-axis
190
191 Parameters
192 ----------
193 newzaxis : list or array-like
194 new z-axis
195 """
196 newz = vector._checkvec(newzaxis)
197
198 if vector.VecAngle([0, 0, 1], newz) < config.EPSILON:
199 newx = [1, 0, 0]
200 newy = [0, 1, 0]
201 newz = [0, 0, 1]
202 elif vector.VecAngle([0, 0, 1], -newz) < config.EPSILON:
203 newx = [-1, 0, 0]
204 newy = [0, 1, 0]
205 newz = [0, 0, -1]
206 else:
207 newx = numpy.cross(newz, [0, 0, 1])
208 newy = numpy.cross(newz, newx)
209 # rotate newx and newy to be similar to old directions
210 ang = numpy.degrees(numpy.arctan2(newz[0], newz[1]))
211 newx = rotarb(newx, newz, ang)
212 newy = rotarb(newy, newz, ang)
213
214 CoordinateTransform.__init__(self, newx, newy, newz)
215
216
217 def _sincos(alpha, deg):
218 if deg:
219 a = numpy.radians(alpha)
220 else:
221 a = alpha
222 return numpy.sin(a), numpy.cos(a)
223
224
225 def XRotation(alpha, deg=True):
226 """
227 Returns a transform that represents a rotation about the x-axis
228 by an angle alpha. If deg=True the angle is assumed to be in
229 degree, otherwise the function expects radiants.
230 """
231 sina, cosa = _sincos(alpha, deg)
232 m = numpy.array(
233 [[1, 0, 0], [0, cosa, -sina], [0, sina, cosa]], dtype=numpy.double)
234 return Transform(m)
235
236
237 def YRotation(alpha, deg=True):
238 """
239 Returns a transform that represents a rotation about the y-axis
240 by an angle alpha. If deg=True the angle is assumed to be in
241 degree, otherwise the function expects radiants.
242 """
243 sina, cosa = _sincos(alpha, deg)
244 m = numpy.array(
245 [[cosa, 0, sina], [0, 1, 0], [-sina, 0, cosa]], dtype=numpy.double)
246 return Transform(m)
247
248
249 def ZRotation(alpha, deg=True):
250 """
251 Returns a transform that represents a rotation about the z-axis
252 by an angle alpha. If deg=True the angle is assumed to be in
253 degree, otherwise the function expects radiants.
254 """
255 sina, cosa = _sincos(alpha, deg)
256 m = numpy.array(
257 [[cosa, -sina, 0], [sina, cosa, 0], [0, 0, 1]], dtype=numpy.double)
258 return Transform(m)
259
260
261 # helper scripts for rotations around arbitrary axis
262 def tensorprod(vec1, vec2):
263 """
264 function implements an elementwise multiplication of two vectors
265 """
266 return vec1[:, numpy.newaxis] * numpy.ones((3, 3)) * vec2[numpy.newaxis, :]
267
268
269 def mycross(vec, mat):
270 """
271 function implements the cross-product of a vector with each column of a
272 matrix
273 """
274 out = numpy.zeros((3, 3))
275 for i in range(3):
276 out[:, i] = numpy.cross(vec, mat[:, i])
277 return out
278
279
280 def ArbRotation(axis, alpha, deg=True):
281 """
282 Returns a transform that represents a rotation around an arbitrary axis by
283 the angle alpha. positive rotation is anti-clockwise when looking from
284 positive end of axis vector
285
286 Parameters
287 ----------
288 axis : list or array-like
289 rotation axis
290 alpha : float
291 rotation angle in degree (deg=True) or in rad (deg=False)
292 deg : bool
293 determines the input format of ang (default: True)
294
295 Returns
296 -------
297 Transform
298 """
299 axis = vector._checkvec(axis)
300 e = axis / numpy.linalg.norm(axis)
301 if deg:
302 rad = numpy.radians(alpha)
303 else:
304 rad = alpha
305 get = tensorprod(e, e)
306 rot = get + numpy.cos(rad) * (numpy.identity(3) - get) + \
307 numpy.sin(rad) * mycross(e, numpy.identity(3))
308 return Transform(rot)
309
310
311 def rotarb(vec, axis, ang, deg=True):
312 """
313 function implements the rotation around an arbitrary axis by an angle ang
314 positive rotation is anti-clockwise when looking from positive end of axis
315 vector
316
317 Parameters
318 ----------
319 vec : list or array-like
320 vector to rotate
321 axis : list or array-like
322 rotation axis
323 ang : float
324 rotation angle in degree (deg=True) or in rad (deg=False)
325 deg : bool
326 determines the input format of ang (default: True)
327
328 Returns
329 -------
330 rotvec : rotated vector as numpy.array
331
332 Examples
333 --------
334 >>> rotarb([1, 0, 0],[0, 0, 1], 90)
335 array([ 6.12323400e-17, 1.00000000e+00, 0.00000000e+00])
336 """
337 return ArbRotation(axis, ang, deg)(vec)
+0
-293
xrayutilities/math/vector.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
16 # Copyright (C) 2010-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
17
18 """
19 module with vector operations for vectors of size 3,
20 since for so short vectors numpy does not give the best performance explicit
21 implementation of the equations is performed together with error checking to
22 ensure vectors of length 3.
23 """
24
25 import math
26 import re
27
28 import numpy
29
30 from .. import config
31 from ..exception import InputError
32
33 circleSyntax = re.compile("[xyz][+-]")
34
35
36 def _checkvec(v):
37 if isinstance(v, (list, tuple, numpy.ndarray)):
38 vtmp = numpy.asarray(v, dtype=numpy.double)
39 else:
40 raise TypeError("Vector must be a list, tuple or numpy array")
41 return vtmp
42
43
44 def VecNorm(v):
45 """
46 Calculate the norm of a vector.
47
48 Parameters
49 ----------
50 v : list or array-like
51 input vector(s), either one vector or an array of vectors with shape
52 (n, 3)
53
54 Returns
55 -------
56 float or ndarray
57 vector norm, either a single float or shape (n, )
58 """
59 if isinstance(v, numpy.ndarray):
60 if len(v.shape) >= 2:
61 return numpy.linalg.norm(v, axis=-1)
62 if len(v) != 3:
63 raise ValueError("Vector must be of length 3, but has length %d!"
64 % len(v))
65 return math.sqrt(v[0]**2 + v[1]**2 + v[2]**2)
66
67
68 def VecUnit(v):
69 """
70 Calculate the unit vector of v.
71
72 Parameters
73 ----------
74 v : list or array-like
75 input vector(s), either one vector or an array of vectors with shape
76 (n, 3)
77
78 Returns
79 -------
80 ndarray
81 unit vector of `v`, either shape (3, ) or (n, 3)
82 """
83 vtmp = _checkvec(v)
84 if len(vtmp.shape) == 1:
85 return vtmp / VecNorm(vtmp)
86 else:
87 return vtmp / VecNorm(vtmp)[..., numpy.newaxis]
88
89
90 def VecDot(v1, v2):
91 """
92 Calculate the vector dot product.
93
94 Parameters
95 ----------
96 v1, v2 : list or array-like
97 input vector(s), either one vector or an array of vectors with shape
98 (n, 3)
99
100 Returns
101 -------
102 float or ndarray
103 innter product of the vectors, either a single float or (n, )
104 """
105 if isinstance(v1, numpy.ndarray):
106 if len(v1.shape) >= 2:
107 return numpy.einsum('...i, ...i', v1, v2)
108 if len(v1) != 3 or len(v2) != 3:
109 raise ValueError("Vectors must be of size 3! (len(v1)=%d len(v2)=%d)"
110 % (len(v1), len(v2)))
111
112 return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]
113
114
115 def VecCross(v1, v2, out=None):
116 """
117 Calculate the vector cross product.
118
119 Parameters
120 ----------
121 v1, v2 : list or array-like
122 input vector(s), either one vector or an array of vectors with shape
123 (n, 3)
124 out : list or array-like, optional
125 output vector
126
127 Returns
128 -------
129 ndarray
130 cross product either of shape (3, ) or (n, 3)
131 """
132 if isinstance(v1, numpy.ndarray):
133 if len(v1.shape) >= 2 or len(v2.shape) >= 2:
134 return numpy.cross(v1, v2)
135 if len(v1) != 3 or len(v2) != 3:
136 raise ValueError("Vectors must be of size 3! (len(v1)=%d len(v2)=%d)"
137 % (len(v1), len(v2)))
138 if out is None:
139 out = numpy.empty(3)
140 out[0] = v1[1] * v2[2] - v1[2] * v2[1]
141 out[1] = v1[2] * v2[0] - v1[0] * v2[2]
142 out[2] = v1[0] * v2[1] - v1[1] * v2[0]
143 return out
144
145
146 def VecAngle(v1, v2, deg=False):
147 """
148 calculate the angle between two vectors. The following
149 formula is used
150 v1.v2 = norm(v1)*norm(v2)*cos(alpha)
151
152 alpha = acos((v1.v2)/(norm(v1)*norm(v2)))
153
154 Parameters
155 ----------
156 v1, v2 : list or array-like
157 input vector(s), either one vector or an array of vectors with shape
158 (n, 3)
159 deg: bool
160 True: return result in degree, False: in radiants
161
162 Returns
163 -------
164 float or ndarray
165 the angle included by the two vectors `v1` and `v2`, either a single
166 float or an array with shape (n, )
167 """
168 u1 = VecNorm(v1)
169 u2 = VecNorm(v2)
170
171 if isinstance(u1, numpy.ndarray) or isinstance(u2, numpy.ndarray):
172 s = VecDot(v1, v2) / u1 / u2
173 s[s > 1.0] = 1.0
174 alpha = numpy.arccos(s)
175 if deg:
176 alpha = numpy.degrees(alpha)
177 else:
178 alpha = math.acos(min(1., VecDot(v1, v2) / u1 / u2))
179 if deg:
180 alpha = math.degrees(alpha)
181
182 return alpha
183
184
185 def distance(x, y, z, point, vec):
186 """
187 calculate the distance between the point (x, y, z) and the line defined by
188 the point and vector vec
189
190 Parameters
191 ----------
192 x : float or ndarray
193 x coordinate(s) of the point(s)
194 y : float or ndarray
195 y coordinate(s) of the point(s)
196 z : float or ndarray
197 z coordinate(s) of the point(s)
198 point : tuple, list or ndarray
199 3D point on the line to which the distance should be calculated
200 vec : tuple, list or ndarray
201 3D vector defining the propergation direction of the line
202 """
203 coords = numpy.vstack((x - point[0], y - point[1], z - point[2])).T
204 return VecNorm(VecCross(coords, numpy.asarray(vec)))/VecNorm(vec)
205
206
207 def getVector(string):
208 """
209 returns unit vector along a rotation axis given in the syntax
210 'x+' 'z-' or equivalents
211
212 Parameters
213 ----------
214 string: str
215 vector string following the synthax [xyz][+-]
216
217 Returns
218 -------
219 ndarray
220 vector along the given direction
221 """
222
223 if len(string) != 2:
224 raise InputError("wrong length of string for conversion given")
225 if not circleSyntax.search(string):
226 raise InputError("getVector: incorrect string syntax (%s)" % string)
227
228 if string[0] == 'x':
229 v = [1., 0, 0]
230 elif string[0] == 'y':
231 v = [0, 1., 0]
232 elif string[0] == 'z':
233 v = [0, 0, 1.]
234 else:
235 raise InputError("wrong first character of string given "
236 "(needs to be one of x, y, z)")
237
238 if string[1] == '+':
239 v = numpy.asarray(v) * (+1)
240 elif string[1] == '-':
241 v = numpy.asarray(v) * (-1)
242 else:
243 raise InputError("wrong second character of string given "
244 "(needs to be + or -)")
245
246 return v
247
248
249 def getSyntax(vec):
250 """
251 returns vector direction in the syntax
252 'x+' 'z-' or equivalents
253 therefore works only for principle vectors of the coordinate system
254 like e.g. [1, 0, 0] or [0, 2, 0]
255
256 Parameters
257 ----------
258 vec : list or array-like
259 vector of length 3
260
261 Returns
262 -------
263 str
264 vector string following the synthax [xyz][+-]
265 """
266 v = _checkvec(vec)
267 if len(v) != 3:
268 raise InputError("no valid 3D vector given")
269
270 x = [1, 0, 0]
271 y = [0, 1, 0]
272 z = [0, 0, 1]
273
274 if VecNorm(numpy.cross(numpy.cross(x, y), v)) <= config.EPSILON:
275 if v[2] >= 0:
276 string = 'z+'
277 else:
278 string = 'z-'
279 elif VecNorm(numpy.cross(numpy.cross(x, z), v)) <= config.EPSILON:
280 if v[1] >= 0:
281 string = 'y+'
282 else:
283 string = 'y-'
284 elif VecNorm(numpy.cross(numpy.cross(y, z), v)) <= config.EPSILON:
285 if v[0] >= 0:
286 string = 'x+'
287 else:
288 string = 'x-'
289 else:
290 raise InputError("no valid 3D vector given")
291
292 return string
+0
-140
xrayutilities/mpl_helper.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
16 """
17 Defines new matplotlib Sqrt scale which further allows for negative values by
18 using the sign of the original value as sign of the plotted value.
19 """
20
21 import math
22
23 import numpy
24 from matplotlib import scale as mscale
25 from matplotlib import ticker as mticker
26 from matplotlib import transforms as mtransforms
27
28
29 class SqrtAllowNegScale(mscale.ScaleBase):
30 """
31 Scales data using a sqrt-function, however, allowing also negative values.
32
33 The scale function:
34 sign(y) * sqrt(abs(y))
35
36 The inverse scale function:
37 sign(y) * y**2
38 """
39 name = 'sqrt'
40
41 def __init__(self, axis, **kwargs):
42 """
43 Any keyword arguments passed to ``set_xscale`` and
44 ``set_yscale`` will be passed along to the scale's
45 constructor.
46 """
47 mscale.ScaleBase.__init__(self)
48
49 def set_default_locators_and_formatters(self, axis):
50 axis.set_major_locator(SqrtTickLocator())
51
52 def limit_range_for_scale(self, vmin, vmax, minpos):
53 """
54 Override to limit the bounds of the axis to the domain of the
55 transform. In the case of Mercator, the bounds should be
56 limited to the threshold that was passed in. Unlike the
57 autoscaling provided by the tick locators, this range limiting
58 will always be adhered to, whether the axis range is set
59 manually, determined automatically or changed through panning
60 and zooming.
61 """
62 return vmin, vmax
63
64 def get_transform(self):
65 return self.SqrtTransform()
66
67 class SqrtTransform(mtransforms.Transform):
68 input_dims = 1
69 output_dims = 1
70 is_separable = True
71
72 def transform_non_affine(self, a):
73 """
74 This transform takes an Nx1 ``numpy`` array and returns a
75 transformed copy.
76 """
77 return numpy.sign(a) * numpy.sqrt(numpy.abs(a))
78
79 def inverted(self):
80 """
81 return the inverse transform for this transform.
82 """
83 return SqrtAllowNegScale.InvertedSqrtTransform()
84
85 class InvertedSqrtTransform(mtransforms.Transform):
86 input_dims = 1
87 output_dims = 1
88 is_separable = True
89
90 def transform_non_affine(self, a):
91 return numpy.sign(a) * a**2
92
93 def inverted(self):
94 return SqrtAllowNegScale.SqrtTransform()
95
96
97 class SqrtTickLocator(mticker.Locator):
98 def __init__(self, nbins=7, symmetric=True):
99 self._base = mticker.Base(1.0)
100 self.set_params(nbins, symmetric)
101
102 def set_params(self, nbins, symmetric):
103 """Set parameters within this locator."""
104 self._nbins = nbins
105 self._symmetric = symmetric
106
107 def __call__(self):
108 'Return the locations of the ticks'
109 vmin, vmax = self.axis.get_view_interval()
110 return self.tick_values(vmin, vmax)
111
112 def tick_values(self, vmin, vmax):
113 if vmax < vmin:
114 vmin, vmax = vmax, vmin
115 tmin = math.copysign(math.sqrt(abs(vmin)), vmin)
116 tmax = math.copysign(math.sqrt(abs(vmax)), vmax)
117 delta = (tmax - tmin) / self._nbins
118 locs = numpy.arange(tmin, tmax, self._base.ge(delta))
119 if self._symmetric and numpy.sign(tmin) != numpy.sign(tmax):
120 locs -= locs[numpy.argmin(numpy.abs(locs))]
121 locs = numpy.sign(locs) * locs**2
122 return self.raise_if_exceeds(locs)
123
124 def view_limits(self, dmin, dmax):
125 """
126 Set the view limits to the nearest multiples of base that
127 contain the data
128 """
129 vmin = self._base.le(math.copysign(math.sqrt(abs(dmin)), dmin))
130 vmax = self._base.ge(math.sqrt(dmax))
131 if vmin == vmax:
132 vmin -= 1
133 vmax += 1
134
135 return mtransforms.nonsingular(math.copysign(vmin**2, vmin), vmax**2)
136
137
138 # register new scale to matplotlib
139 mscale.register_scale(SqrtAllowNegScale)
+0
-504
xrayutilities/normalize.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2011 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 module to provide functions that perform block averaging
19 of intensity arrays to reduce the amount of data (mainly
20 for PSD and CCD measurements
21
22 and
23
24 provide functions for normalizing intensities for
25
26 * count time
27 * absorber (user-defined function)
28 * monitor
29 * flatfield correction
30 """
31
32 import numpy
33
34 from . import config, cxrayutilities, math, utilities
35 from .exception import InputError
36
37 # python 2to3 compatibility
38 try:
39 basestring
40 except NameError:
41 basestring = str
42
43
44 def blockAverage1D(data, Nav):
45 """
46 perform block average for 1D array/list of Scalar values
47 all data are used. at the end of the array a smaller cell
48 may be used by the averaging algorithm
49
50 Parameters
51 ----------
52 data : array-like
53 data which should be contracted (length N)
54 Nav : int
55 number of values which should be averaged
56
57 Returns
58 -------
59 ndarray
60 block averaged numpy array of data type numpy.double
61 (length ceil(N/Nav))
62 """
63
64 if not isinstance(data, (numpy.ndarray, list)):
65 raise TypeError("first argument data must be of type list or "
66 "numpy.ndarray")
67
68 data = numpy.array(data, dtype=numpy.double)
69 block_av = cxrayutilities.block_average1d(data, Nav)
70
71 return block_av
72
73
74 def blockAverage2D(data2d, Nav1, Nav2, **kwargs):
75 """
76 perform a block average for 2D array of Scalar values
77 all data are used therefore the margin cells may differ in size
78
79 Parameters
80 ----------
81 data2d : ndarray
82 array of 2D data shape (N, M)
83 Nav1, Nav2 : int
84 a field of (Nav1 x Nav2) values is contracted
85
86 kwargs : dict, optional
87 optional keyword argument
88 roi : tuple or list, optional
89 region of interest for the 2D array. e.g. [20, 980, 40, 960],
90 reduces M, and M!
91
92 Returns
93 -------
94 ndarray
95 block averaged numpy array with type numpy.double with shape
96 (ceil(N/Nav1), ceil(M/Nav2))
97 """
98
99 if not isinstance(data2d, (numpy.ndarray)):
100 raise TypeError("first argument data2d must be of type numpy.ndarray")
101
102 roi = kwargs.get('roi', [0, data2d.shape[0], 0, data2d.shape[1]])
103 data = numpy.array(data2d[roi[0]:roi[1], roi[2]:roi[3]],
104 dtype=numpy.double)
105
106 if config.VERBOSITY >= config.DEBUG:
107 N, M = (roi[1] - roi[0], roi[3] - roi[2])
108 print("xu.normalize.blockAverage2D: roi: %s" % (str(roi)))
109 print("xu.normalize.blockAverage2D: Nav1, 2: %d,%d" % (Nav1, Nav2))
110 print("xu.normalize.blockAverage2D: number of points: (%d,%d)"
111 % (numpy.ceil(N / float(Nav1)), numpy.ceil(M / float(Nav2))))
112
113 block_av = cxrayutilities.block_average2d(data, Nav1, Nav2,
114 config.NTHREADS)
115
116 return block_av
117
118
119 def blockAveragePSD(psddata, Nav, **kwargs):
120 """
121 perform a block average for serveral PSD spectra
122 all data are used therefore the last cell used for
123 averaging may differ in size
124
125 Parameters
126 ----------
127 psddata : ndarray
128 array of 2D data shape (Nspectra, Nchannels)
129 Nav : int
130 number of channels which should be averaged
131
132 kwargs : dict, optional
133 optional keyword argument
134 roi : tuple or list
135 region of interest for the 2D array. e.g. [20, 980] Nchannels = 980-20
136
137 Returns
138 -------
139 ndarray
140 block averaged psd spectra as numpy array with type numpy.double of
141 shape (Nspectra , ceil(Nchannels/Nav))
142 """
143
144 if not isinstance(psddata, (numpy.ndarray)):
145 raise TypeError("first argument psddata must be of type numpy.ndarray")
146
147 roi = kwargs.get('roi', [0, psddata.shape[1]])
148 data = numpy.array(psddata[:, roi[0]:roi[1]], dtype=numpy.double)
149
150 block_av = cxrayutilities.block_average_PSD(data, Nav, config.NTHREADS)
151
152 return block_av
153
154 # #####################################
155 # # Intensity correction class ##
156 # #####################################
157
158
159 class IntensityNormalizer(object):
160
161 """
162 generic class for correction of intensity (point detector, or MCA,
163 single CCD frames) for count time and absorber factors
164 the class must be supplied with a absorber correction function
165 and works with data structures provided by xrayutilities.io classes or the
166 corresponding objects from hdf5 files
167 """
168
169 def __init__(self, det='', **keyargs):
170 """
171 initialization of the corrector class
172
173 Parameters
174 ----------
175 det : str
176 detector field name of the data structure
177
178 mon : str, optional
179 monitor field name
180 time: float or str, optional
181 count time field name or count time as float
182 av_mon : float, optional
183 average monitor value (default: data[mon].mean())
184 smoothmon : int
185 number of monitor values used to get a smooth monitor signal
186 absfun : callable, optional
187 absorber correction function to be used as in
188 ``absorber_corrected_intensity = data[det]*absfun(data)``
189 flatfield : ndarray
190 flatfield of the detector; shape must be the same as data[det], and
191 is only applied for MCA detectors
192 darkfield : ndarray
193 darkfield of the detector; shape must be the same as data[det], and
194 is only applied for MCA detectors
195
196 Examples
197 --------
198 >>> detcorr = IntensityNormalizer("MCA", time="Seconds",
199 >>> absfun=lambda d: d["PSDCORR"]/d["PSD"].astype(numpy.float))
200 """
201 valid_kwargs = {'mon': 'monitor field name',
202 'time': 'count time field/value',
203 'smoothmon': 'number of monitor values to average',
204 'av_mon': 'average monitor value',
205 'absfun': 'absorber correction function',
206 'flatfield': 'detector flatfield',
207 'darkfield': 'detector darkfield'}
208 utilities.check_kwargs(keyargs, valid_kwargs,
209 self.__class__.__name__)
210
211 # check input arguments
212 self._setdet(det)
213
214 self._setmon(keyargs.get('mon', None))
215 self._settime(keyargs.get('time', None))
216 self._setavmon(keyargs.get('av_mon', None))
217 self._setabsfun(keyargs.get('absfun', None))
218 self._setflatfield(keyargs.get('flatfield', None))
219 self._setdarkfield(keyargs.get('darkfield', None))
220 self.smoothmon = keyargs.get('smoothmon', 1)
221
222 def _getdet(self):
223 """
224 det property handler
225
226 returns the detector field name
227 """
228 return self._det
229
230 def _setdet(self, det):
231 """
232 det property handler
233
234 sets the detector field name
235 """
236 if isinstance(det, basestring):
237 self._det = det
238 else:
239 self._det = None
240 raise TypeError("argument det must be of type str")
241
242 def _gettime(self):
243 """
244 time property handler
245
246 returns the count time or the field name of the count time
247 or None if time is not set
248 """
249 return self._time
250
251 def _settime(self, time):
252 """
253 time property handler
254
255 sets the count time field or value
256 """
257 if isinstance(time, basestring):
258 self._time = time
259 elif isinstance(time, (float, int)):
260 self._time = float(time)
261 elif isinstance(time, type(None)):
262 self._time = None
263 else:
264 raise TypeError("argument time must be of type str, float or None")
265
266 def _getmon(self):
267 """
268 mon property handler
269
270 returns the monitor field name or None if not set
271 """
272 return self._mon
273
274 def _setmon(self, mon):
275 """
276 mon property handler
277
278 sets the monitor field name
279 """
280 if isinstance(mon, basestring):
281 self._mon = mon
282 elif isinstance(mon, type(None)):
283 self._mon = None
284 else:
285 raise TypeError("argument mon must be of type str")
286
287 def _getavmon(self):
288 """
289 av_mon property handler
290
291 returns the value of the average monitor or None
292 if average is calculated from the monitor field
293 """
294 return self._avmon
295
296 def _setavmon(self, avmon):
297 """
298 avmon property handler
299
300 sets the average monitor field name
301 """
302 if isinstance(avmon, (float, int)):
303 self._avmon = float(avmon)
304 elif isinstance(avmon, type(None)):
305 self._avmon = None
306 else:
307 raise TypeError("argument avmon must be of type float or None")
308
309 def _getabsfun(self):
310 """
311 absfun property handler
312
313 returns the costum correction function or None
314 """
315 return self._absfun
316
317 def _setabsfun(self, absfun):
318 """
319 absfun property handler
320
321 sets the costum correction function
322 """
323 if hasattr(absfun, '__call__'):
324 self._absfun = absfun
325 if self._absfun.__code__.co_argcount != 1:
326 raise TypeError("argument absfun must be a function with one "
327 "argument (data object)")
328 elif isinstance(absfun, type(None)):
329 self._absfun = None
330 else:
331 raise TypeError("argument absfun must be of type function or None")
332
333 def _getflatfield(self):
334 """
335 flatfield property handler
336
337 returns the current set flatfield of the detector
338 or None if not set
339 """
340 return self._flatfield
341
342 def _setflatfield(self, flatf):
343 """
344 flatfield property handler
345
346 sets the flatfield of the detector
347 """
348 if isinstance(flatf, (list, tuple, numpy.ndarray)):
349 self._flatfield = numpy.array(flatf, dtype=numpy.float)
350 self._flatfieldav = numpy.mean(self._flatfield[
351 self._flatfield.nonzero()])
352 self._flatfield[self.flatfield < 1.e-5] = 1.0
353 elif isinstance(flatf, type(None)):
354 self._flatfield = None
355 else:
356 raise TypeError("argument flatfield must be of type list, tuple, "
357 "numpy.ndarray or None")
358
359 def _getdarkfield(self):
360 """
361 flatfield property handler
362
363 returns the current set darkfield of the detector
364 or None if not set
365 """
366 return self._darkfield
367
368 def _setdarkfield(self, darkf):
369 """
370 flatfield property handler
371
372 sets the darkfield of the detector
373 """
374 if isinstance(darkf, (list, tuple, numpy.ndarray)):
375 self._darkfield = numpy.array(darkf, dtype=numpy.float)
376 self._darkfieldav = numpy.mean(self._darkfield)
377 elif isinstance(darkf, type(None)):
378 self._darkfield = None
379 else:
380 raise TypeError("argument flatfield must be of type list, tuple, "
381 "numpy.ndarray or None")
382
383 det = property(_getdet, _setdet)
384 time = property(_gettime, _settime)
385 mon = property(_getmon, _setmon)
386 avmon = property(_getavmon, _setavmon)
387 absfun = property(_getabsfun, _setabsfun)
388 flatfield = property(_getflatfield, _setflatfield)
389 darkfield = property(_getdarkfield, _setdarkfield)
390
391 def __call__(self, data, ccd=None):
392 """
393 apply the correction method which was initialized to the measured data
394
395 Parameters
396 ----------
397 data : numpy.recarray
398 data object from xrayutilities.io classes
399 ccd : ndarray, optional
400 optionally CCD data can be given as separate ndarray of shape
401 (len(data), n1, n2), where n1, n2 is the shape of the CCD image.
402
403 Returns
404 -------
405 corrint : ndarray
406 corrected intensity as numpy.ndarray of the same shape as data[det]
407 (or ccd.shape)
408 """
409 if numpy.any(ccd):
410 rawdata = ccd
411 else:
412 rawdata = data[self._det]
413
414 corrint = numpy.zeros(rawdata.shape, dtype=numpy.float)
415
416 # set needed variables
417 # monitor intensity
418 if self._mon:
419 if self.smoothmon == 1:
420 mon = data[self._mon]
421 else:
422 mon = math.smooth(data[self._mon], self.smoothmon)
423 else:
424 mon = 1.
425 # count time
426 if isinstance(self._time, basestring):
427 time = data[self._time]
428 elif isinstance(self._time, float):
429 time = self._time
430 else:
431 time = 1.
432 # average monitor counts
433 if self._avmon:
434 avmon = self._avmon
435 else:
436 avmon = numpy.mean(mon)
437 # absorber correction function
438 if self._absfun:
439 abscorr = self._absfun(data)
440 else:
441 abscorr = 1.
442
443 c = abscorr * avmon / (mon * time)
444 # correct the correction factor if it was evaluated to an incorrect
445 # value
446 if isinstance(c, numpy.ndarray):
447 c[numpy.isnan(c)] = 1.0
448 c[numpy.isinf(c)] = 1.0
449 c[c == 0] = 1.0
450 else:
451 if numpy.isnan(c) or numpy.isinf(c) or c == 0:
452 c = 1.0
453
454 if len(rawdata.shape) == 1:
455 corrint = rawdata * c
456 elif len(rawdata.shape) == 2 and isinstance(c, numpy.ndarray):
457 # 1D detector c.shape[0] should be rawdata.shape[0]
458 if self._darkfield is not None:
459 if self._darkfield.shape[0] != rawdata.shape[1]:
460 raise InputError("data[det] second dimension must have "
461 "the same length as darkfield")
462
463 if isinstance(time, numpy.ndarray):
464 # darkfield correction
465 corrint = (rawdata -
466 self._darkfield[numpy.newaxis, :] *
467 time[:, numpy.newaxis])
468 elif isinstance(time, float):
469 # darkfield correction
470 corrint = (rawdata -
471 self._darkfield[numpy.newaxis, :] * time)
472 else:
473 print("XU.normalize.IntensityNormalizer: check "
474 "initialization and your input")
475 return None
476 corrint[corrint < 0.] = 0.
477
478 else:
479 corrint = rawdata
480
481 corrint = corrint * c[:, numpy.newaxis]
482
483 if self._flatfield is not None:
484 if self._flatfield.shape[0] != rawdata.shape[1]:
485 raise InputError("data[det] second dimension must have "
486 "the same length as flatfield")
487 # flatfield correction
488 corrint = (corrint / self._flatfield[numpy.newaxis, :] *
489 self._flatfieldav)
490
491 elif len(rawdata.shape) == 2 and isinstance(c, numpy.float):
492 # single 2D detector frame
493 corrint = rawdata * c
494
495 elif len(rawdata.shape) == 3:
496 # darkfield and flatfield correction is still missing!
497 corrint = rawdata * c[:, numpy.newaxis, numpy.newaxis]
498
499 else:
500 raise InputError("data[det] must be an array of dimension one "
501 "or two or three")
502
503 return corrint
+0
-235
xrayutilities/q2ang_fit.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2015-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 Module provides functions to convert a q-vector from reciprocal space to
19 angular space. a simple implementation uses scipy optimize routines to perform
20 a fit for a arbitrary goniometer.
21
22 The user is, however, expected to use the bounds variable to put restrictions
23 to the number of free angles to obtain reproducible results. In general only 3
24 angles are needed to fit an arbitrary q-vector (2 sample + 1 detector angles or
25 1 sample + 2 detector). More complicated restrictions can be implemented using
26 the lmfit package. (done upon request!)
27
28 The function is based on a fitting routine. For a specific goniometer also
29 analytic expressions from literature can be used as they are implemented in the
30 predefined experimental classes HXRD, NonCOP, and GID.
31 """
32
33 import numbers
34
35 import numpy
36 import scipy.optimize
37
38 from . import config, math
39 from .exception import InputError
40
41
42 def _makebounds(boundsin):
43 """
44 generate proper bounds for scipy.optimize.minimize function
45 from a list/tuple of more convenient bounds.
46
47 Parameters
48 ----------
49 boundsin : list or tuple or array-like
50 bounds, or fixed values. the number of entries needs to be equal to the
51 number of angle in the goniometer given to the q2ang_general function
52 example input for four gonimeter angles: ((0, 90), 0, (0, 180), (0,
53 90))
54
55 Returns
56 -------
57 tuple
58 bounds to be handed over to the scipy.minimize routine. The function
59 will expand fixed values to two equal bounds
60 """
61 boundsout = []
62 for b in boundsin:
63 if isinstance(b, (tuple, list, numpy.ndarray)):
64 if len(b) == 2:
65 boundsout.append((b[0], b[1]))
66 elif len(b) == 1:
67 boundsout.append((b[0], b[0]))
68 else:
69 raise InputError('bound values must have two or one elements')
70 elif isinstance(b, numbers.Number):
71 boundsout.append((b, b)) # variable fixed
72 elif b is None:
73 boundsout.append((None, None)) # no bound
74 else:
75 raise InputError('bound value is of invalid type (%s)' % type(b))
76
77 return tuple(boundsout)
78
79
80 def _errornorm_q2ang(angles, qvec, hxrd, U=numpy.identity(3)):
81 """
82 function to determine the offset in the qposition calculated from
83 a set of experimental angles and the given vector
84
85 Parameters
86 ----------
87 angles : iterable
88 iterable object with angles of the goniometer
89 qvec : list or tuple or array-like
90 vector with three q-coordinates
91 hxrd : Experiment
92 experiment class to be used for the q calculation
93 U : array-like, optional
94 orientation matrix
95
96 Returns
97 -------
98 error : float
99 q-space error between the current fit-guess and the user-specified
100 position
101 """
102
103 qcalc = hxrd.Ang2Q.point(*angles, UB=U)
104 dq = numpy.linalg.norm(qcalc - qvec)
105 return dq
106
107
108 def exitAngleConst(angles, alphaf, hxrd):
109 """
110 helper function for an pseudo-angle constraint for the Q2AngFit-routine.
111
112 Parameters
113 ----------
114 angles : iterable
115 fit parameters of Q2AngFit
116 alphaf : float
117 the exit angle which should be fixed
118 hxrd : Experiment
119 the Experiment object to use for qconversion
120 """
121 qconv = hxrd._A2QConversion
122 # calc kf
123 detangles = [a for a in angles[-len(qconv.detectorAxis):]]
124 kf = qconv.getDetectorPos(*detangles)
125 if numpy.linalg.norm(kf) == 0:
126 af = 0
127 else:
128 ndirlab = qconv.transformSample2Lab(hxrd.Transform(hxrd.ndir), *angles)
129 af = 90 - math.VecAngle(kf, ndirlab, deg=True) - alphaf
130 return af
131
132
133 def Q2AngFit(qvec, expclass, bounds=None, ormat=numpy.identity(3),
134 startvalues=None, constraints=()):
135 """
136 Functions to convert a q-vector from reciprocal space to angular space.
137 This implementation uses scipy optimize routines to perform a fit for a
138 goniometer with arbitrary number of goniometer angles.
139
140 The user *must* use the bounds variable to put
141 restrictions to the number of free angles to obtain reproducible results.
142 In general only 3 angles are needed to fit an arbitrary q-vector (2 sample
143 + 1 detector angles or 1 sample + 2 detector).
144
145 Parameters
146 ----------
147 qvec : tuple or list or array-like
148 q-vector for which the angular positions should be calculated
149 expclass : Experiment
150 experimental class used to define the goniometer for which the angles
151 should be calculated.
152
153 bounds : tuple or list
154 bounds of the goniometer angles. The number of bounds must correspond
155 to the number of goniometer angles in the expclass. Angles can also be
156 fixed by supplying only one value for a particular angle. e.g.: ((low,
157 up), fix, (low2, up2), (low3, up3))
158 ormat : array-like
159 orientation matrix of the sample to be used in the conversion
160 startvalues : array-like
161 start values for the fit, which can significantly speed up the
162 conversion. The number of values must correspond to the number of
163 angles in the goniometer of the expclass
164 constraints : tuple
165 sequence of constraint dictionaries. This allows applying arbitrary
166 (e.g. pseudo-angle) contraints by supplying according constraint
167 functions. (see scipy.optimize.minimize). The supplied function will be
168 called with the arguments (angles, qvec, Experiment, U).
169
170 Returns
171 -------
172 fittedangles : list
173 list of fitted goniometer angles
174 qerror : float
175 error in reciprocal space
176 errcode : int
177 error-code of the scipy minimize function. for a successful fit the
178 error code should be <=2
179 """
180
181 # check input parameters
182 if len(qvec) != 3:
183 raise ValueError("XU.Q2AngFit: length of given q-vector is not 3 "
184 "-> invalid")
185 lqvec = numpy.asarray(qvec)
186
187 qconv = expclass._A2QConversion
188 nangles = len(qconv.sampleAxis) + len(qconv.detectorAxis)
189
190 # generate starting position for optimization
191 if startvalues is None:
192 start = numpy.zeros(nangles)
193 else:
194 start = startvalues
195
196 # check bounds
197 if bounds is None:
198 bounds = numpy.zeros(2 * nangles) - 180.
199 bounds[::2] = 180.
200 bounds.shape = (nangles, 2)
201 elif len(bounds) != nangles:
202 raise ValueError("XU.Q2AngFit: number of specified bounds invalid")
203
204 # perform optimization
205 res = scipy.optimize.minimize(_errornorm_q2ang, start,
206 args=(lqvec, expclass, ormat),
207 method='SLSQP', bounds=_makebounds(bounds),
208 constraints=constraints,
209 options={'maxiter': 1000,
210 'eps': config.EPSILON,
211 'ftol': config.EPSILON})
212
213 x, errcode, qerror = (res.x, res.status, res.fun)
214 if qerror >= 1e-7:
215 if config.VERBOSITY >= config.DEBUG:
216 print("XU.Q2AngFit: info: need second run")
217 # make a second run
218 res = scipy.optimize.minimize(_errornorm_q2ang, res.x,
219 args=(lqvec, expclass, ormat),
220 method='SLSQP',
221 bounds=_makebounds(bounds),
222 constraints=constraints,
223 options={'maxiter': 1000,
224 'eps': config.EPSILON,
225 'ftol': config.EPSILON})
226 x, errcode, qerror = (res.x, res.status, res.fun)
227
228 if ((config.VERBOSITY >= config.DEBUG) or (qerror > 10*config.EPSILON and
229 config.VERBOSITY >=
230 config.INFO_LOW)):
231 print("XU.Q2AngFit: q-error=%.4g with error-code %d (%s)"
232 % (qerror, errcode, res.message))
233
234 return x, qerror, errcode
+0
-43
xrayutilities/simpack/__init__.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16 """
17 simulation subpackage of xrayutilities.
18
19 This package provides possibilities to simulate X-ray diffraction and
20 reflectivity curves of thin film samples. It could be extended for more
21 general use in future if there is demand for that.
22
23 In addition it provides a fitting routine for reflectivity data which is based
24 on lmfit.
25 """
26
27 from .darwin_theory import (DarwinModel, DarwinModelAlGaAs001,
28 DarwinModelAlloy, DarwinModelGaInAs001,
29 DarwinModelSiGe001, GradedBuffer)
30 from .fit import FitModel, fit_xrr
31 from .helpers import coplanar_alphai, get_qz
32 from .models import (DiffuseReflectivityModel, DynamicalModel,
33 DynamicalReflectivityModel, KinematicalModel,
34 KinematicalMultiBeamModel, LayerModel, Model,
35 SimpleDynamicalCoplanarModel, SpecularReflectivityModel)
36 from .mosaicity import mosaic_analytic
37 from .powder import FP_profile, PowderDiffraction
38 from .powdermodel import PowderModel, Rietveld_error_metrics, plot_powder
39 from .smaterials import (CrystalStack, GradedLayerStack, Layer, LayerStack,
40 MaterialList, Powder, PowderList,
41 PseudomorphicStack001, PseudomorphicStack111,
42 SMaterial)
+0
-787
xrayutilities/simpack/darwin_theory.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16 import abc
17 import collections
18 import copy
19 import warnings
20
21 import numpy
22 from scipy.constants import physical_constants
23 from scipy.misc import derivative
24
25 from .. import materials, utilities
26 from ..math import heaviside
27 from .models import LayerModel
28
29
30 def getit(it, key):
31 """
32 generator to obtain items from nested iterable
33 """
34 for elem in it:
35 if isinstance(elem, collections.Iterable):
36 if key in elem:
37 yield elem[key]
38 else:
39 for subelem in getit(elem, key):
40 yield subelem
41
42
43 def getfirst(iterable, key):
44 """
45 helper function to obtain the first item in a nested iterable
46 """
47 return next(getit(iterable, key))
48
49
50 def GradedBuffer(xfrom, xto, nsteps, thickness, relaxation=1):
51 """
52 create a multistep graded composition buffer.
53
54 Parameters
55 ----------
56 xfrom : float
57 begin of the composition gradient
58 xto : float
59 end of the composition gradient
60 nsteps : int
61 number of steps of the gradient
62 thickness : float
63 total thickness of the Buffer in A
64 relaxation : float
65 relaxation of the buffer
66
67 Returns
68 -------
69 list
70 layer list needed for the Darwin model simulation
71 """
72 subthickness = thickness/nsteps
73 gradedx = numpy.linspace(xfrom, xto, nsteps)
74 layerlist = []
75 for x in gradedx:
76 layerlist.append({'t': subthickness, 'x': x, 'r': relaxation})
77 return layerlist
78
79
80 class DarwinModel(LayerModel):
81 """
82 model class inmplementing the basics of the Darwin theory for layers
83 materials. This class is not fully functional and should be used to derive
84 working models for particular material systems.
85
86 To make the class functional the user needs to implement the
87 init_structurefactors() and _calc_mono() methods
88 """
89 ncalls = 0
90
91 def __init__(self, qz, qx=0, qy=0, **kwargs):
92 """
93 constructor of the model class. The arguments consist of basic
94 parameters which are needed to prepare the calculation of the model.
95
96 Parameters
97 ----------
98 qz : array-like
99 momentum transfer values for the calculation
100 qx, qy : float, optional
101 inplane momentum transfer (not implemented!)
102 I0 : float, optional
103 the primary beam intensity
104 background : float, optional
105 the background added to the simulation
106 resolution_width : float, optional
107 width of the resolution function (deg)
108 polarization : {'S', 'P', 'both'}
109 polarization of the x-ray beam. If set to 'both' also Cmono, the
110 polarization factor of the monochromator should be set
111 experiment : Experiment, optional
112 experiment class containing geometry and energy of the experiment.
113 Cmono : float, optional
114 polarization factor of the monochromator
115 energy : float or str, optional
116 x-ray energy in eV
117 """
118 self.polarization = kwargs.pop('polarization', 'S')
119 exp = kwargs.pop('experiment', None)
120 self.Cmono = kwargs.pop('Cmono', 1)
121 super(LayerModel, self).__init__(exp, **kwargs)
122
123 self.npoints = len(qz)
124 self.qz = numpy.asarray(qz)
125 self.qinp = (qx, qy)
126 if self.qinp != (0, 0):
127 raise NotImplementedError('asymmetric CTR simulation is not yet '
128 'supported -> approach the authors')
129 self.init_structurefactors()
130 # initialize coplanar geometry
131 k = self.exp.k0
132 qv = numpy.asarray([(qx, qy, q) for q in self.qz])
133 Q = numpy.linalg.norm(qv, axis=1)
134 theta = numpy.arcsin(Q / (2 * k))
135 domega = numpy.arctan2(numpy.sqrt(qx**2 + qy**2), self.qz)
136 self.alphai, self.alphaf = (theta + domega, theta - domega)
137 # polarization factor
138 self.C = {'S': numpy.ones(len(self.qz)),
139 'P': numpy.abs(numpy.cos(self.alphai + self.alphaf))}
140
141 def init_structurefactors(self):
142 """
143 calculates the needed atomic structure factors
144 """
145 pass
146
147 def _calc_mono(self, pdict, pol):
148 """
149 calculate the reflection and transmission coefficients of monolayer
150
151 Parameters
152 ----------
153 pdict : dict
154 property dictionary, contains all the properties for the structure
155 factor calculation
156 pol : {'S', 'P'}
157 polarization of the x-rays; sigma or pi
158
159 Returns
160 -------
161 r, rbar, t : float or array-like
162 reflection, backside reflection, and tranmission coefficients
163 """
164 pass
165
166 def _calc_double(self, ra, rabar, ta, rb, rbbar, tb, d):
167 """
168 calculate reflection coefficients for the double layer from the
169 reflection values of the single layers
170
171 Parameters
172 ----------
173 ra, rabar, ta : float or array-like
174 reflection, backside reflection, and transmission coefficients of
175 layer A
176 rb, rbbar, tb : float or array-like
177 same for layer B
178 d : float
179 distance between the layers
180
181 Returns
182 -------
183 r, rbar, t : float or array-like
184 reflection, backside reflection, and tranmission coefficients
185 """
186 self.ncalls += 1
187 e = numpy.exp(-1j*self.qz*d)
188 eh = numpy.exp(-1j*self.qz*d/2)
189 denom = (1-rabar*rb*e)
190 rab = ra + rb*(ta*ta*e)/denom
191 rabbar = rbbar + rabar*(tb*tb*e)/(1-rbbar*ra*e)
192 tab = ta*tb*eh/denom
193 return rab, rabbar, tab
194
195 def simulate(self, ml):
196 """
197 main simulation function for the Darwin model. will calculate the
198 reflected intensity
199
200 Parameters
201 ----------
202 ml : iterable
203 monolayer sequence of the sample. This should be created with the
204 function make_monolayer(). see its documentation for details
205 """
206 self.ncalls = 0
207 Ih = {'S': numpy.zeros(len(self.qz)), 'P': numpy.zeros(len(self.qz))}
208 geomfact = heaviside(self.alphai) * heaviside(self.alphaf)
209 for pol in self.get_polarizations():
210 r, rbar, t = (numpy.zeros(self.npoints, dtype=numpy.complex),
211 numpy.zeros(self.npoints, dtype=numpy.complex),
212 numpy.ones(self.npoints, dtype=numpy.complex))
213 for nrep, subml in ml:
214 r, rbar, t = self._recur_sim(nrep, subml, r, rbar, t, pol)
215 self.r, self.rbar, self.t = r, rbar, t
216 Ih[pol] = numpy.abs(self.r)**2 * geomfact
217
218 ret = self.join_polarizations(Ih['S'], Ih['P'])
219 return self._create_return(self.qz, numpy.sqrt(ret))
220
221 def _recur_sim(self, nrep, ml, r, rbar, t, pol):
222 """
223 recursive simulation function for the calculation of the reflected,
224 backside reflected and transmitted wave factors (internal)
225
226 Parameters
227 ----------
228 ml : iterable
229 monolayer sequence of the calculation block. This should be created
230 with the function make_monolayer(). see its documentation for
231 details
232 r : float or array-like
233 reflection factor of the upper layers (needed for the recursion)
234 rbar : float or array-like
235 back-side reflection factor of the upper layers (needed for the
236 recursion)
237 t : float or array-like
238 transmission factor of the upper layers (needed for the recursion)
239 pol : {'S', 'P'}
240 polarization of the x-rays
241
242 Returns
243 -------
244 r, rbar, t : float or array-like
245 reflection and transmission of the full stack
246 """
247 if isinstance(ml, list):
248 rm, rmbar, tm = (None, None, None)
249 for nsub, subml in ml:
250 rm, rmbar, tm = self._recur_sim(nsub, subml, rm,
251 rmbar, tm, pol)
252 d = getfirst(ml, 'd')
253 else:
254 rm, rmbar, tm = self._calc_mono(ml, pol)
255 d = ml['d']
256
257 Nmax = int(numpy.log(nrep) / numpy.log(2)) + 1
258 for i in range(Nmax):
259 if r is None:
260 r, rbar, t = rm, rmbar, tm
261 elif (nrep // (2**i)) % 2 == 1:
262 r, rbar, t = self._calc_double(r, rbar, t, rm, rmbar, tm, d)
263 rm, rmbar, tm = self._calc_double(rm, rmbar, tm, rm, rmbar, tm, d)
264
265 return r, rbar, t
266
267
268 class DarwinModelAlloy(DarwinModel, utilities.ABC):
269 """
270 extension of the DarwinModel for an binary alloy system were one parameter
271 is used to determine the chemical composition
272
273 To make the class functional the user needs to implement the
274 get_dperp_apar() method and define the substrate lattice parameter (asub).
275 See the DarwinModelSiGe001 class for an implementation example.
276 """
277 @abc.abstractmethod
278 def get_dperp_apar(self, x, apar, r=1):
279 """
280 calculate inplane lattice parameter and the out of plane lattice plane
281 spacing (of the atomic planes!) from composition and relaxation.
282
283 Parameters
284 ----------
285 x : float
286 chemical composition parameter
287 apar : float
288 inplane lattice parameter of the material below the current layer
289 (onto which the present layer is strained to). This value also
290 served as a reference for the relaxation parameter.
291 r : float
292 relaxation parameter. 1=relaxed, 0=pseudomorphic
293
294 Returns
295 -------
296 dperp : float
297 apar : float
298 """
299 raise NotImplementedError("abstract method needs to be overwritten")
300
301 def make_monolayers(self, s):
302 """
303 create monolayer sequence from layer list
304
305 Parameters
306 ----------
307 s : list
308 layer model. list of layer dictionaries including possibility to
309 form superlattices. As an example 5 repetitions of a
310 Si(10nm)/Ge(15nm) superlattice on Si would like like:
311
312 >>> s = [(5, [{'t': 100, 'x': 0, 'r': 0},
313 >>> {'t': 150, 'x': 1, 'r': 0}]),
314 >>> {'t': 3500000, 'x': 0, 'r': 0}]
315
316 the dictionaries must contain 't': thickness in A, 'x': chemical
317 composition, and either 'r': relaxation or 'ai': inplane lattice
318 parameter.
319 Future implementations for asymmetric peaks might include layer
320 type 'l' (not yet implemented). Already now any additional property
321 in the dictionary will be handed on to the returned monolayer list.
322 asub : float
323 inplane lattice parameter of the substrate
324
325 Returns
326 -------
327 list
328 monolayer list in a format understood by the simulate and
329 xGe_profile methods
330 """
331 ml = []
332 ai = self.asub
333 for subl in copy.copy(s):
334 ml, ai = self._recur_makeml(subl, ml, ai)
335 return ml
336
337 def _recur_makeml(self, s, ml, apar):
338 """
339 recursive creation of a monolayer structure (internal)
340
341 Parameters
342 ----------
343 s : list
344 layer model. list of layer dictionaries
345 ml : list
346 list of layers below what should be added (needed for recursion)
347 apar : float
348 inplane lattice parameter of the current surface
349
350 Returns
351 -------
352 list
353 monolayer list in a format understood by the simulate and
354 prop_profile methods
355 """
356
357 if isinstance(s, tuple):
358 nrep, sd = s
359 if isinstance(sd, dict):
360 sd = [sd, ]
361 if any([r > 0 for r in getit(sd, 'r')]): # if relaxation
362 for i in range(nrep):
363 for subsd in sd:
364 ml, apar = self._recur_makeml(subsd, ml, apar=apar)
365 else: # no relaxation in substructure
366 subl = []
367 for subsd in sd:
368 subl, apar = self._recur_makeml(subsd, subl, apar=apar)
369 ml.insert(0, (nrep, subl))
370 elif isinstance(s, dict):
371 x = s.pop('x')
372 if callable(x): # composition profile in layer
373 t = 0
374 T = s.pop('t')
375 if 'r' in s:
376 if s['r'] > 0:
377 warnings.warn(
378 """relaxation for composition gradient may yield
379 weird lattice parameter variation! Consider
380 supplying the inplane lattice parameter 'ai'
381 directly!""")
382 while t < T:
383 if 'r' in s:
384 r = abs(derivative(x, t, dx=1.4, n=1))*s['r']
385 dperp, apar = self.get_dperp_apar(x(t), apar, r)
386 else:
387 dperp, apar = self.get_dperp_apar(x(t), s['ai'])
388 t += dperp
389 d = copy.copy(s)
390 d.pop('r')
391 d.update({'d': dperp, 'x': x(t), 'ai': apar})
392 ml.insert(0, (1, d))
393 else: # constant composition layer
394 if 'r' in s:
395 dperp, apar = self.get_dperp_apar(x, apar, s.pop('r'))
396 else:
397 dperp, apar = self.get_dperp_apar(x, s.pop('ai'))
398 nmono = int(numpy.ceil(s['t']/dperp))
399 s.update({'d': dperp, 'x': x, 'ai': apar})
400 ml.insert(0, (nmono, s))
401 else:
402 raise Exception('wrong type (%s) of sublayer, must be tuple or'
403 ' dict' % (type(s)))
404 return ml, apar
405
406 def prop_profile(self, ml, prop):
407 """
408 calculate the profile of chemical composition or inplane lattice
409 spacing from a monolayer list. One value for each monolayer in the
410 sample is returned.
411
412 Parameters
413 ----------
414 ml : list
415 monolayer list created by make_monolayer()
416 prop : str
417 name of the property which should be evaluated. Use 'x' for the
418 chemical composition and 'ai' for the inplane lattice parameter.
419
420 Returns
421 -------
422 zm : ndarray
423 z-position, z-0 is the surface
424 propx : ndarray
425 value of the property prop for every monolayer
426 """
427
428 def startinterval(start, inter, N):
429 return numpy.arange(start, start+inter*(N+0.5), inter)
430
431 def _recur_prop(nrep, ml, zp, propx, propn):
432 if isinstance(ml, list):
433 lzp, lprop = ([], [])
434 for nreps, subml in ml:
435 lzp, lprop = _recur_prop(nreps, subml, lzp, lprop, propn)
436 d = getfirst(ml, 'd')
437 else:
438 lzp = -ml['d']
439 lprop = ml[propn]
440 d = ml['d']
441
442 Nmax = int(numpy.log(nrep) / numpy.log(2)) + 1
443 for i in range(Nmax):
444 if (nrep // (2**i)) % 2 == 1:
445 try:
446 curzp = zp[-1]
447 except IndexError:
448 curzp = 0.0
449 zp = numpy.append(zp, lzp+curzp)
450 propx = numpy.append(propx, lprop)
451 try:
452 curlzp = lzp[-1]
453 except IndexError:
454 curlzp = lzp
455 lzp = numpy.append(lzp, lzp+curlzp)
456 lprop = numpy.append(lprop, lprop)
457 return zp, propx
458
459 zm = []
460 propx = []
461 for nrep, subml in ml:
462 zm, propx = _recur_prop(nrep, subml, zm, propx, prop)
463 return zm, propx
464
465
466 class DarwinModelSiGe001(DarwinModelAlloy):
467 """
468 model class implementing the Darwin theory of diffraction for SiGe layers.
469 The model is based on separation of the sample structure into building
470 blocks of atomic planes from which a multibeam dynamical model is
471 calculated.
472 """
473 Si = materials.Si
474 Ge = materials.Ge
475 eSi = materials.elements.Si
476 eGe = materials.elements.Ge
477 aSi = materials.Si.a1[0]
478 asub = aSi # needed for the make_monolayer function
479 re = physical_constants['classical electron radius'][0] * 1e10
480
481 @classmethod
482 def abulk(cls, x):
483 """
484 calculate the bulk (relaxed) lattice parameter of the alloy
485 """
486 return cls.aSi + (0.2 * x + 0.027 * x ** 2)
487
488 @staticmethod
489 def poisson_ratio(x):
490 """
491 calculate the Poisson ratio of the alloy
492 """
493 return 2 * (63.9-15.6*x) / (165.8-37.3*x) # according to IOFFE
494
495 @classmethod
496 def get_dperp_apar(cls, x, apar, r=1):
497 """
498 calculate inplane lattice parameter and the out of plane lattice plane
499 spacing (of the atomic planes!) from composition and relaxation
500
501 Parameters
502 ----------
503 x : float
504 chemical composition parameter
505 apar : float
506 inplane lattice parameter of the material below the current layer
507 (onto which the present layer is strained to). This value also
508 served as a reference for the relaxation parameter.
509 r : float, optional
510 relaxation parameter. 1=relaxed, 0=pseudomorphic
511
512 Returns
513 -------
514 dperp : float
515 perpendicular d-spacing
516 apar : float
517 inplane lattice parameter
518 """
519 abulk = cls.abulk(x)
520 aparl = apar + (abulk - apar) * r
521 dperp = abulk*(1+cls.poisson_ratio(x)*(1-aparl/abulk))/4.
522 return dperp, aparl
523
524 def init_structurefactors(self, temp=300):
525 """
526 calculates the needed atomic structure factors
527
528 Parameters
529 ----------
530 temp : float, optional
531 temperature used for the Debye model
532 """
533 en = self.exp.energy
534 q = numpy.sqrt(self.qinp[0]**2 + self.qinp[1]**2 + self.qz**2)
535 self.fSi = self.eSi.f(q, en) * self.Si._debyewallerfactor(temp, q)
536 self.fGe = self.eGe.f(q, en) * self.Ge._debyewallerfactor(temp, q)
537 self.fSi0 = self.eSi.f(0, en)
538 self.fGe0 = self.eGe.f(0, en)
539
540 def _calc_mono(self, pdict, pol):
541 """
542 calculate the reflection and transmission coefficients of monolayer
543
544 Parameters
545 ----------
546 pdict : dict
547 property dictionary, contains the layer properties:
548 'x': Ge-content of the layer (0: Si, 1: Ge);
549 'l': index of the layer in the unit cell (0, 1, 2, 3). important
550 for asymmetric peaks only!
551 pol : {'S', 'P'}
552 polarization of the x-rays
553
554 Returns
555 -------
556 r, rbar, t : float or array-like
557 reflection, backside reflection, and tranmission coefficients
558 """
559 ainp = pdict.get('ai')
560 xGe = pdict.get('x')
561 # pre-factor for reflection: contains footprint correction
562 gamma = 4*numpy.pi*self.re/(self.qz*ainp**2)
563 # ltype = pdict.get('l', 0)
564 # if ltype == 0: # for asymmetric peaks (not yet implemented)
565 # p1, p2 = (0, 0), (0.5, 0.5)
566 # elif ltype == 1:
567 # p1, p2 = (0.25, 0.25), (0.75, 0.75)
568 # elif ltype == 2:
569 # p1, p2 = (0.5, 0.), (0., 0.5)
570 # elif ltype == 3:
571 # p1, p2 = (0.75, 0.25), (0.25, 0.75)
572
573 r = -1j*gamma * self.C[pol] * (self.fSi+(self.fGe-self.fSi)*xGe) * 2
574 # * (exp(1j*(h*p1[0]+k*p1[1])) + exp(1j*(h*p1[0]+k*p1[1])))
575 t = 1 + 1j*gamma * (self.fSi0+(self.fGe0-self.fSi0)*xGe) * 2
576 return r, numpy.copy(r), t
577
578
579 class DarwinModelGaInAs001(DarwinModelAlloy):
580 """
581 Darwin theory of diffraction for Ga_{1-x} In_x As layers.
582 The model is based on separation of the sample structure into building
583 blocks of atomic planes from which a multibeam dynamical model is
584 calculated.
585 """
586 GaAs = materials.GaAs
587 InAs = materials.InAs
588 eGa = materials.elements.Ga
589 eIn = materials.elements.In
590 eAs = materials.elements.As
591 aGaAs = materials.GaAs.a1[0]
592 asub = aGaAs # needed for the make_monolayer function
593 re = physical_constants['classical electron radius'][0] * 1e10
594
595 @classmethod
596 def abulk(cls, x):
597 """
598 calculate the bulk (relaxed) lattice parameter of the Ga_{1-x}In_{x}As
599 alloy
600 """
601 return cls.aGaAs + 0.40505*x
602
603 @staticmethod
604 def poisson_ratio(x):
605 """
606 calculate the Poisson ratio of the alloy
607 """
608 return 2 * (4.54 + 0.8*x) / (8.34 + 3.56*x) # according to IOFFE
609
610 @classmethod
611 def get_dperp_apar(cls, x, apar, r=1):
612 """
613 calculate inplane lattice parameter and the out of plane lattice plane
614 spacing (of the atomic planes!) from composition and relaxation
615
616 Parameters
617 ----------
618 x : float
619 chemical composition parameter
620 apar : float
621 inplane lattice parameter of the material below the current layer
622 (onto which the present layer is strained to). This value also
623 served as a reference for the relaxation parameter.
624 r : float
625 relaxation parameter. 1=relaxed, 0=pseudomorphic
626
627 Returns
628 -------
629 dperp : float
630 perpendicular d-spacing
631 apar : float
632 inplane lattice parameter
633 """
634 abulk = cls.abulk(x)
635 aparl = apar + (abulk - apar) * r
636 dperp = abulk*(1+cls.poisson_ratio(x)*(1-aparl/abulk))/4.
637 return dperp, aparl
638
639 def init_structurefactors(self, temp=300):
640 """
641 calculates the needed atomic structure factors
642
643 Parameters
644 ----------
645 temp : float, optional
646 temperature used for the Debye model
647 """
648 en = self.exp.energy
649 q = numpy.sqrt(self.qinp[0]**2 + self.qinp[1]**2 + self.qz**2)
650 fAs = self.eAs.f(q, en)
651 self.fGaAs = (self.eGa.f(q, en) + fAs) \
652 * self.GaAs._debyewallerfactor(temp, q)
653 self.fInAs = (self.eIn.f(q, en) + fAs) \
654 * self.InAs._debyewallerfactor(temp, q)
655 self.fGaAs0 = self.eGa.f(0, en) + self.eAs.f(0, en)
656 self.fInAs0 = self.eIn.f(0, en) + self.eAs.f(0, en)
657
658 def _calc_mono(self, pdict, pol):
659 """
660 calculate the reflection and transmission coefficients of monolayer
661
662 Parameters
663 ----------
664 pdict : dict
665 property dictionary, contains the layer properties:
666 'x': In-content of the layer (0: GaAs, 1: InAs)
667 pol : {'S', 'P'}
668 polarization of the x-rays
669
670 Returns
671 -------
672 r, rbar, t : float or array-like
673 reflection, backside reflection, and tranmission coefficients
674 """
675 ainp = pdict.get('ai')
676 xInAs = pdict.get('x')
677 # pre-factor for reflection: contains footprint correction
678 gamma = 4*numpy.pi * self.re/(self.qz*ainp**2)
679 r = -1j*gamma*self.C[pol]*(self.fGaAs+(self.fInAs-self.fGaAs)*xInAs)
680 t = 1 + 1j*gamma * (self.fGaAs0+(self.fInAs0-self.fGaAs0)*xInAs)
681 return r, numpy.copy(r), t
682
683
684 class DarwinModelAlGaAs001(DarwinModelAlloy):
685 """
686 Darwin theory of diffraction for Al_x Ga_{1-x} As layers.
687 The model is based on separation of the sample structure into building
688 blocks of atomic planes from which a multibeam dynamical model is
689 calculated.
690 """
691 GaAs = materials.GaAs
692 AlAs = materials.AlAs
693 eGa = materials.elements.Ga
694 eAl = materials.elements.Al
695 eAs = materials.elements.As
696 aGaAs = materials.GaAs.a1[0]
697 asub = aGaAs # needed for the make_monolayer function
698 re = physical_constants['classical electron radius'][0] * 1e10
699
700 @classmethod
701 def abulk(cls, x):
702 """
703 calculate the bulk (relaxed) lattice parameter of the Al_{x}Ga_{1-x}As
704 alloy
705 """
706 return cls.aGaAs + 0.0078*x
707
708 @staticmethod
709 def poisson_ratio(x):
710 """
711 calculate the Poisson ratio of the alloy
712 """
713 return 2 * (5.38+0.32*x) / (11.88+0.14*x) # according to IOFFE
714
715 @classmethod
716 def get_dperp_apar(cls, x, apar, r=1):
717 """
718 calculate inplane lattice parameter and the out of plane lattice plane
719 spacing (of the atomic planes!) from composition and relaxation
720
721 Parameters
722 ----------
723 x : float
724 chemical composition parameter
725 apar : float
726 inplane lattice parameter of the material below the current layer
727 (onto which the present layer is strained to). This value also
728 served as a reference for the relaxation parameter.
729 r : float
730 relaxation parameter. 1=relaxed, 0=pseudomorphic
731
732 Returns
733 -------
734 dperp : float
735 perpendicular d-spacing
736 apar : float
737 inplane lattice parameter
738 """
739 abulk = cls.abulk(x)
740 aparl = apar + (abulk - apar) * r
741 dperp = abulk*(1+cls.poisson_ratio(x)*(1-aparl/abulk))/4.
742 return dperp, aparl
743
744 def init_structurefactors(self, temp=300):
745 """
746 calculates the needed atomic structure factors
747
748 Parameters
749 ----------
750 temp : float, optional
751 temperature used for the Debye model
752 """
753 en = self.exp.energy
754 q = numpy.sqrt(self.qinp[0]**2 + self.qinp[1]**2 + self.qz**2)
755 fAs = self.eAs.f(q, en)
756 self.fGaAs = (self.eGa.f(q, en) + fAs) \
757 * self.GaAs._debyewallerfactor(temp, q)
758 self.fAlAs = (self.eAl.f(q, en) + fAs) \
759 * self.AlAs._debyewallerfactor(temp, q)
760 self.fGaAs0 = self.eGa.f(0, en) + self.eAs.f(0, en)
761 self.fAlAs0 = self.eAl.f(0, en) + self.eAs.f(0, en)
762
763 def _calc_mono(self, pdict, pol):
764 """
765 calculate the reflection and transmission coefficients of monolayer
766
767 Parameters
768 ----------
769 pdict : dict
770 property dictionary, contains the layer properties:
771 'x': Al-content of the layer (0: GaAs, 1: AlAs)
772 pol : {'S', 'P'}
773 polarization of the x-rays
774
775 Returns
776 -------
777 r, rbar, t : float or array-like
778 reflection, backside reflection, and tranmission coefficients
779 """
780 ainp = pdict.get('ai')
781 xAlAs = pdict.get('x')
782 # pre-factor for reflection: contains footprint correction
783 gamma = 4*numpy.pi * self.re/(self.qz*ainp**2)
784 r = -1j*gamma*self.C[pol]*(self.fGaAs+(self.fAlAs-self.fGaAs)*xAlAs)
785 t = 1 + 1j*gamma * (self.fGaAs0+(self.fAlAs0-self.fGaAs0)*xAlAs)
786 return r, numpy.copy(r), t
+0
-522
xrayutilities/simpack/fit.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import warnings
18
19 import numpy
20
21 from .. import config, utilities
22 from ..exception import InputError
23 from . import models
24
25 # python 2to3 compatibility
26 try:
27 basestring
28 except NameError:
29 basestring = str
30
31
32 def fit_xrr(reflmod, params, ai, data=None, eps=None, xmin=-numpy.inf,
33 xmax=numpy.inf, plot=False, verbose=False, elog=True, maxfev=500):
34 """
35 optimize function for a Reflectivity Model using lmfit. The fitting
36 parameters must be specified as instance of lmfits Parameters class.
37
38 Parameters
39 ----------
40 reflmod : SpecularReflectivityModel
41 preconfigured model used for the fitting
42 params : lmfit.Parameters
43 instance of lmfits Parameters class. For every layer the parameters
44 '{}_thickness', '{}_roughness', '{}_density', with '{}' representing
45 the layer name are supported. In addition the setup parameters:
46
47 - 'I0' primary beam intensity
48 - 'background' background added to the simulation
49 - 'sample_width' size of the sample along the beam
50 - 'beam_width' width of the beam in the same units
51 - 'resolution_width' width of the resolution function in deg
52 - 'shift' experimental shift of the incidence angle array
53
54 ai : array-like
55 array of incidence angles for the calculation
56 data : array-like
57 experimental data which should be fitted
58 eps : array-like, optional
59 error bar of the data
60 xmin : float, optional
61 minimum value of ai which should be used. a mask is generated to cut
62 away other data
63 xmax : float, optional
64 maximum value of ai which should be used. a mask is generated to cut
65 away other data
66 plot : bool, optional
67 flag to decide wheter an plot should be created showing the fit's
68 progress. If plot is a string it will be used as figure name, which
69 makes reusing the figures easier.
70 verbose : bool, optional
71 flag to tell if the variation of the fitting error should be output
72 during the fit.
73 elog : bool, optional
74 logarithmic error during the fit
75 maxfev : int, optional
76 maximum number of function evaluations during the leastsq optimization
77
78 Returns
79 -------
80 res : lmfit.MinimizerResult
81 object from lmfit, which contains the fitted parameters in res.params
82 (see res.params.pretty_print) or try lmfit.report_fit(res)
83 """
84 warnings.warn("deprecated function -> change to FitModel",
85 DeprecationWarning)
86 lmfit = utilities.import_lmfit('XU.simpack')
87
88 if plot:
89 plot, plt = utilities.import_matplotlib_pyplot('XU.simpack')
90
91 mask = numpy.logical_and(ai > xmin, ai < xmax)
92 # check Parameters
93 lstack = reflmod.lstack
94 if not isinstance(params, lmfit.Parameters):
95 raise TypeError('params argument must be of type lmfit.Parameters')
96 for lname, l in zip(lstack.namelist, lstack):
97 pname = '{}_thickness'.format(lname)
98 if pname not in params:
99 raise InputError('XU.simpack.fit_xrr: Parameter %s not defined.'
100 % pname)
101 pname = '{}_roughness'.format(lname)
102 if pname not in params:
103 params.add(pname, value=l.roughness, vary=False)
104 if config.VERBOSITY >= config.INFO_LOW:
105 print("XU.simpack.fit_xrr: adding fixed parameter %s to model"
106 % pname)
107 pname = '{}_density'.format(lname)
108 if pname not in params:
109 params.add(pname, value=l.density, vary=False)
110 if config.VERBOSITY >= config.INFO_LOW:
111 print("XU.simpack.fit_xrr: adding fixed parameter %s to model"
112 % pname)
113
114 # residual function
115 def xrr_residual(pars, ai, reflmod, **kwargs):
116 """
117 residual function for lmfit Minimizer routine
118
119 Parameters
120 ----------
121 pars : lmfit.Parameters
122 fit parameters
123 ai : array-like
124 array of incidence angles
125 reflmod : SpecularReflectivityModel
126 reflectivity model object
127 data : array-like, optional
128 experimental data of same shape as ai (default: None)
129 eps : array-like
130 experimental error bars of same shape as ai (default: None)
131 """
132 data = kwargs.get('data', None)
133 eps = kwargs.get('eps', None)
134 pvals = pars.valuesdict()
135 # update reflmod global parameters:
136 reflmod.I0 = pvals.get('I0', reflmod.I0)
137 reflmod.background = pvals.get('background', reflmod.background)
138 reflmod.sample_width = pvals.get('sample_width', reflmod.sample_width)
139 reflmod.beam_width = pvals.get('beam_width', reflmod.beam_width)
140 reflmod.resolution_width = pvals.get('resolution_width',
141 reflmod.resolution_width)
142 shift = pvals.get('shift', 0)
143 # update layer properties
144 for lname, l in zip(reflmod.lstack.namelist, reflmod.lstack):
145 l.thickness = pvals['{}_thickness'.format(lname)]
146 l.roughness = pvals['{}_roughness'.format(lname)]
147 l.density = pvals['{}_density'.format(lname)]
148 # run simulation
149 model = reflmod.simulate(ai - shift)
150 if data is None:
151 return model
152 if kwargs['elog']:
153 logmodel = numpy.log10(model)
154 logdata = numpy.log10(data)
155 mask = numpy.logical_and(numpy.isfinite(logmodel),
156 numpy.isfinite(logdata))
157 return logmodel[mask] - logdata[mask]
158 if eps is None:
159 return (model - data)
160 return (model - data)/eps
161
162 # plot of initial values
163 if plot:
164 plt.ion()
165 if isinstance(plot, basestring):
166 plt.figure(plot)
167 else:
168 plt.figure('XU:fit_xrr')
169 plt.clf()
170 ax = plt.subplot(111)
171 ax.set_yscale("log", nonposy='clip')
172 if data is not None:
173 if eps is not None:
174 eline = plt.errorbar(ai, data, yerr=eps, ecolor='0.3',
175 fmt='ko', errorevery=int(ai.size/80),
176 label='data')[0]
177 else:
178 eline, = plt.semilogy(ai, data, 'ko', label='data')
179 if verbose:
180 init, = plt.semilogy(ai,
181 xrr_residual(params, ai, reflmod, data=None),
182 '-', color='0.5', label='initial')
183 if eline:
184 zord = eline.zorder+2
185 else:
186 zord = 1
187 fline, = plt.semilogy(
188 ai[mask], xrr_residual(params, ai[mask], reflmod, data=None),
189 'r-', lw=2, label='fit', zorder=zord)
190 plt.legend()
191 plt.xlabel('incidence angle (deg)')
192 plt.ylabel('Intensity (arb. u.)')
193 plt.show()
194 else:
195 fline = None
196
197 # create and run minimizer/minimization
198 if eps is None:
199 eps = numpy.ones(ai.shape)
200
201 def cb_func(params, niter, resid, ai, reflmod, **kwargs):
202 if kwargs.get('verbose', False):
203 print('{:04d} {:12.3e}'.format(niter, numpy.sum(resid**2)))
204 if kwargs.get('plot', False) and niter % 20 == 0:
205 fl = kwargs['fline']
206 plt.sca(ax)
207 fl.set_ydata(xrr_residual(params, ai, reflmod, data=None))
208 plt.draw()
209 plt.pause(0.001) # enable better mpl backend compatibility
210
211 minimizer = lmfit.Minimizer(
212 xrr_residual, params, fcn_args=(ai[mask], reflmod),
213 fcn_kws={'data': data[mask], 'eps': eps[mask], 'fline': fline,
214 'verbose': verbose, 'plot': plot, 'elog': elog},
215 iter_cb=cb_func, maxfev=maxfev)
216 res = minimizer.minimize()
217
218 # final update of plot
219 if plot:
220 plt.sca(ax)
221 plt.semilogy(ai, xrr_residual(res.params, ai, reflmod, data=None),
222 'g-', lw=1, label='fit', zorder=fline.zorder-1)
223 cb_func(res.params, 0, res.residual, ai[mask], reflmod, fline=fline,
224 plot=True)
225
226 return res
227
228
229 class FitModel(object):
230 """
231 Wrapper for the lmfit Model class working for instances of LayerModel
232
233 Typically this means that after initialization of `FitModel` you want to
234 use make_params to get a `lmfit.Parameters` list which one customizes for
235 fitting.
236
237 Later on you can call `fit` and `eval` methods with those parameter list.
238 """
239 def __init__(self, lmodel, verbose=False, plot=False, elog=True, **kwargs):
240 """
241 initialization of a FitModel which uses lmfit for the actual fitting,
242 and generates an according lmfit.Model internally for the given
243 pre-configured LayerModel, or subclasses thereof which includes models
244 for reflectivity, kinematic and dynamic diffraction.
245
246 Parameters
247 ----------
248 lmodel : LayerModel
249 pre-configured instance of LayerModel or any subclass
250 verbose : bool, optional
251 flag to enable verbose printing during fitting
252 plot : bool or str, optional
253 flag to decide wheter an plot should be created showing the fit's
254 progress. If plot is a string it will be used as figure name, which
255 makes reusing the figures easier.
256 elog : bool, optional
257 flag to enable a logarithmic error metric between model and data.
258 Since the dynamic range of data is often large its often benefitial
259 to keep this enabled.
260 kwargs : dict, optional
261 additional keyword arguments are forwarded to the `simulate` method
262 of `lmodel`
263 """
264 self.verbose = verbose
265 self.plot = plot
266 self.elog = elog
267 lmfit = utilities.import_lmfit('XU.simpack')
268
269 assert isinstance(lmodel, models.LayerModel)
270 self.lmodel = lmodel
271 # generate dynamic function for model evalution
272 funcstr = "def func(x, "
273 # add LayerModel parameters
274 for p in self.lmodel.fit_paramnames:
275 funcstr += "{}, ".format(p)
276 # add LayerStack parameters
277 for l in self.lmodel.lstack:
278 for param in self.lmodel.lstack_params:
279 funcstr += '{}_{}, '.format(l.name, param)
280 if self.lmodel.lstack_structural_params:
281 for param in l._structural_params:
282 funcstr += '{}_{}, '.format(l.name, param)
283 funcstr += "lmodel=self.lmodel, **kwargs):\n"
284 # define modelfunc content
285 for p in self.lmodel.fit_paramnames:
286 funcstr += " setattr(lmodel, '{}', {})\n".format(p, p)
287 for i, l in enumerate(self.lmodel.lstack):
288 for param in self.lmodel.lstack_params:
289 varname = '{}_{}'.format(l.name, param)
290 cmd = " setattr(lmodel.lstack[{}], '{}', {})\n"
291 funcstr += cmd.format(i, param, varname)
292 if self.lmodel.lstack_structural_params:
293 for param in l._structural_params:
294 varname = '{}_{}'.format(l.name, param)
295 cmd = " setattr(lmodel.lstack[{}], '{}', {})\n"
296 funcstr += cmd.format(i, param, varname)
297 # perform actual model calculation
298 funcstr += " return lmodel.simulate(x, **kwargs)"
299
300 namespace = {'self': self}
301 exec(funcstr, {'lmodel': self.lmodel}, namespace)
302 self.func = namespace['func']
303 self.emetricfunc = numpy.log10 if self.elog else lambda x: x
304
305 def _residual(params, data, weights, **kwargs):
306 """
307 Return the residual. This is a (simplified, only real values)
308 reimplementation of the lmfit.Model._residual function which adds
309 the possibility of a logarithmic error metric.
310
311 Default residual: (data-model)*weights.
312 """
313 scale = self.emetricfunc
314 model = scale(self.eval(params, **kwargs))
315 sdata = scale(data)
316 mask = numpy.logical_and(numpy.isfinite(model),
317 numpy.isfinite(sdata))
318 diff = model[mask] - sdata[mask]
319 if weights is not None and scale(1) == 1:
320 diff *= weights
321 return numpy.asarray(diff).ravel()
322
323 self.lmm = lmfit.Model(self.func,
324 name=self.lmodel.__class__.__name__, **kwargs)
325 self.lmm._residual = _residual
326 for method in ('set_param_hint', 'print_param_hints', 'eval',
327 'make_params'):
328 setattr(self, method, getattr(self.lmm, method))
329 # set default parameter hints
330 self._default_hints()
331 self.set_fit_limits()
332
333 def set_fit_limits(self, xmin=-numpy.inf, xmax=numpy.inf, mask=None):
334 """
335 set fit limits. If mask is given it must have the same size as the
336 `data` and `x` variables given to fit. If mask is None it will be
337 generated from xmin and xmax.
338
339 Parameters
340 ----------
341 xmin : float, optional
342 minimum value of x-values to include in the fit
343 xmax : float, optional
344 maximum value of x-values to include in the fit
345 mask : boolean array, optional
346 mask to be used for the data given to the fit
347 """
348 self.mask = mask
349 self.xmin = xmin
350 self.xmax = xmax
351
352 def fit(self, data, params, x, weights=None, fit_kws=None, **kwargs):
353 """
354 wrapper around lmfit.Model.fit which enables plotting during the
355 fitting
356
357 Parameters
358 ----------
359 data : ndarray
360 experimental values
361 params : lmfit.Parameters
362 list of parameters for the fit, use make_params for generation
363 x : ndarray
364 independent variable (incidence angle or q-position depending on
365 the model)
366 weights : ndarray, optional
367 values of weights for the fit, same size as data
368 fit_kws : dict, optional
369 Options to pass to the minimizer being used
370 kwargs : dict, optional
371 keyword arguments which are passed to lmfit.Model.fit
372
373 Returns
374 -------
375 lmfit.ModelResult
376 """
377 class FitPlot(object):
378 def __init__(self, figname, logscale):
379 self.figname = figname
380 self.logscale = logscale
381 if not self.figname:
382 self.plot = False
383 else:
384 f, plt = utilities.import_matplotlib_pyplot('XU.simpack')
385 self.plt = plt
386 self.plot = f
387
388 def plot_init(self, x, data, weights, model, mask, verbose):
389 if not self.plot:
390 return
391 self.plt.ion()
392 if isinstance(self.figname, basestring):
393 self.fig = self.plt.figure(self.figname)
394 else:
395 self.fig = self.plt.figure('XU:FitModel')
396 self.plt.clf()
397 self.ax = self.plt.subplot(111)
398 if self.logscale:
399 self.ax.set_yscale("log", nonposy='clip')
400 if weights is not None:
401 eline = self.ax.errorbar(
402 x, data, yerr=1/weights, ecolor='0.3', fmt='ko',
403 errorevery=int(x.size/80), label='data')[0]
404 else:
405 eline, = self.ax.plot(x, data, 'ko', label='data')
406 if verbose:
407 init, = self.ax.plot(
408 x, model, '-', color='0.5', label='initial')
409 if eline:
410 self.zord = eline.zorder+2
411 else:
412 self.zord = 1
413 self.fline = None
414
415 def showplot(self, xlab=self.lmodel.xlabelstr,
416 ylab='Intensity (arb. u.)'):
417 if not self.plot:
418 return
419 self.plt.xlabel(xlab)
420 self.plt.ylabel(ylab)
421 self.plt.legend()
422 self.plt.tight_layout()
423 self.plt.show()
424
425 def updatemodelline(self, x, newmodel):
426 if not self.plot:
427 return
428 try:
429 self.plt.sca(self.ax)
430 except ValueError:
431 return
432 if self.fline is None:
433 self.fline, = self.ax.plot(
434 x, newmodel, 'r-', lw=2, label='fit', zorder=self.zord)
435 else:
436 self.fline.set_data(x, newmodel)
437 canvas = self.fig.canvas # see plt.draw function (avoid show!)
438 canvas.draw_idle()
439 canvas.start_event_loop(0.001)
440
441 def addfullmodelline(self, x, y):
442 if not self.plot:
443 return
444 self.ax.plot(x, y, 'g-', lw=1, label='full model',
445 zorder=self.zord-1)
446
447 if self.mask:
448 mask = self.mask
449 else:
450 mask = numpy.logical_and(x >= self.xmin, x <= self.xmax)
451 mweights = weights
452 if mweights is not None:
453 mweights = weights[mask]
454
455 # create initial plot
456 self.fitplot = FitPlot(self.plot, self.elog)
457 initmodel = self.eval(params, x=x, **kwargs)
458 self.fitplot.plot_init(x, data, weights, initmodel, mask, self.verbose)
459 self.fitplot.showplot()
460
461 # create callback function
462 def cb_func(params, niter, resid, *args, **kwargs):
463 if self.verbose:
464 print('{:04d} {:12.3e}'.format(niter, numpy.sum(resid**2)))
465 if self.fitplot.plot and niter % 20 == 0:
466 self.fitplot.updatemodelline(kwargs['x'],
467 self.eval(params, **kwargs))
468
469 # perform fitting
470 res = self.lmm.fit(data[mask], params, x=x[mask], weights=mweights,
471 fit_kws=fit_kws, iter_cb=cb_func, **kwargs)
472
473 # final update of plot
474 if self.fitplot.plot:
475 try:
476 self.fitplot.plt.sca(self.fitplot.ax)
477 except ValueError:
478 self.fitplot.plot_init(x, data, weights, initmodel, mask,
479 self.verbose)
480 fittedmodel = self.eval(res.params, x=x, **kwargs)
481 self.fitplot.addfullmodelline(x, fittedmodel)
482 self.fitplot.updatemodelline(x[mask], fittedmodel[mask])
483 self.fitplot.showplot()
484
485 return res
486
487 def _default_hints(self):
488 """
489 set useful hints for parameters all LayerModels have
490 """
491 # general parameters
492 for pn in self.lmodel.fit_paramnames:
493 self.set_param_hint(pn, value=getattr(self.lmodel, pn), vary=False)
494 for pn in ('I0', 'background'):
495 self.set_param_hint(pn, vary=True, min=0)
496 self.set_param_hint('resolution_width', min=0, vary=False)
497 self.set_param_hint('energy', min=1000, vary=False)
498
499 # parameters of the layerstack
500 for l in self.lmodel.lstack:
501 for param in self.lmodel.lstack_params:
502 varname = '{}_{}'.format(l.name, param)
503 self.set_param_hint(varname, value=getattr(l, param), min=0)
504 if param == 'density':
505 self.set_param_hint(varname, max=1.5*l.material.density)
506 if param == 'thickness':
507 self.set_param_hint(varname, max=2*l.thickness)
508 if param == 'roughness':
509 self.set_param_hint(varname, max=50)
510 if self.lmodel.lstack_structural_params:
511 for param in l._structural_params:
512 varname = '{}_{}'.format(l.name, param)
513 self.set_param_hint(varname, value=getattr(l, param),
514 vary=False)
515 if 'occupation' in param:
516 self.set_param_hint(varname, min=0, max=1)
517 if 'biso' in param:
518 self.set_param_hint(varname, min=0, max=5)
519 if self.lmodel.lstack[0].thickness == numpy.inf:
520 varname = '{}_{}'.format(self.lmodel.lstack[0].name, 'thickness')
521 self.set_param_hint(varname, vary=False)
+0
-85
xrayutilities/simpack/helpers.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import numpy
18
19 from .. import config, utilities
20
21 # python 2to3 compatibility
22 try:
23 basestring
24 except NameError:
25 basestring = str
26
27
28 def coplanar_alphai(qx, qz, en='config'):
29 """
30 calculate coplanar incidence angle from knowledge of the qx and qz
31 coordinates
32
33 Parameters
34 ----------
35 qx : array-like
36 inplane momentum transfer component
37 qz : array-like
38 out of plane momentum transfer component
39 en : float or str, optional
40 x-ray energy (eV). By default the value from the config is used.
41
42 Returns
43 -------
44 alphai : array-like
45 the incidence angle in degree. points in the Laue zone are set to
46 'nan'.
47 """
48 if isinstance(en, basestring) and en == 'config':
49 en = utilities.energy(config.ENERGY)
50 k = 2 * numpy.pi / utilities.en2lam(en)
51 th = numpy.arcsin(numpy.sqrt(qx**2 + qz**2) / (2 * k))
52 ai = numpy.arctan2(qx, qz) + th
53 if isinstance(ai, numpy.ndarray): # remove positions in Laue zone
54 ai[qz < numpy.sqrt(2 * qx * k - qx**2)] = numpy.nan
55 else:
56 if qz < numpy.sqrt(2 * qx * k - qx**2):
57 ai = numpy.nan
58 return numpy.degrees(ai)
59
60
61 def get_qz(qx, alphai, en='config'):
62 """
63 calculate the qz position from the qx position and the incidence angle for
64 a coplanar diffraction geometry
65
66 Parameters
67 ----------
68 qx : array-like
69 inplane momentum transfer component
70 alphai : array-like
71 incidence angle (deg)
72 en : float or str, optional
73 x-ray energy (eV). By default the value from the config is used.
74
75 Returns
76 -------
77 array-like
78 the qz position for the given incidence angle
79 """
80 if isinstance(en, basestring) and en == 'config':
81 en = utilities.energy(config.ENERGY)
82 k = 2 * numpy.pi / utilities.en2lam(en)
83 ai = numpy.radians(alphai)
84 return numpy.sqrt(k**2 - (qx + k * numpy.cos(ai))**2) + k * numpy.sin(ai)
+0
-1934
xrayutilities/simpack/models.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 from __future__ import division
18
19 import abc
20 import copy
21 import math as pymath
22
23 import numpy
24 import scipy.constants as constants
25 import scipy.integrate as integrate
26 import scipy.interpolate as interpolate
27 from scipy.special import erf, j0
28
29 from .. import config, utilities
30 from ..exception import InputError
31 from ..experiment import Experiment
32 from ..math import NormGauss1d, NormLorentz1d, heaviside, solve_quartic
33 from .smaterials import Layer, LayerStack
34
35
36 def startdelta(start, delta, num):
37 end = start + delta * (num - 1)
38 return numpy.linspace(start, end, int(num))
39
40
41 class Model(object):
42 """
43 generic model class from which further models can be derived from
44 """
45
46 def __init__(self, experiment, **kwargs):
47 """
48 constructor for a generical simulation model.
49 currently only the experiment class describing the diffraction geometry
50 is stored in the base class
51
52 Parameters
53 ----------
54 experiment : Experiment
55 class describing the diffraction geometry, energy and wavelength of
56 the model
57 resolution_width : float, optional
58 defines the width of the resolution
59 I0 : float, optional
60 the primary beam flux/intensity
61 background : float, optional
62 the background added to the simulation
63 energy : float or str
64 the experimental energy in eV
65 resolution_type : {'Gauss', 'Lorentz'}, optional
66 type of resolution function, default: Gauss
67 """
68 local_fit_params = {'resolution_width': 'width of the resolution',
69 'I0': 'primary beam intensity',
70 'background': 'background intensity',
71 'energy': 'x-ray energy in eV'}
72 if not hasattr(self, 'fit_paramnames'):
73 self.fit_paramnames = []
74 self.fit_paramnames += local_fit_params.keys()
75 valid_kwargs = copy.copy(local_fit_params)
76 valid_kwargs['resolution_type'] = 'resolution function typ'
77 utilities.check_kwargs(kwargs, valid_kwargs,
78 self.__class__.__name__)
79
80 if experiment:
81 self.exp = experiment
82 else:
83 self.exp = Experiment([1, 0, 0], [0, 0, 1])
84 self.resolution_width = kwargs.get('resolution_width', 0)
85 self.resolution_type = kwargs.get('resolution_type', 'Gauss')
86 self.I0 = kwargs.get('I0', 1)
87 self.background = kwargs.get('background', 0)
88 if 'energy' in kwargs:
89 self.energy = kwargs['energy']
90
91 @property
92 def energy(self):
93 return self.exp.energy
94
95 @energy.setter
96 def energy(self, en):
97 self.exp.energy = en
98
99 def convolute_resolution(self, x, y):
100 """
101 convolve simulation result with a resolution function
102
103 Parameters
104 ----------
105 x : array-like
106 x-values of the simulation, units of x also decide about the unit
107 of the resolution_width parameter
108 y : array-like
109 y-values of the simulation
110
111 Returns
112 -------
113 array-like
114 convoluted y-data with same shape as y
115 """
116 if self.resolution_width == 0:
117 return y
118 else:
119 dx = numpy.mean(numpy.gradient(x))
120 nres = int(20 * numpy.abs(self.resolution_width / dx))
121 xres = startdelta(-10*self.resolution_width, dx, nres + 1)
122 # the following works only exactly for equally spaced data points
123 if self.resolution_type == 'Gauss':
124 fres = NormGauss1d
125 else:
126 fres = NormLorentz1d
127 resf = fres(xres, numpy.mean(xres), self.resolution_width)
128 resf /= numpy.sum(resf) # proper normalization for discrete conv.
129 # pad y to avoid edge effects
130 interp = interpolate.InterpolatedUnivariateSpline(x, y, k=1)
131 nextmin = numpy.ceil(nres/2.)
132 nextpos = numpy.floor(nres/2.)
133 xext = numpy.concatenate(
134 (startdelta(x[0]-dx, -dx, nextmin)[-1::-1],
135 x,
136 startdelta(x[-1]+dx, dx, nextpos)))
137 ypad = numpy.asarray(interp(xext))
138 return numpy.convolve(ypad, resf, mode='valid')
139
140 def scale_simulation(self, y):
141 """
142 scale simulation result with primary beam flux/intensity and add a
143 background.
144
145 Parameters
146 ----------
147 y : array-like
148 y-values of the simulation
149
150 Returns
151 -------
152 array-like
153 scaled y-values
154 """
155 return y * self.I0 + self.background
156
157
158 class LayerModel(Model, utilities.ABC):
159 """
160 generic model class from which further thin film models can be derived from
161 """
162
163 def __init__(self, *args, **kwargs):
164 """
165 constructor for a thin film model. The arguments consist of a
166 LayerStack or individual Layer(s). Optional parameters are specified
167 in the keyword arguments.
168
169 Parameters
170 ----------
171 *args : LayerStack or Layers
172 either one LayerStack or several Layer objects can be given
173 **kwargs : dict
174 optional parameters for the simulation. ones not listed below are
175 forwarded to the superclass.
176 experiment : Experiment, optional
177 class containing geometry and energy of the experiment.
178 surface_hkl : list or tuple, optional
179 Miller indices of the surface (default: (0, 0, 1))
180 """
181 exp = kwargs.pop('experiment', None)
182 super(LayerModel, self).__init__(exp, **kwargs)
183 self.lstack_params = []
184 self.lstack_structural_params = False
185 self.xlabelstr = 'x (1)'
186 if len(args) == 1:
187 if isinstance(args[0], Layer):
188 self.lstack = LayerStack('Stack for %s'
189 % self.__class__.__name__, *args)
190 else:
191 self.lstack = args[0]
192 else:
193 self.lstack = LayerStack('Stack for %s' % self.__class__.__name__,
194 *args)
195
196 @abc.abstractmethod
197 def simulate(self):
198 """
199 abstract method that every implementation of a LayerModel has to
200 override.
201 """
202 pass
203
204 def _create_return(self, x, E, ai=None, af=None, Ir=None,
205 rettype='intensity'):
206 """
207 function to create the return value of a simulation. by default only
208 the diffracted intensity is returned. However, optionally also the
209 incidence and exit angle as well as the reflected intensity can be
210 returned.
211
212 Parameters
213 ----------
214 x : array-like
215 independent coordinate value for the convolution with the
216 resolution function
217 E : array-like
218 electric field amplitude (complex)
219 ai, af : array-like, optional
220 incidence and exit angle of the XRD beam (in radians)
221 Ir : array-like, optional
222 reflected intensity
223 rettype : {'intensity', 'field', 'all'}, optional
224 type of the return value. 'intensity' (default): returns the
225 diffracted beam flux convoluted with the resolution function;
226 'field': returns the electric field (complex) without convolution
227 with the resolution function, 'all': returns the electric field,
228 ai, af (both in degree), and the reflected intensity.
229
230 Returns
231 -------
232 return value depends on value of rettype.
233 """
234 if rettype == 'intensity':
235 ret = self.scale_simulation(
236 self.convolute_resolution(x, numpy.abs(E)**2))
237 elif rettype == 'field':
238 ret = E
239 elif rettype == 'all':
240 ret = (E, numpy.degrees(ai), numpy.degrees(af), Ir)
241 return ret
242
243 def get_polarizations(self):
244 """
245 return list of polarizations which should be calculated
246 """
247 if self.polarization == 'both':
248 return ('S', 'P')
249 else:
250 return (self.polarization,)
251
252 def join_polarizations(self, Is, Ip):
253 """
254 method to calculate the total diffracted intensity from the intensities
255 of S and P-polarization.
256 """
257 if self.polarization == 'both':
258 ret = (Is + self.Cmono * Ip) / (1 + self.Cmono)
259 else:
260 if self.polarization == 'S':
261 ret = Is
262 else:
263 ret = Ip
264 return ret
265
266
267 class KinematicalModel(LayerModel):
268 """
269 Kinematical diffraction model for specular and off-specular qz-scans. The
270 model calculates the kinematical contribution of one (hkl) Bragg peak,
271 however considers the variation of the structure factor for different 'q'.
272 The surface geometry is specified using the Experiment-object given to the
273 constructor.
274 """
275
276 def __init__(self, *args, **kwargs):
277 """
278 constructor for a kinematic thin film model. The arguments consist of a
279 LayerStack or individual Layer(s). Optional parameters are specified in
280 the keyword arguments.
281
282 Parameters
283 ----------
284 *args : LayerStack or Layers
285 either one LayerStack or several Layer objects can be given
286 **kwargs : dict
287 optional parameters; also see LayerModel/Model.
288 experiment : Experiment
289 Experiment class containing geometry and energy of the experiment.
290 """
291 super(KinematicalModel, self).__init__(*args, **kwargs)
292 self.lstack_params += ['thickness', ]
293 self.lstack_structural_params = True
294 self.xlabelstr = r'momentum transfer $Q_z$ ($\mathrm{\AA^{-1}}$)'
295 # precalc optical properties
296 self._init_en = 0
297 self.init_chi0()
298
299 def init_chi0(self):
300 """
301 calculates the needed optical parameters for the simulation. If any of
302 the materials/layers is changing its properties this function needs to
303 be called again before another correct simulation is made. (Changes of
304 thickness does NOT require this!)
305 """
306 if self._init_en != self.energy: # recalc properties if energy changed
307 self.chi0 = numpy.asarray([l.material.chi0(en=self.energy)
308 for l in self.lstack])
309 self._init_en = self.energy
310
311 def _prepare_kincalculation(self, qz, hkl):
312 """
313 prepare kinematic calculation by calculating some helper values
314 """
315 rel = constants.physical_constants['classical electron radius'][0]
316 rel *= 1e10
317 k = self.exp.k0
318
319 # determine q-inplane
320 t = self.exp._transform
321 ql0 = t(self.lstack[0].material.Q(*hkl))
322 qinp = numpy.sqrt(ql0[0]**2 + ql0[1]**2)
323
324 # calculate needed angles
325 qv = numpy.asarray([t.inverse((ql0[0], ql0[1], q)) for q in qz])
326 Q = numpy.linalg.norm(qv, axis=1)
327 theta = numpy.arcsin(Q / (2 * k))
328 domega = numpy.arctan2(qinp, qz)
329 alphai, alphaf = (theta + domega, theta - domega)
330 # calculate structure factors
331 f = numpy.empty((len(self.lstack), len(qz)), dtype=numpy.complex)
332 fhkl = numpy.empty(len(self.lstack), dtype=numpy.complex)
333 for i, l in enumerate(self.lstack):
334 m = l.material
335 fhkl[i] = m.StructureFactor(m.Q(*hkl), en=self.energy) /\
336 m.lattice.UnitCellVolume()
337 f[i, :] = m.StructureFactorForQ(qv, en0=self.energy) /\
338 m.lattice.UnitCellVolume()
339
340 E = numpy.zeros(len(qz), dtype=numpy.complex)
341 return rel, alphai, alphaf, f, fhkl, E, t
342
343 def _get_qz(self, qz, alphai, alphaf, chi0, absorption, refraction):
344 k = self.exp.k0
345 q = qz.astype(numpy.complex)
346 if absorption and not refraction:
347 q += 1j * k * numpy.imag(chi0) / \
348 numpy.sin((alphai + alphaf) / 2)
349 if refraction:
350 q = k * (numpy.sqrt(numpy.sin(alphai)**2 + chi0) +
351 numpy.sqrt(numpy.sin(alphaf)**2 + chi0))
352 return q
353
354 def simulate(self, qz, hkl, absorption=False, refraction=False,
355 rettype='intensity'):
356 """
357 performs the actual kinematical diffraction calculation on the Qz
358 positions specified considering the contribution from a single Bragg
359 peak.
360
361 Parameters
362 ----------
363 qz : array-like
364 simulation positions along qz
365 hkl : list or tuple
366 Miller indices of the Bragg peak whos truncation rod should be
367 calculated
368 absorption : bool, optional
369 flag to tell if absorption correction should be used
370 refraction : bool, optional
371 flag to tell if basic refraction correction should be performed. If
372 refraction is True absorption correction is also included
373 independent of the absorption flag.
374 rettype : {'intensity', 'field', 'all'}
375 type of the return value. 'intensity' (default): returns the
376 diffracted beam flux convoluted with the resolution function;
377 'field': returns the electric field (complex) without convolution
378 with the resolution function, 'all': returns the electric field,
379 ai, af (both in degree), and the reflected intensity.
380
381 Returns
382 -------
383 array-like
384 return value depends on the setting of `rettype`, by default only
385 the calculate intensity is returned
386 """
387 self.init_chi0()
388 rel, ai, af, f, fhkl, E, t = self._prepare_kincalculation(qz, hkl)
389 # calculate interface positions
390 z = numpy.zeros(len(self.lstack))
391 for i, l in enumerate(self.lstack[-1:0:-1]):
392 z[-i-2] = z[-i-1] - l.thickness
393
394 # perform kinematical calculation
395 for i, l in enumerate(self.lstack):
396 q = self._get_qz(qz, ai, af, self.chi0[i], absorption, refraction)
397 q -= t(l.material.Q(*hkl))[-1]
398
399 if l.thickness == numpy.inf:
400 E += fhkl[i] * numpy.exp(-1j * z[i] * q) / (1j * q)
401 else:
402 E += - fhkl[i] * numpy.exp(-1j * q * z[i]) * \
403 (1 - numpy.exp(1j * q * l.thickness)) / (1j * q)
404
405 wf = numpy.sqrt(heaviside(ai) * heaviside(af) * rel**2 /
406 (numpy.sin(ai) * numpy.sin(af))) * E
407 return self._create_return(qz, wf, ai, af, rettype=rettype)
408
409
410 class KinematicalMultiBeamModel(KinematicalModel):
411 """
412 Kinematical diffraction model for specular and off-specular qz-scans. The
413 model calculates the kinematical contribution of several Bragg peaks on
414 the truncation rod and considers the variation of the structure factor.
415 In order to use a analytical description for the kinematic diffraction
416 signal all layer thicknesses are changed to a multiple of the respective
417 lattice parameter along qz. Therefore this description only works for (001)
418 surfaces.
419 """
420
421 def __init__(self, *args, **kwargs):
422 """
423 constructor for a kinematic thin film model. The arguments consist of a
424 LayerStack or individual Layer(s). Optional parameters are specified in
425 the keyword arguments.
426
427 Parameters
428 ----------
429 *args : LayerStack or Layers
430 either one LayerStack or several Layer objects can be given
431 **kwargs : dict
432 optional parameters. see also LayerModel/Model.
433 experiment : Experiment
434 Experiment class containing geometry and energy of the experiment.
435 surface_hkl : list or tuple
436 Miller indices of the surface (default: (0, 0, 1))
437 """
438 self.surface_hkl = kwargs.pop('surface_hkl', (0, 0, 1))
439 super(KinematicalMultiBeamModel, self).__init__(*args, **kwargs)
440
441 def simulate(self, qz, hkl, absorption=False, refraction=True,
442 rettype='intensity'):
443 """
444 performs the actual kinematical diffraction calculation on the Qz
445 positions specified considering the contribution from a full
446 truncation rod
447
448 Parameters
449 ----------
450 qz : array-like
451 simulation positions along qz
452 hkl : list or tuple
453 Miller indices of the Bragg peak whos truncation rod should be
454 calculated
455 absorption : bool, optional
456 flag to tell if absorption correction should be used
457 refraction : bool, optional,
458 flag to tell if basic refraction correction should be performed. If
459 refraction is True absorption correction is also included
460 independent of the absorption flag.
461 rettype : {'intensity', 'field', 'all'}
462 type of the return value. 'intensity' (default): returns the
463 diffracted beam flux convoluted with the resolution function;
464 'field': returns the electric field (complex) without convolution
465 with the resolution function, 'all': returns the electric field,
466 ai, af (both in degree), and the reflected intensity.
467
468 Returns
469 -------
470 array-like
471 return value depends on the setting of `rettype`, by default only
472 the calculate intensity is returned
473 """
474 self.init_chi0()
475 rel, ai, af, f, fhkl, E, t = self._prepare_kincalculation(qz, hkl)
476
477 # calculate interface positions for integer unit-cell thickness
478 z = numpy.zeros(len(self.lstack))
479 for i, l in enumerate(self.lstack[-1:0:-1]):
480 lat = l.material.lattice
481 a3 = t(lat.GetPoint(*self.surface_hkl))[-1]
482 n3 = l.thickness // a3
483 z[-i-2] = z[-i-1] - a3 * n3
484 if config.VERBOSITY >= config.INFO_LOW and \
485 numpy.abs(l.thickness/a3 - n3) > 0.01:
486 print('XU.KinematicMultiBeamModel: %s thickness changed from'
487 ' %.2fA to %.2fA (%d UCs)' % (l.name, l.thickness,
488 a3 * n3, n3))
489
490 # perform kinematical calculation
491 for i, l in enumerate(self.lstack):
492 q = self._get_qz(qz, ai, af, self.chi0[i], absorption, refraction)
493 lat = l.material.lattice
494 a3 = t(lat.GetPoint(*self.surface_hkl))[-1]
495
496 if l.thickness == numpy.inf:
497 E += f[i, :] * a3 * numpy.exp(-1j * z[i] * q) /\
498 (1 - numpy.exp(1j * q * a3))
499 else:
500 n3 = l.thickness // a3
501 E += f[i, :] * a3 * numpy.exp(-1j * z[i] * q) * \
502 (1 - numpy.exp(1j * q * a3 * n3)) /\
503 (1 - numpy.exp(1j * q * a3))
504
505 wf = numpy.sqrt(heaviside(ai) * heaviside(af) * rel**2 /
506 (numpy.sin(ai) * numpy.sin(af))) * E
507 return self._create_return(qz, wf, ai, af, rettype=rettype)
508
509
510 class SimpleDynamicalCoplanarModel(KinematicalModel):
511 """
512 Dynamical diffraction model for specular and off-specular qz-scans.
513 Calculation of the flux of reflected and diffracted waves for general
514 asymmetric coplanar diffraction from an arbitrary pseudomorphic multilayer
515 is performed by a simplified 2-beam theory (2 tiepoints, S and P
516 polarizations)
517
518 No restrictions are made for the surface orientation.
519
520 The first layer in the model is always assumed to be the semiinfinite
521 substrate indepentent of its given thickness
522
523 Note:
524 This model should not be used in real life scenarios since the made
525 approximations severely fail for distances far from the reference
526 position.
527 """
528
529 def __init__(self, *args, **kwargs):
530 """
531 constructor for a diffraction model. The arguments consist of a
532 LayerStack or individual Layer(s). Optional parameters are specified
533 in the keyword arguments.
534
535 Parameters
536 ----------
537 *args : LayerStack or Layers
538 either one LayerStack or several Layer objects can be given
539 **kwargs: dict
540 optional parameters for the simulation
541 I0 : float, optional
542 the primary beam intensity
543 background : float, optional
544 the background added to the simulation
545 resolution_width : float, optional
546 the width of the resolution (deg)
547 polarization: {'S', 'P', 'both'}
548 polarization of the x-ray beam. If set to 'both' also Cmono, the
549 polarization factor of the monochromator should be set
550 Cmono : float, optional
551 polarization factor of the monochromator
552 energy : float or str
553 the experimental energy in eV
554 experiment : Experiment
555 Experiment class containing geometry of the sample; surface
556 orientation!
557 """
558 if not hasattr(self, 'fit_paramnames'):
559 self.fit_paramnames = []
560 self.fit_paramnames += ['Cmono', ]
561 self.polarization = kwargs.pop('polarization', 'S')
562 self.Cmono = kwargs.pop('Cmono', 1)
563 super(SimpleDynamicalCoplanarModel, self).__init__(*args, **kwargs)
564 self.xlabelstr = 'incidence angle (deg)'
565 self.hkl = None
566 self.chih = None
567 self.chimh = None
568
569 def set_hkl(self, *hkl):
570 """
571 To speed up future calculations of the same Bragg peak optical
572 parameters can be pre-calculated using this function.
573
574 Parameters
575 ----------
576 hkl : list or tuple
577 Miller indices of the Bragg peak for the calculation
578 """
579 if hkl != (None, ):
580 if len(hkl) < 3:
581 hkl = hkl[0]
582 if len(hkl) < 3:
583 raise InputError("need 3 Miller indices")
584 newhkl = numpy.asarray(hkl)
585 else:
586 newhkl = self.hkl
587
588 if self.energy != self._init_en or numpy.any(newhkl != self.hkl):
589 self.hkl = newhkl
590 self._init_en = self.energy
591
592 # calculate chih
593 self.chih = {'S': [], 'P': []}
594 self.chimh = {'S': [], 'P': []}
595 for l in self.lstack:
596 q = l.material.Q(self.hkl)
597 thetaB = numpy.arcsin(numpy.linalg.norm(q) / 2 / self.exp.k0)
598 ch = l.material.chih(q, en=self.energy, polarization='S')
599 self.chih['S'].append(-ch[0] + 1j*ch[1])
600 self.chih['P'].append((-ch[0] + 1j*ch[1]) *
601 numpy.abs(numpy.cos(2*thetaB)))
602 if not getattr(l, 'inversion_sym', False):
603 ch = l.material.chih(-q, en=self.energy, polarization='S')
604 self.chimh['S'].append(-ch[0] + 1j*ch[1])
605 self.chimh['P'].append((-ch[0] + 1j*ch[1]) *
606 numpy.abs(numpy.cos(2*thetaB)))
607
608 for pol in ('S', 'P'):
609 self.chih[pol] = numpy.asarray(self.chih[pol])
610 self.chimh[pol] = numpy.asarray(self.chimh[pol])
611
612 def _prepare_dyncalculation(self, geometry):
613 """
614 prepare dynamical calculation by calculating some helper values
615 """
616 t = self.exp._transform
617 ql0 = t(self.lstack[0].material.Q(*self.hkl))
618 hx = numpy.sqrt(ql0[0]**2 + ql0[1]**2)
619 if geometry == 'lo_hi':
620 hx = -hx
621
622 # calculate vertical diffraction vector components and strain
623 hz = numpy.zeros(len(self.lstack))
624 for i, l in enumerate(self.lstack):
625 hz[i] = t(l.material.Q(*self.hkl))[2]
626 return t, hx, hz
627
628 def simulate(self, alphai, hkl=None, geometry='hi_lo', idxref=1):
629 """
630 performs the actual diffraction calculation for the specified
631 incidence angles.
632
633 Parameters
634 ----------
635 alphai : array-like
636 vector of incidence angles (deg)
637 hkl : list or tuple, optional
638 Miller indices of the diffraction vector (preferable use set_hkl
639 method to speed up repeated calculations of the same peak!)
640 geometry : {'hi_lo', 'lo_hi'}, optional
641 'hi_lo' for grazing exit (default) and 'lo_hi' for grazing
642 incidence
643 idxref : int, optional
644 index of the reference layer. In order to get accurate peak
645 position of the film peak you want this to be the index of the film
646 peak (default: 1). For the substrate use 0.
647
648 Returns
649 -------
650 array-like
651 vector of intensities of the diffracted signal
652 """
653 self.set_hkl(hkl)
654
655 # return values
656 Ih = {'S': numpy.zeros(len(alphai)), 'P': numpy.zeros(len(alphai))}
657
658 t, hx, hz = self._prepare_dyncalculation(geometry)
659 epsilon = (hz[idxref] - hz) / hz
660
661 k = self.exp.k0
662 thetaB = numpy.arcsin(numpy.sqrt(hx**2 + hz[idxref]**2) / 2 / k)
663 # asymmetry angle
664 asym = numpy.arctan2(hx, hz[idxref])
665 gamma0 = numpy.sin(asym + thetaB)
666 gammah = numpy.sin(asym - thetaB)
667
668 # deviation of the incident beam from the kinematical maximum
669 eta = numpy.radians(alphai) - thetaB - asym
670
671 for pol in self.get_polarizations():
672 x = numpy.zeros(len(alphai), dtype=numpy.complex)
673 for i, l in enumerate(self.lstack):
674 beta = (2 * eta * numpy.sin(2 * thetaB) +
675 self.chi0[i] * (1 - gammah / gamma0) -
676 2 * gammah * (gamma0 - gammah) * epsilon[i])
677 y = beta / 2 / numpy.sqrt(self.chih[pol][i] *
678 self.chimh[pol][i]) /\
679 numpy.sqrt(numpy.abs(gammah) / gamma0)
680 c1 = -numpy.sqrt(self.chih[pol][i] / self.chih[pol][i] *
681 gamma0 / numpy.abs(gammah)) *\
682 (y + numpy.sqrt(y**2 - 1))
683 c2 = -numpy.sqrt(self.chih[pol][i] / self.chimh[pol][i] *
684 gamma0 / numpy.abs(gammah)) *\
685 (y - numpy.sqrt(y**2 - 1))
686 kz2mkz1 = k * numpy.sqrt(self.chih[pol][i] *
687 self.chimh[pol][i] / gamma0 /
688 numpy.abs(gammah)) *\
689 numpy.sqrt(y**2 - 1)
690 if i == 0: # substrate
691 pp = numpy.abs(gammah) / gamma0 * numpy.abs(c1)**2
692 m = pp < 1
693 x[m] = c1[m]
694 m = pp >= 1
695 x[m] = c2[m]
696 else: # layers
697 cphi = numpy.exp(1j * kz2mkz1 * l.thickness)
698 x = (c1 * c2 * (cphi - 1) + xs * (c1 - cphi * c2)) /\
699 (cphi * c1 - c2 + xs * (1 - cphi))
700 xs = x
701 Ih[pol] = numpy.abs(x)**2 * numpy.abs(gammah) / gamma0
702
703 ret = self.join_polarizations(Ih['S'], Ih['P'])
704 return self.scale_simulation(self.convolute_resolution(alphai, ret))
705
706
707 class DynamicalModel(SimpleDynamicalCoplanarModel):
708 """
709 Dynamical diffraction model for specular and off-specular qz-scans.
710 Calculation of the flux of reflected and diffracted waves for general
711 asymmetric coplanar diffraction from an arbitrary pseudomorphic multilayer
712 is performed by a generalized 2-beam theory (4 tiepoints, S and P
713 polarizations)
714
715 The first layer in the model is always assumed to be the semiinfinite
716 substrate indepentent of its given thickness
717 """
718
719 def simulate(self, alphai, hkl=None, geometry='hi_lo',
720 rettype='intensity'):
721 """
722 performs the actual diffraction calculation for the specified
723 incidence angles and uses an analytic solution for the quartic
724 dispersion equation
725
726 Parameters
727 ----------
728 alphai : array-like
729 vector of incidence angles (deg)
730 hkl : list or tuple, optional
731 Miller indices of the diffraction vector (preferable use set_hkl
732 method to speed up repeated calculations of the same peak!)
733 geometry : {'hi_lo', 'lo_hi'}, optional
734 'hi_lo' for grazing exit (default) and 'lo_hi' for grazing
735 incidence
736 rettype : {'intensity', 'field', 'all'}, optional
737 type of the return value. 'intensity' (default): returns the
738 diffracted beam flux convoluted with the resolution function;
739 'field': returns the electric field (complex) without convolution
740 with the resolution function, 'all': returns the electric field,
741 ai, af (both in degree), and the reflected intensity.
742
743 Returns
744 -------
745 array-like
746 vector of intensities of the diffracted signal, possibly changed
747 return value due the rettype setting!
748 """
749 if len(self.get_polarizations()) > 1 and rettype != "intensity":
750 raise ValueError('XU:DynamicalModel: return type (%s) not '
751 'supported with multiple polarizations!')
752 rettype = 'intensity'
753 self.set_hkl(hkl)
754
755 # return values
756 Ih = {'S': numpy.zeros(len(alphai)), 'P': numpy.zeros(len(alphai))}
757 Ir = {'S': numpy.zeros(len(alphai)), 'P': numpy.zeros(len(alphai))}
758
759 t, hx, hz = self._prepare_dyncalculation(geometry)
760
761 k = self.exp.k0
762 kc = k * numpy.sqrt(1 + self.chi0)
763 ai = numpy.radians(alphai)
764 Kix = k * numpy.cos(ai)
765 Kiz = -k * numpy.sin(ai)
766 Khz = numpy.sqrt(k**2 - (Kix + hx)**2)
767 pp = Khz / k
768 mask = numpy.logical_and(pp > 0, pp < 1)
769 ah = numpy.zeros(len(ai)) # exit angles
770 ah[mask] = numpy.arcsin(pp[mask])
771
772 nal = len(ai)
773 for pol in self.get_polarizations():
774 if pol == 'S':
775 CC = numpy.ones(nal)
776 else:
777 CC = abs(numpy.cos(ai+ah))
778 pom = k**4 * self.chih['S'] * self.chimh['S']
779 if config.VERBOSITY >= config.INFO_ALL:
780 print('XU.DynamicalModel: calc. %s-polarization...' % (pol))
781
782 M = numpy.zeros((nal, 4, 4), dtype=numpy.complex)
783 for j in range(4):
784 M[:, j, j] = numpy.ones(nal)
785
786 for i, l in enumerate(self.lstack[-1::-1]):
787 jL = len(self.lstack) - 1 - i
788 A4 = numpy.ones(nal)
789 A3 = 2 * hz[jL] * numpy.ones(nal)
790 A2 = (Kix + hx)**2 + hz[jL]**2 + Kix**2 - 2 * kc[jL]**2
791 A1 = 2 * hz[jL] * (Kix**2 - kc[jL]**2)
792 A0 = (Kix**2 - kc[jL]**2) *\
793 ((Kix + hx)**2 + hz[jL]**2 - kc[jL]**2) - pom[jL] * CC**2
794 X = solve_quartic(A4, A3, A2, A1, A0)
795 X = numpy.asarray(X).T
796
797 kz = numpy.zeros((nal, 4), dtype=numpy.complex)
798 kz[:, :2] = X[numpy.imag(X) <= 0].reshape(nal, 2)
799 kz[:, 2:] = X[numpy.imag(X) > 0].reshape(nal, 2)
800
801 P = numpy.zeros((nal, 4, 4), dtype=numpy.complex)
802 phi = numpy.zeros((nal, 4, 4), dtype=numpy.complex)
803 c = ((Kix**2)[:, numpy.newaxis] + kz**2 - kc[jL]**2) / k**2 /\
804 self.chimh['S'][jL] / CC[:, numpy.newaxis]
805 if jL > 0:
806 for j in range(4):
807 phi[:, j, j] = numpy.exp(1j * kz[:, j] * l.thickness)
808 else:
809 phi = numpy.tile(numpy.identity(4), (nal, 1, 1))
810 P[:, 0, :] = numpy.ones((nal, 4))
811 P[:, 1, :] = c
812 P[:, 2, :] = kz
813 P[:, 3, :] = c * (kz + hz[jL])
814
815 if i == 0:
816 R = numpy.copy(P)
817 else:
818 temp = numpy.linalg.inv(Ps)
819 try:
820 R = numpy.matmul(temp, P)
821 except AttributeError:
822 R = numpy.einsum('...ij,...jk', temp, P)
823 try:
824 M = numpy.matmul(numpy.matmul(M, R), phi)
825 except AttributeError:
826 M = numpy.einsum('...ij,...jk',
827 numpy.einsum('...ij,...jk', M, R), phi)
828 Ps = numpy.copy(P)
829
830 B = numpy.zeros((nal, 4, 4), dtype=numpy.complex)
831 B[..., :2] = M[..., :2]
832 B[:, 0, 2] = -numpy.ones(nal)
833 B[:, 1, 3] = -numpy.ones(nal)
834 B[:, 2, 2] = Kiz
835 B[:, 3, 3] = -Khz
836 C = numpy.zeros((nal, 4))
837 C[:, 0] = numpy.ones(nal)
838 C[:, 2] = Kiz
839
840 E = numpy.einsum('...ij,...j', numpy.linalg.inv(B), C)
841 Ir[pol] = numpy.abs(E[:, 2])**2 # reflected intensity
842 Ih[pol] = numpy.abs(E[:, 3])**2 * numpy.abs(Khz / Kiz) * mask
843
844 if len(self.get_polarizations()) > 1 and rettype == "intensity":
845 ret = numpy.sqrt(self.join_polarizations(Ih['S'], Ih['P']))
846 else:
847 ret = E[:, 3] * numpy.sqrt(numpy.abs(Khz / Kiz) * mask)
848
849 return self._create_return(alphai, ret, ai, ah, Ir, rettype=rettype)
850
851
852 class SpecularReflectivityModel(LayerModel):
853 """
854 model for specular reflectivity calculations
855 """
856
857 def __init__(self, *args, **kwargs):
858 """
859 constructor for a reflectivity model. The arguments consist of a
860 LayerStack or individual Layer(s). Optional parameters are specified
861 in the keyword arguments.
862
863 Parameters
864 ----------
865 args : LayerStack or Layers
866 either one LayerStack or several Layer objects can be given
867 kwargs: dict
868 optional parameters for the simulation; supported are:
869 I0 : float, optional
870 the primary beam intensity
871 background : float, optional
872 the background added to the simulation
873 sample_width : float, optional
874 width of the sample along the beam
875 beam_width : float, optional
876 beam width in the same units as the sample width
877 offset : float, optional
878 angular offset of the incidence angle (deg)
879 resolution_width : float, optional
880 width of the resolution (deg)
881 energy : float or str
882 x-ray energy in eV
883 """
884 if not hasattr(self, 'fit_paramnames'):
885 self.fit_paramnames = []
886 self.fit_paramnames += ['sample_width', 'beam_width', 'offset']
887 self.sample_width = kwargs.pop('sample_width', numpy.inf)
888 self.beam_width = kwargs.pop('beam_width', 0)
889 self.offset = kwargs.pop('offset', 0)
890 super(SpecularReflectivityModel, self).__init__(*args, **kwargs)
891 self.lstack_params += ['thickness', 'roughness', 'density']
892 self.xlabelstr = 'incidence angle (deg)'
893 # precalc optical properties
894 self._init_en = 0
895 self.init_cd()
896
897 def init_cd(self):
898 """
899 calculates the needed optical parameters for the simulation. If any of
900 the materials/layers is changing its properties this function needs to
901 be called again before another correct simulation is made. (Changes of
902 thickness and roughness do NOT require this!)
903 """
904 if self._init_en != self.energy:
905 self.cd = numpy.asarray([-l.material.chi0(en=self.energy)/2
906 for l in self.lstack])
907 self._init_en = self.energy
908
909 def simulate(self, alphai):
910 """
911 performs the actual reflectivity calculation for the specified
912 incidence angles
913
914 Parameters
915 ----------
916 alphai : array-like
917 vector of incidence angles
918
919 Returns
920 -------
921 array-like
922 vector of intensities of the reflectivity signal
923 """
924 self.init_cd()
925 ns, np = (len(self.lstack), len(alphai))
926 lai = alphai - self.offset
927 # get layer properties
928 t = numpy.asarray([l.thickness for l in self.lstack])
929 sig = numpy.asarray([l.roughness for l in self.lstack])
930 rho = numpy.asarray([l.density/l.material.density
931 for l in self.lstack])
932 cd = self.cd
933
934 sai = numpy.sin(numpy.radians(lai))
935
936 if self.beam_width > 0:
937 shape = self.sample_width * sai / self.beam_width
938 shape[shape > 1] = 1
939 else:
940 shape = numpy.ones(np)
941
942 ETs = numpy.ones(np, dtype=numpy.complex)
943 ERs = numpy.zeros(np, dtype=numpy.complex)
944 ks = -self.exp.k0 * numpy.sqrt(sai**2 - 2 * cd[0] * rho[0])
945
946 for i in range(ns):
947 if i < ns-1:
948 k = -self.exp.k0 * numpy.sqrt(sai**2 - 2 * cd[i+1] * rho[i+1])
949 phi = numpy.exp(1j * k * t[i+1])
950 else:
951 k = -self.exp.k0 * sai
952 phi = numpy.ones(np)
953 r = (k - ks) / (k + ks) * numpy.exp(-2 * sig[i]**2 * k * ks)
954 ET = phi * (ETs + r * ERs)
955 ER = (r * ETs + ERs) / phi
956 ETs = ET
957 ERs = ER
958 ks = k
959
960 R = shape * abs(ER / ET)**2
961 return self.scale_simulation(self.convolute_resolution(lai, R))
962
963 def densityprofile(self, nz, plot=False):
964 """
965 calculates the electron density of the layerstack from the thickness
966 and roughness of the individual layers
967
968 Parameters
969 ----------
970 nz : int
971 number of values on which the profile should be calculated
972 plot : bool, optional
973 flag to tell if a plot of the profile should be created
974
975 Returns
976 -------
977 z : array-like
978 z-coordinates, z = 0 corresponds to the surface
979 eprof : array-like
980 electron profile
981 """
982 if plot:
983 try:
984 from matplotlib import pyplot as plt
985 except ImportError:
986 plot = False
987 if config.VERBOSITY >= config.INFO_LOW:
988 print("XU.simpack: Warning: plot "
989 "functionality not available")
990
991 rel = constants.physical_constants['classical electron radius'][0]
992 rel *= 1e10
993 nl = len(self.lstack)
994
995 # get layer properties
996 t = numpy.asarray([l.thickness for l in self.lstack])
997 sig = numpy.asarray([l.roughness for l in self.lstack])
998 rho = numpy.asarray([l.density/l.material.density
999 for l in self.lstack])
1000 delta = numpy.real(self.cd)
1001
1002 totT = numpy.sum(t[1:])
1003 zmin = -totT - 10 * sig[0]
1004 zmax = 5 * sig[-1]
1005
1006 z = numpy.linspace(zmin, zmax, nz)
1007 pre_factor = 2 * numpy.pi / self.exp.wavelength**2 / rel * 1e24
1008
1009 # generate delta-rho values and interface positions
1010 zz = numpy.zeros(nl)
1011 dr = numpy.zeros(nl)
1012 dr[-1] = delta[-1] * rho[-1] * pre_factor
1013 for i in range(nl-1, 0, -1):
1014 zz[i-1] = zz[i] - t[i]
1015 dr[i-1] = delta[i-1] * rho[i-1] * pre_factor
1016
1017 # calculate profile from contribution of all interfaces
1018 prof = numpy.zeros(nz)
1019 w = numpy.zeros((nl, nz))
1020 for i in range(nl):
1021 s = (1 + erf((z - zz[i]) / sig[i] / numpy.sqrt(2))) / 2
1022 mask = s < (1 - 1e-10)
1023 w[i, mask] = s[mask] / (1 - s[mask])
1024 mask = numpy.logical_not(mask)
1025 w[i, mask] = 1e10
1026
1027 c = numpy.ones((nl, nz))
1028 for i in range(1, nl):
1029 c[i, :] = w[i-1, :] * c[i-1, :]
1030 c0 = w[-1, :] * c[-1, :]
1031 norm = numpy.sum(c, axis=0) + c0
1032
1033 for i in range(nl):
1034 c[i] /= norm
1035
1036 for j in range(nz):
1037 prof[j] = numpy.sum(dr * c[:, j])
1038
1039 if plot:
1040 plt.figure('XU:density_profile', figsize=(5, 3))
1041 plt.plot(z, prof, 'k-', lw=2, label='electron density')
1042 plt.xlabel(r'z ($\mathrm{\AA}$)')
1043 plt.ylabel(r'electron density (e$^-$ cm$^{-3}$)')
1044 plt.tight_layout()
1045
1046 return z, prof
1047
1048
1049 class DynamicalReflectivityModel(SpecularReflectivityModel):
1050 """
1051 model for Dynamical Specular Reflectivity Simulations.
1052 It uses the transfer Matrix methods as given in chapter 3
1053 "Daillant, J., & Gibaud, A. (2008). X-ray and Neutron Reflectivity"
1054 """
1055 def __init__(self, *args, **kwargs):
1056 """
1057 constructor for a reflectivity model. The arguments consist of a
1058 LayerStack or individual Layer(s). Optional parameters are specified
1059 in the keyword arguments.
1060
1061 Parameters
1062 ----------
1063 args : LayerStack or Layers
1064 either one LayerStack or several Layer objects can be given
1065 kwargs: dict
1066 optional parameters for the simulation; supported are:
1067 I0 : float, optional
1068 the primary beam intensity
1069 background : float, optional
1070 the background added to the simulation
1071 sample_width : float, optional
1072 width of the sample along the beam
1073 beam_width : float, optional
1074 beam width in the same units as the sample width
1075 resolution_width : float, optional
1076 width of the resolution (deg)
1077 energy : float or str
1078 x-ray energy in eV
1079 polarization: ['P', 'S']
1080 x-ray polarization
1081 """
1082 self.polarization = kwargs.pop('polarization', 'P')
1083 super(DynamicalReflectivityModel, self).__init__(*args, **kwargs)
1084 self._init_en_opt = 0
1085 self._setOpticalConstants()
1086
1087 def _setOpticalConstants(self):
1088 if self._init_en_opt != self.energy:
1089 self.n_indices = numpy.asarray(
1090 [l.material.idx_refraction(en=self.energy)
1091 for l in self.lstack])
1092 # append n = 1 for vacuum
1093 self.n_indices = numpy.append(self.n_indices, 1)[::-1]
1094 self._init_en_opt = self.energy
1095
1096 def _getTransferMatrices(self, alphai):
1097 """
1098 Calculation of Refraction and Translation Matrices per angle per layer.
1099 """
1100 # Set heights for each layer
1101 heights = numpy.asarray([l.thickness for l in self.lstack[1:]])
1102 heights = numpy.cumsum(heights)[::-1]
1103 heights = numpy.insert(heights, 0, 0.) # first interface is at z=0
1104
1105 # set K-vector in each layer
1106 kz_angles = -self.exp.k0 * numpy.sqrt(numpy.asarray(
1107 [n**2 - numpy.cos(numpy.radians(alphai))**2
1108 for n in self.n_indices]).T)
1109
1110 # set Roughness for each layer
1111 roughness = numpy.asarray([l.roughness for l in self.lstack[1:]])[::-1]
1112 roughness = numpy.insert(roughness, 0, 0.) # first interface is at z=0
1113
1114 # Roughness is approximated by a Gaussian Statistics model modification
1115 # of the transfer matrix elements using Groce-Nevot factors (GNF).
1116
1117 GNF_factor_P = numpy.asarray(
1118 [[numpy.exp(-(kz_next - kz)**2 * (rough**2) / 2)
1119 for (kz, kz_next, rough) in zip(kz_1angle, kz_1angle[1:],
1120 roughness[1:])]
1121 for kz_1angle in kz_angles])
1122
1123 GNF_factor_M = numpy.asarray(
1124 [[numpy.exp(-(kz_next + kz) ** 2 * (rough ** 2) / 2)
1125 for (kz, kz_next, rough) in zip(kz_1angle, kz_1angle[1:],
1126 roughness[1:])]
1127 for kz_1angle in kz_angles])
1128
1129 if self.polarization is 'S':
1130 p_factor_angles = numpy.asarray(
1131 [[(kz + kz_next) / (2 * kz)
1132 for kz, kz_next in zip(kz_1angle, kz_1angle[1:])]
1133 for kz_1angle in kz_angles])
1134
1135 m_factor_angles = numpy.asarray(
1136 [[(kz - kz_next) / (2 * kz)
1137 for kz, kz_next in zip(kz_1angle, kz_1angle[1:])]
1138 for kz_1angle in kz_angles])
1139 else:
1140 p_factor_angles = numpy.asarray(
1141 [[(n_next**2*kz + n**2*kz_next) / (2*n_next**2*kz)
1142 for (kz, kz_next, n, n_next) in zip(kz_1angle, kz_1angle[1:],
1143 self.n_indices,
1144 self.n_indices[1:])]
1145 for kz_1angle in kz_angles])
1146 m_factor_angles = numpy.asarray(
1147 [[(n_next**2*kz - n**2*kz_next) / (2*n_next**2*kz)
1148 for (kz, kz_next, n, n_next) in zip(kz_1angle, kz_1angle[1:],
1149 self.n_indices,
1150 self.n_indices[1:])]
1151 for kz_1angle in kz_angles])
1152
1153 # Translation Matrices dim = (angle, layer, 2, 2)
1154 T_matrices = numpy.asarray(
1155 [[([numpy.exp(-1.j*kz*height), 0], [0, numpy.exp(1.j*kz*height)])
1156 for kz, height in zip(kz_1angle, heights)]
1157 for kz_1angle in kz_angles])
1158
1159 R_matrices = numpy.asarray(
1160 [[([p, m], [m, p]) for p, m in zip(P_fact, M_fact)]
1161 for (P_fact, M_fact) in zip(p_factor_angles, m_factor_angles)])
1162
1163 for R_mat, GNF_P, GNF_M in zip(R_matrices, GNF_factor_P, GNF_factor_M):
1164 R_mat[0, 0] = R_mat[0, 0] * GNF_P
1165 R_mat[0, 1] = R_mat[0, 1] * GNF_M
1166 R_mat[1, 0] = R_mat[1, 0] * GNF_M
1167 R_mat[1, 1] = R_mat[1, 1] * GNF_P
1168
1169 return T_matrices, R_matrices
1170
1171 def simulate(self, alphai):
1172 """
1173 Simulates the Dynamical Reflectivity as a function of angle of
1174 incidence
1175
1176 Parameters
1177 ----------
1178 alphai : array-like
1179 vector of incidence angles
1180
1181 Returns
1182 -------
1183 reflectivity: array-like
1184 vector of intensities of the reflectivity signal
1185 transmitivity: array-like
1186 vector of intensities of the transmitted signal
1187 """
1188 self._setOpticalConstants()
1189 lai = alphai - self.offset
1190 # Get Refraction and Translation Matrices for each angle of incidence
1191 if lai[0] < 1.e-5:
1192 lai[0] = 1.e-5 # cutoff
1193
1194 T_matrices, R_matrices = self._getTransferMatrices(lai)
1195
1196 # Calculate the Transfer Matrix
1197 M_angles = numpy.zeros((lai.size, 2, 2), dtype=numpy.complex128)
1198 for (angle, R), T in zip(enumerate(R_matrices), T_matrices):
1199 pairwiseRT = [numpy.dot(t, r) for r, t in zip(R, T)]
1200 M = numpy.identity(2, dtype=numpy.complex128)
1201 for pair in pairwiseRT:
1202 M = numpy.dot(M, pair)
1203 M_angles[angle] = M
1204
1205 # Reflectance and Transmittance
1206 R = numpy.array([numpy.abs((M[0, 1] / M[1, 1]))**2 for M in M_angles])
1207 T = numpy.array([numpy.abs((1. / M[1, 1]))**2 for M in M_angles])
1208
1209 return R, T
1210
1211 def scanEnergy(self, energies, angle):
1212 # TODO: this is quite inefficient, too many calls to internal functions
1213 # TODO: DO not return normalized refelctivity
1214 """
1215 Simulates the Dynamical Reflectivity as a function of photon energy at
1216 fixed angle.
1217
1218 Parameters
1219 ----------
1220 energies: np.ndarray or list
1221 photon energies (in eV).
1222 angle : float
1223 fixed incidence angle
1224
1225 Returns
1226 -------
1227 reflectivity: array-like
1228 vector of intensities of the reflectivity signal
1229 transmitivity: array-like
1230 vector of intensities of the transmitted signal
1231 """
1232 R_energies, T_energies = numpy.array([]), numpy.array([])
1233 for energy in energies:
1234 self.energy = energy
1235 self._setOpticalConstants()
1236 T_matrices, R_matrices = self._getTransferMatrices([angle, 0])
1237 T_matrix = T_matrices[0]
1238 R_matrix = R_matrices[0]
1239 pairwiseRT = [numpy.dot(t, r) for r, t in zip(R_matrix, T_matrix)]
1240 M = numpy.identity(2, dtype=numpy.complex128)
1241 for pair in pairwiseRT:
1242 M = numpy.dot(M, pair)
1243 R = numpy.abs(M[0, 1] / M[1, 1]) ** 2
1244 T = numpy.abs(1. / M[1, 1]) ** 2
1245 R_energies = numpy.append(R_energies, R)
1246 T_energies = numpy.append(T_energies, T)
1247 return R_energies, T_energies
1248
1249
1250 class ResonantReflectivityModel(SpecularReflectivityModel):
1251 """
1252 model for specular reflectivity calculations
1253 CURRENTLY UNDER DEVELOPEMENT! DO NOT USE!
1254 """
1255 def __init__(self, *args, **kwargs):
1256 """
1257 constructor for a reflectivity model. The arguments consist of a
1258 LayerStack or individual Layer(s). Optional parameters are specified
1259 in the keyword arguments.
1260
1261 Parameters
1262 ----------
1263 args : LayerStack or Layers
1264 either one LayerStack or several Layer objects can be given
1265 kwargs: dict
1266 optional parameters for the simulation; supported are:
1267 I0 : float, optional
1268 the primary beam intensity
1269 background : float, optional
1270 the background added to the simulation
1271 sample_width : float, optional
1272 width of the sample along the beam
1273 beam_width : float, optional
1274 beam width in the same units as the sample width
1275 resolution_width : float, optional
1276 width of the resolution (deg)
1277 energy : float or str
1278 x-ray energy in eV
1279 polarization: ['P', 'S']
1280 x-ray polarization
1281 """
1282 self.polarization = kwargs.pop('polarization', 'S')
1283 super(ResonantReflectivityModel, self).__init__(*args, **kwargs)
1284
1285 def simulate(self, alphai):
1286 """
1287 performs the actual reflectivity calculation for the specified
1288 incidence angles
1289
1290 Parameters
1291 ----------
1292 alphai : array-like
1293 vector of incidence angles
1294
1295 Returns
1296 -------
1297 array-like
1298 vector of intensities of the reflectivity signal
1299 """
1300 self.init_cd()
1301 ns, np = (len(self.lstack), len(alphai))
1302 lai = alphai - self.offset
1303
1304 # get layer properties
1305 t = numpy.asarray([l.thickness for l in self.lstack])
1306 sig = numpy.asarray([l.roughness for l in self.lstack])
1307 rho = numpy.asarray([l.density/l.material.density
1308 for l in self.lstack])
1309 cd = self.cd
1310 qzvec = 4 * numpy.pi * numpy.sin(numpy.radians(lai)) /\
1311 utilities.en2lam(self.energy)
1312 qvec = numpy.array([[0., 0., qz] for qz in qzvec])
1313 chihP = numpy.array([[l.material.chih(q, en=self.energy,
1314 polarization=self.polarization)
1315 for q in qvec]
1316 for l in self.lstack])
1317
1318 if self.polarization in ['S', 'P']:
1319 cd = cd + chihP
1320 else:
1321 cd = cd
1322
1323 sai = numpy.sin(numpy.radians(lai))
1324
1325 if self.beam_width > 0:
1326 shape = self.sample_width * sai / self.beam_width
1327 shape[shape > 1] = 1
1328 else:
1329 shape = numpy.ones(np)
1330
1331 ETs = numpy.ones(np, dtype=numpy.complex)
1332 ERs = numpy.zeros(np, dtype=numpy.complex)
1333 ks = -self.exp.k0 * numpy.sqrt(sai**2 - 2 * cd[0] * rho[0])
1334
1335 for i in range(ns):
1336 if i < ns-1:
1337 k = -self.exp.k0 * numpy.sqrt(sai**2 - 2 * cd[i+1] * rho[i+1])
1338 phi = numpy.exp(1j * k * t[i+1])
1339 else:
1340 k = -self.exp.k0 * sai
1341 phi = numpy.ones(np)
1342 r = (k - ks) / (k + ks) * numpy.exp(-2 * sig[i]**2 * k * ks)
1343 ET = phi * (ETs + r * ERs)
1344 ER = (r * ETs + ERs) / phi
1345 ETs = ET
1346 ERs = ER
1347 ks = k
1348
1349 R = shape * abs(ER / ET)**2
1350 return self.scale_simulation(self.convolute_resolution(lai, R))
1351
1352
1353 class DiffuseReflectivityModel(SpecularReflectivityModel):
1354 """
1355 model for diffuse reflectivity calculations
1356
1357 The 'simulate' method calculates the diffuse reflectivity on the specular
1358 rod in coplanar geometry in analogy to the SpecularReflectivityModel.
1359
1360 The 'simulate_map' method calculates the diffuse reflectivity for a 2D set
1361 of Q-positions. This method can also calculate the intensity for other
1362 geometries, like GISAXS with constant incidence angle or a quasi
1363 omega/2theta scan in GISAXS geometry.
1364 """
1365
1366 def __init__(self, *args, **kwargs):
1367 """
1368 constructor for a reflectivity model. The arguments consist of a
1369 LayerStack or individual Layer(s). Optional parameters are specified
1370 in the keyword arguments.
1371
1372 Parameters
1373 ----------
1374 args : LayerStack or Layers
1375 either one LayerStack or several Layer objects can be given
1376 kwargs : dict
1377 optional parameters for the simulation; supported are:
1378 I0 : float, optional
1379 the primary beam intensity
1380 background : float, optional
1381 the background added to the simulation
1382 sample_width : float, optional
1383 width of the sample along the beam
1384 beam_width : float, optional
1385 beam width in the same units as the sample width
1386 resolution_width : float, optional
1387 defines the width of the resolution (deg)
1388 energy : float, optional
1389 sets the experimental energy (eV)
1390 H : float, optional
1391 Hurst factor defining the fractal dimension of the roughness (0..1,
1392 very slow for H != 1 or H != 0.5), default: 1
1393 vert_correl : float, optional
1394 vertical correlation length in (Angstrom), 0 means full replication
1395 vert_nu : float, optional
1396 exponent in the vertical correlation function
1397 method : int, optional
1398 1..simple DWBA (default), 2..full DWBA (slower)
1399 vert_int : int, optional
1400 0..no integration over the vertical divergence, 1..with integration
1401 over the vertical divergence
1402 qL_zero : float, optional
1403 value of inplane q-coordinate which can be considered 0, using
1404 method 2 it is important to avoid exact 0 and this value will be
1405 used instead
1406 """
1407 if not hasattr(self, 'fit_paramnames'):
1408 self.fit_paramnames = []
1409 self.fit_paramnames += ['H', 'vert_correl', 'vert_nu']
1410 self.H = kwargs.pop('H', 1)
1411 self.vert_correl = kwargs.pop('vert_correl', 0)
1412 self.vert_nu = kwargs.pop('vert_nu', 0)
1413 self.method = kwargs.pop('method', 1)
1414 self.vert = kwargs.pop('vert_int', 0)
1415 self.qL_zero = kwargs.pop('qL_zero', 5e-5)
1416 super(DiffuseReflectivityModel, self).__init__(*args, **kwargs)
1417 self.lstack_params += ['lat_correl', ]
1418
1419 def _get_layer_prop(self):
1420 """
1421 helper function to obtain layer properties needed for all types of
1422 simulations
1423 """
1424 nl = len(self.lstack)
1425 self.init_cd()
1426
1427 t = numpy.asarray([float(l.thickness) for l in self.lstack[nl:0:-1]])
1428 sig = [float(l.roughness) for l in self.lstack[nl::-1]]
1429 rho = [l.density/l.material.density for l in self.lstack[nl::-1]]
1430 delta = self.cd * numpy.asarray(rho)
1431 xiL = [float(l.lat_correl) for l in self.lstack[nl::-1]]
1432
1433 return t, sig, rho, delta, xiL
1434
1435 def simulate(self, alphai):
1436 """
1437 performs the actual diffuse reflectivity calculation for the specified
1438 incidence angles. This method always uses the coplanar geometry
1439 independent of the one set during the initialization.
1440
1441 Parameters
1442 ----------
1443 alphai : array-like
1444 vector of incidence angles
1445
1446 Returns
1447 -------
1448 array-like
1449 vector of intensities of the reflectivity signal
1450 """
1451 lai = alphai - self.offset
1452 # get layer properties
1453 t, sig, rho, delta, xiL = self._get_layer_prop()
1454
1455 deltaA = numpy.sum(delta[:-1]*t)/numpy.sum(t)
1456 lam = utilities.en2lam(self.energy)
1457 if self.method == 2:
1458 qL = [-abs(self.qL_zero), abs(self.qL_zero)]
1459 else:
1460 qL = [0, ]
1461 qz = 4 * numpy.pi / lam * numpy.sin(numpy.radians(lai))
1462 R = self._xrrdiffv2(lam, delta, t, sig, xiL, self.H, self.vert_correl,
1463 self.vert_nu, None, qL, qz, self.sample_width,
1464 self.beam_width, 1e-4, 1000, deltaA, self.method,
1465 1, self.vert)
1466 R = R.mean(axis=0)
1467 return self.scale_simulation(self.convolute_resolution(lai, R))
1468
1469 def simulate_map(self, qL, qz):
1470 """
1471 performs diffuse reflectivity calculation for the rectangular grid of
1472 reciprocal space positions define by qL and qz. This method uses the
1473 method and geometry set during the initialization of the class.
1474
1475 Parameters
1476 ----------
1477 qL : array-like
1478 lateral coordinate in reciprocal space (vector with NqL components)
1479 qz : array-like
1480 vertical coordinate in reciprocal space (vector with Nqz
1481 components)
1482
1483 Returns
1484 -------
1485 array-like
1486 matrix of intensities of the reflectivity signal, with shape
1487 (len(qL), len(qz))
1488 """
1489 # get layer properties
1490 t, sig, rho, delta, xiL = self._get_layer_prop()
1491
1492 deltaA = numpy.sum(delta[:-1]*t)/numpy.sum(t)
1493 lam = utilities.en2lam(self.energy)
1494 localqL = numpy.copy(qL)
1495 if self.method == 2:
1496 localqL[qL == 0] = self.qL_zero
1497
1498 R = self._xrrdiffv2(lam, delta, t, sig, xiL, self.H, self.vert_correl,
1499 self.vert_nu, None, localqL, qz, self.sample_width,
1500 self.beam_width, 1e-4, 1000, deltaA, self.method,
1501 1, self.vert)
1502 return self.scale_simulation(R)
1503
1504 def _xrrdiffv2(self, lam, delta, thick, sigma, xiL, H, xiV, nu, alphai, qL,
1505 qz, samplewidth, beamwidth, eps, nmax, deltaA, method, scan,
1506 vert):
1507 """
1508 simulation of diffuse reflectivity from a rough multilayer. Exact or
1509 simplified DWBA, fractal roughness model, Ming model of the vertical
1510 correlation, various scattering geometries
1511
1512 The used incidence and exit angles are stored in _smap_alphai,
1513 _smap_alphaf
1514
1515 Parameters
1516 ----------
1517 lam : float
1518 x-ray wavelength in Angstrom
1519 delta : list or array-like
1520 vector with the 1-n values (N+1 components, 1st component..layer at
1521 the free surface, last component..substrate)
1522 thick : list or array-like
1523 vector with thicknesses (N components)
1524 sigma : list or array-like
1525 vector with rms roughnesses (N+1 components)
1526 xiL : list or array-like
1527 vector with lateral correlation lengths (N+1 components)
1528 H : float
1529 Hurst factor (scalar)
1530 xiV : float
1531 vertical correlation: 0..full replication, > 0 and nu > 0.. see
1532 below, > 0 and nu = 0..vertical correlation length
1533 nu : float
1534 exponent in the vertical correlation function
1535 exp(-abs(z_m-z_n)*(qL/max(qL))**nu/xiV)
1536 alphai : float
1537 incidence angle (scalar for scan=2, ignored for scan=1, 3)
1538 qL : array-like
1539 lateral coordinate in reciprocal space (vector with NqL components)
1540 qz : array-like
1541 vertical coordinate in reciprocal space (vector with Nqz
1542 components)
1543 samplewidth : float
1544 width of the irradiated sample area (scalar), =0..the irradiated
1545 are is assumed constant
1546 beamwidth : float
1547 width of the primary beam
1548 eps : float
1549 small number
1550 nmax : int
1551 max number of terms in the Taylor series of the lateral correlation
1552 function
1553 deltaA : complex
1554 effective value of 1-n in simple DWBA (ignored for method=2)
1555 method : int
1556 1..simple DWBA, 2..full DWBA
1557 scan : int
1558 1..standard coplanar geometry, 2..standard GISAXS geometry with
1559 constant incidence angle, =3..quasi omega/2theta scan in GISAXS
1560 geometry (incidence and central-exit angles are equal)
1561 vert : int
1562 0..no integration over the vertical divergence, 1..with integration
1563 over the vertical divergence
1564
1565 Returns
1566 -------
1567 diffint : array-like
1568 diffuse reflectivity intensity matrix
1569 """
1570 # worker function definitions
1571 def coherent(alphai, K, delta, thick, N, NqL, Nqz):
1572 """
1573 calculate coherent reflection/transmission signal of a multilayer
1574
1575 Parameters
1576 ----------
1577 alphai : array-like
1578 matrix of incidence angles in radians (NqL x Nqz components)
1579 K : float
1580 x-ray wave-vector (2*pi/lambda)
1581 delta : array-like
1582 vector with the 1-n values (N+1 components, 1st
1583 component..layer at the free surface, last
1584 component..substrate)
1585 thick : array-like
1586 vector with thicknesses (N components)
1587 N : int
1588 number layers in the stack
1589 NqL : int
1590 number of lateral q-points to calculate
1591 Nqz : int
1592 number of vertical q-points to calculate
1593
1594 Returns
1595 -------
1596 T, R, R0, k0, kz : array-like
1597 transmission, reflection, surface reflection, z-component of
1598 k-vector, z-component of k-vector in the material.
1599 """
1600 k0 = -K * numpy.sin(alphai)
1601 kz = numpy.zeros((N+1, NqL, Nqz), dtype=numpy.complex)
1602 T = numpy.zeros((N+1, NqL, Nqz), dtype=numpy.complex)
1603 R = numpy.zeros((N+1, NqL, Nqz), dtype=numpy.complex)
1604 for jn in range(N+1):
1605 kz[jn, ...] = -K * numpy.sqrt(numpy.sin(alphai)**2 -
1606 2 * delta[jn])
1607
1608 T[N, ...] = numpy.ones((NqL, Nqz), dtype=numpy.complex)
1609 kzs = kz[N, ...] # kz in substrate
1610 for jn in range(N-1, -1, -1):
1611 kzn = kz[jn, ...]
1612 tF = 2 * kzn / (kzn + kzs)
1613 rF = (kzn - kzs) / (kzn + kzs)
1614 phi = numpy.exp(1j * kzn * thick[jn])
1615 T[jn, ...] = phi / tF * (T[jn+1, ...] + rF * R[jn+1, ...])
1616 R[jn, ...] = 1 / phi / tF * (rF * T[jn+1, ...] + R[jn+1, ...])
1617 kzs = numpy.copy(kzn)
1618
1619 tF = 2 * k0 / (k0 + kzn)
1620 rF = (k0 - kzn) / (k0 + kzn)
1621 T0 = 1 / tF * (T[0, ...] + rF * R[0, ...])
1622 R0 = 1 / tF * (rF * T[0, ...] + R[0, ...])
1623
1624 T /= T0
1625 R /= T0
1626 R0 /= T0
1627
1628 return T, R, R0, k0, kz
1629
1630 def correl(a, b, L, H, eps, nmax, vert, K, NqL, Nqz, isurf):
1631 """
1632 correlation function
1633
1634 Parameters
1635 ----------
1636 a : array-like
1637 lateral correlation parameter
1638 b : array-like
1639 vertical correlation parameter
1640 L : float
1641 lateral correlation length
1642 H : float
1643 Hurst factor (scalar)
1644 eps : float
1645 small number (decides integration cut-off), typical 1e-3
1646 nmax : int
1647 max number of terms in the Taylor series of the lateral
1648 correlation function
1649 vert : int
1650 flag to tell decide if integration over vertical divergence is
1651 used: 0..no integration, 1..with integration
1652 K : float
1653 length of the x-ray wave-vector (2*pi/lambda)
1654 NqL : int
1655 number of lateral q-points to calculate
1656 Nqz : int
1657 number of vertical q-points to calculate
1658 isurf : array-like
1659 array with NqL, Nqz flags to tell if there is a positive
1660 incidence and exit angle
1661
1662 Returns
1663 -------
1664 psi : array-like
1665 correlation function
1666 """
1667 psi = numpy.zeros((NqL, Nqz), dtype=numpy.complex)
1668 if H == 0.5 or H == 1:
1669 dpsi = numpy.zeros_like(psi, dtype=numpy.complex)
1670 m = isurf > 0
1671 n = 1
1672 s = numpy.copy(b)
1673 errm = numpy.inf
1674 if H == 1 and vert == 0:
1675 def f(a, n):
1676 return numpy.exp(-a**2/4/n) / 2 / n**2
1677 elif H == 0.5 and vert == 0:
1678 def f(a, n):
1679 return 1. / (n**2 + a**2)**(3/2.)
1680 elif H == 1 and vert == 1:
1681 def f(a, n):
1682 return numpy.sqrt(numpy.pi/n**3) * numpy.exp(-a**2/4/n)
1683 elif H == 0.5 and vert == 1:
1684 def f(a, n):
1685 return 2. / (n**2 + a**2)
1686
1687 while errm > eps and n < nmax:
1688 dpsi[m] = s[m] * f(a[m], n)
1689 if n > 1:
1690 errm = abs(numpy.max(dpsi[m] / psi[m]))
1691 psi[m] += dpsi[m]
1692 s[m] *= b[m]/float(n)
1693 n += 1
1694 else:
1695 if vert == 0:
1696 kern = kernel
1697 else:
1698 kern = kernelvert
1699 for jL in range(NqL):
1700 for jz in range(Nqz):
1701 if isurf[jL, jz] == 1:
1702 xmax = (-numpy.log(eps / b[jL, jz]))**(1/(2*H))
1703 psi[jL, jz] = cquad(kern, 0.0, numpy.real(xmax),
1704 epsrel=eps, epsabs=0,
1705 limit=nmax, args=(a[jL, jz],
1706 b[jL, jz], H))
1707
1708 if vert == 0:
1709 psi *= 2 * numpy.pi * L ** 2
1710 else:
1711 psi *= 2 * numpy.pi * L / K
1712 return psi
1713
1714 def kernelvert(x, a, b, H):
1715 """
1716 integration kernel with vertical integration
1717
1718 Parameters
1719 ----------
1720 x : float or array-like
1721 independent parameter of the function
1722 a : float
1723 lateral correlation parameter
1724 b : complex
1725 vertical correlation parameter
1726 H : float
1727 Hurst factor (scalar)
1728
1729 Returns
1730 -------
1731 float or arraylike
1732 """
1733 w = numpy.exp(b * numpy.exp(-x**(2*H))) - 1
1734 F = 2 * numpy.cos(a*x) * w
1735 return F
1736
1737 def kernel(x, a, b, H):
1738 """
1739 integration kernel without vertical integration
1740
1741 Parameters
1742 ----------
1743 x : float or array-like
1744 independent parameter of the function
1745 a : float
1746 lateral correlation parameter
1747 b : complex
1748 vertical correlation parameter
1749 H : float
1750 Hurst factor (scalar)
1751
1752 Returns
1753 -------
1754 float or arraylike
1755 """
1756 w = numpy.exp(b * numpy.exp(-x**(2*H))) - 1
1757 F = x * j0(a*x) * w
1758 return F
1759
1760 def cquad(func, a, b, **kwargs):
1761 """
1762 complex quadrature by spliting real and imaginary part using scipy
1763 """
1764 def real_func(*args):
1765 return numpy.real(func(*args))
1766
1767 def imag_func(*args):
1768 return numpy.imag(func(*args))
1769 real_integral = integrate.quad(real_func, a, b, **kwargs)
1770 imag_integral = integrate.quad(imag_func, a, b, **kwargs)
1771 return (real_integral[0] + 1j*imag_integral[0])
1772
1773 # begin of _xrrdiffv2
1774 K = 2 * numpy.pi / lam
1775
1776 N = len(thick)
1777 NqL = len(qL)
1778 Nqz = len(qz)
1779
1780 QZ, QL = numpy.meshgrid(qz, qL)
1781
1782 # scan types:
1783 if scan == 1: # coplanar geometry
1784 Q = numpy.sqrt(QL**2 + QZ**2)
1785 QP = numpy.abs(QL)
1786 th = numpy.arcsin(Q / 2 / K)
1787 om = numpy.arctan2(QL, QZ)
1788 ALPHAI = th + om
1789 ALPHAF = th - om
1790 elif scan == 2: # GISAXS geometry with constant incidence angle
1791 ALPHAI = numpy.radians(alphai) * numpy.ones((NqL, Nqz))
1792 ALPHAF = numpy.arcsin(QZ / K - numpy.sin(numpy.radians(alphai)))
1793 PHI = numpy.arcsin(QL / K / numpy.cos(ALPHAF))
1794 QP = K * numpy.sqrt(numpy.cos(ALPHAF)**2 + numpy.cos(ALPHAI)**2 -
1795 2*numpy.cos(ALPHAF) * numpy.cos(ALPHAI) *
1796 numpy.cos(PHI))
1797 elif scan == 3: # with quasi omega/2theta scan in GISAXS geometry
1798 ALPHAI = numpy.arcsin(QZ * (K - numpy.sqrt(K**2 - QL**2)) / QL**2)
1799 ALPHAF = numpy.arcsin(QZ / K - numpy.sin(ALPHAI))
1800 PHI = numpy.arcsin(QL / K / numpy.cos(ALPHAF))
1801 QP = K * numpy.sqrt(numpy.cos(ALPHAF)**2 + numpy.cos(ALPHAI)**2 -
1802 2*numpy.cos(ALPHAF) * numpy.cos(ALPHAI) *
1803 numpy.cos(PHI))
1804 else:
1805 raise ValueError("Invalid value of parameter 'scan'")
1806
1807 # removing the values under the horizon
1808 isurf = heaviside(ALPHAI) * heaviside(ALPHAF)
1809
1810 # non-disturbed states:
1811 if method == 1:
1812 k01 = -K * numpy.sin(ALPHAI)
1813 kz1 = -K * numpy.sqrt(numpy.sin(ALPHAI)**2 - 2*deltaA)
1814 k02 = -K * numpy.sin(ALPHAF)
1815 kz2 = -K * numpy.sqrt(numpy.sin(ALPHAF)**2 - 2*deltaA)
1816 T1 = 2 * k01 / (k01 + kz1)
1817 T2 = 2 * k02 / (k02 + kz2)
1818 R01 = (k01 - kz1) / (k01 + kz1)
1819 R02 = (k02 - kz2) / (k02+kz2)
1820 R1 = numpy.zeros((NqL, Nqz), dtype=numpy.complex)
1821 R2 = numpy.copy(R1)
1822 nproc = 1
1823 else: # method == 2
1824 T1, R1, R01, k01, kz1 = coherent(ALPHAI, K, delta, thick, N,
1825 NqL, Nqz)
1826 T2, R2, R02, k02, kz2 = coherent(ALPHAF, K, delta, thick, N,
1827 NqL, Nqz)
1828 nproc = 4
1829
1830 # sample surface
1831 if beamwidth > 0:
1832 S = samplewidth * numpy.sin(ALPHAI) / beamwidth
1833 S[S > 1] = 1
1834 else:
1835 S = 1
1836
1837 # z-coordinates
1838 z = numpy.zeros(N+1)
1839 for jn in range(1, N+1):
1840 z[jn] = z[jn-1] - thick[jn-1]
1841
1842 # calculation of the deltas
1843 delt = numpy.zeros(N+1, dtype=numpy.complex)
1844 for jn in range(N+1):
1845 if jn == 0:
1846 delt[jn] = delta[jn]
1847 if jn > 0:
1848 delt[jn] = delta[jn] - delta[jn-1]
1849
1850 # double sum over interfaces
1851 result = numpy.zeros((NqL, Nqz))
1852 for jn in range(N+1):
1853 # if method == 1 and (H == 1 or H == 0.5):
1854 # print(jn)
1855 if nu != 0 or xiV == 0:
1856 jmdol = 1
1857 else:
1858 jmdol = numpy.argmin(numpy.abs(z - (z[jn] -
1859 xiV * numpy.log(eps))))
1860
1861 for ja in range(nproc):
1862 if method == 1:
1863 Qn = -kz1 - kz2
1864 An = T1 * T2 * numpy.exp(-1j*Qn*z[jn])
1865 else: # method == 2
1866 if ja == 0:
1867 An = T1[jn, ...] * T2[jn, ...]
1868 Qn = -kz1[jn, ...] - kz2[jn, ...]
1869 elif ja == 1:
1870 An = T1[jn, ...] * R2[jn, ...]
1871 Qn = -kz1[jn, ...] + kz2[jn, ...]
1872 elif ja == 2:
1873 An = R1[jn, ...] * T2[jn, ...]
1874 Qn = kz1[jn, ...] - kz2[jn, ...]
1875 elif ja == 3:
1876 An = R1[jn, ...] * R2[jn, ...]
1877 Qn = kz1[jn, ...] + kz2[jn, ...]
1878 for jm in range(jmdol, jn+1):
1879 if jm == jn:
1880 weight = 1
1881 else:
1882 weight = 2
1883 # if method == 1 and (H != 0.5 and H != 1) and ja==1:
1884 # print(jn, jm)
1885 # vertical correlation function:
1886 if xiV > 0:
1887 CV = numpy.exp(-abs(z[jn] - z[jm]) *
1888 (QP/numpy.max(QP))**nu / xiV)
1889 else:
1890 CV = 1
1891 # effective values of sigma and lateral correl. length:
1892 try:
1893 LP = ((float(xiL[jn])**(-2*H) +
1894 float(xiL[jm])**(-2*H)) / 2) ** (-1 / 2 / H)
1895 except ZeroDivisionError:
1896 LP = 0
1897 sig = pymath.sqrt(sigma[jn] * sigma[jm])
1898 for jb in range(nproc):
1899 if method == 1:
1900 Qm = -kz1 - kz2
1901 Am = T1 * T2 * numpy.exp(-1j * Qm * z[jm])
1902 else: # method == 2
1903 # if H != 0.5 or H != 1:
1904 # print(ja, jb, jn, jm)
1905 if jb == 0:
1906 Am = T1[jm, ...] * T2[jm, ...]
1907 Qm = -kz1[jm, ...] - kz2[jm, ...]
1908 elif jb == 1:
1909 Am = T1[jm, ...] * R2[jm, ...]
1910 Qm = -kz1[jm, ...] + kz2[jm, ...]
1911 elif jb == 2:
1912 Am = R1[jm, ...] * T2[jm, ...]
1913 Qm = kz1[jm, ...] - kz2[jm, ...]
1914 elif jb == 3:
1915 Am = R1[jm, ...] * R2[jm, ...]
1916 Qm = +kz1[jm, ...] + kz2[jm, ...]
1917 # lateral correlation function:
1918 Psi = correl(QP*LP, Qn*numpy.conj(Qm)*sig**2, LP, H,
1919 eps, nmax, vert, K, NqL, Nqz, isurf)
1920 result += numpy.real(CV * delt[jn] *
1921 numpy.exp(-Qn**2 *
1922 sigma[jn]**2/2) /
1923 Qn * An *
1924 numpy.conj(delt[jm] *
1925 numpy.exp(-Qm**2*sigma[jm]**2/2) /
1926 Qm * Am) * Psi) * weight
1927
1928 result[isurf == 0] = 0
1929 self._smap_R01 = R01 * isurf
1930 self._smap_R02 = R02 * isurf
1931 self._smap_alphai = numpy.degrees(ALPHAI*isurf)
1932 self._smap_alphaf = numpy.degrees(ALPHAF*isurf)
1933 return result * S * K**4 / (16*numpy.pi**2)
+0
-59
xrayutilities/simpack/mosaicity.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import math
18
19 import numpy
20
21
22 def mosaic_analytic(qx, qz, RL, RV, Delta, hx, hz, shape):
23 """
24 simulation of the coplanar reciprocal space map of a single mosaic layer
25 using a simple analytic approximation
26
27 Parameters
28 ----------
29 qx : array-like
30 vector of the qx values (offset from the Bragg peak)
31 qz : array-like
32 vector of the qz values (offset from the Bragg peak)
33 RL : float
34 lateral block radius in Angstrom
35 RV : float
36 vertical block radius in Angstrom
37 Delta : float
38 root mean square misorientation of the grains in degree
39 hx : float
40 lateral component of the diffraction vector
41 hz : float
42 vertical component of the diffraction vector
43 shape: float
44 shape factor (1..Gaussian)
45
46 Returns
47 -------
48 array-like
49 2D array with calculated intensities
50 """
51 QX, QZ = numpy.meshgrid(qx, qz)
52 QX = QX.T
53 QZ = QZ.T
54 DD = numpy.radians(Delta)
55 tmp = 6 + DD**2 * ((hz*RL)**2 + (hx*RV)**2)
56 F = ((DD*RL*RV)**2*(QZ*hz + QX*hx)**2+6*((RL*QX)**2+(RV*QZ)**2)) / 4 / tmp
57 return math.pi * math.sqrt(6) * RL * RV /\
58 math.sqrt(tmp) * numpy.exp(-F**shape)
+0
-2501
xrayutilities/simpack/powder.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilies is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License and the additonal notes below for
11 # more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, see <http://www.gnu.org/licenses/>.
15 #
16 # Copyright (C) 2016-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
17 # Copyright (C) 2015-2017 Marcus H. Mendenhall <marcus.mendenhall@nist.gov>
18
19 # FP_profile was derived from http://dx.doi.org/10.6028/jres.120.014.c
20
21 # Original copyright notice:
22 # @author Marcus H. Mendenhall (marcus.mendenhall@nist.gov)
23 # @date March, 2015
24 # The "Fundamental Parameters Python Code" ("software") is provided by the
25 # National Institute of Standards and Technology (NIST), an agency of the
26 # United States Department of Commerce, as a public service. This software is
27 # to be used for non-commercial research purposes only and is expressly
28 # provided "AS IS." Use of this software is subject to your acceptance of these
29 # terms and conditions.
30 #
31 # NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED, IN FACT OR ARISING BY
32 # OPERATION OF LAW, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
33 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA
34 # ACCURACY. NIST NEITHER REPRESENTS NOR WARRANTS THAT THE OPERATION OF THE
35 # SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT ANY DEFECTS WILL BE
36 # CORRECTED. NIST DOES NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE
37 # USE OF THE SOFTWARE OR THE RESULTS THEREOF, INCLUDING BUT NOT LIMITED TO THE
38 # CORRECTNESS, ACCURACY, RELIABILITY, OR USEFULNESS OF THE SOFTWARE.
39 #
40 # NIST SHALL NOT BE LIABLE AND YOU HEREBY RELEASE NIST FROM LIABILITY FOR ANY
41 # INDIRECT, CONSEQUENTIAL, SPECIAL, OR INCIDENTAL DAMAGES (INCLUDING DAMAGES
42 # FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS
43 # INFORMATION, AND THE LIKE), WHETHER ARISING IN TORT, CONTRACT, OR
44 # OTHERWISE, ARISING FROM OR RELATING TO THE SOFTWARE (OR THE USE OF OR
45 # INABILITY TO USE THIS SOFTWARE), EVEN IF NIST HAS BEEN ADVISED OF THE
46 # POSSIBILITY OF SUCH DAMAGES.
47 #
48 # Software developed by NIST employees is not subject to copyright protection
49 # within the United States. By using this software, creating derivative works
50 # or by incorporating this software into another product, you agree that you
51 # will use the software only for non-commercial research purposes and will
52 # indemnify and hold harmless the United States Government for any and all
53 # damages or liabilities that arise out of any use by you.
54 #
55 # changelog:
56 #
57 # 15 August, 2018 -- MHM
58 # fixed apparent error in flip of eps0 across twotheta=90 around line 681
59 #
60
61 """
62 This module contains the core definitions for the XRD Fundamental Parameneters
63 Model (FPA) computation in Python. The main computational class is FP_profile,
64 which stores cached information to allow it to efficiently recompute profiles
65 when parameters have been modified. For the user an Powder class is available
66 which can calculate a complete powder pattern of a crystalline material.
67
68 The diffractometer line profile functions are calculated by methods from Cheary
69 & Coelho 1998 and Mullen & Cline paper and 'R' package. Accumulate all
70 convolutions in Fourier space, for efficiency, except for axial divergence,
71 which needs to be weighted in real space for I3 integral.
72
73 More details about the applied algorithms can be found in the paper by
74 M. H. Mendelhall et al., `Journal of Research of NIST 120, 223 (2015)
75 <http://dx.doi.org/10.6028/jres.120.014>`_ to which you should also refer for a
76 careful definition of all the parameters
77 """
78
79 # Known bugs/problems:
80 # in the axial convolver the parameters slit_length_source can not be equal to
81 # slit_length_target!
82
83 # in this file SI units (m) are used for wavelengths, while by default Angstrom
84 # are used in the remaining of the package
85
86 from __future__ import absolute_import, print_function
87
88 import atexit
89 import copy
90 import math
91 import multiprocessing
92 import numbers
93 import sys
94 import threading
95 import time
96 import traceback
97 import warnings
98 from math import cos, pi, sin, sqrt, tan
99 from multiprocessing.managers import BaseManager
100
101 import numpy
102 from numpy import abs as nabs
103 from numpy import arcsin as nasin
104 from numpy import asarray
105 from numpy import cos as ncos
106 from numpy import sin as nsin
107 from scipy.special import sici # for the sine and cosine integral
108
109 # package internal imports
110 from .. import config, materials, utilities
111 from ..experiment import PowderExperiment
112 from ..math import VecNorm
113 from .smaterials import Powder
114
115 # python 2to3 compatibility
116 try:
117 import queue
118 except ImportError:
119 import Queue as queue
120
121 # figure out which FFT package we have, and import it
122 try:
123 from pyfftw.interfaces import numpy_fft, cache
124 # recorded variant of real fft that we will use
125 best_rfft = numpy_fft.rfft
126 # recorded variant of inverse real fft that we will use
127 best_irfft = numpy_fft.irfft
128 cache.enable()
129 cache.set_keepalive_time(1.0)
130 except ImportError:
131 best_rfft = numpy.fft.rfft
132 best_irfft = numpy.fft.irfft
133
134 # create a table of nice factorizations for the FFT package
135 #
136 # this is built once, and shared by all instances
137 # fftw can handle a variety of transform factorizations
138 # numpy fft is not too efficient for things other than a power of two,
139 # although my own measurements says it really does fine. For now, leave
140 # all factors available
141 ft_factors = [
142 2*2**i*3**j*5**k for i in range(20) for j in range(10) for k in range(8)
143 if 2*2**i*3**j*5**k <= 1000000
144 ]
145
146 ft_factors.sort()
147 ft_factors = numpy.array(ft_factors, numpy.int)
148
149 # used for debugging moments from FP_profile.axial_helper().
150 moment_list = []
151 # if this is *True*, compute and save moment errors
152 collect_moment_errors = False
153
154
155 class profile_data(object):
156 """
157 a skeleton class which makes a combined dict and namespace interface for
158 easy pickling and data passing
159 """
160
161 def __init__(self, **kwargs):
162 """
163 initialize the class
164
165 Parameters
166 ----------
167 kwargs : dict
168 keyword=value list to pre-populate the class
169 """
170 mydict = {}
171 mydict.update(kwargs)
172 for k, v in mydict.items():
173 setattr(self, k, v)
174 # a dictionary which shadows our attributes.
175 self.dictionary = mydict
176
177 def add_symbol(self, **kwargs):
178 """
179 add new symbols to both the attributes and dictionary for the class
180
181 Parameters
182 ----------
183 kwargs : dict
184 keyword=value pairs
185 """
186 self.dictionary.update(kwargs)
187 for k, v in kwargs.items():
188 setattr(self, k, v)
189
190
191 class FP_profile:
192 """
193 the main fundamental parameters class, which handles a single reflection.
194 This class is designed to be highly extensible by inheriting new
195 convolvers. When it is initialized, it scans its namespace for specially
196 formatted names, which can come from mixin classes. If it finds a function
197 name of the form conv_xxx, it will call this funtion to create a
198 convolver. If it finds a name of the form info_xxx it will associate the
199 dictionary with that convolver, which can be used in UI generation, for
200 example. The class, as it stands, does nothing significant with it. If it
201 finds str_xxx, it will use that function to format a printout of the
202 current state of the convolver conv_xxx, to allow improved report
203 generation for convolvers.
204
205 When it is asked to generate a profile, it calls all known convolvers.
206 Each convolver returns the Fourier transform of its convolvution. The
207 transforms are multiplied together, inverse transformed, and after fixing
208 the periodicity issue, subsampled, smoothed and returned.
209
210 If a convolver returns *None*, it is not multipled into the product.
211
212 Parameters
213 ----------
214 max_history_length : int
215 the number of histories to cache (default=5); can be overridden if
216 memory is an issue.
217 length_scale_m : float
218 length_scale_m sets scaling for nice printing of parameters. if the
219 units are in mm everywhere, set it to 0.001, e.g. convolvers which
220 implement their own str_xxx method may use this to format their
221 results, especially if 'natural' units are not meters. Typical is
222 wavelengths and lattices in nm or angstroms, for example.
223 """
224 max_history_length = 5 # max number of histories for each convolver
225 length_scale_m = 1.0
226 # class attribute to tell if convolvers in this class
227 # contain anisotropic convolvers
228 isotropic = True
229
230 def __init__(self, anglemode,
231 gaussian_smoother_bins_sigma=1.0,
232 oversampling=10):
233 """
234 initialize the instance
235
236 Parameters
237 ----------
238 anglemode : {'d', 'twotheta'}
239 if setup will be in terms of a d-spacing, otherwise 'twotheta' if
240 setup will be at a fixed 2theta value.
241 gaussian_smoother_bins_sigma : float
242 the number of bins for post-smoothing of data. 1.0 is good. *None*
243 means no final smoothing step.
244 oversampling : int
245 the number of bins internally which will get computed for each bin
246 the the final result.
247 """
248 if anglemode not in ("d", "twotheta"):
249 raise Exception(
250 "invalid angle mode %s, must be 'd' or 'twotheta'" % anglemode)
251 # set to either 'd' for d-spacing based position, or 'twotheta' for
252 # angle-based position
253 self.anglemode = anglemode
254 # sigma, in units of bins, for the final smoother.
255 self.gaussian_smoother_bins_sigma = gaussian_smoother_bins_sigma
256 # the number of internal bins computed for each bin in the final
257 # output. 5-10 is usually plenty.
258 self.oversampling = oversampling
259 # List of our convolvers, found by introspection of names beginning
260 # with 'conv_'
261 self.convolvers = convolvers = [
262 x for x in dir(self) if x.startswith("conv_")]
263 # A dictionary which will store all the parameters local to each
264 # convolution
265 self.param_dicts = dict([(c, {}) for c in convolvers])
266 # add global parameters, associated with no specific convolver
267 # A dictionary of bound functions to call to compute convolutions
268 self.convolver_funcs = dict(
269 [(x, getattr(self, x)) for x in convolvers])
270 # If *True*, print cache hit information
271 self.debug_cache = False
272 # keep a record of things we don't keep when pickled
273 self._clean_on_pickle = set()
274
275 @classmethod
276 def isequivalent(cls, hkl1, hkl2, crystalsystem):
277 """
278 function to determine if according to the convolvers included in this
279 class two sets of Miller indices are equivalent. This function is only
280 called when the class attribute 'isotropic' is False.
281
282 Parameters
283 ----------
284 hkl1, hkl2 : list or tuple
285 Miller indices to be checked for equivalence
286 crystalsystem : str
287 symmetry class of the material which is considered
288
289 Returns
290 -------
291 bool
292 """
293 return True
294
295 def get_function_name(self):
296 """
297 return the name of the function that called this. Useful for convolvers
298 to identify themselves
299
300 Returns
301 ----------
302 str
303 name of calling function
304 """
305 return sys._getframe(1).f_code.co_name
306
307 def add_buffer(self, b):
308 """
309 add a numpy array to the list of objects that can be thrown away on
310 pickling.
311
312 Parameters
313 ----------
314 b : array-like
315 the buffer to add to the list
316
317 Returns
318 -------
319 b : array-like
320 return the same buffer, to make nesting easy.
321 """
322 self._clean_on_pickle.add(id(b))
323 return b
324
325 def set_window(self, twotheta_window_center_deg,
326 twotheta_window_fullwidth_deg, twotheta_output_points):
327 """
328 move the compute window to a new location and compute grids, without
329 resetting all parameters. Clears convolution history and sets up many
330 arrays.
331
332 Parameters
333 ----------
334 twotheta_window_center_deg : float
335 the center position of the middle bin of the window, in degrees
336 twotheta_window_fullwidth_deg : float
337 the full width of the window, in degrees
338 twotheta_output_points : int
339 the number of bins in the final output
340 """
341 # the saved width of the window, in degrees
342 self.twotheta_window_fullwidth_deg = twotheta_window_fullwidth_deg
343 # the saved center of the window, in degrees
344 self.twotheta_window_center_deg = twotheta_window_center_deg
345 # the width of the window, in radians
346 window_fullwidth = math.radians(twotheta_window_fullwidth_deg)
347 self.window_fullwidth = window_fullwidth
348 # the center of the window, in radians
349 twotheta = math.radians(twotheta_window_center_deg)
350 self.twotheta_window_center = twotheta
351 # the number of points to compute in the final results
352 self.twotheta_output_points = twotheta_output_points
353 # the number of points in Fourier space to compute
354 nn = self.oversampling * twotheta_output_points // 2 + 1
355 self.n_omega_points = nn
356 # build all the arrays
357 b = self.add_buffer # shortcut
358
359 # a real-format scratch buffer
360 self._rb1 = b(numpy.zeros(nn, numpy.float))
361 # a real-format scratch buffer
362 self._rb2 = b(numpy.zeros(nn, numpy.float))
363 # a real-format scratch buffer
364 self._rb3 = b(numpy.zeros(nn, numpy.float))
365 # a complex-format scratch buffer
366 self._cb1 = b(numpy.zeros(nn, numpy.complex))
367 # a scratch buffer used by the axial helper
368 self._f0buf = b(numpy.zeros(self.oversampling *
369 twotheta_output_points, numpy.float))
370 # a scratch buffer used for axial divergence
371 self._epsb2 = b(numpy.zeros(self.oversampling *
372 twotheta_output_points, numpy.float))
373 # the I2+ buffer
374 self._I2p = b(numpy.zeros(self.oversampling *
375 twotheta_output_points, numpy.float))
376 # the I2- buffer
377 self._I2m = b(numpy.zeros(self.oversampling *
378 twotheta_output_points, numpy.float))
379 # another buffer used for axial divergence
380 self._axial = b(numpy.zeros(self.oversampling *
381 twotheta_output_points, numpy.float))
382 # the largest frequency in Fourier space
383 omega_max = self.n_omega_points * 2 * pi / window_fullwidth
384 # build the x grid and the complex array that is the convolver
385 # omega is in inverse radians in twotheta space (!)
386 # i.e. if a transform is written
387 # I(ds) = integral(A(L) exp(2 pi i L ds) dL
388 # where L is a real-space length, and s=2 sin(twotheta/2)/lambda
389 # then ds=2*pi*omega*cos(twotheta/2)/lambda (double check this!)
390 # The grid in Fourier space, in inverse radians
391 self.omega_vals = b(numpy.linspace(
392 0, omega_max, self.n_omega_points, endpoint=True))
393 # The grid in Fourier space, in inverse degrees
394 self.omega_inv_deg = b(numpy.radians(self.omega_vals))
395 # The grid in real space, in radians, with full oversampling
396 self.twothetasamples = b(numpy.linspace(
397 twotheta - window_fullwidth/2.0, twotheta + window_fullwidth/2.0,
398 self.twotheta_output_points * self.oversampling, endpoint=False))
399 # The grid in real space, in degrees, with full oversampling
400 self.twothetasamples_deg = b(numpy.linspace(
401 twotheta_window_center_deg - twotheta_window_fullwidth_deg/2.0,
402 twotheta_window_center_deg + twotheta_window_fullwidth_deg/2.0,
403 self.twotheta_output_points * self.oversampling, endpoint=False))
404 # Offsets around the center of the window, in radians
405 self.epsilon = b(self.twothetasamples - twotheta)
406
407 # A dictionary in which we collect recent state for each convolution.
408 # whenever the window gets reset, all of these get cleared
409 self.convolution_history = dict([(x, []) for x in self.convolvers])
410
411 # A dictionary of Lorentz widths, used for de-periodizing the final
412 # result.
413 self.lor_widths = {}
414
415 def get_good_bin_count(self, count):
416 """
417 find a bin count close to what we need, which works well for Fourier
418 transforms.
419
420 Parameters
421 ----------
422 count : int
423 a number of bins.
424
425 Returns
426 -------
427 int
428 a bin count somewhat larger than *count* which is efficient for FFT
429 """
430 return ft_factors[ft_factors.searchsorted(count)]
431
432 def set_optimized_window(self, twotheta_window_center_deg,
433 twotheta_approx_window_fullwidth_deg,
434 twotheta_exact_bin_spacing_deg):
435 """
436 pick a bin count which factors cleanly for FFT, and adjust the window
437 width to preserve the exact center and bin spacing
438
439 Parameters
440 ----------
441 twotheta_window_center_deg : float
442 exact position of center bin, in degrees
443 twotheta_approx_window_fullwidth_deg: float
444 approximate desired width
445 twotheta_exact_bin_spacing_deg: float
446 the exact bin spacing to use
447 """
448 bins = self.get_good_bin_count(
449 int(1 + twotheta_approx_window_fullwidth_deg /
450 twotheta_exact_bin_spacing_deg))
451 window_actwidth = twotheta_exact_bin_spacing_deg * bins
452 self.set_window(twotheta_window_center_deg=twotheta_window_center_deg,
453 twotheta_window_fullwidth_deg=window_actwidth,
454 twotheta_output_points=bins)
455
456 def set_parameters(self, convolver="global", **kwargs):
457 """
458 update the dictionary of parameters associated with the given convolver
459
460 Parameters
461 ----------
462 convolver : str
463 the name of the convolver. name 'global', e.g., attaches to
464 function 'conv_global'
465 kwargs : dict
466 keyword-value pairs to update the convolvers dictionary.
467 """
468 self.param_dicts["conv_" + convolver].update(kwargs)
469
470 def get_conv(self, name, key, format=numpy.float):
471 """
472 get a cached, pre-computed convolver associated with the given
473 parameters, or a newly zeroed convolver if the cache doesn't contain
474 it. Recycles old cache entries.
475
476 This takes advantage of the mutability of arrays. When the contents of
477 the array are changed by the convolver, the cached copy is implicitly
478 updated, so that the next time this is called with the same parameters,
479 it will return the previous array.
480
481 Parameters
482 ----------
483 name : str
484 the name of the convolver to seek
485 key : object
486 any hashable object which identifies the parameters for the
487 computation
488 format : numpy.dtype, optional
489 the type of the array to create, if one is not found.
490
491 Returns
492 -------
493 bool
494 flag, which is *True* if valid data were found, or *False* if the
495 returned array is zero, and *array*, which must be computed by the
496 convolver if *flag* was *False*.
497 """
498
499 # previous computed values as a list
500 history = self.convolution_history[name]
501 for idx, (k, b) in enumerate(history):
502 if k == key:
503 # move to front to mark recently used
504 history.insert(0, history.pop(idx))
505 if self.debug_cache:
506 print(name, True, file=sys.stderr)
507 return True, b # True says we got a buffer with valid data
508 if len(history) == self.max_history_length:
509 buf = history.pop(-1)[1] # re-use oldest buffer
510 buf[:] = 0
511 else:
512 buf = numpy.zeros(self.n_omega_points, format)
513 history.insert(0, (key, buf))
514 if self.debug_cache:
515 print(name, False, file=sys.stderr)
516 return False, buf # False says buffer is empty, need to recompute
517
518 def get_convolver_information(self):
519 """
520 return a list of convolvers, and what we know about them. function
521 scans for functions named conv_xxx, and associated info_xxx entries.
522
523 Returns
524 -------
525 list
526 list of (convolver_xxx, info_xxx) pairs
527 """
528 info_list = []
529 for k, f in self.convolver_funcs.items():
530 info = getattr(self, "info_" + k[5:], {})
531 info["docstring"] = f.__doc__
532 info_list.append((k, info))
533
534 return info_list
535
536 # A dictionary of default parameters for the global namespace,
537 # used to seed a GUI which can harvest this for names, descriptions, and
538 # initial values
539 info_global = dict(
540 group_name="Global parameters",
541 help="this should be help information",
542 param_info=dict(
543 twotheta0_deg=("Bragg center of peak (degrees)", 30.0),
544 d=("d spacing (m)", 4.00e-10),
545 dominant_wavelength=(
546 "wavelength of most intense line (m)", 1.5e-10)
547 )
548 )
549
550 def __str__(self):
551 """
552 return a nicely formatted report describing the current state of this
553 class. this looks for an str_xxx function associated with each conv_xxx
554 name. If it is found, that function if called to get the state of
555 conv_xxx. Otherwise, this simply formats the dictionary of parameters
556 for the convolver, and uses that.
557
558 Returns
559 -------
560 str
561 string of formatted information
562 """
563 keys = list(self.convolver_funcs.keys())
564 keys.sort() # always return info in the same order
565 # global is always first, anyways!
566 keys.insert(0, keys.pop(keys.index('conv_global')))
567 strings = ["", "***convolver id 0x%08x:" % id(self)]
568 for k in keys:
569 strfn = "str_" + k[5:]
570 if hasattr(self, strfn):
571 strings.append(getattr(self, strfn)())
572 else:
573 dd = self.param_dicts["conv_" + k[5:]]
574 if dd:
575 strings.append(k[5:] + ": " + str(dd))
576 return '\n'.join(strings)
577
578 def str_global(self):
579 """
580 returns a string representation for the global context.
581
582 Returns
583 -------
584 str
585 report on global parameters.
586 """
587 # in case it's not initialized
588 self.param_dicts["conv_global"].setdefault("d", 0)
589 return "global: peak center=%(twotheta0_deg).4f, d=%(d).8g, eq. "\
590 "div=%(equatorial_divergence_deg).3f" \
591 % self.param_dicts["conv_global"]
592
593 def conv_global(self):
594 """
595 a dummy convolver to hold global variables and information.
596 the global context isn't really a convolver, returning *None* means
597 ignore result
598
599 Returns
600 -------
601 *None*
602 always returns None
603 """
604 return None
605
606 def axial_helper(self, outerbound, innerbound, epsvals, destination,
607 peakpos=0, y0=0, k=0):
608 """
609 the function F0 from the paper. compute k/sqrt(peakpos-x)+y0 nonzero
610 between outer & inner (inner is closer to peak) or k/sqrt(x-peakpos)+y0
611 if reversed (i.e. if outer > peak) fully evaluated on a specified eps
612 grid, and stuff into destination
613
614 Parameters
615 ----------
616 outerbound : float
617 the edge of the function farthest from the singularity, referenced
618 to epsvals
619 innerbound : float
620 the edge closest to the singularity, referenced to epsvals
621 epsvals : array-like
622 the array of two-theta values or offsets
623 destination : array-like
624 an array into which final results are summed. modified in place!
625 peakpos : float
626 the position of the singularity, referenced to epsvals.
627 y0 : float
628 the constant offset
629 k : float
630 the scale factor
631
632 Returns
633 -------
634 lower_index, upper_index : int
635 python style bounds for region of `destination` which has been
636 modified.
637 """
638 if k == 0:
639 # nothing to do, point at the middle
640 return len(epsvals) // 2, len(epsvals) // 2 + 1
641
642 # bin width for normalizer so sum(result*dx)=exact integral
643 dx = epsvals[1] - epsvals[0]
644 # flag for whether tail is to the left or right.
645 flip = outerbound > peakpos
646
647 delta1 = abs(innerbound - peakpos)
648 delta2 = abs(outerbound - peakpos)
649
650 # this is the analytic area the function must have,
651 # integral(1/sqrt(eps0-eps)) from lower to upper
652 exactintegral = 2 * k * (sqrt(delta2) - sqrt(delta1))
653 exactintegral += y0 * (delta2 - delta1)
654 # exactintegral=max(0, exactintegral) # can never be < 0, beta out of
655 # range.
656 exactintegral *= 1 / dx # normalize so sum is right
657 # compute the exact centroid we need for this
658 if abs(delta2-delta1) < 1e-12:
659 exact_moment1 = 0
660 else:
661 exact_moment1 = ( # simplified from Mathematica FortranForm
662 (4*k*(delta2**1.5-delta1**1.5) + 3*y0*(delta2**2-delta1**2)) /
663 (6.*(2*k*(sqrt(delta2)-sqrt(delta1)) + y0*(delta2-delta1)))
664 )
665 if not flip:
666 exact_moment1 = -exact_moment1
667 exact_moment1 += peakpos
668
669 # note: because of the way the search is done, this produces a result
670 # with a bias of 1/2 channel to the left of where it should be.
671 # this is fixed by shifting all the parameters up 1/2 channel
672 outerbound += dx / 2
673 innerbound += dx / 2
674 peakpos += dx / 2 # fix 1/2 channel average bias from search
675 # note: searchsorted(side="left") always returns the position of the
676 # bin to the right of the match, or exact bin
677 idx0, idx1 = epsvals.searchsorted(
678 (outerbound, innerbound), side='left')
679
680 # peak has been squeezed out, nothing to do
681 if abs(outerbound - innerbound) < (2*dx) or abs(idx1 - idx0) < 2:
682 # preserve the exact centroid: requires summing into two channels
683 # for a peak this narrow, no attempt to preserve the width.
684 # note that x1 (1-f1) + (x1+dx) f1 = mu has solution
685 # (mu - x1) / dx = f1 thus, we want to sum into a channel that has
686 # x1<mu by less than dx, and the one to its right
687 # pick left edge and make sure we are past it
688 idx0 = min(idx0, idx1) - 1
689 while exact_moment1 - epsvals[idx0] > dx:
690 # normally only one step max, but make it a loop in case of
691 # corner case
692 idx0 += 1
693 f1 = (exact_moment1 - epsvals[idx0]) / dx
694 res = (exactintegral * (1 - f1), exactintegral * f1)
695 destination[idx0:idx0 + 2] += res
696 if collect_moment_errors:
697 centroid2 = (res * epsvals[idx0:idx0 + 2]).sum() / sum(res)
698 moment_list.append((centroid2 - exact_moment1) / dx)
699 return [idx0, idx0 + 2] # return collapsed bounds
700
701 if not flip:
702 if epsvals[idx0] != outerbound:
703 idx0 = max(idx0 - 1, 0)
704 idx1 = min(idx1 + 1, len(epsvals))
705 sign = 1
706 deps = self._f0buf[idx0:idx1]
707 deps[:] = peakpos
708 deps -= epsvals[idx0:idx1]
709 deps[-1] = peakpos - min(innerbound, peakpos)
710 deps[0] = peakpos - outerbound
711 else:
712 idx0, idx1 = idx1, idx0
713 if epsvals[idx0] != innerbound:
714 idx0 = max(idx0 - 1, 0)
715 idx1 = min(idx1 + 1, len(epsvals))
716 sign = -1
717 deps = self._f0buf[idx0:idx1]
718 deps[:] = epsvals[idx0:idx1]
719 deps -= peakpos
720 deps[0] = max(innerbound, peakpos) - peakpos
721 deps[-1] = outerbound - peakpos
722
723 dx0 = abs(deps[1] - deps[0])
724 dx1 = abs(deps[-1] - deps[-2])
725
726 # make the numerics accurate: compute average on each bin, which is
727 # integral of 1/sqrt = 2*sqrt, then difference integral
728 # do it in place, return value is actually deps too
729 intg = numpy.sqrt(deps, deps)
730 intg *= 2 * k * sign
731
732 # do difference in place, running forward to avoid self-trampling
733 intg[:-1] -= intg[1:]
734 intg[1:-2] += y0 * dx # add constant
735 # handle narrowed bins on ends carefully
736 intg[0] += y0 * dx0
737 intg[-2] += y0 * dx1
738
739 # intensities are never less than zero!
740 if min(intg[:-1]) < -1e-10 * max(intg[:-1]):
741 print("bad parameters:", (5 * "%10.4f") %
742 (peakpos, innerbound, outerbound, k, y0))
743 print(len(intg), intg[:-1])
744 raise ValueError("Bad axial helper parameters")
745
746 # now, make sure the underlying area is the exactly correct
747 # integral, without bumps due to discretizing the grid.
748 intg *= (exactintegral / (intg[:-1].sum()))
749
750 destination[idx0:idx1 - 1] += intg[:-1]
751
752 # This is purely for debugging. If collect_moment_errors is *True*,
753 # compute exact vs. approximate moments.
754 if collect_moment_errors:
755 centroid2 = (intg[:-1] * epsvals[idx0:idx1 - 1]
756 ).sum() / intg[:-1].sum()
757 moment_list.append((centroid2 - exact_moment1) / dx)
758
759 return [idx0, idx1 - 1] # useful info for peak position
760
761 def full_axdiv_I2(self, Lx=None, Ls=None, Lr=None, R=None, twotheta=None,
762 beta=None, epsvals=None):
763 """
764 return the *I2* function
765
766 Parameters
767 ----------
768 Lx : float
769 length of the xray filament
770 Ls : float
771 length of the sample
772 Lr : float
773 length of the receiver slit
774 R : float
775 diffractometer length, assumed symmetrical
776 twotheta : float
777 angle, in radians, of the center of the computation
778 beta : float
779 offset angle
780 epsvals : array-like
781 array of offsets from center of computation, in radians
782
783 Returns
784 -------
785 epsvals : array-like
786 array of offsets from center of computation, in radians
787 idxmin, idxmax : int
788 the full python-style bounds of the non-zero region of `I2p`
789 and `I2m`
790 I2p, I2m : array-like
791 I2+ and I2- from the paper, the contributions to the intensity
792 """
793 beta1 = (Ls - Lx) / (2 * R) # Ch&Co after eq. 15abcd
794 beta2 = (Ls + Lx) / (2 * R) # Ch&Co after eq. 15abcd, corrected by KM
795
796 eps0 = beta * beta * tan(twotheta) / 2 # after eq. 26 in Ch&Co
797
798 if -beta2 <= beta < beta1:
799 z0p = Lx / 2 + beta * R * (1 + 1 / cos(twotheta))
800 elif beta1 <= beta <= beta2:
801 z0p = Ls / 2 + beta * R / cos(twotheta)
802
803 if -beta2 <= beta <= -beta1:
804 z0m = -1 * Ls / 2 + beta * R / cos(twotheta)
805 elif -beta1 < beta <= beta2:
806 z0m = -1 * Lx / 2 + beta * R * (1 + 1 / cos(twotheta))
807
808 epsscale = tan(pi / 2 - twotheta) / (2 * R * R) # =cotan(twotheta)...
809
810 # Ch&Co 18a&18b, KM sign correction
811 eps1p = (eps0 - epsscale * ((Lr / 2) - z0p)**2)
812 eps2p = (eps0 - epsscale * ((Lr / 2) - z0m)**2)
813 # reversed eps2m and eps1m per KM R
814 eps2m = (eps0 - epsscale * ((Lr / 2) + z0p)**2)
815 eps1m = (eps0 - epsscale * ((Lr / 2) + z0m)**2) # flip all epsilons
816
817 if twotheta > pi/2:
818 # this set of inversions from KM 'R' code, simplified here
819 eps1p, eps2p, eps1m, eps2m = eps1m, eps2m, eps1p, eps2p
820
821 # identify ranges per Ch&Co 4.2.2 and table 1 and select parameters
822 # note table 1 is full of typos, but the minimized
823 # tests from 4.2.2 with redundancies removed seem fine.
824 if Lr > z0p - z0m:
825 if z0p <= Lr/2 and z0m > -1*Lr/2: # beam entirely within slit
826 rng = 1
827 ea = eps1p
828 eb = eps2p
829 ec = eps1m
830 ed = eps2m
831 elif (z0p > Lr/2 and z0m < Lr/2) or \
832 (z0m < -1*Lr/2 and z0p > -1*Lr/2):
833 rng = 2
834 ea = eps2p
835 eb = eps1p
836 ec = eps1m
837 ed = eps2m
838 else:
839 rng = 3
840 ea = eps2p
841 eb = eps1p
842 ec = eps1m
843 ed = eps2m
844 else:
845 # beam hanging off both ends of slit, peak centered
846 if z0m < -1*Lr/2 and z0p > Lr/2:
847 rng = 1
848 ea = eps1m
849 eb = eps2p
850 ec = eps1p
851 ed = eps2m
852 # one edge of beam within slit
853 elif (-1*Lr/2 < z0m < Lr/2 and z0p > Lr/2) or \
854 (-1*Lr/2 < z0p < Lr/2 and z0m < -1*Lr/2):
855 rng = 2
856 ea = eps2p
857 eb = eps1m
858 ec = eps1p
859 ed = eps2m
860 else:
861 rng = 3
862 ea = eps2p
863 eb = eps1m
864 ec = eps1p
865 ed = eps2m
866
867 # now, evaluate function on bounds in table 1 based on ranges
868 # note: because of a sign convention in epsilon, the bounds all get
869 # switched
870
871 # define them in our namespace so they inherit ea, eb, ec, ed, etc.
872 def F1(dst, lower, upper, eea, eeb):
873 return self.axial_helper(destination=dst,
874 innerbound=upper, outerbound=lower,
875 epsvals=epsvals, peakpos=eps0,
876 k=sqrt(abs(eps0-eeb))-sqrt(abs(eps0-eea)),
877 y0=0)
878
879 def F2(dst, lower, upper, eea):
880 return self.axial_helper(destination=dst,
881 innerbound=upper, outerbound=lower,
882 epsvals=epsvals, peakpos=eps0,
883 k=sqrt(abs(eps0 - eea)), y0=-1)
884
885 def F3(dst, lower, upper, eea):
886 return self.axial_helper(destination=dst,
887 innerbound=upper, outerbound=lower,
888 epsvals=epsvals, peakpos=eps0,
889 k=sqrt(abs(eps0 - eea)), y0=+1)
890
891 def F4(dst, lower, upper, eea):
892 # just like F2 but k and y0 negated
893 return self.axial_helper(destination=dst,
894 innerbound=upper, outerbound=lower,
895 epsvals=epsvals, peakpos=eps0,
896 k=-sqrt(abs(eps0 - eea)), y0=+1)
897
898 I2p = self._I2p
899 I2p[:] = 0
900 I2m = self._I2m
901 I2m[:] = 0
902
903 indices = []
904 if rng == 1:
905 indices += F1(dst=I2p, lower=ea, upper=eps0, eea=ea, eeb=eb)
906 indices += F2(dst=I2p, lower=eb, upper=ea, eea=eb)
907 indices += F1(dst=I2m, lower=ec, upper=eps0, eea=ec, eeb=ed)
908 indices += F2(dst=I2m, lower=ed, upper=ec, eea=ed)
909 elif rng == 2:
910 indices += F2(dst=I2p, lower=ea, upper=eps0, eea=ea)
911 indices += F3(dst=I2m, lower=eb, upper=eps0, eea=ea)
912 indices += F1(dst=I2m, lower=ec, upper=eb, eea=ec, eeb=ed)
913 indices += F2(dst=I2m, lower=ed, upper=ec, eea=ed)
914 elif rng == 3:
915 indices += F4(dst=I2m, lower=eb, upper=ea, eea=ea)
916 indices += F1(dst=I2m, lower=ec, upper=eb, eea=ec, eeb=ed)
917 indices += F2(dst=I2m, lower=ed, upper=ec, eea=ed)
918
919 idxmin = min(indices)
920 idxmax = max(indices)
921
922 return epsvals, idxmin, idxmax, I2p, I2m
923
924 def full_axdiv_I3(self, Lx=None, Ls=None, Lr=None, R=None,
925 twotheta=None, epsvals=None, sollerIdeg=None,
926 sollerDdeg=None, nsteps=10, axDiv=""):
927 """
928 carry out the integral of *I2* over *beta* and the Soller slits.
929
930 Parameters
931 ----------
932 Lx : float
933 length of the xray filament
934 Ls : float
935 length of the sample
936 Lr : float
937 length of the receiver slit
938 R : float
939 the (assumed symmetrical) diffractometer radius
940 twotheta : float
941 angle, in radians, of the center of the computation
942 epsvals : array-like
943 array of offsets from center of computation, in radians
944 sollerIdeg : float
945 the full-width (both sides) cutoff angle of the incident Soller
946 slit
947 sollerDdeg : float
948 the full-width (both sides) cutoff angle of the detector Soller
949 slit
950 nsteps : int
951 the number of subdivisions for the integral
952 axDiv : str
953 not used
954
955 Returns
956 -------
957 array-like
958 the accumulated integral, a copy of a persistent buffer *_axial*
959 """
960 beta2 = (Ls + Lx) / (2 * R) # Ch&Co after eq. 15abcd, corrected by KM
961
962 if sollerIdeg is not None:
963 solIrad = math.radians(sollerIdeg) / 2
964
965 def solIfunc(x):
966 return numpy.clip(1.0 - abs(x / solIrad), 0, 1)
967 beta2 = min(beta2, solIrad) # no point going beyond Soller
968 else:
969 def solIfunc(x):
970 return numpy.ones_like(x)
971 if sollerDdeg is not None:
972 solDrad = math.radians(sollerDdeg) / 2
973
974 def solDfunc(x):
975 return numpy.clip(1.0 - abs(x / solDrad), 0, 1)
976 else:
977 def solDfunc(x):
978 return numpy.ones_like(x)
979
980 accum = self._axial
981 accum[:] = 0
982
983 if twotheta > pi / 2:
984 tth1 = pi - twotheta
985 else:
986 tth1 = twotheta
987
988 for iidx in range(nsteps):
989 beta = beta2 * iidx / float(nsteps)
990
991 eps, idxmin, idxmax, I2p, I2m = self.full_axdiv_I2(
992 Lx=Lx, Lr=Lr, Ls=Ls, beta=beta, R=R,
993 twotheta=twotheta, epsvals=epsvals)
994
995 # after eq. 26 in Ch&Co
996 eps0 = beta * beta * tan(twotheta) / 2
997
998 gamma0 = beta / cos(tth1)
999 deps = self._f0buf[idxmin:idxmax]
1000 deps[:] = eps0
1001 deps -= epsvals[idxmin:idxmax]
1002 deps *= 2 * tan(twotheta)
1003 # check two channels on each end for negative argument.
1004 deps[-1] = max(deps[-1], 0)
1005 deps[0] = max(deps[0], 0)
1006 if len(deps) >= 2:
1007 deps[-2] = max(deps[-2], 0)
1008 deps[1] = max(deps[1], 0)
1009
1010 gamarg = numpy.sqrt(deps, deps) # do sqrt in place for speed
1011 # still need to convert these to in-place
1012 gamp = gamma0 + gamarg
1013 gamm = gamma0 - gamarg
1014
1015 if iidx == 0 or iidx == nsteps - 1:
1016 weight = 1.0 # trapezoidal rule weighting
1017 else:
1018 weight = 2.0
1019
1020 # sum into the accumulator only channels which can be non-zero
1021 # do scaling in-place to save a lot of slow array copying
1022 I2p[idxmin:idxmax] *= solDfunc(gamp)
1023 I2p[idxmin:idxmax] *= (weight * solIfunc(beta))
1024 accum[idxmin:idxmax] += I2p[idxmin:idxmax]
1025 I2m[idxmin:idxmax] *= solDfunc(gamm)
1026 I2m[idxmin:idxmax] *= (weight * solIfunc(beta))
1027 accum[idxmin:idxmax] += I2m[idxmin:idxmax]
1028
1029 # keep this normalized
1030 K = 2 * R * R * abs(tan(twotheta))
1031 accum *= K
1032
1033 return accum
1034
1035 def conv_axial(self):
1036 """
1037 compute the Fourier transform of the axial divergence component
1038
1039 Returns
1040 -------
1041 array-like
1042 the transform buffer, or *None* if this is being ignored
1043 """
1044 me = self.get_function_name() # the name of the convolver, as a string
1045 if self.param_dicts[me].get("axDiv", None) is None:
1046 return None
1047 kwargs = {}
1048 kwargs.update(self.param_dicts[me]) # get all of our parameters
1049 kwargs.update(self.param_dicts["conv_global"])
1050 if "equatorial_divergence_deg" in kwargs:
1051 del kwargs["equatorial_divergence_deg"] # not used
1052
1053 flag, axfn = self.get_conv(me, kwargs, numpy.complex)
1054 if flag:
1055 return axfn # already up to date if first return is True
1056
1057 xx = type("data", (), kwargs)
1058 # no axial divergence, transform of delta fn
1059 if xx.axDiv != "full" or xx.twotheta0_deg == 90.0:
1060 axfn[:] = 1
1061 return axfn
1062 else:
1063 axbuf = self.full_axdiv_I3(
1064 nsteps=xx.n_integral_points,
1065 epsvals=self.epsilon,
1066 Lx=xx.slit_length_source,
1067 Lr=xx.slit_length_target,
1068 Ls=xx.length_sample,
1069 sollerIdeg=xx.angI_deg,
1070 sollerDdeg=xx.angD_deg,
1071 R=xx.diffractometer_radius,
1072 twotheta=xx.twotheta0
1073 )
1074 axfn[:] = best_rfft(axbuf)
1075
1076 return axfn
1077
1078 def conv_tube_tails(self):
1079 """
1080 compute the Fourier transform of the rectangular tube tails function
1081
1082 Returns
1083 -------
1084 array-like
1085 the transform buffer, or *None* if this is being ignored
1086 """
1087 me = self.get_function_name() # the name of the convolver, as a string
1088 kwargs = {}
1089 kwargs.update(self.param_dicts[me]) # get all of our parameters
1090 if not kwargs:
1091 return None # no convolver
1092 # we also need the diffractometer radius from the global space
1093 kwargs["diffractometer_radius"] = self.param_dicts[
1094 "conv_global"]["diffractometer_radius"]
1095 flag, tailfn = self.get_conv(me, kwargs, numpy.complex)
1096 if flag:
1097 return tailfn # already up to date
1098
1099 # tube_tails is (main width, left width, right width, intensity),
1100 # so peak is raw peak + tophat centered at (left width+ right width)
1101 # with area intensity*(right width-left width)/main_width
1102 # check this normalization!
1103 # note: this widths are as defined by Topas... really I think it should
1104 # be
1105 # x/(2*diffractometer_radius) since the detector is 2R from the source,
1106 # but since this is just a fit parameter, we'll defin it as does Topas
1107 xx = type("data", (), kwargs) # allow dotted notation
1108
1109 tail_eps = (xx.tail_right - xx.tail_left) / xx.diffractometer_radius
1110 main_eps = xx.main_width / xx.diffractometer_radius
1111 tail_center = (xx.tail_right + xx.tail_left) / \
1112 xx.diffractometer_radius / 2.0
1113 tail_area = xx.tail_intens * \
1114 (xx.tail_right - xx.tail_left) / xx.main_width
1115
1116 cb1 = self._cb1
1117 rb1 = self._rb1
1118
1119 cb1.real = 0
1120 cb1.imag = self.omega_vals
1121 cb1.imag *= tail_center # sign is consistent with Topas definition
1122 numpy.exp(cb1, tailfn) # shifted center, computed into tailfn
1123
1124 rb1[:] = self.omega_vals
1125 rb1 *= (tail_eps / 2 / pi)
1126 rb1 = numpy.sinc(rb1)
1127 tailfn *= rb1
1128 tailfn *= tail_area # normalize
1129
1130 rb1[:] = self.omega_vals
1131 rb1 *= (main_eps / 2 / pi)
1132 rb1 = numpy.sinc(rb1)
1133 tailfn += rb1 # add central peak
1134 return tailfn
1135
1136 def general_tophat(self, name="", width=None):
1137 """
1138 a utility to compute a transformed tophat function and save it in a
1139 convolver buffer
1140
1141 Parameters
1142 ----------
1143 name : str
1144 the name of the convolver cache buffer to update
1145 width : float
1146 the width in 2-theta space of the tophat
1147
1148 Returns
1149 -------
1150 array-like
1151 the updated convolver buffer, or *None* if the width was *None*
1152 """
1153 if width is None:
1154 return # no convolver
1155 flag, conv = self.get_conv(name, width, numpy.float)
1156 if flag:
1157 return conv # already up to date
1158 rb1 = self._rb1
1159 rb1[:] = self.omega_vals
1160 rb1 *= (width / 2 / pi)
1161 conv[:] = numpy.sinc(rb1)
1162 return conv
1163
1164 # A dictionary of default parameters for conv_emissions,
1165 # used to seed a GUI which can harvest this for names, descriptions, and
1166 # initial values
1167 info_emission = dict(
1168 group_name="Incident beam and crystal size",
1169 help="this should be help information",
1170 param_info=dict(
1171 emiss_wavelengths=("wavelengths (m)", (1.58e-10,)),
1172 emiss_intensities=("relative intensities", (1.00,)),
1173 emiss_lor_widths=("Lorenztian emission fwhm (m)", (1e-13,)),
1174 emiss_gauss_widths=("Gaussian emissions fwhm (m)", (1e-13,)),
1175 crystallite_size_gauss=(
1176 "Gaussian crystallite size fwhm (m)", 1e-6),
1177 crystallite_size_lor=(
1178 "Lorentzian crystallite size fwhm (m)", 1e-6),
1179 )
1180 )
1181
1182 def str_emission(self):
1183 """
1184 format the emission spectrum and crystal size information
1185
1186 Returns
1187 -------
1188 str
1189 the formatted information
1190 """
1191 dd = self.param_dicts["conv_emission"]
1192 if not dd:
1193 return "No emission spectrum"
1194 dd.setdefault("crystallite_size_lor", 1e10)
1195 dd.setdefault("crystallite_size_gauss", 1e10)
1196 dd.setdefault("strain_lor", 0)
1197 dd.setdefault("strain_gauss", 0)
1198 xx = type("data", (), dd)
1199 spect = numpy.array((
1200 xx.emiss_wavelengths, xx.emiss_intensities,
1201 xx.emiss_lor_widths, xx.emiss_gauss_widths))
1202 # convert to Angstroms, like Topas
1203 spect[0] *= 1e10 * self.length_scale_m
1204 spect[2] *= 1e13 * self.length_scale_m # milli-Angstroms
1205 spect[3] *= 1e13 * self.length_scale_m # milli-Angstroms
1206 nm = 1e9 * self.length_scale_m
1207 items = ["emission and broadening:"]
1208 items.append("spectrum=\n" + str(spect.transpose()))
1209 items.append("crystallite_size_lor (nm): %.5g" %
1210 (xx.crystallite_size_lor * nm))
1211 items.append("crystallite_size_gauss (nm): %.5g" %
1212 (xx.crystallite_size_gauss * nm))
1213 items.append("strain_lor: %.5g" % xx.strain_lor)
1214 items.append("strain_gauss: %.5g" % xx.strain_gauss)
1215 return '\n'.join(items)
1216
1217 def conv_emission(self):
1218 """
1219 compute the emission spectrum and (for convenience) the particle size
1220 widths
1221
1222 Returns
1223 -------
1224 array-like
1225 the convolver for the emission and particle sizes
1226
1227 Note:
1228 the particle size and strain stuff here is just to be consistent
1229 with *Topas* and to be vaguely efficient about the computation,
1230 since all of these have the same general shape.
1231 """
1232 me = self.get_function_name() # the name of the convolver, as a string
1233 kwargs = {}
1234 kwargs.update(self.param_dicts[me]) # get all of our parameters
1235 kwargs.update(self.param_dicts["conv_global"])
1236 # if the crystallite size and strain parameters are not set, set them
1237 # to values that make their corrections disappear
1238 kwargs.setdefault("crystallite_size_lor", 1e10)
1239 kwargs.setdefault("crystallite_size_gauss", 1e10)
1240 kwargs.setdefault("strain_lor", 0)
1241 kwargs.setdefault("strain_gauss", 0)
1242
1243 # convert arrays to lists for key checking
1244 key = {}
1245 key.update(kwargs)
1246 for k, v in key.items():
1247 if hasattr(v, 'tolist'):
1248 key[k] = v.tolist()
1249
1250 flag, emiss = self.get_conv(me, key, numpy.complex)
1251 if flag:
1252 return emiss # already up to date
1253
1254 xx = type("data", (), kwargs) # make it dot-notation accessible
1255
1256 epsilon0s = (2 * nasin(asarray(xx.emiss_wavelengths)/(2.0*xx.d)) -
1257 xx.twotheta0)
1258 theta = xx.twotheta0 / 2
1259 # Emission profile FWHM + crystallite broadening (scale factors are
1260 # Topas choice!) (Lorentzian)
1261 # note: the strain broadenings in Topas are expressed in degrees
1262 # 2theta, must convert to radians(theta) with pi/360
1263 widths = (
1264 (asarray(xx.emiss_lor_widths) / asarray(xx.emiss_wavelengths)) *
1265 tan(theta) + math.radians(xx.strain_lor) / 2 * tan(theta) +
1266 (asarray(xx.emiss_wavelengths) /
1267 (2*xx.crystallite_size_lor*cos(theta)))
1268 )
1269 # save weighted average width for future reference in periodicity fixer
1270 self.lor_widths[me] = sum(
1271 widths * xx.emiss_intensities) / sum(xx.emiss_intensities)
1272 # gaussian bits add in quadrature
1273 gfwhm2s = (
1274 ((2*asarray(xx.emiss_gauss_widths)/asarray(xx.emiss_wavelengths)) *
1275 tan(theta))**2 +
1276 (math.radians(xx.strain_gauss) / 2 * tan(theta))**2 +
1277 (asarray(xx.emiss_wavelengths) /
1278 (xx.crystallite_size_gauss*cos(theta)))**2
1279 )
1280
1281 # note that the Fourier transform of a lorentzian with FWHM 2a
1282 # is exp(-abs(a omega))
1283 # now, the line profiles in Fourier space have to have phases
1284 # carefully handled to put the lines in the right places.
1285 # note that the transform of f(x+dx)=exp(i omega dx) f~(x)
1286 omega_vals = self.omega_vals
1287 for wid, gfwhm2, eps, intens in zip(widths, gfwhm2s, epsilon0s,
1288 xx.emiss_intensities):
1289 xvals = numpy.clip(omega_vals * (-wid), -100, 0)
1290 sig2 = gfwhm2 / (8 * math.log(2.0)) # convert fwhm**2 to sigma**2
1291 gxv = numpy.clip((sig2 / -2.0) * omega_vals * omega_vals, -100, 0)
1292 emiss += numpy.exp(xvals + gxv + complex(0, -eps) *
1293 omega_vals) * intens
1294 return emiss
1295
1296 def conv_flat_specimen(self):
1297 """
1298 compute the convolver for the flat-specimen correction
1299
1300 Returns
1301 -------
1302 array-like
1303 the convolver
1304 """
1305 me = self.get_function_name() # the name of the convolver, as a string
1306 equatorial_divergence_deg = self.param_dicts[
1307 "conv_global"].get("equatorial_divergence_deg", None)
1308 if not equatorial_divergence_deg:
1309 return None
1310 twotheta0 = self.param_dicts["conv_global"]["twotheta0"]
1311 key = (twotheta0, equatorial_divergence_deg)
1312 flag, conv = self.get_conv(me, key, numpy.complex)
1313 if flag:
1314 return conv # already up to date
1315
1316 # Flat-specimen error, from Cheary, Coelho & Cline 2004 NIST eq. 9 & 10
1317 # compute epsm in radians from eq. divergence in degrees
1318 # to make it easy to use the axial_helper to compute the function
1319 epsm = math.radians(equatorial_divergence_deg)**2 /\
1320 tan(twotheta0/2.0) / 2.0
1321 eqdiv = self._epsb2
1322 eqdiv[:] = 0
1323 dtwoth = (self.twothetasamples[1] - self.twothetasamples[0])
1324 idx0, idx1 = self.axial_helper(destination=eqdiv,
1325 outerbound=-epsm,
1326 innerbound=0,
1327 epsvals=self.epsilon,
1328 peakpos=0, k=dtwoth/(2.0*sqrt(epsm)))
1329
1330 conv[:] = best_rfft(eqdiv)
1331 conv[1::2] *= -1 # flip center
1332 return conv
1333
1334 def conv_absorption(self):
1335 """
1336 compute the sample transparency correction, including the
1337 finite-thickness version
1338
1339 Returns
1340 -------
1341 array-like
1342 the convolver
1343 """
1344 me = self.get_function_name() # the name of the convolver, as a string
1345 kwargs = {}
1346 kwargs.update(self.param_dicts[me]) # get all of our parameters
1347 if not kwargs:
1348 return None
1349 kwargs["twotheta0"] = self.param_dicts["conv_global"]["twotheta0"]
1350 kwargs["diffractometer_radius"] = self.param_dicts[
1351 "conv_global"]["diffractometer_radius"]
1352
1353 flag, conv = self.get_conv(me, kwargs, numpy.complex)
1354 if flag:
1355 return conv # already up to date
1356 xx = type("data", (), kwargs) # make it dot-notation accessible
1357
1358 # absorption, from Cheary, Coelho & Cline 2004 NIST eq. 12,
1359 # EXCEPT delta = 1/(2*mu*R) instead of 2/(mu*R)
1360 # from Mathematica, unnormalized transform is
1361 # (1-exp(epsmin*(i w + 1/delta)))/(i w + 1/delta)
1362 delta = sin(xx.twotheta0) / (2 * xx.absorption_coefficient *
1363 xx.diffractometer_radius)
1364 # arg=(1/delta)+complex(0,-1)*omega_vals
1365 cb = self._cb1
1366 cb.imag = self.omega_vals
1367 cb.imag *= -1
1368 cb.real = 1 / delta
1369 numpy.reciprocal(cb, conv) # limit for thick samples=1/(delta*arg)
1370 conv *= 1.0 / delta # normalize
1371 # rest of transform of function with cutoff
1372 if kwargs.get("sample_thickness", None) is not None:
1373 epsmin = -2.0 * xx.sample_thickness * \
1374 cos(xx.twotheta0 / 2.0) / xx.diffractometer_radius
1375 cb *= epsmin
1376 numpy.expm1(cb, cb)
1377 cb *= -1
1378 conv *= cb
1379 return conv
1380
1381 def conv_displacement(self):
1382 """
1383 compute the peak shift due to sample displacement and the *2theta* zero
1384 offset
1385
1386 Returns
1387 -------
1388 array-like
1389 the convolver
1390 """
1391 me = self.get_function_name() # the name of the convolver, as a string
1392 kwargs = self.param_dicts[me]
1393 twotheta0 = self.param_dicts["conv_global"]["twotheta0"]
1394 diffractometer_radius = self.param_dicts[
1395 "conv_global"]["diffractometer_radius"]
1396 specimen_displacement = kwargs.get("specimen_displacement", 0.0)
1397 if specimen_displacement is None:
1398 specimen_displacement = 0.0
1399 zero_error_deg = kwargs.get("zero_error_deg", 0.0)
1400 if zero_error_deg is None:
1401 zero_error_deg = 0.0
1402
1403 flag, conv = self.get_conv(me,
1404 (twotheta0, diffractometer_radius,
1405 specimen_displacement, zero_error_deg),
1406 numpy.complex)
1407 if flag:
1408 return conv # already up to date
1409
1410 delta = -2 * cos(twotheta0 / 2.0) * \
1411 specimen_displacement / diffractometer_radius
1412 conv.real = 0
1413 conv.imag = self.omega_vals
1414 # convolver *= numpy.exp(complex(0, -delta-zero_error_deg*pi/180.0) *
1415 # omega_vals)
1416 conv.imag *= (-delta - math.radians(zero_error_deg) -
1417 (twotheta0 - self.twotheta_window_center))
1418 numpy.exp(conv, conv)
1419 return conv
1420
1421 def conv_receiver_slit(self):
1422 """
1423 compute the rectangular convolution for the receiver slit or SiPSD
1424 pixel size
1425
1426 Returns
1427 -------
1428 array-like
1429 the convolver
1430 """
1431 me = self.get_function_name() # the name of the convolver, as a string
1432 # The receiver slit convolution is a top-hat of angular half-width
1433 # a=(slit_width/2)/diffractometer_radius
1434 # which has Fourier transform of sin(a omega)/(a omega)
1435 # NOTE! numpy's sinc(x) is sin(pi x)/(pi x), not sin(x)/x
1436 if self.param_dicts[me].get("slit_width", None) is None:
1437 return None
1438
1439 epsr = (self.param_dicts["conv_receiver_slit"]["slit_width"] /
1440 self.param_dicts["conv_global"]["diffractometer_radius"])
1441 return self.general_tophat(me, epsr)
1442
1443 def conv_si_psd(self):
1444 """
1445 compute the convolver for the integral of defocusing of the face of an
1446 Si PSD
1447
1448 Returns
1449 -------
1450 array-like
1451 the convolver
1452 """
1453 # omega offset defocussing from Cheary, Coelho & Cline 2004 eq. 15
1454 # expressed in terms of a Si PSD looking at channels with vertical
1455 # offset from the center between psd_window_lower_offset and
1456 # psd_window_upper_offset do this last, because we may ultimately take
1457 # a list of bounds, and return a list of convolutions, for efficiency
1458 me = self.get_function_name() # the name of the convolver, as a string
1459 kwargs = {}
1460 kwargs.update(self.param_dicts[me]) # get all of our parameters
1461 if not kwargs:
1462 return None
1463 kwargs.update(self.param_dicts["conv_global"])
1464
1465 flag, conv = self.get_conv(me, kwargs, numpy.float)
1466 if flag:
1467 return conv # already up to date
1468
1469 xx = type("data", (), kwargs)
1470
1471 if not xx.equatorial_divergence_deg or not xx.si_psd_window_bounds:
1472 # if either of these is zero or None, convolution is trivial
1473 conv[:] = 1
1474 return conv
1475
1476 psd_lower_window_pos, psd_upper_window_pos = xx.si_psd_window_bounds
1477 dthl = psd_lower_window_pos / xx.diffractometer_radius
1478 dthu = psd_upper_window_pos / xx.diffractometer_radius
1479 alpha = math.radians(xx.equatorial_divergence_deg)
1480 argscale = alpha / (2.0 * tan(xx.twotheta0 / 2))
1481 # WARNING si(x)=integral(sin(x)/x), not integral(sin(pi x)/(pi x))
1482 # i.e. they sinc function is not consistent with the si function
1483 # whence the missing pi in the denominator of argscale
1484 rb1 = self._rb1
1485 rb2 = self._rb2
1486 rb3 = self._rb3
1487 rb1[:] = self.omega_vals
1488 rb1 *= argscale * dthu
1489 sici(rb1, conv, rb3) # gets both sine and cosine integral, si in conv
1490 if dthl: # no need to do this if the lower bound is 0
1491 rb1[:] = self.omega_vals
1492 rb1 *= argscale * dthl
1493 sici(rb1, rb2, rb3) # gets sine and cosine integral, si in rb2
1494 conv -= rb2
1495 conv[1:] /= self.omega_vals[1:]
1496 conv *= 1 / argscale
1497 conv[0] = dthu - dthl # fix 0/0 with proper area
1498 return conv
1499
1500 def conv_smoother(self):
1501 """
1502 compute the convolver to smooth the final result with a Gaussian before
1503 downsampling.
1504
1505 Returns
1506 -------
1507 array-like
1508 the convolver
1509 """
1510 # create a smoother for output result, independent of real physics, if
1511 # wanted
1512 me = self.get_function_name() # the name of the convolver, as a string
1513 if not self.gaussian_smoother_bins_sigma:
1514 return # no smoothing
1515 flag, buf = self.get_conv(me, self.gaussian_smoother_bins_sigma,
1516 format=numpy.float)
1517 if flag:
1518 return buf # already computed
1519 buf[:] = self.omega_vals
1520 buf *= (self.gaussian_smoother_bins_sigma * (
1521 self.twothetasamples[1] - self.twothetasamples[0]))
1522 buf *= buf
1523 buf *= -0.5
1524 numpy.exp(buf, buf)
1525 return buf
1526
1527 def compute_line_profile(self, convolver_names=None,
1528 compute_derivative=False,
1529 return_convolver=False):
1530 """
1531 execute all the convolutions; if convolver_names is None, use
1532 everything we have, otherwise, use named convolutions.
1533
1534 Parameters
1535 ----------
1536 convolver_names: list
1537 a list of convolvers to select. If *None*, use all found
1538 convolvers.
1539 compute_derivative: bool
1540 if *True*, also return d/dx(function) for peak position fitting
1541
1542 Returns
1543 -------
1544 object
1545 a profile_data object with much information about the peak
1546 """
1547
1548 # create a function which is the Fourier transform of the
1549 # combined convolutions of all the factors
1550
1551 # ="d" if we are using 'd' space, "twotheta" if using twotheta
1552 anglemode = self.anglemode
1553
1554 # the rough center of the spectrum, used for things which need it.
1555 # Copied from global convolver.
1556 self.dominant_wavelength = dominant_wavelength = self.param_dicts[
1557 "conv_global"].get("dominant_wavelength", None)
1558
1559 if anglemode == "twotheta":
1560 twotheta0_deg = self.param_dicts["conv_global"]["twotheta0_deg"]
1561 twotheta0 = math.radians(twotheta0_deg)
1562 d = dominant_wavelength / (2 * sin(twotheta0 / 2.0))
1563 else:
1564 d = self.param_dicts["conv_global"]["d"]
1565 twotheta0 = 2 * math.asin(dominant_wavelength / (2.0 * d))
1566 twotheta0_deg = math.degrees(twotheta0)
1567
1568 # set these in global namespace
1569 self.set_parameters(d=d, twotheta0=twotheta0,
1570 twotheta0_deg=twotheta0_deg)
1571
1572 if convolver_names is None:
1573 convolver_names = self.convolver_funcs.keys() # get all names
1574
1575 # run through the name list, and call the convolver to harvest its
1576 # result
1577 conv_list = [self.convolver_funcs[x]() for x in convolver_names]
1578
1579 # now, multiply everything together
1580 convolver = self._cb1 # get a complex scratch buffer
1581 convolver[:] = 1 # initialize
1582 for c in conv_list: # accumulate product
1583 if c is not None:
1584 convolver *= c
1585
1586 if convolver[1].real > 0: # recenter peak!
1587 convolver[1::2] *= -1
1588
1589 peak = best_irfft(convolver)
1590
1591 # now, use the trick from Mendenhall JQSRT Voigt paper to remove
1592 # periodic function correction
1593 # JQSRT 105 number 3 July 2007 p. 519 eq. 7
1594 # total lor widths, created by the various colvolvers
1595 correction_width = 2 * sum(self.lor_widths.values())
1596
1597 d2p = 2.0 * pi / self.window_fullwidth
1598 alpha = correction_width / 2.0 # be consistent with convolver
1599 mu = (peak * self.twothetasamples).sum() / peak.sum() # centroid
1600 dx = self.twothetasamples - mu
1601 eps_corr1 = (math.sinh(d2p * alpha) / self.window_fullwidth) / \
1602 (math.cosh(d2p * alpha) - ncos(d2p * dx))
1603 eps_corr2 = (alpha / pi) / (dx * dx + alpha * alpha)
1604 corr = (convolver[0].real / numpy.sum(eps_corr2)) * \
1605 (eps_corr1 - eps_corr2)
1606 peak -= corr
1607
1608 peak *= self.window_fullwidth / \
1609 (self.twotheta_output_points / self.oversampling) # scale to area
1610
1611 if compute_derivative:
1612 # this is useful
1613 convolver *= self.omega_vals
1614 convolver *= complex(0, 1)
1615 deriv = best_irfft(convolver)
1616 deriv *= self.window_fullwidth / \
1617 (self.twotheta_output_points / self.oversampling)
1618 deriv = deriv[::self.oversampling]
1619 else:
1620 deriv = None
1621
1622 result = profile_data(twotheta0_deg=math.degrees(twotheta0),
1623 twotheta=self.twothetasamples[
1624 ::self.oversampling],
1625 omega_inv_deg=self.omega_inv_deg[
1626 :self.twotheta_output_points // 2 + 1],
1627 twotheta_deg=self.twothetasamples_deg[
1628 ::self.oversampling],
1629 peak=peak[::self.oversampling],
1630 derivative=deriv
1631 )
1632
1633 if return_convolver:
1634 result.add_symbol(
1635 convolver=convolver[:self.twotheta_output_points//2+1])
1636
1637 return result
1638
1639 def self_clean(self):
1640 """
1641 do some cleanup to make us more compact;
1642 Instance can no longer be used after doing this, but can be pickled.
1643 """
1644 clean = self._clean_on_pickle
1645 pd = dict()
1646 pd.update(self.__dict__)
1647 for thing in pd.keys():
1648 x = getattr(self, thing)
1649 if id(x) in clean:
1650 delattr(self, thing)
1651 # delete extra attributes cautiously, in case we have already been
1652 # cleaned
1653 for k in ('convolver_funcs', 'convolvers',
1654 'factors', 'convolution_history'):
1655 if pd.pop(k, None) is not None:
1656 delattr(self, k)
1657
1658 def __getstate__(self):
1659 """
1660 return information for pickling. Removes transient data from cache of
1661 shadow copy so resulting object is fairly compact. This does not
1662 affect the state of the actual instance.
1663
1664 Returns
1665 -------
1666 dict
1667 dictionary of sufficient information to reanimate instance.
1668 """
1669 # do some cleanup on state before we get pickled
1670 # (even if main class not cleaned)
1671 clean = self._clean_on_pickle
1672 pd = dict()
1673 pd.update(self.__dict__)
1674 for thing in pd.keys():
1675 x = getattr(self, thing)
1676 if id(x) in clean:
1677 del pd[thing]
1678 # delete extra attributes cautiously, in case we have already been
1679 # cleaned
1680 for k in ('convolver_funcs', 'convolvers',
1681 'factors', 'convolution_history'):
1682 pd.pop(k, None)
1683 return pd
1684
1685 def __setstate__(self, setdict):
1686 """
1687 reconstruct class from pickled information
1688 This rebuilds the class instance so it is ready to use on unpickling.
1689
1690 Parameters
1691 ----------
1692 self : FP_Profile
1693 an empty class instance
1694 setdict : dict
1695 dictionary from FP_profile.__getstate__()
1696 """
1697 self.__init__(anglemode=setdict["anglemode"],
1698 gaussian_smoother_bins_sigma=setdict[
1699 "gaussian_smoother_bins_sigma"],
1700 oversampling=setdict["oversampling"]
1701 )
1702 for k, v in setdict.items():
1703 setattr(self, k, v)
1704 try:
1705 s = self
1706 self.set_window(
1707 twotheta_window_center_deg=s.twotheta_window_center_deg,
1708 twotheta_window_fullwidth_deg=s.twotheta_window_fullwidth_deg,
1709 twotheta_output_points=s.twotheta_output_points
1710 )
1711 # override clearing of this by set_window
1712 self.lor_widths = setdict["lor_widths"]
1713 except AttributeError:
1714 pass
1715
1716
1717 class convolver_handler(object):
1718 """
1719 manage the convolvers of on process
1720 """
1721
1722 def __init__(self):
1723 self.convolvers = []
1724
1725 def add_convolver(self, convolver):
1726 self.convolvers.append(convolver)
1727
1728 def update_parameters(self, parameters):
1729 for idx, c in enumerate(self.convolvers):
1730 for k, v in parameters.items():
1731 if k == 'classoptions':
1732 continue
1733 c.set_parameters(convolver=k, **v)
1734
1735 def set_windows(self, centers, npoints, flag, width):
1736 for c, cen, np, f in zip(self.convolvers, centers, npoints, flag):
1737 if f:
1738 c.set_window(twotheta_output_points=np,
1739 twotheta_window_center_deg=cen,
1740 twotheta_window_fullwidth_deg=width)
1741
1742 def calc(self, run, ttpeaks):
1743 """
1744 calculate profile function for selected convolvers
1745
1746 Parameters
1747 ----------
1748 run : list
1749 list of flags of length of convolvers to tell which convolver needs
1750 to be run
1751 ttpeaks : array-like
1752 peak positions for the convolvers
1753
1754 Returns
1755 -------
1756 list
1757 list of profile_data result objects
1758 """
1759 results = []
1760 for c, flag, tt in zip(self.convolvers, run, ttpeaks):
1761 if flag:
1762 c.set_parameters(twotheta0_deg=tt)
1763 res = c.compute_line_profile()
1764 del res.twotheta, res.omega_inv_deg
1765 del res.dictionary, res.derivative
1766 results.append(res)
1767 else:
1768 results.append(None)
1769 return results
1770
1771
1772 def chunkify(lst, n):
1773 return [lst[i::n] for i in range(n)]
1774
1775
1776 class manager(BaseManager):
1777 pass
1778
1779
1780 class PowderDiffraction(PowderExperiment):
1781
1782 """
1783 Experimental class for powder diffraction. This class calculates the
1784 structure factors of powder diffraction lines and uses instances of
1785 FP_profile to perform the convolution with experimental resolution function
1786 calculated by the fundamental parameters approach. This class used
1787 multiprocessing to speed up calculation. Set config.NTHREADS=1 to restrict
1788 this to one worker process.
1789 """
1790
1791 _valid_init_kwargs = copy.copy(PowderExperiment._valid_init_kwargs)
1792 _valid_init_kwargs['tt_cutoff'] = '2Theta cut off value in degree'
1793 _valid_init_kwargs['fpclass'] = 'FP_profile derived class'
1794 _valid_init_kwargs['fpsettings'] = 'settings dictionaries'
1795 _valid_init_kwargs['enable_simulation'] = 'flag to enable simulation mode'
1796 del _valid_init_kwargs['sampleor']
1797
1798 def __init__(self, mat, **kwargs):
1799 """
1800 the class is initialized with a xrayutilities.materials.Crystal
1801 instance and calculates the powder intensity and peak positions of the
1802 Crystal up to an angle of tt_cutoff. Results are stored in
1803
1804 data .... array with intensities
1805 ang ..... Bragg angles of the peaks (Theta!)
1806 qpos .... reciprocal space position of intensities
1807
1808 If `enable_simulation` is set to True the `Convolve` and `Calculate`
1809 methods can be used to calculated a powder pattern. Upon such
1810 initialization it is strongly recommend to call the `close` method to
1811 clean up the multiprocessing threads when no further calculations will
1812 be performed.
1813
1814 Parameters
1815 ----------
1816 mat : Crystal or Powder
1817 material for the powder calculation
1818 kwargs : dict
1819 optional keyword arguments same as for the Experiment base class
1820 and:
1821 tt_cutoff : float, optional
1822 Powder peaks are calculated up to an scattering angle of tt_cutoff
1823 (deg)
1824 enable_simulation: False, optional
1825 enables the initialization of the convolvers. Call `close()` after
1826 you are finished with your instance!
1827 fpclass : FP_profile
1828 FP_profile derived class with possible convolver mixins.
1829 (default: FP_profile)
1830 fpsettings : dict
1831 settings dictionaries for the convolvers. Default settings are
1832 loaded from the config file.
1833 """
1834 utilities.check_kwargs(kwargs, self._valid_init_kwargs,
1835 self.__class__.__name__)
1836 if isinstance(mat, materials.Crystal):
1837 self.mat = Powder(mat, 1)
1838 elif isinstance(mat, Powder):
1839 self.mat = mat
1840 else:
1841 raise TypeError("mat must be an instance of class "
1842 "xrayutilities.materials.Crystal or "
1843 "xrayutilities.simpack.Powder")
1844
1845 self._tt_cutoff = kwargs.pop('tt_cutoff', 180)
1846 self.fpclass = kwargs.pop('fpclass', FP_profile)
1847 self.settings = self.load_settings_from_config(
1848 kwargs.pop('fpsettings', {}))
1849 self._enable_sim = kwargs.pop('enable_simulation', False)
1850
1851 PowderExperiment.__init__(self, **kwargs)
1852
1853 # number of significant digits, needed to identify equal floats
1854 self.digits = 5
1855
1856 # determine if convolvers are isotropic
1857 self.isotropic = self.fpclass.isotropic
1858
1859 # calculate powder lines position and intensities
1860 self.init_powder_lines(self._tt_cutoff)
1861
1862 # initialize FP_profile instances (add field to the data dictionary)
1863 for h in self.data:
1864 self.data[h]['conv'] = self._init_fpprofile(self.fpclass)
1865 self.update_settings(self.settings)
1866 self.set_sample_parameters()
1867
1868 # set some other class properties
1869 self.__tt = None
1870 self.__ww = None
1871
1872 # initialize multiprocessing
1873 if self._enable_sim:
1874 self._init_multiprocessing()
1875 # set wavelength from class constructor
1876 if 'wl' in kwargs:
1877 self._set_wavelength_pd(kwargs['wl'])
1878 if 'en' in kwargs:
1879 self._set_energy_pd(kwargs['en'])
1880
1881 def _init_multiprocessing(self):
1882 """
1883 initialize multiprocessing for powder pattern calculation
1884 """
1885 # The structure of the multiprocessing code is as follows:
1886 # There are nproc "manager"s which handle the actual convolver code.
1887 # Additionally there are 4 daemon threads which listen for work to be
1888 # distributed to the managers.
1889 np = config.NTHREADS
1890 self.nproc = np if np != 0 else multiprocessing.cpu_count()
1891 self.chunks = chunkify(list(self.data.keys()), self.nproc)
1892 self.next_proc = len(self.data) % self.nproc
1893 manager.register("conv", convolver_handler)
1894 self.managers = [manager() for idx in range(self.nproc)]
1895 self.conv_handlers = []
1896 self.threads = []
1897 self.output_queue = queue.Queue()
1898 for idx, mg in enumerate(self.managers):
1899 mg.start()
1900 m = mg.conv()
1901 for h in self.chunks[idx]:
1902 m.add_convolver(self.data[h]['conv'])
1903 self.conv_handlers.append(m)
1904 self.threads.append((
1905 threading.Thread(target=self._send_work, args=(idx, )),
1906 queue.Queue(), self.output_queue))
1907 self._running = True
1908 for th, q1, q2 in self.threads:
1909 th.daemon = True
1910 th.start()
1911 atexit.register(self.__stop__)
1912
1913 def close(self):
1914 self.__stop__()
1915
1916 def __stop__(self):
1917 self._running = False
1918 try: # try/except needed only for python2 compatibility
1919 # end daemon threads which distribute the work load
1920 for th, q1, q2 in self.threads:
1921 q1.put(None)
1922 th.join()
1923 delattr(self, 'threads')
1924 # end managers which handle the convolvers
1925 delattr(self, 'conv_handlers')
1926 for m in self.managers:
1927 m.shutdown()
1928 delattr(self, 'managers')
1929 except AttributeError:
1930 pass
1931 if sys.version_info >= (3, 0):
1932 atexit.unregister(self.__stop__) # unregister is python3 only
1933
1934 def load_settings_from_config(self, settings):
1935 """
1936 load parameters from the config and update these settings with the
1937 options from the settings parameter
1938 """
1939 params = dict()
1940 for k in config.POWDER:
1941 params[k] = dict()
1942 params[k].update(config.POWDER[k])
1943 if k in settings:
1944 params[k].update(settings[k])
1945 for k in settings:
1946 if k not in config.POWDER:
1947 params[k] = dict()
1948 params[k].update(settings[k])
1949 return params
1950
1951 def _init_fpprofile(self, fpclass):
1952 """
1953 configure the default parameters of the FP_profile class and return an
1954 instance with these settings
1955
1956 Parameters
1957 ----------
1958 fpclass : FP_profile
1959 class with possible mixins which implement more convolvers
1960
1961 Returns
1962 -------
1963 fpclass
1964 instance of fpclass
1965 """
1966 classparams = dict()
1967 classparams.update(self.settings['classoptions'])
1968 classparams.pop('window_width')
1969 p = fpclass(**classparams)
1970 p.debug_cache = False
1971 return p
1972
1973 def _set_wavelength_pd(self, wl):
1974 PowderExperiment._set_wavelength(self, wl)
1975 s = {'emission': {'emiss_wavelengths': self.wavelength*1e-10}}
1976 self.update_settings(s)
1977
1978 def _set_energy_pd(self, energy):
1979 PowderExperiment._set_energy(self, energy)
1980 s = {'emission': {'emiss_wavelengths': self.wavelength*1e-10}}
1981 self.update_settings(s)
1982
1983 energy = property(PowderExperiment._get_energy, _set_energy_pd)
1984 wavelength = property(PowderExperiment._get_wavelength, _set_wavelength_pd)
1985
1986 def set_wavelength_from_params(self):
1987 """
1988 sets the wavelenth in the base class from the settings dictionary of
1989 the FP_profile classes and also set it in the 'global' part of the
1990 parameters
1991 """
1992 if 'emission' in self.settings:
1993 pem = self.settings['emission']
1994 if 'emiss_wavelengths' in pem:
1995 wl = pem['emiss_wavelengths'][0]
1996 self.settings['global']['dominant_wavelength'] = wl
1997 for h, d in self.data.items():
1998 fp = d['conv']
1999 fp.set_parameters(convolver='global',
2000 **self.settings['global'])
2001 # set wavelength in base class
2002 PowderExperiment._set_wavelength(self, wl*1e10)
2003
2004 def set_sample_parameters(self):
2005 """
2006 load sample parameters from the Powder class and use them in all
2007 FP_profile instances of this object
2008 """
2009 samplesettings = {}
2010 for prop, default in zip(('crystallite_size_lor',
2011 'crystallite_size_gauss',
2012 'strain_lor', 'strain_gauss'),
2013 (1e10, 1e10, 0, 0)):
2014 samplesettings[prop] = getattr(self.mat, prop, default)
2015
2016 self.settings['emission'].update(samplesettings)
2017 for h, d in self.data.items():
2018 fp = d['conv']
2019 fp.set_parameters(convolver='emission', **samplesettings)
2020
2021 def update_settings(self, newsettings={}):
2022 """
2023 update settings of all instances of FP_profile
2024
2025 Parameters
2026 ----------
2027 newsettings : dict
2028 dictionary with new settings. It has to include one subdictionary
2029 for every convolver which should have its settings changed.
2030 """
2031 if 'global' in newsettings:
2032 if 'dominant_wavelength' in newsettings['global']:
2033 print('PowderDiffraction: dominant wavelength is a read only'
2034 'setting \n -> use emission: emiss_wavelength instead')
2035 if 'emission' in newsettings:
2036 nem = newsettings['emission']
2037 for k in ('emiss_wavelengths', 'emiss_intensities',
2038 'emiss_gauss_widths', 'emiss_lor_widths'):
2039 if k in nem:
2040 if isinstance(nem[k], numbers.Number):
2041 nem[k] = (nem[k], )
2042 for k in newsettings:
2043 if k == 'classoptions':
2044 continue
2045 for h, d in self.data.items():
2046 fp = d['conv']
2047 fp.set_parameters(convolver=k, **newsettings[k])
2048 if k not in self.settings:
2049 self.settings[k] = dict()
2050 self.settings[k].update(newsettings[k])
2051 self.set_wavelength_from_params()
2052
2053 @property
2054 def twotheta(self):
2055 return self.__tt
2056
2057 @twotheta.setter
2058 def twotheta(self, tt):
2059 oldtt = self.__tt
2060 self.__tt = tt
2061 if oldtt is None:
2062 self.set_window()
2063 elif len(oldtt) != len(self.__tt):
2064 self.set_window(force=True)
2065 elif not numpy.all(numpy.equal(oldtt, self.__tt)):
2066 self.set_window(force=True)
2067
2068 @property
2069 def window_width(self):
2070 return self.__ww
2071
2072 @window_width.setter
2073 def window_width(self, ww):
2074 oldww = self.__ww
2075 if ww == 'config':
2076 self.__ww = config.POWDER['classoptions']['window_width']
2077 else:
2078 self.__ww = ww
2079 if oldww != self.__ww:
2080 self.set_window(force=True)
2081
2082 def set_window(self, force=False):
2083 """
2084 sets the calcultion window for all convolvers
2085 """
2086 ww = self.window_width
2087 tt = self.twotheta
2088 if not ww or tt is None: # not all necessary information is set up
2089 return
2090 npoints = dict()
2091 nset = dict()
2092 for h, d in self.data.items():
2093 ttpeak = 2 * d['ang']
2094 if ttpeak - ww/2 > tt.max() or ttpeak + ww/2 < tt.min():
2095 continue
2096 idx = numpy.argwhere(numpy.logical_and(tt > ttpeak - ww/2,
2097 tt < ttpeak + ww/2))
2098 try:
2099 np = int(math.ceil(len(idx) / (tt[idx[-1]]-tt[idx[0]]) * ww))
2100 except OverflowError:
2101 np = 1
2102 npoints[h] = np
2103 if hasattr(d['conv'], 'twotheta_window_center_deg'):
2104 fptt = d['conv'].twotheta_window_center_deg
2105 if abs(ttpeak-fptt) / ww < 0.25 and not force:
2106 continue
2107 else:
2108 nset[h] = True
2109 else:
2110 nset[h] = True
2111 # set window in local instances
2112 d['conv'].set_window(twotheta_output_points=np,
2113 twotheta_window_center_deg=ttpeak,
2114 twotheta_window_fullwidth_deg=ww)
2115 # set multiprocessing instances
2116 for chunk, handler in zip(self.chunks, self.conv_handlers):
2117 handler.set_windows([2 * self.data[h]['ang'] for h in chunk],
2118 [npoints.get(h, 0) for h in chunk],
2119 [nset.get(h, False) for h in chunk], ww)
2120
2121 def _send_work(self, idx):
2122 """
2123 a threaded block which watches for data and runs computation
2124 """
2125 th, input, output = self.threads[idx]
2126 while self._running:
2127 try:
2128 settings, run, ttpeaks = input.get(True)
2129 except TypeError:
2130 break
2131 handler = self.conv_handlers[idx]
2132 handler.update_parameters(settings)
2133 results = handler.calc(run, ttpeaks)
2134 output.put((idx, results)) # put results on output queue
2135 self._running = False
2136
2137 def structure_factors(self, tt_cutoff):
2138 """
2139 determine structure factors/reflection strength of all Bragg peaks up
2140 to tt_cutoff
2141
2142 Parameters
2143 ----------
2144 tt_cutoff : float
2145 upper cutoff value of 2theta until which the reflection strength
2146 are calculated
2147
2148 Returns
2149 -------
2150 ndarray
2151 numpy array with field for 'hkl' (Miller indices of the peaks), 'q'
2152 (q-position), and 'r' (reflection strength) of the Bragg peaks
2153 """
2154 mat = self.mat.material
2155 # calculate maximal Bragg indices
2156 hma = int(math.ceil(VecNorm(mat.a1) * self.k0 / pi *
2157 sin(math.radians(tt_cutoff / 2.))))
2158 hmi = -hma
2159 kma = int(math.ceil(VecNorm(mat.a2) * self.k0 / pi *
2160 sin(math.radians(tt_cutoff / 2.))))
2161 kmi = -kma
2162 lma = int(math.ceil(VecNorm(mat.a3) * self.k0 / pi *
2163 sin(math.radians(tt_cutoff / 2.))))
2164 lmi = -lma
2165
2166 if config.VERBOSITY >= config.INFO_ALL:
2167 print("XU.Powder.PowderIntensity: tt_cutoff; (hmax, kmax, lmax): "
2168 "%6.2f (%d,%d,%d)" % (tt_cutoff, hma, kma, lma))
2169
2170 # calculate structure factors
2171 qmax = 2 * self.k0 * sin(math.radians(tt_cutoff/2))
2172 hkl = numpy.mgrid[hma:hmi-1:-1,
2173 kma:kmi-1:-1,
2174 lma:lmi-1:-1].reshape(3, -1).T
2175 q = mat.Q(hkl)
2176 qnorm = numpy.linalg.norm(q, axis=1)
2177 m = qnorm <= qmax
2178
2179 data = numpy.zeros(numpy.sum(m), dtype=[('q', numpy.double),
2180 ('r', numpy.double),
2181 ('hkl', numpy.ndarray)])
2182 data['q'] = qnorm[m]
2183 data['r'] = nabs(mat.StructureFactorForQ(q[m], self.energy)) ** 2
2184 data['hkl'] = list(hkl[m])
2185
2186 return data
2187
2188 def merge_lines(self, data):
2189 """
2190 if calculation if isotropic lines at the same q-position can be merged
2191 to one line to reduce the calculational effort
2192
2193 Parameters
2194 ----------
2195 data : ndarray
2196 numpy field array with values of 'hkl' (Miller indices of the
2197 peaks), 'q' (q-position), and 'r' (reflection strength) as produced
2198 by the structure_factors method
2199
2200 Returns
2201 -------
2202 hkl, q, ang, r : array-like
2203 Miller indices, q-position, diffraction angle (Theta), and
2204 reflection strength of the material
2205 """
2206 data = data[numpy.argsort(data['q'], kind='mergesort')]
2207 qpos = []
2208 refstrength = []
2209 hkl = []
2210
2211 def add_lines(q, ref, chkl):
2212 for R, m in zip(ref, chkl):
2213 qpos.append(q)
2214 refstrength.append(R)
2215 hkl.append(m)
2216
2217 currq = -1
2218 curref = []
2219 currhkl = []
2220 for r in data:
2221 if abs(r[0] - currq) > config.EPSILON:
2222 add_lines(currq, curref, currhkl)
2223 currq = r[0]
2224 curref = [r[1], ]
2225 currhkl = [r[2], ]
2226 else:
2227 if self.isotropic:
2228 curref[-1] += r[1]
2229 else:
2230 # merge lines which are equal according to the crystal
2231 # and convolver symmetries
2232 added = False
2233 for i, m in enumerate(currhkl):
2234 if self.mat.material.lattice.isequivalent(
2235 m, r[2], equalq=True):
2236 if self.fpclass.isequivalent(
2237 m, r[2],
2238 self.mat.material.lattice.crystal_system):
2239 curref[i] += r[1]
2240 added = True
2241 if not added:
2242 curref.append(r[1])
2243 currhkl.append(r[2])
2244 # add remaining lines
2245 add_lines(currq, curref, currhkl)
2246
2247 qpos = numpy.array(qpos[1:], dtype=numpy.double)
2248 ang = self.Q2Ang(qpos)
2249 refstrength = numpy.array(refstrength[1:], dtype=numpy.double)
2250 hkl = hkl[1:]
2251 return hkl, qpos, ang, refstrength
2252
2253 def correction_factor(self, ang):
2254 """
2255 calculate the correction factor for the diffracted intensities. This
2256 contains the polarization effects and the Lorentz factor
2257
2258 Parameters
2259 ----------
2260 ang : aray-like
2261 theta diffraction angles for which the correction should be
2262 calculated
2263
2264 Returns
2265 -------
2266 f : array-like
2267 array of the same shape as ang containing the correction factors
2268 """
2269 # correct data for polarization and lorentzfactor and unit cell volume
2270 # see L.S. Zevin : Quantitative X-Ray Diffractometry
2271 # page 18ff
2272 polarization_factor = (1 +
2273 ncos(numpy.radians(2 * ang)) ** 2) / 2
2274 lorentz_factor = 1. / (nsin(numpy.radians(ang)) ** 2 *
2275 ncos(numpy.radians(ang)))
2276 unitcellvol = self.mat.material.lattice.UnitCellVolume()
2277 return polarization_factor * lorentz_factor / unitcellvol ** 2
2278
2279 def init_powder_lines(self, tt_cutoff):
2280 """
2281 calculates the powder intensity and positions up to an angle of
2282 tt_cutoff (deg) and stores the result in the data dictionary whose
2283 structure is as follows:
2284
2285 The data dictionary has one entry per line with a unique identifier
2286 as key of the entry. The entries themself are dictionaries which
2287 have the following entries:
2288
2289 * hkl : (h, k, l), Miller indices of the Bragg peak
2290 * r : reflection strength of the line
2291 * ang : Bragg angle of the peak (theta = 2theta/2!)
2292 * qpos : reciprocal space position
2293 """
2294
2295 tmp_data = self.structure_factors(tt_cutoff)
2296 hkl, qpos, ang, rs = self.merge_lines(tmp_data)
2297 corrfact = self.correction_factor(ang)
2298 rs *= corrfact
2299 ids = [tuple(idx) for idx in hkl]
2300 self.data = dict()
2301 for i, q, a, r in zip(ids, qpos, ang, rs):
2302 active = True if r/rs.max() > config.EPSILON else False
2303 self.data[i] = {'qpos': q, 'ang': a, 'r': r,
2304 'active': active}
2305
2306 def update_powder_lines(self, tt_cutoff):
2307 """
2308 calculates the powder intensity and positions up to an angle of
2309 tt_cutoff (deg) and updates the values in:
2310
2311 * ids: list of unique identifiers of the powder line
2312 * data: array with intensities
2313 * ang: bragg angles of the peaks (theta=2theta/2!)
2314 * qpos: reciprocal space position of intensities
2315 """
2316 tmp_data = self.structure_factors(tt_cutoff)
2317 hkl, qpos, ang, rs = self.merge_lines(tmp_data)
2318 corrfact = self.correction_factor(ang)
2319 rs *= corrfact
2320 ids = [tuple(idx) for idx in hkl]
2321 for h, q, a, r in zip(ids, qpos, ang, rs):
2322 active = True if r/rs.max() > config.EPSILON else False
2323 if h in self.data:
2324 self.data[h]['qpos'] = q
2325 self.data[h]['ang'] = a
2326 self.data[h]['r'] = r
2327 self.data[h]['active'] = active
2328 else:
2329 # new peak needs a new convolver
2330 fp = self._init_fpprofile(self.fpclass)
2331 for k, v in self.settings.items():
2332 if k == 'classoptions':
2333 continue
2334 fp.set_parameters(convolver=k, **v)
2335 self.data[h] = {'qpos': q, 'ang': a, 'r': r,
2336 'conv': fp, 'active': active}
2337 if self._enable_sim:
2338 self.conv_handlers[self.next_proc].add_convolver(fp)
2339 self.chunks[self.next_proc].append(h)
2340 self.next_proc = (self.next_proc + 1) % self.nproc
2341 for h in self.data:
2342 if h not in ids:
2343 # make entry inactive
2344 self.data[h]['active'] = False
2345
2346 def Convolve(self, twotheta, window_width='config', mode='multi'):
2347 """
2348 convolute the powder lines with the resolution function and map them
2349 onto the twotheta positions. This calculates the powder pattern
2350 excluding any background contribution
2351
2352 Parameters
2353 ----------
2354 twotheta : array-like
2355 two theta values at which the powder pattern should be calculated.
2356 window_width : float, optional
2357 width of the calculation window of a single peak
2358 mode : {'multi, 'local'}, optional
2359 multiprocessing mode, either 'multi' to use multiple processes or
2360 'local' to restrict the calculation to a single process
2361
2362 Note:
2363 Bragg peaks are only included up to tt_cutoff set in the class
2364 constructor!
2365
2366 Returns
2367 -------
2368 output intensity values for the twotheta values given in the input
2369 """
2370 if self._enable_sim:
2371 t_start = time.time()
2372
2373 out = numpy.zeros_like(twotheta)
2374 tt = self.twotheta = twotheta
2375 self.window_width = window_width
2376 ww = self.window_width
2377
2378 # check if twotheta range extends above tt_cutoff
2379 if tt.max() > self._tt_cutoff:
2380 warnings.warn('twotheta range is larger then tt_cutoff. '
2381 'Possibly Bragg peaks in the convolution range '
2382 'are not considered!')
2383
2384 if mode == 'local':
2385 for h, d in self.data.items():
2386 if not d['active']:
2387 continue
2388 ttpeak = 2 * d['ang']
2389 # check if peak is in data range to be calculated
2390 if ttpeak - ww/2 > tt.max() or ttpeak + ww/2 < tt.min():
2391 continue
2392 idx = numpy.argwhere(numpy.logical_and(tt > ttpeak - ww/2,
2393 tt < ttpeak + ww/2))
2394 d['conv'].set_parameters(twotheta0_deg=ttpeak)
2395 result = d['conv'].compute_line_profile()
2396 out[idx] += numpy.interp(tt[idx], result.twotheta_deg,
2397 result.peak*d['r'], left=0,
2398 right=0)
2399 else:
2400 # prepare multiprocess calculation
2401 for idx, chunk in enumerate(self.chunks):
2402 run = []
2403 ttpeaks = []
2404 for h in chunk:
2405 ttpeak = 2 * self.data[h]['ang']
2406 ttpeaks.append(ttpeak)
2407 if (ttpeak - ww/2 > tt.max() or
2408 ttpeak + ww/2 < tt.min()):
2409 run.append(False)
2410 else:
2411 run.append(True)
2412 if not self.data[h]['active']:
2413 run[-1] = False
2414 # start calculation in other processes
2415 self.threads[idx][1].put((self.settings, run, ttpeaks))
2416 gotit = set(range(self.nproc))
2417 while gotit:
2418 # receive ready calculations
2419 idx, res = self.output_queue.get(True)
2420 chunk = self.chunks[idx]
2421 for h, r in zip(chunk, res):
2422 if r is None:
2423 continue
2424 else:
2425 ttpeak = 2 * self.data[h]['ang']
2426 mask = numpy.argwhere(
2427 numpy.logical_and(tt > ttpeak - ww/2,
2428 tt < ttpeak + ww/2))
2429
2430 out[mask] += numpy.interp(tt[mask], r.twotheta_deg,
2431 r.peak*self.data[h]['r'],
2432 left=0, right=0)
2433 gotit.discard(idx) # got that result, don't expect more
2434
2435 if config.VERBOSITY >= config.INFO_ALL:
2436 print("XU.Powder.Convolute: exec time=", time.time() - t_start)
2437 return out
2438 else:
2439 print("XU.Powder: not initialized for calculation -> exiting!")
2440 return None
2441
2442 def Calculate(self, twotheta, **kwargs):
2443 """
2444 calculate the powder diffraction pattern including convolution with the
2445 resolution function and map them onto the twotheta positions. This also
2446 performs the calculation of the peak intensities from the internal
2447 material object
2448
2449 Parameters
2450 ----------
2451 twotheta : array-like
2452 two theta values at which the powder pattern should be calculated.
2453 kwargs : dict
2454 additional keyword arguments are passed to the Convolve function
2455
2456 Returns
2457 -------
2458 array-like
2459 output intensity values for the twotheta values given in the input
2460
2461 Notes
2462 -----
2463 Bragg peaks are only included up to tt_cutoff set in the class
2464 constructor!
2465 """
2466 if self._enable_sim:
2467 self.set_sample_parameters()
2468 self.update_powder_lines(self._tt_cutoff)
2469 self.set_window()
2470 return self.Convolve(twotheta, **kwargs)
2471 else:
2472 print("XU.Powder: not initialized for calculation -> exiting!")
2473 return None
2474
2475 def __str__(self):
2476 """
2477 Prints out available information about the material and reflections
2478 """
2479 ostr = "\nPowder diffraction object \n"
2480 ostr += "-------------------------\n"
2481 ostr += self.mat.__repr__() + "\n"
2482 ostr += "Lattice:\n" + self.mat.material.lattice.__str__()
2483 rmax = 0
2484 for d in self.data.values():
2485 if d['r'] > rmax:
2486 rmax = d['r']
2487 ostr += "\nReflections: \n"
2488 ostr += "--------------\n"
2489 ostr += (" h k l | tth | |Q| |"
2490 "Int | Int (%)\n")
2491 ostr += (" ------------------------------------"
2492 "---------------------------\n")
2493 for h, d in sorted(zip(self.data.keys(), self.data.values()),
2494 key=lambda t: t[1]['ang']):
2495 if d['active']:
2496 ostr += ("%15s %8.4f %8.3f %10.2f %10.2f\n"
2497 % (h.__str__(), 2 * d['ang'],
2498 d['qpos'], d['r'], d['r'] / rmax * 100.))
2499 ostr += "Settings: " + str(self.settings)
2500 return ostr
+0
-435
xrayutilities/simpack/powdermodel.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2016-2017 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import numbers
18 from math import sqrt
19
20 import numpy
21 from scipy import interpolate
22
23 from .. import utilities
24 from .powder import PowderDiffraction
25 from .smaterials import PowderList
26
27
28 class PowderModel(object):
29 """
30 Class to help with powder calculations for multiple materials. For basic
31 calculations the Powder class together with the Fundamental parameters
32 approach is used.
33 """
34
35 def __init__(self, *args, **kwargs):
36 """
37 constructor for a powder model. The arguments consist of a PowderList
38 or individual Powder(s). Optional parameters are specified in the
39 keyword arguments.
40
41 Note:
42 After the end-of-use it is advisable to call the `close()` method to
43 cleanup the multiprocessing calculation!
44
45 Parameters
46 ----------
47 args : PowderList or Powders
48 either one PowderList or several Powder objects can be given
49 kwargs : dict
50 optional parameters for the simulation. supported are:
51 fpclass : FP_profile, optional
52 derived class with possible convolver mixins. (default:
53 FP_profile)
54 fpsettings : dict
55 settings dictionaries for the convolvers. Default settings are
56 loaded from the config file.
57 I0 : float, optional
58 scaling factor for the simulation result
59
60 In particular interesting in fpsettings might be:
61 {'displacement': {'specimen_displacement': z-displacement of the sample
62 from the rotation center
63 'zero_error_deg': zero error of the 2theta angle}
64 'absorption': {'sample_thickness': sample thickness (m),
65 'absorption_coefficient': sample's absorption (m^-1)}
66 'axial': {'length_sample': the length of the sample in the axial
67 direction (m)}
68 }
69 """
70 if len(args) == 1 and isinstance(args[0], PowderList):
71 self.materials = args[0]
72 else:
73 self.materials = PowderList('%s List' % self.__class__.__name__,
74 *args)
75 self.I0 = kwargs.pop('I0', 1.0)
76 self.pdiff = []
77 kwargs['enable_simulation'] = True
78 for mat in self.materials:
79 self.pdiff.append(PowderDiffraction(mat, **kwargs))
80
81 # default background
82 self._bckg_type = 'polynomial'
83 self._bckg_pol = [0, ]
84
85 def set_parameters(self, params):
86 """
87 set simulation parameters of all subobjects
88
89 Parameters
90 ----------
91 params : dict
92 settings dictionaries for the convolvers.
93 """
94 # set parameters for each convolver
95 for pd in self.pdiff:
96 pd.update_settings(params)
97
98 def set_lmfit_parameters(self, lmparams):
99 """
100 function to update the settings of this class during an least squares
101 fit
102
103 Parameters
104 ----------
105 lmparams : lmfit.Parameters
106 lmfit Parameters list of sample and instrument parameters
107 """
108 pv = lmparams.valuesdict()
109 settings = dict()
110 h = list(self.pdiff[0].data.keys())[0]
111 fp = self.pdiff[0].data[h]['conv'].convolvers
112 for conv in fp:
113 name = conv[5:]
114 settings[name] = dict()
115
116 self.I0 = pv.pop('primary_beam_intensity', 1)
117 set_splbkg = False
118 spliney = {}
119 for p in pv:
120 if p.startswith('phase_'): # sample phase parameters
121 midx = 0
122 for i, name in enumerate(self.materials.namelist):
123 if p.find(name) > 0:
124 midx = i
125 name = self.materials.namelist[midx]
126 attrname = p[p.find(name) + len(name) + 1:]
127 setattr(self.materials[midx], attrname, pv[p])
128 elif p.startswith('background_coeff'):
129 self._bckg_pol[int(p.split('_')[-1])] = pv[p]
130 elif p.startswith('background_spl_coeff'):
131 set_splbkg = True
132 spliney[int(p.split('_')[-1])] = pv[p]
133 else: # instrument parameters
134 for k in settings:
135 if p.startswith(k):
136 slist = p[len(k) + 1:].split('_')
137 if len(slist) > 2 and slist[-2] == 'item':
138 name = '_'.join(slist[:-2])
139 if slist[-1] == '0':
140 settings[k][name] = []
141 settings[k][name].append(pv[p])
142 else:
143 name = p[len(k) + 1:]
144 settings[k][name] = pv[p]
145 break
146 if set_splbkg:
147 self._bckg_spline = interpolate.InterpolatedUnivariateSpline(
148 self._bckg_spline._data[0],
149 [spliney[k] for k in sorted(spliney)], ext=0)
150 self.set_parameters(settings)
151
152 def create_fitparameters(self):
153 """
154 function to create a fit model with all instrument and sample
155 parameters.
156
157 Returns
158 -------
159 lmfit.Parameters
160 """
161 lmfit = utilities.import_lmfit('XU.PowderModel')
162
163 params = lmfit.Parameters()
164 # sample phase parameters
165 for mat, name in zip(self.materials, self.materials.namelist):
166 for k in mat.__dict__:
167 attr = getattr(mat, k)
168 if isinstance(attr, numbers.Number):
169 params.add('_'.join(('phase', name, k)), value=attr,
170 vary=False)
171
172 # instrument parameters
173 settings = self.pdiff[0].settings
174 for pg in settings:
175 for p in settings[pg]:
176 val = settings[pg][p]
177 if p == 'dominant_wavelength' and pg == 'global':
178 # wavelength must be fit using emission_emiss_wavelength
179 continue
180 if isinstance(val, numbers.Number):
181 params.add('_'.join((pg, p)), value=val, vary=False)
182 elif isinstance(val, (numpy.ndarray, tuple, list)):
183 for j, item in enumerate(val):
184 params.add('_'.join((pg, p, 'item_%d' % j)),
185 value=item, vary=False)
186
187 # other global parameters
188 params.add('primary_beam_intensity', value=self.I0, vary=False)
189 if self._bckg_type == 'polynomial':
190 for i, coeff in enumerate(self._bckg_pol):
191 params.add('background_coeff_%d' % i, value=coeff, vary=False)
192 elif self._bckg_type == 'spline':
193 for i, coeff in enumerate(self._bckg_spline._data[1]):
194 params.add('background_spl_coeff_%d' % i, value=coeff,
195 vary=False)
196 return params
197
198 def set_background(self, btype, **kwargs):
199 """
200 define background as spline or polynomial function
201
202 Parameters
203 ----------
204 btype : {polynomial', 'spline'}
205 background type; Depending on this
206 value the expected keyword arguments differ.
207 kwargs : dict
208 optional keyword arguments
209 x : array-like, optional
210 x-values (twotheta) of the background points (if btype='spline')
211 y : array-like, optional
212 intensity values of the background (if btype='spline')
213 p : array-like, optional
214 polynomial coefficients from the highest degree to the constant
215 term. len of p decides about the degree of the polynomial (if
216 btype='polynomial')
217 """
218 if btype == 'spline':
219 self._bckg_spline = interpolate.InterpolatedUnivariateSpline(
220 kwargs.get('x'), kwargs.get('y'), ext=0)
221 elif btype == 'polynomial':
222 self._bckg_pol = list(kwargs.get('p'))
223 else:
224 raise ValueError("btype must be either 'spline' or 'polynomial'")
225 self._bckg_type = btype
226
227 def simulate(self, twotheta, **kwargs):
228 """
229 calculate the powder diffraction pattern of all materials and sum the
230 results based on the relative volume of the materials.
231
232 Parameters
233 ----------
234 twotheta : array-like
235 positions at which the powder pattern should be evaluated
236 kwargs : dict
237 optional keyword arguments
238 background : array-like
239 an array of background values (same shape as twotheta) if no
240 background is given then the background is calculated as previously
241 set by the set_background function or is 0.
242
243
244 further keyword arguments are passed to the Convolve function of of the
245 PowderDiffraction objects
246
247 Returns
248 -------
249 array-like
250 summed powder diffraction intensity of all materials present in the
251 model
252 """
253 inte = numpy.zeros_like(twotheta)
254 background = kwargs.pop('background', None)
255 if background is None:
256 if self._bckg_type == 'spline':
257 background = self._bckg_spline(twotheta)
258 else:
259 background = numpy.polyval(self._bckg_pol, twotheta)
260 totalvol = sum(pd.mat.volume for pd in self.pdiff)
261 for pd in self.pdiff:
262 inte += pd.Calculate(twotheta, **kwargs) * pd.mat.volume / totalvol
263 return self.I0 * inte + background
264
265 def fit(self, params, twotheta, data, std=None, maxfev=200):
266 """
267 make least squares fit with parameters supplied by the user
268
269 Parameters
270 ----------
271 params : lmfit.Parameters
272 object with all parameters set as intended by the user
273 twotheta : array-like
274 angular values for the fit
275 data : array-like
276 experimental intensities for the fit
277 std : array-like
278 standard deviation of the experimental data. if 'None' the sqrt of
279 the data will be used
280 maxfev: int
281 maximal number of simulations during the least squares refinement
282
283 Returns
284 -------
285 lmfit.MinimizerResult
286 """
287 lmfit = utilities.import_lmfit('XU.PowderModel')
288
289 def residual(pars, tt, data, weight):
290 """
291 residual function for lmfit Minimizer routine
292
293 Parameters
294 ----------
295 pars : lmfit.Parameters
296 fit Parameters
297 tt : array-like
298 array of twotheta angles
299 data : array-like
300 experimental data, same shape as tt
301 eps : array-like
302 experimental error bars, shape as tt
303 """
304 # set parameters in this instance
305 self.set_lmfit_parameters(pars)
306
307 # run simulation
308 model = self.simulate(tt)
309 return (model - data) * weight
310
311 if std is None:
312 weight = numpy.reciprocal(numpy.sqrt(data))
313 else:
314 weight = numpy.reciprocal(std)
315 weight[numpy.isinf(weight)] = 1
316 self.minimizer = lmfit.Minimizer(residual, params,
317 fcn_args=(twotheta, data, weight))
318 fitres = self.minimizer.minimize(maxfev=maxfev)
319 self.set_lmfit_parameters(fitres.params)
320 return fitres
321
322 def close(self):
323 for pd in self.pdiff:
324 pd.close()
325
326 def __str__(self):
327 """
328 string representation of the PowderModel
329 """
330 ostr = "PowderModel {\n"
331 ostr += "I0: %f\n" % self.I0
332 ostr += str(self.materials)
333 ostr += "}"
334 return ostr
335
336
337 def Rietveld_error_metrics(exp, sim, weight=None, std=None,
338 Nvar=0, disp=False):
339 """
340 calculates common error metrics for Rietveld refinement.
341
342 Parameters
343 ----------
344 exp : array-like
345 experimental datapoints
346 sim : array-like
347 simulated data
348 weight : array-like, optional
349 weight factor in the least squares sum. If it is None the weight is
350 estimated from the counting statistics of 'exp'
351 std : array-like, optional
352 standard deviation of the experimental data. alternative way of
353 specifying the weight factor. when both are given weight overwrites
354 std!
355 Nvar : int, optional
356 number of variables in the refinement
357 disp : bool, optional
358 flag to tell if a line with the calculated values should be printed.
359
360 Returns
361 -------
362 M, Rp, Rwp, Rwpexp, chi2: float
363 """
364 if weight is None and std is None:
365 weight = numpy.reciprocal(exp)
366 elif weight is None:
367 weight = numpy.reciprocal(std**2)
368 weight[numpy.isinf(weight)] = 1
369 M = numpy.sum((exp - sim)**2 * weight)
370 Rp = numpy.sum(numpy.abs(exp - sim))/numpy.sum(exp)
371 Rwp = sqrt(M / numpy.sum(weight * exp**2))
372 chi2 = M / (len(exp) - Nvar)
373 Rwpexp = Rwp / sqrt(chi2)
374 if disp:
375 print('Rp=%.4f Rwp=%.4f Rwpexp=%.4f chi2=%.4f'
376 % (Rp, Rwp, Rwpexp, chi2))
377 return M, Rp, Rwp, Rwpexp, chi2
378
379
380 def plot_powder(twotheta, exp, sim, mask=None, scale='sqrt', fig='XU:powder',
381 show_diff=True, show_legend=True, labelexp='experiment',
382 labelsim='simulate', formatexp='k-.', formatsim='r-'):
383 """
384 Convenience function to plot the comparison between experimental and
385 simulated powder diffraction data
386
387 Parameters
388 ----------
389 twotheta : array-like
390 angle values used for the x-axis of the plot (deg)
391 exp : array-like
392 experimental data (same shape as twotheta). If None only the simulation
393 and no difference will be plotted
394 sim : array-like
395 simulated data
396 mask : array-like, optional
397 mask to reduce the twotheta values to the be used as x-coordinates of
398 sim
399 scale : {'linear', 'sqrt', 'log'}, optional
400 string specifying the scale of the y-axis.
401 fig : str or int, optional
402 matplotlib figure name (figure will be cleared!)
403 show_diff : bool, optional
404 flag to specify if a difference curve should be shown
405 show_legend: bool, optional
406 flag to specify if a legend should be shown
407 """
408 plot, plt = utilities.import_matplotlib_pyplot('XU.simpack')
409 if not plot:
410 return
411
412 plt.figure(fig, figsize=(10, 7))
413 plt.clf()
414 ax = plt.subplot(111)
415 lines = []
416 if exp is not None:
417 lines.append(ax.plot(twotheta, exp, formatexp, label=labelexp)[0])
418 if mask is None:
419 mask = numpy.ones_like(twotheta, dtype=numpy.bool)
420 lines.append(ax.plot(twotheta[mask], sim, formatsim, label=labelsim)[0])
421
422 if show_diff:
423 # plot error between simulation and experiment
424 if exp is not None:
425 lines.append(ax.plot(twotheta[mask], exp[mask]-sim, '.-',
426 color='0.5', label='difference')[0])
427
428 plt.xlabel('2Theta (deg)')
429 plt.ylabel('Intensity')
430 leg = plt.figlegend(lines, [l.get_label() for l in lines],
431 loc='upper right', frameon=True)
432 ax.set_yscale(scale)
433 plt.tight_layout()
434 return lines
+0
-476
xrayutilities/simpack/smaterials.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2015-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 import collections
18 import copy
19 import numbers
20
21 import numpy
22
23 from .. import utilities
24 from ..materials import Crystal, PseudomorphicMaterial
25 from ..math import CoordinateTransform, Transform
26
27 # python 2to3 compatibility
28 try:
29 basestring
30 except NameError:
31 basestring = str
32
33
34 def _multiply(a, b):
35 """
36 implement multiplication of SMaterial and MaterialList with integer
37 """
38 if not isinstance(b, int):
39 raise TypeError("unsupported operand type(s) for *: "
40 "'%s' and '%s'" % (type(a), type(b)))
41 if b < 1:
42 raise ValueError("multiplication factor needs to be positive!")
43 m = MaterialList('%d * (%s)' % (b, a.name), a)
44 for i in range(b-1):
45 m.append(copy.deepcopy(a))
46 return m
47
48
49 class SMaterial(object):
50 """
51 Simulation Material. Extends the xrayutilities Materials by properties
52 needed for simulations
53 """
54
55 def __init__(self, material, **kwargs):
56 """
57 initialize a simulation material by specifiying its Material and
58 optional other properties
59
60 Parameters
61 ----------
62 material : Material (Crystal, or Amorphous)
63 Material object containing optical/crystal properties of for the
64 simulation; a deepcopy is used internally.
65 kwargs : dict
66 optional properties of the material needed for the simulation
67 """
68 self.name = utilities.makeNaturalName(material.name, check=True)
69 self.material = copy.deepcopy(material)
70 for kw in kwargs:
71 setattr(self, kw, kwargs[kw])
72
73 @property
74 def material(self):
75 return self._material
76
77 @material.setter
78 def material(self, material):
79 self._material = material
80 if isinstance(material, Crystal):
81 self._structural_params = []
82 # make lattice parameters attributes
83 for param, value in material.lattice.free_parameters.items():
84 self._structural_params.append(param)
85 setattr(self, param, value)
86 # make attributes from atom positions
87 for i, wp in enumerate(material.lattice._wbase):
88 if wp[1][1] is not None:
89 for j, p in enumerate(wp[1][1]):
90 name = '_'.join(('at%d' % i, wp[0].name,
91 wp[1][0], str(j), 'pos'))
92 self._structural_params.append(name)
93 setattr(self, name, p)
94 # make attributes from atom occupations
95 for i, wp in enumerate(material.lattice._wbase):
96 name = '_'.join(('at%d' % i, wp[0].name,
97 wp[1][0], 'occupation'))
98 self._structural_params.append(name)
99 setattr(self, name, wp[2])
100 # make attributes from Debye waller exponents
101 for i, wp in enumerate(material.lattice._wbase):
102 name = '_'.join(('at%d' % i, wp[0].name, wp[1][0], 'biso'))
103 self._structural_params.append(name)
104 setattr(self, name, wp[3])
105
106 def __setattr__(self, name, value):
107 object.__setattr__(self, name, value)
108 if hasattr(self, 'material'):
109 if isinstance(self.material, Crystal):
110 if name in self.material.lattice.free_parameters:
111 setattr(self.material.lattice, name, value)
112 if name.startswith('at'):
113 nsplit = name.split('_')
114 idx = int(nsplit[0][2:])
115 wp = self.material.lattice._wbase[idx]
116 # wyckoff position parameter
117 if nsplit[-1] == 'pos':
118 pidx = int(nsplit[-2])
119 wyckpos = (wp[1][0], list(wp[1][1]))
120 wyckpos[1][pidx] = value
121 self.material.lattice._wbase[idx] = (wp[0], wyckpos,
122 wp[2], wp[3])
123 # site occupation
124 if nsplit[-1] == 'occupation':
125 self.material.lattice._wbase[idx] = (wp[0], wp[1],
126 value, wp[3])
127 # site DW exponent
128 if nsplit[-1] == 'biso':
129 self.material.lattice._wbase[idx] = (wp[0], wp[1],
130 wp[2], value)
131
132 def __radd__(self, other):
133 return MaterialList('%s + %s' % (other.name, self.name), other, self)
134
135 def __add__(self, other):
136 return MaterialList('%s + %s' % (self.name, other.name), self, other)
137
138 def __mul__(self, other):
139 return _multiply(self, other)
140
141 __rmul__ = __mul__
142
143 def __repr__(self):
144 s = '{cls}-{name} ('.format(name=self.material.name,
145 cls=self.__class__.__name__)
146 for k in self.__dict__:
147 if k not in ('material', 'name'):
148 v = getattr(self, k)
149 if isinstance(v, numbers.Number):
150 s += '{key}: {value:.5g}, '.format(key=k, value=v)
151 else:
152 s += '{key}: {value}, '.format(key=k, value=v)
153 return s + ')'
154
155
156 class MaterialList(collections.MutableSequence):
157 """
158 class representing the basics of a list of materials for simulations within
159 xrayutilities. It extends the built in list type.
160 """
161
162 def __init__(self, name, *args):
163 if not isinstance(name, basestring):
164 raise TypeError("'name' argument must be a string")
165 self.name = name
166 self.list = list()
167 self.namelist = list()
168 self.extend(list(args))
169
170 def check(self, v):
171 if not isinstance(v, SMaterial):
172 raise TypeError('%s can only contain SMaterial as entries!'
173 % self.__class__.__name__)
174
175 def _set_unique_name(self, v):
176 if v.name in self.namelist:
177 splitname = v.name.split('_')
178 if len(splitname) > 1:
179 try:
180 num = int(splitname[-1])
181 basename = '_'.join(splitname[:-1])
182 except ValueError:
183 num = 1
184 basename = v.name
185 else:
186 num = 1
187 basename = v.name
188 name = '{name}_{num:d}'.format(name=basename, num=num)
189 while name in self.namelist:
190 num += 1
191 name = '{name}_{num:d}'.format(name=basename, num=num)
192 v.name = name
193 return v.name
194
195 def __len__(self): return len(self.list)
196
197 def __getitem__(self, i): return self.list[i]
198
199 def __delitem__(self, i): del self.list[i]
200
201 def __setitem__(self, i, v):
202 self.check(v)
203 self.namelist[i] = self._set_unique_name(v)
204 self.list[i] = v
205
206 def insert(self, i, v):
207 if isinstance(v, MaterialList):
208 vs = v
209 else:
210 vs = [v, ]
211 for j, val in enumerate(vs):
212 self.check(val)
213 self.namelist.insert(i+j, self._set_unique_name(val))
214 self.list.insert(i+j, val)
215
216 def __radd__(self, other):
217 ml = MaterialList('%s + %s' % (other.name, self.name))
218 ml.append(other)
219 ml.append(self)
220 return ml
221
222 def __add__(self, other):
223 ml = MaterialList('%s + %s' % (self.name, other.name))
224 ml.append(self)
225 ml.append(other)
226 return ml
227
228 def __mul__(self, other):
229 return _multiply(self, other)
230
231 __rmul__ = __mul__
232
233 def __str__(self):
234 layer = ',\n '.join([str(entry) for entry in self.list])
235 s = '{name} [\n {layer}\n]'.format(name=self.name, layer=layer)
236 return s
237
238 def __repr__(self):
239 return self.name
240
241
242 class Layer(SMaterial):
243 """
244 Object describing part of a thin film sample. The properties of a layer
245 are :
246
247 Attributes
248 ----------
249 material : Material (Crystal or Amorhous)
250 an xrayutilties material describing optical and crystal properties of
251 the thin film
252 thickness : float
253 film thickness in Angstrom
254 """
255
256 _valid_init_kwargs = {'roughness': 'root mean square roughness',
257 'density': 'density in kg/m^3',
258 'relaxation': 'degree of relaxation',
259 'lat_correl': 'lateral correlation length'}
260
261 def __init__(self, material, thickness, **kwargs):
262 """
263 constructor for the material saving its properties
264
265 Parameters
266 ----------
267 material : Material (Crystal or Amorhous)
268 an xrayutilties material describing optical and crystal properties
269 of the thin film
270 thickness : float
271 film thickness in Angstrom
272 kwargs : dict
273 optional keyword arguments with further layer properties.
274 roughness : float, optional
275 root mean square roughness of the top interface in Angstrom
276 density : float, optional
277 density of the material in kg/m^3; If not specified the density of
278 the material will be used.
279 relaxation : float, optional
280 the degree of relaxation in case of crystalline thin films
281 lat_correl : float, optional
282 the lateral correlation length for diffuse reflectivity
283 calculations
284 """
285 utilities.check_kwargs(kwargs, self._valid_init_kwargs,
286 self.__class__.__name__)
287 kwargs['thickness'] = thickness
288 super(Layer, self).__init__(material, **kwargs)
289
290 def __getattr__(self, name):
291 """
292 return default values for properties if they were not set
293 """
294 if name == "density":
295 return self.material.density
296 elif name == "roughness":
297 return 0
298 elif name == "lat_correl":
299 return numpy.inf
300 elif name == "relaxation":
301 return 1
302 else:
303 return super(Layer, self).__getattribute__(name)
304
305
306 class LayerStack(MaterialList):
307 """
308 extends the built in list type to enable building a stack of Layer by
309 various methods.
310 """
311
312 def check(self, v):
313 if not isinstance(v, Layer):
314 raise TypeError('LayerStack can only contain Layer as entries!')
315
316
317 class CrystalStack(LayerStack):
318 """
319 extends the built in list type to enable building a stack of crystalline
320 Layers by various methods.
321 """
322
323 def check(self, v):
324 super(CrystalStack, self).check(v)
325 if not isinstance(v.material, Crystal):
326 raise TypeError('CrystalStack can only contain crystalline Layers'
327 ' as entries!')
328
329
330 class GradedLayerStack(CrystalStack):
331 """
332 generates a sequence of layers with a gradient in chemical composition
333 """
334
335 def __init__(self, alloy, xfrom, xto, nsteps, thickness, **kwargs):
336 """
337 constructor for a graded buffer of the material 'alloy' with chemical
338 composition from 'xfrom' to 'xto' with 'nsteps' number of sublayers.
339 The total thickness of the graded buffer is 'thickness'
340
341 Parameters
342 ----------
343 alloy : function
344 Alloy function which allows to create a material with chemical
345 composition 'x' by alloy(x)
346 xfrom, xto : float
347 chemical composition from the bottom to top
348 nsteps : int
349 number of sublayers in the graded buffer
350 thickness : float
351 total thickness of the graded stack
352 """
353 nfrom = alloy(xfrom).name
354 nto = alloy(xto).name
355 super(GradedLayerStack, self).__init__('(' + nfrom + '-' + nto + ')')
356 for x in numpy.linspace(xfrom, xto, nsteps):
357 layer = Layer(alloy(x), thickness/nsteps, **kwargs)
358 self.append(layer)
359
360
361 class PseudomorphicStack001(CrystalStack):
362 """
363 generate a sequence of pseudomorphic crystalline Layers. Surface
364 orientation is assumed to be 001 and materials must be cubic/tetragonal.
365 """
366 trans = Transform(numpy.identity(3))
367
368 def make_epitaxial(self, i):
369 layer = self.list[i]
370 if i == 0:
371 return layer
372 psub = self.list[i-1].material
373 mpseudo = PseudomorphicMaterial(psub, layer.material, layer.relaxation,
374 trans=self.trans)
375 self.list[i].material = mpseudo
376
377 def __delitem__(self, i):
378 del self.list[i]
379 for j in range(i, len(self)):
380 self.make_epitaxial(j)
381
382 def __setitem__(self, i, v):
383 self.check(v)
384 self.namelist[i] = self._set_unique_name(v)
385 self.list[i] = v
386 for j in range(i, len(self)):
387 self.make_epitaxial(j)
388
389 def insert(self, i, v):
390 if isinstance(v, MaterialList):
391 vs = v
392 else:
393 vs = [v, ]
394 for j, val in enumerate(vs):
395 self.check(val)
396 self.namelist.insert(i+j, self._set_unique_name(val))
397 self.list.insert(i+j, copy.copy(val))
398 for k in range(i+j, len(self)):
399 self.make_epitaxial(k)
400
401
402 class PseudomorphicStack111(PseudomorphicStack001):
403 """
404 generate a sequence of pseudomorphic crystalline Layers. Surface
405 orientation is assumed to be 111 and materials must be cubic.
406 """
407 trans = CoordinateTransform((1, -1, 0), (1, 1, -2), (1, 1, 1))
408
409
410 class Powder(SMaterial):
411 """
412 Object describing part of a powder sample. The properties of a powder
413 are:
414
415 Attributes
416 ----------
417 material : Crystal
418 an xrayutilties material (Crystal) describing optical and crystal
419 properties of the powder
420 volume : float
421 powder's volume (in pseudo units, since only the relative volume enters
422 the calculation)
423
424 crystallite_size_lor : float, optional
425 Lorentzian crystallite size fwhm (m)
426 crystallite_size_gauss : float, optional
427 Gaussian crystallite size fwhm (m)
428 strain_lor : float, optional
429 extra peak width proportional to tan(theta)
430 strain_gauss : float, optional
431 extra peak width proportional to tan(theta)
432 """
433
434 _valid_init_kwargs = {'crystallite_size_lor': 'Lorentzian cryst. size',
435 'crystallite_size_gauss': 'Gaussian cryst. size',
436 'strain_lor': 'microstrain broadening',
437 'strain_gauss': 'microstrain broadening'}
438
439 def __init__(self, material, volume, **kwargs):
440 """
441 constructor for the material saving its properties
442
443 Parameters
444 ----------
445 material : Crystal
446 an xrayutilties material (Crystal) describing optical and crystal
447 properties of the powder
448 volume : float
449 powder's volume (in pseudo units, since only the relative volume
450 enters the calculation)
451 kwargs : dict
452 optional keyword arguments with further powder properties.
453 crystallite_size_lor : float, optional
454 Lorentzian crystallite size fwhm (m)
455 crystallite_size_gauss : float, optional
456 Gaussian crystallite size fwhm (m)
457 strain_lor, strain_gauss : float, optional
458 extra peak width proportional to tan(theta);
459 typically interpreted as microstrain broadening
460 """
461 utilities.check_kwargs(kwargs, self._valid_init_kwargs,
462 self.__class__.__name__)
463 kwargs['volume'] = volume
464 super(Powder, self).__init__(material, **kwargs)
465
466
467 class PowderList(MaterialList):
468 """
469 extends the built in list type to enable building a list of Powder
470 by various methods.
471 """
472
473 def check(self, v):
474 if not isinstance(v, Powder):
475 raise TypeError('PowderList can only contain Powder as entries!')
+0
-213
xrayutilities/src/block_average.c less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2010-2011, 2013 Dominik Kriegner <dominik.kriegner@gmail.com>
17 */
18
19 #include "xrayutilities.h"
20
21 PyObject* block_average1d(PyObject *self, PyObject *args) {
22 /* block average for one-dimensional double array
23 *
24 * Parameters
25 * ----------
26 * input: input array of datatype double
27 * Nav: number of items to average
28 *
29 * Returns
30 * -------
31 * block_av: block averaged output array
32 * size = ceil(N/Nav)
33 *
34 */
35
36 int i, j, Nav, N;
37 PyArrayObject *input = NULL, *outarr = NULL;
38 double *cin, *cout;
39 double buf;
40 npy_intp nout;
41
42 /* Python argument conversion code */
43 if (!PyArg_ParseTuple(args, "O!i", &PyArray_Type, &input, &Nav)) {
44 return NULL;
45 }
46
47 PYARRAY_CHECK(input, 1, NPY_DOUBLE, "input must be a 1D double array!");
48 N = (int) PyArray_SIZE(input);
49 cin = (double *) PyArray_DATA(input);
50
51 /* create output ndarray */
52 nout = ((int) ceil(N / (float) Nav));
53 outarr = (PyArrayObject *) PyArray_SimpleNew(1, &nout, NPY_DOUBLE);
54 cout = (double *) PyArray_DATA(outarr);
55
56 /* c-code following is performing the block averaging */
57 for (i = 0; i < N; i = i + Nav) {
58 buf = 0;
59 /* perform one block average (j-i serves as counter
60 -> last bin is therefore correct) */
61 for (j = i; j < i + Nav && j < N; ++j) {
62 buf += cin[j];
63 }
64 /* save average to output array */
65 cout[i / Nav] = buf / (float) (j - i);
66 }
67
68 /* clean up */
69 Py_DECREF(input);
70
71 /* return output array */
72 return PyArray_Return(outarr);
73 }
74
75 PyObject* block_average2d(PyObject *self, PyObject *args) {
76 /* 2D block average for one CCD frame
77 *
78 * Parameters
79 * ----------
80 * ccd: input array/CCD frame
81 * size = (Nch2, Nch1)
82 * Nch1 is the fast varying index
83 * Nav1, 2: number of channels to average in each dimension
84 * in total a block of Nav1 x Nav2 is averaged
85 * nthreads: number of threads to use in parallel section
86 *
87 * Returns
88 * -------
89 * block_av: block averaged output array
90 * size = (ceil(Nch2/Nav2) , ceil(Nch1/Nav1))
91 *
92 */
93
94 int i = 0, j = 0, k = 0, l = 0; /* loop indices */
95 int Nch1, Nch2; /* number of values in input array */
96 int Nav1, Nav2; /* number of items to average */
97 unsigned int nthreads; /* number of threads to use */
98 PyArrayObject *input = NULL, *outarr = NULL;
99 double *cin, *cout;
100 double buf;
101 npy_intp nout[2];
102
103 /* Python argument conversion code */
104 if (!PyArg_ParseTuple(args, "O!iiI", &PyArray_Type, &input, &Nav2,
105 &Nav1, &nthreads)) {
106 return NULL;
107 }
108
109 PYARRAY_CHECK(input, 2, NPY_DOUBLE, "input must be a 2D double array!");
110 Nch2 = (int) PyArray_DIMS(input)[0];
111 Nch1 = (int) PyArray_DIMS(input)[1];
112 cin = (double *) PyArray_DATA(input);
113
114 /* create output ndarray */
115 nout[0] = ((int) ceil(Nch2 / (float) Nav2));
116 nout[1] = ((int) ceil(Nch1 / (float) Nav1));
117 outarr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
118 cout = (double *) PyArray_DATA(outarr);
119
120 #ifdef __OPENMP__
121 /* set openmp thread numbers dynamically */
122 OMPSETNUMTHREADS(nthreads);
123 #endif
124
125 #pragma omp parallel for default(shared) \
126 private(i, j, k, l, buf) schedule(static)
127 for (i = 0; i < Nch2; i = i + Nav2) {
128 for (j = 0; j < Nch1; j = j + Nav1) {
129 buf = 0.;
130 for (k = 0; k < Nav2 && (i + k) < Nch2; ++k) {
131 for (l = 0; l < Nav1 && (j + l) < Nch1; ++l) {
132 buf += cin[(i + k) * Nch1 + (j + l)];
133 }
134 }
135 cout[(i / Nav2) * nout[1] + j / Nav1] = buf / (float)(k * l);
136 }
137 }
138
139 /* clean up */
140 Py_DECREF(input);
141
142 return PyArray_Return(outarr);
143 }
144
145 PyObject* block_average_PSD(PyObject *self, PyObject *args) {
146 /* block average for a bunch of PSD spectra
147 *
148 * Parameters
149 * ----------
150 * psd: input array of PSD values
151 * size = (Nspec, Nch) (in)
152 * Nav: number of channels to average
153 * nthreads: number of threads to use in parallel section
154 *
155 * Returns
156 * -------
157 * block_av: block averaged output array
158 * size = (Nspec , ceil(Nch/Nav)) (out)
159 */
160 int i, j, k; /* loop indices */
161 int Nspec, Nch; /* number of items in input */
162 int Nav; /* number of items to average */
163 unsigned int nthreads; /* number of threads to use */
164 PyArrayObject *input = NULL, *outarr = NULL;
165 double *cin, *cout;
166 double buf;
167 npy_intp nout[2];
168
169 /* Python argument conversion code */
170 if (!PyArg_ParseTuple(args, "O!iI", &PyArray_Type,
171 &input, &Nav, &nthreads)) {
172 return NULL;
173 }
174 PYARRAY_CHECK(input, 2, NPY_DOUBLE, "input must be a 2D double array!");
175 Nspec = (int) PyArray_DIMS(input)[0];
176 Nch = (int) PyArray_DIMS(input)[1];
177 cin = (double *) PyArray_DATA(input);
178
179 /* create output ndarray */
180 nout[0] = Nspec;
181 nout[1] = ((int) ceil(Nch / (float) Nav));
182 outarr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
183 cout = (double *) PyArray_DATA(outarr);
184
185 #ifdef __OPENMP__
186 /* set openmp thread numbers dynamically */
187 OMPSETNUMTHREADS(nthreads);
188 #endif
189
190 /* c-code following is performing the block averaging */
191 #pragma omp parallel for default(shared) private(i, j, k, buf) \
192 schedule(static)
193 for (i = 0; i < Nspec; ++i) {
194 for (j = 0; j < Nch; j = j + Nav) {
195 buf = 0;
196 /* perform one block average
197 (j-i serves as counter -> last bin is therefore correct) */
198 for (k = j; k < j + Nav && k < Nch; ++k) {
199 buf += cin[i * Nch + k];
200 }
201 /* save average to output array */
202 cout[j / Nav + i * nout[1]] = buf / (float) (k - j);
203 }
204 }
205
206 /* clean up */
207 Py_DECREF(input);
208
209 /* return output array */
210 return PyArray_Return(outarr);
211 }
212
+0
-513
xrayutilities/src/cxrayutilities.c less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
17 * Copyright (C) 2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
18 *
19 */
20 #include <Python.h>
21
22 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
23 #define PY_ARRAY_UNIQUE_SYMBOL XU_UNIQUE_SYMBOL
24 #include <numpy/arrayobject.h>
25
26
27 /* functions from block_average.c */
28 extern PyObject* block_average1d(PyObject *self, PyObject *args);
29 extern PyObject* block_average2d(PyObject *self, PyObject *args);
30 extern PyObject* block_average_PSD(PyObject *self, PyObject *args);
31
32 /* functions from gridder1d.c */
33 extern PyObject* pygridder1d(PyObject *self, PyObject *args);
34 extern PyObject* pyfuzzygridder1d(PyObject *self, PyObject *args);
35
36 /* functions from gridder2d.c */
37 extern PyObject* pygridder2d(PyObject *self, PyObject *args);
38 extern PyObject* pyfuzzygridder2d(PyObject *self, PyObject *args);
39
40 /* function from gridder3d.c */
41 extern PyObject* pygridder3d(PyObject *self, PyObject *args);
42 extern PyObject* pyfuzzygridder3d(PyObject *self, PyObject *args);
43
44 /* functions from qconversion.c */
45 extern PyObject* py_ang2q_conversion(PyObject *self, PyObject *args);
46 extern PyObject* py_ang2q_conversion_linear(PyObject *self, PyObject *args);
47 extern PyObject* py_ang2q_conversion_area(PyObject *self, PyObject *args);
48 extern PyObject* ang2q_conversion_area_pixel(PyObject *self, PyObject *args);
49 extern PyObject* ang2q_conversion_area_pixel2(PyObject *self, PyObject *args);
50
51 extern PyObject* ang2q_detpos(PyObject *self, PyObject *args);
52 extern PyObject* ang2q_detpos_linear(PyObject *self, PyObject *args);
53 extern PyObject* ang2q_detpos_area(PyObject *self, PyObject *args);
54
55 /* functions from file_io.c */
56 extern PyObject* cbfread(PyObject *self, PyObject *args);
57
58 static PyMethodDef XRU_Methods[] = {
59 {"block_average1d", (PyCFunction)block_average1d, METH_VARARGS,
60 "block average for one-dimensional numpy double array\n\n"
61 "Parameters \n"
62 "----------\n"
63 " input: input array of datatype double \n"
64 " Nav: number of items to average \n\n"
65 "Returns\n"
66 "-------\n"
67 " block_av: block averaged output array\n"
68 " size = ceil(N / Nav) \n"
69 },
70 {"block_average2d", block_average2d, METH_VARARGS,
71 "2D block average for one CCD frame\n\n"
72 "Parameters\n"
73 "----------\n"
74 " ccd: input array/CCD frame\n"
75 " size = (Nch2, Nch1) \n"
76 " Nch1 is the fast varying index\n"
77 " Nav1, 2: number of channels to average in each dimension\n"
78 " in total a block of Nav1 x Nav2 is averaged\n"
79 "\n"
80 "Returns\n"
81 "-------\n"
82 " block_av: block averaged output array\n"
83 " size = (ceil(Nch2 / Nav2) , ceil(Nch1 / Nav1))\n"
84 },
85 {"block_average_PSD", block_average_PSD, METH_VARARGS,
86 "1D block average for a bunch of PSD spectra in a 2D array\n"
87 "\n"
88 "Parameters\n"
89 "----------\n"
90 " psd: input array of PSD values\n"
91 " size = (Nspec, Nch) (in)\n"
92 " Nav: number of channels to average\n"
93 "\n"
94 "Returns\n"
95 "-------\n"
96 " block_av: block averaged output array\n"
97 " size = (Nspec , ceil(Nch/Nav)) (out)\n"
98 },
99 {"gridder1d", pygridder1d, METH_VARARGS,
100 "Function performs 1D gridding on 1D input data. \n\n"
101 "Parameters\n"
102 "----------\n"
103 " x ...... input x-values (1D numpy array - float64)\n"
104 " data ... input data (1D numpy array - float64)\n"
105 " nx ..... number of grid points in x-direction\n"
106 " xmin ... minimum x-value of the grid\n"
107 " xmax ... maximum x-value of the grid\n"
108 " out .... output data\n"
109 " norm ... normalization array\n"
110 " flags .. flags to specify behavior\n"
111 },
112 {"fuzzygridder1d", pyfuzzygridder1d, METH_VARARGS,
113 "Function performs fuzzy 1D gridding on 1D input data. \n\n"
114 "Parameters\n"
115 "----------\n"
116 " x ...... input x-values (1D numpy array - float64)\n"
117 " data ... input data (1D numpy array - float64)\n"
118 " nx ..... number of grid points in x-direction\n"
119 " xmin ... minimum x-value of the grid\n"
120 " xmax ... maximum x-value of the grid\n"
121 " out .... output data\n"
122 " norm ... normalization array\n"
123 " fuzzywidth .. fuzzy-size of the input data\n"
124 " flags .. flags to specify behavior\n"
125 },
126 {"gridder2d", pygridder2d, METH_VARARGS,
127 "Function performs 2D gridding on 1D input data. \n\n"
128 "Parameters\n"
129 "----------\n"
130 " x ...... input x-values (1D numpy array - float64)\n"
131 " y ...... input y-values (1D numpy array - float64)\n"
132 " data ... input data (1D numpy array - float64)\n"
133 " nx ..... number of grid points in x-direction\n"
134 " ny ..... number of grid points in y-direction\n"
135 " xmin ... minimum x-value of the grid\n"
136 " xmax ... maximum x-value of the grid\n"
137 " ymin ... minimum y-value of the grid\n"
138 " ymax ... minimum y-value of the grid\n"
139 " out .... output data\n"
140 " norm ... normalization array\n"
141 " flags .. flags to specify behavior\n"
142 },
143 {"fuzzygridder2d", pyfuzzygridder2d, METH_VARARGS,
144 "Function performs 2D fuzzy gridding on 1D input data. \n\n"
145 "Parameters\n"
146 "----------\n"
147 " x ...... input x-values (1D numpy array - float64)\n"
148 " y ...... input y-values (1D numpy array - float64)\n"
149 " data ... input data (1D numpy array - float64)\n"
150 " nx ..... number of grid points in x-direction\n"
151 " ny ..... number of grid points in y-direction\n"
152 " xmin ... minimum x-value of the grid\n"
153 " xmax ... maximum x-value of the grid\n"
154 " ymin ... minimum y-value of the grid\n"
155 " ymax ... minimum y-value of the grid\n"
156 " out .... output data\n"
157 " norm ... normalization array\n"
158 " wx ..... fuzzy width of data points in x-direction\n"
159 " wy ..... fuzzy width of data points in y-direction\n"
160 " flags .. flags to specify behavior\n"
161 },
162 {"gridder3d", pygridder3d, METH_VARARGS,
163 "Function performs 2D gridding on 1D input data. \n\n"
164 "Parameters\n"
165 "----------\n"
166 " x ...... input x-values (1D numpy array - float64)\n"
167 " y ...... input y-values (1D numpy array - float64)\n"
168 " z ...... input z-values (1D numpy array - float64)\n"
169 " data ... input data (1D numpy array - float64)\n"
170 " nx ..... number of grid points in x-direction\n"
171 " ny ..... number of grid points in y-direction\n"
172 " nz ..... number of grid points in z-direction\n"
173 " xmin ... minimum x-value of the grid\n"
174 " xmax ... maximum x-value of the grid\n"
175 " ymin ... minimum y-value of the grid\n"
176 " ymax ... maximum y-value of the grid\n"
177 " zmin ... minimum z-value of the grid\n"
178 " zmax ... maximum z-value of the grid\n"
179 " out .... output data\n"
180 " norm ... normalization array\n"
181 " flags .. flags to specify behavior\n"
182 },
183 {"fuzzygridder3d", pyfuzzygridder3d, METH_VARARGS,
184 "Function performs 3D fuzzy gridding on 1D input data. \n\n"
185 "Parameters\n"
186 "----------\n"
187 " x ...... input x-values (1D numpy array - float64)\n"
188 " y ...... input y-values (1D numpy array - float64)\n"
189 " z ...... input z-values (1D numpy array - float64)\n"
190 " data ... input data (1D numpy array - float64)\n"
191 " nx ..... number of grid points in x-direction\n"
192 " ny ..... number of grid points in y-direction\n"
193 " nz ..... number of grid points in z-direction\n"
194 " xmin ... minimum x-value of the grid\n"
195 " xmax ... maximum x-value of the grid\n"
196 " ymin ... minimum y-value of the grid\n"
197 " ymax ... maximum y-value of the grid\n"
198 " zmin ... minimum z-value of the grid\n"
199 " zmax ... maximum z-value of the grid\n"
200 " out .... output data\n"
201 " norm ... normalization array\n"
202 " wx ..... fuzzy width of data points in x-direction\n"
203 " wy ..... fuzzy width of data points in y-direction\n"
204 " wz ..... fuzzy width of data points in z-direction\n"
205 " flags .. flags to specify behavior\n"
206 },
207 {"ang2q_conversion", py_ang2q_conversion, METH_VARARGS,
208 "conversion of Npoints of goniometer positions to reciprocal space\n"
209 "for a setup with point detector\n"
210 "\n"
211 "Parameters\n"
212 "----------\n"
213 " sampleAngles .... angular positions of the sample goniometer\n"
214 " (Npoints, Ns)\n"
215 " detectorAngles .. angular positions of the detector goniometer\n"
216 " (Npoints, Nd)\n"
217 " ri .............. direction of primary beam (length irrelevant)\n"
218 " (angles zero)\n"
219 " sampleAxis ...... string with sample axis directions\n"
220 " detectorAxis .... string with detector axis directions\n"
221 " kappadir ........ rotation axis of a possible kappa circle\n"
222 " UB .............. orientation matrix and reciprocal space conversion\n"
223 " of investigated crystal (3, 3)\n"
224 " sampledis ....... sample displacement vector in relative units of\n"
225 " the detector distance\n"
226 " lambda .......... wavelength of the used x-rays (Angstreom)\n"
227 " nthreads ........ number of threads to use in parallel section of\n"
228 " the code\n"
229 " flags ........... integer flags to select sub-function\n"
230 "\n"
231 "Returns\n"
232 "-------\n"
233 " qpos .......... momentum transfer (Npoints, 3)\n"
234 },
235 {"ang2q_conversion_linear", py_ang2q_conversion_linear, METH_VARARGS,
236 "conversion of Npoints of goniometer positions to reciprocal space\n"
237 "for a linear detector with a given pixel size mounted along one of\n"
238 "the coordinate axis\n"
239 "\n"
240 "Parameters\n"
241 "----------\n"
242 " sampleAngles .... angular positions of the goniometer (Npoints, Ns)\n"
243 " detectorAngles .. angular positions of the detector goniometer\n"
244 " (Npoints, Nd)\n"
245 " rcch ............ direction + distance of center channel (angles\n"
246 " zero)\n"
247 " sampleAxis ...... string with sample axis directions\n"
248 " detectorAxis .... string with detector axis directions\n"
249 " kappadir ........ rotation axis of a possible kappa circle\n"
250 " cch ............. center channel of the detector\n"
251 " dpixel .......... width of one pixel, same unit as distance rcch\n"
252 " roi ............. region of interest of the detector\n"
253 " dir ............. direction of the detector, e.g.: 'x+'\n"
254 " tilt ............ tilt of the detector direction from dir\n"
255 " UB .............. orientation matrix and reciprocal space conversion\n"
256 " of investigated crystal (9)\n"
257 " sampledis ....... sample displacement vector in same unit as the\n"
258 " detector distance\n"
259 " lambda .......... wavelength of the used x-rays in Angstroem\n"
260 " nthreads ........ number of threads to use in parallel section of the\n"
261 " code\n"
262 " flags ........... integer flags to select sub-function\n"
263 "\n"
264 "Returns\n"
265 "-------\n"
266 " qpos ............ momentum transfer (Npoints * Nch, 3)\n"
267 " \n"
268 },
269 {"ang2q_conversion_area", py_ang2q_conversion_area, METH_VARARGS,
270 "conversion of Npoints of goniometer positions to reciprocal space\n"
271 "for an area detector with a given pixel size mounted along one of\n"
272 "the coordinate axis\n"
273 "\n"
274 "Parameters\n"
275 "----------\n"
276 " sampleAngles .... angular positions of the sample goniometer\n"
277 " (Npoints, Ns)\n"
278 " detectorAngles .. angular positions of the detector goniometer\n"
279 " (Npoints, Nd)\n"
280 " rcch ............ direction + distance of center pixel (angles zero)\n"
281 " sampleAxis ...... string with sample axis directions\n"
282 " detectorAxis .... string with detector axis directions\n"
283 " kappadir ...... rotation axis of a possible kappa circle\n"
284 " cch1 ............ center channel of the detector\n"
285 " cch2 ............ center channel of the detector\n"
286 " dpixel1 ......... width of one pixel in first direction, same unit\n"
287 " as distance rcch\n"
288 " dpixel2 ......... width of one pixel in second direction, same unit\n"
289 " as distance rcch\n"
290 " roi ............. region of interest for the area detector\n"
291 " [dir1min, dir1max, dir2min, dir2max]\n"
292 " dir1 ............ first direction of the detector, e.g.: 'x+'\n"
293 " dir2 ............ second direction of the detector, e.g.: 'z+'\n"
294 " tiltazimuth ..... azimuth of the tilt\n"
295 " tilt ............ tilt of the detector plane (rotation around axis\n"
296 " normal to the direction given by the tiltazimuth\n"
297 " UB .............. orientation matrix and reciprocal space conversion\n"
298 " of investigated crystal (3, 3)\n"
299 " sampledis ....... sample displacement vector in same unit as the\n"
300 " detector distance\n"
301 " lambda .......... wavelength of the used x-rays \n"
302 " nthreads ........ number of threads to use in parallel section of\n"
303 " the code\n"
304 " flags ........... integer flags to select sub-function\n"
305 "\n"
306 "Returns\n"
307 "-------\n"
308 " qpos ............ momentum transfer (Npoints * Npix1 * Npix2, 3)\n"
309 "\n"
310 },
311 {"ang2q_conversion_area_pixel", ang2q_conversion_area_pixel, METH_VARARGS,
312 "conversion of Npoints of detector positions to Q\n"
313 "for an area detector with a given pixel size mounted along one of\n"
314 "the coordinate axis. This function only calculates the q-position for\n"
315 "the pairs of pixel numbers (n1, n2) given in the input and should\n"
316 "therefore be used only for detector calibration purposes.\n"
317 "\n"
318 "Parameters\n"
319 "----------\n"
320 " detectorAngles .. angular positions of the detector goniometer\n"
321 " (Npoints, Nd)\n"
322 " n1 .............. detector pixel numbers dim1 (Npoints)\n"
323 " n2 .............. detector pixel numbers dim2 (Npoints)\n"
324 " rcch ............ direction + distance of center pixel (angles zero)\n"
325 " detectorAxis .... string with detector axis directions\n"
326 " cch1 ............ center channel of the detector\n"
327 " cch2 ............ center channel of the detector\n"
328 " dpixel1 ......... width of one pixel in first direction, same unit as\n"
329 " distance rcch\n"
330 " dpixel2 ......... width of one pixel in second direction, same unit\n"
331 " as distance rcch\n"
332 " dir1 ............ first direction of the detector, e.g.: 'x+'\n"
333 " dir2 ............ second direction of the detector, e.g.: 'z+'\n"
334 " tiltazimuth ..... azimuth of the tilt\n"
335 " tilt ............ tilt of the detector plane (rotation around axis\n"
336 " normal to the direction given by the tiltazimuth\n"
337 " lambda .......... wavelength of the used x-rays\n"
338 " nthreads ........ number of threads to use in parallel section of the\n"
339 " code\n"
340 "\n"
341 "Returns\n"
342 "-------\n"
343 " qpos ............ momentum transfer (Npoints, 3)\n"
344 },
345 {"ang2q_conversion_area_pixel2",
346 ang2q_conversion_area_pixel2, METH_VARARGS,
347 "conversion of Npoints of detector positions to Q.\n"
348 "for an area detector with a given pixel size mounted along one of\n"
349 "the coordinate axis. This function only calculates the q-position for\n"
350 "the pairs of pixel numbers (n1, n2) given in the input and should\n"
351 "therefore be used only for detector calibration purposes.\n"
352 "\n"
353 "This variant of this function also takes a sample orientation matrix\n"
354 "as well as the sample goniometer as input to allow for a simultaneous\n"
355 "fit of reference samples orientation\n"
356 "\n"
357 "Parameters\n"
358 "----------\n"
359 " sampleAngles .... angular positions of the sample goniometer\n"
360 " (Npoints, Ns)\n"
361 " detectorAngles .. angular positions of the detector goniometer\n"
362 " (Npoints, Nd)\n"
363 " n1 .............. detector pixel numbers dim1 (Npoints)\n"
364 " n2 .............. detector pixel numbers dim2 (Npoints)\n"
365 " rcch ............ direction + distance of center pixel (angles zero)\n"
366 " sampleAxis ...... string with sample axis directions\n"
367 " detectorAxis .... string with detector axis directions\n"
368 " cch1 ............ center channel of the detector\n"
369 " cch2 ............ center channel of the detector\n"
370 " dpixel1 ......... width of one pixel in first direction, same unit as\n"
371 " distance rcch\n"
372 " dpixel2 ......... width of one pixel in second direction, same unit\n"
373 " as distance rcch \n"
374 " dir1 ............ first direction of the detector, e.g.: 'x+'\n"
375 " dir2 ............ second direction of the detector, e.g.: 'z+'\n"
376 " tiltazimuth ..... azimuth of the tilt\n"
377 " tilt ............ tilt of the detector plane (rotation around axis\n"
378 " normal to the direction given by the tiltazimuth\n"
379 " UB .............. orientation matrix and reciprocal space conversion\n"
380 " of investigated crystal (3, 3)\n"
381 " lambda .......... wavelength of the used x-rays\n"
382 " nthreads ........ number of threads to use in parallel section of the\n"
383 " code\n"
384 "\n"
385 "Returns\n"
386 "-------\n"
387 " qpos ............ momentum transfer (Npoints, 3)\n"
388 },
389 {"ang2q_detpos", ang2q_detpos, METH_VARARGS,
390 "conversion of Npoints of detector arm angles to real space position\n"
391 "for a setup with point detector\n"
392 "\n"
393 "Parameters\n"
394 "----------\n"
395 " detectorAngles .. angular positions of the detector goniometer\n"
396 " (Npoints, Nd)\n"
397 " ri .............. direction and distance of detector at zero angles\n"
398 " detectorAxis .... string with detector axis directions\n"
399 " nthreads ........ number of threads to use in parallel section of\n"
400 " the code\n"
401 "\n"
402 "Returns\n"
403 "-------\n"
404 " dpos .......... real space detector position (Npoints, 3)\n"
405 },
406 {"ang2q_detpos_linear", ang2q_detpos_linear, METH_VARARGS,
407 "conversion of Npoints of detector arm angles to real space\n"
408 "position for a linear detector\n"
409 "\n"
410 "Parameters\n"
411 "----------\n"
412 " detectorAngles .. angular positions of the detector goniometer\n"
413 " (Npoints, Nd)\n"
414 " rcch ............ direction + distance of center channel\n"
415 " (angles zero)\n"
416 " detectorAxis .... string with detector axis directions\n"
417 " cch ............. center channel of the detector\n"
418 " dpixel .......... width of one pixel, same unit as distance rcch\n"
419 " roi ............. region of interest of the detector\n"
420 " dir ............. direction of the detector, e.g.: 'x+'\n"
421 " tilt ............ tilt of the detector direction from dir\n"
422 " nthreads ........ number of threads to use in parallel section of\n"
423 " the code\n"
424 "\n"
425 "Returns\n"
426 "-------\n"
427 " dpos ............ real space detector position (Npoints * Nch, 3)\n"
428 " \n"
429 },
430 {"ang2q_detpos_area", ang2q_detpos_area, METH_VARARGS,
431 "conversion of Npoints of detector arm angles to reciprocal space\n"
432 "position for an area detector with a given pixel size\n"
433 "\n"
434 "Parameters\n"
435 "----------\n"
436 " detectorAngles .. angular positions of the detector goniometer\n"
437 " (Npoints, Nd)\n"
438 " rcch ............ direction + distance of center pixel (angles zero)\n"
439 " detectorAxis .... string with detector axis directions\n"
440 " cch1 ............ center channel of the detector\n"
441 " cch2 ............ center channel of the detector\n"
442 " dpixel1 ......... width of one pixel in first direction, same unit\n"
443 " as distance rcch\n"
444 " dpixel2 ......... width of one pixel in second direction, same unit\n"
445 " as distance rcch\n"
446 " roi ............. region of interest for the area detector\n"
447 " [dir1min, dir1max, dir2min, dir2max]\n"
448 " dir1 ............ first direction of the detector, e.g.: 'x+'\n"
449 " dir2 ............ second direction of the detector, e.g.: 'z+'\n"
450 " tiltazimuth ..... azimuth of the tilt\n"
451 " tilt ............ tilt of the detector plane (rotation around axis\n"
452 " normal to the direction given by the tiltazimuth\n"
453 " nthreads ........ number of threads to use in parallel section of\n"
454 " the code\n"
455 "\n"
456 "Returns\n"
457 "-------\n"
458 " dpos ............ real space detector position\n"
459 " (Npoints * Npix1 * Npix2, 3)\n"
460 "\n"
461 },
462 {"cbfread", cbfread, METH_VARARGS,
463 "parser for cbf data arrays from Pilatus detector images\n\n"
464 " Parameters\n"
465 " ----------\n"
466 " data: data stream (character array)\n"
467 " nx, ny: number of entries of the two dimensional image\n\n"
468 " Returns\n"
469 " -------\n"
470 " the parsed data values as float ndarray\n"
471 },
472 {NULL, NULL, 0, NULL} /* Sentinel */
473 };
474
475 #if PY_MAJOR_VERSION >= 3
476 static struct PyModuleDef moduledef = {
477 PyModuleDef_HEAD_INIT,
478 "cxrayutilities", /* m_name */
479 "Python C extension including performance critical parts\n"
480 "of xrayutilities (gridder, qconversion, block-averageing)\n", /* m_doc */
481 -1, /* m_size */
482 XRU_Methods, /* m_methods */
483 NULL, /* m_reload */
484 NULL, /* m_traverse */
485 NULL, /* m_clear */
486 NULL, /* m_free */
487 };
488 #endif
489
490 PyMODINIT_FUNC
491 #if PY_MAJOR_VERSION >= 3
492 PyInit_cxrayutilities(void)
493 #else
494 initcxrayutilities(void)
495 #endif
496 {
497 PyObject *m;
498
499 #if PY_MAJOR_VERSION >= 3
500 m = PyModule_Create(&moduledef);
501 #else
502 m = Py_InitModule3("cxrayutilities", XRU_Methods,
503 "Python C extension including performance critical parts\n"
504 "of xrayutilities (gridder, qconversion, block-averageing)\n");
505 #endif
506
507 import_array();
508
509 #if PY_MAJOR_VERSION >= 3
510 return m;
511 #endif
512 }
+0
-124
xrayutilities/src/file_io.c less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013 Dominik Kriegner <dominik.kriegner@gmail.com>
17 */
18
19 #include "xrayutilities.h"
20
21 #include <stdio.h>
22
23 PyObject* cbfread(PyObject *self, PyObject *args) {
24 /* parser for cbf data arrays from Pilatus detector images
25 *
26 * Parameters
27 * ----------
28 * data: data stream (character array)
29 * nx, ny: number of entries of the two dimensional image
30 *
31 * Returns
32 * -------
33 * the parsed data values as float ndarray
34 */
35
36 unsigned int i, start = 0, nx, ny, len;
37 unsigned int parsed = 0;
38 PyArrayObject *outarr = NULL;
39 unsigned char *cin;
40 float *cout;
41 npy_intp nout;
42
43 int cur = 0;
44 int diff = 0;
45 unsigned int np = 0;
46
47 union {
48 const unsigned char* uint8;
49 const unsigned short* uint16;
50 const unsigned int* uint32;
51 const char* int8;
52 const short* int16;
53 const int* int32;
54 } parser;
55
56 /* Python argument conversion code */
57 if (!PyArg_ParseTuple(args, "s#ii", &cin, &len, &nx, &ny)) {
58 return NULL;
59 }
60 /* debug output
61 printf("stream length: %d\n", len);
62 printf("entries: %d %d\n", nx, ny); */
63
64 /* create output ndarray */
65 nout = nx * ny;
66 outarr = (PyArrayObject *) PyArray_SimpleNew(1, &nout, NPY_FLOAT);
67 cout = (float *) PyArray_DATA(outarr);
68
69 i = 0;
70 while (i < len - 10) { /* find the start of the array */
71 if ((cin[i] == 0x0c) && (cin[i + 1] == 0x1a) &&
72 (cin[i + 2] == 0x04) && (cin[i + 3] == 0xd5)) {
73 start = i + 4;
74 i = len + 10;
75 }
76 i++;
77 }
78 if (i == len - 10) {
79 PyErr_SetString(PyExc_ValueError,
80 "start of data in stream not found!");
81 return NULL;
82 }
83
84 /* next while loop was taken from pilatus code and adapted by O. Seeck
85 * and D. Kriegner */
86 parser.uint8 = (const unsigned char*) cin + start;
87
88 while (parsed < (len - start)) {
89 if (*parser.uint8 != 0x80) {
90 diff = (int) *parser.int8;
91 parser.int8++;
92 parsed += 1;
93 }
94 else {
95 parser.uint8++;
96 parsed += 1;
97 if (*parser.uint16 != 0x8000) {
98 diff = (int) *parser.int16;
99 parser.int16++;
100 parsed += 2;
101 }
102 else {
103 parser.uint16++;
104 parsed += 2;
105 diff = (int) *parser.int32;
106 parser.int32++;
107 parsed += 4;
108 }
109 }
110 cur += diff;
111 *cout++ = (float) cur;
112 np++;
113 /* check if we already have all data (file might be longer) */
114 if (np == nout) {
115 /* printf("all data read (%d,%d)\n", np, parsed); */
116 break;
117 }
118 }
119
120 /* return output array */
121 return PyArray_Return(outarr);
122 }
123
+0
-258
xrayutilities/src/gridder.h less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2009 Eugen Wintersberger <eugen.wintersberger@desy.de>
17 * Copyright (C) 2009-2010 Dominik Kriegner <dominik.kriegner@gmail.com>
18 */
19
20 /***** xrayutilities/gridder
21 * NAME
22 * gridder - module with gridder functions
23 * PURPOSE
24 *
25 * AUTHOR
26 * Eugen Wintersberger
27 ****/
28
29 #pragma once
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <Python.h>
34
35 /* define flags for the gridder functions */
36 #define NO_DATA_INIT 1
37 #define NO_NORMALIZATION 4
38 #define VERBOSE 16
39
40 /*!
41 \brief python interface function
42
43 Python interface function for fuzzygridder1d. This function is virtually doing
44 all the Python related stuff to run fuzzygridder1d function.
45 \param self reference to the module
46 \param args function arguments
47 \return return value of the function
48 */
49 PyObject* pyfuzzygridder1d(PyObject *self, PyObject *args);
50
51 /*---------------------------------------------------------------------------*/
52 /*!
53 \brief 1D single threaded fuzzy gridder
54
55 \param x input x-values
56 \param data input data
57 \param n number of input points
58 \param nx number of steps in x-direction
59 \param xmin minimm along x-direction
60 \param xmax maximum along x-direction
61 \param odata output data
62 \param norm normalization data
63 \param fuzzywidth width of the data for the fuzzy assignment to bins
64 \param flags control falgs
65 */
66 int fuzzygridder1d(double *x, double *data, unsigned int n,
67 unsigned int nx, double xmin, double xmax,
68 double *odata, double *norm, double fuzzywidth, int flags);
69
70 /*!
71 \brief python interface function
72
73 Python interface function for gridder1d. This function is virtually doing all
74 the Python related stuff to run gridder1d function.
75 \param self reference to the module
76 \param args function arguments
77 \return return value of the function
78 */
79 PyObject* pygridder1d(PyObject *self, PyObject *args);
80
81 /*---------------------------------------------------------------------------*/
82 /*!
83 \brief 1D single threaded gridder
84
85 \param x input x-values
86 \param data input data
87 \param n number of input points
88 \param nx number of steps in x-direction
89 \param xmin minimm along x-direction
90 \param xmax maximum along x-direction
91 \param odata output data
92 \param norm normalization data
93 \param flags control falgs
94 */
95 int gridder1d(double *x, double *data, unsigned int n,
96 unsigned int nx, double xmin, double xmax,
97 double *odata, double *norm, int flags);
98
99 /*!
100 \brief python interface function
101
102 Python interface function for gridder2d. This function is virtually doing all
103 the Python related stuff to run gridder2d function.
104 \param self reference to the module
105 \param args function arguments
106 \return return value of the function
107 */
108 PyObject* pygridder2d(PyObject *self, PyObject *args);
109
110 /*---------------------------------------------------------------------------*/
111 /*!
112 \brief 2D single threaded gridder
113
114 \param x input x-values
115 \param y input y-values
116 \param data input data
117 \param n number of input points
118 \param nx number of steps in x-direction
119 \param ny number of steps in y-direction
120 \param xmin minimm along x-direction
121 \param xmax maximum along x-direction
122 \param ymin minimum along y-direction
123 \param ymax maximum along y-direction
124 \param odata output data
125 \param norm normalization data
126 \param flags control falgs
127 */
128 int gridder2d(double *x, double *y, double *data, unsigned int n,
129 unsigned int nx, unsigned int ny,
130 double xmin, double xmax,
131 double ymin, double ymax,
132 double *odata, double *norm, int flags);
133
134 /*!
135 \brief python interface function
136
137 Python interface function for fuzzygridder2d. This function is virtually doing
138 all the Python related stuff to run the fuzzygridder2d function.
139 \param self reference to the module
140 \param args function arguments
141 \return return value of the function
142 */
143 PyObject* pyfuzzygridder2d(PyObject *self, PyObject *args);
144
145 /*---------------------------------------------------------------------------*/
146 /*!
147 \brief 2D single threaded fuzzy gridder
148
149 \param x input x-values
150 \param y input y-values
151 \param data input data
152 \param n number of input points
153 \param nx number of steps in x-direction
154 \param ny number of steps in y-direction
155 \param xmin minimm along x-direction
156 \param xmax maximum along x-direction
157 \param ymin minimum along y-direction
158 \param ymax maximum along y-direction
159 \param odata output data
160 \param norm normalization data
161 \param wx fuzzy size of data along x-direction
162 \param wy fuzzy size of data along y-direction
163 \param flags control falgs
164 */
165 int fuzzygridder2d(double *x, double *y, double *data, unsigned int n,
166 unsigned int nx, unsigned int ny,
167 double xmin, double xmax,
168 double ymin, double ymax,
169 double *odata, double *norm,
170 double wx, double wy, int flags);
171
172 /*---------------------------------------------------------------------------*/
173 /*!
174 \brief 3D gridder python interface function
175
176 Python interface function for gridder3d. This function is virtually doing all
177 the Python related stuff to run gridder3d function.
178 \param self reference to the module
179 \param args function arguments
180 \return return value of the function
181 */
182 PyObject* pyfuzzygridder3d(PyObject *self, PyObject *args);
183
184 /*---------------------------------------------------------------------------*/
185 /*!
186 \brief single threaded 3d gridder
187
188 Gridder code rebinning scatterd data onto a regular grid in 3 dimensions.
189
190 \param x pointer to x-coordinates of input data
191 \param y pointer to y-coordinates of input data
192 \param z pointer to z-coordinates of input data
193 \param data pointer to input data
194 \param n number of input points
195 \param nx number of grid points along the x-direction
196 \param ny number of grid points along the y-direction
197 \param nz number of grid points along the z-direction
198 \param xmin minimum value of x-axis on the grid
199 \param xmax maximum value of x-axis on the grid
200 \param ymin minimum value of y-axis on the grid
201 \param ymax maximum value of y-axis on the grid
202 \param zmin minimum value of z-axis on the grid
203 \param zmax maximum value of z-axis on the grid
204 \param odata pointer to grid data (output data)
205 \param norm pointer to optional normalization from previous run
206 \param wx fuzzy width parameter in x-direction
207 \param wy fuzzy width parameter in y-direction
208 \param wz fuzzy width parameter in z-direction
209 \param flags gridder flags
210 */
211 int fuzzygridder3d(double *x, double *y, double *z, double *data,
212 unsigned int n, unsigned int nx, unsigned int ny,
213 unsigned int nz, double xmin, double xmax, double ymin,
214 double ymax, double zmin, double zmax, double *odata,
215 double *norm, double wx, double wy, double wz, int flags);
216
217 /*---------------------------------------------------------------------------*/
218 /*!
219 \brief 3D gridder python interface function
220
221 Python interface function for gridder3d. This function is virtually doing all
222 the Python related stuff to run gridder3d function.
223 \param self reference to the module
224 \param args function arguments
225 \return return value of the function
226 */
227 PyObject* pygridder3d(PyObject *self, PyObject *args);
228
229 /*---------------------------------------------------------------------------*/
230 /*!
231 \brief single threaded 3d gridder
232
233 Gridder code rebinning scatterd data onto a regular grid in 3 dimensions.
234
235 \param x pointer to x-coordinates of input data
236 \param y pointer to y-coordinates of input data
237 \param z pointer to z-coordinates of input data
238 \param data pointer to input data
239 \param n number of input points
240 \param nx number of grid points along the x-direction
241 \param ny number of grid points along the y-direction
242 \param nz number of grid points along the z-direction
243 \param xmin minimum value of x-axis on the grid
244 \param xmax maximum value of x-axis on the grid
245 \param ymin minimum value of y-axis on the grid
246 \param ymax maximum value of y-axis on the grid
247 \param zmin minimum value of z-axis on the grid
248 \param zmax maximum value of z-axis on the grid
249 \param odata pointer to grid data (output data)
250 \param norm pointer to optional normalization from previous run
251 \param flags gridder flags
252 */
253 int gridder3d(double *x, double *y, double *z, double *data, unsigned int n,
254 unsigned int nx, unsigned int ny, unsigned int nz,
255 double xmin, double xmax, double ymin, double ymax,
256 double zmin, double zmax,
257 double *odata, double *norm, int flags);
+0
-331
xrayutilities/src/gridder1d.c less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2014 Dominik Kriegner <dominik.kriegner@gmail.com>
17 *
18 ******************************************************************************
19 *
20 * created: Sep 16, 2014
21 * author: Dominik Kriegner
22 */
23
24 #include "gridder.h"
25 #include "gridder_utils.h"
26
27 PyObject* pyfuzzygridder1d(PyObject *self, PyObject *args)
28 {
29 PyArrayObject *py_x = NULL, *py_data = NULL,
30 *py_output = NULL, *py_norm = NULL;
31
32 double *x = NULL, *data = NULL, *odata = NULL, *norm = NULL;
33 double xmin, xmax, fuzzywidth;
34 unsigned int nx;
35 int flags;
36 int n, result;
37
38 if (!PyArg_ParseTuple(args, "O!O!IddO!|O!di",
39 &PyArray_Type, &py_x,
40 &PyArray_Type, &py_data,
41 &nx, &xmin, &xmax,
42 &PyArray_Type, &py_output,
43 &PyArray_Type, &py_norm,
44 &fuzzywidth, &flags))
45 return NULL;
46
47 /* have to check input variables */
48 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
49 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
50 "input data must be a 1D double array!");
51 PYARRAY_CHECK(py_output, 1, NPY_DOUBLE,
52 "ouput data must be a 1D double array!");
53 if (py_norm != NULL)
54 PYARRAY_CHECK(py_norm, 1, NPY_DOUBLE,
55 "norm data must be a 1D double array!");
56
57 /* get data */
58 x = (double *) PyArray_DATA(py_x);
59 data = (double *) PyArray_DATA(py_data);
60 odata = (double *) PyArray_DATA(py_output);
61 if (py_norm != NULL) {
62 norm = (double *) PyArray_DATA(py_norm);
63 }
64
65 /* get the total number of points */
66 n = (int) PyArray_SIZE(py_x);
67
68 /* call the actual gridder routine */
69 result = fuzzygridder1d(x, data, n, nx, xmin, xmax, odata,
70 norm, fuzzywidth, flags);
71
72 /* clean up */
73 Py_DECREF(py_x);
74 Py_DECREF(py_data);
75 Py_DECREF(py_output);
76 if (py_norm != NULL) {
77 Py_DECREF(py_norm);
78 }
79
80 return Py_BuildValue("i", &result);
81 }
82
83 /*---------------------------------------------------------------------------*/
84 int fuzzygridder1d(double *x, double *data, unsigned int n,
85 unsigned int nx,
86 double xmin, double xmax,
87 double *odata, double *norm, double fuzzywidth, int flags)
88 {
89 double *gnorm;
90 unsigned int offset1, offset2;
91 unsigned int noutofbounds = 0; /* counter for out of bounds points */
92
93 double dx = delta(xmin, xmax, nx);
94 double fraction, dwidth; /* fuzzy fraction and data width */
95
96 unsigned int i, j; /* loop indices */
97
98 /* initialize data if requested */
99 if (!(flags & NO_DATA_INIT)) set_array(odata, nx, 0.);
100
101 /* check if normalization array is passed */
102 if (norm == NULL) {
103 gnorm = malloc(sizeof(double) * nx);
104 if (gnorm == NULL) {
105 fprintf(stderr, "XU.FuzzyGridder1D(c): Cannot allocate memory for "
106 "normalization buffer!\n");
107 return -1;
108 }
109 /* initialize memory for norm */
110 set_array(gnorm, nx, 0.);
111 }
112 else {
113 if (flags & VERBOSE) {
114 fprintf(stdout, "XU.FuzzyGridder1D(c): use user provided buffer "
115 "for normalization data\n");
116 }
117 gnorm = norm;
118 }
119
120 /* the master loop over all data points */
121 dwidth = fuzzywidth / dx;
122 if (flags & VERBOSE) {
123 fprintf(stdout, "XU.FuzzyGridder1D(c): fuzzyness: %f %f\n",
124 fuzzywidth, dwidth);
125 }
126 for (i = 0; i < n; i++) {
127 /* if data point is nan ignore it */
128 if (!isnan(data[i])) {
129 /* if the x value is outside the grid boundaries continue with
130 * the next point */
131 if ((x[i] < (xmin - fuzzywidth/2.)) || (x[i] > xmax + fuzzywidth/2.)) {
132 noutofbounds++;
133 continue;
134 }
135 /* compute the linear offset and distribute the data to the bins */
136 if ((x[i] - fuzzywidth / 2.) <= xmin) {
137 offset1 = 0;
138 }
139 else {
140 offset1 = gindex(x[i] - fuzzywidth / 2., xmin, dx);
141 }
142 offset2 = gindex(x[i] + fuzzywidth / 2., xmin, dx);
143 offset2 = offset2 < nx ? offset2 : nx - 1;
144 for(j = offset1; j <= offset2; j++) {
145 if (offset1 == offset2) {
146 fraction = 1.;
147 }
148 else if (j == offset1) {
149 fraction = (j + 1 - (x[i] - fuzzywidth / 2. - xmin + dx / 2.) / dx) / dwidth;
150 }
151 else if (j == offset2) {
152 fraction = ((x[i] + fuzzywidth / 2. - xmin + dx / 2.) / dx - j) / dwidth;
153 }
154 else {
155 fraction = 1 / dwidth;
156 }
157 odata[j] += data[i]*fraction;
158 gnorm[j] += fraction;
159 }
160 }
161 }
162
163 /* perform normalization */
164 if (!(flags & NO_NORMALIZATION)) {
165 if (flags & VERBOSE) {
166 fprintf(stdout, "XU.FuzzyGridder1D(c): perform normalization\n");
167 }
168
169 for (i = 0; i < nx; i++) {
170 if (gnorm[i] > 1.e-16) {
171 odata[i] = odata[i] / gnorm[i];
172 }
173 }
174 }
175
176 /* free the norm buffer if it has been locally allocated */
177 if (norm == NULL) free(gnorm);
178
179 /* warn the user in case more than half the data points where out
180 * of the gridding area */
181 if (noutofbounds > n / 2) {
182 fprintf(stdout, "XU.FuzzyGridder1D(c): more than half of the "
183 "datapoints out of the data range, consider regridding"
184 " with extended range!\n");
185 }
186 else if (flags & VERBOSE) {
187 fprintf(stdout, "XU.FuzzyGridder1D(c): %d datapoints out of the data "
188 "range!\n", noutofbounds);
189 }
190
191 return 0;
192 }
193
194 /*---------------------------------------------------------------------------*/
195 PyObject* pygridder1d(PyObject *self, PyObject *args)
196 {
197 PyArrayObject *py_x = NULL, *py_data = NULL,
198 *py_output = NULL, *py_norm = NULL;
199
200 double *x = NULL, *data = NULL, *odata = NULL, *norm = NULL;
201 double xmin, xmax;
202 unsigned int nx;
203 int flags;
204 int n, result;
205
206 if (!PyArg_ParseTuple(args, "O!O!IddO!|O!i",
207 &PyArray_Type, &py_x,
208 &PyArray_Type, &py_data,
209 &nx, &xmin, &xmax,
210 &PyArray_Type, &py_output,
211 &PyArray_Type, &py_norm,
212 &flags))
213 return NULL;
214
215 /* have to check input variables */
216 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
217 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
218 "input data must be a 1D double array!");
219 PYARRAY_CHECK(py_output, 1, NPY_DOUBLE,
220 "ouput data must be a 1D double array!");
221 if (py_norm != NULL)
222 PYARRAY_CHECK(py_norm, 1, NPY_DOUBLE,
223 "norm data must be a 1D double array!");
224
225 /* get data */
226 x = (double *) PyArray_DATA(py_x);
227 data = (double *) PyArray_DATA(py_data);
228 odata = (double *) PyArray_DATA(py_output);
229 if (py_norm != NULL) {
230 norm = (double *) PyArray_DATA(py_norm);
231 }
232
233 /* get the total number of points */
234 n = (int) PyArray_SIZE(py_x);
235
236 /* call the actual gridder routine */
237 result = gridder1d(x, data, n, nx, xmin, xmax, odata, norm, flags);
238
239 /* clean up */
240 Py_DECREF(py_x);
241 Py_DECREF(py_data);
242 Py_DECREF(py_output);
243 if (py_norm != NULL) {
244 Py_DECREF(py_norm);
245 }
246
247 return Py_BuildValue("i", &result);
248 }
249
250 /*---------------------------------------------------------------------------*/
251 int gridder1d(double *x, double *data, unsigned int n,
252 unsigned int nx,
253 double xmin, double xmax,
254 double *odata, double *norm, int flags)
255 {
256 double *gnorm;
257 unsigned int offset;
258 unsigned int noutofbounds = 0; /* counter for out of bounds points */
259
260 double dx = delta(xmin, xmax, nx);
261
262 unsigned int i; /* loop index */
263
264 /* initialize data if requested */
265 if (!(flags & NO_DATA_INIT)) set_array(odata, nx, 0.);
266
267 /* check if normalization array is passed */
268 if (norm == NULL) {
269 gnorm = malloc(sizeof(double) * nx);
270 if (gnorm == NULL) {
271 fprintf(stderr, "XU.Gridder1D(c): Cannot allocate memory for "
272 "normalization buffer!\n");
273 return -1;
274 }
275 /* initialize memory for norm */
276 set_array(gnorm, nx, 0.);
277 }
278 else {
279 if (flags & VERBOSE) {
280 fprintf(stdout, "XU.Gridder1D(c): use user provided buffer for "
281 "normalization data\n");
282 }
283 gnorm = norm;
284 }
285
286 /* the master loop over all data points */
287 for (i = 0; i < n; i++) {
288 /* if data point is nan ignore it */
289 if (!isnan(data[i])) {
290 /* if the x value is outside the grid boundaries continue with
291 * the next point */
292 if ((x[i] < xmin) || (x[i] > xmax)) {
293 noutofbounds++;
294 continue;
295 }
296 /* compute the linear offset and set the data */
297 offset = gindex(x[i], xmin, dx);
298
299 odata[offset] += data[i];
300 gnorm[offset] += 1.;
301 }
302 }
303
304 /* perform normalization */
305 if (!(flags & NO_NORMALIZATION)) {
306 if (flags & VERBOSE) {
307 fprintf(stdout, "XU.Gridder1D(c): perform normalization ...\n");
308 }
309
310 for (i = 0; i < nx; i++) {
311 if (gnorm[i] > 1.e-16) {
312 odata[i] = odata[i] / gnorm[i];
313 }
314 }
315 }
316
317 /* free the norm buffer if it has been locally allocated */
318 if (norm == NULL) free(gnorm);
319
320 /* warn the user in case more than half the data points where out
321 * of the gridding area */
322 if (noutofbounds > n / 2) {
323 fprintf(stdout, "XU.Gridder1D(c): more than half of the datapoints "
324 "out of the data range, consider regridding with "
325 "extended range!\n");
326 }
327
328 return 0;
329 }
330
+0
-390
xrayutilities/src/gridder2d.c less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
17 * Copyright (C) 2013, 2015 Dominik Kriegner <dominik.kriegner@gmail.com>
18 *
19 ******************************************************************************
20 *
21 * created: Jun 8, 2013
22 * author: Eugen Wintersberger
23 */
24
25 #include "gridder.h"
26 #include "gridder_utils.h"
27
28
29 PyObject* pyfuzzygridder2d(PyObject *self, PyObject *args)
30 {
31 PyArrayObject *py_x = NULL, *py_y = NULL, *py_data = NULL,
32 *py_output = NULL, *py_norm = NULL;
33
34 double *x = NULL, *y = NULL, *data = NULL, *odata = NULL, *norm = NULL;
35 double xmin, xmax, ymin, ymax, wx, wy;
36 unsigned int nx, ny;
37 int flags;
38 int n, result;
39
40 if (!PyArg_ParseTuple(args, "O!O!O!IIddddO!|O!ddi",
41 &PyArray_Type, &py_x,
42 &PyArray_Type, &py_y,
43 &PyArray_Type, &py_data,
44 &nx, &ny, &xmin, &xmax, &ymin, &ymax,
45 &PyArray_Type, &py_output,
46 &PyArray_Type, &py_norm,
47 &wx, &wy, &flags)) {
48 return NULL;
49 }
50
51 /* have to check input variables */
52 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
53 PYARRAY_CHECK(py_y, 1, NPY_DOUBLE, "y-axis must be a 1D double array!");
54 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
55 "input data must be a 1D double array!");
56 PYARRAY_CHECK(py_output, 2, NPY_DOUBLE,
57 "ouput data must be a 2D double array!");
58 if (py_norm != NULL) {
59 PYARRAY_CHECK(py_norm, 2, NPY_DOUBLE,
60 "norm data must be a 2D double array!");
61 }
62
63 /* get data */
64 x = (double *) PyArray_DATA(py_x);
65 y = (double *) PyArray_DATA(py_y);
66 data = (double *) PyArray_DATA(py_data);
67 odata = (double *) PyArray_DATA(py_output);
68 if (py_norm != NULL) {
69 norm = (double *) PyArray_DATA(py_norm);
70 }
71
72 /* get the total number of points */
73 n = (int) PyArray_SIZE(py_x);
74
75 /* call the actual gridder routine */
76 result = fuzzygridder2d(x, y, data, n, nx, ny, xmin, xmax, ymin, ymax, odata,
77 norm, wx, wy, flags);
78
79 /* clean up */
80 Py_DECREF(py_x);
81 Py_DECREF(py_y);
82 Py_DECREF(py_data);
83 Py_DECREF(py_output);
84 if (py_norm != NULL) {
85 Py_DECREF(py_norm);
86 }
87
88 return Py_BuildValue("i", &result);
89 }
90
91 /*--------------------------------------------------------------------------*/
92 int fuzzygridder2d(double *x, double *y, double *data, unsigned int n,
93 unsigned int nx, unsigned int ny,
94 double xmin, double xmax, double ymin, double ymax,
95 double *odata, double *norm, double wx, double wy,
96 int flags)
97 {
98 double *gnorm;
99 unsigned int offset, offsetx1, offsetx2, offsety1, offsety2;
100 unsigned int ntot = nx * ny; /* total number of points on the grid */
101 unsigned int noutofbounds = 0; /* number of points out of bounds */
102
103 double fractionx, fractiony, dwx, dwy; /* variables for the fuzzy part */
104 double dx = delta(xmin, xmax, nx);
105 double dy = delta(ymin, ymax, ny);
106
107 unsigned int i, j, k; /* loop indices */
108
109 /* initialize data if requested */
110 if (!(flags & NO_DATA_INIT)) {
111 set_array(odata, ntot, 0.);
112 }
113
114 /* check if normalization array is passed */
115 if (norm == NULL) {
116 gnorm = malloc(sizeof(double) * (nx * ny));
117 if (gnorm == NULL) {
118 fprintf(stderr, "XU.FuzzyGridder2D(c): Cannot allocate memory for"
119 " normalization buffer!\n");
120 return -1;
121 }
122 /* initialize memory for norm */
123 set_array(gnorm, nx * ny, 0.);
124 }
125 else {
126 if (flags & VERBOSE) {
127 fprintf(stdout, "XU.FuzzyGridder2D(c): use user provided buffer "
128 "for normalization data\n");
129 }
130 gnorm = norm;
131 }
132
133 /* calculate the fuzzy spread in number of bin sizes */
134 dwx = wx / dx;
135 dwy = wy / dy;
136 if (flags & VERBOSE) {
137 fprintf(stdout, "XU.FuzzyGridder2D(c): fuzzyness: %f %f %f %f\n",
138 wx, wy, dwx, dwy);
139 }
140 /* the master loop over all data points */
141 for (i = 0; i < n; i++) {
142 /* if data point is nan ignore it */
143 if (!isnan(data[i])) {
144 /* if the x and y values are outside the grids boundaries
145 * continue with the next point */
146 if ((x[i] < xmin) || (x[i] > xmax)) {
147 noutofbounds++;
148 continue;
149 }
150 if ((y[i] < ymin) || (y[i] > ymax)) {
151 noutofbounds++;
152 continue;
153 }
154 /* compute the linear offset and distribute the data to the bins */
155 if ((x[i] - wx / 2.) <= xmin) {
156 offsetx1 = 0;
157 }
158 else {
159 offsetx1 = gindex(x[i] - wx / 2., xmin, dx);
160 }
161 offsetx2 = gindex(x[i] + wx / 2., xmin, dx);
162 offsetx2 = offsetx2 < nx ? offsetx2 : nx - 1;
163 if ((y[i] - wy / 2.) <= ymin) {
164 offsety1 = 0;
165 }
166 else {
167 offsety1 = gindex(y[i] - wy / 2., ymin, dy);
168 }
169 offsety2 = gindex(y[i] + wy / 2., ymin, dy);
170 offsety2 = offsety2 < ny ? offsety2 : ny - 1;
171
172 for(j = offsetx1; j <= offsetx2; j++) {
173 if (offsetx1 == offsetx2) {
174 fractionx = 1.;
175 }
176 else if (j == offsetx1) {
177 fractionx = (j + 1 - (x[i] - wx / 2. - xmin + dx / 2.) / dx) / dwx;
178 }
179 else if (j == offsetx2) {
180 fractionx = ((x[i] + wx / 2. - xmin + dx / 2.) / dx - j) / dwx;
181 }
182 else {
183 fractionx = 1 / dwx;
184 }
185
186 for(k = offsety1; k <= offsety2; k++) {
187 if (offsety1 == offsety2) {
188 fractiony = 1.;
189 }
190 else if (k == offsety1) {
191 fractiony = (k + 1 - (y[i] - wy / 2. - ymin + dy / 2.) / dy) / dwy;
192 }
193 else if (k == offsety2) {
194 fractiony = ((y[i] + wy / 2. - ymin + dy / 2.) / dy - k) / dwy;
195 }
196 else {
197 fractiony = 1 / dwy;
198 }
199
200 offset = j * ny + k;
201 odata[offset] += data[i]*fractionx*fractiony;
202 gnorm[offset] += fractionx*fractiony;
203 }
204 }
205 }
206 }
207
208 /* perform normalization */
209 if (!(flags & NO_NORMALIZATION)) {
210 if (flags & VERBOSE)
211 fprintf(stdout, "XU.FuzzyGridder2D(c): perform normalization\n");
212
213 for (i = 0; i < nx * ny; i++) {
214 if (gnorm[i] > 1.e-16) {
215 odata[i] = odata[i] / gnorm[i];
216 }
217 }
218 }
219
220 /* free the norm buffer if it has been locally allocated */
221 if (norm == NULL) {
222 free(gnorm);
223 }
224
225 /* warn the user in case more than half the data points where out
226 * of the gridding area */
227 if (noutofbounds > n / 2) {
228 fprintf(stdout,"XU.FuzzyGridder2D(c): more than half of the datapoints"
229 " out of the data range, consider regridding with"
230 " extended range!\n");
231 }
232
233 return 0;
234 }
235
236
237 /*---------------------------------------------------------------------------*/
238 PyObject* pygridder2d(PyObject *self, PyObject *args)
239 {
240 PyArrayObject *py_x = NULL, *py_y = NULL, *py_data = NULL,
241 *py_output = NULL, *py_norm = NULL;
242
243 double *x = NULL, *y = NULL, *data = NULL, *odata = NULL, *norm = NULL;
244 double xmin, xmax, ymin, ymax;
245 unsigned int nx, ny;
246 int flags;
247 int n, result;
248
249 if (!PyArg_ParseTuple(args, "O!O!O!IIddddO!|O!i",
250 &PyArray_Type, &py_x,
251 &PyArray_Type, &py_y,
252 &PyArray_Type, &py_data,
253 &nx, &ny, &xmin, &xmax, &ymin, &ymax,
254 &PyArray_Type, &py_output,
255 &PyArray_Type, &py_norm,
256 &flags)) {
257 return NULL;
258 }
259
260 /* have to check input variables */
261 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
262 PYARRAY_CHECK(py_y, 1, NPY_DOUBLE, "y-axis must be a 1D double array!");
263 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
264 "input data must be a 1D double array!");
265 PYARRAY_CHECK(py_output, 2, NPY_DOUBLE,
266 "ouput data must be a 2D double array!");
267 if (py_norm != NULL) {
268 PYARRAY_CHECK(py_norm, 2, NPY_DOUBLE,
269 "norm data must be a 2D double array!");
270 }
271
272 /* get data */
273 x = (double *) PyArray_DATA(py_x);
274 y = (double *) PyArray_DATA(py_y);
275 data = (double *) PyArray_DATA(py_data);
276 odata = (double *) PyArray_DATA(py_output);
277 if (py_norm != NULL) {
278 norm = (double *) PyArray_DATA(py_norm);
279 }
280
281 /* get the total number of points */
282 n = (int) PyArray_SIZE(py_x);
283
284 /* call the actual gridder routine */
285 result = gridder2d(x, y, data, n, nx, ny, xmin, xmax, ymin, ymax, odata,
286 norm, flags);
287
288 /* clean up */
289 Py_DECREF(py_x);
290 Py_DECREF(py_y);
291 Py_DECREF(py_data);
292 Py_DECREF(py_output);
293 if (py_norm != NULL) {
294 Py_DECREF(py_norm);
295 }
296
297 return Py_BuildValue("i", &result);
298 }
299
300 /*--------------------------------------------------------------------------*/
301 int gridder2d(double *x, double *y, double *data, unsigned int n,
302 unsigned int nx, unsigned int ny,
303 double xmin, double xmax, double ymin, double ymax,
304 double *odata, double *norm, int flags)
305 {
306 double *gnorm;
307 unsigned int offset;
308 unsigned int ntot = nx * ny; /* total number of points on the grid */
309 unsigned int noutofbounds = 0; /* number of points out of bounds */
310
311 double dx = delta(xmin, xmax, nx);
312 double dy = delta(ymin, ymax, ny);
313
314 unsigned int i; /* loop index */
315
316 /* initialize data if requested */
317 if (!(flags & NO_DATA_INIT)) {
318 set_array(odata, ntot, 0.);
319 }
320
321 /* check if normalization array is passed */
322 if (norm == NULL) {
323 gnorm = malloc(sizeof(double) * (nx * ny));
324 if (gnorm == NULL) {
325 fprintf(stderr, "XU.Gridder2D(c): Cannot allocate memory for "
326 "normalization buffer!\n");
327 return -1;
328 }
329 /* initialize memory for norm */
330 set_array(gnorm, nx * ny, 0.);
331 }
332 else {
333 if (flags & VERBOSE) {
334 fprintf(stdout, "XU.Gridder2D(c): use user provided buffer for "
335 "normalization data\n");
336 }
337 gnorm = norm;
338 }
339
340 /* the master loop over all data points */
341 for (i = 0; i < n; i++) {
342 /* if data point is nan ignore it */
343 if (!isnan(data[i])) {
344 /* if the x and y values are outside the grids boundaries
345 * continue with the next point */
346 if ((x[i] < xmin) || (x[i] > xmax)) {
347 noutofbounds++;
348 continue;
349 }
350 if ((y[i] < ymin) || (y[i] > ymax)) {
351 noutofbounds++;
352 continue;
353 }
354 /* compute the linear offset and set the data */
355 offset = gindex(x[i], xmin, dx) * ny + gindex(y[i], ymin, dy);
356
357 odata[offset] += data[i];
358 gnorm[offset] += 1.;
359 }
360 }
361
362 /* perform normalization */
363 if (!(flags & NO_NORMALIZATION)) {
364 if (flags & VERBOSE)
365 fprintf(stdout, "XU.Gridder2D(c): perform normalization ...\n");
366
367 for (i = 0; i < nx * ny; i++) {
368 if (gnorm[i] > 1.e-16) {
369 odata[i] = odata[i] / gnorm[i];
370 }
371 }
372 }
373
374 /* free the norm buffer if it has been locally allocated */
375 if (norm == NULL) {
376 free(gnorm);
377 }
378
379 /* warn the user in case more than half the data points where out
380 * of the gridding area */
381 if (noutofbounds > n / 2) {
382 fprintf(stdout,"XU.Gridder2D(c): more than half of the datapoints out "
383 "of the data range, consider regridding with extended "
384 "range!\n");
385 }
386
387 return 0;
388 }
389
+0
-427
xrayutilities/src/gridder3d.c less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
17 * Copyright (C) 2013, 2015 Dominik Kriegner <dominik.kriegner@gmail.com>
18 *
19 ******************************************************************************
20 *
21 * created: Jun 21, 2013
22 * author: Eugen Wintersberger
23 */
24
25 #include "gridder.h"
26 #include "gridder_utils.h"
27
28 PyObject* pyfuzzygridder3d(PyObject *self, PyObject *args)
29 {
30 PyArrayObject *py_x = NULL, *py_y = NULL, *py_z = NULL, *py_data = NULL,
31 *py_output = NULL, *py_norm = NULL;
32
33 double *x = NULL, *y = NULL, *z = NULL, *data = NULL, *odata = NULL,
34 *norm = NULL;
35 double xmin, xmax, ymin, ymax, zmin, zmax, wx, wy, wz;
36 unsigned int nx, ny, nz;
37 int flags;
38 int n, result;
39
40 if (!PyArg_ParseTuple(args, "O!O!O!O!IIIddddddO!|O!dddi",
41 &PyArray_Type, &py_x,
42 &PyArray_Type, &py_y,
43 &PyArray_Type, &py_z,
44 &PyArray_Type, &py_data,
45 &nx, &ny, &nz,
46 &xmin, &xmax, &ymin, &ymax, &zmin, &zmax,
47 &PyArray_Type, &py_output,
48 &PyArray_Type, &py_norm,
49 &wx, &wy, &wz, &flags)) {
50 return NULL;
51 }
52
53 /* check input variables */
54 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
55 PYARRAY_CHECK(py_y, 1, NPY_DOUBLE, "y-axis must be a 1D double array!");
56 PYARRAY_CHECK(py_z, 1, NPY_DOUBLE, "z-axis must be a 1D double array!");
57 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
58 "input data must be a 1D double array!");
59 PYARRAY_CHECK(py_output, 3, NPY_DOUBLE,
60 "ouput data must be a 2D double array!");
61 if (py_norm != NULL) {
62 PYARRAY_CHECK(py_norm, 3, NPY_DOUBLE,
63 "norm data must be a 2D double array!");
64 }
65
66 /* get data */
67 x = (double *) PyArray_DATA(py_x);
68 y = (double *) PyArray_DATA(py_y);
69 z = (double *) PyArray_DATA(py_z);
70 data = (double *) PyArray_DATA(py_data);
71 odata = (double *) PyArray_DATA(py_output);
72 if (py_norm != NULL) {
73 norm = (double *) PyArray_DATA(py_norm);
74 }
75
76 /* get the total number of points */
77 n = (int) PyArray_SIZE(py_x);
78
79 /* call the actual gridder routine */
80 result = fuzzygridder3d(x, y, z, data, n, nx, ny, nz,
81 xmin, xmax, ymin, ymax, zmin, zmax, odata, norm,
82 wx, wy, wz, flags);
83
84 /* clean up */
85 Py_DECREF(py_x);
86 Py_DECREF(py_y);
87 Py_DECREF(py_z);
88 Py_DECREF(py_data);
89 Py_DECREF(py_output);
90 if (py_norm != NULL) {
91 Py_DECREF(py_norm);
92 }
93
94 return Py_BuildValue("i", &result);
95 }
96
97 /*---------------------------------------------------------------------------*/
98 int fuzzygridder3d(double *x, double *y, double *z, double *data,
99 unsigned int n, unsigned int nx, unsigned int ny,
100 unsigned int nz, double xmin, double xmax, double ymin,
101 double ymax, double zmin, double zmax,
102 double *odata, double *norm,
103 double wx, double wy, double wz, int flags)
104 {
105 double *gnorm; /* pointer to normalization data */
106 unsigned int offset, offsetx1, offsetx2, offsety1, offsety2, offsetz1, offsetz2;
107 unsigned int ntot = nx * ny * nz; /* total number of points on the grid */
108 unsigned int i, j, k, l; /* loop indeces variables */
109 unsigned int noutofbounds = 0; /* number of points out of bounds */
110
111 double fractionx, fractiony, fractionz, dwx, dwy, dwz; /* variables for
112 the fuzziness */
113 /* compute step width for the grid */
114 double dx = delta(xmin, xmax, nx);
115 double dy = delta(ymin, ymax, ny);
116 double dz = delta(zmin, zmax, nz);
117
118 /* initialize data if requested */
119 if (!(flags & NO_DATA_INIT)) {
120 set_array(odata, ntot, 0.);
121 }
122
123 /* check if normalization array is passed */
124 if (norm == NULL) {
125 gnorm = malloc(sizeof(double) * ntot);
126 if (gnorm == NULL) {
127 fprintf(stderr, "XU.FuzzyGridder3D(c): Cannot allocate memory for "
128 "normalization buffer!\n");
129 return -1;
130 }
131 /* initialize memory for norm */
132 set_array(gnorm, ntot, 0.);
133 }
134 else {
135 gnorm = norm;
136 }
137
138 /* calculate the fuzzy spread in number of bin sizes */
139 dwx = wx / dx;
140 dwy = wy / dy;
141 dwz = wz / dz;
142 if (flags & VERBOSE) {
143 fprintf(stdout, "XU.FuzzyGridder3D(c): fuzzyness: %f %f %f %f %f %f\n",
144 wx, wy, wz, dwx, dwy, dwz);
145 }
146 /* the master loop over all data points */
147 for (i = 0; i < n; i++) {
148 if (!isnan(data[i])) {
149 /* check if the current point is within the bounds of the grid */
150 if ((x[i] < xmin) || (x[i] > xmax)) {
151 noutofbounds++;
152 continue;
153 }
154 if ((y[i] < ymin) || (y[i] > ymax)) {
155 noutofbounds++;
156 continue;
157 }
158 if ((z[i] < zmin) || (z[i] > zmax)) {
159 noutofbounds++;
160 continue;
161 }
162
163 /* compute the offset value of the current input point on the
164 * grid array */
165 /* compute the linear offset and distribute the data to the bins */
166 if ((x[i] - wx / 2.) <= xmin) {
167 offsetx1 = 0;
168 }
169 else {
170 offsetx1 = gindex(x[i] - wx / 2., xmin, dx);
171 }
172 offsetx2 = gindex(x[i] + wx / 2., xmin, dx);
173 offsetx2 = offsetx2 < nx ? offsetx2 : nx - 1;
174 if ((y[i] - wy / 2.) <= ymin) {
175 offsety1 = 0;
176 }
177 else {
178 offsety1 = gindex(y[i] - wy / 2., ymin, dy);
179 }
180 offsety2 = gindex(y[i] + wy / 2., ymin, dy);
181 offsety2 = offsety2 < ny ? offsety2 : ny - 1;
182 if ((z[i] - wz / 2.) <= zmin) {
183 offsetz1 = 0;
184 }
185 else {
186 offsetz1 = gindex(z[i] - wz / 2., zmin, dz);
187 }
188 offsetz2 = gindex(z[i] + wz / 2., zmin, dz);
189 offsetz2 = offsetz2 < nz ? offsetz2 : nz - 1;
190
191 for(j = offsetx1; j <= offsetx2; j++) {
192 if (offsetx1 == offsetx2) {
193 fractionx = 1.;
194 }
195 else if (j == offsetx1) {
196 fractionx = (j + 1 - (x[i] - wx / 2. - xmin + dx / 2.) / dx) / dwx;
197 }
198 else if (j == offsetx2) {
199 fractionx = ((x[i] + wx / 2. - xmin + dx / 2.) / dx - j) / dwx;
200 }
201 else {
202 fractionx = 1 / dwx;
203 }
204
205 for(k = offsety1; k <= offsety2; k++) {
206 if (offsety1 == offsety2) {
207 fractiony = 1.;
208 }
209 else if (k == offsety1) {
210 fractiony = (k + 1 - (y[i] - wy / 2. - ymin + dy / 2.) / dy) / dwy;
211 }
212 else if (k == offsety2) {
213 fractiony = ((y[i] + wy / 2. - ymin + dy / 2.) / dy - k) / dwy;
214 }
215 else {
216 fractiony = 1 / dwy;
217 }
218 for(l = offsetz1; l <= offsetz2; l++) {
219 if (offsetz1 == offsetz2) {
220 fractionz = 1.;
221 }
222 else if (l == offsetz1) {
223 fractionz = (l + 1 - (z[i] - wz / 2. - zmin + dz / 2.) / dz) / dwz;
224 }
225 else if (l == offsetz2) {
226 fractionz = ((z[i] + wz / 2. - zmin + dz / 2.) / dz - l) / dwz;
227 }
228 else {
229 fractionz = 1 / dwz;
230 }
231
232 offset = j * ny * nz + k * nz + l;
233 odata[offset] += data[i]*fractionx*fractiony*fractionz;
234 gnorm[offset] += fractionx*fractiony*fractionz;
235 }
236 }
237 }
238 }
239 }
240
241 /* perform normalization */
242 if (!(flags & NO_NORMALIZATION)) {
243 for (i = 0; i < ntot; i++) {
244 if (gnorm[i] > 1.e-16) {
245 odata[i] = odata[i] / gnorm[i];
246 }
247 }
248 }
249
250 /* free the norm buffer if it has been locally allocated */
251 if (norm == NULL) {
252 free(gnorm);
253 }
254
255 /* warn the user in case more than half the data points where out
256 * of the gridding area */
257 if (noutofbounds > n / 2) {
258 fprintf(stdout, "XU.FuzzyGridder3D(c): more than half of the "
259 "datapoints out of the data range, consider regridding with "
260 "extended range!\n");
261 }
262
263 return 0;
264 }
265
266
267 /*---------------------------------------------------------------------------*/
268 PyObject* pygridder3d(PyObject *self, PyObject *args)
269 {
270 PyArrayObject *py_x = NULL, *py_y = NULL, *py_z = NULL, *py_data = NULL,
271 *py_output = NULL, *py_norm = NULL;
272
273 double *x = NULL, *y = NULL, *z = NULL, *data = NULL, *odata = NULL,
274 *norm = NULL;
275 double xmin, xmax, ymin, ymax, zmin, zmax;
276 unsigned int nx, ny, nz;
277 int flags;
278 int n, result;
279
280 if (!PyArg_ParseTuple(args, "O!O!O!O!IIIddddddO!|O!i",
281 &PyArray_Type, &py_x,
282 &PyArray_Type, &py_y,
283 &PyArray_Type, &py_z,
284 &PyArray_Type, &py_data,
285 &nx, &ny, &nz,
286 &xmin, &xmax, &ymin, &ymax, &zmin, &zmax,
287 &PyArray_Type, &py_output,
288 &PyArray_Type, &py_norm,
289 &flags)) {
290 return NULL;
291 }
292
293 /* check input variables */
294 PYARRAY_CHECK(py_x, 1, NPY_DOUBLE, "x-axis must be a 1D double array!");
295 PYARRAY_CHECK(py_y, 1, NPY_DOUBLE, "y-axis must be a 1D double array!");
296 PYARRAY_CHECK(py_z, 1, NPY_DOUBLE, "z-axis must be a 1D double array!");
297 PYARRAY_CHECK(py_data, 1, NPY_DOUBLE,
298 "input data must be a 1D double array!");
299 PYARRAY_CHECK(py_output, 3, NPY_DOUBLE,
300 "ouput data must be a 2D double array!");
301 if (py_norm != NULL) {
302 PYARRAY_CHECK(py_norm, 3, NPY_DOUBLE,
303 "norm data must be a 2D double array!");
304 }
305
306 /* get data */
307 x = (double *) PyArray_DATA(py_x);
308 y = (double *) PyArray_DATA(py_y);
309 z = (double *) PyArray_DATA(py_z);
310 data = (double *) PyArray_DATA(py_data);
311 odata = (double *) PyArray_DATA(py_output);
312 if (py_norm != NULL) {
313 norm = (double *) PyArray_DATA(py_norm);
314 }
315
316 /* get the total number of points */
317 n = (int) PyArray_SIZE(py_x);
318
319 /* call the actual gridder routine */
320 result = gridder3d(x, y, z, data, n, nx, ny, nz,
321 xmin, xmax, ymin, ymax, zmin, zmax, odata, norm, flags);
322
323 /* clean up */
324 Py_DECREF(py_x);
325 Py_DECREF(py_y);
326 Py_DECREF(py_z);
327 Py_DECREF(py_data);
328 Py_DECREF(py_output);
329 if (py_norm != NULL) {
330 Py_DECREF(py_norm);
331 }
332
333 return Py_BuildValue("i", &result);
334 }
335
336 /*---------------------------------------------------------------------------*/
337 int gridder3d(double *x, double *y, double *z, double *data, unsigned int n,
338 unsigned int nx, unsigned int ny, unsigned int nz,
339 double xmin, double xmax, double ymin, double ymax,
340 double zmin, double zmax,
341 double *odata, double *norm, int flags)
342 {
343 double *gnorm; /* pointer to normalization data */
344 unsigned int offset; /* linear offset for the grid data */
345 unsigned int ntot = nx * ny * nz; /* total number of points on the grid */
346 unsigned int i; /* loop index variable */
347 unsigned int noutofbounds = 0; /* number of points out of bounds */
348
349 /* compute step width for the grid */
350 double dx = delta(xmin, xmax, nx);
351 double dy = delta(ymin, ymax, ny);
352 double dz = delta(zmin, zmax, nz);
353
354 /* initialize data if requested */
355 if (!(flags & NO_DATA_INIT)) {
356 set_array(odata, ntot, 0.);
357 }
358
359 /* check if normalization array is passed */
360 if (norm == NULL) {
361 gnorm = malloc(sizeof(double) * ntot);
362 if (gnorm == NULL) {
363 fprintf(stderr, "XU.Gridder3D(c): Cannot allocate memory for "
364 "normalization buffer!\n");
365 return -1;
366 }
367 /* initialize memory for norm */
368 set_array(gnorm, ntot, 0.);
369 }
370 else {
371 gnorm = norm;
372 }
373
374 /* the master loop over all data points */
375 for (i = 0; i < n; i++) {
376 if (!isnan(data[i])) {
377 /* check if the current point is within the bounds of the grid */
378 if ((x[i] < xmin) || (x[i] > xmax)) {
379 noutofbounds++;
380 continue;
381 }
382 if ((y[i] < ymin) || (y[i] > ymax)) {
383 noutofbounds++;
384 continue;
385 }
386 if ((z[i] < zmin) || (z[i] > zmax)) {
387 noutofbounds++;
388 continue;
389 }
390
391 /* compute the offset value of the current input point on the
392 * grid array */
393 offset = gindex(x[i], xmin, dx) * ny * nz +
394 gindex(y[i], ymin, dy) * nz +
395 gindex(z[i], zmin, dz);
396
397 odata[offset] += data[i];
398 gnorm[offset] += 1.;
399 }
400 }
401
402 /* perform normalization */
403 if (!(flags & NO_NORMALIZATION)) {
404 for (i = 0; i < ntot; i++) {
405 if (gnorm[i] > 1.e-16) {
406 odata[i] = odata[i] / gnorm[i];
407 }
408 }
409 }
410
411 /* free the norm buffer if it has been locally allocated */
412 if (norm == NULL) {
413 free(gnorm);
414 }
415
416 /* warn the user in case more than half the data points where out
417 * of the gridding area */
418 if (noutofbounds > n / 2) {
419 fprintf(stdout, "XU.Gridder3D(c): more than half of the datapoints "
420 "out of the data range, consider regridding with extended "
421 "range!\n");
422 }
423
424 return 0;
425 }
426
+0
-86
xrayutilities/src/gridder_utils.c less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
17 * Copyright (C) 2013 Dominik Kriegner <dominik.kriegner@gmail.com>
18 *
19 ******************************************************************************
20 *
21 * created: Jun 8, 2013
22 * author: Eugen Wintersberger
23 */
24
25 #include "gridder_utils.h"
26
27 /*---------------------------------------------------------------------------*/
28 double get_min(double *a, unsigned int n)
29 {
30 double m = a[0];
31 unsigned int i;
32
33 for (i = 0; i < n; i++) {
34 if (m < a[i]) {
35 m = a[i];
36 }
37 }
38
39 return m;
40 }
41
42 /*---------------------------------------------------------------------------*/
43 double get_max(double *a, unsigned int n)
44 {
45 double m = a[0];
46 unsigned int i;
47
48 for (i = 0; i < n; i++) {
49 if (m > a[i]) {
50 m = a[i];
51 }
52 }
53
54 return m;
55 }
56
57 /*---------------------------------------------------------------------------*/
58 void set_array(double *a, unsigned int n, double value)
59 {
60 unsigned int i;
61
62 for (i = 0; i < n; ++i) {
63 a[i] = value;
64 }
65 }
66
67 /*---------------------------------------------------------------------------*/
68 double delta(double min, double max, unsigned int n)
69 {
70 return fabs(max - min) / (double) (n - 1);
71 }
72
73 /*---------------------------------------------------------------------------*/
74 unsigned int gindex(double x, double min, double d)
75 {
76 return (unsigned int) rint((x - min) / d);
77 }
78
79 /*---------------------------------------------------------------------------*/
80 #ifdef _WIN32
81 double rint(double x)
82 {
83 return x < 0.0 ? ceil(x - 0.5) : floor(x + 0.5);
84 }
85 #endif
+0
-86
xrayutilities/src/gridder_utils.h less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
17 * Copyright (C) 2013 Dominik Kriegner <dominik.kriegner@gmail.com>
18 *
19 ******************************************************************************
20 *
21 * created: Jun 8, 2013
22 * author: Eugen Wintersberger
23 */
24 #pragma once
25
26 #include <stdlib.h>
27 #include <stdio.h>
28
29 #include "xrayutilities.h"
30
31 #ifdef _WIN32
32 double rint(double x);
33 #endif
34
35 /*!
36 \brief find minimum
37
38 Finds the minimum in an array.
39 \param a input data
40 \param n number of elements
41 \return minimum value
42 */
43 double get_min(double *a, unsigned int n);
44
45 /*---------------------------------------------------------------------------*/
46 /*!
47 \brief find maximum
48
49 Finds the maximum value in an array.
50 \param a input data
51 \param n number of elements
52 \return return maximum value
53 */
54 double get_max(double *a, unsigned int n);
55
56 /*---------------------------------------------------------------------------*/
57 /*!
58 \brief set array values
59
60 Set all elements of an array to the same values.
61 \param a input array
62 \param n number of points
63 \param value the new element values
64 */
65 void set_array(double *a, unsigned int n, double value);
66
67 /*---------------------------------------------------------------------------*/
68 /*!
69 \brief compute step width
70
71 Computes the stepwidth of a grid.
72 \param min minimum value
73 \param max maximum value
74 \param n number of steps
75 \return step width
76 */
77 double delta(double min, double max, unsigned int n);
78
79 /*---------------------------------------------------------------------------*/
80 /*!
81 \brief compute grid index
82
83 */
84 unsigned int gindex(double x, double min, double d);
85
+0
-3381
xrayutilities/src/qconversion.c less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2010-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
17 */
18
19 /* ######################################
20 * conversion of angular coordinates
21 * to reciprocal space
22 * using general algorithms to work
23 * for different types of geometries
24 * and detectors
25 * ######################################*/
26
27
28 #include "qconversion.h"
29 #include <ctype.h>
30 #include <math.h>
31
32
33 /* ###################################
34 * matrix vector operations for
35 * 3x3 matrices and vectors of length
36 * 3
37 * ################################### */
38
39 INLINE void ident(double *m) {
40 m[0] = 1.; m[1] = 0.; m[2] = 0.;
41 m[3] = 0.; m[4] = 1.; m[5] = 0.;
42 m[6] = 0.; m[7] = 0.; m[8] = 1.;
43 }
44
45 INLINE void sumvec(double *RESTRICT v1, double *RESTRICT v2) {
46 unsigned int i;
47 for (i = 0; i < 3; ++i) {
48 v1[i] += v2[i];
49 }
50 }
51
52 INLINE void diffvec(double *RESTRICT v1, double *RESTRICT v2) {
53 unsigned int i;
54 for (i = 0; i < 3; ++i) {
55 v1[i] -= v2[i];
56 }
57 }
58
59 INLINE double norm(double *v) {
60 double n = 0;
61 unsigned int i;
62 for (i = 0; i < 3; ++i) {
63 n += v[i] * v[i];
64 }
65 return sqrt(n);
66 }
67
68 INLINE void normalize(double *v) {
69 double n = norm(v);
70 unsigned int i;
71 for (i = 0; i < 3; ++i) {
72 v[i] /= n;
73 }
74 }
75
76 INLINE void veccopy(double *RESTRICT v1, double *RESTRICT v2) {
77 unsigned int i;
78 for (i = 0; i < 3; ++i) {
79 v1[i] = v2[i];
80 }
81 }
82
83 INLINE void vecmul(double *RESTRICT r, double a) {
84 unsigned int i;
85 for (i = 0; i < 3; ++i) {
86 r[i] *= a;
87 }
88 }
89
90 INLINE void cross(double *RESTRICT v1, double *RESTRICT v2,
91 double *RESTRICT r) {
92 r[0] = v1[1] * v2[2] - v1[2] * v2[1];
93 r[1] = -v1[0] * v2[2] + v1[2] * v2[0];
94 r[2] = v1[0] * v2[1] - v1[1] * v2[0];
95 }
96
97 INLINE void vecmatcross(double *RESTRICT v, double *RESTRICT m,
98 double *RESTRICT mr) {
99 unsigned int i;
100 for (i = 0; i < 9; i = i + 3) {
101 mr[0 + i] = v[1] * m[2 + i] - v[2] * m[1 + i];
102 mr[1 + i] = -v[0] * m[2 + i] + v[2] * m[0 + i];
103 mr[2 + i] = v[0] * m[1 + i] - v[1] * m[0 + i];
104 }
105 }
106
107 INLINE void matmulc(double *RESTRICT m, double c) {
108 unsigned int i;
109 for (i = 0; i < 9; i = i + 1) {
110 m[i] *= c;
111 }
112 }
113
114 INLINE void matvec(double *RESTRICT m, double *RESTRICT v,
115 double *RESTRICT r) {
116 r[0] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2];
117 r[1] = m[3] * v[0] + m[4] * v[1] + m[5] * v[2];
118 r[2] = m[6] * v[0] + m[7] * v[1] + m[8] * v[2];
119 }
120
121 INLINE void matmul(double *RESTRICT m1, double *RESTRICT m2) {
122 double a, b, c;
123 unsigned int i;
124 for (i = 0; i < 9; i = i + 3) {
125 a = m1[i] * m2[0] + m1[i + 1] * m2[3] + m1[i + 2] * m2[6];
126 b = m1[i] * m2[1] + m1[i + 1] * m2[4] + m1[i + 2] * m2[7];
127 c = m1[i] * m2[2] + m1[i + 1] * m2[5] + m1[i + 2] * m2[8];
128 m1[i] = a;
129 m1[i + 1] = b;
130 m1[i + 2] = c;
131 }
132 }
133
134 INLINE void tensorprod(double *RESTRICT v1, double *RESTRICT v2,
135 double *RESTRICT m) {
136 unsigned int i, j;
137 for (i = 0; i < 3; i = i + 1) {
138 for (j = 0; j < 3; j = j + 1) {
139 m[i * 3 + j] = v1[i] * v2[j];
140 }
141 }
142 }
143
144 INLINE void summat(double *RESTRICT m1, double *RESTRICT m2) {
145 unsigned int i;
146 for (i = 0; i < 9; ++i) {
147 m1[i] += m2[i];
148 }
149 }
150
151 INLINE void diffmat(double *RESTRICT m1, double *RESTRICT m2) {
152 unsigned int i;
153 for (i = 0; i < 9; ++i) {
154 m1[i] -= m2[i];
155 }
156 }
157
158 INLINE void inversemat(double *RESTRICT m, double *RESTRICT i) {
159 double det;
160 double h1, h2, h3, h4, h5, h6;
161 unsigned int j;
162
163 h1 = m[4] * m[8]; /* m11*m22 */
164 h2 = m[5] * m[6]; /* m12*m20 */
165 h3 = m[3] * m[7]; /* m10*m21 */
166 h4 = m[4] * m[6]; /* m11*m20 */
167 h5 = m[3] * m[8]; /* m10*m22 */
168 h6 = m[5] * m[7]; /* m12*m21 */
169 det = m[0] * h1 + m[1] * h2 + m[2] * h3 - \
170 m[2] * h4 - m[1] * h5 - m[0] * h6;
171
172 i[0] = (h1 - h6);
173 i[1] = (m[2] * m[7] - m[1] * m[8]);
174 i[2] = (m[1] * m[5] - m[2] * m[4]);
175 i[3] = (h2 - h5);
176 i[4] = (m[0] * m[8] - m[2] * m[6]);
177 i[5] = (m[2] * m[3] - m[0] * m[5]);
178 i[6] = (h3 - h4);
179 i[7] = (m[1] * m[6] - m[0] * m[7]);
180 i[8] = (m[0] * m[4] - m[1] * m[3]);
181
182 for (j = 0; j < 9; ++j) {
183 i[j] /= det;
184 }
185 }
186
187 INLINE double determinant(double *RESTRICT m) {
188 double h1, h2, h3, h4, h5, h6;
189 double det = 0;
190
191 h1 = m[4] * m[8]; /* m11*m22 */
192 h2 = m[5] * m[6]; /* m12*m20 */
193 h3 = m[3] * m[7]; /* m10*m21 */
194 h4 = m[4] * m[6]; /* m11*m20 */
195 h5 = m[3] * m[8]; /* m10*m22 */
196 h6 = m[5] * m[7]; /* m12*m21 */
197
198 det = m[0] * h1 + m[1] * h2 + m[2] * h3 - \
199 m[2] * h4 - m[1] * h5 - m[0] * h6;
200 return det;
201 }
202
203
204 /*##############################################
205 # functions which implement rotation matrices
206 # for all coordinate axes and rotation senses
207 #
208 # the routines expect angles in radians
209 # for conversion from degrees to radians
210 # the functions and2rad and rad2ang are
211 # supplied
212 ################################################*/
213
214 INLINE void rotation_xp(double a, double *mat){
215 double sa = sin(a), ca = cos(a);
216 mat[0] = 1.; mat[1] = 0.; mat[2] = 0.;
217 mat[3] = 0.; mat[4] = ca; mat[5] = -sa;
218 mat[6] = 0.; mat[7] = sa; mat[8] = ca;
219 }
220
221 INLINE void apply_xp(double a, double *vec){
222 double mat[9], vtemp[3];
223 rotation_xp(a, mat);
224 veccopy(vtemp, vec);
225 matvec(mat, vtemp, vec);
226 }
227
228 INLINE void rotation_xm(double a, double *mat){
229 double sa = sin(a), ca = cos(a);
230 mat[0] = 1.; mat[1] = 0.; mat[2] = 0.;
231 mat[3] = 0.; mat[4] = ca; mat[5] = sa;
232 mat[6] = 0.; mat[7] = -sa; mat[8] = ca;
233 }
234
235 INLINE void apply_xm(double a, double *vec){
236 double mat[9], vtemp[3];
237 rotation_xm(a, mat);
238 veccopy(vtemp, vec);
239 matvec(mat, vtemp, vec);
240 }
241
242 INLINE void rotation_yp(double a, double *mat){
243 double sa = sin(a), ca = cos(a);
244 mat[0] = ca; mat[1] = 0.; mat[2] = sa;
245 mat[3] = 0.; mat[4] = 1.; mat[5] = 0.;
246 mat[6] = -sa; mat[7] = 0.; mat[8] = ca;
247 }
248
249 INLINE void apply_yp(double a, double *vec){
250 double mat[9], vtemp[3];
251 rotation_yp(a, mat);
252 veccopy(vtemp, vec);
253 matvec(mat, vtemp, vec);
254 }
255
256 INLINE void rotation_ym(double a, double *mat){
257 double sa = sin(a), ca = cos(a);
258 mat[0] = ca; mat[1] = 0.; mat[2] = -sa;
259 mat[3] = 0.; mat[4] = 1.; mat[5] = 0.;
260 mat[6] = sa; mat[7] = 0.; mat[8] = ca;
261 }
262
263 INLINE void apply_ym(double a, double *vec){
264 double mat[9], vtemp[3];
265 rotation_ym(a, mat);
266 veccopy(vtemp, vec);
267 matvec(mat, vtemp, vec);
268 }
269
270 INLINE void rotation_zp(double a, double *mat){
271 double sa = sin(a), ca = cos(a);
272 mat[0] = ca; mat[1] = -sa; mat[2] = 0.;
273 mat[3] = sa; mat[4] = ca; mat[5] = 0.;
274 mat[6] = 0.; mat[7] = 0.; mat[8] = 1.;
275 }
276
277 INLINE void apply_zp(double a, double *vec){
278 double mat[9], vtemp[3];
279 rotation_zp(a, mat);
280 veccopy(vtemp, vec);
281 matvec(mat, vtemp, vec);
282 }
283
284 INLINE void rotation_zm(double a, double *mat){
285 double sa = sin(a), ca = cos(a);
286 mat[0] = ca; mat[1] = sa; mat[2] = 0.;
287 mat[3] = -sa; mat[4] = ca; mat[5] = 0.;
288 mat[6] = 0.; mat[7] = 0.; mat[8] = 1.;
289 }
290
291 INLINE void apply_zm(double a, double *vec){
292 double mat[9], vtemp[3];
293 rotation_zm(a, mat);
294 veccopy(vtemp, vec);
295 matvec(mat, vtemp, vec);
296 }
297
298 INLINE void rotation_kappa(double a, double *mat){
299 double e[3];
300 e[0] = mat[0]; e[1] = mat[1]; e[2] = mat[2];
301 rotation_arb(a, e, mat);
302 }
303
304 INLINE void rotation_arb(double a, double *RESTRICT e, double *RESTRICT mat) {
305 double sa = sin(a), ca = cos(a);
306 double mtemp[9], mtemp2[9];
307
308 /* e must be normalized */
309
310 /* ca*(ident(3) - vec(e) o vec(e))*/
311 ident(mat);
312 tensorprod(e, e, mtemp);
313 diffmat(mat, mtemp);
314 matmulc(mat, ca);
315
316 /* tensorprod(vec(e), vec(e)) */
317 summat(mat, mtemp);
318
319 /* sa*(vec(e) cross ident(3)) */
320 ident(mtemp2);
321 vecmatcross(e, mtemp2, mtemp);
322 matmulc(mtemp, sa);
323 summat(mat, mtemp);
324 }
325
326 INLINE void apply_tx(double x, double *vec){
327 vec[0] += x;
328 }
329
330 INLINE void apply_ty(double y, double *vec){
331 vec[1] += y;
332 }
333
334 INLINE void apply_tz(double z, double *vec){
335 vec[2] += z;
336 }
337
338 /* #######################################
339 * debug helper functions
340 * #######################################*/
341 int print_matrix(double *m) {
342 unsigned int i;
343 for (i = 0; i < 9; i += 3) {
344 printf("%8.5g %8.5g %8.5g\n", m[i], m[i + 1], m[i + 2]);
345 }
346 printf("\n");
347 return 0;
348 }
349
350 int print_vector(double *m) {
351 printf("\n%8.5g %8.5g %8.5g\n", m[0], m[1], m[2]);
352 return 0;
353 }
354
355 /* #######################################
356 * conversion helper functions
357 * #######################################*/
358
359 int determine_detector_pixel(double *rpixel, char *dir, double dpixel,
360 double *r_i, double tilt) {
361 /* determine the direction of a linear detector or one of the directions of
362 * an area detector. the function returns the vector containing the
363 * distance from one to the next pixel a tilt of the detector axis with
364 * respect to the coordinate axis can be considered as well! rotation of
365 * pixel direction around the crossproduct of primary beam and detector
366 * axis. this is mainly usefull for linear detectors, since the tilt of
367 * area detectors is handled different. */
368
369 double tiltaxis[3], tiltmat[9];
370 unsigned int i;
371
372 for (i = 0; i < 3; ++i) {
373 rpixel[i] = 0.;
374 }
375
376 switch (tolower(dir[0])) {
377 case 'x':
378 switch (dir[1]) {
379 case '+':
380 rpixel[0] = dpixel;
381 break;
382 case '-':
383 rpixel[0] = -dpixel;
384 break;
385 default:
386 PyErr_SetString(PyExc_ValueError,
387 "XU.Qconversion(c): detector determination: no valid "
388 "direction sign given");
389 return 1;
390 }
391 break;
392 case 'y':
393 switch (dir[1]) {
394 case '+':
395 rpixel[1] = dpixel;
396 break;
397 case '-':
398 rpixel[1] = -dpixel;
399 break;
400 default:
401 PyErr_SetString(PyExc_ValueError,
402 "XU.Qconversion(c): detector determination: no valid "
403 "direction sign given");
404 return 1;
405 }
406 break;
407 case 'z':
408 switch (dir[1]) {
409 case '+':
410 rpixel[2] = dpixel;
411 break;
412 case '-':
413 rpixel[2] = -dpixel;
414 break;
415 default:
416 PyErr_SetString(PyExc_ValueError,
417 "XU.Qconversion(c): detector determination: no valid "
418 "direction sign given");
419 return 1;
420 }
421 break;
422 default:
423 PyErr_SetString(PyExc_ValueError,
424 "XU.Qconversion(c): detector determination: no valid "
425 "direction direction given");
426 return 2;
427 }
428
429 /* include possible tilt of detector axis with respect to its direction */
430 cross(r_i, rpixel, tiltaxis);
431 normalize(tiltaxis);
432 /* check if there is a problem with the tiltaxis */
433 for (i = 0; i < 3; ++i) {
434 if (isnan(tiltaxis[i])) {
435 memset(tiltaxis, 0, sizeof(tiltaxis));
436 }
437 }
438 /* create needed rotation matrix */
439 rotation_arb(tilt, tiltaxis, tiltmat);
440 /* rotate rpixel */
441 matvec(tiltmat, rpixel, tiltaxis);
442 veccopy(rpixel, tiltaxis);
443 return 0;
444 }
445
446 int determine_axes_directions(fp_rot *fp_circles, char *stringAxis,
447 unsigned int n) {
448 /* feed the function pointer array with the correct
449 * rotation matrix generating functions
450 * */
451 unsigned int i;
452
453 for (i = 0; i < n; ++i) {
454 switch (tolower(stringAxis[2 * i])) {
455 case 'x':
456 switch (stringAxis[2 * i + 1]) {
457 case '+':
458 fp_circles[i] = &rotation_xp;
459 break;
460 case '-':
461 fp_circles[i] = &rotation_xm;
462 break;
463 default:
464 PyErr_SetString(PyExc_ValueError,
465 "XU.Qconversion(c): axis determination: no valid "
466 "rotation sense given");
467 return 1;
468 }
469 break;
470 case 'y':
471 switch (stringAxis[2 * i + 1]) {
472 case '+':
473 fp_circles[i] = &rotation_yp;
474 break;
475 case '-':
476 fp_circles[i] = &rotation_ym;
477 break;
478 default:
479 PyErr_SetString(PyExc_ValueError,
480 "XU.Qconversion(c): axis determination: no valid "
481 "rotation sense given");
482 return 1;
483 }
484 break;
485 case 'z':
486 switch(stringAxis[2 * i + 1]) {
487 case '+':
488 fp_circles[i] = &rotation_zp;
489 break;
490 case '-':
491 fp_circles[i] = &rotation_zm;
492 break;
493 default:
494 PyErr_SetString(PyExc_ValueError,
495 "XU.Qconversion(c): axis determination: no valid "
496 "rotation sense given");
497 return 1;
498 }
499 break;
500 case 'k':
501 fp_circles[i] = &rotation_kappa;
502 break;
503 default:
504 PyErr_SetString(PyExc_ValueError,
505 "XU.Qconversion(c): axis determination: no valid axis "
506 "direction given");
507 return 2;
508 }
509 }
510
511 return 0;
512 }
513
514 int determine_axes_directions_apply(fp_rot *fp_circles, char *stringAxis,
515 unsigned int n) {
516 /* feed the function pointer array with the correct
517 * rotation/translation applying functions
518 * */
519 unsigned int i;
520
521 for (i = 0; i < n; ++i) {
522 switch (tolower(stringAxis[2 * i])) {
523 case 'x':
524 switch (stringAxis[2 * i + 1]) {
525 case '+':
526 fp_circles[i] = &apply_xp;
527 break;
528 case '-':
529 fp_circles[i] = &apply_xm;
530 break;
531 default:
532 PyErr_SetString(PyExc_ValueError,
533 "XU.Qconversion(c): axis determination: no valid "
534 "rotation sense given");
535 return 1;
536 }
537 break;
538 case 'y':
539 switch (stringAxis[2 * i + 1]) {
540 case '+':
541 fp_circles[i] = &apply_yp;
542 break;
543 case '-':
544 fp_circles[i] = &apply_ym;
545 break;
546 default:
547 PyErr_SetString(PyExc_ValueError,
548 "XU.Qconversion(c): axis determination: no valid "
549 "rotation sense given");
550 return 1;
551 }
552 break;
553 case 'z':
554 switch(stringAxis[2 * i + 1]) {
555 case '+':
556 fp_circles[i] = &apply_zp;
557 break;
558 case '-':
559 fp_circles[i] = &apply_zm;
560 break;
561 default:
562 PyErr_SetString(PyExc_ValueError,
563 "XU.Qconversion(c): axis determination: no valid "
564 "rotation sense given");
565 return 1;
566 }
567 break;
568 case 't':
569 switch(stringAxis[2 * i + 1]) {
570 case 'x':
571 fp_circles[i] = &apply_tx;
572 break;
573 case 'y':
574 fp_circles[i] = &apply_ty;
575 break;
576 case 'z':
577 fp_circles[i] = &apply_tz;
578 break;
579 default:
580 PyErr_SetString(PyExc_ValueError,
581 "XU.Qconversion(c): axis determination: no valid "
582 "translation given");
583 return 1;
584 }
585 break;
586 default:
587 PyErr_SetString(PyExc_ValueError,
588 "XU.Qconversion(c): axis determination: no valid axis "
589 "direction given");
590 return 2;
591 }
592 }
593
594 return 0;
595 }
596
597 int tilt_detector_axis(double tiltazimuth, double tilt,
598 double *RESTRICT rpixel1, double *RESTRICT rpixel2) {
599 /* rotate detector pixel vectors of a 2D detector according to tilt and
600 * tiltazimuth */
601 double rtemp[3], rtemp2[3]; /* buffer vectors */
602 double mtemp[9]; /* rotation matrix buffer */
603
604 veccopy(rtemp, rpixel1);
605 normalize(rtemp);
606 vecmul(rtemp, cos(tiltazimuth + M_PI / 2.));
607
608 veccopy(rtemp2, rpixel2);
609 normalize(rtemp2);
610 vecmul(rtemp2, sin(tiltazimuth + M_PI / 2.));
611
612 sumvec(rtemp, rtemp2); /* tiltaxis (rotation axis) now stored in rtemp */
613 rotation_arb(tilt, rtemp, mtemp); /* rotation matrix now in mtemp */
614
615 /* rotate detector pixel directions */
616 veccopy(rtemp, rpixel1);
617 matvec(mtemp, rtemp, rpixel1);
618 veccopy(rtemp, rpixel2);
619 matvec(mtemp, rtemp, rpixel2);
620
621 return 0;
622 }
623
624 /***********************************************
625 * QConversion functions for point detector *
626 ***********************************************/
627
628 PyObject* py_ang2q_conversion(PyObject *self, PyObject *args)
629 /* conversion of Npoints of goniometer positions to reciprocal space
630 * for a setup with point detector. This is the python wrapper function
631 * which should be called by the user. It offers one common interface to
632 * the outside although internally several performance optimized variants
633 * are called.
634 *
635 * Parameters
636 * ----------
637 * sampleAngles .. angular positions of the sample goniometer
638 * (Npoints, Ns)
639 * detectorAngles. angular positions of the detector goniometer
640 * (Npoints, Nd)
641 * ri ............ direction of primary beam (length of detector distance)
642 * (angles zero)
643 * sampleAxis .... string with sample axis directions
644 * detectorAxis .. string with detector axis directions
645 * kappadir ...... rotation axis of a possible kappa circle
646 * UB ............ orientation matrix and reciprocal space
647 * conversion of the investigated crystal (3, 3)
648 * sampledis ..... sample displacement vector in relative units of
649 * the detector distance
650 * lambda ........ wavelength of the used x-rays as array (Npoints,)
651 * in units of Angstreom
652 * nthreads ...... number of threads to use in parallel section of
653 * the code
654 * flags ......... integer with flags: (1: has_translations;
655 * 4: has_sampledis;
656 * 16: verbose)
657 *
658 * Returns
659 * -------
660 * qpos .......... momentum transfer (Npoints, 3)
661 *
662 * */
663 {
664 int Ns, Nd; /* number of sample and detector circles */
665 int Npoints; /* number of angular positions */
666 int r; /* for return value checking */
667 unsigned int nthreads; /* number of threads to use */
668 char *sampleAxis, *detectorAxis; /* str with sample and detector axis */
669 double *sampleAngles,*detectorAngles, *ri, *kappadir, *sampledis,
670 *UB, *qpos, *lambda; /* c-arrays for further usage */
671 int flags;
672 npy_intp nout[2];
673
674 /* numpy arrays */
675 PyArrayObject *sampleAnglesArr = NULL, *detectorAnglesArr = NULL,
676 *riArr = NULL, *kappadirArr = NULL, *sampledisArr = NULL,
677 *UBArr = NULL, *qposArr = NULL, *lambdaArr = NULL;
678
679 /* Python argument conversion code */
680 if (!PyArg_ParseTuple(args, "O!O!O!ssO!O!O!O!Ii",
681 &PyArray_Type, &sampleAnglesArr,
682 &PyArray_Type, &detectorAnglesArr,
683 &PyArray_Type, &riArr,
684 &sampleAxis, &detectorAxis,
685 &PyArray_Type, &kappadirArr,
686 &PyArray_Type, &UBArr,
687 &PyArray_Type, &sampledisArr,
688 &PyArray_Type, &lambdaArr, &nthreads, &flags)) {
689 return NULL;
690 }
691
692 /* check Python array dimensions and types */
693 PYARRAY_CHECK(sampleAnglesArr, 2, NPY_DOUBLE,
694 "sampleAngles must be a 2D double array");
695 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
696 "detectorAngles must be a 2D double array");
697 PYARRAY_CHECK(lambdaArr, 1, NPY_DOUBLE,
698 "wavelength must be a 1D double array");
699 PYARRAY_CHECK(riArr, 1, NPY_DOUBLE,
700 "r_i must be a 1D double array");
701 if (PyArray_SIZE(riArr) != 3) {
702 PyErr_SetString(PyExc_ValueError, "r_i needs to be of length 3");
703 return NULL;
704 }
705 PYARRAY_CHECK(sampledisArr, 1, NPY_DOUBLE,
706 "sampledis must be a 1D double array");
707 if (PyArray_SIZE(sampledisArr) != 3) {
708 PyErr_SetString(PyExc_ValueError,"sampledis needs to be of length 3");
709 return NULL;
710 }
711 PYARRAY_CHECK(kappadirArr, 1, NPY_DOUBLE,
712 "kappa_dir must be a 1D double array");
713 if (PyArray_SIZE(kappadirArr) != 3) {
714 PyErr_SetString(PyExc_ValueError, "kappa_dir needs to be of length 3");
715 return NULL;
716 }
717 PYARRAY_CHECK(UBArr, 2, NPY_DOUBLE, "UB must be a 2D double array");
718 if (PyArray_DIMS(UBArr)[0] != 3 || PyArray_DIMS(UBArr)[1] != 3) {
719 PyErr_SetString(PyExc_ValueError, "UB must be of shape (3, 3)");
720 return NULL;
721 }
722
723 Npoints = (int) PyArray_DIMS(sampleAnglesArr)[0];
724 Ns = (int) PyArray_DIMS(sampleAnglesArr)[1];
725 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
726 if (PyArray_DIMS(detectorAnglesArr)[0] != Npoints) {
727 PyErr_SetString(PyExc_ValueError,
728 "detectorAngles and sampleAngles must have same first dimension");
729 return NULL;
730 }
731 if (PyArray_SIZE(lambdaArr) != Npoints) {
732 PyErr_SetString(PyExc_ValueError,
733 "size of wavelength array need to fit with angle arrays");
734 return NULL;
735 }
736
737 sampleAngles = (double *) PyArray_DATA(sampleAnglesArr);
738 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
739 lambda = (double *) PyArray_DATA(lambdaArr);
740 ri = (double *) PyArray_DATA(riArr);
741 sampledis = (double *) PyArray_DATA(sampledisArr);
742 kappadir = (double *) PyArray_DATA(kappadirArr);
743 UB = (double *) PyArray_DATA(UBArr);
744
745 /* create output ndarray */
746 nout[0] = Npoints;
747 nout[1] = 3;
748 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
749 qpos = (double *) PyArray_DATA(qposArr);
750
751 #ifdef __OPENMP__
752 /* set openmp thread numbers dynamically */
753 OMPSETNUMTHREADS(nthreads);
754 #endif
755
756 /* call worker function */
757 if (flags & HAS_SAMPLEDIS) {
758 if (flags & HAS_TRANSLATIONS) {
759 r = ang2q_conversion_sdtrans(
760 sampleAngles, detectorAngles, ri,
761 sampleAxis, detectorAxis, kappadir, UB,
762 sampledis, lambda, Npoints, Ns, Nd, flags, qpos);
763 }
764 else {
765 r = ang2q_conversion_sd(
766 sampleAngles, detectorAngles, ri,
767 sampleAxis, detectorAxis, kappadir, UB,
768 sampledis, lambda, Npoints, Ns, Nd, flags, qpos);
769 }
770 }
771 else {
772 if (flags & HAS_TRANSLATIONS) {
773 r = ang2q_conversion_trans(
774 sampleAngles, detectorAngles, ri,
775 sampleAxis, detectorAxis, kappadir, UB,
776 lambda, Npoints, Ns, Nd, flags, qpos);
777 }
778 else {
779 r = ang2q_conversion(
780 sampleAngles, detectorAngles, ri,
781 sampleAxis, detectorAxis, kappadir, UB, lambda,
782 Npoints, Ns, Nd, flags, qpos);
783 }
784 }
785
786 /* clean up */
787 Py_DECREF(sampleAnglesArr);
788 Py_DECREF(detectorAnglesArr);
789 Py_DECREF(riArr);
790 Py_DECREF(kappadirArr);
791 Py_DECREF(UBArr);
792 Py_DECREF(sampledisArr);
793 Py_DECREF(lambdaArr);
794 if (r != 0) {
795 return NULL;
796 }
797
798 /* return output array */
799 return PyArray_Return(qposArr);
800 }
801
802
803 int ang2q_conversion(double *sampleAngles, double *detectorAngles,
804 double *ri, char *sampleAxis, char *detectorAxis,
805 double *kappadir, double *UB, double *lambda,
806 int Npoints, int Ns, int Nd, int flags,
807 double *qpos)
808 /* conversion of Npoints of goniometer positions to reciprocal space
809 * for a setup with point detector
810 *
811 * Parameters
812 * ----------
813 * sampleAngles .. angular positions of the sample goniometer
814 * (Npoints, Ns)
815 * detectorAngles. angular positions of the detector goniometer
816 * (Npoints, Nd)
817 * ri ............ direction of primary beam (length irrelevant)
818 * (angles zero)
819 * sampleAxis .... string with sample axis directions
820 * detectorAxis .. string with detector axis directions
821 * kappadir ...... rotation axis of a possible kappa circle
822 * UB ............ orientation matrix and reciprocal space conversion of
823 * investigated crystal (3, 3)
824 * lambda ........ wavelength of the used x-rays as array (Npoints,)
825 * in units of Angstreom
826 * Npoints ....... number of points to calculate
827 * Ns ............ number of sample axes
828 * Nd ............ number of detector axes
829 * flags ......... general flags integer (verbosity)
830 * qpos .......... momentum transfer (Npoints, 3) (OUTPUT array)
831 *
832 * */
833 {
834 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
835 double local_ri[3], ki[3]; /* copy of primary beam direction */
836 int i, j; /* needed indices */
837 /* arrays with function pointers to rotation matrix functions */
838 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
839 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
840
841 /* determine axes directions */
842 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
843 return -1;
844 }
845 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
846 return -1;
847 }
848
849 /* give ri correct length */
850 veccopy(local_ri, ri);
851 normalize(local_ri);
852
853 /* calculate rotation matices and perform rotations */
854 #pragma omp parallel for default(shared) \
855 private(i, j, ki, mtemp, mtemp2, ms, md) \
856 schedule(static)
857 for (i = 0; i < Npoints; ++i) {
858 /* determine sample rotations */
859 ident(mtemp);
860 for (j = 0; j < Ns; ++j) {
861 /* load kappa direction into matrix
862 * (just needed for kappa goniometer) */
863 mtemp2[0] = kappadir[0];
864 mtemp2[1] = kappadir[1];
865 mtemp2[2] = kappadir[2];
866 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
867 matmul(mtemp, mtemp2);
868 }
869 /* apply rotation of orientation matrix */
870 matmul(mtemp, UB);
871 /* determine inverse matrix */
872 inversemat(mtemp, ms);
873
874 /* determine detector rotations */
875 ident(md);
876 for (j = 0; j < Nd; ++j) {
877 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
878 matmul(md, mtemp);
879 }
880 ident(mtemp);
881 diffmat(md, mtemp);
882
883 matmul(ms, md);
884 /* ms contains now the rotation matrix to determine
885 * the momentum transfer.
886 * calculate the momentum transfer */
887 veccopy(ki, local_ri); /* ki is now normalized ri */
888 vecmul(ki, M_2PI / lambda[i]); /* scales k_i */
889 matvec(ms, ki, &qpos[3 * i]);
890 }
891 return 0;
892 }
893
894
895 int ang2q_conversion_sd(
896 double *sampleAngles, double *detectorAngles, double *ri,
897 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
898 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
899 int flags, double *qpos)
900 /* conversion of Npoints of goniometer positions to reciprocal space
901 * for a setup with point detector including the effect of a sample
902 * displacement error.
903 *
904 * Parameters
905 * ----------
906 * sampleAngles .. angular positions of the sample goniometer
907 * (Npoints, Ns)
908 * detectorAngles. angular positions of the detector goniometer
909 * (Npoints, Nd)
910 * ri ............ direction of primary beam (length of detector distance)
911 * (angles zero)
912 * sampleAxis .... string with sample axis directions
913 * detectorAxis .. string with detector axis directions
914 * kappadir ...... rotation axis of a possible kappa circle
915 * UB ............ orientation matrix and reciprocal space
916 * conversion of the investigated crystal (3, 3)
917 * sampledis ..... sample displacement vector in relative units of
918 * the detector distance
919 * lambda ........ wavelength of the used x-rays as array (Npoints,)
920 * in units of Angstreom
921 * Npoints ....... number of points to calculate
922 * Ns ............ number of sample axes
923 * Nd ............ number of detector axes
924 * flags ......... general flags integer (verbosity)
925 * qpos .......... momentum transfer (Npoints, 3) (OUTPUT array)
926 *
927 * */
928 {
929 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
930 double local_ri[3]; /* copy of primary beam direction */
931 int i, j; /* needed indices */
932 /* arrays with function pointers to rotation matrix functions */
933 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
934 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
935
936 /* determine axes directions */
937 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
938 return -1;
939 }
940 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
941 return -1;
942 }
943
944 /* give ri correct length */
945 veccopy(local_ri, ri);
946 normalize(local_ri);
947
948 /* calculate rotation matices and perform rotations */
949 #pragma omp parallel for default(shared) \
950 private(i, j, mtemp, mtemp2, ms, md) \
951 schedule(static)
952 for (i = 0; i < Npoints; ++i) {
953 /* determine sample rotations */
954 ident(mtemp);
955 for (j = 0; j < Ns; ++j) {
956 /* load kappa direction into matrix
957 * (just needed for kappa goniometer) */
958 mtemp2[0] = kappadir[0];
959 mtemp2[1] = kappadir[1];
960 mtemp2[2] = kappadir[2];
961 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
962 matmul(mtemp, mtemp2);
963 }
964 /* apply rotation of orientation matrix */
965 matmul(mtemp, UB);
966 /* determine inverse matrix */
967 inversemat(mtemp, ms);
968
969 /* determine detector rotations */
970 ident(md);
971 for (j = 0; j < Nd; ++j) {
972 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
973 matmul(md, mtemp);
974 }
975
976 /* consider sample displacement in kf
977 * kf = |k| * (\mat D . \hat ri - \vec rs)/||...|| */
978 matvec(md, ri, mtemp);
979 diffvec(mtemp, sampledis);
980 normalize(mtemp);
981 diffvec(mtemp, local_ri); /* ki/|k| - kf/|k| */
982 vecmul(mtemp, M_2PI / lambda[i]); /* defines k_f */
983 /* mtemp now contains the momentum transfer which will be
984 * transformed to the sample q-coordinate system.
985 * calculate the momentum transfer */
986 matvec(ms, mtemp, &qpos[3 * i]);
987 }
988 return 0;
989 }
990
991
992 int ang2q_conversion_trans(
993 double *sampleAngles, double *detectorAngles, double *ri,
994 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
995 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos)
996 /* conversion of Npoints of goniometer positions to reciprocal space
997 * for a setup with point detector and detector translations
998 *
999 * Parameters
1000 * ----------
1001 * sampleAngles .. angular positions of the sample goniometer
1002 * (Npoints, Ns)
1003 * detectorAngles. angular positions of the detector goniometer
1004 * (Npoints, Nd)
1005 * ri ............ direction of primary beam (length irrelevant)
1006 * (angles zero)
1007 * sampleAxis .... string with sample axis directions
1008 * detectorAxis .. string with detector axis directions
1009 * kappadir ...... rotation axis of a possible kappa circle
1010 * UB ............ orientation matrix and reciprocal space conversion of
1011 * investigated crystal (3, 3)
1012 * lambda ........ wavelength of the used x-rays as array (Npoints,)
1013 * in units of Angstreom
1014 * Npoints ....... number of points to calculate
1015 * Ns ............ number of sample axes
1016 * Nd ............ number of detector axes
1017 * flags ......... general flags integer (verbosity)
1018 * qpos .......... momentum transfer (Npoints, 3) (OUTPUT array)
1019 *
1020 * */
1021 {
1022 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
1023 double local_ri[3], rd[3]; /* copy of primary beam direction */
1024 int i, j; /* needed indices */
1025 /* arrays with function pointers to rotation matrix functions */
1026 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1027 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1028
1029 /* determine axes directions */
1030 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1031 return -1;
1032 }
1033 if (determine_axes_directions_apply(detectorRotation,
1034 detectorAxis, Nd) != 0) {
1035 return -1;
1036 }
1037
1038 /* give ri correct length */
1039 veccopy(local_ri, ri);
1040 normalize(local_ri);
1041
1042 /* calculate rotation matices and perform rotations */
1043 #pragma omp parallel for default(shared) \
1044 private(i, j, mtemp, mtemp2, ms, rd) \
1045 schedule(static)
1046 for (i = 0; i < Npoints; ++i) {
1047 /* determine sample rotations */
1048 ident(mtemp);
1049 for (j = 0; j < Ns; ++j) {
1050 /* load kappa direction into matrix
1051 * (just needed for kappa goniometer) */
1052 mtemp2[0] = kappadir[0];
1053 mtemp2[1] = kappadir[1];
1054 mtemp2[2] = kappadir[2];
1055 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1056 matmul(mtemp, mtemp2);
1057 }
1058 /* apply rotation of orientation matrix */
1059 matmul(mtemp, UB);
1060 /* determine inverse matrix */
1061 inversemat(mtemp, ms);
1062
1063 /* determine detector rotations */
1064 veccopy(rd, ri);
1065 for (j = Nd - 1; j >= 0; --j) {
1066 detectorRotation[j](detectorAngles[Nd * i + j], rd);
1067 }
1068 normalize(rd);
1069 diffvec(rd, local_ri);
1070
1071 /* ms contains now the rotation matrix to determine
1072 * the momentum transfer.
1073 * calculate the momentum transfer */
1074 vecmul(rd, M_2PI / lambda[i]); /* scales by k */
1075 matvec(ms, rd, &qpos[3 * i]);
1076 }
1077 return 0;
1078 }
1079
1080
1081 int ang2q_conversion_sdtrans(
1082 double *sampleAngles, double *detectorAngles, double *ri,
1083 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
1084 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
1085 int flags, double *qpos)
1086 /* conversion of Npoints of goniometer positions to reciprocal space
1087 * for a setup with point detector including the effect of a sample
1088 * displacement error and detector translations
1089 *
1090 * Parameters
1091 * ----------
1092 * sampleAngles .. angular positions of the sample goniometer
1093 * (Npoints, Ns)
1094 * detectorAngles. angular positions of the detector goniometer
1095 * (Npoints, Nd)
1096 * ri ............ direction of primary beam (length of detector distance)
1097 * (angles zero)
1098 * sampleAxis .... string with sample axis directions
1099 * detectorAxis .. string with detector axis directions
1100 * kappadir ...... rotation axis of a possible kappa circle
1101 * UB ............ orientation matrix and reciprocal space
1102 * conversion of the investigated crystal (3, 3)
1103 * sampledis ..... sample displacement vector in relative units of
1104 * the detector distance
1105 * lambda ........ wavelength of the used x-rays as array (Npoints,)
1106 * in units of Angstreom
1107 * Npoints ....... number of points to calculate
1108 * Ns ............ number of sample axes
1109 * Nd ............ number of detector axes
1110 * flags ......... general flags integer (verbosity)
1111 * qpos .......... momentum transfer (Npoints, 3) (OUTPUT array)
1112 *
1113 * */
1114 {
1115 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
1116 double local_ri[3], rd[3]; /* copy of primary beam direction */
1117 int i, j; /* needed indices */
1118 /* arrays with function pointers to rotation matrix functions */
1119 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1120 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1121
1122 /* determine axes directions */
1123 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1124 return -1;
1125 }
1126 if (determine_axes_directions_apply(detectorRotation,
1127 detectorAxis, Nd) != 0) {
1128 return -1;
1129 }
1130
1131 /* give ri correct length */
1132 veccopy(local_ri, ri);
1133 normalize(local_ri);
1134
1135 /* calculate rotation matices and perform rotations */
1136 #pragma omp parallel for default(shared) \
1137 private(i, j, mtemp, mtemp2, ms, rd) \
1138 schedule(static)
1139 for (i = 0; i < Npoints; ++i) {
1140 /* determine sample rotations */
1141 ident(mtemp);
1142 for (j = 0; j < Ns; ++j) {
1143 /* load kappa direction into matrix
1144 * (just needed for kappa goniometer) */
1145 mtemp2[0] = kappadir[0];
1146 mtemp2[1] = kappadir[1];
1147 mtemp2[2] = kappadir[2];
1148 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1149 matmul(mtemp, mtemp2);
1150 }
1151 /* apply rotation of orientation matrix */
1152 matmul(mtemp, UB);
1153 /* determine inverse matrix */
1154 inversemat(mtemp, ms);
1155
1156 /* determine detector rotations */
1157 for (j = Nd - 1; j >= 0; --j) {
1158 detectorRotation[j](detectorAngles[Nd * i + j], rd);
1159 }
1160 /* consider sample displacement in kf */
1161 diffvec(rd, sampledis);
1162 normalize(rd);
1163
1164 diffvec(rd, local_ri); /* ki/|k| - kf/|k| */
1165 vecmul(rd, M_2PI / lambda[i]); /* defines k_f */
1166 /* rd now contains the momentum transfer which will be
1167 * transformed to the sample q-coordinate system.
1168 * calculate the momentum transfer */
1169 matvec(ms, rd, &qpos[3 * i]);
1170 }
1171 return 0;
1172 }
1173
1174
1175 /***********************************************
1176 * QConversion functions for linear detector *
1177 ***********************************************/
1178
1179 PyObject* py_ang2q_conversion_linear(PyObject *self, PyObject *args)
1180 /* conversion of Npoints of goniometer positions to reciprocal space
1181 * for a linear detector with a given pixel size mounted along one of the
1182 * coordinate axis. This is the python wrapper function which should be
1183 * called by the user. It offers one common interface to the outside
1184 * although internally several performance optimized variants are called.
1185 *
1186 * Parameters
1187 * ----------
1188 * sampleAngles .... angular positions of the goniometer (Npoints, Ns)
1189 * detectorAngles .. angular positions of the detector goniometer
1190 * (Npoints, Nd)
1191 * rcch ............ direction + distance of center channel (angles zero)
1192 * sampleAxis ...... string with sample axis directions
1193 * detectorAxis .... string with detector axis directions
1194 * kappadir ........ rotation axis of a possible kappa circle
1195 * cch ............. center channel of the detector
1196 * dpixel .......... width of one pixel, same unit as distance rcch
1197 * roi ............. region of interest of the detector
1198 * dir ............. direction of the detector, e.g.: "x+"
1199 * tilt ............ tilt of the detector direction from dir
1200 * UB .............. orientation matrix and reciprocal space conversion
1201 * of investigated crystal (3, 3)
1202 * sampledis ....... sample displacement vector, same units as the
1203 * detector distance
1204 * lambda .......... wavelength of the used x-rays in Angstroem (Npoints,)
1205 * nthreads ........ number of threads to use in parallel section of
1206 * the code
1207 * flags ........... integer with flags: (1: has_translations;
1208 * 4: has_sampledis;
1209 * 16: verbose)
1210 *
1211 * Returns
1212 * -------
1213 * qpos ............ momentum transfer (Npoints * Nch, 3)
1214 * */
1215 {
1216 int Ns, Nd; /* number of sample and detector circles */
1217 int Npoints; /* number of angular positions */
1218 int Nch; /* number of channels in region of interest */
1219 int r; /* return value checking */
1220 int flags; /* flags to select behavior of the function */
1221 unsigned int nthreads; /* number of threads to use */
1222 double cch, dpixel, tilt; /* wavelength and detector parameters */
1223 char *sampleAxis, *detectorAxis, *dir; /* string with sample and
1224 * detector axis, and
1225 * detector direction */
1226 double *sampleAngles, *detectorAngles, *rcch, *kappadir, *sampledis,
1227 *UB, *qpos, *lambda; /* c-arrays for further usage */
1228 int *roi; /* region of interest integer array */
1229 npy_intp nout[2];
1230
1231 PyArrayObject *sampleAnglesArr = NULL, *detectorAnglesArr = NULL,
1232 *rcchArr = NULL, *kappadirArr = NULL, *roiArr = NULL,
1233 *sampledisArr = NULL, *UBArr = NULL, *qposArr = NULL,
1234 *lambdaArr = NULL;
1235
1236 /* Python argument conversion code */
1237 if (!PyArg_ParseTuple(args, "O!O!O!ssO!ddO!sdO!O!O!Ii",
1238 &PyArray_Type, &sampleAnglesArr,
1239 &PyArray_Type, &detectorAnglesArr,
1240 &PyArray_Type, &rcchArr,
1241 &sampleAxis, &detectorAxis,
1242 &PyArray_Type, &kappadirArr,
1243 &cch, &dpixel, &PyArray_Type, &roiArr,
1244 &dir, &tilt,
1245 &PyArray_Type, &UBArr,
1246 &PyArray_Type, &sampledisArr,
1247 &PyArray_Type, &lambdaArr, &nthreads, &flags)) {
1248 return NULL;
1249 }
1250
1251 /* check Python array dimensions and types */
1252 PYARRAY_CHECK(sampleAnglesArr, 2, NPY_DOUBLE,
1253 "sampleAngles must be a 2D double array");
1254 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
1255 "detectorAngles must be a 2D double array");
1256 PYARRAY_CHECK(lambdaArr, 1, NPY_DOUBLE,
1257 "wavelength must be a 1D double array");
1258 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE,
1259 "rcch must be a 1D double array");
1260 if (PyArray_SIZE(rcchArr) != 3) {
1261 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
1262 return NULL;
1263 }
1264
1265 PYARRAY_CHECK(sampledisArr, 1, NPY_DOUBLE,
1266 "sampledis must be a 1D double array");
1267 if (PyArray_SIZE(sampledisArr) != 3) {
1268 PyErr_SetString(PyExc_ValueError, "sampledis needs to be of length 3");
1269 return NULL;
1270 }
1271
1272 PYARRAY_CHECK(kappadirArr, 1, NPY_DOUBLE,
1273 "kappa_dir must be a 1D double array");
1274 if (PyArray_SIZE(kappadirArr) != 3) {
1275 PyErr_SetString(PyExc_ValueError, "kappa_dir needs to be of length 3");
1276 return NULL;
1277 }
1278 PYARRAY_CHECK(UBArr, 2, NPY_DOUBLE, "UB must be a 2D double array");
1279 if (PyArray_DIMS(UBArr)[0] != 3 || PyArray_DIMS(UBArr)[1] != 3) {
1280 PyErr_SetString(PyExc_ValueError, "UB must be of shape (3, 3)");
1281 return NULL;
1282 }
1283 PYARRAY_CHECK(roiArr, 1, NPY_INT32, "roi must be a 1D int array");
1284 if (PyArray_SIZE(roiArr) != 2) {
1285 PyErr_SetString(PyExc_ValueError, "roi must be of length 2");
1286 return NULL;
1287 }
1288
1289 Npoints = (int) PyArray_DIMS(sampleAnglesArr)[0];
1290 Ns = (int) PyArray_DIMS(sampleAnglesArr)[1];
1291 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
1292 if (PyArray_DIMS(detectorAnglesArr)[0] != Npoints) {
1293 PyErr_SetString(PyExc_ValueError,
1294 "detectorAngles and sampleAngles must have same first dimension");
1295 return NULL;
1296 }
1297 if (PyArray_SIZE(lambdaArr) != Npoints) {
1298 PyErr_SetString(PyExc_ValueError,
1299 "size of wavelength array need to fit with angle arrays");
1300 return NULL;
1301 }
1302
1303 sampleAngles = (double *) PyArray_DATA(sampleAnglesArr);
1304 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
1305 lambda = (double *) PyArray_DATA(lambdaArr);
1306 rcch = (double *) PyArray_DATA(rcchArr);
1307 kappadir = (double *) PyArray_DATA(kappadirArr);
1308 UB = (double *) PyArray_DATA(UBArr);
1309 sampledis = (double *) PyArray_DATA(sampledisArr);
1310 roi = (int *) PyArray_DATA(roiArr);
1311
1312 /* derived values from input parameters */
1313 Nch = roi[1] - roi[0]; /* number of channels */
1314
1315 /* create output ndarray */
1316 nout[0] = Npoints * Nch;
1317 nout[1] = 3;
1318 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
1319 qpos = (double *) PyArray_DATA(qposArr);
1320
1321 #ifdef __OPENMP__
1322 /* set openmp thread numbers dynamically */
1323 OMPSETNUMTHREADS(nthreads);
1324 #endif
1325
1326 /* call worker function */
1327 if (flags & HAS_SAMPLEDIS) {
1328 if (flags & HAS_TRANSLATIONS) {
1329 r = ang2q_conversion_linear_sdtrans(
1330 sampleAngles, detectorAngles, rcch, sampleAxis,
1331 detectorAxis, kappadir, cch, dpixel, roi, dir, tilt,
1332 UB, sampledis, lambda, Npoints, Ns, Nd, Nch, flags, qpos);
1333 }
1334 else {
1335 r = ang2q_conversion_linear_sd(
1336 sampleAngles, detectorAngles, rcch, sampleAxis,
1337 detectorAxis, kappadir, cch, dpixel, roi, dir, tilt,
1338 UB, sampledis, lambda, Npoints, Ns, Nd, Nch, flags, qpos);
1339 }
1340 }
1341 else {
1342 if (flags & HAS_TRANSLATIONS) {
1343 r = ang2q_conversion_linear_trans(
1344 sampleAngles, detectorAngles, rcch, sampleAxis,
1345 detectorAxis, kappadir, cch, dpixel, roi, dir, tilt,
1346 UB, lambda, Npoints, Ns, Nd, Nch, flags, qpos);
1347 }
1348 else {
1349 r = ang2q_conversion_linear(
1350 sampleAngles, detectorAngles, rcch, sampleAxis,
1351 detectorAxis, kappadir, cch, dpixel, roi, dir, tilt,
1352 UB, lambda, Npoints, Ns, Nd, Nch, flags, qpos);
1353 }
1354 }
1355
1356 /* clean up */
1357 Py_DECREF(sampleAnglesArr);
1358 Py_DECREF(detectorAnglesArr);
1359 Py_DECREF(rcchArr);
1360 Py_DECREF(kappadirArr);
1361 Py_DECREF(roiArr);
1362 Py_DECREF(UBArr);
1363 Py_DECREF(sampledisArr);
1364 Py_DECREF(lambdaArr);
1365 if (r != 0) {
1366 return NULL;
1367 }
1368
1369 /* return output array */
1370 return PyArray_Return(qposArr);
1371 }
1372
1373
1374 int ang2q_conversion_linear(
1375 double *sampleAngles, double *detectorAngles, double *rcch,
1376 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
1377 double dpixel, int *roi, char *dir, double tilt, double *UB,
1378 double *lambda, int Npoints, int Ns, int Nd, int Nch,
1379 int flags, double *qpos)
1380 /* conversion of Npoints of goniometer positions to reciprocal space
1381 * for a linear detector with a given pixel size mounted along one of the
1382 * coordinate axis. This is the python wrapper function which should be
1383 * called by the user. It offers one common interface to the outside
1384 * although internally several performance optimized variants are called.
1385 *
1386 * Parameters
1387 * ----------
1388 * sampleAngles .... angular positions of the goniometer (Npoints, Ns)
1389 * detectorAngles .. angular positions of the detector goniometer
1390 * (Npoints, Nd)
1391 * rcch ............ direction + distance of center channel (angles zero)
1392 * sampleAxis ...... string with sample axis directions
1393 * detectorAxis .... string with detector axis directions
1394 * kappadir ........ rotation axis of a possible kappa circle
1395 * cch ............. center channel of the detector
1396 * dpixel .......... width of one pixel, same unit as distance rcch
1397 * roi ............. region of interest of the detector
1398 * dir ............. direction of the detector, e.g.: "x+"
1399 * tilt ............ tilt of the detector direction from dir
1400 * UB .............. orientation matrix and reciprocal space conversion
1401 * of investigated crystal (3, 3)
1402 * lambda .......... wavelength of the used x-rays in Angstroem (Npoints,)
1403 * Npoints ......... number of points to calculate
1404 * Ns .............. number of sample axes
1405 * Nd .............. number of detector axes
1406 * Nch ............. number of channels
1407 * flags ........... general flags integer (verbosity)
1408 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
1409 *
1410 * */
1411 {
1412 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
1413 double rd[3], rpixel[3], rcchp[3]; /* detector position */
1414 double r_i[3], rtemp[3]; /* center channel direction */
1415 int i, j, k; /* needed indices */
1416 double f; /* f = M_2PI / lambda */
1417 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1418 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1419
1420 /* determine axes directions */
1421 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1422 return -1;
1423 }
1424 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
1425 return -1;
1426 }
1427
1428 veccopy(r_i, rcch);
1429 normalize(r_i);
1430 /* determine detector pixel vector */
1431 if (determine_detector_pixel(rpixel, dir, dpixel, r_i, tilt) != 0) {
1432 return -1;
1433 }
1434 for (k = 0; k < 3; ++k) {
1435 rcchp[k] = rpixel[k] * cch;
1436 }
1437
1438 /* calculate rotation matices and perform rotations */
1439 #pragma omp parallel for default(shared) \
1440 private(i, j, k, f, mtemp, mtemp2, ms, md, rd, rtemp) \
1441 schedule(static)
1442 for (i = 0; i < Npoints; ++i) {
1443 /* length of k */
1444 f = M_2PI / lambda[i];
1445 /* determine sample rotations */
1446 ident(mtemp);
1447 for (j = 0; j < Ns; ++j) {
1448 /* load kappa direction into matrix
1449 * (just needed for kappa goniometer) */
1450 mtemp2[0] = kappadir[0];
1451 mtemp2[1] = kappadir[1];
1452 mtemp2[2] = kappadir[2];
1453 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1454 matmul(mtemp, mtemp2);
1455 }
1456 /* apply rotation of orientation matrix */
1457 matmul(mtemp, UB);
1458 /* determine inverse matrix */
1459 inversemat(mtemp, ms);
1460
1461 /* determine detector rotations */
1462 ident(md);
1463 for (j = 0; j < Nd; ++j) {
1464 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
1465 matmul(md, mtemp);
1466 }
1467
1468 /* ms contains now the inverse rotation matrix for the sample circles
1469 * md contains the detector rotation matrix
1470 * calculate the momentum transfer for each detector pixel */
1471 for (j = roi[0]; j < roi[1]; ++j) {
1472 for (k = 0; k < 3; ++k) {
1473 rd[k] = j * rpixel[k] - rcchp[k];
1474 }
1475 sumvec(rd, rcch);
1476 normalize(rd);
1477 /* rd contains detector pixel direction,
1478 * r_i contains primary beam direction */
1479 matvec(md, rd, rtemp);
1480 diffvec(rtemp, r_i);
1481 vecmul(rtemp, f);
1482 /* determine momentum transfer */
1483 matvec(ms, rtemp, &qpos[3 * (i * Nch + j - roi[0])]);
1484 }
1485 }
1486 return 0;
1487 }
1488
1489
1490 int ang2q_conversion_linear_sd(
1491 double *sampleAngles, double *detectorAngles, double *rcch,
1492 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
1493 double dpixel, int *roi, char *dir, double tilt, double *UB,
1494 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
1495 int Nch, int flags, double *qpos)
1496 /* conversion of Npoints of goniometer positions to reciprocal space
1497 * for a linear detector with a given pixel size mounted along one of
1498 * the coordinate axis. this variant also considers the effect of a sample
1499 * displacement.
1500 *
1501 * Parameters
1502 * ----------
1503 * sampleAngles .... angular positions of the goniometer (Npoints, Ns)
1504 * detectorAngles .. angular positions of the detector goniometer
1505 * (Npoints, Nd)
1506 * rcch ............ direction + distance of center channel (angles zero)
1507 * sampleAxis ...... string with sample axis directions
1508 * detectorAxis .... string with detector axis directions
1509 * kappadir ........ rotation axis of a possible kappa circle
1510 * cch ............. center channel of the detector
1511 * dpixel .......... width of one pixel, same unit as distance rcch
1512 * roi ............. region of interest of the detector
1513 * dir ............. direction of the detector, e.g.: "x+"
1514 * tilt ............ tilt of the detector direction from dir
1515 * UB .............. orientation matrix and reciprocal space conversion
1516 * of investigated crystal (3, 3)
1517 * sampledis ....... sample displacement vector, same units as the
1518 * detector distance
1519 * lambda .......... wavelength of the used x-rays in Angstroem (Npoints,)
1520 * Npoints ......... number of points to calculate
1521 * Ns .............. number of sample axes
1522 * Nd .............. number of detector axes
1523 * Nch ............. number of channels
1524 * flags ........... general flags integer (verbosity)
1525 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
1526 *
1527 * */
1528 {
1529 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
1530 double rd[3], rpixel[3], rcchp[3]; /* detector position */
1531 double r_i[3], rtemp[3]; /* center channel direction */
1532 int i, j, k; /* needed indices */
1533 double f; /* wavelength parameters */
1534 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1535 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1536
1537 /* determine axes directions */
1538 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1539 return -1;
1540 }
1541 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
1542 return -1;
1543 }
1544
1545 veccopy(r_i, rcch);
1546 normalize(r_i);
1547 /* determine detector pixel vector */
1548 if (determine_detector_pixel(rpixel, dir, dpixel, r_i, tilt) != 0) {
1549 return -1;
1550 }
1551 for (k = 0; k < 3; ++k) {
1552 rcchp[k] = rpixel[k] * cch;
1553 }
1554
1555 /* calculate rotation matices and perform rotations */
1556 #pragma omp parallel for default(shared) \
1557 private(i, j, k, f, mtemp, mtemp2, ms, md, rd, rtemp) \
1558 schedule(static)
1559 for (i = 0; i < Npoints; ++i) {
1560 /* length of k */
1561 f = M_2PI / lambda[i];
1562 /* determine sample rotations */
1563 ident(mtemp);
1564 for (j = 0; j < Ns; ++j) {
1565 /* load kappa direction into matrix
1566 * (just needed for kappa goniometer) */
1567 mtemp2[0] = kappadir[0];
1568 mtemp2[1] = kappadir[1];
1569 mtemp2[2] = kappadir[2];
1570 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1571 matmul(mtemp, mtemp2);
1572 }
1573 /* apply rotation of orientation matrix */
1574 matmul(mtemp, UB);
1575 /* determine inverse matrix */
1576 inversemat(mtemp, ms);
1577
1578 /* determine detector rotations */
1579 ident(md);
1580 for (j = 0; j < Nd; ++j) {
1581 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
1582 matmul(md, mtemp);
1583 }
1584
1585 /* ms contains now the inverse rotation matrix for the sample circles
1586 * md contains the detector rotation matrix
1587 * calculate the momentum transfer for each detector pixel */
1588 for (j = roi[0]; j < roi[1]; ++j) {
1589 for (k = 0; k < 3; ++k) {
1590 rd[k] = j * rpixel[k] - rcchp[k];
1591 }
1592 sumvec(rd, rcch);
1593 matvec(md, rd, rtemp);
1594 /* consider sample displacement vector */
1595 diffvec(rtemp, sampledis);
1596 normalize(rtemp);
1597 /* continue with normal conversion */
1598 /* rtemp contains detector pixel direction,
1599 * r_i contains primary beam direction */
1600 diffvec(rtemp, r_i);
1601 vecmul(rtemp, f);
1602 /* determine momentum transfer */
1603 matvec(ms, rtemp, &qpos[3 * (i * Nch + j - roi[0])]);
1604 }
1605 }
1606 return 0;
1607 }
1608
1609
1610 int ang2q_conversion_linear_trans(
1611 double *sampleAngles, double *detectorAngles, double *rcch,
1612 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
1613 double dpixel, int *roi, char *dir, double tilt, double *UB,
1614 double *lambda, int Npoints, int Ns, int Nd, int Nch,
1615 int flags, double *qpos)
1616 /* conversion of Npoints of goniometer positions to reciprocal space
1617 * for a linear detector with a given pixel size mounted along one of
1618 * the coordinate axis, and translation motors on the detector arm
1619 *
1620 * Parameters
1621 * ----------
1622 * sampleAngles .... angular positions of the goniometer (Npoints, Ns)
1623 * detectorAngles .. angular positions of the detector goniometer
1624 * (Npoints, Nd)
1625 * rcch ............ direction + distance of center channel (angles zero)
1626 * sampleAxis ...... string with sample axis directions
1627 * detectorAxis .... string with detector axis directions
1628 * kappadir ........ rotation axis of a possible kappa circle
1629 * cch ............. center channel of the detector
1630 * dpixel .......... width of one pixel, same unit as distance rcch
1631 * roi ............. region of interest of the detector
1632 * dir ............. direction of the detector, e.g.: "x+"
1633 * tilt ............ tilt of the detector direction from dir
1634 * UB .............. orientation matrix and reciprocal space conversion
1635 * of investigated crystal (3, 3)
1636 * lambda .......... wavelength of the used x-rays in Angstroem (Npoints,)
1637 * Npoints ......... number of points to calculate
1638 * Ns .............. number of sample axes
1639 * Nd .............. number of detector axes
1640 * Nch ............. number of channels
1641 * flags ........... general flags integer (verbosity)
1642 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array))
1643 *
1644 * */
1645 {
1646 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
1647 double rd[3], rpixel[3], rcchp[3]; /* detector position */
1648 double r_i[3]; /* center channel direction */
1649 int i, j, k; /* needed indices */
1650 double f; /* f = M_2PI / lambda */
1651 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1652 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1653
1654 /* determine axes directions */
1655 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1656 return -1;
1657 }
1658 if (determine_axes_directions_apply(detectorRotation,
1659 detectorAxis, Nd) != 0) {
1660 return -1;
1661 }
1662
1663 veccopy(r_i, rcch);
1664 normalize(r_i);
1665 /* determine detector pixel vector */
1666 if (determine_detector_pixel(rpixel, dir, dpixel, r_i, tilt) != 0) {
1667 return -1;
1668 }
1669 for (k = 0; k < 3; ++k) {
1670 rcchp[k] = rpixel[k] * cch;
1671 }
1672
1673 /* calculate rotation matices and perform rotations */
1674 #pragma omp parallel for default(shared) \
1675 private(i, j, k, f, mtemp, mtemp2, ms, rd) \
1676 schedule(static)
1677 for (i = 0; i < Npoints; ++i) {
1678 /* length of k */
1679 f = M_2PI / lambda[i];
1680 /* determine sample rotations */
1681 ident(mtemp);
1682 for (j = 0; j < Ns; ++j) {
1683 /* load kappa direction into matrix
1684 * (just needed for kappa goniometer) */
1685 mtemp2[0] = kappadir[0];
1686 mtemp2[1] = kappadir[1];
1687 mtemp2[2] = kappadir[2];
1688 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1689 matmul(mtemp, mtemp2);
1690 }
1691 /* apply rotation of orientation matrix */
1692 matmul(mtemp, UB);
1693 /* determine inverse matrix */
1694 inversemat(mtemp, ms);
1695
1696 /* ms contains now the inverse rotation matrix for the sample circles
1697 * calculate the momentum transfer for each detector pixel */
1698 for (j = roi[0]; j < roi[1]; ++j) {
1699 for (k = 0; k < 3; ++k) {
1700 rd[k] = j * rpixel[k] - rcchp[k];
1701 }
1702 sumvec(rd, rcch);
1703 /* determine detector rotations */
1704 for (k = Nd - 1; k >= 0; --k) {
1705 detectorRotation[k](detectorAngles[Nd * i + k], rd);
1706 }
1707
1708 normalize(rd);
1709 /* rd contains detector pixel direction,
1710 * r_i contains primary beam direction */
1711 diffvec(rd, r_i);
1712 vecmul(rd, f);
1713 /* determine momentum transfer */
1714 matvec(ms, rd, &qpos[3 * (i * Nch + j - roi[0])]);
1715 }
1716 }
1717 return 0;
1718 }
1719
1720 int ang2q_conversion_linear_sdtrans(
1721 double *sampleAngles, double *detectorAngles, double *rcch,
1722 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
1723 double dpixel, int *roi, char *dir, double tilt, double *UB,
1724 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
1725 int Nch, int flags, double *qpos)
1726 /* conversion of Npoints of goniometer positions to reciprocal space
1727 * for a linear detector with a given pixel size mounted along one of
1728 * the coordinate axis. this variant also considers the effect of a sample
1729 * displacement and can consider detector translation-axis.
1730 *
1731 * Parameters
1732 * ----------
1733 * sampleAngles .... angular positions of the goniometer (Npoints, Ns)
1734 * detectorAngles .. angular positions of the detector goniometer
1735 * (Npoints, Nd)
1736 * rcch ............ direction + distance of center channel (angles zero)
1737 * sampleAxis ...... string with sample axis directions
1738 * detectorAxis .... string with detector axis directions
1739 * kappadir ........ rotation axis of a possible kappa circle
1740 * cch ............. center channel of the detector
1741 * dpixel .......... width of one pixel, same unit as distance rcch
1742 * roi ............. region of interest of the detector
1743 * dir ............. direction of the detector, e.g.: "x+"
1744 * tilt ............ tilt of the detector direction from dir
1745 * UB .............. orientation matrix and reciprocal space conversion
1746 * of investigated crystal (3, 3)
1747 * sampledis ....... sample displacement vector, same units as the
1748 * detector distance
1749 * lambda .......... wavelength of the used x-rays in Angstroem (Npoints,)
1750 * Npoints ......... number of points to calculate
1751 * Ns .............. number of sample axes
1752 * Nd .............. number of detector axes
1753 * Nch ............. number of channels
1754 * flags ........... general flags integer (verbosity)
1755 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array))
1756 *
1757 * */
1758 {
1759 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
1760 double rd[3], rpixel[3], rcchp[3]; /* detector position */
1761 double r_i[3]; /* center channel direction */
1762 int i, j, k; /* needed indices */
1763 double f; /* wavelength parameter */
1764 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
1765 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
1766
1767 /* determine axes directions */
1768 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
1769 return -1;
1770 }
1771 if (determine_axes_directions_apply(detectorRotation,
1772 detectorAxis, Nd) != 0) {
1773 return -1;
1774 }
1775
1776 veccopy(r_i, rcch);
1777 normalize(r_i);
1778 /* determine detector pixel vector */
1779 if (determine_detector_pixel(rpixel, dir, dpixel, r_i, tilt) != 0) {
1780 return -1;
1781 }
1782 for (k = 0; k < 3; ++k) {
1783 rcchp[k] = rpixel[k] * cch;
1784 }
1785
1786 /* calculate rotation matices and perform rotations */
1787 #pragma omp parallel for default(shared) \
1788 private(i, j, k, f, mtemp, mtemp2, ms, rd) \
1789 schedule(static)
1790 for (i = 0; i < Npoints; ++i) {
1791 /* length of k */
1792 f = M_2PI / lambda[i];
1793 /* determine sample rotations */
1794 ident(mtemp);
1795 for (j = 0; j < Ns; ++j) {
1796 /* load kappa direction into matrix
1797 * (just needed for kappa goniometer) */
1798 mtemp2[0] = kappadir[0];
1799 mtemp2[1] = kappadir[1];
1800 mtemp2[2] = kappadir[2];
1801 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
1802 matmul(mtemp, mtemp2);
1803 }
1804 /* apply rotation of orientation matrix */
1805 matmul(mtemp, UB);
1806 /* determine inverse matrix */
1807 inversemat(mtemp, ms);
1808
1809 /* ms contains now the inverse rotation matrix for the sample circles
1810 * calculate the momentum transfer for each detector pixel */
1811 for (j = roi[0]; j < roi[1]; ++j) {
1812 for (k = 0; k < 3; ++k) {
1813 rd[k] = j * rpixel[k] - rcchp[k];
1814 }
1815 sumvec(rd, rcch);
1816 /* apply detector rotations/translations, starting with the
1817 * inner most */
1818 for (k = Nd - 1; k >= 0; --k) {
1819 detectorRotation[k](detectorAngles[Nd * i + k], rd);
1820 }
1821 /* consider sample displacement vector */
1822 diffvec(rd, sampledis);
1823 normalize(rd);
1824 /* continue with normal conversion */
1825 /* rd contains detector pixel direction,
1826 * r_i contains primary beam direction */
1827 diffvec(rd, r_i);
1828 vecmul(rd, f);
1829 /* determine momentum transfer */
1830 matvec(ms, rd, &qpos[3 * (i * Nch + j - roi[0])]);
1831 }
1832 }
1833 return 0;
1834 }
1835
1836 /***********************************************
1837 * QConversion functions for area detectors *
1838 ***********************************************/
1839
1840 PyObject* py_ang2q_conversion_area(PyObject *self, PyObject *args)
1841 /* conversion of Npoints of goniometer positions to reciprocal space for an
1842 * area detector with a given pixel size mounted along one of the coordinate
1843 * axis. This is the python wrapper function which should be called by the
1844 * user. It offers one common interface to the outside although internally
1845 * several performance optimized variants are called.
1846 *
1847 * Parameters
1848 * ----------
1849 * sampleAngles .... angular positions of the sample goniometer
1850 * (Npoints, Ns)
1851 * detectorAngles .. angular positions of the detector goniometer
1852 * (Npoints, Nd)
1853 * rcch ............ direction + distance of center pixel (angles zero)
1854 * sampleAxis ...... string with sample axis directions
1855 * detectorAxis .... string with detector axis directions
1856 * kappadir ........ rotation axis of a possible kappa circle
1857 * cch1 ............ center channel of the detector
1858 * cch2 ............ center channel of the detector
1859 * dpixel1 ......... width of one pixel in first direction, same unit as
1860 * distance rcch
1861 * dpixel2 ......... width of one pixel in second direction, same unit as
1862 * distance rcch
1863 * roi ............. region of interest for the area detector
1864 * [dir1min, dir1max, dir2min, dir2max]
1865 * dir1 ............ first direction of the detector, e.g.: "x+"
1866 * dir2 ............ second direction of the detector, e.g.: "z+"
1867 * tiltazimuth ..... azimuth of the tilt
1868 * tilt ............ tilt of the detector plane (rotation around axis
1869 * normal to the direction given by the tiltazimuth
1870 * UB .............. orientation matrix and reciprocal space conversion
1871 * of investigated crystal (3, 3)
1872 * sampledis ....... sample displacement vector, same units as the
1873 * detector distance
1874 * lambda .......... wavelength of the used x-rays (Npoints,)
1875 * nthreads ........ number of threads to use in parallelization
1876 * flags ........... integer with flags: (1: has_translations;
1877 * 4: has_sampledis;
1878 * 16: verbose)
1879 *
1880 * Returns
1881 * -------
1882 * qpos ............ momentum transfer (Npoints * Npix1 * Npix2, 3)
1883 * */
1884 {
1885 int Ns, Nd; /* number of sample and detector circles */
1886 int Npoints; /* number of angular positions */
1887 int r; /* return value checking */
1888 int flags; /* flags to select behavior of the function */
1889 unsigned int nthreads; /* number threads for OpenMP */
1890 double cch1, cch2, dpixel1, dpixel2, tilt, tiltazimuth;
1891 /* string with sample and detector axis, and detector direction */
1892 char *sampleAxis, *detectorAxis, *dir1, *dir2;
1893 double *sampleAngles,*detectorAngles, *rcch, *kappadir, *UB, *sampledis,
1894 *qpos, *lambda; /* c-arrays for further usage */
1895 int *roi; /* region of interest integer array */
1896 npy_intp nout[2];
1897 /* numpy arrays */
1898 PyArrayObject *sampleAnglesArr = NULL, *detectorAnglesArr = NULL,
1899 *rcchArr = NULL, *kappadirArr = NULL, *roiArr = NULL,
1900 *sampledisArr = NULL, *UBArr = NULL, *qposArr = NULL,
1901 *lambdaArr = NULL;
1902
1903 /* Python argument conversion code */
1904 if (!PyArg_ParseTuple(args, "O!O!O!ssO!ddddO!ssddO!O!O!Ii",
1905 &PyArray_Type, &sampleAnglesArr,
1906 &PyArray_Type, &detectorAnglesArr,
1907 &PyArray_Type, &rcchArr,
1908 &sampleAxis, &detectorAxis,
1909 &PyArray_Type, &kappadirArr,
1910 &cch1, &cch2, &dpixel1, &dpixel2,
1911 &PyArray_Type, &roiArr,
1912 &dir1, &dir2, &tiltazimuth, &tilt,
1913 &PyArray_Type, &UBArr,
1914 &PyArray_Type, &sampledisArr,
1915 &PyArray_Type, &lambdaArr, &nthreads, &flags)) {
1916 return NULL;
1917 }
1918
1919 /* check Python array dimensions and types */
1920 PYARRAY_CHECK(sampleAnglesArr, 2, NPY_DOUBLE,
1921 "sampleAngles must be a 2D double array");
1922 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
1923 "detectorAngles must be a 2D double array");
1924 PYARRAY_CHECK(lambdaArr, 1, NPY_DOUBLE,
1925 "wavelength must be a 1D double array");
1926 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE, "rcch must be a 1D double array");
1927 if (PyArray_SIZE(rcchArr) != 3) {
1928 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
1929 return NULL;
1930 }
1931 PYARRAY_CHECK(kappadirArr, 1, NPY_DOUBLE,
1932 "kappa_dir must be a 1D double array");
1933 if (PyArray_SIZE(kappadirArr) != 3) {
1934 PyErr_SetString(PyExc_ValueError, "kappa_dir needs to be of length 3");
1935 return NULL;
1936 }
1937 PYARRAY_CHECK(UBArr, 2, NPY_DOUBLE, "UB must be a 2D double array");
1938 if (PyArray_DIMS(UBArr)[0] != 3 || PyArray_DIMS(UBArr)[1] != 3) {
1939 PyErr_SetString(PyExc_ValueError, "UB must be of shape (3, 3)");
1940 return NULL;
1941 }
1942 PYARRAY_CHECK(roiArr, 1, NPY_INT32, "roi must be a 1D int array");
1943 if (PyArray_SIZE(roiArr) != 4) {
1944 PyErr_SetString(PyExc_ValueError, "roi must be of length 4");
1945 return NULL;
1946 }
1947 PYARRAY_CHECK(sampledisArr, 1, NPY_DOUBLE,
1948 "sampledis must be a 1D double array");
1949 if (PyArray_SIZE(sampledisArr) != 3) {
1950 PyErr_SetString(PyExc_ValueError, "sampledis needs to be of length 3");
1951 return NULL;
1952 }
1953
1954 Npoints = (int) PyArray_DIMS(sampleAnglesArr)[0];
1955 Ns = (int) PyArray_DIMS(sampleAnglesArr)[1];
1956 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
1957 if (PyArray_DIMS(detectorAnglesArr)[0] != Npoints) {
1958 PyErr_SetString(PyExc_ValueError,
1959 "detectorAngles and sampleAngles must have same first dimension");
1960 return NULL;
1961 }
1962 if (PyArray_SIZE(lambdaArr) != Npoints) {
1963 PyErr_SetString(PyExc_ValueError,
1964 "size of wavelength array need to fit with angle arrays");
1965 return NULL;
1966 }
1967
1968 sampleAngles = (double *) PyArray_DATA(sampleAnglesArr);
1969 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
1970 lambda = (double *) PyArray_DATA(lambdaArr);
1971 rcch = (double *) PyArray_DATA(rcchArr);
1972 kappadir = (double *) PyArray_DATA(kappadirArr);
1973 UB = (double *) PyArray_DATA(UBArr);
1974 roi = (int *) PyArray_DATA(roiArr);
1975 sampledis = (double *) PyArray_DATA(sampledisArr);
1976
1977 /* create output ndarray */
1978 nout[0] = Npoints * (roi[1] - roi[0]) * (roi[3] - roi[2]);
1979 nout[1] = 3;
1980 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
1981 qpos = (double *) PyArray_DATA(qposArr);
1982
1983 #ifdef __OPENMP__
1984 /* set openmp thread numbers dynamically */
1985 OMPSETNUMTHREADS(nthreads);
1986 #endif
1987
1988 /* call worker function */
1989 if (flags & HAS_SAMPLEDIS) {
1990 if (flags & HAS_TRANSLATIONS) {
1991 r = ang2q_conversion_area_sdtrans(
1992 sampleAngles, detectorAngles, rcch, sampleAxis,
1993 detectorAxis, kappadir, cch1, cch2, dpixel1, dpixel2, roi,
1994 dir1, dir2, tiltazimuth, tilt, UB, sampledis, lambda,
1995 Npoints, Ns, Nd, flags, qpos);
1996 }
1997 else {
1998 r = ang2q_conversion_area_sd(
1999 sampleAngles, detectorAngles, rcch, sampleAxis,
2000 detectorAxis, kappadir, cch1, cch2, dpixel1, dpixel2, roi,
2001 dir1, dir2, tiltazimuth, tilt, UB, sampledis, lambda,
2002 Npoints, Ns, Nd, flags, qpos);
2003 }
2004 }
2005 else {
2006 if (flags & HAS_TRANSLATIONS) {
2007 r = ang2q_conversion_area_trans(
2008 sampleAngles, detectorAngles, rcch, sampleAxis,
2009 detectorAxis, kappadir, cch1, cch2, dpixel1, dpixel2, roi,
2010 dir1, dir2, tiltazimuth, tilt, UB, lambda, Npoints, Ns, Nd,
2011 flags, qpos);
2012 }
2013 else {
2014 r = ang2q_conversion_area(
2015 sampleAngles, detectorAngles, rcch, sampleAxis,
2016 detectorAxis, kappadir, cch1, cch2, dpixel1, dpixel2, roi,
2017 dir1, dir2, tiltazimuth, tilt, UB, lambda, Npoints, Ns, Nd,
2018 flags, qpos);
2019 }
2020 }
2021
2022 /* clean up */
2023 Py_DECREF(sampleAnglesArr);
2024 Py_DECREF(detectorAnglesArr);
2025 Py_DECREF(rcchArr);
2026 Py_DECREF(kappadirArr);
2027 Py_DECREF(roiArr);
2028 Py_DECREF(UBArr);
2029 Py_DECREF(sampledisArr);
2030 Py_DECREF(lambdaArr);
2031 if (r != 0) {
2032 return NULL;
2033 }
2034
2035 /* return output array */
2036 return PyArray_Return(qposArr);
2037 }
2038
2039
2040 int ang2q_conversion_area(
2041 double *sampleAngles, double *detectorAngles, double *rcch,
2042 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
2043 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
2044 char *dir2, double tiltazimuth, double tilt, double *UB,
2045 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos)
2046 /* conversion of Npoints of goniometer positions to reciprocal space
2047 * for an area detector with a given pixel size mounted along one of
2048 * the coordinate axis
2049 *
2050 * Parameters
2051 * ----------
2052 * sampleAngles .... angular positions of the sample goniometer
2053 * (Npoints, Ns)
2054 * detectorAngles .. angular positions of the detector goniometer
2055 * (Npoints, Nd)
2056 * rcch ............ direction + distance of center pixel (angles zero)
2057 * sampleAxis ...... string with sample axis directions
2058 * detectorAxis .... string with detector axis directions
2059 * kappadir ........ rotation axis of a possible kappa circle
2060 * cch1 ............ center channel of the detector
2061 * cch2 ............ center channel of the detector
2062 * dpixel1 ......... width of one pixel in first direction, same unit as
2063 * distance rcch
2064 * dpixel2 ......... width of one pixel in second direction, same unit as
2065 * distance rcch
2066 * roi ............. region of interest for the area detector
2067 * [dir1min, dir1max, dir2min, dir2max]
2068 * dir1 ............ first direction of the detector, e.g.: "x+"
2069 * dir2 ............ second direction of the detector, e.g.: "z+"
2070 * tiltazimuth ..... azimuth of the tilt
2071 * tilt ............ tilt of the detector plane (rotation around axis
2072 * normal to the direction
2073 * given by the tiltazimuth
2074 * UB .............. orientation matrix and reciprocal space conversion
2075 * of the investigated crystal (3, 3)
2076 * lambda .......... wavelength of the used x-rays (Npoints,)
2077 * Npoints ......... number of points to calculate
2078 * Ns .............. number of sample axes
2079 * Nd .............. number of detector axes
2080 * flags ........... general flags integer (verbosity)
2081 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
2082 *
2083 * */
2084 {
2085 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
2086 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2087 double r_i[3], rtemp[3]; /* r_i: center channel direction */
2088 int i, j, j1, j2, k; /* loop indices */
2089 int idxh1, idxh2; /* temporary index helper */
2090 double f; /* f = M_2PI / lambda and detector parameters */
2091 /* string with sample and detector axis, and detector direction */
2092 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
2093 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
2094
2095 /* calculate some index shortcuts */
2096 idxh1 = (roi[1] - roi[0]) * (roi[3] - roi[2]);
2097 idxh2 = roi[3] - roi[2];
2098
2099 /* determine axes directions */
2100 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
2101 return -1;
2102 }
2103 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
2104 return -1;
2105 }
2106
2107 veccopy(r_i, rcch);
2108 normalize(r_i);
2109
2110 /* determine detector pixel vector */
2111 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2112 return -1;
2113 }
2114 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2115 return -1;
2116 }
2117
2118 /* rotate detector pixel vectors according to tilt */
2119 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2120
2121 /* calculate center channel position in detector plane */
2122 for (k = 0; k < 3; ++k) {
2123 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2124 }
2125
2126 /* calculate rotation matices and perform rotations */
2127 #pragma omp parallel for default(shared) \
2128 private(i, j, j1, j2, k, f, mtemp, mtemp2, ms, md, rd, rtemp) \
2129 schedule(static)
2130 for (i = 0; i < Npoints; ++i) {
2131 f = M_2PI / lambda[i];
2132 /* determine sample rotations */
2133 ident(mtemp);
2134 for (j = 0; j < Ns; ++j) {
2135 /* load kappa direction into matrix
2136 * (just needed for kappa goniometer) */
2137 mtemp2[0] = kappadir[0];
2138 mtemp2[1] = kappadir[1];
2139 mtemp2[2] = kappadir[2];
2140 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
2141 matmul(mtemp, mtemp2);
2142 }
2143 /* apply rotation of orientation matrix */
2144 matmul(mtemp, UB);
2145 /* determine inverse matrix */
2146 inversemat(mtemp, ms);
2147
2148 /* determine detector rotations */
2149 ident(md);
2150 for (j = 0; j < Nd; ++j) {
2151 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
2152 matmul(md, mtemp);
2153 }
2154
2155 /* ms contains now the inverse rotation matrix for the sample circles
2156 * md contains the detector rotation matrix
2157 * calculate the momentum transfer for each detector pixel */
2158 for (j1 = roi[0]; j1 < roi[1]; ++j1) {
2159 for (j2 = roi[2]; j2 < roi[3]; ++j2) {
2160 for (k = 0; k < 3; ++k) {
2161 rd[k] = j1 * rpixel1[k] + j2 * rpixel2[k] - rcchp[k];
2162 }
2163 sumvec(rd, rcch);
2164 normalize(rd);
2165 /* rd contains detector pixel direction,
2166 * r_i contains primary beam direction */
2167 matvec(md, rd, rtemp);
2168 diffvec(rtemp, r_i);
2169 vecmul(rtemp, f);
2170 /* determine momentum transfer */
2171 matvec(ms, rtemp,
2172 &qpos[3 * (i * idxh1 + idxh2 * (j1 - roi[0]) +
2173 (j2 - roi[2]))]);
2174 }
2175 }
2176 }
2177 return 0;
2178 }
2179
2180
2181 int ang2q_conversion_area_sd(
2182 double *sampleAngles, double *detectorAngles, double *rcch,
2183 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
2184 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
2185 char *dir2, double tiltazimuth, double tilt, double *UB,
2186 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
2187 int flags, double *qpos)
2188 /* conversion of Npoints of goniometer positions to reciprocal space
2189 * for an area detector with a given pixel size mounted along one of
2190 * the coordinate axis. this variant also considers the effect of a
2191 * sample displacement error.
2192 *
2193 * Parameters
2194 * ----------
2195 * sampleAngles .... angular positions of the sample goniometer
2196 * (Npoints, Ns)
2197 * detectorAngles .. angular positions of the detector goniometer
2198 * (Npoints, Nd)
2199 * rcch ............ direction + distance of center pixel (angles zero)
2200 * sampleAxis ...... string with sample axis directions
2201 * detectorAxis .... string with detector axis directions
2202 * kappadir ........ rotation axis of a possible kappa circle
2203 * cch1 ............ center channel of the detector
2204 * cch2 ............ center channel of the detector
2205 * dpixel1 ......... width of one pixel in first direction, same unit as
2206 * distance rcch
2207 * dpixel2 ......... width of one pixel in second direction, same unit as
2208 * distance rcch
2209 * roi ............. region of interest for the area detector
2210 * [dir1min, dir1max, dir2min, dir2max]
2211 * dir1 ............ first direction of the detector, e.g.: "x+"
2212 * dir2 ............ second direction of the detector, e.g.: "z+"
2213 * tiltazimuth ..... azimuth of the tilt
2214 * tilt ............ tilt of the detector plane (rotation around axis
2215 * normal to the direction given by the tiltazimuth
2216 * UB .............. orientation matrix and reciprocal space conversion
2217 * of investigated crystal (3, 3)
2218 * sampledis ....... sample displacement vector, same units as the
2219 * detector distance
2220 * lambda .......... wavelength of the used x-rays (Npoints,)
2221 * Npoints ......... number of points to calculate
2222 * Ns .............. number of sample axes
2223 * Nd .............. number of detector axes
2224 * flags ........... general flags integer (verbosity)
2225 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
2226 *
2227 * */
2228 {
2229 double mtemp[9], mtemp2[9], ms[9], md[9]; /* matrices */
2230 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2231 double r_i[3], rtemp[3]; /* r_i: center channel direction */
2232 int i, j, j1, j2, k; /* loop indices */
2233 int idxh1, idxh2; /* temporary index helper */
2234 double f; /* f = M_2PI / lambda and detector parameters */
2235 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
2236 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
2237
2238 /* calculate some index shortcuts */
2239 idxh1 = (roi[1] - roi[0]) * (roi[3] - roi[2]);
2240 idxh2 = roi[3] - roi[2];
2241
2242 /* determine axes directions */
2243 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
2244 return -1;
2245 }
2246 if (determine_axes_directions(detectorRotation, detectorAxis, Nd) != 0) {
2247 return -1;
2248 }
2249
2250 veccopy(r_i, rcch);
2251 normalize(r_i);
2252
2253 /* determine detector pixel vector */
2254 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2255 return -1;
2256 }
2257 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2258 return -1;
2259 }
2260
2261 /* rotate detector pixel vectors according to tilt */
2262 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2263
2264 /* calculate center channel position in detector plane */
2265 for (k = 0; k < 3; ++k) {
2266 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2267 }
2268
2269 /* calculate rotation matices and perform rotations */
2270 #pragma omp parallel for default(shared) \
2271 private(i, j, j1, j2, k, f, mtemp, mtemp2, ms, md, rd, rtemp) \
2272 schedule(static)
2273 for (i = 0; i < Npoints; ++i) {
2274 /* length of k */
2275 f = M_2PI / lambda[i];
2276 /* determine sample rotations */
2277 ident(mtemp);
2278 for (j = 0; j < Ns; ++j) {
2279 /* load kappa direction into matrix
2280 * (just needed for kappa goniometer) */
2281 mtemp2[0] = kappadir[0];
2282 mtemp2[1] = kappadir[1];
2283 mtemp2[2] = kappadir[2];
2284 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
2285 matmul(mtemp, mtemp2);
2286 }
2287 /* apply rotation of orientation matrix */
2288 matmul(mtemp, UB);
2289 /* determine inverse matrix */
2290 inversemat(mtemp, ms);
2291
2292 /* determine detector rotations */
2293 ident(md);
2294 for (j = 0; j < Nd; ++j) {
2295 detectorRotation[j](detectorAngles[Nd * i + j], mtemp);
2296 matmul(md, mtemp);
2297 }
2298
2299 /* ms contains now the inverse rotation matrix for the sample circles
2300 * md contains the detector rotation matrix
2301 * calculate the momentum transfer for each detector pixel */
2302 for (j1 = roi[0]; j1 < roi[1]; ++j1) {
2303 for (j2 = roi[2]; j2 < roi[3]; ++j2) {
2304 for (k = 0; k < 3; ++k) {
2305 rd[k] = j1 * rpixel1[k] + j2 * rpixel2[k] - rcchp[k];
2306 }
2307 sumvec(rd, rcch);
2308 matvec(md, rd, rtemp);
2309 /* consider the effect of the sample displacement */
2310 diffvec(rtemp, sampledis);
2311 normalize(rtemp);
2312 /* rd contains detector pixel direction,
2313 * r_i contains primary beam direction */
2314 diffvec(rtemp, r_i);
2315 vecmul(rtemp, f);
2316 /* determine momentum transfer */
2317 matvec(ms, rtemp,
2318 &qpos[3 * (i * idxh1 + idxh2 * (j1 - roi[0]) +
2319 (j2 - roi[2]))]);
2320 }
2321 }
2322 }
2323 return 0;
2324 }
2325
2326
2327 int ang2q_conversion_area_trans(
2328 double *sampleAngles, double *detectorAngles, double *rcch,
2329 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
2330 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
2331 char *dir2, double tiltazimuth, double tilt, double *UB,
2332 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos)
2333 /* conversion of Npoints of goniometer positions to reciprocal space
2334 * for an area detector with a given pixel size mounted along one of
2335 * the coordinate axis including translation axis on the detector arm
2336 *
2337 * Parameters
2338 * ----------
2339 * sampleAngles .... angular positions of the sample goniometer
2340 * (Npoints, Ns)
2341 * detectorAngles .. angular positions of the detector goniometer
2342 * (Npoints, Nd)
2343 * rcch ............ direction + distance of center pixel (angles zero)
2344 * sampleAxis ...... string with sample axis directions
2345 * detectorAxis .... string with detector axis directions
2346 * kappadir ........ rotation axis of a possible kappa circle
2347 * cch1 ............ center channel of the detector
2348 * cch2 ............ center channel of the detector
2349 * dpixel1 ......... width of one pixel in first direction, same unit as
2350 * distance rcch
2351 * dpixel2 ......... width of one pixel in second direction, same unit as
2352 * distance rcch
2353 * roi ............. region of interest for the area detector
2354 * [dir1min, dir1max, dir2min, dir2max]
2355 * dir1 ............ first direction of the detector, e.g.: "x+"
2356 * dir2 ............ second direction of the detector, e.g.: "z+"
2357 * tiltazimuth ..... azimuth of the tilt
2358 * tilt ............ tilt of the detector plane (rotation around axis
2359 * normal to the direction
2360 * given by the tiltazimuth
2361 * UB .............. orientation matrix and reciprocal space conversion
2362 * of the investigated crystal (3, 3)
2363 * lambda .......... wavelength of the used x-rays (Npoints,)
2364 * Npoints ......... number of points to calculate
2365 * Ns .............. number of sample axes
2366 * Nd .............. number of detector axes
2367 * flags ........... general flags integer (verbosity)
2368 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
2369 *
2370 * */
2371 {
2372 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
2373 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2374 double r_i[3]; /* r_i: center channel direction */
2375 int i, j, j1, j2, k; /* loop indices */
2376 int idxh1, idxh2; /* temporary index helper */
2377 double f; /* f = M_2PI / lambda and detector parameters */
2378 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
2379 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
2380
2381 /* calculate some index shortcuts */
2382 idxh1 = (roi[1] - roi[0]) * (roi[3] - roi[2]);
2383 idxh2 = roi[3] - roi[2];
2384
2385 /* determine axes directions */
2386 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
2387 return -1;
2388 }
2389 if (determine_axes_directions_apply(detectorRotation,
2390 detectorAxis, Nd) != 0) {
2391 return -1;
2392 }
2393
2394 veccopy(r_i, rcch);
2395 normalize(r_i);
2396
2397 /* determine detector pixel vector */
2398 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2399 return -1;
2400 }
2401 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2402 return -1;
2403 }
2404
2405 /* rotate detector pixel vectors according to tilt */
2406 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2407
2408 /* calculate center channel position in detector plane */
2409 for (k = 0; k < 3; ++k) {
2410 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2411 }
2412
2413 /* calculate rotation matices and perform rotations */
2414 #pragma omp parallel for default(shared) \
2415 private(i, j, j1, j2, k, f, mtemp, mtemp2, ms, rd) \
2416 schedule(static)
2417 for (i = 0; i < Npoints; ++i) {
2418 f = M_2PI / lambda[i];
2419 /* determine sample rotations */
2420 ident(mtemp);
2421 for (j = 0; j < Ns; ++j) {
2422 /* load kappa direction into matrix
2423 * (just needed for kappa goniometer) */
2424 mtemp2[0] = kappadir[0];
2425 mtemp2[1] = kappadir[1];
2426 mtemp2[2] = kappadir[2];
2427 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
2428 matmul(mtemp, mtemp2);
2429 }
2430 /* apply rotation of orientation matrix */
2431 matmul(mtemp, UB);
2432 /* determine inverse matrix */
2433 inversemat(mtemp, ms);
2434
2435 /* ms contains now the inverse rotation matrix for the sample circles
2436 * detector rotations/translations need to be applied separately for
2437 * every pixel */
2438 for (j1 = roi[0]; j1 < roi[1]; ++j1) {
2439 for (j2 = roi[2]; j2 < roi[3]; ++j2) {
2440 for (k = 0; k < 3; ++k) {
2441 rd[k] = j1 * rpixel1[k] + j2 * rpixel2[k] - rcchp[k];
2442 }
2443 sumvec(rd, rcch);
2444 /* apply detector rotations/translations, starting with the
2445 * inner most */
2446 for (j = Nd - 1; j >= 0; --j) {
2447 detectorRotation[j](detectorAngles[Nd * i + j], rd);
2448 }
2449
2450 normalize(rd);
2451 /* rd contains detector pixel direction,
2452 * r_i contains primary beam direction */
2453 diffvec(rd, r_i);
2454 vecmul(rd, f);
2455 /* determine momentum transfer */
2456 matvec(ms, rd,
2457 &qpos[3 * (i * idxh1 + idxh2 * (j1 - roi[0]) +
2458 (j2 - roi[2]))]);
2459 }
2460 }
2461 }
2462 return 0;
2463 }
2464
2465
2466 int ang2q_conversion_area_sdtrans(
2467 double *sampleAngles, double *detectorAngles, double *rcch,
2468 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
2469 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
2470 char *dir2, double tiltazimuth, double tilt, double *UB,
2471 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
2472 int flags, double *qpos)
2473 /* conversion of Npoints of goniometer positions to reciprocal space
2474 * for an area detector with a given pixel size mounted along one of
2475 * the coordinate axis including translation axis on the detector arm
2476 * and considering a sample displacement
2477 *
2478 * Parameters
2479 * ----------
2480 * sampleAngles .... angular positions of the sample goniometer
2481 * (Npoints, Ns)
2482 * detectorAngles .. angular positions of the detector goniometer
2483 * (Npoints, Nd)
2484 * rcch ............ direction + distance of center pixel (angles zero)
2485 * sampleAxis ...... string with sample axis directions
2486 * detectorAxis .... string with detector axis directions
2487 * kappadir ........ rotation axis of a possible kappa circle
2488 * cch1 ............ center channel of the detector
2489 * cch2 ............ center channel of the detector
2490 * dpixel1 ......... width of one pixel in first direction, same unit as
2491 * distance rcch
2492 * dpixel2 ......... width of one pixel in second direction, same unit as
2493 * distance rcch
2494 * roi ............. region of interest for the area detector
2495 * [dir1min, dir1max, dir2min, dir2max]
2496 * dir1 ............ first direction of the detector, e.g.: "x+"
2497 * dir2 ............ second direction of the detector, e.g.: "z+"
2498 * tiltazimuth ..... azimuth of the tilt
2499 * tilt ............ tilt of the detector plane (rotation around axis
2500 * normal to the direction
2501 * given by the tiltazimuth
2502 * UB .............. orientation matrix and reciprocal space conversion
2503 * of the investigated crystal (3, 3)
2504 * sampledis ....... sample displacement vector, same units as the
2505 * detector distance
2506 * lambda .......... wavelength of the used x-rays (Npoints,)
2507 * Npoints ......... number of points to calculate
2508 * Ns .............. number of sample axes
2509 * Nd .............. number of detector axes
2510 * flags ........... general flags integer (verbosity)
2511 * qpos ............ momentum transfer (Npoints * Nch, 3) (OUTPUT array)
2512 *
2513 * */
2514 {
2515 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
2516 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2517 double r_i[3]; /* r_i: center channel direction */
2518 int i, j, j1, j2, k; /* loop indices */
2519 int idxh1, idxh2; /* temporary index helper */
2520 double f; /* f = M_2PI / lambda and detector parameters */
2521 fp_rot *sampleRotation = malloc(Ns * sizeof(fp_rot));
2522 fp_rot *detectorRotation = malloc(Nd * sizeof(fp_rot));
2523
2524 /* calculate some index shortcuts */
2525 idxh1 = (roi[1] - roi[0]) * (roi[3] - roi[2]);
2526 idxh2 = roi[3] - roi[2];
2527
2528 /* determine axes directions */
2529 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
2530 return -1;
2531 }
2532 if (determine_axes_directions_apply(detectorRotation,
2533 detectorAxis, Nd) != 0) {
2534 return -1;
2535 }
2536
2537 veccopy(r_i, rcch);
2538 normalize(r_i);
2539
2540 /* determine detector pixel vector */
2541 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2542 return -1;
2543 }
2544 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2545 return -1;
2546 }
2547
2548 /* rotate detector pixel vectors according to tilt */
2549 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2550
2551 /* calculate center channel position in detector plane */
2552 for (k = 0; k < 3; ++k) {
2553 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2554 }
2555
2556 /* calculate rotation matices and perform rotations */
2557 #pragma omp parallel for default(shared) \
2558 private(i, j, j1, j2, k, f, mtemp, mtemp2, ms, rd) \
2559 schedule(static)
2560 for (i = 0; i < Npoints; ++i) {
2561 f = M_2PI / lambda[i];
2562 /* determine sample rotations */
2563 ident(mtemp);
2564 for (j = 0; j < Ns; ++j) {
2565 /* load kappa direction into matrix
2566 * (just needed for kappa goniometer) */
2567 mtemp2[0] = kappadir[0];
2568 mtemp2[1] = kappadir[1];
2569 mtemp2[2] = kappadir[2];
2570 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
2571 matmul(mtemp, mtemp2);
2572 }
2573 /* apply rotation of orientation matrix */
2574 matmul(mtemp, UB);
2575 /* determine inverse matrix */
2576 inversemat(mtemp, ms);
2577
2578 /* ms contains now the inverse rotation matrix for the sample circles
2579 * detector rotations/translations need to be applied separately for
2580 * every pixel */
2581 for (j1 = roi[0]; j1 < roi[1]; ++j1) {
2582 for (j2 = roi[2]; j2 < roi[3]; ++j2) {
2583 for (k = 0; k < 3; ++k) {
2584 rd[k] = j1 * rpixel1[k] + j2 * rpixel2[k] - rcchp[k];
2585 }
2586 sumvec(rd, rcch);
2587 /* apply detector rotations/translations, starting with the
2588 * inner most */
2589 for (j = Nd - 1; j >= 0; --j) {
2590 detectorRotation[j](detectorAngles[Nd * i + j], rd);
2591 }
2592 /* consider the effect of the sample displacement */
2593 diffvec(rd, sampledis);
2594 normalize(rd);
2595 /* rd contains detector pixel direction,
2596 * r_i contains primary beam direction */
2597 diffvec(rd, r_i);
2598 vecmul(rd, f);
2599 /* determine momentum transfer */
2600 matvec(ms, rd,
2601 &qpos[3 * (i * idxh1 + idxh2 * (j1 - roi[0]) +
2602 (j2 - roi[2]))]);
2603 }
2604 }
2605 }
2606 return 0;
2607 }
2608
2609
2610 PyObject* ang2q_conversion_area_pixel(PyObject *self, PyObject *args)
2611 /* conversion of Npoints of detector positions to Q for an area detector
2612 * with a given pixel size mounted along one of the coordinate axis. This
2613 * function only calculates the q-position for the pairs of pixel numbers
2614 * (n1, n2) given in the input and should therefore be used only for
2615 * detector calibration purposes.
2616 *
2617 * Parameters
2618 * ----------
2619 * detectorAngles .. angular positions of the detector goniometer
2620 * (Npoints, Nd)
2621 * n1 .............. detector pixel numbers dim1 (Npoints)
2622 * n2 .............. detector pixel numbers dim2 (Npoints)
2623 * rcch ............ direction + distance of center pixel (angles zero)
2624 * detectorAxis .... string with detector axis directions
2625 * cch1 ............ center channel of the detector
2626 * cch2 ............ center channel of the detector
2627 * dpixel1 ......... width of one pixel in first direction, same unit as
2628 * distance rcch
2629 * dpixel2 ......... width of one pixel in second direction, same unit as
2630 * distance rcch
2631 * dir1 ............ first direction of the detector, e.g.: "x+"
2632 * dir2 ............ second direction of the detector, e.g.: "z+"
2633 * tiltazimuth ..... azimuth of the tilt
2634 * tilt ............ tilt of the detector plane (rotation around axis
2635 * normal to the direction given by the tiltazimuth
2636 * lambda .......... wavelength of the used x-rays
2637 * nthreads ........ number of threads to use in parallel section of
2638 * the code
2639 *
2640 * Returns
2641 * -------
2642 * qpos ............ momentum transfer (Npoints, 3)
2643 * */
2644 {
2645 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2646 double r_i[3]; /* r_i: center channel direction */
2647 int i, j, k; /* loop indices */
2648 int Nd; /* number of detector circles */
2649 int Npoints; /* number of angular positions */
2650 unsigned int nthreads; /* number of threads to use */
2651 /* x-ray wavelength, f = M_2PI / lambda and detector parameters */
2652 double f, lambda, cch1, cch2, dpixel1, dpixel2, tilt, tiltazimuth;
2653 char *detectorAxis, *dir1, *dir2; /* string with detector axis,
2654 * and detector direction */
2655 double *detectorAngles, *n1, *n2, *rcch, *qpos; /* c-arrays */
2656 fp_rot *detectorRotation;
2657 npy_intp nout[2];
2658
2659 PyArrayObject *detectorAnglesArr = NULL, *n1Arr = NULL, *n2Arr = NULL,
2660 *rcchArr = NULL, *qposArr = NULL; /* numpy arrays */
2661
2662 /* Python argument conversion code */
2663 if (!PyArg_ParseTuple(args, "O!O!O!O!sddddssdddI",
2664 &PyArray_Type, &detectorAnglesArr,
2665 &PyArray_Type, &n1Arr,
2666 &PyArray_Type, &n2Arr,
2667 &PyArray_Type, &rcchArr,
2668 &detectorAxis, &cch1, &cch2, &dpixel1, &dpixel2,
2669 &dir1, &dir2, &tiltazimuth, &tilt,
2670 &lambda, &nthreads)) {
2671 return NULL;
2672 }
2673
2674 /* check Python array dimensions and types */
2675 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
2676 "detectorAngles must be a 2D double array");
2677 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE,
2678 "rcch must be a 1D double array");
2679 if (PyArray_SIZE(rcchArr) != 3) {
2680 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
2681 return NULL;
2682 }
2683 PYARRAY_CHECK(n1Arr, 1, NPY_DOUBLE, "n1 must be a 1D double array");
2684 PYARRAY_CHECK(n2Arr, 1, NPY_DOUBLE, "n2 must be a 1D double array");
2685
2686 Npoints = (int) PyArray_DIMS(detectorAnglesArr)[0];
2687 if (PyArray_SIZE(n1Arr) != Npoints || PyArray_SIZE(n2Arr) != Npoints) {
2688 PyErr_SetString(PyExc_ValueError, "n1, n2 must be of length Npoints");
2689 return NULL;
2690 }
2691 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
2692
2693 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
2694 rcch = (double *) PyArray_DATA(rcchArr);
2695 n1 = (double *) PyArray_DATA(n1Arr);
2696 n2 = (double *) PyArray_DATA(n2Arr);
2697
2698 /* derived values from input parameters */
2699 f = M_2PI / lambda;
2700
2701 /* create output ndarray */
2702 nout[0] = Npoints;
2703 nout[1] = 3;
2704 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
2705 qpos = (double *) PyArray_DATA(qposArr);
2706
2707 #ifdef __OPENMP__
2708 /* set openmp thread numbers dynamically */
2709 OMPSETNUMTHREADS(nthreads);
2710 #endif
2711
2712 /* arrays with function pointers to rotation matrix functions */
2713 detectorRotation = (fp_rot*) malloc(Nd * sizeof(fp_rot));
2714
2715 /* determine axes directions */
2716 if (determine_axes_directions_apply(detectorRotation,
2717 detectorAxis, Nd) != 0) {
2718 return NULL;
2719 }
2720
2721 veccopy(r_i, rcch);
2722 normalize(r_i);
2723
2724 /* determine detector pixel vector */
2725 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2726 return NULL;
2727 }
2728 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2729 return NULL;
2730 }
2731
2732 /* rotate detector pixel vectors according to tilt */
2733 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2734
2735 /* calculate center channel position in detector plane */
2736 for (k = 0; k < 3; ++k) {
2737 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2738 }
2739
2740 /* calculate rotation matices and perform rotations */
2741 #pragma omp parallel for default(shared) \
2742 private(i, j, k, rd) \
2743 schedule(static)
2744 for (i = 0; i < Npoints; ++i) {
2745 /* calculate momentum transfer for the detector pixel n1[i], n2[i] */
2746 for (k = 0; k < 3; ++k) {
2747 rd[k] = n1[i] * rpixel1[k] + n2[i] * rpixel2[k] - rcchp[k];
2748 }
2749 sumvec(rd, rcch);
2750 /* apply detector rotations/translations */
2751 for (j = 0; j < Nd; ++j) {
2752 detectorRotation[j](detectorAngles[Nd * i + j], rd);
2753 }
2754 normalize(rd);
2755 /* rd contains detector pixel direction,
2756 * r_i contains primary beam direction */
2757 diffvec(rd, r_i);
2758 vecmul(rd, f);
2759 /* save momentum transfer to output */
2760 veccopy(&qpos[3 * i], rd);
2761 }
2762
2763 /* clean up */
2764 Py_DECREF(detectorAnglesArr);
2765 Py_DECREF(n1Arr);
2766 Py_DECREF(n2Arr);
2767 Py_DECREF(rcchArr);
2768
2769 /* return output array */
2770 return PyArray_Return(qposArr);
2771 }
2772
2773
2774 PyObject* ang2q_conversion_area_pixel2(PyObject *self, PyObject *args)
2775 /* conversion of Npoints of detector positions to Q
2776 * for an area detector with a given pixel size mounted along one of
2777 * the coordinate axis. This function only calculates the q-position for the
2778 * pairs of pixel numbers (n1, n2) given in the input and should therefore
2779 * be used only for detector calibration purposes.
2780 *
2781 * This variant of this function also takes a sample orientation matrix as
2782 * well as the sample goniometer as input to allow for a simultaneous fit
2783 * of reference samples orientation
2784 *
2785 * Interface:
2786 * sampleAngles .... angular positions of the sample goniometer
2787 * (Npoints, Ns)
2788 * detectorAngles .. angular positions of the detector goniometer
2789 * (Npoints, Nd)
2790 * n1 .............. detector pixel numbers dim1 (Npoints)
2791 * n2 .............. detector pixel numbers dim2 (Npoints)
2792 * rcch ............ direction + distance of center pixel (angles zero)
2793 * sampleAxis ...... string with sample axis directions
2794 * detectorAxis .... string with detector axis directions
2795 * cch1 ............ center channel of the detector
2796 * cch2 ............ center channel of the detector
2797 * dpixel1 ......... width of one pixel in first direction, same unit as
2798 * distance rcch
2799 * dpixel2 ......... width of one pixel in second direction, same unit as
2800 * distance rcch
2801 * dir1 ............ first direction of the detector, e.g.: "x+"
2802 * dir2 ............ second direction of the detector, e.g.: "z+"
2803 * tiltazimuth ..... azimuth of the tilt
2804 * tilt ............ tilt of the detector plane (rotation around axis
2805 * normal to the direction given by the tiltazimuth
2806 * UB .............. orientation matrix and reciprocal space conversion
2807 * of the investigated crystal (3, 3)
2808 * lambda .......... wavelength of the used x-rays
2809 * nthreads ........ number of threads to use in parallel section of the
2810 * code
2811 *
2812 * Returns
2813 * -------
2814 * qpos ............ momentum transfer (Npoints, 3)
2815 * */
2816 {
2817 double mtemp[9], mtemp2[9], ms[9]; /* matrices */
2818 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
2819 double r_i[3]; /* r_i: center channel direction */
2820 int i, j, k; /* loop indices */
2821 int Ns, Nd; /* number of sample / detector circles */
2822 int Npoints; /* number of angular positions */
2823 unsigned int nthreads; /* number of threads to use */
2824 /* x-ray wavelength, f = M_2PI / lambda and detector parameters */
2825 double f, lambda, cch1, cch2, dpixel1, dpixel2, tilt, tiltazimuth;
2826 /* string with sample and detector axis, and detector direction */
2827 char *sampleAxis, *detectorAxis, *dir1, *dir2;
2828 /* c-arrays */
2829 double *sampleAngles, *detectorAngles, *n1, *n2, *rcch, *UB, *qpos;
2830 fp_rot *sampleRotation;
2831 fp_rot *detectorRotation;
2832 npy_intp nout[2];
2833
2834 PyArrayObject *sampleAnglesArr = NULL, *detectorAnglesArr = NULL,
2835 *n1Arr = NULL, *n2Arr = NULL, *rcchArr = NULL,
2836 *UBArr = NULL, *qposArr = NULL; /* numpy arrays */
2837
2838 /* Python argument conversion code */
2839 if (!PyArg_ParseTuple(args, "O!O!O!O!O!ssddddssddO!dI",
2840 &PyArray_Type, &sampleAnglesArr,
2841 &PyArray_Type, &detectorAnglesArr,
2842 &PyArray_Type, &n1Arr, &PyArray_Type, &n2Arr,
2843 &PyArray_Type, &rcchArr,
2844 &sampleAxis, &detectorAxis, &cch1, &cch2,
2845 &dpixel1, &dpixel2, &dir1, &dir2, &tiltazimuth,
2846 &tilt, &PyArray_Type, &UBArr,
2847 &lambda, &nthreads)) {
2848 return NULL;
2849 }
2850
2851 /* check Python array dimensions and types */
2852 PYARRAY_CHECK(sampleAnglesArr, 2, NPY_DOUBLE,
2853 "sampleAngles must be a 2D double array");
2854 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
2855 "detectorAngles must be a 2D double array");
2856 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE,
2857 "rcch must be a 1D double array");
2858 if (PyArray_SIZE(rcchArr) != 3) {
2859 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
2860 return NULL;
2861 }
2862 PYARRAY_CHECK(UBArr, 2, NPY_DOUBLE, "UB must be a 2D double array");
2863 if (PyArray_DIMS(UBArr)[0] != 3 || PyArray_DIMS(UBArr)[1] != 3) {
2864 PyErr_SetString(PyExc_ValueError, "UB must be of shape (3, 3)");
2865 return NULL;
2866 }
2867 PYARRAY_CHECK(n1Arr, 1, NPY_DOUBLE, "n1 must be a 1D double array");
2868 PYARRAY_CHECK(n2Arr, 1, NPY_DOUBLE, "n2 must be a 1D double array");
2869
2870 Npoints = (int) PyArray_DIMS(detectorAnglesArr)[0];
2871 if (PyArray_SIZE(n1Arr) != Npoints || PyArray_SIZE(n2Arr) != Npoints) {
2872 PyErr_SetString(PyExc_ValueError, "n1, n2 must be of length Npoints");
2873 return NULL;
2874 }
2875 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
2876 Ns = (int) PyArray_DIMS(sampleAnglesArr)[1];
2877 if (PyArray_DIMS(sampleAnglesArr)[0] != Npoints) {
2878 PyErr_SetString(PyExc_ValueError,
2879 "detectorAngles and sampleAngles must have same first dimension");
2880 return NULL;
2881 }
2882
2883 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
2884 sampleAngles = (double *) PyArray_DATA(sampleAnglesArr);
2885 rcch = (double *) PyArray_DATA(rcchArr);
2886 UB = (double *) PyArray_DATA(UBArr);
2887 n1 = (double *) PyArray_DATA(n1Arr);
2888 n2 = (double *) PyArray_DATA(n2Arr);
2889
2890 /* derived values from input parameters */
2891 f = M_2PI / lambda;
2892
2893 /* create output ndarray */
2894 nout[0] = Npoints;
2895 nout[1] = 3;
2896 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
2897 qpos = (double *) PyArray_DATA(qposArr);
2898
2899 #ifdef __OPENMP__
2900 /* set openmp thread numbers dynamically */
2901 OMPSETNUMTHREADS(nthreads);
2902 #endif
2903
2904 /* arrays with function pointers to rotation matrix functions */
2905 sampleRotation = (fp_rot*) malloc(Ns * sizeof(fp_rot));
2906 detectorRotation = (fp_rot*) malloc(Nd * sizeof(fp_rot));
2907
2908
2909 /* determine axes directions */
2910 if (determine_axes_directions(sampleRotation, sampleAxis, Ns) != 0) {
2911 return NULL;
2912 }
2913 if (determine_axes_directions_apply(detectorRotation,
2914 detectorAxis, Nd) != 0) {
2915 return NULL;
2916 }
2917
2918 veccopy(r_i, rcch);
2919 normalize(r_i);
2920
2921 /* determine detector pixel vector */
2922 if (determine_detector_pixel(rpixel1, dir1, dpixel1, r_i, 0.) != 0) {
2923 return NULL;
2924 }
2925 if (determine_detector_pixel(rpixel2, dir2, dpixel2, r_i, 0.) != 0) {
2926 return NULL;
2927 }
2928
2929 /* rotate detector pixel vectors according to tilt */
2930 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
2931
2932 /* calculate center channel position in detector plane */
2933 for (k = 0; k < 3; ++k) {
2934 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
2935 }
2936
2937 /* calculate rotation matices and perform rotations */
2938 #pragma omp parallel for default(shared) \
2939 private(i, j, k, mtemp, mtemp2, ms, rd) \
2940 schedule(static)
2941 for (i = 0; i < Npoints; ++i) {
2942 /* determine sample rotations */
2943 ident(mtemp);
2944 for (j = 0; j < Ns; ++j) {
2945 sampleRotation[j](sampleAngles[Ns * i + j], mtemp2);
2946 matmul(mtemp, mtemp2);
2947 }
2948 /* apply rotation of orientation matrix */
2949 matmul(mtemp, UB);
2950 /* determine inverse matrix */
2951 inversemat(mtemp, ms);
2952
2953 /* ms contains now the inverse rotation matrix for the sample circles
2954 * calculate the momentum transfer for a certain detector pixel */
2955 for (k = 0; k < 3; ++k) {
2956 rd[k] = n1[i] * rpixel1[k] + n2[i] * rpixel2[k] - rcchp[k];
2957 }
2958 sumvec(rd, rcch);
2959 /* apply detector rotations/translations */
2960 for (j = 0; j < Nd; ++j) {
2961 detectorRotation[j](detectorAngles[Nd * i + j], rd);
2962 }
2963 normalize(rd);
2964 /* rd contains detector pixel direction,
2965 * r_i contains primary beam direction */
2966 diffvec(rd, r_i);
2967 vecmul(rd, f);
2968 /* determine momentum transfer */
2969 matvec(ms, rd, &qpos[3 * i]);
2970 }
2971
2972 /* clean up */
2973 Py_DECREF(detectorAnglesArr);
2974 Py_DECREF(n1Arr);
2975 Py_DECREF(n2Arr);
2976 Py_DECREF(rcchArr);
2977 Py_DECREF(sampleAnglesArr);
2978 Py_DECREF(UBArr);
2979
2980 /* return output array */
2981 return PyArray_Return(qposArr);
2982 }
2983
2984
2985 /* #################################################
2986 * detector position functions (incl. translations)
2987 * #################################################*/
2988
2989 PyObject* ang2q_detpos(PyObject *self, PyObject *args)
2990 /* conversion of Npoints of detector angles positions to vectorial position
2991 * of the detector in real space for a setup with point detector and
2992 * possible detector translations
2993 *
2994 * Parameters
2995 * ----------
2996 * detectorAngles. angular positions of the detector goniometer
2997 * (Npoints, Nd)
2998 * ri ............ direction of primary beam (length specifies distance
2999 * of the detector)
3000 * detectorAxis .. string with detector axis directions
3001 * nthreads ...... number of threads to use in parallel section of the
3002 * code
3003 *
3004 * Returns
3005 * -------
3006 * dpos .......... real space detector position (Npoints, 3)
3007 *
3008 * */
3009 {
3010 double rd[3]; /* local detector direction */
3011 int i, j; /* needed indices */
3012 int Nd; /* number of detector circles */
3013 int Npoints; /* number of angular positions */
3014 unsigned int nthreads; /* number of threads to use */
3015 char *detectorAxis; /* str with sample and detector axis */
3016 /* c-array pointers for further usage */
3017 double *detectorAngles, *ri, *qpos;
3018 npy_intp nout[2];
3019 /* arrays with function pointers to rotation matrix functions */
3020 fp_rot *detectorRotation;
3021
3022 /* numpy arrays */
3023 PyArrayObject *detectorAnglesArr = NULL, *riArr = NULL, *qposArr = NULL;
3024
3025 /* Python argument conversion code */
3026 if (!PyArg_ParseTuple(args, "O!O!sI",
3027 &PyArray_Type, &detectorAnglesArr,
3028 &PyArray_Type, &riArr,
3029 &detectorAxis,
3030 &nthreads)) {
3031 return NULL;
3032 }
3033
3034 /* check Python array dimensions and types */
3035 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
3036 "detectorAngles must be a 2D double array");
3037 PYARRAY_CHECK(riArr, 1, NPY_DOUBLE,
3038 "r_i must be a 1D double array");
3039 if (PyArray_SIZE(riArr) != 3) {
3040 PyErr_SetString(PyExc_ValueError, "r_i needs to be of length 3");
3041 return NULL;
3042 }
3043
3044 Npoints = (int) PyArray_DIMS(detectorAnglesArr)[0];
3045 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
3046
3047 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
3048 ri = (double *) PyArray_DATA(riArr);
3049
3050 /* create output ndarray */
3051 nout[0] = Npoints;
3052 nout[1] = 3;
3053 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
3054 qpos = (double *) PyArray_DATA(qposArr);
3055
3056 #ifdef __OPENMP__
3057 /* set openmp thread numbers dynamically */
3058 OMPSETNUMTHREADS(nthreads);
3059 #endif
3060
3061 /* arrays with function pointers to rotation matrix functions */
3062 detectorRotation = (fp_rot*) malloc(Nd * sizeof(fp_rot));
3063
3064 /* determine axes directions */
3065 if (determine_axes_directions_apply(detectorRotation,
3066 detectorAxis, Nd) != 0) {
3067 return NULL;
3068 }
3069
3070 /* calculate rotation matices and perform rotations */
3071 #pragma omp parallel for default(shared) \
3072 private(i, j, rd) schedule(static)
3073 for (i = 0; i < Npoints; ++i) {
3074 /* determine detector rotations */
3075 veccopy(rd, ri);
3076 for (j = Nd - 1; j >= 0; --j) {
3077 detectorRotation[j](detectorAngles[Nd * i + j], rd);
3078 }
3079 veccopy(&qpos[3 * i], rd);
3080 }
3081
3082 /* clean up */
3083 Py_DECREF(detectorAnglesArr);
3084 Py_DECREF(riArr);
3085
3086 /* return output array */
3087 return PyArray_Return(qposArr);
3088 }
3089
3090
3091 PyObject* ang2q_detpos_linear(PyObject *self, PyObject *args)
3092 /* conversion of Npoints of detector angles to real space detector
3093 * positions for a linear detector with a given pixel size mounted
3094 * along one of the coordinate axis, and translation motors on the
3095 * detector arm
3096 *
3097 * Parameters
3098 * ----------
3099 * detectorAngles .. angular positions of the detector goniometer
3100 * (Npoints, Nd)
3101 * rcch ............ direction + distance of center channel (angles zero)
3102 * detectorAxis .... string with detector axis directions
3103 * cch ............. center channel of the detector
3104 * dpixel .......... width of one pixel, same unit as distance rcch
3105 * roi ............. region of interest of the detector
3106 * dir ............. direction of the detector, e.g.: "x+"
3107 * tilt ............ tilt of the detector direction from dir
3108 * nthreads ........ number of threads to use in parallel section of the
3109 * code
3110 *
3111 * Returns
3112 * -------
3113 * dpos ............ real space detector position (Npoints * Nch, 3)
3114 * */
3115 {
3116 double rd[3], rpixel[3], rcchp[3]; /* detector position */
3117 int i, j, k; /* needed indices */
3118 int Nd; /* number of detector circles */
3119 int Npoints; /* number of angular positions */
3120 int Nch; /* number of channels in region of interest */
3121 unsigned int nthreads; /* number of threads to use */
3122 double cch, dpixel, tilt; /* detector parameters */
3123 char *detectorAxis, *dir; /* string with detector axis, and detector
3124 * direction */
3125 double *detectorAngles, *rcch, *qpos;
3126 int *roi; /* region of interest integer array */
3127 npy_intp nout[2];
3128 fp_rot *detectorRotation;
3129
3130 /* numpy arrays */
3131 PyArrayObject *detectorAnglesArr = NULL, *rcchArr = NULL,
3132 *roiArr = NULL, *qposArr = NULL;
3133
3134 /* Python argument conversion code */
3135 if (!PyArg_ParseTuple(args, "O!O!sddO!sdI",
3136 &PyArray_Type, &detectorAnglesArr,
3137 &PyArray_Type, &rcchArr,
3138 &detectorAxis,
3139 &cch, &dpixel, &PyArray_Type, &roiArr,
3140 &dir, &tilt, &nthreads)) {
3141 return NULL;
3142 }
3143
3144 /* check Python array dimensions and types */
3145 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
3146 "detectorAngles must be a 2D double array");
3147 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE,
3148 "rcch must be a 1D double array");
3149 if (PyArray_SIZE(rcchArr) != 3) {
3150 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
3151 return NULL;
3152 }
3153 PYARRAY_CHECK(roiArr, 1, NPY_INT32, "roi must be a 1D int array");
3154 if (PyArray_SIZE(roiArr) != 2) {
3155 PyErr_SetString(PyExc_ValueError, "roi must be of length 2");
3156 return NULL;
3157 }
3158
3159 Npoints = (int) PyArray_DIMS(detectorAnglesArr)[0];
3160 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
3161
3162 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
3163 rcch = (double *) PyArray_DATA(rcchArr);
3164 roi = (int *) PyArray_DATA(roiArr);
3165
3166 /* derived values from input parameters */
3167 Nch = roi[1] - roi[0]; /* number of channels */
3168
3169 /* create output ndarray */
3170 nout[0] = Npoints * Nch;
3171 nout[1] = 3;
3172 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
3173 qpos = (double *) PyArray_DATA(qposArr);
3174
3175 #ifdef __OPENMP__
3176 /* set openmp thread numbers dynamically */
3177 OMPSETNUMTHREADS(nthreads);
3178 #endif
3179
3180 /* arrays with function pointers to rotation matrix functions */
3181 detectorRotation = (fp_rot*) malloc(Nd * sizeof(fp_rot));
3182
3183 /* determine axes directions */
3184 if (determine_axes_directions_apply(detectorRotation,
3185 detectorAxis, Nd) != 0) {
3186 return NULL;
3187 }
3188
3189 /* determine detector pixel vector */
3190 if (determine_detector_pixel(rpixel, dir, dpixel, rcch, tilt) != 0) {
3191 return NULL;
3192 }
3193 for (k = 0; k < 3; ++k) {
3194 rcchp[k] = rpixel[k] * cch;
3195 }
3196
3197 /* calculate rotation matices and perform rotations */
3198 #pragma omp parallel for default(shared) \
3199 private(i, j, k, rd) schedule(static)
3200 for (i = 0; i < Npoints; ++i) {
3201 for (j = roi[0]; j < roi[1]; ++j) {
3202 for (k = 0; k < 3; ++k) {
3203 rd[k] = j * rpixel[k] - rcchp[k];
3204 }
3205 sumvec(rd, rcch);
3206 /* determine detector rotations */
3207 for (k = Nd - 1; k >= 0; --k) {
3208 detectorRotation[k](detectorAngles[Nd * i + k], rd);
3209 }
3210
3211 veccopy(&qpos[3 * (i * Nch + j - roi[0])], rd);
3212 }
3213 }
3214
3215 /* clean up */
3216 Py_DECREF(detectorAnglesArr);
3217 Py_DECREF(rcchArr);
3218 Py_DECREF(roiArr);
3219
3220 /* return output array */
3221 return PyArray_Return(qposArr);
3222 }
3223
3224
3225 PyObject* ang2q_detpos_area(PyObject *self, PyObject *args)
3226 /* conversion of Npoints of detector arm angles to real space position
3227 * of the detector for an area detector with a given pixel size
3228 * mounted along one of the coordinate axis including translation axis
3229 * on the detector arm
3230 *
3231 * Parameters
3232 * ----------
3233 * detectorAngles .. angular positions of the detector goniometer
3234 * (Npoints, Nd)
3235 * rcch ............ direction + distance of center pixel (angles zero)
3236 * detectorAxis .... string with detector axis directions
3237 * cch1 ............ center channel of the detector
3238 * cch2 ............ center channel of the detector
3239 * dpixel1 ......... width of one pixel in first direction, same unit as
3240 * distance rcch
3241 * dpixel2 ......... width of one pixel in second direction, same unit as
3242 * distance rcch
3243 * roi ............. region of interest for the area detector
3244 * [dir1min, dir1max, dir2min, dir2max]
3245 * dir1 ............ first direction of the detector, e.g.: "x+"
3246 * dir2 ............ second direction of the detector, e.g.: "z+"
3247 * tiltazimuth ..... azimuth of the tilt
3248 * tilt ............ tilt of the detector plane (rotation around axis
3249 * normal to the direction
3250 * given by the tiltazimuth
3251 * nthreads ........ number of threads to use in parallelization
3252 *
3253 * Returns
3254 * -------
3255 * dpos ............ detector position vector (Npoints * Npix1 * Npix2, 3)
3256 * */
3257 {
3258 double rd[3], rpixel1[3], rpixel2[3], rcchp[3]; /* detector position */
3259 int i, j, j1, j2, k; /* loop indices */
3260 int idxh1, idxh2; /* temporary index helper */
3261 int Nd; /* number of sample and detector circles */
3262 int Npoints; /* number of angular positions */
3263 unsigned int nthreads; /* number threads for OpenMP */
3264 /* detector parameters */
3265 double cch1, cch2, dpixel1, dpixel2, tilt, tiltazimuth;
3266 /* string with detector axis, and detector direction */
3267 char *detectorAxis, *dir1, *dir2;
3268 double *detectorAngles, *rcch, *qpos;
3269 int *roi; /* region of interest integer array */
3270 fp_rot *detectorRotation;
3271 npy_intp nout[2];
3272
3273 /* numpy arrays */
3274 PyArrayObject *detectorAnglesArr = NULL, *rcchArr = NULL,
3275 *roiArr = NULL, *qposArr = NULL;
3276
3277 /* Python argument conversion code */
3278 if (!PyArg_ParseTuple(args, "O!O!sddddO!ssddI",
3279 &PyArray_Type, &detectorAnglesArr,
3280 &PyArray_Type, &rcchArr,
3281 &detectorAxis,
3282 &cch1, &cch2, &dpixel1, &dpixel2,
3283 &PyArray_Type, &roiArr,
3284 &dir1, &dir2, &tiltazimuth, &tilt,
3285 &nthreads)) {
3286 return NULL;
3287 }
3288
3289 /* check Python array dimensions and types */
3290 PYARRAY_CHECK(detectorAnglesArr, 2, NPY_DOUBLE,
3291 "detectorAngles must be a 2D double array");
3292 PYARRAY_CHECK(rcchArr, 1, NPY_DOUBLE,
3293 "rcch must be a 1D double array");
3294 if (PyArray_SIZE(rcchArr) != 3) {
3295 PyErr_SetString(PyExc_ValueError, "rcch needs to be of length 3");
3296 return NULL;
3297 }
3298 PYARRAY_CHECK(roiArr, 1, NPY_INT32, "roi must be a 1D int array");
3299 if (PyArray_SIZE(roiArr) != 4) {
3300 PyErr_SetString(PyExc_ValueError, "roi must be of length 4");
3301 return NULL;
3302 }
3303
3304 Npoints = (int) PyArray_DIMS(detectorAnglesArr)[0];
3305 Nd = (int) PyArray_DIMS(detectorAnglesArr)[1];
3306
3307 detectorAngles = (double *) PyArray_DATA(detectorAnglesArr);
3308 rcch = (double *) PyArray_DATA(rcchArr);
3309 roi = (int *) PyArray_DATA(roiArr);
3310
3311 /* calculate some index shortcuts */
3312 idxh1 = (roi[1] - roi[0]) * (roi[3] - roi[2]);
3313 idxh2 = roi[3] - roi[2];
3314
3315 /* create output ndarray */
3316 nout[0] = Npoints * idxh1;
3317 nout[1] = 3;
3318 qposArr = (PyArrayObject *) PyArray_SimpleNew(2, nout, NPY_DOUBLE);
3319 qpos = (double *) PyArray_DATA(qposArr);
3320
3321 #ifdef __OPENMP__
3322 /* set openmp thread numbers dynamically */
3323 OMPSETNUMTHREADS(nthreads);
3324 #endif
3325
3326 /* arrays with function pointers to rotation matrix functions */
3327 detectorRotation = (fp_rot*) malloc(Nd * sizeof(fp_rot));
3328
3329 /* determine axes directions */
3330 if (determine_axes_directions_apply(detectorRotation,
3331 detectorAxis, Nd) != 0) {
3332 return NULL;
3333 }
3334
3335 /* determine detector pixel vector */
3336 if (determine_detector_pixel(rpixel1, dir1, dpixel1, rcch, 0.) != 0) {
3337 return NULL;
3338 }
3339 if (determine_detector_pixel(rpixel2, dir2, dpixel2, rcch, 0.) != 0) {
3340 return NULL;
3341 }
3342
3343 /* rotate detector pixel vectors according to tilt */
3344 tilt_detector_axis(tiltazimuth, tilt, rpixel1, rpixel2);
3345
3346 /* calculate center channel position in detector plane */
3347 for (k = 0; k < 3; ++k) {
3348 rcchp[k] = rpixel1[k] * cch1 + rpixel2[k] * cch2;
3349 }
3350
3351 /* calculate rotation matices and perform rotations */
3352 #pragma omp parallel for default(shared) \
3353 private(i, j, j1, j2, k, rd) schedule(static)
3354 for (i = 0; i < Npoints; ++i) {
3355 for (j1 = roi[0]; j1 < roi[1]; ++j1) {
3356 for (j2 = roi[2]; j2 < roi[3]; ++j2) {
3357 for (k = 0; k < 3; ++k) {
3358 rd[k] = j1 * rpixel1[k] + j2 * rpixel2[k] - rcchp[k];
3359 }
3360 sumvec(rd, rcch);
3361 /* apply detector rotations/translations, starting with the
3362 * inner most */
3363 for (j = Nd - 1; j >= 0; --j) {
3364 detectorRotation[j](detectorAngles[Nd * i + j], rd);
3365 }
3366
3367 veccopy(&qpos[3 * (i * idxh1 + idxh2 * (j1 - roi[0]) +
3368 (j2 - roi[2]))], rd);
3369 }
3370 }
3371 }
3372
3373 /* clean up */
3374 Py_DECREF(detectorAnglesArr);
3375 Py_DECREF(rcchArr);
3376 Py_DECREF(roiArr);
3377
3378 /* return output array */
3379 return PyArray_Return(qposArr);
3380 }
+0
-227
xrayutilities/src/qconversion.h less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2013-2016 Dominik Kriegner <dominik.kriegner@gmail.com>
17 */
18 #pragma once
19
20 #include "xrayutilities.h"
21
22 #define cdeg2rad (M_PI / 180.)
23 #define crad2deg (180. / M_PI)
24
25 #define deg2rad(ang) (ang * cdeg2rad)
26 #define rad2deg(rad) (rad * crad2deg)
27
28 /* define flags for the qconversion functions */
29 #define HAS_TRANSLATIONS 1
30 #define HAS_SAMPLEDIS 4
31 #define VERBOSE 16
32
33 /* ###################################
34 * matrix vector operations for
35 * 3x3 matrices and vectors of length
36 * 3
37 * ################################### */
38
39 INLINE void ident(double *m);
40
41 INLINE void sumvec(double *RESTRICT v1, double *RESTRICT v2);
42
43 INLINE void diffvec(double *RESTRICT v1, double *RESTRICT v2);
44
45 INLINE double norm(double *v);
46
47 INLINE void normalize(double *v);
48
49 INLINE void veccopy(double *RESTRICT v1, double *RESTRICT v2);
50
51 INLINE void vecmul(double *RESTRICT r, double a);
52
53 INLINE void cross(double *RESTRICT v1, double *RESTRICT v2,
54 double *RESTRICT r);
55
56 INLINE void vecmatcross(double *RESTRICT v, double *RESTRICT m,
57 double *RESTRICT mr);
58
59 INLINE void matmul(double *RESTRICT m1, double *RESTRICT m2);
60
61 INLINE void matmulc(double *RESTRICT m, double c);
62
63 INLINE void matvec(double *RESTRICT m, double *RESTRICT v,
64 double *RESTRICT r);
65
66 INLINE void tensorprod(double *RESTRICT v1, double *RESTRICT v2,
67 double *RESTRICT m);
68
69 INLINE void summat(double *RESTRICT m1, double *RESTRICT m2);
70
71 INLINE void diffmat(double *RESTRICT m1, double *RESTRICT m2);
72
73 INLINE void inversemat(double *RESTRICT m, double *RESTRICT i);
74
75 INLINE double determinant(double *RESTRICT m);
76
77
78 /*##############################################
79 # functions which implement rotation matrices
80 # for all coordinate axes and rotation senses
81 #
82 # the routines expect angles in radians
83 # for conversion from degrees to radians
84 # the functions and2rad and rad2ang are
85 # supplied
86 ################################################*/
87
88 typedef void (*fp_rot)(double, double *);
89
90 INLINE void rotation_xp(double a, double *mat);
91 INLINE void rotation_yp(double a, double *mat);
92 INLINE void rotation_zp(double a, double *mat);
93
94 INLINE void rotation_xm(double a, double *mat);
95 INLINE void rotation_ym(double a, double *mat);
96 INLINE void rotation_zm(double a, double *mat);
97
98 INLINE void rotation_kappa(double a, double *mat);
99
100 INLINE void rotation_arb(double a, double *RESTRICT e, double *RESTRICT mat);
101
102 INLINE void apply_xp(double a, double *vec);
103 INLINE void apply_yp(double a, double *vec);
104 INLINE void apply_zp(double a, double *vec);
105
106 INLINE void apply_xm(double a, double *vec);
107 INLINE void apply_ym(double a, double *vec);
108 INLINE void apply_zm(double a, double *vec);
109
110 INLINE void apply_tx(double x, double *vec);
111 INLINE void apply_ty(double y, double *vec);
112 INLINE void apply_tz(double z, double *vec);
113
114 /*##############################################
115 # functions needed for reciprocal space converions
116 ################################################*/
117
118 int determine_axes_directions(fp_rot *fp_circles, char *stringAxis,
119 unsigned int n);
120
121 int determine_axes_directions_apply(fp_rot *fp_circles, char *stringAxis,
122 unsigned int n);
123
124 int determine_detector_pixel(double *rpixel, char *dir, double dpixel,
125 double *r_i, double tilt);
126
127 int tilt_detector_axis(double tiltazimuth, double tilt,
128 double *RESTRICT rpixel1, double *RESTRICT rpixel2);
129
130 int print_matrix(double *m);
131 int print_vector(double *m);
132
133 /*################################################
134 # reciprocal space converions worker functions
135 # point detector
136 ##################################################*/
137
138 int ang2q_conversion(
139 double *sampleAngles, double *detectorAngles,
140 double *ri, char *sampleAxis, char *detectorAxis,
141 double *kappadir, double *UB, double *lambda,
142 int Npoints, int Ns, int Nd, int flags, double *qpos);
143
144 int ang2q_conversion_sd(
145 double *sampleAngles, double *detectorAngles, double *ri,
146 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
147 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
148 int flags, double *qpos);
149
150 int ang2q_conversion_trans(
151 double *sampleAngles, double *detectorAngles, double *ri,
152 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
153 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos);
154
155 int ang2q_conversion_sdtrans(
156 double *sampleAngles, double *detectorAngles, double *ri,
157 char *sampleAxis, char *detectorAxis, double *kappadir, double *UB,
158 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
159 int flags, double *qpos);
160
161 /*################################################
162 # reciprocal space converions worker functions
163 # linear detector
164 ##################################################*/
165
166 int ang2q_conversion_linear(
167 double *sampleAngles, double *detectorAngles, double *rcch,
168 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
169 double dpixel, int *roi, char *dir, double tilt, double *UB,
170 double *lambda, int Npoints, int Ns, int Nd, int Nch,
171 int flags, double *qpos);
172
173 int ang2q_conversion_linear_sd(
174 double *sampleAngles, double *detectorAngles, double *rcch,
175 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
176 double dpixel, int *roi, char *dir, double tilt, double *UB,
177 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
178 int Nch, int flags, double *qpos);
179
180 int ang2q_conversion_linear_trans(
181 double *sampleAngles, double *detectorAngles, double *rcch,
182 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
183 double dpixel, int *roi, char *dir, double tilt, double *UB,
184 double *lambda, int Npoints, int Ns, int Nd, int Nch,
185 int flags, double *qpos);
186
187 int ang2q_conversion_linear_sdtrans(double *sampleAngles, double *detectorAngles, double *rcch,
188 char *sampleAxis, char *detectorAxis, double *kappadir, double cch,
189 double dpixel, int *roi, char *dir, double tilt, double *UB,
190 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
191 int Nch, int flags, double *qpos);
192
193 /*################################################
194 # reciprocal space converions worker functions
195 # area detector
196 ##################################################*/
197
198 int ang2q_conversion_area(
199 double *sampleAngles, double *detectorAngles, double *rcch,
200 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
201 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
202 char *dir2, double tiltazimuth, double tilt, double *UB,
203 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos);
204
205 int ang2q_conversion_area_sd(
206 double *sampleAngles, double *detectorAngles, double *rcch,
207 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
208 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
209 char *dir2, double tiltazimuth, double tilt, double *UB,
210 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
211 int flags, double *qpos);
212
213 int ang2q_conversion_area_trans(
214 double *sampleAngles, double *detectorAngles, double *rcch,
215 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
216 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
217 char *dir2, double tiltazimuth, double tilt, double *UB,
218 double *lambda, int Npoints, int Ns, int Nd, int flags, double *qpos);
219
220 int ang2q_conversion_area_sdtrans(
221 double *sampleAngles, double *detectorAngles, double *rcch,
222 char *sampleAxis, char *detectorAxis, double *kappadir, double cch1,
223 double cch2, double dpixel1, double dpixel2, int *roi, char *dir1,
224 char *dir2, double tiltazimuth, double tilt, double *UB,
225 double *sampledis, double *lambda, int Npoints, int Ns, int Nd,
226 int flags, double *qpos);
+0
-111
xrayutilities/src/xrayutilities.h less more
0 /*
1 * This file is part of xrayutilities.
2 *
3 * xrayutilities is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2014 Eugen Wintersberger <eugen.wintersberger@gmail.com>
17 */
18 #pragma once
19
20 #include <Python.h>
21 #include <math.h>
22
23 /*****************************************************************************
24 * NUMPY specific macros and header files
25 ****************************************************************************/
26 /*
27 * need to make some definitions before loading the arrayobject.h
28 * header file
29 */
30 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
31 #define PY_ARRAY_UNIQUE_SYMBOL XU_UNIQUE_SYMBOL
32 #define NO_IMPORT_ARRAY
33 #include <numpy/arrayobject.h>
34
35 /*
36 * set numpy API specific macros
37 */
38 #if NPY_FEATURE_VERSION < 0x00000007
39 #define NPY_ARRAY_ALIGNED NPY_ALIGNED
40 #define NPY_ARRAY_C_CONTIGUOUS NPY_C_CONTIGUOUS
41 #endif
42
43 /*
44 * define a macro to check a numpy array. This should mabye go into a
45 * function in future.
46 */
47 #define PYARRAY_CHECK(array, dims, type, msg) \
48 array = (PyArrayObject *) PyArray_FROM_OTF((PyObject *) array, \
49 type, \
50 NPY_ARRAY_C_CONTIGUOUS | \
51 NPY_ARRAY_ALIGNED); \
52 if (PyArray_NDIM(array) != dims || \
53 PyArray_TYPE(array) != type) {\
54 PyErr_SetString(PyExc_ValueError, msg); \
55 return NULL; \
56 }
57
58
59 /*****************************************************************************
60 * Windows build related macros
61 ****************************************************************************/
62 /* 'extern inline' seems to work only on newer version of gcc (>4.6 tested)
63 * gcc 4.1 seems to need this empty, i am not sure if there is a speed gain
64 * by inlining since the calls to those functions are anyhow built dynamically
65 * for compatibility keep this empty unless you can test with several compilers
66 */
67 #define INLINE
68 #ifdef _WIN32
69 #define RESTRICT
70 #else
71 #define RESTRICT restrict
72 #endif
73
74
75 /*
76 * some stuff we need for the Windows build
77 */
78 #ifdef _WIN32
79 #ifndef __MINGW32__
80 #include <float.h>
81 #define isnan _isnan
82 #endif
83
84 #endif
85
86 /*****************************************************************************
87 * general purpose macros
88 ****************************************************************************/
89 /*
90 * if M_PI is not set we do this here
91 */
92 #ifndef M_PI
93 # define M_PI 3.14159265358979323846
94 #endif
95 #define M_2PI (2 * M_PI)
96
97
98 /*****************************************************************************
99 * OpenMP related macros
100 ****************************************************************************/
101 /*
102 * include OpenMP header is required
103 */
104 #ifdef __OPENMP__
105 #include <omp.h>
106 #endif
107
108 #define OMPSETNUMTHREADS(nth) \
109 if (nth == 0) omp_set_num_threads(omp_get_max_threads());\
110 else omp_set_num_threads(nth);
+0
-104
xrayutilities/utilities.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2011 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 xrayutilities utilities contains a conglomeration of useful functions
19 which do not fit into one of the other files
20 """
21
22 import numpy
23
24 from . import config
25 from .utilities_noconf import *
26
27
28 def import_matplotlib_pyplot(funcname='XU'):
29 """
30 lazy import function of matplotlib.pyplot
31
32 Parameters
33 ----------
34 funcname : str
35 identification string of the calling function
36
37 Returns
38 -------
39 flag : bool
40 the flag is True if the loading was successful and False otherwise.
41 pyplot
42 On success pyplot is the matplotlib.pyplot package.
43 """
44 try:
45 from matplotlib import pyplot as plt
46 from .mpl_helper import SqrtAllowNegScale
47 return True, plt
48 except ImportError:
49 if config.VERBOSITY >= config.INFO_LOW:
50 print("%s: Warning: plot functionality not available" % funcname)
51 return False, None
52
53
54 def import_lmfit(funcname='XU'):
55 """
56 lazy import function for lmfit
57 """
58 try:
59 import lmfit
60 return lmfit
61 except ImportError:
62 raise ImportError("%s: Fitting of models needs the lmfit package "
63 "(https://pypi.python.org/pypi/lmfit)" % funcname)
64
65
66 def maplog(inte, dynlow="config", dynhigh="config"):
67 """
68 clips values smaller and larger as the given bounds and returns the log10
69 of the input array. The bounds are given as exponent with base 10 with
70 respect to the maximum in the input array. The function is implemented in
71 analogy to J. Stangl's matlab implementation.
72
73 Parameters
74 ----------
75 inte : ndarray
76 numpy.array, values to be cut in range
77 dynlow : float, optional
78 10^(-dynlow) will be the minimum cut off
79 dynhigh : float, optional
80 10^(-dynhigh) will be the maximum cut off
81
82 Returns
83 -------
84 ndarray
85 numpy.array of the same shape as inte, where values smaller/larger than
86 10^(-dynlow, dynhigh) were replaced by 10^(-dynlow, dynhigh)
87
88 Examples
89 --------
90 >>> lint = maplog(int, 5, 2)
91 """
92 if dynlow == "config":
93 dynlow = config.DYNLOW
94 if dynhigh == "config":
95 dynhigh = config.DYNHIGH
96
97 if inte.max() <= 0.0:
98 raise ValueError("XU.maplog: only negativ or zero values given. "
99 "Log is not defined!")
100 ma = inte.max() * 10 ** (-1*dynhigh) # upper bound
101 mi = inte.max() * 10 ** (-1*dynlow) # lower bound
102
103 return numpy.log10(numpy.minimum(numpy.maximum(inte, mi), ma))
+0
-330
xrayutilities/utilities_noconf.py less more
0 # This file is part of xrayutilities.
1 #
2 # xrayutilities is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 #
15 # Copyright (C) 2010-2019 Dominik Kriegner <dominik.kriegner@gmail.com>
16
17 """
18 xrayutilities utilities contains a conglomeration of useful functions
19 this part of utilities does not need the config class
20 """
21
22 import abc
23 import numbers
24 import os.path
25 import re
26 import sys
27 from ast import parse
28
29 import numpy
30 import scipy.constants
31
32 from .exception import InputError
33
34 try: # works in Python >3.4
35 ABC = abc.ABC
36 except AttributeError: # Python 2.7
37 ABC = abc.ABCMeta('ABC', (object, ), {'__slots__': ()})
38
39 # python 2to3 compatibility
40 try:
41 basestring
42 except NameError:
43 basestring = str
44
45 energies = {
46 'CuKa1': 8047.82310,
47 'CuKa2': 8027.9117,
48 'CuKa12': 8041.18,
49 'CuKb': 8905.337,
50 'MoKa1': 17479.374,
51 'CoKa1': 6930.32,
52 'CoKa2': 6915.30}
53 # wavelength values from International Tables of Crystallography:
54 # Vol C, 2nd Ed. page 203
55 # CuKa1: 1.54059292(45) the value in bracket is the uncertainty
56 # CuKa2: 1.5444140(19)
57 # CuKa12: mixture 2:1 a1 and a2
58 # CuKb: 1.392246(14)
59 # MoKa1: 0.70931713(41)
60 # Xray data booklet:
61 # CoKa1
62 # CoKa2
63
64
65 def set_bit(f, offset):
66 """
67 sets the bit at an offset
68 """
69 mask = 1 << offset
70 return(f | mask)
71
72
73 def clear_bit(f, offset):
74 """
75 clears the bet at an offset
76 """
77 mask = ~(1 << offset)
78 return(f & mask)
79
80
81 def lam2en(inp):
82 """
83 converts the input wavelength in Angstrom to an energy in eV
84
85 Parameters
86 ----------
87 inp : float or str
88 wavelength in Angstrom
89
90 Returns
91 -------
92 float
93 energy in eV
94
95 Examples
96 --------
97 >>> energy = lam2en(1.5406)
98 """
99 # E(eV) = h*c/(e * lambda(A)) *1e10
100 inp = wavelength(inp)
101 c = scipy.constants
102 out = c.h * c.speed_of_light / (c.e * inp) * 1e10
103 return out
104
105
106 def en2lam(inp):
107 """
108 converts the input energy in eV to a wavelength in Angstrom
109
110 Parameters
111 ----------
112 inp : float or str
113 energy in eV
114
115 Returns
116 -------
117 float
118 wavlength in Angstrom
119
120 Examples
121 --------
122 >>> wavelength = en2lam(8048)
123 """
124 # lambda(A) = h*c/(e * E(eV)) *1e10
125 inp = energy(inp)
126 c = scipy.constants
127 out = c.h * c.speed_of_light / (c.e * inp) * 1e10
128 return out
129
130
131 def energy(en):
132 """
133 convert common energy names to energies in eV
134
135 so far this works with CuKa1, CuKa2, CuKa12, CuKb, MoKa1
136
137 Parameters
138 ----------
139 en : float, array-like or str
140 energy either as scalar or array with value in eV, which will be
141 returned unchanged; or string with name of emission line
142
143 Returns
144 -------
145 float or array-like
146 energy in eV
147 """
148
149 if isinstance(en, numbers.Number):
150 return numpy.double(en)
151 elif isinstance(en, (numpy.ndarray, list, tuple)):
152 return numpy.asarray(en)
153 elif isinstance(en, basestring):
154 return energies[en]
155 else:
156 raise InputError("wrong type for argument en")
157
158
159 def wavelength(wl):
160 """
161 convert common energy names to energies in eV
162
163 so far this works with CuKa1, CuKa2, CuKa12, CuKb, MoKa1
164
165 Parameters
166 ----------
167 wl : float, array-like or str
168 wavelength; If scalar or array the wavelength in Angstrom will be
169 returned unchanged, string with emission name is converted to
170 wavelength
171
172 Returns
173 -------
174 float or array-like
175 wavelength in Angstrom
176 """
177
178 if isinstance(wl, numbers.Number):
179 return numpy.double(wl)
180 elif isinstance(wl, (numpy.ndarray, list, tuple)):
181 return numpy.asarray(wl)
182 elif isinstance(wl, basestring):
183 return en2lam(energies[wl])
184 else:
185 raise InputError("wrong type for argument wavelength")
186
187
188 def exchange_path(orig, new, keep=0, replace=None):
189 """
190 function to exchange the root of a path with the option of keeping the
191 inner directory structure. This for example includes such a conversion
192 /dir_a/subdir/images/sample -> /home/user/data/images/sample
193 where the two innermost directory names are kept (keep=2), or equally
194 the three outer most are replaced (replace=3). One can either give keep,
195 or replace, with replace taking preference if both are given. Note that
196 replace=1 on Linux/Unix replaces only the root for absolute paths.
197
198 Parameters
199 ----------
200 orig : str
201 original path which should be replaced by the new path
202 new : str
203 new path which should be used instead
204 keep : int, optional
205 number of inner most directory names which should be kept the same in
206 the output (default = 0)
207 replace : int, optional
208 number of outer most directory names which should be replaced in the
209 output (default = None)
210
211 Returns
212 -------
213 str
214 directory path string
215
216 Examples
217 --------
218 >>> exchange_path('/dir_a/subdir/img/sam', '/home/user/data', keep=2)
219 '/home/user/data/img/sam'
220 """
221 subdirs = []
222 o = orig
223 if replace is None:
224 for i in range(keep):
225 o, s = os.path.split(o)
226 subdirs.append(s)
227 out = new
228 subdirs.reverse()
229 for s in subdirs:
230 out = os.path.join(out, s)
231 else:
232 while True:
233 o, s = os.path.split(o)
234 if not s:
235 subdirs.append(o)
236 break
237 elif not o:
238 subdirs.append(s)
239 break
240 else:
241 subdirs.append(s)
242 subdirs.reverse()
243 out = new
244 for s in subdirs[replace:]:
245 out = os.path.join(out, s)
246 return out
247
248
249 def exchange_filepath(orig, new, keep=0, replace=None):
250 """
251 function to exchange the root of a filename with the option of keeping the
252 inner directory structure. This for example includes such a conversion
253 /dir_a/subdir/sample/file.txt -> /home/user/data/sample/file.txt
254 where the innermost directory name is kept (keep=1), or equally
255 the three outer most are replaced (replace=3). One can either give keep,
256 or replace, with replace taking preference if both are given. Note that
257 replace=1 on Linux/Unix replaces only the root for absolute paths.
258
259 Parameters
260 ----------
261 orig : str
262 original filename which should have its data root replaced
263 new : str
264 new path which should be used instead
265 keep : int, optional
266 number of inner most directory names which should be kept the same in
267 the output (default = 0)
268 replace : int, optional
269 number of outer most directory names which should be replaced in the
270 output (default = None)
271
272 Returns
273 -------
274 str
275 filename string
276
277 Examples
278 --------
279 >>> exchange_filepath('/dir_a/subdir/sam/file.txt', '/data', 1)
280 '/data/sam/file.txt'
281 """
282 if new:
283 if replace is None:
284 return exchange_path(orig, new, keep+1)
285 else:
286 return exchange_path(orig, new, replace=replace)
287 else:
288 return orig
289
290
291 def makeNaturalName(name, check=False):
292 ret = re.sub('[^0-9a-zA-Z]', '_', name.strip())
293 isvalid = is_valid_variable_name(ret)
294 if not check or isvalid:
295 return ret
296 elif not isvalid:
297 raise ValueError("'{}' is not valid variable name".format(ret))
298
299
300 def is_valid_variable_name(name):
301 # Python 3
302 if sys.version_info >= (3, 0):
303 return name.isidentifier()
304 # Python 2
305 try:
306 parse('{} = None'.format(name))
307 return True
308 except (SyntaxError, ValueError, TypeError):
309 return False
310
311
312 def check_kwargs(kwargs, valid_kwargs, identifier):
313 """
314 Raises an TypeError if kwargs included a key which is not in valid_kwargs.
315
316 Parameters
317 ----------
318 kwargs : dict
319 keyword arguments dictionary
320 valid_kwargs : dict
321 dictionary with valid keyword arguments and their description
322 identifier : str
323 string to identifier the caller of this function
324 """
325 desc = ', '.join(["'%s': %s" % (k, d) for k, d in valid_kwargs.items()])
326 for k in kwargs:
327 if k not in valid_kwargs:
328 raise TypeError("%s: unknown keyword argument ('%s') given; "
329 "allowed are %s" % (identifier, k, desc))
+0
-101
xrayutilities/xrayutilities_default.conf less more
0 # XRAYUTILITIES global default configuration
1 # default values for some properties of xrayutilities may be set
2 # the syntax follows the one of the ConfigParser Python module,
3 # which is similar to .ini files
4
5 # begin of xrayutilities configuration
6 [xrayutilities]
7
8 # verbosity level of information and debugging outputs
9 # 0: no output
10 # 1: very import notes for users
11 # 2: less import notes for users (e.g. intermediate results)
12 # 3: debuging output (e.g. print everything, which could be interesing)
13 # levels can be changed in the config file as well
14 verbosity = 1
15
16 # verbosity level borders
17 info_low = 1
18 info_all = 2
19 debug = 3
20
21 # default wavelength in Angstrom
22 wavelength = CuKa1
23
24 # default energy in eV
25 # if energy is given wavelength settings will be ignored
26 # energy = CuKa1
27
28 # number of threads to use in parallel sections of the code
29 nthreads = 0
30 # 0: the maximum number of available threads will be used
31 # (as returned by omp_get_max_threads())
32 # n: n-threads will be used
33
34 # maplog dynlow
35 # at 10^(-dynlow) will be the minimum cut off of the maplog routine
36 dynlow = 6
37 # maplog dyn high
38 # at 10^(-dynhigh) will be the maximum cut off of the maplog routine
39 dynhigh = 0
40
41 # boundary to neglect things in error checks (example the scalar product,
42 # of to vectors, which are supposed to be orthogonal)
43 epsilon = 1e-8
44
45 dbname = elements.db
46
47 # kappa-geometry specifications
48 # direction specifies the direction into which the rotation axis of the
49 # kappa circle is tilted at zero positions of all the gradles
50 # e.g. look at http://en.wikipedia.org/wiki/File:Kappa_goniometer_animation.ogg
51 # assume the following coordinate system and zero angles at the beginning of
52 # the movie: x downstream, y backwards/away from the view, z upwards
53 # the rotation axis of kappa rotation is in the "zy" plane; ~60degree tilted
54 # from z. note that the use of zy to specify that the 60degree are measured
55 # from the z-direction rotation is positive towards y direction
56 kappa_plane = zy
57 kappa_angle = -60
58
59 [powder]
60 # anglemode 'd' is currently not supported by most of the code in xrayutilities
61 anglemode = twotheta
62 oversampling = 4
63 gaussian_smoother_bins_sigma = 1.0
64 window_width = 20
65
66 [powder.global]
67 diffractometer_radius = 300e-3
68 equatorial_divergence_deg = 0.5
69
70 [powder.emission]
71 emiss_wavelengths = ('CuKa1', 'CuKa2')
72 emiss_intensities = (1.0, 0.5)
73 emiss_gauss_widths = (3e-14, 3e-14)
74 emiss_lor_widths = (3e-14, 3e-14)
75
76 [powder.axial]
77 axDiv = full
78 slit_length_source = 8.001e-3
79 slit_length_target = 8e-3
80 length_sample = 10e-3
81 angI_deg = 2.5
82 angD_deg = 2.5
83 n_integral_points = 10
84
85 [powder.absorption]
86 # absorption coefficient in m^{-1}
87 absorption_coefficient = 1e5
88
89 [powder.si_psd]
90 # bounds of solid state detector bounds: e.g. (0, 32e-3)
91 si_psd_window_bounds = None
92
93 [powder.receiver_slit]
94 slit_width = 55e-6
95
96 [powder.tube_tails]
97 main_width = 200e-6
98 tail_left = -1e-3
99 tail_right = 1e-3
100 tail_intens = 0.001
+0
-213
xrayutilities.egg-info/PKG-INFO less more
0 Metadata-Version: 2.1
1 Name: xrayutilities
2 Version: 1.5.3
3 Summary: package for x-ray diffraction data evaluation
4 Home-page: http://xrayutilities.sourceforge.net
5 Author: Eugen Wintersberger, Dominik Kriegner
6 Author-email: eugen.wintersberger@desy.de, dominik.kriegner@gmail.com
7 Maintainer: Dominik Kriegner
8 Maintainer-email: dominik.kriegner@gmail.com
9 License: GPLv2
10 Description: xrayutilities
11 =============
12
13 [![Build
14 Status Travis CI](https://travis-ci.org/dkriegner/xrayutilities.svg?branch=master)](https://travis-ci.org/dkriegner/xrayutilities)
15 [![Build Status AppVeyor](https://ci.appveyor.com/api/projects/status/t8cb5jj0atklxay3/branch/master?svg=true)](https://ci.appveyor.com/project/dkriegner/xrayutilities)
16
17
18 xrayutilities is a collection of scripts used to analyze and simulate x-ray
19 diffraction data. It consists of a Python package and several routines coded
20 in C. For analysis the package is especially useful for the reciprocal space
21 conversion of diffraction data taken with linear and area detectors. For
22 simulations code for X-ray reflectivity, kinematical and dynamical diffraction
23 simulation of crystal truncation rods as well as fundamental parameters powder
24 diffraction is included.
25
26
27 Copyright (C) 2009-2018 Dominik Kriegner <dominik.kriegner@gmail.com>
28
29 Copyright (C) 2009-2013 Eugen Wintersberger <eugen.wintersberger@desy.de>
30
31
32 Mailing list and issue tracker
33 ------------------------------
34
35 To get in touch with us or report an issue please use the mailing list
36 (https://sourceforge.net/p/xrayutilities/mailman/xrayutilities-users/) or the
37 Github issue tracker (https://github.com/dkriegner/xrayutilities/issues). When
38 you want to follow announcements of major changes or new releases its
39 recommended to [sign up for the mailing
40 list](https://sourceforge.net/projects/xrayutilities/lists/xrayutilities-users)
41
42
43 Contents
44 --------
45
46 * *examples*: directory with example scripts and configurations
47 * *xrayutilities*: directory with the sources for the Python package
48 * *tests*: directory with the unittest scripts
49 * *setup.py*: distutils install script used for the package installation
50 * *xrayutilities.pdf*: pdf-file with documentation of the package
51
52
53 Installation (pip)
54 ==================
55 Using the python package manager pip you can install xrayutilities by executing
56
57 pip install xrayutilities
58
59 or for a user installation (without admin access) use
60
61 pip install --user xrayutilities
62
63 If installation using above's command fails due to missing OpenMP libraries, use
64
65 pip install --global-option="--without-openmp" xrayutilities
66
67
68 Installation (source)
69 =====================
70 Installing xrayutilities from source is an easy process done by executing
71
72 python setup.py install
73
74 or
75
76 python setup.py install --prefix=<install_path>
77
78 in the source folder of xrayutilities on the command line/terminal. The first
79 command installs in the systems default directories, whereas in the second
80 command you can manually specify the installation path.
81
82 By default the setup.py script tries to use OpenMP. If you do not want to use
83 OpenMP or do not have it available use the *--without-openmp* option for the
84 installation:
85
86 python setup.py --without-openmp install --prefix=<install_path>
87
88 Requirements
89 ------------
90 The following requirements are needed for installing and using *xrayutilities*:
91
92 - Python (version 2.7 or >= 3.2)
93 - C-compiler (preferential with OpenMP support)
94 - h5py
95 - scipy (version >= 0.13.0)
96 - numpy (version >= 1.8)
97 - setuptools (to provide the pkg_resources module)
98 - lmfit (optional)
99 - matplotlib (optional)
100 - python-lzma (optional)
101
102 When building from source you also might need:
103
104 - python dev headers
105 - unittest2 (optional - only if you want to run the tests)
106 - sphinx (optional - only when you want to build the documentation)
107 - numpydoc (optional - only when you want to build the documentation)
108
109 refer to your operating system documentation to find out how to install
110 those packages. On Microsoft Windows refer to the Documentation for the
111 easiest way of the installation (Python(x,y) or WinPython).
112
113 Python-2.7 and Python-3.X compatibility
114 =======================================
115
116 The current development focuses on Python-3.X and we ask all users to update to
117 Python-3 if possible, however, xrayutilities can be used with Python-2.7 as
118 well. Care was taken to make this possible from the same code-base.
119
120 The Python package configuration
121 ================================
122
123 The following steps should only be necessary for user local installation to
124 ensure the Python module is found by the Python interpreter:
125 In this case the module is installed under
126 *<prefix>/lib[64]/python?.?/site-packages* on Unix systems and
127 *<prefix>/Lib/site-packages* on Windows systems.
128
129 If you have installed the Python package in a directory unknown to your local
130 Python distribution, you have to tell Python where to look for the Package.
131 There are several ways how to do this:
132
133 - add the directory where the package is installed to your
134 *PYTHONPATH* environment variable.
135
136 - add the path to sys.path in the *.pythonrc* file placed in your home
137 directory
138
139 import sys
140 sys.path.append("path to the xrayutilities package")
141
142 - simply apply the previous method in every script where you want to
143 use the xrayutilities package before importing the package
144
145 import sys
146 sys.path.append("path to the xrayutilities package")
147 import xrayutilities
148
149 Obtaining the source code
150 =========================
151
152 The sources are hosted on sourceforge in git repository.
153 Use
154
155 git clone https://github.com/dkriegner/xrayutilities.git
156
157 to clone the git repository. If you would like to have commit rights
158 contact one of the administrators.
159
160 Update
161 ======
162
163 if you already installed xrayutilities you can update it by navigating into
164 its source folder and obtain the new sources by ::
165
166 git pull
167
168 or download the new tarball from sourceforge
169 (http://sf.net/projects/xrayutilities) if any code changed during the update you
170 need to reinstall the Python package. To determine the path in which
171 xrayutilities where installed previously use
172
173 python -c "import xrayutilities as xu; print xu.__file__"
174 /usr/local/lib64/python2.7/site-packages/xrayutilities/__init__.pyc
175
176 if the output is e.g.: */usr/local/lib64/python2.7/site-packages/xrayutilities/__init__.py*
177 you previously installed xrayutilities in */usr/local*, which should be used
178 again as install path. Use ::
179
180 python setup.py install --prefix=<path to install directory>
181
182 to install the updated package.
183
184
185 Documentation
186 =============
187
188 Documentation for xrayutilities is found in the *xrayutilities.pdf* file or on the
189 webpage http://xrayutilities.sourceforge.io
190
191 The API-documentation can also be browsed by
192
193 pydoc -p PORT
194
195 in any web-browser, after the installation is finished.
196
197 Platform: UNKNOWN
198 Classifier: Programming Language :: C
199 Classifier: Programming Language :: Python :: 2.7
200 Classifier: Programming Language :: Python :: 3.2
201 Classifier: Programming Language :: Python :: 3.3
202 Classifier: Programming Language :: Python :: 3.4
203 Classifier: Programming Language :: Python :: 3.5
204 Classifier: Programming Language :: Python :: 3.6
205 Classifier: Programming Language :: Python :: 3.7
206 Classifier: Topic :: Scientific/Engineering :: Physics
207 Classifier: Intended Audience :: Science/Research
208 Classifier: Development Status :: 5 - Production/Stable
209 Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
210 Provides-Extra: lzma
211 Provides-Extra: plot
212 Provides-Extra: fit
+0
-249
xrayutilities.egg-info/SOURCES.txt less more
0 CHANGES.txt
1 CONTRIBUTING.md
2 LICENSE.txt
3 MANIFEST.in
4 README.md
5 VERSION
6 release.txt
7 setup.py
8 xrayutilities.pdf
9 doc/README_sf.rst
10 doc/webpage.patch
11 doc/source/conf.py
12 doc/source/example_xu_ccd_parameter.py
13 doc/source/example_xu_ccd_parameter_hkl.py
14 doc/source/example_xu_linear_detector_parameters.py
15 doc/source/example_xu_read_spec_easy.py
16 doc/source/examples.rst
17 doc/source/index.rst
18 doc/source/modules.rst
19 doc/source/simulations.rst
20 doc/source/xrayutilities.analysis.rst
21 doc/source/xrayutilities.io.rst
22 doc/source/xrayutilities.materials.rst
23 doc/source/xrayutilities.math.rst
24 doc/source/xrayutilities.rst
25 doc/source/xrayutilities.simpack.rst
26 doc/source/_static/favicon.ico
27 doc/source/pics/fit_xrd.svg
28 doc/source/pics/line_cut_intdir.png
29 doc/source/pics/line_cut_radial.png
30 doc/source/pics/reciprocal_space_plane.png
31 doc/source/pics/rsm_xrdml.png
32 doc/source/pics/show_unitcell.png
33 doc/source/pics/xray-logo.png
34 doc/source/pics/xrd_algaas004.svg
35 doc/source/pics/xrd_sige004.svg
36 doc/source/pics/xrr_densityprofile.svg
37 doc/source/pics/xrr_diffuse.png
38 doc/source/pics/xrr_fitting.svg
39 doc/source/pics/xu_usage.svg
40 doc/source/pics/xu_usage_inkscape.svg
41 doc/source/pics/xu_usage_planning.svg
42 doc/source/pics/xu_usage_planning_inkscape.svg
43 examples/simpack_powdermodel.py
44 examples/simpack_xrd_AlGaAs.py
45 examples/simpack_xrd_Darwin_AlGaAs.py
46 examples/simpack_xrd_InAs_fitting.py
47 examples/simpack_xrd_SiGe.py
48 examples/simpack_xrd_SiGe111.py
49 examples/simpack_xrd_SiGe_asymmmetric.py
50 examples/simpack_xrd_SiGe_superlattice.py
51 examples/simpack_xrr_SiO2_Ru_CoFe_IrMn_Al2O3.py
52 examples/simpack_xrr_diffuse.py
53 examples/simpack_xrr_matrixmethod.py
54 examples/xrayutilities_angular2hkl_conversion.py
55 examples/xrayutilities_ccd_parameter.py
56 examples/xrayutilities_components_of_the_structure_factor.py
57 examples/xrayutilities_define_material.py
58 examples/xrayutilities_energy_dependent_structure_factor.py
59 examples/xrayutilities_example_plot_3D_ESRF_ID01.py
60 examples/xrayutilities_experiment_Powder_example_Iron.py
61 examples/xrayutilities_experiment_angle_calculation.py
62 examples/xrayutilities_experiment_kappa.py
63 examples/xrayutilities_export_data2vtk.py
64 examples/xrayutilities_fuzzygridding.py
65 examples/xrayutilities_hotpixelkill_variant.py
66 examples/xrayutilities_id01_functions.py
67 examples/xrayutilities_io_cif_parser.py
68 examples/xrayutilities_io_cif_parser_bi2te3.py
69 examples/xrayutilities_io_pdcif_plot.py
70 examples/xrayutilities_kmap_example_ESRF.py
71 examples/xrayutilities_linear_detector_parameters.py
72 examples/xrayutilities_materials_Alloy_contentcalc.py
73 examples/xrayutilities_math_fitting.py
74 examples/xrayutilities_orientation_matrix.py
75 examples/xrayutilities_peak_angles_beamtime.py
76 examples/xrayutilities_polefigure.py
77 examples/xrayutilities_q2ang_general.py
78 examples/xrayutilities_read_panalytical.py
79 examples/xrayutilities_read_seifert.py
80 examples/xrayutilities_read_spec.py
81 examples/xrayutilities_reflection_strength.py
82 examples/xrayutilities_user.conf
83 examples/data/BaF2.cif
84 examples/data/Calcite.cif
85 examples/data/LaB6_d500_si_psd.xye.bz2
86 examples/data/README.txt
87 examples/data/Silicon.cif
88 examples/data/bi2te3.cif
89 examples/data/inas_layer_radial_002_004.ras.bz2
90 examples/data/polefig_Ge113.xrdml.bz2
91 examples/data/rsm_1.xrdml.bz2
92 examples/data/rsm_2.xrdml.bz2
93 examples/data/rsm_3.xrdml.bz2
94 examples/data/rsm_4.xrdml.bz2
95 examples/data/rsm_5.xrdml.bz2
96 examples/data/test.spec.bz2
97 examples/data/xrr_data.txt
98 tests/README.txt
99 tests/__init__.py
100 tests/test_HXRD.py
101 tests/test_NonCOP.py
102 tests/test_alloy_content_calc.py
103 tests/test_amorphous.py
104 tests/test_analysis_linecuts.py
105 tests/test_area_calib.py
106 tests/test_blockaverage.py
107 tests/test_ccd_normalizer.py
108 tests/test_functions.py
109 tests/test_fuzzygridder1d.py
110 tests/test_fuzzygridder2d.py
111 tests/test_fuzzygridder3d.py
112 tests/test_getang.py
113 tests/test_gridder1d.py
114 tests/test_gridder2d.py
115 tests/test_gridder2dlist.py
116 tests/test_gridder3d.py
117 tests/test_io_cbf.py
118 tests/test_io_edf.py
119 tests/test_io_esg.py
120 tests/test_io_fastscan.py
121 tests/test_io_fio.py
122 tests/test_io_nja.py
123 tests/test_io_nja_map.py
124 tests/test_io_nja_tsk.py
125 tests/test_io_numor.py
126 tests/test_io_pdcif.py
127 tests/test_io_perkinelmer.py
128 tests/test_io_pilatus.py
129 tests/test_io_rigaku.py
130 tests/test_io_roperccd.py
131 tests/test_io_spec.py
132 tests/test_io_specalignmentlog.py
133 tests/test_io_speclog.py
134 tests/test_io_specsardana.py
135 tests/test_io_tty.py
136 tests/test_io_xrdml.py
137 tests/test_linear_calib.py
138 tests/test_maplog.py
139 tests/test_materials.py
140 tests/test_materials_cif.py
141 tests/test_materials_cifexport.py
142 tests/test_materials_database.py
143 tests/test_math_peak_fit.py
144 tests/test_math_solve_quartic.py
145 tests/test_math_vector.py
146 tests/test_miscut_calc.py
147 tests/test_npygridder1d.py
148 tests/test_optical_properties.py
149 tests/test_pseudomorphic.py
150 tests/test_q2angfit.py
151 tests/test_qconversion.py
152 tests/test_qconversion_area.py
153 tests/test_qconversion_linear.py
154 tests/test_qconversion_trans.py
155 tests/test_simpack_dynamicalmodel.py
156 tests/test_simpack_kinematicalmodel.py
157 tests/test_simpack_powdermodel.py
158 tests/test_simpack_xrrdiffuse.py
159 tests/test_simpack_xrrspecular.py
160 tests/test_structure_factor.py
161 tests/test_transforms.py
162 xrayutilities/__init__.py
163 xrayutilities/config.py
164 xrayutilities/exception.py
165 xrayutilities/experiment.py
166 xrayutilities/gridder.py
167 xrayutilities/gridder2d.py
168 xrayutilities/gridder3d.py
169 xrayutilities/mpl_helper.py
170 xrayutilities/normalize.py
171 xrayutilities/q2ang_fit.py
172 xrayutilities/utilities.py
173 xrayutilities/utilities_noconf.py
174 xrayutilities/xrayutilities_default.conf
175 xrayutilities.egg-info/PKG-INFO
176 xrayutilities.egg-info/SOURCES.txt
177 xrayutilities.egg-info/dependency_links.txt
178 xrayutilities.egg-info/requires.txt
179 xrayutilities.egg-info/top_level.txt
180 xrayutilities/analysis/__init__.py
181 xrayutilities/analysis/line_cuts.py
182 xrayutilities/analysis/misc.py
183 xrayutilities/analysis/sample_align.py
184 xrayutilities/io/__init__.py
185 xrayutilities/io/cbf.py
186 xrayutilities/io/desy_tty08.py
187 xrayutilities/io/edf.py
188 xrayutilities/io/fastscan.py
189 xrayutilities/io/filedir.py
190 xrayutilities/io/helper.py
191 xrayutilities/io/ill_numor.py
192 xrayutilities/io/imagereader.py
193 xrayutilities/io/panalytical_xml.py
194 xrayutilities/io/pdcif.py
195 xrayutilities/io/rigaku_ras.py
196 xrayutilities/io/rotanode_alignment.py
197 xrayutilities/io/seifert.py
198 xrayutilities/io/spec.py
199 xrayutilities/io/spectra.py
200 xrayutilities/materials/__init__.py
201 xrayutilities/materials/_create_database.py
202 xrayutilities/materials/atom.py
203 xrayutilities/materials/cif.py
204 xrayutilities/materials/database.py
205 xrayutilities/materials/elements.py
206 xrayutilities/materials/heuslerlib.py
207 xrayutilities/materials/material.py
208 xrayutilities/materials/plot.py
209 xrayutilities/materials/predefined_materials.py
210 xrayutilities/materials/spacegrouplattice.py
211 xrayutilities/materials/wyckpos.py
212 xrayutilities/materials/data/README.txt
213 xrayutilities/materials/data/atomic_radius.dat
214 xrayutilities/materials/data/colors.dat
215 xrayutilities/materials/data/elements.db
216 xrayutilities/materials/data/f0_InterTables.dat.xz
217 xrayutilities/materials/data/f0_xop.dat.xz
218 xrayutilities/materials/data/f1f2_Henke.dat.xz
219 xrayutilities/materials/data/f1f2_asf_Kissel.dat.xz
220 xrayutilities/materials/data/nist_atom.dat
221 xrayutilities/math/__init__.py
222 xrayutilities/math/algebra.py
223 xrayutilities/math/fit.py
224 xrayutilities/math/functions.py
225 xrayutilities/math/misc.py
226 xrayutilities/math/transforms.py
227 xrayutilities/math/vector.py
228 xrayutilities/simpack/__init__.py
229 xrayutilities/simpack/darwin_theory.py
230 xrayutilities/simpack/fit.py
231 xrayutilities/simpack/helpers.py
232 xrayutilities/simpack/models.py
233 xrayutilities/simpack/mosaicity.py
234 xrayutilities/simpack/powder.py
235 xrayutilities/simpack/powdermodel.py
236 xrayutilities/simpack/smaterials.py
237 xrayutilities/src/block_average.c
238 xrayutilities/src/cxrayutilities.c
239 xrayutilities/src/file_io.c
240 xrayutilities/src/gridder.h
241 xrayutilities/src/gridder1d.c
242 xrayutilities/src/gridder2d.c
243 xrayutilities/src/gridder3d.c
244 xrayutilities/src/gridder_utils.c
245 xrayutilities/src/gridder_utils.h
246 xrayutilities/src/qconversion.c
247 xrayutilities/src/qconversion.h
248 xrayutilities/src/xrayutilities.h
+0
-1
xrayutilities.egg-info/dependency_links.txt less more
0
+0
-13
xrayutilities.egg-info/requires.txt less more
0 numpy>=1.9.2
1 scipy>=0.11.0
2 h5py
3 setuptools
4
5 [fit]
6 lmfit
7
8 [lzma]
9 lzma
10
11 [plot]
12 matplotlib
+0
-1
xrayutilities.egg-info/top_level.txt less more
0 xrayutilities
Binary diff not shown