---
execution:
  timeout: 60
jupytext:
  formats: ipynb,md:myst
  text_representation:
    extension: .md
    format_name: myst
kernelspec:
  display_name: Python 3 (ipykernel)
  language: python
  name: python3
---

(sec:InheritanceStructure)=
# Inheritance, Mixins, etc.

## GPU

:::{margin}
Transferring data from the CPU to the GPU and back is a big performance bottleneck.  To
effectively utilize GPU's, the data must remain there as long as possible.
:::
Our current strategy for utilizing GPU acceleration is to use the {meth}`object.__init_subclass__`
functionality introduced to Python in [PEP 487][].  The idea is that the core
computational loop will use GPU-enable methods like `get_Vext_GPU()` etc.  These should
be carefully coded so as to use GPU-arrays without transferring data to the CPU and will
return a reference to the GPU array.  Adding `_GPU` to the name of the method tells the
user/developer that they must be aware of these issues.

This creates two problems:
:::{margin}
In previous implementations, this was done with a class decorator
`_GPU.add_non_GPU_methods`, but we will now subsume this functionality in
{meth}`utils.GPUHelper.__init_subclass__`.
:::
As a quick check, note that `__init_subclass__()` is called once per class.
```{code-cell}
:tags: [margin]
class Base:
    def __init_subclass__(cls):
        print(cls.__name__)
        super().__init_subclass__()


class A(Base):
    pass
    

class B(A):
    pass
```
1. When analyzing data, plotting, etc. we need the data on the CPU.  The default
   implementation would require explicitly getting the data when needed, but only when
   working on the GPU.
   
   To solve this problem, we would like to "automatically" generate non-GPU methods like
   `get_Vext()` that will call `get_Vext_GPU()`, then transfer the data to a CPU array
   iff needed.
2. New users will probably not want to be bothered with these complications, so we would
   like them to be able to simply define `get_Vext()` and move on.
   {meth}`utils.GPUHelper.__init_subclass__` will detect this and provide appropriate
   GPU-methods like `get_Vext_GPU()` that raise a performance warning, but then which
   delegate to `get_Vext()` so that the code functions correctly.
   
The current solution is to ensure that any classes using `*_GPU` methods inherit from
{cls}`gpe.utils.GPUHelper`.  This will provide the require matching methods.  Base
classes should define the `*_GPU` methods required by the core evolution code.  This is
done in all of the foundation classes starting from {class}`gpe.bec.StateDFTBase`.

[PEP 487]: <https://peps.python.org/pep-0487/>



:::{inheritance-diagram} gpe.bec.State
:parts: 1
:private-bases:
:::

```{code-cell}
from gpe.utils_docs import get_inheritance_graph
import gpe.bec
display(get_inheritance_graph(gpe.bec.State, mro=True))
```
