Getting Started With PyCATSHOO-PYCV1228
Getting Started With PyCATSHOO-PYCV1228
2 Not exhaustive review of studies and test cases carried out with PyCATSHOO 3
2.1 Heated Room . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2 Heated Tank . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3 Operating simulation of cascaded spillways . . . . . . . . . . . . . . . . . . . . . . . . 7
2.4 RLC Circuit modeling and simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5 Cooling system of spent fuel pool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3 Basic concepts 11
3.1 Implementing process overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.1.1 Generic modeling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.1.2 Specific system modeling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.3 Creation of a quantification model . . . . . . . . . . . . . . . . . . . . . . . . . 18
4 Generic modeling 18
4.1 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.1.1 addVariable() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.1.2 setXValue() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.1.3 value() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.2 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.2.1 addReference() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.2.2 cnctCount(), value(), xValue()) . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.2.3 sumValue() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.2.4 productValue() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.2.5 Reference.andValue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.2.6 Reference.orValue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.3 Message boxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.3.1 addMessageBox() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.3.2 addMessageBoxImport(), addImport() . . . . . . . . . . . . . . . . . . . . . . . 21
4.3.3 addMessageBoxExport(), addExport() . . . . . . . . . . . . . . . . . . . . . . . 21
4.4 Automata, states and transitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.4.1 addAutomaton() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.4.2 addState() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.4.3 setInitState() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.4.4 currentState() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.4.5 addTransition() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.4.6 setDistLaw() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.4.7 addTarget() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.4.8 setCondition() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.4.9 setInterruptible() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.4.10 setModifiable() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.5 Events and sensitive methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.5.1 addStartMethod() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.5.2 addSensitiveMethod() for states . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.5.3 addSensitiveMethod() for automata . . . . . . . . . . . . . . . . . . . . . . . . 27
1
4.5.4 addSensitiveMethod() for references . . . . . . . . . . . . . . . . . . . . . . . . 27
4.5.5 addSensitiveMethod() for transitions . . . . . . . . . . . . . . . . . . . . . . . 28
4.6 PDMP Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.6.1 addPDMPManager() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.6.2 addPDMPODEVariable(), addODEVariable() . . . . . . . . . . . . . . . . . . . 30
4.6.3 addPDMPExplicitVariable(), addExplicitVariable() . . . . . . . . . . . . . . . . 30
4.6.4 addPDMPEquationMethod(), addEquationMethod() . . . . . . . . . . . . . . 30
4.6.5 setDvdtODE() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.6.6 setDValue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.6.7 addPDMPBoundaryCheckerMethod(), addBoundaryCheckerMethod() . . . 32
4.6.8 addPDMPWatchedTransition() . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
5 System construction 33
6 Quantification 34
6.1 Setting parameters’ and initial values’ files . . . . . . . . . . . . . . . . . . . . . . . . 34
6.1.1 File of parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
6.1.2 File of initial values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
6.2 Call to the system construction method . . . . . . . . . . . . . . . . . . . . . . . . . . 39
6.3 Construction of prior simulation indicators . . . . . . . . . . . . . . . . . . . . . . . . 40
6.4 Setting sequence filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.5 Setting simulation parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
6.6 Launching simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.7 Setting post-simulation indicators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.8 Post-simulation sequences’ filtering and printing . . . . . . . . . . . . . . . . . . . . . 47
6.9 Loading a results’ file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2
1 Introduction
Classic methods of probabilistic safety assessments (PSA), often used in nuclear domain, even
when they go beyond the static approaches, remain, for historical reasons, confined in discrete
events framework and quickly reach their limits when finer dependability assessment is needed.
These methods neglect a large part of information about our systems. In particular, physical phe-
nomena modeling, are not explicitly present in PSA and are often replaced by so called conserva-
tive assumptions.
Yet several attempts have been made to remedy this. But their implementations are sometimes
based on a tool dedicated to the discrete events modeling into which some continuous model-
ing functionalities are intruded. Conversely, the starting point may be a tool dedicated to the
0D physical modeling patched with some discrete events modeling functionalities. In both cases
the results are not satisfactory because their use for the assessment of even simple systems often
requires some convoluted modeling which may be so difficult to maintain..
PyCATSHOO brings an original solution in that it offers a method and a tool where the two
paradigms, continuous deterministic on the one hand and discrete stochastic on the other hand,
are natively integrated. It also provides a lot of convenient tools which make user feel at ease with
the increase of modeling complexity due to this integration.
To ease the understanding of what PyCATSHOO is, we begin this document by a non exhaustive
list of some studies carried out with PyCATSHOO.
2 Not exhaustive review of studies and test cases carried out with Py-
CATSHOO
2.1 Heated Room
In this example the studied system is a room whose temperature is to be kept between two thresh-
olds values using one or several heating devices equipped with thermostats. When the heaters
are stopped, the temperature in the room drops because of thermal leakage and the low external
temperature. As the heaters may fail, there is a chance that the room temperature will not be
maintained in the appropriate range. Several performance indicators of this room heating system
can be assessed with PyCATSHOO:
3
Figure 1
Overview of the test case Heated room
4
Figure 2
An example of the heated room test case outputs - Means and quantiles
Figure 3
An example of the heated room test case outputs
Cumulative distribution of the undesired event probability
5
2.2 Heated Tank
Figure 4
Overview of the test case Heated tank
This example comes from a well known heated tank benchmark. In this case the system is com-
posed of five components:
The two pumps and the valve are actuated according to the level of water in the tank in order to
prevent the overflow and the dryout. The objective here is to evaluate the probability that, at a
given time, the system has experienced one of the three following undesired events.
The figure 4 reminds the benchmark data. In particular, it gives the system of ordinary differential
equations (ODE) which governs the temperature and the level inside the tank. This ODE system
is deduced form the material and energy balances. The figure 4 also gives the failure rate of the
pumps and the valves as a function of the temperature.
6
Despite its simplicity, this example fully falls in the scope of the dynamic reliability. Indeed, its
discrete behavior depends on the continuous state variables in two ways. The first one is deter-
ministic since the system configuration changes when some limits are exceeded by the continuous
variables. The second way is stochastic since the system configuration changes after component
failures whose occurrence rate depends on the continuous variables.
The figure 5 gives the cumulative occurrence probability distribution of the three undesired
events.
Figure 5
An example of the heated tank test case outputs
Cumulative distribution of the undesired events probabilities
7
this study is called to extend the stochastic behavior to the potential failures of the components of
the entire spillway system.
Figure 6
Overview of the dam cascade system
Figure 7
An example of the dam cascade system assessment results
8
2.4 RLC Circuit modeling and simulation
Figure 8 gives the output of the simulation model of a simple RLC circuit. For training purpose,
we have developed a library of several deterministic models of simple electrical components. We
have then used this library to model and simulate circuits such as the one presented in the figure
8.
Figure 8
A simple RLC circuit & its simulation outputs
9
1. Temperature exceeding 80°C
2. Temperature reaching boiling threshold (100°C)
3. Fuel uncovering (water level lesser than 16m)
These three events are expressed in terms of thresholds exceedance by deterministic continuous
variables which result from an ordinary differential equation system.
Because the conventional methods are limited to discrete events paradigms, they cannot be used,
without conservative assumptions, to assess the aforementioned undesired events.
The assessment with PyCATSHOO of these three undesired events have been compared to the
evaluation, by a conventional method, of an event equivalent to the simultaneous loss of the three
cooling trains. The figure 10 below gathers the results of the assessment of the four undesired
events. It shows how high the safety margins loss can be when such a study is carried out with
conventional approaches.
Figure 9
Overview of the PyCATSHOO simplified model
of the Spent Fuel Pool Cooling system
10
Figure 10
Excerpt from the assessment results of
the Spent Fuel Pool Cooling system assessment
3 Basic concepts
PyCATSHOO can be used to model and assess the performances of complex hybrid dynamic
systems. Here, performance, most of the time, means dependability and "hybrid" qualifier denotes
the coexistence and interaction, in the same model, of discrete and stochastic behavior in the one
hand with deterministic and continuous physical phenomena in the other hand.
PyCATSHOO is a dynamic library released in several versions. These different versions allow the
use of PyCATSHOO in C ++ language or in Python language, on Windows 32/64, Linux 32/64 or
Mac OS platforms. The library offers three kinds of tools:
• Tools that help in generic modeling i.e. building libraries of classes that stand for the com-
ponents of a given system category. These libraries are also called knowledge bases.
• Tools dedicated to the modeling of a specific system based on the instantiation of the classes
imported from a knowledge base. These tools also allow to customize and start parallel
simulations.
• Post processing tools to help with the analyzing of the results.
PyCATSHOO is based on the notion of component abstractions which are classes, derived from
the CComponent, that belong to a knowledge base. The PyCATSHOO model of a system must be
included in a class derived from CSystem where:
• The instantiations of the knowledge base classes are realized according to the system com-
position
• The components of the system are linked to each other according to its architecture
• The system quantification is carried out via Monte Carlo simulation
• The equations of the physical phenomena are solved
11
The restitutions of quantification results and their post processing may be done in two ways ac-
cording to the goals of the study.
The first way requires the definition of performance indicators before the simulation run. This is
the recommended approach as it requires less memory although it slightly slows the simulations.
The second way consists in monitoring some system states and variables during the simulation.
This means that all the values taken by these elements during the simulated sequences are mem-
orized. At the end of the simulation, one can use an instance of the CAnalyser class. This class
provides statistical computational routines. It helps requesting indicators such as mean values,
standard deviation, sojourn times, etc.
Figure 11
PyCATSHOO Implementing process
12
Figure 12
System and Component states
Figure 13
Regions of the system and a component continuous state variables
13
So this modeling consists in creating a representing class for every system class of components or
concepts. This class has to be derived form CSystem, a PyCATSHOO builtin class. It holds several
kinds of items:
• Variables: They are the actual intrinsic state variables. They may be discrete or continuous
and they contribute to characterizing the state of a component to which they belong.
• References: Designated below by the term "references", they are references to external vari-
ables. They play a role of a receptacle for the information coming from the outside of the
component and hold its perception of the state of the system surrounding components.
• The message boxes: The object oriented principle of encapsulation requires that only the
owner component has a direct access to its variables. So the raison d’être of the message
boxes is to ensure the communication between a component and the rest of the system while
respecting this principle. A message box holds several channels or slots. Each channel is
connected to one variable or one reference. When connected to a variable a slot exposes the
value of the latter to the component’s outside. Conversely, when connected to a reference,
the slot feeds the latter by incoming values originated from variables of other components.
• Automata: They define the behavior of the component. They are composed of a set of states
and transitions. The latter are characterized by a probability distribution and a condition.
The condition is a boolean expression or function which involves the state variables and the
references. This means that when the variables change or when new values are received
by references, a transition may be triggered since its condition may become true. The au-
tomata in PyCATSHOO are defined at the component level for obvious reasons: avoiding
the combinatorial explosion and preserving the understanding of the system behavior and
its modeling. Indeed, as shown in the two figures 12 & 13, on the one hand the state space
of the entire system is the Cartesian product of the states of its component and, on the other
hand, the zoning of continuous state variables of the entire system is also a Cartesian product
of regions where the component can evolves when it is in a particular state. These Cartesian
products are not actually constructed that is why the combinatorial explosion is avoided.
• The system may be in one of a finite set of modes M and, for each mode m ∈ M the contin-
uous state variables are confined in a specific region Ωm ⊂ Rd
This means that the global state of the system is :
Em where Em = m × Ωm
S
E= m∈ M
14
• When in a mode m the system may be subject to a spontaneous jump into another mode m0 .
Otherwise it evolves until it hits the boundary ∂Ωm , if it exists, of the current region Ωm in
which case the system is forced to jump into a new mode m00 which is compatible with the
new position in the continuous state space. The probability of the jump outside the current
mode is given by the following equation:
( Rt
P( T < t| Z = ( x, m)) = 1 − e− 0 λm ( x (u)) du
if t < t∗ ( x, m)
1 if t > t∗ ( x, m)
where
λ m→m0 ( x )
Kx,m (m0 ) =
λm ( x ) = ∑ λm→n ( x )
n∈ M,n6=m
Where λm→n ( x ) is the transition rate from the mode m to n given the position in the
continuous state space.
In case of a forced jump when the boundary is hit, the probability to reach a particular
mode m00 is given by the following probability:
Km (m00 ) = γm→m00
The transition rate λm→n and the probability γm→m00 are an input data for systems’
assessment.
15
Figure 14
A PDMP trajectory
The figure 14 above gives an example of a PDMP’s trajectory. Green curves stand for deterministic
evolutions which may be interrupted by spontaneous jumps represented by blue arrows. Forced
jumps after a boundary reach are pictured by red arrows.
Hence, even if the PDMP is a global entity shared by the entire system, it is defined in a distributed
way. Indeed, every component contributes to its definition by giving a part of the global ordinary
differential equation system, a part of the global system of linear equation or by giving a part of
the global linear programing system which addresses integer and mixed models.
16
Figure 15
Distributed PDMP Definition
Figure 16
Specific System Modeling
17
3.1.3 Creation of a quantification model
This third step is based on the knowledge base and on the system model. It consists in the creation
of a quantifiable model with new input data about simulation parameters, new definition of the
desired indicators, etc.
4 Generic modeling
The generic modeling consists in producing generic models for every component which belongs
to the targeted class of the system.
The components’ models are basic elements of a knowledge base. They are used to model ev-
ery constituent of a given class of systems, whether material or conceptual. Programmatically, a
component is a class derived form the PyCATSHOO class CComponent.
...
class Heater(Pyc.CComponent):
def __init__(self, name):
Pyc.CComponent.__init__(self, name)
This excerpt creates a class called Heater. This class has a constructor which requires,
as a unique parameter, a string called name.
...
self.aHeater = Heater(externalHeaterName)
A class is mainly made up of three kinds of information. We will describe this information in the
next section using the PyCATSHOO Python API.
4.1 Variables
Variables characterize the intrinsic state of a component. Thus, for instance, a dam’s upstream
basin may be characterized by the water level, a spent fuel pool may be characterized by the level
and the temperature of the water, etc.
The declaration of a variable is done inside a class as follows :
4.1.1 addVariable()
...
self.pythonVariableName = self.addVariable(externalVariableName,
variableType,
initialValue,
toReinitialize)
18
Where :
A variable is a rather complex object. Thus, specific "setters" and "getters" are required to modify
and retrieve its value.
4.1.2 setXValue()
...
self.variable.setDValue(aPythonDoubleValue)
...
self.variable.setFValue(aPythonFloatValue)
...
self.variable.setIValue(aPythonIntegerValue)
...
self.variable.setBValue(aPythonBooleanValue)
These setters modify the variable value in a way that is compatible with its type.
4.1.3 value()
...
self.variable.value()
This getter asks for the variable value whatever its type is.
4.2 References
References are simply receptacles for external information incoming from the component’s out-
side. This may be for instance the values of incoming flow rate measured by a sensor.
4.2.1 addReference()
...
self.outsideTemperature = self.addReference(referenceExternalName)
Where :
19
A reference may only be seen as the image of an actual remote element that the object cannot
access. There is therefore no direct "setters" for the references.
References getters are slightly different from those of variables. To illustrate this difference, let’s
imagine a tank which is fed by several valves. We do not necessarily know in advance the number
of valves. That is why, in the model of the tank, we will only have one reference as a receptacle for
all the incoming flow rates. This means that the flow rates reference will have a behavior close to
that of a vector as shown in the following excerpts.
...
self.flowRate = self.addReference("flowRate")
Declaration of a reference.
...
sumOfFlowRates = 0
for i in range(self.flowRate.cnctCount()):
sumOfFlowRates = sumOfFlowRates + self.flowRate.value(i)
4.2.3 sumValue()
...
self.reference.sumValue(defaultValue)
Gives the sum of all incoming values connected to self.reference. This may replace the
loop given above.
4.2.4 productValue()
...
self.reference.productValue(defaultValue)
20
4.2.5 Reference.andValue
...
self.reference.andValue(defaultValue)
This call is only relevant for references to boolean variables. It applies the and operator
to all the values connected to self.reference and return the result.
4.2.6 Reference.orValue
...
self.reference.orValue(defaultValue)
This call is only relevant for references to boolean variables. It applies the or operator
to all the values connected to self.reference and returns the result.
4.3.1 addMessageBox()
...
messageBox = self.addMessageBox(messageBoxName)
...
self.addMessageBoxImport(messageBoxName, self.reference, slotName)
#or
messageBox.addImport(self.reference, slotName)
...
self.addMessageBoxExport(messageBoxName, self.variable, slotName)
#or
messageBox.addExport(self.variable, slotName)
21
Where :
• messageBoxName is a string that gives the external name of the message box
• slotName is a string that gives the external name of the slot
• self.variable is a component variable
• self.reference is a component reference
4.4.1 addAutomaton()
...
self.anAutomaton = self.addAutomaton(automatonName)
4.4.2 addState()
...
self.stateX = self.addState(automatonName, stateXName, stateXIndex)
Adds to an existing automaton a state named stateXName with an Index equal to sta-
teXIndex.
...
self.stateY = self.anAutomaton(stateYName, stateYIndex)
4.4.3 setInitState()
...
self.anAutomaton.setInitState(stateXName)
...
self.anAutomaton.setInitState(self.stateX)
Where :
22
• stateXName and stateYName are strings which designate the names of the states
A name of a state must be unique not only in the scope of the automaton but also in the
scope of the component
• stateXIndex and stateYIndex are integers which designate the indexes of the states
An index of a state is an integer which is unique in the scope of the automaton
It is strongly recommended to designate the initial state of an automaton just after the creation of
its states. The initial state may obviously be modified later either during the creation of a system
or through an xml parameter file.
An automaton may be asked to return its current state or the index of the latter through the fol-
lowing calls:
4.4.4 currentState()
...
self.anAutomaton.currentState()
self.anAutomaton.currentIndex()
These two calls indicate the current state of the automaton anAutomaton. The first one returns a
reference to the state. The second returns the current state index in the automaton.
4.4.5 addTransition()
Once all the automaton states are declared the creation of the transitions between
these states can be done by the instructions given below.
...
self.aTransition = self.stateX.addTransition(transitionName)
Creates a new transition starting form the previously created state self.stateX and
named transitionName
4.4.6 setDistLaw()
...
self.aTransition.setDistLaw(probabilityLawType, probabilityParameter)
This call is optional. When it is omitted the transition is processed immediately after
its condition becomes True.
Otherwise, this call sets the probability law that determines when the transition can be
fired
The parameter probabilityLawType designates the probability distribution. It can take one of the
folowing values : - Pyc.TLawType.expo for expnential distribution. - Pyc.TLaxType.inst for dis-
crete distribution. - PycTLawType.defer for determenistic transition with fixed delay.
Other kindes of probability distributions will be available in next versions of PyCATSHOO. Mean-
while, the user can create its own distributions.
23
4.4.7 addTarget()
...
self.aTransition.addTarget(self.stateY, transitionType)
Where :
• probabilityLawType designates the type of probability law used to randomly sample the in-
stant where the transition can be fired. This parameter may take one of the three following
values:
– Pyc.TLawType.expo: for exponential probability law. In this case probabilityParameter
designates the transition rate.
– Pyc.TLawType.inst: called instantaneous probability law. It means that when the tran-
sition condition is satisfied the transition is fired immediately. This transition can lead
to two or more targets.
When two targets are possible probabilityParameter gives the probability to reach the
first target declared and the second one may be reached with a probability equal to 1 -
probabilityParameter
– Pyc.TLawType.defer: for a deterministic transition fired after a duration equal to prob-
abilityParameter starting from the instant when the condition of the transition becomes
true
• transitionType designates the type of the transition.
This parameter can take one of the three following values:
– Pyc.TTransType.fault: When the transition is assimilated to a failure
– Pyc.TTransType.rep: When the transition is assimilated to a repair
– Pyc.TTransType.trans: for the other types of transitions
4.4.8 setCondition()
...
self.aTransition.setCondition(...)
This call is optional. When it’s omitted the transition can be fired without condition.
Otherwise, this call sets a condition that must be satisfied before the probability law is
asked to randomly sample the instant when the transition can be fired. This call has
several variants as we can see below:
...
1 self.setCondition(value)
...
2 self.setCondition(self.variable)
24
Here, self.variable is a reference to a component state variable. The value of this variable
may change over the simulation time and so does the transition condition.
...
3 self.setCondition(self.variable, takesTheOpposite)
This call leads to the same result as the previous one if the parameter takesTheOpposite
is False. Otherwise it is equivalent to the following call:
...
self.setCondition(self.not_variable)
...
4 self.setCondition(self.methodReference)
Here, the transition condition is be the result of the call to the component method
self.methodReference. The latter can be replaced by a Python lambda function which
returns a boolean value.
...
5 self.setCondition(self.methodReference, takesTheOpposite)
Here the meaning of the parameter takesTheOpposite is the same as in the case number
3.
4.4.9 setInterruptible()
When the probability law is Pyc.TLawType.expo or Pyc.TLawType.defer, the instant when the
transition must be fired is drawn at the instant when the transition condition becomes true and
the countdown begins just after that. The default behavior is that this countdown never stops
even though, in the meantime, the condition becomes false. To change this behavior and make the
countdown stop when the condition becomes false the following call must be done:
...
self.aTransition.setInterruptible(True)
If the condition becomes true again, a new countdown starts without taking into account the
previously interrupted countdown.
To come back to default behavior the following call has to be done:
...
self.aTransition.setInterruptible(False)
25
4.4.10 setModifiable()
A transition between two states in a component automaton can be fired several times during a sim-
ulation. This means that the time when this transition is fired is also drawn (calculated) several
times. In the default behavior, all of these times are drawn using the initial value of probabilityPa-
rameter, the probability law parameter which is the transition rate for an exponential law.
There are two other alternatives to this behavior that can be set up with the two following calls:
...
self.aTransition.setModifiable(Pyc.TModificationMode.discrete_modification)
...
self.aTransition.setModifiable(Pyc.TModificationMode.continuous_modification)
The time when the transition must be fired is computed, in the case of the behavior induced
by the first call, using the current value of probabilityParameter which is the value given to the
probability law parameter at the most recent change. This behavior allows to take account of
discrete evolution probability law parameters.
The second call allows to consider the continuous evolution over time of the probability law pa-
rameter probabilityParameter. This means that the integral form of the probability law is used to
draw the instant when the transition must be fired. The following equation reminds the cumula-
tive distribution function of the used probability law when it has an exponential form.
Rt
P ( T < t ) = 1 − e− 0 probabilityParameter (u) du
As probabilityParameter evolves continuously over time and it might even depend on the compo-
nent state variables, this parameter must be driven by the PDMP (Piecewise Deterministic Markov
Process) manager that will be introduced in the next sections.
To come back to the default behavior the following call must be done:
...
self.aTransition.setModifiable(Pyc.TModificationMode.not_modifiable)
4.5.1 addStartMethod()
The beginning of the simulation corresponds to the instant 0 just after all states and variables are
reset to their initial values. One may have, for instance, to finalize the initializations by giving to
the induced variables the appropriate initial values. To do so we have to designate a method in
charge of this task with the following call:
...
self.addStartMethod(startMethodName, self.myStartMethod)
26
startMethodName and self.myStartMethod give respectively the internal method name
and the reference of the method that has to be added to the component in order to
accomplish the required tasks at the begining of a simulation.
...
self.stateX.addSensitiveMethod(methodName, self.methodReference, when)
methodName and self.methpdReferences" give the internal name of the method and its refer-
ence. The designated method has to be added to the component in order to accomplish required
tasks at the entering and/or leaving self.stateX.
when* is an optional parameter which can take one of the three values; -1, 1 or 0 (de-
fault value).
* -1 means that the method is called when the state is left (deactivated)
* 1 means that the method is called when the state is entered (activated)
* 0 means that the method is called when the state is entered (activated)
and when it is left (deactivated)
...
self.automatonX.addSensitiveMethod(methodName, self.myMethod, when)
methodName and self.myMethod give respectively the internal name of the method and
its reference. The designed method has added to the component in order to accomplish
the required tasks when the automaton changes its state.
when is an optional parameter which can take one of the three values; -1, 1 or 0 (default
value).
* -1 means that the method is called when the automaton enters a state whose
index is lower than that the one of the state that has been left.
* 1 means that the method is called when the automaton enters a state which
index is higher than that of the left state
* 0 means that the method is called every time the automaton changes
its current state
27
...
self.referenceX.addSensitiveMethod(methodName, self.myMethod)
Here, methodName and self.myMethod give respectively unlike the internal name and
the reference of the method to be called when self.referenceX receives a new value.
self.myMthod has to be added to the component.
...
self.tranistionX.addSensitiveMethod(methodName, self.myMethod)
Here, methodName and self.myMethod give respectively unlike the internal name and
the reference of the method to be called when the transition is fired. self.myMethod has
to be added to the component.
28
Figure 17
The PDMP ingredients in case of a trajectory finished by a forced jump
Figure 18
The PDMP ingredients in case of a trajectory finished by a spontaneous jump
29
4.6.1 addPDMPManager()
...
pdmpManager = self.addPDMPManager(pdmpManagerName)
A PDMP Manager manages the evolution over time of a subset of state variables of the system.
This subset includes a subset of the component state variables. Hence, for every component we
have to designate its managed state variables. This can be done in two ways according to the
manner these variables are calculated:
...
self.addPDMPEquationMethod(pdmpManagerName,
equationMethodName,
30
self.equationMethod,
order)
#or
pdmpManager.addEquationMethod(equationMethodName,
self,
self.equationMethod,
order)
These calls designate, for the PDMP manager which name is pdmpManager-
Name or which is referenced by the variable pdmpManager, the component
method where the differential equations and explicit expressions are de-
clared.
Where :
4.6.5 setDvdtODE()
The declaration of a first order differential equation is done, inside the equation method previ-
ously added to the PDMP Manager, as follows:
...
self.variableX.setDvdtODE(aPythonExpression())
Where :
• aPythonExpression is a Python expression involving any simple Python variables and/or any
PyCATSHOO variables and references
4.6.6 setDValue
The declaration of an explicit equation is done, inside the equation method previously added to
the PDMP Manager, as follows:
...
self.variableY.setDValue(aPythonExpression)
31
Where :
• aPythonExpression is a Python expression involving any simple Python variables and/or any
PyCATSHOO variables and references
Here we have an illustration of the Python implementation of a PDMP Manager equation method.
This illustration is drawn from the heated tank test case. It gives the following expression of the
failure as an explicit function of the temperature inside the tank.
...
self.addPDMPBoundaryCheckerMethod(pdmpManagerName,
boundaryCheckerMethodName,
self.boundaryCheckerMethod)
#or
pdmpManager.addBoundaryCheckerMethod(boundaryCheckerMethodName,
self,
self.boundaryCheckerMethod)
Where:
32
The role of a boundary checker method consists in the accomplishment of a set of appropriate tests
to determine if the current continuous state variables are still in a region coherent with the current
discrete states. In this case the method must return a positive value otherwise, it must return a
negative value.
In most cases the use of bounadry checker methods is not mandatory and can be replaced by the
folowing methods.
4.6.8 addPDMPWatchedTransition()
Roughly speaking, the bounadriy checker methods help detecting crossing the boundaries of cur-
rent states valid regions. But in general the condition of a boudary crossing is also the condition
of a deterministic transition. It may be, for example, a transition between OPEN an CLOSE states
of a valve which must close when the level in the vessel exeeds a particular threshold. In such a
case, we dont need to implement any boudary checker method, we just have to ask PyCATSHOO
to watch the transition by the following call:
...
self.addPDMPWatchedTransition(pdmpManagerName, transition)
Where:
5 System construction
Once a knowledge base is available i.e. when every class of component is modeled, it becomes
quite easy to construct a system model which belongs to the class of systems targeted by such a
knowledge base.
A system construction consists in creating its components by instantiation of the knowledge base
classes. These components are then connected to each others via their message boxes according to
the architecture of the system.
The components created must be gathered within a class derived from the PyCATSHOO class
CSystem. To create such a class we can proceed as follows:
...
class MySystem(Pyc.CSystem):
def __init__(self, systemName):
Pyc.CSystem.__init__(self, systemName)
This excerpt creates a Python class called MySystem. The constructor of this
class requires a string which designates the external name of the system in-
stance.
Within this class, in the constructor itself or in a dedicated method, the constituents of the system
are created by instantiating the knowledge base classes according to the system composition:
33
...
self.aHeater = Heater("MainHeater")
self.room = Room("Room")
In this excerpt we create an instance of the Heater class. This instance will be
known outside the system by its external name "MainHeater". An instance
called "Room" of the class Room is also created.
Once the instances are created, we proceed to the connexion of their message boxes according to
the architecture of the modeled system. Such connexions are created by one of the following calls:
...
self.connect(self.aHeater, aHeaterMessageBoxName, self.room, aRoomMessageBoxName)
In this call, we create a link between the message box called aHeaterMes-
sageBoxName of the component self.aHeater and the message box called
aRoomMessageBoxName of the component self.room.
...
self.connect("MainHeater", aHeaterMessageBoxName, "Room", aRoomMessageBoxName)
In this call, we create a link between the message box called aHeaterMessage-
BoxName of a component called "MainHeater" and the message box called
aRoomMessageBoxName of a component called "Room".
6 Quantification
The quantification is done in 9 steps:
...
self.po_mu = self.addVariable("mu", Pyc.TVarType.t_double, 0.1)
print (self.po_mu.value())
34
This parameter file must be loaded before the creation of the system components.
The next excerpt gives the syntax of a parameter file:
...
<PY_PARAM>
<VAR_P NAME="componentName.externalVariableName" INITV="alternativeInitialValue"/>
<VAR_P NAME="#regularExpression" INITV="alternativeInitialValue"/>
</PY_PARAM>
The attribute NAME of the element VAR_P refers to a variable. The latter is identified by the com-
ponentName which is the name of the component to which it belongs and by externalVariableName
which is the external name of the variable.
The value alternativeInitialValue is literally written. It will replace the one passed as default value
to the variable creation method.
The regular expression "#regularExpression" may replace an explicit name of a variable in order
to refer to several variables and to give them the same alternative initial value. The character #
indicates that the following string is a regular expression.
As a reminder here we have some control characters used in regular expressions:
For instance "#Pump.*\.mu" matches any string which starts with "Pump" followed by any repe-
tition of characters ".*" and then by a point "\." followed by the string "mu". This also means: any
element which external name is "mu" of any component which external name begins by the string
"Pump".
A Parameters’ file may be load by the following instruction:
...
system.loadParameters(fileName)
35
This file can also be used to complete the composition and the architecture of the system. This
functionality will not be developed in the current version of this document.
The next excerpt gives the file of initial values syntax:
...
<PY_PARAM TRACE="0 or 1 or 2"
NAME="nameOfParemetreSet"
TMAX="1000"
SEQ_NB="1000"
RNG="yarn5"
RGN_S="0"
RNG_BS="50"
RES_FILE="ResultFile.xml"
RES_XML="0 or 1"
>
<MONIT_VAR NAME="explicitNameOrRegularExpression"/>
<VAR NAME="explicitNameOrRegularExpression" MONIT="0 or 1"/>
<MONIT_ST NAME="explicitNameOrRegularExpression"/>
<ST NAME="explicitNameOrRegularExpression" MONIT="0 or 1"/>
<MONIT_AUT NAME="explicitNameOrRegularExpression"/>
<AUT NAME="explicitNameOrRegularExpression" MONIT="0 or 1"/>
<MONIT_TR NAME="explicitNameOrRegularExpression"/>
<TR NAME="explicitNameOrRegularExpression" MONIT="0 or 1"/>
</PY_PARAM>
36
File header :
The root element PY_PARAM of this XML file has the following attributes:
• TRACE: This attribute can take one of the three following values:
– 1 : ⇒ the number of sequences are printed only if some elements are traced (see below)
• RNG: name of the random number generator to use. This attributes can take three different
values:
mt19937 Mersenne Twister 19937 generator
KISS (Keep It Simple Stupid by G. Marsaglia)
yarn5 (yet another random number generator) based on a multiple recursive generator with
5 feedback taps. This generator must be used when the simulations are distributed over
several different cores.
• RGN_S: gives the seed of Monte Carlo simulation. If set to 0 (default value), the seed is
actually chosen randomly thanks to the computer internal clock.
• RNG_BS: this attribute is only useful when RNG is yarn5. It gives the size of a generated
block of numbers to skip between simulations when they are distributed over several cores.
• RES_FILE: When given, this attribute asks PyCATSHOO to produce two results files in bi-
nary and XML format. Its value composes the names of these two files.
• RES_XML : this attribute may take 1 which means that the transitions are stored in the xml
file rather than in the binary or 0 which means that the transitions are only stored in the
binary file
Tracing instructions :
In the elements TRACE_XYZ the attribute NAME gives a reference to one or several element
to trace i.e. which values are displayed in the console during the simulation. This reference is
given by explicitNameOrRegularExpression which can be "componentName.externalElementName"
or "#regularExpression". In the latter, all the elements that have their own name and the name of
their parent satisfying the regular expression will be displayed.
As for TRACE_TR, the attribute LEVEL can take three values :
37
• 1: ⇒ Only the instant when the transition is fired is displayed
• 2: ⇒ This also displays the instant when the waiting time before the transition is fired
This trace consists in printing this waiting time, except that this waiting time might not be
known when the rate of transition depends on the continuous state variables. In this case
the "?" character is displayed next to the estimated waiting time.
As for TRACE_VAR, the attribute LEVEL can also take three values :
• 1: ⇒ the values of the variable(s) are printed every time they change
• 2: ⇒ the values of the variable(s) are printed but only at the end of the deterministic phases
TRACE_ST and TRACE_AUT are about states (entering and leaving) and automata (changes of
indexes)
Note that these two instructions are equivalent:
...
<TRACE_XYZ NAME="explicitNameOrRegulaExpression" LEVEL="0 or 1 or 2"/>
<XYZ NAME="explicitNameOrRegulaExpression" TRACE="0 or 1 or 2"/>
Initialization instructions
...
<VAR NAME="explicitNameOrRegularExpression" INITV="alternativeInitialValue"/>
...
<ST NAME="explicitNameOrRegularExpression" INIT="0 or 1"/>
Monitoring instructions :
The following instructions select one or several elements of type (Variable: VAR, State: ST, Au-
tomaton: AUT, Transition: TR) and, for further post processing analysis, ask PyCATSHOO to
monitor i.e. to save their evolution over time during all the simulation. The elements are se-
lected thanks to explicitNameOrRegularExpression which gives the explicit name of the elements or
a pattern (regular expression).
...
<MONIT_[ VAR | ST | AUT | TR] NAME="explicitNameOrRegulaExpression"/>
38
As for the transitions, PyCATSHOO memorizes their name, nature and the instant when they had
been fired.
Note that the two following instructions are equivalent:
...
<MONIT_[ VAR | ST | AUT | TR] NAME="explicitNameOrRegulaExpression"/>
<[ VAR | ST | AUT | TR] NAME="explicitNameOrRegulaExpression" MONIT="1"/>
The configuration of the PDMP Manager can be done by the following instruction :
...
<ODE NAME="pdmpManager" DT="1" DTM="5" DTC="0.001" SCHEME="1"/> <!-- S1 -->
• DTM gives the time step between two instants when the variables’ values are memorized.
• DTC gives the time precision for the seeking algorithm of the boundary crossing of the state
variables’ regions.
• SCHEME gives the id of the ODE solver to use. PyCATSHOO has twelve solver schemes:
39
6.3 Construction of prior simulation indicators
The construction of this kind of indicators must be done before the simulation is launched and
after the creation of the system. At this moment we have a reference to this system. Let’s denote
system such a reference. The creation of these indicators can be done by one of the followings
instructions:
...
1. anIndicatorX = system.addIndicator("nameOfIndicator",
"nameOfAComponent.nameOfElement",
"natureOfElement")
2. anIndicatorY = system.addIndicator("nameOfIndicator",
"nameOfAComponent.nameOfElement",
"natureOfElement",
"operator",
value)
3. anIndicatorZ = system.addIndicator("nameOfIndicator",
anIndicatorFunction)
Where :
• The indicator is designated by the string nameOfIndicator as its identifier. This identifier
can help to retrieve the indicator in case of processing indicators reloaded from a result file
produced by a previous simulation.
• The main element used to calculate the indicator is selected thanks to its external name
nameOfElement and thanks to the external name nameOfAComponent of the component to
which it belongs. This element can be a variable, a state or an automaton.
• The nature of the element is designated by the string natureOfElement which takes one of
these three values: "VAR", "ST" or "AUT".
• We can give an operator operator and a value value which specify the operation to apply to
the designated element to make up a boolean indicator. operator can take one of these values:
"<", "<=", ">", ">=", "=" or "==". The two latter are equivalent.
• An indicator can also be equivalent to the result of a call to a pre-implemented function
anIndicatorFunction.
40
In the instruction 3, the function does not necessarily depend on any particular element. It can be
written as follows :
f () = anIndicatorFunction()
The indicators are computed progressively during the simulations at a set of instants that the user
must specify by the following call:
...
system.addInstants(0, system.tMax(), numberOfInstants)
This call specifies a vector of instants ranging from 0 to the observation time of the system and
which length is equal to numberOfInstants. PyCATSHOO will compute the indicators at every
instants in this vector.
After the creation of an indicator, its nature has to be defined. This is done by the following call:
...
anIndicatorX.setRestitutions(indicatorNature)
indicatorNature can take an expression like: type1 or type2 or .... or typen , where typex specifies
the nature of an indicator that PyCATSHOO will compute. typex cane take one of the following
values:
• Pyc.TIndicatorType.mean_values: Asks for the evolution over time of the indicator function
mean.
The mean evolution is then retrieved after the end of the simulation by the following call:
...
vectorOfMeans = anIndicatorX.means()
• Pyc.TIndicatorType.std_dev: Asks for the evolution over time of the standard deviation.
The standard deviation evolution is then retrieved after the end of the simulation by the
following call:
...
vectorOfStdDevs = anIndicatorX.stdDevs()
• Pyc.TIndicatorType.all_values: Asks for all the values taken by the indicator function for
every simulation carried out at a given instant.
These values are retrieved after the end of the simulation by the following call:
...
vectorOfValues = anIndicatorX.values(indexOfAnInstant)
indexOfAnInstant gives an index in the vector of instants where the indicators have been calculated.
This call retrieves the values taken by the indicator at the given instant at every simulation carried
out by PyCASTHOO.
41
...
vectorOfDistributionValues = anIndicatorX.distributions(indexOfAnInstant)
indexOfAnInstant gives an index in the vector of instants where the indicators have been calcu-
lated.
This call retrieves the distribution values taken by the indicator at the given instant at every sim-
ulation carried out by PyCASTHOO.
• Pyc.TIndicatorType.quantile_gt: Asks for the evolution over time of the values which are
greater than the quantile equal to quantileValue expressed as a percentage. This value must
be set with the following instruction:
...
anIndicatorX.setPctQuantileGtValue(quantileValue)
...
vectorOfQuantiles = anIndicatorX.quantilesGt()
• Pyc.TIndicatorType.quantile_le: Asks for the evolution over time of the values which are
lesser than quantileValue expressed as a percentage. This value must be set with the following
instruction:
...
anIndicatorX.setPctQuantileLeValue(quantileValue)
...
vectorOfQuantiles = anIndicatorX.quantilesLe()
...
system.setSeqFilter(mySequenceFilterFunction)
system.setKeepFilteredSeqForInd(aBoleanValue)
42
The first call sets a boolean function as a filter. So at the end of every simulation i.e. at the end of the
construction of every sequence, PyCATSHOO calls the function mySequenceFilterFunction with the
sequence as its unique parameter. If the function returns True, the sequence will be memorized.
Otherwise it is forgotten by PyCATSHOO. The second call allows to choose if PyCATSHOO has
to take account of the forgotten sequences when computing indicators (aBooleanValue = True) or
not (aBooleanValue = False).
The following excerpt gives an example of mySequenceFilterFunction filter function:
...
def mySequenceFilterFunction(sequence):
system = Pyc.CSystem.glSystem()
The first instruction in this function asks for the reference of the system.
The second instruction asks for the reference to an element of type State ("ST") which corresponds
to the state called "Overflow" of the component called "Tank". The last instruction compares to 1
the value taken by this element at the final time of the simulation in the context of the sequence
which reference is passed to the function. The result of this comparison is then returned by the
function. This means that the function returns True if the state called Overflow of the component
called "Tank" is reached at the end of the simulation. In this case the corresponding sequence is
kept in memory otherwise it is dismissed.
An other convenient function to use in sequence filtering is given below:
...
vectorOfInstants = Pyc.VectorDouble()
vectorOfInstants.append(t1)
vectorOfInstants.append(t2)
....
This call applies to a sequence. It takes as first parameter an element as defined above. The second
parameter is a predicate i.e. a boolean function which operates on the value taken by the element
anElement. The third parameter is a vector of doubles which represents different instants.
The function sequence.realized returns a list of booleans, one for each instant in vectorOfInstants.
The ith boolean is true if predicate returns True at least once at an instant lesser than the ith instant.
Otherwise the boolean is False.
43
...
system.setRNGSeed(theSeed)
If theSeed is 0, the seed is actually randomly chosen thanks to the internal clock of the com-
puter.
...
system.setRNG(theGeneratorName)
theGeneratorName can take one of the three values : "KISS", "yarn5" or "mt19937"
...
system.setNbSeqToSim(nbSequencesToSimulate)
...
system.monitorVariable(elementName, monitoringLevel)
system.monitorState(elementName, monitoringLevel)
system.monitorAutomaton(elementName, monitoringLevel)
system.monitorTransition(elementName, monitoringLevel)
• elementName can be an explicit name which identifies a unique element or a regular expres-
sion which references a set of elements.
• monitoringLevel if 0 the element will not be monitored, if 1 the element will be monitored.
44
...
system.setResultFileName(filename, storeSequenceInBinFile)
• fileName gives the name of the XML file where the results will be stored. PyCATSHOO will
create an other file whose name is fileName suffixed by .bin. This file is a binary version of
the XML file.
• storeSequenceInBinFile this is a boolean parameter. When True, the sequences will be stored
in the binary file rather than in the XML file
...
system.simulate()
if system.MPIRank() > 0 :
exit(0)
This sequence of instructions launches the simulation and at the end of the latter, asks MPI if the
rank of the current process is greater than 0. If so the process exits.
The rank of a process can be greater than 0 if several parallel processes have been launched. In
this case only the process with the rank 0 can continue the post processing of all the parallel
simulations results. Indeed, at the end of simulation the partial results of the simulations carried
out by the processes with non zero rank are automatically transfered to the process of rank 0.
These partial results are also automatically gathered as if they were produced by only one process
If no result file has been specified, the results can still be saved by the following statement.
...
system.dumpResults(filename, storeSequenceInBinFile)
• fileName gives the name of the XML file where the results will be stored. PyCATSHOO will
create an other file whose name is fileName suffixed by .bin. This file is a binary version of
the XML file.
• storeSequenceInBinFile this is a boolean parameter. When True, the sequences are stored in
the binary file rather than in the XML file
The post processing can then be done as explained in the next section.
45
...
analyzer = Pyc.CAnalyser(system)
The created analyzer holds all the values taken by the monitored elements during all the se-
quences. Thus, all the statistical calculations can be applied to these results. However, the val-
ues taken by the same monitored element during two different sequences are not given for the
same instants in the two sequences. Thus prior to any statistical calculation we have to make a
projection of these values over a common vector of instants. The following convenient function is
dedicated to this aim:
...
vectorOfInstants = stepsVector(system.tMax(), stepNumber):
This instruction builds a vector which length is stepNumber + 1 with instants from 0 to sys-
tem.tMax()
Some common statistical calculations are already implemented in the CAnalyzer class. Their use is
illustrated below :
...
1 pctQuantileGt = 1.
2 pctQuantileLe = 1.
3
4 vectorOfInstants = stepsVector(system.tMax(), 1000):
5
6 def indFct(x):
7 return x > 100
8
9 meanTemperature = analyzer.meanValues("room.temperature", vectorOfInstants)
10
11 quantileLe = analyzer.quantilesLe ("room.temperature", vectorOfInstants, pctQuantileLe)
12 quantileGt = analyzer.quantilesGt ("room.temperature", vectorOfInstants, pctQuantileGt)
13
14 cpdfValues1 = analyzer.realized ("room.temperature", vectorOfInstants, indFct)
15
16 cpdfValues2 = analyzer.realized ("room.temperature", vectorOfInstants, lambda x: x > 100)
17
18 cpdfValues3 = analyzer.realized ("room.temperature", vectorOfInstants, ">", 100)
46
The lines 14, 16 and 18 are equivalent as they compute the cumulative probability distribution
function of the following indicator function:
1 if Room.temperature > 100
1Room.temperature>100 =
0 if Room.temperature <= 100
...
analyzer.printFilteredSeq(percentage, xmlFileName, "PySeq.xsl")
This instruction prompts the creation of an HTML presentation of the sequences which represent
percentage percent in term of probability of the sequences produced by the simulations. The result
of this transformation is on the one hand an XML file whose name is given by the parameter
xmlFileName and on the other hand an HTML file which name is composed of the xmlFileName
base name with the "html" extension.
At this stage it is still possible to proceed to another filtering of the sequences before the creation
of their HTML presentation. This filtering can be done exactly in the same way as the filtering
before the simulation previously presented.
...
system = Pyc.CSystem("theNameOfMySystem")
system.loadResults("TheResultsFileNameOfAPreviousSimulation.xml")
The first instruction calls the constructor of an empty instance of the PyCATSHOO class CSystem.
Therefore it is not required to recreate explicitly the original system. This creation will be automat-
ically done by loading the file of results which holds all the informations about the corresponding
system.
The second call loads an XML file where the results of a previous simulation have been stored.
After these instructions a new post-processing can be done in almost the same way as it is done
after a call to system.simulate(). The only difference is related to the references to the indicators
potentially created in the previous simulation. Here, theses references must be rebuild thanks to
their names as follows:
...
anIndicatorX = system.indicator("nameOfIndicator")
47
7 Implementation of a test case
The test case introduced below takes an example of a room with one or several heating devices
aimed to maintain the room temperature between two thresholds: maxTemperature = 20◦ C
and minTemperature = 15◦ C. The room is assumed to be insufficiently thermally insulated.
Thermal leaks are assumed to be proportional to the difference between the room tempera-
ture temperature which initial value is initialTemperature = 17◦ C and the outside tempera-
ture which is outsideTemperature = 13◦ C. The coefficient of proportionality is the leakage rate
leakageRate = 0.1W.◦ C −1 .
The heating devices have their own thermostats and sensors. They start and stop the heaters
according to the room temperature. They can be connected to each others so that they operate in a
passive redundancy. This means that only one heater operates at a given instant and that a heater
can be declared as slave of a master heater. The slave heater can start only when the master is out
of order. It stops when the latter is repaired.
A heater capacity i.e. the nominal power it is able to supply is equal to nominalPower = 5W. The
power it supplies at a given instant is equal to power = nominalPower when it is operating and it
is equal to power = 0W when it has been stopped.
We assume that the devices may fail at any instant with the failure rate lambda = 0.01h−1 and can
be repaired with a rate mu = 0.1h−1 .
Figure 19
Overview of the test case Heated room
The energy balance given below gives the ordinary differential equation which governs the evo-
lution over time of the room temperature:
d(temperature)
= power − leakageRate × (temperature − outsideTemperature)
dt
The aim here is to give a generic model for the concepts and components of the system class
described above in order to be able to address systems represented by the following figure 20:
48
Figure 20
A generic heated room system
In figure 20, the black arrows stand for the passive redundancy. The origin of this arrow is con-
nected to a master heater and its target is connected to a slave heater. The red arrows represent
the thermal power supplied by the heaters to the room.
We will proceed progressively in the four following steps.
• nominalPower: the value of thermal power that the heater supplies when it is operating
• power: the value of thermal power supplied by the heater at a given instant
• lambda: its failure rate
• mu: its repair rate
49
Two automata define the heater’s behavior. The first is the dysfunctional one. It has two states:
OK and KO. The transitions between OK and KO states are governed by exponential probability
laws.
The second automaton is a functional one. It has two states:
1. ON: if it is OK and (it has no master or (it has a master and there is a starting request))
2. OFF: if it is in not OK or (it has a master and there is no starting request)
The transitions between these two states are obviously deterministic and only their conditions
OFF2ONCondition and ON2OFFCondition must be implemented in the Heater class.
When in ON state the thermal power supplied by the heater must be: power = nominalPower and
when on OFF state it must be power = 0W.
We have to ensure that this rule is satisfied at the beginning and at every instant of a simulation.
To do so, we will implement, in the heater class, a sensitive method called updateSuppliedPower
which will be in charge of this task. We will then ask PyCATSHOO to automatically call it at the
beginning of a simulation and at every time the states of the functional automaton changes.
50
Figure 21
The Heater class for the step S1
51
In [2]: # coding: latin-1
'''
Created on 07.06.2017
'''
######################################################################################
class Heater(Pyc.CComponent): #S1
52
# Sets OK state as the initial state
self.setInitState("OK") #S1
53
trans = self.stateON.addTransition("ON_to_OFF") #S1
# Sets condition of the transition
# The transition is fired if the FunctionalAutomation state KO is active
trans.setCondition(self.ON2OFFCondition) #S1
# Definition of the target of the transition
trans.addTarget(self.stateOFF, Pyc.TTransType.trans) #S1
In [3]: ######################################################################################
class MySystem(Pyc.CSystem): #S1
def __init__(self, name): #S1
Pyc.CSystem.__init__(self, name) #S1
54
# Instantiation of a Heater
self.aHeater = Heater("aHeater") #S1
We also have to give the initial values and the configuration of the simulation. To do so, we will
use an XML initial values file with the following items:
• nominalPower = 5W
• lambda = 0.01h−1
• mu = 0.1h−1
• OK and ON are the initial states
Here is a commented Python main program which launches the simulation and displays the graph
of the evolution over time of the thermal power supplied by the heater.
55
In [4]: ######################################################################################
if __name__ == '__main__': #S1
try :
# An instance of the system is constructed (Only one system can be
# constructed in a session)
system = MySystem("S1") #S1
# The simulation parameters and initial values are loaded from an XML file
system.loadParameters("HeatedRoom_S1.xml") #S1
# We create an anlyzer which holds the values of all the monitored elements
analyser = Pyc.CAnalyser(system) #S1
# This vector will hold the instants where the values of the indicators
# will be calculated
vectorOfInstants = stepsVector(system.tMax(), 500) #S1
56
print(type(e),e) #S1
Pyc.printMessages() #S1
Figure 22
The supplied power expected from the Heater in the step S1
The asymptote of the curve produced above indicates that the expected supplied thermal power
is about 4.54W. This value is coherent with the values given by the theoritical expression :
µ 0.1
× nominalPower = 5 × = 4.5454W
λ+µ 0.1 + 0.01
57
1. Every heater can be a master or / and a slave for another heater.
2. When it is a master, a heater must transmit its KO state (True if KO is active and False
otherwise) to the rest of the system through a message box that we will call "MB-toSlave" in
order that the receiver considers it as a start or stop request.
3. When it is a slave, a heater must be ready to receive boolean values (True or False) through
a message box we will call "MB-toMaster". Such a heater must consider that a reception of
at least one True value is equivalent to a start request. Otherwise the heater must stop.
4. If a heater has no master this means that it has a permanent request to start.
b. Add two message boxes to a the Heater class : "MB-toSlave" and "MB-toMaster"
A Heater passes from OFF state to ON state if the heater is OK and, it either
has no master or there is at least one True value in startingRequest. As a
reminder, the latter condition is equivalent to an OR operator over all the
values held by startingRequest.
A Heater passes from ON state to OFF state if the heater is KO or, if it has a
master and there is no True value in startingRequest.
58
Figure 23
The Heater class for the step S2
59
In [1]: # coding: latin-1
'''
Created on 07.06.2017
'''
######################################################################################
class Heater(Pyc.CComponent): #S1
60
self.aDysfunctional = self.addAutomaton("DysfunctionalAutomaton") #S1
# Creates a state named OK and adds it to the automaton
self.stateOK = self.addState("DysfunctionalAutomaton", "OK", 1) #S1
# Creates a state named KO and adds it to the automaton
self.stateKO = self.addState("DysfunctionalAutomaton", "KO", 0) #S1
61
# Define the target of the transition
trans.addTarget(self.stateON, Pyc.TTransType.trans) #S1
62
if not self.stateOK.isActive(): #S2
return True #S2
aMasterHeater.power = 5 − aSlaveHeater.power
In [2]: ######################################################################################
class MySystem(Pyc.CSystem): #S1
def __init__(self, name): #S1
Pyc.CSystem.__init__(self, name) #S1
We also have to give the initial values and the configuration of the simulation. To do so we will
use an XML parameters’ file with the following items:
We will give the same initial values to the two heaters except that for the slave, the OFF will be the
initial state of the functional automaton while the ON will still be the initial state for the master.
63
<TRACE_TR NAME="#.*" LEVEL="0"/> <!-- S1 -->
Hereafter a commented Python main program which launches the simulation and draws the evo-
lution over time curve of the thermal power supplied by the heater.
In [3]: ######################################################################################
if __name__ == '__main__': #S1
try :
# An instance of the system is constructed (Only one system can be
# constructed in a session)
system = MySystem("S2") #S1
# The simulation parameters and initial values are loaded from an XML file
system.loadParameters("HeatedRoom_S2.xml") #S1
64
# We create an anlyzer which holds the values of all the monitored elements
analyser = Pyc.CAnalyser(system) #S1
# This vector will hold the instants where the values of the indicators
# will be calculated
vectorOfInstants = stepsVector(system.tMax(), 500) #S1
65
Figure 24
The Heaters expected supplied power for the step S2
The asymptotes of the two curves produced above indicate that the expected supplied thermal
power of the two heaters satisfy the following constraints :
aMasterHeater.power = 5 − aSlaveHeater.power
• leakageRate: the rate of the thermal power leaks through the room walls
• temperature: the room temperature which evolves over time
66
• initialTemperature: a constant which gives the room temperature at the beginning of the
simulations
• outsideTemperature: a constant which gives the outside temperature
d(temperature)
dt
= ∑ poweri − leakageRate × (temperature − outsideTemperature)
i
This means that there will be a PDMP Manager in the system and that the Room part of this
PDMP manages the state variable temperature. A method that we will call pdmpMethod has to be
defined in the room class. It has to implement the ODE which governs the temperature evolution
over time.
67
Figure 25
The Room class for the step S3
68
In [1]: # coding: latin-1
'''
Created on 07.06.2017
'''
######################################################################################
class Room(Pyc.CComponent):
def __init__(self, name):
Pyc.CComponent.__init__(self, name)
69
self.addStartMethod("start", self.start) #S3
Now that the room is modeled, we have to update the Heater class in order to be able to:
• adding two state variables minTemperature and maxTemperature which hold the pro-
grammed values of the internal thermostat.
• adding a reference roomTemperature which holds the received value of the room tempera-
ture.
• adding a message box ""MB-toRoom" dedicated to send the value of the thermal power
supplied and to receive the room temperature.
• updating the OFF2ONCondition method such that the condition of starting will be that the
temperature of the room is lesser than minTemperature.
• updating the ON2OFFCondition method such that the condition of stopping will be that the
temperature of the room is greater than maxTemperature.
• adding ONBoundaryChecker, a boundary checker associated with ON state which ensures
that temperature > maxTemperature is a forbidden region when the heater is ON.
• adding OFFboundaryChecker, a boundary checker associated with OFF state which ensures
that temperature < minTemperature is a forbidden region when the heater is OFF and KO.
70
Figure 26
The Heater class for the step S3
71
In [2]: # coding: latin-1
'''
Created on 07.06.2017
'''
######################################################################################
class Heater(Pyc.CComponent): #S1
72
# The references to external information -------------------------------------
# This is a reference which holds the received requests that make the heater
# starting when there is a True value
self.pi_startingRequest = self.addReference("startingRequest") #S2
# This reference will be bound to the current temperature of the room
self.pi_roomTemperature = self.addReference("roomTemperature") #S3
73
# Sets OK state as the initial state
self.setInitState("OFF") #S1
74
pdmpManager = self.addPDMPManager("PDMP-Manager") #S3
# Designate transition to watch in order to check if the system
# is still in region consistent with the current states
self.addPDMPWatchedTransition("PDMP-Manager", transOff2On) #S3
self.addPDMPWatchedTransition("PDMP-Manager", transOn2Off) #S3
75
# Otherwise it returns 1
def ONBoundaryChecker(self): #S3
if self.stateKO.isActive(): #S3
return 1. # No boundary crossed #S3
76
7.3.6 Step 3 - testing
The test performed here consists in adding a room to S2 system and in connecting this room to the
two heaters. The indicators to calculate are the evolution of the mean room temperature over time
and also the two temperature quantiles :
1. The temperature lower than the temperature of only 1% of the simulated sequences
2. The temperature higher than the temperature of only 1% of the simulated sequences
In [3]: ######################################################################################
class MySystem(Pyc.CSystem): #S1
def __init__(self, name): #S1
Pyc.CSystem.__init__(self, name) #S1
We also have to update the initial values and the configuration of the simulation. To do so we will
use a third XML file with the following items:
77
<ST NAME="aSlaveHeater.OFF" INIT="1"/> <!-- S2 -->
Below, we give a commented Python main program which launches the simulation and draws the
curves of the evolution over time of the mean of the room temperature and the two quantiles:
This program also draws the curve of the cumulative probability distribution of the indicator
function : Temperature < 14.°C and calculates the mean time spent by the room with a temperature
under 14.°C.
In [4]: ######################################################################################
if __name__ == '__main__': #S1
try :
# An instance of the system is constructed (Only one system can be
# constructed in a session)
system = MySystem("S3") #S3
78
# We create an anlyzer which holds the values of all the monitored elements
analyser = Pyc.CAnalyser(system) #S1
# This vector will holds the instants where the values of the indicators
# will be calculated
vectorOfInstants = stepsVector(system.tMax(), 500) #S1
# Calculates the evolution over time of the mean of the room temperature :
temperatureMeans = analyser.meanValues("Room.temperature",
vectorOfInstants) #S3
79
lambda t: t < 14) #S3
# This is call to a convenient function which draws the curve of the
# cumulative probability distribution calculated above
plot(vectorOfInstants, True, True, #S3
( #S3
(("Simulation of %d Sequences") % system.nbSequences(), #S3
"Time", #S3
"Probability", #S3
((hotTemperatureOccurrences, 'r', "CPDF Ind : Temperature < 14."),) #S3
), #S3
) #S3
) #S3
print("-----------------------------------------") #S3
print("Mean Time spent under 14.°C : %.2f hours"%meanResidenceTime) #S3
print("-----------------------------------------") #S3
80
Simulation time = 16.13 s
-----------------------------------------
Mean Time spent under 14.°C : 0.07 hours
-----------------------------------------
Figure 27
The first graph gives the mean and the quantile curves
of the room temperature
The second graph gives the cumulative probability
distribution of the undesired event
81
7.4 Step 4: Drawing sequences
In this step we will reuse the system S3 to which we will add the instructions that produce the
simulated sequences. The first instructions designate which transitions must be shown in the
produced sequences. Such transitions are those which are monitored. We choose to show only
the failure events of the heaters OK_to_KO and repairs events KO_to_OK. So in the XML file we
have to add the following instructions :
We will then visualize among the produced sequences only those where the temperature drops
under 14◦ C. To do so we will define a function which operates as a filter since it returns True if the
room temperature drops under 14◦ C before the end of the sequence simulation and return False
otherwise. This function has to be set as a filter on the analyzer used to retrieve the simulated
sequences.
In [4]: ######################################################################################
if __name__ == '__main__': #S1
try :
# An instance of the system is constructed (Only one system can be
# constructed in a session)
system = MySystem("S4") #S3
# We create an anlyzer which holds the values of all the monitored elements
analyser = Pyc.CAnalyser(system) #S1
# This vector will holds the instants where the values of the indicators
# will be calculated
vectorOfInstants = stepsVector(system.tMax(), 500) #S1
# Calculates the evolution over time of the mean of the room temperature :
82
temperatureMeans = analyser.meanValues("Room.temperature",
vectorOfInstants) #S3
83
), #S3
) #S3
) #S3
print("-----------------------------------------") #S3
print("Mean Time spent under 14.°C : %.2f hours"%meanResidenceTime) #S3
print("-----------------------------------------") #S3
84
Figure 28
An excerpt of the filtered sequences
produced by the simulation for the step S4
85