The Trivial Rocket Example

To help us learn CML better, lets go through a trivial rocket propulsion example. Since there are actually two parts to any example: (1) the domain language and (2) the scenario file, they have been colored to differentiate between them.

Styles are supported in both browsers: Netscape 3.0 and above and IE 4.0 and above. If you do not see "domain language" in blue and "scenario file" in green then turn on your style sheets. If you do not have either of the above browsers, fear not, for the two files are included at the end of the lesson.


In the beginning was a thing. . .

To make simulation of physical system, we need to be able to describe the physical parts of that system. In CML, building "things" is a two step process: first we need to define the type of thing and then create an instance of our said thing. It is helpful to think of a type of a thing as a category: for example, a dog could be a type. However, no specific instance of a dog will exist until we instantiate one: if we say Lucy's dog, Fido, then Fido becomes an instantiation of the type dog.

Due to the miracle of QR (Qualitative Reasoning), the design and construction of chemical rockets is no more difficult than the instantiation of small dogs. For example to instiantiate our friend Fido, we say:

The same is true of rockets: first we know that rockets (liquid fueled ones anyway) consist mainly of two tanks (fuel + oxidizer), a rocket engine, and an igniter for that engine. Therefore in the scenario file we need to instantiate the tanks, the engine, and the igniter.

(rassert! (container fuel-tank1))
(rassert! (container oxidizer-tank1))
(rassert! (rocket-engine engine1))
(rassert! (switch igniter1))

The rassert! states that there are two tanks: fuel-tank1 and oxidizer-tank1 which are of type container, an engine1 which is of type rocket-engine, and an igniter1 which is of type switch. Gizmo does not automatically know what we mean when we talk about containers, rocket-engines, or switches--we need to define their types in the domain file.

(defEntity container)
(defEntity rocket-engine)
(defEntity switch)

A defEntity states there is a type of object: a (defEntity container) states that there exists a type whose name is "container". And a rassert! on that type "container" tells Gizmo to make an instance of that type. Our rocket now exists but there is no fuel or oxidizer so we have to define those thing too:

(rassert! (rocket-fuel liquid-hydrogen))
(rassert! (rocket-oxidizer liquid-oxygen))
(defEntity rocket-fuel 
  :subclass-of (fluid))
(defEntity rocket-oxidizer
  :subclass-of (fluid))
(defEntity fluid)

This fragment states that our rocket has two working fluids: liquid-hydrogen and liquid-oxygen. These two instances of working fluids are of type rocket-fuel and rocket oxidizer, respectively. The :subclass-of field shows that both the rocket-fuel and the rocket-oxidizer are derived from the base type "fluid" which does not contain a subclass field. Fluid is a base types because it does not contain a subclass field. All derived types must reference valid existing base types! Such as the case in C++, derived types in CML retain the characteristics of the base type. Any operation which can be performed on the base type can also be performed on the derived type. This method of derived object inheritance can be seen in the following example:

(defModelFragment rocket-fluid-flow
  :participants ((src :type container)
                 (dest :type rocket-engine)
                 (stuff :type fluid
                   :constraints ((contains src stuff))))
  :conditions ((aligned src dest)
               (> (amount-of stuff src) 0))
  :quantities ((flow-rate-of)))

The rocket-fluid-flow states that there exist a movement of fluid between a tank and a rocket-engine. By using derived types, we can avoid having to define a rocket-fuel-fluid-flow and a rocket-oxidizer-fluid-flow. Instead we just define a flow on the base "fluid" type and both rocket-fuel and rocket-oxidizer will satisfy the participants slot of this model fragment. In the next section, we will discuss more about the model fragments and how their relationships to entities.


move thing move

Other than static physical objects, there are also changes in the real world. Theses changes can be described via model fragments. Model fragments describe changes such a water boiling, rockets firing, heat flowing from hot places to cold places and other phenomena that changes a world from one state to another.

In the following model fragment, we describe a rocket-fluid-flow which is a fluid flow from a tank to an engine.

