Exemplo n.º 1
0
class Relax_data_store(dict):
    """The relax data storage object."""

    # The current data pipe.
    current_pipe = None
    builtins.cdp = None

    # Class variable for storing the class instance.
    instance = None

    def __new__(self, *args, **kargs):
        """Replacement function for implementing the singleton design pattern."""

        # First initialisation.
        if self.instance is None:
            # Create a new instance.
            self.instance = dict.__new__(self, *args, **kargs)

            # Add some initial structures.
            self.instance.pipe_bundles = {}
            self.instance.relax_gui = Gui()

        # Already initialised, so return the instance.
        return self.instance


    def __repr__(self):
        """The string representation of the object.

        Rather than using the standard Python conventions (either the string representation of the
        value or the "<...desc...>" notation), a rich-formatted description of the object is given.
        """

        # Intro text.
        text = "The relax data storage object.\n"

        # The data pipes.
        text = text + "\n"
        text = text + "Data pipes:\n"
        pipes = sorted(self.instance.keys())
        if pipes:
            for pipe in pipes:
                text = text + "  %s\n" % repr(pipe)
        else:
            text = text + "  None\n"

        # Data store objects.
        text = text + "\n"
        text = text + "Data store objects:\n"
        names = sorted(self.__class__.__dict__.keys())
        for name in names:
            # The object.
            obj = getattr(self, name)

            # The text.
            if obj == None or isinstance(obj, str):
                text = text + "  %s %s: %s\n" % (name, type(obj), obj)
            else:
                text = text + "  %s %s: %s\n" % (name, type(obj), obj.__doc__.split('\n')[0])

        # dict methods.
        text = text + "\n"
        text = text + "Inherited dictionary methods:\n"
        for name in dir(dict):
            # Skip special methods.
            if search("^_", name):
                continue

            # Skip overwritten methods.
            if name in self.__class__.__dict__:
                continue

            # The object.
            obj = getattr(self, name)

            # The text.
            text = text + "  %s %s: %s\n" % (name, type(obj), obj.__doc__.split('\n')[0])

        # All other objects.
        text = text + "\n"
        text = text + "All other objects:\n"
        for name in dir(self):
            # Skip special methods.
            if search("^_", name):
                continue

            # Skip overwritten methods.
            if name in self.__class__.__dict__:
                continue

            # Skip dictionary methods.
            if name in dir(dict):
                continue

            # The object.
            obj = getattr(self, name)

            # The text.
            text = text + "  %s %s: %s\n" % (name, type(obj), obj)

        # Return the text.
        return text


    def __reset__(self):
        """Delete all the data from the relax data storage object.

        This method is to make the current single instance of the Data object identical to a newly
        created instance of Data, hence resetting the relax program state.
        """

        # Loop over the keys of self.__dict__ and delete the corresponding object.
        keys = list(self.__dict__.keys())
        for key in keys:
            # Delete the object.
            del self.__dict__[key]

        # Remove all items from the dictionary.
        self.instance.clear()

        # Reset the current data pipe.
        builtins.cdp = None

        # Recreate the pipe bundle object.
        self.instance.pipe_bundles = {}

        # Re-add the GUI object.
        self.instance.relax_gui = Gui()

        # Signal the change.
        status.observers.reset.notify()
        status.observers.pipe_alteration.notify()


    def _back_compat_hook(self, file_version=None, pipes=None):
        """Method for converting the old data structures to the new ones.

        @keyword file_version:  The relax XML version of the XML file.
        @type file_version:     int
        @keyword pipes:         The list of new pipe names to update.
        @type pipes:            list of str
        """

        # Loop over the new data pipes.
        for pipe_name in pipes:
            # The data pipe object.
            dp = self[pipe_name]

            # Convert the molecule-residue-spin data.
            for mol in dp.mol:
                # Loop over the residues.
                for res in mol.res:
                    # Loop over the spins.
                    for spin in res.spin:
                        # The list of objects to remove at the end.
                        eliminate = []

                        # The current spin ID.
                        spin_id = pipe_control.mol_res_spin.generate_spin_id_unique(pipe_cont=dp, mol=mol, res=res, spin=spin)

                        # Rename the old peak intensity data structures.
                        if hasattr(spin, 'intensities'):
                            spin.peak_intensity = spin.intensities
                            eliminate.append('intensities')
                        if hasattr(spin, 'intensity_err'):
                            spin.peak_intensity_err = spin.intensity_err
                            eliminate.append('intensity_err')
                        if hasattr(spin, 'intensity_sim'):
                            spin.peak_intensity_sim = spin.intensity_sim
                            eliminate.append('intensity_sim')
                        if hasattr(spin, 'sim_intensity'):
                            spin.peak_intensity_sim = spin.sim_intensity
                            eliminate.append('sim_intensity')
                        if hasattr(spin, 'intensity_bc'):
                            spin.peak_intensity_bc = spin.intensity_bc
                            eliminate.append('intensity_bc')

                        # Convert proton spins (the 'heteronuc_type' variable indicates a pre-interatomic container design state).
                        if hasattr(spin, 'heteronuc_type') and hasattr(spin, 'element') and spin.element == 'H':
                            # Rename the nuclear isotope.
                            spin.isotope = spin.proton_type

                            # Append the old structures to be eliminated.
                            eliminate.append('proton_type')

                        # Convert heteronuclear spins (the 'heteronuc_type' variable indicates a pre-interatomic container design state).
                        elif hasattr(spin, 'heteronuc_type'):
                            # Rename the nuclear isotope.
                            spin.isotope = spin.heteronuc_type

                            # Name the spin if needed.
                            if spin.name == None:
                                if search('N', spin.isotope):
                                    pipe_control.mol_res_spin.name_spin(spin_id=spin_id, name='N', pipe=pipe_name)
                                elif search('C', spin.isotope):
                                    pipe_control.mol_res_spin.name_spin(spin_id=spin_id, name='C', pipe=pipe_name)

                            # An attached proton - convert into a spin container.
                            if (hasattr(spin, 'attached_proton') and spin.attached_proton != None) or (hasattr(spin, 'proton_type') and spin.proton_type != None):
                                # The proton name.
                                if hasattr(spin, 'attached_proton') and spin.attached_proton != None:
                                    proton_name = spin.attached_proton
                                else:
                                    proton_name = 'H'

                                # The two spin IDs (newly regenerated due to the above renaming).
                                spin_id1 = pipe_control.mol_res_spin.generate_spin_id_unique(pipe_cont=dp, mol=mol, res=res, spin=spin)
                                spin_id2 = pipe_control.mol_res_spin.generate_spin_id_unique(pipe_cont=dp, mol=mol, res=res, spin_name=proton_name)

                                # Fetch the proton spin if it exists.
                                h_spin = pipe_control.mol_res_spin.return_spin(spin_id2, pipe=pipe_name)
                                if h_spin:
                                    spin_id2 = pipe_control.mol_res_spin.generate_spin_id_unique(pipe_cont=dp, mol=mol, res=res, spin_name=proton_name, spin_num=h_spin.num)

                                # Create a new spin container for the proton if needed.
                                if not h_spin:
                                    h_spin = pipe_control.mol_res_spin.create_spin(mol_name=mol.name, res_num=res.num, res_name=res.name, spin_name=proton_name, pipe=pipe_name)
                                    h_spin.select = False

                                # Set up a dipole interaction between the two spins if needed.
                                if not hasattr(h_spin, 'element'):
                                    pipe_control.mol_res_spin.set_spin_element(spin_id=spin_id2, element='H', pipe=pipe_name)
                                if not hasattr(h_spin, 'isotope'):
                                    pipe_control.mol_res_spin.set_spin_isotope(spin_id=spin_id2, isotope='1H', pipe=pipe_name)
                                pipe_control.interatomic.define(spin_id1, spin_id2, verbose=False, pipe=pipe_name)

                                # Get the interatomic data container.
                                interatom = pipe_control.interatomic.return_interatom(spin_id1=spin_id1, spin_id2=spin_id2, pipe=pipe_name)

                                # Set the interatomic distance.
                                if hasattr(spin, 'r'):
                                    interatom.r = spin.r

                                # Set the interatomic unit vectors.
                                if hasattr(spin, 'xh_vect'):
                                    interatom.vector = spin.xh_vect

                                # Set the RDC values.
                                if hasattr(spin, 'rdc'):
                                    interatom.rdc = spin.rdc
                                if hasattr(spin, 'rdc_err'):
                                    interatom.rdc_err = spin.rdc_err
                                if hasattr(spin, 'rdc_sim'):
                                    interatom.rdc_sim = spin.rdc_sim
                                if hasattr(spin, 'rdc_bc'):
                                    interatom.rdc_bc = spin.rdc_bc

                                # Append the old structures to be eliminated.
                                eliminate += ['heteronuc_type', 'proton_type', 'attached_proton', 'r', 'r_err', 'r_sim', 'rdc', 'rdc_err', 'rdc_sim', 'rdc_bc', 'xh_vect']

                        # Delete the old structures.
                        for name in eliminate:
                            if hasattr(spin, name):
                                delattr(spin, name)

            # Conversions for the interatomic data containers.
            if hasattr(dp, 'interatomic'):
                for interatom in dp.interatomic:
                    # RDC data.
                    if hasattr(interatom, 'rdc') and not hasattr(interatom, 'rdc_data_types'):
                        # Initialise.
                        interatom.rdc_data_types = {}

                        # Add the data type, assumed to be 'D', for each alignment ID.
                        for id in dp.rdc_ids:
                            interatom.rdc_data_types[id] = 'D'

            # Convert the alignment tensors.
            if hasattr(dp, 'align_tensors'):
                for i in range(len(dp.align_tensors)):
                    # Fix for the addition of the alignment ID structure as opposed to the tensor name or tag.
                    if not hasattr(dp.align_tensors[i], 'align_id'):
                        dp.align_tensors[i].set('align_id', dp.align_tensors[i].name)

            # Convert spectrometer frequency information.
            if hasattr(dp, 'frq'):
                # Convert to the new structure.
                dp.spectrometer_frq = dp.frq
                del dp.frq

                # Build the new frequency list structure.
                dp.spectrometer_frq_list = []
                for frq in list(dp.spectrometer_frq.values()):
                    if frq not in dp.spectrometer_frq_list:
                        dp.spectrometer_frq_list.append(frq)

                # And finally count the elements and sort the list.
                dp.spectrometer_frq_count = len(dp.spectrometer_frq_list)
                dp.spectrometer_frq_list.sort()

            # Convert the Sobol' integration information.
            if hasattr(dp, 'num_int_pts'):
                # Convert to the new structure.
                dp.sobol_max_points = dp.num_int_pts
                del dp.num_int_pts

                # Add the oversampling variable.
                cdp.sobol_oversample = 1

            # PCS Q factor conversions.
            if hasattr(dp, 'q_factors_pcs'):
                dp.q_factors_pcs_norm_squared_sum = dp.q_factors_pcs
                del dp.q_factors_pcs
            if hasattr(dp, 'q_pcs'):
                dp.q_pcs_norm_squared_sum = dp.q_pcs
                del dp.q_pcs

            # RDC Q factor conversions.
            if hasattr(dp, 'q_factors_rdc'):
                dp.q_factors_rdc_norm_tensor_size = dp.q_factors_rdc
                del dp.q_factors_rdc
            if hasattr(dp, 'q_rdc'):
                dp.q_rdc_norm_tensor_size = dp.q_rdc
                del dp.q_rdc
            if hasattr(dp, 'q_factors_rdc_norm2'):
                dp.q_factors_rdc_norm_squared_sum = dp.q_factors_rdc_norm2
                del dp.q_factors_rdc_norm2
            if hasattr(dp, 'q_rdc_norm2'):
                dp.q_rdc_norm_squared_sum = dp.q_rdc_norm2
                del dp.q_rdc_norm2


    def add(self, pipe_name, pipe_type, bundle=None, switch=True):
        """Method for adding a new data pipe container to the dictionary.

        This method should be used rather than importing the PipeContainer class and using the statement 'D[pipe] = PipeContainer()', where D is the relax data storage object and pipe is the name of the data pipe.

        @param pipe_name:   The name of the new data pipe.
        @type pipe_name:    str
        @param pipe_type:   The data pipe type.
        @type pipe_type:    str
        @keyword bundle:    The optional data pipe bundle to associate the data pipe with.
        @type bundle:       str or None
        @keyword switch:    A flag which if True will cause the new data pipe to be set to the current data pipe.
        @type switch:       bool
        """

        # Test if the pipe already exists.
        if pipe_name in self.instance:
            raise RelaxPipeError(pipe_name)

        # Create a new container.
        self[pipe_name] = PipeContainer()

        # Add the data pipe type string to the container.
        self[pipe_name].pipe_type = pipe_type

        # The pipe bundle.
        if bundle:
            # A new bundle.
            if bundle not in self.pipe_bundles:
                self.pipe_bundles[bundle] = []

            # Add the pipe to the bundle.
            self.pipe_bundles[bundle].append(pipe_name)

        # Change the current data pipe.
        if switch:
            # Set the current data pipe.
            self.instance.current_pipe = pipe_name
            builtins.cdp = self[pipe_name]

            # Signal the switch.
            status.observers.pipe_alteration.notify()


    def is_empty(self, verbosity=False):
        """Method for testing if the relax data store is empty.

        @keyword verbosity: A flag which if True will cause messages to be printed to STDERR.
        @type verbosity:    bool
        @return:            True if the data store is empty, False otherwise.
        @rtype:             bool
        """

        # No pipes should exist.
        if len(self):
            if verbosity:
                stderr.write("The relax data store contains the data pipes %s.\n" % sorted(self.keys()))
            return False

        # Objects which should be in here.
        blacklist = [
            'pipe_bundles',
            'relax_gui'
        ]

        # An object has been added to the data store.
        for name in dir(self):
            # Skip the data store methods.
            if name in self.__class__.__dict__:
                continue

            # Skip the dict methods.
            if name in dict.__dict__:
                continue

            # Skip special objects.
            if search("^__", name):
                continue

            # Blacklisted objects to skip.
            if name in blacklist:
                continue

            # An object has been added.
            if verbosity:
                stderr.write("The relax data store contains the object %s.\n" % name)
            return False

        # The data store is empty.
        return True


    def from_xml(self, file, dir=None, pipe_to=None, verbosity=1):
        """Parse a XML document representation of a data pipe, and load it into the relax data store.

        @param file:                The open file object.
        @type file:                 file
        @keyword dir:               The name of the directory containing the results file (needed for loading external files).
        @type dir:                  str
        @keyword pipe_to:           The data pipe to load the XML data pipe into (the file must only contain one data pipe).
        @type pipe_to:              str
        @keyword verbosity:         A flag specifying the amount of information to print.  The higher the value, the greater the verbosity.
        @type verbosity:            int
        @raises RelaxError:         If pipe_to is given and the file contains multiple pipe elements;  or if the data pipes in the XML file already exist in the relax data store;  or if the data pipe type is invalid;  or if the target data pipe is not empty.
        @raises RelaxNoPipeError:   If pipe_to is given but the data pipe does not exist.
        @raises RelaxError:         If the data pipes in the XML file already exist in the relax data store, or if the data pipe type is invalid.
        @raises RelaxPipeError:     If the data pipes of the XML file are already present in the relax data store.
        """

        # Create the XML document from the file.
        doc = xml.dom.minidom.parse(file)

        # Get the relax node.
        relax_node = doc.childNodes[0]

        # Get the relax version of the XML file.
        file_version = relax_node.getAttribute('file_version')
        if file_version == '':
            file_version = 1
        else:
            file_version = int(file_version)

        # Get the pipe nodes.
        pipe_nodes = relax_node.getElementsByTagName('pipe')

        # Structure for the names of the new pipes.
        pipes = []

        # Target loading to a specific pipe (for pipe results reading).
        if pipe_to:
            # Check if there are multiple pipes in the XML file.
            if len(pipe_nodes) > 1:
                raise RelaxError("The pipe_to target pipe argument '%s' cannot be given as the file contains multiple pipe elements." % pipe_to)

            # The pipe type.
            pipe_type = pipe_nodes[0].getAttribute('type')

            # Check that the pipe already exists.
            if not pipe_to in self:
                raise RelaxNoPipeError(pipe_to)

            # Check if the pipe type matches.
            if pipe_type != self[pipe_to].pipe_type:
                raise RelaxError("The XML file pipe type '%s' does not match the pipe type '%s'" % (pipe_type, self[pipe_to].pipe_type))

            # Check if the pipe is empty.
            if not self[pipe_to].is_empty():
                raise RelaxError("The data pipe '%s' is not empty." % pipe_to)

            # Load the data.
            self[pipe_to].from_xml(pipe_nodes[0], dir=dir, file_version=file_version)

            # Store the pipe name.
            pipes.append(pipe_to)

        # Load the state.
        else:
            # Get the GUI nodes.
            gui_nodes = relax_node.getElementsByTagName('relax_gui')
            if gui_nodes:
                self.relax_gui.from_xml(gui_nodes[0], file_version=file_version)

            # Get the sequence alignment nodes.
            seq_align_nodes = relax_node.getElementsByTagName('sequence_alignments')
            if seq_align_nodes:
                # Initialise the object.
                self.sequence_alignments = Sequence_alignments()

                # Populate it.
                self.sequence_alignments.from_xml(seq_align_nodes[0], file_version=file_version)

            # Recreate all the data store data structures.
            xml_to_object(relax_node, self, file_version=file_version, blacklist=['pipe', 'relax_gui', 'sequence_alignments'])

            # Checks.
            for pipe_node in pipe_nodes:
                # The pipe name and type.
                pipe_name = str(pipe_node.getAttribute('name'))
                pipe_type = pipe_node.getAttribute('type')

                # Existence check.
                if pipe_name in self:
                    raise RelaxPipeError(pipe_name)

                # Valid type check.
                if not pipe_type in pipe_control.pipes.VALID_TYPES:
                    raise RelaxError("The data pipe type '%s' is invalid and must be one of the strings in the list %s." % (pipe_type, pipe_control.pipes.VALID_TYPES))

            # Load the data pipes.
            for pipe_node in pipe_nodes:
                # The pipe name and type.
                pipe_name = str(pipe_node.getAttribute('name'))
                pipe_type = pipe_node.getAttribute('type')

                # Add the data pipe.
                switch = False
                if self.current_pipe == None:
                    switch = True
                self.add(pipe_name, pipe_type, switch=switch)

                # Fill the pipe.
                self[pipe_name].from_xml(pipe_node, file_version=file_version, dir=dir)

                # Store the pipe name.
                pipes.append(pipe_name)

            # Set the current pipe.
            if self.current_pipe in self:
                builtins.cdp = self[self.current_pipe]

        # Finally update the molecule, residue, and spin metadata for each data pipe.
        for pipe in pipes:
            pipe_control.mol_res_spin.metadata_update(pipe=pipe)

        # Backwards compatibility transformations.
        self._back_compat_hook(file_version, pipes=pipes)


    def to_xml(self, file, pipes=None):
        """Create a XML document representation of the current data pipe.

        This method creates the top level XML document including all the information needed
        about relax, calls the PipeContainer.xml_write() method to fill in the document contents,
        and writes the XML into the file object.

        @param file:        The open file object.
        @type file:         file
        @param pipes:       The name of the pipe, or list of pipes to place in the XML file.
        @type pipes:        str or list of str
        """

        # The pipes to include in the XML file.
        all = False
        if not pipes:
            all = True
            pipes = list(self.keys())
        elif isinstance(pipes, str):
            pipes = [pipes]

        # Sort the pipes.
        pipes.sort()

        # Create the XML document object.
        xmldoc = xml.dom.minidom.Document()

        # Create the top level element, including the relax URL.
        top_element = xmldoc.createElementNS('http://www.nmr-relax.com', 'relax')
        top_element.setAttribute("xmlns", "http://www.nmr-relax.com")

        # Append the element.
        xmldoc.appendChild(top_element)

        # Set the relax version number, and add a creation time.
        top_element.setAttribute('version', version.version)
        top_element.setAttribute('time', asctime())
        top_element.setAttribute('file_version', "2")
        if version.repo_revision:
            top_element.setAttribute('revision', version.repo_revision)
        if version.repo_url:
            top_element.setAttribute('url', version.repo_url)

        # Add all objects in the data store base object to the XML element.
        if all:
            blacklist = list(self.__class__.__dict__.keys()) + list(dict.__dict__.keys())
            for name in dir(self):
                # Skip blacklisted objects.
                if name in blacklist:
                    continue

                # Skip special objects.
                if search('^_', name):
                    continue

                # Execute any to_xml() methods, and add that object to the blacklist.
                obj = getattr(self, name)
                if hasattr(obj, 'to_xml'):
                    obj.to_xml(xmldoc, top_element)
                    blacklist = blacklist + [name]

            # Remove the current data pipe from the blacklist!
            blacklist.remove('current_pipe')

            # Add all simple python objects within the store.
            fill_object_contents(xmldoc, top_element, object=self, blacklist=blacklist)

        # Loop over the pipes.
        for pipe in pipes:
            # Create the pipe XML element and add it to the top level XML element.
            pipe_element = xmldoc.createElement('pipe')
            top_element.appendChild(pipe_element)

            # Set the data pipe attributes.
            pipe_element.setAttribute('desc', 'The contents of a relax data pipe')
            pipe_element.setAttribute('name', pipe)
            pipe_element.setAttribute('type', self[pipe].pipe_type)

            # Fill the data pipe XML element.
            self[pipe].to_xml(xmldoc, pipe_element, pipe_type=self[pipe].pipe_type)

        # Write out the XML file.
        file.write(xmldoc.toprettyxml(indent='    '))
