1 package Atom; 2 # 3 # $RCSfile: Atom.pm,v $ 4 # $Date: 2012/03/17 21:35:26 $ 5 # $Revision: 1.51 $ 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 PeriodicTable; 36 use Vector; 37 use MathUtil; 38 39 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 40 41 @ISA = qw(ObjectProperty Exporter); 42 @EXPORT = qw(); 43 @EXPORT_OK = qw(); 44 45 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]); 46 47 # Setup class variables... 48 my($ClassName, $ObjectID); 49 _InitializeClass(); 50 51 # Overload Perl functions... 52 use overload '""' => 'StringifyAtom'; 53 54 # Class constructor... 55 sub new { 56 my($Class, %NamesAndValues) = @_; 57 58 # Initialize object... 59 my $This = {}; 60 bless $This, ref($Class) || $Class; 61 $This->_InitializeAtom(); 62 63 $This->_InitializeAtomProperties(%NamesAndValues); 64 65 return $This; 66 } 67 68 # Initialize object data... 69 sub _InitializeAtom { 70 my($This) = @_; 71 my($ObjectID) = _GetNewObjectID(); 72 73 # All other property names and values along with all Set/Get<PropertyName> methods 74 # are implemented on-demand using ObjectProperty class. 75 $This->{ID} = $ObjectID; 76 $This->{Name} = "Atom ${ObjectID}"; 77 $This->{AtomSymbol} = ''; 78 $This->{AtomicNumber} = 0; 79 $This->{XYZ} = Vector::ZeroVector; 80 } 81 82 # Initialize atom properties... 83 sub _InitializeAtomProperties { 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 if (!exists $NamesAndValues{'AtomSymbol'}) { 92 carp "Warning: ${ClassName}->new: Atom object instantiated without setting atom symbol..."; 93 } 94 95 return $This; 96 } 97 98 # Initialize class ... 99 sub _InitializeClass { 100 #Class name... 101 $ClassName = __PACKAGE__; 102 103 # ID to keep track of objects... 104 $ObjectID = 0; 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 # Setup an explicit SetMolecule method to block setting of ID by AUTOLOAD function... 117 sub SetMolecule { 118 my($This, $Value) = @_; 119 120 carp "Warning: ${ClassName}->SetMolecule: Molecule property can't be changed: it's used for internal tracking..."; 121 122 return $This; 123 } 124 125 # Assign atom to molecule... 126 sub _SetMolecule { 127 my($This, $Molecule) = @_; 128 129 $This->{Molecule} = $Molecule; 130 131 # Weaken the reference to disable increment of reference count; otherwise, 132 # it it becomes a circular reference and destruction of Molecule object doesn't 133 # get initiated which in turn disables destruction of atom object. 134 # 135 Scalar::Util::weaken($This->{Molecule}); 136 137 return $This; 138 } 139 140 # Setup atom symbol and atomic number for the element... 141 # 142 # Possible atom symbol values: 143 # . An element symbol or some other type of atom: L - Atom list; LP - Lone pair; R# - R group; 144 # A, Q, * - unknown atom; or something else? 145 # 146 # Default mass number corresponds to the most abundant natural isotope unless it's explicity 147 # set using "MassNumber" property. 148 # 149 sub SetAtomSymbol { 150 my($This, $AtomSymbol) = @_; 151 my($AtomicNumber); 152 153 $This->{AtomSymbol} = $AtomSymbol; 154 155 $AtomicNumber = PeriodicTable::GetElementAtomicNumber($AtomSymbol); 156 $This->{AtomicNumber} = (defined $AtomicNumber) ? $AtomicNumber : 0; 157 158 return $This; 159 } 160 161 # Setup atom symbol and atomic number for the element... 162 sub SetAtomicNumber { 163 my($This, $AtomicNumber) = @_; 164 my($AtomSymbol); 165 166 $AtomSymbol = PeriodicTable::GetElementAtomSymbol($AtomicNumber); 167 if (!defined $AtomSymbol) { 168 carp "Warning: ${ClassName}->SetAtomicNumber: Didn't set atomic number: Invalid atomic number, $AtomicNumber, specified..."; 169 return; 170 } 171 $This->{AtomicNumber} = $AtomicNumber; 172 $This->{AtomSymbol} = $AtomSymbol; 173 174 return $This; 175 } 176 177 # Set atom as stereo center... 178 # 179 sub SetStereoCenter { 180 my($This, $StereoCenter) = @_; 181 182 $This->SetProperty('StereoCenter', $StereoCenter); 183 184 return $This; 185 } 186 187 # Is it a stereo center? 188 # 189 sub IsStereoCenter { 190 my($This) = @_; 191 my($StereoCenter); 192 193 $StereoCenter = $This->GetProperty('StereoCenter'); 194 195 return (defined($StereoCenter) && $StereoCenter) ? 1 : 0; 196 } 197 198 # Set atom stereochemistry. 199 # 200 # Supported values are: R, S. 201 # 202 # Notes: 203 # 204 # . After the ligands around a central stereocenter has been ranked using CIP priority scheme and 205 # the lowest ranked ligand lies behind the center atom, then R and S values correspond to: 206 # 207 # R: Clockwise arrangement of remaining ligands around the central atom going from highest to lowest ranked ligand 208 # S: CounterClockwise arrangement of remaining ligands around the central atom going from highest to lowest ranked ligand 209 # 210 # . Assignment of any other arbitray values besides R and S is also allowed; however, a warning is printed. 211 # 212 sub SetStereochemistry { 213 my($This, $Stereochemistry) = @_; 214 215 if ($Stereochemistry !~ /^(R|S)$/i) { 216 carp "Warning: ${ClassName}->SetStereochemistry: Assigning non-supported Stereochemistry value of $Stereochemistry. Supported values: R, S..."; 217 } 218 219 $This->SetProperty('StereoCenter', 1); 220 $This->SetProperty('Stereochemistry', $Stereochemistry); 221 222 return $This; 223 } 224 225 # Setup mass number for atom... 226 sub SetMassNumber { 227 my($This, $MassNumber) = @_; 228 my($AtomicNumber, $AtomSymbol); 229 230 $AtomicNumber = $This->{AtomicNumber}; 231 $AtomSymbol = $This->{AtomSymbol}; 232 if (!$AtomicNumber) { 233 carp "Warning: ${ClassName}->SetMassNumber: Didn't set mass number: Non standard atom with atomic number, $AtomicNumber, and atomic symbol, $AtomSymbol..."; 234 return; 235 } 236 if (!PeriodicTable::IsElementNaturalIsotopeMassNumber($AtomicNumber, $MassNumber)) { 237 carp "Warning: ${ClassName}->SetMassNumber: Unknown mass number, $MassNumber, specified for atom with atomic number, $AtomicNumber, and atomic symbol, $AtomSymbol. Don't forget to Set ExactMass property explicitly; otherwise, GetExactMass method would return mass of most abundant isotope..."; 238 } 239 $This->SetProperty('MassNumber', $MassNumber); 240 241 return $This; 242 } 243 244 # Get mass number... 245 # 246 sub GetMassNumber { 247 my($This) = @_; 248 249 # Is mass number explicity set? 250 if ($This->HasProperty('MassNumber')) { 251 return $This->GetProperty('MassNumber'); 252 } 253 254 # Is it an element symbol? 255 my($AtomicNumber) = $This->{AtomicNumber}; 256 if (!$AtomicNumber) { 257 return 0; 258 } 259 260 # Return most abundant mass number... 261 return PeriodicTable::GetElementMostAbundantNaturalIsotopeMassNumber($AtomicNumber); 262 } 263 264 # Get atomic weight: 265 # . Explicitly set by the caller 266 # . Using atomic number 267 # 268 sub GetAtomicWeight { 269 my($This) = @_; 270 271 # Is atomic weight explicity set? 272 if ($This->HasProperty('AtomicWeight')) { 273 return $This->GetProperty('AtomicWeight'); 274 } 275 276 # Is it an element symbol? 277 my($AtomicNumber) = $This->{AtomicNumber}; 278 if (!$AtomicNumber) { 279 return 0; 280 } 281 282 # Return its atomic weight... 283 return PeriodicTable::GetElementAtomicWeight($AtomicNumber); 284 } 285 286 # Get exact mass weight: 287 # . Explicitly set by the caller 288 # . Using atomic number and mass number explicity set by the caller 289 # . Using atomic number and most abundant isotope 290 # 291 sub GetExactMass { 292 my($This) = @_; 293 294 # Is exact mass explicity set? 295 if ($This->HasProperty('ExactMass')) { 296 return $This->GetProperty('ExactMass'); 297 } 298 299 # Is it an element symbol? 300 my($AtomicNumber) = $This->{AtomicNumber}; 301 if (!$AtomicNumber) { 302 return 0; 303 } 304 305 # Is mass number explicitly set? 306 if ($This->HasProperty('MassNumber')) { 307 my($MassNumber) = $This->GetProperty('MassNumber'); 308 if (PeriodicTable::IsElementNaturalIsotopeMassNumber($AtomicNumber, $MassNumber)) { 309 return PeriodicTable::GetElementNaturalIsotopeMass($AtomicNumber, $MassNumber); 310 } 311 } 312 313 # Return most abundant isotope mass... 314 return PeriodicTable::GetElementMostAbundantNaturalIsotopeMass($AtomicNumber); 315 } 316 317 # Get formal charge: 318 # . Explicitly set by the caller 319 # . Or return zero insetad of undef 320 # 321 sub GetFormalCharge { 322 my($This) = @_; 323 my($FormalCharge); 324 325 $FormalCharge = 0; 326 if ($This->HasProperty('FormalCharge')) { 327 $FormalCharge = $This->GetProperty('FormalCharge'); 328 } 329 330 return defined($FormalCharge) ? $FormalCharge : 0; 331 } 332 333 # Set atom coordinates using: 334 # . An array reference with three values 335 # . An array containg three values 336 # . A 3D vector 337 # 338 sub SetXYZ { 339 my($This, @Values) = @_; 340 341 if (!@Values) { 342 carp "Warning: ${ClassName}->SetXYZ: No values specified..."; 343 return; 344 } 345 346 $This->{XYZ}->SetXYZ(@Values); 347 return $This; 348 } 349 350 # Set X value... 351 sub SetX { 352 my($This, $Value) = @_; 353 354 if (!defined $Value) { 355 carp "Warning: ${ClassName}->SetX: Undefined X value..."; 356 return; 357 } 358 $This->{XYZ}->SetX($Value); 359 return $This; 360 } 361 362 # Set Y value... 363 sub SetY { 364 my($This, $Value) = @_; 365 366 if (!defined $Value) { 367 carp "Warning: ${ClassName}->SetY: Undefined Y value..."; 368 return; 369 } 370 $This->{XYZ}->SetY($Value); 371 return $This; 372 } 373 374 # Set Z value... 375 sub SetZ { 376 my($This, $Value) = @_; 377 378 if (!defined $Value) { 379 carp "Warning: ${ClassName}->SetZ: Undefined Z value..."; 380 return; 381 } 382 $This->{XYZ}->SetZ($Value); 383 return $This; 384 } 385 386 # Return XYZ as: 387 # . Reference to an array 388 # . An array 389 # 390 sub GetXYZ { 391 my($This) = @_; 392 393 return $This->{XYZ}->GetXYZ(); 394 } 395 396 # Return XYZ as a vector object... 397 # 398 sub GetXYZVector { 399 my($This) = @_; 400 401 return $This->{XYZ}; 402 } 403 404 # Get X value... 405 sub GetX { 406 my($This) = @_; 407 408 return $This->{XYZ}->GetX(); 409 } 410 411 # Get Y value... 412 sub GetY { 413 my($This) = @_; 414 415 return $This->{XYZ}->GetY(); 416 } 417 418 # Get Z value... 419 sub GetZ { 420 my($This) = @_; 421 422 return $This->{XYZ}->GetZ(); 423 } 424 425 # Delete atom... 426 sub DeleteAtom { 427 my($This) = @_; 428 429 # Is this atom in a molecule? 430 if (!$This->HasProperty('Molecule')) { 431 # Nothing to do... 432 return $This; 433 } 434 my($Molecule) = $This->GetProperty('Molecule'); 435 436 return $Molecule->_DeleteAtom($This); 437 } 438 439 # Get atom neighbor objects as array. In scalar conetxt, return number of neighbors... 440 sub GetNeighbors { 441 my($This, @ExcludeNeighbors) = @_; 442 443 # Is this atom in a molecule? 444 if (!$This->HasProperty('Molecule')) { 445 return undef; 446 } 447 my($Molecule) = $This->GetProperty('Molecule'); 448 449 if (@ExcludeNeighbors) { 450 return $This->_GetAtomNeighbors(@ExcludeNeighbors); 451 } 452 else { 453 return $This->_GetAtomNeighbors(); 454 } 455 } 456 457 # Get atom neighbor objects as array. In scalar conetxt, return number of neighbors... 458 sub _GetAtomNeighbors { 459 my($This, @ExcludeNeighbors) = @_; 460 my($Molecule) = $This->GetProperty('Molecule'); 461 462 if (!@ExcludeNeighbors) { 463 return $Molecule->_GetAtomNeighbors($This); 464 } 465 466 # Setup a map for neigbhors to exclude... 467 my($ExcludeNeighbor, $ExcludeNeighborID, %ExcludeNeighborsIDsMap); 468 469 %ExcludeNeighborsIDsMap = (); 470 for $ExcludeNeighbor (@ExcludeNeighbors) { 471 $ExcludeNeighborID = $ExcludeNeighbor->GetID(); 472 $ExcludeNeighborsIDsMap{$ExcludeNeighborID} = $ExcludeNeighborID; 473 } 474 475 # Generate a filtered neighbors list... 476 my($Neighbor, $NeighborID, @FilteredAtomNeighbors); 477 @FilteredAtomNeighbors = (); 478 NEIGHBOR: for $Neighbor ($Molecule->_GetAtomNeighbors($This)) { 479 $NeighborID = $Neighbor->GetID(); 480 if (exists $ExcludeNeighborsIDsMap{$NeighborID}) { 481 next NEIGHBOR; 482 } 483 push @FilteredAtomNeighbors, $Neighbor; 484 } 485 486 return wantarray ? @FilteredAtomNeighbors : scalar @FilteredAtomNeighbors; 487 } 488 489 # Get specific atom neighbor objects as array. In scalar conetxt, return number of neighbors. 490 # 491 # Notes: 492 # . AtomSpecification correspond to any valid AtomicInvariant based atomic specifications 493 # as implemented in DoesAtomNeighborhoodMatch method. 494 # . Multiple atom specifications can be used in a string delimited by comma. 495 # 496 sub GetNeighborsUsingAtomSpecification { 497 my($This, $AtomSpecification, @ExcludeNeighbors) = @_; 498 my(@AtomNeighbors); 499 500 @AtomNeighbors = (); 501 @AtomNeighbors = $This->GetNeighbors(@ExcludeNeighbors); 502 503 # Does atom has any neighbors and do they need to be filtered? 504 if (!(@AtomNeighbors && defined($AtomSpecification) && $AtomSpecification)) { 505 return wantarray ? @AtomNeighbors : scalar @AtomNeighbors; 506 } 507 508 # Filter neighbors using atom specification... 509 my($AtomNeighbor, @FilteredAtomNeighbors); 510 511 @FilteredAtomNeighbors = (); 512 NEIGHBOR: for $AtomNeighbor (@AtomNeighbors) { 513 if (!$AtomNeighbor->_DoesAtomSpecificationMatch($AtomSpecification)) { 514 next NEIGHBOR; 515 } 516 push @FilteredAtomNeighbors, $AtomNeighbor; 517 } 518 519 return wantarray ? @FilteredAtomNeighbors : scalar @FilteredAtomNeighbors; 520 } 521 522 523 # Get non-hydrogen atom neighbor objects as array. In scalar context, return number of neighbors... 524 sub GetHeavyAtomNeighbors { 525 my($This) = @_; 526 527 return $This->GetNonHydrogenAtomNeighbors(); 528 } 529 530 # Get non-hydrogen atom neighbor objects as array. In scalar context, return number of neighbors... 531 sub GetNonHydrogenAtomNeighbors { 532 my($This) = @_; 533 534 # Is this atom in a molecule? 535 if (!$This->HasProperty('Molecule')) { 536 return undef; 537 } 538 my($NonHydrogenAtomsOnly, $HydrogenAtomsOnly) = (1, 0); 539 540 return $This->_GetFilteredAtomNeighbors($NonHydrogenAtomsOnly, $HydrogenAtomsOnly); 541 } 542 543 # Get hydrogen atom neighbor objects as array. In scalar context, return numbe of neighbors... 544 sub GetHydrogenAtomNeighbors { 545 my($This) = @_; 546 547 # Is this atom in a molecule? 548 if (!$This->HasProperty('Molecule')) { 549 return undef; 550 } 551 my($NonHydrogenAtomsOnly, $HydrogenAtomsOnly) = (0, 1); 552 553 return $This->_GetFilteredAtomNeighbors($NonHydrogenAtomsOnly, $HydrogenAtomsOnly); 554 } 555 556 # Get non-hydrogen neighbor of hydrogen atom... 557 # 558 sub GetNonHydrogenNeighborOfHydrogenAtom { 559 my($This) = @_; 560 561 # Is it Hydrogen? 562 if (!$This->IsHydrogen()) { 563 return undef; 564 } 565 my(@Neighbors); 566 567 @Neighbors = $This->GetNonHydrogenAtomNeighbors(); 568 569 return (@Neighbors == 1) ? $Neighbors[0] : undef; 570 } 571 572 # Get filtered atom atom neighbors 573 sub _GetFilteredAtomNeighbors { 574 my($This, $NonHydrogenAtomsOnly, $HydrogenAtomsOnly) = @_; 575 576 # Check flags... 577 if (!defined $NonHydrogenAtomsOnly) { 578 $NonHydrogenAtomsOnly = 0; 579 } 580 if (!defined $HydrogenAtomsOnly) { 581 $HydrogenAtomsOnly = 0; 582 } 583 my($Neighbor, @FilteredAtomNeighbors); 584 585 @FilteredAtomNeighbors = (); 586 NEIGHBOR: for $Neighbor ($This->GetNeighbors()) { 587 if ($NonHydrogenAtomsOnly && $Neighbor->IsHydrogen()) { 588 next NEIGHBOR; 589 } 590 if ($HydrogenAtomsOnly && (!$Neighbor->IsHydrogen())) { 591 next NEIGHBOR; 592 } 593 push @FilteredAtomNeighbors, $Neighbor; 594 } 595 596 return wantarray ? @FilteredAtomNeighbors : scalar @FilteredAtomNeighbors; 597 } 598 599 # Get number of neighbors... 600 # 601 sub GetNumOfNeighbors { 602 my($This) = @_; 603 my($NumOfNeighbors); 604 605 $NumOfNeighbors = $This->GetNeighbors(); 606 607 return (defined $NumOfNeighbors) ? $NumOfNeighbors : undef; 608 } 609 610 # Get number of neighbors which are non-hydrogen atoms... 611 sub GetNumOfHeavyAtomNeighbors { 612 my($This) = @_; 613 614 return $This->GetNumOfNonHydrogenAtomNeighbors(); 615 } 616 617 # Get number of neighbors which are non-hydrogen atoms... 618 sub GetNumOfNonHydrogenAtomNeighbors { 619 my($This) = @_; 620 my($NumOfNeighbors); 621 622 $NumOfNeighbors = $This->GetNonHydrogenAtomNeighbors(); 623 624 return (defined $NumOfNeighbors) ? $NumOfNeighbors : undef; 625 } 626 627 # Get number of neighbors which are hydrogen atoms... 628 sub GetNumOfHydrogenAtomNeighbors { 629 my($This) = @_; 630 my($NumOfNeighbors); 631 632 $NumOfNeighbors = $This->GetHydrogenAtomNeighbors(); 633 634 return (defined $NumOfNeighbors) ? $NumOfNeighbors : undef; 635 } 636 637 # Get bond objects as array. In scalar context, return number of bonds... 638 sub GetBonds { 639 my($This) = @_; 640 641 # Is this atom in a molecule? 642 if (!$This->HasProperty('Molecule')) { 643 return undef; 644 } 645 my($Molecule) = $This->GetProperty('Molecule'); 646 647 return $Molecule->_GetAtomBonds($This); 648 } 649 650 # Get bond to specified atom... 651 sub GetBondToAtom { 652 my($This, $Other) = @_; 653 654 # Is this atom in a molecule? 655 if (!$This->HasProperty('Molecule')) { 656 return undef; 657 } 658 my($Molecule) = $This->GetProperty('Molecule'); 659 660 return $Molecule->_GetBondToAtom($This, $Other); 661 } 662 663 # Get bond objects to non-hydrogen atoms as array. In scalar context, return number of bonds... 664 sub GetBondsToHeavyAtoms { 665 my($This) = @_; 666 667 return $This->GetBondsToNonHydrogenAtoms(); 668 } 669 670 # Get bond objects to non-hydrogen atoms as array. In scalar context, return number of bonds... 671 sub GetBondsToNonHydrogenAtoms { 672 my($This) = @_; 673 674 # Is this atom in a molecule? 675 if (!$This->HasProperty('Molecule')) { 676 return undef; 677 } 678 my($BondsToNonHydrogenAtomsOnly, $BondsToHydrogenAtomsOnly) = (1, 0); 679 680 return $This->_GetFilteredBonds($BondsToNonHydrogenAtomsOnly, $BondsToHydrogenAtomsOnly); 681 } 682 683 # Get bond objects to hydrogen atoms as array. In scalar context, return number of bonds... 684 sub GetBondsToHydrogenAtoms { 685 my($This) = @_; 686 687 # Is this atom in a molecule? 688 if (!$This->HasProperty('Molecule')) { 689 return undef; 690 } 691 my($BondsToNonHydrogenAtomsOnly, $BondsToHydrogenAtomsOnly) = (0, 1); 692 693 return $This->_GetFilteredBonds($BondsToNonHydrogenAtomsOnly, $BondsToHydrogenAtomsOnly); 694 } 695 696 # Get filtered bonds... 697 sub _GetFilteredBonds { 698 my($This, $BondsToNonHydrogenAtomsOnly, $BondsToHydrogenAtomsOnly) = @_; 699 700 # Check flags... 701 if (!defined $BondsToNonHydrogenAtomsOnly) { 702 $BondsToNonHydrogenAtomsOnly = 0; 703 } 704 if (!defined $BondsToHydrogenAtomsOnly) { 705 $BondsToHydrogenAtomsOnly = 0; 706 } 707 708 my($Bond, $BondedAtom, @FilteredBonds); 709 710 @FilteredBonds = (); 711 BOND: for $Bond ($This->GetBonds()) { 712 $BondedAtom = $Bond->GetBondedAtom($This); 713 if ($BondsToNonHydrogenAtomsOnly && $BondedAtom->IsHydrogen()) { 714 next BOND; 715 } 716 if ($BondsToHydrogenAtomsOnly && (!$BondedAtom->IsHydrogen())) { 717 next BOND; 718 } 719 push @FilteredBonds, $Bond; 720 } 721 722 return wantarray ? @FilteredBonds : (scalar @FilteredBonds); 723 } 724 725 # Get number of bonds... 726 # 727 sub GetNumOfBonds { 728 my($This) = @_; 729 my($NumOfBonds); 730 731 $NumOfBonds = $This->GetBonds(); 732 733 return (defined $NumOfBonds) ? ($NumOfBonds) : undef; 734 } 735 736 # Get number of bonds to non-hydrogen atoms... 737 sub GetNumOfBondsToHeavyAtoms { 738 my($This) = @_; 739 740 return $This->GetNumOfBondsToNonHydrogenAtoms(); 741 } 742 743 # Get number of bonds to non-hydrogen atoms... 744 sub GetNumOfBondsToNonHydrogenAtoms { 745 my($This) = @_; 746 my($NumOfBonds); 747 748 $NumOfBonds = $This->GetBondsToNonHydrogenAtoms(); 749 750 return (defined $NumOfBonds) ? ($NumOfBonds) : undef; 751 } 752 753 # Get number of single bonds to heavy atoms... 754 sub GetNumOfSingleBondsToHeavyAtoms { 755 my($This) = @_; 756 757 return $This->GetNumOfSingleBondsToNonHydrogenAtoms(); 758 } 759 760 # Get number of single bonds to non-hydrogen atoms... 761 sub GetNumOfSingleBondsToNonHydrogenAtoms { 762 my($This) = @_; 763 764 # Is this atom in a molecule? 765 if (!$This->HasProperty('Molecule')) { 766 return undef; 767 } 768 return $This->_GetNumOfBondsWithSpecifiedBondOrderToNonHydrogenAtoms(1); 769 } 770 771 # Get number of double bonds to heavy atoms... 772 sub GetNumOfDoubleBondsToHeavyAtoms { 773 my($This) = @_; 774 775 return $This->GetNumOfDoubleBondsToNonHydrogenAtoms(); 776 } 777 778 # Get number of double bonds to non-hydrogen atoms... 779 sub GetNumOfDoubleBondsToNonHydrogenAtoms { 780 my($This) = @_; 781 782 # Is this atom in a molecule? 783 if (!$This->HasProperty('Molecule')) { 784 return undef; 785 } 786 return $This->_GetNumOfBondsWithSpecifiedBondOrderToNonHydrogenAtoms(2); 787 } 788 789 # Get number of triple bonds to heavy atoms... 790 sub GetNumOfTripleBondsToHeavyAtoms { 791 my($This) = @_; 792 793 return $This->GetNumOfTripleBondsToNonHydrogenAtoms(); 794 } 795 796 # Get number of triple bonds to non-hydrogen atoms... 797 sub GetNumOfTripleBondsToNonHydrogenAtoms { 798 my($This) = @_; 799 800 # Is this atom in a molecule? 801 if (!$This->HasProperty('Molecule')) { 802 return undef; 803 } 804 return $This->_GetNumOfBondsWithSpecifiedBondOrderToNonHydrogenAtoms(3); 805 } 806 807 # Get number of bonds of specified bond order to non-hydrogen atoms... 808 sub _GetNumOfBondsWithSpecifiedBondOrderToNonHydrogenAtoms { 809 my($This, $SpecifiedBondOrder) = @_; 810 my($NumOfBonds, $Bond, $BondOrder, @Bonds); 811 812 $NumOfBonds = 0; 813 @Bonds = $This->GetBondsToNonHydrogenAtoms(); 814 for $Bond (@Bonds) { 815 $BondOrder = $Bond->GetBondOrder(); 816 if ($SpecifiedBondOrder == $BondOrder) { 817 $NumOfBonds++; 818 } 819 } 820 return $NumOfBonds; 821 } 822 823 # Get number of aromatic bonds to heavy atoms... 824 sub GetNumOfAromaticBondsToHeavyAtoms { 825 my($This) = @_; 826 827 return $This->GetNumOfAromaticBondsToNonHydrogenAtoms(); 828 } 829 830 # Get number of aromatic bonds to non-hydrogen atoms... 831 sub GetNumOfAromaticBondsToNonHydrogenAtoms { 832 my($This) = @_; 833 my($NumOfBonds, $Bond, @Bonds); 834 835 # Is this atom in a molecule? 836 if (!$This->HasProperty('Molecule')) { 837 return undef; 838 } 839 840 $NumOfBonds = 0; 841 @Bonds = $This->GetBondsToNonHydrogenAtoms(); 842 for $Bond (@Bonds) { 843 if ($Bond->IsAromatic()) { $NumOfBonds++; } 844 } 845 return $NumOfBonds; 846 } 847 848 # Get number of different bond types to non-hydrogen atoms... 849 # 850 sub GetNumOfBondTypesToHeavyAtoms { 851 my($This, $CountAromaticBonds) = @_; 852 853 return $This->GetNumOfBondTypesToNonHydrogenAtoms($CountAromaticBonds); 854 } 855 856 # Get number of single, double, triple, and aromatic bonds from an atom to all other 857 # non-hydrogen atoms. Value of CountAtomaticBonds parameter controls whether 858 # number of aromatic bonds is returned; default is not to count aromatic bonds. During 859 # counting of aromatic bonds, the bond marked aromatic is not included in the count 860 # of other bond types. 861 # 862 sub GetNumOfBondTypesToNonHydrogenAtoms { 863 my($This, $CountAromaticBonds) = @_; 864 my($NumOfSingleBonds, $NumOfDoubleBonds, $NumOfTripleBonds, $NumOfAromaticBonds, $None, $Bond, @Bonds); 865 866 $CountAromaticBonds = defined($CountAromaticBonds) ? $CountAromaticBonds : 0; 867 868 ($NumOfSingleBonds, $NumOfDoubleBonds, $NumOfTripleBonds) = ('0') x 3; 869 $NumOfAromaticBonds = $CountAromaticBonds ? 0 : undef; 870 871 # Is this atom in a molecule? 872 if (!$This->HasProperty('Molecule')) { 873 return ($NumOfSingleBonds, $NumOfDoubleBonds, $NumOfTripleBonds, $NumOfAromaticBonds); 874 } 875 876 @Bonds = $This->GetBondsToNonHydrogenAtoms(); 877 878 for $Bond (@Bonds) { 879 BONDTYPE: { 880 if ($CountAromaticBonds) { 881 if ($Bond->IsAromatic()) { $NumOfAromaticBonds++; last BONDTYPE; } 882 } 883 if ($Bond->IsSingle()) { $NumOfSingleBonds++; last BONDTYPE; } 884 if ($Bond->IsDouble()) { $NumOfDoubleBonds++; last BONDTYPE; } 885 if ($Bond->IsTriple()) { $NumOfTripleBonds++; last BONDTYPE; } 886 $None = 1; 887 } 888 } 889 return ($NumOfSingleBonds, $NumOfDoubleBonds, $NumOfTripleBonds, $NumOfAromaticBonds); 890 } 891 892 # Get number of sigma and pi bonds to heavy atoms... 893 # 894 sub GetNumOfSigmaAndPiBondsToHeavyAtoms { 895 my($This) = @_; 896 897 return $This->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms(); 898 } 899 900 # Get number of sigma and pi bonds from an atom to all other non-hydrogen atoms. 901 # Sigma and pi bonds are counted using the following methodology: a single bond 902 # correspond to one sigma bond; a double bond contributes one to sigma bond count 903 # and one to pi bond count; a triple bond contributes one to sigma bond count and 904 # two to pi bond count. 905 # 906 sub GetNumOfSigmaAndPiBondsToNonHydrogenAtoms { 907 my($This) = @_; 908 my($NumOfSingleBonds, $NumOfDoubleBonds, $NumOfTripleBonds, $NumOfSigmaBonds, $NumOfPiBonds); 909 910 ($NumOfSingleBonds, $NumOfDoubleBonds, $NumOfTripleBonds) = $This->GetNumOfBondTypesToNonHydrogenAtoms(); 911 912 $NumOfSigmaBonds = $NumOfSingleBonds + $NumOfDoubleBonds + $NumOfTripleBonds; 913 $NumOfPiBonds = $NumOfDoubleBonds + 2*$NumOfTripleBonds; 914 915 return ($NumOfSigmaBonds, $NumOfPiBonds); 916 } 917 918 # Get information related to atoms for all heavy atoms attached to an atom.. 919 # 920 sub GetHeavyAtomNeighborsAtomInformation { 921 my($This) = @_; 922 923 return $This->GetNonHydrogenAtomNeighborsAtomInformation(); 924 } 925 926 # Get information related to atoms for all non-hydrogen atoms attached to an atom.. 927 # 928 # The following values are returned: 929 # . Number of non-hydrogen atom neighbors 930 # . A reference to an array containing atom objects correpsonding to non-hydrogen 931 # atom neighbors 932 # . Number of different types of non-hydrogen atom neighbors 933 # . A reference to a hash containing atom symbol as key with value corresponding 934 # to its count for non-hydrogen atom neighbors 935 # 936 sub GetNonHydrogenAtomNeighborsAtomInformation { 937 my($This) = @_; 938 939 # Is this atom in a molecule? 940 if (!$This->HasProperty('Molecule')) { 941 return (undef, undef, undef, undef); 942 } 943 my($AtomSymbol, $AtomNeighbor, $NumOfAtomNeighbors, $NumOfAtomNeighborsType, @AtomNeighbors, %AtomNeighborsTypeMap); 944 945 $NumOfAtomNeighbors = 0; @AtomNeighbors = (); 946 $NumOfAtomNeighborsType = 0; %AtomNeighborsTypeMap = (); 947 948 @AtomNeighbors = $This->GetNonHydrogenAtomNeighbors(); 949 $NumOfAtomNeighbors = scalar @AtomNeighbors; 950 951 for $AtomNeighbor (@AtomNeighbors) { 952 $AtomSymbol = $AtomNeighbor->{AtomSymbol}; 953 if (exists $AtomNeighborsTypeMap{$AtomSymbol}) { 954 $AtomNeighborsTypeMap{$AtomSymbol} += 1; 955 } 956 else { 957 $AtomNeighborsTypeMap{$AtomSymbol} = 1; 958 $NumOfAtomNeighborsType++; 959 } 960 } 961 962 return ($NumOfAtomNeighbors, \@AtomNeighbors, $NumOfAtomNeighborsType, \%AtomNeighborsTypeMap); 963 } 964 965 # Get information related to bonds for all heavy atoms attached to an atom.. 966 # 967 sub GetHeavyAtomNeighborsBondformation { 968 my($This) = @_; 969 970 return $This->GetNonHydrogenAtomNeighborsBondInformation(); 971 } 972 973 # Get information related to bonds for all non-hydrogen atoms attached to an atom.. 974 # 975 # The following values are returned: 976 # . Number of bonds to non-hydrogen atom neighbors 977 # . A reference to an array containing bond objects correpsonding to non-hydrogen 978 # atom neighbors 979 # . A reference to a hash containing bond type as key with value corresponding 980 # to its count for non-hydrogen atom neighbors. Bond types are: Single, Double or Triple 981 # . A reference to a hash containing atom symbol as key pointing to bond type as second 982 # key with values correponding to count of bond types for atom symbol for non-hydrogen 983 # atom neighbors 984 # . A reference to a hash containing atom symbol as key pointing to bond type as second 985 # key with values correponding to atom objects array involved in corresponding bond type for 986 # atom symbol for non-hydrogen atom neighbors 987 # 988 sub GetNonHydrogenAtomNeighborsBondInformation { 989 my($This) = @_; 990 991 # Is this atom in a molecule? 992 if (!$This->HasProperty('Molecule')) { 993 return (undef, undef, undef, undef, undef); 994 } 995 my($BondedAtom, $BondedAtomSymbol, $BondType, $None, $Bond, $NumOfBonds, @Bonds, %BondTypeCountMap, %AtomsBondTypesCountMap, %AtomsBondTypeAtomsMap); 996 997 $NumOfBonds = 0; @Bonds = (); 998 %BondTypeCountMap = (); 999 %AtomsBondTypesCountMap = (); %AtomsBondTypeAtomsMap = (); 1000 1001 $BondTypeCountMap{Single} = 0; 1002 $BondTypeCountMap{Double} = 0; 1003 $BondTypeCountMap{Triple} = 0; 1004 1005 @Bonds = $This->GetBondsToNonHydrogenAtoms(); 1006 $NumOfBonds = scalar @Bonds; 1007 1008 BOND: for $Bond (@Bonds) { 1009 $BondType = $Bond->IsSingle() ? "Single" : ($Bond->IsDouble() ? "Double" : ($Bond->IsTriple() ? "Triple" : "")); 1010 if (!$BondType) { 1011 next BOND; 1012 } 1013 1014 # Track bond types... 1015 if (exists $BondTypeCountMap{$BondType}) { 1016 $BondTypeCountMap{$BondType} += 1; 1017 } 1018 else { 1019 $BondTypeCountMap{$BondType} = 1; 1020 } 1021 1022 $BondedAtom = $Bond->GetBondedAtom($This); 1023 $BondedAtomSymbol = $BondedAtom->{AtomSymbol}; 1024 1025 # Track bond types count for atom types involved in specific bond types... 1026 if (!exists $AtomsBondTypesCountMap{$BondedAtomSymbol}) { 1027 %{$AtomsBondTypesCountMap{$BondedAtomSymbol}} = (); 1028 } 1029 if (exists $AtomsBondTypesCountMap{$BondedAtomSymbol}{$BondType}) { 1030 $AtomsBondTypesCountMap{$BondedAtomSymbol}{$BondType} += 1; 1031 } 1032 else { 1033 $AtomsBondTypesCountMap{$BondedAtomSymbol}{$BondType} = 1; 1034 } 1035 1036 # Track atoms involved in specific bond types for specific atom types... 1037 if (!exists $AtomsBondTypeAtomsMap{$BondedAtomSymbol}) { 1038 %{$AtomsBondTypeAtomsMap{$BondedAtomSymbol}} = (); 1039 } 1040 if (!exists $AtomsBondTypeAtomsMap{$BondedAtomSymbol}{$BondType}) { 1041 @{$AtomsBondTypeAtomsMap{$BondedAtomSymbol}{$BondType}} = (); 1042 } 1043 push @{$AtomsBondTypeAtomsMap{$BondedAtomSymbol}{$BondType}}, $BondedAtom; 1044 } 1045 1046 return ($NumOfBonds, \@Bonds, \%BondTypeCountMap, \%AtomsBondTypesCountMap, \%AtomsBondTypeAtomsMap); 1047 } 1048 1049 # Get number of bonds to hydrogen atoms... 1050 sub GetNumOfBondsToHydrogenAtoms { 1051 my($This) = @_; 1052 my($NumOfBonds); 1053 1054 $NumOfBonds = $This->GetBondsToHydrogenAtoms(); 1055 1056 return (defined $NumOfBonds) ? ($NumOfBonds) : undef; 1057 } 1058 1059 # Get sum of bond orders to all bonded atoms... 1060 # 1061 sub GetSumOfBondOrders { 1062 my($This) = @_; 1063 1064 # Is this atom in a molecule? 1065 if (!$This->HasProperty('Molecule')) { 1066 return undef; 1067 } 1068 1069 return $This->_GetSumOfBondOrders(); 1070 } 1071 1072 # Get sum of bond orders to non-hydrogen atoms only... 1073 # 1074 sub GetSumOfBondOrdersToHeavyAtoms { 1075 my($This) = @_; 1076 1077 return $This->GetSumOfBondOrdersToNonHydrogenAtoms(); 1078 } 1079 1080 # Get sum of bond orders to non-hydrogen atoms only... 1081 # 1082 sub GetSumOfBondOrdersToNonHydrogenAtoms { 1083 my($This) = @_; 1084 1085 # Is this atom in a molecule? 1086 if (!$This->HasProperty('Molecule')) { 1087 return undef; 1088 } 1089 my($ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly) = (1, 0); 1090 1091 return $This->_GetSumOfBondOrders($ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly); 1092 } 1093 1094 # Get sum of bond orders to hydrogen atoms only... 1095 # 1096 sub GetSumOfBondOrdersToHydrogenAtoms { 1097 my($This) = @_; 1098 1099 # Is this atom in a molecule? 1100 if (!$This->HasProperty('Molecule')) { 1101 return undef; 1102 } 1103 my($ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly) = (0, 1); 1104 1105 return $This->_GetSumOfBondOrders($ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly); 1106 } 1107 1108 # Get sum of bond orders to all bonded atoms, non-hydrogen or hydrogen bonded atoms... 1109 # 1110 sub _GetSumOfBondOrders { 1111 my($This, $ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly) = @_; 1112 1113 # Check flags... 1114 if (!defined $ToNonHydrogenAtomsOnly) { 1115 $ToNonHydrogenAtomsOnly = 0; 1116 } 1117 if (!defined $ToHydrogenAtomsOnly) { 1118 $ToHydrogenAtomsOnly = 0; 1119 } 1120 my($Bond, $SumOfBondOrders, $BondOrder, $NumOfAromaticBonds, @Bonds); 1121 @Bonds = (); 1122 1123 if ($ToNonHydrogenAtomsOnly) { 1124 @Bonds = $This->GetBondsToNonHydrogenAtoms(); 1125 } 1126 elsif ($ToHydrogenAtomsOnly) { 1127 @Bonds = $This->GetBondsToHydrogenAtoms(); 1128 } 1129 else { 1130 # All bonds... 1131 @Bonds = $This->GetBonds(); 1132 } 1133 1134 $SumOfBondOrders = 0; 1135 $NumOfAromaticBonds = 0; 1136 for $Bond (@Bonds) { 1137 $BondOrder = $Bond->GetBondOrder(); 1138 $SumOfBondOrders += $BondOrder; 1139 if ($BondOrder == 1.5) { 1140 $NumOfAromaticBonds++; 1141 } 1142 } 1143 1144 if ($NumOfAromaticBonds) { 1145 # As long as aromatic bond orders in a ring are correctly assigned in a ring using 1146 # using 4n + 2 Huckel rule (BondOrder: 1.5) or explicity set as Kekule bonds (alternate single/double), 1147 # SumOfBondOrders should add up to an integer. Just in case it's not, turn it into an integer... 1148 $SumOfBondOrders = int $SumOfBondOrders; 1149 } 1150 1151 return $SumOfBondOrders; 1152 } 1153 1154 # Get largest bond order to any bonded atoms... 1155 # 1156 sub GetLargestBondOrder { 1157 my($This) = @_; 1158 1159 # Is this atom in a molecule? 1160 if (!$This->HasProperty('Molecule')) { 1161 return undef; 1162 } 1163 1164 return $This->_GetLargestBondOrder(); 1165 } 1166 1167 # Get largest bond order to bonded non-hydrogen atoms... 1168 # 1169 sub GetLargestBondOrderToHeavyAtoms { 1170 my($This) = @_; 1171 1172 return $This->GetLargestBondOrderToNonHydrogenAtoms(); 1173 } 1174 1175 # Get largest bond order to bonded non-hydrogen atoms... 1176 # 1177 sub GetLargestBondOrderToNonHydrogenAtoms { 1178 my($This) = @_; 1179 1180 # Is this atom in a molecule? 1181 if (!$This->HasProperty('Molecule')) { 1182 return undef; 1183 } 1184 1185 my($ToNonHydrogenAtomsOnly) = (1); 1186 1187 return $This->_GetLargestBondOrder($ToNonHydrogenAtomsOnly); 1188 } 1189 1190 # Get largest bond order to all bonded atoms, non-hydrogen or hydrogen bonded atoms... 1191 # 1192 sub _GetLargestBondOrder { 1193 my($This, $ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly) = @_; 1194 1195 # Check flags... 1196 if (!defined $ToNonHydrogenAtomsOnly) { 1197 $ToNonHydrogenAtomsOnly = 0; 1198 } 1199 if (!defined $ToHydrogenAtomsOnly) { 1200 $ToHydrogenAtomsOnly = 0; 1201 } 1202 my($Bond, $LargestBondOrder, $BondOrder, @Bonds); 1203 @Bonds = (); 1204 1205 if ($ToNonHydrogenAtomsOnly) { 1206 @Bonds = $This->GetBondsToNonHydrogenAtoms(); 1207 } 1208 elsif ($ToHydrogenAtomsOnly) { 1209 @Bonds = $This->GetBondsToHydrogenAtoms(); 1210 } 1211 else { 1212 # All bonds... 1213 @Bonds = $This->GetBonds(); 1214 } 1215 1216 $LargestBondOrder = 0; 1217 for $Bond (@Bonds) { 1218 $BondOrder = $Bond->GetBondOrder(); 1219 if ($BondOrder > $LargestBondOrder) { 1220 $LargestBondOrder = $BondOrder; 1221 } 1222 } 1223 1224 return $LargestBondOrder; 1225 } 1226 1227 1228 # Valence corresponds to number of electrons used by an atom in bonding: 1229 # 1230 # Valence = ValenceElectrons - ValenceFreeElectrons = BondingElectrons 1231 # 1232 # Single, double, triple bonds with bond orders of 1, 2 and 3 correspond to contribution of 1233 # 1, 2, and 3 bonding electrons. So Valence can be computed using: 1234 # 1235 # Valence = SumOfBondOrders + FormalCharge 1236 # 1237 # where positive and negative values of FormalCharge increase and decrease the number 1238 # of bonding electrons respectively. 1239 # 1240 # Notes: 1241 # . For neutral molecules, valence and sum of bond order are equal. 1242 # . For molecues containing only single bonds, SumOfBondOrders and NumOfBonds are equal. 1243 # 1244 sub GetValence { 1245 my($This) = @_; 1246 1247 # Is this atom in a molecule? 1248 if (!$This->HasProperty('Molecule')) { 1249 return undef; 1250 } 1251 1252 # Is Valence property explicitly set? 1253 if ($This->HasProperty('Valence')) { 1254 return $This->GetProperty('Valence'); 1255 } 1256 my($FormalCharge, $Valence); 1257 1258 $Valence = $This->GetSumOfBondOrders(); 1259 if ($This->HasProperty('FormalCharge')) { 1260 $Valence += $This->GetProperty('FormalCharge'); 1261 } 1262 return $Valence; 1263 } 1264 1265 # For elements with one one common valence, ImplictValence corresponds to: 1266 # 1267 # . ImplicitValence = CommonValence + FormalCharge + SpinMultiplicityCorrection 1268 # 1269 # For elements with multiple common valences and no explicit FormalCharge assignment, 1270 # ImplicitValence is determined based on SumOfBondOrdersToNonHydrogenAtoms and next 1271 # available common valence. 1272 # 1273 # For elements with multiple common valences and explicit FormalCharge assignment, 1274 # ImplicitValence corresponds to: 1275 # 1276 # . ImplicitValence = HighestCommonValence + FormalCharge + SpinMultiplicityCorrection 1277 # 1278 # Notes: 1279 # . For atoms with explicit assignment of SpinMultiplicity property values corresponding to 1280 # Singlet (two unparied electrons corresponding to one spin state), Doublet (free radical; an unpaired 1281 # electron corresponding to two spin states), and Triplet (two unparied electrons corresponding to 1282 # three spin states; divalent carbon atoms (carbenes)), SpinMultiplicityCorrection is calculated as follows: 1283 # 1284 # SpinMultiplicity: Doublet(2); SpinMultiplicityCorrection: -1 (one valence electron not available for bonding) 1285 # SpinMultiplicity: Singlet(1)/Triplet(3); SpinMultiplicityCorrection: -2 (two valence electrons not available for bonding) 1286 # 1287 # 1288 sub GetImplicitValence { 1289 my($This) = @_; 1290 1291 # Is this atom in a molecule? 1292 if (!$This->HasProperty('Molecule')) { 1293 return undef; 1294 } 1295 1296 # Is ImplicitValence property explicitly set? 1297 if ($This->HasProperty('ImplicitValence')) { 1298 return $This->GetProperty('ImplicitValence'); 1299 } 1300 # Assign implicit valence... 1301 my($AtomicNumber, $CommonValences); 1302 1303 $AtomicNumber = $This->{AtomicNumber}; 1304 if (!$AtomicNumber) { 1305 return 0; 1306 } 1307 1308 $CommonValences = PeriodicTable::GetElementCommonValences($AtomicNumber); 1309 if (!$CommonValences) { 1310 return undef; 1311 } 1312 my($ImplicitValence, $FormalCharge, $SpinMultiplicityCorrection, @AvailableCommonValences); 1313 1314 $FormalCharge = $This->GetFormalCharge(); 1315 if (!defined $FormalCharge) { 1316 $FormalCharge = 0; 1317 } 1318 1319 $SpinMultiplicityCorrection = 0; 1320 if ($This->HasProperty('SpinMultiplicity')) { 1321 my($SpinMultiplicity); 1322 $SpinMultiplicity = $This->GetSpinMultiplicity(); 1323 SPINMULTIPLICITY: { 1324 if ($SpinMultiplicity =~ /^2$/i) { $SpinMultiplicityCorrection = -1; last SPINMULTIPLICITY; } 1325 if ($SpinMultiplicity =~ /^(1|3)$/i) { $SpinMultiplicityCorrection = -2; last SPINMULTIPLICITY; } 1326 $SpinMultiplicityCorrection = 0; 1327 } 1328 } 1329 1330 @AvailableCommonValences = split /\,/, $CommonValences; 1331 1332 # Only only available common valence... 1333 if (@AvailableCommonValences == 1) { 1334 my($CommonValence); 1335 1336 $CommonValence = $CommonValences; 1337 $ImplicitValence = $CommonValence + $FormalCharge + $SpinMultiplicityCorrection; 1338 1339 return $ImplicitValence; 1340 } 1341 1342 # Non-zero formal charge... 1343 if ($FormalCharge != 0) { 1344 # Use HighestCommonValence to set ImplicitValence... 1345 my($HighestCommonValence); 1346 1347 $HighestCommonValence = $This->GetHighestCommonValence(); 1348 $ImplicitValence = $HighestCommonValence + $FormalCharge + $SpinMultiplicityCorrection; 1349 1350 return $ImplicitValence; 1351 } 1352 1353 # Set ImplicitValence using SumOfBondOrdersToNonHydrogenAtoms and appropriate CommonValence 1354 # value... 1355 my($SumOfBondOrders, $ValenceFound, $Valence, $AvailableCommonValence); 1356 1357 $SumOfBondOrders = $This->GetSumOfBondOrdersToNonHydrogenAtoms(); 1358 if (!defined $SumOfBondOrders) { 1359 $SumOfBondOrders = 0; 1360 } 1361 1362 # Get first available valence higher than SumOfBondOrders... 1363 $ValenceFound = 0; 1364 $Valence = 0; 1365 VALENCE: for $AvailableCommonValence (@AvailableCommonValences) { 1366 if ($AvailableCommonValence >= $SumOfBondOrders) { 1367 $ValenceFound = 1; 1368 $Valence = $AvailableCommonValence; 1369 last VALENCE; 1370 } 1371 } 1372 if (!$ValenceFound) { 1373 # Use highest common valence... 1374 $Valence = $This->GetHighestCommonValence(); 1375 } 1376 $ImplicitValence = $Valence + $SpinMultiplicityCorrection; 1377 1378 return $ImplicitValence; 1379 } 1380 1381 # Get lowest common valence... 1382 sub GetLowestCommonValence { 1383 my($This) = @_; 1384 1385 # Is LowestCommonValence property explicitly set? 1386 if ($This->HasProperty('LowestCommonValence')) { 1387 return $This->GetProperty('LowestCommonValence'); 1388 } 1389 my($AtomicNumber, $LowestCommonValence); 1390 1391 $AtomicNumber = $This->{AtomicNumber}; 1392 if (!$AtomicNumber) { 1393 return 0; 1394 } 1395 1396 # LowestCommonValence is not set for all elements... 1397 $LowestCommonValence = PeriodicTable::GetElementLowestCommonValence($AtomicNumber); 1398 if (!$LowestCommonValence) { 1399 $LowestCommonValence = undef; 1400 } 1401 1402 return $LowestCommonValence; 1403 } 1404 1405 # Get highest common valence... 1406 sub GetHighestCommonValence { 1407 my($This) = @_; 1408 1409 # Is HighestCommonValence property explicitly set? 1410 if ($This->HasProperty('HighestCommonValence')) { 1411 return $This->GetProperty('HighestCommonValence'); 1412 } 1413 my($AtomicNumber, $HighestCommonValence); 1414 1415 $AtomicNumber = $This->{AtomicNumber}; 1416 if (!$AtomicNumber) { 1417 return 0; 1418 } 1419 1420 # HighestCommonValence is not set for all elements... 1421 $HighestCommonValence = PeriodicTable::GetElementHighestCommonValence($AtomicNumber); 1422 if (!$HighestCommonValence) { 1423 $HighestCommonValence = undef; 1424 } 1425 1426 return $HighestCommonValence; 1427 } 1428 1429 # Get valence electrons... 1430 sub GetValenceElectrons { 1431 my($This) = @_; 1432 1433 # Is ValenceElectrons property explicitly set? 1434 if ($This->HasProperty('ValenceElectrons')) { 1435 return $This->GetProperty('ValenceElectrons'); 1436 } 1437 my($AtomicNumber, $ValenceElectrons); 1438 1439 $AtomicNumber = $This->{AtomicNumber}; 1440 if (!$AtomicNumber) { 1441 return 0; 1442 } 1443 1444 $ValenceElectrons = PeriodicTable::GetElementValenceElectrons($AtomicNumber); 1445 1446 return $ValenceElectrons; 1447 } 1448 1449 # Get free non-bodning valence electrons left on atom after taking into account 1450 # sum of bond orders and formal charged on atom. 1451 # 1452 # Valence corresponds to number of electrons used by atom in bonding: 1453 # 1454 # Valence = ValenceElectrons - ValenceFreeElectrons 1455 # 1456 # Additionally, valence can also be calculated by: 1457 # 1458 # Valence = SumOfBondOrders + FormalCharge 1459 # 1460 # Implying for neutral molecules, Valence and SumOfBondOrders are equal. 1461 # 1462 # From two formulas for Valence described above, non-bonding free electrons 1463 # left can be computed by: 1464 # 1465 # ValenceFreeElectrons = ValenceElectrons - SumOfBondOrders - FormalCharge 1466 # 1467 # Examples: 1468 # 1469 # o NH3: ValenceFreeElectrons = 5 - 3 - 0 = 2 1470 # o NH4+; ValenceFreeElectrons = 5 - 4 - 1 = 0 1471 # o C(=O)O- : ValenceFreeElectrons on O- = 6 - 1 + 1 = 6 1472 # o C(=O)O- : ValenceFreeElectrons on =O = 6 - 2 - 0 = 4 1473 # 1474 # 1475 sub GetValenceFreeElectrons { 1476 my($This) = @_; 1477 1478 # Is this atom in a molecule? 1479 if (!$This->HasProperty('Molecule')) { 1480 return undef; 1481 } 1482 1483 # Is ValenceFreeElectrons property explicitly set? 1484 if ($This->HasProperty('ValenceFreeElectrons')) { 1485 return $This->GetProperty('ValenceFreeElectrons'); 1486 } 1487 my($ValenceElectrons, $SumOfBondOrders, $ValenceFreeElectrons); 1488 1489 if (!$This->{AtomicNumber}) { 1490 return 0; 1491 } 1492 $ValenceElectrons = $This->GetValenceElectrons(); 1493 $SumOfBondOrders = $This->GetSumOfBondOrders(); 1494 1495 $ValenceFreeElectrons = $ValenceElectrons - $SumOfBondOrders; 1496 1497 if ($This->HasProperty('FormalCharge')) { 1498 $ValenceFreeElectrons -= $This->GetProperty('FormalCharge'); 1499 } 1500 return $ValenceFreeElectrons; 1501 } 1502 1503 # Get num of missing hydrogens... 1504 # 1505 sub GetNumOfMissingHydrogens { 1506 my($This) = @_; 1507 my($NumOfMissingHydrogens); 1508 1509 $NumOfMissingHydrogens = $This->GetImplicitHydrogens() - $This->GetExplicitHydrogens(); 1510 1511 return $NumOfMissingHydrogens; 1512 } 1513 1514 # Get number of implicit hydrogen for atom... 1515 # 1516 sub GetImplicitHydrogens { 1517 my($This) = @_; 1518 1519 # Is this atom in a molecule? 1520 if (!$This->HasProperty('Molecule')) { 1521 return undef; 1522 } 1523 1524 # Is ImplicitHydrogens property explicitly set? 1525 if ($This->HasProperty('ImplicitHydrogens')) { 1526 return $This->GetProperty('ImplicitHydrogens'); 1527 } 1528 1529 # Is it an element symbol? 1530 if (!$This->{AtomicNumber}) { 1531 return 0; 1532 } 1533 my($ImplicitHydrogens, $ImplicitValence, $SumOfBondOrders); 1534 1535 $ImplicitHydrogens = 0; 1536 $ImplicitValence = $This->GetImplicitValence(); 1537 $SumOfBondOrders = $This->GetSumOfBondOrdersToNonHydrogenAtoms(); 1538 1539 if (defined($ImplicitValence) && defined($SumOfBondOrders)) { 1540 $ImplicitHydrogens = ($SumOfBondOrders >= $ImplicitValence) ? 0 : ($ImplicitValence - $SumOfBondOrders); 1541 } 1542 1543 return $ImplicitHydrogens; 1544 } 1545 1546 # Get number of explicit hydrogens for atom... 1547 sub GetExplicitHydrogens { 1548 my($This) = @_; 1549 1550 return $This->GetNumOfHydrogenAtomNeighbors(); 1551 } 1552 1553 # Add hydrogens to specified atom in molecule and return number of hydrogens added: 1554 # 1555 # o HydrogensToAdd = ImplicitHydrogenCount - ExplicitHydrogenCount 1556 # 1557 # o XYZ are set to ZeroVector 1558 # 1559 sub AddHydrogens { 1560 my($This, $HydrogenPositionsWarning) = @_; 1561 1562 # Is this atom in a molecule? 1563 if (!$This->HasProperty('Molecule')) { 1564 return undef; 1565 } 1566 if (!defined $HydrogenPositionsWarning) { 1567 $HydrogenPositionsWarning = 1; 1568 } 1569 if ($HydrogenPositionsWarning) { 1570 carp "Warning: ${ClassName}->AddHydrogens: The current release of MayaChemTools doesn't assign any hydrogen positions..."; 1571 } 1572 1573 # Is it an element symbol? 1574 if (!$This->{AtomicNumber}) { 1575 return 0; 1576 } 1577 1578 my($Molecule, $HydrogensAdded, $HydrogensToAdd); 1579 1580 $Molecule = $This->GetProperty('Molecule'); 1581 $HydrogensAdded = 0; 1582 $HydrogensToAdd = $This->GetNumOfMissingHydrogens(); 1583 if ($HydrogensToAdd <= 0) { 1584 return $HydrogensAdded; 1585 } 1586 1587 my($Count, $Hydrogen); 1588 1589 for $Count (1 .. $HydrogensToAdd) { 1590 $HydrogensAdded++; 1591 1592 $Hydrogen = $Molecule->NewAtom('AtomSymbol' => 'H', 'XYZ' => [0, 0, 0]); 1593 $Molecule->NewBond('Atoms' => [$This, $Hydrogen], 'BondOrder' => 1); 1594 } 1595 1596 return $HydrogensAdded; 1597 } 1598 1599 # Delete hydrogens attached to atom in molecule and return total number of hydrogens deleted... 1600 sub DeleteHydrogens { 1601 my($This) = @_; 1602 1603 # Is this atom in a molecule? 1604 if (!$This->HasProperty('Molecule')) { 1605 return undef; 1606 } 1607 1608 # Is it an element symbol? 1609 if (!$This->{AtomicNumber}) { 1610 return 0; 1611 } 1612 1613 my($Molecule, $Neighbor, $HydrogensDeleted, @Neighbors); 1614 1615 $Molecule = $This->GetProperty('Molecule'); 1616 $HydrogensDeleted = 0; 1617 @Neighbors = $This->GetNeighbors(); 1618 1619 NEIGHBOR: for $Neighbor (@Neighbors) { 1620 if (!$Neighbor->IsHydrogen()) { 1621 next NEIGHBOR; 1622 } 1623 $Molecule->_DeleteAtom($Neighbor); 1624 $HydrogensDeleted++; 1625 } 1626 1627 return $HydrogensDeleted; 1628 } 1629 1630 # Copy atom and all its associated data... 1631 sub Copy { 1632 my($This) = @_; 1633 my($Atom); 1634 1635 $Atom = Storable::dclone($This); 1636 1637 return $Atom; 1638 } 1639 1640 # Get atomic invariant value... 1641 # 1642 sub GetAtomicInvariantValue { 1643 my($This, $AtomicInvariant) = @_; 1644 my($Value); 1645 1646 $Value = ""; 1647 1648 ATOMICVARIANT: { 1649 if ($AtomicInvariant =~ /^(AS|AtomSymbol|ElementSymbol)$/i) { 1650 $Value = $This->GetAtomSymbol(); 1651 last ATOMICVARIANT; 1652 } 1653 if ($AtomicInvariant =~ /^(X|NumOfNonHydrogenAtomNeighbors|NumOfHeavyAtomNeighbors)$/i) { 1654 $Value = $This->GetNumOfNonHydrogenAtomNeighbors(); 1655 last ATOMICVARIANT; 1656 } 1657 if ($AtomicInvariant =~ /^(BO|SumOfBondOrdersToNonHydrogenAtoms|SumOfBondOrdersToHeavyAtoms)$/i) { 1658 $Value = $This->GetSumOfBondOrdersToNonHydrogenAtoms(); 1659 last ATOMICVARIANT; 1660 } 1661 if ($AtomicInvariant =~ /^(LBO|LargestBondOrderToNonHydrogenAtoms|LargestBondOrderToHeavyAtoms)$/i) { 1662 $Value = $This->GetLargestBondOrderToNonHydrogenAtoms(); 1663 last ATOMICVARIANT; 1664 } 1665 if ($AtomicInvariant =~ /^(H|NumOfImplicitAndExplicitHydrogens)$/i) { 1666 $Value = $This->GetNumOfMissingHydrogens() + $This->GetExplicitHydrogens(); 1667 last ATOMICVARIANT; 1668 } 1669 if ($AtomicInvariant =~ /^(SB|NumOfSingleBondsToNonHydrogenAtoms|NumOfSingleBondsToHeavyAtoms)$/i) { 1670 $Value = $This->GetNumOfSingleBondsToNonHydrogenAtoms(); 1671 last ATOMICVARIANT; 1672 } 1673 if ($AtomicInvariant =~ /^(DB|NumOfDoubleBondsToNonHydrogenAtoms|NumOfDoubleBondsToHeavyAtoms)$/i) { 1674 $Value = $This->GetNumOfDoubleBondsToNonHydrogenAtoms(); 1675 last ATOMICVARIANT; 1676 } 1677 if ($AtomicInvariant =~ /^(TB|NumOfTripleBondsToNonHydrogenAtoms|NumOfTripleBondsToHeavyAtoms)$/i) { 1678 $Value = $This->GetNumOfTripleBondsToNonHydrogenAtoms(); 1679 last ATOMICVARIANT; 1680 } 1681 if ($AtomicInvariant =~ /^(AB|NumOfAromaticBondsToNonHydrogenAtoms|NumOfAromaticBondsToHeavyAtoms)$/i) { 1682 $Value = $This->GetNumOfAromaticBondsToNonHydrogenAtoms(); 1683 last ATOMICVARIANT; 1684 } 1685 if ($AtomicInvariant =~ /^(FC|FormalCharge)$/i) { 1686 $Value = $This->GetFormalCharge(); 1687 $Value = defined $Value ? $Value : 0; 1688 last ATOMICVARIANT; 1689 } 1690 if ($AtomicInvariant =~ /^(T|TotalNumOfAtomNeighbors)$/i) { 1691 $Value = $This->GetNumOfNonHydrogenAtomNeighbors() + $This->GetNumOfMissingHydrogens() + $This->GetExplicitHydrogens(); 1692 last ATOMICVARIANT; 1693 } 1694 if ($AtomicInvariant =~ /^(TSB|TotalNumOfSingleBonds)$/i) { 1695 $Value = $This->GetNumOfSingleBondsToNonHydrogenAtoms() + $This->GetNumOfMissingHydrogens() + $This->GetExplicitHydrogens(); 1696 last ATOMICVARIANT; 1697 } 1698 if ($AtomicInvariant =~ /^(Ar|Aromatic)$/i) { 1699 $Value = $This->IsAromatic() ? 1 : 0; 1700 last ATOMICVARIANT; 1701 } 1702 if ($AtomicInvariant =~ /^(RA|RingAtom)$/i) { 1703 $Value = $This->IsInRing() ? 1 : 0; 1704 last ATOMICVARIANT; 1705 } 1706 if ($AtomicInvariant =~ /^(Str|Stereochemistry)$/i) { 1707 $Value = $This->GetStereochemistry(); 1708 $Value= (defined($Value) && ($Value =~ /^(R|S)$/i)) ? $Value : ''; 1709 last ATOMICVARIANT; 1710 } 1711 if ($AtomicInvariant =~ /^(AN|AtomicNumber)$/i) { 1712 $Value = $This->GetAtomicNumber(); 1713 last ATOMICVARIANT; 1714 } 1715 if ($AtomicInvariant =~ /^(AM|AtomicMass)$/i) { 1716 $Value = round($This->GetExactMass(), 4) + 0; 1717 last ATOMICVARIANT; 1718 } 1719 if ($AtomicInvariant =~ /^(MN|MassNumber)$/i) { 1720 $Value = $This->GetMassNumber(); 1721 last ATOMICVARIANT; 1722 } 1723 if ($AtomicInvariant =~ /^(SM|SpinMultiplicity)$/i) { 1724 $Value = $This->GetSpinMultiplicity(); 1725 $Value = defined $Value ? $Value : ''; 1726 last ATOMICVARIANT; 1727 } 1728 $Value = ""; 1729 carp "Warning: ${ClassName}->GetAtomicInvariantValue: Unknown atomic invariant $AtomicInvariant..."; 1730 } 1731 1732 return $Value; 1733 } 1734 1735 # Get period number of the atom.. 1736 # 1737 sub GetPeriodNumber { 1738 my($This) = @_; 1739 1740 # Is PeriodNumber property explicitly set? 1741 if ($This->HasProperty('PeriodNumber')) { 1742 return $This->GetProperty('PeriodNumber'); 1743 } 1744 my($AtomicNumber, $PeriodNumber); 1745 1746 $AtomicNumber = $This->{AtomicNumber}; 1747 if (!$AtomicNumber) { 1748 return 0; 1749 } 1750 1751 $PeriodNumber = PeriodicTable::GetElementPeriodNumber($AtomicNumber); 1752 1753 return $PeriodNumber; 1754 } 1755 1756 # Get group number of the atom.. 1757 # 1758 sub GetGroupNumber { 1759 my($This) = @_; 1760 1761 # Is GroupNumber property explicitly set? 1762 if ($This->HasProperty('GroupNumber')) { 1763 return $This->GetProperty('GroupNumber'); 1764 } 1765 my($AtomicNumber, $GroupNumber); 1766 1767 $AtomicNumber = $This->{AtomicNumber}; 1768 if (!$AtomicNumber) { 1769 return 0; 1770 } 1771 1772 $GroupNumber = PeriodicTable::GetElementGroupNumber($AtomicNumber); 1773 1774 return $GroupNumber; 1775 } 1776 1777 # Is it a specified topological pharmacophore atom type? 1778 # 1779 sub IsTopologicalPharmacophoreType { 1780 my($This, $Type) = @_; 1781 1782 return $This->_IsFunctionalClassType($Type); 1783 } 1784 1785 # Is it a specified functional class atom type? 1786 # 1787 sub IsFunctionalClassType { 1788 my($This, $Type) = @_; 1789 1790 return $This->_IsFunctionalClassType($Type); 1791 } 1792 1793 # Is it a specified functional/topological pharmacophore atom type? 1794 # 1795 sub _IsFunctionalClassType { 1796 my($This, $Type) = @_; 1797 my($Value); 1798 1799 $Value = 0; 1800 1801 TYPE: { 1802 if ($Type =~ /^(HBD|HydrogenBondDonor)$/i) { 1803 $Value = $This->IsHydrogenBondDonor(); 1804 last TYPE; 1805 } 1806 if ($Type =~ /^(HBA|HydrogenBondAcceptor)$/i) { 1807 $Value = $This->IsHydrogenBondAcceptor(); 1808 last TYPE; 1809 } 1810 if ($Type =~ /^(PI|PositivelyIonizable)$/i) { 1811 $Value = $This->IsPositivelyIonizable(); 1812 last TYPE; 1813 } 1814 if ($Type =~ /^(NI|NegativelyIonizable)$/i) { 1815 $Value = $This->IsNegativelyIonizable(); 1816 last TYPE; 1817 } 1818 if ($Type =~ /^(H|Hydrophobic)$/i) { 1819 $Value = $This->IsHydrophobic(); 1820 last TYPE; 1821 } 1822 if ($Type =~ /^(Ar|Aromatic)$/i) { 1823 $Value = $This->IsAromatic(); 1824 last TYPE; 1825 } 1826 if ($Type =~ /^(Hal|Halogen)$/i) { 1827 $Value = $This->IsHalogen(); 1828 last TYPE; 1829 } 1830 if ($Type =~ /^(RA|RingAtom)$/i) { 1831 $Value = $This->IsInRing(); 1832 last TYPE; 1833 } 1834 if ($Type =~ /^(CA|ChainAtom)$/i) { 1835 $Value = $This->IsNotInRing(); 1836 last TYPE; 1837 } 1838 $Value = 0; 1839 carp "Warning: ${ClassName}->_IsType: Unknown functional/pharmacohore type $Type..."; 1840 } 1841 return $Value; 1842 } 1843 1844 # Is it a Hydrogen atom? 1845 sub IsHydrogen { 1846 my($This) = @_; 1847 1848 return ($This->{AtomicNumber} == 1) ? 1 : 0; 1849 } 1850 1851 # Is it a Carbon atom? 1852 sub IsCarbon { 1853 my($This) = @_; 1854 1855 return ($This->{AtomicNumber} == 6) ? 1 : 0; 1856 } 1857 1858 # Is it a Nitrogen atom? 1859 sub IsNitrogen { 1860 my($This) = @_; 1861 1862 return ($This->{AtomicNumber} == 7) ? 1 : 0; 1863 } 1864 1865 # Is it a Oxygen atom? 1866 sub IsOxygen { 1867 my($This) = @_; 1868 1869 return ($This->{AtomicNumber} == 8) ? 1 : 0; 1870 } 1871 1872 # Is it a Fluorine atom? 1873 sub IsFluorine { 1874 my($This) = @_; 1875 1876 return ($This->{AtomicNumber} == 9) ? 1 : 0; 1877 } 1878 1879 # Is it a Silicon atom? 1880 sub IsSilicon { 1881 my($This) = @_; 1882 1883 return ($This->{AtomicNumber} == 14) ? 1 : 0; 1884 } 1885 1886 # Is it a Phosphorus atom? 1887 sub IsPhosphorus { 1888 my($This) = @_; 1889 1890 return ($This->{AtomicNumber} == 15) ? 1 : 0; 1891 } 1892 1893 # Is it a Sulphur atom? 1894 sub IsSulphur { 1895 my($This) = @_; 1896 1897 return $This->IsSulfur(); 1898 } 1899 1900 # Is it a Sulfur atom? 1901 sub IsSulfur { 1902 my($This) = @_; 1903 1904 return ($This->{AtomicNumber} == 16) ? 1 : 0; 1905 } 1906 1907 # Is it a Chlorine atom? 1908 sub IsChlorine { 1909 my($This) = @_; 1910 1911 return ($This->{AtomicNumber} == 17) ? 1 : 0; 1912 } 1913 1914 # Is it a Bromine atom? 1915 sub IsBromine { 1916 my($This) = @_; 1917 1918 return ($This->{AtomicNumber} == 35) ? 1 : 0; 1919 } 1920 1921 # Is it a Iodine atom? 1922 sub IsIodine { 1923 my($This) = @_; 1924 1925 return ($This->{AtomicNumber} == 53) ? 1 : 0; 1926 } 1927 1928 # Is it a hetro atom? (N, O, F, P, S, Cl, Br, I) 1929 sub IsHetroAtom { 1930 my($This) = @_; 1931 1932 return ($This->{AtomicNumber} =~ /^(7|8|9|15|16|17|35|53)$/) ? 1 : 0; 1933 } 1934 1935 # Is it a halogen atom? (F, Cl, Br, I) 1936 sub IsHalogen { 1937 my($This) = @_; 1938 1939 return ($This->{AtomicNumber} =~ /^(9|17|35|53)$/) ? 1 : 0; 1940 } 1941 1942 # Is it classified as metallic? 1943 sub IsMetallic { 1944 my($This) = @_; 1945 my($Classification); 1946 1947 $Classification = PeriodicTable::GetElementClassification($This->{AtomicNumber}); 1948 1949 return ($Classification =~ /^Metallic$/i) ? 1 : 0; 1950 } 1951 1952 # Is it a non carbon or hydrogen atom? (C, H) 1953 sub IsNonCarbonOrHydrogen { 1954 my($This) = @_; 1955 1956 return ($This->{AtomicNumber} =~ /^(1|6)$/) ? 0 : 1; 1957 } 1958 1959 # Is it a polar atom? ( N, O, P, S) 1960 sub IsPolarAtom { 1961 my($This) = @_; 1962 1963 return ($This->{AtomicNumber} =~ /^(7|8|15|16)$/) ? 1 : 0; 1964 } 1965 1966 # Is it an isotope? 1967 sub IsIsotope { 1968 my($This) = @_; 1969 1970 my($AtomicNumber) = $This->{AtomicNumber}; 1971 if (!$AtomicNumber) { 1972 return 0; 1973 } 1974 1975 if (!$This->HasProperty('MassNumber')) { 1976 return 0; 1977 } 1978 my($MassNumber, $MostAbundantMassNumber); 1979 1980 $MassNumber = $This->GetProperty('MassNumber'); 1981 $MostAbundantMassNumber = PeriodicTable::GetElementMostAbundantNaturalIsotopeMassNumber($AtomicNumber); 1982 1983 return ($MassNumber == $MostAbundantMassNumber) ? 0 : 1; 1984 } 1985 1986 # Is aromatic property set for the atom? 1987 sub IsAromatic { 1988 my($This) = @_; 1989 my($Aromatic); 1990 1991 $Aromatic = $This->GetAromatic(); 1992 1993 return (defined($Aromatic) && $Aromatic) ? 1 : 0; 1994 } 1995 1996 # Is this a hydrogen atom and attached to one of these atoms: N, O, P, S 1997 sub IsPolarHydrogen { 1998 my($This) = @_; 1999 2000 if (!$This->IsHydrogen()) { 2001 return 0; 2002 } 2003 2004 my(@Bonds); 2005 @Bonds = $This->GetBonds(); 2006 if (@Bonds > 1) { 2007 return 0; 2008 } 2009 2010 my($Bond, $BondedAtom); 2011 ($Bond) = @Bonds; 2012 $BondedAtom = $Bond->GetBondedAtom($This); 2013 2014 return $BondedAtom->IsPolarAtom() ? 1 : 0; 2015 } 2016 2017 # Is it a hydrogen bond donor atom? 2018 # 2019 sub IsHBondDonor { 2020 my($This, $HydrogenBondsType) = @_; 2021 2022 return $This->IsHydrogenBondDonor($HydrogenBondsType); 2023 } 2024 2025 # The currrent release of MayaChemTools supports identification of two types of 2026 # hydrogen bond donor and acceptor atoms with these names: 2027 # 2028 # HBondsType1 or HydrogenBondsType1 2029 # HBondsType2 or HydrogenBondsType2 2030 # 2031 # The names of these hydrogen bond types are rather arbitrary. However, their 2032 # definitions have specific meaning and are as follows: 2033 # 2034 # HydrogenBondsType1 [ Ref 60-61, Ref 65-66 ]: 2035 # . Donor: NH, NH2, NH3, OH - Any N and O with available H 2036 # . Acceptor: N[!H], O - Any N without available H and any O 2037 # 2038 # HydrogenBondsType2 [ Ref 91 ]: 2039 # . Donor: NH, NH2, NH3, OH - Any N and O with availabe H 2040 # . Acceptor: N, O - Any N and O 2041 # 2042 # Note: 2043 # . HydrogenBondsType2 definition corresponds to Rule of 5. 2044 # 2045 2046 # Is it a hydrogen bond donor atom? 2047 # 2048 # The currrent release of MayaChemTools supports identification of two types of 2049 sub IsHydrogenBondDonor { 2050 my($This, $HydrogenBondsType) = @_; 2051 my($Status); 2052 2053 $HydrogenBondsType = defined $HydrogenBondsType ? $HydrogenBondsType : 'HBondsType1'; 2054 $Status = 0; 2055 2056 HYDROGENBONDSTYPE: { 2057 2058 if ($HydrogenBondsType =~ /^(HBondsType1|HydrogenBondsType1)$/i) { 2059 $Status = $This->_IsHydrogenBondDonorOfType1(); 2060 last HYDROGENBONDSTYPE; 2061 } 2062 2063 if ($HydrogenBondsType =~ /^(HBondsType2|HydrogenBondsType2)$/i) { 2064 $Status = $This->_IsHydrogenBondDonorOfType2(); 2065 last HYDROGENBONDSTYPE; 2066 } 2067 2068 $Status = 0; 2069 carp "Warning: ${ClassName}->IsHydrogenBondDonor: The current release of MayaChemTools doesn't support specified value, $HydrogenBondsType, for HydrogenBondsType. Valid values: HBondsType1, HydrogenBondsType1, HBondsType2 HydrogenBondsType2 ..."; 2070 } 2071 2072 return $Status; 2073 } 2074 2075 # Is it a MayaChemTools HBondType1 hydrogen bond donor atom? 2076 # 2077 sub _IsHydrogenBondDonorOfType1 { 2078 my($This) = @_; 2079 2080 return $This->_IsHydrogenBondDonorOfType1OrType2(); 2081 } 2082 2083 # Is it a MayaChemTools HBondType2 hydrogen bond donor atom? 2084 # 2085 sub _IsHydrogenBondDonorOfType2 { 2086 my($This) = @_; 2087 2088 return $This->_IsHydrogenBondDonorOfType1OrType2(); 2089 } 2090 2091 # Is it a hydrogen bond donor atom of MayaChemTools Type1 or Type2? 2092 # 2093 # HydrogenBondDonor definition [ Ref 60-61, Ref 65-66, Ref 91 ]: NH, NH2, OH 2094 # 2095 # In other words: 2096 # . NH, NH2 - Nitrogen atom with available hydrogen 2097 # . OH - Oxygen atom with avilable hydrogen 2098 # 2099 sub _IsHydrogenBondDonorOfType1OrType2 { 2100 my($This) = @_; 2101 2102 # Is this atom in a molecule? 2103 if (!$This->HasProperty('Molecule')) { 2104 return 0; 2105 } 2106 2107 # Is it N or O? 2108 if ($This->{AtomicNumber} !~ /^(7|8)$/) { 2109 return 0; 2110 } 2111 2112 # Any explicitly attached hydrogens? 2113 if ($This->GetExplicitHydrogens()) { 2114 return 1; 2115 } 2116 2117 # Any missing hydrogens? 2118 return $This->GetNumOfMissingHydrogens() ? 1 : 0; 2119 } 2120 2121 # Is it a hydrogen bond acceptor atom? 2122 # 2123 sub IsHBondAcceptor { 2124 my($This, $HydrogenBondsType) = @_; 2125 2126 return $This->IsHydrogenBondAcceptor($HydrogenBondsType); 2127 } 2128 2129 # Is it a hydrogen bond acceptor atom? 2130 # 2131 sub IsHydrogenBondAcceptor { 2132 my($This, $HydrogenBondsType) = @_; 2133 my($Status); 2134 2135 $HydrogenBondsType = defined $HydrogenBondsType ? $HydrogenBondsType : 'HBondsType1'; 2136 $Status = 0; 2137 2138 HYDROGENBONDSTYPE: { 2139 2140 if ($HydrogenBondsType =~ /^(HBondsType1|HydrogenBondsType1)$/i) { 2141 $Status = $This->_IsHydrogenBondAcceptorOfType1(); 2142 last HYDROGENBONDSTYPE; 2143 } 2144 2145 if ($HydrogenBondsType =~ /^(HBondsType2|HydrogenBondsType2)$/i) { 2146 $Status = $This->_IsHydrogenBondAcceptorOfType2(); 2147 last HYDROGENBONDSTYPE; 2148 } 2149 2150 $Status = 0; 2151 carp "Warning: ${ClassName}->IsHydrogenBondAcceptor: The current release of MayaChemTools doesn't support specified value, $HydrogenBondsType, for HydrogenBondsType. Valid values: HBondsType1, HydrogenBondsType1, HBondsType2 HydrogenBondsType2 ..."; 2152 } 2153 2154 return $Status; 2155 } 2156 2157 # Is it a MayaChemTools HBondType1 hydrogen bond acceptor atom? 2158 # 2159 # HydrogenBondAcceptor definition [ Ref 60-61, Ref 65-66 ]: N[!H], O 2160 # 2161 # In other words: 2162 # . N[!H] - Nitrogen atom with no hydrogen 2163 # . O - Oxygen atom 2164 # 2165 sub _IsHydrogenBondAcceptorOfType1 { 2166 my($This) = @_; 2167 2168 # Is this atom in a molecule? 2169 if (!$This->HasProperty('Molecule')) { 2170 return 0; 2171 } 2172 2173 # Is it N or O? 2174 if ($This->{AtomicNumber} !~ /^(7|8)$/) { 2175 return 0; 2176 } 2177 2178 # Is it O? 2179 if ($This->{AtomicNumber} == 8 ) { 2180 return 1; 2181 } 2182 2183 # Any explicitly attached hydrogens? 2184 if ($This->GetExplicitHydrogens()) { 2185 return 0; 2186 } 2187 2188 # Any missing hydrogens? 2189 return $This->GetNumOfMissingHydrogens() ? 0 : 1; 2190 } 2191 2192 # Is it a MayaChemTools HBondType2 hydrogen bond acceptor atom? 2193 # 2194 # HydrogenBondAcceptor definition [ Ref 91 ]: N, O 2195 # 2196 # In other words: 2197 # . Any Nitrogen or Oxygen atom 2198 # 2199 # Note: 2200 # . HydrogenBondsType2 definition corresponds to Rule of 5. 2201 # 2202 sub _IsHydrogenBondAcceptorOfType2 { 2203 my($This) = @_; 2204 2205 # Is this atom in a molecule? 2206 if (!$This->HasProperty('Molecule')) { 2207 return 0; 2208 } 2209 2210 return ($This->{AtomicNumber} =~ /^(7|8)$/) ? 1 : 0; 2211 } 2212 2213 # Is it a positively ionizable atom? 2214 # 2215 # PositivelyIonizable defintion [ Ref 60-61, Ref 65-66 ]: +, NH2 2216 # 2217 # In other words: 2218 # . Any atom with positve formal charge 2219 # . NH2 - Nitogen atom in amino group 2220 # 2221 sub IsPositivelyIonizable { 2222 my($This) = @_; 2223 my($FormalCharge); 2224 2225 # Is this atom in a molecule? 2226 if (!$This->HasProperty('Molecule')) { 2227 return 0; 2228 } 2229 2230 # Any explicit positive formal charge? 2231 $FormalCharge = $This->GetFormalCharge(); 2232 if (defined($FormalCharge) && $FormalCharge > 0) { 2233 return 1; 2234 } 2235 2236 # Is it N? 2237 if ($This->{AtomicNumber} != 7 ) { 2238 return 0; 2239 } 2240 2241 return (($This->GetExplicitHydrogens() + $This->GetNumOfMissingHydrogens()) == 2) ? 1 : 0; 2242 } 2243 2244 # Is it a negatively ionizable atom? 2245 # 2246 # NegativelyIonizable definition [ Ref 60-61, Ref 65-66 ]: -, C(=O)OH, S(=O)OH, P(=O)OH 2247 # 2248 # In other words: 2249 # . Any atom with negative formal charge 2250 # . Carbon atom in C(=O)OH group 2251 # . Phosphorous in P(=O)OH group 2252 # . Sulfur atom in S(=O)OH group 2253 # 2254 sub IsNegativelyIonizable { 2255 my($This) = @_; 2256 my($FormalCharge); 2257 2258 # Is this atom in a molecule? 2259 if (!$This->HasProperty('Molecule')) { 2260 return 0; 2261 } 2262 2263 # Any explicit negative formal charge? 2264 $FormalCharge = $This->GetFormalCharge(); 2265 if (defined($FormalCharge) && $FormalCharge < 0) { 2266 return 1; 2267 } 2268 2269 # Is it C, P or S? 2270 if ($This->{AtomicNumber} !~ /^(6|15|16)$/ ) { 2271 return 0; 2272 } 2273 2274 # Collect oxygens connected to C, P or S with single or double bonds and not connected to 2275 # any other heavy atom... 2276 my($Neighbor, $NeighborOxygenBondOrder, $NumOfNeighborOxygensWithSingleBonds, $NumOfNeighborOxygensWithDoubleBonds); 2277 2278 $NumOfNeighborOxygensWithSingleBonds = 0; $NumOfNeighborOxygensWithDoubleBonds = 0; 2279 2280 NEIGHBOR: for $Neighbor ($This->GetNeighbors()) { 2281 # Is it an oxygen? 2282 if ($Neighbor->{AtomicNumber} != 8) { 2283 next NEIGHBOR; 2284 } 2285 # Is oxygent connected to only heavy atom? 2286 if ($Neighbor->GetNumOfHeavyAtomNeighbors() != 1) { 2287 next NEIGHBOR; 2288 } 2289 $NeighborOxygenBondOrder = $This->GetBondToAtom($Neighbor)->GetBondOrder(); 2290 2291 if ($NeighborOxygenBondOrder == 2) { 2292 $NumOfNeighborOxygensWithDoubleBonds++; 2293 } 2294 elsif ($NeighborOxygenBondOrder == 1) { 2295 $NumOfNeighborOxygensWithSingleBonds++; 2296 } 2297 } 2298 return ($NumOfNeighborOxygensWithDoubleBonds >= 1 && $NumOfNeighborOxygensWithSingleBonds >= 1) ? 1 : 0; 2299 } 2300 2301 # Is it a liphophilic atom? 2302 # 2303 # Lipophilic definition [ Ref 60-61, Ref 65-66 ]: C(C)(C)(C)(C), Cl, Br, I, S(C)(C) 2304 # 2305 # In other words: 2306 # . C(C)(C)(C)(C) - Carbon atom connected to only other carbons 2307 # . Chlorine, Bromine or Iodine atom 2308 # . S(C)(C) - Sulfur connected to two carbons 2309 # 2310 sub IsLipophilic { 2311 my($This) = @_; 2312 2313 # Is this atom in a molecule? 2314 if (!$This->HasProperty('Molecule')) { 2315 return 0; 2316 } 2317 2318 # Is it Cl, Br, I? 2319 if ($This->{AtomicNumber} =~ /^(17|35|53)$/) { 2320 return 1; 2321 } 2322 2323 # Is it C, S? 2324 if ($This->{AtomicNumber} !~ /^(6|16)$/) { 2325 return 0; 2326 } 2327 2328 # Are all heavy atom neighbors Carbons? 2329 my($HeavyAtomNeighbor, @HeavyAtomNeighbors); 2330 @HeavyAtomNeighbors = (); 2331 @HeavyAtomNeighbors = $This->GetHeavyAtomNeighbors(); 2332 2333 for $HeavyAtomNeighbor (@HeavyAtomNeighbors) { 2334 if ($HeavyAtomNeighbor->{AtomicNumber} != 6) { 2335 return 0; 2336 } 2337 } 2338 2339 # Does sulfur has two carbon neighbors? 2340 if ($This->{AtomicNumber} == 16) { 2341 if (@HeavyAtomNeighbors != 2) { 2342 return 0; 2343 } 2344 } 2345 return 1; 2346 } 2347 2348 # Is it hydrophobic? 2349 # 2350 sub IsHydrophobic { 2351 my($This) = @_; 2352 2353 return $This->IsLipophilic(); 2354 } 2355 2356 # Is it a Nitrogen atom in Guadinium group? 2357 # 2358 sub IsGuadiniumNitrogen { 2359 my($This) = @_; 2360 2361 # Is it Nitrogen? 2362 if (!$This->IsNitrogen()) { 2363 return 0; 2364 } 2365 2366 # Is it connected to a Guadinium Carbon? 2367 my($AtomNeighbor); 2368 2369 for $AtomNeighbor ($This->GetNonHydrogenAtomNeighbors()) { 2370 if ($AtomNeighbor->IsGuadiniumCarbon()) { 2371 return 1; 2372 } 2373 } 2374 2375 return 0; 2376 } 2377 2378 # Is it a Carbon atom in Guadinium group? 2379 # 2380 # Guadinium group definition: 2381 # 2382 # R2N-C(=NR)-(NR2) or R2N-C(=NR2+)-(NR2) 2383 # 2384 # where: 2385 # . R = Hydrogens or group of atoms attached through Carbon 2386 # . Only one of the three Nitrogens has a double bond to Carbon and has optional 2387 # formal charge allowing it to be neutral or charged state 2388 # 2389 sub IsGuadiniumCarbon { 2390 my($This) = @_; 2391 2392 # Is it Carbon? 2393 if (!$This->IsCarbon()) { 2394 return 0; 2395 } 2396 2397 # Match atom neighborhood... 2398 my($CentralAtomSpec, @NbrAtomSpecsRef, @NbrBondSpecsRef, @NbrOfNbrAtomSpecsRef); 2399 2400 $CentralAtomSpec = 'C.X3.BO4'; 2401 @NbrAtomSpecsRef = ('N.FC0', 'N.FC0', 'N.FC0,N.FC+1'); 2402 @NbrBondSpecsRef = ('-', '-', '='); 2403 @NbrOfNbrAtomSpecsRef = ('C,H', 'C,H', 'C,H'); 2404 2405 if ($This->DoesAtomNeighborhoodMatch($CentralAtomSpec, \@NbrAtomSpecsRef, \@NbrBondSpecsRef, \@NbrOfNbrAtomSpecsRef)) { 2406 return 1; 2407 } 2408 2409 return 0; 2410 } 2411 2412 # Is it a Nitrogen atom in Amide group? 2413 # 2414 sub IsAmideNitrogen { 2415 my($This) = @_; 2416 2417 # Is it Nitrogen? 2418 if (!$This->IsNitrogen()) { 2419 return 0; 2420 } 2421 2422 # Is it connected to a Amide Carbon? 2423 my($AtomNeighbor); 2424 2425 for $AtomNeighbor ($This->GetNonHydrogenAtomNeighbors()) { 2426 if ($AtomNeighbor->IsAmideCarbon()) { 2427 return 1; 2428 } 2429 } 2430 2431 return 0; 2432 } 2433 2434 # Is it a Carbon atom in Amide group? 2435 # 2436 # Amide group definition: R-C(=O)-N(-R')-R'' 2437 # 2438 # where: 2439 # . R = Hydrogen or groups of atoms attached through Carbon 2440 # . R' = Hydrogens or groups of atoms attached through Carbon or hetro atoms 2441 # . R'' = Hydrogens or groups of atoms attached through Carbon or hetro atoms 2442 # 2443 sub IsAmideCarbon { 2444 my($This) = @_; 2445 2446 # Is this atom in a molecule? 2447 if (!$This->HasProperty('Molecule')) { 2448 return 0; 2449 } 2450 2451 # Is it Carbon? 2452 if (!$This->IsCarbon()) { 2453 return 0; 2454 } 2455 2456 # Match atom neighborhood... 2457 my($CentralAtomSpec, @NbrAtomSpecsRef, @NbrBondSpecsRef, @NbrOfNbrAtomSpecsRef); 2458 2459 $CentralAtomSpec = 'C.X3.BO4,C.X2.BO3'; 2460 @NbrAtomSpecsRef = ('C,H', 'O', 'N'); 2461 @NbrBondSpecsRef = ('-', '=', '-'); 2462 @NbrOfNbrAtomSpecsRef = ('C,H', 'C', 'C,H,N,O,S,P,F,Cl,Br,I'); 2463 2464 if ($This->DoesAtomNeighborhoodMatch($CentralAtomSpec, \@NbrAtomSpecsRef, \@NbrBondSpecsRef, \@NbrOfNbrAtomSpecsRef)) { 2465 return 1; 2466 } 2467 2468 return 0; 2469 } 2470 2471 # Is it a Oxygen atom in Carboxylate group? 2472 # 2473 sub IsCarboxylateOxygen { 2474 my($This) = @_; 2475 2476 return $This->_MatchCarboxylateAndOrCarboxylOxygen('Carboxylate'); 2477 } 2478 2479 # Is it a Carbon atom in Carboxylate group? 2480 # 2481 # Carboxyl group definition: R-C(=O)-O- 2482 # 2483 sub IsCarboxylateCarbon { 2484 my($This) = @_; 2485 2486 return $This->_MatchCarboxylateAndOrCarboxylCarbon('Carboxylate'); 2487 } 2488 2489 # Is it a Oxygen atom in Carboxyl group? 2490 # 2491 sub IsCarboxylOxygen { 2492 my($This) = @_; 2493 2494 return $This->_MatchCarboxylateAndOrCarboxylOxygen('Carboxyl'); 2495 } 2496 2497 # Is it a Carbon atom in Carboxyl group? 2498 # 2499 # Carboxyl group definition: R-C(=O)-OH 2500 # 2501 sub IsCarboxylCarbon { 2502 my($This) = @_; 2503 2504 return $This->_MatchCarboxylateAndOrCarboxylCarbon('Carboxyl'); 2505 } 2506 2507 # Match Carboxylate and/or Carboxyl oxygen... 2508 # 2509 sub _MatchCarboxylateAndOrCarboxylOxygen { 2510 my($This, $Mode) = @_; 2511 2512 # Is it Oxygen? 2513 if (!$This->IsOxygen()) { 2514 return 0; 2515 } 2516 2517 # Is it connected to a Carboxylate Carbon? 2518 my($AtomNeighbor); 2519 2520 for $AtomNeighbor ($This->GetNonHydrogenAtomNeighbors()) { 2521 if ($AtomNeighbor->_MatchCarboxylateAndOrCarboxylCarbon($Mode)) { 2522 return 1; 2523 } 2524 } 2525 2526 return 0; 2527 } 2528 2529 # Match Carboxylate and Carboxyl Carbon 2530 # 2531 # Carboxylate group definition: R-C(=O)-O- 2532 # Carboxyl group definition: R-C(=O)-OH 2533 # 2534 # where: 2535 # . R = Hydrogens or groups of atoms attached through Carbon 2536 # 2537 sub _MatchCarboxylateAndOrCarboxylCarbon { 2538 my($This, $Mode) = @_; 2539 2540 # Is this atom in a molecule? 2541 if (!$This->HasProperty('Molecule')) { 2542 return 0; 2543 } 2544 2545 # Is it Carbon? 2546 if (!$This->IsCarbon()) { 2547 return 0; 2548 } 2549 2550 # Match atom neighborhood... 2551 my($CentralAtomSpec, @NbrAtomSpecsRef, @NbrBondSpecsRef, @NbrOfNbrAtomSpecsRef); 2552 2553 $CentralAtomSpec = 'C.X3.BO4,C.X2.BO3'; 2554 MODE: { 2555 if ($Mode =~ /^Carboxylate$/i) { 2556 @NbrAtomSpecsRef = ('C,H', 'O', 'O.X1.FC-1'); 2557 last MODE; 2558 } 2559 if ($Mode =~ /^Carboxyl$/i) { 2560 @NbrAtomSpecsRef = ('C,H', 'O', 'O.X1.FC0'); 2561 last MODE; 2562 } 2563 if ($Mode =~ /^CarboxylateOrCarboxyl$/i) { 2564 @NbrAtomSpecsRef = ('C,H', 'O', 'O.X1.FC-1,O.X1.FC0'); 2565 last MODE; 2566 } 2567 carp "Warning: ${ClassName}->_MatchCarboxylateAndCarboxylCarbon.: Unknown mode $Mode..."; 2568 return 0; 2569 } 2570 @NbrBondSpecsRef = ('-', '=', '-'); 2571 @NbrOfNbrAtomSpecsRef = ('C,H', 'C', 'C'); 2572 2573 if ($This->DoesAtomNeighborhoodMatch($CentralAtomSpec, \@NbrAtomSpecsRef, \@NbrBondSpecsRef, \@NbrOfNbrAtomSpecsRef)) { 2574 return 1; 2575 } 2576 2577 return 0; 2578 } 2579 2580 # Is it a Oxygen atom in Phosphate group? 2581 # 2582 sub IsPhosphateOxygen { 2583 my($This) = @_; 2584 2585 # Is it Oxygen? 2586 if (!$This->IsOxygen()) { 2587 return 0; 2588 } 2589 2590 # Is it connected to a Phosphate Phosphorus? 2591 my($AtomNeighbor); 2592 2593 for $AtomNeighbor ($This->GetNonHydrogenAtomNeighbors()) { 2594 if ($AtomNeighbor->IsPhosphatePhosphorus()) { 2595 return 1; 2596 } 2597 } 2598 2599 return 0; 2600 } 2601 2602 # Is it a Phosphorus atom in Phosphate group? 2603 # 2604 # Phosphate group definition: AO-(O=)P(-OA)-OA 2605 # 2606 # where: 2607 # . A = Any Groups of atoms including hydrogens 2608 # 2609 sub IsPhosphatePhosphorus { 2610 my($This) = @_; 2611 2612 # Is this atom in a molecule? 2613 if (!$This->HasProperty('Molecule')) { 2614 return 0; 2615 } 2616 2617 # Is it Phosphorus? 2618 if (!$This->IsPhosphorus()) { 2619 return 0; 2620 } 2621 2622 # Match atom neighborhood... 2623 my($CentralAtomSpec, @NbrAtomSpecsRef, @NbrBondSpecsRef, @NbrOfNbrAtomSpecsRef); 2624 2625 $CentralAtomSpec = 'P.X4.BO5'; 2626 @NbrAtomSpecsRef = ('O', 'O', 'O', 'O'); 2627 @NbrBondSpecsRef = ('-', '=', '-', '-'); 2628 @NbrOfNbrAtomSpecsRef = (undef, undef, undef, undef); 2629 2630 if ($This->DoesAtomNeighborhoodMatch($CentralAtomSpec, \@NbrAtomSpecsRef, \@NbrBondSpecsRef, \@NbrOfNbrAtomSpecsRef)) { 2631 return 1; 2632 } 2633 2634 return 0; 2635 } 2636 2637 2638 # Match central atom and its neighborhood using specified atom and bonds specifications... 2639 # 2640 # Let: 2641 # AS = Atom symbol corresponding to element symbol, atomic number (#n) or any 2642 # atom (A) 2643 # 2644 # X<n> = Number of non-hydrogen atom neighbors or heavy atoms attached to atom 2645 # T<n> = Total number of atom neighbors including implcit and explicit hydrogens 2646 # BO<n> = Sum of bond orders to non-hydrogen atom neighbors or heavy atoms attached to atom 2647 # LBO<n> = Largest bond order of non-hydrogen atom neighbors or heavy atoms attached to atom 2648 # SB<n> = Number of single bonds to non-hydrogen atom neighbors or heavy atoms attached to atom 2649 # TSB<n> = Total number of single bonds to atom neighbors including implcit and explicit hydrogens 2650 # DB<n> = Number of double bonds to non-hydrogen atom neighbors or heavy atoms attached to atom 2651 # TB<n> = Number of triple bonds to non-hydrogen atom neighbors or heavy atoms attached to atom 2652 # H<n> = Number of implicit and explicit hydrogens for atom 2653 # Ar = Aromatic annotation indicating whether atom is aromatic 2654 # RA or RA<n> = Ring atom annotation indicating whether atom is a ring 2655 # TR<n> = Total number of rings containing atom 2656 # FC<+n/-n> = Formal charge assigned to atom 2657 # MN<n> = Mass number indicating isotope other than most abundant isotope 2658 # SM<n> = Spin multiplicity of atom. Possible values: 1 (singlet), 2 (doublet) or 3 (triplet) 2659 # 2660 # Then: 2661 # 2662 # Atom specification corresponds to: 2663 # 2664 # AS.X<n>.T<n>.BO<n>.LBO<n>.<SB><n>.TSB<n>.<DB><n>.<TB><n>.H<n>.Ar.RA<n>.TR<n>FC<+n/-n>.MN<n>.SM<n> 2665 # 2666 # Except for AS which is a required atomic invariant in atom specification, all other atomic invariants are 2667 # optional. For an atom specification to match an atom, the values of all specified atomic invariants must 2668 # match. Exclamation in from of atomic invariant can be used to negate its effect during the match. 2669 # 2670 # A comma delimited atom specification string is used to match any one of the specifed atom specification. 2671 # 2672 # Notes: 2673 # . During atom specification match to an atom, the first atomic invariant is always assumed to 2674 # atom symbol. 2675 # . Atom match specfication is based on AtomicInvariantAtomTypes implemented in 2676 # AotmTypes::AtomicInvariantAtomType.pm module 2677 # 2678 # Examples: 2679 # . ('N', 'N', 'N') 2680 # . ('N.FC0', 'N.FC0', 'N,N.FC+1.H1') 2681 # . ('N.H2', 'N.H2', 'N.H1') 2682 # . ('C,N', '!N', '!H') 2683 # . ('C,N', 'N.Ar', 'N.R5') 2684 # 2685 # Let: 2686 # -|1|s|Single = Single bond 2687 # =|2|d|Double = Double bond 2688 # #|3|t|Triple = Triple bond 2689 # :|1.5|a|Ar|Aromatic = Aromatic bond 2690 # 2691 # @|RB|Ring = Ring bond 2692 # ~|*|Any = Any bond 2693 # 2694 # Then: 2695 # 2696 # Bond specification corresponds to: 2697 # 2698 # -.: 2699 # =.@ 2700 # Double.Aromatic 2701 # 2702 # For a bond specification to match bond between two atoms, the values of all specified bond symbols must 2703 # match. Exclamation in from of bond symbol can be used to negate its effect during the match. 2704 # 2705 # A comma delimited bond specification string is used to match any one of the specifed atom specification. 2706 # 2707 sub DoesAtomNeighborhoodMatch { 2708 my($CentralAtom, $CentralAtomSpec, $NbrAtomSpecsRef, $NbrBondSpecsRef, $NbrOfNbrAtomSpecsRef) = @_; 2709 my($NumOfNbrAtomSpecs, $NumOfNbrBondSpecs, $NumOfNbrOfNbrAtomSpecs); 2710 2711 # Is this atom in a molecule? 2712 if (!$CentralAtom->HasProperty('Molecule')) { 2713 return 0; 2714 } 2715 2716 $NumOfNbrAtomSpecs = defined $NbrAtomSpecsRef ? scalar @{$NbrAtomSpecsRef} : 0; 2717 $NumOfNbrBondSpecs = defined $NbrBondSpecsRef ? scalar @{$NbrBondSpecsRef} : 0; 2718 $NumOfNbrOfNbrAtomSpecs = defined $NbrOfNbrAtomSpecsRef ? scalar @{$NbrOfNbrAtomSpecsRef} : 0; 2719 2720 # Validate number of specifications... 2721 if ($NumOfNbrBondSpecs && ($NumOfNbrAtomSpecs != $NumOfNbrBondSpecs)) { 2722 carp "Warning: ${ClassName}->DoesAtomNeighborhoodMatch: Number of specified central atom, $NumOfNbrAtomSpecs, and bond, $NumOfNbrBondSpecs, specifications must be same; No neighborhood match performed ..."; 2723 return 0; 2724 } 2725 2726 if ($NumOfNbrOfNbrAtomSpecs && ($NumOfNbrOfNbrAtomSpecs != $NumOfNbrAtomSpecs)) { 2727 carp "Warning: ${ClassName}->DoesAtomNeighborhoodMatch: Number of specified central atom, $NumOfNbrAtomSpecs, and neighbor of neighbor atoms specifications, $NumOfNbrOfNbrAtomSpecs, must be same; No neighborhood match performed ..."; 2728 return 0; 2729 } 2730 2731 # Does central atom specification match? 2732 if (!$CentralAtom->_DoesAtomSpecificationMatch($CentralAtomSpec)) { 2733 return 0; 2734 } 2735 2736 # No neighbors to match... 2737 if (!$NumOfNbrAtomSpecs) { 2738 return 1; 2739 } 2740 2741 # Match neighbors... 2742 my($NbrSpecsMatched, $NbrSpecCount, $NbrSpecMatchCount, %NbrSpecAlreadyMatchedMap); 2743 2744 $NbrSpecCount = $NumOfNbrAtomSpecs; 2745 $NbrSpecMatchCount = 0; 2746 2747 %NbrSpecAlreadyMatchedMap = (); 2748 ($NbrSpecsMatched, $NbrSpecMatchCount) = $CentralAtom->_MatchAtomNeighborhoodUsingAtomBondSpecs($NbrSpecCount, $NbrSpecMatchCount, \%NbrSpecAlreadyMatchedMap, $NbrAtomSpecsRef, $NbrBondSpecsRef, $NbrOfNbrAtomSpecsRef); 2749 2750 if ($NbrSpecsMatched) { 2751 # It's match... 2752 return 1; 2753 } 2754 2755 # Match central atom's missing hydrogens with any unmatched atom 2756 # and bond specifications... 2757 # 2758 ($NbrSpecsMatched, $NbrSpecMatchCount) = $CentralAtom->_MatchAtomNeighborhoodUsingMissingHydrogens($NbrSpecCount, $NbrSpecMatchCount, \%NbrSpecAlreadyMatchedMap, $NbrAtomSpecsRef, $NbrBondSpecsRef, $NbrOfNbrAtomSpecsRef); 2759 2760 if ($NbrSpecsMatched) { 2761 # It's match... 2762 return 1; 2763 } 2764 2765 # No match... 2766 return 0; 2767 } 2768 2769 # Match central atom neighborhood atom and bond specifications... 2770 # 2771 sub _MatchAtomNeighborhoodUsingAtomBondSpecs { 2772 my($CentralAtom, $NbrSpecCount, $NbrSpecMatchCount, $NbrSpecAlreadyMatchedRef, $NbrAtomSpecsRef, $NbrBondSpecsRef, $NbrOfNbrAtomSpecsRef) = @_; 2773 my($Index, $NbrAtom, $NbrAtomSpec, $NbrBondSpec, $NbrOfNbrAtom, $NbrOfNbrAtomSpec, $MatchNbrOfNbrAtomSpecs, $NbrSpecsMatched); 2774 2775 $MatchNbrOfNbrAtomSpecs = (defined $NbrOfNbrAtomSpecsRef && scalar @{$NbrOfNbrAtomSpecsRef}) ? 1 : 0; 2776 2777 $NbrSpecsMatched = 0; 2778 2779 # Match central atom's immediate neighbors atom and bond specifications... 2780 NBRATOM: for $NbrAtom ($CentralAtom->GetNeighbors()) { 2781 NBRATOMSPEC: for $Index (0 .. ($NbrSpecCount - 1)) { 2782 if (exists $NbrSpecAlreadyMatchedRef->{$Index}) { 2783 next NBRATOMSPEC; 2784 } 2785 $NbrAtomSpec = $NbrAtomSpecsRef->[$Index]; 2786 $NbrBondSpec = $NbrBondSpecsRef->[$Index]; 2787 2788 $NbrOfNbrAtomSpec = $MatchNbrOfNbrAtomSpecs ? $NbrOfNbrAtomSpecsRef->[$Index] : undef; 2789 2790 # Match neighbor atom specification... 2791 if (!$NbrAtom->_DoesAtomSpecificationMatch($NbrAtomSpec)) { 2792 next NBRATOMSPEC; 2793 } 2794 2795 # Match central atom to neighbor atom bond specification... 2796 if (!$CentralAtom->_DoesBondSpecificationMatch($NbrAtom, $NbrBondSpec)) { 2797 next NBRATOMSPEC; 2798 } 2799 2800 # Match any neighbor of neighbor atom specifications... 2801 if (defined $NbrOfNbrAtomSpec) { 2802 # Go over the neighbors of central atom skipping the central atom... 2803 for $NbrOfNbrAtom ($NbrAtom->GetNeighbors($CentralAtom)) { 2804 if (!$NbrOfNbrAtom->_DoesAtomSpecificationMatch($NbrOfNbrAtomSpec)) { 2805 next NBRATOMSPEC; 2806 } 2807 } 2808 } 2809 2810 # It's a match for a neighbor atom specification... 2811 $NbrSpecAlreadyMatchedRef->{$Index} = $Index; 2812 $NbrSpecMatchCount++; 2813 2814 if ($NbrSpecMatchCount == $NbrSpecCount) { 2815 # It's match... 2816 $NbrSpecsMatched = 1; 2817 last NBRATOM; 2818 } 2819 # Match next neighbor atom... 2820 next NBRATOM; 2821 } 2822 } 2823 return ($NbrSpecsMatched, $NbrSpecMatchCount); 2824 } 2825 2826 # Match central atom's missing hydrogens with any unmatched atom and bond 2827 # specifications... 2828 # 2829 sub _MatchAtomNeighborhoodUsingMissingHydrogens { 2830 my($CentralAtom, $NbrSpecCount, $NbrSpecMatchCount, $NbrSpecAlreadyMatchedRef, $NbrAtomSpecsRef, $NbrBondSpecsRef, $NbrOfNbrAtomSpecsRef) = @_; 2831 my($Index, $NbrAtom, $NbrAtomSpec, $NbrBondSpec, $NumOfMissingHydrogens, $MissingHydrogensIndex, $NbrSpecsMatched, $AtomSpecMatched, $AtomSpec, $AtomSymbol); 2832 2833 $NbrSpecsMatched = 0; 2834 2835 $NumOfMissingHydrogens = $CentralAtom->GetNumOfMissingHydrogens(); 2836 if (($NbrSpecCount - $NbrSpecMatchCount) > $NumOfMissingHydrogens) { 2837 # It won't match... 2838 return ($NbrSpecsMatched, $NbrSpecMatchCount); 2839 } 2840 2841 MISSINGHYDROGENNBR: for $MissingHydrogensIndex (0 .. ($NumOfMissingHydrogens - 1)) { 2842 NBRATOMSPEC: for $Index (0 .. ($NbrSpecCount - 1)) { 2843 if (exists $NbrSpecAlreadyMatchedRef->{$Index}) { 2844 next NBRATOMSPEC; 2845 } 2846 $NbrAtomSpec = $NbrAtomSpecsRef->[$Index]; 2847 $NbrBondSpec = $NbrBondSpecsRef->[$Index]; 2848 2849 $NbrAtomSpec =~ s/ //g; 2850 2851 # Match neighbor atom specification hydrogen atom symbol... 2852 $AtomSpecMatched = 0; 2853 ATOMSPEC: for $AtomSpec (split /\,/, $NbrAtomSpec) { 2854 ($AtomSymbol) = split /\./, $AtomSpec; 2855 if ($AtomSymbol =~ /^(H|A|\*)$/i) { 2856 $AtomSpecMatched = 1; 2857 last ATOMSPEC; 2858 } 2859 } 2860 if (!$AtomSpecMatched) { 2861 next NBRATOMSPEC; 2862 } 2863 2864 # Match neighbor atom bond specification to singal bond... 2865 if (defined $NbrBondSpec) { 2866 $NbrBondSpec =~ s/ //g; 2867 if ($NbrBondSpec !~ /^(-|1|s|Single|\~|\*|Any)/i) { 2868 next NBRATOMSPEC; 2869 } 2870 } 2871 2872 # It's a match for a neighbor atom specification... 2873 $NbrSpecAlreadyMatchedRef->{$Index} = $Index; 2874 $NbrSpecMatchCount++; 2875 2876 if ($NbrSpecMatchCount == $NbrSpecCount) { 2877 # It's match... 2878 $NbrSpecsMatched = 1; 2879 last MISSINGHYDROGENNBR; 2880 } 2881 # Match next missing hydrogen neighbor... 2882 next MISSINGHYDROGENNBR; 2883 } 2884 } 2885 2886 return ($NbrSpecsMatched, $NbrSpecMatchCount); 2887 } 2888 2889 # Check whether atom matches supported atom specification... 2890 # 2891 sub _DoesAtomSpecificationMatch { 2892 my($This, $AtomSpecificationToMatch) = @_; 2893 my($AtomSpecification, $AtomicInvariant, $AtomSpecificationMatched, $AtomicInvariantMatched, $FirstMatch); 2894 2895 # Anything to match... 2896 if (!(defined($AtomSpecificationToMatch) && $AtomSpecificationToMatch)) { 2897 return 1; 2898 } 2899 2900 # Take out any spaces... 2901 $AtomSpecificationToMatch =~ s/ //g; 2902 2903 # Match specified atom specifications. For multiple atom specifications in a comma delimited string, 2904 # only one atom specification needs to match for a successful match... 2905 # 2906 for $AtomSpecification (split /\,/, $AtomSpecificationToMatch) { 2907 $AtomSpecificationMatched = 1; 2908 $FirstMatch = 1; 2909 2910 # Match all atom symbol atomic invariants... 2911 ATOMICINVARIANT: for $AtomicInvariant (split /\./, $AtomSpecification) { 2912 if ($FirstMatch) { 2913 # Match atom symbol atomic invariant... 2914 $FirstMatch = 0; 2915 $AtomicInvariantMatched = $This->_MatchAtomSymbolAtomicInvariant($AtomicInvariant); 2916 } 2917 else { 2918 # Match non atom symbol atomic invariant... 2919 $AtomicInvariantMatched = $This->_MatchNonAtomSymbolAtomicInvariant($AtomicInvariant); 2920 } 2921 2922 if (!$AtomicInvariantMatched) { 2923 # No need to match other atomic invariants... 2924 $AtomSpecificationMatched = 0; 2925 last ATOMICINVARIANT; 2926 } 2927 } 2928 2929 if ($AtomSpecificationMatched) { 2930 # No need to match other atom specifications... 2931 return 1; 2932 } 2933 } 2934 2935 # Nothing matched... 2936 return 0; 2937 } 2938 2939 # Check whether atom matches atom symbol atomic invariant... 2940 # 2941 sub _MatchAtomSymbolAtomicInvariant { 2942 my($This, $AtomicInvariant) = @_; 2943 my($NegateMatch, $Status, $AtomicNumber); 2944 2945 $Status = 0; 2946 $NegateMatch = 0; 2947 2948 # Does match needs to be negated? 2949 if ($AtomicInvariant =~ /^!/) { 2950 $NegateMatch = 1; 2951 $AtomicInvariant =~ s/^!//; 2952 } 2953 2954 ATOMICINVARIANT: { 2955 # Any atom match... 2956 if ($AtomicInvariant =~ /^(A|\*)$/i) { 2957 $Status = 1; 2958 last ATOMICINVARIANT; 2959 } 2960 2961 # Atomic number match... 2962 if ($AtomicInvariant =~ /^#/) { 2963 $AtomicNumber = $AtomicInvariant; $AtomicNumber =~ s/^#//; 2964 $Status = ($This->{AtomicNumber} == $AtomicNumber) ? 1 : 0; 2965 last ATOMICINVARIANT; 2966 } 2967 2968 # Atom symbol match... 2969 $Status = ($This->{AtomSymbol} =~ /^$AtomicInvariant$/i) ? 1 : 0; 2970 } 2971 2972 if ($NegateMatch) { 2973 $Status = $Status ? 0 : 1; 2974 } 2975 2976 return $Status; 2977 } 2978 2979 # Check whether atom matches non atom symbol atomic invariants... 2980 # 2981 sub _MatchNonAtomSymbolAtomicInvariant { 2982 my($This, $AtomicInvariant) = @_; 2983 my($NegateMatch, $Status, $Name, $Value, $UnknownName); 2984 2985 ($Status, $NegateMatch, $UnknownName) = ('0') x 3; 2986 2987 # Does match needs to be negated? 2988 if ($AtomicInvariant =~ /^!/) { 2989 $NegateMatch = 1; 2990 $AtomicInvariant =~ s/^!//; 2991 } 2992 2993 # Extract atomic invariant name and any value... 2994 if ($AtomicInvariant =~ /[0-9\*]+/) { 2995 ($Name, $Value) = $AtomicInvariant =~ /^([a-zA-Z]+)([0-9\-\+\*\>\<\=]+)$/; 2996 } 2997 else { 2998 ($Name, $Value) = ($AtomicInvariant, undef); 2999 } 3000 3001 NAME: { 3002 # Match number of non-hydrogen atom neighbors 3003 if ($Name =~ /^X$/i) { 3004 $Status = (defined($Value) && $This->GetNumOfNonHydrogenAtomNeighbors() == $Value) ? 1 : 0; 3005 last NAME; 3006 } 3007 3008 # Match total number of atom neighbors including missing and explicit hydrogens... 3009 if ($Name =~ /^T$/i) { 3010 $Status = (defined($Value) && ($This->GetNumOfNonHydrogenAtomNeighbors() + $This->GetNumOfMissingHydrogens() + $This->GetExplicitHydrogens()) == $Value) ? 1 : 0; 3011 last NAME; 3012 } 3013 3014 # Match formal charge... 3015 if ($Name =~ /^FC$/i) { 3016 my $FormalCharge = $This->GetFormalCharge(); 3017 $Status = $This->_MatchNonAtomSymbolAtomicInvariantValue($FormalCharge, $Value); 3018 last NAME; 3019 } 3020 3021 # Match aromatic annotation indicating whether atom is aromatic... 3022 if ($Name =~ /^Ar$/i) { 3023 $Status = $This->IsAromatic() ? 1 : 0; 3024 last NAME; 3025 } 3026 3027 # Match number of implicit and explicit hydrogens... 3028 if ($Name =~ /^H$/i) { 3029 $Status = (defined($Value) && (($This->GetNumOfMissingHydrogens() + $This->GetExplicitHydrogens()) == $Value)) ? 1 : 0; 3030 last NAME; 3031 } 3032 3033 # Match ring atom annotation indicating whether atom is in ring... 3034 if ($Name =~ /^RA$/i) { 3035 $Status = defined($Value) ? $This->IsInRingOfSize($Value) : ($This->IsInRing() ? 1 : 0); 3036 last NAME; 3037 } 3038 3039 # Match number of rings for atom.. 3040 if ($Name =~ /^TR$/i) { 3041 $Status = (defined($Value) && ($Value == $This->GetNumOfRings())) ? 1 : 0; 3042 last NAME; 3043 } 3044 3045 # Match sum of bond orders to non-hydrogen atom neighbors... 3046 if ($Name =~ /^BO$/i) { 3047 $Status = (defined($Value) && $This->GetSumOfBondOrdersToNonHydrogenAtoms() == $Value) ? 1 : 0; 3048 last NAME; 3049 } 3050 3051 # Match largest bond order of non-hydrogen atom neighbors... 3052 if ($Name =~ /^LBO$/i) { 3053 $Status = (defined($Value) && $This->GetLargestBondOrderToNonHydrogenAtoms() == $Value) ? 1 : 0; 3054 last NAME; 3055 } 3056 3057 # Match number of single bonds to non-hydrogen atom neighbors... 3058 if ($Name =~ /^SB$/i) { 3059 $Status = (defined($Value) && $This->GetNumOfSingleBondsToNonHydrogenAtoms() == $Value) ? 1 : 0; 3060 last NAME; 3061 } 3062 3063 # Match total number of single bonds to atom neighbors including missing and explicit hydrogens... 3064 if ($Name =~ /^TSB$/i) { 3065 $Status = (defined($Value) && ($This->GetNumOfSingleBondsToNonHydrogenAtoms() + $This->GetNumOfMissingHydrogens() + $This->GetExplicitHydrogens()) == $Value) ? 1 : 0; 3066 last NAME; 3067 } 3068 3069 # Match number of double bonds to non-hydrogen atom neighbors... 3070 if ($Name =~ /^DB$/i) { 3071 $Status = (defined($Value) && $This->GetNumOfDoubleBondsToNonHydrogenAtoms() == $Value) ? 1 : 0; 3072 last NAME; 3073 } 3074 3075 # Match number of triple bonds to non-hydrogen atom neighbors... 3076 if ($Name =~ /^TB$/i) { 3077 $Status = (defined($Value) && $This->GetNumOfTripleBondsToNonHydrogenAtoms() == $Value) ? 1 : 0; 3078 last NAME; 3079 } 3080 3081 # Match number of aromatic bonds to non-hydrogen atom neighbors... 3082 if ($Name =~ /^AB$/i) { 3083 $Status = (defined($Value) && $This->GetNumOfAromaticBondsToNonHydrogenAtoms() == $Value) ? 1 : 0; 3084 last NAME; 3085 } 3086 3087 3088 # Match mass number indicating isotope other than most abundant isotope... 3089 if ($Name =~ /^MN$/i) { 3090 $Status = (defined($Value) && $This->GetMassNumber() == $Value) ? 1 : 0; 3091 last NAME; 3092 } 3093 3094 # Match spin multiplicity... 3095 if ($Name =~ /^SM$/i) { 3096 my $SpinMultiplicity = $This->GetSpinMultiplicity(); 3097 if (!defined $SpinMultiplicity) { $SpinMultiplicity = 0; } 3098 $Status = (defined($Value) && defined($SpinMultiplicity) && $Value == $SpinMultiplicity) ? 1 : 0; 3099 last NAME; 3100 } 3101 3102 $UnknownName = 1; 3103 carp "Warning: ${ClassName}->_MatchNonAtomSymbolAtomicInvariant: Unknown atomic invariant $AtomicInvariant..."; 3104 } 3105 3106 if (!$UnknownName) { 3107 if ($NegateMatch) { 3108 $Status = $Status ? 0 : 1; 3109 } 3110 } 3111 3112 return $Status; 3113 } 3114 3115 # Match atomic invariant value... 3116 # 3117 # Specified value format: 3118 # . +* : Any positive value 3119 # . -* : Any negative value 3120 # . >ValidNumber or >=ValidNumber 3121 # . <ValidNumber or <=ValidNumber 3122 # . Any valid number 3123 # 3124 sub _MatchNonAtomSymbolAtomicInvariantValue { 3125 my($This, $TargetValue, $SpecifiedValue) = @_; 3126 my($Status); 3127 3128 $Status = 0; 3129 3130 if (!(defined($TargetValue) && defined($SpecifiedValue))) { 3131 return $Status; 3132 } 3133 3134 VALUE: { 3135 if ($SpecifiedValue =~ /^\+\*/) { 3136 $Status = ($TargetValue > 0) ? 1 : 0; 3137 last VALUE; 3138 } 3139 if ($SpecifiedValue =~ /^\-\*/) { 3140 $Status = ($TargetValue < 0) ? 1 : 0; 3141 last VALUE; 3142 } 3143 if ($SpecifiedValue =~ /^>/) { 3144 if ($SpecifiedValue =~ /^>=/) { 3145 $SpecifiedValue =~ s/^>=//; 3146 $Status = ($SpecifiedValue >= $TargetValue) ? 1 : 0; 3147 } 3148 else { 3149 $SpecifiedValue =~ s/^>//; 3150 $Status = ($SpecifiedValue > $TargetValue) ? 1 : 0; 3151 } 3152 last VALUE; 3153 } 3154 if ($SpecifiedValue =~ /^</) { 3155 if ($SpecifiedValue =~ /^<=/) { 3156 $SpecifiedValue =~ s/^<=//; 3157 $Status = ($SpecifiedValue <= $TargetValue) ? 1 : 0; 3158 } 3159 else { 3160 $SpecifiedValue =~ s/^<//; 3161 $Status = ($SpecifiedValue < $TargetValue) ? 1 : 0; 3162 } 3163 last VALUE; 3164 } 3165 # Default is do perform an equality match... 3166 $Status = ($SpecifiedValue == $TargetValue) ? 1 : 0; 3167 } 3168 3169 return $Status; 3170 } 3171 3172 # Check whether atoms match bond specifications... 3173 # 3174 sub _DoesBondSpecificationMatch { 3175 my($This, $BondedAtom, $BondSpecificationToMatch) = @_; 3176 my($BondSpecification, $BondSymbolSpecification, $BondSpecificationMatched); 3177 3178 # Anything to match... 3179 if (!(defined($BondSpecificationToMatch) && $BondSpecificationToMatch)) { 3180 return 1; 3181 } 3182 3183 # Take out any spaces... 3184 $BondSpecificationToMatch =~ s/ //g; 3185 3186 # Match specified bond specifications. For multiple bond specifications in a comma delimited string, 3187 # only one bond specification needs to match for a successful match... 3188 # 3189 for $BondSpecification (split /\,/, $BondSpecificationToMatch) { 3190 $BondSpecificationMatched = 1; 3191 3192 # Match all specified bond symbol specifications... 3193 BONDSYMBOL: for $BondSymbolSpecification (split /\./, $BondSpecification) { 3194 if (!$This->_MatchBondSymbolSpecification($BondedAtom, $BondSymbolSpecification)) { 3195 # No need to match other bond symbol specifications... 3196 $BondSpecificationMatched = 0; 3197 last BONDSYMBOL; 3198 } 3199 } 3200 if ($BondSpecificationMatched) { 3201 # No need to try matching other bond specifications... 3202 return 1; 3203 } 3204 } 3205 3206 # Nothing matched... 3207 return 0; 3208 } 3209 3210 # Check whether atoms match bond symbol specification... 3211 # 3212 sub _MatchBondSymbolSpecification { 3213 my($This, $BondedAtom, $BondSymbolSpecification) = @_; 3214 my($NegateMatch, $Status, $Bond, $BondSymbol, $UnknownBondSymbol); 3215 3216 ($Status, $NegateMatch, $UnknownBondSymbol) = ('0') x 3; 3217 3218 # Does match needs to be negated? 3219 if ($BondSymbolSpecification =~ /^!/) { 3220 $NegateMatch = 1; 3221 $BondSymbolSpecification =~ s/^!//; 3222 } 3223 $BondSymbol = $BondSymbolSpecification; 3224 $Bond = $This->GetBondToAtom($BondedAtom); 3225 3226 BONDSYMBOL: { 3227 if ($BondSymbol =~ /^(-|1|s|Single)$/i) { $Status = $Bond->IsSingle() ? 1 : 0; last BONDSYMBOL; } 3228 if ($BondSymbol =~ /^(=|2|d|Double)$/i) { $Status = $Bond->IsDouble() ? 1 : 0; last BONDSYMBOL; } 3229 if ($BondSymbol =~ /^(#|3|t|Triple)$/i) { $Status = $Bond->IsTriple() ? 1 : 0; last BONDSYMBOL; } 3230 if ($BondSymbol =~ /^(:|a|Ar|Aromatic)$/i) { $Status = $Bond->IsAromatic() ? 1 : 0; last BONDSYMBOL; } 3231 3232 if ($BondSymbol =~ /^(\@|RB|Ring)$/i) { $Status = $Bond->IsInRing() ? 1 : 0; last BONDSYMBOL; } 3233 3234 if ($BondSymbol =~ /^(\~|\*|Any)$/i) { $Status = 1; last BONDSYMBOL; } 3235 3236 $UnknownBondSymbol = 1; 3237 carp "Warning: ${ClassName}->_MatchBondSpecification: Unknown bond specification $BondSymbolSpecification..."; 3238 } 3239 3240 if (!$UnknownBondSymbol) { 3241 if ($NegateMatch) { 3242 $Status = $Status ? 0 : 1; 3243 } 3244 } 3245 3246 return $Status; 3247 } 3248 3249 # Is it a saturated atom? 3250 # 3251 sub IsSaturated { 3252 my($This) = @_; 3253 3254 return !$This->IsUnsaturated(); 3255 } 3256 3257 # Is it an unsaturated atom containing at least one non-single bond? 3258 # 3259 sub IsUnsaturated { 3260 my($This) = @_; 3261 my($NumOfSingleBonds, $NumOfDoubleBonds, $NumOfTripleBonds, $NumOfAromaticBonds); 3262 3263 ($NumOfSingleBonds, $NumOfDoubleBonds, $NumOfTripleBonds, $NumOfAromaticBonds) = $This->GetNumOfBondTypesToNonHydrogenAtoms(); 3264 3265 return ($NumOfDoubleBonds || $NumOfTripleBonds || $NumOfAromaticBonds) ? 1 : 0; 3266 } 3267 3268 # Is atom in a ring? 3269 # 3270 sub IsInRing { 3271 my($This) = @_; 3272 3273 # Is this atom in a molecule? 3274 if (!$This->HasProperty('Molecule')) { 3275 return undef; 3276 } 3277 my($Molecule); 3278 $Molecule = $This->GetProperty('Molecule'); 3279 3280 return $Molecule->_IsAtomInRing($This); 3281 } 3282 3283 # Is atom not in a ring? 3284 # 3285 sub IsNotInRing { 3286 my($This) = @_; 3287 3288 # Is this atom in a molecule? 3289 if (!$This->HasProperty('Molecule')) { 3290 return undef; 3291 } 3292 my($Molecule); 3293 $Molecule = $This->GetProperty('Molecule'); 3294 3295 return $Molecule->_IsAtomNotInRing($This); 3296 } 3297 3298 # Is atom only in one ring? 3299 # 3300 sub IsOnlyInOneRing { 3301 my($This) = @_; 3302 3303 # Is this atom in a molecule? 3304 if (!$This->HasProperty('Molecule')) { 3305 return undef; 3306 } 3307 my($Molecule); 3308 $Molecule = $This->GetProperty('Molecule'); 3309 3310 return $Molecule->_IsAtomInOnlyOneRing($This); 3311 } 3312 3313 # Is atom in a ring of specific size? 3314 # 3315 sub IsInRingOfSize { 3316 my($This, $RingSize) = @_; 3317 3318 # Is this atom in a molecule? 3319 if (!$This->HasProperty('Molecule')) { 3320 return undef; 3321 } 3322 my($Molecule); 3323 $Molecule = $This->GetProperty('Molecule'); 3324 3325 return $Molecule->_IsAtomInRingOfSize($This, $RingSize); 3326 } 3327 3328 # Get size of smallest ring containing the atom... 3329 # 3330 sub GetSizeOfSmallestRing { 3331 my($This) = @_; 3332 3333 # Is this atom in a molecule? 3334 if (!$This->HasProperty('Molecule')) { 3335 return undef; 3336 } 3337 my($Molecule); 3338 $Molecule = $This->GetProperty('Molecule'); 3339 3340 return $Molecule->_GetSizeOfSmallestAtomRing($This); 3341 } 3342 3343 # Get size of largest ring containing the atom... 3344 # 3345 sub GetSizeOfLargestRing { 3346 my($This) = @_; 3347 3348 # Is this atom in a molecule? 3349 if (!$This->HasProperty('Molecule')) { 3350 return undef; 3351 } 3352 my($Molecule); 3353 $Molecule = $This->GetProperty('Molecule'); 3354 3355 return $Molecule->_GetSizeOfLargestAtomRing($This); 3356 } 3357 3358 # Get number of rings containing the atom... 3359 # 3360 sub GetNumOfRings { 3361 my($This) = @_; 3362 3363 # Is this atom in a molecule? 3364 if (!$This->HasProperty('Molecule')) { 3365 return undef; 3366 } 3367 my($Molecule); 3368 $Molecule = $This->GetProperty('Molecule'); 3369 3370 return $Molecule->_GetNumOfAtomRings($This); 3371 } 3372 3373 # Get number of rings with odd size containing the atom... 3374 # 3375 sub GetNumOfRingsWithOddSize { 3376 my($This) = @_; 3377 3378 # Is this atom in a molecule? 3379 if (!$This->HasProperty('Molecule')) { 3380 return undef; 3381 } 3382 my($Molecule); 3383 $Molecule = $This->GetProperty('Molecule'); 3384 3385 return $Molecule->_GetNumOfAtomRingsWithOddSize($This); 3386 } 3387 3388 # Get number of rings with even size containing the atom... 3389 # 3390 sub GetNumOfRingsWithEvenSize { 3391 my($This) = @_; 3392 3393 # Is this atom in a molecule? 3394 if (!$This->HasProperty('Molecule')) { 3395 return undef; 3396 } 3397 my($Molecule); 3398 $Molecule = $This->GetProperty('Molecule'); 3399 3400 return $Molecule->_GetNumOfAtomRingsWithEvenSize($This); 3401 } 3402 3403 # Get number of rings with specified size containing the atom... 3404 # 3405 sub GetNumOfRingsWithSize { 3406 my($This, $RingSize) = @_; 3407 3408 # Is this atom in a molecule? 3409 if (!$This->HasProperty('Molecule')) { 3410 return undef; 3411 } 3412 my($Molecule); 3413 $Molecule = $This->GetProperty('Molecule'); 3414 3415 return $Molecule->_GetNumOfAtomRingsWithSize($This, $RingSize); 3416 3417 } 3418 3419 # Get number of rings with size less than specified containing the atom... 3420 # 3421 sub GetNumOfRingsWithSizeLessThan { 3422 my($This, $RingSize) = @_; 3423 3424 # Is this atom in a molecule? 3425 if (!$This->HasProperty('Molecule')) { 3426 return undef; 3427 } 3428 my($Molecule); 3429 $Molecule = $This->GetProperty('Molecule'); 3430 3431 return $Molecule->_GetNumOfAtomRingsWithSizeLessThan($This, $RingSize); 3432 } 3433 3434 # Get number of rings with size greater than specified size containing the atom... 3435 # 3436 sub GetNumOfRingsWithSizeGreaterThan { 3437 my($This, $RingSize) = @_; 3438 3439 # Is this atom in a molecule? 3440 if (!$This->HasProperty('Molecule')) { 3441 return undef; 3442 } 3443 my($Molecule); 3444 $Molecule = $This->GetProperty('Molecule'); 3445 3446 return $Molecule->_GetNumOfAtomRingsWithSizeGreaterThan($This, $RingSize); 3447 } 3448 3449 # Get all rings an array of references to arrays containing ring atoms... 3450 # 3451 sub GetRings { 3452 my($This) = @_; 3453 3454 # Is this atom in a molecule? 3455 if (!$This->HasProperty('Molecule')) { 3456 return undef; 3457 } 3458 my($Molecule); 3459 $Molecule = $This->GetProperty('Molecule'); 3460 3461 return $Molecule->_GetAtomRings($This); 3462 } 3463 3464 # Get smallest ring as an array containing ring atoms... 3465 # 3466 sub GetSmallestRing { 3467 my($This) = @_; 3468 3469 # Is this atom in a molecule? 3470 if (!$This->HasProperty('Molecule')) { 3471 return undef; 3472 } 3473 my($Molecule); 3474 $Molecule = $This->GetProperty('Molecule'); 3475 3476 return $Molecule->_GetSmallestAtomRing($This); 3477 } 3478 3479 # Get largest ring as an array containing ring atoms... 3480 # 3481 sub GetLargestRing { 3482 my($This) = @_; 3483 3484 # Is this atom in a molecule? 3485 if (!$This->HasProperty('Molecule')) { 3486 return undef; 3487 } 3488 my($Molecule); 3489 $Molecule = $This->GetProperty('Molecule'); 3490 3491 return $Molecule->_GetLargestAtomRing($This); 3492 } 3493 3494 # Get odd size rings an array of references to arrays containing ring atoms... 3495 # 3496 sub GetRingsWithOddSize { 3497 my($This) = @_; 3498 3499 # Is this atom in a molecule? 3500 if (!$This->HasProperty('Molecule')) { 3501 return undef; 3502 } 3503 my($Molecule); 3504 $Molecule = $This->GetProperty('Molecule'); 3505 3506 return $Molecule->_GetAtomRingsWithOddSize($This); 3507 } 3508 3509 # Get even size rings an array of references to arrays containing ring atoms... 3510 # 3511 sub GetRingsWithEvenSize { 3512 my($This) = @_; 3513 3514 # Is this atom in a molecule? 3515 if (!$This->HasProperty('Molecule')) { 3516 return undef; 3517 } 3518 my($Molecule); 3519 $Molecule = $This->GetProperty('Molecule'); 3520 3521 return $Molecule->_GetAtomRingsWithEvenSize($This); 3522 } 3523 3524 # Get rings with specified size as an array of references to arrays containing ring atoms... 3525 # 3526 sub GetRingsWithSize { 3527 my($This, $RingSize) = @_; 3528 3529 # Is this atom in a molecule? 3530 if (!$This->HasProperty('Molecule')) { 3531 return undef; 3532 } 3533 my($Molecule); 3534 $Molecule = $This->GetProperty('Molecule'); 3535 3536 return $Molecule->_GetAtomRingsWithSize($This, $RingSize); 3537 } 3538 3539 # Get rings with size less than specfied size as an array of references to arrays containing ring atoms... 3540 # 3541 sub GetRingsWithSizeLessThan { 3542 my($This, $RingSize) = @_; 3543 3544 # Is this atom in a molecule? 3545 if (!$This->HasProperty('Molecule')) { 3546 return undef; 3547 } 3548 my($Molecule); 3549 $Molecule = $This->GetProperty('Molecule'); 3550 3551 return $Molecule->_GetAtomRingsWithSizeLessThan($This, $RingSize); 3552 } 3553 3554 # Get rings with size greater than specfied size as an array of references to arrays containing ring atoms... 3555 # 3556 sub GetRingsWithSizeGreaterThan { 3557 my($This, $RingSize) = @_; 3558 3559 # Is this atom in a molecule? 3560 if (!$This->HasProperty('Molecule')) { 3561 return undef; 3562 } 3563 my($Molecule); 3564 $Molecule = $This->GetProperty('Molecule'); 3565 3566 return $Molecule->_GetAtomRingsWithSizeGreaterThan($This, $RingSize); 3567 } 3568 3569 # Get next object ID... 3570 sub _GetNewObjectID { 3571 $ObjectID++; 3572 return $ObjectID; 3573 } 3574 3575 # Return a string containing vertices, edges and other properties... 3576 sub StringifyAtom { 3577 my($This) = @_; 3578 my($AtomString, $ID, $Name, $AtomSymbol, $AtomicNumber, $XYZVector, $AtomicWeight, $ExactMass, $NumOfNeighbors, $NumOfBonds, $Valence, $FormalCharge, $Charge, $StereoCenter, $StereoCenterStatus, $StereoChemistry, $StereochemistryString, $RingAtom, $NumOfRings); 3579 3580 $ID = $This->GetID(); 3581 $Name = $This->GetName(); 3582 $AtomSymbol = $This->GetAtomSymbol(); 3583 $AtomicNumber = $This->GetAtomicNumber(); 3584 $XYZVector = $This->GetXYZVector(); 3585 3586 $AtomicWeight = $This->GetAtomicWeight(); 3587 if (!defined $AtomicWeight) { 3588 $AtomicWeight = 'undefined'; 3589 } 3590 $ExactMass = $This->GetExactMass(); 3591 if (!defined $ExactMass) { 3592 $ExactMass = 'undefined'; 3593 } 3594 $NumOfNeighbors = $This->GetNumOfNeighbors(); 3595 if (!defined $NumOfNeighbors) { 3596 $NumOfNeighbors = 'undefined'; 3597 } 3598 $NumOfBonds = $This->GetNumOfBonds(); 3599 if (!defined $NumOfBonds) { 3600 $NumOfBonds = 'undefined'; 3601 } 3602 $Valence = $This->GetValence(); 3603 if (!defined $Valence) { 3604 $Valence = 'undefined'; 3605 } 3606 $FormalCharge = $This->GetFormalCharge(); 3607 if (!defined $FormalCharge) { 3608 $FormalCharge = 'undefined'; 3609 } 3610 $Charge = $This->GetCharge(); 3611 if (!defined $Charge) { 3612 $Charge = 'undefined'; 3613 } 3614 $RingAtom = $This->IsInRing(); 3615 if (defined $RingAtom) { 3616 $RingAtom = $RingAtom ? 'Yes' : 'No'; 3617 $NumOfRings = $This->GetNumOfRings(); 3618 } 3619 else { 3620 $RingAtom = 'undefined'; 3621 $NumOfRings = 'undefined'; 3622 } 3623 3624 $StereochemistryString = ''; 3625 $StereoCenter = $This->GetStereoCenter(); 3626 if (defined $StereoCenter) { 3627 $StereoCenterStatus = $This->IsStereoCenter() ? 'Yes' : 'No'; 3628 $StereoChemistry = $This->GetStereochemistry(); 3629 if (!defined $StereoChemistry) { 3630 $StereoChemistry = 'undefined'; 3631 } 3632 $StereochemistryString = "StereoCenter: $StereoCenterStatus; Stereochemistry: $StereoChemistry"; 3633 } 3634 3635 $AtomString = "Atom: ID: $ID; Name: \"$Name\"; AtomSymbol: \"$AtomSymbol\"; AtomicNumber: $AtomicNumber; XYZ: $XYZVector; AtomicWeight: $AtomicWeight; ExactMass: $ExactMass; NumOfNeighbors: $NumOfNeighbors; NumOfBonds: $NumOfBonds; Valence: $Valence; FormalCharge: $FormalCharge; Charge: $Charge; RingAtom: $RingAtom; NumOfAtomRings: $NumOfRings"; 3636 3637 if ($StereochemistryString) { 3638 $AtomString .= "; $StereochemistryString"; 3639 } 3640 3641 return $AtomString; 3642 } 3643