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