Task 2.1 - Simple pipeline
Task Overview
We will build the following model:
Batches of 1000 m³ of random products (product IDs 1 through 20) are produced into a suitably sized tank. Once full, the contents will be pumped into a pipeline with one receiving tank on the other end. The tank has a capacity of 5000 m³ to accommodate the statistical case in which multiple batches of the same type are produced in sequence. However, once the product flowing out of the pipe changes, the receiving tank first has to empty into some other process that we will not model.
Step 1 Create the basic model
Setup the basic model: connect a flow source, tank, two flow pipes, another tank and a sink together. Rotate the second flow pipe by 90 degrees. Set the following basic properties:
- The source has a maximum outflow rate of 100.
- The first tank has a maximum capacity of 1000 and maximum inflow and outflow rate of 100.
- The flow pipes have a maximum flow rate of 100.
- The second tank has a maximum capacity of 5000 and maximum inflow and outflow rate of 100.
- The sink has an unlimited inflow rate (default).
When the model is run, nothing interesting happens: this is a static situation in which all the flow rates are maximized. Let's add a few triggers to make the first tank fill completely, then empty completely.
Open the properties of the tank, and go to the Triggers tab. Click the Add button () next to the On Empty trigger. From the “FloWorks basics” menu, select “Open or close flow” and “Open inputs and close outputs”. This will ensure that the tank will start filling up again without releasing into the pipe as soon as it empties.
Similarly, add a trigger to the On Full event that will “Close inputs and open outputs” to stop the inflow and start releasing into the pipe when the full 1000 m³ batch has been produced.
Finally, FloWorks objects start with all their inputs and outputs opened by default, so you need to make sure that you don't end up in the static situation on reset. Add the “Open inputs and close outputs” trigger to the On Reset event as well. Run the model; the final situation for this step should look as follows:
Step 2 Producing and sending batches of different products
The production tank now fills and empties with the same product every cycle.
As you know, FloWorks products do not automatically carry over from one object to the next, so the product type of the source is irrelevant. Instead you will sample a new product every time the tank starts filling a new batch.
Go into the properties of the tank and once more select the Triggers tab. Use the properties () button to open the
existing trigger logic for the On Empty trigger. Then use the Add () button at the bottom left to add another action to
this trigger. From the “FloWorks basics” menu, select “Change product type”. The value current
for Object references the
tank itself, so leave that as it is. For the product, sample a random value between 1 and 10 (with equal probabilities) by
entering the expression duniform(1, 10)
into the Product field.
When you run the model now, you will see a different product color being produced in the tank at every new cycle in general – but note that because of the nature of statistics sometimes you will get two consecutive batches of the same product.
We will also want to change the inflow product of the pipe when the production tank starts emptying. On the Triggers tab,
open the properties of the On Full trigger, and add another “Change product type” action. However, this time you should not
change the product of the tank itself, but of the flow pipe it's connected to. To do this, use the Sampler button next to the
Object field and click on the pipe. Enter current.product
for the product to set on the first pipe.
Finally, you will notice that the product is not automatically passed from the first flow pipe into the next. Double click on the first flow pipe, go to the Triggers tab and add a new trigger action for the On Product Out Change, which is executed every time a different product reaches the end of the pipe. From the menu, select the “Pass product downstream” option with the default options.
If you have followed all the steps correctly, you should now see randomly colored batches move through the pipelines into the receiving tank. In the next step you will implement the batching logic on the receiving side.
Step 3 Batching in the receiving tank
The receiving tank will have similar logic to the producing tank. When it is empty, or when the model is reset, it will open its inputs and close its outputs. Add these triggers to the tank now.
In contrast to the previous step, however, the tank does not decide for itself when to switch from input mode
to output mode. This will be determined by the On Product Out Change trigger on the flow pipe. Add a “Close
Inputs and Open Outputs” action there, but instead of applying the action to the current
object
(i.e. the flow pipe) use the sampler button () to click on the receiving tank.
FlexSim will automatically reference the tank as current.outObjects[1]
.
If you are not used to working with these kinds of expressions, try reading them right to left: the object that the action
applies to is the first ([1]
) output object of the current object, where the current object is always the object
that you are setting the trigger on – the Flow Pipe in this case. It is considered good practice to use such references as
they are more robust than referencing the tank by its name in the model. For example, you could now rename the receiving
tank or connect the pipe to another tank altogether without having to remember to update the trigger action.
Step 4 Setting the product in the receiving tank
You are almost done! When you run the model, you will see different products flowing through the pipeline, into the receiving tank. The latter will continue to accept input as long as the inflow product does not change. When it does switch, it will block the inflow and first empty itself into the sink. The only thing remaining is that the receiving tank does not switch products based on the incoming product yet.
Unfortunately, you cannot use the “Pass product downstream” option on the pipe's On Product Out Change trigger to do that.
Just like on the sending side, the product needs to be changed in the On Empty trigger of the receiving tank. The idea is similar to what you've done before, except the expressions become a bit more complicated. First let's do it: add a “Change product type” action to the On Empty trigger of the receiving tank, in addition to the “Open Inputs and Close Outputs” action that it already had. Set the product on current, which is the tank itself, and for the new product type enter the following expression:
current.inObjects[1].as(FlowObject).product
We'll break this down in just a moment, first reset and run the model and check that you get the desired result.
What does that expression mean?
You should see the receiving tank now taking on the correct product type every time it starts filling again.
So how does that expression you entered work? Before, we have used the expression current.outObjects[1]
to get at
the first output object of the current object; similarly we can get the object connected to input port 1 with
current.inObjects[1]
.
We would like to get the product type of this object, which is a property called product
.
So going with the “read-from-right-to-left” strategy, we would like to write current.inObject[1].product
(“the product of the first input object of ...”). However, there is small catch which has to do with typing:
according to FlexSim, input objects are of the type Object
, which can be anything like Queues, Processors, Conveyors, etc.
And of course, Object
s don't – in general – have a product. It's just in this particular model that we, as human modellers,
happen to know that the input object is not just any Object
, it's actually a more specific type of object
known as FlowObject
. This hierarchy is shown in the diagram below, where an arrow pointing from A
to
B
means that A
is actually a more specific type of B
, usually inheriting all its properties
(so that you can still call obj.outObjects[1]
defined on Object
when obj
happens to be a FlowObject
)
but adding some properties of its own (such as FlowObject
s having a .product
property that Object
s don't have).
By inserting ....as(FlowObject)
you tell FlexSim that the whole expression to the left of it,
current.inObjects[1]
, is actually a FlowObject
on which you can then call ....product
.
Conclusion
In this task you have used some default trigger options to control flows and product types. In the next tutorial task, you'll learn how to control flows from trigger logic to create a concept similar to the use of tank pools in chemical processing. Continue to Tutorial Task 2.2 - Creating a tank pool.