Download IPython notebook here. Binder badge.

GitHub issues by-label

PdosPlot

[1]:
import sisl
import sisl.viz
# This is just for convenience to retreive files
siesta_files = sisl._environ.get_environ_variable("SISL_FILES_TESTS") / "sisl" / "io" / "siesta"

We are going to get the PDOS from a SIESTA .PDOS file, but we could get it from a hamiltonian as well.

[2]:
plot = sisl.get_sile(siesta_files / "SrTiO3.PDOS").plot(Erange=[-10,10])
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:
        - siesta output: FileNotFoundError.[Errno 2] No such file or directory: '_THIS_DIRECTORY_DOES_NOT_EXIST_/sisl/io/siesta/SrTiO3.PDOS'
        - TB trans: TypeError.expected str, bytes or os.PathLike object, not NoneType
        - wfsx file: TypeError.expected str, bytes or os.PathLike object, not NoneType
        - hamiltonian: TypeError.expected str, bytes or os.PathLike object, not NoneType

By default, a PDOS plot shows the total density of states:

[3]:
plot
'<sisl.viz.plots.pdos.PdosPlot object at 0x7effa67e9ea0>'

PDOS requests

There’s a very important setting in the PdosPlot: requests. This setting expects a list of PDOS requests, where each request is a dictionary that can specify - species - atoms - orbitals (the orbital name) - n, l, m (the quantum numbers) - Z (the Z shell of the orbital) - spin

involved in the PDOS line that you want to draw. Apart from that, a request also accepts the name, color, linewidth and dash keys that manage the aesthetics of the line and normalize, which indicates if the PDOS should be normalized (divided by number of orbitals).

Here is an example of how to use the requests setting to create a line that displays the Oxygen 2p PDOS:

[4]:
plot.update_settings(requests=[{"name": "My first PDOS (Oxygen)", "species": ["O"], "n": 2, "l": 1}])
# or (it's equivalent)
plot.update_settings(requests=[{
    "name": "My first PDOS (Oxygen)", "species": ["O"],
    "orbitals": ["2pzZ1", "2pzZ2", "2pxZ1", "2pxZ2", "2pyZ1", "2pyZ2"]
}])
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 plot.update_settings(requests=[{"name": "My first PDOS (Oxygen)", "species": ["O"], "n": 2, "l": 1}])
      2 # or (it's equivalent)
      3 plot.update_settings(requests=[{
      4     "name": "My first PDOS (Oxygen)", "species": ["O"],
      5     "orbitals": ["2pzZ1", "2pzZ2", "2pxZ1", "2pxZ2", "2pyZ1", "2pyZ2"]
      6 }])

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:440, in ConfigurableMeta.__new__.<locals>.update_settings(self, *args, **kwargs)
    439 def update_settings(self, *args, **kwargs):
