MayaChemTools

   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