Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import numpy as np 

2import matplotlib as mpl 

3import matplotlib.pyplot as plt 

4import matplotlib.colors as colors 

5from scipy.constants import golden_ratio 

6 

7import astropy.units as u 

8from astropy.cosmology import z_at_value 

9from astropy.cosmology import WMAP9 as cosmo 

10 

11 

12def Plot_SNR( 

13 var_x, 

14 sample_x, 

15 var_y, 

16 sample_y, 

17 SNRMatrix, 

18 fig=None, 

19 ax=None, 

20 display=True, 

21 return_plt=False, 

22 dl_axis=False, 

23 lb_axis=False, 

24 smooth_contours=True, 

25 cfill=True, 

26 display_cbar=True, 

27 x_axis_label=True, 

28 y_axis_label=True, 

29 x_axis_line=None, 

30 y_axis_line=None, 

31 logLevels_min=-1.0, 

32 logLevels_max=0.0, 

33 hspace=0.15, 

34 wspace=0.1, 

35 contour_kwargs={}, 

36 contourf_kwargs={}, 

37 xticklabels_kwargs={}, 

38 xlabels_kwargs={}, 

39 xline_kwargs={}, 

40 yticklabels_kwargs={}, 

41 ylabels_kwargs={}, 

42 yline_kwargs={}, 

43): 

44 """Plots the SNR contours from calcSNR 

45 

46 Parameters 

47 ---------- 

48 fig : object 

49 matplotlib figure object on which to collate the individual plots 

50 ax : object 

51 matplotlib axes object on which to plot the individual plot 

52 var_x : str 

53 x-axis variable 

54 sample_x : array 

55 samples at which SNRMatrix was calculated corresponding to the x-axis variable 

56 var_y : str 

57 y-axis variable 

58 sample_y : array 

59 samples at which SNRMatrix was calculated corresponding to the y-axis variable 

60 SNRMatrix : array-like 

61 the matrix at which the SNR was calculated corresponding to the particular x and y-axis variable choices 

62 

63 display : bool, optional 

64 Option to turn off display if saving multiple plots to a file 

65 return_plt : bool, optional 

66 Option to return fig and ax 

67 dl_axis : bool, optional 

68 Option to turn on the right hand side labels of luminosity distance 

69 smooth_contours : bool, optional 

70 Option to interpolate contours to a finer mesh size to appear smooth instead of tiered contours 

71 cfill : bool, optional 

72 Option to use filled contours or not, default is True 

73 display_cbar : bool, optional 

74 Option to display the colorbar on the axes object 

75 x_axis_label : bool, optional 

76 Option to display the x axis label 

77 y_axis_label : bool, optional 

78 Option to display the y axis label 

79 x_axis_line : int,float, optional 

80 Option to display a line on the x axis if not None 

81 y_axis_line : int,float, optional 

82 Option to display a line on the y axis if not None 

83 logLevels_min : float, optional 

84 Sets the minimum log level of the colorbar, default is -1.0 

85 logLevels_max : float, optional 

86 Sets the maximum log level of the colorbar, default is 0.0, which sets the maximum to the log maximum value of SNRMatrix 

87 hspace : float, optional 

88 Sets the horizontal space between axes objects, default is 0.15 

89 wspace : float, optional 

90 Sets the horizontal space between axes objects, default is 0.1 

91 contour_kwargs : dict, optional 

92 Sets additional kwargs taken by contour in matplotlib 

93 contourf_kwargs : dict, optional 

94 Sets additional kwargs taken by contourf in matplotlib 

95 xticklabels_kwargs : dict, optional 

96 Sets additional kwargs taken by xticklabel in matplotlib 

97 xlabels_kwargs= : dict, optional 

98 Sets additional kwargs taken by xlabel in matplotlib 

99 xline_kwargs : dict, optional 

100 Sets additional kwargs taken by ax.axvline in matplotlib 

101 yticklabels_kwargs : dict, optional 

102 Sets additional kwargs taken by yticklabel in matplotlib 

103 ylabels_kwargs : dict, optional 

104 Sets additional kwargs taken by ylabel in matplotlib 

105 yline_kwargs : dict, optional 

106 Sets additional kwargs taken by ax.axhline in matplotlib 

107 

108 """ 

109 if fig is not None: 

110 if ax is not None: 

111 pass 

112 else: 

113 fig, ax = plt.subplots() 

114 else: 

115 fig, ax = plt.subplots() 

116 

