1 package Atom; 2 # 3 # $RCSfile: Atom.pm,v $ 4 # $Date: 2008/04/25 00:00:44 $ 5 # $Revision: 1.28 $ 6 # 7 # Author: Manish Sud <msud@san.rr.com> 8 # 9 # Copyright (C) 2004-2008 Manish Sud. All rights reserved. 10 # 11 # This file is part of MayaChemTools. 12 # 13 # MayaChemTools is free software; you can redistribute it and/or modify it under 14 # the terms of the GNU Lesser General Public License as published by the Free 15 # Software Foundation; either version 3 of the License, or (at your option) any 16 # later version. 17 # 18 # MayaChemTools is distributed in the hope that it will be useful, but without 19 # any warranty; without even the implied warranty of merchantability of fitness 20 # for a particular purpose. See the GNU Lesser General Public License for more 21 # details. 22 # 23 # You should have received a copy of the GNU Lesser General Public License 24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or 25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330, 26 # Boston, MA, 02111-1307, USA. 27 # 28 use 5.006; 29 use strict; 30 use Carp; 31 use Exporter; 32 use Storable (); 33 use ObjectProperty; 34 use PeriodicTable; 35 use Vector; 36 37 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 38 39 $VERSION = '1.00'; 40 @ISA = qw(ObjectProperty Exporter); 41 @EXPORT = qw(); 42 @EXPORT_OK = qw(); 43 44 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]); 45 46 # Setup class variables... 47 my($ClassName, $ObjectID); 48 _InitializeClass(); 49 50 # Overload Perl functions... 51 use overload '""' => 'StringifyAtom'; 52 53 # Class constructor... 54 sub new { 55 my($Class, %NamesAndValues) = @_; 56 57 # Initialize object... 58 my $This = {}; 59 bless $This, ref($Class) || $Class; 60 $This->_InitializeAtom(); 61 62 $This->_InitializeAtomProperties(%NamesAndValues); 63 64 return $This; 65 } 66 67 # Initialize object data... 68 sub _InitializeAtom { 69 my($This) = @_; 70 my($ObjectID) = _GetNewObjectID(); 71 72 # All other property names and values along with all Set/Get<PropertyName> methods 73 # are implemented on-demand using ObjectProperty class. 74 $This->{ID} = $ObjectID; 75 $This->{Name} = "Atom ${ObjectID}"; 76 $This->{AtomSymbol} = ''; 77 $This->{AtomicNumber} = 0; 78 $This->{XYZ} = Vector::ZeroVector; 79 } 80 81 # Initialize atom properties... 82 sub _InitializeAtomProperties { 83 my($This, %NamesAndValues) = @_; 84 85 my($Name, $Value, $MethodName); 86 while (($Name, $Value) = each %NamesAndValues) { 87 $MethodName = "Set${Name}"; 88 $This->$MethodName($Value); 89 } 90 if (!exists $NamesAndValues{'AtomSymbol'}) { 91 carp "Warning: ${ClassName}->new: Atom object instantiated without setting atom symbol..."; 92 } 93 94 return $This; 95 } 96 97 # Initialize class ... 98 sub _InitializeClass { 99 #Class name... 100 $ClassName = __PACKAGE__; 101 102 # ID to keep track of objects... 103 $ObjectID = 0; 104 } 105 106 # Setup an explicit SetID method to block setting of ID by AUTOLOAD function... 107 sub SetID { 108 my($This, $Value) = @_; 109 110 carp "Warning: ${ClassName}->SetID: Object ID can't be changed: it's used for internal tracking..."; 111 112 return $This; 113 } 114 115 # Setup atom symbol and atomic number for the element... 116 # 117 # Possible atom symbol values: 118 # . An element symbol or some other type of atom: L - Atom list; LP - Lone pair; R# - R group; 119 # A, Q, * - unknown atom; or something else? 120 # 121 # Default mass number corresponds to the most abundant natural isotope unless it's explicity 122 # set using "MassNumber" property. 123 # 124 sub SetAtomSymbol { 125 my($This, $AtomSymbol) = @_; 126 my($AtomicNumber); 127 128 $This->{AtomSymbol} = $AtomSymbol; 129 130 $AtomicNumber = PeriodicTable::GetElementAtomicNumber($AtomSymbol); 131 $This->{AtomicNumber} = (defined $AtomicNumber) ? $AtomicNumber : 0; 132 133 return $This; 134 } 135 136 # Setup atom symbol and atomic number for the element... 137 sub SetAtomicNumber { 138 my($This, $AtomicNumber) = @_; 139 my($AtomSymbol); 140 141 $AtomSymbol = PeriodicTable::GetElementAtomSymbol($AtomicNumber); 142 if (!defined $AtomSymbol) { 143 carp "Warning: ${ClassName}->SetAtomicNumber: Didn't set atomic number: Invalid atomic number, $AtomicNumber, specified..."; 144 return; 145 } 146 $This->{AtomicNumber} = $AtomicNumber; 147 $This->{AtomSymbol} = $AtomSymbol; 148 149 return $This; 150 } 151 152 # Set atom as stereo center... 153 # 154 sub SetStereoCenter { 155 my($This, $StereoCenter) = @_; 156 157 $This->SetProperty('StereoCenter', $StereoCenter); 158 159 return $This; 160 } 161 162 # Is it a stereo center? 163 # 164 sub IsStereoCenter { 165 my($This) = @_; 166 my($StereoCenter); 167 168 $StereoCenter = $This->GetProperty('StereoCenter'); 169 170 return (defined($StereoCenter) && $StereoCenter) ? 1 : 0; 171 } 172 173 # Set atom stereochemistry. 174 # 175 # Supported values are: R, S. 176 # 177 # Notes: 178 # 179 # . After the ligands around a central stereocenter has been ranked using CIP priority scheme and 180 # the lowest ranked ligand lies behind the center atom, then R and S values correspond to: 181 # 182 # R: Clockwise arrangement of remaining ligands around the central atom going from highest to lowest ranked ligand 183 # S: CounterClockwise arrangement of remaining ligands around the central atom going from highest to lowest ranked ligand 184 # 185 # . Assignment of any other arbitray values besides R and S is also allowed; however, a warning is printed. 186 # 187 sub SetStereochemistry { 188 my($This, $Stereochemistry) = @_; 189 190 if ($Stereochemistry !~ /^(R|S)$/i) { 191 carp "Warning: ${ClassName}->SetStereochemistry: Assigning non-supported Stereochemistry value of $Stereochemistry. Supported values: R, S..."; 192 } 193 194 $This->SetProperty('StereoCenter', 1); 195 $This->SetProperty('Stereochemistry', $Stereochemistry); 196 197 return $This; 198 } 199 200 # Setup mass number for atom... 201 sub SetMassNumber { 202 my($This, $MassNumber) = @_; 203 my($AtomicNumber, $AtomSymbol); 204 205 $AtomicNumber = $This->{AtomicNumber}; 206 $AtomSymbol = $This->{AtomSymbol}; 207 if (!$AtomicNumber) { 208 carp "Warning: ${ClassName}->SetMassNumber: Didn't set mass number: Non standard atom with atomic number, $AtomicNumber, and atomic symbol, $AtomSymbol..."; 209 return; 210 } 211 if (!PeriodicTable::IsElementNaturalIsotopeMassNumber($AtomicNumber, $MassNumber)) { 212 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..."; 213 } 214 $This->SetProperty('MassNumber', $MassNumber); 215 216 return $This; 217 } 218 219 # Get mass number... 220 # 221 sub GetMassNumber { 222 my($This) = @_; 223 224 # Is mass number explicity set? 225 if ($This->HasProperty('MassNumber')) { 226 return $This->GetProperty('MassNumber'); 227 } 228 229 # Is it an element symbol? 230 my($AtomicNumber) = $This->{AtomicNumber}; 231 if (!$AtomicNumber) { 232 return undef; 233 } 234 235 # Return most abundant mass number... 236 return PeriodicTable::GetElementMostAbundantNaturalIsotopeMassNumber($AtomicNumber); 237 } 238 239 # Get atomic weight: 240 # . Explicitly set by the caller 241 # . Using atomic number 242 # 243 sub GetAtomicWeight { 244 my($This) = @_; 245 246 # Is atomic weight explicity set? 247 if ($This->HasProperty('AtomicWeight')) { 248 return $This->GetProperty('AtomicWeight'); 249 } 250 251 # Is it an element symbol? 252 my($AtomicNumber) = $This->{AtomicNumber}; 253 if (!$AtomicNumber) { 254 return undef; 255 } 256 257 # Return its atomic weight... 258 return PeriodicTable::GetElementAtomicWeight($AtomicNumber); 259 } 260 261 # Get exact mass weight: 262 # . Explicitly set by the caller 263 # . Using atomic number and mass number explicity set by the caller 264 # . Using atomic number and most abundant isotope 265 # 266 sub GetExactMass { 267 my($This) = @_; 268 269 # Is exact mass explicity set? 270 if ($This->HasProperty('ExactMass')) { 271 return $This->GetProperty('ExactMass'); 272 } 273 274 # Is it an element symbol? 275 my($AtomicNumber) = $This->{AtomicNumber}; 276 if (!$AtomicNumber) { 277 return undef; 278 } 279 280 # Is mass number explicitly set? 281 if ($This->HasProperty('MassNumber')) { 282 my($MassNumber) = $This->GetProperty('MassNumber'); 283 if (PeriodicTable::IsElementNaturalIsotopeMassNumber($AtomicNumber, $MassNumber)) { 284 return PeriodicTable::GetElementNaturalIsotopeMass($AtomicNumber, $MassNumber); 285 } 286 } 287 288 # Return most abundant isotope mass... 289 return PeriodicTable::GetElementMostAbundantNaturalIsotopeMass($AtomicNumber); 290 } 291 292 293 # Set atom coordinates using: 294 # . An array reference with three values 295 # . An array containg three values 296 # . A 3D vector 297 # 298 sub SetXYZ { 299 my($This, @Values) = @_; 300 301 if (!@Values) { 302 carp "Warning: ${ClassName}->SetXYZ: No values specified..."; 303 return; 304 } 305 306 $This->{XYZ}->SetXYZ(@Values); 307 return $This; 308 } 309 310 # Set X value... 311 sub SetX { 312 my($This, $Value) = @_; 313 314 if (!defined $Value) { 315 carp "Warning: ${ClassName}->SetX: Undefined X value..."; 316 return; 317 } 318 $This->{XYZ}->SetX($Value); 319 return $This; 320 } 321 322 # Set Y value... 323 sub SetY { 324 my($This, $Value) = @_; 325 326 if (!defined $Value) { 327 carp "Warning: ${ClassName}->SetY: Undefined Y value..."; 328 return; 329 } 330 $This->{XYZ}->SetY($Value); 331 return $This; 332 } 333 334 # Set Z value... 335 sub SetZ { 336 my($This, $Value) = @_; 337 338 if (!defined $Value) { 339 carp "Warning: ${ClassName}->SetZ: Undefined Z value..."; 340 return; 341 } 342 $This->{XYZ}->SetZ($Value); 343 return $This; 344 } 345 346 # Return XYZ as: 347 # . Reference to an array 348 # . An array 349 # 350 sub GetXYZ { 351 my($This) = @_; 352 353 return $This->{XYZ}->GetXYZ(); 354 } 355 356 # Return XYZ as a vector object... 357 # 358 sub GetXYZVector { 359 my($This) = @_; 360 361 return $This->{XYZ}; 362 } 363 364 # Get X value... 365 sub GetX { 366 my($This) = @_; 367 368 return $This->{XYZ}->GetX(); 369 } 370 371 # Get Y value... 372 sub GetY { 373 my($This) = @_; 374 375 return $This->{XYZ}->GetY(); 376 } 377 378 # Get Z value... 379 sub GetZ { 380 my($This) = @_; 381 382 return $This->{XYZ}->GetZ(); 383 } 384 385 # Delete atom... 386 sub DeleteAtom { 387 my($This) = @_; 388 389 # Is this atom in a molecule? 390 if (!$This->HasProperty('Molecule')) { 391 # Nothing to do... 392 return $This; 393 } 394 my($Molecule) = $This->GetProperty('Molecule'); 395 396 return $Molecule->_DeleteAtom($This); 397 } 398 399 # Get atom neighbor objects as array. In scalar conetxt, return number of neighbors... 400 sub GetNeighbors { 401 my($This) = @_; 402 403 # Is this atom in a molecule? 404 if (!$This->HasProperty('Molecule')) { 405 return undef; 406 } 407 my($Molecule) = $This->GetProperty('Molecule'); 408 409 return $Molecule->_GetAtomNeighbors($This); 410 } 411 412 # Get non-hydrogen atom neighbor objects as array. In scalar context, return number of neighbors... 413 sub GetHeavyAtomNeighbors { 414 my($This) = @_; 415 416 return $This->GetNonHydrogenAtomNeighbors(); 417 } 418 419 # Get non-hydrogen atom neighbor objects as array. In scalar context, return number of neighbors... 420 sub GetNonHydrogenAtomNeighbors { 421 my($This) = @_; 422 423 # Is this atom in a molecule? 424 if (!$This->HasProperty('Molecule')) { 425 return undef; 426 } 427 my($NonHydrogenAtomsOnly, $HydrogenAtomsOnly) = (1, 0); 428 429 return $This->_GetFilteredAtomNeighbors($NonHydrogenAtomsOnly, $HydrogenAtomsOnly); 430 } 431 432 # Get hydrogen atom neighbor objects as array. In scalar context, return numbe of neighbors... 433 sub GetHydrogenAtomNeighbors { 434 my($This) = @_; 435 436 # Is this atom in a molecule? 437 if (!$This->HasProperty('Molecule')) { 438 return undef; 439 } 440 my($NonHydrogenAtomsOnly, $HydrogenAtomsOnly) = (0, 1); 441 442 return $This->_GetFilteredAtomNeighbors($NonHydrogenAtomsOnly, $HydrogenAtomsOnly); 443 } 444 445 # Get filtered atom atom neighbors 446 sub _GetFilteredAtomNeighbors { 447 my($This, $NonHydrogenAtomsOnly, $HydrogenAtomsOnly) = @_; 448 449 # Check flags... 450 if (!defined $NonHydrogenAtomsOnly) { 451 $NonHydrogenAtomsOnly = 0; 452 } 453 if (!defined $HydrogenAtomsOnly) { 454 $HydrogenAtomsOnly = 0; 455 } 456 my($Neighbor, @FilteredAtomNeighbors); 457 458 @FilteredAtomNeighbors = (); 459 NEIGHBOR: for $Neighbor ($This->GetNeighbors()) { 460 if ($NonHydrogenAtomsOnly && $Neighbor->IsHydrogen()) { 461 next NEIGHBOR; 462 } 463 if ($HydrogenAtomsOnly && (!$Neighbor->IsHydrogen())) { 464 next NEIGHBOR; 465 } 466 push @FilteredAtomNeighbors, $Neighbor; 467 } 468 469 return wantarray ? @FilteredAtomNeighbors : scalar @FilteredAtomNeighbors; 470 } 471 472 # Get number of neighbors... 473 # 474 sub GetNumOfNeighbors { 475 my($This) = @_; 476 my($NumOfNeighbors); 477 478 $NumOfNeighbors = $This->GetNeighbors(); 479 480 return (defined $NumOfNeighbors) ? ($NumOfNeighbors) : undef; 481 } 482 483 # Get number of neighbors which are non-hydrogen atoms... 484 sub GetNumOfHeavyAtomNeighbors { 485 my($This) = @_; 486 487 return $This->GetNumOfNonHydrogenAtomNeighbors(); 488 } 489 490 # Get number of neighbors which are non-hydrogen atoms... 491 sub GetNumOfNonHydrogenAtomNeighbors { 492 my($This) = @_; 493 my($NumOfNeighbors); 494 495 $NumOfNeighbors = $This->GetNonHydrogenAtomNeighbors(); 496 497 return (defined $NumOfNeighbors) ? ($NumOfNeighbors) : undef; 498 } 499 500 # Get number of neighbors which are hydrogen atoms... 501 sub GetNumOfHydrogenAtomNeighbors { 502 my($This) = @_; 503 my($NumOfNeighbors); 504 505 $NumOfNeighbors = $This->GetHydrogenAtomNeighbors(); 506 507 return (defined $NumOfNeighbors) ? ($NumOfNeighbors) : undef; 508 } 509 510 # Get bond objects as array. In scalar context, return number of bonds... 511 sub GetBonds { 512 my($This) = @_; 513 514 # Is this atom in a molecule? 515 if (!$This->HasProperty('Molecule')) { 516 return undef; 517 } 518 my($Molecule) = $This->GetProperty('Molecule'); 519 520 return $Molecule->_GetAtomBonds($This); 521 } 522 523 # Get bond to specified atom... 524 sub GetBondToAtom { 525 my($This, $Other) = @_; 526 527 # Is this atom in a molecule? 528 if (!$This->HasProperty('Molecule')) { 529 return undef; 530 } 531 my($Molecule) = $This->GetProperty('Molecule'); 532 533 return $Molecule->_GetBondToAtom($This, $Other); 534 } 535 536 # Get bond objects to non-hydrogen atoms as array. In scalar context, return number of bonds... 537 sub GetBondsToHeavyAtoms { 538 my($This) = @_; 539 540 return $This->GetBondsToNonHydrogenAtoms(); 541 } 542 543 # Get bond objects to non-hydrogen atoms as array. In scalar context, return number of bonds... 544 sub GetBondsToNonHydrogenAtoms { 545 my($This) = @_; 546 547 # Is this atom in a molecule? 548 if (!$This->HasProperty('Molecule')) { 549 return undef; 550 } 551 my($BondsToNonHydrogenAtomsOnly, $BondsToHydrogenAtomsOnly) = (1, 0); 552 553 return $This->_GetFilteredBonds($BondsToNonHydrogenAtomsOnly, $BondsToHydrogenAtomsOnly); 554 } 555 556 # Get bond objects to hydrogen atoms as array. In scalar context, return number of bonds... 557 sub GetBondsToHydrogenAtoms { 558 my($This) = @_; 559 560 # Is this atom in a molecule? 561 if (!$This->HasProperty('Molecule')) { 562 return undef; 563 } 564 my($BondsToNonHydrogenAtomsOnly, $BondsToHydrogenAtomsOnly) = (0, 1); 565 566 return $This->_GetFilteredBonds($BondsToNonHydrogenAtomsOnly, $BondsToHydrogenAtomsOnly); 567 } 568 569 # Get filtered bonds... 570 sub _GetFilteredBonds { 571 my($This, $BondsToNonHydrogenAtomsOnly, $BondsToHydrogenAtomsOnly) = @_; 572 573 # Check flags... 574 if (!defined $BondsToNonHydrogenAtomsOnly) { 575 $BondsToNonHydrogenAtomsOnly = 0; 576 } 577 if (!defined $BondsToHydrogenAtomsOnly) { 578 $BondsToHydrogenAtomsOnly = 0; 579 } 580 581 my($Bond, $BondedAtom, @FilteredBonds); 582 583 @FilteredBonds = (); 584 BOND: for $Bond ($This->GetBonds()) { 585 $BondedAtom = $Bond->GetBondedAtom($This); 586 if ($BondsToNonHydrogenAtomsOnly && $BondedAtom->IsHydrogen()) { 587 next BOND; 588 } 589 if ($BondsToHydrogenAtomsOnly && (!$BondedAtom->IsHydrogen())) { 590 next BOND; 591 } 592 push @FilteredBonds, $Bond; 593 } 594 595 return wantarray ? @FilteredBonds : (scalar @FilteredBonds); 596 } 597 598 # Get number of bonds... 599 # 600 sub GetNumOfBonds { 601 my($This) = @_; 602 my($NumOfBonds); 603 604 $NumOfBonds = $This->GetBonds(); 605 606 return (defined $NumOfBonds) ? ($NumOfBonds) : undef; 607 } 608 609 # Get number of bonds to non-hydrogen atoms... 610 sub GetNumOfBondsToHeavyAtoms { 611 my($This) = @_; 612 613 return $This->GetNumOfBondsToNonHydrogenAtoms(); 614 } 615 616 # Get number of bonds to non-hydrogen atoms... 617 sub GetNumOfBondsToNonHydrogenAtoms { 618 my($This) = @_; 619 my($NumOfBonds); 620 621 $NumOfBonds = $This->GetBondsToNonHydrogenAtoms(); 622 623 return (defined $NumOfBonds) ? ($NumOfBonds) : undef; 624 } 625 626 # Get number of bonds to hydrogen atoms... 627 sub GetNumOfBondsToHydrogenAtoms { 628 my($This) = @_; 629 my($NumOfBonds); 630 631 $NumOfBonds = $This->GetBondsToHydrogenAtoms(); 632 633 return (defined $NumOfBonds) ? ($NumOfBonds) : undef; 634 } 635 636 # Get sum of bond orders to all bonded atoms... 637 # 638 sub GetSumOfBondOrders { 639 my($This) = @_; 640 641 # Is this atom in a molecule? 642 if (!$This->HasProperty('Molecule')) { 643 return undef; 644 } 645 646 return $This->_GetSumOfBondOrders(); 647 } 648 649 # Get sum of bond orders to non-hydrogen atoms only... 650 # 651 sub GetSumOfBondOrdersToHeavyAtoms { 652 my($This) = @_; 653 654 return $This->GetSumOfBondOrdersToNonHydrogenAtoms(); 655 } 656 657 # Get sum of bond orders to non-hydrogen atoms only... 658 # 659 sub GetSumOfBondOrdersToNonHydrogenAtoms { 660 my($This) = @_; 661 662 # Is this atom in a molecule? 663 if (!$This->HasProperty('Molecule')) { 664 return undef; 665 } 666 my($ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly) = (1, 0); 667 668 return $This->_GetSumOfBondOrders($ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly); 669 } 670 671 # Get sum of bond orders to hydrogen atoms only... 672 # 673 sub GetSumOfBondOrdersToHydrogenAtoms { 674 my($This) = @_; 675 676 # Is this atom in a molecule? 677 if (!$This->HasProperty('Molecule')) { 678 return undef; 679 } 680 my($ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly) = (0, 1); 681 682 return $This->_GetSumOfBondOrders($ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly); 683 } 684 685 # Get sum of bond orders to all bonded atoms, non-hydrogen or hydrogen bonded atoms... 686 # 687 sub _GetSumOfBondOrders { 688 my($This, $ToNonHydrogenAtomsOnly, $ToHydrogenAtomsOnly) = @_; 689 690 # Check flags... 691 if (!defined $ToNonHydrogenAtomsOnly) { 692 $ToNonHydrogenAtomsOnly = 0; 693 } 694 if (!defined $ToHydrogenAtomsOnly) { 695 $ToHydrogenAtomsOnly = 0; 696 } 697 my($Bond, $SumOfBondOrders, $BondOrder, $NumOfAromaticBonds, @Bonds); 698 @Bonds = (); 699 700 if ($ToNonHydrogenAtomsOnly) { 701 @Bonds = $This->GetBondsToNonHydrogenAtoms(); 702 } 703 elsif ($ToHydrogenAtomsOnly) { 704 @Bonds = $This->GetBondsToHydrogenAtoms(); 705 } 706 else { 707 # All bonds... 708 @Bonds = $This->GetBonds(); 709 } 710 711 $SumOfBondOrders = 0; 712 $NumOfAromaticBonds = 0; 713 for $Bond (@Bonds) { 714 $BondOrder = $Bond->GetBondOrder(); 715 $SumOfBondOrders += $BondOrder; 716 if ($BondOrder == 1.5) { 717 $NumOfAromaticBonds++; 718 } 719 } 720 721 if ($NumOfAromaticBonds) { 722 # As long as aromatic bond orders in a ring are correctly assigned in a ring using 723 # using 4n + 2 Huckel rule (BondOrder: 1.5) or explicity set as Kekule bonds (alternate single/double), 724 # SumOfBondOrders should add up to an integer. Just in case it's not, turn it into an integer... 725 $SumOfBondOrders = int $SumOfBondOrders; 726 } 727 728 return $SumOfBondOrders; 729 } 730 731 # Valence corresponds to number of electrons used by an atom in bonding: 732 # 733 # Valence = ValenceElectrons - ValenceFreeElectrons = BondingElectrons 734 # 735 # Single, double, triple bonds with bond orders of 1, 2 and 3 correspond to contribution of 736 # 1, 2, and 3 bonding electrons. So Valence can be computed using: 737 # 738 # Valence = SumOfBondOrders + FormalCharge 739 # 740 # where positive and negative values of FormalCharge increase and decrease the number 741 # of bonding electrons respectively. 742 # 743 # Notes: 744 # . For neutral molecules, valence and sum of bond order are equal. 745 # . For molecues containing only single bonds, SumOfBondOrders and NumOfBonds are equal. 746 # 747 sub GetValence { 748 my($This) = @_; 749 750 # Is this atom in a molecule? 751 if (!$This->HasProperty('Molecule')) { 752 return undef; 753 } 754 755 # Is Valence property explicitly set? 756 if ($This->HasProperty('Valence')) { 757 return $This->GetProperty('Valence'); 758 } 759 my($FormalCharge, $Valence); 760 761 $Valence = $This->GetSumOfBondOrders(); 762 if ($This->HasProperty('FormalCharge')) { 763 $Valence += $This->GetProperty('FormalCharge'); 764 } 765 return $Valence; 766 } 767 768 # For elements with one one common valence, ImplictValence corresponds to: 769 # 770 # . ImplicitValence = CommonValence + FormalCharge + SpinMultiplicityCorrection 771 # 772 # For elements with multiple common valences and no explicit FormalCharge assignment, 773 # ImplicitValence is determined based on SumOfBondOrdersToNonHydrogenAtoms and next 774 # available common valence. 775 # 776 # For elements with multiple common valences and explicit FormalCharge assignment, 777 # ImplicitValence corresponds to: 778 # 779 # . ImplicitValence = HighestCommonValence + FormalCharge + SpinMultiplicityCorrection 780 # 781 # Notes: 782 # . For atoms with explicit assignment of SpinMultiplicity property values corresponding to 783 # Singlet (two unparied electrons corresponding to one spin state), Doublet (free radical; an unpaired 784 # electron corresponding to two spin states), and Triplet (two unparied electrons corresponding to 785 # three spin states; divalent carbon atoms (carbenes)), SpinMultiplicityCorrection is calculated as follows: 786 # 787 # SpinMultiplicity: Doublet(2); SpinMultiplicityCorrection: -1 (one valence electron not available for bonding) 788 # SpinMultiplicity: Singlet(1)/Triplet(3); SpinMultiplicityCorrection: -2 (two valence electrons not available for bonding) 789 # 790 # 791 sub GetImplicitValence { 792 my($This) = @_; 793 794 # Is this atom in a molecule? 795 if (!$This->HasProperty('Molecule')) { 796 return undef; 797 } 798 799 # Is ImplicitValence property explicitly set? 800 if ($This->HasProperty('ImplicitValence')) { 801 return $This->GetProperty('ImplicitValence'); 802 } 803 # Assign implicit valence... 804 my($AtomicNumber, $CommonValences); 805 806 $AtomicNumber = $This->{AtomicNumber}; 807 if (!$AtomicNumber) { 808 return undef; 809 } 810 811 $CommonValences = PeriodicTable::GetElementCommonValences($AtomicNumber); 812 if (!$CommonValences) { 813 return undef; 814 } 815 my($ImplicitValence, $FormalCharge, $SpinMultiplicityCorrection, @AvailableCommonValences); 816 817 $FormalCharge = $This->GetFormalCharge(); 818 if (!defined $FormalCharge) { 819 $FormalCharge = 0; 820 } 821 822 $SpinMultiplicityCorrection = 0; 823 if ($This->HasProperty('SpinMultiplicity')) { 824 my($SpinMultiplicity); 825 $SpinMultiplicity = $This->GetSpinMultiplicity(); 826 SPINMULTIPLICITY: { 827 if ($SpinMultiplicity =~ /^2$/i) { $SpinMultiplicityCorrection = -1; last SPINMULTIPLICITY; } 828 if ($SpinMultiplicity =~ /^(1|3)$/i) { $SpinMultiplicityCorrection = -2; last SPINMULTIPLICITY; } 829 $SpinMultiplicityCorrection = 0; 830 } 831 } 832 833 @AvailableCommonValences = split /\,/, $CommonValences; 834 835 # Only only available common valence... 836 if (@AvailableCommonValences == 1) { 837 my($CommonValence); 838 839 $CommonValence = $CommonValences; 840 $ImplicitValence = $CommonValence + $FormalCharge + $SpinMultiplicityCorrection; 841 842 return $ImplicitValence; 843 } 844 845 # Non-zero formal charge... 846 if ($FormalCharge != 0) { 847 # Use HighestCommonValence to set ImplicitValence... 848 my($HighestCommonValence); 849 850 $HighestCommonValence = $This->GetHighestCommonValence(); 851 $ImplicitValence = $HighestCommonValence + $FormalCharge + $SpinMultiplicityCorrection; 852 853 return $ImplicitValence; 854 } 855 856 # Set ImplicitValence using SumOfBondOrdersToNonHydrogenAtoms and appropriate CommonValence 857 # value... 858 my($SumOfBondOrders, $ValenceFound, $Valence, $AvailableCommonValence); 859 860 $SumOfBondOrders = $This->GetSumOfBondOrdersToNonHydrogenAtoms(); 861 if (!defined $SumOfBondOrders) { 862 $SumOfBondOrders = 0; 863 } 864 865 # Get first available valence higher than SumOfBondOrders... 866 $ValenceFound = 0; 867 $Valence = 0; 868 VALENCE: for $AvailableCommonValence (@AvailableCommonValences) { 869 if ($AvailableCommonValence >= $SumOfBondOrders) { 870 $ValenceFound = 1; 871 $Valence = $AvailableCommonValence; 872 last VALENCE; 873 } 874 } 875 if (!$ValenceFound) { 876 # Use highest common valence... 877 $Valence = $This->GetHighestCommonValence(); 878 } 879 $ImplicitValence = $Valence + $SpinMultiplicityCorrection; 880 881 return $ImplicitValence; 882 } 883 884 # Get lowest common valence... 885 sub GetLowestCommonValence { 886 my($This) = @_; 887 888 # Is LowestCommonValence property explicitly set? 889 if ($This->HasProperty('LowestCommonValence')) { 890 return $This->GetProperty('LowestCommonValence'); 891 } 892 my($AtomicNumber, $LowestCommonValence); 893 894 $AtomicNumber = $This->{AtomicNumber}; 895 if (!$AtomicNumber) { 896 return undef; 897 } 898 899 # LowestCommonValence is not set for all elements... 900 $LowestCommonValence = PeriodicTable::GetElementLowestCommonValence($AtomicNumber); 901 if (!$LowestCommonValence) { 902 $LowestCommonValence = undef; 903 } 904 905 return $LowestCommonValence; 906 } 907 908 # Get highest common valence... 909 sub GetHighestCommonValence { 910 my($This) = @_; 911 912 # Is HighestCommonValence property explicitly set? 913 if ($This->HasProperty('HighestCommonValence')) { 914 return $This->GetProperty('HighestCommonValence'); 915 } 916 my($AtomicNumber, $HighestCommonValence); 917 918 $AtomicNumber = $This->{AtomicNumber}; 919 if (!$AtomicNumber) { 920 return undef; 921 } 922 923 # HighestCommonValence is not set for all elements... 924 $HighestCommonValence = PeriodicTable::GetElementHighestCommonValence($AtomicNumber); 925 if (!$HighestCommonValence) { 926 $HighestCommonValence = undef; 927 } 928 929 return $HighestCommonValence; 930 } 931 932 # Get valence electrons... 933 sub GetValenceElectrons { 934 my($This) = @_; 935 936 # Is ValenceElectrons property explicitly set? 937 if ($This->HasProperty('ValenceElectrons')) { 938 return $This->GetProperty('ValenceElectrons'); 939 } 940 my($AtomicNumber, $ValenceElectrons); 941 942 $AtomicNumber = $This->{AtomicNumber}; 943 if (!$AtomicNumber) { 944 return undef; 945 } 946 947 $ValenceElectrons = PeriodicTable::GetElementValenceElectrons($AtomicNumber); 948 949 return $ValenceElectrons; 950 } 951 952 # Get free non-bodning valence electrons left on atom after taking into account 953 # sum of bond orders and formal charged on atom. 954 # 955 # Valence corresponds to number of electrons used by atom in bonding: 956 # 957 # Valence = ValenceElectrons - ValenceFreeElectrons 958 # 959 # Additionally, valence can also be calculated by: 960 # 961 # Valence = SumOfBondOrders + FormalCharge 962 # 963 # Implying for neutral molecules, Valence and SumOfBondOrders are equal. 964 # 965 # From two formulas for Valence described above, non-bonding free electrons 966 # left can be computed by: 967 # 968 # ValenceFreeElectrons = ValenceElectrons - SumOfBondOrders - FormalCharge 969 # 970 # Examples: 971 # 972 # o NH3: ValenceFreeElectrons = 5 - 3 - 0 = 2 973 # o NH4+; ValenceFreeElectrons = 5 - 4 - 1 = 0 974 # o C(=O)O- : ValenceFreeElectrons on O- = 6 - 1 + 1 = 6 975 # o C(=O)O- : ValenceFreeElectrons on =O = 6 - 2 - 0 = 4 976 # 977 # 978 sub GetValenceFreeElectrons { 979 my($This) = @_; 980 981 # Is this atom in a molecule? 982 if (!$This->HasProperty('Molecule')) { 983 return undef; 984 } 985 986 # Is ValenceFreeElectrons property explicitly set? 987 if ($This->HasProperty('ValenceFreeElectrons')) { 988 return $This->GetProperty('ValenceFreeElectrons'); 989 } 990 my($ValenceElectrons, $SumOfBondOrders, $ValenceFreeElectrons); 991 992 if (!$This->{AtomicNumber}) { 993 return undef; 994 } 995 $ValenceElectrons = $This->GetValenceElectrons(); 996 $SumOfBondOrders = $This->GetSumOfBondOrders(); 997 998 $ValenceFreeElectrons = $ValenceElectrons - $SumOfBondOrders; 999 1000 if ($This->HasProperty('FormalCharge')) { 1001 $ValenceFreeElectrons -= $This->GetProperty('FormalCharge'); 1002 } 1003 return $ValenceFreeElectrons; 1004 } 1005 1006 # Get num of missing hydrogens... 1007 # 1008 sub GetNumOfMissingHydrogens { 1009 my($This) = @_; 1010 my($NumOfMissingHydrogens); 1011 1012 $NumOfMissingHydrogens = $This->GetImplicitHydrogens() - $This->GetExplicitHydrogens(); 1013 1014 return $NumOfMissingHydrogens; 1015 } 1016 1017 # Get number of implicit hydrogen for atom... 1018 # 1019 sub GetImplicitHydrogens { 1020 my($This) = @_; 1021 1022 # Is this atom in a molecule? 1023 if (!$This->HasProperty('Molecule')) { 1024 return undef; 1025 } 1026 1027 # Is ImplicitHydrogens property explicitly set? 1028 if ($This->HasProperty('ImplicitHydrogens')) { 1029 return $This->GetProperty('ImplicitHydrogens'); 1030 } 1031 1032 # Is it an element symbol? 1033 if (!$This->{AtomicNumber}) { 1034 return undef; 1035 } 1036 my($ImplicitHydrogens, $ImplicitValence, $SumOfBondOrders); 1037 1038 $ImplicitHydrogens = 0; 1039 $ImplicitValence = $This->GetImplicitValence(); 1040 $SumOfBondOrders = $This->GetSumOfBondOrdersToNonHydrogenAtoms(); 1041 1042 if (defined($ImplicitValence) && defined($SumOfBondOrders)) { 1043 $ImplicitHydrogens = ($SumOfBondOrders >= $ImplicitValence) ? 0 : ($ImplicitValence - $SumOfBondOrders); 1044 } 1045 1046 return $ImplicitHydrogens; 1047 } 1048 1049 # Get number of explicit hydrogens for atom... 1050 sub GetExplicitHydrogens { 1051 my($This) = @_; 1052 1053 return $This->GetNumOfHydrogenAtomNeighbors(); 1054 } 1055 1056 # Add hydrogens to specified atom in molecule and return number of hydrogens added: 1057 # 1058 # o HydrogensToAdd = ImplicitHydrogenCount - ExplicitHydrogenCount 1059 # 1060 # o XYZ are set to ZeroVector 1061 # 1062 sub AddHydrogens { 1063 my($This, $HydrogenPositionsWarning) = @_; 1064 1065 # Is this atom in a molecule? 1066 if (!$This->HasProperty('Molecule')) { 1067 return undef; 1068 } 1069 if (!defined $HydrogenPositionsWarning) { 1070 $HydrogenPositionsWarning = 1; 1071 } 1072 if ($HydrogenPositionsWarning) { 1073 carp "Warning: ${ClassName}->AddHydrogens: The current release of MayaChemTools doesn't assign any hydrogen positions..."; 1074 } 1075 1076 # Is it an element symbol? 1077 if (!$This->{AtomicNumber}) { 1078 return undef; 1079 } 1080 1081 my($Molecule, $HydrogensAdded, $HydrogensToAdd); 1082 1083 $Molecule = $This->GetProperty('Molecule'); 1084 $HydrogensAdded = 0; 1085 $HydrogensToAdd = $This->GetNumOfMissingHydrogens(); 1086 if ($HydrogensToAdd <= 0) { 1087 return $HydrogensAdded; 1088 } 1089 1090 my($Count, $Hydrogen); 1091 1092 for $Count (1 .. $HydrogensToAdd) { 1093 $HydrogensAdded++; 1094 1095 $Hydrogen = $Molecule->NewAtom('AtomSymbol' => 'H', 'XYZ' => [0, 0, 0]); 1096 $Molecule->NewBond('Atoms' => [$This, $Hydrogen], 'BondOrder' => 1); 1097 } 1098 1099 return $HydrogensAdded; 1100 } 1101 1102 # Delete hydrogens attached to atom in molecule and return total number of hydrogens deleted... 1103 sub DeleteHydrogens { 1104 my($This) = @_; 1105 1106 # Is this atom in a molecule? 1107 if (!$This->HasProperty('Molecule')) { 1108 return undef; 1109 } 1110 1111 # Is it an element symbol? 1112 if (!$This->{AtomicNumber}) { 1113 return undef; 1114 } 1115 1116 my($Molecule, $Neighbor, $HydrogensDeleted, @Neighbors); 1117 1118 $Molecule = $This->GetProperty('Molecule'); 1119 $HydrogensDeleted = 0; 1120 @Neighbors = $This->GetNeighbors(); 1121 1122 NEIGHBOR: for $Neighbor (@Neighbors) { 1123 if (!$Neighbor->IsHydrogen()) { 1124 next NEIGHBOR; 1125 } 1126 $Molecule->_DeleteAtom($Neighbor); 1127 $HydrogensDeleted++; 1128 } 1129 1130 return $HydrogensDeleted; 1131 } 1132 1133 # Copy atom and all its associated data... 1134 sub Copy { 1135 my($This) = @_; 1136 my($Atom); 1137 1138 $Atom = Storable::dclone($This); 1139 1140 return $Atom; 1141 } 1142 1143 # Is it a Hydrogen atom? 1144 sub IsHydrogen { 1145 my($This) = @_; 1146 1147 return ($This->{AtomicNumber} == 1) ? 1 : 0; 1148 } 1149 1150 # Is it a Carbon atom? 1151 sub IsCarbon { 1152 my($This) = @_; 1153 1154 return ($This->{AtomicNumber} == 6) ? 1 : 0; 1155 } 1156 1157 # Is it a Nitrogen atom? 1158 sub IsNitrogen { 1159 my($This) = @_; 1160 1161 return ($This->{AtomicNumber} == 7) ? 1 : 0; 1162 } 1163 1164 # Is it a Oxygen atom? 1165 sub IsOxygen { 1166 my($This) = @_; 1167 1168 return ($This->{AtomicNumber} == 8) ? 1 : 0; 1169 } 1170 1171 # Is it a Fluorine atom? 1172 sub IsFluorine { 1173 my($This) = @_; 1174 1175 return ($This->{AtomicNumber} == 9) ? 1 : 0; 1176 } 1177 1178 # Is it a Phosphorus atom? 1179 sub IsPhosphorus { 1180 my($This) = @_; 1181 1182 return ($This->{AtomicNumber} == 15) ? 1 : 0; 1183 } 1184 1185 # Is it a Sulfur atom? 1186 sub IsSulfur { 1187 my($This) = @_; 1188 1189 return ($This->{AtomicNumber} == 16) ? 1 : 0; 1190 } 1191 1192 # Is it a Chlorine atom? 1193 sub IsChlorine { 1194 my($This) = @_; 1195 1196 return ($This->{AtomicNumber} == 17) ? 1 : 0; 1197 } 1198 1199 # Is it a Bromine atom? 1200 sub IsBromine { 1201 my($This) = @_; 1202 1203 return ($This->{AtomicNumber} == 35) ? 1 : 0; 1204 } 1205 1206 # Is it a Iodine atom? 1207 sub IsIodine { 1208 my($This) = @_; 1209 1210 return ($This->{AtomicNumber} == 53) ? 1 : 0; 1211 } 1212 1213 # Is it a hetro atom? (N, O, F, P, S, Cl, Br, I) 1214 sub IsHetroAtom { 1215 my($This) = @_; 1216 1217 return ($This->{AtomicNumber} =~ /^(7|8|9|15|16|17|35|53)$/) ? 1 : 0; 1218 } 1219 1220 # Is it a polar atom? ( N, O, P, S) 1221 sub IsPolarAtom { 1222 my($This) = @_; 1223 1224 return ($This->{AtomicNumber} =~ /^(7|8|15|16)$/) ? 1 : 0; 1225 } 1226 1227 # Is aromatic property set for the atom? 1228 sub IsAromatic { 1229 my($This) = @_; 1230 my($Aromatic); 1231 1232 $Aromatic = $This->GetAromatic(); 1233 1234 return (defined($Aromatic) && $Aromatic) ? 1 : 0; 1235 } 1236 1237 # Is this a hydrogen atom and attached to one of these atoms: N, O, P, S 1238 sub IsPolarHydrogen { 1239 my($This) = @_; 1240 1241 if (!$This->IsHydrogen()) { 1242 return 0; 1243 } 1244 1245 my(@Bonds); 1246 @Bonds = $This->GetBonds(); 1247 if (@Bonds > 1) { 1248 return 0; 1249 } 1250 1251 my($Bond, $BondedAtom); 1252 ($Bond) = @Bonds; 1253 $BondedAtom = $Bond->GetBondedAtom($This); 1254 1255 return $BondedAtom->IsPolarAtom() ? 1 : 0; 1256 } 1257 1258 # Is atom in a ring? 1259 # 1260 sub IsInRing { 1261 my($This) = @_; 1262 1263 # Is this atom in a molecule? 1264 if (!$This->HasProperty('Molecule')) { 1265 return undef; 1266 } 1267 my($Molecule); 1268 $Molecule = $This->GetProperty('Molecule'); 1269 1270 return $Molecule->_IsAtomInRing($This); 1271 } 1272 1273 # Is atom not in a ring? 1274 # 1275 sub IsNotInRing { 1276 my($This) = @_; 1277 1278 # Is this atom in a molecule? 1279 if (!$This->HasProperty('Molecule')) { 1280 return undef; 1281 } 1282 my($Molecule); 1283 $Molecule = $This->GetProperty('Molecule'); 1284 1285 return $Molecule->_IsAtomNotInRing($This); 1286 } 1287 1288 # Is atom only in one ring? 1289 # 1290 sub IsOnlyInOneRing { 1291 my($This) = @_; 1292 1293 # Is this atom in a molecule? 1294 if (!$This->HasProperty('Molecule')) { 1295 return undef; 1296 } 1297 my($Molecule); 1298 $Molecule = $This->GetProperty('Molecule'); 1299 1300 return $Molecule->_IsAtomInOnlyOneRing($This); 1301 } 1302 1303 # Is atom in a ring of specific size? 1304 # 1305 sub IsInRingOfSize { 1306 my($This, $RingSize) = @_; 1307 1308 # Is this atom in a molecule? 1309 if (!$This->HasProperty('Molecule')) { 1310 return undef; 1311 } 1312 my($Molecule); 1313 $Molecule = $This-&