##
# File: GromacsPTParserListener.py
# Date: 02-Jun-2022
#
# Updates:
""" ParserLister class for GROMACS PT files.
    @author: Masashi Yokochi
"""
import sys
import collections
import copy

from antlr4 import ParseTreeListener
from rmsd.calculate_rmsd import NAMES_ELEMENT  # noqa: F401 pylint: disable=no-name-in-module, import-error, unused-import

try:
    from wwpdb.utils.align.alignlib import PairwiseAlign  # pylint: disable=no-name-in-module
    from wwpdb.utils.nmr.mr.GromacsPTParser import GromacsPTParser
    from wwpdb.utils.nmr.mr.ParserListenerUtil import (coordAssemblyChecker,
                                                       translateToStdAtomName,
                                                       translateToStdAtomNameOfDmpc,
                                                       translateToStdResName,
                                                       translateToLigandName,
                                                       REPRESENTATIVE_MODEL_ID,
                                                       REPRESENTATIVE_ALT_ID)
    from wwpdb.utils.nmr.ChemCompUtil import ChemCompUtil
    from wwpdb.utils.nmr.BMRBChemShiftStat import BMRBChemShiftStat
    from wwpdb.utils.nmr.NEFTranslator.NEFTranslator import NEFTranslator
    from wwpdb.utils.nmr.AlignUtil import (monDict3,
                                           protonBeginCode,
                                           aminoProtonCode,
                                           isReservedLigCode,
                                           letterToDigit, indexToLetter,
                                           alignPolymerSequence,
                                           assignPolymerSequence,
                                           trimSequenceAlignment,
                                           retrieveAtomIdentFromMRMap,
                                           alignPolymerSequenceWithConflicts,
                                           getRestraintFormatName,
                                           getOneLetterCodeCanSequence)
except ImportError:
    from nmr.align.alignlib import PairwiseAlign  # pylint: disable=no-name-in-module
    from nmr.mr.GromacsPTParser import GromacsPTParser
    from nmr.mr.ParserListenerUtil import (coordAssemblyChecker,
                                           translateToStdAtomName,
                                           translateToStdAtomNameOfDmpc,
                                           translateToStdResName,
                                           translateToLigandName,
                                           REPRESENTATIVE_MODEL_ID,
                                           REPRESENTATIVE_ALT_ID)
    from nmr.ChemCompUtil import ChemCompUtil
    from nmr.BMRBChemShiftStat import BMRBChemShiftStat
    from nmr.NEFTranslator.NEFTranslator import NEFTranslator
    from nmr.AlignUtil import (monDict3,
                               protonBeginCode,
                               aminoProtonCode,
                               isReservedLigCode,
                               letterToDigit, indexToLetter,
                               alignPolymerSequence,
                               assignPolymerSequence,
                               trimSequenceAlignment,
                               retrieveAtomIdentFromMRMap,
                               alignPolymerSequenceWithConflicts,
                               getRestraintFormatName,
                               getOneLetterCodeCanSequence)


