       1 package AtomTypes::SLogPAtomTypes;
       2 #
       3 # File: SLogPAtomTypes.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 FileUtil ();
      32 use AtomTypes::AtomTypes;
      33 use Molecule;
      34
      35 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      36
      37 @ISA = qw(AtomTypes::AtomTypes Exporter);
      38 @EXPORT = qw(GetSLogPAtomTypesData GetAllPossibleSLogPAtomTypes GetAllPossibleSLogPNonHydrogenAtomTypes);
      39 @EXPORT_OK = qw();
      40
      41 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
      42
      43 # Setup class variables...
      44 my($ClassName, %SLogPAtomTypesDataMap);
      45 _InitializeClass();
      46
      47 # Overload Perl functions...
      48 use overload '""' => 'StringifySLogPAtomTypes';
      49
      50 # Class constructor...
      51 sub new {
      52   my($Class, %NamesAndValues) = @_;
      53
      54   # Initialize object...
      55   my $This = $Class->SUPER::new();
      56   bless $This, ref($Class) || $Class;
      57   $This->_InitializeSLogPAtomTypes();
      58
      59   $This->_InitializeSLogPAtomTypesProperties(%NamesAndValues);
      60
      61   return $This;
      62 }
      63
      64 # Initialize class ...
      65 sub _InitializeClass {
      66   #Class name...
      67   $ClassName = __PACKAGE__;
      68
      69   # Initialize the data hash. It'll be loaded on demand later...
      70   %SLogPAtomTypesDataMap = ();
      71 }
      72
      73 # Initialize object data...
      74 #
      75 sub _InitializeSLogPAtomTypes {
      76   my($This) = @_;
      77
      78   # Type of AtomTypes...
      79   $This->{Type} = 'SLogP';
      80
      81   # By default, SLogP atom types are also assigned to hydrogens...
      82   $This->{IgnoreHydrogens} = 0;
      83
      84   return $This;
      85 }
      86
      87 # Initialize object properties...
      88 #
      89 sub _InitializeSLogPAtomTypesProperties {
      90   my($This, %NamesAndValues) = @_;
      91
      92   my($Name, $Value, $MethodName);
      93   while (($Name, $Value) = each  %NamesAndValues) {
      94     $MethodName = "Set${Name}";
      95     $This->$MethodName($Value);
      96   }
      97
      98   # Make sure molecule object was specified...
      99   if (!exists $NamesAndValues{Molecule}) {
     100     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying molecule...";
     101   }
     102
     103   return $This;
     104 }
     105
     106 # Get SLogP atom types and associated data loaded from SLogP data file as
     107 # a reference to hash with the following hash data format:
     108 #
     109 # @{$SLogPAtomTypesDataMap{AtomTypes}} - Array of all possible atom types for all atoms
     110 # @{$SLogPAtomTypesDataMap{NonHydrogenAtomTypes}} - Array of all possible atom types for non-hydrogen atoms
     111 # @{$SLogPAtomTypesDataMap->{ColLabels}} - Array of column labels
     112 # %{$SLogPAtomTypesDataMap->{DataCol<Num>}} - Hash keys pair: <DataCol<Num>, AtomType>
     113 #
     114 # This functionality can be either invoked as a class function or an
     115 # object method.
     116 #
     117 sub GetSLogPAtomTypesData {
     118
     119   # Make sure data is loaded...
     120   _CheckAndLoadSLogPAtomTypesData();
     121
     122   return \%SLogPAtomTypesDataMap;
     123 }
     124
     125 # Get all possible SLogP atom types corresponding to hydrogen and non-hydrogen
     126 # atoms as an array reference...
     127 #
     128 # This functionality can be either invoked as a class function or an
     129 # object method.
     130 #
     131 sub GetAllPossibleSLogPAtomTypes {
     132   return _GetAllPossibleSLogPAtomTypes();
     133 }
     134
     135 # Get all possible SLogP atom types corresponding to non-hydrogen atoms
     136 # as an array reference...
     137 #
     138 # This functionality can be either invoked as a class function or an
     139 # object method.
     140 #
     141 sub GetAllPossibleSLogPNonHydrogenAtomTypes {
     142   my($NonHydrogensOnly);
     143
     144   $NonHydrogensOnly = 1;
     145   return _GetAllPossibleSLogPAtomTypes($NonHydrogensOnly);
     146 }
     147
     148 # Get all possible SLogP atom types as an array reference...
     149 #
     150 sub _GetAllPossibleSLogPAtomTypes {
     151   my($NonHydrogensOnly) = @_;
     152   my($SLogPAtomTypesDataRef);
     153
     154   $NonHydrogensOnly = defined $NonHydrogensOnly ? $NonHydrogensOnly : 0;
     155
     156   $SLogPAtomTypesDataRef = GetSLogPAtomTypesData();
     157
     158   return $NonHydrogensOnly ? \@{$SLogPAtomTypesDataRef->{NonHydrogenAtomTypes}}: \@{$SLogPAtomTypesDataRef->{AtomTypes}};
     159 }
     160
     161 # Assign SLogP [ Ref 89 ] atom types to all atoms...
     162 #
     163 # Notes:
     164 #     o 72 SLogP atom type symbols are listed
     165 #     o Number of atom types symbols for:
     166 #         o C: 28
     167 #         o N: 15
     168 #         o O: 13
     169 #         o P: 1
     170 #         o S: 3
     171 #         o H: 5
     172 #         o F, Cl, Br, I: 1 each
     173 #         o Ionic halogen: 1
     174 #         o p-block elements: 1
     175 #         o d-block elements: 1
     176 #
     177 sub AssignAtomTypes {
     178   my($This) = @_;
     179   my($Atom, $AtomType);
     180
     181   ATOM: for $Atom ($This->GetMolecule()->GetAtoms()) {
     182     if ($This->{IgnoreHydrogens} && $Atom->IsHydrogen()) {
     183       next ATOM;
     184     }
     185     $AtomType = $This->_GetAtomType($Atom);
     186     $This->SetAtomType($Atom, $AtomType);
     187   }
     188
     189   return $This;
     190 }
     191
     192 # Get SLogP atom type for atom...
     193 #
     194 sub _GetAtomType {
     195   my($This, $Atom) = @_;
     196   my($AtomType);
     197
     198   $AtomType = 'None';
     199
     200   ATOM: {
     201     if ($Atom->IsCarbon()) {
     202       $AtomType = $This->_GetAtomTypeForCarbon($Atom);
     203       last ATOM;
     204     }
     205
     206     if ($Atom->IsNitrogen()) {
     207       $AtomType = $This->_GetAtomTypeForNitrogen($Atom);
     208       last ATOM;
     209     }
     210
     211     if ($Atom->IsOxygen()) {
     212       $AtomType = $This->_GetAtomTypeForOxygen($Atom);
     213       last ATOM;
     214     }
     215
     216     if ($Atom->IsPhosphorus()) {
     217       $AtomType = $This->_GetAtomTypeForPhosphorus($Atom);
     218       last ATOM;
     219     }
     220
     221     if ($Atom->IsSulfur()) {
     222       $AtomType = $This->_GetAtomTypeForSulfur($Atom);
     223       last ATOM;
     224     }
     225
     226     if ($Atom->IsHydrogen()) {
     227       $AtomType = $This->_GetAtomTypeForHydrogen($Atom);
     228       last ATOM;
     229     }
     230
     231     $AtomType = $This->_GetAtomTypeForOtherAtoms($Atom);
     232   }
     233
     234   return $AtomType;
     235 }
     236
     237 # Get SLogP atom type for Carbon atom...
     238 #
     239 # 28 AtomTypeSymbols for element C:
     240 #
     241 # AtomTypeSymbol - Description - SMARTS
     242 # C1 - primary, secondary aliphatic - '[CH4]','[CH3]C','[CH2](C)C'
     243 # C2 - tertiary, quaternary aliphatic - '[CH](C)(C)C','[C](C)(C)(C)C'
     244 # C3 - primary, secondary heteroatom - '[CH3][(N,O,P,S,F,Cl,Br,I)]','[CH2X4](N,O,P,S,F,Cl,Br,I)]'
     245 # C4 - tertiary, quaternary heteroatom - '[CH1X4][(N,O,P,S,F,Cl,Br,I)]','[CH0X4][(N,O,P,S,F,Cl,Br,I)]'
     246 # C5 - C = heteroatom - '[C]=[A#X]'
     247 # C6 - C = C aliphatic - '[CH2]=C','[CH1](=C)A','[CH0](=C)(A)A','[C](=C)=C'
     248 # C7 - acetylene, nitrile - '[CX2]#A'
     249 # C8 - primary aromatic carbon - '[CH3]c'
     250 # C9 - primary aromatic heteroatom - '[CH3][a#X]'
     251 # C10 - secondary aromatic - '[CH2X4]a'
     252 # C11 - tertiary aromatic - '[CHX4]a'
     253 # C12 - quaternary aromatic - '[CH0X4]a'
     254 # C13 - aromatic heteroatom - '[cH0]-[!(C,N,O,S,F,Cl,Br,I)]'
     255 # C14 - aromatic halide - '[c][#9]'
     256 # C15 - aromatic halide - '[c][#17]'
     257 # C16 - aromatic halide - '[c][#35]'
     258 # C17 - aromatic halide - '[c][#53]'
     259 # C18 - aromatic - '[cH]'
     260 # C19 - aromatic bridgehead - '[c](:a)(:a):a'
     261 # C20 - quaternary aromatic - '[c](:a)(:a)-a'
     262 # C21 - quaternary aromatic - '[c](:a)(:a)-C'
     263 # C22 - quaternary aromatic - '[c](:a)(:a)-N'
     264 # C23 - quaternary aromatic - '[c](:a)(:a)-O'
     265 # C24 - quaternary aromatic - '[c](:a)(:a)-S'
     266 # C25 - quaternary aromatic - '[c](:a)(:a)=C','[c](:a)(:a)=N','[c](:a)(:a)=O'
     267 # C26 - C = C aromatic - '[C](=C)(a)A','[C](=C)(c)a','[CH](=C)a','[C]=c'
     268 # C27 - aliphatic heteroatom - '[CX4][!(C,N,O,P,S,F,Cl,Br,I)]'
     269 # CS - carbon supplemental not matching any basic C type - '[#6]'
     270 #
     271 sub _GetAtomTypeForCarbon {
     272   my($This, $Atom) = @_;
     273   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
     274
     275   $AtomType = 'None';
     276
     277   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
     278
     279   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
     280   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
     281
     282   ATOMTYPE: {
     283
     284     # Only single bonds...
     285     if ($NumOfPiBonds == 0) {
     286       $AtomType = $This->_GetAtomTypeForCarbonWithOnlySigmaBonds($Atom);
     287       last ATOMTYPE;
     288     }
     289
     290     # One double bond...
     291     if ($NumOfPiBonds == 1) {
     292       $AtomType = $This->_GetAtomTypeForCarbonWithOnePiBond($Atom);
     293       last ATOMTYPE;
     294     }
     295
     296     # One triple bond or two double bonds...
     297     if ($NumOfPiBonds == 2) {
     298       $AtomType = $This->_GetAtomTypeForCarbonWithTwoPiBonds($Atom);
     299       last ATOMTYPE;
     300     }
     301
     302     $AtomType = 'CS';
     303   }
     304   return $AtomType;
     305 }
     306
     307 # Get SLogP atom type for Nitrogen atom...
     308 #
     309 # 15 AtomTypeSymbols for element N:
     310 #
     311 # AtomTypeSymbol - Description - SMARTS
     312 # N1 - primary amine - '[NH2+0]A'
     313 # N2 - secondary amine - '[NH+0](A)A'
     314 # N3 - primary aromatic amine - '[NH2+0]a'
     315 # N4 - secondary aromatic amine - '[NH+0](A)a','[NH+0](a)a'
     316 # N5 - imine - '[NH+0]=A','[NH+0]=a'
     317 # N6 - substituted imine - '[N+0](=A)A','[N+0](=A)a','[N+0](=a)A','[N+0](=a)a'
     318 # N7 - tertiary amine - '[N+0](A)(A)A'
     319 # N8 - tertiary aromatic amine - '[N+0](a)(A)A','[N+0](a)(a)A','[N+0](a)(a)a'
     320 # N9 - nitrile - '[N+0]#A'
     321 # N10 - protonated amine - '[NH3+*]','[NH2+*]','[NH+*]'
     322 # N11 - unprotonated aromatic - '[n+0]'
     323 # N12 - protonated aromatic - '[n+*]'
     324 # N13 - quaternary amine - '[NH0+*](A)(A)(A)A','[NH0+*](=A)(A)A','[NH0+*](=A)(A)a','[NH0+*](=[#6])=[#7]'
     325 # N14 - other ionized nitrogen - '[N+*]#A','[N-*]','[N+*](=[N-*])=N'
     326 # NS - nitrogen supplemental not matching any basic N type - '[#7]'
     327 #
     328 sub _GetAtomTypeForNitrogen {
     329   my($This, $Atom) = @_;
     330   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
     331
     332   $AtomType = 'None';
     333
     334   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
     335
     336   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
     337   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
     338
     339   ATOMTYPE: {
     340
     341     # Only single bonds...
     342     if ($NumOfPiBonds == 0) {
     343       $AtomType = $This->_GetAtomTypeForNitrogenWithOnlySigmaBonds($Atom);
     344       last ATOMTYPE;
     345     }
     346
     347     # One double bond...
     348     if ($NumOfPiBonds == 1) {
     349       $AtomType = $This->_GetAtomTypeForNitrogenWithOnePiBond($Atom);
     350       last ATOMTYPE;
     351     }
     352
     353     # One triple bond or two double bonds...
     354     if ($NumOfPiBonds == 2) {
     355       $AtomType = $This->_GetAtomTypeForNitrogenWithTwoPiBonds($Atom);
     356       last ATOMTYPE;
     357     }
     358
     359     $AtomType = 'NS';
     360   }
     361   return $AtomType;
     362 }
     363
     364 # Get SLogP atom type for Oxygen atom...
     365 #
     366 # 13 AtomTypeSymbols for element O:
     367 #
     368 # AtomTypeSymbol - Description - SMARTS
     369 # O1 - aromatic - '[o]'
     370 # O2 - alcohol - '[OH]','[OH2]'
     371 # O3 - aliphatic ether - '[O](C)C','[O](C)[A#X]','[O]([A#X])[A#X]'
     372 # O4 - aromatic ether - '[O](A)a','[O](a)a'
     373 # O5 - oxide - '[O]=[#8]','[O]=[#7]','[OX1-*][#7]'
     374 # O6 - oxide - '[OX1-*][#16]'
     375 # O7 - oxide - '[OX1-*][!(N,S)]'
     376 # O8 - aromatic carbonyl - '[O]=c'
     377 # O9 - carbonyl aliphatic - '[O]=[CH]C','[O]=C(C)C','[O]=C(C)[A#X]','[O]=[CH]N','[O]=[CH]O','[O]=[CH2]','[O]=[CX2]=O'
     378 # O10 - carbonyl aromatic - '[O]=[CH]c','[O]=C(C)c','[O]=C(c)c','[O]=C(c)[a#X]','[O]=C(c)[A#X]','[O]=C(C)[a#X]'
     379 # O11 - carbonyl heteroatom - '[O]=C([A#X])[A#X]','[O]=C([A#X])[a#X]','[O]=C([a#X])[a#X]'
     380 # O12 - acid - '[O-1]C(=O)'
     381 # OS - oxygen supplemental not matching any basic O type - '[#8]'
     382 #
     383 sub _GetAtomTypeForOxygen {
     384   my($This, $Atom) = @_;
     385   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
     386
     387   $AtomType = 'None';
     388
     389   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
     390
     391   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
     392   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
     393
     394   ATOMTYPE: {
     395
     396     # Only single bonds...
     397     if ($NumOfPiBonds == 0) {
     398       $AtomType = $This->_GetAtomTypeForOxygenWithOnlySigmaBonds($Atom);
     399       last ATOMTYPE;
     400     }
     401
     402     # One double bond...
     403     if ($NumOfPiBonds == 1) {
     404       $AtomType = $This->_GetAtomTypeForOxygenWithOnePiBond($Atom);
     405       last ATOMTYPE;
     406     }
     407
     408     # OS - oxygen supplemental not matching any basic O type - '[#8]'
     409     $AtomType = 'OS';
     410   }
     411   return $AtomType;
     412 }
     413
     414 # Get SLogP atom type for Phosphorus atom...
     415 #
     416 # 1 AtomTypeSymbols for element P:
     417 #
     418 # AtomTypeSymbol - Description - SMARTS
     419 # P - phosphorous - '[#15]'
     420 #
     421 sub _GetAtomTypeForPhosphorus {
     422   my($This, $Atom) = @_;
     423   my($AtomType);
     424
     425   $AtomType = 'P';
     426
     427   return $AtomType;
     428 }
     429
     430 # Get SLogP atom type for Sulfur atom...
     431 #
     432 # 3 AtomTypeSymbols for element S:
     433 #
     434 # AtomTypeSymbol - Description - SMARTS
     435 # S1 - aliphatic - '[S-0]'
     436 # S2 - ionic sulfur - '[S-*]','[S+*]'
     437 # S3 - aromatic - '[s]'
     438 #
     439 sub _GetAtomTypeForSulfur {
     440   my($This, $Atom) = @_;
     441   my($AtomType);
     442
     443   $AtomType = 'None';
     444
     445   ATOMTYPE: {
     446
     447     # S1 - aliphatic - '[S-0]'
     448     if ($This->_IsS1Sulfur($Atom)) {
     449       $AtomType = 'S1';
     450       last ATOMTYPE;
     451     }
     452
     453     # S2 - ionic sulfur - '[S-*]','[S+*]'
     454     if ($This->_IsS2Sulfur($Atom)) {
     455       $AtomType = 'S2';
     456       last ATOMTYPE;
     457     }
     458
     459     # S3 - aromatic - '[s]'
     460     if ($This->_IsS3Sulfur($Atom)) {
     461       $AtomType = 'S3';
     462       last ATOMTYPE;
     463     }
     464
     465     # S1 - aliphatic - '[S-0]'
     466     $AtomType = 'S1';
     467   }
     468
     469   return $AtomType;
     470 }
     471
     472 # Get SLogP atom type for Hydrogen atom...
     473 #
     474 # 5 AtomTypeSymbols for element H:
     475 #
     476 # AtomTypeSymbol - Description - SMARTS
     477 # H1 - hydrocarbon - '[#1][#6]','[#1][#1]'
     478 # H2 - alcohol - '[#1]O[CX4]','[#1]Oc','[#1]O[!(C,N,O,S)]','[#1][!C,N,O)]'
     479 # H3 - amine - '[#1][#7]','[#1]O[#7]'
     480 # H4 - acid - '[#1]OC=[#6]','[#1]OC=[#7]','[#1]OC=O','[#1]OC=S','[#1]OO','[#1]OS'
     481 # HS - hydrogen supplemental not matching any basic H type - '[#1]'
     482 #
     483 sub _GetAtomTypeForHydrogen {
     484   my($This, $Atom) = @_;
     485   my($AtomType);
     486
     487   $AtomType = 'None';
     488
     489   ATOMTYPE: {
     490
     491     # H1 - hydrocarbon - '[#1][#6]','[#1][#1]'
     492     if ($This->_IsH1Hydrogen($Atom)) {
     493       $AtomType = 'H1';
     494       last ATOMTYPE;
     495     }
     496
     497     # H2 - alcohol - '[#1]O[CX4]','[#1]Oc','[#1]O[!(C,N,O,S)]','[#1][!C,N,O)]'
     498     if ($This->_IsH2Hydrogen($Atom)) {
     499       $AtomType = 'H2';
     500       last ATOMTYPE;
     501     }
     502
     503     # H3 - amine - '[#1][#7]','[#1]O[#7]'
     504     if ($This->_IsH3Hydrogen($Atom)) {
     505       $AtomType = 'H3';
     506       last ATOMTYPE;
     507     }
     508
     509     # H4 - acid - '[#1]OC=[#6]','[#1]OC=[#7]','[#1]OC=O','[#1]OC=S','[#1]OO','[#1]OS'
     510     if ($This->_IsH4Hydrogen($Atom)) {
     511       $AtomType = 'H4';
     512       last ATOMTYPE;
     513     }
     514
     515     $AtomType = 'HS';
     516   }
     517   return $AtomType;
     518 }
     519
     520 # Get SLogP atom type for atoms other than Carbon, Nitrogen, Oxygen, Phosporus,
     521 # Sulfur and Hydrogen...
     522 #
     523 # AtomTypeSymbol - Description - SMARTS
     524 # F - fluorine - '[#9-0]'
     525 # Cl - chlorine - '[#17-0]'
     526 # Br - bromine - '[#35-0]'
     527 # I - iodine - '[#53-0]'
     528 # Hal - ionic halogens - '[#9-*]','[#17-*]','[#35-*]',[#53-*]','[#53+*]'
     529 # Hal - all remaining s-block elements
     530 # Me1 - all remaining p-block elements
     531 # Me2 - all remaining d-block elements
     532 #
     533 sub _GetAtomTypeForOtherAtoms {
     534   my($This, $Atom) = @_;
     535   my($AtomType, $AtomSymbol);
     536
     537   $AtomType = 'None';
     538   $AtomSymbol = $Atom->GetAtomSymbol();
     539
     540   ATOMTYPE: {
     541
     542     # Halogens...
     543     if ($AtomSymbol =~ /^(F|Cl|Br|I)$/i) {
     544       $AtomType = $Atom->GetFormalCharge() ? 'Hal' : $AtomSymbol;
     545       last ATOMTYPE;
     546     }
     547
     548     # Me1 - all remaining p-block elements
     549     if ($This->_IsPBlockElement($Atom)) {
     550       $AtomType = 'Me1';
     551       last ATOMTYPE;
     552     }
     553
     554     # Me2 - all remaining d-block elements
     555     if ($This->_IsDBlockElement($Atom)) {
     556       $AtomType = 'Me2';
     557       last ATOMTYPE;
     558     }
     559
     560     # Hal - all remaining s-block elements
     561     if ($This->_IsSBlockElement($Atom)) {
     562       $AtomType = 'Hal';
     563       last ATOMTYPE;
     564     }
     565
     566     $AtomType = 'None';
     567     carp "Warning: ${ClassName}->_GetAtomTypeForOtherAtoms: SLogP atom type for $AtomSymbol cann't be assigned...";
     568   }
     569   return $AtomType;
     570 }
     571
     572 # Get SLogP atom type for Carbon with only sigma bonds...
     573 #
     574 sub _GetAtomTypeForCarbonWithOnlySigmaBonds {
     575   my($This, $Atom) = @_;
     576   my($AtomType);
     577
     578   $AtomType = 'None';
     579
     580   ATOMTYPE: {
     581
     582     # C1 - primary, secondary aliphatic - '[CH4]','[CH3]C','[CH2](C)C'
     583     if ($This->_IsC1Carbon($Atom)) {
     584       $AtomType = 'C1';
     585       last ATOMTYPE;
     586     }
     587
     588     # C2 - tertiary, quaternary aliphatic - '[CH](C)(C)C','[C](C)(C)(C)C'
     589     if ($This->_IsC2Carbon($Atom)) {
     590       $AtomType = 'C2';
     591       last ATOMTYPE;
     592     }
     593
     594     # C3 - primary, secondary heteroatom - '[CH3][(N,O,P,S,F,Cl,Br,I)]','[CH2X4](N,O,P,S,F,Cl,Br,I)]'
     595     if ($This->_IsC3Carbon($Atom)) {
     596       $AtomType = 'C3';
     597       last ATOMTYPE;
     598     }
     599
     600     # C4 - tertiary, quaternary heteroatom - '[CH1X4][(N,O,P,S,F,Cl,Br,I)]','[CH0X4][(N,O,P,S,F,Cl,Br,I)]'
     601     if ($This->_IsC4Carbon($Atom)) {
     602       $AtomType = 'C4';
     603       last ATOMTYPE;
     604     }
     605
     606     # C8 - primary aromatic carbon - '[CH3]c'
     607     if ($This->_IsC8Carbon($Atom)) {
     608       $AtomType = 'C8';
     609       last ATOMTYPE;
     610     }
     611
     612     # C9 - primary aromatic heteroatom - '[CH3][a#X]'
     613     if ($This->_IsC9Carbon($Atom)) {
     614       $AtomType = 'C9';
     615       last ATOMTYPE;
     616     }
     617
     618     # C10 - secondary aromatic - '[CH2X4]a'
     619     if ($This->_IsC10Carbon($Atom)) {
     620       $AtomType = 'C10';
     621       last ATOMTYPE;
     622     }
     623
     624     # C11 - tertiary aromatic - '[CHX4]a'
     625     if ($This->_IsC11Carbon($Atom)) {
     626       $AtomType = 'C11';
     627       last ATOMTYPE;
     628     }
     629
     630     # C12 - quaternary aromatic - '[CH0X4]a'
     631     if ($This->_IsC12Carbon($Atom)) {
     632       $AtomType = 'C12';
     633       last ATOMTYPE;
     634     }
     635
     636     # C27 - aliphatic heteroatom - '[CX4][!(C,N,O,P,S,F,Cl,Br,I)]'
     637     if ($This->_IsC27Carbon($Atom)) {
     638       $AtomType = 'C27';
     639       last ATOMTYPE;
     640     }
     641
     642     # CS - carbon supplemental not matching any basic C type - '[#6]'
     643     $AtomType = 'CS';
     644   }
     645
     646   return $AtomType;
     647 }
     648
     649 # Get SLogP atom type for Carbon with one pi bond...
     650 #
     651 sub _GetAtomTypeForCarbonWithOnePiBond {
     652   my($This, $Atom) = @_;
     653   my($AtomType);
     654
     655   $AtomType = 'None';
     656
     657   ATOMTYPE: {
     658
     659     # C5 - C = heteroatom - '[C]=[A#X]'
     660     if ($This->_IsC5Carbon($Atom)) {
     661       $AtomType = 'C5';
     662       last ATOMTYPE;
     663     }
     664
     665     # C6 - C = C aliphatic - '[CH2]=C','[CH1](=C)A','[CH0](=C)(A)A','[C](=C)=C'
     666     if ($This->_IsC6Carbon($Atom)) {
     667       $AtomType = 'C6';
     668       last ATOMTYPE;
     669     }
     670
     671     # C13 - aromatic heteroatom - '[cH0]-[!(C,N,O,S,F,Cl,Br,I)]'
     672     if ($This->_IsC13Carbon($Atom)) {
     673       $AtomType = 'C13';
     674       last ATOMTYPE;
     675     }
     676
     677     # C14 - aromatic halide - '[c][#9]'
     678     if ($This->_IsC14Carbon($Atom)) {
     679       $AtomType = 'C14';
     680       last ATOMTYPE;
     681     }
     682
     683     # C15 - aromatic halide - '[c][#17]'
     684     if ($This->_IsC15Carbon($Atom)) {
     685       $AtomType = 'C15';
     686       last ATOMTYPE;
     687     }
     688
     689     # C16 - aromatic halide - '[c][#35]'
     690     if ($This->_IsC16Carbon($Atom)) {
     691       $AtomType = 'C16';
     692       last ATOMTYPE;
     693     }
     694
     695     # C17 - aromatic halide - '[c][#53]'
     696     if ($This->_IsC17Carbon($Atom)) {
     697       $AtomType = 'C17';
     698       last ATOMTYPE;
     699     }
     700
     701     # C18 - aromatic - '[cH]'
     702     if ($This->_IsC18Carbon($Atom)) {
     703       $AtomType = 'C18';
     704       last ATOMTYPE;
     705     }
     706
     707     # C19 - aromatic bridgehead - '[c](:a)(:a):a'
     708     if ($This->_IsC19Carbon($Atom)) {
     709       $AtomType = 'C19';
     710       last ATOMTYPE;
     711     }
     712
     713     # C20 - quaternary aromatic - '[c](:a)(:a)-a'
     714     if ($This->_IsC20Carbon($Atom)) {
     715       $AtomType = 'C20';
     716       last ATOMTYPE;
     717     }
     718
     719     # C21 - quaternary aromatic - '[c](:a)(:a)-C'
     720     if ($This->_IsC21Carbon($Atom)) {
     721       $AtomType = 'C21';
     722       last ATOMTYPE;
     723     }
     724
     725     # C22 - quaternary aromatic - '[c](:a)(:a)-N'
     726     if ($This->_IsC22Carbon($Atom)) {
     727       $AtomType = 'C22';
     728       last ATOMTYPE;
     729     }
     730
     731     # C23 - quaternary aromatic - '[c](:a)(:a)-O'
     732     if ($This->_IsC23Carbon($Atom)) {
     733       $AtomType = 'C23';
     734       last ATOMTYPE;
     735     }
     736
     737     # C24 - quaternary aromatic - '[c](:a)(:a)-S'
     738     if ($This->_IsC24Carbon($Atom)) {
     739       $AtomType = 'C24';
     740       last ATOMTYPE;
     741     }
     742
     743     # C26 - C = C aromatic - '[C](=C)(a)A','[C](=C)(c)a','[CH](=C)a','[C]=c'
     744     if ($This->_IsC26Carbon($Atom)) {
     745       $AtomType = 'C26';
     746       last ATOMTYPE;
     747     }
     748
     749     # CS - carbon supplemental not matching any basic C type - '[#6]'
     750     $AtomType = 'CS';
     751   }
     752
     753   return $AtomType;
     754 }
     755
     756 # Get SLogP atom type for Carbon with two pi bonds...
     757 #
     758 sub _GetAtomTypeForCarbonWithTwoPiBonds {
     759   my($This, $Atom) = @_;
     760   my($AtomType);
     761
     762   $AtomType = 'None';
     763
     764   ATOMTYPE: {
     765
     766     # C6 - C = C aliphatic - '[CH2]=C','[CH1](=C)A','[CH0](=C)(A)A','[C](=C)=C'
     767     if ($This->_IsC6Carbon($Atom)) {
     768       $AtomType = 'C6';
     769       last ATOMTYPE;
     770     }
     771
     772     # C7 - acetylene, nitrile - '[CX2]#A'
     773     if ($This->_IsC7Carbon($Atom)) {
     774       $AtomType = 'C7';
     775       last ATOMTYPE;
     776     }
     777
     778     # CS - carbon supplemental not matching any basic C type - '[#6]'
     779     $AtomType = 'CS';
     780   }
     781
     782   return $AtomType;
     783 }
     784
     785 # C1 - primary, secondary aliphatic - '[CH4]','[CH3]C','[CH2](C)C'
     786 #
     787 sub _IsC1Carbon {
     788   my($This, $Atom) = @_;
     789
     790   return $Atom->DoesAtomNeighborhoodMatch('C.T4.!Ar', ['C.!Ar,H', 'C.!Ar,H', 'H', 'H'], ['-', '-', '-', '-']) ? 1 : 0;
     791 }
     792
     793 # C2 - tertiary, quaternary aliphatic - '[CH](C)(C)C','[C](C)(C)(C)C'
     794 #
     795 sub _IsC2Carbon {
     796   my($This, $Atom) = @_;
     797
     798   return $Atom->DoesAtomNeighborhoodMatch('C.T4.!Ar', ['C.!Ar', 'C.!Ar', 'C.!Ar', 'C.!Ar,H'], ['-', '-', '-', '-']) ? 1 : 0;
     799 }
     800
     801 # C3 - primary, secondary heteroatom - '[CH3][(N,O,P,S,F,Cl,Br,I)]','[CH2X4](N,O,P,S,F,Cl,Br,I)]'
     802 #
     803 sub _IsC3Carbon {
     804   my($This, $Atom) = @_;
     805
     806   return $Atom->DoesAtomNeighborhoodMatch('C.T4.!Ar', ['N.!Ar,O.!Ar,P.!Ar,S.!Ar,F,Cl,Br,I', 'N.!Ar,O.!Ar,P.!Ar,S.!Ar,F,Cl,Br,I,H', 'H', 'H'], ['-', '-', '-', '-']) ? 1 : 0;
     807 }
     808
     809 # C4 - tertiary, quaternary heteroatom - '[CH1X4][(N,O,P,S,F,Cl,Br,I)]','[CH0X4][(N,O,P,S,F,Cl,Br,I)]'
     810 #
     811 sub _IsC4Carbon {
     812   my($This, $Atom) = @_;
     813
     814   return $Atom->DoesAtomNeighborhoodMatch('C.T4.!Ar', ['N.!Ar,O.!Ar,P.!Ar,.!ArS,F,Cl,Br,I', 'N.!Ar,O.!Ar,P.!Ar,S.!Ar,F,Cl,Br,I', 'N.!Ar,O.!Ar,P.!Ar,S.!Ar,F,Cl,Br,I', 'N.!Ar,O.!Ar,P.!Ar,S.!Ar,F,Cl,Br,I,H'], ['-', '-', '-', '-']) ? 1 : 0;
     815 }
     816
     817 # C5 - C = heteroatom - '[C]=[A#X]'
     818 #
     819 sub _IsC5Carbon {
     820   my($This, $Atom) = @_;
     821
     822   return $Atom->DoesAtomNeighborhoodMatch('C.!Ar', ['!C.!Ar'], ['=']) ? 1 : 0;
     823 }
     824
     825 # C6 - C = C aliphatic - '[CH2]=C','[CH1](=C)A','[CH0](=C)(A)A','[C](=C)=C'
     826 #
     827 sub _IsC6Carbon {
     828   my($This, $Atom) = @_;
     829
     830   if ($Atom->DoesAtomNeighborhoodMatch('C.DB1.!Ar', ['C.!Ar', 'H,!H.!Ar', 'H,!H.!Ar'], ['=', '-', '-']) ||
     831      $Atom->DoesAtomNeighborhoodMatch('C.H0.DB2.!Ar', ['C.!Ar', 'C.!Ar'], ['=', '='])) {
     832     return 1;
     833   }
     834   return 0;
     835 }
     836
     837 # C7 - acetylene, nitrile - '[CX2]#A'
     838 #
     839 sub _IsC7Carbon {
     840   my($This, $Atom) = @_;
     841
     842   return $Atom->DoesAtomNeighborhoodMatch('C.T2.TB1', ['*', '*'], ['#', '-']) ? 1 : 0;
     843 }
     844
     845 # C8 - primary aromatic carbon - '[CH3]c'
     846 #
     847 sub _IsC8Carbon {
     848   my($This, $Atom) = @_;
     849
     850   return $Atom->DoesAtomNeighborhoodMatch('C.T4.!Ar', ['C.Ar', 'H', 'H', 'H'], ['-', '-', '-', '-']) ? 1 : 0;
     851 }
     852
     853 # C9 - primary aromatic heteroatom - '[CH3][a#X]'
     854 #
     855 sub _IsC9Carbon {
     856   my($This, $Atom) = @_;
     857
     858   return $Atom->DoesAtomNeighborhoodMatch('C.T4.!Ar', ['!C.Ar', 'H', 'H', 'H'], ['-', '-', '-', '-']) ? 1 : 0;
     859 }
     860
     861 # C10 - secondary aromatic - '[CH2X4]a'
     862 #
     863 sub _IsC10Carbon {
     864   my($This, $Atom) = @_;
     865
     866   return $Atom->DoesAtomNeighborhoodMatch('C.T4.!Ar', ['!H.Ar', '!H', 'H', 'H'], ['-', '-', '-', '-']) ? 1 : 0;
     867 }
     868
     869 # C11 - tertiary aromatic - '[CHX4]a'
     870 #
     871 sub _IsC11Carbon {
     872   my($This, $Atom) = @_;
     873
     874   return $Atom->DoesAtomNeighborhoodMatch('C.T4.!Ar', ['!H.Ar', '!H', '!H', 'H'], ['-', '-', '-', '-']) ? 1 : 0;
     875 }
     876
     877 # C12 - quaternary aromatic - '[CH0X4]a'
     878 #
     879 sub _IsC12Carbon {
     880   my($This, $Atom) = @_;
     881
     882   return $Atom->DoesAtomNeighborhoodMatch('C.T4.!Ar', ['!H.Ar', '!H', '!H', '!H'], ['-', '-', '-', '-']) ? 1 : 0;
     883 }
     884
     885 # C13 - aromatic heteroatom - '[cH0]-[!(C,N,O,S,F,Cl,Br,I)]' or matching [ Table 1 annotations, Ref 89 ]
     886 # '[cH0]-[B,Si,P,As,Se,Sn,Hg]'
     887 #
     888 #
     889 sub _IsC13Carbon {
     890   my($This, $Atom) = @_;
     891
     892   return $Atom->DoesAtomNeighborhoodMatch('C.Ar.H0', ['B,Si,P,As,Se,Sn,Hg'], ['-']) ? 1 : 0;
     893 }
     894
     895 # C14 - aromatic halide - '[c][#9]'
     896 #
     897 sub _IsC14Carbon {
     898   my($This, $Atom) = @_;
     899
     900   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['F'], ['-']) ? 1 : 0;
     901 }
     902
     903 # C15 - aromatic halide - '[c][#17]'
     904 #
     905 sub _IsC15Carbon {
     906   my($This, $Atom) = @_;
     907
     908   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['Cl'], ['-']) ? 1 : 0;
     909 }
     910
     911 # C16 - aromatic halide - '[c][#35]'
     912 #
     913 sub _IsC16Carbon {
     914   my($This, $Atom) = @_;
     915
     916   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['Br'], ['-']) ? 1 : 0;
     917 }
     918
     919 # C17 - aromatic halide - '[c][#53]'
     920 #
     921 sub _IsC17Carbon {
     922   my($This, $Atom) = @_;
     923
     924   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['I'], ['-']) ? 1 : 0;
     925 }
     926
     927 # C18 - aromatic - '[cH]'
     928 #
     929 sub _IsC18Carbon {
     930   my($This, $Atom) = @_;
     931
     932   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['H'], ['-']) ? 1 : 0;
     933 }
     934
     935 # C19 - aromatic bridgehead - '[c](:a)(:a):a'
     936 #
     937 sub _IsC19Carbon {
     938   my($This, $Atom) = @_;
     939
     940   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['!H.Ar', '!H.Ar', '!H.Ar'], [':', ':', ':']) ? 1 : 0;
     941 }
     942
     943 # C20 - quaternary aromatic - '[c](:a)(:a)-a'
     944 #
     945 sub _IsC20Carbon {
     946   my($This, $Atom) = @_;
     947
     948   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['!H.Ar', '!H.Ar', '!H.Ar'], [':', ':', '-.!:']) ? 1 : 0;
     949 }
     950
     951 # C21 - quaternary aromatic - '[c](:a)(:a)-C'
     952 #
     953 sub _IsC21Carbon {
     954   my($This, $Atom) = @_;
     955
     956   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['!H.Ar', '!H.Ar', 'C.!Ar'], [':', ':', '-']) ? 1 : 0;
     957 }
     958
     959 # C22 - quaternary aromatic - '[c](:a)(:a)-N'
     960 #
     961 sub _IsC22Carbon {
     962   my($This, $Atom) = @_;
     963
     964   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['!H.Ar', '!H.Ar', 'N.!Ar'], [':', ':', '-']) ? 1 : 0;
     965 }
     966
     967 # C23 - quaternary aromatic - '[c](:a)(:a)-O'
     968 #
     969 sub _IsC23Carbon {
     970   my($This, $Atom) = @_;
     971
     972   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['!H.Ar', '!H.Ar', 'O.!Ar'], [':', ':', '-']) ? 1 : 0;
     973 }
     974
     975 # C24 - quaternary aromatic - '[c](:a)(:a)-S'
     976 #
     977 sub _IsC24Carbon {
     978   my($This, $Atom) = @_;
     979
     980   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['!H.Ar', '!H.Ar', 'S.!Ar'], [':', ':', '-']) ? 1 : 0;
     981 }
     982
     983 # C25 - quaternary aromatic - '[c](:a)(:a)=C','[c](:a)(:a)=N','[c](:a)(:a)=O'
     984 #
     985 sub _IsC25Carbon {
     986   my($This, $Atom) = @_;
     987
     988   return $Atom->DoesAtomNeighborhoodMatch('C.Ar', ['!H.Ar', '!H.Ar', 'C.!Ar,N.!Ar,O.!Ar'], [':', ':', '=']) ? 1 : 0;
     989 }
     990
     991 # C26 - C = C aromatic - '[C](=C)(a)A','[C](=C)(c)a','[CH](=C)a','[C]=c'
     992 #
     993 sub _IsC26Carbon {
     994   my($This, $Atom) = @_;
     995
     996   if ($Atom->DoesAtomNeighborhoodMatch('C.!Ar.T3', ['C.!Ar', '!H.Ar', '!H.!Ar'], ['=', '-', '-']) ||
     997      $Atom->DoesAtomNeighborhoodMatch('C.!Ar.T3', ['C.!Ar', 'C.Ar', '!H.Ar'], ['=', '-', '-']) ||
     998      $Atom->DoesAtomNeighborhoodMatch('C.!Ar.T3.H1', ['C.!Ar', '!H.Ar', 'H'], ['=', '-', '-']) ||
     999      $Atom->DoesAtomNeighborhoodMatch('C.!Ar', ['C.Ar'], ['='])) {
    1000     return 1;
    1001   }
    1002   return 0;
    1003 }
    1004
    1005 # C27 - aliphatic heteroatom - '[CX4][!(C,N,O,P,S,F,Cl,Br,I)]'
    1006 #
    1007 # Notes:
    1008 #   . X4 implies four neighbors including Hydrogen
    1009 #   . For C27 match, at least one of the neighbors must be a hetro atom other
    1010 #     than C,N,O,P,S,F,Cl,Br,I. In other words, it's primary, secondary, tertiary,
    1011 #     or queaternary aliphatic heteroatom not in (C,N,O,P,S,F,Cl,Br,I) group defined
    1012 #     using C3 and C4.
    1013 #
    1014 sub _IsC27Carbon {
    1015   my($This, $Atom) = @_;
    1016   my($AtomNeighbor, $AtomNeighborSymbol);
    1017
    1018   if (!$Atom->DoesAtomNeighborhoodMatch('C.T4.!Ar')) {
    1019     return 0;
    1020   }
    1021
    1022   ATOMNEIGHBOR: for $AtomNeighbor ($Atom->GetNonHydrogenAtomNeighbors()) {
    1023     $AtomNeighborSymbol = $AtomNeighbor->GetAtomSymbol();
    1024     # Is it a heteroatom?
    1025     if ($AtomNeighborSymbol =~ /^(C|N|O|P|S|F|Cl|Br|I|H)$/) {
    1026       next ATOMNEIGHBOR;
    1027     }
    1028     # Is it aromatic?
    1029     if ($AtomNeighbor->IsAromatic()) {
    1030       next ATOMNEIGHBOR;
    1031     }
    1032     return 1;
    1033   }
    1034   return 0;
    1035 }
    1036
    1037 # CS - carbon supplemental not matching any basic C type - '[#6]'
    1038 #
    1039 sub _IsCSCarbon {
    1040   my($This, $Atom) = @_;
    1041
    1042   return $Atom->IsCarbon() ? 1 : 0;
    1043 }
    1044
    1045 # Get SLogP atom type for Nitrogen with only sigma bonds...
    1046 #
    1047 sub _GetAtomTypeForNitrogenWithOnlySigmaBonds {
    1048   my($This, $Atom) = @_;
    1049   my($AtomType);
    1050
    1051   $AtomType = 'None';
    1052
    1053   ATOMTYPE: {
    1054
    1055     # N1 - primary amine - '[NH2+0]A'
    1056     if ($This->_IsN1Nitrogen($Atom)) {
    1057       $AtomType = 'N1';
    1058       last ATOMTYPE;
    1059     }
    1060
    1061     # N2 - secondary amine - '[NH+0](A)A'
    1062     if ($This->_IsN2Nitrogen($Atom)) {
    1063       $AtomType = 'N2';
    1064       last ATOMTYPE;
    1065     }
    1066
    1067     # N3 - primary aromatic amine - '[NH2+0]a'
    1068     if ($This->_IsN3Nitrogen($Atom)) {
    1069       $AtomType = 'N3';
    1070       last ATOMTYPE;
    1071     }
    1072
    1073     # N4 - secondary aromatic amine - '[NH+0](A)a','[NH+0](a)a'
    1074     if ($This->_IsN4Nitrogen($Atom)) {
    1075       $AtomType = 'N4';
    1076       last ATOMTYPE;
    1077     }
    1078
    1079     # N7 - tertiary amine - '[N+0](A)(A)A'
    1080     if ($This->_IsN7Nitrogen($Atom)) {
    1081       $AtomType = 'N7';
    1082       last ATOMTYPE;
    1083     }
    1084
    1085     # N8 - tertiary aromatic amine - '[N+0](a)(A)A','[N+0](a)(a)A','[N+0](a)(a)a'
    1086     if ($This->_IsN8Nitrogen($Atom)) {
    1087       $AtomType = 'N8';
    1088       last ATOMTYPE;
    1089     }
    1090
    1091     # N10 - protonated amine - '[NH3+*]','[NH2+*]','[NH+*]'
    1092     if ($This->_IsN10Nitrogen($Atom)) {
    1093       $AtomType = 'N10';
    1094       last ATOMTYPE;
    1095     }
    1096
    1097     # N11 - unprotonated aromatic - '[n+0]'
    1098     if ($This->_IsN11Nitrogen($Atom)) {
    1099       $AtomType = 'N11';
    1100       last ATOMTYPE;
    1101     }
    1102
    1103     # N12 - protonated aromatic - '[n+*]'
    1104     if ($This->_IsN12Nitrogen($Atom)) {
    1105       $AtomType = 'N12';
    1106       last ATOMTYPE;
    1107     }
    1108
    1109     # N13 - quaternary amine - '[NH0+*](A)(A)(A)A','[NH0+*](=A)(A)A','[NH0+*](=A)(A)a','[NH0+*](=[#6])=[#7]'
    1110     if ($This->_IsN13Nitrogen($Atom)) {
    1111       $AtomType = 'N13';
    1112       last ATOMTYPE;
    1113     }
    1114
    1115     # N14 - other ionized nitrogen - '[N+*]#A','[N-*]','[N+*](=[N-*])=N'
    1116     if ($This->_IsN14Nitrogen($Atom)) {
    1117       $AtomType = 'N14';
    1118       last ATOMTYPE;
    1119     }
    1120
    1121     $AtomType = 'NS';
    1122   }
    1123
    1124   return $AtomType;
    1125 }
    1126
    1127 # Get SLogP atom type for Nitrogen with one pi bond...
    1128 #
    1129 sub _GetAtomTypeForNitrogenWithOnePiBond {
    1130   my($This, $Atom) = @_;
    1131   my($AtomType);
    1132
    1133   $AtomType = 'None';
    1134
    1135   ATOMTYPE: {
    1136
    1137     # N5 - imine - '[NH+0]=A','[NH+0]=a'
    1138     if ($This->_IsN5Nitrogen($Atom)) {
    1139       $AtomType = 'N5';
    1140       last ATOMTYPE;
    1141     }
    1142
    1143     # N6 - substituted imine - '[N+0](=A)A','[N+0](=A)a','[N+0](=a)A','[N+0](=a)a'
    1144     if ($This->_IsN6Nitrogen($Atom)) {
    1145       $AtomType = 'N6';
    1146       last ATOMTYPE;
    1147     }
    1148
    1149     # N11 - unprotonated aromatic - '[n+0]'
    1150     if ($This->_IsN11Nitrogen($Atom)) {
    1151       $AtomType = 'N11';
    1152       last ATOMTYPE;
    1153     }
    1154
    1155     # N12 - protonated aromatic - '[n+*]'
    1156     if ($This->_IsN12Nitrogen($Atom)) {
    1157       $AtomType = 'N12';
    1158       last ATOMTYPE;
    1159     }
    1160
    1161     # N13 - quaternary amine - '[NH0+*](A)(A)(A)A','[NH0+*](=A)(A)A','[NH0+*](=A)(A)a','[NH0+*](=[#6])=[#7]'
    1162     if ($This->_IsN13Nitrogen($Atom)) {
    1163       $AtomType = 'N13';
    1164       last ATOMTYPE;
    1165     }
    1166
    1167     # N14 - other ionized nitrogen - '[N+*]#A','[N-*]','[N+*](=[N-*])=N'
    1168     if ($This->_IsN14Nitrogen($Atom)) {
    1169       $AtomType = 'N14';
    1170       last ATOMTYPE;
    1171     }
    1172
    1173     $AtomType = 'NS';
    1174   }
    1175
    1176   return $AtomType;
    1177 }
    1178
    1179 # Get SLogP atom type for Nitrogen with two pi bonds...
    1180 #
    1181 sub _GetAtomTypeForNitrogenWithTwoPiBonds {
    1182   my($This, $Atom) = @_;
    1183   my($AtomType);
    1184
    1185   $AtomType = 'None';
    1186
    1187   ATOMTYPE: {
    1188
    1189     # N9 - nitrile - '[N+0]#A'
    1190     if ($This->_IsN9Nitrogen($Atom)) {
    1191       $AtomType = 'N9';
    1192       last ATOMTYPE;
    1193     }
    1194
    1195     # N13 - quaternary amine - '[NH0+*](A)(A)(A)A','[NH0+*](=A)(A)A','[NH0+*](=A)(A)a','[NH0+*](=[#6])=[#7]'
    1196     if ($This->_IsN13Nitrogen($Atom)) {
    1197       $AtomType = 'N13';
    1198       last ATOMTYPE;
    1199     }
    1200
    1201     # N14 - other ionized nitrogen - '[N+*]#A','[N-*]','[N+*](=[N-*])=N'
    1202     if ($This->_IsN14Nitrogen($Atom)) {
    1203       $AtomType = 'N14';
    1204       last ATOMTYPE;
    1205     }
    1206
    1207     $AtomType = 'NS';
    1208   }
    1209
    1210   return $AtomType;
    1211 }
    1212
    1213 # N1 - primary amine - '[NH2+0]A'
    1214 #
    1215 sub _IsN1Nitrogen {
    1216   my($This, $Atom) = @_;
    1217
    1218   return $Atom->DoesAtomNeighborhoodMatch('N.!Ar.T3.FC0', ['!H.!Ar', 'H', 'H'], ['-', '-', '-']) ? 1 : 0;
    1219 }
    1220
    1221 # N2 - secondary amine - '[NH+0](A)A'
    1222 #
    1223 sub _IsN2Nitrogen {
    1224   my($This, $Atom) = @_;
    1225
    1226   return $Atom->DoesAtomNeighborhoodMatch('N.!Ar.T3.FC0', ['!H.!Ar', '!H.!Ar', 'H'], ['-', '-', '-']) ? 1 : 0;
    1227 }
    1228
    1229 # N3 - primary aromatic amine - '[NH2+0]a'
    1230 #
    1231 sub _IsN3Nitrogen {
    1232   my($This, $Atom) = @_;
    1233
    1234   return $Atom->DoesAtomNeighborhoodMatch('N.!Ar.T3.FC0', ['!H.Ar', 'H', 'H'], ['-', '-', '-']) ? 1 : 0;
    1235 }
    1236
    1237 # N4 - secondary aromatic amine - '[NH+0](A)a','[NH+0](a)a'
    1238 #
    1239 sub _IsN4Nitrogen {
    1240   my($This, $Atom) = @_;
    1241
    1242   return $Atom->DoesAtomNeighborhoodMatch('N.!Ar.T3.FC0', ['!H.Ar', '!H', 'H'], ['-', '-', '-']) ? 1 : 0;
    1243 }
    1244
    1245 # N5 - imine - '[NH+0]=A','[NH+0]=a'
    1246 #
    1247 sub _IsN5Nitrogen {
    1248   my($This, $Atom) = @_;
    1249
    1250   return $Atom->DoesAtomNeighborhoodMatch('N.!Ar.T2.FC0', ['!H', 'H'], ['=', '-']) ? 1 : 0;
    1251 }
    1252
    1253 # N6 - substituted imine - '[N+0](=A)A','[N+0](=A)a','[N+0](=a)A','[N+0](=a)a'
    1254 #
    1255 sub _IsN6Nitrogen {
    1256   my($This, $Atom) = @_;
    1257
    1258   return $Atom->DoesAtomNeighborhoodMatch('N.!Ar.T2.FC0', ['!H', '!H'], ['=', '-']) ? 1 : 0;
    1259 }
    1260
    1261 # N7 - tertiary amine - '[N+0](A)(A)A'
    1262 #
    1263 sub _IsN7Nitrogen {
    1264   my($This, $Atom) = @_;
    1265
    1266   return $Atom->DoesAtomNeighborhoodMatch('N.!Ar.T3.FC0', ['!H.!Ar', '!H.!Ar', '!H.!Ar'], ['-', '-', '-']) ? 1 : 0;
    1267 }
    1268
    1269 # N8 - tertiary aromatic amine - '[N+0](a)(A)A','[N+0](a)(a)A','[N+0](a)(a)a'
    1270 #
    1271 sub _IsN8Nitrogen {
    1272   my($This, $Atom) = @_;
    1273
    1274   return $Atom->DoesAtomNeighborhoodMatch('N.!Ar.T3.FC0', ['!H.Ar', '!H', '!H'], ['-', '-', '-']) ? 1 : 0;
    1275 }
    1276
    1277 # N9 - nitrile - '[N+0]#A'
    1278 #
    1279 sub _IsN9Nitrogen {
    1280   my($This, $Atom) = @_;
    1281
    1282   return $Atom->DoesAtomNeighborhoodMatch('N.!Ar.T1.FC0', ['!H.!Ar'], ['#']) ? 1 : 0;
    1283 }
    1284
    1285 # N10 - protonated amine - '[NH3+*]','[NH2+*]','[NH+*]'
    1286 #
    1287 sub _IsN10Nitrogen {
    1288   my($This, $Atom) = @_;
    1289
    1290   return $Atom->DoesAtomNeighborhoodMatch('N.!Ar.FC+*.H3,N.!Ar.FC+*.H2,N.!Ar.FC+*.H1') ? 1 : 0;
    1291 }
    1292
    1293 # N11 - unprotonated aromatic - '[n+0]'
    1294 #
    1295 sub _IsN11Nitrogen {
    1296   my($This, $Atom) = @_;
    1297
    1298   return $Atom->DoesAtomNeighborhoodMatch('N.Ar.FC0.H0') ? 1 : 0;
    1299 }
    1300
    1301 # N12 - protonated aromatic - '[n+*]'
    1302 #
    1303 sub _IsN12Nitrogen {
    1304   my($This, $Atom) = @_;
    1305
    1306   return $Atom->DoesAtomNeighborhoodMatch('N.Ar.FC+*.!H0') ? 1 : 0;
    1307 }
    1308
    1309 # N13 - quaternary amine - '[NH0+*](A)(A)(A)A','[NH0+*](=A)(A)A','[NH0+*](=A)(A)a','[NH0+*](=[#6])=[#7]'
    1310 #
    1311 sub _IsN13Nitrogen {
    1312   my($This, $Atom) = @_;
    1313
    1314   if ($Atom->DoesAtomNeighborhoodMatch('N.!Ar.FC+*.T4.H0', ['!H.!Ar', '!H.!Ar', '!H.!Ar', '!H.!Ar'], ['-', '-', '-', '-']) ||
    1315      $Atom->DoesAtomNeighborhoodMatch('N.!Ar.FC+*.T3.H0', ['!H.!Ar', '!H.!Ar', '!H.!Ar'], ['=', '-', '-']) ||
    1316      $Atom->DoesAtomNeighborhoodMatch('N.!Ar.FC+*.T3.H0', ['!H.!Ar', '!H.!Ar', '!H.Ar'], ['=', '-', '-']) ||
    1317      $Atom->DoesAtomNeighborhoodMatch('N.!Ar.FC+*.T2.H0', ['C.!Ar', 'N.!Ar'], ['=', '=']) ) {
    1318     return 1;
    1319   }
    1320   return 0;
    1321 }
    1322
    1323 # N14 - other ionized nitrogen - '[N+*]#A','[N-*]','[N+*](=[N-*])=N'
    1324 #
    1325 sub _IsN14Nitrogen {
    1326   my($This, $Atom) = @_;
    1327
    1328   if ($Atom->DoesAtomNeighborhoodMatch('N.!Ar.FC+*', ['!H.!Ar'], ['#']) ||
    1329      $Atom->DoesAtomNeighborhoodMatch('N.!Ar.FC-*') ||
    1330      $Atom->DoesAtomNeighborhoodMatch('N.!Ar.FC+*', ['N.!Ar.FC-*', 'N.!Ar'], ['=', '=']) ) {
    1331     return 1;
    1332   }
    1333   return 0;
    1334 }
    1335
    1336 # NS - nitrogen supplemental not matching any basic N type - '[#7]'
    1337 #
    1338 sub _IsNSNitrogen {
    1339   my($This, $Atom) = @_;
    1340
    1341   return $Atom->IsNitrogen() ? 1 : 0;
    1342 }
    1343
    1344 # Get SLogP atom type for Oxygen with only sigma bonds...
    1345 #
    1346 sub _GetAtomTypeForOxygenWithOnlySigmaBonds {
    1347   my($This, $Atom) = @_;
    1348   my($AtomType);
    1349
    1350   $AtomType = 'None';
    1351
    1352   ATOMTYPE: {
    1353
    1354     # O1 - aromatic - '[o]'
    1355     if ($This->_IsO1Oxygen($Atom)) {
    1356       $AtomType = 'O1';
    1357       last ATOMTYPE;
    1358     }
    1359
    1360     # O2 - alcohol - '[OH]','[OH2]'
    1361     if ($This->_IsO2Oxygen($Atom)) {
    1362       $AtomType = 'O2';
    1363       last ATOMTYPE;
    1364     }
    1365
    1366     # O3 - aliphatic ether - '[O](C)C','[O](C)[A#X]','[O]([A#X])[A#X]'
    1367     if ($This->_IsO3Oxygen($Atom)) {
    1368       $AtomType = 'O3';
    1369       last ATOMTYPE;
    1370     }
    1371
    1372     # O4 - aromatic ether - '[O](A)a','[O](a)a'
    1373     if ($This->_IsO4Oxygen($Atom)) {
    1374       $AtomType = 'O4';
    1375       last ATOMTYPE;
    1376     }
    1377
    1378     # O5 - oxide - '[O]=[#8]','[O]=[#7]','[OX1-*][#7]'
    1379     if ($This->_IsO5Oxygen($Atom)) {
    1380       $AtomType = 'O5';
    1381       last ATOMTYPE;
    1382     }
    1383
    1384     # O6 - oxide - '[OX1-*][#16]'
    1385     if ($This->_IsO6Oxygen($Atom)) {
    1386       $AtomType = 'O6';
    1387       last ATOMTYPE;
    1388     }
    1389
    1390     # O7 - oxide - '[OX1-*][!(N,S)]'
    1391     if ($This->_IsO7Oxygen($Atom)) {
    1392       $AtomType = 'O7';
    1393       last ATOMTYPE;
    1394     }
    1395
    1396     # O12 - acid - '[O-1]C(=O)'
    1397     if ($This->_IsO12Oxygen($Atom)) {
    1398       $AtomType = 'O12';
    1399       last ATOMTYPE;
    1400     }
    1401
    1402     $AtomType = 'OS';
    1403   }
    1404
    1405   return $AtomType;
    1406 }
    1407
    1408 # Get SLogP atom type for Oxygen with only sigma bonds...
    1409 #
    1410 sub _GetAtomTypeForOxygenWithOnePiBond {
    1411   my($This, $Atom) = @_;
    1412   my($AtomType);
    1413
    1414   $AtomType = 'None';
    1415
    1416   ATOMTYPE: {
    1417
    1418     # O1 - aromatic - '[o]'
    1419     if ($This->_IsO1Oxygen($Atom)) {
    1420       $AtomType = 'O1';
    1421       last ATOMTYPE;
    1422     }
    1423
    1424     # O5 - oxide - '[O]=[#8]','[O]=[#7]','[OX1-*][#7]'
    1425     if ($This->_IsO5Oxygen($Atom)) {
    1426       $AtomType = 'O5';
    1427       last ATOMTYPE;
    1428     }
    1429
    1430     # O8 - aromatic carbonyl - '[O]=c'
    1431     if ($This->_IsO8Oxygen($Atom)) {
    1432       $AtomType = 'O8';
    1433       last ATOMTYPE;
    1434     }
    1435
    1436     # O9 - carbonyl aliphatic - '[O]=[CH]C','[O]=C(C)C','[O]=C(C)[A#X]','[O]=[CH]N','[O]=[CH]O','[O]=[CH2]','[O]=[CX2]=O'
    1437     if ($This->_IsO9Oxygen($Atom)) {
    1438       $AtomType = 'O9';
    1439       last ATOMTYPE;
    1440     }
    1441
    1442     # O10 - carbonyl aromatic - '[O]=[CH]c','[O]=C(C)c','[O]=C(c)c','[O]=C(c)[a#X]','[O]=C(c)[A#X]','[O]=C(C)[a#X]'
    1443     if ($This->_IsO10Oxygen($Atom)) {
    1444       $AtomType = 'O10';
    1445       last ATOMTYPE;
    1446     }
    1447
    1448     # O11 - carbonyl heteroatom - '[O]=C([A#X])[A#X]','[O]=C([A#X])[a#X]','[O]=C([a#X])[a#X]'
    1449     if ($This->_IsO11Oxygen($Atom)) {
    1450       $AtomType = 'O11';
    1451       last ATOMTYPE;
    1452     }
    1453
    1454     $AtomType = 'OS';
    1455   }
    1456
    1457   return $AtomType;
    1458 }
    1459
    1460 # O1 - aromatic - '[o]'
    1461 #
    1462 sub _IsO1Oxygen {
    1463   my($This, $Atom) = @_;
    1464
    1465   return $Atom->DoesAtomNeighborhoodMatch('O.Ar') ? 1 : 0;
    1466 }
    1467
    1468 # O2 - alcohol - '[OH]','[OH2]'
    1469 #
    1470 sub _IsO2Oxygen {
    1471   my($This, $Atom) = @_;
    1472
    1473   if ($Atom->DoesAtomNeighborhoodMatch('O.TSB2.!Ar', ['C', 'H'], ['-', '-']) ||
    1474      $Atom->DoesAtomNeighborhoodMatch('O.TSB2.!Ar', ['H', 'H'])) {
    1475     return 1;
    1476   }
    1477   return 0;
    1478 }
    1479
    1480 # O3 - aliphatic ether - '[O](C)C','[O](C)[A#X]','[O]([A#X])[A#X]'
    1481 #
    1482 sub _IsO3Oxygen {
    1483   my($This, $Atom) = @_;
    1484
    1485   return $Atom->DoesAtomNeighborhoodMatch('O.!Ar.X2', ['!H.!Ar', '!H.!Ar'], ['-', '-']) ? 1 : 0;
    1486 }
    1487
    1488 # O4 - aromatic ether - '[O](A)a','[O](a)a'
    1489 #
    1490 sub _IsO4Oxygen {
    1491   my($This, $Atom) = @_;
    1492
    1493   return $Atom->DoesAtomNeighborhoodMatch('O.X2.!Ar', ['!H', '!H.Ar'], ['-', '-']) ? 1 : 0;
    1494 }
    1495
    1496 # O5 - oxide - '[O]=[#8]','[O]=[#7]','[OX1-*][#7]'
    1497 #
    1498 sub _IsO5Oxygen {
    1499   my($This, $Atom) = @_;
    1500
    1501   if ($Atom->DoesAtomNeighborhoodMatch('O.DB1.FC0', ['N,O'], ['=']) ||
    1502      $Atom->DoesAtomNeighborhoodMatch('O.T1.FC-*', ['N'], ['-']))  {
    1503     return 1;
    1504   }
    1505   return 0;
    1506 }
    1507
    1508 # O6 - oxide - '[OX1-*][#16]'
    1509 #
    1510 sub _IsO6Oxygen {
    1511   my($This, $Atom) = @_;
    1512
    1513   return $Atom->DoesAtomNeighborhoodMatch('O.T1.FC-*', ['S'], ['-']) ? 1 : 0;
    1514 }
    1515
    1516 # O7 - oxide - '[OX1-*][!(N,S)]'  or matching [ Table 1 annotations, Ref 89 ]
    1517 #  '[OX1-*](P,As,Tc,I)
    1518 #
    1519 sub _IsO7Oxygen {
    1520   my($This, $Atom) = @_;
    1521
    1522   return $Atom->DoesAtomNeighborhoodMatch('O.T1.FC-*', ['P,As,Tc,I'], ['-']) ? 1 : 0;
    1523 }
    1524
    1525 # O8 - aromatic carbonyl - '[O]=c'
    1526 #
    1527 sub _IsO8Oxygen {
    1528   my($This, $Atom) = @_;
    1529
    1530   return $Atom->DoesAtomNeighborhoodMatch('O.DB1.!Ar', ['C.Ar'], ['=']) ? 1 : 0;
    1531 }
    1532
    1533 # O9 - carbonyl aliphatic - '[O]=[CH]C','[O]=C(C)C','[O]=C(C)[A#X]','[O]=[CH]N','[O]=[CH]O','[O]=[CH2]','[O]=[CX2]=O'
    1534 #
    1535 sub _IsO9Oxygen {
    1536   my($This, $Atom) = @_;
    1537   my($AtomNeighbor);
    1538
    1539   # Is it a doubly bonded non-aromatic Oxygen?
    1540   if (!$Atom->DoesAtomNeighborhoodMatch('O.DB1.!Ar.FC0')) {
    1541     return 0;
    1542   }
    1543
    1544   # Is it attached to appopriate Carbon?
    1545   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C')) {
    1546     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3', ['O.!Ar', 'C.!Ar', 'H,C.!Ar'], ['=', '-', '-'])  ||
    1547        $AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3.H0', ['O.!Ar', '!C.!Ar', 'C.!Ar'], ['=', '-', '-'])  ||
    1548        $AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3', ['O.!Ar', 'N.!Ar,O.!Ar', 'H'], ['=', '-', '-']) ||
    1549        $AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3', ['O.!Ar', 'H', 'H'], ['=', '-', '-']) ||
    1550        $AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB2.T2', ['O.!Ar', 'O.!Ar'], ['=', '='])) {
    1551       return 1;
    1552     }
    1553   }
    1554   return 0;
    1555 }
    1556
    1557 # O10 - carbonyl aromatic - '[O]=[CH]c','[O]=C(C)c','[O]=C(c)c','[O]=C(c)[a#X]','[O]=C(c)[A#X]','[O]=C(C)[a#X]'
    1558 #
    1559 sub _IsO10Oxygen {
    1560   my($This, $Atom) = @_;
    1561   my($AtomNeighbor);
    1562
    1563   # Is it a doubly bonded non-aromatic Oxygen?
    1564   if (!$Atom->DoesAtomNeighborhoodMatch('O.DB1.!Ar.FC0')) {
    1565     return 0;
    1566   }
    1567
    1568   # Is it attached to appopriate Carbon?
    1569   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C')) {
    1570     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3', ['O.!Ar', 'C.Ar', 'H'], ['=', '-', '-'])  ||
    1571        $AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3', ['O.!Ar', 'C.Ar', 'C.!Ar'], ['=', '-', '-'])  ||
    1572        $AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3', ['O.!Ar', 'C.Ar', 'C.Ar'], ['=', '-', '-'])  ||
    1573        $AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3.H0', ['O.!Ar', '!C', 'C.Ar'], ['=', '-', '-'])  ||
    1574        $AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3.H0', ['O.!Ar', '!C.Ar', 'C.!Ar'], ['=', '-', '-'])) {
    1575       return 1;
    1576     }
    1577   }
    1578   return 0;
    1579 }
    1580
    1581 # O11 - carbonyl heteroatom - '[O]=C([A#X])[A#X]','[O]=C([A#X])[a#X]','[O]=C([a#X])[a#X]'
    1582 #
    1583 sub _IsO11Oxygen {
    1584   my($This, $Atom) = @_;
    1585   my($AtomNeighbor);
    1586
    1587   # Is it a doubly bonded non-aromatic Oxygen?
    1588   if (!$Atom->DoesAtomNeighborhoodMatch('O.DB1.!Ar.FC0')) {
    1589     return 0;
    1590   }
    1591
    1592   # Is it attached to appopriate Carbon?
    1593   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C')) {
    1594     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3.H0', ['O.!Ar', '!C', '!C'], ['=', '-', '-'])) {
    1595       return 1;
    1596     }
    1597   }
    1598   return 0;
    1599 }
    1600
    1601 # O12 - acid - '[O-1]C(=O)'
    1602 #
    1603 sub _IsO12Oxygen {
    1604   my($This, $Atom) = @_;
    1605   my($AtomNeighbor);
    1606
    1607   # Is it a acid Oxygen?
    1608   if (!$Atom->DoesAtomNeighborhoodMatch('O.DB0.!Ar.FC-1')) {
    1609     return 0;
    1610   }
    1611
    1612   # Is it attached to appopriate Carbon?
    1613   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C')) {
    1614     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.DB1.T3', ['O.FC0', 'O.FC-1'], ['=', '-'])) {
    1615       return 1;
    1616     }
    1617   }
    1618   return 0;
    1619 }
    1620
    1621 # OS - oxygen supplemental not matching any basic O type - '[#8]'
    1622 #
    1623 sub _IsOSOxygen {
    1624   my($This, $Atom) = @_;
    1625
    1626   return $Atom->IsOxygen() ? 1 : 0;
    1627 }
    1628
    1629 # S1 - aliphatic - '[S-0]'
    1630 #
    1631 sub _IsS1Sulfur {
    1632   my($This, $Atom) = @_;
    1633
    1634   return $Atom->DoesAtomNeighborhoodMatch('S.!Ar.FC0') ? 1 : 0;
    1635 }
    1636
    1637 # S2 - ionic sulfur - '[S-*]','[S+*]'
    1638 #
    1639 sub _IsS2Sulfur {
    1640   my($This, $Atom) = @_;
    1641
    1642   return $Atom->DoesAtomNeighborhoodMatch('S.!Ar.FC-*,S.!Ar.FC+*') ? 1 : 0;
    1643 }
    1644
    1645 # S3 - aromatic - '[s]'
    1646 #
    1647 sub _IsS3Sulfur {
    1648   my($This, $Atom) = @_;
    1649
    1650   return $Atom->DoesAtomNeighborhoodMatch('S.Ar') ? 1 : 0;
    1651 }
    1652
    1653 # Hal - all remaining s-block elements
    1654 #
    1655 sub _IsSBlockElement {
    1656   my($This, $Atom) = @_;
    1657   my($GroupNumber);
    1658
    1659   $GroupNumber = $Atom->GetGroupNumber();
    1660
    1661   return ($GroupNumber >= 1 && $GroupNumber <= 2) ? 1 : 0;
    1662 }
    1663
    1664 # Me1 - all remaining p-block elements
    1665 #
    1666 sub _IsPBlockElement {
    1667   my($This, $Atom) = @_;
    1668   my($GroupNumber);
    1669
    1670   $GroupNumber = $Atom->GetGroupNumber();
    1671
    1672   return ($GroupNumber >= 13 && $GroupNumber <= 18) ? 1 : 0;
    1673 }
    1674
    1675 # Me2 - all remaining d-block elements
    1676 #
    1677 sub _IsDBlockElement {
    1678   my($This, $Atom) = @_;
    1679   my($GroupNumber);
    1680
    1681   $GroupNumber = $Atom->GetGroupNumber();
    1682
    1683   return ($GroupNumber >= 3 && $GroupNumber <= 12) ? 1 : 0;
    1684 }
    1685
    1686 # H1 - hydrocarbon - '[#1][#6]','[#1][#1]'
    1687 #
    1688 sub _IsH1Hydrogen {
    1689   my($This, $Atom) = @_;
    1690
    1691   return $Atom->DoesAtomNeighborhoodMatch('H', ['C,H']) ? 1 : 0;
    1692 }
    1693
    1694 # H2 - alcohol - '[#1]O[CX4]','[#1]Oc','[#1]O[!(C,N,O,S)]','[#1][!C,N,O)]' or matching [ Table 1 annotations, Ref 89 ]
    1695 # '[H]O[CX4]', '[H]Oc', '[H]O[H,B,Si,P,As,Sn]','[H][B,Si,P,S,Sn]'
    1696 #
    1697 sub _IsH2Hydrogen {
    1698   my($This, $Atom) = @_;
    1699   my($AtomNeighbor);
    1700
    1701   # Is Hydrogen directly attached to B,Si,P,S,Sn?
    1702   if ($Atom->DoesAtomNeighborhoodMatch('H', ['B,Si,P,S,Sn'], ['-'])) {
    1703     return 1;
    1704   }
    1705
    1706   # Is Hydrogen directly attached to Oxygen?
    1707   if (!$Atom->DoesAtomNeighborhoodMatch('H', ['O'])) {
    1708     return 0;
    1709   }
    1710
    1711   # Is it attached to appropriate Oxygen?
    1712   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('O')) {
    1713     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('O.T2', ['C.T4', 'H'], ['-', '-'])  ||
    1714        $AtomNeighbor->DoesAtomNeighborhoodMatch('O.T2', ['C.Ar', 'H'], ['-', '-'])  ||
    1715        $AtomNeighbor->DoesAtomNeighborhoodMatch('O.T2', ['B,Si,P,As,Sn', 'H'], ['-', '-']) ||
    1716        $AtomNeighbor->DoesAtomNeighborhoodMatch('O.T2', ['H', 'H'], ['-', '-'])) {
    1717       return 1;
    1718     }
    1719   }
    1720   return 0;
    1721 }
    1722
    1723 # H3 - amine - '[#1][#7]','[#1]O[#7]'
    1724 #
    1725 sub _IsH3Hydrogen {
    1726   my($This, $Atom) = @_;
    1727   my($AtomNeighbor);
    1728
    1729   # Is Hydrogen directly attached to Nitrogen?
    1730   if ($Atom->DoesAtomNeighborhoodMatch('H', ['N'])) {
    1731     return 1;
    1732   }
    1733
    1734   # Is Hydrogen directly attached to Oxygen?
    1735   if (!$Atom->DoesAtomNeighborhoodMatch('H', ['O'])) {
    1736     return 0;
    1737   }
    1738
    1739   # Is it attached to appropriate Oxygen?
    1740   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('O')) {
    1741     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('O.T2', ['N', 'H'], ['-', '-'])) {
    1742       return 1;
    1743     }
    1744   }
    1745   return 0;
    1746 }
    1747
    1748 # H4 - acid - '[#1]OC=[#6]','[#1]OC=[#7]','[#1]OC=O','[#1]OC=S','[#1]OO','[#1]OS'
    1749 #
    1750 sub _IsH4Hydrogen {
    1751   my($This, $Atom) = @_;
    1752   my($AtomNeighbor, $AtomNbrOfNbr);
    1753
    1754   # Get non-hydrogen atom neighbor...
    1755   $AtomNeighbor = $Atom->GetNonHydrogenNeighborOfHydrogenAtom();
    1756   if (!$AtomNeighbor) {
    1757     return 0;
    1758   }
    1759
    1760   # Is it Oxygen neighbor?
    1761   if (!$AtomNeighbor->IsOxygen()) {
    1762     return 0;
    1763   }
    1764
    1765   # '[#1]OO','[#1]OS'
    1766   if ($AtomNeighbor->DoesAtomNeighborhoodMatch('O.TSB2', ['O,S', 'H'], ['-', '-'])) {
    1767     return 1;
    1768   }
    1769
    1770   # '[#1]OC=[#6]','[#1]OC=[#7]','[#1]OC=O','[#1]OC=S'
    1771   for $AtomNbrOfNbr ($AtomNeighbor->GetNeighborsUsingAtomSpecification('C.DB1')) {
    1772     if ($AtomNbrOfNbr->DoesAtomNeighborhoodMatch('C.T3.DB1', ['O.H1', 'O,C,N,S', '*'], ['-', '=', '-'])) {
    1773       return 1;
    1774     }
    1775   }
    1776   return 0;
    1777 }
    1778
    1779 # HS - hydrogen supplemental not matching any basic H type - '[#1]'
    1780 #
    1781 sub _IsHSHydrogen {
    1782   my($This, $Atom) = @_;
    1783
    1784   return $Atom->IsHydrogen() ? 1 : 0;
    1785 }
    1786
    1787 # Return a string containg data for SLogPAtomTypes object...
    1788 #
    1789 sub StringifySLogPAtomTypes {
    1790   my($This) = @_;
    1791   my($AtomTypesString);
    1792
    1793   # Type of AtomTypes...
    1794   $AtomTypesString = "AtomTypes: $This->{Type}; IgnoreHydrogens: " . ($This->{IgnoreHydrogens} ? "Yes" : "No");
    1795
    1796   # Setup atom types information...
    1797   my($AtomID, $AtomType, @AtomTypesInfo, %AssignedAtomTypes);
    1798
    1799   @AtomTypesInfo = ();
    1800   %AssignedAtomTypes = $This->GetAtomTypes();
    1801
    1802   for $AtomID (sort { $a <=> $b } keys %AssignedAtomTypes) {
    1803     $AtomType = $AssignedAtomTypes{$AtomID} ? $AssignedAtomTypes{$AtomID} : 'None';
    1804     push @AtomTypesInfo, "$AtomID:$AtomType";
    1805   }
    1806   $AtomTypesString .= "; AtomIDs:AtomTypes: <" . TextUtil::JoinWords(\@AtomTypesInfo, ", ", 0) . ">";
    1807
    1808   return $AtomTypesString;
    1809 }
    1810
    1811 # Is it a SLogPAtomTypes object?
    1812 sub _IsSLogPAtomTypes {
    1813   my($Object) = @_;
    1814
    1815   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
    1816 }
    1817
    1818 # Check and load SLogP atom types data...
    1819 #
    1820 sub _CheckAndLoadSLogPAtomTypesData {
    1821
    1822   # Is it already loaded?
    1823   if (exists $SLogPAtomTypesDataMap{AtomTypes}) {
    1824     return;
    1825   }
    1826
    1827   _LoadSLogPAtomTypesData();
    1828 }
    1829
    1830 # Load SLogP atom types data from the file assuming first column to be atom type symbol..
    1831 #
    1832 # Format:
    1833 #
    1834 # "AtomTypeSymbol","Description","SMARTS","LogPContribution","MRContribution"
    1835 # "C1","primary, secondary aliphatic","'[CH4]','[CH3]C','[CH2](C)C'","0.1441","2.503"
    1836 # "C2","tertiary, quaternary aliphatic","'[CH](C)(C)C','[C](C)(C)(C)C'","0.0000","2.433"
    1837 #
    1838 sub _LoadSLogPAtomTypesData {
    1839   my($AtomTypesDataFile, $MayaChemToolsLibDir);
    1840
    1841   $MayaChemToolsLibDir = FileUtil::GetMayaChemToolsLibDirName();
    1842
    1843   $AtomTypesDataFile =  "$MayaChemToolsLibDir" . "/data/SLogPAtomTypes.csv";
    1844   if (! -e "$AtomTypesDataFile") {
    1845     croak "Error: MayaChemTools package file, $AtomTypesDataFile, is missing: Possible installation problems...";
    1846   }
    1847
    1848   %SLogPAtomTypesDataMap = ();
    1849   AtomTypes::AtomTypes::LoadAtomTypesData($AtomTypesDataFile, \%SLogPAtomTypesDataMap);
    1850 }
    1851