
Conjunctions Examplesο
This notebook shows how MontuPython finds angular conjunctions between planets, stars, and mixed groups.
``Conjunction``: evaluate separation, visibility, and lapse at one epoch.
``ConjunctionExplorer``: scan a date interval for local minima below a threshold.
MontuPython uses the maximum pairwise separation among all body pairs. A conjunction is in range when that value is at or below maxseparation (default 5Β°).
If running in Google Colab, MontuPython must be installed first. In a local copy of the repository this cell can remain commented out.
[1]:
try:
from google.colab import drive
%pip install -Uq montu
except ImportError:
print("Not running in Colab, skipping installation")
import plotly.io as pio
pio.renderers.default = "notebook_connected"
%load_ext autoreload
%autoreload 2
# Create folders for figures and temporal files
!mkdir -p ./gallery/ ./montu_dem/
Not running in Colab, skipping installation
[2]:
%matplotlib inline
import montu
import pandas as pd
pd.options.display.float_format = "{:.3f}".format
MontuPython version 0.50.0. πππππ
π΅ ππ‘πΏπππππ‘ (ii-ti m Htp, HkAx Hn'-k)
Setup and conventionsο
``separation``: maximum pairwise angular separation [deg] at the epoch;
``in_conjunction``:
Truewhenseparation <= maxseparation;``visible_from_site``: all bodies above the horizon and the Sun below β5Β° (or
n/afor geocentric runs);``explore_lapse()``: UTC interval during which the group stays within
maxseparationaround the reference day.
Use return_as='Star' when selecting one star from montu.Stars.
[3]:
mars = montu.Planet('Mars')
aldebaran = montu.Stars(subset='bright', ProperName='Aldebaran', return_as='Star')
# Observers used throughout the notebook
medellin = montu.Observer(lat=6, lon=-75)
thebes = montu.Observer(site='thebes')
athens = montu.Observer(site='athens')
sites = {
'geocentric': 'geocentric',
'MedellΓn': medellin,
'Thebes': thebes,
'Athens': athens,
}
Loading stellar catalogue montu_stellar_catalogue_v38_bright.csv
ConjunctionExplorer β scan an intervalο
ConjunctionExplorer.search(start, end, observer=...) returns a list of fully computed Conjunction objects, one per qualifying local minimum.
[4]:
explorer = montu.ConjunctionExplorer(bodies=[mars, aldebaran], maxseparation=5)
conjs = explorer.search(
start=montu.Time('2022-09-01'),
end=montu.Time('2022-10-01'),
observer='geocentric',
verbose=True
)
for conj in conjs:
conj.show_details()
100%|βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ| 31/31 [00:00<00:00, 20274.98it/s]
Conjunction: MarsβAldebaran
Epoch (UTC) : 2022-09-07 14:28:28
Julian Day (UTC) : 2459830.103152
Observer : geocentric
Angular separation : 4.2746Β° (max allowed 5.0Β°)
In conjunction : yes
Is visible from site : n/a (geocentric)
Pair MarsβAldebaran
Separation : 4.2746Β°
Position angle : 170.19Β° (NβE)
Mars
Phase : 85.42%
Angular size : 0.169 arcmin
V magnitude : -0.22
Aldebaran
V magnitude : 0.87
Once a conjunction in a geocentric location, we can evaluate the condition at different locations
[5]:
conjunction = montu.Conjunction(
bodies=[mars, aldebaran],
maxseparation=5,
mtime=conjs[0].mtime,
observer=medellin,
)
conjunction.show_details()
Conjunction: MarsβAldebaran
Epoch (UTC) : 2022-09-07 14:28:28
Julian Day (UTC) : 2459830.103152
Observer : lat 6.000000Β°, lon -75.000000Β°
Local solar time : 09:28:32.298
Angular separation : 4.2756Β° (max allowed 5.0Β°)
In conjunction : yes
Sun altitude : 52.85Β°
Is visible from site : no (bodies above horizon and Sun < -5Β°)
Pair MarsβAldebaran
Separation : 4.2756Β°
Position angle : 170.18Β° (NβE)
Mars
Elevation / azimuth: 29.94Β° / 290.54Β° (above horizon: yes)
Rise (UTC) : 2022-09-08 04:14:14
Set (UTC) : 2022-09-07 16:38:38
Phase : 85.42%
Angular size : 0.169 arcmin
V magnitude : -0.22
Aldebaran
Elevation / azimuth: 30.95Β° / 285.72Β° (above horizon: yes)
Rise (UTC) : 2022-09-08 04:18:18
Set (UTC) : 2022-09-07 16:39:39
V magnitude : 0.87
As you can see the conjunction at minimum canβt be seen from this site. We can then explore times to check when is visible:
[6]:
conjunction = montu.Conjunction(
bodies=[mars, aldebaran],
maxseparation=5,
mtime=conjs[0].mtime,
observer=medellin,
)
lapse = conjunction.explore_lapse(verbose=False)
conjunction.plot_lapse(lapse[0], lapse[1], step_hours=1)
As you can see there are several dates and times at which the conjunction will be visible. For instance on 2022-09-07, 02:00 local time. Letβs see the conditions:
[7]:
lapse
[7]:
(Time('2022-09-02 11:59:18.095983'/'2022-09-02 11:59:59'/'[hrw 4806] I peret 18'/JED 2459824.999515/JTD 2459825.0003611),
Time('2022-09-12 18:33:42.295690'/'2022-09-12 18:33:33'/'[hrw 4806] I peret 28'/JED 2459835.2734062/JTD 2459835.2742523))
[8]:
conditions = conjunction.is_visible(
from_site=medellin,
at=montu.Time('2022-09-07 02:00:00', zone=medellin),
verbose=False,
)
montu.Util.print_dict(conditions)
| Key | Value |
|-------------------|-------------------------------------|
| mtime | 2022-09-07 07:00:00 |
| observer | lat 6.000000Β°, lon -75.000000Β°, 0 m |
| from_site | lat 6.000000Β°, lon -75.000000Β°, 0 m |
| is_geocentric | no |
| separation | 4.28 |
| maxseparation | 5.00 |
| in_conjunction | yes |
| above_horizon | yes |
| sun_altitude | -57.29 |
| visible_from_site | yes |
| visible | yes |
| body_conditions | [2 rows β see below] |
| pairs | [1 rows β see below] |
body_conditions:
| name | az | el | above_horizon | ra_epoch | dec_epoch | vmag | rise_time | rise_az | set_time | set_az | phase | angsize_arcmin | elongation |
|-----------|-------|-------|-----------------|------------|-------------|--------|---------------------|-----------|---------------------|----------|---------|------------------|--------------|
| Mars | 68.39 | 37.54 | yes | 4.55886 | 20.741 | -0.22 | 2022-09-08 04:14:14 | 68.99 | 2022-09-07 16:38:38 | 290.96 | 85.40 | 0.17 | -94.82 |
| Aldebaran | 73.75 | 37.18 | yes | 4.62022 | 16.555 | 0.87 | 2022-09-08 04:18:18 | 73.29 | 2022-09-07 16:39:39 | 286.71 | β | β | -94.54 |
pairs:
| bodies | separation_deg | position_angle_deg | distance_au |
|-----------------|------------------|----------------------|---------------|
| Mars, Aldebaran | 4.28 | 168.08 | β |
Sky map with stellar contextο
plot_map() draws the conjunction on a Plotly equatorial map with stars from the visible catalogue and constellation names in the field of view (same spirit as Stars.plot_stars). The view is centred on the geometric mean of the body directions and is only produced when in_conjunction is true.
[9]:
conjunction.plot_map(mag_namelimit=5.0)
Loading stellar catalogue montu_stellar_catalogue_v38_visible.csv
Other casesο
The examples below reproduce ground-truth conjunctions from the project reference summary. Each case is evaluated explicitly at several observers so you can see how topocentric parallax and local visibility differ from the geocentric geometry.
Mars and AldebarΓ‘nο
Documented separations for five synodic cycles:
Fecha |
Nota |
|---|---|
2026-07-13 |
> 5Β° |
2024-08-04 |
< 5Β° |
2022-09-07 |
< 5Β° (reference) |
2021-03-20 |
> 5Β° |
2019-04-11 |
> 5Β° |
[10]:
explorer = montu.ConjunctionExplorer(bodies=[mars, aldebaran], maxseparation=8)
conjs = explorer.search(
start=montu.Time('2019-01-01'),
end=montu.Time('2027-01-01'),
observer='geocentric',
)
for conj in conjs:
print(
f"Fecha y hora: {conj.mtime.readable.datespice}, "
f"SeparaciΓ³n angular: {conj.separation:.3f}Β°"
)
Fecha y hora: 2019-04-15 09:03:00.394556, SeparaciΓ³n angular: 6.470Β°
Fecha y hora: 2021-03-21 06:49:37.896944, SeparaciΓ³n angular: 6.944Β°
Fecha y hora: 2022-09-07 14:28:32.298229, SeparaciΓ³n angular: 4.275Β°
Fecha y hora: 2024-08-04 14:44:14.196480, SeparaciΓ³n angular: 4.929Β°
Fecha y hora: 2026-07-13 01:08:11.100481, SeparaciΓ³n angular: 5.313Β°
Planetary triosο
Three planets qualify as a Meeus-style grouping when their maximum pairwise separation is at or below maxseparation.
[11]:
trio_bodies = [
montu.Planet('Mercury'),
montu.Planet('Mars'),
montu.Planet('Saturn'),
]
trio_explorer = montu.ConjunctionExplorer(bodies=trio_bodies, maxseparation=5)
trio_hits = trio_explorer.search(
start=montu.Time('2026-04-15'),
end=montu.Time('2026-04-25'),
observer='geocentric',
)
for hit in trio_hits:
print(hit.mtime.readable.datespice, f"sep={hit.separation:.2f}Β°")
2026-04-20 22:41:32.305921 sep=1.65Β°
[12]:
trio_2026 = montu.Conjunction(
bodies=trio_bodies,
maxseparation=5,
mtime=trio_hits[0].mtime,
observer='geocentric',
)
trio_2026.show_details()
Conjunction: MercuryβMarsβSaturn
Epoch (UTC) : 2026-04-20 22:41:41
Julian Day (UTC) : 2461151.445513
Observer : geocentric
Angular separation : 1.6511Β° (max allowed 5.0Β°)
In conjunction : yes
Is visible from site : n/a (geocentric)
Pair MercuryβMars
Separation : 1.6511Β°
Position angle : 335.74Β° (NβE)
Distance : 1.137052 AU
Pair MercuryβSaturn
Separation : 0.8186Β°
Position angle : 280.09Β° (NβE)
Distance : 9.275015 AU
Pair MarsβSaturn
Separation : 1.3678Β°
Position angle : 185.33Β° (NβE)
Distance : 8.139589 AU
Mercury
Phase : 72.96%
Angular size : 0.100 arcmin
V magnitude : -0.15
Mars
Phase : 98.08%
Angular size : 0.069 arcmin
V magnitude : 1.21
Saturn
Phase : 99.96%
Angular size : 0.265 arcmin
V magnitude : 0.91
[13]:
trio_2026.plot_map()
[14]:
trio_2021_bodies = [
montu.Planet('Mercury'),
montu.Planet('Jupiter'),
montu.Planet('Saturn'),
]
trio_2021_explorer = montu.ConjunctionExplorer(bodies=trio_2021_bodies, maxseparation=5)
trio_2021_hits = trio_2021_explorer.search(
start=montu.Time('2021-01-05'),
end=montu.Time('2021-01-15'),
observer='geocentric',
)
for hit in trio_2021_hits:
print(hit.mtime.readable.datespice, f"sep={hit.separation:.2f}Β°")
2021-01-10 12:47:23.104313 sep=2.26Β°
[15]:
trio_2021 = montu.Conjunction(
bodies=trio_2021_bodies,
maxseparation=5,
mtime=trio_2021_hits[0].mtime,
observer='geocentric',
)
trio_2021.show_details()
trio_2021.plot_map()
Conjunction: MercuryβJupiterβSaturn
Epoch (UTC) : 2021-01-10 12:47:47
Julian Day (UTC) : 2459225.032906
Observer : geocentric
Angular separation : 2.2590Β° (max allowed 5.0Β°)
In conjunction : yes
Is visible from site : n/a (geocentric)
Pair MercuryβJupiter
Separation : 2.2266Β°
Position angle : 34.51Β° (NβE)
Distance : 4.767688 AU
Pair MercuryβSaturn
Separation : 1.6998Β°
Position angle : 325.75Β° (NβE)
Distance : 9.672860 AU
Pair JupiterβSaturn
Separation : 2.2590Β°
Position angle : 258.59Β° (NβE)
Distance : 4.916213 AU
Mercury
Phase : 90.88%
Angular size : 0.088 arcmin
V magnitude : -0.75
Jupiter
Phase : 99.94%
Angular size : 0.543 arcmin
V magnitude : -1.79
Saturn
Phase : 99.99%
Angular size : 0.252 arcmin
V magnitude : 0.62
[16]:
trio_2013_bodies = [
montu.Planet('Venus'),
montu.Planet('Jupiter'),
montu.Planet('Mercury'),
]
trio_2013_explorer = montu.ConjunctionExplorer(bodies=trio_2013_bodies, maxseparation=5)
trio_2013_hits = trio_2013_explorer.search(
start=montu.Time('2013-05-20'),
end=montu.Time('2013-05-31'),
observer='geocentric',
)
for hit in trio_2013_hits:
print(hit.mtime.readable.datespice, f"sep={hit.separation:.2f}Β°")
2013-05-27 06:42:06.292795 sep=2.36Β°
[17]:
trio_2013 = montu.Conjunction(
bodies=trio_2013_bodies,
maxseparation=5,
mtime=trio_2013_hits[0].mtime,
observer='geocentric',
)
trio_2013.show_details()
trio_2013.plot_map()
Conjunction: VenusβJupiterβMercury
Epoch (UTC) : 2013-05-27 06:42:42
Julian Day (UTC) : 2456439.779239
Observer : geocentric
Angular separation : 2.3634Β° (max allowed 5.0Β°)
In conjunction : yes
Is visible from site : n/a (geocentric)
Pair VenusβJupiter
Separation : 1.7971Β°
Position angle : 118.04Β° (NβE)
Distance : 4.431820 AU
Pair VenusβMercury
Separation : 2.0285Β°
Position angle : 41.99Β° (NβE)
Distance : 0.507662 AU
Pair JupiterβMercury
Separation : 2.3634Β°
Position angle : 355.14Β° (NβE)
Distance : 4.937255 AU
Venus
Phase : 96.30%
Angular size : 0.172 arcmin
V magnitude : -3.82
Jupiter
Phase : 99.92%
Angular size : 0.540 arcmin
V magnitude : -1.78
Mercury
Phase : 74.58%
Angular size : 0.099 arcmin
V magnitude : -0.73
Keplerβs Fire Triangle (autumn 1604)ο
Mars, Jupiter, and Saturn formed a wide historic grouping. Here we relax the threshold to 10Β° and search geocentrically through autumn 1604.
[18]:
fire_triangle = montu.ConjunctionExplorer(
bodies=[montu.Planet('Mars'), montu.Planet('Jupiter'), montu.Planet('Saturn')],
maxseparation=10,
)
fire_hits = fire_triangle.search(
start=montu.Time('1604-08-01', calendar='mixed'),
end=montu.Time('1604-12-31', calendar='mixed'),
observer='geocentric',
)
for hit in fire_hits:
print(hit.mtime.readable.datespice, f"sep={hit.separation:.2f}Β°")
fire_hits[0].plot_map()
1604-09-26 13:59:59.997117 sep=7.55Β°
[19]:
# Mars, Jupiter, Saturn β February 6 BCE (post triple conjunction)
trio_6bce = montu.ConjunctionExplorer(
bodies=[montu.Planet('Mars'), montu.Planet('Jupiter'), montu.Planet('Saturn')],
maxseparation=10,
)
bce_hits = trio_6bce.search(
start=montu.Time('-0006-02-01', calendar='mixed'),
end=montu.Time('-0005-03-31', calendar='mixed'),
observer='geocentric',
)
for hit in bce_hits:
print(hit.mtime.readable.datespice, f"sep={hit.separation:.2f}Β°")
bce_hits[0].plot_map()
0006 B.C. 02-18 07:00:00.2880 sep=6.44Β°
Letβs make a search in 1700 years:
[20]:
# Mars, Jupiter, Saturn β February 6 BCE (post triple conjunction)
explorer = montu.ConjunctionExplorer(
bodies=[montu.Planet('Mars'), montu.Planet('Jupiter'), montu.Planet('Saturn')],
maxseparation=5,
)
trio_hits = explorer.search(
start=montu.Time('-0006-02-01', calendar='mixed'),
end=montu.Time('1700-03-31', calendar='mixed'),
observer='geocentric',
verbose=True,
)
for hit in trio_hits:
print(hit.mtime.readable.datespice, f"sep={hit.separation:.2f}Β°")
100%|βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ| 623165/623165 [00:31<00:00, 19879.77it/s]
0015 A.D. 02-08 08:28:30.302408 sep=4.66Β°
0035 A.D. 07-05 06:00:00.00 sep=4.70Β°
0113 A.D. 06-08 16:59:59.997120 sep=3.25Β°
0153 A.D. 10-17 00:00:00.00 sep=3.92Β°
0214 A.D. 06-13 01:59:59.997120 sep=3.65Β°
0292 A.D. 05-19 09:21:29.701432 sep=3.82Β°
0471 A.D. 05-07 02:02:20.699512 sep=4.44Β°
0491 A.D. 03-28 14:56:53.203208 sep=0.56Β°
0650 A.D. 04-22 18:59:59.994232 sep=4.52Β°
0670 A.D. 02-26 22:59:59.997120 sep=3.28Β°
0690 A.D. 08-04 03:00:00.00 sep=4.27Β°
0828 A.D. 10-12 12:00:00.00 sep=2.25Β°
0869 A.D. 01-21 15:00:00.00 sep=2.66Β°
0967 A.D. 05-21 03:59:59.994232 sep=3.54Β°
1007-09-29 15:00:00.000000 sep=3.58Β°
1047-12-27 07:35:10.599360 sep=3.71Β°
1146-05-03 13:00:00.002884 sep=4.16Β°
1246-11-13 18:00:00.000000 sep=4.70Β°
1345-03-13 16:59:59.997116 sep=1.98Β°
1425-10-25 09:00:00.000000 sep=4.84Β°
1503-10-24 09:00:00.000000 sep=3.06Β°
1524-02-14 23:00:00.005766 sep=0.94Β°
1544-02-12 15:00:00.000000 sep=2.27Β°
1682-09-21 13:00:00.002883 sep=2.34Β°
Letβs plot the closest:
[21]:
# Escoge la conjunciΓ³n mΓ‘s cercana de trio_hits y haz un plot
if len(trio_hits) > 0:
closest = min(trio_hits, key=lambda hit: hit.separation)
closest.plot_map()
else:
print("No se encontraron conjunciones en el rango especificado.")
2 planetas y 1 estrellaο
Mixed groupings combine planets and bright stars. Around 21β22 July 2021, Venus and Regulus close to ~1.1Β° while Mars stays near the group (~5Β°). Because MarsβRegulus sits right at that limit, we use maxseparation=5.5 for the three-body search (strict 5Β° finds no simultaneous minimum).
[22]:
vmr_bodies = [
montu.Planet('Venus'),
montu.Planet('Mars'),
montu.Stars(subset='bright', ProperName='Regulus', return_as='Star'),
]
vmr_explorer = montu.ConjunctionExplorer(bodies=vmr_bodies, maxseparation=10)
vmr_hits = vmr_explorer.search(
start=montu.Time('2021-07-15'),
end=montu.Time('2021-07-25'),
observer='geocentric',
)
for hit in vmr_hits:
print(hit.mtime.readable.datespice, f"sep={hit.separation:.2f}Β°")
2021-07-22 03:41:18.798705 sep=4.99Β°
[23]:
vmr = montu.Conjunction(
bodies=vmr_bodies,
maxseparation=5.5,
mtime=vmr_hits[0].mtime,
observer='geocentric',
)
vmr.show_details()
vmr.plot_map()
Conjunction: VenusβMarsβRegulus
Epoch (UTC) : 2021-07-22 03:41:41
Julian Day (UTC) : 2459417.653690
Observer : geocentric
Angular separation : 4.9933Β° (max allowed 5.5Β°)
In conjunction : yes
Is visible from site : n/a (geocentric)
Pair VenusβMars
Separation : 4.9933Β°
Position angle : 286.06Β° (NβE)
Distance : 1.157413 AU
Pair VenusβRegulus
Separation : 1.0880Β°
Position angle : 202.56Β° (NβE)
Pair MarsβRegulus
Separation : 4.9885Β°
Position angle : 117.43Β° (NβE)
Venus
Phase : 84.82%
Angular size : 0.205 arcmin
V magnitude : -3.85
Mars
Phase : 98.23%
Angular size : 0.062 arcmin
V magnitude : 1.83
Regulus
V magnitude : 1.36
Triple conjunctions (retrograde loops)ο
When a faster planet laps a slower one near a stationary point, the separation can reach three local minima within a few months. ConjunctionExplorer.search recovers each crossing.
[24]:
jupiter = montu.Planet('Jupiter')
saturn = montu.Planet('Saturn')
js_explorer = montu.ConjunctionExplorer(bodies=[jupiter, saturn], maxseparation=5)
js_crossings = js_explorer.search(
start=montu.Time('-0006-01-01', calendar='mixed'),
end=montu.Time('-0006-12-31', calendar='mixed'),
observer='geocentric',
)
print(f"JupiterβSaturn, 7 BCE: {len(js_crossings)} crossings")
for crossing in js_crossings:
print(f" {crossing.mtime.readable.datespice} sep={crossing.separation:.2f}Β°")
JupiterβSaturn, 7 BCE: 3 crossings
0007 B.C. 05-27 05:51:35.804160 sep=0.98Β°
0007 B.C. 09-28 18:38:28.296952 sep=0.97Β°
0007 B.C. 12-03 09:47:06.498248 sep=1.05Β°
[25]:
regulus = montu.Stars(subset='bright', ProperName='Regulus', return_as='Star')
jr_explorer = montu.ConjunctionExplorer(bodies=[jupiter, regulus], maxseparation=5)
jr_crossings = jr_explorer.search(
start=montu.Time('-0003-01-01', calendar='mixed'),
end=montu.Time('-0001-12-31', calendar='mixed'),
observer='geocentric',
)
print(f"JupiterβRegulus, 3β1 BCE: {len(jr_crossings)} crossings")
for crossing in jr_crossings:
print(f" {crossing.mtime.readable.datespice} sep={crossing.separation:.2f}Β°")
JupiterβRegulus, 3β1 BCE: 3 crossings
0003 B.C. 09-12 06:04:04.598392 sep=0.33Β°
0002 B.C. 02-15 07:15:23.999040 sep=0.86Β°
0002 B.C. 05-07 00:03:33.96952 sep=0.72Β°
Powered by MontuPython. For more examples see MontuPython GitHub repo.
Jorge I. Zuluaga Β© 2023-present