el1xr_opt.Modules.oM_HeatSector#

Heat sector – a third energy carrier alongside electricity and hydrogen.

create_heat_sector is wired into oM_Sequence.build_model (after the green-hydrogen step) and builds a behind-the-meter heat sector when the case carries heat data; it is a no-op otherwise, so electricity/hydrogen-only cases are unchanged.

Scope today – home / residential heat: a building-level, nodal heat demand met by a heat pump or boiler plus a thermal store, produced and used at the same node (no heat network yet – district heating with pipe flows and losses is the planned next setting). The power-heat loop is open with a heat pump alone and closed by adding a heat-to-power unit.

Input tables (read by load_heat_data):

  • oM_Dict_HeatGeneration / oM_Data_HeatGeneration (with a Type column – HeatPump / Boiler / Heat2Ele / Storage)

  • oM_Data_HeatDemand / oM_Data_VarMaxHeatDemand

Sets, variables and constraints:

  • sets: htg (heat generation), htp (heat pumps), htw (heat-to-power),

    hts (thermal storage), htd (heat demand)

  • variables: vHeatOutput, vHeatPumpElec, vHeatCharge / vHeatDischarge /

    vHeatInventory, vHeatConsumed, vHeatToEle, vHeatNotServed

  • constraints: nodal heat balance (eHeatBalance), heat-pump coupling

    (eHeatPumpCOP), heat-to-power (eHeatToEle), thermal-store inventory (eHeatInventory).

The heat-pump electricity draw and the heat-to-power injection are folded into the electricity balance, and the heat operating cost (period-discounted, duration-weighted) into the objective.

el1xr_opt.Modules.oM_HeatSector.heat_tables_present(source)[source]#

True if the opened input source carries any heat data table.

Lets the pipeline detect a heat-bearing case once the formulation exists.

Return type:

bool

el1xr_opt.Modules.oM_HeatSector.load_heat_data(model, dir_name, case_name)[source]#

Read the heat input tables and populate the heat sets and parameters on the model. No-op (returns False) when the case carries no heat tables, so the four validation cases are unaffected. The minimal schema:

  • oM_Data_HeatGeneration – index = unit; columns Node, Type (HeatPump | Boiler | Heat2Ele | Storage), MaximumPower, Cost, COP, Efficiency, MaxStorage, StoEff, InitialStorage.

  • oM_Data_HeatDemand – index = demand unit; column Node.

  • oM_Data_VarMaxHeatDemand – (period, scenario, load level) index; one column per heat-demand unit, the heat demand at that step.

Heat-not-served cost is taken from the Parameter table (pParHeatNSCost) or a high default.

el1xr_opt.Modules.oM_HeatSector.create_heat_sector(model, optmodel, indlog='False', dir_name=None, case_name=None)[source]#

Build the nodal home-heat sector (demand, heat pump, boiler, thermal store).

Home / behind-the-meter heat: heat is produced and used at the same node (no heat network yet). It mirrors the electricity and hydrogen sectors in nodal style. It builds only when the case carries heat sets, so calling it on an electricity/hydrogen-only model is a no-op (the four validation cases are unaffected). When dir_name / case_name are given it reads the case’s heat tables via load_heat_data() (the CSV pipeline, used by oM_Sequence); a case can also set the sets/params directly, which is how tests/test_heat_sector exercises the formulation. Coupling into the electricity balance (heat-pump load, heat-to-power output) and the objective (heat operating cost) is done by the callers, so this runs before create_constraints in the build.

Expected on model when a heat case is present:
  • sets htd (heat demands), htg (heat generators), htp (the heat

    pumps, a subset of htg driven by electricity), hts (thermal stores), n (load levels), and the node maps n2htd / n2htg / n2hts giving the (node, unit) membership.

  • params in model.Par: pHeatDemand[htd][n], pHeatGenMaxPower[htg],

    pHeatGenCost[htg] (per heat unit, e.g. boiler fuel), pHeatPumpCOP[htp], pHeatStoMax[hts], pHeatStoEff[hts], pHeatStoInitial[hts], pHeatNSCost, pDuration[n].

It creates, on optmodel: vHeatOutput (per generator), vHeatPumpElec (electricity drawn by each heat pump – the cross-sector coupling), vHeatCharge / vHeatInventory (thermal store), vHeatNotServed; and the constraints eHeatBalance (nodal), eHeatPumpCOP (heat = COP x electricity), eHeatInventory (store dynamics). The heat operating cost (heat-not-served plus generator running cost) is exposed as optmodel.HeatOperatingCost so the caller can add it to the objective; the heat-pump electricity load at a node is available from heat_electricity_load() for the electricity balance.

el1xr_opt.Modules.oM_HeatSector.heat_electricity_load(optmodel, node_units, p, sc, n)[source]#

Total electricity drawn by the heat pumps at a node in (period, scenario, load level). node_units is the list of heat-pump units at the node. The electricity balance adds this as a load so the heat-pump COP coupling closes across sectors. Returns 0 when the model has no heat pumps (no heat case).

el1xr_opt.Modules.oM_HeatSector.heat_to_power_output(optmodel, node_units, p, sc, n)[source]#

Electricity produced by the heat-to-power units at a node in (period, scenario, load level) – the analogue of the hydrogen fuel cell’s electricity output. The electricity balance adds this as generation, closing the power<->heat loop. Returns 0 when the model has no heat-to-power units.