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