3
0
mirror of https://github.com/triqs/dft_tools synced 2024-08-29 07:22:01 +02:00

* Restructured the directory structure * Added some half-day long development including tests

This commit is contained in:
Oleg Peil 2015-02-13 22:24:48 +01:00 committed by Michel Ferrero
parent e85d3b4f2b
commit bd6198d264
24 changed files with 424 additions and 2669 deletions

View File

@ -1,74 +0,0 @@
2 2 1 2
0.1333597E+02 0.2987800E-09 0.2987800E-09 0.2987800E-09 0.5000000E-15
1.000000000000000E-004
CAR
V
22 4 15
0.0000000E+00 0.0000000E+00 0.0000000E+00 0.3703704E-01
1 -31.708596 -31.708596
2 -31.708596 -31.708596
3 -31.708596 -31.708596
4 -31.061475 -31.061475
5 -31.061475 -31.061475
6 -31.061475 -31.061475
7 -0.811953 -0.811953
8 2.607665 2.607665
9 2.607665 2.607665
10 6.002735 6.002735
11 6.002735 6.002735
12 6.002735 6.002735
13 8.013630 8.013630
14 8.013630 8.013630
15 9.361252 9.361252
0.3333333E+00 0.0000000E+00 0.0000000E+00 0.2222222E+00
1 -31.682851 -31.682851
2 -31.539793 -31.539793
3 -31.539793 -31.539793
4 -31.363786 -31.363786
5 -31.215960 -31.215960
6 -31.215960 -31.215960
7 1.216017 1.216017
8 3.394994 3.394994
9 3.771669 3.771669
10 6.050595 6.050595
11 6.050595 6.050595
12 6.457025 6.457025
13 6.771661 6.771661
14 7.383619 7.383619
15 7.383620 7.383620
0.3333333E+00 0.3333333E+00 0.0000000E+00 0.4444444E+00
1 -31.935665 -31.935665
2 -31.782518 -31.782518
3 -31.447103 -31.447103
4 -31.284953 -31.284953
5 -31.234384 -31.234384
6 -31.067231 -31.067231
7 2.201004 2.201004
8 3.123182 3.123182
9 4.809697 4.809697
10 5.123957 5.123957
11 5.357025 5.357025
12 5.853431 5.853431
13 7.260017 7.260017
14 7.862532 7.862532
15 8.230907 8.230907
0.3333333E+00 0.3333333E+00 0.3333333E+00 0.2962963E+00
1 -31.808727 -31.808727
2 -31.713049 -31.713049
3 -31.713049 -31.713049
4 -31.278227 -31.278227
5 -31.278227 -31.278227
6 -31.179331 -31.179331
7 3.831291 3.831291
8 3.998572 3.998572
9 4.466211 4.466211
10 4.466211 4.466211
11 4.724276 4.724276
12 4.724276 4.724276
13 8.230309 8.230309
14 8.253908 8.253831
15 8.292606 8.292606

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +0,0 @@
LuNiO3, low-T, P2_1/n
1.0
5.1234998703 0.0000000000 0.0000000000
0.0000000000 5.5089001656 0.0000000000
-0.0166880521 0.0000000000 7.3551808822
Lu Ni O
4 4 12
Cartesian
5.00246185 0.42418531 1.86086070
0.10434997 5.08471473 5.49431996
2.67444393 3.17863552 1.81672974
2.43236789 2.33026481 5.53845136
2.56174994 0.00000000 0.00000000
-0.00834403 2.75445008 3.67759044
2.55340591 0.00000000 3.67759044
0.00000000 2.75445008 0.00000000
0.56002379 2.54896816 1.79539968
4.54678788 2.95993201 5.55978141
1.99338212 5.30341824 1.88219076
3.11342985 0.20548193 5.47298991
3.55122302 1.72814193 0.39644425
1.55558880 3.78075840 6.95873661
4.12568276 4.48259185 3.28114617
0.98112906 1.02630815 4.07403471
0.93260966 1.11059427 6.94181952
4.17420208 4.39830581 0.41336136
1.60410812 3.86504444 4.09095181
3.50270370 1.64385573 3.26422908

View File

