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 2021\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.