117 if "colors" not in contour_kwargs.keys() and "cmap" not in contour_kwargs.keys(): 

118 contour_kwargs["colors"] = "k" 

119 if "linewidths" not in contour_kwargs.keys(): 

120 contour_kwargs["linewidths"] = 2.0 

121 

122 if "cmap" not in contourf_kwargs.keys(): 

123 contourf_kwargs["cmap"] = "viridis" 

124 

125 logSNR = np.log10(SNRMatrix) 

126 if logLevels_min == -1.0: 

127 logLevels_min = np.log10(np.array([1.0])) 

128 if logLevels_max == 0.0: 

129 logLevels_max = np.ceil(np.amax(logSNR)) 

130 if logLevels_max < logLevels_min: 

131 raise ValueError("All SNRs are lower than 5.") 

132 

133 logLevels_add = np.log10(np.array([3.0, 10.0, 31.0])) 

134 print_logLevels = np.concatenate( 

135 (logLevels_min, logLevels_add, np.arange(2.0, logLevels_max + 1.0)) 

136 ) 

137 

138 logLevels = print_logLevels 

139 

140 ylabel_min = min(sample_y) 

141 ylabel_max = max(sample_y) 

142 xlabel_min = min(sample_x) 

143 xlabel_max = max(sample_x) 

144 

145 # Set whether log or linearly spaced axes 

146 if xlabel_max < 0.0 or xlabel_min < 0.0 or var_x in ["n_p", "T_obs"]: 

147 xaxis_type = "lin" 

148 step_size = int(xlabel_max - xlabel_min + 1) 

149 x_labels = np.linspace(xlabel_min, xlabel_max, step_size) 

150 else: 

151 x_log_range = np.log10(xlabel_max) - np.log10(xlabel_min) 

152 if x_log_range >= 2.0: 

153 xaxis_type = "log" 

154 step_size = int(np.log10(xlabel_max) - np.log10(xlabel_min) + 1) 

155 x_labels = np.logspace( 

156 np.log10(xlabel_min), np.log10(xlabel_max), step_size 

157 ) 

158 else: 

159 xaxis_type = "lin" 

160 x_scale = 10 ** round(np.log10(xlabel_min)) 

161 x_labels = ( 

162 np.arange( 

163 round(xlabel_min / x_scale), round(xlabel_max / x_scale) + 1, 1 

164 ) 

165 * x_scale 

166 ) 

167 if x_labels[0] < xlabel_min: 

168 x_labels[0] = xlabel_min 

169 if x_labels[-1] > xlabel_max: 

170 x_labels[-1] = xlabel_max 

171 

172 if ylabel_max < 0.0 or ylabel_min < 0.0 or var_y in ["n_p", "T_obs"]: 

173 yaxis_type = "lin" 

174 step_size = int(ylabel_max - ylabel_min + 1) 

175 y_labels = np.linspace(ylabel_min, ylabel_max, step_size) 

176 else: 

177 y_log_range = np.log10(ylabel_max) - np.log10(ylabel_min) 

178 if y_log_range >= 2.0: 

179 yaxis_type = "log" 

180 step_size = int(np.log10(ylabel_max) - np.log10(ylabel_min) + 1) 

181 y_labels = np.logspace( 

182 np.log10(ylabel_min), np.log10(ylabel_max), step_size 

183 ) 

184 else: 

185 yaxis_type = "lin" 

186 y_scale = 10 ** round(np.log10(ylabel_min)) 

187 y_labels = ( 

188 np.arange( 

189 round(ylabel_min / y_scale), round(ylabel_max / y_scale) + 1, 1 

190 ) 

191 * y_scale 

192 ) 

193 if y_labels[0] < ylabel_min: 

194 y_labels[0] = ylabel_min 

195 if y_labels[-1] > ylabel_max: 

196 y_labels[-1] = ylabel_max 

197 

198 # Set axis scales based on what data sampling we used 

199 if yaxis_type == "lin" and xaxis_type == "log": 

200 if cfill == False: 

201 CS1 = ax.contour( 

202 np.log10(sample_x), sample_y, logSNR, print_logLevels, **contour_kwargs 

203 ) 

204 else: 

205 if smooth_contours: 

206 cmap = mpl.cm.get_cmap(name=contourf_kwargs["cmap"]) 

207 cmap.set_under(color="white") 

