MayaChemTools

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