Skip to content

Type Reader ​

Types reads a type workbook and looks up types by Name or alias. It covers LV and MV cables, fuses and transformers, and GM types.

python
from pyptp.type_reader import Types

types = Types()  # bundled workbook

cable = types.get_lv_cable("4* 50 VMvK/Alk")
fuse = types.get_lv_fuse("Voorbeeld 35 A")
MethodReturnsSheet
get_lv_cable(name)pyptp.elements.lv.shared.CableTypeCable
get_mv_cable(name)pyptp.elements.mv.shared.CableTypeCable
get_lv_fuse(name)pyptp.elements.lv.shared.FuseTypeFuse
get_mv_fuse(name)FuseMV.FuseTypeFuse
get_lv_transformer(name)TransformerLV.TransformerTypeTrafo
get_mv_transformer(name)TransformerMV.TransformerTypeTrafo
get_lv_gm_type(name)GMTypeLVGM

Every lookup returns a new object, so changing the type data of one element leaves the others alone.

A GM type from get_lv_gm_type() has number 0. Adding it to a network gives it the next free number. See Adding Elements.

Applying a type to an element ​

Elements that carry type data have a setter that takes the type library and a name, so you never copy the type object and its name into place yourself. An element with one type has set_type(), an element with several has set_<what>_type():

python
cable.set_cable_type(types, "4*150 VVMvKsas/Alk")    # CableLV
fuse.set_type(types, "Jean Muller 200 A gL/gG")       # FuseLV, FuseMV
transformer.set_type(types, "10250/400 V  630 kVA")   # TransformerLV, TransformerMV
connection.set_cable_type(types, "Vulto 4x10 Cu")     # ConnectionLV
connection.set_fuse_type(types, "Jean Muller 25 A gL/gG")

The setters store the type Name, also when you pass an alias.

An MV cable is a chain of parts, so it grows one part at a time. A new CableMV starts with no parts, types or presentations:

python
cable = CableMV(CableMV.General(name="Kerkplein - Molenweg", node1=..., node2=...))
cable.add_part(types, "3*150 AL Kudi 6/10", 340.0)
cable.add_part(types, "3*150 CU XLPE   6/10", 120.0)

cable_parts holds the length and the type name of every part, cable_types holds the electrical data, and the two lists are matched by position: cable_parts[i] belongs to cable_types[i]. add_part appends to both, so they stay in step. See MV Cable Parts for a full example.

Names and aliases ​

All lookups and setters take the Name or an alias from the Cable alias, Fuse alias or Trafo alias sheet, never a type object. GM types have no alias sheet.

type_name(key, name) returns the Name a lookup would use, without building the type object:

python
types.type_name("lv_cable", "some alias")  # the Name the alias points to

key is one of the TypeKey values: "lv_cable", "mv_cable", "lv_fuse", "mv_fuse", "lv_transformer", "mv_transformer" or "lv_gm_type". TypeKey is exported from pyptp.type_reader for type hints.

When a sheet has the same Name on more than one row, a warning is logged and the last row is used.

Errors ​

SituationRaised
The workbook path names no fileFileNotFoundError, when Types is created
No type under that name or aliasUnknownTypeError, naming the workbook and listing close matches
The workbook has no sheet for that kind of typeMissingSheetError, on the first lookup
A row cannot be turned into a type objectValueError

UnknownTypeError and MissingSheetError are LookupErrors, and both are exported from pyptp.type_reader:

python
from pyptp.type_reader import UnknownTypeError

try:
    cable.set_cable_type(types, "4*150 VVMvKsas/Alk")
except UnknownTypeError as exc:
    logger.error("%s", exc)

Workbook resolution ​

The workbook path is resolved in this order:

  1. The path argument: Types("C:/data/my_types.xlsx")
  2. The PYPTP_TYPES_EXCEL environment variable
  3. The bundled types.xlsx shipped with the package

Expected sheet layout ​

Types reads the sheets Cable, Fuse, Trafo and GM, and the alias sheets Cable alias, Fuse alias and Trafo alias. Each sheet is read on first use.

RowContent
0Column headers (Name, ShortName, Unom, ...)
1 (optional)Unit row (kV, MVA, ...)
2+Data rows

The unit row is detected by content, not position: a first row whose Name (or ShortName) cell is empty is treated as a unit row and skipped. Workbooks without a unit row work as-is, with the first data row preserved.

Column names ​

The expected headers are the property names used in the GNF/VNF formats: Name, ShortName, Unom, R_c, X_c, Ik1s and so on.

Headers are matched ignoring case, so SHORTNAME, Shortname and R_C all land where you expect without any configuration. An exact match always wins, so a sheet carrying both r and R keeps them apart.

Headers that differ from the format property name by more than case are renamed by default:

Sheet headerProperty
Tan_deltaTanDelta (MV cable)
VoPPulseVelocity (MV cable)
R_CC_T, X_CC_T, R_CH_T, X_CH_T, R_HH_T, X_HH_TR_cc_o, X_cc_o, … (LV cable)
Pnul, InulPo, Io (transformer)
s1, s2WindingConnection1, WindingConnection2 (transformer)
ClockClockNumber (transformer)
Side Z0Side_Z0 (transformer)

The LV cable group may be written with a _T or an _O suffix. Both reach the same field.

Custom column names ​

For headers that are named something else in your workbook, map them with column_renames. Keys are TypeKey values and values map source Excel header → expected header:

python
types = Types(
    "C:/data/my_types.xlsx",
    column_renames={
        "lv_cable": {"Weerstand": "R_c", "Naam": "Name"},
        "mv_fuse": {"Naam": "Name"},
    },
)

LV and MV cable types are read from the same Cable sheet, which is why renames are keyed per kind of type rather than per sheet.

An unknown key raises ValueError. The valid keys are available as pyptp.type_reader.RENAME_KEYS.

Aliases (Cable alias, Fuse alias, Trafo alias) are not covered by column_renames. Those sheets always need a Name column.

Reading other sheets ​

For sheets Types does not read (coils, generators, loads, ...), use the public helpers read_type_sheet and clean_row_dict and the deserialize method of the element's type class. This reads the Coil sheet into MV reactance coil types:

python
from pyptp.elements.mv.reactance_coil import ReactanceCoilMV
from pyptp.type_reader import clean_row_dict, read_type_sheet


def read_coil_types(path: str) -> dict[str, ReactanceCoilMV.ReactanceCoilType]:
    coil_types = {}
    for row in read_type_sheet(path, sheet_name="Coil"):
        cleaned = clean_row_dict(row)
        name = str(cleaned.get("Name", "")).strip()
        if name:
            coil_types[name] = ReactanceCoilMV.ReactanceCoilType.deserialize(cleaned)
    return coil_types

read_type_sheet applies the same unit-row detection as Types, accepts a rename mapping and raises MissingSheetError for a sheet the workbook does not have. clean_row_dict drops empty cells so dataclass defaults apply, and its lookups ignore case like the built-in ones.