Python API Reference

For advanced users who prefer integrating microlens-submit directly into Python scripts, this section documents the public API. The API provides programmatic access to all functionality available through the command-line interface.

Core Classes

The API is built around three main classes that represent the hierarchical structure of microlensing submissions:

  • Submission: Top-level container for all events and team information

  • Event: Container for multiple solutions for a single microlensing event

  • Solution: Individual microlensing model fit with parameters and metadata

Loading and Saving

microlens_submit.load(project_path: str) Submission[source]

Load or create a submission project from a directory.

This is the main entry point for working with submission projects. If the directory doesn’t exist, it will be created with a basic project structure. If it exists, the submission data will be loaded from disk.

Parameters:

project_path – Path to the project directory.

Returns:

A Submission instance representing the project.

Example

>>> from microlens_submit import load
>>>
>>> # Load or create a submission project
>>> submission = load("./my_project")
>>>
>>> # Set submission metadata
>>> submission.team_name = "Team Alpha"
>>> submission.tier = "experienced"
>>> submission.repo_url = "https://github.com/team/repo"
>>>
>>> # Add an event and solution
>>> event = submission.get_event("EVENT001")
>>> params = {"t0": 2459123.5, "u0": 0.1, "tE": 20.0}
>>> solution = event.add_solution("1S1L", params)
>>> solution.log_likelihood = -1234.56
>>> solution.set_compute_info(cpu_hours=2.5, wall_time_hours=0.5)
>>>
>>> # Save the submission
>>> submission.save()
>>>
>>> # Export for submission
>>> submission.export("submission.zip")

Note

The project directory structure is automatically created when you first call load() with a new directory. All data is stored in JSON format with a clear directory structure for events and solutions.

Submission Class

The main container class that represents an entire microlensing challenge submission.

class microlens_submit.Submission(*, project_path: str = '', team_name: str = '', tier: str = '', hardware_info: dict | None = None, events: Dict[str, ~microlens_submit.models.event.Event]=<factory>, repo_url: str | None = None, git_dir: str | None = None)[source]

Bases: BaseModel

Top-level object representing an on-disk submission project.

A Submission manages a collection of Event objects and handles serialization to the project directory. Users typically obtain an instance via load() and then interact with events and solutions before calling save() or export().

Variables:
  • project_path (str) – Root directory where submission files are stored.

  • team_name (str) – Name of the participating team (required for validation).

  • tier (str) – Challenge tier for the submission (e.g., “beginner”, “experienced”) (required for validation).

  • hardware_info (dict | None) – Dictionary describing the compute platform (required for validation).

  • events (Dict[str, microlens_submit.models.event.Event]) – Mapping of event IDs to Event instances.

  • repo_url (str | None) – GitHub repository URL for the team codebase (required for validation).

  • git_dir (str | None) – Optional path to the codebase (used for git metadata capture).

Example

>>> from microlens_submit import load
>>>
>>> # Load or create a submission project
>>> submission = load("./my_project")
>>>
>>> # Set submission metadata
>>> submission.team_name = "Team Alpha"
>>> submission.tier = "experienced"
>>> submission.repo_url = "https://github.com/team/microlens-submit"
>>>
>>> # Add events and solutions
>>> event1 = submission.get_event("EVENT001")
>>> solution1 = event1.add_solution("1S1L", {"t0": 2459123.5, "u0": 0.1, "tE": 20.0})
>>>
>>> event2 = submission.get_event("EVENT002")
>>> params2 = {"t0": 2459156.2, "u0": 0.08, "tE": 35.7, "s": 0.95, "q": 0.0005, "alpha": 78.3}
>>> solution2 = event2.add_solution("1S2L", params2)
>>>
>>> # Validate the submission
>>> warnings = submission.run_validation()
>>> if warnings:
...     print("Validation warnings:")
...     for warning in warnings:
...         print(f"  - {warning}")
... else:
...     print("✅ Submission is valid!")
>>>
>>> # Save the submission
>>> submission.save()
>>>
>>> # Export for submission
>>> submission.export("submission.zip")

Note

The submission project structure is automatically created when you first call load() with a new directory. All data is stored in JSON format with a clear directory structure for events and solutions.

Key Methods:

  • get_event(event_id): Retrieve or create an event

  • save(): Persist all changes to disk

  • run_validation(): Check submission completeness and consistency

  • export(path): Create submission package

Example:

