Planetary Phenomena Factory #
The PlanetaryPhenomenaFactory calculates observational phenomena for planets using the ephemeris backend’s ephe.pheno_ut() function (libephemeris by default). It computes elongation, illumination fraction, phase angle, apparent diameter/magnitude, morning/evening star status, and the body’s condition relative to the Sun (solar_phase).
Basic Usage #
from kerykeion import AstrologicalSubjectFactory, PlanetaryPhenomenaFactory
subject = AstrologicalSubjectFactory.from_birth_data(
"Example", 2025, 4, 1, 12, 0,
lng=12.5, lat=41.9, tz_str="Europe/Rome", online=False,
)
results = PlanetaryPhenomenaFactory.from_subject(subject)
for p in results.phenomena:
print(f"{p.name}: elongation={p.elongation:.1f}, mag={p.apparent_magnitude:.1f}")
if p.is_morning_star:
print(f" Morning star")
elif p.is_evening_star:
print(f" Evening star")
if p.solar_phase != "free":
print(f" {p.solar_phase}")
Solar Phase #
Every phenomenon carries solar_phase: the classical reading of how near the Sun a body stands, as a condition of visibility rather than a bare number of degrees.
| Value | Meaning | Default cut-off |
|---|---|---|
"cazimi" |
In the heart of the Sun | below 0.2833° (17 arcminutes) |
"combust" |
Burnt — close enough that the body cannot be seen at all | below 8.5° |
"under_the_beams" |
Within the Sun’s rays; not yet out of the twilight | below 17° |
"free" |
Far enough from the Sun to be seen in a dark sky | 17° or more |
The label is read off the same rounded elongation the model publishes, so a value that rounds onto a cut-off is never named one thing in the field and another in the phase. The comparisons are strict: a body sitting exactly on a cut-off takes the outer name. The quantity compared is the true angular separation the ephemeris reports, latitude included — not the difference in ecliptic longitude, which is what the tradition’s tables were built on. The two part company for a body off the ecliptic.
The three cut-offs are conventions, not measurements, and the schools disagree on all three. Pass your own SolarPhaseThresholdsModel to either constructor; the instance actually used is echoed on the returned collection, so a consumer never has to guess which convention produced a label. A set that does not widen outwards is rejected.
from kerykeion import PlanetaryPhenomenaFactory
from kerykeion.schemas import SolarPhaseThresholdsModel
# Venus at its 2024 superior conjunction.
default = PlanetaryPhenomenaFactory.from_julian_day(2460466.0, planets=["Venus"])
print(default.phenomena[0].solar_phase, round(default.phenomena[0].elongation, 3))
# cazimi 0.077
strict = PlanetaryPhenomenaFactory.from_julian_day(
2460466.0, planets=["Venus"],
solar_phase_thresholds=SolarPhaseThresholdsModel(cazimi_deg=0.05),
)
print(strict.phenomena[0].solar_phase) # combust
print(strict.solar_phase_thresholds.cazimi_deg) # 0.05
The phase is named for every supported body, the Moon included: its elongation is the same astronomical quantity, and the names still describe what they always describe — the dark of the Moon is exactly the interval in which it is under the beams. What a given school does with a combust Moon is the school’s business, not the library’s.
A central solar eclipse is not a promise of "cazimi", and the reason is the frame. The published elongation is geocentric; an eclipse is a topocentric alignment, seen by an observer standing under the shadow, and lunar parallax between the two reaches about a degree. The 2026-08-12 total eclipse bottoms out at a geocentric 0.891865° — "combust" — while the 2027-08-02 totality reaches 0.144957° and does read "cazimi". Cazimi at the moment of totality is what the observer under the shadow sees; the number this factory publishes is what the Earth’s centre sees.
is_morning_star and is_evening_star are a different question, and are purely geometric: which side of the Sun the planet stands on in longitude, with no visibility threshold of any kind. A planet one degree from the Sun is still an “evening star” there — invisible, but east of it. Read solar_phase for whether it can be seen.
Methods #
from_subject(subject, planets, solar_phase_thresholds) #
Calculate phenomena from an existing astrological subject.
| Parameter | Type | Default | Description |
|---|---|---|---|
subject |
AstrologicalSubjectModel | – | An astrological subject |
planets |
List[str] or None | None | Planet names (defaults to all) |
solar_phase_thresholds |
SolarPhaseThresholdsModel or None | None | Cut-offs for solar_phase; defaults to the classical 0.2833° / 8.5° / 17° |
Returns: PlanetaryPhenomenaCollectionModel
Raises: KerykeionException if the subject has no Julian Day — a composite
subject has no single moment in time and is not supported here. An unknown or
mistyped planet name raises ValueError (names are case-sensitive).
from_julian_day(julian_day, planets, solar_phase_thresholds) #
Calculate phenomena from a Julian Day number.
| Parameter | Type | Default | Description |
|---|---|---|---|
julian_day |
float | – | Finite Julian Day number |
planets |
List[str] or None | None | Planet names (defaults to all) |
solar_phase_thresholds |
SolarPhaseThresholdsModel or None | None | Cut-offs for solar_phase; defaults to the classical 0.2833° / 8.5° / 17° |
Returns: PlanetaryPhenomenaCollectionModel
Raises: ValueError for an unknown or mistyped planet name (names are
case-sensitive) or a non-finite julian_day, and KerykeionException if every
requested planet fails, which usually means the ephemeris backend is unavailable
or the moment is out of its range.
Supported Planets #
Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto.
Morning/evening star status is calculated only for the inferior planets (Mercury, Venus).
Data Models #
PlanetaryPhenomenaModel #
| Field | Type | Description |
|---|---|---|
name |
str | Planet name |
phase_angle |
float | Phase angle in degrees |
phase |
float | Illuminated fraction (0.0 to 1.0) |
elongation |
float | Angular distance from the Sun in degrees |
apparent_diameter |
float | Apparent diameter in degrees |
apparent_magnitude |
float | Apparent visual magnitude |
is_morning_star |
Optional[bool] | Geometric: the planet is west of the Sun and so rises before it. None for planets other than Mercury/Venus |
is_evening_star |
Optional[bool] | Geometric: the planet is east of the Sun and so sets after it. None for planets other than Mercury/Venus |
solar_phase |
SolarPhase | "cazimi", "combust", "under_the_beams" or "free" |
PlanetaryPhenomenaCollectionModel #
| Field | Type | Description |
|---|---|---|
iso_datetime |
str | ISO datetime of moment |
julian_day |
float | Julian Day number |
phenomena |
List[PlanetaryPhenomenaModel] | Phenomena for each planet |
solar_phase_thresholds |
SolarPhaseThresholdsModel | The cut-offs every solar_phase in this collection was labelled with |
SolarPhaseThresholdsModel #
Import from kerykeion.schemas. Each value is a half-width in degrees, measured from the Sun’s centre; they must widen outwards or the model raises.
| Field | Type | Default | Description |
|---|---|---|---|
cazimi_deg |
float | 0.2833 |
Below this separation the body is in the heart of the Sun |
combust_deg |
float | 8.5 |
Below this separation the body is burnt |
under_beams_deg |
float | 17.0 |
Below this separation the body is still in the rays |