MayaChemTools

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