The control frameworks for the Unit Pool Allocation control and the Transfer to/from Unit Pool control in the new INOSIM version 13 have changed. The changes offer some new possibilities to use the controls. Even though they offer new features, you do not have to change the way to write these controls. Here we want to show what has changed and what the benefits of the new behavior of the controls are.
Unit Pool Allocation control
In INOSIM, Unit Pool Allocation controls are used to decide which unit out of a pool should be allocated by a UnitProcedure. With the new INOSIM version, the functionality of this control is improved. In version 12, the framework for the Unit Pool Allocation control looked like this:
Function Sample_UnitPoolAllocation(proc As ProcedureInstance, u As Unit) As Boolean
' Always allocate the requested unit
Sample_UnitPoolAllocation = True
End Function
The control has two input arguments: the ProcedureInstance calling the control and the unit it tries to allocate. The return value of the new control is a Boolean that allows the proposed unit to be allocated. When the control is called, it proposes the first available unit (u) in the pool. Within your control, you can decide if a unit is accepted or not. If you decline a unit, the control is called again for the next available unit in the pool. This behavior can result in many calls of the control, even though you know right away which unit must be allocated.
In Version 13, this behavior has been improved. The new framework looks like the following:
Sub Sample_UnitPoolAllocation(proc As ProcedureInstance, p As UnitPool, _
u As Unit, ByRef allocate As Boolean)
End Sub
First thing you notice is that the new control is, like other controls in INOSIM, a sub instead of a function. Instead of setting the return value of the control to true or false to accept or decline a unit, the variable allocate is used. The main improvement is that in the first call of the control it is possible to decide whether a unit can be allocated for the procedure by setting the PendingUnitsMode of the procedure for the members of the unit pool. The PendingUnitsMode can be set to puAllocate, puIgnore and puDefault. For puAllocate, the unit is used for the Procedure Instance, with puIgnore it is not used. Units with the PendingUnitsMode puDefault are treated like in Version 12, the control is called for all units with this PendingUnitsMode until a unit is accepted for the procedure. Differently than in Version 12, where u is the first proposed unit in the first call, the first call of the control in Version 13 can be identified when the argument u is nothing. Additionally, the unit pool for which the control is called has been introduced as a new argument of the control. This makes it easier to access the members of the unit pool.
Example: Multi-Product Plant
We will have a look at a multi-product plant as an example for the benefits of the new control. In our example, we have a plant which produces 20 different products and for these products has 20 different product tanks. In the model, the production of each product takes one day. After the production, the product is filled into trucks. The same Allocation Control is used for choosing the tank within the production and for choosing the tank that is emptied by a truck.
Layout for the example. There is one production line which produces 20 different products. The products are stored in 20 different tanks.
Both, the production orders and the filling orders, do have the material which they are currently producing as a property. In this example, the products and tanks are numbered by 1 to 20. So, the tank can be chosen according to the product’s name. The behavior of the control is like in Version 12. For every order, the control is called for the members of the tank pool until the fitting unit is found:
Sub Product_Tank_V12(proc As ProcedureInstance, p As UnitPool, _
u As Unit, ByRef allocate As Boolean)
If u Is Nothing Then
Exit Sub
End If
'skipping the first call of the control to have the same behavior as in Version 12
If u.Name="Tank " & Right(proc.Order.Material.Name,Len(proc.Order.Material.Name)-Len("Produkt ")) Then
allocate=True
'If the number of the product name is the same as the number of the tank
Else
allocate=False '
End If
End Sub
To find the right tank, the control tests every member of the unit pool that is currently available. If the control does not find the right unit, e.g., because it is allocated by another order, the control must be called again if any of the members of the pool is becoming available again. This can lead to a lot of calls of the control to find the correct unit.
The benefit of the behavior in Version 13 is now that in the first call of the control you can decide which unit is allowed for the allocation of the procedure. As the call is only called once, this saves a lot of simulation time.
Dim pm As Unit
If u Is Nothing Then 'identifies the first call of the control
For Each pm In p.Members
If pm.Name="Tank " & Right(proc.Order.Material.Name,Len(proc.Order.Material.Name)-Len("Produkt ")) Then
proc.PendingUnitsMode(pm.Name)=PendingUnitMode.puAllocate
'the tank which has the same number as the product is allowed to be allocated
Else
proc.PendingUnitsMode(pm.Name)=PendingUnitMode.puIgnore
'all other tanks are ignored
End If
Next
End If
Within the same model, the use of the control that exploits the new behavior of the Version 13 control saves 36% of simulation time in this simple model, when simulating one year of production. In more complex models, this could be even more advantageous. Furthermore, it is possible to assign a procedure to a unit that is not ready for allocation at the moment, e.g., because it is still allocated. In Version 12, the control would have been called for this procedure every time a unit from the pool becomes available and all units would have been tested. What you might have accomplished with custom code, INOSIM 13 is now supporting natively by assigning a fixed unit from a pool to a procedure.
Workaround / Compatibility with V12 controls
If you wish to use a Version 12 model with Version 13 or want to have the behavior of the control like it was in Version 12, you can just skip the first call of the control by inserting the following code at the very top of your control:
If u Is Nothing Then
Exit Function 'in the first call the control is skipped
End If
Transfer from/to Unit Pool Control
The behavior of the Transfer from/to Unit Pool Control changed as well. In a transfer control, you can select the sink or source for the transfer operations SimpleInflux, SimpleOutflux or SimpleTransfer. In Version 12, the framework for the Transfer from/to Unit Pool Control looks like this:
Function Sample_Transfer(cop As OrderOperation, u As Unit, _
amount As Double, ByRef retry As Boolean) As Unit
' take the first suitable unit
Set Sample_Transfer = cop.SourceSinkUnits(1)
End Function
The inputs of the control are the current OrderOperation that is calling the control, the remaining amount of the transfer, and the Boolean retry. If retry is set to true, the control is called again to select another suitable unit in case the transfer is stopped due to full or empty units. The parameter u is nothing in the first call of the control. In later calls, it is the currently used unit for the transfer. The return value of the function is the unit that should be used for the transfer.
In the new version, the framework has slightly changed:
Sub Sample_Transfer(p As UnitPool, cop As OrderOperation, amount As Double, _
ByRef u As Unit, ByRef retry As Boolean)
End Sub
First of all, the control is no longer a function with a return value but a procedure. The parameter u is the unit that is used for the transfer. In the first call, the proposed unit for the transfer is stored in the parameter u. To identify the first call of the control in Version 13, you would use:
If cop.Transfer Is Nothing Then
'first call
Else
'later call
End If
In case the transfer property is nothing, you can change the parameter u to the unit that should be used for the transfer. As an additional parameter, you have the unit pool for which the control is called.
Example: Educt Transfer
In the following example, Educt A for a reaction should be taken from a tank pool. In the first call of the control, a tank is set for the transfer. When this tank runs empty the control is called again and the fullest tank from the pool is selected to continue the transfer. For the next transfer, the same tank is used until it is empty. Then again, the now fullest tank should be selected. In Version 12, the control could look like this:
Public LastEductTank As Unit 'Global Variable
Function Educt_Transfer(cop As OrderOperation, u As Unit, amount As Double, _
ByRef retry As Boolean) As Unit
Dim pm As Unit
Dim EductTank As Unit
Dim i As Integer
retry=True
If LastEductTank Is Nothing Then
'in the beginning the last tank has to be set to the first member of
'the SourceSinkUnits collection, the pool cannot directly be accessed
Set LastEductTank=cop.SourceSinkUnits(1)
End If
If u Is Nothing Then
'for the first call of the control the tank from the last
'transfer should be used until it is empty
Set EductTank=LastEductTank
Else
'if one tank runs empty, the control is called again and
'the tank with the highest content should be used
Set EductTank=u
For i=1 To cop.SourceSinkUnits.Count
If cop.SourceSinkUnits.Item(i).Contents > EductTank.Contents Then
Set EductTank=cop.SourceSinkUnits.Item(i)
End If
Next
End If
Set Educt_Transfer=EductTank 'set the chosen unit
Set LastEductTank=EductTank 'update the last educt tank used
End Function
In Version 12, the unit pool for which the control is called cannot be addressed directly. Instead, you could access the SourceSinkUnits collection of the operation. In Version 13, it is possible to directly access the unit pool that calls the control.
Public LastEductTank As Unit 'Global Variable
Sub Educt_Transfer(p As UnitPool, cop As OrderOperation, amount As Double, _
ByRef u As Unit, ByRef retry As Boolean)
Dim pm As Unit
Dim EductTank As Unit
retry=True
If LastEductTank Is Nothing Then
'in the beginning the Last Tank has to be set to
'the first member of the unit pool
lastEductTank=p.Members.Item(1)
End If
If cop.Transfer Is Nothing Then
'identifies the first call of the control for this transfer
'for the first call of the control the tank from the
'last transfer should be first used until it is empty
EductTank=LastEductTank
Else
'if one tank runs empty, the control is called again
'and the tank with the highest content should be used
EductTank=u
For Each pm In p.Members
If pm.Contents > EductTank.Contents Then
EductTank=pm
End If
Next
End If
u=EductTank 'set the chosen unit
LastEductTank=u 'update the last Educt tank used
End Sub
With the control in Version 12, it was not possible to access information about the Unit Pool for which the control is called. Now it is not only possible to access the members of this pool, but also other properties, like its name.
Workaround / Compatibility with V12 Controls
If you want to use an existing model from Version 12 in Version 13, you can keep the old controls. One thing that needs to be changed is the way to check if it is the first call of the control. In Version 12, u is nothing, but in Version 13 in the first call u is the first proposed unit. Here, to identify the first call you can use:
If cop.Transfer Is Nothing Then
'first call
Else
'later call
End If
- PDF printout of this Tip & Trick
More Questions?
Want to know more about this topic or have another question? Please contact us!
More Tips & Tricks
Two Ways Of Simulating Unit Failures
In your INOSIM project, you can integrate unit failures to represent reality even more precisely in your model, as failures might have a great impact…
Benefit from your Excel VBA Know-how
Benefit from your Excel VBA Know-how In this tip and trick we want to show you how to activate and use Excel’s VBA commands from…
INOSIM 13 – WWB.NET vs. WWB-COM
With the INOSIM 13 Version, the Basic Editor supports, in addition to WWB-COM, the WWB.NET language, which is compatible with Visual Basic .NET. Therefore, also…