Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add time_constant function #179

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Conversation

d-hoshino2626
Copy link

I added some APIs for a time constant measurement with the wire grid and the HWP into the wiregrid.py.
Here is the added functions and its explanation:

  1. _check_wiregrid_position()
    • This function checks the wire grid is off the window or on the window from the logs for the limit switches.
  2. _stop_hwp_with_wiregrid()
    • This function confirms the wire grid is on the window and stops the HWP.
  3. _spin_hwp_with_wiregrid(target_hwp_direction)
    • This function confirms the wire grid is on the window and spins up the HWP in the direction same as target_hwp_direction.
    • target_hwp_direction is bool argument, 'forward' or 'backward'.
  4. _reverse_hwp_with_wiregrid(initial_hwp_direction, streaming=False, stepwise_before=False, stepwise_after=False)
    • This function reverses the HWP spinning direction from initial_hwp_direction using _stop_hwp_with_wiregrid() and _spin_hwp_with_wiregrid(target_hwp_direction)
    • Also, the stepwise rotation can be performed before and/or after the reversing. The bool arguments stepwise_before and stepwise_after are to identify whether performing them or not.
    • SMuRF streaming can be performed during the stepwise rotation and HWP speed change.
  5. time_constant(initial_hwp_direction, stepwise_first=True, stepwise_last=True, stepwise_mid=False, repeat=1)
    • This is the main public API. This function inserts the wire grid, changes the HWP direction as many times as repeat, and ejects the wire grid.
    • Also, it takes bias steps before and after the insertion and ejection, execute the stepwise rotations before, in the middle of, or after the HWP direction changes.

Any questions and comments are welcome.

Copy link

@ykyohei ykyohei left a comment

Choose a reason for hiding this comment

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

I comment in terms of hwp controls for this type of measurements that spins up and down hwp.

  1. I suggest streaming separately for forward spin and backward spin. This is because hwp solution pipeline assumes constant rotation direction within the data range. It requires more work to analyze it if both forward and reverse spin are included in the data range.

  2. For the hwp rotation direction estimation, you can just use. apply_hwp_angle_model(tod, on_sign_ambiguous='offcenter'). It might be possible to simplify functions. The hwp rotation direction estimation has to be different from cmb observations bacause hwp_solution.pid_direction is not reliable for spin down measurement. This is because commanded spin direction and actual spin direction is different when hwp is spinning down. hwp_solution.offcenter_direction will be the robust rotation direction estimator for this hwp spin down up measurements.

if stepwise_before:
rotate(False)
# Stop the HWP
_stop_hwp_with_wiregrid()
Copy link

Choose a reason for hiding this comment

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

I suggest to add following here, to stream separately for forward and backward

run.smurf.stream('off')
run.smurf.stream('on')

@d-hoshino2626
Copy link
Author

Thanks Kyohei!

  1. I consulted with Hironobu about whether it is better to separate the stream or not and his opinion was not to separate. I heard from him that the HWP pipeline works well if the stream is separated properly by analysis and it is not difficult. He will mainly work on this analysis, so it would be better to follow his opinion, I think.
  2. Thanks for your information! I will re-write it using apply_hwp_angle_model(tod, on_sign_ambiguous='offcenter').

Copy link
Contributor

@sadachi5 sadachi5 left a comment

Choose a reason for hiding this comment

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

I add comments around insert() and eject().

run.smurf.bias_step(tag=bs_tag, concurrent=True)

# Insert the wiregrid
insert()
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add detector streams during the insert() and eject(), and add sleep (5 sec?) before and after the insert()/eject()?
Sleep times have two purposes. One is to see the detector response with/without the wiregrid right before and after the insertion/ejection. The second is to wait for the wiregrid to be stable before the bias-step measurement.

Copy link
Author

Choose a reason for hiding this comment

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

I added detector streams and sleep. In these streams, I used new tags, wg_inserting and wg_ejecting. The commit for these modification is here: f1e1157

run.smurf.bias_step(tag=bs_tag, concurrent=True)

# Eject the wiregrid
eject()
Copy link
Contributor

Choose a reason for hiding this comment

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

Here, I want to add also detector stream ON --> sleep --> eject --> sleep.

@ykyohei
Copy link

ykyohei commented Oct 18, 2024

  1. I consulted with Hironobu about whether it is better to separate the stream or not and his opinion was not to separate. I heard from him that the HWP pipeline works well if the stream is separated properly by analysis and it is not difficult. He will mainly work on this analysis, so it would be better to follow his opinion, I think.

I'm concerned about future automation of gl calibration. I will let you decide, but I don't see any disadvantage of splitting stream. If you split, you don't need to re-calculate hwp solution.

@codecov-commenter
Copy link

Codecov Report

Attention: Patch coverage is 4.76190% with 100 lines in your changes missing coverage. Please review.

Please upload report for BASE (main@1fc4725). Learn more about missing BASE report.

