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