Skip to content

Commit 99e543c

Browse files
authored
Merge pull request #78 from LSSTDESC/rotate_config
Add options to determine rotation angles in the reducer
2 parents 1f59b83 + 5e110e0 commit 99e543c

5 files changed

Lines changed: 147 additions & 15 deletions

File tree

examples/cardinal_project_library.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ Reducers:
172172
name: cardinal
173173
Reduce: CardinalReducer
174174
Module: rail.projects.reducer
175+
rotation_angle: [60, 0, 0]
176+
flip_dec: true
175177

176178

177179
# These describe the various data analysis pipelines

examples/flagship_project_library.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ Reducers:
168168
name: flagship
169169
Reduce: FlagshipReducer
170170
Module: rail.projects.reducer
171+
rotation_angle: [-180, 0, 0]
172+
flip_dec: true
171173

172174

173175
# These describe the various data analysis pipelines

src/rail/projects/algorithm_holder.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,18 @@ class RailReducerAlgorithmHolder(RailAlgorithmHolder):
223223
required=True,
224224
msg="Data Reducer Class",
225225
),
226+
rotation_angle=StageParameter(
227+
list,
228+
[0.0, 0.0, 0.0],
229+
fmt="%s",
230+
msg="Three rotation angles, applied to RA, DEC, and around the line of sight axis",
231+
),
232+
flip_dec=StageParameter(
233+
float,
234+
False,
235+
fmt="%f",
236+
msg="Shortcut to flip sign of Dec. If True, multiply Dec by -1. Excecuted BEFORE the rotator",
237+
),
226238
)
227239
yaml_tag = "Reducer"
228240