(defModelFragment rocket-fluid-flow
  :participants ((src :type container)
                 (dest :type rocket-engine)
                 (stuff :type fluid
                   :constraints ((contains src stuff))))
  :conditions ((aligned src dest)
               (> (amount-of stuff src) 0))
  :quantities ((flow-rate-of)))

The defModelFragment can take colon-parameter arguments of several types (:subclass-of, :participants, :conditions, :consequences...). In this fragment, we see the three of the more commonly used parameter arguments: participants, conditions, and quantities.

The :participants argument states what entities are necessary before the model fragment will be instantiated. In rocket-fluid-flow we see that it requires three participants: a container, a rocket-engine, and a fluid. These participants need to be defEntitied earlier in the domain file. Notice that on the fluid type there is a :constraints which states that the type has restriction placed upon it. The constraint on the fluid type states that the form

(contains src the-stuff)

must exist. This form can be translated into the human readable sentence: the src, a container, must contain the-stuff, a fluid. Therefore the form

(the-stuff :type fluid
  :constraints ((contains src the-stuff)))

means that for the type fluid in this instance, there needs to be a container which contains the-stuff. The astute reader may ask were does the "contains" predicate come from: the answer is that it must be instantiated:

(rassert! (contains fuel-tank1 liquid-hydrogen))
(rassert! (contains oxidizer-tank1 liquid-oxygen))

Next we have the :conditions parameter. The conditions parameter controls not the instantiation of the model fragment, only the activeness. If a model fragment meets the contains all the necessary participants and fulfills all the constraints associated with those participants the model fragment is instantiated. The conditions, a list, then determines the whether the model fragment is active.

:conditions ((aligned src dest)
             (> (amount-of stuff src) 0))

In this model fragment, there are two necessary and sufficient conditions activation: (1) the source must be aligned with the destination (2) the source must contain a non-zero quantity of the flowable stuff. The word "aligned" in models at ILS usually means that the source and the destination are connected: there exists a path, rail, tube for the dog, train, fluid to go from one the source to the destination. And since the fuel/oxidizer tanks and the engine are connected:

