1""""Panels for DDB files."""
2
3import param
4import panel as pn
5import panel.widgets as pnw
6import bokeh.models.widgets as bkw
7
8from abipy.panels.core import AbipyParameterized
9
10
11class DdbFilePanel(AbipyParameterized):
12    """
13    A panel to analyze a |DdbFile|.
14    Provides widgets to invoke anaddb and visualize the results.
15    """
16    verbose = param.Integer(0, bounds=(0, None), doc="Verbosity Level")
17    mpi_procs = param.Integer(1, bounds=(1, None), doc="Number of MPI processes used in anaddb")
18
19    nqsmall = param.Integer(10, bounds=(1, None), doc="Number of divisions for smallest vector to generate Q-mesh")
20    ndivsm = param.Integer(5, bounds=(1, None), doc="Number of divisions for smallest segment in q-path")
21    lo_to_splitting = param.ObjectSelector(default="automatic", objects=["automatic", True, False])
22    chneut = param.ObjectSelector(default=1, objects=[0, 1, 2], doc="Abinit variable")
23    dipdip = param.ObjectSelector(default=1, objects=[0, 1], doc="Abinit variable")
24    asr = param.ObjectSelector(default=2, objects=[0, 1, 2], doc="Abinit variable")
25    units = param.ObjectSelector(default="eV", objects=["eV", "meV", "Ha", "cm-1", "Thz"], doc="Energy units")
26
27    dos_method = param.ObjectSelector(default="tetra", objects=["tetra", "gaussian"], doc="Integration method for DOS")
28    temp_range = pnw.RangeSlider(name="T-range", start=0.0, end=1000, value=(0.0, 300.0), step=20)
29
30    gamma_ev = param.Number(1e-4, bounds=(1e-20, None), doc="Phonon linewidth in eV")
31    w_range = pnw.RangeSlider(name="Frequency range (eV)", start=0.0, end=1.0,
32                              value=(0.0, 0.1), step=0.001)
33
34    get_epsinf_btn = pnw.Button(name="Compute", button_type='primary')
35    plot_phbands_btn = pnw.Button(name="Plot Bands and DOS", button_type='primary')
36    plot_eps0w_btn = pnw.Button(name="Plot eps0(omega)", button_type='primary')
37
38    plot_vsound_btn = pnw.Button(name="Calculate speed of sound", button_type='primary')
39    plot_check_asr_dipdip_btn = pnw.Button(name="Compute phonons with/wo ASR and DIPDIP", button_type='primary')
40    plot_ifc_btn = pnw.Button(name="Compute IFC(R)", button_type='primary')
41
42    def __init__(self, ddb, **params):
43        super().__init__(**params)
44        self.ddb = ddb
45
46    @param.depends('get_epsinf_btn.clicks')
47    def get_epsinf(self):
48        """Compute eps_infinity and Born effective charges from DDB."""
49        if self.get_epsinf_btn.clicks == 0: return
50
51        epsinf, becs = self.ddb.anaget_epsinf_and_becs(chneut=self.chneut,
52                                                       mpi_procs=self.mpi_procs, verbose=self.verbose)
53
54        gen, inp = self.ddb.anaget_dielectric_tensor_generator(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
55                                                               mpi_procs=self.mpi_procs, verbose=self.verbose,
56                                                               return_input=True)
57
58        eps0 = gen.tensor_at_frequency(w=0, gamma_ev=self.gamma_ev)
59        #eps0 = pnw.DataFrame(eps0.get_dataframe())
60        return pn.Column(epsinf, eps0, becs, pn.pane.HTML(inp._repr_html_()))
61
62    @param.depends('plot_eps0w_btn.clicks')
63    def plot_eps0w(self):
64        """Compute eps0(omega) from DDB and plot the results."""
65        if self.plot_eps0w_btn.clicks == 0: return
66        gen, inp = self.ddb.anaget_dielectric_tensor_generator(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
67                                                               mpi_procs=self.mpi_procs, verbose=self.verbose,
68                                                               return_input=True)
69        ws = self.w_range.value
70        w_max = ws[1]
71        if w_max == 1.0: w_max = None # Will compute w_max in plot routine from ph freqs.
72
73        def p(component, reim):
74            return gen.plot(w_min=ws[0], w_max=w_max, gamma_ev=self.gamma_ev, num=500, component=component,
75                            reim=reim, units=self.units, **self.fig_kwargs)
76
77        # Build grid
78        gspec = pn.GridSpec(sizing_mode='scale_width')
79        gspec[0, 0] = p("diag", "re")
80        gspec[0, 1] = p("diag", "im")
81        gspec[1, 0] = p("offdiag", "re")
82        gspec[1, 1] = p("offdiag", "im")
83        gspec[2, :] = gen.get_oscillator_dataframe(reim="all", tol=1e-6)
84        # Add HTML pane with input.
85        gspec[3, 0] = pn.pane.HTML(inp._repr_html_())
86
87        return gspec
88
89    @param.depends('plot_phbands_btn.clicks')
90    def plot_phbands_and_phdos(self, event=None):
91        """Compute phonon bands and ph-DOSes from DDB and plot the results."""
92        if self.plot_phbands_btn.clicks == 0: return
93        #self.plot_phbands_btn.button_type = "warning"
94
95        #print("Computing phbands")
96        with self.ddb.anaget_phbst_and_phdos_files(
97                nqsmall=self.nqsmall, qppa=None, ndivsm=self.ndivsm,
98                line_density=None, asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
99                dos_method=self.dos_method, lo_to_splitting=self.lo_to_splitting,
100                verbose=self.verbose, mpi_procs=self.mpi_procs, return_input=True) as g:
101
102            phbst_file, phdos_file = g
103            phbands, phdos = phbst_file.phbands, phdos_file.phdos
104            #print("Computing phbands completed")
105
106            # Build grid
107            gspec = pn.GridSpec(sizing_mode='scale_width')
108            gspec[0, 0] = phbands.plot_with_phdos(phdos, units=self.units, **self.fig_kwargs)
109            gspec[0, 1] = phdos_file.plot_pjdos_type(units=self.units, exchange_xy=True, **self.fig_kwargs)
110            gspec[1, 0] = phdos_file.msqd_dos.plot(units=self.units, **self.fig_kwargs)
111            temps = self.temp_range.value
112            gspec[1, 1] = phdos.plot_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, **self.fig_kwargs)
113            #msqd_dos.plot_tensor(**self.fig_kwargs)
114            #self.plot_phbands_btn.button_type = "primary"
115
116            # Add HTML pane with input
117            gspec[2,:] = pn.pane.HTML(g.input._repr_html_())
118
119        return gspec
120
121    @param.depends('plot_vsound_btn.clicks')
122    def plot_vsound(self):
123        """
124        Compute the speed of sound by fitting phonon frequencies
125        along selected directions by linear least-squares fit.
126        """
127        if self.plot_vsound_btn.clicks == 0: return
128        from abipy.dfpt.vsound import SoundVelocity
129        sv = SoundVelocity.from_ddb(self.ddb.filepath, num_points=20, qpt_norm=0.1,
130                                    ignore_neg_freqs=True, asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
131                                    verbose=self.verbose, mpi_procs=self.mpi_procs)
132
133        # Insert results in grid.
134        gspec = pn.GridSpec(sizing_mode='scale_width')
135        gspec[0, :1] = sv.get_dataframe()
136        gspec[1, :1] = sv.plot(**self.fig_kwargs)
137
138        return gspec
139
140    @param.depends('plot_check_asr_dipdip_btn.clicks')
141    def plot_without_asr_dipdip(self):
142        if self.plot_check_asr_dipdip_btn.clicks == 0: return
143
144        # Insert results in grid.
145        gspec = pn.GridSpec(sizing_mode='scale_width')
146
147        asr_plotter = self.ddb.anacompare_asr(asr_list=(0, 2), chneut_list=(1, ), dipdip=1,
148                                              lo_to_splitting=self.lo_to_splitting,
149                                              nqsmall=self.nqsmall, ndivsm=self.ndivsm,
150                                              dos_method=self.dos_method, ngqpt=None,
151                                              verbose=self.verbose, mpi_procs=self.mpi_procs)
152        gspec[0, :1] = asr_plotter.plot(**self.fig_kwargs)
153
154        dipdip_plotter = self.ddb.anacompare_dipdip(chneut_list=(1,), asr=2, lo_to_splitting=self.lo_to_splitting,
155                                                    nqsmall=self.nqsmall, ndivsm=self.ndivsm,
156                                                    dos_method=self.dos_method, ngqpt=None,
157                                                    verbose=self.verbose, mpi_procs=self.mpi_procs)
158        gspec[1, :1] = dipdip_plotter.plot(**self.fig_kwargs)
159        return gspec
160
161    @param.depends('plot_ifc_btn.clicks')
162    def plot_ifc(self):
163        if self.plot_ifc_btn.clicks == 0: return
164
165        ifc = self.ddb.anaget_ifc(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip)
166
167        # Insert results in grid.
168        gspec = pn.GridSpec(sizing_mode='scale_width')
169        gspec[0, :1] = ifc.plot_longitudinal_ifc(title="Longitudinal IFCs", show=False)
170        gspec[1, :1] = ifc.plot_longitudinal_ifc_short_range(title="Longitudinal IFCs short range", show=False)
171        gspec[2, :1] = ifc.plot_longitudinal_ifc_ewald(title="Longitudinal IFCs Ewald", show=False)
172
173        return gspec
174
175    def get_panel(self):
176        """Return tabs with widgets to interact with the DDB file."""
177        tabs = pn.Tabs(); app = tabs.append
178        row = pn.Row(bkw.PreText(text=self.ddb.to_string(verbose=self.verbose), sizing_mode="scale_both"))
179        app(("Summary", row))
180        app(("Ph-bands", pn.Row(
181            pn.Column("# PH-bands options",
182                      *[self.param[k] for k in ("nqsmall", "ndivsm", "asr", "chneut", "dipdip",
183                                                "lo_to_splitting", "dos_method")],
184                      self.temp_range, self.plot_phbands_btn),
185            self.plot_phbands_and_phdos)
186        ))
187        app(("BECs", pn.Row(
188            pn.Column("# Born effective charges options",
189                     *[self.param[k] for k in ("asr", "chneut", "dipdip", "gamma_ev")], self.get_epsinf_btn),
190            self.get_epsinf)
191        ))
192        app(("eps0", pn.Row(
193            pn.Column("# epsilon_0",
194                      *[self.param[k] for k in ("asr", "chneut", "dipdip", "gamma_ev")],
195                      self.w_range, self.plot_eps0w_btn),
196            self.plot_eps0w)
197        ))
198        app(("Speed of sound", pn.Row(
199            pn.Column("# Speed of sound options",
200                      *[self.param[k] for k in ("asr", "chneut", "dipdip")],
201                      self.plot_vsound_btn),
202            self.plot_vsound)
203        ))
204        app(("Check ASR and DIPDIP", pn.Row(
205            pn.Column("# Options",
206                      *[self.param[k] for k in ("nqsmall", "ndivsm", "dos_method")],
207                      self.plot_check_asr_dipdip_btn),
208            self.plot_without_asr_dipdip)
209        ))
210
211        app(("IFCs", pn.Row(
212            pn.Column("# Options",
213                      *[self.param[k] for k in ("asr", "dipdip", "chneut")],
214                      self.plot_ifc_btn),
215            self.plot_ifc)
216        ))
217
218        app(("Global Opts", pn.Column("# Global Options", *[self.param[k] for k in ("units", "mpi_procs", "verbose")])))
219
220        return tabs
221