diff --git a/.github/workflows/autoblack.yml b/.github/workflows/autoblack.yml new file mode 100644 index 00000000..bbccefad --- /dev/null +++ b/.github/workflows/autoblack.yml @@ -0,0 +1,15 @@ +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/examples/Constellation_example/constellation_example_utils.py b/examples/Constellation_example/constellation_example_utils.py index a75d86b2..1fb578f6 100644 --- a/examples/Constellation_example/constellation_example_utils.py +++ b/examples/Constellation_example/constellation_example_utils.py @@ -18,7 +18,6 @@ def get_closest_entry(df, t, id): def get_analysis_df(df, timestep=60, orbital_period=1): - t = np.round(np.linspace(0, df.Time.max(), int(df.Time.max() // timestep))) sats = df.ID.unique() df["known_actors"] = pd.Categorical(df.known_actors) diff --git a/examples/Learning_example/simple_neural_network.py b/examples/Learning_example/simple_neural_network.py index 5cb7f301..41e867d7 100644 --- a/examples/Learning_example/simple_neural_network.py +++ b/examples/Learning_example/simple_neural_network.py @@ -57,9 +57,7 @@ def __len__(self): # Instantiate training and test data X, y = make_circles(n_samples=10000, noise=0.05, random_state=26) - X_train, X_test, y_train, y_test = train_test_split( - X, y, test_size=0.33, random_state=26 - ) + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=26) # divide the training set so none of the peers have the same data if self.node_id == 1: @@ -75,12 +73,8 @@ def __len__(self): # Create dataloaders train_data = Data(X_train, y_train) test_data = Data(X_test, y_test) - self.train_dataloader = DataLoader( - dataset=train_data, batch_size=64, shuffle=True - ) - self.test_dataloader = DataLoader( - dataset=test_data, batch_size=64, shuffle=True - ) + self.train_dataloader = DataLoader(dataset=train_data, batch_size=64, shuffle=True) + self.test_dataloader = DataLoader(dataset=test_data, batch_size=64, shuffle=True) def forward(self, x): """Do inference on model diff --git a/examples/Learning_example/simple_node.py b/examples/Learning_example/simple_node.py index 33eb70a7..8269601a 100644 --- a/examples/Learning_example/simple_node.py +++ b/examples/Learning_example/simple_node.py @@ -13,13 +13,10 @@ class Node: """ def __init__(self, node_id, pos_and_vel, paseos_cfg, power_consumption_in_watt): - # Create PASEOS instance to node earth = pk.planet.jpl_lp("earth") self.node_id = node_id - sat = ActorBuilder.get_actor_scaffold( - f"sat{node_id}", SpacecraftActor, pk.epoch(0) - ) + sat = ActorBuilder.get_actor_scaffold(f"sat{node_id}", SpacecraftActor, pk.epoch(0)) ActorBuilder.set_orbit(sat, pos_and_vel[0], pos_and_vel[1], pk.epoch(0), earth) ActorBuilder.set_power_devices( actor=sat, @@ -37,8 +34,7 @@ def __init__(self, node_id, pos_and_vel, paseos_cfg, power_consumption_in_watt): transmit_bits = self.model_size() self.transmit_duration = transmit_bits / ( - 1000 - * self.paseos.local_actor.communication_devices["link"].bandwidth_in_kbps + 1000 * self.paseos.local_actor.communication_devices["link"].bandwidth_in_kbps ) self.current_activity = "train" @@ -70,9 +66,7 @@ def model_size(self): # Return model parameters as a list of NumPy ndarrays bytestream = b"" # Empty byte represenation for _, val in self.model.state_dict().items(): # go over each layer - bytestream += ( - val.cpu().numpy().tobytes() - ) # convert layer to bytes and concatenate + bytestream += val.cpu().numpy().tobytes() # convert layer to bytes and concatenate return len(bytestream) * 8 def local_time(self): @@ -112,9 +106,7 @@ def transmission_is_feasible(self, target_node): target_actor = target_node.paseos.local_actor local_actor = self.paseos.local_actor - transmit_end = pk.epoch( - self.local_time().mjd2000 + self.transmit_duration * pk.SEC2DAY - ) + transmit_end = pk.epoch(self.local_time().mjd2000 + self.transmit_duration * pk.SEC2DAY) los_end = local_actor.is_in_line_of_sight(target_actor, transmit_end) return los_end diff --git a/examples/MPI_example/mpi_example.py b/examples/MPI_example/mpi_example.py index 1370c35e..a9f6ad4d 100644 --- a/examples/MPI_example/mpi_example.py +++ b/examples/MPI_example/mpi_example.py @@ -26,9 +26,7 @@ try: from mpi4py import MPI except: - print( - "This example requires mpi4py. Please install with conda install mpi4py -c conda-forge" - ) + print("This example requires mpi4py. Please install with conda install mpi4py -c conda-forge") import pykep as pk import paseos @@ -44,9 +42,7 @@ # Now we will initialize MPI, for more details please refer to the mpi4py docs. # In MPI "rank" indicates the index of the compute node (so 0-3 in our example). comm = MPI.COMM_WORLD -assert ( - comm.Get_size() == 4 -), "Please run the example with mpiexec -n 4 python mpi_example.py" +assert comm.Get_size() == 4, "Please run the example with mpiexec -n 4 python mpi_example.py" rank = comm.Get_rank() other_ranks = [x for x in range(4) if x != rank] print(f"Started rank {rank}, other ranks are {other_ranks}") @@ -65,9 +61,7 @@ planet_list, sats_pos_and_v, _ = get_constellation( altitude, inclination, nSats, nPlanes, t0, verbose=False ) -print( - f"Rank {rank} set up its orbit with altitude={altitude}m and inclination={inclination}deg" -) +print(f"Rank {rank} set up its orbit with altitude={altitude}m and inclination={inclination}deg") ############ PASEOS INIT ############# # We will now initialize the PASEOS instance on each rank @@ -78,9 +72,7 @@ local_actor = ActorBuilder.get_actor_scaffold( name="Sat_" + str(rank), actor_type=SpacecraftActor, epoch=t0 ) -ActorBuilder.set_orbit( - actor=local_actor, position=pos, velocity=v, epoch=t0, central_body=earth -) +ActorBuilder.set_orbit(actor=local_actor, position=pos, velocity=v, epoch=t0, central_body=earth) paseos_instance = paseos.init_sim(local_actor=local_actor) print(f"Rank {rank} set up its PASEOS instance for its local actor {local_actor}") @@ -97,6 +89,7 @@ # Let's define the variable to track the actors we see total_seen_actors = 0 + # We will (ab)use PASEOS constraint function to track all the actors # we see in an evaluation window (see timestep below). # Turn on SHOW_ALL_WINDOWS if you want to see each window @@ -135,7 +128,6 @@ def constraint_func(verbose=SHOW_ALL_WINDOWS): # Run until end of simulation while t <= simulation_time: - # Advance the simulation state of this rank # Note how we pass the "constraint_func" to tell paseos # to track windows @@ -147,9 +139,7 @@ def constraint_func(verbose=SHOW_ALL_WINDOWS): sys.stdout.flush() # update prints to better see parallelism # Exchange actors between all ranks - exchange_actors( - comm, paseos_instance, local_actor, other_ranks, rank, verbose=SHOW_ALL_COMMS - ) + exchange_actors(comm, paseos_instance, local_actor, other_ranks, rank, verbose=SHOW_ALL_COMMS) # Wait until all ranks finished print(f"Rank {rank} finished the simulation. Waiting for all to finish.") diff --git a/examples/MPI_example/mpi_utility_func.py b/examples/MPI_example/mpi_utility_func.py index 71c67fed..4952af79 100644 --- a/examples/MPI_example/mpi_utility_func.py +++ b/examples/MPI_example/mpi_utility_func.py @@ -48,9 +48,7 @@ def _parse_actor_data(actor_data): return actor -def exchange_actors( - comm, paseos_instance, local_actor, other_ranks, rank, verbose=False -): +def exchange_actors(comm, paseos_instance, local_actor, other_ranks, rank, verbose=False): """This function exchanges the states of various actors among all MPI ranks. Args: @@ -69,9 +67,7 @@ def exchange_actors( # Send local actor to other ranks for i in other_ranks: actor_data = _encode_actor(local_actor) - send_requests.append( - comm.isend(actor_data, dest=i, tag=int(str(rank) + str(i))) - ) + send_requests.append(comm.isend(actor_data, dest=i, tag=int(str(rank) + str(i)))) # Receive from other ranks for i in other_ranks: diff --git a/examples/Sentinel_2_example_notebook/utils.py b/examples/Sentinel_2_example_notebook/utils.py index f44db5e6..2b6d1956 100644 --- a/examples/Sentinel_2_example_notebook/utils.py +++ b/examples/Sentinel_2_example_notebook/utils.py @@ -96,12 +96,8 @@ def get_thresholds( alpha = np.logical_and( np.where(sentinel_img[:, :, 2] >= alpha_thr[2], 1, 0), np.logical_and( - np.where( - sentinel_img[:, :, 2] / sentinel_img[:, :, 1] >= alpha_thr[0], 1, 0 - ), - np.where( - sentinel_img[:, :, 2] / sentinel_img[:, :, 0] >= alpha_thr[1], 1, 0 - ), + np.where(sentinel_img[:, :, 2] / sentinel_img[:, :, 1] >= alpha_thr[0], 1, 0), + np.where(sentinel_img[:, :, 2] / sentinel_img[:, :, 0] >= alpha_thr[1], 1, 0), ), ) beta = np.logical_and( @@ -159,9 +155,7 @@ def get_alert_matrix_and_thresholds( numpy.array: gamma threshold map. """ - alpha, beta, S, gamma = get_thresholds( - sentinel_img, alpha_thr, beta_thr, S_thr, gamma_thr - ) + alpha, beta, S, gamma = get_thresholds(sentinel_img, alpha_thr, beta_thr, S_thr, gamma_thr) alert_matrix = np.logical_or(np.logical_or(np.logical_or(alpha, beta), gamma), S) return alert_matrix, alpha, beta, S, gamma diff --git a/paseos/__init__.py b/paseos/__init__.py index 5319f18e..3681c8e0 100644 --- a/paseos/__init__.py +++ b/paseos/__init__.py @@ -21,9 +21,7 @@ logger.debug("Loaded module.") -def init_sim( - local_actor: BaseActor, cfg: DotMap = None, starting_epoch: pk.epoch = None -): +def init_sim(local_actor: BaseActor, cfg: DotMap = None, starting_epoch: pk.epoch = None): """Initializes PASEOS Args: diff --git a/paseos/activities/activity_manager.py b/paseos/activities/activity_manager.py index 117e4b85..6be65b96 100644 --- a/paseos/activities/activity_manager.py +++ b/paseos/activities/activity_manager.py @@ -43,9 +43,7 @@ def remove_activity(self, name: str): name (str): Name of the activity. """ if name not in self._activities.keys(): - raise ValueError( - "Trying to remove non-existing activity with name: " + name - ) + raise ValueError("Trying to remove non-existing activity with name: " + name) else: del self._activities[name] diff --git a/paseos/activities/activity_processor.py b/paseos/activities/activity_processor.py index a9b07839..6e712e58 100644 --- a/paseos/activities/activity_processor.py +++ b/paseos/activities/activity_processor.py @@ -89,9 +89,7 @@ async def _update(self, elapsed_time: float): logger.debug(f"Time since last update: {elapsed_time}s") logger.trace(f"Applying time multiplier of {self._time_multiplier}") elapsed_time *= self._time_multiplier - self._paseos_instance.advance_time( - elapsed_time, self._power_consumption_in_watt - ) + self._paseos_instance.advance_time(elapsed_time, self._power_consumption_in_watt) async def _run(self): """Main processor loop. Will track time, update paseos and check constraints of the activity.""" diff --git a/paseos/actors/actor_builder.py b/paseos/actors/actor_builder.py index 66a7cb16..c56f5d2b 100644 --- a/paseos/actors/actor_builder.py +++ b/paseos/actors/actor_builder.py @@ -20,7 +20,9 @@ def __new__(self): if not hasattr(self, "instance"): self.instance = super(ActorBuilder, self).__new__(self) else: - logger.debug("Tried to create another instance of ActorBuilder. Keeping original one...") + logger.debug( + "Tried to create another instance of ActorBuilder. Keeping original one..." + ) return self.instance def __init__(self): @@ -37,7 +39,9 @@ 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 != 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." @@ -67,7 +71,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, @@ -116,10 +122,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}") @@ -143,7 +153,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" # Check if the actor already had a power device if actor.has_power_model: @@ -156,7 +168,8 @@ def set_power_devices( assert max_battery_level_in_Ws > 0, "Battery level must be positive" assert charging_rate_in_W > 0, "Battery level must be positive" assert ( - power_device_type == PowerDeviceType.SolarPanel or power_device_type == PowerDeviceType.RTG + power_device_type == PowerDeviceType.SolarPanel + or power_device_type == PowerDeviceType.RTG ), "Only SolarPanel and RTG devices supported." actor._power_device_type = power_device_type @@ -187,7 +200,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." @@ -241,7 +256,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: @@ -251,12 +268,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." @@ -266,7 +289,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( @@ -293,13 +318,18 @@ def add_comm_device(actor: BaseActor, device_name: str, bandwidth_in_kbps: float bandwidth_in_kbps (float): device bandwidth in kbps. """ if device_name in actor.communication_devices: - raise ValueError("Trying to add already existing communication device with device_name: " + device_name) + raise ValueError( + "Trying to add already existing communication device with device_name: " + + device_name + ) actor._communication_devices[device_name] = DotMap(bandwidth_in_kbps=bandwidth_in_kbps) logger.debug(f"Added comm device with bandwith={bandwidth_in_kbps} kbps to actor {actor}.") - def add_custom_property(actor: BaseActor, property_name: str, initial_value: Any, update_function: Callable): + def add_custom_property( + actor: BaseActor, property_name: str, initial_value: Any, update_function: Callable + ): """Adds a custom property to the actor. This e.g. allows tracking any physical the user would like to track. @@ -324,7 +354,9 @@ def add_custom_property(actor: BaseActor, property_name: str, initial_value: Any try: logger.trace(f"Checking update function for actor {actor} with time 0 and power 0.") new_value = update_function(actor, 0, 0) - logger.debug(f"Update function returned {new_value} for actor {actor} with time 0 and power 0.") + logger.debug( + f"Update function returned {new_value} for actor {actor} with time 0 and power 0." + ) except TypeError as e: logger.error(e) # remove property if this failed @@ -337,13 +369,17 @@ def add_custom_property(actor: BaseActor, property_name: str, initial_value: Any if type(new_value) != type(initial_value): # remove property if this failed del actor._custom_properties[property_name] - raise TypeError(f"Update function must return a value of type {type(initial_value)} matching initial vaue.") + raise TypeError( + f"Update function must return a value of type {type(initial_value)} matching initial vaue." + ) # Check that the initial value is the same as the value returned by the update function with time 0 if new_value != initial_value: # remove property if this failed del actor._custom_properties[property_name] - raise ValueError("Update function must return the existing value when called with unchanged time (dt = 0).") + raise ValueError( + "Update function must return the existing value when called with unchanged time (dt = 0)." + ) actor._custom_properties_update_function[property_name] = update_function diff --git a/paseos/actors/base_actor.py b/paseos/actors/base_actor.py index 705ac35d..5543cd67 100644 --- a/paseos/actors/base_actor.py +++ b/paseos/actors/base_actor.py @@ -232,7 +232,6 @@ def discharge(self, consumption_rate_in_W: float, duration_in_s: float): """ pass - @property def altitude( self, t0: pk.epoch = None, @@ -262,10 +261,18 @@ def get_position(self, epoch: pk.epoch): Returns: np.array: [x,y,z] in meters """ - logger.trace("Computing " + self._orbital_parameters.name + " position at time " + str(epoch.mjd2000) + " (mjd2000).") + logger.trace( + "Computing " + + self._orbital_parameters.name + + " position at time " + + str(epoch.mjd2000) + + " (mjd2000)." + ) if self._orbital_parameters is not None and self._position is not None: - raise ValueError("Ambiguous position definition. Either set an orbit OR position with ActorBuilder.") + raise ValueError( + "Ambiguous position definition. Either set an orbit OR position with ActorBuilder." + ) # If the actor has no orbit, return position if self._orbital_parameters is None: @@ -290,10 +297,16 @@ def get_position_velocity(self, epoch: pk.epoch): np.array: [x,y,z] in meters """ if self._orbital_parameters is None: - raise NotImplementedError("No suitable way added to determine actor velocity. Set an orbit with ActorBuilder.") + raise NotImplementedError( + "No suitable way added to determine actor velocity. Set an orbit with ActorBuilder." + ) logger.trace( - "Computing " + self._orbital_parameters.name + " position / velocity at time " + str(epoch.mjd2000) + " (mjd2000)." + "Computing " + + self._orbital_parameters.name + + " position / velocity at time " + + str(epoch.mjd2000) + + " (mjd2000)." ) pos, vel = self._orbital_parameters.eph(epoch) self._previous_position = pos diff --git a/paseos/communication/find_next_window.py b/paseos/communication/find_next_window.py index e27b6ce7..1a5ed688 100644 --- a/paseos/communication/find_next_window.py +++ b/paseos/communication/find_next_window.py @@ -32,9 +32,7 @@ def find_next_window( "Trying to use a not-existing communication link with the name: " + local_actor.communication_devices ) - local_actor_comm_link = local_actor.communication_devices[ - local_actor_communication_link_name - ] + local_actor_comm_link = local_actor.communication_devices[local_actor_communication_link_name] assert local_actor_comm_link.bandwidth_in_kbps > 0, "Bandiwidth has to be positive." assert search_step_size > 0, "dt has to be positive." diff --git a/paseos/communication/get_communication_window.py b/paseos/communication/get_communication_window.py index d55be37b..3ace0f4d 100644 --- a/paseos/communication/get_communication_window.py +++ b/paseos/communication/get_communication_window.py @@ -41,9 +41,7 @@ def get_communication_window( "Trying to use a not-existing communication link with the name: " + local_actor.communication_devices ) - local_actor_comm_link = local_actor.communication_devices[ - local_actor_communication_link_name - ] + local_actor_comm_link = local_actor.communication_devices[local_actor_communication_link_name] assert local_actor_comm_link.bandwidth_in_kbps > 0, "Bandiwidth has to be positive." assert dt > 0, "dt has to be positive." @@ -54,9 +52,7 @@ def get_communication_window( transmitted_data_in_b = 0 current_time_in_s = t0_in_s while ( - local_actor.is_in_line_of_sight( - target_actor, pk.epoch(current_time_in_s * pk.SEC2DAY) - ) + local_actor.is_in_line_of_sight(target_actor, pk.epoch(current_time_in_s * pk.SEC2DAY)) ) and (current_time_in_s - t0_in_s < window_timeout_value_in_s): current_time_in_s += dt transmitted_data_in_b += int( diff --git a/paseos/communication/is_in_line_of_sight.py b/paseos/communication/is_in_line_of_sight.py index 95a4b1c6..8a31a77c 100644 --- a/paseos/communication/is_in_line_of_sight.py +++ b/paseos/communication/is_in_line_of_sight.py @@ -8,9 +8,7 @@ from skyfield.vectorlib import VectorFunction -_SKYFIELD_EARTH_PATH = os.path.join( - os.path.dirname(__file__) + "/../resources/", "de421.bsp" -) +_SKYFIELD_EARTH_PATH = os.path.join(os.path.dirname(__file__) + "/../resources/", "de421.bsp") # Skyfield Earth, in the future we may not always want to load this. _SKYFIELD_EARTH = load(_SKYFIELD_EARTH_PATH)["earth"] @@ -37,9 +35,7 @@ def _at(self, t): return self.r, v, self.center, "SkyfieldSkyCoordinate" -def _is_in_line_of_sight_spacecraft_to_spacecraft( - actor, other_actor, epoch: pk.epoch, plot=False -): +def _is_in_line_of_sight_spacecraft_to_spacecraft(actor, other_actor, epoch: pk.epoch, plot=False): """Determines whether a position is in line of sight of this actor Args: @@ -51,17 +47,12 @@ def _is_in_line_of_sight_spacecraft_to_spacecraft( Returns: bool: true if in line-of-sight. """ - logger.debug( - "Computing line of sight between actors: " + str(actor) + " " + str(other_actor) - ) + logger.debug("Computing line of sight between actors: " + str(actor) + " " + str(other_actor)) my_pos, _ = actor.get_position_velocity(epoch) other_actor_pos, _ = other_actor.get_position_velocity(epoch) logger.trace( - "Computed positions for actors are " - + str(my_pos) - + " and " - + str(other_actor_pos) + "Computed positions for actors are " + str(my_pos) + " and " + str(other_actor_pos) ) line_between_actors = Line( my_pos, @@ -121,10 +112,7 @@ def _is_in_line_of_sight_ground_station_to_spacecraft( ), "0 < Minimum altitude angle < 90" logger.debug( - "Computing line of sight between actors: " - + str(ground_station) - + " " - + str(spacecraft) + "Computing line of sight between actors: " + str(ground_station) + " " + str(spacecraft) ) # Converting time to skyfield to use its API @@ -202,9 +190,7 @@ def is_in_line_of_sight( assert ( actor._central_body_sphere is not None ), f"Please set the central sphere on actor {actor} for line of sight computations." - return _is_in_line_of_sight_spacecraft_to_spacecraft( - actor, other_actor, epoch, plot - ) + return _is_in_line_of_sight_spacecraft_to_spacecraft(actor, other_actor, epoch, plot) elif ( type(actor).__name__ == "GroundstationActor" and type(other_actor).__name__ == "SpacecraftActor" diff --git a/paseos/paseos.py b/paseos/paseos.py index 90010f83..71933a2e 100644 --- a/paseos/paseos.py +++ b/paseos/paseos.py @@ -63,7 +63,9 @@ def __init__(self, local_actor: BaseActor, cfg=None): self.local_actor.set_time(pk.epoch(self._cfg.sim.start_time * pk.SEC2DAY)) # Set line of sight blocking sphere self.local_actor.set_central_body_shape(self._central_body_sphere) - self._activity_manager = ActivityManager(self, self._cfg.sim.activity_timestep, self._cfg.sim.time_multiplier) + self._activity_manager = ActivityManager( + self, self._cfg.sim.activity_timestep, self._cfg.sim.time_multiplier + ) self._operations_monitor = OperationsMonitor(self.local_actor.name) async def wait_for_activity(self): @@ -112,7 +114,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 @@ -124,7 +128,10 @@ def advance_time( # then final smaller or equal timestep to reach target_time while self._state.time < target_time: # Check constraint function - if constraint_function is not None and time_since_constraint_check > self._cfg.sim.activity_timestep: + if ( + constraint_function is not None + and time_since_constraint_check > self._cfg.sim.activity_timestep + ): time_since_constraint_check = 0 if not constraint_function(): logger.info("Time advancing interrupted. Constraint false.") @@ -158,7 +165,9 @@ def advance_time( # Update actor temperature if self.local_actor.has_thermal_model: - self.local_actor._thermal_model.update_temperature(dt, current_power_consumption_in_W) + self.local_actor._thermal_model.update_temperature( + dt, current_power_consumption_in_W + ) # Update state of charge if self.local_actor.has_power_model: @@ -166,7 +175,9 @@ def advance_time( # Update user-defined properties in the actor for property_name in self.local_actor.custom_properties.keys(): - update_function = self.local_actor.get_custom_property_update_function(property_name) + update_function = self.local_actor.get_custom_property_update_function( + property_name + ) new_value = update_function(self.local_actor, dt, current_power_consumption_in_W) self.local_actor.set_custom_property(property_name, new_value) @@ -294,7 +305,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 78d98a78..f5961df0 100644 --- a/paseos/power/charge_model.py +++ b/paseos/power/charge_model.py @@ -46,9 +46,7 @@ def charge( # Apply specified charging model if model == "simple": actor._battery_level_in_Ws += actor._charging_rate_in_W * charging_time_in_s - actor._battery_level_in_Ws = min( - actor.battery_level_in_Ws, actor._max_battery_level_in_Ws - ) + actor._battery_level_in_Ws = min(actor.battery_level_in_Ws, actor._max_battery_level_in_Ws) return actor else: raise NotImplementedError("Unknown charging model " + model) diff --git a/paseos/power/is_in_eclipse.py b/paseos/power/is_in_eclipse.py index 6a5c5d25..6c44b8c5 100644 --- a/paseos/power/is_in_eclipse.py +++ b/paseos/power/is_in_eclipse.py @@ -26,16 +26,12 @@ def is_in_eclipse( try: logger.debug(f"Checking whether {actor} is in eclipse at {t}.") except RuntimeError: - logger.debug( - f"Checking whether {actor} is in eclipse at {t.mjd2000} (mjd2000)." - ) + logger.debug(f"Checking whether {actor} is in eclipse at {t.mjd2000} (mjd2000).") # Compute central body position in solar reference frame r_central_body_heliocentric, _ = np.array(central_body.eph(t)) logger.trace("r_central_body_heliocentric is" + str(r_central_body_heliocentric)) - central_body_sphere = Sphere( - r_central_body_heliocentric, actor._central_body_sphere.radius - ) + central_body_sphere = Sphere(r_central_body_heliocentric, actor._central_body_sphere.radius) # Compute satellite / actor position in solar reference frame r_sat_central_body_frame = np.array(actor.get_position(t)) diff --git a/paseos/radiation/radiation_model.py b/paseos/radiation/radiation_model.py index 0c7f7076..7c360863 100644 --- a/paseos/radiation/radiation_model.py +++ b/paseos/radiation/radiation_model.py @@ -18,9 +18,7 @@ def __init__( restart_events_per_s (float): Device restart being triggered, events per second. failure_events_per_s (float): Complete device failure, events per second. """ - assert ( - data_corruption_events_per_s >= 0 - ), "data_corruption_events_per_s cannot be negative." + 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." assert failure_events_per_s >= 0, "failure_events_per_s cannot be negative." @@ -89,9 +87,7 @@ def did_device_restart(self, interval_in_s: float): bool: Whether restart event occurred. """ assert interval_in_s > 0, "Time interval must be positive." - return RadiationModel._sample_poisson_process( - self._restart_events_per_s, interval_in_s - ) + return RadiationModel._sample_poisson_process(self._restart_events_per_s, interval_in_s) def did_device_experience_failure(self, interval_in_s: float): """Models whether the device experienced a failure in this interval. @@ -103,6 +99,4 @@ def did_device_experience_failure(self, interval_in_s: float): bool: Whether restart event occurred. """ assert interval_in_s > 0, "Time interval must be positive." - return RadiationModel._sample_poisson_process( - self._failure_events_per_s, interval_in_s - ) + return RadiationModel._sample_poisson_process(self._failure_events_per_s, interval_in_s) diff --git a/paseos/tests/activity_test.py b/paseos/tests/activity_test.py index b7e81fb1..6f3f8330 100644 --- a/paseos/tests/activity_test.py +++ b/paseos/tests/activity_test.py @@ -54,9 +54,7 @@ async def func(args): await asyncio.sleep(0.2) # Register an activity that draws 10 watt per second - sim.register_activity( - "Testing", activity_function=func, power_consumption_in_watt=10 - ) + sim.register_activity("Testing", activity_function=func, power_consumption_in_watt=10) # Run the activity sim.perform_activity("Testing", activity_func_args=[test_val]) diff --git a/paseos/tests/communication_window_test.py b/paseos/tests/communication_window_test.py index cc950309..c86e72b5 100644 --- a/paseos/tests/communication_window_test.py +++ b/paseos/tests/communication_window_test.py @@ -30,12 +30,8 @@ def setup_sentinel_example(t0): # Define Sentinel 2 orbit sentinel2B = ActorBuilder.get_actor_scaffold("Sentinel2B", SpacecraftActor, t0) - sentinel2B_line1 = ( - "1 42063U 17013A 22300.18652110 .00000099 00000+0 54271-4 0 9998" - ) - sentinel2B_line2 = ( - "2 42063 98.5693 13.0364 0001083 104.3232 255.8080 14.30819357294601" - ) + sentinel2B_line1 = "1 42063U 17013A 22300.18652110 .00000099 00000+0 54271-4 0 9998" + sentinel2B_line2 = "2 42063 98.5693 13.0364 0001083 104.3232 255.8080 14.30819357294601" s2b = pk.planet.tle(sentinel2B_line1, sentinel2B_line2) # Calculating S2B ephemerides. @@ -66,7 +62,6 @@ def setup_sentinel_example(t0): def test_find_next_window(): - # Test window from other test is found t0 = pk.epoch_from_string("2022-Oct-27 21:00:00") sentinel2B, maspalomas_groundstation = setup_sentinel_example(t0) diff --git a/paseos/tests/custom_property_test.py b/paseos/tests/custom_property_test.py index 900e99b9..839ee312 100644 --- a/paseos/tests/custom_property_test.py +++ b/paseos/tests/custom_property_test.py @@ -25,7 +25,9 @@ def test_custom_power_consumption_property(): def prop_update_fn(actor, dt, power_consumption): return actor.get_custom_property(prop_name) + power_consumption * dt - ActorBuilder.add_custom_property(actor=sat1, property_name=prop_name, update_function=prop_update_fn, initial_value=0) + ActorBuilder.add_custom_property( + actor=sat1, property_name=prop_name, update_function=prop_update_fn, initial_value=0 + ) print(f"Actor custom properties are now {sat1.custom_properties}") # init simulation diff --git a/paseos/tests/operations_monitor_test.py b/paseos/tests/operations_monitor_test.py index 4da86ba7..0622f5fc 100644 --- a/paseos/tests/operations_monitor_test.py +++ b/paseos/tests/operations_monitor_test.py @@ -37,13 +37,9 @@ async def func2(args): await asyncio.sleep(1.0) # Register an activity that draws 10 watt per second - sim.register_activity( - "Activity_1", activity_function=func1, power_consumption_in_watt=2 - ) + sim.register_activity("Activity_1", activity_function=func1, power_consumption_in_watt=2) - sim.register_activity( - "Activity_2", activity_function=func2, power_consumption_in_watt=10 - ) + sim.register_activity("Activity_2", activity_function=func2, power_consumption_in_watt=10) # Run the activity sim.perform_activity("Activity_1") diff --git a/paseos/tests/power_test.py b/paseos/tests/power_test.py index 600bada4..bc4c8d0b 100644 --- a/paseos/tests/power_test.py +++ b/paseos/tests/power_test.py @@ -21,9 +21,7 @@ def test_power_charging(): # Define local actor 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) - ActorBuilder.set_power_devices( - sat1, 500, 10000, 1, paseos.PowerDeviceType.SolarPanel - ) + ActorBuilder.set_power_devices(sat1, 500, 10000, 1, paseos.PowerDeviceType.SolarPanel) # init simulation sim = paseos.init_sim(sat1) @@ -44,7 +42,6 @@ def test_power_charging(): def test_RTG_charging_in_eclipse(): - # Define central body earth = pk.planet.jpl_lp("earth") diff --git a/paseos/tests/radiation_test.py b/paseos/tests/radiation_test.py index 9667aeb5..d927b4d2 100644 --- a/paseos/tests/radiation_test.py +++ b/paseos/tests/radiation_test.py @@ -79,9 +79,7 @@ async def func(args): await asyncio.sleep(1.0) # Register an activity that draws 10 watt per second - sim.register_activity( - "Testing", activity_function=func, power_consumption_in_watt=0 - ) + sim.register_activity("Testing", activity_function=func, power_consumption_in_watt=0) # Run the activity sim.perform_activity("Testing") @@ -118,9 +116,7 @@ async def func(args): await asyncio.sleep(0.2) # Register an activity that draws 10 watt per second - sim.register_activity( - "Testing", activity_function=func, power_consumption_in_watt=0 - ) + sim.register_activity("Testing", activity_function=func, power_consumption_in_watt=0) # Run the activity sim.perform_activity("Testing", activity_func_args=[test_val]) diff --git a/paseos/tests/thermal_model_test.py b/paseos/tests/thermal_model_test.py index 2c9977b0..0744a6e3 100644 --- a/paseos/tests/thermal_model_test.py +++ b/paseos/tests/thermal_model_test.py @@ -47,9 +47,7 @@ async def func(args): await asyncio.sleep(16.0) # Register an activity that draws 10 watt per second - sim.register_activity( - "Activity_1", activity_function=func, power_consumption_in_watt=10 - ) + sim.register_activity("Activity_1", activity_function=func, power_consumption_in_watt=10) # Run the activity sim.perform_activity("Activity_1") diff --git a/paseos/tests/time_multiplier_test.py b/paseos/tests/time_multiplier_test.py index bc7541f5..0f43a1dd 100644 --- a/paseos/tests/time_multiplier_test.py +++ b/paseos/tests/time_multiplier_test.py @@ -39,9 +39,7 @@ async def func(args): await asyncio.sleep(0.2) # Register an activity that draws 10 watt per second - sim.register_activity( - "Testing", activity_function=func, power_consumption_in_watt=10 - ) + sim.register_activity("Testing", activity_function=func, power_consumption_in_watt=10) # Run the activity sim.perform_activity("Testing", activity_func_args=[test_val]) diff --git a/paseos/thermal/thermal_model.py b/paseos/thermal/thermal_model.py index c9c59aba..a7590c09 100644 --- a/paseos/thermal/thermal_model.py +++ b/paseos/thermal/thermal_model.py @@ -100,9 +100,7 @@ def _initialize_constants(self): # EQ 15 in source self._C_solar_input = ( - self._actor_sun_absorptance - * self._actor_sun_facing_area - * self._body_solar_irradiance + self._actor_sun_absorptance * self._actor_sun_facing_area * self._body_solar_irradiance ) logger.trace(f"self._C_solar_input={self._C_solar_input}") @@ -127,9 +125,7 @@ def _initialize_constants(self): # EQ 20 self._C_actor_emission = ( - self._actor_infrared_absorptance - * self._actor_emissive_area - * self._boltzmann_constant + self._actor_infrared_absorptance * self._actor_emissive_area * self._boltzmann_constant ) logger.trace(f"self._C_actor_emission={self._C_actor_emission}") @@ -139,7 +135,7 @@ def _compute_body_view_from_actor(self) -> None: Returns: float: constant from above defined EQs. """ - h = self._actor.altitude / self._body_radius + h = self._actor.altitude() / self._body_radius return 1.0 / (h * h) def _compute_solar_input(self): @@ -205,11 +201,11 @@ def update_temperature(self, dt: float, current_power_consumption: float = 0): logger.debug(f"Actor's old temperature was {self._actor_temperature_in_K}.") logger.trace(f"Actor in eclipse: {self._actor.is_in_eclipse()}") - logger.trace(f"Actor altitude: {self._actor.altitude}") + logger.trace(f"Actor altitude: {self._actor.altitude()}") - self._actor_temperature_in_K = self._actor_temperature_in_K + ( - dt * total_change_in_W - ) / (self._actor.mass * self._actor_thermal_capacity) + self._actor_temperature_in_K = self._actor_temperature_in_K + (dt * total_change_in_W) / ( + self._actor.mass * self._actor_thermal_capacity + ) # Ensure value cannot go below 0 self._actor_temperature_in_K = max(0.0, self._actor_temperature_in_K) diff --git a/paseos/utils/check_cfg.py b/paseos/utils/check_cfg.py index 16f2f1f3..51d506e8 100644 --- a/paseos/utils/check_cfg.py +++ b/paseos/utils/check_cfg.py @@ -43,9 +43,7 @@ def _check_for_keys(cfg: DotMap, major_categories: list) -> None: 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}" - ) + 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: @@ -65,9 +63,7 @@ def _check_entry_types(cfg: DotMap, major_categories: list) -> None: for key in float_keys: for category in major_categories: - if key in cfg[category].keys() and not isinstance( - cfg[category][key], float - ): + 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: @@ -88,7 +84,8 @@ def _check_entry_types(cfg: DotMap, major_categories: list) -> None: 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.""" + 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 diff --git a/paseos/utils/load_default_cfg.py b/paseos/utils/load_default_cfg.py index eb310d21..3459b42f 100644 --- a/paseos/utils/load_default_cfg.py +++ b/paseos/utils/load_default_cfg.py @@ -7,9 +7,7 @@ def load_default_cfg(): """Loads the default toml config file from the cfg folder.""" - path = os.path.join( - os.path.dirname(__file__) + "/../resources/", "default_cfg.toml" - ) + path = os.path.join(os.path.dirname(__file__) + "/../resources/", "default_cfg.toml") logger.debug(f"loading default cfg from path: {path}") with open(path) as cfg: diff --git a/paseos/utils/operations_monitor.py b/paseos/utils/operations_monitor.py index 02875d48..51850e47 100644 --- a/paseos/utils/operations_monitor.py +++ b/paseos/utils/operations_monitor.py @@ -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: @@ -70,9 +70,7 @@ def log( known_actors (list): List of names of the known actors. """ logger.trace("Logging iteration") - assert local_actor.name == self._actor_name, ( - "Expected actor's name was" + self._actor_name - ) + assert local_actor.name == self._actor_name, "Expected actor's name was" + self._actor_name self._log.timesteps.append(local_actor.local_time.mjd2000 * pk.DAY2SEC) self._log.current_activity.append(local_actor.current_activity) self._log.position.append(local_actor._previous_position) @@ -95,7 +93,7 @@ def log( # Track all custom properties for key, value in local_actor.custom_properties.items(): if key not in self._log.custom_properties.keys(): - logger.warning(f"Property {key} was not tracked beforem, adding now.") + logger.info(f"Property {key} was not tracked beforem, adding now.") self._log.custom_properties[key] = [] self._log.custom_properties[key].append(value) diff --git a/paseos/visualization/plot.py b/paseos/visualization/plot.py index 580cae60..6baf530b 100644 --- a/paseos/visualization/plot.py +++ b/paseos/visualization/plot.py @@ -28,6 +28,4 @@ def plot(sim: PASEOS, plot_type: PlotType): if plot_type is PlotType.SpacePlot: return SpaceAnimation(sim) else: - raise ValueError( - f"PlotType {plot_type} not known. Available are {[e for e in PlotType]}" - ) + raise ValueError(f"PlotType {plot_type} not known. Available are {[e for e in PlotType]}") diff --git a/paseos/visualization/space_animation.py b/paseos/visualization/space_animation.py index 6ea72924..4a8206d6 100644 --- a/paseos/visualization/space_animation.py +++ b/paseos/visualization/space_animation.py @@ -37,7 +37,6 @@ def __init__(self, sim: PASEOS, n_trajectory: int = 32) -> None: self.objects.append(DotMap(actor=known_actor, positions=np.array(pos_norm))) with plt.style.context("dark_background"): - # Create figure for 3d animation self.fig, default_ax = plt.subplots( 1, @@ -129,9 +128,7 @@ def _get_los_matrix(self, current_actors: List[BaseActor]) -> np.ndarray: for i, a1 in enumerate(current_actors): for j, a2 in enumerate(current_actors[i + 1 :]): # Skip LOS between groundstations (leads to crash) - if isinstance(a1, GroundstationActor) and isinstance( - a2, GroundstationActor - ): + if isinstance(a1, GroundstationActor) and isinstance(a2, GroundstationActor): continue elif a1.is_in_line_of_sight(a2, local_time) is True: los_matrix[i, j + i + 1] = 1.0 @@ -223,9 +220,7 @@ def _plot_actors(self) -> None: if isinstance(obj.actor, SpacecraftActor) or isinstance( obj.actor, GroundstationActor ): - trajectory = self.ax_3d.plot3D(data[0, 0], data[0, 1], data[0, 2])[ - 0 - ] + trajectory = self.ax_3d.plot3D(data[0, 0], data[0, 1], data[0, 2])[0] obj.plot.trajectory = trajectory obj.plot.point = self.ax_3d.plot( data[0, 0], @@ -301,9 +296,7 @@ def update(self, sim: PASEOS, creating_animation=False) -> None: # if known_actor does not exist in objects, add the actors and update in plot objects_to_add = list(current_actors.difference(objects_in_plot)) - plot_objects_to_remove = [ - x for x in self.objects if x.actor in objects_to_remove - ] + plot_objects_to_remove = [x for x in self.objects if x.actor in objects_to_remove] for obj in plot_objects_to_remove: obj.plot.trajectory.remove() obj.plot.point.remove() @@ -371,9 +364,7 @@ def update(self, sim: PASEOS, creating_animation=False) -> None: except RuntimeError: logger.trace("Animation date label could not be updated.") - self.time_label.set_text( - f"t={sim._state.time - sim._cfg.sim.start_time:<10.2e}" - ) + self.time_label.set_text(f"t={sim._state.time - sim._cfg.sim.start_time:<10.2e}") logger.debug("Plot updated.") @@ -404,9 +395,7 @@ def _animation_wrapper(self, step: int, sim: PASEOS, dt: float) -> List[Artist]: """ return self._animate(sim, dt) - def animate( - self, sim: PASEOS, dt: float, steps: int = 1, save_to_file: str = None - ) -> None: + def animate(self, sim: PASEOS, dt: float, steps: int = 1, save_to_file: str = None) -> None: """Animates paseos for a given number of steps with dt in each step. Args: