1 package Molecule; 2 # 3 # $RCSfile: Molecule.pm,v $ 4 # $Date: 2011/12/26 21:53:29 $ 5 # $Revision: 1.47 $ 6 # 7 # Author: Manish Sud <msud@san.rr.com> 8 # 9 # Copyright (C) 2004-2012 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 29 use strict; 30 use Carp; 31 use Exporter; 32 use Storable (); 33 use Scalar::Util (); 34 use ObjectProperty; 35 use Graph; 36 use Atom; 37 use Bond; 38 use MolecularFormula; 39 use MathUtil; 40 use PeriodicTable; 41 42 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 43 44 @ISA = qw(Graph ObjectProperty Exporter); 45 @EXPORT = qw(IsMolecule); 46 @EXPORT_OK = qw(FormatElementalCompositionInformation); 47 48 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]); 49 50 # Setup class variables... 51 my($ClassName, $ObjectID); 52 _InitializeClass(); 53 54 # Overload Perl functions... 55 use overload '""' => 'StringifyMolecule'; 56 57 # Class constructor... 58 sub new { 59 my($Class, %NamesAndValues) = @_; 60 61 # Initialize object... 62 my $This = $Class->SUPER::new(); 63 bless $This, ref($Class) || $Class; 64 $This->_InitializeMolecule(); 65 66 if (keys %NamesAndValues) { $This->_InitializeMoleculeProperties(%NamesAndValues); } 67 68 return $This; 69 } 70 71 # Initialize object data... 72 sub _InitializeMolecule { 73 my($This) = @_; 74 my($ObjectID) = _GetNewObjectID(); 75 76 # All other property names and values along with all Set/Get<PropertyName> methods 77 # are implemented on-demand using ObjectProperty class. 78 $This->{ID} = $ObjectID; 79 $This->{Name} = "Molecule ${ObjectID}"; 80 } 81 82 # Initialize molecule properties... 83 sub _InitializeMoleculeProperties { 84 my($This, %NamesAndValues) = @_; 85 86 my($Name, $Value, $MethodName); 87 while (($Name, $Value) = each %NamesAndValues) { 88 $MethodName = "Set${Name}"; 89 $This->$MethodName($Value); 90 } 91 } 92 93 # Initialize class ... 94 sub _InitializeClass { 95 #Class name... 96 $ClassName = __PACKAGE__; 97 98 # ID to keep track of objects... 99 $ObjectID = 0; 100 } 101 102 # Setup an explicit SetID method to block setting of ID by AUTOLOAD function... 103 sub SetID { 104 my($This, $Value) = @_; 105 106 carp "Warning: ${ClassName}->SetID: Object ID can't be changed: it's used for internal tracking..."; 107 108 return $This; 109 } 110 111 # Add an atom... 112 sub AddAtom { 113 my($This, $Atom) = @_; 114 115 if (!defined $Atom) { 116 carp "Warning: ${ClassName}->AddAtom: No atom added: Atom must be specified..."; 117 return undef; 118 } 119 if ($This->HasAtom($Atom)) { 120 carp "Warning: ${ClassName}->AddAtom: No atom added: Atom already exists..."; 121 return undef; 122 } 123 return $This->_AddAtom($Atom); 124 } 125 126 # Add an atom... 127 sub _AddAtom { 128 my($This, $Atom) = @_; 129 130 # Assign atom to this molecule... 131 $Atom->_SetMolecule($This); 132 133 # Add it to the graph as a vertex... 134 my($AtomID); 135 $AtomID = $Atom->GetID(); 136 $This->AddVertex($AtomID); 137 $This->SetVertexProperty('Atom', $Atom, $AtomID); 138 139 return $This; 140 } 141 142 # Add atoms... 143 sub AddAtoms { 144 my($This, @Atoms) = @_; 145 146 if (!@Atoms) { 147 carp "Warning: ${ClassName}->AddAtoms: No atoms added: Atoms list must be specified..."; 148 return undef; 149 } 150 my($Atom); 151 for $Atom (@Atoms) { 152 $This->AddAtom($Atom); 153 } 154 return $This; 155 } 156 157 # Create an atom and add it to molecule... 158 sub NewAtom { 159 my($This, %NamesAndValues) = @_; 160 my($Atom); 161 162 $Atom = new Atom(%NamesAndValues); 163 $This->AddAtom($Atom); 164 165 return $Atom; 166 } 167 168 # Delete an atom... 169 sub DeleteAtom { 170 my($This, $Atom) = @_; 171 172 if (!defined $Atom) { 173 carp "Warning: ${ClassName}->DeleteAtom: No atom deleted: Atom must be specified..."; 174 return undef; 175 } 176 # Does the atom exist in molecule? 177 if (!$This->HasAtom($Atom)) { 178 carp "Warning: ${ClassName}->DeleteAtom: No atom deleted: Atom doesn't exist..."; 179 return undef; 180 } 181 return $This->_DeleteAtom($Atom); 182 } 183 184 # Delete atom... 185 sub _DeleteAtom { 186 my($This, $Atom) = @_; 187 188 my($AtomID); 189 $AtomID = $Atom->GetID(); 190 $This->DeleteVertex($AtomID); 191 192 return $This; 193 } 194 195 # Delete atoms... 196 sub DeleteAtoms { 197 my($This, @Atoms) = @_; 198 199 if (!@Atoms) { 200 carp "Warning: ${ClassName}->DeleteAtoms: No atoms added: Atoms list must be specified..."; 201 return undef; 202 } 203 my($Atom); 204 for $Atom (@Atoms) { 205 $This->DeleteAtom($Atom); 206 } 207 208 return $This; 209 } 210 211 # Is this atom present? 212 sub HasAtom { 213 my($This, $Atom) = @_; 214 215 if (!defined $Atom) { 216 return 0; 217 } 218 if (!$Atom->HasProperty('Molecule')) { 219 # It's not in molecule... 220 return 0; 221 } 222 my($AtomID); 223 $AtomID = $Atom->GetID(); 224 if (!$This->HasVertex($AtomID)) { 225 # It's not in molecule... 226 return 0; 227 } 228 my($Molecule); 229 $Molecule = $Atom->GetProperty('Molecule'); 230 231 return ($This->HasVertex($AtomID) && $This->GetID() == $Molecule->GetID()) ? 1 : 0; 232 } 233 234 # Return an array of atoms. In scalar context, it returns number of atoms. Additionally, 235 # atoms array can be filtered by any user specifiable Atom class method... 236 # 237 sub GetAtoms { 238 my($This, $AtomCheckMethodName, $NegateMethodResult) = @_; 239 my(@Atoms, @AtomIDs); 240 241 @Atoms = (); @AtomIDs = (); 242 243 @AtomIDs = $This->GetVertices(); 244 if (!@AtomIDs) { 245 return wantarray ? @Atoms : scalar @Atoms; 246 } 247 248 @Atoms = $This->GetVerticesProperty('Atom', @AtomIDs); 249 250 if (!defined $AtomCheckMethodName) { 251 return wantarray ? @Atoms : scalar @Atoms; 252 } 253 $NegateMethodResult = (defined($NegateMethodResult) && $NegateMethodResult) ? 1 : 0; 254 255 # Filter out atoms... 256 my($Atom, $KeepAtom, @FilteredAtoms); 257 @FilteredAtoms = (); 258 for $Atom (@Atoms) { 259 $KeepAtom = $NegateMethodResult ? (!$Atom->$AtomCheckMethodName()) : $Atom->$AtomCheckMethodName(); 260 if ($KeepAtom) { 261 push @FilteredAtoms, $Atom; 262 } 263 } 264 return wantarray ? @FilteredAtoms : scalar @FilteredAtoms; 265 } 266 267 # Return an array of bonds. In scalar context, it returns number of bonds... 268 sub GetBonds { 269 my($This) = @_; 270 my(@Bonds, @EdgesAtomsIDs); 271 272 @Bonds = (); @EdgesAtomsIDs = (); 273 274 @EdgesAtomsIDs = $This->GetEdges(); 275 if (@EdgesAtomsIDs) { 276 @Bonds = $This->GetEdgesProperty('Bond', @EdgesAtomsIDs); 277 } 278 return wantarray ? @Bonds : scalar @Bonds; 279 } 280 281 # Get number of atoms in molecule... 282 sub GetNumOfAtoms { 283 my($This) = @_; 284 my($NumOfAtoms); 285 286 $NumOfAtoms = $This->GetAtoms(); 287 288 return $NumOfAtoms; 289 } 290 291 # Get number of bonds in molecule... 292 sub GetNumOfBonds { 293 my($This) = @_; 294 my($NumOfBonds); 295 296 $NumOfBonds = $This->GetBonds(); 297 298 return $NumOfBonds; 299 } 300 301 # Get number of heavy atoms in molecule... 302 sub GetNumOfHeavyAtoms { 303 my($This) = @_; 304 305 return $This->GetNumOfNonHydrogenAtoms(); 306 } 307 308 # Get number of non-hydrogen atoms in molecule... 309 sub GetNumOfNonHydrogenAtoms { 310 my($This) = @_; 311 my($NumOfNonHydrogenAtoms, $Atom, @Atoms); 312 313 @Atoms = $This->GetAtoms(); 314 $NumOfNonHydrogenAtoms = 0; 315 for $Atom (@Atoms) { 316 if (!$Atom->IsHydrogen()) { 317 $NumOfNonHydrogenAtoms++; 318 } 319 } 320 return $NumOfNonHydrogenAtoms; 321 } 322 323 # Get number of hydrogen atoms in molecule... 324 sub GetNumOfHydrogenAtoms { 325 my($This) = @_; 326 my($NumOfHydrogenAtoms, $Atom, @Atoms); 327 328 @Atoms = $This->GetAtoms(); 329 $NumOfHydrogenAtoms = 0; 330 for $Atom (@Atoms) { 331 if ($Atom->IsHydrogen()) { 332 $NumOfHydrogenAtoms++; 333 } 334 } 335 return $NumOfHydrogenAtoms; 336 } 337 338 # Get number of missing hydrogen atoms in molecule... 339 sub GetNumOfMissingHydrogenAtoms { 340 my($This) = @_; 341 my($NumOfMissingHydrogenAtoms, $Atom, @Atoms); 342 343 @Atoms = $This->GetAtoms(); 344 $NumOfMissingHydrogenAtoms = 0; 345 for $Atom (@Atoms) { 346 if (!$Atom->IsHydrogen()) { 347 $NumOfMissingHydrogenAtoms += $Atom->GetNumOfMissingHydrogens(); 348 } 349 } 350 return $NumOfMissingHydrogenAtoms; 351 } 352 353 # Add bond... 354 sub AddBond { 355 my($This, $Bond) = @_; 356 my($Atom1, $Atom2); 357 358 ($Atom1, $Atom2) = $Bond->GetAtoms(); 359 if (!(defined($Atom1) && defined($Atom2))) { 360 carp "Warning: ${ClassName}->AddBond: No bond added: Both atoms must be specified..."; 361 return undef; 362 } 363 if (!($This->HasAtom($Atom1) && $This->HasAtom($Atom2))) { 364 carp "Warning: ${ClassName}->AddBond: No bond added: Both atoms must be present..."; 365 return undef; 366 } 367 if ($This->HasBond($Bond)) { 368 carp "Warning: ${ClassName}->AddBond: No bond added: Bond already exists..."; 369 return undef; 370 } 371 return $This->_AddBond($Bond); 372 } 373 374 # Add bond... 375 sub _AddBond { 376 my($This, $Bond) = @_; 377 378 # Assign bond to this molecule... 379 $Bond->_SetMolecule($This); 380 381 # Add it to the graph as an edge... 382 my($Atom1, $Atom2, $AtomID1, $AtomID2); 383 ($Atom1, $Atom2) = $Bond->GetAtoms(); 384 $AtomID1 = $Atom1->GetID(); $AtomID2 = $Atom2->GetID(); 385 $This->AddEdge($AtomID1, $AtomID2); 386 $This->SetEdgeProperty('Bond', $Bond, $AtomID1, $AtomID2); 387 388 return $This; 389 } 390 391 # Add Bonds... 392 sub AddBonds { 393 my($This, @Bonds) = @_; 394 395 if (!@Bonds) { 396 carp "Warning: ${ClassName}->AddBonds: No bonds added: Bonds list must be specified..."; 397 return undef; 398 } 399 my($Bond); 400 for $Bond (@Bonds) { 401 $This->AddBond($Bond); 402 } 403 return $This; 404 } 405 406 # Create a bond and add it to molecule... 407 sub NewBond { 408 my($This, %NamesAndValues) = @_; 409 my($Bond); 410 411 $Bond = new Bond(%NamesAndValues); 412 $This->AddBond($Bond); 413 414 return $Bond; 415 } 416 417 # Delete a bond... 418 sub DeleteBond { 419 my($This, $Bond) = @_; 420 421 if (!defined $Bond) { 422 carp "Warning: ${ClassName}->DeleteBond: No bond deleted: Bond must be specified..."; 423 return undef; 424 } 425 # Does the bond exist in molecule? 426 if (!$This->HasBond($Bond)) { 427 carp "Warning: ${ClassName}->DeleteBond: No bond deleted: Bond doesn't exist..."; 428 return undef; 429 } 430 return $This->_DeleteBond($Bond); 431 } 432 433 # Delete bond... 434 sub _DeleteBond { 435 my($This, $Bond) = @_; 436 437 my($Atom1, $Atom2, $AtomID1, $AtomID2); 438 ($Atom1, $Atom2) = $Bond->GetAtoms(); 439 $AtomID1 = $Atom1->GetID(); $AtomID2 = $Atom2->GetID(); 440 $This->DeleteEdge($AtomID1, $AtomID2); 441 442 return $This; 443 } 444 445 # Delete bonds... 446 sub DeleteBonds { 447 my($This, @Bonds) = @_; 448 449 if (!@Bonds) { 450 carp "Warning: ${ClassName}->DeleteBonds: No bonds added: Bonds list must be specified..."; 451 return undef; 452 } 453 my($Bond); 454 for $Bond (@Bonds) { 455 $This->DeleteBond($Bond); 456 } 457 458 return $This; 459 } 460 461 # Has bond... 462 sub HasBond { 463 my($This, $Bond) = @_; 464 my($Atom1, $Atom2); 465 466 ($Atom1, $Atom2) = $Bond->GetAtoms(); 467 if (!($This->HasAtom($Atom1) && $This->HasAtom($Atom2))) { 468 return 0; 469 } 470 if (!$Bond->HasProperty('Molecule')) { 471 # It's not in molecule... 472 return 0; 473 } 474 my($AtomID1, $AtomID2, $Molecule); 475 $AtomID1 = $Atom1->GetID(); $AtomID2 = $Atom2->GetID(); 476 $Molecule = $Bond->GetMolecule(); 477 478 return ($This->HasEdge($AtomID1, $AtomID2) && $This->GetID() == $Molecule->GetID()) ? 1 : 0; 479 } 480 481 # Get atom neighbors... 482 sub _GetAtomNeighbors { 483 my($This, $Atom) = @_; 484 485 my($AtomID, @Atoms, @AtomIDs); 486 487 @Atoms = (); @AtomIDs = (); 488 $AtomID = $Atom->GetID(); 489 @AtomIDs = $This->GetNeighbors($AtomID); 490 if (@AtomIDs) { 491 @Atoms = $This->GetVerticesProperty('Atom', @AtomIDs); 492 } 493 return wantarray ? @Atoms : scalar @Atoms; 494 } 495 496 # Get atom bonds... 497 sub _GetAtomBonds { 498 my($This, $Atom) = @_; 499 my($AtomID, @AtomIDs, @Bonds); 500 501 @Bonds = (); @AtomIDs = (); 502 $AtomID = $Atom->GetID(); 503 @AtomIDs = $This->GetEdges($AtomID); 504 if (@AtomIDs) { 505 @Bonds = $This->GetEdgesProperty('Bond', @AtomIDs); 506 } 507 return wantarray ? @Bonds : scalar @Bonds; 508 } 509 510 # Get bond to specified atom... 511 sub _GetBondToAtom { 512 my($This, $Atom1, $Atom2) = @_; 513 my($AtomID1, $AtomID2); 514 515 $AtomID1 = $Atom1->GetID(); 516 $AtomID2 = $Atom2->GetID(); 517 518 return $This->GetEdgeProperty('Bond', $AtomID1, $AtomID2); 519 } 520 521 # Add hydrogens to each atoms in molecule and return total number of hydrogens added... 522 sub AddHydrogens { 523 my($This) = @_; 524 525 return $This->_AddHydrogens(); 526 } 527 528 # Add hydrogens to polar atoms (N, O, P, S) in molecule and return total number of hydrogens added... 529 sub AddPolarHydrogens { 530 my($This) = @_; 531 my($PolarHydrogensOnly) = 1; 532 533 return $This->_AddHydrogens($PolarHydrogensOnly); 534 } 535 536 # Add all the hydrogens or hydrogens for polar atoms only... 537 # 538 # Note: 539 # . The current release of MayaChemTools doesn't assign any hydrogen positions. 540 # 541 sub _AddHydrogens { 542 my($This, $PolarHydrogensOnly) = @_; 543 my($Atom, $NumOfHydrogensAdded, $HydrogenPositionsWarning, @Atoms); 544 545 if (! defined $PolarHydrogensOnly) { 546 $PolarHydrogensOnly = 0; 547 } 548 549 $NumOfHydrogensAdded = 0; 550 @Atoms = $This->GetAtoms(); 551 $HydrogenPositionsWarning = 0; 552 553 ATOM: for $Atom (@Atoms) { 554 if ($PolarHydrogensOnly) { 555 if (!$Atom->IsPolarAtom()) { 556 next ATOM; 557 } 558 } 559 $NumOfHydrogensAdded += $Atom->AddHydrogens($HydrogenPositionsWarning); 560 } 561 return $NumOfHydrogensAdded; 562 } 563 564 # Delete all hydrogens atoms in molecule and return total number of hydrogens removed... 565 sub DeleteHydrogens { 566 my($This) = @_; 567 568 return $This->_DeleteHydrogens(); 569 } 570 571 # Delete hydrogens to polar atoms (N, O, P, S) in molecule and return total number of hydrogens removed... 572 sub DeletePolarHydrogens { 573 my($This) = @_; 574 my($PolarHydrogensOnly) = 1; 575 576 return $This->_DeleteHydrogens($PolarHydrogensOnly); 577 } 578 579 # Delete all hydrogens atoms in molecule and return total number of hydrogens removed... 580 sub _DeleteHydrogens { 581 my($This, $PolarHydrogensOnly) = @_; 582 my($Atom, $NumOfHydrogensRemoved, @Atoms); 583 584 if (! defined $PolarHydrogensOnly) { 585 $PolarHydrogensOnly = 0; 586 } 587 588 $NumOfHydrogensRemoved = 0; 589 @Atoms = $This->GetAtoms(); 590 591 ATOM: for $Atom (@Atoms) { 592 if ($PolarHydrogensOnly) { 593 if (!$Atom->IsPolarHydrogen()) { 594 next ATOM; 595 } 596 } 597 elsif (!$Atom->IsHydrogen()) { 598 next ATOM; 599 } 600 $This->_DeleteAtom($Atom); 601 $NumOfHydrogensRemoved++; 602 } 603 return $NumOfHydrogensRemoved; 604 } 605 606 # Get molecular weight by summing up atomic weights of all the atoms... 607 sub GetMolecularWeight { 608 my($This, $IncludeMissingHydrogens) = @_; 609 my($MolecularWeight, $AtomicWeight, @Atoms, $Atom); 610 611 $IncludeMissingHydrogens = defined($IncludeMissingHydrogens) ? $IncludeMissingHydrogens : 1; 612 613 $MolecularWeight = 0; 614 @Atoms = $This->GetAtoms(); 615 for $Atom (@Atoms) { 616 $AtomicWeight = $Atom->GetAtomicWeight(); 617 if (defined $AtomicWeight) { 618 $MolecularWeight += $AtomicWeight; 619 } 620 } 621 622 if (!$IncludeMissingHydrogens) { 623 return $MolecularWeight; 624 } 625 626 # Account for missing hydrogen atoms... 627 my($NumOfMissingHydrogenAtoms); 628 629 $NumOfMissingHydrogenAtoms = $This->GetNumOfMissingHydrogenAtoms(); 630 if ($NumOfMissingHydrogenAtoms) { 631 $MolecularWeight += $NumOfMissingHydrogenAtoms * PeriodicTable::GetElementAtomicWeight('H'); 632 } 633 634 return $MolecularWeight; 635 } 636 637 # Get exact mass by summing up exact masses of all the atoms... 638 sub GetExactMass { 639 my($This, $IncludeMissingHydrogens) = @_; 640 my($ExactMass, $AtomicMass, @Atoms, $Atom); 641 642 $IncludeMissingHydrogens = defined($IncludeMissingHydrogens) ? $IncludeMissingHydrogens : 1; 643 644 $ExactMass = 0; 645 @Atoms = $This->GetAtoms(); 646 for $Atom (@Atoms) { 647 $AtomicMass = $Atom->GetExactMass(); 648 if (defined $AtomicMass) { 649 $ExactMass += $AtomicMass; 650 } 651 } 652 653 if (!$IncludeMissingHydrogens) { 654 return $ExactMass; 655 } 656 657 # Account for missing hydrogen atoms... 658 my($NumOfMissingHydrogenAtoms); 659 660 $NumOfMissingHydrogenAtoms = $This->GetNumOfMissingHydrogenAtoms(); 661 if ($NumOfMissingHydrogenAtoms) { 662 $ExactMass += $NumOfMissingHydrogenAtoms * PeriodicTable::GetElementMostAbundantNaturalIsotopeMass('H'); 663 } 664 665 return $ExactMass; 666 } 667 668 # Get net formal charge on the molecule using one of the following two methods: 669 # . Using explicitly set FormalCharge property 670 # . Adding up formal charge on each atom in the molecule 671 # 672 # Caveats: 673 # . FormalCharge is different from Charge property of the molecule which corresponds to 674 # sum of partial atomic charges explicitly set for each atom using a specific methodology. 675 # 676 sub GetFormalCharge { 677 my($This) = @_; 678 679 # Is FormalCharge property explicitly set? 680 if ($This->HasProperty('FormalCharge')) { 681 return $This->GetProperty('FormalCharge'); 682 } 683 my($FormalCharge, $AtomicFormalCharge, @Atoms, $Atom); 684 685 $FormalCharge = 0; 686 @Atoms = $This->GetAtoms(); 687 for $Atom (@Atoms) { 688 $AtomicFormalCharge = $Atom->GetFormalCharge(); 689 if (defined $AtomicFormalCharge) { 690 $FormalCharge += $AtomicFormalCharge; 691 } 692 } 693 return $FormalCharge; 694 } 695 696 # Get net charge on the molecule using one of the following two methods: 697 # . Using explicitly set Charge property 698 # . Adding up charge on each atom in the molecule 699 # 700 # Caveats: 701 # . FormalCharge is different from Charge property of the molecule which corresponds to 702 # sum of partial atomic charges explicitly set for each atom using a specific methodology. 703 # 704 sub GetCharge { 705 my($This) = @_; 706 707 # Is Charge property explicitly set? 708 if ($This->HasProperty('Charge')) { 709 return $This->GetProperty('Charge'); 710 } 711 my($Charge, $AtomicCharge, @Atoms, $Atom); 712 713 $Charge = 0; 714 @Atoms = $This->GetAtoms(); 715 for $Atom (@Atoms) { 716 $AtomicCharge = $Atom->GetCharge(); 717 if (defined $AtomicCharge) { 718 $Charge += $AtomicCharge; 719 } 720 } 721 return $Charge; 722 } 723 724 # Get net SpinMultiplicity on the molecule using one of the following two methods: 725 # . Using explicitly set SpinMultiplicity property 726 # . Adding up SpinMultiplicity on each atom in the molecule 727 # 728 # 729 sub GetSpinMultiplicity { 730 my($This) = @_; 731 732 # Is SpinMultiplicity property explicitly set? 733 if ($This->HasProperty('SpinMultiplicity')) { 734 return $This->GetProperty('SpinMultiplicity'); 735 } 736 my($AtomicSpinMultiplicity, $SpinMultiplicity, @Atoms, $Atom); 737 738 $SpinMultiplicity = 0; 739 @Atoms = $This->GetAtoms(); 740 for $Atom (@Atoms) { 741 $AtomicSpinMultiplicity = $Atom->GetSpinMultiplicity(); 742 if (defined $AtomicSpinMultiplicity) { 743 $SpinMultiplicity += $AtomicSpinMultiplicity; 744 } 745 } 746 return $SpinMultiplicity; 747 } 748 749 # Get molecular formula by collecting information about all atoms in the molecule and 750 # composing the formula using Hills ordering system: 751 # . C shows up first and H follows assuming C is present 752 # . All other standard elements are sorted alphanumerically 753 # . All other non-stanard atom symbols are also sorted alphanumerically and follow standard elements 754 # 755 # Caveats: 756 # . By default, missing hydrogens and nonelements are also included 757 # . Elements for disconnected fragments are combined into the same formula 758 # 759 sub GetMolecularFormula { 760 my($This, $IncludeMissingHydrogens, $IncludeNonElements) = @_; 761 my($MolecularFormula, $AtomSymbol, $ElementsCountRef, $NonElementsCountRef); 762 763 $IncludeMissingHydrogens = defined($IncludeMissingHydrogens) ? $IncludeMissingHydrogens : 1; 764 $IncludeNonElements = defined($IncludeNonElements) ? $IncludeNonElements : 1; 765 766 # Get elements count and setup molecular formula... 767 ($ElementsCountRef, $NonElementsCountRef) = $This->GetElementsAndNonElements($IncludeMissingHydrogens); 768 $MolecularFormula = ''; 769 770 # Count C and H first... 771 if (exists $ElementsCountRef->{C} ) { 772 $MolecularFormula .= 'C' . ($ElementsCountRef->{C} > 1 ? $ElementsCountRef->{C} : ''); 773 delete $ElementsCountRef->{C}; 774 775 if (exists $ElementsCountRef->{H} ) { 776 $MolecularFormula .= 'H' . ($ElementsCountRef->{H} > 1 ? $ElementsCountRef->{H} : ''); 777 delete $ElementsCountRef->{H}; 778 } 779 } 780 781 # Sort elements... 782 for $AtomSymbol (sort {$a cmp $b} keys %{$ElementsCountRef}) { 783 $MolecularFormula .= $AtomSymbol . ($ElementsCountRef->{$AtomSymbol} > 1 ? $ElementsCountRef->{$AtomSymbol} : ''); 784 } 785 if (!$IncludeNonElements) { 786 return $MolecularFormula; 787 } 788 789 # Sort non-elements... 790 for $AtomSymbol (sort {$a cmp $b} keys %{$NonElementsCountRef}) { 791 $MolecularFormula .= $AtomSymbol . ($NonElementsCountRef->{$AtomSymbol} > 1 ? $NonElementsCountRef->{$AtomSymbol} : ''); 792 } 793 794 # Check formal charge... 795 my($FormalCharge); 796 $FormalCharge = $This->GetFormalCharge(); 797 if ($FormalCharge) { 798 # Setup formal charge string... 799 my($FormalChargeString); 800 if ($FormalCharge == 1 ) { 801 $FormalChargeString = "+"; 802 } 803 elsif ($FormalCharge == -1 ) { 804 $FormalChargeString = "-"; 805 } 806 else { 807 $FormalChargeString = ($FormalCharge > 0) ? ("+" . abs($FormalCharge)) : ("-" . abs($FormalCharge)); 808 } 809 $MolecularFormula = "${MolecularFormula}${FormalChargeString}"; 810 } 811 812 return $MolecularFormula; 813 } 814 815 # Count elements and non-elements in molecule and return references to hashes 816 # containing count of elements and non-elements respectively. By default, missing 817 # hydrogens are not added to the element hash. 818 # 819 # 820 sub GetElementsAndNonElements { 821 my($This, $IncludeMissingHydrogens) = @_; 822 my($Atom, $AtomicNumber, $AtomSymbol, $NumOfMissingHydrogens, @Atoms, %ElementsCount, %NonElementsCount); 823 824 $IncludeMissingHydrogens = defined($IncludeMissingHydrogens) ? $IncludeMissingHydrogens : 0; 825 826 %ElementsCount = (); %NonElementsCount = (); 827 $NumOfMissingHydrogens = 0; 828 829 # Count elements and non elements... 830 @Atoms = $This->GetAtoms(); 831 for $Atom (@Atoms) { 832 $AtomicNumber = $Atom->GetAtomicNumber(); 833 $AtomSymbol = $Atom->GetAtomSymbol(); 834 if ($AtomicNumber) { 835 if (exists $ElementsCount{$AtomSymbol}) { 836 $ElementsCount{$AtomSymbol} += 1; 837 } 838 else { 839 $ElementsCount{$AtomSymbol} = 1; 840 } 841 if ($IncludeMissingHydrogens) { 842 if (!$Atom->IsHydrogen()) { 843 $NumOfMissingHydrogens += $Atom->GetNumOfMissingHydrogens(); 844 } 845 } 846 } 847 else { 848 if (exists $NonElementsCount{$AtomSymbol}) { 849 $NonElementsCount{$AtomSymbol} += 1; 850 } 851 else { 852 $NonElementsCount{$AtomSymbol} = 1; 853 } 854 } 855 } 856 if ($IncludeMissingHydrogens && $NumOfMissingHydrogens) { 857 $AtomSymbol = 'H'; 858 if (exists $ElementsCount{$AtomSymbol}) { 859 $ElementsCount{$AtomSymbol} += $NumOfMissingHydrogens; 860 } 861 else { 862 $ElementsCount{$AtomSymbol} = $NumOfMissingHydrogens; 863 } 864 } 865 866 return (\%ElementsCount, \%NonElementsCount); 867 } 868 869 # Get number of element and non-elements in molecule. By default, missing 870 # hydrogens are not added to element count. 871 # 872 sub GetNumOfElementsAndNonElements { 873 my($This, $IncludeMissingHydrogens) = @_; 874 my($ElementCount, $NonElementCount, $Atom); 875 876 $IncludeMissingHydrogens = defined($IncludeMissingHydrogens) ? $IncludeMissingHydrogens : 0; 877 878 ($ElementCount, $NonElementCount) = (0, 0); 879 880 ATOM: for $Atom ($This->GetAtoms()) { 881 if (!$Atom->GetAtomicNumber()) { 882 $NonElementCount++; 883 next ATOM; 884 } 885 # Process element... 886 $ElementCount++; 887 if ($IncludeMissingHydrogens) { 888 if (!$Atom->IsHydrogen()) { 889 $ElementCount += $Atom->GetNumOfMissingHydrogens(); 890 } 891 } 892 } 893 894 return ($ElementCount, $NonElementCount); 895 } 896 897 # Calculate elemental composition and return reference to arrays 898 # containing elements and their percent composition. 899 # 900 # Caveats: 901 # . By default, missing hydrogens are included 902 # . Non elemnents are ignored 903 # . Mass number are ignored 904 # 905 sub GetElementalComposition { 906 my($This, $IncludeMissingHydrogens) = @_; 907 my($MolecularFormula, $IncludeNonElements, $ElementsCountRef, $NonElementsCountRef, $ElementsRef, $ElementsCompositionRef); 908 909 $IncludeMissingHydrogens = defined($IncludeMissingHydrogens) ? $IncludeMissingHydrogens : 1; 910 911 $IncludeNonElements = 0; 912 ($ElementsCountRef, $NonElementsCountRef) = $This->GetElementsAndNonElements($IncludeMissingHydrogens); 913 914 $MolecularFormula = $This->GetMolecularFormula($IncludeMissingHydrogens, $IncludeNonElements); 915 916 ($ElementsRef, $ElementsCompositionRef) = MolecularFormula::CalculateElementalComposition($MolecularFormula); 917 918 return ($ElementsRef, $ElementsCompositionRef); 919 } 920 921 # Using refernece to element and its composition arrays, format composition information 922 # as: Element: Composition;... 923 # 924 sub FormatElementalCompositionInformation { 925 my($FirstParameter, $SecondParameter, $ThirdParameter, $FourthParameter) = @_; 926 my($This, $ElementsRef, $ElementCompositionRef, $Precision); 927 928 if (_IsMolecule($FirstParameter)) { 929 ($This, $ElementsRef, $ElementCompositionRef, $Precision) = ($FirstParameter, $SecondParameter, $ThirdParameter, $FourthParameter); 930 } 931 else { 932 ($ElementsRef, $ElementCompositionRef, $Precision) = ($FirstParameter, $SecondParameter, $ThirdParameter); 933 } 934 my($FormattedInfo) = ''; 935 936 if (!(defined($ElementsRef) && @{$ElementsRef})) { 937 carp "Warning: ${ClassName}->FormatElementalCompositionInformation: Elements list is not defined or empty..."; 938 return undef; 939 } 940 if (!(defined($ElementCompositionRef) && @{$ElementCompositionRef})) { 941 carp "Warning: ${ClassName}->FormatElementalCompositionInformation: Elements composition list is not defined or empty..."; 942 return undef; 943 } 944 945 if (!defined $Precision) { 946 $Precision = 2; 947 } 948 949 $FormattedInfo = MolecularFormula::FormatCompositionInfomation($ElementsRef, $ElementCompositionRef, $Precision); 950 951 return $FormattedInfo; 952 } 953 954 # Copy molecule and its associated data using Storable::dclone and update: 955 # 956 # o Atom references corresponding atoms and bonds objects 957 # o Bond object references 958 # 959 # Object IDs for Atoms and Bonds don't get changed. So there is no need to clear 960 # up any exisiting ring data attached to molecule via graph as vertex IDs. 961 # 962 sub Copy { 963 my($This) = @_; 964 my($NewMolecule, $Atom, $NewAtom, $AtomID, @Atoms, @AtomIDs, %AtomsIDsToNewAtoms); 965 966 $NewMolecule = Storable::dclone($This); 967 968 # Update atom references stored as vertex property... 969 970 @Atoms = (); @AtomIDs = (); 971 %AtomsIDsToNewAtoms = (); 972 973 @AtomIDs = $This->GetVertices(); 974 if (@AtomIDs) { 975 @Atoms = $This->GetVerticesProperty('Atom', @AtomIDs); 976 } 977 978 for $Atom (@Atoms) { 979 $AtomID = $Atom->GetID(); 980 981 # Setup a reference to copied atom object... 982 $NewAtom = $Atom->Copy(); 983 $AtomsIDsToNewAtoms{$AtomID} = $NewAtom; 984 985 # Update atom reference to new atom object... 986 $NewMolecule->UpdateVertexProperty('Atom', $NewAtom, $AtomID); 987 } 988 989 # Update bond object and bond atom references stored as edge property... 990 my($Index, $AtomID1, $AtomID2, $Bond, $NewBond, $NewAtom1, $NewAtom2, @EdgesAtomsIDs); 991 @EdgesAtomsIDs = (); 992 @EdgesAtomsIDs = $This->GetEdges(); 993 for ($Index = 0; $Index < $#EdgesAtomsIDs; $Index += 2) { 994 $AtomID1 = $EdgesAtomsIDs[$Index]; $AtomID2 = $EdgesAtomsIDs[$Index + 1]; 995 996 # Get reference to bond object... 997 $Bond = $This->GetEdgeProperty('Bond', $AtomID1, $AtomID2); 998 999 # Make a new bond object and update its atom references... 1000 $NewBond = $Bond->Copy(); 1001 $NewAtom1 = $AtomsIDsToNewAtoms{$AtomID1}; 1002 $NewAtom2 = $AtomsIDsToNewAtoms{$AtomID2}; 1003 $NewBond->SetAtoms($NewAtom1, $NewAtom2); 1004 1005 # Update bond object reference in the new molecule... 1006 $NewMolecule->UpdateEdgeProperty('Bond', $NewBond, $AtomID1, $AtomID2); 1007 } 1008 1009 return $NewMolecule; 1010 } 1011 1012 # Get number of connected components... 1013 # 1014 sub GetNumOfConnectedComponents { 1015 my($This) = @_; 1016 my($NumOfComponents); 1017 1018 $NumOfComponents = $This->GetConnectedComponentsVertices(); 1019 1020 return $NumOfComponents; 1021 } 1022 1023 # Return a reference to an array containing molecules corresponding 1024 # to connected components sorted in decreasing order of component size... 1025 # 1026 sub GetConnectedComponents { 1027 my($This) = @_; 1028 my($Index, @ComponentMolecules, @ConnectedComponents); 1029 1030 @ConnectedComponents = (); 1031 @ConnectedComponents = $This->GetConnectedComponentsVertices(); 1032 @ComponentMolecules = (); 1033 1034 for $Index (0 .. $#ConnectedComponents) { 1035 push @ComponentMolecules, $This->_GetConnectedComponent(\@ConnectedComponents, $Index); 1036 } 1037 return @ComponentMolecules; 1038 } 1039 1040 # Return a reference to largest connected component as a molecule object... 1041 # 1042 sub GetLargestConnectedComponent { 1043 my($This) = @_; 1044 my($LargestComponentIndex, @ConnectedComponents); 1045 1046 $LargestComponentIndex = 0; 1047 @ConnectedComponents = (); 1048 @ConnectedComponents = $This->GetConnectedComponentsVertices(); 1049 1050 return $This->_GetConnectedComponent(\@ConnectedComponents, $LargestComponentIndex); 1051 } 1052 1053 # Return connected component as a molecule... 1054 # 1055 sub _GetConnectedComponent { 1056 my($This, $ConnectedComponentsRef, $ComponentIndex) = @_; 1057 my($ComponentMolecule); 1058 1059 # Copy existing molecule... 1060 $ComponentMolecule = $This->Copy(); 1061 1062 # Delete all atoms besides the ones in specified component... 1063 $ComponentMolecule->_DeleteConnectedComponents($ConnectedComponentsRef, $ComponentIndex); 1064 1065 # Clear any deteced rings... 1066 if ($ComponentMolecule->HasRings()) { 1067 $ComponentMolecule->ClearRings(); 1068 } 1069 return $ComponentMolecule; 1070 } 1071 1072 # Delete atoms corresponding to all connected components except the one specified... 1073 # 1074 sub _DeleteConnectedComponents { 1075 my($This, $ConnectedComponentsRef, $KeepComponentIndex) = @_; 1076 my($Index, $AtomID); 1077 1078 INDEX: for $Index (0 .. $#{$ConnectedComponentsRef}) { 1079 if ($Index == $KeepComponentIndex) { 1080 next INDEX; 1081 } 1082 for $AtomID (@{$ConnectedComponentsRef->[$Index]}) { 1083 $This->DeleteVertex($AtomID); 1084 } 1085 } 1086 return $This; 1087 } 1088 1089 # Return an array containing references to atom arrays corresponding to atoms of 1090 # connected components sorted in order of their decreasing size... 1091 # 1092 sub GetConnectedComponentsAtoms { 1093 my($This) = @_; 1094 my($Index, @ComponentsAtoms, @ConnectedComponents); 1095 1096 @ConnectedComponents = (); 1097 @ConnectedComponents = $This->GetConnectedComponentsVertices(); 1098 1099 @ComponentsAtoms = (); 1100 for $Index (0 .. $#ConnectedComponents) { 1101 my(@ComponentAtoms); 1102 1103 @ComponentAtoms = (); 1104 @ComponentAtoms = $This->_GetConnectedComponentAtoms(\@ConnectedComponents, $Index); 1105 push @ComponentsAtoms, \@ComponentAtoms; 1106 } 1107 return @ComponentsAtoms; 1108 } 1109 1110 # Return an array containing atoms correspondig to largest connected component... 1111 # 1112 sub GetLargestConnectedComponentAtoms { 1113 my($This) = @_; 1114 my($LargestComponentIndex, @ConnectedComponents); 1115 1116 $LargestComponentIndex = 0; 1117 @ConnectedComponents = (); 1118 @ConnectedComponents = $This->GetConnectedComponentsVertices(); 1119 1120 return $This->_GetConnectedComponentAtoms(\@ConnectedComponents, $LargestComponentIndex); 1121 } 1122 1123 # Return an array containing atoms corresponding to specified connected component... 1124 # 1125 sub _GetConnectedComponentAtoms { 1126 my($This, $ConnectedComponentsRef, $ComponentIndex) = @_; 1127 my($AtomID, @AtomIDs, @ComponentAtoms); 1128 1129 @ComponentAtoms = (); 1130 @AtomIDs = (); 1131 1132 for $AtomID (@{$ConnectedComponentsRef->[$ComponentIndex]}) { 1133 push @AtomIDs, $AtomID; 1134 } 1135 @ComponentAtoms = $This->_GetAtomsFromAtomIDs(@AtomIDs); 1136 1137 return @ComponentAtoms; 1138 } 1139 1140 # Except for the largest connected component, delete atoms corresponding to all other 1141 # connected components... 1142 # 1143 sub KeepLargestComponent { 1144 my($This) = @_; 1145 my($LargestComponentIndex, @ConnectedComponents); 1146 1147 @ConnectedComponents = (); 1148 @ConnectedComponents = $This->GetConnectedComponentsVertices(); 1149 if (@ConnectedComponents == 1) { 1150 return $This; 1151 } 1152 $LargestComponentIndex = 0; 1153 $This->_DeleteConnectedComponents(\@ConnectedComponents, $LargestComponentIndex); 1154 1155 # Clear any deteced rings... 1156 if ($This->HasRings()) { 1157 $This->ClearRings(); 1158 } 1159 1160 return $This; 1161 } 1162 1163 # Get an array of topologically sorted atoms starting from a specified atom or 1164 # an arbitrary atom in the molecule... 1165 # 1166 sub GetTopologicallySortedAtoms { 1167 my($This, $StartAtom) = @_; 1168 my(@SortedAtoms); 1169 1170 @SortedAtoms = (); 1171 if (defined($StartAtom) && !$This->HasAtom($StartAtom)) { 1172 carp "Warning: ${ClassName}->_GetTopologicallySortedAtoms: No atoms retrieved: Start atom doesn't exist..."; 1173 return @SortedAtoms; 1174 } 1175 my($StartAtomID, @AtomIDs); 1176 1177 @AtomIDs = (); 1178 $StartAtomID = defined($StartAtom) ? $StartAtom->GetID() : undef; 1179 1180 @AtomIDs = $This->GetTopologicallySortedVertices($StartAtomID); 1181 @SortedAtoms = $This->_GetAtomsFromAtomIDs(@AtomIDs); 1182 1183 return @SortedAtoms; 1184 } 1185 1186 # Detect rings in molecule... 1187 # 1188 sub DetectRings { 1189 my($This) = @_; 1190 1191 # Use graph method to detect all cycles and associate 'em to graph as graph 1192 # and vertex properties... 1193 return $This->DetectCycles(); 1194 } 1195 1196 # Clear rings in molecule... 1197 # 1198 sub ClearRings { 1199 my($This) = @_; 1200 1201 # Use graph method to clear all cycles... 1202 $This->ClearCycles(); 1203 1204 return $This; 1205 } 1206 1207 1208 # Setup rings type paths to use during all ring related methods. Possible values: 1209 # Independent or All. Default is to use Independent rings. 1210 # 1211 sub SetActiveRings { 1212 my($This, $RingsType) = @_; 1213 1214 if (!defined $This->SetActiveCyclicPaths($RingsType)) { 1215 return undef; 1216 } 1217 return $This; 1218 } 1219 1220 # Identify aromatic rings in molecule and set aromaticity for corresponding atoms and bonds... 1221 # 1222 # Note: 1223 # . Assumes ring detection has been performed. 1224 # . Any exisiting atom and bond "Aromaticity" property is cleared up. 1225 # 1226 sub DetectAromaticity { 1227 my($This) = @_; 1228 1229 # Delete aromaticity property for atoms and bonds... 1230 $This->DeleteAromaticity(); 1231 1232 # Any ring out there... 1233 if (!$This->HasRings()) { 1234 return $This; 1235 } 1236 1237 if ($This->HasFusedRings()) { 1238 $This->_DetectAromaticityUsingFusedRingSets(); 1239 } 1240 else { 1241 $This->_DetectAromaticityUsingIndividualRings(); 1242 } 1243 return $This; 1244 } 1245 1246 # Go over all rings and set aromaticity property for corresponding ring atoms 1247 # and bonds involved in aromatic rings... 1248 # 1249 sub _DetectAromaticityUsingIndividualRings { 1250 my($This) = @_; 1251 1252 return $This->_DetectRingsAromaticity($This->GetRings()); 1253 } 1254 1255 # For each fused ring set, detect aromaticity by treating all of its ring as one aromatic 1256 # system for counting pi electrons to satisfy Huckel's rule; In case of a failure, rings in 1257 # fused set are treated individually for aromaticity detection. Additionally, non-fused 1258 # rings are handled on their own during aromaticity detection. 1259 # 1260 # Note: 1261 # . pi electrons in common bonds involved in fused ring sets are only counted once. 1262 # 1263 # 1264 sub _DetectAromaticityUsingFusedRingSets { 1265 my($This) = @_; 1266 my($Index, $RingAtomsRef, $FusedRingSetRef, $FusedRingSetsRef, $NonFusedRingsRef, @FusedRingSetIsAromatic); 1267 1268 ($FusedRingSetsRef, $NonFusedRingsRef) = $This->GetFusedAndNonFusedRings(); 1269 1270 @FusedRingSetIsAromatic = (); 1271 RINGSET: for $Index (0 .. $#{$FusedRingSetsRef}) { 1272 $FusedRingSetRef = $FusedRingSetsRef->[$Index]; 1273 $FusedRingSetIsAromatic[$Index] = 0; 1274 1275 my($NumOfPiElectronsInRingSet, $NumOfPiElectronsInRing, %FusedRingSetCommonBonds); 1276 1277 $NumOfPiElectronsInRingSet = 0; 1278 %FusedRingSetCommonBonds = (); 1279 1280 for $RingAtomsRef (@{$FusedRingSetRef}) { 1281 my(@RingBonds); 1282 1283 @RingBonds = (); 1284 @RingBonds = $This->GetRingBonds(@{$RingAtomsRef}); 1285 $NumOfPiElectronsInRing = $This->_GetNumOfPiElectrons($RingAtomsRef, \@RingBonds, \%FusedRingSetCommonBonds); 1286 1287 if (!$NumOfPiElectronsInRing) { 1288 next RINGSET; 1289 } 1290 $NumOfPiElectronsInRingSet += $NumOfPiElectronsInRing; 1291 } 1292 if ($This->_DoPiElectronSatifyHuckelsRule($NumOfPiElectronsInRingSet)) { 1293 $FusedRingSetIsAromatic[$Index] = 1; 1294 } 1295 } 1296 1297 # Set atom and bond aromatic flags for ring sets whose pi electrons satisfy Huckel's rule; otherwise, 1298 # treat rings in a ring set as individual rings for detecting aromaticity... 1299 for $Index (0 .. $#{$FusedRingSetsRef}) { 1300 $FusedRingSetRef = $FusedRingSetsRef->[$Index]; 1301 if ($FusedRingSetIsAromatic[$Index]) { 1302 $This->_SetRingsAromaticity(@{$FusedRingSetRef}); 1303 } 1304 else { 1305 $This->_DetectRingsAromaticity(@{$FusedRingSetRef}); 1306 } 1307 } 1308 1309 $This->_DetectRingsAromaticity(@{$NonFusedRingsRef}); 1310 1311 return $This; 1312 } 1313 1314 # Detect and set aromaticity for rings... 1315 # 1316 sub _DetectRingsAromaticity { 1317 my($This, @Rings) = @_; 1318 my($RingAtom, $RingBond, $RingAtomsRef); 1319 1320 RING: for $RingAtomsRef (@Rings) { 1321 if (!$This->_CheckRingAromaticity(@{$RingAtomsRef})) { 1322 next RING; 1323 } 1324 $This->_SetRingAromaticity(@{$RingAtomsRef}); 1325 } 1326 return $This; 1327 } 1328 1329 # Set aromatic property for all all atoms and bonds involved in all specified rings.. 1330 # 1331 sub _SetRingsAromaticity { 1332 my($This, @Rings) = @_; 1333 my($RingAtomsRef ); 1334 1335 for $RingAtomsRef (@Rings) { 1336 $This->_SetRingAromaticity(@{$RingAtomsRef}); 1337 } 1338 return $This; 1339 } 1340 1341 # Set aromatic property for all all atoms and bonds involved in ring.. 1342 # 1343 sub _SetRingAromaticity { 1344 my($This, @RingAtoms) = @_; 1345 my($RingAtom, $RingBond); 1346 1347 for $RingAtom (@RingAtoms) { 1348 $RingAtom->SetAromatic(1); 1349 } 1350 for $RingBond ($This->GetRingBonds(@RingAtoms)) { 1351 $RingBond->SetAromatic(1); 1352 } 1353 return $This; 1354 } 1355 1356 1357 # For a ring to be an aromatic ring, all of its atoms must have aromatic property 1358 # set. 1359 # 1360 sub IsRingAromatic { 1361 my($This, @RingAtoms) = @_; 1362 my($RingAtom); 1363 1364 for $RingAtom (@RingAtoms) { 1365 if (!$RingAtom->IsAromatic()) { 1366 return 0; 1367 } 1368 } 1369 return 1; 1370 } 1371 1372 # Delete aromatic property for all atoms and bonds... 1373 # 1374 sub DeleteAromaticity { 1375 my($This) = @_; 1376 1377 return $This->_DeleteAtomsAndBondsAromaticity(); 1378 } 1379 1380 # Check ring aromaticity... 1381 # 1382 sub _CheckRingAromaticity { 1383 my($This, @RingAtoms) = @_; 1384 my($NumOfPiElectrons, @RingBonds); 1385 1386 @RingBonds = (); 1387 @RingBonds = $This->GetRingBonds(@RingAtoms); 1388 1389 $NumOfPiElectrons = $This->_GetNumOfPiElectrons(\@RingAtoms, \@RingBonds); 1390 1391 return $This->_DoPiElectronSatifyHuckelsRule($NumOfPiElectrons); 1392 } 1393 1394 # Get number of pi electrons in the ring... 1395 # 1396 # Notes: 1397 # . A ring containing 4n + 2 pi electrons, Huckel's rule, is considered aromatic. 1398 # 1399 # . Hetrocyclic rings containing N, O and S atoms fall into two classes: Basic aromatic and 1400 # Non-basic atomatic. In Basic aromatic hetrocyclic rings, hetroatom itself is involved 1401 # in a double bond. (e.g. Pyridine) However, in non-basic hetrocyclic rings, hetroatom might have an attached 1402 # hydrogen arom and the remaining lone pair contribute to electron delocalization and 1403 # controbutes to 4n + 2 electrons. (e.g. Pyrrole, Furan) 1404 # 1405 # . Only single or double bonds in the ring. 1406 # 1407 # . Alternate single and double bonds with expception for lone pair contribution by hetro atoms. 1408 # 1409 # . FusedBondsRef is used to avoid double counting of fused bonds. 1410 # 1411 sub _GetNumOfPiElectrons { 1412 my($This, $RingAtomsRef, $RingBondsRef, $FusedBondsVisitedRef) = @_; 1413 my($NumOfPiElectrons, $NumOfDoubleBonds, $NumOfExplicitAromaticBonds, $NumOfHetroAtoms); 1414 1415 ($NumOfPiElectrons, $NumOfDoubleBonds, $NumOfExplicitAromaticBonds, $NumOfHetroAtoms) = (0) x 5; 1416 1417 if (!(defined($RingAtomsRef) && @{$RingAtomsRef})) { 1418 return 0; 1419 } 1420 1421 my($Index, $RingBond, $RingAtom, $RingBondID, $BondOrder, $PreviousIndex, $PreviousRingBond, $PreviousRingAtom, $CommonBondAtom, $NumOfRingBondsProcessed); 1422 1423 $NumOfRingBondsProcessed = 0; 1424 RINGBOND: for $Index (0 .. $#{$RingBondsRef}) { 1425 $RingBond = $RingBondsRef->[$Index]; 1426 $RingAtom = $RingAtomsRef->[$Index]; 1427 1428 # Is this ring bond part of a fused ring system which has been already processed? 1429 if (defined($FusedBondsVisitedRef) && $RingBond->GetNumOfRings() == 2) { 1430 $RingBondID = $RingBond->GetID(); 1431 if (exists $FusedBondsVisitedRef->{$RingBondID}) { 1432 next RINGBOND; 1433 } 1434 $FusedBondsVisitedRef->{$RingBondID} = $RingBondID; 1435 } 1436 1437 $NumOfRingBondsProcessed++; 1438 $BondOrder = $RingBond->GetBondOrder(); 1439 1440 if (($BondOrder > 2) || ($RingAtom->GetNumOfBonds() + $RingAtom->GetNumOfMissingHydrogens()) > 3) { 1441 # It's not an sp2 atom... 1442 return 0; 1443 } 1444 1445 # For first ring, previous ring bond corrresponds to last ring bond... 1446 $PreviousIndex = $Index ? ($Index -1) : $#{$RingBondsRef}; 1447 $PreviousRingBond = $RingBondsRef->[$PreviousIndex]; 1448 $PreviousRingAtom = $RingAtomsRef->[$PreviousIndex]; 1449 1450 if ($BondOrder == 2) { 1451 # To count double bond in pi electron count, make sure it's in an alternate single/double bond configuration... 1452 if ($PreviousRingBond->GetBondOrder() != 1) { 1453 return 0; 1454 } 1455 $NumOfDoubleBonds += 1; 1456 } 1457 elsif ($BondOrder == 1.5) { 1458 # To count explicity set aromatic bond in pi electron count, the previoud bond should also be an explicit aromatic bond... 1459 if ($PreviousRingBond->GetBondOrder() != 1.5) { 1460 return 0; 1461 } 1462 $NumOfExplicitAromaticBonds += 1; 1463 } 1464 elsif ($BondOrder == 1) { 1465 # Contribution from potential hetro atoms... 1466 if ($PreviousRingBond->GetBondOrder() != 1) { 1467 # A potential hetro atom is already involved in a pi bond... 1468 next RINGBOND; 1469 } 1470 $CommonBondAtom = $RingBond->GetCommonAtom($PreviousRingBond); 1471 if (defined $CommonBondAtom) { 1472 if ($CommonBondAtom->IsNitrogen() || $CommonBondAtom->IsOxygen() || $CommonBondAtom->IsSulfur() ) { 1473 # Hetro atom lone pair contributes to pi electrons count... 1474 if ($CommonBondAtom->GetValenceFreeElectrons() >= 2) { 1475 $NumOfHetroAtoms += 1; 1476 } 1477 } 1478 } 1479 } 1480 } 1481 $NumOfPiElectrons = 0; 1482 if ($NumOfExplicitAromaticBonds) { 1483 if (($NumOfExplicitAromaticBonds != $NumOfRingBondsProcessed) || $NumOfDoubleBonds || $NumOfHetroAtoms) { 1484 # For an explicit aromatic bonds, all ring bonds must be explicitly set to aromatic... 1485 return 0; 1486 } 1487 $NumOfPiElectrons = $NumOfExplicitAromaticBonds * 1.5; 1488 } 1489 elsif ($NumOfDoubleBonds > 0) { 1490 # Must have some some double bonds.. 1491 $NumOfPiElectrons = $NumOfDoubleBonds * 2 + $NumOfHetroAtoms * 2; 1492 } 1493 1494 return $NumOfPiElectrons; 1495 } 1496 1497 # Do pi electrons satify huckel's rule: Number of pi electrons correspond to 4n + 2 where 1498 # n is a positive integer... 1499 # 1500 sub _DoPiElectronSatifyHuckelsRule { 1501 my($This, $NumOfPiElectrons) = @_; 1502 1503 $NumOfPiElectrons = $NumOfPiElectrons - 2; 1504 1505 return ($NumOfPiElectrons > 0) ? (($NumOfPiElectrons % 4) ? 0 : 1) : 0; 1506 } 1507 1508 # Delete aromatic property for all atoms and bonds... 1509 # 1510 sub _DeleteAtomsAndBondsAromaticity { 1511 my($This) = @_; 1512 my($Atom, $Bond); 1513 1514 for $Atom ($This->GetAtoms()) { 1515 $Atom->DeleteAromatic(); 1516 } 1517 for $Bond ($This->GetBonds()) { 1518 $Bond->DeleteAromatic(); 1519 } 1520 return $This; 1521 } 1522 1523 # Does molecule contains aromatic rings? 1524 # 1525 sub HasAromaticRings { 1526 my($This) = @_; 1527 1528 return $This->GetNumOfAromaticRings() ? 1 : 0; 1529 } 1530 1531 # Does molecule contains rings? 1532 # 1533 sub HasRings { 1534 my($This) = @_; 1535 1536 return $This->IsCyclic(); 1537 } 1538 1539 # Does molecule contains only one ring? 1540 # 1541 sub HasOnlyOneRing { 1542 my($This) = @_; 1543 1544 return $This->IsUnicyclic(); 1545 } 1546 1547 # Does molecule contains any rings? 1548 # 1549 sub HasNoRings { 1550 my($This) = @_; 1551 1552 return $This->IsAcyclic(); 1553 } 1554 1555 # Get size of smallest ring... 1556 # 1557 sub GetSizeOfSmallestRing { 1558 my($This) = @_; 1559 1560 return $This->GetSizeOfSmallestCycle(); 1561 } 1562 1563 # Get size of largest ring... 1564 # 1565 sub GetSizeOfLargestRing { 1566 my($This) = @_; 1567 1568 return $This->GetSizeOfLargestCycle(); 1569 } 1570 1571 # Get number of rings... 1572 # 1573 sub GetNumOfRings { 1574 my($This) = @_; 1575 1576 return $This->GetNumOfCycles(); 1577 } 1578 1579 # Get number of aromatic rings... 1580 # 1581 sub GetNumOfAromaticRings { 1582 my($This) = @_; 1583 my($NumOfRings); 1584 1585 $NumOfRings = scalar $This->GetAromaticRings(); 1586 1587 return $NumOfRings; 1588 } 1589 1590 # Get num of rings with odd size... 1591 # 1592 sub GetNumOfRingsWithOddSize { 1593 my($This) = @_; 1594 1595 return $This->GetNumOfCyclesWithOddSize(); 1596 } 1597 1598 # Get num of rings with even size... 1599 # 1600 sub GetNumOfRingsWithEvenSize { 1601 my($This) = @_; 1602 1603 return $This->GetNumOfCyclesWithEvenSize(); 1604 } 1605 1606 # Get num of rings with specified size... 1607 # 1608 sub GetNumOfRingsWithSize { 1609 my($This, $RingSize) = @_; 1610 1611 return $This->GetNumOfCyclesWithSize($RingSize); 1612 } 1613 1614 # Get num of rings with size less than a specified size... 1615 # 1616 sub GetNumOfRingsWithSizeLessThan { 1617 my($This, $RingSize) = @_; 1618 1619 return $This->GetNumOfCyclesWithSizeLessThan($RingSize); 1620 } 1621 1622 # Get num of rings with size greater than a specified size... 1623 # 1624 sub GetNumOfRingsWithSizeGreaterThan { 1625 my($This, $RingSize) = @_; 1626 1627 return $This->GetNumOfCyclesWithSizeGreaterThan($RingSize); 1628 } 1629 1630 # Get largest ring as an array containing ring atoms... 1631 # 1632 sub GetLargestRing { 1633 my($This) = @_; 1634 1635 return $This->_GetRing($This->GetLargestCycle()); 1636 } 1637 1638 # Get smallest ring as an array containing ring atoms... 1639 # 1640 sub GetSmallestRing { 1641 my($This) = @_; 1642 1643 return $This->_GetRing($This->GetSmallestCycle()); 1644 } 1645 1646 # Get rings as an array containing references to arrays with ring atoms... 1647 # 1648 sub GetRings { 1649 my($This) = @_; 1650 1651 return $This->_GetRings($This->GetCycles()); 1652 } 1653 1654 # Get aromatic rings as an array containing references to arrays with ring atoms... 1655 # 1656 sub GetAromaticRings { 1657 my($This) = @_; 1658 1659 return $This->_GetAromaticRings($This->GetCycles()); 1660 } 1661 1662 # Get odd size rings as an array containing references to arrays with ring atoms... 1663 # 1664 sub GetRingsWithOddSize { 1665 my($This) = @_; 1666 1667 return $This->_GetRings($This->GetCyclesWithOddSize()); 1668 } 1669 1670 # Get even size rings as an array containing references to arrays with ring atoms... 1671 # 1672 sub GetRingsWithEvenSize { 1673 my($This) = @_; 1674 1675 return $This->_GetRings($This->GetCyclesWithEvenSize()); 1676 } 1677 1678 # Get rings with a specific size as an array containing references to arrays with ring atoms... 1679 # 1680 sub GetRingsWithSize { 1681 my($This, $RingSize) = @_; 1682 1683 return $This->_GetRings($This->GetCyclesWithSize($RingSize)); 1684 } 1685 1686 # Get rings with size less than a specific size as an array containing references to arrays with ring atoms... 1687 # 1688 sub GetRingsWithSizeLessThan { 1689 my($This, $RingSize) = @_; 1690 1691 return $This->_GetRings($This->GetCyclesWithSizeLessThan($RingSize)); 1692 } 1693 1694 # Get rings with size greater than a specific size as an array containing references to arrays with ring atoms... 1695 # 1696 sub GetRingsWithSizeGreaterThan { 1697 my($This, $RingSize) = @_; 1698 1699 return $This->_GetRings($This->GetCyclesWithSizeGreaterThan($RingSize)); 1700 } 1701 1702 # Generate an array of bond objects for an array of ring atoms and return an array 1703 # of bond objects... 1704 # 1705 sub GetRingBonds { 1706 my($This, @RingAtoms) = @_; 1707 my(@Bonds); 1708 1709 @Bonds = (); 1710 if (!@RingAtoms) { 1711 # Return an empty ring bonds list... 1712 return @Bonds; 1713 } 1714 1715 my(@RingAtomIDs); 1716 1717 @RingAtomIDs = (); 1718 @RingAtomIDs = $This->_GetAtomsIDsFromAtoms(@RingAtoms); 1719 if (!@RingAtomIDs) { 1720 carp "Warning: ${ClassName}->GetRingBonds: No ring bonds retrieved: Atom IDs couldn't be retrieved for specified atoms..."; 1721 return @Bonds; 1722 } 1723 1724 # Add start atom to the end to make it a cyclic path for ring: It's taken out during conversion 1725 # of cyclic path to a ring... 1726 push @RingAtomIDs, $RingAtomIDs[0]; 1727 1728 return $This->_GetPathBonds(@RingAtomIDs); 1729 } 1730 1731 # Does molecule has any fused rings? 1732 # 1733 sub HasFusedRings { 1734 my($This) = @_; 1735 1736 return $This->HasFusedCycles(); 1737 } 1738 1739 # Get references to array of fused ring sets and non-fused rings. Fused ring sets array reference 1740 # contains refernces to arrays of rings; Non-fused rings array reference contains references to 1741 # arrays of ring atoms... 1742 # rings. 1743 # 1744 sub GetFusedAndNonFusedRings { 1745 my($This) = @_; 1746 my($FusedCyclesSetsRef, $NonFusedCyclesRef, @FusedRingSets, @NonFusedRings); 1747 1748 @FusedRingSets = (); @NonFusedRings = (); 1749 ($FusedCyclesSetsRef, $NonFusedCyclesRef) = $This->GetFusedAndNonFusedCycles(); 1750 if (!(defined($FusedCyclesSetsRef) && defined($NonFusedCyclesRef))) { 1751 return (\@FusedRingSets, \@NonFusedRings); 1752 } 1753 my($FusedCyclesSetRef); 1754 1755 for $FusedCyclesSetRef (@{$FusedCyclesSetsRef}) { 1756 my(@FusedRingSet); 1757 @FusedRingSet = (); 1758 @FusedRingSet = $This->_GetRings(@{$FusedCyclesSetRef}); 1759 push @FusedRingSets, \@FusedRingSet; 1760 } 1761 1762 @NonFusedRings = $This->_GetRings(@{$NonFusedCyclesRef}); 1763 1764 return (\@FusedRingSets, \@NonFusedRings); 1765 } 1766 1767 # Get rings as an array containing references to arrays with ring atoms... 1768 # 1769 sub _GetRings { 1770 my($This, @CyclicPaths) = @_; 1771 my($CyclicPath, @Rings); 1772 1773 @Rings = (); 1774 if (!@CyclicPaths) { 1775 return @Rings; 1776 } 1777 if (!@CyclicPaths) { 1778 # Return an empty ring list... 1779 return @Rings; 1780 } 1781 1782 for $CyclicPath (@CyclicPaths) { 1783 my(@RingAtoms); 1784 @RingAtoms = (); 1785 push @RingAtoms, $This->_GetRing($CyclicPath); 1786 1787 push @Rings, \@RingAtoms; 1788 } 1789 return @Rings; 1790 } 1791 1792 # Get aromatic rings as an array containing references to arrays with ring atoms... 1793 # 1794 sub _GetAromaticRings { 1795 my($This, @CyclicPaths) = @_; 1796 my($RingAtomsRef, @Rings, @AromaticRings); 1797 1798 @AromaticRings = (); 1799 @Rings = $This->_GetRings(@CyclicPaths); 1800 1801 if (!@Rings) { 1802 return @AromaticRings; 1803 } 1804 RING: for $RingAtomsRef (@Rings) { 1805 if (!$This->IsRingAromatic(@{$RingAtomsRef})) { 1806 next RING; 1807 } 1808 my(@RingAtoms); 1809 @RingAtoms = (); 1810 push @RingAtoms, @{$RingAtomsRef}; 1811 1812 push @AromaticRings, \@RingAtoms; 1813 } 1814 return @AromaticRings; 1815 } 1816 1817 # Map atom IDs in cyclic path to atoms and return a reference to an array containing ring atoms... 1818 # 1819 # Note: 1820 # . Start and end vertex is same for cyclic paths. So end atom is removed before 1821 # returning atoms array as ring atoms... 1822 # 1823 sub _GetRing { 1824 my($This, $CyclicPath) = @_; 1825 my(@RingAtoms); 1826 1827 @RingAtoms = (); 1828 if (!defined $CyclicPath) { 1829 # Return an empty atoms list... 1830 return @RingAtoms; 1831 } 1832 1833 @RingAtoms = $This->_GetPathAtoms($CyclicPath); 1834 if (@RingAtoms) { 1835 pop @RingAtoms; 1836 } 1837 return @RingAtoms; 1838 } 1839 1840 # Map atom IDs to atoms and return a reference to an array containing these atoms... 1841 # 1842 sub _GetPathAtoms { 1843 my($This, $Path) = @_; 1844 my(@PathAtoms); 1845 1846 @PathAtoms = (); 1847 if (!defined $Path) { 1848 carp "Warning: ${ClassName}->_GetPathAtoms: No path atoms retrieved: Path must be defined..."; 1849 return @PathAtoms; 1850 } 1851 my(@AtomIDs); 1852 1853 @AtomIDs = (); 1854 @AtomIDs = $Path->GetVertices(); 1855 1856 @PathAtoms = $This->_GetAtomsFromAtomIDs(@AtomIDs); 1857 1858 return @PathAtoms; 1859 } 1860 1861 # Get bonds for a path specified by atom IDs... 1862 # 1863 sub _GetPathBonds { 1864 my($This, @AtomIDs) = @_; 1865 my($Index, $AtomID1, $AtomID2, @Bonds, @EdgesAtomIDs); 1866 1867 @Bonds = (); @EdgesAtomIDs = (); 1868 1869 if (!@AtomIDs || @AtomIDs == 1) { 1870 return @Bonds; 1871 } 1872 1873 # Setup edges... 1874 for $Index (0 .. ($#AtomIDs - 1) ) { 1875 $AtomID1 = $AtomIDs[$Index]; 1876 $AtomID2 = $AtomIDs[$Index + 1]; 1877 push @EdgesAtomIDs, ($AtomID1, $AtomID2); 1878 } 1879 @Bonds = $This->GetEdgesProperty('Bond', @EdgesAtomIDs); 1880 1881 return @Bonds; 1882 } 1883 1884 # Map atom ID to an atom... 1885 # 1886 sub _GetAtomFromAtomID { 1887 my($This, $AtomID) = @_; 1888 1889 return $This->GetVertexProperty('Atom', $AtomID); 1890 } 1891 1892 # Map atom IDs to atoms and return an array containing these atoms... 1893 # 1894 sub _GetAtomsFromAtomIDs { 1895 my($This, @AtomIDs) = @_; 1896 1897 return $This->GetVerticesProperty('Atom', @AtomIDs); 1898 } 1899 1900 # Map atoms to atom IDs and return an array containing these atoms... 1901 # 1902 sub _GetAtomsIDsFromAtoms { 1903 my($This, @Atoms) = @_; 1904 1905 return map { $_->GetID() } @Atoms; 1906 } 1907 1908 # Is atom in a ring? 1909 # 1910 sub _IsAtomInRing { 1911 my($This, $Atom) = @_; 1912 1913 return $This->IsCyclicVertex($Atom->GetID()); 1914 } 1915 1916 # Is atom not in a ring? 1917 # 1918 sub _IsAtomNotInRing { 1919 my($This, $Atom) = @_; 1920 1921 return $This->IsAcyclicVertex($Atom->GetID()); 1922 } 1923 1924 # Is atom only in one ring? 1925 # 1926 sub _IsAtomInOnlyOneRing { 1927 my($This, $Atom) = @_; 1928 1929 return $This->IsUnicyclicVertex($Atom->GetID()); 1930 } 1931 1932 # Is atom in a ring of specified size? 1933 # 1934 sub _IsAtomInRingOfSize { 1935 my($This, $Atom, $RingSize) = @_; 1936 1937 return $This->GetNumOfVertexCyclesWithSize($Atom->GetID(), $RingSize) ? 1 : 0; 1938 } 1939 1940 # Get size of smallest ring containing specified atom... 1941 # 1942 sub _GetSizeOfSmallestAtomRing { 1943 my($This, $Atom) = @_; 1944 1945 return $This->GetSizeOfSmallestVertexCycle($Atom->GetID()); 1946 } 1947 1948 # Get size of largest ring containing specified atom... 1949 # 1950 sub _GetSizeOfLargestAtomRing { 1951 my($This, $Atom) = @_; 1952 1953 return $This->GetSizeOfLargestVertexCycle($Atom->GetID()); 1954 } 1955 1956 # Get number of rings containing specified atom... 1957 # 1958 sub _GetNumOfAtomRings { 1959 my($This, $Atom) = @_; 1960 1961 return $This->GetNumOfVertexCycles($Atom->GetID()); 1962 } 1963 1964 # Get number of rings with odd size containing specified atom... 1965 # 1966 sub _GetNumOfAtomRingsWithOddSize { 1967 my($This, $Atom) = @_; 1968 1969 return $This->GetNumOfVertexCyclesWithOddSize($Atom->GetID()); 1970 } 1971 1972 # Get number of rings with even size containing specified atom... 1973 # 1974 sub _GetNumOfAtomRingsWithEvenSize { 1975 my($This, $Atom) = @_; 1976 1977 return $This->GetNumOfVertexCyclesWithEvenSize($Atom->GetID()); 1978 } 1979 1980 # Get number of rings with specified size containing specified atom... 1981 # 1982 sub _GetNumOfAtomRingsWithSize { 1983 my($This, $Atom, $RingSize) = @_; 1984 1985 return $This->GetNumOfVertexCyclesWithSize($Atom->GetID(), $RingSize); 1986 } 1987 1988 # Get number of rings with size less than specified containing specified atom... 1989 # 1990 sub _GetNumOfAtomRingsWithSizeLessThan { 1991 my($This, $Atom, $RingSize) = @_; 1992 1993 return $This->GetNumOfVertexCyclesWithSizeLessThan($Atom->GetID(), $RingSize); 1994 } 1995 1996 # Get number of rings with size greater than specified containing specified atom... 1997 # 1998 sub _GetNumOfAtomRingsWithSizeGreaterThan { 1999 my($This, $Atom, $RingSize) = @_; 2000 2001 return $This->GetNumOfVertexCyclesWithSizeGreaterThan($Atom->GetID(), $RingSize); 2002 } 2003 2004 # Get smallest ring as an array containing ring atoms... 2005 # 2006 sub _GetSmallestAtomRing { 2007 my($This, $Atom) = @_; 2008 2009 return $This->_GetRing($This->GetSmallestVertexCycle($Atom->GetID())); 2010 } 2011 2012 # Get odd size rings an array of references to arrays containing ring atoms... 2013 # 2014 sub _GetLargestAtomRing { 2015 my($This, $Atom) = @_; 2016 2017 return $This->_GetRing($This->GetLargestVertexCycle($Atom->GetID())); 2018 } 2019 2020 # Get all rings an array of references to arrays containing ring atoms... 2021 # 2022 sub _GetAtomRings { 2023 my($This, $Atom) = @_; 2024 2025 return $This->_GetRings($This->GetVertexCycles($Atom->GetID())); 2026 } 2027 2028 # Get odd size rings an array of references to arrays containing ring atoms... 2029 # 2030 sub _GetAtomRingsWithOddSize { 2031 my($This, $Atom) = @_; 2032 2033 return $This->_GetRings($This->GetVertexCyclesWithOddSize($Atom->GetID())); 2034 } 2035 2036 # Get even size rings an array of references to arrays containing ring atoms... 2037 # 2038 sub _GetAtomRingsWithEvenSize { 2039 my($This, $Atom) = @_; 2040 2041 return $This->_GetRings($This->GetVertexCyclesWithEvenSize($Atom->GetID())); 2042 } 2043 2044 # Get rings with specified size an array of references to arrays containing ring atoms... 2045 # 2046 sub _GetAtomRingsWithSize { 2047 my($This, $Atom, $RingSize) = @_; 2048 2049 return $This->_GetRings($This->GetVertexCyclesWithSize($Atom->GetID(), $RingSize)); 2050 } 2051 2052 # Get rings with size less than specfied size as an array of references to arrays containing ring atoms... 2053 # 2054 sub _GetAtomRingsWithSizeLessThan { 2055 my($This, $Atom, $RingSize) = @_; 2056 2057 return $This->_GetRings($This->GetVertexCyclesWithSizeLessThan($Atom->GetID(), $RingSize)); 2058 } 2059 2060 # Get rings with size less than specfied size as an array of references to arrays containing ring atoms... 2061 # 2062 sub _GetAtomRingsWithSizeGreaterThan { 2063 my($This, $Atom, $RingSize) = @_; 2064 2065 return $This->_GetRings($This->GetVertexCyclesWithSizeGreaterThan($Atom->GetID(), $RingSize)); 2066 } 2067 2068 # Is bond in a ring? 2069 # 2070 sub _IsBondInRing { 2071 my($This, $Bond) = @_; 2072 my($Atom1, $Atom2); 2073 2074 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2075 2076 return $This->IsCyclicEdge($Atom1->GetID(), $Atom2->GetID()); 2077 } 2078 2079 # Is bond not in a ring? 2080 # 2081 sub _IsBondNotInRing { 2082 my($This, $Bond) = @_; 2083 my($Atom1, $Atom2); 2084 2085 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2086 2087 return $This->IsAcyclicEdge($Atom1->GetID(), $Atom2->GetID()); 2088 } 2089 2090 # Is bond only in one ring? 2091 # 2092 sub _IsBondInOnlyOneRing { 2093 my($This, $Bond) = @_; 2094 my($Atom1, $Atom2); 2095 2096 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2097 2098 return $This->IsUnicyclicEdge($Atom1->GetID(), $Atom2->GetID()); 2099 } 2100 2101 # Is bond in a ring of specified size? 2102 # 2103 sub _IsBondInRingOfSize { 2104 my($This, $Bond, $RingSize) = @_; 2105 my($Atom1, $Atom2); 2106 2107 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2108 2109 return $This->GetNumOfEdgeCyclesWithSize($Atom1->GetID(), $Atom2->GetID(), $RingSize) ? 1 : 0; 2110 } 2111 2112 # Get size of smallest ring containing specified bond... 2113 # 2114 sub _GetSizeOfSmallestBondRing { 2115 my($This, $Bond) = @_; 2116 my($Atom1, $Atom2); 2117 2118 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2119 2120 return $This->GetSizeOfSmallestEdgeCycle($Atom1->GetID(), $Atom2->GetID()); 2121 } 2122 2123 # Get size of largest ring containing specified bond... 2124 # 2125 sub _GetSizeOfLargestBondRing { 2126 my($This, $Bond) = @_; 2127 my($Atom1, $Atom2); 2128 2129 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2130 2131 return $This->GetSizeOfLargestEdgeCycle($Atom1->GetID(), $Atom2->GetID()); 2132 } 2133 2134 # Get number of rings containing specified bond... 2135 # 2136 sub _GetNumOfBondRings { 2137 my($This, $Bond) = @_; 2138 my($Atom1, $Atom2); 2139 2140 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2141 2142 return $This->GetNumOfEdgeCycles($Atom1->GetID(), $Atom2->GetID()); 2143 } 2144 2145 # Get number of rings with odd size containing specified bond... 2146 # 2147 sub _GetNumOfBondRingsWithOddSize { 2148 my($This, $Bond) = @_; 2149 my($Atom1, $Atom2); 2150 2151 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2152 2153 return $This->GetNumOfEdgeCyclesWithOddSize($Atom1->GetID(), $Atom2->GetID()); 2154 } 2155 2156 # Get number of rings with even size containing specified bond... 2157 # 2158 sub _GetNumOfBondRingsWithEvenSize { 2159 my($This, $Bond) = @_; 2160 my($Atom1, $Atom2); 2161 2162 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2163 2164 return $This->GetNumOfEdgeCyclesWithEvenSize($Atom1->GetID(), $Atom2->GetID()); 2165 } 2166 2167 # Get number of rings with specified size containing specified bond... 2168 # 2169 sub _GetNumOfBondRingsWithSize { 2170 my($This, $Bond, $RingSize) = @_; 2171 my($Atom1, $Atom2); 2172 2173 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2174 2175 return $This->GetNumOfEdgeCyclesWithSize($Atom1->GetID(), $Atom2->GetID(), $RingSize); 2176 } 2177 2178 # Get number of rings with size less than specified containing specified bond... 2179 # 2180 sub _GetNumOfBondRingsWithSizeLessThan { 2181 my($This, $Bond, $RingSize) = @_; 2182 my($Atom1, $Atom2); 2183 2184 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2185 2186 return $This->GetNumOfEdgeCyclesWithSizeLessThan($Atom1->GetID(), $Atom2->GetID(), $RingSize); 2187 } 2188 2189 # Get number of rings with size greater than specified containing specified bond... 2190 # 2191 sub _GetNumOfBondRingsWithSizeGreaterThan { 2192 my($This, $Bond, $RingSize) = @_; 2193 my($Atom1, $Atom2); 2194 2195 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2196 2197 return $This->GetNumOfEdgeCyclesWithSizeGreaterThan($Atom1->GetID(), $Atom2->GetID(), $RingSize); 2198 } 2199 2200 # Get smallest ring as an array containing ring atoms... 2201 # 2202 sub _GetSmallestBondRing { 2203 my($This, $Bond) = @_; 2204 my($Atom1, $Atom2); 2205 2206 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2207 2208 return $This->_GetRing($This->GetSmallestEdgeCycle($Atom1->GetID(), $Atom2->GetID())); 2209 } 2210 2211 # Get odd size rings an array of references to arrays containing ring atoms... 2212 # 2213 sub _GetLargestBondRing { 2214 my($This, $Bond) = @_; 2215 my($Atom1, $Atom2); 2216 2217 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2218 2219 return $This->_GetRing($This->GetLargestEdgeCycle($Atom1->GetID(), $Atom2->GetID())); 2220 } 2221 2222 # Get all rings an array of references to arrays containing ring atoms... 2223 # 2224 sub _GetBondRings { 2225 my($This, $Bond) = @_; 2226 my($Atom1, $Atom2); 2227 2228 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2229 2230 return $This->_GetRings($This->GetEdgeCycles($Atom1->GetID(), $Atom2->GetID())); 2231 } 2232 2233 # Get odd size rings an array of references to arrays containing ring atoms... 2234 # 2235 sub _GetBondRingsWithOddSize { 2236 my($This, $Bond) = @_; 2237 my($Atom1, $Atom2); 2238 2239 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2240 2241 return $This->_GetRings($This->GetEdgeCyclesWithOddSize($Atom1->GetID(), $Atom2->GetID())); 2242 } 2243 2244 # Get even size rings an array of references to arrays containing ring atoms... 2245 # 2246 sub _GetBondRingsWithEvenSize { 2247 my($This, $Bond) = @_; 2248 my($Atom1, $Atom2); 2249 2250 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2251 2252 return $This->_GetRings($This->GetEdgeCyclesWithEvenSize($Atom1->GetID(), $Atom2->GetID())); 2253 } 2254 2255 # Get rings with specified size an array of references to arrays containing ring atoms... 2256 # 2257 sub _GetBondRingsWithSize { 2258 my($This, $Bond, $RingSize) = @_; 2259 my($Atom1, $Atom2); 2260 2261 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2262 2263 return $This->_GetRings($This->GetEdgeCyclesWithSize($Atom1->GetID(), $Atom2->GetID(), $RingSize)); 2264 } 2265 2266 # Get rings with size less than specfied size as an array of references to arrays containing ring atoms... 2267 # 2268 sub _GetBondRingsWithSizeLessThan { 2269 my($This, $Bond, $RingSize) = @_; 2270 my($Atom1, $Atom2); 2271 2272 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2273 2274 return $This->_GetRings($This->GetEdgeCyclesWithSizeLessThan($Atom1->GetID(), $Atom2->GetID(), $RingSize)); 2275 } 2276 2277 # Get rings with size less than specfied size as an array of references to arrays containing ring atoms... 2278 # 2279 sub _GetBondRingsWithSizeGreaterThan { 2280 my($This, $Bond, $RingSize) = @_; 2281 my($Atom1, $Atom2); 2282 2283 ($Atom1, $Atom2) = $Bond->GetAtoms(); 2284 2285 return $This->_GetRings($This->GetEdgeCyclesWithSizeGreaterThan($Atom1->GetID(), $Atom2->GetID(), $RingSize)); 2286 } 2287 2288 2289 # Get atom paths starting from a specified atom as a reference to an array containing references 2290 # to arrays with path atoms. 2291 # 2292 # Path atoms atoms correspond to to all possible paths for specified atom in molecule with length 2293 # upto a specified length and sharing of bonds in paths traversed. By default, rings are 2294 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2295 # 2296 # Note: 2297 # . For molecule without any rings, this method returns the same set of atom paths 2298 # as GetAtomPathsStartingAtWithLengthUpto method. 2299 # 2300 sub GetAllAtomPathsStartingAtWithLengthUpto { 2301 my($This, $StartAtom, $Length, $AllowCycles) = @_; 2302 2303 return $This->_GetAtomPathsStartingAt('AllAtomPathsWithLengthUpto', $StartAtom, $Length, $AllowCycles); 2304 } 2305 2306 # Get atom paths starting from a specified atom as a reference to an array containing references 2307 # to arrays with path atoms. 2308 # 2309 # Path atoms atoms correspond to to all possible paths for specified atom in molecule with 2310 # specified length and sharing of bonds in paths traversed. By default, rings are 2311 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2312 # 2313 # Note: 2314 # . For molecule without any rings, this method returns the same set of atom paths 2315 # as GetAtomPathsStartingAtWithLengthUpto method. 2316 # 2317 sub GetAllAtomPathsStartingAtWithLength { 2318 my($This, $StartAtom, $Length, $AllowCycles) = @_; 2319 2320 return $This->_GetAtomPathsStartingAt('AllAtomPathsWithLength', $StartAtom, $Length, $AllowCycles); 2321 } 2322 2323 # Get atom paths starting from a specified atom as a reference to an array containing references 2324 # to arrays with path atoms. 2325 # 2326 # Path atoms atoms correspond to to all possible paths for specified atom in molecule with all 2327 # possible lengths and sharing of bonds in paths traversed. By default, rings are 2328 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2329 # 2330 # Note: 2331 # . For molecule without any rings, this method returns the same set of atom paths 2332 # as GetAtomPathsStartingAt method. 2333 # 2334 sub GetAllAtomPathsStartingAt { 2335 my($This, $StartAtom, $AllowCycles) = @_; 2336 2337 return $This->_GetAtomPathsStartingAt('AllAtomPathsWithAllLengths', $StartAtom, undef, $AllowCycles); 2338 } 2339 2340 # Get atom paths starting from a specified atom as a reference to an array containing references 2341 # to arrays with path atoms. 2342 # 2343 # Path atoms atoms correspond to to all possible paths for specified atom in molecule with length 2344 # upto a specified length and no sharing of bonds in paths traversed. By default, rings are 2345 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2346 # 2347 sub GetAtomPathsStartingAtWithLengthUpto { 2348 my($This, $StartAtom, $Length, $AllowCycles) = @_; 2349 2350 return $This->_GetAtomPathsStartingAt('AtomPathsWithLengthUpto', $StartAtom, $Length, $AllowCycles); 2351 } 2352 2353 # Get atom paths starting from a specified atom as a reference to an array containing references 2354 # to arrays with path atoms. 2355 # 2356 # Path atoms atoms correspond to to all possible paths for specified atom in molecule with 2357 # specified length and no sharing of bonds in paths traversed. By default, rings are 2358 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2359 # 2360 sub GetAtomPathsStartingAtWithLength { 2361 my($This, $StartAtom, $Length, $AllowCycles) = @_; 2362 2363 return $This->_GetAtomPathsStartingAt('AtomPathsWithLength', $StartAtom, $Length, $AllowCycles); 2364 } 2365 2366 # Get atom paths starting from a specified atom as a reference to an array containing references 2367 # to arrays with path atoms. 2368 # 2369 # Path atoms atoms correspond to to all possible paths for specified atom in molecule with all 2370 # possible lengths and no sharing of bonds in paths traversed. By default, rings are 2371 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2372 # 2373 # 2374 sub GetAtomPathsStartingAt { 2375 my($This, $StartAtom, $AllowCycles) = @_; 2376 2377 return $This->_GetAtomPathsStartingAt('AtomPathsWithAllLengths', $StartAtom, undef, $AllowCycles); 2378 } 2379 2380 # Get atom paths as an array containing references to arrays with path atoms... 2381 # 2382 sub _GetAtomPathsStartingAt { 2383 my($This, $Mode, $StartAtom, $Length, $AllowCycles) = @_; 2384 my(@AtomPaths); 2385 2386 @AtomPaths = (); 2387 if (!defined $StartAtom) { 2388 carp "Warning: ${ClassName}->_GetAtomPathsStartingAt: No atom paths retrieved: Start atom is not defined..."; 2389 return @AtomPaths; 2390 } 2391 if (!$This->HasAtom($StartAtom)) { 2392 carp "Warning: ${ClassName}->_GetAtomPathsStartingAt: No atom paths retrieved: Start atom doesn't exist..."; 2393 return @AtomPaths; 2394 } 2395 my($StartAtomID, @Paths); 2396 2397 $StartAtomID = $StartAtom->GetID(); 2398 @Paths = (); 2399 2400 # Collect appropriate atom paths... 2401 MODE: { 2402 if ($Mode =~ /^AtomPathsWithLengthUpto$/i) { @Paths = $This->GetPathsStartingAtWithLengthUpto($StartAtomID, $Length, $AllowCycles); last MODE; } 2403 if ($Mode =~ /^AtomPathsWithLength$/i) { @Paths = $This->GetPathsStartingAtWithLength($StartAtomID, $Length, $AllowCycles); last MODE; } 2404 if ($Mode =~ /^AtomPathsWithAllLengths$/i) { @Paths = $This->GetPathsStartingAt($StartAtomID, $AllowCycles); last MODE; } 2405 2406 if ($Mode =~ /^AllAtomPathsWithLengthUpto$/i) { @Paths = $This->GetAllPathsStartingAtWithLengthUpto($StartAtomID, $Length, $AllowCycles); last MODE; } 2407 if ($Mode =~ /^AllAtomPathsWithLength$/i) { @Paths = $This->GetAllPathsStartingAtWithLength($StartAtomID, $Length, $AllowCycles); last MODE; } 2408 if ($Mode =~ /^AllAtomPathsWithAllLengths$/i) { @Paths = $This->GetAllPathsStartingAt($StartAtomID, $AllowCycles); last MODE; } 2409 2410 print "Warn: ${ClassName}->_GetAtomPathsStartingAt: No atom paths retrieved: Mode, $Mode, is not supported..."; 2411 return @AtomPaths; 2412 } 2413 return $This->_GetAtomPathsFromPaths(\@Paths); 2414 } 2415 2416 # Get atom paths for all atoms as a reference to an array containing references to arrays with 2417 # path atoms. 2418 # 2419 # Path atoms correspond to to all possible paths for each atom in molecule with length 2420 # upto a specified length and sharing of bonds in paths traversed. By default, rings are 2421 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2422 # 2423 # Notes: 2424 # . For molecule without any rings, this method returns the same set of atom paths 2425 # as GetAtomPathsWithLengthUpto method. 2426 # 2427 sub GetAllAtomPathsWithLengthUpto { 2428 my($This, $Length, $AllowCycles) = @_; 2429 2430 return $This->_GetAtomPaths('AllAtomPathsWithLengthUpto', $Length, $AllowCycles); 2431 } 2432 2433 # Get atom paths for all atoms as a reference to an array containing references to arrays with 2434 # path atoms. 2435 # 2436 # Path atoms correspond to to all possible paths for each atom in molecule with 2437 # a specified length and sharing of bonds in paths traversed. By default, rings are 2438 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2439 # 2440 # Notes: 2441 # . For molecule without any rings, this method returns the same set of atom paths 2442 # as GetAtomPathsWithLengthUpto method. 2443 # 2444 sub GetAllAtomPathsWithLength { 2445 my($This, $Length, $AllowCycles) = @_; 2446 2447 return $This->_GetAtomPaths('AllAtomPathsWithLength', $Length, $AllowCycles); 2448 } 2449 2450 # Get atom paths for all atoms as a reference to an array containing references to arrays with 2451 # path atoms. 2452 # 2453 # Path atoms correspond to to all possible paths for each atom in molecule with all 2454 # possible lengths and sharing of bonds in paths traversed. By default, rings are 2455 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2456 # 2457 # Notes: 2458 # . For molecule without any rings, this method returns the same set of atom paths 2459 # as GetAtomPaths method. 2460 # 2461 sub GetAllAtomPaths { 2462 my($This, $AllowCycles) = @_; 2463 2464 return $This->_GetAtomPaths('AllAtomPathsWithAllLengths', undef, $AllowCycles); 2465 } 2466 2467 # Get atom paths for all atoms as a reference to an array containing references to arrays with 2468 # path atoms. 2469 # 2470 # Path atoms correspond to to all possible paths for each atom in molecule with length 2471 # upto a specified length and no sharing of bonds in paths traversed. By default, rings are 2472 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2473 # 2474 sub GetAtomPathsWithLengthUpto { 2475 my($This, $Length, $AllowCycles) = @_; 2476 2477 return $This->_GetAtomPaths('AtomPathsWithLengthUpto', $Length, $AllowCycles); 2478 } 2479 2480 # Get atom paths for all atoms as a reference to an array containing references to arrays with 2481 # path atoms. 2482 # 2483 # Path atoms correspond to to all possible paths for each atom in molecule with 2484 # a specified length and no sharing of bonds in paths traversed. By default, rings are 2485 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2486 # 2487 sub GetAtomPathsWithLength { 2488 my($This, $Length, $AllowCycles) = @_; 2489 2490 return $This->_GetAtomPaths('AtomPathsWithLength', $Length, $AllowCycles); 2491 } 2492 2493 2494 # Get atom paths for all atoms as a reference to an array containing references to arrays with 2495 # path atoms. 2496 # 2497 # Path atoms correspond to to all possible paths for each atom in molecule with all 2498 # possible lengths and no sharing of bonds in paths traversed. By default, rings are 2499 # included in paths. A path containing a ring is terminated at an atom completing the ring. 2500 # 2501 sub GetAtomPaths { 2502 my($This, $AllowCycles) = @_; 2503 2504 return $This->_GetAtomPaths('AtomPathsWithAllLengths', undef, $AllowCycles); 2505 } 2506 2507 # Get atom paths for all atoms as a reference to an array containing references to arrays with 2508 # path atoms. 2509 # 2510 sub _GetAtomPaths { 2511 my($This, $Mode, $Length, $AllowCycles) = @_; 2512 my($PathsRef, @AtomPaths); 2513 2514 @AtomPaths = (); 2515 # Collect appropriate atom paths... 2516 MODE: { 2517 if ($Mode =~ /^AtomPathsWithLengthUpto$/i) { $PathsRef = $This->GetPathsWithLengthUpto($Length, $AllowCycles); last MODE; } 2518 if ($Mode =~ /^AtomPathsWithLength$/i) { $PathsRef = $This->GetPathsWithLength($Length, $AllowCycles); last MODE; } 2519 if ($Mode =~ /^AtomPathsWithAllLengths$/i) { $PathsRef = $This->GetPaths($AllowCycles); last MODE; } 2520 2521 if ($Mode =~ /^AllAtomPathsWithLengthUpto$/i) { $PathsRef = $This->GetAllPathsWithLengthUpto($Length, $AllowCycles); last MODE; } 2522 if ($Mode =~ /^AllAtomPathsWithLength$/i) { $PathsRef = $This->GetAllPathsWithLength($Length, $AllowCycles); last MODE; } 2523 if ($Mode =~ /^AllAtomPathsWithAllLengths$/i) { $PathsRef = $This->GetAllPaths($AllowCycles); last MODE; } 2524 2525 print "Warn: ${ClassName}->_GetAtomPaths: No atom paths retrieved: Mode, $Mode, is not supported..."; 2526 return \@AtomPaths; 2527 } 2528 return $This->_GetAtomPathsFromPaths($PathsRef); 2529 } 2530 2531 # Get atom paths as an array reference containing references to arrays with path atoms... 2532 # 2533 sub _GetAtomPathsFromPaths { 2534 my($This, $PathsRef) = @_; 2535 my($Path, @AtomPaths); 2536 2537 @AtomPaths = (); 2538 if (!defined $PathsRef) { 2539 return \@AtomPaths; 2540 } 2541 if (!@{$PathsRef}) { 2542 # Return an empty atom paths list... 2543 return \@AtomPaths; 2544 } 2545 for $Path (@{$PathsRef}) { 2546 my(@PathAtoms); 2547 @PathAtoms = (); 2548 @PathAtoms = $This->_GetAtomPathFromPath($Path); 2549 2550 push @AtomPaths, \@PathAtoms; 2551 } 2552 return \@AtomPaths; 2553 } 2554 2555 # Generate an array of bond objects for an array of path atoms and return an array 2556 # of bond objects... 2557 # 2558 sub GetAtomPathBonds { 2559 my($This, @PathAtoms) = @_; 2560 my(@Bonds); 2561 2562 if (!@PathAtoms) { 2563 # Return an empty ring bonds list... 2564 return @Bonds; 2565 } 2566 my(@PathAtomIDs); 2567 2568 @PathAtomIDs = (); 2569 @PathAtomIDs = $This->_GetAtomsIDsFromAtoms(@PathAtoms); 2570 2571 return $This->_GetPathBonds(@PathAtomIDs); 2572 } 2573 2574 # Map atom IDs in path to atoms and return a reference to an array containing ring atoms... 2575 # 2576 sub _GetAtomPathFromPath { 2577 my($This, $Path) = @_; 2578 my(@PathAtoms); 2579 2580 @PathAtoms = (); 2581 if (!defined $Path) { 2582 # Return an empty atoms list... 2583 return @PathAtoms; 2584 } 2585 2586 return $This->_GetPathAtoms($Path); 2587 } 2588 2589 # Get atom paths between two specified atoms as a reference to an array containing references 2590 # to arrays with path atoms. For molecules with rings, atom paths array contains may contain 2591 # two paths. 2592 # 2593 sub GetAtomPathsBetween { 2594 my($This, $StartAtom, $EndAtom) = @_; 2595 my(@AtomPaths); 2596 2597 @AtomPaths = (); 2598 if (!(defined($StartAtom) && $This->HasAtom($StartAtom))) { 2599 carp "Warning: ${ClassName}->_GetAtomPathsBetween: No atom paths retrieved: Start atom is not defined or it doesn't exist..."; 2600 return @AtomPaths; 2601 } 2602 if (!(defined($EndAtom) && $This->HasAtom($EndAtom))) { 2603 carp "Warning: ${ClassName}->_GetAtomPathsBetween: No atom paths retrieved: End atom is not defined or it doesn't exist..."; 2604 return @AtomPaths; 2605 } 2606 return $This->_GetAtomPathsBetween($StartAtom, $EndAtom); 2607 } 2608 2609 # Get atom paths between two specified atoms as a reference to an array containing references 2610 # to arrays with path atoms. 2611 # 2612 sub _GetAtomPathsBetween { 2613 my($This, $StartAtom, $EndAtom) = @_; 2614 my($StartAtomID, $EndAtomID, @Paths); 2615 2616 $StartAtomID = $StartAtom->GetID(); 2617 $EndAtomID = $EndAtom->GetID(); 2618 2619 @Paths = (); 2620 @Paths = $This->GetPathsBetween($StartAtomID, $EndAtomID); 2621 2622 return $This->_GetAtomPathsFromPaths(\@Paths); 2623 } 2624 2625 # Get atom neighborhoods around a specified atom as an array containing references 2626 # to arrays with neighborhood atoms at different radii upto specified radius... 2627 # 2628 sub GetAtomNeighborhoodsWithRadiusUpto { 2629 my($This, $StartAtom, $Radius) = @_; 2630 2631 return $This->_GetAtomNeighborhoods('RadiusUpto', $StartAtom, $Radius); 2632 } 2633 2634 # Get atom neighborhoods around a specified atom as an array containing references 2635 # to arrays with neighborhood atoms at possible radii... 2636 # 2637 sub GetAtomNeighborhoods { 2638 my($This, $StartAtom) = @_; 2639 2640 return $This->_GetAtomNeighborhoods('AllRadii', $StartAtom, undef); 2641 } 2642 2643 # Get atom neighborhood around a specified atom, along with their successor connected atoms, collected 2644 # with in a specified radius as a list containing references to lists with first value corresponding to neighborhood 2645 # atom at a specific radius and second value as reference to a list containing its successor connected atoms. 2646 # 2647 # For a neighborhood atom at each radius level, the successor connected atoms correspond to the 2648 # neighborhood atoms at the next radius level. Consequently, the neighborhood atoms at the last 2649 # radius level don't contain any successor atoms which fall outside the range of specified radius. 2650 # 2651 sub GetAtomNeighborhoodsWithSuccessorAtomsAndRadiusUpto { 2652 my($This, $StartAtom, $Radius) = @_; 2653 2654 return $This->_GetAtomNeighborhoods('WithSuccessorsAndRadiusUpto', $StartAtom, $Radius); 2655 } 2656 2657 # Get atom neighborhood around a specified atom, along with their successor connected atoms, collected 2658 # at all radii as a list containing references to lists with first value corresponding to neighborhood 2659 # atom at a specific radius and second value as reference to a list containing its successor connected atoms. 2660 # 2661 # For a neighborhood atom at each radius level, the successor connected atoms correspond to the 2662 # neighborhood atoms at the next radius level. Consequently, the neighborhood atoms at the last 2663 # radius level don't contain any successor atoms which fall outside the range of specified radius. 2664 # 2665 # 2666 sub GetAtomNeighborhoodsWithSuccessorAtoms { 2667 my($This, $StartAtom) = @_; 2668 2669 return $This->_GetAtomNeighborhoods('WithSuccessorsAndAllRadii', $StartAtom, undef); 2670 } 2671 2672 # Get atom neighborhoods... 2673 # 2674 sub _GetAtomNeighborhoods { 2675 my($This, $Mode, $StartAtom, $Radius) = @_; 2676 my(@AtomNeighborhoods); 2677 2678 @AtomNeighborhoods = (); 2679 2680 if (!(defined($StartAtom) && $This->HasAtom($StartAtom))) { 2681 carp "Warning: ${ClassName}->_GetAtomNeighborhoods: No atom neighborhoods retrieved: Start atom is not defined or it doesn't exist..."; 2682 return @AtomNeighborhoods; 2683 } 2684 if ($Mode =~ /^(RadiusUpto|WithSuccessorsAndRadiusUpto)$/i) { 2685 if (!(defined($Radius) && $Radius > 0)) { 2686 carp "Warning: ${ClassName}->_GetAtomNeighborhoods: No atom neighborhoods retrieved: Radius is not defined or it's <= 0 ..."; 2687 return @AtomNeighborhoods; 2688 } 2689 } 2690 2691 # Collect neighborhood atom IDs... 2692 my($StartAtomID, @NeighborhoodAtomIDs, @NeighborhoodAtomIDsWithSuccessors); 2693 2694 @NeighborhoodAtomIDs = (); @NeighborhoodAtomIDsWithSuccessors = (); 2695 $StartAtomID = $StartAtom->GetID(); 2696 2697 MODE: { 2698 if ($Mode =~ /^RadiusUpto$/i) { @NeighborhoodAtomIDs = $This->GetNeighborhoodVerticesWithRadiusUpto($StartAtomID, $Radius); last MODE; } 2699 if ($Mode =~ /^AllRadii$/i) { @NeighborhoodAtomIDs = $This->GetNeighborhoodVertices($StartAtomID); last MODE; } 2700 2701 if ($Mode =~ /^WithSuccessorsAndRadiusUpto$/i) { @NeighborhoodAtomIDsWithSuccessors = $This->GetNeighborhoodVerticesWithSuccessorsAndRadiusUpto($StartAtomID, $Radius); last MODE; } 2702 if ($Mode =~ /^WithSuccessorsAndAllRadii$/i) { @NeighborhoodAtomIDsWithSuccessors = $This->GetNeighborhoodVerticesWithSuccessors($StartAtomID); last MODE; } 2703 2704 print "Warn: ${ClassName}->_GetAtomNeighborhood: No atom neighborhoods retrieved: Mode, $Mode, is not supported..."; 2705 return @AtomNeighborhoods; 2706 } 2707 if ($Mode =~ /^(RadiusUpto|AllRadii)$/i) { 2708 return $This->_GetNeighborhoodAtomsFromAtomIDs(\@NeighborhoodAtomIDs); 2709 } 2710 elsif ($Mode =~ /^(WithSuccessorsAndRadiusUpto|WithSuccessorsAndAllRadii)$/i) { 2711 return $This->_GetNeighborhoodAtomsWithSuccessorsFromAtomIDs(\@NeighborhoodAtomIDsWithSuccessors); 2712 } 2713 2714 return @AtomNeighborhoods; 2715 } 2716 2717 # Map neighborhood atom IDs to atoms... 2718 # 2719 sub _GetNeighborhoodAtomsFromAtomIDs { 2720 my($This, $NeighborhoodsAtomIDsRef) = @_; 2721 my($NeighborhoodAtomIDsRef, @AtomNeighborhoods); 2722 2723 @AtomNeighborhoods = (); 2724 for $NeighborhoodAtomIDsRef (@{$NeighborhoodsAtomIDsRef}) { 2725 my(@AtomNeighborhood); 2726 2727 @AtomNeighborhood = (); 2728 @AtomNeighborhood = $This->_GetAtomsFromAtomIDs(@{$NeighborhoodAtomIDsRef}); 2729 push @AtomNeighborhoods, \@AtomNeighborhood; 2730 } 2731 return @AtomNeighborhoods; 2732 } 2733 2734 # Map neighborhood atom IDs with successors to atoms... 2735 # 2736 sub _GetNeighborhoodAtomsWithSuccessorsFromAtomIDs { 2737 my($This, $NeighborhoodsAtomIDsWithSuccessorsRef) = @_; 2738 my($Depth, $NeighborhoodAtomIDsWithSuccessorsRef, $NeighborhoodAtomIDWithSuccessorsRef, $NeighborhoodAtomID, $NeighborhoodAtomSuccessorsIDsRef, @AtomNeighborhoods); 2739 2740 $Depth = 0; 2741 @AtomNeighborhoods = (); 2742 2743 # Go over neighborhoods at each level... 2744 for $NeighborhoodAtomIDsWithSuccessorsRef (@{$NeighborhoodsAtomIDsWithSuccessorsRef}) { 2745 @{$AtomNeighborhoods[$Depth]} = (); 2746 2747 # Go over the neighborhood atoms and their successors at a specific level.. 2748 for $NeighborhoodAtomIDWithSuccessorsRef (@{$NeighborhoodAtomIDsWithSuccessorsRef}) { 2749 my($NeighborhoodAtom, @NeighborhoodAtomWithSuccessors, @NeighborhoodAtomSuccessorAtoms); 2750 2751 @NeighborhoodAtomWithSuccessors = (); @NeighborhoodAtomSuccessorAtoms = (); 2752 ($NeighborhoodAtomID, $NeighborhoodAtomSuccessorsIDsRef) = @{$NeighborhoodAtomIDWithSuccessorsRef}; 2753 2754 # Map atom IDs to atoms... 2755 $NeighborhoodAtom = $This->_GetAtomFromAtomID($NeighborhoodAtomID); 2756 if (@{$NeighborhoodAtomSuccessorsIDsRef}) { 2757 @NeighborhoodAtomSuccessorAtoms = $This->_GetAtomsFromAtomIDs(@{$NeighborhoodAtomSuccessorsIDsRef}); 2758 } 2759 2760 # Store an atom and its successors at each level in an array... 2761 push @NeighborhoodAtomWithSuccessors, ($NeighborhoodAtom, \@NeighborhoodAtomSuccessorAtoms); 2762 2763 push @{$AtomNeighborhoods[$Depth]} , \@NeighborhoodAtomWithSuccessors; 2764 } 2765 $Depth++; 2766 } 2767 return @AtomNeighborhoods; 2768 } 2769 2770 # Get next object ID... 2771 sub _GetNewObjectID { 2772 $ObjectID++; 2773 return $ObjectID; 2774 } 2775 2776 # Is aromatic property set for the molecule? 2777 sub IsAromatic { 2778 my($This) = @_; 2779 my($Aromatic); 2780 2781 $Aromatic = $This->GetAromatic(); 2782 2783 return (defined($Aromatic) && $Aromatic) ? 1 : 0; 2784 } 2785 2786 # Does molecule contains any atoms with non-zero Z coordiates? 2787 sub IsThreeDimensional { 2788 my($This) = @_; 2789 my($Atom, @Atoms); 2790 2791 @Atoms = $This->GetAtoms(); 2792 ATOM: for $Atom (@Atoms) { 2793 if ($Atom->GetZ() != 0) { 2794 return 1; 2795 } 2796 } 2797 return 0; 2798 } 2799 2800 2801 # Is it a molecule object? 2802 sub IsMolecule ($) { 2803 my($Object) = @_; 2804 2805 return _IsMolecule($Object); 2806 } 2807 2808 # Return a string containing vertices, edges and other properties... 2809 sub StringifyMolecule { 2810 my($This) = @_; 2811 my($MoleculeString, $ID, $Name, $NumOfAtoms, $NumOfBonds, $MolecularFormula, $NumOfRings, $MolecularWeight, $ExactMass, $FormalCharge, $SpinMultiplicity, $Charge, $ElementsRef, $ElementsCompositionRef, $ElementalComposition); 2812 2813 $ID = $This->GetID(); 2814 $Name = $This->GetName(); 2815 $NumOfAtoms = $This->GetNumOfAtoms(); 2816 $NumOfBonds = $This->GetNumOfBonds(); 2817 2818 $NumOfRings = $This->GetNumOfRings(); 2819 if (!defined $NumOfRings) { 2820 $NumOfRings = 'undefined'; 2821 } 2822 2823 $MolecularFormula = $This->GetMolecularFormula(); 2824 2825 $MolecularWeight = $This->GetMolecularWeight(); 2826 $MolecularWeight = round($MolecularWeight, 4) + 0; 2827 2828 $ExactMass = $This->GetExactMass(); 2829 $ExactMass = round($ExactMass, 4) + 0; 2830 2831 $FormalCharge = $This->GetFormalCharge(); 2832 $Charge = $This->GetCharge(); 2833 2834 $SpinMultiplicity = $This->GetSpinMultiplicity(); 2835 2836 ($ElementsRef, $ElementsCompositionRef) = $This->GetElementalComposition(); 2837 $ElementalComposition = 'None'; 2838 if (defined($ElementsRef) && @{$ElementsRef}) { 2839 $ElementalComposition = "[ " . FormatElementalCompositionInformation($ElementsRef, $ElementsCompositionRef) . " ]"; 2840 } 2841 2842 $MoleculeString = "Molecule: ID: $ID; Name: \"$Name\"; NumOfAtoms: $NumOfAtoms; NumOfBonds: $NumOfBonds; NumOfRings: $NumOfRings; MolecularFormula: $MolecularFormula; MolecularWeight: $MolecularWeight; ExactMass: $ExactMass; FormalCharge: $FormalCharge; Charge: $Charge; SpinMultiplicity: $SpinMultiplicity; ElementalComposition: $ElementalComposition"; 2843 2844 return $MoleculeString; 2845 } 2846 2847 # Is it a molecule object? 2848 sub _IsMolecule { 2849 my($Object) = @_; 2850 2851 return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0; 2852 } 2853