Skip to content

Stokes response#193

Merged
amaurea merged 15 commits intomasterfrom
stokes_response
Jan 17, 2025
Merged

Stokes response#193
amaurea merged 15 commits intomasterfrom
stokes_response

Conversation

@amaurea
Copy link
Contributor

@amaurea amaurea commented Dec 1, 2024

Edit: No longer breaks backwards compatibility - see jan 10 comment.

The pointing matrix in so3g has had a hardcoded polarization efficiency of 100%. This pull request updates it to support arbitrary T and P responses per detector. This was needed to be able to represent demodulated timestreams as virtual detectors, which will be a separate pull request for sotodlib later. The virtual detector approach to demodulation mapmaking has the advantage that none of the rest of the code needs to know about the demodulation, only the demodulation function itself.

This breaks backwards compatibility in the interface of the FocalPlane object. It used to be an OrderedDict of [name, q] where q is an entry in a G3VectorQuat, but it is now a class with the members .quats, which is an [ndet,4] array of quaternion coefficients, and .resps, which is an [ndet,2] array of T and P responses. This means that FocalPlane no longer supports named detectors. This simplifies Assembly, which now just contains a FocalPlane instance.

This incompatibility will require corresponding changes in sotodlib. Do we have to go through a slow dance of add compatibility layer in sotodlib, wait for a few weeks, update so3g, then remove compatibility layer in sotodlib, again? I hope not. What do you think?

@amaurea
Copy link
Contributor Author

amaurea commented Dec 1, 2024

Looks like some other code needs to be updated to support the new FocalPlane class that replaced the old OrderedDict. Some places expect to be able to iterate over .items() and get names and G3VectorQuats out. How could this best be handled? The most quick-and-dirty solution would be to implement .items() and out it spit out things like that. However, this interface can't capture the new response stuff, so I fear that this would hide code that actually should fail because it needs to be updated to take the responses into account.

@amaurea amaurea requested a review from mhasself December 1, 2024 19:37
@amaurea
Copy link
Contributor Author

amaurea commented Jan 10, 2025

This pull request is now backwards compatible with the current version of sotodlib. Let's get it merged so I can get my new sotodlib branch merged! It's needed for my demod_ml_mapmaker for example.

@amaurea amaurea requested review from keskitalo and tskisner January 14, 2025 10:40
@tskisner
Copy link
Member

I'm not as familiar with this part of so3g, so perhaps better to have @mhasself review. I'll just note that there may be some conflicts with the new relative imports in the build system PR, so I will plan to rebase #192 after this PR is merged.

Copy link
Member

@mhasself mhasself left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, and for the iterations (offline) on the docs. Couple minor things inline... the only larger concern is whether focalplane.quats should be an array, or ... quats.

Comment on lines +184 to +186
[array([[ 0.25715457, -0.92720643, 0.9999161 , 0.01295328]]),
array([[ 0.24261138, -0.92726871, 0.86536489, 0.5011423 ]]),
array([[ 0.22806774, -0.92722945, 0.50890634, 0.86082189]])]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first sentence, below this, would appear now to be false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixing. We should check whether its sin(gamma) or sin(2*gamma) at some point too. I'm leaving in the sin(gamma) for now, since I don't know that it's wrong.

Comment on lines +168 to 171
# Get vector of quaternion pointings for detector 0
q_total = csl.Q * spt3g.core.quat(*fp.quats[0])
# Decompose q_total into lon, lat, roll angles
ra, dec, gamma = so3g.proj.quat.decompose_lonlat(q_total)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So why is focal_plane.quats an array instead of a G3VectorQuat? The quat math is really handy, and it will usually be more work for a user to dig up the quat / G3VectorQuat class for use as a cast, than it would be if someone just wanted to turn the quats into an array.

In toast/quaternion_array, numpy arrays are used to hold quaternions but in the opposite convention (real component last), so using a special quat object is also a way to be explicit about the convention.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about the same thing when preparing the pull request. I think the reason was that the C++ code needs a numpy array. Ideally G3VectorQuat would have the same memory layout as the corresponding numpy array of coefficients, so conversion would be free, but as it is, one has to do a conversion any time one wants to use any pointing operation. I wanted the internal representation to have no impedance mismatch with the low-level pointing code, and that's why I ended up with the numpy array.

That said, this is a small array. The conversion cost will be completely negligible unless we have very few samples. Let's just switch to it being a G3VectorQuat and eating the conversion cost.

