Skip to content

Rooftop Solar Model

The rooftop solar model simulates distributed solar PV adoption and generation. It is implemented in models/solar_rooftop.py.

Overview

Unlike utility-scale solar (modeled as generators with investment decisions), rooftop solar follows an exogenous adoption curve. The model:

  1. Projects rooftop PV adoption per node using S-curve scenarios
  2. Estimates available rooftop area and installable capacity
  3. Generates stochastic availability profiles with weather variability
  4. Injects behind-the-meter generation that reduces net demand

Rooftop solar is activated by setting rooftop_solar_config.enabled: true in the system configuration and enabling simulate_rooftop: true in the system settings.

Adoption Model

Rooftop solar adoption follows a logistic growth curve with three scenarios:

Scenario Growth Rate Max Penetration Typical Use Case
low Conservative 15-20% of potential Minimal policy support, low feed-in tariffs
medium Moderate 40-60% of potential Standard incentive programs
high Aggressive 70-90% of potential Strong mandates, net metering, subsidies

The number of rooftop systems at node \(n\) in year \(y\):

\[ N^{roof}_{n}(y) = N^{roof,0}_n \cdot \frac{K^{roof}}{1 + e^{-r^{roof}(y - y_{mid})}} \tag{ROOF-1} \]

where: - \(N^{roof,0}_n\) is the initial number of systems at node \(n\) (initial_systems_per_node) - \(K^{roof}\) is the carrying capacity ratio (max_systems_per_node / initial_systems_per_node) - \(r^{roof}\) is the growth rate (determined by the adoption scenario) - \(y_{mid}\) is the inflection year (midpoint of the planning horizon)

Total rooftop capacity:

\[ \bar{P}^{roof}_n(y) = N^{roof}_n(y) \cdot \bar{P}^{avg}_{roof} \tag{ROOF-2} \]

where \(\bar{P}^{avg}_{roof}\) is the average system size (typically 3-10 kW), configured via avg_system_size_kw.

Adoption Curve Example

For a node with 100 initial systems and 5000 maximum under the medium scenario:

Year Systems Capacity (5 kW avg)
1 120 0.6 MW
5 350 1.75 MW
10 1,500 7.5 MW
15 3,800 19.0 MW
20 4,700 23.5 MW
25 4,950 24.75 MW

The S-curve shape reflects typical technology adoption patterns: slow initial uptake, rapid growth during the "takeoff" phase, and saturation as the market matures.

Area Estimation

The maximum installable capacity at each node can be constrained by available rooftop area:

\[ \bar{P}^{roof,max}_n = A^{roof}_n \cdot \eta^{panel} \cdot G^{peak} \tag{ROOF-AREA} \]

where: - \(A^{roof}_n\) is the suitable rooftop area in m² (estimated from building footprints × suitability factor) - \(\eta^{panel}\) is the panel efficiency (typically 0.18-0.22 for modern panels) - \(G^{peak}\) is the peak solar irradiance (1.0 kW/m² standard)

A typical suitability factor of 0.3-0.5 accounts for shading, orientation, structural constraints, and architectural features that prevent installation.

Orientation and Tilt

Rooftop solar output depends on panel orientation and tilt angle. The availability profile implicitly captures these effects through location-specific solar resource data. Key factors:

Factor Effect Typical Range
Tilt angle Optimal tilt ≈ latitude for annual energy 10-45 degrees
Azimuth South-facing (Northern hemisphere) optimal ±30° from optimal
Shading Trees, adjacent buildings reduce output 5-20% loss
Soiling Dust accumulation 2-5% loss

These losses are incorporated into the base availability profile rather than modeled explicitly, keeping the optimization problem linear and tractable.

Availability Profiles

Rooftop solar generation profiles include:

  • Base irradiance: Hourly solar resource profile (similar to utility-scale)
  • Weather variability: Stochastic perturbation with configurable levels:
  • low: \(\sigma = 0.05\) — clear-sky dominated regions
  • normal: \(\sigma = 0.10\) — typical temperate climate
  • high: \(\sigma = 0.15\) — tropical or highly variable climate
  • Panel degradation: Annual capacity reduction (typically 0.5%/year)

The hourly generation at node \(n\) in time step \(t\):

\[ P^{roof}_{n,t} = \bar{P}^{roof}_n(y) \cdot \alpha^{roof}_{n,t} \cdot (1 - \delta^{roof})^{age} \tag{ROOF-3} \]

where: - \(\alpha^{roof}_{n,t}\) is the availability factor (0-1) from the profile - \(\delta^{roof}\) is the annual degradation rate - \(age\) is the average fleet age in years

Generated by generate_rooftop_solar_profiles().

Profile Generation

The availability profile can be:

  1. Provided externally: A CSV file with hourly capacity factors (8760 values for one year)
  2. Generated internally: Using the built-in solar resource model with location coordinates and weather variability