>>> from microlens_submit import load
>>>
>>> # Load existing submission
>>> submission = load("./my_project")
>>>
>>> # Set team information
>>> submission.team_name = "Team Alpha"
>>> submission.tier = "experienced"
>>> submission.repo_url = "https://github.com/team-alpha/microlens-analysis"
>>> submission.git_dir = "/path/to/microlens-analysis"
>>> # GPU info is optional; omit it for CPU-only environments.
>>> submission.hardware_info = {
...     "cpu": "Intel i9",
...     "ram_gb": 64,
...     "gpu": {"model": "NVIDIA A100", "count": 1, "memory_gb": 40},
... }
>>>
>>> # Save changes
>>> submission.save()
>>>
>>> # Validate submission
>>> warnings = submission.run_validation()
>>> for warning in warnings:
...     print(f"Warning: {warning}")
autofill_nexus_info() None[source]
events: Dict[str, Event]
export(output_path: str) None[source]
get_event(event_id: str) Event[source]
get_solution_by_alias(event_id: str, alias: str) Solution | None[source]
get_solution_status() dict[source]
git_dir: str | None
hardware_info: dict | None
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

notebook_display_dashboard(output_dir: str | None = None) str[source]

Return dashboard HTML with local assets inlined for Jupyter display.

This is a convenience helper for JupyterLab/JupyterHub, where relative file URLs are often blocked. It reads the generated dossier HTML and replaces local asset image paths with base64 data URIs.

Parameters:

output_dir – Optional dossier output directory. Defaults to <project_path>/dossier.

Returns:

HTML content suitable for display with IPython.display.HTML.

Return type:

str

notebook_display_event(event_id: str, output_dir: str | None = None) str[source]

Return event dossier HTML with local assets inlined for Jupyter display.

Parameters:
  • event_id – Event identifier to render.

  • output_dir – Optional dossier output directory. Defaults to <project_path>/dossier.

Returns:

HTML content suitable for display with IPython.display.HTML.

Return type:

str

notebook_display_full_dossier(output_dir: str | None = None) str[source]

Return full dossier HTML with local assets inlined for Jupyter display.

Parameters:

output_dir – Optional dossier output directory. Defaults to <project_path>/dossier.

Returns:

HTML content suitable for display with IPython.display.HTML.

Return type:

str

notebook_display_solution(solution_id: str, output_dir: str | None = None, regenerate: bool = False) str[source]

Return solution dossier HTML with local assets inlined for Jupyter display.

Parameters:
  • solution_id – Solution identifier to render.

  • output_dir – Optional dossier output directory. Defaults to <project_path>/dossier.

  • regenerate – If True, regenerate the solution HTML even if it exists.

Returns:

HTML content suitable for display with IPython.display.HTML.

Return type:

str

print_solution_status() None[source]
project_path: str
remove_event(event_id: str, force: bool = False) bool[source]
repo_url: str | None
run_validation() List[str][source]

Validate the entire submission for missing or incomplete information.

This method performs comprehensive validation of the submission structure, including metadata completeness, event configuration, and solution validation. It returns a list of human-readable validation messages.

Returns:

Human-readable validation messages. Empty list indicates

all validations passed.

Return type:

List[str]

Example

>>> submission = load("./my_project")
>>> messages = submission.run_validation()
>>> if messages:
...     print("Validation issues found:")
...     for msg in messages:
...         print(f"  - {msg}")
... else:
...     print("Submission is valid!")

Note

This method calls run_validation() on all events and solutions, providing a comprehensive validation report for the entire submission.

run_validation_warnings() List[str][source]

Validate the submission and return warnings only (non-blocking issues).

This method performs validation but only returns warnings for missing optional fields. It does not fail for missing required fields like repo_url or hardware_info.

Returns:

Human-readable warning messages. Empty list indicates

no warnings.

Return type:

List[str]

save(force: bool = False) None[source]
team_name: str
tier: str

Event Class

Represents a microlensing event that can contain multiple solutions.

class microlens_submit.Event(*, event_id: str, solutions: Dict[str, ~microlens_submit.models.solution.Solution]=<factory>, submission: Submission | None = None)[source]

Bases: BaseModel

A collection of solutions for a single microlensing event.

Events act as containers that group one or more Solution objects under a common event_id. They are created on demand via Submission.get_event() and are written to disk when the parent submission is saved.

Variables:

Example

