void MoleculeJsonSaver::saveAtoms()

in core/indigo-core/molecule/src/molecule_json_saver.cpp [683:1070]


void MoleculeJsonSaver::saveAtoms(BaseMolecule& mol, JsonWriter& writer)
{
    QS_DEF(Array<char>, buf);
    ArrayOutput out(buf);
    for (auto i : mol.vertices())
    {
        buf.clear();
        int anum = mol.getAtomNumber(i);
        int isotope = mol.getAtomIsotope(i);
        writer.StartObject();
        if (mol.attachmentPointCount())
            saveAttachmentPoint(mol, i, writer);
        QS_DEF(Array<int>, rg_list);
        int radical = 0;
        int query_atom_type = QueryMolecule::QUERY_ATOM_UNKNOWN;
        bool needCustomQuery = false;
        std::map<int, std::unique_ptr<QueryMolecule::Atom>> query_atom_properties;
        bool is_rSite = mol.isRSite(i);
        if (is_rSite)
        {
            mol.getAllowedRGroups(i, rg_list);
            writer.Key("type");
            writer.String("rg-label");
            writer.Key("$refs");
            writer.StartArray();
            for (int j = 0; j < rg_list.size(); ++j)
            {
                buf.clear();
                out.printf("rg-%d", rg_list[j]);
                buf.push(0);
                writer.String(buf.ptr());
            }
            writer.EndArray();
        }
        else
        {
            bool is_qatom_list = false;
            std::vector<std::unique_ptr<QueryMolecule::Atom>> atoms;
            if (_pqmol)
            {
                query_atom_type = QueryMolecule::parseQueryAtomSmarts(*_pqmol, i, atoms, query_atom_properties);
                needCustomQuery = query_atom_type == QueryMolecule::QUERY_ATOM_UNKNOWN;
                if (query_atom_properties.count(QueryMolecule::ATOM_CHIRALITY) &&
                    (query_atom_properties[QueryMolecule::ATOM_CHIRALITY]->value_min != QueryMolecule::CHIRALITY_GENERAL ||
                     query_atom_properties[QueryMolecule::ATOM_CHIRALITY]->value_max & QueryMolecule::CHIRALITY_OR_UNSPECIFIED))
                    needCustomQuery = true;
            }
            if (mol.isPseudoAtom(i))
            {
                buf.readString(mol.getPseudoAtom(i), true);
            }
            else if (mol.isTemplateAtom(i))
            {
                buf.readString(mol.getTemplateAtom(i), true);
            }
            else if (anum != VALUE_UNKNOWN)
            {
                buf.readString(Element::toString(anum, isotope), true);
                radical = mol.getAtomRadical(i);
            }
            else if (_pqmol)
            {
                if (query_atom_type != QueryMolecule::QUERY_ATOM_UNKNOWN)
                {
                    if (query_atom_type == QueryMolecule::QUERY_ATOM_LIST || query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST)
                    {
                        is_qatom_list = true;
                        writer.Key("type");
                        writer.String("atom-list");
                        if (query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST)
                        {
                            writer.Key("notList");
                            writer.Bool(true);
                        }
                        writer.Key("elements");
                        writer.StartArray();
                        for (auto& atom : atoms)
                            if (atom->type == QueryMolecule::ATOM_NUMBER)
                                writer.String(Element::toString(atom->value_max));
                            else if (atom->type == QueryMolecule::ATOM_PSEUDO)
                                writer.String(atom->alias.ptr());
                            else
                                throw Error("Wrong atom type %d", atom->type);
                        writer.EndArray();
                    }
                    else if (query_atom_type == QueryMolecule::QUERY_ATOM_SINGLE)
                    {
                        anum = (*atoms.begin()).get()->value_max;
                        buf.readString(Element::toString(anum), true);
                        if (anum == ELEM_H && query_atom_properties.count(QueryMolecule::ATOM_ISOTOPE) > 0)
                        {
                            int h_isotope = query_atom_properties[QueryMolecule::ATOM_ISOTOPE]->value_min;
                            if (h_isotope == DEUTERIUM)
                            {
                                buf.clear();
                                buf.appendString("D", true);
                            }
                            else if (h_isotope == TRITIUM)
                            {
                                buf.clear();
                                buf.appendString("T", true);
                            }
                        }
                    }
                    else
                        QueryMolecule::getQueryAtomLabel(query_atom_type, buf);
                }
                else // query_atom_type == QueryMolecule::QUERY_ATOM_UNKNOWN
                {
                    needCustomQuery = true;
                }
            }

            if (needCustomQuery)
            {
                writer.Key("label");
                writer.String("A"); // Set label any atom
            }
            else if (!is_qatom_list)
            {
                writer.Key("label");
                writer.String(buf.ptr());
            }

            if (mol.isAlias(i))
            {
                writer.Key("alias");
                writer.String(mol.getAlias(i));
            }
        }

        if (mol.isAtomSelected(i))
        {
            writer.Key("selected");
            writer.Bool(true);
        }

        Vec3f coord = mol.getAtomXyz(i);
        writer.Key("location");
        writer.StartArray();
        writeFloat(writer, coord.x);
        writeFloat(writer, coord.y);
        writeFloat(writer, coord.z);
        writer.EndArray();

        int charge = mol.getAtomCharge(i);
        int evalence = mol.getExplicitValence(i);
        int mapping = mol.reaction_atom_mapping[i];
        int inv_ret = mol.reaction_atom_inversion[i];
        bool ecflag = mol.reaction_atom_exact_change[i];
        int hcount = MoleculeSavers::getHCount(mol, i, anum, charge);

        if (_pqmol && !is_rSite) // No custom query for RSite
        {
            std::map<int, const char*> qprops{{QueryMolecule::ATOM_SSSR_RINGS, "ringMembership"},
                                              {QueryMolecule::ATOM_SMALLEST_RING_SIZE, "ringSize"},
                                              {QueryMolecule::ATOM_CONNECTIVITY, "connectivity"}};
            bool hasQueryProperties =
                query_atom_properties.count(QueryMolecule::ATOM_AROMATICITY) > 0 || query_atom_properties.count(QueryMolecule::ATOM_CHIRALITY) > 0 ||
                std::any_of(qprops.cbegin(), qprops.cend(), [&query_atom_properties](auto p) { return query_atom_properties.count(p.first) > 0; });
            if (needCustomQuery || hasQueryProperties)
            {
                writer.Key("queryProperties");
                writer.StartObject();
                if (needCustomQuery)
                {
                    QueryMolecule::Atom& atom = _pqmol->getAtom(i);
                    std::string customQuery = QueryMolecule::getSmartsAtomStr(&atom, _pqmol->original_format, false);
                    writer.Key("customQuery");
                    writer.String(customQuery.c_str());
                }
                else
                {
                    int value = VALUE_UNKNOWN;

                    if (query_atom_properties.count(QueryMolecule::ATOM_AROMATICITY))
                    {
                        value = query_atom_properties[QueryMolecule::ATOM_AROMATICITY]->value_min;
                        writer.Key("aromaticity");
                        if (value == ATOM_AROMATIC)
                            writer.String(ATOM_AROMATIC_STR);
                        else if (value == ATOM_ALIPHATIC)
                            writer.String(ATOM_ALIPHATIC_STR);
                        else
                            throw "Wrong aromaticity value";
                    }
                    if (query_atom_properties.count(QueryMolecule::ATOM_CHIRALITY))
                    {
                        // This is CHIRALITY_GENERAL without CHIRALITY_OR_UNSPECIFIED
                        writer.Key("chirality");
                        value = query_atom_properties[QueryMolecule::ATOM_CHIRALITY]->value_max;
                        if (value == QueryMolecule::CHIRALITY_CLOCKWISE)
                            writer.String("clockwise");
                        else if (value == QueryMolecule::CHIRALITY_ANTICLOCKWISE)
                            writer.String("anticlockwise");
                        else
                            throw Error("Wrong chirality value %d", value);
                    }
                    for (auto p : qprops)
                    {
                        if (query_atom_properties.count(p.first) > 0)
                        {
                            writer.Key(p.second);
                            writer.Uint(query_atom_properties[p.first]->value_min);
                        }
                    }
                    // 2do add hirality
                    //*/
                }
                writer.EndObject();
            }

            int subst = 0, rbc = 0;
            if (MoleculeSavers::getRingBondCountFlagValue(*_pqmol, i, rbc))
            {
                writer.Key("ringBondCount");
                writer.Int(rbc);
            }
            if (MoleculeSavers::getSubstitutionCountFlagValue(*_pqmol, i, subst))
            {
                writer.Key("substitutionCount");
                writer.Int(subst);
            }

            int unsat = 0;
            if (_pqmol->getAtom(i).sureValue(QueryMolecule::ATOM_UNSATURATION, unsat))
            {
                writer.Key("unsaturatedAtom");
                writer.Bool(true);
            }

            if (hcount == VALUE_UNKNOWN)
                hcount = 0;
            else
                hcount++;
            if (hcount > 0)
            {
                writer.Key("hCount");
                writer.Int(hcount);
            }
            if (query_atom_type >= 0 && query_atom_properties.count(QueryMolecule::ATOM_IMPLICIT_H) > 0)
            {
                writer.Key("implicitHCount");
                writer.Int(query_atom_properties[QueryMolecule::ATOM_IMPLICIT_H]->value_min);
            }
        }
        else if (_pmol)
        {
            if (Molecule::shouldWriteHCount(mol.asMolecule(), i) && hcount > 0)
            {
                writer.Key("implicitHCount");
                writer.Int(hcount);
            }
        }

        if (mapping)
        {
            writer.Key("mapping");
            writer.Int(mapping);
        }

        if ((mol.isQueryMolecule() && charge != CHARGE_UNKNOWN) || (!mol.isQueryMolecule() && charge != 0))
        {
            writer.Key("charge");
            writer.Int(charge);
        }

        // int total_bond_count = 0;
        if (evalence > 0)
        {
            writer.Key("explicitValence");
            writer.Int(evalence);
        }
        if (radical > 0)
        {
            writer.Key("radical");
            writer.Int(radical);
        }

        if (isotope > 0 && anum != ELEM_H)
        {
            writer.Key("isotope");
            writer.Int(isotope);
        }

        if (inv_ret > 0)
        {
            writer.Key("invRet");
            writer.Int(inv_ret);
        }

        if (ecflag)
        {
            writer.Key("exactChangeFlag");
            writer.Bool(ecflag);
        }

        int enh_stereo_type = mol.stereocenters.getType(i);
        if (enh_stereo_type > 1)
        {
            writer.Key("stereoLabel");
            switch (enh_stereo_type)
            {
            case MoleculeStereocenters::ATOM_ABS:
                writer.String("abs");
                break;
            case MoleculeStereocenters::ATOM_OR:
                writer.String((std::string("or") + std::to_string(mol.stereocenters.getGroup(i))).c_str());
                break;
            case MoleculeStereocenters::ATOM_AND:
                writer.String((std::string("&") + std::to_string(mol.stereocenters.getGroup(i))).c_str());
                break;
            default:
                throw Error("Unknows enhanced stereo type %d", enh_stereo_type);
                break;
            }
        }

        auto cip = mol.getAtomCIP(i);
        if (cip != CIPDesc::NONE)
        {
            auto cip_str = CIPToString(cip);
            if (cip_str.size())
            {
                writer.Key("cip");
                writer.String(cip_str.c_str());
            }
        }

        if (mol.isRSite(i) && !_checkAttPointOrder(mol, i))
        {
            const Vertex& vertex = mol.getVertex(i);
            writer.Key("attachmentOrder");
            writer.StartArray();
            for (int k = 0; k < vertex.degree(); k++)
            {
                writer.StartObject();
                writer.Key("attachmentAtom");
                writer.Int(mol.getRSiteAttachmentPointByOrder(i, k));
                writer.Key("attachmentId");
                writer.Int(k);
                writer.EndObject();
            }
            writer.EndArray();
        }

        if (mol.isTemplateAtom(i))
        {
            auto pclass = mol.getTemplateAtomClass(i);
            if (pclass && strlen(pclass))
            {
                writer.Key("class");
                writer.String(pclass);
            }

            auto seqid = mol.getTemplateAtomSeqid(i);
            if (seqid != VALUE_UNKNOWN)
            {
                writer.Key("seqid");
                writer.Int(seqid);
            }

            if (mol.template_attachment_points.size())
            {
                if (mol.getTemplateAtomAttachmentPointsCount(i))
                {
                    writer.Key("attOrder");
                    writer.StartArray();
                    for (int j = mol.template_attachment_points.begin(); j != mol.template_attachment_points.end(); j = mol.template_attachment_points.next(j))
                    {
                        BaseMolecule::TemplateAttPoint& ap = mol.template_attachment_points.at(j);
                        if (ap.ap_occur_idx == i)
                        {
                            writer.StartObject();
                            writer.Key("index");
                            writer.Int(ap.ap_aidx);
                            writer.Key("id");
                            writer.String(ap.ap_id.ptr());
                            writer.EndObject();
                        }
                    }
                    writer.EndArray();
                }
            }
        }
        writer.EndObject();
    }
}