208 CS1 = ax.imshow( 

209 logSNR, 

210 extent=[ 

211 np.log10(xlabel_min), 

212 np.log10(xlabel_max), 

213 ylabel_min, 

214 ylabel_max, 

215 ], 

216 vmin=logLevels_min, 

217 vmax=logLevels_max, 

218 origin="lower", 

219 aspect="auto", 

220 cmap=cmap, 

221 ) 

222 else: 

223 CS1 = ax.contourf( 

224 np.log10(sample_x), sample_y, logSNR, logLevels, **contourf_kwargs 

225 ) 

226 ax.contour( 

227 np.log10(sample_x), sample_y, logSNR, print_logLevels, **contour_kwargs 

228 ) 

229 ax.set_xlim(np.log10(xlabel_min), np.log10(xlabel_max)) 

230 ax.set_ylim(ylabel_min, ylabel_max) 

231 

232 elif yaxis_type == "log" and xaxis_type == "lin": 

233 if cfill == False: 

234 CS1 = ax.contour( 

235 sample_x, np.log10(sample_y), logSNR, print_logLevels, **contour_kwargs 

236 ) 

237 else: 

238 if smooth_contours: 

239 cmap = mpl.cm.get_cmap(name=contourf_kwargs["cmap"]) 

240 cmap.set_under(color="white") 

241 CS1 = ax.imshow( 

242 logSNR, 

243 extent=[ 

244 xlabel_min, 

245 xlabel_max, 

246 np.log10(ylabel_min), 

247 np.log10(ylabel_max), 

248 ], 

249 vmin=logLevels_min, 

250 vmax=logLevels_max, 

251 origin="lower", 

252 aspect="auto", 

253 cmap=cmap, 

254 ) 

255 else: 

256 CS1 = ax.contourf( 

257 sample_x, np.log10(sample_y), logSNR, logLevels, **contourf_kwargs 

258 ) 

259 ax.contour( 

260 sample_x, np.log10(sample_y), logSNR, print_logLevels, **contour_kwargs 

261 ) 

262 ax.set_xlim(xlabel_min, xlabel_max) 

263 ax.set_ylim(np.log10(ylabel_min), np.log10(ylabel_max)) 

264 elif yaxis_type == "lin" and xaxis_type == "lin": 

265 if cfill == False: 

266 CS1 = ax.contour( 

267 sample_x, sample_y, logSNR, print_logLevels, **contour_kwargs 

268 ) 

269 else: 

270 if smooth_contours: 

271 cmap = mpl.cm.get_cmap(name=contourf_kwargs["cmap"]) 

272 cmap.set_under(color="white") 

273 CS1 = ax.imshow( 

274 logSNR, 

275 extent=[xlabel_min, xlabel_max, ylabel_min, ylabel_max], 

276 vmin=logLevels_min, 

277 vmax=logLevels_max, 

278 origin="lower", 

279 aspect="auto", 

280 cmap=cmap, 

281 ) 

282 else: 

283 CS1 = ax.contourf( 

284 sample_x, sample_y, logSNR, logLevels, **contourf_kwargs 

285 ) 

286 ax.contour(sample_x, sample_y, logSNR, print_logLevels, **contour_kwargs) 

287 ax.set_xlim(xlabel_min, xlabel_max) 

288 ax.set_ylim(ylabel_min, ylabel_max) 

289 else: 

290 if cfill == False: 

291 CS1 = ax.contour( 

292 np.log10(sample_x), 

293 np.log10(sample_y), 

294 logSNR, 

295 print_logLevels, 

296 **contour_kwargs 

297 ) 

298 else: 

299 if smooth_contours: 

300 cmap = mpl.cm.get_cmap(name=contourf_kwargs["cmap"]) 

301 cmap.set_under(color="white") 

302 CS1 = ax.imshow( 

303 logSNR, 

304 extent=[ 

305 np.log10(xlabel_min), 

306 np.log10(xlabel_max), 

307 np.log10(ylabel_min), 

308 np.log10(ylabel_max), 

309 ], 

310 vmin=logLevels_min, 

311 vmax=logLevels_max, 

312 origin="lower", 

313 aspect="auto", 

314 cmap=cmap, 

315 interpolation="None", 

316 ) 

317 else: 

318 CS1 = ax.contourf( 

319 np.log10(sample_x), 

320 np.log10(sample_y), 

321 logSNR, 

322 logLevels, 

323 **contourf_kwargs 

324 ) 

325 ax.contour( 

326 np.log10(sample_x), 

327 np.log10(sample_y), 

328 logSNR, 

329 print_logLevels, 

330 **contour_kwargs 

331 ) 

