MayaChemTools

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