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