Author: bugman Date: Tue Nov 18 11:54:21 2014 New Revision: 26611 URL: http://svn.gna.org/viewcvs/relax?rev=26611&view=rev Log: Implemented a new default basis set for the align_tensor.matrix_angles user function. This is uses standard definition of the inter-matrix angle using the Euclidean inner product of the two matrices divided by the product of the Frobenius norm of each matrix. As this is a linear map, it should produce the most correct definition of inter-tensor angles. Modified: trunk/pipe_control/align_tensor.py trunk/user_functions/align_tensor.py Modified: trunk/pipe_control/align_tensor.py URL: http://svn.gna.org/viewcvs/relax/trunk/pipe_control/align_tensor.py?rev=26611&r1=26610&r2=26611&view=diff ============================================================================== --- trunk/pipe_control/align_tensor.py (original) +++ trunk/pipe_control/align_tensor.py Tue Nov 18 11:54:21 2014 @@ -24,8 +24,8 @@ # Python module imports. from copy import deepcopy -from math import pi, sqrt -from numpy import arccos, dot, float64, linalg, zeros +from math import acos, pi, sqrt +from numpy import arccos, dot, float64, inner, linalg, zeros from numpy.linalg import norm import sys from warnings import warn @@ -907,7 +907,8 @@ tensor_num = tensor_num + 1 # Create the matrix which contains the 5D vectors. - matrix = zeros((tensor_num, 5), float64) + matrix_3D = zeros((tensor_num, 3, 3), float64) + matrix_5D = zeros((tensor_num, 5), float64) # Loop over the tensors. i = 0 @@ -919,24 +920,29 @@ # Unitary basis set. if basis_set == 0: # Pack the elements. - matrix[i, 0] = tensor.Sxx - matrix[i, 1] = tensor.Syy - matrix[i, 2] = tensor.Sxy - matrix[i, 3] = tensor.Sxz - matrix[i, 4] = tensor.Syz + matrix_5D[i, 0] = tensor.Sxx + matrix_5D[i, 1] = tensor.Syy + matrix_5D[i, 2] = tensor.Sxy + matrix_5D[i, 3] = tensor.Sxz + matrix_5D[i, 4] = tensor.Syz # Geometric basis set. elif basis_set == 1: # Pack the elements. - matrix[i, 0] = tensor.Szz - matrix[i, 1] = tensor.Sxxyy - matrix[i, 2] = tensor.Sxy - matrix[i, 3] = tensor.Sxz - matrix[i, 4] = tensor.Syz + matrix_5D[i, 0] = tensor.Szz + matrix_5D[i, 1] = tensor.Sxxyy + matrix_5D[i, 2] = tensor.Sxy + matrix_5D[i, 3] = tensor.Sxz + matrix_5D[i, 4] = tensor.Syz + + # Full matrix. + elif basis_set == 2: + matrix_3D[i] = tensor.A # Normalisation. - norm = linalg.norm(matrix[i]) - matrix[i] = matrix[i] / norm + if basis_set in [0, 1]: + norm_5D = linalg.norm(matrix_5D[i]) + matrix_5D[i] = matrix_5D[i] / norm_5D # Increment the index. i = i + 1 @@ -945,12 +951,15 @@ cdp.align_tensors.angles = zeros((tensor_num, tensor_num), float64) # Header printout. - sys.stdout.write("\nData pipe: " + repr(pipes.cdp_name()) + "\n") - sys.stdout.write("\n5D angles in deg between the vectors ") if basis_set == 0: + sys.stdout.write("5d angles in deg between the vectors ") sys.stdout.write("{Sxx, Syy, Sxy, Sxz, Syz}") elif basis_set == 1: + sys.stdout.write("5d angles in deg between the vectors ") sys.stdout.write("{Szz, Sxx-yy, Sxy, Sxz, Syz}") + elif basis_set == 2: + sys.stdout.write("Angles in deg between the matrices ") + sys.stdout.write("(using the Euclidean inner product and Frobenius norm)") sys.stdout.write(":\n") # Initialise the table of data. @@ -974,15 +983,35 @@ # Second loop over the columns. for j in range(tensor_num): - # Dot product. - delta = dot(matrix[i], matrix[j]) - - # Check. - if delta > 1: - delta = 1 - - # The angle (in rad). - cdp.align_tensors.angles[i, j] = arccos(delta) + # The 5D angles. + if basis_set in [0, 1]: + # Dot product. + delta = dot(matrix_5D[i], matrix_5D[j]) + + # Check. + if delta > 1: + delta = 1 + + # The angle. + theta = arccos(delta) + + # The full matrix angle. + elif basis_set in [2]: + # The Euclidean inner product. + nom = inner(matrix_3D[i].flatten(), matrix_3D[j].flatten()) + + # The Frobenius norms. + denom = norm(matrix_3D[i]) * norm(matrix_3D[j]) + + # The angle. + ratio = nom / denom + if ratio <= 1.0: + theta = arccos(nom / denom) + else: + theta = 0.0 + + # Store the angle (in rad). + cdp.align_tensors.angles[i, j] = theta # Add to the table as degrees. table[i+1].append("%8.1f" % (cdp.align_tensors.angles[i, j]*180.0/pi)) Modified: trunk/user_functions/align_tensor.py URL: http://svn.gna.org/viewcvs/relax/trunk/user_functions/align_tensor.py?rev=26611&r1=26610&r2=26611&view=diff ============================================================================== --- trunk/user_functions/align_tensor.py (original) +++ trunk/user_functions/align_tensor.py Tue Nov 18 11:54:21 2014 @@ -297,18 +297,18 @@ # The align_tensor.matrix_angles user function. uf = uf_info.add_uf('align_tensor.matrix_angles') -uf.title = "Calculate the 5D angles between all alignment tensors." +uf.title = "Calculate the angles between all alignment tensors." uf.title_short = "Alignment tensor angle calculation." uf.display = True uf.add_keyarg( name = "basis_set", - default = 0, + default = 2, py_type = "int", desc_short = "basis set", desc = "The basis set to operate with.", wiz_element_type = "combo", - wiz_combo_choices = ["{Sxx, Syy, Sxy, Sxz, Syz}", "{Szz, Sxxyy, Sxy, Sxz, Syz}"], - wiz_combo_data = [0, 1] + wiz_combo_choices = ["{Sxx, Syy, Sxy, Sxz, Syz}", "{Szz, Sxxyy, Sxy, Sxz, Syz}", "Full matrix angles using the Euclidean inner product"], + wiz_combo_data = [0, 1, 2] ) uf.add_keyarg( name = "tensors", @@ -322,11 +322,23 @@ ) # Description. uf.desc.append(Desc_container()) -uf.desc[-1].add_paragraph("This will calculate the angles between all loaded alignment tensors for the current data pipe. The matrices are first converted to a 5D vector form and then then angles are calculated. The angles are dependent on the basis set. If the basis set is set to the default of 0, the vectors {Sxx, Syy, Sxy, Sxz, Syz} are used. If the basis set is set to 1, the vectors {Szz, Sxxyy, Sxy, Sxz, Syz} are used instead.") +uf.desc[-1].add_paragraph("This will calculate the angles between all loaded alignment tensors for the current data pipe. Depending upon the basis set, the matrices are first converted to a 5D vector form and then then angles are calculated. The angles are dependent upon the basis set:") +uf.desc[-1].add_item_list_element("0", "The basis set {Sxx, Syy, Sxy, Sxz, Syz},") +uf.desc[-1].add_item_list_element("1", "The basis set {Szz, Sxxyy, Sxy, Sxz, Syz} of the Pales standard notation,") +uf.desc[-1].add_item_list_element("2", "The full matrix angle via the Euclidean inner product of the alignment matrices together with the Frobenius norm ||A||_F.") +uf.desc[-1].add_paragraph("The full matrix angle via the Euclidean inner product is defined as:") +uf.desc[-1].add_verbatim(""" + / <A1 , A2> \ + theta = arccos | ------------- | , + \ ||A1|| ||A2|| / +""") +uf.desc[-1].add_paragraph("where <a,b> is the Euclidean inner product and ||a|| is the Frobenius norm of the matrix.") +uf.desc[-1].add_paragraph("Note that the inner product solution is a linear map which hence preserves angles, whereas the {Sxx, Syy, Sxy, Sxz, Syz} and {Szz, Sxxyy, Sxy, Sxz, Syz} basis sets are non-linear maps which do not preserve angles. Therefore the angles from all three basis sets will be different.") uf.backend = align_tensor.matrix_angles uf.menu_text = "&matrix_angles" uf.gui_icon = "oxygen.categories.applications-education" -uf.wizard_size = (800, 600) +uf.wizard_height_desc = 500 +uf.wizard_size = (1000, 700) uf.wizard_image = WIZARD_IMAGE_PATH + 'align_tensor.png'