Reorganize component name and type#216
Reorganize component name and type#216genevievestarke merged 43 commits intoNatLabRockies:developfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR addresses Issue #207 by formalizing the distinction between component_name (user-chosen unique YAML key), component_type (class selector), and a new component_category (broad grouping used for generator/battery behavior). It updates HybridPlant and input loading to support multiple instances of the same component type while preserving existing single-instance YAML conventions.
Changes:
- Introduces
component_categoryon component classes and auto-derivescomponent_typefrom the concrete Python class name inComponentBase. - Updates
HybridPlantandload_hercules_inputto discover components by presence of acomponent_typefield, enabling multi-instance components. - Updates tests and documentation to pass
component_nameinto component constructors and to document the new naming/type/category model.
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/wind_farm_scada_power_test.py | Updates WindFarmSCADAPower tests to pass component_name into constructor. |
| tests/wind_farm_precom_floris_test.py | Updates WindFarm tests to pass component_name into constructor. |
| tests/wind_farm_dynamic_floris_test.py | Updates WindFarm dynamic-mode tests for new constructor signature. |
| tests/wind_farm_direct_test.py | Updates WindFarm direct-mode tests for new constructor signature. |
| tests/utilities_test.py | Aligns expected loader error message for invalid component_type. |
| tests/thermal_component_base_test.py | Updates ThermalComponentBase tests for new constructor signature. |
| tests/solar_pysam_pvwatts_test.py | Updates SolarPySAMPVWatts tests for new constructor signature. |
| tests/regression_tests/solar_pysam_pvwatts_regression_test.py | Updates regression test to pass component_name. |
| tests/regression_tests/electrolyzer_plant_regression_test.py | Updates regression test to pass component_name. |
| tests/regression_tests/battery_regression_test.py | Updates regression tests to pass component_name. |
| tests/open_cycle_gas_turbine_test.py | Updates OpenCycleGasTurbine tests for new constructor signature. |
| tests/hybrid_plant_test.py | Adds tests for component_category, auto-set component_type, and multi-instance batteries. |
| tests/electrolyzer_plant_test.py | Updates ElectrolyzerPlant tests for new constructor signature. |
| tests/battery_simple_test.py | Updates battery tests for new constructor signature. |
| hercules/utilities.py | Simplifies component validation and discovers components by component_type; adds a global valid-type list. |
| hercules/plant_components/wind_farm_scada_power.py | Adds component_category and requires component_name argument. |
| hercules/plant_components/wind_farm.py | Adds component_category and requires component_name argument; uses name param early for config read. |
| hercules/plant_components/thermal_component_base.py | Switches from fixed name/type to component_category + component_name param. |
| hercules/plant_components/solar_pysam_pvwatts.py | Requires component_name and relies on base to set component_type. |
| hercules/plant_components/solar_pysam_base.py | Adds component_category and replaces hard-coded h_dict keys with self.component_name in metadata. |
| hercules/plant_components/open_cycle_gas_turbine.py | Requires component_name and removes fixed name/type class attributes. |
| hercules/plant_components/electrolyzer_plant.py | Adds component_category and requires component_name argument. |
| hercules/plant_components/component_base.py | Enforces component_category on subclasses and auto-derives component_type. |
| hercules/plant_components/battery_simple.py | Adds component_category and requires component_name argument (no hard-coded name/type). |
| hercules/plant_components/battery_lithium_ion.py | Adds component_category and requires component_name argument (no hard-coded name/type). |
| hercules/hybrid_plant.py | Adds a component registry; discovers components by component_type; generator classification via component_category; battery sign logic via category. |
| docs/solar_pv.md | Updates YAML instructions to reflect component_name vs component_type and links to new doc. |
| docs/open_cycle_gas_turbine.md | Documents YAML usage with user-chosen component_name and component_type. |
| docs/hybrid_plant.md | Updates component discovery explanation and adds type/category/generator classification table. |
| docs/hercules_input.md | Updates input structure docs to allow arbitrary component section keys with component_type. |
| docs/h_dict.md | Updates h_dict docs to reflect auto-discovery and adds OCGT section. |
| docs/electrolyzer.md | Updates YAML instructions to reflect component_name vs component_type. |
| docs/component_types.md | Adds new reference doc defining component_name, component_type, and component_category. |
| docs/battery.md | Clarifies YAML component_name convention for batteries in docs. |
| docs/_toc.yml | Adds component_types page to documentation TOC. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| self.wake_method = wake_method | ||
|
|
||
| # Store the type of this component (for backward compatibility) | ||
| component_type = h_dict[self.component_name].get("component_type", "WindFarm") | ||
| self.component_type = component_type | ||
|
|
||
| # Call the base class init | ||
| super().__init__(h_dict, self.component_name) | ||
| # Call the base class init (sets self.component_name and self.component_type) | ||
| super().__init__(h_dict, component_name) |
There was a problem hiding this comment.
WindFarm now accepts an explicit component_name for multi-instance support, but get_initial_conditions_and_meta_data() still writes to h_dict["wind_farm"] (hard-coded key). That will break/clobber metadata when the instance name is not exactly "wind_farm". Please switch those updates to use h_dict[self.component_name] instead of the literal key (and similarly for the optional starttime_utc field).
There was a problem hiding this comment.
@paulf81 is this and the similar one below now resolved?
| """ | ||
| # Store the name of this component | ||
| self.component_name = "wind_farm" | ||
|
|
||
| self.component_type = "WindFarmSCADAPower" | ||
|
|
||
| # Call the base class init | ||
| super().__init__(h_dict, self.component_name) | ||
| # Call the base class init (sets self.component_name and self.component_type) | ||
| super().__init__(h_dict, component_name) |
There was a problem hiding this comment.
WindFarmSCADAPower now supports a user-chosen component_name, but get_initial_conditions_and_meta_data() still writes to h_dict["wind_farm"] (hard-coded key). This breaks multi-instance support (e.g., "wind_farm_2") and can overwrite another component’s metadata. Update those assignments to use h_dict[self.component_name] consistently.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 36 out of 36 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
I'll need to check whether this affects Hycon. At this stage, supervisory controllers in Hycon can only accept a single of each type of component (e.g. a single wind farm). Updating that will be a bit more complicated, but for now I'll just plan to see what (if any) updates I'll need to make in Hycon for this update. |
Thanks @misi9170, maybe it's hopeful to point out these changes didn't necessitate any changes to the examples since "wind_farm" is still for example an acceptable name |
|
@paulf81 , thanks, and you're right, looks like Hycon is indeed unaffected :) |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 36 out of 36 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
misi9170
left a comment
There was a problem hiding this comment.
Overall this looks good to me. I've made a couple of documentation suggestions for you to conisder @paulf81 , and I will take a look at why github is treating the two battery files as having completely changed and see if I can fix that.
| if cls is None: | ||
| raise ValueError( | ||
| f"Unknown component_type '{component_type}' for component '{component_name}'. " | ||
| f"Available types: {sorted(COMPONENT_REGISTRY)}" |
There was a problem hiding this comment.
Perhaps here we could also say something like: if you are adding a new component, please update the COMPONENT_REGISTRY. But maybe that's too much information for someone that, 9 times out of 10, just has a typo in their input file.
There was a problem hiding this comment.
I guess this error only gets raised if users don't use load_hercules_input() to create the initial h_dict. That's enough of an edge case that we probably don't need to update this error.
…nto feature/clean_up_type
|
@paulf81 I think I've now fixed the battery file diffs. Can you just take a quick look over them and make sure I didn't miss any changes needed (tests all pass, and were helpful because there were some differences I'd missed!). I'm still not sure what happened there, so there's a chance it happens again. Hopefully not. |
It looks good to me @misi9170 ! Checked the two battery files and they matched my expectations |
| - [hybrid_plant.md](hybrid_plant.md) — Available Components table | ||
| - [component_types.md](component_types.md) — Complete Component Type Reference table | ||
|
|
||
| ## Testing |
There was a problem hiding this comment.
This is personal preference, but I usually put testing in with developing the model. Thoughts on moving it up?
| pytest tests/my_component_test.py -v | ||
| ``` | ||
|
|
||
| ## Summary Checklist |
There was a problem hiding this comment.
This is a great summary!!
This pull request is meant to address Issue #207. The main points are to:
component_nameandcomponent_typeThis PR accomplished this via:
component_nameto be a unique identifier that can be any string. It's backwards-compatible since it is still defined as the YAML top level key and the names for example in the examples likewind_farmstill work since there is only one thing calledwind_farmin those examplescomponent_typeto represent the specific class use to define the unit, however making that more explicit by automatically defining the type in theComponentBaseusing the__name__methodcomponent_categorythat captures what thecomponent_nameused to represent. These are class attributes defined at least in each base class and then optionally in subclassesThen also the following organization steps are taken:
component_nameto be passed in. This is a smallish change handled mainly byHybridPlantbut all tests of the components themselves need this added.HybridPlantupdated to use these more explicit variablesutilities.pysimplified since many of the types/classes are kept withinHybridPlant