Example availability profile CSV (solar_rooftop_profile.csv):

hour,capacity_factor
0,0.000
1,0.000
...
6,0.050
7,0.180
8,0.420
9,0.650
10,0.820
11,0.910
12,0.950
13,0.920
14,0.830
15,0.680
16,0.450
17,0.200
18,0.060
19,0.000
...

Self-Consumption vs Grid Injection

Rooftop solar generation is split between self-consumption (consumed locally, never enters the grid) and grid injection (surplus exported to the network):

\[ P^{roof}_{n,t} = P^{self}_{n,t} + P^{inject}_{n,t} \]

In the current ESFEX implementation, all rooftop generation is treated as grid-visible supply injection. Self-consumption can be modeled by reducing the net demand profile before optimization:

\[ D^{net}_{n,t} = D^{gross}_{n,t} - P^{self}_{n,t} \]

This approach is valid when self-consumption follows a predictable pattern (e.g., a fixed fraction of generation during business hours).

Power System Integration

Rooftop solar generation appears in the power balance as supply:

\[ \underbrace{\sum_g P_{g,n,t} + P^{roof}_{n,t}}_{\text{total generation}} + \ldots = D_{n,t} + C^{roof}_{n,t} + \ldots \]

where \(C^{roof}_{n,t}\) is rooftop curtailment (penalized but allowed).

Behind-the-Meter

Rooftop solar is treated as negative demand (or supply injection). It is not dispatchable -- the optimizer can only curtail it, not increase it.

Interaction with Utility-Scale Solar

Rooftop and utility-scale solar are modeled independently:

Aspect Utility-Scale Rooftop
Investment Optimized (decision variable) Exogenous (S-curve)
Dispatch Curtailable, scheduled Non-dispatchable
Location At network buses Behind-the-meter
Size 10-500 MW 3-10 kW per system
Grid visibility Full Reduced net demand
Degradation Per-generator Fleet average

Interaction with Battery Storage

Rooftop solar excess can charge batteries at the same node. The optimizer naturally coordinates rooftop injection with battery charging to minimize system costs. High rooftop penetration often drives battery investment to shift midday solar surplus to evening peak demand.

Capacity Expansion Interaction

In the master problem, rooftop solar capacity is not a decision variable. Instead:

  1. The adoption model projects rooftop capacity for each planning year
  2. This capacity is subtracted from gross demand to compute net demand
  3. The master problem optimizes investments in utility-scale generation and storage to meet the remaining net demand

This means rooftop solar acts as a demand modifier in capacity expansion, reducing the need for centralized generation investments.

Configuration

rooftop_solar_config:
  enabled: true
  adoption_scenario: medium       # low, medium, high
  avg_system_size_kw: 5.0
  initial_systems_per_node: [100]
  max_systems_per_node: [5000]
  degradation_rate: 0.005         # 0.5%/year
  availability_file: solar_rooftop_profile.csv
  weather_variability: normal     # low, normal, high
Parameter Type Default Description
enabled bool false Enable rooftop solar simulation
adoption_scenario str "medium" Growth scenario: low, medium, high
avg_system_size_kw float 5.0 Average rooftop system size in kW
initial_systems_per_node list[int] [0] Starting number of systems per node
max_systems_per_node list[int] [1000] Maximum installable systems per node
degradation_rate float 0.005 Annual panel degradation (0.5%/year)
availability_file str -- Path to hourly capacity factor CSV
weather_variability str "normal" Stochastic variability level

Output

The model produces:

Output Shape Description
rooftop_generation (hours, nodes) Hourly generation in MW
rooftop_curtailment (hours, nodes) Curtailed rooftop generation
adoption_factors (nodes,) Adoption multiplier per node per year
max_potential (nodes,) Maximum installable capacity per node
degradation_factor scalar Fleet-average degradation multiplier

Accessing Results

Rooftop solar results are stored in the HDF5 output file under each year's group:

/year_1/rooftop/generation     # (hours, nodes) array
/year_1/rooftop/curtailment    # (hours, nodes) array
/year_1/rooftop/capacity       # (nodes,) installed capacity

Limitations

  • No individual system modeling: All systems at a node are aggregated; individual orientation, shading, and sizing are averaged.
  • No battery pairing: Rooftop batteries (home storage) are not explicitly modeled; use grid-level battery investment instead.
  • Fixed adoption curve: The adoption trajectory is exogenous and does not respond to electricity prices or incentives within the optimization.
  • No net metering tariff: Feed-in tariff or net metering compensation is not modeled; all generation is valued at the system marginal cost.

References

The distributed generation market demand modeling follows the NREL dGen model by Sigrin et al. [54]. Rooftop suitability estimation methods are reviewed by Melius et al. [55]. IRENA renewable cost benchmarks [48] are used for default PV cost assumptions.

See the full bibliography for complete citation details.