       1 package MolecularDescriptors::TPSADescriptors;
       2 #
       3 # $RCSfile: TPSADescriptors.pm,v $
       4 # $Date: 2015/02/28 20:49:20 $
       5 # $Revision: 1.18 $
       6 #
       7 # Author: Manish Sud <msud@san.rr.com>
       8 #
       9 # Copyright (C) 2015 Manish Sud. All rights reserved.
      10 #
      11 # This file is part of MayaChemTools.
      12 #
      13 # MayaChemTools is free software; you can redistribute it and/or modify it under
      14 # the terms of the GNU Lesser General Public License as published by the Free
      15 # Software Foundation; either version 3 of the License, or (at your option) any
      16 # later version.
      17 #
      18 # MayaChemTools is distributed in the hope that it will be useful, but without
      19 # any warranty; without even the implied warranty of merchantability of fitness
      20 # for a particular purpose.  See the GNU Lesser General Public License for more
      21 # details.
      22 #
      23 # You should have received a copy of the GNU Lesser General Public License
      24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
      25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
      26 # Boston, MA, 02111-1307, USA.
      27 #
      28
      29 use strict;
      30 use Carp;
      31 use Exporter;
      32 use Scalar::Util ();
      33 use TextUtil ();
      34 use MathUtil ();
      35 use Atom;
      36 use Molecule;
      37 use MolecularDescriptors::MolecularDescriptors;
      38 use AtomTypes::TPSAAtomTypes;
      39
      40 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      41
      42 @ISA = qw(MolecularDescriptors::MolecularDescriptors Exporter);
      43 @EXPORT = qw();
      44 @EXPORT_OK = qw(GetDescriptorNames);
      45
      46 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
      47
      48 # Setup class variables...
      49 my($ClassName, @DescriptorNames);
      50 _InitializeClass();
      51
      52 # Overload Perl functions...
      53 use overload '""' => 'StringifyTPSADescriptors';
      54
      55 # Class constructor...
      56 sub new {
      57   my($Class, %NamesAndValues) = @_;
      58
      59   # Initialize object...
      60   my $This = $Class->SUPER::new();
      61   bless $This, ref($Class) || $Class;
      62   $This->_InitializeTPSADescriptors();
      63
      64   $This->_InitializeTPSADescriptorsProperties(%NamesAndValues);
      65
      66   return $This;
      67 }
      68
      69 # Initialize class ...
      70 sub _InitializeClass {
      71   #Class name...
      72   $ClassName = __PACKAGE__;
      73
      74   # Descriptor names...
      75   @DescriptorNames = ('TPSA');
      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 _InitializeTPSADescriptors {
      91   my($This) = @_;
      92
      93   # Type of MolecularDescriptor...
      94   $This->{Type} = 'TPSA';
      95
      96   # By default, TPSA atom contributions from Phosphorus and Sulfur atoms
      97   # are not included during TPSA calculations. [ Ref 91 ]
      98   #
      99   $This->{IgnorePhosphorus} = 1;
     100   $This->{IgnoreSulfur} = 1;
     101
     102   # TPSA atom types assigned to appropriate atoms...
     103   %{$This->{AtomTypes}} = ();
     104
     105   # Intialize descriptor names and values...
     106   $This->_InitializeDescriptorNamesAndValues(@DescriptorNames);
     107
     108   return $This;
     109 }
     110
     111 # Initialize object properties...
     112 #
     113 sub _InitializeTPSADescriptorsProperties {
     114   my($This, %NamesAndValues) = @_;
     115
     116   my($Name, $Value, $MethodName);
     117   while (($Name, $Value) = each  %NamesAndValues) {
     118     $MethodName = "Set${Name}";
     119     $This->$MethodName($Value);
     120   }
     121
     122   return $This;
     123 }
     124
     125 # Calculate Topological Polar Surface Area (TPSA) value [ Ref 90-91 ] for molecule...
     126 #
     127 # Methodology:
     128 #   . Assign TPSA atom types [ Ref 90-91 ] to Nitrogen and Oxygen
     129 #     atoms with optional assignment to Phosphorus and Sulfur atoms.
     130 #   . Calculate TPSA value adding contribution of appropriate atom types.
     131 #
     132 sub GenerateDescriptors {
     133   my($This) = @_;
     134
     135   # Initialize descriptor values...
     136   $This->_InitializeDescriptorValues();
     137
     138   # Check availability of molecule...
     139   if (!$This->{Molecule}) {
     140     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Molecule data is not available: Molecule object hasn't been set...";
     141     return undef;
     142   }
     143
     144   # Cache appropriate molecule data...
     145   $This->_SetupMoleculeDataCache();
     146
     147   # Assign TPSA atom types...
     148   if (!$This->_AssignAtomTypes()) {
     149     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Couldn't assign valid TPSA atom types to appropriate atoms...";
     150     return undef;
     151   }
     152
     153   # Calculate descriptor values...
     154   if (!$This->_CalculateDescriptorValues()) {
     155     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Couldn't calculate TPSA values corresponding to assigned TPSA atom types...";
     156     return undef;
     157   }
     158
     159   # Set final descriptor values...
     160   $This->_SetFinalDescriptorValues();
     161
     162   # Clear cached molecule data...
     163   $This->_ClearMoleculeDataCache();
     164
     165   return $This;
     166 }
     167
     168 # Assign TPSA atom types..
     169 #
     170 sub _AssignAtomTypes {
     171   my($This) = @_;
     172   my($TPSAAtomTypes, $Atom, $AtomID);
     173
     174   %{$This->{AtomTypes}} = ();
     175
     176   # Assign atom types...
     177   $TPSAAtomTypes = new AtomTypes::TPSAAtomTypes('Molecule' => $This->{Molecule}, 'IgnorePhosphorus' => $This->{IgnorePhosphorus}, 'IgnoreSulfur' => $This->{IgnoreSulfur});
     178   $TPSAAtomTypes->AssignAtomTypes();
     179
     180   # Make sure TPSA atom types assignment is successful...
     181   if (!$TPSAAtomTypes->IsAtomTypesAssignmentSuccessful()) {
     182     return undef;
     183   }
     184
     185   # Collect assigned atom types...
     186   for $Atom (@{$This->{Atoms}}) {
     187     $AtomID = $Atom->GetID();
     188     $This->{AtomTypes}{$AtomID} = $TPSAAtomTypes->GetAtomType($Atom);
     189   }
     190
     191   return $This;
     192 }
     193
     194 # Calculate TPSA value...
     195 #
     196 sub _CalculateDescriptorValues {
     197   my($This) = @_;
     198   my($Atom, $AtomID, $TPSA, $TPSAContribution, $TPSADataRef, $AtomType);
     199
     200   $TPSA = 0;
     201
     202   # Get TPSA atom types data...
     203   $TPSADataRef = AtomTypes::TPSAAtomTypes::GetTPSAAtomTypesData();
     204
     205   ATOM: for $Atom (@{$This->{Atoms}}) {
     206     $AtomID = $Atom->GetID();
     207     $AtomType = $This->{AtomTypes}{$AtomID};
     208
     209     # Ignore inappropriate atoms...
     210     if ($AtomType =~ /^None$/i) {
     211       next ATOM;
     212     }
     213
     214     $TPSAContribution = 0.0;
     215
     216     if ($AtomType =~ /^(N|O)$/i) {
     217       # TPSA contributions for Nitrogen and Oxygen atoms not explicity defined using atom
     218       # environments in Table 1 [ Ref 90 ]
     219       if ($AtomType =~ /^N$/i) {
     220         # N = 30.5 - X*8.2 + H*1.5 or 0.0 for negative value
     221         $TPSAContribution = 30.5 - $Atom->GetAtomicInvariantValue('X') * 8.2 + $Atom->GetAtomicInvariantValue('H') * 1.5;
     222       }
     223       elsif ($AtomType =~ /^O$/i) {
     224         # O = 28.5 - X*8.6 + H*1.5 or 0.0 for negative value
     225         $TPSAContribution = 28.5 - $Atom->GetAtomicInvariantValue('X') * 8.6 + $Atom->GetAtomicInvariantValue('H') * 1.5;
     226       }
     227       if ($TPSAContribution < 0.0) {
     228         $TPSAContribution = 0.0;
     229       }
     230     }
     231     elsif (exists $TPSADataRef->{DataCol3}{$AtomType}) {
     232       # Data for TPSA contribution is in column number 3...
     233       $TPSAContribution = $TPSADataRef->{DataCol3}{$AtomType};
     234     }
     235     else {
     236       # No TPSA data for assigned atom type...
     237       return undef;
     238     }
     239     $TPSA += $TPSAContribution;
     240   }
     241
     242   # Track the calculated values...
     243   $This->{TPSA} = MathUtil::round($TPSA, 2);
     244
     245   return $This;
     246 }
     247
     248 # Setup final descriptor values...
     249 #
     250 sub _SetFinalDescriptorValues {
     251   my($This) = @_;
     252
     253   $This->{DescriptorsGenerated} = 1;
     254
     255   $This->SetDescriptorValues($This->{TPSA});
     256
     257   return $This;
     258 }
     259
     260 # Cache  appropriate molecule data...
     261 #
     262 sub _SetupMoleculeDataCache {
     263   my($This) = @_;
     264
     265   @{$This->{Atoms}} = $This->GetMolecule()->GetAtoms();
     266
     267   return $This;
     268 }
     269
     270 # Clear cached molecule data...
     271 #
     272 sub _ClearMoleculeDataCache {
     273   my($This) = @_;
     274
     275   @{$This->{Atoms}} = ();
     276
     277   return $This;
     278 }
     279
     280 # Return a string containg data for TPSADescriptors object...
     281 #
     282 sub StringifyTPSADescriptors {
     283   my($This) = @_;
     284   my($TPSADescriptorsString);
     285
     286   # Type of MolecularDescriptors...
     287   $TPSADescriptorsString = "MolecularDescriptorType: $This->{Type}; IgnorePhosphorus: " . ($This->{IgnorePhosphorus} ? "Yes" : "No") . "; IgnoreSulfur: " .  ($This->{IgnoreSulfur} ? "Yes" : "No");
     288
     289   # Setup molecular descriptor information...
     290   $TPSADescriptorsString .= "; " . $This->_StringifyDescriptorNamesAndValues();
     291
     292   return $TPSADescriptorsString;
     293 }
     294
     295 # Is it a TPSADescriptors object?
     296 sub _IsTPSADescriptors {
     297   my($Object) = @_;
     298
     299   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
     300 }
     301