Loading sample_simulation_mcvine/post_processing/detector_utils.py 0 → 100644 +288 −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, selected_banks=None): """ Extracts bank data from an XML file. Parameters: - file_path (str): Path to the XML file. - selected_banks (list, optional): List of bank names to extract (e.g., ["bank1", "bank2"]). If None, all banks will be extracted. Returns: - dict: Dictionary with bank names as keys and their data as values. """ 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: bank_name = location.attrib.get("name", "") # If selected_banks is None, extract all banks. Otherwise, filter based on selection. if selected_banks is None or bank_name in selected_banks: 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 Usage: # file_path = "your_file.xml" # # # **Option 1: Extract All Banks (Default)** # all_banks_data = extract_bank_data(file_path) # print("Extracted All Banks:", all_banks_data) # # # **Option 2: Extract Only Specific Banks (e.g., bank1 to bank14)** # selected_banks = [f"bank{i}" for i in range(1, 15)] # specific_banks_data = extract_bank_data(file_path, selected_banks) # print("Extracted Selected Banks:", specific_banks_data) # 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") def count_total_pixels(bank_data): """ Calculates the total number of pixels across all detector banks. Parameters: bank_data (dict): Dictionary containing detector bank data. Returns: int: Total number of pixels. """ pack_types = get_pack_types() # Assuming this function returns detector pack types total_pixels = 0 for data in bank_data.values(): pack = pack_types[data["pack_type"]] total_pixels += pack["tube_count"] * pack["pixels_in_tube"] return total_pixels # Example usage: # total_pixels = count_total_pixels(bank_data) # print("Total number of pixels:", total_pixels) Loading
sample_simulation_mcvine/post_processing/detector_utils.py 0 → 100644 +288 −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, selected_banks=None): """ Extracts bank data from an XML file. Parameters: - file_path (str): Path to the XML file. - selected_banks (list, optional): List of bank names to extract (e.g., ["bank1", "bank2"]). If None, all banks will be extracted. Returns: - dict: Dictionary with bank names as keys and their data as values. """ 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: bank_name = location.attrib.get("name", "") # If selected_banks is None, extract all banks. Otherwise, filter based on selection. if selected_banks is None or bank_name in selected_banks: 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 Usage: # file_path = "your_file.xml" # # # **Option 1: Extract All Banks (Default)** # all_banks_data = extract_bank_data(file_path) # print("Extracted All Banks:", all_banks_data) # # # **Option 2: Extract Only Specific Banks (e.g., bank1 to bank14)** # selected_banks = [f"bank{i}" for i in range(1, 15)] # specific_banks_data = extract_bank_data(file_path, selected_banks) # print("Extracted Selected Banks:", specific_banks_data) # 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") def count_total_pixels(bank_data): """ Calculates the total number of pixels across all detector banks. Parameters: bank_data (dict): Dictionary containing detector bank data. Returns: int: Total number of pixels. """ pack_types = get_pack_types() # Assuming this function returns detector pack types total_pixels = 0 for data in bank_data.values(): pack = pack_types[data["pack_type"]] total_pixels += pack["tube_count"] * pack["pixels_in_tube"] return total_pixels # Example usage: # total_pixels = count_total_pixels(bank_data) # print("Total number of pixels:", total_pixels)