Initializing help system before first use

Other programming language interfaces

Topics covered in this chapter:

In this chapter we show how the examples from Chapter C interface may be written with other programming languages, namely Java, .NET and VBA.

Java

To use the Mosel Java classes the line import com.dashoptimization.*; must be added at the beginning of the program.

Compiling and executing a model in Java

With Java Mosel is initialized by creating a new instance of class XPRM. To execute a Mosel model in Java we call the three Mosel functions performing the standard compile/load/run sequence as shown in the following example (file ugcomp.java).

import com.dashoptimization.*;

public class ugcomp
{
 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;

  mosel = new XPRM();                       // Initialize Mosel

  System.out.println("Compiling `burglar2'");
  mosel.compile("burglar2.mos");

  System.out.println("Loading `burglar2'");
  mod = mosel.loadModel("burglar2.bim");

  System.out.println("Executing `burglar2'");
  mod.run();

  System.out.println("`burglar2' returned: " + mod.getResult());
 }
}

Termination

If the model execution is embedded in a larger appplication it may be useful to reset the model after its execution to free some resources allocated to it:

  mod.reset();                        // Reset the model

This will release all intermediate objects created during the execution without deleting the model itself.

It is also possible to explicitly remove the temporary directory/files created by the execution of Mosel:

  mosel.removeTmpDir();               // Delete temporary files

Unloading models or Mosel from memory is ensured through standard finalization + garbage collection functionalities of Java. The finalizers are public and may be called from the user's Java program. Finalization of Mosel only takes effect once all loaded models have been finalized. Finalizing Mosel also removes the temporary directory/files created by the execution of Mosel.

  mod.finalize();                     // Finalize a model
  mod = null;
  mosel.finalize();                   // Finalize Mosel
  mosel = null;

Parameters

When executing a Mosel model in Java, it is possible to pass new values for its parameters into the model. If, for instance, we want to run model `Prime' from Section Working with sets to obtain all prime numbers up to 500 (instead of the default value 100 set for the parameter LIMIT in the model), we may write the following program:

import com.dashoptimization.*;

public class ugparam
{
 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  int LIM=500;

  mosel = new XPRM();                       // Initialize Mosel

  System.out.println("Compiling `prime'");
  mosel.compile("prime.mos");

  System.out.println("Loading `prime'");
  mod = mosel.loadModel("prime.bim");

  System.out.println("Executing `prime'");
  mod.execParams = "LIMIT=" + LIM;
  mod.run();

  System.out.println("`prime' returned: " + mod.getResult());
 }
}

Using the Mosel Java interface, it is not only possible to compile and run models, but also to access information on the different modeling objects as is shown in the following sections.

Accessing sets

A complete version of a program (file ugparam.java) for running the model `Prime' may look as follows (we work with a model prime2 that corresponds to the one printed in Section Working with sets but with all output printing removed because we are doing this in Java, and all entities accessed from Java are explicitly declared as public):

import com.dashoptimization.*;

public class ugparam
{
 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  XPRMSet set;
  int LIM=500, first, last;

  mosel = new XPRM();                   // Initialize Mosel

  System.out.println("Compiling `prime2'");
  mosel.compile("prime2.mos");

  System.out.println("Loading `prime2'");
  mod = mosel.loadModel("prime2.bim");

  System.out.println("Executing `prime2'");
  mod.execParams = "LIMIT=" + LIM;
  mod.run();

  System.out.println("`prime2' returned: " + mod.getResult());

  set=(XPRMSet)mod.findIdentifier("SPrime");  // Get the object 'SPrime'
                                              // it must be a set
  if(!set.isEmpty())
  {
   first = set.getFirstIndex();         // Get the number of the first index
   last = set.getLastIndex();           // Get the number of the last index
   System.out.println("Prime numbers from 2 to " + LIM);
   for(int i=first;i<=last;i++)         // Print all set elements
    System.out.print(" " + set.getAsInteger(i) + ",");
   System.out.println();
  }
 }
}

To print the contents of set SPrime that contains the desired result (prime numbers between 2 and 500), we retrieve the Mosel object of this name using method findIdentifier. If this set is not empty, then we enumerate the elements of the set (using methods getFirstIndex and getLastIndex to obtain the index range).

Retrieving solution values

The following program ugsol.java executes the model `Burglar3' (the same as model `Burglar2' from Chapter Some illustrative examples but with all output printing removed and all model entities declared as public) and prints out its solution.

import com.dashoptimization.*;

public class ugsol
{
 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  XPRMArray varr, darr;
  XPRMMPVar x;
  XPRMSet set;
  int[] indices;
  double val;

  mosel = new XPRM();                 // Initialize Mosel

  mosel.compile("burglar3.mos");      // Compile, load & run the model
  mod = mosel.loadModel("burglar3.bim");
  mod.run();

  if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
   System.exit(1);                    // Stop if no solution found

  System.out.println("Objective value: " + mod.getObjectiveValue());
                                      // Print the objective function value

  varr=(XPRMArray)mod.findIdentifier("take");  // Get model object 'take',
                                               // it must be an array
  darr=(XPRMArray)mod.findIdentifier("VALUE"); // Get model object 'VALUE',
                                               // it must be an array
  set=(XPRMSet)mod.findIdentifier("ITEMS");    // Get model object 'ITEMS',
                                               // it must be a set

  indices = varr.getFirstIndex();     // Get the first entry of array varr
                                      // (we know that the array is dense)
  do
  {
   x = varr.get(indices).asMPVar();   // Get a variable from varr
   val = darr.getAsReal(indices);     // Get the corresponding value
   System.out.println("take(" + set.get(indices[0]) + "): " +
                      x.getSolution() + "\t (item value: " + val + ")");
                                      // Print the solution value
  } while(varr.nextIndex(indices));   // Get the next index

  mod.reset();                        // Reset the model
 }
}

The array of variables varr is enumerated using the array functions getFirstIndex and nextIndex. These methods may be applied to arrays of any type and dimension. With these functions we run systematically through all possible combinations of index tuples, hence the hint at dense arrays in the example. In the case of sparse arrays it is preferrable to use different enumeration functions that only enumerate those entries that are defined (see next section).

Sparse arrays

