"""Some handy tools to construct strings for building pwscf input files.
Readers for QE postprocessing tool output (matdyn.x, dynmat.x)."""
import re, os, warnings
import numpy as np
from pwtools.common import fix_eps, str_arr, file_readlines, pj
from pwtools import parse, crys, common
from pwtools.num import EPS
from math import sin, acos, sqrt
[docs]
def atpos_str(symbols, coords, fmt="%.16e", zero_eps=None, eps=EPS, delim=4*' '):
"""Convenience function to make a string for the ATOMIC_POSITIONS section
of a pw.x input file.
Parameters
----------
symbols : sequence
strings with atom symbols, (natoms,), must match with the
rows of coords
coords : array (natoms, 3)
with atomic coords, can also be (natoms, >3) to add constraints on
atomic forces in PWscf
eps : float
Print values as 0.0 where abs(coords[i,j]) < eps. If eps < 0.0, then
disable this.
delim : str
delimiter between columns
Returns
-------
string
Examples
--------
>>> print atpos_str(['Al', 'N'], array([[0,0,0], [0,0,1.]]))
Al 0.0000000000 0.0000000000 0.0000000000
N 0.0000000000 0.0000000000 1.0000000000
"""
if zero_eps is not None:
warnings.warn("`zero_eps` is deprecated, use `eps` > 0 instead",
DeprecationWarning)
coords = np.asarray(coords)
assert len(symbols) == coords.shape[0], "len(symbols) != coords.shape[0]"
txt = '\n'.join("%s%s%s" %(symbols[i], delim, str_arr(row, fmt=fmt, eps=eps,
delim=delim)) \
for i,row in enumerate(coords))
return txt
[docs]
def atpos_str_fast(symbols, coords):
"""Fast version of atpos_str() for usage in loops. We use a fixed string
dtype ``U32`` to convert the array `coords` to string form. We also avoid
all asserts for speed.
Parameters
----------
symbols : list of strings with atom symbols, (natoms,), must match with the
rows of coords
coords : array (natoms, 3) with atomic coords, can also be (natoms, >3) to
add constraints on atomic forces in PWscf
Returns
-------
string
Notes
-----
The string dtype + flatten trick used here is the fastest way to convert a
numpy array to string. However the number of digits is limited to 32 chars.
We use 32 b/c
>>> array([pi]*2).astype('U')
array(['3.141592653589793', '3.141592653589793'], dtype='<U32')
``<`` means little endian and is based on the machine arch automatically.
Needs about 1/3 of the time of :func:`atpos_str`.
"""
nrows = coords.shape[0]
ncols = coords.shape[1]
arr = np.empty((nrows, ncols+1), dtype='U32')
arr[:,0] = symbols
arr[:,1:] = coords
txt = (' '.join(['%s']*(ncols+1)) + '\n')*nrows %tuple(arr.flatten())
return txt
[docs]
def atspec_str(symbols, masses, pseudos):
"""Convenience function to make a string for the ATOMIC_SPECIES section
of a pw.x input file.
Parameters
----------
symbols : sequence of strings with atom symbols, (natoms,)
masses : sequence of floats (natoms,) w/ atom masses
pseudos : sequence of strings (natoms,) w/ pseudopotential file names
Returns
-------
string
Examples
--------
>>> print pwscf.atspec_str(['Al', 'N'], ['1.23', '2.34'], ['Al.UPF', 'N.UPF'])
Al 1.23 Al.UPF
N 2.34 N.UPF
"""
assert len(symbols) == len(masses) == len(pseudos), \
"len(symbols) != len(masses) != len(pseudos)"
txt = '\n'.join(["%s\t%s\t%s" %(sym, str(mass), pp) for sym, mass, pp in
zip(symbols, masses, pseudos)])
return txt
[docs]
def kpoints_str(lst, base='nk'):
"""[3,3,3] -> "nk1=3,nk2=3,nk3=3"
Useful for QE's phonon toolchain ph.x, q2r.x, matdyn.x
"""
return ','.join(['%s%i=%i' %(base, i+1, x) for i, x in enumerate(lst)])
kpointstr = kpoints_str
[docs]
def kpoints_str_pwin(lst, shift=[0,0,0]):
"""[3,3,3] -> " 3 3 3 0 0 0"
Useful for pwscf input files, card K_POINTS.
Parameters
----------
lst : sequence (3,)
shift : sequence (3,), optional
"""
return ' '.join(map(str, lst+shift))
kpointstr_pwin = kpoints_str_pwin
[docs]
def kpoints_str_pwin_full(lst, shift=[0,0,0], gamma=True):
"""Full k-points string for pw.x input files, card K_POINTS.
Parameters
----------
lst : sequence (3,)
shift : sequence (3,), optional
gamma : bool, optional
If lst == [1,1,1] then return "K_POINTS gamma", else
"K_POINTS automatic <newline> 1 1 1 <`shift`>".
"""
lst = lst if type(lst) == type('s') else list(lst)
if (lst == [1,1,1] and gamma) or (lst == 'gamma'):
return "K_POINTS gamma"
else:
return "K_POINTS automatic\n%s" %kpointstr_pwin(lst, shift=shift)
kpointstr_pwin2 = kpoints_str_pwin_full
[docs]
def bool2str(x):
"""Return Fortran bool string for bool input."""
return '.true.' if x else '.false.'
[docs]
def read_matdyn_modes(filename, natoms=None):
"""Parse modes file produced by QE's matdyn.x.
Parameters
----------
filename : str
File to parse (usually "matdyn.modes")
natoms : int
Number of atoms.
Returns
-------
qpoints, freqs, vecs
qpoints : 2d array (nqpoints, 3)
All qpoints on the grid.
freqs : 2d array, (nqpoints, nmodes) where nmodes = 3*natoms
Each row: 3*natoms phonon frequencies in [cm^-1] at each q-point.
vecs : 4d complex array (nqpoints, nmodes, natoms, 3)
Complex eigenvectors of the dynamical matrix for each q-point.
Examples
--------
>>> qpoints,freqs,vecs=read_matdyn_modes('matdyn.modes',natoms=27)
# how many q-points? -> 8
>>> qpoints.shape
(8,3)
# 1st q-point in file, mode #3 (out of 3*27) -> vectors on all 27 atoms
>>> vecs[0,2,...].shape
(27,3)
# 1st q-point in file, mode #3, vector on atom #15
>>> vecs[0,2,14,:].real
array([-0.010832, 0.026063, -0.089511])
>>> vecs[0,2,14,:].imag
array([ 0., 0., 0.])
Notes
-----
The file to be parsed looks like this::
diagonalizing the dynamical matrix ...
q = 0.0000 0.0000 0.0000
**************************************************************************
omega( 1) = -26.663631 [THz] = -889.402992 [cm-1]
( -0.218314 0.000000 -0.025643 0.000000 -0.116601 0.000000 )
( -0.086633 0.000000 0.108966 0.000000 -0.066513 0.000000 )
[... natoms lines: x_real x_imag y_real y_imag z_real z_imag ... until
next omega ...]
omega( 2) = -16.330246 [THz] = -544.718372 [cm-1]
( 0.172149 0.000000 0.008336 0.000000 -0.121991 0.000000 )
( -0.061497 0.000000 0.003782 0.000000 -0.018304 0.000000 )
[... until omega(3*natoms) ...]
**************************************************************************
diagonalizing the dynamical matrix ...
[... until next q-point ...]
q = 0.0000 0.0000 -0.5000
**************************************************************************
omega( 1) = -24.881828 [THz] = -829.968443 [cm-1]
( -0.225020 0.000464 -0.031584 0.000061 -0.130217 0.000202 )
( -0.085499 0.000180 0.107383 -0.000238 -0.086854 0.000096 )
[...]
**************************************************************************
"""
assert natoms is not None
cmd = r"grep 'q.*=' %s | sed -re 's/.*q\s*=(.*)/\1/'" %filename
qpoints = parse.arr2d_from_txt(common.backtick(cmd))
nqpoints = qpoints.shape[0]
nmodes = 3*natoms
cmd = r"grep '^[ ]*(' %s | sed -re 's/^\s*\((.*)\)/\1/g'" %filename
# vecs_file_flat: (nqpoints * nmodes * natoms, 6)
# this line is the bottleneck
vecs_file_flat = parse.arr2d_from_txt(common.backtick(cmd))
vecs_flat = np.empty((vecs_file_flat.shape[0], 3), dtype=complex)
vecs_flat[:,0] = vecs_file_flat[:,0] + 1j*vecs_file_flat[:,1]
vecs_flat[:,1] = vecs_file_flat[:,2] + 1j*vecs_file_flat[:,3]
vecs_flat[:,2] = vecs_file_flat[:,4] + 1j*vecs_file_flat[:,5]
vecs = vecs_flat.flatten().reshape(nqpoints, nmodes, natoms, 3)
cmd = r"grep omega %s | sed -re \
's/.*omega.*=.*\[.*=(.*)\s*\[.*/\1/g'" %filename
freqs = np.fromstring(common.backtick(cmd), sep=' ').reshape((nqpoints, nmodes))
return qpoints, freqs, vecs
[docs]
def read_dyn(filename, natoms=None):
"""Read one dynamical matrix file (for 1 qpoint) produced by ``ph.x`` and
extract the same as :func:`read_matdyn_modes` for this qpoint only.
All arrays have one dim less compared to :func:`read_matdyn_modes`.
Parameters
----------
filename : str
Name of dyn file. Example: "ph.dyn3" for qpoint 3.
natoms : int
number of atoms in the cell (used for nmodes=3*natoms only)
Returns
-------
qpoints, freqs, vecs
qpoints : 1d array (3,)
The qpoint of the dyn file.
freqs : 1d array, (nmodes,) where nmodes = 3*natoms
3*natoms phonon frequencies in [cm^-1] at the q-point.
vecs : 3d complex array (nmodes, natoms, 3)
Complex eigenvectors of the dynamical matrix for the q-point.
"""
assert natoms is not None
cmd = r"egrep 'q.*=.*\(' %s | tail -n1 | sed -re 's/.*q\s*=.*\((.*)\)/\1/'" %filename
qpoints = np.fromstring(common.backtick(cmd), sep=' ')
assert qpoints.shape == (3,)
nmodes = 3*natoms
cmd = r"grep -v 'q.*=' %s | grep '^[ ]*(' | sed -re 's/^\s*\((.*)\)/\1/g'" %filename
# vecs_file_flat: (nmodes * natoms, 6)
# this line is the bottleneck
vecs_file_flat = parse.arr2d_from_txt(common.backtick(cmd))
vecs_flat = np.empty((vecs_file_flat.shape[0], 3), dtype=complex)
vecs_flat[:,0] = vecs_file_flat[:,0] + 1j*vecs_file_flat[:,1]
vecs_flat[:,1] = vecs_file_flat[:,2] + 1j*vecs_file_flat[:,3]
vecs_flat[:,2] = vecs_file_flat[:,4] + 1j*vecs_file_flat[:,5]
vecs = vecs_flat.flatten().reshape(nmodes, natoms, 3)
cmd = r"grep omega %s | sed -re \
's/.*omega.*=.*\[.*=(.*)\s*\[.*/\1/g'" %filename
freqs = np.fromstring(common.backtick(cmd), sep=' ')
return qpoints, freqs, vecs
[docs]
def read_all_dyn(path, nqpoints=None, natoms=None, base='ph.dyn'):
"""Same as :func:`read_matdyn_modes()`, but instead of the file
``matdyn.modes`` which contains freqs,vecs for all qpoints, we read all
dynamical matrix files in `path`, one per qpoint.
Parameters
----------
path : str
Path where dyn files live.
nqpoints : int
number of dyn files (e.g. 5 for "ph.dyn1", ..., "ph.dyn5" if
``base='ph.dyn'``)
natoms : int
base : str
Basename of the dyn files.
Returns
-------
(qpoint,freqs,vecs)
Same as :func:`read_matdyn_modes`
"""
nmodes = 3*natoms
qpoints = np.empty((nqpoints,3), dtype=float)
freqs = np.empty((nqpoints, nmodes), dtype=float)
vecs = np.empty((nqpoints, nmodes, natoms, 3), dtype=complex)
for iq in range(nqpoints):
filename = os.path.join(path, '%s%i' %(base, iq+1))
qq, ff, vv = read_dyn(filename, natoms=natoms)
qpoints[iq,...] = qq
freqs[iq,...] = ff
vecs[iq,...] = vv
return qpoints, freqs, vecs
[docs]
def read_dynmat(path='.', natoms=None, filename='dynmat.out', axsf='dynmat.axsf'):
"""Read ``dynmat.x`` output.
`freqs` are parsed from `filename` and `vecs` from `axsf`. `qpoints` is
alawys Gamma, i.e. [0,0,0].
Output format is the same as in :func:`read_dyn`.
Parameters
----------
path : str
path where output files are
natoms : int
filename : str
Text output from dynmat.x, where the frequencies are printed, relative
to `path`.
axsf : str
AXSF file (``filxsf`` in input) with mode vectors as forces.
Returns
-------
qpoints, freqs, vecs
qpoints : 1d array (3,)
The qpoint, which is Gamma, i.e. [0,0,0]
freqs : 1d array, (nmodes,) where nmodes = 3*natoms
3*natoms phonon frequencies in [cm^-1] at the q-point.
vecs : 3d real array (nmodes, natoms, 3)
Real parts (???) if the eigenvectors of the dynamical matrix for the
q-point.
Notes
-----
We assume the output to be generated with ``dynmat.x < dynmat.in >
dynmat.out``.
"""
assert natoms is not None, ("natoms is None")
nmodes = 3*natoms
out_fn = pj(path, filename)
axsf_fn = pj(path, axsf)
cmd = "grep -A{0} PRIMCO {1} | sed -re '/PRIMCO.*/{{N;d;}}' | \
awk '{{print $5\" \"$6\" \"$7}}'".format(natoms+1, axsf_fn)
qpoints = np.zeros((3,))
vecs = np.fromstring(common.backtick(cmd), sep=' ').reshape(nmodes,natoms,3)
cmd = "grep -A{0} 'mode.*cm-1' {1} | grep -v mode | \
awk '{{print $2}}'".format(nmodes, out_fn)
freqs = np.fromstring(common.backtick(cmd), sep=' ')
return qpoints,freqs,vecs
[docs]
def read_dynmat_ir_raman(filename='dynmat.out', natoms=None,
cols={1: 'freqs', 3:'ir', 4: 'raman', 5: 'depol'}):
"""Read ``dynmat.x`` text output file and extract IR and Raman
intensities.
Parameters
----------
filename : str
dynmat.x text output file (e.g. from ``dynmat.x < dynmat.in >
dynmat.out``)
natoms : int
number of atoms in the cell
cols : dict
column numbers of the text block
Returns
-------
cols = None
Return the parsed array as found in the file
cols = dict
Return dict with keys from `cols` and 1d arrays ``{'freqs': <array>,
'ir': <array>, 'raman': <array>, 'depol': <array>}``. If a column is
not present, the array is None.
Notes
-----
The parsed textblock looks like this::
# mode [cm-1] [THz] IR Raman depol.fact
1 0.00 0.0000 0.0000 0.0005 0.7414
2 0.00 0.0000 0.0000 0.0005 0.7465
3 0.00 0.0000 0.0000 0.0018 0.2647
4 252.27 7.5627 0.0000 0.0073 0.7500
5 252.27 7.5627 0.0000 0.0073 0.7500
6 548.44 16.4419 0.0000 0.0000 0.7434
7 603.32 18.0872 35.9045 18.9075 0.7366
8 656.82 19.6910 0.0000 7.9317 0.7500
9 656.82 19.6910 0.0000 7.9317 0.7500
10 669.67 20.0762 31.5712 5.0265 0.7500
11 738.22 22.1311 0.0000 0.0000 0.7306
12 922.64 27.6600 31.5712 5.0265 0.7500
Some columns (e.g. IR, Raman) may be missing.
"""
assert natoms is not None, ("natoms is None")
cmd = "grep -A{0} 'mode.*cm-1' {1} | grep -v mode".format(3*natoms, filename)
arr = parse.arr2d_from_txt(common.backtick(cmd))
if cols is None:
return arr
else:
dct = {}
for ii,name in cols.items():
if arr.shape[1] >= (ii+1):
dct[name] = arr[:,ii]
else:
dct[name] = None
return dct
[docs]
def read_dynmat_out(*args, **kwds):
"""Backward compat wrapper for :func:`read_dynmat_ir_raman`."""
warnings.warn("read_dynmat_out() is deprecated, use read_dynmat_ir_raman()",
DeprecationWarning)
return read_dynmat_ir_raman(*args, **kwds)
[docs]
def read_matdyn_freq(filename):
"""Parse frequency file produced by QE's matdyn.x ("flfrq" in matdyn.x
input, usually "matdyn.freq" or so) when calculating a phonon dispersion on
a grid (ldisp=.true., used for phonon dos) or a pre-defined k-path in the
BZ.
Used in :file:`bin/plot_dispersion.py`.
In QE 5.x, a file with suffix ".gp" (e.g. "matdyn.freq.gp") is now written,
where::
>>> import numpy as np
>>> from pwtools import kpath, pwscf
>>> d = np.loadtxt("matdyn.freq.gp")
>>> kpoints,freqs = pwscf.read_matdyn_freq("matdyn.freq")
>>> np.allclose(d[:,0], kpath.get_path_norm(kpoints))
>>> np.allclose(d[:,1:], freqs)
Parameters
----------
filename : file with k-points and phonon frequencies
Returns
-------
kpoints : array (nks, 3)
Array with `nks` k-points. AFAIK the unit is always ``2*pi/alat`` with
``alat = celldm(1)``.
freqs : array (nks, nbnd)
Array with `nbnd` energies/frequencies at each of the `nks` k-points.
For phonon DOS, nbnd == 3*natoms.
Notes
-----
`matdyn.in`::
&input
asr='simple',
amass(1)=26.981538,
amass(2)=14.00674,
flfrc='fc',
flfrq='matdyn.freq.disp'
/
101 | nks
0.000000 0.000000 0.000000 |
0.037500 0.037500 0.000000 | List of nks = 101 k-points
.... |
`filename` has the form::
<header>
<k-point, (3,)>
<frequencies,(nbnd,)
<k-point, (3,)>
<frequencies,(nbnd,)
...
for example::
&plot nbnd= 6, nks= 101 /
0.000000 0.000000 0.000000
0.0000 0.0000 0.0000 456.2385 456.2385 871.5931
0.037500 0.037500 0.000000
23.8811 37.3033 54.3776 455.7569 457.2338 869.8832
.....
See Also
--------
:func:`pwtools.kpath.plot_dis`,
:func:`pwtools.kpath.get_path_norm`
"""
lines = file_readlines(filename)
# Read number of bands (nbnd) and k-points (nks). OK, Fortran's namelists
# win here :)
# nbnd: number of bands = number of frequencies per k-point = 3*natoms for
# phonons
# nks: number of k-points
pat = r'.*\s+nbnd\s*=\s*([0-9]+)\s*,\s*nks\s*=\s*([0-9]+)\s*/'
match = re.match(pat, lines[0])
assert (match is not None), "match is None"
nbnd = int(match.group(1))
nks = int(match.group(2))
kpoints = np.empty((nks, 3), dtype=float)
freqs = np.empty((nks, nbnd), dtype=float)
step = 3 + nbnd
# nasty trick: join all lines containing data into one 1d array: " ".join()
# does "1 2\n3 4" -> "1 2\n 3 4" and split() splits at \n + whitespace.
items = np.array(' '.join(lines[1:]).split(), dtype=float)
for ii in range(len(items) // step):
kpoints[ii,:] = items[ii*step:(ii*step+3)]
freqs[ii,:] = items[(ii*step+3):(ii*step+step)]
return kpoints, freqs
[docs]
def ibrav2cell(ibrav, celldm):
"""Convert PWscf's ibrav + celldm to cell. All formulas are taken straight
from the PWscf homepage. Don't blame me for errors. Use after testing.
This function generates *primitive* cells. Note that in crys.py (and
anywhere else in the package, for that matter) we do not have a distinction
between conventional/primitive cell. We always think in primitive cells.
Especially celldm in crys.py can be converted to/from `cell` and
`cryst_const`. But here, `celldm` is the PWscf style celldm, which
describes the *conventional* cell. For example, for an fcc cell (ibrav=2),
celldm[0] == a == alat is the lattice constant "a" of the cubic
conventional cell (cell=a*identity(3)), which is also found in a .cif file
together with all symmetries. OTOH, for a hexagonal cell (ibrav=4)
primitive == conventional cell.
`celldm` (a = celldm[0]) is assumed be in the unit that you want for
`cell` (Bohr, Angstrom, etc).
Note: There are some documentation <-> forluma errors / inconsistencies for
ibrav=12,13. See test/test_ibrav.py. If you really need that, have a look
at the PWscf source for how they do it there.
Parameters
----------
ibrav : int
1 ... 14
celldm : sequence of length 6
This not the isame length 6 array `celldm` in crys.py. Here, the
entries which are not needed can be None.
Returns
-------
array (3,3) : cell vectors as rows, unit is that of celldm[0], i.e. a
Notes
-----
* ibrav = 14 is actually the only case where all 6 entries of `celldm` are
needed and therefore the same as crys.cc2cell(crys.celldm2cc(celldm)).
The returned `cell` here has the same spatial orientation as the one
returned from crys.cc2cell(): a along x, b in xy-plane.
"""
# some of celldm can be None
tofloat = lambda x: x if x is None else float(x)
aa, bb_aa, cc_aa, cos_alpha, cos_beta, cos_gamma = [tofloat(x) for x in celldm]
if bb_aa is not None:
bb = bb_aa * aa
if cc_aa is not None:
cc = cc_aa * aa
if cos_gamma is not None:
sin_gamma = sin(acos(cos_gamma))
if ibrav == 1:
# cubic P (sc), sc simple cubic
# v1 = a(1,0,0), v2 = a(0,1,0), v3 = a(0,0,1)
cell = aa * np.identity(3)
elif ibrav == 2:
# cubic F (fcc), fcc face centered cubic
# v1 = (a/2)(-1,0,1), v2 = (a/2)(0,1,1), v3 = (a/2)(-1,1,0)
cell = 0.5*aa * np.array([[-1, 0, 1],
[ 0, 1, 1],
[-1, 1, 0.0]])
elif ibrav == 3:
# cubic I (bcc), bcc body entered cubic
# v1 = (a/2)(1,1,1), v2 = (a/2)(-1,1,1), v3 = (a/2)(-1,-1,1)
cell = 0.5*aa * np.array([[ 1, 1, 1],
[-1, 1, 1],
[-1, -1, 1.0]])
elif ibrav == 4:
# Hexagonal and Trigonal P, simple hexagonal and trigonal(p)
# v1 = a(1,0,0), v2 = a(-1/2,sqrt(3)/2,0), v3 = a(0,0,c/a)
cell = aa * np.array([[ 1, 0, 0],
[-0.5, sqrt(3)/2.0, 0],
[ 0, 0, cc/aa]])
elif ibrav == 5:
# Trigonal R, trigonal(r)
# v1 = a(tx,-ty,tz), v2 = a(0,2ty,tz), v3 = a(-tx,-ty,tz)
# where c=cos(alpha) is the cosine of the angle alpha between any pair
# of crystallographic vectors, tc, ty, tz are defined as
# tx=sqrt((1-c)/2), ty=sqrt((1-c)/6), tz=sqrt((1+2c)
tx = sqrt((1.0 - cos_alpha)/2.0)
ty = sqrt((1.0 - cos_alpha)/6.0)
tz = sqrt((1.0 + 2*cos_alpha)/3.0)
cell = aa * np.array([[ tx, -ty, tz],
[ 0.0, 2.0*ty, tz],
[-tx, -ty, tz]])
elif ibrav == 6:
# Tetragonal P (st), simple tetragonal (p)
# v1 = a(1,0,0), v2 = a(0,1,0), v3 = a(0,0,c/a)
cell = aa * np.array([[1, 0, 0],
[0, 1, 0],
[0, 0, cc/aa]])
elif ibrav == 7:
# Tetragonal I (bct), body centered tetragonal (i)
# v1 = (a/2)(1,-1,c/a), v2 = (a/2)(1,1,c/a), v3 = (a/2)(-1,-1,c/a)
cell = 0.5*aa * np.array([[ 1, -1, cc/aa],
[ 1, 1, cc/aa],
[-1, -1, cc/aa]])
elif ibrav == 8:
# Orthorhombic P, simple orthorhombic (p)
# v1 = (a,0,0), v2 = (0,b,0), v3 = (0,0,c)
cell = np.array([[aa, 0, 0],
[0, bb, 0],
[0, 0, cc]])
elif ibrav == 9:
# Orthorhombic base-centered(bco), bco base centered orthorhombic
# v1 = (a/2,b/2,0), v2 = (-a/2,b/2,0), v3 = (0,0,c)
cell = np.array([[ aa/2.0, bb/2.0, 0],
[-aa/2.0, bb/2.0, 0],
[ 0, 0, cc]])
elif ibrav == 10:
# Orthorhombic face-centered, face centered orthorhombic
# v1 = (a/2,0,c/2), v2 = (a/2,b/2,0), v3 = (0,b/2,c/2)
cell = np.array([[aa/2.0, 0, cc/2.0],
[aa/2.0, bb/2.0, 0],
[0, bb/2.0, cc/2.0]])
elif ibrav == 11:
# Orthorhombic body-centered, body centered orthorhombic
# v1 = (a/2,b/2,c/2), v2 = (-a/2,b/2,c/2), v3 = (-a/2,-b/2,c/2)
cell = np.array([[ aa/2.0, bb/2.0, cc/2.0],
[-aa/2.0, bb/2.0, cc/2.0],
[-aa/2.0, -bb/2.0, cc/2.0]])
elif ibrav == 12:
# Monoclinic P, monoclinic (p)
# v1 = (a,0,0), v2= (b*cos(gamma), b*sin(gamma), 0), v3 = (0, 0, c)
cell = np.array([[aa, 0, 0],
[bb*cos_gamma, bb*sin_gamma, 0],
[0, 0, cc]])
elif ibrav == 13:
# Monoclinic base-centered, base centered monoclinic
# v1 = (a/2,0,-c/2), v2 = (b*cos(gamma),b*sin(gamma), 0), v3 = (a/2,0,c/2)
cell = np.array([[aa/2.0, 0, -cc/2.0],
[bb*cos_gamma, bb*sin_gamma, 0],
[aa/2.0, 0, cc/2.0]])
elif ibrav == 14:
# Triclinic
# v1 = (a, 0, 0),
# v2 = (b*cos(gamma), b*sin(gamma), 0)
# v3 = (c*cos(beta), c*(cos(alpha)-cos(beta)cos(gamma))/sin(gamma),
# c*sqrt( 1 + 2*cos(alpha)cos(beta)cos(gamma)
# - cos(alpha)^2-cos(beta)^2-cos(gamma)^2 )/sin(gamma)
v1 = np.array([aa,0,0])
v2 = np.array([bb*cos_gamma, bb*sin_gamma, 0])
v3 = np.array([cc*cos_beta,
cc*(cos_alpha - cos_beta*cos_gamma)/sin_gamma,
cc*sqrt( 1 + 2*cos_alpha*cos_beta*cos_gamma - \
cos_alpha**2.0-cos_beta**2.0-cos_gamma**2.0)/sin_gamma])
cell = np.array([v1, v2, v3])
else:
raise Exception("illegal ibrav: %s" %ibrav)
return cell