Introduction

Intended audience

System integrators who want to develop solutions based on the modeling of industrial continuous processes with the LIBPF™ enabling technology.

Scope

This is a simple tutorial for the usage of the Pasteurization models through the Python Model User API.

We will list the available model types, instantiate one model case, manipulate it, calculate it and get some results.

Prerequisites

  • Basic knowledge of Python 2.7.

  • You should have received from a Model Developer a process model in the form of a kernel, prepared for interfacing with Python. This comes as two files you should put in your current directory:

    • LibpfUserPython.py: the module definition

    • _LibpfUserPython.so: the shared library that contains the actual implementation.

in this example is it assumed that you are working with the Pasteurization model and process (see references below).

Step-by-step

Enter the following Python code in a test.py file.

Set-up

Python scripts start with the shebang to indicate that the Python interpreter should be used for execution.

Next we need to load the LibpfUserPython module and the json built-in Python module for JSON encoding /decoding.

Finally we initialize the LibpfUserPython module with initializeKernel.

#!/usr/bin/env python
import LibpfUserPython
import json
LibpfUserPython.initializeKernel()

See what model types are supported

Before we create a case (an instance of a model type) we might want to ask the LibpfUserPython module for the supported model types.

This is easy to do using the Kernel jsonListTypes API.

This method returns a complex JSON object, but in this example we just list the names of the types that can actually be instantiated (many types can only occur as sub-objects and can not be directly instantiated).

Note that the functions in the LibpfUserPython module return error codes that you should better check !

error = LibpfUserPython.intPointer()
error.assign(1)
print '== Now listing the types that can be instantiatied'
jlt = LibpfUserPython.jsonListTypes(error)
print '== jsonListTypes error code = %d '% error.value()
lt = json.loads(jlt)
print '== List of types that can be instantiatied:'
for t in lt['types']:
    if t['instantiable']:
        print t['name']

Create a case instance

Let us proceed to create a case instance of type Pasteur with some string options set.

For that, we need first to setup a JSON object with the required information. The easy way to do so in Python is to populate a dictionary and then encode it in JSON.

The createCase method can be called with the JSON object as argument, and it will return a handle which can be used to access the new case.

cd = {u'type': u'Pasteur',
      u'tag': u'test',
      u'description': u'my first test',
      u'stringOptions': {u'processType': u'HTST25',
                        u'feedType': u'chocolateIceCream'},
      u'integerOptions': {}}
cdj = json.dumps(cd)
print '== Now instantiating type Pasteur:'
ch = LibpfUserPython.createCase(cdj, error)
print '== createCase error code = %d' % error.value()
myCase = LibpfUserPython.Case(ch)
print '== Directory of case object:'
dir(myCase)

Calculate the case

A case right after instantiation has all variables at their default value. For the variable values to be meaningful, it must be explicitely calculated with a call to the calculateSync Case API endpoint.

print '== Now calculating instance'
ret = myCase.calculateSync()
print '== calculateSync error code = %d' % ret

Make changes and recalculate

The main use of the LIBPF™ Python Model User API is to change the values of inputs and request additional calculations.

The value changes must be submitted to the set Case API endpoint as a JSON object, and the calculation must be explicitely requested with a call to the calculateSync endpoint:

hi = {u'controlled': [{u'variable': u'coolT', u'end': 283.15},
                      {u'variable': u'HEATER.deltaP', u'end': 12000.0}]}
jhi = json.dumps(hi)
print '== Now setting variables'
ret = myCase.set(jhi)
print '== set error code = %d '% ret
ret = myCase.calculateSync()
print '== calculateSync error code = %d' % ret

Read results

The results can be accessed via the get Case API endpoint.

mdot = myCase.get("S01:Tphase.mdot", error)
print '== get error code = %d' % error.value()
print '== S01:Tphase.mdot = %g' % mdot

Clean-up

For clean-up, de-initialize the LibpfUserPython module with uninitializeKernel.

LibpfUserPython.uninitializeKernel()

Execution

Make the test.py file executable then launch it:

chmod u+x test.py 
./test.py

Expected output

LIBPF is correctly activated for site com.example_1.0 on this computer.
* ****************** LIBPF 01.00.2193 [2015/06/07 15:28:37] ******************
* (C) Copyright 2004-2015 Paolo Greppi simevo s.r.l.
* ******************************** Pasteurize ********************************
* (C) Copyright 2014-2015 Paolo Greppi simevo s.r.l.
* ****************************************************************************
* All rights reserved; do not distribute without permission.
* ****************************************************************************

== Now listing the types that can be instantiatied
== jsonListTypes error code = 0 
== List of types that can be instantiatied:
Pasteur
PasteurHTST15_milkWhole
== Now instantiating type Pasteur:
Libpf::User::createCase * Creating a new model [ si [] ss [[ feedType, chocolateIceCream] [ processType, HTST25] ]]
createCase_ * Instantiate flowsheet
...
createCase_ * Creation complete
createCase_ * Saving to persistent storage
createCase_ * Saved to persistent storage
== createCase error code = 0
Libpf::User::Case::Case *** Retrieving dd504852-5eea-4d8c-bb9f-1f8f3cf2ab86 of type Pasteur from persistent storage database
Pipe::Pipe *** Entered with 964
Pasteur::Pasteur *** Entered
== Directory of case object:
== Now calculating instance
FlowSheet::calculate * Starting sequential pre-computation
...
FlowSheet::calculate * Switch to simultaneous computation
...
Model::reportWarning ** ===== Logging warning convergence on deltax; dubious convergence in test
Libpf::User::Case::calculateSync * Computation complete with 0 errors and 1 warnings
Libpf::User::Case::calculateSync * Updating results to persistent storage
Libpf::User::Case::calculateSync * Updated results to persistent storage
== calculateSync error code = 0
== Now setting variables
Trying to set coolT
Trying to set HEATER.deltaP
== set error code = 0 
...
Model::reportWarning ** ===== Logging warning convergence on deltax; dubious convergence in test
Libpf::User::Case::calculateSync * Computation complete with 0 errors and 1 warnings
Libpf::User::Case::calculateSync * Updating results to persistent storage
Libpf::User::Case::calculateSync * Updated results to persistent storage
== calculateSync error code = 0
== get error code = 0
== S01:Tphase.mdot = 1

Pasteurization Model references

LIBPF references