Skip to content
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

Feature attrdict and model definition declutter #719

Merged
merged 15 commits into from
Dec 6, 2024

Conversation

irm-codebase
Copy link
Contributor

@irm-codebase irm-codebase commented Nov 28, 2024

Prepares our IO structure for pydantic by moving as much YAML read features as possible out of AttrDict.

Summary of changes in this pull request

  • YAML reading functionality has been moved out of AttrDict
  • templates: improvements
    • now resolved before model definition reaches calliope.Model
    • template: can be used anywhere within a YAML file, not just nodes, techs or data_tables
    • cyclic template inheritance errors (template1 -> template2 -> template1 -> template2...) are now detected by using a stack
  • removed inheritance helper function. It was made redundant by the template solving.
  • Grouped scenario, override_dict and templates into preprocess/model_definition.py to ensure a single point of entry for defining their order of priority.

Reviewer checklist

  • Test(s) added to cover contribution
  • Documentation updated
  • Changelog updated
  • Coverage maintained or improved

@irm-codebase irm-codebase marked this pull request as ready for review November 29, 2024 19:23
@irm-codebase
Copy link
Contributor Author

irm-codebase commented Nov 29, 2024

@brynpickering tests run well on my side, with no visible impact on performance.

@irm-codebase irm-codebase changed the title Feature attrdict declutter Feature attrdict and model definition declutter Dec 2, 2024
@irm-codebase
Copy link
Contributor Author

Some additional context @brynpickering:
One of the main ideas behind this PR is to make clear where the configuration object is delivered to calliope, so that the pydantic model that #704 (and #717) use has a single place of delivery. This would be the output of the new preprocess/model_definition.py file.

@irm-codebase
Copy link
Contributor Author