We now again work with the problem `Transport' that has been introduced in Chapter More advanced modeling features. The objective of this problem is to calculate the flows flowpr from a set of plants to a set of sales regions that satisfy all demand and supply constraints and minimize the total cost. Not all plants may deliver goods to all regions. The flow variables flowpr are therefore defined as a sparse array. The following example ugarray.java prints out all existing entries of the array of variables.

import com.dashoptimization.*;

public class ugarray
{
 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  XPRMArray varr;
  XPRMSet[] sets;
  int[] indices;
  int dim;

  mosel = new XPRM();                  // Initialize Mosel

  mosel.compile("transport.mos");      // Compile, load & run the model
  mod = mosel.loadModel("transport.bim");
  mod.run();

  varr=(XPRMArray)mod.findIdentifier("flow"); // Get model object 'flow'
                                       // it must be an array
  dim = varr.getDimension();           // Get the number of dimensions
                                       // of the array
  sets = varr.getIndexSets();          // Get the indexing sets

  indices = varr.getFirstTEIndex();    // Get the first true entry index
  do
  {
   System.out.print("flow(");
   for(int i=0;i<dim-1;i++)
    System.out.print(sets[i].get(indices[i]) + ",");
   System.out.print(sets[dim-1].get(indices[dim-1]) + "), ");
  } while(varr.nextTEIndex(indices));  // Get next true entry index tuple
  System.out.println();

  mod.reset();                         // Reset the model
 }
}

In this example, we first get the number of indices (dimensions) of the array of variables varr (using method getDimension). We use this information to enumerate the entries of every index tuple for generating a nicely formatted output. The array sets holds all the index sets of varr and the array indices corresponds to a single index tuple.

The enumeration starts with the first defined index tuple, obtained with method getFirstTEIndex, and continues with a series of calls to nextTEIndex until all defined entries have been enumerated.

Exchanging data between an application and a model

In the previous examples we have seen how to retrieve information about the model objects from a Mosel model after its execution. In all cases the input data is defined in the model itself or read in from an external (text) file. However, when embedding a model into an application frequently the input data for the model will be stored (or generated by) the application itself. In such a case the user will certainly wish a more immediate means of communication to the model than having to write the input data to an external text file or database. In the following two subsections we therefore show how to pass data in memory from an application to a Mosel model, and with the same mechanism (namely, using the jraw I/O driver) from the model back to the calling application.

Dense arrays

As a first example we shall look at the case of dense arrays holding the input and solution data. In the underlying Mosel model this corresponds to arrays indexed by range sets that are known in the model before the data are read in. In this example, we shall work with a version of the `Burglar model based on the very first version we have seen in Section The burglar problem where all arrays are indexed by the range set ITEMS = 1..8.

The following Java program ugiodense.java compiles, loads, and runs a Mosel model and then prints out the solution values. The input data (arrays vdata and wdata) and the array solution that is to receive the solution values are passed on to the model through model parameters. Communication of the data between the application and the Mosel model is achieved through the jraw I/O driver. File names for this driver have the form jrawoption[,...],filename, where filename is an object reference. Since we are working with dense, one-dimensional arrays we use the option noindex, indicating that only the data and not the index tuples are to be exchanged.

import com.dashoptimization.*;

public class ugiodense
{                                     // Input data
 static final double[] vdata={15,100,90,60,40,15,10, 1};   // VALUE
 static final double[] wdata={ 2, 20,20,30,40,30,60,10};   // WEIGHT

                                      // Array to receive solution values
 static double[] solution = new double[8];

 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;

  mosel = new XPRM();                 // Initialize Mosel

  mosel.compile("burglar8.mos");      // Compile & load the model
  mod = mosel.loadModel("burglar8.bim");

                      // Associate the Java objects with names in Mosel
  mosel.bind("vdat", vdata);
  mosel.bind("wdat", wdata);
  mosel.bind("sol", solution);
                      // File names are passed through execution parameters
  mod.execParams =
   "VDATA='noindex,vdat',WDATA='noindex,wdat',SOL='noindex,sol'";

  mod.run();                          // Run the model

  if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
   System.exit(1);                    // Stop if no solution found

                      // Display solution values obtained from the model
  System.out.println("Objective value: " + mod.getObjectiveValue());
  for(int i=0;i<8;i++)
   System.out.println(" take(" + (i+1) + "): " + solution[i]);

  mod.reset();                        // Reset the model
 }
} 

The model file burglar8.mos is the same as model burglar6.mos from Section Dense arrays with the only difference that the name of the I/O driver in the initializations blocks now is jraw instead of raw, such as:

 initializations from 'jraw:'
  VALUE as VDATA  WEIGHT as WDATA
 end-initializations 

Sparse arrays

Let us now study the probably more frequent case of data stored in sparse format. In the Mosel model (burglar9.mos) we use a set of strings instead of a simple range set to index the various arrays and in the Java program (ugiosparse.java) we need to define slightly more complicated structures to hold the indices and the data entries. To save us writing out the indices twice, we have grouped the two input data arrays into a single class. When passing the data arrays to the Mosel model we now do not use any option, meaning that data is transferred in sparse format. Instead, we now need to indicate which fields of the Java objects are to be selected (in brackets after the object reference).

import com.dashoptimization.*;

public class ugiosparse
{
                        // Class to store initial values for array 'data'
 public static class MyData
 {
  public String ind;                // index name
  public double val,wght;           // value and weight data entries
  MyData(String i, double v, double w)
  { ind=i; val=v; wght=w; }
 }
                        // Class to receive solution values
 public static class MySol
 {
  public String ind;                // index name
  public double val;                // solution value
 }

 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  MyData data[]={new MyData("camera",15,2), new MyData("necklace",100,20),
                 new MyData("vase",90,20), new MyData("picture",60,30),
		 new MyData("tv",40,40), new MyData("video",15,30),
                 new MyData("chest",10,60), new MyData("brick",1,10)};
  MySol[] solution=new MySol[8];

  for(int i=0;i<8;i++) solution[i] = new MySol();

  mosel = new XPRM();                 // Initialize Mosel

  mosel.compile("burglar9.mos");      // Compile & load the model
  mod = mosel.loadModel("burglar9.bim");

                        // Associate the Java objects with names in Mosel
  mosel.bind("dt", data);
  mosel.bind("sol", solution);
                        // File names are passed through execution parameters
  mod.execParams = "DATA='dt(ind,val,wght)',SOL='sol(ind,val)'";

  mod.run();                          // Run the model

  if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
   System.exit(1);                    // Stop if no solution found

                        // Display solution values obtained from the model
  System.out.println("Objective value: " + mod.getObjectiveValue());
  for(int i=0;i<8;i++)
   System.out.println(" take(" + solution[i].ind + "): " + solution[i].val);

  mod.reset();                        // Reset the model
 }
} 

The model burglar9.mos run by this program is the same as the model burglar7.mos displayed in Section Sparse arrays, but using the I/O driver jraw instead of raw.

Dynamic data

The two examples of in-memory communication of dense and sparse data in the preceding sections have in commun that all data structures in the application, and in particular the structures to receive output data, are of fixed size. We therefore now introduce an alternative communication mechanism working with streams, that enables dynamic sizing of data structures on the application level, a feature that is particularly useful for solution output where effective data sizes are not known a priori. This communication mechanism employs the I/O driver java (see also Section Redirecting the Mosel output). The main part of our Java program (file ugiocb.java) now looks as follows.

 public static modelInit cbinit=new modelInit();

 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;

  mosel = new XPRM();                 // Initialize Mosel

  mosel.compile("burglar13.mos");     // Compile & load the model
  mod = mosel.loadModel("burglar13.bim");

                        // File names are passed through execution parameters
  mod.execParams = "DATAFILE='java:ugiocb.cbinit'," +
                   "SOLFILE='java:ugiocb.cbinit'";

  mod.run();                          // Run the model

  if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
   System.exit(1);                    // Stop if no solution found

                        // Display solution values obtained from the model
  System.out.println("Objective value: " + mod.getObjectiveValue());
  for(int i=0;i<solsize;i++)
   System.out.println(" take(" + solution[i].ind + "): " + solution[i].val);

  mod.reset();                        // Reset the model
 } 

