3.4. Perturbed Earth-orbiting Satellite Lifetime Maximisation Using a Custom C++ Application¶
In this tutorial, the creation of a custom JSON-based application is described. Read first the general introduction on Writing Custom JSON-based Applications.
The example described on this page is identical to that described in Perturbed Earth-orbiting Satellite Lifetime Maximisation. The only difference is that there, the json_interface
application is used with many input files to run the different cases, while in this case only one input file, containing the shared settings, is used, and a custom C++ application is written to manually modify a few parameters for each case. The C++ code can be found in:
tudatBundle/tudatExampleApplications/satellitePropagatorExamples/SatellitePropagatorExamples/lifetimeMaximisation.cpp
The file lifetimeMaximisation.json
used as input for all the propagations is identical to the file shared.json
created in Perturbed Earth-orbiting Satellite Lifetime Maximisation.
In this case, we do not write a C++ application because we want to use Tudat features that cannot be provided through a JSON file, but because we want to avoid having to generate a different input file for each propagation. The disadvantage of this choice is that we cannot use parallel processing, as all the propagations will be run sequentially by a single process, while when using the json_interface
with different input files, we are able to run many cases concurrently.
In this case there is no need to write a derived class of JsonSimulationManager
with custom implementations for virtual methods. We can simply modify the JSON object containing the settings read from the JSON file before it is actually used to set up the simulation objects. Thus, all the code we need to write is:
1#include <Tudat/JsonInterface/jsonInterface.h>
2#include <SatellitePropagatorExamples/applicationOutput.h>
3
4int main( )
5{
6 const std::string cppFilePath( __FILE__ );
7 const std::string cppFolder = cppFilePath.substr( 0, cppFilePath.find_last_of("/\\") + 1 );
8 tudat::json_interface::JsonSimulationManager< > jsonSimulationManager( cppFolder + "lifetimeMaximisation.json" );
9
10 const std::string outputDirectory = tudat_applications::getOutputPath( ) + "LifetimeMaximisation/";
11 const unsigned int numberOfCases = 365;
12 for ( unsigned int i = 0; i < numberOfCases; ++i )
13 {
14 // Notify on propagation start
15 std::cout << "Running propagation " << i + 1 << " of " << numberOfCases << "..." << std::endl;
16
17 // Define the initial and final epochs
18 const double initialEpoch = i * tudat::physical_constants::JULIAN_DAY;
19 jsonSimulationManager[ "initialEpoch" ] = initialEpoch;
20 jsonSimulationManager[ "finalEpoch" ] = initialEpoch + tudat::physical_constants::JULIAN_YEAR;
21
22 // Define the output file
23 jsonSimulationManager[ "export" ][ 0 ][ "file" ] = outputDirectory + "day" + std::to_string( i + 1 ) + ".txt";
24
25 // Create settings objects
26 jsonSimulationManager.updateSettings( );
27
28 // Propagate
29 jsonSimulationManager.runPropagation( );
30
31 // Export results
32 jsonSimulationManager.exportResults( );
33
34 // Silence unused key warnings after first propagation
35 if ( i == 0 )
36 {
37 jsonSimulationManager[ "options" ][ "unusedKey" ] = tudat::json_interface::continueSilently;
38 }
39 }
40
41 return EXIT_SUCCESS;
42}
In this example, we first create a JsonSimulationManager
, which will contain the JSON object obtained by parsing the specified input file. Then, we write a loop in which we modify this JSON object before it is actually used to set up the objects needed for the propagation (integrator, propagators, bodies, etc.) when we call the updateSettings
method. We can access this JSON object by using the method getJsonObject
. However, we can also modify this object by accessing the at
method and the [ ]
operators of the JsonSimulationManager
directly. Thus, these two lines are equivalent:
jsonSimulationManager.getJsonObject( )[ "initialEpoch" ] = 0;
jsonSimulationManager[ "initialEpoch" ] = 0;
And so are these two lines too:
std::cout << jsonSimulationManager.getJsonObject( ).at( "initialEpoch" ) << std::endl;
std::cout << jsonSimulationManager.at( "initialEpoch" ) << std::endl;
Inside the loop, in which we iterate for each of the propagations to be carried out, we modify the keys initialEpoch and finalEpoch of the JSON object. Additionally, we want each propagation to generate an output file with a unique name, so we also modify the key export[ 0 ].file. Then, we can set up the simulation, run the propagation and export the results.
After the first propagation has been completed, we turn off warnings for unused keys. This is done to silence warnings about the key bodies.satellite.initialState being unused. When running the first propagation, the Keplerian state defined in bodies.satellite.initialState is converted to Cartesian and assigned to propagators[ 0 ].initialStates. Further propagations find that the key propagators[ 0 ].initialStates is defined, and thus they do not use the information at bodies.satellite.initialState, resulting in an unused key warning if we do not set the key options.unusedKey to "continueSilently"
.