332 ax.set_xlim(np.log10(xlabel_min), np.log10(xlabel_max)) 

333 ax.set_ylim(np.log10(ylabel_min), np.log10(ylabel_max)) 

334 

335 Get_Axes_Labels( 

336 ax, 

337 "x", 

338 var_x, 

339 xaxis_type, 

340 x_labels, 

341 x_axis_line, 

342 xlabels_kwargs, 

343 xticklabels_kwargs, 

344 xline_kwargs, 

345 ) 

346 Get_Axes_Labels( 

347 ax, 

348 "y", 

349 var_y, 

350 yaxis_type, 

351 y_labels, 

352 y_axis_line, 

353 ylabels_kwargs, 

354 yticklabels_kwargs, 

355 yline_kwargs, 

356 ) 

357 

358 if not x_axis_label: 

359 ax.set_xticklabels("") 

360 ax.set_xlabel("") 

361 if not y_axis_label: 

362 ax.set_yticklabels("") 

363 ax.set_ylabel("") 

364 

365 # If true, display luminosity distance on right side of plot 

366 if dl_axis: 

367 if var_y != "z": 

368 raise ValueError( 

369 "Sorry, we can only plot luminosity distance when redshift is on the y axis." 

370 ) 

371 

372 # Set other side y-axis for luminosity distance scalings 

373 ax2 = ax.twinx() 

374 # Set axis scales based on what data sampling we used 

375 if yaxis_type == "lin" and xaxis_type == "log": 

376 ax2.contour( 

377 np.log10(sample_x), sample_y, logSNR, print_logLevels, **contour_kwargs 

378 ) 

379 elif yaxis_type == "log" and xaxis_type == "lin": 

380 ax2.contour( 

381 sample_x, np.log10(sample_y), logSNR, print_logLevels, **contour_kwargs 

382 ) 

383 else: 

384 ax2.contour( 

385 np.log10(sample_x), 

386 np.log10(sample_y), 

387 logSNR, 

388 print_logLevels, 

389 **contour_kwargs 

390 ) 

391 

392 dists_min = cosmo.luminosity_distance(ylabel_min).to("Gpc") 

393 dists_min = np.ceil(np.log10(dists_min.value)) 

394 dists_max = cosmo.luminosity_distance(ylabel_max).to("Gpc") 

395 dists_max = np.ceil(np.log10(dists_max.value)) 

396 dists = np.arange(dists_min, dists_max) 

397 dists = 10 ** dists * u.Gpc 

398 

399 distticks = [z_at_value(cosmo.luminosity_distance, dist) for dist in dists] 

400 # Set other side y-axis for lookback time scalings 

401 ax2.set_yticks(np.log10(distticks)) 

402 # ax2.set_yticklabels(['%f' %dist for dist in distticks],fontsize = axissize) 

403 ax2.set_yticklabels( 

404 [ 

405 r"$10^{%i}$" % np.log10(dist) 

406 if np.abs(int(np.log10(dist))) > 1 

407 else "{:g}".format(dist) 

408 for dist in dists.value 

409 ] 

410 ) 

411 ax2.set_ylabel(r"$D_{L}$ [Gpc]") 

412 

413 # cbar = fig.colorbar(CS1,cax=cbar_ax,ax=(ax,ax2),pad=0.01,ticks=print_logLevels) 

414 elif lb_axis: 

415 if var_y != "z": 

416 raise ValueError( 

417 "Sorry, we can only plot lookback time when redshift is on the y axis." 

418 ) 

419 # Set other side y-axis for lookback time scalings 

420 ax2 = ax.twinx() 

421 # Set axis scales based on what data sampling we used 

422 if yaxis_type == "lin" and xaxis_type == "log": 

423 ax2.contour( 

424 np.log10(sample_x), sample_y, logSNR, print_logLevels, **contour_kwargs 

425 ) 

426 elif yaxis_type == "log" and xaxis_type == "lin": 

427 ax2.contour( 

428 sample_x, np.log10(sample_y), logSNR, print_logLevels, **contour_kwargs 

429 ) 

430 else: 

431 ax2.contour( 

432 np.log10(sample_x), 

433 np.log10(sample_y), 

434 logSNR, 

435 print_logLevels, 

436 **contour_kwargs 

437 ) 

438 

439 ages1 = np.array([13.5, 13, 10, 5, 1]) * u.Gyr 

