Data containers - Result
and Data
objects¶
Overview - Basic structure¶
There are two kinds of objects which contain the outcome of a Z2Pack calculation: Result
and Data
objects. The distinction between the two is simple (although the naming is quite arbitrary):
Data
objects contain only the data produced in a calculation, but not the convergence information.Result
objects contain theData
object 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 – __getattr__
tricks¶
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 Result
object:
The
__getattr__
method of theResult
forwards attribute access (for attributes which don’t exist in theResult
) to itsData
object.The
SurfaceData
__getattr__
forwards attribute access to each of theLineResult
objects it contains, returning a list of objects.
With these two tricks, it is possible to just write
result = z2pack.surface.run(...)
print(result.wcc)
The 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.
WccLineData
and EigenstateLineData
- lazy properties and the Locker metaclass¶
The WccLineData
and EigenstateLineData
classes use two things that might not be obvious at first glance – lazy properties, and the ConstLocker
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
@property
.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.
- class z2pack.line._data._LazyProperty(method)[source]¶
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.