>>> from microlens_submit import load
>>>
>>> # Load a submission and get/create an event
>>> submission = load("./my_project")
>>> event = submission.get_event("EVENT001")
>>>
>>> # Add multiple solutions to the event
>>> solution1 = event.add_solution("1S1L", {
...     "t0": 2459123.5, "u0": 0.1, "tE": 20.0
... })
>>> solution2 = event.add_solution("1S2L", {
...     "t0": 2459123.5, "u0": 0.1, "tE": 20.0,
...     "s": 1.2, "q": 0.5, "alpha": 45.0
... })
>>>
>>> # Get active solutions
>>> active_solutions = event.get_active_solutions()
>>> print(f"Event {event.event_id} has {len(active_solutions)} active solutions")
>>>
>>> # Deactivate a solution
>>> solution1.deactivate()
>>>
>>> # Save the submission (includes all events and solutions)
>>> submission.save()

Note

Events are automatically created when you call submission.get_event() with a new event_id. All solutions for an event are stored together in the project directory structure.

Key Methods:

  • add_solution(model_type, parameters): Create and add a new solution

  • get_solution(solution_id): Retrieve a specific solution

  • get_active_solutions(): Get only active solutions

  • clear_solutions(): Deactivate all solutions

Example:

>>> # Get or create an event
>>> event = submission.get_event("EVENT123")
>>>
>>> # Add a solution
>>> solution = event.add_solution(
...     model_type="1S1L",
...     parameters={"t0": 2459123.5, "u0": 0.15, "tE": 20.5}
... )
>>>
>>> # Set solution metadata
>>> solution.log_likelihood = -1234.56
>>> solution.n_data_points = 1250
>>> solution.relative_probability = 1.0
>>>
>>> # Save the submission
>>> submission.save()
classmethod _from_dir(event_dir: Path, submission: Submission) Event[source]

Load an event from disk.

_save() None[source]

Write this event and its solutions to disk.

add_solution(model_type: str, parameters: dict, alias: str | None = None, bands: List[str] | None = None) Solution[source]

Create and attach a new solution to this event.

Parameters are stored as provided and the new solution is returned for further modification. A unique solution_id is automatically generated.

Parameters:
  • model_type – Short label describing the model type (e.g., “1S1L”, “1S2L”).

  • parameters – Dictionary of model parameters for the fit.

  • alias – Optional human-readable alias for the solution (e.g., “best_fit”, “parallax_model”). When provided, this alias is used as the primary identifier in dossier displays, with the UUID shown as a secondary identifier. The combination of event_id and alias must be unique within the project.

  • bands – Optional list of photometric bands used in the fit (e.g., [“0”, “1”]).

Returns:

The newly created solution instance.

Return type:

Solution

Example

>>> event = submission.get_event("EVENT001")
>>>
>>> # Create a simple point lens solution
>>> solution = event.add_solution("1S1L", {
...     "t0": 2459123.5,  # Time of closest approach
...     "u0": 0.1,       # Impact parameter
...     "tE": 20.0       # Einstein crossing time
... })
>>>
>>> # Create a solution with an alias
>>> solution_with_alias = event.add_solution("1S2L", {
...     "t0": 2459123.5, "u0": 0.1, "tE": 20.0,
...     "s": 1.2, "q": 0.5, "alpha": 45.0
... }, alias="best_binary_fit")
>>>
>>> # The solution is automatically added to the event
>>> print(f"Event now has {len(event.solutions)} solutions")
>>> print(f"Solution ID: {solution.solution_id}")

Note

The solution is automatically marked as active and assigned a unique UUID. You can modify the solution attributes after creation and then save the submission to persist changes. If an alias is provided, it will be validated for uniqueness when the submission is saved. Remember to call submission.save() to persist the solution to disk.

clear_solutions() None[source]

Deactivate every solution associated with this event.

This method marks all solutions in the event as inactive, effectively removing them from submission exports and dossier generation.

Example

>>> event = submission.get_event("EVENT001")
>>>
>>> # Deactivate all solutions in this event
>>> event.clear_solutions()
>>>
>>> # Now no solutions are active
>>> active_solutions = event.get_active_solutions()
>>> print(f"Active solutions: {len(active_solutions)}")  # 0

Note

This only deactivates solutions; they are not deleted. You can reactivate individual solutions using solution.activate().

event_id: str
get_active_solutions() List[Solution][source]

Return a list of active solutions for this event.

Returns:

List of solutions where is_active is True.

Return type:

List[Solution]

Example