Files with missing lines Patch % Lines
src/sorunlib/wiregrid.py 4.76% 100 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #179   +/-   ##
=======================================
  Coverage        ?   84.15%           
=======================================
  Files           ?       10           
  Lines           ?      631           
  Branches        ?        0           
=======================================
  Hits            ?      531           
  Misses          ?      100           
  Partials        ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@hnakata-JP
Copy link
Contributor

Thanks, Kyohei and Daiki.
As Daiki says, I prefer taking the stream all at once. In my previous analysis, I divided the stream by two by the quad key of hwp_solution and removed about 2000 samples just before/after the split point. What I am concerned about is an opportunity for an interruption between these measurements. I think it would be better to remove any room between them in order not to make any other ambiguous gap.

@ykyohei
Copy link

ykyohei commented Oct 21, 2024

Thanks, Kyohei and Daiki. As Daiki says, I prefer taking the stream all at once. In my previous analysis, I divided the stream by two by the quad key of hwp_solution and removed about 2000 samples just before/after the split point. What I am concerned about is an opportunity for an interruption between these measurements. I think it would be better to remove any room between them in order not to make any other ambiguous gap.

OK, I'm fine if you don't mind recalculating the hwp angle.

Copy link
Member

@BrianJKoopman BrianJKoopman left a comment

Choose a reason for hiding this comment

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

Here's a first pass at a review from me. A couple general comments/comments:

  • Please wrap lines to ~80 characters. I'm not super strict on the exact length, but there are some pretty long lines (100+) characters here. Try to be consistent with the existing code here.
  • How often will this be run? Daily? Monthly? Once? I was a bit surprised to see the PR, thinking we had implemented the regular calibration in wiregrid.calibrate() already.
  • We need tests. I've been pretty meticulous to keep 100% coverage from the get-go in this repo. sorunlib is tricky because I often can't just interrupt daily operations to test some new functionality. Check out the tests/ directory to see existing tests. New tests for this will go in tests/test_wiregrid.py. There are instructions for running tests in the main README. Also, please merge in the latest main, there will be a new code coverage check that runs.

Comment on lines +205 to +216
ls_status = resp.session['data']['fields']['limitswitch']
if (ls_status['LSR1'] == 1 or ls_status['LSL1'] == 1) and not (ls_status['LSR2'] == 1 and ls_status['LSL2'] == 1):
position = 'outside'
elif (ls_status['LSR2'] == 1 or ls_status['LSL2'] == 1) and not (ls_status['LSR1'] == 1 or ls_status['LSL1'] == 1):
position = 'inside'
else:
raise RuntimeError("The wiregrid position is unknown. The limitswitch response is like this:\n" +
"LSR1: {}\n".format('ON' if ls_status['LSR1'] == 1 else 'OFF')
+ "LSL1: {}\n".format('ON' if ls_status['LSL1'] == 1 else 'OFF')
+ "LSR2: {}\n".format('ON' if ls_status['LSR2'] == 1 else 'OFF')
+ "LSL2: {}\n".format('ON' if ls_status['LSL2'] == 1 else 'OFF')
+ "Aborting...")
Copy link
Member

Choose a reason for hiding this comment

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

I've wondered if there was a method for determining whether the wiregrid was inserted or not for a while now. It would be better to put this logic into the Agent itself, within the acq process' session.data. Then here you would only need to check resp.session['data']['fields']['position']. This also exposes the wiregrid state for other clients, such as ocs-web.

Copy link
Author

@d-hoshino2626 d-hoshino2626 Nov 8, 2024

Choose a reason for hiding this comment

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

I agree with you. I created the PR to implement the position data in session.data.

Copy link
Member

Choose a reason for hiding this comment

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

Great, I just reviewed that. Once it's merged we should drop the code here and use the session.data.

