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=False, 

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 var_x : str 

49 x-axis variable 

50 sample_x : array 

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

52 var_y : str 

53 y-axis variable 

54 sample_y : array 

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

56 SNRMatrix : array-like 

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

58 

59 fig : object, optional 

60 matplotlib figure object on which to collate the individual plots 

61 ax : object, optional 

62 matplotlib axes object on which to plot the individual plot 

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 lb_axis : bool, optional 

70 Option to turn on the right hand side labels of lookback time 

71 smooth_contours : bool, optional 

72 Option to have contours appear smooth instead of tiered (depending on sample size the edges appear boxey). 

73 cfill : bool, optional 

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

75 display_cbar : bool, optional 

76 Option to display the colorbar on the axes object 

77 x_axis_label : bool, optional 

78 Option to display the x axis label 

79 y_axis_label : bool, optional 

80 Option to display the y axis label 

81 x_axis_line : int,float, optional 

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

83 y_axis_line : int,float, optional 

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

85 logLevels_min : float, optional 

86 Sets the minimum log level of the colorbar, default is -1.0 which set the minimum to the log minimum of the given SNRMatrix 

87 logLevels_max : float, optional 

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

89 hspace : float, optional 

90 Sets the vertical space between axes objects, default is 0.15 

91 wspace : float, optional 

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

93 contour_kwargs : dict, optional 

94 Sets additional kwargs taken by contour in matplotlib 

95 contourf_kwargs : dict, optional 

96 Sets additional kwargs taken by contourf in matplotlib 

97 xticklabels_kwargs : dict, optional 

98 Sets additional kwargs taken by xticklabel in matplotlib 

99 xlabels_kwargs= : dict, optional 

100 Sets additional kwargs taken by xlabel in matplotlib 

101 xline_kwargs : dict, optional 

102 Sets additional kwargs taken by ax.axvline in matplotlib 

103 yticklabels_kwargs : dict, optional 

104 Sets additional kwargs taken by yticklabel in matplotlib 

105 ylabels_kwargs : dict, optional 

106 Sets additional kwargs taken by ylabel in matplotlib 

107 yline_kwargs : dict, optional 

108 Sets additional kwargs taken by ax.axhline in matplotlib 

109 

110 """ 

111 if fig is not None: 

112 if ax is not None: 

113 pass 

114 else: 

115 fig, ax = plt.subplots() 

116 else: 

117 fig, ax = plt.subplots() 

118 

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

120 contour_kwargs["colors"] = "k" 

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

122 contour_kwargs["linewidths"] = 2.0 

123 

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

125 contourf_kwargs["cmap"] = "viridis" 

126 

127 logSNR = np.log10(SNRMatrix) 

128 if logLevels_min == -1.0: 

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

130 if logLevels_max == 0.0: 

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

132 if logLevels_max < logLevels_min: 

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

134 

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

136 print_logLevels = np.concatenate( 

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

138 ) 

139 

140 logLevels = print_logLevels 

141 

142 ylabel_min = min(sample_y) 

143 ylabel_max = max(sample_y) 

144 xlabel_min = min(sample_x) 

145 xlabel_max = max(sample_x) 

146 

147 # Set whether log or linearly spaced axes 

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

149 xaxis_type = "lin" 

150 step_size = int(xlabel_max - xlabel_min + 1) 

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

152 else: 

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

154 if x_log_range >= 2.0: 

155 xaxis_type = "log" 

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

157 x_labels = np.logspace( 

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

159 ) 

160 else: 

161 xaxis_type = "lin" 

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

163 x_labels = ( 

164 np.arange( 

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

166 ) 

167 * x_scale 

168 ) 

169 if x_labels[0] < xlabel_min: 

170 x_labels[0] = xlabel_min 

171 if x_labels[-1] > xlabel_max: 

172 x_labels[-1] = xlabel_max 

173 

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

175 yaxis_type = "lin" 

176 step_size = int(ylabel_max - ylabel_min + 1) 

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

178 else: 

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

180 if y_log_range >= 2.0: 

181 yaxis_type = "log" 

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

183 y_labels = np.logspace( 

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

185 ) 

186 else: 

187 yaxis_type = "lin" 

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

189 y_labels = ( 

190 np.arange( 

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

192 ) 

193 * y_scale 

194 ) 

195 if y_labels[0] < ylabel_min: 

196 y_labels[0] = ylabel_min 

197 if y_labels[-1] > ylabel_max: 

198 y_labels[-1] = ylabel_max 

199 

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

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

202 if cfill == False: 

203 CS1 = ax.contour( 

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

205 ) 

206 else: 

207 if smooth_contours: 

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

209 cmap.set_under(color="white") 

210 CS1 = ax.imshow( 

211 logSNR, 

212 extent=[ 

213 np.log10(xlabel_min), 

214 np.log10(xlabel_max), 

215 ylabel_min, 

216 ylabel_max, 

217 ], 

218 vmin=logLevels_min, 

219 vmax=logLevels_max, 

220 origin="lower", 

221 aspect="auto", 

222 cmap=cmap, 

223 ) 

224 else: 

225 CS1 = ax.contourf( 

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

227 ) 

228 ax.contour( 

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

230 ) 

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

232 ax.set_ylim(ylabel_min, ylabel_max) 

233 

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

235 if cfill == False: 

236 CS1 = ax.contour( 

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

238 ) 

239 else: 

240 if smooth_contours: 

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

242 cmap.set_under(color="white") 

243 CS1 = ax.imshow( 

244 logSNR, 

245 extent=[ 

246 xlabel_min, 

247 xlabel_max, 

248 np.log10(ylabel_min), 

249 np.log10(ylabel_max), 

250 ], 

251 vmin=logLevels_min, 

252 vmax=logLevels_max, 

253 origin="lower", 

254 aspect="auto", 

255 cmap=cmap, 

256 ) 

257 else: 

258 CS1 = ax.contourf( 

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

260 ) 

261 ax.contour( 

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

263 ) 

264 ax.set_xlim(xlabel_min, xlabel_max) 

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

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

267 if cfill == False: 

268 CS1 = ax.contour( 

269 sample_x, sample_y, logSNR, print_logLevels, **contour_kwargs 

270 ) 

271 else: 

272 if smooth_contours: 

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

274 cmap.set_under(color="white") 

275 CS1 = ax.imshow( 

276 logSNR, 

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

278 vmin=logLevels_min, 

279 vmax=logLevels_max, 

280 origin="lower", 

281 aspect="auto", 

282 cmap=cmap, 

283 ) 

284 else: 

285 CS1 = ax.contourf( 

286 sample_x, sample_y, logSNR, logLevels, **contourf_kwargs 

287 ) 

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

289 ax.set_xlim(xlabel_min, xlabel_max) 

290 ax.set_ylim(ylabel_min, ylabel_max) 

291 else: 

292 if cfill == False: 

293 CS1 = ax.contour( 

294 np.log10(sample_x), 

295 np.log10(sample_y), 

296 logSNR, 

297 print_logLevels, 

298 **contour_kwargs 

299 ) 

300 else: 

301 if smooth_contours: 

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

303 cmap.set_under(color="white") 

304 CS1 = ax.imshow( 

305 logSNR, 

306 extent=[ 

307 np.log10(xlabel_min), 

308 np.log10(xlabel_max), 

309 np.log10(ylabel_min), 

310 np.log10(ylabel_max), 

311 ], 

312 vmin=logLevels_min, 

313 vmax=logLevels_max, 

314 origin="lower", 

315 aspect="auto", 

316 cmap=cmap, 

317 interpolation="None", 

318 ) 

319 else: 

320 CS1 = ax.contourf( 

321 np.log10(sample_x), 

322 np.log10(sample_y), 

323 logSNR, 

324 logLevels, 

325 **contourf_kwargs 

326 ) 

327 ax.contour( 

328 np.log10(sample_x), 

329 np.log10(sample_y), 

330 logSNR, 

331 print_logLevels, 

332 **contour_kwargs 

333 ) 

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

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

336 

337 Get_Axes_Labels( 

338 ax, 

339 "x", 

340 var_x, 

341 xaxis_type, 

342 x_labels, 

343 x_axis_line, 

344 xlabels_kwargs, 

345 xticklabels_kwargs, 

346 xline_kwargs, 

347 ) 

348 Get_Axes_Labels( 

349 ax, 

350 "y", 

351 var_y, 

352 yaxis_type, 

353 y_labels, 

354 y_axis_line, 

355 ylabels_kwargs, 

356 yticklabels_kwargs, 

357 yline_kwargs, 

358 ) 

359 

360 if not x_axis_label: 

361 ax.set_xticklabels("") 

362 ax.set_xlabel("") 

363 if not y_axis_label: 

364 ax.set_yticklabels("") 

365 ax.set_ylabel("") 

366 

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

368 if dl_axis: 

369 if var_y != "z": 

370 raise ValueError( 

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

372 ) 

373 

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

375 ax2 = ax.twinx() 

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

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

378 ax2.contour( 

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

380 ) 

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

382 ax2.contour( 

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

384 ) 

385 else: 

386 ax2.contour( 

387 np.log10(sample_x), 

388 np.log10(sample_y), 

389 logSNR, 

390 print_logLevels, 

391 **contour_kwargs 

392 ) 

393 

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

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

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

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

398 dists = np.arange(dists_min, dists_max) 

399 dists = 10 ** dists * u.Gpc 

400 

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

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

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

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

405 ax2.set_yticklabels( 

406 [ 

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

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

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

410 for dist in dists.value 

411 ] 

412 ) 

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

414 

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

416 elif lb_axis: 

417 if var_y != "z": 

418 raise ValueError( 

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

420 ) 

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

422 ax2 = ax.twinx() 

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

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

425 ax2.contour( 

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

427 ) 

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

429 ax2.contour( 

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

431 ) 

432 else: 

433 ax2.contour( 

434 np.log10(sample_x), 

435 np.log10(sample_y), 

436 logSNR, 

437 print_logLevels, 

438 **contour_kwargs 

439 ) 

440 

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

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

443 ages2 = ages2.to("Gyr") 

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

445 ages = ages * u.Gyr 

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

447 

448 # Set axes limits 

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

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

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

452 ax2.yaxis.set_label_coords(1.2, 0.5) 

453 

454 if display_cbar: 

455 if lb_axis or dl_axis: 

456 fig.subplots_adjust(right=0.8) 

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

458 # Make colorbar 

459 if cfill == False: 

460 # Make colorbar 

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

462 tick_levels = np.linspace( 

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

464 ) 

465 cbar = mpl.colorbar.ColorbarBase( 

466 cbar_ax, 

467 ax=(ax, ax2), 

468 pad=0.01, 

469 cmap=CS1.cmap, 

470 norm=norm, 

471 boundaries=tick_levels, 

472 ticks=tick_levels, 

473 spacing="proportional", 

474 ) 

475 else: 

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

477 else: 

478 fig.subplots_adjust(right=0.8) 

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

480 if cfill == False: 

481 # Make colorbar 

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

483 tick_levels = np.linspace( 

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

485 ) 

486 cbar = mpl.colorbar.ColorbarBase( 

487 cbar_ax, 

488 cmap=CS1.cmap, 

489 norm=norm, 

490 boundaries=tick_levels, 

491 ticks=tick_levels, 

492 spacing="proportional", 

493 ) 

494 else: 

495 # Make colorbar 

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

497 

498 cbar.set_label(r"SNR") 

499 cbar.ax.set_yticklabels( 

500 [ 

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

502 for x in print_logLevels 

503 ], 

504 **yticklabels_kwargs 

505 ) 

506 

507 if display: 

508 # fig.tight_layout() 

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

510 plt.show() 

511 

512 if return_plt: 

513 return fig, ax 

514 

515 

516def Get_Axes_Labels( 

517 ax, 

518 var_axis, 

519 var, 

520 var_scale, 

521 orig_labels, 

522 line_val, 

523 label_kwargs, 

524 tick_label_kwargs, 

525 line_kwargs, 

526): 

527 """Gives paper plot labels for given axis 

