#
# Copyright The NOMAD Authors.
#
# This file is part of NOMAD.
# See https://nomad-lab.eu for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import os
import re
import numpy as np
import logging
from datetime import datetime
from ase.data import chemical_symbols

from nomad.units import ureg
from nomad.parsing.parser import FairdiParser
from nomad.parsing.file_parser.text_parser import TextParser, Quantity, DataTextParser
from nomad.datamodel.metainfo.common_dft import Run, Method, MethodBasisSet,\
    BasisSetCellDependent, XCFunctionals, System, SingleConfigurationCalculation,\
    ScfIteration, Dos, Eigenvalues, SamplingMethod
from .metainfo import m_env
from .metainfo.abinit_autogenerated import x_abinit_section_dataset, x_abinit_section_input


ABINIT_NATIVE_IXC = {
    0: [{}],
    1: [{'XC_functional_name': 'LDA_XC_TETER93'}],
    2: [{'XC_functional_name': 'LDA_X'}, {'XC_functional_name': 'LDA_C_PZ'}],
    # 3 - LDA, old Teter rational polynomial parametrization (4/91)
    4: [{'XC_functional_name': 'LDA_X'}, {'XC_functional_name': 'LDA_C_WIGNER'}],
    5: [{'XC_functional_name': 'LDA_X'}, {'XC_functional_name': 'LDA_C_HL'}],
    6: [{'XC_functional_name': 'LDA_X'}, {'XC_functional_name': 'LDA_C_XALPHA'}],
    7: [{'XC_functional_name': 'LDA_X'}, {'XC_functional_name': 'LDA_C_PW'}],
    # 8 - x-only part of the Perdew-Wang 92 functional
    # 9 - x- and RPA correlation part of the Perdew-Wang 92 functional
    # 10 - non-existent
    11: [{'XC_functional_name': 'GGA_X_PBE'}, {'XC_functional_name': 'GGA_C_PBE'}],
    12: [{'XC_functional_name': 'GGA_X_PBE'}],
    13: [{'XC_functional_name': 'GGA_X_LB'}, {'XC_functional_name': 'LDA_C_PW'}],
    14: [{'XC_functional_name': 'GGA_X_PBE_R'}, {'XC_functional_name': '?'}],
    15: [{'XC_functional_name': 'GGA_X_RPBE'}, {'XC_functional_name': '?'}],
    16: [{'XC_functional_name': 'GGA_XC_HCTH_93'}],
    17: [{'XC_functional_name': 'GGA_XC_HCTH_120'}],
    18: [{'XC_functional_name': 'GGA_X_B88'}, {'XC_functional_name': 'GGA_C_LYP'}],
    19: [{'XC_functional_name': 'GGA_X_B88'}, {'XC_functional_name': 'GGA_C_P86'}],
    # 20 - Fermi-Amaldi xc ( -1/N Hartree energy, where N is the number of electrons per cell;
    #      G=0 is not taken into account however), for TDDFT tests.
    # 21 - same as 20, except that the xc-kernel is the LDA (ixc=1) one, for TDDFT tests.
    # 22 - same as 20, except that the xc-kernel is the Burke-Petersilka-Gross hybrid, for
    #      TDDFT tests.
    23: [{'XC_functional_name': 'GGA_X_WC'}, {'XC_functional_name': '?'}],
    24: [{'XC_functional_name': 'GGA_X_C09X'}, {'XC_functional_name': '?'}],
    # 25 - non-existent
    26: [{'XC_functional_name': 'GGA_XC_HCTH_147'}],
    27: [{'XC_functional_name': 'GGA_XC_HCTH_407'}],
    28: [{'XC_functional_name': 'GGA_X_OPTX'}, {'XC_functional_name': 'GGA_C_LYP'}],
    40: [{'XC_functional_name': 'HF_X'}],
    41: [{'XC_functional_name': 'HYB_GGA_XC_PBEH'}],
    42: [{'XC_functional_name': 'HYB_GGA_XC_PBE0_13'}]}

