ModuleTolerancing

Provides functions for analysing the alignment tolerance of a system.

Mostly it consists in mis-aligning the optical elements of the system and checking the impact on the focal spot and delays.

There are two parts to this module:

  • Misaligning/deteriorating the optical elements of the system
  • Analysing the impact of these misalignments on the focal spot and delays

The end goal is to have a simple function "GetTolerance" that will return the tolerance of the system to misalignments of each optical element.

Created in Nov 2024

@author: Andre Kalouguine

 1"""
 2Provides functions for analysing the alignment tolerance of a system.
 3
 4Mostly it consists in mis-aligning the optical elements of the system and checking the impact on the focal spot and delays.
 5
 6There are two parts to this module:
 7- Misaligning/deteriorating the optical elements of the system
 8- Analysing the impact of these misalignments on the focal spot and delays
 9
10The end goal is to have a simple function "GetTolerance" that will return the tolerance of the system to misalignments of each optical element.
11
12Created in Nov 2024
13
14@author: Andre Kalouguine
15"""
16import numpy as np
17import matplotlib.pyplot as plt
18from copy import copy
19from scipy.stats import linregress
20
21import ARTcore.ModuleOpticalChain as moc
22
23
24def GetTolerance(OpticalChain,
25                 time_error=1.0,
26                 Detector = "Focus",
27                 n_iter = 5,
28                 elements_to_misalign = None):
29    """
30    Returns the tolerance of the system to misalignments of each optical element.
31    It first constructs a normalisation vector:
32    For each optical element, for each degree of freedom, it iteratively calculates the required amplitude of misalignment to reach the time_error.
33    This gives a n-dimensional vector. The smaller the value, the more sensitive the system is to misalignments of this axis.
34    """
35    # Define the normalisation vector
36    normalisation_vector = np.zeros(len(OpticalChain.optical_elements) * 6)*np.nan
37    durations = np.zeros(len(OpticalChain.optical_elements) * 6)*np.nan
38    misalignments = ["rotate_roll_by", "rotate_pitch_by", "rotate_yaw_by", "shift_along_normal", "shift_along_major", "shift_along_cross"]
39    if elements_to_misalign is None:
40        elements_to_misalign = range(len(OpticalChain.optical_elements))
41    if isinstance(Detector, str):
42        Det = OpticalChain.detectors[Detector]
43    else:
44        Det = Detector
45    for i in elements_to_misalign:
46        for j in range(6):
47            # Misalign the optical element
48            amplitude = 1e-3
49            for k in range(n_iter):
50                try:
51                    misaligned_optical_chain = copy(OpticalChain)
52                    r_before = misaligned_optical_chain[i].r
53                    q_before = misaligned_optical_chain[i].q
54                    misaligned_optical_chain[i].__getattribute__(misalignments[j])(amplitude)
55                    r_after = misaligned_optical_chain[i].r
56                    q_after = misaligned_optical_chain[i].q
57                    rays = misaligned_optical_chain.get_output_rays()
58                    Det.optimise_distance(rays[Det.index], [Det.distance-100, Det.distance+100], Det._spot_size, maxiter=10, tol=1e-10)
59                except:
60                    print(f"OE {i} failed to misalign {misalignments[j]} by {amplitude}")
61                    amplitude /= 10
62                    continue
63                rays = misaligned_optical_chain.get_output_rays(force=True)
64                duration = np.std(Det.get_Delays(rays[Det.index]))
65                if len(rays[Det.index]) <= 50:
66                    amplitude /= 2
67                    continue
68                if duration > time_error:
69                    amplitude /= 3
70                elif duration < time_error / 10:
71                    amplitude *= 3
72                elif duration < time_error / 1.5:
73                    amplitude *= 1.2
74                else:
75                    break
76            if not (time_error/2 < duration < time_error*2):
77                print(f"OE {i} failed to misalign {misalignments[j]} by {amplitude}: duration = {duration}")
78                if duration > time_error:
79                    amplitude = np.nan
80                else:
81                    amplitude = 0.1
82            normalisation_vector[i*6+j] = amplitude
83            durations[i*6+j] = duration
84    return normalisation_vector, durations
def GetTolerance( OpticalChain, time_error=1.0, Detector='Focus', n_iter=5, elements_to_misalign=None):
25def GetTolerance(OpticalChain,
26                 time_error=1.0,
27                 Detector = "Focus",
28                 n_iter = 5,
29                 elements_to_misalign = None):
30    """
31    Returns the tolerance of the system to misalignments of each optical element.
32    It first constructs a normalisation vector:
33    For each optical element, for each degree of freedom, it iteratively calculates the required amplitude of misalignment to reach the time_error.
34    This gives a n-dimensional vector. The smaller the value, the more sensitive the system is to misalignments of this axis.
35    """
36    # Define the normalisation vector
37    normalisation_vector = np.zeros(len(OpticalChain.optical_elements) * 6)*np.nan
38    durations = np.zeros(len(OpticalChain.optical_elements) * 6)*np.nan
39    misalignments = ["rotate_roll_by", "rotate_pitch_by", "rotate_yaw_by", "shift_along_normal", "shift_along_major", "shift_along_cross"]
40    if elements_to_misalign is None:
41        elements_to_misalign = range(len(OpticalChain.optical_elements))
42    if isinstance(Detector, str):
43        Det = OpticalChain.detectors[Detector]
44    else:
45        Det = Detector
46    for i in elements_to_misalign:
47        for j in range(6):
48            # Misalign the optical element
49            amplitude = 1e-3
50            for k in range(n_iter):
51                try:
52                    misaligned_optical_chain = copy(OpticalChain)
53                    r_before = misaligned_optical_chain[i].r
54                    q_before = misaligned_optical_chain[i].q
55                    misaligned_optical_chain[i].__getattribute__(misalignments[j])(amplitude)
56                    r_after = misaligned_optical_chain[i].r
57                    q_after = misaligned_optical_chain[i].q
58                    rays = misaligned_optical_chain.get_output_rays()
59                    Det.optimise_distance(rays[Det.index], [Det.distance-100, Det.distance+100], Det._spot_size, maxiter=10, tol=1e-10)
60                except:
61                    print(f"OE {i} failed to misalign {misalignments[j]} by {amplitude}")
62                    amplitude /= 10
63                    continue
64                rays = misaligned_optical_chain.get_output_rays(force=True)
65                duration = np.std(Det.get_Delays(rays[Det.index]))
66                if len(rays[Det.index]) <= 50:
67                    amplitude /= 2
68                    continue
69                if duration > time_error:
70                    amplitude /= 3
71                elif duration < time_error / 10:
72                    amplitude *= 3
73                elif duration < time_error / 1.5:
74                    amplitude *= 1.2
75                else:
76                    break
77            if not (time_error/2 < duration < time_error*2):
78                print(f"OE {i} failed to misalign {misalignments[j]} by {amplitude}: duration = {duration}")
79                if duration > time_error:
80                    amplitude = np.nan
81                else:
82                    amplitude = 0.1
83            normalisation_vector[i*6+j] = amplitude
84            durations[i*6+j] = duration
85    return normalisation_vector, durations

Returns the tolerance of the system to misalignments of each optical element. It first constructs a normalisation vector: For each optical element, for each degree of freedom, it iteratively calculates the required amplitude of misalignment to reach the time_error. This gives a n-dimensional vector. The smaller the value, the more sensitive the system is to misalignments of this axis.