-
Notifications
You must be signed in to change notification settings - Fork 7
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
Centralise Ocean parameters #64
Conversation
This frees up the name to use as consistent interface for setting/updating ParameterList.
It's only used once to initialise belos and not touched later, if needed in the future it should be integrated with the default parameter list.
|
If we want to make the original ParameterList the "single source of truth" we really don't want other parts of the code accidentally "adding" parameters that were not in the original list, because then we're back to square 1 of having no centralised places were all parameters can be found. I'll have to think how we can make that work together with still tracking whether a parameter was used or not. Having a "default" parameter list we lose the ability to track whether a default was used anyway, since all the used parameters were already explicitly set in this default list I initialise from. If this is really important I think the simplest way to do it would be to subclass Teuchos::ParameterList and track "unused"/"used"/"default" in that? The current THCM doesn't actually ever use the default value anyway, since the get function is only ever called for parameters that are actually in the list, see: https://github.com/nlesc-smcm/i-emic/pull/64/files#diff-5c096fb489dae5c82b49f6a8261e9a45L1902 Which basically means that it can only ever report used/unused. I actually think we don't lose anything here since there's no conditional selection of which variables to set, so each variable that THCM knows about is always set if present (so they can't be left unused). This means only variables that THCM doesn't know about will ever be "unused", but once we add the validation so that we only allow "known" parameters, that can't happen either. Anyway, if you still think this functionality is important we can brainstorm some way to make it work again. |
Adding parameters is not a problem. You should just always add them with the default value by using #include <iostream>
#include "Teuchos_ParameterList.hpp"
int main()
{
Teuchos::ParameterList pars;
pars.set("Test 1", 1);
pars.set("Test 2", 2);
std::cout << pars.get("Test 1", 2) << std::endl;
std::cout << pars.get("Test 3", 3) << std::endl;
std::cout << pars.get("Test 3", 4) << std::endl;
std::cout << pars;
} gives as output
Note that the second The reason that the THCM parameters are not read in this way yet, is that the default values are in the Fortran code. If you move them to the C++ code this can be done with the proper get method. |
It's a problem in the sense that, if I query Ocean "hey, which parameters do you support?" and 'Test3' is not in the default list, then omuse doesn't know 'Test3' is a parameter that we can set, since omuse only knows about the parameters it can query. That's why I made the ParameterList const, that way we know it can only use parameters that were already in the list (and thus must be in the default list, since the validation will reject any parameter that's not in the default list). |
Yes, but that's my point. You just
and |
Just to be sure, are we talking about the same thing? I thought you were referring to the change in THCM.C's readParameters, but do you mean you're referring to the parameter setting inside Ocean? |
Both. They should both do the same thing. |
Ok, I see what you're getting at, but there's a bit of trickiness. I can see two approaches that make sense, but I'm unsure what has the best trade-off. The approach I just pushed uses get + default value before initialising There's an alternate approach which is to indeed use a single template<typename T>
void set(bool doAssign, T& var, T val)
{ if (doAssign) var = val; }
void
Ocean::setParametersHelper(bool doAssign, Teuchos::ParameterList& params)
{
set(doAssign, loadSalinityFlux_, params.get("Load salinity flux", false));
....
}
Teuchos::ParameterList
Ocean::getDefaultParameters()
{
Teuchos::ParameterList result;
setParametersHelper(false, result);
return result;
}
void
Ocean::setParameters(Teuchos::ParameterList newParams)
{
setParametersHelper(true, newParams);
...
} This second option gets rid of the duplication, but 1) I think the |
Relatedly, I've been using const for the ParameterList references being passed in, but if you want to use this used/unused/default setup that'd mean having Although I think the same information would be available in |
Ah, you're right. We actually talked about this the first time we discussed these parameters (here in Groningen). What I suggested was to use parameter list validators. Something like this: static Teuchos::ParameterList getValidParameters() const
{
Teuchos::ParameterList params;
Teuchos::RCP<Teuchos::StringToIntegralParameterEntryValidator<int> >
varValidator = Teuchos::rcp(
new Teuchos::StringToIntegralParameterEntryValidator<int>(
Teuchos::tuple<std::string>("Velocity", "Pressure", "Temperature"),"Velocity"));
params.set("Variable Type", "Velocity", "The types of variables that can be found at the node.", varValidator);
params.set("Dimension", 3, "Dimension of the problem");
return params;
}
static Teuchos::ParameterList getDefaultParameters() const
{
return getValidParameters();
}
void setParameters(Teuchos::ParameterList ¶ms)
{
params.validateParametersAndSetDefaults(getValidParameters());
varType_ = params.get<std::string>("Variable Type");
dim_ = params.get<int>("Dimension");
} In this example, using "Salinity" as a "Variable Type" now results in an error, since it is not in the list in the StringToIntegralParameterEntryValidator. Also the "Dimension" can not be "3.0" since it expects an int. Note that the 3rd parameter of |
Your suggestions was my plan all along. But I first wanted to centralise the parameterlists, then ensure setParameters verifies against the default, and then figure out how to add the validators (I actually already have some stuff for validation, but it breaks everything until I sort out THCM and solver_params). |
Ok, perfect. The commits that you pushed now also seem fine to me. The only thing I noticed is the |
src/ocean/THCM.C
Outdated
@@ -2058,7 +2010,7 @@ bool THCM::setParameter(std::string label, double value) | |||
else if (param==0) // 0 is non-dimensional time | |||
{ | |||
// set monthly forcing data | |||
bool time_dep_forcing = paramList.get("Time Dependent Forcing",false); | |||
bool time_dep_forcing = paramList.get<bool>("Time Dependent Forcing"); |
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 think this should probably become an instance variable.
Some of the constructor only parameters used in THCM are used to initialise this here, would it be sensible to allow updating these later or should these only really be set at initialisation? The fortran code gets initialised here but ideally we want to be able to set/overwrite these parameters later via |
I think everything but the grid can be changed later. Maybe even the grid could be changed later, but then you would have to re-initialize most of the code anyway, so it would be better to just call the constructor again. |
Relatedly, right now I've been adding the same functions to different classes, but would it perhaps make sense to define a baseclass that provides virtual implementations of all those getParameters/setParameters function and inherit from those? (This would especially be useful to avoid duplicating a lot of work if/when we also add a separate |
Yeah, go ahead. |
Well, this pull request actually changes the THCM sublist from Ocean here (if the lists are shared): https://github.com/nlesc-smcm/i-emic/pull/64/files#diff-24e73cc71ccdf36ee75c0b8350cdf86cR2231 |
ok, I am not sure 100% what setparams of teuchos::ParameterList actually does, but my understanding is that this snippet:
first updates params_ from the newParams and then validates them, checks whether any parameters are in there that should not be and fills in any missing parameters from defaults?
|
Well, as I commented, the first thing that is wrong is already that it updates the parameters BEFORE validating that they are valid. But as for your comments, what happens:
What I think should happen is that |
ok, from what I get the newparams can also be just a subset or one particular parameter that you want to change - in this case validating before will fill it with default values for the other parameters??
ok, i see... this is necessary...basically setParameters and constructor are the only places that should be used to set/change parameters..its not too far from the current setup I think though..let me think about this (and also @merijn to comment)... |
this commit fixes the validation and propagates the sublist to THCM setParameters (and fixes constructor). the paramlist passed to the constructor will get updated, which is ok. The only thing which you should not do is update the paramlist afterwards with the expectation that these changes will be committed to the sim code.. |
Apparently that made the tests fail... Also with that change the parameter list passed to setParameters doesn't get updated with what is done with the parameters (used, unused, default, etc). The entire point of passing a nonconst reference to that method is that the parameter list gets updated. I still don't see what's wrong with my approach, since at the very least it's much easier to read. |
yea we are working on it (its not entirely obvious why it fails.. |
this should fix it. note that at the end the input newparam gets updated so that you have same information as your proposal. in your proposal the original input parameterlist to the constructor is only updated after the constructor - not with later changes. I guess what is preferable depends a bit on the usage pattern! The main items for conversation are according to @merijn: |
btw we are not fundamentally opposed to your proposal (merijn is ok with implementing that if you wish so), but I think we should first reach agreement on how it looks on the "user side" cause I got the feeling Merijn was chasing a moving target and led to a bit of unhappiness ;-) |
Yes, your points perfectly summarize the current state of the discussion. Also, as for the extra thing you added to |
Ideally I think the check we added in As for testing, I didn't want to invest any time into writing any tests until we at least agreed on the actual API provided by |
Sure, maybe that check should be deleted, but there are many more things that are done in the constructor that depend on parameters that can be set. If the getting these parameters has been moved to Let's say the ultimate goal (not from this pull request, but after many more pull requests) is to be able to call the constructor first (without parameters), then |
On my bike I came up with an example of why this is preferable. If the parameter list is shared, we get this: Say we have a class C with internal parameters a_ and b_ that can be set through the parameter list with "A" and "B". Teuchos::RCP<Teuchos::ParameterList> params = Teuchos::rcp(new Teuchos::ParameterList);
params->set("A", 1);
params->set("B", 2);
C c(params);
// internal parameter a_ = 1
// internal parameter b_ = 2
// lots of code
params->set("A", 3);
// lots of code
// internal parameter a_ = 1
// internal parameter b_ = 2
Teuchos::ParameterList newParams;
newParams.set("B", 4);
c.setParameters(newParams);
// internal parameter a_ = 3!!!!!
// internal parameter b_ = 4 Now a_ is something completely different while we didn't pass it to setParameters. Note that this doesn't even have to happen in the same file. It can be changed from somewhere completely different because of the shared RCP. |
ok, that can indeed be a problem; in effect the input parameterlist is only safe for reading in our case..@merijn shall we change it and go for @Sbte approach? note that this will make the "check the parameters" part (after constructor or setparameters) brittle in the sense that the input parameter list only provides a valid view immediately after the call..but maybe it is better to explicitly to a getParameters call on the class if you want an up to date status... |
ok, I have changed parameter list now..(let me know what you think, @Sbte ..it may need some cleanup/simplfication) |
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 found 2 things that are actually broken after the recent changes. There are many other broken things, such as THCM not setting the proper internal variables after a parameter change, but this can be changed in another pull request, since it doesn't affect how we currently use the code. In this new pull request, please fix one variable at a time and add a test for every fix.
Also, since we now have something that supposedly works (for the most part), please add some tests to make sure it doesn't break in the future. Some tests can be: checking that the defaults are set, used and unused are set, we can't validate with an incorrect parameter, changing the parameter list after the constructor doesn't change the internal parameter list. This should be done in this pull request (to spot possible errors).
to do, tests to:
|
please add any other to do items! |
I made an issue for the remaining stuff that is required after this pull request. I also updated your comment with something else I came up with. |
Rebasing these changes into a cleaner/more minimal PR. |
Doesn't currently include validation because THCM parameters are passed via ocean and don't pass validation.
I think the best approach is to refactor THCM in a similar fashion so that Ocean can simply query the default parameters from THCM and extend its default parameters with those. The main obstacle is that some of THCM's parameters are only used in the constructor, so it's unclear what "setting" those would mean/look like. Specifically THCM parameters only used in the constructor to initialise other values are:
@erik808 @Sbte what do you think should be done with those parameters?