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