@ -1,29 +0,0 @@
LuNiO3, low-T, P2_1/n
1.0
5.1234998703 0.0000000000 0.0000000000
0.0000000000 5.5089001656 0.0000000000
-0.0166880521 0.0000000000 7.3551808822
Lu Ni O
4 4 12
Kartesian
5.00246185 0.42418531 1.86086070
0.10434997 5.08471473 5.49431996
2.67444393 3.17863552 1.81672974
2.43236789 2.33026481 5.53845136
2.56174994 0.00000000 0.00000000
-0.00834403 2.75445008 3.67759044
2.55340591 0.00000000 3.67759044
0.00000000 2.75445008 0.00000000
0.56002379 2.54896816 1.79539968
4.54678788 2.95993201 5.55978141
1.99338212 5.30341824 1.88219076
3.11342985 0.20548193 5.47298991
3.55122302 1.72814193 0.39644425
1.55558880 3.78075840 6.95873661
4.12568276 4.48259185 3.28114617
0.98112906 1.02630815 4.07403471
0.93260966 1.11059427 6.94181952
4.17420208 4.39830581 0.41336136
1.60410812 3.86504444 4.09095181
3.50270370 1.64385573 3.26422908

View File

@ -1,28 +0,0 @@
LuNiO3, low-T, P2_1/n
1.0
5.1234998703 0.0000000000 0.0000000000
0.0000000000 5.5089001656 0.0000000000
-0.0166880521 0.0000000000 7.3551808822
Lu Ni O
4 4 12
Direct
0.977199972 0.077000000 0.252999991
0.022800028 0.922999978 0.746999979
0.522800028 0.577000022 0.247000009
0.477199972 0.423000008 0.753000021
0.500000000 0.000000000 0.000000000
0.000000000 0.500000000 0.500000000
0.500000000 0.000000000 0.500000000
0.000000000 0.500000000 0.000000000
0.110100001 0.462700009 0.244100004
0.889899969 0.537299991 0.755900025
0.389899999 0.962700009 0.255899996
0.610100031 0.037299991 0.744099975
0.693300009 0.313699991 0.053900000
0.306699991 0.686300039 0.946099997
0.806699991 0.813699961 0.446099997
0.193300009 0.186300009 0.553900003
0.185100004 0.201600000 0.943799973
0.814899981 0.798399985 0.056200027
0.314899981 0.701600015 0.556200027
0.685100019 0.298399985 0.443799973

View File

@ -1,4 +1,5 @@
makefile makefile
Makefile
*.so *.so
*.o *.o
*.pyc *.pyc

View File

@ -5,5 +5,4 @@ LIB_FLAGS=-L/opt/local/Library/Frameworks/Python.framework/Versions/Current/lib/
c_plocar_io.so: c_plocar_io.c c_plocar_io.so: c_plocar_io.c
$(CC) $< -fPIC -shared -o $@ $(INC_FLAGS) $(LIB_FLAGS) -lpython2.7 $(CC) $< -fPIC -shared -o $@ $(INC_FLAGS) $(LIB_FLAGS) -lpython2.7
cp $@ ..

View File

@ -0,0 +1,8 @@
CC=gcc
INC_FLAGS=-I/usr/include/python2.7 -I/usr/lib/python2.7/dist-packages/numpy/core/include/numpy
LIB_FLAGS=-L/usr/lib/python2.7
c_plocar_io.so: c_plocar_io.c
$(CC) $< -fPIC -shared -o $@ $(INC_FLAGS) $(LIB_FLAGS) -lpython2.7

View File