# response too. Writing it like this handles both cases, and
# as a bonus(?) any combination of them
# FIXME: old sotodlib compat - remove later
xi, eta, gamma, T, P, Q, U, hwp, dets = cls._xieta_compat(xi,eta,gamma,T,P,Q,U,hwp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not quite work ... the 4th argument changes from "gamma=0" to "T=1". So old syntax that does not pass gamma will have gamma=1. Fix is to have default T=None in signature.

This might not actually affect any code that's out there...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed it in a more direct way by manipulating *args and **kwargs directly, basically just removing the first element of args.

Comment on lines +367 to +370
"""Slice the FocalPlane with slice sel, resulting in a new
FocalPlane with a subset of the detectors. Only 1d slice supported,
not integers or multidimensional slices. Example: ``focal_plane[10:20]``
would make a sub-FocalPlane with detector indices 10,11,...,19.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention that 1d index array, and boolean mask selection arrays (my favorite) also work fine.

Comment on lines +222 to +224
If fplane is not None, then the result will be
[n_samp,{lon,lat,cos2psi,sin2psi}]. Otherwise it will
be [n_det,n_Samp,{lon,lat,cos2psi,sin2psi}]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove "not".

n_Samp -> n_samp

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

t, az, el = get_scan()
csl = proj.CelestialSightLine.az_el(t, az, el, weather='vacuum', site='so')
fp = proj.FocalPlane.from_xieta(['a', 'b'], [0., .1*DEG], [0, .1*DEG])
fp = proj.FocalPlane.from_xieta([0., .1*DEG], [0, .1*DEG])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also please fix demos/demo_proj_hp.py ... this does it:

--- a/demos/demo_proj_hp.py
+++ b/demos/demo_proj_hp.py
@@ -191,13 +191,6 @@ def simulate_detectors(ndets, rmax, seed=0):
     eta = rr * np.sin(phi)
     return xi, eta, gamma
 
-def make_fp(xi, eta, gamma):
-    fp = so3g.proj.quat.rotation_xieta(xi, eta, gamma)
-    so3g_fp = so3g.proj.FocalPlane()
-    for i, q in enumerate(fp):
-        so3g_fp[f'a{i}'] = q
-    return so3g_fp
-
 def extract_coord(sight, fp, isignal=0, groupsize=100):
     coord_out = []
     for ii in range(0, len(fp), groupsize):
@@ -207,15 +200,14 @@ def extract_coord(sight, fp, isignal=0, groupsize=100):
     return coord_out
 
 def make_signal(ndets, rmax, sight, isignal, seed=0):
-    xi, eta, gamma = simulate_detectors(ndets, rmax, seed)
-    fp = so3g.proj.quat.rotation_xieta(xi, eta, gamma)
+    fp = so3g.proj.FocalPlane.from_xieta(*simulate_detectors(ndets, rmax, seed))
     signal = extract_coord(sight, fp, isignal)
     return signal
 
 def make_tod(time, az, el, ndets, rmax, isignal, seed=0):
     sight = so3g.proj.CelestialSightLine.naive_az_el(time, az, el)
     signal = make_signal(ndets, rmax, sight, isignal, seed)
-    so3g_fp = make_fp(*simulate_detectors(ndets, rmax, seed))
+    so3g_fp = so3g.proj.FocalPlane.from_xieta(*simulate_detectors(ndets, rmax, seed))
     asm = so3g.proj.Assembly.attach(sight, so3g_fp)
     return signal, asm

@amaurea amaurea requested a review from mhasself January 16, 2025 17:23
mhasself
mhasself previously approved these changes Jan 16, 2025
Copy link
Member

@mhasself mhasself left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last couple of minor things... but approving.

Squash this on merge!!

quats: G3VectorQuat representing the pointing quaternions for
each detector. Can be turned into a numpy array of coefficients
with np.array(). Or call .coeffs() to get them directly.
resps: Array of float64 with shape [ndet,2] representing the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's float32 in the code...

"""This class collects the focal plane positions and orientations for
multiple named detectors. The classmethods can be used to
construct the object from some common input formats.
Members:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if sphinx likes "members" -- can you change to "Attributes:"?

def from_xieta(cls, *args, **kwargs):
"""
def from_xieta(cls, xi, eta, gamma=0, T=1, P=1, Q=1, U=0, hwp=False):
Construct a FocalPlane from focal plane coordinates (xi,eta).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention "for backwards compatibility, the signature from_xieta(names, xi, eta, gamma=0) is also supported; but this will be removed in the future."

Copy link
Member

@mhasself mhasself left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooray! So stoked!

Squash on merge!

@amaurea amaurea merged commit 4081bfe into master Jan 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants