1 package MolecularDescriptors::TPSADescriptors; 2 # 3 # File: TPSADescriptors.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 MolecularDescriptors::MolecularDescriptors; 35 use AtomTypes::TPSAAtomTypes; 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); 42 43 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]); 44 45 # Setup class variables... 46 my($ClassName, @DescriptorNames); 47 _InitializeClass(); 48 49 # Overload Perl functions... 50 use overload '""' => 'StringifyTPSADescriptors'; 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->_InitializeTPSADescriptors(); 60 61 $This->_InitializeTPSADescriptorsProperties(%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 = ('TPSA'); 73 74 } 75 76 # Get descriptor names as an array. 77 # 78 # This functionality can be either invoked as a class function or an 79 # object method. 80 # 81 sub GetDescriptorNames { 82 return @DescriptorNames; 83 } 84 85 # Initialize object data... 86 # 87 sub _InitializeTPSADescriptors { 88 my($This) = @_; 89 90 # Type of MolecularDescriptor... 91 $This->{Type} = 'TPSA'; 92 93 # By default, TPSA atom contributions from Phosphorus and Sulfur atoms 94 # are not included during TPSA calculations. [ Ref 91 ] 95 # 96 $This->{IgnorePhosphorus} = 1; 97 $This->{IgnoreSulfur} = 1; 98 99 # TPSA atom types assigned to appropriate atoms... 100 %{$This->{AtomTypes}} = (); 101 102 # Intialize descriptor names and values... 103 $This->_InitializeDescriptorNamesAndValues(@DescriptorNames); 104 105 return $This; 106 } 107 108 # Initialize object properties... 109 # 110 sub _InitializeTPSADescriptorsProperties { 111 my($This, %NamesAndValues) = @_; 112 113 my($Name, $Value, $MethodName); 114 while (($Name, $Value) = each %NamesAndValues) { 115 $MethodName = "Set${Name}"; 116 $This->$MethodName($Value); 117 } 118 119 return $This; 120 } 121 122 # Calculate Topological Polar Surface Area (TPSA) value [ Ref 90-91 ] for molecule... 123 # 124 # Methodology: 125 # . Assign TPSA atom types [ Ref 90-91 ] to Nitrogen and Oxygen 126 # atoms with optional assignment to Phosphorus and Sulfur atoms. 127 # . Calculate TPSA value adding contribution of appropriate atom types. 128 # 129 sub GenerateDescriptors { 130 my($This) = @_; 131 132 # Initialize descriptor values... 133 $This->_InitializeDescriptorValues(); 134 135 # Check availability of molecule... 136 if (!$This->{Molecule}) { 137 carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Molecule data is not available: Molecule object hasn't been set..."; 138 return undef; 139 } 140 141 # Cache appropriate molecule data... 142 $This->_SetupMoleculeDataCache(); 143 144 # Assign TPSA atom types... 145 if (!$This->_AssignAtomTypes()) { 146 carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Couldn't assign valid TPSA atom types to appropriate atoms..."; 147 return undef; 148 } 149 150 # Calculate descriptor values... 151 if (!$This->_CalculateDescriptorValues()) { 152 carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Couldn't calculate TPSA values corresponding to assigned TPSA atom types..."; 153 return undef; 154 } 155 156 # Set final descriptor values... 157 $This->_SetFinalDescriptorValues(); 158 159 # Clear cached molecule data... 160 $This->_ClearMoleculeDataCache(); 161 162 return $This; 163 } 164 165 # Assign TPSA atom types.. 166 # 167 sub _AssignAtomTypes { 168 my($This) = @_; 169 my($TPSAAtomTypes, $Atom, $AtomID); 170 171 %{$This->{AtomTypes}} = (); 172 173 # Assign atom types... 174 $TPSAAtomTypes = new AtomTypes::TPSAAtomTypes('Molecule' => $This->{Molecule}, 'IgnorePhosphorus' => $This->{IgnorePhosphorus}, 'IgnoreSulfur' => $This->{IgnoreSulfur}); 175 $TPSAAtomTypes->AssignAtomTypes(); 176 177 # Make sure TPSA atom types assignment is successful... 178 if (!$TPSAAtomTypes->IsAtomTypesAssignmentSuccessful()) { 179 return undef; 180 } 181 182 # Collect assigned atom types... 183 for $Atom (@{$This->{Atoms}}) { 184 $AtomID = $Atom->GetID(); 185 $This->{AtomTypes}{$AtomID} = $TPSAAtomTypes->GetAtomType($Atom); 186 } 187 188 return $This; 189 } 190 191 # Calculate TPSA value... 192 # 193 sub _CalculateDescriptorValues { 194 my($This) = @_; 195 my($Atom, $AtomID, $TPSA, $TPSAContribution, $TPSADataRef, $AtomType); 196 197 $TPSA = 0; 198 199 # Get TPSA atom types data... 200 $TPSADataRef = AtomTypes::TPSAAtomTypes::GetTPSAAtomTypesData(); 201 202 ATOM: for $Atom (@{$This->{Atoms}}) { 203 $AtomID = $Atom->GetID(); 204 $AtomType = $This->{AtomTypes}{$AtomID}; 205 206 # Ignore inappropriate atoms... 207 if ($AtomType =~ /^None$/i) { 208 next ATOM; 209 } 210 211 $TPSAContribution = 0.0; 212 213 if ($AtomType =~ /^(N|O)$/i) { 214 # TPSA contributions for Nitrogen and Oxygen atoms not explicity defined using atom 215 # environments in Table 1 [ Ref 90 ] 216 if ($AtomType =~ /^N$/i) { 217 # N = 30.5 - X*8.2 + H*1.5 or 0.0 for negative value 218 $TPSAContribution = 30.5 - $Atom->GetAtomicInvariantValue('X') * 8.2 + $Atom->GetAtomicInvariantValue('H') * 1.5; 219 } 220 elsif ($AtomType =~ /^O$/i) { 221 # O = 28.5 - X*8.6 + H*1.5 or 0.0 for negative value 222 $TPSAContribution = 28.5 - $Atom->GetAtomicInvariantValue('X') * 8.6 + $Atom->GetAtomicInvariantValue('H') * 1.5; 223 } 224 if ($TPSAContribution < 0.0) { 225 $TPSAContribution = 0.0; 226 } 227 } 228 elsif (exists $TPSADataRef->{DataCol3}{$AtomType}) { 229 # Data for TPSA contribution is in column number 3... 230 $TPSAContribution = $TPSADataRef->{DataCol3}{$AtomType}; 231 } 232 else { 233 # No TPSA data for assigned atom type... 234 return undef; 235 } 236 $TPSA += $TPSAContribution; 237 } 238 239 # Track the calculated values... 240 $This->{TPSA} = MathUtil::round($TPSA, 2); 241 242 return $This; 243 } 244 245 # Setup final descriptor values... 246 # 247 sub _SetFinalDescriptorValues { 248 my($This) = @_; 249 250 $This->{DescriptorsGenerated} = 1; 251 252 $This->SetDescriptorValues($This->{TPSA}); 253 254 return $This; 255 } 256 257 # Cache appropriate molecule data... 258 # 259 sub _SetupMoleculeDataCache { 260 my($This) = @_; 261 262 @{$This->{Atoms}} = $This->GetMolecule()->GetAtoms(); 263 264 return $This; 265 } 266 267 # Clear cached molecule data... 268 # 269 sub _ClearMoleculeDataCache { 270 my($This) = @_; 271 272 @{$This->{Atoms}} = (); 273 274 return $This; 275 } 276 277 # Return a string containg data for TPSADescriptors object... 278 # 279 sub StringifyTPSADescriptors { 280 my($This) = @_; 281 my($TPSADescriptorsString); 282 283 # Type of MolecularDescriptors... 284 $TPSADescriptorsString = "MolecularDescriptorType: $This->{Type}; IgnorePhosphorus: " . ($This->{IgnorePhosphorus} ? "Yes" : "No") . "; IgnoreSulfur: " . ($This->{IgnoreSulfur} ? "Yes" : "No"); 285 286 # Setup molecular descriptor information... 287 $TPSADescriptorsString .= "; " . $This->_StringifyDescriptorNamesAndValues(); 288 289 return $TPSADescriptorsString; 290 } 291 292 # Is it a TPSADescriptors object? 293 sub _IsTPSADescriptors { 294 my($Object) = @_; 295 296 return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0; 297 } 298