--> 440     return self._update_settings(*args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:561, in Configurable._update_settings(self, run_updates, **kwargs)
    559     #Do things after updating the settings
    560     if len(self.settings_history.last_updated) > 0 and run_updates:
--> 561         self._run_updates(self.settings_history.last_updated)
    563 return self

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:589, in Configurable._run_updates(self, for_keys)
    587 # Execute the functions that we need to execute.
    588 for f_name in func_names:
--> 589     getattr(self, f_name)()
    591 return self

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plotutils.py:829, in repeat_if_children.<locals>.apply_to_all_plots(obj, children_sel, *args, **kwargs)
    825     obj.get_figure()
    827 else:
--> 829     return method(obj, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:891, in vizplotly_settings.<locals>.decorator.<locals>.func(obj, *args, **kwargs)
    888 @wraps(method)
    889 def func(obj, *args, **kwargs):
    890     getattr(obj, method_name)(**kwargs, **extra_kwargs)
--> 891     return method(obj, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plot.py:1108, in Plot.set_data(self, update_fig, **kwargs)
   1100 @repeat_if_children
   1101 @vizplotly_settings('before')
   1102 def set_data(self, update_fig = True, **kwargs):
   1103     """ Method to process the data that has been read beforehand by read_data() and prepare the figure
   1104
   1105     If everything is succesful, it calls the next step in plotting (`get_figure`)
   1106     """
-> 1108     self._for_backend = self._set_data()
   1110     if update_fig:
   1111         self.get_figure()

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:972, in _populate_with_settings.<locals>.f_default_setting_args(self, *args, **kwargs)
    969         except KeyError:
    970             pass
--> 972 return f(self, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:466, in PdosPlot._set_data(self, requests, E0, Erange)
    463     Emin, Emax = Erange + E0
    465 # Get only the part of the arra
--> 466 E_PDOS = self.PDOS.where(
    467     (self.PDOS.E > Emin) & (self.PDOS.E < Emax), drop=True)
    469 # Build the dictionary that will be passed to the backend
    470 for_backend = {"Es": E_PDOS.E.values - E0, "PDOS_values": {}, "request_metadata": {}}

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plot.py:723, in Plot.__getattr__(self, key)
    720     except (KeyError, AttributeError):
    721         pass
--> 723 raise AttributeError(f"The attribute '{key}' was not found either in the plot, its backend, or in shared attributes.")

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

And now we are going to create three lines, one for each species

[5]:
plot.update_settings(requests=[
    {"name": "Oxygen", "species": ["O"], "color": "darkred", "dash": "dash", "normalize": True},
    {"name": "Titanium", "species": ["Ti"], "color": "grey", "linewidth": 3, "normalize": True},
    {"name": "Sr", "species": ["Sr"], "color": "green", "normalize": True},
], Erange=[-5, 5])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [5], in <cell line: 1>()
----> 1 plot.update_settings(requests=[
      2     {"name": "Oxygen", "species": ["O"], "color": "darkred", "dash": "dash", "normalize": True},
      3     {"name": "Titanium", "species": ["Ti"], "color": "grey", "linewidth": 3, "normalize": True},
      4     {"name": "Sr", "species": ["Sr"], "color": "green", "normalize": True},
      5 ], Erange=[-5, 5])

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:440, in ConfigurableMeta.__new__.<locals>.update_settings(self, *args, **kwargs)
    439 def update_settings(self, *args, **kwargs):
--> 440     return self._update_settings(*args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:561, in Configurable._update_settings(self, run_updates, **kwargs)
    559     #Do things after updating the settings
    560     if len(self.settings_history.last_updated) > 0 and run_updates:
--> 561         self._run_updates(self.settings_history.last_updated)
    563 return self

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:589, in Configurable._run_updates(self, for_keys)
    587 # Execute the functions that we need to execute.
    588 for f_name in func_names:
--> 589     getattr(self, f_name)()
    591 return self

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plotutils.py:829, in repeat_if_children.<locals>.apply_to_all_plots(obj, children_sel, *args, **kwargs)
    825     obj.get_figure()
    827 else:
--> 829     return method(obj, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:891, in vizplotly_settings.<locals>.decorator.<locals>.func(obj, *args, **kwargs)
    888 @wraps(method)
    889 def func(obj, *args, **kwargs):
    890     getattr(obj, method_name)(**kwargs, **extra_kwargs)
--> 891     return method(obj, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plot.py:766, in Plot.read_data(self, update_fig, **kwargs)
    763 call_method_if_present(self, "_before_read")
    765 # We try to read from the different entry points available
--> 766 self._read_from_sources()
    768 # We don't update the last dataread here in case there has been a succesful data read because we want to
    769 # wait for the after_read() method to be succesful
    770 if self.source is None:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:972, in _populate_with_settings.<locals>.f_default_setting_args(self, *args, **kwargs)
    969         except KeyError:
    970             pass
--> 972 return f(self, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plot.py:814, in Plot._read_from_sources(self, entry_points_order)
    812 else:
    813     self.source = None
--> 814     raise ValueError("Could not read or generate data for {} from any of the possible sources.\nHere are the errors for each source:\n{}"
    815                      .format(self.__class__.__name__, "\n".join(errors)))

ValueError: Could not read or generate data for PdosPlot from any of the possible sources.
Here are the errors for each source:
        - siesta output: FileNotFoundError.[Errno 2] No such file or directory: '_THIS_DIRECTORY_DOES_NOT_EXIST_/sisl/io/siesta/SrTiO3.PDOS'
        - TB trans: TypeError.expected str, bytes or os.PathLike object, not NoneType
        - wfsx file: TypeError.expected str, bytes or os.PathLike object, not NoneType
        - hamiltonian: TypeError.expected str, bytes or os.PathLike object, not NoneType

It’s interesting to note that the atoms key of each request accepts the same possibilities as the atoms argument of the Geometry methods. Therefore, you can use indices, categories, dictionaries, strings…

For example:

[6]:
# Let's import the AtomZ and AtomOdd categories just to play with them
from sisl.geom import AtomZ, AtomOdd

plot.update_settings(requests=[
    {"atoms": [0,1], "name": "Atoms 0 and 1"},
    {"atoms": {"Z": 8}, "name": "Atoms with Z=8"},
    {"atoms": AtomZ(8) & ~ AtomOdd(), "name": "Oxygens with even indices"}
])
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [6], in <cell line: 4>()
      1 # Let's import the AtomZ and AtomOdd categories just to play with them
      2 from sisl.geom import AtomZ, AtomOdd
----> 4 plot.update_settings(requests=[
      5     {"atoms": [0,1], "name": "Atoms 0 and 1"},
      6     {"atoms": {"Z": 8}, "name": "Atoms with Z=8"},
      7     {"atoms": AtomZ(8) & ~ AtomOdd(), "name": "Oxygens with even indices"}
      8 ])

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:440, in ConfigurableMeta.__new__.<locals>.update_settings(self, *args, **kwargs)
    439 def update_settings(self, *args, **kwargs):
--> 440     return self._update_settings(*args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:561, in Configurable._update_settings(self, run_updates, **kwargs)
    559     #Do things after updating the settings
    560     if len(self.settings_history.last_updated) > 0 and run_updates:
--> 561         self._run_updates(self.settings_history.last_updated)
    563 return self

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:589, in Configurable._run_updates(self, for_keys)
    587 # Execute the functions that we need to execute.
    588 for f_name in func_names:
--> 589     getattr(self, f_name)()
    591 return self

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plotutils.py:829, in repeat_if_children.<locals>.apply_to_all_plots(obj, children_sel, *args, **kwargs)
    825     obj.get_figure()
    827 else:
--> 829     return method(obj, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:891, in vizplotly_settings.<locals>.decorator.<locals>.func(obj, *args, **kwargs)
    888 @wraps(method)
    889 def func(obj, *args, **kwargs):
    890     getattr(obj, method_name)(**kwargs, **extra_kwargs)
--> 891     return method(obj, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plot.py:1108, in Plot.set_data(self, update_fig, **kwargs)
   1100 @repeat_if_children
   1101 @vizplotly_settings('before')
   1102 def set_data(self, update_fig = True, **kwargs):
   1103     """ Method to process the data that has been read beforehand by read_data() and prepare the figure
   1104
   1105     If everything is succesful, it calls the next step in plotting (`get_figure`)
   1106     """
-> 1108     self._for_backend = self._set_data()
   1110     if update_fig:
   1111         self.get_figure()

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:972, in _populate_with_settings.<locals>.f_default_setting_args(self, *args, **kwargs)
    969         except KeyError:
    970             pass
--> 972 return f(self, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:466, in PdosPlot._set_data(self, requests, E0, Erange)
    463     Emin, Emax = Erange + E0
    465 # Get only the part of the arra
--> 466 E_PDOS = self.PDOS.where(
    467     (self.PDOS.E > Emin) & (self.PDOS.E < Emax), drop=True)
    469 # Build the dictionary that will be passed to the backend
    470 for_backend = {"Es": E_PDOS.E.values - E0, "PDOS_values": {}, "request_metadata": {}}

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plot.py:723, in Plot.__getattr__(self, key)
    720     except (KeyError, AttributeError):
    721         pass
--> 723 raise AttributeError(f"The attribute '{key}' was not found either in the plot, its backend, or in shared attributes.")

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

Easy and fast DOS splitting

As you might have noticed, sometimes it might be cumbersome to build all the requests you want. If your needs are simple and you don’t need the flexibility of defining every parameter by yourself, there is a set of methods that will help you explore your PDOS data faster than ever before. These are: split_DOS, split_requests, update_requests, remove_requests and add_requests.?

Let’s begin with split_DOS. As you can imagine, this method splits the density of states:

[7]:
plot.split_DOS()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [7], in <cell line: 1>()
----> 1 plot.split_DOS()

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:867, in PdosPlot.split_DOS(self, on, only, exclude, clean, **kwargs)
    830 def split_DOS(self, on="species", only=None, exclude=None, clean=True, **kwargs):
    831     """
    832     Splits the density of states to the different contributions.
    833
   (...)
    865     >>> plot.split_DOS(on="n+l", species=["Au"], name="Au $ns")
    866     """
--> 867     requests = self.get_param('requests')._generate_queries(
    868         on=on, only=only, exclude=exclude, query_gen=self._new_request, **kwargs)
    870     # If the user doesn't want to clean the plot, we will just add the requests to the existing ones
    871     if not clean:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:358, in OrbitalQueries._generate_queries(self, on, only, exclude, query_gen, **kwargs)
    330 def _generate_queries(self, on, only=None, exclude=None, query_gen=None, **kwargs):
    331     """
    332     Automatically generates queries based on the current options.
    333
   (...)
    356         only those that belong to carbon atoms.
    357     """
--> 358     return self._split_query({}, on=on, only=only, exclude=exclude, query_gen=query_gen, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:278, in OrbitalQueries._split_query(self, query, on, only, exclude, query_gen, ignore_constraints, **kwargs)
    275             constraints[key] = val
    277 # Knowing what are our constraints (which may be none), get the available options
--> 278 values = self.get_options("+".join(on), **constraints)
    280 # We are going to make sure that, even if there was only one parameter to split on,
    281 # the values are two dimensional. In this way, we can take the same actions for the
    282 # case when there is only one parameter and the case when there are multiple.
    283 if values.ndim == 1:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:147, in OrbitalQueries.get_options(self, key, **kwargs)
    116 """
    117 Gets the options for a given key or combination of keys.
    118
   (...)
    144 >>> plot.get_param("requests").get_options("n+l", atoms=[0,1])
    145 """
    146 # Get the tadatframe
--> 147 df = self.orb_filtering_df
    149 # Filter the dataframe according to the constraints imposed by the kwargs,
    150 # if there are any.
    151 if kwargs:

AttributeError: 'OrbitalQueries' object has no attribute 'orb_filtering_df'

By default, it splits on the different species, but you can use the on argument to change that.

[8]:
plot.split_DOS(on="atoms")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [8], in <cell line: 1>()
----> 1 plot.split_DOS(on="atoms")

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:867, in PdosPlot.split_DOS(self, on, only, exclude, clean, **kwargs)
    830 def split_DOS(self, on="species", only=None, exclude=None, clean=True, **kwargs):
    831     """
    832     Splits the density of states to the different contributions.
    833
   (...)
    865     >>> plot.split_DOS(on="n+l", species=["Au"], name="Au $ns")
    866     """
--> 867     requests = self.get_param('requests')._generate_queries(
    868         on=on, only=only, exclude=exclude, query_gen=self._new_request, **kwargs)
    870     # If the user doesn't want to clean the plot, we will just add the requests to the existing ones
    871     if not clean:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:358, in OrbitalQueries._generate_queries(self, on, only, exclude, query_gen, **kwargs)
    330 def _generate_queries(self, on, only=None, exclude=None, query_gen=None, **kwargs):
    331     """
    332     Automatically generates queries based on the current options.
    333
   (...)
    356         only those that belong to carbon atoms.
    357     """
--> 358     return self._split_query({}, on=on, only=only, exclude=exclude, query_gen=query_gen, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:278, in OrbitalQueries._split_query(self, query, on, only, exclude, query_gen, ignore_constraints, **kwargs)
    275             constraints[key] = val
    277 # Knowing what are our constraints (which may be none), get the available options
--> 278 values = self.get_options("+".join(on), **constraints)
    280 # We are going to make sure that, even if there was only one parameter to split on,
    281 # the values are two dimensional. In this way, we can take the same actions for the
    282 # case when there is only one parameter and the case when there are multiple.
    283 if values.ndim == 1:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:147, in OrbitalQueries.get_options(self, key, **kwargs)
    116 """
    117 Gets the options for a given key or combination of keys.
    118
   (...)
    144 >>> plot.get_param("requests").get_options("n+l", atoms=[0,1])
    145 """
    146 # Get the tadatframe
--> 147 df = self.orb_filtering_df
    149 # Filter the dataframe according to the constraints imposed by the kwargs,
    150 # if there are any.
    151 if kwargs:

AttributeError: 'OrbitalQueries' object has no attribute 'orb_filtering_df'

Now we have the contribution of each atom.

But here comes the powerful part: split_DOS accepts as keyword arguments all the keys that a request accepts. Then, it adds that extra constrain to the splitting by adding the value to each request. So, if we want to get the separate contributions of all oxygen atoms, we can impose an extra constraint on species:

[9]:
plot.split_DOS(on="atoms", species=["O"], name="Oxygen $atoms")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [9], in <cell line: 1>()
----> 1 plot.split_DOS(on="atoms", species=["O"], name="Oxygen $atoms")

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:867, in PdosPlot.split_DOS(self, on, only, exclude, clean, **kwargs)
    830 def split_DOS(self, on="species", only=None, exclude=None, clean=True, **kwargs):
    831     """
    832     Splits the density of states to the different contributions.
    833
   (...)
    865     >>> plot.split_DOS(on="n+l", species=["Au"], name="Au $ns")
    866     """
--> 867     requests = self.get_param('requests')._generate_queries(
    868         on=on, only=only, exclude=exclude, query_gen=self._new_request, **kwargs)
    870     # If the user doesn't want to clean the plot, we will just add the requests to the existing ones
    871     if not clean:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:358, in OrbitalQueries._generate_queries(self, on, only, exclude, query_gen, **kwargs)
    330 def _generate_queries(self, on, only=None, exclude=None, query_gen=None, **kwargs):
    331     """
    332     Automatically generates queries based on the current options.
    333
   (...)
    356         only those that belong to carbon atoms.
    357     """
--> 358     return self._split_query({}, on=on, only=only, exclude=exclude, query_gen=query_gen, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:278, in OrbitalQueries._split_query(self, query, on, only, exclude, query_gen, ignore_constraints, **kwargs)
    275             constraints[key] = val
    277 # Knowing what are our constraints (which may be none), get the available options
--> 278 values = self.get_options("+".join(on), **constraints)
    280 # We are going to make sure that, even if there was only one parameter to split on,
    281 # the values are two dimensional. In this way, we can take the same actions for the
    282 # case when there is only one parameter and the case when there are multiple.
    283 if values.ndim == 1:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:147, in OrbitalQueries.get_options(self, key, **kwargs)
    116 """
    117 Gets the options for a given key or combination of keys.
    118
   (...)
    144 >>> plot.get_param("requests").get_options("n+l", atoms=[0,1])
    145 """
    146 # Get the tadatframe
--> 147 df = self.orb_filtering_df
    149 # Filter the dataframe according to the constraints imposed by the kwargs,
    150 # if there are any.
    151 if kwargs:

AttributeError: 'OrbitalQueries' object has no attribute 'orb_filtering_df'

and then we have only the oxygen atoms, which are all equivalent.

Note that we also set a name for all requests, with the additional twist that we used the templating supported by split_DOS. If you are splitting on parameter, you can use $parameter inside your name and the method will replace it with the value for each request. In this case parameter was atoms, but it could be anything you are splitting the DOS on.

You can also exclude some values of the parameter you are splitting on:

[10]:
plot.split_DOS(on="atoms", exclude=[1,3])
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [10], in <cell line: 1>()
----> 1 plot.split_DOS(on="atoms", exclude=[1,3])

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:867, in PdosPlot.split_DOS(self, on, only, exclude, clean, **kwargs)
    830 def split_DOS(self, on="species", only=None, exclude=None, clean=True, **kwargs):
    831     """
    832     Splits the density of states to the different contributions.
    833
   (...)
    865     >>> plot.split_DOS(on="n+l", species=["Au"], name="Au $ns")
    866     """
--> 867     requests = self.get_param('requests')._generate_queries(
    868         on=on, only=only, exclude=exclude, query_gen=self._new_request, **kwargs)
    870     # If the user doesn't want to clean the plot, we will just add the requests to the existing ones
    871     if not clean:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:358, in OrbitalQueries._generate_queries(self, on, only, exclude, query_gen, **kwargs)
    330 def _generate_queries(self, on, only=None, exclude=None, query_gen=None, **kwargs):
    331     """
    332     Automatically generates queries based on the current options.
    333
   (...)
    356         only those that belong to carbon atoms.
    357     """
--> 358     return self._split_query({}, on=on, only=only, exclude=exclude, query_gen=query_gen, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:278, in OrbitalQueries._split_query(self, query, on, only, exclude, query_gen, ignore_constraints, **kwargs)
    275             constraints[key] = val
    277 # Knowing what are our constraints (which may be none), get the available options
--> 278 values = self.get_options("+".join(on), **constraints)
    280 # We are going to make sure that, even if there was only one parameter to split on,
    281 # the values are two dimensional. In this way, we can take the same actions for the
    282 # case when there is only one parameter and the case when there are multiple.
    283 if values.ndim == 1:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:147, in OrbitalQueries.get_options(self, key, **kwargs)
    116 """
    117 Gets the options for a given key or combination of keys.
    118
   (...)
    144 >>> plot.get_param("requests").get_options("n+l", atoms=[0,1])
    145 """
    146 # Get the tadatframe
--> 147 df = self.orb_filtering_df
    149 # Filter the dataframe according to the constraints imposed by the kwargs,
    150 # if there are any.
    151 if kwargs:

AttributeError: 'OrbitalQueries' object has no attribute 'orb_filtering_df'

Or indicate the only values that you want:

[11]:
plot.split_DOS(on="atoms", only=[0,2])
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [11], in <cell line: 1>()
----> 1 plot.split_DOS(on="atoms", only=[0,2])

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:867, in PdosPlot.split_DOS(self, on, only, exclude, clean, **kwargs)
    830 def split_DOS(self, on="species", only=None, exclude=None, clean=True, **kwargs):
    831     """
    832     Splits the density of states to the different contributions.
    833
   (...)
    865     >>> plot.split_DOS(on="n+l", species=["Au"], name="Au $ns")
    866     """
--> 867     requests = self.get_param('requests')._generate_queries(
    868         on=on, only=only, exclude=exclude, query_gen=self._new_request, **kwargs)
    870     # If the user doesn't want to clean the plot, we will just add the requests to the existing ones
    871     if not clean:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:358, in OrbitalQueries._generate_queries(self, on, only, exclude, query_gen, **kwargs)
    330 def _generate_queries(self, on, only=None, exclude=None, query_gen=None, **kwargs):
    331     """
    332     Automatically generates queries based on the current options.
    333
   (...)
    356         only those that belong to carbon atoms.
    357     """
--> 358     return self._split_query({}, on=on, only=only, exclude=exclude, query_gen=query_gen, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:278, in OrbitalQueries._split_query(self, query, on, only, exclude, query_gen, ignore_constraints, **kwargs)
    275             constraints[key] = val
    277 # Knowing what are our constraints (which may be none), get the available options
--> 278 values = self.get_options("+".join(on), **constraints)
    280 # We are going to make sure that, even if there was only one parameter to split on,
    281 # the values are two dimensional. In this way, we can take the same actions for the
    282 # case when there is only one parameter and the case when there are multiple.
    283 if values.ndim == 1:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:147, in OrbitalQueries.get_options(self, key, **kwargs)
    116 """
    117 Gets the options for a given key or combination of keys.
    118
   (...)
    144 >>> plot.get_param("requests").get_options("n+l", atoms=[0,1])
    145 """
    146 # Get the tadatframe
--> 147 df = self.orb_filtering_df
    149 # Filter the dataframe according to the constraints imposed by the kwargs,
    150 # if there are any.
    151 if kwargs:

AttributeError: 'OrbitalQueries' object has no attribute 'orb_filtering_df'

Finally, if you want to split on multiple parameters at the same time, you can use + between different parameters. For example, to get all the oxygen orbitals:

[12]:
plot.split_DOS(on="n+l+m", species=["O"], name="Oxygen")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [12], in <cell line: 1>()
----> 1 plot.split_DOS(on="n+l+m", species=["O"], name="Oxygen")

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:867, in PdosPlot.split_DOS(self, on, only, exclude, clean, **kwargs)
    830 def split_DOS(self, on="species", only=None, exclude=None, clean=True, **kwargs):
    831     """
    832     Splits the density of states to the different contributions.
    833
   (...)
    865     >>> plot.split_DOS(on="n+l", species=["Au"], name="Au $ns")
    866     """
--> 867     requests = self.get_param('requests')._generate_queries(
    868         on=on, only=only, exclude=exclude, query_gen=self._new_request, **kwargs)
    870     # If the user doesn't want to clean the plot, we will just add the requests to the existing ones
    871     if not clean:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:358, in OrbitalQueries._generate_queries(self, on, only, exclude, query_gen, **kwargs)
    330 def _generate_queries(self, on, only=None, exclude=None, query_gen=None, **kwargs):
    331     """
    332     Automatically generates queries based on the current options.
    333
   (...)
    356         only those that belong to carbon atoms.
    357     """
--> 358     return self._split_query({}, on=on, only=only, exclude=exclude, query_gen=query_gen, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:278, in OrbitalQueries._split_query(self, query, on, only, exclude, query_gen, ignore_constraints, **kwargs)
    275             constraints[key] = val
    277 # Knowing what are our constraints (which may be none), get the available options
--> 278 values = self.get_options("+".join(on), **constraints)
    280 # We are going to make sure that, even if there was only one parameter to split on,
    281 # the values are two dimensional. In this way, we can take the same actions for the
    282 # case when there is only one parameter and the case when there are multiple.
    283 if values.ndim == 1:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:147, in OrbitalQueries.get_options(self, key, **kwargs)
    116 """
    117 Gets the options for a given key or combination of keys.
    118
   (...)
    144 >>> plot.get_param("requests").get_options("n+l", atoms=[0,1])
    145 """
    146 # Get the tadatframe
--> 147 df = self.orb_filtering_df
    149 # Filter the dataframe according to the constraints imposed by the kwargs,
    150 # if there are any.
    151 if kwargs:

AttributeError: 'OrbitalQueries' object has no attribute 'orb_filtering_df'

Managing existing requests

Not only you can create requests easily with split_DOS, but it’s also easy to manage the requests that you have created.

The methods that help you accomplish this are split_requests, update_requests, remove_requests. All three methods accept an undefined number of arguments that are used to select the requests you want to act on. You can refer to requests by their name (using a str) or their position (using an int). It’s very easy to understand with examples. Then, keyword arguments depend on the functionality of each method.

For example, let’s say that we have splitted the DOS on species

[13]:
plot.split_DOS(name="$species")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [13], in <cell line: 1>()
----> 1 plot.split_DOS(name="$species")

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:867, in PdosPlot.split_DOS(self, on, only, exclude, clean, **kwargs)
    830 def split_DOS(self, on="species", only=None, exclude=None, clean=True, **kwargs):
    831     """
    832     Splits the density of states to the different contributions.
    833
   (...)
    865     >>> plot.split_DOS(on="n+l", species=["Au"], name="Au $ns")
    866     """
--> 867     requests = self.get_param('requests')._generate_queries(
    868         on=on, only=only, exclude=exclude, query_gen=self._new_request, **kwargs)
    870     # If the user doesn't want to clean the plot, we will just add the requests to the existing ones
    871     if not clean:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:358, in OrbitalQueries._generate_queries(self, on, only, exclude, query_gen, **kwargs)
    330 def _generate_queries(self, on, only=None, exclude=None, query_gen=None, **kwargs):
    331     """
    332     Automatically generates queries based on the current options.
    333
   (...)
    356         only those that belong to carbon atoms.
    357     """
--> 358     return self._split_query({}, on=on, only=only, exclude=exclude, query_gen=query_gen, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:278, in OrbitalQueries._split_query(self, query, on, only, exclude, query_gen, ignore_constraints, **kwargs)
    275             constraints[key] = val
    277 # Knowing what are our constraints (which may be none), get the available options
--> 278 values = self.get_options("+".join(on), **constraints)
    280 # We are going to make sure that, even if there was only one parameter to split on,
    281 # the values are two dimensional. In this way, we can take the same actions for the
    282 # case when there is only one parameter and the case when there are multiple.
    283 if values.ndim == 1:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:147, in OrbitalQueries.get_options(self, key, **kwargs)
    116 """
    117 Gets the options for a given key or combination of keys.
    118
   (...)
    144 >>> plot.get_param("requests").get_options("n+l", atoms=[0,1])
    145 """
    146 # Get the tadatframe
--> 147 df = self.orb_filtering_df
    149 # Filter the dataframe according to the constraints imposed by the kwargs,
    150 # if there are any.
    151 if kwargs:

AttributeError: 'OrbitalQueries' object has no attribute 'orb_filtering_df'

and we want to remove the Sr and O lines. That’s easy:

[14]:
plot.remove_requests("Sr", 2)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [14], in <cell line: 1>()
----> 1 plot.remove_requests("Sr", 2)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:673, in PdosPlot.remove_requests(self, all, update_fig, *i_or_names)
    670 else:
    671     requests = [req for i, req in enumerate(self.get_setting("requests", copy=False)) if not self._matches_request(req, i_or_names, i)]
--> 673 return self.update_settings(run_updates=update_fig, requests=requests)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:440, in ConfigurableMeta.__new__.<locals>.update_settings(self, *args, **kwargs)
    439 def update_settings(self, *args, **kwargs):
--> 440     return self._update_settings(*args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:561, in Configurable._update_settings(self, run_updates, **kwargs)
    559     #Do things after updating the settings
    560     if len(self.settings_history.last_updated) > 0 and run_updates:
--> 561         self._run_updates(self.settings_history.last_updated)
    563 return self

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:589, in Configurable._run_updates(self, for_keys)
    587 # Execute the functions that we need to execute.
    588 for f_name in func_names:
--> 589     getattr(self, f_name)()
    591 return self

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plotutils.py:829, in repeat_if_children.<locals>.apply_to_all_plots(obj, children_sel, *args, **kwargs)
    825     obj.get_figure()
    827 else:
--> 829     return method(obj, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:891, in vizplotly_settings.<locals>.decorator.<locals>.func(obj, *args, **kwargs)
    888 @wraps(method)
    889 def func(obj, *args, **kwargs):
    890     getattr(obj, method_name)(**kwargs, **extra_kwargs)
--> 891     return method(obj, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plot.py:1108, in Plot.set_data(self, update_fig, **kwargs)
   1100 @repeat_if_children
   1101 @vizplotly_settings('before')
   1102 def set_data(self, update_fig = True, **kwargs):
   1103     """ Method to process the data that has been read beforehand by read_data() and prepare the figure
   1104
   1105     If everything is succesful, it calls the next step in plotting (`get_figure`)
   1106     """
-> 1108     self._for_backend = self._set_data()
   1110     if update_fig:
   1111         self.get_figure()

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/configurable.py:972, in _populate_with_settings.<locals>.f_default_setting_args(self, *args, **kwargs)
    969         except KeyError:
    970             pass
--> 972 return f(self, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:466, in PdosPlot._set_data(self, requests, E0, Erange)
    463     Emin, Emax = Erange + E0
    465 # Get only the part of the arra
--> 466 E_PDOS = self.PDOS.where(
    467     (self.PDOS.E > Emin) & (self.PDOS.E < Emax), drop=True)
    469 # Build the dictionary that will be passed to the backend
    470 for_backend = {"Es": E_PDOS.E.values - E0, "PDOS_values": {}, "request_metadata": {}}

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plot.py:723, in Plot.__getattr__(self, key)
    720     except (KeyError, AttributeError):
    721         pass
--> 723 raise AttributeError(f"The attribute '{key}' was not found either in the plot, its backend, or in shared attributes.")

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

We have indicated that we wanted to remove the request with name "Sr" and the 2nd request. Simple, isn’t it?

Now that we know how to indicate the requests that we want to act on, let’s use it to get the total Sr contribution, and then the Ti and O contributions splitted by n and l.

It sounds difficult, but it’s actually not. Just split the DOS on species:

[15]:
plot.split_DOS(name="$species", normalize=True)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [15], in <cell line: 1>()
----> 1 plot.split_DOS(name="$species", normalize=True)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plots/pdos.py:867, in PdosPlot.split_DOS(self, on, only, exclude, clean, **kwargs)
    830 def split_DOS(self, on="species", only=None, exclude=None, clean=True, **kwargs):
    831     """
    832     Splits the density of states to the different contributions.
    833
   (...)
    865     >>> plot.split_DOS(on="n+l", species=["Au"], name="Au $ns")
    866     """
--> 867     requests = self.get_param('requests')._generate_queries(
    868         on=on, only=only, exclude=exclude, query_gen=self._new_request, **kwargs)
    870     # If the user doesn't want to clean the plot, we will just add the requests to the existing ones
    871     if not clean:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:358, in OrbitalQueries._generate_queries(self, on, only, exclude, query_gen, **kwargs)
    330 def _generate_queries(self, on, only=None, exclude=None, query_gen=None, **kwargs):
    331     """
    332     Automatically generates queries based on the current options.
    333
   (...)
    356         only those that belong to carbon atoms.
    357     """
--> 358     return self._split_query({}, on=on, only=only, exclude=exclude, query_gen=query_gen, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:278, in OrbitalQueries._split_query(self, query, on, only, exclude, query_gen, ignore_constraints, **kwargs)
    275             constraints[key] = val
    277 # Knowing what are our constraints (which may be none), get the available options
--> 278 values = self.get_options("+".join(on), **constraints)
    280 # We are going to make sure that, even if there was only one parameter to split on,
    281 # the values are two dimensional. In this way, we can take the same actions for the
    282 # case when there is only one parameter and the case when there are multiple.
    283 if values.ndim == 1:

File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/input_fields/orbital.py:147, in OrbitalQueries.get_options(self, key, **kwargs)
    116 """
    117 Gets the options for a given key or combination of keys.
    118
   (...)
    144 >>> plot.get_param("requests").get_options("n+l", atoms=[0,1])
    145 """
    146 # Get the tadatframe
--> 147 df = self.orb_filtering_df
    149 # Filter the dataframe according to the constraints imposed by the kwargs,
    150 # if there are any.
    151 if kwargs:

AttributeError: 'OrbitalQueries' object has no attribute 'orb_filtering_df'

And then use split_requests to split only the requests that we want to split:

[16]:
plot.split_requests("Sr", 2, on="n+l", dash="dot")
'<sisl.viz.plots.pdos.PdosPlot object at 0x7effa67e9ea0>'

Notice how we’ve also set dash for all the requests that split_requests has generated. We can do this because split_requests works exactly as split_DOS, with the only difference that splits specific requests.

Just as a last thing, we will let you figure out how update_requests works:

[17]:
plot.update_requests("Ti", color="red", linewidth=2)
'<sisl.viz.plots.pdos.PdosPlot object at 0x7effa67e9ea0>'

We hope you enjoyed what you learned!


This next cell is just to create the thumbnail for the notebook in the docs

[18]:
thumbnail_plot = plot.update_requests("Ti", color=None, linewidth=1)

if thumbnail_plot:
    thumbnail_plot.show("png")
warn:0: SislWarning: There is no plotting backend selected, so the plot can't be displayed.