3 Commits 5e28a8d108 ... 7ffba9a295

Author SHA1 Message Date
  Andrei Bondarenko 7ffba9a295 More docs 2 years ago
  Andrei Bondarenko 04fed97deb Added docs 2 years ago
  Andrei Bondarenko b604785f33 Added some documentation 2 years ago
6 changed files with 474 additions and 8 deletions
  1. 111 0
      services/bottom/V0.py
  2. 72 2
      services/bottom/V1.py
  3. 68 0
      services/pn.py
  4. 50 0
      services/point/cartesian.py
  5. 50 0
      services/point/polar.py
  6. 123 6
      services/scd.py

+ 111 - 0
services/bottom/V0.py

@@ -4,33 +4,94 @@ from typing import Any, List
 
 
 class Bottom:
+    """
+    Implements services for LTM bottom (not yet explicitly modelled).
+    Implemented using the (Modelverse) state graph data structure
+    """
     def __init__(self, state: State):
         self.state = state
 
     def create_node(self, value=None) -> UUID:
+        """
+        Creates a node, which optionally contains a value.
+
+        Args:
+            value: value to be stored in the node
+
+        Returns:
+            UUID of the node.
+        """
         if value is None:
             return self.state.create_node()
         else:
             return self.state.create_nodevalue(value)
 
     def create_edge(self, source: UUID, target: UUID, label=None):
+        """
+        Creates an edge, which optionally is labelled.
+
+        Args:
+            source: source element of the edge
+            target: target element of the edge
+            label: value to label the edge with
+
+        Returns:
+            UUID of the edge.
+        """
         if label is None:
             return self.state.create_edge(source, target)
         else:
             return self.state.create_dict(source, label, target)
 
     def read_value(self, node: UUID) -> Any:
+        """
+        Reads value stored in a node.
+
+        Args:
+            node: UUID of the node to read value from
+
+        Returns:
+            Value contained in the node. If no value is found, returns None
+        """
         return self.state.read_value(node)
 
     def read_edge_source(self, edge: UUID) -> UUID:
+        """
+        Reads source element of an edge.
+
+        Args:
+            edge: UUID of the edge to read source element of
+
+        Returns:
+            UUID of source element of the edge
+        """
         result = self.state.read_edge(edge)
         return result[0] if result is not None else result
 
     def read_edge_target(self, edge: UUID) -> UUID:
+        """
+        Reads target element of an edge.
+
+        Args:
+            edge: UUID of the edge to read target element of
+
+        Returns:
+            UUID of target element of the edge
+        """
         result = self.state.read_edge(edge)
         return result[1] if result is not None else result
 
     def read_incoming_edges(self, target: UUID, label=None) -> List[UUID]:
+        """
+        Reads incoming edges of an element. Optionally, filter them based on their label
+
+        Args:
+            target: UUID of the element to read incoming edges for
+            label: value to filter edge labels by
+
+        Returns:
+            List of UUIDs of incoming edges
+        """
         def read_label(_edge: UUID):
             try:
                 label_edge, = self.state.read_outgoing(_edge)
@@ -48,6 +109,16 @@ class Bottom:
         return edges
 
     def read_outgoing_edges(self, source: UUID, label=None) -> List[UUID]:
+        """
+        Reads outgoing edges of an element. Optionally, filter them based on their label
+
+        Args:
+            source: UUID of the element to read outgoing edges for
+            label: value to filter edge labels by
+
+        Returns:
+            List of UUIDs of outgoing edges
+        """
         def read_label(_edge: UUID):
             try:
                 label_edge, = self.state.read_outgoing(_edge)
@@ -65,6 +136,17 @@ class Bottom:
         return edges
 
     def read_incoming_elements(self, target: UUID, label=None) -> List[UUID]:
+        """
+        Reads elements connected to given element via incoming edges. 
+        Optionally, filter them based on the edge label.
+
+        Args:
+            target: UUID of the element to read incoming elements for
+            label: value to filter edge labels by
+
+        Returns:
+            List of UUIDs of elements connected via incoming edges
+        """
         edges = self.read_incoming_edges(target, label)
         if edges is None or len(edges) == 0:
             return []
@@ -72,6 +154,17 @@ class Bottom:
             return [self.read_edge_source(e) for e in edges]
 
     def read_outgoing_elements(self, source: UUID, label=None) -> List[UUID]:
