123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- *******************
- Python API Overview
- *******************
- Model Execution Phases
- ######################
- There are two phases in model execution.
- The first phase is **build phase**, where all the components
- of the simulated system are defined by creating objects such as ReactionRule,
- ReleaseSite, or Count. All class attributes of these objects
- are accessible, and any modifications are allowed.
- The second phase is entered by calling Model.initialize() and is called
- **simulation phase**. What precisely Model.initialize() does is that it
- transforms information in API objects (such as the ReactionRule object)
- into an internal MCell4 representation. Thus, modifying attributes or
- adding new objects, e.g., to reaction rules or geometry objects won't have
- any effect and attempting to do modifications raises an error in most cases.
- Some exceptions that are explicitly described in the API documentation,
- for instance changing rate of a reaction is allowed.
- Accessing API Object Attributes
- ###############################
- Attributes of API objects can be divided into **scalar**, **object**,
- and **vector** attributes.
- Types of scalar attributes are str (string), bool, float, integer, or enumerations.
- Types of object attributes are API classes and represent references to
- such objects.
- And the vector attributes are those that use ``List`` in their data type.
-
- Internally, getter and setter methods are generated for attributes and they
- are called when an attribute is read or set.
- Scalar Attributes
- *****************
- As an example, let's take a look on the forward rate of a ReactionRule object
- that is a scalar attribute.
- .. code-block:: python
- # get a reference to a ReactionRule object
- reaction = model.find_reaction_rule('my_reaction')
- # reading calls C++ method ReactionRule.get_fwd_rate()
- a = reaction.fwd_rate
-
- # writing calls C++ method ReactionRule.set_fwd_rate(float)
- reaction.fwd_rate = 1e5
-
- Reads of a scalar attribute are always allowed and a copy of the value is returned,
- therefore no change is propagated back.
- When a write to a scalar attribute is executed, the setter method is executed and
- when we are in the build phase, MCell allows any changes. In the simulation phase,
- the majority of writes cause an exception that disallows modification except for
- several cases where the modification is explicitly allowed, such as for the fwd_rate
- in our example. The way how the modifications are handled is that the ReactionRule
- contains a link to the internal MCell4 reaction representation and the call to
- set_fwd_rate, besides modifying the attribute value, also informs the MCell4
- engine to change the rate wherever needed.
- Object Attributes
- *****************
- Object attributes behave practically in the same way as scalar attributes.
- The only difference that instead of returning a value, a reference to an object
- is returned.
- .. code-block:: python
- # get a reference to a ReleaseSite object
- rel_site = model.find_release_site('rel_a')
-
- # get a reference to a Region object used by the ReleaseSite
- region = rel_site.region
-
- # create a 'box' object that we will use as a new region
- new_region = geometry_utils.create_box('box', 1)
-
- # and replace the original region, allowed only in build phase
- rel_site.region = new_region
-
- Reading a reference is allowed at all times. Accessing individual attributes
- of the object follows the same rules as for any other API object.
- Writes (replacing the reference) are only allowed in the build phase.
- No attribute of the object type is allowed to be changed in the simulation phase.
- Vector Attributes
- *****************
- In order to allow propagation of modifications of lists on the Python side to the
- C++ side and vice versa, special vector classes are used on the Python side instead
- of the standard list type.
- These vector attributes try to emulate the Python list data type, but currently allow only a
- subset of operations provided for lists.
- An example for states of a ComponentType is shown here:
- .. code-block:: python
- # create an object of class ComponentType
- # initially only one state with name 's1' is set
- ct_u = m.ComponentType('u', states = ['s1'])
-
- # accessing ct_u.states causes a getter method to be called that
- # returns a reference to an object of class VectorStr that
- # provides method append
- ct_u.states.append('s2')
- # we can also modify a specific item in this vector
- ct_u.states[1] = '2'
-
- Reading a vector attribute is always allowed. The returned reference to the Vector
- object is not guarded against writes and there are no semantic checks.
- In the build phase, such modifications are used when the model is initialized.
- However, in the simulation phase, such modifications are ignored by the MCell4 engine
- and no error is reported.
- When writing to a vector attribute, the original vector is replaced by the new one.
- This replacement is allowed in the build phase, but when attempting to replace the whole vector in the
- simulation phase, an error is reported.
- Object Cloning Support
- ######################
- API objects support shallow and deep copy operations provided through Python methods
- *copy.copy(x)* and *copy.deepcopy(x[, memo])*.
- Cloning is allowed even if the model was already initialized.
- However, all links in the cloned object to the initialized model are lost. E.g., it is not possible
- to clone a *Count* object and then call the clone's method *get_current_value* because the new object
- will be uninitialized and won't know which model's internal count it is referencing.
- Due to MCell4 being implemented primarily in C++, there is one significant difference
- in *copy* from Python semantics. All lists are copied
- by value, not by reference as Python's lists since they are internally implemented
- with C++ std::vector. This behavior is shown in the following code snippet:
- .. code-block:: python
- ct = m.ComponentType('u', states = ['0', '1'])
-
- ct_copy = copy.copy(ct3)
- ct_copy.states[0] = 'X' # change item in a copied list
-
- assert ct.states == ['0', '1']
- assert ct_copy.states == ['X', '1']
- For *copy.deepcopy(x[, memo])*, the optional *memo* argument is ignored.
- Object Debug Printouts
- ######################
- Each of the API objects provides method *__str__* to convert it to
- a string representation that shows the contents of this object.
- This method is used when a method *print* or cast *str(...)* is used.
- By default, not all details are shown for all objects because that would make the
- output too lengthy (especially for the *GeometryObject* and *Complex* classes).
- The method *__str__* has two arguments *all_details* (default False) and
- *ind* (indent, default ""). To obtain access to all details, set *all_details*
- to True.
- .. code-block:: python
- cplx = m.Complex('A(x~0)')
-
- print(cplx) # prints only 'A(x~0)'
-
- print(cplx.__str__(True)) # prints a detailed representation
|