Skip to content

Review architecture of parameter configuration if extending #37

@edwenger

Description

@edwenger

Architectural Notes: Config Structure and API Design

Context

The v0.1.0 config refactor (PR #38) introduced instance-based MalariaConfig for thread-safe parallel simulations. This issue documents
architectural considerations for future reference.

1. Module Structure and Run_Number Placement

Current State

emodlib/
├── params.py          # Params dict class, deep_update utility
└── malaria/
    ├── __init__.py    # create_config(), update() wrappers
    └── MalariaConfig  # All params including Run_Number/random_seed

Observation

The original project structure anticipated potential additional submodules beyond malaria. However, MalariaConfig now contains Run_Number
(random seed), which is conceptually a general concern not specific to malaria.

If future submodules were added (e.g., emodlib.hiv, emodlib.tb), the current design would require each to independently handle Run_Number and
RNG initialization.

Potential Future Design

A base config at the emodlib level could own shared concerns:

emodlib.BaseConfig              # Run_Number, RNG, SUID generator
  └── emodlib.malaria.MalariaConfig   # Inherits base, adds malaria params
  └── emodlib.other.OtherConfig       # Inherits base, adds other params

Recommendation

No action needed now. Current design is pragmatic for a malaria-only library. Document this consideration if/when additional disease modules are
planned.

2. Configuration API Redundancy

Current API Surface

Method Layer Role
MalariaConfig() C++ Default constructor
MalariaConfig.configure(pset) C++ Configure from ParamSet dict
MalariaConfig.from_params(pset) C++ Static factory: construct + configure
create_config(params) Python User API: merge with defaults, create config
config.update(params) Python User API: incremental merge, reconfigure

Analysis

C++ level redundancy: from_params is a convenience wrapper around constructor + configure:

static std::shared_ptr<MalariaConfig> FromParamSet(const ParamSet& pset) {
    auto config = std::make_shared<MalariaConfig>();
    config->Configure(pset);
    return config;
}

Both from_params and configure are exposed via pybind11, but configure alone would suffice.

Python wrappers add value:

  • create_config(): Merges user params with YAML defaults, stores _params for yaml property
  • config.update(): Incremental merge on existing config (equivalent to old update_params)

These are not redundant with the C++ methods—they provide the user-friendly API with defaults handling.

Potential Simplification

Could reduce C++ public API to just configure(), with Python create_config() handling all construction:

def create_config(params=None):
    merged = deep_update(defaults, params or {})
    cfg = MalariaConfig()  # default constructor
    cfg.configure(merged)  # configure instance
    cfg._params = merged
    return cfg

This would remove from_params from public API without breaking functionality.

Recommendation

Low priority. Current API works correctly. If simplifying, could deprecate from_params in favor of Python-side factory pattern, but the marginal
benefit is small.

Summary

These are documentation notes, not bugs. The current architecture is functional and thread-safe. These considerations are relevant only if:

  1. Additional disease submodules are planned
  2. API surface reduction becomes a priority

Related: config-refactor branch, v0.1.0 release

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions