-
Notifications
You must be signed in to change notification settings - Fork 38
Headroom/reserve rate connections #755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
f5d64e1
25372bf
fc9a33b
05d904b
f612216
9c3fcee
4d1edc1
b48d30d
313dbae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -100,10 +100,26 @@ def setup(self): | |
| "electricity_sold", | ||
| val=0.0, | ||
| shape=n_timesteps, | ||
| units="kW", | ||
| units=self.commodity_rate_units, | ||
| desc="Electricity sold to the grid", | ||
| ) | ||
|
|
||
| self.add_output( | ||
| "electricity_headroom_sold", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this naming convention is a little confusing. the current name makes it sound like this is electricity that has already been sold. Maybe
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
| val=0.0, | ||
| shape=n_timesteps, | ||
| units=self.commodity_rate_units, | ||
| desc="Reserve capacity that could be sold to the grid", | ||
| ) | ||
|
|
||
| self.add_output( | ||
| "electricity_headroom_out", | ||
| val=0.0, | ||
| shape=n_timesteps, | ||
| units=self.commodity_rate_units, | ||
| desc="Reserve capacity that could be bought from the grid", | ||
| ) | ||
|
|
||
| self.add_output( | ||
| "electricity_unmet_demand", | ||
| val=0.0, | ||
|
|
@@ -148,6 +164,8 @@ def compute(self, inputs, outputs): | |
| max_production = ( | ||
| inputs["interconnection_size"] * len(outputs["electricity_out"]) * (self.dt / 3600) | ||
| ) | ||
| outputs["electricity_headroom_sold"] = interconnection_size - electricity_sold | ||
| outputs["electricity_headroom_out"] = interconnection_size - electricity_bought | ||
| outputs["rated_electricity_production"] = inputs["interconnection_size"] | ||
| outputs["total_electricity_produced"] = np.sum(outputs["electricity_out"]) * ( | ||
| self.dt / 3600 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,6 +118,21 @@ def test_grid_performance_outputs(plant_config, subtests): | |
| with subtests.test(f"{commodity}_out length"): | ||
| assert len(prob.get_val(f"comp.{commodity}_out", units=commodity_rate_units)) == n_timesteps | ||
|
|
||
| # Test that interconnect output headroom is greater than zero (plant oversized) and less than the rating | ||
| with subtests.test(f"0 < {commodity}_headroom_out < rated_{commodity}_production"): | ||
| assert np.all(prob.get_val(f"comp.{commodity}_headroom_out", units="MW") >= 0) | ||
| assert np.all( | ||
| prob.get_val(f"comp.{commodity}_headroom_out", units="MW") | ||
| <= prob.get_val(f"comp.rated_{commodity}_production", units="MW") | ||
| ) | ||
|
|
||
| # Test that interconnect sales headroom is at the rating (nothing fed to grid) | ||
| with subtests.test(f"{commodity}_headroom_sold == rated_{commodity}_production"): | ||
| assert np.all( | ||
| prob.get_val(f"comp.{commodity}_headroom_sold", units="MW") | ||
| == prob.get_val(f"comp.rated_{commodity}_production", units="MW") | ||
| ) | ||
|
|
||
| # Test default values | ||
| with subtests.test("operational_life default value"): | ||
| assert prob.get_val("comp.operational_life", units="yr") == plant_life | ||
|
|
@@ -220,6 +235,13 @@ def test_selling_electricity(plant_config, n_timesteps): | |
| actual_in = prob.get_val("grid.electricity_in") | ||
| np.testing.assert_array_almost_equal(actual_in, electricity_in) | ||
|
|
||
| # The headroom should be the difference between electricity_in and the rating | ||
| headroom = prob.get_val("grid.electricity_headroom_sold") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think headroom is a good name. Could refer to https://docs.nrel.gov/docs/fy19osti/73590.pdf for discussion of the different forms headroom might take. |
||
| np.testing.assert_array_almost_equal( | ||
| headroom, | ||
| tech_config["model_inputs"]["shared_parameters"]["interconnection_size"] - electricity_in, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.unit | ||
| @pytest.mark.parametrize("n_timesteps", [10]) | ||
|
|
@@ -281,6 +303,13 @@ def test_varying_demand_profile(plant_config, n_timesteps): | |
| expected = np.clip(demand, 0, 100000) | ||
| np.testing.assert_array_almost_equal(electricity_out, expected) | ||
|
|
||
| # The output headroom should be the difference between electricity_out and the rating | ||
| headroom = prob.get_val("grid.electricity_headroom_out") | ||
| np.testing.assert_array_almost_equal( | ||
| headroom, | ||
| tech_config["model_inputs"]["shared_parameters"]["interconnection_size"] - electricity_out, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.unit | ||
| @pytest.mark.parametrize("n_timesteps", [10]) | ||
|
|
@@ -413,6 +442,10 @@ def test_grid_integration_dt_1800(subtests, tmp_path): | |
| annual_energy = h2i.prob.get_val("grid.annual_electricity_produced", units="kW*h/year") | ||
| assert annual_energy == pytest.approx(expected_annual) | ||
|
|
||
| with subtests.test("headroom calculation accurately represents rating less demand"): | ||
| headroom = h2i.prob.get_val("grid.electricity_headroom_out", units="kW") | ||
| assert headroom == pytest.approx(tech_config["technologies"]["grid"]["model_inputs"]["shared_parameters"]["interconnection_size"] - demand) | ||
|
|
||
|
|
||
| @pytest.mark.unit | ||
| @pytest.mark.parametrize("n_timesteps", [8760]) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The units of
electricity_soldneeds to be kW because it is interconnected with sell price in the cost model and comes from the same input in the config. That said, you could add the unit conversion in the cost model to make it work.