Writing Custom Functions for MotionSolve

The steps below describe how to write a custom function for a MotionSolve input deck. The example makes use of the Python scripting support for MotionSolve. Appendix A contains an example of a custom function written using the C programming language.

Create a Custom Mapping File

Copy and paste the following XML element into a file. For example, C:/test/my_custom_mapping.xml.
  <?xml version="1.0"?>
  <!-- MotionSolve custom mapping definition file -->
  <MSolve_Custom_Mapping>
    <CustomFunction
        symbol           = "MyCustomFnc"
        interpreter      = "Python"
        script_name      = "C:/my_python_scripts/test.py"
        usrsub_fnc_name  = "CustomFnc"      
        num_param        = "5"
    /> 
  </MSolve_Custom_Mapping>

The name defined by the parameter symbol will be used as the custom function name by MotionSolve. Thus, the symbol name is merely an alias for the function defined by the parameter usrsub_fnc_name contained in the script file defined by the parameter script_name. So, the script file (test.py in this case) should contain the method CustomFnc.

Define the Environment Variable

Define and set the environment variable MS_CUSTOM_MAPPING_FILE.


Figure 1.

After completing the above steps, you will be able to use the custom function MyCustomFnc(..,..) as you would a regular MotionSolve expression, such as CUBSPL(..).

The same procedure described above works if you want to use a user subroutine that does not make use of an interpreted script, but of compiled C code instead. In this case, the custom function needs to be defined as:
<CustomFunction
    symbol           = "MyCustomFnc"
    usrsub_dll_name  = "valid_path_name"
    usrsub_fnc_name  = "CustomFnc"
    num_param        = "5"
  />

Appendix A

The following example illustrates a custom function written to calculate the dot product of a unit axis of marker J with another unit axis of marker I.

DOT1(I,1,J,3) = UVX(I)*UVZ(J).

<!-- Custom Function Mapping section-->
  
  <CustomFunction
      symbol           = "DOT1"
      usrsub_dll_name  = "NULL"
      usrsub_fnc_name  = "VARSUB_DOT1"
      num_param        = "4"
  />
An implementation of the corresponding usersub associated with the DOT1:
DLLFUNC void STDCALL VARSUB_DOT1 
                  (int *id, double *time, double *par, int *npar,  int *dflag, int *iflag, double *value)
{
    int nstates,errflg;
    int i_marker_id = (int)par[0];
    int i_axis_idx = (int)par[1];
    int j_marker_id = (int)par[2];
    int j_axis_idx = (int)par[3];
    double axis_i[3], axis_j[3];
    switch (i_axis_idx)
    {
    case 1:
        c_sysary("uvx",&i_marker_id,1,axis_i, &nstates, &errflg);
        break;
    case 2:
        c_sysary("uvy",&i_marker_id,1,axis_i, &nstates, &errflg);
        break;
    case 3:
        c_sysary("uvz",&i_marker_id,1,axis_i, &nstates, &errflg);
        break;
    default:
        break;
    }
    switch (j_axis_idx)
    {
    case 1:
        c_sysary("uvx",&j_marker_id,1,axis_j, &nstates, &errflg);
        break;
    case 2:
        c_sysary("uvy",&j_marker_id,1,axis_j, &nstates, &errflg);
        break;
    case 3:
        c_sysary("uvz",&j_marker_id,1,axis_j, &nstates, &errflg);
        break;
    default:
        break;
    }
    *value = axis_i[0]*axis_j[0]+axis_i[1]*axis_j[1]+axis_i[2]*axis_j[2];
}

Now, the function DOT1 can be used. For example, DOT1(I,1,J,3) which is simply UVX(I)*UVZ(J).