Download IPython notebook here. Binder badge.

Using python to interact with sisl’s GUI

This notebook will show you how you can benefit from knowing how to interact with the graphical interface. Some things just make no sense to do with just clicks, but having a dashboard to visualize and layout your automatically-generated plots is great. However there are some things that the GUI can already help you do automatically. You will be a true master in the moment you know how to use sisl plots in pyton, how to use the GUI and how to make the two worlds interact.

But don’t worry, it is not that difficult really to become a true master :)

You will also understand that you can build your own custom sessions to adapt to your needs, boost your productivity and save you hours of work.

Disclaimer: This notebook will be very simple and straightforward.

[1]:
%%html
<! RUN ME PLEASEEEEEE>
<style>
.user {
    background:aliceblue;
    padding:5px 10px;
    margin 0px 10px;
    border-radius: 3px;
    color: darkblue;
    font-style:italic
}
</style>
[2]:
# This is just for convenience to retreive files
import sisl
siesta_files = sisl._environ.get_environ_variable("SISL_FILES_TESTS") / "sisl" / "io" / "siesta"

1. LAUNCHING THE GUI

[3]:
import sisl_gui

sisl_gui.launch()
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
/tmp/ipykernel_3167/2871562012.py in <module>
----> 1 import sisl_gui
      2
      3 sisl_gui.launch()

ModuleNotFoundError: No module named 'sisl_gui'

A browser tab should have opened. Do you see it?

Yeees!

2. UNDERSTANDING HOW THE GUI WORKS

Well, it’s not magic. It uses a structure that you will probably be familiar with already.

There is a session. This session contains tabs. Tabs contain plots.

How can I see that session?

[4]:
session = sisl_gui.get_session()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_3167/1546544291.py in <module>
----> 1 session = sisl_gui.get_session()

NameError: name 'sisl_gui' is not defined

What can I do with it?

Use half screen for the GUI and another half for this notebook and I will show you. Or, you know, use two monitors in case you are living a fancy life.

[5]:
# Let's write the session to a new variable for convenience
session = sisl_gui.get_session()

# Then add a new tab ( run help(session) to see all the available methods )
session.add_tab("Tab added from jupyter")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_3167/3882033278.py in <module>
      1 # Let's write the session to a new variable for convenience
----> 2 session = sisl_gui.get_session()
      3
      4 # Then add a new tab ( run help(session) to see all the available methods )
      5 session.add_tab("Tab added from jupyter")

NameError: name 'sisl_gui' is not defined

Do you see the magic?

No.

Yeah me neither. You have added a new tab, but the GUI still doesn’t know about it. One way of solving this is to refresh the browser with F5.

Lame.

I know. That’s why there are better ways. You can either emit the changes when you are ready:

[6]:
session.emit()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_3167/2393031917.py in <module>
----> 1 session.emit()

NameError: name 'session' is not defined

Or use the magic of autosync:

[7]:
session.autosync.add_tab("This is syncing!")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_3167/3990576989.py in <module>
----> 1 session.autosync.add_tab("This is syncing!")

NameError: name 'session' is not defined

That’s cool, but do I have to write autosync each time?.

No, the autosync property basically returns your session with autosyncing super-powers. So, you can store that and then run your methods happily:

[8]:
# From now on we are going to just the autosynced version of the
# session. We could keep the other one, but we just don't care
# so we will overwrite it
session = session.autosync

# Just delete our useless tabs
session.remove_tab('This is syncing!')
session.remove_tab('Tab added from jupyter')
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_3167/2649324768.py in <module>
      2 # session. We could keep the other one, but we just don't care
      3 # so we will overwrite it
----> 4 session = session.autosync
      5
      6 # Just delete our useless tabs

NameError: name 'session' is not defined

We can now start to add plots.

[9]:
from sisl.viz.plotly import Plot

# Let's start a PDOS plot
plot = Plot(siesta_files / "SrTiO3.PDOS")

session.add_plot(plot, "First tab")
info:0: SislInfo:

The plot has been initialized correctly, but the current settings were not enough to generate the figure.
Error: Could not read or generate data for PdosPlot from any of the possible sources.
Here are the errors for each source:
        - TB trans: TypeError.expected str, bytes or os.PathLike object, not NoneType
        - hamiltonian: TypeError.expected str, bytes or os.PathLike object, not NoneType
        - siesta output: FileNotFoundError.[Errno 2] No such file or directory: '_THIS_DIRECTORY_DOES_NOT_EXIST_/sisl/io/siesta/SrTiO3.PDOS'

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_3167/3810488281.py in <module>
      4 plot = Plot(siesta_files / "SrTiO3.PDOS")
      5
