MayaChemTools

   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