Skip to content

Commit a41f520

Browse files
authored
Merge pull request #21 from simonsobs/sky_coverage
Sky Coverage Initial Script
2 parents a7aa7f2 + ac9b30d commit a41f520

13 files changed

Lines changed: 398 additions & 100 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ dist/*
66
*.egg-info
77
*.fits
88
*.hdf
9+
.coverage

mapcat/core/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .core import get_maps_by_coverage
2+
3+
__all__ = [
4+
"get_maps_by_coverage",
5+
]

mapcat/core/core.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from astropy.coordinates import ICRS
2+
from sqlalchemy import select
3+
from sqlalchemy.orm import Session
4+
5+
from mapcat.database import DepthOneMapTable
6+
from mapcat.toolkit.update_sky_coverage import dec_to_index, ra_to_index
7+
8+
9+
def get_maps_by_coverage(
10+
position: ICRS,
11+
session: Session,
12+
) -> list[DepthOneMapTable]:
13+
"""
14+
Get the depth one maps that cover a given position.
15+
16+
Parameters
17+
----------
18+
position : ICRS
19+
The position to query for coverage. Should be in ICRS coordinates.
20+
session : Session
21+
The database session to use for the query.
22+
23+
Returns
24+
-------
25+
session.execute(stmt).scalars().all() : list[DepthOneMapTable]
26+
A list of depth one maps that cover the given position.
27+
28+
Raises
29+
------
30+
ValueError
31+
If the RA or Dec of the position is out of bounds.
32+
"""
33+
ra = position.ra.deg
34+
dec = position.dec.deg
35+
36+
# These aren't covered since ICRS automatically wraps
37+
# values back aground to 0-360 for RA and -90 to 90 for Dec.
38+
if ra < 0 or ra > 360: # pragma: no cover
39+
raise ValueError("RA must be between 0 and 360 degrees")
40+
if dec < -90 or dec > 90: # pragma: no cover
41+
raise ValueError("Dec must be between -90 and 90 degrees")
42+
43+
ra_idx = ra_to_index(ra)
44+
dec_idx = dec_to_index(dec)
45+
46+
stmt = (
47+
select(DepthOneMapTable)
48+
.join(DepthOneMapTable.depth_one_sky_coverage)
49+
.filter_by(x=ra_idx, y=dec_idx)
50+
)
51+
52+
return session.execute(stmt).scalars().all()

mapcat/database/atomic_coadd.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
if TYPE_CHECKING:
1010
from .atomic_map import AtomicMapTable
1111

12-
from .links import AtomicMapToCoaddTable, CoaddMapToCoaddTable
12+
from .links import AtomicMapToCoaddTable, CoaddMapToCoaddTable # pragma: no cover
1313

1414

1515
class AtomicMapCoaddTable(SQLModel, table=True):

mapcat/database/atomic_map.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .links import AtomicMapToCoaddTable
1010

1111
if TYPE_CHECKING:
12-
from .atomic_coadd import AtomicMapCoaddTable
12+
from .atomic_coadd import AtomicMapCoaddTable # pragma: no cover
1313

1414

1515
class AtomicMapTable(SQLModel, table=True):

mapcat/database/depth_one_map.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from sqlmodel import JSON, Field, Relationship, SQLModel
88

9-
if TYPE_CHECKING:
9+
if TYPE_CHECKING: # pragma: no cover
1010
from .depth_one_coadd import DepthOneCoaddTable
1111
from .pipeline_information import PipelineInformationTable
1212
from .pointing_residual import PointingResidualTable

mapcat/helper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def session(self) -> sessionmaker:
6666
settings = Settings()
6767

6868

69-
def migrate():
69+
def migrate(): # pragma: no cover
7070
"""
7171
CLI script to run 'alembic upgrade head' with the correct location.
7272
"""

mapcat/toolkit/plot_tiles.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import argparse as ap
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
from pixell import enmap
6+
7+
from mapcat.toolkit.update_sky_coverage import *
8+
9+
parser = ap.ArgumentParser()
10+
parser.add_argument("--imap_path", type=str)
11+
parser.add_argument("--d1map_path", type=str)
12+
parser.add_argument("--opath", type=str)
13+
14+
args = parser.parse_args()
15+
imap_path = args.imap_path
16+
d1map_path = args.d1map_path
17+
opath = args.opath
18+
19+
imap = enmap.read_map(str(imap_path))
20+
21+
box = imap.box()
22+
23+
dec_min, ra_max = np.rad2deg(box[0])
24+
dec_max, ra_min = np.rad2deg(box[1])
25+
26+
pad_low = int((90 + dec_min) * 6 * 2)
27+
pad_high = int((90 - dec_max) * 6 * 2)
28+
29+
imap = imap[0][::10, ::10]
30+
31+
pad_map = np.pad(
32+
imap, ((pad_low, pad_high), (0, 0)), mode="constant", constant_values=0
33+
)
34+
del imap
35+
36+
left_limit = pad_map.shape[1]
37+
right_limit = 0
38+
top_limit = pad_map.shape[0]
39+
bottom_limit = 0
40+
extent = [left_limit, right_limit, bottom_limit, top_limit]
41+
42+
43+
plt.imshow(pad_map, vmin=-300, vmax=300, origin="lower", extent=extent)
44+
plt.vlines(
45+
np.arange(0, 360 * 6 * 2, 10 * 6 * 2), ymin=0, ymax=180 * 6 * 2, color="black", lw=1
46+
)
47+
plt.hlines(
48+
np.arange(0, 180 * 6 * 2, 10 * 6 * 2), xmin=0, xmax=360 * 6 * 2, color="black", lw=1
49+
)
50+
plt.xticks(np.arange(0, 360 * 6 * 2, 20 * 6 * 2), labels=np.arange(0, 360, 20))
51+
plt.yticks(np.arange(0, 180 * 6 * 2, 10 * 6 * 2), labels=np.arange(-90, 90, 10))
52+
plt.xlabel("RA (degrees)")
53+
plt.ylabel("Dec (degrees)")
54+
55+
d1map = enmap.read_map(str(d1map_path))
56+
coverage_tiles = get_sky_coverage(d1map)
57+
58+
d1box = d1map.box()
59+
60+
d1dec_min, d1ra_max = np.rad2deg(d1box[0])
61+
d1dec_max, d1ra_min = np.rad2deg(d1box[1])
62+
63+
d1pad_low_dec = int((90 + d1dec_min) * 6 * 2)
64+
d1pad_high_dec = int((90 - d1dec_max) * 6 * 2)
65+
66+
d1pad_low_ra = int((180 + d1ra_min) * 6 * 2)
67+
d1pad_high_ra = int((180 - d1ra_max) * 6 * 2)
68+
69+
d1map = d1map[0][::10, ::10]
70+
d1pad_map = np.pad(
71+
d1map,
72+
((d1pad_low_dec, d1pad_high_dec), (d1pad_high_ra, d1pad_low_ra)),
73+
mode="constant",
74+
constant_values=0,
75+
)
76+
77+
plt.imshow(
78+
d1pad_map,
79+
vmin=-300,
80+
vmax=300,
81+
origin="lower",
82+
alpha=0.5,
83+
cmap="seismic",
84+
extent=extent,
85+
)
86+
87+
for tile in coverage_tiles:
88+
plt.gca().add_patch(
89+
plt.Rectangle(
90+
(tile[0] * 10 * 6 * 2, tile[1] * 10 * 6 * 2),
91+
10 * 6 * 2,
92+
10 * 6 * 2,
93+
fill=False,
94+
edgecolor="red",
95+
lw=2,
96+
)
97+
)
98+
99+
plt.savefig(opath + "act_coverage.png", dpi=300)

mapcat/toolkit/reset.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,7 @@ def main():
123123
type=float,
124124
default=None,
125125
help=(
126-
"Only reset entries for maps whose ctime is >= START_TIME "
127-
"(unix timestamp)."
126+
"Only reset entries for maps whose ctime is >= START_TIME (unix timestamp)."
128127
),
129128
)
130129

@@ -133,8 +132,7 @@ def main():
133132
type=float,
134133
default=None,
135134
help=(
136-
"Only reset entries for maps whose ctime is <= END_TIME "
137-
"(unix timestamp)."
135+
"Only reset entries for maps whose ctime is <= END_TIME (unix timestamp)."
138136
),
139137
)
140138

mapcat/toolkit/update_sky_coverage.py

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,88 @@ def resolve_tmap(d1table: DepthOneMapTable) -> Path:
2525
return settings.depth_one_parent / d1table.mean_time_path
2626

2727

28+
def index_to_skybox(ra_idx: int, dec_idx: int) -> np.ndarray:
29+
"""
30+
Convert a sky coverage tile index to a sky box in radians
31+
32+
Parameters
33+
----------
34+
ra_idx : int
35+
The RA index of the sky coverage tile
36+
dec_idx : int
37+
The Dec index of the sky coverage tile
38+
39+
Returns
40+
-------
41+
skybox : np.ndarray
42+
A 2x2 array containing the corners of the sky box in radians, in the format [[dec_min, ra_max], [dec_max, ra_min]]
43+
"""
44+
ra_min = ra_idx * 10
45+
ra_max = ra_min + 10
46+
dec_min = (dec_idx - 9) * 10
47+
dec_max = dec_min + 10
48+
49+
return np.array(
50+
[
51+
[np.deg2rad(dec_min), np.deg2rad(ra_max)],
52+
[np.deg2rad(dec_max), np.deg2rad(ra_min)],
53+
]
54+
)
55+
56+
57+
def ra_to_index(ra: float) -> int:
58+
"""
59+
Convert an ra in degrees to a sky coverage tile index
60+
61+
Parameters
62+
----------
63+
ra : float
64+
The ra in degrees to convert
65+
66+
Returns
67+
-------
68+
idx : int
69+
The sky coverage tile index corresponding to the input ra
70+
"""
71+
return int(np.floor(ra / 10))
72+
73+
74+
def dec_to_index(dec: float) -> int:
75+
"""
76+
Convert a dec in degrees to a sky coverage tile index
77+
78+
Parameters
79+
----------
80+
dec : float
81+
The dec in degrees to convert
82+
83+
Returns
84+
-------
85+
idx : int
86+
The sky coverage tile index corresponding to the input dec
87+
"""
88+
return int(np.floor(dec / 10)) + 9
89+
90+
91+
def _ra_to_index_pixell(ra: float) -> int:
92+
"""
93+
Convert an ra in degrees to a sky coverage tile index using the
94+
pixell convention where -180 < ra < 180. You should probably
95+
not ever touch this function.
96+
97+
Parameters
98+
----------
99+
ra : float
100+
The ra in degrees to convert
101+
102+
Returns
103+
-------
104+
idx : int
105+
The sky coverage tile index corresponding to the input ra
106+
"""
107+
return int(np.floor(ra / 10)) + 18
108+
109+
28110
def get_sky_coverage(tmap: enmap.ndmap) -> list:
29111
"""
30112
Given the time map of a depth1 map, return the list
@@ -49,27 +131,27 @@ def get_sky_coverage(tmap: enmap.ndmap) -> list:
49131
dec_max = np.ceil(dec_max / 10) * 10
50132
ra_min = np.floor(ra_min / 10) * 10
51133
ra_max = np.ceil(ra_max / 10) * 10
134+
ra_min += 180 # Convert from pixel standard to normal RA convention
135+
ra_max += 180
52136

53137
ras = np.arange(ra_min, ra_max, 10)
54138
decs = np.arange(dec_min, dec_max, 10)
55139

56140
ra_idx = []
57-
dec_id = []
141+
dec_idx = []
58142

59143
for ra in ras:
60144
for dec in decs:
61-
skybox = np.array(
62-
[
63-
[np.deg2rad(dec), np.deg2rad(ra + 10)],
64-
[np.deg2rad(dec + 10), np.deg2rad(ra)],
65-
]
66-
)
145+
ra_id = ra_to_index(ra)
146+
dec_id = dec_to_index(dec)
147+
skybox = index_to_skybox(ra_id, dec_id)
148+
skybox[..., 1] -= np.pi # Convert from standard RA to pixell convention
67149
submap = enmap.submap(tmap, skybox)
68150
if np.any(submap):
69-
ra_idx.append(int(ra / 10))
70-
dec_id.append(int(dec / 10) + 9)
151+
ra_idx.append(ra_id)
152+
dec_idx.append(dec_id)
71153

72-
return list(zip(ra_idx, dec_id))
154+
return list(zip(ra_idx, dec_idx))
73155

74156

75157
def coverage_from_depthone(d1table: DepthOneMapTable) -> list[SkyCoverageTable]:

0 commit comments

Comments
 (0)