Skip to content

Release 1.1.1

Choose a tag to compare

@Julian-Patzner Julian-Patzner released this 17 Jun 16:37
· 315 commits to main since this release
23a3af0

This patch improves simulation performance with lock-free parallel staging for intervention triggers and fixes to type instability in present_individuals! and setting activation.


Parallel Trigger Dispatch

Symptom and hospitalization triggers fired during the parallel individual-update loop now use a lock-free staging path rather than acquiring the event-queue lock on every event.

  • Per-thread staging buffers: EventQueue gains a staging field — one Vector{Tuple{Event, Int16}} per thread, pre-allocated to Threads.maxthreadid() entries. Events staged into these buffers during a parallel loop are invisible to the queue until flush_staging! merges them.
  • stage!: Lock-free variant of enqueue!. Appends the event and its target tick to the calling thread's staging buffer in O(1). Safe to call concurrently from Threads.@threads loops without any synchronization.
  • flush_staging!: Merges all staging buffers into the tick buckets in thread-index order and clears them. Must be called single-threaded; takes no lock. Called automatically by step! after the parallel individual-update loop completes.
  • staged keyword on trigger and trigger_strategy: Pass staged = true to route events through stage! instead of enqueue!. Set automatically when triggers are fired from update_individual!, which runs inside Threads.@threads :static.
  • empty! updated: Clears staging buffers in addition to tick buckets, so staged-but-unflushed events are also discarded.

Type-Stable Setting Traversal

  • Type instability in present_individuals!: ContainerSetting structs previously stored their child setting type in a contains_type::DataType field. Lookups via this field returned an abstractly-typed Vector, preventing the compiler from specializing the iteration over contained settings in present_individuals! (and other hierarchy-traversal functions such as get_contained!, individuals!, open!, close!, size, and geolocation). contains_type and contained_type are now type-dispatch functions (contains_type(::Type{SchoolYear}) = SchoolClass, etc.), encoding the parent–child relationship as a compile-time constant. The DataType-typed fields have been removed from all ContainerSetting structs; use contains_type(setting) / contained_type(setting) in their place.

  • Type instability in setting activation: When an individual was infected, their memberships were activated by iterating settings_tuple(i) — a tuple of (DataType, id) pairs — and calling settings(sim, type)[id] with a runtime DataType. This made every activate! call dynamically dispatched. setting_id and setting_id! are now per-type @inline dispatch methods rather than a runtime if-else chain, and a new activate_memberships! helper unrolls the activation loop over membership_setting_types(Individual) at compile time, making every field access and activate! call concretely typed. The same fix is applied in all four start-condition initializers (InfectedFraction, PatientZero, PatientZeros, RegionalSeeds) and in process_infections!.


Other Changes & API Cleanup

  • settings(sim, type::DataType) removed: The runtime-DataType overload of settings has been removed. Use the typed overload settings(sim, Household) for literal types, or get(settingscontainer(sim), type) where a runtime DataType variable is genuinely needed.
  • Expanded test coverage: New tests cover the stage!/flush_staging! staging pipeline (staged events not visible until flushed, double-flush idempotence, empty! clearing unflushed staged events), the setting_id/setting_id! type-dispatch rewrites, and two previously untested SettingsContainer edge cases — unmapped container IDs in new_setting_ids! and out-of-bounds contained IDs in delete_dangling_ids!.
  • _insert! internal helper: The shared insertion logic between enqueue! and flush_staging! has been extracted into a non-exported _insert! function. No behavioral change.

Public API Surface Tightened

The export list has been audited and substantially trimmed. A large number of names that were internal helpers (utilities, post-processing intermediates, state-mutating setters on Individual, and similar implementation details) are no longer exported, and many have been renamed with a leading underscore to mark them as private.

This is a tightening of the public surface, not a removal of functionality. Every affected name remains accessible by qualifying it with the module, e.g. GEMS.functionname. Code that was calling these directly will keep working with that one change; the audit simply makes explicit which names are the supported, stable API and which are internal and may change without a version bump.