---
myst:
html_meta:
"description lang=en": |
How PANORAMA reads and writes HDF5 pangenome files and produces flat text outputs.
"keywords": |
Pangenomics, Python, Developer, HDF5, I/O, Format, Read, Write
---
# I/O Layer
PANORAMA stores all computed data in the same **HDF5 `.h5` file** that PPanGGOLiN uses,
extending it with PANORAMA-specific groups and tables. Flat text outputs are written
separately by {py:mod}`panorama.format.write_flat` and {py:mod}`panorama.systems.write_systems`.
---
## Module overview
| Module | Role |
|---|---|
| {py:mod}`panorama.format.read_binaries` | Read HDF5 files; parallel multi-pangenome loading |
| {py:mod}`panorama.format.write_binaries` | Write / update / erase system data in HDF5 files |
| {py:mod}`panorama.format.write_flat` | Write flat TSV/CSV outputs for gene families and spots |
| {py:mod}`panorama.systems.write_systems` | Write flat TSV outputs for detected systems |
---
## HDF5 structure
PANORAMA reuses the PPanGGOLiN HDF5 layout and adds one new top-level group:
```
/ (PPanGGOLiN root)
├── status/ extended with PANORAMA attributes
│ ├── systems bool — True if any systems have been written
│ └── systems_sources set of source name strings
│
├── annotations/ PPanGGOLiN (unchanged)
├── gene_families/ PPanGGOLiN (unchanged)
├── RGP/ PPanGGOLiN (unchanged)
├── spots/ PPanGGOLiN (unchanged)
├── modules/ PPanGGOLiN (unchanged)
│
└── systems/ PANORAMA addition
└── / one group per annotation source
├── systems table — one row per System
├── units table — one row per (System, SystemUnit, GeneFamily) triple
├── canonical table — one row per canonical System
└── canonical_units table — one row per canonical (System, SystemUnit, GeneFamily) triple
```
### Table schemas
#### `systems` table
| Column | Type | Description |
|---|---|---|
| `ID` | `Int64` | System identifier |
| `name` | `String` | System name (from the model) |
| `canonical_count` | `Int64` | Number of canonical representations |
#### `units` table
| Column | Type | Description |
|---|---|---|
| `ID` | `Int64` | SystemUnit identifier |
| `name` | `String` | FuncUnit name |
| `geneFam` | `String` | GeneFamily name |
| `metadata_id` | `Int64` | Metadata record identifier |
| `metadata_source` | `String` | Annotation source name |
The `canonical` and `canonical_units` tables follow the same schemas as `systems` and
`units` respectively.
String column widths are computed dynamically before the table is created by
{py:func}`panorama.format.write_binaries.calculate_system_table_sizes` to avoid wasting
space while still fitting all values.
---
## Reading HDF5 files
### Status check
{py:func}`panorama.format.read_binaries.get_status` is called by
{py:meth}`Pangenome.add_file() ` for every
pangenome. It calls PPanGGOLiN's own `get_status` first, then reads the PANORAMA-specific
`status.systems` and `status.systems_sources` attributes to populate
`pangenome.status["systems"]` and `pangenome.status["systems_sources"]`.
### Loading a single pangenome
{py:func}`panorama.format.read_binaries.read_pangenome` is the all-in-one loader for a
single `.h5` file. It accepts a `need_info` dict that controls which data groups are
read:
| `need_info` key | What is loaded |
|---|---|
| `need_annotations` | Organism / gene annotations |
| `need_families` | Gene family graph |
| `need_families_info` | Per-family metadata (HMM hits) |
| `need_rgp` | Regions of genomic plasticity |
| `need_spots` | Spot structures |
| `need_modules` | Module structures |
| `need_systems` | PANORAMA system objects |
Internally it delegates to the matching PPanGGOLiN reader functions for all standard
groups and to {py:func}`panorama.format.read_binaries.read_systems` for the PANORAMA
systems group.
### Parallel loading of multiple pangenomes
{py:func}`panorama.format.read_binaries.load_pangenomes` loads a
{py:class}`Pangenomes ` container using a
`ThreadPoolExecutor`. A shared `multiprocessing.Manager().Lock()` (initialised via
{py:func}`panorama.utils.init_lock`) serialises all concurrent HDF5 table reads because
PyTables is not thread-safe for simultaneous reads.
```python
# Typical call pattern in a launch() function
need_info = check_my_command_args(args)
pangenomes = load_pangenomes(
pangenomes_file=args.pangenomes,
need_info=need_info,
disable_bar=args.disable_prog_bar,
cpu=args.cpus,
)
```
---
## Writing HDF5 files
### Writing systems
{py:func}`panorama.format.write_binaries.write_systems_to_hdf5` is the low-level writer.
It is called by {py:func}`panorama.format.write_binaries.write_pangenome`, which also
handles the PPanGGOLiN base tables and updates the status group.
The write sequence is:
1. {py:func}`panorama.format.write_binaries.calculate_system_table_sizes` — scan all
systems to determine maximum string lengths and row counts
2. {py:func}`panorama.format.write_binaries.create_system_tables` — create the four HDF5
tables with the computed column sizes
3. {py:func}`panorama.format.write_binaries.write_system_data_to_tables` — iterate
systems and write one row per `(System, SystemUnit, GeneFamily)` triple
4. {py:func}`panorama.format.write_binaries.write_pangenome_status` — update
`status.systems` and `status.systems_sources`
### Erasing systems
{py:func}`panorama.format.write_binaries.erase_pangenome` removes the HDF5 group for a
given source (called when `--force` is passed to re-run detection).
---
## Flat file outputs
### Gene families and spots ({py:mod}`panorama.format.write_flat`)
Invoked by the `write` subcommand. Writes TSV files describing:
- HMM annotation results per gene family
- Spot border compositions
- Conserved spot memberships
### Systems ({py:mod}`panorama.systems.write_systems`)
Invoked by the `write_systems` subcommand (and by `pansystems`). Writes TSV files
describing:
- Per-pangenome system summaries
- System-to-organism projections
- System-to-spot / system-to-module associations
- Partition heatmap data
```{note}
All flat files are regenerated from in-memory objects — they do not read back from HDF5.
Re-running `write_systems` with different filters produces different outputs without
modifying the `.h5` file.
```