440 ages2 = np.array([500, 100, 10, 1]) * u.Myr 

441 ages2 = ages2.to("Gyr") 

442 ages = np.hstack((ages1.value, ages2.value)) 

443 ages = ages * u.Gyr 

444 ageticks = [z_at_value(cosmo.age, age) for age in ages] 

445 

446 # Set axes limits 

447 ax2.set_yticks(np.log10(ageticks)) 

448 ax2.set_yticklabels(["{:g}".format(age) for age in ages.value]) 

449 ax2.set_ylabel(r"$t_{\rm cosmic}$ [Gyr]") 

450 ax2.yaxis.set_label_coords(1.2, 0.5) 

451 

452 if display_cbar: 

453 if lb_axis or dl_axis: 

454 fig.subplots_adjust(right=0.8) 

455 cbar_ax = fig.add_axes([0.9, 0.15, 0.025, 0.7]) 

456 # Make colorbar 

457 if cfill == False: 

458 # Make colorbar 

459 norm = colors.Normalize(vmin=logLevels_min, vmax=logLevels_max) 

460 tick_levels = np.linspace( 

461 float(logLevels_min), logLevels_max, len(print_logLevels) 

462 ) 

463 cbar = mpl.colorbar.ColorbarBase( 

464 cbar_ax, 

465 ax=(ax, ax2), 

466 pad=0.01, 

467 cmap=CS1.cmap, 

468 norm=norm, 

469 boundaries=tick_levels, 

470 ticks=tick_levels, 

471 spacing="proportional", 

472 ) 

473 else: 

474 cbar = fig.colorbar(CS1, cax=cbar_ax, ax=(ax, ax2), pad=0.01) 

475 else: 

476 fig.subplots_adjust(right=0.8) 

477 cbar_ax = fig.add_axes([0.82, 0.15, 0.025, 0.7]) 

478 if cfill == False: 

479 # Make colorbar 

480 norm = colors.Normalize(vmin=logLevels_min, vmax=logLevels_max) 

481 tick_levels = np.linspace( 

482 float(logLevels_min), logLevels_max, len(print_logLevels) 

483 ) 

484 cbar = mpl.colorbar.ColorbarBase( 

485 cbar_ax, 

486 cmap=CS1.cmap, 

487 norm=norm, 

488 boundaries=tick_levels, 

489 ticks=tick_levels, 

490 spacing="proportional", 

491 ) 

492 else: 

493 # Make colorbar 

494 cbar = fig.colorbar(CS1, cax=cbar_ax, ticks=print_logLevels) 

495 

496 cbar.set_label(r'SNR') 

497 cbar.ax.set_yticklabels( 

498 [ 

499 r"$10^{%i}$" % x if int(x) > 1 else r"$%i$" % (10 ** x) 

500 for x in print_logLevels 

501 ], 

502 **yticklabels_kwargs 

503 ) 

504 

505 if display: 

506 # fig.tight_layout() 

507 fig.subplots_adjust(hspace=hspace, wspace=wspace) 

508 plt.show() 

509 

510 if return_plt: 

511 return fig, ax 

512 

513 

514def Get_Axes_Labels( 

515 ax, 

516 var_axis, 

517 var, 

518 var_scale, 

519 orig_labels, 

520 line_val, 

521 label_kwargs, 

522 tick_label_kwargs, 

523 line_kwargs, 

524): 

525 """Gives paper plot labels for given axis 

526 

527 Parameters 

528 ---------- 

529 ax: object 

530 The current axes object 

531 var_axis: str 

532 The axis to change labels and ticks, can either be 'y' or 'x' 

533 var: str 

534 The variable to label 

535 orig_labels: list,np.ndarray 

536 The original labels for the particular axis, may be updated depending on parameter 

537 line_val: int,float 

538 Value of line plotted on var_axis if not None. Assumed to be non-log10 value 

539 label_kwargs: dict 

540 The dictionary adjusting the particular axis' label kwargs 

541 tick_label_kwargs: dict 

542 The dictionary adjusting the particular axis' tick label kwargs 

543 line_kwargs: dict 

544 The dictionary associated with the line displayed on var_axis 

545 

546 """ 

547 

548 # Set axes labels and whether log or linearly spaced 

549 if var_axis not in ["y", "x"]: 

550 raise ValueError("var_axis can only by x or y") 

551 

552 ax_dict = {} 

553 

554 if var == "M": 

555 ax_dict[var_axis + "ticks"] = np.log10(orig_labels) 

