void MoleculeJsonLoader::parseAtoms()

in core/indigo-core/molecule/src/molecule_json_loader.cpp [265:679]


void MoleculeJsonLoader::parseAtoms(const rapidjson::Value& atoms, BaseMolecule& mol, std::vector<EnhancedStereoCenter>& stereo_centers)
{
    mol.reaction_atom_mapping.clear_resize(atoms.Size());
    mol.reaction_atom_mapping.zerofill();
    mol.reaction_atom_inversion.clear_resize(atoms.Size());
    mol.reaction_atom_inversion.zerofill();
    mol.reaction_atom_exact_change.clear_resize(atoms.Size());
    mol.reaction_atom_exact_change.zerofill();

    std::vector<int> hcounts;
    hcounts.resize(atoms.Size(), 0);
    for (SizeType i = 0; i < atoms.Size(); i++)
    {
        std::string label;
        int atom_idx = 0, charge = 0, valence = 0, radical = 0, isotope = 0, elem = 0, rsite_idx = 0;
        bool is_not_list = false;
        std::unique_ptr<QueryMolecule::Atom> atomlist;
        const Value& a = atoms[i];
        if (a.HasMember("isotope"))
            isotope = a["isotope"].GetInt();
        if (a.HasMember("attachmentPoints"))
        {
            int val = a["attachmentPoints"].GetInt();
            for (int att_idx = 0; (1 << att_idx) <= val; att_idx++)
                if (val & (1 << att_idx))
                    mol.addAttachmentPoint(att_idx + 1, i);
        }

        if (a.HasMember("type"))
        {
            std::string atom_type = a["type"].GetString();
            if (atom_type == "rg-label" && a.HasMember("$refs") && a["$refs"].Size())
            {
                std::string ref = a["$refs"][0].GetString();
                if (ref.find("rg-") == 0 && ref.erase(0, 3).size())
                {
                    rsite_idx = std::stoi(ref);
                    elem = ELEM_RSITE;
                    label = "R";
                    if (a.HasMember("attachmentOrder"))
                    {
                        const auto& rattachments = a["attachmentOrder"];
                        for (SizeType j = 0; j < rattachments.Size(); ++j)
                        {
                            const auto& rattachment = rattachments[j];
                            if (rattachment.HasMember("attachmentAtom"))
                            {
                                auto attindex = rattachment["attachmentAtom"].GetInt();
                                int attid = -1;
                                if (rattachment.HasMember("attachmentId"))
                                {
                                    attid = rattachment["attachmentId"].GetInt();
                                }
                                mol.setRSiteAttachmentOrder(i, attindex, attid);
                            }
                        }
                    }
                }
                else
                    throw Error("invalid refs: %s", ref.c_str());
            }
            else if (atom_type == "atom-list")
            {
                if (!_pqmol)
                    throw Error("atom-list is allowed only for queries");
                is_not_list = a.HasMember("notList") ? a["notList"].GetBool() : false;
                const Value& elements = a["elements"];
                int pseudo_count = 0;
                elem = ELEM_ATOMLIST;
                for (rapidjson::SizeType j = 0; j < elements.Size(); ++j)
                {
                    auto elem_label = elements[j].GetString();
                    int list_elem = Element::fromString2(elem_label);
                    std::unique_ptr<QueryMolecule::Atom> cur_atom;
                    if (list_elem != -1)
                    {
                        cur_atom = std::make_unique<QueryMolecule::Atom>(QueryMolecule::ATOM_NUMBER, list_elem);
                    }
                    else
                    {
                        pseudo_count++;
                        if (pseudo_count > 1)
                            throw Error("%s inside atom list, if present, must be single", elem_label);
                        cur_atom = std::make_unique<QueryMolecule::Atom>(QueryMolecule::ATOM_PSEUDO, elem_label);
                    }

                    if (atomlist.get() == 0)
                        atomlist.reset(cur_atom.release());
                    else
                        atomlist.reset(QueryMolecule::Atom::oder(atomlist.release(), cur_atom.release()));
                }

                if (is_not_list)
                    atomlist.reset(QueryMolecule::Atom::nicht(atomlist.release()));
            }
            else
                throw Error("invalid atom type: %s", atom_type.c_str());
        }
        else
        {
            label = a["label"].GetString();
            if (label == "D")
            {
                elem = ELEM_H;
                isotope = DEUTERIUM;
            }
            else if (label == "T")
            {
                elem = ELEM_H;
                isotope = TRITIUM;
            }
            else
            {
                elem = Element::fromString2(label.c_str());
                if (elem == -1)
                {
                    if (!_pqmol && QueryMolecule::getAtomType(label.c_str()) != _ATOM_PSEUDO)
                        throw Error("'%s' label is allowed only for queries", label.c_str());
                    elem = ELEM_PSEUDO;
                    if (isotope != 0)
                    {
                        throw Error("isotope number not allowed on pseudo-atoms");
                    }
                }
            }
        }

        if (a.HasMember("charge"))
            charge = a["charge"].GetInt();
        if (a.HasMember("explicitValence"))
            valence = a["explicitValence"].GetInt();
        if (a.HasMember("radical"))
            radical = a["radical"].GetInt();

        if (_pmol)
        {
            atom_idx = _pmol->addAtom(elem);
            _pmol->setAtomCharge_Silent(atom_idx, charge);
            _pmol->setAtomRadical(atom_idx, radical);
            _pmol->setAtomIsotope(atom_idx, isotope);
            if (valence > 0 && valence <= 14)
                _pmol->setExplicitValence(atom_idx, valence);
            if (valence == 15)
                _pmol->setExplicitValence(atom_idx, 0);
            if (elem == ELEM_PSEUDO)
            {
                _pmol->setPseudoAtom(atom_idx, label.c_str());
            }
        }
        else
        {
            atom_idx = addAtomToMoleculeQuery(label.c_str(), elem, charge, valence, radical, isotope);

            if (atomlist.get())
                _pqmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx), atomlist.release()));
        }

        if (a.HasMember("selected") && a["selected"].GetBool())
        {
            if (_pmol)
                _pmol->selectAtom(atom_idx);
            else
                _pqmol->selectAtom(atom_idx);
        }

        if (a.HasMember("ringBondCount"))
        {
            if (_pqmol)
            {
                int rbcount = a["ringBondCount"].GetInt();
                if (rbcount == -1) // no ring bonds
                    _pqmol->resetAtom(atom_idx,
                                      QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, 0)));
                else if (rbcount == -2) // as drawn
                {
                    int k, rbonds = 0;
                    const Vertex& vertex = _pqmol->getVertex(atom_idx);

                    for (k = vertex.neiBegin(); k != vertex.neiEnd(); k = vertex.neiNext(k))
                        if (_pqmol->getEdgeTopology(vertex.neiEdge(k)) == TOPOLOGY_RING)
                            rbonds++;

                    _pqmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx),
                                                                         new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS_AS_DRAWN, rbonds)));
                }
                else if (rbcount > 1)
                    _pqmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx),
                                                                         new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, rbcount, rbcount)));
                else
                    throw Error("ring bond count = %d makes no sense", rbcount);
            }
            else if (!ignore_noncritical_query_features)
                throw Error("ring bond count is allowed only for queries");
        }

        if (a.HasMember("substitutionCount"))
        {
            if (!_pqmol)
                throw Error("substitution counts are allowed only for queries");
            int sub_count = a["substitutionCount"].GetInt();

            if (sub_count == -1) // no substitution
                _pqmol->resetAtom(atom_idx,
                                  QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS, 0)));
            else if (sub_count == -2)
            {
                _pqmol->resetAtom(atom_idx,
                                  QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS_AS_DRAWN,
                                                                                                                  _pqmol->getVertex(atom_idx).degree())));
            }
            else if (sub_count > 0)
                _pqmol->resetAtom(atom_idx,
                                  QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS, sub_count,
                                                                                                                  (sub_count < 6 ? sub_count : 100))));
            else
                throw Error("invalid SUB value: %d", sub_count);
        }

        if (a.HasMember("hCount"))
        {
            if (!_pqmol)
            {
                if (!ignore_noncritical_query_features)
                    throw Error("H count is allowed only for queries");
            }
            hcounts[atom_idx] = a["hCount"].GetInt();
        }

        if (a.HasMember("implicitHCount"))
        {
            if (_pmol)
                _pmol->setImplicitH(atom_idx, a["implicitHCount"].GetInt());
            else
            {
                int count = a["implicitHCount"].GetInt();
                if (count < 0)
                    throw Error("Wrong value for implicitHCount: %d", count);
                _pqmol->resetAtom(atom_idx,
                                  QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_IMPLICIT_H, count)));
            }
        }

        if (a.HasMember("invRet"))
        {
            mol.reaction_atom_inversion[atom_idx] = a["invRet"].GetInt();
        }

        if (a.HasMember("unsaturatedAtom"))
        {
            if (_pqmol)
            {
                if (a["unsaturatedAtom"].GetBool())
                    _pqmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_pqmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_UNSATURATION, 0)));
            }
            else if (!ignore_noncritical_query_features)
                throw Error("unsaturation flag is allowed only for queries");
        }

        if (a.HasMember("exactChangeFlag"))
        {
            mol.reaction_atom_exact_change[atom_idx] = a["exactChangeFlag"].GetBool();
        }

        if (a.HasMember("mapping"))
        {
            mol.reaction_atom_mapping[atom_idx] = a["mapping"].GetInt();
        }

        if (rsite_idx)
            mol.allowRGroupOnRSite(atom_idx, rsite_idx);

        if (a.HasMember("stereoLabel"))
        {
            std::string sl = a["stereoLabel"].GetString();
            if (sl.find("abs") != std::string::npos)
            {
                stereo_centers.emplace_back(atom_idx, MoleculeStereocenters::ATOM_ABS, 1);
            }
            else if (sl.find("or") != std::string::npos)
            {
                int grp = std::stoi(sl.substr(2));
                if (grp)
                    stereo_centers.emplace_back(atom_idx, MoleculeStereocenters::ATOM_OR, grp);
            }
            else if (sl.find("&") != std::string::npos)
            {
                int grp = std::stoi(sl.substr(1));
                if (grp)
                    stereo_centers.emplace_back(atom_idx, MoleculeStereocenters::ATOM_AND, grp);
            }
        }

        if (a.HasMember("location"))
        {
            const Value& coords = a["location"];
            if (coords.Size() > 0)
            {
                Vec3f a_pos;
                a_pos.x = coords[0].GetFloat();
                a_pos.y = coords[1].GetFloat();
                a_pos.z = coords[2].GetFloat();
                mol.setAtomXyz(atom_idx, a_pos);
            }
        }

        if (a.HasMember("alias"))
        {
            mol.setAlias(atom_idx, a["alias"].GetString());
        }

        if (a.HasMember("cip"))
        {
            std::string cip_str = a["cip"].GetString();
            auto cip = stringToCIP(cip_str);
            if (cip != CIPDesc::NONE)
                mol.setAtomCIP(atom_idx, cip);
        }

        if (a.HasMember("queryProperties"))
        {
            if (_pqmol)
            {
                auto qProps = a["queryProperties"].GetObject();
                if (qProps.HasMember("customQuery"))
                {
                    std::string customQuery = qProps["customQuery"].GetString();
                    std::unique_ptr<QueryMolecule::Atom> atom = make_unique<QueryMolecule::Atom>();
                    SmilesLoader::readSmartsAtomStr(customQuery, atom);
                    _pqmol->resetAtom(atom_idx, atom.release());
                }
                else
                {
                    if (qProps.HasMember("aromaticity"))
                    {
                        std::string arom = qProps["aromaticity"].GetString();
                        int aromatic;
                        if (arom == ATOM_AROMATIC_STR)
                            aromatic = ATOM_AROMATIC;
                        else if (arom == ATOM_ALIPHATIC_STR)
                            aromatic = ATOM_ALIPHATIC;
                        else
                            throw Error("Wrong value for aromaticity.");
                        _pqmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx),
                                                                             new QueryMolecule::Atom(QueryMolecule::ATOM_AROMATICITY, aromatic)));
                    }
                    if (qProps.HasMember("ringMembership"))
                    {
                        int rmem = qProps["ringMembership"].GetInt();
                        _pqmol->resetAtom(
                            atom_idx, QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_SSSR_RINGS, rmem)));
                    }
                    if (qProps.HasMember("ringSize"))
                    {
                        int rsize = qProps["ringSize"].GetInt();
                        _pqmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx),
                                                                             new QueryMolecule::Atom(QueryMolecule::ATOM_SMALLEST_RING_SIZE, rsize)));
                    }
                    if (qProps.HasMember("connectivity"))
                    {
                        int conn = qProps["connectivity"].GetInt();
                        _pqmol->resetAtom(
                            atom_idx, QueryMolecule::Atom::und(_pqmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_CONNECTIVITY, conn)));
                    }
                    if (qProps.HasMember("chirality"))
                    {
                        std::string chirality_value = qProps["chirality"].GetString();
                        int chirality;
                        if (chirality_value == "clockwise")
                            chirality = QueryMolecule::CHIRALITY_CLOCKWISE;
                        else if (chirality_value == "anticlockwise")
                            chirality = QueryMolecule::CHIRALITY_ANTICLOCKWISE;
                        else
                            throw Error("Wrong value for chirality.");
                        _pqmol->resetAtom(atom_idx, QueryMolecule::Atom::und(
                                                        _pqmol->releaseAtom(atom_idx),
                                                        new QueryMolecule::Atom(QueryMolecule::ATOM_CHIRALITY, QueryMolecule::CHIRALITY_GENERAL, chirality)));
                    }
                }
            }
            else
                throw Error("queryProperties is allowed only for queries");
        }
    }

    if (_pqmol)
        for (int k = 0; k < static_cast<int>(hcounts.size()); k++)
        {
            int expl_h = 0;

            if (hcounts[k] >= 0)
            {
                // count explicit hydrogens
                const Vertex& vertex = mol.getVertex(k);
                int i;

                for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i))
                {
                    if (mol.getAtomNumber(vertex.neiVertex(i)) == ELEM_H)
                        expl_h++;
                }
            }

            if (hcounts[k] == 1)
            {
                // no hydrogens unless explicitly drawn
                _pqmol->resetAtom(k, QueryMolecule::Atom::und(_pqmol->releaseAtom(k), new QueryMolecule::Atom(QueryMolecule::ATOM_TOTAL_H, expl_h)));
            }
            else if (hcounts[k] > 1)
            {
                // no hydrogens unless explicitly drawn
                _pqmol->resetAtom(
                    k, QueryMolecule::Atom::und(_pqmol->releaseAtom(k), new QueryMolecule::Atom(QueryMolecule::ATOM_TOTAL_H, expl_h + hcounts[k] - 1, 100)));
            }
        }
}