       1 package MolecularDescriptors::SLogPAndSMRDescriptors;
       2 #
       3 # File: SLogPAndSMRDescriptors.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::SLogPAtomTypes;
      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 '""' => 'StringifySLogPAndSMRDescriptors';
      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->_InitializeSLogPAndSMRDescriptors();
      60
      61   $This->_InitializeSLogPAndSMRDescriptorsProperties(%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 = ('SLogP', 'SMR');
      73 }
      74
      75 # Get descriptor names as an array.
      76 #
      77 # This functionality can be either invoked as a class function or an
      78 # object method.
      79 #
      80 sub GetDescriptorNames {
      81   return @DescriptorNames;
      82 }
      83
      84 # Initialize object data...
      85 #
      86 sub _InitializeSLogPAndSMRDescriptors {
      87   my($This) = @_;
      88
      89   # Type of MolecularDescriptor...
      90   $This->{Type} = 'SLogPAndSMR';
      91
      92   # SLogPAndSMR atom types assigned to hydrogen and non-hydrogen atoms...
      93   %{$This->{AtomTypes}} = ();
      94
      95   # Intialize descriptor names and values...
      96   $This->_InitializeDescriptorNamesAndValues(@DescriptorNames);
      97
      98   return $This;
      99 }
     100
     101 # Initialize object properties...
     102 #
     103 sub _InitializeSLogPAndSMRDescriptorsProperties {
     104   my($This, %NamesAndValues) = @_;
     105
     106   my($Name, $Value, $MethodName);
     107   while (($Name, $Value) = each  %NamesAndValues) {
     108     $MethodName = "Set${Name}";
     109     $This->$MethodName($Value);
     110   }
     111
     112   return $This;
     113 }
     114
     115
     116 # Calculate SLogPAndSMR value [ Ref 89 ] for a molecule...
     117 #
     118 # Methodology:
     119 #   . Assign SLogP atom types to all atoms.
     120 #   . Calculate SLogP and SMR value by adding contribution of each atom type.
     121 #
     122 # Caveats:
     123 #   . All hydrogens must be added to molecule before calling GenerateDescriptors.
     124 #
     125 sub GenerateDescriptors {
     126   my($This) = @_;
     127
     128   # Initialize descriptor values...
     129   $This->_InitializeDescriptorValues();
     130
     131   # Check availability of molecule...
     132   if (!$This->{Molecule}) {
     133     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Molecule data is not available: Molecule object hasn't been set...";
     134     return undef;
     135   }
     136
     137   # Cache appropriate molecule data...
     138   $This->_SetupMoleculeDataCache();
     139
     140   # Assign SLogP atom types...
     141   if (!$This->_AssignAtomTypes()) {
     142     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Couldn't assign valid SLogPAndSMR atom types to all atoms...";
     143     return undef;
     144   }
     145
     146   # Calculate descriptor values...
     147   if (!$This->_CalculateDescriptorValues()) {
     148     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular description generation didn't succeed: Couldn't calculate SLogPAndSMR values corresponding to assigned SLogP atom types...";
     149     return undef;
     150   }
     151
     152   # Set final descriptor values...
     153   $This->_SetFinalDescriptorValues();
     154
     155   # Clear cached molecule data...
     156   $This->_ClearMoleculeDataCache();
     157
     158   return $This;
     159 }
     160
     161 # Assign SLogPAndSMR atom types..
     162 #
     163 sub _AssignAtomTypes {
     164   my($This) = @_;
     165   my($SLogPAtomTypes, $Atom, $AtomID);
     166
     167   %{$This->{AtomTypes}} = ();
     168
     169   # Assign atom types...
     170   $SLogPAtomTypes = new AtomTypes::SLogPAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => 0);
     171   $SLogPAtomTypes->AssignAtomTypes();
     172
     173   # Make sure SLogP atom types assignment is successful...
     174   if (!$SLogPAtomTypes->IsAtomTypesAssignmentSuccessful()) {
     175     return undef;
     176   }
     177
     178   # Collect assigned atom types...
     179   for $Atom (@{$This->{Atoms}}) {
     180     $AtomID = $Atom->GetID();
     181     $This->{AtomTypes}{$AtomID} = $SLogPAtomTypes->GetAtomType($Atom);
     182   }
     183
     184   return $This;
     185 }
     186
     187 # Calculate SLogP and SMR values...
     188 #
     189 sub _CalculateDescriptorValues {
     190   my($This) = @_;
     191   my($SLogP, $SMR, $AtomID, $SLogPAndSMRDataRef, $AtomType);
     192
     193   $SLogP = 0; $SMR = 0;
     194
     195   # Get SLogP and SMR atom types data...
     196   $SLogPAndSMRDataRef = AtomTypes::SLogPAtomTypes::GetSLogPAtomTypesData();
     197
     198   for $AtomID (keys %{$This->{AtomTypes}}) {
     199     $AtomType = $This->{AtomTypes}{$AtomID};
     200
     201     # Makes sure data for SLogp and SMR contribution exists for atom type...
     202     if (!(exists($SLogPAndSMRDataRef->{DataCol4}{$AtomType}) && exists($SLogPAndSMRDataRef->{DataCol5}{$AtomType}))) {
     203       return undef;
     204     }
     205
     206     # Data for SLogP contribution is in column number 4...
     207     $SLogP += $SLogPAndSMRDataRef->{DataCol4}{$AtomType};
     208
     209     # Data for SMR contribution is in column number 5...
     210     $SMR += $SLogPAndSMRDataRef->{DataCol5}{$AtomType};
     211   }
     212
     213   # Track the calculated values...
     214   $This->{SLogP} = MathUtil::round($SLogP, 2);
     215   $This->{SMR} = MathUtil::round($SMR, 2);
     216
     217   return $This;
     218 }
     219
     220 # Setup final descriptor values...
     221 #
     222 sub _SetFinalDescriptorValues {
     223   my($This) = @_;
     224
     225   $This->{DescriptorsGenerated} = 1;
     226
     227   $This->SetDescriptorValues($This->{SLogP}, $This->{SMR});
     228
     229   return $This;
     230 }
     231
     232 # Cache  appropriate molecule data...
     233 #
     234 sub _SetupMoleculeDataCache {
     235   my($This) = @_;
     236
     237   @{$This->{Atoms}} = $This->GetMolecule()->GetAtoms();
     238
     239   return $This;
     240 }
     241
     242 # Clear cached molecule data...
     243 #
     244 sub _ClearMoleculeDataCache {
     245   my($This) = @_;
     246
     247   @{$This->{Atoms}} = ();
     248
     249   return $This;
     250 }
     251
     252 # Return a string containg data for SLogPAndSMRDescriptors object...
     253 #
     254 sub StringifySLogPAndSMRDescriptors {
     255   my($This) = @_;
     256   my($SLogPAndSMRDescriptorsString);
     257
     258   $SLogPAndSMRDescriptorsString = "MolecularDescriptorType: $This->{Type}; "  . $This->_StringifyDescriptorNamesAndValues();
     259
     260   return $SLogPAndSMRDescriptorsString;
     261 }
     262
     263 # Is it a SLogPAndSMRDescriptors object?
     264 sub _IsSLogPAndSMRDescriptors {
     265   my($Object) = @_;
     266
     267   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
     268 }
     269