The information passed to the model in the runtime parameters now is an instance of a class that implements interfaces for initialization from and to streams as shown below. The functionality for dynamic output retrieval employs the Mosel library functions that we have already seen in Sections Accessing sets and Retrieving solution values for accessing models after their termination. The dynamic data input to a Mosel model uses a new set of dedicated functions that are explained with some more detail after the program extract.

 static final double[] vdata={15,100,90,60,40,15,10, 1};   // VALUE
 static final double[] wdata={ 2, 20,20,30,40,30,60,10};   // WEIGHT
 static final String[] ind={"camera", "necklace", "vase", "picture",
     "tv", "video", "chest", "brick"};                     // Index names
 static final int datasize=8;

 public static class MySol {
  public String ind;                // index name
  public double val;                // solution value
 }
 static MySol[] solution;
 static int solsize;


 public static class modelInit implements XPRMInitializationFrom, XPRMInitializationTo
 {
  public boolean initializeTo(String label, XPRMValue value)
  {
   XPRMArray solarr;
   XPRMSet[] sets;
   int[] indices;
   int ct;

   if(label.equals("SOL"))
   {
    solarr=(XPRMArray)value;
    solsize=solarr.getSize();
    solution = new MySol[solsize];
    for(int i=0;i<solsize;i++) solution[i] = new MySol();

    sets = solarr.getIndexSets();          // Get the indexing sets
    ct=0;
    indices = solarr.getFirstTEIndex();    // Get the first entry of the array
    do
    {
     solution[ct].ind=sets[0].getAsString(indices[0]);
     solution[ct].val=solarr.getAsReal(indices);
     ct++;
    } while(solarr.nextTEIndex(indices));  // Get the next index
   }
   else System.out.println("Unknown output data item: " + label + "=" + value);
   return true;
  }

  public boolean initializeFrom(XPRMInitializeContext ictx, String label, XPRMTyped type)
  {
   try
   {
    if(label.equals("DATA"))
    {
     ictx.sendControl(ictx.CONTROL_OPENLST);
     for(int i=0;i<datasize;i++)
     {
      ictx.sendControl(ictx.CONTROL_OPENNDX);
       ictx.send(ind[i]);
      ictx.sendControl(ictx.CONTROL_CLOSENDX);
      ictx.sendControl(ictx.CONTROL_OPENLST);
       ictx.send(vdata[i]);
       ictx.send(wdata[i]);
      ictx.sendControl(ictx.CONTROL_CLOSELST);
     }
     ictx.sendControl(ictx.CONTROL_CLOSELST);
     return true;
    }
    else
    {
     System.err.println("Label `"+label+"' not found.");
     return false;
    }
   }
   catch(java.io.IOException e)
   {
    System.err.println("`"+label+"' could not be initialized - "+e);
    return false;
   }
  }
 } 

The format used to represent data for dynamic data input is the same as the default text format used by initializations blocks. For example, the array definition

 mydata: [ ("ind1" 3) [5 1.2] ("ind2" 7) [4 6.5] ] 

is represented by the following sequence of function calls:

ictx.sendControl(ictx.CONTROL_OPENLST);     ! [
ictx.sendControl(ictx.CONTROL_OPENNDX);     !   (
ictx.send("ind1");                          !     "ind1"
ictx.send(3);                               !     3
ictx.sendControl(ictx.CONTROL_CLOSENDX);    !   )
ictx.sendControl(ictx.CONTROL_OPENLST);     !   [
ictx.send(5);                               !     5
ictx.send(1.2);                             !     1.2
ictx.sendControl(ictx.CONTROL_CLOSELST);    !   ]
ictx.sendControl(ictx.CONTROL_OPENNDX);     !   (
ictx.send("ind2");                          !     "ind2"
ictx.send(7);                               !      7
ictx.sendControl(ictx.CONTROL_CLOSENDX);    !   )
ictx.sendControl(ictx.CONTROL_OPENLST);     !   [
ictx.send(4);                               !     4
ictx.send(6.5);                             !     6.5
ictx.sendControl(ictx.CONTROL_CLOSELST);    !   ]
ictx.sendControl(ictx.CONTROL_CLOSELST);    ! ]         

The send and sendControl methods may take an additional last argument indicating whether data is to be processed immediately or only once the queue of tokens is full (default).

With Java, we use exactly the same model file burglar13.mos as with C (see Section Dynamic data for the listing).

Scalars

Besides arrays one might also wish to simply exchange scalars between the calling application and a Mosel model. One way of passing the value of a scalar to a model is to define it as a model parameter and pass the new value as an execution parameter to the model (as shown in Section Parameters). Alternatively, we might read or write scalar values in initializations blocks similarly to what we have seen in the previous section for arrays.

Consider the following Java program: we wish to exchange the values of the three scalars, wmax, numitem, and objval with the Mosel model run by this program. The value of the first scalar should be read in by the Mosel model and the last two receive solution values from the optimization run in the model. Since it is not possible to address scalars directly from the model we have collected them into a class MyData the fields of which are then specified in the execution parameters as the locations of the data.

import com.dashoptimization.*;

public class ugioscalar
{
 public static class MyData            // Scalars for data in/output
 {
  public int wmax;
  public int numitem;
  public double objval;
 }

 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  MyData data=new MyData();

  data.wmax=100;

  mosel = new XPRM();                 // Initialize Mosel

  mosel.compile("burglar11.mos");     // Compile & load the model
  mod = mosel.loadModel("burglar11.bim");

                     // Associate the Java object with a name in Mosel
  mosel.bind("data", data);
                     // File names are passed through execution parameters
  mod.execParams =
   "WMAX='data(wmax)',NUM='data(numitem)',SOLVAL='data(objval)'";

  mod.run();                          // Run the model

  if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
   System.exit(1);                    // Stop if no solution found

                     // Display solution values obtained from the model
  System.out.println("Objective value: " + data.objval);
  System.out.println("Total number of items: " + data.numitem);

  mod.reset();                        // Reset the model
 }
} 

The Mosel model burglar11.mos run by this program is the same as the model burglar12.mos displayed in Section Scalars, but using the I/O driver jraw instead of raw. This model takes as execution parameters the filenames (location in memory) of the three scalars. The integer WTMAX is initialized from the value in the Java application and the two other locations are written to in the initializations to block at the end of the model.

Redirecting the Mosel output

When executing a Mosel model from a Java application it may be desirable to be able to process the output produced by Mosel directly in the application. The following Java program ugcb.java shows a callback-style functionality that redirects the Mosel standard output to an OutputStream object which is used to prefix every line of Mosel output with the string Mosel: before printing it.

