Author: bugman Date: Fri Mar 15 11:23:54 2013 New Revision: 18829 URL: http://svn.gna.org/viewcvs/relax?rev=18829&view=rev Log: Merged revisions 18822-18828 via svnmerge from svn+ssh://bugman@xxxxxxxxxxx/svn/relax/trunk ........ r18822 | bugman | 2013-03-13 18:24:47 +0100 (Wed, 13 Mar 2013) | 9 lines Fix for a strange and extremely rare typo bug in the model-free specific analysis code. This was identified by Manish Chaubey <manish dott chaubey att tuebingen dott mpg dott de> in the message at http://thread.gmane.org/gmane.science.nmr.relax.user/1422. This only occurs if a relaxation data error of zero is encountered and is a bug in the RelaxError message explaining the problem with the data. ........ r18823 | bugman | 2013-03-14 17:54:01 +0100 (Thu, 14 Mar 2013) | 5 lines Created the front end for the new structure.create_rotor_pdb user function. This will be used to create a PDB representation of a rotor motional model. ........ r18824 | bugman | 2013-03-14 19:35:40 +0100 (Thu, 14 Mar 2013) | 3 lines Added file, directory and overwrite force arguments to the structure.create_rotor_pdb user function. ........ r18825 | bugman | 2013-03-14 19:59:45 +0100 (Thu, 14 Mar 2013) | 3 lines Started to implement the backend of the structure.create_rotor_pdb user function. ........ r18826 | bugman | 2013-03-15 10:32:04 +0100 (Fri, 15 Mar 2013) | 3 lines The internal structural object MolContainer.add_atom() method now returns the index of the new atom. ........ r18827 | bugman | 2013-03-15 10:59:10 +0100 (Fri, 15 Mar 2013) | 3 lines Created the internal structural object MolContainer.last_residue() method. ........ r18828 | bugman | 2013-03-15 11:21:12 +0100 (Fri, 15 Mar 2013) | 5 lines Fully implemented the structure.create_rotor_pdb user function. For this, the generic_fns.structure.geometric.create_rotor_propellers() function was created. ........ Modified: branches/frame_order_testing/ (props changed) branches/frame_order_testing/generic_fns/structure/geometric.py branches/frame_order_testing/generic_fns/structure/internal.py branches/frame_order_testing/specific_fns/model_free/mf_minimise.py branches/frame_order_testing/user_functions/structure.py Propchange: branches/frame_order_testing/ ------------------------------------------------------------------------------ --- svnmerge-integrated (original) +++ svnmerge-integrated Fri Mar 15 11:23:54 2013 @@ -1,1 +1,1 @@ -/trunk:1-18820 +/trunk:1-18828 Modified: branches/frame_order_testing/generic_fns/structure/geometric.py URL: http://svn.gna.org/viewcvs/relax/branches/frame_order_testing/generic_fns/structure/geometric.py?rev=18829&r1=18828&r2=18829&view=diff ============================================================================== --- branches/frame_order_testing/generic_fns/structure/geometric.py (original) +++ branches/frame_order_testing/generic_fns/structure/geometric.py Fri Mar 15 11:23:54 2013 @@ -1,6 +1,6 @@ ############################################################################### # # -# Copyright (C) 2003-2012 Edward d'Auvergne # +# Copyright (C) 2003-2013 Edward d'Auvergne # # # # This file is part of the program relax (http://www.nmr-relax.com). # # # @@ -21,7 +21,8 @@ # Python module imports. from math import cos, pi, sin -from numpy import arccos, array, dot, eye, float64, transpose, zeros +from numpy import arccos, array, cross, dot, eye, float64, transpose, zeros +from numpy.linalg import norm from os import getcwd from string import ascii_uppercase from warnings import warn @@ -32,7 +33,8 @@ from generic_fns import pipes from generic_fns.structure.internal import Internal from generic_fns.structure.mass import centre_of_mass -from maths_fns.rotation_matrix import two_vect_to_R +from lib.geometry.lines import closest_point_ax +from maths_fns.rotation_matrix import axis_angle_to_R, two_vect_to_R from relax_errors import RelaxError, RelaxNoPdbError, RelaxNoSequenceError, RelaxNoTensorError, RelaxNoVectorsError from relax_io import get_file_path, open_write_file from relax_warnings import RelaxWarning @@ -583,6 +585,189 @@ status.observers.result_file.notify() +def create_rotor_pdb(file=None, dir=None, rotor_angle=None, axis=None, axis_pt=True, centre=None, span=2e-9, blade_length=5e-10, force=False, staggered=False): + """Create a PDB representation of a rotor motional model. + + @keyword file: The name of the PDB file to create. + @type file: str + @keyword dir: The name of the directory to place the PDB file into. + @type dir: str + @keyword rotor_angle: The angle of the rotor motion in degrees. + @type rotor_angle: float + @keyword axis: The vector defining the rotor axis. + @type axis: numpy rank-1, 3D array + @keyword axis_pt: A point lying anywhere on the rotor axis. This is used to define the position of the axis in 3D space. + @type axis_pt: numpy rank-1, 3D array + @keyword centre: The central point of the representation. If this point is not on the rotor axis, then the closest point on the axis will be used for the centre. + @type centre: numpy rank-1, 3D array + @keyword span: The distance from the central point to the rotor blades (meters). + @type span: float + @keyword blade_length: The length of the representative rotor blades. + @type blade_length: float + @keyword force: A flag which if set will overwrite any pre-existing file. + @type force: bool + @keyword staggered: A flag which if True will cause the rotor blades to be staggered. This is used to avoid blade overlap. + @type staggered: bool + """ + + # Convert the arguments to numpy arrays, radians and Angstrom. + axis = array(axis, float64) + axis_pt = array(axis_pt, float64) + centre = array(centre, float64) + rotor_angle = rotor_angle / 360.0 * 2.0 * pi + span = span * 1e10 + blade_length = blade_length * 1e10 + + # Normalise. + axis_norm = axis / norm(axis) + + # Test if the current pipe exists. + pipes.test() + + # Create the structural object. + structure = Internal() + + # Add a structure. + structure.add_molecule(name='rotor') + + # Alias the single molecule from the single model. + mol = structure.get_molecule('rotor') + + # The central point. + mid_point = closest_point_ax(line_pt=axis_pt, axis=axis, point=centre) + mol.atom_add(pdb_record='HETATM', atom_num=1, atom_name='CTR', res_name='AX', res_num=1, pos=mid_point, element='PT') + + # Centre of the propellers. + prop1 = mid_point + axis_norm * span + prop1_index = 1 + mol.atom_add(pdb_record='HETATM', atom_num=2, atom_name='PRP', res_name='PRC', res_num=2, pos=prop1, element='O') + mol.atom_connect(index1=0, index2=prop1_index) + + # Centre of the propellers. + prop2 = mid_point - axis_norm * span + prop2_index = 2 + mol.atom_add(pdb_record='HETATM', atom_num=3, atom_name='PRP', res_name='PRC', res_num=3, pos=prop2, element='O') + mol.atom_connect(index1=0, index2=prop2_index) + + # Create the rotor propellers. + create_rotor_propellers(mol=mol, rotor_angle=rotor_angle, centre=prop1, axis=axis, blade_length=blade_length, staggered=staggered) + create_rotor_propellers(mol=mol, rotor_angle=rotor_angle, centre=prop2, axis=-axis, blade_length=blade_length, staggered=staggered) + + # Print out. + print("\nGenerating the PDB file.") + + # Open the PDB file for writing. + tensor_pdb_file = open_write_file(file, dir, force=force) + + # Write the data. + structure.write_pdb(tensor_pdb_file) + + # Close the file. + tensor_pdb_file.close() + + # Add the file to the results file list. + if not hasattr(cdp, 'result_files'): + cdp.result_files = [] + if dir == None: + dir = getcwd() + cdp.result_files.append(['diff_tensor_pdb', 'Diffusion tensor PDB', get_file_path(file, dir)]) + status.observers.result_file.notify() + + +def create_rotor_propellers(mol=None, rotor_angle=None, centre=None, axis=None, blade_length=5.0, staggered=False): + """Create a PDB representation of a rotor motional model. + + @keyword mol: The internal structural object molecule container to add the atoms to. + @type mol: MolContainer instance + @keyword rotor_angle: The angle of the rotor motion in radians. + @type rotor_angle: float + @keyword centre: The central point of the propeller. + @type centre: numpy rank-1, 3D array + @keyword axis: The vector defining the rotor axis. + @type axis: numpy rank-1, 3D array + @keyword blade_length: The length of the representative rotor blades in Angstrom. + @type blade_length: float + @keyword staggered: A flag which if True will cause the rotor blades to be staggered. This is used to avoid blade overlap. + @type staggered: bool + """ + + # Init. + step_angle = 2.0 / 360.0 * 2.0 * pi + R = zeros((3, 3), float64) + res_num = mol.last_residue() + 1 + + # Blade vectors. + blades = zeros((4, 3), float64) + if abs(dot(axis, array([0, 0, 1], float64))) == 1.0: # Avoid failures in artificial situations. + blades[0] = cross(axis, array([1, 0, 0], float64)) + else: + blades[0] = cross(axis, array([0, 0, 1], float64)) + blades[0] = blades[0] / norm(blades[0]) + blades[1] = cross(axis, blades[0]) + blades[1] = blades[1] / norm(blades[1]) + blades[2] = -blades[0] + blades[3] = -blades[1] + print axis + print blades + + # Create the 4 blades. + for i in range(len(blades)): + # Staggering. + if staggered and i % 2: + blade_origin = centre - axis * 2 + + # Non-staggered. + else: + blade_origin = centre + + # Add an atom for the blage origin. + blade_origin_index = mol.atom_add(pdb_record='HETATM', atom_name='BLO', res_name='PRB', res_num=res_num, pos=blade_origin, element='O') + + # The centre edge point of the blade. + mid_point = blade_origin + blades[i] * blade_length + mid_pt_index = mol.atom_add(pdb_record='HETATM', atom_name='BLD', res_name='PRB', res_num=res_num, pos=mid_point, element='N') + mol.atom_connect(index1=mid_pt_index, index2=blade_origin_index) + + # Build the blade. + angle = 0.0 + pos_last_index = mid_pt_index + neg_last_index = mid_pt_index + while True: + # Increase the angle. + angle += step_angle + + # The edge rotation. + if angle > rotor_angle: + axis_angle_to_R(axis, rotor_angle, R) + + # The normal rotation matrix. + else: + axis_angle_to_R(axis, angle, R) + + # The positive edge. + pos_point = dot(R, mid_point - blade_origin) + blade_origin + pos_index = mol.atom_add(pdb_record='HETATM', atom_name='BLD', res_name='PRB', res_num=res_num, pos=pos_point, element='N') + mol.atom_connect(index1=pos_index, index2=pos_last_index) + mol.atom_connect(index1=pos_index, index2=blade_origin_index) + + # The negative edge. + neg_point = dot(transpose(R), mid_point - blade_origin) + blade_origin + neg_index = mol.atom_add(pdb_record='HETATM', atom_name='BLD', res_name='PRB', res_num=res_num, pos=neg_point, element='N') + mol.atom_connect(index1=neg_index, index2=neg_last_index) + mol.atom_connect(index1=neg_index, index2=blade_origin_index) + + # Update the indices. + pos_last_index = pos_index + neg_last_index = neg_index + + # Finish. + if angle > rotor_angle: + break + + # Increment the residue number. + res_num += 1 + + def create_vector_dist(length=None, symmetry=True, file=None, dir=None, force=False): """Create a PDB representation of the vector distribution. Modified: branches/frame_order_testing/generic_fns/structure/internal.py URL: http://svn.gna.org/viewcvs/relax/branches/frame_order_testing/generic_fns/structure/internal.py?rev=18829&r1=18828&r2=18829&view=diff ============================================================================== --- branches/frame_order_testing/generic_fns/structure/internal.py (original) +++ branches/frame_order_testing/generic_fns/structure/internal.py Fri Mar 15 11:23:54 2013 @@ -2180,6 +2180,8 @@ @type segment_id: str or None @keyword pdb_record: The optional PDB record name, e.g. 'ATOM' or 'HETATM'. @type pdb_record: str or None + @return: The index of the added atom. + @rtype: int """ # Append to all the arrays. @@ -2196,6 +2198,9 @@ self.y.append(pos[1]) self.z.append(pos[2]) + # Return the index. + return len(self.atom_num) - 1 + def atom_connect(self, index1=None, index2=None): """Method for connecting two atoms within the data structure object. @@ -2344,6 +2349,17 @@ return True + def last_residue(self): + """Return the number of the last residue. + + @return: The last residue number. + @rtype: int + """ + + # Return the number. + return self.res_num[-1] + + def to_xml(self, doc, element): """Create XML elements for the contents of this molecule container. Modified: branches/frame_order_testing/specific_fns/model_free/mf_minimise.py URL: http://svn.gna.org/viewcvs/relax/branches/frame_order_testing/specific_fns/model_free/mf_minimise.py?rev=18829&r1=18828&r2=18829&view=diff ============================================================================== --- branches/frame_order_testing/specific_fns/model_free/mf_minimise.py (original) +++ branches/frame_order_testing/specific_fns/model_free/mf_minimise.py Fri Mar 15 11:23:54 2013 @@ -1095,7 +1095,7 @@ # Checks. if err != None and err == 0.0: - raise RelaxError("Zero error for spin '%s' for the relaxation data ID '%s', minimisation not possible." % (errid)) + raise RelaxError("Zero error for spin '%s' for the relaxation data ID '%s', minimisation not possible." % (data_store.spin_id, ri_id)) elif err != None and err < 0.0: raise RelaxError("Negative error of %s for spin '%s' for the relaxation data ID '%s', minimisation not possible." % (err, data_store.spin_id, ri_id)) Modified: branches/frame_order_testing/user_functions/structure.py URL: http://svn.gna.org/viewcvs/relax/branches/frame_order_testing/user_functions/structure.py?rev=18829&r1=18828&r2=18829&view=diff ============================================================================== --- branches/frame_order_testing/user_functions/structure.py (original) +++ branches/frame_order_testing/user_functions/structure.py Fri Mar 15 11:23:54 2013 @@ -230,6 +230,100 @@ uf.wizard_size = (1000, 750) uf.wizard_apply_button = False uf.wizard_image = WIZARD_IMAGE_PATH + 'structure' + sep + 'create_diff_tensor_pdb.png' + + +# The structure.create_rotor_pdb user function. +uf = uf_info.add_uf('structure.create_rotor_pdb') +uf.title = "Create a PDB file representation of a rotor." +uf.title_short = "Rotor PDB representation." +uf.add_keyarg( + name = "file", + default = "rotor.pdb", + py_type = "str", + arg_type = "file sel", + desc_short = "file name", + desc = "The name of the PDB file.", + wiz_filesel_wildcard = "PDB files (*.pdb)|*.pdb;*.PDB", + wiz_filesel_style = FD_SAVE +) +uf.add_keyarg( + name = "dir", + py_type = "str", + arg_type = "dir", + desc_short = "directory name", + desc = "The directory to place the file into.", + can_be_none = True +) +uf.add_keyarg( + name = "rotor_angle", + default = 0.0, + py_type = "float", + desc_short = "rotor angle", + desc = "The angle of the rotor motion in degrees." +) +uf.add_keyarg( + name = "axis", + py_type = "float_array", + dim = 3, + desc_short = "rotor axis vector", + desc = "The vector defining the rotor axis." +) +uf.add_keyarg( + name = "axis_pt", + py_type = "float_array", + dim = 3, + desc_short = "rotor axis point", + desc = "A point lying anywhere on the rotor axis. This is used to define the position of the axis in 3D space." +) +uf.add_keyarg( + name = "centre", + py_type = "float_array", + dim = 3, + desc_short = "central point", + desc = "The central point of the representation. If this point is not on the rotor axis, then the closest point on the axis will be used for the centre." +) +uf.add_keyarg( + name = "span", + default = 2e-9, + py_type = "num", + desc_short = "representation span", + desc = "The distance from the central point to the rotor blades (meters)." +) +uf.add_keyarg( + name = "blade_length", + default = 5e-10, + py_type = "num", + desc_short = "blade length", + desc = "The length of the representative rotor blades." +) +uf.add_keyarg( + name = "force", + default = False, + py_type = "bool", + desc_short = "force flag", + desc = "A flag which if True will overwrite the file if it already exists." +) +uf.add_keyarg( + name = "staggered", + default = False, + py_type = "bool", + desc_short = "staggered flag", + desc = "A flag which if True will cause the rotor blades to be staggered. This is used to avoid blade overlap." +) +# Description. +uf.desc.append(Desc_container()) +uf.desc[-1].add_paragraph("This creates a PDB file representation of a rotor motional model. The model axis is defined by a vector and a single point on the axis. The centre of the representation will be taken as the point on the rotor axis closest to the given centre position. The size of the representation is defined by the span, which is the distance from the central point to the rotors, and the length of the blades.") +# Prompt examples. +uf.desc.append(Desc_container("Prompt examples")) +uf.desc[-1].add_paragraph("The following is a synthetic example:") +uf.desc[-1].add_prompt("relax> structure.create_rotor_pdb(file='rotor.pdb', rotor_angle=20.0, axis=[0., 0., 1.], axis_pt=[1., 1., 0.], centre=[0., 0., 2.], span=2e-9, blade_length=1e-9)") +uf.backend = generic_fns.structure.geometric.create_rotor_pdb +uf.menu_text = "create_&rotor_pdb" +uf.gui_icon = "oxygen.actions.list-add-relax-blue" +uf.wizard_height_desc = 400 +uf.wizard_size = (900, 700) +uf.wizard_apply_button = False +uf.wizard_image = WIZARD_IMAGE_PATH + 'structure' + sep + '2JK4.png' # The structure.create_vector_dist user function.