Skip to content

Commit

Permalink
Merge pull request #148 from aidotse/check_cfg
Browse files Browse the repository at this point in the history
Check cfg
  • Loading branch information
gomezzz committed Feb 27, 2023
2 parents badebf3 + 471056d commit ca2d72c
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 974 deletions.
967 changes: 6 additions & 961 deletions examples/Learning_example/learning_example.ipynb

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions paseos/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from loguru import logger
from .utils.load_default_cfg import load_default_cfg
from .utils.check_cfg import check_cfg
from .paseos import PASEOS
from .actors.base_actor import BaseActor
from .actors.actor_builder import ActorBuilder
Expand Down Expand Up @@ -29,6 +30,8 @@ def init_sim(local_actor: BaseActor, cfg=None):
logger.debug("Initializing simulation.")
if cfg is None:
cfg = load_default_cfg()
else:
check_cfg(cfg)
sim = PASEOS(local_actor=local_actor, cfg=cfg)
return sim

Expand Down
14 changes: 4 additions & 10 deletions paseos/resources/default_cfg.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
[sim]
start_time = 0 # [s] Start time of the simulation in seconds after MJD2000
dt = 10 # [s] Maximal Internal timestep used for computing charging, etc.
activity_timestep = 1 # [s] Internal timestep at which activities update, try to charge and discharge etc.
start_time = 0.0 # [s] Start time of the simulation in seconds after MJD2000
dt = 10.0 # [s] Maximal Internal timestep used for computing charging, etc.
activity_timestep = 1.0 # [s] Internal timestep at which activities update, try to charge and discharge etc.
time_multiplier = 1.0 # [unitless] Defines how much real time passes

[io]
logging_interval = 10 # [s] after how many seconds should paseos log the actor status

[power]

[comms]

[radiation]
logging_interval = 10.0 # [s] after how many seconds should paseos log the actor status
17 changes: 17 additions & 0 deletions paseos/tests/default_cfg_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Tests whether the default cfg passes validation."""

import paseos
import pykep as pk
from paseos import load_default_cfg, ActorBuilder, SpacecraftActor


def test_default_cfg():
"""Load default cfg and check it."""

# Need some actor to init paseos
earth = pk.planet.jpl_lp("earth")
sat1 = ActorBuilder.get_actor_scaffold("sat1", SpacecraftActor, pk.epoch(0))
ActorBuilder.set_orbit(sat1, [10000000, 0, 0], [0, 8000.0, 0], pk.epoch(0), earth)

cfg = load_default_cfg()
_ = paseos.init_sim(sat1, cfg)
2 changes: 1 addition & 1 deletion paseos/tests/operations_monitor_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async def test_monitor():
cfg.sim.dt = 0.1 # setting lower timestep to run things quickly
cfg.sim.activity_timestep = 0.1
cfg.io.logging_interval = 0.25 # log every 0.25 seconds
cfg.sim.time_multiplier = 10 # speed up execution for convenience
cfg.sim.time_multiplier = 10.0 # speed up execution for convenience
sim = paseos.init_sim(sat1, cfg)

async def func1(args):
Expand Down
2 changes: 1 addition & 1 deletion paseos/tests/thermal_model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async def test_thermal():
cfg.sim.dt = 5.0 # setting higher timestep to run things quickly
cfg.sim.activity_timestep = 1.0
cfg.io.logging_interval = 10.0 # log every 0.25 seconds
cfg.sim.time_multiplier = 200 # speed up execution for convenience
cfg.sim.time_multiplier = 200.0 # speed up execution for convenience
sim = paseos.init_sim(sat1, cfg)

# Initial temperature is 0C / 273.15K
Expand Down
2 changes: 1 addition & 1 deletion paseos/tests/time_multiplier_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ async def test_activity():
# init simulation

cfg = load_default_cfg() # loading cfg to modify defaults
cfg.sim.time_multiplier = 10
cfg.sim.time_multiplier = 10.0
sim = paseos.init_sim(sat1, cfg)

# Initial power is 500
Expand Down
105 changes: 105 additions & 0 deletions paseos/utils/check_cfg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from dotmap import DotMap
from loguru import logger


def check_cfg(cfg: DotMap):
"""This function validates that all required entries are in the config.
Args:
cfg (DotMap): Run config you intend to use.
"""
major_categories = ["sim", "io"]
_check_for_keys(cfg, major_categories)
_check_entry_types(cfg, major_categories)
_check_value_ranges(cfg, major_categories)
logger.debug("Config validated successfully.")


def _check_for_keys(cfg: DotMap, major_categories: list) -> None:
"""Checks that all required keys are present in the config"""
required_keys = [
"start_time",
"dt",
"activity_timestep",
"time_multiplier",
"logging_interval",
]
# Check only expected categories are there that are expected
for key in cfg.keys():
if key not in major_categories:
raise KeyError(f"Found unexpected category in cfg: {key}")

# Check required keys are there
for key in required_keys:
key_found = False
for category in major_categories:
if key in cfg[category]:
key_found = True
if not key_found:
raise KeyError(f"CFG missing required key: {key}")

# Check no other keys are there (to e.g. catch typos)
for category in major_categories:
for key in cfg[category].keys():
if key not in required_keys:
raise KeyError(
f"CFG Key {key} is not a valid key. Valid are {required_keys}"
)


def _check_entry_types(cfg: DotMap, major_categories: list) -> None:
"""Check that all entries in the config are of the correct type"""
# fmt: off
integer_keys = [] # noqa
float_keys = ["start_time","dt","activity_timestep","time_multiplier","logging_interval",] # noqa
boolean_keys = [] # noqa
string_keys = [] # noqa
list_keys = []
# fmt: on

for key in integer_keys:
for category in major_categories:
if key in cfg[category].keys() and not isinstance(cfg[category][key], int):
raise TypeError(f"{key} must be an integer")

for key in float_keys:
for category in major_categories:
if key in cfg[category].keys() and not isinstance(
cfg[category][key], float
):
raise TypeError(f"{key} must be a float")

for key in boolean_keys:
for category in major_categories:
if key in cfg[category].keys() and not isinstance(cfg[category][key], bool):
raise TypeError(f"{key} must be a boolean")

for key in string_keys:
for category in major_categories:
if key in cfg[category].keys() and not isinstance(cfg[category][key], str):
raise TypeError(f"{key} must be a string")

for key in list_keys:
for category in major_categories:
if key in cfg[category].keys() and not isinstance(cfg[category][key], list):
raise TypeError(f"{key} must be a list")


def _check_value_ranges(cfg: DotMap, major_categories: list) -> None:
"""Check that all values in the config are within the correct range.
This throws runtime errors as ValueErrors are caught in training to avoid NaNs crashing the training."""

# fmt: off
positive_value_keys = ["dt","activity_timestep","time_multiplier","logging_interval",] # noqa
positive_or_zero_value_keys = ["start_time"] # noqa
# fmt: on

for key in positive_value_keys:
for category in major_categories:
if key in cfg[category].keys() and not (cfg[category][key] > 0):
raise RuntimeError(f"{key} must be a positive integer")

for key in positive_or_zero_value_keys:
for category in major_categories:
if key in cfg[category].keys() and not (cfg[category][key] >= 0):
raise RuntimeError(f"{key} must be a positive or zero integer")

0 comments on commit ca2d72c

Please sign in to comment.