Skip to content

Commit ce42872

Browse files
committed
Select technology data for a single year
1 parent 6fe3ebe commit ce42872

3 files changed

Lines changed: 84 additions & 88 deletions

File tree

src/muse/sectors/sector.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,12 @@ def group_assets(x: xr.DataArray) -> xr.DataArray:
216216

217217
# Investments
218218
# uses technology data from the investment year
219+
techs = self.technologies.sel(year=investment_year, drop=True)
219220
for subsector in self.subsectors:
220-
subsector.invest(
221-
technologies=self.technologies.sel(year=investment_year),
222-
market=market,
223-
)
221+
subsector.invest(technologies=techs, market=market)
224222

225223
# Full output data
226-
supply, consume, costs = self.market_variables(market, self.technologies)
224+
supply, consume, costs = self.market_variables(market, techs)
227225
self.output_data = xr.Dataset(
228226
dict(
229227
supply=supply,
@@ -293,6 +291,7 @@ def market_variables(self, market: xr.Dataset, technologies: xr.Dataset) -> Any:
293291
from muse.quantities import consumption
294292
from muse.utilities import broadcast_techs
295293

294+
assert "year" not in technologies.dims
296295
years = market.year.values
297296
capacity = self.capacity.interp(year=years, **self.interpolation)
298297

src/muse/utilities.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ def broadcast_techs(
201201
This function broadcast the first representation to the shape and coordinates
202202
of the second.
203203
204+
This function does not support technologies with a 'year' dimension. Please select
205+
technology data for a specific year before calling this function.
206+
204207
Arguments:
205208
technologies: The dataset to broadcast
206209
template: the dataset or data-array to use as a template
@@ -212,6 +215,8 @@ def broadcast_techs(
212215
kwargs: further arguments are used initial filters over the
213216
`technologies` dataset.
214217
"""
218+
assert "year" not in technologies.dims
219+
215220
# this assert will trigger if 'year' is changed to 'installed' in
216221
# technologies, because then this function should be modified.
217222
assert "installed" not in technologies.dims
@@ -226,19 +231,6 @@ def broadcast_techs(
226231
first_sel.update({k: v for k, v in kwargs.items() if k != "year"})
227232
techs = technologies.sel(first_sel)
228233

229-
if "year" in technologies.dims:
230-
year = None
231-
if installed_as_year and "installed" in names:
232-
year = template["installed"]
233-
elif (not installed_as_year) and "year" in template.dims:
234-
year = template["year"]
235-
if year is not None and len(year) > 0:
236-
techs = techs.interp(
237-
year=sorted(set(cast(Iterable, year.values))), method=interpolation
238-
)
239-
if installed_as_year and "installed" in names:
240-
techs = techs.rename(year="installed")
241-
242234
second_sel = {n: template[n] for n in template.coords if n in techs.dims}
243235

244236
return techs.sel(second_sel)

tests/test_quantities.py

Lines changed: 75 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,57 +5,62 @@
55
from pytest import approx, fixture
66

77

8+
@fixture
9+
def _technologies(technologies):
10+
return technologies.sel(year=2010, drop=True)
11+
12+
813
@fixture
914
def production(
10-
technologies: xr.Dataset, capacity: xr.DataArray, timeslice
15+
_technologies: xr.Dataset, capacity: xr.DataArray, timeslice
1116
) -> xr.DataArray:
1217
from muse.timeslices import broadcast_timeslice, distribute_timeslice
1318

1419
return (
1520
broadcast_timeslice(capacity)
16-
* distribute_timeslice(technologies.fixed_outputs)
17-
* broadcast_timeslice(technologies.utilization_factor)
21+
* distribute_timeslice(_technologies.fixed_outputs)
22+
* broadcast_timeslice(_technologies.utilization_factor)
1823
)
1924

2025

21-
def test_supply_enduse(technologies, capacity, timeslice):
26+
def test_supply_enduse(_technologies, capacity, timeslice):
2227
"""End-use part of supply."""
2328
from muse.commodities import is_enduse
2429
from muse.quantities import maximum_production, supply
2530

26-
production = maximum_production(technologies, capacity)
31+
production = maximum_production(_technologies, capacity)
2732
demand = production.sum("asset") + 1
28-
spl = supply(capacity, demand, technologies).where(
29-
is_enduse(technologies.comm_usage), 0
33+
spl = supply(capacity, demand, _technologies).where(
34+
is_enduse(_technologies.comm_usage), 0
3035
)
3136
assert (abs(spl - production) < 1e-12).all()
3237
assert (spl.sum("asset") < demand).all()
3338

3439
demand = production.sum("asset") * 0.7
35-
spl = supply(capacity, demand, technologies).where(
36-
is_enduse(technologies.comm_usage), 0
40+
spl = supply(capacity, demand, _technologies).where(
41+
is_enduse(_technologies.comm_usage), 0
3742
)
3843
assert (spl <= production + 1e-12).all()
3944
assert (
4045
abs(spl.sum("asset") - demand.where(production.sum("asset") > 0, 0)) < 1e-12
4146
).all()
4247

4348

44-
def test_supply_emissions(technologies, capacity, timeslice):
49+
def test_supply_emissions(_technologies, capacity, timeslice):
4550
"""Emission part of supply."""
4651
from muse.commodities import is_enduse, is_pollutant
4752
from muse.quantities import emission, maximum_production, supply
4853

49-
production = maximum_production(technologies, capacity)
50-
spl = supply(capacity, production.sum("asset") + 1, technologies)
51-
msn = emission(spl.where(is_enduse(spl.comm_usage), 0), technologies.fixed_outputs)
54+
production = maximum_production(_technologies, capacity)
55+
spl = supply(capacity, production.sum("asset") + 1, _technologies)
56+
msn = emission(spl.where(is_enduse(spl.comm_usage), 0), _technologies.fixed_outputs)
5257
actual, expected = xr.broadcast(
5358
spl.sel(commodity=is_pollutant(spl.comm_usage)), msn
5459
)
5560
assert actual.values == approx(expected.values)
5661

5762

58-
def test_gross_margin(technologies, capacity, market, timeslice):
63+
def test_gross_margin(_technologies, capacity, market, timeslice):
5964
from muse.commodities import is_enduse, is_fuel, is_pollutant
6065
from muse.quantities import gross_margin
6166

@@ -67,22 +72,22 @@ def test_gross_margin(technologies, capacity, market, timeslice):
6772
# we modify the variables to have just the values we want for the testing
6873
selected = capacity.technology.values[0]
6974

70-
technologies = technologies.sel(technology=technologies.technology == selected)
75+
_technologies = _technologies.sel(technology=_technologies.technology == selected)
7176
capa = capacity.where(capacity.technology == selected, drop=True)
7277

7378
# Filtering commodity outputs
74-
usage = technologies.comm_usage
79+
usage = _technologies.comm_usage
7580

76-
technologies.var_par[:] = vp = 2
77-
technologies.var_exp[:] = ve = 0.5
78-
technologies.fixed_inputs[{"commodity": is_fuel(usage)}] = fuels = 2
79-
technologies.fixed_outputs[{"commodity": is_pollutant(usage)}] = envs = 10
80-
technologies.fixed_outputs[{"commodity": is_enduse(usage)}] = prod = 5
81+
_technologies.var_par[:] = vp = 2
82+
_technologies.var_exp[:] = ve = 0.5
83+
_technologies.fixed_inputs[{"commodity": is_fuel(usage)}] = fuels = 2
84+
_technologies.fixed_outputs[{"commodity": is_pollutant(usage)}] = envs = 10
85+
_technologies.fixed_outputs[{"commodity": is_enduse(usage)}] = prod = 5
8186

8287
market.prices[:] = prices = 3
8388
market.prices[{"commodity": is_pollutant(usage)}] = env_prices = 6
8489
# We expect a xr.DataArray with 1 replacement technology
85-
actual = gross_margin(technologies, capa, market.prices)
90+
actual = gross_margin(_technologies, capa, market.prices)
8691

8792
revenues = prices * prod * sum(is_enduse(usage))
8893
env_costs = env_prices * envs * sum(is_pollutant(usage))
@@ -96,25 +101,25 @@ def test_gross_margin(technologies, capacity, market, timeslice):
96101
assert actual.values == approx(expected.values)
97102

98103

99-
def test_consumption(technologies, production, market):
104+
def test_consumption(_technologies, production, market):
100105
from muse.quantities import consumption
101106

102107
# Prices not provided, so flexible inputs are ignored
103-
consump = consumption(technologies, production)
108+
consump = consumption(_technologies, production)
104109
assert set(production.dims) == set(consump.dims)
105110

106111
# Prices provided, but no flexible inputs -> should be the same as above
107-
technologies.flexible_inputs[:] = 0
108-
consump2 = consumption(technologies, production, market.prices)
112+
_technologies.flexible_inputs[:] = 0
113+
consump2 = consumption(_technologies, production, market.prices)
109114
assert consump2.values == approx(consump.values)
110115

111116
# Flexible inputs considered
112-
consump3 = consumption(technologies, production, market.prices)
117+
consump3 = consumption(_technologies, production, market.prices)
113118
assert set(production.dims) == set(consump3.dims)
114119

115120

116121
def test_production_aggregate_asset_view(
117-
capacity: xr.DataArray, technologies: xr.Dataset
122+
capacity: xr.DataArray, _technologies: xr.Dataset
118123
):
119124
"""Production when capacity has format of agent.sector.
120125
@@ -123,36 +128,36 @@ def test_production_aggregate_asset_view(
123128
from muse.commodities import is_enduse
124129
from muse.quantities import maximum_production
125130

126-
technologies: xr.Dataset = technologies[ # type:ignore
131+
_technologies: xr.Dataset = _technologies[ # type:ignore
127132
["fixed_outputs", "utilization_factor"]
128133
]
129134

130-
enduses = is_enduse(technologies.comm_usage)
135+
enduses = is_enduse(_technologies.comm_usage)
131136
assert enduses.any()
132137

133-
technologies.fixed_outputs[:] = 1
134-
technologies.utilization_factor[:] = 1
135-
prod = maximum_production(technologies, capacity)
138+
_technologies.fixed_outputs[:] = 1
139+
_technologies.utilization_factor[:] = 1
140+
prod = maximum_production(_technologies, capacity)
136141
assert set(prod.dims) == set(capacity.dims).union({"commodity", "timeslice"})
137142
assert prod.sel(commodity=~enduses).values == approx(0)
138143
prod, expected = xr.broadcast(
139144
prod.sel(commodity=enduses).sum("timeslice"), capacity
140145
)
141146
assert prod.values == approx(expected.values)
142147

143-
technologies.fixed_outputs[:] = fouts = 2
144-
technologies.utilization_factor[:] = ufact = 0.5
145-
prod = maximum_production(technologies, capacity)
148+
_technologies.fixed_outputs[:] = fouts = 2
149+
_technologies.utilization_factor[:] = ufact = 0.5
150+
prod = maximum_production(_technologies, capacity)
146151
assert prod.sel(commodity=~enduses).values == approx(0)
147152
assert set(prod.dims) == set(capacity.dims).union({"commodity", "timeslice"})
148153
prod, expected = xr.broadcast(
149154
prod.sel(commodity=enduses).sum("timeslice"), capacity
150155
)
151156
assert prod.values == approx(fouts * ufact * expected.values)
152157

153-
technologies.fixed_outputs[:] = fouts = 3
154-
technologies.utilization_factor[:] = ufact = 0.5
155-
prod = maximum_production(technologies, capacity)
158+
_technologies.fixed_outputs[:] = fouts = 3
159+
_technologies.utilization_factor[:] = ufact = 0.5
160+
prod = maximum_production(_technologies, capacity)
156161
assert prod.sel(commodity=~enduses).values == approx(0)
157162
assert set(prod.dims) == set(capacity.dims).union({"commodity", "timeslice"})
158163
prod, expected = xr.broadcast(
@@ -162,93 +167,93 @@ def test_production_aggregate_asset_view(
162167

163168

164169
def test_production_agent_asset_view(
165-
capacity: xr.DataArray, technologies: xr.Dataset, timeslice
170+
capacity: xr.DataArray, _technologies: xr.Dataset, timeslice
166171
):
167172
"""Production when capacity has format of agent.assets.capacity."""
168173
from muse.utilities import coords_to_multiindex, reduce_assets
169174

170175
capacity = coords_to_multiindex(reduce_assets(capacity)).unstack("asset").fillna(0)
171-
test_production_aggregate_asset_view(capacity, technologies)
176+
test_production_aggregate_asset_view(capacity, _technologies)
172177

173178

174-
def test_capacity_in_use(production: xr.DataArray, technologies: xr.Dataset):
179+
def test_capacity_in_use(production: xr.DataArray, _technologies: xr.Dataset):
175180
from muse.commodities import is_enduse
176181
from muse.quantities import capacity_in_use
177182

178-
technologies: xr.Dataset = technologies[ # type: ignore
183+
_technologies: xr.Dataset = _technologies[ # type: ignore
179184
["fixed_outputs", "utilization_factor"]
180185
]
181186
production[:] = prod = 10
182-
technologies.fixed_outputs[:] = fout = 5
183-
technologies.utilization_factor[:] = ufac = 2
187+
_technologies.fixed_outputs[:] = fout = 5
188+
_technologies.utilization_factor[:] = ufac = 2
184189

185-
enduses = is_enduse(technologies.comm_usage)
186-
capa = capacity_in_use(production, technologies, max_dim=None)
190+
enduses = is_enduse(_technologies.comm_usage)
191+
capa = capacity_in_use(production, _technologies, max_dim=None)
187192
assert "commodity" in capa.dims
188193
capa, expected = xr.broadcast(capa, enduses * prod / fout / ufac)
189194
assert capa.values == approx(expected.values)
190195

191-
capa = capacity_in_use(production, technologies)
196+
capa = capacity_in_use(production, _technologies)
192197
assert "commodity" not in capa.dims
193198
assert capa.values == approx(prod / fout / ufac)
194199

195200
maxcomm = np.random.choice(production.commodity.sel(commodity=enduses).values)
196201
production.loc[{"commodity": maxcomm}] = prod = 11
197-
capa = capacity_in_use(production, technologies)
202+
capa = capacity_in_use(production, _technologies)
198203
assert "commodity" not in capa.dims
199204
assert capa.values == approx(prod / fout / ufac)
200205

201206

202-
def test_emission(production: xr.DataArray, technologies: xr.Dataset):
207+
def test_emission(production: xr.DataArray, _technologies: xr.Dataset):
203208
from muse.commodities import is_enduse, is_pollutant
204209
from muse.quantities import emission
205210

206-
envs = is_pollutant(technologies.comm_usage)
207-
technologies = cast(xr.Dataset, technologies[["fixed_outputs"]])
208-
technologies.fixed_outputs[{"commodity": envs}] = fout = 1.5
209-
technologies.fixed_outputs[{"commodity": ~envs}] = 2
211+
envs = is_pollutant(_technologies.comm_usage)
212+
_technologies = cast(xr.Dataset, _technologies[["fixed_outputs"]])
213+
_technologies.fixed_outputs[{"commodity": envs}] = fout = 1.5
214+
_technologies.fixed_outputs[{"commodity": ~envs}] = 2
210215

211-
enduses = is_enduse(technologies.comm_usage.sel(commodity=production.commodity))
216+
enduses = is_enduse(_technologies.comm_usage.sel(commodity=production.commodity))
212217
production[{"commodity": enduses}] = prod = 0.5
213218
production[{"commodity": ~enduses}] = 5
214219

215-
em = emission(production, technologies)
220+
em = emission(production, _technologies)
216221
assert em.commodity.isin(envs.commodity).all()
217222
assert em.values == approx(fout * enduses.sum().values * prod)
218223

219224

220-
def test_min_production(technologies, capacity, timeslice):
225+
def test_min_production(_technologies, capacity, timeslice):
221226
"""Test minimum production quantity."""
222227
from muse.quantities import maximum_production, minimum_production
223228

224229
# If no minimum service factor is defined, the minimum production is zero
225-
assert "minimum_service_factor" not in technologies
226-
production = minimum_production(technologies, capacity)
230+
assert "minimum_service_factor" not in _technologies
231+
production = minimum_production(_technologies, capacity)
227232
assert (production == 0).all()
228233

229234
# If minimum service factor is defined, then the minimum production is not zero
230235
# and it is less than the maximum production
231-
technologies["minimum_service_factor"] = 0.5
232-
production = minimum_production(technologies, capacity)
236+
_technologies["minimum_service_factor"] = 0.5
237+
production = minimum_production(_technologies, capacity)
233238
assert not (production == 0).all()
234-
assert (production <= maximum_production(technologies, capacity)).all()
239+
assert (production <= maximum_production(_technologies, capacity)).all()
235240

236241

237-
def test_supply_capped_by_min_service(technologies, capacity, timeslice):
242+
def test_supply_capped_by_min_service(_technologies, capacity, timeslice):
238243
"""Test supply is capped by the minimum service."""
239244
from muse.commodities import CommodityUsage
240245
from muse.quantities import minimum_production, supply
241246

242-
technologies["minimum_service_factor"] = 0.3
243-
minprod = minimum_production(technologies, capacity)
247+
_technologies["minimum_service_factor"] = 0.3
248+
minprod = minimum_production(_technologies, capacity)
244249

245250
# If minimum service factor is defined, then the minimum production is not zero
246251
assert not (minprod == 0).all()
247252

248253
# And even if the demand is smaller than the minimum production, the supply
249254
# should be equal to the minimum production
250255
demand = minprod / 2
251-
spl = supply(capacity, demand, technologies)
256+
spl = supply(capacity, demand, _technologies)
252257
spl = spl.sel(commodity=spl.comm_usage == CommodityUsage.PRODUCT).sum(
253258
["year", "asset"]
254259
)
@@ -259,8 +264,8 @@ def test_supply_capped_by_min_service(technologies, capacity, timeslice):
259264

260265
# But if there is not minimum service factor, the supply should be equal to the
261266
# demand and should not be capped by the minimum production
262-
del technologies["minimum_service_factor"]
263-
spl = supply(capacity, demand, technologies)
267+
del _technologies["minimum_service_factor"]
268+
spl = supply(capacity, demand, _technologies)
264269
spl = spl.sel(commodity=spl.comm_usage == CommodityUsage.PRODUCT).sum(
265270
["year", "asset"]
266271
)
@@ -271,8 +276,8 @@ def test_supply_capped_by_min_service(technologies, capacity, timeslice):
271276
assert (spl <= minprod).all()
272277

273278

274-
def test_production_amplitude(production, technologies):
279+
def test_production_amplitude(production, _technologies):
275280
from muse.quantities import production_amplitude
276281

277-
result = production_amplitude(production, technologies)
282+
result = production_amplitude(production, _technologies)
278283
assert set(result.dims) == set(production.dims) - {"commodity"}

0 commit comments

Comments
 (0)