How to Add a Resource to GMAT
Wendy Shoan, GSFC, Code 583
February 3, 2015
GMAT has the capability to model many types of Resources, for use in various types of space missions. Resources in GMAT are created, via script or GUI, and used by other Resources or in Commands, to run a particular mission of interest. Here is an example of the scripting of Resources for, in this case, a Lunar Orbit StationKeeping script:
The corresponding Resource Tree (top part only), showing the Resources defined in the script, looks like this:
GMAT provides many types of Resources that are useful in modeling Spacecraft Missions. However, you may need a particular type of resource that is not included in GMAT. In this case, you can add the needed resource to GMAT. There are two types of additions to discuss:
Case 1: In the simpler case, a new subtype of an existing resource type may be required. For example, a mission might require a new type of Thruster, and in this case, you can add to GMAT the code necessary to allow objects of the new Thruster type to be created and used in a mission.
Case 2: In a more complex case, an entirely new class of Resource needs to be created. You can add the new class of Resource to GMAT, along with other supporting code.
In both cases, you will also have to modify other executive and GUI classes to handle the new resource type or subtype.
This document describes a general procedure to follow in order to add a new Resource type or subtype to the baseline GMAT code. There are also considerations specific to the type of resource to be added, e.g., for a new type of Thruster, the Spacecraft and FiniteBurn code may need to be modified to allow use of the new type. This document will not specifically address these resource-specific issues.
If you would prefer to add your new functionality as a plugin, please also see the document "Building a GMAT Plug-in: A Walkthrough Using Visual Studio 2010".
GMAT has a base class,
GmatBase, from which all configurable resource types must be derived. There are often intermediate classes as well, to encapsulate common data and methods for the resource type. For example, there is a
CelestialBody class, which derives from the
SpacePoint class (which itself derives from
GmatBase) and from which
Asteroid are derived [See Figure]. Data and method implementations that are common to all five of these subclasses are placed in the
CelestialBody class, and only those that need specific modifications are added or overridden in the leaf classes. Similarly, methods and data common to all
SpacePoint objects are encapsulated in the
SpacePoint class, and are thus inherited by derived classes. At the top of the inheritance path,
GmatBase provides many methods, data definitions, and settings that are useful to derived Resource classes.
Since a user must set relevant and/or required data on each Resource in order to specify the desired behavior of the resource, the process of adding a new resource to GMAT will include the requirement to, if applicable, provide appropriate user-settable data, or properties (with reasonable defaults), and related methods for the resource so that it can be used as part of the mission. For example, the
Spacecraft class defines a
CoordinateSystem property that a user may set and which is used by its methods. In addition, GMAT classes performing script interpretation, mission execution, object mapping, and other executive functions, require other methods that must be implemented in
Methods for Initialization, Copy, and Cloning
These are a few examples of methods that may require implementation:
This method is called at the beginning of the run, to initialize internal data needed by the resource, and to perform validation of user input values.
This method is called to copy the input object's data to the current one.
This method is called to return a clone of the object. [required on all leaf classes]
Methods for Script User Interfaces
As mentioned, there are properties of your resource that a user may inspect or optionally set. When you define a property for your resource, you need to answer some questions:
- Is the property read-only or can it be set by the user of your resource?
- What is the type of the property?
Each of your new properties must be added to the enumeration in the header which lists available properties, and to the string array specifying the string representation of the property. This will be used by GMAT scripting for access to the properties.
GmatBase provides methods for accessing these properties. Based on how you answered the questions above, you will need to override the following methods:
This method returns the text representation of a specified parameter, given the id.
This method returns the units of a specified parameter, given the id. [useful for the GUI for your class]
This method returns the id of a specified parameter, given the text.
This method returns the Gmat type of the specified parameter. The enumeration
ObjectType in gmatdefs.hpp defines the parameter types.
This method returns string representation of the Gmat type of the specified parameter.
This method returns true if the specified parameter is read-only – that is, the user will not be allowed to modify it. This may be necessary if you want parameters available for other parts of the system to set but not available to the user.
This method returns the
Real value of the specified parameter.
This method sets the
Real value of the specified parameter.
NOTE: there are equivalent Get/Setmethods for other types – Integer, UnsignedInt, UnsignedIntArray, IntegerArray, Rvector, Rmatrix, String, StringArray, OnOff, Boolean, BooleanArray – that may need to be implemented, depending on the types of data needed for your class.
Methods for Setting Reference Objects
If your class needs reference objects set (for example, your class needs to access a Spacecraft object), you will need to implement additional methods, for example:
This method returns the name of the reference object of the specified type. [The name of the reference object is usually set by the user – this will be set via the
SetStringParameter method or the
SetRefObjectName method (see below)]
This method returns an array of the names of the reference objects of the specified type.
This method sets the name of the reference object of the specified type.
This method sets the name of the reference object of the specified type.
This method sets the pointer to the reference object with the specified name and type.
This is just an example of the methods that may need to be implemented for your new class. Please see GmatBase.hpp for the complete list of base methods. It is extremely helpful to find a class similar in function to your new class to determine what other methods/data may need to be included or overridden.
Required Code for Type Determination
GmatBase, we have two arrays –
objectTypeNames – that specify the type of object. In each class, the constructor must add to these in order for the system to know the correct type for each object. These are arrays, so each subclass will add its type. For example, a
Barycenter is derived from
CalculatedPoint, which itself is derived from
SpacePoint. So, by setting these types in each class, a
Barycenter knows that it is a
CalculatedPoint, and a
SpacePoint object. This is set in the default constructor for
In addition, each class must set its parameterCount based on its own enumeration:
Required First Steps
In order to add a new Resource to GMAT, some preliminary steps must be taken to set up your build environment:
1. Download the configured GMAT source
2. Download, or refer to, the GMAT C++ Code Style Guide from the GMAT Wiki – standard GMAT coding style must be followed for any new/modified code.
3. Build GMAT on your platform with no code changes (see instructions on building on GMAT Wiki) [NOTE: to maintain cross-platform compatibility, code must build successfully using the gcc compiler in order to be considered for inclusion into GMAT]
4. Run Sample Missions (or SmokeTests* and possibly a subset of relevant system test folders, if you have access to those) before code modification, to
1) Confirm successful execution of the unmodified GMAT, and
2) Obtain an initial set of 'truth' data for later testing
Once these steps have been completed and GMAT runs successfully as-is, addition of the new resource may begin.
Though GMAT is a modular system, adding a new resource is not as simple as just compiling and linking a new class. There are requirements that must be met regarding, among other things:
- Deriving from base classes (either an existing resource class, or
- Implementing, at a minimum, specific methods
- Adding a new type to the appropriate factory (or creating a new factory) that knows how to create your new resource
- Updating base type lists and related enumerated types
- Adding and/or modifying types and methods in GUI management classes
- Determining look-and-feel of GUI components added for the new resource
The steps required to create and add a new resource to GMAT are described below.
There are practices and/or strategies that may be helpful to a developer in creating a new component, e.g., sometimes it is helpful to:
- First, determine scripting of the new component
- Next, modify/add the minimal set of code for the new resource until the script parses
- Then, add validation of the input data
- Next, incrementally add and validate new functionality until the resource is complete
Adding a New Resource Subtype of an Existing Type (Type 1)
- Decide on the scripting needed to create and access your new resource, as best as you can.
- Determine the public properties needed, their valid values, and how the class will be used in GMAT.
Base Code modifications
- Decide which GMAT resource base class will be the parent of your new class. For example, if you are adding a new type of Thruster, you will need to derive your class from the base
Thrusterclass, located in src/base/hardware.
- Determine whether or not you can copy-and-paste portions of your new class from an existing class. If not, you will need to start from scratch, deriving your class from the base class you selected in the previous step. In either case, make sure the standard header information is present and correct, and your new code follows the GMAT C++ Style Guide.
- Add additional data or methods to the new class as needed. If you add additional user-settable parameters, you will need to create an enumeration listing those new data, as well as static string and
Gmat::ParameterTypearrays to hold the strings and data types for those new parameters (see Appendix A example).
- Implement the pure virtual methods of the parent class (or inherited by the base class), and other public methods whose base/default implementation are not correct or sufficient for your new class. For example, if you need to add some validation to the Initialize() method, you would include new code in your
Initialize()method and (almost certainly) call the parent
Initialize()from your method as well.
- If your class is a leaf class, implement the
- If you do not have any reference objects, use "
DEFAULT_TO_NO_REFOBJECTS" in the public part of your header file.
- If you do not have any owned clone members, use "
DEFAULT_TO_NO_CLONES" in the public part of your header file.
- Add your new type to the appropriate
Factorysubclass in /src/base/factory.
- Add your new resource name to the
ObjectTypeenum in include/gmatdefs.hpp.
- Add to
AUTOMATIC_GLOBAL_FLAGSin GmatBase.cpp, being careful to add your new entries in the correct spot in each.
- Implement validation of your added class data, following the style of other classes (e.g. using
- Implement the functionality of the new class.
- Add your new class(es) to the MakeBase.eclipse file to make sure it is compiled.
- Add your new resource class to the Microsoft Visual C++ 2010 Express libGmatBase project if you are building using MS Visual Studio.
GUI Code modifications
- Add your new type to
- Check the
CreateNewResource()method in the
GmatMainFrameclass to see if you need to add or modify the list (e.g. in the switch statement), to match the modifications to
- Decide whether you can use the
GmatBaseSetupPanel, or if you will need a custom GUI panel for your new resource. See How to Create GMAT Panels for further instructions on creating a new GUI panel. NOTE that if you use the
GmatBaseSetupPanel, you will also need to implement these methods in your new base class: GetPropertyObjectType and GetTypesForList (if you have a parameter of
- In the
ResourceTreeclass, you may need to modify or add methods for your new type, e.g. the OnAdd* methods. Also, add item(s) to the
POPUPenum for your class, or modify existing items as needed.
- You will need to add (or modify) methods in
GuiItemManagerthat keep a list of configured objects of your new type and update that list when necessary, and create/unregister GUI widgets (e.g. combo boxes or lists) that may be needed
- Add your new class(es) to the MakeGui.eclipse file to make sure it is compiled
- Add your new resource class to the Microsoft Visual C++ 2010 Express GMAT_wxGui project if you are building using MS Visual Studio.
Adding a New Resource Type (Type 2)
For this case, there are several steps that must be taken before completing the steps listed above.
Since you are creating a new Resource 'from scratch', you will need to tell GMAT about the new Resource. There are several places in GMAT where you will need to update the code.
- You need to make an entry in the gmatdefs.hpp
Gmat::ObjectTypefor your new Resource. For example, if I were to add an
ErrorModelclass, I would add an ERROR_MODEL entry to
ObjectType. Your new entry should be in a location in the list that makes sense (for example, near similar items, or at the end immediately before the UNKNOWN_OBJECT entry).
- There are two places in GmatBase.cpp that you will need to modify. You must make sure these additions are each inserted into the correct position in the array, corresponding to the position where you added your entry into
- First, you will need to add your class/Resource name to
- Also, you will need to add to the
- First, you will need to add your class/Resource name to
- Create your new class, making sure to derive it from
GmatBase. You will implement it as described above for Type 1, adding methods and data as needed, and implementing applicable methods inherited from
- You need to create a factory that will know how to create objects of your new Resource type. To do this, create the new
Factoryclass, e.g. ErrorModelFactory.hpp and ErrorModelFactory.cpp (see other
Factoryclasses for examples). You will want to make sure that it includes the CreateObject generic method – this is needed for compatibility with recent
- Because objects are generally configured, you need to tell the
ConfigManagerclass about your new Resource. You will need an Add method and a Get method for your new Resource, e.g. AddErrorModel and GetErrorModel.
Moderatoralso needs to know about your new Resource, so you will need to modify Moderator.cpp to register your new factory.
- To ensure that the
Interpretercode can handle your new type:
- Add a string array to Interpreter.hpp, e.g.
Interpreter::BuildCreatableObjectMaps, add a section for your new list.
Interpreter::GetCreatableList, add a section for you new type
- To make sure that objects of your new class are written out correctly to a script, you will need to add to
ScriptInterpreter::WriteScript. Add code there, similar to that for other types, to write objects of your new type.
- You may need to modify
ObjectInitializercode if you need objects of your type to be initialized before or after another specific type of object, or if you need to build references between your Resource type and objects of another type.
The steps for adding a panel and incorporating your new Resource into the other GUI code are identical to steps taken for Type 1.
System Test Readiness Criteria
Once you have completed your code modifications and additions, there are several criteria that must be met before the code will be accepted for inclusion into the GMAT base.
- Update your code base with the latest configured GMAT code, merging the configured code into your code base when necessary.
- Build with the gcc compiler
Then, for those with code modification and system test privileges:
- Run SmokeTests and the applicable resource and/or command system test folder(s) successfully
- Inform testers of changes to GUI components, when applicable
- Wait for approval/confirmation before checking your code in to the repository.
Or, for those without code modification and system test privileges:
- Run unit-tests with the new code – these unit-tests must be thorough and should use input test data obtained from GMAT engineers when available
- Coordinate with GMAT team members to deliver code modifications
Appendix A: Example (Type 1)
This appendix shows an example, illustrating the steps that need to be taken to add a
- Decide on the script syntax for the new resource, based on the data and functionality required. Decide on the fields needed. For example, based on the needed fields, we may need the scripting to include something like this:
Decide on the new class structure. Since we may need or want to add other Power Systems at a later time, we will derive a
Hardwareand then derive a
NuclearPowerSystemclass from that.
Create the new resource class from scratch, or start from a similar class (derived from the same base type) and modify as needed. In this example, we can start from a similar
Hardwareclass and modify. Edit the header to make sure all the author, date, and descriptive information is correct:
- For the
PowerSystembase class, it might be necessary to remove, or not implement,
Hardwaredata and methods that are not needed. In this case, however, it looks like we will need all of the methods from
Add additional data or methods to the new class as needed. For example, since a basic requirement for a power system will be to compute and provide the available power, we will want to add these methods (among others) in the public part of the class:
and in the protected part of the class:
The GetBasePower() method will be fully implemented here. However, other methods, including the GetPowerGenerated() method, may need to be fully implemented in a derived class. We could define a default implementation in the
PowerSystem base class, or leave it as a pure virtual method (as we do here), to require an implementation in descendant classes.
- Implement the pure virtual methods of the parent class and other public methods whose base/default implementation are not correct or sufficient for your new class. Note that we don't need to implement the Clone method here, as it is only required in leaf classes. However, we will want to implement access methods (Get/Set) for Real and String parameters – they should perform as much input validation as it is possible to do at build time. In this case, we do not need to implement methods related to reference objects for this class.
- We will implement the Initialize method, though currently it is only a call-through to the parent
Hardwareclass. The Initialize method is used to do run-time validation of inputs (for things that cannot be verified at build time) and to check for unset reference objects.
- Since the parent class,
Hardware, already contains "DEFAULT_TO_NO_CLONES" in its header, we do not need to add it here.
- Since we don't know if a future derived class will require reference objects, we will not add "DEFAULT_TO_NO_REFOBJECTS" here. However, we will need to remember to add it to the leaf class,
NuclearPowerSystem, since that subclass has no reference objects.
- Since this is not a leaf class, we do not need to add it to any factory class, such as the HardwareFactory.cpp. Again, this is a step we need to remember to perform for the leaf classes.
- We need to tell the system what type of resource this class is:
In addition, remember that each class must set its
parameterCountbased on its own enumeration:
- We also need to add this 'base'
PowerSystemclass to the main list of object types in gmatdefs.hpp, as other parts of GMAT may access objects by this general type:
POWER_SYSTEM, // for PowerSystems
- We will add this new type to the OBJECT_TYPE_STRING in GmatBase.cpp, before the 'UnknownObject':
PowerSystemobjects will not automatically be of global scope, we will also need to add 'false' to the
AUTOMATIC_GLOBAL_FLAGSin GmatBase.cpp, being careful to add it at the correct spot in the array.
- We should also add as much validation as possible to the Set<>Parameter methods. For example, in the SetRealParameter method, add this (among other modifications) to the switch statement:
Be sure to always use the exception class relevant to the subsystem to which your new class belongs. Note the use of the
errorMessageFormat string – this should be used whenever possible, to maintain consistency of error messages.
- Now we can start implementing the 'guts' of the
PowerSystemclass. This means implementing all remaining methods, including, for example, the GetBasePower method.
- Our base
PowerSystemcode changes are complete for now. Add the
PowerSystemclass to the list in MakeBase.eclipse file to make sure it is compiled. If you are developing on a Windows platform, you should also add the class to the project file for Microsoft Visual C++ 2010 Express.
GuiItemTreeData: We need to add our new
GmatMainFrame, add the following code to the CreateNewResource method:
- For the power system class, we need a custom GUI panel. See How to Create GMAT Panels for further instructions on creating a new GUI panel.
- In the
GuiItemManagerclass, we need to do several modifications:
- Remember to set
theNumPowerSystemin the constructors, and, as appropriate
- Add the UpdatePowerSystem method referenced (see Appendix B)
- Add the methods GetPowerSystemComboBox and UpdatePowerSystemList (see Appendix B)
Modify the GetAttachedHardwareList method:
Update the UpdateAll method with:
and further down, with:
- In the UnregisterComboBox method, add:
- Remember to set
- In the
ResourceTreeclass, we need to add or modify code in several places:
In the UpdateGuiItem method, add the following:
In the GetItemTypeAndIcon method, add the following:
- For this example, we are going to treat the
PowerSystemclass like other hardware plugins, even though it is not a plugin. This makes sense as there is already existing code to handle non-thruster, non-tank hardware objects. So, in OnAddHardware, we will need to check the type of the selection (see example code in Appendix B).
We also need to add the
PowerSystemtype to the GetObjectType and GetTreeItemId methods' switch statements:
To make sure
PowerSystemitems are updated correctly, we need to add to the switch statement in the UpdateGUIItem method:
- Now we should create our PowerSystemConfgPanel GUI Panel (please see How to Create GMAT Panels for more information).
- Since the
PowerSystemwill be 'attached' to a
Spacecraft, we also need to create a panel to be used as a tab on the
SpacecraftPanel. See the
PowerSystemPanelheader in Appendix B. The implementation of this panel is very similar to that of existing 'tabs' used for the
- Now, we need to add the
PowerSystemConfigPanelclasses to the list in MakeBase.eclipse file to make sure they are compiled. If you developing on a Windows platform, you should also add the classes to the project file for Microsoft Visual C++ 2010 Express.
Back to Base Code
- Now we need to create the subclass,
NuclearPowerSystem. We will derive it from the
PowerSystemclass so that it inherits all of the methods and data we defined earlier.
- In this case, recall that we have a pure virtual method, GetPowerGenerated, in the
NuclearPowerSystemis a leaf class, we must implement this method here.
- Also, we must implement the Clone() method since this is a leaf class.
Each class must set its parameterCount based on its own enumeration:
- Add "DEFAULT_TO_NO_REFOBJECTS" to the leaf class.
- The class needs to tell the system what type of resource it is:
Back to GUI Code
No changes are needed here. We use the general
PowerSystemConfigPanel class to configure our
Appendix B: Sample Code (Type 1)
Code examples for the following are included here:
Code examples for the following are included here:
ResourceTree.cpp (selected portions)
Appendix C: Example (Type 2)
This appendix shows a brief example, illustrating the extra steps needed in order to add an
ErrorModel class. After these steps are completed, the remaining work would be similar to Type 1 (see Appendix A). Base Code
- We add an entry in the gmatdefs.hpp
- We modify the two arrays in
GmatBase, adding "ErrorModel" to
OBJECT_TYPE_STRINGafter "CoreMeasurement" and adding a value of "false" to the
AUTOMATIC_GLOBAL_FLAGSin the corresponding position.
- We create the
ErrorModelclass, making sure to derive it from
We create the corresponding
- Now we need to tell the
ConfigManagerclass about the new
ErrorModel. We create an AddErrorModel method and a GetErrorModel:
Next we modify Moderator.cpp to register the new ErrorModelFactory. We add an errorModelList to Interpreter. Then we edit Interpreter::BuildCreatableObjectMaps, to add a section for this new list:
Interpreter::GetCreatableList, we add a section to the switch statement for this type as well:
- To ScriptInterpreter::WriteScript, we add:
We do not need to modify
ObjectInitializerat this time.
The steps for adding a panel and incorporating your new Resource into the other GUI code are identical to steps taken for Type 1 (see Appendix A).