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