Skip to content

simplenet.io.xlsx

xlsx I/O matching the matlab2*.xlsx schema used by the MATLAB workflow.

Input layout (sheets): - Bus bus matrix (13-17 columns) - Gen gen matrix (21-25 columns) - Branch branch matrix (13-21 columns) - GenCost optional gencost matrix - Gentype optional 1-column string list (header row, then values) - Genfuel optional 1-column string list - Bus Names optional 1-column string list

The MATLAB code uses readmatrix / readcell with 'Range','A2' for the string sheets - i.e. it skips the header row. Numeric sheets have no header.

Output (dump_xlsx) mirrors reduction_test.m's Result_*.xlsx: sheets Summary, Gen, Bus, Branch with the column headers defined there.

load_xlsx

load_xlsx(path: str | Path) -> PowerCase

Load a matlab2*.xlsx style workbook into a :class:PowerCase.

The baseMVA is fixed to 100 to match case_ACTIVSg10kCopy2.m. Override case.base_mva after loading if the source case uses a different base.

Source code in src/simplenet/io/xlsx.py
def load_xlsx(path: str | Path) -> PowerCase:
    """Load a ``matlab2*.xlsx`` style workbook into a :class:`PowerCase`.

    The ``baseMVA`` is fixed to 100 to match
    ``case_ACTIVSg10kCopy2.m``. Override ``case.base_mva`` after loading
    if the source case uses a different base.
    """

    p = Path(path)
    bus = _read_matrix(p, "Bus")
    gen = _read_matrix(p, "Gen")
    branch = _read_matrix(p, "Branch")
    if bus is None or gen is None or branch is None:
        raise ValueError(
            f"{path}: expected Bus/Gen/Branch sheets, got {bus is not None}/"
            f"{gen is not None}/{branch is not None}"
        )

    gencost = _read_matrix(p, "GenCost")
    if gencost is not None and gencost.size == 0:
        gencost = None

    gentype = _read_string_list(p, "Gentype")
    genfuel = _read_string_list(p, "Genfuel")
    bus_name = _read_string_list(p, "Bus Names")
    if bus_name is None:
        bus_name = _read_string_list(p, "Bus_Names")

    return PowerCase(
        base_mva=100.0,
        bus=bus,
        gen=gen,
        branch=branch,
        gencost=gencost,
        gentype=gentype,
        genfuel=genfuel,
        bus_name=bus_name,
    )

dump_xlsx

dump_xlsx(reduced: PowerCase, path: str | Path, *, summary: str | list[str] | None = None) -> None

Write a reduced :class:PowerCase to a multi-sheet xlsx.

Mirrors the output of reduction_test.m: sheets Summary (one column of text lines), Gen, Bus, Branch (with header rows from the original MATLAB script). Generator type / fuel / bus-name lists, when present, are appended as their own sheets.

Source code in src/simplenet/io/xlsx.py
def dump_xlsx(
    reduced: PowerCase,
    path: str | Path,
    *,
    summary: str | list[str] | None = None,
) -> None:
    """Write a reduced :class:`PowerCase` to a multi-sheet xlsx.

    Mirrors the output of ``reduction_test.m``: sheets ``Summary`` (one
    column of text lines), ``Gen``, ``Bus``, ``Branch`` (with header
    rows from the original MATLAB script). Generator type / fuel /
    bus-name lists, when present, are appended as their own sheets.
    """

    p = Path(path)
    p.parent.mkdir(parents=True, exist_ok=True)

    if summary is None:
        summary_lines: list[str] = []
    elif isinstance(summary, str):
        summary_lines = summary.splitlines()
    else:
        summary_lines = list(summary)
    summary_df = pd.DataFrame({"Summary": summary_lines})

    bus_header = _trim_header(BUS_FULL_HEADER, reduced.bus.shape[1])
    gen_header = _trim_header(GEN_FULL_HEADER, reduced.gen.shape[1])
    branch_header = _trim_header(BRANCH_FULL_HEADER, reduced.branch.shape[1])

    bus_df = pd.DataFrame(reduced.bus, columns=bus_header)
    gen_df = pd.DataFrame(reduced.gen, columns=gen_header)
    branch_df = pd.DataFrame(reduced.branch, columns=branch_header)

    with pd.ExcelWriter(p, engine="openpyxl") as writer:
        summary_df.to_excel(writer, sheet_name="Summary", index=False)
        gen_df.to_excel(writer, sheet_name="Gen", index=False)
        bus_df.to_excel(writer, sheet_name="Bus", index=False)
        branch_df.to_excel(writer, sheet_name="Branch", index=False)
        if reduced.gencost is not None and reduced.gencost.size:
            pd.DataFrame(reduced.gencost).to_excel(writer, sheet_name="GenCost", index=False, header=False)
        if reduced.gentype:
            pd.DataFrame({"Gentype": list(reduced.gentype)}).to_excel(writer, sheet_name="Gentype", index=False)
        if reduced.genfuel:
            pd.DataFrame({"Genfuel": list(reduced.genfuel)}).to_excel(writer, sheet_name="Genfuel", index=False)
        if reduced.bus_name:
            pd.DataFrame({"Bus Names": list(reduced.bus_name)}).to_excel(writer, sheet_name="Bus Names", index=False)

dump_excluded_template

dump_excluded_template(path: str | Path, bus_ids: list[Any]) -> None

Write a one-column excluded_nodes.csv-style template file.

Source code in src/simplenet/io/xlsx.py
def dump_excluded_template(path: str | Path, bus_ids: list[Any]) -> None:
    """Write a one-column ``excluded_nodes.csv``-style template file."""

    p = Path(path)
    p.parent.mkdir(parents=True, exist_ok=True)
    pd.DataFrame({"ExcludedNodes": list(bus_ids)}).to_csv(p, index=False)