C++ FISH Intrinsic Plug-Ins

Introduction

The user may create FISH intrinsics and load them at runtime as plug-ins. The FISH intrinsic must be written in C++, and compiled as a DLL file (dynamic link library) that can be loaded whenever it is needed. The FISH intrinsic uses a C++ interface that provides access to the internal structure of FISH, as well as the data of FLAC3D. When loaded, this intrinsic behaves exactly the same as any of the predefined FISH intrinsics (e.g., math.cos, zone.head, etc.). These custom intrinsics have many advantages over traditional FISH functions. For the same functionality, C++ intrinsics should be from 10 to 100 times faster to execute than FISH functions. If the user is familiar with concurrent programming, even faster execution is possible on multiprocessor hardware. Additionally, direct access to internal data structures that are not available via predefined FISH intrinsics is provided. Finally, a C++ FISH plug-in can link to and make use of any other library or DLL it requires.

In the C++ language, the emphasis is on an object-oriented approach to program structure, using classes to represent objects. The data associated with an object are encapsulated by the object, and are invisible outside of the object. Communication with the object is by member functions that operate on the encapsulated data. In addition, there is strong support for a hierarchy of objects: new object types may be derived from a base object, and the base-object’s member functions may be superseded by similar functions provided by the derived objects. This arrangement confers a distinct benefit in terms of program modularity. For example, the main program may need access to many different varieties of derived objects in many different parts of the code, but it is only necessary to make reference to base objects, not to the derived objects. The runtime system automatically calls the member functions of the appropriate derived objects. A good introduction to programming in C++ is provided by Stevens (1994); it is assumed that the reader has a working knowledge of the language.

The methodology of writing a FISH intrinsic in C++ for operation in FLAC3D is described in Methodology. This includes descriptions of the base class, how to create a Visual Studio project, loading and registration, and how FLAC3D data are accessed. All of the files referenced in this section are contained in the “\ITASCA\Flac3d600\pluginfiles\fish\example” directory. Note that a DLL must be compiled using Microsoft Visual Studio 2010 SP1 for operation in FLAC3D. The interface also requires the use of Qt, a C++ utility library. This library is available free of charge at https://www.qt.io/ Documentation on how to install and operate Qt is available there. We also provide instructions on how to install and configure Qt for Visual Studio for use with the C++ plug-ins. See the Options page of the FLAC3D section of the Itasca web site (http://www.itascacg.com/software/products/flac3d/flac3d-options). Integration into Visual Studio is available, and this documentation assumes it has been installed. Check the Help/About Qt menu in the FLAC3D GUI to determine the exact version of Qt in use, although binary compatibility is provided across versions.

To get started quickly and provide a project for examination, take the following steps.

  • Assuming FLAC3D 6.0 and Visual Studio 2010 are already installed, find file “FLAC3D600VS2010Addin.msi” in the FLAC3D 6.00 installation directory.
  • Execute this installation file by double-clicking on it. (You may need to have administrator’s rights to the machine to do this.)
  • Launch Visual Studio and select File/New/Project from the menu.
  • Select the “Visual C++” category from the “Installed Templates” section.
  • Scroll down to the right, where “Win32 Console Application” appears at the top. You should see the “FLAC3D600 Fish Intrinsic Function” option.
  • Assign a name to the project below. You also have the option to choose a new location in which to place the new project and solution.
  • A dialog will appear, asking you to name your new intrinsic. This will be the name of the class, and a prefix attached to the names of all intrinsic functions added to the code. This name can be changed later by directly editing the source.
  • A project will be created for you, using some simple example functions as a source.
  • This can be freely modified by you to match your specific desired behavior.

Methodology

Base Class Interface for |fish| Intrinsics

The methodology described above is exploited in FLAC3D’s support for user-written C++ FISH intrinsics. A base class interface provides a framework for specific implementation of all of the FISH intrinsics. The many predefined FISH intrinsics are implemented using this same interface. The base interface, called IFishLibrary, is termed an “abstract interface” because it consists entirely of “pure virtual” member functions (signified by the =0 syntax appended to the function prototypes). This means that no object of this base class can be created, and that any derived class object must supply real member functions to replace each one of the pure virtual functions of IFishLibrary. The Example provides a partial listing of IFishLibrary (contained in file “interface\fish\interface\ifishlibrary.h”). This listing has been edited for clarity, including removing comment fields that help document the interface. Note that an IFishLibrary class might add one or many new FISH intrinsics when loaded.

