1# coding: utf-8 2""" 3Objects to read and analyze optical properties stored in the optic.nc file produced by optic executable. 4""" 5import numpy as np 6import abipy.core.abinit_units as abu 7 8from collections import OrderedDict 9from monty.string import marquee, list_strings 10from monty.functools import lazy_property 11from abipy.core.mixins import AbinitNcFile, Has_Header, Has_Structure, Has_ElectronBands, NotebookWriter 12from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt, get_axarray_fig_plt, set_axlims, data_from_cplx_mode 13from abipy.abio.robots import Robot 14from abipy.electrons.ebands import ElectronsReader, RobotWithEbands 15 16ALL_CHIS = OrderedDict([ 17 ("linopt", { 18 "longname": "Dielectric function", 19 "rank": 2, 20 #"terms": 21 #"latex": r"\chi(\omega)" 22 } 23 ), 24 ("shg", { 25 "longname": "Second Harmonic Generation", 26 "rank": 3, 27 "terms": ["shg_inter2w", "shg_inter1w", "shg_intra2w", 28 "shg_intra1w", "shg_intra1wS", "shg_chi2tot"], 29 } 30 #"latex": r"\chi(-2\omega, \omega, \omega)" 31 ), 32 ("leo", { 33 "longname": "Linear Electro-optic", 34 "rank": 3, 35 "terms": ["leo_chi", "leo_eta", "leo_sigma", "leo_chi2tot"], 36 } 37 #"latex": r"\chi(-\omega, \omega, 0)" 38 ) 39]) 40 41#LEO2_TERMS = OrderedDict([ 42# ("leo2_chiw", None), 43# ("leo2_etaw", None), 44# ("leo2_chi2w", None), 45# ("leo2_eta2w", None), 46# ("leo2_sigmaw", None), 47# ("leo2_chi2tot", None), 48#]) 49 50 51##################################### 52# Helper functions for linear optic # 53##################################### 54 55def reflectivity(eps): 56 """Reflectivity(w) from vacuum, at normal incidence""" 57 return np.sqrt(0.5 * (np.abs(eps) + eps.real)) 58 59 60#def abs_coeff(eps): 61# """absorption coeff (in m-1) = omega Im(eps) / c n(eps)""" 62# if (abs(eps(iw)) + dble(eps(iw)) > zero) then 63# tmpabs = aimag(eps(iw))*ene / sqrt(half*( abs(eps(iw)) + dble(eps(iw)) )) / Sp_Lt / Bohr_meter 64# end if 65 66 67def kappa(eps): 68 """Im(refractive index(w)) aka kappa""" 69 return np.sqrt(0.5 * (np.abs(eps) - eps.real)) 70 71 72def n(eps): 73 """Re(refractive index(w)) aka n""" 74 return np.sqrt(0.5 * (np.abs(eps) + eps.real)) 75 76 77#def eels(eps): 78# np.where(np.abs(eps) 79# return - (1 / eps).imag 80 81 82LINEPS_WHAT2EFUNC = dict( 83 n=n, 84 reflectivity=reflectivity, 85 kappa=kappa, 86 re=lambda eps: eps.real, 87 im=lambda eps: eps.imag, 88 #abs: lambda: eps: np.abs(eps), 89 #angle: lambda: eps: np.angle(eps, deg=False), 90 #abs_coeff=abs_coeff 91 #eels=lambda: eps / 92) 93 94 95class OpticNcFile(AbinitNcFile, Has_Header, Has_Structure, Has_ElectronBands, NotebookWriter): 96 """ 97 This file contains the results produced by optic. Provides methods to plot optical 98 properties and susceptibilty tensors. Used by :class:`OpticRobot` to analyze multiple files. 99 100 Usage example: 101 102 .. code-block:: python 103 104 with OpticNcFile("out_optic.nc") as optic: 105 optic.ebands.plot() 106 optic.plot() 107 108 .. rubric:: Inheritance Diagram 109 .. inheritance-diagram:: OpticNcFile 110 """ 111 112 @classmethod 113 def from_file(cls, filepath): 114 """Initialize the object from a netcdf file.""" 115 return cls(filepath) 116 117 def __init__(self, filepath): 118 super().__init__(filepath) 119 self.reader = OpticReader(filepath) 120 121 # Read optic input variables and info on k-point sampling and store them in self. 122 keys = [ 123 "kptopt", 124 # optic input variables 125 "broadening", "maxomega", "domega", "scissor", "tolerance", "do_antiresonant", "do_ep_renorm", 126 ] 127 for key in keys: 128 setattr(self, key, self.reader.read_value(key)) 129 130 @lazy_property 131 def wmesh(self): 132 """ 133 Frequency mesh in eV. Note that the same frequency-mesh is used 134 for the different optical properties. 135 """ 136 return self.reader.read_value("wmesh") 137 138 def __str__(self): 139 """String representation.""" 140 return self.to_string() 141 142 def to_string(self, verbose=0): 143 """String representation.""" 144 lines = []; app = lines.append 145 146 app(marquee("File Info", mark="=")) 147 app(self.filestat(as_string=True)) 148 app("") 149 app(self.structure.to_string(verbose=verbose, title="Structure")) 150 app("") 151 app(self.ebands.to_string(with_structure=False, title="Electronic Bands")) 152 153 app(marquee("Optic calculation", mark="=")) 154 # Show Optic variables. 155 app("broadening: %s [Ha], %.3f (eV)" % (self.broadening, self.broadening * abu.Ha_eV)) 156 app("scissor: %s [Ha], %.3f (eV)" % (self.scissor, self.scissor * abu.Ha_eV)) 157 app("tolerance: %s [Ha], %.3f (eV)" % (self.tolerance, self.tolerance * abu.Ha_eV)) 158 app("maxomega: %s [Ha], %.3f (eV)" % (self.maxomega, self.maxomega * abu.Ha_eV)) 159 app("domega: %s [Ha], %.3f (eV)" % (self.domega, self.domega * abu.Ha_eV)) 160 app("do_antiresonant %s, do_ep_renorm %s" % (self.do_antiresonant, self.do_ep_renorm)) 161 app("Number of temperatures: %d" % self.reader.ntemp) 162 163 # Show available tensors and computed components. 164 for key, info in ALL_CHIS.items(): 165 if not self.reader.computed_components[key]: continue 166 app("%s components computed: %s" % ( 167 info["longname"], ", ".join(self.reader.computed_components[key]))) 168 169 if verbose > 1: 170 app(marquee("Abinit Header", mark="=")) 171 app(self.hdr.to_string(verbose=verbose)) 172 173 return "\n".join(lines) 174 175 @lazy_property 176 def ebands(self): 177 """|ElectronBands| object.""" 178 return self.reader.read_ebands() 179 180 @property 181 def structure(self): 182 """|Structure| object.""" 183 return self.ebands.structure 184 185 @lazy_property 186 def has_linopt(self): 187 """True if the ncfile contains Second Harmonic Generation tensor.""" 188 return "linopt" in self.reader.computed_components 189 190 @lazy_property 191 def has_shg(self): 192 """True if the ncfile contains Second Harmonic Generation tensor.""" 193 return "shg" in self.reader.computed_components 194 195 @lazy_property 196 def has_leo(self): 197 """True if the ncfile contains the Linear Electro-optic tensor""" 198 return "leo" in self.reader.computed_components 199 200 #@lazy_property 201 #def xc(self): 202 # """:class:`XcFunc object with info on the exchange-correlation functional.""" 203 # return self.reader.read_abinit_xcfunc() 204 205 def close(self): 206 """Close the file.""" 207 self.reader.close() 208 209 @lazy_property 210 def params(self): 211 """:class:`OrderedDict` with parameters that might be subject to convergence studies.""" 212 od = self.get_ebands_params() 213 return od 214 215 @staticmethod 216 def get_linopt_latex_label(what, comp): 217 """ 218 Return latex label for linear-optic quantities. Used in plots. 219 """ 220 return dict( 221 n=r"$n_{%s}$" % comp, 222 reflectivity=r"$R_{%s}$" % comp, 223 kappa=r"$\kappa_{%s}$" % comp, 224 re=r"$\Re(\epsilon_{%s})$" % comp, 225 im=r"$\Im(\epsilon_{%s})$" % comp, 226 #abs=r"$|\epsilon_{%s}|$" % comp, 227 #abs_coeff=abs_coeff_{%s}} % comp, 228 #eels:r"EELS_{%s}" % comp, 229 )[what] 230 231 def get_chi2_latex_label(self, key, what, comp): 232 """ 233 Build latex label for chi2-related quantities. Used in plots. 234 """ 235 symb = "{%s}" % key.capitalize() 236 return dict( 237 re=r"$\Re(%s_{%s})$" % (symb, comp), 238 im=r"$\Im(%s_{%s})$" % (symb, comp), 239 abs=r"$|%s_{%s}|$" % (symb, comp), 240 )[what] 241 242 @add_fig_kwargs 243 def plot_linear_epsilon(self, components="all", what="im", itemp=0, 244 ax=None, xlims=None, with_xlabel=True, label=None, fontsize=12, **kwargs): 245 """ 246 Plot components of the linear dielectric function. 247 248 Args: 249 components: List of cartesian tensor components to plot e.g. ["xx", "xy"]. 250 "all" if all components available on file should be plotted on the same ax. 251 what: quantity to plot. "re" for real part, "im" for imaginary. Accepts also "abs", "angle". 252 itemp: Temperature index. 253 ax: |matplotlib-Axes| or None if a new figure should be created. 254 xlims: Set the data limits for the x-axis. Accept tuple e.g. ``(left, right)`` 255 or scalar e.g. ``left``. If left (right) is None, default values are used. 256 with_xlabel: True if x-label should be added. 257 label: True to add legend label to each curve. 258 fontsize: Legend and label fontsize. 259 260 Returns: |matplotlib-Figure| 261 """ 262 comp2eps = self.reader.read_lineps(components, itemp=itemp) 263 264 ax, fig, plt = get_ax_fig_plt(ax=ax) 265 for comp, eps in comp2eps.items(): 266 values = LINEPS_WHAT2EFUNC[what](eps) 267 # Note: I'm skipping the first point at w=0 because optic does not compute it! 268 # The same trick is used in the other plots. 269 ax.plot(self.wmesh[1:], values[1:], 270 label=self.get_linopt_latex_label(what, comp) if label is None else label) 271 272 ax.grid(True) 273 if with_xlabel: ax.set_xlabel('Photon Energy (eV)') 274 set_axlims(ax, xlims, "x") 275 ax.legend(loc="best", fontsize=fontsize, shadow=True) 276 277 return fig 278 279 @add_fig_kwargs 280 def plot_linopt(self, select="all", itemp=0, xlims=None, **kwargs): 281 """ 282 Subplots with all linear optic quantities selected by ``select`` at temperature ``itemp``. 283 284 Args: 285 select: 286 itemp: Temperature index. 287 xlims: Set the data limits for the x-axis. Accept tuple e.g. ``(left, right)`` 288 or scalar e.g. ``left``. If left (right) is None, default values are used. 289 290 Returns: |matplotlib-Figure| 291 """ 292 key = "linopt" 293 if not self.reader.computed_components[key]: return None 294 if select == "all": select = list(LINEPS_WHAT2EFUNC.keys()) 295 select = list_strings(select) 296 297 nrows, ncols = len(select), 1 298 ax_mat, fig, plt = get_axarray_fig_plt(None, nrows=nrows, ncols=ncols, 299 sharex=True, sharey=False, squeeze=True) 300 301 components = self.reader.computed_components[key] 302 for i, (what, ax) in enumerate(zip(select, ax_mat)): 303 self.plot_linear_epsilon(what=what, itemp=itemp, components=components, 304 ax=ax, xlims=xlims, with_xlabel=(i == len(select) - 1), 305 show=False) 306 return fig 307 308 @add_fig_kwargs 309 def plot_chi2(self, key, components="all", what="abs", itemp=0, decompose=False, 310 ax=None, xlims=None, with_xlabel=True, label=None, fontsize=12, **kwargs): 311 """ 312 Low-level function to plot chi2 tensor. 313 314 Args: 315 key: 316 components: List of cartesian tensor components to plot e.g. ["xxx", "xyz"]. 317 "all" if all components available on file should be plotted on the same ax. 318 what: quantity to plot. "re" for real part, "im" for imaginary, Accepts also "abs", "angle". 319 itemp: Temperature index. 320 decompose: True to plot individual contributions. 321 ax: |matplotlib-Axes| or None if a new figure should be created. 322 xlims: Set the data limits for the x-axis. Accept tuple e.g. ``(left, right)`` 323 or scalar e.g. ``left``. If left (right) is None, default values are used. 324 with_xlabel: True to add x-label. 325 label: True to add legend label to each curve. 326 fontsize: Legend and label fontsize. 327 328 Returns: |matplotlib-Figure| 329 """ 330 if not self.reader.computed_components[key]: return None 331 comp2terms = self.reader.read_tensor3_terms(key, components, itemp=itemp) 332 333 ax, fig, plt = get_ax_fig_plt(ax=ax) 334 for comp, terms in comp2terms.items(): 335 for name, values in terms.items(): 336 if not decompose and not name.endswith("tot"): continue 337 values = data_from_cplx_mode(what, values) 338 ax.plot(self.wmesh[1:], values[1:], 339 label=self.get_chi2_latex_label(key, what, comp) if label is None else label, 340 ) 341 342 ax.grid(True) 343 if with_xlabel: ax.set_xlabel('Photon Energy (eV)') 344 set_axlims(ax, xlims, "x") 345 ax.legend(loc="best", fontsize=fontsize, shadow=True) 346 347 return fig 348 349 @add_fig_kwargs 350 def plot_shg(self, **kwargs): 351 """Plot Second Harmonic Generation. See plot_chi2 for args supported.""" 352 return self.plot_chi2(key="shg", show=False, **kwargs) 353 354 @add_fig_kwargs 355 def plot_leo(self, **kwargs): 356 """Plot Linear Electro-optic. See plot_chi2 for args supported.""" 357 return self.plot_chi2(key="leo", show=False, **kwargs) 358 359 def yield_figs(self, **kwargs): # pragma: no cover 360 """ 361 This function *generates* a predefined list of matplotlib figures with minimal input from the user. 362 Used in abiview.py to get a quick look at the results. 363 """ 364 if self.has_linopt: 365 yield self.plot_linear_epsilon(what="re", show=False) 366 yield self.plot_linear_epsilon(what="im", show=False) 367 yield self.plot_linopt(show=False) 368 if self.has_shg: 369 yield self.plot_shg(show=False) 370 if self.has_leo: 371 yield self.plot_leo(show=False) 372 373 def write_notebook(self, nbpath=None): 374 """ 375 Write a jupyter_ notebook to ``nbpath``. If nbpath is None, a temporay file in the current 376 working directory is created. Return path to the notebook. 377 """ 378 nbformat, nbv, nb = self.get_nbformat_nbv_nb(title=None) 379 380 nb.cells.extend([ 381 nbv.new_code_cell("optic = abilab.abiopen('%s')" % self.filepath), 382 nbv.new_code_cell("print(optic)"), 383 nbv.new_code_cell("optic.ebands.plot();"), 384 ]) 385 386 # Add plot calls if quantities have been computed. 387 for key, info in ALL_CHIS.items(): 388 if not self.reader.computed_components[key]: continue 389 pycall = "optic.plot_%s();" % key 390 nb.cells.extend([ 391 nbv.new_code_cell(pycall), 392 ]) 393 394 return self._write_nb_nbpath(nb, nbpath) 395 396 397class OpticReader(ElectronsReader): 398 """ 399 This object reads the results stored in the optic.nc file 400 It provides helper function to access the most important quantities. 401 402 .. rubric:: Inheritance Diagram 403 .. inheritance-diagram:: OpticReader 404 """ 405 def __init__(self, filepath): 406 super().__init__(filepath) 407 self.ntemp = self.read_dimvalue("ntemp") 408 409 self.computed_components = OrderedDict() 410 self.computed_ids = OrderedDict() 411 for chiname, info in ALL_CHIS.items(): 412 comp_name = chiname + "_components" 413 if comp_name not in self.rootgrp.variables: 414 fi_comps, ids = [], [] 415 else: 416 fi_comps = [str(i) for i in self.read_value(comp_name)] 417 if info["rank"] == 2: 418 ids = [(int(i[0])-1, int(i[1])-1) for i in fi_comps] 419 elif info["rank"] == 3: 420 ids = [(int(i[0])-1, int(i[1])-1, int(i[2])-1) for i in fi_comps] 421 else: 422 raise NotImplementedError("rank %s" % info["rank"]) 423 424 self.computed_ids[chiname] = ids 425 self.computed_components[chiname] = [abu.itup2s(it) for it in ids] 426 427 def read_lineps(self, components, itemp=0): 428 """ 429 Args: 430 components: List of cartesian tensor components to plot e.g. ["xx", "xy"]. 431 "all" if all components available on file should be plotted on the same ax. 432 itemp: Temperature index. 433 """ 434 # linopt_epsilon has *Fortran* shape [two, nomega, num_comp, ntemp] 435 key = "linopt" 436 if components == "all": components = self.computed_components[key] 437 if not (self.ntemp > itemp >= 0): 438 raise ValueError("Invalid itemp: %s, ntemp: %s" % (itemp, self.ntemp)) 439 440 var = self.read_variable("linopt_epsilon") 441 od = OrderedDict() 442 for comp in list_strings(components): 443 try: 444 ijp = self.computed_components[key].index(comp) 445 except ValueError: 446 raise ValueError("epsilon_component %s was not computed" % comp) 447 448 values = var[itemp, ijp] 449 od[comp] = values[:, 0] + 1j * values[:, 1] 450 return od 451 452 def read_tensor3_terms(self, key, components, itemp=0): 453 """ 454 Args: 455 key: Name of the netcdf variable to read. 456 components: List of cartesian tensor components to plot e.g. ["xxx", "xyz"]. 457 "all" if all components available on file should be plotted on the same ax. 458 itemp: Temperature index. 459 460 Return: 461 :class:`OrderedDict` mapping cartesian components e.g. "xyz" to data dictionary. 462 Individual entries are listed in ALL_CHIS[key]["terms"] 463 """ 464 # arrays have Fortran shape [two, nomega, num_comp, ntemp] 465 if components == "all": components = self.computed_components[key] 466 components = list_strings(components) 467 if not (self.ntemp > itemp >= 0): 468 raise ValueError("Invalid itemp: %s, ntemp: %s" % (itemp, self.ntemp)) 469 470 od = OrderedDict([(comp, OrderedDict()) for comp in components]) 471 for chiname in ALL_CHIS[key]["terms"]: 472 #print("About to read:", chiname) 473 var = self.read_variable(chiname) 474 for comp in components: 475 try: 476 ijkp = self.computed_components[key].index(comp) 477 except ValueError: 478 raise ValueError("%s component %s was not computed" % (key, comp)) 479 values = var[itemp, ijkp] 480 od[comp][chiname] = values[:, 0] + 1j * values[:, 1] 481 return od 482 483 484class OpticRobot(Robot, RobotWithEbands): 485 """ 486 This robot analyzes the results contained in multiple optic.nc files. 487 488 .. rubric:: Inheritance Diagram 489 .. inheritance-diagram:: OpticRobot 490 """ 491 EXT = "OPTIC" 492 493 @lazy_property 494 def computed_components_intersection(self): 495 """ 496 Dictionary with the list of cartesian tensor components 497 available in each file. Use keys from ALL_CHIS. 498 """ 499 od = OrderedDict() 500 for ncfile in self.abifiles: 501 for chiname in ALL_CHIS: 502 comps = ncfile.reader.computed_components[chiname] 503 if chiname not in od: 504 od[chiname] = comps 505 else: 506 # Build intersection while preserving order. 507 od[chiname] = self.ordered_intersection(od[chiname], comps) 508 return od 509 510 @add_fig_kwargs 511 def plot_linopt_convergence(self, components="all", what_list=("re", "im"), 512 sortby="nkpt", itemp=0, xlims=None, **kwargs): 513 """ 514 Plot the convergence of the dielectric function tensor with respect to 515 parameter defined by ``sortby``. 516 517 Args: 518 components: List of cartesian tensor components to plot e.g. ["xx", "xy"]. 519 "all" if all components available on file should be plotted on the same ax. 520 what_list: List of quantities to plot. "re" for real part, "im" for imaginary. 521 Accepts also "abs", "angle". 522 sortby: Define the convergence parameter, sort files and produce plot labels. Can be None, string or function. 523 If None, no sorting is performed. 524 If string, it's assumed that the ncfile has an attribute with the same name and getattr is invoked. 525 If callable, the output of callable(ncfile) is used. 526 itemp: Temperature index. 527 xlims: Set the data limits for the x-axis. Accept tuple e.g. ``(left, right)`` 528 or scalar e.g. ``left``. If left (right) is None, default values are used. 529 530 Returns: |matplotlib-Figure| 531 """ 532 # Build grid plot: computed tensors along the rows, what_list along columns. 533 key = "linopt" 534 components = self.computed_components_intersection[key] 535 536 nrows, ncols = len(components), len(what_list) 537 ax_mat, fig, plt = get_axarray_fig_plt(None, nrows=nrows, ncols=ncols, 538 sharex=True, sharey=False, squeeze=False) 539 540 label_ncfile_param = self.sortby(sortby) 541 for i, comp in enumerate(components): 542 for j, what in enumerate(what_list): 543 ax = ax_mat[i, j] 544 for ifile, (label, ncfile, param) in enumerate(label_ncfile_param): 545 546 ncfile.plot_linear_epsilon(components=comp, what=what, itemp=itemp, ax=ax, 547 xlims=xlims, with_xlabel=(i == len(components) - 1), 548 label="%s %s" % (sortby, param) if not callable(sortby) else str(param), 549 show=False) 550 551 if ifile == 0: 552 ax.set_title(ncfile.get_linopt_latex_label(what, comp)) 553 554 if (i, j) != (0, 0): 555 ax.legend().set_visible(False) 556 557 return fig 558 559 @add_fig_kwargs 560 def plot_shg_convergence(self, **kwargs): 561 """Plot Second Harmonic Generation. See plot_convergence_rank3 for args supported.""" 562 if "what_list" not in kwargs: kwargs["what_list"] = ["abs"] 563 return self.plot_convergence_rank3(key="shg", **kwargs) 564 565 @add_fig_kwargs 566 def plot_leo_convergence(self, **kwargs): 567 """Plot Linear electron-optic. See plot_convergence_rank3 for args supported.""" 568 if "what_list" not in kwargs: kwargs["what_list"] = ["abs"] 569 return self.plot_convergence_rank3(key="leo", **kwargs) 570 571 @add_fig_kwargs 572 def plot_convergence_rank3(self, key, components="all", itemp=0, what_list=("abs",), 573 sortby="nkpt", decompose=False, xlims=None, **kwargs): 574 """ 575 Plot convergence of arbitrary rank3 tensor. This is a low-level routine used in other plot methods. 576 577 Args: 578 key: Name of the quantity to analyze. 579 components: List of cartesian tensor components to plot e.g. ["xxx", "xyz"]. 580 "all" if all components available on file should be plotted on the same ax. 581 itemp: Temperature index. 582 what_list: List of quantities to plot. "re" for real part, "im" for imaginary. 583 Accepts also "abs", "angle". 584 sortby: Define the convergence parameter, sort files and produce plot labels. Can be None, string or function. 585 If None, no sorting is performed. 586 If string, it's assumed that the ncfile has an attribute with the same name and ``getattr`` is invoked. 587 If callable, the output of callable(ncfile) is used. 588 decompose: True to plot individual contributions. 589 xlims: Set the data limits for the x-axis. Accept tuple e.g. ``(left, right)`` 590 or scalar e.g. ``left``. If left (right) is None, default values are used. 591 592 Returns: |matplotlib-Figure| 593 """ 594 # Build grid plot: computed tensors along the rows, what_list along columns. 595 components = self.computed_components_intersection[key] 596 597 nrows, ncols = len(components), len(what_list) 598 ax_mat, fig, plt = get_axarray_fig_plt(None, nrows=nrows, ncols=ncols, 599 sharex=True, sharey=False, squeeze=False) 600 601 label_ncfile_param = self.sortby(sortby) 602 for i, comp in enumerate(components): 603 for j, what in enumerate(what_list): 604 ax = ax_mat[i, j] 605 for ifile, (label, ncfile, param) in enumerate(label_ncfile_param): 606 607 ncfile.plot_chi2(key=key, components=comp, what=what, itemp=itemp, decompose=decompose, 608 ax=ax, xlims=xlims, with_xlabel=(i == len(components) - 1), 609 label="%s %s" % (sortby, param) if not callable(sortby) else str(param), 610 show=False, **kwargs) 611 612 if ifile == 0: 613 ax.set_title(ncfile.get_chi2_latex_label(key, what, comp)) 614 615 if (i, j) != (0, 0): 616 ax.legend().set_visible(False) 617 618 return fig 619 620 def yield_figs(self, **kwargs): # pragma: no cover 621 """ 622 This function *generates* a predefined list of matplotlib figures with minimal input from the user. 623 Used in abiview.py to get a quick look at the results. 624 """ 625 for key, comps in self.computed_components_intersection.items(): 626 if not comps: continue 627 plot_fig = getattr(self, "plot_%s_convergence" % key) 628 yield plot_fig(show=False) 629 630 def write_notebook(self, nbpath=None): 631 """ 632 Write a jupyter_ notebook to nbpath. If ``nbpath`` is None, a temporay file in the current 633 working directory is created. Return path to the notebook. 634 """ 635 nbformat, nbv, nb = self.get_nbformat_nbv_nb(title=None) 636 637 args = [(l, f.filepath) for l, f in self.items()] 638 nb.cells.extend([ 639 #nbv.new_markdown_cell("# This is a markdown cell"), 640 nbv.new_code_cell("robot = abilab.OpticRobot(*%s)\nrobot.trim_paths()\nrobot" % str(args)), 641 ]) 642 643 for key, comps in self.computed_components_intersection.items(): 644 if not comps: continue 645 pycall = "robot.plot_%s_convergence();" % key 646 nb.cells.extend([ 647 nbv.new_code_cell(pycall), 648 ]) 649 650 # Mixins 651 nb.cells.extend(self.get_baserobot_code_cells()) 652 nb.cells.extend(self.get_ebands_code_cells()) 653 654 return self._write_nb_nbpath(nb, nbpath) 655