Skip to content

Conversation

@elenya-grant
Copy link
Collaborator

@elenya-grant elenya-grant commented Jan 2, 2026

Enable multiple site functionality

This PR introduces a way to define multiple sites. This is a first-pass at enabling this functionality at a high-level. This PR is intended to lay some preliminary groundwork for further detailed work with site definitions, such as having certain technologies at specific sites (this is not part of this PR). This implementation is intended to be able to handle existing site definitions while being able to handle multiple site definitions. The use-cases this enables are being able to run a design sweep with wind and solar resource at different locations. Before, site locations could only be swept if wind and solar resource were for the same latitude/longitude.

Multiple sites can be defined using "sites" rather than "site" in the plant_config.yaml. Each site is expected to define latitude, longitude, and resources (if needed) for that site:

sites:
  site_A:
    latitude: 34.22
    longitude: -102.75
    resources: # resources for this site
      wind_resource:
        resource_model: "wind_toolkit_v2_api"
        resource_parameters:
          resource_year: 2012

Each site group (om.Group()) has a site subsystem (which is defined by latitude and longitude at the moment) and subsystems for each resource defined in resources for that site.

Remaining Question for Reviewers (No longer applicable, going with Option 1)

Currently, resource_to_tech_connections now requires the site name to be specified as a 3 element list in this format:

resource_to_tech_connections: [
  ['site_A.wind_resource', 'wind', 'wind_resource_data'],
 # formatted as:
 # [site_name.resource_name, tech_name, variable_name]
]

An alternative approach would be to use a 4 element list in this format:

resource_to_tech_connections: [
  ['site_A', 'wind_resource', 'wind', 'wind_resource_data'],
 # formatted as:
 # [site_name, resource_name, tech_name, variable_name]
]

What do y'all like more? 3-element list or 4-element list?

Section 1: Type of Contribution

  • Feature Enhancement
    • Framework
    • New Model
    • Updated Model
    • Tools/Utilities
    • Other (please describe):
  • Bug Fix
  • Documentation Update
  • CI Changes
  • Other (please describe):

Section 2: Draft PR Checklist

  • Open draft PR
  • Describe the feature that will be added
  • Fill out TODO list steps
  • Describe requested feedback from reviewers on draft PR
  • Complete Section 7: New Model Checklist (if applicable)

TODO:

  • Add doc page
  • Add docstrings and inline comments to updated logic in H2IntegrateModel methods
  • Add test in test_all_examples.py for new example
  • Add example and/or test for defining wind resource at multiple sites, add notes on documentation about any constraints on naming (if found). Check if we can define 2 sites (each with wind resource) and connect those to separate wind farms (defined by two wind farm technologies included in the tech config). (DONE - expanded example 26)

Type of Reviewer Feedback Requested (on Draft PR)

Structural feedback:

  • thoughts on the addition of site component models in h2integrate/core/sites.py?

Implementation feedback:

Other feedback:

  • general thoughts on alternative methods for defining a site?

Section 3: General PR Checklist

  • PR description thoroughly describes the new feature, bug fix, etc.
  • Added tests for new functionality or bug fixes
  • Tests pass (If not, and this is expected, please elaborate in the Section 6: Test Results)
  • Documentation
    • Docstrings are up-to-date
    • Related docs/ files are up-to-date, or added when necessary
    • Documentation has been rebuilt successfully
    • Examples have been updated (if applicable)
  • CHANGELOG.md has been updated to describe the changes made in this PR

Section 3: Related Issues

Section 4: Impacted Areas of the Software