To redirect Mosel streams to a Java object (Java streams or ByteBuffer) we need to use the java I/O driver. The same mechanism that is used here for redirecting the output stream of Mosel (indicated by XPRM.F_OUTPUT, with the additional option XPRM.F_LINBUF to enable line buffering) can equally be used to redirect, for instance, the error stream (denoted by the constant XPRM.F_ERROR).

import java.io.*;
import com.dashoptimization.*;

public class ugcb
{
                            // OutputStream class to handle default output
 public static class MyOut extends OutputStream
 {
  public void flush()
  { System.out.flush(); }
  public void write(byte[] b)
  {
   System.out.print("Mosel: ");
   System.out.write(b,0,b.length);
  }
  // These methods are not used by Mosel:
  public void write(byte[] b,int off,int len) {}
  public void write(int b) {}
  public void close() {}
 }

 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  MyOut cbmsg = new MyOut();            // Define output stream as "MyOut"

  mosel = new XPRM();                   // Initialize Mosel

  mosel.bind("mycb", cbmsg);    // Associate Java object with a name in Mosel
                                // Set default output stream to cbmsg
  mosel.setDefaultStream(XPRM.F_OUTPUT|XPRM.F_LINBUF, "java:mycb");

  mosel.compile("burglar2.mos");        // Compile, load & run the model
  mod = mosel.loadModel("burglar2.bim");
  mod.run();
 }
} 

.NET

Example code in this guide will be in C#, however one can access the Mosel .NET interface via other languages that target the .NET Framework, such as VB.NET.

To use the Mosel .NET classes the line using Mosel; must be added at the beginning of the program, and your project should have a dependency on the Xpress package FICO.Xpress.XPRMdn. This package is available in the lib/nuget folder of the Xpress installation. To make the packages visible to NuGet, this folder should be configured as a NuGet local package source.

Compiling and executing a model in C#

With C# Mosel is initialized by obtaining a new instance of class XPRM via the static method XPRM.Init(). To execute a Mosel model in C# we call the three Mosel functions performing the standard compile/load/run sequence as shown in the following example (file ugcomptmp.cs).

using System;
using System.IO;
using Mosel;


namespace ugcomptmp.cs {

  public class ugcomptmp {
    /// <summary>
    /// Main entry point for the application
    /// </summary>
    [STAThread]
    static void Main(string[] args) {
      // Initialize Mosel
      XPRM mosel = XPRM.Init();

      // Compile the Mosel model, save the BIM file in Mosel's temp. dir.
      mosel.Compile("", "burglar2.mos", "tmp:burglar2.bim");

      // Load the BIM file
      XPRMModel model = mosel.LoadModel("tmp:burglar2.bim");

      // Run the model
      model.Run();
    }
  }

}

Termination

If the model execution is embedded in a larger appplication it may be useful to reset the model after its execution to free some resources allocated to it:

  mod.Reset();                        // Reset the model

This will release all intermediate objects created during the execution without deleting the model itself.

It is also possible to explicitly remove the temporary directory/files created by the execution of Mosel:

  mosel.RemoveTmpDir();               // Delete temporary files

Unloading models or Mosel from memory is ensured through standard disposal, finalization + garbage collection functionalities of the .NET runtime. The disposal methods are public and may be called from the user's C# program. Finalization of Mosel only takes effect once all loaded models have been finalized or disposed. Finalizing or diposing Mosel also removes the temporary directory/files created by the execution of Mosel.

  mod.Dispose();                     // Dispose a model
  mod = null;
  mosel.Dispose();                   // Dispose Mosel
  mosel = null;

Parameters

When executing a Mosel model in C#, it is possible to pass new values for its parameters into the model. If, for instance, we want to run model `Prime' from Section Working with sets to obtain all prime numbers up to 500 (instead of the default value 100 set for the parameter LIMIT in the model), we may write the following program:

using System;
using System.IO;
using Mosel;


namespace ugparam.cs {

  public class ugparam {
    /// <summary>
    /// Main entry point for the application
    /// </summary>
    [STAThread]
    static void Main(string[] args) {
      XPRMSet set;
      int LIM=500, first, last;

      // Initialize Mosel
      XPRM mosel = XPRM.Init();

      // Compile and load a model
      XPRMModel model = mosel.CompileAndLoad("prime.mos");

      // Run the model
      model.ExecParams = "LIMIT=" + LIM;
      model.Run();
      Console.WriteLine("`prime' returned: " + model.Result);
    }
  }
}

Using the Mosel .NET interface, it is not only possible to compile and run models, but also to access information on the different modeling objects as is shown in the following sections.

Accessing sets

A complete version of a program (file ugparam.cs) for running the model `Prime' may look as follows (we work with a model prime2 that corresponds to the one printed in Section Working with sets but with all output printing removed because we are doing this in C#, and all entities accessed from C# are declared as public):

using System;
using System.IO;
using Mosel;


namespace ugparam.cs {

  public class ugparam {
    /// <summary>
    /// Main entry point for the application
    /// </summary>
    [STAThread]
    static void Main(string[] args) {
      XPRMSet set;
      int LIM=500, first, last;

      // Initialize Mosel
      XPRM mosel = XPRM.Init();

      // Compile and load a model
      XPRMModel model = mosel.CompileAndLoad("prime.mos");

      // Run the model
      model.ExecParams = "LIMIT=" + LIM;
      model.Run();
      Console.WriteLine("`prime' returned: " + model.Result);

      // Get model object 'SPrime', it must be a set
      set=(XPRMSet)model.FindIdentifier("SPrime");

      // Enumerate the set elements
      if (!set.IsEmpty)
      {
        first = set.FirstIndex;         // Get the number of the first index
        last = set.LastIndex;           // Get the number of the last index
        Console.WriteLine("Prime numbers from 2 to " + LIM);
        for (int i=first;i<=last;i++)   // Print all set elements
          Console.Write(" {0},", set.GetAsInteger(i));
        Console.WriteLine();
      }
    }
  }
}

To print the contents of set SPrime that contains the desired result (prime numbers between 2 and 500), we retrieve the Mosel object of this name using method FindIdentifier. If this set is not empty, then we enumerate the elements of the set (using properties FirstIndex and LastIndex to obtain the index range).

Retrieving solution values

The following program ugsol.cs executes the model `Burglar3' (the same as model `Burglar2' from Chapter Some illustrative examples but with all output printing removed and all model entities declared as public) and prints out its solution.

using System;
using System.IO;
using Mosel;


namespace ugsol.cs {