(assume! '(aligned fuel-tank1 engine1) :initial-condition)
(assume! '(aligned oxidizer-tank1 engine1) :initial-condition)

And since the tanks are filled:

(assume! '(> (amount-of liquid-hydrogen fuel-tank1) 0) :initial-condition)
(assume! '(> (amount-of liquid-oxygen oxidizer-tank1) 0) :initial-condition)

Note that these scenario definitions are not rassert! instead they are assume! the difference is that rassert! is used to state fragments which will remain unchanged for the lifetime of the model; however, assume! is used to state forms which may change during the lifetime of the model. Therefore the "aligned" forms may be rasserted if the purpose of the model does not include failure, blockage, or intentional stoppage (e.g. stopping the pump) of the connections between the fuel/oxidizer tanks and the engine. The "amount-of" forms may be rasserted if the fuel/oxidizer tanks will always contain fuel/oxidizer. In our model, since neither the "aligned" form nor the "amount-of" form may not hold true for the lifetime of the model and therefore both must be assumed.

The :quantities parameter state the which variables have a numeric value associated with them.

:quantities ((flow-rate-of)))

In this case the quantities refer to a flow-rate-of; therefore, a flow-rate-of numeric quantity needs to be defined:

(defQuantityFunction flow-rate-of (?fluid-flow))

The defQuantityFunction is usually called with a name and a ?variable form. The defQuantityFunction is stating that the flow-rate-of a variable named ?fluid-flow is a numeric quantity. Note that ?fluid-flow is not a special variable; it is just a placeholder. The following two functions are identical:

(defQuantityFunction flow-rate-of (?fluid-flow))
(defQuantityFunction flow-rate-of (?a-random-variable))

things within things within things

Finally, we are now at the core of the rocket example: how to the fire the engines. As demonstrated previously since the liquid engine burn is a physical process, we use a defModelFragment. A liquid engine burn takes several participants: the flow of fuel into the engine, the oxidizer flow into the engine, the state of the igniter, and the engine itself. Therefore we write the following participants:

(defModelFragment liquid-engine-burning
  :participants ((fuel :type rocket-fuel)
                 (oxidizer :type rocket-oxidizer)
                 (the-fuel-flow :type rocket-fluid-flow
                   :constraints ((stuff-of the-fuel-flow fuel)))
                 (the-oxidizer-flow :type rocket-fluid-flow
                   :constraints ((stuff-of the-oxidizer-flow oxidizer)))
                 (the-engine :type rocket-engine
                   :constraints 
                   ((has-participant-role the-engine dest the-fuel-flow)
                    (has-participant-role the-engine dest the-oxidizer-flow)))
                 (the-igniter :type switch
                   :constraints ((controls the-igniter the-engine))))
  :quantities ((thrust))
  :conditions ((active the-fuel-flow)
               (active the-oxidizer-flow)
               (is-on the-igniter))
  :consequences ((qprop thrust (flow-rate-of the-fuel-flow))
                 (qprop (thrust :self) (flow-rate-of the-oxidizer-flow))))

Notice that there are two more participant roles than we expected: namely fuel and oxidizer. This is due to the constraints in the-fuel-flow and the-oxidizer-flow. In the-fuel-flow we have to limit the fluid traveling in the-fuel-flow to fuel only and keep out oxidizers; similarly, we need to keep fuel out of the oxidizer flow. The constraint:

(the-fuel-flow :type rocket-fluid-flow
  :constraints ((stuff-of the-fuel-flow fuel)))

is another type of constraint. It is not a direct constraint like earlier mentioned contains constraint where the constraint form is specified in the scenario file but is instead a constraint based on "automagically" generated procedural forms of other defModelFragments.

The "stuff-of" is automatically generated from the rocket-fluid-flow model fragment:

(defModelFragment rocket-fluid-flow
  :participants ((src :type container)
                 (dest :type rocket-engine)
                 (stuff :type fluid
                   :constraints ((contains src stuff))))

where stuff becomes stuff-of, src becomes src-of, dest becomes dest-of. And because the-fuel-flow is a rocket-fluid-flow and fuel is a rocket-fuel then

(stuff-of the-fuel-flow fuel)

can be expanded to read the roughly the following:

(= (the participant "stuff" in rocket-fluid-flow)
   (rocket-fuel))

Another method of stating the procedural-"of" form of the participant role is the predicate has-participant-role. The two are equivalent:

((dest-of the-fuel-flow the-engine)
 (dest-of the-oxidizer-flow the-engine))

((has-participant-role the-engine dest the-fuel-flow)
 (has-participant-role the-engine dest the-oxidizer-flow)))

and in this example we show both of them for demonstrative purposes. They can be interchanged. The last participant the-igniter also has a constraint but it is a direct constraint like the "contains" constraint discussed earlier. It requires the following to be defined in the scenario:

(rassert! (controls igniter1 engine1))

After our participants are defined, notice that a rocket engine does produce a measurable numeric quantity, thrust. The thrust therefore is defined in the :quantities parameter and is then defQuantitiyFunctioned.

  :quantities ((thrust))

(defQuantityFunction thrust (?rocket-burn-process)
  :=> (liquid-engine-burning ?rocket-burn-process))

**** NEED SOMETHING HERE ABOUT IMPLICATION ASENTENCE. DON'T UNDERSTAND WHAT IT DOES. ****

We next have a conditions clause which is reasonably similar to the one above in rocket-fluid-flow. The only new thing predicate is the activity predicate:

  :conditions ((active the-fuel-flow)
               (active the-oxidizer-flow)
               (is-on the-igniter))

In this conditions list, there are two types of conditions: (1) the active predicate (2) directly stated forms. The active predicate is usually followed by a participant; the active form is true if the participant is true (conditions met) and instantiated (constraints met). Directly stated forms were discussed earlier in rocket-fluid-flow. The directly stated forms require instantiation in the scenario file:

(assume! '(is-on igniter1) :initial-condition)

Once the liquid-engine-burning model fragment is instantiated and active. Then it causes an effect in the world. The effects of the model fragment are stated in the :consequences parameter. There three types of effects that a model fragment could have upon the simulation: qualitative proportionality, direct influence, and **** WHATEVER C+ C- IS****. If in this simple model fragment we assume that thrust is a qualitative proportionality of both the flow rate of the fuel and the flow rate of the oxidizer then the consequences could be stated as:

  :consequences ((qprop thrust (flow-rate-of the-fuel-flow))
                 (qprop (thrust :self) (flow-rate-of the-oxidizer-flow))))

For this example,a qprop states (Forbus, BPS, 353) that (1) there is some function which determines the thrust (2) determines that the thrust depends on flow-rate-of the-fuel-flow and (3) in increasingly monotonic in it's dependence on the flow-rate.


simple rocket domain model

(in-package :data)
;;;; ------------------------------------------------------------------------------

(defEntity fluid)
(defEntity rocket-fuel 
  :subclass-of (fluid))
(defEntity rocket-oxidizer
  :subclass-of (fluid))

(defEntity container)
(defEntity rocket-engine)
(defEntity switch)

(defQuantityFunction flow-rate-of (?fluid-flow))

(defModelFragment rocket-fluid-flow
  :participants ((src :type container)
                 (dest :type rocket-engine)
                 (stuff :type fluid
                   :constraints ((contains src stuff))))
  :conditions ((aligned src dest)
               (> (amount-of stuff src) 0))
  :quantities ((flow-rate-of)))

(defQuantityFunction thrust (?rocket-burn-process)
  :=> (liquid-engine-burning ?rocket-burn-process))

(defModelFragment liquid-engine-burning
  :participants ((fuel :type rocket-fuel)
                 (oxidizer :type rocket-oxidizer)
                 (the-fuel-flow :type rocket-fluid-flow
                   :constraints ((stuff-of the-fuel-flow fuel)))
                 (the-oxidizer-flow :type rocket-fluid-flow
                   :constraints ((stuff-of the-oxidizer-flow oxidizer)))
                 (the-engine :type rocket-engine
                   :constraints 
                   ;; Equivalent:
                   ;; ((dest-of the-fuel-flow the-engine)
                   ;;  (dest-of the-oxidizer-flow the-engine))
                   ((has-participant-role the-engine dest the-fuel-flow)
                    (has-participant-role the-engine dest the-oxidizer-flow)))
                 (the-igniter :type switch
                   :constraints ((controls the-igniter the-engine))))
  :quantities ((thrust))
  :conditions ((active the-fuel-flow)
               (active the-oxidizer-flow)
               (is-on the-igniter))
  :consequences ((qprop thrust (flow-rate-of the-fuel-flow))
                 (qprop (thrust :self) (flow-rate-of the-oxidizer-flow))))

simple rocket scenario file

(in-package :data)
;;;; ------------------------------------------------------------------------------

;; entities
(rassert! (container fuel-tank1))
(rassert! (container oxidizer-tank1))
(rassert! (rocket-engine engine1))
(rassert! (switch igniter1))
(rassert! (rocket-fuel liquid-hydrogen))
(rassert! (rocket-oxidizer liquid-oxygen))

;;;; ------------------------------------------------------------------------------
;; structural relations
(rassert! (controls igniter1 engine1))
(rassert! (contains fuel-tank1 liquid-hydrogen))
(rassert! (contains oxidizer-tank1 liquid-oxygen))

;;;; ------------------------------------------------------------------------------
;; state conditions
(assume! '(is-on igniter1) :initial-condition)
(assume! '(> (amount-of liquid-hydrogen fuel-tank1) 0) :initial-condition)
(assume! '(> (amount-of liquid-oxygen oxidizer-tank1) 0) :initial-condition)
(assume! '(aligned fuel-tank1 engine1) :initial-condition)
(assume! '(aligned oxidizer-tank1 engine1) :initial-condition)