528 

529 Parameters 

530 ---------- 

531 ax: object 

532 The current axes object 

533 var_axis: str 

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

535 var: str 

536 The variable to label 

537 orig_labels: list,np.ndarray 

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

539 line_val: int,float 

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

541 label_kwargs: dict 

542 The dictionary adjusting the particular axis' label kwargs 

543 tick_label_kwargs: dict 

544 The dictionary adjusting the particular axis' tick label kwargs 

545 line_kwargs: dict 

546 The dictionary associated with the line displayed on var_axis 

547 

548 """ 

549 

550 # Set axes labels and whether log or linearly spaced 

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

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

553 

554 ax_dict = {} 

555 

556 if var == "M": 

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

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

559 ax_dict[var_axis + "ticklabels"] = [ 

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

561 for x in np.log10(orig_labels) 

562 ] 

563 elif var == "q": 

564 new_labels = orig_labels[::2] 

565 ax_dict[var_axis + "ticks"] = new_labels 

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

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

568 elif var == "z": 

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

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

571 ax_dict[var_axis + "ticklabels"] = [ 

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

573 ] 

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

575 new_labels = ( 

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

577 / 10 

578 ) 

579 new_labels = new_labels[::2] 

580 ax_dict[var_axis + "ticks"] = new_labels 

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

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

583 elif var == "L": 

584 # Proposed Value = 2.5Gm: L3 LISA 

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

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

587 ax_dict[var_axis + "ticklabels"] = [ 

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

589 for x in np.log10(orig_labels) 

590 ] 

591 elif var == "A_acc": 

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

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

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

595 ax_dict[var_axis + "ticklabels"] = [ 

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

597 ] 

598 elif var == "A_IFO": 

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

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

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

602 ax_dict[var_axis + "ticklabels"] = [ 

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

604 ] 

605 elif var == "f_acc_break_low": 

606 # Proposed Value = 0.4mHz: L3 LISA 

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

608 new_labels = ( 

609 np.arange( 

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

611 ) 

612 * scale 

613 ) 

614 ax_dict[var_axis + "ticks"] = new_labels 

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

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

617 elif var == "f_acc_break_high": 

618 # Proposed Value = 8mHz: L3 LISA 

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

620 new_labels = ( 

621 np.arange( 

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

623 ) 

624 * scale 

625 ) 

626 ax_dict[var_axis + "ticks"] = new_labels 

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

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

629 elif var == "f_IFO_break": 

630 # Proposed Value = 2mHz: L3 LISA 

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

632 new_labels = ( 

633 np.arange( 

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

635 ) 

636 * scale 

637 ) 

638 ax_dict[var_axis + "ticks"] = new_labels 

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

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

641 elif var == "n_p": 

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

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

644 new_labels = orig_labels[::sample_rate] 

645 ax_dict[var_axis + "ticks"] = new_labels 

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

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

648 elif var == "cadence": 

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

650 ax_dict[var_axis + "ticks"] = new_labels 

651 ax_dict[ 

652 var_axis + "label" 

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

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

655 elif var == "sigma": 

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

657 new_labels = ( 

658 np.arange( 

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

660 ) 

661 * scale 

662 ) 

663 ax_dict[var_axis + "ticks"] = new_labels 

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

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

666 elif var == "T_obs": 

667 new_labels = orig_labels[::2] 

668 ax_dict[var_axis + "ticks"] = new_labels 

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

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

671 elif var == "Infrastructure Length": 

672 # Proposed Value = 3995: aLIGO, Voyager 

673 # Proposed Value = 40000: CE1 

674 if var_scale == "log": 

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

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

677 ax_dict[var_axis + "ticklabels"] = [ 

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

679 for y in np.log10(orig_labels) 

680 ] 

681 elif var_scale == "lin": 

682 ax_dict[var_axis + "ticks"] = orig_labels 

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

684 ax_dict[var_axis + "ticklabels"] = [ 

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

686 ] 

687 elif var == "Laser Power": 

688 # Proposed Value = 125: aLIGO 

689 # Proposed Value = 145: Voyager 

690 # Proposed Value = 150: CE1 

691 if var_scale == "log": 

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

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

694 ax_dict[var_axis + "ticklabels"] = [ 

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

696 for x in np.log10(orig_labels) 

697 ] 

698 elif var_scale == "lin": 

699 ax_dict[var_axis + "ticks"] = orig_labels 

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

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

702 elif var == "Seismic Gamma": 

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

704 if var_scale == "log": 

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

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

707 ax_dict[var_axis + "ticklabels"] = [ 

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

709 for y in np.log10(orig_labels) 

710 ] 

711 elif var_scale == "lin": 

712 ax_dict[var_axis + "ticks"] = orig_labels 

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

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

715 elif var == "Materials Substrate Temp": 

716 # Proposed Value = 295: aLIGO, CE1 

717 # Proposed Value = 123: Voyager 

718 if var_scale == "lin": 

719 ax_dict[var_axis + "ticks"] = orig_labels 

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

721 ax_dict[var_axis + "ticklabels"] = [ 

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

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

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

725 for x in orig_labels 

726 ] 

727 elif var_scale == "log": 

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

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

730 ax_dict[var_axis + "ticklabels"] = [ 

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

732 for y in np.log10(orig_labels) 

733 ] 

734 else: 

735 if var_scale == "lin": 

736 ax_dict[var_axis + "ticks"] = orig_labels 

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

738 ax_dict[var_axis + "ticklabels"] = [ 

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

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

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

742 for x in orig_labels 

743 ] 

744 elif var_scale == "log": 

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

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

747 ax_dict[var_axis + "ticklabels"] = [ 

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

749 for y in np.log10(orig_labels) 

750 ] 

751 if line_val is not None: 

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

753 line_kwargs["linestyle"] = "--" 

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

755 line_kwargs["color"] = "k" 

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

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

758 

759 if var_scale == "log": 

760 if var_axis == "y": 

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

762 elif var_axis == "x": 

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

764 elif var_scale == "lin": 

765 if var_axis == "y": 

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

767 elif var_axis == "x": 

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

769 

770 ax.update(ax_dict) 

771 if label_kwargs: 

772 if var_axis == "y": 

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

774 elif var_axis == "x": 

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

776 

777 if tick_label_kwargs: 

778 if var_axis == "y": 

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

780 elif var_axis == "x": 

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