       1 package MolecularDescriptors::MolecularVolumeDescriptors;
       2 #
       3 # $RCSfile: MolecularVolumeDescriptors.pm,v $
       4 # $Date: 2015/02/28 20:49:20 $
       5 # $Revision: 1.16 $
       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 TextUtil ();
      34 use MathUtil ();
      35 use Atom;
      36 use Molecule;
      37 use AtomTypes::AtomTypes;
      38 use MolecularDescriptors::MolecularDescriptors;
      39
      40 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      41
      42 @ISA = qw(MolecularDescriptors::MolecularDescriptors Exporter);
      43 @EXPORT = qw();
      44 @EXPORT_OK = qw(GetDescriptorNames GetVDWAtomRadiiAndVolumesData);
      45
      46 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
      47
      48 # Setup class variables...
      49 my($ClassName, @DescriptorNames, %VDWAtomRadiiAndVolumesDataMap);
      50 _InitializeClass();
      51
      52 # Overload Perl functions...
      53 use overload '""' => 'StringifyMolecularVolumeDescriptors';
      54
      55 # Class constructor...
      56 sub new {
      57   my($Class, %NamesAndValues) = @_;
      58
      59   # Initialize object...
      60   my $This = $Class->SUPER::new();
      61   bless $This, ref($Class) || $Class;
      62   $This->_InitializeMolecularVolumeDescriptors();
      63
      64   $This->_InitializeMolecularVolumeDescriptorsProperties(%NamesAndValues);
      65
      66   return $This;
      67 }
      68
      69 # Initialize class ...
      70 sub _InitializeClass {
      71   #Class name...
      72   $ClassName = __PACKAGE__;
      73
      74   # Descriptor names...
      75   @DescriptorNames = ('MolecularVolume');
      76
      77   # Initialize the data hash. It'll be loaded on demand later...
      78   %VDWAtomRadiiAndVolumesDataMap = ();
      79
      80 }
      81
      82 # Get descriptor names as an array.
      83 #
      84 # This functionality can be either invoked as a class function or an
      85 # object method.
      86 #
      87 sub GetDescriptorNames {
      88   return @DescriptorNames;
      89 }
      90
      91 # Initialize object data...
      92 #
      93 sub _InitializeMolecularVolumeDescriptors {
      94   my($This) = @_;
      95
      96   # Type of MolecularDescriptor...
      97   $This->{Type} = 'MolecularVolume';
      98
      99   # Intialize descriptor names and values...
     100   $This->_InitializeDescriptorNamesAndValues(@DescriptorNames);
     101
     102   return $This;
     103 }
     104
     105 # Initialize object properties...
     106 #
     107 sub _InitializeMolecularVolumeDescriptorsProperties {
     108   my($This, %NamesAndValues) = @_;
     109
     110   my($Name, $Value, $MethodName);
     111   while (($Name, $Value) = each  %NamesAndValues) {
     112     $MethodName = "Set${Name}";
     113     $This->$MethodName($Value);
     114   }
     115
     116   return $This;
     117 }
     118
     119 # Get VDW atom data loaded from VDW atom radii and and volumes data file as
     120 # a reference to hash with the following hash data format:
     121 #
     122 # @{$VDWAtomRadiiAndVolumesDataMap{AtomTypes}} - Array of all possible atom type symbols for all atoms
     123 # @{$VDWAtomRadiiAndVolumesDataMap->{ColLabels}} - Array of column labels
     124 # %{$VDWAtomRadiiAndVolumesDataMap->{DataCol<Num>}} - Hash keys pair: <DataCol<Num>, AtomType>
     125 #
     126 # This functionality can be either invoked as a class function or an
     127 # object method.
     128 #
     129 sub GetVDWAtomRadiiAndVolumesData {
     130
     131   # Make sure data is loaded...
     132   _CheckAndLoadVDWAtomRadiiAndVolumesData();
     133
     134   return \%VDWAtomRadiiAndVolumesDataMap;
     135 }
     136
     137 # Calculate van der Waals molecular volume [ Ref 93 ] of a molecule using
     138 # atomic and bonds contributions...
     139 #
     140 # van der Waals molecular volume (A**3/molecule) is defined as:
     141 #
     142 # vdwMolecularVolume = SumOfAtomicVDWVolumeContributions - 5.92 * NumOfBonds
     143 #                      - 14.7 * NumOfAromaticRings - 3.8 * NumOfNonAromaticRings
     144 #
     145 # Methodology:
     146 #   . Add up van der Waals atom volumne of all atoms
     147 #   . Calculate molecular volume by correcting sum of atom volumes for num of
     148 #     bonds and rings
     149 #
     150 # Caveats:
     151 #   . All hydrogens must be added to molecule before calling GenerateDescriptors.
     152 #
     153 sub GenerateDescriptors {
     154   my($This) = @_;
     155
     156   # Initialize descriptor values...
     157   $This->_InitializeDescriptorValues();
     158
     159   # Check availability of molecule...
     160   if (!$This->{Molecule}) {
     161     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Molecule data is not available: Molecule object hasn't been set...";
     162     return undef;
     163   }
     164
     165   # Calculate descriptor values...
     166   if (!$This->_CalculateDescriptorValues()) {
     167     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Couldn't calculate MolecularVolume values: van der Waals atom volume data is not available for all atoms...";
     168     return undef;
     169   }
     170
     171   # Set final descriptor values...
     172   $This->_SetFinalDescriptorValues();
     173
     174   return $This;
     175 }
     176
     177 # Calculate MolecularVolume value...
     178 #
     179 sub _CalculateDescriptorValues {
     180   my($This) = @_;
     181   my($Atom, $AtomID, $AtomSymbol, $SumOfVDWAtomVolumes, $Molecule, $MolecularVolume, $NumOfBonds, $NumOfAromaticRings, $NumOfNonAromaticRings, $VDWAtomRadiiAndVolumesDataMapRef);
     182
     183   $MolecularVolume = 0;
     184
     185   $VDWAtomRadiiAndVolumesDataMapRef = $This->GetVDWAtomRadiiAndVolumesData();
     186   $Molecule = $This->{Molecule};
     187
     188   # Calculate atom volumes contribution to molecular volume...
     189   #
     190   $SumOfVDWAtomVolumes = 0;
     191
     192   ATOM: for $Atom ($Molecule->GetAtoms()) {
     193     $AtomID = $Atom->GetID();
     194     $AtomSymbol = $Atom->GetAtomSymbol();
     195
     196     # Make sure van der Waals atom volume is available...
     197     if (!exists $VDWAtomRadiiAndVolumesDataMap{DataCol3}{$AtomSymbol}) {
     198       return undef;
     199     }
     200     $SumOfVDWAtomVolumes += $VDWAtomRadiiAndVolumesDataMapRef->{DataCol3}{$AtomSymbol};
     201   }
     202
     203   $NumOfBonds = $Molecule->GetNumOfBonds();
     204   $NumOfAromaticRings = $Molecule->GetNumOfAromaticRings();
     205   $NumOfNonAromaticRings = $Molecule->GetNumOfRings() - $NumOfAromaticRings;
     206
     207   # Apply correction for bonds and rings...
     208   $MolecularVolume = $SumOfVDWAtomVolumes - 5.92 * $NumOfBonds - 14.7 * $NumOfAromaticRings - 3.8 * $NumOfNonAromaticRings;
     209
     210   # Track the calculated values...
     211   $This->{MolecularVolume} = MathUtil::round($MolecularVolume, 2);
     212
     213   return $This;
     214 }
     215
     216 # Setup final descriptor values...
     217 #
     218 sub _SetFinalDescriptorValues {
     219   my($This) = @_;
     220
     221   $This->{DescriptorsGenerated} = 1;
     222
     223   $This->SetDescriptorValues($This->{MolecularVolume});
     224
     225   return $This;
     226 }
     227
     228 # Return a string containg data for MolecularVolumeDescriptors object...
     229 #
     230 sub StringifyMolecularVolumeDescriptors {
     231   my($This) = @_;
     232   my($MolecularVolumeDescriptorsString);
     233
     234   $MolecularVolumeDescriptorsString = "MolecularDescriptorType: $This->{Type}; " . $This->_StringifyDescriptorNamesAndValues();
     235
     236   return $MolecularVolumeDescriptorsString;
     237 }
     238
     239 # Is it a MolecularVolumeDescriptors object?
     240 sub _IsMolecularVolumeDescriptors {
     241   my($Object) = @_;
     242
     243   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
     244 }
     245
     246 # Check and load van der Waals atom radii and volumes data...
     247 #
     248 sub _CheckAndLoadVDWAtomRadiiAndVolumesData {
     249
     250   # Is it already loaded?
     251   if (exists $VDWAtomRadiiAndVolumesDataMap{AtomTypes}) {
     252     return;
     253   }
     254
     255   _LoadVDWAtomRadiiAndVolumesData();
     256 }
     257
     258 # Initialize van der Waals atom radii and volumes data from the file...
     259 #
     260 # Format:
     261 #
     262 # "AtomTypeSymbol","VDWAtomRadius(A)","VDWAtomVolume(A**3)/molecule"
     263 # "H","1.20","7.24"
     264 # "He","1.40","11.49"
     265 #
     266 sub  _LoadVDWAtomRadiiAndVolumesData {
     267   my($VDWAtomDataFile, $MayaChemToolsLibDir);
     268
     269   $MayaChemToolsLibDir = FileUtil::GetMayaChemToolsLibDirName();
     270
     271   $VDWAtomDataFile =  "$MayaChemToolsLibDir" . "/data/VDWAtomRadiiAndVolumes.csv";
     272   if (! -e "$VDWAtomDataFile") {
     273     croak "Error: MayaChemTools package file, $VDWAtomDataFile, is missing: Possible installation problems...";
     274   }
     275
     276   %VDWAtomRadiiAndVolumesDataMap = ();
     277   AtomTypes::AtomTypes::LoadAtomTypesData($VDWAtomDataFile, \%VDWAtomRadiiAndVolumesDataMap);
     278 };
     279