Documentation has been updated. The YAML section now also explains templating (since it's now a generic feature).
I've also separated the section into 'standard YAML' and our 'rich YAML', for clarity.

Copy link

codecov bot commented Dec 2, 2024

Codecov Report

Attention: Patch coverage is 99.37888% with 1 line in your changes missing coverage. Please review.

Project coverage is 96.09%. Comparing base (c300f2e) to head (8d58894).
Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/calliope/util/generate_runs.py 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #719      +/-   ##
==========================================
+ Coverage   95.98%   96.09%   +0.10%     
==========================================
  Files          29       29              
  Lines        4060     4067       +7     
  Branches      580      584       +4     
==========================================
+ Hits         3897     3908      +11     
+ Misses         72       68       -4     
  Partials       91       91              
Files with missing lines Coverage Δ
src/calliope/attrdict.py 97.63% <100.00%> (+1.15%) ⬆️
src/calliope/backend/backend_model.py 97.98% <100.00%> (ø)
src/calliope/backend/helper_functions.py 96.72% <ø> (-0.26%) ⬇️
src/calliope/cli.py 81.57% <100.00%> (ø)
src/calliope/io.py 98.08% <100.00%> (+1.28%) ⬆️
src/calliope/model.py 95.94% <100.00%> (-0.06%) ⬇️
src/calliope/preprocess/__init__.py 100.00% <100.00%> (ø)
src/calliope/preprocess/data_tables.py 100.00% <100.00%> (ø)
src/calliope/preprocess/model_data.py 100.00% <100.00%> (ø)
src/calliope/preprocess/model_definition.py 95.23% <100.00%> (ø)
... and 4 more

Copy link
Member

@brynpickering brynpickering 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'm on board with this. We're slowly but surely extracting content from AttrDict and will eventually be able to remove it entirely! We'll need to work out why this change has impacted the national-scale example model, though. Something about the order of dict unions is likely messing up the national-scale model.

docs/creating/yaml.md Outdated Show resolved Hide resolved
@@ -125,9 +206,160 @@ scenarios:
* The imported files may include further files, so arbitrary degrees of nested configurations are possible.
* The `import` statement can either give an absolute path or a path relative to the importing file.

## Overriding one file with another
### Reusing definitions through templates
Copy link
Member

Choose a reason for hiding this comment

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

should we make it clear that templates can only be used in nodes, techs, and data_tables (at present)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not needed, they can be used anywhere now
The code in nodes, techs and data_tables that handled this was removed in this PR.

Copy link
Member

Choose a reason for hiding this comment

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

ok, interesting. so you could technically have:

templates:
  my_template:
    tech1: ...
    tech2: ...
techs:
  template: my_template

?

I suppose it's fine that it can cover that edge case, even if it wouldn't be realistically used. I see now why you were concerned about the order of overrides/templates. I.e., this can't work:

templates:
  my_template:
    techs:
      tech1: ...
      tech2: ...
overrides:
  override1:
    template: my_template

Because the overrides are resolved before templates are?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct. Your first example would work well.

I saw templates as purely 'extended' yaml. As in, they would work with any YAML file, and the templates code would live inside io.py.

You are also correct in that using templates within the override would introduce issues. My code currently does not cover for it. This is the kind of edge-case that is avoided by just solving it before overrides.

So, two choices:

  • Do we keep the current behaviour, and add code for the edge-case you described?
  • Do we move templates to the io.py function (meaning that read_rich_yaml always solves them)?

I prefer the second even if we lose some templating power because it is more robust overall, code-wise.

Copy link
Member

Choose a reason for hiding this comment

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

In terms of power of the functionality for the user, the first is the best option (since you can update just a template in an override and have it propagate throughout the rest of the model). Let's leave it like that and if we get lots of complaints of weird behaviour, we can revisit. It does require us to protect "template" as a key and not allow it as a subkey of override or scenario.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct. I will add a function that tests for that then.
It is important because after this goes in, schemas (pydantic) and so on must check the data structure of the dictionary returned by model_definition, so this edge case will be missed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After going through this edge-case, it seems like a non-issue. The templates algorithm will just see the 'new' templates or template requested if you change it on the data, and solve accordingly.

This is more a question of what would feel 'natural' to users: updating what templates 'do' within the overrides, or just seeing them as 'copy-paste' within the overrides. Either case should be basically the same in terms of the amount of bugs.

Updating the templates within the overrides seems too convoluted for me, so I would not use it, but I think it's fine to keep the code as-is.

@@ -181,10 +181,9 @@ This split means you can change configuration options on-the-fly if you are work
`locations` (abbreviated to `locs` in the Calliope data dimensions) has been renamed to `nodes` (no abbreviation).
This allows us to not require an abbreviation and is a disambiguation from the [pandas.DataFrame.loc][] and [xarray.DataArray.loc][] methods.

### `parent` → `base_tech` + `template`
### `parent` → `base_tech`
Copy link
Member

Choose a reason for hiding this comment

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

The original is correct. in v0.6 you could have an inheritance chain with parent. You could define a parent from tech_groups, which in turn could define a base_tech as its parent.

Copy link
Contributor Author

@irm-codebase irm-codebase Dec 5, 2024

Choose a reason for hiding this comment

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

The problem here is that this PR removes all the 'inheritance' stuff. The original text would be confusing...
Would this make sense?


### `parent` → `base_tech`

Technology inheritance has been unlinked from its abstract "base" technology. Technologies must now inherit from `base_tech` which fixed to be one of [`demand`, `supply`, `conversion`, `transmission`, `storage`].

Copy link
Member

Choose a reason for hiding this comment

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

OK, but for migrating from having an inheritance chain (previously defined by parent alone) is having base_tech + template. Maybe there needs to be a separate section for tech_groups -> templates. Basically, if a user has tech_groups, this page should provide them with a clear pointer for how to migrate. Currently there isn't one. That templates can do even more than tech_groups is besides the point.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds reasonable. I will include that once other aspects of this PR are clarified.

src/calliope/preprocess/model_definition.py Show resolved Hide resolved
tests/test_core_attrdict.py Outdated Show resolved Hide resolved
tests/test_io.py Outdated Show resolved Hide resolved
tests/test_io.py Outdated Show resolved Hide resolved
tests/test_preprocess_model_definition.py Show resolved Hide resolved
@irm-codebase
Copy link
Contributor Author

irm-codebase commented Dec 5, 2024

Ready for review again @brynpickering!

I've included all your suggestions, except a couple that I believe are not necessary (see the unresolved comments).

The random error in the national example model has been fixed. I must be honest in saying that I still see it as a bug, but it is not within the scope of this PR since it predates it.

@brynpickering
Copy link
Member

Thanks @irm-codebase - I've responded. Where has the loss in coverage come from?

@irm-codebase
Copy link
Contributor Author

Thanks @irm-codebase - I've responded. Where has the loss in coverage come from?

No prob. Looking at the codeconv report, it seems like it comes from the thinning of tools.py and other files.
This PR removed a lot of code from src/, so the percentages lie a bit here.

I can add tests, but they would not be for the functionality impacted by this PR.

@brynpickering
Copy link
Member

I can add tests, but they would not be for the functionality impacted by this PR.

Makes sense. Nah, I won't force that on you!

@irm-codebase
Copy link
Contributor Author

An exception seems to be in _load_yaml... for something that might not even be used anymore.
I'll give it a look in the next round of the review.

Copy link
Member

@brynpickering brynpickering left a comment

Choose a reason for hiding this comment

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

Sorry, forgot to comment on this!

src/calliope/io.py Outdated Show resolved Hide resolved
@irm-codebase
Copy link
Contributor Author

@brynpickering final update is in!
The important change is only the 'migrating' section. To keep this section short, I did not a new subsection. Instead, I modified the title a bit and added a short sentence.

I did this because the example serves both cases, and adding more text just increases our maintenance burden.

@brynpickering brynpickering merged commit 9f33ecb into main Dec 6, 2024
13 checks passed
@brynpickering brynpickering deleted the feature-attrdict-declutter branch December 6, 2024 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants