Skip to content

Store schedule and pulse history details from output for post processing - #240

Merged
gonuke merged 47 commits into
svalinn:mainfrom
anu1217:post_proc_sched
Feb 17, 2026
Merged

gonuke merged 47 commits into
svalinn:mainfrom
anu1217:post_proc_sched

Conversation

@anu1217

@anu1217 anu1217 commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

This script writes the schedule and pulsing history related output to dictionaries so that they can be accessed and manipulated in other scripts. The schedule details are written in a nested dictionary to accommodate sub-schedules.

fixes #220

add more logic

more logic

store output

combine multiple commits into one
@anu1217 anu1217 changed the title Store output details for post processing Store schedule and pulse history details from output for post processing Feb 3, 2026

@gonuke gonuke left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There is a repeated suggestion here to use a while loop instead of a for loop when looking through the lines. This is a typical design pattern for a parser.

A "design pattern" is typical and usually well-tested algorithm pattern for accomplishing a given task. Design patterns transcend languages and when you use them, others can quickly grasp what the intent of the code is and not worry about understanding the high level algorithm, but quickly focus on the details.

A "parser" is a typical pattern for moving through a stream of tokens (lines in your case) with two primary things that can happen at each line:

  • change the "state"
  • take action depending on the state

Zero, one, or both of these things can happen at each new token.

The most common action is to process the value of a token into some other data structure.

Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Resolved

def read_out(output_path):
with open(output_path, "r") as output_file:
lines = output_file.readlines()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just learned that this might make life easier by stripping all the lines when you read:

Suggested change
lines = output_file.readlines()
lines = output_file.read().striplines()

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.

Hmm...output_file.read() returns a string that has no method called striplines(). I also can't seem to find any documentation on this method?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Outdated
matches = []
for key, value in top_dict.items():

if key.startswith(search_str):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

At this point, haven't you parsed things such that this should be an equality test rather than a startswith() test?

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.

This is because of the schedule ... lines. Because the name of the schedule is not known beforehand, it is not possible to find an exact match. At first, I set each key to be just "schedule" without the name, but in that case make_nested_dict() will only store the last schedule line.

Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Outdated
}
search_fields = ["schedule", "pulse_entry:"]
for search_field in search_fields:
matches = search_for_match(sch_dict, search_field)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

search_for_match seems to be designed to return a list, but this method seems to assume that only value will be found. Is that right?

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.

Initially I set this up such that the list was essentially a dummy list with only one entry, but this was not correctly capturing all of the entries. The list now contains multiple entries, which are values corresponding to each of the search field keys. The loop also iterates over each of the entries in the list.

Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Resolved
anu1217 and others added 16 commits February 4, 2026 11:04
Co-authored-by: Paul Wilson <paul.wilson@wisc.edu>
Co-authored-by: Paul Wilson <paul.wilson@wisc.edu>
Co-authored-by: Paul Wilson <paul.wilson@wisc.edu>
Co-authored-by: Paul Wilson <paul.wilson@wisc.edu>
change order of multiplication

fix indentation

modify logic for counter

further modify counter

correctly increment counter when pulse_entry is on the same indentation level as schedule

combine similar changes into one commit
@anu1217

anu1217 commented Feb 5, 2026

Copy link
Copy Markdown
Contributor Author

I realized that the counter I had originally wasn't correctly keeping track of pulse entries when they were on the same indentation level as the schedule lines. The new counter should be able to account for those cases.

@gonuke gonuke left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think make_nested_dict is more complicated than necessary- see my comments

Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Outdated
unit_multipliers = make_unit_multipliers()
while not lines[line_idx].startswith("pulse_history:"): # next section of output
child_level = lines[line_idx].count('\t')
newline_name = lines[line_idx].strip().split()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This variable name doesn't really seem to express what this data is...

