Applying user-defined volume-delay functions

The file storing a user-defined VDF must include a specific interface in order to be recognized by Visum. Most importantly, each file *.dll must export the functions listed below, so that Visum can call the *.dll file correctly during assignment.

Preparing a *.dll file for a user-defined VDF

Visum comes with a header file UserDefinedVDF.h which can be used directly for implementing the required functions in C++. The header file and a complete sample project for Microsoft Visual Studio can be found in ...programs\PTV Vision\Data\UserDefVDF. Generally any programming language can be used, as long as it can produce a Windows *.dll file which exposes an interface equivalent to the C++ function declarations.

The functions must have exactly the same signature as in UserDefinedVDF.h. You may not change the return value or remove or swap parameters.

Note: The volume-delay function needs to be continuous and monotonously rising with respect to volumes. This applies to all possible values of the parameters.

Mandatory functions in the *.dll

Declaration

char Init ();

Description

This function is called by Visum once immediately after start-up and before the first use of any of the other functions.

Note

Use this function to reset your data structures or perform any other preparatory functions, if necessary.

Parameters

None

Return value

true – reset successful

false – reset failed; the *.dll file should not be called.

Declaration

void Destroy ();

Description

This function is called by Visum once immediately before shut-down and after the last use of any of the other functions.

Note

Use this function to perform any clean-up, if necessary. For example, free up dynamically allocated memory.

Parameters

None

Return value

None

Declaration

char IsThreadSafe();

Description

This function is called by Visum once immediately after start-up and before the first call of any of the other functions. The returned value indicates whether the Calc... functions listed below have been implemented for simultaneous use. If so, the function can be called once more before the previous call has been finished.

Note

This parallelism can be used in case of multithread implementation of assignment procedures in Visum.

Parameters

None

Return value

true – reentrant

false – not re-entrant

Declaration

char DependsOnTSys ();

Description

This function is called by Visum once immediately after start-up and before the first call of any of the other functions. Use this function to indicate whether your function makes use of separate volumes per transport system or whether volumes are required per transport system. If this is not the case, Visum can avoid extracting and passing these volumes to the Calc… functions and the function does not have to be called for every transport system, which saves calculation time.

If the function is used for connectors, the value 1 must be returned (see return value).

For (main) turns and (main) nodes, the value 0 must be returned (see return value).

Parameters

None

Return value

2 - requires volumes per transport system, but returns the same result for all transport systems

1 - requires volumes per transport system and potentially returns a different result for different transport systems

0 - uses only the total volume in PCU and returns the same result for all transport systems

Declaration

const wchar_t * GetName (const char * langid);

Description

Returns a readable name for the functional form which is used as the list entry in the VDF selection list.

Parameters

langid is a language code which can be used to optionally translate the name to other languages.

Currently, the following options are provided.

‘ENG’ – English

‘DEU’ – German

‘FRA’ – French

‘ITA’ – Italian

‘POL’ – Polish

‘ESP’ – Spanish

‘CHI’ – Chinese

‘JAP’ - Japanese

Notes

If not required, you may ignore this functionality. If you use the language code, always include the case of an unknown code, because languages may be added in the future without explicit notice.

Return value

The readable name of the functional form as a 16-bit UTF-8 character string. The value must be returned as UTF-8 to accommodate special characters in some languages, notably the Asiatic ones. Visum expects a pointer to a string stored in the *.dll file.

Note

Be sure not to allocate the string on the stack, so that the address is still valid after the function call.

Declaration

const char * GetID ();

Description

Should return a string to be used as a unique ID for the functional form.

This ID is stored internally in the version file to record the user allocation to connectors and types of links, nodes or turns, and it serves as the ID of the functional form in the *.xml format for procedure parameters.

Parameters

None

Return value

The ID string as an ASCII string. The string must contain only the characters 0..9, a..z, A..Z. Visum expects a pointer to a string stored in the *.dll file.

Note

Be sure not to allocate the string on the stack, so that the address is still valid after the function call.

Declaration

int GetInterfaceVersion();

Description

The interface definition is versioned for files of the *.dll type, so that function declarations can be changed or extended in the future. Return the version number of the header file for which you program your functions. Visum compares the returned number to the version numbers it knows about, calls the dll functions accordingly or returns an error message if the interface version is not supported.

Parameters

None

Return value

the version number

Declaration

void SetTsysInfo (int numtsys, const wchar_t * tsysids[])

Description

Visum calls this function each time the set of private transport systems changes. As parameters, it passes the number of transport systems and an array of the transport system codes. For efficiency reasons, Visum passes the transport system as a number to the Calc functions, i.e. as the 0-based index into the tsysids array.

