def get_Lshells(lshells, tvec_datetime, crs, carsph, units): Lshell_fline = [] for lsh in lshells: # needs to be cartesian, output will be in Re # plot l shell as if at equator and prime merid newt = convert2([[lsh * R_E, 0, 0]], tvec_datetime, 'GEO', 'sph', ['m', 'deg', 'deg'], 'SM', 'car', ['m', 'm', 'm']) T = getBline(newt[0], tvec_datetime[0], 100) # repack T_repackx = T.x T_repacky = T.y T_repackz = T.z T_repack = [[tx, ty, tz] for tx, ty, tz in zip(T_repackx, T_repacky, T_repackz)] LshellT = [tvec_datetime[0] for i in range(len(T_repack))] T_convert = convert2(T_repack, LshellT, 'SM', 'car', ['Re', 'Re', 'Re'], crs, carsph, units) T_repackx = [tt[0] for tt in T_convert] T_repackz = [tt[2] for tt in T_convert] Lshell_fline.append([T_repackx, T_repackz]) return Lshell_fline
def rotateplane(rc, tvec_datetime, crs, carsph, units): # rotate to be at prime meridian for plotting purposes rot_crs = convert2(rc, tvec_datetime, crs, carsph, units, 'GEO', 'sph', ['Re', 'deg', 'deg']) rot_crs_new = [[rcs[0], rcs[1], 0] for rcs in rot_crs] final_crs = convert2(rot_crs_new, tvec_datetime, 'GEO', 'sph', ['Re', 'deg', 'deg'], crs, carsph, units) return final_crs
def find_init_list(raylist): ray_count = 0 final_list = [] xs = [] ys = [] #FLATTEN flat_list = [item for sublist in raylist for item in sublist] for r in flat_list: ray_count+=1 new_coords = convert2([r], [ray_datenum], 'SM', 'car', ['m','m','m'], 'GEO', 'sph', ['m','deg','deg']) new_coords = new_coords[0] if new_coords[2] > 180: long_new = new_coords[2] - 180 long_new = -1*(180 - long_new) else: long_new = new_coords[2] final_array = (float(new_coords[1]), float(long_new)) final_list.append(final_array) xs.append(long_new) ys.append(new_coords[1]) plt.scatter(xs,ys) plt.show() plt.close() print('found initial positions for', ray_count, 'rays') return final_list
def write_to_txt(raylist): ray_count = 0 bad_ray = 0 filename = ray_out_dir+'/output_mode'+str(md)+'.txt' a_file = open(filename, "w") xs = [] ys = [] for r in raylist: ray_count+=1 new_coords = convert2([r], [ray_datenum], 'SM', 'car', ['m','m','m'], 'GEO', 'sph', ['m','deg','deg']) new_coords = new_coords[0] xs.append(new_coords[2]) ys.append(new_coords[1]) final_array = [ray_count, new_coords[0], new_coords[1], new_coords[2]] line = [str(i) for i in final_array] # convert to strings a_file.write(' '.join(line) + "\n") print('saved output for', ray_count, 'rays') print(len(xs)) plt.scatter(xs,ys) plt.show() plt.close() a_file.close()
def get_initial_pos(raylist): init_pos = [] for r in raylist: ip = r['pos'].iloc[0] ip = [[ip.x,ip.y,ip.z]] pp = convert2(ip, [dt.datetime(2020,6,1,12,0,0)], 'SM','car',['m','m','m'],'GEO','sph',['m','deg','deg']) init_pos.append([pp[0][1],pp[0][2]]) return init_pos
def get_final_pos(raylist): final_pos = [] for r in raylist: ip = r['pos'].iloc[-1] ip = [[ip.x,ip.y,ip.z]] pp = convert2(ip, [dt.datetime(2020,6,1,12,0,0)], 'SM','car',['m','m','m'],'GEO','sph',['m','deg','deg']) final_pos.append(pp[0]) return final_pos
def plotgeomfactor(ray_datenum, raylist, ray_out_dir, crs, carsph, units, show_plot=True): # convert to desired coordinate system into vector list rays ray_coords = [] for r in raylist: w = r['w'] # comes in as SM car in m tmp_coords = list(zip(r['pos'].x, r['pos'].y, r['pos'].z)) # convert to a unit vector first tvec_datetime = [ ray_datenum + dt.timedelta(seconds=s) for s in r['time'] ] seconds_passed = [s for s in r['time']] new_coords = convert2(tmp_coords, tvec_datetime, 'SM', 'car', ['m', 'm', 'm'], crs, carsph, units) # save it ray_coords.append(new_coords) # -------------------------------- PLOTTING -------------------------------- # flatten ray_coords = ray_coords[0] r_init = ray_coords[0][0] lat_init = ray_coords[0][1] fig, ax = plt.subplots(figsize=(6, 6)) for ti, rc in enumerate(ray_coords): # find geometric factor gf = r_init * np.cos(lat_init * D2R) / (rc[0] * np.cos(rc[1] * D2R)) ax.scatter(seconds_passed[ti], gf, color='b') plt.xlabel('seconds') #plt.gcf().autofmt_xdate() plt.ylabel('Geom. Factor') plt.title( dt.datetime.strftime(ray_datenum, '%Y-%m-%d %H:%M:%S') + ' ' + str(round(w / (1e3 * np.pi * 2), 1)) + 'kHz') plt.savefig(ray_out_dir + '/' + dt.datetime.strftime(ray_datenum, '%Y_%m_%d_%H%M%S') + '_' + str(round(w / (1e3 * np.pi * 2), 1)) + 'kHz' + '_geomfac_RE' + '.png') if show_plot: plt.show() return
def propagatefromTLE(self, sec, orbit_dir, crs, carsph, units): if self.TLE == None: print('need TLE') return if orbit_dir == 'future': dt_array = [ self.time + dt.timedelta(seconds=s) for s in range(0, sec + 1) ] elif orbit_dir == 'past': dt_array = [ self.time + dt.timedelta(seconds=s) for s in range(-sec - 1, 0) ] else: dt_array = [ self.time + dt.timedelta(seconds=s) for s in range(-sec - 1, sec + 1) ] # store pos and vel data pos_array = np.zeros((len(dt_array), 3)) vel_array = np.zeros((len(dt_array), 3)) for ti, tt in enumerate(dt_array): satellite = twoline2rv(self.TLE[0], self.TLE[1], wgs84) position, velocity = satellite.propagate(tt.year, tt.month, tt.day, tt.hour, tt.minute, tt.second) pos_array[ti, :] = position vel_array[ti, :] = velocity # finally, convert (starts from GEI coords in km cartesian) converted_coords = convert2(pos_array, dt_array, 'GEI', 'car', ['km', 'km', 'km'], crs, carsph, units) converted_vel = convert2(vel_array, dt_array, 'GEI', 'car', ['km', 'km', 'km'], crs, carsph, units) self.pos = converted_coords self.vel = converted_vel self.time = dt_array # update time as well
def getBdir(ray_start, ray_datenum, rayfile_directory, thetas, phis, md, select_random=False): positions = ray_start directions = [(0, 0, 0)] freqs = [15e3] # going to run a ray real quick single_run_rays(ray_datenum, positions, directions, freqs, rayfile_directory, md, runmodeldump=False) # Load all the rayfiles in the output directory ray_out_dir = rayfile_directory + '/' + dt.datetime.strftime( ray_datenum, '%Y-%m-%d %H:%M:%S') file_titles = os.listdir(ray_out_dir) # create empty lists to fill with ray files and damp files raylist = [] for filename in file_titles: if '.ray' in filename and str(md) in filename and str( 'Main') in filename: print(filename) raylist += read_rayfile(os.path.join(ray_out_dir, filename)) # get b direction for this ray for r in raylist: B0 = [r['B0'].x[0], r['B0'].y[0], r['B0'].z[0]] # vec in T in SM car coordinates # create unit vector Bmag = np.sqrt(r['B0'].x[0]**2 + r['B0'].y[0]**2 + r['B0'].z[0]**2) Bunit = [r['B0'].x[0] / Bmag, r['B0'].y[0] / Bmag, r['B0'].z[0] / Bmag] # now we have Bunit in SM car # let's put it in spherical (easier for changing the wavenormal) sph_dir = convert2([Bunit], ray_datenum, 'SM', 'car', ['Re', 'Re', 'Re'], 'SM', 'sph', ['Re', 'deg', 'deg']) # also return resonance angle, can be useful for initializing rays from ray_plots import stix_parameters R, L, P, S, D = stix_parameters( r, 0, r['w']) # get stix params for initial time point resangle = np.arctan(np.sqrt(-P / S)) converted_dirs = [] # if select random was chosen, thetas and phis are passed in as list of zeros of length nrays if select_random == True: nrays = len(thetas) hemi_mult = thetas[0] thetas = [] phis = [] resangle_deg = resangle * 180 / np.pi for n in range(0, nrays): # sample theta as concentric circles around the z axis, max at resonance angle thetas.append((random.random() * (resangle_deg - 3))) # uniform azimuth around the z axis phis.append(random.random() * 360) if Bunit[0] == 0 or Bunit[2] == 0: r1 = [1, (-1 * Bunit[0] - Bunit[2]) / Bunit[1], 1] else: r1 = [1, 1, (-1 * Bunit[1] - Bunit[0]) / Bunit[2]] r1 = np.array(r1) / np.linalg.norm(np.array(r1)) r2 = np.cross(r1, Bunit) T_rotate = np.column_stack((r1, r2, Bunit)) #ax = plt.axes(projection='3d') for th, ph in zip(thetas, phis): r = 1 / (np.cos(th * D2R)) cone_vec = np.array([ r * np.sin(th * D2R) * np.cos(ph * D2R), r * np.sin(th * D2R) * np.sin(ph * D2R), r * np.cos(th * D2R) ]) cone_vec = np.matmul(T_rotate, np.transpose(cone_vec)) if hemi_mult == 180: zsign = -1 else: zsign = 1 cone_vec = cone_vec / np.linalg.norm(cone_vec) converted_dirs.append(zsign * cone_vec) #ax.quiver(0,0,0,zsign*cone_vec[0],zsign*cone_vec[1],zsign*cone_vec[2],length=1) #ax.quiver(0,0,0,Bunit[0],Bunit[1],Bunit[2],length=1.25) #ax.axes.set_xlim3d(left=0, right=1.5) #ax.axes.set_ylim3d(bottom=0, top=1.5) #ax.axes.set_zlim3d(bottom=0, top=1.5) #plt.show() #plt.close() # add theta and phi as desired else: for theta, phi in zip(thetas, phis): new_dir = [ sph_dir[0][0], sph_dir[0][1] + theta, sph_dir[0][2] + phi ] converted_dir = convert2([new_dir], ray_datenum, 'SM', 'sph', ['Re', 'deg', 'deg'], 'SM', 'car', ['Re', 'Re', 'Re']) converted_dirs.append(converted_dir[0]) return converted_dirs, resangle, thetas, phis # returns unit vector of directions corresponding to input theta and phi vals
# fudge_factor for o dens -- too high? ff = 0.45 idens[0, 0] = ff * ne / (1.0 + R13) if (R13 > 1e-30): idens[0, 1] = ne / (1.0 + 1.0 / R13) else: idens[0, 1] = 0.0 print(edens, idens) return edens, idens # ----------------------- some test runs --------------------- ray_datenum = dt.datetime(2014, 1, 1, 12, 0, tzinfo=dt.timezone.utc) ray_start = [10566782.256884905, 20779954.593417391, 2440378.7949459073] ray_start = convert2([ray_start], [ray_datenum], 'SM', 'car', ['m', 'm', 'm'], 'GEO', 'car', ['m', 'm', 'm']) edens, idens = get_edens(np.array(ray_start[0]) / 1e6, ray_datenum) # ne= 713500969.23876691 # noh= 3.2972745831626054E-011 # zbrat= 0.013818528031958991E-002 # # somewhat accurate far away, inacurate up close # in Mm # for an equatorial slice """ edens_array = [] odens_array = [] hdens_array = []
def plotray2D(ray_datenum, raylist, ray_out_dir, crs, carsph, units, md, t_save=None, show_plot=True, plot_density=False, damping_vals=None): # !!!!!!! ONLY suports cartesian plotting! # first, find the max index of refraction if plot_density is selected for weighting purposes if plot_density: r = raylist[0] # just grab the first ray w = r['w'] # call stix parameters to find resonance cone R, L, P, S, D = stix_parameters(r, 0, w) resangle = np.arctan(np.sqrt(-P / S)) root = -1 # whistler solution # Solve the cold plasma dispersion relation # subtract 3 to avoid infinity index of refr. and match the initialization at bfield script cos2phi = pow(np.cos(resangle - (3 * D2R)), 2) sin2phi = pow(np.sin(resangle - (3 * D2R)), 2) A = S * sin2phi + P * cos2phi B = R * L * sin2phi + P * S * (1.0 + cos2phi) discriminant = B * B - 4.0 * A * R * L * P n1sq = (B + np.sqrt(discriminant)) / (2.0 * A) n2sq = (B - np.sqrt(discriminant)) / (2.0 * A) # negative refers to the fact that ^^^ B - sqrt #n1 = np.sqrt(n1sq) if n2sq < 0: n2 = np.sqrt(n2sq * -1) else: n2 = np.sqrt(n2sq) nmax = n2 # initialize for next step weights = [] # convert to desired coordinate system into vector list rays ray_coords = [] ray_count = 0 for r in raylist: ray_count += 1 w = r['w'] # comes in as SM car in m tmp_coords = list(zip(r['pos'].x, r['pos'].y, r['pos'].z)) nmag = np.sqrt(r['n'].x.iloc[0]**2 + r['n'].y.iloc[0]**2 + r['n'].z.iloc[0]**2) tvec_datetime = [ ray_datenum + dt.timedelta(seconds=s) for s in r['time'] ] new_coords = convert2(tmp_coords, tvec_datetime, 'SM', 'car', ['m', 'm', 'm'], crs, carsph, units) # save it ray_coords.append(new_coords) if plot_density: # save weighting factor, constant along path the_weight = nmag / nmax if damping_vals: ray_damp = np.ones(len(new_coords)) time_array = damping_vals[0] all_damp = damping_vals[1] for rt, tt in enumerate(r['time']): for ti, ta in enumerate(time_array): if np.abs(tt - ta ) < 1e-2: # find which time point is closest ray_damp[rt] = all_damp[ti] weights.append(ray_damp * the_weight) else: weights.append(np.ones(len(new_coords)) * the_weight) # -------------------------------- PLOTTING -------------------------------- ray1 = ray_coords[0][0] ray1_sph = convert2([ray1], tvec_datetime, crs, carsph, units, 'GEO', 'sph', ['m', 'deg', 'deg']) # find ray starting long long = ray1_sph[0][2] # convert for cartopy issues if long > 180: plane_long = 180 - (long - 180) catch = -1 else: plane_long = long catch = 1 try: import cartopy import cartopy.crs as ccrs # make this into a an image (sneaky) ax = plt.axes(projection=ccrs.Orthographic( central_longitude=(catch * plane_long) - 90, central_latitude=0.0)) ax.add_feature(cartopy.feature.OCEAN, zorder=0) ax.add_feature(cartopy.feature.LAND, zorder=0, edgecolor='black') ax.coastlines() plt.savefig(ray_out_dir + '/ccrs_proj.png') plt.cla() import matplotlib.patches as patches import matplotlib.cbook as cbook img = plt.imread(ray_out_dir + '/ccrs_proj.png', format='png') fig, ax = plt.subplots(figsize=(6, 6)) im = ax.imshow(img, extent=[-1.62, 1.62, -1.3, 1.3], zorder=2) # not the most scientific patch = patches.Circle((0, 0), radius=1.02, transform=ax.transData) im.set_clip_path(patch) except: fig, ax = plt.subplots(figsize=(6, 6)) earth = plt.Circle((0, 0), 1, color='b', alpha=0.5, zorder=100) iono = plt.Circle((0, 0), (R_E + H_IONO) / R_E, color='g', alpha=0.5, zorder=99) ax.add_artist(earth) ax.add_artist(iono) # plot the rays rotated_rcoords = [] for rayc in ray_coords: for rc in rayc: rotated = rotateplane([rc], tvec_datetime, crs, carsph, units) rotated_rcoords.append(rotated[0]) # repack in xz plane rotated_rcoords_x = [rcs[0] for rcs in rotated_rcoords] rotated_rcoords_z = [rcs[2] for rcs in rotated_rcoords] if plot_density: # clean up weights list weights = [item for sublist in weights for item in sublist] binnum = 40 binlon = np.linspace(0, 4, num=binnum) binlat = np.linspace(-2, 2, num=binnum) cmap = plt.cm.get_cmap('Blues') # create the density plot # log norm scale nrays = 10000 h = ax.hist2d(rotated_rcoords_x, rotated_rcoords_z, bins=[np.array(binlon), np.array(binlat)], weights=weights, norm=mpl.colors.LogNorm(), cmap=cmap) ticks = [1, 10, 100, 1000, 1e4] tlabels = [10 * np.log10(i / nrays) for i in ticks] cbar = fig.colorbar(h[3], ax=ax, shrink=0.84, pad=0.02, ticks=ticks) cbar.set_label(label='dBW', weight='bold') cbar.ax.set_yticklabels([str(int(tt)) for tt in tlabels ]) # vertically oriented colorbar else: # or just plot the ray paths ax.scatter(rotated_rcoords_x[0], rotated_rcoords_z[0], c='Blue', s=10) ax.scatter(rotated_rcoords_x, rotated_rcoords_z, c='Black', s=1, zorder=103) if t_save: ax.scatter(rotated_rcoords_x[t_save], rotated_rcoords_z[t_save], c='r', s=20, zorder=104) # set as dummy variable for return nmax = 1 # final clean up # plot field lines (from IGRF13 model) L_shells = [2, 3, 4] # Field lines to draw Lshell_flines = get_Lshells(L_shells, tvec_datetime, crs, carsph, units) for lfline in Lshell_flines: ax.plot(lfline[0], lfline[1], color='Black', linewidth=1, linestyle='dashed') plt.xlabel('L (R$_E$)') plt.ylabel('L (R$_E$)') #plt.xlim([0, max(L_shells)]) plt.xlim([0, 3]) plt.ylim([-2, 2]) if t_save: plt.savefig(ray_out_dir + '/' + dt.datetime.strftime(ray_datenum, '%Y_%m_%d_%H%M%S') + '_' + str(round(w / (1e3 * np.pi * 2), 1)) + 'kHz' + '_2Dview' + str(md) + '_' + str(t_save) + '.png') else: plt.title( dt.datetime.strftime(ray_datenum, '%Y-%m-%d %H:%M:%S') + ' ' + str(round(w / (1e3 * np.pi * 2), 1)) + 'kHz') plt.savefig(ray_out_dir + '/' + dt.datetime.strftime(ray_datenum, '%Y_%m_%d_%H%M%S') + '_' + str(round(w / (1e3 * np.pi * 2), 1)) + 'kHz' + '_2Dview' + str(md) + '.png') if show_plot: plt.show() plt.close() return nmax
carsph='car', units=['m', 'm', 'm']) # propagate the orbit! setting sec=0 will give you just the position at that time dsx.propagatefromTLE(sec=burst_len - 1, orbit_dir='future', crs='SM', carsph='car', units=['m', 'm', 'm']) for i in range(0, burst_len, 1): ray_datenum = ray_datenum1 + dt.timedelta(seconds=i) #print(ray_datenum) # trace fieldline to exactly VPM's altitude vpm_GEO = convert2([vpm.pos[i]], [ray_datenum], 'SM', 'car', ['m', 'm', 'm'], 'GEO', 'sph', ['m', 'deg', 'deg']) vpm_GEO_alt = vpm_GEO[0][0] / 1000 # alt in km vpm_alt = vpm_GEO_alt - (R_E / 1000) # get fieldline at this point T = getBline(dsx.pos[i], ray_datenum, vpm_alt) # repack T_repackx = T.x T_repacky = T.y T_repackz = T.z T_repack = [[tx, ty, tz] for tx, ty, tz in zip(T_repackx, T_repacky, T_repackz)] T_footpoint = T_repack[dir] T_convert = convert2([T_footpoint], [ray_datenum], 'SM', 'car', ['Re', 'Re', 'Re'], 'GEO', 'sph',
n_mags = [] thetas_save = [] for r in raylist: ray_count += 1 tmp_coords = list(zip(r['pos'].x, r['pos'].y, r['pos'].z)) # get nmag -- INITIAL AND all nmagi = np.sqrt(r['n'].x.iloc[0]**2 +r['n'].y.iloc[0]**2+r['n'].z.iloc[0]**2) nmag = list(np.sqrt(r['n'].x**2 +r['n'].y**2+r['n'].z**2)) n_magsi.append(nmagi) n_mags.append(nmag) tvec_datetime = [ray_datenum + dt.timedelta(seconds=s) for s in r['time']] new_coords = convert2(tmp_coords, tvec_datetime, 'SM', 'car', ['m','m','m'], 'GEO', 'sph', ['m','deg','deg']) # save it ray_coords.append(new_coords) w = r['w'] B = r['B0'].iloc[0] Bmag = np.linalg.norm(B) bunit = B/Bmag # get the initial wna (already confirmed this is equiv to the initial) kveci = [(w/C)*r['n'].x.iloc[0],(w/C)*r['n'].y.iloc[0],(w/C)*r['n'].z.iloc[0]] kunit = np.array(kveci)/np.linalg.norm(kveci) alpha = np.arccos(np.dot(kunit, bunit)) alphaedg = float(alpha)*R2D
for r in raylist: w = r['w'] tmp_coords = list(zip(r['pos'].x, r['pos'].y, r['pos'].z)) # get nmag -- INITIAL AND all nmagi = np.sqrt(r['n'].x.iloc[0]**2 + r['n'].y.iloc[0]**2 + r['n'].z.iloc[0]**2) nmag = list(np.sqrt(r['n'].x**2 + r['n'].y**2 + r['n'].z**2)) #if r['stopcond'] == 1: n_magsf.append(nmag[-1]) n_magsi.append(nmagi) n_mags.append(nmag) tvec_datetime = [ray_datenum + dt.timedelta(seconds=s) for s in r['time']] new_coords = convert2(tmp_coords, tvec_datetime, 'SM', 'car', ['m', 'm', 'm'], 'GEO', 'sph', ['m', 'deg', 'deg']) # save it ray_coords.append(new_coords) B = r['B0'].iloc[0] Bmag = np.linalg.norm(B) bunit = B / Bmag # get the initial wna (already confirmed this is equiv to the initial) kveci = [(w / C) * r['n'].x.iloc[0], (w / C) * r['n'].y.iloc[0], (w / C) * r['n'].z.iloc[0]] kunit = np.array(kveci) / np.linalg.norm(kveci) alpha = np.arccos(np.dot(kunit, bunit)) alphaedg = float(alpha) * R2D thetas_save.append(alphaedg)
# save positions here positions = [] pos_order = [] dirs = [] count = 0 makedate = ray_datenum.strftime('%Y%m%d') Date = int(makedate) ut = ray_datenum.hour + ray_datenum.minute/60 + ray_datenum.second/3600 for la, lo in zip(lats,lons): tx_loc = [R_E+500e3, la, lo] # set location # convert to SM car for field line tracer tx_loc = convert2([tx_loc], [ray_datenum], 'GEO','sph',['m','deg','deg'], 'SM', 'car', ['m','m','m']) positions.append(tx_loc[0]) # save lat long pair pos_order.append((la,lo)) count+=1 # get Bfield direction Bx,By,Bz = gp.ModelField(tx_loc[0][0]/R_E,tx_loc[0][1]/R_E,tx_loc[0][2]/R_E,Date,ut,Model='T96',CoordIn='SM',CoordOut='SM') Bdir = np.array([Bx, By, Bz]) Bunit = Bdir/np.linalg.norm(Bdir) Bsouth = [-1*float(Bunit[0]), -1*float(Bunit[1]), -1*float(Bunit[2])] dirs.append(Bsouth) # for a quick run #dirs.append([0,0,0])
plt.scatter(lons, lats, s=3) plt.show() plt.close() rayfile_directory = '/home/rileyannereid/workspace/SR_output' # store output here ray_datenum = dt.datetime(2020, 6, 1, 12, 0, tzinfo=dt.timezone.utc) md = 1 positions = [] dirs = [] count = 0 for la, lo in zip(lats, lons): tx_loc = [R_E + 500e3, la, lo] # set location count += 1 # convert to SM for field line tracer tx_loc = convert2([tx_loc], [ray_datenum], 'GEO', 'sph', ['m', 'deg', 'deg'], 'SM', 'car', ['m', 'm', 'm']) positions.append(tx_loc[0]) positions_list = [ positions[int(i * (nrays / nworkers)):int((i + 1) * nrays / nworkers)] for i in range(nworkers) ] freq = 19.88e3 # Hz # same freq and starting position for all freqs_list = [[freq for p in range(len(d))] for d in positions_list] directions_list = [[np.zeros(3) for p in range(len(d))] for d in positions_list] tvec = [ray_datenum for n in range(nworkers)]