-
Notifications
You must be signed in to change notification settings - Fork 93
/
mdfviewer.py
368 lines (288 loc) · 12.5 KB
/
mdfviewer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
from __future__ import print_function, division, unicode_literals, absolute_import
import os
import wx
import wx.lib.agw.flatnotebook as fnb
import abipy.gui.awx as awx
from wx.py.shell import Shell
from monty.string import marquee
from abipy.abilab import abiopen
from abipy.electrons.bse import MdfPlotter
from abipy.iotools.visualizer import Visualizer
from abipy.gui.kpoints import KpointsPanel
from abipy.gui import mixins as mix
from abipy.gui.baseviewer import MultiViewerFrame
# TODO Add ebands to MDF.nc
class MdfViewerFrame(MultiViewerFrame, mix.Has_Structure, mix.Has_MultipleEbands, mix.Has_Tools, mix.Has_NetcdfFiles):
#class MdfViewerFrame(MultiViewerFrame, mix.Has_Structure, mix.Has_Tools, mix.Has_NetcdfFiles):
VERSION = "0.1"
HELP_MSG = """Quick help:
Toolbar:
Click the button to plot the spectrum (averaged over the q-points)
Use the combo boxes to select the type of spectrum and the quantity to plot.
Qpoint list:
Right-Click: display popup menu with choices.
Select: plot the spectra for this qpoint.
Also, these key bindings can be used
(For Mac OSX, replace 'Ctrl' with 'Apple'):
Ctrl-Q: quit
"""
@property
def codename(self):
return "MdfViewer"
@property
def active_mdf_file(self):
"""The active MDF file i.e. the MDF associated to the active tab."""
return self.active_tab.mdf_file
@property
def structure(self):
"""`Structure` associated to the active tab."""
return self.active_mdf_file.structure
@property
def ebands(self):
"""`ElectronBands` associated to the active tab."""
return self.active_mdf_file.ebands
@property
def ebands_list(self):
"""List of `ElectronBands`."""
ebands_list = []
for page in range(self.notebook.GetPageCount()):
tab = self.notebook.GetPage(page)
ebands_list.append(tab.mdf_file.ebands)
return ebands_list
@property
def ebands_filepaths(self):
"""
Return a list with the absolute paths of the files
from which the `ElectronBands` have been read.
"""
paths = []
for page in range(self.notebook.GetPageCount()):
tab = self.notebook.GetPage(page)
paths.append(tab.mdf_file.filepath)
return paths
@property
def nc_filepaths(self):
"""String with the absolute paths of the netcdf files."""
paths = []
for page in range(self.notebook.GetPageCount()):
tab = self.notebook.GetPage(page)
paths.append(tab.mdf_file.filepath)
return paths
@property
def mdf_filepaths(self):
"""
Return a list with the absolute paths of the files
from which the `MDF_Files` have been read.
"""
paths = []
for page in range(self.notebook.GetPageCount()):
tab = self.notebook.GetPage(page)
paths.append(tab.mdf_file.filepath)
return paths
@property
def mdf_files_list(self):
"""List of `MDF_Files`."""
mdf_lists = []
for page in range(self.notebook.GetPageCount()):
tab = self.notebook.GetPage(page)
mdf_lists.append(tab.mdf_file)
return mdf_lists
def makeMenu(self):
"""Creates the main menu."""
# Base menu.
menu_bar = super(MdfViewerFrame, self).makeMenu()
# Add Mixin menus.
menu_bar.Append(self.CreateStructureMenu(), "Structure")
#menu_bar.Append(self.CreateEbandsMenu(), "Ebands")
menu_bar.Append(self.CreateMdfMenu(), "Mdf")
menu_bar.Append(self.CreateToolsMenu(), "Tools")
menu_bar.Append(self.CreateNetcdfMenu(), "Netcdf")
# Help menu
help_menu = self.makeHelpMenu()
menu_bar.Append(help_menu, "Help")
self.SetMenuBar(menu_bar)
def CreateMdfMenu(self):
# MDF Menu ID's
self.ID_MDF_PLOT_AVERAGE = wx.NewId()
self.ID_MDF_COMPARE = wx.NewId()
menu = wx.Menu()
menu.Append(self.ID_MDF_PLOT_AVERAGE, "Plot averaged MDF", "Plot the average of the macroscopic dielectric function")
self.Bind(wx.EVT_MENU, self.onPlotAveragedMdf, id=self.ID_MDF_PLOT_AVERAGE)
menu.AppendSeparator()
menu.Append(self.ID_MDF_COMPARE, "Compare MDF", "Compare multiple macroscopic dielectric functions")
self.Bind(wx.EVT_MENU, self.OnMdfCompare, id=self.ID_MDF_COMPARE)
return menu
def onPlotAveragedMdf(self, event):
"""Plot the average of the macroscopic dielectric function"""
mdf_type = self.getMdfType()
cplx_mode = self.getCplxMode()
#print(mdf_type, cplx_mode)
mdf_file = self.active_mdf_file
mdf_file.plot_mdfs(cplx_mode=cplx_mode, mdf_type=mdf_type)
def OnMdfCompare(self, event):
"""Compare multiple averaged macroscopic dielectric functions"""
mdf_type = self.getMdfType()
cplx_mode = self.getCplxMode()
if mdf_type == "ALL":
return awx.showErrorMessage(self, "ALL is not supported by Compare. Please use EXC, RPA, GWRPA")
plotter = MdfPlotter()
for path, mdf_file in zip(self.mdf_filepaths, self.mdf_files_list):
label = os.path.relpath(path)
mdf = mdf_file.get_mdf(mdf_type)
plotter.add_mdf(label, mdf)
plotter.plot(cplx_mode, qpoint=None)
def makeToolBar(self):
"""Creates the toolbar."""
self.toolbar = toolbar = self.CreateToolBar()
toolbar.SetToolBitmapSize(wx.Size(48, 48))
def bitmap(path):
return wx.Bitmap(awx.path_img(path))
artBmp = wx.ArtProvider.GetBitmap
toolbar.AddSimpleTool(wx.ID_OPEN, artBmp(wx.ART_FILE_OPEN, wx.ART_TOOLBAR), "Open")
toolbar.AddSeparator()
# Button to plot the averaged MDD
# TODO: Change icon.
toolbar.AddSimpleTool(self.ID_MDF_PLOT_AVERAGE, bitmap("wave.png"), "Plot averaged MDF")
# Combo box with the list of MDF types
mdf_types = ["ALL", "EXC", "RPA", "GWRPA"]
self.mdftype_cbox = wx.ComboBox(toolbar, id=-1, name='MDF type', choices=mdf_types, value="ALL", style=wx.CB_READONLY)
self.mdftype_cbox.SetToolTipString("Select the type of MDF spectra to plot.")
toolbar.AddControl(control=self.mdftype_cbox, label="MDF Type:")
# Combo box with the list of complex modes
cplx_modes = ["Re-Im", "Re", "Im"]
self.cplx_cbox = wx.ComboBox(toolbar, id=-1, name='COMPLEX mode', choices=cplx_modes, value="Re-Im", style=wx.CB_READONLY)
self.cplx_cbox.SetToolTipString("Select the component of the MDF spectra to plot (real or imaginary part).")
toolbar.AddControl(control=self.cplx_cbox, label="Complex Mode:")
toolbar.Realize()
def addFileTab(self, parent, filepath):
"""Open a file and add a new tab to the notebook."""
mdf_file = abiopen(filepath)
tab = MdfFileTab(self.notebook, mdf_file)
self.notebook.AddPage(tab, os.path.basename(filepath))
# List to events triggered by the popup menu in the qpoints_table.
qpanel = tab.qpoints_panel
self.Bind(qpanel.MYEVT_COMPARE_SPECTRAQ, self.onCompareSpectraQ)
def getMdfType(self):
"""Return the sting with the MDF type selected by the user."""
mdf_type = self.mdftype_cbox.GetStringSelection()
if not mdf_type: mdf_type = "ALL"
return mdf_type
def getCplxMode(self):
"""Return the sting with the complex mode used for plotting the spectra."""
cplx_mode = self.cplx_cbox.GetStringSelection()
if not cplx_mode: cplx_mode = "Re-Im"
return cplx_mode
def onCompareSpectraQ(self, event):
"""
Compare different MDF(q) spectra (provided that we have multiple tabs in the notebook).
This callback is executed when MdfQpointsPanel fires CompareSpectraQEvent.
"""
qpoint = event.qpoint
mdf_type = self.getMdfType()
cplx_mode = self.getCplxMode()
if mdf_type == "ALL":
return awx.showErrorMessage(self, "ALL is not supported by Compare. Please use EXC, RPA, GWRPA")
plotter = MdfPlotter()
# Extract the type of MDF we are interested in
for path, mdf_file in zip(self.mdf_filepaths, self.mdf_files_list):
label = os.path.relpath(path)
mdf = mdf_file.get_mdf(mdf_type)
plotter.add_mdf(label, mdf)
plotter.plot(cplx_mode, qpoint=qpoint)
class MdfQpointsPanel(KpointsPanel):
"""Extend KpointsPanel adding popupmenus"""
# Command event used to signal that the we should compare multiple spectra (fixed q, move along files).
CompareSpectraQEvent, MYEVT_COMPARE_SPECTRAQ = wx.lib.newevent.NewCommandEvent()
def __init__(self, parent, mdf_file, **kwargs):
KpointsPanel.__init__(self, parent, mdf_file.structure, mdf_file.qpoints)
self.parent = parent
self.mdf_file = mdf_file
# Connect the events fired by klist_ctrl.
self.klist_ctrl.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.onQpointActivated)
def makePopupMenu(self):
"""Build the popup menu."""
# Menu of the base class
menu = super(MdfQpointsPanel, self).makePopupMenu()
# Add other options
self.ID_POPUP_MDF_QCOMPARE = wx.NewId()
menu.Append(self.ID_POPUP_MDF_QCOMPARE, "Compare spectra(q)")
# Associate menu/toolbar items with their handlers.
menu_handlers = [
(self.ID_POPUP_MDF_QCOMPARE, self.onCompareSpectraQ),
]
for combo in menu_handlers:
mid, handler = combo[:2]
self.Bind(wx.EVT_MENU, handler, id=mid)
return menu
@property
def viewer_frame(self):
"""The parent frame `MdfViewerFrame`."""
try:
return self._viewer_frame
except AttributeError:
self._viewer_frame = self.getParentWithType(MdfViewerFrame)
return self._viewer_frame
def onQpointActivated(self, event):
"""Plot MDF(q) for the selected q-point."""
qpoint = self.getSelectedKpoint()
if qpoint is None: return
# Get the options from the ViewerFrame.
mdf_type = self.viewer_frame.getMdfType()
cplx_mode = self.viewer_frame.getCplxMode()
#print(qpoint)
self.mdf_file.plot_mdfs(cplx_mode=cplx_mode, mdf_type=mdf_type, qpoint=qpoint)
def onCompareSpectraQ(self, event):
"""
Get the selected q-point and fire CompareSpectraQEvent.
The parent frame will detect the event and will compare the
different MDF spectra (provided that we have multiple tabs in the notebook.
"""
qpoint = self.getSelectedKpoint()
if qpoint is None: return
# Post the event.
event = self.CompareSpectraQEvent(id=-1, qpoint=qpoint)
wx.PostEvent(self.parent, event)
class MdfFileTab(awx.Panel):
"""Tab showing information on a single MDF file."""
def __init__(self, parent, mdf_file, **kwargs):
"""
Args:
parent:
parent window.
mdf_file:
"""
super(MdfFileTab, self).__init__(parent, -1, **kwargs)
self.mdf_file = mdf_file
splitter = wx.SplitterWindow(self, id=-1, style=wx.SP_3D)
splitter.SetSashGravity(0.95)
self.qpoints_panel = MdfQpointsPanel(splitter, mdf_file)
# Add Python shell
msg = "MDF_object is accessible via the mdf_file variable. Use mdf_file.<TAB> to access the list of methods."
msg = marquee(msg, width=len(msg) + 8, mark="#")
msg = "#"*len(msg) + "\n" + msg + "\n" + "#"*len(msg) + "\n"
# FIXME <Error>: CGContextRestoreGState: invalid context 0x0
pyshell = Shell(splitter, introText=msg, locals={"mdf_file": mdf_file})
splitter.SplitHorizontally(self.qpoints_panel, pyshell)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(splitter, 1, wx.EXPAND, 5)
self.SetSizerAndFit(sizer)
@property
def statusbar(self):
return self.viewer_frame.statusbar
@property
def viewer_frame(self):
"""The parent frame `MdfViewerFrame`."""
try:
return self._viewer_frame
except AttributeError:
self._viewer_frame = self.getParentWithType(MdfViewerFrame)
return self._viewer_frame
class MdfViewerApp(awx.App):
pass
def wxapp_mdfviewer(mdf_filepaths):
"""Standalone application."""
app = MdfViewerApp()
frame = MdfViewerFrame(None, filepaths=mdf_filepaths)
app.SetTopWindow(frame)
frame.Show()
return app