Data containers -
Overview - Basic structure¶
There are two kinds of objects which contain the outcome of a Z2Pack calculation:
Data objects. The distinction between the two is simple (although the naming is quite arbitrary):
Dataobjects contain only the data produced in a calculation, but not the convergence information.
Resultobjects contain the
Dataobject and the Control states which store the convergence / iteration information.
The reason for these two distinct kinds of data container is that the Control objects (which will be discussed later) need to know the outcome of a calculation. For this reason, they are passed the
Data object. To in turn save the state of the Control, the
Result is needed.
Taking into account that surface calculations contain a series of line calculations, the final structure is this:
Simplified public interface –
This nested structure is quite practical for the internal implementation. However, for the public interface a simpler way to access data attributes is needed. Two tricks are used to allow the end user to get all the data he needs directly from the topmost
__getattr__method of the
Resultforwards attribute access (for attributes which don’t exist in the
Result) to its
__getattr__forwards attribute access to each of the
LineResultobjects it contains, returning a list of objects.
With these two tricks, it is possible to just write
result = z2pack.surface.run(...) print(result.wcc)
wcc attribute access will be forwarded from the
SurfaceResult to the
SurfaceData, which forwards it to each of the
LineResult objects. Finally, those forward it to the line
Data objects, which contain the
wcc attribute. The result is a list, containing a list of WCC for each line.
EigenstateLineData - lazy properties and the Locker metaclass¶
First off, the lazy properties: This is a decorator which has two effects
The function is a property. That is, accessing the function gives you its returns value, just like the regular
When the function has been evaluated for the first time, the attribute is replaced by its value. The purpose of this is to avoid computing the property multiple times.
Descriptor that replaces itself with the return value of the method when accessed. The class is unlocked before setting the attribute, s.t. it can be used with a Locker type class.
The attributes of the line
Data objects have a hierarchical structure (the gap is calculated from the WCC, which is calculated from the Wilson loop, etc.), and the lazy properties are used to easily implement this without having to worry about computing anything twice.
By itself, using the lazy properties has one drawback: The user (or the programmer) could inadvertently change an attribute of the
Data instance. Because the subsequent properties might already be evaluated, this change will not be reflected. Since this is not desired, I decided to forbid changing attributes altogether. This is the purpose of the
ConstLocker metaclass. Its only effect is that it is not possible to change attributes after the
__init__ method – unless the instance is explicitly unlocked.