Exemplo n.º 2
0
    def from_xml(self, file, dir=None, pipe_to=None, verbosity=1):
        """Parse a XML document representation of a data pipe, and load it into the relax data store.

        @param file:                The open file object.
        @type file:                 file
        @keyword dir:               The name of the directory containing the results file (needed for loading external files).
        @type dir:                  str
        @keyword pipe_to:           The data pipe to load the XML data pipe into (the file must only contain one data pipe).
        @type pipe_to:              str
        @keyword verbosity:         A flag specifying the amount of information to print.  The higher the value, the greater the verbosity.
        @type verbosity:            int
        @raises RelaxError:         If pipe_to is given and the file contains multiple pipe elements;  or if the data pipes in the XML file already exist in the relax data store;  or if the data pipe type is invalid;  or if the target data pipe is not empty.
        @raises RelaxNoPipeError:   If pipe_to is given but the data pipe does not exist.
        @raises RelaxError:         If the data pipes in the XML file already exist in the relax data store, or if the data pipe type is invalid.
        @raises RelaxPipeError:     If the data pipes of the XML file are already present in the relax data store.
        """

        # Create the XML document from the file.
        doc = xml.dom.minidom.parse(file)

        # Get the relax node.
        relax_node = doc.childNodes[0]

        # Get the relax version of the XML file.
        file_version = relax_node.getAttribute('file_version')
        if file_version == '':
            file_version = 1
        else:
            file_version = int(file_version)

        # Get the pipe nodes.
        pipe_nodes = relax_node.getElementsByTagName('pipe')

        # Structure for the names of the new pipes.
        pipes = []

        # Target loading to a specific pipe (for pipe results reading).
        if pipe_to:
            # Check if there are multiple pipes in the XML file.
            if len(pipe_nodes) > 1:
                raise RelaxError("The pipe_to target pipe argument '%s' cannot be given as the file contains multiple pipe elements." % pipe_to)

            # The pipe type.
            pipe_type = pipe_nodes[0].getAttribute('type')

            # Check that the pipe already exists.
            if not pipe_to in self:
                raise RelaxNoPipeError(pipe_to)

            # Check if the pipe type matches.
            if pipe_type != self[pipe_to].pipe_type:
                raise RelaxError("The XML file pipe type '%s' does not match the pipe type '%s'" % (pipe_type, self[pipe_to].pipe_type))

            # Check if the pipe is empty.
            if not self[pipe_to].is_empty():
                raise RelaxError("The data pipe '%s' is not empty." % pipe_to)

            # Load the data.
            self[pipe_to].from_xml(pipe_nodes[0], dir=dir, file_version=file_version)

            # Store the pipe name.
            pipes.append(pipe_to)

        # Load the state.
        else:
            # Get the GUI nodes.
            gui_nodes = relax_node.getElementsByTagName('relax_gui')
            if gui_nodes:
                self.relax_gui.from_xml(gui_nodes[0], file_version=file_version)

            # Get the sequence alignment nodes.
            seq_align_nodes = relax_node.getElementsByTagName('sequence_alignments')
            if seq_align_nodes:
                # Initialise the object.
                self.sequence_alignments = Sequence_alignments()

                # Populate it.
                self.sequence_alignments.from_xml(seq_align_nodes[0], file_version=file_version)

            # Recreate all the data store data structures.
            xml_to_object(relax_node, self, file_version=file_version, blacklist=['pipe', 'relax_gui', 'sequence_alignments'])

            # Checks.
            for pipe_node in pipe_nodes:
                # The pipe name and type.
                pipe_name = str(pipe_node.getAttribute('name'))
                pipe_type = pipe_node.getAttribute('type')

                # Existence check.
                if pipe_name in self:
                    raise RelaxPipeError(pipe_name)

                # Valid type check.
                if not pipe_type in pipe_control.pipes.VALID_TYPES:
                    raise RelaxError("The data pipe type '%s' is invalid and must be one of the strings in the list %s." % (pipe_type, pipe_control.pipes.VALID_TYPES))

            # Load the data pipes.
            for pipe_node in pipe_nodes:
                # The pipe name and type.
                pipe_name = str(pipe_node.getAttribute('name'))
                pipe_type = pipe_node.getAttribute('type')

                # Add the data pipe.
                switch = False
                if self.current_pipe == None:
                    switch = True
                self.add(pipe_name, pipe_type, switch=switch)

                # Fill the pipe.
                self[pipe_name].from_xml(pipe_node, file_version=file_version, dir=dir)

                # Store the pipe name.
                pipes.append(pipe_name)

            # Set the current pipe.
            if self.current_pipe in self:
                builtins.cdp = self[self.current_pipe]

        # Finally update the molecule, residue, and spin metadata for each data pipe.
        for pipe in pipes:
            pipe_control.mol_res_spin.metadata_update(pipe=pipe)

        # Backwards compatibility transformations.
        self._back_compat_hook(file_version, pipes=pipes)
Exemplo n.º 3
0
    def setUp(self):
        """Set 'self.seq_align' to an empty instance of the Sequence_alignments class."""

        self.seq_align = Sequence_alignments()
Exemplo n.º 4
0
class Test_seq_align(TestCase):
    """Unit tests for the data.seq_align relax module."""


    def setUp(self):
        """Set 'self.seq_align' to an empty instance of the Sequence_alignments class."""

        self.seq_align = Sequence_alignments()


    def generate_ids(self, object_ids, models, molecules):
        """Generate the expected IDs."""

        # Generate the IDs.
        ids = []
        for i in range(len(object_ids)):
            ids.append("Object '%s'" % object_ids[i])
            if models[i] != None:
                ids[-1] += "; Model %i" % models[i]
            ids[-1] += "; Molecule '%s'" % molecules[i]

        # Return the IDs.
        return ids


    def return_align_data(self):
        """Return a data set for alignment testing."""

        # The data.
        object_ids = ['frame_order', 'ensemble', 'ensemble', 'ensemble', 'ensemble', 'ensemble', 'ensemble', 'ensemble']
        models = [None, 1, 1, 1, 1, 1, 1, 1]
        molecules = [
            'N-dom',
            'ensemble 4M A',
            'ensemble 4M B',
            'ensemble 4M C',
            'ensemble 4M D',
            'CaM-IQ A',
            'CaM-IQ B',
            'CaM-IQ C'
        ]
        sequences = [
            'LTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARK*****',
            '*DQLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKMKDTDSEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMTA*****',
            '*DQLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKMKDTDSEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMTA*****',
            '*DQLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKMKDTDSEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMTA*****',
            '*DQLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKMKDTDSEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMTA*****',
            '                                                                                                                           QLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKMKSEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMT*****',
            '                                                                                                                           LTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKMKDEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMT*****',
            '                                                                                                                           LTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMT*****',

            'TEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADG',
            'ADQLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKM',
            'LTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMAR'
        ]
        strings = [
            '---LTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARK------------------------------------------------------------------------*****----------------------------------------------------------------------------',
            '*DQLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARK----------------------------------------------------------------------------MKDTDSEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMTA*****',
            '*DQLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKMKDTDSEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMTA*****----------------------------------------------------------------------------',
            '*DQLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKMKDTDSEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMTA*****----------------------------------------------------------------------------',
            '*DQLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARKMKDTDSEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMTA*****----------------------------------------------------------------------------',
            '--QLTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARK----MKSEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMT*****----------------------------------------------------------------------------',
            '---LTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARK----MKDEEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMT*****----------------------------------------------------------------------------',
            '---LTEEQIAEFKEAFSLFDKDGDGTITTKELGTVMRSLGQNPTEAELQDMINEVDADGNGTIDFPEFLTMMARK-------EEEIREAFRVFDKDGNGYISAAELRHVMTNLGEKLTDEEVDEMIREADIDGDGQVNYEEFVQMMT*****----------------------------------------------------------------------------',
        ]
        gaps = []
        for i in range(len(strings)):
            gaps.append([])
            for j in range(len(strings[0])):
                if strings[i][j] == '-':
                    gaps[i].append(1)
                else:
                    gaps[i].append(0)
        msa_algorithm = 'Central Star'
        pairwise_algorithm = 'NW70'
        matrix = 'BLOSUM62'
        gap_open_penalty = 10.0
        gap_extend_penalty = 1.0
        end_gap_open_penalty = 0.0
        end_gap_extend_penalty = 0.0

        # Return the data.
        return object_ids, models, molecules, sequences, strings, gaps, msa_algorithm, pairwise_algorithm, matrix, gap_open_penalty, gap_extend_penalty, end_gap_open_penalty, end_gap_extend_penalty


    def test_alignment_addition(self):
        """Test the creation of a new sequence alignment object."""

        # The data.
        object_ids, models, molecules, sequences, strings, gaps, msa_algorithm, pairwise_algorithm, matrix, gap_open_penalty, gap_extend_penalty, end_gap_open_penalty, end_gap_extend_penalty = self.return_align_data()

        # Add the alignment.
        self.seq_align.add(object_ids=object_ids, models=models, molecules=molecules, sequences=sequences, strings=strings, gaps=gaps, msa_algorithm=msa_algorithm, pairwise_algorithm=pairwise_algorithm, matrix=matrix, gap_open_penalty=gap_open_penalty, gap_extend_penalty=gap_extend_penalty, end_gap_open_penalty=end_gap_open_penalty, end_gap_extend_penalty=end_gap_extend_penalty)

        # Generate the expected IDs.
        ids = self.generate_ids(object_ids, models, molecules)

        # Check the data.
        for i in range(8):
            print("Checking \"%s\"" % ids[i])
            self.assertEqual(self.seq_align[0].ids[i], ids[i])
            self.assertEqual(self.seq_align[0].object_ids[i], object_ids[i])
            self.assertEqual(self.seq_align[0].models[i], models[i])
            self.assertEqual(self.seq_align[0].molecules[i], molecules[i])
            self.assertEqual(self.seq_align[0].sequences[i], sequences[i])
            self.assertEqual(self.seq_align[0].strings[i], strings[i])
            self.assertEqual(self.seq_align[0].gaps[i], gaps[i])
            self.assertEqual(self.seq_align[0].msa_algorithm, msa_algorithm)
            self.assertEqual(self.seq_align[0].pairwise_algorithm, pairwise_algorithm)
            self.assertEqual(self.seq_align[0].matrix, matrix)
            self.assertEqual(self.seq_align[0].gap_open_penalty, gap_open_penalty)
            self.assertEqual(self.seq_align[0].gap_extend_penalty, gap_extend_penalty)
            self.assertEqual(self.seq_align[0].end_gap_open_penalty, end_gap_open_penalty)
            self.assertEqual(self.seq_align[0].end_gap_extend_penalty, end_gap_extend_penalty)


    def test_find_alignment(self):
        """Test the retrieval of pre-existing alignment."""

        # Execute the body of the test_alignment_addition() unit test to set up the object.
        self.test_alignment_addition()

        # The identifying data.
        object_ids, models, molecules, sequences, strings, gaps, msa_algorithm, pairwise_algorithm, matrix, gap_open_penalty, gap_extend_penalty, end_gap_open_penalty, end_gap_extend_penalty = self.return_align_data()

        # Retrieve the alignment.
        align = self.seq_align.find_alignment(object_ids=object_ids, models=models, molecules=molecules, sequences=sequences, msa_algorithm=msa_algorithm, pairwise_algorithm=pairwise_algorithm, matrix=matrix, gap_open_penalty=gap_open_penalty, gap_extend_penalty=gap_extend_penalty, end_gap_open_penalty=end_gap_open_penalty, end_gap_extend_penalty=end_gap_extend_penalty)

        # Check that something was returned.
        self.assertNotEqual(align, None)

        # Generate the expected IDs.
        ids = self.generate_ids(object_ids, models, molecules)

        # Check some of the data.
        for i in range(8):
            print("Checking \"%s\"" % ids[i])
            self.assertEqual(self.seq_align[0].object_ids[i], object_ids[i])
            self.assertEqual(self.seq_align[0].models[i], models[i])
            self.assertEqual(self.seq_align[0].molecules[i], molecules[i])


    def test_find_missing_alignment(self):
        """Test the retrieval of non-existent alignment."""

        # Execute the body of the test_alignment_addition() unit test to set up the object.
        self.test_alignment_addition()

        # The identifying data.
        object_ids, models, molecules, sequences, strings, gaps, msa_algorithm, pairwise_algorithm, matrix, gap_open_penalty, gap_extend_penalty, end_gap_open_penalty, end_gap_extend_penalty = self.return_align_data()

        # Change a gap penalty.
        gap_open_penalty = 0.5

        # Retrieve the alignment.
        align = self.seq_align.find_alignment(object_ids=object_ids, models=models, molecules=molecules, sequences=sequences, msa_algorithm=msa_algorithm, pairwise_algorithm=pairwise_algorithm, matrix=matrix, gap_open_penalty=gap_open_penalty, gap_extend_penalty=gap_extend_penalty, end_gap_open_penalty=end_gap_open_penalty, end_gap_extend_penalty=end_gap_extend_penalty)

        # Check that nothing was returned.
        self.assertEqual(align, None)