----> 6 session.add_plot(plot, "First tab")

NameError: name 'session' is not defined

Can you see it?

No.

Maybe you don’t have the tab open in the GUI.

Now, you should be able to interact as you wish with it. Since the reference is kept, you are able to run the methods on your notebook variable directly. Like so:

[10]:
plot.autosync.split_DOS(on="species")
# Yes, plots can also autosync after they have been bound to a session
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_3167/3147319848.py in <module>
----> 1 plot.autosync.split_DOS(on="species")
      2 # Yes, plots can also autosync after they have been bound to a session

~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.11.0/sisl/viz/plotly/plot.py in __getattr__(self, key)
    734                 pass
    735
--> 736         raise AttributeError(f"The attribute '{key}' was not found either in the plot, its figure, or in shared attributes.")
    737
    738     def __setattr__(self, key, val):

AttributeError: The attribute 'autosync' was not found either in the plot, its figure, or in shared attributes.

Let’s add two more plots to this tab.

[11]:
bands = Plot(siesta_files / "SrTiO3.bands")
rho = Plot(siesta_files / "SrTiO3.RHO")

for pt in (rho, bands):
    session.add_plot(pt, "First tab")
info:0: SislInfo:

The plot has been initialized correctly, but the current settings were not enough to generate the figure.
Error: Could not read or generate data for BandsPlot from any of the possible sources.
Here are the errors for each source:
        - aiida bands: AttributeError.'NoneType' object has no attribute '_get_bandplot_data'
        - path: ValueError.You need to provide at least 2 points of the path to draw the bands. Please update the 'path' setting. The current path is: []
        - band structure: ValueError.No band structure (k points path) was provided
        - bands file: FileNotFoundError.[Errno 2] No such file or directory: '_THIS_DIRECTORY_DOES_NOT_EXIST_/sisl/io/siesta/SrTiO3.bands'

info:0: SislInfo:

The plot has been initialized correctly, but the current settings were not enough to generate the figure.
Error: Could not read or generate data for GridPlot from any of the possible sources.
Here are the errors for each source:
        - grid file: SileError.rhoSileSiesta(SrTiO3.RHO, base=/home/docs/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.11.0/docs/visualization/plotly/basic-tutorials/_THIS_DIRECTORY_DOES_NOT_EXIST_/sisl/io/siesta).read_grid_size could not read grid sizes. (ierr=2)
        - grid: ValueError.grid was not set

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_3167/2701097848.py in <module>
      3
      4 for pt in (rho, bands):
----> 5     session.add_plot(pt, "First tab")

NameError: name 'session' is not defined

3. CHANGING THE GUI’S LAYOUT

Each tab has a layouts attribute:

[12]:
session.tab("First tab")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_3167/3949681589.py in <module>
----> 1 session.tab("First tab")

NameError: name 'session' is not defined

This layouts attribute is a dictionary stating how plots should be displayed in different screen sizes (e.g. lg is the largest and xxs is the smallest). You can change this very easily using the GUI by resizing and dragging your plots, but if you need the same layout every time it is definitely not ideal.

The layouts structure seems a bit complicated. But don’t worry, usually you just need to set the lg size, because this is how you see it in a computer. Just ignore the other sizes.

As you can see, the lg size is a list of dicts, each dict contains: - w: The width of the plot in columns out of a total of { lg: 12, sm: 6, xs: 4, xxs: 2 } columns. - h: The height of the plot. I don’t really know the scale of this honestly. Just try to see what fits best for you. - x: The position along the x axis of the left side of the plot (in columns). - y: The position along the y axis of the top part of the plot. - id: The ID of the plot to which this properties apply. It is available under plot.id - moved: Whether the plot has been moved. - static: Whether the GUI should prevent users from resizing/dragging this plot.

I’d say the best strategy is probably to set the layout in the GUI and then copy it to build your automatic layouts. In my case, I like how this one looks:

[{'w': 6, 'h': 28, 'x': 0, 'y': 0,
    'i': '26c94e23-9f6b-4bce-b0a4-3363e69f0fd3',
    'moved': False,
    'static': False},
   {'w': 12, 'h': 14, 'x': 0, 'y': 28,
    'i': '0a2f9502-3fbb-4c06-a402-8d46518d3bc3',
    'moved': False,
    'static': False},
   {'w': 6, 'h': 28, 'x': 6, 'y': 0,
    'i': 'f6ae7c4a-38c6-4d36-81cb-04c73a794555',
    'moved': False,
    'static': False}]

