MayaChemTools

   1 package AtomTypes::TPSAAtomTypes;
   2 #
   3 # $RCSfile: TPSAAtomTypes.pm,v $
   4 # $Date: 2015/02/28 20:48:03 $
   5 # $Revision: 1.15 $
   6 #
   7 # Author: Manish Sud <msud@san.rr.com>
   8 #
   9 # Copyright (C) 2015 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 Scalar::Util ();
  33 use Text::ParseWords;
  34 use AtomTypes::AtomTypes;
  35 use Molecule;
  36 
  37 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  38 
  39 @ISA = qw(AtomTypes::AtomTypes Exporter);
  40 @EXPORT = qw(GetTPSAAtomTypesData GetAllPossibleTPSAAtomTypes);
  41 @EXPORT_OK = qw();
  42 
  43 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
  44 
  45 # Setup class variables...
  46 my($ClassName, %TPSAAtomTypesDataMap);
  47 _InitializeClass();
  48 
  49 # Overload Perl functions...
  50 use overload '""' => 'StringifyTPSAAtomTypes';
  51 
  52 # Class constructor...
  53 sub new {
  54   my($Class, %NamesAndValues) = @_;
  55 
  56   # Initialize object...
  57   my $This = $Class->SUPER::new();
  58   bless $This, ref($Class) || $Class;
  59   $This->_InitializeTPSAAtomTypes();
  60 
  61   $This->_InitializeTPSAAtomTypesProperties(%NamesAndValues);
  62 
  63   return $This;
  64 }
  65 
  66 # Initialize class ...
  67 sub _InitializeClass {
  68   #Class name...
  69   $ClassName = __PACKAGE__;
  70 
  71   # Initialize the data hash. It'll be loaded on demand later...
  72   %TPSAAtomTypesDataMap = ();
  73 }
  74 
  75 # Initialize object data...
  76 #
  77 sub _InitializeTPSAAtomTypes {
  78   my($This) = @_;
  79 
  80   # Type of AtomTypes...
  81   $This->{Type} = 'TPSA';
  82 
  83   # Besides polar atoms - N, O, P, S - no TPSA atom types are assigned to any other
  84   # atoms.
  85   #
  86   # By default, TPSA atom types are not assigned to Phosphorus and Sulfur atoms.
  87   #
  88   $This->{IgnorePhosphorus} = 0;
  89   $This->{IgnoreSulfur} = 0;
  90 
  91   return $This;
  92 }
  93 
  94 # Initialize object properties...
  95 #
  96 sub _InitializeTPSAAtomTypesProperties {
  97   my($This, %NamesAndValues) = @_;
  98 
  99   my($Name, $Value, $MethodName);
 100   while (($Name, $Value) = each  %NamesAndValues) {
 101     $MethodName = "Set${Name}";
 102     $This->$MethodName($Value);
 103   }
 104 
 105   # Make sure molecule object was specified...
 106   if (!exists $NamesAndValues{Molecule}) {
 107     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying molecule...";
 108   }
 109 
 110   return $This;
 111 }
 112 
 113 # Get TPSA atom types and associated data loaded from TPSA data file as
 114 # a reference to hash with the following hash data format:
 115 #
 116 # @{$TPSAAtomTypesDataMap{AtomTypes}} - Array of all possible atom types for all atoms
 117 # @{$TPSAAtomTypesDataMap->{ColLabels}} - Array of column labels
 118 # %{$TPSAAtomTypesDataMap->{DataCol<Num>}} - Hash keys pair: <DataCol<Num>, AtomType>
 119 #
 120 # This functionality can be either invoked as a class function or an
 121 # object method.
 122 #
 123 sub GetTPSAAtomTypesData {
 124 
 125   # Make sure data is loaded...
 126   _CheckAndLoadTPSAAtomTypesData();
 127 
 128   return \%TPSAAtomTypesDataMap;
 129 }
 130 
 131 # Get all possible TPSA atom types atoms as an array reference...
 132 #
 133 # This functionality can be either invoked as a class function or an
 134 # object method.
 135 #
 136 sub GetAllPossibleTPSAAtomTypes {
 137   return _GetAllPossibleTPSAAtomTypes();
 138 }
 139 
 140 # Are all atoms types successfully assigned?
 141 #
 142 # Notes:
 143 #   . Dynamic checking of atom types assignment for atoms eliminates the need
 144 #     to check and synchronize valid atom types during SetAtomType.
 145 #   . Base class method is overrided to check atom assignment to nitrogen and
 146 #     oxygen atom with optional check for phosphorus and sulfur atoms.
 147 #
 148 sub IsAtomTypesAssignmentSuccessful {
 149   my($This) = @_;
 150   my($Atom, $AtomType);
 151 
 152   ATOM: for $Atom ($This->{Molecule}->GetAtoms()) {
 153     if (!($Atom->IsNitrogen() || $Atom->IsOxygen() ||
 154         ($Atom->IsPhosphorus() && !$This->{IgnorePhosphorus}) ||
 155         ($Atom->IsSulfur() && !$This->{IgnoreSulfur}))) {
 156       next ATOM;
 157     }
 158     $AtomType = $This->GetAtomType($Atom);
 159     if ($AtomType =~ /^None$/i) {
 160       return 0;
 161     }
 162   }
 163 
 164   return 1;
 165 }
 166 
 167 # Get all possible TPSA atom types as an array reference...
 168 #
 169 sub _GetAllPossibleTPSAAtomTypes {
 170   my($TPSAAtomTypesDataRef);
 171 
 172   $TPSAAtomTypesDataRef = GetTPSAAtomTypesData();
 173 
 174   return \@{$TPSAAtomTypesDataRef->{AtomTypes}};
 175 }
 176 
 177 # Assign Topological Polar Surface Area (TPSA) atom types [ Ref 90-91 ] to Nitrogen and Oxygen
 178 # atoms with optional assignment to Phosphorus and Sulfur atoms.
 179 #
 180 # Notes:
 181 #     o Number of atom type symbols for:
 182 #         o N: 27
 183 #         o O: 7
 184 #         o P: 5
 185 #         o S: 8
 186 #
 187 sub AssignAtomTypes {
 188   my($This) = @_;
 189   my($Atom, $AtomType);
 190 
 191   ATOM: for $Atom ($This->GetMolecule()->GetAtoms()) {
 192     $AtomType = $This->_GetAtomType($Atom);
 193     $This->SetAtomType($Atom, $AtomType);
 194   }
 195 
 196   return $This;
 197 }
 198 
 199 # Get TPSA atom type for atom...
 200 #
 201 sub _GetAtomType {
 202   my($This, $Atom) = @_;
 203   my($AtomType);
 204 
 205   $AtomType = 'None';
 206 
 207   ATOM: {
 208     if ($Atom->IsNitrogen()) {
 209       $AtomType = $This->_GetAtomTypeForNitrogen($Atom);
 210       last ATOM;
 211     }
 212 
 213     if ($Atom->IsOxygen()) {
 214       $AtomType = $This->_GetAtomTypeForOxygen($Atom);
 215       last ATOM;
 216     }
 217 
 218     if ($Atom->IsPhosphorus() && !$This->{IgnorePhosphorus}) {
 219       $AtomType = $This->_GetAtomTypeForPhosphorus($Atom);
 220       last ATOM;
 221     }
 222 
 223     if ($Atom->IsSulfur() && !$This->{IgnoreSulfur}) {
 224       $AtomType = $This->_GetAtomTypeForSulfur($Atom);
 225       last ATOM;
 226     }
 227     $AtomType = 'None';
 228   }
 229 
 230   return $AtomType;
 231 }
 232 
 233 
 234 # Get TPSA atom type for Nitrogen atom...
 235 #
 236 # 27 AtomTypeSymbols for element N:
 237 #
 238 # AtomTypeSymbol - SMARTS - Comments
 239 # N1 - '[N](-*)(-*)-*'
 240 # N2 - '[N](-*)=*'
 241 # N3 - '[N]#*'
 242 # N4 - '[N](-*)(=*)=*' - As in nitro group
 243 # N5 - '[N](=*)#*' - Middle nitrogen in azide group
 244 # N6 - '[N]1(-*)-*-*-1' - Atom in a 3 membered ring
 245 # N7 - '[NH](-*)-*'
 246 # N8 - '[NH]1-*-*-1' - Atom in a 3 membered ring
 247 # N9 - '[NH]=*'
 248 # N10 - '[NH2]-*'
 249 # N11 - '[N+](-*)(-*)(-*)-*'
 250 # N12 - '[N+](-*)(-*)=*'
 251 # N13 - '[N+](-*)#*' - Nitrogen in isocyano group
 252 # N14 - '[NH+](-*)(-*)-*'
 253 # N15 - '[NH+](-*)=*'
 254 # N16 - '[NH2+](-*)-*'
 255 # N17 - '[NH2+]=*'
 256 # N18 - '[NH3+]-*'
 257 # N19 - '[n](:*):*'
 258 # N20 - '[n](:*)(:*):*'
 259 # N21 - '[n](-*)(:*):*'
 260 # N22 - '[n](=*)(:*):*' - As in pyridine N-oxide
 261 # N23 - '[nH](:*):*'
 262 # N24 - '[n+](:*)(:*):*'
 263 # N25 - '[n+](-*)(:*):*'
 264 # N26 - '[nH+](:*):*'
 265 # N - '[#7]' - Any other Nitrogen; Contribution: 30.5 - X*8.2 + H*1.5 or 0.0 for negative value
 266 #
 267 sub _GetAtomTypeForNitrogen {
 268   my($This, $Atom) = @_;
 269   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
 270 
 271   $AtomType = 'None';
 272 
 273   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
 274 
 275   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
 276   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
 277 
 278   ATOMTYPE: {
 279 
 280     # Aromatic Nitrogens...
 281     if ($Atom->IsAromatic()) {
 282       $AtomType = $This->_GetAtomTypeForAromaticNitrogen($Atom);
 283       last ATOMTYPE;
 284     }
 285 
 286     # Only single bonds...
 287     if ($NumOfPiBonds == 0) {
 288       $AtomType = $This->_GetAtomTypeForNitrogenWithOnlySigmaBonds($Atom);
 289       last ATOMTYPE;
 290     }
 291 
 292     # One double bond...
 293     if ($NumOfPiBonds == 1) {
 294       $AtomType = $This->_GetAtomTypeForNitrogenWithOnePiBond($Atom);
 295       last ATOMTYPE;
 296     }
 297 
 298     # One triple bond or two double bonds...
 299     if ($NumOfPiBonds == 2) {
 300       $AtomType = $This->_GetAtomTypeForNitrogenWithTwoPiBonds($Atom);
 301       last ATOMTYPE;
 302     }
 303 
 304     # One triple bond and a double bond...
 305     if ($NumOfPiBonds == 3) {
 306       $AtomType = $This->_GetAtomTypeForNitrogenWithThreePiBonds($Atom);
 307       last ATOMTYPE;
 308     }
 309 
 310     $AtomType = 'N';
 311   }
 312   return $AtomType;
 313 }
 314 
 315 # Get TPSA atom type for Oxygen atom...
 316 #
 317 # AtomTypeSymbol - SMARTS - Comments
 318 # O1 - '[O](-*)-*'
 319 # O2 - '[O]1-*-*-1' - Atom in a 3 membered ring
 320 # O3 - '[O]=*'
 321 # O4 - '[OH]-*'
 322 # O5 - '[O-]-*'
 323 # O6 - '[o](:*):*'
 324 # O - '[#8]' - Any other Oxygen; Contribution: 28.5 - X*8.6 + H*1.5 or 0.0 for negative value
 325 #
 326 sub _GetAtomTypeForOxygen {
 327   my($This, $Atom) = @_;
 328   my($AtomType);
 329 
 330   $AtomType = 'None';
 331 
 332   ATOMTYPE: {
 333 
 334     # O6 - '[o](:*):*'
 335     if ($This->_IsO6Oxygen($Atom)) {
 336       $AtomType = 'O6';
 337       last ATOMTYPE;
 338     }
 339 
 340     # O3 - '[O]=*'
 341     if ($This->_IsO3Oxygen($Atom)) {
 342       $AtomType = 'O3';
 343       last ATOMTYPE;
 344     }
 345 
 346     # O4 - '[OH]-*'
 347     if ($This->_IsO4Oxygen($Atom)) {
 348       $AtomType = 'O4';
 349       last ATOMTYPE;
 350     }
 351 
 352     # O5 - '[O-]-*'
 353     if ($This->_IsO5Oxygen($Atom)) {
 354       $AtomType = 'O5';
 355       last ATOMTYPE;
 356     }
 357 
 358     # O2 - '[O]1-*-*-1' - Atom in a 3 membered ring
 359     if ($This->_IsO2Oxygen($Atom)) {
 360       $AtomType = 'O2';
 361       last ATOMTYPE;
 362     }
 363 
 364     # O1 - '[O](-*)-*'
 365     if ($This->_IsO1Oxygen($Atom)) {
 366       $AtomType = 'O1';
 367       last ATOMTYPE;
 368     }
 369 
 370     # Any other Oxygen...
 371     $AtomType = 'O';
 372   }
 373 
 374   return $AtomType;
 375 }
 376 
 377 # Get TPSA atom type for Phosphorus atom...
 378 #
 379 # 4 AtomTypeSymbols for element P:
 380 #
 381 # AtomTypeSymbol - SMARTS - Comments
 382 # P1 - '[P](-*)(-*)-*'
 383 # P2 - '[P](-*)=*'
 384 # P3 - '[P](-*)(-*)(-*)=*'
 385 # P4 - '[PH](-*)(-*)=*'
 386 # P - '[#15]' - Any other Sulfur
 387 #
 388 sub _GetAtomTypeForPhosphorus {
 389   my($This, $Atom) = @_;
 390   my($AtomType);
 391 
 392   $AtomType = 'None';
 393 
 394   ATOMTYPE: {
 395 
 396     # P1 - '[P](-*)(-*)-*'
 397     if ($This->_IsP1Phosphorus($Atom)) {
 398       $AtomType = 'P1';
 399       last ATOMTYPE;
 400     }
 401 
 402     # P2 - '[P](-*)=*'
 403     if ($This->_IsP2Phosphorus($Atom)) {
 404       $AtomType = 'P2';
 405       last ATOMTYPE;
 406     }
 407 
 408     # P3 - '[P](-*)(-*)(-*)=*'
 409     if ($This->_IsP3Phosphorus($Atom)) {
 410       $AtomType = 'P3';
 411       last ATOMTYPE;
 412     }
 413 
 414     # P4 - '[PH](-*)(-*)=*'
 415     if ($This->_IsP4Phosphorus($Atom)) {
 416       $AtomType = 'P4';
 417       last ATOMTYPE;
 418     }
 419 
 420     # Any other Phosphorus...
 421     $AtomType = 'P';
 422   }
 423 
 424   return $AtomType;
 425 }
 426 
 427 # Get TPSA atom type for Sulfur atom...
 428 #
 429 # 7 AtomTypeSymbols for element S:
 430 #
 431 # AtomTypeSymbol - SMARTS - Comments
 432 # S1 - '[S](-*)-*'
 433 # S2 - '[S]=*'
 434 # S3 - '[S](-*)(-*)=*'
 435 # S4 - '[S](-*)(-*)(=*)=*'
 436 # S5 - '[SH]-*'
 437 # S6 - '[s](:*):*'
 438 # S - '[#16]' - Any other Phosphorus
 439 #
 440 sub _GetAtomTypeForSulfur {
 441   my($This, $Atom) = @_;
 442   my($AtomType);
 443 
 444   $AtomType = 'None';
 445 
 446   ATOMTYPE: {
 447 
 448     # S6 - '[s](:*):*'
 449     if ($This->_IsS6Sulfur($Atom)) {
 450       $AtomType = 'S6';
 451       last ATOMTYPE;
 452     }
 453 
 454     # S4 - '[S](-*)(-*)(=*)=*'
 455     if ($This->_IsS4Sulfur($Atom)) {
 456       $AtomType = 'S4';
 457       last ATOMTYPE;
 458     }
 459 
 460     # S1 - '[S](-*)-*'
 461     if ($This->_IsS1Sulfur($Atom)) {
 462       $AtomType = 'S1';
 463       last ATOMTYPE;
 464     }
 465 
 466     # S2 - '[S]=*'
 467     if ($This->_IsS2Sulfur($Atom)) {
 468       $AtomType = 'S2';
 469       last ATOMTYPE;
 470     }
 471 
 472     # S3 - '[S](-*)(-*)=*'
 473     if ($This->_IsS3Sulfur($Atom)) {
 474       $AtomType = 'S3';
 475       last ATOMTYPE;
 476     }
 477 
 478     # S5 - '[SH]-*'
 479     if ($This->_IsS5Sulfur($Atom)) {
 480       $AtomType = 'S5';
 481       last ATOMTYPE;
 482     }
 483 
 484     # Any other Sulfur...
 485     $AtomType = 'S';
 486   }
 487 
 488   return $AtomType;
 489 }
 490 
 491 # Get TPSA atom type for aromatic Nitrogen...
 492 #
 493 sub _GetAtomTypeForAromaticNitrogen {
 494   my($This, $Atom) = @_;
 495   my($AtomType);
 496 
 497   $AtomType = 'None';
 498 
 499   ATOMTYPE: {
 500 
 501     # N19 - '[n](:*):*'
 502     if ($This->_IsN19Nitrogen($Atom)) {
 503       $AtomType = 'N19';
 504       last ATOMTYPE;
 505     }
 506 
 507     # N20 - '[n](:*)(:*):*'
 508     if ($This->_IsN20Nitrogen($Atom)) {
 509       $AtomType = 'N20';
 510       last ATOMTYPE;
 511     }
 512 
 513     # N21 - '[n](-*)(:*):*'
 514     if ($This->_IsN21Nitrogen($Atom)) {
 515       $AtomType = 'N21';
 516       last ATOMTYPE;
 517     }
 518 
 519     # N22 - '[n](=*)(:*):*' - As in pyridine N-oxide
 520     if ($This->_IsN22Nitrogen($Atom)) {
 521       $AtomType = 'N22';
 522       last ATOMTYPE;
 523     }
 524 
 525     # N23 - '[nH](:*):*'
 526     if ($This->_IsN23Nitrogen($Atom)) {
 527       $AtomType = 'N23';
 528       last ATOMTYPE;
 529     }
 530 
 531     # N24 - '[n+](:*)(:*):*'
 532     if ($This->_IsN24Nitrogen($Atom)) {
 533       $AtomType = 'N24';
 534       last ATOMTYPE;
 535     }
 536 
 537     # N25 - '[n+](-*)(:*):*'
 538     if ($This->_IsN25Nitrogen($Atom)) {
 539       $AtomType = 'N25';
 540       last ATOMTYPE;
 541     }
 542 
 543     # N26 - '[nH+](:*):*'
 544     if ($This->_IsN26Nitrogen($Atom)) {
 545       $AtomType = 'N26';
 546       last ATOMTYPE;
 547     }
 548 
 549     $AtomType = 'N';
 550   }
 551 
 552   return $AtomType;
 553 }
 554 
 555 # Get TPSA atom type for Nitrogen with only sigma bonds...
 556 #
 557 sub _GetAtomTypeForNitrogenWithOnlySigmaBonds {
 558   my($This, $Atom) = @_;
 559   my($AtomType);
 560 
 561   $AtomType = 'None';
 562 
 563   ATOMTYPE: {
 564 
 565    # N6 - '[N]1(-*)-*-*-1' - Atom in a 3 membered ring
 566     if ($This->_IsN6Nitrogen($Atom)) {
 567       $AtomType = 'N6';
 568       last ATOMTYPE;
 569     }
 570 
 571     # N1 - '[N](-*)(-*)-*'
 572     if ($This->_IsN1Nitrogen($Atom)) {
 573       $AtomType = 'N1';
 574       last ATOMTYPE;
 575     }
 576 
 577     # N8 - '[NH]1-*-*-1' - Atom in a 3 membered ring
 578     if ($This->_IsN8Nitrogen($Atom)) {
 579       $AtomType = 'N8';
 580       last ATOMTYPE;
 581     }
 582 
 583     # N7 - '[NH](-*)-*'
 584     if ($This->_IsN7Nitrogen($Atom)) {
 585       $AtomType = 'N7';
 586       last ATOMTYPE;
 587     }
 588 
 589     # N10 - '[NH2]-*'
 590     if ($This->_IsN10Nitrogen($Atom)) {
 591       $AtomType = 'N10';
 592       last ATOMTYPE;
 593     }
 594 
 595     # N11 - '[N+](-*)(-*)(-*)-*'
 596     if ($This->_IsN11Nitrogen($Atom)) {
 597       $AtomType = 'N11';
 598       last ATOMTYPE;
 599     }
 600 
 601     # N14 - '[NH+](-*)(-*)-*'
 602     if ($This->_IsN14Nitrogen($Atom)) {
 603       $AtomType = 'N14';
 604       last ATOMTYPE;
 605     }
 606 
 607     # N16 - '[NH2+](-*)-*'
 608     if ($This->_IsN16Nitrogen($Atom)) {
 609       $AtomType = 'N16';
 610       last ATOMTYPE;
 611     }
 612 
 613     # N18 - '[NH3+]-*'
 614     if ($This->_IsN18Nitrogen($Atom)) {
 615       $AtomType = 'N18';
 616       last ATOMTYPE;
 617     }
 618 
 619     $AtomType = 'N';
 620   }
 621 
 622   return $AtomType;
 623 }
 624 
 625 # Get TPSA atom type for Nitrogen with one pi bonds...
 626 #
 627 sub _GetAtomTypeForNitrogenWithOnePiBond {
 628   my($This, $Atom) = @_;
 629   my($AtomType);
 630 
 631   $AtomType = 'None';
 632 
 633   ATOMTYPE: {
 634 
 635     # N2 - '[N](-*)=*'
 636     if ($This->_IsN2Nitrogen($Atom)) {
 637       $AtomType = 'N2';
 638       last ATOMTYPE;
 639     }
 640 
 641     # N9 - '[NH]=*'
 642     if ($This->_IsN9Nitrogen($Atom)) {
 643       $AtomType = 'N9';
 644       last ATOMTYPE;
 645     }
 646 
 647     # N12 - '[N+](-*)(-*)=*'
 648     if ($This->_IsN12Nitrogen($Atom)) {
 649       $AtomType = 'N12';
 650       last ATOMTYPE;
 651     }
 652 
 653     # N15 - '[NH+](-*)=*'
 654     if ($This->_IsN15Nitrogen($Atom)) {
 655       $AtomType = 'N15';
 656       last ATOMTYPE;
 657     }
 658 
 659     # N17 - '[NH2+]=*'
 660     if ($This->_IsN17Nitrogen($Atom)) {
 661       $AtomType = 'N17';
 662       last ATOMTYPE;
 663     }
 664 
 665     $AtomType = 'N';
 666   }
 667 
 668   return $AtomType;
 669 }
 670 
 671 # Get TPSA atom type for Nitrogen with two pi bonds...
 672 #
 673 sub _GetAtomTypeForNitrogenWithTwoPiBonds {
 674   my($This, $Atom) = @_;
 675   my($AtomType);
 676 
 677   $AtomType = 'None';
 678 
 679   ATOMTYPE: {
 680 
 681     # N3 - '[N]#*'
 682     if ($This->_IsN3Nitrogen($Atom)) {
 683       $AtomType = 'N3';
 684       last ATOMTYPE;
 685     }
 686 
 687     # N4 - '[N](-*)(=*)=*' - As in nitro group
 688     if ($This->_IsN4Nitrogen($Atom)) {
 689       $AtomType = 'N4';
 690       last ATOMTYPE;
 691     }
 692 
 693     # N13 - '[N+](-*)#*'- Nitrogen in isocyano group
 694     if ($This->_IsN13Nitrogen($Atom)) {
 695       $AtomType = 'N13';
 696       last ATOMTYPE;
 697     }
 698 
 699     $AtomType = 'N';
 700   }
 701 
 702   return $AtomType;
 703 }
 704 
 705 # Get TPSA atom type for Nitrogen with three pi bonds...
 706 #
 707 sub _GetAtomTypeForNitrogenWithThreePiBonds {
 708   my($This, $Atom) = @_;
 709   my($AtomType);
 710 
 711   $AtomType = 'None';
 712 
 713   ATOMTYPE: {
 714 
 715     # N5 - '[N](=*)#*' - Middle nitrogen in azide group
 716     if ($This->_IsN5Nitrogen($Atom)) {
 717       $AtomType = 'N5';
 718       last ATOMTYPE;
 719     }
 720 
 721     $AtomType = 'N';
 722   }
 723 
 724   return $AtomType;
 725 }
 726 
 727 # N1 - '[N](-*)(-*)-*'
 728 #
 729 sub _IsN1Nitrogen {
 730   my($This, $Atom) = @_;
 731 
 732   return $Atom->DoesAtomNeighborhoodMatch('N.!RA3.X3.SB3.H0.FC0') ? 1 : 0;
 733 }
 734 
 735 # N2 - '[N](-*)=*'
 736 #
 737 sub _IsN2Nitrogen {
 738   my($This, $Atom) = @_;
 739 
 740   return $Atom->DoesAtomNeighborhoodMatch('N.X2.SB1.DB1.H0.FC0') ? 1 : 0;
 741 }
 742 
 743 # N3 - '[N]#*'
 744 #
 745 sub _IsN3Nitrogen {
 746   my($This, $Atom) = @_;
 747 
 748   return $Atom->DoesAtomNeighborhoodMatch('N.X1.TB1.H0.FC0') ? 1 : 0;
 749 }
 750 
 751 # N4 - '[N](-*)(=*)=*' - As in nitro group
 752 #
 753 sub _IsN4Nitrogen {
 754   my($This, $Atom) = @_;
 755 
 756   return $Atom->DoesAtomNeighborhoodMatch('N.X3.SB1.DB2.H0.FC0') ? 1 : 0;
 757 }
 758 
 759 # N5 - '[N](=*)#*' - Middle nitrogen in azide group
 760 #
 761 sub _IsN5Nitrogen {
 762   my($This, $Atom) = @_;
 763 
 764   return $Atom->DoesAtomNeighborhoodMatch('N.X2.DB1.TB1.H0.FC0') ? 1 : 0;
 765 }
 766 
 767 # N6 - '[N]1(-*)-*-*-1' - Atom in a 3 membered ring
 768 #
 769 sub _IsN6Nitrogen {
 770   my($This, $Atom) = @_;
 771 
 772   return $Atom->DoesAtomNeighborhoodMatch('N.RA3.X3.SB3.H0.FC0') ? 1 : 0;
 773 }
 774 
 775 # N7 - '[NH](-*)-*'
 776 #
 777 sub _IsN7Nitrogen {
 778   my($This, $Atom) = @_;
 779 
 780   return $Atom->DoesAtomNeighborhoodMatch('N.!RA3.X2.SB2.H1.FC0') ? 1 : 0;
 781 }
 782 
 783 # N8 - '[NH]1-*-*-1' - Atom in a 3 membered ring
 784 #
 785 sub _IsN8Nitrogen {
 786   my($This, $Atom) = @_;
 787 
 788   return $Atom->DoesAtomNeighborhoodMatch('N.RA3.X2.SB2.H1.FC0') ? 1 : 0;
 789 }
 790 
 791 # N9 - '[NH]=*'
 792 #
 793 sub _IsN9Nitrogen {
 794   my($This, $Atom) = @_;
 795 
 796   return $Atom->DoesAtomNeighborhoodMatch('N.X1.DB1.H1.FC0') ? 1 : 0;
 797 }
 798 
 799 # N10 - '[NH2]-*'
 800 #
 801 sub _IsN10Nitrogen {
 802   my($This, $Atom) = @_;
 803 
 804   return $Atom->DoesAtomNeighborhoodMatch('N.X1.SB1.H2.FC0') ? 1 : 0;
 805 }
 806 
 807 # N11 - '[N+](-*)(-*)(-*)-*'
 808 #
 809 sub _IsN11Nitrogen {
 810   my($This, $Atom) = @_;
 811 
 812   return $Atom->DoesAtomNeighborhoodMatch('N.X4.SB4.H0.FC+1') ? 1 : 0;
 813 }
 814 
 815 # N12 - '[N+](-*)(-*)=*'
 816 #
 817 sub _IsN12Nitrogen {
 818   my($This, $Atom) = @_;
 819 
 820   return $Atom->DoesAtomNeighborhoodMatch('N.X3.SB2.DB1.H0.FC+1') ? 1 : 0;
 821 }
 822 
 823 # N13 - '[N+](-*)#*'- Nitrogen in isocyano group
 824 #
 825 sub _IsN13Nitrogen {
 826   my($This, $Atom) = @_;
 827 
 828   return $Atom->DoesAtomNeighborhoodMatch('N.X2.SB1.TB1.H0.FC+1') ? 1 : 0;
 829 }
 830 
 831 # N14 - '[NH+](-*)(-*)-*'
 832 #
 833 sub _IsN14Nitrogen {
 834   my($This, $Atom) = @_;
 835 
 836   return $Atom->DoesAtomNeighborhoodMatch('N.X3.SB3.H1.FC+1') ? 1 : 0;
 837 }
 838 
 839 # N15 - '[NH+](-*)=*'
 840 #
 841 sub _IsN15Nitrogen {
 842   my($This, $Atom) = @_;
 843 
 844   return $Atom->DoesAtomNeighborhoodMatch('N.X2.SB1.DB1.H1.FC+1') ? 1 : 0;
 845 }
 846 
 847 # N16 - '[NH2+](-*)-*'
 848 #
 849 sub _IsN16Nitrogen {
 850   my($This, $Atom) = @_;
 851 
 852   return $Atom->DoesAtomNeighborhoodMatch('N.X2.SB2.H2.FC+1') ? 1 : 0;
 853 }
 854 
 855 # N17 - '[NH2+]=*'
 856 #
 857 sub _IsN17Nitrogen {
 858   my($This, $Atom) = @_;
 859 
 860   return $Atom->DoesAtomNeighborhoodMatch('N.X1.DB1.H2.FC+1') ? 1 : 0;
 861 }
 862 
 863 # N18 - '[NH3+]-*'
 864 #
 865 sub _IsN18Nitrogen {
 866   my($This, $Atom) = @_;
 867 
 868   return $Atom->DoesAtomNeighborhoodMatch('N.X1.SB1.H3.FC+1') ? 1 : 0;
 869 }
 870 
 871 # N19 - '[n](:*):*'
 872 #
 873 sub _IsN19Nitrogen {
 874   my($This, $Atom) = @_;
 875 
 876   return $Atom->DoesAtomNeighborhoodMatch('N.Ar.X2.AB2.H0.FC0') ? 1 : 0;
 877 }
 878 
 879 # N20 - '[n](:*)(:*):*'
 880 #
 881 sub _IsN20Nitrogen {
 882   my($This, $Atom) = @_;
 883 
 884   return $Atom->DoesAtomNeighborhoodMatch('N.Ar.X3.AB3.H0.FC0') ? 1 : 0;
 885 }
 886 
 887 # N21 - '[n](-*)(:*):*'
 888 #
 889 sub _IsN21Nitrogen {
 890   my($This, $Atom) = @_;
 891 
 892   return $Atom->DoesAtomNeighborhoodMatch('N.Ar.X3.AB2.H0.FC0', ['*', '*', '*'], [':', ':', '-']) ? 1 : 0;
 893 }
 894 
 895 # N22 - '[n](=*)(:*):*' - As in pyridine N-oxide
 896 #
 897 sub _IsN22Nitrogen {
 898   my($This, $Atom) = @_;
 899 
 900   return $Atom->DoesAtomNeighborhoodMatch('N.Ar.X3.AB2.H0.FC0', ['*', '*', '*'], [':', ':', '=']) ? 1 : 0;
 901 }
 902 
 903 # N23 - '[nH](:*):*'
 904 #
 905 sub _IsN23Nitrogen {
 906   my($This, $Atom) = @_;
 907 
 908   return $Atom->DoesAtomNeighborhoodMatch('N.Ar.X2.AB2.H1.FC0') ? 1 : 0;
 909 }
 910 
 911 # N24 - '[n+](:*)(:*):*'
 912 #
 913 sub _IsN24Nitrogen {
 914   my($This, $Atom) = @_;
 915 
 916   return $Atom->DoesAtomNeighborhoodMatch('N.Ar.X3.AB3.H0.FC+1') ? 1 : 0;
 917 }
 918 
 919 # N25 - '[n+](-*)(:*):*'
 920 #
 921 sub _IsN25Nitrogen {
 922   my($This, $Atom) = @_;
 923 
 924   return $Atom->DoesAtomNeighborhoodMatch('N.Ar.X3.AB2.H0.FC+1', ['*', '*', '*'], [':', ':', '-']) ? 1 : 0;
 925 }
 926 
 927 # N26 - '[nH+](:*):*'
 928 #
 929 sub _IsN26Nitrogen {
 930   my($This, $Atom) = @_;
 931 
 932   return $Atom->DoesAtomNeighborhoodMatch('N.Ar.X2.AB2.H1.FC+1') ? 1 : 0;
 933 }
 934 
 935 # O1 - '[O](-*)-*'
 936 #
 937 sub _IsO1Oxygen {
 938   my($This, $Atom) = @_;
 939 
 940   return $Atom->DoesAtomNeighborhoodMatch('O.!RA.X2.SB2.H0.FC0') ? 1 : 0;
 941 }
 942 
 943 # O2 - '[O]1-*-*-1' - Atom in a 3 membered ring
 944 #
 945 sub _IsO2Oxygen {
 946   my($This, $Atom) = @_;
 947 
 948   return $Atom->DoesAtomNeighborhoodMatch('O.RA3.X2.SB2.H0.FC0') ? 1 : 0;
 949 }
 950 
 951 # O3 - '[O]=*'
 952 #
 953 sub _IsO3Oxygen {
 954   my($This, $Atom) = @_;
 955 
 956   return $Atom->DoesAtomNeighborhoodMatch('O.X1.DB1.H0.FC0') ? 1 : 0;
 957 }
 958 
 959 # O4 - '[OH]-*'
 960 #
 961 sub _IsO4Oxygen {
 962   my($This, $Atom) = @_;
 963 
 964   return $Atom->DoesAtomNeighborhoodMatch('O.X1.SB1.H1.FC0') ? 1 : 0;
 965 }
 966 
 967 # O5 - '[O-]-*'
 968 #
 969 sub _IsO5Oxygen {
 970   my($This, $Atom) = @_;
 971 
 972   return $Atom->DoesAtomNeighborhoodMatch('O.X1.SB1.H0.FC-1') ? 1 : 0;
 973 }
 974 
 975 # O6 - '[o](:*):*'
 976 #
 977 sub _IsO6Oxygen {
 978   my($This, $Atom) = @_;
 979 
 980   return $Atom->DoesAtomNeighborhoodMatch('O.Ar.X2.AB2.H0.FC0') ? 1 : 0;
 981 }
 982 
 983 # P1 - '[P](-*)(-*)-*'
 984 #
 985 sub _IsP1Phosphorus {
 986   my($This, $Atom) = @_;
 987 
 988   return $Atom->DoesAtomNeighborhoodMatch('P.X3.SB3.H0.FC0') ? 1 : 0;
 989 }
 990 
 991 # P2 - '[P](-*)=*'
 992 #
 993 sub _IsP2Phosphorus {
 994   my($This, $Atom) = @_;
 995 
 996   return $Atom->DoesAtomNeighborhoodMatch('P.X2.SB1.DB1.H0.FC0') ? 1 : 0;
 997 }
 998 
 999 # P3 - '[P](-*)(-*)(-*)=*'
