abstract-math 0.0.0.17__py3-none-any.whl → 0.0.0.18__py3-none-any.whl
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.
Potentially problematic release.
This version of abstract-math might be problematic. Click here for more details.
- abstract_math/__init__.py +1 -0
- abstract_math/flask_scripts/__init__.py +1 -0
- abstract_math/flask_scripts/flask_utils.py +664 -0
- abstract_math/flask_scripts/make_flask.py +1 -0
- abstract_math/solar_math/__init__.py +2 -0
- abstract_math/solar_math/flask_utils.py +11 -0
- abstract_math/solar_math/main.py +263 -0
- abstract_math/solar_math/src/__init__.py +4 -0
- abstract_math/solar_math/src/constants/__init__.py +19 -0
- abstract_math/solar_math/src/constants/distance_constants.py +45 -0
- abstract_math/solar_math/src/constants/planet_constants.py +156 -0
- abstract_math/solar_math/src/constants/time_constants.py +56 -0
- abstract_math/solar_math/src/imports.py +5 -0
- abstract_math/solar_math/src/utils/__init__.py +3 -0
- abstract_math/solar_math/src/utils/escape_velocity.py +79 -0
- abstract_math/solar_math/src/utils/geometry_utils.py +104 -0
- abstract_math/solar_math/src/utils/velocity_utils.py +26 -0
- {abstract_math-0.0.0.17.dist-info → abstract_math-0.0.0.18.dist-info}/METADATA +1 -1
- abstract_math-0.0.0.18.dist-info/RECORD +23 -0
- {abstract_math-0.0.0.17.dist-info → abstract_math-0.0.0.18.dist-info}/WHEEL +1 -1
- abstract_math/deriveit.py +0 -97
- abstract_math-0.0.0.17.dist-info/RECORD +0 -8
- {abstract_math-0.0.0.17.dist-info → abstract_math-0.0.0.18.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from abstract_flask import *
|
|
2
|
+
from abstract_utilities import write_to_file
|
|
3
|
+
directory='/home/computron/Documents/pythonTools/modules/abstract_math/src/abstract_math/solar_math'
|
|
4
|
+
output_file='/home/computron/Documents/pythonTools/modules/abstract_math/src/abstract_math/flask_scripts/flask_utils.py'
|
|
5
|
+
output = generate_from_files(
|
|
6
|
+
directory=directory,
|
|
7
|
+
bp_name='math_data_bp',
|
|
8
|
+
url_prefix='api'
|
|
9
|
+
)
|
|
10
|
+
write_to_file(contents=output,file_path=output_file)
|
|
11
|
+
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# adapt_units_api.py (or wherever you glue this in)
|
|
2
|
+
from typing import *
|
|
3
|
+
from .src.constants.distance_constants import convert as dconvert, _factor as dfactor
|
|
4
|
+
from .src.constants.time_constants import seconds_per
|
|
5
|
+
from .src.constants.planet_constants import planet_radius, get_body, g_at_radius
|
|
6
|
+
from .src.utils.geometry_utils import visible_area_flat, visible_surface_angle
|
|
7
|
+
from .src.imports import math, mul, div, add # your abstract_math ops
|
|
8
|
+
def normalized_velocity_conversioin(starting_velocity, input_time, input_units):
|
|
9
|
+
sec_per_time = seconds_per(input_time) # sec / timeunit
|
|
10
|
+
v_units_per_sec = div(starting_velocity, sec_per_time) # <input_units> / s
|
|
11
|
+
v0_mps = dconvert(v_units_per_sec, input_units, "meters") # m / s
|
|
12
|
+
return v0_mps
|
|
13
|
+
|
|
14
|
+
# --- Analyzer (prints a scan; no blocking input) ---
|
|
15
|
+
def analyze_visible_surface(
|
|
16
|
+
altitude_step: float = 200.0,
|
|
17
|
+
max_steps: int = 20,
|
|
18
|
+
fov_range: tuple[int, int] = (20, 160),
|
|
19
|
+
fov_interval: int = 10,
|
|
20
|
+
input_units: str = 'meters', # how to interpret altitude numbers
|
|
21
|
+
display_units: str = 'meters', # how to print areas/radii
|
|
22
|
+
planet: str = 'earth',
|
|
23
|
+
printit: bool = False
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Scan altitudes/FOVs. Altitudes are interpreted in `input_units`.
|
|
27
|
+
Results are printed in `display_units`.
|
|
28
|
+
"""
|
|
29
|
+
# Planet radius and full area (compute in meters, convert for display)
|
|
30
|
+
r_m = planet_radius(planet, 'meters')
|
|
31
|
+
full_area_m2 = 4.0 * math.pi * (r_m ** 2)
|
|
32
|
+
k_disp = dfactor('meters', display_units) # linear meter->display factor
|
|
33
|
+
full_area_disp = full_area_m2 * (k_disp ** 2) # convert area to display units^2
|
|
34
|
+
|
|
35
|
+
all_stats = {"output": "", "input_units": input_units,
|
|
36
|
+
"display_units": display_units, "vars": []}
|
|
37
|
+
|
|
38
|
+
for i in range(1, max_steps + 1):
|
|
39
|
+
all_stats["vars"].append({})
|
|
40
|
+
altitude_in = altitude_step * i # input_units
|
|
41
|
+
altitude_m = dconvert(altitude_in, input_units, 'meters')
|
|
42
|
+
|
|
43
|
+
all_stats["vars"][-1]['altitude_input'] = altitude_in
|
|
44
|
+
all_stats["vars"][-1]['input_units'] = input_units
|
|
45
|
+
all_stats["vars"][-1]['fov'] = []
|
|
46
|
+
|
|
47
|
+
alt_pretty = dconvert(altitude_in, input_units, display_units)
|
|
48
|
+
all_stats["output"] += (
|
|
49
|
+
f"\n=== Altitude: {altitude_in} {input_units} (≈ {alt_pretty:.3f} {display_units}) ===\n"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
for fov in range(fov_range[0], fov_range[1] + 1, fov_interval):
|
|
53
|
+
# Compute visible area/radius in meters, convert to display units
|
|
54
|
+
area_m2, vis_radius_m = visible_area_flat(fov, altitude_m, 'meters')
|
|
55
|
+
area_disp = area_m2 * (k_disp ** 2)
|
|
56
|
+
vis_radius_disp = dconvert(vis_radius_m, 'meters', display_units)
|
|
57
|
+
|
|
58
|
+
# Span uses geometry only; r_m is in meters
|
|
59
|
+
_, angle_deg = visible_surface_angle(vis_radius_m, r_m)
|
|
60
|
+
|
|
61
|
+
coverage_pct = 100.0 * (area_disp / full_area_disp)
|
|
62
|
+
|
|
63
|
+
fov_output = (
|
|
64
|
+
f"FOV: {fov:>3}° | "
|
|
65
|
+
f"Area: {area_disp:>12.2f} {display_units}² | "
|
|
66
|
+
f"Span: {angle_deg:>7.2f}° | "
|
|
67
|
+
f"Flat-visible: {coverage_pct:>6.3f}% | "
|
|
68
|
+
f"visR≈{vis_radius_disp:.3f} {display_units}"
|
|
69
|
+
)
|
|
70
|
+
all_stats["output"] += fov_output + "\n"
|
|
71
|
+
|
|
72
|
+
all_stats["vars"][-1]['fov'].append({
|
|
73
|
+
"FOV": fov,
|
|
74
|
+
"area": area_disp,
|
|
75
|
+
"angle_deg": angle_deg,
|
|
76
|
+
"coverage_pct": coverage_pct,
|
|
77
|
+
"visible_radius": vis_radius_disp,
|
|
78
|
+
"output": fov_output
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
if printit:
|
|
82
|
+
print(all_stats.get('output'))
|
|
83
|
+
return all_stats
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def simulate_radial_flight(
|
|
87
|
+
planet: str,
|
|
88
|
+
start_altitude: float,
|
|
89
|
+
start_units: str,
|
|
90
|
+
v0_mps: float,
|
|
91
|
+
dt_s: float = 1.0,
|
|
92
|
+
max_steps: int = 5_000_000,
|
|
93
|
+
target_altitude_m: Optional[float] = None
|
|
94
|
+
) -> dict:
|
|
95
|
+
"""
|
|
96
|
+
Forward-Euler radial integrator (toy model).
|
|
97
|
+
Returns a dict with SI (meters, seconds) internal results.
|
|
98
|
+
"""
|
|
99
|
+
if not (isinstance(start_altitude, (int, float)) and start_altitude >= 0):
|
|
100
|
+
return {"ok": False, "error": "Invalid start_altitude", "steps": 0}
|
|
101
|
+
if not (isinstance(v0_mps, (int, float)) and math.isfinite(v0_mps)):
|
|
102
|
+
return {"ok": False, "error": "Invalid starting_velocity (after unit conversion)", "steps": 0}
|
|
103
|
+
if not (dt_s > 0):
|
|
104
|
+
return {"ok": False, "error": "dt_s must be > 0", "steps": 0}
|
|
105
|
+
|
|
106
|
+
body = get_body(planet)
|
|
107
|
+
mu = body["mu"]
|
|
108
|
+
R = body["radius"]
|
|
109
|
+
|
|
110
|
+
r0 = add(R, dconvert(start_altitude, start_units, "meters"))
|
|
111
|
+
r = r0
|
|
112
|
+
v = v0_mps # outward positive, m/s
|
|
113
|
+
t = 0.0
|
|
114
|
+
|
|
115
|
+
has_target = target_altitude_m is not None and target_altitude_m > 0
|
|
116
|
+
r_target = add(R, target_altitude_m if has_target else float("inf"))
|
|
117
|
+
|
|
118
|
+
hit_surface = False
|
|
119
|
+
hit_target = False
|
|
120
|
+
turned_back_below_start = False
|
|
121
|
+
|
|
122
|
+
steps = 0
|
|
123
|
+
for _ in range(max_steps):
|
|
124
|
+
if r <= R:
|
|
125
|
+
hit_surface = True
|
|
126
|
+
break
|
|
127
|
+
if has_target and r >= r_target:
|
|
128
|
+
hit_target = True
|
|
129
|
+
break
|
|
130
|
+
|
|
131
|
+
a = - div(mu, mul(r, r)) # inward
|
|
132
|
+
v = add(v, mul(a, dt_s))
|
|
133
|
+
r = add(r, mul(v, dt_s))
|
|
134
|
+
t = add(t, dt_s)
|
|
135
|
+
steps += 1
|
|
136
|
+
|
|
137
|
+
if (not has_target) and (v < 0) and (r < r0):
|
|
138
|
+
turned_back_below_start = True
|
|
139
|
+
break
|
|
140
|
+
|
|
141
|
+
altitude_m = max(0.0, r - R)
|
|
142
|
+
g_end = g_at_radius(mu, r)
|
|
143
|
+
g_surface = g_at_radius(mu, R)
|
|
144
|
+
traveled_m = max(0.0, altitude_m - (r0 - R))
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
"ok": True,
|
|
148
|
+
"planet": planet,
|
|
149
|
+
"rFromCenter_m": r,
|
|
150
|
+
"altitude_m": altitude_m,
|
|
151
|
+
"vEnd_mps": v,
|
|
152
|
+
"time_s": t,
|
|
153
|
+
"gAtEnd_mps2": g_end,
|
|
154
|
+
"gRatioSurface": div(g_end, g_surface),
|
|
155
|
+
"steps": steps,
|
|
156
|
+
"hitSurface": hit_surface,
|
|
157
|
+
"hitTarget": hit_target,
|
|
158
|
+
"turnedBackBelowStart": turned_back_below_start,
|
|
159
|
+
"traveled_m": traveled_m,
|
|
160
|
+
"vEsc_end_mps": math.sqrt(mul(2.0, div(mu, r))),
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def radial_travel(
|
|
165
|
+
start_distance: float,
|
|
166
|
+
starting_velocity: float,
|
|
167
|
+
input_units: str = "meters", # distance part of starting_velocity & start_distance
|
|
168
|
+
input_time: str = "s", # time part of starting_velocity
|
|
169
|
+
output_units: str = "meters",
|
|
170
|
+
output_time: str = "s",
|
|
171
|
+
planet: str = "earth",
|
|
172
|
+
*,
|
|
173
|
+
dt_s: float = 1.0,
|
|
174
|
+
max_steps: int = 5_000_000,
|
|
175
|
+
target_distance: Optional[float] = None # in input_units above surface
|
|
176
|
+
) -> dict:
|
|
177
|
+
"""
|
|
178
|
+
Single-call wrapper:
|
|
179
|
+
|
|
180
|
+
- start_distance: altitude above surface (input_units)
|
|
181
|
+
- starting_velocity: <input_units>/<input_time>
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
v0_mps = normalized_velocity_conversioin(starting_velocity, input_time, input_units)
|
|
185
|
+
|
|
186
|
+
# Optional target altitude (to meters)
|
|
187
|
+
target_alt_m = None
|
|
188
|
+
if target_distance is not None:
|
|
189
|
+
target_alt_m = dconvert(target_distance, input_units, "meters")
|
|
190
|
+
|
|
191
|
+
# Integrate in SI
|
|
192
|
+
sim = simulate_radial_flight(
|
|
193
|
+
planet=planet,
|
|
194
|
+
start_altitude=start_distance,
|
|
195
|
+
start_units=input_units,
|
|
196
|
+
v0_mps=v0_mps,
|
|
197
|
+
dt_s=dt_s,
|
|
198
|
+
max_steps=max_steps,
|
|
199
|
+
target_altitude_m=target_alt_m
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
if not sim.get("ok", False):
|
|
203
|
+
return sim
|
|
204
|
+
|
|
205
|
+
# Distances for output
|
|
206
|
+
alt_out = dconvert(sim["altitude_m"], "meters", output_units)
|
|
207
|
+
r_out = dconvert(sim["rFromCenter_m"], "meters", output_units)
|
|
208
|
+
trav_out = dconvert(sim["traveled_m"], "meters", output_units)
|
|
209
|
+
|
|
210
|
+
# Velocity output: (output_units)/(output_time)
|
|
211
|
+
sec_per_out = seconds_per(output_time) # sec / out_timeunit
|
|
212
|
+
v_units_per_sec_out = dconvert(sim["vEnd_mps"], "meters", output_units)
|
|
213
|
+
v_out = mul(v_units_per_sec_out, sec_per_out)
|
|
214
|
+
|
|
215
|
+
# Escape velocity at end
|
|
216
|
+
vesc_units_per_sec_out = dconvert(sim["vEsc_end_mps"], "meters", output_units)
|
|
217
|
+
vesc_end_out = mul(vesc_units_per_sec_out, sec_per_out)
|
|
218
|
+
|
|
219
|
+
# Escape velocity at destination (if provided)
|
|
220
|
+
body = get_body(planet)
|
|
221
|
+
mu = body["mu"]; R = body["radius"]
|
|
222
|
+
destination_radius_m = None
|
|
223
|
+
vesc_dest_out = None
|
|
224
|
+
if target_alt_m is not None:
|
|
225
|
+
destination_radius_m = add(R, target_alt_m)
|
|
226
|
+
vesc_dest_mps = math.sqrt(mul(2.0, div(mu, destination_radius_m)))
|
|
227
|
+
vesc_dest_units_per_sec = dconvert(vesc_dest_mps, "meters", output_units)
|
|
228
|
+
vesc_dest_out = mul(vesc_dest_units_per_sec, sec_per_out)
|
|
229
|
+
|
|
230
|
+
# Time output in requested unit
|
|
231
|
+
t_out = div(sim["time_s"], sec_per_out)
|
|
232
|
+
t_label = output_time if output_time in ("s", "sec", "seconds", "m", "min", "minutes", "h", "hr", "hours") else "s"
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
"ok": True,
|
|
236
|
+
"planet": planet,
|
|
237
|
+
"inputs": {
|
|
238
|
+
"start_distance": start_distance,
|
|
239
|
+
"starting_velocity": starting_velocity,
|
|
240
|
+
"input_units": input_units,
|
|
241
|
+
"input_time": input_time,
|
|
242
|
+
"output_units": output_units,
|
|
243
|
+
"output_time": output_time,
|
|
244
|
+
"target_distance": target_distance,
|
|
245
|
+
},
|
|
246
|
+
"altitude": alt_out, # in output_units
|
|
247
|
+
"radius_from_center": r_out, # in output_units
|
|
248
|
+
"distance_traveled": trav_out, # in output_units
|
|
249
|
+
"velocity": v_out, # in output_units / output_time
|
|
250
|
+
"velocity_escape_end": vesc_end_out, # same units/time as velocity
|
|
251
|
+
"velocity_escape_destination": vesc_dest_out,
|
|
252
|
+
"destination_radius": (
|
|
253
|
+
dconvert(destination_radius_m, "meters", output_units) if destination_radius_m is not None else None
|
|
254
|
+
),
|
|
255
|
+
"time": t_out, # in output_time units
|
|
256
|
+
"time_unit": t_label,
|
|
257
|
+
"g_end_mps2": sim["gAtEnd_mps2"], # keep SI precise for g
|
|
258
|
+
"g_ratio_surface": sim["gRatioSurface"],
|
|
259
|
+
"steps": sim["steps"],
|
|
260
|
+
"hit_surface": sim["hitSurface"],
|
|
261
|
+
"hit_target": sim["hitTarget"],
|
|
262
|
+
"turned_back_below_start": sim["turnedBackBelowStart"],
|
|
263
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# src/constants/__init__.py
|
|
2
|
+
from .distance_constants import (
|
|
3
|
+
DISTANCE_CONVERSIONS, ALL_DISTANCE_UNITS,
|
|
4
|
+
normalize_distance_unit, get_distance_unit_conversions,
|
|
5
|
+
_factor, convert,
|
|
6
|
+
)
|
|
7
|
+
from .time_constants import (
|
|
8
|
+
TIME_CONVERSIONS, ALL_TIME_UNITS,
|
|
9
|
+
normalize_time_unit, get_time_unit_conversions,
|
|
10
|
+
time_factor, convert_time, seconds_per,
|
|
11
|
+
)
|
|
12
|
+
from .planet_constants import (
|
|
13
|
+
PLANETS, G, g0, MU_MOON,
|
|
14
|
+
get_planet_vars, planet_radius, planet_diameter,
|
|
15
|
+
full_planet_surface_area, planet_volume, planet_circumference,
|
|
16
|
+
planet_mass, planet_surface_g, escape_velocity,
|
|
17
|
+
earth_radius, earth_diameter, full_earth_surface_area,
|
|
18
|
+
earth_volume, earth_circumference,
|
|
19
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#src/constants/distaance_constants.py
|
|
2
|
+
from ..imports import *
|
|
3
|
+
# -------------------------
|
|
4
|
+
# distance Unit schema
|
|
5
|
+
# -------------------------
|
|
6
|
+
MILES = {"strings": ['mi', 'mile', 'miles'],
|
|
7
|
+
"conv": {"miles": 1.0, "meters": 1609.34, "kilometers": 1.60934, 'feet': 5280.0}}
|
|
8
|
+
METER = {"strings": ['m', 'meter', 'meters'],
|
|
9
|
+
"conv": {"miles": 0.00621371, "meters": 1.0, "kilometers": 0.001, 'feet': 3.28084}}
|
|
10
|
+
KILOMETER = {"strings": ['km', 'kilo', 'kilometer', 'kilometers'],
|
|
11
|
+
"conv": {"miles": 0.621371, "meters": 1000.0, "kilometers": 1.0, 'feet': 3280.84}}
|
|
12
|
+
FEET = {"strings": ['ft', 'foot', 'feet', 'foots', 'feets'],
|
|
13
|
+
"conv": {"miles": 0.000189394, "meters": 0.3048, "kilometers": 0.0003048, 'feet': 1.0}}
|
|
14
|
+
|
|
15
|
+
DISTANCE_CONVERSIONS: Dict[str, Dict[str, Dict[str, float]]] = {
|
|
16
|
+
"miles": MILES,
|
|
17
|
+
"meters": METER,
|
|
18
|
+
"kilometers": KILOMETER,
|
|
19
|
+
"feet": FEET
|
|
20
|
+
}
|
|
21
|
+
ALL_DISTANCE_UNITS = ("meters", "kilometers", "miles", "feet")
|
|
22
|
+
# -------------------------
|
|
23
|
+
# Unit helpers
|
|
24
|
+
# -------------------------
|
|
25
|
+
def normalize_distance_unit(unit: str) -> str:
|
|
26
|
+
u = unit.strip().lower()
|
|
27
|
+
if u in DISTANCE_CONVERSIONS:
|
|
28
|
+
return u
|
|
29
|
+
for key, values in DISTANCE_CONVERSIONS.items():
|
|
30
|
+
if u in values.get("strings", []):
|
|
31
|
+
return key
|
|
32
|
+
# was: CONVERSIONS
|
|
33
|
+
raise ValueError(f"Unknown unit '{unit}'. Supported: {list(DISTANCE_CONVERSIONS.keys())}")
|
|
34
|
+
|
|
35
|
+
def get_distance_unit_conversions(unit: str) -> Dict[str, Dict[str, float]]:
|
|
36
|
+
return DISTANCE_CONVERSIONS[normalize_distance_unit(unit)]
|
|
37
|
+
|
|
38
|
+
def _factor(unit_from: str, unit_to: str) -> float:
|
|
39
|
+
"""Multiplicative factor s.t. value_in_to = value_in_from * factor."""
|
|
40
|
+
uf = get_distance_unit_conversions(unit_from)["conv"]["meters"] # meters per 1 from-unit
|
|
41
|
+
ut = get_distance_unit_conversions(unit_to)["conv"]["meters"] # meters per 1 to-unit
|
|
42
|
+
return div(uf, ut)
|
|
43
|
+
|
|
44
|
+
def convert(value: float, unit_from: str, unit_to: str) -> float:
|
|
45
|
+
return mul(value, _factor(unit_from, unit_to))
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# src/constants/planet_constants.py
|
|
2
|
+
from ..imports import * # <-- add this line
|
|
3
|
+
from .distance_constants import *
|
|
4
|
+
from .time_constants import *
|
|
5
|
+
# -------------------------
|
|
6
|
+
# Physical constants (SI)
|
|
7
|
+
# -------------------------
|
|
8
|
+
G = 6.67430e-11 # m^3 kg^-1 s^-2
|
|
9
|
+
g0 = 9.80665 # m/s^2
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
MU_MOON
|
|
13
|
+
except NameError:
|
|
14
|
+
MU_MOON = 4.9048695e12 # m^3/s^2
|
|
15
|
+
|
|
16
|
+
# -------------------------
|
|
17
|
+
# Bodies (your data)
|
|
18
|
+
# -------------------------
|
|
19
|
+
PLANETS = [
|
|
20
|
+
{ "name":'Sun',"m0_deg":0,"mu":1.32712440018e20,"a":0,"e":0,"radiusPx":20,"color":'yellow',"radius":696000000,"escapeVel":61770,"n":0,"peri_lon_deg":0 },
|
|
21
|
+
{ "name":'Mercury',"m0_deg":252.25032350,"mu":2.20320e13,"a":5.7909927e10,"e":0.20563593,"radiusPx":4,"color":'#a6a6a6',"radius":2440000,"escapeVel":4300,"n":1,"peri_lon_deg":77.45779628 },
|
|
22
|
+
{ "name":'Venus',"m0_deg":181.97909950,"mu":3.24859e14,"a":1.0820948e11,"e":0.00677672,"radiusPx":7,"color":'#e0c16b',"radius":6052000,"escapeVel":10400,"n":2,"peri_lon_deg":131.60246718 },
|
|
23
|
+
{ "name":'Earth',"m0_deg":100.46457166,"mu":3.98600e14,"a":1.49598261e11,"e":0.01671123,"radiusPx":8,"color":'#4e6ef2',"radius":6371000,"escapeVel":11200,"n":3,"peri_lon_deg":102.93768193 },
|
|
24
|
+
{ "name":'Mars',"m0_deg":-4.55343205,"mu":4.28284e13,"a":2.2793664e11,"e":0.09339410,"radiusPx":6,"color":'#c1440e',"radius":3390000,"escapeVel":5030,"n":4,"peri_lon_deg":-23.94362959 },
|
|
25
|
+
{ "name":'Jupiter',"m0_deg":34.39644051,"mu":1.26687e17,"a":7.7841200e11,"e":0.04838624,"radiusPx":14,"color":'#d2a679',"radius":71492000,"escapeVel":59500,"n":5,"peri_lon_deg":14.72847983 },
|
|
26
|
+
{ "name":'Saturn',"m0_deg":49.95424423,"mu":3.79312e16,"a":1.4267254e12,"e":0.05386179,"radiusPx":12,"color":'#e3c168',"radius":60268000,"escapeVel":35500,"n":6,"peri_lon_deg":92.59887831 },
|
|
27
|
+
{ "name":'Uranus',"m0_deg":313.23810451,"mu":5.79394e15,"a":2.8706582e12,"e":0.04725744,"radiusPx":10,"color":'#7fbde8',"radius":25559000,"escapeVel":21300,"n":7,"peri_lon_deg":170.95427630 },
|
|
28
|
+
{ "name":'Neptune',"m0_deg":-55.12002969,"mu":6.83653e15,"a":4.4983964e12,"e":0.00859048,"radiusPx":10,"color":'#4363d8',"radius":24764000,"escapeVel":23500,"n":8,"peri_lon_deg":44.96476227 },
|
|
29
|
+
{ "name":'Moon',"m0_deg":0,"mu":MU_MOON,"a":3.844e8,"e":0.0549,"radiusPx":5,"color":'lightgray',"radius":1.737e6,"escapeVel":2380,"n":9}
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
# -------------------------
|
|
33
|
+
# Body enrichment + lookup
|
|
34
|
+
# -------------------------
|
|
35
|
+
def _enrich_body(b: Dict[str, Any]) -> Dict[str, Any]:
|
|
36
|
+
"""Add derived diameter, mass, surface g."""
|
|
37
|
+
mu = b["mu"]
|
|
38
|
+
r = b["radius"]
|
|
39
|
+
if "diameter" not in b or not b["diameter"]:
|
|
40
|
+
b["diameter"] = mul(2.0, r)
|
|
41
|
+
b["mass"] = div(mu, G)
|
|
42
|
+
surf_g = div(mu, mul(r, r))
|
|
43
|
+
b["surface_g"] = surf_g
|
|
44
|
+
b["surface_g_g0"] = div(surf_g, g0)
|
|
45
|
+
return b
|
|
46
|
+
|
|
47
|
+
_NAME_ALIASES = {"sol": "sun", "terra": "earth", "luna": "moon"}
|
|
48
|
+
def _normalize_name(name: str) -> str:
|
|
49
|
+
n = name.strip().lower()
|
|
50
|
+
return _NAME_ALIASES.get(n, n)
|
|
51
|
+
|
|
52
|
+
_BODY_BY_NAME: Dict[str, Dict[str, Any]] = {}
|
|
53
|
+
for entry in PLANETS:
|
|
54
|
+
_BODY_BY_NAME[entry["name"].lower()] = _enrich_body(dict(entry))
|
|
55
|
+
|
|
56
|
+
# -------------------------
|
|
57
|
+
# Public API
|
|
58
|
+
# -------------------------
|
|
59
|
+
def get_planet_vars(name: str, units: str = "meters") -> Dict[str, Any]:
|
|
60
|
+
"""
|
|
61
|
+
Return body properties with radius/diameter in `units`.
|
|
62
|
+
Mass in kg; mu in m^3/s^2; surface_g in m/s^2.
|
|
63
|
+
"""
|
|
64
|
+
key = _normalize_name(name)
|
|
65
|
+
body = _BODY_BY_NAME.get(key)
|
|
66
|
+
if body is None:
|
|
67
|
+
raise KeyError(f"Unknown body '{name}'. Available: {sorted(_BODY_BY_NAME.keys())}")
|
|
68
|
+
|
|
69
|
+
units_norm = normalize_distance_unit(units)
|
|
70
|
+
r_m = body["radius"]
|
|
71
|
+
d_m = body["diameter"]
|
|
72
|
+
|
|
73
|
+
out = dict(body)
|
|
74
|
+
out["radius"] = convert(r_m, "meters", units_norm)
|
|
75
|
+
out["diameter"] = convert(d_m, "meters", units_norm)
|
|
76
|
+
out["radius_units"] = units_norm
|
|
77
|
+
out["diameter_units"] = units_norm
|
|
78
|
+
return out
|
|
79
|
+
|
|
80
|
+
def planet_radius(name: str = "earth", units: str = "meters") -> float:
|
|
81
|
+
return get_planet_vars(name, units)["radius"]
|
|
82
|
+
|
|
83
|
+
def planet_diameter(name: str = "earth", units: str = "meters") -> float:
|
|
84
|
+
return get_planet_vars(name, units)["diameter"]
|
|
85
|
+
|
|
86
|
+
def full_planet_surface_area(name: str = "earth", units: str = 'meters') -> float:
|
|
87
|
+
r = planet_radius(name,units)
|
|
88
|
+
return mul(4 * pi(), exp(r, 2))
|
|
89
|
+
|
|
90
|
+
def planet_volume(name: str = "earth", units: str = 'meters') -> float:
|
|
91
|
+
r = planet_radius(name,units)
|
|
92
|
+
return mul((4.0/3.0) * pi(), exp(r, 3))
|
|
93
|
+
|
|
94
|
+
def planet_circumference(name: str = "earth", units: str = 'meters') -> float:
|
|
95
|
+
r = planet_radius(name,units)
|
|
96
|
+
return mul(2 * pi(), r)
|
|
97
|
+
|
|
98
|
+
def planet_mass(name: str = "earth") -> float:
|
|
99
|
+
return get_planet_vars(name)["mass"]
|
|
100
|
+
|
|
101
|
+
def planet_surface_g(name: str = "earth", as_g0: bool = False) -> float:
|
|
102
|
+
v = get_planet_vars(name)["surface_g"]
|
|
103
|
+
return div(v, g0) if as_g0 else v
|
|
104
|
+
|
|
105
|
+
def escape_velocity(name: str = "earth", altitude: float = 0.0, units: str = "meters") -> float:
|
|
106
|
+
"""
|
|
107
|
+
Escape velocity (m/s) from altitude above surface.
|
|
108
|
+
"""
|
|
109
|
+
mu = _BODY_BY_NAME[_normalize_name(name)]["mu"]
|
|
110
|
+
r = _BODY_BY_NAME[_normalize_name(name)]["radius"] # meters
|
|
111
|
+
h_m = convert(altitude, units, "meters")
|
|
112
|
+
R = add(r, h_m)
|
|
113
|
+
return math.sqrt(mul(2.0, div(mu, R)))
|
|
114
|
+
|
|
115
|
+
# -------------------------
|
|
116
|
+
# Earth-centric geometry utils (unit-consistent)
|
|
117
|
+
# -------------------------
|
|
118
|
+
def pi() -> float:
|
|
119
|
+
return math.pi
|
|
120
|
+
|
|
121
|
+
def earth_radius(units: str = 'meters') -> float:
|
|
122
|
+
return planet_radius('earth', units)
|
|
123
|
+
|
|
124
|
+
def earth_diameter(units: str = 'meters') -> float:
|
|
125
|
+
return planet_diameter('earth', units)
|
|
126
|
+
|
|
127
|
+
def full_earth_surface_area(units: str = 'meters') -> float:
|
|
128
|
+
r = earth_radius(units)
|
|
129
|
+
return mul(4 * pi(), exp(r, 2))
|
|
130
|
+
|
|
131
|
+
def earth_volume(units: str = 'meters') -> float:
|
|
132
|
+
r = earth_radius(units)
|
|
133
|
+
return mul((4.0/3.0) * pi(), exp(r, 3))
|
|
134
|
+
|
|
135
|
+
def earth_circumference(units: str = 'meters') -> float:
|
|
136
|
+
r = earth_radius(units)
|
|
137
|
+
return mul(2 * pi(), r)
|
|
138
|
+
|
|
139
|
+
# =========================
|
|
140
|
+
# Radial toy + single-call wrapper
|
|
141
|
+
# =========================
|
|
142
|
+
def distance_per_sec_to_mps(v_per_sec: float, units: str) -> float:
|
|
143
|
+
"""Convert a speed given in `units`/s into m/s using your convert()."""
|
|
144
|
+
# v [units/s] * (meters per 1 units) => [m/s]
|
|
145
|
+
return mul(v_per_sec, get_distance_unit_conversions(_normalize_unit(units))["conv"]["meters"])
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_body(planet: str) -> Dict[str, Any]:
|
|
149
|
+
key = _normalize_name(planet)
|
|
150
|
+
body = _BODY_BY_NAME.get(key)
|
|
151
|
+
if not body:
|
|
152
|
+
raise KeyError(f"Unknown body '{planet}'. Available: {sorted(_BODY_BY_NAME.keys())}")
|
|
153
|
+
return body
|
|
154
|
+
|
|
155
|
+
def g_at_radius(mu: float, r_m: float) -> float:
|
|
156
|
+
return div(mu, mul(r_m, r_m))
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#src/constants/time_constants.py
|
|
2
|
+
# --- Time unit schema (seconds-per-unit) ---
|
|
3
|
+
second = 1.0
|
|
4
|
+
minute = 60.0 * second
|
|
5
|
+
hour = 60.0 * minute
|
|
6
|
+
day = 24.0 * hour
|
|
7
|
+
|
|
8
|
+
SECONDS = {"strings": ['s', 'sec', 'secs', 'second', 'seconds'],
|
|
9
|
+
"conv": {"seconds": 1.0}}
|
|
10
|
+
MINUTES = {"strings": ['min', 'mins', 'minute', 'minutes'],
|
|
11
|
+
"conv": {"seconds": minute}}
|
|
12
|
+
HOURS = {"strings": ['h', 'hr', 'hrs', 'hour', 'hours'],
|
|
13
|
+
"conv": {"seconds": hour}}
|
|
14
|
+
DAYS = {"strings": ['d', 'day', 'days'],
|
|
15
|
+
"conv": {"seconds": day}}
|
|
16
|
+
|
|
17
|
+
TIME_CONVERSIONS = {
|
|
18
|
+
"seconds": SECONDS,
|
|
19
|
+
"minutes": MINUTES,
|
|
20
|
+
"hours": HOURS,
|
|
21
|
+
"days": DAYS,
|
|
22
|
+
}
|
|
23
|
+
ALL_TIME_UNITS = ("seconds", "minutes", "hours", "days")
|
|
24
|
+
|
|
25
|
+
def normalize_time_unit(unit: str) -> str:
|
|
26
|
+
u = unit.strip().lower()
|
|
27
|
+
if u in TIME_CONVERSIONS:
|
|
28
|
+
return u
|
|
29
|
+
for key, values in TIME_CONVERSIONS.items():
|
|
30
|
+
if u in values.get("strings", []):
|
|
31
|
+
return key
|
|
32
|
+
raise ValueError(f"Unknown time unit '{unit}'. Supported: {list(TIME_CONVERSIONS.keys())}")
|
|
33
|
+
|
|
34
|
+
def get_time_unit_conversions(unit: str) -> dict:
|
|
35
|
+
return TIME_CONVERSIONS[normalize_time_unit(unit)]
|
|
36
|
+
|
|
37
|
+
def time_factor(unit_from: str, unit_to: str) -> float:
|
|
38
|
+
"""
|
|
39
|
+
multiplicative factor s.t.
|
|
40
|
+
value_in_to = value_in_from * _time_factor(unit_from, unit_to)
|
|
41
|
+
|
|
42
|
+
seconds per 1 unit_from / seconds per 1 unit_to
|
|
43
|
+
"""
|
|
44
|
+
sf = get_time_unit_conversions(unit_from)["conv"]["seconds"] # sec / unit_from
|
|
45
|
+
st = get_time_unit_conversions(unit_to)["conv"]["seconds"] # sec / unit_to
|
|
46
|
+
return sf / st
|
|
47
|
+
|
|
48
|
+
def convert_time(value: float, unit_from: str, unit_to: str) -> float:
|
|
49
|
+
return value * time_factor(unit_from, unit_to)
|
|
50
|
+
|
|
51
|
+
def seconds_per(unit: str) -> float:
|
|
52
|
+
"""Return seconds in one <unit> (unit aliases supported)."""
|
|
53
|
+
return get_time_unit_conversions(normalize_time_unit(unit))["conv"]["seconds"]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# src/utils/escape_velocity.py
|
|
2
|
+
|
|
3
|
+
from ..imports import math, mul, div, add
|
|
4
|
+
from ..constants.planet_constants import get_body
|
|
5
|
+
from ..constants.distance_constants import convert as dconvert
|
|
6
|
+
from ..constants.time_constants import get_time_unit_conversions, normalize_time_unit
|
|
7
|
+
|
|
8
|
+
def _seconds_per(unit: str) -> float:
|
|
9
|
+
"""
|
|
10
|
+
Map a time unit (alias-friendly) to seconds per that unit, using your time schema.
|
|
11
|
+
"""
|
|
12
|
+
return get_time_unit_conversions(normalize_time_unit(unit))["conv"]["seconds"]
|
|
13
|
+
|
|
14
|
+
def escape_velocity_at(
|
|
15
|
+
planet: str = "earth",
|
|
16
|
+
distance: float = 0.0,
|
|
17
|
+
*,
|
|
18
|
+
input_units: str = "meters", # how to interpret `distance`
|
|
19
|
+
output_units: str = "meters", # distance unit for the *speed*
|
|
20
|
+
output_time: str = "s", # time unit for the *speed*
|
|
21
|
+
as_radius: bool = False # False => `distance` is altitude above surface; True => radius from center
|
|
22
|
+
) -> dict:
|
|
23
|
+
"""
|
|
24
|
+
Compute v_escape at a given location around `planet`.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
planet: body name (must exist in PLANETS)
|
|
28
|
+
distance: if as_radius=False => altitude above surface; if as_radius=True => radius from center
|
|
29
|
+
input_units: units of `distance`
|
|
30
|
+
output_units: distance unit of the returned speed
|
|
31
|
+
output_time: time unit of the returned speed ('s'|'min'|'h' etc.)
|
|
32
|
+
as_radius: interpret `distance` as radius-from-center when True
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
{
|
|
36
|
+
"ok": True,
|
|
37
|
+
"planet": str,
|
|
38
|
+
"radius_from_center": <float in output_units>,
|
|
39
|
+
"v_escape": <float in output_units/output_time>,
|
|
40
|
+
"v_escape_mps": <float in m/s>,
|
|
41
|
+
"units": {"distance": output_units, "time": output_time}
|
|
42
|
+
}
|
|
43
|
+
"""
|
|
44
|
+
if not (isinstance(distance, (int, float)) and math.isfinite(distance) and distance >= 0):
|
|
45
|
+
return {"ok": False, "error": "distance must be a non-negative number"}
|
|
46
|
+
|
|
47
|
+
body = get_body(planet)
|
|
48
|
+
mu = body["mu"] # m^3/s^2
|
|
49
|
+
R = body["radius"] # m
|
|
50
|
+
|
|
51
|
+
# Determine radius from center in meters
|
|
52
|
+
if as_radius:
|
|
53
|
+
r_m = dconvert(distance, input_units, "meters")
|
|
54
|
+
else:
|
|
55
|
+
alt_m = dconvert(distance, input_units, "meters")
|
|
56
|
+
r_m = add(R, alt_m)
|
|
57
|
+
|
|
58
|
+
if r_m <= 0:
|
|
59
|
+
return {"ok": False, "error": "computed radius is non-positive"}
|
|
60
|
+
|
|
61
|
+
# v_esc (m/s)
|
|
62
|
+
vesc_mps = math.sqrt(mul(2.0, div(mu, r_m)))
|
|
63
|
+
|
|
64
|
+
# Convert speed to <output_units>/<output_time>
|
|
65
|
+
vesc_units_per_sec = dconvert(vesc_mps, "meters", output_units)
|
|
66
|
+
sec_per = _seconds_per(output_time) # seconds per 1 output_time
|
|
67
|
+
vesc_out = mul(vesc_units_per_sec, sec_per) # <output_units>/<output_time>
|
|
68
|
+
|
|
69
|
+
# Also return the radius in output_units for convenience
|
|
70
|
+
r_out = dconvert(r_m, "meters", output_units)
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
"ok": True,
|
|
74
|
+
"planet": planet,
|
|
75
|
+
"radius_from_center": r_out,
|
|
76
|
+
"v_escape": vesc_out,
|
|
77
|
+
"v_escape_mps": vesc_mps,
|
|
78
|
+
"units": {"distance": output_units, "time": output_time}
|
|
79
|
+
}
|