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.
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")| Method | Returns | Sheet |
|---|---|---|
get_lv_cable(name) | pyptp.elements.lv.shared.CableType | Cable |
get_mv_cable(name) | pyptp.elements.mv.shared.CableType | Cable |
get_lv_fuse(name) | pyptp.elements.lv.shared.FuseType | Fuse |
get_mv_fuse(name) | FuseMV.FuseType | Fuse |
get_lv_transformer(name) | TransformerLV.TransformerType | Trafo |
get_mv_transformer(name) | TransformerMV.TransformerType | Trafo |
get_lv_gm_type(name) | GMTypeLV | GM |
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():
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:
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:
types.type_name("lv_cable", "some alias") # the Name the alias points tokey 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
| Situation | Raised |
|---|---|
| The workbook path names no file | FileNotFoundError, when Types is created |
| No type under that name or alias | UnknownTypeError, naming the workbook and listing close matches |
| The workbook has no sheet for that kind of type | MissingSheetError, on the first lookup |
| A row cannot be turned into a type object | ValueError |
UnknownTypeError and MissingSheetError are LookupErrors, and both are exported from pyptp.type_reader:
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:
- The
pathargument:Types("C:/data/my_types.xlsx") - The
PYPTP_TYPES_EXCELenvironment variable - The bundled
types.xlsxshipped 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.
| Row | Content |
|---|---|
| 0 | Column 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 header | Property |
|---|---|
Tan_delta | TanDelta (MV cable) |
VoP | PulseVelocity (MV cable) |
R_CC_T, X_CC_T, R_CH_T, X_CH_T, R_HH_T, X_HH_T | R_cc_o, X_cc_o, … (LV cable) |
Pnul, Inul | Po, Io (transformer) |
s1, s2 | WindingConnection1, WindingConnection2 (transformer) |
Clock | ClockNumber (transformer) |
Side Z0 | Side_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:
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:
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_typesread_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.