Skip to content
This repository was archived by the owner on Sep 2, 2024. It is now read-only.

Add deadtime optimisation#755

Merged
DominicOram merged 25 commits into
mainfrom
754_add_deadtime_optimisation
Jul 4, 2023
Merged

Add deadtime optimisation#755
DominicOram merged 25 commits into
mainfrom
754_add_deadtime_optimisation

Conversation

@olliesilvester

@olliesilvester olliesilvester commented Jun 22, 2023

Copy link
Copy Markdown
Contributor

Fixes #754

Link to dodal PR (if required): DiamondLightSource/dodal#102

To test:

  1. Confirm deadtime optimisation logic is the same of that in the GDA script mx-config/scripts/attenuation_optimiser.py, in the run_xspress3mini_optimisation_routine()

  2. Confirm new tests run and are sensible

@codecov

codecov Bot commented Jun 22, 2023

Copy link
Copy Markdown

Codecov Report

Merging #755 (47b245a) into main (473c4c8) will decrease coverage by 1.63%.
The diff coverage is 71.62%.

❗ Current head 47b245a differs from pull request most recent head 93e800e. Consider uploading reports for the commit 93e800e to get more accurate results

@@            Coverage Diff             @@
##             main     DiamondLightSource/hyperion#755      +/-   ##
==========================================
- Coverage   94.19%   92.56%   -1.63%     
==========================================
  Files          37       37              
  Lines        1808     1869      +61     
==========================================
+ Hits         1703     1730      +27     
- Misses        105      139      +34     
Impacted Files Coverage Δ
...emis/experiment_plans/optimise_attenuation_plan.py 71.42% <71.62%> (-23.02%) ⬇️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

)
)

if upper_limit < lower_limit:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this check is implied in the if statement before, so I will remove this

@olliesilvester
olliesilvester force-pushed the 754_add_deadtime_optimisation branch 2 times, most recently from 25f8d94 to 4bf3e1f Compare June 28, 2023 09:53
@olliesilvester
olliesilvester marked this pull request as ready for review June 28, 2023 12:07

@DominicOram DominicOram left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good work, thank you. Some comments in code

Comment on lines +82 to +97
def deadtime_is_transmission_optimised(
direction, deadtime, deadtime_threshold, transmission
) -> tuple[bool, bool]:
flip_direction = False
if direction:
if deadtime >= deadtime_threshold:
flip_direction = True
return False, flip_direction
else:
# The 0.9 is hardcoded in GDA
if transmission >= 0.9:
return True, flip_direction
else:
if deadtime <= deadtime_threshold:
return True, flip_direction
return False, flip_direction

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should: This function will benefit from a docstring to explain parameters, what it's doing etc.

direction = True
LOGGER.info(f"Target deadtime is {deadtime_threshold}")

while True:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should: while True can accidentally end up infinite quickly. Can we have a max_cycles like for the other optimiser

def deadtime_optimisation(
attenuator, xspress3mini, zebra, transmission, increment, deadtime_threshold
):
direction = True

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: It would be more readable to have this set as an enum for positive/negative rather than True/False

Comment on lines +134 to +227
total_time = xspress3mini.channel_1.total_time.get()
reset_ticks = xspress3mini.channel_1.reset_ticks.get()

LOGGER.info(f"Current total time = {total_time}")
LOGGER.info(f"Current reset ticks = {reset_ticks}")
deadtime = 0
if total_time != reset_ticks:
deadtime = 1 - abs(total_time - reset_ticks) / (total_time)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should: Can we document what this calculation is doing and why?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

e.g. what does reset_ticks mean?

Comment on lines +123 to +174
def deadtime_optimisation(
attenuator, xspress3mini, zebra, transmission, increment, deadtime_threshold
):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should: Can we have a docstring on this function and type hints?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll add a docstring to the total counts version as well

Comment on lines +103 to +104
if transmission > 0.999:
transmission = 1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should: I think @neil-i03 was talking about wanting a max value on transmission that we can set in a config somewhere. 100% is too high. Can you pull out a max/min into constants for now and have a separate function that checks them and throws errors if they're reached

Comment on lines +91 to +93
# The 0.9 is hardcoded in GDA
if transmission >= 0.9:
return True, flip_direction

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Must: Are we sure this is right? It looks wrong to me because if the deadtime is less than threshold and the transmission less than 90% the function returns nothing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this is the 90% transmission limit in GDA Neil was talking about. If the transmission is >=90% and deadtime is still higher than the threshold, it just uses that as the optimised transmission. I'll add parameters for max and min as you suggested in previous comment. Also I'll add a logging message if it uses the maximum transmission as the optimised value.

yield from arm_zebra(zebra)


def deadtime_is_transmission_optimised(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could: It might be cleaner if the function returned whether you're optimised and then which direction to move in, rather than the flip

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could: It would be even more clear if we just separate it out into two functions, one to work out if we're there and one to work out if to flip. It might lead to some repeated work but clarity is more important than efficiency here

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should: Can we have some separate tests on this function

break

if flip_direction:
direction = not direction

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should: We need some tests that cause the direction to be flipped

Comment on lines +223 to +226
def test_deadtime_calc_new_transmission_gets_correct_value():
assert deadtime_calc_new_transmission(True, 0.05, 2) == 0.1
assert deadtime_calc_new_transmission(False, 0.05, 2) == 0.025
assert deadtime_calc_new_transmission(True, 1, 2) == 1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: Better done using parameterize, will be clearer when one fails

return status


@pytest.mark.skip(reason="Flakey test which is refactored in another PR")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Skipped this test for now as it's refactored in #763 (although still isn't working properly after that)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Coverage is low due to this test being skipped, but #763 should fix that

@olliesilvester
olliesilvester force-pushed the 754_add_deadtime_optimisation branch from d85f7e1 to 76c9209 Compare July 3, 2023 14:47
deadtime = 0

""" Deadtime is the time after each event during which the detector cannot record another event.
The reset ticks PV stops ticking while the detector is unable to process events, so the absolute difference between the total time and the

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not 100% sure about this description of the reset ticks, but given the formula and definition for deadtime it looks like it must be something like this

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think we should have comments in that we're not sure about. Let's look more inhttps://github.com/DiamondLightSource/python-artemis/issues/776

@DominicOram DominicOram left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

One minor issue

Comment on lines +89 to +100
def get_new_direction(direction: Direction, deadtime, deadtime_threshold):
if direction == Direction.POSITIVE:
if deadtime >= deadtime_threshold:
direction = Direction.NEGATIVE
return direction


def calculate_new_direction(direction: Direction, deadtime, deadtime_threshold):
if direction == Direction.POSITIVE:
if deadtime > deadtime_threshold:
direction = Direction.NEGATIVE
return direction

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should: These functions are basically the same, only one is used, can we remove the other?

@DominicOram
DominicOram merged commit 6b7abf5 into main Jul 4, 2023
@DominicOram
DominicOram deleted the 754_add_deadtime_optimisation branch July 4, 2023 16:28
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add deadtime optimisation for attenuator

2 participants