>>> event = submission.get_event("EVENT001")
>>> active_solutions = event.get_active_solutions()
>>> print(f"Found {len(active_solutions)} active solutions")
get_solution(solution_id: str) Solution[source]

Return a previously added solution.

Parameters:

solution_id – Identifier of the solution to retrieve.

Returns:

The corresponding solution.

Return type:

Solution

Raises:

KeyError – If the solution_id is not found in this event.

Example

>>> event = submission.get_event("EVENT001")
>>>
>>> # Get a specific solution
>>> solution = event.get_solution("solution_uuid_here")
>>> print(f"Model type: {solution.model_type}")
>>> print(f"Parameters: {solution.parameters}")

Note

Use this method to retrieve existing solutions. If you need to create a new solution, use add_solution() instead.

model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

remove_all_solutions(force: bool = False) int[source]

Remove all solutions from this event.

⚠️ WARNING: This permanently removes ALL solutions from this event. This action cannot be undone. Use clear_solutions() instead if you want to keep the solutions but exclude them from exports.

Parameters:

force – If True, skip confirmation prompts and remove immediately. If False, will warn about data loss.

Returns:

Number of solutions removed.

Return type:

int

Example

>>> event = submission.get_event("EVENT001")
>>>
>>> # Remove all solutions (use with caution!)
>>> removed_count = event.remove_all_solutions(force=True)
>>> print(f"Removed {removed_count} solutions from event {event.event_id}")

Note

This is equivalent to calling remove_solution() for each solution in the event. Use clear_solutions() if you want to keep the data.

remove_solution(solution_id: str, force: bool = False) bool[source]

Completely remove a solution from this event.

⚠️ WARNING: This permanently removes the solution from memory and any associated files. This action cannot be undone. Use deactivate() instead if you want to keep the solution but exclude it from exports.

Parameters:
  • solution_id – Identifier of the solution to remove.

  • force – If True, skip confirmation prompts and remove immediately. If False, will warn about data loss.

Returns:

True if solution was removed, False if not found or cancelled.

Return type:

bool

Raises:

ValueError – If solution is saved and force=False (to prevent accidental removal of persisted data).

Example

>>> event = submission.get_event("EVENT001")
>>>
>>> # Remove an unsaved solution (safe)
>>> solution = event.add_solution("1S1L", {"t0": 2459123.5, "u0": 0.1})
>>> removed = event.remove_solution(solution.solution_id)
>>> print(f"Removed: {removed}")
>>>
>>> # Remove a saved solution (requires force=True)
>>> saved_solution = event.get_solution("existing_uuid")
...     removed = event.remove_solution(saved_solution.solution_id, force=True)
...     print(f"Force removed saved solution: {removed}")

Note

This method: 1. Removes the solution from the event’s solutions dict 2. Cleans up any temporary notes files in tmp/ 3. For saved solutions, requires force=True to prevent accidents 4. Cannot be undone - use deactivate() if you want to keep the data

run_validation(tier: str | None = None) List[str][source]

Validate all active solutions in this event.

This method performs validation on all active solutions in the event, including parameter validation, physical consistency checks, and event-specific validation like relative probability sums.

Parameters:

tier – Optional challenge tier to use for model type validation. If provided, model types will be validated against tier-specific allowed types. If None, no tier-specific validation is performed.

Returns:

Human-readable validation messages. Empty list indicates

all validations passed. Messages may include warnings (non-critical) and errors (critical issues).

Return type:

List[str]

Example

>>> event = submission.get_event("EVENT001")
>>>
>>> # Validate the event
>>> warnings = event.run_validation()
>>> if warnings:
...     print("Event validation issues:")
...     for msg in warnings:
...         print(f"  - {msg}")
... else:
...     print("✅ Event is valid!")

Note

This method validates all active solutions regardless of whether they have been saved to disk. It does not check alias uniqueness across the entire submission (use submission.run_validation() for that). Always validate before saving or exporting.

solutions: Dict[str, Solution]
submission: Submission | None

Solution Class

Represents an individual microlensing model fit with all associated metadata.