556 ax_dict[var_axis + "label"] = r"$M_{\mathrm{tot}}~[M_{\odot}]$" 

557 ax_dict[var_axis + "ticklabels"] = [ 

558 r"$10^{%i}$" % x if int(x) > 1 else r"$%i$" % (10 ** x) 

559 for x in np.log10(orig_labels) 

560 ] 

561 elif var == "q": 

562 new_labels = orig_labels[::2] 

563 ax_dict[var_axis + "ticks"] = new_labels 

564 ax_dict[var_axis + "label"] = r"$\mathrm{Mass~Ratio}~q$" 

565 ax_dict[var_axis + "ticklabels"] = [r"$%i$" % int(x) for x in new_labels] 

566 elif var == "z": 

567 ax_dict[var_axis + "ticks"] = np.log10(orig_labels) 

568 ax_dict[var_axis + "label"] = r"$\mathrm{Redshift}~z$" 

569 ax_dict[var_axis + "ticklabels"] = [ 

570 x if int(x) < 1 else int(x) for x in orig_labels 

571 ] 

572 elif var in ["chi1", "chi2"]: 

573 new_labels = ( 

574 np.arange(round(min(orig_labels) * 10), round(max(orig_labels) * 10) + 1, 1) 

575 / 10 

576 ) 

577 new_labels = new_labels[::2] 

578 ax_dict[var_axis + "ticks"] = new_labels 

579 ax_dict[var_axis + "label"] = r"$\mathrm{Spin}~\chi_{i}$" 

580 ax_dict[var_axis + "ticklabels"] = [r"$%.1f$" % x for x in new_labels] 

581 elif var == "L": 

582 # Proposed Value = 2.5Gm: L3 LISA 

583 ax_dict[var_axis + "ticks"] = np.log10(orig_labels) 

584 ax_dict[var_axis + "label"] = r"Arm Length [m]" 

585 ax_dict[var_axis + "ticklabels"] = [ 

586 r"$10^{%i}$" % x if int(x) > 1 else r"$%i$" % (10 ** x) 

587 for x in np.log10(orig_labels) 

588 ] 

589 elif var == "A_acc": 

590 # Proposed Value = 3x10^{-15}: L3 LISA 

591 ax_dict[var_axis + "ticks"] = np.log10(orig_labels) 

592 ax_dict[var_axis + "label"] = r"$A_{\mathrm{acc}} [\mathrm{m~s^{-2}}]$" 

593 ax_dict[var_axis + "ticklabels"] = [ 

594 r"$10^{%.0f}$" % x for x in np.log10(orig_labels) 

595 ] 

596 elif var == "A_IFO": 

597 # Proposed Value = 10^{-12}: L3 LISA 

598 ax_dict[var_axis + "ticks"] = np.log10(orig_labels) 

599 ax_dict[var_axis + "label"] = r"$A_{\mathrm{IFO}}$ [m]" 

600 ax_dict[var_axis + "ticklabels"] = [ 

601 r"$10^{%.0f}$" % x for x in np.log10(orig_labels) 

602 ] 

603 elif var == "f_acc_break_low": 

604 # Proposed Value = 0.4mHz: L3 LISA 

605 scale = 10 ** round(np.log10(min(orig_labels))) 

606 new_labels = ( 

607 np.arange( 

608 round(min(orig_labels) / scale), round(max(orig_labels) / scale) + 1, 1 

609 ) 

610 * scale 

611 ) 

612 ax_dict[var_axis + "ticks"] = new_labels 

613 ax_dict[var_axis + "label"] = r"$f_{\mathrm{acc,low}}$ [mHz]" 

614 ax_dict[var_axis + "ticklabels"] = [r"$%.1f$" % x for x in new_labels * 1e3] 

615 elif var == "f_acc_break_high": 

616 # Proposed Value = 8mHz: L3 LISA 

617 scale = 10 ** round(np.log10(min(orig_labels))) 

618 new_labels = ( 

619 np.arange( 

620 round(min(orig_labels) / scale), round(max(orig_labels) / scale) + 1, 1 

621 ) 

622 * scale 

623 ) 

624 ax_dict[var_axis + "ticks"] = new_labels 

625 ax_dict[var_axis + "label"] = r"$f_{\mathrm{acc,high}}$ [mHz]" 

626 ax_dict[var_axis + "ticklabels"] = [r"$%.1f$" % x for x in new_labels * 1e3] 