Comment on lines +211 to +212
raise RuntimeError("The wiregrid position is unknown. The limitswitch response is like this:\n" +
"LSR1: {}\n".format('ON' if ls_status['LSR1'] == 1 else 'OFF')
Copy link
Member

Choose a reason for hiding this comment

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

If this stays here for the moment...pre-commit is complaining about this particular line. Move the + down from line 211 to the start of line 212, like you have the rest of the lines starting with +.

Comment on lines +231 to +232
else:
raise RuntimeError("Unknown wiregrid position. Aborting...")
Copy link
Member

Choose a reason for hiding this comment

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

_check_wiregrid_position() only ever returns 'outside' or 'inside', so this code path will never execute.

Comment on lines +430 to +433
# Check the initial HWP direction
if initial_hwp_direction not in ['forward', 'backward']:
raise RuntimeError("Initial HWP direction should be either 'forward' or 'backward'. Aborting...")
current_hwp_direction = initial_hwp_direction
Copy link
Member

Choose a reason for hiding this comment

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

This must be able to be determined automatically, no? I would think from the monitor process' session.data, from 'hwp_freq'.

Copy link
Author

Choose a reason for hiding this comment

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

I asked the HWP team about how to know its current spin direction in a robust and fast way, but I was told there is no way. The scheduler can remember the last command to set the direction (the arugment in run.hwp.set_freq()) and whether it was finished successful, so the scheduler can feed its information into the time_constant() function. I thought this is safer than reading the session.data. Does this seem overly verbose?

Copy link

Choose a reason for hiding this comment

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

'direction' in session.data of pid controller agent might be good for initial direction? This value is not reliable when we spin down hwp, or when hwp is stopped, but it is reliable when hwp is spinning stablly.

Copy link
Author

Choose a reason for hiding this comment

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

For now, I assumed the scheduler knows the HWP spin direction but this is also under the condition which the HWP is already spinning. Using 'direction' would be more simple than now. I will modify. Thanks Kyohei!

_check_motor_on()
_check_telescope_position(elevation_check=True, boresight_check=False)

if _check_zenith:
Copy link
Member

Choose a reason for hiding this comment

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

_check_zenith() is a function call, this is missing the parentheses.

rotate(continuous=True, duration=10)

# Bias step (the wire grid is off the window)
bs_tag = f'biasstep, wg_time_constant, wg_ejected, hwp_2hz_{current_hwp_direction}' + el_tag
Copy link
Member

Choose a reason for hiding this comment

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

A few comments on these tags:

  • Do we need a 'biasstep' tag when taking a bias step? I'll admit I don't know how these tags are used downstream, but is the operation not implicit?
  • The ideal state might be wiregrid ejected and HWP rotating at 2Hz, but that isn't actually checked until _reverse_hwp_with_wiregrid() is called later on. I would want to verify those before tagging a bias step with them. My recommendation is to add them to the checks above, at the start of the method.

Comment on lines +241 to +242
# Check the wiregrid position
position = _check_wiregrid_position()
Copy link
Member

Choose a reason for hiding this comment

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

The checks in both _stop_hwp_with_wiregrid() and _spin_hwp_with_wiregrid() seem like are duplicating the check, and as a result making the functions more complicated than they need to be. They're only used in _reverse_hwp_with_wiregrid(), which is only called after insert(). If insert() fails to insert the wiregrid it raises and the schedule with exit.

If you still want to check the wiregrid is inserted, I'd say do the check in _reverse_hwp_with_wiregrid() before doing anything else. You can then assume its inserted in these other two functions, and they simplify down to:

def _stop_hwp():
    """Stop the HWP."""
    print("Starting to stop the HWP.")
    run.hwp.stop(active=True)
    print("The HWP stopped successfully.")
def _spin_hwp(target_hwp_direction):
    """Spin the HWP to the target direction at 2Hz.
    Args:
        target_hwp_direction (str): Target HWP direction, 'forward' or 'backward'.
    """
    if target_hwp_direction == 'forward':
        print("Starting to spin up the HWP in forward.")
        run.hwp.set_freq(freq=2.0)
        print("The HWP is spinning at 2Hz in forward successfully.")
    elif target_hwp_direction == 'backward':
        print("Starting to spin up the HWP in backward.")
        run.hwp.set_freq(freq=-2.0)
        print("The HWP is spinning at 2Hz in backward successfully.")

Comment on lines +273 to +276
if current_hwp_direction == 'forward':
target_hwp_direction = 'backward'
elif current_hwp_direction == 'backward':
target_hwp_direction = 'forward'
Copy link
Member

Choose a reason for hiding this comment

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

I want to make sure we get this convention correct. The HWP supervisor agent has an argument called --forward-dir that defines 'forward' to be clockwise or counter-clockwise, which seems to imply to me this isn't really a fixed direction. Is there some document about what our convention should be?

(I tried to avoid this question myself when writing hwp.set_freq(), but it seems I can't escape it.)

Copy link
Author

@d-hoshino2626 d-hoshino2626 Nov 8, 2024

Choose a reason for hiding this comment

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

I found the confluence page about the rotation direction definition: CHWP main page -- Rotation direction definition. The cw/ccw seems to correspond to the PID value depending on each satp and the PID corresponds to 'forward' or 'reverse' not depending on the satp. We should pay attention which physical direction the 'forward' and 'reverse' mean - cw or ccw. This document uses 'reverse' instead of 'backward'.
Also, I found the 'forwards' and 'backwards' (not 'reverse') in the observation plans on the confluence. I guess a lot of people may make the 'forwards' correspond to the plus frequency like run.hwp.set_freq(freq=+2) and 'backwards' to the minus.

It may be good time to integrate the convention.

@d-hoshino2626
Copy link
Author

Thanks Brian! Sorry for the late reply and the lack of explanation. I'm replying about some discussion points.

  • This measurement is to calibrate the detectors' time constant by changing the HWP spin direction with the wiregrid inserted. This will be performed about once a week. Now we change the HWP spin direction once a week, so we will do it on that time.

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.

6 participants