1000 #
1001 sub _IsP3Phosphorus {
1002   my($This, $Atom) = @_;
1003 
1004   return $Atom->DoesAtomNeighborhoodMatch('P.X4.SB3.DB1.H0.FC0') ? 1 : 0;
1005 }
1006 
1007 # P4 - '[PH](-*)(-*)=*'
1008 #
1009 sub _IsP4Phosphorus {
1010   my($This, $Atom) = @_;
1011 
1012   return $Atom->DoesAtomNeighborhoodMatch('P.X3.SB2.DB1.H1.FC0') ? 1 : 0;
1013 }
1014 
1015 # S1 - '[S](-*)-*'
1016 #
1017 sub _IsS1Sulfur {
1018   my($This, $Atom) = @_;
1019 
1020   return $Atom->DoesAtomNeighborhoodMatch('S.X2.SB2.H0.FC0') ? 1 : 0;
1021 }
1022 
1023 # S2 - '[S]=*'
1024 #
1025 sub _IsS2Sulfur {
1026   my($This, $Atom) = @_;
1027 
1028   return $Atom->DoesAtomNeighborhoodMatch('S.X1.DB1.H0.FC0') ? 1 : 0;
1029 }
1030 
1031 # S3 - '[S](-*)(-*)=*'
1032 #
1033 sub _IsS3Sulfur {
1034   my($This, $Atom) = @_;
1035 
1036   return $Atom->DoesAtomNeighborhoodMatch('S.X3.SB2.DB1.H0.FC0') ? 1 : 0;
1037 }
1038 
1039 # S4 - '[S](-*)(-*)(=*)=*'
1040 #
1041 sub _IsS4Sulfur {
1042   my($This, $Atom) = @_;
1043 
1044   return $Atom->DoesAtomNeighborhoodMatch('S.X4.SB2.DB2.H0.FC0') ? 1 : 0;
1045 }
1046 
1047 # S5 - '[SH]-*'
1048 #
1049 sub _IsS5Sulfur {
1050   my($This, $Atom) = @_;
1051 
1052   return $Atom->DoesAtomNeighborhoodMatch('S.X1.SB1.H1.FC0') ? 1 : 0;
1053 }
1054 
1055 # S6 - '[s](:*):*'
1056 #
1057 sub _IsS6Sulfur {
1058   my($This, $Atom) = @_;
1059 
1060   return $Atom->DoesAtomNeighborhoodMatch('S.Ar.X2.AB2.H0.FC0') ? 1 : 0;
1061 }
1062 
1063 # Return a string containg data for TPSAAtomTypes object...
1064 #
1065 sub StringifyTPSAAtomTypes {
1066   my($This) = @_;
1067   my($AtomTypesString);
1068 
1069   # Type of AtomTypes...
1070   $AtomTypesString = "AtomTypes: $This->{Type}; IgnorePhosphorus: " . ($This->{IgnorePhosphorus} ? "Yes" : "No") . "; IgnoreSulfur: " .  ($This->{IgnoreSulfur} ? "Yes" : "No");
1071 
1072   # Setup atom types information...
1073   my($AtomID, $AtomType, @AtomTypesInfo, %AssignedAtomTypes);
1074 
1075   @AtomTypesInfo = ();
1076   %AssignedAtomTypes = $This->GetAtomTypes();
1077 
1078   for $AtomID (sort { $a <=> $b } keys %AssignedAtomTypes) {
1079     $AtomType = $AssignedAtomTypes{$AtomID} ? $AssignedAtomTypes{$AtomID} : 'None';
1080     push @AtomTypesInfo, "$AtomID:$AtomType";
1081   }
1082   $AtomTypesString .= "; AtomIDs:AtomTypes: <" . TextUtil::JoinWords(\@AtomTypesInfo, ", ", 0) . ">";
1083 
1084   return $AtomTypesString;
1085 }
1086 
1087 # Is it a TPSAAtomTypes object?
1088 sub _IsTPSAAtomTypes {
1089   my($Object) = @_;
1090 
1091   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
1092 }
1093 
1094 # Check and load TPSA atom types data...
1095 #
1096 sub _CheckAndLoadTPSAAtomTypesData {
1097 
1098   # Is it already loaded?
1099   if (exists $TPSAAtomTypesDataMap{AtomTypes}) {
1100     return;
1101   }
1102 
1103   _LoadTPSAAtomTypesData();
1104 }
1105 
1106 # Load TPSA atom types data from the file assuming first column to be atom type symbol..
1107 #
1108 # Format:
1109 #
1110 # "AtomType","SMARTS","TPSAContribution","Comments"
1111 # "N1","[N](-*)(-*)-*","3.24",""
1112 # "N2","[N](-*)=*","12.36",""
1113 #
1114 sub _LoadTPSAAtomTypesData {
1115   my($AtomTypesDataFile, $MayaChemToolsLibDir);
1116 
1117   $MayaChemToolsLibDir = FileUtil::GetMayaChemToolsLibDirName();
1118 
1119   $AtomTypesDataFile =  "$MayaChemToolsLibDir" . "/data/TPSAAtomTypes.csv";
1120   if (! -e "$AtomTypesDataFile") {
1121     croak "Error: MayaChemTools package file, $AtomTypesDataFile, is missing: Possible installation problems...";
1122   }
1123 
1124   %TPSAAtomTypesDataMap = ();
1125   AtomTypes::AtomTypes::LoadAtomTypesData($AtomTypesDataFile, \%TPSAAtomTypesDataMap);
1126 }
1127