The following is my commit message from r2796 (). These modifications have occurred in the 'tensor_pdb' branch. I have replicated the commit message in full because I will use this as a template for the changes which will need to be made to 'self.relax.data' when the redesign of the relax data model occurs. It also serves as an example demonstrating behavioural modifications to a Python object being used as a data container.
Cheers,
Edward
The text of the commit message is:
Complete redesign of 'self.relax.data.diff': auto-calculated objects updated when params are set.
The previous commits redesigned the 'self.relax.data.diff' code so that when a diffusion tensor object (e.g. Diso, tensor, rotation, etc.) which is not one of the standard internal parameters is accessed, they would be calculated on the fly. Therefore if you asked for 'self.relax.data.diff[run].tensor', it would first be calculated and then returned. Most of these automatically created objects were setup when the program was initialised, although a few were returned by the 'self.__getattr__()' function.
However this approach is incredibly slow. This is especially apparent when each vector of the spherical vector distribution for creating the geometric representation of the Brownian rotational diffusion tensor is rotated and then extended to the correct length. The chain of auto-object creation by other auto-objects dependant on them together with the large number of accesses to 'self.relax.data.diff' caused the implementation to take minutes to create the vector distribution.
Therefore I have completely redesigned the code. Rather than being calculated on the fly, the objects are created when parameters are set. This uses the 'self.__setattr__()' function. All the auto-object functions of the file 'data/diff_tensor_auto_objects.py' have been shifted back into the diffusion tensor class DiffTensorElement. They have been renamed to 'self._calc_'+obj where obj is the calculated object name. The underscored first character is used by the 'Element' base class method 'self.__repr__()' to hide the object when printing the contents of 'self.relax.data.diff'.
The 'self.__setattr__()' function consists of two parts. Firstly the 'self.relax.data.diff' object attribute is set and then the data structures are updated. Before the attribute is set, it is checked to see if it belongs to a list of modifiable attributes. If it doesn't, a RelaxError is thrown saying that the object is not modifiable. If it is modifiable, it is set using the statement: self.__dict__[name] = value
The objects which are currently updated include: Diso (the isotropic diffusion rate), Dpar (the diffusion rate parallel to the unique axis, spheroid only), Dper (the diffusion rate perpendicular to the unique axis, spheroid only), Dx (the diffusion rate parallel to the x-axis, ellipsoid only), Dy (the diffusion rate parallel to the y-axis, ellipsoid only), Dz (the diffusion rate parallel to the z-axis, ellipsoid only), Dratio (the ratio of Dpar and Dper, spheroid only), Dpar_unit (the unit vector parallel to the unique axis, spheroid only), Dx_unit (the unit vector parallel to the x-axis, ellipsoid only), Dy_unit (the unit vector parallel to the y-axis, ellipsoid only), Dz_unit (the unit vector parallel to the z-axis, ellipsoid only), tensor_diag (the diagonalised diffusion tensor), rotation (the rotation matrix), tensor (the diffusion tensor in the PDB frame).
These are all updated using the function 'self._update_object()'. Dependant on the category (val, err, sim) of the object being set by 'self.__setattr__()', this function updates solely the value, error, or Monte Carlo simulation objects affected by it. For efficiency it returns without doing anything if the object being set is not within the 'update_if_set' list. This is a list of parameter names that, if modified, the 'target' object would then change. The 'self._update_object()' function automatically determines which 'self._calc_xxx()' function to use. Then to update the 'target', the objects corresponding to the names in the 'depends' list are sent to the 'self._calc_xxx()' function which then returns the calculated object. If the object being set belongs to the Monte Carlo simulation category, then the 'self._update_object()' will create an array by looping over each simulation calling 'self._calc_xxx()' for each sim. The updated object is then set using the statement:
self.__dict__[target] = value
In the case of MC simulations, 'value' is replaced with 'sim_values'.