1 #include <openbabel/stereo/cistrans.h> 2 #include <openbabel/mol.h> 3 #include <openbabel/atom.h> 4 #include <openbabel/oberror.h> 5 6 using namespace std; 7 8 namespace OpenBabel { 9 10 // 11 // OBCisTransStereo::Config struct 12 // 13 operator ==(const Config & other) const14 bool OBCisTransStereo::Config::operator==(const Config &other) const 15 { 16 if ((begin != other.begin) && (begin != other.end)) 17 return false; 18 if ((end != other.begin) && (end != other.end)) 19 return false; 20 if ((refs.size() != 4) || (other.refs.size() != 4)) 21 return false; 22 23 Config u1, u2; 24 if (!OBStereo::ContainsSameRefs(refs, other.refs)) { 25 // find a ref that occurs in both 26 for (OBStereo::ConstRefIter i = refs.begin(); i != refs.end(); ++i) 27 if (OBStereo::ContainsRef(other.refs, *i)) { 28 u1 = OBTetraPlanarStereo::ToConfig(*this, *i, OBStereo::ShapeU); // refs[0] = u1.refs[0] 29 u2 = OBTetraPlanarStereo::ToConfig(other, *i, OBStereo::ShapeU); // refs[0] = u2.refs[0] 30 } 31 32 // check if they actualy share an id... 33 if (u1.refs.empty()) 34 return false; 35 } else { 36 // normalize the other Config struct 37 u1 = OBTetraPlanarStereo::ToConfig(*this, refs.at(0), OBStereo::ShapeU); // refs[0] = u1.refs[0] 38 u2 = OBTetraPlanarStereo::ToConfig(other, refs.at(0), OBStereo::ShapeU); // refs[0] = u2.refs[0] 39 // both now start with the same ref 40 // 41 // 2 possiblilities: 42 // 43 // 1 2 3 4 1 2 3 4 44 // | | | | <- in any case, refs[0] & refs[2] remain unchanged 45 // 1 2 3 4 1 4 3 2 46 // 47 return (u1.refs[2] == u2.refs[2]); 48 } 49 50 // possibilities: 51 // 52 // 1 2 3 4 53 // | | <- refs[0] & refs[2] remain unchanged 54 // 1 H 3 H 55 // 56 // 1 2 3 4 57 // | | <- refs[0] & refs[3] remain unchanged 58 // 1 H H 4 59 // 60 // 1 2 3 4 61 // | | <- refs[0] & refs[1] remain unchanged 62 // 1 2 H H 63 if ((u1.refs[2] == OBStereo::ImplicitRef) || (u2.refs[2] == OBStereo::ImplicitRef)) { 64 // 1 2 H 4 65 if ((u1.refs[3] == OBStereo::ImplicitRef) || (u2.refs[3] == OBStereo::ImplicitRef)) { 66 return (u1.refs[1] == u2.refs[1]); // 1 2 H H 67 } else { 68 return (u1.refs[3] == u2.refs[3]); // 1 H H 4 69 } 70 } else 71 return (u1.refs[2] == u2.refs[2]); // 1 2 3 4 & 1 H 3 4 & 1 2 3 H 72 73 return false; 74 } 75 76 // 77 // OBCisTransStereo class 78 // 79 OBCisTransStereo(OBMol * mol)80 OBCisTransStereo::OBCisTransStereo(OBMol *mol) : OBTetraPlanarStereo(mol) 81 { 82 } 83 ~OBCisTransStereo()84 OBCisTransStereo::~OBCisTransStereo() 85 { 86 } 87 IsValid() const88 bool OBCisTransStereo::IsValid() const 89 { 90 if ((m_cfg.begin == OBStereo::NoRef) || (m_cfg.end == OBStereo::NoRef)) 91 return false; 92 if (m_cfg.refs.size() != 4) 93 return false; 94 return true; 95 } 96 SetConfig(const Config & config)97 void OBCisTransStereo::SetConfig(const Config &config) 98 { 99 if (config.begin == OBStereo::NoRef) { 100 obErrorLog.ThrowError(__FUNCTION__, 101 "OBCisTransStereo::SetConfig : double bond begin id is invalid.", obError); 102 m_cfg = Config(); 103 return; 104 } 105 if (config.end == OBStereo::NoRef) { 106 obErrorLog.ThrowError(__FUNCTION__, 107 "OBCisTransStereo::SetConfig : double bond end id is invalid.", obError); 108 m_cfg = Config(); 109 return; 110 } 111 if (config.refs.size() != 4) { 112 std::stringstream ss; 113 ss << "OBCisTransStereo::SetConfig : found " << config.refs.size(); 114 ss << " reference ids, should be 4."; 115 obErrorLog.ThrowError(__FUNCTION__, ss.str(), obError); 116 m_cfg = Config(); 117 return; 118 } 119 120 // store using U shape 121 m_cfg = OBTetraPlanarStereo::ToConfig(config, config.refs.at(0), OBStereo::ShapeU); 122 } 123 GetConfig(OBStereo::Shape shape) const124 OBCisTransStereo::Config OBCisTransStereo::GetConfig(OBStereo::Shape shape) const 125 { 126 if (!IsValid()) 127 return Config(); 128 129 return OBTetraPlanarStereo::ToConfig(m_cfg, m_cfg.refs.at(0), shape); 130 } 131 GetConfig(unsigned long start,OBStereo::Shape shape) const132 OBCisTransStereo::Config OBCisTransStereo::GetConfig(unsigned long start, 133 OBStereo::Shape shape) const 134 { 135 if (!IsValid()) 136 return Config(); 137 138 return OBTetraPlanarStereo::ToConfig(m_cfg, start, shape); 139 } 140 IsTrans(unsigned long id1,unsigned long id2) const141 bool OBCisTransStereo::IsTrans(unsigned long id1, unsigned long id2) const 142 { 143 return (GetTransRef(id1) == id2); 144 } 145 IsCis(unsigned long id1,unsigned long id2) const146 bool OBCisTransStereo::IsCis(unsigned long id1, unsigned long id2) const 147 { 148 return (GetCisRef(id1) == id2); 149 } 150 operator ==(const OBCisTransStereo & other) const151 bool OBCisTransStereo::operator==(const OBCisTransStereo &other) const 152 { 153 if (!IsValid() || !other.IsValid()) 154 return false; 155 156 Config u = OBTetraPlanarStereo::ToConfig(other.GetConfig(), 157 m_cfg.refs.at(0), OBStereo::ShapeU); 158 unsigned long a1 = u.refs.at(0); 159 unsigned long b1 = u.refs.at(2); 160 161 if ((a1 == OBStereo::ImplicitRef) && (b1 == OBStereo::ImplicitRef)) { 162 a1 = u.refs.at(1); 163 b1 = u.refs.at(3); 164 } 165 166 if (b1 != OBStereo::ImplicitRef) 167 if (a1 == GetTransRef(b1)) 168 return true; 169 if (a1 != OBStereo::ImplicitRef) 170 if (b1 == GetTransRef(a1)) 171 return true; 172 173 return false; 174 } 175 GetCisOrTransRef(unsigned long id,bool getcisref) const176 unsigned long OBCisTransStereo::GetCisOrTransRef(unsigned long id, bool getcisref) const 177 { 178 if (!IsValid()) 179 return OBStereo::NoRef; 180 181 if (id == OBStereo::ImplicitRef) 182 return OBStereo::NoRef; 183 184 // find id 185 for (int i = 0; i < 4; ++i) { 186 if (m_cfg.refs.at(i) == id) { 187 // Use its index to find the index of the cis (or trans) atom 188 int j; 189 if (getcisref) // GetCisRef 190 j = 3 - i; // Convert 0 to 3, and 3 to 0 191 else // GetTransRef 192 j = (i > 1) ? i - 2 : i + 2; 193 194 unsigned long refId = m_cfg.refs.at(j); 195 return refId; 196 } 197 } 198 199 // id not found 200 return OBStereo::NoRef; 201 } 202 GetTransRef(unsigned long id) const203 unsigned long OBCisTransStereo::GetTransRef(unsigned long id) const 204 { 205 return GetCisOrTransRef(id, false); 206 } 207 GetCisRef(unsigned long id) const208 unsigned long OBCisTransStereo::GetCisRef(unsigned long id) const 209 { 210 return GetCisOrTransRef(id, true); 211 } 212 IsOnSameAtom(unsigned long id1,unsigned long id2) const213 bool OBCisTransStereo::IsOnSameAtom(unsigned long id1, unsigned long id2) const 214 { 215 const OBMol *mol = GetMolecule(); 216 if (!mol) { 217 obErrorLog.ThrowError(__FUNCTION__, "OBCisTransStereo::IsOnSameAtom : No valid molecule set", obError); 218 return false; 219 } 220 221 OBAtom *begin = mol->GetAtomById(m_cfg.begin); 222 if (!begin) { 223 obErrorLog.ThrowError(__FUNCTION__, "OBCisTransStereo::IsOnSameAtom : Begin reference id is not valid.", obError); 224 return false; 225 } 226 OBAtom *end = mol->GetAtomById(m_cfg.end); 227 if (!end) { 228 obErrorLog.ThrowError(__FUNCTION__, "OBCisTransStereo::IsOnSameAtom : End reference id is not valid.", obError); 229 return false; 230 } 231 232 OBAtom *a = mol->GetAtomById(id1); 233 OBAtom *b = mol->GetAtomById(id2); 234 235 if (a && b) { 236 // both on begin atom? 237 if (a->IsConnected(begin) && b->IsConnected(begin)) 238 return true; 239 // both on end atom? 240 if (a->IsConnected(end) && b->IsConnected(end)) 241 return true; 242 return false; 243 } else { 244 if (a) { 245 // b atom not found, could be a deleted hydrogen... 246 if (a->IsConnected(begin)) { 247 // a is connected to begin. if this is the atom missing a hydrogen, return false 248 if (begin->GetExplicitDegree() == 2) 249 return true; 250 // check if the end atom really is missing an atom 251 if (end->GetExplicitDegree() != 2) { 252 obErrorLog.ThrowError(__FUNCTION__, 253 "OBCisTransStereo::IsOnSameAtom : id2 is not valid and is not a missing hydrogen.", obError); 254 return false; 255 } 256 // inform user we are treating id2 as deleted hydrogen 257 obErrorLog.ThrowError(__FUNCTION__, 258 "OBCisTransStereo::IsOnSameAtom : Atom with id2 doesn't exist anymore, must be a (deleted) hydrogen.", obInfo); 259 } else if (a->IsConnected(end)) { 260 // a is connected to end. again, if this is the atom missing a hydrogen, return false 261 if (end->GetExplicitDegree() == 2) 262 return true; 263 // check if the begin atom really is missing an atom 264 if (begin->GetExplicitDegree() != 2) { 265 obErrorLog.ThrowError(__FUNCTION__, 266 "OBCisTransStereo::IsOnSameAtom : id2 is not valid and is not a missing hydrogen.", obError); 267 return true; 268 } 269 // inform user we are treating id2 as deleted hydrogen 270 obErrorLog.ThrowError(__FUNCTION__, 271 "OBCisTransStereo::IsOnSameAtom : Atom with id2 doesn't exist, must be a (deleted) hydrogen.", obInfo); 272 273 } else { 274 obErrorLog.ThrowError(__FUNCTION__, 275 "OBCisTransStereo::IsOnSameAtom : Atom with id1 isn't connected to the begin or end atom.", obError); 276 return true; 277 } 278 } else if (b) { 279 // a atom not found, could be a deleted hydrogen... 280 if (b->IsConnected(begin)) { 281 // b is connected to begin. if this is the atom missing a hydrogen, return false 282 if (begin->GetExplicitDegree() == 2) 283 return true; 284 // check if the end atom really is missing an atom 285 if (end->GetExplicitDegree() != 2) { 286 obErrorLog.ThrowError(__FUNCTION__, 287 "OBCisTransStereo::IsOnSameAtom : id1 is not valid and is not a missing hydrogen.", obError); 288 return true; 289 } 290 // inform user we are treating id1 as deleted hydrogen 291 obErrorLog.ThrowError(__FUNCTION__, 292 "OBCisTransStereo::IsOnSameAtom : Atom with id1 doesn't exist, must be a (deleted) hydrogen.", obInfo); 293 } else if (b->IsConnected(end)) { 294 // a is connected to end. again, if this is the atom missing a hydrogen, return false 295 if (end->GetExplicitDegree() == 2) 296 return true; 297 // check if the begin atom really is missing an atom 298 if (begin->GetExplicitDegree() != 2) { 299 obErrorLog.ThrowError(__FUNCTION__, 300 "OBCisTransStereo::IsOnSameAtom : id1 is not valid and is not a missing hydrogen.", obError); 301 return true; 302 } 303 // inform user we are treating id2 as deleted hydrogen 304 obErrorLog.ThrowError(__FUNCTION__, 305 "OBCisTransStereo::IsOnSameAtom : Atom with id1 doesn't exist, must be a (deleted) hydrogen.", obInfo); 306 } else { 307 obErrorLog.ThrowError(__FUNCTION__, 308 "OBCisTransStereo::IsOnSameAtom : Atom with id1 isn't connected to the begin or end atom.", obError); 309 return true; 310 } 311 } else { 312 OBAtom *c = nullptr, *d = nullptr; 313 // no a & b, check the remaining ids which will reveal same info 314 for (int i = 0; i < 4; ++i) { 315 if ((m_cfg.refs.at(i) == id1) || (m_cfg.refs.at(i) == id2)) 316 continue; 317 if (!c) { 318 c = mol->GetAtomById(m_cfg.refs.at(i)); 319 } else { 320 d = mol->GetAtomById(m_cfg.refs.at(i)); 321 } 322 } 323 if (!c || !d) { 324 obErrorLog.ThrowError(__FUNCTION__, "OBCisTransStereo::IsOnSameAtom : invalid stereochemistry!", obError); 325 return true; 326 } 327 if ((begin->GetExplicitDegree() != 2) || (end->GetExplicitDegree() != 2)) { 328 obErrorLog.ThrowError(__FUNCTION__, "OBCisTransStereo::IsOnSameAtom : invalid stereochemistry!", obError); 329 return true; 330 } 331 obErrorLog.ThrowError(__FUNCTION__, 332 "OBCisTransStereo::IsOnSameAtom : Atoms with id1 & id2 don't exist, must be a (deleted) hydrogens.", obInfo); 333 return IsOnSameAtom(c->GetId(), d->GetId()); 334 } 335 } 336 337 return false; 338 } 339 Clone(OBBase * mol) const340 OBGenericData* OBCisTransStereo::Clone(OBBase *mol) const 341 { 342 OBCisTransStereo *data = new OBCisTransStereo(static_cast<OBMol*>(mol)); 343 data->SetConfig(m_cfg); 344 return data; 345 } 346 347 } // namespace OpenBabel 348 349 namespace std { 350 operator <<(ostream & out,const OpenBabel::OBCisTransStereo & ct)351 ostream& operator<<(ostream &out, const OpenBabel::OBCisTransStereo &ct) 352 { 353 OpenBabel::OBCisTransStereo::Config cfg = ct.GetConfig(); 354 out << "OBCisTransStereo(begin = " << cfg.begin; 355 out << ", end = " << cfg.end; 356 357 out << ", refs = "; 358 for (OpenBabel::OBStereo::Refs::iterator i = cfg.refs.begin(); i != cfg.refs.end(); ++i) 359 if (*i != OpenBabel::OBStereo::ImplicitRef) 360 out << *i << " "; 361 else 362 out << "H "; 363 364 switch (cfg.shape) { 365 case OpenBabel::OBStereo::ShapeU: 366 out << ", shape = U)"; 367 break; 368 case OpenBabel::OBStereo::ShapeZ: 369 out << ", shape = Z)"; 370 break; 371 case OpenBabel::OBStereo::Shape4: 372 out << ", shape = 4)"; 373 break; 374 } 375 376 return out; 377 } 378 operator <<(ostream & out,const OpenBabel::OBCisTransStereo::Config & cfg)379 ostream& operator<<(ostream &out, const OpenBabel::OBCisTransStereo::Config &cfg) 380 { 381 out << "OBCisTransStereo::Config(begin = " << cfg.begin; 382 out << ", end = " << cfg.end; 383 384 out << ", refs = "; 385 for (OpenBabel::OBStereo::Refs::const_iterator i = cfg.refs.begin(); i != cfg.refs.end(); ++i) 386 if (*i != OpenBabel::OBStereo::ImplicitRef) 387 out << *i << " "; 388 else 389 out << "H "; 390 391 switch (cfg.shape) { 392 case OpenBabel::OBStereo::ShapeU: 393 out << ", shape = U)"; 394 break; 395 case OpenBabel::OBStereo::ShapeZ: 396 out << ", shape = Z)"; 397 break; 398 case OpenBabel::OBStereo::Shape4: 399 out << ", shape = 4)"; 400 break; 401 } 402 403 return out; 404 } 405 406 } // namespace std 407 408