How to add math node and built-in GMAT Functions

Introduction

GMAT provides a flexible mechanism that lets users place both scalar and matrix computations into the command sequence for a mission. This mechanism is called inline mathematics in GMAT. For architectural design and specification on inline mathematics, refer to Chapter 30 “Inline Mathematics in GMAT” in /doc/SystemDocs/ArchitecturalSpecification/GMAT-Architectural-Specification.pdf.

The GMAT function allows users to write a sub-script and save to separate file and called from the main script or within a function itself. For architectural design and specification on GMAT functions, refer to Chapter 31 “GMAT and MATLAB Functions” in /doc/SystemDocs/ArchitecturalSpecification/GMAT-Architectural-Specification.pdf.

The Built-in GMAT function is predefined function implemented in GMAT which allows to access internal objects and perform specified tasks, such as reading a file, perform coordinate system conversion, etc.

There are many types of built-in functions implemented in GMAT. However new functions can be created to extend GMAT to perform a new task. This document will describe a general procedure to add a new function to the baseline GMAT code.

Overview

Inline functions can be used in the script for math or string operations. The assignment command supports the use of inline mathematical expressions on the right-hand side of the command. These expressions follow the general syntax rules of MATLAB expressions, and can use a variety of operators and built-in functions. The built-in GMAT functions can be used to access internal data and perform predefined tasks.

The following sample script shows how math equations used in the script:

Create Spacecraft Sat;
Create Variable a b c d result;
Create Array A[3,3] B[3,3] C[1,1];
BeginMissionSequence
C(1,1) = A(1,1) + B(1,1);
Sat.X = Sat.X + 1000;
result = Sat.X/(1000/c/d) - 1.09056168*10^(-5);
result = sin(  abs(-.5 ) + acos(.5 ) -  asin(.5 ) * atan(.5 ) * atan2(.5,.5));

The following sample script shows how built-in GMAT functions can be used in the script:

Create Spacecraft sat;
sat.EphemerisName = 'Code500_86400sec.eph';
Create String initialEpoch finalEpoch;
Create Array initialState[6,1] finalState[6,1]
BeginMissionSequence
[initialEpoch, initialState, finalEpoch, finalState] = GetEphemStates('Code500', sat, 'A1ModJulian', EarthMJ2000Eq);

See GMAT help document for more sample scripts.

The following table briefly describes the difference between math node functions and built-in GMAT functions:

 

Base Class

Calling Command

# Input

# Output

Return Type

MathFunction

MathNode

Assignment

1 or 2

1

Real or Rmatrix

NumericFunctionNode

MathNode

Assignment

1 or more

1

Real or Rmatrix

StringFunctionNode

MathNode

Assignment

1 or more

1

Real or std::string

BuiltinGmatFunction

Function

CallFunction

0 or more

0 or more

Any GMAT type

As of November 8, 2016, there are 34 math functions (Abs, Acos, Acosh, Add, Asin, Asinh, Atan, Atan2, Ceil, Cos, Cosh, DegToRad, Determinant, Divide, Exp, Fix, Floor, Inverse, Log, Log10, Multiply, Negate, Norm, Power, RadToDeg, Rand, Randn, Sin, Sinh, Sqrt, Subtract, Tan, Tanh, Tranpose), 4 numeric functions (Cross3, Diag, Min, Mod), 5 string functions (Sprintf, Strcat, Strcmp, Strfind, Strrep), and 3 BuiltinGmatFunction (GetEphemState, GetLastState, SetSeed) are implemented in GMAT.

Procedure for adding a new math function

As the following class diagram shows, note that not all math functions are shown in the diagram, all math functions used in the arithmetic equation should be derived from the MathFunction class.

All math functions must operate on Real or Rmatrix values. Math functions such as Sin()or Cos() require only one Real input and return one Real output while other functions require two Real inputs such as Add() or Multiply(). If a new function needs only one Real input and output, it can be created by copying Sin() function.  Below is the procedure for creating a new Round() function that returns the nearest integer value in Real, rounding away from zero in halfway cases.

  1. Copy base/math/Sin.hpp and Sin.cpp to base/math/Round.hpp and Round.cpp.
  2. Replace “Sin” with “Round” in Round.hpp and Round.cpp files.
  3. Update file prolog and method prologs.
  4. In Round::Evaluate(), make sure it is calling GmatMathUtil::Round() with leftNode value.
  5. In base/interpreter/MathParser.cpp, add “round” to realFuncList in MathParser::BuildAllFunctionList().
  6. In base/factory/MathFactory.cpp, add “#include “Round.hpp” and add new math node type “Round” in MathFactory::CreateMathNode() to create an instance of Round. In MathFactory::BuildCreatables(), add “Round” to creatable list.
  7. Add math/Round.cpp to base/CMakeList.txt.
  8. Build GMAT.
  9. Create a script to test Round() function in the script and run the test.

Procedure for adding a new string function

 

As the following class diagram shows, all string functions used in the assignment command should be derived from the StringFunctionNode class.

 

 

 

