       1 package AtomTypes::MMFF94AtomTypes;
       2 #
       3 # $RCSfile: MMFF94AtomTypes.pm,v $
       4 # $Date: 2015/02/28 20:48:03 $
       5 # $Revision: 1.20 $
       6 #
       7 # Author: Manish Sud <msud@san.rr.com>
       8 #
       9 # Copyright (C) 2015 Manish Sud. All rights reserved.
      10 #
      11 # This file is part of MayaChemTools.
      12 #
      13 # MayaChemTools is free software; you can redistribute it and/or modify it under
      14 # the terms of the GNU Lesser General Public License as published by the Free
      15 # Software Foundation; either version 3 of the License, or (at your option) any
      16 # later version.
      17 #
      18 # MayaChemTools is distributed in the hope that it will be useful, but without
      19 # any warranty; without even the implied warranty of merchantability of fitness
      20 # for a particular purpose.  See the GNU Lesser General Public License for more
      21 # details.
      22 #
      23 # You should have received a copy of the GNU Lesser General Public License
      24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
      25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
      26 # Boston, MA, 02111-1307, USA.
      27 #
      28
      29 use strict;
      30 use Carp;
      31 use Exporter;
      32 use Scalar::Util ();
      33 use Text::ParseWords;
      34 use AtomTypes::AtomTypes;
      35 use Molecule;
      36
      37 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      38
      39 @ISA = qw(AtomTypes::AtomTypes Exporter);
      40 @EXPORT = qw(GetMMFF94AtomTypesData GetAllPossibleMMFF94AtomTypes GetAllPossibleMMFF94NonHydrogenAtomTypes);
      41 @EXPORT_OK = qw();
      42
      43 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
      44
      45 # Setup class variables...
      46 my($ClassName, %MMFF94AtomTypesDataMap);
      47 _InitializeClass();
      48
      49 # Overload Perl functions...
      50 use overload '""' => 'StringifyMMFF94AtomTypes';
      51
      52 # Class constructor...
      53 sub new {
      54   my($Class, %NamesAndValues) = @_;
      55
      56   # Initialize object...
      57   my $This = $Class->SUPER::new();
      58   bless $This, ref($Class) || $Class;
      59   $This->_InitializeMMFF94AtomTypes();
      60
      61   $This->_InitializeMMFF94AtomTypesProperties(%NamesAndValues);
      62
      63   return $This;
      64 }
      65
      66 # Initialize class ...
      67 sub _InitializeClass {
      68   #Class name...
      69   $ClassName = __PACKAGE__;
      70
      71   # Initialize the data hash. It'll be loaded on demand later...
      72   %MMFF94AtomTypesDataMap = ();
      73 }
      74
      75
      76 # Initialize object data...
      77 #
      78 sub _InitializeMMFF94AtomTypes {
      79   my($This) = @_;
      80
      81   # Type of AtomTypes...
      82   $This->{Type} = 'MMFF94';
      83
      84   # By default, MMFF94 atom types are also assigned to hydrogens...
      85   $This->{IgnoreHydrogens} = 0;
      86
      87   return $This;
      88 }
      89
      90 # Initialize object properties...
      91 #
      92 sub _InitializeMMFF94AtomTypesProperties {
      93   my($This, %NamesAndValues) = @_;
      94
      95   my($Name, $Value, $MethodName);
      96   while (($Name, $Value) = each  %NamesAndValues) {
      97     $MethodName = "Set${Name}";
      98     $This->$MethodName($Value);
      99   }
     100
     101   # Make sure molecule object was specified...
     102   if (!exists $NamesAndValues{Molecule}) {
     103     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying molecule...";
     104   }
     105
     106   return $This;
     107 }
     108
     109 # Get MMFF94 atom types and associated data loaded from MMFF94 data file as
     110 # a reference to hash with the following hash data format:
     111 #
     112 # @{$MMFF94AtomTypesDataMap{AtomTypes}} - Array of all possible atom types for all atoms
     113 # @{$MMFF94AtomTypesDataMap{NonHydrogenAtomTypes}} - Array of all possible atom types for non-hydrogen atoms
     114 # @{$MMFF94AtomTypesDataMap->{ColLabels}} - Array of column labels
     115 # %{$MMFF94AtomTypesDataMap->{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 GetMMFF94AtomTypesData {
     121
     122   # Make sure data is loaded...
     123   _CheckAndLoadMMFF94AtomTypesData();
     124
     125   return \%MMFF94AtomTypesDataMap;
     126 }
     127
     128 # Get all possible MMFF94 atom types corresponding to hydrogen and non-hydrogen
     129 # atoms as an array reference...
     130 #
     131 # This functionality can be either invoked as a class function or an
     132 # object method.
     133 #
     134 sub GetAllPossibleMMFF94AtomTypes {
     135   return _GetAllPossibleMMFF94AtomTypes();
     136 }
     137
     138 # Get all possible MMFF94 atom types corresponding to non-hydrogen atoms
     139 # as an array reference...
     140 #
     141 # This functionality can be either invoked as a class function or an
     142 # object method.
     143 #
     144 sub GetAllPossibleMMFF94NonHydrogenAtomTypes {
     145   my($NonHydrogensOnly);
     146
     147   $NonHydrogensOnly = 1;
     148   return _GetAllPossibleMMFF94AtomTypes($NonHydrogensOnly);
     149 }
     150
     151 # Get all possible MMFF94 atom types as an array reference...
     152 #
     153 sub _GetAllPossibleMMFF94AtomTypes {
     154   my($NonHydrogensOnly) = @_;
     155   my($MMFF94AtomTypesDataRef);
     156
     157   $NonHydrogensOnly = defined $NonHydrogensOnly ? $NonHydrogensOnly : 0;
     158
     159   $MMFF94AtomTypesDataRef = GetMMFF94AtomTypesData();
     160
     161   return $NonHydrogensOnly ? \@{$MMFF94AtomTypesDataRef->{NonHydrogenAtomTypes}}: \@{$MMFF94AtomTypesDataRef->{AtomTypes}};
     162 }
     163
     164 # Assign MMFF94 [ Ref 83-87 ] atom types to all atoms...
     165 #
     166 # Notes:
     167 #     o 212 MMFF94 atom type symbols are listed
     168 #     o 95 MMFF94 atom type numbers are listed
     169 #     o Atom type numbers from 83 to 86 are not used
     170 #     o Number of atom type symbols for:
     171 #         o C: 34
     172 #         o N: 47
     173 #         o O: 45
     174 #         o P: 7
     175 #         o S: 18
     176 #         o F, Br: 2
     177 #         o Cl: 3
     178 #         o I: 1
     179 #         o H: 41
     180 #         o Fe,Cu, Zn: 2
     181 #         o Li, Na, L, K, Mg, Si, : 1
     182 #
     183 sub AssignAtomTypes {
     184   my($This) = @_;
     185
     186   $This->_AssignAtomTypesToNonHydrogenAtoms();
     187
     188   if (!$This->{IgnoreHydrogens}) {
     189     $This->_AssignAtomTypesToHydrogenAtoms();
     190   }
     191
     192   return $This;
     193 }
     194
     195 # Assign atom types to all non-Hydrogen atoms..
     196 #
     197 sub _AssignAtomTypesToNonHydrogenAtoms {
     198   my($This) = @_;
     199   my($Atom, $AtomType);
     200
     201   ATOM: for $Atom ($This->GetMolecule()->GetAtoms()) {
     202     if ($Atom->IsHydrogen()) {
     203       next ATOM;
     204     }
     205     $AtomType = $This->_GetAtomType($Atom);
     206     $This->SetAtomType($Atom, $AtomType);
     207   }
     208   return $This;
     209 }
     210
     211 # Assign atom types to Hydrogen atoms..
     212 #
     213 sub _AssignAtomTypesToHydrogenAtoms {
     214   my($This) = @_;
     215   my($Atom, $AtomType);
     216
     217   if ($This->{IgnoreHydrogens}) {
     218     return $This;
     219   }
     220
     221   ATOM: for $Atom ($This->GetMolecule()->GetAtoms()) {
     222     if (!$Atom->IsHydrogen()) {
     223       next ATOM;
     224     }
     225     $AtomType = $This->_GetAtomTypeForHydrogen($Atom);
     226     $This->SetAtomType($Atom, $AtomType);
     227   }
     228   return $This;
     229 }
     230
     231 # Get MMFF94 atom type for atom...
     232 #
     233 sub _GetAtomType {
     234   my($This, $Atom) = @_;
     235   my($AtomType);
     236
     237   $AtomType = '';
     238
     239   ATOM: {
     240     if ($Atom->IsCarbon()) {
     241       $AtomType = $This->_GetAtomTypeForCarbon($Atom);
     242       last ATOM;
     243     }
     244     if ($Atom->IsNitrogen()) {
     245       $AtomType = $This->_GetAtomTypeForNitrogen($Atom);
     246       last ATOM;
     247     }
     248     if ($Atom->IsOxygen()) {
     249       $AtomType = $This->_GetAtomTypeForOxygen($Atom);
     250       last ATOM;
     251     }
     252     if ($Atom->IsPhosphorus()) {
     253       $AtomType = $This->_GetAtomTypeForPhosphorus($Atom);
     254       last ATOM;
     255     }
     256     if ($Atom->IsSulfur()) {
     257       $AtomType = $This->_GetAtomTypeForSulfur($Atom);
     258       last ATOM;
     259     }
     260     if ($Atom->IsHydrogen()) {
     261       $AtomType = $This->_GetAtomTypeForHydrogen($Atom);
     262       last ATOM;
     263     }
     264     $AtomType = $This->_GetAtomTypeForOtherAtoms($Atom);
     265   }
     266
     267   return $AtomType;
     268 }
     269
     270 # Get MMFF94 atom type for Carbon atom...
     271 #
     272 # 34 AtomTypeSymbols for element C:
     273 #
     274 # AtomTypeSymbol   AtomTypeNum   AtomTypeDefinition
     275 #   CR       1     ALKYL CARBON, SP3
     276 #   C=C      2     VINYLIC CARBON, SP2
     277 #   CSP2     2     GENERIC SP2 CARBON
     278 #   C=O      3     GENERAL CARBONYL CARBON
     279 #   C=N      3     SP2 CARBON IN C=N
     280 #   CGD      3     GUANIDINE CARBON, DOUBLY BONDED TO N
     281 #   C=OR     3     KETONE OR ALDEHYDE CARBONYL CARBON
     282 #   C=ON     3     AMIDE CARBONYL CARBON
     283 #   CONN     3     UREA CARBONYL CARBON
     284 #   COO      3     CARBOXYLIC ACID OR ESTER CARBONYL CARBON
     285 #   COON     3     CARBAMATE CARBONYL CARBON
     286 #   COOO     3     CARBONIC ACID OR ESTER CARBONYL CARBON
     287 #   C=OS     3     THIOESTER CARBONYL CARBON, DOUBLE BONDED TO O
     288 #   C=S      3     THIOESTER CARBON, DOUBLY BONDED TO S
     289 #   C=SN     3     THIOAMIDE, CARBON, DOUBLY BONDED TO S
     290 #   CSO2     3     CARBON IN >C=SO2
     291 #   CS=O     3     CARBON IN >C=S=O (SULFINYL GROUP)
     292 #   CSS      3     THIOCARBOXYLIC ACID OR ESTER CARBONYL CARBON
     293 #   C=P      3     CARBON DOUBLE BONDED TO PHOSPHOROUS
     294 #   CSP      4     ACETYLENIC CARBON
     295 #   =C=      4     ALLENIC CARBON
     296 #   CR4R     20    CARBON IN 4-MEMBERED RINGS
     297 #   CR3R     22    CARBON IN A 3-MEMBERED RING
     298 #   CE4R     30    OLEFINIC CARBON IN 4-MEMBERED RINGS
     299 #   CB       37    CARBON AS IN BENZENE, PYRROLE
     300 #   CO2M     41    CARBOXYLATE ANION CARBON
     301 #   CS2M     41    CARBON IN THIOCARBOXYLATE ANION
     302 #   CGD+     57    GUANIDINIUM CARBON
     303 #   CNN+     57    C IN +N=C-N RESONANCE STRUCTURES
     304 #   C%       60    ISONITRILE CARBON
     305 #   C5A      63    ALPHA CARBON IN 5-MEMBERED HETEROAROMATIC RING
     306 #   C5B      64    BETA CARBON IN 5-MEMBERED HETEROAROMATIC RING
     307 #   C5       78    GENERAL CARBON IN 5-MEMBERED HETEROAROMATIC RING
     308 #   CIM+     80    C IN N-C-N IN IMIDAZOLIUM ION
     309 #
     310 # Notes:
     311 #     . During atom type assignments, matches are performed starting from specific to generic.
     312 #
     313 sub _GetAtomTypeForCarbon {
     314   my($This, $Atom) = @_;
     315   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
     316
     317   $AtomType = 'None';
     318
     319   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
     320
     321   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
     322   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
     323
     324   ATOMTYPE: {
     325
     326     # Only single bonds...
     327     if ($NumOfPiBonds == 0) {
     328       $AtomType = $This->_GetAtomTypeForCarbonWithOnlySigmaBonds($Atom);
     329       last ATOMTYPE;
     330     }
     331
     332     # One double bond...
     333     if ($NumOfPiBonds == 1) {
     334       $AtomType = $This->_GetAtomTypeForCarbonWithOnePiBond($Atom);
     335       last ATOMTYPE;
     336     }
     337
     338     # One triple bond or two double bonds...
     339     if ($NumOfPiBonds == 2) {
     340       $AtomType = $This->_GetAtomTypeForCarbonWithTwoPiBonds($Atom);
     341       last ATOMTYPE;
     342     }
     343
     344     $AtomType = 'None';
     345     carp "Warning: ${ClassName}->_GetAtomTypeForCarbon: MMFF94 atom type for Carbon cann't be assigned...";
     346   }
     347   return $AtomType;
     348 }
     349
     350 # Get MMFF94 atom type for Nitrogen atom...
     351 #
     352 # 47 AtomTypeSymbols for element N:
     353 #
     354 # AtomTypeSymbol   AtomTypeNum   AtomTypeDefinition
     355 #   NR       8     NITROGEN IN ALIPHATIC AMINES
     356 #   N=C      9     NITROGEN IN IMINES
     357 #   N=N      9     NITROGEN IN AZO COMPOUNDS
     358 #   NC=O     10    NITROGEN IN AMIDES
     359 #   NC=S     10    NITROGEN IN N-C=S, THIOAMIDE
     360 #   NN=C     10    NITROGEN IN N-N=C
     361 #   NN=N     10    NITROGEN IN N-N=N
     362 #   NR+      34    QUATERNARY NITROGEN, SP3, POSITIVELY CHARGED
     363 #   NPYD     38    NITROGEN, AS IN PYRIDINE
     364 #   NPYL     39    NITROGEN, AS IN PYRROLE
     365 #   NC=C     40    NITROGEN ON N-C=C
     366 #   NC=N     40    NITROGEN IN N-C=N
     367 #   NC=P     40    NITROGEN IN N-C=P
     368 #   NC%C     40    NITROGEN ATTACHED TO C-C TRIPLE BOND
     369 #   NSP      42    NITROGEN, TRIPLE BONDED
     370 #   NSO2     43    NITROGEN IN SULFONAMIDES
     371 #   NSO3     43    NITROGEN IN SULFONAMIDES, THREE Os ON S
     372 #   NPO2     43    NITROGEN IN PHOSPHONAMIDES
     373 #   NPO3     43    NITROGEN IN PHOSPHONAMIDES, THREE Os ON P
     374 #   NC%N     43    NITROGEN ATTACHED TO CYANO GROUP
     375 #   NO2      45    NITRO GROUP NITROGEN
     376 #   NO3      45    NITRATE GROUP NITROGEN
     377 #   N=O      46    NITROSO NITROGEN
     378 #   NAZT     47    TERMINAL NITROGEN IN AZIDO OR DIAZO GROUP
     379 #   NSO      48    DIVALENT NITROGEN REPLACING MONOVALENT O IN SO2 GROUP
     380 #   =N=      53    NITROGEN IN C=N=N OR -N=N=N
     381 #   N+=C     54    POSITIVELY CHARGED IMINIUM NITROGEN
     382 #   N+=N     54    POSITIVELY CHARGED NITROGEN DOUBLE-BONDED TO N
     383 #   NCN+     55    N IN +N=C-N RESONANCE STRUCTURES - FORMAL CHARGE=1/2
     384 #   NGD+     56    GUANIDINIUM-TYPE NITROGEN - FORMAL CHARGE=1/3
     385 #   NPD+     58    PYRIDINIUM-TYPE NITROGEN - FORMAL CHARGE=1
     386 #   NR%      61    ISONITRILE NITROGEN [FC = 0] OR DIAZO NITROGEN [FC = 1]
     387 #   NM       62    DEPROTONATED SULFONAMIDE N-; FORMAL CHARGE=-1
     388 #   N5A      65    ALPHA AROM HETEROCYCLIC 5-RING  NITROGEN
     389 #   N5B      66    BETA AROM HETEROCYCLIC 5-RING  NITROGEN
     390 #   N2OX     67    SP2-HYDRIDIZED N-OXIDE NITROGEN
     391 #   N3OX     68    SP3-HYDRIDIZED N-OXIDE NITROGEN
     392 #   NPOX     69    PYRIDINE N-OXIDE NITROGEN
     393 #   N5M      76    NEGATIVELY CHARGED N IN, E.G, TRI- OR TETRAZOLE ANION
     394 #   N5       79    GENERAL NITROGEN IN 5-MEMBERED HETEROCYCLIC RING
     395 #   NIM+     81    IMIDAZOLIUM-TYPE NITROGEN - FORMAL CHARGE=1/2
     396 #   N5A+     81    POSITIVE N5A NITROGEN - FORMAL CHARGE=1
     397 #   N5B+     81    POSITIVE N5B NITROGEN - FORMAL CHARGE=1
     398 #   N5+      81    POSITIVE N5 NITROGEN - FORMAL CHARGE=1
     399 #   N5AX     82    N-OXIDE NITROGEN IN 5-RING ALPHA POSITION
     400 #   N5BX     82    N-OXIDE NITROGEN IN 5-RING BETA POSITION
     401 #   N5OX     82    N-OXIDE NITROGEN IN GENERAL 5-RING POSITION
     402 #
     403 # Notes:
     404 #   . The current release of MayaChemTools assigns "None" to Oxygens in the following environment
     405 #     as no generic or specific MMFF94 atom types exists to handle them:
     406 #
     407 #      . Terminal Nitrogens attched to Sulfur in >S=N
     408 #
     409 sub _GetAtomTypeForNitrogen {
     410   my($This, $Atom) = @_;
     411   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
     412
     413   $AtomType = 'None';
     414
     415   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
     416
     417   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
     418   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
     419
     420   ATOMTYPE: {
     421
     422     # Nitrogens in five membered rings...
     423     if ($Atom->IsInRingOfSize(5)) {
     424       $AtomType = $This->_GetAtomTypeForFiveMemberedRingNitrogen($Atom);
     425       last ATOMTYPE;
     426     }
     427
     428     # -N(-)-, -N+(-)(-)-, -(N-1)(-)
     429     if ($NumOfPiBonds == 0) {
     430       $AtomType = $This->_GetAtomTypeForNitrogenWithOnlySigmaBonds($Atom);
     431       last ATOMTYPE;
     432     }
     433
     434     # -N=, and -N+(=)-
     435     if ($NumOfPiBonds == 1) {
     436       $AtomType = $This->_GetAtomTypeForNitrogenWithOnePiBond($Atom);
     437       last ATOMTYPE;
     438     }
     439
     440     # #N, #N+-, and =N+=
     441     if ($NumOfPiBonds == 2) {
     442       $AtomType = $This->_GetAtomTypeForNitrogenWithTwoPiBonds($Atom);
     443       last ATOMTYPE;
     444     }
     445
     446     $AtomType = 'None';
     447     carp "Warning: ${ClassName}->_GetAtomTypeForNitrogen: MMFF94 atom type for Nitrogen cann't be assigned...";
     448   }
     449   return $AtomType;
     450 }
     451
     452 # Get MMFF94 atom type for Oxygen atom...
     453 #
     454 # 45 AtomTypeSymbols for element O:
     455 #
     456 # AtomTypeSymbol   AtomTypeNum   AtomTypeDefinition
     457 #   OR       6     ALCOHOL OR ETHER OXYGEN
     458 #   OC=O     6     ESTER OR CARBOXYLIC ACID -O-
     459 #   OC=C     6     ENOLIC OR PHENOLIC OXYGEN
     460 #   OC=N     6     DIVALENT OXYGEN
     461 #   OC=S     6     THIOESTER OR THIOACID -O-
     462 #   ONO2     6     DIVALENT NITRATE ETHER OXYGEN
     463 #   ON=O     6     DIVALENT NITRITE ETHER OXYGEN
     464 #   OSO3     6     DIVALENT OXYGEN ATTACHED TO SULFUR
     465 #   OSO2     6     DIVALENT OXYGEN ATTACHED TO SULFUR
     466 #   OSO      6     DIVALENT OXYGEN ATTACHED TO SULFUR
     467 #   OS=O     6     DIVALENT OXYGEN ATTACHED TO SULFOXIDE SULFUR
     468 #   -OS      6     GENERAL DIVALENT OXYGEN ATTACHED TO S
     469 #   OPO3     6     DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
     470 #   OPO2     6     DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
     471 #   OPO      6     DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
     472 #   -OP      6     DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
     473 #   -O-      6     GENERAL DIVALENT O
     474 #   O=C      7     GENERAL C=O
     475 #   O=CN     7     CARBONYL OXYGEN, AMIDES
     476 #   O=CR     7     CARBONYL OXYGEN, ALDEHYDES AND KETONES
     477 #   O=CO     7     CARBONYL OXYGEN, CARBOXYLIC ACIDS AND ESTERS
     478 #   O=N      7     NITROSO OXYGEN
     479 #   O=S      7     O=S IN SULFOXIDES
     480 #   O=S=     7     O=S ON SULFUR DOUBLY BONDED TO, E.G., CARBON
     481 #   O2CM     32    OXYGEN IN CARBOXYLATE ANION
     482 #   OXN      32    N-OXIDE OXYGEN
     483 #   O2N      32    NITRO OXYGEN
     484 #   O2NO     32    NITRO-GROUP OXYGEN IN NITRATE
     485 #   O3N      32    NITRATE ANION OXYGEN
     486 #   O-S      32    SINGLE TERMINAL OXYGEN ON TETRACOORD SULFUR
     487 #   O2S      32    TERMINAL O-S IN SULFONES AND SULFONAMIDES
     488 #   O3S      32    TERMINAL O IN SULFONATES
     489 #   O4S      32    TERMINAL O IN SO4(-3)
     490 #   OSMS     32    TERM O IN THIOSULFINATE ANION - FORMAL CHARGE=-0.5
     491 #   OP       32    TERMINAL O IN PHOSPHOXIDES
     492 #   O2P      32    TERMINAL O IN PHOSPHINATES
     493 #   O3P      32    TERMINAL OXYGEN IN PHOSPHONATES
     494 #   O4P      32    TERMINAL OXYGEN IN PHOSPHATES AND PHOSPHODIESTERS
     495 #   O4CL     32    OXYGEN IN CLO4(-) ANION - FORMAL CHARGE=-0.25
     496 #   OM       35    ALKOXIDE OXYGEN, NEGATIVELY CHARGED
     497 #   OM2      35    OXIDE OXYGEN ON SP2 CARBON, NEGATIVELY CHARGED
     498 #   O+       49    POSITIVELY CHARGED OXONIUM (TRICOORDINATE) OXYGEN
     499 #   O=+      51    POSITIVELY CHARGED OXENIUM (DICOORDINATE) OXYGEN
     500 #   OFUR     59    AROMATIC OXYGEN AS IN FURAN
     501 #   OH2      70    OXYGEN ON WATER
     502 #
     503 # Notes:
     504 #   . The current release of MayaChemTools assigns "None" to Oxygens in the following environment
     505 #     as no generic or specific MMFF94 atom types exists to handle them:
     506 #
     507 #      . Terminal anion Oxygen corresponding to divalent Oxygen attached to Sulfoxide Sulfur in
     508 #        OS=O and divalent Oxygen attached to Sulfur in -OS
     509 #      . Terminal Oxygens attched to Sulfur in =SO2
     510 #      . Terminal anion Oxygen attched to Sulfur in SO2M which is same as OS=O
     511 #
     512 sub _GetAtomTypeForOxygen {
     513   my($This, $Atom) = @_;
     514   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds, $OxygenAttachedToSulfur, $OxygenAttachedToPhosphorus);
     515
     516   $AtomType = 'None';
     517
     518   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
     519
     520   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
     521   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
     522
     523   $OxygenAttachedToSulfur = $Atom->GetNeighborsUsingAtomSpecification('S');
     524   $OxygenAttachedToPhosphorus = $Atom->GetNeighborsUsingAtomSpecification('P');
     525
     526   ATOMTYPE: {
     527
     528    # Divalent or terminal Oxygen attached to Sulfur...
     529     if ($OxygenAttachedToSulfur) {
     530       $AtomType = $This->_GetAtomTypeForOxygenAttachedToSulfur($Atom);
     531       last ATOMTYPE;
     532     }
     533
     534    # Divalent or terminal Oxygen attached to Phosphorous...
     535     if ($OxygenAttachedToPhosphorus) {
     536       $AtomType = $This->_GetAtomTypeForOxygenAttachedToPhosphorus($Atom);
     537       last ATOMTYPE;
     538     }
     539
     540     # Only single bonds...
     541     if ($NumOfPiBonds == 0) {
     542       $AtomType = $This->_GetAtomTypeForOxygenWithOnlySigmaBonds($Atom);
     543       last ATOMTYPE;
     544     }
     545
     546     # One double bond...
     547     if ($NumOfPiBonds == 1) {
     548       $AtomType = $This->_GetAtomTypeForOxygenWithOnePiBond($Atom);
     549       last ATOMTYPE;
     550     }
     551
     552     $AtomType = 'None';
     553     carp "Warning: ${ClassName}->_GetAtomTypeForOxygen: MMFF94 atom type for Oxygen cann't be assigned...";
     554   }
     555   return $AtomType;
     556 }
     557
     558 # Get MMFF94 atom type for Phosphorus atom...
     559 #
     560 # 7 AtomTypeSymbols for element P:
     561 #
     562 # AtomTypeSymbol   AtomTypeNum   AtomTypeDefinition
     563 #   PO4      25    PHOSPHOROUS IN PHOSPHATES AND PHOSPHODIESTERS
     564 #   PO3      25    TETRACOORDINATE P WITH THREE ATTACHED OXYGENS
     565 #   PO2      25    TETRACOORDINATE P WITH TWO ATTACHED OXYGENS
     566 #   PO       25    TETRACOORDINATE P WITH ONE ATTACHED OXYGEN
     567 #   PTET     25    GENERAL TETRACOORDINATE PHOSPHORUS
     568 #   P        26    TRICOORDINATE P, AS IN PHOSPHINES
     569 #   -P=C     75    PHOSPHOROUS DOUBLY BONDED TO CARBON
     570 #
     571 sub _GetAtomTypeForPhosphorus {
     572   my($This, $Atom) = @_;
     573   my($AtomType);
     574
     575   $AtomType = 'None';
     576
     577   ATOMTYPE: {
     578
     579     # PO4 : PHOSPHOROUS IN PHOSPHATES AND PHOSPHODIESTERS
     580     if ($This->_IsPhosphateOrPhosphodiesterPhosphorus($Atom)) {
     581       $AtomType = 'PO4';
     582       last ATOMTYPE;
     583     }
     584
     585     # PO3 : TETRACOORDINATE P WITH THREE ATTACHED OXYGENS
     586     if ($This->_IsPhosphonatePhosphorus($Atom)) {
     587       $AtomType = 'PO3';
     588       last ATOMTYPE;
     589     }
     590
     591     # PO2 : TETRACOORDINATE P WITH TWO ATTACHED OXYGENS
     592     if ($This->_IsPhosphinatePhosphorus($Atom)) {
     593       $AtomType = 'PO2';
     594       last ATOMTYPE;
     595     }
     596
     597     # PO : TETRACOORDINATE P WITH ONE ATTACHED OXYGEN
     598     if ($This->_IsPhosphoxidePhosphorus($Atom)) {
     599       $AtomType = 'PO';
     600       last ATOMTYPE;
     601     }
     602
     603     # -P=C : PHOSPHOROUS DOUBLY BONDED TO CARBON
     604     if ($This->_IsDoublyBondedToCarbonPhosphorous($Atom)) {
     605       $AtomType = '-P=C';
     606       last ATOMTYPE;
     607     }
     608
     609     # PTET : GENERAL TETRACOORDINATE PHOSPHORUS
     610     if ($This->_IsTetraCoordinatedPhosphorus($Atom)) {
     611       $AtomType = 'PTET';
     612       last ATOMTYPE;
     613     }
     614
     615     # P : TRICOORDINATE P, AS IN PHOSPHINES
     616     if ($This->_IsTriCoordinatedPhosphorus($Atom)) {
     617       $AtomType = 'P';
     618       last ATOMTYPE;
     619     }
     620
     621     $AtomType = 'None';
     622     carp "Warning: ${ClassName}->_GetAtomTypeForPhosphorus: MMFF94 atom type for Phosphorous cann't be assigned...";
     623   }
     624
     625   return $AtomType;
     626 }
     627
     628 # Get MMFF94 atom type for Sulfur atom...
     629 #
     630 # 18 AtomTypeSymbols for element S:
     631 #
     632 # AtomTypeSymbol   AtomTypeNum   AtomTypeDefinition
     633 #   S        15    SULFUR IN THIOETHERS AND MERCAPTANS
     634 #   S=C      16    TERMINAL SULFUR DOUBLY BONDED TO CARBON
     635 #   S=O      17    SULFUR IN SULFOXIDES
     636 #   >S=N     17    SULFUR, TRICOORD, DOUBLY BONDED TO N
     637 #   SO2      18    SULFUR IN SULFONES
     638 #   SO2N     18    SULFUR IN SULFONAMIDES
     639 #   SO3      18    SULFONATE SULFUR
     640 #   SO4      18    SULFATE SULFUR
     641 #   =SO2     18    SULFONE SULPHER DOUBLY BONDED TO CARBON
     642 #   SNO      18    SULFUR IN NITROGEN ANALOG OF A SULFONE
     643 #   STHI     44    SULFUR AS IN THIOPHENE
     644 #   S-P      72    TERMINAL SULFUR BONDED TO PHOSPHORUS
     645 #   S2CM     72    TERMINAL SULFUR IN THIOCARBOXYLATE ANION
     646 #   SM       72    TERMINAL SULFUR - FORMAL CHARGE=-1
     647 #   SSMO     72    TERMINAL SULFUR IN THIOSULFINATE GROUP
     648 #   SO2M     73    SULFUR IN NEGATIVELY CHARGED SULFINATE GROUP
     649 #   SSOM     73    TRICOORD SULFUR IN THIOSULFINATE GROUP
     650 #   =S=O     74    SULFINYL SULFUR, EG. IN C=S=O
     651 #
     652 sub _GetAtomTypeForSulfur {
     653   my($This, $Atom) = @_;
     654   my($AtomType);
     655
     656   $AtomType = 'None';
     657
     658   ATOMTYPE: {
     659
     660     # SO4 : SULFATE SULFUR
     661     if ($This->_IsSulfateSulfur($Atom)) {
     662       $AtomType = 'SO4';
     663       last ATOMTYPE;
     664     }
     665
     666     # SO3 : SULFONATE SULFUR
     667     if ($This->_IsSulfonateSulfur($Atom)) {
     668       $AtomType = 'SO3';
     669       last ATOMTYPE;
     670     }
     671
     672     # SO2N : SULFUR IN SULFONAMIDES
     673     if ($This->_IsSulfonamideSulfur($Atom)) {
     674       $AtomType = 'SO2N';
     675       last ATOMTYPE;
     676     }
     677
     678     # SO2 : SULFUR IN SULFONES
     679     if ($This->_IsSulfoneSulfur($Atom)) {
     680       $AtomType = 'SO2';
     681       last ATOMTYPE;
     682     }
     683
     684     #  =SO2: SULFONE SULPHER DOUBLY BONDED TO CARBON
     685     if ($This->_IsDoublyBondedToCarbonSulfoneSulfur($Atom)) {
     686       $AtomType = '=SO2';
     687       last ATOMTYPE;
     688     }
     689
     690     # SO2M: SULFUR IN NEGATIVELY CHARGED SULFINATE GROUP
     691     if ($This->_IsNegativelyChargedSulfinateSulfur($Atom)) {
     692       $AtomType = 'SO2M';
     693       last ATOMTYPE;
     694     }
     695
     696     # SNO : SULFUR IN NITROGEN ANALOG OF A SULFONE
     697     if ($This->_IsNitrogenAnalogOfSulfoneSulfur($Atom)) {
     698       $AtomType = 'SNO';
     699       last ATOMTYPE;
     700     }
     701
     702     # S=O : SULFUR IN SULFOXIDES
     703     if ($This->_IsSulfoxideSulfur($Atom)) {
     704       $AtomType = 'S=O';
     705       last ATOMTYPE;
     706     }
     707
     708     # >S=N : SULFUR, TRICOORD, DOUBLY BONDED TO N
     709     if ($This->_IsSNTricoordinatedSulfur($Atom)) {
     710       $AtomType = '>S=N';
     711       last ATOMTYPE;
     712     }
     713
     714     # STHI : SULFUR AS IN THIOPHENE
     715     if ($This->_IsSTHISulfur($Atom)) {
     716       $AtomType = 'STHI';
     717       last ATOMTYPE;
     718     }
     719
     720     # S2CM : TERMINAL SULFUR IN THIOCARBOXYLATE ANION
     721     if ($This->_IsThioCarboxylateAnionTerminalSulfur($Atom)) {
     722       $AtomType = 'S2CM';
     723       last ATOMTYPE;
     724     }
     725
     726     # SSMO : TERMINAL SULFUR IN THIOSULFINATE GROUP
     727     if ($This->_IsThioSulfinateTerminalSulfur($Atom)) {
     728       $AtomType = 'SSMO';
     729       last ATOMTYPE;
     730     }
     731
     732     # SSOM : TRICOORD SULFUR IN THIOSULFINATE GROUP
     733     if ($This->_IsTriCoordinatedThioSulfinateSulfur($Atom)) {
     734       $AtomType = 'SSOM';
     735       last ATOMTYPE;
     736     }
     737
     738     #   =S=O:  SULFINYL SULFUR, EG. IN C=S=O
     739     if ($This->_IsSulfinylSulfur($Atom)) {
     740       $AtomType = '=S=O';
     741       last ATOMTYPE;
     742     }
     743
     744     # S-P : TERMINAL SULFUR BONDED TO PHOSPHORUS
     745     if ($This->_IsSPTerminalSulfur($Atom)) {
     746       $AtomType = 'S-P';
     747       last ATOMTYPE;
     748     }
     749
     750     # S=C : TERMINAL SULFUR DOUBLY BONDED TO CARBON
     751     if ($This->_IsSCTerminalSulfur($Atom)) {
     752       $AtomType = 'S=C';
     753       last ATOMTYPE;
     754     }
     755
     756     # SM : TERMINAL SULFUR - FORMAL CHARGE=-1
     757     if ($This->_IsNegativelyChargedTerminalSulfur($Atom)) {
     758       $AtomType = 'SM';
     759       last ATOMTYPE;
     760     }
     761
     762     # S : SULFUR IN THIOETHERS AND MERCAPTANS
     763     if ($This->_IsThioEthersOrMercaptansSulfur($Atom)) {
     764       $AtomType = 'S';
     765       last ATOMTYPE;
     766     }
     767
     768     $AtomType = 'None';
     769     carp "Warning: ${ClassName}->_GetAtomTypeForSulfur: MMFF94 atom type for Sulfur cann't be assigned...";
     770   }
     771   return $AtomType;
     772 }
     773
     774 # Get MMFF94 atom type for Hydrogen atom...
     775 #
     776 # 41 AtomTypeSymbols for element H:
     777 #
     778 # AtomTypeSymbol   AtomTypeNum   AtomTypeDefinition
     779 #   HC       5     H  ATTACHED TO C
     780 #   HSI      5     H ATTACHED TO SI
     781 #   HOR      21    HYDROGEN IN ALCOHOLS
     782 #   HO       21    GENERAL H ON OXYGEN
     783 #   HOM      21    HYDROGEN IN HYDROXIDE ANION
     784 #   HNR      23    H-N(SP3)
     785 #   H3N      23    H-N(SP3), AMMONIA
     786 #   HPYL     23    H-N IN PYRROLE
     787 #   HNOX     23    H-N IN IN A N-OXIDE
     788 #   HNM      23    H ON DICOORD, NEGATIVELY CHARGED NITROGEN
     789 #   HN       23    GENERAL H ON NITROGEN
     790 #   HOCO     24    H-O IN CARBOXYLIC ACIDS
     791 #   HOP      24    HYDROGEN ON OXYGEN ATTACHED TO PHOSPHOROUS
     792 #   HN=N     27    AZO HYDROGEN
     793 #   HN=C     27    IMINE HYDROGEN
     794 #   HNCO     28    AMIDE HYDROGEN
     795 #   HNCS     28    THIOAMIDE HYDROGEN
     796 #   HNCC     28    H-N IN ENAMINES
     797 #   HNCN     28    H-N IN H-N-C=N
     798 #   HNNC     28    H-N IN H-N-N=C
     799 #   HNNN     28    H-N IN H-N-N=N
     800 #   HNSO     28    H-N IN SULFONAMIDE
     801 #   HNPO     28    H-N IN PHOSPHONAMIDE
     802 #   HNC%     28    HYDROGEN ON N ATTACHED TO TRIPLY BONDED CARBON
     803 #   HSP2     28    GENERAL H ON SP2 NITROGEN
     804 #   HOCC     29    H-O IN ENOLS AND PHENOLS
     805 #   HOCN     29    H-O IN HO-C=N
     806 #   HOH      31    HYDROGEN IN H2O
     807 #   HOS      33    H ON OXYGEN ATTACHED TO SULFUR
     808 #   HNR+     36    H ON QUATERNARY NITROGEN
     809 #   HIM+     36    H ON IMIDAZOLIUM-TYPE NITROGEN
     810 #   HPD+     36    H ON PROTONATED PYRIDINE NITROGEN
     811 #   HNN+     36    H ON AMIDINIUM-TYPE NITROGEN
     812 #   HNC+     36    H ON PROTONATED IMINE NITROGEN
     813 #   HGD+     36    H ON GUANIDINIUM-TYPE NITROGEN
     814 #   HN5+     36    H ON N5+, N5A+ OR N5B+
     815 #   HO+      50    HYDROGEN ON O+ OXYGEN
     816 #   HO=+     52    HYDROGEN ON OXENIUM OXYGEN
     817 #   HS       71    H ATTACHED TO DIVALENT, DICOORDINATE S
     818 #   HS=N     71    H ATTACHED TO TETRAVALENT, TRICOODR S DBL BONDED TO N
     819 #   HP       71    H ATTACHED TO TRI- OR TETRACOORDINATE PHOSPHORUS
     820 #
     821 sub _GetAtomTypeForHydrogen {
     822   my($This, $Atom) = @_;
     823   my($AtomType, $AtomNeighbor);
     824
     825   $AtomType = 'None';
     826
     827   # Get non-hydrogen atom neighbor...
     828   $AtomNeighbor = $Atom->GetNonHydrogenNeighborOfHydrogenAtom();
     829   if (!$AtomNeighbor) {
     830     return $AtomType;
     831   }
     832
     833   ATOMNEIGHBOR: {
     834     if ($AtomNeighbor->IsCarbon()) {
     835       $AtomType = $This->_GetAtomTypeForHydrogenAttachedToCarbon($AtomNeighbor);
     836       last ATOMNEIGHBOR;
     837     }
     838     if ($AtomNeighbor->IsNitrogen()) {
     839       $AtomType = $This->_GetAtomTypeForHydrogenAttachedToNitrogen($AtomNeighbor);
     840       last ATOMNEIGHBOR;
     841     }
     842     if ($AtomNeighbor->IsOxygen()) {
     843       $AtomType = $This->_GetAtomTypeForHydrogenAttachedToOxygen($AtomNeighbor);
     844       last ATOMNEIGHBOR;
     845     }
     846     if ($AtomNeighbor->IsPhosphorus()) {
     847       $AtomType = $This->_GetAtomTypeForHydrogenAttachedToPhosphorus($AtomNeighbor);
     848       last ATOMNEIGHBOR;
     849     }
     850     if ($AtomNeighbor->IsSulfur()) {
     851       $AtomType = $This->_GetAtomTypeForHydrogenAttachedToSulfur($AtomNeighbor);
     852       last ATOMNEIGHBOR;
     853     }
     854     if ($AtomNeighbor->IsSilicon()) {
     855       $AtomType = $This->_GetAtomTypeForHydrogenAttachedToSilicon($AtomNeighbor);
     856       last ATOMNEIGHBOR;
     857     }
     858     $AtomType = "None";
     859     carp "Warning: ${ClassName}->_GetAtomTypeForHydrogen: MMFF94 atom type for Hydrogen cann't be assigned...";
     860   }
     861   return $AtomType;
     862 }
     863
     864 # Get MMFF94 atom type for atoms other than Carbon, Nitrogen, Oxygen, Phosporus,
     865 # Sulfur and Hydrogen...
     866 #
     867 # AtomTypeSymbol   AtomTypeNum   AtomTypeDefinition
     868 #   LI+      92    LITHIUM CATION
     869 #   F        11    FLUORINE
     870 #   F-       89    FLUORIDE ANION
     871 #   NA+      93    SODIUM CATION
     872 #   MG+2     99    DIPOSITIVE MAGNESIUM CATION
     873 #   SI       19    SILICON
     874 #   CL       12    CHLORINE
     875 #   CLO4     77    CHLORINE IN PERCHLORATE ANION, CLO4(-)
     876 #   CL-      90    CHLORIDE ANION
     877 #   K+       94    POTASSIUM CATION
     878 #   CA+2     96    DIPOSITIVE CALCIUM
     879 #   FE+2     87    IRON +2 CATION
     880 #   FE+3     88    IROM +3 CATION
     881 #   CU+1     97    MONOPOSITIVE COPPER
     882 #   CU+2     98    DIPOSITIVE COPPER
     883 #   ZINC     95    DIPOSITIVE ZINC
     884 #   ZN+2     95    DIPOSITIVE ZINC
     885 #   BR       13    BROMINE
     886 #   BR-      91    BROMIDE ANION
     887 #   I        14    IODINE
     888 #
     889 #
     890 sub _GetAtomTypeForOtherAtoms {
     891   my($This, $Atom) = @_;
     892   my($AtomType, $AtomSymbol, $FormalCharge, $CallingMethod, @AllowedFormalCharges);
     893
     894   $AtomType = 'None';
     895
     896   $AtomSymbol = $Atom->GetAtomSymbol();
     897   $FormalCharge = $Atom->GetFormalCharge();
     898
     899   $CallingMethod = "_GetAtomTypeForOtherAtoms";
     900   @AllowedFormalCharges = ();
     901
     902   ATOMSYMBOL: {
     903     # FLUORINE
     904     if ($AtomSymbol =~ /^F$/i) {
     905       #  F : FLUORINE;  F- :  FLUORIDE ANION
     906       $AtomType = ($FormalCharge == -1) ? 'F-' : 'F';
     907       @AllowedFormalCharges = ('0', '-1');
     908       last ATOMSYMBOL;
     909     }
     910     # CHLORINE
     911     if ($AtomSymbol =~ /^Cl$/i) {
     912       # CL : CHLORINE; CLO4 : CHLORINE IN PERCHLORATE ANION, CLO4(-);
     913       # CL- : CHLORIDE ANION
     914       $AtomType = $This->_IsPerChlorateAnionChlorine($Atom) ? 'CLO4' : (($FormalCharge == -1) ? 'CL-' : 'CL');
     915       @AllowedFormalCharges = ('0', '-1');
     916       last ATOMSYMBOL;
     917     }
     918     # BROMINE
     919     if ($AtomSymbol =~ /^Br$/i) {
     920       # BR : BROMINE; BR- : BROMIDE ANION
     921       $AtomType = ($FormalCharge == -1) ? 'BR-' : 'BR';
     922       @AllowedFormalCharges = ('0', '-1');
     923       last ATOMSYMBOL;
     924     }
     925     # IODINE
     926     if ($AtomSymbol =~ /^I$/i) {
     927       $AtomType = 'I';
     928       @AllowedFormalCharges = ('0');
     929       last ATOMSYMBOL;
     930     }
     931     # LI+ : LITHIUM CATION
     932     if ($AtomSymbol =~ /^Li$/i) {
     933       $AtomType = 'LI+';
     934       @AllowedFormalCharges = ('+1');
     935       last ATOMSYMBOL;
     936     }
     937     # NA+ : SODIUM CATION
     938     if ($AtomSymbol =~ /^Na$/i) {
     939       $AtomType = 'NA+';
     940       @AllowedFormalCharges = ('+1');
     941       last ATOMSYMBOL;
     942     }
     943     # MG+2 : DIPOSITIVE MAGNESIUM CATION
     944     if ($AtomSymbol =~ /^Mg$/i) {
     945       $AtomType = 'MG+2';
     946       @AllowedFormalCharges = ('+2');
     947       last ATOMSYMBOL;
     948     }
     949     # SI : SILICON
     950     if ($AtomSymbol =~ /^Si$/i) {
     951       $AtomType = 'SI';
     952       @AllowedFormalCharges = ('0');
     953       last ATOMSYMBOL;
     954     }
     955     # K+ : POTASSIUM CATION
     956     if ($AtomSymbol =~ /^K$/i) {
     957       $AtomType = 'K+';
     958       @AllowedFormalCharges = ('+1');
     959       last ATOMSYMBOL;
     960     }
     961     # CA+2 : DIPOSITIVE CALCIUM
     962     if ($AtomSymbol =~ /^Ca$/i) {
     963       $AtomType = 'CA+2';
     964       @AllowedFormalCharges = ('+2');
     965       last ATOMSYMBOL;
     966     }
     967     # IRON
     968     if ($AtomSymbol =~ /^Fe$/i) {
     969       # FE+2 : IRON +2 CATION; FE+3 : IROM +3 CATION
     970       $AtomType = ($FormalCharge == 3) ? 'FE+3' : 'FE+2';
     971       @AllowedFormalCharges = ('+2', '+3');
     972       last ATOMSYMBOL;
     973     }
     974     # COPPER
     975     if ($AtomSymbol =~ /^Cu$/i) {
     976       # CU+1 : MONOPOSITIVE COPPER; CU+2 : DIPOSITIVE COPPER
     977       $AtomType = ($FormalCharge == 1) ? 'CU+1' : 'CU+2';
     978       @AllowedFormalCharges = ('+1', '+2');
     979       last ATOMSYMBOL;
     980     }
     981     # ZINC
     982     if ($AtomSymbol =~ /^Zn$/i) {
     983       # ZN+2 : DIPOSITIVE ZINC
     984       $AtomType = 'Zn+2';
     985       @AllowedFormalCharges = ('+2');
     986       last ATOMSYMBOL;
     987     }
     988     $AtomType = 'None';
     989     carp "Warning: ${ClassName}->_GetAtomTypeForOtherAtoms: MMFF94 atom type for $AtomSymbol cann't be assigned...";
     990   }
     991   if (@AllowedFormalCharges) {
     992     $This->_CheckFormalChargeMismatch($CallingMethod, $AtomSymbol, $AtomType, $FormalCharge, \@AllowedFormalCharges);
     993   }
     994   return $AtomType;
     995 }
     996
     997 # Check any formal charge mismatches...
     998 #
     999 sub _CheckFormalChargeMismatch {
    1000   my($This, $CallingMethod, $AtomSymbol, $AssignedAtomType, $FormalCharge, $AllowedFormalChargesRef) = @_;
    1001   my($AllowedFormalCharge, $FormalChargeMismatch);
    1002
    1003   $FormalChargeMismatch = 1;
    1004
    1005   FORMALCHARGE: for $AllowedFormalCharge (@{$AllowedFormalChargesRef}) {
    1006     if ($AllowedFormalCharge == $FormalCharge) {
    1007       $FormalChargeMismatch = 0;
    1008       last FORMALCHARGE;
    1009     }
    1010   }
    1011   if ($FormalChargeMismatch) {
    1012     my($AllowedFormalCharges);
    1013     $AllowedFormalCharges = TextUtil::JoinWords($AllowedFormalChargesRef, ",", 0);
    1014
    1015     carp "\nWarning: ${ClassName}->${CallingMethod}:_CheckFormalChargeMismatch: MMFF94 atom for $AtomSymbol with formal charge, $FormalCharge, cann't be assigned: Formal charge, $FormalCharge, is different from allowed formal charge(s): $AllowedFormalCharges. Default UFF atom type, $AssignedAtomType, has been assigned...";
    1016   }
    1017
    1018   return $This;
    1019 }
    1020
    1021 # Get MMFF94 atom type for Carbon with only sigma bonds...
    1022 #
    1023 sub _GetAtomTypeForCarbonWithOnlySigmaBonds {
    1024   my($This, $Atom) = @_;
    1025   my($AtomType);
    1026
    1027   $AtomType = 'None';
    1028
    1029   ATOMTYPE: {
    1030     # CR4R : CARBON IN 4-MEMBERED RINGS
    1031     if ($This->_IsFourMemberedRingCarbon($Atom)) {
    1032       $AtomType = 'CR4R';
    1033       last ATOMTYPE;
    1034     }
    1035
    1036     # CR3R : CARBON IN A 3-MEMBERED RING
    1037     if ($This->_IsThreeMemberedRingCarbon($Atom)) {
    1038       $AtomType = 'CR3R';
    1039       last ATOMTYPE;
    1040     }
    1041
    1042     #  CR : ALKYL CARBON, SP3
    1043     if ($This->_IsAlkylCarbon($Atom)) {
    1044       $AtomType = 'CR';
    1045       last ATOMTYPE;
    1046     }
    1047
    1048     # As far as I can tell, MMFF94 doesn't have a generic atom type for SP3 carbon. So the current
    1049     # release of MayaChemTools package used CR as defaul SP3 carbon.
    1050     #
    1051     $AtomType = 'CR';
    1052   }
    1053
    1054   return $AtomType;
    1055 }
    1056
    1057 # Get MMFF94 atom type for Carbon with one pi bond...
    1058 #
    1059 sub _GetAtomTypeForCarbonWithOnePiBond {
    1060   my($This, $Atom) = @_;
    1061   my($AtomType);
    1062
    1063   $AtomType = 'None';
    1064
    1065   ATOMTYPE: {
    1066     # CR3R :  CARBON IN 3-MEMBERED RINGS
    1067     if ($This->_IsThreeMemberedRingOlefinicCarbon($Atom)) {
    1068       $AtomType = 'CR3R';
    1069       last ATOMTYPE;
    1070     }
    1071
    1072     # CE4R :  OLEFINIC CARBON IN 4-MEMBERED RINGS
    1073     if ($This->_IsFourMemberedRingOlefinicCarbon($Atom)) {
    1074       $AtomType = 'CE4R';
    1075       last ATOMTYPE;
    1076     }
    1077
    1078     # CIM+ : C IN N-C-N IN IMIDAZOLIUM ION
    1079     if ($This->_IsImidazoliumCarbon($Atom)) {
    1080       $AtomType = 'CIM+';
    1081       last ATOMTYPE;
    1082     }
    1083
    1084     # C5A : ALPHA CARBON IN 5-MEMBERED HETEROAROMATIC RING
    1085     if ($This->_IsFiveMemberedHeteroAromaticRingAlphaCarbon($Atom)) {
    1086       $AtomType = 'C5A';
    1087       last ATOMTYPE;
    1088     }
    1089
    1090     # C5B : BETA CARBON IN 5-MEMBERED HETEROAROMATIC RING
    1091     if ($This->_IsFiveMemberedHeteroAromaticRingBetaCarbon($Atom)) {
    1092       $AtomType = 'C5B';
    1093       last ATOMTYPE;
    1094     }
    1095
    1096     # C5 : GENERAL CARBON IN 5-MEMBERED HETEROAROMATIC RING
    1097     if ($This->_IsFiveMemberedHeteroAromaticRingCarbon($Atom)) {
    1098       $AtomType = 'C5';
    1099       last ATOMTYPE;
    1100     }
    1101
    1102     # CB : CARBON AS IN BENZENE, PYRROLE
    1103     if ($This->_IsRingAromaticCarbon($Atom)) {
    1104       $AtomType = 'CB';
    1105       last ATOMTYPE;
    1106     }
    1107
    1108     # C=C : VINYLIC CARBON, SP2
    1109     if ($This->_IsVinylicCarbon($Atom)) {
    1110       $AtomType = 'C=C';
    1111       last ATOMTYPE;
    1112     }
    1113
    1114     # C=OR : KETONE OR ALDEHYDE CARBONYL CARBON
    1115     if ($This->_IsKetoneOrAldehydeCarbonylCarbon($Atom)) {
    1116       $AtomType = 'C=OR';
    1117       last ATOMTYPE;
    1118     }
    1119
    1120     # C=ON : AMIDE CARBONYL CARBON
    1121     if ($This->_IsAmideCarbonylCarbon($Atom)) {
    1122       $AtomType = 'C=ON';
    1123       last ATOMTYPE;
    1124     }
    1125
    1126     # CONN : UREA CARBONYL CARBON
    1127     if ($This->_IsUreaCarbonylCarbon($Atom)) {
    1128       $AtomType = 'CONN';
    1129       last ATOMTYPE;
    1130     }
    1131
    1132     # COO : CARBOXYLIC ACID OR ESTER CARBONYL CARBON
    1133     if ($This->_IsCarboxylicAcidOrEsterCarbonylCarbon($Atom)) {
    1134       $AtomType = 'COO';
    1135       last ATOMTYPE;
    1136     }
    1137
    1138     # CO2M : CARBOXYLATE ANION CARBON
    1139     if ($This->_IsCarboxylateAnionCarbon($Atom)) {
    1140       $AtomType = 'CO2M';
    1141       last ATOMTYPE;
    1142     }
    1143
    1144     # COON: CARBAMATE CARBONYL CARBON
    1145     if ($This->_IsCarbamateCarbonylCarbon($Atom)) {
    1146       $AtomType = 'COON';
    1147       last ATOMTYPE;
    1148     }
    1149
    1150     # COOO: CARBONIC ACID OR ESTER CARBONYL CARBON
    1151     if ($This->_IsCarbonicAcidOrEsterCarbonylCarbon($Atom)) {
    1152       $AtomType = 'COOO';
    1153       last ATOMTYPE;
    1154     }
    1155
    1156     # C=OS : THIOESTER CARBONYL CARBON, DOUBLE BONDED TO O
    1157     if ($This->_IsThioEsterCarbonylCarbon($Atom)) {
    1158       $AtomType = 'C=OS';
    1159       last ATOMTYPE;
    1160     }
    1161
    1162     # C=O : GENERAL CARBONYL CARBON
    1163     if ($This->_IsGeneralCarbonylCarbon($Atom)) {
    1164       $AtomType = 'C=O';
    1165       last ATOMTYPE;
    1166     }
    1167
    1168     # C=SN : THIOAMIDE, CARBON, DOUBLY BONDED TO S
    1169     if ($This->_IsThioAmideCarbon($Atom)) {
    1170       $AtomType = 'C=SN';
    1171       last ATOMTYPE;
    1172     }
    1173
    1174     # CSO2 : CARBON IN >C=SO2
    1175     if ($This->_IsSulfonylCarbon($Atom)) {
    1176       $AtomType = 'CSO2';
    1177       last ATOMTYPE;
    1178     }
    1179
    1180     # CS=O :  CARBON IN >C=S=O (SULFINYL GROUP)
    1181     if ($This->_IsSulfinylCarbon($Atom)) {
    1182       $AtomType = 'CS=O';
    1183       last ATOMTYPE;
    1184     }
    1185
    1186     # CSS : THIOCARBOXYLIC ACID OR ESTER CARBONYL CARBON
    1187     if ($This->_IsThioCarboxylicAcidOrEsterCarbonylCarbon($Atom)) {
    1188       $AtomType = 'CSS';
    1189       last ATOMTYPE;
    1190     }
    1191
    1192     # CS2M : CARBON IN THIOCARBOXYLATE ANION
    1193     if ($This->_IsThioCarboxylateAnionCarbon($Atom)) {
    1194       $AtomType = 'CS2M';
    1195       last ATOMTYPE;
    1196     }
    1197
    1198     # C=S : THIOESTER CARBON, DOUBLY BONDED TO S
    1199     if ($This->_IsThioEsterCarbon($Atom)) {
    1200       $AtomType = 'C=S';
    1201       last ATOMTYPE;
    1202     }
    1203
    1204     # C=P : CARBON DOUBLE BONDED TO PHOSPHOROUS
    1205     if ($This->_IsDoublyBondedToPhosphorousCarbon($Atom)) {
    1206       $AtomType = 'C=P';
    1207       last ATOMTYPE;
    1208     }
    1209
    1210     # CGD : GUANIDINE CARBON, DOUBLY BONDED TO N
    1211     if ($This->_IsGuandineCarbon($Atom)) {
    1212       $AtomType = 'CGD';
    1213       last ATOMTYPE;
    1214     }
    1215
    1216     # CGD+ : GUANIDINIUM CARBON
    1217     if ($This->_IsGuandiniumCarbon($Atom)) {
    1218       $AtomType = 'CGD+';
    1219       last ATOMTYPE;
    1220     }
    1221
    1222     #CNN+ : C IN +N=C-N RESONANCE STRUCTURES
    1223     if ($This->_IsNCNResonaceStructuresCarbon($Atom)) {
    1224       $AtomType = 'CNN+';
    1225       last ATOMTYPE;
    1226     }
    1227
    1228     # C=N : SP2 CARBON IN C=N
    1229     if ($This->_IsDoublyBondedToNitrogenSP2Carbon($Atom)) {
    1230       $AtomType = 'C=N';
    1231       last ATOMTYPE;
    1232     }
    1233
    1234     # CSP2 : GENERIC SP2 CARBON
    1235     if ($This->_IsGenericSP2Carbon($Atom)) {
    1236       $AtomType = 'CSP2';
    1237       last ATOMTYPE;
    1238     }
    1239
    1240     $AtomType = 'CSP2';
    1241     carp "Warning: ${ClassName}->_GetAtomTypeForCarbonWithOnePiBond: MMFF94 atom type for Carbon cann't be assigned; Default MMFF94 atom type, $AtomType, has been assigned...\n";
    1242   }
    1243
    1244   return $AtomType;
    1245 }
    1246
    1247 # Get MMFF94 atom type for Carbon with two pi bonds...
    1248 #
    1249 sub _GetAtomTypeForCarbonWithTwoPiBonds {
    1250   my($This, $Atom) = @_;
    1251   my($AtomType);
    1252
    1253   $AtomType = 'None';
    1254
    1255   ATOMTYPE: {
    1256     # =C= : ALLENIC CARBON
    1257     if ($This->_IsAllenicCarbon($Atom)) {
    1258       $AtomType = '=C=';
    1259       last ATOMTYPE;
    1260     }
    1261
    1262     # C% : ISONITRILE CARBON ( R-N+#C- )
    1263     if ($This->_IsIsoNitrileCarbon($Atom)) {
    1264       $AtomType = 'C%';
    1265       last ATOMTYPE;
    1266     }
    1267
    1268     # CSP : ACETYLENIC CARBON
    1269     if ($This->_IsAcetylenicCarbon($Atom)) {
    1270       $AtomType = 'CSP';
    1271       last ATOMTYPE;
    1272     }
    1273
    1274     $AtomType = 'CSP';
    1275     carp "Warning: ${ClassName}->_GetAtomTypeForCarbonWithTwoPiBonds: MMFF94 atom type for Carbon cann't be assigned; Default MMFF94 atom type, $AtomType, has been assigned...\n";
    1276   }
    1277
    1278   return $AtomType;
    1279 }
    1280
    1281
    1282 # CR4R : CARBON IN 4-MEMBERED RINGS
    1283 #
    1284 sub _IsFourMemberedRingCarbon {
    1285   my($This, $Atom) = @_;
    1286
    1287   return $Atom->DoesAtomNeighborhoodMatch('C.RA4.T4.TSB4') ? 1 : 0;
    1288 }
    1289
    1290 # CR4R : CARBON IN 3-MEMBERED RINGS
    1291 #
    1292 sub _IsThreeMemberedRingCarbon {
    1293   my($This, $Atom) = @_;
    1294
    1295   return $Atom->DoesAtomNeighborhoodMatch('C.RA3.T4.TSB4') ? 1 : 0;
    1296 }
    1297
    1298 #  CR : ALKYL CARBON, SP3
    1299 #
    1300 sub _IsAlkylCarbon {
    1301   my($This, $Atom) = @_;
    1302
    1303   return $Atom->DoesAtomNeighborhoodMatch('C.T4.TSB4', ['C,H', 'C,H', 'C,H', 'C,H'], ['-', '-', '-', '-']) ? 1 : 0;
    1304 }
    1305
    1306 # C5A : ALPHA CARBON IN 5-MEMBERED HETEROAROMATIC RING
    1307 #
    1308 sub _IsFiveMemberedHeteroAromaticRingAlphaCarbon {
    1309   my($This, $Atom) = @_;
    1310
    1311   # Is it an aromatic atom in a five membered ring?
    1312   if (!$Atom->DoesAtomNeighborhoodMatch('C.Ar.RA5')) {
    1313     return 0;
    1314   }
    1315
    1316   # Is it part of a five membered ring containing hetero atom at alpha position?
    1317   my($RingAtomsRef);
    1318
    1319   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    1320     if ($This->_IsAtomPositionAlphaInHeteroAromaticRing($Atom, $RingAtomsRef)) {
    1321       return 1;
    1322     }
    1323   }
    1324   return 0;
    1325 }
    1326
    1327 # C5B : BETA CARBON IN 5-MEMBERED HETEROAROMATIC RING
    1328 #
    1329 sub _IsFiveMemberedHeteroAromaticRingBetaCarbon {
    1330   my($This, $Atom) = @_;
    1331
    1332   # Is it an aromatic atom in a five membered ring?
    1333   if (!$Atom->DoesAtomNeighborhoodMatch('C.Ar.RA5')) {
    1334     return 0;
    1335   }
    1336
    1337   # Is it part of five membered rings containing hetero atom at beta position?
    1338   my($RingAtomsRef);
    1339
    1340   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    1341     if ($This->_IsAtomPositionBetaInHeteroAromaticRing($Atom, $RingAtomsRef)) {
    1342       return 1;
    1343     }
    1344   }
    1345   return 0;
    1346 }
    1347
    1348
    1349 # C5 : GENERAL CARBON IN 5-MEMBERED HETEROAROMATIC RING
    1350 #
    1351 sub _IsFiveMemberedHeteroAromaticRingCarbon {
    1352   my($This, $Atom) = @_;
    1353
    1354   # Is it an aromatic atom in a five membered ring?
    1355   if (!$Atom->DoesAtomNeighborhoodMatch('C.Ar.RA5')) {
    1356     return 0;
    1357   }
    1358
    1359   # Is it part of five membered rings containing at least one hetero atom?
    1360   my($RingAtomsRef, $RingIsAromatic, $NumOfHeteroAtoms);
    1361   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    1362     ($RingIsAromatic, $NumOfHeteroAtoms) = $This->_GetHeteroAtomsInformationInRing($RingAtomsRef);
    1363     if ($RingIsAromatic && $NumOfHeteroAtoms >= 1) {
    1364       return 1;
    1365     }
    1366   }
    1367   return 0;
    1368 }
    1369
    1370 # CR3R :  OLEFINIC CARBON IN 3-MEMBERED RINGS
    1371 #
    1372 # Notes:
    1373 #    . MMFF94 atom type for olefinic Carbon in 3-membered rings is same as SP3 Carbon.
    1374 #
    1375 sub _IsThreeMemberedRingOlefinicCarbon {
    1376   my($This, $Atom) = @_;
    1377
    1378   return $Atom->DoesAtomNeighborhoodMatch('C.RA3.T3.DB1', ['*', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    1379 }
    1380
    1381 # CE4R :  OLEFINIC CARBON IN 4-MEMBERED RINGS
    1382 #
    1383 sub _IsFourMemberedRingOlefinicCarbon {
    1384   my($This, $Atom) = @_;
    1385
    1386   return $Atom->DoesAtomNeighborhoodMatch('C.RA4.T3.DB1', ['*', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    1387 }
    1388
    1389 # CB : CARBON AS IN BENZENE, PYRROLE
    1390 #
    1391 # Notes:
    1392 #    . MayaChemTools assigns all aromatic carbons, other than five membered
    1393 #      hetero aromatic ring Carbons, as CB.
    1394 #
    1395 sub _IsRingAromaticCarbon {
    1396   my($This, $Atom) = @_;
    1397
    1398   return ($Atom->DoesAtomNeighborhoodMatch('C.Ar.RA')) ? 1 : 0;
    1399 }
    1400
    1401 # C=C : VINYLIC CARBON, SP2
    1402 #
    1403 sub _IsVinylicCarbon {
    1404   my($This, $Atom) = @_;
    1405
    1406   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['C', '*', '*'], ['=', '-', '-'], ['C,H', 'C,H', 'C,H']) ? 1 : 0;
    1407 }
    1408
    1409 # C=OR : KETONE OR ALDEHYDE CARBONYL CARBON
    1410 #
    1411 sub _IsKetoneOrAldehydeCarbonylCarbon {
    1412   my($This, $Atom) = @_;
    1413
    1414   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['O', 'C,H', 'C,H'], ['=', '-', '-']) ? 1 : 0;
    1415 }
    1416
    1417 # C=ON : AMIDE CARBONYL CARBON
    1418 #
    1419 sub _IsAmideCarbonylCarbon {
    1420   my($This, $Atom) = @_;
    1421
    1422   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['O', 'N', 'C,H'], ['=', '-', '-']) ? 1 : 0;
    1423 }
    1424
    1425 # CONN : UREA CARBONYL CARBON : R-(R'-)N-C(=O)-N(-R")-R'''
    1426 #
    1427 sub _IsUreaCarbonylCarbon {
    1428   my($This, $Atom) = @_;
    1429
    1430   return $Atom->DoesAtomNeighborhoodMatch('C.X3.DB1', ['O', 'N', 'N'], ['=', '-', '-']) ? 1 : 0;
    1431 }
    1432
    1433 # COO : CARBOXYLIC ACID OR ESTER CARBONYL CARBON
    1434 #
    1435 sub _IsCarboxylicAcidOrEsterCarbonylCarbon {
    1436   my($This, $Atom) = @_;
    1437
    1438   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['O', 'O.X1.FC0,O.X2', 'C,H'], ['=', '-', '-'], ['C', 'C,H', 'C,H']) ? 1 : 0;
    1439 }
    1440
    1441 # CO2M : CARBOXYLATE ANION CARBON
    1442 #
    1443 sub _IsCarboxylateAnionCarbon {
    1444   my($This, $Atom) = @_;
    1445
    1446   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['O', 'O.X1.FC-1', 'C,H'], ['=', '-', '-'], ['C', 'C,H', 'C,H']) ? 1 : 0;
    1447 }
    1448
    1449 # COON: CARBAMATE CARBONYL CARBON : R-O-C(=O)-N(-R')-R"
    1450 #
    1451 sub _IsCarbamateCarbonylCarbon {
    1452   my($This, $Atom) = @_;
    1453
    1454   return $Atom->DoesAtomNeighborhoodMatch('C.X3.DB1', ['O', 'O', 'N'], ['=', '-', '-']) ? 1 : 0;
    1455 }
    1456
    1457 # COOO: CARBONIC ACID OR ESTER CARBONYL CARBON:  R-O-C(=O)-O-R'
    1458 #
    1459 sub _IsCarbonicAcidOrEsterCarbonylCarbon {
    1460   my($This, $Atom) = @_;
    1461
    1462   return $Atom->DoesAtomNeighborhoodMatch('C.X3.DB1', ['O', 'O', 'O'], ['=', '-', '-']) ? 1 : 0;
    1463 }
    1464
    1465 # C=OS : THIOESTER CARBONYL CARBON, DOUBLE BONDED TO O
    1466 #
    1467 sub _IsThioEsterCarbonylCarbon {
    1468   my($This, $Atom) = @_;
    1469
    1470   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['O', 'S', 'C,H'], ['=', '-', '-']) ? 1 : 0;
    1471 }
    1472
    1473 # C=O : GENERAL CARBONYL CARBON
    1474 #
    1475 sub _IsGeneralCarbonylCarbon {
    1476   my($This, $Atom) = @_;
    1477
    1478   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['O', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    1479 }
    1480
    1481 # C=SN : THIOAMIDE, CARBON, DOUBLY BONDED TO S
    1482 #
    1483 sub _IsThioAmideCarbon {
    1484   my($This, $Atom) = @_;
    1485
    1486   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['S', 'N', 'C,H'], ['=', '-', '-']) ? 1 : 0;
    1487 }
    1488
    1489 # CSO2 : CARBON IN >C=SO2
    1490 #
    1491 sub _IsSulfonylCarbon {
    1492   my($This, $Atom) = @_;
    1493   my($AtomNeighbor);
    1494
    1495   # Is it connected to a sulfone Sulfur?
    1496   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S')) {
    1497     if ($This->_IsDoublyBondedToCarbonSulfoneSulfur($AtomNeighbor)) {
    1498       return 1;
    1499     }
    1500   }
    1501   return 0;
    1502 }
    1503
    1504
    1505 # CS=O :  CARBON IN >C=S=O (SULFINYL GROUP)
    1506 #
    1507 sub _IsSulfinylCarbon {
    1508   my($This, $Atom) = @_;
    1509   my($AtomNeighbor);
    1510
    1511   # Is it connected to a sulfinyl Sulfur?
    1512   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S')) {
    1513     if ($This->_IsSulfinylSulfur($AtomNeighbor)) {
    1514       return 1;
    1515     }
    1516   }
    1517   return 0;
    1518 }
    1519
    1520 # CSS : THIOCARBOXYLIC ACID OR ESTER CARBONYL CARBON
    1521 #
    1522 sub _IsThioCarboxylicAcidOrEsterCarbonylCarbon {
    1523   my($This, $Atom) = @_;
    1524
    1525   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['S', 'S.X1.FC0,S.X2', 'C,H'], ['=', '-', '-'], ['C', 'C,H', 'C,H']) ? 1 : 0;
    1526 }
    1527
    1528 # CS2M : CARBON IN THIOCARBOXYLATE ANION
    1529 #
    1530 sub _IsThioCarboxylateAnionCarbon {
    1531   my($This, $Atom) = @_;
    1532
    1533   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['S', 'S.X1.FC-1', 'C,H'], ['=', '-', '-'], ['C', 'C,H', 'C,H']) ? 1 : 0;
    1534 }
    1535
    1536 # C=S : THIOESTER CARBON, DOUBLY BONDED TO S
    1537 #
    1538 sub _IsThioEsterCarbon {
    1539   my($This, $Atom) = @_;
    1540
    1541   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['S.X1', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    1542 }
    1543
    1544 # C=P : CARBON DOUBLE BONDED TO PHOSPHOROUS
    1545 #
    1546 sub _IsDoublyBondedToPhosphorousCarbon {
    1547   my($This, $Atom) = @_;
    1548
    1549   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['P.X1', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    1550 }
    1551
    1552 # CIM+ : C IN N-C-N IN IMIDAZOLIUM ION
    1553 #
    1554 # Notes:
    1555 #    . Imidazole is five membered ring contain  C*=C-N-C=N* (* - Ring bond)
    1556 #    . Imidazolium ion is where N in C=N has a formal charge of +1 and has an extra substituent
    1557 #
    1558 sub _IsImidazoliumCarbon {
    1559   my($This, $Atom) = @_;
    1560
    1561   # Is it in a 5 membered aromatic ring surrounded by two aromatic Nitrogens?
    1562   #
    1563   if (!($Atom->DoesAtomNeighborhoodMatch('C.Ar.RA5.TR1', ['N.Ar.RA5.TR1.FC0', 'N.Ar.RA5.TR1.FC+1'], ['-', '=']) ||
    1564        $Atom->DoesAtomNeighborhoodMatch('C.Ar.RA5.TR1', ['N.Ar.RA5.TR1.!FC0', 'N.Ar.RA5.TR1.!FC0'], ['-', '=']))) {
    1565     return 0;
    1566   }
    1567
    1568   # Check to make sure ring contains only two Nitrogen hetero atom...
    1569   my($RingAtomsRef, $RingIsAromatic, $NumOfHeteroAtoms, $HeteroAtomSymbolsRef);
    1570
    1571   ($RingAtomsRef) = $Atom->GetRingsWithSize(5);
    1572   ($RingIsAromatic, $NumOfHeteroAtoms, $HeteroAtomSymbolsRef) = $This->_GetHeteroAtomsInformationInRing($RingAtomsRef);
    1573
    1574   if ($NumOfHeteroAtoms != 2) {
    1575     return 0;
    1576   }
    1577
    1578   return (exists($HeteroAtomSymbolsRef->{N}) && ($HeteroAtomSymbolsRef->{N} == 2)) ? 1 : 0;
    1579 }
    1580
    1581 # CNN+ : C IN +N=C-N RESONANCE STRUCTURES
    1582 #
    1583 sub _IsNCNResonaceStructuresCarbon {
    1584   my($This, $Atom) = @_;
    1585
    1586   # Match +1 formal charge on Nitrogen...
    1587   if ($Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['N.FC+1', 'N.FC0'], ['=', '-'])) {
    1588     return 1;
    1589   }
    1590
    1591   # Match non-zero (+1/2) formal charge on Nitrogen with single, double or resonnace bonds...
    1592   if ($Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['N.!FC0', 'N.!FC0'], ['-,=,:', '-,=,:'])) {
    1593     return 1;
    1594   }
    1595
    1596   return 0;
    1597 }
    1598
    1599 # CGD : GUANIDINE CARBON, DOUBLY BONDED TO N
    1600 #
    1601 sub _IsGuandineCarbon {
    1602   my($This, $Atom) = @_;
    1603
    1604   return $Atom->DoesAtomNeighborhoodMatch('C.X3.DB1', ['N.FC0', 'N.FC0', 'N.FC0'], ['=', '-', '-']) ? 1 : 0;
    1605 }
    1606
    1607 # CGD+ : GUANIDINIUM CARBON
    1608 #
    1609 sub _IsGuandiniumCarbon {
    1610   my($This, $Atom) = @_;
    1611
    1612   # Match +1 formal charge on a Nitrogen with explicitly single and double bonds...
    1613   if ($Atom->DoesAtomNeighborhoodMatch('C.X3.DB1', ['N.FC+1', 'N.FC0', 'N.FC0'], ['=', '-', '-'])) {
    1614     return 1;
    1615   }
    1616
    1617   # Match +1/3 formal charge on each Nitrogen with single, double or resonance bonds...
    1618   if ($Atom->DoesAtomNeighborhoodMatch('C.X3.DB1', ['N.!FC0', 'N.!FC0', 'N.!FC0'], ['-,=,:', '-,=,:', '-,=,:'])) {
    1619     return 1;
    1620   }
    1621
    1622   return 0;
    1623 }
    1624
    1625 # C=N : SP2 CARBON IN C=N
    1626 #
    1627 sub _IsDoublyBondedToNitrogenSP2Carbon {
    1628   my($This, $Atom) = @_;
    1629
    1630   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['N', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    1631 }
    1632
    1633 # CSP2 : GENERIC SP2 CARBON
    1634 #
    1635 sub _IsGenericSP2Carbon {
    1636   my($This, $Atom) = @_;
    1637
    1638   return $Atom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['*', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    1639 }
    1640
    1641 # =C= : ALLENIC CARBON
    1642 #
    1643 sub _IsAllenicCarbon {
    1644   my($This, $Atom) = @_;
    1645
    1646   return $Atom->DoesAtomNeighborhoodMatch('C.X2.DB2', ['*', '*'], ['=', '=']) ? 1 : 0;
    1647 }
    1648
    1649 # C% : ISONITRILE CARBON ( R-N+#C- )
    1650 #
    1651 sub _IsIsoNitrileCarbon {
    1652   my($This, $Atom) = @_;
    1653
    1654   return $Atom->DoesAtomNeighborhoodMatch('C.X1.TB1.FC-1', ['N.T2.TB1.FC+1'], ['#'], ['C,H']) ? 1 : 0;
    1655 }
    1656
    1657 # CSP : ACETYLENIC CARBON
    1658 #
    1659 sub _IsAcetylenicCarbon {
    1660   my($This, $Atom) = @_;
    1661
    1662   return $Atom->DoesAtomNeighborhoodMatch('C.T2.TB1', ['*', '*'], ['#', '-']) ? 1 : 0;
    1663 }
    1664
    1665 # Get MMFF94 atom type Nitrogen in five membered ring...
    1666 #
    1667 # Note:
    1668 #    . The method handles both SP3 and SP2 five membered ring Nitrogens.
    1669 #
    1670 sub _GetAtomTypeForFiveMemberedRingNitrogen {
    1671   my($This, $Atom) = @_;
    1672   my($AtomType);
    1673
    1674   $AtomType = 'None';
    1675
    1676   ATOMTYPE: {
    1677
    1678     # NIM+ : IMIDAZOLIUM-TYPE NITROGEN - FORMAL CHARGE=1/2
    1679     if ($This->_IsImidazoliumNitrogen($Atom)) {
    1680       $AtomType = 'NIM+';
    1681       last ATOMTYPE;
    1682     }
    1683
    1684     # N5A+ : POSITIVE N5A NITROGEN - FORMAL CHARGE=1
    1685     if ($This->_IsPositivelyChargedFiveMemberedHeteroAromaticRingAlphaNitrogen($Atom)) {
    1686       $AtomType = 'N5A+';
    1687       last ATOMTYPE;
    1688     }
    1689
    1690     # N5A : ALPHA AROM HETEROCYCLIC 5-RING  NITROGEN
    1691     if ($This->_IsFiveMemberedHeteroAromaticRingAlphaNitrogen($Atom)) {
    1692       $AtomType = 'N5A';
    1693       last ATOMTYPE;
    1694     }
    1695
    1696     # N5B+ : POSITIVE N5B NITROGEN - FORMAL CHARGE=1
    1697     if ($This->_IsPositivelyChargedFiveMemberedHeteroAromaticRingBetaNitrogen($Atom)) {
    1698       $AtomType = 'N5B+';
    1699       last ATOMTYPE;
    1700     }
    1701
    1702     # N5B : BETA AROM HETEROCYCLIC 5-RING  NITROGEN
    1703     if ($This->_IsFiveMemberedHeteroAromaticRingBetaNitrogen($Atom)) {
    1704       $AtomType = 'N5B';
    1705       last ATOMTYPE;
    1706     }
    1707
    1708     # N5AX : N-OXIDE NITROGEN IN 5-RING ALPHA POSITION
    1709     if ($This->_IsNOxideFiveMemberedHeteroCyclicRingAlphaNitrogen($Atom)) {
    1710       $AtomType = 'N5AX';
    1711       last ATOMTYPE;
    1712     }
    1713
    1714     # N5BX : N-OXIDE NITROGEN IN 5-RING BETA POSITION
    1715     if ($This->_IsNOxideFiveMemberedHeteroCyclicRingBetaNitrogen($Atom)) {
    1716       $AtomType = 'N5BX';
    1717       last ATOMTYPE;
    1718     }
    1719
    1720     # N5OX : N-OXIDE NITROGEN IN GENERAL 5-RING POSITION
    1721     if ($This->_IsNOxideFiveMemberedHeteroCyclicRingNitrogen($Atom)) {
    1722       $AtomType = 'N5OX';
    1723       last ATOMTYPE;
    1724     }
    1725
    1726     # N5M : NEGATIVELY CHARGED N IN, E.G, TRI- OR TETRAZOLE ANION
    1727     if ($This->_IsNegativelyChargedFiveMemberedHeteroCyclicRingNitrogen($Atom)) {
    1728       $AtomType = 'N5M';
    1729       last ATOMTYPE;
    1730     }
    1731
    1732     # N5+ : POSITIVE N5 NITROGEN - FORMAL CHARGE=1
    1733     if ($This->_IsPositivelyChargedFiveMemberedHeteroCyclicRingNitrogen($Atom)) {
    1734       $AtomType = 'N5+';
    1735       last ATOMTYPE;
    1736     }
    1737
    1738     # N5 : GENERAL NITROGEN IN 5-MEMBERED HETEROCYCLIC RING
    1739     if ($This->_IsFiveMemberedHeteroCyclicRingNitrogen($Atom)) {
    1740       $AtomType = 'N5';
    1741       last ATOMTYPE;
    1742     }
    1743
    1744     $AtomType = 'N5';
    1745   }
    1746
    1747   return $AtomType;
    1748 }
    1749
    1750 # Get MMFF94 atom type for Nitrogen with only sigma bonds...
    1751 #
    1752 sub _GetAtomTypeForNitrogenWithOnlySigmaBonds {
    1753   my($This, $Atom) = @_;
    1754   my($AtomType);
    1755
    1756   $AtomType = 'None';
    1757
    1758   ATOMTYPE: {
    1759
    1760     # NPYL : NITROGEN, AS IN PYRROLE
    1761     if ($This->_IsPyrroleNitrogen($Atom)) {
    1762       $AtomType = 'NPYL';
    1763       last ATOMTYPE;
    1764     }
    1765
    1766     # NC=O : NITROGEN IN AMIDES
    1767     if ($This->_IsAmideNitrogen($Atom)) {
    1768       $AtomType = 'NC=O';
    1769       last ATOMTYPE;
    1770     }
    1771
    1772     # NC=S : NITROGEN IN N-C=S, THIOAMIDE
    1773     if ($This->_IsThioAmideNitrogen($Atom)) {
    1774       $AtomType = 'NC=S';
    1775       last ATOMTYPE;
    1776     }
    1777
    1778     # NN=C : NITROGEN IN N-N=C
    1779     if ($This->_IsNNCNitrogen($Atom)) {
    1780       $AtomType = 'NN=C';
    1781       last ATOMTYPE;
    1782     }
    1783
    1784     # NGD+ : GUANIDINIUM-TYPE NITROGEN - FORMAL CHARGE=1/3
    1785     if ($This->_IsGuanidiniumNitrogen($Atom)) {
    1786       $AtomType = 'NGD+';
    1787       last ATOMTYPE;
    1788     }
    1789
    1790     # NCN+ :  N IN +N=C-N RESONANCE STRUCTURES - FORMAL CHARGE=1/2
    1791     if ($This->_IsNCNResonaceStructuresNitrogen($Atom)) {
    1792       $AtomType = 'NCN+';
    1793       last ATOMTYPE;
    1794     }
    1795
    1796     # NN=N : NITROGEN IN N-N=N
    1797     if ($This->_IsNNNNitrogen($Atom)) {
    1798       $AtomType = 'NN=N';
    1799       last ATOMTYPE;
    1800     }
    1801
    1802     #  NC=C : NITROGEN ON N-C=C
    1803     if ($This->_IsNCCNitrogen($Atom)) {
    1804       $AtomType = 'NC=C';
    1805       last ATOMTYPE;
    1806     }
    1807
    1808     # NC=N : NITROGEN IN N-C=N
    1809     if ($This->_IsNCNNitrogen($Atom)) {
    1810       $AtomType = 'NC=N';
    1811       last ATOMTYPE;
    1812     }
    1813
    1814     # NC=P : NITROGEN IN N-C=P
    1815     if ($This->_IsNCPNitrogen($Atom)) {
    1816       $AtomType = 'NC=P';
    1817       last ATOMTYPE;
    1818     }
    1819
    1820     # NM : DEPROTONATED SULFONAMIDE N-; FORMAL CHARGE=-1
    1821     if ($This->_IsDeprotonatedSulfonamideNitrogen($Atom)) {
    1822       $AtomType = 'NM';
    1823       last ATOMTYPE;
    1824     }
    1825
    1826     # NSO2 : NITROGEN IN SULFONAMIDES
    1827     if ($This->_IsNSO2SulfonamideNitrogen($Atom)) {
    1828       $AtomType = 'NSO2';
    1829       last ATOMTYPE;
    1830     }
    1831
    1832     # NSO3 : NITROGEN IN SULFONAMIDES, THREE Os ON S
    1833     if ($This->_IsNSO3SulfonamideNitrogen($Atom)) {
    1834       $AtomType = 'NSO3';
    1835       last ATOMTYPE;
    1836     }
    1837
    1838     # NPO2 : NITROGEN IN PHOSPHONAMIDES
    1839     if ($This->_IsNPO2PhosphonamideNitrogen($Atom)) {
    1840       $AtomType = 'NPO2';
    1841       last ATOMTYPE;
    1842     }
    1843
    1844     # NPO3 : NITROGEN IN PHOSPHONAMIDES, THREE Os ON P
    1845     if ($This->_IsNPO3PhosphonamideNitrogen($Atom)) {
    1846       $AtomType = 'NPO3';
    1847       last ATOMTYPE;
    1848     }
    1849
    1850     #  NC%N : NITROGEN ATTACHED TO CYANO GROUP
    1851     if ($This->_IsAttachedToCyanoNitrogen($Atom)) {
    1852       $AtomType = 'NC%N';
    1853       last ATOMTYPE;
    1854     }
    1855
    1856     # N3OX : SP3-HYDRIDIZED N-OXIDE NITROGEN
    1857     if ($This->_IsSP3NOxideNitrogen($Atom)) {
    1858       $AtomType = 'N3OX';
    1859       last ATOMTYPE;
    1860     }
    1861
    1862     #  NC%C : NITROGEN ATTACHED TO C-C TRIPLE BOND
    1863     if ($This->_IsAttchedToCCTripleBondNitrogen($Atom)) {
    1864       $AtomType = 'NC%C';
    1865       last ATOMTYPE;
    1866     }
    1867
    1868     # NR : NITROGEN IN ALIPHATIC AMINES
    1869     if ($This->_IsAliphaticAmineNitrogen($Atom)) {
    1870       $AtomType = 'NR';
    1871       last ATOMTYPE;
    1872     }
    1873
    1874     # NR+ : QUATERNARY NITROGEN, SP3, POSITIVELY CHARGED
    1875     if ($This->_IsAliphaticAmineQuaternaryNitrogen($Atom)) {
    1876       $AtomType = 'NR+';
    1877       last ATOMTYPE;
    1878     }
    1879
    1880     $AtomType = 'NR';
    1881   }
    1882
    1883   return $AtomType;
    1884 }
    1885
    1886 # Get MMFF94 atom type for Nitrogen with one pi bond...
    1887 #
    1888 sub _GetAtomTypeForNitrogenWithOnePiBond {
    1889   my($This, $Atom) = @_;
    1890   my($AtomType);
    1891
    1892   $AtomType = 'None';
    1893
    1894   ATOMTYPE: {
    1895
    1896     # NPOX : PYRIDINE N-OXIDE NITROGEN
    1897     if ($This->_IsPyridineNOxideNitrogen($Atom)) {
    1898       $AtomType = 'NPOX';
    1899       last ATOMTYPE;
    1900     }
    1901
    1902     # NPD+ : PYRIDINIUM-TYPE NITROGEN - FORMAL CHARGE=1
    1903     if ($This->_IsPyridiniumNitrogen($Atom)) {
    1904       $AtomType = 'NPD+';
    1905       last ATOMTYPE;
    1906     }
    1907
    1908     # NPYD : NITROGEN, AS IN PYRIDINE
    1909     if ($This->_IsPyridineNitrogen($Atom)) {
    1910       $AtomType = 'NPYD';
    1911       last ATOMTYPE;
    1912     }
    1913
    1914     # N2OX : SP2-HYDRIDIZED N-OXIDE NITROGEN
    1915     if ($This->_IsSP2NOxideNitrogen($Atom)) {
    1916       $AtomType = 'N2OX';
    1917       last ATOMTYPE;
    1918     }
    1919
    1920     # NAZT : TERMINAL NITROGEN IN AZIDO OR DIAZO GROUP
    1921     if ($This->_IsNAZTNitrogen($Atom)) {
    1922       $AtomType = 'NAZT';
    1923       last ATOMTYPE;
    1924     }
    1925
    1926     # NR% :  ISONITRILE NITROGEN [FC = 0] OR DIAZO NITROGEN [FC = 1]
    1927     if ($This->_IsIsoNitrileOrDiAzoNitrogen($Atom)) {
    1928       $AtomType = 'NR%';
    1929       last ATOMTYPE;
    1930     }
    1931
    1932     # NGD+ : GUANIDINIUM-TYPE NITROGEN - FORMAL CHARGE=1/3
    1933     if ($This->_IsGuanidiniumNitrogen($Atom)) {
    1934       $AtomType = 'NGD+';
    1935       last ATOMTYPE;
    1936     }
    1937
    1938     # NCN+ :  N IN +N=C-N RESONANCE STRUCTURES - FORMAL CHARGE=1/2
    1939     if ($This->_IsNCNResonaceStructuresNitrogen($Atom)) {
    1940       $AtomType = 'NCN+';
    1941       last ATOMTYPE;
    1942     }
    1943
    1944     # N=C : NITROGEN IN IMINES
    1945     if ($This->_IsImineNitrogen($Atom)) {
    1946       $AtomType = 'N=C';
    1947       last ATOMTYPE;
    1948     }
    1949
    1950     # N+=C : POSITIVELY CHARGED IMINIUM NITROGEN
    1951     if ($This->_IsPositivelyChargedIminiumNitrogen($Atom)) {
    1952       $AtomType = 'N+=C';
    1953       last ATOMTYPE;
    1954     }
    1955
    1956     # N=N :  NITROGEN IN AZO COMPOUNDS
    1957     if ($This->_IsAzoNitrogen($Atom)) {
    1958       $AtomType = 'N=N';
    1959       last ATOMTYPE;
    1960     }
    1961
    1962     # N+=N : POSITIVELY CHARGED NITROGEN DOUBLE-BONDED TO N
    1963     if ($This->_IsPositivelyChargedAzoNitrogen($Atom)) {
    1964       $AtomType = 'N+=N';
    1965       last ATOMTYPE;
    1966     }
    1967
    1968     # NO2 : NITRO GROUP NITROGEN
    1969     if ($This->_IsNitroNitrogen($Atom)) {
    1970       $AtomType = 'NO2';
    1971       last ATOMTYPE;
    1972     }
    1973
    1974     # NO3 : NITRATE GROUP NITROGEN
    1975     if ($This->_IsNitrateNitrogen($Atom)) {
    1976       $AtomType = 'NO3';
    1977       last ATOMTYPE;
    1978     }
    1979
    1980     # N=O : NITROSO NITROGEN
    1981     if ($This->_IsNitrosoNitrogen($Atom)) {
    1982       $AtomType = 'N=O';
    1983       last ATOMTYPE;
    1984     }
    1985
    1986     # NSO : DIVALENT NITROGEN REPLACING MONOVALENT O IN SO2 GROUP
    1987     if ($This->_IsNSONitrogen($Atom)) {
    1988       $AtomType = 'NSO';
    1989       last ATOMTYPE;
    1990     }
    1991
    1992     $AtomType = 'None';
    1993     carp "Warning: ${ClassName}->_GetAtomTypeForNitrogenWithOnePiBond: MMFF94 atom type for Nitrogen cann't be assigned; Default MMFF94 atom type, $AtomType, has been assigned...\n";
    1994   }
    1995
    1996   return $AtomType;
    1997 }
    1998
    1999 # Get MMFF94 atom type for Nitrogen with two pi bonds...
    2000 #
    2001 sub _GetAtomTypeForNitrogenWithTwoPiBonds {
    2002   my($This, $Atom) = @_;
    2003   my($AtomType);
    2004
    2005   $AtomType = 'None';
    2006
    2007   ATOMTYPE: {
    2008
    2009     # NAZT : TERMINAL NITROGEN IN AZIDO OR DIAZO GROUP
    2010     if ($This->_IsNAZTNitrogen($Atom)) {
    2011       $AtomType = 'NAZT';
    2012       last ATOMTYPE;
    2013     }
    2014
    2015     # NR% :  ISONITRILE NITROGEN [FC = 0] OR DIAZO NITROGEN [FC = 1]
    2016     if ($This->_IsIsoNitrileOrDiAzoNitrogen($Atom)) {
    2017       $AtomType = 'NR%';
    2018       last ATOMTYPE;
    2019     }
    2020
    2021     #  =N= : NITROGEN IN C=N=N OR -N=N=N
    2022     if ($This->_IsTwoDoublyBondedNitrogen($Atom)) {
    2023       $AtomType = '=N=';
    2024       last ATOMTYPE;
    2025     }
    2026
    2027     # NSP : NITROGEN, TRIPLE BONDED
    2028     if ($This->_IsTriplyBondedSPNitrogen($Atom)) {
    2029       $AtomType = 'NSP';
    2030       last ATOMTYPE;
    2031     }
    2032
    2033     $AtomType = 'NSP';
    2034   }
    2035
    2036   return $AtomType;
    2037 }
    2038
    2039 # NR : NITROGEN IN ALIPHATIC AMINES
    2040 #
    2041 sub _IsAliphaticAmineNitrogen {
    2042   my($This, $Atom) = @_;
    2043
    2044   return $Atom->DoesAtomNeighborhoodMatch('N.T3', ['C,H', 'C,H', 'C,H'], ['-', '-','-']) ? 1 : 0;
    2045 }
    2046
    2047 # NR+ : QUATERNARY NITROGEN, SP3, POSITIVELY CHARGED
    2048 #
    2049 sub _IsAliphaticAmineQuaternaryNitrogen {
    2050   my($This, $Atom) = @_;
    2051
    2052   return $Atom->DoesAtomNeighborhoodMatch('N.T4.FC+1', ['C,H', 'C,H', 'C,H', 'C,H'], ['-', '-','-','-']) ? 1 : 0;
    2053 }
    2054
    2055 # NC=O : NITROGEN IN AMIDES
    2056 #
    2057 sub _IsAmideNitrogen {
    2058   my($This, $Atom) = @_;
    2059   my($AtomNeighbor);
    2060
    2061   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2062     return 0;
    2063   }
    2064
    2065   # Is it attached to amide carbonyl Carbon?
    2066   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.T3.DB1')) {
    2067     if ($This->_IsAmideCarbonylCarbon($AtomNeighbor)) {
    2068       return 1;
    2069     }
    2070   }
    2071   return 0;
    2072 }
    2073
    2074 # NC=S : NITROGEN IN N-C=S, THIOAMIDE
    2075 #
    2076 sub _IsThioAmideNitrogen {
    2077   my($This, $Atom) = @_;
    2078   my($AtomNeighbor);
    2079
    2080   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2081     return 0;
    2082   }
    2083
    2084   # Is it attached to thioamide Carbon?
    2085   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.T3.DB1')) {
    2086     if ($This->_IsThioAmideCarbon($AtomNeighbor)) {
    2087       return 1;
    2088     }
    2089   }
    2090   return 0;
    2091 }
    2092
    2093 # NN=C : NITROGEN IN N-N=C
    2094 #
    2095 sub _IsNNCNitrogen {
    2096   my($This, $Atom) = @_;
    2097   my($AtomNeighbor);
    2098
    2099   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2100     return 0;
    2101   }
    2102
    2103   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('N.DB1')) {
    2104     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('N.T2.DB1', ['C.T3', 'N.T3'], ['=', '-'])) {
    2105       return 1;
    2106     }
    2107   }
    2108   return 0;
    2109 }
    2110
    2111 # NN=N : NITROGEN IN N-N=N
    2112 #
    2113 sub _IsNNNNitrogen {
    2114   my($This, $Atom) = @_;
    2115   my($AtomNeighbor);
    2116
    2117   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2118     return 0;
    2119   }
    2120
    2121   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('N.DB1')) {
    2122     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('N.T2.DB1', ['N.T2', 'N.T3'], ['=', '-'])) {
    2123       return 1;
    2124     }
    2125   }
    2126   return 0;
    2127 }
    2128
    2129 # NPYD : NITROGEN, AS IN PYRIDINE
    2130 #
    2131 sub _IsPyridineNitrogen {
    2132   my($This, $Atom) = @_;
    2133
    2134   # Is it an aromatic Nitrogen in only one six membered ring?
    2135   if (!$Atom->DoesAtomNeighborhoodMatch('N.Ar.RA6.TR1')) {
    2136     return 0;
    2137   }
    2138
    2139   # Is it part of six membered ring containing only one hetero atom?
    2140   my($RingAtomsRef, $RingIsAromatic, $NumOfHeteroAtoms);
    2141   for $RingAtomsRef ($Atom->GetRingsWithSize(6)) {
    2142     ($RingIsAromatic, $NumOfHeteroAtoms) = $This->_GetHeteroAtomsInformationInRing($RingAtomsRef);
    2143     if ($RingIsAromatic && $NumOfHeteroAtoms == 1) {
    2144       return 1;
    2145     }
    2146   }
    2147   return 0;
    2148 }
    2149
    2150 # NPYL : NITROGEN, AS IN PYRROLE
    2151 #
    2152 sub _IsPyrroleNitrogen {
    2153   my($This, $Atom) = @_;
    2154
    2155   # Is it an aromatic Nitrogen in only one five membered ring?
    2156   if (!$Atom->DoesAtomNeighborhoodMatch('N.Ar.RA5.TR1')) {
    2157     return 0;
    2158   }
    2159
    2160   # Is it part of five membered ring containing only one hetero atom?
    2161   my($RingAtomsRef, $RingIsAromatic, $NumOfHeteroAtoms);
    2162   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    2163     ($RingIsAromatic, $NumOfHeteroAtoms) = $This->_GetHeteroAtomsInformationInRing($RingAtomsRef);
    2164     if ($RingIsAromatic && $NumOfHeteroAtoms == 1) {
    2165       return 1;
    2166     }
    2167   }
    2168   return 0;
    2169 }
    2170
    2171 #  NC=C : NITROGEN ON N-C=C
    2172 #
    2173 sub _IsNCCNitrogen {
    2174   my($This, $Atom) = @_;
    2175   my($AtomNeighbor);
    2176
    2177   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2178     return 0;
    2179   }
    2180
    2181   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    2182     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.T3.DB1', ['C.T3', 'N.T3', '*'], ['=', '-','-'])) {
    2183       return 1;
    2184     }
    2185   }
    2186   return 0;
    2187 }
    2188
    2189 # NC=N : NITROGEN IN N-C=N
    2190 #
    2191 sub _IsNCNNitrogen {
    2192   my($This, $Atom) = @_;
    2193   my($AtomNeighbor);
    2194
    2195   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2196     return 0;
    2197   }
    2198
    2199   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    2200     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.T3.DB1', ['N.T2', 'N.T3', '*'], ['=', '-','-'])) {
    2201       return 1;
    2202     }
    2203   }
    2204   return 0;
    2205 }
    2206
    2207 # NC=P : NITROGEN IN N-C=P
    2208 #
    2209 sub _IsNCPNitrogen {
    2210   my($This, $Atom) = @_;
    2211   my($AtomNeighbor);
    2212
    2213   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2214     return 0;
    2215   }
    2216
    2217   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    2218     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.T3.DB1', ['P.X1', 'N.T3', '*'], ['=', '-', '-'])) {
    2219       return 1;
    2220     }
    2221   }
    2222   return 0;
    2223 }
    2224
    2225 #  NC%C : NITROGEN ATTACHED TO C-C TRIPLE BOND
    2226 #
    2227 sub _IsAttchedToCCTripleBondNitrogen {
    2228   my($This, $Atom) = @_;
    2229   my($AtomNeighbor);
    2230
    2231   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2232     return 0;
    2233   }
    2234
    2235   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.TB1')) {
    2236     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.X2.TB1', ['C.T2', 'N.T3'], ['#', '-'])) {
    2237       return 1;
    2238     }
    2239   }
    2240   return 0;
    2241 }
    2242
    2243 # NSO2 : NITROGEN IN SULFONAMIDES
    2244 #
    2245 sub _IsNSO2SulfonamideNitrogen {
    2246   my($This, $Atom) = @_;
    2247   my($AtomNeighbor);
    2248
    2249   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2250     return 0;
    2251   }
    2252
    2253   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T4.DB2')) {
    2254     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('S.T4.DB2', ['N', 'O', 'O', '!O'], ['-', '=', '=', '-'])) {
    2255       return 1;
    2256     }
    2257   }
    2258   return 0;
    2259 }
    2260
    2261 # NSO3 : NITROGEN IN SULFONAMIDES, THREE Os ON S
    2262 #
    2263 sub _IsNSO3SulfonamideNitrogen {
    2264   my($This, $Atom) = @_;
    2265   my($AtomNeighbor);
    2266
    2267   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2268     return 0;
    2269   }
    2270
    2271   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T4.DB2')) {
    2272     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('S.X4.DB2', ['N', 'O', 'O', 'O'], ['-', '=', '=', '-'])) {
    2273       return 1;
    2274     }
    2275   }
    2276   return 0;
    2277 }
    2278
    2279 # NPO2 : NITROGEN IN PHOSPHONAMIDES
    2280 #
    2281 sub _IsNPO2PhosphonamideNitrogen {
    2282   my($This, $Atom) = @_;
    2283   my($AtomNeighbor);
    2284
    2285   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2286     return 0;
    2287   }
    2288
    2289   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('P.DB1')) {
    2290     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('P.T4.DB1', ['N', 'O', 'O', '!O'], ['-', '=', '-', '-'])) {
    2291       return 1;
    2292     }
    2293   }
    2294   return 0;
    2295 }
    2296
    2297 # NPO3 : NITROGEN IN PHOSPHONAMIDES, THREE Os ON P
    2298 #
    2299 sub _IsNPO3PhosphonamideNitrogen {
    2300   my($This, $Atom) = @_;
    2301   my($AtomNeighbor);
    2302
    2303   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2304     return 0;
    2305   }
    2306
    2307   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('P.DB1')) {
    2308     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('P.X4.DB1', ['N', 'O', 'O', 'O'], ['-', '=', '-', '-'])) {
    2309       return 1;
    2310     }
    2311   }
    2312   return 0;
    2313 }
    2314
    2315 #  NC%N : NITROGEN ATTACHED TO CYANO GROUP
    2316 #
    2317 sub _IsAttachedToCyanoNitrogen {
    2318   my($This, $Atom) = @_;
    2319   my($AtomNeighbor);
    2320
    2321   if (!$Atom->DoesAtomNeighborhoodMatch('N.T3.TSB3')) {
    2322     return 0;
    2323   }
    2324
    2325   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.TB1')) {
    2326     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.T2.TB1', ['N', 'N'], ['-', '#'])) {
    2327       return 1;
    2328     }
    2329   }
    2330   return 0;
    2331 }
    2332
    2333 # NM : DEPROTONATED SULFONAMIDE N-; FORMAL CHARGE=-1
    2334 #
    2335 sub _IsDeprotonatedSulfonamideNitrogen {
    2336   my($This, $Atom) = @_;
    2337   my($AtomNeighbor);
    2338
    2339   if (!$Atom->DoesAtomNeighborhoodMatch('N.T2.FC-1.TSB2')) {
    2340     return 0;
    2341   }
    2342
    2343   # Is it attached to sulfonamide Sulfur?
    2344   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T4.DB2')) {
    2345     if ($This->_IsSulfonamideSulfur($AtomNeighbor)) {
    2346       return 1;
    2347     }
    2348   }
    2349   return 0;
    2350 }
    2351
    2352 # N=C : NITROGEN IN IMINES
    2353 #
    2354 sub _IsImineNitrogen {
    2355   my($This, $Atom) = @_;
    2356
    2357   return $Atom->DoesAtomNeighborhoodMatch('N.T2.DB1', ['C', '*'], ['=', '-']) ? 1 : 0;
    2358 }
    2359
    2360 # N+=C : POSITIVELY CHARGED IMINIUM NITROGEN
    2361 #
    2362 sub _IsPositivelyChargedIminiumNitrogen {
    2363   my($This, $Atom) = @_;
    2364
    2365   return $Atom->DoesAtomNeighborhoodMatch('N.DB1.FC+1', ['C', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    2366 }
    2367
    2368 # N=N :  NITROGEN IN AZO COMPOUNDS
    2369 #
    2370 sub _IsAzoNitrogen {
    2371   my($This, $Atom) = @_;
    2372
    2373   return $Atom->DoesAtomNeighborhoodMatch('N.T2.DB1', ['N', '*'], ['=', '-']) ? 1 : 0;
    2374 }
    2375
    2376 # N+=N : POSITIVELY CHARGED NITROGEN DOUBLE-BONDED TO N
    2377 #
    2378 sub _IsPositivelyChargedAzoNitrogen {
    2379   my($This, $Atom) = @_;
    2380
    2381   return $Atom->DoesAtomNeighborhoodMatch('N.DB1.FC+1', ['N', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    2382 }
    2383
    2384 # NO2 : NITRO GROUP NITROGEN
    2385 #
    2386 sub _IsNitroNitrogen {
    2387   my($This, $Atom) = @_;
    2388
    2389   # R-N+(=O)-(O-)
    2390   return $Atom->DoesAtomNeighborhoodMatch('N.T3.DB1.FC+1', ['O', 'O.FC-1', '!O'], ['=', '-', '-']) ? 1 : 0;
    2391 }
    2392
    2393 # NO3 : NITRATE GROUP NITROGEN
    2394 #
    2395 sub _IsNitrateNitrogen {
    2396   my($This, $Atom) = @_;
    2397
    2398   # (O-)-N+(=O)-(O-) or R-O-N+(=O)-(O-)
    2399   #
    2400   return $Atom->DoesAtomNeighborhoodMatch('N.T3.DB1.FC+1', ['O', 'O.FC-1', 'O.T2.FC0'], ['=', '-', '-']) ? 1 : 0;
    2401 }
    2402
    2403 # N=O : NITROSO NITROGEN
    2404 #
    2405 sub _IsNitrosoNitrogen {
    2406   my($This, $Atom) = @_;
    2407
    2408   # R-N=O
    2409   return $Atom->DoesAtomNeighborhoodMatch('N.T2.DB1', ['O'], ['=']) ? 1 : 0;
    2410 }
    2411
    2412 # NAZT : TERMINAL NITROGEN IN AZIDO OR DIAZO GROUP
    2413 #
    2414 sub _IsNAZTNitrogen {
    2415   my($This, $Atom) = @_;
    2416
    2417   return ($This->_IsAzidoTerminalNitrogen($Atom) || $This->_IsDiazoTerminalNitrogen($Atom)) ? 1 : 0;
    2418 }
    2419
    2420 # TERMINAL NITROGEN IN AZIDO GROUP
    2421 #
    2422 sub _IsAzidoTerminalNitrogen {
    2423   my($This, $Atom) = @_;
    2424   my($AtomNeighbor);
    2425
    2426   if (!$Atom->DoesAtomNeighborhoodMatch('N.X1.DB1')) {
    2427     return 0;
    2428   }
    2429
    2430   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('N.DB2')) {
    2431     if ($This->_IsAzidoGroupMiddleNitrogen($AtomNeighbor)) {
    2432       return 1;
    2433     }
    2434   }
    2435   return 0;
    2436 }
    2437
    2438 # TERMINAL NITROGEN IN  DIAZO GROUP
    2439 #
    2440 sub _IsDiazoTerminalNitrogen {
    2441   my($This, $Atom) = @_;
    2442   my($AtomNeighbor);
    2443
    2444   if (!$Atom->DoesAtomNeighborhoodMatch('N.X1.TB1,N.X1.DB1')) {
    2445     return 0;
    2446   }
    2447
    2448   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('N.DB2,N.TB1')) {
    2449     if ($This->_IsDiazoGroupMiddleNitrogen($AtomNeighbor)) {
    2450       return 1;
    2451     }
    2452   }
    2453   return 0;
    2454 }
    2455
    2456 # Azido group: R-N=N+=N-
    2457 #
    2458 sub _IsAzidoGroupMiddleNitrogen {
    2459   my($This, $Atom) = @_;
    2460
    2461   return $Atom->DoesAtomNeighborhoodMatch('N.X2.DB2.FC+1', ['N.X1.FC-1', 'N.T2.FC0'], ['=', '=']) ? 1 : 0;
    2462 }
    2463
    2464 # Diazo group: R'-(C-)(-R")-(N+)#N or R'-C(-R")=(N+)=(N-)
    2465 #
    2466 sub _IsDiazoGroupMiddleNitrogen {
    2467   my($This, $Atom) = @_;
    2468
    2469   # Diazo group: R'-C(-R")=(N+)=(N-)
    2470   if ($Atom->DoesAtomNeighborhoodMatch('N.X2.DB2.FC+1', ['C.T3.FC0', 'N.X1.FC-1'], ['=', '='])) {
    2471     return 1;
    2472   }
    2473
    2474   # Diazo group: R'-(C-)(-R")-(N+)#N
    2475   if ($Atom->DoesAtomNeighborhoodMatch('N.X2.TB1.FC+1', ['C.T3.FC-1', 'N.X1.FC0'], ['-', '#'])) {
    2476     return 1;
    2477   }
    2478
    2479   return 0;
    2480 }
    2481
    2482 # NSO : DIVALENT NITROGEN REPLACING MONOVALENT O IN SO2 GROUP
    2483 #
    2484 sub _IsNSONitrogen {
    2485   my($This, $Atom) = @_;
    2486   my($AtomNeighbor);
    2487
    2488   if (!$Atom->DoesAtomNeighborhoodMatch('N.T2.DB1')) {
    2489     return 0;
    2490   }
    2491
    2492   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.DB2')) {
    2493     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('S.DB2', ['N', 'O'], ['=', '='])) {
    2494       return 1;
    2495     }
    2496   }
    2497   return 0;
    2498 }
    2499
    2500 # NCN+ :  N IN +N=C-N RESONANCE STRUCTURES - FORMAL CHARGE=1/2
    2501 #
    2502 # Notes:
    2503 #    . This method is called invoked for both SP and SP2 Nitrogens to check and assign
    2504 #      NCN+ to both Nitrogens.
    2505 #
    2506 sub _IsNCNResonaceStructuresNitrogen {
    2507   my($This, $Atom) = @_;
    2508   my($AtomNeighbor);
    2509
    2510   if (!$Atom->DoesAtomNeighborhoodMatch('N')) {
    2511     return 0;
    2512   }
    2513
    2514   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    2515     if ($This->_IsNCNResonaceStructuresCarbon($AtomNeighbor)) {
    2516       return 1;
    2517     }
    2518   }
    2519   return 0;
    2520 }
    2521
    2522 # NGD+ : GUANIDINIUM-TYPE NITROGEN - FORMAL CHARGE=1/3
    2523 #
    2524 # Notes:
    2525 #    . This method is called invoked for both SP and SP2 Nitrogens to check and assign
    2526 #      NGD+ to all three Guanidinium Nitrogens.
    2527 #
    2528 sub _IsGuanidiniumNitrogen {
    2529   my($This, $Atom) = @_;
    2530   my($AtomNeighbor);
    2531
    2532   if (!$Atom->DoesAtomNeighborhoodMatch('N')) {
    2533     return 0;
    2534   }
    2535
    2536   # Is it attached to Guandinium Carbon?
    2537   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    2538     if ($This->_IsGuandiniumCarbon($AtomNeighbor)) {
    2539       return 1;
    2540     }
    2541   }
    2542   return 0;
    2543 }
    2544
    2545 # NPD+ : PYRIDINIUM-TYPE NITROGEN - FORMAL CHARGE=1
    2546 #
    2547 sub _IsPyridiniumNitrogen {
    2548   my($This, $Atom) = @_;
    2549
    2550   return ($This->_IsPyridineNitrogen($Atom) && $Atom->DoesAtomNeighborhoodMatch('N.FC+1')) ? 1 : 0;
    2551 }
    2552
    2553 # NR% :  ISONITRILE NITROGEN [FC = 0] OR DIAZO NITROGEN [FC = 1]
    2554 #
    2555 # Notes:
    2556 #    . This method is called invoked for both SP and SP2 Nitrogens to check and assign
    2557 #      NR%.
    2558 #
    2559 sub _IsIsoNitrileOrDiAzoNitrogen {
    2560   my($This, $Atom) = @_;
    2561
    2562   return ($This->_IsIsoNitrileNitrogen($Atom) || $This->_IsDiAzoNitrogen($Atom)) ? 1 : 0;
    2563 }
    2564
    2565 # Isonitrile nitrogen: R-N+#C-
    2566 #
    2567 # Notes:
    2568 #    . MayaChemTools assumes isonitrile Nitrogen to have a formal charge of +1; otherwise
    2569 #      it won't be an isonitrile group. It's not clear why it would be zero as indicated in
    2570 #      MMFF documentation.
    2571 #
    2572 sub _IsIsoNitrileNitrogen {
    2573   my($This, $Atom) = @_;
    2574   my($AtomNeighbor);
    2575
    2576   if (!$Atom->DoesAtomNeighborhoodMatch('N.FC+1.TB1')) {
    2577     return 0;
    2578   }
    2579
    2580   # Is it attached to isonitrile Carbon?
    2581   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.TB1')) {
    2582     if ($This->_IsIsoNitrileCarbon($AtomNeighbor)) {
    2583       return 1;
    2584     }
    2585   }
    2586   return 0;
    2587 }
    2588
    2589 # Diazo group: R1-(C-)(-R2)-(N+)#N or R1-C(-R2)=(N+)=(N-)
    2590 #
    2591 sub _IsDiAzoNitrogen {
    2592   my($This, $Atom) = @_;
    2593   my($AtomNeighbor);
    2594
    2595   return $This->_IsDiazoGroupMiddleNitrogen($Atom) ? 1 : 0;
    2596 }
    2597
    2598 # N5A : ALPHA AROM HETEROCYCLIC 5-RING  NITROGEN
    2599 #
    2600 sub _IsFiveMemberedHeteroAromaticRingAlphaNitrogen {
    2601   my($This, $Atom) = @_;
    2602
    2603   # Is it an aromatic atom in a five membered ring?
    2604   if (!$Atom->DoesAtomNeighborhoodMatch('N.Ar.RA5.FC0')) {
    2605     return 0;
    2606   }
    2607
    2608   # Is it part of five membered rings containing hetero atom at alpha position?
    2609   my($RingAtomsRef);
    2610
    2611   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    2612     if ($This->_IsAtomPositionAlphaInHeteroAromaticRing($Atom, $RingAtomsRef)) {
    2613       return 1;
    2614     }
    2615   }
    2616   return 0;
    2617 }
    2618
    2619 # N5B : BETA AROM HETEROCYCLIC 5-RING  NITROGEN
    2620 #
    2621 sub _IsFiveMemberedHeteroAromaticRingBetaNitrogen {
    2622   my($This, $Atom) = @_;
    2623
    2624   # Is it an aromatic atom in a five membered ring?
    2625   if (!$Atom->DoesAtomNeighborhoodMatch('N.Ar.RA5.FC0')) {
    2626     return 0;
    2627   }
    2628
    2629   # Is it part of five membered rings containing hetero atom at beta position?
    2630   my($RingAtomsRef);
    2631
    2632   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    2633     if ($This->_IsAtomPositionBetaInHeteroAromaticRing($Atom, $RingAtomsRef)) {
    2634       return 1;
    2635     }
    2636   }
    2637   return 0;
    2638 }
    2639
    2640 # N5A+ : POSITIVE N5A NITROGEN - FORMAL CHARGE=1
    2641 #
    2642 sub _IsPositivelyChargedFiveMemberedHeteroAromaticRingAlphaNitrogen {
    2643   my($This, $Atom) = @_;
    2644
    2645   # Is it an aromatic atom in a five membered ring?
    2646   if (!$Atom->DoesAtomNeighborhoodMatch('N.Ar.RA5.FC+1')) {
    2647     return 0;
    2648   }
    2649
    2650   # Is it part of five membered rings containing hetero atom at alpha position?
    2651   my($RingAtomsRef);
    2652
    2653   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    2654     if ($This->_IsAtomPositionAlphaInHeteroAromaticRing($Atom, $RingAtomsRef)) {
    2655       return 1;
    2656     }
    2657   }
    2658   return 0;
    2659 }
    2660
    2661 # N5B+ : POSITIVE N5B NITROGEN - FORMAL CHARGE=1
    2662 #
    2663 sub _IsPositivelyChargedFiveMemberedHeteroAromaticRingBetaNitrogen {
    2664   my($This, $Atom) = @_;
    2665
    2666   # Is it an aromatic atom in a five membered ring?
    2667   if (!$Atom->DoesAtomNeighborhoodMatch('N.Ar.RA5.FC+1')) {
    2668     return 0;
    2669   }
    2670
    2671   # Is it part of five membered rings containing hetero atom at beta position?
    2672   my($RingAtomsRef);
    2673
    2674   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    2675     if ($This->_IsAtomPositionBetaInHeteroAromaticRing($Atom, $RingAtomsRef)) {
    2676       return 1;
    2677     }
    2678   }
    2679   return 0;
    2680 }
    2681
    2682 # N2OX : SP2-HYDRIDIZED N-OXIDE NITROGEN
    2683 #
    2684 sub _IsSP2NOxideNitrogen {
    2685   my($This, $Atom) = @_;
    2686
    2687   # R=(R'-)(N+)-(O-)
    2688   #
    2689   return $Atom->DoesAtomNeighborhoodMatch('N.FC+1.DB1.T3', ['O.X1.FC-1', 'C', '*'], ['-', '=', '-']) ? 1 : 0;
    2690 }
    2691
    2692 # N3OX : SP3-HYDRIDIZED N-OXIDE NITROGEN
    2693 #
    2694 sub _IsSP3NOxideNitrogen {
    2695   my($This, $Atom) = @_;
    2696
    2697   # R-(R'-)(R"-)(N+)-(O-)
    2698   #
    2699   return $Atom->DoesAtomNeighborhoodMatch('N.FC+1.T4', ['O.X1.FC-1', '*', '*', '*'], ['-', '-', '-', '-']) ? 1 : 0;
    2700 }
    2701
    2702 # NPOX : PYRIDINE N-OXIDE NITROGEN
    2703 #
    2704 sub _IsPyridineNOxideNitrogen {
    2705   my($This, $Atom) = @_;
    2706
    2707   return ($This->_IsPyridineNitrogen($Atom) && $This->_IsSP2NOxideNitrogen($Atom)) ? 1 : 0;
    2708 }
    2709
    2710 # N5M : NEGATIVELY CHARGED N IN, E.G, TRI- OR TETRAZOLE ANION
    2711 #
    2712 sub _IsNegativelyChargedFiveMemberedHeteroCyclicRingNitrogen {
    2713   my($This, $Atom) = @_;
    2714
    2715   return $Atom->DoesAtomNeighborhoodMatch('N.RA5.FC-1') ? 1 : 0;
    2716 }
    2717
    2718 # N5 : GENERAL NITROGEN IN 5-MEMBERED HETEROCYCLIC RING
    2719 #
    2720 sub _IsFiveMemberedHeteroCyclicRingNitrogen {
    2721   my($This, $Atom) = @_;
    2722
    2723   return $Atom->DoesAtomNeighborhoodMatch('N.RA5') ? 1 : 0;
    2724 }
    2725
    2726 # N5+ : POSITIVE N5 NITROGEN - FORMAL CHARGE=1
    2727 #
    2728 sub _IsPositivelyChargedFiveMemberedHeteroCyclicRingNitrogen {
    2729   my($This, $Atom) = @_;
    2730
    2731   return $Atom->DoesAtomNeighborhoodMatch('N.RA5.FC+1') ? 1 : 0;
    2732 }
    2733
    2734 # N5AX : N-OXIDE NITROGEN IN 5-RING ALPHA POSITION
    2735 #
    2736 sub _IsNOxideFiveMemberedHeteroCyclicRingAlphaNitrogen {
    2737   my($This, $Atom) = @_;
    2738
    2739   # Is it a N-oxide Nitrogen atom in a five membered ring?
    2740   if (!$Atom->DoesAtomNeighborhoodMatch('N.RA5.FC+1', ['O.X1.FC-1.!RA'], ['-'])) {
    2741     return 0;
    2742   }
    2743
    2744   # Is it part of five membered rings containing hetero atom at alpha position?
    2745   my($RingAtomsRef);
    2746
    2747   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    2748     if ($This->_IsAtomPositionAlphaInHeteroRing($Atom, $RingAtomsRef)) {
    2749       return 1;
    2750     }
    2751   }
    2752   return 0;
    2753 }
    2754
    2755 # N5BX : N-OXIDE NITROGEN IN 5-RING BETA POSITION
    2756 #
    2757 sub _IsNOxideFiveMemberedHeteroCyclicRingBetaNitrogen {
    2758   my($This, $Atom) = @_;
    2759
    2760   # Is it a N-oxide Nitrogen atom in a five membered ring?
    2761   if (!$Atom->DoesAtomNeighborhoodMatch('N.RA5.FC+1', ['O.X1.FC-1.!RA'], ['-'])) {
    2762     return 0;
    2763   }
    2764
    2765   # Is it part of five membered rings containing hetero atom at alpha position?
    2766   my($RingAtomsRef);
    2767
    2768   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    2769     if ($This->_IsAtomPositionBetaInHeteroRing($Atom, $RingAtomsRef)) {
    2770       return 1;
    2771     }
    2772   }
    2773   return 0;
    2774 }
    2775
    2776 # N5OX : N-OXIDE NITROGEN IN GENERAL 5-RING POSITION
    2777 #
    2778 sub _IsNOxideFiveMemberedHeteroCyclicRingNitrogen {
    2779   my($This, $Atom) = @_;
    2780
    2781   return $Atom->DoesAtomNeighborhoodMatch('N.FC+1.RA5', ['O.X1.FC-1.!RA'], ['-']) ? 1 : 0;
    2782 }
    2783
    2784 # NIM+ : IMIDAZOLIUM-TYPE NITROGEN - FORMAL CHARGE=1/2
    2785 #
    2786 # Notes:
    2787 #    . MayaChemTools assigns NIM+ to both the Nitrogens around Imidazolium Carbon.
    2788 #
    2789 sub _IsImidazoliumNitrogen {
    2790   my($This, $Atom) = @_;
    2791   my($AtomNeighbor);
    2792
    2793   # Is it an aromatic Nitrogen in only one five membered ring?
    2794   if (!$Atom->DoesAtomNeighborhoodMatch('N.Ar.RA5.TR1')) {
    2795     return 0;
    2796   }
    2797
    2798   # Is it attached to Imidazolium Carbon?
    2799   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.RA5')) {
    2800     if ($This->_IsImidazoliumCarbon($AtomNeighbor)) {
    2801       return 1;
    2802     }
    2803   }
    2804   return 0;
    2805 }
    2806
    2807 # =N= : NITROGEN IN C=N=N OR -N=N=N
    2808 #
    2809 # Notes:
    2810 #    . MayaChemTools assign =N= to all doubly bonded Nitrogen atoms
    2811 #      irrespective of whether it has explcit formal charge of +1.
    2812 #
    2813 sub _IsTwoDoublyBondedNitrogen {
    2814   my($This, $Atom) = @_;
    2815
    2816   if ($Atom->DoesAtomNeighborhoodMatch('N.DB2', ['N', 'N'], ['=', '='])) {
    2817     return 1;
    2818   }
    2819   if ($Atom->DoesAtomNeighborhoodMatch('N.DB2', ['C', 'N'], ['=', '='])) {
    2820     return 1;
    2821   }
    2822   return 0;
    2823 }
    2824
    2825 # NSP : NITROGEN, TRIPLE BONDED
    2826 #
    2827 sub _IsTriplyBondedSPNitrogen {
    2828   my($This, $Atom) = @_;
    2829
    2830   return $Atom->DoesAtomNeighborhoodMatch('N.TB1') ? 1 : 0;
    2831 }
    2832
    2833
    2834 # Get MMFF94 atom type for divalent or terminal Oxygen attached to Sulfur...
    2835 #
    2836 sub _GetAtomTypeForOxygenAttachedToSulfur {
    2837   my($This, $Atom) = @_;
    2838   my($AtomType);
    2839
    2840   $AtomType = 'None';
    2841
    2842   ATOMTYPE: {
    2843
    2844     # OSMS : TERM O IN THIOSULFINATE ANION - FORMAL CHARGE=-0.5
    2845     if ($This->_IsThioSulfinateTerminalOxygen($Atom)) {
    2846       $AtomType = 'OSMS';
    2847       last ATOMTYPE;
    2848     }
    2849
    2850     # OSO3 : DIVALENT OXYGEN ATTACHED TO SULFUR
    2851     if ($This->_IsOSO3DivalentOxygen($Atom)) {
    2852       $AtomType = 'OSO3';
    2853       last ATOMTYPE;
    2854     }
    2855
    2856     # OSO2 : DIVALENT OXYGEN ATTACHED TO SULFUR
    2857     if ($This->_IsOSO2DivalentOxygen($Atom)) {
    2858       $AtomType = 'OSO2';
    2859       last ATOMTYPE;
    2860     }
    2861
    2862     # OSO : DIVALENT OXYGEN ATTACHED TO SULFUR
    2863     if ($This->_IsOSODivalentOxygen($Atom)) {
    2864       $AtomType = 'OSO';
    2865       last ATOMTYPE;
    2866     }
    2867
    2868     # OS=O : DIVALENT OXYGEN ATTACHED TO SULFOXIDE SULFUR
    2869     if ($This->_IsSulfoxideDivalentOxygen($Atom)) {
    2870       $AtomType = 'OS=O';
    2871       last ATOMTYPE;
    2872     }
    2873
    2874     # -OS : GENERAL DIVALENT OXYGEN ATTACHED TO S
    2875     if ($This->_IsOSDivalentOxygen($Atom)) {
    2876       $AtomType = ' -OS';
    2877       last ATOMTYPE;
    2878     }
    2879
    2880     # O4S : TERMINAL O IN SO4(-3)
    2881     if ($This->_IsSulfateTerminalOxygen($Atom)) {
    2882       $AtomType = 'O4S';
    2883       last ATOMTYPE;
    2884     }
    2885
    2886     # O3S : TERMINAL O IN SULFONATES
    2887     if ($This->_IsSulfonateTerminalOxygen($Atom)) {
    2888       $AtomType = 'O3S';
    2889       last ATOMTYPE;
    2890     }
    2891
    2892     # O2S : TERMINAL O-S IN SULFONES AND SULFONAMIDES
    2893     if ($This->_IsSulfoneOrSulfonamideTerminalOxygen($Atom)) {
    2894       $AtomType = 'O2S';
    2895       last ATOMTYPE;
    2896     }
    2897
    2898     # O=S : O=S IN SULFOXIDES
    2899     if ($This->_IsSulfoxideOxygen($Atom)) {
    2900       $AtomType = 'O=S';
    2901       last ATOMTYPE;
    2902     }
    2903
    2904     # O=S= :  O=S ON SULFUR DOUBLY BONDED TO, E.G., CARBON
    2905     if ($This->_IsDoublyBondedOSOxygen($Atom)) {
    2906       $AtomType = 'O=S=';
    2907       last ATOMTYPE;
    2908     }
    2909
    2910     # O-S : SINGLE TERMINAL OXYGEN ON TETRACOORD SULFUR
    2911     if ($This->_IsOSTerminalOxygen($Atom)) {
    2912       $AtomType = 'O-S';
    2913       last ATOMTYPE;
    2914     }
    2915
    2916     $AtomType = 'None';
    2917     carp "Warning: ${ClassName}->_GetAtomTypeForOxygenAttachedToSulfur: MMFF94 atom type for Oxygen cann't be assigned...";
    2918   }
    2919
    2920   return $AtomType;
    2921 }
    2922
    2923 # Get MMFF94 atom type for divalent or terminal Oxygen attached to Phosphorus...
    2924 #
    2925 sub _GetAtomTypeForOxygenAttachedToPhosphorus {
    2926   my($This, $Atom) = @_;
    2927   my($AtomType);
    2928
    2929   $AtomType = 'None';
    2930
    2931   ATOMTYPE: {
    2932
    2933     # OPO3 : DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
    2934     if ($This->_IsOPO3DivalentOxygen($Atom)) {
    2935       $AtomType = 'OPO3';
    2936       last ATOMTYPE;
    2937     }
    2938
    2939     # OPO2 : DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
    2940     if ($This->_IsOPO2DivalentOxygen($Atom)) {
    2941       $AtomType = 'OPO2';
    2942       last ATOMTYPE;
    2943     }
    2944
    2945     # OPO : DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
    2946     if ($This->_IsOPODivalentOxygen($Atom)) {
    2947       $AtomType = 'OPO';
    2948       last ATOMTYPE;
    2949     }
    2950
    2951     # -OP : DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
    2952     if ($This->_IsOPDivalentOxygen($Atom)) {
    2953       $AtomType = '-OP';
    2954       last ATOMTYPE;
    2955     }
    2956
    2957     # O4P : TERMINAL OXYGEN IN PHOSPHATES AND PHOSPHODIESTERS
    2958     if ($This->_IsPhosphateOrPhosphodiesterTerminalOxygen($Atom)) {
    2959       $AtomType = 'O4P';
    2960       last ATOMTYPE;
    2961     }
    2962
    2963     # O3P : TERMINAL OXYGEN IN PHOSPHONATES
    2964     if ($This->_IsPhosphonateTerminalOxygen($Atom)) {
    2965       $AtomType = 'O3P';
    2966       last ATOMTYPE;
    2967     }
    2968
    2969     # O2P : TERMINAL O IN PHOSPHINATES
    2970     if ($This->_IsPhosphinateTerminalOxygen($Atom)) {
    2971       $AtomType = 'O2P';
    2972       last ATOMTYPE;
    2973     }
    2974
    2975     # OP : TERMINAL O IN PHOSPHOXIDES
    2976     if ($This->_IsPhosphoxideTerminalOxygen($Atom)) {
    2977       $AtomType = 'OP';
    2978       last ATOMTYPE;
    2979     }
    2980
    2981     $AtomType = 'None';
    2982     carp "Warning: ${ClassName}->_GetAtomTypeForOxygenAttachedToPhosphorus: MMFF94 atom type for Oxygen cann't be assigned...";
    2983   }
    2984
    2985   return $AtomType;
    2986 }
    2987
    2988 # Get MMFF94 atom type for Oxygen with only sigma bonds...
    2989 #
    2990 sub _GetAtomTypeForOxygenWithOnlySigmaBonds {
    2991   my($This, $Atom) = @_;
    2992   my($AtomType);
    2993
    2994   $AtomType = 'None';
    2995
    2996   ATOMTYPE: {
    2997
    2998     # OFUR : AROMATIC OXYGEN AS IN FURAN
    2999     if ($This->_IsAromaticOxygen($Atom)) {
    3000       $AtomType = 'OFUR';
    3001       last ATOMTYPE;
    3002     }
    3003
    3004     # OC=O : ESTER OR CARBOXYLIC ACID -O-
    3005     if ($This->_IsEsterOrCarboxylicAcidOxygen($Atom)) {
    3006       $AtomType = 'OC=O';
    3007       last ATOMTYPE;
    3008     }
    3009
    3010     # O2CM : OXYGEN IN CARBOXYLATE ANION
    3011     if ($This->_IsCarboxylateAnionOxygen($Atom)) {
    3012       $AtomType = 'O2CM';
    3013       last ATOMTYPE;
    3014     }
    3015
    3016     # OC=C : ENOLIC OR PHENOLIC OXYGEN
    3017     if ($This->_IsEnolicOrPhenolicOxygen($Atom)) {
    3018       $AtomType = 'OC=C ';
    3019       last ATOMTYPE;
    3020     }
    3021
    3022     # OC=N : DIVALENT OXYGEN
    3023     if ($This->_IsOCNDivalentOxygen($Atom)) {
    3024       $AtomType = 'OC=N';
    3025       last ATOMTYPE;
    3026     }
    3027
    3028     # OC=S : THIOESTER OR THIOACID -O-
    3029     if ($This->_IsThioEsterOrThioAcidOxygen($Atom)) {
    3030       $AtomType = 'OC=S';
    3031       last ATOMTYPE;
    3032     }
    3033
    3034     # ON=O : DIVALENT NITRITE ETHER OXYGEN
    3035     if ($This->_IsDivalentNitriteEtherOxygen($Atom)) {
    3036       $AtomType = 'ON=O';
    3037       last ATOMTYPE;
    3038     }
    3039
    3040     # ONO2 : DIVALENT NITRATE ETHER OXYGEN
    3041     if ($This->_IsDivalentNitrateEtherOxygen($Atom)) {
    3042       $AtomType = 'ONO2';
    3043       last ATOMTYPE;
    3044     }
    3045
    3046     # O3N : NITRATE ANION OXYGEN
    3047     if ($This->_IsNitrateAnionOxygen($Atom)) {
    3048       $AtomType = 'O3N';
    3049       last ATOMTYPE;
    3050     }
    3051
    3052     # O2N : NITRO OXYGEN
    3053     if ($This->_IsNitroOxygen($Atom)) {
    3054       $AtomType = 'O2N';
    3055       last ATOMTYPE;
    3056     }
    3057
    3058     # OXN : N-OXIDE OXYGEN
    3059     if ($This->_IsNOxideOxygen($Atom)) {
    3060       $AtomType = 'OXN';
    3061       last ATOMTYPE;
    3062     }
    3063
    3064     # OM2 : OXIDE OXYGEN ON SP2 CARBON, NEGATIVELY CHARGED
    3065     if ($This->_IsNegativelyChargedSP2OxideOxygen($Atom)) {
    3066       $AtomType = 'OM2';
    3067       last ATOMTYPE;
    3068     }
    3069
    3070     # OM : ALKOXIDE OXYGEN, NEGATIVELY CHARGED
    3071     if ($This->_IsNegativelyChargedAlkoxideOxygen($Atom)) {
    3072       $AtomType = 'OM';
    3073       last ATOMTYPE;
    3074     }
    3075
    3076     # O4CL : OXYGEN IN CLO4(-) ANION - FORMAL CHARGE=-0.25
    3077     if ($This->_IsPerChlorateAnionOxygen($Atom)) {
    3078       $AtomType = 'O4CL';
    3079       last ATOMTYPE;
    3080     }
    3081
    3082     # O+ : POSITIVELY CHARGED OXONIUM (TRICOORDINATE) OXYGEN
    3083     if ($This->_IsPositivelyChargedOxoniumOxygen($Atom)) {
    3084       $AtomType = 'O+';
    3085       last ATOMTYPE;
    3086     }
    3087
    3088     # OH2 : OXYGEN ON WATER
    3089     if ($This->_IsWaterOxygen($Atom)) {
    3090       $AtomType = 'OH2';
    3091       last ATOMTYPE;
    3092     }
    3093
    3094     # OR : ALCOHOL OR ETHER OXYGEN
    3095     if ($This->_IsAlcoholOrEtherOxygen($Atom)) {
    3096       $AtomType = 'OR';
    3097       last ATOMTYPE;
    3098     }
    3099
    3100     # GENERAL DIVALENT O
    3101     if ($This->_IsDivalentOxygen($Atom)) {
    3102       $AtomType = '-O-';
    3103       last ATOMTYPE;
    3104     }
    3105
    3106     $AtomType = 'None';
    3107     carp "Warning: ${ClassName}->_GetAtomTypeForOxygenWithOnlySigmaBonds: MMFF94 atom type for Oxygen cann't be assigned...";
    3108   }
    3109
    3110   return $AtomType;
    3111 }
    3112
    3113 # Get MMFF94 atom type for Oxygen with only sigma bonds...
    3114 #
    3115 sub _GetAtomTypeForOxygenWithOnePiBond {
    3116   my($This, $Atom) = @_;
    3117   my($AtomType);
    3118
    3119   $AtomType = 'None';
    3120
    3121   ATOMTYPE: {
    3122
    3123     # OFUR : AROMATIC OXYGEN AS IN FURAN
    3124     if ($This->_IsAromaticOxygen($Atom)) {
    3125       $AtomType = 'OFUR';
    3126       last ATOMTYPE;
    3127     }
    3128
    3129     # O=+ : POSITIVELY CHARGED OXENIUM (DICOORDINATE) OXYGEN
    3130     if ($This->_IsPositivelyChargedOxeniumOxygen($Atom)) {
    3131       $AtomType = 'O=+';
    3132       last ATOMTYPE;
    3133     }
    3134
    3135     # O=CN : CARBONYL OXYGEN, AMIDES
    3136     if ($This->_IsAmideCarbonylOxygen($Atom)) {
    3137       $AtomType = 'O=CN';
    3138       last ATOMTYPE;
    3139     }
    3140
    3141     # O=CO : CARBONYL OXYGEN, CARBOXYLIC ACIDS AND ESTERS
    3142     if ($This->_IsCarbobylCarboxylicAcidsOrEstersOxygen($Atom)) {
    3143       $AtomType = 'O=CO';
    3144       last ATOMTYPE;
    3145     }
    3146
    3147     # O=CR : CARBONYL OXYGEN, ALDEHYDES AND KETONES
    3148     if ($This->_IsCarbonylAldehydeOrKetoneOxygen($Atom)) {
    3149       $AtomType = 'O=CR';
    3150       last ATOMTYPE;
    3151     }
    3152
    3153     # O=C : GENERAL C=O
    3154     if ($This->_IsCarbonylOxygen($Atom)) {
    3155       $AtomType = 'O=C';
    3156       last ATOMTYPE;
    3157     }
    3158
    3159     # O2NO : NITRO-GROUP OXYGEN IN NITRATE
    3160     if ($This->_IsNitroGroupNitrateOxygen($Atom)) {
    3161       $AtomType = 'O2NO';
    3162       last ATOMTYPE;
    3163     }
    3164
    3165     # O2N : NITRO OXYGEN
    3166     if ($This->_IsNitroOxygen($Atom)) {
    3167       $AtomType = 'O2N';
    3168       last ATOMTYPE;
    3169     }
    3170
    3171     # O=N : NITROSO OXYGEN
    3172     if ($This->_IsNitrosoOxygen($Atom)) {
    3173       $AtomType = 'O=N';
    3174       last ATOMTYPE;
    3175     }
    3176
    3177     # O4CL : OXYGEN IN CLO4(-) ANION - FORMAL CHARGE=-0.25
    3178     if ($This->_IsPerChlorateAnionOxygen($Atom)) {
    3179       $AtomType = 'O4CL';
    3180       last ATOMTYPE;
    3181     }
    3182
    3183     $AtomType = 'None';
    3184     carp "Warning: ${ClassName}->_GetAtomTypeForOxygenWithOnePiBond: MMFF94 atom type for Oxygen cann't be assigned...";
    3185   }
    3186
    3187   return $AtomType;
    3188 }
    3189
    3190 # OR : ALCOHOL OR ETHER OXYGEN
    3191 #
    3192 sub _IsAlcoholOrEtherOxygen {
    3193   my($This, $Atom) = @_;
    3194
    3195   return ($This->_IsAlcoholOxygen($Atom) || $This->_IsEtherOxygen($Atom)) ? 1 : 0;
    3196 }
    3197
    3198 # Alcohol Oxygen..
    3199 #
    3200 sub _IsAlcoholOxygen {
    3201   my($This, $Atom) = @_;
    3202
    3203   return $Atom->DoesAtomNeighborhoodMatch('O.TSB2', ['C', 'H'], ['-', '-']) ? 1 : 0;
    3204 }
    3205
    3206 # Ether Oxygen..
    3207 #
    3208 sub _IsEtherOxygen {
    3209   my($This, $Atom) = @_;
    3210
    3211   return $Atom->DoesAtomNeighborhoodMatch('O.TSB2', ['C', 'C'], ['-', '-']) ? 1 : 0;
    3212 }
    3213
    3214
    3215 # OC=O : ESTER OR CARBOXYLIC ACID -O-
    3216 #
    3217 # Notes:
    3218 #    . Carboxylate anion Oxygen is matched using O2CM atom type.
    3219 #
    3220 sub _IsEsterOrCarboxylicAcidOxygen {
    3221   my($This, $Atom) = @_;
    3222   my($AtomNeighbor);
    3223
    3224   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2')) {
    3225     return 0;
    3226   }
    3227
    3228   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    3229     if ($This->_IsCarboxylicAcidOrEsterCarbonylCarbon($AtomNeighbor)) {
    3230       return 1;
    3231     }
    3232   }
    3233   return 0;
    3234 }
    3235
    3236 # OC=C : ENOLIC OR PHENOLIC OXYGEN
    3237 #
    3238 sub _IsEnolicOrPhenolicOxygen {
    3239   my($This, $Atom) = @_;
    3240   my($AtomNeighbor);
    3241
    3242   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2')) {
    3243     return 0;
    3244   }
    3245
    3246   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    3247     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.T3.DB1', ['C', 'O', '*'], ['=', '-', '-'])) {
    3248       return 1;
    3249     }
    3250   }
    3251   return 0;
    3252 }
    3253
    3254 # OC=N : DIVALENT OXYGEN
    3255 #
    3256 sub _IsOCNDivalentOxygen {
    3257   my($This, $Atom) = @_;
    3258   my($AtomNeighbor);
    3259
    3260   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2')) {
    3261     return 0;
    3262   }
    3263
    3264   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    3265     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.T3.DB1', ['N', 'O', '*'], ['=', '-', '-'])) {
    3266       return 1;
    3267     }
    3268   }
    3269   return 0;
    3270 }
    3271
    3272 # OC=S : THIOESTER OR THIOACID -O-
    3273 #
    3274 sub _IsThioEsterOrThioAcidOxygen {
    3275   my($This, $Atom) = @_;
    3276   my($AtomNeighbor);
    3277
    3278   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2,O.X1.FC-1')) {
    3279     return 0;
    3280   }
    3281
    3282   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    3283     # Thio ester, acid or anion Oxygen...
    3284     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('C.T3.DB1', ['S', 'O.X1.FC0,O.X2,O.X1.FC-1', 'C,H'], ['=', '-', '-'], ['C', 'C,H', 'C,H'])) {
    3285       return 1;
    3286     }
    3287   }
    3288   return 0;
    3289 }
    3290
    3291 # ONO2 : DIVALENT NITRATE ETHER OXYGEN
    3292 #
    3293 # Notes:
    3294 #    . Nitrate anion Oxygen is matched using O3N atom type.
    3295 #    . Divalent Oxygen in Nitrate with one Hydrogen atom is not treated as ether ONO2.
    3296 #
    3297 sub _IsDivalentNitrateEtherOxygen {
    3298   my($This, $Atom) = @_;
    3299   my($AtomNeighbor);
    3300
    3301   if (!$Atom->DoesAtomNeighborhoodMatch('O.X2')) {
    3302     return 0;
    3303   }
    3304
    3305   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('N.DB1')) {
    3306     if ($This->_IsNitrateNitrogen($AtomNeighbor)) {
    3307       return 1;
    3308     }
    3309   }
    3310   return 0;
    3311 }
    3312
    3313 # ON=O : DIVALENT NITRITE ETHER OXYGEN
    3314 #
    3315 # Notes:
    3316 #    . Divalent Oxygen in Nitrite with one Hydrogen atom is not treated as ether ON=O.
    3317 #
    3318 sub _IsDivalentNitriteEtherOxygen {
    3319   my($This, $Atom) = @_;
    3320   my($AtomNeighbor);
    3321
    3322   if (!$Atom->DoesAtomNeighborhoodMatch('O.X2')) {
    3323     return 0;
    3324   }
    3325
    3326   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('N.DB1')) {
    3327     # R-O-N=O
    3328     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('N.T2.DB1.FC0', ['O', 'O.FC0'], ['=', '-'])) {
    3329       return 1;
    3330     }
    3331   }
    3332   return 0;
    3333 }
    3334
    3335 # OSO3 : DIVALENT OXYGEN ATTACHED TO SULFUR
    3336 #
    3337 # Notes:
    3338 #    . It corresponds to divalent Oxygen in Sulfates.
    3339 #    . Oxygen attached to one heavy atom and one Hydrogen atom is treated as divalent
    3340 #      Oxygen and matched to OSO3.
    3341 #    . Anion Oxygen attached to one heavy atom with no Hydrogen atom is treated as terminal
    3342 #      Oxygen and matched to O4S.
    3343 #
    3344 sub _IsOSO3DivalentOxygen {
    3345   my($This, $Atom) = @_;
    3346   my($AtomNeighbor);
    3347
    3348   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2', ['S.T4'])) {
    3349     return 0;
    3350   }
    3351
    3352   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T4')) {
    3353     # R'-O-S(=O)(=O)-O-R"
    3354     if ($This->_IsSulfateSulfur($AtomNeighbor)) {
    3355       return 1;
    3356     }
    3357   }
    3358   return 0;
    3359 }
    3360
    3361 # OSO2 : DIVALENT OXYGEN ATTACHED TO SULFUR
    3362 #
    3363 # Notes:
    3364 #    . It corresponds to divalent Oxygen in Sulfonates, Sulfones and so on.
    3365 #    . Oxygen attached to one heavy atom and one Hydrogen atom is treated as divalent
    3366 #      Oxygen and matched to OSO2.
    3367 #    . Anion Oxygen attached to one heavy atom with no Hydrogen atom is treated as terminal
    3368 #      Oxygen and matched to O3S.
    3369 #
    3370 sub _IsOSO2DivalentOxygen {
    3371   my($This, $Atom) = @_;
    3372   my($AtomNeighbor);
    3373
    3374   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2', ['S.T4'])) {
    3375     return 0;
    3376   }
    3377
    3378   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T4')) {
    3379     # R'-O-S(=O)(=O)-R"
    3380     if ($This->_IsSulfonateSulfur($AtomNeighbor)) {
    3381       return 1;
    3382     }
    3383   }
    3384   return 0;
    3385 }
    3386
    3387 # OSO : DIVALENT OXYGEN ATTACHED TO SULFUR
    3388 #
    3389 # Notes:
    3390 #    . It corresponds to divalent Oxygen attached to Sulfur with single bond.
    3391 #    . Oxygen attached to one heavy atom and one Hydrogen atom is treated as divalent
    3392 #      Oxygen and matched to OSO.
    3393 #
    3394 sub _IsOSODivalentOxygen {
    3395   my($This, $Atom) = @_;
    3396   my($AtomNeighbor);
    3397
    3398   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2', ['S.T2'])) {
    3399     return 0;
    3400   }
    3401
    3402   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T2.DB0')) {
    3403     # R'-O-S-O-R"
    3404     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('S', ['O', 'O'], ['-', '-'])) {
    3405       return 1;
    3406     }
    3407   }
    3408   return 0;
    3409 }
    3410
    3411 # OS=O : DIVALENT OXYGEN ATTACHED TO SULFOXIDE SULFUR
    3412 #
    3413 # Notes:
    3414 #    . It corresponds to divalent Oxygen in Sulfoxides.
    3415 #    . Oxygen attached to one heavy atom and one Hydrogen atom is treated as divalent
    3416 #      Oxygen and matched to OS=O.
    3417 #
    3418 sub _IsSulfoxideDivalentOxygen {
    3419   my($This, $Atom) = @_;
    3420   my($AtomNeighbor);
    3421
    3422   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2', ['S.T3'])) {
    3423     return 0;
    3424   }
    3425
    3426   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T3.DB1')) {
    3427     # R'-O-S(=O)-R"
    3428     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('S.T3.DB1', ['O', 'O', '*'], ['=', '-', '-'])) {
    3429       return 1;
    3430     }
    3431   }
    3432   return 0;
    3433 }
    3434
    3435 # -OS : GENERAL DIVALENT OXYGEN ATTACHED TO S
    3436 #
    3437 # Notes:
    3438 #    . It covers any divalent Oxygen attached to Sulfur.
    3439 #
    3440 sub _IsOSDivalentOxygen {
    3441   my($This, $Atom) = @_;
    3442   my($AtomNeighbor);
    3443
    3444   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2', ['S'])) {
    3445     return 0;
    3446   }
    3447
    3448   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S')) {
    3449     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('S', ['O'], ['-'])) {
    3450       return 1;
    3451     }
    3452   }
    3453   return 0;
    3454 }
    3455
    3456 # OPO3 : DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
    3457 #
    3458 # Notes:
    3459 #    . It corresponds to divalent Oxygen in Phopsphates or Phosphodiesters.
    3460 #
    3461 sub _IsOPO3DivalentOxygen {
    3462   my($This, $Atom) = @_;
    3463   my($AtomNeighbor);
    3464
    3465   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2')) {
    3466     return 0;
    3467   }
    3468
    3469   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('P.T4')) {
    3470     if ($This->_IsPhosphateOrPhosphodiesterPhosphorus($AtomNeighbor)) {
    3471       return 1;
    3472     }
    3473   }
    3474   return 0;
    3475 }
    3476
    3477 # OPO2 : DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
    3478 #
    3479 # Notes:
    3480 #    . It corresponds to divalent Oxygen in Phopsphonates or their esters.
    3481 #
    3482 sub _IsOPO2DivalentOxygen {
    3483   my($This, $Atom) = @_;
    3484   my($AtomNeighbor);
    3485
    3486   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2')) {
    3487     return 0;
    3488   }
    3489
    3490   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('P.T4')) {
    3491     if ($This->_IsPhosphonatePhosphorus($AtomNeighbor)) {
    3492       return 1;
    3493     }
    3494   }
    3495   return 0;
    3496 }
    3497
    3498 # OPO : DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
    3499 #
    3500 # Notes:
    3501 #    . It corresponds to divalent Oxygen in Phopsphinates.
    3502 #
    3503 sub _IsOPODivalentOxygen {
    3504   my($This, $Atom) = @_;
    3505   my($AtomNeighbor);
    3506
    3507   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2')) {
    3508     return 0;
    3509   }
    3510
    3511   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('P.T4')) {
    3512     if ($This->_IsPhosphinatePhosphorus($AtomNeighbor)) {
    3513       return 1;
    3514     }
    3515   }
    3516   return 0;
    3517 }
    3518
    3519 # -OP : DIVALENT OXYGEN ATTACHED TO PHOSPHOROUS
    3520 #
    3521 sub _IsOPDivalentOxygen {
    3522   my($This, $Atom) = @_;
    3523   my($AtomNeighbor);
    3524
    3525   if (!$Atom->DoesAtomNeighborhoodMatch('O.TSB2')) {
    3526     return 0;
    3527   }
    3528
    3529   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('P')) {
    3530     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('P', ['O'], ['-'])) {
    3531       return 1;
    3532     }
    3533   }
    3534   return 0;
    3535 }
    3536
    3537 # -O- : GENERAL DIVALENT O
    3538 #
    3539 sub _IsDivalentOxygen {
    3540   my($This, $Atom) = @_;
    3541
    3542   return $Atom->DoesAtomNeighborhoodMatch('O.TSB2') ? 1 : 0;
    3543 }
    3544
    3545 # O=C : GENERAL C=O
    3546 #
    3547 sub _IsCarbonylOxygen {
    3548   my($This, $Atom) = @_;
    3549
    3550   return $Atom->DoesAtomNeighborhoodMatch('O.T1.DB1', ['C'], ['=']) ? 1 : 0;
    3551 }
    3552
    3553 # O=CN : CARBONYL OXYGEN, AMIDES
    3554 #
    3555 sub _IsAmideCarbonylOxygen {
    3556   my($This, $Atom) = @_;
    3557   my($AtomNeighbor);
    3558
    3559   if (!$Atom->DoesAtomNeighborhoodMatch('O.T1.DB1')) {
    3560     return 0;
    3561   }
    3562
    3563   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    3564     if ($This->_IsAmideCarbonylCarbon($AtomNeighbor)) {
    3565       return 1;
    3566     }
    3567   }
    3568   return 0;
    3569 }
    3570
    3571 # O=CR : CARBONYL OXYGEN, ALDEHYDES AND KETONES
    3572 #
    3573 sub _IsCarbonylAldehydeOrKetoneOxygen {
    3574   my($This, $Atom) = @_;
    3575   my($AtomNeighbor);
    3576
    3577   if (!$Atom->DoesAtomNeighborhoodMatch('O.T1.DB1')) {
    3578     return 0;
    3579   }
    3580
    3581   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    3582     if ($This->_IsKetoneOrAldehydeCarbonylCarbon($AtomNeighbor)) {
    3583       return 1;
    3584     }
    3585   }
    3586   return 0;
    3587 }
    3588
    3589 # O=CO : CARBONYL OXYGEN, CARBOXYLIC ACIDS AND ESTERS
    3590 #
    3591 sub _IsCarbobylCarboxylicAcidsOrEstersOxygen {
    3592   my($This, $Atom) = @_;
    3593   my($AtomNeighbor);
    3594
    3595   if (!$Atom->DoesAtomNeighborhoodMatch('O.T1.DB1')) {
    3596     return 0;
    3597   }
    3598
    3599   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    3600     if ($This->_IsCarboxylicAcidOrEsterCarbonylCarbon($AtomNeighbor)) {
    3601       return 1;
    3602     }
    3603   }
    3604   return 0;
    3605 }
    3606
    3607 # O=N : NITROSO OXYGEN
    3608 #
    3609 sub _IsNitrosoOxygen {
    3610   my($This, $Atom) = @_;
    3611
    3612   return $Atom->DoesAtomNeighborhoodMatch('O.T1.DB1', ['N'], ['=']) ? 1 : 0;
    3613 }
    3614
    3615 # O=S : O=S IN SULFOXIDES
    3616 #
    3617 sub _IsSulfoxideOxygen {
    3618   my($This, $Atom) = @_;
    3619   my($AtomNeighbor);
    3620
    3621   if (!$Atom->DoesAtomNeighborhoodMatch('O.T1.DB1')) {
    3622     return 0;
    3623   }
    3624
    3625   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.DB1')) {
    3626     if ($This->_IsSulfoxideSulfur($AtomNeighbor)) {
    3627       return 1;
    3628     }
    3629   }
    3630   return 0;
    3631 }
    3632
    3633 # O=S= :  O=S ON SULFUR DOUBLY BONDED TO, E.G., CARBON
    3634 #
    3635 sub _IsDoublyBondedOSOxygen {
    3636   my($This, $Atom) = @_;
    3637   my($AtomNeighbor);
    3638
    3639   if (!$Atom->DoesAtomNeighborhoodMatch('O.T1.DB1')) {
    3640     return 0;
    3641   }
    3642
    3643   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.DB2')) {
    3644     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('S', ['O', '!O'], ['=', '='])) {
    3645       return 1;
    3646     }
    3647   }
    3648   return 0;
    3649 }
    3650
    3651 # O2CM : OXYGEN IN CARBOXYLATE ANION
    3652 #
    3653 sub _IsCarboxylateAnionOxygen {
    3654   my($This, $Atom) = @_;
    3655   my($AtomNeighbor);
    3656
    3657   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1.FC-1')) {
    3658     return 0;
    3659   }
    3660
    3661   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.T3.DB1')) {
    3662     if ($This->_IsCarboxylateAnionCarbon($AtomNeighbor)) {
    3663       return 1;
    3664     }
    3665   }
    3666   return 0;
    3667 }
    3668
    3669 # OXN : N-OXIDE OXYGEN
    3670 #
    3671 sub _IsNOxideOxygen {
    3672   my($This, $Atom) = @_;
    3673
    3674   return $Atom->DoesAtomNeighborhoodMatch('O.X1.FC-1', ['N.FC+1'], ['-']) ? 1 : 0;
    3675 }
    3676
    3677 # O2N : NITRO OXYGEN
    3678 #
    3679 sub _IsNitroOxygen {
    3680   my($This, $Atom) = @_;
    3681   my($AtomNeighbor);
    3682
    3683   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1.DB1,O.X1.FC-1')) {
    3684     return 0;
    3685   }
    3686
    3687   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('N.T3.DB1')) {
    3688     if ($This->_IsNitroNitrogen($AtomNeighbor)) {
    3689       return 1;
    3690     }
    3691   }
    3692   return 0;
    3693 }
    3694
    3695 # O2NO : NITRO-GROUP OXYGEN IN NITRATE
    3696 #
    3697 sub _IsNitroGroupNitrateOxygen {
    3698   my($This, $Atom) = @_;
    3699   my($AtomNeighbor);
    3700
    3701   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1.DB1')) {
    3702     return 0;
    3703   }
    3704
    3705   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('N.T3.DB1')) {
    3706     if ($This->_IsNitrateNitrogen($AtomNeighbor)) {
    3707       return 1;
    3708     }
    3709   }
    3710   return 0;
    3711 }
    3712
    3713 # O3N : NITRATE ANION OXYGEN
    3714 #
    3715 sub _IsNitrateAnionOxygen {
    3716   my($This, $Atom) = @_;
    3717   my($AtomNeighbor);
    3718
    3719   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1.FC-1')) {
    3720     return 0;
    3721   }
    3722
    3723   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('N.T3.DB1')) {
    3724     if ($This->_IsNitrateNitrogen($AtomNeighbor)) {
    3725       return 1;
    3726     }
    3727   }
    3728   return 0;
    3729 }
    3730
    3731 # O-S : SINGLE TERMINAL OXYGEN ON TETRACOORD SULFUR
    3732 #
    3733 sub _IsOSTerminalOxygen {
    3734   my($This, $Atom) = @_;
    3735   my($AtomNeighbor);
    3736
    3737   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1', ['S.T4'])) {
    3738     return 0;
    3739   }
    3740
    3741   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T4')) {
    3742     if ($AtomNeighbor->DoesAtomNeighborhoodMatch('S', ['O.X1', '!O', '!O', '!O'])) {
    3743       return 1;
    3744     }
    3745   }
    3746   return 0;
    3747 }
    3748
    3749 # O2S : TERMINAL O-S IN SULFONES AND SULFONAMIDES
    3750 #
    3751 sub _IsSulfoneOrSulfonamideTerminalOxygen {
    3752   my($This, $Atom) = @_;
    3753   my($AtomNeighbor);
    3754
    3755   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1', ['S.T4'])) {
    3756     return 0;
    3757   }
    3758
    3759   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T4')) {
    3760     if ($This->_IsSulfonamideSulfur($AtomNeighbor) || $This->_IsSulfoneSulfur($AtomNeighbor)) {
    3761       return 1;
    3762     }
    3763   }
    3764   return 0;
    3765 }
    3766
    3767 # O3S : TERMINAL O IN SULFONATES
    3768 #
    3769 # Notes:
    3770 #    . It corresponds to monovalent Oxygen in Sulfonates.
    3771 #    . Anion Oxygen attached to one heavy atom with no Hydrogen atom is treated as terminal
    3772 #      Oxygen and matched to O3S.
    3773 #
    3774 sub _IsSulfonateTerminalOxygen {
    3775   my($This, $Atom) = @_;
    3776   my($AtomNeighbor);
    3777
    3778   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1', ['S.T4'])) {
    3779     return 0;
    3780   }
    3781
    3782   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T4')) {
    3783     # R'-O-S(=O)(=O)-R", [O-]-S(=O)(=O)-R",
    3784     if ($This->_IsSulfonateSulfur($AtomNeighbor)) {
    3785       return 1;
    3786     }
    3787   }
    3788   return 0;
    3789 }
    3790
    3791 # O4S : TERMINAL O IN SO4(-3)
    3792 #
    3793 # Notes:
    3794 #    . It corresponds to monovalent Oxygen in Sulfates.
    3795 #    . As far I can tell, SO4 should have a formal charge of -2.
    3796 #    . Anion Oxygen attached to one heavy atom with no Hydrogen atom is treated as terminal
    3797 #      Oxygen and matched to O4S.
    3798 #
    3799 sub _IsSulfateTerminalOxygen {
    3800   my($This, $Atom) = @_;
    3801   my($AtomNeighbor);
    3802
    3803   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1', ['S.T4'])) {
    3804     return 0;
    3805   }
    3806
    3807   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.T4')) {
    3808     # R'-O-S(=O)(=O)-O-R", R'-O-S(=O)(=O)-[O-]
    3809     if ($This->_IsSulfateSulfur($AtomNeighbor)) {
    3810       return 1;
    3811     }
    3812   }
    3813   return 0;
    3814 }
    3815
    3816 # OSMS : TERM O IN THIOSULFINATE ANION - FORMAL CHARGE=-0.5
    3817 #
    3818 sub _IsThioSulfinateTerminalOxygen {
    3819   my($This, $Atom) = @_;
    3820   my($AtomNeighbor);
    3821
    3822   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1', ['S.FC+1'])) {
    3823     return 0;
    3824   }
    3825
    3826   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.FC+1')) {
    3827     if ($This->_IsThioSulfinateSulfur($AtomNeighbor)) {
    3828       return 1;
    3829     }
    3830   }
    3831   return 0;
    3832 }
    3833
    3834 # OP : TERMINAL O IN PHOSPHOXIDES
    3835 #
    3836 sub _IsPhosphoxideTerminalOxygen {
    3837   my($This, $Atom) = @_;
    3838   my($AtomNeighbor);
    3839
    3840   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1', ['P.T4'])) {
    3841     return 0;
    3842   }
    3843
    3844   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('P.T4')) {
    3845     if ($This->_IsPhosphoxidePhosphorus($AtomNeighbor)) {
    3846       return 1;
    3847     }
    3848   }
    3849   return 0;
    3850 }
    3851
    3852 # O2P : TERMINAL O IN PHOSPHINATES
    3853 #
    3854 sub _IsPhosphinateTerminalOxygen {
    3855   my($This, $Atom) = @_;
    3856   my($AtomNeighbor);
    3857
    3858   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1', ['P.T4'])) {
    3859     return 0;
    3860   }
    3861
    3862   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('P.T4')) {
    3863     if ($This->_IsPhosphinatePhosphorus($AtomNeighbor)) {
    3864       return 1;
    3865     }
    3866   }
    3867   return 0;
    3868 }
    3869
    3870 # O3P : TERMINAL OXYGEN IN PHOSPHONATES
    3871 #
    3872 sub _IsPhosphonateTerminalOxygen {
    3873   my($This, $Atom) = @_;
    3874   my($AtomNeighbor);
    3875
    3876   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1', ['P.T4'])) {
    3877     return 0;
    3878   }
    3879
    3880   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('P.T4')) {
    3881     if ($This->_IsPhosphonatePhosphorus($AtomNeighbor)) {
    3882       return 1;
    3883     }
    3884   }
    3885   return 0;
    3886 }
    3887
    3888 # O4P : TERMINAL OXYGEN IN PHOSPHATES AND PHOSPHODIESTERS
    3889 #
    3890 sub _IsPhosphateOrPhosphodiesterTerminalOxygen {
    3891   my($This, $Atom) = @_;
    3892   my($AtomNeighbor);
    3893
    3894   if (!$Atom->DoesAtomNeighborhoodMatch('O.X1', ['P.T4'])) {
    3895     return 0;
    3896   }
    3897
    3898   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('P.T4')) {
    3899     if ($This->_IsPhosphateOrPhosphodiesterPhosphorus($AtomNeighbor)) {
    3900       return 1;
    3901     }
    3902   }
    3903   return 0;
    3904 }
    3905
    3906 # O4CL : OXYGEN IN CLO4(-) ANION - FORMAL CHARGE=-0.25
    3907 #
    3908 sub _IsPerChlorateAnionOxygen {
    3909   my($This, $Atom) = @_;
    3910   my($AtomNeighbor);
    3911
    3912   # All Oxygens in PerChlorate anion and ester are matched to O4Cl...
    3913   if (!$Atom->DoesAtomNeighborhoodMatch('O', ['Cl'])) {
    3914     return 0;
    3915   }
    3916
    3917   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('Cl')) {
    3918     if ($This->_IsPerChlorateAnionChlorine($AtomNeighbor)) {
    3919       return 1;
    3920     }
    3921   }
    3922   return 0;
    3923 }
    3924
    3925 # OM : ALKOXIDE OXYGEN, NEGATIVELY CHARGED
    3926 #
    3927 sub _IsNegativelyChargedAlkoxideOxygen {
    3928   my($This, $Atom) = @_;
    3929
    3930   # R-(O-)
    3931
    3932   return $Atom->DoesAtomNeighborhoodMatch('O.FC-1.T1', ['C,H'], ['-']) ? 1 : 0;
    3933 }
    3934
    3935 # OM2 : OXIDE OXYGEN ON SP2 CARBON, NEGATIVELY CHARGED
    3936 #
    3937 sub _IsNegativelyChargedSP2OxideOxygen {
    3938   my($This, $Atom) = @_;
    3939
    3940   return $Atom->DoesAtomNeighborhoodMatch('O.FC-1.T1', ['C.DB1.T3'], ['-']) ? 1 : 0;
    3941 }
    3942
    3943 # O+ : POSITIVELY CHARGED OXONIUM (TRICOORDINATE) OXYGEN
    3944 #
    3945 sub _IsPositivelyChargedOxoniumOxygen {
    3946   my($This, $Atom) = @_;
    3947
    3948   return $Atom->DoesAtomNeighborhoodMatch('O.FC+1.T3.DB0') ? 1 : 0;
    3949 }
    3950
    3951 # O=+ : POSITIVELY CHARGED OXENIUM (DICOORDINATE) OXYGEN
    3952 #
    3953 sub _IsPositivelyChargedOxeniumOxygen {
    3954   my($This, $Atom) = @_;
    3955
    3956   return $Atom->DoesAtomNeighborhoodMatch('O.FC+1.T2.DB1') ? 1 : 0;
    3957 }
    3958
    3959 # OFUR : AROMATIC OXYGEN AS IN FURAN
    3960 #
    3961 sub _IsAromaticOxygen {
    3962   my($This, $Atom) = @_;
    3963
    3964   return $Atom->DoesAtomNeighborhoodMatch('O.Ar.RA5') ? 1 : 0;
    3965 }
    3966
    3967 # OH2 : OXYGEN ON WATER
    3968 #
    3969 sub _IsWaterOxygen {
    3970   my($This, $Atom) = @_;
    3971
    3972   return $Atom->DoesAtomNeighborhoodMatch('O.T2.TSB2', ['H', 'H'], ['-', '-']) ? 1 : 0;
    3973 }
    3974
    3975 # PO4 : PHOSPHOROUS IN PHOSPHATES AND PHOSPHODIESTERS
    3976 #
    3977 sub _IsPhosphateOrPhosphodiesterPhosphorus {
    3978   my($This, $Atom) = @_;
    3979
    3980   # Phosphate: R-O-P(=O)(-O)-O; Phosphate diester: R'-O-P(=O)(-O)-O-R"
    3981
    3982   return $Atom->DoesAtomNeighborhoodMatch('P.T4.DB1', ['O', 'O', 'O', 'O'], ['=', '-', '-', '-']) ? 1 : 0;
    3983 }
    3984
    3985 # PO3 : TETRACOORDINATE P WITH THREE ATTACHED OXYGENS
    3986 #
    3987 sub _IsPhosphonatePhosphorus {
    3988   my($This, $Atom) = @_;
    3989
    3990   # Phosphonate: R-P(=O)(-O)-O; Phosphonate ester: R'-P(=O)(-O)-O-R"
    3991
    3992   return $Atom->DoesAtomNeighborhoodMatch('P.T4.DB1', ['O', 'O', 'O', '!O'], ['=', '-', '-', '-']) ? 1 : 0;
    3993 }
    3994
    3995 # PO2 : TETRACOORDINATE P WITH TWO ATTACHED OXYGENS
    3996 #
    3997 sub _IsPhosphinatePhosphorus {
    3998   my($This, $Atom) = @_;
    3999
    4000   # Phosphinate: R-P(=O)(-O)-R"; Phosphinate ester: R'-P(=O)(-O)-R"
    4001
    4002   return $Atom->DoesAtomNeighborhoodMatch('P.T4.DB1', ['O', 'O', '!O', '!O'], ['=', '-', '-', '-']) ? 1 : 0;
    4003 }
    4004
    4005 # PO : TETRACOORDINATE P WITH ONE ATTACHED OXYGEN
    4006 #
    4007 sub _IsPhosphoxidePhosphorus {
    4008   my($This, $Atom) = @_;
    4009
    4010   # Phosphoxide: R-P(=O)(-R")-R'''
    4011
    4012   return $Atom->DoesAtomNeighborhoodMatch('P.T4.DB1', ['O', '!O', '!O', '!O'], ['=', '-', '-', '-']) ? 1 : 0;
    4013 }
    4014
    4015 # PTET : GENERAL TETRACOORDINATE PHOSPHORUS
    4016 #
    4017 sub _IsTetraCoordinatedPhosphorus {
    4018   my($This, $Atom) = @_;
    4019
    4020   return $Atom->DoesAtomNeighborhoodMatch('P.T4') ? 1 : 0;
    4021 }
    4022
    4023 # P : TRICOORDINATE P, AS IN PHOSPHINES
    4024 #
    4025 sub _IsTriCoordinatedPhosphorus {
    4026   my($This, $Atom) = @_;
    4027
    4028   return $Atom->DoesAtomNeighborhoodMatch('P.T3') ? 1 : 0;
    4029 }
    4030
    4031 # -P=C : PHOSPHOROUS DOUBLY BONDED TO CARBON
    4032 #
    4033 sub _IsDoublyBondedToCarbonPhosphorous {
    4034   my($This, $Atom) = @_;
    4035
    4036   return $Atom->DoesAtomNeighborhoodMatch('P.DB1', ['C'], ['=']) ? 1 : 0;
    4037 }
    4038
    4039 # S : SULFUR IN THIOETHERS AND MERCAPTANS
    4040 #
    4041 sub _IsThioEthersOrMercaptansSulfur {
    4042   my($This, $Atom) = @_;
    4043
    4044   return ($This->_IsThioEtherSulfur($Atom) || $This->_IsMercaptansSulfur($Atom)) ? 1 : 0;
    4045
    4046   return 0;
    4047 }
    4048
    4049 # Thioethers: R'-S-R"
    4050 #
    4051 sub _IsThioEtherSulfur {
    4052   my($This, $Atom) = @_;
    4053   my($AtomNeighbor);
    4054
    4055   return $Atom->DoesAtomNeighborhoodMatch('S.X2.T2', ['C', 'C'], ['-', '-']) ? 1 : 0;
    4056 }
    4057
    4058 # Mercaptans or Thiols: R-S-H
    4059 #
    4060 sub _IsMercaptansSulfur {
    4061   my($This, $Atom) = @_;
    4062
    4063   return $Atom->DoesAtomNeighborhoodMatch('S.T2', ['C', 'C,H'], ['-', '-']) ? 1 : 0;
    4064 }
    4065
    4066 # Is it a divalent dicoordinated Sulfur...
    4067 #
    4068 sub _IsDivalentDiCoordinatedSulfur {
    4069   my($This, $Atom) = @_;
    4070
    4071   return $Atom->DoesAtomNeighborhoodMatch('S.T2.FC0', ['*', '*'], ['-', '-']) ? 1 : 0;
    4072 }
    4073
    4074 # S=C : TERMINAL SULFUR DOUBLY BONDED TO CARBON
    4075 #
    4076 sub _IsSCTerminalSulfur {
    4077   my($This, $Atom) = @_;
    4078
    4079   return $Atom->DoesAtomNeighborhoodMatch('S.X1.DB1', ['C'], ['=']) ? 1 : 0;
    4080 }
    4081
    4082 # >S=N : SULFUR, TRICOORD, DOUBLY BONDED TO N
    4083 #
    4084 sub _IsSNTricoordinatedSulfur {
    4085   my($This, $Atom) = @_;
    4086
    4087   return $Atom->DoesAtomNeighborhoodMatch('S.T3.DB1', ['N', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    4088 }
    4089
    4090 # S=O : SULFUR IN SULFOXIDES
    4091 #
    4092 sub _IsSulfoxideSulfur {
    4093   my($This, $Atom) = @_;
    4094
    4095   # Sulfone: R'-S(=O)-R", R'-S(=O)-O-R", and so on...
    4096
    4097   return $Atom->DoesAtomNeighborhoodMatch('S.T3.DB1', ['O', '*', '*'], ['=', '-', '-']) ? 1 : 0;
    4098 }
    4099
    4100 # SO2 : SULFUR IN SULFONES
    4101 #
    4102 sub _IsSulfoneSulfur {
    4103   my($This, $Atom) = @_;
    4104
    4105   # Sulfone: R'-S(=O)(=O)-R"
    4106
    4107   return $Atom->DoesAtomNeighborhoodMatch('S.T4.DB2', ['O', 'O', '!O', '!O'], ['=', '=', '-', '-']) ? 1 : 0;
    4108 }
    4109
    4110 #  =SO2: SULFONE SULPHER DOUBLY BONDED TO CARBON
    4111 #
    4112 sub _IsDoublyBondedToCarbonSulfoneSulfur {
    4113   my($This, $Atom) = @_;
    4114
    4115   return $Atom->DoesAtomNeighborhoodMatch('S.T3.DB3', ['C', 'O', 'O'], ['=', '=', '=']) ? 1 : 0;
    4116 }
    4117
    4118 # SO2N : SULFUR IN SULFONAMIDES
    4119 #
    4120 sub _IsSulfonamideSulfur {
    4121   my($This, $Atom) = @_;
    4122
    4123   # Sulfonamide: R-S(=O)(=O)-N(-R)(-R")
    4124
    4125   return $Atom->DoesAtomNeighborhoodMatch('S.T4.DB2', ['O', 'O', 'N', '!O'], ['=', '=', '-', '-']) ? 1 : 0;
    4126 }
    4127
    4128 # SO3 : SULFONATE SULFUR
    4129 #
    4130 sub _IsSulfonateSulfur {
    4131   my($This, $Atom) = @_;
    4132
    4133   # Sulfonate ion: R'-S(=O)(=O)-(O-); Sulfonate ester: R'-S(=O)(=O)-O-R"
    4134
    4135   return $Atom->DoesAtomNeighborhoodMatch('S.T4.DB2', ['O', 'O', 'O', '!O'], ['=', '=', '-', '-']) ? 1 : 0;
    4136 }
    4137
    4138 # SO4 : SULFATE SULFUR
    4139 #
    4140 sub _IsSulfateSulfur {
    4141   my($This, $Atom) = @_;
    4142
    4143   # Sulfate ion: (O-)-S(=O)(=O)-(O-); Sulfate esters: R-O-S(=O)(=O)-O-R"
    4144
    4145   return $Atom->DoesAtomNeighborhoodMatch('S.T4.DB2', ['O', 'O', 'O', 'O'], ['=', '=', '-', '-']) ? 1 : 0;
    4146 }
    4147
    4148 # SNO : SULFUR IN NITROGEN ANALOG OF A SULFONE
    4149 #
    4150 sub _IsNitrogenAnalogOfSulfoneSulfur {
    4151   my($This, $Atom) = @_;
    4152
    4153   # Sulfone: R'-S(=N)(=O)-R"
    4154
    4155   return $Atom->DoesAtomNeighborhoodMatch('S.T4.DB2', ['N', 'O', '!O', '!O'], ['=', '=', '-', '-']) ? 1 : 0;
    4156 }
    4157
    4158 # STHI : SULFUR AS IN THIOPHENE
    4159 #
    4160 sub _IsSTHISulfur {
    4161   my($This, $Atom) = @_;
    4162
    4163   # Is it an aromatic atom in a five membered ring?
    4164   if (!$Atom->DoesAtomNeighborhoodMatch('S.Ar.RA5.FC0')) {
    4165     return 0;
    4166   }
    4167
    4168   # Is it part of five membered ring containing only one hetero atom?
    4169   my($RingAtomsRef, $RingIsAromatic, $NumOfHeteroAtoms);
    4170
    4171   for $RingAtomsRef ($Atom->GetRingsWithSize(5)) {
    4172     ($RingIsAromatic, $NumOfHeteroAtoms) = $This->_GetHeteroAtomsInformationInRing($RingAtomsRef);
    4173     if ($RingIsAromatic && $NumOfHeteroAtoms == 1) {
    4174       return 1;
    4175     }
    4176   }
    4177   return 0;
    4178 }
    4179
    4180 # S-P : TERMINAL SULFUR BONDED TO PHOSPHORUS
    4181 #
    4182 sub _IsSPTerminalSulfur {
    4183   my($This, $Atom) = @_;
    4184
    4185   return $Atom->DoesAtomNeighborhoodMatch('S.X1.T2', ['P', '*'], ['-', '-']) ? 1 : 0;
    4186 }
    4187
    4188 # S2CM : TERMINAL SULFUR IN THIOCARBOXYLATE ANION
    4189 #
    4190 sub _IsThioCarboxylateAnionTerminalSulfur {
    4191   my($This, $Atom) = @_;
    4192   my($AtomNeighbor);
    4193
    4194   if (!$Atom->DoesAtomNeighborhoodMatch('S.X1.FC-1, S.X1.DB1.FC0')) {
    4195     return 0;
    4196   }
    4197
    4198   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('C.DB1')) {
    4199     if ($This->_IsThioCarboxylateAnionCarbon($AtomNeighbor)) {
    4200       return 1;
    4201     }
    4202   }
    4203   return 0;
    4204 }
    4205
    4206 # SM : TERMINAL SULFUR - FORMAL CHARGE=-1
    4207 #
    4208 sub _IsNegativelyChargedTerminalSulfur {
    4209   my($This, $Atom) = @_;
    4210
    4211   return $Atom->DoesAtomNeighborhoodMatch('S.X1.FC-1', ['*'], ['-']) ? 1 : 0;
    4212 }
    4213
    4214 # SSMO : TERMINAL SULFUR IN THIOSULFINATE GROUP
    4215 #
    4216 sub _IsThioSulfinateTerminalSulfur {
    4217   my($This, $Atom) = @_;
    4218   my($AtomNeighbor);
    4219
    4220   if (!$Atom->DoesAtomNeighborhoodMatch('S.X1', ['S.FC+1'])) {
    4221     return 0;
    4222   }
    4223
    4224   for $AtomNeighbor ($Atom->GetNeighborsUsingAtomSpecification('S.FC+1')) {
    4225     if ($This->_IsThioSulfinateSulfur($AtomNeighbor)) {
    4226       return 1;
    4227     }
    4228   }
    4229   return 0;
    4230 }
    4231
    4232 # SO2M : SULFUR IN NEGATIVELY CHARGED SULFINATE GROUP
    4233 #
    4234 sub _IsNegativelyChargedSulfinateSulfur {
    4235   my($This, $Atom) = @_;
    4236
    4237   # Sulfinate ion: R'-S(=O)-(O-)
    4238
    4239   return $Atom->DoesAtomNeighborhoodMatch('S.T3.DB1', ['O', 'O.X1.FC-1', '!O'], ['=', '-', '-']) ? 1 : 0;
    4240 }
    4241
    4242 # Sulfinate: R'-S(=O)-OH; Sulfinate esters: R'-S(=O)-O-R
    4243 #
    4244 sub _IsSulfinateSulfur {
    4245   my($This, $Atom) = @_;
    4246
    4247   return $Atom->DoesAtomNeighborhoodMatch('S.T3.DB1', ['O', 'O.X1.FC0,O.X2', '!O'], ['=', '-', '-']) ? 1 : 0;
    4248 }
    4249
    4250 # SSOM : TRICOORD SULFUR IN THIOSULFINATE GROUP
    4251 #
    4252 sub _IsTriCoordinatedThioSulfinateSulfur {
    4253   my($This, $Atom) = @_;
    4254   my($AtomNeighbor);
    4255
    4256   if (!$Atom->DoesAtomNeighborhoodMatch('S.X3', ['S'])) {
    4257     return 0;
    4258   }
    4259
    4260   if ($This->_IsThioSulfinateSulfur($Atom)) {
    4261       return 1;
    4262   }
    4263
    4264   return 0;
    4265 }
    4266
    4267 # Is it a Thiosulfinate group?
    4268 #
    4269 sub _IsThioSulfinateSulfur {
    4270   my($This, $Atom) = @_;
    4271
    4272   # R'-[S+1](-[O-1])-S-R"
    4273
    4274   return $Atom->DoesAtomNeighborhoodMatch('S.FC+1', ['O.FC-1', 'S', '!O'], ['-', '-', '-']) ? 1 : 0;
    4275 }
    4276
    4277 #   =S=O:  SULFINYL SULFUR, EG. IN C=S=O
    4278 #
    4279 sub _IsSulfinylSulfur {
    4280   my($This, $Atom) = @_;
    4281
    4282   return $Atom->DoesAtomNeighborhoodMatch('S.X2.DB2', ['C', 'O'], ['=', '=']) ? 1 : 0;
    4283 }
    4284
    4285 # CLO4 : CHLORINE IN PERCHLORATE ANION, CLO4(-)
    4286 #
    4287 sub _IsPerChlorateAnionChlorine {
    4288   my($This, $Atom) = @_;
    4289
    4290   # R-O-Cl(=O)(=O)=O or (O-)-Cl(=O)(=O)=O
    4291   if ($Atom->DoesAtomNeighborhoodMatch('Cl.X4.DB3', ['O.X1.FC-1,O.T2.FC0', 'O', 'O', 'O'], ['-', '=', '=', '='])) {
    4292     return 1;
    4293   }
    4294
    4295   # Match distributed formal charge of -0.25 on each Oxygen?
    4296   if ($Atom->DoesAtomNeighborhoodMatch('Cl.X4.DB3', ['O.FC-0.25', 'O.FC-0.25', 'O.FC-0.25', 'O.FC-0.25'], ['*', '*', '*', '*'])) {
    4297     return 1;
    4298   }
    4299
    4300   return 0;
    4301 }
    4302
    4303 # Get MMFF94 atom type for Hydrogen attached to Carbon...
    4304 #
    4305 sub _GetAtomTypeForHydrogenAttachedToCarbon {
    4306   my($This, $CarbonAtom) = @_;
    4307   my($AtomType);
    4308
    4309   # HC :  H  ATTACHED TO C
    4310   $AtomType = 'HC';
    4311
    4312   return $AtomType;
    4313 }
    4314
    4315 # Get MMFF94 atom type for Hydrogen attached to Nitrogen...
    4316 #
    4317 # 25 AtomTypeSymbols for element H:
    4318 #
    4319 # AtomTypeSymbol   AtomTypeNum   AtomTypeDefinition
    4320 #   HNR      23    H-N(SP3)
    4321 #   H3N      23    H-N(SP3), AMMONIA
    4322 #   HPYL     23    H-N IN PYRROLE
    4323 #   HNOX     23    H-N IN IN A N-OXIDE
    4324 #   HNM      23    H ON DICOORD, NEGATIVELY CHARGED NITROGEN
    4325 #   HN       23    GENERAL H ON NITROGEN
    4326 #   HN=N     27    AZO HYDROGEN
    4327 #   HN=C     27    IMINE HYDROGEN
    4328 #   HNCO     28    AMIDE HYDROGEN
    4329 #   HNCS     28    THIOAMIDE HYDROGEN
    4330 #   HNCC     28    H-N IN ENAMINES
    4331 #   HNCN     28    H-N IN H-N-C=N
    4332 #   HNNC     28    H-N IN H-N-N=C
    4333 #   HNNN     28    H-N IN H-N-N=N
    4334 #   HNSO     28    H-N IN SULFONAMIDE
    4335 #   HNPO     28    H-N IN PHOSPHONAMIDE
    4336 #   HNC%     28    HYDROGEN ON N ATTACHED TO TRIPLY BONDED CARBON
    4337 #   HSP2     28    GENERAL H ON SP2 NITROGEN
    4338 #   HNR+     36    H ON QUATERNARY NITROGEN
    4339 #   HIM+     36    H ON IMIDAZOLIUM-TYPE NITROGEN
    4340 #   HPD+     36    H ON PROTONATED PYRIDINE NITROGEN
    4341 #   HNN+     36    H ON AMIDINIUM-TYPE NITROGEN
    4342 #   HNC+     36    H ON PROTONATED IMINE NITROGEN
    4343 #   HGD+     36    H ON GUANIDINIUM-TYPE NITROGEN
    4344 #   HN5+     36    H ON N5+, N5A+ OR N5B+
    4345 #
    4346 sub _GetAtomTypeForHydrogenAttachedToNitrogen {
    4347   my($This, $NitrogenAtom) = @_;
    4348   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
    4349
    4350   $AtomType = 'None';
    4351
    4352   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
    4353
    4354   ($NumOfSigmaBonds, $NumOfPiBonds) = $NitrogenAtom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
    4355   $NumOfSigmaBonds += $NitrogenAtom->GetAtomicInvariantValue('H');
    4356
    4357   ATOMTYPE: {
    4358
    4359     if ($NumOfPiBonds == 0) {
    4360       $AtomType = $This->_GetAtomTypeForHydrogenAttachedToNitrogenWithOnlySigmaBonds($NitrogenAtom);
    4361       last ATOMTYPE;
    4362     }
    4363
    4364     if ($NumOfPiBonds == 1) {
    4365       $AtomType = $This->_GetAtomTypeForHydrogenAttachedToNitrogenWithOnePiBond($NitrogenAtom);
    4366       last ATOMTYPE;
    4367     }
    4368
    4369     if ($NumOfPiBonds == 2) {
    4370       $AtomType = $This->_GetAtomTypeForHydrogenAttachedToNitrogenWithTwoPiBonds($NitrogenAtom);
    4371       last ATOMTYPE;
    4372     }
    4373
    4374     # HN : GENERAL H ON NITROGEN
    4375     $AtomType = 'HN';
    4376   }
    4377   return $AtomType;
    4378 }
    4379
    4380 # Get atom type for Hydrogen attached to a Nitrogen with only sigma bonds...
    4381 #
    4382 sub _GetAtomTypeForHydrogenAttachedToNitrogenWithOnlySigmaBonds {
    4383   my($This, $NitrogenAtom) = @_;
    4384   my($AtomType);
    4385
    4386   $AtomType = 'None';
    4387
    4388   ATOMTYPE: {
    4389
    4390     # HNC% : HYDROGEN ON N ATTACHED TO TRIPLY BONDED CARBON
    4391     if ($This->_IsHydrogenAttachedToTriplyBondedToCarbonNitrogen($NitrogenAtom)) {
    4392       $AtomType = 'HNC%';
    4393       last ATOMTYPE;
    4394     }
    4395
    4396     # HPYL : H-N IN PYRROLE
    4397     if ($This->_IsHydrogenAttachedToPyrroleNitrogen($NitrogenAtom)) {
    4398       $AtomType = 'HPYL';
    4399       last ATOMTYPE;
    4400     }
    4401
    4402     # HN5+ : H ON N5+, N5A+ OR N5B+
    4403     if ($This->_IsHydrogenAttachedToFiveMemberedHetreoCyclicPostivelyChargedNitrogen($NitrogenAtom)) {
    4404       $AtomType =  'HN5+';
    4405       last ATOMTYPE;
    4406     }
    4407
    4408     # HGD+ : H ON GUANIDINIUM-TYPE NITROGEN
    4409     if ($This->_IsHydrogenAttachedToGuanidiniumNitrogen($NitrogenAtom)) {
    4410       $AtomType = 'HGD+';
    4411       last ATOMTYPE;
    4412     }
    4413
    4414     # HNCS : THIOAMIDE HYDROGEN
    4415     if ($This->_IsHydrogenAttachedToThioamideNitrogen($NitrogenAtom)) {
    4416       $AtomType = 'HNCS';
    4417       last ATOMTYPE;
    4418     }
    4419
    4420     # HNCO : AMIDE HYDROGEN
    4421     if ($This->_IsHydrogenAttachedToAmideNitrogen($NitrogenAtom)) {
    4422       $AtomType = 'HNCO';
    4423       last ATOMTYPE;
    4424     }
    4425
    4426     # HNSO : H-N IN SULFONAMIDE
    4427     if ($This->_IsHydrogenAttachedToSulfonamideNitrogen($NitrogenAtom)) {
    4428       $AtomType = 'HNSO';
    4429       last ATOMTYPE;
    4430     }
    4431
    4432     # HNPO : H-N IN PHOSPHONAMIDE
    4433     if ($This->_IsHydrogenAttachedToPhosphonamideNitrogen($NitrogenAtom)) {
    4434       $AtomType = 'HNPO';
    4435       last ATOMTYPE;
    4436     }
    4437
    4438     # HNOX : H-N IN IN A N-OXIDE
    4439     if ($This->_IsHydrogenAttachedToNOXideNitrogen($NitrogenAtom)) {
    4440       $AtomType = 'HNOX';
    4441       last ATOMTYPE;
    4442     }
    4443
    4444     # HNCC : H-N IN ENAMINES (H-N-C=C)
    4445     if ($This->_IsHydrogenAttachedToEnamineNitrogen($NitrogenAtom)) {
    4446       $AtomType = 'HNCC';
    4447       last ATOMTYPE;
    4448     }
    4449
    4450     # HNCN : H-N IN H-N-C=N
    4451     if ($This->_IsHydrogenAttachedToNCNNitrogen($NitrogenAtom)) {
    4452       $AtomType = 'HNCN';
    4453       last ATOMTYPE;
    4454     }
    4455
    4456     #  HNNC : H-N IN H-N-N=C
    4457     if ($This->_IsHydrogenAttachedToNNCNitrogen($NitrogenAtom)) {
    4458       $AtomType = 'HNNC';
    4459       last ATOMTYPE;
    4460     }
    4461
    4462     # HNNN : H-N IN H-N-N=N
    4463     if ($This->_IsHydrogenAttachedToNNNNitrogen($NitrogenAtom)) {
    4464       $AtomType = 'HNNN';
    4465       last ATOMTYPE;
    4466     }
    4467
    4468     # HNM : H ON DICOORD, NEGATIVELY CHARGED NITROGEN
    4469     if ($This->_IsHydrogenAttachedToNegativelyChargedDicoordinatedNitrogen($NitrogenAtom)) {
    4470       $AtomType = 'HNM';
    4471       last ATOMTYPE;
    4472     }
    4473
    4474     # H3N : H-N(SP3), AMMONIA
    4475     if ($This->_IsHydrogenAttachedToSP3AmmoniaNitrogen($NitrogenAtom)) {
    4476       $AtomType = 'H3N';
    4477       last ATOMTYPE;
    4478     }
    4479
    4480     # HNR : H-N(SP3)
    4481     if ($This->_IsHydrogenAttachedToSP3Nitrogen($NitrogenAtom)) {
    4482       $AtomType = 'HNR';
    4483       last ATOMTYPE;
    4484     }
    4485
    4486     # HNR+ : H ON QUATERNARY NITROGEN
    4487     if ($This->_IsHydrogenAttachedToQuaternaryNitrogen($NitrogenAtom)) {
    4488       $AtomType = 'HNR+';
    4489       last ATOMTYPE;
    4490     }
    4491
    4492     # HN : GENERAL H ON NITROGEN
    4493     $AtomType = 'HN';
    4494   }
    4495   return $AtomType;
    4496 }
    4497
    4498 # Get atom type for Hydrogen attached to a Nitrogen with one pi bonds...
    4499 #
    4500 sub _GetAtomTypeForHydrogenAttachedToNitrogenWithOnePiBond {
    4501   my($This, $NitrogenAtom) = @_;
    4502   my($AtomType);
    4503
    4504   $AtomType = 'None';
    4505
    4506   ATOMTYPE: {
    4507
    4508     # HPYL : H-N IN PYRROLE
    4509     if ($This->_IsHydrogenAttachedToPyrroleNitrogen($NitrogenAtom)) {
    4510       $AtomType = 'HPYL';
    4511       last ATOMTYPE;
    4512     }
    4513
    4514     # HIM+ : H ON IMIDAZOLIUM-TYPE NITROGEN
    4515     if ($This->_IsHydrogenAttachedToImidazoliumNitrogen($NitrogenAtom)) {
    4516       $AtomType =  'HIM+';
    4517       last ATOMTYPE;
    4518     }
    4519
    4520     # HN5+ : H ON N5+, N5A+ OR N5B+
    4521     if ($This->_IsHydrogenAttachedToFiveMemberedHetreoCyclicPostivelyChargedNitrogen($NitrogenAtom)) {
    4522       $AtomType =  'HN5+';
    4523       last ATOMTYPE;
    4524     }
    4525
    4526     # HPD+ : H ON PROTONATED PYRIDINE NITROGEN
    4527     if ($This->_IsHydrogenAttachedToPositivelyChargedPyridineNitrogen($NitrogenAtom)) {
    4528       $AtomType = 'HPD+';
    4529       last ATOMTYPE;
    4530     }
    4531
    4532     # HNOX : H-N IN IN A N-OXIDE
    4533     if ($This->_IsHydrogenAttachedToNOXideNitrogen($NitrogenAtom)) {
    4534       $AtomType = 'HNOX';
    4535       last ATOMTYPE;
    4536     }
    4537
    4538     # HGD+ : H ON GUANIDINIUM-TYPE NITROGEN
    4539     if ($This->_IsHydrogenAttachedToGuanidiniumNitrogen($NitrogenAtom)) {
    4540       $AtomType = 'HGD+';
    4541       last ATOMTYPE;
    4542     }
    4543
    4544     # HNN+ : H ON AMIDINIUM-TYPE NITROGEN
    4545     if ($This->_IsHydrogenAttachedToAmidiniumNitrogen($NitrogenAtom)) {
    4546       $AtomType = 'HNN+';
    4547       last ATOMTYPE;
    4548     }
    4549
    4550     # HNC+ : H ON PROTONATED IMINE NITROGEN
    4551     if ($This->_IsHydrogenAttachedToPositivelyChargedImineNitrogen($NitrogenAtom)) {
    4552       $AtomType = 'HNC+';
    4553       last ATOMTYPE;
    4554     }
    4555
    4556     # HN=N : AZO HYDROGEN
    4557     if ($This->_IsHydrogenAttachedToAzoNitrogen($NitrogenAtom)) {
    4558       $AtomType = 'HN=N';
    4559       last ATOMTYPE;
    4560     }
    4561
    4562     # HN=C : IMINE HYDROGEN
    4563     if ($This->_IsHydrogenAttachedToImineNitrogen($NitrogenAtom)) {
    4564       $AtomType = 'HN=C';
    4565       last ATOMTYPE;
    4566     }
    4567
    4568     # HSP2: GENERAL H ON SP2 NITROGEN
    4569     $AtomType = 'HSP2';
    4570   }
    4571   return $AtomType;
    4572 }
    4573
    4574 # Get atom type for Hydrogen attached to a Nitrogen with two pi bonds...
    4575 #
    4576 sub _GetAtomTypeForHydrogenAttachedToNitrogenWithTwoPiBonds {
    4577   my($This, $NitrogenAtom) = @_;
    4578   my($AtomType);
    4579
    4580   $AtomType = 'None';
    4581
    4582   ATOMTYPE: {
    4583
    4584     # HN : GENERAL H ON NITROGEN
    4585     $AtomType = 'HN';
    4586   }
    4587   return $AtomType;
    4588 }
    4589
    4590 # HNR : H-N(SP3)
    4591 #
    4592 sub _IsHydrogenAttachedToSP3Nitrogen {
    4593   my($This, $NitrogenAtom) = @_;
    4594
    4595   return $NitrogenAtom->DoesAtomNeighborhoodMatch('N.T3.FC0', ['*', '*', '*'], ['-', '-', '-']) ? 1 : 0;
    4596 }
    4597
    4598 # H3N : H-N(SP3), AMMONIA
    4599 #
    4600 sub _IsHydrogenAttachedToSP3AmmoniaNitrogen {
    4601   my($This, $NitrogenAtom) = @_;
    4602
    4603   return $NitrogenAtom->DoesAtomNeighborhoodMatch('N.T4.FC+1', ['H', 'H', 'H', 'H'], ['-', '-', '-', '-']) ? 1 : 0;
    4604 }
    4605
    4606 # HPYL : H-N IN PYRROLE
    4607 #
    4608 sub _IsHydrogenAttachedToPyrroleNitrogen {
    4609   my($This, $NitrogenAtom) = @_;
    4610
    4611   return $This->_IsPyrroleNitrogen($NitrogenAtom) ? 1 : 0;
    4612 }
    4613
    4614 # HNOX : H-N IN IN A N-OXIDE
    4615 #
    4616 sub _IsHydrogenAttachedToNOXideNitrogen {
    4617   my($This, $NitrogenAtom) = @_;
    4618
    4619   return $NitrogenAtom->DoesAtomNeighborhoodMatch('N.FC+1', ['O.X1.FC-1'], ['-']) ? 1 : 0;
    4620 }
    4621
    4622 # HNM : H ON DICOORD, NEGATIVELY CHARGED NITROGEN
    4623 #
    4624 sub _IsHydrogenAttachedToNegativelyChargedDicoordinatedNitrogen {
    4625   my($This, $NitrogenAtom) = @_;
    4626
    4627   return $NitrogenAtom->DoesAtomNeighborhoodMatch('N.T2.FC-1', ['*', '*'], ['-', '-']) ? 1 : 0;
    4628 }
    4629
    4630 # HN=N : AZO HYDROGEN
    4631 #
    4632 sub _IsHydrogenAttachedToAzoNitrogen {
    4633   my($This, $NitrogenAtom) = @_;
    4634
    4635   return $This->_IsAzoNitrogen($NitrogenAtom) ? 1 : 0;
    4636 }
    4637
    4638 # HN=C : IMINE HYDROGEN
    4639 #
    4640 sub _IsHydrogenAttachedToImineNitrogen {
    4641   my($This, $NitrogenAtom) = @_;
    4642
    4643   return $This->_IsImineNitrogen($NitrogenAtom) ? 1 : 0;
    4644 }
    4645
    4646 # HNCO : AMIDE HYDROGEN
    4647 #
    4648 sub _IsHydrogenAttachedToAmideNitrogen {
    4649   my($This, $NitrogenAtom) = @_;
    4650
    4651   return $This->_IsAmideNitrogen($NitrogenAtom) ? 1 : 0;
    4652 }
    4653
    4654 # HNCS : THIOAMIDE HYDROGEN
    4655 #
    4656 sub _IsHydrogenAttachedToThioamideNitrogen {
    4657   my($This, $NitrogenAtom) = @_;
    4658
    4659   return $This->_IsThioAmideNitrogen($NitrogenAtom) ? 1 : 0;
    4660 }
    4661
    4662 # HNCC : H-N IN ENAMINES (H-N-C=C)
    4663 #
    4664 sub _IsHydrogenAttachedToEnamineNitrogen {
    4665   my($This, $NitrogenAtom) = @_;
    4666
    4667   return $This->_IsNCCNitrogen($NitrogenAtom) ? 1 : 0;
    4668 }
    4669
    4670 # HNCN : H-N IN H-N-C=N
    4671 #
    4672 sub _IsHydrogenAttachedToNCNNitrogen {
    4673   my($This, $NitrogenAtom) = @_;
    4674
    4675   return $This->_IsNCNNitrogen($NitrogenAtom) ? 1 : 0;
    4676 }
    4677
    4678 #  HNNC : H-N IN H-N-N=C
    4679 #
    4680 sub _IsHydrogenAttachedToNNCNitrogen {
    4681   my($This, $NitrogenAtom) = @_;
    4682
    4683   return $This->_IsNNCNitrogen($NitrogenAtom) ? 1 : 0;
    4684 }
    4685
    4686 # HNNN : H-N IN H-N-N=N
    4687 #
    4688 sub _IsHydrogenAttachedToNNNNitrogen {
    4689   my($This, $NitrogenAtom) = @_;
    4690
    4691   return $This->_IsNNNNitrogen($NitrogenAtom) ? 1 : 0;
    4692 }
    4693
    4694 # HNSO : H-N IN SULFONAMIDE
    4695 #
    4696 sub _IsHydrogenAttachedToSulfonamideNitrogen {
    4697   my($This, $NitrogenAtom) = @_;
    4698
    4699   return $This->_IsNSO2SulfonamideNitrogen($NitrogenAtom) ? 1 : 0;
    4700 }
    4701
    4702 # HNPO : H-N IN PHOSPHONAMIDE
    4703 #
    4704 sub _IsHydrogenAttachedToPhosphonamideNitrogen {
    4705   my($This, $NitrogenAtom) = @_;
    4706
    4707   return $This->_IsNPO2PhosphonamideNitrogen($NitrogenAtom) ? 1 : 0;
    4708 }
    4709
    4710 # HNC% : HYDROGEN ON N ATTACHED TO TRIPLY BONDED CARBON
    4711 #
    4712 sub _IsHydrogenAttachedToTriplyBondedToCarbonNitrogen {
    4713   my($This, $NitrogenAtom) = @_;
    4714
    4715   return $This->_IsAttchedToCCTripleBondNitrogen($NitrogenAtom) ? 1 : 0;
    4716 }
    4717
    4718 # HSP2 : GENERAL H ON SP2 NITROGEN
    4719 #
    4720 sub _IsHydrogenAttachedToSP2Nitrogen {
    4721   my($This, $NitrogenAtom) = @_;
    4722
    4723   return $NitrogenAtom->DoesAtomNeighborhoodMatch('N.DB1') ? 1 : 0;
    4724 }
    4725
    4726 # HNR+ : H ON QUATERNARY NITROGEN
    4727 #
    4728 sub _IsHydrogenAttachedToQuaternaryNitrogen {
    4729   my($This, $NitrogenAtom) = @_;
    4730
    4731   return $NitrogenAtom->DoesAtomNeighborhoodMatch('N.T4.FC+1', ['*', '*', '*', '*'], ['-', '-', '-', '-']) ? 1 : 0;
    4732 }
    4733
    4734 # HIM+ : H ON IMIDAZOLIUM-TYPE NITROGEN
    4735 #
    4736 sub _IsHydrogenAttachedToImidazoliumNitrogen {
    4737   my($This, $NitrogenAtom) = @_;
    4738
    4739   return $This->_IsImidazoliumNitrogen($NitrogenAtom) ? 1 : 0;
    4740 }
    4741
    4742 # HPD+ : H ON PROTONATED PYRIDINE NITROGEN
    4743 #
    4744 sub _IsHydrogenAttachedToPositivelyChargedPyridineNitrogen {
    4745   my($This, $NitrogenAtom) = @_;
    4746
    4747   return $This->_IsPyridiniumNitrogen($NitrogenAtom) ? 1 : 0;
    4748 }
    4749
    4750 # HNN+ : H ON AMIDINIUM-TYPE NITROGEN
    4751 #
    4752 sub _IsHydrogenAttachedToAmidiniumNitrogen {
    4753   my($This, $NitrogenAtom) = @_;
    4754
    4755   return $This->_IsPositivelyChargedAzoNitrogen($NitrogenAtom) ? 1 : 0;
    4756 }
    4757
    4758 # HNC+ : H ON PROTONATED IMINE NITROGEN
    4759 #
    4760 sub _IsHydrogenAttachedToPositivelyChargedImineNitrogen {
    4761   my($This, $NitrogenAtom) = @_;
    4762
    4763   return $This->_IsPositivelyChargedIminiumNitrogen($NitrogenAtom) ? 1 : 0;
    4764 }
    4765
    4766 # HGD+ : H ON GUANIDINIUM-TYPE NITROGEN
    4767 #
    4768 sub _IsHydrogenAttachedToGuanidiniumNitrogen {
    4769   my($This, $NitrogenAtom) = @_;
    4770
    4771   return $This->_IsGuanidiniumNitrogen($NitrogenAtom) ? 1 : 0;
    4772 }
    4773
    4774 # HN5+ : H ON N5+, N5A+ OR N5B+
    4775 #
    4776 sub _IsHydrogenAttachedToFiveMemberedHetreoCyclicPostivelyChargedNitrogen {
    4777   my($This, $NitrogenAtom) = @_;
    4778
    4779   if (!$NitrogenAtom->DoesAtomNeighborhoodMatch('N.RA5.FC+1')) {
    4780     return 0;
    4781   }
    4782
    4783   return ($This->_IsPositivelyChargedFiveMemberedHeteroAromaticRingAlphaNitrogen($NitrogenAtom) ||
    4784          $This->_IsPositivelyChargedFiveMemberedHeteroAromaticRingBetaNitrogen($NitrogenAtom) ||
    4785          $This->_IsPositivelyChargedFiveMemberedHeteroCyclicRingNitrogen($NitrogenAtom)) ? 1 : 0;
    4786 }
    4787
    4788 # Get MMFF94 atom type for Hydrogen attached to Oxygen...
    4789 #
    4790 # 11 AtomTypeSymbols for element H:
    4791 #
    4792 # AtomTypeSymbol   AtomTypeNum   AtomTypeDefinition
    4793 #   HOR      21    HYDROGEN IN ALCOHOLS
    4794 #   HO       21    GENERAL H ON OXYGEN
    4795 #   HOM      21    HYDROGEN IN HYDROXIDE ANION
    4796 #   HOCO     24    H-O IN CARBOXYLIC ACIDS
    4797 #   HOP      24    HYDROGEN ON OXYGEN ATTACHED TO PHOSPHOROUS
    4798 #   HOCC     29    H-O IN ENOLS AND PHENOLS
    4799 #   HOCN     29    H-O IN HO-C=N
    4800 #   HOH      31    HYDROGEN IN H2O
    4801 #   HOS      33    H ON OXYGEN ATTACHED TO SULFUR
    4802 #   HO+      50    HYDROGEN ON O+ OXYGEN
    4803 #   HO=+     52    HYDROGEN ON OXENIUM OXYGEN
    4804 #
    4805 sub _GetAtomTypeForHydrogenAttachedToOxygen {
    4806   my($This, $OxygenAtom) = @_;
    4807   my($AtomType);
    4808
    4809   $AtomType = 'None';
    4810
    4811   ATOMTYPE: {
    4812
    4813     # HOP : HYDROGEN ON OXYGEN ATTACHED TO PHOSPHOROUS
    4814     if ($This->_IsHydrogenAttachedToOPOxygen($OxygenAtom)) {
    4815       $AtomType = 'HOP';
    4816       last ATOMTYPE;
    4817     }
    4818
    4819     # HOS : H ON OXYGEN ATTACHED TO SULFUR
    4820     if ($This->_IsHydrogenAttachedToOSOxygen($OxygenAtom)) {
    4821       $AtomType = 'HOS';
    4822       last ATOMTYPE;
    4823     }
    4824
    4825     # HOCO : H-O IN CARBOXYLIC ACIDS
    4826     if ($This->_IsHydrogenAttachedToCarboxylicAcidOxygen($OxygenAtom)) {
    4827       $AtomType = 'HOCO';
    4828       last ATOMTYPE;
    4829     }
    4830
    4831     # HOCC : H-O IN ENOLS AND PHENOLS
    4832     if ($This->_IsHydrogenAttachedToEnolOrPhenolOxygen($OxygenAtom)) {
    4833       $AtomType = 'HOCC';
    4834       last ATOMTYPE;
    4835     }
    4836
    4837     # HOCN : H-O IN HO-C=N
    4838     if ($This->_IsHydrogenAttachedToOCNOxygen($OxygenAtom)) {
    4839       $AtomType = 'HOCN';
    4840       last ATOMTYPE;
    4841     }
    4842
    4843     # HOM : HYDROGEN IN HYDROXIDE ANION
    4844     if ($This->_IsHydrogenAttachedToHydroxideAnionOxygen($OxygenAtom)) {
    4845       $AtomType = 'HOM';
    4846       last ATOMTYPE;
    4847     }
    4848
    4849     # HO+ : HYDROGEN ON O+ OXYGEN
    4850     if ($This->_IsHydrogenAttachedToPositivelyChargedOxygen($OxygenAtom)) {
    4851       $AtomType = 'HO+';
    4852       last ATOMTYPE;
    4853     }
    4854
    4855     # HO=+ : HYDROGEN ON OXENIUM OXYGEN
    4856     if ($This->_IsHydrogenAttachedToOxeniumOxygen($OxygenAtom)) {
    4857       $AtomType = 'HO=+';
    4858       last ATOMTYPE;
    4859     }
    4860
    4861     # HOR : HYDROGEN IN ALCOHOLS
    4862     if ($This->_IsHydrogenAttachedToAlcoholOxygen($OxygenAtom)) {
    4863       $AtomType = 'HOR';
    4864       last ATOMTYPE;
    4865     }
    4866
    4867     # HOH : HYDROGEN IN H2O
    4868     if ($This->_IsHydrogenAttachedToWaterOxygen($OxygenAtom)) {
    4869       $AtomType = 'HOH';
    4870       last ATOMTYPE;
    4871     }
    4872
    4873     # HO: GENERAL H ON OXYGEN
    4874     $AtomType = 'HO';
    4875   }
    4876   return $AtomType;
    4877 }
    4878
    4879 # HOR : HYDROGEN IN ALCOHOLS
    4880 #
    4881 sub _IsHydrogenAttachedToAlcoholOxygen {
    4882   my($This, $OxygenAtom) = @_;
    4883
    4884   return $This->_IsAlcoholOxygen($OxygenAtom) ? 1 : 0;
    4885 }
    4886
    4887 # HO : GENERAL H ON OXYGEN
    4888 #
    4889 sub _IsHydrogenAttachedToOxygen {
    4890   my($This, $OxygenAtom) = @_;
    4891
    4892   return 1;
    4893 }
    4894
    4895 # HOM : HYDROGEN IN HYDROXIDE ANION
    4896 #
    4897 sub _IsHydrogenAttachedToHydroxideAnionOxygen {
    4898   my($This, $OxygenAtom) = @_;
    4899
    4900   # H-(O-)
    4901   return $OxygenAtom->DoesAtomNeighborhoodMatch('O.FC-1.T1', ['H'], ['-']) ? 1 : 0;
    4902 }
    4903
    4904 # HOCO : H-O IN CARBOXYLIC ACIDS
    4905 #
    4906 sub _IsHydrogenAttachedToCarboxylicAcidOxygen {
    4907   my($This, $OxygenAtom) = @_;
    4908
    4909   return $This->_IsEsterOrCarboxylicAcidOxygen($OxygenAtom) ? 1 : 0;
    4910 }
    4911
    4912 # HOP : HYDROGEN ON OXYGEN ATTACHED TO PHOSPHOROUS
    4913 #
    4914 sub _IsHydrogenAttachedToOPOxygen {
    4915   my($This, $OxygenAtom) = @_;
    4916
    4917   return $OxygenAtom->DoesAtomNeighborhoodMatch('O', ['P'], ['*']) ? 1 : 0;
    4918 }
    4919
    4920 # HOCC : H-O IN ENOLS AND PHENOLS
    4921 #
    4922 sub _IsHydrogenAttachedToEnolOrPhenolOxygen {
    4923   my($This, $OxygenAtom) = @_;
    4924
    4925   return $This->_IsEnolicOrPhenolicOxygen($OxygenAtom) ? 1 : 0;
    4926 }
    4927
    4928 # HOCN : H-O IN HO-C=N
    4929 #
    4930 sub _IsHydrogenAttachedToOCNOxygen {
    4931   my($This, $OxygenAtom) = @_;
    4932
    4933   return $This->_IsOCNDivalentOxygen($OxygenAtom) ? 1 : 0;
    4934 }
    4935
    4936 # HOH : HYDROGEN IN H2O
    4937 #
    4938 sub _IsHydrogenAttachedToWaterOxygen {
    4939   my($This, $OxygenAtom) = @_;
    4940
    4941   return $This->_IsWaterOxygen($OxygenAtom) ? 1 : 0;
    4942 }
    4943
    4944 # HOS : H ON OXYGEN ATTACHED TO SULFUR
    4945 #
    4946 sub _IsHydrogenAttachedToOSOxygen {
    4947   my($This, $OxygenAtom) = @_;
    4948
    4949   return $OxygenAtom->DoesAtomNeighborhoodMatch('O', ['S'], ['*']) ? 1 : 0;
    4950 }
    4951
    4952 # HO+ : HYDROGEN ON O+ OXYGEN
    4953 #
    4954 sub _IsHydrogenAttachedToPositivelyChargedOxygen {
    4955   my($This, $OxygenAtom) = @_;
    4956
    4957   return $This->_IsPositivelyChargedOxoniumOxygen($OxygenAtom) ? 1 : 0;
    4958 }
    4959
    4960 # HO=+ : HYDROGEN ON OXENIUM OXYGEN
    4961 #
    4962 sub _IsHydrogenAttachedToOxeniumOxygen {
    4963   my($This, $OxygenAtom) = @_;
    4964
    4965   return $This->_IsPositivelyChargedOxeniumOxygen($OxygenAtom) ? 1 : 0;
    4966 }
    4967
    4968 # Get MMFF94 atom type for Hydrogen attached to Phosphorus...
    4969 #
    4970 sub _GetAtomTypeForHydrogenAttachedToPhosphorus {
    4971   my($This, $PhosphorusAtom) = @_;
    4972   my($AtomType);
    4973
    4974   # HP : H ATTACHED TO TRI- OR TETRACOORDINATE PHOSPHORUS
    4975   $AtomType = 'HP';
    4976
    4977   return $AtomType;
    4978 }
    4979
    4980 # Get MMFF94 atom type for Hydrogen attached to Sulfur...
    4981 #
    4982 sub _GetAtomTypeForHydrogenAttachedToSulfur {
    4983   my($This, $SulfurAtom) = @_;
    4984   my($AtomType);
    4985
    4986   $AtomType = 'None';
    4987
    4988   ATOMTYPE: {
    4989
    4990    # HS=N : H ATTACHED TO TETRAVALENT, TRICOODR S DBL BONDED TO N
    4991     if ($This->_IsSNTricoordinatedSulfur($SulfurAtom)) {
    4992       $AtomType = 'HS=N';
    4993       last ATOMTYPE;
    4994     }
    4995
    4996     # HS : H ATTACHED TO DIVALENT, DICOORDINATE S
    4997     if ($This->_IsDivalentDiCoordinatedSulfur($SulfurAtom)) {
    4998       $AtomType = 'HS';
    4999       last ATOMTYPE;
    5000     }
    5001
    5002     $AtomType = 'None';
    5003     carp "Warning: ${ClassName}->_GetAtomTypeForHydrogenAttachedToSulfur: MMFF94 atom type for Sulfur cann't be assigned...";
    5004   }
    5005   return $AtomType;
    5006 }
    5007
    5008 # Get MMFF94 atom type for Hydrogen attached to Silicon...
    5009 #
    5010 sub _GetAtomTypeForHydrogenAttachedToSilicon {
    5011   my($This, $SiliconAtom) = @_;
    5012   my($AtomType);
    5013
    5014   # HSI : H ATTACHED TO SI
    5015   $AtomType = 'HSI';
    5016
    5017   return $AtomType;
    5018 }
    5019
    5020 # Get information about number and types of hetero atoms present in ring...
    5021 #
    5022 # Note:
    5023 #   . Any atom other than Carbon and Hydrogen atom is considered a hetero atom.
    5024 #
    5025 sub _GetHeteroAtomsInformationInRing {
    5026   my($This, $RingAtomsRef) = @_;
    5027   my($RingAtom, $RingAtomSymbol, $RingIsAromatic, $NumOfAromaticAtoms, $NumOfHeteroAtoms, %HeteroAtomSymbolsMap);
    5028
    5029   %HeteroAtomSymbolsMap = ();
    5030
    5031   $NumOfAromaticAtoms = 0;
    5032   $NumOfHeteroAtoms = 0;
    5033
    5034   RINGATOM: for $RingAtom (@{$RingAtomsRef}) {
    5035     if ($RingAtom->IsAromatic()) {
    5036       $NumOfAromaticAtoms++;
    5037     }
    5038
    5039     if (!$This->_IsHeteroAtom($RingAtom)) {
    5040       next RINGATOM;
    5041     }
    5042     $NumOfHeteroAtoms++;
    5043
    5044     $RingAtomSymbol = $RingAtom->GetAtomSymbol();
    5045     if (exists $HeteroAtomSymbolsMap{$RingAtomSymbol}) {
    5046       $HeteroAtomSymbolsMap{$RingAtomSymbol} += 1;
    5047     }
    5048     else {
    5049       $HeteroAtomSymbolsMap{$RingAtomSymbol} = 1;
    5050     }
    5051   }
    5052   $RingIsAromatic = ($NumOfAromaticAtoms == scalar @{$RingAtomsRef}) ? 1 : 0;
    5053
    5054   return ($RingIsAromatic, $NumOfHeteroAtoms, \%HeteroAtomSymbolsMap);
    5055 }
    5056
    5057 # Check whether specified atom has a hetero atom at alpha position in an aromatic ring...
    5058 #
    5059 sub _IsAtomPositionAlphaInHeteroAromaticRing {
    5060   my($This, $Atom, $RingAtomsRef) = @_;
    5061   my($CheckRingAromaticity);
    5062
    5063   $CheckRingAromaticity = 1;
    5064
    5065   return $This->_IsAtomPositionAlphaInHeteroRing($Atom, $RingAtomsRef, $CheckRingAromaticity);
    5066 }
    5067
    5068 # Check whether specified atom has a hetero atom at beta position in an aromatic ring...
    5069 #
    5070 sub _IsAtomPositionBetaInHeteroAromaticRing {
    5071   my($This, $Atom, $RingAtomsRef) = @_;
    5072   my($CheckRingAromaticity);
    5073
    5074   $CheckRingAromaticity = 1;
    5075
    5076   return $This->_IsAtomPositionBetaInHeteroRing($Atom, $RingAtomsRef, $CheckRingAromaticity);
    5077 }
    5078
    5079 # Check whether specified atom has a hetero atom at alpha position in an aromatic or non-aromatic
    5080 # ring...
    5081 #
    5082 sub _IsAtomPositionAlphaInHeteroRing {
    5083   my($This, $Atom, $RingAtomsRef, $CheckRingAromaticity) = @_;
    5084   my($RingIsAromatic, $NumOfHeteroAtoms, $NumOfAllowedHeteroAtoms, $HeteroAtomPositionsRef);
    5085
    5086   $CheckRingAromaticity = defined($CheckRingAromaticity) && $CheckRingAromaticity ? 1 : 0;
    5087
    5088   # Is it an aromatic ring containing appropriate number hertero atoms?
    5089   ($RingIsAromatic, $NumOfHeteroAtoms) = $This->_GetHeteroAtomsInformationInRing($RingAtomsRef);
    5090   $NumOfAllowedHeteroAtoms = $Atom->IsCarbon() ? 1 : 2;
    5091
    5092   if ($CheckRingAromaticity && !$RingIsAromatic) {
    5093     return 0;
    5094   }
    5095
    5096   if ($NumOfHeteroAtoms != $NumOfAllowedHeteroAtoms) {
    5097     return 0;
    5098   }
    5099
    5100   # Does ring contain hetero atoms at alpha position?
    5101   $HeteroAtomPositionsRef = $This->_GetHeteroAtomPositionsInRing($Atom, $RingAtomsRef);
    5102   if (exists($HeteroAtomPositionsRef->{Alpha}) && !exists($HeteroAtomPositionsRef->{Beta})) {
    5103     return 1;
    5104   }
    5105
    5106   return 0;
    5107 }
    5108
    5109 # Check whether specified atom has a hetero atom at alpha position in an aromatic or non-aromatic
    5110 # ring...
    5111 #
    5112 sub _IsAtomPositionBetaInHeteroRing {
    5113   my($This, $Atom, $RingAtomsRef, $CheckRingAromaticity) = @_;
    5114   my($RingIsAromatic, $NumOfHeteroAtoms, $NumOfAllowedHeteroAtoms, $HeteroAtomPositionsRef);
    5115
    5116   $CheckRingAromaticity = defined($CheckRingAromaticity) && $CheckRingAromaticity ? 1 : 0;
    5117
    5118   # Is it an aromatic ring containing hertero atoms?
    5119   ($RingIsAromatic, $NumOfHeteroAtoms) = $This->_GetHeteroAtomsInformationInRing($RingAtomsRef);
    5120   $NumOfAllowedHeteroAtoms = $Atom->IsCarbon() ? 1 : 2;
    5121
    5122   if ($CheckRingAromaticity && !$RingIsAromatic) {
    5123     return 0;
    5124   }
    5125
    5126   if ($NumOfHeteroAtoms != $NumOfAllowedHeteroAtoms) {
    5127     return 0;
    5128   }
    5129
    5130   # Does ring contain hetero atoms at alpha position?
    5131   $HeteroAtomPositionsRef = $This->_GetHeteroAtomPositionsInRing($Atom, $RingAtomsRef);
    5132   if (exists($HeteroAtomPositionsRef->{Beta}) && !exists($HeteroAtomPositionsRef->{Alpha})) {
    5133     return 1;
    5134   }
    5135
    5136   return 0;
    5137 }
    5138
    5139 # Get hetro atom positions relative to atom position...
    5140 #
    5141 # Notes:
    5142 #    . Any atom other than Carbon and Hydrogen atom is considered a hetero atom.
    5143 #
    5144 sub _GetHeteroAtomPositionsInRing {
    5145   my($This, $Atom, $RingAtomsRef) = @_;
    5146   my($RingAtom, $Index, $AtomIndex, $NumOfHeteroAtoms, %HeteroAtomPositionsMap);
    5147
    5148   %HeteroAtomPositionsMap = ();
    5149
    5150   $NumOfHeteroAtoms = 0;
    5151   $AtomIndex = 0;
    5152   $Index = 0;
    5153
    5154   # Find position of specified atom in the ring and count hetero atoms...
    5155   for $RingAtom (@{$RingAtomsRef}) {
    5156     if ($This->_IsHeteroAtom($RingAtom)) {
    5157       $NumOfHeteroAtoms++;
    5158     }
    5159     if ($RingAtom->GetID() == $Atom->GetID()) {
    5160       $AtomIndex = $Index;
    5161     }
    5162     $Index++;
    5163   }
    5164
    5165   # Does ring contain any hetereo atoms?
    5166   if (!$NumOfHeteroAtoms) {
    5167     return \%HeteroAtomPositionsMap;
    5168   }
    5169
    5170   # Check hetero atoms around specified atom to determine their position using their
    5171   # their distance from specified atom: 1 - Alpha, 2 - Beta, 3 - Gamma, 4 - Delta, 5 - Omega
    5172   #
    5173   my($RingSize, $MaxPositionNum, $PositionNum, $PositionName, $MaxAtomIndex, $BeforeAtomIndex, $AfterAtomIndex, $BeforeAtom, $AfterAtom, %PositionNumToNameMap);
    5174
    5175   %PositionNumToNameMap = ('1' => 'Alpha', '2' => 'Beta', '3' => 'Gamma', '4' => 'Delta', '5' => 'Omega');
    5176
    5177   $RingSize = scalar @{$RingAtomsRef};
    5178   $MaxPositionNum = int $RingSize/2;
    5179   $MaxAtomIndex = $RingSize - 1;
    5180
    5181   POSITIONNUM: for $PositionNum (1 .. $MaxPositionNum) {
    5182     # Get atom before atom at a specific position...
    5183     $BeforeAtomIndex = $AtomIndex - $PositionNum;
    5184     if ($BeforeAtomIndex < 0) {
    5185       $BeforeAtomIndex = $MaxAtomIndex + $BeforeAtomIndex + 1;
    5186     }
    5187     $BeforeAtom = $RingAtomsRef->[$BeforeAtomIndex];
    5188
    5189     $PositionName = exists $PositionNumToNameMap{$PositionNum} ? $PositionNumToNameMap{$PositionNum} : 'Unknown';
    5190
    5191     # Is atom before atom at a specific position a hetero atom?
    5192     if (!$BeforeAtom->IsCarbon()) {
    5193       $This->_TrackHeteroAtomPositionInRing($BeforeAtom, $PositionName, \%HeteroAtomPositionsMap);
    5194     }
    5195
    5196     # Get atom after atom at a specific position...
    5197     $AfterAtomIndex = $AtomIndex + $PositionNum;
    5198     if ($AfterAtomIndex > $MaxAtomIndex) {
    5199       $AfterAtomIndex = $AfterAtomIndex - $MaxAtomIndex - 1;
    5200     }
    5201
    5202     # Is it a different atom?
    5203     if ($AfterAtomIndex == $BeforeAtomIndex) {
    5204       next POSITIONNUM;
    5205     }
    5206
    5207     # Is atom after atom at a specific position a hetero atom?
    5208     $AfterAtom = $RingAtomsRef->[$AfterAtomIndex];
    5209
    5210     if (!$AfterAtom->IsCarbon()) {
    5211       $This->_TrackHeteroAtomPositionInRing($AfterAtom, $PositionName, \%HeteroAtomPositionsMap);
    5212     }
    5213   }
    5214   return \%HeteroAtomPositionsMap;
    5215 }
    5216
    5217 # Is it a hetero atom?
    5218 #
    5219 # Notes:
    5220 #    . Any atom other than Carbon and Hydrogen atom is considered a hetero atom.
    5221 #
    5222 sub _IsHeteroAtom {
    5223   my($This, $Atom) = @_;
    5224
    5225   return ($Atom->IsCarbon() || $Atom->IsHydrogen()) ? 0 : 1;
    5226 }
    5227
    5228 # Track hetero atom positions in ring by updating data in specified data hash reference...
    5229 #
    5230 sub _TrackHeteroAtomPositionInRing {
    5231   my($This, $HeteroAtom, $PositionName, $HeteroAtomPositionsMapRef) = @_;
    5232   my($HeteroAtomSymbol);
    5233
    5234   # Is it a new hetero atom position?
    5235   if (!exists $HeteroAtomPositionsMapRef->{$PositionName}) {
    5236     %{$HeteroAtomPositionsMapRef->{$PositionName}} = ();
    5237   }
    5238
    5239   $HeteroAtomSymbol = $HeteroAtom->GetAtomSymbol();
    5240   if (exists $HeteroAtomPositionsMapRef->{$PositionName}{$HeteroAtomSymbol}) {
    5241     $HeteroAtomPositionsMapRef->{$PositionName}{$HeteroAtomSymbol} += 1;
    5242   }
    5243   else {
    5244     $HeteroAtomPositionsMapRef->{$PositionName}{$HeteroAtomSymbol} += 1;
    5245   }
    5246   return $This;
    5247 }
    5248
    5249 # Return a string containg data for MMFF94AtomTypes object...
    5250 #
    5251 sub StringifyMMFF94AtomTypes {
    5252   my($This) = @_;
    5253   my($AtomTypesString);
    5254
    5255   # Type of AtomTypes...
    5256   $AtomTypesString = "AtomTypes: $This->{Type}; IgnoreHydrogens: " . ($This->{IgnoreHydrogens} ? "Yes" : "No");
    5257
    5258   # Setup atom types information...
    5259   my($AtomID, $AtomType, @AtomTypesInfo, %AssignedAtomTypes);
    5260
    5261   @AtomTypesInfo = ();
    5262   %AssignedAtomTypes = $This->GetAtomTypes();
    5263
    5264   for $AtomID (sort { $a <=> $b } keys %AssignedAtomTypes) {
    5265     $AtomType = $AssignedAtomTypes{$AtomID} ? $AssignedAtomTypes{$AtomID} : 'None';
    5266     push @AtomTypesInfo, "$AtomID:$AtomType";
    5267   }
    5268   $AtomTypesString .= "; AtomIDs:AtomTypes: <" . TextUtil::JoinWords(\@AtomTypesInfo, ", ", 0) . ">";
    5269
    5270   return $AtomTypesString;
    5271 }
    5272
    5273 # Is it a MMFF94AtomTypes object?
    5274 sub _IsMMFF94AtomTypes {
    5275   my($Object) = @_;
    5276
    5277   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
    5278 }
    5279
    5280 # Check and load MMFF94 atom types data...
    5281 #
    5282 sub _CheckAndLoadMMFF94AtomTypesData {
    5283
    5284   # Is it already loaded?
    5285   if (exists $MMFF94AtomTypesDataMap{AtomTypes}) {
    5286     return;
    5287   }
    5288
    5289   _LoadMMFF94AtomTypesData();
    5290 }
    5291
    5292 # Load MMFF94 atom types data from the file assuming first column to be atom type symbol..
    5293 #
    5294 # Format:
    5295 #
    5296 # "AtomTypeSymbol","AtomTypeNum","ElementSymbol","AtomTypeDefinition"
    5297 # "CR","1","C","ALKYL CARBON, SP3"
    5298 # "C=C","2","C","VINYLIC CARBON, SP2"
    5299 #
    5300 sub _LoadMMFF94AtomTypesData {
    5301   my($AtomTypesDataFile, $MayaChemToolsLibDir);
    5302
    5303   $MayaChemToolsLibDir = FileUtil::GetMayaChemToolsLibDirName();
    5304
    5305   $AtomTypesDataFile =  "$MayaChemToolsLibDir" . "/data/MMFF94AtomTypes.csv";
    5306   if (! -e "$AtomTypesDataFile") {
    5307     croak "Error: MayaChemTools package file, $AtomTypesDataFile, is missing: Possible installation problems...";
    5308   }
    5309
    5310   %MMFF94AtomTypesDataMap = ();
    5311   AtomTypes::AtomTypes::LoadAtomTypesData($AtomTypesDataFile, \%MMFF94AtomTypesDataMap);
    5312 }
    5313