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
Submissioninstance 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:
BaseModelTop-level object representing an on-disk submission project.
A
Submissionmanages a collection ofEventobjects and handles serialization to the project directory. Users typically obtain an instance viaload()and then interact with events and solutions before callingsave()orexport().- 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
Eventinstances.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 eventsave(): Persist all changes to diskrun_validation(): Check submission completeness and consistencyexport(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}")
- 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:
- 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:
- 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:
- 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:
- 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]
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:
BaseModelA collection of solutions for a single microlensing event.
Events act as containers that group one or more
Solutionobjects under a commonevent_id. They are created on demand viaSubmission.get_event()and are written to disk when the parent submission is saved.- Variables:
event_id (str) – Identifier used to reference the event within the project.
solutions (Dict[str, microlens_submit.models.solution.Solution]) – Mapping of solution IDs to
Solutioninstances.submission (Submission | None) – The parent
SubmissionorNoneif detached.
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 solutionget_solution(solution_id): Retrieve a specific solutionget_active_solutions(): Get only active solutionsclear_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.
- 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:
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().
- 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:
- 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:
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:
- 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.
- 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:
BaseModelContainer 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 whenSubmission.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 informationactivate()/deactivate(): Control solution statusget_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
aliasattribute- _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.
- 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.
- 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:
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.
- higher_order_effects: List[Literal['lens-orbital-motion', 'parallax', 'finite-source', 'limb-darkening', 'xallarap', 'stellar-rotation', 'fitted-limb-darkening', 'gaussian-process', 'other']]
- model_config = {}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- 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:
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.
- 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:
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.
- 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_infowith 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
- 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:
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
Always save after changes: Call
submission.save()after modifying dataUse validation: Check
submission.run_validation()before exportingHandle errors gracefully: Wrap operations in try/except blocks
Use relative paths: Keep file paths relative to the project directory
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}")