monolith-pkg 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.3
2
+ Name: monolith-pkg
3
+ Version: 0.1.0
4
+ Summary: Monolith package with C++ extensions
5
+ Author: Aaron Berkhoff
6
+ Author-email: aaronberkhoff@outlook.com
7
+ Requires-Python: >=3.12,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Provides-Extra: dev
12
+ Requires-Dist: mypy (>=1.16.0,<2.0.0) ; extra == "dev"
13
+ Requires-Dist: numpy (>=2.2.6,<3.0.0)
14
+ Requires-Dist: pybind11 (>=2.11.0,<3.0.0) ; extra == "dev"
15
+ Requires-Dist: pybind11-stubgen (>=2.5.5,<3.0.0) ; extra == "dev"
@@ -0,0 +1,50 @@
1
+ [tool.poetry]
2
+ name = "monolith-pkg"
3
+ version = "0.1.0"
4
+ description = "Monolith package with C++ extensions"
5
+ authors = ["Aaron Berkhoff <aaronberkhoff@outlook.com>"]
6
+ packages = [{include = "monolith", from = "python"}]
7
+ include = [
8
+ {path = "python/monolith/dynamics*.so", format = "wheel"},
9
+ {path = "python/monolith/planets*.so", format = "wheel"},
10
+
11
+ ]
12
+
13
+ [tool.poetry.dependencies]
14
+ python = "^3.12"
15
+ numpy = ">=2.2.6,<3.0.0"
16
+ pybind11 = "^2.11.0"
17
+ mypy = ">=1.16.0,<2.0.0"
18
+ pybind11-stubgen = "^2.5.5"
19
+
20
+ [tool.poetry.group.dev.dependencies]
21
+ pytest = "^8.4.0"
22
+ mypy = "^1.16.0"
23
+ pylint = "^3.3.7"
24
+ black = "^25.1.0"
25
+ pre-commit = "^4.2.0"
26
+ poetry = "^1.7.0"
27
+ pybind11-stubgen = "^2.5.5"
28
+
29
+ [tool.poetry.extras]
30
+ dev = [
31
+ "pytest",
32
+ "mypy",
33
+ "pylint",
34
+ "black",
35
+ "pre-commit",
36
+ "poetry",
37
+ "pybind11",
38
+ "pybind11-stubgen",
39
+ ]
40
+
41
+ # Include compiled C++ extensions
42
+ # [[tool.poetry.include]]
43
+ # path = "python/monolith/dynamics*.so"
44
+
45
+ # [[tool.poetry.include]]
46
+ # path = "python/monolith/planets*.so"
47
+
48
+ [build-system]
49
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
50
+ build-backend = "poetry.core.masonry.api"
File without changes
@@ -0,0 +1,42 @@
1
+ """
2
+ module for dynamics
3
+ """
4
+
5
+ import torch
6
+
7
+
8
+ class Dynamic:
9
+ def __init__(self, func=None):
10
+ self._func = func if func is not None else self.func
11
+
12
+ def __call__(self, state):
13
+ return self._func(state)
14
+
15
+ def __add__(self, other):
16
+ if not isinstance(other, Dynamic):
17
+ raise TypeError(
18
+ f"Cannot add object of type <{type(other)}> to type Dynamic"
19
+ )
20
+
21
+ return CombinedDynamics(self, other)
22
+
23
+ def __radd__(self, other):
24
+ if other == 0:
25
+ return self
26
+ return self.__add__(other)
27
+
28
+ def func(self, state):
29
+ return torch.zeros_like(state) # Default: no dynamics
30
+
31
+
32
+ class CombinedDynamics(Dynamic):
33
+
34
+ def __init__(self, *dynamics):
35
+ super().__init__()
36
+ self.dynamics = dynamics
37
+
38
+ def func(self, state):
39
+
40
+ for dyn in self.dynamics:
41
+
42
+ state @= dyn(state)
File without changes
File without changes
File without changes
@@ -0,0 +1,64 @@
1
+ import torch
2
+
3
+ from monolith.dynamics_dep import Dynamic
4
+
5
+
6
+ class TwoBody(Dynamic):
7
+ """
8
+ A class to represent a Keplerian dynamic.
9
+
10
+ Attributes
11
+ ----------
12
+ scenario : Scenario
13
+ The scenario of the dynamic.
14
+ agent : Agent
15
+ The agent of the dynamic.
16
+ stm : STM
17
+ The state transition matrix of the dynamic.
18
+ function : function
19
+ The function of the dynamic.
20
+ """
21
+
22
+ def __init__(self, central_body):
23
+ super.__init__(self)
24
+
25
+ """
26
+ Constructs all the necessary attributes for the Keplerian object.
27
+
28
+ Parameters
29
+ ----------
30
+ scenario : Scenario
31
+ The scenario of the dynamic.
32
+ agent : Agent
33
+ The agent of the dynamic.
34
+ stm : STM
35
+ The state transition matrix of the dynamic.
36
+
37
+ """
38
+
39
+ self.central_body = central_body
40
+
41
+ def func(self, state, time: float = None):
42
+ """
43
+ The function of the Keplerian dynamic.
44
+
45
+ Parameters
46
+ ----------
47
+ state : State
48
+ The state of the dynamic.
49
+ time : float
50
+ The time of the dynamic.
51
+
52
+ Returns
53
+ -------
54
+ State
55
+ The result of the function.
56
+ """
57
+
58
+ radius = torch.norm(state[0:3])
59
+
60
+ acceleration = -self.central_body.mu * state[0:3] / radius
61
+
62
+ dot = torch.stack([state[3:6], acceleration])
63
+
64
+ return acceleration
@@ -0,0 +1,21 @@
1
+ """
2
+
3
+ Module that holds Base ODE class and methods
4
+
5
+ """
6
+
7
+ import torch
8
+
9
+
10
+ class ODE:
11
+
12
+ def __init__(self, agents):
13
+
14
+ self.agents = agents
15
+ self.functions = [agent.dynamic for agent in agents]
16
+
17
+ pass
18
+
19
+ def solve(self):
20
+
21
+ pass
@@ -0,0 +1,155 @@
1
+ import torch
2
+ from typing import Callable, Tuple
3
+
4
+
5
+ class ODE78:
6
+ def __init__(self):
7
+ self.alpha = torch.tensor(
8
+ [
9
+ 2 / 27,
10
+ 1 / 9,
11
+ 1 / 6,
12
+ 5 / 12,
13
+ 0.5,
14
+ 5 / 6,
15
+ 1 / 6,
16
+ 2 / 3,
17
+ 1 / 3,
18
+ 1.0,
19
+ 0.0,
20
+ 1.0,
21
+ ],
22
+ dtype=torch.float64,
23
+ )
24
+
25
+ self.beta = torch.tensor(
26
+ [
27
+ [2 / 27] + [0] * 12,
28
+ [1 / 36, 1 / 12] + [0] * 11,
29
+ [1 / 24, 0, 1 / 8] + [0] * 10,
30
+ [5 / 12, 0, -25 / 16, 25 / 16] + [0] * 9,
31
+ [0.05, 0, 0, 0.25, 0.2] + [0] * 8,
32
+ [-25 / 108, 0, 0, 125 / 108, -65 / 27, 125 / 54] + [0] * 7,
33
+ [31 / 300, 0, 0, 0, 61 / 225, -2 / 9, 13 / 900] + [0] * 6,
34
+ [2, 0, 0, -53 / 6, 704 / 45, -107 / 9, 67 / 90, 3] + [0] * 5,
35
+ [
36
+ -91 / 108,
37
+ 0,
38
+ 0,
39
+ 23 / 108,
40
+ -976 / 135,
41
+ 311 / 54,
42
+ -19 / 60,
43
+ 17 / 6,
44
+ -1 / 12,
45
+ ]
46
+ + [0] * 4,
47
+ [
48
+ 2383 / 4100,
49
+ 0,
50
+ 0,
51
+ -341 / 164,
52
+ 4496 / 1025,
53
+ -301 / 82,
54
+ 2133 / 4100,
55
+ 45 / 82,
56
+ 45 / 164,
57
+ 18 / 41,
58
+ ]
59
+ + [0] * 3,
60
+ [3 / 205, 0, 0, 0, 0, -6 / 41, -3 / 205, -3 / 41, 3 / 41, 6 / 41, 0]
61
+ + [0] * 2,
62
+ [
63
+ -1777 / 4100,
64
+ 0,
65
+ 0,
66
+ -341 / 164,
67
+ 4496 / 1025,
68
+ -289 / 82,
69
+ 2193 / 4100,
70
+ 51 / 82,
71
+ 33 / 164,
72
+ 12 / 41,
73
+ 0,
74
+ 1,
75
+ ],
76
+ ],
77
+ dtype=torch.float64,
78
+ )
79
+
80
+ self.chi = torch.tensor(
81
+ [
82
+ 0,
83
+ 0,
84
+ 0,
85
+ 0,
86
+ 0,
87
+ 34 / 105,
88
+ 9 / 35,
89
+ 9 / 35,
90
+ 9 / 280,
91
+ 9 / 280,
92
+ 0,
93
+ 41 / 840,
94
+ 41 / 840,
95
+ ],
96
+ dtype=torch.float64,
97
+ )
98
+ self.psi = torch.tensor(
99
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, -1], dtype=torch.float64
100
+ )
101
+ self.pow = 1 / 8
102
+
103
+ def integrate(
104
+ self,
105
+ func: Callable[[torch.Tensor, torch.Tensor], torch.Tensor],
106
+ t0: float,
107
+ tfinal: float,
108
+ dtmin: float,
109
+ y0: torch.Tensor,
110
+ tol: float,
111
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
112
+
113
+ t = torch.tensor(t0, dtype=torch.float64)
114
+ tf = torch.tensor(tfinal, dtype=torch.float64)
115
+ h = torch.sign(tf - t) * abs(dtmin)
116
+ hmin = abs(dtmin)
117
+ hmax = abs(tf - t)
118
+ y = y0.clone().double().reshape(-1, 1)
119
+ f = y.repeat(1, 13) * 0
120
+
121
+ forward = 1 if h.item() > 0 else -1
122
+ tout = [t]
123
+ yout = [y.view(-1).clone()]
124
+ varstep = tol != 0
125
+ tau = tol * max(torch.norm(y, p=float("inf")).item(), 1.0)
126
+
127
+ while t * forward < tf * forward:
128
+ if (t + h) * forward > tf * forward:
129
+ h = tf - t
130
+
131
+ f[:, 0] = func(t, y).view(-1)
132
+ for j in range(12):
133
+ yint = y + h * torch.matmul(f[:, : j + 1], self.beta[: j + 1, j])
134
+ f[:, j + 1] = func(t + self.alpha[j] * h, yint).view(-1)
135
+
136
+ if varstep:
137
+ gamma1 = h * 41 / 840 * torch.matmul(f, self.psi.view(-1, 1))
138
+ delta = torch.norm(gamma1, p=float("inf")).item()
139
+ tau = tol * max(torch.norm(y, p=float("inf")).item(), 1.0)
140
+ else:
141
+ delta = 0
142
+
143
+ if not varstep or delta <= tau or abs(h) == hmin:
144
+ t = t + h
145
+ y = y + h * torch.matmul(f, self.chi.view(-1, 1))
146
+ tout.append(t.clone())
147
+ yout.append(y.view(-1).clone())
148
+
149
+ if varstep and delta != 0.0:
150
+ h = forward * min(hmax, abs(0.8 * h * (tau / delta) ** self.pow))
151
+ if (t + h) * forward > tf * forward:
152
+ h = tf - t
153
+ h = forward * max(abs(h), hmin)
154
+
155
+ return torch.stack(tout), torch.stack(yout)
@@ -0,0 +1,117 @@
1
+ """
2
+ Author: Aaron Berkhoff:
3
+
4
+ """
5
+
6
+ from typing import Optional
7
+ from datetime import datetime
8
+ import torch
9
+
10
+
11
+ class State:
12
+ """
13
+ Object that stores state information:
14
+
15
+ Attributes:
16
+ ----------
17
+ position: torch tensor (3,1), optional
18
+ velocity: torch tensor (3,1), optional
19
+ acceleration: torch tensor (3,1), optional
20
+ attitude: torch tensor (4,1) or (3,1), optional
21
+ angular_velocity: torch tensor (3,1), optional
22
+ angular_acceleration: torch tensor (3,1), optional
23
+ latitude: torch tensor (1,1)
24
+ longitude: torch tensor (1,1)
25
+ altitude: torch tensor
26
+ time: datetime, optional
27
+ frame: str, optional:
28
+ The reference frame of the state ('inertial' or 'ECEF', default is 'inertial').
29
+ orbital_elements: monolith.orbital_elements, optional
30
+ """
31
+
32
+ position: Optional[torch.Tensor]
33
+ velocity: Optional[torch.Tensor]
34
+ acceleration: Optional[torch.Tensor]
35
+ attitude: Optional[torch.Tensor]
36
+ angular_velocity: Optional[torch.Tensor]
37
+ angular_acceleration: Optional[torch.Tensor]
38
+ latitude: Optional[torch.Tensor]
39
+ longitude: Optional[torch.Tensor]
40
+ altitude: Optional[torch.Tensor]
41
+ time: Optional[datetime]
42
+ orbital_elements: Optional[object] # Replace with actual type if you have it
43
+
44
+ _supported_attributes = [
45
+ "position",
46
+ "velocity",
47
+ "acceleration",
48
+ "attitude",
49
+ "angular_velocity",
50
+ "angular_acceleration",
51
+ "latitude",
52
+ "longitude",
53
+ "altitude",
54
+ "time",
55
+ "orbital_elements",
56
+ ]
57
+
58
+ def __init__(self, frame="inertial", **kwargs):
59
+
60
+ # Initialize all known attributes to None
61
+ for attr in self._supported_attributes:
62
+ setattr(self, attr, None)
63
+
64
+ # Set attributes passed via kwargs
65
+ for key, value in kwargs.items():
66
+ if key in State._supported_attributes:
67
+ if isinstance(value, (list, tuple, float, int)):
68
+ value = torch.tensor(value).view(-1, 1)
69
+ setattr(self, key, value)
70
+ else:
71
+ raise ValueError(f"kwarg <{key}> is not supported")
72
+
73
+ self.frame = frame
74
+
75
+ def get_full_state(self) -> torch.Tensor:
76
+ """
77
+ Concatenates all available tensor attributes in the documented order
78
+ into a single (N, 1) torch tensor.
79
+
80
+ Order:
81
+ position, velocity, acceleration, attitude,
82
+ angular_velocity, angular_acceleration,
83
+ latitude, longitude, altitude
84
+
85
+ Returns:
86
+ torch.Tensor: Concatenated state vector.
87
+ """
88
+ ordered_attrs = [
89
+ "position",
90
+ "velocity",
91
+ "acceleration",
92
+ "attitude",
93
+ "angular_velocity",
94
+ "angular_acceleration",
95
+ "latitude",
96
+ "longitude",
97
+ "altitude",
98
+ ]
99
+
100
+ tensors = []
101
+
102
+ for attr in ordered_attrs:
103
+ value = getattr(self, attr, None)
104
+ if isinstance(value, torch.Tensor):
105
+ tensors.append(value)
106
+
107
+ if tensors:
108
+ return torch.cat(tensors, dim=0)
109
+
110
+ raise ValueError("No tensor attributes are set on the State object.")
111
+
112
+ def placeholder(self, x):
113
+ """
114
+ Place holder
115
+ """
116
+
117
+ return False, x
@@ -0,0 +1,6 @@
1
+ """
2
+ Module that for utilities
3
+
4
+ """
5
+
6
+ DATESTR = "%Y-%m-%dT%H:%M:%S.%f" # Format for datetime strings compatible with SPICE