Skip to content

Add a pellet-aware time step calculator. - #2313

Open
MateoSochard wants to merge 7 commits into
mainfrom
pellet_aware_time_step_calculator
Open

Add a pellet-aware time step calculator.#2313
MateoSochard wants to merge 7 commits into
mainfrom
pellet_aware_time_step_calculator

Conversation

@MateoSochard

Copy link
Copy Markdown
Collaborator

New time step calculator named pellet_aware_time_step_calculator, created for the coupling between TORAX and HPI2-NN, that aligns time steps with pellet injection (trigger) times and ablation windows, resolving each ablation window as a single step. Generic for every pellet source with trigger_times/frequency and ablation_time in their runtime parameters. Also Added documentation for this new time step calculator and the HPI2-NN pellet source.

New time step calculator named pellet_aware_time_step_calculator, created for the coupling between TORAX and HPI2-NN, that aligns time steps with pellet injection (trigger) times and ablation windows, resolving each ablation window as a single step. Generic for every pellet source with trigger_times/frequency and ablation_time in their runtime parameters. Also Added documentation for this new time step calculator and the HPI2-NN pellet source.

Co-authored-by: alexpanera <paneralex@hotmail.com>
PELLET_AWARE = 'pellet_aware'

@enum.unique
class BaseTimeStepCalculatorType(enum.Enum):

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.

This should be a StrEnum

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

base_calculator is now a nested time step calculator config object instead of a string enum, so this enum is gone.

self,
base_calculator_type: str = 'chi',
trigger_tolerance: float = 1e-8,
pellet_source_name: str = 'pellet',

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 not be needed. There is only ever a single pellet source name. Can hard-code it internally to 'pellet'

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

Comment on lines +52 to +53
pellet_source_name: For the 'pellet_aware' calculator, the name of the pellet
source to align time steps with. Defaults to 'pellet'.

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 not be needed. There is only ever a single pellet source name. Can hard-code it internally to 'pellet'

dtype = dt_standard.dtype

t = sim_state.t
pellet_params = runtime_params.sources.get(self._pellet_source_name)

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.

just pellet_params = runtime_params.sources.get('pellet') and remove pellet_source_name from everywhere

CHI = 'chi'
FIXED = 'fixed'
FROM_PREVIOUS_DT = 'from_previous_dt'
PELLET_AWARE = 'pellet_aware'

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.

There is currently no validation that if the TimeStepCalculator is PELLET_AWARE , that an appropriate pellet model is actually being used.

Recommend making a new Pydantic validator on ToraxConfig . Here is an agent recommended function for that

@pydantic.model_validator(mode='after')
def _check_pellet_aware_time_step_calculator_compatibility(self) -> typing_extensions.Self:
  if self.time_step_calculator.calculator_type == time_step_calculator_pydantic_model.TimeStepCalculatorType.PELLET_AWARE:
    if self.sources.pellet is None:
      raise ValueError(
          "time_step_calculator.calculator_type='pellet_aware' requires a pellet "
          "source to be configured under sources.pellet."
      )
    has_trigger_times = hasattr(self.sources.pellet, 'trigger_times')
    has_frequency = hasattr(self.sources.pellet, 'frequency')
    has_ablation_time = hasattr(self.sources.pellet, 'ablation_time')
    if not ((has_trigger_times or has_frequency) and has_ablation_time):
      raise ValueError(
          f"time_step_calculator.calculator_type='pellet_aware' requires a pellet "
          f"source supporting discrete pellet injection ('trigger_times' or 'frequency', "
          f"and 'ablation_time'), e.g. 'hpi2_nn'. "
          f"Got pellet model_name='{self.sources.pellet.model_name}'."
      )
  return self

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

)
else:
in_ablation = jnp.logical_and(
jnp.logical_and(positive_frequency, after_start),

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.

this shouldn't be needed. We should validate positive_frequency on the Pydantic level, and as mentioned above, we should only be in this code pathway if the pellet injector is "ON" as seen by the new attribute.

delta_to_next_period,
)
dt_trigger = jnp.where(
jnp.logical_and(positive_frequency, after_start),

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.

as above

if self._dt_after_pellet is not None:
dt_after_pellet = jnp.asarray(self._dt_after_pellet, dtype=dtype)
in_post_pellet_freq = jnp.logical_and(
jnp.logical_and(positive_frequency, after_start),

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.

as above

Comment thread docs/configuration.rst Outdated
Comment on lines 2461 to 2462

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.

This can be updated

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

Comment thread docs/configuration.rst
also speed up simulations where a small dt is required for a short section,
but then a larger dt is be appropriate for the remainder of the simulation.

* ``'pellet_aware'``

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.

list here and explain in more detail all the new attributes of the pellet_aware time_step_calculator

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

@jcitrin

jcitrin commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

@tamaranorman could you also take a look at reviewing this one?



class PelletAwareTimeStepCalculator(time_step_calculator.TimeStepCalculator):
"""TimeStepCalculator that resolves pellet trigger and ablation windows.

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 something that could be made generic to many other discrete events to allow supporting other things moving forward

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

For this PR I have kept it only for pellets.

do not skip over these events.

Arguments:
base_calculator_type: The type of the base time step calculator to use for

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.

Should this be instead base_calculator to make sure that any parameter are created if a calculator has these - then it can be something other than chi or fixed potentially

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

…culator config object. Remove the pellet_source_name option and read the single 'pellet' source directly.
…exactly one of trigger_times/frequency and detecting the trigger locally ('at_trigger = jnp.any(jnp.abs(t - triggers) <= tol') from the pellet source own trigger_tolerance. Vectorize the trigger scan, and drop the redundant array casts, the preventive early returns (now guaranteed by the ToraxConfig validator), and the duplicate exact_t_final limit already applied by the base next_dt.
Assume a strictly positive frequency (validated by the pellet source config) instead of silently substituting 1.0. Replace the frequency_t_start "after start" gate with a generic injection_enabled flag read from the pellet source (defaulting to on), so the injector can be toggled on and off during the run while frequency_t_start only sets the phase.

Because floating point prevents a step from landing exactly on a boundary, an injection_enabled that switches on exactly at a pellet time can miss it, so it must be turned on a small margin before the intended pellet time (documented in the code). Also drop the redundant + tol phase offset.
…c build/validation, and the ToraxConfig compatibility validator
@MateoSochard
MateoSochard requested a review from jcitrin August 7, 2026 08:44

@jcitrin jcitrin 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.

Thanks for this feature! Any additional minor changes we can take care of internally since I am aware that you are at the end of your project.

@jcitrin jcitrin added the copybara:import-manual Set when ready for copybara manual import label Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

copybara:import-manual Set when ready for copybara manual import

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants