Loading sample_simulation_mcvine/base/detector_utils.py 0 → 100644 +189 −0 Original line number Diff line number Diff line import xml.etree.ElementTree as ET import numpy as np from scipy.spatial.transform import Rotation as R import re def extract_bank_data(file_path): tree = ET.parse(file_path) root = tree.getroot() namespace = '{http://www.mantidproject.org/IDF/1.0}' bank_data = {} for component in root.findall(f".//{namespace}component"): component_type = component.attrib.get("type", "") if re.match(r'^pack', component_type): # Match any type that starts with 'pack' location = component.find(f"{namespace}location") if location is not None and re.match(r'bank\d+', location.attrib.get("name", "")): bank_name = location.attrib["name"] x = float(location.attrib.get("x")) y = float(location.attrib.get("y")) z = float(location.attrib.get("z")) # Check for different rotation formats y1_rot = location.find(f"{namespace}rot") if y1_rot is not None: y1 = float(y1_rot.attrib['val']) # First Y rotation # Check if there are nested rotations z_rot = y1_rot.find(f"{namespace}rot") if z_rot is not None: z1 = float(z_rot.attrib['val']) # Z rotation y2_rot = z_rot.find(f"{namespace}rot") if y2_rot is not None: y2 = float(y2_rot.attrib['val']) # Second Y rotation # Store both rotations in nested format bank_data[bank_name] = { "location": (x, y, z), "yzy_angles": (y1, z1, y2), "pack_type": component_type } else: # For a single rotation element, store as Y rotation only bank_data[bank_name] = { "location": (x, y, z), "yzy_angles": (y1, 0, 0), "pack_type": component_type } return bank_data # ## example use # file_path = 'NOMAD_Definition.xml' # bank_data = extract_bank_data(file_path) def convert_to_euler_angles_xyz(yzy_angles): """ Converts given rotations around Y, Z, and Y axes to Euler angles in X-Y-Z convention (alpha, beta, gamma). Parameters: yzy_angles (tuple or list): A tuple or list containing (theta_Y1, theta_Z, theta_Y2) in degrees. Returns: tuple: (alpha, beta, gamma) in degrees, in the X-Y-Z convention. """ theta_Y1, theta_Z, theta_Y2 = yzy_angles # Unpack the angles # Convert angles to radians theta_Y1 = np.deg2rad(theta_Y1) theta_Z = np.deg2rad(theta_Z) theta_Y2 = np.deg2rad(theta_Y2) # Rotation matrices R_Y1 = np.array([ [np.cos(theta_Y1), 0, np.sin(theta_Y1)], [0, 1, 0], [-np.sin(theta_Y1), 0, np.cos(theta_Y1)] ]) R_Z = np.array([ [np.cos(theta_Z), -np.sin(theta_Z), 0], [np.sin(theta_Z), np.cos(theta_Z), 0], [0, 0, 1] ]) R_Y2 = np.array([ [np.cos(theta_Y2), 0, np.sin(theta_Y2)], [0, 1, 0], [-np.sin(theta_Y2), 0, np.cos(theta_Y2)] ]) # Combined rotation matrix R = R_Y2 @ R_Z @ R_Y1 # print('Rotation matrix:', R) # Extract Euler angles in X-Y-Z convention alpha = np.arctan2(R[2, 1], R[2, 2]) beta = np.arcsin(-R[2, 0]) gamma = np.arctan2(R[1, 0], R[0, 0]) # Convert to degrees alpha_deg = np.rad2deg(alpha) beta_deg = np.rad2deg(beta) gamma_deg = np.rad2deg(gamma) return alpha_deg, beta_deg, gamma_deg # ##example use # for bank_name, data in bank_data.items(): # location = data["location"] # yzy_angles = data["yzy_angles"] # # euler_angles = convert_to_euler(yzy_angles) # euler_angles = convert_to_euler_angles_xyz(yzy_angles[::-1]) # print(f"Bank: {bank_name}") # print("Location:", location) # print("YZY Angles:", yzy_angles) # print("Euler Angles (XYZ):", euler_angles) # print("-" * 30) from detector import Detector # Default pack types for NOMAD def get_pack_types(): return { "pack": {"tube_count": 8, "pixels_in_tube": 128, "width": 0.2172, "height": 128 * 0.00794}, "packhalf": {"tube_count": 8, "pixels_in_tube": 128, "width": 0.0606, "height": 128 * 0.00781}, "packhalfshort": {"tube_count": 16, "pixels_in_tube": 64, "width": 0.11546, "height": 64 * 0.00720} } def generate_detectors(bank_data, sample="sample_reference", output_file=None): """ Generates detector instances and returns a formatted script string. Parameters: bank_data (dict): Dictionary containing detector bank data. sample (str): Reference to the sample position. output_file (str, optional): If provided, writes the output to a file. Returns: str: The formatted script containing detector definitions. """ pack_types = get_pack_types() snippet_string = "" snippet_template = """ {bank_name} = Detector(name='{bank_name}', xwidth={width}, yheight={height}, Nx={Nx}, Ny={Ny}, outfile='{bank_name}', start_index={start_index}) instrument.append( {bank_name}, position={location}, orientation={orientation}, relativeTo={sample} ) """ start_index = 0 for bank_name, data in bank_data.items(): location = data["location"] yzy_angles = data["yzy_angles"] pack = pack_types[data["pack_type"]] euler_angles = convert_to_euler_angles_xyz(yzy_angles[::-1]) snippet_string += snippet_template.format( bank_name=bank_name, width=pack["width"], height=pack["height"], Nx=pack["tube_count"], Ny=pack["pixels_in_tube"], start_index=start_index, location=location, orientation=tuple(euler_angles), sample=sample ) start_index += pack["tube_count"] * pack["pixels_in_tube"] if output_file: with open(output_file, "w") as file: file.write(snippet_string) return snippet_string # Example usage: # generated_script = generate_detectors(bank_data, output_file="nom_detectors_only.py") Loading
sample_simulation_mcvine/base/detector_utils.py 0 → 100644 +189 −0 Original line number Diff line number Diff line import xml.etree.ElementTree as ET import numpy as np from scipy.spatial.transform import Rotation as R import re def extract_bank_data(file_path): tree = ET.parse(file_path) root = tree.getroot() namespace = '{http://www.mantidproject.org/IDF/1.0}' bank_data = {} for component in root.findall(f".//{namespace}component"): component_type = component.attrib.get("type", "") if re.match(r'^pack', component_type): # Match any type that starts with 'pack' location = component.find(f"{namespace}location") if location is not None and re.match(r'bank\d+', location.attrib.get("name", "")): bank_name = location.attrib["name"] x = float(location.attrib.get("x")) y = float(location.attrib.get("y")) z = float(location.attrib.get("z")) # Check for different rotation formats y1_rot = location.find(f"{namespace}rot") if y1_rot is not None: y1 = float(y1_rot.attrib['val']) # First Y rotation # Check if there are nested rotations z_rot = y1_rot.find(f"{namespace}rot") if z_rot is not None: z1 = float(z_rot.attrib['val']) # Z rotation y2_rot = z_rot.find(f"{namespace}rot") if y2_rot is not None: y2 = float(y2_rot.attrib['val']) # Second Y rotation # Store both rotations in nested format bank_data[bank_name] = { "location": (x, y, z), "yzy_angles": (y1, z1, y2), "pack_type": component_type } else: # For a single rotation element, store as Y rotation only bank_data[bank_name] = { "location": (x, y, z), "yzy_angles": (y1, 0, 0), "pack_type": component_type } return bank_data # ## example use # file_path = 'NOMAD_Definition.xml' # bank_data = extract_bank_data(file_path) def convert_to_euler_angles_xyz(yzy_angles): """ Converts given rotations around Y, Z, and Y axes to Euler angles in X-Y-Z convention (alpha, beta, gamma). Parameters: yzy_angles (tuple or list): A tuple or list containing (theta_Y1, theta_Z, theta_Y2) in degrees. Returns: tuple: (alpha, beta, gamma) in degrees, in the X-Y-Z convention. """ theta_Y1, theta_Z, theta_Y2 = yzy_angles # Unpack the angles # Convert angles to radians theta_Y1 = np.deg2rad(theta_Y1) theta_Z = np.deg2rad(theta_Z) theta_Y2 = np.deg2rad(theta_Y2) # Rotation matrices R_Y1 = np.array([ [np.cos(theta_Y1), 0, np.sin(theta_Y1)], [0, 1, 0], [-np.sin(theta_Y1), 0, np.cos(theta_Y1)] ]) R_Z = np.array([ [np.cos(theta_Z), -np.sin(theta_Z), 0], [np.sin(theta_Z), np.cos(theta_Z), 0], [0, 0, 1] ]) R_Y2 = np.array([ [np.cos(theta_Y2), 0, np.sin(theta_Y2)], [0, 1, 0], [-np.sin(theta_Y2), 0, np.cos(theta_Y2)] ]) # Combined rotation matrix R = R_Y2 @ R_Z @ R_Y1 # print('Rotation matrix:', R) # Extract Euler angles in X-Y-Z convention alpha = np.arctan2(R[2, 1], R[2, 2]) beta = np.arcsin(-R[2, 0]) gamma = np.arctan2(R[1, 0], R[0, 0]) # Convert to degrees alpha_deg = np.rad2deg(alpha) beta_deg = np.rad2deg(beta) gamma_deg = np.rad2deg(gamma) return alpha_deg, beta_deg, gamma_deg # ##example use # for bank_name, data in bank_data.items(): # location = data["location"] # yzy_angles = data["yzy_angles"] # # euler_angles = convert_to_euler(yzy_angles) # euler_angles = convert_to_euler_angles_xyz(yzy_angles[::-1]) # print(f"Bank: {bank_name}") # print("Location:", location) # print("YZY Angles:", yzy_angles) # print("Euler Angles (XYZ):", euler_angles) # print("-" * 30) from detector import Detector # Default pack types for NOMAD def get_pack_types(): return { "pack": {"tube_count": 8, "pixels_in_tube": 128, "width": 0.2172, "height": 128 * 0.00794}, "packhalf": {"tube_count": 8, "pixels_in_tube": 128, "width": 0.0606, "height": 128 * 0.00781}, "packhalfshort": {"tube_count": 16, "pixels_in_tube": 64, "width": 0.11546, "height": 64 * 0.00720} } def generate_detectors(bank_data, sample="sample_reference", output_file=None): """ Generates detector instances and returns a formatted script string. Parameters: bank_data (dict): Dictionary containing detector bank data. sample (str): Reference to the sample position. output_file (str, optional): If provided, writes the output to a file. Returns: str: The formatted script containing detector definitions. """ pack_types = get_pack_types() snippet_string = "" snippet_template = """ {bank_name} = Detector(name='{bank_name}', xwidth={width}, yheight={height}, Nx={Nx}, Ny={Ny}, outfile='{bank_name}', start_index={start_index}) instrument.append( {bank_name}, position={location}, orientation={orientation}, relativeTo={sample} ) """ start_index = 0 for bank_name, data in bank_data.items(): location = data["location"] yzy_angles = data["yzy_angles"] pack = pack_types[data["pack_type"]] euler_angles = convert_to_euler_angles_xyz(yzy_angles[::-1]) snippet_string += snippet_template.format( bank_name=bank_name, width=pack["width"], height=pack["height"], Nx=pack["tube_count"], Ny=pack["pixels_in_tube"], start_index=start_index, location=location, orientation=tuple(euler_angles), sample=sample ) start_index += pack["tube_count"] * pack["pixels_in_tube"] if output_file: with open(output_file, "w") as file: file.write(snippet_string) return snippet_string # Example usage: # generated_script = generate_detectors(bank_data, output_file="nom_detectors_only.py")