+        """
+        Reads elements connected to given element via outgoing edges. 
+        Optionally, filter them based on the edge label.
+
+        Args:
+            source: UUID of the element to read outgoing elements for
+            label: value to filter edge labels by
+
+        Returns:
+            List of UUIDs of elements connected via outgoing edges
+        """
         edges = self.read_outgoing_edges(source, label)
         if edges is None or len(edges) == 0:
             return []
@@ -79,11 +172,29 @@ class Bottom:
             return [self.read_edge_target(e) for e in edges]
 
     def read_keys(self, element: UUID) -> List[str]:
+        """
+        Retrieve list of outgoing edge labels
+
+        Args:
+            element: UUID of the element to read outgoing edge labels for
+
+        Returns:
+            List of outgoing edge labels
+        """
         key_nodes = self.state.read_dict_keys(element)
         unique_keys = {self.state.read_value(node) for node in key_nodes}
         return list(unique_keys)
 
     def delete_element(self, element: UUID):
+        """
+        Delete an element
+
+        Args:
+            element: UUID of the element to be deleted
+
+        Returns:
+            Nothing
+        """
         src, tgt = self.state.read_edge(element)
         if src is None and tgt is None:
             # node

+ 72 - 2
services/bottom/V1.py

@@ -4,6 +4,10 @@ from services.bottom.V0 import Bottom as BottomV0
 
 
 class Bottom:
+    """
+    Implements services for LTM bottom.
+    Implemented using V0.Bottom
+    """
     def __init__(self, model: UUID, state: State):
         type_model_id = state.read_dict(state.read_root(), "Bottom")
         self.type_model = UUID(state.read_value(type_model_id))
@@ -11,7 +15,16 @@ class Bottom:
         self.bottom = BottomV0(state)
 
     def create_node(self, name: str, value=None):
+        """
+        Creates a node.
 
+        Args:
+            name: node name in model
+            value: value to be stored in the node
+
+        Returns:
+            Nothing
+        """
         if value is None:
             n = self.bottom.create_node()
         else:
@@ -19,6 +32,17 @@ class Bottom:
         self.bottom.create_edge(self.model, n, label=name)
 
     def create_edge(self, name: str, source: str, target: str):
+        """
+        Creates an edge.
+
+        Args:
+            name: edge name in model
+            source: source element of the edge
+            target: target element of the edge
+
+        Returns:
+            Nothing
+        """
         try:
             src, = self.bottom.read_outgoing_elements(self.model, source)
         except ValueError:
@@ -31,6 +55,15 @@ class Bottom:
         self.bottom.create_edge(self.model, e, label=name)
 
     def read_value(self, name: str):
+        """
+        Reads value stored in a node.
+
+        Args:
+            name: name of the node to read value from
+
+        Returns:
+            Value contained in the node. If no value is found, returns None
+        """
         try:
             element, = self.bottom.read_outgoing_elements(self.model, name)
             return self.bottom.read_value(element)
@@ -38,16 +71,47 @@ class Bottom:
             raise RuntimeError(f"No element named {name}")
 
     def read_edge_source(self, name: str):
+        """
+        Reads source element of an edge.
+
+        Args:
+            name: name of the edge to read source element of
+
+        Returns:
+            UUID of source element of the edge
+        """
         try:
             element, = self.bottom.read_outgoing_elements(self.model, name)
-            return self.bottom.read_value(element)
+            return self.bottom.read_edge_source(element)
         except ValueError:
             raise RuntimeError(f"No element named {name}")
 
     def read_edge_target(self, name: str):
-        pass
+        """
+        Reads target element of an edge.
+
+        Args:
+            name: name of the edge to read target element of
+
+        Returns:
+            UUID of target element of the edge
+        """
+        try:
+            element, = self.bottom.read_outgoing_elements(self.model, name)
+            return self.bottom.read_edge_target(element)
+        except ValueError:
+            raise RuntimeError(f"No element named {name}")
 
     def delete_element(self, name: str):
+        """
+        Delete an element
+
+        Args:
+            element: UUID of the element to be deleted
+
+        Returns:
+            Nothing
+        """
         try:
             element, = self.bottom.read_outgoing_elements(self.model, name)
             self.bottom.delete_element(element)
@@ -55,6 +119,12 @@ class Bottom:
             raise RuntimeError(f"No element named {name}")
 
     def list_elements(self):
+        """
+        Lists elements in the model.
+
+        Returns:
+            A list of elements in alphabetical order.
+        """
         tm_names = {}
         for key in self.bottom.read_keys(self.type_model):
             element, = self.bottom.read_outgoing_elements(self.type_model, key)

+ 68 - 0
services/pn.py

@@ -8,6 +8,11 @@ import re
 
 
 class PN:
+    """
+    Implements services for the petri nets LTM.
+    Implementation is done in terms of services provided by LTM-bottom.
+    Implementation is very similar to that in scd.py, which has more extensive comments
+    """
     def __init__(self, model: UUID, state: State):
         ltm_pn_id = state.read_dict(state.read_root(), "PN")
         self.ltm_pn = UUID(state.read_value(ltm_pn_id))
@@ -15,6 +20,15 @@ class PN:
         self.bottom = Bottom(state)
 
     def create_place(self, name: str, tokens: int):
+        """
+        Creates a place element.
+
+        Args:
+            name: name of the place
+
+        Returns:
+            Nothing.
+        """
         # instantiate Place class
         place_node = self.bottom.create_node()  # create place node
         self.bottom.create_edge(self.model, place_node, name)  # attach to model
@@ -44,6 +58,15 @@ class PN:
         self.bottom.create_edge(tokens_link, ltm_pn_link, "Morphism")
 
     def create_transition(self, name: str):
+        """
+        Creates a transition element.
+
+        Args:
+            name: name of the transition
+
+        Returns:
+            Nothing.
+        """
         # instantiate Transition class
         transition_node = self.bottom.create_node()  # create transition node
         self.bottom.create_edge(self.model, transition_node, name)  # attach to model
@@ -62,6 +85,17 @@ class PN:
         self.bottom.create_edge(name_link, ltm_pn_link, "Morphism")
 
     def create_p2t(self, place: str, transition: str, weight: int):
+        """
+        Creates a place to transition  link.
+
+        Args:
+            place: source of the link
+            transition: target of the link
+            weight: weight of the link
+
+        Returns:
+            Nothing.
+        """
         # create p2t link + morphism links
         edge = self.bottom.create_edge(
             *self.bottom.read_outgoing_elements(self.model, place),
@@ -83,6 +117,17 @@ class PN:
         self.bottom.create_edge(weight_link, scd_link, "Morphism")
 
     def create_t2p(self, transition: str, place: str, weight: int):
+        """
+        Creates a transition to place link.
+
+        Args:
+            transition: source of the link
+            place: target of the link
+            weight: weight of the link
+
+        Returns:
+            Nothing.
+        """
         # create t2p link + morphism links
         edge = self.bottom.create_edge(
             *self.bottom.read_outgoing_elements(self.model, transition),
@@ -104,6 +149,12 @@ class PN:
         self.bottom.create_edge(weight_link, scd_link, "Morphism")
 
     def list_elements(self):
+        """
+        Lists elements in the model.
+
+        Returns:
+            A list of elements in alphabetical order.
+        """
         pn_names = {}
         for key in self.bottom.read_keys(self.ltm_pn):
             element, = self.bottom.read_outgoing_elements(self.ltm_pn, key)
@@ -119,6 +170,15 @@ class PN:
             print("{} : {}".format(*elem))
 
     def delete_element(self, name: str):
+        """
+        Deletes an element from the model.
+
+        Args:
+            name: name of the element to delete
+
+        Returns:
+            Nothing.
+        """
         keys = self.bottom.read_keys(self.model)
         r = re.compile(r"{}\..*".format(name))
         to_delete = list(filter(r.match, keys))
@@ -126,3 +186,11 @@ class PN:
             # TODO: find way to solve memory leak, primitive models are not deleted this way
             node, = self.bottom.read_outgoing_elements(self.model, label=key)
             self.bottom.delete_element(node)
+    
+    def to_bottom(self):
+        # already implemented in terms of LTM bottom
+        pass
+
+    def from_bottom(self):
+        # already implemented in terms of LTM bottom
+        pass

+ 50 - 0
services/point/cartesian.py

@@ -5,6 +5,10 @@ from services.primitives.float_type import Float
 
 
 class PointCartesian:
+    """
+    Implements services for the point cartesian LTM.
+    Implementation is done in terms of Python data structures
+    """
     def __init__(self, model: UUID, state: State):
         type_model_id = state.read_dict(state.read_root(), "PointCartesian")
         self.type_model = UUID(state.read_value(type_model_id))
@@ -14,27 +18,66 @@ class PointCartesian:
         self.point = None
 
     def create_point(self, x: float, y: float):
+        """
+        Creates a point.
+
+        Args:
+            x: x coordinate
+            y: y coordinate
+
+        Returns:
+            Nothing.
+        """
         if self.point is None:
             self.point = (x, y)
         else:
             raise RuntimeError("A PointCartesian model can contain at most 1 point.")
 
     def read_point(self):
+        """
+        Reads point.
+
+        Returns:
+            Textual representation of the point data.
+        """
         if self.point is None:
             raise RuntimeError("No point found in model.")
         else:
             return f"(X = {self.point[0]}, Y = {self.point[1]})"
 
     def delete_point(self):
+        """
+        Deletes point.
+
+        Returns:
+            Nothing.
+        """
         self.point = None
 
     def apply_movement(self, delta_x: float, delta_y: float):
+        """
+        Moves point.
+
+        Args:
+            delta_x: change in x dimension
+            delta_y: change in y dimension
+
+        Returns:
+            Nothing.
+        """
         if self.point is not None:
             self.point = (self.point[0] + delta_x, self.point[1] + delta_y)
         else:
             raise RuntimeError("No point found in model.")
 
     def to_bottom(self):
+        """
+        Converts implementation specific model representation to 
+        canonical representation.
+
+        Returns:
+            Nothing.
+        """
         bottom = Bottom(self.state)
         # clear residual model
         for element in bottom.read_outgoing_elements(self.model):
@@ -69,6 +112,13 @@ class PointCartesian:
         bottom.create_edge(c2_link, ltm_point_link, "Morphism")
 
     def from_bottom(self):
+        """
+        Converts canonical representation to 
+        implementation specific model representation.
+
+        Returns:
+            Nothing.
+        """
         bottom = Bottom(self.state)
         keys = bottom.read_keys(self.model)
         x_key, = filter(lambda k: k.endswith(".c1"), keys)

+ 50 - 0
services/point/polar.py

@@ -7,6 +7,10 @@ import math
 
 
 class PointPolar:
+    """
+    Implements services for the point polar LTM.
+    Implementation is done in terms of Python data structures
+    """
     def __init__(self, model: UUID, state: State):
         type_model_id = state.read_dict(state.read_root(), "PointPolar")
         self.type_model = UUID(state.read_value(type_model_id))
@@ -16,27 +20,66 @@ class PointPolar:
         self.point = None
 
     def create_point(self, r: float, theta: float):
+        """
+        Creates a point.
+
+        Args:
+            r: distance from pole
+            theta: angle from polar axis
+
+        Returns:
+            Nothing.
+        """
         if self.point is None:
             self.point = (r, theta)
         else:
             raise RuntimeError("A PointPolar model can contain at most 1 point.")
 
     def read_point(self):
+        """
+        Reads point.
+
+        Returns:
+            Textual representation of the point data.
+        """
         if self.point is None:
             raise RuntimeError("No point found in model.")
         else:
             return f"(r = {self.point[0]}, \u03B8 = {self.point[1]})"
 
     def delete_point(self):
+        """
+        Deletes point.
+
+        Returns:
+            Nothing.
+        """
         self.point = None
 
     def apply_movement(self, delta_r: float, delta_theta: float):
+        """
+        Moves point.
+
+        Args:
+            delta_r: change in distance from pole
+            delta_theta: change in angle from polar axis
+
+        Returns:
+            Nothing.
+        """
         if self.point is not None:
             self.point = (self.point[0] + delta_r, self.point[1] + delta_theta)
         else:
             raise RuntimeError("No point found in model.")
 
     def to_bottom(self):
+        """
+        Converts implementation specific model representation to 
+        canonical representation.
+
+        Returns:
+            Nothing.
+        """
         x = self.point[0]*math.cos(self.point[1])  # x = r * cos(theta)
         y = self.point[0]*math.sin(self.point[1])  # y = r * sin(theta)
         bottom = Bottom(self.state)
@@ -73,6 +116,13 @@ class PointPolar:
         bottom.create_edge(c2_link, ltm_point_link, "Morphism")
 
     def from_bottom(self):
+        """
+        Converts canonical representation to 
+        implementation specific model representation.
+
+        Returns:
+            Nothing.
+        """
         bottom = Bottom(self.state)
         keys = bottom.read_keys(self.model)
         x_key, = filter(lambda k: k.endswith(".c1"), keys)

+ 123 - 6
services/scd.py

@@ -9,6 +9,10 @@ import re
 
 
 class SCD:
+    """
+    Implements services for the simple class diagrams LTM.
+    Implementation is done in terms of services provided by LTM-bottom
+    """
     def __init__(self, model: UUID, state: State):
         type_model_id = state.read_dict(state.read_root(), "SCD")
         self.scd_model = UUID(state.read_value(type_model_id))
@@ -16,16 +20,33 @@ class SCD:
         self.bottom = Bottom(state)
 
     def create_class(self, name: str, abstract: bool = None, min_c: int = None, max_c: int = None):
+        """
+        Creates an instance of a class.
+
+        Args:
+            name: the name of the class to be created
+            abstract: indicates whether or not the class is an abstract class
+            min_c: lower bound for class multicplicity
+            max_c: upper bound for class multicplicity
+
+        Returns:
+            Nothing.
+        """
         
         def set_cardinality(bound: str, value: int):
+            """ Helper for setting cardinality attributes """
+            # Create cardinality attribute root node
+            # Do note that this is an instance of a ModelRef!
             _c_model = self.bottom.create_node()
             Integer(_c_model, self.bottom.state).create(value)
-            _c_node = self.bottom.create_node(str(_c_model))
-            self.bottom.create_edge(self.model, _c_node, f"{name}.{bound}_cardinality")
-            _c_link = self.bottom.create_edge(class_node, _c_node)
-            self.bottom.create_edge(self.model, _c_link, f"{name}.{bound}_cardinality_link")
+            _c_node = self.bottom.create_node(str(_c_model))  # store UUID of primitive value model
+            self.bottom.create_edge(self.model, _c_node, f"{name}.{bound}_cardinality")  # link to model root
+            _c_link = self.bottom.create_edge(class_node, _c_node)  # link class to attribute
+            self.bottom.create_edge(self.model, _c_link, f"{name}.{bound}_cardinality_link")  # link attr link to model root
+            # retrieve types from metamodel
             _scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Integer")
             _scd_link, = self.bottom.read_outgoing_elements(self.scd_model, f"Class_{bound}_cardinality")
+            # type newly created elements
             self.bottom.create_edge(_c_node, _scd_node, "Morphism")
             self.bottom.create_edge(_c_link, _scd_link, "Morphism")
         
@@ -35,6 +56,7 @@ class SCD:
         scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Class")  # retrieve type
         self.bottom.create_edge(class_node, scd_node, "Morphism")  # create morphism link
         if abstract is not None:
+            # operations similar to set_cardinality function defined above
             abstract_model = self.bottom.create_node()
             Boolean(abstract_model, self.bottom.state).create(abstract)
             abstract_node = self.bottom.create_node(str(abstract_model))
@@ -53,8 +75,24 @@ class SCD:
     def create_association(self, name: str, source: str, target: str,
                            src_min_c: int = None, src_max_c: int = None,
                            tgt_min_c: int = None, tgt_max_c: int = None):
+        """
+        Creates an instance of an association.
+
+        Args:
+            name: the name of the association to be created
+            source: the name of the source of the association
+            target: the name of the target of the association
+            src_min_c: lower bound for source multicplicity
+            src_max_c: upper bound for source multicplicity
+            tgt_min_c: lower bound for target multicplicity
+            tgt_max_c: upper bound for target multicplicity
+
+        Returns:
+            Nothing.
+        """
 
         def set_cardinality(bound: str, value: int):
+            # similar to set_cardinality function defined in create_class
             _c_model = self.bottom.create_node()
             Integer(_c_model, self.bottom.state).create(value)
             _c_node = self.bottom.create_node(str(_c_model))
@@ -66,7 +104,7 @@ class SCD:
             self.bottom.create_edge(_c_node, _scd_node, "Morphism")
             self.bottom.create_edge(_c_link, _scd_link, "Morphism")
 
-        # create class + attributes + morphism links
+        # create association + attributes + morphism links
         assoc_edge = self.bottom.create_edge(
             *self.bottom.read_outgoing_elements(self.model, source),
             *self.bottom.read_outgoing_elements(self.model, target),
@@ -84,6 +122,15 @@ class SCD:
             set_cardinality("target_upper", tgt_max_c)
 
     def create_global_constraint(self, name: str):
+        """
+        Defines a global constraint element.
+
+        Args:
+            name: the name of the global constraint to be created
+
+        Returns:
+            Nothing.
+        """
         # create element + morphism links
         element_node = self.bottom.create_node()  # create element node
         self.bottom.create_edge(self.model, element_node, name)  # attach to model
@@ -91,6 +138,15 @@ class SCD:
         self.bottom.create_edge(element_node, scd_node, "Morphism")  # create morphism link
 
     def create_attribute(self, name: str):
+        """
+        Defines an attribute element.
+
+        Args:
+            name: the name of the attribute to be created
+
+        Returns:
+            Nothing.
+        """
         # create element + morphism links
         element_node = self.bottom.create_node()  # create element node
         self.bottom.create_edge(self.model, element_node, name)  # attach to model
@@ -98,15 +154,28 @@ class SCD:
         self.bottom.create_edge(element_node, scd_node, "Morphism")  # create morphism link
 
     def create_attribute_link(self, source: str, target: str, name: str, optional: bool):
+        """
+        Defines an attribute link element.
+
+        Args:
+            source: element attribute will be attached to
+            target: attribute element
+            name: name of the attribute
+            optional: indicates whether attribute is optional
+
+        Returns:
+            Nothing.
+        """
         # create attribute link + morphism links
         assoc_edge = self.bottom.create_edge(
             *self.bottom.read_outgoing_elements(self.model, source),
             *self.bottom.read_outgoing_elements(self.model, target),
-        )  # create inheritance edge
+        )  # create v edge
         self.bottom.create_edge(self.model, assoc_edge, f"{source}_{name}")  # attach to model
         scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink")  # retrieve type
         self.bottom.create_edge(assoc_edge, scd_node, "Morphism")  # create morphism link
         # name attribute
+        # Do note that this is an instance of a ModelRef!
         name_model = self.bottom.create_node()
         String(name_model, self.bottom.state).create(name)
         name_node = self.bottom.create_node(str(name_model))
@@ -118,6 +187,7 @@ class SCD:
         self.bottom.create_edge(name_node, scd_node, "Morphism")
         self.bottom.create_edge(name_link, scd_link, "Morphism")
         # optional attribute
+        # Do note that this is an instance of a ModelRef!
         optional_model = self.bottom.create_node()
         Boolean(optional_model, self.bottom.state).create(optional)
         optional_node = self.bottom.create_node(str(optional_model))
@@ -130,6 +200,16 @@ class SCD:
         self.bottom.create_edge(optional_link, scd_link, "Morphism")
 
     def create_model_ref(self, name: str, model: UUID):
+        """
+        Defines a model ref element.
+
+        Args:
+            name: name of the model ref
+            model: uuid of the external model
+
+        Returns:
+            Nothing.
+        """
         # create element + morphism links
         element_node = self.bottom.create_node(str(model))  # create element node
         self.bottom.create_edge(self.model, element_node, name)  # attach to model
@@ -137,6 +217,16 @@ class SCD:
         self.bottom.create_edge(element_node, scd_node, "Morphism")  # create morphism link
 
     def create_inheritance(self, child: str, parent: str):
+        """
+        Defines an inheritance link element.
+
+        Args:
+            child: child element of the inheritance relationship
+            parent: parent element of the inheritance relationship
+
+        Returns:
+            Nothing.
+        """
         # create inheritance + morphism links
         assoc_edge = self.bottom.create_edge(
             *self.bottom.read_outgoing_elements(self.model, child),
@@ -147,6 +237,16 @@ class SCD:
         self.bottom.create_edge(assoc_edge, scd_node, "Morphism")  # create morphism link
 
     def add_constraint(self, element: str, code: str):
+        """
+        Defines a constraint on an element.
+
+        Args:
+            element: element the constraint is attached to
+            code: constraint code
+
+        Returns:
+            Nothing.
+        """
         element_node, = self.bottom.read_outgoing_elements(self.model, element)  # retrieve element
         # code attribute
         code_node = self.bottom.create_node(code)
@@ -159,6 +259,12 @@ class SCD:
         self.bottom.create_edge(code_link, scd_link, "Morphism")
 
     def list_elements(self):
+        """
+        Lists elements in the model.
+
+        Returns:
+            A list of elements in alphabetical order.
+        """
         scd_names = {}
         for key in self.bottom.read_keys(self.scd_model):
             element, = self.bottom.read_outgoing_elements(self.scd_model, key)
@@ -173,6 +279,15 @@ class SCD:
         return sorted(unsorted)
 
     def delete_element(self, name: str):
+        """
+        Deletes an element from the model.
+
+        Args:
+            name: name of the element to delete
+
+        Returns:
+            Nothing.
+        """
         keys = self.bottom.read_keys(self.model)
         r = re.compile(r"{}\..*".format(name))
         to_delete = list(filter(r.match, keys))
@@ -182,9 +297,11 @@ class SCD:
             self.bottom.delete_element(node)
 
     def to_bottom(self):
+        # already implemented in terms of LTM bottom
         pass
 
     def from_bottom(self):
+        # already implemented in terms of LTM bottom
         pass