def __init__(self, **kwargs): """ Valid keyword arguments for a Component are: *ucp_a*: unit cell property along a axis *ucp_b*: unit cell property along b axis *d001*: unit cell length c (aka d001) *default_c*: default c-value *delta_c*: the variation in basal spacing due to defects *layer_atoms*: ObjectListStore of layer Atoms *interlayer_atoms*: ObjectListStore of interlayer Atoms *atom_relations*: ObjectListStore of AtomRelations *inherit_ucp_a*: whether or not to inherit the ucp_a property from the linked component (if linked) *inherit_ucp_b*: whether or not to inherit the ucp_b property from the linked component (if linked) *inherit_d001*: whether or not to inherit the d001 property from the linked component (if linked) *inherit_default_c*: whether or not to inherit the default_c property from the linked component (if linked) *inherit_delta_c*: whether or not to inherit the delta_c property from the linked component (if linked) *inherit_layer_atoms*: whether or not to inherit the layer_atoms property from the linked component (if linked) *inherit_interlayer_atoms*: whether or not to inherit the interlayer_atoms property from the linked component (if linked) *inherit_atom_relations*: whether or not to inherit the atom_relations property from the linked component (if linked) *linked_with_uuid*: the UUID for the component this one is linked with Deprecated, but still supported: *linked_with_index*: the index of the component this one is linked with in the ObjectListStore of the parent based on phase. """ my_kwargs = self.pop_kwargs( kwargs, "data_name", "data_layer_atoms", "data_interlayer_atoms", "data_atom_relations", "data_atom_ratios", "data_d001", "data_default_c", "data_delta_c", "lattice_d", "data_cell_a", "data_ucp_a", "data_cell_b", "data_ucp_b", "linked_with_uuid", "linked_with_index", "inherit_cell_a", "inherit_cell_b", *[ names[0] for names in type(self).Meta.get_local_storable_properties() ]) super(Component, self).__init__(**kwargs) kwargs = my_kwargs # Setup signals: self.atoms_changed = HoldableSignal() # Set up data object self._data_object = ComponentData(d001=0.0, delta_c=0.0) # Set attributes: self.name = self.get_kwarg(kwargs, "", "name", "data_name") # Load lists: self.layer_atoms = self.get_list(kwargs, [], "layer_atoms", "data_layer_atoms", parent=self) self.interlayer_atoms = self.get_list(kwargs, [], "interlayer_atoms", "data_interlayer_atoms", parent=self) self.atom_relations = self.get_list(kwargs, [], "atom_relations", "data_atom_relations", parent=self) # Add all atom ratios to the AtomRelation list for atom_ratio in self.get_list(kwargs, [], "atom_ratios", "data_atom_ratios", parent=self): self.atom_relations.append(atom_ratio) # Observe the inter-layer atoms, and make sure they get stretched for atom in self.interlayer_atoms: atom.stretch_values = True self.observe_model(atom) # Observe the layer atoms for atom in self.layer_atoms: self.observe_model(atom) # Resolve their relations and observe the atom relations for relation in self.atom_relations: relation.resolve_relations() self.observe_model(relation) # Connect signals to lists and dicts: self._layer_atoms_observer = ListObserver(self._on_layer_atom_inserted, self._on_layer_atom_removed, prop_name="layer_atoms", model=self) self._interlayer_atoms_observer = ListObserver( self._on_interlayer_atom_inserted, self._on_interlayer_atom_removed, prop_name="interlayer_atoms", model=self) self._atom_relations_observer = ListObserver( self._on_atom_relation_inserted, self._on_atom_relation_removed, prop_name="atom_relations", model=self) # Update lattice values: self.d001 = self.get_kwarg(kwargs, self._d001, "d001", "data_d001") self._default_c = float( self.get_kwarg(kwargs, self._d001, "default_c", "data_default_c")) self.delta_c = float( self.get_kwarg(kwargs, self._delta_c, "delta_c", "data_delta_c")) self.update_lattice_d() # Set/Create & observe unit cell properties: ucp_a = self.get_kwarg(kwargs, None, "ucp_a", "data_ucp_a", "data_cell_a") if isinstance(ucp_a, float): ucp_a = UnitCellProperty(name="cell length a", value=ucp_a, parent=self) self._ucp_a = self.parse_init_arg(ucp_a, UnitCellProperty, child=True, default_is_class=True, name="Cell length a [nm]", parent=self) self.observe_model(self._ucp_a) ucp_b = self.get_kwarg(kwargs, None, "ucp_b", "data_ucp_b", "data_cell_b") if isinstance(ucp_b, float): ucp_b = UnitCellProperty(name="cell length b", value=ucp_b, parent=self) self._ucp_b = self.parse_init_arg(ucp_b, UnitCellProperty, child=True, default_is_class=True, name="Cell length b [nm]", parent=self) self.observe_model(self._ucp_b) # Set links: self._linked_with_uuid = self.get_kwarg(kwargs, "", "linked_with_uuid") self._linked_with_index = self.get_kwarg(kwargs, -1, "linked_with_index") # Set inherit flags: self.inherit_d001 = self.get_kwarg(kwargs, False, "inherit_d001") self.inherit_ucp_a = self.get_kwarg(kwargs, False, "inherit_ucp_a", "inherit_cell_a") self.inherit_ucp_b = self.get_kwarg(kwargs, False, "inherit_ucp_b", "inherit_cell_b") self.inherit_default_c = self.get_kwarg(kwargs, False, "inherit_default_c") self.inherit_delta_c = self.get_kwarg(kwargs, False, "inherit_delta_c") self.inherit_layer_atoms = self.get_kwarg(kwargs, False, "inherit_layer_atoms") self.inherit_interlayer_atoms = self.get_kwarg( kwargs, False, "inherit_interlayer_atoms") self.inherit_atom_relations = self.get_kwarg(kwargs, False, "inherit_atom_relations")
class Component(DataModel, Storable, RefinementGroup): # MODEL INTEL: __metaclass__ = PyXRDRefinableMeta class Meta(DataModel.Meta): properties = [ PropIntel(name="name", data_type=unicode, label="Name", is_column=True, has_widget=True, storable=True), PropIntel(name="linked_with", data_type=object, label="Linked with", widget_type='custom', is_column=True, has_widget=True), PropIntel(name="d001", data_type=float, label="Cell length c [nm]", is_column=True, has_widget=True, storable=True, refinable=True, minimum=0.0, maximum=5.0, inh_name="inherit_d001", stor_name="_d001", inh_from="linked_with"), PropIntel(name="lattice_d", data_type=float, label="Lattice c length [nm]"), PropIntel(name="default_c", data_type=float, label="Default c length [nm]", is_column=True, has_widget=True, storable=True, minimum=0.0, maximum=5.0, inh_name="inherit_default_c", stor_name="_default_c", inh_from="linked_with"), PropIntel(name="delta_c", data_type=float, label="C length dev. [nm]", is_column=True, has_widget=True, storable=True, refinable=True, minimum=0.0, maximum=0.05, inh_name="inherit_delta_c", stor_name="_delta_c", inh_from="linked_with"), PropIntel(name="ucp_a", data_type=object, label="Cell length a [nm]", is_column=True, has_widget=True, storable=True, refinable=True, inh_name="inherit_ucp_a", stor_name="_ucp_a", inh_from="linked_with"), PropIntel(name="ucp_b", data_type=object, label="Cell length b [nm]", is_column=True, has_widget=True, storable=True, refinable=True, inh_name="inherit_ucp_b", stor_name="_ucp_b", inh_from="linked_with"), PropIntel(name="inherit_d001", data_type=bool, label="Inh. cell length c", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_ucp_b", data_type=bool, label="Inh. cell length b", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_ucp_a", data_type=bool, label="Inh. cell length a", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_default_c", data_type=bool, label="Inh. default length c", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_delta_c", data_type=bool, label="Inh. c length dev.", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_layer_atoms", data_type=bool, label="Inh. layer atoms", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_interlayer_atoms", data_type=bool, label="Inh. interlayer atoms", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_atom_relations", data_type=bool, label="Inh. atom relations", is_column=True, has_widget=True, storable=True), PropIntel(name="atom_relations", data_type=object, label="Atom relations", is_column=True, has_widget=True, storable=True, widget_type="custom", refinable=True, inh_name="inherit_atom_relations", stor_name="_atom_relations", inh_from="linked_with", class_type=AtomRelation), PropIntel(name="layer_atoms", data_type=object, label="Layer atoms", is_column=True, has_widget=True, storable=True, widget_type="custom", inh_name="inherit_layer_atoms", stor_name="_layer_atoms", inh_from="linked_with", class_type=Atom), PropIntel(name="interlayer_atoms", data_type=object, label="Interlayer atoms", is_column=True, has_widget=True, storable=True, widget_type="custom", inh_name="inherit_interlayer_atoms", stor_name="_interlayer_atoms", inh_from="linked_with", class_type=Atom), PropIntel(name="atoms_changed", data_type=object, is_column=False, storable=False, has_widget=False) ] store_id = "Component" _data_object = None @property def data_object(self): weight = 0.0 self._data_object.layer_atoms = [None] * len(self.layer_atoms) for i, atom in enumerate(self.layer_atoms): self._data_object.layer_atoms[i] = atom.data_object weight += atom.weight self._data_object.interlayer_atoms = [None] * len(self.interlayer_atoms) for i, atom in enumerate(self.interlayer_atoms): self._data_object.interlayer_atoms[i] = atom.data_object weight += atom.weight self._data_object.volume = self.get_volume() self._data_object.weight = weight self._data_object.d001 = self.d001 self._data_object.default_c = self.default_c self._data_object.delta_c = self.delta_c self._data_object.lattice_d = self._lattice_d return self._data_object phase = property(DataModel.parent.fget, DataModel.parent.fset) # SIGNALS: atoms_changed = None # UNIT CELL DIMENSION SHORTCUTS: @property def cell_a(self): return self._ucp_a.value @property def cell_b(self): return self._ucp_b.value @property def cell_c(self): return self.d001 # PROPERTIES: _name = "" def get_name(self): return self._name def set_name(self, value): self._name = value self.visuals_changed.emit() def get_inherit_ucp_a(self): return self._ucp_a.inherited def set_inherit_ucp_a(self, value): self._ucp_a.inherited = value def get_inherit_ucp_b(self): return self._ucp_b.inherited def set_inherit_ucp_b(self, value): self._ucp_b.inherited = value def _set_float_data_property(self, name, value): try: value = float(value) except ValueError: return # ignore faulty values setattr(self._data_object, name, value) self.data_changed.emit() def _set_bool_property(self, name, value): try: value = bool(value) except ValueError: return # ignore faulty values if getattr(self, "_%s" % name) != value: with self.data_changed.hold_and_emit(): setattr(self, "_%s" % name, value) _inherit_d001 = False def get_inherit_d001(self): return self._inherit_d001 def set_inherit_d001(self, value): self._set_bool_property("inherit_d001", value) _inherit_default_c = False def get_inherit_default_c(self): return self._inherit_default_c def set_inherit_default_c(self, value): self._set_bool_property("inherit_default_c", value) _inherit_delta_c = False def get_inherit_delta_c(self): return self._inherit_delta_c def set_inherit_delta_c(self, value): self._set_bool_property("inherit_delta_c", value) _inherit_layer_atoms = False def get_inherit_layer_atoms(self): return self._inherit_layer_atoms def set_inherit_layer_atoms(self, value): self._set_bool_property("inherit_layer_atoms", value) _inherit_interlayer_atoms = False def get_inherit_interlayer_atoms(self): return self._inherit_interlayer_atoms def set_inherit_interlayer_atoms(self, value): self._set_bool_property("inherit_interlayer_atoms", value) _inherit_atom_relations = False def get_inherit_atom_relations(self): return self._inherit_atom_relations def set_inherit_atom_relations(self, value): self._set_bool_property("inherit_atom_relations", value) _linked_with = None _linked_with_index = None _linked_with_uuid = None def get_linked_with(self): return self._linked_with def set_linked_with(self, value): if value != self._linked_with: if self._linked_with is not None: self.relieve_model(self._linked_with) self._linked_with = value if self._linked_with is not None: self.observe_model(self._linked_with) else: for prop in self.Meta.get_inheritable_properties(): setattr(self, "inherit_%s" % prop, False) self.data_changed.emit() # Lattice d-value __lattice_d = 0.0 @property def _lattice_d(self): return self.__lattice_d; @_lattice_d.setter def _lattice_d(self, value): try: value = float(value) except ValueError: return # ignore faulty values if self.__lattice_d != value: self.__lattice_d = value self.data_changed.emit() def get_lattice_d(self): return self._lattice_d # INHERITABLE PROPERTIES: def _set_inheritable_property_value(self, name, value): current = getattr(self, "_%s" % name) if current != value: with self.data_changed.hold_and_emit(): if name.startswith("ucp_"): if current is not None: self.relieve_model(current) setattr(self, "_%s" % name, value) if name.startswith("ucp_"): if value is not None: self.observe_model(value) _ucp_a = None def get_ucp_a(self): return self._get_inheritable_property_value("ucp_a") def set_ucp_a(self, value): self._set_inheritable_property_value("ucp_a", value) _ucp_b = None def get_ucp_b(self): return self._get_inheritable_property_value("ucp_b") def set_ucp_b(self, value): self._set_inheritable_property_value("ucp_b", value) _d001 = 1.0 def get_d001(self): return self._get_inheritable_property_value("d001") def set_d001(self, value): self._set_inheritable_property_value("d001", float(value)) _default_c = 1.0 def get_default_c(self): return self._get_inheritable_property_value("default_c") def set_default_c(self, value): self._set_inheritable_property_value("default_c", float(value)) _delta_c = 0.0 def get_delta_c(self): return self._get_inheritable_property_value("delta_c") def set_delta_c(self, value): self._set_inheritable_property_value("delta_c", float(value)) _layer_atoms = [] def get_layer_atoms(self): return self._get_inheritable_property_value("layer_atoms") def set_layer_atoms(self, value): with self.data_changed.hold_and_emit(): self._layer_atoms = value _interlayer_atoms = [] def get_interlayer_atoms(self): return self._get_inheritable_property_value("interlayer_atoms") def set_interlayer_atoms(self, value): with self.data_changed.hold_and_emit(): self._interlayer_atoms = value _atom_relations = [] def get_atom_relations(self): return self._get_inheritable_property_value("atom_relations") def set_atom_relations(self, value): with self.data_changed.hold_and_emit(): self._atom_relations = value # Instance flag indicating whether or not linked_with & inherit flags should be saved save_links = True # Class flag indicating whether or not atom types in the component should be # exported using their name rather then their project-uuid. export_atom_types = False # REFINEMENT GROUP IMPLEMENTATION: @property def refine_title(self): return self.name @property def refine_descriptor_data(self): return dict( phase_name=self.phase.refine_title, component_name=self.refine_title ) # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, **kwargs): """ Valid keyword arguments for a Component are: *ucp_a*: unit cell property along a axis *ucp_b*: unit cell property along b axis *d001*: unit cell length c (aka d001) *default_c*: default c-value *delta_c*: the variation in basal spacing due to defects *layer_atoms*: ObjectListStore of layer Atoms *interlayer_atoms*: ObjectListStore of interlayer Atoms *atom_relations*: ObjectListStore of AtomRelations *inherit_ucp_a*: whether or not to inherit the ucp_a property from the linked component (if linked) *inherit_ucp_b*: whether or not to inherit the ucp_b property from the linked component (if linked) *inherit_d001*: whether or not to inherit the d001 property from the linked component (if linked) *inherit_default_c*: whether or not to inherit the default_c property from the linked component (if linked) *inherit_delta_c*: whether or not to inherit the delta_c property from the linked component (if linked) *inherit_layer_atoms*: whether or not to inherit the layer_atoms property from the linked component (if linked) *inherit_interlayer_atoms*: whether or not to inherit the interlayer_atoms property from the linked component (if linked) *inherit_atom_relations*: whether or not to inherit the atom_relations property from the linked component (if linked) *linked_with_uuid*: the UUID for the component this one is linked with Deprecated, but still supported: *linked_with_index*: the index of the component this one is linked with in the ObjectListStore of the parent based on phase. """ my_kwargs = self.pop_kwargs(kwargs, "data_name", "data_layer_atoms", "data_interlayer_atoms", "data_atom_relations", "data_atom_ratios", "data_d001", "data_default_c", "data_delta_c", "data_cell_a", "data_ucp_a", "data_cell_b", "data_ucp_b", "linked_with_uuid", "linked_with_index", "inherit_cell_a", "inherit_cell_b", *[names[0] for names in type(self).Meta.get_local_storable_properties()] ) super(Component, self).__init__(**kwargs) kwargs = my_kwargs # Setup signals: self.atoms_changed = HoldableSignal() # Set up data object self._data_object = ComponentData( d001=0.0, delta_c=0.0 ) # Set attributes: self.name = self.get_kwarg(kwargs, "", "name", "data_name") # Load lists: self.layer_atoms = self.get_list(kwargs, [], "layer_atoms", "data_layer_atoms", parent=self) self.interlayer_atoms = self.get_list(kwargs, [], "interlayer_atoms", "data_interlayer_atoms", parent=self) self.atom_relations = self.get_list(kwargs, [], "atom_relations", "data_atom_relations", parent=self) # Add all atom ratios to the AtomRelation list for atom_ratio in self.get_list(kwargs, [], "atom_ratios", "data_atom_ratios", parent=self): self.atom_relations.append(atom_ratio) # Observe the inter-layer atoms, and make sure they get stretched for atom in self.interlayer_atoms: atom.stretch_values = True self.observe_model(atom) # Observe the layer atoms for atom in self.layer_atoms: self.observe_model(atom) # Resolve their relations and observe the atom relations for relation in self.atom_relations: relation.resolve_relations() self.observe_model(relation) # Connect signals to lists and dicts: self._layer_atoms_observer = ListObserver( self._on_layer_atom_inserted, self._on_layer_atom_removed, prop_name="layer_atoms", model=self ) self._interlayer_atoms_observer = ListObserver( self._on_interlayer_atom_inserted, self._on_interlayer_atom_removed, prop_name="interlayer_atoms", model=self ) self._atom_relations_observer = ListObserver( self._on_atom_relation_inserted, self._on_atom_relation_removed, prop_name="atom_relations", model=self ) # Update lattice values: self.d001 = self.get_kwarg(kwargs, self._d001, "d001", "data_d001") self._default_c = float(self.get_kwarg(kwargs, self._d001, "default_c", "data_default_c")) self.delta_c = float(self.get_kwarg(kwargs, self._delta_c, "delta_c", "data_delta_c")) self.update_lattice_d() # Set/Create & observe unit cell properties: ucp_a = self.get_kwarg(kwargs, None, "ucp_a", "data_ucp_a", "data_cell_a") if isinstance(ucp_a, float): ucp_a = UnitCellProperty(name="cell length a", value=ucp_a, parent=self) self._ucp_a = self.parse_init_arg( ucp_a, UnitCellProperty, child=True, default_is_class=True, name="Cell length a [nm]", parent=self ) self.observe_model(self._ucp_a) ucp_b = self.get_kwarg(kwargs, None, "ucp_b", "data_ucp_b", "data_cell_b") if isinstance(ucp_b, float): ucp_b = UnitCellProperty(name="cell length b", value=ucp_b, parent=self) self._ucp_b = self.parse_init_arg( ucp_b, UnitCellProperty, child=True, default_is_class=True, name="Cell length b [nm]", parent=self ) self.observe_model(self._ucp_b) # Set links: self._linked_with_uuid = self.get_kwarg(kwargs, "", "linked_with_uuid") self._linked_with_index = self.get_kwarg(kwargs, -1, "linked_with_index") # Set inherit flags: self.inherit_d001 = self.get_kwarg(kwargs, False, "inherit_d001") self.inherit_ucp_a = self.get_kwarg(kwargs, False, "inherit_ucp_a", "inherit_cell_a") self.inherit_ucp_b = self.get_kwarg(kwargs, False, "inherit_ucp_b", "inherit_cell_b") self.inherit_default_c = self.get_kwarg(kwargs, False, "inherit_default_c") self.inherit_delta_c = self.get_kwarg(kwargs, False, "inherit_delta_c") self.inherit_layer_atoms = self.get_kwarg(kwargs, False, "inherit_layer_atoms") self.inherit_interlayer_atoms = self.get_kwarg(kwargs, False, "inherit_interlayer_atoms") self.inherit_atom_relations = self.get_kwarg(kwargs, False, "inherit_atom_relations") def __repr__(self): return "Component(name='%s', linked_with=%r)" % (self.name, self.linked_with) # ------------------------------------------------------------ # Notifications of observable properties # ------------------------------------------------------------ @DataModel.observe("data_changed", signal=True) def _on_data_model_changed(self, model, prop_name, info): # Check whether the changed model is an AtomRelation or Atom, if so # re-apply the atom_relations. with self.data_changed.hold(): if isinstance(model, AtomRelation) or isinstance(model, Atom): self._apply_atom_relations() self._update_ucp_values() if isinstance(model, UnitCellProperty): self.data_changed.emit() # propagate signal @DataModel.observe("removed", signal=True) def _on_data_model_removed(self, model, prop_name, info): # Check whether the removed component is linked with this one, if so # clears the link and emits the data_changed signal. if model != self and self.linked_with is not None and self.linked_with == model: with self.data_changed.hold_and_emit(): self.linked_with = None def _on_layer_atom_inserted(self, atom): """Sets the atoms parent and stretch_values property, updates the components lattice d-value, and emits a data_changed signal""" with self.data_changed.hold_and_emit(): with self.atoms_changed.hold_and_emit(): atom.parent = self atom.stretch_values = False self.observe_model(atom) self.update_lattice_d() def _on_layer_atom_removed(self, atom): """Clears the atoms parent, updates the components lattice d-value, and emits a data_changed signal""" with self.data_changed.hold_and_emit(): with self.atoms_changed.hold_and_emit(): self.relieve_model(atom) atom.parent = None self.update_lattice_d() def _on_interlayer_atom_inserted(self, atom): """Sets the atoms parent and stretch_values property, and emits a data_changed signal""" with self.data_changed.hold_and_emit(): with self.atoms_changed.hold_and_emit(): atom.stretch_values = True atom.parent = self def _on_interlayer_atom_removed(self, atom): """Clears the atoms parent property, and emits a data_changed signal""" with self.data_changed.hold_and_emit(): with self.atoms_changed.hold_and_emit(): atom.parent = None def _on_atom_relation_inserted(self, item): item.parent = self self.observe_model(item) self._apply_atom_relations() def _on_atom_relation_removed(self, item): self.relieve_model(item) item.parent = None self._apply_atom_relations() # ------------------------------------------------------------ # Input/Output stuff # ------------------------------------------------------------ def resolve_json_references(self): for atom in self.layer_atoms: atom.resolve_json_references() for atom in self.interlayer_atoms: atom.resolve_json_references() self._ucp_a.resolve_json_references() self._ucp_a.update_value() self._ucp_b.resolve_json_references() self._ucp_b.update_value() if getattr(self, "_linked_with_uuid", None): self.linked_with = type(type(self)).object_pool.get_object(self._linked_with_uuid) del self._linked_with_uuid elif getattr(self, "_linked_with_index", None) and self._linked_with_index != -1: warn("The use of object indeces is deprected since version 0.4. Please switch to using object UUIDs.", DeprecationWarning) self.linked_with = self.parent.based_on.components.get_user_from_index(self._linked_with_index) del self._linked_with_index @classmethod def save_components(cls, components, filename): """ Saves multiple components to a single file. """ Component.export_atom_types = True for comp in components: comp.save_links = False with zipfile.ZipFile(filename, 'w', compression=COMPRESSION) as zfile: for component in components: zfile.writestr(component.uuid, component.dump_object()) for comp in components: comp.save_links = True Component.export_atom_types = False # After export we change all the UUID's # This way, we're sure that we're not going to import objects with # duplicate UUID's! type(cls).object_pool.change_all_uuids() @classmethod def load_components(cls, filename, parent=None): """ Returns multiple components loaded from a single file. """ # Before import, we change all the UUID's # This way we're sure that we're not going to import objects # with duplicate UUID's! type(cls).object_pool.change_all_uuids() if zipfile.is_zipfile(filename): with zipfile.ZipFile(filename, 'r') as zfile: for uuid in zfile.namelist(): yield cls.load_object(zfile.open(uuid), parent=parent) else: yield cls.load_object(filename, parent=parent) def json_properties(self): if self.phase == None or not self.save_links: retval = Storable.json_properties(self) for prop in self.Meta.all_properties: if prop.inh_name: retval[prop.inh_name] = False else: retval = Storable.json_properties(self) retval["linked_with_uuid"] = self.linked_with.uuid if self.linked_with is not None else "" return retval # ------------------------------------------------------------ # Methods & Functions # ------------------------------------------------------------ def get_factors(self, range_stl): """ Get the structure factor for the given range of sin(theta)/lambda values. :param range_stl: A 1D numpy ndarray """ return get_factors(range_stl, self.data_object) def get_interlayer_stretch_factors(self): z_factor = (self.cell_c - self.lattice_d) / (self.default_c - self.lattice_d) return self.lattice_d, z_factor def update_lattice_d(self): """ Updates the lattice_d attribute for this :class:`~.Component`. Should normally not be called from outside the component. """ for atom in self.layer_atoms: self._lattice_d = float(max(self.lattice_d, atom.default_z)) def _apply_atom_relations(self): """ Applies the :class:`~..atom_relations.AtomRelation` objects in this component. Should normally not be called from outside the component. """ with self.data_changed.hold_and_emit(): for relation in self.atom_relations: # Clear the 'driven by' flags: relation.driven_by_other = False for relation in self.atom_relations: # Apply the relations, will also take care of flag setting: relation.apply_relation() def _update_ucp_values(self): """ Updates the :class:`~..unit_cell_prop.UnitCellProperty` objects in this component. Should normally not be called from outside the component. """ with self.data_changed.hold(): for ucp in [self._ucp_a, self._ucp_b]: ucp.update_value() def get_volume(self): """ Get the volume for this :class:`~.Component`. Will always return a value >= 1e-25, to prevent division-by-zero errors in calculation code. """ return max(self.cell_a * self.cell_b * self.cell_c, 1e-25) def get_weight(self): """ Get the total atomic weight for this :class:`~.Component`. """ weight = 0 for atom in (self.layer_atoms + self.interlayer_atoms): weight += atom.weight return weight # ------------------------------------------------------------ # AtomRelation list related # ------------------------------------------------------------ def move_atom_relation_up(self, relation): """ Move the passed :class:`~..atom_relations.AtomRelation` up one slot """ index = self.atom_relations.index(relation) del self.atom_relations[index] self.atom_relations.insert(max(index - 1, 0), relation) def move_atom_relation_down(self, relation): """ Move the passed :class:`~..atom_relations.AtomRelation` down one slot """ index = self.atom_relations.index(relation) del self.atom_relations[index] self.atom_relations.insert(min(index + 1, len(self.atom_relations)), relation) pass # end of class
class Component(RefinementGroup, DataModel, Storable): # MODEL INTEL: __metaclass__ = PyXRDRefinableMeta class Meta(DataModel.Meta): properties = [ PropIntel(name="name", data_type=unicode, label="Name", is_column=True, has_widget=True, storable=True), PropIntel(name="linked_with", data_type=object, label="Linked with", widget_type='custom', is_column=True, has_widget=True, storable=True), PropIntel(name="d001", data_type=float, label="Cell length c [nm]", is_column=True, has_widget=True, storable=True, refinable=True, minimum=0.0, maximum=5.0, inh_name="inherit_d001", stor_name="_d001", inh_from="linked_with"), PropIntel(name="lattice_d", data_type=float, label="Lattice c length [nm]"), PropIntel(name="default_c", data_type=float, label="Default c length [nm]", is_column=True, has_widget=True, storable=True, minimum=0.0, maximum=5.0, inh_name="inherit_default_c", stor_name="_default_c", inh_from="linked_with"), PropIntel(name="delta_c", data_type=float, label="C length dev. [nm]", is_column=True, has_widget=True, storable=True, refinable=True, minimum=0.0, maximum=0.05, inh_name="inherit_delta_c", stor_name="_delta_c", inh_from="linked_with"), PropIntel(name="ucp_a", data_type=object, label="Cell length a [nm]", is_column=True, has_widget=True, storable=True, refinable=True, inh_name="inherit_ucp_a", stor_name="_ucp_a", inh_from="linked_with"), PropIntel(name="ucp_b", data_type=object, label="Cell length b [nm]", is_column=True, has_widget=True, storable=True, refinable=True, inh_name="inherit_ucp_b", stor_name="_ucp_b", inh_from="linked_with"), PropIntel(name="inherit_d001", data_type=bool, label="Inh. cell length c", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_ucp_b", data_type=bool, label="Inh. cell length b", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_ucp_a", data_type=bool, label="Inh. cell length a", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_default_c", data_type=bool, label="Inh. default length c", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_delta_c", data_type=bool, label="Inh. c length dev.", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_layer_atoms", data_type=bool, label="Inh. layer atoms", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_interlayer_atoms", data_type=bool, label="Inh. interlayer atoms", is_column=True, has_widget=True, storable=True), PropIntel(name="inherit_atom_relations", data_type=bool, label="Inh. atom relations", is_column=True, has_widget=True, storable=True), PropIntel(name="atom_relations", data_type=object, label="Atom relations", is_column=True, has_widget=True, storable=True, widget_type="custom", refinable=True, inh_name="inherit_atom_relations", stor_name="_atom_relations", inh_from="linked_with", class_type=AtomRelation), PropIntel(name="layer_atoms", data_type=object, label="Layer atoms", is_column=True, has_widget=True, storable=True, widget_type="custom", inh_name="inherit_layer_atoms", stor_name="_layer_atoms", inh_from="linked_with", class_type=Atom), PropIntel(name="interlayer_atoms", data_type=object, label="Interlayer atoms", is_column=True, has_widget=True, storable=True, widget_type="custom", inh_name="inherit_interlayer_atoms", stor_name="_interlayer_atoms", inh_from="linked_with", class_type=Atom), PropIntel(name="atoms_changed", data_type=object, is_column=False, storable=False, has_widget=False) ] store_id = "Component" _data_object = None @property def data_object(self): weight = 0.0 self._data_object.layer_atoms = [None] * len(self.layer_atoms) for i, atom in enumerate(self.layer_atoms): self._data_object.layer_atoms[i] = atom.data_object weight += atom.weight self._data_object.interlayer_atoms = [None] * len( self.interlayer_atoms) for i, atom in enumerate(self.interlayer_atoms): self._data_object.interlayer_atoms[i] = atom.data_object weight += atom.weight self._data_object.volume = self.get_volume() self._data_object.weight = weight self._data_object.d001 = self.d001 self._data_object.default_c = self.default_c self._data_object.delta_c = self.delta_c self._data_object.lattice_d = self._lattice_d return self._data_object phase = property(DataModel.parent.fget, DataModel.parent.fset) # SIGNALS: atoms_changed = None # UNIT CELL DIMENSION SHORTCUTS: @property def cell_a(self): return self._ucp_a.value @property def cell_b(self): return self._ucp_b.value @property def cell_c(self): return self.d001 # PROPERTIES: _name = "" def get_name(self): return self._name def set_name(self, value): self._name = value self.visuals_changed.emit() def get_inherit_ucp_a(self): return self._ucp_a.inherited def set_inherit_ucp_a(self, value): self._ucp_a.inherited = value def get_inherit_ucp_b(self): return self._ucp_b.inherited def set_inherit_ucp_b(self, value): self._ucp_b.inherited = value def _set_float_data_property(self, name, value): try: value = float(value) except ValueError: return # ignore faulty values setattr(self._data_object, name, value) self.data_changed.emit() def _set_bool_property(self, name, value): try: value = bool(value) except ValueError: return # ignore faulty values if getattr(self, "_%s" % name) != value: with self.data_changed.hold_and_emit(): setattr(self, "_%s" % name, value) _inherit_d001 = False def get_inherit_d001(self): return self._inherit_d001 def set_inherit_d001(self, value): self._set_bool_property("inherit_d001", value) _inherit_default_c = False def get_inherit_default_c(self): return self._inherit_default_c def set_inherit_default_c(self, value): self._set_bool_property("inherit_default_c", value) _inherit_delta_c = False def get_inherit_delta_c(self): return self._inherit_delta_c def set_inherit_delta_c(self, value): self._set_bool_property("inherit_delta_c", value) _inherit_layer_atoms = False def get_inherit_layer_atoms(self): return self._inherit_layer_atoms def set_inherit_layer_atoms(self, value): self._set_bool_property("inherit_layer_atoms", value) _inherit_interlayer_atoms = False def get_inherit_interlayer_atoms(self): return self._inherit_interlayer_atoms def set_inherit_interlayer_atoms(self, value): self._set_bool_property("inherit_interlayer_atoms", value) _inherit_atom_relations = False def get_inherit_atom_relations(self): return self._inherit_atom_relations def set_inherit_atom_relations(self, value): self._set_bool_property("inherit_atom_relations", value) _linked_with = None _linked_with_index = None _linked_with_uuid = None def get_linked_with(self): return self._linked_with def set_linked_with(self, value): if value != self._linked_with: if self._linked_with is not None: self.relieve_model(self._linked_with) self._linked_with = value if self._linked_with is not None: self.observe_model(self._linked_with) else: for prop in self.Meta.get_inheritable_properties(): setattr(self, "inherit_%s" % prop, False) self.data_changed.emit() # Lattice d-value __lattice_d = 0.0 @property def _lattice_d(self): return self.__lattice_d @_lattice_d.setter def _lattice_d(self, value): try: value = float(value) except ValueError: return # ignore faulty values if self.__lattice_d != value: self.__lattice_d = value self.data_changed.emit() def get_lattice_d(self): return self._lattice_d # INHERITABLE PROPERTIES: def _set_inheritable_property_value(self, name, value): current = getattr(self, "_%s" % name) if current != value: with self.data_changed.hold_and_emit(): if name.startswith("ucp_"): if current is not None: self.relieve_model(current) setattr(self, "_%s" % name, value) if name.startswith("ucp_"): if value is not None: self.observe_model(value) _ucp_a = None def get_ucp_a(self): return self._get_inheritable_property_value("ucp_a") def set_ucp_a(self, value): self._set_inheritable_property_value("ucp_a", value) _ucp_b = None def get_ucp_b(self): return self._get_inheritable_property_value("ucp_b") def set_ucp_b(self, value): self._set_inheritable_property_value("ucp_b", value) _d001 = 1.0 def get_d001(self): return self._get_inheritable_property_value("d001") def set_d001(self, value): self._set_inheritable_property_value("d001", float(value)) _default_c = 1.0 def get_default_c(self): return self._get_inheritable_property_value("default_c") def set_default_c(self, value): self._set_inheritable_property_value("default_c", float(value)) _delta_c = 0.0 def get_delta_c(self): return self._get_inheritable_property_value("delta_c") def set_delta_c(self, value): self._set_inheritable_property_value("delta_c", float(value)) _layer_atoms = [] def get_layer_atoms(self): return self._get_inheritable_property_value("layer_atoms") def set_layer_atoms(self, value): with self.data_changed.hold_and_emit(): self._layer_atoms = value _interlayer_atoms = [] def get_interlayer_atoms(self): return self._get_inheritable_property_value("interlayer_atoms") def set_interlayer_atoms(self, value): with self.data_changed.hold_and_emit(): self._interlayer_atoms = value _atom_relations = [] def get_atom_relations(self): return self._get_inheritable_property_value("atom_relations") def set_atom_relations(self, value): with self.data_changed.hold_and_emit(): self._atom_relations = value # Instance flag indicating whether or not linked_with & inherit flags should be saved save_links = True # Class flag indicating whether or not atom types in the component should be # exported using their name rather then their project-uuid. export_atom_types = False # REFINEMENT GROUP IMPLEMENTATION: @property def refine_title(self): return self.name @property def refine_descriptor_data(self): return dict(phase_name=self.phase.refine_title, component_name=self.refine_title) # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, **kwargs): """ Valid keyword arguments for a Component are: *ucp_a*: unit cell property along a axis *ucp_b*: unit cell property along b axis *d001*: unit cell length c (aka d001) *default_c*: default c-value *delta_c*: the variation in basal spacing due to defects *layer_atoms*: ObjectListStore of layer Atoms *interlayer_atoms*: ObjectListStore of interlayer Atoms *atom_relations*: ObjectListStore of AtomRelations *inherit_ucp_a*: whether or not to inherit the ucp_a property from the linked component (if linked) *inherit_ucp_b*: whether or not to inherit the ucp_b property from the linked component (if linked) *inherit_d001*: whether or not to inherit the d001 property from the linked component (if linked) *inherit_default_c*: whether or not to inherit the default_c property from the linked component (if linked) *inherit_delta_c*: whether or not to inherit the delta_c property from the linked component (if linked) *inherit_layer_atoms*: whether or not to inherit the layer_atoms property from the linked component (if linked) *inherit_interlayer_atoms*: whether or not to inherit the interlayer_atoms property from the linked component (if linked) *inherit_atom_relations*: whether or not to inherit the atom_relations property from the linked component (if linked) *linked_with_uuid*: the UUID for the component this one is linked with Deprecated, but still supported: *linked_with_index*: the index of the component this one is linked with in the ObjectListStore of the parent based on phase. """ my_kwargs = self.pop_kwargs( kwargs, "data_name", "data_layer_atoms", "data_interlayer_atoms", "data_atom_relations", "data_atom_ratios", "data_d001", "data_default_c", "data_delta_c", "lattice_d", "data_cell_a", "data_ucp_a", "data_cell_b", "data_ucp_b", "linked_with_uuid", "linked_with_index", "inherit_cell_a", "inherit_cell_b", *[ names[0] for names in type(self).Meta.get_local_storable_properties() ]) super(Component, self).__init__(**kwargs) kwargs = my_kwargs # Setup signals: self.atoms_changed = HoldableSignal() # Set up data object self._data_object = ComponentData(d001=0.0, delta_c=0.0) # Set attributes: self.name = self.get_kwarg(kwargs, "", "name", "data_name") # Load lists: self.layer_atoms = self.get_list(kwargs, [], "layer_atoms", "data_layer_atoms", parent=self) self.interlayer_atoms = self.get_list(kwargs, [], "interlayer_atoms", "data_interlayer_atoms", parent=self) self.atom_relations = self.get_list(kwargs, [], "atom_relations", "data_atom_relations", parent=self) # Add all atom ratios to the AtomRelation list for atom_ratio in self.get_list(kwargs, [], "atom_ratios", "data_atom_ratios", parent=self): self.atom_relations.append(atom_ratio) # Observe the inter-layer atoms, and make sure they get stretched for atom in self.interlayer_atoms: atom.stretch_values = True self.observe_model(atom) # Observe the layer atoms for atom in self.layer_atoms: self.observe_model(atom) # Resolve their relations and observe the atom relations for relation in self.atom_relations: relation.resolve_relations() self.observe_model(relation) # Connect signals to lists and dicts: self._layer_atoms_observer = ListObserver(self._on_layer_atom_inserted, self._on_layer_atom_removed, prop_name="layer_atoms", model=self) self._interlayer_atoms_observer = ListObserver( self._on_interlayer_atom_inserted, self._on_interlayer_atom_removed, prop_name="interlayer_atoms", model=self) self._atom_relations_observer = ListObserver( self._on_atom_relation_inserted, self._on_atom_relation_removed, prop_name="atom_relations", model=self) # Update lattice values: self.d001 = self.get_kwarg(kwargs, self._d001, "d001", "data_d001") self._default_c = float( self.get_kwarg(kwargs, self._d001, "default_c", "data_default_c")) self.delta_c = float( self.get_kwarg(kwargs, self._delta_c, "delta_c", "data_delta_c")) self.update_lattice_d() # Set/Create & observe unit cell properties: ucp_a = self.get_kwarg(kwargs, None, "ucp_a", "data_ucp_a", "data_cell_a") if isinstance(ucp_a, float): ucp_a = UnitCellProperty(name="cell length a", value=ucp_a, parent=self) self._ucp_a = self.parse_init_arg(ucp_a, UnitCellProperty, child=True, default_is_class=True, name="Cell length a [nm]", parent=self) self.observe_model(self._ucp_a) ucp_b = self.get_kwarg(kwargs, None, "ucp_b", "data_ucp_b", "data_cell_b") if isinstance(ucp_b, float): ucp_b = UnitCellProperty(name="cell length b", value=ucp_b, parent=self) self._ucp_b = self.parse_init_arg(ucp_b, UnitCellProperty, child=True, default_is_class=True, name="Cell length b [nm]", parent=self) self.observe_model(self._ucp_b) # Set links: self._linked_with_uuid = self.get_kwarg(kwargs, "", "linked_with_uuid") self._linked_with_index = self.get_kwarg(kwargs, -1, "linked_with_index") # Set inherit flags: self.inherit_d001 = self.get_kwarg(kwargs, False, "inherit_d001") self.inherit_ucp_a = self.get_kwarg(kwargs, False, "inherit_ucp_a", "inherit_cell_a") self.inherit_ucp_b = self.get_kwarg(kwargs, False, "inherit_ucp_b", "inherit_cell_b") self.inherit_default_c = self.get_kwarg(kwargs, False, "inherit_default_c") self.inherit_delta_c = self.get_kwarg(kwargs, False, "inherit_delta_c") self.inherit_layer_atoms = self.get_kwarg(kwargs, False, "inherit_layer_atoms") self.inherit_interlayer_atoms = self.get_kwarg( kwargs, False, "inherit_interlayer_atoms") self.inherit_atom_relations = self.get_kwarg(kwargs, False, "inherit_atom_relations") def __repr__(self): return "Component(name='%s', linked_with=%r)" % (self.name, self.linked_with) # ------------------------------------------------------------ # Notifications of observable properties # ------------------------------------------------------------ @DataModel.observe("data_changed", signal=True) def _on_data_model_changed(self, model, prop_name, info): # Check whether the changed model is an AtomRelation or Atom, if so # re-apply the atom_relations. with self.data_changed.hold(): if isinstance(model, AtomRelation) or isinstance(model, Atom): self._apply_atom_relations() self._update_ucp_values() if isinstance(model, UnitCellProperty): self.data_changed.emit() # propagate signal @DataModel.observe("removed", signal=True) def _on_data_model_removed(self, model, prop_name, info): # Check whether the removed component is linked with this one, if so # clears the link and emits the data_changed signal. if model != self and self.linked_with is not None and self.linked_with == model: with self.data_changed.hold_and_emit(): self.linked_with = None def _on_layer_atom_inserted(self, atom): """Sets the atoms parent and stretch_values property, updates the components lattice d-value, and emits a data_changed signal""" with self.data_changed.hold_and_emit(): with self.atoms_changed.hold_and_emit(): atom.parent = self atom.stretch_values = False self.observe_model(atom) self.update_lattice_d() def _on_layer_atom_removed(self, atom): """Clears the atoms parent, updates the components lattice d-value, and emits a data_changed signal""" with self.data_changed.hold_and_emit(): with self.atoms_changed.hold_and_emit(): self.relieve_model(atom) atom.parent = None self.update_lattice_d() def _on_interlayer_atom_inserted(self, atom): """Sets the atoms parent and stretch_values property, and emits a data_changed signal""" with self.data_changed.hold_and_emit(): with self.atoms_changed.hold_and_emit(): atom.stretch_values = True atom.parent = self def _on_interlayer_atom_removed(self, atom): """Clears the atoms parent property, and emits a data_changed signal""" with self.data_changed.hold_and_emit(): with self.atoms_changed.hold_and_emit(): atom.parent = None def _on_atom_relation_inserted(self, item): item.parent = self self.observe_model(item) self._apply_atom_relations() def _on_atom_relation_removed(self, item): self.relieve_model(item) item.parent = None self._apply_atom_relations() # ------------------------------------------------------------ # Input/Output stuff # ------------------------------------------------------------ def resolve_json_references(self): for atom in self.layer_atoms: atom.resolve_json_references() for atom in self.interlayer_atoms: atom.resolve_json_references() self._ucp_a.resolve_json_references() self._ucp_a.update_value() self._ucp_b.resolve_json_references() self._ucp_b.update_value() if getattr(self, "_linked_with_uuid", None): self.linked_with = type(type(self)).object_pool.get_object( self._linked_with_uuid) del self._linked_with_uuid elif getattr(self, "_linked_with_index", None) and self._linked_with_index != -1: warn( "The use of object indeces is deprected since version 0.4. Please switch to using object UUIDs.", DeprecationWarning) self.linked_with = self.parent.based_on.components.get_user_from_index( self._linked_with_index) del self._linked_with_index @classmethod def save_components(cls, components, filename): """ Saves multiple components to a single file. """ Component.export_atom_types = True for comp in components: comp.save_links = False with zipfile.ZipFile(filename, 'w', compression=COMPRESSION) as zfile: for component in components: zfile.writestr(component.uuid, component.dump_object()) for comp in components: comp.save_links = True Component.export_atom_types = False # After export we change all the UUID's # This way, we're sure that we're not going to import objects with # duplicate UUID's! type(cls).object_pool.change_all_uuids() @classmethod def load_components(cls, filename, parent=None): """ Returns multiple components loaded from a single file. """ # Before import, we change all the UUID's # This way we're sure that we're not going to import objects # with duplicate UUID's! type(cls).object_pool.change_all_uuids() if zipfile.is_zipfile(filename): with zipfile.ZipFile(filename, 'r') as zfile: for uuid in zfile.namelist(): obj = JSONParser.parse(zfile.open(uuid)) obj.parent = parent yield obj else: obj = JSONParser.parse(filename) obj.parent = parent yield obj def json_properties(self): if self.phase == None or not self.save_links: retval = Storable.json_properties(self) for prop in self.Meta.all_properties: if prop.inh_name: retval[prop.inh_name] = False else: retval = Storable.json_properties(self) retval[ "linked_with_uuid"] = self.linked_with.uuid if self.linked_with is not None else "" return retval # ------------------------------------------------------------ # Methods & Functions # ------------------------------------------------------------ def get_factors(self, range_stl): """ Get the structure factor for the given range of sin(theta)/lambda values. :param range_stl: A 1D numpy ndarray """ return get_factors(range_stl, self.data_object) def get_interlayer_stretch_factors(self): z_factor = (self.cell_c - self.lattice_d) / (self.default_c - self.lattice_d) return self.lattice_d, z_factor def update_lattice_d(self): """ Updates the lattice_d attribute for this :class:`~.Component`. Should normally not be called from outside the component. """ for atom in self.layer_atoms: self._lattice_d = float(max(self.lattice_d, atom.default_z)) def _apply_atom_relations(self): """ Applies the :class:`~..atom_relations.AtomRelation` objects in this component. Should normally not be called from outside the component. """ with self.data_changed.hold_and_emit(): for relation in self.atom_relations: # Clear the 'driven by' flags: relation.driven_by_other = False for relation in self.atom_relations: # Apply the relations, will also take care of flag setting: relation.apply_relation() def _update_ucp_values(self): """ Updates the :class:`~..unit_cell_prop.UnitCellProperty` objects in this component. Should normally not be called from outside the component. """ with self.data_changed.hold(): for ucp in [self._ucp_a, self._ucp_b]: ucp.update_value() def get_volume(self): """ Get the volume for this :class:`~.Component`. Will always return a value >= 1e-25, to prevent division-by-zero errors in calculation code. """ return max(self.cell_a * self.cell_b * self.cell_c, 1e-25) def get_weight(self): """ Get the total atomic weight for this :class:`~.Component`. """ weight = 0 for atom in (self.layer_atoms + self.interlayer_atoms): weight += atom.weight return weight # ------------------------------------------------------------ # AtomRelation list related # ------------------------------------------------------------ def move_atom_relation_up(self, relation): """ Move the passed :class:`~..atom_relations.AtomRelation` up one slot """ index = self.atom_relations.index(relation) del self.atom_relations[index] self.atom_relations.insert(max(index - 1, 0), relation) def move_atom_relation_down(self, relation): """ Move the passed :class:`~..atom_relations.AtomRelation` down one slot """ index = self.atom_relations.index(relation) del self.atom_relations[index] self.atom_relations.insert(min(index + 1, len(self.atom_relations)), relation) pass # end of class
def __init__(self, **kwargs): """ Valid keyword arguments for a Component are: *ucp_a*: unit cell property along a axis *ucp_b*: unit cell property along b axis *d001*: unit cell length c (aka d001) *default_c*: default c-value *delta_c*: the variation in basal spacing due to defects *layer_atoms*: ObjectListStore of layer Atoms *interlayer_atoms*: ObjectListStore of interlayer Atoms *atom_relations*: ObjectListStore of AtomRelations *inherit_ucp_a*: whether or not to inherit the ucp_a property from the linked component (if linked) *inherit_ucp_b*: whether or not to inherit the ucp_b property from the linked component (if linked) *inherit_d001*: whether or not to inherit the d001 property from the linked component (if linked) *inherit_default_c*: whether or not to inherit the default_c property from the linked component (if linked) *inherit_delta_c*: whether or not to inherit the delta_c property from the linked component (if linked) *inherit_layer_atoms*: whether or not to inherit the layer_atoms property from the linked component (if linked) *inherit_interlayer_atoms*: whether or not to inherit the interlayer_atoms property from the linked component (if linked) *inherit_atom_relations*: whether or not to inherit the atom_relations property from the linked component (if linked) *linked_with_uuid*: the UUID for the component this one is linked with Deprecated, but still supported: *linked_with_index*: the index of the component this one is linked with in the ObjectListStore of the parent based on phase. """ my_kwargs = self.pop_kwargs(kwargs, "data_name", "data_layer_atoms", "data_interlayer_atoms", "data_atom_relations", "data_atom_ratios", "data_d001", "data_default_c", "data_delta_c", "data_cell_a", "data_ucp_a", "data_cell_b", "data_ucp_b", "linked_with_uuid", "linked_with_index", "inherit_cell_a", "inherit_cell_b", *[names[0] for names in type(self).Meta.get_local_storable_properties()] ) super(Component, self).__init__(**kwargs) kwargs = my_kwargs # Setup signals: self.atoms_changed = HoldableSignal() # Set up data object self._data_object = ComponentData( d001=0.0, delta_c=0.0 ) # Set attributes: self.name = self.get_kwarg(kwargs, "", "name", "data_name") # Load lists: self.layer_atoms = self.get_list(kwargs, [], "layer_atoms", "data_layer_atoms", parent=self) self.interlayer_atoms = self.get_list(kwargs, [], "interlayer_atoms", "data_interlayer_atoms", parent=self) self.atom_relations = self.get_list(kwargs, [], "atom_relations", "data_atom_relations", parent=self) # Add all atom ratios to the AtomRelation list for atom_ratio in self.get_list(kwargs, [], "atom_ratios", "data_atom_ratios", parent=self): self.atom_relations.append(atom_ratio) # Observe the inter-layer atoms, and make sure they get stretched for atom in self.interlayer_atoms: atom.stretch_values = True self.observe_model(atom) # Observe the layer atoms for atom in self.layer_atoms: self.observe_model(atom) # Resolve their relations and observe the atom relations for relation in self.atom_relations: relation.resolve_relations() self.observe_model(relation) # Connect signals to lists and dicts: self._layer_atoms_observer = ListObserver( self._on_layer_atom_inserted, self._on_layer_atom_removed, prop_name="layer_atoms", model=self ) self._interlayer_atoms_observer = ListObserver( self._on_interlayer_atom_inserted, self._on_interlayer_atom_removed, prop_name="interlayer_atoms", model=self ) self._atom_relations_observer = ListObserver( self._on_atom_relation_inserted, self._on_atom_relation_removed, prop_name="atom_relations", model=self ) # Update lattice values: self.d001 = self.get_kwarg(kwargs, self._d001, "d001", "data_d001") self._default_c = float(self.get_kwarg(kwargs, self._d001, "default_c", "data_default_c")) self.delta_c = float(self.get_kwarg(kwargs, self._delta_c, "delta_c", "data_delta_c")) self.update_lattice_d() # Set/Create & observe unit cell properties: ucp_a = self.get_kwarg(kwargs, None, "ucp_a", "data_ucp_a", "data_cell_a") if isinstance(ucp_a, float): ucp_a = UnitCellProperty(name="cell length a", value=ucp_a, parent=self) self._ucp_a = self.parse_init_arg( ucp_a, UnitCellProperty, child=True, default_is_class=True, name="Cell length a [nm]", parent=self ) self.observe_model(self._ucp_a) ucp_b = self.get_kwarg(kwargs, None, "ucp_b", "data_ucp_b", "data_cell_b") if isinstance(ucp_b, float): ucp_b = UnitCellProperty(name="cell length b", value=ucp_b, parent=self) self._ucp_b = self.parse_init_arg( ucp_b, UnitCellProperty, child=True, default_is_class=True, name="Cell length b [nm]", parent=self ) self.observe_model(self._ucp_b) # Set links: self._linked_with_uuid = self.get_kwarg(kwargs, "", "linked_with_uuid") self._linked_with_index = self.get_kwarg(kwargs, -1, "linked_with_index") # Set inherit flags: self.inherit_d001 = self.get_kwarg(kwargs, False, "inherit_d001") self.inherit_ucp_a = self.get_kwarg(kwargs, False, "inherit_ucp_a", "inherit_cell_a") self.inherit_ucp_b = self.get_kwarg(kwargs, False, "inherit_ucp_b", "inherit_cell_b") self.inherit_default_c = self.get_kwarg(kwargs, False, "inherit_default_c") self.inherit_delta_c = self.get_kwarg(kwargs, False, "inherit_delta_c") self.inherit_layer_atoms = self.get_kwarg(kwargs, False, "inherit_layer_atoms") self.inherit_interlayer_atoms = self.get_kwarg(kwargs, False, "inherit_interlayer_atoms") self.inherit_atom_relations = self.get_kwarg(kwargs, False, "inherit_atom_relations")