General information

The Fluid Library allows you to model storage and transfer of fluids, bulk matter, or large amounts of discrete items, which you do not want to model as separate objects.

The library includes blocks such as Tank, Pipeline, Valve, FluidSource, and FluidDispose. There are also blocks for routing, merging, and diverging the flow. In addition, there is an object FluidConveyor designed to model transfer of bulk or condensable matter. See the full list of Fluid Library blocks here.

The Fluid Library interoperates with the Process Modeling Library: it can convert agents into portions of fluid and vice versa.

The Fluid Library blocks put linear constrains on the flow rates, such as maximum rates, proportional rates, and so on. The library engine maximizes the flow throughout the system. As a result, the flow rates in the library are piecewise-constant (constant within time intervals), and only change instantly at discrete moments of time.

The linear nature of the flow dynamics allows for using LP (linear programming) solver to calculate the maximum rates. The solver is invoked only at the discrete moments of change, which makes the execution speed of the Fluid Library models a lot higher than that of the System Dynamics models. At the same time, the LP calculations are much more accurate as there is no notion of time step in the LP solver. We recommend using the Fluid Library when the system is linear, and using System Dynamics only for non-linear cases, i.e. when there are continuous feedback loops in the model, or continuous rate changes.

Maximizing rates and flow priorities

By default, these blocks of the Fluid Library ask the LP solver to maximize their outflow: FluidSource, AgentToFluid, Tank, and BulkConveyor. In addition, the FluidMerge and FluidSplit blocks in the priority mode will ask to maximize the flow at one of the inputs or outputs. Internally, this is done by raising the priority (coefficient) of the corresponding rate in the LP objective function. By default, the rate being maximized gets the priority 1, all other rates – 0. However, this will not always result in the desirable global picture. The user can create flowchart configurations with priority conflicts, and the library will not be able to figure out the correct global priorities without additional information. The Fluid Library allows you to provide that information in the form of custom priority values, see the corresponding parameters of the blocks listed above. The color indication of priority violation in FluidSplit and FluidMerge blocks may help you to track down issues with flow distribution.

Maximum and minimum rates and amounts

If a rate evaluates to Utils.MAXIMUM_RATE (1.0E10) during runtime, the model stops with an error “Flow rate evaluated to the maximum value. You need to set a limit for this rate.” This is done because typically maximum rate is not normal and is a result of the user forgetting to set a limit somewhere. To avoid this, please set the upper limit for rate in flowchart block(s) where required.

Handling numeric errors

In the Fluid Library, rates, amounts, time intervals, and other values are frequently recalculated with the use of linear formulas, e.g.: amount = rate * time interval, etc. As a result, numeric errors can occur. For example, if the tank capacity is 100 cubic meters, and the inflow keeps the rate of exactly 1 cubic meter per second for exactly 100 seconds, the "straightforward" implementation of the tank could result with the amount of, say, 100 /- 1e-12 cubic meters. The Fluid Library does its best to minimize the effect of numeric errors by snapping the calculated values to where they should be. The snapping uses Utils.TOLERANCE – an internal constant that is applied across the library.

Here are some important places where TOLERANCE is used:

Although normally you should not think about numeric errors while building models, sometimes the combination of time, rate, and amount units of different scale may cause very small numeric values, or small value variations, and then this knowledge may be useful.

Similarly, there is another notion of tolerance for rates, the corresponding constant is Utils.RATE_TOLERANCE. Its value is 1.0e-9 and it works like this:


Theoretically, one can create a flow configuration that may cause a livelock (infinite event loop without progress in time). The simplest example would be a user action in the On rate change field of a Fluid Library block that causes another rate recalculation and, therefore, another call of On rate change. The recommendation, obviously, would be to avoid making such actions. However, more complicated situations may occur. For example, the event of a certain tank becoming empty can cause rate recalculation, which, in turn, can switch the tank to a non-empty state. This changes the rates back to the initial state, and the tank becomes empty again. Although there is no universal method to cure a livelock, a general advice would be to play with custom rate priorities in Split / Merge blocks in order to stabilize the rate system.