GWBplugin – Creating a wrapper

The GWB plug-in feature is implemented as a Dynamic-Link Library (DLL). For ease of use we provide wrapper classes for a variety of languages that handle loading the DLL, binding to the needed functions, and conversion to C data types. The current list of languages we have wrappers for includes C++, Fortran, and Python. This page answers common queries about how to create a wrapper for the plug-in feature in other languages.


Language requirements

Any language that can load DLLs, call C functions from them, and handle some basic C data types should be able to use the GWB plug-in feature. The C data types that need to be handled are void*, char*, int*, double*, and int. If the language you want to use is similar to one that we provide a wrapper for, a good place to start is looking at how we implemented that particular wrapper. However if none of the languages are very similar you will want to look at whichever one is most familiar to you.

Creating the wrapper

You will usually want to create a wrapper class, interface, or whatever makes sense for your target language. This can be accomplished in three main steps.

Load the GWBplugin DLL.
Generally this will be done during run-time with a call to LoadLibrary or whatever the equivalent is in your language. Some languages (mostly compiled and linked ones) can instead link to the export library GWBplugin.lib.
Tell your program about the functions you will be calling from the DLL.
Usually done by giving prototypes in some way or possibly directly including GWBplugin.h. Sometimes this is unneeded and you can more or less call the functions directly. The needed DLL functions and their prototypes are listed in the next section.
Encapsulate.
Create functions in your wrapper that call the corresponding DLL function and handle data type conversions. The wrapper, if possible, should also have a void* member variable that can be passed by address to the DLL functions. This void* member variable keeps track of a particular GWBplugin instance.

Function details

  1. GWBplugin.dll function prototypes.
  2. Initializing the GWB application you wish to use.
  3. Configure and execute your calculations.
  4. Retrieving the results.
  5. Retrieving the results with string lengths.
  6. Cleaning up.

GWBplugin.dll function prototypes.

This is the list of the definitions and functions exported from GWBplugin.dll that your wrapper will need to use. Please note that function parameters labeled as [optional] are in fact required when you call the C function. But it is suggested you make these arguments optional for your own wrapper if possible and use the provided suggested defaults.

// GWBplugin.h

#define ANULL  -999999.0       // marker for an undefined value 


extern "C" __declspec(dllexport) 
  int  c_initialize (void* plugin, const char* app_name, const char* file_name, const char* cmds);

extern "C" __declspec(dllexport) 
  int  c_exec_cmd   (void* plugin, char* uline);

extern "C" __declspec(dllexport) 
  int  c_results    (void* plugin, void* data, const char* value, const char* units, int ix ,
  					 int jy);

extern "C" __declspec(dllexport) 
  int  c_results_c  (void* plugin, void* data, const char* value, const char* units, int ix , 
                     int jy, int* slen);

extern "C" __declspec(dllexport) 
  void c_destroy    (void* plugin);

c_initialize – Initializing the GWB application you wish to use.

Within your code you must first create a void* equivalent variable or really anything that can hold a pointer data type ... i.e. is 32-bits (for a 32-bit application) or 64-bits (for 64-bit application) long. Usually this will be a member variable of your class if possible. Next, use the "c_initialize" function to start the GWB application of interest by passing the address of the void* variable, the application name, a optional file name for the GWB application to write output to, and any command-line type arguments.

Syntax

int c_initialize(
	void* plugin,
	const char* app_name,
	const char* file_name,
	const char* cmds
);

Parameters

plugin
A dereferencable pointer that points to a pointer which can be assigned a value. Keeps track of a particular plugged in GWB application.
app_name
A string containing the GWB application name you wish to use. Valid options are rxn, spece8, react, x1t, and x2t.
file_name [optional] (default: NULL or empty string)
A string containing the name of the file you want the GWB application to write its output to. Can be NULL or an empty string if you do not want the output to be written to a file.
cmds [optional] (default: NULL or empty string)
A string containing command-line options you could normally pass to the application when running it from the command-line. Can be NULL or an empty string.