  public class ugsol {
    /// <summary>
    /// Main entry point for the application
    /// </summary>
    [STAThread]
    static int Main(string[] args) {
      XPRMArray varr, darr;
      XPRMSet set;
      XPRMMPVar x;
      double val;

      // Initialize Mosel
      XPRM mosel = XPRM.Init();

      // Compile and load a model
      XPRMModel model = mosel.CompileAndLoad("burglar3.mos");

      // Run the model
      model.Run();

      if(model.ProblemStatus!=XPRMProblemStatus.PB_OPTIMAL)
        return 1;                    // Stop if no solution found

      Console.WriteLine("Objective value: " + model.ObjectiveValue);
                                     // Print the objective function value

      // Get model object 'take', it must be an array
      varr=(XPRMArray)model.FindIdentifier("take");
      // Get model object 'VALUE', it must be an array
      darr=(XPRMArray)model.FindIdentifier("VALUE");
      // Get model object 'ITEMS', it must be a set
      set=(XPRMSet)model.FindIdentifier("ITEMS");

      // Enumerate all entries of 'varr' (dense array)
      foreach(int[] indices in varr.Indices)
      {
        x = varr.Get(indices).AsMPVar();   // Get a variable from varr
        val = darr.GetAsReal(indices);     // Get the corresponding value
//      Console.WriteLine("take(" + set.GetAsString(indices[0]) + "): " +
        Console.WriteLine("take" + varr.IndexToString(indices) + ": " +
	                x.Solution + "\t (item value: " + val + ")");
      }

      model.Reset();                         // Reset the model
      return 0;
    }
  }
}

The array of variables varr is enumerated using via the array Indices property. This may be applied to arrays of any type and dimension. With these functions we run systematically through all possible combinations of index tuples, hence the hint at dense arrays in the example. In the case of sparse arrays it is preferrable to use a different enumeration property that only enumerate those entries that are defined (see next section).

Sparse arrays

