1 package AtomTypes::FunctionalClassAtomTypes; 2 # 3 # $RCSfile: FunctionalClassAtomTypes.pm,v $ 4 # $Date: 2015/02/28 20:48:03 $ 5 # $Revision: 1.20 $ 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 AtomTypes::AtomTypes; 34 use Molecule; 35 36 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 37 38 @ISA = qw(AtomTypes::AtomTypes Exporter); 39 @EXPORT = qw(); 40 @EXPORT_OK = qw(IsFunctionalClassAvailable GetAvailableFunctionalClasses GetFunctionalClassesOrder); 41 42 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]); 43 44 # Setup class variables... 45 my($ClassName, @FunctionalClassesOrder, %AvailableFunctionalClasses, %AvailableFunctionalClassesByDescription); 46 _InitializeClass(); 47 48 # Overload Perl functions... 49 use overload '""' => 'StringifyFunctionalClassAtomTypes'; 50 51 # Class constructor... 52 sub new { 53 my($Class, %NamesAndValues) = @_; 54 55 # Initialize object... 56 my $This = $Class->SUPER::new(); 57 bless $This, ref($Class) || $Class; 58 $This->_InitializeFunctionalClassAtomTypes(); 59 60 $This->_InitializeFunctionalClassAtomTypesProperties(%NamesAndValues); 61 62 return $This; 63 } 64 65 # Initialize class ... 66 sub _InitializeClass { 67 #Class name... 68 $ClassName = __PACKAGE__; 69 70 # Initialize class level functional classes information... 71 _InitializeFunctionalClasses(); 72 } 73 74 # Initialize class level functional class information which doesn't change during 75 # instantiations of objects... 76 # 77 sub _InitializeFunctionalClasses { 78 # Available functional classes for generating atom types... 79 # 80 %AvailableFunctionalClasses = (); 81 %AvailableFunctionalClasses = ('HBD' => 'HydrogenBondDonor', 82 'HBA' => 'HydrogenBondAcceptor', 83 'PI' => 'PositivelyIonizable', 84 'NI' => 'NegativelyIonizable', 85 'Ar' => 'Aromatic', 86 'Hal' => 'Halogen', 87 'H' => 'Hydrophobic', 88 'RA' => 'RingAtom', 89 'CA' => 'ChainAtom'); 90 91 # Setup available functional classe description to abbreviation map... 92 # 93 my($Key, $Value, $Description, @Descriptions); 94 %AvailableFunctionalClassesByDescription = (); 95 while (($Key, $Value) = each %AvailableFunctionalClasses) { 96 @Descriptions = ($Value =~ /|/) ? (split /\|/, $Value) : ($Value); 97 for $Description (@Descriptions) { 98 $AvailableFunctionalClassesByDescription{$Description} = $Key; 99 } 100 } 101 102 # Functional classes order used for generating atom types... 103 # 104 @FunctionalClassesOrder = (); 105 @FunctionalClassesOrder = sort keys %AvailableFunctionalClasses; 106 } 107 108 # Initialize object data... 109 # 110 sub _InitializeFunctionalClassAtomTypes { 111 my($This) = @_; 112 113 # Type of AtomTypes... 114 $This->{Type} = 'FunctionalClass'; 115 116 # By default hydrogens are also assigned atom types... 117 $This->{IgnoreHydrogens} = 0; 118 119 # Initialize atom types information... 120 $This->_InitializeAtomTypesInformation(); 121 122 return $This; 123 } 124 125 # Inialize functional class information used for generating atom types... 126 # 127 sub _InitializeAtomTypesInformation { 128 my($This) = @_; 129 130 # Default functional classes to use for generating atom types: HBD, HBA, PI, NI, Ar, Hal 131 # 132 %{$This->{FunctionalClassesToUse}} = (); 133 %{$This->{FunctionalClassesToUse}} = ('HBD' => 1, 'HBA' => 1, 134 'PI' => 1, 'NI' => 1, 135 'Ar' => 1, 136 'Hal' => 1, 137 'H' => 0, 138 'RA' => 0, 'CA' => 0); 139 return $This; 140 } 141 142 # Initialize object properties... 143 # 144 sub _InitializeFunctionalClassAtomTypesProperties { 145 my($This, %NamesAndValues) = @_; 146 147 my($Name, $Value, $MethodName); 148 while (($Name, $Value) = each %NamesAndValues) { 149 $MethodName = "Set${Name}"; 150 $This->$MethodName($Value); 151 } 152 153 # Make sure molecule object was specified... 154 if (!exists $NamesAndValues{Molecule}) { 155 croak "Error: ${ClassName}->New: Object can't be instantiated without specifying molecule..."; 156 } 157 158 return $This; 159 } 160 161 # Disable change of AvailableFunctionalClasses... 162 # 163 sub SetAvailableFunctionalClasses { 164 my($This) = @_; 165 166 carp "Warning: ${ClassName}->SetFunctionalClassesOrder: Available functional classes can't be changed..."; 167 168 return $This; 169 } 170 171 # Disable change of functional classes order used for generation of atom types... 172 # 173 sub SetFunctionalClassesOrder { 174 my($This) = @_; 175 176 carp "Warning: ${ClassName}->SetFunctionalClassesOrder: functional classes order can't be changed..."; 177 178 return $This; 179 } 180 181 # Set functional classes to use for atom types... 182 # 183 sub SetFunctionalClassesToUse { 184 my($This, @Values) = @_; 185 my($FirstValue, $TypeOfFirstValue, $FunctionalClass, $SpecifiedFunctionalClass, $FunctionalClassValue, @SpecifiedFunctionalClasses, %FunctionalClassesToUse); 186 187 if (!@Values) { 188 carp "Warning: ${ClassName}->SetFunctionalClassesToUse: No values specified..."; 189 return; 190 } 191 192 $FirstValue = $Values[0]; 193 $TypeOfFirstValue = ref $FirstValue; 194 @SpecifiedFunctionalClasses = (); 195 196 if ($TypeOfFirstValue =~ /^ARRAY/) { 197 push @SpecifiedFunctionalClasses, @{$FirstValue}; 198 } 199 else { 200 push @SpecifiedFunctionalClasses, @Values; 201 } 202 203 # Make sure specified FunctionalClasses are valid... 204 for $SpecifiedFunctionalClass (@SpecifiedFunctionalClasses) { 205 if (exists $AvailableFunctionalClasses{$SpecifiedFunctionalClass}) { 206 $FunctionalClass = $SpecifiedFunctionalClass; 207 } 208 elsif ($AvailableFunctionalClassesByDescription{$SpecifiedFunctionalClass}) { 209 $FunctionalClass = $AvailableFunctionalClassesByDescription{$SpecifiedFunctionalClass}; 210 } 211 else { 212 croak "Error: ${ClassName}->SetFunctionalClassesToUse: Specified functional class, $SpecifiedFunctionalClass, is not supported...\n "; 213 } 214 $FunctionalClassesToUse{$FunctionalClass} = 1; 215 } 216 217 # Set functional classes... 218 for $FunctionalClass (keys %{$This->{FunctionalClassesToUse}}) { 219 $This->{FunctionalClassesToUse}{$FunctionalClass} = 0; 220 if (exists $FunctionalClassesToUse{$FunctionalClass}) { 221 $This->{FunctionalClassesToUse}{$FunctionalClass} = 1; 222 } 223 } 224 225 return $This; 226 } 227 228 # Is it an available FunctionalClass? 229 # 230 sub IsFunctionalClassAvailable { 231 my($FirstParameter, $SecondParameter) = @_; 232 my($This, $FunctionalClass, $Status); 233 234 if ((@_ == 2) && (_IsFunctionalClassAtomTypes($FirstParameter))) { 235 ($This, $FunctionalClass) = ($FirstParameter, $SecondParameter); 236 } 237 else { 238 $FunctionalClass = $FirstParameter; 239 } 240 $Status = exists($AvailableFunctionalClasses{$FunctionalClass}) || exists($AvailableFunctionalClassesByDescription{$FunctionalClass}) ? 1 : 0; 241 242 return $Status; 243 } 244 245 # Get a hash containing available functional classes and their description 246 # as key/value pairs. 247 # 248 sub GetAvailableFunctionalClasses { 249 return %AvailableFunctionalClasses; 250 } 251 252 # Get an array containing order of functional classes used to generate atom types... 253 # 254 sub GetFunctionalClassesOrder { 255 return @FunctionalClassesOrder; 256 } 257 258 # Assign functional class atom types to all atoms... 259 # 260 # Let: 261 # HBD: HydrogenBondDonor 262 # HBA: HydrogenBondAcceptor 263 # PI : PositivelyIonizable 264 # NI : NegativelyIonizable 265 # Ar : Aromatic 266 # Hal : Halogen 267 # H : Hydrophobic 268 # RA : RingAtom 269 # CA : ChainAtom 270 # 271 # Then: 272 # 273 # Function class atom type specification for an atom corresponds to: 274 # 275 # Ar.CA.H.HBA.HBD.Hal.NI.PI.RA 276 # 277 # Default functional classes used are: HBD, HBA, PI, NI, Ar, Hal 278 # 279 # FunctionalAtomTypes are assigned using the following definitions [ Ref 60-61, Ref 65-66 ]: 280 # 281 # HydrogenBondDonor: NH, NH2, OH 282 # HydrogenBondAcceptor: N[!H], O 283 # PositivelyIonizable: +, NH2 284 # NegativelyIonizable: -, C(=O)OH, S(=O)OH, P(=O)OH 285 # 286 # Notes: 287 # . Final functional class atom type shows only those functional 288 # classes to which an atom belongs; others are not shown. 289 # . A null string is assigned as final atom type to those atom which 290 # don't belong to any of the specified functional classes. 291 # 292 # Examples of functional class atom types: 293 # 294 # HBD.HBA - Hydrogen bond donor and acceptor 295 # HBD.RA - Hydrogen bond donor in a ring 296 # 297 # 298 sub AssignAtomTypes { 299 my($This) = @_; 300 my($Atom, $AtomType, $FunctionalClass, $FunctionalClassValue, @FunctionalClasses); 301 302 ATOM: for $Atom ($This->GetMolecule()->GetAtoms()) { 303 if ($This->{IgnoreHydrogens} && $Atom->IsHydrogen()) { 304 next ATOM; 305 } 306 @FunctionalClasses = (); 307 308 # Go over functional classes... 309 ATOMICINVARIANT: for $FunctionalClass (@FunctionalClassesOrder) { 310 if (!$This->{FunctionalClassesToUse}{$FunctionalClass}) { 311 next ATOMICINVARIANT; 312 } 313 if ($Atom->IsFunctionalClassType($FunctionalClass)) { 314 push @FunctionalClasses, $FunctionalClass; 315 } 316 } 317 # Create and assign atom type to atom... 318 $AtomType = (scalar @FunctionalClasses) ? TextUtil::JoinWords(\@FunctionalClasses, ".", 0) : "None"; 319 $This->SetAtomType($Atom, $AtomType); 320 } 321 return $This; 322 } 323 324 # Are all atoms types successfully assigned? 325 # 326 # Notes: 327 # . Base class method is overridden to always return 1: An appropriate value, functional 328 # class types delmited by dot or None, is always assigned to atoms. 329 # 330 sub IsAtomTypesAssignmentSuccessful { 331 my($This) = @_; 332 333 return 1; 334 } 335 336 # Return a string containg data for FunctionalClassAtomTypes object... 337 # 338 sub StringifyFunctionalClassAtomTypes { 339 my($This) = @_; 340 my($AtomTypesString); 341 342 # Type of AtomTypes... 343 $AtomTypesString = "AtomTypes: $This->{Type}; IgnoreHydrogens: " . ($This->{IgnoreHydrogens} ? "Yes" : "No"); 344 345 # AvailableFunctionalClasses and FunctionalClassesToUse... 346 my($FunctionalClass, @FunctionalClasses, @FunctionalClassesToUse); 347 348 @FunctionalClassesToUse = (); 349 @FunctionalClasses = (); 350 for $FunctionalClass (@FunctionalClassesOrder) { 351 push @FunctionalClasses, "$FunctionalClass: $AvailableFunctionalClasses{$FunctionalClass}"; 352 if ($This->{FunctionalClassesToUse}{$FunctionalClass}) { 353 push @FunctionalClassesToUse, $FunctionalClass; 354 } 355 } 356 $AtomTypesString .= "; FunctionalClassesToUse: <" . TextUtil::JoinWords(\@FunctionalClassesToUse, ", ", 0) . ">"; 357 $AtomTypesString .= "; FunctionalClassesOrder: <" . TextUtil::JoinWords(\@FunctionalClassesOrder, ", ", 0) . ">"; 358 $AtomTypesString .= "; AvailableFunctionalClasses: <" . TextUtil::JoinWords(\@FunctionalClasses, ", ", 0) . ">"; 359 360 # Setup atom types information... 361 my($AtomID, $AtomType, @AtomTypesInfo, %AssignedAtomTypes); 362 363 @AtomTypesInfo = (); 364 %AssignedAtomTypes = $This->GetAtomTypes(); 365 366 for $AtomID (sort { $a <=> $b } keys %AssignedAtomTypes) { 367 $AtomType = $AssignedAtomTypes{$AtomID} ? $AssignedAtomTypes{$AtomID} : 'None'; 368 push @AtomTypesInfo, "$AtomID:$AtomType"; 369 } 370 $AtomTypesString .= "; AtomIDs:AtomTypes: <" . TextUtil::JoinWords(\@AtomTypesInfo, ", ", 0) . ">"; 371 372 return $AtomTypesString; 373 } 374 375 # Is it a FunctionalClassAtomTypes object? 376 sub _IsFunctionalClassAtomTypes { 377 my($Object) = @_; 378 379 return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0; 380 } 381