diff --git a/api/c/indigo/indigo.h b/api/c/indigo/indigo.h index 2103849b6b..561b117e5f 100644 --- a/api/c/indigo/indigo.h +++ b/api/c/indigo/indigo.h @@ -404,6 +404,17 @@ CEXPORT int indigoAtomicNumber(int atom); CEXPORT int indigoIsotope(int atom); // Not applicable to query molecules. CEXPORT int indigoValence(int atom); +// Return atom hybridization +// S = 1, +// SP = 2, +// SP2 = 3, +// SP3 = 4, +// SP3D = 5, +// SP3D2 = 6, +// SP3D3 = 7, +// SP3D4 = 8, +// SP2D = 9 +CEXPORT int indigoGetHybridization(int atom); // Returns zero if valence of the atom is wrong CEXPORT int indigoCheckValence(int atom); diff --git a/api/c/indigo/src/indigo_molecule.cpp b/api/c/indigo/src/indigo_molecule.cpp index 28520d604b..493283a8d5 100644 --- a/api/c/indigo/src/indigo_molecule.cpp +++ b/api/c/indigo/src/indigo_molecule.cpp @@ -25,6 +25,7 @@ #include "indigo_mapping.h" #include "molecule/canonical_smiles_saver.h" #include "molecule/elements.h" +#include "molecule/hybridization.h" #include "molecule/molecule_auto_loader.h" #include "molecule/molecule_automorphism_search.h" #include "molecule/molecule_fingerprint.h" @@ -1271,6 +1272,17 @@ CEXPORT int indigoValence(int atom) INDIGO_END(-1); } +CEXPORT int indigoGetHybridization(int atom) +{ + INDIGO_BEGIN + { + IndigoAtom& ia = IndigoAtom::cast(self.getObject(atom)); + auto& molecule = ia.mol.asMolecule(); + return static_cast(HybridizationCalculator::calculate(molecule, ia.idx)); + } + INDIGO_END(-1); +} + CEXPORT int indigoCheckValence(int atom) { INDIGO_BEGIN diff --git a/api/cpp/src/BingoNoSQL.cpp b/api/cpp/src/BingoNoSQL.cpp index e760729c63..73df91b5a9 100644 --- a/api/cpp/src/BingoNoSQL.cpp +++ b/api/cpp/src/BingoNoSQL.cpp @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #include "BingoNoSQL.h" #include diff --git a/api/cpp/src/BingoNoSQL.h b/api/cpp/src/BingoNoSQL.h index dc05a7eef9..246d881026 100644 --- a/api/cpp/src/BingoNoSQL.h +++ b/api/cpp/src/BingoNoSQL.h @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #pragma once #include "BingoResultIterator.h" diff --git a/api/cpp/src/BingoResult.cpp b/api/cpp/src/BingoResult.cpp index d71d63eecc..c5848fd62b 100644 --- a/api/cpp/src/BingoResult.cpp +++ b/api/cpp/src/BingoResult.cpp @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #include #include "BingoResult.h" diff --git a/api/cpp/src/BingoResult.h b/api/cpp/src/BingoResult.h index f2adcc8d0d..518647eb35 100644 --- a/api/cpp/src/BingoResult.h +++ b/api/cpp/src/BingoResult.h @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #pragma once #include "IndigoSession.h" diff --git a/api/cpp/src/BingoResultIterator.cpp b/api/cpp/src/BingoResultIterator.cpp index 274e4e07e8..c034f736f9 100644 --- a/api/cpp/src/BingoResultIterator.cpp +++ b/api/cpp/src/BingoResultIterator.cpp @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #include "BingoResultIterator.h" #include diff --git a/api/cpp/src/BingoResultIterator.h b/api/cpp/src/BingoResultIterator.h index cc77e97426..4521f84350 100644 --- a/api/cpp/src/BingoResultIterator.h +++ b/api/cpp/src/BingoResultIterator.h @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #pragma once #include "IndigoSession.h" diff --git a/api/cpp/src/IndigoAtom.cpp b/api/cpp/src/IndigoAtom.cpp new file mode 100644 index 0000000000..ecdfb25886 --- /dev/null +++ b/api/cpp/src/IndigoAtom.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +#include "IndigoAtom.h" + +#include + +namespace indigo_cpp +{ + IndigoAtom::IndigoAtom(int id, IndigoSessionPtr session) : IndigoObject(id, std::move(session)) + { + } + + Hybridization IndigoAtom::getHybridization() const + { + session()->setSessionId(); + return Hybridization(session()->_checkResult(indigoGetHybridization(id()))); + } +} diff --git a/api/cpp/src/IndigoAtom.h b/api/cpp/src/IndigoAtom.h new file mode 100644 index 0000000000..df66390eb8 --- /dev/null +++ b/api/cpp/src/IndigoAtom.h @@ -0,0 +1,45 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +#pragma once + +#include "IndigoObject.h" + +namespace indigo_cpp +{ + enum class Hybridization + { + S = 1, + SP = 2, + SP2 = 3, + SP3 = 4, + SP3D = 5, + SP3D2 = 6, + SP3D3 = 7, + SP3D4 = 8, + SP2D = 9 + }; + + class IndigoAtom : public IndigoObject + { + public: + explicit IndigoAtom(int id, IndigoSessionPtr session); + + Hybridization getHybridization() const; + }; +} diff --git a/api/cpp/src/IndigoBaseMolecule.cpp b/api/cpp/src/IndigoBaseMolecule.cpp index 41fc4459e2..17ec0c4678 100644 --- a/api/cpp/src/IndigoBaseMolecule.cpp +++ b/api/cpp/src/IndigoBaseMolecule.cpp @@ -41,3 +41,9 @@ std::string IndigoBaseMolecule::ctfile() const { return molfile(); } + +IndigoAtom IndigoBaseMolecule::getAtom(int atomIndex) const +{ + session()->setSessionId(); + return IndigoAtom(session()->_checkResult(indigoGetAtom(id(), atomIndex)), session()); +} diff --git a/api/cpp/src/IndigoBaseMolecule.h b/api/cpp/src/IndigoBaseMolecule.h index e116c32dd7..b4f65bcc69 100644 --- a/api/cpp/src/IndigoBaseMolecule.h +++ b/api/cpp/src/IndigoBaseMolecule.h @@ -18,6 +18,7 @@ #pragma once +#include "IndigoAtom.h" #include "IndigoChemicalStructure.h" namespace indigo_cpp @@ -35,5 +36,7 @@ namespace indigo_cpp public: std::string molfile() const; std::string ctfile() const override; + + IndigoAtom getAtom(int atomIndex) const; }; } diff --git a/api/cpp/src/IndigoCopyableObject.cpp b/api/cpp/src/IndigoCopyableObject.cpp index 81dfe2f0c6..e1cae6fc84 100644 --- a/api/cpp/src/IndigoCopyableObject.cpp +++ b/api/cpp/src/IndigoCopyableObject.cpp @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #include "IndigoCopyableObject.h" #include diff --git a/api/cpp/src/IndigoCopyableObject.h b/api/cpp/src/IndigoCopyableObject.h index 0395b2e1bc..3657fde7ca 100644 --- a/api/cpp/src/IndigoCopyableObject.h +++ b/api/cpp/src/IndigoCopyableObject.h @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #pragma once #include "IndigoObject.h" diff --git a/api/cpp/src/IndigoMolecule.cpp b/api/cpp/src/IndigoMolecule.cpp index 389dcb11d7..ce3d10de2d 100644 --- a/api/cpp/src/IndigoMolecule.cpp +++ b/api/cpp/src/IndigoMolecule.cpp @@ -34,35 +34,42 @@ IndigoMolecule::IndigoMolecule(const IndigoMolecule& other) : IndigoBaseMolecule double IndigoMolecule::molecularWeight() const { + session()->setSessionId(); return session()->_checkResultFloat(indigoMolecularWeight(id())); } double IndigoMolecule::tpsa(const bool includeSP) const { + session()->setSessionId(); return session()->_checkResultFloat(indigoTPSA(id(), static_cast(includeSP))); } int IndigoMolecule::numRotatableBonds() const { + session()->setSessionId(); return session()->_checkResult(indigoNumRotatableBonds(id())); } int IndigoMolecule::numHydrogenBondAcceptors() const { + session()->setSessionId(); return session()->_checkResult(indigoNumHydrogenBondAcceptors(id())); } int IndigoMolecule::numHydrogenBondDonors() const { + session()->setSessionId(); return session()->_checkResult(indigoNumHydrogenBondDonors(id())); } double IndigoMolecule::cLogP() const { + session()->setSessionId(); return session()->_checkResultFloat(indigoCLogP(id())); } double IndigoMolecule::cMolarRefractivity() const { + session()->setSessionId(); return session()->_checkResultFloat(indigoCMolarRefractivity(id())); } diff --git a/api/cpp/src/IndigoRDFileIterator.cpp b/api/cpp/src/IndigoRDFileIterator.cpp index 64e2e45ad9..e5f82f8edf 100644 --- a/api/cpp/src/IndigoRDFileIterator.cpp +++ b/api/cpp/src/IndigoRDFileIterator.cpp @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #include "IndigoRDFileIterator.h" #include diff --git a/api/cpp/src/IndigoRDFileIterator.h b/api/cpp/src/IndigoRDFileIterator.h index ef7bfe8b76..52c7e406cf 100644 --- a/api/cpp/src/IndigoRDFileIterator.h +++ b/api/cpp/src/IndigoRDFileIterator.h @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #pragma once #include diff --git a/api/cpp/src/IndigoSDFileIterator.cpp b/api/cpp/src/IndigoSDFileIterator.cpp index c0affedaf4..a82d6f340f 100644 --- a/api/cpp/src/IndigoSDFileIterator.cpp +++ b/api/cpp/src/IndigoSDFileIterator.cpp @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #include "IndigoSDFileIterator.h" #include diff --git a/api/cpp/src/IndigoSDFileIterator.h b/api/cpp/src/IndigoSDFileIterator.h index 85de9c51e2..417fc52d7e 100644 --- a/api/cpp/src/IndigoSDFileIterator.h +++ b/api/cpp/src/IndigoSDFileIterator.h @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #pragma once #include diff --git a/api/cpp/src/IndigoSimilarityMetric.cpp b/api/cpp/src/IndigoSimilarityMetric.cpp index 52aa1f63e6..50f8627806 100644 --- a/api/cpp/src/IndigoSimilarityMetric.cpp +++ b/api/cpp/src/IndigoSimilarityMetric.cpp @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #include "IndigoSimilarityMetric.h" #include diff --git a/api/cpp/src/IndigoSimilarityMetric.h b/api/cpp/src/IndigoSimilarityMetric.h index 3fb95c2c30..657346deda 100644 --- a/api/cpp/src/IndigoSimilarityMetric.h +++ b/api/cpp/src/IndigoSimilarityMetric.h @@ -1,3 +1,21 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + #pragma once namespace indigo_cpp diff --git a/api/cpp/tests/basic/atom.cpp b/api/cpp/tests/basic/atom.cpp new file mode 100644 index 0000000000..c2e3ced595 --- /dev/null +++ b/api/cpp/tests/basic/atom.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +#include + +#include +#include + +#include "common.h" + +using namespace indigo_cpp; + +TEST(Atom, getHybridization) +{ + const auto& session = IndigoSession::create(); + { + const auto& molecule = session->loadMolecule(METHANE); + const auto& atom = molecule.getAtom(0); + EXPECT_EQ(Hybridization::SP3, atom.getHybridization()); + } +} diff --git a/api/dotnet/src/Indigo.cs b/api/dotnet/src/Indigo.cs index 3e7d8efb6d..72bd4f88e6 100644 --- a/api/dotnet/src/Indigo.cs +++ b/api/dotnet/src/Indigo.cs @@ -17,6 +17,19 @@ public enum ReactingCenter ORDER_CHANGED = 8 } + public enum Hybridization + { + S = 1, + SP = 2, + SP2 = 3, + SP3 = 4, + SP3D = 5, + SP3D2 = 6, + SP3D3 = 7, + SP3D4 = 8, + SP2D = 9 + } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public unsafe class Indigo : IDisposable { diff --git a/api/dotnet/src/IndigoLib.cs b/api/dotnet/src/IndigoLib.cs index 5abfa1f3d6..068b6a007d 100644 --- a/api/dotnet/src/IndigoLib.cs +++ b/api/dotnet/src/IndigoLib.cs @@ -377,6 +377,9 @@ public unsafe class IndigoLib [DllImport("indigo"), SuppressUnmanagedCodeSecurity] public static extern int indigoValence(int atom); + [DllImport("indigo"), SuppressUnmanagedCodeSecurity] + public static extern int indigoGetHybridization(int atom); + [DllImport("indigo"), SuppressUnmanagedCodeSecurity] public static extern int indigoCheckValence(int atom); diff --git a/api/dotnet/src/IndigoObject.cs b/api/dotnet/src/IndigoObject.cs index 24768ff70d..883b775596 100644 --- a/api/dotnet/src/IndigoObject.cs +++ b/api/dotnet/src/IndigoObject.cs @@ -480,6 +480,17 @@ public int valence() return dispatcher.checkResult(IndigoLib.indigoValence(self)); } + public Hybridization getHybridization() + { + dispatcher.setSessionID(); + return (Hybridization)dispatcher.checkResult(IndigoLib.indigoGetHybridization(self)); + } + + public string getHybridizationStr() + { + return getHybridization().ToString(); + } + public int checkValence() { dispatcher.setSessionID(); diff --git a/api/java/indigo/src/main/java/com/epam/indigo/Hybridization.java b/api/java/indigo/src/main/java/com/epam/indigo/Hybridization.java new file mode 100644 index 0000000000..2d6d29576d --- /dev/null +++ b/api/java/indigo/src/main/java/com/epam/indigo/Hybridization.java @@ -0,0 +1,55 @@ +package com.epam.indigo; + +public enum Hybridization { + S(1, "S"), + SP(2, "SP"), + SP2(3, "SP2"), + SP3(4, "SP3"), + SP3D(5, "SP3D"), + SP3D2(6, "SP3D2"), + SP3D3(7, "SP3D3"), + SP3D4(8, "SP3D4"), + SP2D(9, "SP2D"); + + private int numVal; + private String strVal; + + Hybridization(int numVal, String strVal) { + this.numVal = numVal; + this.strVal = strVal; + } + + public int getNumVal() { + return numVal; + } + + public String getStrVal() { + return strVal; + } + + + public static Hybridization fromNum(int value) { + switch (value) { + case 1: + return Hybridization.S; + case 2: + return Hybridization.SP; + case 3: + return Hybridization.SP2; + case 4: + return Hybridization.SP3; + case 5: + return Hybridization.SP3D; + case 6: + return Hybridization.SP3D2; + case 7: + return Hybridization.SP3D3; + case 8: + return Hybridization.SP3D4; + case 9: + return Hybridization.SP2D; + default: + throw new IndigoException(new Object(), "Unknown hybridization"); + } + } +} diff --git a/api/java/indigo/src/main/java/com/epam/indigo/Indigo.java b/api/java/indigo/src/main/java/com/epam/indigo/Indigo.java index 7618875a41..ed0416e2b6 100644 --- a/api/java/indigo/src/main/java/com/epam/indigo/Indigo.java +++ b/api/java/indigo/src/main/java/com/epam/indigo/Indigo.java @@ -65,6 +65,8 @@ public class Indigo { public static final int SG_TYPE_FOR = 13; public static final int SG_TYPE_ANY = 14; + + // JNA does not allow throwing exception from callbacks, thus we can not // use the error handler and we have to check the error codes. Below are // four functions to ease checking them. diff --git a/api/java/indigo/src/main/java/com/epam/indigo/IndigoLib.java b/api/java/indigo/src/main/java/com/epam/indigo/IndigoLib.java index c850baf6ee..6b427b7a80 100644 --- a/api/java/indigo/src/main/java/com/epam/indigo/IndigoLib.java +++ b/api/java/indigo/src/main/java/com/epam/indigo/IndigoLib.java @@ -278,6 +278,8 @@ public interface IndigoLib extends Library { int indigoValence(int atom); + int indigoGetHybridization(int atom); + int indigoCheckValence(int atom); int indigoCheckQuery(int item); diff --git a/api/java/indigo/src/main/java/com/epam/indigo/IndigoObject.java b/api/java/indigo/src/main/java/com/epam/indigo/IndigoObject.java index 887a8d0f43..3e9fea03ff 100644 --- a/api/java/indigo/src/main/java/com/epam/indigo/IndigoObject.java +++ b/api/java/indigo/src/main/java/com/epam/indigo/IndigoObject.java @@ -397,6 +397,15 @@ public int valence() { return Indigo.checkResult(this, lib.indigoValence(self)); } + public Hybridization getHybridization() { + dispatcher.setSessionID(); + return Hybridization.fromNum(Indigo.checkResult(this, lib.indigoGetHybridization(self))); + } + + public String getHybridizationStr() { + return getHybridization().getStrVal(); + } + public int checkValence() { dispatcher.setSessionID(); return Indigo.checkResult(this, lib.indigoCheckValence(self)); diff --git a/api/python/indigo/__init__.py b/api/python/indigo/__init__.py index 4c4997e50e..682d062db7 100644 --- a/api/python/indigo/__init__.py +++ b/api/python/indigo/__init__.py @@ -36,15 +36,27 @@ pointer, sizeof, ) +from enum import Enum from indigo.exceptions import IndigoException -from indigo.hybridization import get_hybridization from indigo.salts import SALTS DECODE_ENCODING = "utf-8" ENCODE_ENCODING = "utf-8" +class Hybridization(Enum): + S = 1 + SP = 2 + SP2 = 3 + SP3 = 4 + SP3D = 5 + SP3D2 = 6 + SP3D3 = 7 + SP3D4 = 8 + SP2D = 9 + + class IndigoObject(object): """Wraps all Indigo model objects""" @@ -1069,9 +1081,14 @@ def getHybridization(self): """Atom method returns HybridizationType Returns: - HybridizationType: atom hybridization + Hybridization: atom hybridization """ - return get_hybridization(self) + self.dispatcher._setSessionId() + return Hybridization( + self.dispatcher._checkResult( + Indigo._lib.indigoGetHybridization(self.id) + ) + ) def getHybridizationStr(self): """Atom method returns hybridization type string @@ -1079,7 +1096,7 @@ def getHybridizationStr(self): Returns: str: atom hybridization """ - return get_hybridization(self).name + return self.getHybridization().name def getExplicitValence(self): """Atom method returns the explicit valence @@ -4739,6 +4756,8 @@ def __init__(self, path=None): Indigo._lib.indigoIsotope.argtypes = [c_int] Indigo._lib.indigoValence.restype = c_int Indigo._lib.indigoValence.argtypes = [c_int] + Indigo._lib.indigoGetHybridization.restype = c_int + Indigo._lib.indigoGetHybridization.argtypes = [c_int] Indigo._lib.indigoCheckValence.restype = c_int Indigo._lib.indigoCheckValence.argtypes = [c_int] Indigo._lib.indigoCheckQuery.restype = c_int diff --git a/api/python/indigo/hybridization.py b/api/python/indigo/hybridization.py deleted file mode 100644 index 9fadb8d137..0000000000 --- a/api/python/indigo/hybridization.py +++ /dev/null @@ -1,226 +0,0 @@ -import math -from enum import Enum -from typing import TYPE_CHECKING - -from indigo.exceptions import IndigoException - -if TYPE_CHECKING: - from indigo import IndigoObject - -OUTER_ELECTRONS = { - "H": 1, - "He": 2, - "Li": 1, - "Be": 2, - "B": 3, - "C": 4, - "N": 5, - "O": 6, - "F": 7, - "Ne": 8, - "Na": 1, - "Mg": 2, - "Al": 3, - "Si": 4, - "P": 5, - "S": 6, - "Cl": 7, - "Ar": 8, - "K": 1, - "Ca": 2, - "Sc": 3, - "Ti": 4, - "V": 5, # 4s2 3d3 (!) - "Cr": 6, # 4s1 3d5 (!) - "Mn": 7, # 4s2 3d5 - "Fe": 8, # 4s2 3d6 - "Co": 9, - "Ni": 10, - "Cu": 1, - "Zn": 2, - "Ga": 3, - "Ge": 4, - "As": 5, - "Se": 6, - "Br": 7, - "Kr": 8, - "Rb": 1, - "Sr": 2, - "Y": 3, # 5s2 4d1 - "Zr": 4, # 5s2 4d2 - "Nb": 5, # 5s1 4d4 (!) - "Mo": 6, # 5s1 4d5 - "Tc": 7, # 5s2 4d5 - "Ru": 8, # 5s1 4d7 (!!!) - "Rh": 9, # 5s1 4d8 - "Pd": 10, # 4d10 (!) - "Ag": 1, - "Cd": 2, - "In": 3, - "Sn": 4, - "Sb": 5, - "Te": 6, - "I": 7, - "Xe": 8, - "Cs": 1, - "Ba": 2, - "La": 3, # 6s2 5d1 - "Ce": 4, # 6s2 4f2 (!!!) Atomic num = 58, lantanoids -} - - -class HybridizationType(Enum): - S = 1 - SP = 2 - SP2 = 3 - SP3 = 4 - SP3D = 5 - SP3D2 = 6 - SP3D3 = 7 - SP3D4 = 8 - SP2D = 9 - - -def num_bonds(atom: "IndigoObject") -> int: - bonds = 0 - for nei in atom.iterateNeighbors(): - bonds += nei.bond().bondOrder() - bonds += atom.countImplicitHydrogens() - return bonds - - -def has_lone_pair(atom: "IndigoObject", n_bonds: int) -> bool: - return OUTER_ELECTRONS[atom.symbol()] - n_bonds >= 2 - - -def lone_pairs(atom: "IndigoObject", n_bonds: int) -> int: - outer_electrons = OUTER_ELECTRONS[atom.symbol()] - pairs = math.floor((outer_electrons - n_bonds) / 2) - return pairs - - -def carbon_hybridization(carbon: "IndigoObject") -> HybridizationType: - neighbors = carbon.degree() + carbon.countImplicitHydrogens() - if neighbors == 4: - return HybridizationType.SP3 - if neighbors == 3: - return HybridizationType.SP2 - if neighbors <= 2: - return HybridizationType.SP - raise IndigoException("Couldn't calculate C hybridization properly") - - -def match_minus_induction(atom: "IndigoObject") -> bool: - for nei in atom.iterateNeighbors(): - if nei.bond().bondOrder() == 2: - return True - return False - - -def oxygen_hybridization(oxygen: "IndigoObject") -> HybridizationType: - for nei in oxygen.iterateNeighbors(): - if nei.bond().bondOrder() == 2: - return HybridizationType.SP2 - if nei.bond().bondOrder() == 3: - # for CO, but some sources do not recognize "sp" for oxygen - return HybridizationType.SP - if nei.symbol() == "C" and match_minus_induction(nei): - return HybridizationType.SP2 - return HybridizationType.SP3 - - -def nitrogen_hybridization( - nitrogen: "IndigoObject", n_bonds: int -) -> HybridizationType: - n_orbs = nitrogen.degree() + has_lone_pair(nitrogen, n_bonds) - minus_induction = False - for nei in nitrogen.iterateNeighbors(): - if nei.symbol() == "C" and match_minus_induction(nei): - minus_induction = True - if n_orbs == 4 and minus_induction: - return HybridizationType(n_orbs - 1) - if n_orbs <= 4: - return HybridizationType(n_orbs) - raise IndigoException("Couldn't calculate N hybridization properly") - - -def complex_hybridization( - atom: "IndigoObject", neighbors: int -) -> HybridizationType: - if neighbors == 4: - if atom.symbol() in ["Pt", "Ni", "Cu", "Au", "Pd"]: - return HybridizationType.SP2D - return HybridizationType.SP3 - if neighbors == 5: - return HybridizationType.SP3D - if neighbors == 6: - return HybridizationType.SP3D2 - if neighbors == 7: - return HybridizationType.SP3D3 - if neighbors == 8: - return HybridizationType.SP3D4 - raise IndigoException( - f"Couldn't calculate {atom.symbol()} hybridization properly" - ) - - -def in_aromatic_ring(atom: "IndigoObject") -> bool: - for nei in atom.iterateNeighbors(): - if nei.bond().topology() == 10: - return True - return False - - -def get_hybridization(atom: "IndigoObject") -> HybridizationType: - """Returns HybridizationType for an atom from a molecule. - - Works only with atoms with atomic numbers from 1 to 56. Don't bother with - the lantanoids and beyond. If atomic number is undefined or ambiguous or > - 56 raises IndigoException. - - Args: - atom: an indigo.IndigoObject for the atom. - Returns: - HybridizationType: atom hybridization. Could be HybridizationType.S - for unhybridized atom and HybridizationType.SP, HybridizationType.SP2, - etc. for hybridized. - """ - # if the atomic number is undefined or ambiguous - if atom.atomicNumber() == 0: - raise IndigoException("Atomic number is undefined or ambiguous") - - # don't bother with the lantanoids and beyond - elif atom.atomicNumber() >= 57: - raise IndigoException( - "Hybridization calculation is not implemented for atomic numbers " - ">= 57 " - ) - if atom.atomicNumber() == 1: - return HybridizationType.S - - n_bonds = num_bonds(atom) - - if n_bonds == 0: - return HybridizationType.S - - if atom.symbol() in ["C", "N", "O", "P", "S", "B"] and in_aromatic_ring( - atom - ): - return HybridizationType.SP2 - - if atom.atomicNumber() == 6: - return carbon_hybridization(atom) - if atom.atomicNumber() == 8: - return oxygen_hybridization(atom) - if atom.atomicNumber() == 7: - return nitrogen_hybridization(atom, n_bonds) - - if atom.degree() >= 4: - return complex_hybridization(atom, atom.degree()) - - # number of hybridized orbitals = number of neighbours + number of lone - # electron pairs - # H = N + L - - n_orbs = atom.degree() + lone_pairs(atom, n_bonds) - return HybridizationType(n_orbs) diff --git a/api/python/tests/test_hybridization.py b/api/python/tests/test_hybridization.py deleted file mode 100644 index 2a00bf54bc..0000000000 --- a/api/python/tests/test_hybridization.py +++ /dev/null @@ -1,135 +0,0 @@ -import pytest - -from indigo import Indigo -from indigo.hybridization import HybridizationType, get_hybridization - - -@pytest.mark.parametrize( - "molecule_smiles, expecting", - [ - ( - "c1ccccc1", # benzene - [ - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - ], - ), - ( - "OC1=CC=CC=C1", # phenol - [ - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - ], - ), - ( - "[C-]#[O+]", - [HybridizationType.SP, HybridizationType.SP], - ), # carbon monoxide - ( - "O=C=O", - [ - HybridizationType.SP2, - HybridizationType.SP, - HybridizationType.SP2, - ], - ), # carbon dioxide - ( - "C#N", - [HybridizationType.SP, HybridizationType.SP], - ), # hydrogen cyanide - ( - "O=C(N)C", # acetamide - [ - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP, - HybridizationType.SP3, - ], - ), - ( - "OS(=O)(=O)O", # sulfuric acid - [ - HybridizationType.SP3, - HybridizationType.SP3, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP3, - ], - ), - ( - "N(=O)O", - [ - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP3, - ], - ), # nitrous acid - ( - "O=[Xe](=O)(=O)=O", # xenon tetroxide - [ - HybridizationType.SP2, - HybridizationType.SP3, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - ], - ), - ( - "FS(F)(F)(F)(F)F", # sulfur hexafluoride - [ - HybridizationType.SP3, - HybridizationType.SP3D2, - HybridizationType.SP3, - HybridizationType.SP3, - HybridizationType.SP3, - HybridizationType.SP3, - HybridizationType.SP3, - ], - ), - ( - "FBr(F)F", - [ - HybridizationType.SP3, - HybridizationType.SP3D, - HybridizationType.SP3, - HybridizationType.SP3, - ], - ), # bromine trifluoride - ( - "[Be](Cl)Cl", - [ - HybridizationType.SP, - HybridizationType.SP3, - HybridizationType.SP3, - ], - ), - ( - "C1=CC=CS1", - [ - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - HybridizationType.SP2, - ], - ), # thiophene - ], -) -def test_get_hybridization(molecule_smiles, expecting): - indigo = Indigo() - mol = indigo.loadMolecule(molecule_smiles) - - hybridizations = [] - for atom in mol.iterateAtoms(): - hybridization = get_hybridization(atom) - hybridizations.append(hybridization) - assert hybridizations == expecting diff --git a/api/tests/integration/ref/basic/hybridization.py.out b/api/tests/integration/ref/basic/hybridization.py.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/tests/integration/tests/basic/hybridization.py b/api/tests/integration/tests/basic/hybridization.py new file mode 100644 index 0000000000..69690467ce --- /dev/null +++ b/api/tests/integration/tests/basic/hybridization.py @@ -0,0 +1,145 @@ +import sys + +sys.path.append("../../common") +from env_indigo import * + +indigo = Indigo() + + +def do_test_get_hybridization(molecule_smiles, expecting): + mol = indigo.loadMolecule(molecule_smiles) + + hybridizations = [] + for atom in mol.iterateAtoms(): + hybridizations.append(atom.getHybridizationStr()) + if hybridizations != expecting: + print(str(hybridizations) + " != " + str(expecting)) + + +if __name__ == "__main__": + do_test_get_hybridization( + "c1ccccc1", + [ + "SP2", + "SP2", + "SP2", + "SP2", + "SP2", + "SP2", + ], + ) + do_test_get_hybridization( + "OC1=CC=CC=C1", + [ + "SP2", + "SP2", + "SP2", + "SP2", + "SP2", + "SP2", + "SP2", + ], + ) + do_test_get_hybridization( + "[C-]#[O+]", + ["SP", "SP"], + ), # carbon monoxide + do_test_get_hybridization( + "O=C=O", + [ + "SP2", + "SP", + "SP2", + ], + ), # carbon dioxide + do_test_get_hybridization( + "C#N", + ["SP", "SP"], + ), # hydrogen cyanide + do_test_get_hybridization( + "O=C(N)C", # acetamide + [ + "SP2", + "SP2", + "SP", + "SP3", + ], + ), + do_test_get_hybridization( + "OS(=O)(=O)O", # sulfuric acid + [ + "SP3", + "SP3", + "SP2", + "SP2", + "SP3", + ], + ), + do_test_get_hybridization( + "N(=O)O", + [ + "SP2", + "SP2", + "SP3", + ], + ), # nitrous acid + do_test_get_hybridization( + "O=[Xe](=O)(=O)=O", # xenon tetroxide + [ + "SP2", + "SP3", + "SP2", + "SP2", + "SP2", + ], + ), + do_test_get_hybridization( + "FS(F)(F)(F)(F)F", # sulfur hexafluoride + [ + "SP3", + "SP3D2", + "SP3", + "SP3", + "SP3", + "SP3", + "SP3", + ], + ), + do_test_get_hybridization( + "FBr(F)F", + [ + "SP3", + "SP3D", + "SP3", + "SP3", + ], + ), # bromine trifluoride + do_test_get_hybridization( + "[Be](Cl)Cl", + [ + "SP", + "SP3", + "SP3", + ], + ), + do_test_get_hybridization( + "C1=CC=CS1", + [ + "SP2", + "SP2", + "SP2", + "SP2", + "SP2", + ], + ) + +do_test_get_hybridization( + "C1=CC=CS1", + [ + "SP2", + "SP2", + "SP2", + "SP2", + "SP2", + ], +) diff --git a/core/indigo-core/molecule/elements.h b/core/indigo-core/molecule/elements.h index dcb88b3197..99e33cc318 100644 --- a/core/indigo-core/molecule/elements.h +++ b/core/indigo-core/molecule/elements.h @@ -213,6 +213,8 @@ namespace indigo static bool canBeAromatic(int element); + static int getNumOuterElectrons(int element); + Element(const Element&) = delete; Element(Element&&) = delete; Element& operator=(const Element&) = delete; diff --git a/core/indigo-core/molecule/hybridization.h b/core/indigo-core/molecule/hybridization.h new file mode 100644 index 0000000000..3151aa73c6 --- /dev/null +++ b/core/indigo-core/molecule/hybridization.h @@ -0,0 +1,47 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +#pragma once + +#include + +namespace indigo +{ + class Molecule; + + enum class Hybridization + { + S = 1, + SP = 2, + SP2 = 3, + SP3 = 4, + SP3D = 5, + SP3D2 = 6, + SP3D3 = 7, + SP3D4 = 8, + SP2D = 9 + }; + + class HybridizationCalculator + { + public: + DECL_ERROR; + + static Hybridization calculate(Molecule& molecule, int atomIndex); + }; +} diff --git a/core/indigo-core/molecule/src/elements.cpp b/core/indigo-core/molecule/src/elements.cpp index 16fdd85cda..9e440ac5a7 100644 --- a/core/indigo-core/molecule/src/elements.cpp +++ b/core/indigo-core/molecule/src/elements.cpp @@ -1237,3 +1237,75 @@ double Element::_getRelativeIsotopicMass(int element, int isotope) const } throw Error("getRelativeIsotopicMass: isotope (%s, %d) not found", toString(element), isotope); } + +int Element::getNumOuterElectrons(int element) +{ + // clang-format off + constexpr std::array outerElements{ + 0, // Pseudo-element + 1, // H + 2, // H3 + 1, // Li + 2, // Be + 3, // B + 4, // C + 5, // N + 6, // O + 7, // F + 8, // Ne + 1, // Na + 2, // Mg + 3, // Al + 4, // Si + 5, // P + 6, // S + 7, // Cl + 8, // Ar + 1, // K + 2, // Ca + 3, // Sc + 4, // Ti + 5, // V + 6, // Cr + 7, // Mn + 8, // Fe + 9, // Co + 10, // Ni + 1, // Cu + 2, // Zn + 3, // Ga + 4, // Ge + 5, // As + 6, // Se + 7, // Br + 8, // Kr + 1, // Rb + 2, // Sr + 3, // Y + 4, // Zr + 5, // Nb + 6, // Mo + 7, // Tc + 8, // Ru + 9, // Rh + 10, // Pd + 1, // Ag, + 2, // Cd + 3, // In + 4, // Sn + 5, // Sb + 6, // Te + 7, // I + 8, // Xe + 1, // Cs + 2, // Ba + 3, // La + 4 // Ce + }; + // clang-format on + if (element > outerElements.size()) + { + throw Error("outerElements are currently filled only for elements up to lantanoids"); + } + return outerElements[element]; +} diff --git a/core/indigo-core/molecule/src/hybridization.cpp b/core/indigo-core/molecule/src/hybridization.cpp new file mode 100644 index 0000000000..09318131e6 --- /dev/null +++ b/core/indigo-core/molecule/src/hybridization.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +#include "molecule/hybridization.h" + +#include "molecule/elements.h" +#include "molecule/molecule.h" + +namespace +{ + using namespace indigo; + + int getTotalBondsOrder(Molecule& molecule, int atomIndex) + { + const auto& atom = molecule.getVertex(atomIndex); + int order = 0; + for (auto neiIndex = atom.neiBegin(); neiIndex != atom.neiEnd(); neiIndex = atom.neiNext(neiIndex)) + { + order += molecule.getBondOrder(atom.neiEdge(neiIndex)); + } + order += molecule.getImplicitH(atomIndex); + return order; + } + + Hybridization getCarbonHybridization(Molecule& molecule, int atomIndex) + { + const auto numNeighbors = molecule.getVertex(atomIndex).degree() + molecule.getImplicitH(atomIndex); + switch (numNeighbors) + { + case 4: + return Hybridization::SP3; + case 3: + return Hybridization::SP2; + case 2: + case 1: + return Hybridization::SP; + default: + throw HybridizationCalculator::Error("Couldn't calculate C hybridization properly"); + } + } + + bool matchMinusInduction(Molecule& molecule, int atomIndex) + { + const auto& atom = molecule.getVertex(atomIndex); + for (auto neiIndex = atom.neiBegin(); neiIndex != atom.neiEnd(); neiIndex = atom.neiNext(neiIndex)) + { + const auto bondOrder = molecule.getBondOrder(atom.neiEdge(neiIndex)); + if (bondOrder == BOND_DOUBLE || bondOrder == BOND_AROMATIC) + { + return true; + } + } + return false; + } + + bool hasLonePair(Molecule& molecule, int atomIndex, int totalBondsOrder) + { + return Element::getNumOuterElectrons(molecule.getAtomNumber(atomIndex)) - totalBondsOrder >= 2; + } + + int lonePairs(Molecule& molecule, int atomIndex, int totalBondsOrder) + { + const auto numOuterElectrons = Element::getNumOuterElectrons(molecule.getAtomNumber(atomIndex)); + return (numOuterElectrons - totalBondsOrder) / 2; + } + + Hybridization getNitrogenHybridization(Molecule& molecule, int atomIndex, int totalBondsOrder) + { + const auto nOrbs = molecule.getVertex(atomIndex).degree() + static_cast(hasLonePair(molecule, atomIndex, totalBondsOrder)); + bool minusInduction = false; + const auto& atom = molecule.getVertex(atomIndex); + for (auto neiIndex = atom.neiBegin(); neiIndex != atom.neiEnd(); neiIndex = atom.neiNext(neiIndex)) + { + const auto neiVertexIndex = atom.neiVertex(neiIndex); + if (molecule.getAtomNumber(neiVertexIndex) == ELEM_C && matchMinusInduction(molecule, neiVertexIndex)) + { + minusInduction = true; + break; + } + } + if (nOrbs == 4 && minusInduction) + { + return Hybridization(nOrbs - 1); + } + if (nOrbs <= 4) + { + return Hybridization(nOrbs); + } + throw HybridizationCalculator::Error("Couldn't calculate N hybridization properly"); + } + + Hybridization getOxygenHybridization(Molecule& molecule, int atomIndex) + { + const auto& atom = molecule.getVertex(atomIndex); + for (auto neiIndex = atom.neiBegin(); neiIndex < atom.neiEnd(); neiIndex = atom.neiNext(neiIndex)) + { + const auto bondOrder = molecule.getBondOrder(atom.neiEdge(neiIndex)); + const auto neiVertexIndex = atom.neiVertex(neiIndex); + + if (bondOrder == BOND_DOUBLE) + { + return Hybridization::SP2; + } + if (bondOrder == BOND_TRIPLE) + { + // for CO, but some sources do not recognize "sp" for oxygen + return Hybridization::SP; + } + + if (molecule.getAtomNumber(neiVertexIndex) == ELEM_C && matchMinusInduction(molecule, neiVertexIndex)) + { + return Hybridization::SP2; + } + } + return Hybridization::SP3; + } + + Hybridization getComplexHybridization(Molecule& molecule, int atomIndex) + { + const auto& numNeighbors = molecule.getVertex(atomIndex).degree(); + const auto atomNumber = molecule.getAtomNumber(atomIndex); + switch (numNeighbors) + { + case 4: + if (atomNumber == ELEM_Pt || atomNumber == ELEM_Ni || atomNumber == ELEM_Cu || atomNumber == ELEM_Au || atomNumber == ELEM_Pd) + { + return Hybridization::SP2D; + } + return Hybridization::SP3; + case 5: + return Hybridization::SP3D; + case 6: + return Hybridization::SP3D2; + case 7: + return Hybridization::SP3D3; + case 8: + return Hybridization::SP3D4; + default: + throw HybridizationCalculator::Error("Could not calculate %s hybridization properly", Element::toString(atomNumber)); + } + } + + bool isAtomInAromaticRing(Molecule& rawMolecule, const int atomIndex) + { + Molecule molecule; + molecule.clone(rawMolecule); + if (!molecule.isAromatized()) + { + molecule.aromatize({}); + } + return molecule.getAtomAromaticity(atomIndex) == ATOM_AROMATIC; + // const auto& atom = molecule.getVertex(atomIndex); + // int order = 0; + // for (auto neiIndex = atom.neiBegin(); neiIndex != atom.neiEnd(); neiIndex = atom.neiNext(neiIndex)) + // { + // if (molecule.getBondTopology(atom.neiEdge(neiIndex)) == TOPOLOGY_RING + // } + return false; + } +} + +namespace indigo +{ + IMPL_ERROR(HybridizationCalculator, "HybridizationCalculator"); + + Hybridization HybridizationCalculator::calculate(Molecule& molecule, int atomIndex) + { + const auto atomNumber = molecule.getAtomNumber(atomIndex); + if (atomNumber == 0) + { + throw Error("Atomic number is undefined or ambiguous"); + } + if (atomNumber >= 57) + { + throw Error("Hybridization calculation is not implemented for atomic numbers >= 57"); + } + if (atomNumber == ELEM_H) + { + return Hybridization::S; + } + + auto totalBondsOrder = getTotalBondsOrder(molecule, atomIndex); + + if (totalBondsOrder == 0) + { + return Hybridization::S; + } + + if ((atomNumber == ELEM_C || atomNumber == ELEM_N || atomNumber == ELEM_O || atomNumber == ELEM_P || atomNumber == ELEM_S || atomNumber == ELEM_B) && + isAtomInAromaticRing(molecule, atomIndex)) + { + return Hybridization::SP2; + } + + if (atomNumber == ELEM_C) + { + return getCarbonHybridization(molecule, atomIndex); + } + if (atomNumber == ELEM_N) + { + return getNitrogenHybridization(molecule, atomIndex, totalBondsOrder); + } + if (atomNumber == ELEM_O) + { + return getOxygenHybridization(molecule, atomIndex); + } + + if (molecule.getVertex(atomIndex).degree() >= 4) + { + return getComplexHybridization(molecule, atomIndex); + } + + const auto nOrbs = molecule.getVertex(atomIndex).degree() + lonePairs(molecule, atomIndex, totalBondsOrder); + return Hybridization(nOrbs); + } +} diff --git a/core/indigo-core/tests/common.h.in b/core/indigo-core/tests/common.h.in index 96ebc10b66..0a0ff9f32b 100644 --- a/core/indigo-core/tests/common.h.in +++ b/core/indigo-core/tests/common.h.in @@ -8,6 +8,7 @@ #define METHANE "C" #define BENZENE "C1=CC=CC=C1" +#define BENZENE_AROMATIC "c1ccccc1" #define CAFFEINE "CN1C=NC2=C1C(=O)N(C(=O)N2C)C" #define SULFASALAZINE "C1=CC=NC(=C1)NS(=O)(=O)C2=CC=C(C=C2)N=NC3=CC(=C(C=C3)O)C(=O)O" diff --git a/core/indigo-core/tests/tests/molecule.cpp b/core/indigo-core/tests/tests/molecule.cpp index 4e9354b205..0142fb3435 100644 --- a/core/indigo-core/tests/tests/molecule.cpp +++ b/core/indigo-core/tests/tests/molecule.cpp @@ -20,13 +20,16 @@ #include #include +#include #include #include #include #include #include "common.h" +#include "molecule/elements.h" +using namespace std; using namespace indigo; class IndigoCoreMoleculeTest : public IndigoCoreTest @@ -167,3 +170,130 @@ TEST_F(IndigoCoreMoleculeTest, cMolarRefractivity) EXPECT_NEAR(100.73, Crippen::molarRefractivity(molecule), 0.01); } } + +TEST_F(IndigoCoreMoleculeTest, hybridization) +{ + Molecule molecule; + { + loadMolecule(METHANE, molecule); + EXPECT_EQ(Hybridization::SP3, HybridizationCalculator::calculate(molecule, 0)); + } + { + loadMolecule(BENZENE_AROMATIC, molecule); + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(Hybridization::SP2, HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("OC1=CC=CC=C1", molecule); // Phenol + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(Hybridization::SP2, HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("[C-]#[O+]", molecule); // Carbon monoxide + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(Hybridization::SP, HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("O=C=O", molecule); // Carbon monoxide + array expected{Hybridization::SP2, Hybridization::SP, Hybridization::SP2}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("C#N", molecule); + array expected{Hybridization::SP, Hybridization::SP}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("O=C(N)C", molecule); + array expected{Hybridization::SP2, Hybridization::SP2, Hybridization::SP, Hybridization::SP3}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("OS(=O)(=O)O", molecule); + array expected{Hybridization::SP3, Hybridization::SP3, Hybridization::SP2, Hybridization::SP2, Hybridization::SP3}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("N(=O)O", molecule); + array expected{Hybridization::SP2, Hybridization::SP2, Hybridization::SP3}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("N(=O)O", molecule); + array expected{Hybridization::SP2, Hybridization::SP2, Hybridization::SP3}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("O=[Xe](=O)(=O)=O", molecule); + array expected{Hybridization::SP2, Hybridization::SP3, Hybridization::SP2, Hybridization::SP2, Hybridization::SP2}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("FS(F)(F)(F)(F)F", molecule); + array expected{Hybridization::SP3, Hybridization::SP3D2, Hybridization::SP3, Hybridization::SP3, + Hybridization::SP3, Hybridization::SP3, Hybridization::SP3}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("FS(F)(F)(F)(F)F", molecule); + array expected{Hybridization::SP3, Hybridization::SP3D2, Hybridization::SP3, Hybridization::SP3, + Hybridization::SP3, Hybridization::SP3, Hybridization::SP3}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("FBr(F)F", molecule); + array expected{Hybridization::SP3, Hybridization::SP3D, Hybridization::SP3, Hybridization::SP3}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("[Be](Cl)Cl", molecule); + array expected{Hybridization::SP, Hybridization::SP3, Hybridization::SP3}; + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(expected[i], HybridizationCalculator::calculate(molecule, i)); + } + } + { + loadMolecule("C1=CC=CS1", molecule); // Thiophene + for (auto i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) + { + EXPECT_EQ(Hybridization::SP2, HybridizationCalculator::calculate(molecule, i)); + } + } +}