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