Exemplo n.º 5
0
    def from_xml(self, file, dir=None, pipe_to=None, verbosity=1):
        """Parse a XML document representation of a data pipe, and load it into the relax data store.

        @param file:                The open file object.
        @type file:                 file
        @keyword dir:               The name of the directory containing the results file (needed for loading external files).
        @type dir:                  str
        @keyword pipe_to:           The data pipe to load the XML data pipe into (the file must only contain one data pipe).
        @type pipe_to:              str
        @keyword verbosity:         A flag specifying the amount of information to print.  The higher the value, the greater the verbosity.
        @type verbosity:            int
        @raises RelaxError:         If pipe_to is given and the file contains multiple pipe elements;  or if the data pipes in the XML file already exist in the relax data store;  or if the data pipe type is invalid;  or if the target data pipe is not empty.
        @raises RelaxNoPipeError:   If pipe_to is given but the data pipe does not exist.
        @raises RelaxError:         If the data pipes in the XML file already exist in the relax data store, or if the data pipe type is invalid.
        @raises RelaxPipeError:     If the data pipes of the XML file are already present in the relax data store.
        """

        # Create the XML document from the file.
        doc = xml.dom.minidom.parse(file)

        # Get the relax node.
        relax_node = doc.childNodes[0]

        # Get the relax version of the XML file.
        file_version = relax_node.getAttribute('file_version')
        if file_version == '':
            file_version = 1
        else:
            file_version = int(file_version)

        # Get the pipe nodes.
        pipe_nodes = relax_node.getElementsByTagName('pipe')

        # Structure for the names of the new pipes.
        pipes = []

        # Target loading to a specific pipe (for pipe results reading).
        if pipe_to:
            # Check if there are multiple pipes in the XML file.
            if len(pipe_nodes) > 1:
                raise RelaxError(
                    "The pipe_to target pipe argument '%s' cannot be given as the file contains multiple pipe elements."
                    % pipe_to)

            # The pipe type.
            pipe_type = pipe_nodes[0].getAttribute('type')

            # Check that the pipe already exists.
            if not pipe_to in self:
                raise RelaxNoPipeError(pipe_to)

            # Check if the pipe type matches.
            if pipe_type != self[pipe_to].pipe_type:
                raise RelaxError(
                    "The XML file pipe type '%s' does not match the pipe type '%s'"
                    % (pipe_type, self[pipe_to].pipe_type))

            # Check if the pipe is empty.
            if not self[pipe_to].is_empty():
                raise RelaxError("The data pipe '%s' is not empty." % pipe_to)

            # Load the data.
            self[pipe_to].from_xml(pipe_nodes[0],
                                   dir=dir,
                                   file_version=file_version)

            # Store the pipe name.
            pipes.append(pipe_to)

        # Load the state.
        else:
            # Get the GUI nodes.
            gui_nodes = relax_node.getElementsByTagName('relax_gui')
            if gui_nodes:
                self.relax_gui.from_xml(gui_nodes[0],
                                        file_version=file_version)

            # Get the sequence alignment nodes.
            seq_align_nodes = relax_node.getElementsByTagName(
                'sequence_alignments')
            if seq_align_nodes:
                # Initialise the object.
                self.sequence_alignments = Sequence_alignments()

                # Populate it.
                self.sequence_alignments.from_xml(seq_align_nodes[0],
                                                  file_version=file_version)

            # Recreate all the data store data structures.
            xml_to_object(
                relax_node,
                self,
                file_version=file_version,
                blacklist=['pipe', 'relax_gui', 'sequence_alignments'])

            # Checks.
            for pipe_node in pipe_nodes:
                # The pipe name and type.
                pipe_name = str(pipe_node.getAttribute('name'))
                pipe_type = pipe_node.getAttribute('type')

                # Existence check.
                if pipe_name in self:
                    raise RelaxPipeError(pipe_name)

                # Valid type check.
                if not pipe_type in pipe_control.pipes.VALID_TYPES:
                    raise RelaxError(
                        "The data pipe type '%s' is invalid and must be one of the strings in the list %s."
                        % (pipe_type, pipe_control.pipes.VALID_TYPES))

            # Load the data pipes.
            for pipe_node in pipe_nodes:
                # The pipe name and type.
                pipe_name = str(pipe_node.getAttribute('name'))
                pipe_type = pipe_node.getAttribute('type')

                # Add the data pipe.
                switch = False
                if self.current_pipe == None:
                    switch = True
                self.add(pipe_name, pipe_type, switch=switch)

                # Fill the pipe.
                self[pipe_name].from_xml(pipe_node,
                                         file_version=file_version,
                                         dir=dir)

                # Store the pipe name.
                pipes.append(pipe_name)

            # Set the current pipe.
            if self.current_pipe in self:
                builtins.cdp = self[self.current_pipe]

        # Finally update the molecule, residue, and spin metadata for each data pipe.
        for pipe in pipes:
            pipe_control.mol_res_spin.metadata_update(pipe=pipe)

        # Backwards compatibility transformations.
        self._back_compat_hook(file_version, pipes=pipes)