Example - class definition for the IFishLibrary interface:

class IFishLibrary
{
    public:
    virtual QStringList createIntrinsics(IFish *fish)=0;
    virtual void set(IProgram *prog,uint ID,const IFishArguments *reg,
    const IFishParam *p)=0;
    virtual void get(IProgram *prog,uint ID,const IFishArguments *reg,
    IFishParam *p)=0;
    virtual uint getID() const=0;
    virtual QString getName() const=0;
    virtual bool getReadOnly() const=0;
    virtual int getNumArguments() const=0;
    virtual QString getReturnDesc() const=0;
    virtual QString getArgumentDesc() const=0;
    virtual bool willSave() const=0;
    virtual void archiveLibrary(IArchive &a)=0;
    virtual void reset()=0;
    virtual void destroy()=0;
};

General Interface Documentation

The IFishLibrary definition is just one of many classes that make up the general FLAC3D programmer’s interface. The entirety of the computational engine is implemented as a DLL. An entirely new user interface could be created using the functions made available. In fact, the GUI provided by Itasca interacts with FLAC3D entirely through this same interface. The interface is located in the “interface” directory of your FLAC3D installation, in the form of C++ header files. Full documentation of the interface is provided in HTML format in the Programmer’s Interface section of the on-screen Help menu. We will not duplicate that information here. For example, each method in the IFishLibrary interface is fully documented there. This documentation will be kept up-to-date with changes to FLAC3D.