class microlens_submit.Solution(*, solution_id: str, model_type: ~typing.Literal['1S1L', '1S2L', '2S1L', '2S2L', '1S3L', '2S3L', '1S4L', '2S4L', 'other'], bands: ~typing.List[str] = <factory>, higher_order_effects: ~typing.List[~typing.Literal['lens-orbital-motion', 'parallax', 'finite-source', 'limb-darkening', 'xallarap', 'stellar-rotation', 'fitted-limb-darkening', 'gaussian-process', 'other']] = <factory>, t_ref: float | None = None, parameters: dict, is_active: bool = True, alias: str | None = None, compute_info: dict = <factory>, hardware_info: dict | None = None, posterior_path: str | None = None, lightcurve_plot_path: str | None = None, lens_plane_plot_path: str | None = None, notes_path: str | None = None, used_astrometry: bool = False, used_postage_stamps: bool = False, limb_darkening_model: str | None = None, limb_darkening_coeffs: dict | None = None, parameter_uncertainties: dict | None = None, uncertainty_method: str | None = None, confidence_level: float | None = 0.68, physical_parameters: dict | None = None, physical_parameter_uncertainties: dict | None = None, log_likelihood: float | None = None, relative_probability: float | None = None, n_data_points: int | None = None, creation_timestamp: str = <factory>, saved: bool = False)[source]

Bases: BaseModel

Container for an individual microlensing model fit.

This data model stores everything required to describe a single microlensing solution, including the numeric parameters of the fit and metadata about how it was produced. Instances are normally created via Event.add_solution() and persisted to disk when Submission.save() is called.

Variables:
  • solution_id (str) – Unique identifier for the solution (auto-generated UUID).

  • model_type (Literal['1S1L', '1S2L', '2S1L', '2S2L', '1S3L', '2S3L', '1S4L', '2S4L', 'other']) – Specific lens/source configuration such as “1S1L” or “1S2L”.

  • bands (List[str]) – List of photometric bands used in the fit (e.g., [“0”, “1”, “2”]).

  • higher_order_effects (List[Literal['lens-orbital-motion', 'parallax', 'finite-source', 'limb-darkening', 'xallarap', 'stellar-rotation', 'fitted-limb-darkening', 'gaussian-process', 'other']]) – List of physical effects modeled (e.g., [“parallax”]).

  • t_ref (float | None) – Reference time for time-dependent effects (Julian Date).

  • parameters (dict) – Dictionary of model parameters used for the fit.

  • is_active (bool) – Flag indicating whether the solution should be included in the final submission export.

  • alias (str | None) – Optional human-readable alias for the solution (e.g., “best_fit”, “parallax_model”). When provided, this alias is used as the primary identifier in dossier displays, with the UUID shown as a secondary identifier. The combination of event_id and alias must be unique within the project. If not unique, an error will be raised during validation or save operations.

  • compute_info (dict) – Metadata about the computing environment, populated by set_compute_info().

  • hardware_info (dict | None) – Optional solution-specific hardware metadata override.

  • posterior_path (str | None) – Optional path to a file containing posterior samples.

  • lightcurve_plot_path (str | None) – Optional path to the lightcurve plot file.

  • lens_plane_plot_path (str | None) – Optional path to the lens plane plot file.

  • notes_path (str | None) – Path to the markdown notes file for this solution.

  • used_astrometry (bool) – Whether astrometric information was used when fitting.

  • used_postage_stamps (bool) – Whether postage stamp data was used.

  • limb_darkening_model (str | None) – Name of the limb darkening model employed.

  • limb_darkening_coeffs (dict | None) – Mapping of limb darkening coefficients.

  • parameter_uncertainties (dict | None) – Uncertainties for parameters in parameters.

  • physical_parameters (dict | None) – Physical parameters derived from the model.

  • log_likelihood (float | None) – Log-likelihood value of the fit.

  • relative_probability (float | None) – Optional probability of this solution being the best model.

  • n_data_points (int | None) – Number of data points used in the fit.

  • creation_timestamp (str) – UTC timestamp when the solution was created.

  • saved (bool) – Flag indicating whether the solution has been persisted to disk.

Example

>>> from microlens_submit import load
>>>
>>> # Load a submission and get an event
>>> submission = load("./my_project")
>>> event = submission.get_event("EVENT001")
>>>
>>> # Create a simple 1S1L solution
>>> solution = event.add_solution("1S1L", {
...     "t0": 2459123.5,  # Time of closest approach
...     "u0": 0.1,       # Impact parameter
...     "tE": 20.0       # Einstein crossing time
... })
>>>
>>> # Add metadata
>>> solution.log_likelihood = -1234.56
>>> solution.n_data_points = 1250
>>> solution.relative_probability = 0.8
>>> solution.higher_order_effects = ["parallax"]
>>> solution.t_ref = 2459123.0
>>> solution.alias = "best_parallax_fit"  # Set a human-readable alias
>>>
>>> # Record compute information
>>> solution.set_compute_info(cpu_hours=2.5, wall_time_hours=0.5)
>>>
>>> # Add notes
>>> solution.set_notes('''
...     # My Solution Notes
...
...     This is a simple point lens fit.
... ''')
>>>
>>> # Validate the solution
>>> messages = solution.run_validation()
>>> if messages:
...     print("Validation issues:", messages)