Command-line options:
-cd
Change the working directory to the directory containing the input script specified with the -i option.
-nocd
Do not change the working directory.
-i <input_script>
Read initial input commands from the specified file.
-gtd <gtdata_dir>
Set directory to search for thermodynamic datasets.
-cond <cond_data>
Set the dataset for calculating electrical conductivity.
-d <thermo_data>
Set the thermodynamic dataset.
-s <surf_data>
Set a dataset of surface sorption reactions.
-iso <isotope_data>
Set a dataset of isotope fractionation factors.

Return value

Non-zero on success and zero on failure.

Remarks

  • For this function to succeed you must have your GWB installation folder added to the PATH environment variable so all the required DLLs can be found.
  • initialize must be done before trying to use any of the other functions.
  • The plugin parameter is a "pointer to a pointer".

Examples

void* myPlugin = NULL;
...
// plug-in SpecE8 with no output written and no options
int success = c_initialize(&myPlugin,"spece8");
...
// plug-in React with output written to output.txt and no options
int success = c_initialize(&myPlugin,"react","output.txt");
...
// plug-in X1t with no output written, no working directory change,
//  and read input from pb_contam.x1t
int success = c_initialize(&myPlugin,"x1t",NULL,
     "-nocd -i \"c:/program files/gwb/script/pb_contam.x1t\"");

c_exec_cmd – Configure and execute your calculations.

The "c_exec_cmd" function can be used to transmit commands to the plug-in. Each application has a chapter in the reference manual of the documentation on what commands are available. You use these commands to configure the application and then send a "go" command to trigger the calculations.

Syntax

int c_exec_cmd(
  void* plugin,
  char* uline
);

Parameters

plugin
A dereferencable pointer that has already been used with c_initialize. Keeps track of a particular plugged in GWB application.
uline
A string containing the command you wish to send to the GWB application.

Return value

Non-zero on success and zero on failure.

Remarks

None.

Examples

c_exec_cmd(&myPlugin,"3 mmol H+");
c_exec_cmd(&myPlugin,"2 mmol Ca++");
c_exec_cmd(&myPlugin,"5 mmolar Cl-");
c_exec_cmd(&myPlugin,"go");

c_results – Retrieving the results.

Retrieving the results using the GWB plug-in feature can be accomplished using the "c_results" function. The keywords, default units, and return types are the same as those listed in the Report Command chapter of the reference manual in the documentation. To use the "c_results" function you provide the usual plugin parameter, the address of a data block to fill, along with the desired report command and keywords, optional desired units, and the node location of choice (X1t and X2t only).

Syntax

int c_results(
  void* plugin,
  void* data,
  const char* value,
  const char* units,
  int ix,
  int jy
);

Parameters

plugin
A dereferencable pointer that has already been used with c_initialize. Keeps track of a particular plugged in GWB application.
data
Address of data block to fill. Can be NULL.
value
String containing the report command keyword and arguments.
units [optional] (default: NULL or an empty string)
String containing the units you would like the results returned in. Can be NULL or an empty string if you want default units.
ix [optional] (default: 0)
X node position.
jy [optional] (default: 0)
Y node position.

Return value

The number of values written (or to be written) to the data block.

Remarks

  • To determine the size of data block you will need first call this function with data parameter as NULL and with the rest of the parameters filled. If you know the report command you are using only returns a single value you can simply pass a pointer to the correct data type. Please see the "Appendix: Report Command" section in the GWB Reference Manual for details on data types and available keywords. The Reference Manual can be found in the documentation section of the website.
  • If the command fails for any reason, for example if the requested data doesn't exist or the specified unit conversion failed, the data will be filled with ANULL (-999999.0).
  • You will generally want to "#define ANULL -999999.0" (or language equivalent) somewhere in your wrapper.
  • ix is only used when running X1t and X2t. Otherwise it is ignored.
  • jy is only used when running X2t. Otherwise it is ignored.
  • For languages that are dynamically typed (e.g. Python) you will either need to create multiple wrapper results functions (one for each possible data type: int, double, and char*) or pass the expected type as an extra parameter. It is also often best to omit the data parameter in the wrapper function. You then call c_results with a NULL value for data to get the size, allocate C compatible memory, call c_results with the data parameter, convert data, and then finally just return an array of the results. See GWBplugin.py for examples of this.

