       1 package MolecularDescriptors::RotatableBondsDescriptors;
       2 #
       3 # $RCSfile: RotatableBondsDescriptors.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 Atom;
      35 use Molecule;
      36 use MolecularDescriptors::MolecularDescriptors;
      37
      38 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      39
      40 @ISA = qw(MolecularDescriptors::MolecularDescriptors Exporter);
      41 @EXPORT = qw();
      42 @EXPORT_OK = qw(GetDescriptorNames);
      43
      44 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
      45
      46 # Setup class variables...
      47 my($ClassName, @DescriptorNames);
      48 _InitializeClass();
      49
      50 # Overload Perl functions...
      51 use overload '""' => 'StringifyRotatableBondsDescriptors';
      52
      53 # Class constructor...
      54 sub new {
      55   my($Class, %NamesAndValues) = @_;
      56
      57   # Initialize object...
      58   my $This = $Class->SUPER::new();
      59   bless $This, ref($Class) || $Class;
      60   $This->_InitializeRotatableBondsDescriptors();
      61
      62   $This->_InitializeRotatableBondsDescriptorsProperties(%NamesAndValues);
      63
      64   return $This;
      65 }
      66
      67 # Initialize class ...
      68 sub _InitializeClass {
      69   #Class name...
      70   $ClassName = __PACKAGE__;
      71
      72   # Descriptor names...
      73   @DescriptorNames = ('RotatableBonds');
      74
      75 }
      76
      77 # Get descriptor names as an array.
      78 #
      79 # This functionality can be either invoked as a class function or an
      80 # object method.
      81 #
      82 sub GetDescriptorNames {
      83   return @DescriptorNames;
      84 }
      85
      86 # Initialize object data...
      87 #
      88 sub _InitializeRotatableBondsDescriptors {
      89   my($This) = @_;
      90
      91   # Type of MolecularDescriptor...
      92   $This->{Type} = 'RotatableBonds';
      93
      94   # MayaChemTools rotatable bonds default definition corresponds to modifed
      95   # version of rotatable bonds definition used by Veber et al. [ Ref 92 ]
      96   #
      97   $This->{IgnoreTerminalBonds} = 1;
      98   $This->{IgnoreBondsToTripleBonds} = 1;
      99   $This->{IgnoreAmideBonds} = 1;
     100   $This->{IgnoreThioamideBonds} = 1;
     101   $This->{IgnoreSulfonamideBonds} = 1;
     102
     103   # Intialize descriptor names and values...
     104   $This->_InitializeDescriptorNamesAndValues(@DescriptorNames);
     105
     106   return $This;
     107 }
     108
     109 # Initialize object properties...
     110 #
     111 sub _InitializeRotatableBondsDescriptorsProperties {
     112   my($This, %NamesAndValues) = @_;
     113
     114   my($Name, $Value, $MethodName);
     115   while (($Name, $Value) = each  %NamesAndValues) {
     116     $MethodName = "Set${Name}";
     117     $This->$MethodName($Value);
     118   }
     119
     120   return $This;
     121 }
     122
     123 # Calculate number of rotatable bonds in a molecule...
     124 #
     125 # A rotatable bond is defined as any single bond which is not in a ring
     126 # and involves only non-hydrogen atoms. By default, the following types
     127 # of single bonds are not considered rotatable bonds:
     128 #
     129 #   . Terminal bonds
     130 #   . Bonds attached to triple bonds
     131 #   . Amide C-N bonds
     132 #   . Thioamide C-N bond bonds
     133 #   . Sulfonamide S-N bonds
     134 #
     135 # MayaChemTools rotatable bonds default definition corresponds to modifed
     136 # version of rotatable bonds definition used by Veber et al. [ Ref 92 ]
     137 #
     138 sub GenerateDescriptors {
     139   my($This) = @_;
     140
     141   # Initialize descriptor values...
     142   $This->_InitializeDescriptorValues();
     143
     144   # Check availability of molecule...
     145   if (!$This->{Molecule}) {
     146     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Molecule data is not available: Molecule object hasn't been set...";
     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 RotatableBonds values...";
     153     return undef;
     154   }
     155
     156   # Set final descriptor values...
     157   $This->_SetFinalDescriptorValues();
     158
     159   return $This;
     160 }
     161
     162 # Calculate RotatableBonds value...
     163 #
     164 sub _CalculateDescriptorValues {
     165   my($This) = @_;
     166   my($Bond, $RotatableBonds, $Atom1, $Atom2);
     167
     168   $RotatableBonds = 0;
     169
     170   BOND: for $Bond ($This->{Molecule}->GetBonds()) {
     171     # Is it a non-ring ring bond?
     172     if (!$Bond->IsSingle() || $Bond->IsInRing()) {
     173       next BOND;
     174     }
     175
     176     ($Atom1, $Atom2) = $Bond->GetAtoms();
     177
     178     # Does bond contain any Hydrogen atoms?
     179     if ($Atom1->IsHydrogen() || $Atom2->IsHydrogen()) {
     180       next BOND;
     181     }
     182
     183     # Check for terminal bonds...
     184     if ($This->{IgnoreTerminalBonds} && $This->_IsTerminalBond($Atom1, $Atom2)) {
     185       next BOND;
     186     }
     187
     188     # Check for bonds attached to triple bonds...
     189     if ($This->{IgnoreBondsToTripleBonds} && $This->_IsAttachedToTripleBond($Atom1, $Atom2)) {
     190       next BOND;
     191     }
     192
     193     # Check for amide bonds...
     194     if ($This->{IgnoreAmideBonds} && $This->_IsAmideBond($Atom1, $Atom2)) {
     195       next BOND;
     196     }
     197
     198     # Check for amide bonds...
     199     if ($This->{IgnoreThioamideBonds} && $This->_IsThioamideBond($Atom1, $Atom2)) {
     200       next BOND;
     201     }
     202
     203     # Check for sulfonamide bonds...
     204     if ($This->{IgnoreSulfonamideBonds} && $This->_IsSulfonamideBond($Atom1, $Atom2)) {
     205       next BOND;
     206     }
     207
     208     $RotatableBonds += 1;
     209   }
     210
     211   # Track the calculated values...
     212   $This->{RotatableBonds} = $RotatableBonds;
     213
     214   return $This;
     215 }
     216
     217 # Is it a terminal bond?
     218 #
     219 sub _IsTerminalBond {
     220   my($This, $Atom1, $Atom2) = @_;
     221
     222   return ($Atom1->GetAtomicInvariantValue('X') <= 1 || $Atom2->GetAtomicInvariantValue('X') <= 1 ) ? 1 : 0;
     223 }
     224
     225 # Is it attached to a terminal bond?
     226 #
     227 sub _IsAttachedToTripleBond {
     228   my($This, $Atom1, $Atom2) = @_;
     229
     230   return ($Atom1->GetAtomicInvariantValue('LBO') == 3 || $Atom2->GetAtomicInvariantValue('LBO') == 3) ? 1 : 0;
     231 }
     232
     233 # Is it an amide bond?
     234 #
     235 # Amide: R-C(=O)-N(-R)(-R")
     236 #
     237 sub _IsAmideBond {
     238   my($This, $Atom1, $Atom2) = @_;
     239   my($CarbonAtom, $NitrogenAtom);
     240
     241   ($CarbonAtom, $NitrogenAtom) = (undef, undef);
     242
     243   if ($Atom1->IsCarbon() && $Atom2->IsNitrogen()) {
     244     ($CarbonAtom, $NitrogenAtom) = ($Atom1, $Atom2);
     245   }
     246   elsif ($Atom2->IsCarbon() && $Atom1->IsNitrogen()) {
     247     ($CarbonAtom, $NitrogenAtom) = ($Atom2, $Atom1);
     248   }
     249
     250   if (!$CarbonAtom) {
     251     return 0;
     252   }
     253
     254   return $CarbonAtom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['O', 'N', 'C,H'], ['=', '-', '-']) ? 1 : 0;
     255 }
     256
     257 # Is it a thioamide bond?
     258 #
     259 # Thioamide: R-C(=S)-N(-R)(-R")
     260 #
     261 sub _IsThioamideBond {
     262   my($This, $Atom1, $Atom2) = @_;
     263   my($CarbonAtom, $NitrogenAtom);
     264
     265   ($CarbonAtom, $NitrogenAtom) = (undef, undef);
     266
     267   if ($Atom1->IsCarbon() && $Atom2->IsNitrogen()) {
     268     ($CarbonAtom, $NitrogenAtom) = ($Atom1, $Atom2);
     269   }
     270   elsif ($Atom2->IsCarbon() && $Atom1->IsNitrogen()) {
     271     ($CarbonAtom, $NitrogenAtom) = ($Atom2, $Atom1);
     272   }
     273
     274   if (!$CarbonAtom) {
     275     return 0;
     276   }
     277
     278   return $CarbonAtom->DoesAtomNeighborhoodMatch('C.T3.DB1', ['S', 'N', 'C,H'], ['=', '-', '-']) ? 1 : 0;
     279 }
     280
     281 # Is it a sulfonamide bond?
     282 #
     283 # Sulfonamide: R-S(=O)(=O)-N(-R)(-R")
     284 #
     285 sub _IsSulfonamideBond {
     286   my($This, $Atom1, $Atom2) = @_;
     287   my($SulfurAtom, $NitrogenAtom);
     288
     289   ($SulfurAtom, $NitrogenAtom) = (undef, undef);
     290
     291   if ($Atom1->IsSulfur() && $Atom2->IsNitrogen()) {
     292     ($SulfurAtom, $NitrogenAtom) = ($Atom1, $Atom2);
     293   }
     294   elsif ($Atom2->IsSulfur() && $Atom1->IsNitrogen()) {
     295     ($SulfurAtom, $NitrogenAtom) = ($Atom2, $Atom1);
     296   }
     297
     298   if (!$SulfurAtom) {
     299     return 0;
     300   }
     301
     302   return $SulfurAtom->DoesAtomNeighborhoodMatch('S.T4.DB2', ['O', 'O', 'N', '!O'], ['=', '=', '-', '-']) ? 1 : 0;
     303 }
     304
     305 # Setup final descriptor values...
     306 #
     307 sub _SetFinalDescriptorValues {
     308   my($This) = @_;
     309
     310   $This->{DescriptorsGenerated} = 1;
     311
     312   $This->SetDescriptorValues($This->{RotatableBonds});
     313
     314   return $This;
     315 }
     316
     317 # Return a string containg data for RotatableBondsDescriptors object...
     318 #
     319 sub StringifyRotatableBondsDescriptors {
     320   my($This) = @_;
     321   my($RotatableBondsDescriptorsString);
     322
     323   # Type of MolecularDescriptors...
     324   $RotatableBondsDescriptorsString = "MolecularDescriptorType: $This->{Type}; IgnoreTerminalBonds: " . ($This->{IgnoreTerminalBonds} ? "Yes" : "No") . "; IgnoreBondsToTripleBonds: " .  ($This->{IgnoreBondsToTripleBonds} ? "Yes" : "No") . "; IgnoreAmideBonds: " .  ($This->{IgnoreAmideBonds} ? "Yes" : "No") . "; IgnoreThioamideBonds: " .  ($This->{IgnoreThioamideBonds} ? "Yes" : "No") . "; IgnoreSulfonamideBonds: " .  ($This->{IgnoreSulfonamideBonds} ? "Yes" : "No");
     325
     326   # Setup molecular descriptor information...
     327   $RotatableBondsDescriptorsString .= "; " . $This->_StringifyDescriptorNamesAndValues();
     328
     329   return $RotatableBondsDescriptorsString;
     330 }
     331
     332 # Is it a RotatableBondsDescriptors object?
     333 sub _IsRotatableBondsDescriptors {
     334   my($Object) = @_;
     335
     336   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
     337 }
     338