Note

To avoid string comparisons in these frequently called functions, you should - in SetTsysInfo - evaluate the numerical TSys index of transport systems that require special treatment in volume-delay functions and store this data.

Parameters

numtsys – the number of transport systems (= length of array tsysids)

tsysids – array of 16-bit UTF-8 character strings, each of which is the value of the attribute Code for one transport system used in the assignment.

Return value

No

Declaration

double Calc (int tsysind, bool tsysisopen, int typ, int numlanes, double length, double cap, double v0, double t0, double gradient, double pcuvol, double vehvolsys[], int uval1, int uval2, int uval3, int uvaltsys, double para_a, double para_b, double para_c, double para_d, double para_f, double para_a2, double para_b2, double para_d2, double para_f2, double satcrit)

Description

The implementation of the volume-delay function itself. Visum calls this function in order to calculate the current travel time tCur by link / turn / connector / node and transport system.

Note

Care should be taken to code the function in a computationally efficient form, because it will be called very frequently.

Parameters

For the network objects, various parameters are passed.

Note

For details, please refer to the table below.

Return value

tCur in [s]

Depending on the network object type, Visum passes the following parameters to the Calc function.

 

Link

Connector

Turn

Node

int tsysind

Zero-based index of the transport system (refers to the array tsysids passed in SetTsysInfo) for which tCur should be computed

bool tsysisopen

Is the network object open for the transport system

int typ

0..99

0..9

0..9

0..99

int numlanes

Number of lanes

1 (arbitrary)

1 (arbitrary)

1 (arbitrary)

double length

Length

[short length]

Length

[short length]

0 (arbitrary)

0 (arbitrary)

double cap

CapPrT [pcu]

10E10 or CapPrT[pcu] if connectors have specified shares for the total origin/destination demand.

CapPrT [pcu]

CapPrT [pcu]

double v0

v0 [m/s]

Length/t0 (or 0 if t0=0)

0

0

double t0

Length/v0 [s] or 10E10 if v0=0

t0 [s]

double gradient

Slope

0 (arbitrary)

0 (arbitrary)

0 (arbitrary)

double pcuvol

VolPCU[pcu] as a linear combination of all TSys volumes in [veh], multiplied by the TSys-specific value of the attribute PCU.

Note

Recommended for most of the applications.

double vehvolsys[]

Alternatively, for non-standard PCU calculations, an array of all TSys volumes in [veh]. The order of entries complies with the order of entries in the tsysids array which is passed to SetTsysInfo.

double uval1/2/3

Values of the attributes AddValue1, AddValue2 and AddValue3

double uvaltsys

Value of the TSys-specific attribute AddValue-TSys

0

double para_a..f2

in the Volume-delay function parameters window

double satcrit

in the Volume-delay function parameters window (Selecting a VDF and setting the parameters)

Optional functions in the *.dll

Declaration

double CalcDerivative (int tsysind, bool tsysisopen, int typ, int numlanes, double length, double cap, double v0, double t0, double gradient, double pcuvol, double vehvolsys[], int uval1, int uval2, int uval3, int uvaltsys, double para_a, double para_b, double para_c, double para_d, double para_f, double para_a2, double para_b2, double para_d2, double para_f2, double satcrit)

Description

The program calls this function in order to calculate the derivative of the current travel time tCur with respect to saturation by link / turn / connector / node and TSys. This function is only called within the bicriterial toll assignment methods TRIBUT - Equilibrium assignment and TRIBUT - Equilibrium_Lohse.

Notes

Care should be taken to code the function in a computationally efficient form, because it will be called very frequently.

If the function has not been implemented in the *.dll, Visum will compute the derivative numerically. However, this requires more time than supplying the CalcDerivative() in the *.dll.

Parameters

Same as for Calc()

Return value

Derivative of tCur in [s]

Declaration

double CalcIntegral (int tsysind, bool tsysisopen, int typ, int numlanes, double length, double cap, double v0, double t0, double gradient, double pcuvol, double vehvolsys[], int uval1, int uval2, int uval3, int uvaltsys, double para_a, double para_b, double para_c, double para_d, double para_f, double para_a2, double para_b2, double para_d2, double para_f2, double satcrit)

Description

The program calls this function in order to calculate the integral of the current travel time tCur with respect to saturation by link / turn / connector / node and TSys.

Note

This function is no longer required. Thus it does not have to be implemented.

If the function is not implemented in the *.dll, Visum will compute the integral numerically. However, this will require more time than supplying CalcIntegral() in the *.dll.

Parameters

Same as for Calc()

Return value

Integral from 0 to saturation of the volume-delay function in [s].

Alternative interface with specification of static attributes in the *.dll

