diff --git a/.github/workflows/autoblack.yml b/.github/workflows/autoblack.yml deleted file mode 100644 index bbccefad..00000000 --- a/.github/workflows/autoblack.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Black Format Check - -on: [pull_request] - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: psf/black@stable - with: - options: "--check --line-length 100" - src: "." - jupyter: false - version: "23.3.0" \ No newline at end of file diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml new file mode 100644 index 00000000..e2b1df86 --- /dev/null +++ b/.github/workflows/formatting.yml @@ -0,0 +1,17 @@ +name: Ruff Lint and Format Check + +on: [pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/ruff-action@v3 + with: + args: "check" + src: "." + - uses: astral-sh/ruff-action@v3 + with: + args: "format --check" + src: "." diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index a609f667..3d5a085c 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -9,20 +9,6 @@ on: pull_request: workflow_dispatch: jobs: - lint_flake8: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.9 - uses: actions/setup-python@v2 - with: - python-version: 3.9 - - name: Lint with flake8 - run: | - pip install flake8 - flake8 . --count --show-source --statistics --max-line-length=127 --ignore=E402,W503,E203 - build: runs-on: ubuntu-latest permissions: @@ -35,11 +21,11 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.9 - - name: Search for severe code errors with flake8 + - name: Search for severe code errors with ruff run: | # stop the build if there are Python syntax errors or undefined names - pip install flake8 - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --max-line-length=127 + pip install ruff + ruff check . --select=E9,F63,F7,F82 --output-format=full - name: provision-with-micromamba uses: mamba-org/provision-with-micromamba@main with: diff --git a/docs/source/conf.py b/docs/source/conf.py index ba8ddc47..2af7d1a5 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -38,7 +38,7 @@ extensions = [ "sphinx.ext.autodoc", # for autodocs "sphinx.ext.napoleon", # for numpy style documentation - "myst_parser" # for markdown support + "myst_parser", # for markdown support # "sphinx.ext.viewcode", # "sphinx.ext.imgmath", ] diff --git a/examples/Constellation_example/constellation_example_utils.py b/examples/Constellation_example/constellation_example_utils.py index 1fb578f6..db8a4294 100644 --- a/examples/Constellation_example/constellation_example_utils.py +++ b/examples/Constellation_example/constellation_example_utils.py @@ -1,5 +1,5 @@ -import pandas as pd import numpy as np +import pandas as pd def get_known_actor_comms_status(values): diff --git a/examples/Learning_example/simple_neural_network.py b/examples/Learning_example/simple_neural_network.py index 41e867d7..0818288d 100644 --- a/examples/Learning_example/simple_neural_network.py +++ b/examples/Learning_example/simple_neural_network.py @@ -1,8 +1,8 @@ -import torch import numpy as np -from torch.utils.data import Dataset, DataLoader +import torch from sklearn.datasets import make_circles from sklearn.model_selection import train_test_split +from torch.utils.data import DataLoader, Dataset class SimpleNeuralNetwork(torch.nn.Module): diff --git a/examples/Learning_example/simple_node.py b/examples/Learning_example/simple_node.py index 8269601a..6c6a06f7 100644 --- a/examples/Learning_example/simple_node.py +++ b/examples/Learning_example/simple_node.py @@ -1,10 +1,12 @@ import asyncio + +import numpy as np import pykep as pk import torch -import numpy as np +from simple_neural_network import SimpleNeuralNetwork + import paseos from paseos import ActorBuilder, SpacecraftActor -from simple_neural_network import SimpleNeuralNetwork class Node: diff --git a/examples/MPI_example/mpi_example.py b/examples/MPI_example/mpi_example.py index a9f6ad4d..2251673b 100644 --- a/examples/MPI_example/mpi_example.py +++ b/examples/MPI_example/mpi_example.py @@ -1,11 +1,11 @@ -""" This example showcases how you can run PASEOS on a high-performance computing infrastructure +"""This example showcases how you can run PASEOS on a high-performance computing infrastructure using MPI (see also https://mpi4py.readthedocs.io/en/stable/tutorial.html). Please run the example with mpiexec -n 4 python mpi_example.py In the example, we model four satellites in low-Earth orbit that are only very infrequently in line of sight of each other. For simplicity, the modelled task here will be to count the number of -window encounters for each satellite. +window encounters for each satellite. N.B. There are some simplifications in this example. Running this on a compute cluster / supercomputer you would want to minimize communications between ranks further. For clarity, we stay simple. @@ -153,7 +153,7 @@ def constraint_func(verbose=SHOW_ALL_WINDOWS): print() print(f"########## Simulation completed ###########") print( - f"Simulation ran {end-start:.2f}s to simulate {simulation_time:.2f}s. {simulation_time / (end-start):.2f}x real-time." + f"Simulation ran {end - start:.2f}s to simulate {simulation_time:.2f}s. {simulation_time / (end - start):.2f}x real-time." ) sys.stdout.flush() # update prints to better see parallelism comm.Barrier() diff --git a/examples/MPI_example/mpi_utility_func.py b/examples/MPI_example/mpi_utility_func.py index 4952af79..4a1dd035 100644 --- a/examples/MPI_example/mpi_utility_func.py +++ b/examples/MPI_example/mpi_utility_func.py @@ -2,9 +2,11 @@ Currently actors cannot be pickled, so we have a simple function to encode them. """ -from paseos import ActorBuilder, SpacecraftActor + import pykep as pk +from paseos import ActorBuilder, SpacecraftActor + earth = pk.planet.jpl_lp("earth") # define our central body diff --git a/examples/Orekit_example/orekit_propagator.py b/examples/Orekit_example/orekit_propagator.py index 5617b843..c96d0fbd 100644 --- a/examples/Orekit_example/orekit_propagator.py +++ b/examples/Orekit_example/orekit_propagator.py @@ -1,16 +1,13 @@ -from org.orekit.orbits import KeplerianOrbit, PositionAngle -from org.orekit.time import AbsoluteDate -from org.orekit.utils import Constants -from org.orekit.frames import FramesFactory -from org.orekit.orbits import OrbitType -from org.orekit.propagation.numerical import NumericalPropagator +from orekit import JArray_double from org.hipparchus.ode.nonstiff import DormandPrince853Integrator -from org.orekit.propagation import SpacecraftState -from org.orekit.utils import IERSConventions -from org.orekit.forces.gravity.potential import GravityFieldFactory from org.orekit.forces.gravity import HolmesFeatherstoneAttractionModel - -from orekit import JArray_double +from org.orekit.forces.gravity.potential import GravityFieldFactory +from org.orekit.frames import FramesFactory +from org.orekit.orbits import KeplerianOrbit, OrbitType, PositionAngle +from org.orekit.propagation import SpacecraftState +from org.orekit.propagation.numerical import NumericalPropagator +from org.orekit.time import AbsoluteDate +from org.orekit.utils import Constants, IERSConventions class OrekitPropagator: diff --git a/examples/Sentinel_2_example_notebook/utils.py b/examples/Sentinel_2_example_notebook/utils.py index 2b6d1956..70b35dd9 100644 --- a/examples/Sentinel_2_example_notebook/utils.py +++ b/examples/Sentinel_2_example_notebook/utils.py @@ -1,6 +1,6 @@ import numpy as np -import scipy import rasterio +import scipy from skimage.measure import label, regionprops # [1] Massimetti, Francesco, et al. ""Volcanic hot-spot detection using SENTINEL-2: diff --git a/paseos/__init__.py b/paseos/__init__.py index e9025da1..ef5e38ab 100644 --- a/paseos/__init__.py +++ b/paseos/__init__.py @@ -1,22 +1,21 @@ -from loguru import logger -from dotmap import DotMap import pykep as pk +from dotmap import DotMap +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 +from .actors.base_actor import BaseActor from .actors.ground_station_actor import GroundstationActor from .actors.spacecraft_actor import SpacecraftActor from .central_body.central_body import CentralBody -from .communication.get_communication_window import get_communication_window from .communication.find_next_window import find_next_window +from .communication.get_communication_window import get_communication_window +from .paseos import PASEOS from .power.power_device_type import PowerDeviceType +from .utils.check_cfg import check_cfg +from .utils.load_default_cfg import load_default_cfg from .utils.reference_frame import ReferenceFrame from .utils.set_log_level import set_log_level -from .visualization.plot import plot, PlotType - +from .visualization.plot import PlotType, plot set_log_level("WARNING") diff --git a/paseos/activities/activity_manager.py b/paseos/activities/activity_manager.py index 6be65b96..ce14d193 100644 --- a/paseos/activities/activity_manager.py +++ b/paseos/activities/activity_manager.py @@ -1,8 +1,8 @@ -import types import asyncio +import types -from loguru import logger from dotmap import DotMap +from loguru import logger from paseos.activities.activity_processor import ActivityProcessor from paseos.activities.activity_runner import ActivityRunner @@ -25,12 +25,12 @@ def __init__( paseos_time_multiplier (float): Multiplier for the time. At 1, it is real time. """ logger.trace("Initializing ActivityManager") - assert ( - paseos_update_interval > 1e-4 - ), f"Too small paseos update interval. Should not be less than 1e-4, was {paseos_update_interval}" - assert ( - paseos_time_multiplier > 1e-4 - ), f"Too small paseos paseos_time_multiplier. Should not be less than 1e-4, was {paseos_time_multiplier}" + assert paseos_update_interval > 1e-4, ( + f"Too small paseos update interval. Should not be less than 1e-4, was {paseos_update_interval}" + ) + assert paseos_time_multiplier > 1e-4, ( + f"Too small paseos paseos_time_multiplier. Should not be less than 1e-4, was {paseos_time_multiplier}" + ) self._activities = DotMap(_dynamic=False) self._paseos_update_interval = paseos_update_interval self._paseos_time_multiplier = paseos_time_multiplier @@ -104,16 +104,15 @@ def perform_activity( constraint_func_args (list, optional): Arguments for the constraint function. Defaults to None. """ # Check if activity exists and if it already had consumption specified - assert ( - name in self._activities.keys() - ), f"Activity not found. Declared activities are {self._activities.keys()}" + assert name in self._activities.keys(), ( + f"Activity not found. Declared activities are {self._activities.keys()}" + ) activity = self._activities[name] logger.debug(f"Performing activity {activity}") - assert ( - activity.power_consumption_in_watt >= 0 - ), "Power consumption has to be positive but was specified as " + str( - activity.power_consumption_in_watt + assert activity.power_consumption_in_watt >= 0, ( + "Power consumption has to be positive but was specified as " + + str(activity.power_consumption_in_watt) ) activity_runner = ActivityRunner( diff --git a/paseos/activities/activity_processor.py b/paseos/activities/activity_processor.py index 6e712e58..8e35acf9 100644 --- a/paseos/activities/activity_processor.py +++ b/paseos/activities/activity_processor.py @@ -31,10 +31,9 @@ def __init__( """ logger.trace("Initalized ActivityProcessor.") assert update_interval > 0, "Update update_interval has to be positive." - assert ( - power_consumption_in_watt >= 0 - ), "Power consumption has to be positive but was specified as " + str( - power_consumption_in_watt + assert power_consumption_in_watt >= 0, ( + "Power consumption has to be positive but was specified as " + + str(power_consumption_in_watt) ) self._power_consumption_in_watt = power_consumption_in_watt diff --git a/paseos/activities/activity_runner.py b/paseos/activities/activity_runner.py index 0e9a6196..75947b5c 100644 --- a/paseos/activities/activity_runner.py +++ b/paseos/activities/activity_runner.py @@ -1,6 +1,6 @@ import asyncio -from contextlib import suppress import types +from contextlib import suppress from loguru import logger diff --git a/paseos/actors/actor_builder.py b/paseos/actors/actor_builder.py index 00bbccbc..9fcff96b 100644 --- a/paseos/actors/actor_builder.py +++ b/paseos/actors/actor_builder.py @@ -1,19 +1,20 @@ -from typing import Callable, Any +from typing import Any, Callable -from loguru import logger import numpy as np -from dotmap import DotMap import pykep as pk +from dotmap import DotMap +from loguru import logger from skyfield.api import wgs84 -from .base_actor import BaseActor -from .spacecraft_actor import SpacecraftActor -from .ground_station_actor import GroundstationActor +from paseos.geometric_model.geometric_model import GeometricModel + from ..central_body.central_body import CentralBody -from ..thermal.thermal_model import ThermalModel from ..power.power_device_type import PowerDeviceType from ..radiation.radiation_model import RadiationModel -from paseos.geometric_model.geometric_model import GeometricModel +from ..thermal.thermal_model import ThermalModel +from .base_actor import BaseActor +from .ground_station_actor import GroundstationActor +from .spacecraft_actor import SpacecraftActor class ActorBuilder: @@ -43,12 +44,12 @@ def get_actor_scaffold(name: str, actor_type: object, epoch: pk.epoch): Returns: Created actor """ - assert ( - actor_type != BaseActor - ), "BaseActor cannot be initiated. Please use SpacecraftActor or GroundstationActor" - assert ( - actor_type == SpacecraftActor or actor_type == GroundstationActor - ), f"Unsupported actor_type {actor_type}, Please use SpacecraftActor or GroundstationActor." + assert actor_type != BaseActor, ( + "BaseActor cannot be initiated. Please use SpacecraftActor or GroundstationActor" + ) + assert actor_type == SpacecraftActor or actor_type == GroundstationActor, ( + f"Unsupported actor_type {actor_type}, Please use SpacecraftActor or GroundstationActor." + ) logger.trace(f"Creating an actor blueprint with name {name}") @@ -76,9 +77,9 @@ def set_ground_station_location( """ assert latitude >= -90 and latitude <= 90, "Latitude is -90 <= lat <= 90" assert longitude >= -180 and longitude <= 180, "Longitude is -180 <= lat <= 180" - assert ( - minimum_altitude_angle >= 0 and minimum_altitude_angle <= 90 - ), "0 <= minimum_altitude_angle <= 90." + assert minimum_altitude_angle >= 0 and minimum_altitude_angle <= 90, ( + "0 <= minimum_altitude_angle <= 90." + ) actor._skyfield_position = wgs84.latlon( latitude_degrees=latitude, longitude_degrees=longitude, @@ -115,9 +116,9 @@ def set_central_body( the central body's inertial frame. Rotation at current actor local time is presumed to be 0. rotation_period (float): Rotation period in seconds. Rotation at current actor local time is presumed to be 0. """ - assert isinstance( - actor, SpacecraftActor - ), "Central body only supported for SpacecraftActors" + assert isinstance(actor, SpacecraftActor), ( + "Central body only supported for SpacecraftActors" + ) # Fuzzy type check for pykep planet assert "pykep.planet" in str(type(pykep_planet)), "pykep_planet has to be a pykep planet." @@ -126,13 +127,13 @@ def set_central_body( # Check rotation parameters if rotation_declination is not None: - assert ( - rotation_declination >= -90 and rotation_declination <= 90 - ), "Rotation declination has to be -90 <= dec <= 90" + assert rotation_declination >= -90 and rotation_declination <= 90, ( + "Rotation declination has to be -90 <= dec <= 90" + ) if rotation_right_ascension is not None: - assert ( - rotation_right_ascension >= -180 and rotation_right_ascension <= 180 - ), "Rotation right ascension has to be -180 <= ra <= 180" + assert rotation_right_ascension >= -180 and rotation_right_ascension <= 180, ( + "Rotation right ascension has to be -180 <= ra <= 180" + ) if rotation_period is not None: assert rotation_period > 0, "Rotation period has to be > 0" @@ -142,12 +143,12 @@ def set_central_body( or rotation_right_ascension is not None or rotation_declination is not None ): - assert ( - rotation_right_ascension is not None - ), "Rotation right ascension has to be set for rotation." - assert ( - rotation_declination is not None - ), "Rotation declination has to be set. for rotation." + assert rotation_right_ascension is not None, ( + "Rotation right ascension has to be set for rotation." + ) + assert rotation_declination is not None, ( + "Rotation declination has to be set. for rotation." + ) assert rotation_period is not None, "Rotation period has to be set for rotation." assert mesh is not None, "Radius cannot only be set for mesh-defined bodies." @@ -158,9 +159,9 @@ def set_central_body( assert isinstance(mesh[0], np.ndarray), "Mesh vertices have to be a numpy array." assert isinstance(mesh[1], np.ndarray), "Mesh triangles have to be a numpy array." assert len(mesh[0].shape) == 2, "Mesh vertices have to be a numpy array of shape (n,3)." - assert ( - len(mesh[1].shape) == 2 - ), "Mesh triangles have to be a numpy array of shape (n,3)." + assert len(mesh[1].shape) == 2, ( + "Mesh triangles have to be a numpy array of shape (n,3)." + ) # Check if pykep planet is either orbiting the sun or is the sunitself # by comparing mu values @@ -203,22 +204,22 @@ def set_custom_orbit(actor: SpacecraftActor, propagator_func: Callable, epoch: p assert isinstance(epoch, pk.epoch), "epoch has to be a pykep epoch." assert isinstance(actor, SpacecraftActor), "Orbit only supported for SpacecraftActors" assert actor._orbital_parameters is None, "Actor already has an orbit." - assert np.isclose( - actor.local_time.mjd2000, epoch.mjd2000 - ), "The initial epoch has to match actor's local time." + assert np.isclose(actor.local_time.mjd2000, epoch.mjd2000), ( + "The initial epoch has to match actor's local time." + ) actor._custom_orbit_propagator = propagator_func # Try evaluating position and velocity to check if the function works try: position, velocity = actor.get_position_velocity(epoch) assert len(position) == 3, "Position has to be list of 3 floats." - assert all( - [isinstance(val, float) for val in position] - ), "Position has to be list of 3 floats." + assert all([isinstance(val, float) for val in position]), ( + "Position has to be list of 3 floats." + ) assert len(velocity) == 3, "Velocity has to be list of 3 floats." - assert all( - [isinstance(val, float) for val in velocity] - ), "Velocity has to be list of 3 floats." + assert all([isinstance(val, float) for val in velocity]), ( + "Velocity has to be list of 3 floats." + ) except Exception as e: logger.error(f"Error evaluating custom orbit propagator function: {e}") raise RuntimeError("Error evaluating custom orbit propagator function.") @@ -308,14 +309,14 @@ def set_position(actor: BaseActor, position: list): actor (BaseActor): Actor set the position on. position (list): [x,y,z] position for SpacecraftActor. """ - assert not isinstance( - actor, GroundstationActor - ), "Position changing not supported for GroundstationActors" + assert not isinstance(actor, GroundstationActor), ( + "Position changing not supported for GroundstationActors" + ) assert len(position) == 3, "Position has to be list of 3 floats." - assert all( - [isinstance(val, float) for val in position] - ), "Position has to be list of 3 floats." + assert all([isinstance(val, float) for val in position]), ( + "Position has to be list of 3 floats." + ) actor._position = position logger.debug(f"Setting position {position} on actor {actor}") @@ -368,9 +369,9 @@ def set_power_devices( """ # check for spacecraft actor - assert isinstance( - actor, SpacecraftActor - ), "Power devices are only supported for SpacecraftActors" + assert isinstance(actor, SpacecraftActor), ( + "Power devices are only supported for SpacecraftActors" + ) # If solar panel, check if the actor has a central body # to check eclipse @@ -421,9 +422,9 @@ def set_radiation_model( failure_events_per_s (float): Complete device failure, events per second, i.e. a Single Event Latch-Up (SEL). """ # check for spacecraft actor - assert isinstance( - actor, SpacecraftActor - ), "Radiation models are only supported for SpacecraftActors" + assert isinstance(actor, SpacecraftActor), ( + "Radiation models are only supported for SpacecraftActors" + ) assert data_corruption_events_per_s >= 0, "data_corruption_events_per_s cannot be negative." assert restart_events_per_s >= 0, "restart_events_per_s cannot be negative." @@ -478,9 +479,9 @@ def set_thermal_model( 0 leads to know heat-up due to activity. Defaults to 0.5. """ # check for spacecraft actor - assert isinstance( - actor, SpacecraftActor - ), "Thermal models are only supported for SpacecraftActors" + assert isinstance(actor, SpacecraftActor), ( + "Thermal models are only supported for SpacecraftActors" + ) # Check if the actor already had a thermal model if actor.has_thermal_model: @@ -490,18 +491,18 @@ def set_thermal_model( assert actor_mass > 0, "Actor mass has to be positive." - assert ( - 0 <= power_consumption_to_heat_ratio and power_consumption_to_heat_ratio <= 1.0 - ), "Heat ratio has to be 0 to 1." + assert 0 <= power_consumption_to_heat_ratio and power_consumption_to_heat_ratio <= 1.0, ( + "Heat ratio has to be 0 to 1." + ) logger.trace("Checking actor thermal values for sensibility.") assert 0 <= actor_initial_temperature_in_K, "Actor initial temperature cannot be below 0K." - assert ( - 0 <= actor_sun_absorptance and actor_sun_absorptance <= 1.0 - ), "Absorptance has to be 0 to 1." - assert ( - 0 <= actor_infrared_absorptance and actor_infrared_absorptance <= 1.0 - ), "Absorptance has to be 0 to 1." + assert 0 <= actor_sun_absorptance and actor_sun_absorptance <= 1.0, ( + "Absorptance has to be 0 to 1." + ) + assert 0 <= actor_infrared_absorptance and actor_infrared_absorptance <= 1.0, ( + "Absorptance has to be 0 to 1." + ) assert 0 < actor_sun_facing_area, "Sun-facing area has to be > 0." assert 0 < actor_central_body_facing_area, "Body-facing area has to be > 0." assert 0 < actor_emissive_area, "Actor emissive area has to be > 0." @@ -511,9 +512,9 @@ def set_thermal_model( assert 0 < body_solar_irradiance, "Solar irradiance has to be > 0." assert 0 <= body_surface_temperature_in_K, "Body surface temperature cannot be below 0K." assert 0 <= body_emissivity and body_emissivity <= 1.0, "Body emissivity has to be 0 to 1" - assert ( - 0 <= body_reflectance and body_reflectance <= 1.0 - ), "Body reflectance has to be 0 to 1" + assert 0 <= body_reflectance and body_reflectance <= 1.0, ( + "Body reflectance has to be 0 to 1" + ) actor._mass = actor_mass actor._thermal_model = ThermalModel( diff --git a/paseos/actors/base_actor.py b/paseos/actors/base_actor.py index bae022cd..643c6420 100644 --- a/paseos/actors/base_actor.py +++ b/paseos/actors/base_actor.py @@ -1,13 +1,13 @@ from abc import ABC -from typing import Callable, Any +from typing import Any, Callable -from loguru import logger -import pykep as pk import numpy as np +import pykep as pk from dotmap import DotMap +from loguru import logger -from ..central_body.is_in_line_of_sight import is_in_line_of_sight from ..central_body.central_body import CentralBody +from ..central_body.is_in_line_of_sight import is_in_line_of_sight class BaseActor(ABC): diff --git a/paseos/actors/ground_station_actor.py b/paseos/actors/ground_station_actor.py index 6055f02c..0a35ac52 100644 --- a/paseos/actors/ground_station_actor.py +++ b/paseos/actors/ground_station_actor.py @@ -1,5 +1,5 @@ -from loguru import logger import pykep as pk +from loguru import logger from skyfield.api import load from paseos.actors.base_actor import BaseActor diff --git a/paseos/actors/spacecraft_actor.py b/paseos/actors/spacecraft_actor.py index 30f75272..1e3b4d1a 100644 --- a/paseos/actors/spacecraft_actor.py +++ b/paseos/actors/spacecraft_actor.py @@ -1,9 +1,8 @@ -from loguru import logger import pykep as pk +from loguru import logger from paseos.actors.base_actor import BaseActor -from paseos.power import discharge_model -from paseos.power import charge_model +from paseos.power import charge_model, discharge_model class SpacecraftActor(BaseActor): @@ -157,9 +156,9 @@ def charge(self, duration_in_s: float): duration_in_s (float): How long the activity is performed in seconds """ logger.debug(f"Charging actor {self} for {duration_in_s}s.") - assert ( - duration_in_s > 0 - ), "Charging interval has to be positive but t1 was less or equal t0." + assert duration_in_s > 0, ( + "Charging interval has to be positive but t1 was less or equal t0." + ) self = charge_model.charge(self, duration_in_s) diff --git a/paseos/central_body/central_body.py b/paseos/central_body/central_body.py index d844525f..f6d9049f 100644 --- a/paseos/central_body/central_body.py +++ b/paseos/central_body/central_body.py @@ -1,15 +1,15 @@ """This file serves to collect functionality related to central bodies.""" -from math import radians, pi +from math import pi, radians +import numpy as np +import pykep as pk from loguru import logger from pyquaternion import Quaternion from skspatial.objects import Sphere -import pykep as pk -import numpy as np -from paseos.central_body.sphere_between_points import sphere_between_points from paseos.central_body.mesh_between_points import mesh_between_points +from paseos.central_body.sphere_between_points import sphere_between_points from paseos.utils.reference_frame import ReferenceFrame diff --git a/paseos/central_body/is_in_line_of_sight.py b/paseos/central_body/is_in_line_of_sight.py index e66230bd..756f5f31 100644 --- a/paseos/central_body/is_in_line_of_sight.py +++ b/paseos/central_body/is_in_line_of_sight.py @@ -1,9 +1,10 @@ -from loguru import logger -import pykep as pk import os + import numpy as np -from skyfield.units import AU_M +import pykep as pk +from loguru import logger from skyfield.api import load +from skyfield.units import AU_M from skyfield.vectorlib import VectorFunction _SKYFIELD_EARTH_PATH = os.path.join(os.path.dirname(__file__) + "/../resources/", "de421.bsp") @@ -46,9 +47,9 @@ def _is_in_line_of_sight_spacecraft_to_spacecraft(actor, other_actor, epoch: pk. bool: true if in line-of-sight. """ # Check actor has central body - assert ( - actor.central_body is not None - ), f"Please set the central body on actor {actor} for line of sight computations." + assert actor.central_body is not None, ( + f"Please set the central body on actor {actor} for line of sight computations." + ) return not actor.central_body.is_between_actors(actor, other_actor, epoch, plot) @@ -73,9 +74,9 @@ def _is_in_line_of_sight_ground_station_to_spacecraft( Returns: bool: true if in line-of-sight. """ - assert ( - minimum_altitude_angle < 90 and minimum_altitude_angle > 0 - ), "0 < Minimum altitude angle < 90" + assert minimum_altitude_angle < 90 and minimum_altitude_angle > 0, ( + "0 < Minimum altitude angle < 90" + ) logger.debug( "Computing line of sight between actors: " + str(ground_station) + " " + str(spacecraft) @@ -103,8 +104,8 @@ def _is_in_line_of_sight_ground_station_to_spacecraft( # Plot if requested if plot: - from skspatial.plotting import plot_3d from skspatial.objects import Line, Point + from skspatial.plotting import plot_3d def plot(gs_pos_t, sat_pos_t, t): # Converting to geocentric @@ -155,9 +156,9 @@ def is_in_line_of_sight( type(actor).__name__ == "SpacecraftActor" and type(other_actor).__name__ == "SpacecraftActor" ): - assert ( - actor.central_body is not None - ), f"Please set the central body on actor {actor} for line of sight computations." + assert actor.central_body is not None, ( + f"Please set the central body on actor {actor} for line of sight computations." + ) return _is_in_line_of_sight_spacecraft_to_spacecraft(actor, other_actor, epoch, plot) elif ( type(actor).__name__ == "GroundstationActor" @@ -165,9 +166,9 @@ def is_in_line_of_sight( ): if minimum_altitude_angle is None: minimum_altitude_angle = actor._minimum_altitude_angle - assert ( - other_actor.central_body.planet.name.lower() == "earth" - ), f"Ground stations can only be used with Earth for now (not {other_actor.central_body.planet.name})." + assert other_actor.central_body.planet.name.lower() == "earth", ( + f"Ground stations can only be used with Earth for now (not {other_actor.central_body.planet.name})." + ) return _is_in_line_of_sight_ground_station_to_spacecraft( actor, other_actor, epoch, minimum_altitude_angle, plot ) diff --git a/paseos/central_body/sphere_between_points.py b/paseos/central_body/sphere_between_points.py index 86497333..8e960316 100644 --- a/paseos/central_body/sphere_between_points.py +++ b/paseos/central_body/sphere_between_points.py @@ -1,5 +1,5 @@ -from skspatial.objects import Sphere, LineSegment, Line, Point from loguru import logger +from skspatial.objects import Line, LineSegment, Point, Sphere def sphere_between_points(point_1, point_2, sphere: Sphere, plot=False) -> bool: diff --git a/paseos/communication/find_next_window.py b/paseos/communication/find_next_window.py index 1a5ed688..d9bc5853 100644 --- a/paseos/communication/find_next_window.py +++ b/paseos/communication/find_next_window.py @@ -1,8 +1,8 @@ import pykep as pk from loguru import logger -from .get_communication_window import get_communication_window from ..actors.base_actor import BaseActor +from .get_communication_window import get_communication_window def find_next_window( diff --git a/paseos/geometric_model/geometric_model.py b/paseos/geometric_model/geometric_model.py index 7cd92162..2fbbea2d 100644 --- a/paseos/geometric_model/geometric_model.py +++ b/paseos/geometric_model/geometric_model.py @@ -1,5 +1,5 @@ -from loguru import logger import trimesh +from loguru import logger class GeometricModel: diff --git a/paseos/paseos.py b/paseos/paseos.py index 94661f00..d842528d 100644 --- a/paseos/paseos.py +++ b/paseos/paseos.py @@ -1,13 +1,13 @@ -import types import asyncio import sys +import types +import pykep as pk from dotmap import DotMap from loguru import logger -import pykep as pk -from paseos.actors.base_actor import BaseActor from paseos.activities.activity_manager import ActivityManager +from paseos.actors.base_actor import BaseActor from paseos.utils.operations_monitor import OperationsMonitor @@ -98,9 +98,9 @@ def advance_time( float: Time remaining to advance (or 0 if done) """ - assert ( - not self._is_advancing_time - ), "advance_time is already running. This function is not thread-safe. Avoid mixing (async) activities and calling it." + assert not self._is_advancing_time, ( + "advance_time is already running. This function is not thread-safe. Avoid mixing (async) activities and calling it." + ) self._is_advancing_time = True assert time_to_advance > 0, "Time to advance has to be positive." @@ -108,9 +108,9 @@ def advance_time( # Check constraint function returns something if constraint_function is not None: - assert ( - constraint_function() is not None - ), "Your constraint function failed to return True or False." + assert constraint_function() is not None, ( + "Your constraint function failed to return True or False." + ) logger.debug("Advancing time by " + str(time_to_advance) + " s.") target_time = self._state.time + time_to_advance @@ -299,9 +299,9 @@ def remove_known_actor(self, actor_name: str): Args: actor_name (str): name of the actor to remove. """ - assert ( - actor_name in self.known_actors - ), f"Actor {actor_name} is not in known. Available are {self.known_actors.keys()}" + assert actor_name in self.known_actors, ( + f"Actor {actor_name} is not in known. Available are {self.known_actors.keys()}" + ) del self._known_actors[actor_name] def remove_activity(self, name: str): diff --git a/paseos/power/charge_model.py b/paseos/power/charge_model.py index 41088dd0..4e851206 100644 --- a/paseos/power/charge_model.py +++ b/paseos/power/charge_model.py @@ -1,6 +1,7 @@ """This file contains models of the battery charge via e.g. solar power""" -from loguru import logger + import pykep as pk +from loguru import logger from paseos.power.power_device_type import PowerDeviceType diff --git a/paseos/radiation/radiation_model.py b/paseos/radiation/radiation_model.py index 7c360863..eb911cb1 100644 --- a/paseos/radiation/radiation_model.py +++ b/paseos/radiation/radiation_model.py @@ -57,7 +57,7 @@ def _sample_poisson_process(events_per_s, interval_in_s): ) logger.trace(f"poisson_prob={poisson_prob}") sample = np.random.rand() - logger.trace(f"sample ={sample }") + logger.trace(f"sample ={sample}") return sample < poisson_prob def model_data_corruption(self, data_shape: list, exposure_period_in_s: float): diff --git a/paseos/tests/activity_test.py b/paseos/tests/activity_test.py index 969b07b2..acafa461 100644 --- a/paseos/tests/activity_test.py +++ b/paseos/tests/activity_test.py @@ -1,9 +1,11 @@ """Simple test of starting an activity""" -from test_utils import get_default_instance, wait_for_activity -from paseos import SpacecraftActor import asyncio + import pytest +from test_utils import get_default_instance, wait_for_activity + +from paseos import SpacecraftActor # Below test can be used to check what happens when you formulate an invalid constraint function. # It is temporarily commented out as it doesn't really check right now because I could not figure diff --git a/paseos/tests/actor_builder_test.py b/paseos/tests/actor_builder_test.py index 7dafab5b..ff5b117e 100644 --- a/paseos/tests/actor_builder_test.py +++ b/paseos/tests/actor_builder_test.py @@ -1,14 +1,16 @@ """Tests to see if we can create satellites with different devices.""" + +import sys + import numpy as np import pykep as pk -import sys sys.path.append("../..") -from paseos import ActorBuilder, SpacecraftActor - from test_utils import get_default_instance +from paseos import ActorBuilder, SpacecraftActor + def test_set_TLE(): """Check if we can set a TLE correctly""" diff --git a/paseos/tests/communication_window_test.py b/paseos/tests/communication_window_test.py index 8b1fc525..bf03f2b4 100644 --- a/paseos/tests/communication_window_test.py +++ b/paseos/tests/communication_window_test.py @@ -1,15 +1,16 @@ """Test to check the communication function(s)""" + +import numpy as np +import pykep as pk + from paseos import ( - SpacecraftActor, - GroundstationActor, ActorBuilder, + GroundstationActor, + SpacecraftActor, find_next_window, get_communication_window, ) -import pykep as pk -import numpy as np - def setup_sentinel_example(t0): """Sets up the example with sentinel2B and maspolamas ground station.""" diff --git a/paseos/tests/custom_propagator_test.py b/paseos/tests/custom_propagator_test.py index 293c12c6..f236154a 100644 --- a/paseos/tests/custom_propagator_test.py +++ b/paseos/tests/custom_propagator_test.py @@ -1,11 +1,13 @@ """Test the ability to use a custom propagator in a paseos simulation.""" + import sys sys.path.append("../..") import numpy as np import pykep as pk -from paseos import SpacecraftActor, ActorBuilder + +from paseos import ActorBuilder, SpacecraftActor def test_custom_propagator(): diff --git a/paseos/tests/custom_property_test.py b/paseos/tests/custom_property_test.py index 839ee312..a5bba55f 100644 --- a/paseos/tests/custom_property_test.py +++ b/paseos/tests/custom_property_test.py @@ -1,13 +1,12 @@ """This test checks whether power charging is performed correctly""" +import numpy as np +import pykep as pk from test_utils import get_default_instance import paseos from paseos import ActorBuilder, SpacecraftActor -import pykep as pk -import numpy as np - def test_custom_power_consumption_property(): """Checks whether we can create a custom property to track power consumption of an actor""" diff --git a/paseos/tests/default_cfg_test.py b/paseos/tests/default_cfg_test.py index f1205da8..331784d7 100644 --- a/paseos/tests/default_cfg_test.py +++ b/paseos/tests/default_cfg_test.py @@ -1,8 +1,9 @@ """Tests whether the default cfg passes validation.""" -import paseos import pykep as pk -from paseos import load_default_cfg, ActorBuilder, SpacecraftActor + +import paseos +from paseos import ActorBuilder, SpacecraftActor, load_default_cfg def test_default_cfg(): diff --git a/paseos/tests/eclipse_test.py b/paseos/tests/eclipse_test.py index 784b530c..658883f3 100644 --- a/paseos/tests/eclipse_test.py +++ b/paseos/tests/eclipse_test.py @@ -1,10 +1,10 @@ """Test to check the eclipse function(s)""" + import sys sys.path.append("../..") import pykep as pk - from test_utils import get_default_instance diff --git a/paseos/tests/line_of_sight_test.py b/paseos/tests/line_of_sight_test.py index f5fc9b89..fca60224 100644 --- a/paseos/tests/line_of_sight_test.py +++ b/paseos/tests/line_of_sight_test.py @@ -1,15 +1,15 @@ """Tests to check line of sight computations.""" + import sys sys.path.append("../..") -from paseos import SpacecraftActor, ActorBuilder, GroundstationActor -from paseos.central_body.is_in_line_of_sight import is_in_line_of_sight - import pykep as pk - from test_utils import get_default_instance +from paseos import ActorBuilder, GroundstationActor, SpacecraftActor +from paseos.central_body.is_in_line_of_sight import is_in_line_of_sight + def test_los_between_sats(): """create satellites where sat1 and 2 are in sight of each other (as well as sat 1 and 3) diff --git a/paseos/tests/mesh_test.py b/paseos/tests/mesh_test.py index 974b395e..f2e9ce2c 100644 --- a/paseos/tests/mesh_test.py +++ b/paseos/tests/mesh_test.py @@ -1,11 +1,12 @@ """Test using a mesh for the central body.""" -import numpy as np import pickle + +import numpy as np import pykep as pk -from paseos import ActorBuilder, SpacecraftActor import paseos +from paseos import ActorBuilder, SpacecraftActor mesh_path = "paseos/tests/test_data/67P_low_poly.pk" diff --git a/paseos/tests/operations_monitor_test.py b/paseos/tests/operations_monitor_test.py index 0622f5fc..2e4463c3 100644 --- a/paseos/tests/operations_monitor_test.py +++ b/paseos/tests/operations_monitor_test.py @@ -1,10 +1,11 @@ """Simple test for the operations monitor""" import asyncio -import pytest -import pykep as pk +import pykep as pk +import pytest from test_utils import wait_for_activity + import paseos from paseos import ActorBuilder, SpacecraftActor, load_default_cfg diff --git a/paseos/tests/power_test.py b/paseos/tests/power_test.py index c85cc23a..80240719 100644 --- a/paseos/tests/power_test.py +++ b/paseos/tests/power_test.py @@ -1,12 +1,11 @@ """This test checks whether power charging is performed correctly""" +import pykep as pk from test_utils import get_default_instance import paseos from paseos import ActorBuilder, SpacecraftActor -import pykep as pk - def test_power_charging(): """Checks whether we can charge an actor""" diff --git a/paseos/tests/radiation_test.py b/paseos/tests/radiation_test.py index b5fd5993..f4deeae0 100644 --- a/paseos/tests/radiation_test.py +++ b/paseos/tests/radiation_test.py @@ -1,13 +1,14 @@ """This test checks whether power charging is performed correctly""" + import asyncio import numpy as np import pytest - from test_utils import get_default_instance, wait_for_activity -from paseos.radiation.radiation_model import RadiationModel -from paseos import ActorBuilder + import paseos +from paseos import ActorBuilder +from paseos.radiation.radiation_model import RadiationModel def test_radiation_model(): diff --git a/paseos/tests/test_utils.py b/paseos/tests/test_utils.py index fa04da63..d01c8835 100644 --- a/paseos/tests/test_utils.py +++ b/paseos/tests/test_utils.py @@ -1,14 +1,15 @@ """Utility for tests""" -import sys + import asyncio +import sys sys.path.append("../..") +import pykep as pk + import paseos from paseos import ActorBuilder, SpacecraftActor -import pykep as pk - def get_default_instance() -> (paseos.PASEOS, SpacecraftActor, pk.planet): """Sets up a instance of paseos with a satellite in orbit around Earth diff --git a/paseos/tests/thermal_model_test.py b/paseos/tests/thermal_model_test.py index 0744a6e3..b6af5a1d 100644 --- a/paseos/tests/thermal_model_test.py +++ b/paseos/tests/thermal_model_test.py @@ -1,11 +1,13 @@ """Simple test of the thermal model to see if temperatures evolve as expected""" -import pykep as pk -from test_utils import wait_for_activity -import paseos -from paseos import SpacecraftActor, ActorBuilder, load_default_cfg import asyncio + +import pykep as pk import pytest +from test_utils import wait_for_activity + +import paseos +from paseos import ActorBuilder, SpacecraftActor, load_default_cfg # tell pytest to create an event loop and execute the tests using the event loop diff --git a/paseos/tests/time_multiplier_test.py b/paseos/tests/time_multiplier_test.py index 1623a5ab..3ae6a815 100644 --- a/paseos/tests/time_multiplier_test.py +++ b/paseos/tests/time_multiplier_test.py @@ -1,10 +1,11 @@ """Simple test of modifying rate of time passing""" import asyncio -import pytest -import pykep as pk +import pykep as pk +import pytest from test_utils import wait_for_activity + import paseos from paseos import ActorBuilder, SpacecraftActor, load_default_cfg diff --git a/paseos/tests/visualization_test.py b/paseos/tests/visualization_test.py index 100156b7..6e9136bf 100644 --- a/paseos/tests/visualization_test.py +++ b/paseos/tests/visualization_test.py @@ -1,12 +1,14 @@ """Tests to check visualization.""" + import sys sys.path.append("../..") +import pykep as pk +from test_utils import get_default_instance + from paseos import ActorBuilder, SpacecraftActor from paseos.visualization.space_animation import SpaceAnimation -from test_utils import get_default_instance -import pykep as pk def test_animation(): diff --git a/paseos/utils/load_default_cfg.py b/paseos/utils/load_default_cfg.py index 3459b42f..e845b5e3 100644 --- a/paseos/utils/load_default_cfg.py +++ b/paseos/utils/load_default_cfg.py @@ -1,4 +1,5 @@ import os + import toml from dotmap import DotMap from loguru import logger diff --git a/paseos/utils/operations_monitor.py b/paseos/utils/operations_monitor.py index 51850e47..6bcbf68b 100644 --- a/paseos/utils/operations_monitor.py +++ b/paseos/utils/operations_monitor.py @@ -1,9 +1,9 @@ import csv -from loguru import logger -from dotmap import DotMap -import pykep as pk import matplotlib.pyplot as plt +import pykep as pk +from dotmap import DotMap +from loguru import logger from paseos.actors.base_actor import BaseActor @@ -37,17 +37,17 @@ def __getitem__(self, item): item (str): Name of item. Available are "timesteps","current_activity","state_of_charge", "is_in_eclipse","known_actors","position","velocity","temperature" """ - assert item in ( - list(self._log.keys()) + list(self._log.custom_properties.keys()) - ), f"Untracked quantity. Available are {self._log.keys() + self._log.custom_properties.keys()}" + assert item in (list(self._log.keys()) + list(self._log.custom_properties.keys())), ( + f"Untracked quantity. Available are {self._log.keys() + self._log.custom_properties.keys()}" + ) if item in self._log.custom_properties.keys(): return self._log.custom_properties[item] return self._log[item] def plot(self, item): - assert item in ( - list(self._log.keys()) + list(self._log.custom_properties.keys()) - ), f"Untracked quantity. Available are {self._log.keys() + self._log.custom_properties.keys()}" + assert item in (list(self._log.keys()) + list(self._log.custom_properties.keys())), ( + f"Untracked quantity. Available are {self._log.keys() + self._log.custom_properties.keys()}" + ) if item in self._log.custom_properties.keys(): values = self._log.custom_properties[item] else: diff --git a/paseos/utils/set_log_level.py b/paseos/utils/set_log_level.py index 4b8c9aa2..d09e7692 100644 --- a/paseos/utils/set_log_level.py +++ b/paseos/utils/set_log_level.py @@ -1,6 +1,7 @@ -from loguru import logger import sys +from loguru import logger + def set_log_level(log_level: str): """Set the log level for the logger. diff --git a/paseos/visualization/animation.py b/paseos/visualization/animation.py index 1be4e97d..e77db9b3 100644 --- a/paseos/visualization/animation.py +++ b/paseos/visualization/animation.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod + from paseos.paseos import PASEOS diff --git a/paseos/visualization/space_animation.py b/paseos/visualization/space_animation.py index 013bcada..6355ee14 100644 --- a/paseos/visualization/space_animation.py +++ b/paseos/visualization/space_animation.py @@ -1,16 +1,17 @@ -import numpy as np -from dotmap import DotMap from typing import List -import matplotlib.pyplot as plt + import matplotlib.animation as animation +import matplotlib.pyplot as plt +import numpy as np +from dotmap import DotMap +from loguru import logger from matplotlib.artist import Artist from matplotlib.colors import LinearSegmentedColormap from mpl_toolkits.axes_grid1 import make_axes_locatable -from loguru import logger from paseos.actors.base_actor import BaseActor -from paseos.actors.spacecraft_actor import SpacecraftActor from paseos.actors.ground_station_actor import GroundstationActor +from paseos.actors.spacecraft_actor import SpacecraftActor from paseos.paseos import PASEOS from paseos.visualization.animation import Animation @@ -203,7 +204,7 @@ def _populate_textbox(self, actor: BaseActor) -> str: info_str += f"\nBattery: {battery_level:.0f}%" if actor.has_thermal_model: - info_str += f"\nTemp.: {actor.temperature_in_K-273.15:.2f}C" + info_str += f"\nTemp.: {actor.temperature_in_K - 273.15:.2f}C" # Disabled for now as fixed values atm # for name in actor.communication_devices.keys(): diff --git a/pyproject.toml b/pyproject.toml index 6dbe800c..6b4d42c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,11 @@ namespaces = true [tool.ruff] line-length = 100 target-version = "py38" +# Notebooks were never formatted by the previous black setup (jupyter: false); +# keep them out of ruff to avoid churning the example/tutorial notebooks. +# Markdown is excluded too: newer ruff reformats python code blocks inside +# .md files, which would churn the hand-written README/docs snippets. +extend-exclude = ["*.ipynb", "*.md"] [tool.ruff.lint] select = ["E", "F", "W", "I"]