We now again work with the problem `Transport' that has been introduced in Chapter More advanced modeling features. The objective of this problem is to calculate the flows flowpr from a set of plants to a set of sales regions that satisfy all demand and supply constraints and minimize the total cost. Not all plants may deliver goods to all regions. The flow variables flowpr are therefore defined as a sparse array. The following example ugarray.cs prints out all existing entries of the array of variables.

using System;
using System.IO;
using Mosel;


namespace ugarray.cs {

  public class ugarray {
    /// <summary>
    /// Main entry point for the application
    /// </summary>
    [STAThread]
    static void Main(string[] args) {
      XPRMArray varr;
      XPRMSet[] sets;
      XPRMValue[] vindex;
      int dim;

      // Initialize Mosel
      XPRM mosel = XPRM.Init();

      // Compile and load a model
      XPRMModel model = mosel.CompileAndLoad("transport.mos");

      // Run the model
      model.Run();

      // Get model object 'flow', it must be an array
      varr=(XPRMArray)model.FindIdentifier("flow");
      dim = varr.Dim;           // Get the number of dimensions of the array
      sets = varr.IndexSets;    // Get the indexing sets

      // Enumerate over the true entries
      foreach(int[] indices in varr.TEIndices)
      {
        // Get the values for this index
        vindex = varr.DereferenceIndex(indices);
        Console.Write("flow(");
        for(int i=0;i<dim-1;i++)
          Console.Write(vindex[i] + ",");
        Console.Write(vindex[dim-1] + "), ");

        // Alternative printing method:
        // Console.Write("flow" + varr.IndexToString(indices) + ", ");
      }
      Console.WriteLine();

      model.Reset();                         // Reset the model
    }
  }
}

In this example, we first get the number of indices (dimensions) of the array of variables varr (using property Dim). We use this information to enumerate the entries of every index tuple for generating a nicely formatted output. The array sets holds all the index sets of varr and the array indices corresponds to a single index tuple.

The enumeration runs over all the defined index tuples, obtained with property TEIndices.

Exchanging data between an application and a model

In the previous examples we have seen how to retrieve information about the model objects from a Mosel model after its execution. In all cases the input data is defined in the model itself or read in from an external (text) file. However, when embedding a model into an application frequently the input data for the model will be stored (or generated by) the application itself. In such a case the user will certainly wish a more immediate means of communication to the model than having to write the input data to an external text file or database. In the following two subsections we therefore show how to pass data in memory from an application to a Mosel model, and with the same mechanism (namely, using the dotnetraw I/O driver) from the model back to the calling application.

Dense arrays

As a first example we shall look at the case of dense arrays holding the input and solution data. In the underlying Mosel model this corresponds to arrays indexed by range sets that are known in the model before the data are read in. In this example, we shall work with a version of the Burglar model based on the very first version we have seen in Section The burglar problem where all arrays are indexed by the range set ITEMS = 1..8.

The following C# program ugiodense.cs compiles, loads, and runs a Mosel model and then prints out the solution values. The input data (arrays vdata and wdata) and the array solution that is to receive the solution values are passed on to the model through model parameters. Communication of the data between the application and the Mosel model is achieved through the dotnetraw I/O driver. File names for this driver have the form dotnetrawoption[,...],filename, where filename is an object reference. Since we are working with dense, one-dimensional arrays we use the option noindex, indicating that only the data and not the index tuples are to be exchanged.

using System;
using System.IO;
using Mosel;


namespace ugiodense.cs {

  public class ugiodense {

    /// <summary>
    /// Arrays containing initialization data for the model
    /// </summary>
    static double[] vdata = new double[] {15,100,90,60,40,15,10, 1};  // VALUE
    static double[] wdata = new double[] { 2, 20,20,30,40,30,60,10};  // WEIGHT

    /// <summary>
    /// Main entry point for the application
    /// </summary>
    [STAThread]
    static void Main(string[] args) {
      // Initialize Mosel
      XPRM mosel = XPRM.Init();

      // Compile and load the Mosel model
      XPRMModel model = mosel.CompileAndLoad("burglar8d.mos");

      // Associate the .NET objects with names in Mosel
      model.Bind("vdat", vdata);
      model.Bind("wdat", wdata);

      // Create a new array for solution data and bind that to the name 'SOL'
      double[] solution = new double[8];
      mosel.Bind("sol", solution);

      // Pass data location as a parameter to the model
      model.ExecParams = "VDATA='noindex,vdat',WDATA='noindex,wdat',SOL='noindex,sol'";

      // Run the model
      model.Run();

      // Print the solution
      Console.WriteLine("Objective value: {0}", model.ObjectiveValue);
      for (int i=0;i<8;i++)
        Console.Write(" take({0}): {1}", (i+1), solution[i]);
      Console.WriteLine();
    }
  }

}

The model file burglar8d.mos is the same as model burglar6.mos from Section Dense arrays with the only difference that the name of the I/O driver in the initializations blocks now is dotnetraw instead of raw, such as:

 initializations from 'dotnetraw:'
  VALUE as VDATA  WEIGHT as WDATA
 end-initializations 

Sparse arrays

Let us now study the probably more frequent case of data stored in sparse format. In the Mosel model (burglar9d.mos) we use a set of strings instead of a simple range set to index the various arrays and in the C# program (ugiosparse.cs) we need to define slightly more complicated structures to hold the indices and the data entries. To save us writing out the indices twice, we have grouped the two input data arrays into a single class. When passing the data arrays to the Mosel model we now do not use any option, meaning that data is transferred in sparse format. Instead, we now need to indicate which fields of the C# objects are to be selected (in brackets after the object reference).

using System;
using System.IO;
using Mosel;


namespace ugiosparse.cs {

  public class ugiosparse {

    /// <summary>
    /// Arrays containing initialization data for the model
    /// </summary>
    static double[] vdata = new double[] {15,100,90,60,40,15,10, 1};  // VALUE
    static double[] wdata = new double[] { 2, 20,20,30,40,30,60,10};  // WEIGHT

    /// <summary>
    /// Structure to store initial values for the array 'data'
    /// </summary>
    class MyData {
      public string ind;
      public double val;
      public double wght;

      public MyData(string i, double v, double w) {
        this.ind = i;
        this.val = v;
        this.wght = w;
      }
    }

    /// <summary>
    /// Structure to receive solution values
    /// </summary>
    class MySol {
      public string ind;
      public double val;
    }

    /// <summary>
    /// The initial values for the array 'data'
    /// </summary>
    private static MyData[] data = new MyData[] {
                 new MyData("camera",15,2), new MyData("necklace",100,20),
                 new MyData("vase",90,20), new MyData("picture",60,30),
		 new MyData("tv",40,40), new MyData("video",15,30),
                 new MyData("chest",10,60), new MyData("brick",1,10) };


    /// <summary>
    /// Main entry point for the application
    /// </summary>
    [STAThread]
    static void Main(string[] args) {
      // Initialize Mosel
      XPRM mosel = XPRM.Init();

      // Compile and load the Mosel model
      XPRMModel model = mosel.CompileAndLoad("burglar9d.mos");

      // Associate the .NET object with a name in Mosel
      model.Bind("dt", data);

      // Create a new array for solution data and bind that to the name 'SOL'
      MySol[] solution=new MySol[8];
      for(int i=0;i<8;i++) solution[i] = new MySol();
      mosel.Bind("sol", solution);

      // Pass data location as a parameter to the model
      model.ExecParams = "DATA='dt(ind,val,wght)',SOL='sol(ind,val)'";

      // Run the model
      model.Run();

      // Print the solution
      Console.WriteLine("Objective value: {0}", model.ObjectiveValue);
      for (int i=0;i<8;i++)
        Console.Write(" take({0}): {1}",  solution[i].ind, solution[i].val);
      Console.WriteLine();
    }
  }

}

The model burglar9d.mos run by this program is the same as the model burglar7.mos displayed in Section Sparse arrays, but using the I/O driver dotnetraw instead of raw.

Dynamic data

The two examples of in-memory communication of dense and sparse data in the preceding sections have in commun that all data structures in the application, and in particular the structures to receive output data, are of fixed size. We therefore now introduce an alternative communication mechanism working with streams, that enables dynamic sizing of data structures on the application level, a feature that is particularly useful for solution output where effective data sizes are not known a priori. This communication mechanism employs the I/O driver dotnet (see also Section Redirecting the Mosel output). The main part of our C# program (file ugiocb.cs) now looks as follows.

    [STAThread]
    static int Main(string[] args) {
      // Initialize Mosel
      XPRM mosel = XPRM.Init();

      // Compile and load the Mosel model
      XPRMModel model = mosel.CompileAndLoad("burglar13.mos");

      // Set the execution parameters and bind the variables
      model.SetExecParam("DATAFILE","dotnet:cbinitfrom");
      model.SetExecParam("SOLFILE","dotnet:cbinitto");
      model.Bind("cbinitfrom", new XPRMInitializationFrom(initializeFrom));
      model.Bind("cbinitto", new XPRMInitializationTo(initializeTo));

      // Run the model
      model.Run();

      if(model.ProblemStatus!=XPRMProblemStatus.PB_OPTIMAL)
        return 1;                      // Stop if no solution found

     // Display solution values obtained from the model
      Console.WriteLine("Objective value: {0}", model.ObjectiveValue);
      for(int i=0;i<solsize;i++)
        Console.WriteLine(" take({0}): {1}", solution[i].ind, solution[i].val);

      model.Reset();                   // Reset the model
      return 0;
    }

The information passed to the model in the runtime parameters are now instances of delegates for initialization from and to streams as shown below. The functionality for dynamic output retrieval employs the Mosel library functions that we have already seen in Sections Accessing sets and Retrieving solution values for accessing models after their termination. The dynamic data input to a Mosel model uses a new set of dedicated functions that are explained with some more detail after the program extract.

    static double[] vdata=new double[] {15,100,90,60,40,15,10, 1};  // VALUE
    static double[] wdata=new double[] { 2, 20,20,30,40,30,60,10};  // WEIGHT
    static string[] ind=new string[] {"camera", "necklace", "vase", "picture",
                  "tv", "video", "chest", "brick"};            // Index names
    static int datasize=8;

    /// <summary>
    /// Structure to receive solution values
    /// </summary>
    class MySol {
      public string ind;                // index name
      public double val;                // solution value
    }
    static MySol[] solution;
    static int solsize;

    /// <summary>
    /// A function to initialize the Mosel data-structures via callback
    /// </summary>
    public static bool initializeFrom(XPRMInitializeContext ictx,string label,XPRMTyped type)
    {
      try {
        switch (label) {
          case "DATA":
            ictx.Send(XPRMInitializeControl.OpenList);
            for (int i=0;i<datasize;i++) {
              ictx.Send(XPRMInitializeControl.OpenIndices);
                ictx.Send(ind[i]);
              ictx.Send(XPRMInitializeControl.CloseIndices);
              ictx.Send(XPRMInitializeControl.OpenList);
                ictx.Send(vdata[i]);
                ictx.Send(wdata[i]);
              ictx.Send(XPRMInitializeControl.CloseList);
            }
            ictx.Send(XPRMInitializeControl.CloseList);
            return true;
          default:
            Console.WriteLine("Label '{0}' not found", label);
            return false;
        }
      } catch (Exception e) {
        Console.WriteLine("Label '{0}' could not be initialized - {1}", label, e.Message);
        return false;
      }
    }


    /// <summary>
    /// A method to retrieve data from Mosel
    /// </summary>
    public static bool initializeTo(string label,XPRMValue val) {
//      Console.WriteLine(".NET: {0} = {1}", label, val);

      XPRMArray solarr;
      XPRMValue[] vindex;

      switch (label) {
        case "SOL":
          solarr=(XPRMArray)val;
          solsize=solarr.Size;
          solution = new MySol[solsize];
          for(int i=0;i<solsize;i++) solution[i] = new MySol();

          int ct=0;
          // Enumerate solarr as sparse array
          foreach(int [] indices in solarr.TEIndices) {
            vindex = solarr.DereferenceIndex(indices);
            solution[ct].ind = vindex[0].AsString();
            solution[ct].val = solarr.GetAsReal(indices);
            ct++;
          }
          return true;
        default:
           Console.WriteLine("Unknown output data item: '{0}'={1} not found", label, val);
           return false;
      }
    }

The format used to represent data for dynamic data input is the same as the default text format used by initializations blocks. For example, the array definition

 mydata: [ ("ind1" 3) [5 1.2] ("ind2" 7) [4 6.5] ] 

is represented by the following sequence of function calls:

ictx.Send(XPRMInitializeControl.OpenList);      ! [
ictx.Send(XPRMInitializeControl.OpenIndices);   !   (
ictx.Send("ind1");                              !     "ind1"
ictx.Send(3);                                   !     3
ictx.Send(XPRMInitializeControl.CloseIndices);  !   )
ictx.Send(XPRMInitializeControl.OpenList);      !   [
ictx.Send(5);                                   !     5
ictx.Send(1.2);                                 !     1.2
ictx.Send(XPRMInitializeControl.CloseList);     !   ]
ictx.Send(XPRMInitializeControl.OpenIndices);   !   (
ictx.Send("ind2");                              !     "ind2"
ictx.Send(7);                                   !      7
ictx.Send(XPRMInitializeControl.CloseIndices);  !   )
ictx.Send(XPRMInitializeControl.OpenList);      !   [
ictx.Send(4);                                   !     4
ictx.Send(6.5);                                 !     6.5
ictx.Send(XPRMInitializeControl.CloseList);     !   ]
ictx.Send(XPRMInitializeControl.CloseList);     ! ]

The send and sendControl methods may take an additional last argument indicating whether data is to be processed immediately or only once the queue of tokens is full (default).

With C#, we use exactly the same model file burglar13.mos as with C (see Section Dynamic data for the listing).

Scalars

Besides arrays one might also wish to simply exchange scalars between the calling application and a Mosel model. One way of passing the value of a scalar to a model is to define it as a model parameter and pass the new value as an execution parameter to the model (as shown in Section Parameters). Alternatively, we might read or write scalar values in initializations blocks similarly to what we have seen in the previous section for arrays.

Consider the following C# program: we wish to exchange the values of the three scalars, wmax, numitem, and objval with the Mosel model run by this program. The value of the first scalar should be read in by the Mosel model and the last two receive solution values from the optimization run in the model. Since it is not possible to address scalars directly from the model we have collected them into a class MyData the fields of which are then specified in the execution parameters as the locations of the data.

using System;
using System.IO;
using Mosel;


namespace ugioscalar.cs {

  public class ugioscalar {
    /// <summary>
    /// Structure to receive solution values
    /// </summary>
    class MyData {
      public int wmax;
      public int numitem;
      public double objval;
    }

    /// <summary>
    /// Main entry point for the application
    /// </summary>
    [STAThread]
    static int Main(string[] args) {
      MyData data=new MyData();
      data.wmax=100;

      // Initialize Mosel
      XPRM mosel = XPRM.Init();

      // Compile and load a model
      XPRMModel model = mosel.CompileAndLoad("burglar11.mos");

      // Associate the .NET object with a name in Mosel
      model.Bind("data", data);

      // Run the model, passing data location as parameters
      model.ExecParams =
        "WMAX='data(wmax)',NUM='data(numitem)',SOLVAL='data(objval)'," +
	"IODRV='dotnetraw:'";
      model.Run();

      if(model.ProblemStatus!=XPRMProblemStatus.PB_OPTIMAL)
        return 1;                    // Stop if no solution found

                        // Display solution values obtained from the model
      Console.WriteLine("Objective value: " + data.objval);
      Console.WriteLine("Total number of items: " + data.numitem);

      model.Reset();                             // Reset the model
      return 0;
    }
  }
}

The Mosel model burglar11.mos run by this program is the same as the model burglar12.mos displayed in Section Scalars, but using the I/O driver dotnetraw instead of raw (which we set through the IODRV parameter). This model takes as execution parameters the filenames (location in memory) of the three scalars. The integer WTMAX is initialized from the value in the .NET application and the two other locations are written to in the initializations to block at the end of the model.

Redirecting the Mosel output

When executing a Mosel model from a .NET application it may be desirable to be able to process the output produced by Mosel directly in the application. The following C# program ugcb.cs shows a callback-style functionality that redirects the Mosel standard output to a TextWriter object which is used to prefix every line of Mosel output with the string Mosel: before printing it.

To redirect Mosel streams to a .NET object (.NET Stream (including MemoryStream for in-memory buffers), TextReader, or TextWriter) we need to use the dotnet I/O driver. The same mechanism that is used here for redirecting the output stream of Mosel (indicated by XPRMStreamType.F_OUTPUT_LINEBUF which also enables line buffering) can equally be used to redirect, for instance, the error stream (denoted by the constant XPRMStreamType.F_ERROR).

Note that text read from a TextReader will be encoded into bytes via the UTF-8 character encoding before being passed to Mosel; conversely, the text to be written to a TextWriter will have been produced by decoding the Mosel output which is assumed to be in UTF-8. If this is not the desired result, consider using a Stream instead. If you wish data exchange to be performed in a different encoding (such as the platform's default encoding), this can be done Mosel by use of the enc I/O driver (see How can I convert the character encoding of a text file?); only Streams, not TextReaders or TextWriters, are suitable for this.

using System;
using System.IO;
using System.Text;
using Mosel;


namespace ugcb.cs {

  public class ugcb {

    /// <summary>
    /// Main entry point for the application
    /// </summary>
    [STAThread]
    static void Main(string[] args) {
      // Initialize Mosel
      XPRM mosel = XPRM.Init();

      // Associate .NET object with a name in Mosel
      mosel.Bind("mycb", new MyOut());

      // Redirect error stream to stdout
      mosel.SetDefaultStream(XPRMStreamType.F_ERROR, Console.Out);

      // Compile and load the Mosel model
      XPRMModel model = mosel.CompileAndLoad("burglar2.mos");

      // Redirect the model's output to a custom TextWriter
      MyOut modelOut = new MyOut();
      model.SetDefaultStream(XPRMStreamType.F_OUTPUT_LINEBUF, modelOut);

      // Alternative:
      // Redirect the model's output to our printing function 'cbmsg'
      model.SetDefaultStream(XPRMStreamType.F_OUTPUT_LINEBUF, "dotnet:mycb");

      // Run the model
      model.Run();

    }
  }

  public class MyOut: TextWriter
  {
   private bool atStartOfLine = true;
   public override void Write(char b)
   {
     if (atStartOfLine) {
      Console.Write("Mosel: ");
      atStartOfLine=false;
     }
     if (b=='\n') {
      Console.WriteLine();
      atStartOfLine=true;
     }
     else if (b=='\r') {
      // ignore
     }
     else {
      Console.Write(b);
     }
   }
   public override Encoding Encoding {
    get {
     return Encoding.UTF8;
    }
   }
  }
}

VBA

VBA typically serves for embedding a Mosel model into an Excel spreadsheet. In this section we shall only show the parts relevant to the Mosel functions, assuming that the execution of a model is trigged by the action of clicking on some object such as the buttons shown in Figure Excel spreadsheet embedding VBA macros.

MoselUG/moselugvb.png

Figure 17.1: Excel spreadsheet embedding VBA macros

Compiling and executing a model in VBA

As with the other programming languages, to execute a Mosel model in VBA we need to perform the standard compile/load/run sequence as shown in the following example (contained in the file ugvb.bas). We use a slightly modified version burglar5.mos of the burglar problem where we have redirected the output printing to the file burglar_out.txt.

Private Sub burglar_Click()
  Dim model
  Dim ret As Long
  Dim result As Long
  Dim outfile As String, moselfile As String

'Initialize Mosel
  ret = XPRMinit
  If ret Then
    MsgBox "Initialization error (" & ret & ")"
    Exit Sub
  End If

'Compile burglar5.mos
  XPRMsetdefworkdir GetFullPath()
  moselfile = GetFullPath() & "\" & "burglar5"
  outfile = GetFullPath() & "\" & "burglar_out.txt"
  ret = XPRMcompmod(vbNullString, moselfile & ".mos", vbNullString, _
        "Burglar problem")
  If ret <> 0 Then
    MsgBox "Compile error (" & ret & ")"
    Exit Sub
  End If

'Load burglar5.bim
  model = XPRMloadmod(moselfile & ".bim", vbNullString)
  If model = 0 Then
    MsgBox "Error loading model"
    Exit Sub
  End If

'Run the model
  ret = XPRMrunmod(model, result, "OUTFILE=""" & Replace(outfile, "\", _
        "\\") & """")
  If ret <> 0 Then
    MsgBox "Execution error (" & ret & ")"
    GoTo done
  Else
    ShowFile outfile
  End If
  MsgBox vbNewLine & "model Burglar returned: " & result

done:
  XPRMfree
End Sub

'Auxiliary routines
Private Sub ShowFile(fn As String)
  Dim vs As String
  vs = CreateObject("Scripting.FileSystemObject").OpenTextFile(fn).ReadAll
  MsgBox vs
End Sub

Private Function GetFullPath() As String
  Dim path As String
  path = ThisWorkbook.path
  If Right(path, 1) = "\" Then path = Left(path, Len(path) - 1)
  GetFullPath = path
End Function

This implementation redirects the output into a log file vbout.txt the contents of which is displayed after a successful model run.

Parameters

When executing a Mosel model in VBA, it is possible to pass new values for its parameters into the model. The following program (also contained in the file ugvb.frm) extract shows how we may run model `Prime' from Section Working with sets to obtain all prime numbers up to 500 (instead of the default value 100 set for the parameter LIMIT in the model). We use a slightly modified version prime4.mos of the model where we have redirected the output printing to a file denoted by the parameter OUTFILE.

