DifferentialLab is organized around clear subsystem boundaries:
config: validated environment and constantssolver: numerical engines and expression parsingplotting: matplotlib figure buildersfrontend: Tkinter dialogs and embeddingpipeline: orchestration for the standardSolvepathcomplex_problems: plugin-based specialized workflowstransforms: scalar transform toolsutils: shared infrastructure (logging, export, exceptions, update checks)
src/
main_program.py
pipeline.py
config/
solver/
plotting/
transforms/
complex_problems/
frontend/
utils/
EquationDialog -> ParametersDialog -> run_solver_pipeline
-> validation + parser + solver
-> statistics + export
-> ResultDialog
Key design goal: keep the general solver stack independent from UI-specific behavior.
complex_problems uses a lazy plugin registry.
Core pieces:
base.py:ProblemDescriptorandComplexProblemprotocolproblem_registry.py: lazy registration and dispatchcomplex_problems_dialog.py: selector windowcommon/: shared helpers for plugin modules- background execution (
run_solver_with_loading) - safe expression compilation
- numeric UI parsing helpers
- background execution (
Plugin contract:
problem.pyexposesPROBLEMwith descriptor +open_dialog(parent)ui.pygathers validated inputsolver.pyreturns structured result dataclassresult_dialog.pyrenders module-specific diagnosticsmodel.pyoptional physical/math helpers
coupled_oscillatorsmembrane_2dnonlinear_wavesschrodinger_tdantenna_radiationaerodynamics_2dpipe_flow
main_program.pycallsinitialize_and_validate_config()..envis loaded and validated againstENV_SCHEMA.- Invalid values are corrected to defaults and logged.
- Runtime reads values through
get_env_from_schema(key). - In-app
Configurationrewrites.envand restarts the app.
- Public APIs are re-exported in package
__init__.pyfiles. - Heavy libraries are imported lazily where practical.
- Internal modules import siblings directly to avoid circular re-export issues.
- Expression inputs are AST-validated before evaluation.
- Solver failures propagate as user-facing dialog errors, not hard crashes.
- Plugin solvers run in background threads with loading/progress dialogs.
- Keep numeric kernels separate from UI code.
- Use result dataclasses with explicit fields (
metadata,magnitudes, raw fields). - Add plugin modules without changing generic solver pipeline.
- Register new plugins only in
problem_registry.py.