The entry point for access to all model data is the IProgram interface (found in “\interface\kernel\interface\iprogram.h” This interface is passed to the get() and set() methods of IFishLibrary. FLAC3D -specific data (e.g., zone and gridpoint data) are generally available through the IProgram template function findInterface<>; this function template parameter is the interface class that you are interested in accessing.

Example Intrinsic Plug-in

There is an example FISH intrinsic plug-in project provided in the directory “fish\example”. Full documentation for this example can be found in the HTML documentation (see the Example module) or in the source files. This particular example creates seven new FISH intrinsics that allow the specification and creation of a sinusoidal function stored as a table in FLAC3D.

The source file “fish\example\fishexample.cpp” contains additional information (in comments) that is not available in HTML form.

Creating User-Written FISH Intrinsic DLLs

The simplest way to create a FISH intrinsic project in Visual Studio 2010 is to use the “Project Template” add-in provided by Itasca - follow the steps detailed at the end of Section 4.1. Detailed instructions for creating a project from scratch follow. In order to create a DLL in Visual Studio 2010, it is first necessary to create a solution. The solution will contain projects that are essentially a collection of C++ source and header files and their dependencies.

An example solution and project have been provided. This collection of files can be found in the “fish\example” folder of the FLAC3D install directory (“Program Files\Itasca\Flac3D600” by default). To create a custom DLL, the user may simply wish to modify the example provided. We do not, however, recommend this, since the original example will no longer be available for reference. Additionally, Visual Studio embeds a unique identifier (a GUIID) in each solution and project file. Copying a project and renaming it can cause serious confusion within the IDE. Instead, we recommend you create a new project using the following steps and settings:

  1. Launch Visual Studio 2010 from the Start menu and select File/New/Project. Under “Project Types”, select Qt Projects. Under “Templates”, select Qt Library. Select a location and name for the project, and press OK . Note that the Qt project options will only appear if you have Qt’s Visual Studio Add-In installed.
  2. A Qt Library project wizard will appear. Simply select Finish and accept the default settings.
  3. By default, “test.h”, “test.cpp” and “test_global.h” will be created for your project. Right-click on all of these and select Remove in the context menu, followed by Delete in the option dialog.
  4. Copy the files “fishexample.h”, “fishexample.cpp”, “version.rc” and “version.txt” from “fish\example” into the directory created by Visual Studio for your solution and project. Rename the C++ source files to indicate your new library (e.g., “fishtest.h” and “fishtest. cpp”).
  5. In the “Solution Explorer” window of Visual Studio, right-click on the “Header Files” folder and add “fishtest.h”. Then right-click on the “Source Files” folder and add “fishtest. cpp”. Add the file “version.rc” to the “Resources” folder. Add the file “version.txt” as a project object. (Add/Existing Item from the project context menu.)
  6. Right-click on the project entry and select “Properties”. Make certain that Configuration: reads “Active(Debug)” and Platform: reads “Active(Win32)”. You can create Release and x64 versions of the project later by copying these steps with appropriate modifications.
  7. Make the following changes to the project properties:
  • C/C++\General\Additional Include Directories, Add the “interface” subdirectory of your program installation directory.
  • C/C++\General\Debug Information Format = program database.
  • C/C++\General\Warning Level = level 4.
  • C/C++\General\Detect 64-bit Portability Issues = yes.
  • C/C++\General\Treat Warnings as Errors = yes.
  • C/C++\Preprocessor\Preprocessor Definitions = Add EXAMPLE-FISH_EXPORTS (or some other export indicator). You may also want to add FISHDEBUG or some other debug compile indicator (but not _DEBUG).
  • C/C++\Code Generation\Runtime Library = Multi-threaded DLL (not the debug version - the FLAC3D executable you are linking to is a release version using the release runtime libraries).
  • Linker\General\OutputFile = fishtest006.dll - The name of the DLL produced should follow the convention “fish<name>006.dll”, where <name> is the (unique) name of your library as returned by the getName() method. The “fish” prefix identifies it as a FISH plug-in, and “006” indicates the major version number used to indicate binary compatibility. You may, if you have access rights, place the output file in FLAC3D’s “plugins\fish” directory, so it will be loaded automatically upon start-up.
  • Linker\General\Additional Library Directories = $(QtDir)\lib;$(FLAC3D)\exe64\lib - Here “FLAC3D” is assumed to be an environment variable indicating FLAC3D’s install directory. Such an environment variable is not created on install; it must be created manually by the user. Alternatively, the full path could be entered here.
  • Linker\Input\Additional Dependencies = base006.lib; QtCore4.lib - Note that, by default, the Debug configuration will attempt to link to QtCore4d.lib. In this case, the plug-in needs to link to the release version of Qt.
  • Linker\Debugging\Generate Debug Info = yes

To create a release version, make the same property changes in the “Release” configuration properties, but make the appropriate changes in the “C/C++\Optimization” section of the project properties page.

To create a 64-bit version (Debug or Release), make the same property change to the “x64” platform, but link to the 64-bit version of the .lib files in the exe64 directory and the 64-bit version of Qt. Also, by Itasca convention, the resulting DLL name needs to have “64” appended to the base name (for example, “fishtest005 64.dll”). Make sure that you change the Qt version to 64-bit. (Right-click the solution and select “Change Solution’s Qt Version”).

Loading and Running User-Written Model DLLs

If the plug-in DLL has been placed in the “pluginsfish” directory, then it will be loaded automatically on GUI start-up. Otherwise, the plug-in may be loaded explicitly by using the LOAD function <name> command. If the DLL uses the Itasca naming convention, then <name> may simply be the library name (i.e., “test” if the library DLL is named “fishtest005.dll”). Otherwise, the full file and path can be specified. The following directories are searched automatically when attempting to load a plug-in, in order:

  • The directory in the “dir” registry entry, if present, and all sub-directories. The user must set this in registry location “HKEY CURRENT USER\Software\Itasca\Flac3d600”.
  • The directory where the FLAC3D executable resides, and all sub-directories.
  • The current directory, and all sub-directories.

The FISH intrinsics created will not be able to be executed unless the code has been configured for user-defined FISH by using the model configure plugin command. Once so configured, a model will not be able to cycle unless your FLAC3D license has the C++ plug-in option. Note that model save files created with FISH code that calls user-defined FISH intrinsics will require that these intrinsics be present upon restore. FLAC3D will attempt to automatically find and load these intrinsics during restore, but will fail if the file is not in one of the locations FLAC3D automatically searches.

References

Stevens, A. Teach Yourself C++, 4th Ed. New York: MIS Press (1994).