ABINIT_LIBXC_IXC = {
    1: {"XC_functional_name": "LDA_X"},
    2: {"XC_functional_name": "LDA_C_WIGNER"},
    3: {"XC_functional_name": "LDA_C_RPA"},
    4: {"XC_functional_name": "LDA_C_HL"},
    5: {"XC_functional_name": "LDA_C_GL"},
    6: {"XC_functional_name": "LDA_C_XALPHA"},
    7: {"XC_functional_name": "LDA_C_VWN"},
    8: {"XC_functional_name": "LDA_C_VWN_RPA"},
    9: {"XC_functional_name": "LDA_C_PZ"},
    10: {"XC_functional_name": "LDA_C_PZ_MOD"},
    11: {"XC_functional_name": "LDA_C_OB_PZ"},
    12: {"XC_functional_name": "LDA_C_PW"},
    13: {"XC_functional_name": "LDA_C_PW_MOD"},
    14: {"XC_functional_name": "LDA_C_OB_PW"},
    15: {"XC_functional_name": "LDA_C_2D_AMGB"},
    16: {"XC_functional_name": "LDA_C_2D_PRM"},
    17: {"XC_functional_name": "LDA_C_vBH"},
    18: {"XC_functional_name": "LDA_C_1D_CSC"},
    19: {"XC_functional_name": "LDA_X_2D"},
    20: {"XC_functional_name": "LDA_XC_TETER93"},
    21: {"XC_functional_name": "LDA_X_1D"},
    22: {"XC_functional_name": "LDA_C_ML1"},
    23: {"XC_functional_name": "LDA_C_ML2"},
    24: {"XC_functional_name": "LDA_C_GOMBAS"},
    25: {"XC_functional_name": "LDA_C_PW_RPA"},
    26: {"XC_functional_name": "LDA_C_1D_LOOS"},
    27: {"XC_functional_name": "LDA_C_RC04"},
    28: {"XC_functional_name": "LDA_C_VWN_1"},
    29: {"XC_functional_name": "LDA_C_VWN_2"},
    30: {"XC_functional_name": "LDA_C_VWN_3"},
    31: {"XC_functional_name": "LDA_C_VWN_4"},
    32: {"XC_functional_name": "GGA_X_GAM"},
    33: {"XC_functional_name": "GGA_C_GAM"},
    34: {"XC_functional_name": "GGA_X_HCTH_A"},
    35: {"XC_functional_name": "GGA_X_EV93"},
    36: {"XC_functional_name": "HYB_MGGA_X_DLDF"},
    37: {"XC_functional_name": "MGGA_C_DLDF"},
    38: {"XC_functional_name": "GGA_X_BCGP"},
    39: {"XC_functional_name": "GGA_C_BCGP"},
    40: {"XC_functional_name": "GGA_X_LAMBDA_OC2_N"},
    41: {"XC_functional_name": "GGA_X_B86_R"},
    42: {"XC_functional_name": "MGGA_XC_ZLP"},
    43: {"XC_functional_name": "LDA_XC_ZLP"},
    44: {"XC_functional_name": "GGA_X_LAMBDA_CH_N"},
    45: {"XC_functional_name": "GGA_X_LAMBDA_LO_N"},
    46: {"XC_functional_name": "GGA_X_HJS_B88_V2"},
    47: {"XC_functional_name": "GGA_C_Q2D"},
    48: {"XC_functional_name": "GGA_X_Q2D"},
    49: {"XC_functional_name": "GGA_X_PBE_MOL"},
    50: {"XC_functional_name": "LDA_K_TF"},
    51: {"XC_functional_name": "LDA_K_LP"},
    52: {"XC_functional_name": "GGA_K_TFVW"},
    53: {"XC_functional_name": "GGA_K_REVAPBEINT"},
    54: {"XC_functional_name": "GGA_K_APBEINT"},
    55: {"XC_functional_name": "GGA_K_REVAPBE"},
    56: {"XC_functional_name": "GGA_X_AK13"},
    57: {"XC_functional_name": "GGA_K_MEYER"},
    58: {"XC_functional_name": "GGA_X_LV_RPW86"},
    59: {"XC_functional_name": "GGA_X_PBE_TCA"},
    60: {"XC_functional_name": "GGA_X_PBEINT"},
    61: {"XC_functional_name": "GGA_C_ZPBEINT"},
    62: {"XC_functional_name": "GGA_C_PBEINT"},
    63: {"XC_functional_name": "GGA_C_ZPBESOL"},
    64: {"XC_functional_name": "MGGA_XC_OTPSS_D"},
    65: {"XC_functional_name": "GGA_XC_OPBE_D"},
    66: {"XC_functional_name": "GGA_XC_OPWLYP_D"},
    67: {"XC_functional_name": "GGA_XC_OBLYP_D"},
    68: {"XC_functional_name": "GGA_X_VMT84_GE"},
    69: {"XC_functional_name": "GGA_X_VMT84_PBE"},
    70: {"XC_functional_name": "GGA_X_VMT_GE"},
    71: {"XC_functional_name": "GGA_X_VMT_PBE"},
    72: {"XC_functional_name": "MGGA_C_CS"},
    73: {"XC_functional_name": "MGGA_C_MN12_SX"},
    74: {"XC_functional_name": "MGGA_C_MN12_L"},
    75: {"XC_functional_name": "MGGA_C_M11_L"},
    76: {"XC_functional_name": "MGGA_C_M11"},
    77: {"XC_functional_name": "MGGA_C_M08_SO"},
    78: {"XC_functional_name": "MGGA_C_M08_HX"},
    79: {"XC_functional_name": "GGA_C_N12_SX"},
    80: {"XC_functional_name": "GGA_C_N12"},
    81: {"XC_functional_name": "HYB_GGA_X_N12_SX"},
    82: {"XC_functional_name": "GGA_X_N12"},
    83: {"XC_functional_name": "GGA_C_REGTPSS"},
    84: {"XC_functional_name": "GGA_C_OP_XALPHA"},
    85: {"XC_functional_name": "GGA_C_OP_G96"},
    86: {"XC_functional_name": "GGA_C_OP_PBE"},
    87: {"XC_functional_name": "GGA_C_OP_B88"},
    88: {"XC_functional_name": "GGA_C_FT97"},
    89: {"XC_functional_name": "GGA_C_SPBE"},
    90: {"XC_functional_name": "GGA_X_SSB_SW"},
    91: {"XC_functional_name": "GGA_X_SSB"},
    92: {"XC_functional_name": "GGA_X_SSB_D"},
    93: {"XC_functional_name": "GGA_XC_HCTH_407P"},
    94: {"XC_functional_name": "GGA_XC_HCTH_P76"},
    95: {"XC_functional_name": "GGA_XC_HCTH_P14"},
    96: {"XC_functional_name": "GGA_XC_B97_GGA1"},
    97: {"XC_functional_name": "GGA_C_HCTH_A"},
    98: {"XC_functional_name": "GGA_X_BPCCAC"},
    99: {"XC_functional_name": "GGA_C_REVTCA"},
    100: {"XC_functional_name": "GGA_C_TCA"},
    101: {"XC_functional_name": "GGA_X_PBE"},
    102: {"XC_functional_name": "GGA_X_PBE_R"},
    103: {"XC_functional_name": "GGA_X_B86"},
    104: {"XC_functional_name": "GGA_X_HERMAN"},
    105: {"XC_functional_name": "GGA_X_B86_MGC"},
    106: {"XC_functional_name": "GGA_X_B88"},
    107: {"XC_functional_name": "GGA_X_G96"},
    108: {"XC_functional_name": "GGA_X_PW86"},
    109: {"XC_functional_name": "GGA_X_PW91"},
    110: {"XC_functional_name": "GGA_X_OPTX"},
    111: {"XC_functional_name": "GGA_X_DK87_R1"},
    112: {"XC_functional_name": "GGA_X_DK87_R2"},
    113: {"XC_functional_name": "GGA_X_LG93"},
    114: {"XC_functional_name": "GGA_X_FT97_A"},
    115: {"XC_functional_name": "GGA_X_FT97_B"},
    116: {"XC_functional_name": "GGA_X_PBE_SOL"},
    117: {"XC_functional_name": "GGA_X_RPBE"},
    118: {"XC_functional_name": "GGA_X_WC"},
    119: {"XC_functional_name": "GGA_X_MPW91"},
    120: {"XC_functional_name": "GGA_X_AM05"},
    121: {"XC_functional_name": "GGA_X_PBEA"},
    122: {"XC_functional_name": "GGA_X_MPBE"},
    123: {"XC_functional_name": "GGA_X_XPBE"},
    124: {"XC_functional_name": "GGA_X_2D_B86_MGC"},
    125: {"XC_functional_name": "GGA_X_BAYESIAN"},
    126: {"XC_functional_name": "GGA_X_PBE_JSJR"},
    127: {"XC_functional_name": "GGA_X_2D_B88"},
    128: {"XC_functional_name": "GGA_X_2D_B86"},
    129: {"XC_functional_name": "GGA_X_2D_PBE"},
    130: {"XC_functional_name": "GGA_C_PBE"},
    131: {"XC_functional_name": "GGA_C_LYP"},
    132: {"XC_functional_name": "GGA_C_P86"},
    133: {"XC_functional_name": "GGA_C_PBE_SOL"},
    134: {"XC_functional_name": "GGA_C_PW91"},
    135: {"XC_functional_name": "GGA_C_AM05"},
    136: {"XC_functional_name": "GGA_C_XPBE"},
    137: {"XC_functional_name": "GGA_C_LM"},
    138: {"XC_functional_name": "GGA_C_PBE_JRGX"},
    139: {"XC_functional_name": "GGA_X_OPTB88_VDW"},
    140: {"XC_functional_name": "GGA_X_PBEK1_VDW"},
    141: {"XC_functional_name": "GGA_X_OPTPBE_VDW"},
    142: {"XC_functional_name": "GGA_X_RGE2"},
    143: {"XC_functional_name": "GGA_C_RGE2"},
    144: {"XC_functional_name": "GGA_X_RPW86"},
    145: {"XC_functional_name": "GGA_X_KT1"},
    146: {"XC_functional_name": "GGA_XC_KT2"},
    147: {"XC_functional_name": "GGA_C_WL"},
    148: {"XC_functional_name": "GGA_C_WI"},
    149: {"XC_functional_name": "GGA_X_MB88"},
    150: {"XC_functional_name": "GGA_X_SOGGA"},
    151: {"XC_functional_name": "GGA_X_SOGGA11"},
    152: {"XC_functional_name": "GGA_C_SOGGA11"},
    153: {"XC_functional_name": "GGA_C_WI0"},
    154: {"XC_functional_name": "GGA_XC_TH1"},
    155: {"XC_functional_name": "GGA_XC_TH2"},
    156: {"XC_functional_name": "GGA_XC_TH3"},
    157: {"XC_functional_name": "GGA_XC_TH4"},
    158: {"XC_functional_name": "GGA_X_C09X"},
    159: {"XC_functional_name": "GGA_C_SOGGA11_X"},
    160: {"XC_functional_name": "GGA_X_LB"},
    161: {"XC_functional_name": "GGA_XC_HCTH_93"},
    162: {"XC_functional_name": "GGA_XC_HCTH_120"},
    163: {"XC_functional_name": "GGA_XC_HCTH_147"},
    164: {"XC_functional_name": "GGA_XC_HCTH_407"},
    165: {"XC_functional_name": "GGA_XC_EDF1"},
    166: {"XC_functional_name": "GGA_XC_XLYP"},
    167: {"XC_functional_name": "GGA_X_EB88"},
    168: {"XC_functional_name": "GGA_C_PBE_MOL"},
    169: {"XC_functional_name": "HYB_GGA_XC_PBE_MOL0"},
    170: {"XC_functional_name": "GGA_XC_B97_D"},
    171: {"XC_functional_name": "HYB_GGA_XC_PBE_SOL0"},
    172: {"XC_functional_name": "HYB_GGA_XC_PBEB0"},
    173: {"XC_functional_name": "GGA_XC_PBE1W"},
    174: {"XC_functional_name": "GGA_XC_MPWLYP1W"},
    175: {"XC_functional_name": "GGA_XC_PBELYP1W"},
    176: {"XC_functional_name": "HYB_GGA_XC_PBE_MOLB0"},
    177: {"XC_functional_name": "GGA_K_ABSP3"},
    178: {"XC_functional_name": "GGA_K_ABSP4"},
    182: {"XC_functional_name": "GGA_X_LBM"},
    183: {"XC_functional_name": "GGA_X_OL2"},
    184: {"XC_functional_name": "GGA_X_APBE"},
    185: {"XC_functional_name": "GGA_K_APBE"},
    186: {"XC_functional_name": "GGA_C_APBE"},
    187: {"XC_functional_name": "GGA_K_TW1"},
    188: {"XC_functional_name": "GGA_K_TW2"},
    189: {"XC_functional_name": "GGA_K_TW3"},
    190: {"XC_functional_name": "GGA_K_TW4"},
    191: {"XC_functional_name": "GGA_X_HTBS"},
    192: {"XC_functional_name": "GGA_X_AIRY"},
    193: {"XC_functional_name": "GGA_X_LAG"},
    194: {"XC_functional_name": "GGA_XC_MOHLYP"},
    195: {"XC_functional_name": "GGA_XC_MOHLYP2"},
    196: {"XC_functional_name": "GGA_XC_TH_FL"},
    197: {"XC_functional_name": "GGA_XC_TH_FC"},
    198: {"XC_functional_name": "GGA_XC_TH_FCFO"},
    199: {"XC_functional_name": "GGA_XC_TH_FCO"},
    200: {"XC_functional_name": "GGA_C_OPTC"},
    201: {"XC_functional_name": "MGGA_X_LTA"},
    202: {"XC_functional_name": "MGGA_X_TPSS"},
    203: {"XC_functional_name": "MGGA_X_M06_L"},
    204: {"XC_functional_name": "MGGA_X_GVT4"},
    205: {"XC_functional_name": "MGGA_X_TAU_HCTH"},
    206: {"XC_functional_name": "MGGA_X_BR89"},
    207: {"XC_functional_name": "MGGA_X_BJ06"},
    208: {"XC_functional_name": "MGGA_X_TB09"},
    209: {"XC_functional_name": "MGGA_X_RPP09"},
    210: {"XC_functional_name": "MGGA_X_2D_PRHG07"},
    211: {"XC_functional_name": "MGGA_X_2D_PRHG07_PRP10"},
    212: {"XC_functional_name": "MGGA_X_REVTPSS"},
    213: {"XC_functional_name": "MGGA_X_PKZB"},
    214: {"XC_functional_name": "MGGA_X_M05"},
    215: {"XC_functional_name": "MGGA_X_M05_2X"},
    216: {"XC_functional_name": "MGGA_X_M06_HF"},
    217: {"XC_functional_name": "MGGA_X_M06"},
    218: {"XC_functional_name": "MGGA_X_M06_2X"},
    219: {"XC_functional_name": "MGGA_X_M08_HX"},
    220: {"XC_functional_name": "MGGA_X_M08_SO"},
    221: {"XC_functional_name": "MGGA_X_MS0"},
    222: {"XC_functional_name": "MGGA_X_MS1"},
    223: {"XC_functional_name": "MGGA_X_MS2"},
    224: {"XC_functional_name": "HYB_MGGA_X_MS2H"},
    225: {"XC_functional_name": "MGGA_X_M11"},
    226: {"XC_functional_name": "MGGA_X_M11_L"},
    227: {"XC_functional_name": "MGGA_X_MN12_L"},
    229: {"XC_functional_name": "MGGA_C_CC06"},
    230: {"XC_functional_name": "MGGA_X_MK00"},
    231: {"XC_functional_name": "MGGA_C_TPSS"},
    232: {"XC_functional_name": "MGGA_C_VSXC"},
    233: {"XC_functional_name": "MGGA_C_M06_L"},
    234: {"XC_functional_name": "MGGA_C_M06_HF"},
    235: {"XC_functional_name": "MGGA_C_M06"},
    236: {"XC_functional_name": "MGGA_C_M06_2X"},
    237: {"XC_functional_name": "MGGA_C_M05"},
    238: {"XC_functional_name": "MGGA_C_M05_2X"},
    239: {"XC_functional_name": "MGGA_C_PKZB"},
    240: {"XC_functional_name": "MGGA_C_BC95"},
    241: {"XC_functional_name": "MGGA_C_REVTPSS"},
    242: {"XC_functional_name": "MGGA_XC_TPSSLYP1W"},
    243: {"XC_functional_name": "MGGA_X_MK00B"},
    244: {"XC_functional_name": "MGGA_X_BLOC"},
    245: {"XC_functional_name": "MGGA_X_MODTPSS"},
    246: {"XC_functional_name": "GGA_C_PBELOC"},
    247: {"XC_functional_name": "MGGA_C_TPSSLOC"},
    248: {"XC_functional_name": "HYB_MGGA_X_MN12_SX"},
    249: {"XC_functional_name": "MGGA_X_MBEEF"},
    250: {"XC_functional_name": "MGGA_X_MBEEFVDW"},
    254: {"XC_functional_name": "MGGA_XC_B97M_V"},
    255: {"XC_functional_name": "GGA_XC_VV10"},
    257: {"XC_functional_name": "MGGA_X_MVS"},
    258: {"XC_functional_name": "GGA_C_PBEFE"},
    259: {"XC_functional_name": "LDA_XC_KSDT"},
    260: {"XC_functional_name": "MGGA_X_MN15_L"},
    261: {"XC_functional_name": "MGGA_C_MN15_L"},
    262: {"XC_functional_name": "GGA_C_OP_PW91"},
    263: {"XC_functional_name": "MGGA_X_SCAN"},
    264: {"XC_functional_name": "HYB_MGGA_X_SCAN0"},
    265: {"XC_functional_name": "GGA_X_PBEFE"},
    266: {"XC_functional_name": "HYB_GGA_XC_B97_1p"},
    267: {"XC_functional_name": "MGGA_C_SCAN"},
    268: {"XC_functional_name": "HYB_MGGA_X_MN15"},
    269: {"XC_functional_name": "MGGA_C_MN15"},
    270: {"XC_functional_name": "GGA_X_CAP"},
    401: {"XC_functional_name": "HYB_GGA_XC_B3PW91"},
    402: {"XC_functional_name": "HYB_GGA_XC_B3LYP"},
    403: {"XC_functional_name": "HYB_GGA_XC_B3P86"},
    404: {"XC_functional_name": "HYB_GGA_XC_O3LYP"},
    405: {"XC_functional_name": "HYB_GGA_XC_mPW1K"},
    406: {"XC_functional_name": "HYB_GGA_XC_PBEH"},
    407: {"XC_functional_name": "HYB_GGA_XC_B97"},
    408: {"XC_functional_name": "HYB_GGA_XC_B97_1"},
    410: {"XC_functional_name": "HYB_GGA_XC_B97_2"},
    411: {"XC_functional_name": "HYB_GGA_XC_X3LYP"},
    412: {"XC_functional_name": "HYB_GGA_XC_B1WC"},
    413: {"XC_functional_name": "HYB_GGA_XC_B97_K"},
    414: {"XC_functional_name": "HYB_GGA_XC_B97_3"},
    415: {"XC_functional_name": "HYB_GGA_XC_MPW3PW"},
    416: {"XC_functional_name": "HYB_GGA_XC_B1LYP"},
    417: {"XC_functional_name": "HYB_GGA_XC_B1PW91"},
    418: {"XC_functional_name": "HYB_GGA_XC_mPW1PW"},
    419: {"XC_functional_name": "HYB_GGA_XC_MPW3LYP"},
    420: {"XC_functional_name": "HYB_GGA_XC_SB98_1a"},
    421: {"XC_functional_name": "HYB_GGA_XC_SB98_1b"},
    422: {"XC_functional_name": "HYB_GGA_XC_SB98_1c"},
    423: {"XC_functional_name": "HYB_GGA_XC_SB98_2a"},
    424: {"XC_functional_name": "HYB_GGA_XC_SB98_2b"},
    425: {"XC_functional_name": "HYB_GGA_XC_SB98_2c"},
    426: {"XC_functional_name": "HYB_GGA_X_SOGGA11_X"},
    427: {"XC_functional_name": "HYB_GGA_XC_HSE03"},
    428: {"XC_functional_name": "HYB_GGA_XC_HSE06"},
    429: {"XC_functional_name": "HYB_GGA_XC_HJS_PBE"},
    430: {"XC_functional_name": "HYB_GGA_XC_HJS_PBE_SOL"},
    431: {"XC_functional_name": "HYB_GGA_XC_HJS_B88"},
    432: {"XC_functional_name": "HYB_GGA_XC_HJS_B97X"},
    433: {"XC_functional_name": "HYB_GGA_XC_CAM_B3LYP"},
    434: {"XC_functional_name": "HYB_GGA_XC_TUNED_CAM_B3LYP"},
    435: {"XC_functional_name": "HYB_GGA_XC_BHANDH"},
    436: {"XC_functional_name": "HYB_GGA_XC_BHANDHLYP"},
    437: {"XC_functional_name": "HYB_GGA_XC_MB3LYP_RC04"},
    438: {"XC_functional_name": "HYB_MGGA_XC_M05"},
    439: {"XC_functional_name": "HYB_MGGA_XC_M05_2X"},
    440: {"XC_functional_name": "HYB_MGGA_XC_B88B95"},
    441: {"XC_functional_name": "HYB_MGGA_XC_B86B95"},
    442: {"XC_functional_name": "HYB_MGGA_XC_PW86B95"},
    443: {"XC_functional_name": "HYB_MGGA_XC_BB1K"},
    444: {"XC_functional_name": "HYB_MGGA_XC_M06_HF"},
    445: {"XC_functional_name": "HYB_MGGA_XC_MPW1B95"},
    446: {"XC_functional_name": "HYB_MGGA_XC_MPWB1K"},
    447: {"XC_functional_name": "HYB_MGGA_XC_X1B95"},
    448: {"XC_functional_name": "HYB_MGGA_XC_XB1K"},
    449: {"XC_functional_name": "HYB_MGGA_XC_M06"},
    450: {"XC_functional_name": "HYB_MGGA_XC_M06_2X"},
    451: {"XC_functional_name": "HYB_MGGA_XC_PW6B95"},
    452: {"XC_functional_name": "HYB_MGGA_XC_PWB6K"},
    453: {"XC_functional_name": "HYB_GGA_XC_MPWLYP1M"},
    454: {"XC_functional_name": "HYB_GGA_XC_REVB3LYP"},
    455: {"XC_functional_name": "HYB_GGA_XC_CAMY_BLYP"},
    456: {"XC_functional_name": "HYB_GGA_XC_PBE0_13"},
    457: {"XC_functional_name": "HYB_MGGA_XC_TPSSH"},
    458: {"XC_functional_name": "HYB_MGGA_XC_REVTPSSH"},
    459: {"XC_functional_name": "HYB_GGA_XC_B3LYPs"},
    460: {"XC_functional_name": "HYB_MGGA_XC_M08_HX"},
    461: {"XC_functional_name": "HYB_MGGA_XC_M08_SO"},
    462: {"XC_functional_name": "HYB_MGGA_XC_M11"},
    463: {"XC_functional_name": "HYB_GGA_XC_WB97"},
    464: {"XC_functional_name": "HYB_GGA_XC_WB97X"},
    465: {"XC_functional_name": "HYB_GGA_XC_LRC_WPBEH"},
    466: {"XC_functional_name": "HYB_GGA_XC_WB97X_V"},
    467: {"XC_functional_name": "HYB_GGA_XC_LCY_PBE"},
    468: {"XC_functional_name": "HYB_GGA_XC_LCY_BLYP"},
    469: {"XC_functional_name": "HYB_GGA_XC_LC_VV10"},
    470: {"XC_functional_name": "HYB_GGA_XC_CAMY_B3LYP"},
    471: {"XC_functional_name": "HYB_GGA_XC_WB97X_D"},
    472: {"XC_functional_name": "HYB_GGA_XC_HPBEINT"},
    473: {"XC_functional_name": "HYB_GGA_XC_LRC_WPBE"},
    474: {"XC_functional_name": "HYB_MGGA_X_MVSH"},
    475: {"XC_functional_name": "HYB_GGA_XC_B3LYP5"},
    476: {"XC_functional_name": "HYB_GGA_XC_EDF2"},
    477: {"XC_functional_name": "HYB_GGA_XC_CAP0"},
    478: {"XC_functional_name": "HYB_GGA_XC_LC_WPBE"},
    500: {"XC_functional_name": "GGA_K_VW"},
    501: {"XC_functional_name": "GGA_K_GE2"},
    502: {"XC_functional_name": "GGA_K_GOLDEN"},
    503: {"XC_functional_name": "GGA_K_YT65"},
    504: {"XC_functional_name": "GGA_K_BALTIN"},
    505: {"XC_functional_name": "GGA_K_LIEB"},
    506: {"XC_functional_name": "GGA_K_ABSP1"},
    507: {"XC_functional_name": "GGA_K_ABSP2"},
    508: {"XC_functional_name": "GGA_K_GR"},
    509: {"XC_functional_name": "GGA_K_LUDENA"},
    510: {"XC_functional_name": "GGA_K_GP85"},
    511: {"XC_functional_name": "GGA_K_PEARSON"},
    512: {"XC_functional_name": "GGA_K_OL1"},
    513: {"XC_functional_name": "GGA_K_OL2"},
    514: {"XC_functional_name": "GGA_K_FR_B88"},
    515: {"XC_functional_name": "GGA_K_FR_PW86"},
    516: {"XC_functional_name": "GGA_K_DK"},
    517: {"XC_functional_name": "GGA_K_PERDEW"},
    518: {"XC_functional_name": "GGA_K_VSK"},
    519: {"XC_functional_name": "GGA_K_VJKS"},
    520: {"XC_functional_name": "GGA_K_ERNZERHOF"},
    521: {"XC_functional_name": "GGA_K_LC94"},
    522: {"XC_functional_name": "GGA_K_LLP"},
    523: {"XC_functional_name": "GGA_K_THAKKAR"},
    524: {"XC_functional_name": "GGA_X_WPBEH"},
    525: {"XC_functional_name": "GGA_X_HJS_PBE"},
    526: {"XC_functional_name": "GGA_X_HJS_PBE_SOL"},
    527: {"XC_functional_name": "GGA_X_HJS_B88"},
    528: {"XC_functional_name": "GGA_X_HJS_B97X"},
    529: {"XC_functional_name": "GGA_X_ITYH"},
    530: {"XC_functional_name": "GGA_X_SFAT"},
    531: {"XC_functional_name": "HYB_MGGA_XC_WB97M_V"}}


