Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
07ed880
added baseline optimization models
Sep 23, 2025
37d84f8
Merge branch 'fdannert:fov_taper' into fov_taper
JonahHansen Sep 23, 2025
9798caa
Update core.py
JonahHansen Sep 23, 2025
761af59
Merge branch 'fdannert:fov_taper' into fov_taper
JonahHansen Sep 23, 2025
7d5e308
Merge branch 'fdannert:fov_taper' into fov_taper
JonahHansen Oct 2, 2025
f021ab1
changed reference to Dannert+22
fdannert Nov 3, 2025
56d2841
first running implementation of multiprocessing, validated against si…
fdannert Nov 4, 2025
d9b5222
added science yield wrapper
fdannert Nov 4, 2025
82db3aa
load catalog from ppop
fdannert Nov 4, 2025
a35c73e
changed pandas assignment in instrument.py
fdannert Nov 5, 2025
7040fff
added cnvergence test to yield wrapper
fdannert Nov 5, 2025
aa0830d
suppress unneeded verbosity of LIFEsim in yield wrapper
fdannert Nov 5, 2025
18c6049
fixed bug where diameter was not taken into account
fdannert Nov 5, 2025
51644c7
current state of my fork
Nov 27, 2025
b1d04e4
Merge branch 'multiprocessing_v2' into fov_taper
JonahHansen Nov 27, 2025
7ec06af
documentation for yield wrapper
fdannert Dec 4, 2025
9163788
significant changes to the yield_wrapper and the characterization tim…
fdannert Dec 10, 2025
4459e9a
included failsafe if there are insufficient targets to observe
fdannert Dec 12, 2025
b3d0829
changed logic for completion fraction in optimization runs.
fdannert Dec 12, 2025
655c916
small fix to make sure that planets occurring in multiple experiments…
fdannert Dec 12, 2025
ead1a81
number of detections were computed wrong
fdannert Dec 12, 2025
e3899a1
minor bugfix; sum missing
fdannert Dec 15, 2025
a6d0510
added processing code for the diameter sweeps
fdannert Dec 16, 2025
1daaeb3
fixed a bug that put too much focus on the charaterization time (by f…
fdannert Jan 6, 2026
1ccb3aa
adapted convergence test data handling to yield wrapper
fdannert Jan 7, 2026
ddeb088
tiny bug in data handling of convergence test
fdannert Jan 7, 2026
12792ea
added function to merge HDF catalogs from CSV files with ID remapping…
fdannert Jan 9, 2026
b5f9635
refactor merge function to handle directory-based HDF catalog merging
fdannert Jan 9, 2026
1b1ad63
added the optimization manager that can run multiple optimizations pe…
fdannert Jan 9, 2026
4edcec4
tiny bugfix: drop only existing columns
fdannert Jan 9, 2026
951647c
added numpy varibale handling to yaml writer, some bugfixes in yield_…
fdannert Jan 13, 2026
70d7453
Merge branch 'fov_taper' into multiprocessing_v2
JonahHansen Jan 29, 2026
7044cd6
Merge pull request #1 from JonahHansen/multiprocessing_v2
JonahHansen Jan 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lifesim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@
from lifesim.optimize.optimizer import Optimizer
from lifesim.optimize.ahgs import AhgsModule

from lifesim.gui.spectrum_gui import Gui
from lifesim.gui.spectrum_gui import Gui

from lifesim.analysis.yield_wrapper import ScienceYield
1,206 changes: 1,206 additions & 0 deletions lifesim/analysis/yield_wrapper.py

Large diffs are not rendered by default.

171 changes: 171 additions & 0 deletions lifesim/analysis/yield_wrapper_notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# `ScienceYield` Wrapper

The purpose of the science yield wrapper is to collect all functions necessary to run a yield with LIFEsim.
While detection yields with single catalogs or instrument setups can be quickly run using
`instrument.snr()` and `opt.ahgs()`, this wrapper is aimed at
- yields for varying instrument setups (e.g. aperture size)
- yields including the characterization yield

Central tools to achieve this are
- a table of all synthetic planets, to which LIFEsim adds the `snr_1h` column. We call this _snr table_.
- the optimizer, which based on the `snr_1h` column creates an optimized observing sequence

## Current State
So far, the ScienceYield wrapper has grown organically.
Here, we provide an overview of the current state.

---

## `ScienceYield`

### Constructor

#### `__init__(self, ...)`

**Description:**
Sets up the working environment, mainly paths to file locations.

**Arguments:**

| Argument | Type | Description |
|-----------------|------|----------------------------------------------|
| `config_path` | str | Path to the config file in .yaml format |
| `catalog_path` | str | Path to the input catalog |
| `output_path` | str | Location where the snr catalogs are saved |
| `n_cpu` | int | Number of CPUs to be used in multiprocessing |
| `cat_from_ppop` | bool | True if input catalog is in .txt format |

**Example:**
```python
import lifesim
ywrap = lifesim.ScienceYield(config_path='/path/CONFIG_251104.yaml',
catalog_path='/path/Bryson2021_hab2low.txt',
output_path='/path/hab2low_500/',
n_cpu=64)
```

#### `_compute_snrs(self, ...)`

**Description:**
Calculates on individual SNR table for one setting of the instrument.
Saves a catalog with `snr_1h` and a config file called `output_filename_catalog.hdf5` and `output_filename.yaml` to `output_path`.
This function is mainly meant to be an internal helper function, it is usually not required to use it.

**Arguments:**

| Argument | Type | Description |
|-------------------|------|---------------------------------------------------------------------------------|
| `output_path` | str | Path to where this individual run is saved |
| `output_filename` | str | Filename of catalog and config table |
| `run_maxsep` | bool | If true, all planets will be placed in their maximum separation |
| `diameter` | int | Mirror diameter for this run. If set to None, diameter from config file is used |

**Files created:**

| Name / Location | Content and Purpose |
|-------------------------------------------|--------------------------------|
| `output_path/output_filename_catalog.hdf5`| snr table for the single run |
| `output_path/output_filename.yaml` |config file for the single run |

**Example:**
(continued from above)
```python
ywrap.compute_snrs(output_path='path/',
output_filename='single_test',
run_maxsep=False,
diameter=None)
```

#### `run_aperture_sweep_snr(self, ...)`

**Description:**
One of the key instrument parameters is the sensitivity.
For LIFE, we usually fix the photon-conversion-efficiency, and the aperture is varied.
This function creates snr tables for an array of mirror diameters.

**Arguments:**

| Argument | Type | Description |
|--------------------|------------------|-------------------------------------------|
| `mirror_diameters` | list/np.ndarray | Array of all mirror sizes to be evaluated |
| `run_name` | str | Name of the run (for file creation) |

**Files created:**

For every diameter in `mirror_diameters`, the following files will be created (here we take 2.5m as example)

| Name / Location | Content and Purpose |
|-------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
| `self.output_path/run_name/diam_2_5/` | new parent directory is created |
| `self.output_path/run_name/diam_2_5/sweep_diam_2_5.yaml` | _config file_ for run with the actual planet position for the detection phase |
| `self.output_path/run_name/diam_2_5/sweep_diam_2_5_catalog.hdf5` | ↑ and its _snr catalog_ |
| `self.output_path/run_name/diam_2_5/sweep_diam_maxsep_2_5.yaml` | _config file_ for run with the maximum planet separation for the characterization phase |
| `self.output_path/run_name/diam_2_5/sweep_diam_maxsep_2_5_catalog.hdf5` | ↑ and its _snr catalog_ |

**Example:**
(continued from above)
```python
ywrap.run_aperture_sweep_snr(mirror_diameters=np.arange(2.0, 5.1, 0.25),
run_name='aperture_size_2_to_5')
```

#### `run_optimizer_sweep(self, ...)`

**Description:**
Runs the optimizer on all snr tables in a given source folder.

**Arguments:**

| Argument | Type | Description |
|---------------|------|-----------------------------------------------------------------------|
| `run_name` | str | Name of the run (for file creation) |
| `source_name` | str | Name of the folder in self.output_path that contains the _snr tables_ |

**Files created:**

| Name / Location | Content and Purpose |
|----------------------------------------------------------------|----------------------------------------------------------------|
| `self.output_path/run_name/` | new parent directory is created, if it does not already exists |
| `self.output_path/run_name/subdir/` | New directory for every directory in the source folder |
| `self.output_path/run_name/subdir/sweep_diam_2_5.yaml` | _config file_ for run of optimizer |
| `self.output_path/run_name/subdir/sweep_diam_2_5_catalog.hdf5` | ↑ and its _snr catalog_ with optimized sequence |

**Example:**
(continued from above)
```python
ywrap.run_optimizer_sweep(run_name='opt_aperture_size_2_to_5',
source_name='aperture_size_2_to_5')
```

---

## Helper Functions

#### `compute_yields_mp()`

**Description:**
Runs the optimizer on all snr tables in a given source folder.

**Arguments:**

| Argument | Type | Description |
|-------------------|----------|-------------------------------------------------------------------------------|
| `output_filename` | str | Name of output files |
| `output_path` | str | Path to which catalog and config file will be saved |
| `catalog_path` | str | Path to the input catalog file |
| `config_path` | str | Path to the config file |
| `uni_sel` | int/None | Number of universes to select from all universes. If `None`, all are selected |
| `return_yields` | bool | Set true to return experiment yield of this run |

**Files created:**

| Name / Location | Content and Purpose |
|-------------------------------------------|----------------------------------|
| `output_path/output_filename_catalog.hdf5`| snr table for the single run |
| `output_path/output_filename.yaml` | config file for the single run |


# What happens in the characterization ahgs



17 changes: 15 additions & 2 deletions lifesim/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import os

import yaml
from numpy import ndarray, array
from numpy import ndarray, array, float64, float32, int64, int32, int16
import git

from lifesim.core.data import Data
Expand Down Expand Up @@ -442,6 +442,8 @@ def add_module(self,
module.data = self.data

def write_config(self):
add_numpy_representers()

module_dict = {key: str(type(module)) for (key, module) in self.modules.items()}

# fetch version number and git commit hash if available
Expand Down Expand Up @@ -475,7 +477,7 @@ def write_config(self):
def build_from_config(self,
filename: str):
with open(filename) as file:
config_dict = yaml.load(file, Loader=yaml.FullLoader)
config_dict = yaml.load(file, Loader=yaml.UnsafeLoader)

self.data.options.array = convert_to_np(config_dict['array'])
self.data.options.optimization = convert_to_np(config_dict['optimization'])
Expand Down Expand Up @@ -520,3 +522,14 @@ def convert_to_np(inp: dict):
else:
out[k] = inp[k]
return out

def add_numpy_representers():
def numpy_repr(dumper, data):
return dumper.represent_data(data.item())

# Add for various numpy types
for dtype in [float64, float32, int64, int32, int16]:
yaml.add_representer(dtype, numpy_repr)

# Handle numpy arrays specifically
yaml.add_representer(ndarray, lambda dumper, data: dumper.represent_list(data.tolist()))
Loading