def angle2Geo(loc_coord, ref_loc): point = Position(0, 0, 0) point.x, point.y, point.z = loc_coord[0], loc_coord[1], loc_coord[2] point.pos_geo(ref_loc) return point
def makePropLine(ref_pos, D, alpha=255): lats = [] lons = [] for line in D: temp = Position(0, 0, 0) temp.x = line[0] temp.y = line[1] temp.z = line[2] temp.pos_geo(ref_pos) if not np.isnan(temp.lat) and not np.isnan(temp.lon): lats.append(temp.lat) lons.append(temp.lon) lats.sort() lons.sort() return lats, lons
def makePropLine(self, D, alpha=255): ref_pos = Position(self.bam.setup.lat_centre, self.bam.setup.lon_centre, 0) lats = [] lons = [] for line in D: temp = Position(0, 0, 0) temp.x = line[0] temp.y = line[1] temp.z = line[2] temp.pos_geo(ref_pos) if not np.isnan(temp.lat) and not np.isnan(temp.lon): lats.append(temp.lat) lons.append(temp.lon) lats.sort() lons.sort() start_pt = pg.PlotCurveItem() start_pt.setData(x=lons, y=lats) start_pt.setPen((255, 85, 0, alpha)) return start_pt
def psoSearch(stns, w, s_name, bam, prefs, ref_pos, manual=False, pert_num=0, override_supra=[], theo=False): """ Optimizes the paths between the detector stations and a supracenter to find the best fit for the position of the supracenter, within the given search area. The supracenter is found with an initial guess, in a given grid, and is moved closer to points of better fit through particle swarm optimization. Produces a graph with the stations and residual results, and prints out the optimal supracenter location """ print('Data converted. Searching...') setup = bam.setup atmos = bam.atmos search_min = Position(setup.lat_min, setup.lon_min, setup.elev_min) search_max = Position(setup.lat_max, setup.lon_max, setup.elev_max) if not manual: try: search_min.pos_loc(ref_pos) search_max.pos_loc(ref_pos) except (AttributeError, TypeError) as e: errorMessage('Search min and search max have not been defined! Aborting search!', 2, info='Please define a search area in the "Sources" tab on the left side of the screen!', detail='{:}'.format(e)) return None output_name = prefs.workdir if not isinstance(override_supra, list): single_point = override_supra else: single_point = setup.manual_fragmentation_search[0] if single_point.toList()[0] is None and manual: errorMessage('Manual Fragmentation Point undefined', 2, info='Unable to parse: Lat: {:} Lon: {:} Elev: {:} Time: {:}'.format(*single_point.toList())) return None ref_time = setup.fireball_datetime if setup.enable_restricted_time: kotc = setup.restricted_time else: kotc = None # check if user defined occurrence time is used if kotc != None: kotc = (kotc - ref_time).total_seconds() # number of stations total n_stations = len(stns) # Station Location xstn = stns[0:n_stations, 0:3] # Initialize arrays # Travel time to each station time3D = np.zeros(n_stations) # Initial azimuths angles of each station az = np.zeros(n_stations) # Initial takeoff angles of each station tf = np.zeros(n_stations) # difference in theoretical and simulated travel times sotc = np.zeros_like(n_stations) # Initialize variables # combined weights nwn = sum(w) if prefs.ballistic_en: try: v = -setup.trajectory.vector.xyz setup.ref_pos = setup.trajectory.pos_f if prefs.debug: print("Constraining Trajectory") except: v = [None] if prefs.debug: print("Free Search") else: v = [None] if prefs.debug: print("Free Search") # If automatic search if not manual: # Prevent search below stations if search_min.elev < max(xstn[:, 2]): # Must be just above the stations search_min.elev = max(xstn[:, 2]) + 0.0001 # Boundaries of search volume # [x, y, z] local coordinates # arguments to be passed to timeFunction() args = (stns, w, kotc, setup, ref_pos, atmos, prefs, v, pert_num, theo) # Particle Swarm Optimization # x_opt - optimal supracenter location # f_opt - optimal supracenter error # if setup.restrict_to_trajectory: # #cons = [lineConstraintx, lineConstrainty, lineConstraintz] # x_opt, f_opt = pso(timeFunction, lb, ub, f_ieqcons=lineConstraint, args=args, swarmsize=int(setup.swarmsize), maxiter=int(setup.maxiter), \ # phip=setup.phip, phig=setup.phig, debug=False, omega=setup.omega, minfunc=setup.minfunc, minstep=setup.minstep) # else: # # Restricted to trajectory # if v[0] != None: # x_opt_temp, f_opt, sup, errors = pso(timeFunction, search_min.xyz, search_max.xyz, \ # ieqcons=[trajConstraints, timeConstraints], args=args,\ # swarmsize=int(prefs.pso_swarm_size), maxiter=int(prefs.pso_max_iter), \ # phip=prefs.pso_phi_p, phig=prefs.pso_phi_g, debug=False, omega=prefs.pso_omega, \ # minfunc=prefs.pso_min_error, minstep=prefs.pso_min_step, processes=1, particle_output=True) x_opt_temp, f_opt, sup, errors = pso(timeFunction, search_min.xyz, search_max.xyz, \ args=args, swarmsize=int(prefs.pso_swarm_size), maxiter=int(prefs.pso_max_iter), \ phip=prefs.pso_phi_p, phig=prefs.pso_phi_g, debug=False, omega=prefs.pso_omega,\ minfunc=prefs.pso_min_error, minstep=prefs.pso_min_step, processes=1, particle_output=True) print('Done Searching') x_opt = Position(0, 0, 0) x_opt.x = x_opt_temp[0] x_opt.y = x_opt_temp[1] x_opt.z = x_opt_temp[2] x_opt.pos_geo(ref_pos) # If manual search else: single_point.position.pos_loc(ref_pos) x_opt = single_point.position sup = single_point.position.xyz errors=0 # Get results for current Supracenter time3D, az, tf, r, motc, sotc, trace = outputWeather(n_stations, x_opt, stns, setup, \ ref_pos, atmos, output_name, s_name, kotc, w, prefs, theo) for ii, element in enumerate(time3D): if np.isnan(element): w[ii] = 0 sotc[ii] = 0 # Find error for manual searches if manual: f_opt = np.dot(w, np.absolute(sotc - np.dot(w, sotc)/nwn))/nwn # x, y distance from Supracenter to each station horz_dist = np.zeros(n_stations) for i in range(n_stations): horz_dist[i] = np.sqrt((x_opt.x - xstn[i, 0])**2 + (x_opt.y - xstn[i, 1])**2)/1000 # Calculate and Set the Occurrence Time into HH:MM:SS time_diff = motc + ref_time.microsecond/1e6 + ref_time.second + ref_time.minute*60 + ref_time.hour*3600 try: otc = (datetime.datetime.min + datetime.timedelta(seconds=time_diff)).time() except (ValueError, OverflowError): print('STATUS: Unable to parse otc') otc = None try: #while max(errors) > setup.max_error/100: a = [] std_error = np.std(errors) lim = np.mean(errors) + 0*std_error for i in range(len(errors)): if errors[i] >= lim: a.append(i) errors = np.delete(errors, (a), axis=0) sup = np.delete(sup, (a), axis=0) except: print("WARNING: Unable to filter errors") class Results: def __init__(self): pass results = Results() results.r = r results.w = w results.x_opt = x_opt results.f_opt = f_opt results.sup = sup results.errors = errors results.horz_dist = horz_dist results.time3D = time3D results.az = az results.tf = tf results.motc = motc results.sotc = sotc results.kotc = kotc results.otc = otc results.trace = trace return results # # scatter plot(s) # min_search, max_search = scatterPlot(single_point, n_stations, xstn, s_name, r, x_opt, \ # reported_points, search, output_name, ref_pos, sup, errors, tweaks, dataset) # # residual plot # residPlot(x_opt, s_name, xstn, r, output_name, n_stations) # # output results # outputText(min_search, max_search, single_point, ref_time, otc, kotc, x_opt, f_opt, n_stations, tweaks, s_name, xstn, \ # r, w, az, tf, time3D, horz_dist, output_name, tstn)
def timeFunction(x, *args): ''' Helper function for PSO Takes in supracenter ranges, and calculates the travel time, which is used to find the error. Returns the residual error with each guess of a supracenter from pso() Arguments: x: [ndarray] position to search with args: list of passed arguments stns: [list] list of station positions and arrival times w: [list] list of station weights kotc: [float] user-defined occurence time tweaks: [Object] user-defined option ref_pos: [list] mean station location used for converting to/from local coordinates dataset: [ndarray] atmospheric profile for the entire search area pool: [multiprocessing pool] pool of workers for multiprocessing Returns: err: [float] error in the current position, x, searched ''' # Retrieve passed arguments stns, w, kotc, setup, ref_pos, atmos, prefs, v, pert_num, theo = args # number of stations total n_stations = len(stns) # Residuals to each station sotc = np.empty(n_stations) # Initialize variables # Weight of each station wn = w # total weight nwn = sum(w) # Mean occurrence time motc = 0 S = Position(0, 0, 0) S.x, S.y, S.z = x[0], x[1], x[2] S.pos_geo(ref_pos) # number of stations total n_stations = len(stns) # Station Times tobs = stns[0:n_stations, 4] # Station Location xstn = stns[0:n_stations, 0:3] # Initialize arrays # Simulated travel times to each station time3D = np.empty(n_stations) # Residuals to each station sotc = np.empty(n_stations) for j in range(n_stations): # if station has weight if w[j] > 0: D = Position(0, 0, 0) D.x, D.y, D.z = xstn[j, 0], xstn[j, 1], xstn[j, 2] D.pos_geo(ref_pos) if not theo: if pert_num == 0: # No perturbations used here sounding, _ = atmos.getSounding(lat=[S.lat, D.lat], lon=[S.lon, D.lon], heights=[S.elev, D.elev], ref_time=setup.fireball_datetime) else: # Sounding is a specfic perturbation number # TODO: Go back and fix how this is done, not all perts need to be generated, just one here nom, sounding = atmos.getSounding(lat=[S.lat, D.lat], lon=[S.lon, D.lon], heights=[S.elev, D.elev], ref_time=setup.fireball_datetime) # sounding is none when there is an error in getting sounding if sounding is not None: sounding = sounding[pert_num - 1] else: sounding = nom # Use distance and atmospheric data to find path time time3D[j], _, _, _ = cyscan(S.xyz, D.xyz, sounding, \ wind=prefs.wind_en, h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist, processes=1) # Residual time for each station else: distance = np.sqrt((S.x - D.x)**2 + (S.y - D.y)**2 + (S.z - D.z)**2) time3D[j] = distance/prefs.avg_sp_sound sotc[j] = tobs[j] - time3D[j] # If station has no weight else: sotc[j] = tobs[j] motc = np.nanmean(sotc) ########## N_s = np.count_nonzero(~np.isnan(sotc)) # User defined occurrence time if kotc != None: # make kOTC a list err = np.dot(wn, np.absolute(sotc - np.array([kotc]*n_stations)))/nwn # Unknown occurrence time else: #err = np.dot(wn, np.absolute(sotc - motc))/nwn N_s = 0 err = 0 for s in sotc: if np.isnan(s): continue else: err += (1 + (s - motc)**2)**0.5 - 1 N_s += 1 if N_s == 0: err = np.inf else: err = err/N_s # if setup.debug: # # print out current search location # print("Supracenter: {:10.2f} m x {:10.2f} m y {:10.2f} m z Time: {:8.2f} Error: {:25.2f}".format(x[0], x[1], x[2], motc, err)) # variable to be minimized by the particle swarm optimization perc_fail = 100 - (n_stations-N_s)/n_stations*100 # temporary adjustment to try and get the most stations if N_s >= 3: total_error = err# + 2*max(error_list)*(failed_stats) else: total_error = np.inf if np.isnan(total_error): total_error = np.inf if prefs.debug: print("Error {:10.4f} | Solution {:10.4f}N {:10.4f}E {:8.2f} km {:8.2f} s | Failed Stats {:3} {:}".format(total_error, S.lat, S.lon, S.elev/1000, motc, n_stations-N_s, printPercent(perc_fail, N_s))) # Quick adjustment to try and better include stations return total_error
def psoTrajectory(station_list, bam, prefs): point_on_traj = None ref_pos = Position(bam.setup.lat_centre, bam.setup.lon_centre, 0) # ref_pos = bam.setup.trajectory.pos_f if bam.setup.pos_min.isNone() or bam.setup.pos_max.isNone(): errorMessage( 'Search boundaries are not defined!', 2, info= 'Please define the minimum and maximum parameters in the "Sources" tab on the left side of the screen!' ) return None bam.setup.pos_min.pos_loc(ref_pos) bam.setup.pos_max.pos_loc(ref_pos) if point_on_traj is None: bounds = [ (bam.setup.pos_min.x, bam.setup.pos_max.x), # X0 (bam.setup.pos_min.y, bam.setup.pos_max.y), # Y0 (bam.setup.t_min, bam.setup.t_max), # t0 (bam.setup.v_min, bam.setup.v_max), # Velocity (m/s) (bam.setup.azimuth_min.deg, bam.setup.azimuth_max.deg), # Azimuth (bam.setup.zenith_min.deg, bam.setup.zenith_max.deg ) # Zenith angle ] else: bounds = [ (bam.setup.v_min, bam.setup.v_max), # Velocity (m/s) (bam.setup.azimuth_min.deg, bam.setup.azimuth_max.deg), # Azimuth (bam.setup.zenith_min.deg, bam.setup.zenith_max.deg ) # Zenith angle ] lower_bounds = [bound[0] for bound in bounds] upper_bounds = [bound[1] for bound in bounds] if prefs.debug: print('Free Search') import matplotlib.pyplot as plt plt.ion() fig, ax = plt.subplots(2, 3, sharey='row') ax[0, 0].set_ylabel("Total Error") ax[1, 0].set_ylabel("Total Error") ax[0, 0].set_xlabel("Latitude") ax[0, 1].set_xlabel("Longitude") ax[0, 2].set_xlabel("Time") ax[1, 0].set_xlabel("Velocity") ax[1, 1].set_xlabel("Azimuth") ax[1, 2].set_xlabel("Zenith") plot = [] for i in range(2): for j in range(3): plot.append(ax[i, j].scatter([], [])) x, fopt = pso(trajSearch, lower_bounds, upper_bounds, args=(station_list, ref_pos, bam, prefs, plot, ax, fig, point_on_traj), \ maxiter=prefs.pso_max_iter, swarmsize=prefs.pso_swarm_size, \ phip=prefs.pso_phi_p, phig=prefs.pso_phi_g, debug=False, omega=prefs.pso_omega, \ particle_output=False) # if point_on_traj is None: print('Results:') print('X: {:.4f}'.format(x[0])) print('Y: {:.4f}'.format(x[1])) print('Time: {:.4f}'.format(x[2])) print('Velocity: {:.4f}'.format(x[3])) print('Azimuth: {:.4f}'.format(x[4])) print('Zenith: {:.4f}'.format(x[5])) print('Adjusted Error: {:.4f}'.format(fopt)) # else: # print('Results:') # print('Velocity: {:.4f}'.format(x[0])) # print('Azimuth: {:.4f}'.format(x[1])) # print('Zenith: {:.4f}'.format(x[2])) # print('Adjusted Error: {:.4f}'.format(fopt)) # if point_on_traj is None: geo = Position(0, 0, 0) geo.x = x[0] geo.y = x[1] geo.z = 0 geo.pos_geo(ref_pos) print('Geometric Landing Point:') print(geo) stat_names = [] stat_pick = [] final_traj = Trajectory(x[2], x[3], zenith=Angle(x[5]), azimuth=Angle(x[4]), pos_f=geo) points = final_traj.findPoints(gridspace=100, min_p=17000, max_p=50000) for stn in station_list: stat_names.append("{:}-{:}".format(stn[1], stn[2])) t_nom, t_pert = timeOfArrival(np.array([stn[3], stn[4], stn[5]]), final_traj, bam, prefs, points, ref_loc=ref_pos) stat_pick.append(t_nom - stn[6]) return [x, fopt, geo, stat_names, stat_pick]
def trajSearch(params, station_list, ref_pos, bam, prefs, plot, ax, fig, point_on_traj): if point_on_traj is None: x0, y0, t0, v, azim, zangle = params pos_f = Position(0, 0, 0) pos_f.x = x0 pos_f.y = y0 pos_f.z = 0 pos_f.pos_geo(ref_pos) temp_traj = Trajectory(t0, v, zenith=Angle(zangle), azimuth=Angle(azim), pos_f=pos_f) else: v, azim, zangle = params temp_traj = Trajectory(point_on_traj.time, v, zenith=Angle(zangle), azimuth=Angle(azim), pos_i=point_on_traj.position) pos_f = temp_traj.pos_f temp_traj = Trajectory(point_on_traj.time, v, zenith=Angle(zangle), azimuth=Angle(azim), pos_f=pos_f) #points = temp_traj.findPoints(gridspace=1000, min_p=pos_f.elev, max_p=100000) points = temp_traj.trajInterp2(div=50, min_p=17000, max_p=60000, xyz=False) if prefs.debug: dif = points[1] - points[0] dis = (dif[0]**2 + dif[1]**2 + dif[2]**2)**0.5 tim = dis / 330 u = temp_traj.vector.xyz cost_value = 0 failed_stats = 0 error_list = [] N = len(station_list) for stn in station_list: t_theo, t_pert = timeOfArrival(np.array([stn[3], stn[4], stn[5]]), temp_traj, bam, prefs, points, ref_loc=ref_pos) t_obs = stn[6] if not np.isnan(t_theo): cost_value = ((1 + (t_theo - t_obs)**2)**0.5 - 1) error_list.append(cost_value) else: failed_stats += 1 # if np.isnan(t_theo): # if prefs.debug: # print(np.inf) # return np.inf perc_fail = 100 - failed_stats / len(station_list) * 100 # temporary adjustment to try and get the most stations if N - failed_stats >= 3: total_error = sum(error_list) / ( N - failed_stats) # + 2*max(error_list)*(failed_stats) else: total_error = np.inf if prefs.debug: print( "Error {:10.4f} | Failed Stats {:3} {:} | Error between points: {:.2f} km ({:.2f} s)" .format(total_error, failed_stats, printPercent(perc_fail, N - failed_stats), dis / 1000, tim)) # Quick adjustment to try and better include stations for i in range(6): array = np.array(plot[i].get_offsets()) if not np.isinf(total_error): if i == 0: point = np.array([pos_f.lat, total_error]) elif i == 1: point = np.array([pos_f.lon, total_error]) elif i == 2: if point_on_traj is not None: t0 = 0 point = np.array([t0, total_error]) elif i == 3: point = np.array([v, total_error]) elif i == 4: point = np.array([azim, total_error]) elif i == 5: point = np.array([zangle, total_error]) # add the points to the plot array = np.append(array, [point], axis=0) plot[i].set_offsets(array) # # update x and ylim to show all points: ax[i // 3, i % 3].set_xlim(array[:, 0].min() - 0.01, array[:, 0].max() + 0.01) ax[i // 3, i % 3].set_ylim(array[:, 1].min() - 0.01, array[:, 1].max() + 0.01) # update the figure fig.canvas.draw() plt.pause(0.05) return total_error
def waveReleasePointWindsContour(bam, traj, ref_loc, points, div=37, mode='ballistic'): setup = bam.setup atmos = bam.atmos steps = 90 alpha = np.linspace(0, 360 * ((steps - 1) / steps), steps) alpha = np.radians(alpha) beta = np.linspace(0, 90 * ((steps - 1) / steps), steps) beta = np.radians(beta) theta = setup.trajectory.azimuth.rad phi = setup.trajectory.zenith.rad tol = 25 #deg # tol_fact = np.radians(np.linspace(-tol, tol, 10)) WIND = True u = traj.getTrajVect() v_list = [] for i in range(steps): for j in range(steps): v = np.array([np.sin(alpha[i])*np.sin(beta[j]),\ np.cos(alpha[i])*np.sin(beta[j]),\ -np.cos(beta[j])]) if np.abs(90 - np.degrees(np.arccos(np.dot(u, v)))) <= tol: v_list.append([alpha[i], beta[j]]) v_list = np.array(v_list) results = [] # temp hotfix if mode == 'ballistic_old': grid_space = 25 p1 = Position(43.8, 13.1, 0) p2 = Position(47.8, 17.1, 0) p1.pos_loc(ref_loc) p2.pos_loc(ref_loc) xs = np.linspace(p1.x, p2.x, grid_space) ys = np.linspace(p1.y, p2.y, grid_space) n_steps = grid_space**2 * len(points) for xnum, xx in enumerate(xs): for ynum, yy in enumerate(ys): angle_list = [] time_list = [] D = [xx, yy, 0] for pnum, pp in enumerate(points): step = pnum + ynum * len(points) + xnum * grid_space * len( points) loadingBar("Contour Calculation", step, n_steps) P = Position(pp[0], pp[1], pp[2]) P.pos_loc(ref_loc) S = P.xyz R = Position(0, 0, 0) R.x = D[0] R.y = D[1] R.z = D[2] R.pos_geo(ref_loc) lats = [P.lat, R.lat] lons = [P.lon, R.lon] elev = [P.elev, R.elev] z_profile, _ = atmos.getSounding( lats, lons, elev, ref_time=setup.fireball_datetime, spline=100) res = cyscan(np.array(S), np.array(D), z_profile, wind=True, h_tol=330, v_tol=3000, debug=False) alpha = np.radians(res[1]) beta = np.radians(180 - res[2]) res_vector = np.array([np.sin(alpha)*np.sin(beta),\ np.cos(alpha)*np.sin(beta),\ -np.cos(beta)]) angle_list.append( np.abs(90 - np.degrees( np.arccos( np.dot( u / np.sqrt(u.dot(u)), res_vector / np.sqrt(res_vector.dot(res_vector))))))) time_list.append(res[0]) if np.nanmin(angle_list) <= tol: best_angle_index = np.nanargmin(angle_list) best_time = time_list[best_angle_index] if not np.isnan(best_time): res = [xx, yy, 0, best_time] results.append(res) # u = np.array([bam.setup.trajectory.vector.x, # bam.setup.trajectory.vector.y, # bam.setup.trajectory.vector.z]) # angle_off = [] # X = [] # for i in range(len(bam.setup.fragmentation_point)): # az = stn.times.fragmentation[i][0][1] # tf = stn.times.fragmentation[i][0][2] # az = np.radians(az) # tf = np.radians(180 - tf) # v = np.array([np.sin(az)*np.sin(tf), np.cos(az)*np.sin(tf), -np.cos(tf)]) # angle_off.append(np.degrees(np.arccos(np.dot(u/np.sqrt(u.dot(u)), v/np.sqrt(v.dot(v)))))) # X.append(bam.setup.fragmentation_point[i].position.elev) # angle_off = np.array(angle_off) # try: # best_indx = np.nanargmin(abs(angle_off - 90)) # except ValueError: # best_indx = None # a.append(np.array([np.nan, np.nan, np.nan, np.nan])) # for pert in perturbations: # a.append(np.array([np.nan, np.nan, np.nan, np.nan])) # stn.times.ballistic.append(a) # continue # np.array([t_arrival, azimuth, takeoff, E[k, l]]) elif mode == 'ballistic': n_steps = len(v_list) * len(points) WIND = True for pp, p in enumerate(points): for vv, v in enumerate(v_list): step = vv + pp * len(v_list) loadingBar("Contour Calculation", step, n_steps) # v[i] = np.array([np.sin(alpha[i])*np.sin(beta[i]),\ # np.cos(alpha[i])*np.sin(beta[i]),\ # -np.cos(beta[i])]) P = Position(p[0], p[1], p[2]) S = Position(p[0], p[1], 0) P.pos_loc(ref_loc) pt = P.xyz # s = p + p[2]/np.cos(beta[i])*v[i] # S = Position(0, 0, 0) # P = Position(0, 0, 0) # S.x, S.y, S.z = s[0], s[1], s[2] # P.x, P.y, P.z = p[0], p[1], p[2] # S.pos_geo(ref_loc) # P.pos_geo(ref_loc) lats = [P.lat, S.lat] lons = [P.lon, S.lon] elev = [P.elev, S.elev] # z_profile, _ = supra.Supracenter.cyweatherInterp.getWeather(p, s, setup.weather_type, \ # ref_loc, copy.copy(sounding), convert=True) if WIND: z_profile, _ = atmos.getSounding( lats, lons, elev, ref_time=setup.fireball_datetime, spline=200) res = anglescan(pt, np.degrees(v[0]), np.degrees(v[1]) + 90, z_profile, wind=True, debug=False) else: # if no wind, just draw a straight line vect = np.array([np.sin(v[0])*np.sin(v[1]),\ np.cos(v[0])*np.sin(v[1]),\ -np.cos(v[1])]) s = -pt[2] / vect[2] ground_point = pt + s * vect ground_time = s / 330 res = [ ground_point[0], ground_point[1], ground_point[2], ground_time ] # This is the limit in distance from the trajectory (hardcoded) if res[-1] <= 1000: # if np.sqrt(res[0]**2 + res[1]**2) <= 150000: results.append(res) else: # n_steps = len(tol_fact)*len(points)*steps beta = np.linspace(90 + 0.01, 180, steps) beta = np.radians(beta) p = points for ii, i in enumerate(range(steps)): for jj, j in enumerate(range(steps)): # print(np.degrees(beta[i])) step = jj + ii * steps loadingBar("Contour Calculation", step, n_steps) v[i*steps + j] = np.array([np.sin(alpha[i])*np.sin(beta[j]),\ np.cos(alpha[i])*np.sin(beta[j]),\ -np.cos(beta[j])]) s = p + p[2] / np.cos(beta[j]) * v[i * steps + j] S = Position(0, 0, 0) P = Position(0, 0, 0) S.x, S.y, S.z = s[0], s[1], s[2] P.x, P.y, P.z = p[0], p[1], p[2] S.pos_geo(ref_loc) P.pos_geo(ref_loc) lats = [P.lat, S.lat] lons = [P.lon, S.lon] elev = [P.elev, S.elev] z_profile, _ = atmos.getSounding( lats, lons, elev, ref_time=setup.fireball_datetime, spline=100) res = anglescan(p, np.degrees(alpha[i]), np.degrees(beta[j]), z_profile, wind=True, debug=False) # if np.sqrt(res[0]**2 + res[1]**2) <= 200000: results.append(res) return results
def outputWeather(n_stations, x_opt, stns, setup, ref_pos, atmos, output_name, s_name, kotc, w, prefs, theo): """ Function to calculate the results of the optimal Supracenter and export interpolated weather data from each station. Arguments: n_stations: [int] number of stations x_opt: [list] lat, lon, and height of the optimal supracenter xstn: [list] lat, lon, and height of all the stations setup: [Object] object storing user-defined setup consts: [Object] object storing physical constants ref_pos: [list] mean position of the stations to act as the origin for the local coordinate system dataset: [ndarray] atmospheric profile of search area output_name: [string] folder name to save output files in s_name: [list] name of all the stations kotc: [list] user-defined occurence time [HH, MM, SS] tobs: [list] station arrival times to each station w: [list] weights of each station Returns: time3D: [list] calculated travel time to each station az: [list] initial azimuth angle to each station tf: [list] initial takeoff angle to each station r: [list] residuals to each station """ consts = Constants() # Station Times tobs = stns[0:n_stations, 4] # Station Location xstn = stns[0:n_stations, 0:3] # Travel time to each station time3D = np.zeros(n_stations) # Initial azimuths angles of each station az = np.zeros(n_stations) # Initial takeoff angles of each station tf = np.zeros(n_stations) # difference in theoretical and simulated travel times sotc = np.zeros_like(tobs) #difference in theoretical and simulated travel times r = np.zeros_like(tobs) trace = [] # Find parameters of optimal location print('Exporting weather profiles...') for j in range(n_stations): #print(np.array(x_opt), np.array(xstn[j, :])) # get interpolated weather profile D = Position(0, 0, 0) D.x, D.y, D.z = xstn[j, 0], xstn[j, 1], xstn[j, 2] D.pos_geo(ref_pos) if not theo: sounding, _ = atmos.getSounding(lat=[x_opt.lat, D.lat], lon=[x_opt.lon, D.lon], heights=[x_opt.elev, D.elev], ref_time=setup.fireball_datetime) # Rotate winds to match with coordinate system #sounding[:, 3] = np.radians(angle2NDE(np.degrees(sounding[:, 3]))) # Export the interpolated weather profiles from the optimal Supracenter for each station # with open(os.path.join(output_name, s_name[j] + '_sounding.txt'), 'w') as f: # # With winds # if prefs.wind_en == True: # if prefs.atm_type == 'custom' or prefs.atm_type == 'none' or prefs.atm_type == 'radio': # f.write('| Height (m) | Temp (K) | soundSpd (m/s) | wdSpd (m/s) | wdDir (deg fN) |\n') # else: # f.write('| Latitude (deg N) | Longitude (deg E) | Height (m) | Temp (K) | soundSpd (m/s) | wdSpd (m/s) | wdDir (deg fN) |\n') # # No winds # else: # if prefs.atm_type == 'custom' or prefs.atm_type == 'none' or prefs.atm_type== 'radio': # f.write('| Height (m) | Temp (K) | soundSpd (m/s) |\n') # else: # f.write('| Latitude (deg N) | Longitude (deg E) | Height (m) | Temp (K) | soundSpd (m/s) |\n') # for ii in range(len(sounding)): # if prefs.wind_en == True: # if prefs.atm_type == 'custom' or prefs.atm_type == 'none' or prefs.atm_type == 'radio': # f.write('| {:8.2f} | {:7.3f} | {:6.4f} | {:7.3f} | {:6.2f} |\n'\ # .format(sounding[ii, 0], sounding[ii, 1]**2*consts.M_0/consts.GAMMA/consts.R, \ # sounding[ii, 1], sounding[ii, 2], sounding[ii, 3])) # else: # f.write('| {:7.4f} | {:7.4f} | {:8.2f} | {:7.3f} | {:6.4f} | {:7.3f} | {:6.2f} |\n'\ # .format(points[ii][0], points[ii][1], sounding[ii, 0], sounding[ii, 1]**2*consts.M_0/consts.GAMMA/consts.R, \ # sounding[ii, 1], sounding[ii, 2], sounding[ii, 3])) # else: # if prefs.atm_type == 'custom' or prefs.atm_type == 'none' or prefs.atm_type == 'radio': # f.write('| {:8.2f} | {:7.3f} | {:6.4f} |\n'\ # .format(sounding[ii, 0], sounding[ii, 1]**2*consts.M_0/consts.GAMMA/consts.R, sounding[ii, 1])) # else: # f.write('| {:7.4f} | {:7.4f} | {:8.2f} | {:7.3f} | {:6.4f} |\n'\ # .format(points[ii][0], points[ii][1], sounding[ii, 0], sounding[ii, 1]**2*consts.M_0/consts.GAMMA/consts.R, sounding[ii, 1])) # ray tracing function # _, _, _, _, temp_trace = slowscan(x_opt.xyz, np.array(xstn[j, :]), sounding, \ # wind=prefs.wind_en, n_theta=prefs.pso_theta, n_phi=prefs.pso_phi,\ # h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist) time3D[j], _, _, _ = cyscan(x_opt.xyz, np.array(xstn[j, :]), sounding, \ wind=prefs.wind_en, h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist, processes=1) else: time3D[j] = x_opt.pos_distance(D) / prefs.avg_sp_sound # trace.append(temp_trace) # find residuals sotc[j] = tobs[j] - time3D[j] # for ii, element in enumerate(time3D): # if np.isnan(element): # w[ii] = 0 # User defined occurrence time if kotc != None: motc = kotc index = [] # elif setup.manual_fragmentation_search != '' and len(setup.manual_fragmentation_search) > 0: # motc = setup.manual_fragmentation_search[3] # Unknown occurrence time else: w = np.array([1] * len(sotc)) index = np.isnan(sotc) sotc[index] = 0 motc = np.dot(w, sotc) / sum(w) # Station residuals (from average) r = sotc - motc r[index] = np.nan return time3D, az, tf, r, motc, sotc, trace
def propegateBackwards(ref_pos, stn, bam, offset=0): S = stn.metadata.position S.pos_loc(ref_pos) # Initial guess for sounding sounding, _ = bam.atmos.getSounding(lat=[S.lat, S.lat], lon=[S.lon, S.lon], heights=[S.elev, 50000]) D = [] offset = 0 for pol in range(len(stn.polarization.azimuth)): min_az = stn.polarization.azimuth[ pol] - SIGMA * stn.polarization.azimuth_error[pol] max_az = stn.polarization.azimuth[ pol] + SIGMA * stn.polarization.azimuth_error[pol] for azimuth in np.linspace(min_az, max_az, 5): for zenith in np.linspace(1, 89, 100): # T - expected final arrival, with bad sounding # Recalculate winds # D - real, expected final arrival # This is overkill, atmosphere won't change that much T_pos = anglescanrev(S.xyz, (azimuth + offset) % 360, zenith, sounding, wind=True) T = Position(0, 0, 0) T.x = T_pos[0] T.y = T_pos[1] T.z = T_pos[2] T.pos_geo(ref_pos) try: sounding_plus, perts = bam.atmos.getSounding( lat=[S.lat, T.lat], lon=[S.lon, T.lon], heights=[S.elev, 50000]) except ValueError: sounding_plus, perts = bam.atmos.getSounding( lat=[S.lat, S.lat], lon=[S.lon, S.lon], heights=[S.elev, 50000]) nom_data = [None] * len(perts + 1) nom_data[0] = anglescanrev(S.xyz, (azimuth + offset) % 360, zenith, sounding_plus, wind=True, trace=True) for pp, pert in enumerate(perts): nom_data[pp] = anglescanrev(S.xyz, (azimuth + offset) % 360, zenith, pert, wind=True, trace=True) # Repeat 180 deg away T_pos = anglescanrev(S.xyz, (azimuth + offset + 180) % 360, zenith, sounding, wind=True) T = Position(0, 0, 0) T.x = T_pos[0] T.y = T_pos[1] T.z = T_pos[2] T.pos_geo(ref_pos) try: sounding_plus, perts = bam.atmos.getSounding( lat=[S.lat, T.lat], lon=[S.lon, T.lon], heights=[S.elev, 50000]) except ValueError: sounding_plus, perts = bam.atmos.getSounding( lat=[S.lat, S.lat], lon=[S.lon, S.lon], heights=[S.elev, 50000]) nom_data_rev = [None] * len(perts + 1) nom_data_rev[0] = anglescanrev(S.xyz, (azimuth + offset + 180) % 360, zenith, sounding_plus, wind=True, trace=True) for pp, pert in enumerate(perts): nom_data_rev[pp] = anglescanrev( S.xyz, (azimuth + offset + 180) % 360, zenith, pert, wind=True, trace=True) for ii in range(len(nom_data)): for line in nom_data[ii]: line[3] -= stn.polarization.time[pol] D.append(line) for line in nom_data_rev[ii]: line[3] -= stn.polarization.time[pol] D.append(line) return D
def supSearch(bam, prefs, manual=True, results_print=False, obj=None, misfits=False, theo=False): """ Function to initiate PSO Search of a Supracenter """ if theo: theoSearch(bam, prefs) return None # Reference location to convert to local coordinates ref_pos = Position(bam.setup.lat_centre, bam.setup.lon_centre, 0) # Pull station information from picks file s_info, s_name, weights = getStationData(bam.setup.station_picks_file, ref_pos) n_stations = len(s_name) xstn = s_info[0:n_stations, 0:3] # Nominal Run if prefs.debug: print("Current status: Nominal Supracenter") results = psoSearch(s_info, weights, s_name, bam, prefs, ref_pos, manual=manual, pert_num=0) # Check for if results returns None try: # Error function is the absolute L1 norm ?? print("Nominal Results") print("Error Function: {:5.2f}".format(results.f_opt)) print("Opt: {:.4f} {:.4f} {:.2f} {:.4f}"\ .format(results.x_opt.lat, results.x_opt.lon, results.x_opt.elev, results.motc)) except AttributeError as e: errorMessage('Unable to find Supracenter Solution', 2, detail='{:}'.format(e)) return None # Perturbation Runs if prefs.pert_en: pert_results = [None] * prefs.pert_num for i in range(prefs.pert_num): if prefs.debug: print("Current status: Perturbation {:}".format(i + 1)) pert_results[i] = psoSearch(s_info, weights, s_name, bam, prefs, ref_pos, manual=manual, pert_num=i + 1) # Results show an L2 norm, normalized by the number of stations that have arrivals print('Residuals:') for i in range(len(s_name)): print('{:}: {:.4f} s'.format(s_name[i], results.r[i])) norm_res = 0 stat = 0 for res in results.r: if not np.isnan(res): stat += 1 norm_res += res**2 if stat != 0: print('Residual Norm: {:.4f} s'.format(np.sqrt(norm_res) / stat)) else: print('Residual Norm: Undefined') pert_results = [] reses = [np.sqrt(norm_res) / stat] if prefs.pert_en: for i in range(prefs.pert_num): # Error function is the absolute L1 norm ?? print("Perturbation {:} Results".format(i + 1)) print("Error Function: {:5.2f}".format(pert_results[i].f_opt)) print("Opt: {:.4f} {:.4f} {:.2f} {:.4f}"\ .format(pert_results[i].x_opt.lat, pert_results[i].x_opt.lon, pert_results[i].x_opt.elev, pert_results[i].motc)) # Results show an L2 norm, normalized by the number of stations that have arrivals print('Residuals:') for ii in range(len(s_name)): print('{:}: {:.4f} s'.format(s_name[ii], pert_results[i].r[ii])) norm_res = 0 stat = 0 for res in pert_results[i].r: if not np.isnan(res): stat += 1 norm_res += res**2 if stat != 0: print('Residual Norm: {:.4f} s'.format( np.sqrt(norm_res) / stat)) else: print('Residual Norm: Undefined') reses.append(norm_res / stat) if misfits: genMisfits(bam, prefs, results, s_info, weights, s_name, ref_pos) if results_print: return [results, pert_results, reses, s_name] else: defTable(obj.supra_res_table, n_stations + 1, 5, headers=[ 'Station Name', "Latitude", "Longitude", "Elevation", "Residuals" ]) if stat != 0: resids = norm_res / stat else: resids = np.nan setTableRow(obj.supra_res_table, 0, terms=[ "Total (Time = ({:} s))".format(results.motc), results.x_opt.lat, results.x_opt.lon, results.x_opt.elev, resids ]) for i in range(n_stations): stn_pos = Position(0, 0, 0) stn_pos.x, stn_pos.y, stn_pos.z = xstn[i][0], xstn[i][1], xstn[i][ 2] stn_pos.pos_geo(ref_pos) setTableRow(obj.supra_res_table, i + 1, terms=[ s_name[i], stn_pos.lat, stn_pos.lon, stn_pos.elev, results.r[i] ]) clearLayout(obj.plots) supScatterPlot(bam, prefs, results, xstn, s_name, obj, manual=manual, pert_results=pert_results) residPlot(bam, prefs, results, pert_results, s_name, xstn, prefs.workdir, obj, manual=manual) return None