Add year field to ProcessParameter, create ProcessParameterMap#475
Add year field to ProcessParameter, create ProcessParameterMap#475
ProcessParameter, create ProcessParameterMap#475Conversation
ProcessParameter, create ProcessParameterMap (alternative to #474)
8610b55 to
be12c26
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #475 +/- ##
==========================================
- Coverage 95.03% 94.33% -0.70%
==========================================
Files 36 37 +1
Lines 4893 4751 -142
Branches 4893 4751 -142
==========================================
- Hits 4650 4482 -168
- Misses 123 138 +15
- Partials 120 131 +11 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
ProcessParameter, create ProcessParameterMap (alternative to #474)ProcessParameter, create ProcessParameterMap
alexdewar
left a comment
There was a problem hiding this comment.
Looks good! A few suggestions.
Given that Assets need to look up various process parameters for their commission year, I think it would be better to just do that lookup once and store the process parameters as a field in the asset. You could store Rc<ProcessParameter>s in the parameter map to make them cheap to copy (also useful for when there are ranges of years specified).
I don't think we actually need the YearSelection struct as we're not going to be using it outside of the input layer. You could just represent years as strings in the relevant *Raw structs and then have a function to parse them, kind of like I suggested before, e.g.:
pub fn parse_year_str(s: &str, milestone_years: &[u32]) -> Result<Vec<u32>> {
if s.eq_ignore_ascii_case("all") {
return Ok(Vec::from_iter(milestone_years.iter().copied()));
}
s.split(";")
.map(|y| {
let year = y.parse::<u32>()?;
ensure!(milestone_years.binary_search(&year).is_ok());
Ok(year)
})
.try_collect()
.context("Invalid year")
}This would mean you could do parsing and validation (e.g. checking that they're all valid milestone years) in one step.
|
PS -- I agree with you about test fixtures. I've written a few functions in places which essentially serve as fixtures, but I haven't been very systematic about it. One approach would be to have a file with these sorts of functions (or constants) in for tests. Alternatively, this crate seems to let you write fixtures in the same kind of way you can with pytest, which is kinda neat. Might be worth looking into. |
Co-authored-by: alexdewar <alexdewar@users.noreply.github.com>
|
Thanks @alexdewar I'm going to keep But yeah, I think if we do this for both year and region then life will be simpler |
|
I think it would be better to fix it now, unless you're really time pressured or something. I'm keen not to repeat the mistake I made with The |
|
Done! |
alexdewar
left a comment
There was a problem hiding this comment.
A few really minor things, but other than that, I think we're nearly there.
| .with_context(|| format!("Missing availabilities for process {id}"))?; | ||
| process.flows = flows | ||
| .remove(id) | ||
| .with_context(|| format!("Missing flows for process {id}"))?; |
There was a problem hiding this comment.
I think the reason an unwrap() was used here (and for the other ones) is because we already check whether all process IDs are covered earlier on, so we know it won't fail. We could always get rid of the other checks.
There was a problem hiding this comment.
I don't think so actually (at least not that I can find)
I'd probably propose making availabilities optional, although I'll leave for now as this will all change with #363
| pub fn try_insert<K, V>(map: &mut HashMap<K, V>, key: K, value: V) -> Result<()> | ||
| where | ||
| K: Eq + Hash + std::fmt::Display, | ||
| K: Eq + Hash + std::fmt::Display + std::marker::Copy, |
There was a problem hiding this comment.
I don't think K needs to implement Copy for this. If you do map.insert(key.clone(), value) instead below, then it will also work for non-Copy types.
There was a problem hiding this comment.
Basic question: what's the difference between copy and clone?
There was a problem hiding this comment.
Copy is essentially a version of clone where you don't actually have to call clone() to get a copy of it, but it only works if you can just copy the underlying bytes to do so. So AgentID can implement Copy (because it's just a u32 under the hood) but e.g. CommodityID can't (if you just copy the raw bytes of an Rc<str> bad things will happen).
| ); | ||
| Ok(year) | ||
| }) | ||
| .collect() |
There was a problem hiding this comment.
I seem to remember your code also rejected empty sets of years (i.e. the user has just input an empty string), which makes sense. Shall we keep that check? I don't think we want users to be able to provide no years as an input.
There was a problem hiding this comment.
Good point. We could either disallow or interpret an empty string to mean all years. I think I'd favour the former for now, but possibly something to revisit
Description
Creating
ProcessParameterMapwhich hold process parameters for each year. I've added a year column toprocess_parameters.csv, which can be either "all", a single year or a list of years. This gets read in bydeserialize_yearto create aYearSelection, and then aProcessParameterMapis created out of all of the entries inread_process_parameters_from_iterComments:
try_inserthelper function, which will return an error if attempting to insert data for a key that already exists in the map. For example, if you had year = "all" and year = 2020 rows for a certain process, you'd get an error telling you there's duplicate data for 2020. The annoying thing is that this won't tell you which process this is for, as this information isn't available totry_insert. Not sure if there's a good solution here. Would be nice to use this for other maps as well.AnnualField, implement onProcessParameter#474 was that you could just doAnnualField::Constantand you didn't have to worry about this. In any case, since the test data is becoming more and more complex, I wonder if it's worth thinking about making test fixtures which we can reuse between modules, like we (sort-of) do in MUSE1 (if such a thing is even possible/standard in Rust).start_yearandend_yearover the the main processes table, as these should not vary by yearPartially fixes #493 (also need to add a region dimension)
Type of change
Key checklist
$ cargo test$ cargo docFurther checks