MayaChemTools

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