ModuleMask
Provides a class for masks, which are a type of optical element that simply stops rays that hit it.
Also provides the function TransmitMaskRayList that returns the rays transmitted by the mask.
Created in 2021
@author: Stefan Haessler + André Kalouguine
1""" 2Provides a class for masks, which are a type of optical element that simply stops rays that hit it. 3 4Also provides the function *TransmitMaskRayList* that returns the rays transmitted by the mask. 5 6 7 8 9 10Created in 2021 11 12@author: Stefan Haessler + André Kalouguine 13""" 14# %% Modules 15import ARTcore.ModuleGeometry as mgeo 16from ARTcore.ModuleGeometry import Point, Vector, Origin 17import ARTcore.ModuleSupport as msup 18import ARTcore.ModuleOpticalRay as mray 19import ARTcore.ModuleOpticalElement as moe 20import ARTcore.ModuleMirror as mmirror 21 22import numpy as np 23from copy import copy 24import logging 25 26logger = logging.getLogger(__name__) 27 28# %%############################################################################ 29class Mask(moe.OpticalElement): 30 """ 31 A mask: a plane surface of the shape of its [Support](ModuleSupport.html), which stops all rays that hit it, 32 while all other rays continue their path unchanged. No diffraction effects. 33 34 Attributes 35 ---------- 36 support : [Support](ModuleSupport.html)-object 37 38 type : str 'Mask'. 39 40 Methods 41 ---------- 42 get_local_normal(Point) 43 44 get_centre() 45 46 get_grid3D(NbPoints) 47 48 """ 49 50 def __init__(self, Support, **kwargs): 51 """ 52 Parameters 53 ---------- 54 Support : [Support](ModuleSupport.html)-object 55 """ 56 self.type = "Mask" 57 self.support = Support 58 self.curvature = mmirror.Curvature.FLAT 59 60 self.r0 = mgeo.Point([0.0, 0.0, 0.0]) 61 self._r = mgeo.Vector([0.0, 0.0, 0.0]) 62 self._q = np.quaternion(1) 63 64 self.centre_ref = mgeo.Point([0, 0, 0]) 65 66 self.support_normal_ref = mgeo.Vector([0, 0, 1]) 67 self.normal_ref = mgeo.Vector([0, 0, 1]) 68 self.majoraxis_ref = mgeo.Vector([1, 0, 0]) 69 70 self.add_global_vectors("support_normal", "normal", "majoraxis") 71 self.add_global_points("centre") 72 73 super().__init__() 74 75 def _get_intersection(self, Ray): 76 """ 77 Return the intersection point between Ray and the xy-plane. 78 If not in alignment mode, any intersection point that is on the support is blocked. 79 """ 80 t = -Ray.point[2] / Ray.vector[2] 81 I = mgeo.Point(Ray.point +Ray.vector * t) 82 return I, I in self.support 83 84 def _get_intersections(self, RayList): 85 """ 86 Return the intersection point between Ray and the xy-plane. 87 If not in alignment mode, any intersection point that is on the support is blocked. 88 """ 89 vector = mgeo.VectorArray([ray.vector for ray in RayList]) 90 point = mgeo.PointArray([ray.point for ray in RayList]) 91 t = -point[:, 2] / vector[:, 2] 92 I = mgeo.PointArray(point + vector * t[:, np.newaxis]) 93 return I, [not(i in self.support) for i in I] 94 95 def get_local_normal(self, Point): 96 """Return normal unit vector in point 'Point' on the mask.""" 97 return np.array([0, 0, 1]) 98 99 def _zfunc(self, PointArray): 100 """Return the z-values of the plane surface at the points in PointArray.""" 101 return np.zeros(len(PointArray)) 102 103 def propagate_raylist(self, RayList, alignment=False): 104 """ 105 Propagate a list of rays through the mask, returning the transmitted rays. 106 107 Parameters 108 ---------- 109 RayList : list 110 List of Ray objects to be propagated through the mask. 111 alignment : bool, optional 112 If True, the alignment of the mask is considered. Default is False. 113 114 Returns 115 ------- 116 list 117 List of transmitted Ray objects. 118 """ 119 transmitted_rays = [] 120 local_rays = [ray.to_basis(*self.basis) for ray in RayList] 121 intersection_points, OK = self._get_intersections(local_rays) 122 N = len(RayList) 123 for i in range(N): 124 # Then we find the intersection point in the mirror reference frame 125 intersection_point = intersection_points[i] 126 local_ray = local_rays[i] 127 if OK[i]: 128 local_transmittedray = copy(local_ray) 129 local_transmittedray.point = intersection_point 130 local_transmittedray.vector = local_ray.vector 131 local_transmittedray.incidence = mgeo.AngleBetweenTwoVectors(-local_ray.vector, self.normal_ref) 132 local_transmittedray.path = local_ray.path + (np.linalg.norm(intersection_point - local_ray.point),) 133 transmitted_rays.append(local_transmittedray.from_basis(*self.basis)) 134 if len(transmitted_rays) == 0: 135 logger.warning("No rays were transmitted by the mask.") 136 logger.debug(f"Mask: {self}") 137 logger.debug(f"First ray: {RayList[0]}") 138 logger.debug(f"First ray in mask reference frame: {RayList[0].to_basis(self.r0, self.r, self.q)}") 139 logger.debug(f"First ray intersection point: {self._get_intersection(RayList[0].to_basis(self.r0, self.r, self.q))}") 140 return mray.RayList.from_list(transmitted_rays) 141 142 143# %%############################################################################ 144def _TransmitMaskRay(Mask, PointMask, Ray): 145 """Returns the transmitted ray""" 146 PointRay = Ray.point 147 VectorRay = Ray.vector 148 NormalMask = Mask.get_local_normal(PointMask) 149 150 AngleIncidence = mgeo.AngleBetweenTwoVectors(VectorRay, NormalMask) 151 Path = Ray.path + (np.linalg.norm(PointMask - PointRay),) 152 153 RayTransmitted = Ray.copy_ray() 154 RayTransmitted.point = PointMask 155 RayTransmitted.vector = VectorRay 156 RayTransmitted.incidence = AngleIncidence 157 RayTransmitted.path = Path 158 159 return RayTransmitted 160 161 162# %% 163def TransmitMaskRayList(Mask, RayList): 164 """ 165 Returns the the transmitted rays that pass the mask for the list of 166 incident rays ListRay. 167 168 Rays that hit the support are not further propagated. 169 170 Updates the reflected rays' incidence angle and path. 171 172 Parameters 173 ---------- 174 Mask : Mask-object 175 176 ListRay : list[Ray-object] 177 178 """ 179 ListRayTransmitted = [] 180 for Ray in RayList: 181 PointMask = Mask._get_intersection(Ray) 182 183 if PointMask is not None: 184 RayTransmitted = _TransmitMaskRay(Mask, PointMask, Ray) 185 ListRayTransmitted.append(RayTransmitted) 186 187 return ListRayTransmitted
30class Mask(moe.OpticalElement): 31 """ 32 A mask: a plane surface of the shape of its [Support](ModuleSupport.html), which stops all rays that hit it, 33 while all other rays continue their path unchanged. No diffraction effects. 34 35 Attributes 36 ---------- 37 support : [Support](ModuleSupport.html)-object 38 39 type : str 'Mask'. 40 41 Methods 42 ---------- 43 get_local_normal(Point) 44 45 get_centre() 46 47 get_grid3D(NbPoints) 48 49 """ 50 51 def __init__(self, Support, **kwargs): 52 """ 53 Parameters 54 ---------- 55 Support : [Support](ModuleSupport.html)-object 56 """ 57 self.type = "Mask" 58 self.support = Support 59 self.curvature = mmirror.Curvature.FLAT 60 61 self.r0 = mgeo.Point([0.0, 0.0, 0.0]) 62 self._r = mgeo.Vector([0.0, 0.0, 0.0]) 63 self._q = np.quaternion(1) 64 65 self.centre_ref = mgeo.Point([0, 0, 0]) 66 67 self.support_normal_ref = mgeo.Vector([0, 0, 1]) 68 self.normal_ref = mgeo.Vector([0, 0, 1]) 69 self.majoraxis_ref = mgeo.Vector([1, 0, 0]) 70 71 self.add_global_vectors("support_normal", "normal", "majoraxis") 72 self.add_global_points("centre") 73 74 super().__init__() 75 76 def _get_intersection(self, Ray): 77 """ 78 Return the intersection point between Ray and the xy-plane. 79 If not in alignment mode, any intersection point that is on the support is blocked. 80 """ 81 t = -Ray.point[2] / Ray.vector[2] 82 I = mgeo.Point(Ray.point +Ray.vector * t) 83 return I, I in self.support 84 85 def _get_intersections(self, RayList): 86 """ 87 Return the intersection point between Ray and the xy-plane. 88 If not in alignment mode, any intersection point that is on the support is blocked. 89 """ 90 vector = mgeo.VectorArray([ray.vector for ray in RayList]) 91 point = mgeo.PointArray([ray.point for ray in RayList]) 92 t = -point[:, 2] / vector[:, 2] 93 I = mgeo.PointArray(point + vector * t[:, np.newaxis]) 94 return I, [not(i in self.support) for i in I] 95 96 def get_local_normal(self, Point): 97 """Return normal unit vector in point 'Point' on the mask.""" 98 return np.array([0, 0, 1]) 99 100 def _zfunc(self, PointArray): 101 """Return the z-values of the plane surface at the points in PointArray.""" 102 return np.zeros(len(PointArray)) 103 104 def propagate_raylist(self, RayList, alignment=False): 105 """ 106 Propagate a list of rays through the mask, returning the transmitted rays. 107 108 Parameters 109 ---------- 110 RayList : list 111 List of Ray objects to be propagated through the mask. 112 alignment : bool, optional 113 If True, the alignment of the mask is considered. Default is False. 114 115 Returns 116 ------- 117 list 118 List of transmitted Ray objects. 119 """ 120 transmitted_rays = [] 121 local_rays = [ray.to_basis(*self.basis) for ray in RayList] 122 intersection_points, OK = self._get_intersections(local_rays) 123 N = len(RayList) 124 for i in range(N): 125 # Then we find the intersection point in the mirror reference frame 126 intersection_point = intersection_points[i] 127 local_ray = local_rays[i] 128 if OK[i]: 129 local_transmittedray = copy(local_ray) 130 local_transmittedray.point = intersection_point 131 local_transmittedray.vector = local_ray.vector 132 local_transmittedray.incidence = mgeo.AngleBetweenTwoVectors(-local_ray.vector, self.normal_ref) 133 local_transmittedray.path = local_ray.path + (np.linalg.norm(intersection_point - local_ray.point),) 134 transmitted_rays.append(local_transmittedray.from_basis(*self.basis)) 135 if len(transmitted_rays) == 0: 136 logger.warning("No rays were transmitted by the mask.") 137 logger.debug(f"Mask: {self}") 138 logger.debug(f"First ray: {RayList[0]}") 139 logger.debug(f"First ray in mask reference frame: {RayList[0].to_basis(self.r0, self.r, self.q)}") 140 logger.debug(f"First ray intersection point: {self._get_intersection(RayList[0].to_basis(self.r0, self.r, self.q))}") 141 return mray.RayList.from_list(transmitted_rays)
A mask: a plane surface of the shape of its Support, which stops all rays that hit it, while all other rays continue their path unchanged. No diffraction effects.
Attributes
support : [Support](ModuleSupport.html)-object
type : str 'Mask'.
Methods
get_local_normal(Point)
get_centre()
get_grid3D(NbPoints)
51 def __init__(self, Support, **kwargs): 52 """ 53 Parameters 54 ---------- 55 Support : [Support](ModuleSupport.html)-object 56 """ 57 self.type = "Mask" 58 self.support = Support 59 self.curvature = mmirror.Curvature.FLAT 60 61 self.r0 = mgeo.Point([0.0, 0.0, 0.0]) 62 self._r = mgeo.Vector([0.0, 0.0, 0.0]) 63 self._q = np.quaternion(1) 64 65 self.centre_ref = mgeo.Point([0, 0, 0]) 66 67 self.support_normal_ref = mgeo.Vector([0, 0, 1]) 68 self.normal_ref = mgeo.Vector([0, 0, 1]) 69 self.majoraxis_ref = mgeo.Vector([1, 0, 0]) 70 71 self.add_global_vectors("support_normal", "normal", "majoraxis") 72 self.add_global_points("centre") 73 74 super().__init__()
Parameters
Support : [Support](ModuleSupport.html)-object
96 def get_local_normal(self, Point): 97 """Return normal unit vector in point 'Point' on the mask.""" 98 return np.array([0, 0, 1])
Return normal unit vector in point 'Point' on the mask.
104 def propagate_raylist(self, RayList, alignment=False): 105 """ 106 Propagate a list of rays through the mask, returning the transmitted rays. 107 108 Parameters 109 ---------- 110 RayList : list 111 List of Ray objects to be propagated through the mask. 112 alignment : bool, optional 113 If True, the alignment of the mask is considered. Default is False. 114 115 Returns 116 ------- 117 list 118 List of transmitted Ray objects. 119 """ 120 transmitted_rays = [] 121 local_rays = [ray.to_basis(*self.basis) for ray in RayList] 122 intersection_points, OK = self._get_intersections(local_rays) 123 N = len(RayList) 124 for i in range(N): 125 # Then we find the intersection point in the mirror reference frame 126 intersection_point = intersection_points[i] 127 local_ray = local_rays[i] 128 if OK[i]: 129 local_transmittedray = copy(local_ray) 130 local_transmittedray.point = intersection_point 131 local_transmittedray.vector = local_ray.vector 132 local_transmittedray.incidence = mgeo.AngleBetweenTwoVectors(-local_ray.vector, self.normal_ref) 133 local_transmittedray.path = local_ray.path + (np.linalg.norm(intersection_point - local_ray.point),) 134 transmitted_rays.append(local_transmittedray.from_basis(*self.basis)) 135 if len(transmitted_rays) == 0: 136 logger.warning("No rays were transmitted by the mask.") 137 logger.debug(f"Mask: {self}") 138 logger.debug(f"First ray: {RayList[0]}") 139 logger.debug(f"First ray in mask reference frame: {RayList[0].to_basis(self.r0, self.r, self.q)}") 140 logger.debug(f"First ray intersection point: {self._get_intersection(RayList[0].to_basis(self.r0, self.r, self.q))}") 141 return mray.RayList.from_list(transmitted_rays)
Propagate a list of rays through the mask, returning the transmitted rays.
Parameters
RayList : list List of Ray objects to be propagated through the mask. alignment : bool, optional If True, the alignment of the mask is considered. Default is False.
Returns
list List of transmitted Ray objects.
234def _RenderMirror(Mirror, Npoints=1000, draw_support = False, draw_points = False, draw_vectors = True , recenter_support = True): 235 """ 236 This function renders a mirror in 3D. 237 """ 238 mesh = Mirror._Render(Npoints) 239 p = pv.Plotter() 240 p.add_mesh(mesh) 241 if draw_support: 242 support = Mirror.support._Render() 243 if recenter_support: 244 support.translate(Mirror.r0,inplace=True) 245 p.add_mesh(support, color="gray", opacity=0.5) 246 247 if draw_vectors: 248 # We draw the important vectors of the optical element 249 # For that, if we have a "vectors" attribute, we use that 250 # (a dictionary with the vector names as keys and the colors as values) 251 # Otherwise we use the default vectors: "support_normal", "majoraxis" 252 if hasattr(Mirror, "vectors"): 253 vectors = Mirror.vectors 254 else: 255 vectors = {"support_normal_ref": "red", "majoraxis_ref": "blue"} 256 for vector, color in vectors.items(): 257 if hasattr(Mirror, vector): 258 p.add_arrows(Mirror.r0, 10*getattr(Mirror, vector), color=color) 259 260 if draw_points: 261 # We draw the important points of the optical element 262 # For that, if we have a "points" attribute, we use that 263 # (a dictionary with the point names as keys and the colors as values) 264 # Otherwise we use the default points: "centre_ref" 265 if hasattr(Mirror, "points"): 266 points = Mirror.points 267 else: 268 points = {"centre_ref": "red"} 269 for point, color in points.items(): 270 if hasattr(Mirror, point): 271 p.add_mesh(pv.Sphere(radius=1, center=getattr(Mirror, point)), color=color) 272 else: 273 p.add_mesh(pv.Sphere(radius=1, center=Mirror.r0), color="red") 274 p.show() 275 return p
This function renders a mirror in 3D.
164def TransmitMaskRayList(Mask, RayList): 165 """ 166 Returns the the transmitted rays that pass the mask for the list of 167 incident rays ListRay. 168 169 Rays that hit the support are not further propagated. 170 171 Updates the reflected rays' incidence angle and path. 172 173 Parameters 174 ---------- 175 Mask : Mask-object 176 177 ListRay : list[Ray-object] 178 179 """ 180 ListRayTransmitted = [] 181 for Ray in RayList: 182 PointMask = Mask._get_intersection(Ray) 183 184 if PointMask is not None: 185 RayTransmitted = _TransmitMaskRay(Mask, PointMask, Ray) 186 ListRayTransmitted.append(RayTransmitted) 187 188 return ListRayTransmitted
Returns the the transmitted rays that pass the mask for the list of incident rays ListRay.
Rays that hit the support are not further propagated.
Updates the reflected rays' incidence angle and path.
Parameters
Mask : Mask-object
ListRay : list[Ray-object]