Note

The notes_path field supports Markdown formatting, allowing you to create rich, structured documentation with headers, lists, code blocks, tables, and links. This is particularly useful for creating detailed submission dossiers for evaluators.

The run_validation() method performs comprehensive validation of parameters, higher-order effects, and physical consistency. Always validate solutions before submission.

Key Methods:

  • set_compute_info(): Automatically capture compute information

  • activate() / deactivate(): Control solution status

  • get_notes(): Retrieve solution notes as markdown

Example:

>>> # Create a solution with higher-order effects
>>> solution = event.add_solution(
...     model_type="1S2L",
...     parameters={
...         "t0": 2459123.5,
...         "u0": 0.12,
...         "tE": 22.1,
...         "q": 0.001,
...         "s": 1.15,
...         "alpha": 45.2
...     }
... )
>>>
>>> # Add higher-order effects
>>> solution.higher_order_effects = ["parallax", "finite-source"]
>>> solution.t_ref = 2459123.0
>>>
>>> # Set uncertainties
>>> solution.parameter_uncertainties = {
...     "t0": [0.1, 0.1],
...     "u0": 0.02,
...     "tE": [0.3, 0.4]
... }
>>>
>>> # Set compute information
>>> solution.set_compute_info(cpu_hours=15.2, wall_time_hours=3.8)
>>>
>>> # Add notes
>>> solution.notes = "# Binary Lens Solution\n\nThis solution includes parallax and finite source effects."
>>>
>>> # Set file paths
>>> solution.posterior_path = "posteriors/chain.h5"
>>> solution.lightcurve_plot_path = "plots/event123_lc.png"
>>> solution.lens_plane_plot_path = "plots/event123_lens.png"

Solution Aliases:

You can assign human-readable aliases to solutions for easier identification:

>>> # Create a solution with an alias
>>> solution = event.add_solution(
...     model_type="1S1L",
...     parameters={"t0": 2459123.5, "u0": 0.15, "tE": 20.5}
... )
>>>
>>> # Set an alias for the solution
>>> solution.alias = "best_fit"
>>>
>>> # Aliases must be unique within each event
>>> # This would raise an error if another solution in EVENT123 has alias "best_fit"
>>> submission.save()

Alias Features: - Aliases are displayed as primary identifiers in dossier generation - In the full dossier report, solutions are titled as “Solution: <event_id> <alias>” - The UUID is shown as a subtitle for technical reference - Solutions without aliases fall back to UUID-based identification - Aliases can be edited later by setting the alias attribute

_save(event_path: Path) None[source]

Write this solution to disk.

Parameters:

event_path – Directory of the parent event within the project.

Example

>>> # This is called automatically by Event._save()
>>> event._save()  # This calls solution._save() for each solution

Note

This is an internal method. Solutions are automatically saved when the parent event is saved via submission.save().

activate() None[source]

Mark this solution as active.

Active solutions are included in submission exports and dossier generation. This is the default state for new solutions.

Example

>>> solution = event.get_solution("solution_uuid")
>>> solution.activate()
>>>
>>> # The solution is now active and will be included in exports
>>> submission.save()  # Persist the change

Note

This method only changes the is_active flag. The solution data remains intact.

alias: str | None
autofill_hardware_info() None[source]

Populate solution-level hardware metadata from the current environment.

This mirrors Submission.autofill_nexus_info() but stores the metadata on the solution itself. It is optional and can be used when solutions are produced on different servers or environments.

autofill_nexus_info() None[source]

Alias for autofill_hardware_info() for Nexus users.

bands: List[str]
compute_info: dict
confidence_level: float | None
creation_timestamp: str
deactivate() None[source]

Mark this solution as inactive.

Inactive solutions are excluded from submission exports and dossier generation. This is useful for keeping alternative fits without including them in the final submission.

Example

>>> solution = event.get_solution("solution_uuid")
>>> solution.deactivate()
>>>
>>> # The solution is now inactive and won't be included in exports
>>> submission.save()  # Persist the change

Note

