├── BUILD
├── CHANGELOG
├── README.md
├── build.sh
└── src
├── plot
└── __main__.py
└── unfolding
├── __main__.py
├── __pycache__
└── utils.cpython-34.pyc
├── errors.py
├── parse.py
├── unfolding.py
├── utils.py
└── write.py
/BUILD:
--------------------------------------------------------------------------------
1 | BUILD instructions
2 | ==================
3 |
4 | vasp_unfold is actually just a collection of Python
5 | source files. They can be bundled together into an
6 | executable archive by running the build.sh script.
7 | Once vasp_unfold is generated by build sript, it can
8 | be run independently and you can place it wherever
9 | it fits you best to use it. Since version 1.3 this
10 | script also builds plotting utility fatplot.
11 |
12 | If Python is invoked in a non-standard way on your
13 | system, please change the PYTHON_COMMAND variable
14 | in build.sh script.
15 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | Jan 23th 2015
2 | =============
3 |
4 | v1.0
5 |
6 | * Added the possiblity to parse the PROCAR file from the
7 | non-collinear spin-polarized calculation.
8 |
9 | * Added the possibility to unfold both spin up and spin
10 | down components.
11 |
12 |
13 | Jan 28th 2015
14 | =============
15 |
16 | v1.1
17 |
18 | * Refactored code into separate files. App can be packed
19 | into single executable with pack.sh script.
20 |
21 | * Spin polarized and non-collinear PROCARS are now fully
22 | parsed.
23 |
24 |
25 | Aug 18th 2015
26 | =============
27 |
28 | v1.2
29 |
30 | * Can handle vacancies and excess atoms not. One to one
31 | mapping check is now optional.
32 |
33 |
34 | Aug 31st 2015
35 | =============
36 |
37 | v1.3
38 |
39 | * Changed the way orbital weights are unfolded. Orbital
40 | weights from the non-collinear spin-polarized calculation
41 | are now fully unfolded.
42 |
43 | * Added a small plotting utility to plot fatbands.
44 |
45 | * File structure reorganized again.
46 |
47 | * Added extended error message regarding broken PROCAR
48 | formatting.
49 |
50 |
51 | Mar 4th 2016
52 | =============
53 |
54 | v1.31
55 |
56 | * Fixed the output format for the unfolded PROCAR in the case
57 | of the non-collinear calculation.
58 |
59 |
60 | Apr 25th 2017
61 | =============
62 |
63 | v1.32
64 |
65 | * Added handling of fractional translation generators components
66 | as general fractions (previously only 1/x was possible).
67 |
68 | May 30th 2017
69 | =============
70 |
71 | v1.33
72 |
73 | * Fixed improper checking of linear independence of fractional translation
74 | generators.
75 |
76 |
77 | May 31st 2017
78 | =============
79 |
80 | v1.34
81 |
82 | * Fixed bug with numpy fraction issues.
83 |
84 |
85 | December 12th 2017
86 | =============
87 |
88 | v1.4
89 |
90 | * Added handling of PROCAR files for VASP 5.4.4
91 | * Fixed problem where POSCARS with Selective Dynamics were not handled
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Bandstructure unfolding for VASP
2 |
3 | Here you can download vasp_unfold, a Python script which you can use to unfold the bandstructures obtained with VASP. This code is based on the method discussed in [Ref. 1](#ref_1).
4 |
5 | You can use the code in whatever way you see fit, but if it is used to produce the data for the publication, please cite [Ref. 1](#ref_1).
6 |
7 | In addition, a small utility called fatplot is provided to quickly plot the bandstructures contained in the VASPs PROCAR files.
8 |
9 | The code is given as is, ie. there is no guarantee that it will work. However, if you have some problems, or the code does not behave in a way you expect it to, I encourage you to report the problem to my [e-mail](mailto:tomkeus@gmail.com) and I will try to fix it as soon as possible.
10 |
11 | ## Installation
12 |
13 | Dependencies are
14 |
15 | * [Python](http://www.python.org/downloads/) ofcourse. The script requires Python2.7 or greater
16 | * [NumPy](http://www.scipy.org/scipylib/download.html) library for Python
17 | * [matplotlib](http://matplotlib.org/downloads.html) library for Python (only needed if fatplot is used)
18 |
19 | No special installation is required. Just place it wherever it suits you and run it.
20 |
21 | ## Usage
22 |
23 | vasp_unfold works as a postprocessing tool. The unfolding is achieved through manipulation of orbital weights. Basically, in the unfolded bandstructure, orbital weights of some bands are set to zero. This means that **the effect of unfolding can only be seen with the fatband plots**. In order for vasp_unfold to be able to work, the phase information must be present in the PROCAR file which means that **LORBIT=12 must be set in the INCAR file**.
24 |
25 | The workflow is as follows: once the bandstructure is calculated with VASP, the vasp_unfold is operated on the PROCAR file obtained in the bandstructure calculation, whereby a number of PROCAR files is produced, containing the unfolded bandstructure. Every PROCAR file generated in the output contains projection to one of the irreps as explained in [Ref. 1](#ref_1).
26 |
27 | The command line usage of vasp_unfold is following
28 |
29 | ```
30 | usage: vasp_unfold [-h] [--tgen SX,SY,SZ] [--out OUT] [--eps EPS]
31 | [--all-irreps] [--check-mapping]
32 | poscar procar
33 | ```
34 |
35 | where the parameters are
36 |
37 | ```
38 | --tgen Fractional translation generator
39 | --out Output filename prefix
40 | --eps Numerical tolerance for position discrimination
41 | --all-irreps Write all irreps from the unfolding
42 | --check-mapping Verify if fractional translations map atoms one-to-one
43 | --vasp-version Which version of VASP was used to produce the PROCAR file
44 | poscar Location of POSCAR file
45 | procar Location of PROCAR file
46 | ```
47 |
48 | Let us say that we have a 2x3x1 supercell. In total we have six unit cells contained within the supercell. To every unit cell in the supercell, corresponds one fractional translation (relative to the supercell), so that we have six fractional translations in total. These six fractional translations can be generated from two fractional translations given by vectors (1/2,0,0) and (0,1/3,0). We can then unfold the supercell bandstructure with
49 |
50 | ```
51 | vasp_unfold --tgen 1/2,0,0 --tgen 0,1/3,0 POSCAR PROCAR
52 | ```
53 |
54 | The unfolded bandstructures will be located in PROCAR.irrep.0 file. In case --all-irreps flag was specified, the unfolded bandstructure will be located in PROCAR.irrep.0 through PROCAR.irrep.5 files.
55 |
56 | **NOTE 1**: No whitespace is allowed in the fractional translation generator specification. Also, the components can be either 0, or 1/N, where N is an integer. Floating point values are not allowed.
57 |
58 | **NOTE 2**: Do not enable --check-mapping flag if your structure has vacancies or excess atoms, since in this case fractional translations do not map every atom onto some other atom.
59 |
60 | **NOTE 3**: Numerical tolerance for position discrimination, --eps, is not specified in the units of your unit cell (ie. Angstroms). Instead it is given in the fractional units.
61 |
62 | **NOTE 4**: Unfolding of the k-path is **NOT** automatic! This means, that you have to manually specify the k-path in the Brillouin zone of your supercell. Fortunately, that is easy to do! If you have a n1 x n2 x n3 supercell, you just need to multiply every k-point's 1st, 2nd and 3rd components by n1, n2 and n3 respectively (when the KPOINTS file is in the reciprocal mode).
63 |
64 | ## Resolving the issues with the code
65 |
66 | Here is a little advice pertaining to the "Translations are not one-to-one" error when --chek-mapping flag is enabled. This problem arises because the vasp_unfold script tries to figure out which atoms are mapped onto which atoms under the action of the fractional translations. If the supercell would be perfectly symmetrical under the fractional translations, this issue would not occur. However, in real life, the supercell will usually break this translational symmetry which means that atoms wont be mapped exactly onto each other by the fractional translations.
67 |
68 | The resolution to this problem is quite simple. If increase of the numerical tolerance parameter does not give any result, simply use the POSCAR file where the atomic positions are restored to their symmetryc sites. Practically, if your supercell is a result of the structural optimization, use the initial POSCAR file instead of the final one.
69 |
70 | ## References
71 |
72 | 1. M. Tomić, H. O. Jeschke and R. Valentí - Unfolding of the electronic structure through the induced representations of space groups: Application to Fe-based superconductors, [Phys. Rev. **B** 90, 195121 (2014)](http://journals.aps.org/prb/abstract/10.1103/PhysRevB.90.195121) ([arXiv preprint](http://arxiv.org/abs/1408.2258))
73 |
74 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # This scripts packs the Python source files
4 | # into an executable ZIP archive. This is done
5 | # separatly for the vasp_unfold and
6 |
7 | # Names for the executables
8 | VASP_UNFOLD="vasp_unfold"
9 | PLOT="fatplot"
10 |
11 | # Command used to invoke Python
12 | PYTHON_COMMAND="python"
13 |
14 | # Command used to invoke env
15 | ENV_COMMAND="/usr/bin/env"
16 |
17 |
18 | SRC_FILES="__main__.py parse.py unfolding.py utils.py write.py errors.py"
19 | PLOT_SRC_FILES="__main__.py"
20 |
21 | # Change into source directory
22 | cd src
23 |
24 | # Zip the source files
25 | cd unfolding; zip ../../${VASP_UNFOLD}.zip ${SRC_FILES}; cd ..;
26 | cd plot; zip ../../${PLOT}.zip ${PLOT_SRC_FILES}; cd ..;
27 |
28 | cd ..
29 |
30 | # Prepend shebang to the zip archive
31 | echo "#! ${ENV_COMMAND} ${PYTHON_COMMAND}" | cat - ${VASP_UNFOLD}.zip > ${VASP_UNFOLD}
32 | echo "#! ${ENV_COMMAND} ${PYTHON_COMMAND}" | cat - ${PLOT}.zip > ${PLOT}
33 |
34 | # Remove zip archive
35 | rm ${VASP_UNFOLD}.zip
36 | rm ${PLOT}.zip
37 |
38 | # Make it executable
39 | chmod +x ${VASP_UNFOLD}
40 | chmod +x ${PLOT}
41 |
--------------------------------------------------------------------------------
/src/plot/__main__.py:
--------------------------------------------------------------------------------
1 | #===========================================================
2 | #
3 | # PROJECT: vasp_unfold/fatplot
4 | # FILE: __main__.py
5 | # AUTHOR: Milan Tomic
6 | # EMAIL: tomic@th.physik.uni-frankfurt.de
7 | # VERSION: 1.32
8 | # DATE: Apr 25th 2017
9 | #
10 | #===========================================================
11 |
12 |
13 | import os
14 | import numpy as np
15 | import argparse
16 |
17 | import matplotlib
18 | matplotlib.use('Agg')
19 | import matplotlib.pyplot as plot
20 |
21 | # Handle color arguments
22 | def color(string):
23 | try:
24 | # Try to convert to tuple
25 | return eval(string)
26 | except:
27 | # If cannot convert to tuple just
28 | # return the original string
29 | return string
30 |
31 | desc_str = '''Simple program used to quickly plot the orbital weights
32 | of the band structure contained in the specified PROCAR file.
33 | '''
34 |
35 | parser = argparse.ArgumentParser(prog='fatplot', description = desc_str)
36 |
37 | parser.add_argument('procar', type=str, help='POSCAR file')
38 | parser.add_argument('output', type=str, help='Filename for the plot. It accepts '
39 | 'all image formats supported by matplotlib.')
40 |
41 | parser.add_argument('--figsize', type=eval, default=(6, 4),
42 | help='Figure size in inches formatted as width,height '
43 | '(no spaces allowed). Default is 6,4')
44 | parser.add_argument('--dpi', type=float, default=300,
45 | help='Plot resolution in dots per inch. Default is 300.')
46 | parser.add_argument('--marker', type=str, default='o',
47 | help='Marker for the fatband plot. Default is o.')
48 | parser.add_argument('--markersize', type=float, default=20,
49 | help='Marker size. Default is 20.')
50 | parser.add_argument('--color', type=color, default='"steelblue"',
51 | help='Color for the marker. It accepts any color specification '
52 | 'accepted by matplotlib. Color specified as r,g,b tuple should '
53 | 'not have any spaces.')
54 | parser.add_argument('--elim', type=eval, default=(1,-1),
55 | help='Energy range for the plot, specified as emin,emax '
56 | '(no spaces allowed). Default is entire band range.')
57 | parser.add_argument('--efermi', type=float, default=0,
58 | help='Fermi energy. Default is 0.')
59 | parser.add_argument('--pow', type=float, default=1,
60 | help='Raise orbital weights to the specified integer power. '
61 | 'Powers larger than 1 help to filter out the ghost bands '
62 | 'in the unfolded band structures.')
63 |
64 | args = parser.parse_args()
65 |
66 |
67 | # Commands to extract the needed information from the PROCAR file
68 | grep_npoints = 'grep -m 1 "of k\-points" {0} | tr -s " " | cut -d" " -f4'
69 | grep_kpoints = 'grep -E "^ k\-point " {0} | tr -s " " | cut -d" " -f5-7'
70 | grep_bands = 'grep -E "^band" {0} | tr -s " " | cut -d" " -f5'
71 | grep_weights = 'grep -E "^tot" {0} | tr -s " " | cut -d" " -f11'
72 |
73 | # Extract the number of k-points
74 | npoints = int(os.popen(grep_npoints.format(args.procar)).read())
75 |
76 | # Extract the k-points
77 | kpoints = os.popen(grep_kpoints.format(args.procar))
78 | kpoints = np.fromfile(kpoints, count=3*npoints, dtype=float, sep=' ')
79 |
80 | # Extract the band energies
81 | bands = os.popen(grep_bands.format(args.procar))
82 | bands = np.fromfile(bands, count=-1, dtype=float, sep=' ')
83 |
84 | # Extract the total orbital weights for each band
85 | weights = os.popen(grep_weights.format(args.procar))
86 | weights = np.fromfile(weights, count=-1, dtype=float, sep=' ')
87 |
88 | # Figure out the number of bands
89 | nbands = len(bands)/npoints
90 |
91 | # Figure out the number of orbital blocks per band
92 | # 1 for collinear calculation, 4 for non-collinear
93 | # In non-collinear case we just need the first one
94 | wdim = len(weights)/(npoints*nbands)
95 |
96 | # Reshape the arrays into their proper shapes
97 | kpoints = kpoints.reshape((npoints, 3))
98 | bands = bands.reshape((npoints, nbands))-args.efermi
99 | weights = weights[::wdim].reshape((npoints, nbands))
100 |
101 | # Raise the weights to the specified power
102 | if args.pow != 1:
103 | weights = np.power(weights, args.pow)
104 |
105 |
106 | # Try to guess where the high symmetry points
107 | # are by looking for the repeated k-points
108 | dk = np.zeros((npoints, 3), float)
109 |
110 | # Displacement between i+1st and ith k-point
111 | dk[1:] = kpoints[1:]-kpoints[:-1]
112 |
113 | # Get the magnitude of displacements
114 | dk = np.sqrt(np.sum(dk*dk, axis=1))
115 |
116 | # Locate the high-symmetry points
117 | ipoint = np.where(dk < 1e-6)[0]
118 |
119 | # Generate plot's x-axis from the adjacent k-point
120 | # displacement magnitudes
121 | x = np.cumsum(dk)
122 |
123 | # Get the plot's x-coordinates for high-symmetry k-points
124 | xsym = x[ipoint]
125 |
126 | # Figure out the maximum possible energy boundaries
127 | # based on the energy extend of all bands
128 | e_low = np.min(bands)-1.0
129 | e_high = np.max(bands)+1.0
130 |
131 | plot.figure(figsize=args.figsize)
132 |
133 | # Plot horizontal line for the Fermi level
134 | plot.plot(x[[0,-1]], [0, 0], color='gray', zorder=-1)
135 |
136 | # Plot vertical lines for the high-symmetry points
137 | for xi in xsym:
138 | plot.plot([xi, xi], [e_low, e_high], color='gray', zorder=-1)
139 |
140 | # Plot the weights
141 | for bi, wi in zip(bands.T, weights.T):
142 | plot.scatter(x, bi, s=args.markersize*wi, marker=args.marker,
143 | color=args.color, lw=0)
144 |
145 | # Fix x and y-axis boundaries
146 | plot.xlim(x[0], x[-1])
147 |
148 | if args.elim[0] < args.elim[1]:
149 | plot.ylim(*args.elim)
150 | else:
151 | plot.ylim(e_low, e_high)
152 |
153 | # Set x-axis ticks
154 | plot.xticks(xsym, ['']*len(xsym))
155 |
156 | # Save the figure
157 | plot.savefig(args.output, dpi=args.dpi, bbox_inches='tight')
158 |
159 |
160 |
--------------------------------------------------------------------------------
/src/unfolding/__main__.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 |
4 | #============================================================================
5 | #
6 | # PROJECT: vasp_unfold
7 | # FILE: __main__.py
8 | # AUTHOR: Milan Tomic
9 | # EMAIL: tomic@th.physik.uni-frankfurt.de
10 | # VERSION: 1.4
11 | # DATE: December 12th 2017
12 | #
13 | #
14 | # vasp_unfold is a code whose purpose is to perform the unfolding
15 | # of band structures calculated with VASP. It is based on the method
16 | # described in (Phys. Rev. B 90, 195121). If this code have been used to
17 | # obtain the data in your publication please cite the given reference.
18 | #
19 | # This code is provided as is. There is no guarantee that it will work.
20 | # However, if you encounter errors or unexpected behavor, please report
21 | # it to tomic@itp.uni-frankfurt.de and I will try to improve it.
22 | #
23 | #============================================================================
24 |
25 |
26 | import numpy as np
27 | import argparse
28 | import sys
29 | from utils import post_error, translation, version
30 | from unfolding import build_translations, build_operators, build_projectors
31 | from parse import parse_poscar, parse_procar
32 | from write import write_procar
33 | import errors
34 |
35 | def main():
36 | desc_str = 'Unfold bands calculated by VASP. For this, phase '\
37 | 'information needs to be present in the PROCAR file '\
38 | 'which means that bands need to be calculated with '\
39 | 'the option LORBIT=12 specified in the INCAR file. '\
40 | 'The required input consists of POSCAR file, PROCAR '\
41 | 'file and a set of up to three fractional translations. '\
42 | 'There is no need to specify all translations, just the '\
43 | 'generators. The programs will generate all distinct '\
44 | 'translations from the generators and generate all irreps. '\
45 | 'NOTE: In cas of non-collinear spin-polarized calculations ' \
46 | 'mx, my and mz components of orbital weights are not ' \
47 | 'unfolded, only spin up and spin down totals.'
48 |
49 |
50 | parser = argparse.ArgumentParser(prog='vasp_unfold', description = desc_str)
51 |
52 | parser.add_argument('poscar', type=str, help='POSCAR file')
53 | parser.add_argument('procar', type=str, help='PROCAR file')
54 |
55 | parser.add_argument('--tgen', type=translation, action='append',
56 | metavar='SX,SY,SZ', help='Fractional translation '
57 | 'generator. No whitespaces are allowed between the '
58 | 'components! SX, SY and SZ can be either 0 or 1/n, '
59 | 'where n is an integer. Up to three linearly '
60 | 'independant generators can be specified.')
61 |
62 | parser.add_argument('--out', type=str, help='Output filename. If left '
63 | 'unspecified output is writen to PROCAR.irrep.n '
64 | 'where PROCAR is location of the input PROCAR file. '
65 | 'If specified, output is written to OUT.irrep.n.')
66 |
67 | parser.add_argument('--eps', type=float, default=1e-6, help='Numerical '
68 | 'precision. When building permutation representation '
69 | 'of the fractional translations this parameter is used '
70 | 'to determine whether two fractional positions are '
71 | 'identical. For irregular structures, it may need to '
72 | 'be increased. If this does not help, try to tweak '
73 | 'the atomic positions in POSCAR to make the structure '
74 | 'more regular. Default is 1e-6.')
75 |
76 | parser.add_argument('--all-irreps', default=False, action='store_true',
77 | help='Flag specifying that all irreps from the unfolding '
78 | 'will be written to the output. By default, only irrep 0 '
79 | '(unit irrep) is written out.')
80 |
81 | parser.add_argument('--check-mapping', action='store_true', default=False,
82 | help='Specifies to check if supplied translation '
83 | 'generators generate fractional translations which are '
84 | 'one-to-one, ie. map every atom on exactly one other atom '
85 | 'in the unit cell. This MUST not be enabled for the cases '
86 | 'where vacancies or excess atoms are present.')
87 |
88 | parser.add_argument('--vasp-version', type=version, default='5.2.2', help='Version of VASP'
89 | 'that produced the PROCAR file. All versions prior to 5.4.4'
90 | 'use the same format. Since 5.4.4 there is a new format.')
91 |
92 | args = parser.parse_args()
93 |
94 | tgens = args.tgen
95 |
96 | trans, irreps = build_translations(tgens)
97 |
98 | try:
99 | cell, spos, symbols = parse_poscar(args.poscar)
100 | except:
101 | post_error('Unable to parse the input POSCAR file. Please '
102 | 'check if the file exists and is formatted properly.', True)
103 |
104 | ops = build_operators(spos, trans, args.check_mapping, args.eps)
105 |
106 | try:
107 | data = parse_procar(args.procar, args.vasp_version)
108 | except Exception as exc:
109 | post_error(errors.poscar_parse_error, True)
110 |
111 | if data[-1] is None:
112 | post_error('Phase information has to be present in the PROCAR '
113 | 'file. Please repeat the calculation with LORBIT=12.', True)
114 |
115 | phases = np.copy(data[-1])
116 |
117 | norbs = phases.shape[1]/len(spos)
118 |
119 | projs = build_projectors(irreps, ops, norbs)
120 |
121 | if args.out is None:
122 | output = args.procar
123 | else:
124 | output = args.out
125 |
126 | if args.all_irreps:
127 | nirrep = len(projs)
128 | else:
129 | nirrep = 1
130 |
131 | for i, p in enumerate(projs[:nirrep]):
132 | for s in xrange(data[-1].shape[-1]):
133 | try:
134 | data[-1][:,:,:,s] = np.dot(p, phases[:,:,:,s]).swapaxes(0, 1)
135 | except:
136 | post_error('Unable to apply projectors. Are you sure '
137 | 'that specified POSCAR and PROCAR file belong to '
138 | 'the same crystal structure?')
139 |
140 | # We update total absolute weights to correspond to
141 | # unfolded phases (by multiplying them by the magnitude
142 | # ratio of unfolded and folded phases
143 | phase_ratio = np.abs(data[-1])/(np.abs(phases)+1e-4)
144 |
145 | for idim in xrange(data[-2].shape[3]):
146 | data[-2][:,:,:,idim,:] *= phase_ratio
147 |
148 | write_procar('{0}.irrep.{1}'.format(output, i), *data)
149 |
150 |
151 | if __name__ == '__main__':
152 | main()
153 |
154 |
155 |
--------------------------------------------------------------------------------
/src/unfolding/__pycache__/utils.cpython-34.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tomkeus/vasp_unfold/d5ce631cc33e84803920c6191d5cf26c50eab58f/src/unfolding/__pycache__/utils.cpython-34.pyc
--------------------------------------------------------------------------------
/src/unfolding/errors.py:
--------------------------------------------------------------------------------
1 | #===========================================================
2 | #
3 | # PROJECT: vasp_unfold
4 | # FILE: __errors__.py
5 | # AUTHOR: Milan Tomic
6 | # EMAIL: tomic@th.physik.uni-frankfurt.de
7 | # VERSION: 1.4
8 | # DATE: December 12th 2017
9 | #
10 | #===========================================================
11 |
12 | poscar_parse_error = """Unable to parse the input PROCAR file. Please check if the
13 | PROCAR file is properly formatted.
14 |
15 | PLEASE READ CAREFULLY
16 |
17 | Beware that output formatting in VASP is broken. Output field
18 | widths are not set to a large enough value in the source code.
19 | As a result, two common types of errors occur:
20 |
21 | 1. When a k-point has negative component, k-point output merges
22 | into a single string, for example
23 |
24 | k-point 75 : 0.25510204-0.74489796-0.00000000 weight = 0.00500000
25 | ^ ^
26 | error error
27 |
28 | HOW TO FIX:
29 |
30 | Use sed to insert spaces between the components
31 |
32 | sed "s/\([0-9]\)\-\([0-9]\)/\\1 -\\2/g" PROCAR.broken > PROCAR.fixed
33 |
34 |
35 | 2. Orbital weight/phase precision requires field width larger than
36 | the field width allows. As a result, instead of orbital weight/
37 | phase, asterisks are written. For example
38 |
39 | ion s py pz px dxy dyz dz2 dxz dx2
40 | 1 0.000 -0.292 0.000 -0.503 0.000 0.001 0.000 -0.001 0.000
41 | 1 0.000 -1.286 0.000 0.049 0.000 0.003 0.000 0.000 0.000
42 | 2 0.000 0.292 0.000 0.503 0.000 0.001 0.000 -0.001 0.000
43 | 2 0.000 1.286 0.000 -0.049 0.000 0.003 0.000 0.000 0.000
44 | 3 0.000 -0.003 0.000 0.006 0.000 0.002 0.000 -0.003 0.000
45 | 3 0.000 -0.015 0.000 -0.001 0.000 0.008 0.000 0.000 0.000
46 | 4 ***** 0.003 0.000 -0.006 0.000 0.002 0.000 -0.003 0.000
47 | 4 ***** 0.015 0.000 0.001 0.000 0.008 0.000 0.000 0.000
48 | ^
49 | errors
50 |
51 | HOW TO FIX:
52 |
53 | Use sed to replace every asterisk with a zero
54 |
55 | sed "s/\*/0/g" PROCAR > PROCAR.edited
56 |
57 |
58 | So far no other sources of errors have been encountered. In order
59 | to solve them for good, either edit VASPs source code or bug VASPs
60 | developer until they fix it.
61 | """
62 |
--------------------------------------------------------------------------------
/src/unfolding/parse.py:
--------------------------------------------------------------------------------
1 | #===========================================================
2 | #
3 | # PROJECT: vasp_unfold
4 | # FILE: parse.py
5 | # AUTHOR: Milan Tomic
6 | # EMAIL: tomic@th.physik.uni-frankfurt.de
7 | # VERSION: 1.4
8 | # DATE: December 12th 2017
9 | #
10 | #===========================================================
11 |
12 | import numpy as np
13 | from utils import Getlines, post_error
14 |
15 | def parse_poscar(filename):
16 | '''Parses POSCAR file. Returns 3x3 float array
17 | containing unit cell vectors as rows, Nx3
18 | array containing fractional positions of atoms,
19 | N being number of atoms and a list of chemical
20 | symbols.
21 | '''
22 | try:
23 | gl = Getlines(filename)
24 | except:
25 | post_error('Unable to open "{0}" for reading.'.format(filename))
26 |
27 | # Skip the comment line
28 | gl.readline()
29 |
30 | # Read the scaling factor
31 | f = float(gl.readline())
32 |
33 | # Read the unit cell vectors
34 | cell = np.zeros((3, 3), float)
35 |
36 | cell[0] = f*np.array(gl.readline().split(), float)
37 | cell[1] = f*np.array(gl.readline().split(), float)
38 | cell[2] = f*np.array(gl.readline().split(), float)
39 |
40 | # Read the chemical symbols
41 | syms = gl.readline().split()
42 |
43 | # Read the atom counts
44 | counts = np.array(gl.readline().split(), int)
45 |
46 | # Get the number of atoms
47 | natoms = np.sum(counts)
48 |
49 | # Rearrange into the long list of chemical symbols
50 | symbols = []
51 |
52 | for s, c in zip(syms, counts):
53 | symbols += [s]*c
54 |
55 | # Cartesian or fractional coordinates?
56 | ctype = gl.readline()[0].lower()
57 |
58 | if ctype == 's':
59 | ctype = gl.readline()[0].lower()
60 |
61 | if ctype == 'c':
62 | mult = np.linalg.inv(cell)
63 | elif ctype == 'd':
64 | mult = np.eye(3)
65 | else:
66 | post_error('"{0}" is unknown POSCAR option'.format(ctype))
67 |
68 | # Allocate storage for positions
69 | spos = np.zeros((len(symbols), 3))
70 |
71 | # Read the positions
72 | for i in xrange(len(symbols)):
73 | spos[i] = np.array(gl.readline().split()[:3], float)
74 |
75 | # If necessary, this will convert from
76 | # Cartesian to fractional coordinates
77 | spos = np.dot(spos, mult)
78 |
79 | return cell, spos, symbols
80 |
81 |
82 | def parse_procar(filename, vasp_version):
83 | '''This function parses a PROCAR file. It returns a tuple
84 | consisting of following elements:
85 |
86 | orbitals - (norbs) string array of orbital labels (s, px, py etc...)
87 | kpoints - (npoints,3) float array of k-point coordinates
88 | kweights - (npoints) float array of k-point weights
89 | bands - (npoints,nbands,nspin) float array of band energies
90 | occupancies - (npoints,nbands,nspin) float array of band occupancies
91 | weights - (npoints,nions*norbs,nbands,ndim,nspin) float array
92 | of orbital weights
93 | phases - (npoints,nions*norbs,nbands,nspin) complex array of
94 | phases of orbital weights if LORBIT=12, otherwise None
95 |
96 | Where:
97 |
98 | norbs - number of orbitals (It can be 9 or 16 with f orbitals)
99 | npoints - number of k-points
100 | nbands - number of bands
101 | nspin - number of spins (1 for non spin-polarized, 2 otherwise)
102 | nions - number of atoms
103 | ndim - orbital weight dimensionality (1 for collinear, 4 otherwise)
104 | '''
105 |
106 | try:
107 | gl = Getlines(filename)
108 | except:
109 | post_error('Unable to open "{0}" for reading.'.format(filename))
110 |
111 | header_1 = gl.readline()
112 | header_2 = gl.readline().split()
113 |
114 | npoints = int(header_2[3])
115 | nbands = int(header_2[7])
116 | nions = int(header_2[-1])
117 |
118 | # Remember the position in file
119 | start = gl.tell()
120 |
121 | # Skip two lines containing first k-point and band
122 | gl.readline()
123 | gl.readline()
124 |
125 | # Determine the number of orbitals
126 | orbitals = gl.readline().split()[1:-1]
127 |
128 | norbs = len(orbitals)
129 |
130 | # Allocate maximal storage. In case calculation was
131 | # non spin-polarized or collinear we can just trim
132 | # the excess components at the end
133 | kpoints = np.zeros((npoints, 3), float)
134 | kweights = np.zeros(npoints, float)
135 | bands = np.zeros((npoints, nbands, 2), float)
136 | occupancies = np.zeros((npoints, nbands, 2), float)
137 | weights = np.zeros((npoints, nions*norbs, nbands, 4, 2), float)
138 |
139 | dim = 0
140 |
141 | # Determine if the calculation was non-collinear
142 | # by counting how many lines in the first band
143 | # block begin with tot. That number will be equal
144 | # to the number of sub-blocks for orbital weights
145 | # (1 in case of collinear and 4 otherwise)
146 | while True:
147 | line = gl.readline()
148 |
149 | if line.startswith('tot'):
150 | dim += 1
151 | elif line.startswith('band'):
152 | break
153 |
154 | # This function will read block of absolute weights
155 | # for i-th k-point, j-th band and s-th spin component
156 | def get_absweights(i, j, s):
157 | # Skip line with orbital names
158 | gl.readline()
159 |
160 | for k in xrange(dim):
161 | # Fetch entire orbital weight block
162 | data = np.fromfile(gl, sep=" ", count=nions*(norbs+2))
163 |
164 | # Cast it into tabular shape
165 | data = data.reshape((nions, norbs+2))
166 |
167 | # Discard first and last columns and store weights
168 | weights[i,:,j,k,s] = data[:,1:-1].flatten()
169 |
170 | # Skip line with the totals
171 | gl.readline()
172 |
173 | # Check whether phase information is included
174 | if '+ phase' in header_1:
175 | # Allocate storage for phases
176 | phases = np.zeros((npoints, nions*norbs, nbands, 2), complex)
177 |
178 | if vasp_version < (5, 4, 4):
179 | # Declare nested function that handles
180 | # parsing of complex weights
181 | def get_weights(i, j, s):
182 | # Read abs values of weights
183 | get_absweights(i, j, s)
184 |
185 | # Skip line with orbital names
186 | gl.readline()
187 |
188 | # Fetch entire phase block
189 | data = np.fromfile(gl, sep=" ", count=2*nions*(norbs+1))
190 | # Cast it into tabular shape
191 | data = data.reshape((2*nions, norbs+1))
192 |
193 | # Discard first column and store real and imaginary
194 | # parts respectively
195 | phases[i,:,j,s] = data[::2,1:].flatten()
196 | phases[i,:,j,s] += 1j*data[1::2,1:].flatten()
197 | else:
198 | # Declare nested function that handles
199 | # parsing of complex weights
200 | def get_weights(i, j, s):
201 | # Read abs values of weights
202 | get_absweights(i, j, s)
203 |
204 | # Skip line with orbital names
205 | gl.readline()
206 |
207 | # Fetch entire phase block
208 | data = np.fromfile(gl, sep=" ", count=nions*(2*norbs+2))
209 | # Cast it into tabular shape
210 | data = data.reshape((nions, 2*norbs+2))
211 |
212 | # Discard first column and store real and imaginary
213 | # parts respectively
214 | phases[i,:,j,s] = data[:,1:-1:2].flatten()
215 | phases[i,:,j,s] += 1j*data[:,2:-1:2].flatten()
216 |
217 | # Skip line with charges
218 | gl.readline()
219 | else:
220 | # Phases are None in this case
221 | phases = None
222 |
223 | # In this case we just have absolutes of weights
224 | # No need for a new function
225 | get_weights = get_absweights
226 |
227 | # Go back to the beginning of the first k-point
228 | gl.seek(start)
229 |
230 | for i in xrange(npoints):
231 | # Parse k-point coordinates
232 | k_line = gl.readline().split()
233 |
234 | kpoints[i] = [float(k_line[c]) for c in [3, 4, 5]]
235 | kweights[i] = float(k_line[-1])
236 |
237 | for j in xrange(nbands):
238 | # Parse band energy
239 | band_line = gl.readline().split()
240 |
241 | bands[i, j, 0] = float(band_line[4])
242 | occupancies[i, j, 0] = float(band_line[-1])
243 |
244 | # Parse orbital weights
245 | get_weights(i, j, 0)
246 |
247 | # Seek now for the second spin component
248 | res = gl.readline(False)
249 |
250 | # If there is no second spin component, finish by
251 | # returning just the first component
252 | if res is None:
253 | # Trim the second spin component
254 | bands = bands[:,:,:1]
255 | occupancies = occupancies[:,:,:1]
256 | weights = weights[:,:,:,:dim,:1]
257 |
258 | if phases is not None:
259 | phases = phases[:,:,:,:1]
260 |
261 | return [orbitals, kpoints, kweights, bands, occupancies, \
262 | weights, phases]
263 |
264 | # Otherwise, read bands and weights for the second component
265 | for i in xrange(npoints):
266 | # Skip k-point coordinates
267 | gl.readline()
268 |
269 | for j in xrange(nbands):
270 | # Parse band energy
271 | band_line = gl.readline().split()
272 |
273 | bands[i, j, 1] = float(band_line[4])
274 | occupancies[i, j, 1] = float(band_line[-1])
275 |
276 | # Parse orbital weights
277 | get_weights(i, j, 1)
278 |
279 | return [orbitals, kpoints, kweights, bands, occupancies, \
280 | weights[:,:,:,:dim,:], phases]
281 |
282 |
283 |
--------------------------------------------------------------------------------
/src/unfolding/unfolding.py:
--------------------------------------------------------------------------------
1 | #===========================================================
2 | #
3 | # PROJECT: vasp_unfold
4 | # FILE: unfolding.py
5 | # AUTHOR: Milan Tomic
6 | # EMAIL: tomic@th.physik.uni-frankfurt.de
7 | # VERSION: 1.34
8 | # DATE: May 31st 2017
9 | #
10 | #===========================================================
11 |
12 | import numpy as np
13 | from utils import post_error, frac_translation_order
14 |
15 |
16 | def build_operators(spos, trans, check_mapping=False, eps=1e-6):
17 | '''Given a list of fractional positions, and a list of
18 | fractional translations, produce a set of matrices,
19 | describing how fractional translations permute atomic
20 | positions within the unit cell. Two fractional positions
21 | si and sj are considered to be identical when |si-sj| 3:
57 | post_error('There can be at most three generators '
58 | 'of fractional translations.')
59 |
60 | # Use folded generators as floats to perform linear independece tests
61 | tgensf = np.array(tgens, dtype=float)%1
62 |
63 | # Tolerance for linear independenec per dimension
64 | eps = 1e-2
65 |
66 | # Check if generators are linearly independent
67 | if len(tgens) == 2 and np.all(np.cross(tgensf[0], tgensf[1]) < eps**2):
68 | post_error('Generators are not linearly independant.')
69 | elif len(tgens) == 3 and np.linalg.det(np.array(tgensf)) < eps**3:
70 | post_error('Generators are not linearly independant.')
71 |
72 | # Expand the generator list to be a 3x3 matrix
73 | tgens = np.append(tgens, np.ones((3-len(tgens), 3)), axis=0)
74 |
75 | # Get the order of every generator
76 | order = np.array([frac_translation_order(g) for g in tgens], int)
77 |
78 | # Get the corresponding roots of unity which will be
79 | # used to construct irreps
80 | deltag = np.exp(-2*np.pi*1j/order)
81 |
82 | # Fold the translation vectors into the unit cell
83 | tgens = tgens % 1
84 |
85 | # Total number of translations
86 | ntrans = np.prod(order)
87 |
88 | # Storage for translations
89 | trans = np.zeros((ntrans, 3), float)
90 |
91 | # Calculate irreps of every generator
92 | irrepg = np.zeros((ntrans, 3), complex)
93 |
94 | # Loop over all possible products of powers of generators
95 | # and store the irreps
96 | for i in xrange(order[0]):
97 | for j in xrange(order[1]):
98 | for k in xrange(order[2]):
99 | ii = i*order[1]*order[2]+j*order[2]+k
100 |
101 | irrepg[ii] = deltag**[i, j, k]
102 |
103 | # Irrep table for all translations
104 | irreps = np.zeros((ntrans, ntrans), complex)
105 |
106 | # Loop over all posible products of powers of generators
107 | for i in xrange(order[0]):
108 | for j in xrange(order[1]):
109 | for k in xrange(order[2]):
110 | ti = i*order[1]*order[2]+j*order[2]+k
111 |
112 | # Get the translation vector
113 | trans[ti] = np.dot([i, j, k], tgens)%1
114 |
115 | # Get all irreps of that translations
116 | for l in xrange(ntrans):
117 | irreps[l, ti] = np.prod(irrepg[l]**[i, j, k])
118 |
119 | return trans, irreps
120 |
121 |
122 | def build_projectors(irreps, ops, intdim=1):
123 | '''Builds irrep projection operators, given irreps
124 | and corresponding translation operator matrices.
125 | Intdim specifies how many orbitals per atomic site
126 | there are.
127 | '''
128 | projs = np.zeros((len(irreps), ops.shape[1], ops.shape[2]), complex)
129 |
130 | # Loop over irreps
131 | for i, ii in enumerate(irreps):
132 | # Sum operators multiplied by their irrep
133 | for j, oj in enumerate(ops):
134 | projs[i] += irreps[i, j]*oj
135 |
136 | # Expand onto the orbital space and normalize
137 | return np.kron(projs, np.eye(intdim))/len(ops)
138 |
--------------------------------------------------------------------------------
/src/unfolding/utils.py:
--------------------------------------------------------------------------------
1 | #===========================================================
2 | #
3 | # PROJECT: vasp_unfold
4 | # FILE: utils.py
5 | # AUTHOR: Milan Tomic
6 | # EMAIL: tomic@th.physik.uni-frankfurt.de
7 | # VERSION: 1.4
8 | # DATE: December 12th 2017
9 | #
10 | #===========================================================
11 |
12 | import numpy as np
13 | import argparse
14 | import sys
15 | import fractions
16 | import traceback
17 |
18 |
19 | def post_error(error_info, show_traceback=False):
20 | '''Write error message to sderr and exit program
21 | '''
22 | message = '\nError: '+error_info+'\n\n'
23 |
24 | if show_traceback:
25 | # Show exception traceback
26 | tb_msg = traceback.format_exc()
27 |
28 | max_line_len = max(len(line) for line in tb_msg.split('\n'))
29 |
30 | message += 'Operation failed due to the following exception:\n'
31 | message += '='*max_line_len
32 | message += '\n' + traceback.format_exc()
33 | message += '='*max_line_len
34 |
35 | sys.stderr.write(message)
36 |
37 | exit()
38 |
39 |
40 | class Getlines(file):
41 | '''Small wrapper of the Python's built-in file
42 | class. It's purpose is to skip empty lines
43 | while reading the file'''
44 |
45 | def __init__(self, fname, comment=None):
46 | '''Constructor opens the file in the read mode'''
47 | super(Getlines, self).__init__(fname, 'r')
48 | self.comment = comment
49 |
50 |
51 | def __advance__(self, advance_func, eof_error):
52 | '''Advances along file line by line, skipping empty
53 | lines and stripping trailing and leading whitspaces
54 | and comments. advance_func specifies function which
55 | provides contents of next line. If eof_error is True
56 | error will be posted and execution terminated if end
57 | of file is reached.
58 | '''
59 | line = ''
60 |
61 | while len(line) == 0:
62 | line = advance_func()
63 |
64 | if line == '':
65 | if eof_error is True:
66 | post_error('Reached end of: {0}.'.format(self.name))
67 | else:
68 | return None
69 | if self.comment is not None:
70 | # Strip comments
71 | line = line[:line.find(self.comment)]
72 |
73 | # Strip spaces
74 | line = line.strip()
75 |
76 | return line
77 |
78 |
79 | def next(self):
80 | '''Overides file.next() so that empty lines are skipped
81 | and leading and trailing whitespaces and comments stripped.
82 | '''
83 | return self.__advance__(super(Getlines, self).next, False)
84 |
85 |
86 | def readline(self, eof_error=True):
87 | '''Overides file.readline() so that empty lines are skipped
88 | and leading and trailing whitespaces and comments stripped.
89 | Optionally, if eof_error is True, error will be posted and
90 | execution terminated in case end of file is reached
91 | '''
92 | return self.__advance__(super(Getlines, self).readline, eof_error)
93 |
94 |
95 | def translation(tstring):
96 | '''Parse string describing fractional translations.
97 | Valid form has comma separated components, where
98 | every component either 0 or 1/n, with n positive
99 | integer. During parsing 0 is replaced with 1/1
100 | and array of three fractions is returned.
101 | '''
102 | try:
103 | return [fractions.Fraction(s) if s.strip() != "0" else 1 for s in tstring.split(',')]
104 | except:
105 | post_error('Unable to parse string: "{0}". Check help for valid '
106 | 'translation generator specification'.format(tstring))
107 |
108 | def version(vstring):
109 | '''Parse string containing version information.
110 | Valid form has dot separated digits. Returns tuple
111 | of integers which are to be lexicographically compared.
112 | '''
113 | try:
114 | return tuple(int(d) for d in vstring.split('.'))
115 | except:
116 | post_error('Unable to parse string: "{0}". The valid version '
117 | 'is composed of dot separated digists'.format(vstring))
118 |
119 | def lcm(a, b):
120 | '''Return lowest common multiple.'''
121 | return a * b // fractions.gcd(a, b)
122 |
123 |
124 | def lcmm(*args):
125 | '''Return lcm of args.'''
126 | return reduce(lcm, args)
127 |
128 |
129 | def fraction_order(fract):
130 | '''Return the lowest integer multiple of a fraction
131 | that yields an integer.
132 | '''
133 | if not isinstance(fract, fractions.Fraction):
134 | fract = fractions.Fraction(fract)
135 |
136 | return lcm(fract.numerator, fract.denominator) / fract.numerator
137 |
138 |
139 | def frac_translation_order(trans):
140 | '''Returns the lowest integer multiple of a fractional
141 | translation that yields translation with integer components.
142 | '''
143 | order = [fraction_order(ti) for ti in trans]
144 |
145 | return lcmm(*order)
146 |
--------------------------------------------------------------------------------
/src/unfolding/write.py:
--------------------------------------------------------------------------------
1 | #===========================================================
2 | #
3 | # PROJECT: vasp_unfold
4 | # FILE: write.py
5 | # AUTHOR: Milan Tomic
6 | # EMAIL: tomic@th.physik.uni-frankfurt.de
7 | # VERSION: 1.32
8 | # DATE: Apr 25th 2017
9 | #
10 | #===========================================================
11 |
12 | import numpy as np
13 | from utils import post_error
14 |
15 |
16 | def write_procar(fname, orbitals, kpoints, kweights, bands,
17 | occupations, weights, phases):
18 | '''Write PROCAR file based on supplied data
19 | '''
20 | # Labels for orbitals
21 | orblabels = ['s', 'py', 'pz', 'px', 'dxy', 'dyz', 'dz2', 'dxz', 'dx2',
22 | 'f-3', 'f-2', 'f-1', 'f0', 'f1', 'f2', 'f3']
23 |
24 | norb = len(orbitals)
25 | npoints = len(kpoints)
26 | nbands = bands.shape[1]
27 | nions = weights.shape[1]/norb
28 | nspin = weights.shape[-1]
29 | ndim = weights.shape[-2]
30 |
31 | try:
32 | out = open(fname, 'w')
33 | except:
34 | post_error('Unable to open "{0}" for writing'.format(fname))
35 |
36 | # Write the first line of the PROCAR file
37 | if phases is not None:
38 | out.write('PROCAR lm decomposed + phase\n')
39 | else:
40 | out.write('PROCAR lm decomposed\n')
41 |
42 | # Format out the column title line for orbital weights
43 | orb_ttl_1 = 'ion '
44 | orb_ttl_2 = 'ion '
45 |
46 | for i in xrange(norb):
47 | orb_ttl_1 += '{0: >6} '.format(orblabels[i])
48 | orb_ttl_2 += '{0: >6} '.format(orblabels[i])
49 |
50 | orb_ttl_1 += '{0: >6}\n'.format('tot')
51 | orb_ttl_2 += '\n'
52 |
53 | for s in xrange(nspin):
54 | # Write the second line containing the sizes
55 | out.write('# of k-points: {0} # of bands: {1}'
56 | ' # of ions: {2}\n\n'.format(npoints, nbands, nions))
57 |
58 | for i, k in enumerate(kpoints):
59 | # Write the k-point info
60 | out.write(' k-point {0: >4} : '.format(i+1))
61 | out.write('{0:.8f} {1:.8f} {2:.8f} '.format(*k))
62 | out.write('weight = {0:.8f}\n\n'.format(kweights[i]))
63 |
64 | for j, b in enumerate(bands[i,:,s]):
65 | # Write the band info
66 | out.write('band {0: >4} # '.format(j+1))
67 | out.write('energy {0: >13.8f} # '.format(b))
68 | out.write('occ. {0: >11.8f}\n\n'.format(occupations[i, j, s]))
69 |
70 | # Write absolute weight blocks. In case of
71 | # non-collinear calculation, there is four
72 | # such blocks
73 | for d in xrange(ndim):
74 | if d == 0:
75 | # Write names of orbitals (s, px, py etc...)
76 | out.write(orb_ttl_1)
77 |
78 | # Allocate storage for accumulation of orbital totals
79 | tot_orb = np.zeros(norb, float)
80 |
81 | # Loop over individual atoms
82 | for k in xrange(nions):
83 | # Write atom's index
84 | out.write('{0: >3} '.format(k+1))
85 |
86 | # Extract corresponding weights
87 | w = weights[i, k*norb:(k+1)*norb, j,d,s]
88 |
89 | # Add to totals
90 | tot_orb += w
91 |
92 | # Write out row of weights
93 | for l in xrange(norb):
94 | out.write('{0: >6.3f} '.format(w[l]))
95 |
96 | # Write atom total
97 | out.write('{0: >6.3f}\n'.format(np.sum(w)))
98 |
99 | # We will now write line with orbital totals
100 | out.write('tot ')
101 |
102 | for l in xrange(norb):
103 | out.write('{0: >6.3f} '.format(tot_orb[l]))
104 |
105 | # Finally, atom+orbital total
106 | out.write('{0: >6.3f}\n'.format(np.sum(tot_orb)))
107 |
108 | # If we have phases we write them now
109 | if phases is not None:
110 | # Write again names of orbitals
111 | out.write(orb_ttl_2)
112 |
113 | # Loop over atoms
114 | for k in xrange(nions):
115 | # Write atom index
116 | out.write('{0: >3} '.format(k+1))
117 |
118 | # Extract corresponding phase
119 | phs = phases[i, k*norb:(k+1)*norb, j, s]
120 |
121 | # Write real part
122 | for l in xrange(norb):
123 | out.write('{0: >6.3f} '.format(phs[l].real))
124 |
125 | # Write atom index
126 | out.write('\n{0: >3} '.format(k+1))
127 |
128 | # Write imaginary part
129 | for l in xrange(norb):
130 | out.write('{0: >6.3f} '.format(phs[l].imag))
131 |
132 | out.write('\n')
133 |
134 | out.write('\n')
135 |
136 | out.write('\n')
137 |
138 | out.close()
139 |
140 |
--------------------------------------------------------------------------------