@ -1,8 +1,12 @@
r"""
Module for parsing and checking an input config-file.
"""
import ConfigParser import ConfigParser
import numpy as np import numpy as np
import re import re
import sys import sys
import itertools as it
import vaspio import vaspio
def issue_warning(message): def issue_warning(message):
@ -12,6 +16,7 @@ def issue_warning(message):
print print
print " !!! WARNING !!!: " + message print " !!! WARNING !!!: " + message
print print
################################################################################ ################################################################################
################################################################################ ################################################################################
# #
@ -34,23 +39,6 @@ class ConfigParameters:
1. internal name of a parameter 1. internal name of a parameter
2. function used to convert an input string into data for a given parameter 2. function used to convert an input string into data for a given parameter
""" """
self.sh_required = {
'ions': ('ion_list', self.parse_ion_list),
'lshell': ('lshell', int)}
self.sh_optional = {
'rtransform': ('tmatrix', lambda s: self.parse_tmatrix(s, real=True)),
'ctransform': ('tmatrix', lambda s: self.parse_tmatrix(s, real=False))}
self.gr_required = {
'emin': ('emin', float),
'emax': ('emax', float)}
self.gr_optional = {
'normalize' : ('normalize', self.parse_logical),
'normion' : ('normion', self.parse_logical)}
################################################################################ ################################################################################
# #
# __init__() # __init__()
@ -58,10 +46,29 @@ class ConfigParameters:
################################################################################ ################################################################################
def __init__(self, input_filename, verbosity=1): def __init__(self, input_filename, verbosity=1):
self.verbosity = verbosity self.verbosity = verbosity
self.cp = ConfigParser.ConfigParser() self.cp = ConfigParser.SafeConfigParser()
self.cp.readfp(open(input_filename, 'r')) self.cp.readfp(open(input_filename, 'r'))
self.conf_pars = {} self.parameters = {}
self.sh_required = {
'ions': ('ion_list', self.parse_string_ion_list),
'lshell': ('lshell', int)}
self.sh_optional = {
'rtransform': ('tmatrix', lambda s: self.parse_string_tmatrix(s, real=True)),
'ctransform': ('tmatrix', lambda s: self.parse_string_tmatrix(s, real=False))}
self.gr_required = {
'shells': ('shells', lambda s: map(int, s.split())),
'emin': ('emin', float),
'emax': ('emax', float)}
self.gr_optional = {
'normalize' : ('normalize', self.parse_string_logical),
'normion' : ('normion', self.parse_string_logical)}
# #
# Special parsers # Special parsers
@ -75,13 +82,25 @@ class ConfigParameters:
""" """
The ion list accepts two formats: The ion list accepts two formats:
1). A list of ion indices according to POSCAR. 1). A list of ion indices according to POSCAR.
The list can be defined as a range '9..20'.
2). An element name, in which case all ions with 2). An element name, in which case all ions with
this name are included. this name are included.
The second option requires an input from POSCAR file. The second option requires an input from POSCAR file.
""" """
# First check if a range is given
patt = '([0-9]+)\.\.([0-9]+)'
match = re.match(patt, par_str)
if match:
i1, i2 = tuple(map(int, match.groups()[:2]))
mess = "First index of the range must be smaller or equal to the second"
assert i1 <= i2, mess
ion_list = np.array(range(i1 - 1, i2))
else:
# Check if a set of indices is given
try: try:
l_tmp = map(int, par_str.split()) l_tmp = map(int, par_str.split())
l_tmp.sort()
# Subtract 1 so that VASP indices (starting with 1) are converted # Subtract 1 so that VASP indices (starting with 1) are converted
# to Python indices (starting with 0) # to Python indices (starting with 0)
ion_list = np.array(l_tmp) - 1 ion_list = np.array(l_tmp) - 1
@ -89,6 +108,9 @@ class ConfigParameters:
err_msg = "Only an option with a list of ion indices is implemented" err_msg = "Only an option with a list of ion indices is implemented"
raise NotImplementedError(err_msg) raise NotImplementedError(err_msg)
err_mess = "Lowest ion index is smaller than 1 in '%s'"%(par_str)
assert np.all(ion_list >= 0), err_mess
return ion_list return ion_list
################################################################################ ################################################################################
@ -102,8 +124,44 @@ class ConfigParameters:
(case does not matter). In fact, only the first symbol matters so that (case does not matter). In fact, only the first symbol matters so that
one can write 'T' or 'F'. one can write 'T' or 'F'.
""" """
assert par_str[0] in 'tf', "Logical parameters should be given by either 'True' or 'False'" first_char = par_str[0].lower()
return par_str[0] == 't' assert first_char in 'tf', "Logical parameters should be given by either 'True' or 'False'"
return first_char == 't'
################################################################################
#
# parse_string_tmatrix()
#
################################################################################
def parse_string_tmatrix(self, par_str, real):
"""
Transformation matrix is defined as a set of rows separated
by a new line symbol.
"""
str_rows = par_str.split('\n')
try:
rows = [map(float, s.split()) for s in str_rows]
except ValueError:
err_mess = "Cannot parse a matrix string:\n%s"%(par_str)
raise ValueError(err_mess)
nr = len(rows)
nm = len(rows[0])
err_mess = "Number of columns must be the same:\n%s"%(par_str)
for row in rows:
assert len(row) == nm, err_mess
if real:
mat = np.array(rows)
else:
err_mess = "Complex matrix must contain 2*M values:\n%s"%(par_str)
assert 2 * (nm / 2) == nm, err_mess
tmp = np.array(rows, dtype=np.complex128)
mat = tmp[:, 0::2] + 1.0j * tmp[:, 1::2]
return mat
################################################################################ ################################################################################
# #
@ -122,7 +180,7 @@ class ConfigParameters:
except ConfigParser.NoOptionError: except ConfigParser.NoOptionError:
if exception: if exception:
message = "Required parameter '%s' not found in section [%s]"%(par, section) message = "Required parameter '%s' not found in section [%s]"%(par, section)
raise ConfigParser.NoOptionError(message) raise Exception(message)
else: else:
continue continue
@ -149,9 +207,8 @@ class ConfigParameters:
# (note that ConfigParser transforms all names to lower case) # (note that ConfigParser transforms all names to lower case)
sections = self.cp.sections() sections = self.cp.sections()
sh_patt = 'shell *([0-9]*)' sh_patt1 = re.compile('shell +.*', re.IGNORECASE)
ismatch = lambda s: not re.match(sh_patt, s) is None sec_shells = filter(sh_patt1.match, sections)
sec_shells = filter(ismatch, sections)
self.nshells = len(sec_shells) self.nshells = len(sec_shells)
assert self.nshells > 0, "No projected shells found in the input file" assert self.nshells > 0, "No projected shells found in the input file"
@ -164,15 +221,17 @@ class ConfigParameters:
print " Found 1 projected shell" print " Found 1 projected shell"
# Get shell indices # Get shell indices
get_ind = lambda s: int(re.match(sh_patt, s).groups()[0]) sh_patt2 = re.compile('shell +([0-9]*)$', re.IGNORECASE)
try: try:
get_ind = lambda s: int(sh_patt2.match(s).groups()[0])
sh_inds = map(get_ind, sec_shells) sh_inds = map(get_ind, sec_shells)
except ValueError: except (ValueError, AttributeError):
raise ValueError("Failed to extract shell indices from a list: %s"%(sec_shells)) raise ValueError("Failed to extract shell indices from a list: %s"%(sec_shells))
self.sh_sections = {ind: sec for ind, sec in sh_inds, sec_shells} self.sh_sections = {ind: sec for ind, sec in it.izip(sh_inds, sec_shells)}
# Check that all indices are unique # Check that all indices are unique
# In principle redundant because the list of sections will contain only unique names
assert len(sh_inds) == len(set(sh_inds)), "There must be no shell with the same index!" assert len(sh_inds) == len(set(sh_inds)), "There must be no shell with the same index!"
# Ideally, indices should run from 1 to <nshells> # Ideally, indices should run from 1 to <nshells>
@ -180,7 +239,7 @@ class ConfigParameters:
sh_inds.sort() sh_inds.sort()
if sh_inds != range(1, len(sh_inds) + 1): if sh_inds != range(1, len(sh_inds) + 1):
issue_warning("Shell indices are not uniform or not starting from 1. " issue_warning("Shell indices are not uniform or not starting from 1. "
"This might be an indication of a incorrect setup." "This might be an indication of a incorrect setup.")
# Parse shell parameters # Parse shell parameters
self.shells = {} self.shells = {}
@ -230,9 +289,8 @@ class ConfigParameters:
# Find group sections # Find group sections
sections = self.cp.sections() sections = self.cp.sections()
gr_patt = 'group *([0-9]*)' gr_patt = re.compile('group *([0-9]*)')
ismatch = lambda s: not re.match(gr_patt, s) is None sec_groups = filter(gr_patt.match, sections)
sec_groups = filter(ismatch, sections)
self.ngroups = len(sec_groups) self.ngroups = len(sec_groups)

View File

@ -1,13 +0,0 @@
[General]
[PLO Group 1]
IONS = 5 6 7 8
#IONS = Ni
EMIN = -0.6
EMAX = 2.7
LSHELL = 2
RTRANSFORM =
0.0 0.0 0.0 0.0 1.0
0.0 0.0 1.0 0.0 0.0

View File

@ -0,0 +1,20 @@
[General]
EFERMI = -0.6
[Group 1]
SHELLS = 1 2
EMIN = -7.6
EMAX = 2.7
[Shell 1]
# Ni shell
IONS = 5 6 7 8
LSHELL = 2
RTRANSFORM =
0.0 0.0 0.0 0.0 1.0
0.0 0.0 1.0 0.0 0.0
[Shell 2]
# Oxygen shell
IONS = 9..20
LSHELL = 1

View File

@ -0,0 +1 @@
*.pyc

View File

@ -0,0 +1 @@
PYTHONPATH=../../python:../../c:$PYTHONPATH python $1

View File

@ -0,0 +1,12 @@
[General]
EFERMI = 0.7
[Group 1]
SHELLS = 1
[Shell 1]
LSHELL = 2
[Shell 2]
IONS = 9..20
LSHELL = 1

View File

@ -0,0 +1,4 @@
[General]
[Group 1]

View File

@ -0,0 +1,5 @@
[General]
[Group 1]
[Shell x]

View File

@ -0,0 +1,7 @@
[General]
[Group 1]
[Shell 1]
LSHELL = 2

View File

@ -0,0 +1,17 @@
[General]
[Group 1]
SHELLS = 1 2
[Shell 1]
LSHELL = 2
IONS = 5..8
[Shell 2]
LSHELL = 1
IONS = 1..4
RTRANSFORM = 0.0 1.0 0.0
1.0 0.0 0.0
0.0 0.0 1.0

View File

@ -0,0 +1,252 @@
r"""
Test suite for module `inpconf.py`.
"""
import unittest
import numpy as np
from inpconf import ConfigParameters
import ConfigParser
class TestSpecialParsers(unittest.TestCase):
"""
Tests of special parsers.
"""
def __init__(self, *args, **kwargs):
"""
Initializes a custom equality function for comparing numpy arrays.
"""
super(TestSpecialParsers, self).__init__(*args, **kwargs)
self.addTypeEqualityFunc(np.ndarray, self.is_arrays_equal)
def is_arrays_equal(self, arr1, arr2, msg=None):
"""
Raises self.failureException is arrays arr1 and arr2
are not equal.
"""
if not np.allclose(arr1, arr2):
raise self.failureException(msg)
def setUp(self):
"""
"""
pass
################################################################################
#
# test_parse_string_logical()
#
################################################################################
def test_parse_string_logical(self):
"""
Function:
def parse_string_logical(self, par_str)
Scenarios:
- **if** par_str == 'True' **return** True
- **if** par_str == 'False' **return** False
- **if** par_str == '0' **raise** assertion
"""
conf_pars = ConfigParameters('test1.cfg')
# Scenario 1
res = conf_pars.parse_string_logical('True')
self.assertEqual(res, True)
# Scenario 2
res = conf_pars.parse_string_logical('False')
self.assertEqual(res, False)
# Scenario 3
with self.assertRaises(AssertionError):
conf_pars.parse_string_logical('0')
################################################################################
#
# test_parse_string_ion_list()
#
################################################################################
def test_parse_string_ion_list(self):
"""
Function:
def parse_string_ion_list(self, par_str)
Scenarios:
- **if** par_str == '5 6 7 8' **return** array([4, 5, 6, 7])
- **if** par_str == 'Ni' **raise** NotImplementedError
- **if** par_str == '0 1' **raise** AssertionError
- **if** par_str == '5..8' **return** array([4, 5, 6, 7])
- **if** par_str == '8..5' **raise** AssertionError
"""
conf_pars = ConfigParameters('test1.cfg')
# Scenario 1
expected = np.array([4, 5, 6, 7])
res = conf_pars.parse_string_ion_list('5 6 7 8')
self.assertEqual(res, expected)
# Scenario 2
with self.assertRaises(NotImplementedError):
conf_pars.parse_string_ion_list('Ni')
# Scenario 3
with self.assertRaises(AssertionError):
conf_pars.parse_string_ion_list('0 1')
# Scenario 4
res = conf_pars.parse_string_ion_list('5..8')
self.assertEqual(res, expected)
# Scenario 5
err_mess = "First index of the range"
with self.assertRaisesRegexp(AssertionError, err_mess):
conf_pars.parse_string_ion_list('8..5')
################################################################################
#
# test_parse_string_tmatrix()
#
################################################################################
def test_parse_string_tmatrix(self):
"""
Function:
def parse_string_tmatrix(self, par_str)
Parses a matrix defined as a set of rows in the conf-file.
Scenarios:
- **if** number of columns is not the same **raise** AssertionError
- **if** complex matrix is read and the number of columns is odd
**raise** AssertionError
- **if** a correct matrix is given **return** an array
"""
conf_pars = ConfigParameters('test1.cfg')
# Scenario 1
par_str = "1.0 0.0\n1.0"
err_mess = "Number of columns"
with self.assertRaisesRegexp(AssertionError, err_mess):
conf_pars.parse_string_tmatrix(par_str, real=True)
# Scenario 2
par_str = "1.0 0.0 2.0 1.0 0.0\n0.0 1.0 2.0 3.0 -1.0"
err_mess = "Complex matrix must"
with self.assertRaisesRegexp(AssertionError, err_mess):
conf_pars.parse_string_tmatrix(par_str, real=False)
# Scenario 3
par_str = "1.0 0.0 2.0 -3.0\n0.0 1.0 -1.0 1.0"
res = conf_pars.parse_string_tmatrix(par_str, real=False)
expected = np.array([[1.0, 2.0 - 3.0j], [1.0j, -1.0 + 1.0j]])
self.assertEqual(res, expected)
################################################################################
#
# test_parse_parameter_set()
#
################################################################################
def test_parse_parameter_set(self):
"""
Function:
def parse_parameter_set(self, section, param_set, excpetion=False)
Scenarios:
- **if** config-file section [Shell 1] contains 'LSHELL = 2' **and**
'lshell' and 'ions' are in `param_set` **return** a dictionary {'lshell': 2}
- **if** config-file section [Shell 1] contains 'LSHELL = 2' **and**
'lshell' and 'ions' are in `param_set` and
exception=True **raise** Exception
"""
conf_pars = ConfigParameters('test1.cfg')
param_set = conf_pars.sh_required # contains 'lshell' and 'ions'
# Scenario 1
res = conf_pars.parse_parameter_set('Shell 1', param_set)
expected = {'lshell': 2}
self.assertDictEqual(res, expected)
# Scenario 2
section = 'Shell 1'
err_mess = "Required parameter" # .* in section [%s]"%(section)
with self.assertRaisesRegexp(Exception, err_mess):
conf_pars.parse_parameter_set(section, param_set, exception=True)
################################################################################
#
# test_parse_shells()
#
################################################################################
def test_parse_shells(self):
"""
Function:
def parse_shells(self)
Scenarios:
- **if** config-file does not contain a valid [Shell] section
**raise** AssertionError
- **if** a [Shell] section does not contain a valid index
**raise** ValueError
- **if** a [Shell] section does not contain all required parameters
**raise** Exception
- **if** two correct [Shell] sections are defined
**return** a dictionary of shell parameters
"""
# Scenario 1
conf_pars = ConfigParameters('test2.cfg')
err_mess = "No projected shells"
with self.assertRaisesRegexp(AssertionError, err_mess):
conf_pars.parse_shells()
# Scenario 2
conf_pars = ConfigParameters('test3.cfg')
err_mess = "Failed to extract shell indices"
with self.assertRaisesRegexp(ValueError, err_mess):
conf_pars.parse_shells()
# Scenario 3
conf_pars = ConfigParameters('test4.cfg')
err_mess = "Required parameter"
with self.assertRaisesRegexp(Exception, err_mess):
conf_pars.parse_shells()
# Scenario 4
conf_pars = ConfigParameters('test5.cfg')
conf_pars.parse_shells()
res = conf_pars.shells
expected = {1: {'lshell': 2, 'ion_list': np.array([4, 5, 6, 7])},
2: {'lshell': 1, 'ion_list': np.array([0, 1, 2, 3]),
'tmatrix': np.array([[ 0., 1., 0.], [ 1., 0., 0.], [ 0., 0., 1.]])}}
self.assertSetEqual(set(res.keys()), set(expected.keys()))
arr = res[1].pop('ion_list')
arr_exp = expected[1].pop('ion_list')
self.assertEqual(arr, arr_exp)
arr = res[2].pop('ion_list')
arr_exp = expected[2].pop('ion_list')
self.assertEqual(arr, arr_exp)
arr = res[2].pop('tmatrix')
arr_exp = expected[2].pop('tmatrix')
self.assertEqual(arr, arr_exp)
self.assertDictEqual(res, expected)
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(TestSpecialParsers)
# unittest.TextTestRunner(verbosity=2, buffer=False).run(suite)
unittest.TextTestRunner(verbosity=2, buffer=True).run(suite)