1# Copyright 2017 Jacob D. Durrant
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from __future__ import absolute_import
16from scoria import dumbpy as numpy
17from .FileIO import FileIO
18from .AtomsAndBonds import AtomsAndBonds
19from .Selections import Selections
20from .Manipulation import Manipulation
21from .Information import Information
22from .OtherMolecules import OtherMolecules
23from .Geometry import Geometry
24import copy
25
26
27class Molecule: # here's the actual Molecule class
28    """
29    Loads, saves, and manupulates molecuar models. The main scoria
30    class. Contains Wrapper functions for subclasses.
31
32    Examples assume::
33
34        >>> import scoria
35        >>> PDB = "./test_file.pdb"
36        >>> mol = scoria.Molecule()
37        >>> mol.load_pdb_into(PDB)
38    """
39
40    def __init__(self, *args):
41        """
42        Initializes the variables of the Molecule class.
43
44        :param files args: Additional files added to the end of the paramter
45            will be input with the method indicated by the fileType parameter.
46        """
47
48        self.fileio = FileIO(self)
49        self.atoms_and_bonds = AtomsAndBonds(self)
50        self.selections = Selections(self)
51        self.manipulation = Manipulation(self)
52        self.information = Information(self)
53        self.other_molecules = OtherMolecules(self)
54        self.geometry = Geometry(self)
55
56        # Based on the file type, we will attempt to open the file.
57        if len(args) > 0:
58            if len(args) == 1:
59                file = args[0]
60                file_type = file.split('.')[-1].upper()
61
62                if file_type == 'PDB':
63                    self.load_pdb_trajectory_into(file)
64                elif file_type == 'PDBQT':
65                    self.load_pdbqt_trajectory_into(file)
66                elif file_type == 'PYM':
67                    self.load_pym_into(file)
68
69    # Information methods
70    ### Wrappers ###
71    # Gets
72    def get_coordinates(self, frame = None):
73        """
74        Returns the set of coordinates from the specified frame.
75
76        Wrapper function for :meth:`~scoria.Information.Information.get_coordinates`
77
78        :param int frame: The timestep from which the coordinates shoule be
79                        returned. If ommitted, it defaults to the first
80                        frame of the trajectory.
81
82        :returns: The set of coordinates from the specified frame.
83                    ::
84
85                    [[x1, y1, z1], ... [xn, yn, zn]]
86
87        :rtype: :any:`numpy.array`
88
89        ::
90
91            >>> print(mol.get_coordinates())
92            [[ -30.85199928  -81.45800018  365.05499268]
93             [ -31.99500084  -80.69300079  365.66900635]
94             [ -32.0530014   -81.13200378  367.18200684]
95             ...,
96             [ -27.54199982  -96.25099945  402.83700562]
97             [ -23.54199982  -94.7539978   400.41900635]
98             [ -22.86100006  -93.72499847  400.55300903]]
99
100            >>> print(mol.get_coordinates(2))
101            [[ -28.88899994  -80.45700073  365.51699829]
102             [ -30.20000076  -79.73699951  365.99700928]
103             [ -30.90699959  -80.5510025   367.13000488]
104             ...,
105             [ -26.0189991   -97.28099823  403.52600098]
106             [ -23.2140007   -94.73999786  400.94699097]
107             [ -22.52899933  -93.73300171  400.81399536]]
108        """
109
110        return self.information.get_coordinates(frame)
111
112    def get_trajectory_coordinates(self):
113        """
114        Returns the trajectory for the molecule.
115
116        Wrapper function for :meth:`~scoria.Information.Information.get_trajectory_coordinates`
117
118        :returns: The set of all coordinates.
119                    ::
120
121                        [[[x11, y11, z11], ... [x1n, y1n, z1n]],
122                         ...,
123                         [[xm1, ym1, zm1], ... [xmn, ymn, zmn]]]
124
125        :rtype: :any:`numpy.array`
126
127        ::
128
129            >>> for coord in mol.get_trajectory_coordinates():
130            >>>     print(coord)
131            >>>     print()
132            [[ -30.85199928  -81.45800018  365.05499268]
133             [ -31.99500084  -80.69300079  365.66900635]
134             [ -32.0530014   -81.13200378  367.18200684]
135             ...,
136             [ -27.54199982  -96.25099945  402.83700562]
137             [ -23.54199982  -94.7539978   400.41900635]
138             [ -22.86100006  -93.72499847  400.55300903]]
139
140            [[ -30.6779995   -81.32499695  365.73199463]
141             [ -31.88100052  -80.38600159  366.0289917 ]
142             [ -32.40399933  -80.62799835  367.45700073]
143             ...,
144             [ -27.44400024  -96.71099854  402.64700317]
145             [ -23.79199982  -94.58899689  400.63598633]
146             [ -23.10700035  -93.56300354  400.79598999]]
147             <more>
148        """
149
150        return self.information.get_trajectory_coordinates()
151
152    def get_filename(self):
153        """
154        Returns the filename that the molecule was originally loaded from.
155
156        Wrapper function for :meth:`~scoria.Information.Information.get_filename`
157
158        :returns: The name of the file.
159
160        :rtype: :any:`str`
161
162        ::
163
164            >>> mol = scoria.Molecule()
165            >>> mol.load_pdb_into("single_frame.pdb")
166            >>> print(mol.get_filename())
167            single_frame.pdb
168        """
169
170        return self.information.get_filename()
171
172    def get_remarks(self):
173        """
174        Returns the remarks from the file the molecule was loaded from.
175
176        Wrapper function for :meth:`~scoria.Information.Information.get_remarks`
177
178        :returns: The remarks from the file an a list of strings.
179
180        :rtype: :any:`list`
181
182        ::
183
184            >>> mol = scoria.Molecule()
185            >>> mol.load_pdb_into("single_frame.pdb")
186            >>> print(mol.get_remarks())
187            [' This is a remark.']
188        """
189
190        return self.information.get_remarks()
191
192    def get_atom_information(self):
193        """
194        Retreives the atomic information for the molecule.
195
196        Wrapper function for :meth:`~scoria.Information.Information.get_atom_information`
197
198        :returns: A masked array containing the atom information.
199
200        :rtype: :any:`numpy.ma.MaskedArray`
201
202        The contents of the array are as follows:
203
204        ================ ===== ================= ==============================
205        member name      dtype Full Type         Description
206        ================ ===== ================= ==============================
207        record_name      S6    six char string   What the atom belongs to
208        serial           <i8   64-bit integer    The index of the atom
209        name             S5    five char string  The atom name
210        resname          S5    five char string  The residue name
211        chainid          S1    one char string   The chain identifier
212        resseq           <i8   64-bit integer    The Residue sequence number
213        occupancy        <f8   64-bit float      Occupancy of atom
214        tempfactor       <f8   64-bit float      Tempature Factor
215        element          S2    two char string   The element symbol
216        charge           S3    three char string Charge on the atom
217        name_stripped    S5    five char string  Atom name without space
218        resname_stripped S5    five char string  Residue name without space
219        chainid_stripped S1    one char string   Chain identifier without space
220        element_stripped S2    two char string   Element symbol without space
221        ================ ===== ================= ==============================
222
223        An example for printing the elemental symbols of the first five atoms::
224
225            >>> atom_info = mol.get_atom_information()
226            >>> print(atom_info['element'][0:5])
227            ['N' 'C' 'C' 'O' 'C']
228        """
229
230        return self.information.get_atom_information()
231
232    def get_coordinates_undo_point(self):
233        """
234        NEEDS CLARIFICATION.
235        Retreives a previously save set of coordinates to revert to.
236
237        Wrapper function for :meth:`~scoria.Information.Information.get_coordinates_undo_point`
238
239        :returns: A set of coordinates from which to return to.
240
241        :rtype: :any:`numpy.array` or :any:`None`
242        """
243
244        return self.information.get_coordinates_undo_point()
245
246    def get_bonds(self):
247        """
248        Retreives the bonds beteween atoms as a n x n matrix.
249
250        Wrapper function for :meth:`~scoria.Information.Information.get_bonds`
251
252        :returns: A binary n x n matrix, where bonds are represented by 1.
253
254        :rtype: :any:`numpy.array`
255
256        An example for finding all atoms bonded with atom 153::
257
258            >>> bonds = mol.get_bonds()
259            >>> for i in range(0,len(bonds)):
260            ...     if bonds[153][i] == 1:
261            ...             print(153,"-",i)
262            153 - 152
263            153 - 154
264            153 - 155
265        """
266
267        return self.information.get_bonds()
268
269    def get_hierarchy(self):
270        """
271        NEEDS CLARIFICATION.
272
273        Wrapper function for :meth:`~scoria.Information.Information.get_hierarchy`
274
275        :returns: A dictionary?
276
277        :rtype: :any:`dict`
278        """
279
280        return self.information.get_hierarchy()
281
282    def get_constants(self):
283        """
284        Returns a dictionary containing the constants assumed for the molecular model.
285
286        Wrapper function for :meth:`~scoria.Information.Information.get_constants`
287
288        :returns: The constants assumed by the model.
289
290        :rtype: :any:`dict`
291
292        ============================== =============== ===============================
293        Dictionary Keys                Value Type      Contains
294        ============================== =============== ===============================
295        mass_dict                      dict{str:float} The mass of elements
296        rna_residues                   list(str)       RNA residue names
297        f8_fields                      list(str)       Atom Information floats
298        vdw_dict                       dict{str:float} Van der Waals force of elements
299        i8_fields                      list(str)       Atom Information integers
300        protein_residues               list(str)       Protein residue names
301        bond_length_dict               dict{str:float} Element-pair bond length
302        element_names_with_two_letters list(str)       Element symbols with 2 letters
303        max_number_of_bonds_permitted  dict{str:int}   Max bonds per element
304        dna_residues                   list(str)       DNA reside names
305        ============================== =============== ===============================
306
307        """
308
309        return self.information.get_constants()
310
311    def get_center_of_mass(self, selection = None, frame = None):
312        """
313        Determines the center of mass.
314
315        Wrapper function for :meth:`~scoria.Information.Information.get_center_of_mass`
316
317        :param numpy.array selection: The indices of
318                          the atoms to consider when calculating the center of mass.
319                          If ommitted, all atoms of the scoria.Molecule object
320                          will be considered.
321
322        :param int frame: The timestep at which the center of mass
323                      should be calculated. If ommitted, it defaults to the first
324                      frame of the trajectory.
325
326        :returns: The x, y, and z coordinates of the center of mass.
327
328        :rtype: :any:`numpy.ma.MaskedArray`
329
330        ::
331
332            >>> mol = scoria.Molecule()
333            >>> mol.load_pdb_into("single_frame.pdb")
334            >>> print(mol.get_center_of_mass())
335            [33.0643089093134 19.135747088722564 16.05629867850796]
336        """
337
338        return self.information.get_center_of_mass(selection, frame)
339
340    def get_geometric_center(self, selection = None, frame = None):
341        """
342        Determines the geometric center of the molecule.
343
344        Wrapper function for :meth:`~scoria.Information.Information.get_geometric_center`
345
346        :param numpy.array selection: The indices of
347                          the atoms to consider when calculating the geometric.
348                          If ommitted, all atoms of the scoria.Molecule object
349                          will be considered.
350
351        :param int frame: The timestep at which the geometric center
352                      should be calculated. If ommitted, it defaults to the first
353                      frame of the trajectory.
354
355        :returns: The x, y, and z coordinates of the geometric center.
356
357        :rtype: :any:`numpy.array`
358
359        ::
360
361            >>> mol = scoria.Molecule()
362            >>> mol.load_pdb_into("single_frame.pdb")
363            >>> print(mol.get_geometric_center())
364            [ 33.09860848  19.1221197   16.0426808 ]
365        """
366
367        return self.information.get_geometric_center(selection, frame)
368
369    def get_total_mass(self, selection = None):
370        """
371        Returns the total mass of all atoms within the molecule, or of a given
372        selection.
373
374        Wrapper function for :meth:`~scoria.Information.Information.get_total_mass`
375
376        :param numpy.array selection: The indices of
377                        the atoms to consider when calculating the geometric.
378                        If ommitted, all atoms of the scoria.Molecule object
379                        will be considered.
380
381        :returns: The total mass of the atom or selection
382
383        :rtype: :any:`float`
384
385        ::
386
387            >>> print(mol.get_total_mass())
388            5289.1729999999998
389
390        """
391
392        return self.information.get_total_mass(selection)
393
394    def get_total_number_of_atoms(self, selection = None):
395        """
396        Counts the number of atoms.
397
398        Wrapper function for
399        :meth:`~scoria.Information.Information.get_total_number_of_atoms`
400
401        :param numpy.array selection: An optional numpy.array containing the indices of
402                    the atoms to count. If ommitted, all atoms of the
403                    scoria.Molecule object will be considered.
404        :param int frame: An integer indicating at which timestep the center of
405                    mass should be calculated. If ommitted, it defaults to the
406                    first frame of the trajectory.
407
408        :returns:  The total number of atoms.
409        :rtype: :any:`int`
410        """
411
412        return self.information.get_total_number_of_atoms(selection)
413
414    def get_total_number_of_heavy_atoms(self, selection = None):
415        """
416        Counts the number of heavy atoms (i.e., atoms that are not
417        hydrogens).
418
419        Wrapper function for
420        :meth:`~scoria.Information.Information.get_total_number_of_heavy_atoms`
421
422        :param numpy.array selection: An optional numpy.array containing the indices of
423                    the atoms to count. If ommitted, all atoms of the
424                    scoria.Molecule object will be considered.
425
426        :returns: The total number of heavy (non-hydrogen) atoms.
427        :rtype: :any:`int`
428        """
429
430        return self.information.get_total_number_of_heavy_atoms(selection)
431
432    def get_bounding_box(self, selection = None, padding = 0.0, frame = None):
433        """
434        Calculates a box that bounds (encompasses) a set of atoms.
435
436        Wrapper function for :meth:`~scoria.Information.Information.get_bounding_box`
437
438        :param numpy.array selection: An optional numpy.array containing the indices of
439                    the atoms to consider. If ommitted, all atoms of the
440                    scoria.Molecule object will be considered.
441        :param float padding: An optional float. The bounding box will extend this
442                    many angstroms beyond the atoms being considered.
443        :param int frame: An integer indicating at which timestep the center of
444                    mass should be calculated. If ommitted, it defaults to the
445                    first frame of the trajectory.
446
447        :returns: A numpy array representing two 3D points, (min_x, min_y, min_z)
448                    and (max_x, max_y, max_z), that bound the molecule.
449        :rtype: :any:`numpy.array`
450        """
451
452        return self.information.get_bounding_box(selection, padding, frame)
453
454    def get_bounding_sphere(self, selection = None, padding = 0.0, frame = None):
455        """
456        Calculates a sphere that bounds (encompasses) a set of atoms.
457
458        Requires the :any:`numpy` and :any:`scipy<scipy.spatial>` libraries.
459
460        Wrapper function for :meth:`~scoria.Information.Information.get_bounding_sphere`
461
462        :param numpy.array selection: An optional numpy.array containing the indices of
463                    the atoms to consider. If ommitted, all atoms of the
464                    scoria.Molecule object will be considered.
465        :param float padding: An optional float. The bounding sphere will extend
466                    this many angstroms beyond the atoms being considered.
467        :param int frame: An integer indicating at which timestep the center of
468                    mass should be calculated. If ommitted, it defaults to the
469                    first frame of the trajectory.
470
471        :returns: A tuple containing two elements. The first is a numpy.array
472                    representing a 3D point, the (x, y, z) center of the
473                    sphere. The second is a float, the radius of the sphere.
474        :rtype: :any:`tuple` (:any:`numpy.array`, :any:`float`)
475        """
476
477        return self.information.get_bounding_sphere(selection, padding, frame)
478
479    def get_default_trajectory_frame(self):
480        """
481        Retreives the default trajectory frame index.
482
483        Wrapper function for :meth:`~scoria.Information.Information.get_default_trajectory_frame`
484
485        :returns: An *int* representing the index of the default trajectory frame.
486        """
487
488        return self.information.get_default_trajectory_frame()
489
490
491    # Set
492    def set_filename(self, filename):
493        """
494        Sets the __filename variable. Note: this does not reload or modify the
495        molecule in anyway.
496
497        Wrapper function for :meth:`~scoria.Information.Information.set_filename`
498
499        :param str filename: String representation of the filename.
500        """
501
502        self.information.set_filename(filename)
503
504    def set_remarks(self, remarks):
505        """
506        Sets the __remarks variable.
507
508        Wrapper function for :meth:`~scoria.Information.Information.set_remarks`
509
510        :param list(str) remarks: List containing remarks.
511        """
512
513        self.information.set_remarks(remarks)
514
515    def set_atom_information(self, atom_information):
516        """
517        Sets the __atom_information variable. See
518        :meth:`~scoria.Molecule.Molecule.get_atom_information` for
519        information on the numpy.array structure.
520
521        Wrapper function for :meth:`~scoria.Information.Information.set_atom_information`
522
523        :param numpy.array atom_information: An array containing details
524                            on the constituent atoms.
525        """
526
527        self.information.set_atom_information(atom_information)
528
529    def set_coordinates(self, coordinates, frame = None):
530        """
531        Sets a specified frame of the __trajectory variable.
532
533        Wrapper function for :meth:`~scoria.Information.Information.set_coordinates`
534
535        :param numpy.array coordinates: An array of atomic coordinates.
536        :param int frame: An integer represeting the frame of the trajectory to be modified
537        """
538
539        self.information.set_coordinates(coordinates, frame)
540
541    def set_trajectory_coordinates(self, trajectory):
542        """
543        Sets the __trajectory variable.
544
545        Wrapper function for :meth:`~scoria.Information.Information.set_trajectory_coordinates`
546
547        :param numpy.array trajectory: An array of atomic coordinates.
548        """
549
550        self.information.set_trajectory_coordinates(trajectory)
551
552    def set_coordinates_undo_point(self, coordinates_undo_point):
553        """
554        Sets the __coordinates_undo_point variable.
555
556        Wrapper function for :meth:`~scoria.Information.Information.set_coordinates_undo_point`
557
558        :param numpy.array coordinates_undo_point: A coordinate set to revert
559            to after modification.
560        """
561
562        self.information.set_coordinates_undo_point(coordinates_undo_point)
563
564    def set_bonds(self, bonds):
565        """
566        Sets the __bonds variable. See
567        :meth:`~scoria.Molecule.Molecule.get_bonds` for additional
568        information.
569
570        Wrapper function for :meth:`~scoria.Information.Information.set_bonds`
571
572        :param numpy.array bonds: A binary n x n matrix containing bonding
573            information.
574        """
575
576        self.information.set_bonds(bonds)
577
578    def set_hierarchy(self, hierarchy):
579        """
580        DEPRECIATED?
581
582        Wrapper function for :meth:`~scoria.Information.Information.set_hierarchy`
583        """
584
585        self.information.set_hierarchy(hierarchy)
586
587    def set_default_trajectory_frame(self, frame):
588        """
589        Set's the default trajectory frame for various calculations.
590
591        Wrapper function for :meth:`~scoria.Information.Information.set_default_trajectory_frame`
592
593        :param int frame: The default frame for coordinate selection.
594        """
595
596        self.information.set_default_trajectory_frame(frame)
597
598    # Information functions
599    def assign_masses(self):
600        """
601        Assigns masses to the atoms of the scoria.Molecule object.
602
603        Wrapper function for :meth:`~scoria.Information.Information.assign_masses`
604
605        ``Note``:
606        This will autopopulate the masses according to their element
607        identification and takes no input.
608        """
609
610        self.information.assign_masses()
611
612    def assign_elements_from_atom_names(self, selection = None):
613        """
614        Determines the elements of all atoms from the atom names. Note that
615        this will overwrite any existing element assignments, including those
616        explicitly specified in loaded files. Note that this doesn't populate
617        elements_stripped.
618
619        Wrapper function for :meth:`~scoria.Information.Information.assign_elements_from_atom_names`
620
621        :param numpy.array selection: An optional numpy.array containing the indices of
622                    the atoms to consider when calculating the center of mass.
623                    If ommitted, all atoms of the scoria.Molecule object
624                    will be considered.
625        """
626
627        self.information.assign_elements_from_atom_names(selection)
628
629    def define_molecule_chain_residue_spherical_boundaries(self):
630        """
631        Identifies spheres that bound (encompass) the entire molecule, the
632        chains, and the residues. This information is stored in
633        scoria.Information.Information.hierarchy.
634
635        Requires the :any:`numpy` and :any:`scipy<scipy.spatial>` libraries.
636
637        Wrapper function for
638        :meth:`~scoria.Information.Information.define_molecule_chain_residue_spherical_boundaries`
639        """
640
641        self.information.define_molecule_chain_residue_spherical_boundaries()
642
643    def serial_reindex(self):
644        """
645        Reindexes the serial field of the atoms in the molecule, starting
646        with 1.
647
648        Wrapper function for :meth:`~scoria.Information.Information.serial_reindex`
649        """
650
651        self.information.serial_reindex()
652
653    def resseq_reindex(self):
654        """
655        Reindexes the resseq field of the atoms in the molecule, starting
656        with 1.
657
658        Wrapper function for :meth:`~scoria.Information.Information.resseq_reindex`
659        """
660
661        self.information.resseq_reindex()
662
663    def belongs_to_protein(self, atom_index):
664        """
665        Checks if the atom is part of a protein. Taken primarily from Amber
666        residue names.
667
668        Wrapper function for :meth:`~scoria.Information.Information.belongs_to_protein`
669
670        :param int atom_index: An int, the index of the atom to consider.
671
672        :returns: A boolean. True if part of protein, False if not.
673        """
674
675        return self.information.belongs_to_protein(atom_index)
676
677    def belongs_to_rna(self, atom_index):
678        """
679        Checks if the atom is part of RNA.
680
681        Wrapper function for :meth:`~scoria.Information.Information.belongs_to_rna`
682
683        :param int atom_index: An int, the index of the atom to consider.
684
685        :returns: A boolean. True if part of rna, False if not.
686
687        """
688
689        return self.information.belongs_to_rna(atom_index)
690
691    def belongs_to_dna(self, atom_index):
692        """
693        Checks if the atom is part of DNA.
694
695        Wrapper function for :meth:`~scoria.Information.Information.belongs_to_dna`
696
697        :param int atom_index: An int, the index of the atom to consider.
698
699        :returns: A boolean. True if part of dna, False if not.
700        """
701
702        return self.information.belongs_to_dna(atom_index)
703
704    def insert_trajectory_frame(self, index, coordinates):
705        """
706        Inserts a new coordinate frame at the end of the trajectory.
707
708        Wrapper function for :meth:`~scoria.Information.Information.insert_trajectory_frame`
709
710        :param numpy.array coordinates: A single frame of coordinates to append.
711        :param int index: The location where the frame should be added.
712        """
713
714        return self.information.insert_trajectory_frame(index, coordinates)
715
716    def delete_trajectory_frame(self, index):
717        """
718        Removes a given frame from the trajectory.
719
720        Wrapper function for :meth:`~scoria.Information.Information.delete_trajectory_frame`
721
722        :param int index: Integer of the frame to remove.
723        """
724
725        return self.information.delete_trajectory_frame(index)
726
727    def get_trajectory_frame_count(self):
728        """
729        Returns the number of frames in __trajectory.
730
731        Wrapper function for :meth:`~scoria.Information.Information.get_trajectory_frame_count`
732
733        :returns: The number of frames in the trajectory.
734        :rtype: :any:`int`
735        """
736
737        return self.information.get_trajectory_frame_count()
738
739    # File I/O class methods
740    def load_pym_into(self, filename):
741        """
742        Loads the molecular data contained in a pym file into the current
743        scoria.Molecule object.
744
745        Requires the :any:`numpy` library.
746
747        Wrapper function for :meth:`~scoria.FileIO.FileIO.load_pym_into`
748
749        :param str filename: A string, the filename of the pym file.
750        """
751
752        self.fileio.load_pym_into(filename)
753
754    def load_pdb_into(self, filename, bonds_by_distance = True,
755                      serial_reindex = True, resseq_reindex = False,
756                      is_trajectory = False):
757        """
758        Loads the molecular data contained in a pdb file into the current
759        scoria.Molecule object.
760
761        Wrapper function for :meth:`~scoria.FileIO.FileIO.load_pdb_into`
762
763        :param str filename: A string, the filename of the pdb file.
764        :param bool bonds_by_distance: An optional boolean, whether or not to
765                    determine atomic bonds based on atom proximity. True by
766                    default.
767        :param bool serial_reindex: An optional boolean, whether or not to
768                    reindex the pdb serial field. True by default.
769        :param bool resseq_reindex: An optional boolean, whether or not to
770                    reindex the pdb resseq field. False by default.
771        :param bool is_trajectory: An optional boolean, whether or not the PDB
772                    is multi-frame.
773        """
774
775        self.fileio.load_pdb_into(
776            filename, bonds_by_distance, serial_reindex,
777            resseq_reindex, is_trajectory
778        )
779
780    def load_pdb_into_using_file_object(self, file_obj,
781                                        bonds_by_distance = True,
782                                        serial_reindex = True,
783                                        resseq_reindex = False,
784                                        is_trajectory = False):
785        """
786        Loads molecular data from a python file object (pdb formatted) into
787        the current scoria.Molecule object. Note that most users will want
788        to use the load_pdb_into() function instead, which is identical except
789        that it accepts a filename string instead of a python file object.
790
791        Requires the :any:`numpy` library.
792
793        Wrapper function for :meth:`~scoria.FileIO.FileIO.load_pdb_into_using_file_object`
794
795        :param file file_obj: A python file object, containing pdb-formatted
796                    data.
797        :param bool bonds_by_distance: An optional boolean, whether or not to
798                    determine atomic bonds based on atom proximity. True by
799                    default.
800        :param bool serial_reindex: An optional boolean, whether or not to
801                    reindex the pdb serial field. True by default.
802        :param bool resseq_reindex: An optional boolean, whether or not to
803                    reindex the pdb resseq field. False by default.
804        :param bool is_trajectory: An optional boolean, whether or not the PDB
805                    is multi-frame.
806        """
807
808        self.fileio.load_pdb_into_using_file_object(
809            file_obj, bonds_by_distance, serial_reindex,
810            resseq_reindex, is_trajectory
811        )
812
813    def load_pdbqt_into(self, filename, bonds_by_distance = False,
814                      serial_reindex = True, resseq_reindex = False,
815                      is_trajectory = False):
816        """
817        Loads the molecular data contained in a pdbqt file into the current
818        scoria.Molecule object. Note that this implementation is
819        incomplete. It doesn't save atomic charges, for example. The atom
820        types are stored in the "element_padded" and "element" columns.
821
822        Wrapper function for :meth:`~scoria.FileIO.FileIO.load_pdbqt_into`
823
824        :param str filename: A string, the filename of the pdbqt file.
825        :param bool bonds_by_distance: An optional boolean, whether or not to
826                    determine atomic bonds based on atom proximity. False by
827                    default, unlike for PDB.
828        :param bool serial_reindex: An optional boolean, whether or not to
829                    reindex the pdb serial field. True by default.
830        :param bool resseq_reindex: An optional boolean, whether or not to
831                    reindex the pdbqt resseq field. False by default.
832        :param bool is_trajectory: An optional boolean, whether or not the PDB
833                    is multi-frame. Defaults of False.
834        """
835
836        self.fileio.load_pdbqt_into(
837            filename, bonds_by_distance, serial_reindex, resseq_reindex,
838            is_trajectory = is_trajectory
839        )
840
841    def load_pdbqt_into_using_file_object(self, file_obj,
842                                        bonds_by_distance = False,
843                                        serial_reindex = True,
844                                        resseq_reindex = False,
845                                        is_trajectory = False):
846        """
847        Loads molecular data from a python file object (pdbqt formatted)
848        into the current scoria.Molecule object. Note that most users will
849        want to use the load_pdb_into() function instead, which is identical
850        except that it accepts a filename string instead of a python file
851        object.
852
853        Requires the :any:`numpy` library.
854
855        Wrapper function for :meth:`~scoria.FileIO.FileIO.load_pdbqt_into_using_file_object`
856
857        :param file file_obj: A python file object, containing pdb-formatted
858                    data.
859        :param bool bonds_by_distance: An optional boolean, whether or not to
860                    determine atomic bonds based on atom proximity. False by
861                    default, unlike for PDB.
862        :param bool serial_reindex: An optional boolean, whether or not to
863                    reindex the pdb serial field. True by default.
864        :param bool resseq_reindex: An optional boolean, whether or not to
865                    reindex the pdb resseq field. False by default.
866        :param bool is_trajectory: An optional boolean, whether or not the PDB
867                    is multi-frame. Defaults of False.
868        """
869
870        self.fileio.load_pdbqt_into_using_file_object(
871            file_obj, bonds_by_distance, serial_reindex, resseq_reindex,
872            is_trajectory = is_trajectory
873        )
874
875    def save_pym(self, filename, save_bonds = False, save_filename = False,
876                 save_remarks = False, save_hierarchy = False,
877                 save_coordinates_undo_point = False):
878        """
879        Saves the molecular data contained in a scoria.Molecule object
880        to a pym file.
881
882        Requires the :any:`numpy` library.
883
884        Wrapper function for :meth:`~scoria.FileIO.FileIO.save_pym`
885
886        :param str filename: An string, the filename to use for saving. (Note
887                    that this is actually a directory, not a file.)
888        :param bool save_bonds: An optional boolean, whether or not to save
889                    information about atomic bonds. False by default.
890        :param bool save_filename: An optional boolean, whether or not to save
891                    the original (pdb) filename. False by default.
892        :param bool save_remarks: An optional boolean, whether or not to save
893                    remarks associated with the molecule. False by default.
894        :param bool save_hierarchy: An optional boolean, whether or not to save
895                    information about spheres the bound (encompass) the whole
896                    molecule, the chains, and the residues. False by default.
897        :param bool save_coordinates_undo_point: An optional boolean, whether or
898                    not to save the last coordinate undo point. False by
899                    default.
900        """
901
902        self.fileio.save_pym(
903            filename, save_bonds, save_filename, save_remarks,
904            save_hierarchy, save_coordinates_undo_point
905        )
906
907    def save_pdb(self, filename = "", serial_reindex = True,
908                 resseq_reindex = False, return_text = False, frame = None):
909        """
910        Saves the molecular data contained in a scoria.Molecule object
911        to a pdb file.
912
913        Wrapper function for :meth:`~scoria.FileIO.FileIO.save_pdb`
914
915        :param str filename: An string, the filename to use for saving.
916        :param bool serial_reindex: An optional boolean, whether or not to
917                    reindex the pdb serial field. True by default.
918        :param bool resseq_reindex: An optional boolean, whether or not to
919                    reindex the pdb resseq field. False by default.
920        :param bool return_text: An optional boolean, whether or not to return
921                    text instead of writing to a file. If True, the filename
922                    variable is ignored.
923        :param int frame: If specified, a single-frame PDB will be generated.
924                    If not specified, a multi-frame PDB will be generated if
925                    the Molecule has multiple frames. Otherwise, the single
926                    existing frame will be used.
927
928
929        :returns: If return_text is True, a PDB-formatted string. Otherwise,
930                returns nothing.
931        :rtype: :any:`str` or :any:`None`
932        """
933
934        return self.fileio.save_pdb(
935            filename, serial_reindex, resseq_reindex, return_text, frame
936        )
937
938    def load_pdbqt_trajectory_into(self, filename, bonds_by_distance = True,
939                                   serial_reindex = True,
940                                   resseq_reindex = False):
941        """
942        Loads the molecular data contained in a pdbqt trajectoy file (e.g., an
943        AutoDock Vina output file) into the current scoria.Molecule
944        object.
945
946        Should be called via the wrapper function :meth:`scoria.Molecule.Molecule.load_pdbqt_trajectory_into`
947
948        :param str filename: A string, the filename of the pdbqt file.
949        :param bool bonds_by_distance: An optional boolean, whether or not to
950                    determine atomic bonds based on atom proximity. True by
951                    default.
952        :param bool serial_reindex: An optional boolean, whether or not to
953                    reindex the pdb serial field. True by default.
954        :param bool resseq_reindex: An optional boolean, whether or not to
955                    reindex the pdb resseq field. False by default.
956        """
957
958        return self.fileio.load_pdbqt_trajectory_into(filename,
959                                             bonds_by_distance,
960                                             serial_reindex,
961                                             resseq_reindex)
962
963    def load_pdbqt_trajectory_into_using_file_object(self, file_obj,
964                                                     bonds_by_distance = True,
965                                                     serial_reindex = True,
966                                                     resseq_reindex = False):
967        """
968        Loads molecular data from a python file object (pdbqt trajectory
969        formatted) into the current scoria.Molecule object. Note that most
970        users will want to use the load_pdbqt_trajectory_into() function
971        instead, which is identical except that it accepts a filename string
972        instead of a python file object.
973
974        Wrapper function for
975        :meth:`~scoria.FileIO.FileIO.load_pdbqt_trajectory_into_using_file_object`
976
977        :param file file_obj: A python file object, containing pdbqt-formatted
978                    trajectory data.
979        :param bool bonds_by_distance: An optional boolean, whether or not to
980                    determine atomic bonds based on atom proximity. True by
981                    default.
982        :param bool serial_reindex: An optional boolean, whether or not to
983                    reindex the pdb serial field. True by default.
984        :param bool resseq_reindex: An optional boolean, whether or not to
985                    reindex the pdb resseq field. False by default.
986        """
987
988        return self.fileio.load_pdbqt_trajectory_into_using_file_object(file_obj,
989                                                     bonds_by_distance,
990                                                     serial_reindex,
991                                                     resseq_reindex)
992
993    def load_pdb_trajectory_into(self, filename, bonds_by_distance = True,
994                                 serial_reindex = True,
995                                 resseq_reindex = False):
996        """
997        Loads the molecular data contained in a pdb trajectory file into the
998        current scoria.Molecule object.
999
1000        Should be called via the wrapper function :meth:`scoria.Molecule.Molecule.load_pdb_trajectory_into`
1001
1002        :param str filename: A string, the filename of the pdb trajectory
1003                   file.
1004        :param bool bonds_by_distance: An optional boolean, whether or not to
1005                    determine atomic bonds based on atom proximity. True by
1006                    default.
1007        :param bool serial_reindex: An optional boolean, whether or not to
1008                    reindex the pdb serial field. True by default.
1009        :param bool resseq_reindex: An optional boolean, whether or not to
1010                    reindex the pdb resseq field. False by default.
1011        """
1012
1013        return self.fileio.load_pdb_trajectory_into(filename, bonds_by_distance,
1014                                        serial_reindex, resseq_reindex)
1015
1016    def load_pdb_trajectory_into_using_file_object(self, file_obj,
1017                                                   bonds_by_distance = True,
1018                                                   serial_reindex = True,
1019                                                   resseq_reindex = False):
1020        """
1021        Loads molecular data from a python file object (pdb trajectory
1022        formatted) into the current scoria.Molecule object. Note that most
1023        users will want to use the load_pdb_trajectory_into() function
1024        instead, which is identical except that it accepts a filename string
1025        instead of a python file object.
1026
1027        Should be called via the wrapper function :meth:`scoria.Molecule.Molecule.load_pdb_trajectory_into_using_file_object`
1028
1029        :param file file_obj: A python file object, containing pdb-formatted
1030                    trajectory data.
1031        :param bool bonds_by_distance: An optional boolean, whether or not to
1032                    determine atomic bonds based on atom proximity. True by
1033                    default.
1034        :param bool serial_reindex: An optional boolean, whether or not to
1035                    reindex the pdb serial field. True by default.
1036        :param bool resseq_reindex: An optional boolean, whether or not to
1037                    reindex the pdb resseq field. False by default.
1038        """
1039
1040        return self.fileio.load_pdb_trajectory_into_using_file_object(file_obj,
1041                                                                    bonds_by_distance,
1042                                                                    serial_reindex,
1043                                                                    resseq_reindex)
1044
1045    def load_MDAnalysis_into(self, *args):
1046        """
1047        Allows import of molecular structure with MDAnalysis
1048
1049        Requires the :any:`MDAnalysis <MDAnalysis.core.AtomGroup>` library.
1050
1051        Wrapper function for
1052        :meth:`~scoria.FileIO.FileIO.load_MDAnalysis_into`
1053
1054        :param \*args: Filename, filenames, or list of file names. Used to
1055            inizalize a MDAnalysis.Universe object.
1056        """
1057
1058        self.fileio.load_MDAnalysis_into(*args)
1059
1060
1061    def load_MDAnalysis_into_using_universe_object(self, universe):
1062        """
1063        Allows import of molecular structure with MDAnalysis
1064
1065        Requires the :any:`MDAnalysis <MDAnalysis.core.AtomGroup>` library.
1066
1067        Wrapper function for
1068        :meth:`~scoria.FileIO.FileIO.load_MDAnalysis_into_using_universe_object`
1069
1070        :param MDAnalysis.core.Universe universe: MDAnalysis Universe object.
1071        """
1072
1073        self.fileio.load_MDAnalysis_into_using_universe_object(universe)
1074
1075
1076    # Atoms and Bonds class methods
1077    def get_number_of_bond_partners_of_element(self, atom_index, the_element):
1078        """
1079        Counts the number of atoms of a given element bonded to a specified
1080        atom of interest.
1081
1082        Requires the :any:`numpy` library.
1083
1084        Wrapper function for
1085        :meth:`~scoria.AtomsAndBonds.AtomsAndBonds.get_number_of_bond_partners_of_element`
1086
1087        :param int atom_index: An int, the index of the atom of interest.
1088        :param str the_element: A string describing the element of the neighbors
1089                    to be counted.
1090
1091        :returns: An int, the number of neighboring atoms of the specified
1092                element.
1093        :rtype: :any:`int`
1094        """
1095
1096        return self.atoms_and_bonds.get_number_of_bond_partners_of_element(
1097            atom_index, the_element
1098        )
1099
1100    def get_index_of_first_bond_partner_of_element(self, atom_index,
1101                                                   the_element):
1102        """
1103        For a given atom of interest, returns the index of the first
1104        neighbor of a specified element.
1105
1106        Wrapper function for :meth:`~scoria.AtomsAndBonds.AtomsAndBonds.get_index_of_first_bond_partner_of_element`
1107
1108        :param int atom_index: An int, the index of the atom of interest.
1109        :param str the_element: A string specifying the desired element of the
1110                    neighbor.
1111
1112        :returns: An int, the index of the first neighbor atom of the specified
1113                element. If no such neighbor exists, returns -1.
1114        :rtype: :any:`int`
1115        """
1116
1117        return self.atoms_and_bonds.get_index_of_first_bond_partner_of_element(
1118            atom_index, the_element
1119        )
1120
1121    def create_bonds_by_distance(self, remove_old_bond_data = True,
1122                                 delete_excessive_bonds = True):
1123        """
1124        Determines which atoms are bound to each other based on their
1125        proximity.
1126
1127        Requires the :any:`numpy` and :any:`scipy<scipy.spatial>` libraries.
1128
1129        Wrapper function for
1130        :meth:`~scoria.AtomsAndBonds.AtomsAndBonds.create_bonds_by_distance`
1131
1132        :param bool remove_old_bond_data: An optional boolean, whether or not to
1133                    discard old bond data before adding in bonds determined by
1134                    distance. True by default.
1135        :param bool delete_excessive_bonds: An optional boolean, whether or not
1136                    to check for and delete excessive bonds. True by default.
1137        """
1138
1139        self.atoms_and_bonds.create_bonds_by_distance(
1140            remove_old_bond_data, delete_excessive_bonds
1141        )
1142
1143    def delete_bond(self, index1, index2):
1144        """
1145        Deletes a bond.
1146
1147        Wrapper function for :meth:`~scoria.AtomsAndBonds.AtomsAndBonds.delete_bond`
1148
1149        :param int index1: An int, the index of the first atom of the bonded
1150                    pair.
1151        :param int index2: An int, the index of the second atom of the bonded
1152                    pair.
1153        """
1154
1155        self.atoms_and_bonds.delete_bond(index1, index2)
1156
1157    def add_bond(self, index1, index2, order = 1):
1158        """
1159        Adds a bond.
1160
1161        Wrapper function for :meth:`~scoria.AtomsAndBonds.AtomsAndBonds.add_bond`
1162
1163        :param int index1: An int, the index of the first atom of the bonded
1164                    pair.
1165        :param int index2: An int, the index of the second atom of the bonded
1166                    pair.
1167        :param int order: An optional int, the order of the bond. 1 by default.
1168        """
1169
1170        self.atoms_and_bonds.add_bond(index1, index2, order)
1171
1172    def delete_atom(self, index):
1173        """
1174        Deletes an atom.
1175
1176        Wrapper function for :meth:`~scoria.AtomsAndBonds.AtomsAndBonds.delete_atom`
1177
1178        :param int index: An int, the index of the atom to delete.
1179        """
1180
1181        self.atoms_and_bonds.delete_atom(index)
1182
1183    def add_atom(self, record_name = "ATOM", serial = 1, name = "X",
1184                 resname = "XXX", chainid = "X", resseq = 1, occupancy = 0.0,
1185                 tempfactor = 0.0, charge = '', element = "X",
1186                 coordinates = numpy.array([0.0, 0.0, 0.0]), autoindex = True):
1187        """
1188        Adds an atom.
1189
1190        Wrapper function for :meth:`~scoria.AtomsAndBonds.AtomsAndBonds.add_atom`
1191
1192        :param str record_name: An optional string, the record name of the atom.
1193                    "ATOM" is the default.
1194        :param int serial: An optional int, the serial field of the atom. 1 is
1195                    the default.
1196        :param str name: An optional string, the name of the atom. 'X' is the
1197                    default.
1198        :param str resname: An optional string, the resname of the atom. 'XXX'
1199                    is the default.
1200        :param str chainid: An optional string, chainid of the atom. 'X' is the
1201                    default.
1202        :param int resseq: An optional int, the resseq field of the atom. 1 is
1203                    the default.
1204        :param float occupancy: An optional float, the occupancy of the atom. 0.0
1205                    is the default.
1206        :param float tempfactor: An optional float, the tempfactor of the atom.
1207                    0.0 is the default.
1208        :param str charge: An optional string, the charge of the atom. '' is the
1209                    default.
1210        :param str element: An optional string, the element of the atom. 'X' is
1211                    the default.
1212        :param numpy.array coordinates: An optional numpy.array, the (x, y, z)
1213                    coordinates of the atom. numpy.array([0.0, 0.0, 0.0]) is
1214                    the default.
1215        """
1216
1217        self.atoms_and_bonds.add_atom(
1218            record_name, serial, name, resname, chainid, resseq, occupancy,
1219            tempfactor, charge, element, coordinates, autoindex
1220        )
1221
1222    # Selections class
1223    def get_molecule_from_selection(self, selection, serial_reindex = True,
1224                                    resseq_reindex = False):
1225        """
1226        Creates a scoria.Molecule from a user-defined atom selection.
1227
1228        Wrapper function for :meth:`~scoria.Selections.Selections.get_molecule_from_selection`
1229
1230        :param numpy.array selection: A numpy.array containing the indices of the atoms
1231                    in the user-defined selection.
1232        :param bool serial_reindex: An optional boolean, whether or not to
1233                    reindex the atom serial fields. Default is True.
1234        :param bool resseq_reindex: An optional boolean, whether or not to
1235                    reindex the atom resseq fields. Default is False.
1236
1237        :returns: A scoria.Molecule object containing the atoms of the
1238                    user-defined selection.
1239        """
1240
1241        return self.selections.get_molecule_from_selection(
1242            selection, serial_reindex, resseq_reindex
1243        )
1244
1245    def select_atoms(self, selection_criteria):
1246        """
1247        Select a set of atoms based on user-specified criteria.
1248
1249        Wrapper function for :meth:`~scoria.Selections.Selections.select_atoms`
1250
1251        :param dict selection_criteria: A dictionary, where the keys correspond
1252                    to keys in the
1253                    self.__parent_Information.Information.get_atom_information()
1254                    structured numpy array, and the values are lists of
1255                    acceptable matches. The selection is a logical "AND"
1256                    between dictionary entries, but "OR" within the value lists
1257                    themselves. For example: {'atom':['CA', 'O'], 'chain':'A',
1258                    'resname':'PRO'} would select all atoms with the names CA
1259                    or O that are located in the PRO residues of chain A.
1260
1261        :returns: A numpy.array containing the indices of the atoms of the
1262                    selection.
1263        """
1264
1265        return self.selections.select_atoms(selection_criteria)
1266
1267    def select_atoms_in_bounding_box(self, bounding_box):
1268        """
1269        Selects all the atoms that are within a bounding box.
1270
1271        Requires the :any:`numpy` library.
1272
1273        Wrapper function for :meth:`~scoria.Selections.Selections.select_atoms_in_bounding_box`
1274
1275        :param numpy.array bounding_box: A 2x3 numpy.array containing the minimum and
1276                    maximum points of the bounding box. Example:
1277                    numpy.array( [[min_x, min_y, min_z], [max_x, max_y, max_z]] ).
1278
1279        :returns: A numpy.array containing the indices of the atoms that are
1280                    within the bounding box.
1281        """
1282
1283        return self.selections.select_atoms_in_bounding_box(bounding_box)
1284
1285    def select_branch(self, root_atom_index, directionality_atom_index):
1286        """
1287        Identify an isolated "branch" of a molecular model. Assumes the
1288        atoms with indices root_atom_index and directionality_atom_index are
1289        bound to one another and that the branch starts at root_atom_index one
1290        and "points" in the direction of directionality_atom_index.
1291
1292        Requires the :any:`numpy` library.
1293
1294        Wrapper function for :meth:`~scoria.Selections.Selections.select_branch`
1295
1296        :param int root_atom_index: An int, the index of the first atom in the
1297                branch (the "root").
1298        :param int directionality_atom_index: An int, the index of the second atom
1299                in the branch, used to establish directionality
1300
1301        :returns: A numpy array containing the indices of the atoms of the branch.
1302        """
1303
1304        return self.selections.select_branch(
1305            root_atom_index, directionality_atom_index
1306        )
1307
1308    def select_all_atoms_bound_to_selection(self, selections):
1309        """
1310        Selects all the atoms that are bound to a user-specified selection.
1311
1312        Requires the :any:`numpy` library.
1313
1314        Wrapper function for :meth:`~scoria.Selections.Selections.select_all_atoms_bound_to_selection`
1315
1316        :param numpy.array selection: A numpy.array containing the indices of the
1317                    user-specified selection.
1318
1319        :returns: A numpy.array containing the indices of the atoms that are
1320                bound to the user-specified selection. Note that this new
1321                selection does not necessarily include the indices of the
1322                original user-specified selection.
1323        """
1324
1325        return self.selections.select_all_atoms_bound_to_selection(selections)
1326
1327    def select_atoms_from_same_molecule(self, selection):
1328        """
1329        Selects all the atoms that belong to the same molecule as a
1330        user-defined selection, assuming that the scoria.Molecule object
1331        actually contains multiple physically distinct molecules that are not
1332        bound to each other via covalent bonds.
1333
1334        Requires the :any:`numpy` library.
1335
1336        Wrapper function for :meth:`~scoria.Selections.Selections.select_atoms_from_same_molecule`
1337
1338        :param numpy.array selection: A numpy.array containing the indices of the
1339                    user-defined selection.
1340
1341        :returns: A numpy.array containing the indices of the atoms belonging to
1342                    the same molecules as the atoms of the user-defined
1343                    selection.
1344        """
1345
1346        return self.selections.select_atoms_from_same_molecule(selection)
1347
1348    def selections_of_constituent_molecules(self):
1349        """
1350        Identifies the indices of atoms belonging to separate molecules,
1351        assuming that the scoria.Molecule object actually contains multiple
1352        physically distinct molecules that are not bound to each other via
1353        covalent bonds.
1354
1355        Requires the :any:`numpy` library.
1356
1357        Wrapper function for :meth:`~scoria.Selections.Selections.selections_of_constituent_molecules`
1358
1359        :Returns: A python list of numpy.array objects containing the indices of
1360                    the atoms belonging to each molecule of the composite
1361                    scoria.Molecule object.
1362        """
1363
1364        return self.selections.selections_of_constituent_molecules()
1365
1366    def select_atoms_near_other_selection(self, selection, cutoff):
1367        """
1368        Selects all atoms that are near the atoms of a user-defined
1369        selection.
1370
1371        Requires the :any:`numpy` and :any:`scipy<scipy.spatial>` libraries.
1372
1373        Wrapper function for :meth:`~scoria.Selections.Selections.select_atoms_near_other_selection`
1374
1375        :param numpy.array selection: A numpy.array containing the indices of the
1376                    user-defined selection.
1377        :param float cutoff: A float, the distance cutoff (in Angstroms).
1378
1379        :returns: A numpy.array containing the indices of all atoms near the
1380                    user-defined selection, not including the atoms of the
1381                    user-defined selection themselves.
1382        """
1383
1384        return self.selections.select_atoms_near_other_selection(
1385            selection, cutoff
1386        )
1387
1388    def select_atoms_in_same_residue(self, selection):
1389        """
1390        Selects all atoms that are in the same residue as any of the atoms
1391        of a user-defined seleciton. Residues are considered unique if they
1392        have a unique combination of resname, resseq, and chainid fields.
1393
1394        Wrapper function for :meth:`~scoria.Selections.Selections.select_atoms_in_same_residue`
1395
1396        :param numpy.array selection: A numpy.array containing the indices of the
1397                    user-defined selection.
1398
1399        :returns: A numpy.array containing the indices of all atoms in the same
1400                    residue as any of the atoms of the user-defined selection.
1401        """
1402
1403        return self.selections.select_atoms_in_same_residue(selection)
1404
1405    def invert_selection(self, selection):
1406        """
1407        Inverts a user-defined selection (i.e., identifies all atoms that
1408        are not in the seleciton).
1409
1410        Wrapper function for :meth:`~scoria.Selections.Selections.invert_selection`
1411
1412        :param numpy.array selection: A numpy.array containing the indices of the
1413                    user-defined selection.
1414
1415        :returns: A numpy.array containing the indices of all atoms that are not
1416                    in the user-defined seleciton.
1417        """
1418
1419        return self.selections.invert_selection(selection)
1420
1421    def select_all(self):
1422        """
1423        Selects all the atoms in a scoria.Molecule object.
1424
1425        Wrapper function for :meth:`~scoria.Selections.Selections.select_all`
1426
1427        :returns: A numpy.array containing the indices of all atoms in the
1428                    scoria.Molecule object.
1429        """
1430
1431        return self.selections.select_all()
1432
1433    def select_close_atoms_from_different_molecules(self, other_mol, cutoff,
1434                                                    pairwise_comparison = True,
1435                                                    terminate_early = False):
1436        """
1437        Effectively detects steric clashes between self and another
1438        scoria.Molecule.
1439
1440        Requires the :any:`numpy` and :any:`scipy<scipy.spatial>` libraries.
1441
1442        Wrapper function for :meth:`~scoria.Selections.Selections.select_close_atoms_from_different_molecules`
1443
1444        :param scoria.Molecule other_mol: A scoria.Molecule object of the other
1445                    molecule.
1446        :param float cutoff: A float, the user-defined distance cutoff in
1447                    Angstroms.
1448        :param bool pairwise_comparison: An optional boolean, whether or not to
1449                    perform a simple pairwise distance comparison (if True) or
1450                    to use a more sophisitcated method (if False). True by
1451                    default.
1452        :param bool terminate_early: An optional boolean, whether or not to stop
1453                    looking for steric clashes once one is found. False by
1454                    default.
1455
1456        :returns: A tuple containing two elements. The first is a numpy.array
1457                    containing the indices of all nearby atoms from this
1458                    scoria.Molecule object (self). The second is a
1459                    numpy.array containing the indices of all nearby atoms from
1460                    the other molecule.
1461        """
1462
1463        return self.selections.select_close_atoms_from_different_molecules(
1464            other_mol, cutoff, pairwise_comparison, terminate_early
1465        )
1466
1467    def selections_of_chains(self):
1468        """
1469        Identifies the atom selections of each chain.
1470
1471        Requires the :any:`numpy` library.
1472
1473        Wrapper function for :meth:`~scoria.Selections.Selections.selections_of_chains`
1474
1475        :returns: A dictionary. The keys of the dictionary correspond to the
1476                    chainids, and the values are numpy.array objects containing
1477                    the indices of the associated chain atoms.
1478        """
1479
1480        return self.selections.selections_of_chains()
1481
1482    def selections_of_residues(self):
1483        """
1484        Identifies the atom selections of each residue.
1485
1486        Requires the :any:`numpy` library.
1487
1488        Wrapper function for :meth:`~scoria.Selections.Selections.selections_of_residues`
1489
1490        :returns: A dictionary. The keys of this dictionary correspond to the
1491                    unique resname-resseq-chainid residue identifiers, and the
1492                    values are numpy.array objects containing the indices of
1493                    the associated residue atoms.
1494        """
1495
1496        return self.selections.selections_of_residues()
1497
1498    # Manipulation class
1499    def set_atom_location(self, atom_index, new_location):
1500        """
1501        Translates the entire molecular model (without rotating) so that the
1502        atom with the specified index is located at the specified coordinate.
1503
1504        Wrapper function for :meth:`~scoria.Manipulation.Manipulation.set_atom_location`
1505
1506        :param int atom_index: An int, the index of the target atom.
1507        :param numpy.array new_location: A numpy.array specifying the new (x, y, z)
1508                    coordinate of the specified atom.
1509
1510        :returns: A numpy.array specifying the (delta_x, delta_y, delta_z) vector
1511                by which the pmolecule.Molecule was translated.
1512        """
1513
1514        return self.manipulation.set_atom_location(atom_index, new_location)
1515
1516    def set_coordinate_undo_point(self):
1517        """
1518        Sets ("saves") the undo point of the atom coordinates. Any
1519        subsequent manipulations of atomic coordinates can be "undone" by
1520        reseting to this configuration via the coordinate_undo function.
1521
1522        Wrapper function for :meth:`~scoria.Manipulation.Manipulation.set_coordinate_undo_point`
1523        """
1524
1525        self.manipulation.set_coordinate_undo_point()
1526
1527    def coordinate_undo(self):
1528        """
1529        Resets the coordinates of all atoms to those saved using the
1530        set_coordinate_undo_point function.
1531
1532        Wrapper function for :meth:`~scoria.Manipulation.Manipulation.coordinate_undo`
1533        """
1534
1535        self.manipulation.coordinate_undo()
1536
1537    def translate_molecule(self, delta):
1538        """
1539        Translate all the atoms of the molecular model by a specified
1540        vector.
1541
1542        Wrapper function for :meth:`~scoria.Manipulation.Manipulation.translate_molecule`
1543
1544        :param numpy.array delta: A numpy.array (delta_x, delta_y, delta_z) specifying the
1545            amount to move each atom along the x, y, and z coordinates.
1546        """
1547
1548        self.manipulation.translate_molecule(delta)
1549
1550    def rotate_molecule_around_a_line_between_points(self, line_point1,
1551                                                     line_point2, rotate):
1552        """
1553        Rotate the molecular model about a line segment. The end points of
1554        the line segment are explicitly specified coordinates.
1555
1556        Wrapper function for :meth:`~scoria.Manipulation.Manipulation.rotate_molecule_around_a_line_between_points`
1557
1558        :param numpy.array line_point1: A numpy.array (x, y, z) corresponding to one end
1559                    of the line segment.
1560        :param numpy.array line_point2: A numpy.array (x, y, z) corresponding to the
1561                    other end of the line segment.
1562        :param float rotate: A float, the angle of rotation, in radians.
1563        """
1564
1565        self.manipulation.rotate_molecule_around_a_line_between_points(
1566            line_point1, line_point2, rotate
1567        )
1568
1569    def rotate_molecule_around_a_line_between_atoms(self, line_point1_index,
1570                                                    line_point2_index, rotate):
1571        """
1572        Rotate the molecular model about a line segment. The end points of
1573        the line segment are atoms of specified indices.
1574
1575        Wrapper function for :meth:`~scoria.Manipulation.Manipulation.rotate_molecule_around_a_line_between_atoms`
1576
1577        :param int line_point1_index: An int, the index of the first atom at one
1578                    end of the line segment.
1579        :param int line_point2_index: An int, the index of the second atom at
1580                    the other end of the line segment.
1581        :param float rotate: A float, the angle of rotation, in radians.
1582        """
1583
1584        self.manipulation.rotate_molecule_around_a_line_between_atoms(
1585            line_point1_index, line_point2_index, rotate
1586        )
1587
1588    def rotate_molecule_around_pivot_point(self, pivot, thetax,
1589                                           thetay, thetaz):
1590        """
1591        Rotate the molecular model around a specified atom.
1592
1593        Requires the :any:`numpy` library.
1594
1595        Wrapper function for :meth:`~scoria.Manipulation.Manipulation.rotate_molecule_around_pivot_point`
1596
1597        :param numpy.array pivot: A numpy.array, the (x, y, z) coordinate about which
1598                    the molecular model will be rotated.
1599        :param float thetax: A float, the angle to rotate relative to the x axis,
1600                    in radians.
1601        :param float thetay: A float, the angle to rotate relative to the y axis,
1602                    in radians.
1603        :param float thetaz: A float, the angle to rotate relative to the z axis,
1604                    in radians.
1605        """
1606
1607        self.manipulation.rotate_molecule_around_pivot_point(
1608            pivot, thetax, thetay, thetaz
1609        )
1610
1611    def rotate_molecule_around_pivot_atom(self, pivot_index, thetax,
1612                                          thetay, thetaz):
1613        """
1614        Rotate the molecular model around a specified atom.
1615
1616        Requires the :any:`numpy` library.
1617
1618        Wrapper function for :meth:`~scoria.Manipulation.Manipulation.rotate_molecule_around_pivot_atom`
1619
1620        :param int pivot_index: An int, the index of the atom about which the
1621                    molecular model will be rotated.
1622        :param float thetax: A float, the angle to rotate relative to the x axis,
1623                    in radians.
1624        :param float thetay: A float, the angle to rotate relative to the y axis,
1625                    in radians.
1626        :param float thetaz: A float, the angle to rotate relative to the z axis,
1627                    in radians.
1628        """
1629
1630        self.manipulation.rotate_molecule_around_pivot_atom(
1631            pivot_index, thetax, thetay, thetaz
1632        )
1633
1634    # Geometry class
1635    def get_angle_between_three_points(self, pt1, pt2, pt3):
1636        """
1637        Computes the angle (in radians) formed by three points (numpy.array
1638        objects).
1639
1640        Wrapper function for :meth:`~scoria.Geometry.Geometry.get_angle_between_three_points`
1641
1642        :param numpy.array pt1: A numpy.array (x, y, z) representing the first of the
1643                    three 3D points.
1644        :param numpy.array pt2: A numpy.array (x, y, z) representing the second of the
1645                    three 3D points.
1646        :param numpy.array pt3: A numpy.array (x, y, z) representing the third of the
1647                    three 3D points.
1648
1649        :returns: A float containing the angle between the three points, in
1650                    radians.
1651        """
1652
1653        return self.geometry.get_angle_between_three_points(pt1, pt2, pt3)
1654
1655    def get_dihedral_angle(self, pt1, pt2, pt3, pt4):
1656        """
1657        Calculates the dihedral angle formed by four points (numpy.array
1658        objects).
1659
1660        Wrapper function for :meth:`~scoria.Geometry.Geometry.get_dihedral_angle`
1661
1662        :param numpy.array pt1: A numpy.array (x, y, z) representing the first 3D
1663                    point.
1664        :param numpy.array pt2: A numpy.array (x, y, z) representing the second 3D
1665                    point.
1666        :param numpy.array pt3: A numpy.array (x, y, z) representing the third 3D
1667                    point.
1668        :param numpy.array pt4: A numpy.array (x, y, z) representing the fourth 3D
1669                    point.
1670
1671        :returns: A float containing the dihedral angle between the four points,
1672                    in radians.
1673        """
1674
1675        return self.geometry.get_dihedral_angle(pt1, pt2, pt3, pt4)
1676
1677    def get_planarity_deviation(self, pt1, pt2, pt3, pt4):
1678        """
1679        Determines how close four points (numpy.array objects) come to lying
1680        in a common plane.
1681
1682        Wrapper function for :meth:`~scoria.Geometry.Geometry.get_planarity_deviation`
1683
1684        :param numpy.array pt1: A numpy.array (x, y, z) representing a 3D point.
1685        :param numpy.array pt2: A numpy.array (x, y, z) representing a 3D point.
1686        :param numpy.array pt3: A numpy.array (x, y, z) representing a 3D point.
1687        :param numpy.array pt4: A numpy.array (x, y, z) representing a 3D point.
1688
1689        :returns: A float, the minimum distance between one point and the plane
1690                    formed by the other three.
1691        """
1692
1693        return self.geometry.get_planarity_deviation(pt1, pt2, pt3, pt4)
1694
1695    def is_planar(self, pt1, pt2, pt3, pt4, planarity_cutoff = 0.2):
1696        """
1697        Checks whether four points (numpy.array) lie in a common plane.
1698
1699        Wrapper function for :meth:`~scoria.Geometry.Geometry.is_planar`
1700
1701        :param numpy.array pt1: A numpy.array (x, y, z) representing a 3D point.
1702        :param numpy.array pt2: A numpy.array (x, y, z) representing a 3D point.
1703        :param numpy.array pt3: A numpy.array (x, y, z) representing a 3D point.
1704        :param numpy.array pt4: A numpy.array (x, y, z) representing a 3D point.
1705        :param float planarity_cutoff: An optional float. How much the points can
1706                    deviate (in Angstroms) and still be considered planar. The
1707                    default is 0.2.
1708
1709        :returns: A boolean, whether the 4 points can be considered planar.
1710        """
1711
1712        return self.geometry.is_planar(pt1, pt2, pt3, pt4, planarity_cutoff)
1713
1714    # Other molecule class
1715    def get_other_molecules_aligned_to_this(self, other_mol, tethers):
1716        """
1717        Aligns a molecule to self (this scoria.Molecule object) using a
1718        quaternion RMSD alignment.
1719
1720        Requires the :any:`numpy` library.
1721
1722        Wrapper function for :meth:`~scoria.OtherMolecules.OtherMolecules.get_other_molecules_aligned_to_this`
1723
1724        :param scoria.Molecule other_mol: A scoria.Molecule that is to be aligned to
1725                    this one.
1726        :param tuple tethers: A tuple of two numpy.array objects, where each array
1727                    contains the indices of self and other_mol, respectively,
1728                    such that equivalent atoms are listed in the same order.
1729                    So, for example, if (atom 1, self = atom 3, other) and
1730                    (atom2, self = atom6, other) than the tethers would be
1731                    (numpy.array([1, 2]), numpy.array([3, 6])).
1732
1733        :returns: The new molecule.
1734        """
1735
1736        # Add Weight Matrix
1737        return self.other_molecules.get_other_molecules_aligned_to_this(
1738            other_mol, tethers
1739        )
1740
1741    def get_distance_to_another_molecule(self, other_molecules,
1742                                         pairwise_comparison = True):
1743        """
1744        Computes the minimum distance between any of the atoms of this
1745        molecular model and any of the atoms of a second specified model.
1746
1747        Requires the :any:`numpy` and :any:`scipy<scipy.spatial>` libraries.
1748
1749        Wrapper function for :meth:`~scoria.OtherMolecules.OtherMolecules.get_distance_to_another_molecule`
1750
1751        :param scoria.Molecule other_molecules: a scoria.Molecule, the other molecular
1752                    model.
1753        :param bool pairwise_comparison: An optional boolean, whether or not to
1754                    perform a simple pairwise distance comparison (if True) or
1755                    to use a more sophisitcated method (if False). True by
1756                    default.
1757
1758        :returns: A float, the minimum distance between any two atoms of the two
1759                specified molecular models (self and other_molecules).
1760        """
1761
1762        return self.other_molecules.get_distance_to_another_molecule(
1763            other_molecules, pairwise_comparison
1764        )
1765
1766    def get_distance_to_another_molecules(self, other_molecules,
1767                                         pairwise_comparison = True):
1768        """
1769        DEPRECATION WARNING: Please use :meth:`~scoria.Molecule.Molecule.get_distance_to_another_molecule`
1770
1771        Computes the minimum distance between any of the atoms of this
1772        molecular model and any of the atoms of a second specified model.
1773
1774        Requires the :any:`numpy` and :any:`scipy<scipy.spatial>` libraries.
1775
1776        Wrapper function for :meth:`~scoria.OtherMolecules.OtherMolecules.get_distance_to_another_molecule`
1777
1778        :param scoria.Molecule other_molecules: a scoria.Molecule, the other molecular
1779                    model.
1780        :param bool pairwise_comparison: An optional boolean, whether or not to
1781                    perform a simple pairwise distance comparison (if True) or
1782                    to use a more sophisitcated method (if False). True by
1783                    default.
1784
1785        :returns: A float, the minimum distance between any two atoms of the two
1786                specified molecular models (self and other_molecules).
1787        """
1788
1789        return self.other_molecules.get_distance_to_another_molecule(
1790            other_molecules, pairwise_comparison
1791        )
1792
1793    def get_rmsd_equivalent_atoms_specified(self, other_mol, tethers):
1794        """
1795        Calculates the RMSD between this scoria.Molecle object and
1796        another, where equivalent atoms are explicitly specified.
1797
1798        Wrapper function for :meth:`~scoria.OtherMolecules.OtherMolecules.get_rmsd_equivalent_atoms_specified`
1799
1800        :param scoria.Molecule other_mol: The other scoria.Molecule object.
1801        :param tuple tethers: A tuple of two numpy.array objects, where each array
1802                    contains the indices of self and other_mol, respectively,
1803                    such that equivalent atoms are listed in the same order.
1804                    So, for example, if (atom 1, self = atom 3, other) and
1805                    (atom2, self = atom6, other) than the tethers would be
1806                    (numpy.array([1, 2]), numpy.array([3, 6])).
1807
1808        :returns: A float, the RMSD between self and other_mol.
1809        """
1810
1811        return self.other_molecules.get_rmsd_equivalent_atoms_specified(
1812            other_mol, tethers
1813        )
1814
1815    def get_rmsd_order_dependent(self, other_mol):
1816        """
1817        Calculates the RMSD between two structures, where equivalent atoms
1818        are listed in the same order.
1819
1820        Wrapper function for :meth:`~scoria.OtherMolecules.OtherMolecules.get_rmsd_order_dependent`
1821
1822        :param scoria.Molecule other_mol: The other scoria.Molecule object.
1823
1824        :returns: A float, the RMSD between self and other_mol.
1825        """
1826
1827        return self.other_molecules.get_rmsd_order_dependent(other_mol)
1828
1829    def get_rmsd_heuristic(self, other_mol):
1830        """
1831        Caluclates the RMSD between two identical molecules with different
1832        conformations, per the definition given in "AutoDock Vina: Improving
1833        the speed and accuracy of docking with a new scoring function,
1834        efficient optimization, and multithreading,"" by Oleg Trott and Arthur
1835        J. Olson. Note: Identical means the order of the atoms is the same as
1836        well.
1837
1838        Requires the :any:`numpy` library.
1839
1840        Wrapper function for :meth:`~scoria.OtherMolecules.OtherMolecules.get_rmsd_heuristic`
1841
1842        :param scoria.Molecule other_mol: The other scoria.Molecule object.
1843
1844        :returns: A float, the RMSD between self and other_mol.
1845        """
1846
1847        return self.other_molecules.get_rmsd_heuristic(other_mol)
1848
1849    def steric_clash_with_another_molecules(self, other_mol, cutoff,
1850                                           pairwise_comparison = True):
1851        """
1852        DEPRECATION WARNING: Please use :meth:`~scoria.Molecule.Molecule.steric_clash_with_another_molecule`
1853        Detects steric clashes between the scoria.Molecule (self) and
1854        another scoria.Molecule.
1855
1856        Requires the :any:`numpy` and :any:`scipy<scipy.spatial>` libraries.
1857
1858        Wrapper function for :meth:`~scoria.OtherMolecules.OtherMolecules.steric_clash_with_another_molecule`
1859
1860        :param scoria.Molecule other_mol: The scoria.Molecule object that will be
1861                    evaluated for steric clashes.
1862        :param float cutoff: A float, the user-defined distance cutoff in
1863                    Angstroms.
1864        :param bool pairwise_comparison: An optional boolean, whether or not to
1865                    perform a simple pairwise distance comparison (if True) or
1866                    to use a more sophisitcated method (if False). True by
1867                    default.
1868
1869        :returns: A boolean. True if steric clashes are present, False if they
1870                    are not.
1871        """
1872
1873        return self.other_molecules.steric_clash_with_another_molecule(
1874            other_mol, cutoff, pairwise_comparison
1875        )
1876
1877    def steric_clash_with_another_molecule(self, other_mol, cutoff,
1878                                           pairwise_comparison = True):
1879        """
1880        Detects steric clashes between the scoria.Molecule (self) and
1881        another scoria.Molecule.
1882
1883        Requires the :any:`numpy` and :any:`scipy<scipy.spatial>` libraries.
1884
1885        Wrapper function for :meth:`~scoria.OtherMolecules.OtherMolecules.steric_clash_with_another_molecule`
1886
1887        :param scoria.Molecule other_mol: The scoria.Molecule object that will be
1888                    evaluated for steric clashes.
1889        :param float cutoff: A float, the user-defined distance cutoff in
1890                    Angstroms.
1891        :param bool pairwise_comparison: An optional boolean, whether or not to
1892                    perform a simple pairwise distance comparison (if True) or
1893                    to use a more sophisitcated method (if False). True by
1894                    default.
1895
1896        :returns: A boolean. True if steric clashes are present, False if they
1897                    are not.
1898        """
1899
1900        return self.other_molecules.steric_clash_with_another_molecule(
1901            other_mol, cutoff, pairwise_comparison
1902        )
1903
1904    def merge_with_another_molecules(self, other_molecules):
1905        """
1906        DEPRECATION WARNING: Please use :meth:`~scoria.Molecule.Molecule.merge_with_another_molecule`
1907        Merges two molecular models into a single model.
1908
1909        Wrapper function for :meth:`~scoria.OtherMolecules.OtherMolecules.merge_with_another_molecule`
1910
1911        :param scoria.Molecule other_molecules: A molecular model (scoria.Molecule
1912                    object).
1913
1914        :returns: A single scoria.Molecule object containing the atoms of
1915                    this model combined with the atoms of other_molecules.
1916        """
1917
1918        return self.other_molecules.merge_with_another_molecule(other_molecules)
1919
1920    def merge_with_another_molecule(self, other_molecules):
1921        """
1922        Merges two molecular models into a single model.
1923
1924        Wrapper function for :meth:`~scoria.OtherMolecules.OtherMolecules.merge_with_another_molecule`
1925
1926        :param scoria.Molecule other_molecules: A molecular model (scoria.Molecule
1927                    object).
1928
1929        :returns: A single scoria.Molecule object containing the atoms of
1930                    this model combined with the atoms of other_molecules.
1931        """
1932
1933        return self.other_molecules.merge_with_another_molecule(other_molecules)
1934
1935    ######## Supporting functions ########
1936
1937    def numpy_structured_array_remove_field(self, narray, field_names):
1938        """
1939        Removes a specific field name from a structured numpy array.
1940
1941        :param numpy.array narray: A structured numpy array.
1942        :param list(str) field_names: A list of strings, where each string is one of
1943                the field names of narray.
1944
1945        :returns: A structured numpy array identical to narray, but with the
1946                field names in field_names removed.
1947        """
1948
1949        # surprised this doesn't come with numpy
1950
1951        # now remove the coordinates from the atom_information object to save
1952        # memory
1953        names = list(narray.dtype.names)
1954        for f in field_names:
1955            names.remove(f)
1956
1957        return narray[names]
1958
1959    def __is_number(self, s):
1960        """
1961        Determines whether or not a string represents a number.
1962
1963        :param str s: A string (e.g., "5.4").
1964
1965        :returns: A boolean, whether or not the string can be represented by a
1966                    float.
1967        """
1968
1969        try:
1970            float(s)
1971            return True
1972        except ValueError:
1973            return False
1974
1975    def copy(self):
1976        """
1977        Returns an exact copy (scoria.Molecule) of this Molecule object.
1978        Undo points are NOT copied.
1979
1980        :returns: A scoria.Molecule, containing to the same atomic
1981                    information as this scoria.Molecule object.
1982        """
1983
1984#        new_molecule = Molecule()
1985#        new_molecule.set_filename(self.get_filename()[:])
1986#        new_molecule.set_remarks(self.get_remarks()[:])
1987#        new_molecule.set_atom_information(self.get_atom_information().copy())
1988#        new_molecule.set_trajectory_coordinates(self.get_trajectory_coordinates().copy())
1989#
1990#        if not self.get_bonds() is None:
1991#            new_molecule.set_bonds(self.get_bonds().copy())
1992#        else:
1993#            new_molecule.set_bonds(None)
1994#
1995#        new_molecule.set_hierarchy(copy.deepcopy(self.get_hierarchy()))
1996
1997        new_molecule = copy.deepcopy(self)
1998
1999        return new_molecule
2000