Author: bugman Date: Tue Oct 21 15:58:31 2014 New Revision: 26327 URL: http://svn.gna.org/viewcvs/relax?rev=26327&view=rev Log: Implemented the multiple molecule merging functionality of the structure.load_spins user function. The argument has been added to the user function frontend and a description added for this new functionality. In the backend, the pipe_control.structure.main.load_spins() function will now call the load_spins_multi_mol() function if from_mols is supplied. This alternative function is required to handle missing atoms and differential atom numbering. Modified: trunk/pipe_control/structure/main.py trunk/user_functions/structure.py Modified: trunk/pipe_control/structure/main.py URL: http://svn.gna.org/viewcvs/relax/trunk/pipe_control/structure/main.py?rev=26327&r1=26326&r2=26327&view=diff ============================================================================== --- trunk/pipe_control/structure/main.py (original) +++ trunk/pipe_control/structure/main.py Tue Oct 21 15:58:31 2014 @@ -32,6 +32,7 @@ from lib.checks import Check from lib.errors import RelaxError, RelaxFileError from lib.io import get_file_path, open_write_file, write_data +from lib.selection import tokenise from lib.sequence import write_spin_data from lib.structure.internal.displacements import Displacements from lib.structure.internal.object import Internal @@ -699,18 +700,25 @@ write_data(out=sys.stdout, headings=["Spin_ID", "Position"], data=data) -def load_spins(spin_id=None, str_id=None, mol_name_target=None, ave_pos=False): +def load_spins(spin_id=None, str_id=None, from_mols=None, mol_name_target=None, ave_pos=False): """Load the spins from the structural object into the relax data store. @keyword spin_id: The molecule, residue, and spin identifier string. @type spin_id: str @keyword str_id: The structure identifier. This can be the file name, model number, or structure number. @type str_id: int or str + @keyword from_mols: The list of similar, but not necessarily identical molecules to load spin information from. + @type from_mols: list of str or None @keyword mol_name_target: The name of target molecule container, overriding the name of the loaded structures @type mol_name_target: str or None @keyword ave_pos: A flag specifying if the average atom position or the atom position from all loaded structures is loaded into the SpinContainer. @type ave_pos: bool """ + + # The multi-molecule case. + if from_mols != None: + load_spins_multi_mol(spin_id=spin_id, str_id=str_id, from_mols=from_mols, mol_name_target=mol_name_target, ave_pos=ave_pos) + return # Checks. check_pipe() @@ -775,6 +783,117 @@ # Print out. write_spin_data(file=sys.stdout, mol_names=mol_names, res_nums=res_nums, res_names=res_names, spin_nums=spin_nums, spin_names=spin_names) + + +def load_spins_multi_mol(spin_id=None, str_id=None, from_mols=None, mol_name_target=None, ave_pos=False): + """Load the spins from the structural object into the relax data store. + + @keyword spin_id: The molecule, residue, and spin identifier string. + @type spin_id: str + @keyword str_id: The structure identifier. This can be the file name, model number, or structure number. + @type str_id: int or str + @keyword from_mols: The list of similar, but not necessarily identical molecules to load spin information from. + @type from_mols: list of str or None + @keyword mol_name_target: The name of target molecule container, overriding the name of the loaded structures + @type mol_name_target: str or None + @keyword ave_pos: A flag specifying if the average atom position or the atom position from all loaded structures is loaded into the SpinContainer. + @type ave_pos: bool + """ + + # Checks. + check_pipe() + check_structure() + + # The target molecule name must be supplied. + if mol_name_target == None: + raise RelaxError("The target molecule name must be supplied when specifying multiple molecules to load spins from.") + + # Disallow multiple structural models. + if cdp.structure.num_models() != 1: + raise RelaxError("Only a single structural model is allowed when specifying multiple molecules to load spins from.") + + # Split up the selection string. + if spin_id: + mol_token, res_token, spin_token = tokenise(spin_id) + if mol_token != None: + raise RelaxError("The spin ID string cannot contain molecular information when specifying multiple molecules to load spins from.") + + # Print out. + print("Adding the following spins to the relax data store.\n") + + # Initialise the data structures. + ids = [] + res_nums = {} + res_names = {} + spin_names = {} + positions = {} + elements = {} + + # Loop over all target molecules. + for mol_name in from_mols: + # Create a new spin ID with the molecule name. + new_id = '#' + mol_name + if spin_id != None: + new_id += spin_id + + # Loop over all atoms of the new spin ID selection. + selection = cdp.structure.selection(atom_id=new_id) + for res_num, res_name, atom_num, atom_name, element, pos in cdp.structure.atom_loop(selection=selection, str_id=str_id, res_num_flag=True, res_name_flag=True, atom_num_flag=True, atom_name_flag=True, element_flag=True, pos_flag=True, ave=ave_pos): + # Remove the '+' regular expression character from the res and atom names. + if res_name and search('\+', res_name): + res_name = res_name.replace('+', '') + if atom_name and search('\+', atom_name): + atom_name = atom_name.replace('+', '') + + # Generate a spin ID for the current atom. + id = generate_spin_id_unique(mol_name=mol_name_target, res_num=res_num, res_name=res_name, spin_name=atom_name) + + # Not a new ID. + if id in ids: + # Store the position info. + positions[id].append(pos) + continue + + # Store the ID, residue, spin, element and position info. + ids.append(id) + res_nums[id] = res_num + res_names[id] = res_name + spin_names[id] = atom_name + positions[id] = [pos] + elements[id] = element + + # Catch no data. + if len(ids) == 0: + warn(RelaxWarning("No spins matching the '%s' ID string could be found." % spin_id)) + return + + # Create the spin containers. + mol_names2 = [] + res_nums2 = [] + res_names2 = [] + spin_names2 = [] + for id in ids: + # Fetch the spin. + spin_cont = return_spin(id) + + # Create the spin if it does not exist. + if spin_cont == None: + spin_cont = create_spin(mol_name=mol_name_target, res_num=res_nums[id], res_name=res_names[id], spin_name=spin_names[id]) + + # Position vector. + spin_cont.pos = positions[id] + + # Add the element. + spin_cont.element = elements[id] + + # Update the structures for the printout. + mol_names2.append(mol_name_target) + res_nums2.append(res_nums[id]) + res_names2.append(res_names[id]) + spin_names2.append(spin_names[id]) + + # Print out. + write_spin_data(file=sys.stdout, mol_names=mol_names2, res_nums=res_nums2, res_names=res_names2, spin_names=spin_names2) def mean(): Modified: trunk/user_functions/structure.py URL: http://svn.gna.org/viewcvs/relax/trunk/user_functions/structure.py?rev=26327&r1=26326&r2=26327&view=diff ============================================================================== --- trunk/user_functions/structure.py (original) +++ trunk/user_functions/structure.py Tue Oct 21 15:58:31 2014 @@ -704,6 +704,15 @@ can_be_none = True ) uf.add_keyarg( + name = "from_mols", + py_type = "str_list", + desc_short = "molecules to load spins from", + desc = "The list of similar, but not necessarily identical molecules to load spin information from.", + wiz_combo_iter = pipe_names, + wiz_read_only = False, + can_be_none = True +) +uf.add_keyarg( name = "mol_name_target", py_type = "str", desc_short = "target molecule name", @@ -720,7 +729,8 @@ # Description. uf.desc.append(Desc_container()) uf.desc[-1].add_paragraph("This allows a sequence to be generated within the relax data store using the atomic information from the structure already associated with this data pipe. The spin ID string is used to select which molecules, which residues, and which atoms will be recognised as spin systems within relax. If the spin ID is left unspecified, then all molecules, residues, and atoms will be placed within the data store (and all atoms will be treated as spins).") -uf.desc[-1].add_paragraph("If averaging the atomic positions, then average position of all models will be loaded into the spin container. Otherwise the positions from all models will be loaded separately.") +uf.desc[-1].add_paragraph("As an alternative to using structural models, by specifying the list of molecules to load spins from similar though not necessarily identical molecules will be combined. In this case, the target molecule name must be supplied to create a single combined molecule. And only a single model can be loaded in the current data pipe. The spin numbering will be dropped to allow for sequential atom numbering in the PDB and other formats. Therefore only the residue number and name and atom name will be preserved for creating the spin containers. If the spin is only present in a subset of the structures, then the positional information will only be taken from that subset and hence the number of positions might be different for different spins.") +uf.desc[-1].add_paragraph("If averaging the atomic positions, then average position of all models or molecules will be loaded into the spin container. Otherwise the positions from all models or molecules will be loaded separately.") # Prompt examples. uf.desc.append(Desc_container("Prompt examples")) uf.desc[-1].add_paragraph("For a model-free backbone amide nitrogen analysis, to load just the backbone N sequence from the file '1F3Y.pdb' (which is a single protein), type the following two user functions:") @@ -742,8 +752,8 @@ uf.backend = pipe_control.structure.main.load_spins uf.menu_text = "&load_spins" uf.gui_icon = "relax.spin" -uf.wizard_height_desc = 300 -uf.wizard_size = (800, 600) +uf.wizard_height_desc = 500 +uf.wizard_size = (900, 700) uf.wizard_image = WIZARD_IMAGE_PATH + 'structure' + sep + 'load_spins.png'