This method only changes the is_active flag. The solution data remains intact and can be reactivated later using activate().

get_notes(project_root: Path | None = None) str[source]

Read notes from the notes file, if present.

Parameters:

project_root – Optional project root path for resolving relative notes_path. If None, uses the current working directory.

Returns:

The contents of the notes file as a string, or empty string

if no notes file exists or notes_path is not set.

Return type:

str

Example

>>> solution = event.get_solution("solution_uuid")
>>> notes = solution.get_notes(project_root=Path("./my_project"))
>>> print(notes)
# My Solution Notes

This is a detailed description of my fit…

Note

This method handles both absolute and relative notes_path values. If notes_path is relative, it’s resolved against project_root.

hardware_info: dict | None
higher_order_effects: List[Literal['lens-orbital-motion', 'parallax', 'finite-source', 'limb-darkening', 'xallarap', 'stellar-rotation', 'fitted-limb-darkening', 'gaussian-process', 'other']]
is_active: bool
lens_plane_plot_path: str | None
lightcurve_plot_path: str | None
limb_darkening_coeffs: dict | None
limb_darkening_model: str | None
log_likelihood: float | None
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_type: Literal['1S1L', '1S2L', '2S1L', '2S2L', '1S3L', '2S3L', '1S4L', '2S4L', 'other']
n_data_points: int | None
property notes: str

Return the Markdown notes string from the notes file (read-only).

Returns:

The contents of the notes file as a string, or empty string

if no notes file exists.

Return type:

str

Example

>>> solution = event.get_solution("solution_uuid")
>>> print(solution.notes)
# My Solution Notes

This is a detailed description of my fit…

Note

This is a read-only property. Use set_notes() to modify the notes. The property uses the current working directory to resolve relative notes_path. For more control, use get_notes() with project_root.

notes_path: str | None
parameter_uncertainties: dict | None
parameters: dict
physical_parameter_uncertainties: dict | None
physical_parameters: dict | None
posterior_path: str | None
relative_probability: float | None
run_validation() List[str][source]

Validate this solution’s parameters and configuration.

This method performs comprehensive validation using centralized validation logic to ensure the solution is complete, consistent, and ready for submission.

The validation includes:

  • Parameter completeness for the given model type

  • Higher-order effect requirements (e.g., parallax needs piEN, piEE)

  • Band-specific flux parameters when bands are specified

  • Reference time requirements for time-dependent effects

  • Parameter data types and physically meaningful ranges

  • Physical consistency checks

  • Model-specific parameter requirements

Parameters:

None

Returns:

Human-readable validation messages. Empty list indicates all

validations passed. Messages may include warnings (non-critical) and errors (critical issues that should be addressed).

Return type:

list[str]

Example

>>> solution = event.add_solution("1S2L", {"t0": 2459123.5, "u0": 0.1})
>>> messages = solution.run_validation()
>>> if messages:
...     print("Validation issues found:")
...     for msg in messages:
...         print(f"  - {msg}")
... else:
...     print("Solution is valid!")

Note

Always validate solutions before submission. The validation logic is centralized and covers all model types and higher-order effects. Some warnings may be non-critical but should be reviewed.

saved: bool
set_compute_info(cpu_hours: float | None = None, wall_time_hours: float | None = None, git_dir: str | None = None) None[source]

Record compute metadata and capture environment details.

When called, this method populates compute_info with timing information as well as a list of installed Python packages and the current Git state. It is safe to call multiple times—previous values will be overwritten.

Parameters:
  • cpu_hours – Total CPU time consumed by the model fit in hours.

  • wall_time_hours – Real-world time consumed by the fit in hours.

  • git_dir – Optional path to the code repository for git metadata capture.

Example

>>> solution = event.add_solution("1S1L", {"t0": 2459123.5, "u0": 0.1})
>>>
>>> # Record compute information
>>> solution.set_compute_info(cpu_hours=2.5, wall_time_hours=0.5)
>>>
>>> # The compute_info now contains:
>>> # - cpu_hours: 2.5
>>> # - wall_time_hours: 0.5
>>> # - dependencies: [list of installed packages]
>>> # - git_info: {commit, branch, is_dirty}

Note

This method automatically captures the current Python environment (via pip freeze) and Git state (commit, branch, dirty status). If Git is not available or not a repository, git_info will be None. If pip is not available, dependencies will be an empty list.

set_notes(content: str, project_root: Path | None = None, convert_escapes: bool = False) None[source]

Write notes to the notes file, creating it if needed.