627 elif var == "f_IFO_break": 

628 # Proposed Value = 2mHz: L3 LISA 

629 scale = 10 ** round(np.log10(min(orig_labels))) 

630 new_labels = ( 

631 np.arange( 

632 round(min(orig_labels) / scale), round(max(orig_labels) / scale) + 1, 1 

633 ) 

634 * scale 

635 ) 

636 ax_dict[var_axis + "ticks"] = new_labels 

637 ax_dict[var_axis + "label"] = r"$f_{\mathrm{IFO,break}}$ [mHz]" 

638 ax_dict[var_axis + "ticklabels"] = [r"$%.1f$" % x for x in new_labels * 1e3] 

639 elif var == "n_p": 

640 sample_range = max(orig_labels) - min(orig_labels) 

641 sample_rate = max(2, int(sample_range / 10)) 

642 new_labels = orig_labels[::sample_rate] 

643 ax_dict[var_axis + "ticks"] = new_labels 

644 ax_dict[var_axis + "label"] = r"$\mathrm{Number~of~Pulsars}$" 

645 ax_dict[var_axis + "ticklabels"] = [r"$%i$" % int(x) for x in new_labels] 

646 elif var == "cadence": 

647 new_labels = np.arange(round(min(orig_labels)), round(max(orig_labels)) + 1, 5) 

648 ax_dict[var_axis + "ticks"] = new_labels 

649 ax_dict[ 

650 var_axis + "label" 

651 ] = r"$\mathrm{Observation~Cadence}$ $[\mathrm{yr}^{-1}]$" 

652 ax_dict[var_axis + "ticklabels"] = [r"$%i$" % int(x) for x in new_labels] 

653 elif var == "sigma": 

654 scale = 10 ** round(np.log10(min(orig_labels))) 

655 new_labels = ( 

656 np.arange( 

657 round(min(orig_labels) / scale), round(max(orig_labels) / scale) + 1, 1 

658 ) 

659 * scale 

660 ) 

661 ax_dict[var_axis + "ticks"] = new_labels 

662 ax_dict[var_axis + "label"] = r"TOA Error RMS [ns]" 

663 ax_dict[var_axis + "ticklabels"] = [r"$%.0f$" % x for x in new_labels * 1e9] 

664 elif var == "T_obs": 

665 new_labels = orig_labels[::2] 

666 ax_dict[var_axis + "ticks"] = new_labels 

667 ax_dict[var_axis + "label"] = r"${\rm T_{obs}}$ [yr]" 

668 ax_dict[var_axis + "ticklabels"] = [r"$%i$" % int(x) for x in new_labels] 

669 elif var == "Infrastructure Length": 

670 # Proposed Value = 3995: aLIGO, Voyager 

671 # Proposed Value = 40000: CE1 

672 if var_scale == "log": 

673 ax_dict[var_axis + "ticks"] = np.log10(orig_labels) 

674 ax_dict[var_axis + "label"] = r"Infrastructure Length [m]" 

675 ax_dict[var_axis + "ticklabels"] = [ 

676 r"$10^{%.0f}$" % y if abs(int(y)) > 1 else r"$%.1f$" % (10 ** y) 

677 for y in np.log10(orig_labels) 

678 ] 

679 elif var_scale == "lin": 

680 ax_dict[var_axis + "ticks"] = orig_labels 

681 ax_dict[var_axis + "label"] = r"Infrastructure Length [km]" 

682 ax_dict[var_axis + "ticklabels"] = [ 

683 r"$%.1f$" % (x / 1e3) for x in orig_labels 

684 ] 

685 elif var == "Laser Power": 

686 # Proposed Value = 125: aLIGO 

687 # Proposed Value = 145: Voyager 

688 # Proposed Value = 150: CE1 

689 if var_scale == "log": 

690 ax_dict[var_axis + "ticks"] = np.log10(orig_labels) 

691 ax_dict[var_axis + "label"] = r"Laser Power [W]" 

692 ax_dict[var_axis + "ticklabels"] = [ 

693 r"$10^{%.0f}$" % x if abs(int(x)) > 1 else r"$%.1f$" % (10 ** x) 

694 for x in np.log10(orig_labels) 

695 ] 

696 elif var_scale == "lin": 

697 ax_dict[var_axis + "ticks"] = orig_labels 

698 ax_dict[var_axis + "label"] = r"Laser Power [W]" 

699 ax_dict[var_axis + "ticklabels"] = [r"$%.1f$" % x for x in orig_labels] 

