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