MayaChemTools

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