Private Sub prime_Click()
  Dim model
  Dim ret As Long
  Dim result As Long
  Dim outfile As String, moselfile As String

'Initialize Mosel
  ret = XPRMinit
  If ret Then
    MsgBox "Initialization error (" & ret & ")"
    Exit Sub
  End If

'Compile prime4.mos
  XPRMsetdefworkdir GetFullPath()
  moselfile = GetFullPath() & "\" & "prime4"
  outfile = GetFullPath() & "\" & "vbout.txt"
  ret = XPRMcompmod(vbNullString, moselfile & ".mos", vbNullString, _
        "Prime numbers")
  If ret <> 0 Then
    MsgBox "Compile error (" & ret & ")"
    Exit Sub
  End If

'Load prime4.bim
  model = XPRMloadmod("prime4.bim", vbNullString)
  If model = 0 Then
    MsgBox "Error loading model"
    Exit Sub
  End If

'Run model with new parameter settings
  ret = XPRMrunmod(model, result, "LIMIT=500,OUTFILE=""" & Replace(outfile, _
        "\", "\\") & """")

  If ret <> 0 Then
    MsgBox "Execution error (" & ret & ")"
    GoTo done
  Else
    ShowFile outfile
  End If
  MsgBox vbNewLine & "model Prime returned: " & result

done:
  XPRMfree
End Sub

Redirecting the Mosel output

In the previous example we have hardcorded the redirection of the output directly in the model. With Mosel's VBA interface the user may also redirect all output produced by Mosel to files directly from the host application, that is, redirect the output stream.

To redirect all output of a model to the file myout.txt add the following function call before the execution of the Mosel model:

' Redirect all output to the file "myout.txt"
 XPRMsetdefstream 0, XPRM_F_OUTPUT, "myout.txt"

Similarly, any possible error messages produced by Mosel can be recovered by replacing in the line above XPRM_F_OUTPUT by XPRM_F_ERROR. This will redirect the error stream to the file myout.txt.

The following VBA program extract (file ugcb.bas) shows how to use a callback in VBA to receive all output from a Mosel model (standard output and errors). The output will be displayed in the spreadsheet from where the model run was started.

Private ROWNUM As Long
Public Sub example()
  Dim ret As Long
  Dim result As Long
  Dim module

  ClearColumn

' Initialize Mosel. Must be called first
  ret = XPRMinit
  If ret <> 0 Then
    PrintLn ("Failed to initialize Mosel")
    Exit Sub
  End If

' Redirect the output and error streams to the callback
  ret = XPRMsetdefstream(0, XPRM_F_OUTPUT, XPRM_IO_CB(AddressOf OutputCB))
  ret = XPRMsetdefstream(0, XPRM_F_ERROR, XPRM_IO_CB(AddressOf OutputCB))

  PrintLn "Starting model..."

' Run the model
  ret = XPRMexecmod("", GetFullPath() & "\" & "burglar10.mos", _
                    "FULLPATH='" & GetFullPath() & "'", result, module)
  If ret <> 0 Then
    PrintLn ("Failed to execute model")
    GoTo done
  Else
    PrintLn "Finished model"
  End If

  done:
    XPRMfree
End Sub

#If VBA7 Then
Private Sub OutputCB(ByVal model As LongPtr, ByVal ref As LongPtr, _
                    ByVal msg As String, ByVal size As Long)
  ' Output to the spreadsheet
  Call PrintLn(msg)
End Sub
#Else
Private Sub OutputCB(ByVal model As Long, ByVal ref As Long, _
                    ByVal msg As String, ByVal size As Long)
  ' Output to the spreadsheet
  Call PrintLn(msg)
End Sub
#End If

Public Sub PrintLn(ByVal msg As String)
  ' Strip any trailing newlines first
  If Right(msg, Len(vbLf)) = vbLf Then msg = Left(msg, Len(msg) - Len(vbLf))
  If Right(msg, Len(vbCr)) = vbCr Then msg = Left(msg, Len(msg) - Len(vbCr))
  Worksheets("Run Model").Cells(ROWNUM, 2) = Trim(msg)
  ROWNUM = ROWNUM + 1
End Sub

Sub ClearColumn()
  Worksheets("Run Model").Columns(2).ClearContents
  ROWNUM = 1
End Sub

Function GetFullPath() As String
  Dim path As String
  path = ThisWorkbook.path
  If Right(path, 1) = "\" Then path = Left(path, Len(path) - 1)
  GetFullPath = path
End Function

© 2001-2025 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.