physpyx 3.1.0__tar.gz → 3.2.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.
- {physpyx-3.1.0 → physpyx-3.2.0}/PKG-INFO +1 -1
- {physpyx-3.1.0 → physpyx-3.2.0}/pyproject.toml +1 -1
- {physpyx-3.1.0 → physpyx-3.2.0}/pyproject.toml.orig +1 -1
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/astronomy.py +6 -5
- physpyx-3.2.0/src/physpyx/tikz.py +196 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/README.md +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/__init__.py +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/constants.py +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/context.py +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/data/all-nuclides.csv +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/helper.py +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/manager.py +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/nuclide.py +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/physpyx.py +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/qty.py +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/quantity.py +0 -0
- {physpyx-3.1.0 → physpyx-3.2.0}/src/physpyx/registry.py +0 -0
|
@@ -40,6 +40,11 @@ class Orbit:
|
|
|
40
40
|
else:
|
|
41
41
|
raise ValueError
|
|
42
42
|
|
|
43
|
+
if self.a < self.b:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
"The semi-major axis should be bigger or equal to the semi-minor axis (a>=b)"
|
|
46
|
+
)
|
|
47
|
+
|
|
43
48
|
self.c = self.a * self.e # linear eccentricity
|
|
44
49
|
self.p = self.a * (1.0 - self.e * self.e) # semi-latus
|
|
45
50
|
self.T = 2 * np.pi * self.a * np.sqrt(self.a / self.mu) # period
|
|
@@ -69,11 +74,7 @@ class Orbit:
|
|
|
69
74
|
r = self.p / (1 + self.e * np.cos(self.theta))
|
|
70
75
|
self.x = r * np.array([np.cos(self.theta), np.sin(self.theta)]) + self.v * dt
|
|
71
76
|
|
|
72
|
-
def run(
|
|
73
|
-
self,
|
|
74
|
-
steps: int,
|
|
75
|
-
every: int = 1,
|
|
76
|
-
) -> Iterator[NDArray]:
|
|
77
|
+
def run(self, steps: int, every: int = 1) -> Iterator[NDArray]:
|
|
77
78
|
if steps % every:
|
|
78
79
|
raise ValueError(f"{every} is not a divisor {steps} steps")
|
|
79
80
|
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from collections.abc import Generator
|
|
2
|
+
from enum import IntEnum
|
|
3
|
+
from typing import Self
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Coordinate:
|
|
9
|
+
def __init__(self, x: float, y: float):
|
|
10
|
+
self.x = x
|
|
11
|
+
self.y = y
|
|
12
|
+
|
|
13
|
+
def norm(self) -> float:
|
|
14
|
+
return np.sqrt(self.x * self.x + self.y * self.y)
|
|
15
|
+
|
|
16
|
+
def orientation(self) -> float:
|
|
17
|
+
return np.atan2(self.y, self.x) * 180 / np.pi
|
|
18
|
+
|
|
19
|
+
def __isub__(self, other: "Coordinate") -> Self:
|
|
20
|
+
self.x -= other.x
|
|
21
|
+
self.y -= other.y
|
|
22
|
+
return self
|
|
23
|
+
|
|
24
|
+
def __sub__(self, other: "Coordinate") -> "Coordinate":
|
|
25
|
+
return Coordinate(self.x - other.x, self.y - other.y)
|
|
26
|
+
|
|
27
|
+
def __iadd__(self, other: "Coordinate") -> Self:
|
|
28
|
+
self.x += other.x
|
|
29
|
+
self.y += other.y
|
|
30
|
+
return self
|
|
31
|
+
|
|
32
|
+
def __mul__(self, number: float) -> "Coordinate":
|
|
33
|
+
return Coordinate(self.x * number, self.y * number)
|
|
34
|
+
|
|
35
|
+
__rmul__ = __mul__
|
|
36
|
+
|
|
37
|
+
def __truediv__(self, number: float) -> "Coordinate":
|
|
38
|
+
return Coordinate(self.x / number, self.y / number)
|
|
39
|
+
|
|
40
|
+
def __add__(self, other: "Coordinate") -> "Coordinate":
|
|
41
|
+
return Coordinate(self.x + other.x, self.y + other.y)
|
|
42
|
+
|
|
43
|
+
def dot(self, other: "Coordinate") -> float:
|
|
44
|
+
return self.x * other.x + self.y * other.y
|
|
45
|
+
|
|
46
|
+
def __str__(self) -> str:
|
|
47
|
+
return f"({self.x}, {self.y})"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Rectangle:
|
|
51
|
+
def __init__(self, c1: Coordinate, c2: Coordinate):
|
|
52
|
+
self.c1 = c1
|
|
53
|
+
self.c2 = c2
|
|
54
|
+
|
|
55
|
+
def __str__(self) -> str:
|
|
56
|
+
return f"{self.c1} rectangle {self.c2}"
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def center(self) -> Coordinate:
|
|
60
|
+
return 0.5 * self.c1 + 0.5 * self.c2
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def north(self) -> Coordinate:
|
|
64
|
+
return Coordinate(0.5 * self.c1.x + 0.5 * self.c2.x, max(self.c1.y, self.c2.y))
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def south(self) -> Coordinate:
|
|
68
|
+
return Coordinate(0.5 * self.c1.x + 0.5 * self.c2.x, min(self.c1.y, self.c2.y))
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def east(self) -> Coordinate:
|
|
72
|
+
return Coordinate(max(self.c1.x, self.c2.x), 0.5 * self.c1.y + 0.5 * self.c2.y)
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def west(self) -> Coordinate:
|
|
76
|
+
return Coordinate(min(self.c1.x, self.c2.x), 0.5 * self.c1.y + 0.5 * self.c2.y)
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def north_east(self) -> Coordinate:
|
|
80
|
+
return Coordinate(max(self.c1.x, self.c2.x), max(self.c1.y, self.c2.y))
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def north_west(self) -> Coordinate:
|
|
84
|
+
return Coordinate(min(self.c1.x, self.c2.x), max(self.c1.y, self.c2.y))
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def south_west(self) -> Coordinate:
|
|
88
|
+
return Coordinate(min(self.c1.x, self.c2.x), min(self.c1.y, self.c2.y))
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def south_east(self) -> Coordinate:
|
|
92
|
+
return Coordinate(max(self.c1.x, self.c2.x), min(self.c1.y, self.c2.y))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Circle:
|
|
96
|
+
class LR(IntEnum):
|
|
97
|
+
LEFT = -1
|
|
98
|
+
RIGHT = 1
|
|
99
|
+
|
|
100
|
+
class IO(IntEnum):
|
|
101
|
+
OUTER = -1
|
|
102
|
+
INNER = 1
|
|
103
|
+
|
|
104
|
+
class ORIENT(IntEnum):
|
|
105
|
+
DEFAULT = 0
|
|
106
|
+
CLOCKWISE = -1
|
|
107
|
+
COUNTER_CLOCKWISE = 1
|
|
108
|
+
|
|
109
|
+
def __init__(self, center: Coordinate, radius: float):
|
|
110
|
+
self.center = center
|
|
111
|
+
self.radius = radius
|
|
112
|
+
|
|
113
|
+
def __str__(self) -> str:
|
|
114
|
+
return f"{self.center} circle ({self.radius})"
|
|
115
|
+
|
|
116
|
+
def tangent_points_to_circle(
|
|
117
|
+
self, circ: "Circle", lr: LR, io: IO
|
|
118
|
+
) -> tuple[Coordinate, Coordinate]:
|
|
119
|
+
D = circ.center - self.center
|
|
120
|
+
d = D.norm()
|
|
121
|
+
R = (circ.radius + io * self.radius) / d
|
|
122
|
+
e1 = D / d
|
|
123
|
+
e2 = Coordinate(-e1.y, e1.x)
|
|
124
|
+
|
|
125
|
+
ab = R * e1 + lr * np.sqrt(1 - R * R) * e2
|
|
126
|
+
c = -io * self.radius - ab.dot(self.center)
|
|
127
|
+
return (
|
|
128
|
+
self.center - ab * (ab.dot(self.center) + c),
|
|
129
|
+
circ.center - ab * (ab.dot(circ.center) + c),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def tangent_points_passing_by(self, P: Coordinate, lr: LR) -> Coordinate:
|
|
133
|
+
D = P - self.center
|
|
134
|
+
d = D.norm()
|
|
135
|
+
e1 = D / d
|
|
136
|
+
e2 = Coordinate(-e1.y, e1.x)
|
|
137
|
+
a = self.radius * self.radius / d * e1
|
|
138
|
+
b = self.radius / d * np.sqrt(d * d - self.radius * self.radius) * e2
|
|
139
|
+
return self.center + a + lr * b
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class Pulley(Circle):
|
|
143
|
+
def to_tex(self) -> Generator[str]:
|
|
144
|
+
yield rf"\fill[black!30] {self};"
|
|
145
|
+
yield rf"\draw[black!50, line width={2 * self.radius}] {Circle(self.center, 0.85 * self.radius)};"
|
|
146
|
+
yield rf"\fill[black!70] {Circle(self.center, 0.25 * self.radius)};"
|
|
147
|
+
shift = np.random.random() * np.pi / 3
|
|
148
|
+
for i in range(6):
|
|
149
|
+
c = self.center + Coordinate(
|
|
150
|
+
0.6 * self.radius * np.sin(i * np.pi / 3 + shift),
|
|
151
|
+
0.6 * self.radius * np.cos(i * np.pi / 3 + shift),
|
|
152
|
+
)
|
|
153
|
+
yield rf"\fill[black!60]{Circle(c, 0.1 * self.radius)};"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class Rope:
|
|
157
|
+
def __init__(self, start: Coordinate):
|
|
158
|
+
self.current = start
|
|
159
|
+
self._path = f"{start}"
|
|
160
|
+
|
|
161
|
+
def __str__(self) -> str:
|
|
162
|
+
return self._path
|
|
163
|
+
|
|
164
|
+
def to_pulley(self, pulley: Pulley, lr: Circle.LR) -> None:
|
|
165
|
+
self.current = pulley.tangent_points_passing_by(self.current, lr)
|
|
166
|
+
self._path += f" -- {self.current}"
|
|
167
|
+
|
|
168
|
+
def _compute_angles(
|
|
169
|
+
self, a: Coordinate, b: Coordinate, o: Circle.ORIENT
|
|
170
|
+
) -> tuple[float, float]:
|
|
171
|
+
return (self.current - a).orientation(), (b - a).orientation() + o * 360
|
|
172
|
+
|
|
173
|
+
def from_pulley(
|
|
174
|
+
self,
|
|
175
|
+
pulley: Pulley,
|
|
176
|
+
to: Coordinate,
|
|
177
|
+
lr: Circle.LR,
|
|
178
|
+
orientation: Circle.ORIENT = Circle.ORIENT.DEFAULT,
|
|
179
|
+
) -> None:
|
|
180
|
+
tmp = pulley.tangent_points_passing_by(to, lr)
|
|
181
|
+
angle_i, angle_o = self._compute_angles(pulley.center, tmp, orientation)
|
|
182
|
+
self._path += f" arc ({angle_i}:{angle_o}:{pulley.radius}) -- {to}"
|
|
183
|
+
self.current = to
|
|
184
|
+
|
|
185
|
+
def from_pulley_to_pulley(
|
|
186
|
+
self,
|
|
187
|
+
P1: Pulley,
|
|
188
|
+
P2: Pulley,
|
|
189
|
+
lr: Circle.LR,
|
|
190
|
+
io: Circle.IO,
|
|
191
|
+
orientation: Circle.ORIENT = Circle.ORIENT.DEFAULT,
|
|
192
|
+
) -> None:
|
|
193
|
+
t1, t2 = P1.tangent_points_to_circle(P2, lr, io)
|
|
194
|
+
angle_i, angle_o = self._compute_angles(P1.center, t1, orientation)
|
|
195
|
+
self._path += f" arc ({angle_i}:{angle_o}:{P1.radius}) -- {t2}"
|
|
196
|
+
self.current = t2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|