class AbinitOutParser(TextParser):
    def __init__(self):
        self.energy_components = {
            'kinetic': 'Kinetic energy',
            'hartree': 'Hartree energy',
            'xc': 'XC energy',
            'ewald': 'Ewald energy',
            'psp_core': 'PspCore energy',
            'psp_local': 'Loc. psp. energy',
            'psp_nonlocal': 'NL   psp  energy',
            'internal': '>>>>> Internal E',
            'ktentropy': r'\-kT*entropy',
            'total': ' >>>>>>>>> Etotal',
            'band': r'Band energy \(Ha\)'}
        self._input_vars = None
        self._n_datasets = None
        super().__init__(None)

    def init_quantities(self):
        self._quantities = [
            Quantity(
                'program_version', r'\.Version ([\w\.]+) of ABINIT', repeats=False,
                convert=False, flatten=False),
            Quantity(
                'x_abinit_parallel_compilation', r'\.\((\w+) version,', repeats=False,
                convert=False, flatten=False),
            Quantity(
                'program_compilation_host', r'prepared for a ([\w\.]+) computer',
                repeats=False, convert=False, flatten=False),
            Quantity(
                'x_abinit_start_date', r'\.Starting date : ([\w ]+)\.', repeats=False,
                convert=False, flatten=False),
            Quantity(
                'x_abinit_start_time', r'\- \( at (\w+) \)', repeats=False, convert=False,
                flatten=False),
            Quantity(
                'x_abinit_input_file', r'\- input\s*file\s*\-\> ([\w\.]+)', repeats=False,
                convert=False, flatten=False),
            Quantity(
                'x_abinit_output_file', r'\- output\s*file\s*\-\> ([\w\.]+)', repeats=False,
                convert=False, flatten=False),
            Quantity(
                'x_abinit_input_files_root', r'\- root for input\s*files \-\> (\w+)',
                repeats=False, convert=False, flatten=False),
            Quantity(
                'x_abinit_output_files_root', r'\- root for output\s*files \-\> (\w+)',
                repeats=False, convert=False, flatten=False),
            Quantity(
                'x_abinit_total_cpu_time',
                r'\-\s*Total cpu\s*time\s*\(s,m,h\):\s*([\d\.]+)', dtype=float),
            Quantity(
                'x_abinit_total_wallclock_time',
                r'\-\s*Total wall clock time\s*\(s,m,h\):\s*([\d\.]+)', dtype=float),
            Quantity(
                'run_clean_end',
                r'(Calculation completed)', repeats=False, convert=False, flatten=False)]

        self._quantities.append(Quantity(
            'input_vars',
            r'\-outvars: echo values of preprocessed input variables \-+([\s\S]+?)\={10}',
            repeats=False, sub_parser=TextParser(quantities=[Quantity(
                'key_value', r'(\w+)\s*([\d\.\+\-E\s]+)', repeats=True)])))

        def str_to_array(val_in):
            val = val_in.strip().split('\n')
            val = [v.split()[-3:] for v in val]
            return np.array(val, dtype=float)

        def str_to_stress_tensor(val_in):
            val = np.array(val_in.split(), dtype=float)
            stress_tensor = np.zeros((3, 3))
            stress_tensor[0][0] = val[0]
            stress_tensor[2][1] = stress_tensor[1][2] = val[1]
            stress_tensor[1][1] = val[2]
            stress_tensor[2][0] = stress_tensor[0][2] = val[3]
            stress_tensor[2][2] = val[4]
            stress_tensor[1][0] = stress_tensor[0][1] = val[5]
            return stress_tensor * (ureg.hartree / ureg.bohr**3)

        def str_to_eigenvalues(val_in):
            return [float(v) for v in val_in.split() if v[-1].isdecimal()]

        self_consistent = [
            Quantity(
                'energy_total_scf_iteration',
                r'ETOT\s*\d+\s*([\+\-\d\.Ee ]+)\n', repeats=True, dtype=float),
            Quantity(
                'convergence',
                r'At SCF step\s*([0-9]+)\s*'
                r', etot|, forces|vres2\s*=\s*[\-\+\d\.Ee]+?\s*<\s*tolvrs=\s*[\-\+\d\.Ee]+?\s*=>\s*'
                r'([\w ]+)', repeats=False),
        ]

        relaxation = [
            Quantity(
                'stress_tensor',
                r'Cartesian components of stress tensor \(hartree/bohr\^3\)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*',
                repeats=False, str_operation=str_to_stress_tensor, convert=False),
            Quantity(
                'self_consistent',
                r'SELF\-CONSISTENT\-FIELD CONVERGENCE\-*\s*(iter\s*Etot[\s\S]+?)\-*OUTPUT',
                sub_parser=TextParser(quantities=self_consistent), repeats=False),
            Quantity(
                'cartesian_coordinates',
                r'Cartesian coordinates \(xcart\) \[bohr\]\s*([\s\d\.Ee\+\-]+)',
                repeats=False, str_operation=str_to_array, convert=False, unit=ureg.bohr),
            Quantity(
                'cartesian_forces',
                r'Cartesian forces \(fcart\)[\s\S]+?\(free atoms\)\s*([\s\d\.Ee\+\-]+)',
                repeats=False, str_operation=str_to_array, convert=False, unit=ureg.hartree / ureg.bohr),
            Quantity(
                'energy_total',
                r'Total energy \(etotal\) \[Ha\]=\s*([\-\+\d\.Ee]+)', repeats=False,
                dtype=float, unit=ureg.hartree)]

        energy_components = [Quantity(
            key, r'\s*%s\s*=\s*([\d\.E\-\+]+)' % val, repeats=False, dtype=float,
            unit=ureg.hartree) for key, val in self.energy_components.items()]

        results = [
            Quantity(
                'cartesian_coordinates',
                r'\s*cartesian coordinates \(angstrom\) at end:\s*([\s\d\.Ee\+\-]+)',
                repeats=False, str_operation=str_to_array, convert=False, unit=ureg.angstrom),
            Quantity(
                'cartesian_forces',
                r'\s*cartesian forces \(hartree/bohr\) at end\:\s*([\s\d\.Ee\+\-]+)',
                repeats=False, str_operation=str_to_array, convert=False, unit=ureg.hartree / ureg.bohr),
            Quantity(
                'x_abinit_eig_filename',
                r'\s*prteigrs : about to open file\s*(\S+)', repeats=False,
                convert=False),
            Quantity(
                'fermi_energy',
                r'\s*Fermi \(or HOMO\) energy \(hartree\) =\s*([-+0-9.]+)',
                repeats=False, dtype=float, unit=ureg.hartree),
            Quantity(
                'x_abinit_magnetisation',
                r'\s*Magnetisation \(Bohr magneton\)=\s*([-+0-9.eEdD]*)\s*',
                repeats=False, dtype=float, unit=ureg.bohr_magneton),
            Quantity(
                'eigenvalues',
                r'\s*kpt#\s*(\d+), nband=\s*(\d+), wtk=\s*([\d\.]+)\s*, '
                r'kpt=([\d\.\-\+Ee ]+)\s*\(reduced coord\)\s*([\d\.\-\+Ee\s]+)',
                repeats=True, str_operation=str_to_eigenvalues, convert=False),
            Quantity(
                'occupation_numbers',
                r'\s*occupation numbers for kpt#\s*\d+\s*([\d\.\-\+Ee ]+)',
                repeats=True, dtype=float),
            Quantity(
                'energy_total',
                r'Total energy\(eV\)=\s*([\d\.\-\+Ee]+)\s*;',
                repeats=False, dtype=float, unit=ureg.eV),
            Quantity(
                'stress_tensor',
                r'Cartesian components of stress tensor \(hartree/bohr\^3\)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*'
                r'sigma\(\d \d\)\s*=\s*([\-\+\d\.Ee]+)\s*',
                repeats=False, str_operation=str_to_stress_tensor, convert=False)
        ] + energy_components

        self._quantities.append(Quantity(
            'dataset',
            r'==\s*(DATASET\s*\d+\s*==[\s\S]+?)(?:\-Cartesian components of stress tensor \(GPa\)|== END DATASET)',
            repeats=True, sub_parser=TextParser(quantities=[
                Quantity(
                    'x_abinit_dataset_number',
                    r'DATASET\s*(\d+)', dtype=int, repeats=False),
                Quantity(
                    'x_abinit_var_ixc',
                    r'\-\s*ixc\s*=\s*([\d\-]+)', dtype=int, repeats=False),
                Quantity(
                    'x_abinit_vprim',
                    r'R\(\d\)=\s*(\-*[\d\.]+)\s*(\-*[\d\.]+)\s*(\-*[\d\.]+)\s*G\(\d\)=',
                    repeats=True, dtype=float),
                Quantity(
                    'x_abinit_unit_cell_volume',
                    r'Unit cell volume ucvol\s*=\s*([\d\.\+Ee]+)', dtype=float,
                    unit=ureg.bohr ** 3),
                Quantity(
                    'self_consistent',
                    r'\={10}\s*(iter\s*Etot[\s\S]+?)\={10}', repeats=False,
                    sub_parser=TextParser(quantities=self_consistent)),
                Quantity(
                    'relaxation',
                    r'\-\-\- Iteration: \( \d+\/\d+\)([\s\S]+?Total energy \(etotal\) \[Ha\]=\s*[\-\+\d\.Ee]+)',
                    repeats=True, sub_parser=TextParser(quantities=relaxation)),
                Quantity(
                    'results',
                    r'\-+iterations are completed or convergence reached\-+([\s\S]+)'
                    r'(?:(sigma\(\d \d\)\s*=\s*[\+\-\d\.E]+)|={80})', repeats=False,
                    sub_parser=TextParser(quantities=results))])))

    @property
    def n_datasets(self):
        if self._n_datasets is None:
            # we could also simply return the length of dataset but this is safer
            dataset_numbers = [
                d.get('x_abinit_dataset_number', 1) for d in self.get('dataset', [])]
            self._n_datasets = max(dataset_numbers) if dataset_numbers else 1
        return self._n_datasets

    @property
    def input_vars(self):
        if self._input_vars is None:
            # set defaults
            self._input_vars = {key: [1] + [None] * (self.n_datasets - 1) for key in [
                'ntypat', 'npsp', 'nshiftk', 'natrd', 'nsppol', 'nspden', 'nkpt',
                'occopt', 'ixc']}

            key_val = self.get('input_vars', {}).get('key_value', [])
            for i in range(len(key_val)):
                key, n_dataset = re.search(r'(\D+)?(\d*)', key_val[i][0]).groups()
                self._input_vars.setdefault(key, [None] * self.n_datasets)

                m_quantity = m_env.all_definitions_by_name.get(
                    'x_abinit_var_%s' % key, [None])[0]
                if m_quantity is None:
                    continue

                val = np.array(key_val[i][1:], dtype=m_quantity.type)
                if not m_quantity.shape:
                    val = val[0]
                if n_dataset:
                    self._input_vars[key][int(n_dataset) - 1] = val
                else:
                    self._input_vars[key] = [val] * self.n_datasets
        return self._input_vars

    def get_input_var(self, key, n_dataset, default=None):
        val = self.input_vars.get(key)
        if val is None:
            val = [default] * n_dataset
        return val[n_dataset - 1]

    def get_atom_labels(self, n_dataset):
        znucl = self.get_input_var('znucl', n_dataset)
        typat = self.get_input_var('typat', n_dataset)
        if znucl is None or typat is None:
            return

        return [chemical_symbols[int(znucl[n_at - 1])] for n_at in typat]


class AbinitParser(FairdiParser):
    def __init__(self):
        super().__init__(
            name='parsers/abinit', code_name='ABINIT', code_homepage='https://www.abinit.org/',
            mainfile_contents_re=(r'^\n*\.Version\s*[0-9.]*\s*of ABINIT\s*'))
        self._metainfo_env = m_env
        self.out_parser = AbinitOutParser()
        self.dos_parser = DataTextParser()

    def parse_system(self, n_dataset, section):
        dataset = self.out_parser.get('dataset')[n_dataset]
        sec_run = self.archive.section_run[-1]

        nd = dataset.get('x_abinit_dataset_number')
        atom_labels = self.out_parser.get_atom_labels(nd)
        atom_positions = section.get('cartesian_coordinates')
        if atom_labels is None or atom_positions is None:
            return

        sec_system = sec_run.m_create(System)
        sec_system.atom_labels = atom_labels
        sec_system.atom_positions = atom_positions
        sec_system.configuration_periodic_dimensions = [True, True, True]
        sec_system.number_of_atoms = len(atom_labels)
        lattice_vectors = dataset.get('x_abinit_vprim')
        lattice_vectors = np.eye(3) if lattice_vectors is None else lattice_vectors * ureg.bohr
        sec_system.lattice_vectors = lattice_vectors
        sec_system.simulation_cell = lattice_vectors

        return sec_system

    def parse_scc(self, n_dataset, section):
        dataset = self.out_parser.get('dataset')[n_dataset]
        sec_run = self.archive.section_run[-1]

        energy_total = section.get('energy_total')

        sec_scc = sec_run.m_create(SingleConfigurationCalculation)
        sec_scc.energy_total = energy_total
        forces = section.get('cartesian_forces')
        if forces is not None:
            sec_scc.atom_forces_raw = forces

        stress_tensor = section.get('stress_tensor')
        if stress_tensor is not None:
            sec_scc.stress_tensor = stress_tensor

        nsppol = self.out_parser.get_input_var(
            'nsppol', dataset.get('x_abinit_dataset_number'), 1)
        fermi_energy = section.get('fermi_energy')
        if fermi_energy is not None:
            sec_scc.energy_reference_fermi = ([fermi_energy.magnitude] * nsppol) * fermi_energy.units

        # energy contributions, should be moved to EnergyContributions
        # TODO fix metainfo for energy contributions
        for key in self.out_parser.energy_components.keys():
            energy = section.get(key)
            if energy is not None:
                # no units in metainfo
                setattr(sec_scc, 'x_abinit_energy_%s' % key, energy.to('joule').magnitude)

        return sec_scc

    def parse_method(self, n_dataset):
        dataset = self.out_parser.get('dataset')[n_dataset]
        sec_run = self.archive.section_run[-1]
        smearing_kinds = [
            '', '', '', 'fermi', 'marzari-vanderbilt', 'marzari-vanderbilt',
            'methfessel-paxton', 'gaussian', '']
        nd = dataset.get('x_abinit_dataset_number')
        sec_method = sec_run.m_create(Method)

        sec_method.electronic_structure_method = 'DFT'
        sec_method.stress_tensor_method = 'analytic'
        sec_method.number_of_spin_channels = int(self.out_parser.get_input_var('nsppol', nd, 1))
        sec_method.scf_max_iteration = int(self.out_parser.get_input_var('nstep', nd, 1))

        toldfe = self.out_parser.get_input_var('toldfe', nd)
        if toldfe is not None:
            sec_method.scf_threshold_energy_change = toldfe * ureg.hartree
        # not sure what this is for
        sec_method.self_interaction_correction_method = ''
        occopt = self.out_parser.get_input_var('occopt', nd, 1)
        smearing = smearing_kinds[occopt] if occopt < len(smearing_kinds) else ''
        sec_method.smearing_kind = smearing
        tsmear = self.out_parser.get_input_var('tsmear', nd)
        if tsmear is not None:
            sec_method.smearing_width = (tsmear * ureg.hartree).to('joule').magnitude

        sec_method_basis_set = sec_method.m_create(MethodBasisSet)
        sec_method_basis_set.method_basis_set_kind = 'wavefunction'
        sec_method_basis_set.mapping_section_method_basis_set_cell_associated =\
            sec_run.section_basis_set_cell_dependent[-1]

        ixc = self.out_parser.get_input_var('ixc', nd)
        ixc = dataset.get('x_abinit_var_ixc', 0) if ixc is None else ixc

        if ixc >= 0:
            xc_functionals = ABINIT_NATIVE_IXC.get(ixc, [])
        else:
            xc_functionals = []
            functional1 = -ixc // 1000
            if functional1 > 0:
                xc_functionals.append(ABINIT_LIBXC_IXC.get(functional1))
            functional2 = -ixc - (-ixc // 1000) * 1000
            if functional2 > 0:
                xc_functionals.append(ABINIT_LIBXC_IXC.get(functional2))

        for xc_functional in xc_functionals:
            sec_XC_functionals = sec_method.m_create(XCFunctionals)
            for key, value in sorted(xc_functional.items()):
                setattr(sec_XC_functionals, key, value)

        return sec_method

    def parse_sampling_method(self, n_dataset):
        dataset = self.out_parser.get('dataset')[n_dataset]
        sec_run = self.archive.section_run[-1]
        nd = dataset.get('x_abinit_dataset_number')
        sec_sampling_method = sec_run.m_create(SamplingMethod)

        geometry_optimizations = {
            1: 'viscous_damped_md', 2: 'bfgs', 3: 'bfgs', 4: 'conjugate_gradient',
            5: 'steepest_descent', 7: 'quenched_md', 10: 'dic_bfgs', 11: 'dic_bfgs',
            20: 'diis'}

        ionmov = self.out_parser.get_input_var('ionmov', nd)
        vis = self.out_parser.get_input_var('vis', nd, 0.0)
        if ionmov in [2, 3, 4, 5, 7, 10, 11, 20] or (ionmov == 1 and vis > 0.0):
            sampling_method = 'geometry_optimization'
            sec_sampling_method.geometry_optimization_method = geometry_optimizations[ionmov]
        elif ionmov in [6, 8, 12, 13, 14, 23] or (ionmov == 1 and vis == 0.0):
            sampling_method = 'molecular_dynamics'
        elif ionmov == 9:
            sampling_method = 'langevin_dynamics'
        else:
            sampling_method = ''
        sec_sampling_method.sampling_method = sampling_method

        tolmxf = self.out_parser.get_input_var('tolmxf', nd)
        if tolmxf is not None:
            sec_sampling_method.geometry_optimization_threshold_force = tolmxf * (ureg.hartree / ureg.bohr)

        tolmxde = self.out_parser.get_input_var('tolmxde', nd)
        if tolmxde is not None:
            sec_sampling_method.geometry_optimization_energy_change = tolmxde * ureg.hartree

    def parse_dataset(self, n_dataset):
        dataset = self.out_parser.get('dataset')[n_dataset]
        sec_run = self.archive.section_run[-1]
        sec_basis_set_cell_dependent = sec_run.m_create(BasisSetCellDependent)
        sec_basis_set_cell_dependent.basis_set_cell_dependent_kind = 'plane_waves'

        nd = dataset.get('x_abinit_dataset_number')
        ecut = self.out_parser.get_input_var('ecut', nd)
        if ecut is not None:
            ecut = ecut * ureg.hartree
            sec_basis_set_cell_dependent.basis_set_planewave_cutoff = ecut
            name = 'PW_%s' % ecut.to('rydberg').magnitude
            sec_basis_set_cell_dependent.basis_set_cell_dependent_name = name

        # one section_method per dataset
        sec_method = self.parse_method(n_dataset)

        self.parse_sampling_method(n_dataset)

        def parse_configurations(section):
            sec_system = self.parse_system(n_dataset, section)
            sec_scc = self.parse_scc(n_dataset, section)

            if sec_scc is None:
                return

            if sec_system is not None:
                sec_scc.single_configuration_calculation_to_system_ref = sec_system
            sec_scc.single_configuration_to_calculation_method_ref = sec_method

        def parse_dos():
            file_root = self.out_parser.get('x_abinit_output_files_root')
            if file_root is None:
                file_root = '%s_o' % os.path.basename(self.filepath).rstrip('.out')
            dos_file = os.path.join(self.maindir, '%s_DS%d_DOS' % (file_root, nd))
            if not os.path.isfile(dos_file):
                return

            self.dos_parser.mainfile = dos_file
            dos = self.dos_parser.data
            nsppol = self.out_parser.get_input_var('nsppol', nd, 1)
            dos = np.reshape(dos, (nsppol, len(dos) // nsppol, np.size(dos) // len(dos)))

            sec_scc = sec_run.section_single_configuration_calculation[-1]
            sec_dos = sec_scc.m_create(Dos)
            sec_dos.dos_kind = 'electronic'

            sec_dos.dos_energies = dos.T[0].T[0] * ureg.hartree
            sec_dos.number_of_dos_values = np.shape(dos)[1]

            unit_volume = dataset.get('x_abinit_unit_cell_volume')
            dos_values = dos.T[1].T * (1 / ureg.hartree) * unit_volume.magnitude
            sec_dos.dos_values = dos_values.to('1/joule').magnitude
            integrated_dos = dos.T[2].T * unit_volume.magnitude
            sec_dos.dos_integrated_values = integrated_dos

        def parse_eigenvalues():
            data = dataset.get('results', {}).get('eigenvalues', None)
            if data is None:
                return

            nsppol = self.out_parser.get_input_var('nsppol', nd, 1)
            data = np.reshape(data, (nsppol, len(data) // nsppol, np.size(data) // len(data)))

            nband = int(data.T[1].T[0][0])
            kpts = data.T[3:6].T[0]
            eigs = data.T[6:6 + nband].T

            sec_scc = sec_run.section_single_configuration_calculation[-1]
            sec_eigenvalues = sec_scc.m_create(Eigenvalues)
            sec_eigenvalues.eigenvalues_kind = 'normal'
            sec_eigenvalues.number_of_eigenvalues = nband
            sec_eigenvalues.number_of_eigenvalues_kpoints = len(kpts)
            sec_eigenvalues.eigenvalues_kpoints = kpts
            sec_eigenvalues.eigenvalues_values = eigs * ureg.hartree
            occs = dataset.get('results', {}).get('occupation_numbers')
            if occs is not None:
                occs = np.reshape(occs, (nsppol, len(occs) // nsppol, np.size(occs) // len(occs)))
                sec_eigenvalues.eigenvalues_occupation = occs

        def parse_scf(section):
            if not sec_run.section_single_configuration_calculation:
                return

            sec_scc = sec_run.section_single_configuration_calculation[-1]

            self_consistent = section.get('self_consistent', {})
            if self_consistent.get('convergence', None) in ['converged', 'is converged', 'are converged']:
                sec_scc.single_configuration_calculation_converged = True

            energy_total_scf_iteration = self_consistent.get('energy_total_scf_iteration', [])
            for energy in energy_total_scf_iteration:
                sec_scf_iteration = sec_scc.m_create(ScfIteration)
                sec_scf_iteration.energy_total_scf_iteration = energy[0] * ureg.hartree

        # results of the single point calculation
        results = dataset.get('results')
        parse_configurations(results)
        # add scf iterations
        parse_scf(dataset)
        # add dos and eigenvalues to full-scf calculation
        parse_dos()
        parse_eigenvalues()

        # relaxations
        relaxation = dataset.get('relaxation', [])
        for step in relaxation:
            parse_configurations(step)
            parse_scf(step)

    def parse_var(self):
        # TODO reshape input_var arrays
        sec_run = self.archive.section_run[-1]
        for i in range(len(self.out_parser.get('dataset', []))):
            sec_dataset = sec_run.m_create(x_abinit_section_dataset)
            sec_input = sec_dataset.m_create(x_abinit_section_input)
            for key, val in self.out_parser.input_vars.items():
                if val[i] is None:
                    continue
                setattr(sec_input, 'x_abinit_var_%s' % (key), val[i])

    def init_parser(self):
        self.out_parser.mainfile = self.filepath
        self.out_parser.logger = self.logger
        self.out_parser._input_vars = None
        self.out_parser._n_datasets = None

    def reuse_parser(self, parser):
        self.out_parser.quantities = parser.out_parser.quantities

    def parse(self, filepath, archive, logger):
        self.filepath = os.path.abspath(filepath)
        self.archive = archive
        self.maindir = os.path.dirname(self.filepath)
        self.logger = logger if logger is not None else logging

        self.init_parser()

        sec_run = self.archive.m_create(Run)
        sec_run.program_name = 'ABINIT'
        sec_run.program_basis_set_type = 'plane waves'

        section_run_keys = [
            'program_version', 'x_abinit_parallel_compilation', 'program_compilation_host',
            'x_abinit_start_date', 'x_abinit_start_time', 'x_abinit_input_file',
            'x_abinit_output_file', 'x_abinit_input_files_root', 'x_abinit_output_files_root',
            'x_abinit_total_cpu_time', 'x_abinit_total_wallclock_time']

        for key in section_run_keys:
            val = self.out_parser.get(key)
            if val is None:
                continue
            setattr(sec_run, key, val)

        if self.out_parser.get('run_clean_end') is not None:
            sec_run.run_clean_end = True

        abi_time = datetime.strptime(str("%s %s") % (
            self.out_parser.get('x_abinit_start_date'),
            self.out_parser.get('x_abinit_start_time')), "%a %d %b %Y %Hh%M")
        sec_run.time_run_date_start = (abi_time - datetime(1970, 1, 1)).total_seconds()

        self.parse_var()

        for n in range(len(self.out_parser.get('dataset', []))):
            self.parse_dataset(n)
