       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