src/rail/projects/project.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,14 @@ def reduce_data(
463463
assert issubclass(reducer_class, RailReducer)
464464

465465
reducer_args = library.get_selection(selection)
466-
reducer = reducer_class(**reducer_args.config.to_dict())
466+
467+
# also fetch the reducer configs:
468+
reducer_config = reducer_args.config.to_dict()
469+
reducer_holder = library.get_algorithm("Reducer", reducer_class_name)
470+
reducer_config["rotation_angle"] = reducer_holder.config.rotation_angle
471+
reducer_config["flip_dec"] = reducer_holder.config.flip_dec
472+
reducer = reducer_class(**reducer_config)
473+
#reducer = reducer_class(**reducer_args.config.to_dict())
467474

468475
if not dry_run: # pragma: no cover
469476
for source_, sink_ in zip(sources, sinks):

src/rail/projects/reducer.py

Lines changed: 123 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Any
66

77
import numpy as np
8+
import pyarrow as pa
89
import pyarrow.compute as pc
910
import pyarrow.dataset as ds
1011
import pyarrow.parquet as pq
@@ -16,6 +17,59 @@
1617
from .arrow_utils import parse_item
1718
from .dynamic_class import DynamicClass
1819

20+
21+
def rotate_gal_pyarrow(ra, dec, rot_ra, rot_dec, rot_x_ang=0):
22+
"""
23+
Rotate ra, dec in degrees to new coordinate
24+
given three rotation angles
25+
"""
26+
27+
# --- Degrees to radians ---
28+
k = math.pi / 180.0
29+
ra_r = pc.multiply(ra, k)
30+
dec_r = pc.multiply(dec, k)
31+
phi_r = rot_ra * k # scalar
32+
theta_r = rot_dec * k # scalar
33+
34+
# --- Step 1: (RA, Dec) → Cartesian ---
35+
x = pc.multiply(pc.cos(dec_r), pc.cos(ra_r))
36+
y = pc.multiply(pc.cos(dec_r), pc.sin(ra_r))
37+
z = pc.sin(dec_r)
38+
39+
# --- Step 2a: rot_z(-phi) ---
40+
cp, sp = math.cos(-phi_r), math.sin(-phi_r)
41+
x1 = pc.subtract(pc.multiply(pc.scalar(cp), x), pc.multiply(pc.scalar(sp), y))
42+
y1 = pc.add(pc.multiply(pc.scalar(sp), x), pc.multiply(pc.scalar(cp), y))
43+
z1 = z
44+
45+
# --- Step 2b: rot_y(theta) ---
46+
ct, st = math.cos(theta_r), math.sin(theta_r)
47+
x2 = pc.add(pc.multiply(pc.scalar(ct), x1), pc.multiply(pc.scalar(st), z1))
48+
y2 = y1
49+
z2 = pc.subtract(pc.multiply(pc.scalar(ct), z1), pc.multiply(pc.scalar(st), x1))
50+
51+
# --- Step 2c: Optional rot_x ---
52+
if rot_x_ang != 0:
53+
cx = math.cos(rot_x_ang * k)
54+
sx = math.sin(rot_x_ang * k)
55+
x3 = x2
56+
y3 = pc.subtract(pc.multiply(pc.scalar(cx), y2), pc.multiply(pc.scalar(sx), z2))
57+
z3 = pc.add(pc.multiply(pc.scalar(sx), y2), pc.multiply(pc.scalar(cx), z2))
58+
else:
59+
x3, y3, z3 = x2, y2, z2
60+
61+
# --- Step 3: Cartesian → (RA, Dec) ---
62+
ra_deg = pc.multiply(pc.atan2(y3, x3), 180 / math.pi)
63+
new_ra = pc.if_else(
64+
pc.less(ra_deg, 0),
65+
pc.add(ra_deg, 360),
66+
ra_deg
67+
)
68+
new_dec = pc.multiply(pc.asin(pc.min_element_wise(pc.max_element_wise(z3, -1.0), 1.0)), 180/math.pi)
69+
70+
return new_ra, new_dec
71+
72+
1973
COLUMNS = [
2074
"galaxy_id",
2175
"ra",
@@ -140,8 +194,8 @@
140194
PROJECTIONS_CARDINAL = [
141195
{
142196
# "Roman_K213": pc.field("k213"),
143-
"shift_ra": pc.add(pc.field("ra"), -60.),
144-
"shift_dec": pc.multiply(pc.field("dec"), -1.),
197+
#"shift_ra": pc.add(pc.field("ra"), -60.),
198+
#"shift_dec": pc.multiply(pc.field("dec"), -1.),
145199
"Ellipticity1": pc.field("Ellipticity_1"),
146200
"Ellipticity2": pc.field("Ellipticity_2"),
147201
"mag_y_euclid_nisp": pc.field("Euclid_Y"),
@@ -214,12 +268,12 @@
214268

215269
PROJECTIONS_FLAGSHIP = [
216270
{
217-
"ra": pc.if_else(
218-
pc.greater(pc.add(pc.field("ra_mag_gal"), pc.scalar(180)), pc.scalar(360)),
219-
pc.subtract(pc.field("ra_mag_gal"), pc.scalar(180)),
220-
pc.add(pc.field("ra_mag_gal"), pc.scalar(180))
221-
),
222-
"dec": pc.multiply(pc.scalar(-1), pc.field("dec_mag_gal")),
271+
#"ra": pc.if_else(
272+
# pc.greater(pc.add(pc.field("ra_mag_gal"), pc.scalar(180)), pc.scalar(360)),
273+
# pc.subtract(pc.field("ra_mag_gal"), pc.scalar(180)),
274+
# pc.add(pc.field("ra_mag_gal"), pc.scalar(180))
275+
# ),
276+
#"dec": pc.multiply(pc.scalar(-1), pc.field("dec_mag_gal")),
223277
"redshift": pc.field("observed_redshift_gal"),
224278
"mag_u_lsst": pc.subtract(
225279
pc.multiply(pc.scalar(-2.5), pc.log10(pc.field("lsst_u_el_model3_ext"))), pc.scalar(48.6)
@@ -307,7 +361,16 @@ class RailReducer(Configurable, DynamicClass):
307361
output catalog
308362
"""
309363

310-
config_options: dict[str, StageParameter] = {}
364+
config_options: dict[str, StageParameter] = dict(
365+
rotation_angle=StageParameter(
366+
list, [0.0, 0.0, 0.0], fmt="%s",
367+
msg="Three rotation angles, applied to RA, DEC, and around the line of sight axis",
368+
),
369+
flip_dec=StageParameter(
370+
float, False, fmt="%f",
371+
msg="Shortcut to flip sign of Dec. If True, multiply Dec by -1. Excecuted BEFORE the rotator",
372+
),
373+
)
311374
sub_classes: dict[str, type[DynamicClass]] = {}
312375

313376
def __init__(self, **kwargs: Any):
@@ -343,7 +406,12 @@ def run(
343406
class RomanRubinReducer(RailReducer):
344407
"""Class to reduce the 'roman_rubin' simulation input files for pz analysis"""
345408

346-
config_options: dict[str, StageParameter] = dict(
409+
#config_options: dict[str, StageParameter] = dict(
410+
# name=StageParameter(str, None, fmt="%s", required=True, msg="Reducer Name"),
411+
# cuts=StageParameter(dict, {}, fmt="%s", msg="Selections"),
412+
#)
413+
config_options = RailReducer.config_options.copy()
414+
config_options.update(
347415
name=StageParameter(str, None, fmt="%s", required=True, msg="Reducer Name"),
348416
cuts=StageParameter(dict, {}, fmt="%s", msg="Selections"),
349417
)
@@ -430,7 +498,12 @@ class CardinalReducer(RailReducer):
430498
preprocessing stage was performed to put them into pyarrow parquet
431499
"""
432500

433-
config_options: dict[str, StageParameter] = dict(
501+
#config_options: dict[str, StageParameter] = dict(
502+
# name=StageParameter(str, None, fmt="%s", required=True, msg="Reducer Name"),
503+
# cuts=StageParameter(dict, {}, fmt="%s", msg="Selections"),
504+
#)
505+
config_options = RailReducer.config_options.copy()
506+
config_options.update(
434507
name=StageParameter(str, None, fmt="%s", required=True, msg="Reducer Name"),
435508
cuts=StageParameter(dict, {}, fmt="%s", msg="Selections"),
436509
)
@@ -479,6 +552,16 @@ def run(
479552
),
480553
)
481554

555+
rot_ra, rot_dec, rot_x = self.config.rotation_angle
556+
ra = pc.field("ra")
557+
if self.config.flip_dec == True:
558+
dec = pc.multiply(pc.scalar(-1), pc.field("dec"))
559+
else:
560+
dec = pc.field("dec")
561+
new_ra, new_dec = rotate_gal_pyarrow(ra, dec, float(rot_ra), float(rot_dec), rot_x_ang=float(rot_x))
562+
PROJECTIONS_CARDINAL[0]['shift_ra'] = new_ra
563+
PROJECTIONS_CARDINAL[0]['shift_dec'] = new_dec
564+
482565
column_projection = {k: pc.field(k) for k in COLUMNS_CARDINAL}
483566
projection = column_projection
484567
project_nodes = []
@@ -514,7 +597,12 @@ def run(
514597
class FlagshipReducer(RailReducer):
515598
"""Class to reduce the 'flagship' simulation input files for pz analysis"""
516599

517-
config_options: dict[str, StageParameter] = dict(
600+
#config_options: dict[str, StageParameter] = dict(
601+
# name=StageParameter(str, None, fmt="%s", required=True, msg="Reducer Name"),
602+
# cuts=StageParameter(dict, {}, fmt="%s", msg="Selections"),
603+
#)
604+
config_options = RailReducer.config_options.copy()
605+
config_options.update(
518606
name=StageParameter(str, None, fmt="%s", required=True, msg="Reducer Name"),
519607
cuts=StageParameter(dict, {}, fmt="%s", msg="Selections"),
520608
)
@@ -559,6 +647,17 @@ def run(
559647
),
560648
)
561649

650+
# add ra, dec projections with rotation
651+
rot_ra, rot_dec, rot_x = self.config.rotation_angle
652+
ra = pc.field("ra_mag_gal")
653+
if self.config.flip_dec == True:
654+
dec = pc.multiply(pc.scalar(-1), pc.field("dec_mag_gal"))
655+
else:
656+
dec = pc.field("dec_mag_gal")
657+
new_ra, new_dec = rotate_gal_pyarrow(ra, dec, float(rot_ra), float(rot_dec), rot_x_ang=float(rot_x))
658+
PROJECTIONS_FLAGSHIP[0]['ra'] = new_ra
659+
PROJECTIONS_FLAGSHIP[0]['dec'] = new_dec
660+
562661
column_projection = {k: pc.field(k) for k in COLUMNS_FLAGSHIP}
563662
projection = column_projection
564663
project_nodes = []
@@ -594,7 +693,12 @@ def run(
594693
class ComCamReducer(RailReducer):
595694
"""Class to reduce the 'com_cam' input files for pz analysis"""
596695

597-
config_options: dict[str, StageParameter] = dict(
696+
#config_options: dict[str, StageParameter] = dict(
697+
# name=StageParameter(str, None, fmt="%s", required=True, msg="Reducer Name"),
698+
# cuts=StageParameter(dict, {}, fmt="%s", msg="Selections"),
699+
#)
700+
config_options = RailReducer.config_options.copy()
701+
config_options.update(
598702
name=StageParameter(str, None, fmt="%s", required=True, msg="Reducer Name"),
599703
cuts=StageParameter(dict, {}, fmt="%s", msg="Selections"),
600704
)
@@ -679,7 +783,12 @@ def run(
679783
class DP1Reducer(RailReducer):
680784
"""Class to reduce the 'DP1' input files for pz analysis"""
681785

682-
config_options: dict[str, StageParameter] = dict(
786+
#config_options: dict[str, StageParameter] = dict(
787+
# name=StageParameter(str, None, fmt="%s", required=True, msg="Reducer Name"),
788+
# cuts=StageParameter(dict, {}, fmt="%s", msg="Selections"),
789+
#)
790+
config_options = RailReducer.config_options.copy()
791+
config_options.update(
683792
name=StageParameter(str, None, fmt="%s", required=True, msg="Reducer Name"),
684793
cuts=StageParameter(dict, {}, fmt="%s", msg="Selections"),
685794
)

0 commit comments

Comments
 (0)