The Calc function is called with a number of network object-dependent attributes such as type, number of lanes, etc., but this does not allow access to other attributes such as user-defined attributes. For this case, three additional, optional functions can be implemented in the *.dll, which allow access to any numerical attributes of the network object. These attributes must be static within an assignment, i.e., they must not depend on the volume. Access to volume-dependent attributes leads to incorrect values. If the following three functions are implemented, the alternative interface is used. The functions Calc(), CalcDerivative() and CalcIntegral() should not be implemented in this case.

Declaration

int GetNumStaticAttributes()

Description

This function returns the number of static attributes for which values are passed to CalculateWithStaticAttributes().

Parameters

None

Return value

Number of static attributes

 

Declaration

const wchar_t * GetStaticAttributeID(int attributesIndexZeroBased)

Description

Should return a string that contains a valid attributeID

Parameters

The index of the attribute (0 to GetNumStaticAttributes()-1)

Return value

The attributeID string as an ASCII string. Visum expects a pointer to a string stored in the *.dll file.

Note

Be sure not to allocate the string on the stack, so that the address is still valid after the function call.

 

Declaration

double CalculateWithStaticAttributes(int tsysind, char tsysisopen, double cap, double t0, pcuvol, double basevol, double vehvolsys[], double staticAttributeValues[],

double para_a, double para_b, double para_c, double para_d, double para_f, double para_a2, double para_b2, double para_d2, double para_f2, double satcrit)

Description

The implementation of the volume-delay function itself. Visum calls this function in order to calculate the current travel time tCur by link / turn / connector / node and transport system.

Note

Care should be taken to code the function in a computationally efficient form, because it will be called very frequently.

Parameters

For the network objects, various parameters are passed. The parameters correspond to those of the function Calc(), but type, numlanes, length, v0, uval1, uval2, uval3, uvaltsys are missing.

Instead in staticAttributeValues an array of the length GetNumStaticAttributes() is passed with the values specified by GetStaticAttributeID(). If an attribute does not exist or the attribute is not numeric, 0 is passed.

Return value

tCur in [s]

Example for a user-defined VDF

Assume that two transport systems CAR and HGV are used in the assignment.

  • For CAR the volume-delay function is a linear form with one breakpoint at satcrit.

  • For HGV two breakpoints d and e are used.

The following applies.

The derivations are:

Source code of the *.dll file
#include “UserDefinedVDF.h”
#include “tchar.h”
int indHGV;  // index of the HGV tsys
wchar_t VDFName[] = _T(“ManualExample”); // UTF-8 !!
char  VDFID[] = “MANEX”;
int INTERFACE_VERSION = 1;
boolchar Init()
{
	indHGV = -1;
	return true;
}
void Destroy()
{
}
char DependsOnTSys()
{
  return 1;
}
const wchar_t* GetName(const char *langid)
{
	return VDFName;
}
const char* GetID(const char *langid)
{
	return VDFID;
}
int GetInterfaceVersion()
{
	return INTERFACE_VERSION;
}
void SetTsysInfo (int numtsys, const wchar_t * tsysids[])  
{
	for (int i = 0; i < numtsys; i++)
	{
		if (_tcscmp(tsysids[i], _T(“HGV”)) == 0) indHGV = i;
	}
}
double Calc (int tsysind, bool tsysisopen, int typ, int numlanes, double length, double cap, double v0, double t0, double gradient, double pcuvol, double vehvolsys[], int uval1, int uval2, int uval3, int uvaltsys, double para_a, double para_b, double para_c, double para_d, double para_f, double para_a2, double para_b2, double para_d2, double para_f2, double satcrit)
{ 
	double sat = pcuvol / cap;
	if (tsysind != indHGV) {
		if (sat < satcrit)
			return t0 * (1 + para_a * sat);
		else
			return t0 * (1 + para_a * satcrit + para_b * (sat-satcrit));
}
	else {
		if (sat < d)
			return t0 * (1 + para_a2 * sat);
		else {
		   if (d <= sat && sat < e)
			  return t0 * (1 + para_a2 * d + para_b2 * (sat-d));
		else
		    return t0 * (1 + para_a2 * d + para_b2*(e-d) + para_c*(sat-e));
		}
	}
}
double CalcDerivative (int tsysind, bool tsysisopen, int typ, int numlanes, double length, double cap, double v0, double t0, double gradient, double pcuvol, double vehvolsys[], int uval1, int uval2, int uval3, int uvaltsys, double para_a, double para_b, double para_c, double para_d, double para_f, double para_a2, double para_b2, double para_d2, double para_f2, double satcrit)
{ 
	double sat = pcuvol / cap;
	if (tsysind != indHGV) {
		if (sat < satcrit)
			return para_a;
		else
			return para_b;
	}
	else {
		if (sat < d)
			return para_a2;
		else {
			if (d <= sat && sat < e)
				return para_b2;
			else
				return para_c;
		}
	}
}
Example of a user-defined VD function with static attributes

