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