Comment thread tools/sched_post_processor.py Outdated
last_upper_indent_level[child_level][f"pulse_entry num_{counter}_in_sched"] = make_pe_sub_dict(newline_name,
unit_multipliers)
last_upper_indent_level[child_level + 1] = last_upper_indent_level[child_level][f"pulse_entry num_{counter}_in_sched"]
counter = counter_dict[child_level] = counter + 1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think you need to set counter here... you never use it before it gets reassigned

Comment thread tools/sched_post_processor.py Outdated
last_upper_indent_level[child_level + 1] = last_upper_indent_level[child_level][newline_name[1].strip("':")]
line_idx += 1
new_child_level = lines[line_idx].count('\t')
counter = counter_dict[new_child_level] = 1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You don't need counter here... you never use it before it gets reassigned

Comment thread tools/sched_post_processor.py Outdated
last_upper_indent_level[child_level + 1] = last_upper_indent_level[child_level][f'schedule {newline_name[1]}']
line_idx += 1
new_child_level = lines[line_idx].count('\t')
counter = counter_dict[new_child_level] = 1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this based on the assumption that this will be the first time we encounter this child_level such that we are initializing this counter for the first time? Or are we also resetting this counter under some circumstances?

I think it's the latter, in which case I would do this elsewhere. Also - why not initialize with 0?

Comment thread tools/sched_post_processor.py Outdated
counter_dict = {}
unit_multipliers = make_unit_multipliers()
while not lines[line_idx].startswith("pulse_history:"): # next section of output
child_level = lines[line_idx].count('\t')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If my understanding about the counters is correct, you can initialize new counters here by seeing whether a counter already exists at this child_level and initializing one if it doesn't, and then avoid doing it in two places below.

Comment thread tools/sched_post_processor.py Resolved
@anu1217

anu1217 commented Feb 5, 2026 •

Copy link
Copy Markdown
Contributor Author

To explain the idea behind the counters further...I'm initializing it to 1 instead of 0 so that we can track the order of the pulse entries belonging to a schedule in a way that's perhaps more human readable.

There are a few different scenarios where this counter needs to be set/reset.

a) The else condition initializes the counter to 1 at child_level 1 to account for the case where a pulse entry is part of the top schedule and not any sub-schedule.
b) Whenever a schedule line is found, the counter is reset at the line following it, since a pulse_entry must follow the schedule line.
c) The counter is reset at the line after schedule, and not at schedule itself, to avoid changing the pulse entry count at the indentation level of schedule. As an example, in:

top ...
  pulse_entry
  sch ..
      pulse_entry
  pulse_entry

resetting the counter at the 3rd line would mean that the last pulse entry is incorrectly counted as the 1st pulse in the top schedule.

This did take some trial and error to figure out, and would probably be a good discussion topic for hacker-within.

New design structure for nested dictionary in svalinn#240 (svalinn/ALARA)

@gonuke gonuke left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here is a coordinated set of changes to streamline the algorithm. It is designed to always initialize things correctly rather than have to check later to do that.

  • always put a "children" entry in the dictionary when we make it
  • initialize ancestors rather than wait to find "top_schedule"

Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Resolved
Comment thread tools/sched_post_processor.py Resolved
anu1217 and others added 5 commits February 17, 2026 10:46
Co-authored-by: Paul Wilson <paul.wilson@wisc.edu>
Co-authored-by: Paul Wilson <paul.wilson@wisc.edu>
Co-authored-by: Paul Wilson <paul.wilson@wisc.edu>
Co-authored-by: Paul Wilson <paul.wilson@wisc.edu>
Co-authored-by: Paul Wilson <paul.wilson@wisc.edu>

@gonuke gonuke left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems OK now, but still probably a little unusual in the way the parsing is put together.

@gonuke
gonuke merged commit 5e9e872 into svalinn:main Feb 17, 2026
1 check passed
@anu1217
anu1217 deleted the post_proc_sched branch February 24, 2026 19:52
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.

Develop post-processing methods to read schedule into convenient data structure

2 participants