       1 package MolecularDescriptors::TPSADescriptors;
       2 #
       3 # File: TPSADescriptors.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 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