700 elif var == "Seismic Gamma": 

701 # Proposed Value = 0.8: aLIGO, Voyager, CE1 

702 if var_scale == "log": 

703 ax_dict[var_axis + "ticks"] = np.log10(orig_labels) 

704 ax_dict[var_axis + "label"] = r"Seismic Gamma" 

705 ax_dict[var_axis + "ticklabels"] = [ 

706 r"$10^{%.0f}$" % y if abs(int(y)) > 1 else r"$%.1f$" % (10 ** y) 

707 for y in np.log10(orig_labels) 

708 ] 

709 elif var_scale == "lin": 

710 ax_dict[var_axis + "ticks"] = orig_labels 

711 ax_dict[var_axis + "label"] = r"Seismic Gamma" 

712 ax_dict[var_axis + "ticklabels"] = [r"$%.1f$" % y for y in orig_labels] 

713 elif var == "Materials Substrate Temp": 

714 # Proposed Value = 295: aLIGO, CE1 

715 # Proposed Value = 123: Voyager 

716 if var_scale == "lin": 

717 ax_dict[var_axis + "ticks"] = orig_labels 

718 ax_dict[var_axis + "label"] = r"Mirror Substrate Temp [K]" 

719 ax_dict[var_axis + "ticklabels"] = [ 

720 r"$%.1f \times 10^{%i}$" % (x / 10 ** int(np.log10(x)), np.log10(x)) 

721 if np.abs(int(np.log10(x))) > 1 

722 else "{:g}".format(x) 

723 for x in orig_labels 

724 ] 

725 elif var_scale == "log": 

726 ax_dict[var_axis + "ticks"] = np.log10(orig_labels) 

727 ax_dict[var_axis + "label"] = r"Mirror Substrate Temp [K]" 

728 ax_dict[var_axis + "ticklabels"] = [ 

729 r"$10^{%.0f}$" % y if abs(int(y)) > 1 else r"$%.1f$" % (10 ** y) 

730 for y in np.log10(orig_labels) 

731 ] 

732 else: 

733 if var_scale == "lin": 

734 ax_dict[var_axis + "ticks"] = orig_labels 

735 ax_dict[var_axis + "label"] = str(var) 

736 ax_dict[var_axis + "ticklabels"] = [ 

737 r"$%.1f \times 10^{%i}$" % (x / 10 ** int(np.log10(x)), np.log10(x)) 

738 if np.abs(int(np.log10(x))) > 1 

739 else "{:g}".format(x) 

740 for x in orig_labels 

741 ] 

742 elif var_scale == "log": 

743 ax_dict[var_axis + "ticks"] = np.log10(orig_labels) 

744 ax_dict[var_axis + "label"] = str(var) 

745 ax_dict[var_axis + "ticklabels"] = [ 

746 r"$10^{%.0f}$" % y if abs(int(y)) > 1 else r"$%.1f$" % (10 ** y) 

747 for y in np.log10(orig_labels) 

748 ] 

749 if line_val is not None: 

750 if "linestyle" not in line_kwargs.keys(): 

751 line_kwargs["linestyle"] = "--" 

752 if "color" not in line_kwargs.keys(): 

753 line_kwargs["color"] = "k" 

754 if "label" not in line_kwargs.keys(): 

755 line_kwargs["label"] = "Proposed Value" 

756 

757 if var_scale == "log": 

758 if var_axis == "y": 

759 ax.axhline(y=np.log10(line_val), **line_kwargs) 

760 elif var_axis == "x": 

761 ax.axvline(x=np.log10(line_val), **line_kwargs) 

762 elif var_scale == "lin": 

763 if var_axis == "y": 

764 ax.axhline(y=line_val, **line_kwargs) 

765 elif var_axis == "x": 

766 ax.axvline(x=line_val, **line_kwargs) 

767 

768 ax.update(ax_dict) 

769 if label_kwargs: 

770 if var_axis == "y": 

771 ax.set_ylabel(ax.get_ylabel(), **label_kwargs) 

772 elif var_axis == "x": 

773 ax.set_xlabel(ax.get_xlabel(), **label_kwargs) 

774 

775 if tick_label_kwargs: 

776 if var_axis == "y": 

777 ax.set_yticklabels(ax.get_yticklabels(), **tick_label_kwargs) 

778 elif var_axis == "x": 

779 ax.set_xticklabels(ax.get_xticklabels(), **tick_label_kwargs)