Skip to content

Commit

Permalink
Merge pull request #185 from aidotse/main
Browse files Browse the repository at this point in the history
main -> release (for v0.2.0)
  • Loading branch information
gomezzz authored Sep 4, 2023
2 parents de801ac + 61d0610 commit 7453b97
Show file tree
Hide file tree
Showing 56 changed files with 2,560 additions and 681 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/autoblack.yml
Original file line number Diff line number Diff line change
@@ -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"
23 changes: 19 additions & 4 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ jobs:
build:
runs-on: ubuntu-latest

permissions:
pull-requests: write
contents: read
id-token: write
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.9
Expand All @@ -47,6 +50,18 @@ jobs:
shell: bash -l {0}
run: |
micromamba activate paseos
cd paseos/tests
micromamba install pytest
pytest
micromamba install pytest pytest-cov pytest-asyncio
pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=paseos paseos/tests/ | tee pytest-coverage.txt
- name: Pytest coverage comment
uses: MishaKav/pytest-coverage-comment@main
if: github.event_name == 'pull_request'
with:
pytest-coverage-path: ./pytest-coverage.txt
title: Coverage Report
badge-title: Overall Coverage
hide-badge: false
hide-report: false
create-new-comment: false
hide-comment: false
report-only-changed-files: false
junitxml-path: ./pytest.xml
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,7 @@ examples/Sentinel_2_example_notebook/La_Palma_02.tif
paseos/tests/de421.bsp
test.csv
thermal_test.csv
results
results
pytest-coverage.txt
pytest.xml
examples/Orekit_example/orekit-data.zip
486 changes: 393 additions & 93 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies:
- numpy==1.23.5 # core non-optional depedency
- myst-parser # for markdown math in docs
- pykep>=2.6 # core non-optional dependency
- pyquaternion>=0.9.9 # core non-optional dependency
- pytest # for tests
- pytest-asyncio # for tests involving activities
- python>=3.8 # core non-optional dependency
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 3 additions & 9 deletions examples/Learning_example/simple_neural_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down
16 changes: 4 additions & 12 deletions examples/Learning_example/simple_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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"
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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

Expand Down
22 changes: 6 additions & 16 deletions examples/MPI_example/mpi_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}")
Expand All @@ -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
Expand All @@ -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}")
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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.")
Expand Down
8 changes: 2 additions & 6 deletions examples/MPI_example/mpi_utility_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
256 changes: 256 additions & 0 deletions examples/Orekit_example/orekit_integration.ipynb

Large diffs are not rendered by default.

108 changes: 108 additions & 0 deletions examples/Orekit_example/orekit_propagator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
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 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


class OrekitPropagator:
"""This class serves as a wrapper to orekit. It initializes the orekit
virtual machine and provides a method to propagate a satellite orbit.
It follows the example from the orekit documentation:
https://gitlab.orekit.org/orekit-labs/python-wrapper/-/blob/master/examples/Propagation.ipynb
"""

# Constants for the numerical propagator, see orekit docs for details
minStep = 0.0001
maxstep = 1000.0
initStep = 60.0
positionTolerance = 1.0

def __init__(self, orbital_elements: list, epoch: AbsoluteDate, satellite_mass: float) -> None:
"""Initialize the propagator.
Args:
orbital_elements (list): List of orbital elements.
epoch (AbsoluteDate): Epoch of the orbit.
satellite_mass (float): Mass of the satellite.
"""

# Inertial frame where the satellite is defined
inertialFrame = FramesFactory.getEME2000()

# Unpack the orbital elements
a, e, i, omega, raan, lv = orbital_elements

self.initialDate = epoch

# Orbit construction as Keplerian
initialOrbit = KeplerianOrbit(
a,
e,
i,
omega,
raan,
lv,
PositionAngle.TRUE,
inertialFrame,
epoch,
Constants.WGS84_EARTH_MU,
)

# Set up the numerical propagator tolerance
tolerances = NumericalPropagator.tolerances(
self.positionTolerance, initialOrbit, initialOrbit.getType()
)

# Set up the numerical integrator
integrator = DormandPrince853Integrator(
self.minStep,
self.maxstep,
JArray_double.cast_(
tolerances[0]
), # Double array of doubles needs to be casted in Python
JArray_double.cast_(tolerances[1]),
)
integrator.setInitialStepSize(self.initStep)

# Define the initial state of the spacecraft
satellite_mass = 100.0 # The models need a spacecraft mass, unit kg.
initialState = SpacecraftState(initialOrbit, satellite_mass)

# Set up the numerical propagator
self.propagator_num = NumericalPropagator(integrator)
self.propagator_num.setOrbitType(OrbitType.CARTESIAN)
self.propagator_num.setInitialState(initialState)

# Add the force models
gravityProvider = GravityFieldFactory.getNormalizedProvider(10, 10)
self.propagator_num.addForceModel(
HolmesFeatherstoneAttractionModel(
FramesFactory.getITRF(IERSConventions.IERS_2010, True), gravityProvider
)
)

def eph(self, time_since_epoch_in_seconds: float):
"""Get the position and velocity of the satellite at a given time since epoch.
Args:
time_since_epoch_in_seconds (float): Time since epoch in seconds.
Returns:
orekit SpacecraftState: The position and velocity of the satellite.
"""
state = self.propagator_num.propagate(
self.initialDate, self.initialDate.shiftedBy(time_since_epoch_in_seconds)
)

return state
Loading

0 comments on commit 7453b97

Please sign in to comment.