Skip to content

Commit 38a14ff

Browse files
committed
Add calculate_observing_night
1 parent dc04700 commit 38a14ff

File tree

3 files changed

+162
-0
lines changed

3 files changed

+162
-0
lines changed

src/adam_core/observers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# flake8: noqa: F401
22
from .observers import Observers
33
from .state import get_observer_state
4+
from .utils import calculate_observing_night
45

56
__all__ = ["Observers", "get_observer_state"]
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import pyarrow as pa
2+
import pyarrow.compute as pc
3+
4+
from ...time import Timestamp
5+
from ..utils import calculate_observing_night
6+
7+
8+
def test_calculate_observing_night() -> None:
9+
# Rubin Observatory is UTC-7
10+
# Reasonable observating times would be +- 12 hours from local midnight
11+
# or 7:00 to 17:00 UTC
12+
13+
# ZTF is UTC-8
14+
# Reasonable observating times would be +- 12 hours from local midnight
15+
# or 8:00 to 16:00 UTC
16+
17+
# M22 is UTC+2
18+
# Reasonable observating times would be +- 12 hours from local midnight
19+
# or 22:00 to 10:00 UTC
20+
21+
# 000 is UTC
22+
# Reasonable observating times would be +- 12 hours from local midnight
23+
# or 0:00 to 12:00 UTC
24+
25+
codes = pa.array(
26+
[
27+
"X05",
28+
"X05",
29+
"X05",
30+
"X05",
31+
"X05",
32+
"I41",
33+
"I41",
34+
"I41",
35+
"I41",
36+
"I41",
37+
"M22",
38+
"M22",
39+
"M22",
40+
"M22",
41+
"M22",
42+
"000",
43+
"000",
44+
"000",
45+
"000",
46+
"000",
47+
]
48+
)
49+
times_utc = Timestamp.from_mjd(
50+
[
51+
# Rubin Observatory
52+
59000 + 7 / 24 - 12 / 24,
53+
59000 + 7 / 24 - 6 / 24,
54+
59000 + 7 / 24,
55+
59000 + 7 / 24 + 6 / 24,
56+
59000 + 7 / 24 + 12 / 24,
57+
# ZTF
58+
59000 + 8 / 24 - 12 / 24,
59+
59000 + 8 / 24 - 4 / 24,
60+
59000 + 8 / 24,
61+
59000 + 8 / 24 + 4 / 24,
62+
59000 + 8 / 24 + 12 / 24,
63+
# M22
64+
59000 - 2 / 24 - 12 / 24,
65+
59000 - 2 / 24 - 6 / 24,
66+
59000 - 2 / 24,
67+
59000 - 2 / 24 + 6 / 24,
68+
59000 - 2 / 24 + 12 / 24,
69+
# 000
70+
59000 - 12 / 24,
71+
59000 - 6 / 24,
72+
59000,
73+
59000 + 6 / 24,
74+
59000 + 12 / 24,
75+
],
76+
scale="utc",
77+
)
78+
79+
observing_night = calculate_observing_night(codes, times_utc)
80+
desired = pa.array(
81+
[
82+
58999,
83+
58999,
84+
58999,
85+
58999,
86+
59000,
87+
58999,
88+
58999,
89+
58999,
90+
58999,
91+
59000,
92+
58999,
93+
58999,
94+
58999,
95+
58999,
96+
59000,
97+
58999,
98+
58999,
99+
58999,
100+
58999,
101+
59000,
102+
]
103+
)
104+
assert pc.all(pc.equal(observing_night, desired)).as_py()

src/adam_core/observers/utils.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from datetime import timedelta
2+
from zoneinfo import ZoneInfo
3+
4+
import numpy as np
5+
import pyarrow as pa
6+
import pyarrow.compute as pc
7+
from astropy.time import Time
8+
9+
from ..time import Timestamp
10+
from .observers import OBSERVATORY_PARALLAX_COEFFICIENTS
11+
12+
13+
def calculate_observing_night(codes: pa.Array, times: Timestamp) -> pa.Array:
14+
"""
15+
Compute the observing night for a given set of observatory codes and times. An observing night is defined as the night
16+
during which the observation is made, in the local time of the observatory +- 12 hours. The observing night is defined
17+
as the integer MJD of the night in the local time of the observatory.
18+
19+
Parameters
20+
----------
21+
codes : pyarrow.Array (N)
22+
An array of observatory codes.
23+
times : Timestamp (N)
24+
An array of times.
25+
26+
Returns
27+
-------
28+
observing_night : pyarrow.Array (N)
29+
An array of observing nights.
30+
"""
31+
half_day = timedelta(hours=12)
32+
observing_night = np.empty(len(times), dtype=np.int64)
33+
34+
for code in pc.unique(codes):
35+
36+
parallax_coefficients = OBSERVATORY_PARALLAX_COEFFICIENTS.select("code", code)
37+
if len(parallax_coefficients) == 0:
38+
raise ValueError(f"Unknown observatory code: {code}")
39+
40+
tz = ZoneInfo(parallax_coefficients.timezone()[0])
41+
42+
mask = pc.equal(codes, code)
43+
times_code = times.apply_mask(mask)
44+
45+
observing_night_code = Time(
46+
[
47+
time + time.astimezone(tz).utcoffset() - half_day
48+
for time in times_code.to_astropy().datetime
49+
],
50+
format="datetime",
51+
scale="utc",
52+
)
53+
observing_night_code = observing_night_code.mjd.astype(int)
54+
55+
observing_night[mask.to_numpy(zero_copy_only=False)] = observing_night_code
56+
57+
return pa.array(observing_night)

0 commit comments

Comments
 (0)