Examples

// get aqueous species names
int ndata = c_results(&myPlugin,NULL,"species");
char** Species = (char**) malloc(sizeof(char*) * ndata);
c_results(&myPlugin,Species,"species");

// get aqueous species concentrations in mg/kg
double* Conc = (double*) malloc(sizeof(double) * ndata);
c_results(&myPlugin,Conc,"concentration aqueous","mg/kg");

// get pH at node 3,5
double pH = ANULL;
c_results(&myPlugin,&pH,"pH",NULL,3,5);

c_results_c – Retrieving the results with string lengths.

If you are retrieving string values and you need to know the string lengths for conversion purposes, you will need to use the "c_results_c" function. It is equivalent to the "c_results" function, but it also takes an extra parameter which will store the length of the strings.

Syntax

int c_results_c(
  void* plugin,
  void* data,
  const char* value,
  const char* units,
  int ix,
  int jy,
  int* slen
);

Parameters

plugin
A dereferencable pointer that has already been used with c_initialize. Keeps track of a particular plugged in GWB application.
data
Address of data block to fill. Can be NULL.
value
String containing the report command keyword and arguments.
units
String containing the units you would like the results returned in. Can be NULL or an empty string if you want default units.
ix
X node position.
jy
Y node position.
slen
Address of data block to fill with retrieved string lengths.

Return value

The number of values written (or to be written) to the data block.

Remarks

  • To determine the size of data block you will need first call this function with data parameter as NULL and with the rest of the parameters filled. If you know the report command you are using only returns a single value you can simply pass a pointer to the correct data type. Please see the "Appendix: Report Command" section in the GWB Reference Manual for details on data types and available keywords. The Reference Manual can be found in the documentation section of the website.
  • If the command fails for any reason, for example if the requested data doesn't exist or the specified unit conversion failed, the data will be filled with ANULL (-999999.0).
  • ix is only used when running X1t and X2t. Otherwise it is ignored.
  • jy is only used when running X2t. Otherwise it is ignored.

Examples

// get aqueous species names
int ndata = c_results(&myPlugin,NULL,"species");
char** Species = (char**) malloc(sizeof(char*) * ndata);
int* Lengths = (int*) malloc(sizeof(int) * ndata);
c_results_c(&myPlugin,Species,"species",NULL,0,0,Lengths);

c_destroy – Cleaning up.

The "c_destroy" function can be used to free up the underlying memory associated with the plugged in GWB application.

Syntax

 void c_destroy(
  void* plugin
);

Parameters

plugin
A dereferencable pointer that has already been used with c_initialize. Keeps track of a particular plugged in GWB application.

Return value

None.

Remarks

None.

Examples

c_destroy(&myPlugin);

Wrapper support FAQ

What languages do you provide wrappers for?
We currently provide wrappers for C++, Fortran, and Python.

Will you provide wrappers for other languages in the future?
We plan on providing wrappers for other languages where there is a strong demand and it is technically feasible.

Where are the GWBplugin files that I can look at for reference and link against located?
The example code and wrapper files are located in the "src" folder in your GWB installation. The GWBplugin export library and dynamic link library are located in the main GWB installation folder.

Why does my compiler say that it can't find the GWBplugin library to link against even though I am using the correct path to link?
This is commonly caused from using a 32-bit compiler to link against a 64-bit library, or vice versa. Check to ensure the version of the compiler you are using is the same as the version of GWB you installed. Also be sure you are linking against the GWBplugin export library GWBplugin.lib and not the DLL itself.

Why does my computer say that it can't load the GWBplugin DLL?
You need to add your GWB installation folder to your PATH environment variable.

I added my GWB installation path to the PATH environment variable, but it still won't load the GWBplugin DLL?
Be sure the version of GWB you installed (32-bit or 64-bit) is the same as the program you are trying to execute.

For answers to additional questions please check the plug-in section of the reference manual in the documentation, our main support FAQ, or contact us.
 .