MayaChemTools

   1 package Bond;
   2 #
   3 # $RCSfile: Bond.pm,v $
   4 # $Date: 2008/04/25 00:00:45 $
   5 # $Revision: 1.19 $
   6 #
   7 # Author: Manish Sud <msud@san.rr.com>
   8 #
   9 # Copyright (C) 2004-2008 Manish Sud. All rights reserved.
  10 #
  11 # This file is part of MayaChemTools.
  12 #
  13 # MayaChemTools is free software; you can redistribute it and/or modify it under
  14 # the terms of the GNU Lesser General Public License as published by the Free
  15 # Software Foundation; either version 3 of the License, or (at your option) any
  16 # later version.
  17 #
  18 # MayaChemTools is distributed in the hope that it will be useful, but without
  19 # any warranty; without even the implied warranty of merchantability of fitness
  20 # for a particular purpose.  See the GNU Lesser General Public License for more
  21 # details.
  22 #
  23 # You should have received a copy of the GNU Lesser General Public License
  24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
  25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
  26 # Boston, MA, 02111-1307, USA.
  27 #
  28 use 5.006;
  29 use strict;
  30 use Carp;
  31 use Exporter;
  32 use Storable ();
  33 use ObjectProperty;
  34 
  35 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  36 
  37 $VERSION = '1.00';
  38 @ISA = qw(ObjectProperty Exporter);
  39 @EXPORT = qw();
  40 @EXPORT_OK = qw();
  41 
  42 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
  43 
  44 # Setup class variables...
  45 my($ClassName, $ObjectID);
  46 _InitializeClass();
  47 
  48 # Overload Perl functions...
  49 use overload '""' => 'StringifyBond';
  50 
  51 # Class constructor...
  52 sub new {
  53   my($Class, %NamesAndValues) = @_;
  54 
  55   # Initialize object...
  56   my $This = {};
  57   bless $This, ref($Class) || $Class;
  58   $This->_InitializeBond();
  59 
  60   $This->_InitializeBondProperties(%NamesAndValues);
  61 
  62   return $This;
  63 }
  64 
  65 # Initialize object data...
  66 #
  67 sub _InitializeBond {
  68   my($This) = @_;
  69   my($ObjectID) = _GetNewObjectID();
  70 
  71   # All other property names and values along with all Set/Get<PropertyName> methods
  72   # are implemented on-demand using ObjectProperty class.
  73   $This->{ID} = $ObjectID;
  74   @{$This->{Atoms}} = ();
  75   $This->{BondOrder} = '';
  76   $This->{BondType} = '';
  77 }
  78 
  79 # Initialize class ...
  80 sub _InitializeClass {
  81   #Class name...
  82   $ClassName = __PACKAGE__;
  83 
  84   # ID to keep track of objects...
  85   $ObjectID = 0;
  86 }
  87 
  88 # Initialize bond properties...
  89 sub _InitializeBondProperties {
  90   my($This, %NamesAndValues) = @_;
  91 
  92   my($Name, $Value, $MethodName);
  93   while (($Name, $Value) = each  %NamesAndValues) {
  94     $MethodName = "Set${Name}";
  95     $This->$MethodName($Value);
  96   }
  97 
  98   if (!exists $NamesAndValues{'Atoms'}) {
  99     carp "Warning: ${ClassName}->new: Bond object instantiated without specifying atoms list...";
 100   }
 101   if (!exists $NamesAndValues{'BondOrder'}) {
 102     carp "Warning: ${ClassName}->new: Bond object instantiated without setting bond order...";
 103   }
 104   return $This;
 105 }
 106 
 107 # Setup an explicit SetID method to block setting of ID by AUTOLOAD function...
 108 sub SetID {
 109   my($This, $Value) = @_;
 110 
 111   carp "Warning: ${ClassName}->SetID: Object ID can't be changed: it's used for internal tracking...";
 112 
 113   return $This;
 114 }
 115 
 116 # Set bond atoms...
 117 sub SetAtoms {
 118   my($This, @Values) = @_;
 119 
 120   if (!@Values) {
 121     croak "Error: ${ClassName}->SetAtoms: No atoms specified...";
 122   }
 123 
 124   my($FirstValue, $TypeOfFirstValue, $Atom1, $Atom2, $AtomID1, $AtomID2);
 125   $FirstValue = $Values[0];
 126   $TypeOfFirstValue = ref $FirstValue;
 127 
 128   if ($TypeOfFirstValue =~ /^ARRAY/) {
 129     # Initialize using array refernce...
 130     if (@{$FirstValue} != 2) {
 131       croak "Warning: ${ClassName}->SetAtoms: Number of atoms specified in bond object is not equal to 2...";
 132     }
 133     ($Atom1, $Atom2) = @{$FirstValue};
 134   }
 135   else {
 136     # It's a list of values...
 137     if (@Values != 2) {
 138       croak "Warning: ${ClassName}->SetAtoms: Number of atoms specified in bond object is not equal to 2...";
 139     }
 140     ($Atom1, $Atom2) = @Values;
 141     push @{$This->{Atoms}}, @Values;
 142   }
 143 
 144   $AtomID1 = $Atom1->GetID(); $AtomID2 = $Atom2->GetID();
 145   if ($AtomID1 == $AtomID2) {
 146       croak "Warning: ${ClassName}->SetAtoms: Can't specify same atoms to create a bond...";
 147   }
 148 
 149   if ($AtomID1 < $AtomID2) {
 150     push @{$This->{Atoms}}, ($Atom1, $Atom2);
 151   }
 152   else {
 153     push @{$This->{Atoms}}, ($Atom2, $Atom1);
 154   }
 155 
 156   return $This;
 157 }
 158 
 159 # Get bond atoms as array. In scalar context, return number of atoms involved in
 160 # bond...
 161 #
 162 sub GetAtoms {
 163   my($This) = @_;
 164 
 165   return wantarray ? @{$This->{Atoms}} : scalar @{$This->{Atoms}};
 166 }
 167 
 168 # Get atom bonded to specified atom...
 169 sub GetBondedAtom {
 170   my($This, $Atom) = @_;
 171   my($Atom1, $Atom2, $AtomID1, $AtomID2, $AtomID);
 172 
 173   ($Atom1, $Atom2) = $This->GetAtoms();
 174   $AtomID1 = $Atom1->GetID(); $AtomID2 = $Atom2->GetID(); $AtomID = $Atom->GetID();
 175 
 176   return ($AtomID1 == $AtomID) ? $Atom2 : (($AtomID2 == $AtomID) ? $Atom1 : undef) ;
 177 }
 178 
 179 # Get common atom between two bonds...
 180 sub GetCommonAtom {
 181   my($This, $Other) = @_;
 182   my($Atom1, $Atom2, $AtomID1, $AtomID2, $OtherAtom1, $OtherAtom2, $OtherAtomID1, $OtherAtomID2);
 183 
 184   ($Atom1, $Atom2) = $This->GetAtoms();
 185   $AtomID1 = $Atom1->GetID(); $AtomID2 = $Atom2->GetID();
 186 
 187   ($OtherAtom1, $OtherAtom2) = $Other->GetAtoms();
 188   $OtherAtomID1 = $OtherAtom1->GetID(); $OtherAtomID2 = $OtherAtom2->GetID();
 189 
 190   return ($AtomID1 == $OtherAtomID1 || $AtomID1 == $OtherAtomID2) ? $Atom1 : (($AtomID2 == $OtherAtomID1 || $AtomID2 == $OtherAtomID2) ? $Atom2 : undef) ;
 191 }
 192 
 193 # Set bond order...
 194 #
 195 # Possible BondOrder are: 1 = Single, 1.5 = Atomatic, 2 = Double, 3 = Triple, 4 = Quadruple.
 196 #
 197 # Possible BondType for different BondOrders are:
 198 #
 199 # 1 : Single, SingleWedge (Up), SingleHash (Down), SingleWavy, Dative
 200 # 2 : Double, DoubleCross (Cis/Trans)
 201 # 3 : Triple
 202 # 4 : Quadruple
 203 # 1.5 : Aromatic (Single/Double), Tautomeric(Single/Double)
 204 #
 205 # Notes:
 206 #   . BondType property is automatially assiged using default BondType values for
 207 #     specified BondOrder.
 208 #   . BondType values can also be explicit set.
 209 #   . To make bonds aromatic in a ring, explicitly set "Aromatic" property for bond/atoms and make sure
 210 #     apporpriate BondOrder values are assgined.
 211 #   . Dative bond types are treated as single bond types with explicit formal charge of + and - on first
 212 #     and second bond atoms.
 213 #
 214 sub SetBondOrder {
 215   my($This, $BondOrder) = @_;
 216 
 217   if ($BondOrder !~ /^(1|1.5|2|3|4)$/) {
 218     croak "Error: ${ClassName}->SetBondOrder: BondOrder value $BondOrder is not valid. Supported values: 1, 1.5, 2, 3, 4..";
 219   }
 220 
 221   # Set bond order and type...
 222   my($BondType);
 223 
 224   BONDORDER: {
 225       if ($BondOrder == 1) {$BondType = 'Single'; last BONDORDER; }
 226       if ($BondOrder == 1.5) {$BondType = 'Aromatic'; last BONDORDER; }
 227       if ($BondOrder == 2) {$BondType = 'Double'; last BONDORDER; }
 228       if ($BondOrder == 3) {$BondType = 'Triple'; last BONDORDER; }
 229       if ($BondOrder == 4) {$BondType = 'Quadruple'; last BONDORDER; }
 230       $BondType = '';
 231       $BondOrder = '';
 232   }
 233   $This->{BondType} = $BondType;
 234   $This->{BondOrder} = $BondOrder;
 235 
 236   return $This;
 237 
 238 }
 239 
 240 # Set bond type for a specific bond...
 241 #
 242 sub SetBondType {
 243   my($This, $BondType) = @_;
 244 
 245   if ($BondType !~ /^(Single|SingleWedge|SingleUp|SingleHash|SingleDown|SingleWavy|Dative|Double|DoubleCross|Aromatic|Tautomeric|Triple|Quadruple)$/i) {
 246     croak "Error: ${ClassName}->SetBondType: BondType value $BondType is not valid. Supported values: Single, SingleWedge, SingleUp, SingleHash, SingleDown, SingleWavy, Dative, Double, DoubleCross, Aromatic, Tautomeric, Triple, Quadruple...";
 247   }
 248 
 249   # Make sure its a valid BondType value for BondOrder...
 250   my($BondOrder, $ValidBondType);
 251 
 252   $ValidBondType = 0;
 253   $BondOrder = $This->{BondOrder};
 254   if (!$BondOrder) {
 255     croak "Error: ${ClassName}->SetBondType: BondOrder is empty: It must be set before BondOrder...";
 256   }
 257 
 258   BONDORDER: {
 259       if ($BondOrder == 1 && $BondType =~ /^(Single|SingleWedge|SingleUp|SingleHash|SingleDown|SingleWavy|Dative|Aromatic|Tautomeric)$/i) {$ValidBondType = 1; last BONDORDER; }
 260       if ($BondOrder == 1.5 && $BondType =~ /^(Aromatic|Tautomeric)$/i) {$ValidBondType = 1; last BONDORDER; }
 261       if ($BondOrder == 2 && $BondType =~ /^(Double|DoubleCross|Aromatic|Tautomeric)$/i ) {$ValidBondType = 1; last BONDORDER; }
 262       if ($BondOrder == 3 && $BondType =~ /^(Triple)$/i) {$ValidBondType = 1; last BONDORDER; }
 263       if ($BondOrder == 4 && $BondType =~ /^(Quadruple)$/i) {$ValidBondType = 1; last BONDORDER; }
 264       $ValidBondType = 0;
 265   }
 266 
 267   if (!$ValidBondType) {
 268     croak "Error: ${ClassName}->SetBondType: BondType value, $BondType, is not valid for BondOrder $BondOrder...";
 269   }
 270 
 271   $This->{BondType} = $BondType;
 272 
 273   # Set explicit formal charge for atoms involved in Dative bonds...
 274   if ($BondType =~ /^Dative$/i) {
 275     my($Atom1, $Atom2) = $This->GetAtoms();
 276     $Atom1->SetFormalCharge('1');
 277     $Atom2->SetFormalCharge('-1');
 278   }
 279 
 280   return $This;
 281 }
 282 
 283 # Set bond stereochemistry...
 284 #
 285 # Supported values are:
 286 #
 287 # Z, cis: Highest ranking group using CIP priority scheme on the same side of double bond
 288 # E, trans: Highest ranking group using CIP priority scheme on the same side of double bond
 289 #
 290 sub SetStereochemistry {
 291   my($This, $Stereochemistry) = @_;
 292 
 293   if ($Stereochemistry !~ /^(Z|cis|E|trans)$/i) {
 294     croak "Error: ${ClassName}->SetStereochemistry: Stereochemistry value $Stereochemistry is not valid. Supported values: Z, cis, E, trans..";
 295   }
 296   my($BondOrder);
 297   $BondOrder = $This->{BondOrder};
 298 
 299   if ($BondOrder != 2) {
 300     croak "Error: ${ClassName}->SetStereochemistry: Stereochemistry value, $Stereochemistry,  can't be assigned to bond with  BondOrder value of $BondOrder: It's only valid for double bonds ...";
 301   }
 302 
 303   $This->SetProperty('Stereochemistry', $Stereochemistry);
 304 
 305   return $This;
 306 }
 307 
 308 # Is aromatic property set for the bond?
 309 sub IsAromatic {
 310   my($This) = @_;
 311   my($Aromatic);
 312 
 313   $Aromatic = $This->GetAromatic();
 314 
 315   return (defined($Aromatic) && $Aromatic) ? 1 : 0;
 316 }
 317 
 318 # Delete bond...
 319 sub DeleteBond {
 320   my($This) = @_;
 321 
 322   # Is this atom in a molecule?
 323   if (!$This->HasProperty('Molecule')) {
 324     # Nothing to do...
 325     return $This;
 326   }
 327   my($Molecule);
 328   $Molecule = $This->GetProperty('Molecule');
 329   $Molecule->DeleteBond($This);
 330 
 331   return $This;
 332 }
 333 
 334 # Copy bond and all its associated data...
 335 sub Copy {
 336   my($This) = @_;
 337   my($Bond);
 338 
 339   $Bond = Storable::dclone($This);
 340 
 341   return $Bond;
 342 }
 343 
 344 # Is bond in a ring?
 345 #
 346 sub IsInRing {
 347   my($This) = @_;
 348 
 349   # Is this bond in a molecule?
 350   if (!$This->HasProperty('Molecule')) {
 351     return undef;
 352   }
 353   my($Molecule);
 354   $Molecule = $This->GetProperty('Molecule');
 355 
 356   return $Molecule->_IsBondInRing($This);
 357 }
 358 
 359 # Is bond not in a ring?
 360 #
 361 sub IsNotInRing {
 362   my($This) = @_;
 363 
 364   # Is this bond in a molecule?
 365   if (!$This->HasProperty('Molecule')) {
 366     return undef;
 367   }
 368   my($Molecule);
 369   $Molecule = $This->GetProperty('Molecule');
 370 
 371   return $Molecule->_IsBondNotInRing($This);
 372 }
 373 
 374 # Is bond only in one ring?
 375 #
 376 sub IsOnlyInOneRing {
 377   my($This) = @_;
 378 
 379   # Is this bond in a molecule?
 380   if (!$This->HasProperty('Molecule')) {
 381     return undef;
 382   }
 383   my($Molecule);
 384   $Molecule = $This->GetProperty('Molecule');
 385 
 386   return $Molecule->_IsBondInOnlyOneRing($This);
 387 }
 388 
 389 # Is bond in a ring of specific size?
 390 #
 391 sub IsInRingOfSize {
 392   my($This, $RingSize) = @_;
 393 
 394   # Is this bond in a molecule?
 395   if (!$This->HasProperty('Molecule')) {
 396     return undef;
 397   }
 398   my($Molecule);
 399   $Molecule = $This->GetProperty('Molecule');
 400 
 401   return $Molecule->_IsBondInRingOfSize($This, $RingSize);
 402 }
 403 
 404 # Get size of smallest ring containing the bond...
 405 #
 406 sub GetSizeOfSmallestRing {
 407   my($This) = @_;
 408 
 409   # Is this bond in a molecule?
 410   if (!$This->HasProperty('Molecule')) {
 411     return undef;
 412   }
 413   my($Molecule);
 414   $Molecule = $This->GetProperty('Molecule');
 415 
 416   return $Molecule->_GetSizeOfSmallestBondRing($This);
 417 }
 418 
 419 # Get size of largest ring containing the bond...
 420 #
 421 sub GetSizeOfLargestRing {
 422   my($This) = @_;
 423 
 424   # Is this bond in a molecule?
 425   if (!$This->HasProperty('Molecule')) {
 426     return undef;
 427   }
 428   my($Molecule);
 429   $Molecule = $This->GetProperty('Molecule');
 430 
 431   return $Molecule->_GetSizeOfLargestBondRing($This);
 432 }
 433 
 434 # Get number of  rings containing the bond...
 435 #
 436 sub GetNumOfRings {
 437   my($This) = @_;
 438 
 439   # Is this bond in a molecule?
 440   if (!$This->HasProperty('Molecule')) {
 441     return undef;
 442   }
 443   my($Molecule);
 444   $Molecule = $This->GetProperty('Molecule');
 445 
 446   return $Molecule->_GetNumOfBondRings($This);
 447 }
 448 
 449 # Get number of  rings with odd size containing the bond...
 450 #
 451 sub GetNumOfRingsWithOddSize {
 452   my($This) = @_;
 453 
 454   # Is this bond in a molecule?
 455   if (!$This->HasProperty('Molecule')) {
 456     return undef;
 457   }
 458   my($Molecule);
 459   $Molecule = $This->GetProperty('Molecule');
 460 
 461   return $Molecule->_GetNumOfBondRingsWithOddSize($This);
 462 }
 463 
 464 # Get number of  rings with even size containing the bond...
 465 #
 466 sub GetNumOfRingsWithEvenSize {
 467   my($This) = @_;
 468 
 469   # Is this bond in a molecule?
 470   if (!$This->HasProperty('Molecule')) {
 471     return undef;
 472   }
 473   my($Molecule);
 474   $Molecule = $This->GetProperty('Molecule');
 475 
 476   return $Molecule->_GetNumOfBondRingsWithEvenSize($This);
 477 }
 478 
 479 # Get number of  rings with specified size containing the bond...
 480 #
 481 sub GetNumOfRingsWithSize {
 482   my($This, $RingSize) = @_;
 483 
 484   # Is this bond in a molecule?
 485   if (!$This->HasProperty('Molecule')) {
 486     return undef;
 487   }
 488   my($Molecule);
 489   $Molecule = $This->GetProperty('Molecule');
 490 
 491   return $Molecule->_GetNumOfBondRingsWithSize($This, $RingSize);
 492 }
 493 
 494 # Get number of  rings with size less than specified containing the bond...
 495 #
 496 sub GetNumOfRingsWithSizeLessThan {
 497   my($This, $RingSize) = @_;
 498 
 499   # Is this bond in a molecule?
 500   if (!$This->HasProperty('Molecule')) {
 501     return undef;
 502   }
 503   my($Molecule);
 504   $Molecule = $This->GetProperty('Molecule');
 505 
 506   return $Molecule->_GetNumOfBondRingsWithSizeLessThan($This, $RingSize);
 507 }
 508 
 509 # Get number of  rings with size greater than specified size containing the bond...
 510 #
 511 sub GetNumOfRingsWithSizeGreaterThan {
 512   my($This, $RingSize) = @_;
 513 
 514   # Is this bond in a molecule?
 515   if (!$This->HasProperty('Molecule')) {
 516     return undef;
 517   }
 518   my($Molecule);
 519   $Molecule = $This->GetProperty('Molecule');
 520 
 521   return $Molecule->_GetNumOfBondRingsWithSizeGreaterThan($This, $RingSize);
 522 }
 523 
 524 # Get all rings as an array of references to arrays containing ring atoms...
 525 #
 526 sub GetRings {
 527   my($This) = @_;
 528 
 529   # Is this bond in a molecule?
 530   if (!$This->HasProperty('Molecule')) {
 531     return undef;
 532   }
 533   my($Molecule);
 534   $Molecule = $This->GetProperty('Molecule');
 535 
 536   return $Molecule->_GetBondRings($This);
 537 }
 538 
 539 # Get smallest ring as an array containing ring atoms...
 540 #
 541 sub GetSmallestRing {
 542   my($This) = @_;
 543 
 544   # Is this bond in a molecule?
 545   if (!$This->HasProperty('Molecule')) {
 546     return undef;
 547   }
 548   my($Molecule);
 549   $Molecule = $This->GetProperty('Molecule');
 550 
 551   return $Molecule->_GetSmallestBondRing($This);
 552 }
 553 
 554 # Get largest ring as an array containing ring atoms...
 555 #
 556 sub GetLargestRing {
 557   my($This) = @_;
 558 
 559   # Is this bond in a molecule?
 560   if (!$This->HasProperty('Molecule')) {
 561     return undef;
 562   }
 563   my($Molecule);
 564   $Molecule = $This->GetProperty('Molecule');
 565 
 566   return $Molecule->_GetLargestBondRing($This);
 567 }
 568 
 569 # Get odd size rings an array of references to arrays containing ring atoms...
 570 #
 571 sub GetRingsWithOddSize {
 572   my($This) = @_;
 573 
 574   # Is this bond in a molecule?
 575   if (!$This->HasProperty('Molecule')) {
 576     return undef;
 577   }
 578   my($Molecule);
 579   $Molecule = $This->GetProperty('Molecule');
 580 
 581   return $Molecule->_GetBondRingsWithOddSize($This);
 582 }
 583 
 584 # Get even size rings an array of references to arrays containing ring atoms...
 585 #
 586 sub GetRingsWithEvenSize {
 587   my($This) = @_;
 588 
 589   # Is this bond in a molecule?
 590   if (!$This->HasProperty('Molecule')) {
 591     return undef;
 592   }
 593   my($Molecule);
 594   $Molecule = $This->GetProperty('Molecule');
 595 
 596   return $Molecule->_GetBondRingsWithEvenSize($This);
 597 }
 598 
 599 # Get rings with specified size  an array of references to arrays containing ring atoms...
 600 #
 601 sub GetRingsWithSize {
 602   my($This, $RingSize) = @_;
 603 
 604   # Is this bond in a molecule?
 605   if (!$This->HasProperty('Molecule')) {
 606     return undef;
 607   }
 608   my($Molecule);
 609   $Molecule = $This->GetProperty('Molecule');
 610 
 611   return $Molecule->_GetBondRingsWithSize($This, $RingSize);
 612 }
 613 
 614 # Get rings with size less than specfied size as an array of references to arrays containing ring atoms...
 615 #
 616 sub GetRingsWithSizeLessThan {
 617   my($This, $RingSize) = @_;
 618 
 619   # Is this bond in a molecule?
 620   if (!$This->HasProperty('Molecule')) {
 621     return undef;
 622   }
 623   my($Molecule);
 624   $Molecule = $This->GetProperty('Molecule');
 625 
 626   return $Molecule->_GetBondRingsWithSizeLessThan($This, $RingSize);
 627 }
 628 
 629 # Get rings with size greater than specfied size as an array of references to arrays containing ring atoms...
 630 #
 631 sub GetRingsWithSizeGreaterThan {
 632   my($This, $RingSize) = @_;
 633 
 634   # Is this bond in a molecule?
 635   if (!$This->HasProperty('Molecule')) {
 636     return undef;
 637   }
 638   my($Molecule);
 639   $Molecule = $This->GetProperty('Molecule');
 640 
 641   return $Molecule->_GetBondRingsWithSizeGreaterThan($This, $RingSize);
 642 }
 643 
 644 # Get next object ID...
 645 sub _GetNewObjectID {
 646   $ObjectID++;
 647   return $ObjectID;
 648 }
 649 
 650 # Return a string containing bond and other properties...
 651 sub StringifyBond {
 652   my($This) = @_;
 653   my($BondString, $ID, $BondOrder, $BondType, $Stereochemistry, $AtomsString, $RingBond, $NumOfRings, $Atom1, $Atom2);
 654 
 655   $ID = $This->GetID();
 656   $BondOrder = $This->GetBondOrder();
 657   if (!defined $BondOrder) {
 658     $BondOrder = "undefined";
 659   }
 660   $BondType = $This->GetBondType();
 661   if (!defined $BondOrder) {
 662     $BondType = "undefined";
 663   }
 664   if (defined($BondOrder) && $BondOrder == 2) {
 665     $Stereochemistry = $This->GetStereochemistry();
 666     if (!defined $Stereochemistry) {
 667       $Stereochemistry = "undefined";
 668     }
 669   }
 670   $RingBond = $This->IsInRing();
 671   if (defined $RingBond) {
 672     $RingBond = $RingBond  ? 'Yes' : 'No';
 673     $NumOfRings = $This->GetNumOfRings();
 674   }
 675   else {
 676     $RingBond = 'undefined';
 677     $NumOfRings = 'undefined';
 678   }
 679 
 680   ($Atom1, $Atom2) = $This->GetAtoms();
 681   $AtomsString = "Atoms: undefined";
 682   if (defined($Atom1) && defined($Atom2)) {
 683     my($Atom, $AtomID, $AtomCount, $AtomName, $AtomSymbol, $AtomicNumber, @BondAtoms);
 684     @BondAtoms = ();
 685     push @BondAtoms, ($Atom1, $Atom2);
 686     $AtomCount = 0;
 687     $AtomsString = "";
 688     for $Atom (@BondAtoms) {
 689       $AtomCount++;
 690       $AtomID = $Atom->GetID();
 691       $AtomName = $Atom->GetName();
 692       $AtomSymbol = $Atom->GetAtomSymbol();
 693       $AtomicNumber = $Atom->GetAtomicNumber();
 694       if ($AtomCount == 1) {
 695 	$AtomsString .= "FirstAtom:";
 696       }
 697       else {
 698 	$AtomsString .= "; SecondAtom:";
 699       }
 700       $AtomsString .= " [ ID: $AtomID; Name: \"$AtomName\"; AtomSymbol: \"$AtomSymbol\"; AtomicNumber: $AtomicNumber ]";
 701     }
 702   }
 703   $BondString = "Bond: ID: $ID; BondOrder: $BondOrder; BondType: $BondType; RingBond: $RingBond; NumOfBondRings: $NumOfRings";
 704   if (defined($BondOrder) && $BondOrder == 2) {
 705   $BondString .= " Stereochemistry: $Stereochemistry;";
 706   }
 707   $BondString .= " $AtomsString";
 708 
 709   return $BondString;
 710 }
 711