def go(argdict : dict): searchdirs = str.split(os.getenv("F1_TRACK_DIRS"), os.pathsep) trackname : str = argdict["trackname"] racelinefile : str = deepracing.searchForFile(trackname+"_baseline.json", searchdirs) with open(racelinefile, "r") as f: racelinedict : dict = json.load(f) innerboundaryfile : str = deepracing.searchForFile(trackname+"_innerlimit.json", searchdirs) with open(innerboundaryfile, "r") as f: innerboundarydict : dict = json.load(f) outerboundaryfile : str = deepracing.searchForFile(trackname+"_outerlimit.json", searchdirs) with open(outerboundaryfile, "r") as f: outerboundarydict : dict = json.load(f) racelinet : np.ndarray = np.asarray(racelinedict["t"]) racelinex : np.ndarray = np.asarray(racelinedict["x"]) raceliney : np.ndarray = np.asarray(racelinedict["y"]) racelinez : np.ndarray = np.asarray(racelinedict["z"]) raceline : np.ndarray = np.column_stack([racelinex, raceliney, racelinez]) innerboundaryx : np.ndarray = np.asarray(innerboundarydict["x"]) innerboundaryy : np.ndarray = np.asarray(innerboundarydict["y"]) innerboundaryz : np.ndarray = np.asarray(innerboundarydict["z"]) innerboundaryaug : np.ndarray = np.stack([innerboundaryx, innerboundaryy, innerboundaryz, np.ones_like(innerboundaryz)], axis=0) outerboundaryx : np.ndarray = np.asarray(outerboundarydict["x"]) outerboundaryy : np.ndarray = np.asarray(outerboundarydict["y"]) outerboundaryz : np.ndarray = np.asarray(outerboundarydict["z"]) outerboundaryaug : np.ndarray = np.stack([outerboundaryx, outerboundaryy, outerboundaryz, np.ones_like(outerboundaryz)], axis=0) allpoints : np.ndarray = np.concatenate([raceline, innerboundaryaug[0:3].T, outerboundaryaug[0:3].T], axis=0) minz : float = np.min(allpoints[:,2])-10.0 maxz : float = np.max(allpoints[:,2])+10.0 meany : float = np.mean(allpoints[:,1]) raceline[:,1]=meany innerboundaryaug[1]=meany outerboundaryaug[1]=meany racelinespline : scipy.interpolate.BSpline = scipy.interpolate.make_interp_spline(racelinet, raceline, k=3) racelinesplineder : scipy.interpolate.BSpline = racelinespline.derivative() racelinespline2ndder : scipy.interpolate.BSpline = racelinesplineder.derivative() # tsamp : np.ndarray = racelinet tsamp : np.ndarray = np.linspace(racelinet[0], racelinet[-1]+0.0375, num=10000) raceline : np.ndarray = racelinespline(tsamp) fig2 : matplotlib.figure.Figure = plt.figure() plt.plot(raceline[0,0], raceline[0,2], "g*") plt.plot(raceline[:,0], raceline[:,2], color="blue") plt.plot(innerboundaryaug[0], innerboundaryaug[2], color="black", alpha=0.5) plt.plot(outerboundaryaug[0], outerboundaryaug[2], color="black", alpha=0.5) plt.ylim(maxz+10.0, minz-10.0) plt.show() racelinevelvecs : np.ndarray = racelinesplineder(tsamp) racelinevelvecs[:,1]=0.0 racelinetangentvecs : np.ndarray = racelinevelvecs/(np.linalg.norm(racelinevelvecs, ord=2, axis=1)[:,np.newaxis]) ref : np.ndarray = np.zeros_like(racelinetangentvecs) ref[:,1]=1.0 racelinelateralvecs : np.ndarray = np.cross(ref, racelinetangentvecs) racelineposes : np.ndarray = np.zeros((racelinevelvecs.shape[0], 4, 4), dtype=racelinevelvecs.dtype) racelineposes[:,-1,-1] = 1.0 racelineposes[:,0:3,0]=racelinetangentvecs racelineposes[:,0:3,1]=racelinelateralvecs racelineposes[:,0:3,2]=ref racelineposes[:,0:3,3]=raceline iboffsets : np.ndarray = np.zeros_like(racelineposes[:,0,0]) oboffsets : np.ndarray = np.zeros_like(racelineposes[:,0,0]) ib_kdtree : KDTree = KDTree(innerboundaryaug[0:3].T) ob_kdtree : KDTree = KDTree(outerboundaryaug[0:3].T) for i in tqdm(range(racelineposes.shape[0])): try: currentrlpose : np.ndarray = racelineposes[i] tmat : np.ndarray = np.linalg.inv(currentrlpose) closest_ib_index : int = ib_kdtree.query(currentrlpose[0:3,3])[1] ib_idx_samp : np.ndarray = np.arange(closest_ib_index-300, closest_ib_index+301, step=1, dtype=np.int64)%innerboundaryaug.shape[1] ib_samp : np.ndarray = (np.matmul(tmat, innerboundaryaug[:,ib_idx_samp])[0:3]).T ib_local_s : np.ndarray = np.zeros_like(ib_samp[:,0]) ib_local_s[1:]=np.cumsum(np.linalg.norm(ib_samp[1:] - ib_samp[:-1], ord=2, axis=1)) ib_local_s=ib_local_s-ib_local_s[int(ib_local_s.shape[0]/2)] ib_local_spline : scipy.interpolate.CubicSpline = scipy.interpolate.CubicSpline(ib_local_s, ib_samp) ib_roots : np.ndarray = ib_local_spline.roots(extrapolate=False, discontinuity=False)[0] ib_root_idx : int = np.argmin(np.abs(ib_roots)) ib_root : np.ndarray = ib_roots[ib_root_idx] ib_root_point : np.ndarray = ib_local_spline(ib_root) iboffsets[i] = ib_root_point[1] closest_ob_index : int = ob_kdtree.query(currentrlpose[0:3,3])[1] ob_idx_samp : np.ndarray = np.arange(closest_ob_index-300, closest_ob_index+301, step=1, dtype=np.int64)%outerboundaryaug.shape[1] ob_samp : np.ndarray = (np.matmul(tmat, outerboundaryaug[:,ob_idx_samp])[0:3]).T ob_local_s : np.ndarray = np.zeros_like(ob_samp[:,0]) ob_local_s[1:]=np.cumsum(np.linalg.norm(ob_samp[1:] - ob_samp[:-1], ord=2, axis=1)) ob_local_s=ob_local_s-ob_local_s[int(ob_local_s.shape[0]/2)] ob_local_spline : scipy.interpolate.CubicSpline = scipy.interpolate.CubicSpline(ob_local_s, ob_samp) ob_roots : np.ndarray = ob_local_spline.roots(extrapolate=False, discontinuity=False)[0] ob_root_idx : int = np.argmin(np.abs(ob_roots)) ob_root : np.ndarray = ob_roots[ob_root_idx] ob_root_point : np.ndarray = ob_local_spline(ob_root) oboffsets[i] = ob_root_point[1] except Exception as e: print(e) racelinesamp : np.ndarray = racelineposes[np.arange(0, 25, step=1, dtype=np.int64), :, 3] racelinelocal : np.ndarray = np.matmul(racelinesamp, tmat.T)[:,0:3] fig : matplotlib.figure.Figure = plt.figure() plt.plot(racelinelocal[:,1], racelinelocal[:,0]) plt.plot(ib_samp[:,1], ib_samp[:,0]) plt.plot(ob_samp[:,1], ob_samp[:,0]) plt.show() iboutpoints : np.ndarray = raceline + racelineposes[:,0:3,1]*(iboffsets[:,np.newaxis]) oboutpoints : np.ndarray = raceline + racelineposes[:,0:3,1]*(oboffsets[:,np.newaxis]) delta_outer_rl : np.ndarray = oboutpoints - racelineposes[:,0:3,3] delta_outer_rl = delta_outer_rl/(np.linalg.norm(delta_outer_rl, ord=2, axis=1)[:,np.newaxis]) delta_rl_inner : np.ndarray = racelineposes[:,0:3,3] - iboutpoints delta_rl_inner = delta_rl_inner/(np.linalg.norm(delta_rl_inner, ord=2, axis=1)[:,np.newaxis]) dots = np.sum(delta_outer_rl*delta_rl_inner, axis=1) print(dots) print(np.min(dots)) print(np.max(dots)) # print(oboutposes[2]) fig2 : matplotlib.figure.Figure = plt.figure() plt.plot(racelinex[0], racelinez[0], "g*") plt.quiver(raceline[:,0], raceline[:,2], racelinetangentvecs[:,0], racelinetangentvecs[:,2], angles="xy", scale=30.0) plt.quiver(iboutpoints[:,0], iboutpoints[:,2], racelinetangentvecs[:,0], racelinetangentvecs[:,2], angles="xy", scale=30.0, color="red") plt.quiver(oboutpoints[:,0], oboutpoints[:,2], racelinetangentvecs[:,0], racelinetangentvecs[:,2], angles="xy", scale=30.0, color="green") plt.ylim(maxz, minz) fig3 : matplotlib.figure.Figure = plt.figure() plt.plot(tsamp, iboffsets, label="Inner Boundary Offsets") plt.plot(tsamp, oboffsets, label="Outer Boundary Offsets") plt.legend() plt.show() raceline_dir : str = os.path.dirname(racelinefile) with open(os.path.join(raceline_dir, trackname+"_reparameterized.npz"), "wb") as f: np.savez(f, racelineposes=racelineposes, iboffsets=iboffsets, oboffsets=oboffsets)
parser = argparse.ArgumentParser( description="Look at some statistical metrics of the optimal raceline") parser.add_argument("racelinefile", type=str, help="Path to the raceline") args = parser.parse_args() argdict = vars(args) racelinefile = os.path.abspath(argdict["racelinefile"]) racelinebase = os.path.basename(racelinefile) racelinedir = os.path.dirname(racelinefile) with open(racelinefile, "r") as f: racelinedict = json.load(f) raceline = np.column_stack([racelinedict[k] for k in ["x", "y", "z"]]) trackname = str.split(os.path.splitext(racelinebase)[0], "_")[0] innerboundaryfile = deepracing.searchForFile( trackname + "_innerlimit.json", str.split(os.getenv("F1_TRACK_DIRS"), os.pathsep)) outerboundaryfile = deepracing.searchForFile( trackname + "_outerlimit.json", str.split(os.getenv("F1_TRACK_DIRS"), os.pathsep)) with open(innerboundaryfile, "r") as f: innerboundarydict = json.load(f) with open(outerboundaryfile, "r") as f: outerboundarydict = json.load(f) innerboundary = np.column_stack( [innerboundarydict[k] for k in ["x", "y", "z"]]) outerboundary = np.column_stack( [outerboundarydict[k] for k in ["x", "y", "z"]]) allpoints = np.concatenate([outerboundary, innerboundary, raceline], axis=0) pca = sklearn.decomposition.PCA(n_components=2) pca.fit(allpoints)
def go(argdict): global rin, xin, yin, zin, rsamp, spline, jsonout, radii, dsvec # if argdict["negate_normals"]: # normalsign = -1.0 # else: # normalsign = 1.0 trackfilein = os.path.abspath(argdict["trackfile"]) trackname = str.split( os.path.splitext(os.path.basename(trackfilein))[0], "_")[0] isinnerboundary = "innerlimit" in os.path.basename(trackfilein) isouterboundary = "outerlimit" in os.path.basename(trackfilein) isracingline = not (isinnerboundary or isouterboundary) trackdir = os.path.abspath(os.path.dirname(trackfilein)) print("trackdir: %s" % (trackdir, )) ds = argdict["ds"] k = argdict["k"] if isracingline: searchdirs: List[str] = str.split(os.getenv("F1_TRACK_DIRS"), os.pathsep) print(searchdirs) with open( deepracing.searchForFile(trackname + "_innerlimit.json", searchdirs), "r") as f: d = json.load(f) innerboundary = np.column_stack([d[k] for k in ["x", "y", "z"]]) with open( deepracing.searchForFile(trackname + "_outerlimit.json", searchdirs), "r") as f: d = json.load(f) outerboundary = np.column_stack([d[k] for k in ["x", "y", "z"]]) del d trackfileext = str.lower(os.path.splitext(trackfilein)[1]) if trackfileext == ".csv": trackin = np.loadtxt(trackfilein, delimiter=";") trackin = trackin[:-1] Xin = np.zeros((trackin.shape[0], 4)) Xin[:, 0] = trackin[:, 0] Xin[:, 1] = trackin[:, 1] Xin[:, 2] = 0.5 * (np.mean(innerboundary[:, 1]) + np.mean( outerboundary[:, 1])) #np.zeros_like(trackin[:,1]) Xin[:, 3] = trackin[:, 2] x0 = None minimumcurvatureguess = True elif trackfileext == ".json": with open(trackfilein, "r") as f: d = json.load(f) Xin = np.column_stack([ np.asarray(d[k], dtype=np.float64) for k in ["rin", "xin", "yin", "zin"] ]) x0 = None else: trackin = np.loadtxt(trackfilein, delimiter=",", skiprows=2) I = np.argsort(trackin[:, 0]) track = trackin[I].copy() r = track[:, 0].copy() Xin = np.zeros((track.shape[0], 4)) Xin[:, 1] = track[:, 1] # Xin[:,2] = np.mean(track[:,3]) Xin[:, 2] = track[:, 3] Xin[:, 3] = track[:, 2] Xin[:, 0] = r x0 = None Xin[:, 0] = Xin[:, 0] - Xin[0, 0] minimumcurvatureguess = False if isracingline and argdict["pca"]: allpoints = np.concatenate([innerboundary, outerboundary], axis=0) print("Doing PCA projection", flush=True) pca: sklearn.decomposition.PCA = sklearn.decomposition.PCA( n_components=2) pca.fit(allpoints) print(pca.components_) normalvec: np.ndarray = np.cross(pca.components_[0], pca.components_[1]) print(pca.explained_variance_ratio_) print(np.sum(pca.explained_variance_ratio_)) Xin[:, 1:] = pca.inverse_transform(pca.transform(Xin[:, 1:])) innerboundary = pca.inverse_transform(pca.transform(innerboundary)) outerboundary = pca.inverse_transform(pca.transform(outerboundary)) else: pca = None normalvec: np.ndarray = np.ones(3, dtype=Xin.dtype) Amat: np.ndarray = np.column_stack( [Xin[:, 1], Xin[:, 3], np.ones_like(Xin[:, 1])]) planecoefs: np.ndarray = np.matmul(np.linalg.pinv(Amat), -Xin[:, 2]) normalvec[0] = planecoefs[0] normalvec[2] = planecoefs[1] normalvec = normalvec / np.linalg.norm(normalvec, ord=2) if normalvec[1] < 0: normalvec *= -1.0 print(normalvec) fig1 = plt.figure() zmin: float = np.min(Xin[:, 3]) zmax: float = np.max(Xin[:, 3]) plt.plot(Xin[0, 1], Xin[0, 3], 'g*') plt.plot(Xin[:, 1], Xin[:, 3], c="blue") plt.ylim(zmax + 10.0, zmin - 10.0) if isracingline: plt.plot(innerboundary[:, 0], innerboundary[:, 2], c="black") plt.plot(outerboundary[:, 0], outerboundary[:, 2], c="black") plt.title("Input Trackfile") bc_type = None rin = Xin[:, 0].copy() rin = rin - rin[0] ref = np.array([0.0, 1.0, 0.0], dtype=np.float64) if isinnerboundary: ref *= -1.0 spline, Xsamp, speeds, unit_tangents, unit_normals, rsamp = geometric.computeTangentsAndNormals( rin, Xin[:, 1:], k=k, ref=ref, ds=ds) print("Final delta: %f", (np.linalg.norm(Xsamp[0] - Xsamp[-1], ord=2), )) normaltangentdots = np.sum(unit_tangents * unit_normals, axis=1) if not np.all(np.abs(normaltangentdots) <= 1E-6): raise ValueError( "Something went wrong. one of the tangents is not normal to it's corresponding normal." ) print("Max dot between normals and tangents: %f" % (np.max(normaltangentdots), ), flush=True) offset_points = Xsamp + unit_normals * 1.0 xin = Xin[:, 1] yin = Xin[:, 2] zin = Xin[:, 3] xsamp: np.ndarray = Xsamp[:, 0] ysamp: np.ndarray = Xsamp[:, 1] zsamp: np.ndarray = Xsamp[:, 2] diffs = Xsamp[1:] - Xsamp[:-1] diffnorms = np.linalg.norm(diffs, axis=1) fig2 = plt.figure() plt.ylim(np.max(zsamp) + 10, np.min(zsamp) - 10) # ax = fig.gca(projection='3d') # ax.scatter(x, y, z, c='r', marker='o', s =2.0*np.ones_like(x)) # ax.quiver(x, y, z, unit_normals[:,0], unit_normals[:,1], unit_normals[:,2], length=50.0, normalize=True) plt.scatter(xin, zin, c='b', marker='o', s=16.0 * np.ones_like(Xin[:, 1])) # plt.scatter(x, z, c='r', marker='o', s = 4.0*np.ones_like(x)) plt.plot(xsamp, zsamp, 'r') plt.plot(xsamp, zsamp, 'r*') plt.plot(xsamp[0], zsamp[0], 'g*') if not isracingline: plt.quiver(xsamp, zsamp, unit_normals[:, 0], unit_normals[:, 2], angles="xy", scale=4.0, scale_units="inches") else: plt.plot(innerboundary[:, 0], innerboundary[:, 2], c="black") plt.plot(outerboundary[:, 0], outerboundary[:, 2], c="black") try: plt.show() except: plt.close() print("Output shape: %s" % (str(Xsamp.shape), ), flush=True) fileout = argdict["outfile"] if (fileout is not None) and os.path.isabs(fileout): jsonout = fileout else: resultdir = os.path.join(trackdir, "results") os.makedirs(resultdir, exist_ok=True) if (fileout is not None): jsonout = os.path.abspath(os.path.join(resultdir, fileout)) else: jsonout = os.path.abspath( os.path.join( resultdir, os.path.splitext(os.path.basename(trackfilein))[0] + ".json")) print("jsonout: %s" % (jsonout, ), flush=True) if isinnerboundary or isouterboundary: jsondict = dict() jsondict["rin"] = rin.tolist() jsondict["xin"] = xin.tolist() jsondict["yin"] = yin.tolist() jsondict["zin"] = zin.tolist() jsondict["r"] = rsamp.tolist() jsondict["x"] = xsamp.tolist() jsondict["y"] = ysamp.tolist() jsondict["z"] = zsamp.tolist() jsondict["nx"] = unit_normals[:, 0].tolist() jsondict["ny"] = unit_normals[:, 1].tolist() jsondict["nz"] = unit_normals[:, 2].tolist() jsondict["k"] = k jsondict["ds"] = ds with open(jsonout, "w") as f: json.dump(jsondict, f, indent=1) return print("Optimizing over a space of size: %d" % (rsamp.shape[0], ), flush=True) radii = np.inf * np.ones_like(rsamp) # searchrange : float = 30.0 # dI : int = int(round(searchrange/ds)) dI: int = 2 for i in tqdm(range(Xsamp.shape[0]), desc="Estimating radii of curvature"): ilocal: np.ndarray = np.arange( i - dI, i + dI + 1, step=1, dtype=np.int64) % (Xsamp.shape[0]) Xlocal: np.ndarray = Xsamp[ilocal].T Xlocal = Xlocal - (Xlocal[:, dI])[:, np.newaxis] xvec = Xlocal[:, dI + 1] - Xlocal[:, dI - 1] xvec = xvec / np.linalg.norm(xvec, ord=2) yvec = np.cross(normalvec, xvec) yvec = yvec / np.linalg.norm(yvec, ord=2) zvec = np.cross(xvec, yvec) Xlocal = np.matmul(np.row_stack([xvec, yvec, zvec]), Xlocal) polynomial: np.ndarray = np.polyfit(Xlocal[0], Xlocal[1], 3) polynomialderiv: np.ndarray = np.polyder(polynomial) polynomial2ndderiv: np.ndarray = np.polyder(polynomialderiv) fprime: float = float(np.polyval(polynomialderiv, Xlocal[0, dI])) fprimeprime: float = float( np.polyval(polynomial2ndderiv, Xlocal[0, dI])) radii[i] = ((1.0 + fprime**2)**1.5) / np.abs(fprimeprime) rprint = 100 idxhardcode = int(round(100.0 / ds)) print("idxhardcode: %d" % (idxhardcode, ), flush=True) radii[0:idxhardcode] = radii[-idxhardcode:] = np.inf radii[radii > 250.0] = np.inf print("First %d radii:\n%s" % ( rprint, str(radii[0:rprint]), ), flush=True) print("Final %d radii:\n%s" % ( rprint, str(radii[-rprint:]), ), flush=True) print("Min radius: %f" % (np.min(radii)), flush=True) print("Max radius: %f" % (np.max(radii)), flush=True) print("radii.shape: %s", (str(radii.shape), ), flush=True) maxspeed = argdict["maxv"] dsvec = np.array((rsamp[1:] - rsamp[:-1]).tolist() + [np.linalg.norm(Xsamp[-1] - Xsamp[0])]) #dsvec[-int(round(40/ds)):] = np.inf print("Final %d delta s:\n%s" % ( rprint, str(dsvec[-rprint:]), )) fig3 = plt.figure() plt.plot(rsamp, radii) plt.show() #del track, trackin, xin, yin, zin, dotsquares, Xin, rin, diffs, diffnorms, accels, accelnorms, tangents, tangentnorms, unit_tangents, unit_normals #, rsamp, Xsamp print("yay") writefunction = partial(writeRacelineToFile, argdict) sqp = OptimWrapper(maxspeed, dsvec, radii, callback=writefunction) #method="trust-constr" method = argdict["method"] maxiter = argdict["maxiter"] accelfactor = argdict["accelfactor"] brakefactor = argdict["brakefactor"] cafactor = argdict["cafactor"] x0, optimres = sqp.optimize(maxiter=maxiter, method=method, disp=True, keep_feasible=False, \ x0=x0, accelfactor=accelfactor, brakefactor=brakefactor, cafactor=cafactor, initial_guess_ratio=argdict["initialguessratio"]) writefunction(optimres.x)
s = input("Directory %s already exists. overwrite it? (y\\n)" % (output_dir, )) if s == 'y': shutil.rmtree(output_dir) time.sleep(1.0) else: print("Thanks for playing!") exit(0) os.makedirs(output_dir) session_tags = sorted(getAllSessionPackets(session_folder, use_json), key=udpPacketKey) trackId = session_tags[0].udp_packet.m_trackId trackName = trackNames[trackId] searchFile = trackName + "_racingline.json" racelineFile = searchForFile(searchFile, os.getenv("F1_TRACK_DIRS").split(os.pathsep)) if racelineFile is None: raise ValueError("Could not find trackfile %s" % searchFile) racelinetimes_, racelinedists_, raceline_ = loadRaceline(racelineFile) Nsamp = int(15E3) racelinedists = np.linspace(racelinedists_[0].item(), racelinedists_[-1].item(), Nsamp) racelinetimes = np.linspace(racelinetimes_[0].item(), racelinetimes_[-1].item(), Nsamp) rlfit_t = racelinetimes_.numpy() rlfit_x = raceline_[0:3].numpy().transpose() rlspline: scipy.interpolate.BSpline = scipy.interpolate.make_interp_spline( rlfit_t, rlfit_x) raceline = rlspline(racelinetimes) print("raceline shape: %s" % (str(raceline.shape), )) rlkdtree: scipy.spatial.KDTree = scipy.spatial.KDTree(raceline)
#parser.add_argument("ds", type=float, help="Sample the boundaries at points this distance apart") parser.add_argument("--track-dirs", help="Directories to search for track files, defaults to value of F1_TRACK_DIRS environment variable", type=str, nargs="+", default=None) parser.add_argument("--out", help="where to put the output file", type=str, default=os.path.curdir) args = parser.parse_args() argdict = vars(args) outdir = argdict["out"] track_name = argdict["track_name"] track_dirs = argdict["track_dirs"] if track_dirs is None: track_dirs_env = os.getenv("F1_TRACK_DIRS", None) if track_dirs_env is None: raise ValueError("Must either set F1_TRACK_DIRS environment variable or explicity specify --track-dirs") track_dirs = str.split(track_dirs_env, os.pathsep) inner_boundary_file = deepracing.searchForFile(track_name+"_innerlimit.json", track_dirs) with open(inner_boundary_file,"r") as f: inner_boundary_dict = json.load(f) inner_boundary_in = np.column_stack([inner_boundary_dict["x"], inner_boundary_dict["y"], inner_boundary_dict["z"]]) outer_boundary_file = deepracing.searchForFile(track_name+"_outerlimit.json", track_dirs) with open(outer_boundary_file,"r") as f: outer_boundary_dict = json.load(f) outer_boundary_in = np.column_stack([outer_boundary_dict["x"], outer_boundary_dict["y"], outer_boundary_dict["z"]]) all_points = np.concatenate([inner_boundary_in, outer_boundary_in], axis=0) minx = np.min(all_points[:,0]) maxx = np.max(all_points[:,0]) stdevy = np.std(all_points[:,1])