# This class defines a complete listener for a parse tree produced by GromacsPTParser.
class GromacsPTParserListener(ParseTreeListener):

    __file_type = 'nm-aux-gro'

    # atom name mapping of public MR file between the archive coordinates and submitted ones
    __mrAtomNameMapping = None

    # CCD accessing utility
    __ccU = None

    # BMRB chemical shift statistics
    __csStat = None

    # NEFTranslator
    __nefT = None

    # Pairwise align
    __pA = None

    # coordinates information generated by ParserListenerUtil.coordAssemblyChecker()
    __polySeqModel = None
    __nonPolyModel = None
    __branchedModel = None
    __coordAtomSite = None
    __coordUnobsRes = None
    __chemCompAtom = None

    __hasPolySeqModel = False
    __hasNonPolyModel = False
    __hasBranchedModel = False

    # polymer sequence of GROMACS parameter/topology file
    __polySeqPrmTop = None

    __seqAlign = None
    __chainAssign = None

    # system
    __system = None

    # atoms
    __atoms = []

    # molecules
    __molecules = []

    # collection of number selection
    numberSelection = []

    # GROMACS atom number dictionary
    __atomNumberDict = None

    # __cur_column_len = None
    __cur_word_len = None

    __f = None
    warningMessage = None

    def __init__(self, verbose=True, log=sys.stdout,
                 representativeModelId=REPRESENTATIVE_MODEL_ID,
                 representativeAltId=REPRESENTATIVE_ALT_ID,
                 mrAtomNameMapping=None,
                 cR=None, caC=None, ccU=None, csStat=None, nefT=None):

        self.__mrAtomNameMapping = None if mrAtomNameMapping is None or len(mrAtomNameMapping) == 0 else mrAtomNameMapping

        if cR is not None:
            ret = coordAssemblyChecker(verbose, log, representativeModelId, representativeAltId,
                                       cR, caC, None, fullCheck=True)
            self.__polySeqModel = ret['polymer_sequence']
            self.__nonPolyModel = ret['non_polymer']
            self.__branchedModel = ret['branched']
            self.__coordAtomSite = ret['coord_atom_site']
            self.__coordUnobsRes = ret['coord_unobs_res']
            self.__chemCompAtom = ret['chem_comp_atom']

        self.__hasPolySeqModel = self.__polySeqModel is not None and len(self.__polySeqModel) > 0
        self.__hasNonPolyModel = self.__nonPolyModel is not None and len(self.__nonPolyModel) > 0
        self.__hasBranchedModel = self.__branchedModel is not None and len(self.__branchedModel) > 0

        # CCD accessing utility
        self.__ccU = ChemCompUtil(verbose, log) if ccU is None else ccU

        # BMRB chemical shift statistics
        self.__csStat = BMRBChemShiftStat(verbose, log, self.__ccU) if csStat is None else csStat

        # NEFTranslator
        self.__nefT = NEFTranslator(verbose, log, self.__ccU, self.__csStat) if nefT is None else nefT

        # Pairwise align
        if self.__hasPolySeqModel:
            self.__pA = PairwiseAlign()
            self.__pA.setVerbose(verbose)

        self.defaultStatements = 0
        self.moleculetypeStatements = 0
        self.atomtypesStatements = 0
        self.pairtypesStatements = 0
        self.bondtypesStatements = 0
        self.angletypesStatements = 0
        self.dihedraltypesStatements = 0
        self.constrainttypesStatements = 0
        self.nonbond_paramsStatements = 0
        self.atomsStatements = 0
        self.bondsStatements = 0
        self.pairsStatements = 0
        self.pairs_nbStatements = 0
        self.anglesStatements = 0
        self.dihedralsStatements = 0
        self.exclusionsStatements = 0
        self.constraintsStatements = 0
        self.settlesStatements = 0
        self.virtual_sites1Statements = 0
        self.virtual_sites2Statements = 0
        self.virtual_sites3Statements = 0
        self.virtual_sites4Statements = 0
        self.virtual_sitesnStatements = 0
        self.systemStatements = 0
        self.moleculesStatements = 0

    # Enter a parse tree produced by GromacsPTParser#gromacs_pt.
    def enterGromacs_pt(self, ctx: GromacsPTParser.Gromacs_ptContext):  # pylint: disable=unused-argument
        self.__atomNumberDict = {}
        self.__polySeqPrmTop = []
        self.__f = []

    # Exit a parse tree produced by GromacsPTParser#gromacs_pt.
    def exitGromacs_pt(self, ctx: GromacsPTParser.Gromacs_ptContext):  # pylint: disable=unused-argument

        try:

            if not self.__hasPolySeqModel:
                return

            if len(self.__atoms) == 0:
                return

            chainIndex = letterToDigit(self.__polySeqModel[0]['chain_id']) - 1  # set tentative chain_id from label_asym_id, which will be assigned to coordinate auth_asym_id
            chainId = indexToLetter(chainIndex)

            terminus = [atom['auth_atom_id'].endswith('T') for atom in self.__atoms]

            atomTotal = len(self.__atoms)
            if terminus[0]:
                terminus[0] = False
            for i in range(0, atomTotal - 1):
                j = i + 1
                if terminus[i] and terminus[j]:
                    terminus[i] = False
            if terminus[-1]:
                terminus[-1] = False

            seqIdList = []
            compIdList = []
            retrievedAtomNumList = []

            NON_METAL_ELEMENTS = ('H', 'C', 'N', 'O', 'P', 'S')

            def is_segment(prev_comp_id, prev_atom_name, comp_id, atom_name):
                if prev_comp_id is None:
                    return False
                is_prev_term_atom = prev_atom_name.endswith('T')
                if is_prev_term_atom and atom_name.endswith('T'):
                    return True
                is_prev_3_prime_comp = prev_comp_id.endswith('3')
                if is_prev_3_prime_comp and (is_prev_term_atom
                                             or self.__csStat.peptideLike(translateToStdResName(comp_id, ccU=self.__ccU))):
                    return True
                return comp_id.endswith('5')\
                    and (is_prev_3_prime_comp
                         or self.__csStat.peptideLike(translateToStdResName(prev_comp_id, ccU=self.__ccU)))

            def is_ligand(prev_comp_id, comp_id):
                if prev_comp_id is None or not self.__hasNonPolyModel:
                    return False
                if not prev_comp_id.endswith('3') or prev_comp_id == comp_id:
                    return False
                for np in self.__nonPolyModel:
                    if comp_id in np['comp_id']:
                        return True
                for np in self.__nonPolyModel:
                    if 'alt_comp_id' in np:
                        if comp_id in np['alt_comp_id']:
                            return True
                return False

            def is_metal_ion(comp_id, atom_name):
                if comp_id is None:
                    return False
                if comp_id != atom_name:
                    return False
                return comp_id.split('+')[0].title() in NAMES_ELEMENT\
                    or comp_id.split('-')[0].title() in NAMES_ELEMENT

            def is_metal_elem(prev_atom_name, prev_seq_id, seq_id):
                if len(prev_atom_name) == 0:
                    return False
                return prev_seq_id != seq_id and prev_atom_name[0] not in NON_METAL_ELEMENTS

            hasSegCompId = False
            ancAtomName = prevAtomName = ''
            prevSeqId = prevCompId = None
            offset = 0
            for atom in self.__atoms:
                atomNum = atom['atom_number']
                atomName = atom['auth_atom_id']
                atomType = atom['atom_type']
                _seqId = atom['auth_seq_id']
                compId = atom['auth_comp_id']
                if not hasSegCompId and (compId.endswith('5') or compId.endswith('3')):
                    hasSegCompId = True
                if not hasSegCompId and compId not in monDict3 and self.__mrAtomNameMapping is not None and atomName[0] in protonBeginCode:
                    _, compId, _atomName = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, _seqId, compId, atomName)
                    if _atomName != atomName:
                        atomName = _atomName
                        retrievedAtomNumList.append(atomNum)

                if (terminus[atomNum - 1] and ancAtomName.endswith('T'))\
                   or is_segment(prevCompId, prevAtomName, compId, atomName)\
                   or is_ligand(prevCompId, compId)\
                   or is_metal_ion(compId, atomName)\
                   or is_metal_ion(prevCompId, prevAtomName)\
                   or is_metal_elem(prevAtomName, prevSeqId, _seqId):

                    self.__polySeqPrmTop.append({'chain_id': chainId,
                                                 'seq_id': seqIdList,
                                                 'auth_comp_id': compIdList})
                    seqIdList = []
                    compIdList = []
                    chainIndex += 1
                    chainId = indexToLetter(chainIndex)
                    offset = 1 - _seqId

                seqId = _seqId + offset
                if seqId not in seqIdList:
                    seqIdList.append(seqId)
                    compIdList.append(compId)
                self.__atomNumberDict[atomNum] = {'chain_id': chainId,
                                                  'seq_id': seqId,
                                                  'auth_comp_id': compId,
                                                  'auth_atom_id': atomName,
                                                  'atom_type': atomType}
                ancAtomName = prevAtomName
                prevAtomName = atomName
                prevSeqId = _seqId
                prevCompId = compId

            self.__polySeqPrmTop.append({'chain_id': chainId,
                                         'seq_id': seqIdList,
                                         'auth_comp_id': compIdList})

            nonPolyCompIdList = []
            if self.__hasNonPolyModel:
                for np in self.__nonPolyModel:
                    compId = np['comp_id'][0]
                    if compId not in nonPolyCompIdList:
                        nonPolyCompIdList.append(compId)

            for ps in self.__polySeqPrmTop:
                chainId = ps['chain_id']
                compIdList = []
                for seqId, authCompId in zip(ps['seq_id'], ps['auth_comp_id']):
                    authAtomIds = [translateToStdAtomName(atomNum['auth_atom_id'], atomNum['auth_comp_id'],
                                                          ccU=self.__ccU, unambig=True)
                                   for atomNum in self.__atomNumberDict.values()
                                   if atomNum['chain_id'] == chainId
                                   and atomNum['seq_id'] == seqId
                                   and atomNum['auth_atom_id'][0] not in protonBeginCode]
                    authCompId = translateToStdResName(authCompId, ccU=self.__ccU)
                    if self.__ccU.updateChemCompDict(authCompId):
                        chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]
                        valid = True
                        for _atomId in authAtomIds:
                            if _atomId not in chemCompAtomIds:
                                valid = False
                                break
                            if not valid:
                                break
                        if valid:
                            compIdList.append(authCompId)
                            for k, atomNum in self.__atomNumberDict.items():
                                if atomNum['chain_id'] == chainId and atomNum['seq_id'] == seqId:
                                    atomNum['comp_id'] = authCompId

                                    if authCompId in nonPolyCompIdList and self.__mrAtomNameMapping is not None\
                                       and atomNum['auth_atom_id'][0] in protonBeginCode and k not in retrievedAtomNumList:
                                        _, _, atomId = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, None, authCompId, atomNum['auth_atom_id'], None, None, True)
                                    else:
                                        atomId = atomNum['auth_atom_id']

                                    atomId = translateToStdAtomName(atomId, authCompId, chemCompAtomIds, ccU=self.__ccU, unambig=True)

                                    if atomId[0] not in protonBeginCode or atomId in chemCompAtomIds:
                                        atomNum['atom_id'] = atomId
                                        if 'atom_type' in atomNum:
                                            del atomNum['atom_type']
                                    else:
                                        if atomId in chemCompAtomIds:
                                            atomNum['atom_id'] = atomId
                                            if 'atom_type' in atomNum:
                                                del atomNum['atom_type']

                        else:
                            compId = self.__csStat.getSimilarCompIdFromAtomIds([translateToStdAtomName(atomNum['auth_atom_id'],
                                                                                                       atomNum['auth_comp_id'],
                                                                                                       ccU=self.__ccU,
                                                                                                       unambig=True)
                                                                                for atomNum in self.__atomNumberDict.values()
                                                                                if atomNum['chain_id'] == chainId
                                                                                and atomNum['seq_id'] == seqId])

                            if self.__hasNonPolyModel and compId != authCompId:
                                ligands = 0
                                for np in self.__nonPolyModel:
                                    if 'alt_comp_id' in np:
                                        ligands += np['alt_comp_id'].count(authCompId)
                                if ligands > 0:
                                    for np in self.__nonPolyModel:
                                        if authCompId in np['alt_comp_id']:
                                            compId = np['comp_id'][0]
                                            break
                                if ligands == 0:
                                    __compId = None
                                    for np in self.__nonPolyModel:
                                        for ligand in np['comp_id']:
                                            __compId = translateToLigandName(authCompId, ligand, self.__ccU)
                                            if __compId == ligand:
                                                ligands += 1
                                    if ligands == 1:
                                        compId = __compId
                                    elif len(self.__nonPolyModel) == 1 and self.__ccU.updateChemCompDict(authCompId):
                                        if self.__ccU.lastChemCompDict['_chem_comp.pdbx_release_status'] == 'OBS' or isReservedLigCode(authCompId):
                                            compId = self.__nonPolyModel[0]['comp_id'][0]

                            if compId is not None:
                                compIdList.append(compId + '?')  # decide when coordinate is available
                                chemCompAtomIds = None
                                if self.__ccU.updateChemCompDict(compId):
                                    chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]
                                for k, atomNum in self.__atomNumberDict.items():
                                    if atomNum['chain_id'] == chainId and atomNum['seq_id'] == seqId:
                                        atomNum['comp_id'] = compId

                                        if compId in nonPolyCompIdList and self.__mrAtomNameMapping is not None\
                                           and atomNum['auth_atom_id'][0] in protonBeginCode and k not in retrievedAtomNumList:
                                            _, _, atomId = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, None, compId, atomNum['auth_atom_id'], None, None, True)
                                        else:
                                            atomId = atomNum['auth_atom_id']

                                        atomId = translateToStdAtomName(atomId, compId, chemCompAtomIds, ccU=self.__ccU, unambig=True)

                                        if chemCompAtomIds is not None and atomId in chemCompAtomIds:
                                            atomNum['atom_id'] = atomId
                                            if 'atom_type' in atomNum:
                                                del atomNum['atom_type']
                                        elif chemCompAtomIds is not None:
                                            if atomId in chemCompAtomIds:
                                                atomNum['atom_id'] = atomId
                                                if 'atom_type' in atomNum:
                                                    del atomNum['atom_type']
                            else:
                                compIdList.append('.')
                                unknownAtomIds = [_atomId for _atomId in authAtomIds if _atomId not in chemCompAtomIds]
                                self.__f.append(f"[Unknown atom name] "
                                                f"{unknownAtomIds} are unknown atom names for {authCompId} residue.")
                                compIdList.append(f"? {authCompId} {unknownAtomIds}")
                    else:
                        compId = self.__csStat.getSimilarCompIdFromAtomIds([atomNum['auth_atom_id']
                                                                            for atomNum in self.__atomNumberDict.values()
                                                                            if atomNum['chain_id'] == chainId
                                                                            and atomNum['seq_id'] == seqId])

                        if self.__hasNonPolyModel and compId != authCompId:
                            ligands = 0
                            for np in self.__nonPolyModel:
                                if 'alt_comp_id' in np:
                                    ligands += np['alt_comp_id'].count(authCompId)
                            if ligands == 1:
                                for np in self.__nonPolyModel:
                                    if authCompId in np['alt_comp_id']:
                                        compId = np['comp_id'][0]
                            if ligands == 0:
                                __compId = None
                                for np in self.__nonPolyModel:
                                    for ligand in np['comp_id']:
                                        __compId = translateToLigandName(authCompId, ligand, self.__ccU)
                                        if __compId == ligand:
                                            ligands += 1
                                if ligands == 1:
                                    compId = __compId
                                elif len(self.__nonPolyModel) == 1 and self.__ccU.updateChemCompDict(authCompId):
                                    if self.__ccU.lastChemCompDict['_chem_comp.pdbx_release_status'] == 'OBS' or isReservedLigCode(authCompId):
                                        compId = self.__nonPolyModel[0]['comp_id'][0]

                        if compId is not None:
                            compIdList.append(compId + '?')  # decide when coordinate is available
                            chemCompAtomIds = None
                            if self.__ccU.updateChemCompDict(compId):
                                chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]
                            for k, atomNum in self.__atomNumberDict.items():
                                if atomNum['chain_id'] == chainId and atomNum['seq_id'] == seqId:
                                    atomNum['comp_id'] = compId

                                    if compId in nonPolyCompIdList and self.__mrAtomNameMapping is not None\
                                       and atomNum['auth_atom_id'][0] in protonBeginCode and k not in retrievedAtomNumList:
                                        _, _, atomId = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, None, compId, atomNum['auth_atom_id'], None, None, True)
                                    else:
                                        atomId = atomNum['auth_atom_id']

                                    atomId = translateToStdAtomName(atomId, compId, chemCompAtomIds, ccU=self.__ccU, unambig=True)

                                    if chemCompAtomIds is not None and atomId in chemCompAtomIds:
                                        atomNum['atom_id'] = atomId
                                        if 'atom_type' in atomNum:
                                            del atomNum['atom_type']
                                    elif chemCompAtomIds is not None:
                                        if atomId in chemCompAtomIds:
                                            atomNum['atom_id'] = atomId
                                            if 'atom_type' in atomNum:
                                                del atomNum['atom_type']
                        else:
                            compIdList.append('.')
                            """ deferred to assignNonPolymer()
                            self.__f.append(f"[Unknown residue name] "
                                            f"{authCompId!r} is unknown residue name.")
                            """

                ps['comp_id'] = compIdList

            for k, atomNum in self.__atomNumberDict.items():
                if 'atom_type' not in atomNum:
                    continue
                if 'comp_id' in atomNum and atomNum['comp_id'] != atomNum['auth_comp_id']\
                   and 'atom_id' not in atomNum:
                    compId = atomNum['comp_id']
                    if self.__ccU.updateChemCompDict(compId):
                        chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]

                        if compId in nonPolyCompIdList and self.__mrAtomNameMapping is not None\
                           and atomNum['auth_atom_id'][0] in protonBeginCode and k not in retrievedAtomNumList:
                            _, _, atomId = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, None, compId, atomNum['auth_atom_id'], None, None, True)
                        else:
                            atomId = atomNum['auth_atom_id']

                        atomId = translateToStdAtomName(atomId, compId, chemCompAtomIds, ccU=self.__ccU, unambig=True)

                        if atomId is not None and atomId in chemCompAtomIds:
                            atomNum['atom_id'] = atomId
                            if 'atom_type' in atomNum:
                                del atomNum['atom_type']
                        elif atomNum['comp_id'] != atomNum['auth_comp_id']:
                            authCompId = translateToStdResName(atomNum['auth_comp_id'], ccU=self.__ccU)
                            if self.__ccU.updateChemCompDict(authCompId):
                                chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]

                                if authCompId in nonPolyCompIdList and self.__mrAtomNameMapping is not None\
                                   and atomNum['auth_atom_id'][0] in protonBeginCode and k not in retrievedAtomNumList:
                                    _, _, atomId = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, None, authCompId, atomNum['auth_atom_id'], None, None, True)
                                else:
                                    atomId = atomNum['auth_atom_id']

                                atomId = translateToStdAtomName(atomId, authCompId, chemCompAtomIds, ccU=self.__ccU, unambig=True)

                                if atomId is not None and atomId in chemCompAtomIds:
                                    atomNum['atom_id'] = atomId
                                    if 'atom_type' in atomNum:
                                        del atomNum['atom_type']
                else:
                    authCompId = translateToStdResName(atomNum['auth_comp_id'], ccU=self.__ccU)
                    if self.__ccU.updateChemCompDict(authCompId):

                        if authCompId in nonPolyCompIdList and self.__mrAtomNameMapping is not None\
                           and atomNum['auth_atom_id'][0] in protonBeginCode and k not in retrievedAtomNumList:
                            _, _, atomId = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, None, authCompId, atomNum['auth_atom_id'], None, None, True)
                        else:
                            atomId = atomNum['auth_atom_id']

                        atomId = translateToStdAtomName(atomId, authCompId, ccU=self.__ccU, unambig=True)
                        atomIds = self.__nefT.get_valid_star_atom_in_xplor(authCompId, atomId)[0]
                        if len(atomIds) == 1:
                            atomNum['atom_id'] = atomIds[0]
                            if 'atom_type' in atomNum:
                                del atomNum['atom_type']

            polySeqModel = copy.copy(self.__polySeqModel)
            if self.__hasBranchedModel:
                polySeqModel.extend(self.__branchedModel)

            self.__seqAlign, compIdMapping = alignPolymerSequence(self.__pA, polySeqModel, self.__polySeqPrmTop)

            if len(self.__seqAlign) == 0:
                for c in range(1, 5):
                    self.__seqAlign, compIdMapping = alignPolymerSequenceWithConflicts(self.__pA, polySeqModel, self.__polySeqPrmTop, c)
                    if len(self.__seqAlign) > 0:
                        break

            if len(self.__seqAlign) == 0:
                len_cif_na = sum(len(ps_cif['seq_id']) for ps_cif in polySeqModel if 'identical_chain_id' in ps_cif and len(ps_cif['seq_id']) > 3)
                len_top_na = sum(len(ps_top['seq_id']) for ps_top in self.__polySeqPrmTop
                                 if len(ps_top['seq_id']) > 3 and any(compId in ('DA?', 'DT?', 'DG?', 'DC?', 'A?', 'U?', 'G?', 'C?') for compId in ps_top['comp_id']))
                if len_cif_na == len_top_na:
                    chainIdList = []
                    seqIdList = []
                    authCompIdList = []
                    for ps_top in self.__polySeqPrmTop:
                        len_ps_cif_seq = len(ps_top['seq_id'])
                        if len_ps_cif_seq > 3 and any(compId in ('DA?', 'DT?', 'DG?', 'DC?', 'A?', 'U?', 'G?', 'C?') for compId in ps_top['comp_id']):
                            chainId = ps_top['chain_id']
                            for seqId, compId in zip(ps_top['seq_id'], ps_top['auth_comp_id']):
                                chainIdList.append(chainId)
                                seqIdList.append(seqId)
                                authCompIdList.append(compId)

                    chainIndex = letterToDigit(self.__polySeqModel[0]['chain_id']) - 1
                    idOffset = 0

                    touched = []

                    polySeqPrmTop = []
                    for ps_cif in polySeqModel:
                        len_ps_cif_seq = len(ps_cif['seq_id'])
                        if 'identical_chain_id' in ps_cif and len_ps_cif_seq > 3:
                            chainId = indexToLetter(chainIndex)
                            polySeqPrmTop.append({'chain_id': chainId,
                                                  'seq_id': seqIdList[idOffset:idOffset + len_ps_cif_seq],
                                                  'comp_id': ps_cif['comp_id'],
                                                  'auth_comp_id': authCompIdList[idOffset:idOffset + len_ps_cif_seq]})

                            for idx, (_chainId, _seqId) in enumerate(zip(chainIdList[idOffset:idOffset + len_ps_cif_seq],
                                                                         seqIdList[idOffset:idOffset + len_ps_cif_seq])):
                                for k, atomNum in self.__atomNumberDict.items():
                                    if atomNum['chain_id'] == _chainId and atomNum['seq_id'] == _seqId:
                                        atomNum['chain_id'] = chainId
                                        atomNum['cif_comp_id'] = ps_cif['comp_id'][idx]
                                        touched.append(k)

                            idOffset += len_ps_cif_seq
                            chainIndex += 1

                    for ps_top in self.__polySeqPrmTop:
                        if len(ps_top['seq_id']) > 3 and any(compId in ('DA?', 'DT?', 'DG?', 'DC?', 'A?', 'U?', 'G?', 'C?') for compId in ps_top['comp_id']):
                            continue
                        _chainId = copy.copy(ps_top['chain_id'])
                        chainId = indexToLetter(chainIndex)
                        ps_top['chain_id'] = chainId
                        polySeqPrmTop.append(ps_top)

                        for k, atomNum in self.__atomNumberDict.items():
                            if k in touched:
                                continue
                            if atomNum['chain_id'] == _chainId:
                                atomNum['chain_id'] = chainId
                                touched.append(k)

                        chainIndex += 1

                    self.__polySeqPrmTop = polySeqPrmTop

                    self.__seqAlign, compIdMapping = alignPolymerSequence(self.__pA, polySeqModel, self.__polySeqPrmTop)

                    _seqAlign = copy.copy(self.__seqAlign)
                    for sa in _seqAlign:
                        if sa['ref_chain_id'] != sa['test_chain_id']:
                            self.__seqAlign.remove(sa)

            # test chain assignment before applying comp_id mapping
            self.__chainAssign, message = assignPolymerSequence(self.__pA, self.__ccU, self.__file_type, self.__polySeqModel, self.__polySeqPrmTop, self.__seqAlign)

            for cmap in compIdMapping:
                if any(ca for ca in self.__chainAssign if ca['test_chain_id'] == cmap['chain_id']):
                    for k, atomNum in self.__atomNumberDict.items():
                        if atomNum['chain_id'] == cmap['chain_id'] and atomNum['seq_id'] == cmap['seq_id']:
                            atomNum['comp_id'] = cmap['comp_id']
                            atomNum['auth_comp_id'] = cmap['auth_comp_id']
                            if 'atom_type' in atomNum:
                                authCompId = cmap['auth_comp_id']
                                if self.__ccU.updateChemCompDict(authCompId):
                                    chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]

                                    if authCompId in nonPolyCompIdList and self.__mrAtomNameMapping is not None\
                                       and atomNum['auth_atom_id'][0] in protonBeginCode and k not in retrievedAtomNumList:
                                        _, _, atomId = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, None, authCompId, atomNum['auth_atom_id'],
                                                                                  atomNum['comp_id'], None, True)
                                    else:
                                        atomId = atomNum['auth_atom_id']

                                    atomNum['atom_id'] = translateToStdAtomName(atomId, authCompId, chemCompAtomIds, ccU=self.__ccU, unambig=True)
                                    del atomNum['atom_type']

            for k, atomNum in self.__atomNumberDict.items():
                if 'atom_type' not in atomNum:
                    continue
                if 'atom_id' not in atomNum:
                    if 'comp_id' not in atomNum or atomNum['comp_id'] == atomNum['auth_comp_id']:
                        authCompId = translateToStdResName(atomNum['auth_comp_id'], ccU=self.__ccU)

                        if self.__mrAtomNameMapping is not None\
                           and atomNum['auth_atom_id'][0] in protonBeginCode and k not in retrievedAtomNumList:
                            _, _, atomId = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, None, authCompId, atomNum['auth_atom_id'], None, None, True)
                        else:
                            atomId = atomNum['auth_atom_id']

                        if self.__ccU.updateChemCompDict(authCompId):
                            chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]
                            atomId = translateToStdAtomName(atomId, authCompId, chemCompAtomIds, ccU=self.__ccU, unambig=True)
                            if atomId in chemCompAtomIds:
                                atomNum['atom_id'] = atomId
                                continue
                            if self.__chemCompAtom is not None:
                                if 'comp_id' in atomNum and atomNum['comp_id'] in self.__chemCompAtom:
                                    if atomId in self.__chemCompAtom[atomNum['comp_id']]:
                                        atomNum['atom_id'] = atomId
                                        continue
                                if 'cif_comp_id' in atomNum and atomNum['cif_comp_id'] in self.__chemCompAtom:
                                    if atomId in self.__chemCompAtom[atomNum['cif_comp_id']]:
                                        atomNum['atom_id'] = atomId
                                        continue
                            if atomId == "HO5'" and atomNum['seq_id'] == 1 and self.__csStat.getTypeOfCompId(atomNum['auth_comp_id'])[1]:
                                continue
                            self.__f.append(f"[Unknown atom name] "
                                            f"{atomNum['auth_atom_id']!r} is not recognized as the atom name of {atomNum['auth_comp_id']!r} residue.")
                        elif self.__chemCompAtom is not None:
                            if 'comp_id' in atomNum and atomNum['comp_id'] in self.__chemCompAtom:
                                if atomId in self.__chemCompAtom[atomNum['comp_id']]:
                                    atomNum['atom_id'] = atomId
                                    continue
                            if 'cif_comp_id' in atomNum and atomNum['cif_comp_id'] in self.__chemCompAtom:
                                if atomId in self.__chemCompAtom[atomNum['cif_comp_id']]:
                                    atomNum['atom_id'] = atomId
                                    continue
                    else:
                        authCompId = translateToStdResName(atomNum['auth_comp_id'], ccU=self.__ccU)

                        if self.__mrAtomNameMapping is not None\
                           and atomNum['auth_atom_id'][0] in protonBeginCode and k not in retrievedAtomNumList:
                            _, _, atomId = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, None, authCompId, atomNum['auth_atom_id'],
                                                                      atomNum['comp_id'], None, True)
                        else:
                            atomId = atomNum['auth_atom_id']

                        if self.__ccU.updateChemCompDict(authCompId):
                            chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]
                            atomId = translateToStdAtomName(atomId, authCompId, chemCompAtomIds, ccU=self.__ccU, unambig=True)
                            if atomId in chemCompAtomIds:
                                atomNum['atom_id'] = atomId
                                continue
                            if self.__chemCompAtom is not None:
                                if 'comp_id' in atomNum and atomNum['comp_id'] in self.__chemCompAtom:
                                    if atomId in self.__chemCompAtom[atomNum['comp_id']]:
                                        atomNum['atom_id'] = atomId
                                        continue
                                if 'cif_comp_id' in atomNum and atomNum['cif_comp_id'] in self.__chemCompAtom:
                                    if atomId in self.__chemCompAtom[atomNum['cif_comp_id']]:
                                        atomNum['atom_id'] = atomId
                                        continue
                            atomNum['atom_id'] = atomNum['auth_atom_id']
                            if atomNum['atom_id'] == "HO5'" and atomNum['seq_id'] == 1 and self.__csStat.getTypeOfCompId(atomNum['comp_id'])[1]:
                                continue
                            self.__f.append(f"[Unknown atom name] "
                                            f"{atomNum['auth_atom_id']!r} is not recognized as the atom name of {atomNum['comp_id']!r} residue "
                                            f"(the original residue label is {atomNum['auth_comp_id']!r}).")
                        elif self.__chemCompAtom is not None:
                            if 'comp_id' in atomNum and atomNum['comp_id'] in self.__chemCompAtom:
                                if atomId in self.__chemCompAtom[atomNum['comp_id']]:
                                    atomNum['atom_id'] = atomId
                                    continue
                            if 'cif_comp_id' in atomNum and atomNum['cif_comp_id'] in self.__chemCompAtom:
                                if atomId in self.__chemCompAtom[atomNum['cif_comp_id']]:
                                    atomNum['atom_id'] = atomId
                                    continue

            self.__chainAssign, message = assignPolymerSequence(self.__pA, self.__ccU, self.__file_type, self.__polySeqModel, self.__polySeqPrmTop, self.__seqAlign)

            if len(message) > 0:
                self.__f.extend(message)

            if len(self.__seqAlign) == 0:
                mrFormatName = getRestraintFormatName(self.__file_type)
                _a_mr_format_name = 'the ' + mrFormatName

                ref_code = getOneLetterCodeCanSequence(self.__polySeqModel[0]['comp_id'])
                test_code = getOneLetterCodeCanSequence(self.__polySeqPrmTop[0]['comp_id'])

                hint = ''
                if abs(len(ref_code) - len(test_code)) < 20 and len(ref_code) > 40:
                    hint = f"For example, coordinates ({self.__polySeqModel[0]['auth_chain_id']}): {ref_code} vs topology: {test_code}. "

                self.__f.append(f"[Sequence mismatch] Polymer sequence between the coordinate and {_a_mr_format_name} data does not match. {hint}"
                                "Please verify the two sequences and re-upload the correct file(s) if required.")

            assi_ref_chain_ids = {}
            proc_test_chain_ids = []
            atom_nums = []
            delete_atom_nums = []

            def update_atom_num(seq_align, orphan):
                ref_chain_id = seq_align['ref_chain_id']
                test_chain_id = seq_align['test_chain_id']

                if ref_chain_id in assi_ref_chain_ids or test_chain_id in proc_test_chain_ids:
                    return

                ps_cif = next(ps for ps in self.__polySeqModel if ps['auth_chain_id'] == ref_chain_id)

                if ref_chain_id not in assi_ref_chain_ids:
                    assi_ref_chain_ids[ref_chain_id] = seq_align['length'] - seq_align['matched'] - seq_align['conflict']
                else:
                    assi_ref_chain_ids[ref_chain_id] -= seq_align['matched'] + seq_align['conflict']
                proc_test_chain_ids.append(test_chain_id)

                offset = first_seq_id = None

                for atom_num, atomNum in self.__atomNumberDict.items():
                    if atom_num in atom_nums:
                        continue
                    if atomNum['chain_id'] == test_chain_id:
                        atom_nums.append(atom_num)

                        test_seq_id = atomNum['seq_id']

                        if first_seq_id is None:
                            first_seq_id = test_seq_id

                        if test_seq_id in seq_align['test_seq_id']:
                            idx = seq_align['test_seq_id'].index(test_seq_id)
                            if 'ref_auth_seq_id' in seq_align and idx < len(seq_align['ref_auth_seq_id']):
                                ref_seq_id = seq_align['ref_auth_seq_id'][idx]
                            elif offset is not None:
                                ref_seq_id = test_seq_id + offset
                            else:
                                continue
                        elif offset is not None:
                            ref_seq_id = test_seq_id + offset
                        else:
                            continue

                        if offset is None:
                            offset = ref_seq_id - test_seq_id

                        atomNum['chain_id'] = ref_chain_id
                        atomNum['seq_id'] = ref_seq_id

                        if ref_seq_id in ps_cif['auth_seq_id']:
                            idx = ps_cif['auth_seq_id'].index(ref_seq_id)
                            atomNum['comp_id'] = ps_cif['comp_id'][idx]

                        if orphan and test_seq_id == first_seq_id\
                           and self.__csStat.peptideLike(translateToStdResName(atomNum['comp_id'], ccU=self.__ccU)):
                            if self.__ccU.updateChemCompDict(atomNum['comp_id']):
                                chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]
                                leavingAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList
                                                  if cca[self.__ccU.ccaLeavingAtomFlag] == 'Y']
                                if atomNum['atom_id'] not in chemCompAtomIds or atomNum['atom_id'] in leavingAtomIds:
                                    delete_atom_nums.append(atom_num)

            while True:

                orphanPolySeqPrmTop = []

                for ps in self.__polySeqPrmTop:
                    test_chain_id = ps['chain_id']
                    if test_chain_id in proc_test_chain_ids:
                        continue
                    try:
                        ca = next(ca for ca in self.__chainAssign if ca['test_chain_id'] == test_chain_id)

                        ref_chain_id = ca['ref_chain_id']
                        sa = next((sa for sa in self.__seqAlign
                                   if sa['ref_chain_id'] == ref_chain_id and sa['test_chain_id'] == test_chain_id), None)

                        if sa is not None:  # and sa['conflict'] == 0:
                            update_atom_num(sa, False)

                    except StopIteration:
                        orphanPolySeqPrmTop.append(ps)

                resolved = False

                if len(orphanPolySeqPrmTop) > 0:
                    max_length = max(len(ps['seq_id']) for ps in orphanPolySeqPrmTop)
                    __polySeqModel__ = [ps for ps in self.__polySeqModel
                                        if ps['auth_chain_id'] not in assi_ref_chain_ids
                                        or assi_ref_chain_ids[ps['auth_chain_id']] >= max_length]
                    __seqAlign__, _ = alignPolymerSequence(self.__pA, __polySeqModel__, orphanPolySeqPrmTop)
                    if len(__seqAlign__) > 0:
                        for sa in __seqAlign__:
                            if sa['conflict'] == 0:
                                update_atom_num(sa, True)

                                resolved = True

                    if not resolved:
                        for c in range(1, 5):
                            __seqAlign__, _ = alignPolymerSequenceWithConflicts(self.__pA, __polySeqModel__, orphanPolySeqPrmTop, c)
                            if len(__seqAlign__) > 0:
                                for sa in __seqAlign__:
                                    if sa['conflict'] <= c:
                                        update_atom_num(sa, True)

                                        resolved = True
                            if resolved:
                                break

                if not resolved:
                    break

            for ps in self.__polySeqPrmTop:
                test_chain_id = ps['chain_id']

                if test_chain_id in proc_test_chain_ids:
                    continue

                for cif_ps in self.__polySeqModel:
                    ref_chain_id = cif_ps['auth_chain_id']

                    if ref_chain_id in assi_ref_chain_ids:
                        continue

                    len_gap = abs(len(ps['seq_id']) - len(cif_ps['auth_seq_id']))

                    if len_gap > 20:
                        continue

                    if len_gap == 0:
                        offset = cif_ps['auth_seq_id'][0] - ps['seq_id'][0]

                    for atomNum in self.__atomNumberDict.values():
                        if atomNum['chain_id'] == test_chain_id:
                            atomNum['chain_id'] = ref_chain_id
                            if len_gap == 0:
                                atomNum['seq_id'] += offset

                    proc_test_chain_ids.append(test_chain_id)
                    assi_ref_chain_ids[ref_chain_id] = len_gap

            if len(delete_atom_nums) > 0:
                for atom_num in sorted(delete_atom_nums, reverse=True):
                    del self.__atomNumberDict[atom_num]

            for atomNum in self.__atomNumberDict.values():
                if 'atom_id' in atomNum and atomNum['atom_id'] in aminoProtonCode:
                    _seqKey = (atomNum['chain_id'], atomNum['seq_id'] - 1)
                    seqKey = (atomNum['chain_id'], atomNum['seq_id'])
                    if _seqKey in self.__coordUnobsRes and seqKey in self.__coordAtomSite:
                        coordAtomSite = self.__coordAtomSite[seqKey]
                        if atomNum['atom_id'] not in coordAtomSite['atom_id']:
                            for atomId in aminoProtonCode:
                                if atomId in coordAtomSite['atom_id']:
                                    atomNum['atom_id'] = atomId
                                    break

            if self.__chainAssign is not None:
                trimSequenceAlignment(self.__seqAlign, self.__chainAssign)

                if self.__hasNonPolyModel:

                    # metal ion
                    if any(ps for ps in self.__polySeqPrmTop
                           if len(ps['seq_id']) == 1 and ps['comp_id'][0].title() in NAMES_ELEMENT):
                        self.assignMetalIon()

                    # other non-polymer
                    nonPolyIndices = [idx for idx, ps in enumerate(self.__polySeqPrmTop)
                                      if not any(ca for ca in self.__chainAssign
                                                 if ca['test_chain_id'] == ps['chain_id'])
                                      and len(set(ps['comp_id'])) > 0 and ps['comp_id'][0] == '.']

                    if len(nonPolyIndices) > 0:
                        self.assignNonPolymer(nonPolyIndices)

                        for idx in sorted(nonPolyIndices, reverse=True):
                            del self.__polySeqPrmTop[idx]

            if self.__hasNonPolyModel:
                compIdMapping = {}
                mappedSeqVal = []
                mappedAtomNum = []

                for np in self.__nonPolyModel:
                    authChainId = np['auth_chain_id']
                    authSeqId = np['auth_seq_id'][0]
                    compId = np['comp_id'][0]

                    for k, v in self.__atomNumberDict.items():
                        if k in mappedAtomNum:
                            continue
                        if 'comp_id' in v and v['comp_id'] == compId:
                            seqKey = (v['comp_id'], v['chain_id'], v['seq_id'])
                            seqVal = (authChainId, authSeqId)
                            if seqKey not in compIdMapping:
                                if seqVal not in mappedSeqVal:
                                    compIdMapping[seqKey] = seqVal
                            if seqKey in compIdMapping:
                                v['chain_id'], v['seq_id'] = compIdMapping[seqKey]
                                mappedSeqVal.append(seqVal)
                                mappedAtomNum.append(k)

            if any(f for f in message if '[Concatenated sequence]' in f):  # DAOTHER-9511: resolve concatenated sequence
                test_chain_id_map = {}
                for ca in self.__chainAssign:
                    ref_chain_id = ca['ref_chain_id']
                    test_chain_id = ca['test_chain_id']
                    if test_chain_id not in test_chain_id_map:
                        test_chain_id_map[test_chain_id] = []
                    test_chain_id_map[test_chain_id].append(ref_chain_id)

                _test_chain_id_map = copy.copy(test_chain_id_map)
                for test_chain_id, cmap in _test_chain_id_map.items():
                    if len(cmap) < 2:
                        del test_chain_id_map[test_chain_id]

                if len(test_chain_id_map) > 0:
                    cmap = {}
                    for test_chain_id, ref_chain_ids in test_chain_id_map.items():
                        ref_chain_id0 = ref_chain_ids[0]
                        for ref_chain_id in ref_chain_ids[1:]:
                            sa = next((sa for sa in self.__seqAlign if sa['ref_chain_id'] == ref_chain_id and sa['test_chain_id'] == test_chain_id), None)
                            if sa is None:
                                continue
                            ps = next((ps for ps in self.__polySeqModel if ps['auth_chain_id'] == ref_chain_id), None)
                            if ps is None:
                                continue
                            for auth_seq_id, comp_id in zip(ps['auth_seq_id'], ps['comp_id']):
                                seq_key = (ref_chain_id0, auth_seq_id)
                                cmap[seq_key] = (ref_chain_id, comp_id)
                    if len(cmap) > 0:
                        for atomNum in self.__atomNumberDict.values():
                            seq_key = (atomNum['chain_id'], atomNum['seq_id'])
                            if seq_key in cmap:
                                atomNum['chain_id'], atomNum['comp_id'] = cmap[seq_key]

        finally:
            self.warningMessage = sorted(list(set(self.__f)), key=self.__f.index)

    def assignMetalIon(self):
        if not self.__hasNonPolyModel:
            return

        metals = collections.Counter(s2['comp_id'][0] for s2 in self.__polySeqPrmTop
                                     if len(s2['seq_id']) == 1 and s2['comp_id'][0].title() in NAMES_ELEMENT).most_common()

        for metal in metals:
            compId = metal[0]

            atomNums = [atomNum for atomNum in self.__atomNumberDict.values()
                        if atomNum['auth_comp_id'] == compId and atomNum['auth_atom_id'] == compId]

            nonPolys = [nonPoly for nonPoly in self.__nonPolyModel
                        if nonPoly['comp_id'][0] == compId]

            for atomNum, nonPoly in zip(atomNums, nonPolys):
                atomNum['chain_id'] = nonPoly['auth_chain_id']
                atomNum['seq_id'] = nonPoly['auth_seq_id'][0]

    def assignNonPolymer(self, nonPolyIndices):
        if not self.__hasNonPolyModel:
            return

        authCompIds = []

        for idx, ps in enumerate(self.__polySeqPrmTop):
            if idx not in nonPolyIndices:
                continue
            for authCompId, compId in zip(ps['auth_comp_id'], ps['comp_id']):
                if compId != '.':
                    continue
                authCompIds.append(authCompId)

        nonPolyCompIds = collections.Counter(authCompIds).most_common()

        compIds = []
        for nonPoly in self.__nonPolyModel:
            compId = nonPoly['comp_id'][0]
            if compId.title() in NAMES_ELEMENT:
                continue
            compIds.append(compId)

        refCompIds = collections.Counter(compIds).most_common()

        comp_id_mapping = {}

        for authCompId in nonPolyCompIds:
            refCompId = next((compId[0] for compId in refCompIds if compId[1] == authCompId[1] and compId[1] not in comp_id_mapping.values()), None)
            if refCompId is None:
                self.__f.append(f"[Unknown residue name] "
                                f"{authCompId[0]!r} is unknown residue name.")
                continue
            comp_id_mapping[authCompId[0]] = refCompId

        for authCompId, compId in comp_id_mapping.items():
            chemCompAtomIds = None
            if self.__ccU.updateChemCompDict(compId):
                chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]

            authSeqKeys = []

            for idx, ps in enumerate(self.__polySeqPrmTop):
                if idx not in nonPolyIndices:
                    continue
                _chainId = ps['chain_id']
                for _authCompId, _compId, _seqId in zip(ps['auth_comp_id'], ps['comp_id'], ps['seq_id']):
                    if _authCompId != authCompId or _compId != '.':
                        continue
                    authSeqKeys.append((_chainId, _seqId))

            nonPolys = [nonPoly for nonPoly in self.__nonPolyModel
                        if nonPoly['comp_id'][0] == compId]

            reported_auth_atom_id = []

            for authSeqKey, nonPoly in zip(authSeqKeys, nonPolys):
                atomNums = [atomNum for atomNum in self.__atomNumberDict.values()
                            if atomNum['chain_id'] == authSeqKey[0] and atomNum['seq_id'] == authSeqKey[1]]
                authAtomNames = [atomNum['auth_atom_id'] for atomNum in self.__atomNumberDict.values()
                                 if atomNum['chain_id'] == authSeqKey[0] and atomNum['seq_id'] == authSeqKey[1]]

                for atomNum in atomNums:
                    atomNum['chain_id'] = nonPoly['auth_chain_id']
                    atomNum['seq_id'] = nonPoly['auth_seq_id'][0]
                    atomNum['comp_id'] = compId
                    authAtomId = atomNum['auth_atom_id']
                    if chemCompAtomIds is not None and authAtomId in chemCompAtomIds:
                        atomNum['atom_id'] = authAtomId
                    else:
                        dmpcNameSystemId = -1
                        if compId == 'PX4':
                            if 'OE' in authAtomNames:
                                dmpcNameSystemId = 1
                            elif 'OS31' in authAtomNames:
                                dmpcNameSystemId = 2
                            elif 'O21' in authAtomNames:
                                if 'C314' in authAtomNames:
                                    dmpcNameSystemId = 3
                                elif 'C114' in authAtomNames:
                                    dmpcNameSystemId = 4

                        if dmpcNameSystemId != -1:
                            atomId = translateToStdAtomNameOfDmpc(authAtomId, dmpcNameSystemId)
                        else:
                            atomId = translateToStdAtomName(authAtomId, compId, chemCompAtomIds, ccU=self.__ccU)

                        if atomId in chemCompAtomIds:
                            atomNum['atom_id'] = atomId
                        else:
                            _, _, atomId = retrieveAtomIdentFromMRMap(self.__ccU, self.__mrAtomNameMapping, None, compId, authAtomId, None, None, True)

                            if atomId in chemCompAtomIds:
                                atomNum['atom_id'] = atomId
                                continue
                            if authAtomId not in reported_auth_atom_id:
                                atomNum['atom_id'] = atomNum['auth_atom_id']
                                if authAtomId == "HO5'" and atomNum['seq_id'] == 1 and self.__csStat.getTypeOfCompId(compId)[1]:
                                    continue
                                self.__f.append(f"[Unknown atom name] "
                                                f"{authAtomId!r} is not recognized as the atom name of {compId!r} residue "
                                                f"(the original residue label is {authCompId!r}).")
                                reported_auth_atom_id.append(authAtomId)

    # Enter a parse tree produced by GromacsPTParser#default_statement.
    def enterDefault_statement(self, ctx: GromacsPTParser.Default_statementContext):  # pylint: disable=unused-argument
        self.defaultStatements += 1

    # Exit a parse tree produced by GromacsPTParser#default_statement.
    def exitDefault_statement(self, ctx: GromacsPTParser.Default_statementContext):
        if ctx.Integer(0):
            return
        self.defaultStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#moleculetype_statement.
    def enterMoleculetype_statement(self, ctx: GromacsPTParser.Moleculetype_statementContext):  # pylint: disable=unused-argument
        self.moleculetypeStatements += 1

    # Exit a parse tree produced by GromacsPTParser#moleculetype_statement.
    def exitMoleculetype_statement(self, ctx: GromacsPTParser.Moleculetype_statementContext):
        if ctx.moleculetype(0):
            return
        self.moleculetypeStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#moleculetype.
    def enterMoleculetype(self, ctx: GromacsPTParser.MoleculetypeContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#moleculetype.
    def exitMoleculetype(self, ctx: GromacsPTParser.MoleculetypeContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#atomtypes_statement.
    def enterAtomtypes_statement(self, ctx: GromacsPTParser.Atomtypes_statementContext):  # pylint: disable=unused-argument
        self.atomtypesStatements += 1

    # Exit a parse tree produced by GromacsPTParser#atomtypes_statement.
    def exitAtomtypes_statement(self, ctx: GromacsPTParser.Atomtypes_statementContext):
        if ctx.atomtypes(0):
            return
        self.atomtypesStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#atomtypes.
    def enterAtomtypes(self, ctx: GromacsPTParser.AtomtypesContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#atomtypes.
    def exitAtomtypes(self, ctx: GromacsPTParser.AtomtypesContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#pairtypes_statement.
    def enterPairtypes_statement(self, ctx: GromacsPTParser.Pairtypes_statementContext):  # pylint: disable=unused-argument
        self.pairtypesStatements += 1

    # Exit a parse tree produced by GromacsPTParser#pairtypes_statement.
    def exitPairtypes_statement(self, ctx: GromacsPTParser.Pairtypes_statementContext):
        if ctx.pairtypes(0):
            return
        self.pairtypesStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#pairtypes.
    def enterPairtypes(self, ctx: GromacsPTParser.PairtypesContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#pairtypes.
    def exitPairtypes(self, ctx: GromacsPTParser.PairtypesContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#bondtypes_statement.
    def enterBondtypes_statement(self, ctx: GromacsPTParser.Bondtypes_statementContext):  # pylint: disable=unused-argument
        self.bondtypesStatements += 1

    # Exit a parse tree produced by GromacsPTParser#bondtypes_statement.
    def exitBondtypes_statement(self, ctx: GromacsPTParser.Bondtypes_statementContext):
        if ctx.bondtypes(0):
            return
        self.bondtypesStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#bondtypes.
    def enterBondtypes(self, ctx: GromacsPTParser.BondtypesContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#bondtypes.
    def exitBondtypes(self, ctx: GromacsPTParser.BondtypesContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#angletypes_statement.
    def enterAngletypes_statement(self, ctx: GromacsPTParser.Angletypes_statementContext):  # pylint: disable=unused-argument
        self.angletypesStatements += 1

    # Exit a parse tree produced by GromacsPTParser#angletypes_statement.
    def exitAngletypes_statement(self, ctx: GromacsPTParser.Angletypes_statementContext):
        if ctx.angletypes(0):
            return
        self.angletypesStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#angletypes.
    def enterAngletypes(self, ctx: GromacsPTParser.AngletypesContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#angletypes.
    def exitAngletypes(self, ctx: GromacsPTParser.AngletypesContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#dihedraltypes_statement.
    def enterDihedraltypes_statement(self, ctx: GromacsPTParser.Dihedraltypes_statementContext):  # pylint: disable=unused-argument
        self.dihedraltypesStatements += 1

    # Exit a parse tree produced by GromacsPTParser#dihedraltypes_statement.
    def exitDihedraltypes_statement(self, ctx: GromacsPTParser.Dihedraltypes_statementContext):
        if ctx.dihedraltypes(0):
            return
        self.dihedraltypesStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#dihedraltypes.
    def enterDihedraltypes(self, ctx: GromacsPTParser.DihedraltypesContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#dihedraltypes.
    def exitDihedraltypes(self, ctx: GromacsPTParser.DihedraltypesContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#constrainttypes_statement.
    def enterConstrainttypes_statement(self, ctx: GromacsPTParser.Constrainttypes_statementContext):  # pylint: disable=unused-argument
        self.constrainttypesStatements += 1

    # Exit a parse tree produced by GromacsPTParser#constrainttypes_statement.
    def exitConstrainttypes_statement(self, ctx: GromacsPTParser.Constrainttypes_statementContext):
        if ctx.constrainttypes(0):
            return
        self.constrainttypesStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#constrainttypes.
    def enterConstrainttypes(self, ctx: GromacsPTParser.ConstrainttypesContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#constrainttypes.
    def exitConstrainttypes(self, ctx: GromacsPTParser.ConstrainttypesContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#nonbonded_params_statement.
    def enterNonbonded_params_statement(self, ctx: GromacsPTParser.Nonbonded_params_statementContext):  # pylint: disable=unused-argument
        self.nonbond_paramsStatements += 1

    # Exit a parse tree produced by GromacsPTParser#nonbonded_params_statement.
    def exitNonbonded_params_statement(self, ctx: GromacsPTParser.Nonbonded_params_statementContext):
        if ctx.nonbonded_params(0):
            return
        self.nonbond_paramsStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#nonbonded_params.
    def enterNonbonded_params(self, ctx: GromacsPTParser.Nonbonded_paramsContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#nonbonded_params.
    def exitNonbonded_params(self, ctx: GromacsPTParser.Nonbonded_paramsContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#atoms_statement.
    def enterAtoms_statement(self, ctx: GromacsPTParser.Atoms_statementContext):  # pylint: disable=unused-argument
        self.atomsStatements += 1

    # Exit a parse tree produced by GromacsPTParser#atoms_statement.
    def exitAtoms_statement(self, ctx: GromacsPTParser.Atoms_statementContext):  # pylint: disable=unused-argument
        if ctx.atoms(0):
            return
        self.atomsStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#atoms.
    def enterAtoms(self, ctx: GromacsPTParser.AtomsContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#atoms.
    def exitAtoms(self, ctx: GromacsPTParser.AtomsContext):

        try:

            nr = int(str(ctx.Integer(0)))
            seqId = int(str(ctx.Integer(1)))
            # cgnr = int(str(ctx.Integer(2)))

            type = str(ctx.Simple_name(0))
            compId = str(ctx.Simple_name(1))
            atomId = str(ctx.Simple_name(2))

            atom = {'atom_number': nr,
                    'auth_seq_id': seqId,
                    'auth_comp_id': compId,
                    'auth_atom_id': atomId,
                    'atom_type': type}

            if atom not in self.__atoms:
                self.__atoms.append(atom)

        except ValueError:
            pass

        finally:
            self.numberSelection.clear()

    # Enter a parse tree produced by GromacsPTParser#bonds_statement.
    def enterBonds_statement(self, ctx: GromacsPTParser.Bonds_statementContext):  # pylint: disable=unused-argument
        self.bondsStatements += 1

    # Exit a parse tree produced by GromacsPTParser#bonds_statement.
    def exitBonds_statement(self, ctx: GromacsPTParser.Bonds_statementContext):
        if ctx.bonds(0):
            return
        self.bondsStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#bonds.
    def enterBonds(self, ctx: GromacsPTParser.BondsContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#bonds.
    def exitBonds(self, ctx: GromacsPTParser.BondsContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#pairs_statement.
    def enterPairs_statement(self, ctx: GromacsPTParser.Pairs_statementContext):  # pylint: disable=unused-argument
        self.pairsStatements += 1

    # Exit a parse tree produced by GromacsPTParser#pairs_statement.
    def exitPairs_statement(self, ctx: GromacsPTParser.Pairs_statementContext):
        if ctx.pairs(0):
            return
        self.pairsStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#pairs.
    def enterPairs(self, ctx: GromacsPTParser.PairsContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#pairs.
    def exitPairs(self, ctx: GromacsPTParser.PairsContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#pairs_nb_statement.
    def enterPairs_nb_statement(self, ctx: GromacsPTParser.Pairs_nb_statementContext):  # pylint: disable=unused-argument
        self.pairs_nbStatements += 1

    # Exit a parse tree produced by GromacsPTParser#pairs_nb_statement.
    def exitPairs_nb_statement(self, ctx: GromacsPTParser.Pairs_nb_statementContext):
        if ctx.pairs_nb(0):
            return
        self.pairs_nbStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#pairs_nb.
    def enterPairs_nb(self, ctx: GromacsPTParser.Pairs_nbContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#pairs_nb.
    def exitPairs_nb(self, ctx: GromacsPTParser.Pairs_nbContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#angles_statement.
    def enterAngles_statement(self, ctx: GromacsPTParser.Angles_statementContext):  # pylint: disable=unused-argument
        self.anglesStatements += 1

    # Exit a parse tree produced by GromacsPTParser#angles_statement.
    def exitAngles_statement(self, ctx: GromacsPTParser.Angles_statementContext):
        if ctx.angles(0):
            return
        self.anglesStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#angles.
    def enterAngles(self, ctx: GromacsPTParser.AnglesContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#angles.
    def exitAngles(self, ctx: GromacsPTParser.AnglesContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#dihedrals_statement.
    def enterDihedrals_statement(self, ctx: GromacsPTParser.Dihedrals_statementContext):  # pylint: disable=unused-argument
        self.dihedralsStatements += 1

    # Exit a parse tree produced by GromacsPTParser#dihedrals_statement.
    def exitDihedrals_statement(self, ctx: GromacsPTParser.Dihedrals_statementContext):
        if ctx.dihedrals(0):
            return
        self.dihedralsStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#dihedrals.
    def enterDihedrals(self, ctx: GromacsPTParser.DihedralsContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#dihedrals.
    def exitDihedrals(self, ctx: GromacsPTParser.DihedralsContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#exclusions_statement.
    def enterExclusions_statement(self, ctx: GromacsPTParser.Exclusions_statementContext):  # pylint: disable=unused-argument
        self.exclusionsStatements += 1

    # Exit a parse tree produced by GromacsPTParser#exclusions_statement.
    def exitExclusions_statement(self, ctx: GromacsPTParser.Exclusions_statementContext):
        if ctx.exclusions(0):
            return
        self.exclusionsStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#exclusions.
    def enterExclusions(self, ctx: GromacsPTParser.ExclusionsContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#exclusions.
    def exitExclusions(self, ctx: GromacsPTParser.ExclusionsContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#constraints_statement.
    def enterConstraints_statement(self, ctx: GromacsPTParser.Constraints_statementContext):  # pylint: disable=unused-argument
        self.constraintsStatements += 1

    # Exit a parse tree produced by GromacsPTParser#constraints_statement.
    def exitConstraints_statement(self, ctx: GromacsPTParser.Constraints_statementContext):
        if ctx.constraints(0):
            return
        self.constraintsStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#constraints.
    def enterConstraints(self, ctx: GromacsPTParser.ConstraintsContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#constraints.
    def exitConstraints(self, ctx: GromacsPTParser.ConstraintsContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#settles_statement.
    def enterSettles_statement(self, ctx: GromacsPTParser.Settles_statementContext):  # pylint: disable=unused-argument
        self.settlesStatements += 1

    # Exit a parse tree produced by GromacsPTParser#settles_statement.
    def exitSettles_statement(self, ctx: GromacsPTParser.Settles_statementContext):
        if ctx.settles(0):
            return
        self.settlesStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#settles.
    def enterSettles(self, ctx: GromacsPTParser.SettlesContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#settles.
    def exitSettles(self, ctx: GromacsPTParser.SettlesContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#virtual_sites1_statement.
    def enterVirtual_sites1_statement(self, ctx: GromacsPTParser.Virtual_sites1_statementContext):  # pylint: disable=unused-argument
        self.virtual_sites1Statements += 1

    # Exit a parse tree produced by GromacsPTParser#virtual_sites1_statement.
    def exitVirtual_sites1_statement(self, ctx: GromacsPTParser.Virtual_sites1_statementContext):
        if ctx.virtual_sites1(0):
            return
        self.virtual_sites1Statements -= 1

    # Enter a parse tree produced by GromacsPTParser#virtual_sites1.
    def enterVirtual_sites1(self, ctx: GromacsPTParser.Virtual_sites1Context):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#virtual_sites1.
    def exitVirtual_sites1(self, ctx: GromacsPTParser.Virtual_sites1Context):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#virtual_sites2_statement.
    def enterVirtual_sites2_statement(self, ctx: GromacsPTParser.Virtual_sites2_statementContext):  # pylint: disable=unused-argument
        self.virtual_sites2Statements += 1

    # Exit a parse tree produced by GromacsPTParser#virtual_sites2_statement.
    def exitVirtual_sites2_statement(self, ctx: GromacsPTParser.Virtual_sites2_statementContext):
        if ctx.virtual_sites2(0):
            return
        self.virtual_sites2Statements -= 1

    # Enter a parse tree produced by GromacsPTParser#virtual_sites2.
    def enterVirtual_sites2(self, ctx: GromacsPTParser.Virtual_sites2Context):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#virtual_sites2.
    def exitVirtual_sites2(self, ctx: GromacsPTParser.Virtual_sites2Context):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#virtual_sites3_statement.
    def enterVirtual_sites3_statement(self, ctx: GromacsPTParser.Virtual_sites3_statementContext):  # pylint: disable=unused-argument
        self.virtual_sites3Statements += 1

    # Exit a parse tree produced by GromacsPTParser#virtual_sites3_statement.
    def exitVirtual_sites3_statement(self, ctx: GromacsPTParser.Virtual_sites3_statementContext):
        if ctx.virtual_sites3(0):
            return
        self.virtual_sites3Statements -= 1

    # Enter a parse tree produced by GromacsPTParser#virtual_sites3.
    def enterVirtual_sites3(self, ctx: GromacsPTParser.Virtual_sites3Context):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#virtual_sites3.
    def exitVirtual_sites3(self, ctx: GromacsPTParser.Virtual_sites3Context):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#virtual_sites4_statement.
    def enterVirtual_sites4_statement(self, ctx: GromacsPTParser.Virtual_sites4_statementContext):  # pylint: disable=unused-argument
        self.virtual_sites4Statements += 1

    # Exit a parse tree produced by GromacsPTParser#virtual_sites4_statement.
    def exitVirtual_sites4_statement(self, ctx: GromacsPTParser.Virtual_sites4_statementContext):
        if ctx.virtual_sites4(0):
            return
        self.virtual_sites4Statements -= 1

    # Enter a parse tree produced by GromacsPTParser#virtual_sites4.
    def enterVirtual_sites4(self, ctx: GromacsPTParser.Virtual_sites4Context):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#virtual_sites4.
    def exitVirtual_sites4(self, ctx: GromacsPTParser.Virtual_sites4Context):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#virtual_sitesn_statement.
    def enterVirtual_sitesn_statement(self, ctx: GromacsPTParser.Virtual_sitesn_statementContext):  # pylint: disable=unused-argument
        self.virtual_sitesnStatements += 1

    # Exit a parse tree produced by GromacsPTParser#virtual_sitesn_statement.
    def exitVirtual_sitesn_statement(self, ctx: GromacsPTParser.Virtual_sitesn_statementContext):
        if ctx.virtual_sitesn(0):
            return
        self.virtual_sitesnStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#virtual_sitesn.
    def enterVirtual_sitesn(self, ctx: GromacsPTParser.Virtual_sitesnContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#virtual_sitesn.
    def exitVirtual_sitesn(self, ctx: GromacsPTParser.Virtual_sitesnContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#system_statement.
    def enterSystem_statement(self, ctx: GromacsPTParser.System_statementContext):  # pylint: disable=unused-argument
        self.systemStatements += 1

    # Exit a parse tree produced by GromacsPTParser#system_statement.
    def exitSystem_statement(self, ctx: GromacsPTParser.System_statementContext):
        if ctx.Simple_name_AA(0):
            title = []
            i = 0
            while ctx.Simple_name_AA(i):
                title.append(str(ctx.Simple_name_AA(i)))
                i += 1

            self.__system = ' '.join(title)
            return
        self.systemStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#molecules_statement.
    def enterMolecules_statement(self, ctx: GromacsPTParser.Molecules_statementContext):  # pylint: disable=unused-argument
        self.moleculesStatements += 1

    # Exit a parse tree produced by GromacsPTParser#molecules_statement.
    def exitMolecules_statement(self, ctx: GromacsPTParser.Molecules_statementContext):
        if ctx.molecules(0):
            return
        self.moleculesStatements -= 1

    # Enter a parse tree produced by GromacsPTParser#molecules.
    def enterMolecules(self, ctx: GromacsPTParser.MoleculesContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#molecules.
    def exitMolecules(self, ctx: GromacsPTParser.MoleculesContext):
        name = str(ctx.Simple_name())
        number = int(str(ctx.Integer()))
        if number > 0:
            self.__molecules.append({'molecule_name': name, 'number_of_copies': number})

    # Enter a parse tree produced by GromacsPTParser#number.
    def enterNumber(self, ctx: GromacsPTParser.NumberContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#number.
    def exitNumber(self, ctx: GromacsPTParser.NumberContext):
        """ not used the 'number' in the '[ atoms ]' statement so that pass through for performance
        if ctx.Real():
            self.numberSelection.append(float(str(ctx.Real())))

        elif ctx.Integer():
            self.numberSelection.append(float(str(ctx.Integer())))

        else:
            self.numberSelection.append(None)
        """

    # Enter a parse tree produced by GromacsPTParser#position_restraints.
    def enterPosition_restraints(self, ctx: GromacsPTParser.Position_restraintsContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#position_restraints.
    def exitPosition_restraints(self, ctx: GromacsPTParser.Position_restraintsContext):  # pylint: disable=unused-argument
        pass

    # Enter a parse tree produced by GromacsPTParser#position_restraint.
    def enterPosition_restraint(self, ctx: GromacsPTParser.Position_restraintContext):  # pylint: disable=unused-argument
        pass

    # Exit a parse tree produced by GromacsPTParser#position_restraint.
    def exitPosition_restraint(self, ctx: GromacsPTParser.Position_restraintContext):  # pylint: disable=unused-argument
        pass

    def getContentSubtype(self):
        """ Return content subtype of GROMACS parameter/topology file.
        """

        contentSubtype = {'default': self.defaultStatements,
                          'moleculetype': self.moleculetypeStatements,
                          'atomtypes': self.atomtypesStatements,
                          'pairtypes': self.pairtypesStatements,
                          'bondtypes': self.bondtypesStatements,
                          'angletypes': self.angletypesStatements,
                          'dihedraltypes': self.dihedraltypesStatements,
                          'constrainttypes': self.constrainttypesStatements,
                          'nonbond_params': self.nonbond_paramsStatements,
                          'atoms': self.atomsStatements,
                          'bonds': self.bondsStatements,
                          'pairs': self.pairsStatements,
                          'pairs_nb': self.pairs_nbStatements,
                          'angles': self.anglesStatements,
                          'dihedrals': self.dihedralsStatements,
                          'exclusions': self.exclusionsStatements,
                          'constraints': self.constraintsStatements,
                          'settles': self.settlesStatements,
                          'virtual_sites1': self.virtual_sites1Statements,
                          'virtual_sites2': self.virtual_sites2Statements,
                          'virtual_sites3': self.virtual_sites3Statements,
                          'virtual_sites4': self.virtual_sites4Statements,
                          'virtual_sitesn': self.virtual_sitesnStatements,
                          'system': self.systemStatements,
                          'molecules': self.moleculesStatements
                          }

        return {k: 1 for k, v in contentSubtype.items() if v > 0}

    def getSystem(self):
        """ Return system name of GROMACS parameter/topology file.
        """
        return self.__system

    def getMolecules(self):
        """ Return list of molecules and its number of copies in GROMACS parameter/topology file.
        """
        return self.__molecules

    def getAtomNumberDict(self):
        """ Return GROMACS atomic number dictionary.
        """
        return self.__atomNumberDict

    def getPolymerSequence(self):
        """ Return polymer sequence of GROMACS parameter/topology file.
        """
        return None if self.__polySeqPrmTop is None or len(self.__polySeqPrmTop) == 0 else self.__polySeqPrmTop

    def getSequenceAlignment(self):
        """ Return sequence alignment between coordinates and GROMACS parameter/topology.
        """
        return None if self.__seqAlign is None or len(self.__seqAlign) == 0 else self.__seqAlign

    def getChainAssignment(self):
        """ Return chain assignment between coordinates and GROMACS parameter/topology.
        """
        return None if self.__chainAssign is None or len(self.__chainAssign) == 0 else self.__chainAssign

# del GromacsPTParser