All string functions must operate on string values. Most string functions return string value. There is currently one string function Strcmp() returns Real number. If new string function needs to return Real number, it can be created by copying Strcmp() and implement Evaluate() method. For function returning string value can be created by copying Strcat() and implement EvaluateString() method. For step by step procedure, a new Upper() function will be created as an example. Upper() function returns upper case of input string.

  1. Copy base/math/Strcat.hpp and Strcat.cpp to base/math/Upper.hpp and Upper.cpp.
  2. Replace “Strcat” to “Upper” in Upper.hpp and Upper.cpp files.
  3. Update file prolog and method prologs.
  4. In Upper::EvaluateString(), make sure it is calling GmatStringUtil::ToUpper() with wrapper->EvaluateString() value.
  5. In base/interpreter/MathParser.cpp, add “upper” to builtinFuncList in MathParser::BuildAllFunctionList().
  6. In base/factory/MathFactory.cpp, add “#include “Upper.hpp” and add new math node type “Upper” in MathFactory::CreateMathNode() to create an instance of Upper. In MathFactory::BuildCreatables(), add “Upper” to creatable list.
  7. Add math/Upper.cpp to base/CMakeList.txt.
  8. Build GMAT.
  9. Create a script to test Upper() function in the script and run the test.

 

Procedure for adding a new built-in GMAT function

 

As the following class diagram shows, all built-in GMAT functions should be derived from the BuiltinGmatFunction class.

 

 

Built-in GMAT function call can have any number of predefined inputs and outputs. As an example, a new function called QuatToCosineMatrix() will be created. This function takes 4 element Array of quaternion as an input and converts to 3 by 3 element Array of cosine matrix as output. The function interface will look like “[cosMat] = QuatToCosineMatrix(quat).” This function will call base/util/AttitudeConversionUtility::ToCosineMatrix when executing.

  • Copy base/function/GetLastState.hpp and GetLastState.cpp to base/function/ QuatToCosineMatrix.hpp and QuatToCosineMatrix.cpp.
  • Replace “GetLastState” to “QuatToCosineMatrix” in QuatToCosineMatrix.hpp and QuatToCosineMatrix.cpp files.
  • Update file prolog and method prologs.
  • In QuatToCosineMatrix.hpp, under protected, add member data to hold input and output values.
Rvector quat;
Rmatrix33 cosMat;
  • In constructor, add dummy input names to inputNames and inputArgMap. Input names must be unique since these names will be added to function object store. Initialize wrappers to NULL.
inputNames.push_back(“__BuiltinFunction_QuatToCosineMatrix_input_1_quat__”);
inputArgMap.insert(std::make_pair(“__BuiltinFunction_QuatToCosineMatrix_input_1_quat__”, (ElementWrapper*) NULL));
  • In constructor, add dummy output names and expected output wrapper type. Output names must be unique since these names will be added to function object store. Initialize wrappers to NULL. Add output types and size of output.
outputNames.push_back("__BuiltinFunction_QuatToCosineMatrix_output_1_cosmat__");
outputArgMap.insert(std::make_pair("__BuiltinFunction_QuatToCosineMatrix_output_1_cosmat__", (ElementWrapper*) NULL));
outputWrapperTypes.push_back(Gmat::ARRAY_WT);
outputRowCounts.push_back(3);
outputColCounts.push_back(3);
  • In copy constructor and assignment operator, copy member data.
  • In Execute(), check for output type and size. Check if input object exist in the object store. If it exist get object pointer and check for its type. Input must be a type of Gmat::ARRAY.
std::string msg;
GmatBase *obj = NULL;
Array *input_quat = NULL;
for (unsigned int i = 0; i < inputNames.size(); i++)
{
   std::string objName = inputNames[i];
   ObjectMap::iterator objIter = objectStore->find(objName);
   if (objIter != objectStore->end())
   {
      obj = objIter->second;
      if (obj == NULL)
      {
         msg = msg + "Cannot find the object '" + objName + "' in the objectStore\n";
      }
      else
      {
         // Check for Array type
         if (obj->IsOfType(Gmat::ARRAY))
         {
            input_quat = (Array*)obj;
         }
         else
         {
            msg = msg + "The object '" + objName + "' is not valid type to retrieve QuatToCosineMatrix()\n";
         }         
      }
   }
}
  • Get row and column number from the input array.
  • Convert input quaternion to cosine matrix by calling AttitudeConversionUtility::ToCosineMatrix().
  • Create local output Array to hold value of cosine matrix.
  • Create output array wrapper and set it to wrapper pointer in outputArgMap which was initially set to NULL.
  • In base/factory/FunctionFactory.cpp, add “#include “QuatToCosineMatrix.hpp” and add new builtin function type “QuatToCosineMatrix” in FunctionFactory::CreateFunction() to create an instance of QuatToCosineMatrix. In FunctionFactory::BuildCreatables(), add “QuatToCosineMatrix” to creatable list.
  • Add function/QuatToCosineMatirx.cpp to base/CMakeList.txt.
  • Build GMAT.
  • Create a script to test QuatToCosineMatrix() function in the script and run the test.