The implementation of a BPR function to show access to static attributes.

Source code of the *.dll file
#include "UserDefinedVDF.h"
#include "tchar.h"
#include <math.h>
#include <float.h>
 
wchar_t VDFName[] = _T("ExampleStaticAttributes");
char  VDFID[] = "PTV_EXAMPLESTATICATTRIBUTES";
int INTERFACE_VERSION = 1;
 
char Init()
{
return true;
}
 
enum MyStaticIDs
{
V0PrT,
Length,
CapPrT,
TypeNo,
LastId
};
 
static const int MyMaxIDLength = 100;
static wchar_t staticAttributeIDs[LastId][MyMaxIDLength] =
{
L"V0PRT",
L"LENGTH",
L"CAPPRT",
L"TYPENO"
};
 
int GetNumStaticAttributes()
{
return LastId;
}
 
const wchar_t * GetStaticAttributeID(int attributesIndexZeroBased)
{
return staticAttributeIDs[attributesIndexZeroBased];
}
 
void Destroy()
{
}
 
char IsThreadSafe()
{
return true;
}
 
 
char DependsOnTSys()
{
return 0;
}
 
const wchar_t* GetName(const char *langid)
{
return VDFName;
}
 
const char* GetID()
{
return VDFID;
}
 
int GetInterfaceVersion()
{
return INTERFACE_VERSION;
}
 
void SetTsysInfo (int numtsys, const wchar_t * tsysids[])
{
}
 
double CalculateWithStaticAttributes(int tsysind, char tsysisopen, double cap, double t0,
double pcuvol, double basevol, double vehvolsys[],
double staticAttributeValues[],
double para_a, double para_b, double para_c, double para_d, double para_f,
double para_a2, double para_b2, double para_d2, double para_f2, double satcrit)
{
double const v0PrT = staticAttributeValues[V0PrT];
double const length = staticAttributeValues[Length];
double const mycap = staticAttributeValues[CapPrT];
double const typeNo = staticAttributeValues[TypeNo];
 
if (cap <= 0 || para_c <= 0 || v0PrT <= 0 || typeNo < 0) {
return DBL_MAX;
}
double sat = pcuvol / (cap * para_c);
 
double myt0 = 3.6 * length / v0PrT;
return myt0 * (1 + para_a * (pow(sat, para_b)));
}
 
Importing user-defined VDFs

1.  Compile and link the *.dll with the development environment of your choice.

2.  Name the library file according to the following pattern: VisumVDF***.dll.

Notes: Here, *** can be replaced by any string that matches the Windows file name conventions.

Note that the IDs returned by GetID() must be unique. If two *.dll files use the same ID, only the first one will be loaded into Visum. All others will be ignored.

3.  Store the file in the folder ...\User\<User>\AppData\Roaming\PTV Vision\PTV Visum 2022\UserVDF-DLLs.

Notes: During the installation process, this folder is created by default. It is evaluated, whenever a program session starts.

Alternatively, you may select a different folder as standard folder for your *.dll files. In this case, the different path must be saved to the standard path file std.pfd (Editing the storage location of files). The modification takes effect once you restart the program.

If you change the project directory for user-defined VD functions, the contained *.dll files will be loaded additionally and the existing ones will remain.

During the program start, Visum detects the *.dll files and loads the contents. Only those files are opened, whose Windows edition (32 or 64 Bit) complies with the current Visum edition. Then, the user-defined VDFs are listed jointly with the predefined VDFs in the Volume-delay function parameters window.

Notes: After start-up Visum no longer scans for new *.dll files. If you add another user-defined VDF, you have to start Visum again.

If you store a *.bmp file and the *.dll file with identical file names in the same folder, the graph of the selected VDF is displayed in the Volume-delay function parameters window.

Creating user-defined VD functions with VisualStudio C++

In the installation directory of Visum in the subfolder Data\UserDefVDF you will find a sample environment for Visual Studio 2019.

Note: Before use, you should copy the contents of Data\UserDefVDF to a separate folder outside of C:\Program Files, because this folder Visum can be overwritten in case of an update.

Both the debug and release configuration create *.dll files that are compatible with Visum. However, the release variant is faster at runtime.

  • ►  Start Visual Studio 2019 and open the solution file UserDefVDF.sln..

To create your own *.dll , you can either modify one of the existing projects or add a new project to the solution.