If notes_path is not set, creates a temporary file in tmp/<solution_id>.md and sets notes_path. On Submission.save(), temporary notes files are moved to the canonical location.

⚠️ WARNING: This method writes files immediately. If you’re testing and don’t want to create files, consider using a temporary project directory or checking the content before calling this method.

Parameters:
  • content – The markdown content to write to the notes file.

  • project_root – Optional project root path for resolving relative notes_path. If None, uses the current working directory.

  • convert_escapes – If True, convert literal n and r to actual newlines and carriage returns. Useful for CSV import where notes contain literal escape sequences. Defaults to False for backward compatibility.

Example

>>> solution = event.get_solution("solution_uuid")
>>>
>>> # Set notes with markdown content
>>> solution.set_notes('''
... # My Solution Notes
...
... This is a detailed description of my microlensing fit.
...
... ## Parameters
... - t0: Time of closest approach
... - u0: Impact parameter
... - tE: Einstein crossing time
...
... ## Notes
... The fit shows clear evidence of a binary lens...
... ''', project_root=Path("./my_project"))
>>>
>>> # The notes are now saved and can be read back
>>> notes = solution.get_notes(project_root=Path("./my_project"))

Note

This method supports markdown formatting. The notes will be rendered as HTML in the dossier with syntax highlighting for code blocks.

For testing purposes, you can: 1. Use a temporary project directory: load(“./tmp_test_project”) 2. Check the content before calling: print(“Notes content:”, content) 3. Use a dry-run approach by setting notes_path manually

solution_id: str
t_ref: float | None
uncertainty_method: str | None
used_astrometry: bool
used_postage_stamps: bool
classmethod validate_solution_at_creation(values)[source]

Perform only basic type/structure checks at creation. Warn if issues, but allow creation.

view_notes(render_html: bool = True, project_root: Path | None = None) str[source]

Return the notes as Markdown or rendered HTML.

Parameters:
  • render_html – If True, return HTML using markdown.markdown with extensions for tables and fenced code blocks. If False, return the raw Markdown string.

  • project_root – Optionally specify the project root for relative notes_path resolution.

Returns:

Markdown or HTML string depending on render_html parameter.

Return type:

str

Example

>>> solution = event.get_solution("solution_uuid")
>>>
>>> # Get raw markdown
>>> md = solution.view_notes(render_html=False)
>>> print(md)
# My Solution Notes
>>> # Get rendered HTML (useful for Jupyter/IPython)
>>> html = solution.view_notes(render_html=True)
>>> print(html)
<h1>My Solution Notes</h1>
<p>...</p>

Note

When render_html=True, the markdown is rendered with extensions for tables, fenced code blocks, and other advanced features. This is particularly useful for displaying notes in Jupyter notebooks or other HTML contexts.

Advanced Usage

Working with Multiple Solutions:

>>> # Compare solutions using BIC
>>> from microlens_submit.cli import compare_solutions
>>>
>>> # Get all solutions for an event
>>> event = submission.get_event("EVENT123")
>>> solutions = list(event.solutions.values())
>>>
>>> # Calculate relative probabilities
>>> for solution in solutions:
...     if solution.log_likelihood and solution.n_data_points:
...         # BIC calculation would go here
...         pass

Validation and Error Handling:

>>> # Comprehensive validation
>>> warnings = submission.run_validation()
>>>
>>> if warnings:
...     print("Validation warnings:")
...     for warning in warnings:
...         print(f"  - {warning}")
... else:
...     print("Submission is valid!")

Export and Dossier Generation:

>>> # Export submission package
>>> submission.export("final_submission.zip")
>>>
>>> # Generate dossier
>>> from microlens_submit.dossier import generate_dashboard_html
>>> from pathlib import Path
>>>
>>> generate_dashboard_html(submission, Path("./dossier_output"))

Best Practices

  1. Always save after changes: Call submission.save() after modifying data

  2. Use validation: Check submission.run_validation() before exporting

  3. Handle errors gracefully: Wrap operations in try/except blocks

  4. Use relative paths: Keep file paths relative to the project directory

  5. Document solutions: Add detailed notes to explain your analysis

Error Handling

The API raises various exceptions that should be handled appropriately:

>>> try:
...     submission = load("./my_project")
... except FileNotFoundError:
...     print("Project directory not found")
... except ValidationError as e:
...     print(f"Validation error: {e}")
... except Exception as e:
...     print(f"Unexpected error: {e}")