       1 package AtomTypes::UFFAtomTypes;
       2 #
       3 # File: UFFAtomTypes.pm
       4 # Author: Manish Sud <msud@san.rr.com>
       5 #
       6 # Copyright (C) 2018 Manish Sud. All rights reserved.
       7 #
       8 # This file is part of MayaChemTools.
       9 #
      10 # MayaChemTools is free software; you can redistribute it and/or modify it under
      11 # the terms of the GNU Lesser General Public License as published by the Free
      12 # Software Foundation; either version 3 of the License, or (at your option) any
      13 # later version.
      14 #
      15 # MayaChemTools is distributed in the hope that it will be useful, but without
      16 # any warranty; without even the implied warranty of merchantability of fitness
      17 # for a particular purpose.  See the GNU Lesser General Public License for more
      18 # details.
      19 #
      20 # You should have received a copy of the GNU Lesser General Public License
      21 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
      22 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
      23 # Boston, MA, 02111-1307, USA.
      24 #
      25
      26 use strict;
      27 use Carp;
      28 use Exporter;
      29 use Scalar::Util ();
      30 use 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