def gpi_g10s40_asmanufactured(mag): """ In PPM metal space - measured cooordinates from sample G10S40""" holedia = 0.920 * mm holectrs = [ [-1.061 * mm, -3.882 * mm], [-0.389 * mm, -5.192 * mm], [2.814 * mm, -0.243 * mm], [3.616 * mm, 0.995 * mm], [-4.419 * mm, 2.676 * mm], [-1.342 * mm, 5.077 * mm], [-4.672 * mm, -2.421 * mm], [4.157 * mm, -2.864 * mm], [5.091 * mm, 0.920 * mm], [1.599 * mm, 4.929 * mm], ] # design2metal mag holedia = holedia * mag print(mag) ctrs = [] #REVERSE = -1 # Flip y dimensions to match I&T data Dec 2012 REVERSE = 1 # With new affine updates, need to reverse the reverse March 2019 for r in holectrs: ctrs.append([r[0] * mag, r[1] * mag * REVERSE]) # return cut-the-metal coords per Lenox PPM mm spec in meters on PM ctrs_asbuilt = copy(ctrs) # March 2019: # Rotate hole centers by 90 deg to match GPI detector with new coordinate handling # no affine2d transformations 8/2018 AS ctrs_asbuilt = rotate2dccw(ctrs_asbuilt, 160 * np.pi / 180.0) # overwrites attributes return holedia, ctrs_asbuilt
def set_ctrs_rot(self, rotation_ccw_deg): """ test routine or used for preferrably small rotations of as_built centers. No-op, unchanged self.ctrs, if rotation exactly zero or False. Otherwise overwrites self.ctrs, the "live" centers of mask holes. In the interests of code clarity do not use for coordinate flips In data reduction piupelines DO NOT USE THIS ROUTINE - use the BEING REMOVED - DON'T USE - use Affine2d resampling of the image plane instead. """ vprint(""" LG_Model.NRM_Model.set_ctrs_rot(): BEING REMOVED - DON'T USE - use Affine2d instead""" ) if rotation_ccw_deg: self.ctrs = utils.rotate2dccw(self.ctrs_asbuilt, rotation_ccw_deg * np.pi / 180.0) self.rotation_ccw_deg = rotation_ccw_deg # record-keeping vprint( "\tLG_Model.NRM_Model.set_ctrs_rot: ctrs in V2V3, meters after rot %.2f deg:" % rotation_ccw_deg) else: vprint( "\tLG_Model.NRM_ModelLG_Model.set_ctrs_rot: ctrs in V2V3, meters:" ) vprint(self.ctrs, "****************")
def mast2sky(self): """ Rotate hole center coordinates: Clockwise by the V3 position angle - V3I_YANG from north in degrees if VPARITY = -1 Counterclockwise by the V3 position angle - V3I_YANG from north in degrees if VPARITY = 1 Hole center coords are in the V2, V3 plane in meters. Return rotated coordinates to be put in info4oif_dict. implane2oifits.ObservablesFromText uses these to calculate baselines. """ pa = self.pa mask_ctrs = self.mask.ctrs # rotate by an extra 90 degrees (RAC 9/21) # these coords are just used to orient output in OIFITS files # NOT used for the fringe fitting itself mask_ctrs = utils.rotate2dccw(mask_ctrs, np.pi / 2.) vpar = self.vparity # Relative sense of rotation between Ideal xy and V2V3 v3iyang = self.v3i_yang rot_ang = pa - v3iyang # subject to change! if pa != 0.0: # Using rotate2sccw, which rotates **vectors** CCW in a fixed coordinate system, # so to rotate coord system CW instead of the vector, reverse sign of rotation angle. Double-check comment if vpar == -1: # rotate clockwise <rotate coords clockwise?> ctrs_rot = utils.rotate2dccw(mask_ctrs, np.deg2rad(-rot_ang)) print( f'InstrumentData.mast2sky: Rotating mask hole centers clockwise by {rot_ang:.3f} degrees' ) else: # counterclockwise <rotate coords counterclockwise?> ctrs_rot = utils.rotate2dccw(mask_ctrs, np.deg2rad(rot_ang)) print( 'InstrumentData.mast2sky: Rotating mask hole centers counterclockwise by {rot_ang:.3f} degrees' ) else: ctrs_rot = mask_ctrs return ctrs_rot
def jwst_g7s6_centers_asbuilt( chooseholes=None): # was jwst_g7s6_centers_asdesigned holedict = { } # as_built names, C2 open, C5 closed, but as designed coordinates # Assemble holes by actual open segment names (as_built). Either the full mask or the # subset-of-holes mask will be V2-reversed after the as_designed centers are defined # Debug orientations with b4,c6,[c2] allholes = ('b4', 'c2', 'b5', 'b2', 'c1', 'b6', 'c6') b4, c2, b5, b2, c1, b6, c6 = ('b4', 'c2', 'b5', 'b2', 'c1', 'b6', 'c6') # design built holedict['b4'] = [0.00000000, -2.640000] #B4 -> B4 holedict['c2'] = [-2.2863100, 0.0000000] #C5 -> C2 holedict['b5'] = [2.2863100, -1.3200001] #B3 -> B5 holedict['b2'] = [-2.2863100, 1.3200001] #B6 -> B2 holedict['c1'] = [-1.1431500, 1.9800000] #C6 -> C1 holedict['b6'] = [2.2863100, 1.3200001] #B2 -> B6 holedict['c6'] = [1.1431500, 1.9800000] #C1 -> C6 # as designed MB coordinates (Mathilde Beaulieu, Peter, Anand). # as designed: segments C5 open, C2 closed, meters V2V3 per Paul Lightsey def # as built C5 closed, C2 open # # undistorted pupil coords on PM. These numbers are considered immutable. # as designed seg -> as built seg in comments each ctr entry (no distortion) if chooseholes: #holes B4 B5 C6 asbuilt for orientation testing print("\n chooseholes creates mask with JWST as_built holes ", chooseholes) #time.sleep(2) holelist = [] for h in allholes: if h in chooseholes: holelist.append(holedict[h]) ctrs_asdesigned = np.array(holelist) else: # the REAL THING - as_designed 7 hole, m in PM space, no distortion shape (7,2) ctrs_asdesigned = np.array([ [0.00000000, -2.640000], #B4 -> B4 as-designed -> as-built mapping [-2.2863100, 0.0000000], #C5 -> C2 [2.2863100, -1.3200001], #B3 -> B5 [-2.2863100, 1.3200001], #B6 -> B2 [-1.1431500, 1.9800000], #C6 -> C1 [2.2863100, 1.3200001], #B2 -> B6 [1.1431500, 1.9800000] ]) #C1 -> C6 # Preserve ctrs.as-designed (treat as immutable) # Reverse V2 axis coordinates to close C5 open C2, and others follow suit... # preserve cts.as_built (treat as immutable) ctrs_asbuilt = ctrs_asdesigned.copy() # create 'live' hole centers in an ideal, orthogonal undistorted xy pupil space, # eg maps open hole C5 in as_designed to C2 as_built, eg C4 unaffacted.... ctrs_asbuilt[:, 0] *= -1 # LG++ rotate hole centers by 90 deg to match MAST o/p DMS PSF with # no affine2d transformations 8/2018 AS # LG++ The above aligns the hole patern with the hex analytic FT, # flat top & bottom as seen in DMS data. 8/2018 AS ctrs_asbuilt = rotate2dccw(ctrs_asbuilt, np.pi / 2.0) # overwrites attributes # create 'live' hole centers in an ideal, orthogonal undistorted xy pupil space, return ctrs_asbuilt * m
def __init__(self, maskname=None, rotdeg=None, holeshape="circ", rescale=False,\ verbose=False, chooseholes=None): print("NRM_Mask_definitions.NIRISS: maskname", maskname) if maskname not in ["gpi_g10s40", "jwst_g7s6", "jwst_g7s6c", "visir_sam", \ "p1640", "keck_nirc2", "pharo", "NIRC2_9NRM"]: # Second row hopefully coming someday raise ValueError("mask %s not supported" % maskname) if holeshape == None: holeshape = 'circ' if holeshape not in [ "circ", "hex", ]: raise ValueError("mask %s not supported" % maskname) self.maskname = maskname if verbose: print("\n\t=====================================") print("Mask %s being created" % self.maskname) if self.maskname == "gpi_g10s40": self.hdia, self.ctrs = gpi_g10s40(rescale=rescale) #self.rotdeg = 115.0 # By inspection of I&T data Dec 2012 #if rotdeg is not None: # self.rotdeg = rotdeg # print("rotating by {0} deg".format(rotdeg)) #else: # print("rotating by 115.0 deg -- hard coded.") #self.ctrs = rotate2dccw(self.ctrs, self.rotdeg*np.pi/180.0) #self.rotate = self.rotdeg self.activeD = self.showmask( ) # calculates circle dia including all holes self.OD = 7770.1 * mm # DGS = 7770.1 * mm with M2 cardboard baffle # GS OD from GPI fundamental values self.ID = 1023.75 * mm # Formed by M2 diameter # (Bauman GPI_Optical_Fundamental_Values.doc) elif self.maskname == "jwst_g7s6c": """ activeD and D taken from webbpsf-data/NIRISS/coronagraph/MASK_NRM.fits""" print('self.maskname == "jwst_g7s6c":') self.hdia, self.ctrs = jwst_g7s6c(chooseholes=chooseholes) # self.activeD = 6.559 * m # webbpsf kwd DIAM - not a 'circle including all holes' self.OD = 6.610645669291339 * m # Full pupil file size, incl padding, webbpsf kwd PUPLDIAM if rotdeg is not None: self.rotdeg = rotdeg elif self.maskname == "jwst_g7s6": print("\tnot finished") elif self.maskname == "visir_sam": """Mask dimensions from Eric Pantin""" self.hdia, self.ctrs = visir_sam(rescale=rescale) self.rotdeg = 16.5 # By inspection of data if rotdeg is not None: self.rotdeg += rotdeg print("rotating by {0} + 9 (hardcoded) deg".format(rotdeg)) else: print("rotating by 9 deg -- hard coded") self.ctrs = rotate2dccw(self.ctrs, self.rotdeg * np.pi / 180.0) self.rotate = self.rotdeg self.activeD = self.showmask( ) # calculated circle dia including all holes self.OD = 8115.0 * mm # DVLT = 8115.0 * mm -- but what is the M2 dia?? # From Eric Pantin Document self.ID = 1000.0 * mm # Don't know this, but this number shouldn't matter elif self.maskname == "NIRC2_9NRM": """Mask dimensions from Steph Sallum?""" self.hdia, self.ctrs = keck_nrm() self.rotdeg = 28.0 # By inspection of data if rotdeg is not None: self.rotdeg += rotdeg print("rotating by {0} + 9 (hardcoded) deg".format(rotdeg)) else: print("rotating by 9 deg -- hard coded") self.ctrs = rotate2dccw(self.ctrs, self.rotdeg * np.pi / 180.0) self.rotate = self.rotdeg self.activeD = self.showmask( ) # calculated circle dia including all holes self.OD = 10.0 # DVLT = 8115.0 * mm -- but what is the M2 dia?? # From Eric Pantin Document self.ID = 1.0 # Don't know this, but this number shouldn't matter else: print("\tcheck back later")
def __init__(self, mask=None, holeshape="circ", pixscale=None, over=1, log=_default_log, pixweight=None, datapath="", phi=None, refdir="", chooseholes=False, affine2d=None, **kwargs): """ mask will either be a string keyword for built-in values or an NRM_mask_geometry object. pixscale should be input in radians. phi (rad) default changedfrom "perfect" to None (with bkwd compat.) """ # define a handler to write log messages to stdout sh = logging.StreamHandler(stream=sys.stdout) # define the format for the log messages, here: "level name: message" formatter = logging.Formatter("[%(levelname)s]: %(message)s") sh.setFormatter(formatter) self.logger = log self.logger.addHandler(sh) self.holeshape = holeshape self.pixel = pixscale # det pix in rad (square) self.over = over self.maskname = mask self.pixweight = pixweight # WARNING! JWST CHOOSEHOLES CODE NOW DUPLICATED IN mask_definitions.py WARNING! ### holedict = { } # as_built names, C2 open, C5 closed, but as designed coordinates # Assemble holes by actual open segment names. Either the full mask or the # subset-of-holes mask will be V2-reversed after the as_designed ceenters # are installed in the object. allholes = ('b4', 'c2', 'b5', 'b2', 'c1', 'b6', 'c6') b4, c2, b5, b2, c1, b6, c6 = ('b4', 'c2', 'b5', 'b2', 'c1', 'b6', 'c6') holedict['b4'] = [0.00000000, -2.640000] #B4 -> B4 holedict['c2'] = [-2.2863100, 0.0000000] #C5 -> C2 holedict['b5'] = [2.2863100, -1.3200001] #B3 -> B5 holedict['b2'] = [-2.2863100, 1.3200001] #B6 -> B2 holedict['c1'] = [-1.1431500, 1.9800000] #C6 -> C1 holedict['b6'] = [2.2863100, 1.3200001] #B2 -> B6 holedict['c6'] = [1.1431500, 1.9800000] #C1 -> C6 if mask == 'jwst': # as designed MB coordinates (Mathilde Beaulieu, Peter, Anand). # as designed: segments C5 open, C2 closed, meters V2V3 per Paul Lightsey def # as built C5 closed, C2 open # # undistorted pupil coords on PM. These numbers are considered immutable. # as designed seg -> as built seg in comments each ctr entry (no distortion) if chooseholes is not False: #holes B4 B5 C6 asbuilt for orientation testing holelist = [] for h in allholes: if h in chooseholes: holelist.append(holedict[h]) self.ctrs_asdesigned = np.array(holelist) else: # the REAL THING - as_designed 7 hole, m in PM space, no distortion shape (7,2) self.ctrs_asdesigned = np.array([ [0.00000000, -2.640000], #B4 -> B4 as-designed -> as-built mapping [-2.2863100, 0.0000000], #C5 -> C2 [2.2863100, -1.3200001], #B3 -> B5 [-2.2863100, 1.3200001], #B6 -> B2 [-1.1431500, 1.9800000], #C6 -> C1 [2.2863100, 1.3200001], #B2 -> B6 [1.1431500, 1.9800000] ]) #C1 -> C6 self.d = 0.80 * m self.D = 6.5 * m else: try: #print(mask.ctrs) pass except AttributeError: raise AttributeError( "Mask must be either 'jwst' or NRM_mask_geometry object") self.ctrs, self.d, self.D = np.array( mask.ctrs), mask.hdia, mask.activeD if mask == 'jwst': """ Preserve ctrs.as_designed (treat as immutable) Reverse V2 axis coordinates to close C5 open C2, and others follow suit... Preserve ctrs.as_built (treat as immutable) """ self.ctrs_asbuilt = self.ctrs_asdesigned.copy() # create 'live' hole centers in an ideal, orthogonal undistorted xy pupil space, # eg maps open hole C5 in as_designed to C2 as_built, eg C4 unaffacted.... self.ctrs_asbuilt[:, 0] *= -1 # LG++ rotate hole centers by 90 deg to match MAST o/p DMS PSF with # no affine2d transformations 8/2018 AS # LG++ The above aligns the hole patern with the hex analytic FT, # flat top & bottom as seen in DMS data. 8/2018 AS self.ctrs_asbuilt = utils.rotate2dccw(self.ctrs_asbuilt, np.pi / 2.0) # overwrites attributes # create 'live' hole centers in an ideal, orthogonal undistorted xy pupil space, self.ctrs = self.ctrs_asbuilt.copy() self.N = len(self.ctrs) self.datapath = datapath self.refdir = refdir self.fmt = "%10.4e" if phi: # meters of OPD at central wavelength if phi == "perfect": self.phi = np.zeros(self.N) # backwards compatibility vprint( 'LG_Model.__init__(): phi="perfect" deprecated in LG++. Omit phi or use phi=None' ) else: self.phi = phi else: self.phi = np.zeros(self.N) self.chooseholes = chooseholes """ affine2d property not to be changed in NRM_Model - create a new instance instead Save affine deformation of pupil object or create a no-deformation object. We apply this when sampling the PSF, not to the pupil geometry. """ if affine2d is None: self.affine2d = utils.Affine2d(mx=1.0, my=1.0, sx=0.0, sy=0.0, xo=0.0, yo=0.0, name="Ideal") else: self.affine2d = affine2d