MayaChemTools

   1 package AtomTypes::UFFAtomTypes;
   2 #
   3 # $RCSfile: UFFAtomTypes.pm,v $
   4 # $Date: 2015/02/28 20:48:03 $
   5 # $Revision: 1.20 $
   6 #
   7 # Author: Manish Sud <msud@san.rr.com>
   8 #
   9 # Copyright (C) 2015 Manish Sud. All rights reserved.
  10 #
  11 # This file is part of MayaChemTools.
  12 #
  13 # MayaChemTools is free software; you can redistribute it and/or modify it under
  14 # the terms of the GNU Lesser General Public License as published by the Free
  15 # Software Foundation; either version 3 of the License, or (at your option) any
  16 # later version.
  17 #
  18 # MayaChemTools is distributed in the hope that it will be useful, but without
  19 # any warranty; without even the implied warranty of merchantability of fitness
  20 # for a particular purpose.  See the GNU Lesser General Public License for more
  21 # details.
  22 #
  23 # You should have received a copy of the GNU Lesser General Public License
  24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
  25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
  26 # Boston, MA, 02111-1307, USA.
  27 #
  28 
  29 use strict;
  30 use Carp;
  31 use Exporter;
  32 use Scalar::Util ();
  33 use AtomTypes::AtomTypes;
  34 use Molecule;
  35 
  36 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  37 
  38 @ISA = qw(AtomTypes::AtomTypes Exporter);
  39 @EXPORT = qw(GetUFFAtomTypesData GetAllPossibleUFFAtomTypes GetAllPossibleUFFNonHydrogenAtomTypes);
  40 @EXPORT_OK = qw();
  41 
  42 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
  43 
  44 # Setup class variables...
  45 my($ClassName, %UFFAtomTypesDataMap);
  46 _InitializeClass();
  47 
  48 # Overload Perl functions...
  49 use overload '""' => 'StringifyUFFAtomTypes';
  50 
  51 # Class constructor...
  52 sub new {
  53   my($Class, %NamesAndValues) = @_;
  54 
  55   # Initialize object...
  56   my $This = $Class->SUPER::new();
  57   bless $This, ref($Class) || $Class;
  58   $This->_InitializeUFFAtomTypes();
  59 
  60   $This->_InitializeUFFAtomTypesProperties(%NamesAndValues);
  61 
  62   return $This;
  63 }
  64 
  65 # Initialize class ...
  66 sub _InitializeClass {
  67   #Class name...
  68   $ClassName = __PACKAGE__;
  69 
  70   # Initialize the data hash. It'll be loaded on demand later...
  71   %UFFAtomTypesDataMap = ();
  72 }
  73 
  74 # Initialize object data...
  75 #
  76 sub _InitializeUFFAtomTypes {
  77   my($This) = @_;
  78 
  79   # Type of AtomTypes...
  80   $This->{Type} = 'UFF';
  81 
  82   # By default, UFF atom types are also assigned to hydrogens...
  83   $This->{IgnoreHydrogens} = 0;
  84 
  85   return $This;
  86 }
  87 
  88 # Initialize object properties...
  89 #
  90 sub _InitializeUFFAtomTypesProperties {
  91   my($This, %NamesAndValues) = @_;
  92 
  93   my($Name, $Value, $MethodName);
  94   while (($Name, $Value) = each  %NamesAndValues) {
  95     $MethodName = "Set${Name}";
  96     $This->$MethodName($Value);
  97   }
  98 
  99   # Make sure molecule object was specified...
 100   if (!exists $NamesAndValues{Molecule}) {
 101     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying molecule...";
 102   }
 103 
 104   return $This;
 105 }
 106 
 107 # Get UFF atom types and associated data loaded from UFF data file as
 108 # a reference to hash with the following hash data format:
 109 #
 110 # @{$UFFAtomTypesDataMap{AtomTypes}} - Array of all possible atom types for all atoms
 111 # @{$UFFAtomTypesDataMap{NonHydrogenAtomTypes}} - Array of all possible atom types for non-hydrogen atoms
 112 # @{$UFFAtomTypesDataMap->{ColLabels}} - Array of column labels
 113 # %{$UFFAtomTypesDataMap->{DataCol<Num>}} - Hash keys pair: <DataCol<Num>, AtomType>
 114 #
 115 # This functionality can be either invoked as a class function or an
 116 # object method.
 117 #
 118 sub GetUFFAtomTypesData {
 119 
 120   # Make sure data is loaded...
 121   _CheckAndLoadUFFAtomTypesData();
 122 
 123   return \%UFFAtomTypesDataMap;
 124 }
 125 
 126 # Get all possible UFF atom types corresponding to hydrogen and non-hydrogen
 127 # atoms as an array reference...
 128 #
 129 # This functionality can be either invoked as a class function or an
 130 # object method.
 131 #
 132 sub GetAllPossibleUFFAtomTypes {
 133   return _GetAllPossibleUFFAtomTypes();
 134 }
 135 
 136 # Get all possible UFF atom types corresponding to non-hydrogen atoms
 137 # as an array reference...
 138 #
 139 # This functionality can be either invoked as a class function or an
 140 # object method.
 141 #
 142 sub GetAllPossibleUFFNonHydrogenAtomTypes {
 143   my($NonHydrogensOnly);
 144 
 145   $NonHydrogensOnly = 1;
 146   return _GetAllPossibleUFFAtomTypes($NonHydrogensOnly);
 147 }
 148 
 149 # Get all possible UFF atom types as an array reference...
 150 #
 151 sub _GetAllPossibleUFFAtomTypes {
 152   my($NonHydrogensOnly) = @_;
 153   my($UFFAtomTypesDataRef);
 154 
 155   $NonHydrogensOnly = defined $NonHydrogensOnly ? $NonHydrogensOnly : 0;
 156 
 157   $UFFAtomTypesDataRef = GetUFFAtomTypesData();
 158 
 159   return $NonHydrogensOnly ? \@{$UFFAtomTypesDataRef->{NonHydrogenAtomTypes}}: \@{$UFFAtomTypesDataRef->{AtomTypes}};
 160 }
 161 # Assign UFF [ Ref 81-82 ] atom types to all atoms...
 162 #
 163 # Notes:
 164 #   . Some listed atom types - O_3_z,
 165 #     are not assigned to any atom
 166 #     o 126 UFF atom types are listed for elements with atomic number upto 103
 167 #     o AtomTypes::AtomTypes::UFFAtomTypes.pm module is used to assign UFF atom types
 168 #     o Units:
 169 #         o ValenceBondRadius and NonBondRadius: Angstroms
 170 #         o ValenceAngle: Degrees
 171 #         o NonBondEnergy and SP3TorsionalBarrier: kcal/mol
 172 #     o Five-character mnemonic label for UFF atom types
 173 #         o First two characters correspond to chemical symbol with an underscore as second
 174 #           character for elements with one character symbol
 175 #         o Third character describes hybridization or geometry: 1 - linear; 2 - trigonal; R - resonant;
 176 #           3 = tetrahedral; 4 - square planar; 5 - trigonal bipyramidal; 6 - octahedral
 177 #         o Fourth and fifth characters are used as indicators of alternate parameters: formal oxidation
 178 #           state, bridging hydrogens and so on.
 179 #
 180 #
 181 sub AssignAtomTypes {
 182   my($This) = @_;
 183   my($Atom, $AtomType);
 184 
 185   ATOM: for $Atom ($This->GetMolecule()->GetAtoms()) {
 186     if ($This->{IgnoreHydrogens} && $Atom->IsHydrogen()) {
 187       next ATOM;
 188     }
 189     $AtomType = $This->_GetAtomType($Atom);
 190     $This->SetAtomType($Atom, $AtomType);
 191   }
 192   return $This;
 193 }
 194 
 195 # Get UFF atom type for atom...
 196 #
 197 sub _GetAtomType {
 198   my($This, $Atom) = @_;
 199   my($AtomType);
 200 
 201   $AtomType = '';
 202 
 203   ATOM: {
 204     if ($Atom->IsCarbon()) {
 205       $AtomType = $This->_GetAtomTypeForCarbon($Atom);
 206       last ATOM;
 207     }
 208     if ($Atom->IsNitrogen()) {
 209       $AtomType = $This->_GetAtomTypeForNitrogen($Atom);
 210       last ATOM;
 211     }
 212     if ($Atom->IsOxygen()) {
 213       $AtomType = $This->_GetAtomTypeForOxygen($Atom);
 214       last ATOM;
 215     }
 216     if ($Atom->IsPhosphorus()) {
 217       $AtomType = $This->_GetAtomTypeForPhosphorus($Atom);
 218       last ATOM;
 219     }
 220     if ($Atom->IsSulfur()) {
 221       $AtomType = $This->_GetAtomTypeForSulfur($Atom);
 222       last ATOM;
 223     }
 224     if ($Atom->IsHydrogen()) {
 225       $AtomType = $This->_GetAtomTypeForHydrogen($Atom);
 226       last ATOM;
 227     }
 228     $AtomType = $This->_GetAtomTypeForOtherAtoms($Atom);
 229   }
 230 
 231   return $AtomType;
 232 }
 233 
 234 # Get UFF atom type for Carbon atom...
 235 #
 236 sub _GetAtomTypeForCarbon {
 237   my($This, $Atom) = @_;
 238   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
 239 
 240   $AtomType = 'None';
 241 
 242   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
 243 
 244   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
 245   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
 246 
 247   ATOMTYPE: {
 248     if ($Atom->IsAromatic()) {
 249       $AtomType = 'C_R';
 250       last ATOMTYPE;
 251     }
 252 
 253     # Only single bonds...
 254     if ($NumOfPiBonds == 0) {
 255       $AtomType = 'C_3';
 256       last ATOMTYPE;
 257     }
 258 
 259     # One double bond...
 260     if ($NumOfPiBonds == 1) {
 261       $AtomType = 'C_2';
 262       last ATOMTYPE;
 263     }
 264 
 265     # One triple bond or two double bonds...
 266     if ($NumOfPiBonds == 2) {
 267       $AtomType = 'C_1';
 268       last ATOMTYPE;
 269     }
 270 
 271     $AtomType = 'None';
 272     carp "Warning: ${ClassName}->_GetAtomTypeForCarbon: UFF atom types for Carbon cann't be assigned...";
 273   }
 274 
 275   return $AtomType;
 276 }
 277 
 278 # Get UFF atom type for Nitrogen atom...
 279 #
 280 sub _GetAtomTypeForNitrogen {
 281   my($This, $Atom) = @_;
 282   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
 283 
 284   $AtomType = 'None';
 285 
 286   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
 287 
 288   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
 289   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
 290 
 291   ATOMTYPE: {
 292     if ($Atom->IsAromatic()) {
 293       $AtomType = 'N_R';
 294       last ATOMTYPE;
 295     }
 296 
 297     # Only single bonds...
 298     if ($NumOfPiBonds == 0) {
 299       $AtomType = 'N_3';
 300       last ATOMTYPE;
 301     }
 302 
 303     # One double bond...
 304     if ($NumOfPiBonds == 1) {
 305       $AtomType = 'N_2';
 306       last ATOMTYPE;
 307     }
 308 
 309     # One triple bond or two double bonds...
 310     if ($NumOfPiBonds == 2) {
 311       $AtomType = 'N_1';
 312       last ATOMTYPE;
 313     }
 314     $AtomType = 'None';
 315     carp "Warning: ${ClassName}->_GetAtomTypeForNitrogen: UFF atom types for Nitrogen cann't be assigned...";
 316   }
 317 
 318   return $AtomType;
 319 }
 320 
 321 # Get UFF atom type for Oxygen atom...
 322 #
 323 sub _GetAtomTypeForOxygen {
 324   my($This, $Atom) = @_;
 325   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
 326 
 327   $AtomType = 'None';
 328 
 329   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
 330 
 331   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
 332   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
 333 
 334   ATOMTYPE: {
 335     if ($Atom->IsAromatic()) {
 336       $AtomType = 'O_R';
 337       last ATOMTYPE;
 338     }
 339 
 340     # Only single bonds...
 341     if ($NumOfPiBonds == 0) {
 342       $AtomType = 'O_3';
 343       last ATOMTYPE;
 344     }
 345 
 346     # One double bond...
 347     if ($NumOfPiBonds == 1) {
 348       $AtomType = 'O_2';
 349       last ATOMTYPE;
 350     }
 351 
 352     # One triple bond or two double bonds...
 353     if ($NumOfPiBonds == 2) {
 354       $AtomType = 'O_1';
 355       last ATOMTYPE;
 356     }
 357 
 358     $AtomType = 'None';
 359     carp "Warning: ${ClassName}->_GetAtomTypeForOxygen: UFF atom types for Oxygen cann't be assigned...";
 360   }
 361 
 362   return $AtomType;
 363 }
 364 
 365 # Get UFF atom type for Phosphorus atom...
 366 #
 367 sub _GetAtomTypeForPhosphorus {
 368   my($This, $Atom) = @_;
 369   my($AtomType, $NumOfSigmaBonds, $NumOfPiBonds);
 370 
 371   $AtomType = 'None';
 372 
 373   ($NumOfSigmaBonds, $NumOfPiBonds) = ('0') x 2;
 374 
 375   ($NumOfSigmaBonds, $NumOfPiBonds) = $Atom->GetNumOfSigmaAndPiBondsToNonHydrogenAtoms();
 376   $NumOfSigmaBonds += $Atom->GetAtomicInvariantValue('H');
 377 
 378   ATOMTYPE: {
 379     # Is it a four-coordinated Phosphorus for describing organometallic coordinated phosphines?
 380     if ($This->_IsFourCoordinatedOrganometallicPhosphorus($Atom)) {
 381       $AtomType = 'P_3+q';
 382       last ATOMTYPE;
 383     }
 384 
 385     # -P(-)-
 386     if ($NumOfSigmaBonds == 3 && $NumOfPiBonds == 0) {
 387       $AtomType = 'P_3+3';
 388       last ATOMTYPE;
 389     }
 390 
 391     # =P(-)(-)-
 392     if ($NumOfSigmaBonds == 4 && $NumOfPiBonds == 1) {
 393       $AtomType = 'P_3+5';
 394       last ATOMTYPE;
 395     }
 396 
 397     $AtomType = 'None';
 398     carp "Warning: ${ClassName}->_GetAtomTypeForPhosphorus: UFF atom types for Phosphorus cann't be assigned...";
 399   }
 400 
 401   return $AtomType;
 402 }
 403 
 404 # Get UFF atom type for Sulfur atom...
 405 #
 406 sub _GetAtomTypeForSulfur {
 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     if ($Atom->IsAromatic()) {
 419       $AtomType = 'S_R';
 420       last ATOMTYPE;
 421     }
 422 
 423     # -S-
 424     if ($NumOfSigmaBonds == 2 && $NumOfPiBonds == 0) {
 425       $AtomType = 'S_3+2';
 426       last ATOMTYPE;
 427     }
 428 
 429     # -S(=)-
 430     if ($NumOfSigmaBonds == 3 && $NumOfPiBonds == 1) {
 431       $AtomType = 'S_3+4';
 432       last ATOMTYPE;
 433     }
 434 
 435     # -S(=)(=)-
 436     if ($NumOfSigmaBonds == 4 && $NumOfPiBonds == 2) {
 437       $AtomType = 'S_3+6';
 438       last ATOMTYPE;
 439     }
 440 
 441     # S=
 442     if ($NumOfSigmaBonds == 1 && $NumOfPiBonds == 1) {
 443       $AtomType = 'S_2';
 444       last ATOMTYPE;
 445     }
 446 
 447     $AtomType = 'None';
 448     carp "Warning: ${ClassName}->_GetAtomTypeForSulfur: UFF atom types for Sulfur cann't be assigned...";
 449   }
 450   return $AtomType;
 451 }
 452 
 453 # Get UFF atom type for Hydrogen atom...
 454 #
 455 sub _GetAtomTypeForHydrogen {
 456   my($This, $Atom) = @_;
 457   my($AtomType);
 458 
 459   if ($Atom->GetNumOfHeavyAtomNeighbors() > 1) {
 460     # Bridging hydrogen as in B2H6
 461     $AtomType = 'H___b';
 462   }
 463   else {
 464     $AtomType = 'H_';
 465   }
 466 
 467   return $AtomType;
 468 }
 469 
 470 # Get UFF atom type for atoms other than Carbon, Nitrogen, Oxygen, Phosporus,
 471 # Sulfur and Hydrogen...
 472 #
 473 sub _GetAtomTypeForOtherAtoms {
 474   my($This, $Atom) = @_;
 475   my($AtomType, $AtomicNumber, $AtomSymbol, $GroupNumber, $MethodName);
 476 
 477   $AtomType = 'None';
 478 
 479   $AtomicNumber = $Atom->GetAtomicNumber();
 480   $AtomSymbol = $Atom->GetAtomSymbol();
 481   $GroupNumber = $Atom->GetGroupNumber();
 482 
 483   ATOMTYPE: {
 484     # Get atom types for atoms in a valid periodic table group number...
 485     if (defined($GroupNumber) && $GroupNumber) {
 486       $MethodName = "_GetAtomTypeForOtherAtomsInGroupNumber${GroupNumber}";
 487       $AtomType = $This->$MethodName($Atom);
 488       last ATOMTYPE;
 489     }
 490 
 491     # Get atom types for Lanthanidies...
 492     if ($AtomicNumber >= 57 && $AtomicNumber <= 71) {
 493       $AtomType = $This->_GetAtomTypeForOtherAtomsInLanthanoidGroup($Atom);
 494       last ATOMTYPE;
 495     }
 496 
 497     # Get atom types for Actinides...
 498     if ($AtomicNumber >= 89 && $AtomicNumber <= 103) {
 499       $AtomType = $This->_GetAtomTypeForOtherAtomsInActinoidGroup($Atom);
 500       last ATOMTYPE;
 501     }
 502 
 503     $AtomType = 'None';
 504     carp "Warning: ${ClassName}->_GetAtomTypeForOtherAtoms: UFF atom types for atom, $AtomSymbol, with atomic number, $AtomicNumber, cann't be assigned...";
 505   }
 506 
 507   return $AtomType;
 508 }
 509 
 510 # Get UFF atom type for atoms in periodic table group number 1...
 511 #
 512 # Group number 1 contains: H, Li, Na, K, Rb, Cs, Fr
 513 #
 514 # And corresponding UFF atom types are: Li, Na, K_, Rb, Cs, Fr
 515 #
 516 # Notes:
 517 #   . This method doesn't assign UFF atom type for H.
 518 #
 519 sub _GetAtomTypeForOtherAtomsInGroupNumber1 {
 520   my($This, $Atom) = @_;
 521   my($AtomType, $AtomSymbol);
 522 
 523   $AtomSymbol = $Atom->GetAtomSymbol();
 524   $AtomType = (length($AtomSymbol) == 1) ? "${AtomSymbol}_" : $AtomSymbol;
 525 
 526   return $AtomType;
 527 }
 528 
 529 # Get UFF atom type for atoms in periodic table group number 2...
 530 #
 531 # Group number 2 contains: Be, Mg, Ca, Sr, Ba, Ra
 532 #
 533 # And corresponding UFF atom types are: Be3+2, Mg3+2, Ca6+2, Sr6+2, Ba6+2, Ra6+2
 534 #
 535 # Notes:
 536 #   . Although the number of valence electrons is two, the tetrahedral and octahedral
 537 #     geometry is attributed to coordination bonds from other atoms.
 538 #
 539 sub _GetAtomTypeForOtherAtomsInGroupNumber2 {
 540   my($This, $Atom) = @_;
 541   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 542 
 543   $AtomType = 'None';
 544   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber2';
 545 
 546   $AtomSymbol = $Atom->GetAtomSymbol();
 547   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
 548 
 549   ATOMSYMBOL: {
 550     if ($AtomSymbol =~ /^(Be|Mg)$/) {
 551       $AtomType = "${AtomSymbol}3+2";
 552       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'tetrahedral', 4, 2);
 553       last ATOMSYMBOL;
 554     }
 555 
 556     if ($AtomSymbol =~ /^(Ca|Sr|Ba|Ra)$/) {
 557       $AtomType = "${AtomSymbol}6+2";
 558       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 2);
 559       last ATOMSYMBOL;
 560     }
 561     $AtomType = 'None';
 562   }
 563 
 564   return $AtomType;
 565 }
 566 
 567 # Get UFF atom type for atoms in periodic table group number 3...
 568 #
 569 # Group number 3 contains: Sc, Y, Lu, Lr
 570 #
 571 # And corresponding UFF atom types are: Sc3+3, Y_3+3, Lu6+3, Lr6+3
 572 #
 573 sub _GetAtomTypeForOtherAtomsInGroupNumber3 {
 574   my($This, $Atom) = @_;
 575   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 576 
 577   $AtomType = 'None';
 578   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber3';
 579 
 580   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
 581 
 582   $AtomSymbol = $Atom->GetAtomSymbol();
 583 
 584   ATOMSYMBOL: {
 585     if ($AtomSymbol =~ /^(Sc|Y)$/) {
 586       $AtomType = (length($AtomSymbol) == 1) ? "${AtomSymbol}_3+3" : "${AtomSymbol}3+3";
 587       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'tetrahedral', 4, 3);
 588       last ATOMSYMBOL;
 589     }
 590 
 591     if ($AtomSymbol =~ /^(Lu|Lr)$/) {
 592       $AtomType = "${AtomSymbol}6+3";
 593       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 3);
 594       last ATOMSYMBOL;
 595     }
 596 
 597     $AtomType = 'None';
 598   }
 599 
 600   return $AtomType;
 601 }
 602 
 603 # Get UFF atom type for atoms in periodic table group number 4...
 604 #
 605 # Group number 4 contains: Ti, Zr, Hf, Rf
 606 #
 607 # And corresponding UFF atom types are: Ti3+4, Ti3+6, Zr3+4, Hf3+4
 608 #
 609 # Notes:
 610 #   . No UFF atom type for Rf
 611 #
 612 sub _GetAtomTypeForOtherAtomsInGroupNumber4 {
 613   my($This, $Atom) = @_;
 614   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 615 
 616   $AtomType = 'None';
 617   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber4';
 618 
 619   $AtomSymbol = $Atom->GetAtomSymbol();
 620   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
 621 
 622   ATOMSYMBOL: {
 623     if ($AtomSymbol =~ /^Ti$/) {
 624       TI: {
 625         if ($NumOfNeighbors == 4 && $FormalOxidationState == 4) {
 626           $AtomType = "Ti3+4";
 627           $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 4, 4);
 628           last TI;
 629         }
 630 
 631         if ($NumOfNeighbors == 4 && $FormalOxidationState == 6) {
 632           $AtomType = "Ti3+6";
 633           $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 4, 6);
 634           last TI;
 635         }
 636 
 637         # Assign default value...
 638         $AtomType = "Ti3+6";
 639         $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 4, 6);
 640       }
 641       last ATOMSYMBOL;
 642     }
 643 
 644     if ($AtomSymbol =~ /^(Zr|Hf)$/) {
 645       $AtomType = "${AtomSymbol}3+4";
 646       $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 4, 4);
 647       last ATOMSYMBOL;
 648     }
 649 
 650     $AtomType = 'None';
 651   }
 652 
 653   return $AtomType;
 654 }
 655 
 656 # Get UFF atom type for atoms in periodic table group number 5...
 657 #
 658 # Group number 5 contains: V, Nb, Ta, Db
 659 #
 660 # And corresponding UFF atom types are: V_3+5, Nb3+5, Ta3+5
 661 #
 662 # Notes:
 663 #   . No UFF atom type for Db
 664 #
 665 sub _GetAtomTypeForOtherAtomsInGroupNumber5 {
 666   my($This, $Atom) = @_;
 667   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 668 
 669   $AtomType = 'None';
 670   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber5';
 671 
 672   $AtomSymbol = $Atom->GetAtomSymbol();
 673   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
 674 
 675   ATOMSYMBOL: {
 676     if ($AtomSymbol =~ /^(V|Nb|Ta)$/) {
 677       $AtomType = (length($AtomSymbol) == 1) ? "${AtomSymbol}_3+5" : "${AtomSymbol}3+5";
 678       $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 4, 5);
 679       last ATOMSYMBOL;
 680     }
 681 
 682     $AtomType = 'None';
 683   }
 684 
 685   return $AtomType;
 686 }
 687 
 688 # Get UFF atom type for atoms in periodic table group number 6...
 689 #
 690 # Group number 6 contains: Cr, Mo, W, Sg
 691 #
 692 # And corresponding UFF atom types are: Cr6+3, Mo6+6, Mo3+6, W_6+6, W_3+4, W_3+6
 693 #
 694 # Notes:
 695 #   . No UFF atom type for Sg
 696 #
 697 sub _GetAtomTypeForOtherAtomsInGroupNumber6 {
 698   my($This, $Atom) = @_;
 699   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 700 
 701   $AtomType = 'None';
 702   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber6';
 703 
 704   $AtomSymbol = $Atom->GetAtomSymbol();
 705   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
 706 
 707   ATOMSYMBOL: {
 708     if ($AtomSymbol =~ /^Cr$/) {
 709       $AtomType = "Cr6+3";
 710       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 3);
 711       last ATOMSYMBOL;
 712     }
 713 
 714     if ($AtomSymbol =~ /^Mo$/) {
 715       MO: {
 716         if ($NumOfNeighbors == 6 && $FormalOxidationState == 6) {
 717           $AtomType = "Mo6+6";
 718           $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'octahedral', 6, 6);
 719           last MO;
 720         }
 721 
 722         if ($NumOfNeighbors == 4 && $FormalOxidationState == 6) {
 723           $AtomType = "Mo3+6";
 724           $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 4, 6);
 725           last MO;
 726         }
 727 
 728         # Assign default value...
 729         $AtomType = "Mo6+6";
 730         $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'octahedral', 6, 6);
 731       }
 732       last ATOMSYMBOL;
 733     }
 734 
 735     if ($AtomSymbol =~ /^W$/) {
 736       W: {
 737         if ($NumOfNeighbors == 4 && $FormalOxidationState == 4) {
 738           $AtomType = "W_3+4";
 739           $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 4, 4);
 740           last W;
 741         }
 742 
 743         if ($NumOfNeighbors == 4 && $FormalOxidationState == 6) {
 744           $AtomType = "W_3+6";
 745           $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 4, 6);
 746           last W;
 747         }
 748 
 749         if ($NumOfNeighbors == 6 && $FormalOxidationState == 6) {
 750           $AtomType = "W_6+6";
 751           $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'octahedral', 6, 6);
 752           last W;
 753         }
 754 
 755         # Assign default value...
 756         $AtomType = "W_6+6";
 757         $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'octahedral', 6, 6);
 758       }
 759       last ATOMSYMBOL;
 760     }
 761 
 762     $AtomType = 'None';
 763   }
 764 
 765   return $AtomType;
 766 }
 767 
 768 # Get UFF atom type for atoms in periodic table group number 7...
 769 #
 770 # Group number 7 contains: Mn, Tc, Re, Bh
 771 #
 772 # And corresponding UFF atom types are: Mn6+2, Tc6+5, Re6+5, Re3+7
 773 #
 774 # Notes:
 775 #   . No UFF atom type for Bh
 776 #
 777 sub _GetAtomTypeForOtherAtomsInGroupNumber7 {
 778   my($This, $Atom) = @_;
 779   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 780 
 781   $AtomType = 'None';
 782   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber7';
 783 
 784   $AtomSymbol = $Atom->GetAtomSymbol();
 785   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
 786 
 787   ATOMSYMBOL: {
 788     if ($AtomSymbol =~ /^Mn$/) {
 789       $AtomType = "Mn6+2";
 790       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 2);
 791       last ATOMSYMBOL;
 792     }
 793 
 794     if ($AtomSymbol =~ /^Tc$/) {
 795       $AtomType = "Tc6+5";
 796       $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 6);
 797       last ATOMSYMBOL;
 798     }
 799 
 800     if ($AtomSymbol =~ /^Re$/) {
 801       RE: {
 802         if ($NumOfNeighbors == 6) {
 803           $AtomType = "Re6+5";
 804           $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'octahedral', 6, 6);
 805           last RE;
 806         }
 807 
 808         if ($NumOfNeighbors == 4 && $FormalOxidationState == 7) {
 809           $AtomType = "Re3+7";
 810           $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 4, 7);
 811           last RE;
 812         }
 813 
 814         # Assign default value...
 815         $AtomType = "Re6+5";
 816         $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'octahedral', 6, 6);
 817       }
 818       last ATOMSYMBOL;
 819     }
 820 
 821     $AtomType = 'None';
 822   }
 823 
 824   return $AtomType;
 825 }
 826 
 827 # Get UFF atom type for atoms in periodic table group number 8...
 828 #
 829 # Group number 8 contains: Fe, Ru, Os, Hs
 830 #
 831 # And corresponding UFF atom types are: Fe6+2, Ru6+2, Ru6+3, Os6+6
 832 #
 833 # Notes:
 834 #   . No UFF atom type for Hs
 835 #
 836 sub _GetAtomTypeForOtherAtomsInGroupNumber8 {
 837   my($This, $Atom) = @_;
 838   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 839 
 840   $AtomType = 'None';
 841   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber8';
 842 
 843   $AtomSymbol = $Atom->GetAtomSymbol();
 844   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
 845 
 846   ATOMSYMBOL: {
 847     if ($AtomSymbol =~ /^Fe$/) {
 848       $AtomType = "Fe6+2";
 849       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 2);
 850       last ATOMSYMBOL;
 851     }
 852 
 853     if ($AtomSymbol =~ /^Ru$/) {
 854       RU: {
 855         if ($NumOfNeighbors == 6 && $FormalCharge == 2) {
 856           $AtomType = "Ru6+2";
 857           $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 2);
 858           last RU;
 859         }
 860 
 861         if ($NumOfNeighbors == 6 && $FormalCharge == 3) {
 862           $AtomType = "Ru6+3";
 863           $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 3);
 864           last RU;
 865         }
 866 
 867         # Assign default value...
 868         $AtomType = "Ru6+3";
 869         $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 2);
 870       }
 871       last ATOMSYMBOL;
 872     }
 873 
 874     if ($AtomSymbol =~ /^Os$/) {
 875       $AtomType = "Os6+6";
 876       $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 6);
 877       last ATOMSYMBOL;
 878     }
 879 
 880     $AtomType = 'None';
 881   }
 882 
 883   return $AtomType;
 884 }
 885 
 886 # Get UFF atom type for atoms in periodic table group number 9...
 887 #
 888 # Group number 9 contains: Co, Rh, Ir, Mt
 889 #
 890 # And corresponding UFF atom types are: Co6+3, Rh6+3, Ir6+3
 891 #
 892 # Notes:
 893 #   . No UFF atom type for Mt
 894 #
 895 sub _GetAtomTypeForOtherAtomsInGroupNumber9 {
 896   my($This, $Atom) = @_;
 897   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 898 
 899   $AtomType = 'None';
 900   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber9';
 901 
 902   $AtomSymbol = $Atom->GetAtomSymbol();
 903   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
 904 
 905   ATOMSYMBOL: {
 906     if ($AtomSymbol =~ /^(Co|Rh|Ir)$/) {
 907       $AtomType = "${AtomSymbol}6+3";
 908       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 3);
 909       last ATOMSYMBOL;
 910     }
 911 
 912     $AtomType = 'None';
 913   }
 914 
 915   return $AtomType;
 916 }
 917 
 918 # Get UFF atom type for atoms in periodic table group number 10...
 919 #
 920 # Group number 10 contains: Ni, Pd, Pt
 921 #
 922 # And corresponding UFF atom types are: Ni4+2, Pd4+2, Pt4+2
 923 #
 924 sub _GetAtomTypeForOtherAtomsInGroupNumber10 {
 925   my($This, $Atom) = @_;
 926   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 927 
 928   $AtomType = 'None';
 929   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber10';
 930 
 931   $AtomSymbol = $Atom->GetAtomSymbol();
 932   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
 933 
 934   ATOMSYMBOL: {
 935     if ($AtomSymbol =~ /^(Ni|Pd|Pt)$/) {
 936       $AtomType = "${AtomSymbol}4+2";
 937       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'planar', 4, 2);
 938       last ATOMSYMBOL;
 939     }
 940 
 941     $AtomType = 'None';
 942   }
 943 
 944   return $AtomType;
 945 }
 946 
 947 # Get UFF atom type for atoms in periodic table group number 11...
 948 #
 949 # Group number 11 contains: Cu, Ag, Au
 950 #
 951 # And corresponding UFF atom types are: Cu3+1, Ag1+1, Au4+3
 952 #
 953 sub _GetAtomTypeForOtherAtomsInGroupNumber11 {
 954   my($This, $Atom) = @_;
 955   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 956 
 957   $AtomType = 'None';
 958   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber11';
 959 
 960   $AtomSymbol = $Atom->GetAtomSymbol();
 961   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
 962 
 963   ATOMSYMBOL: {
 964     if ($AtomSymbol =~ /^Cu$/) {
 965       $AtomType = "Cu3+1";
 966       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'tetrahedral', 4, 1);
 967       last ATOMSYMBOL;
 968     }
 969 
 970     if ($AtomSymbol =~ /^Ag$/) {
 971       $AtomType = "Ag1+1";
 972       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'linear', 1, 1);
 973       last ATOMSYMBOL;
 974     }
 975 
 976     if ($AtomSymbol =~ /^Au$/) {
 977       $AtomType = "Au4+3";
 978       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'planar', 4, 3);
 979       last ATOMSYMBOL;
 980     }
 981 
 982     $AtomType = 'None';
 983   }
 984 
 985   return $AtomType;
 986 }
 987 
 988 # Get UFF atom type for atoms in periodic table group number 102..
 989 #
 990 # Group number 12 contains: Zn, Cd, Hg
 991 #
 992 # And corresponding UFF atom types are: Zn3+2, Cd3+2, Hg1+2
 993 #
 994 sub _GetAtomTypeForOtherAtomsInGroupNumber12 {
 995   my($This, $Atom) = @_;
 996   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
 997 
 998   $AtomType = 'None';
 999   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber12';
1000 
1001   $AtomSymbol = $Atom->GetAtomSymbol();
1002   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
1003 
1004   ATOMSYMBOL: {
1005     if ($AtomSymbol =~ /^(Zn|Cd)$/) {
1006       $AtomType = "${AtomSymbol}3+2";
1007       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'tetrahedral', 4, 2);
1008       last ATOMSYMBOL;
1009     }
1010 
1011     if ($AtomSymbol =~ /^Hg$/) {
1012       $AtomType = "Hg1+2";
1013       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'linear', 1, 2);
1014       last ATOMSYMBOL;
1015     }
1016 
1017     $AtomType = 'None';
1018   }
1019 
1020   return $AtomType;
1021 }
1022 
1023 # Get UFF atom type for atoms in periodic table group number 13...
1024 #
1025 # Group number 13 contains: B, Al, Ga, In, Tl
1026 #
1027 # And corresponding UFF atom types are: B_3, B_2, Al3, Ga3+3, In3+3, Tl3+3
1028 #
1029 sub _GetAtomTypeForOtherAtomsInGroupNumber13 {
1030   my($This, $Atom) = @_;
1031   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
1032 
1033   $AtomType = 'None';
1034   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber13';
1035 
1036   $AtomSymbol = $Atom->GetAtomSymbol();
1037   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
1038 
1039   ATOMSYMBOL: {
1040     if ($AtomSymbol =~ /^B$/) {
1041       B: {
1042         if ($NumOfNeighbors == 4) {
1043           $AtomType = "B_3";
1044           $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'tetrahedral', 4, 0);
1045           last B;
1046         }
1047 
1048         if ($NumOfNeighbors == 3) {
1049           $AtomType = "B_2";
1050           $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'trigonal', 3, 0);
1051           last B;
1052         }
1053 
1054         # Assign default value...
1055         $AtomType = "B_2";
1056         $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'trigonal', 3, 0);
1057       }
1058 
1059       last ATOMSYMBOL;
1060     }
1061 
1062     if ($AtomSymbol =~ /^Al$/) {
1063       $AtomType = "Al3";
1064       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'tetrahedral', 4, 0);
1065       last ATOMSYMBOL;
1066     }
1067 
1068     if ($AtomSymbol =~ /^(Ga|In|Tl)$/) {
1069       $AtomType = "${AtomSymbol}3+3";
1070       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'tetrahedral', 4, 3);
1071       last ATOMSYMBOL;
1072     }
1073 
1074     $AtomType = 'None';
1075   }
1076 
1077   return $AtomType;
1078 }
1079 
1080 # Get UFF atom type for atoms in periodic table group number 14...
1081 #
1082 # Group number 14 contains: C, Si, Ge, Sn, Pb
1083 #
1084 # And corresponding UFF atom types are: Si3, Ge3, Sn3, Pb3
1085 #
1086 # Notes:
1087 #   . This method doesn't assign UFF atom type for C.
1088 #
1089 sub _GetAtomTypeForOtherAtomsInGroupNumber14 {
1090   my($This, $Atom) = @_;
1091   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
1092 
1093   $AtomType = 'None';
1094   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber14';
1095 
1096   $AtomSymbol = $Atom->GetAtomSymbol();
1097   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
1098 
1099   ATOMSYMBOL: {
1100     if ($AtomSymbol =~ /^(Si|Ge|Sn|Pb)$/) {
1101       $AtomType = "${AtomSymbol}3";
1102       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'tetrahedral', 4, 0);
1103       last ATOMSYMBOL;
1104     }
1105 
1106     $AtomType = 'None';
1107   }
1108 
1109   return $AtomType;
1110 }
1111 
1112 # Get UFF atom type for atoms in periodic table group number 15...
1113 #
1114 # Group number 15 contains: N, P, As, Sb, Bi
1115 #
1116 # And corresponding UFF atom types are: As3+3, Sb3+3, Bi3+3
1117 #
1118 # Notes:
1119 #   . This method doesn't assign UFF atom type for N and P.
1120 #
1121 sub _GetAtomTypeForOtherAtomsInGroupNumber15 {
1122   my($This, $Atom) = @_;
1123   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
1124 
1125   $AtomType = 'None';
1126   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber15';
1127 
1128   $AtomSymbol = $Atom->GetAtomSymbol();
1129   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
1130 
1131   ATOMSYMBOL: {
1132     if ($AtomSymbol =~ /^(As|Sb|Bi)$/) {
1133       $AtomType = "${AtomSymbol}3+3";
1134       $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 3, 3);
1135       last ATOMSYMBOL;
1136     }
1137 
1138     $AtomType = 'None';
1139   }
1140 
1141   return $AtomType;
1142 }
1143 
1144 # Get UFF atom type for atoms in periodic table group number 16...
1145 #
1146 # Group number 16 contains: O, S, Se, Te, Po
1147 #
1148 # And corresponding UFF atom types are: Se3+2, Te3+2, Po3+2
1149 #
1150 # Notes:
1151 #   . This method doesn't assign UFF atom type for O and S.
1152 #
1153 sub _GetAtomTypeForOtherAtomsInGroupNumber16 {
1154   my($This, $Atom) = @_;
1155   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
1156 
1157   $AtomType = 'None';
1158   $MethodName = '_GetAtomTypeForOtherAtomsInGroupNumber16';
1159 
1160   $AtomSymbol = $Atom->GetAtomSymbol();
1161   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
1162 
1163   ATOMSYMBOL: {
1164     if ($AtomSymbol =~ /^(Se|Te|Po)$/) {
1165       $AtomType = "${AtomSymbol}3+2";
1166       $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'tetrahedral', 2, 2);
1167       last ATOMSYMBOL;
1168     }
1169 
1170     $AtomType = 'None';
1171   }
1172 
1173   return $AtomType;
1174 }
1175 
1176 # Get UFF atom type for atoms in periodic table group number 17...
1177 #
1178 # Group number 17 contains: F, Cl, Br, I, At
1179 #
1180 # And corresponding UFF atom types are: F_, Cl, Br, I_, At
1181 #
1182 sub _GetAtomTypeForOtherAtomsInGroupNumber17 {
1183   my($This, $Atom) = @_;
1184   my($AtomType, $AtomSymbol);
1185 
1186   $AtomSymbol = $Atom->GetAtomSymbol();
1187   $AtomType = (length($AtomSymbol) == 1) ? "${AtomSymbol}_" : $AtomSymbol;
1188 
1189   return $AtomType;
1190 }
1191 
1192 # Get UFF atom type for atoms in periodic table group number 18...
1193 #
1194 # Group number 18 contains: He, Ne, Ar, Kr, Xe, Rn
1195 #
1196 # And corresponding UFF atom types are: He4+4, Ne4+4, Ar4+4, Kr4+4, Xe4+4, Rn4+4
1197 #
1198 sub _GetAtomTypeForOtherAtomsInGroupNumber18 {
1199   my($This, $Atom) = @_;
1200   my($AtomSymbol, $AtomType);
1201 
1202   $AtomType = 'None';
1203 
1204   $AtomSymbol = $Atom->GetAtomSymbol();
1205   $AtomType = "${AtomSymbol}4+4";
1206 
1207   return $AtomType;
1208 }
1209 
1210 # Get UFF atom type for atoms in periodic table group name Lanthanoid...
1211 #
1212 # Group name Lanthanoid contains: La, Ce, Pr, Nd, Pm, Sm, Eu, Gd, Tb, Dy, Ho, Er, Tm, Yb, Lu
1213 #
1214 # And corresponding UFF atom types are: La3+3, Ce6+3, Pr6+3, Nd6+3, Pm6+3, Sm6+3,
1215 # Eu6+3, Gd6+3, Tb6+3, Dy6+3, Ho6+3, Er6+3, Tm6+3, Yb6+3, Lu6+3
1216 #
1217 sub _GetAtomTypeForOtherAtomsInLanthanoidGroup {
1218   my($This, $Atom) = @_;
1219   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
1220 
1221   $AtomType = 'None';
1222   $MethodName = '_GetAtomTypeForOtherAtomsInLanthanoidGroup';
1223 
1224   $AtomSymbol = $Atom->GetAtomSymbol();
1225   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
1226 
1227   ATOMSYMBOL: {
1228     if ($AtomSymbol =~ /^La$/) {
1229       $AtomType = "La3+3";
1230       $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'tetrahedral', 4, 3);
1231       last ATOMSYMBOL;
1232     }
1233 
1234     $AtomType = "${AtomSymbol}6+3";
1235     $This->_CheckGeometryAndFormalChargeMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalCharge, 'octahedral', 6, 3);
1236   }
1237 
1238   return $AtomType;
1239 
1240 }
1241 
1242 # Get UFF atom type for atoms in periodic table group name Actinoid...
1243 #
1244 # Group name Actinoid contains: Ac, Th, Pa, U, Np, Pu, Am, Cm, Bk, Cf, Es, Fm, Md, No, Lr
1245 #
1246 # And corresponding UFF atom types are: Ac6+3, Th6+4, Pa6+4, U_6+4, Np6+4, Pu6+4,
1247 # Am6+4, Cm6+3, Bk6+3, Cf6+3, Es6+3, Fm6+3, Md6+3, No6+3, Lr6+3
1248 #
1249 sub _GetAtomTypeForOtherAtomsInActinoidGroup {
1250   my($This, $Atom) = @_;
1251   my($AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, $FormalCharge, $MethodName);
1252 
1253   $AtomType = 'None';
1254   $MethodName = '_GetAtomTypeForOtherAtomsInActinoidGroup';
1255 
1256   $AtomSymbol = $Atom->GetAtomSymbol();
1257   ($NumOfNeighbors, $FormalOxidationState, $FormalCharge) = $This->_GetAtomEnvironmentInfoForUFFAtomTypes($Atom);
1258 
1259   ATOMSYMBOL: {
1260     if ($AtomSymbol =~ /^(Ac|Cm|Bk|Cf|Es|Fm|Md|No|Lr)$/) {
1261       $AtomType = "${AtomSymbol}6+3";
1262       $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'octahedral', 6, 3);
1263       last ATOMSYMBOL;
1264     }
1265 
1266     if ($AtomSymbol =~ /^(Th|Pa|U|Np|Pu|Am)$/) {
1267       $AtomType = length($AtomSymbol) == 1 ? "${AtomSymbol}_6+4" : "${AtomSymbol}6+4";
1268       $This->_CheckGeometryAndOxidationStateMismatch($MethodName, $AtomSymbol, $AtomType, $NumOfNeighbors, $FormalOxidationState, 'octahedral', 6, 4);
1269       last ATOMSYMBOL;
1270     }
1271 
1272     $AtomType = 'None';
1273   }
1274 
1275   return $AtomType;
1276 }
1277 
1278 
1279 # Is it a four-coordinated Phosphorus for describing organometallic coordinated phosphines?
1280 #
1281 sub _IsFourCoordinatedOrganometallicPhosphorus {
1282   my($This, $Atom) = @_;
1283   my($AtomNeighbor, $NumOfNeighbors, $MetalNeighborFound, @AtomNeighbors);
1284 
1285   @AtomNeighbors = $Atom->GetHeavyAtomNeighbors();
1286 
1287   # Is it attached to a metallic atom?
1288   $MetalNeighborFound = 0;
1289   NEIGHBOR: for $AtomNeighbor (@AtomNeighbors) {
1290     if ($AtomNeighbor->IsMetallic()) {
1291       $MetalNeighborFound = 1;
1292       last NEIGHBOR;
1293     }
1294   }
1295 
1296   if (!$MetalNeighborFound) {
1297     return 0;
1298   }
1299 
1300   # Is it four coordinated Phosphorus?
1301   $NumOfNeighbors = scalar @AtomNeighbors;
1302   if ($NumOfNeighbors <= 4) {
1303     # As long as total number of heavy atom neighbors, including attached
1304     # metal atom is <= 4, missing hydrogens would make it a tetra coordinated
1305     # Phosphorous...
1306     return 1;
1307   }
1308 
1309   return 0;
1310 }
1311 
1312 # Get UFF atom environment information, number of neighbors and formal oxidatiion state,
1313 #  for assigning UFF atom types...
1314 #
1315 sub _GetAtomEnvironmentInfoForUFFAtomTypes {
1316   my($This, $Atom) = @_;
1317   my($NumOfNeighbors, $NumOfHydrogens, $FormalOxidationState, $FormalCharge);
1318 
1319   $NumOfHydrogens = $Atom->GetNumOfMissingHydrogens() + $Atom->GetExplicitHydrogens();
1320 
1321   # Total number of neighbor atoms...
1322   $NumOfNeighbors = $Atom->GetNumOfNonHydrogenAtomNeighbors() + $NumOfHydrogens;
1323 
1324   # UFF formal oxidation state appears to just the sum of bond orders to all attched non-hyrdogen
1325   # atoms and all hydrogen atoms...
1326   #
1327   $FormalOxidationState = $Atom->GetSumOfBondOrdersToNonHydrogenAtoms() + $NumOfHydrogens;
1328 
1329   # Any explicit formal charge...
1330   $FormalCharge = $Atom->GetFormalCharge();
1331 
1332   return ($NumOfNeighbors, $FormalOxidationState, $FormalCharge);
1333 }
1334 
1335 # Check and warn about any geometry and/or formal charge mismatch...
1336 #
1337 sub _CheckGeometryAndFormalChargeMismatch {
1338   my($This, $CallingMethod, $AtomSymbol, $AssignedAtomType, $NumOfNeighbors, $FormalCharge, $ExpectedGeometryType, $ExpectedNumOfNeighbors, $ExpectedFormalCharge) = @_;
1339   my($MsgHeader);
1340 
1341   $MsgHeader = "Warning: ${ClassName}->${CallingMethod}:_CheckGeometryAndFormalChargeMismatch";
1342 
1343   if ($NumOfNeighbors !=$ExpectedNumOfNeighbors  && $FormalCharge != $ExpectedFormalCharge) {
1344     carp "\n${MsgHeader}: UFF atom type for $AtomSymbol corresponding to $ExpectedGeometryType geometry and formal charge $ExpectedFormalCharge cann't be assigned; Number of neighbors, $NumOfNeighbors, is different from $ExpectedNumOfNeighbors and formal charge, $FormalCharge, is different from $ExpectedFormalCharge. Default UFF atom type, $AssignedAtomType, has been assigned...";
1345   }
1346   elsif ($NumOfNeighbors !=$ExpectedNumOfNeighbors) {
1347     carp "\n${MsgHeader}: UFF atom type for $AtomSymbol corresponding to $ExpectedGeometryType geometry and formal charge $ExpectedFormalCharge cann't be assigned; Number of neighbors, $NumOfNeighbors, is different from $ExpectedNumOfNeighbors. Default UFF atom type, $AssignedAtomType, has been assigned...";
1348   }
1349   elsif ($FormalCharge != $ExpectedFormalCharge) {
1350     carp "\n${MsgHeader}: UFF atom type for $AtomSymbol corresponding to $ExpectedGeometryType geometry and formal charge $ExpectedFormalCharge cann't be assigned; Formal charge, $FormalCharge, is different from $ExpectedFormalCharge. Default UFF atom type, $AssignedAtomType, has been assigned...";
1351   }
1352 }
1353 
1354 # Check and warn about any geometry and/or formal oxidation state mismatch...
1355 #
1356 sub _CheckGeometryAndOxidationStateMismatch {
1357   my($This, $CallingMethod, $AtomSymbol, $AssignedAtomType, $NumOfNeighbors, $FormalOxidationState, $ExpectedGeometryType, $ExpectedNumOfNeighbors, $ExpectedFormalOxidationState) = @_;
1358   my($MsgHeader);
1359 
1360   $MsgHeader = "Warning: ${ClassName}->${CallingMethod}:_CheckGeometryAndOxidationStateMismatch";
1361 
1362   if ($NumOfNeighbors !=$ExpectedNumOfNeighbors  && $FormalOxidationState != $ExpectedFormalOxidationState) {
1363     carp "\n${MsgHeader}: UFF atom type for $AtomSymbol corresponding to $ExpectedGeometryType geometry and formal oxidation state $ExpectedFormalOxidationState cann't be assigned; Number of neighbors, $NumOfNeighbors, is different from $ExpectedNumOfNeighbors and formal oxidation state, $FormalOxidationState, is different from $ExpectedFormalOxidationState. Default UFF atom type, $AssignedAtomType, has been assigned...";
1364   }
1365   elsif ($NumOfNeighbors !=$ExpectedNumOfNeighbors) {
1366     carp "\n${MsgHeader}: UFF atom type for $AtomSymbol corresponding to $ExpectedGeometryType geometry and formal oxidation state $ExpectedFormalOxidationState cann't be assigned; Number of neighbors, $NumOfNeighbors, is different from $ExpectedNumOfNeighbors. Default UFF atom type, $AssignedAtomType, has been assigned...";
1367   }
1368   elsif ($FormalOxidationState != $ExpectedFormalOxidationState) {
1369     carp "\n${MsgHeader}: UFF atom type for $AtomSymbol corresponding to $ExpectedGeometryType geometry and formal oxidation state $ExpectedFormalOxidationState cann't be assigned; Formal oxidation state, $FormalOxidationState, is different from $ExpectedFormalOxidationState. Default UFF atom type, $AssignedAtomType, has been assigned...";
1370   }
1371 }
1372 
1373 
1374 # Return a string containg data for UFFAtomTypes object...
1375 #
1376 sub StringifyUFFAtomTypes {
1377   my($This) = @_;
1378   my($AtomTypesString);
1379 
1380   # Type of AtomTypes...
1381   $AtomTypesString = "AtomTypes: $This->{Type}; IgnoreHydrogens: " . ($This->{IgnoreHydrogens} ? "Yes" : "No");
1382 
1383   # Setup atom types information...
1384   my($AtomID, $AtomType, @AtomTypesInfo, %AssignedAtomTypes);
1385 
1386   @AtomTypesInfo = ();
1387   %AssignedAtomTypes = $This->GetAtomTypes();
1388 
1389   for $AtomID (sort { $a <=> $b } keys %AssignedAtomTypes) {
1390     $AtomType = $AssignedAtomTypes{$AtomID} ? $AssignedAtomTypes{$AtomID} : 'None';
1391     push @AtomTypesInfo, "$AtomID:$AtomType";
1392   }
1393   $AtomTypesString .= "; AtomIDs:AtomTypes: <" . TextUtil::JoinWords(\@AtomTypesInfo, ", ", 0) . ">";
1394 
1395   return $AtomTypesString;
1396 }
1397 
1398 # Is it a UFFAtomTypes object?
1399 sub _IsUFFAtomTypes {
1400   my($Object) = @_;
1401 
1402   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
1403 }
1404 
1405 # Check and load UFF atom types data...
1406 #
1407 sub _CheckAndLoadUFFAtomTypesData {
1408 
1409   # Is it already loaded?
1410   if (exists $UFFAtomTypesDataMap{AtomTypes}) {
1411     return;
1412   }
1413 
1414   _LoadUFFAtomTypesData();
1415 }
1416 
1417 # Load UFF atom types data from the file assuming first column to be atom type symbol..
1418 #
1419 # Format:
1420 #
1421 # "AtomType","Mass","ValenceBondRadius","ValenceAngle","NonBondRadius","NonBondEnergy","NonBondScale","EffectiveCharge","SP3TorsionalBarrier"
1422 # "H_","1.0080","0.354","180.000","2.886","0.044","12.000","0.733","0.000"
1423 # "C_3","12.0110","0.757","109.471","3.851","0.105","12.730","1.967","2.119"
1424 # "C_R","12.0110","0.729","120.000","3.851","0.105","12.730","1.967","0.000"
1425 # "C_2","12.0110","0.732","120.000","3.851","0.105","12.730","1.967","0.000"
1426 # "C_1","12.0110","0.711","180.000","3.851","0.105","12.730","1.967","0.000"
1427 #
1428 sub _LoadUFFAtomTypesData {
1429   my($AtomTypesDataFile, $MayaChemToolsLibDir);
1430 
1431   $MayaChemToolsLibDir = FileUtil::GetMayaChemToolsLibDirName();
1432 
1433   $AtomTypesDataFile =  "$MayaChemToolsLibDir" . "/data/UFFAtomTypes.csv";
1434   if (! -e "$AtomTypesDataFile") {
1435     croak "Error: MayaChemTools package file, $AtomTypesDataFile, is missing: Possible installation problems...";
1436   }
1437 
1438   %UFFAtomTypesDataMap = ();
1439   AtomTypes::AtomTypes::LoadAtomTypesData($AtomTypesDataFile, \%UFFAtomTypesDataMap);
1440 }
1441