Exemplo n.º 6
0
class Relax_data_store(dict):
    """The relax data storage object."""

    # The current data pipe.
    current_pipe = None
    builtins.cdp = None

    # Class variable for storing the class instance.
    instance = None

    def __new__(self, *args, **kargs):
        """Replacement function for implementing the singleton design pattern."""

        # First initialisation.
        if self.instance is None:
            # Create a new instance.
            self.instance = dict.__new__(self, *args, **kargs)

            # Add some initial structures.
            self.instance.pipe_bundles = {}
            self.instance.relax_gui = Gui()

        # Already initialised, so return the instance.
        return self.instance

    def __repr__(self):
        """The string representation of the object.

        Rather than using the standard Python conventions (either the string representation of the
        value or the "<...desc...>" notation), a rich-formatted description of the object is given.
        """

        # Intro text.
        text = "The relax data storage object.\n"

        # The data pipes.
        text = text + "\n"
        text = text + "Data pipes:\n"
        pipes = sorted(self.instance.keys())
        if pipes:
            for pipe in pipes:
                text = text + "  %s\n" % repr(pipe)
        else:
            text = text + "  None\n"

        # Data store objects.
        text = text + "\n"
        text = text + "Data store objects:\n"
        names = sorted(self.__class__.__dict__.keys())
        for name in names:
            # The object.
            obj = getattr(self, name)

            # The text.
            if obj == None or isinstance(obj, str):
                text = text + "  %s %s: %s\n" % (name, type(obj), obj)
            else:
                text = text + "  %s %s: %s\n" % (name, type(obj),
                                                 obj.__doc__.split('\n')[0])

        # dict methods.
        text = text + "\n"
        text = text + "Inherited dictionary methods:\n"
        for name in dir(dict):
            # Skip special methods.
            if search("^_", name):
                continue

            # Skip overwritten methods.
            if name in self.__class__.__dict__:
                continue

            # The object.
            obj = getattr(self, name)

            # The text.
            text = text + "  %s %s: %s\n" % (name, type(obj),
                                             obj.__doc__.split('\n')[0])

        # All other objects.
        text = text + "\n"
        text = text + "All other objects:\n"
        for name in dir(self):
            # Skip special methods.
            if search("^_", name):
                continue

            # Skip overwritten methods.
            if name in self.__class__.__dict__:
                continue

            # Skip dictionary methods.
            if name in dir(dict):
                continue

            # The object.
            obj = getattr(self, name)

            # The text.
            text = text + "  %s %s: %s\n" % (name, type(obj), obj)

        # Return the text.
        return text

    def __reset__(self):
        """Delete all the data from the relax data storage object.

        This method is to make the current single instance of the Data object identical to a newly
        created instance of Data, hence resetting the relax program state.
        """

        # Loop over the keys of self.__dict__ and delete the corresponding object.
        keys = list(self.__dict__.keys())
        for key in keys:
            # Delete the object.
            del self.__dict__[key]

        # Remove all items from the dictionary.
        self.instance.clear()

        # Reset the current data pipe.
        builtins.cdp = None

        # Recreate the pipe bundle object.
        self.instance.pipe_bundles = {}

        # Re-add the GUI object.
        self.instance.relax_gui = Gui()

        # Signal the change.
        status.observers.reset.notify()
        status.observers.pipe_alteration.notify()

    def _back_compat_hook(self, file_version=None, pipes=None):
        """Method for converting the old data structures to the new ones.

        @keyword file_version:  The relax XML version of the XML file.
        @type file_version:     int
        @keyword pipes:         The list of new pipe names to update.
        @type pipes:            list of str
        """

        # Loop over the new data pipes.
        for pipe_name in pipes:
            # The data pipe object.
            dp = self[pipe_name]

            # Convert the molecule-residue-spin data.
            for mol in dp.mol:
                # Loop over the residues.
                for res in mol.res:
                    # Loop over the spins.
                    for spin in res.spin:
                        # The list of objects to remove at the end.
                        eliminate = []

                        # The current spin ID.
                        spin_id = pipe_control.mol_res_spin.generate_spin_id_unique(
                            pipe_cont=dp, mol=mol, res=res, spin=spin)

                        # Rename the old peak intensity data structures.
                        if hasattr(spin, 'intensities'):
                            spin.peak_intensity = spin.intensities
                            eliminate.append('intensities')
                        if hasattr(spin, 'intensity_err'):
                            spin.peak_intensity_err = spin.intensity_err
                            eliminate.append('intensity_err')
                        if hasattr(spin, 'intensity_sim'):
                            spin.peak_intensity_sim = spin.intensity_sim
                            eliminate.append('intensity_sim')
                        if hasattr(spin, 'sim_intensity'):
                            spin.peak_intensity_sim = spin.sim_intensity
                            eliminate.append('sim_intensity')
                        if hasattr(spin, 'intensity_bc'):
                            spin.peak_intensity_bc = spin.intensity_bc
                            eliminate.append('intensity_bc')

                        # Convert proton spins (the 'heteronuc_type' variable indicates a pre-interatomic container design state).
                        if hasattr(spin, 'heteronuc_type') and hasattr(
                                spin, 'element') and spin.element == 'H':
                            # Rename the nuclear isotope.
                            spin.isotope = spin.proton_type

                            # Append the old structures to be eliminated.
                            eliminate.append('proton_type')

                        # Convert heteronuclear spins (the 'heteronuc_type' variable indicates a pre-interatomic container design state).
                        elif hasattr(spin, 'heteronuc_type'):
                            # Rename the nuclear isotope.
                            spin.isotope = spin.heteronuc_type

                            # Name the spin if needed.
                            if spin.name == None:
                                if search('N', spin.isotope):
                                    pipe_control.mol_res_spin.name_spin(
                                        spin_id=spin_id,
                                        name='N',
                                        pipe=pipe_name)
                                elif search('C', spin.isotope):
                                    pipe_control.mol_res_spin.name_spin(
                                        spin_id=spin_id,
                                        name='C',
                                        pipe=pipe_name)

                            # An attached proton - convert into a spin container.
                            if (hasattr(spin, 'attached_proton')
                                    and spin.attached_proton != None) or (
                                        hasattr(spin, 'proton_type')
                                        and spin.proton_type != None):
                                # The proton name.
                                if hasattr(spin, 'attached_proton'
                                           ) and spin.attached_proton != None:
                                    proton_name = spin.attached_proton
                                else:
                                    proton_name = 'H'

                                # The two spin IDs (newly regenerated due to the above renaming).
                                spin_id1 = pipe_control.mol_res_spin.generate_spin_id_unique(
                                    pipe_cont=dp, mol=mol, res=res, spin=spin)
                                spin_id2 = pipe_control.mol_res_spin.generate_spin_id_unique(
                                    pipe_cont=dp,
                                    mol=mol,
                                    res=res,
                                    spin_name=proton_name)

                                # Fetch the proton spin if it exists.
                                h_spin = pipe_control.mol_res_spin.return_spin(
                                    spin_id=spin_id2, pipe=pipe_name)
                                if h_spin:
                                    spin_id2 = pipe_control.mol_res_spin.generate_spin_id_unique(
                                        pipe_cont=dp,
                                        mol=mol,
                                        res=res,
                                        spin_name=proton_name,
                                        spin_num=h_spin.num)

                                # Create a new spin container for the proton if needed.
                                if not h_spin:
                                    h_spin = pipe_control.mol_res_spin.create_spin(
                                        mol_name=mol.name,
                                        res_num=res.num,
                                        res_name=res.name,
                                        spin_name=proton_name,
                                        pipe=pipe_name)[0]
                                    h_spin.select = False

                                # Set up a dipole interaction between the two spins if needed.
                                if not hasattr(h_spin, 'element'):
                                    pipe_control.mol_res_spin.set_spin_element(
                                        spin_id=spin_id2,
                                        element='H',
                                        pipe=pipe_name)
                                if not hasattr(h_spin, 'isotope'):
                                    pipe_control.mol_res_spin.set_spin_isotope(
                                        spin_id=spin_id2,
                                        isotope='1H',
                                        pipe=pipe_name)
                                pipe_control.interatomic.define_dipole_pair(
                                    spin_id1=spin_id1,
                                    spin_id2=spin_id2,
                                    spin1=spin,
                                    spin2=h_spin,
                                    verbose=False,
                                    pipe=pipe_name)

                                # Get the interatomic data container.
                                interatom = pipe_control.interatomic.return_interatom(
                                    spin_hash1=spin._hash,
                                    spin_hash2=h_spin._hash,
                                    pipe=pipe_name)

                                # Set the interatomic distance.
                                if hasattr(spin, 'r'):
                                    interatom.r = spin.r

                                # Set the interatomic unit vectors.
                                if hasattr(spin, 'xh_vect'):
                                    interatom.vector = spin.xh_vect

                                # Set the RDC values.
                                if hasattr(spin, 'rdc'):
                                    interatom.rdc = spin.rdc
                                if hasattr(spin, 'rdc_err'):
                                    interatom.rdc_err = spin.rdc_err
                                if hasattr(spin, 'rdc_sim'):
                                    interatom.rdc_sim = spin.rdc_sim
                                if hasattr(spin, 'rdc_bc'):
                                    interatom.rdc_bc = spin.rdc_bc

                                # Append the old structures to be eliminated.
                                eliminate += [
                                    'heteronuc_type', 'proton_type',
                                    'attached_proton', 'r', 'r_err', 'r_sim',
                                    'rdc', 'rdc_err', 'rdc_sim', 'rdc_bc',
                                    'xh_vect'
                                ]

                        # Delete the old structures.
                        for name in eliminate:
                            if hasattr(spin, name):
                                delattr(spin, name)

            # Conversions for the interatomic data containers.
            if hasattr(dp, 'interatomic'):
                for interatom in dp.interatomic:
                    # RDC data.
                    if hasattr(interatom, 'rdc') and not hasattr(
                            interatom, 'rdc_data_types'):
                        # Initialise.
                        interatom.rdc_data_types = {}

                        # Add the data type, assumed to be 'D', for each alignment ID.
                        for id in dp.rdc_ids:
                            interatom.rdc_data_types[id] = 'D'

            # Convert the alignment tensors.
            if hasattr(dp, 'align_tensors'):
                for i in range(len(dp.align_tensors)):
                    # Fix for the addition of the alignment ID structure as opposed to the tensor name or tag.
                    if not hasattr(dp.align_tensors[i], 'align_id'):
                        dp.align_tensors[i].set('align_id',
                                                dp.align_tensors[i].name)

            # Convert spectrometer frequency information.
            if hasattr(dp, 'frq'):
                # Convert to the new structure.
                dp.spectrometer_frq = dp.frq
                del dp.frq

                # Build the new frequency list structure.
                dp.spectrometer_frq_list = []
                for frq in list(dp.spectrometer_frq.values()):
                    if frq not in dp.spectrometer_frq_list:
                        dp.spectrometer_frq_list.append(frq)

                # And finally count the elements and sort the list.
                dp.spectrometer_frq_count = len(dp.spectrometer_frq_list)
                dp.spectrometer_frq_list.sort()

            # Convert the Sobol' integration information.
            if hasattr(dp, 'num_int_pts'):
                # Convert to the new structure.
                dp.sobol_max_points = dp.num_int_pts
                del dp.num_int_pts

                # Add the oversampling variable.
                cdp.sobol_oversample = 1

            # PCS Q factor conversions.
            if hasattr(dp, 'q_factors_pcs'):
                dp.q_factors_pcs_norm_squared_sum = dp.q_factors_pcs
                del dp.q_factors_pcs
            if hasattr(dp, 'q_pcs'):
                dp.q_pcs_norm_squared_sum = dp.q_pcs
                del dp.q_pcs

            # RDC Q factor conversions.
            if hasattr(dp, 'q_factors_rdc'):
                dp.q_factors_rdc_norm_tensor_size = dp.q_factors_rdc
                del dp.q_factors_rdc
            if hasattr(dp, 'q_rdc'):
                dp.q_rdc_norm_tensor_size = dp.q_rdc
                del dp.q_rdc
            if hasattr(dp, 'q_factors_rdc_norm2'):
                dp.q_factors_rdc_norm_squared_sum = dp.q_factors_rdc_norm2
                del dp.q_factors_rdc_norm2
            if hasattr(dp, 'q_rdc_norm2'):
                dp.q_rdc_norm_squared_sum = dp.q_rdc_norm2
                del dp.q_rdc_norm2

    def add(self, pipe_name, pipe_type, bundle=None, switch=True):
        """Method for adding a new data pipe container to the dictionary.

        This method should be used rather than importing the PipeContainer class and using the statement 'D[pipe] = PipeContainer()', where D is the relax data storage object and pipe is the name of the data pipe.

        @param pipe_name:   The name of the new data pipe.
        @type pipe_name:    str
        @param pipe_type:   The data pipe type.
        @type pipe_type:    str
        @keyword bundle:    The optional data pipe bundle to associate the data pipe with.
        @type bundle:       str or None
        @keyword switch:    A flag which if True will cause the new data pipe to be set to the current data pipe.
        @type switch:       bool
        """

        # Test if the pipe already exists.
        if pipe_name in self.instance:
            raise RelaxPipeError(pipe_name)

        # Create a new container.
        self[pipe_name] = PipeContainer()

        # Add the data pipe type string to the container.
        self[pipe_name].pipe_type = pipe_type

        # The pipe bundle.
        if bundle:
            # A new bundle.
            if bundle not in self.pipe_bundles:
                self.pipe_bundles[bundle] = []

            # Add the pipe to the bundle.
            self.pipe_bundles[bundle].append(pipe_name)

        # Change the current data pipe.
        if switch:
            # Set the current data pipe.
            self.instance.current_pipe = pipe_name
            builtins.cdp = self[pipe_name]

            # Signal the switch.
            status.observers.pipe_alteration.notify()

    def is_empty(self, verbosity=False):
        """Method for testing if the relax data store is empty.

        @keyword verbosity: A flag which if True will cause messages to be printed to STDERR.
        @type verbosity:    bool
        @return:            True if the data store is empty, False otherwise.
        @rtype:             bool
        """

        # No pipes should exist.
        if len(self):
            if verbosity:
                stderr.write(
                    "The relax data store contains the data pipes %s.\n" %
                    sorted(self.keys()))
            return False

        # Objects which should be in here.
        blacklist = ['pipe_bundles', 'relax_gui']

        # An object has been added to the data store.
        for name in dir(self):
            # Skip the data store methods.
            if name in self.__class__.__dict__:
                continue

            # Skip the dict methods.
            if name in dict.__dict__:
                continue

            # Skip special objects.
            if search("^__", name):
                continue

            # Blacklisted objects to skip.
            if name in blacklist:
                continue

            # An object has been added.
            if verbosity:
                stderr.write("The relax data store contains the object %s.\n" %
                             name)
            return False

        # The data store is empty.
        return True

    def from_xml(self, file, dir=None, pipe_to=None, verbosity=1):
        """Parse a XML document representation of a data pipe, and load it into the relax data store.

        @param file:                The open file object.
        @type file:                 file
        @keyword dir:               The name of the directory containing the results file (needed for loading external files).
        @type dir:                  str
        @keyword pipe_to:           The data pipe to load the XML data pipe into (the file must only contain one data pipe).
        @type pipe_to:              str
        @keyword verbosity:         A flag specifying the amount of information to print.  The higher the value, the greater the verbosity.
        @type verbosity:            int
        @raises RelaxError:         If pipe_to is given and the file contains multiple pipe elements;  or if the data pipes in the XML file already exist in the relax data store;  or if the data pipe type is invalid;  or if the target data pipe is not empty.
        @raises RelaxNoPipeError:   If pipe_to is given but the data pipe does not exist.
        @raises RelaxError:         If the data pipes in the XML file already exist in the relax data store, or if the data pipe type is invalid.
        @raises RelaxPipeError:     If the data pipes of the XML file are already present in the relax data store.
        """

        # Create the XML document from the file.
        doc = xml.dom.minidom.parse(file)

        # Get the relax node.
        relax_node = doc.childNodes[0]

        # Get the relax version of the XML file.
        file_version = relax_node.getAttribute('file_version')
        if file_version == '':
            file_version = 1
        else:
            file_version = int(file_version)

        # Get the pipe nodes.
        pipe_nodes = relax_node.getElementsByTagName('pipe')

        # Structure for the names of the new pipes.
        pipes = []

        # Target loading to a specific pipe (for pipe results reading).
        if pipe_to:
            # Check if there are multiple pipes in the XML file.
            if len(pipe_nodes) > 1:
                raise RelaxError(
                    "The pipe_to target pipe argument '%s' cannot be given as the file contains multiple pipe elements."
                    % pipe_to)

            # The pipe type.
            pipe_type = pipe_nodes[0].getAttribute('type')

            # Check that the pipe already exists.
            if not pipe_to in self:
                raise RelaxNoPipeError(pipe_to)

            # Check if the pipe type matches.
            if pipe_type != self[pipe_to].pipe_type:
                raise RelaxError(
                    "The XML file pipe type '%s' does not match the pipe type '%s'"
                    % (pipe_type, self[pipe_to].pipe_type))

            # Check if the pipe is empty.
            if not self[pipe_to].is_empty():
                raise RelaxError("The data pipe '%s' is not empty." % pipe_to)

            # Load the data.
            self[pipe_to].from_xml(pipe_nodes[0],
                                   dir=dir,
                                   file_version=file_version)

            # Store the pipe name.
            pipes.append(pipe_to)

        # Load the state.
        else:
            # Get the GUI nodes.
            gui_nodes = relax_node.getElementsByTagName('relax_gui')
            if gui_nodes:
                self.relax_gui.from_xml(gui_nodes[0],
                                        file_version=file_version)

            # Get the sequence alignment nodes.
            seq_align_nodes = relax_node.getElementsByTagName(
                'sequence_alignments')
            if seq_align_nodes:
                # Initialise the object.
                self.sequence_alignments = Sequence_alignments()

                # Populate it.
                self.sequence_alignments.from_xml(seq_align_nodes[0],
                                                  file_version=file_version)

            # Recreate all the data store data structures.
            xml_to_object(
                relax_node,
                self,
                file_version=file_version,
                blacklist=['pipe', 'relax_gui', 'sequence_alignments'])

            # Checks.
            for pipe_node in pipe_nodes:
                # The pipe name and type.
                pipe_name = str(pipe_node.getAttribute('name'))
                pipe_type = pipe_node.getAttribute('type')

                # Existence check.
                if pipe_name in self:
                    raise RelaxPipeError(pipe_name)

                # Valid type check.
                if not pipe_type in pipe_control.pipes.VALID_TYPES:
                    raise RelaxError(
                        "The data pipe type '%s' is invalid and must be one of the strings in the list %s."
                        % (pipe_type, pipe_control.pipes.VALID_TYPES))

            # Load the data pipes.
            for pipe_node in pipe_nodes:
                # The pipe name and type.
                pipe_name = str(pipe_node.getAttribute('name'))
                pipe_type = pipe_node.getAttribute('type')

                # Add the data pipe.
                switch = False
                if self.current_pipe == None:
                    switch = True
                self.add(pipe_name, pipe_type, switch=switch)

                # Fill the pipe.
                self[pipe_name].from_xml(pipe_node,
                                         file_version=file_version,
                                         dir=dir)

                # Store the pipe name.
                pipes.append(pipe_name)

            # Set the current pipe.
            if self.current_pipe in self:
                builtins.cdp = self[self.current_pipe]

        # Finally update the molecule, residue, and spin metadata for each data pipe.
        for pipe in pipes:
            pipe_control.mol_res_spin.metadata_update(pipe=pipe)

        # Backwards compatibility transformations.
        self._back_compat_hook(file_version, pipes=pipes)

    def to_xml(self, file, pipes=None):
        """Create a XML document representation of the current data pipe.

        This method creates the top level XML document including all the information needed
        about relax, calls the PipeContainer.xml_write() method to fill in the document contents,
        and writes the XML into the file object.

        @param file:        The open file object.
        @type file:         file
        @param pipes:       The name of the pipe, or list of pipes to place in the XML file.
        @type pipes:        str or list of str
        """

        # The pipes to include in the XML file.
        all = False
        if not pipes:
            all = True
            pipes = list(self.keys())
        elif isinstance(pipes, str):
            pipes = [pipes]

        # Sort the pipes.
        pipes.sort()

        # Create the XML document object.
        xmldoc = xml.dom.minidom.Document()

        # Create the top level element, including the relax URL.
        top_element = xmldoc.createElementNS('http://www.nmr-relax.com',
                                             'relax')
        top_element.setAttribute("xmlns", "http://www.nmr-relax.com")

        # Append the element.
        xmldoc.appendChild(top_element)

        # Set the relax version number, and add a creation time.
        top_element.setAttribute('version', version.version)
        top_element.setAttribute('time', asctime())
        top_element.setAttribute('file_version', "2")
        if version.repo_head:
            top_element.setAttribute('head', version.repo_head)
        if version.repo_url:
            top_element.setAttribute('url',
                                     version.repo_url.replace('\n', '; '))

        # Add all objects in the data store base object to the XML element.
        if all:
            blacklist = list(self.__class__.__dict__.keys()) + list(
                dict.__dict__.keys())
            for name in dir(self):
                # Skip blacklisted objects.
                if name in blacklist:
                    continue

                # Skip special objects.
                if search('^_', name):
                    continue

                # Execute any to_xml() methods, and add that object to the blacklist.
                obj = getattr(self, name)
                if hasattr(obj, 'to_xml'):
                    obj.to_xml(xmldoc, top_element)
                    blacklist = blacklist + [name]

            # Remove the current data pipe from the blacklist!
            blacklist.remove('current_pipe')

            # Add all simple python objects within the store.
            fill_object_contents(xmldoc,
                                 top_element,
                                 object=self,
                                 blacklist=blacklist)

        # Loop over the pipes.
        for pipe in pipes:
            # Create the pipe XML element and add it to the top level XML element.
            pipe_element = xmldoc.createElement('pipe')
            top_element.appendChild(pipe_element)

            # Set the data pipe attributes.
            pipe_element.setAttribute('desc',
                                      'The contents of a relax data pipe')
            pipe_element.setAttribute('name', pipe)
            pipe_element.setAttribute('type', self[pipe].pipe_type)

            # Fill the data pipe XML element.
            self[pipe].to_xml(xmldoc,
                              pipe_element,
                              pipe_type=self[pipe].pipe_type)

        # Write out the XML file.
        file.write(xmldoc.toprettyxml(indent='    '))