So I’m just going to use it automating the ID stuff:

[13]:
struct = "SrTiO3"
tab_name = "Layout attempt"

session.add_tab(tab_name)

# Get all the plots
plots = [Plot(siesta_files / f'{struct}.{ext}') for ext in ("PDOS", "RHO", "bands")]

for plot in plots:
    session.add_plot(plot, tab_name)

# This is how you update tab parameters
# (in this case, we want to update the layouts parameter)
session.update_tab(
    tab_name,
    layouts = {'lg': [
        {'w': 6, 'h': 28, 'x': 0, 'y': 0, 'i': plots[0].id, 'static': False},
        {'w': 12, 'h': 14, 'x': 0, 'y': 28, 'i': plots[1].id, 'static': False},
        {'w': 6, 'h': 28, 'x': 6, 'y': 0, 'i': plots[2].id, 'static': False}
    ]}
)

# And also I'd like the PDOS to be splitted on species
plots[0].split_DOS(on="species").update_settings(Erange=[-10,10]).emit()
# And the bands to show the gap
plots[2].update_settings(gap=True, Erange=[-10,10]).emit()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_3167/2797782125.py in <module>
      2 tab_name = "Layout attempt"
      3
----> 4 session.add_tab(tab_name)
      5
      6 # Get all the plots

NameError: name 'session' is not defined

You can check the “Layout attempt” tab to see that we really achieved what we wanted.

4. BUILDING YOUR OWN CUSTOM SESSIONS

Well, at this point there is not much mistery to it. Specially if you have gone through the guide on how to build plots You just need to extend the Session class. Session, as Plot, inherits from Configurable, so you have exactly the same management of settings.

If you have not read the guide for plots, don’t worry. The basics to build a session class are very simple.

Let’s build a session using what we wrote in the previous section. We really just need to change the session variable for self and we have a method!

[14]:
# Import the parent class
from sisl.viz.plotly import Session

# Start building our session
class VeryCoolSession(Session):

    # Add the method to see PDOS, bands and RHO of a structure
    def see_results(self, struct, wdir=siesta_files, tab_name=None):

        if tab_name is None:
            tab_name = struct

        self.add_tab(tab_name)

        plots = [Plot(wdir /  f'{struct}.{ext}') for ext in ("PDOS", "RHO", "bands")]

        for plot in plots:
            self.add_plot(plot, tab_name)

        # This is how you update tab parameters
        self.update_tab(
            tab_name,
            layouts = {'lg': [
                {'w': 6, 'h': 28, 'x': 0, 'y': 0, 'i': plots[0].id, 'static': False},
                {'w': 12, 'h': 14, 'x': 0, 'y': 28, 'i': plots[1].id, 'static': False},
                {'w': 6, 'h': 28, 'x': 6, 'y': 0, 'i': plots[2].id, 'static': False}
            ]}
        )

        # And also I'd like the PDOS to be splitted ¿on species
        plots[0].split_DOS(on="species").update_settings(Erange=[-10,10])
        # And the bands to show the gap
        plots[2].update_settings(gap=True, Erange=[-10,10])

And that’s basically it.

Yeah nice but how do I use this session now

You just need to set the session of the GUI:

[15]:
# Maybe you want to save the existing one?
# session.save("My first try.session")

# We initialize the session
new_session = VeryCoolSession()
# And then we just tell the GUI to use it
sisl_gui.set_session(new_session)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_3167/4189676402.py in <module>
      5 new_session = VeryCoolSession()
      6 # And then we just tell the GUI to use it
----> 7 sisl_gui.set_session(new_session)

NameError: name 'sisl_gui' is not defined

Now, let’s see if this works:

[16]:
new_session.autosync.see_results("SrTiO3")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_3167/3914800996.py in <module>
----> 1 new_session.autosync.see_results("SrTiO3")

AttributeError: 'VeryCoolSession' object has no attribute 'autosync'

Just take a moment now to admire the simplicity of the previous line and the beauty and usefulness that it can bring to your research.

With this, we end this short but intense tutorial. I hope that I have been able to convince you of the power this gives to you, but if not, thanks for the read anyway :)

Just as a last thing, notice that you can add parameters to the session and they can be tweaked from the GUI. Give a glimpse at the notebook where plot building is explained if you are further interested, there’s a section where parameters are introduced, close to the beggining.

Oh, and by the way, you can load saved sessions just as we did with plots in the Demo Notebook

If there is a request for further tutorials on sessions, we will extend more on the topic, so don’t hesitate to ask.

Cheers!