diff --git a/sn_tools/sn_calcFast.py b/sn_tools/sn_calcFast.py index 7c667f68..a5daf4b8 100644 --- a/sn_tools/sn_calcFast.py +++ b/sn_tools/sn_calcFast.py @@ -56,7 +56,7 @@ def __init__(self, reference_lc, dustcorr, x1, color, telescope, mjdCol='observationStartMJD', RACol='fieldRA', DecCol='fieldDec', filterCol='filter', exptimeCol='visitExposureTime', - m5Col='fiveSigmaDepth', seasonCol='season', nexpCol='numExposures', + m5Col='fiveSigmaDepth', seasonCol='season', nexpCol='numExposures',seeingCol='seeingFwhmEff', snr_min=5., lightOutput=True, ebvofMW=-1.0, @@ -72,6 +72,8 @@ def __init__(self, reference_lc, dustcorr, x1, color, self.exptimeCol = exptimeCol self.seasonCol = seasonCol self.nexpCol = nexpCol + self.seeingCol = seeingCol + self.x1 = x1 self.color = color self.lightOutput = lightOutput @@ -399,9 +401,13 @@ def processBand(self, sel_obs, ebvofMW, band, gen_par, j=-1, output_q=None): np.tile(sel_obs[self.nexpCol], (nvals, 1)), mask=~flag) m5_obs = np.ma.array( np.tile(sel_obs[self.m5Col], (nvals, 1)), mask=~flag) + if self.seeingCol in sel_obs.dtype.names: + seeings = np.ma.array( + np.tile(sel_obs[self.seeingCol], (nvals, 1)), mask=~flag) + healpixIds = np.ma.array( np.tile(sel_obs['healpixID'].astype(int), (nvals, 1)), mask=~flag) - + pixRAs = np.ma.array( np.tile(sel_obs['pixRA'], (nvals, 1)), mask=~flag) @@ -430,12 +436,15 @@ def processBand(self, sel_obs, ebvofMW, band, gen_par, j=-1, output_q=None): lc['mag'] = mag_obs[~mag_obs.mask] if not self.lightOutput: lc['gamma'] = gammas[~gammas.mask] - lc['m5'] = m5_obs[~m5_obs.mask] + lc['m5'] = m5_obs[~m5_obs.mask] lc['mag'] = mag_obs[~mag_obs.mask] lc['magerr'] = (2.5/np.log(10.))/snr_m5[~snr_m5.mask] lc['time'] = obs_time[~obs_time.mask] lc[self.exptimeCol] = exp_time[~exp_time.mask] lc[self.nexpCol] = nexposures[~nexposures.mask] + if self.seeingCol in sel_obs.dtype.names: + lc[self.seeingCol] = seeings[~seeings.mask] + lc['band'] = ['LSST::'+band]*len(lc) lc['zp'] = self.zp[band] lc['zp'] = 2.5*np.log10(3631) diff --git a/sn_tools/sn_utils.py b/sn_tools/sn_utils.py index 37a949cc..825d7c2d 100644 --- a/sn_tools/sn_utils.py +++ b/sn_tools/sn_utils.py @@ -138,19 +138,13 @@ class GenerateSample: filterCol : str, opt name of the column corresponding to filter Default : 'filter' - min_rf_phase : float, opt - min rest-frame phase for supernovae - Default : -15. - max_rf_phase : float, opt - max rest-frame phase for supernovae - Default : 30. area : float, opt area of the survey (in deg\^2) Default : 9.6 deg\^2 """ - def __init__(self, sn_parameters, cosmo_parameters, mjdCol='mjd', seasonCol='season', filterCol='filter', min_rf_phase=-15., max_rf_phase=30., area=9.6, dirFiles='reference_files', web_path=''): + def __init__(self, sn_parameters, cosmo_parameters, mjdCol='mjd', seasonCol='season', filterCol='filter', area=9.6, dirFiles='reference_files', web_path=''): self.dirFiles = dirFiles self.params = sn_parameters self.sn_rate = SN_Rate(rate=self.params['z']['rate'], @@ -162,8 +156,10 @@ def __init__(self, sn_parameters, cosmo_parameters, mjdCol='mjd', seasonCol='sea self.seasonCol = seasonCol self.filterCol = filterCol self.area = area - self.min_rf_phase = min_rf_phase - self.max_rf_phase = max_rf_phase + self.min_rf_phase = self.params['min_rf_phase'] + self.max_rf_phase =self.params['max_rf_phase'] + self.min_rf_phase_qual = self.params['min_rf_phase_qual'] + self.max_rf_phase_qual =self.params['max_rf_phase_qual'] self.web_path = web_path def __call__(self, obs): @@ -195,6 +191,10 @@ def __call__(self, obs): min rest-frame phase for LC points max_rf_phase, float max rest-frame phase for LC points + min_rf_phase_qual, float + min rest-frame phase for T0 estimation + max_rf_phase_qual, float + max rest-frame phase for T0 estimation """ epsilon = 1.e-08 @@ -216,7 +216,7 @@ def __call__(self, obs): if len(r) > 0: names = ['z', 'x1', 'color', 'daymax', 'epsilon_x0', 'epsilon_x1', 'epsilon_color', - 'epsilon_daymax', 'min_rf_phase', 'max_rf_phase'] + 'epsilon_daymax', 'min_rf_phase', 'max_rf_phase','min_rf_phase_qual','max_rf_phase_qual'] types = ['f8']*len(names) # params = np.zeros(len(r), dtype=list(zip(names, types))) params = np.asarray(r, dtype=list(zip(names, types))) @@ -259,7 +259,10 @@ def getParameters(self, daymin, daymax, duration): min rest-frame phase for LC points max_rf_phase, float max rest-frame phase for LC points - + min_rf_phase_qual, float + min rest-frame phase for T0 estimation + max_rf_phase, float + max rest-frame phase for T0 estimation """ # get z range @@ -304,7 +307,7 @@ def getParameters(self, daymin, daymax, duration): T0_values = [daymin+21.*(1.+z)] if self.params['daymax']['type'] == 'random': T0_values = np.arange( - daymin-(1.+z)*self.min_rf_phase, daymax-(1.+z)*self.max_rf_phase, 0.1) + daymin-(1.+z)*self.min_rf_phase_qual, daymax-(1.+z)*self.max_rf_phase, 0.1) dist_daymax = T0_values # print('daymax',dist_daymax,type(dist_daymax)) if dist_daymax.size == 0: @@ -313,7 +316,7 @@ def getParameters(self, daymin, daymax, duration): -1., dist_daymax, [1./len(dist_daymax)]*len(dist_daymax)) r.append((z, x1_color[0], x1_color[1], T0, 0., - 0., 0., 0., self.min_rf_phase, self.max_rf_phase)) + 0., 0., 0., self.min_rf_phase_qual, self.max_rf_phase)) if self.params['z']['type'] == 'uniform': zstep = self.params['z']['step'] @@ -326,8 +329,8 @@ def getParameters(self, daymin, daymax, duration): if z < 1.e-6: z = 0.01 if self.params['daymax']['type'] == 'uniform': - T0_min = daymin-(1.+z)*self.min_rf_phase - T0_max = daymax-(1.+z)*self.max_rf_phase + T0_min = daymin-(1.+z)*self.min_rf_phase_qual + T0_max = daymax-(1.+z)*self.max_rf_phase_qual T0_min = daymin T0_max = daymax nT0 = int((T0_max-T0_min)/daystep) @@ -341,22 +344,22 @@ def getParameters(self, daymin, daymax, duration): for T0 in T0_values: r.append((z, x1_color[0], x1_color[1], T0, 0., - 0., 0., 0., self.min_rf_phase, self.max_rf_phase)) + 0., 0., 0., self.min_rf_phase, self.max_rf_phase,self.min_rf_phase_qual, self.max_rf_phase_qual)) if self.params['z']['type'] == 'unique': daystep = self.params['daymax']['step'] x1_color = self.params['x1_color']['min'] z = self.params['z']['min'] if self.params['daymax']['type'] == 'uniform': - T0_min = daymin-(1.+z)*self.min_rf_phase - T0_max = daymax-(1.+z)*self.max_rf_phase + T0_min = daymin-(1.+z)*self.min_rf_phase_qual + T0_max = daymax-(1.+z)*self.max_rf_phase_qual nT0 = int((T0_max-T0_min)/daystep) T0_values = np.linspace(T0_min, T0_max, nT0+1) if self.params['daymax']['type'] == 'unique': T0_values = [daymin+21.*(1.+z)] for T0 in T0_values: r.append((z, x1_color[0], x1_color[1], T0, 0., - 0., 0., 0., self.min_rf_phase, self.max_rf_phase)) + 0., 0., 0., self.min_rf_phase, self.max_rf_phase,self.min_rf_phase_qual, self.max_rf_phase_qual)) rdiff = [] if self.params['differential_flux']: for rstart in r: @@ -471,11 +474,6 @@ class SimuParameters: filterCol : str, opt name of the column corresponding to filter Default : 'filter' - min_rf_phase : float, opt - min rest-frame phase for supernovae - Default : -15. - max_rf_phase : float, opt - max rest-frame phase for supernovae Default : 30. area : float, opt area of the survey (in deg\^2) @@ -488,8 +486,7 @@ class SimuParameters: """ def __init__(self, sn_parameters, cosmo_parameters, - mjdCol='mjd', seasonCol='season', filterCol='filter', - min_rf_phase=-15., max_rf_phase=30., area=9.6, dirFiles='reference_files', web_path=''): + mjdCol='mjd', seasonCol='season', filterCol='filter',area=9.6, dirFiles='reference_files', web_path=''): self.dirFiles = dirFiles self.params = sn_parameters self.sn_rate = SN_Rate(rate=self.params['z']['rate'], @@ -501,8 +498,10 @@ def __init__(self, sn_parameters, cosmo_parameters, self.seasonCol = seasonCol self.filterCol = filterCol self.area = area - self.min_rf_phase = min_rf_phase - self.max_rf_phase = max_rf_phase + self.min_rf_phase = self.params['min_rf_phase'] + self.max_rf_phase = self.params['max_rf_phase'] + self.min_rf_phase_qual = self.params['min_rf_phase_qual'] + self.max_rf_phase_qual = self.params['max_rf_phase_qual'] def getDist(self, rate): """ get (x1,color) distributions @@ -549,7 +548,7 @@ def Params(self, obs): ---------- numpy array with the following columns: color, z, daymax, x1, epsilon_x0, epsilon_x1, - epsilon_color, epsilon_daymax, min_rf_phase, max_rf_phase + epsilon_color, epsilon_daymax, min_rf_phase, max_rf_phase,min_rf_phase_qual,max_rf_phase_qual """ @@ -601,7 +600,10 @@ def Params(self, obs): # finally add min and max rf pars['min_rf_phase'] = self.min_rf_phase pars['max_rf_phase'] = self.max_rf_phase + pars['min_rf_phase_qual'] = self.min_rf_phase_qual + pars['max_rf_phase_qual'] = self.max_rf_phase_qual + #print('total number of SN to simulate:', len(pars)) return pars.to_records(index=False) @@ -689,8 +691,8 @@ def daymaxdist(self, pars, daymin, daymax): if daymaxtype == 'uniform': daymaxdf = pd.DataFrame() for z in pars['z'].values: - daymax_min = daymin-(1.+z)*self.min_rf_phase - daymax_max = daymax-(1.+z)*self.max_rf_phase + daymax_min = daymin-(1.+z)*self.min_rf_phase_qual + daymax_max = daymax-(1.+z)*self.max_rf_phase_qual if daymax_max-daymax_min >= 10: ndaymax = int((daymax_max-daymax_min)/daymaxstep)+1 df = pd.DataFrame(np.linspace( @@ -700,8 +702,8 @@ def daymaxdist(self, pars, daymin, daymax): if daymaxtype == 'random': daymaxdf = pd.DataFrame(pars) - daymaxdf['daymax_min'] = daymin-(1.+pars['z'])*self.min_rf_phase - daymaxdf['daymax_max'] = daymax-(1.+pars['z'])*self.max_rf_phase + daymaxdf['daymax_min'] = daymin-(1.+pars['z'])*self.min_rf_phase_qual + daymaxdf['daymax_max'] = daymax-(1.+pars['z'])*self.max_rf_phase_qual idx = daymaxdf['daymax_max']-daymaxdf['daymax_min'] >= 10. daymaxdf = daymaxdf[idx] if len(daymaxdf) > 0: diff --git a/sn_tools/sn_visu.py b/sn_tools/sn_visu.py index e007968c..06b555ff 100644 --- a/sn_tools/sn_visu.py +++ b/sn_tools/sn_visu.py @@ -6,12 +6,12 @@ from descartes.patch import PolygonPatch from shapely.geometry import MultiPolygon from shapely.ops import unary_union -from sn_tools.sn_obs import LSSTPointing, renameFields import time import itertools import numpy.lib.recfunctions as rf import pandas as pd - +from shapely import geometry +from shapely import affinity def fieldType(obs, RACol, DecCol): """ @@ -47,6 +47,72 @@ def fieldType(obs, RACol, DecCol): return df.to_records(index=False) +def LSSTPointing(xc, yc, angle_rot=0., area=None, maxbound=None): + """ + Function to build a focal plane for LSST + + Parameters + --------------- + + xc: float + x-position of the center FP (RA) + yc: float + y-position of the center FP (Dec) + angle_rot: float, opt + angle of rotation of the FP (default: 0.) + area: float + area for the FP (default: None) + maxbound: float + to reduce area (default: None) + Returns + ---------- + LSST FP (geometry.Polygon) + + """ + + """ + arr = [[3, 0], [12, 0], [12, 1], [13, 1], [13, 2], [14, 2], [14, 3], [15, 3], + [15, 12], [14, 12], [14, 13], [13, 13], [ + 13, 14], [12, 14], [12, 15], + [3, 15], [3, 14], [2, 14], [2, 13], [1, 13], [1, 12], [0, 12], + [0, 3], [1, 3], [1, 2], [2, 2], [2, 1], [3, 1]] + """ + # this is a quarter of LSST FP (with corner rafts) + arr = [[0.0, 7.5], [4.5, 7.5], [4.5, 6.5], [5.5, 6.5], [ + 5.5, 5.5], [6.5, 5.5], [6.5, 4.5], [7.5, 4.5], [7.5, 0.0]] + + # this is a quarter of LSST FP (without corner rafts) + arr = [[0.0, 7.5], [4.5, 7.5], [4.5, 4.5], [7.5, 4.5], [7.5, 0.0]] + if maxbound is not None: + arr = [[0.0, maxbound], [maxbound*4.5/7.5, maxbound], [maxbound*4.5 / + 7.5, maxbound*4.5/7.5], [maxbound, maxbound*4.5/7.5], [maxbound, 0.0]] + # symmetry I: y -> -y + arrcp = list(arr) + for val in arr[::-1]: + if val[1] > 0.: + arrcp.append([val[0], -val[1]]) + + # symmetry II: x -> -x + arr = list(arrcp) + for val in arrcp[::-1]: + if val[0] > 0.: + arr.append([-val[0], val[1]]) + + # build polygon + poly_orig = geometry.Polygon(arr) + + # set area + if area is not None: + poly_orig = affinity.scale(poly_orig, xfact=np.sqrt( + area/poly_orig.area), yfact=np.sqrt(area/poly_orig.area)) + + # set rotation angle + rotated_poly = affinity.rotate(poly_orig, angle_rot) + + return affinity.translate(rotated_poly, + xoff=xc-rotated_poly.centroid.x, + yoff=yc-rotated_poly.centroid.y) + def area(polylist): """Estimate area of a set of polygons (without overlaps) @@ -76,10 +142,8 @@ class to get a snapshot of the (RA, Dec) pointings (LSST FP) observed map per ni location dir of the db file dbName: str name of the db of interest. Extension: npy! - night_min: int, opt - min night to consider (default: 1) - night_max: int, opt - max night to consider (default: 3) + nights: list(int) + list of nights to display saveFig: bool, opt to save the figure result or not (default: False) areaTime: bool, opt @@ -89,7 +153,7 @@ class to get a snapshot of the (RA, Dec) pointings (LSST FP) observed map per ni """ - def __init__(self, dbDir, dbName, nightmin=1, nightmax=3, saveFig=False, areaTime=False, realTime=False): + def __init__(self, dbDir, dbName, nights=[1,2,3], saveFig=False, areaTime=False, realTime=False): self.dbName = dbName self.saveFig = saveFig @@ -102,11 +166,16 @@ def __init__(self, dbDir, dbName, nightmin=1, nightmax=3, saveFig=False, areaTim obs.sort(order='observationStartMJD') # Select observations + """ idx = obs['night'] <= nightmax idx &= obs['night'] >= nightmin obs = obs[idx] - - for night in range(np.min(obs['night']), np.max(obs['night'])+1): + """ + idx = np.isin(obs['night'],nights) + obs = obs[idx] + + #for night in range(np.min(obs['night']), np.max(obs['night'])+1): + for night in nights: # if night > np.min(obs['night']): # self.ax.clear() idx = obs['night'] == night @@ -120,14 +189,29 @@ def __init__(self, dbDir, dbName, nightmin=1, nightmax=3, saveFig=False, areaTim mjds = obs_disp['observationStartMJD']-mjd0 nchanges = len(list(itertools.groupby(obs_disp['filter'])))-1 iwfd = obs_disp['fieldType'] == 'WFD' + selWFD = obs_disp[iwfd] nchanges_noddf = len( - list(itertools.groupby(obs_disp[iwfd]['filter'])))-1 + list(itertools.groupby(selWFD['filter'])))-1 + + countfilter = {} + for b in 'ugrizy': + idx = selWFD['filter'] == b + countfilter[b] = len(selWFD[idx]) + iddf = obs_disp['fieldType'] != 'WFD' + nddf = len(obs_disp[iddf]) + + nvisits = '' + for key, vals in countfilter.items(): + nvisits += '{} : {} - '.format(key,int(vals)) + + nvisits += 'ddf : {}'.format(nddf) + self.fig.suptitle( - 'night {} - filter changes: {}/{}'.format(night, nchanges_noddf, nchanges)) + 'night {} \n filter changes: {}/{} \n {}'.format(night, nchanges_noddf, nchanges,nvisits)) # area observed versus time for val in obs_disp: - pointing = LSSTPointing(val['fieldRA'], val['fieldDec']) + pointing = LSSTPointing(val['fieldRA'], val['fieldDec'],area=1.) if val['fieldType'] == 'WFD': p = PolygonPatch( pointing, facecolor=self.colors[val['filter']], edgecolor=self.colors[val['filter']]) @@ -245,7 +329,7 @@ class CadenceMovie: draw observed area vs time in an embedded histo (default: False) """ - def __init__(self, dbDir, dbName, nights=1, title='', total=600, sub=100, fps=24, saveMovie=False, realTime=False, saveFig=False, areaTime=False): + def __init__(self, dbDir, dbName, nights=[1,2,3], title='', total=600, sub=100, fps=24, saveMovie=False, realTime=False, saveFig=False, areaTime=False): self.realTime = realTime self.plotParams() @@ -271,26 +355,41 @@ def __init__(self, dbDir, dbName, nights=1, title='', total=600, sub=100, fps=24 self.customize() # Select observations + """ idx = obs['night'] <= nights obs = obs[idx] + """ + + idx = np.isin(obs['night'],nights) + obs = obs[idx] + #print('hello',np.unique(obs['night'])) + self.polylist = [] if saveMovie: # Warning : to save the movie ffmpeg needs to be installed! print(manimation.writers.list()) - FFMpegWriter = manimation.writers['ffmpeg'] + writer_type = 'pillow' + extension = 'gif' + """ + writer_type = 'ffmpeg' + extension = 'mp4' + """ + #FFMpegWriter = manimation.writers['ffmpeg'] + Writer = manimation.writers[writer_type] metadata = dict(title=title, artist='Matplotlib', comment=title) - writer = FFMpegWriter( - fps=fps, metadata=metadata, bitrate=6000) - writer = anim.FFMpegWriter(fps=30, codec='hevc') - Name_mp4 = title + #writer = FFMpegWriter(fps=fps, metadata=metadata, bitrate=6000) + writer = Writer(fps=fps, metadata=metadata, bitrate=6000) + #writer = anim.FFMpegWriter(fps=30, codec='hevc') + Name_mp4 = '{}.{}'.format(title,extension) + print('name for saving',Name_mp4) with writer.saving(self.fig, Name_mp4, 250): self.loopObs(obs, writer=writer) else: self.loopObs(obs) - def showObs(self, obs, nchanges, nchanges_noddf, area, mjd0): + def showObs(self, obs, nchanges, nchanges_noddf, area, mjd0,countfilter,nddf): """ Display observation (RA, Dec) as time (MJD) evolves. Parameters @@ -309,12 +408,19 @@ def showObs(self, obs, nchanges, nchanges_noddf, area, mjd0): # grab MJD and night mjd = obs['observationStartMJD'] night = obs['night'] + moonPhase = obs['moonPhase'] + + nvisits = '' + for key, vals in countfilter.items(): + nvisits += '{} : {} - '.format(key,int(vals)) + nvisits += 'ddf : {}'.format(nddf) + self.fig.suptitle( - 'night {} - MJD {} \n filter changes: {}/{}'.format(night, np.round(mjd, 3), nchanges_noddf, nchanges)) + 'night {} - MJD {} \n filter changes: {}/{} \n {}'.format(night, np.round(mjd, 3), nchanges_noddf, nchanges,nvisits)) # LSST focal plane corresponding to this pointing - pointing = LSSTPointing(obs['fieldRA'], obs['fieldDec']) + pointing = LSSTPointing(obs['fieldRA'], obs['fieldDec'],area=1.) if obs['fieldType'] == 'WFD': p = PolygonPatch( pointing, facecolor=self.colors[obs['filter']], edgecolor=self.colors[obs['filter']]) @@ -339,7 +445,8 @@ def loopObs(self, obs, writer=None): default: None """ - for night in range(np.min(obs['night']), np.max(obs['night'])+1): + #for night in range(np.min(obs['night']), np.max(obs['night'])+1): + for night in np.unique(obs['night']): idx = obs['night'] == night obs_disp = obs[idx] # get fieldtype(WFD or DDF) @@ -351,17 +458,26 @@ def loopObs(self, obs, writer=None): sel = obs_disp[:k] nchanges = len(list(itertools.groupby(sel['filter'])))-1 ifw = sel['fieldType'] == 'WFD' + selWFD = sel[ifw] nchanges_noddf = len( - list(itertools.groupby(sel[ifw]['filter'])))-1 + list(itertools.groupby(selWFD['filter'])))-1 + countfilter = {} + for b in 'ugrizy': + idx = selWFD['filter'] == b + countfilter[b] = len(selWFD[idx]) + iddf = sel['fieldType'] != 'WFD' + nddf = len(sel[iddf]) + # show observations if self.areaTime: self.polylist.append(LSSTPointing( - obs_disp[k]['fieldRA'], obs_disp[k]['fieldDec'])) + obs_disp[k]['fieldRA'], obs_disp[k]['fieldDec']),area=1.) self.showObs(obs_disp[k], nchanges, nchanges_noddf, - area(self.polylist), mjd0) + area(self.polylist), mjd0,countfilter,nddf) else: + #print(sel[['observationStartMJD','fieldRA','fieldDec','filter']]) self.showObs(obs_disp[k], nchanges, nchanges_noddf, - 0., mjd0) + 0., mjd0,countfilter,nddf) self.fig.canvas.flush_events() if writer: writer.grab_frame() @@ -407,7 +523,7 @@ def customize(self): self.ax.set_xlabel('RA [deg]') self.ax.set_ylabel('Dec [deg]') self.ax.set_xlim(0, 360.) - self.ax.set_ylim(-90., 10.) + self.ax.set_ylim(-90., 20.) colorfilters = [] for band in self.colors.keys(): @@ -417,3 +533,66 @@ def customize(self): color='k', label='ddf')) plt.legend(handles=colorfilters, loc='upper left', bbox_to_anchor=(1., 0.5)) + +def renameFields(tab): + """ + Function to rename fields + + Parameters + -------------- + tab: array + original array of data + + Returns + --------- + array of data with modified field names + + """ + corresp = {} + + fillCorresp(tab, corresp, 'mjd', 'observationStartMJD') + fillCorresp(tab, corresp, 'RA', 'fieldRA') + fillCorresp(tab, corresp, 'Ra', 'fieldRA') + fillCorresp(tab, corresp, 'Dec', 'fieldDec') + fillCorresp(tab, corresp, 'band', 'filter') + fillCorresp(tab, corresp, 'exptime', 'visitExposureTime') + fillCorresp(tab, corresp, 'nexp', 'numExposures') + + # print(tab.dtype) + + rb = np.copy(tab) + for vv, vals in corresp.items(): + rb = rf.drop_fields(rb, vv) + if vv != 'band': + rb = rf.append_fields(rb, vals, tab[vv]) + else: + rb = rf.append_fields(rb, vals, tab[vv], dtypes=' varb + + """ + + if vara in tab.dtype.names and varb not in tab.dtype.names: + corres[vara] = varb