-
Notifications
You must be signed in to change notification settings - Fork 0
The Sim Config
The Fundamentals → The Sim Config |
---|
Written by Connor Jakubik.
Prerequisites:
The Simulation Configuration file, or Sim Config for short, is a JSON
-formatted text file that holds a simulation's Entities, Systems, and their initial conditions. This sim config is intended to represent the entirety of the initial state and settings of a simulation. This way, changes to a simulation such as tweaking a value or adding another Entity are localized in one file, and not in compiled code.
When the sim starts, all of the parameters and other data in the sim config are applied to the simulation's copy of the Entities and Systems, and from then on any param value accesses and modifications are done to those Entities and Systems, not to the Sim Config. A running sim can be saved to a new Sim Config.
The sim config JSON format maps directly to the ParamMap representation of Entity and System parameters that will exist in the simulation. It also may contain references to other assets such as other Sim Configs, Entity Configs, and assets to be loaded during the simulation.
Besides where noted, all of these correspond to Schema Version 2.0.0, which means the syntax uses our 2.0.0 parser. We plan to at some point make a 3.0.0 sim config schema, and maintain ways to upgrade a 2.0.0 sim config to the latest schema.
This is an empty Sim Config. Sim Configs without a "meta"
field are assumed to be schema version 1.0.0 (deprecated). This here is the bare minimum to constitute a simulation. If UseCurrentTime
is true
, StartDateTAI
can be omitted as well. This sim will not start in a graphical client like SpaceCRAFT because there are no pawns in the sim.
{
"meta": {
"schemaversion_major": 2,
"schemaversion_minor": 0,
"schemaversion_patch": 0,
"Title": "Example Empty Sim Config",
"Description": ""
},
"IncludeSims": [
],
"DataManager": {
"SimEntity": {
"Time": {
"UseCurrentTime": false,
"Timescale": 1.0,
"StartDateTAI": [ "timestamp", "2022-10-01T00:00:00.000000Z" ]
}
},
"entities": [
]
},
"SystemManager": {
"Systems": [
],
"Taskflows": [
]
}
}
This is an array of asset paths to other Sim Configs that you are "including" in your sim. The way this works is the Entities and Systems defined in the included sims are combined with those of the sim config that is including them.
NOTE: There is not yet a functionality to deduplicate Entity names between simulations, so try to avoid overly common names for entities, or names that correspond with existing names like Earth.
Here is an example from the SolarSystemExplorer
sim:
"IncludeSims": [
"Core/Compute_Server/packages/BaseSims/SolarSystem.json",
"Core/Compute_Server/packages/BaseSims/SolarSystemLabels.json"
],
The SolarSystem
sim config, or one of the other Base Sims configs like EarthMoonSun
, are meant to be used as an IncludeSim
to set up the solar system digital twin for the simulation. Because it's referenced as an IncludeSim instead of having its objects copy pasted, sims that use it can inherit updates that are made to the base sim without modifications themselves. The included Base Sims are all in Core/Compute_Server/packages/BaseSims
, but any sim config can be included in another sim config as long as circular dependencies are avoided.
SolarSystem
has all of the planets and moons in the sim, and assigns the Spicy
System to all of those. Spicy
is set to be a Singleton System (duplicate definitions in included sims are merged with it), so we can also define and assign Spicy
inside other including or included sim configs.
SolarSystemLabels
adds optional holographic labels to major celestial bodies, and also uses Spicy
to place them.
The SimEntity is a globally accessible Entity, made specifically to contain simulation-wide configuration settings like Time.
Here is an example SimEntity from the SolarSystem
base sim.
"SimEntity": {
"Time": {
"UseCurrentTime": false,
"Timescale": 1.0,
"StartDateTAI": [ "timestamp", "2025-01-12T23:54:49.000000Z" ]
},
"CelestialObjects":
{
"Sun": ["EntityRef", "Sun"],
"Mercury": ["EntityRef", "Mercury"],
"Venus": ["EntityRef", "Venus"],
"Earth": ["EntityRef", "Earth"],
"Moon": ["EntityRef", "Moon"],
"Mars": ["EntityRef", "Mars"],
"Phobos": ["EntityRef", "Phobos"],
"Deimos": ["EntityRef", "Deimos"],
"Jupiter": ["EntityRef", "Jupiter"],
"Saturn": ["EntityRef", "Saturn"],
"Titan": ["EntityRef", "Titan"],
"Uranus": ["EntityRef", "Uranus"],
"Neptune": ["EntityRef", "Neptune"],
"Pluto": ["EntityRef", "Pluto"],
"Charon": ["EntityRef", "Charon"],
"Eros": ["EntityRef", "Eros"],
"Vesta": ["EntityRef", "Vesta"]
},
"GraphicsFrame": ["EntityRef", "Earth"],
"J2000Frame": ["EntityRef", "SSB"]
},
In the SimEntity
object...
"SimEntity": {
We have no Name, Type, or Systems field unlike the other Entities. The SimEntity will always be of type Data
. Data
entities still exist in the client application but don't have any spatial/visual significance. They're just data containers.
"Time": {
"UseCurrentTime": false,
"Timescale": 1.0,
"StartDateTAI": [ "timestamp", "2022-10-01T00:00:00.000000Z" ]
},
The Time structure here consists of an option to use the current real-life time, a number for timescale (can be negative), and a date-time structure for use if "UseCurrentTime": false
. This time right here is 2022 October 1st, 00:00:00. This is in the TAI time standard (UTC minus leapseconds), which you can convert UTC or other timezones to through various online conversion tools. The "EndDateTAI"
parameter is an optional timestamp parameter. If you have EndDateTAI
, your sim will be stopped if the sim time is greater than this end time. Otherwise, your sim will continue until something else stops the sim.
"CelestialObjects":
{
"Sun": ["EntityRef", "Sun"],
"Mercury": ["EntityRef", "Mercury"],
"Venus": ["EntityRef", "Venus"],
"Earth": ["EntityRef", "Earth"],
"Moon": ["EntityRef", "Moon"],
"Mars": ["EntityRef", "Mars"],
"Phobos": ["EntityRef", "Phobos"],
"Deimos": ["EntityRef", "Deimos"],
"Jupiter": ["EntityRef", "Jupiter"],
"Saturn": ["EntityRef", "Saturn"],
"Titan": ["EntityRef", "Titan"],
"Uranus": ["EntityRef", "Uranus"],
"Neptune": ["EntityRef", "Neptune"],
"Pluto": ["EntityRef", "Pluto"],
"Charon": ["EntityRef", "Charon"],
"Eros": ["EntityRef", "Eros"],
"Vesta": ["EntityRef", "Vesta"]
},
"GraphicsFrame": ["EntityRef", "Earth"],
"J2000Frame": ["EntityRef", "SSB"]
Here we have several parameters that each do different things in the sim. For all sims, a J2000Frame
EntityRef is required to enable core functionality for establishing the J2000 / ICRF reference frame in the sim, which is used for things like rotating the star rendering. You won't have to worry about this parameter if you include one of the Base Sims. The GraphicsFrame
EntityRef in the SimEntity is subordinate to the same parameter in a Pawn
, but if the same parameter does not exist in the currently-possessed Pawn, GraphicsFrame
sets the frame for the graphical programs in the sim to use as their coordinate basis. More information about Pawn GraphicsFrame: https://github.com/SimDynamX/SpaceTeamsPro/wiki/The-Entity#pawn.
The other parameters here are sim-specific but for some reason or another are placed in the SimEntity to be globally accessible. "CelestialObjects" is an array of EntityRefs that makes it easy to reference all the major celestial bodies in the SolarSystem
base sim.
},
End of the SimEntity.
All of the objects in your simulation will be Entities, spawned from the specifications given by each Entity declaration in the Sim Config. Each Entity gets parsed by the Compute_Server, SpaceCRAFT, and other Space Teams programs, but they do very different things with the data.
This example is used for testing of parameter reading, so it uses most of the parameter types with dummy values.
{
"DataManager": {
"entities": [
{
"#Required": {
"Name": "BigEntity",
"Type": "Object_Loaded",
"Systems": []
},
"#Object_Loaded": {
"Mesh_Path": "Some/Path/To/Mesh"
},
"Dynamics": {
"PropagationType": "ComputePosition",
"ControlledObject": true,
"GenerateCollider": true,
"Mass": 200.4
},
"Graphics": {
"Scale": 2.0,
"IsLightSource": true,
"RenderFar": true
},
"some_string": "Some string",
"double_down": 10,
"floating_high": [ "float", 88.6 ],
"is_cool": [ "bool", true ],
"age": [ "int32", 55 ],
"two_times": [ "doubleV2", 2, 3 ],
"three_times": [ "doubleV3", 4, 5, 6 ],
"four_times": [ "doubleV4", 7, 8, 9, 10 ],
"mat_2": [
"doubleM2x2",
11,
12,
21,
22
],
"mat_3": [
"doubleM3x3",
11,
12,
13,
21,
22,
23,
31,
32,
33
],
"little": [ "EntityRef", "LittleEntity" ],
"dub_arr": [ "array", "double", 0.0, 1.1, 2.2, 3.3, 4.4, 5.5 ],
"bool_arr": [ "array", "bool", true, true, false, false ],
"int_arr": [ "array", "int32", 0, 1, 2, 3, 4, 5 ],
"v_arr": [
"array",
"doubleV2",
[ 0.0, 0.1 ],
[ 1.0, 1.1 ],
[ 2.0, 2.1 ],
[ 3.0, 3.1 ],
[ 4.0, 4.1 ]
],
"empty_dub_arr": [ "array", "double" ],
"str_arr": [ "array", "string", "0", "1", "2" ],
"advanced_parameter_JSON": [
{
"Type": "string",
"NotType": "JSON",
"Value": "yo",
"NotValue": {
"JSONstuff": "JSONvaluestuff",
"_": "Any valid JSON is fine here"
},
"Description": ""
}
],
"advanced_parameter_array": [
{
"Type": "array",
"Value": [ "double", 0.1, 0.2 ]
}
],
"advanced_parameter_units": [
{
"Type": "double",
"Value": 0.1,
"Unit": "lumen"
}
],
"advanced_parameter_specifiers": [
{
"Type": "double",
"Value": 0.1,
"Unit": "lumen",
"Description": "Test advanced parameter with more advanced stuff",
"Const": true,
"RateLimitRate": 100.0
}
],
"advanced_parameter_specifiers2": [
{
"Type": "string",
"Value": "hello",
"Unit": "",
"Description": "Test advanced parameter with more advanced stuff",
"Const": false
}
],
"SensorArmOffset": [
{
"Type": "doubleV3",
"Value": [ 0.0, 0.1, 0.3 ],
"Unit": "meter",
"Description": "Example usage of advanced parameter"
}
],
"NestedParamMap1": {
"double_down": 10,
"floating_high": [ "float", 88.6 ],
"is_cool": [ "bool", true ],
"name": "Big Cool Guy",
"SensorArmOffset": [
{
"Type": "doubleV3",
"Value": [ 0.0, 0.1, 0.3 ],
"Unit": "meter",
"Description": "Example usage of advanced parameter"
}
]
},
"SCTimestamp": [ "timestamp", "1973-09-16T06:03:52.000Z" ],
"SCDuration": [ "duration", 0.123, "s" ],
"SCDurationMin": [ "duration", 0.123, "m" ],
"SCDurationDays": [ "duration", 3.14, "d" ],
"SCDurationKyears": [ "duration", 20.71, "kyear" ],
"SCTimestampArr": [ "array", "timestamp", "1973-09-16T06:03:52.000Z", "1776-07-4T12:43:52.000Z", "1876-10-4T00:03:52.000Z" ],
"SCDurationArr": [ "array", "duration", 1.0, -0.83, "sec" ],
"SCDurationArrDays": [ "array", "duration", 8.5, -9.9, "days" ],
"test_int8": [ "int8", 0 ],
"test_int16": [ "int16", 0 ],
"test_int32": [ "int32", 0 ],
"test_int64": [ "int64", 0 ],
"test_uint8": [ "uint8", 0 ],
"test_uint16": [ "uint16", 0 ],
"test_uint32": [ "uint32", 0 ],
"test_uint64": [ "uint64", 0 ],
"Empty_Array": [ "array", "double" ],
"Empty_Map": {}
},
{
"#Required": {
"Name": "LittleEntity",
"Type": "Object",
"Systems": []
},
"#Object": {
"Mesh_Path": "Some/Path/To/Mesh"
},
"some_other_param": 42.0
}
]
}
}
NOTE: Advanced Parameters are currently deprecated but their functionality may be reintroduced in a later version of the Platform (since v0.25.0).
{
"#Required": {
"Name": "BigEntity",
"Type": "Object_Loaded",
"Systems": ["ExampleSystem1", "ExampleSystem1"]
},
"#Object_Loaded": {
"Mesh_Path": "Some/Path/To/Mesh"
},
- The
{
starts the Entity. - The
"#Required"
section is always required, no matter the type. -
"Name"
is used as the name for this Entity for labeling and EntityRef purposes. -
"Type"
affects how this Entity is spawned. -
"Systems"
is a JSON array of System nametags for the Systems that apply to this Entity.
All values, with the one exception of the "#Required": { "Systems"
array, are loaded as parameters on that Entity (as of v0.25.0).
For all Entity types besides "Data"
, there will be another required section that is dependent on Type
. It will be named the same as the value of Type, so Planet
-type Entities will have a #Planet
nested param map for configuring planet-specific properties. Information on Entity Types and their necessary parameters is here: https://github.com/SimDynamX/SpaceTeamsPro/wiki/The-Entity#entity-types.
The further data on an Entity is the rest of the Parameters:
"Dynamics": {
"PropagationType": "ComputePosition",
"GenerateCollider": true,
"Mass": 200.4
},
"Graphics": {
"Scale": 2.0,
"IsLightSource": true,
"RenderFar": true
},
"some_string": "Some string",
"double_down": 10,
"floating_high": ["float", 88.6],
"Location": ["doubleV3", 0.0, 0.0, 0.0],
"Rotation": ["doubleV4", 0.0, 0.0, 0.0, 1.0],
"Velocity": ["doubleV3", 0.0, 0.0, 0.0],
"AngVelocity": ["doubleV3", 0.0, 0.0, 0.0],
...
- The common parameters that are used for common functionality such as dynamics and graphics are described here: https://github.com/SimDynamX/SpaceTeamsPro/wiki/The-Entity#entity-common-parameters
- The Directional Light in SpaceCRAFT is handled by finding the first Entity with the parameter
"IsSimLightSource": true
, then using that Entity's location compared to the graphics origin for the light direction. If no entity is found with that parameter true, the light direction does not change from the X+ direction. - Any parameters can be added to an Entity, for data storage or to be used as variables that are affected by Systems.
- Top level parameters: Just add the parameter anywhere outside of the
#Required
and#<Type name>
sections. - Nested Parameter Maps: example:
"_": "(Inside an Entity in entities)", "NestedMap1": { "NestedMap2": { "NestedMap3": { "NestedMap4": { "NestedMap5": { "ParameterKey1": "ParameterValue1" } }, "ParameterKey1": "All of these nested maps are valid places for more parameters", "ParameterKey2": "You only need to follow JSON's rules on no duplicate keys; you can use the same key in a different map.", "RandomName_jlkejoasjdflsflkw": 1.1 } } }
- Here's how you would get the value
string this_got_ParameterValue1 = anEntity->GetParam<string>({""NestedMap1","NestedMap2","NestedMap3","NestedMap4","NestedMap5","ParameterKey1"});
- Top level parameters: Just add the parameter anywhere outside of the
The "behavior" of a simulation will always be handled by Systems, but the Sim Config is where the user specifies which Systems are needed and how each System instance is configured. System code only runs in the single, heavily parallel multithreaded Compute_Server process that is started with the simulation, with the exception of Python systems. We have planned functionality to federate (distribute) System-running tasks to other processes and computers.
{
"SystemManager": {
"Systems": [
{
"Nametag": "SDCC_Setup",
"Active": true,
"Source": "SDCC_2020/bin/SDCC_Setup",
"Type": "TaskflowTask",
"Frequency": 1.0,
"Inst_Parameters": {
"timescale": 1.0
}
},
{
"Nametag": "ISS_Trajectory",
"Active": true,
"Source": "ISS_Orbit/bin/Satellite",
"Type": "TaskflowTask",
"Frequency": 100.0,
"Inst_Parameters": {
"timescale": 1.0,
"UseRealTelemetry": false,
"UseExistingLocation": true
}
},
{
"Nametag": "Point_NegZ_At_Earth",
"Active": true,
"Source": "PointAt/bin/PointAt",
"Type": "TaskflowTask",
"Frequency": 200.0,
"Inst_Parameters": {
"PointedAtName": "Earth",
"AxisToPointWith": "-z",
"UseSecondConstraint": true,
"SecondConstraintMode": "VelocityDirection",
"SecondConstraintAxis": "x"
}
},
{
"Nametag": "Data_Monitor",
"Active": true,
"Source": "Data_Monitor/bin/Data_Monitor_3",
"Type": "Data",
"Frequency": 15.0,
"Inst_Parameters": {
}
},
{
"Nametag": "Spicy",
"Source": "Spicy/bin/Spicy",
"Type": "TaskflowTask",
"Frequency": 100.0,
"Inst_Parameters": {
"timescale": 1.0,
"UseCurrentTime": true
}
}
],
"Taskflows": [
{
"Nametag": "OrbitalMotion",
"Active": true,
"Type": "DependencyGraph",
"Frequency": 100.0,
"Systems": [
"SDCC_Setup",
"Spicy",
"ISS_Trajectory"
],
"Dependencies": [
["SDCC_Setup","before","Spicy"],
["Spicy","before","ISS_Trajectory"]
]
}
]
},
- The
"Nametag"
field should be used for giving the System a short but identifiable name, which is then used for assigning that System instance to Entities. For example, if I was using the Data_Monitor_2 System, I may make the Nametag just "Data_Monitor". This allows the user to change out the Source of a System while keeping the same Nametag on the assigned Entities. - The
"Source"
field is for the path to the compiled binary of a System. The path is relative from inside theCompute_Server/packages/
directory. This binary file is a Dynamically Loaded Library (.dll) AKA Shared Object (.so), which is made when the System is compiled. SpaceCRAFT tries to load the DLL, and reports to the console output if the loading fails. - The
"Type"
field is used to specify how a System is set up, and if it is a special variant of System.- Types:
-
"Normal"
-C++
-code System that compiled along with the SpaceCRAFT build; normal. -
"Internal"
-C++
-code System that is normal except for having extended access to core SpaceCRAFT code. These libraries require full SpaceCRAFT Compute_Server source code access to compile/recompile. Use this type only if you know what you are doing; the access you get violates core SpaceCRAFT architecture principles in order to provide what amount to "admin tools".- Subclass of
Internal_System
- Subclass of
-
"Lua"
-lua
-script System that starts an interpreter that executes a given.lua
script at "Source", with access to the SpaceCRAFT Lua API.- You don't make another C++ file for this; just the
.lua
script in a/src
directory.
- You don't make another C++ file for this; just the
-
"Python"
- python-script System that starts another process that executes a given.py
script at "Source", with access to the SC_Compute_Server Python API. -
"TaskflowTask"
- A Normal System which has its normal update cycle replaced by special update ordering in a Taskflow with other Systems. This is most often arranged in a"DependencyGraph"
Taskflow, where dependencies between Systems are specified and all Systems run as asynchronously as possible with the exception of the dependencies.
-
- Types:
- The
"Frequency"
field is for specifying a target update rate (in Hz) for the System to attempt to hit. This is how often the update() function will be called in your System. Warnings will appear if your System spends too long in the update() to fit within the allotted period. - The
"FreqType"
field specifies how the System update scheduler reacts to changes in timescale.- Types:
-
"Sim"
- The System update frequency will be multiplied by the absolute value of sim timescale. The update frequency only changes if absolute value of sim timescale is greater than 1 (as of v0.25.0). -
"IRL"
- The System update frequency will remain the same relative to real-world time despite sim timescale changes. -
"NoSleep"
- (Not yet implemented) Skips thread-sleep and immediately starts next update when previous update finishes.
-
- Types:
- The
"Active"
field specifies if the System init and update functions run.- If
"Active": true
or"Active"
is not specified, the System will run its init functions right away on sim startup, and then continue to update. - If
"Active": false
, the System will not start immediately. If something ends up activating the System later, it will init if it hasn't already, then it will proceed to update. - If a System is set inactive by something later on, no further updates will run on that System until it is reactivated.
- If
- The
"Inst_Parameters"
are very similar to Entity parameters. These are accessible throughGetInstParam<type>(string key)
(or the equivalent functions withoutInst
) inside the System, and use the Generic Parameter interface. These should be used as a means of configuring a System with state values like Timescale, current date/time, etc. Anything you can do with Entity parameters, you can do with System Instance parameters too, including nesting and EntityRef parameters.
For both Entities and Systems, initial parameter values are defined in Sim Configs and Entity Configs, which are saved to JSON-formatted files. The ParamMap JSON format holds a nested map set of Parameters and their initial values. They are a key-value pair with a string for the key and one of a few formats for the value.
{
"key": "value of some sort",
"this_is_parsed_as_double": 10,
"this2_is_parsed_as_double": 10.0,
"this3_is_parsed_as_double": 10.0e6,
"this_is_parsed_as_string": "hello",
"this_is_parsed_as_bool": true,
"this_is_parsed_as_integer": ["int32", 55],
"this_is_parsed_as_EntityRef": ["EntityRef", "NameOfOtherEntityInThisSimConfig"]
}
For high performance in the core code of Space Teams, we have decided to set limitations on parameter keys that allow us to encode them into 64-bit hash values. The first 6 characters, each with 6 bits, are reproducible from the 64-bit hash.
Valid characters for param keys are limited to (in sorting order):
#
-
0
-9
-
A
-Z
anda
-z
- interleaved to alphabetize without case sensitivity (AaBbCd...)
_
The remaining 28 bits after the 36-bit reversible section are taken up by a rolling polynomial hash of the rest of the letters in the string, which provides deterministic and mostly-unique values. This means that only the first 6 characters of a param key are guaranteed to sort properly and be unique, but typical use of Space Teams where key length is under ~16 characters is likely to never run into an issue with two different key strings hashing to the same value.
For certain common param value types, the type is inferred instead of needing to specify the type explicitly. If an integer or floating point number is used in JSON, that parses as a double
param value. If a Boolean is used in JSON, that parses as a bool
param value. If a string is used, that parses as a string
param value.
For all other parameter types, using array notation is required. The first element of the JSON array is a string with the value type. The remaining elements are used for the initial value of that type.
{
"this_is_parsed_as_integer": ["int32", 55],
"doubleV3_example": ["doubleV3", 55, 100.5, -0.2e6],
}
ParamArrays are defined by a similar syntax. For multi-element values such as vectors, nested JSON arrays are used.
{
"empty_array_example": ["array", "int32"],
"array_of_integer_example": ["array", "int32", 55],
"array_of_integer_example2": ["array", "int32", 55, 70, -4],
"array_of_doubleV3_example": ["array", "doubleV3", [55, 100.5, -0.2e6], [55, 100.5, -0.2e6], [55, 200.7, -0.2e6]]
}
An "asset" is a file that is managed by Space Teams for loading into a simulation. When referencing a filepath for another asset such as an included sim or an EntityConfig
Source
in a FromTemplate type, the path can be relative to one of a few separate asset directories. Core
assets correspond to the installation directory of Space Teams, Local
assets correspond to the C:/Users/<yourself>/AppData/Local/SimDynamX/STPro/Assets
folder, and more asset directory types may be added in the future.
- Sim Config specified by command line or sim config browsing
-
Core
top dir:Installation Dir/Compute_Server
- In sim config browsing UI, for core files, also omit
/packages
-
- Included sim configs
-
Core
top dir:Installation Dir
-
- Entity Configs / Assembly Subparts / FromTemplate Type
-
Core
top dir:Installation Dir
-
- Python and C++ System
Source
-
Core
top dir:Installation Dir/Compute_Server/packages
-
This syntax is still allowed for now (0.24.1) but is deprecated and will be removed in a future schema.
{
"DataManager": {
"SimEntity": {
"Time": {
"UseCurrentTime": false,
"Timescale": 1.0,
"dateYear": [ "int32", 2022 ],
"dateMonth": [ "int32", 10 ],
"dateDay": [ "int32", 1 ],
"dateHour": [ "int32", 0 ],
"dateMinute": [ "int32", 0 ],
"dateSecond": [ "double", 0.0 ]
}
}
}
}