Section 4.1: New Files

  • docs/user_guide/defining_sites_and_resources.md: doc page for defining sites and resource_to_tech_connections
  • h2intergate/core/sites.py: new file containing site components and configs
    • SiteLocationComponentConfig: configuration class for SiteLocationComponent
    • SiteLocationComponent: site location component with outputs of latitude, longitude, and elevation
    • SiteBaseConfig: Base configuration class for site models
    • SiteBaseComponent: Base om.IndepVarComp component class for site components
  • examples/xx_site_doe_diff/*: new example that runs a design of experiments with wind and solar resource at different locations

Section 4.2: Modified Files

  • h2integrate/core/h2integrate_model.py
    - connect_technologies(): updated logic related to resource_to_tech_connections. The two main updates are:
    - updated initialization of resource_models variable to handle sites site definition. This variable is used to throw errors in case theres an extra or missing resource to technology connection
    - the actual connection between resource data and technology model has logic to handle multiple sites.
    - create_site_model(): new method that creates multiple site groups
    - create_site_group(): new method heavily based on the functionality in the previous create_site_model() method, but includes inputs. This is called within the new create_site_model() method.
  • h2integrate/core/inputs/plant_schema.yaml: removed "site" as a required key, added "sites" as a required key.
  • examples/26_floris: expanded example to check that no errors arise with having multiple sites output wind_resource_data.
    • plant_config.yaml: added another site for another wind plant. Added technology_interconnections, and added more finance subgroups
    • tech_config.yaml: added another wind farm model using PySAM (utility wind plant), renamed floris wind plant tech to "distributed_wind_plant" and added a combiner.

Tests (new and modified):

  • examples/test/test_all_examples.py::test_floris_example: updated for expanded example. Added subtests to check that latitude and longitude for each wind site is as expected
  • examples/test/test_all_examples.py::test_sweeping_different_resource_sites_doe: new integration test for added example
  • h2integrate/converters/iron/test/test_iron_transport.py: updated fixture for plant_config to be compatible with changes to iron transport model.
  • h2integrate/preprocess/test/test_wind_turbine_file_tools.py: updated to reformat plant config for creating wind resource models.

Other modified files:

  • docs/user_guide/how_to_set_up_an_analysis.ipynb: removed year from plant config. Added updates to what information is provided under 'plant' section of the plant config.
  • examples/15_wind_solar_electrolyzer/plant_config.yaml: updated example 15 to show usage of defining multiple sites
  • h2integrate/converters/iron/iron_transport.py: had to update way of accessing latitude and longitude from plant config, this was a temporary fix (requires that the site name is 'site') and should be updated so latitude and longitude are an input to the transport model. This update would be part of a separate PR that defines a framework for connecting sites to technologies (rather than resources from a site to technologies).

Other Modified Example Files:

  • all example plant_config.yaml files were reformatted to be compatible with new framework.
  • plant configs with "year" under "site":
    • examples/16_natural_gas/plant_config.yaml
    • examples/20_solar_electrolyzer_doe/plant_config.yaml
    • examples/22_site_doe/plant_config.yaml
    • examples/24_solar_battery_grid/plant_config.yaml
  • plant configs with unused "boundaries" input:
    • examples/17_splitter_wind_doc_h2/plant_config.yaml
    • examples/21_iron_mn_to_il/plant_config.yaml
    • examples/21_iron_mn_to_il/plant_config_old.yaml
    • examples/25_sizing_modes/plant_config.yaml
    • examples/23_solar_wind_ng_demand/plant_config.yaml

Section 5: Additional Supporting Information

Suppose we want to run a single simulation where wind resource data is pulled for the location (30.6617, -101.7096) and solar resource data is pulled for the location (35.2018863, -101.945027). There are two ways this could be done:

Option 1: using a single site:

sites:
  site:
    latitude: 30.6617
    longitude: -101.7096
    resources:
      wind_resource:
        resource_model: "wind_toolkit_v2_api"
        resource_parameters:
          resource_year: 2012
          use_fixed_resource_location: True # this is the default
      solar_resource:
        resource_model: "goes_aggregated_solar_v4_api"
        resource_parameters:
          resource_year: 2012
          latitude: 35.2018863
          longitude: -101.945027
          use_fixed_resource_location: True # this is the default
resource_to_tech_connections: [
  ['site.wind_resource', 'wind', 'wind_resource_data'], # Note how we have to specify the site
  ['site.solar_resource', 'solar', 'solar_resource_data'],
]

Option 2: using multiple sites or running design of experiments:

sites:
  wind_site:
    latitude: 30.6617
    longitude: -101.7096
    resources:
      wind_resource:
        resource_model: "wind_toolkit_v2_api"
        resource_parameters:
          resource_year: 2012
          use_fixed_resource_location: False # lat/lon will be input from the 'wind_site'
  solar_site:
    latitude: 35.2018863
    longitude: -101.945027
    resources:
      solar_resource:
        resource_model: "goes_aggregated_solar_v4_api"
        resource_parameters:
          resource_year: 2012
          use_fixed_resource_location: False # lat/lon will be input from the 'solar_site'
resource_to_tech_connections: [
  ['wind_site.wind_resource', 'wind', 'wind_resource_data'], # Note how we have to specify the site
  ['solar_site.solar_resource', 'solar', 'solar_resource_data'],
]

Section 6: Test Results, if applicable

Section 7 (Optional): New Model Checklist

  • Model Structure:
    • Follows established naming conventions outlined in docs/developer_guide/coding_guidelines.md
    • Used attrs class to define the Config to load in attributes for the model
      • [-] If applicable: inherit from BaseConfig or CostModelBaseConfig
    • [-] Added: initialize() method, setup() method, compute() method
      • [-] If applicable: inherit from CostModelBaseClass
  • Integration: Model has been properly integrated into H2Integrate
    • Added to supported_models.py
    • [-] If a new commodity_type is added, update create_financial_model in h2integrate_model.py
  • Tests: Unit tests have been added for the new model
    • Pytest-style unit tests
    • Unit tests are in a "test" folder within the folder a new model was added to
    • If applicable add integration tests
  • Example: If applicable, a working example demonstrating the new model has been created
    • Input file comments
    • Run file comments
    • Example has been tested and runs successfully in test_all_examples.py
  • Documentation:
    • Write docstrings using the Google style
    • [-] Model added to the main models list in docs/user_guide/model_overview.md
      • Model documentation page added to the appropriate docs/ section
      • <model_name>.md is added to the _toc.yml

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@elenya-grant elenya-grant added the ready for review This PR is ready for input from folks label Jan 2, 2026
@elenya-grant elenya-grant added enhancement New feature or request framework labels Jan 2, 2026
@elenya-grant elenya-grant marked this pull request as ready for review January 6, 2026 20:53
Copy link
Collaborator

@johnjasa johnjasa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for being on the ball and putting this together! It adds some neat capability that we'll obviously need for multiple projects, and I love that you've both modified existing examples and added a new example to really showcase everything.

I put mostly high-level comments and questions throughout. Happy to chat through them if you'd find it useful, but I'm hoping others can see my thoughts typed out here and benefit from them as they review this PR too.

Comment on lines 4 to 26
site_groups:
solar_site:
site_model: "location"
site_parameters:
latitude: 30.6617
longitude: -101.7096
resources:
solar_resource:
resource_model: "goes_aggregated_solar_v4_api"
resource_parameters:
resource_year: 2013
resource_dir: "../11_hybrid_energy_plant/tech_inputs/weather/solar"
resource_filename: "30.6617_-101.7096_psmv3_60_2013.csv"
wind_site:
site_model: "location"
site_parameters:
latitude: 35.2018863
longitude: -101.945027
resources:
wind_resource:
resource_model: "wind_toolkit_v2_api"
resource_parameters:
resource_year: 2012
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating this example (that already uses disparate locations) for the new site setup!

resource_filename: "30.6617_-101.7096_psmv3_60_2013.csv"
site_groups:
solar_site:
site_model: "location"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What other values are you envisioning being allowed for site_model? is it always "location"? or is it a nickname for a location?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"location" or "boundary" are the current options, they're in supported_models

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why they need to be different models. Could we just make boundaries optional and allow techs that need boundaries to throw an error if no boundaries are provided?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the idea of "site_model" since we only have one site component model now.

Comment on lines +1 to +9
wind_site.wind_resource.latitude,wind_site.wind_resource.longitude,solar_site.solar_resource.latitude,solar_site.solar_resource.longitude,solar.system_capacity_DC
35.2018863,-101.945027,35.2018863,-101.945027,25000.0
35.2018863,-101.945027,35.2018863,-101.945027,50000.0
35.2018863,-101.945027,34.22,-102.75,25000.0
35.2018863,-101.945027,34.22,-102.75,50000.0
39.22629,-82.938,35.2018863,-101.945027,25000.0
39.22629,-82.938,35.2018863,-101.945027,50000.0
39.22629,-82.938,34.22,-102.75,25000.0
39.22629,-82.938,34.22,-102.75,50000.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I'm wrong: all of these sites required API access to download weather information because we don't have the data saved in the repo. That means that this example can't be easily tested via CI as we don't have the API key included. Is that right? If so, do you have another way to test multiple sites in an integration test?

I know you didn't tackle this for this stage of the PR because you're looking for high-level feedback, but I'm commenting this now mostly so I don't forget!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for catching that! I can update the locations and add in a resource file or two if needed so that it can run with the CI tests. I am not sure whether I have a unit test or integration test yet - I'll be sure to add those in though!

Comment on lines 7 to 9
site_parameters:
latitude: 34.22
longitude: -102.75
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have latitude and longitude live at the same level as site_model and get rid of the site_parameters nesting? Do you expect other values to go into that site_parameters level later that aren't represented here? I ask this because we could remove some nesting this way and it more closely matches the case when there's one site and lat and lon are at that top level alongside resources

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to format the site things similar to the finance groups and the technology definitions. We define a model and the the model_inputs but - I suppose that's not fully necessary. I think that it maybe does make sense to have latitude and longitude live at the same level as site_model and to remove site parameters/

Comment on lines 322 to 323
if "site_groups" not in self.plant_config:
if "site" in self.plant_config:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not in love with the ability to have either site or site_groups at the top level, mostly because it leads to logic like this and the fact that the user has two potential entry points. I do love that you've been thinking about backwards compatibility and made sure the other way works too.

That being said, do you see the ability to have site_groups and site available remaining for all future versions of H2I? Is there a world where we only allow users to define sites as plural and the singular case is a subset of that?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could also just be site and check for lat and lon in the next level of nesting and if not there then we know it's multiple sites.

Copy link
Collaborator Author

@elenya-grant elenya-grant Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - either of these options seem good to me!

If we choose one way of defining site or site_groups - then should we also require that the site is specified in resource_to_tech_connections if theres only one site? Prior to this PR - the resource_to_tech_connections looked like this:

resource_to_tech_connections: [
  ['wind_resource', 'wind', 'wind_resource_data'],
  ['solar_resource', 'solar', 'solar_resource_data'],
]

but - should we require it to be?

resource_to_tech_connections: [
  ['site.wind_resource', 'wind', 'wind_resource_data'],
  ['site.solar_resource', 'solar', 'solar_resource_data'],
]

The logic in H2I connect_technologies() right now is that if there isn't a . in the first entry of a connection (lets call it resource_source), it will connect f"site.{resource_source}" to the second entry of the connection (the tech). But - if there is a . in the resource_source, then it'll connect f"{resource_source}" to the tech, because the . indicates that the site was specified in the connection.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question and thanks for the context. In the case of having just one site, I like the idea of not having to specify the site in the connections. I don't love the behind-the-scenes logic based on the .. I do like the idea of connections looking the same if you have one site or multiple, though. I think I'm leaning towards requiring the latter (so the site is always specified), mostly so the connections look the same if you have one site or multiple. Open to feedback here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @johnjasa here that consistency is better in this case than backwards compatibility.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also agree with this! I'd rather it be specified than inconsistent!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnjasa - do you prefer site or site_groups as the top-level key? I think site_groups makes more sense.

Comment on lines 143 to 145
if "site" not in plant_config and "site_groups" not in plant_config:
msg = "Missing attribute site or site_groups in plant_config"
raise ValueError(msg)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice check here!

Comment on lines 994 to 1001
if "site" in self.plant_config:
resource_models = self.plant_config.get("site", {}).get("resources", {})
if "site_groups" in self.plant_config:
resource_models = {}
for site_grp, site_grp_inputs in self.plant_config["site_groups"].items():
for resource_key, resource_params in site_grp_inputs.get("resources", {}).items():
resource_models[f"{site_grp}-{resource_key}"] = resource_params

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another spot that motivates my previous comment about potentially moving towards just one way to define site(s) in the future. This could lead to more easily maintainable code once we no longer need the backwards compatibility.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm down to only have one way of defining sites!

Comment on lines 49 to 60
class BoundarySiteComponent(SiteBaseComponent):
def __init__(self, site_config: dict, name=None, val=1.0, **kwargs):
self.config = BoundarySiteComponentConfig.from_dict(site_config)
super().__init__(name, val, **kwargs)

def set_outputs(self):
# latitude and longitude are set as outputs in ``SiteBaseComponent.__init__()``
self.add_output("elevation", val=self.config.elevation, units="m")

for i, boundary in enumerate(self.config.boundaries):
self.add_output(f"boundary_{i}_x", val=np.array(boundary.get("x", [])))
self.add_output(f"boundary_{i}_y", val=np.array(boundary.get("y", [])))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tell me more about this component; why does it exist and what does it do differently than the location site above?

In general, since we're not necessarily doing plant layout or really considering the boundaries, I'm thinking we could get rid of all notion of boundaries within H2I. In the future, we could reexamine and add that capability when needed. I'm not asking you to change this PR or remove all the boundaries from here, but would you be amenable with that path forward? Is there a reason to include boundaries at this time?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea with site_model is to specify different types of sites, to me the different types of sites that I could envision immediately are:

  1. unbounded location ("location" model)
  2. bounded location ("boundary" model - I'm not sure if Ard takes into account site boundaries (@jaredthomas68), but that's why I added it in for now. Happy to remove it for the time being since its unused)
  3. transit/transport route model with multiple lat/lons.

All that being said - the BoundarySiteComponent is not necessary for this PR

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks again for the detail. I'd say we should stick to just one site definition type using lat/lon right now, remove the BoundarySiteComponent and then you wouldn't need the inherited class structure for the sites. I do really like that you started down that path, though! My goal is to keep the available models and options limited to things that are actually implemented until we get further down the line.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ard does include boundaries, but can just own their definition. I imagine we will need boundary definition eventually, especially for nice visualizations, but I also think we don't need them defined right now outside of wind farm codes that require them that is.

prefix="finance_",
)

def create_site_model(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I'd like to see more comments and docstrings in this method, but I acknowledge right now we're giving high-level feedback so this might shift. More just noting it for later -- and I'll acknowledge that the existing method didn't have a docstring!

Copy link
Collaborator Author

@elenya-grant elenya-grant Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a simple doc strings and in-line comments.

@johnjasa
Copy link
Collaborator

johnjasa commented Jan 7, 2026

Thanks for your responses and iteration here, @elenya-grant! Based on that, I'd say we should:

  • make it so there's one of defining site(s) (can break backwards compatibility)
  • always link up resource info from sites to technologies in the same way regardless of number of sites
  • put lat/lon at the top level of the site config section
  • remove boundary site handling for now

I put more context in my text responses to you, but do let me know if you want clarification on anything.

Copy link
Collaborator

@jaredthomas68 jaredthomas68 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like where this is going. Thank you @elenya-grant! I left some comments, but nothing major outside of what has already been mentioned. I left a few comments though.

I did not note specifically, but it looks like there are no tests for specific methods/functions. I would like to see each method or function added have accompanying tests.

" latitude: 47.5233\n",
" longitude: -92.5366\n",
" elevation_m: 439.0\n",
" elevation: 439.0\n",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like having the _m in here for clarity. Why did you decide to remove it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cause we can specify units within OpenMDAO, but I see the value of having '_m' in the variable name in the plant config, whereas the variable name for the site (which has units) could just be elevation.

# connect the wind resource to the wind technology
['wind_resource', 'wind', 'wind_resource_data'],
['solar_resource', 'solar', 'solar_resource_data'],
['wind_site.wind_resource', 'wind', 'wind_resource_data'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in other places we have connections that can be different lengths for when certain information is used or not. I'm fine with the . syntax, but it may be good to have a consistent approach. - non-blocking comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an alternative would be to change resource_to_tech_connections to be 4-length tuple:
['wind_site', 'wind_resource', 'wind', 'wind_resource_data']

where the organization is [site_name, resource_name, tech_name, variable_to_pass]

do you like the 4-length tuple more than the 3-length tuple (['wind_site.wind_resource', 'wind', 'wind_resource_data']) which is formatted as [site_name.resource_name, tech_name, variable_to_pass]`?

I'd also be curious what @johnjasa, @kbrunik, and @genevievestarke think about the different ways of organizing resource_to_tech_connections to accommodate this new update.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm still pro 3-way connection. I know we discussed in the standup but I see that being most similar to our current tech connections. Potentially could be reopened in the future but I see it working as is

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like where we landed after our discussion with the 3-way connection!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

Comment on lines 322 to 323
if "site_groups" not in self.plant_config:
if "site" in self.plant_config:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @johnjasa here that consistency is better in this case than backwards compatibility.

site_group = self.create_site_group(plant_config_reorg, site_info)
self.model.add_subsystem(site_name, site_group)

def create_site_group(self, plant_config_dict: dict, site_config: dict):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is a way to simplify the logic so we can use class attributes instead of creating copies of the configs I think it would help avoid errors. I think having a single syntax for sites (site_groups > site, etc) would make it so you could use the class attributes self.plant_config and self.site_config directly instead of passing them around.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did make it somewhat simpler by not allowing backward compatibility with the previous format of sites.

Some of this complexity is similar to the complexity required for the finance groups and subgroups, because the resource models (and finance models) expect certain keys in the plant_config and don't know the name of their model. Resource models expect site at a top-level (rather than 'sites') and since the resource models are blind to what site they're apart of (or what the name of that site is), we have to reformat the plant config prior to initializing the resource models to only include the site that the resource model is connected to.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I still don't love the copying, but I'll role with it. Thanks for the explanation and some simplification

Comment on lines 49 to 60
class BoundarySiteComponent(SiteBaseComponent):
def __init__(self, site_config: dict, name=None, val=1.0, **kwargs):
self.config = BoundarySiteComponentConfig.from_dict(site_config)
super().__init__(name, val, **kwargs)

def set_outputs(self):
# latitude and longitude are set as outputs in ``SiteBaseComponent.__init__()``
self.add_output("elevation", val=self.config.elevation, units="m")

for i, boundary in enumerate(self.config.boundaries):
self.add_output(f"boundary_{i}_x", val=np.array(boundary.get("x", [])))
self.add_output(f"boundary_{i}_y", val=np.array(boundary.get("y", [])))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ard does include boundaries, but can just own their definition. I imagine we will need boundary definition eventually, especially for nice visualizations, but I also think we don't need them defined right now outside of wind farm codes that require them that is.

Copy link
Collaborator

@genevievestarke genevievestarke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is looking really good!! I don't have specific comments beyond the other comments made on this PR. Thank you for your work on this @elenya-grant !

@johnjasa johnjasa added the needs modifications This PR has been reviewed, at least partially, and is ready for PR author response label Jan 13, 2026
@johnjasa
Copy link
Collaborator

Just discussed this more with Elenya and Kaitlin:

  • landed on sites up top
  • no site_model definition (for now)
  • just lat/lon, no boundaries

@elenya-grant elenya-grant removed the needs modifications This PR has been reviewed, at least partially, and is ready for PR author response label Jan 19, 2026
Copy link
Collaborator

@kbrunik kbrunik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! Thanks :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we update from xx and the 23 to 27?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

The `resource_to_tech_connections` section in your plant configuration file defines how different technologies are connected to sites and the resource data for that site.
This is how the H2I framework establishes the necessary OpenMDAO connections between your sites and technologies based on these specifications.

Resource to technology connections are defined as an array of 3-element arrays in your `plant_config.yaml`:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these arrays or lists?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk - I used the word array because that what was used in docs/user_guide/connecting_technologies.md to describe the technology interconnections.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think array is valid here, as yaml has a slightly different lingo than Python: https://stackoverflow.com/a/33136212

- **site_name**: Name of the site for the resource model
- **resource_name**: Name of the resource model outputting the resource data
- **tech_name**: Name of the technology receiving the input resourec data
- **variable_name**: The resource variable name to pass from the site to the technology ("wind_resource_data" for wind technology models, and "solar_resource_data" for solar technology models.)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the rest of the things in this list can be named whatever you want, I believe, but does this one have to be either "wind_resource_data" or "solar_resource_data"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes! I can add a note for that!

Some examples that define a single site with multiple resources are:
- `examples/23_solar_wind_ng_demand/plant_config.yaml`

### Multiple sites with resources
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these sections with the corresponding examples are fantastic! I think for this last one though, the case with two wind farms would be more descriptive than doing a wind and solar example, just to show how the naming can work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point! I will update it!

Copy link
Collaborator

@johnjasa johnjasa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I like the work you put into this iterating based on others' feedback! It's really a nice improvement. I pushed up some nano doc formatting changes.

The `resource_to_tech_connections` section in your plant configuration file defines how different technologies are connected to sites and the resource data for that site.
This is how the H2I framework establishes the necessary OpenMDAO connections between your sites and technologies based on these specifications.

Resource to technology connections are defined as an array of 3-element arrays in your `plant_config.yaml`:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think array is valid here, as yaml has a slightly different lingo than Python: https://stackoverflow.com/a/33136212

# connect the wind resource to the wind technology
['wind_resource', 'wind', 'wind_resource_data'],
['solar_resource', 'solar', 'solar_resource_data'],
['wind_site.wind_resource', 'wind', 'wind_resource_data'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like where we landed after our discussion with the 3-way connection!

@johnjasa johnjasa dismissed jaredthomas68’s stale review January 23, 2026 23:49

Dismissing as we discussed this in the standup; if not re-reviewed by Friday, will just bring it in. Thanks!

@johnjasa johnjasa enabled auto-merge (squash) January 23, 2026 23:49
Copy link
Collaborator

@jaredthomas68 jaredthomas68 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ready to be pulled it.

# connect the wind resource to the wind technology
['wind_resource', 'wind', 'wind_resource_data'],
['solar_resource', 'solar', 'solar_resource_data'],
['wind_site.wind_resource', 'wind', 'wind_resource_data'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

site_group = self.create_site_group(plant_config_reorg, site_info)
self.model.add_subsystem(site_name, site_group)

def create_site_group(self, plant_config_dict: dict, site_config: dict):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I still don't love the copying, but I'll role with it. Thanks for the explanation and some simplification

@johnjasa johnjasa merged commit 1750b36 into NatLabRockies:develop Jan 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request framework ready for review This PR is ready for input from folks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants