1 package Fingerprints::TopologicalAtomTorsionsFingerprints; 2 # 3 # $RCSfile: TopologicalAtomTorsionsFingerprints.pm,v $ 4 # $Date: 2015/02/28 20:48:54 $ 5 # $Revision: 1.26 $ 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 Fingerprints::Fingerprints; 33 use TextUtil (); 34 use Molecule; 35 use AtomTypes::AtomicInvariantsAtomTypes; 36 use AtomTypes::DREIDINGAtomTypes; 37 use AtomTypes::EStateAtomTypes; 38 use AtomTypes::FunctionalClassAtomTypes; 39 use AtomTypes::MMFF94AtomTypes; 40 use AtomTypes::SLogPAtomTypes; 41 use AtomTypes::SYBYLAtomTypes; 42 use AtomTypes::TPSAAtomTypes; 43 use AtomTypes::UFFAtomTypes; 44 45 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 46 47 @ISA = qw(Fingerprints::Fingerprints Exporter); 48 @EXPORT = qw(); 49 @EXPORT_OK = qw(); 50 51 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]); 52 53 # Setup class variables... 54 my($ClassName); 55 _InitializeClass(); 56 57 # Overload Perl functions... 58 use overload '""' => 'StringifyTopologicalAtomTorsionsFingerprints'; 59 60 # Class constructor... 61 sub new { 62 my($Class, %NamesAndValues) = @_; 63 64 # Initialize object... 65 my $This = $Class->SUPER::new(); 66 bless $This, ref($Class) || $Class; 67 $This->_InitializeTopologicalAtomTorsionsFingerprints(); 68 69 $This->_InitializeTopologicalAtomTorsionsFingerprintsProperties(%NamesAndValues); 70 71 return $This; 72 } 73 74 # Initialize object data... 75 # 76 sub _InitializeTopologicalAtomTorsionsFingerprints { 77 my($This) = @_; 78 79 # Type of fingerprint... 80 $This->{Type} = 'TopologicalAtomTorsions'; 81 82 # Type of vector... 83 $This->{VectorType} = 'FingerprintsVector'; 84 85 # Type of FingerprintsVector... 86 $This->{FingerprintsVectorType} = 'NumericalValues'; 87 88 # Atom identifier type to use for atom IDs in atom torsions... 89 # 90 # Currently supported values are: AtomicInvariantsAtomTypes, DREIDINGAtomTypes, 91 # EStateAtomTypes, FunctionalClassAtomTypes, MMFF94AtomTypes, SLogPAtomTypes, 92 # SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes 93 # 94 $This->{AtomIdentifierType} = ''; 95 96 # Atom types assigned to each heavy atom... 97 # 98 %{$This->{AssignedAtomTypes}} = (); 99 100 # Final unique atom torsions... 101 # 102 @{$This->{AtomTorsionsIDs}} = (); 103 %{$This->{AtomTorsionsCount}} = (); 104 } 105 106 # Initialize class ... 107 sub _InitializeClass { 108 #Class name... 109 $ClassName = __PACKAGE__; 110 } 111 112 # Initialize object properties.... 113 sub _InitializeTopologicalAtomTorsionsFingerprintsProperties { 114 my($This, %NamesAndValues) = @_; 115 116 my($Name, $Value, $MethodName); 117 while (($Name, $Value) = each %NamesAndValues) { 118 $MethodName = "Set${Name}"; 119 $This->$MethodName($Value); 120 } 121 122 # Make sure molecule object was specified... 123 if (!exists $NamesAndValues{Molecule}) { 124 croak "Error: ${ClassName}->New: Object can't be instantiated without specifying molecule..."; 125 } 126 if (!exists $NamesAndValues{AtomIdentifierType}) { 127 croak "Error: ${ClassName}->New: Object can't be instantiated without specifying AtomIdentifierType..."; 128 } 129 130 $This->_InitializeFingerprintsVector(); 131 132 return $This; 133 } 134 135 # Set atom identifier type.. 136 # 137 sub SetAtomIdentifierType { 138 my($This, $IdentifierType) = @_; 139 140 if ($IdentifierType !~ /^(AtomicInvariantsAtomTypes|DREIDINGAtomTypes|EStateAtomTypes|FunctionalClassAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) { 141 croak "Error: ${ClassName}->SetAtomIdentifierType: Specified value, $IdentifierType, for AtomIdentifierType is not vaild. Supported types in current release of MayaChemTools: AtomicInvariantsAtomTypes, DREIDINGAtomTypes, EStateAtomTypes, FunctionalClassAtomTypes, MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes, and UFFAtomTypes."; 142 } 143 144 if ($This->{AtomIdentifierType}) { 145 croak "Error: ${ClassName}->SeAtomIdentifierType: Can't change intial atom identifier type: It's already set..."; 146 } 147 148 $This->{AtomIdentifierType} = $IdentifierType; 149 150 # Initialize atom identifier type information... 151 $This->_InitializeAtomIdentifierTypeInformation(); 152 153 return $This; 154 } 155 156 # Generate fingerprints description... 157 # 158 sub GetDescription { 159 my($This) = @_; 160 161 # Is description explicity set? 162 if (exists $This->{Description}) { 163 return $This->{Description}; 164 } 165 166 # Generate fingerprints description... 167 168 return "$This->{Type}:$This->{AtomIdentifierType}"; 169 } 170 171 # Generate topological atom torsions [ Ref 58, Ref 72 ] fingerprints... 172 # 173 # Methodology: 174 # . Assign atom types to all the atoms. 175 # . Generate and count atom torsions. 176 # 177 # Notes: 178 # . Hydrogen atoms are ignored during the fingerprint generation. 179 # 180 sub GenerateFingerprints { 181 my($This) = @_; 182 183 # Cache appropriate molecule data... 184 $This->_SetupMoleculeDataCache(); 185 186 # Assign atom types to all heavy atoms... 187 if (!$This->_AssignAtomTypes()) { 188 carp "Warning: ${ClassName}->GenerateFingerprints: $This->{AtomIdentifierType} fingerprints generation didn't succeed: Couldn't assign valid $This->{AtomIdentifierType} to all atoms..."; 189 return $This; 190 } 191 192 # Count atom torsions... 193 $This->_GenerateAndCountAtomTorsions(); 194 195 # Set final fingerprints... 196 $This->_SetFinalFingerprints(); 197 198 # Clear cached molecule data... 199 $This->_ClearMoleculeDataCache(); 200 201 return $This; 202 } 203 204 # Assign appropriate atom types to all heavy atoms... 205 # 206 sub _AssignAtomTypes { 207 my($This) = @_; 208 my($SpecifiedAtomTypes, $Atom, $AtomID, $IgnoreHydrogens); 209 210 %{$This->{AssignedAtomTypes}} = (); 211 $IgnoreHydrogens = 1; 212 213 $SpecifiedAtomTypes = undef; 214 215 IDENTIFIERTYPE: { 216 if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) { 217 $SpecifiedAtomTypes = new AtomTypes::AtomicInvariantsAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens, 'AtomicInvariantsToUse' => $This->{AtomicInvariantsToUse}); 218 last IDENTIFIERTYPE; 219 } 220 221 if ($This->{AtomIdentifierType} =~ /^DREIDINGAtomTypes$/i) { 222 $SpecifiedAtomTypes = new AtomTypes::DREIDINGAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens); 223 last IDENTIFIERTYPE; 224 } 225 226 if ($This->{AtomIdentifierType} =~ /^EStateAtomTypes$/i) { 227 $SpecifiedAtomTypes = new AtomTypes::EStateAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens); 228 last IDENTIFIERTYPE; 229 } 230 231 if ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) { 232 $SpecifiedAtomTypes = new AtomTypes::FunctionalClassAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens, 'FunctionalClassesToUse' => $This->{FunctionalClassesToUse}); 233 last IDENTIFIERTYPE; 234 } 235 236 if ($This->{AtomIdentifierType} =~ /^MMFF94AtomTypes$/i) { 237 $SpecifiedAtomTypes = new AtomTypes::MMFF94AtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens); 238 last IDENTIFIERTYPE; 239 } 240 241 if ($This->{AtomIdentifierType} =~ /^SLogPAtomTypes$/i) { 242 $SpecifiedAtomTypes = new AtomTypes::SLogPAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens); 243 last IDENTIFIERTYPE; 244 } 245 if ($This->{AtomIdentifierType} =~ /^SYBYLAtomTypes$/i) { 246 $SpecifiedAtomTypes = new AtomTypes::SYBYLAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens); 247 last IDENTIFIERTYPE; 248 } 249 250 if ($This->{AtomIdentifierType} =~ /^TPSAAtomTypes$/i) { 251 $SpecifiedAtomTypes = new AtomTypes::TPSAAtomTypes('Molecule' => $This->{Molecule}, 'IgnorePhosphorus' => 0, 'IgnoreSulfur' => 0); 252 last IDENTIFIERTYPE; 253 } 254 255 if ($This->{AtomIdentifierType} =~ /^UFFAtomTypes$/i) { 256 $SpecifiedAtomTypes = new AtomTypes::UFFAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens); 257 last IDENTIFIERTYPE; 258 } 259 260 croak "Error: ${ClassName}->_AssignAtomTypes: Unknown atom indentifier type $This->{AtomIdentifierType}..."; 261 } 262 263 # Assign atom types... 264 $SpecifiedAtomTypes->AssignAtomTypes(); 265 266 # Make sure atom types assignment is successful... 267 if (!$SpecifiedAtomTypes->IsAtomTypesAssignmentSuccessful()) { 268 return undef; 269 } 270 271 # Collect assigned atom types... 272 ATOM: for $Atom (@{$This->{Atoms}}) { 273 if ($Atom->IsHydrogen()) { 274 next ATOM; 275 } 276 $AtomID = $Atom->GetID(); 277 $This->{AssignedAtomTypes}{$AtomID} = $SpecifiedAtomTypes->GetAtomType($Atom); 278 } 279 280 return $This; 281 } 282 283 # Count atom torsions involving non-hydrogen atoms by going over the structurally 284 # unique atom torsions... 285 # 286 sub _GenerateAndCountAtomTorsions { 287 my($This) = @_; 288 my($Atom1, $Atom2, $Atom3, $Atom4, $AtomID1, $AtomID2, $AtomID3, $AtomID4, $AtomTorsionID, @Atom1Neighbors, @Atom2Neighbors, @Atom3Neighbors); 289 290 # Setup a hash to track structurally unique atom torsions by atom IDs... 291 %{$This->{StructurallyUniqueAtomTorsions}} = (); 292 293 ATOM1: for $Atom1 (@{$This->{Atoms}}) { 294 if ($Atom1->IsHydrogen()) { 295 next ATOM1; 296 } 297 $AtomID1 = $Atom1->GetID(); 298 # Go over Atom1 neighbors other than Atom1... 299 @Atom1Neighbors = $Atom1->GetNeighbors($Atom1); 300 ATOM2: for $Atom2 (@Atom1Neighbors) { 301 if ($Atom2->IsHydrogen()) { 302 next ATOM2; 303 } 304 $AtomID2 = $Atom2->GetID(); 305 # Go over Atom2 neighbors other than Atom1 and Atom2... 306 @Atom2Neighbors = $Atom2->GetNeighbors($Atom1, $Atom2); 307 ATOM3: for $Atom3 (@Atom2Neighbors) { 308 if ($Atom3->IsHydrogen()) { 309 next ATOM3; 310 } 311 $AtomID3 = $Atom3->GetID(); 312 @Atom3Neighbors = $Atom3->GetNeighbors($Atom1, $Atom2, $Atom3); 313 # Go over Atom3 neighbors other than Atom1, Atom2 and Atom3... 314 ATOM4: for $Atom4 (@Atom3Neighbors) { 315 if ($Atom4->IsHydrogen()) { 316 next ATOM4; 317 } 318 $AtomID4 = $Atom4->GetID(); 319 320 # Is it a structurally unique torsion? 321 if (!$This->_IsStructurallyUniqueTorsion($AtomID1, $AtomID2, $AtomID3, $AtomID4)) { 322 next ATOM4; 323 } 324 325 # Track structurally unique torsions... 326 $AtomTorsionID = $This->_GetAtomTorsionID($AtomID1, $AtomID2, $AtomID3, $AtomID4); 327 if (exists $This->{AtomTorsionsCount}{$AtomTorsionID}) { 328 $This->{AtomTorsionsCount}{$AtomTorsionID} += 1; 329 } 330 else { 331 $This->{AtomTorsionsCount}{$AtomTorsionID} = 1; 332 } 333 } 334 } 335 } 336 } 337 338 return $This; 339 } 340 341 # Is it a structurally unique torsions? 342 # 343 # Notes: 344 # . For a torsion to be structurally unique which hasn't already been encountered, 345 # all the four atoms involved in the torsion must be new atoms. And this can be 346 # simply implemented by tracking the torsions using atom IDs and maintaining a 347 # hash of already encountered torsions using lexicographically smaller torsion ID 348 # consisting of four atom IDs. 349 # 350 sub _IsStructurallyUniqueTorsion { 351 my($This, @AtomIDs) = @_; 352 my($TorsionID, $ReverseTorsionID); 353 354 $TorsionID = join "-", @AtomIDs; 355 $ReverseTorsionID = join "-", reverse @AtomIDs; 356 357 # Use lexicographically smaller string... 358 if ($ReverseTorsionID lt $TorsionID) { 359 $TorsionID = $ReverseTorsionID; 360 } 361 362 if (exists $This->{StructurallyUniqueAtomTorsions}{$TorsionID}) { 363 return 0; 364 } 365 366 # Keep track... 367 $This->{StructurallyUniqueAtomTorsions}{$TorsionID} = 1; 368 369 return 1; 370 } 371 372 # Get atom torsion ID corresponding to atom types involved in torsion... 373 # 374 # Notes: 375 # . TorsionID corresponds to assigned atom types of all the four torsion atoms 376 # concatenated by hyphen. 377 # . TorsionIDs are generated for both forward and backward sequence of atoms 378 # in the torsion and keeping the lexicographically smaller TorsionID to keep TorsionID 379 # independent of atom ordering. 380 # 381 sub _GetAtomTorsionID { 382 my($This, @AtomIDs) = @_; 383 my($AtomTorsionID, $ReverseAtomTorsionID, @AtomTypes); 384 385 @AtomTypes = (); 386 @AtomTypes = map { $This->{AssignedAtomTypes}{$_} } @AtomIDs; 387 388 $AtomTorsionID = join "-", @AtomTypes; 389 $ReverseAtomTorsionID = join "-", reverse @AtomTypes; 390 391 # Use lexicographically smaller string as ID... 392 return ($ReverseAtomTorsionID lt $AtomTorsionID) ? $ReverseAtomTorsionID : $AtomTorsionID; 393 } 394 395 # Set final fingerpritns vector... 396 # 397 sub _SetFinalFingerprints { 398 my($This) = @_; 399 my($AtomTorsionID, $Value, @Values); 400 401 # Mark successful generation of fingerprints... 402 $This->{FingerprintsGenerated} = 1; 403 404 @Values = (); 405 @{$This->{AtomTorsionsIDs}} = (); 406 407 for $AtomTorsionID (sort keys %{$This->{AtomTorsionsCount}}) { 408 $Value = $This->{AtomTorsionsCount}{$AtomTorsionID}; 409 push @{$This->{AtomTorsionsIDs}}, $AtomTorsionID; 410 push @Values, $Value; 411 } 412 413 # Add AtomPairsIDs and values to fingerprint vector... 414 $This->{FingerprintsVector}->AddValueIDs(\@{$This->{AtomTorsionsIDs}}); 415 $This->{FingerprintsVector}->AddValues(\@Values); 416 417 return $This; 418 } 419 420 # Get atom torsions IDs corresponding to atom torsions count values in fingerprint 421 # vector as an array or reference to an array... 422 # 423 # AtomTorsionsIDs list differes in molecules and is generated during finalization 424 # of fingerprints to make sure the fingerprint vector containing count values 425 # matches the atom torsions array. 426 # 427 sub GetAtomTorsionsIDs { 428 my($This) = @_; 429 430 return wantarray ? @{$This->{AtomTorsionsIDs}} : \@{$This->{AtomTorsionsIDs}}; 431 } 432 433 # Cache appropriate molecule data... 434 # 435 sub _SetupMoleculeDataCache { 436 my($This) = @_; 437 438 # Get all atoms including hydrogens. The hydrogen atoms are ignored during processing... 439 @{$This->{Atoms}} = $This->GetMolecule()->GetAtoms(); 440 441 return $This; 442 } 443 444 # Clear cached molecule data... 445 # 446 sub _ClearMoleculeDataCache { 447 my($This) = @_; 448 449 @{$This->{Atoms}} = (); 450 451 return $This; 452 } 453 454 # Set atomic invariants to use for atom identifiers... 455 # 456 sub SetAtomicInvariantsToUse { 457 my($This, @Values) = @_; 458 my($FirstValue, $TypeOfFirstValue, $AtomicInvariant, $SpecifiedAtomicInvariant, $AtomicInvariantValue, @SpecifiedAtomicInvariants, @AtomicInvariantsToUse); 459 460 if (!@Values) { 461 carp "Warning: ${ClassName}->SetAtomicInvariantsToUse: No values specified..."; 462 return; 463 } 464 465 $FirstValue = $Values[0]; 466 $TypeOfFirstValue = ref $FirstValue; 467 468 @SpecifiedAtomicInvariants = (); 469 @AtomicInvariantsToUse = (); 470 471 if ($TypeOfFirstValue =~ /^ARRAY/) { 472 push @SpecifiedAtomicInvariants, @{$FirstValue}; 473 } 474 else { 475 push @SpecifiedAtomicInvariants, @Values; 476 } 477 478 # Make sure specified AtomicInvariants are valid... 479 for $SpecifiedAtomicInvariant (@SpecifiedAtomicInvariants) { 480 if (!AtomTypes::AtomicInvariantsAtomTypes::IsAtomicInvariantAvailable($SpecifiedAtomicInvariant)) { 481 croak "Error: ${ClassName}->SetAtomicInvariantsToUse: Specified atomic invariant, $SpecifiedAtomicInvariant, is not supported...\n "; 482 } 483 $AtomicInvariant = $SpecifiedAtomicInvariant; 484 push @AtomicInvariantsToUse, $AtomicInvariant; 485 } 486 487 # Set atomic invariants to use... 488 @{$This->{AtomicInvariantsToUse}} = (); 489 push @{$This->{AtomicInvariantsToUse}}, @AtomicInvariantsToUse; 490 491 return $This; 492 } 493 494 # Set functional classes to use for atom identifiers... 495 # 496 sub SetFunctionalClassesToUse { 497 my($This, @Values) = @_; 498 my($FirstValue, $TypeOfFirstValue, $FunctionalClass, $SpecifiedFunctionalClass, @SpecifiedFunctionalClasses, @FunctionalClassesToUse); 499 500 if (!@Values) { 501 carp "Warning: ${ClassName}->SetFunctionalClassesToUse: No values specified..."; 502 return; 503 } 504 505 if ($This->{AtomIdentifierType} !~ /^FunctionalClassAtomTypes$/i) { 506 carp "Warning: ${ClassName}->SetFunctionalClassesToUse: FunctionalClassesToUse can't be set for InitialAtomIdentifierType of $This->{AtomIdentifierType}..."; 507 return; 508 } 509 510 $FirstValue = $Values[0]; 511 $TypeOfFirstValue = ref $FirstValue; 512 513 @SpecifiedFunctionalClasses = (); 514 @FunctionalClassesToUse = (); 515 516 if ($TypeOfFirstValue =~ /^ARRAY/) { 517 push @SpecifiedFunctionalClasses, @{$FirstValue}; 518 } 519 else { 520 push @SpecifiedFunctionalClasses, @Values; 521 } 522 523 # Make sure specified FunctionalClasses are valid... 524 for $SpecifiedFunctionalClass (@SpecifiedFunctionalClasses) { 525 if (!AtomTypes::FunctionalClassAtomTypes::IsFunctionalClassAvailable($SpecifiedFunctionalClass)) { 526 croak "Error: ${ClassName}->SetFunctionalClassesToUse: Specified functional class, $SpecifiedFunctionalClass, is not supported...\n "; 527 } 528 push @FunctionalClassesToUse, $SpecifiedFunctionalClass; 529 } 530 531 # Set functional classes to use... 532 @{$This->{FunctionalClassesToUse}} = (); 533 push @{$This->{FunctionalClassesToUse}}, @FunctionalClassesToUse; 534 535 return $This; 536 } 537 538 # Initialize atom indentifier type information... 539 # 540 # Current supported values: 541 # 542 # AtomicInvariantsAtomTypes, DREIDINGAtomTypes, EStateAtomTypes, FunctionalClassAtomTypes, 543 # MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes 544 # 545 sub _InitializeAtomIdentifierTypeInformation { 546 my($This) = @_; 547 548 if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) { 549 $This->_InitializeAtomicInvariantsAtomTypesInformation(); 550 } 551 elsif ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) { 552 $This->_InitializeFunctionalClassAtomTypesInformation(); 553 } 554 elsif ($This->{AtomIdentifierType} =~ /^(DREIDINGAtomTypes|EStateAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) { 555 # Nothing to do for now... 556 } 557 else { 558 croak "Error: ${ClassName}->_InitializeAtomIdentifierTypeInformation: Unknown atom indentifier type $This->{AtomIdentifierType}..."; 559 } 560 561 return $This; 562 } 563 564 # Initialize atomic invariants to use for generating atom IDs in atom torsions... 565 # 566 # Let: 567 # AS = Atom symbol corresponding to element symbol 568 # 569 # X<n> = Number of non-hydrogen atom neighbors or heavy atoms attached to atom 570 # BO<n> = Sum of bond orders to non-hydrogen atom neighbors or heavy atoms attached to atom 571 # LBO<n> = Largest bond order of non-hydrogen atom neighbors or heavy atoms attached to atom 572 # SB<n> = Number of single bonds to non-hydrogen atom neighbors or heavy atoms attached to atom 573 # DB<n> = Number of double bonds to non-hydrogen atom neighbors or heavy atoms attached to atom 574 # TB<n> = Number of triple bonds to non-hydrogen atom neighbors or heavy atoms attached to atom 575 # H<n> = Number of implicit and explicit hydrogens for atom 576 # Ar = Aromatic annotation indicating whether atom is aromatic 577 # RA = Ring atom annotation indicating whether atom is a ring 578 # FC<+n/-n> = Formal charge assigned to atom 579 # MN<n> = Mass number indicating isotope other than most abundant isotope 580 # SM<n> = Spin multiplicity of atom. Possible values: 1 (singlet), 2 (doublet) or 3 (triplet) 581 # 582 # AtomTypeIDx = Atomic invariants atom type for atom x 583 # AtomTypeIDy = Atomic invariants atom type for atom y 584 # AtomTypeIDz = Atomic invariants atom type for atom z 585 # AtomTypeIDw = Atomic invariants atom type for atom w 586 # 587 # Then: 588 # 589 # Atom torsion AtomID generated by AtomTypes::AtomicInvariantsAtomTypes class corresponds to: 590 # 591 # AS.X<n>.BO<n>.LBO<n>.<SB><n>.<DB><n>.<TB><n>.H<n>.Ar.RA.FC<+n/-n>.MN<n>.SM<n> 592 # 593 # AtomTorsion ID corresponds to: 594 # 595 # AtomTypeIDx-AtomTypeIDy-AtomTypeIDz-AtomTypeIDw 596 # 597 # Except for AS which is a required atomic invariant in atom torsions AtomIDs, all other atomic invariants are 598 # optional. Default atomic invariants used for AtomID are: AS, X<n>, BO<n>, H<n>, FC<+n/-n>. 599 # AtomID specification doesn't include atomic invariants with zero or undefined values. 600 # 601 # Examples of atom torsion AtomIDs in Aspirin using default atomic invariants: 602 # 603 # C.X1.BO1.H3-C.X3.BO4-O.X2.BO2-C.X3.BO4 604 # C.X2.BO3.H1-C.X2.BO3.H1-C.X2.BO3.H1-C.X2.BO3.H1 605 # C.X3.BO4-C.X3.BO4-O.X2.BO2-C.X3.BO4 606 # C.X3.BO4-O.X2.BO2-C.X3.BO4-O.X1.BO2 607 # 608 # Examples of atom torsion AtomIDs in Aspirin using AS, X and BO atomic invariants: 609 # 610 # C.X1.BO1-C.X3.BO4-O.X2.BO2-C.X3.BO4 611 # C.X2.BO3-C.X2.BO3-C.X2.BO3-C.X2.BO3 612 # C.X3.BO4-C.X3.BO4-O.X2.BO2-C.X3.BO4 613 # C.X3.BO4-O.X2.BO2-C.X3.BO4-O.X1.BO2 614 # 615 sub _InitializeAtomicInvariantsAtomTypesInformation { 616 my($This) = @_; 617 618 # Default atomic invariants to use for generating atom torsions atom IDs: AS, X, BO, H, FC 619 # 620 @{$This->{AtomicInvariantsToUse}} = (); 621 @{$This->{AtomicInvariantsToUse}} = ('AS', 'X', 'BO', 'H', 'FC'); 622 623 return $This; 624 } 625 626 # Initialize functional class atom types, generated by AtomTypes::FunctionalClassAtomTypes 627 # class, to use for generating atom identifiers... 628 # 629 # Let: 630 # HBD: HydrogenBondDonor 631 # HBA: HydrogenBondAcceptor 632 # PI : PositivelyIonizable 633 # NI : NegativelyIonizable 634 # Ar : Aromatic 635 # Hal : Halogen 636 # H : Hydrophobic 637 # RA : RingAtom 638 # CA : ChainAtom 639 # 640 # Then: 641 # 642 # Functiononal class atom type specification for an atom corresponds to: 643 # 644 # Ar.CA.H.HBA.HBD.Hal.NI.PI.RA 645 # 646 # Default functional classes used are: HBD, HBA, PI, NI, Ar, Hal 647 # 648 # FunctionalAtomTypes are assigned using the following definitions [ Ref 60-61, Ref 65-66 ]: 649 # 650 # HydrogenBondDonor: NH, NH2, OH 651 # HydrogenBondAcceptor: N[!H], O 652 # PositivelyIonizable: +, NH2 653 # NegativelyIonizable: -, C(=O)OH, S(=O)OH, P(=O)OH 654 # 655 sub _InitializeFunctionalClassAtomTypesInformation { 656 my($This) = @_; 657 658 # Default functional class atom typess to use for generating atom identifiers 659 # are: HBD, HBA, PI, NI, Ar, Hal 660 # 661 @{$This->{FunctionalClassesToUse}} = (); 662 @{$This->{FunctionalClassesToUse}} = ('HBD', 'HBA', 'PI', 'NI', 'Ar', 'Hal'); 663 664 return $This; 665 } 666 667 # Return a string containg data for TopologicalAtomTorsionsFingerprints object... 668 # 669 sub StringifyTopologicalAtomTorsionsFingerprints { 670 my($This) = @_; 671 my($FingerprintsString); 672 673 # Type of fingerprint... 674 $FingerprintsString = "Fingerprint type: $This->{Type}; AtomIdentifierType: $This->{AtomIdentifierType}"; 675 676 if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) { 677 my($AtomicInvariant, @AtomicInvariants, @AtomicInvariantsOrder, %AvailableAtomicInvariants); 678 679 @AtomicInvariantsOrder = AtomTypes::AtomicInvariantsAtomTypes::GetAtomicInvariantsOrder(); 680 %AvailableAtomicInvariants = AtomTypes::AtomicInvariantsAtomTypes::GetAvailableAtomicInvariants(); 681 682 for $AtomicInvariant (@AtomicInvariantsOrder) { 683 push @AtomicInvariants, "$AtomicInvariant: $AvailableAtomicInvariants{$AtomicInvariant}"; 684 } 685 686 $FingerprintsString .= "; AtomicInvariantsToUse: <" . TextUtil::JoinWords(\@{$This->{AtomicInvariantsToUse}}, ", ", 0) . ">"; 687 $FingerprintsString .= "; AtomicInvariantsOrder: <" . TextUtil::JoinWords(\@AtomicInvariantsOrder, ", ", 0) . ">"; 688 $FingerprintsString .= "; AvailableAtomicInvariants: <" . TextUtil::JoinWords(\@AtomicInvariants, ", ", 0) . ">"; 689 } 690 elsif ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) { 691 my($FunctionalClass, @FunctionalClasses, @FunctionalClassesOrder, %AvailableFunctionalClasses); 692 693 @FunctionalClassesOrder = AtomTypes::FunctionalClassAtomTypes::GetFunctionalClassesOrder(); 694 %AvailableFunctionalClasses = AtomTypes::FunctionalClassAtomTypes::GetAvailableFunctionalClasses(); 695 696 for $FunctionalClass (@FunctionalClassesOrder) { 697 push @FunctionalClasses, "$FunctionalClass: $AvailableFunctionalClasses{$FunctionalClass}"; 698 } 699 700 $FingerprintsString .= "; FunctionalClassesToUse: <" . TextUtil::JoinWords(\@{$This->{FunctionalClassesToUse}}, ", ", 0) . ">"; 701 $FingerprintsString .= "; FunctionalClassesOrder: <" . TextUtil::JoinWords(\@FunctionalClassesOrder, ", ", 0) . ">"; 702 $FingerprintsString .= "; AvailableFunctionalClasses: <" . TextUtil::JoinWords(\@FunctionalClasses, ", ", 0) . ">"; 703 } 704 705 # Total number of atom torsions... 706 $FingerprintsString .= "; NumOfAtomTorsions: " . $This->{FingerprintsVector}->GetNumOfValues(); 707 708 # FingerprintsVector... 709 $FingerprintsString .= "; FingerprintsVector: < $This->{FingerprintsVector} >"; 710 711 return $FingerprintsString; 712 } 713