-
Notifications
You must be signed in to change notification settings - Fork 27
Standardized Performance Outputs: Generic Storage & Control #493
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
a862e69
bc6a798
73edcd0
d3d5616
3a8ad35
682959e
0aef02a
8f993d4
a660e21
1f9788a
d1f2387
5db62ae
f515f5a
4227c13
ff3de30
d84c10a
1169376
40ae090
82116bc
08f94ef
7da471c
8df11d0
5a38e8f
1a51e7b
3ea3568
d02ec1b
e868d82
16a44c5
d8fda02
c32e752
910a374
cca9e4b
b899fae
e570263
ee220e5
1b64406
496fba0
8117034
6c7790c
1d0b1ea
f582c4c
71a1b85
be535a3
b54f32f
8349163
199c6fd
d0ac7d8
95fe559
9503a1b
d98ed88
022f6ca
11d45b2
01f4124
9c8dd0e
1011686
36c0772
632d028
6c9e6ba
61dddc7
65b1450
f36cb4d
9a770fd
ed4988c
0cca96d
5d0832a
0a83daf
35ab37d
16b54d8
6bfdb07
9976fcc
2ac03d2
6221d3e
fe991ae
cbf40b5
4804f8b
48a20e4
f80224f
078874c
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 | ||||
|---|---|---|---|---|---|---|
| @@ -1,7 +1,7 @@ | ||||||
| import openmdao.api as om | ||||||
| from attrs import field, define | ||||||
|
|
||||||
| from h2integrate.core.utilities import BaseConfig, merge_shared_inputs | ||||||
| from h2integrate.core.model_baseclasses import PerformanceModelBaseClass | ||||||
|
|
||||||
|
|
||||||
| @define(kw_only=True) | ||||||
|
|
@@ -10,26 +10,49 @@ class SimpleGenericStorageConfig(BaseConfig): | |||||
| commodity_units: str = field() # TODO: update to commodity_rate_units | ||||||
|
|
||||||
|
|
||||||
| class SimpleGenericStorage(om.ExplicitComponent): | ||||||
| """ | ||||||
| Simple generic storage model. | ||||||
| class SimpleGenericStorage(PerformanceModelBaseClass): | ||||||
| """ | ||||||
| Simple generic storage model that acts as a pass-through component. | ||||||
|
|
||||||
| Note: this storage performance model is intended to be used with the | ||||||
| `DemandOpenLoopStorageController` controller and has not been tested | ||||||
| with other controllers. | ||||||
|
|
||||||
| def initialize(self): | ||||||
| self.options.declare("tech_config", types=dict) | ||||||
| self.options.declare("plant_config", types=dict) | ||||||
| self.options.declare("driver_config", types=dict) | ||||||
| """ | ||||||
|
|
||||||
| def setup(self): | ||||||
| n_timesteps = self.options["plant_config"]["plant"]["simulation"]["n_timesteps"] | ||||||
| self.config = SimpleGenericStorageConfig.from_dict( | ||||||
| merge_shared_inputs(self.options["tech_config"]["model_inputs"], "performance"), | ||||||
| strict=False, | ||||||
| additional_cls_name=self.__class__.__name__, | ||||||
| ) | ||||||
| commodity_name = self.config.commodity_name | ||||||
| commodity_units = self.config.commodity_units | ||||||
| self.add_input(f"{commodity_name}_in", val=0.0, shape=n_timesteps, units=commodity_units) | ||||||
| self.commodity = self.config.commodity_name | ||||||
| self.commodity_rate_units = self.config.commodity_units | ||||||
| self.commodity_amount_units = f"({self.commodity_rate_units})*h" | ||||||
|
Comment on lines
+29
to
+31
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. Move to
Collaborator
Author
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. the tech config is not accessbile at the initialize(). I get this error: |
||||||
| super().setup() | ||||||
| self.add_input( | ||||||
| f"{self.commodity}_set_point", | ||||||
| val=0.0, | ||||||
| shape=self.n_timesteps, | ||||||
| units=self.commodity_rate_units, | ||||||
| ) | ||||||
|
|
||||||
| def compute(self, inputs, outputs): | ||||||
| pass | ||||||
| # Pass the commodity_out as the commodity_set_point | ||||||
| outputs[f"{self.commodity}_out"] = inputs[f"{self.commodity}_set_point"] | ||||||
|
|
||||||
| # Estimate the rated commodity production as the maximum value from the commodity_set_point | ||||||
| outputs[f"rated_{self.commodity}_production"] = inputs[f"{self.commodity}_set_point"].max() | ||||||
|
Comment on lines
+44
to
+45
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. Is this the best way of getting the rated production? If the set point never reaches the actual maximum of the system, is this a problem and could lead to confusing metrics? Legitimately asking, mostly about the generality of this and if it works well in all cases.
Collaborator
Author
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. Idk a better way - but this is just a pass-through component. In this case, it'd be better for this to be done in the
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 don't think we should determine rated production this way. I think both the storage and the controller need to require the technology rated production like we do with other systems. For storage, the charge and discharge rates and the rated storage capacity should be given at a minimum I think now that we are using the
Collaborator
Author
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. Does this mean you want the |
||||||
|
|
||||||
| # Calculate the total and annual commodity produced | ||||||
| outputs[f"total_{self.commodity}_produced"] = outputs[f"{self.commodity}_out"].sum() | ||||||
| outputs[f"annual_{self.commodity}_produced"] = outputs[ | ||||||
| f"total_{self.commodity}_produced" | ||||||
| ] * (1 / self.fraction_of_year_simulated) | ||||||
|
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 just dividing will be more clear
Suggested change
|
||||||
|
|
||||||
| # Calculate the maximum commodity production over the simulation | ||||||
| max_production = ( | ||||||
| inputs[f"{self.commodity}_set_point"].max() * self.n_timesteps * (self.dt / 3600) | ||||||
| ) | ||||||
|
Comment on lines
+54
to
+56
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. We should calculate the maximum theoretical production based on name-plate capacity based on the user inputs (see comment above), not the actual in the given scenario as is being done here.
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. Also see comment below |
||||||
|
|
||||||
| outputs["capacity_factor"] = outputs[f"total_{self.commodity}_produced"] / max_production | ||||||
|
Comment on lines
+47
to
+58
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. Tagging @jaredthomas68 and @genevievestarke for special attention to this math to make sure it matches what you expect.
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. The capacity factor should be based on rated production in the denominator, not max production. Rated production should be a user input to the system (see previous comment).
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. Capacity factors for storage systems (at least batteries), as far as I understand, are typically based on the nameplate discharge rate of the system. cp = total_product_discharged_in_time_period / (name_plate_discharge_rate*time_period_duration) # of course make sure units make senseOf course, the above leads to some fairly low capacity factors, like about 0.167 for a 4 hour battery discharged daily as discussed in the ATB
Collaborator
Author
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. @jaredthomas68 - the |
||||||
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.
I don't think the test values should change for these code changes. Maybe you can convince me though. As I see it all the changes should be more a matter of where calculations are done and what components connect to what rather than changing any actual calculations.