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