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

Separate BMI wrapper from core model #19

Merged
merged 42 commits into from
Apr 23, 2020

Conversation

ericbarefoot
Copy link
Collaborator

@ericbarefoot ericbarefoot commented Apr 16, 2020

One of the themes that keeps coming up in our discussions is the confusion caused by the BMI wrapper being so intertwined with the workings of the core model. I went through and tried to concretely identify all the places where the core model requires some component from the wrapper, or where the two doubly define a parameter. In both cases, I deferred to the core model, shunting lists that were required by both to the core model, and modifying the wrapper so it accesses the needed parameters from the model instance.

The two key things that had something like this were

  1. the model _time which was defined both when the BMI delta was initialized, and again re-set when the pyDeltaRCM delta object was initialized.
  2. some lists of variable names, which were needed to parse the YAML file, and are also returned as methods in the BMIDelta class. In both cases, I moved everything into the model class, with the idea that the user will not be trying to access the variable names until a model is initialized anyway.

I also made a new script, so that the two should be able to run separately

Thoughts are appreciated. This relates partly to the question of whether to separate into two repos, but is really just a first step towards that.

In this commit, I went through. and tried to find all the things in the 
core model that are wrapped and accessed by the BMI wrapper. In the 
process, I tried to take things that didn't need to be in the wrapper, 
and instead put them in the main model. The biggest thing like this that 
I found were the model time, which is critical for the model, but was 
defined both in the BMI wrapper and in the core model itself. The other 
thing was a list of input and output variable names, that is used to 
parse the yaml file that specifies the run. 

I moved all these things into the core model, under the premise that if 
the model is instantiated from a BMI framework, it will acquire those 
processes once it is initialized, and doesn't need them before 
initialization.
We would like to see that the model can run alone and also with a BMI 
interface, so I wrote a new script which runs the model as if it were a 
normal model, and then also as a BMI model.
File import is a core initialization function, and it should be with the 
other initialization methods in that class, not with the other tools.
@ericbarefoot ericbarefoot added the enhancement New feature or request label Apr 16, 2020
Also change script to only import the core model and only update for one timestep to test for runtime errors

Add output files to gitignore
@ericbarefoot
Copy link
Collaborator Author

just added a few small changes to fix an import error and keep output files from accumulating in the git log.

@elbeejay
Copy link
Member

I suspect the double variables existed so that the BMI-wrapper held all of the variables that are expected for the model to be BMI-compliant so someone less familiar with DeltaRCM but knowledgeable with regards to the BMI scheme could easily access those variables. But if this is the first step to separating the core DeltaRCM model from the BMI-wrapper (which I think is worthwhile) then I support it.

@amoodie
Copy link
Member

amoodie commented Apr 16, 2020

@ericbarefoot can you change the requirement in requirements.txt to match the new bmi name bmipy?

Want to make sure tests pass. More thoughts coming.


EDIT: you need to change it here too

@ericbarefoot
Copy link
Collaborator Author

I suspect the double variables existed so that the BMI-wrapper held all of the variables that are expected for the model to be BMI-compliant so someone less familiar with DeltaRCM but knowledgeable with regards to the BMI scheme could easily access those variables. But if this is the first step to separating the core DeltaRCM model from the BMI-wrapper (which I think is worthwhile) then I support it.

Hmm. Can you elaborate on that? The only attribute that was doubly defined was _time and the other variable names are things that I think BMI needs, but I wasn't sure if they needed to be accessed from an uninitialized bmidelta object or not.

pyDeltaRCM/bmi_delta.py Outdated Show resolved Hide resolved
@amoodie
Copy link
Member

amoodie commented Apr 16, 2020

Cool this is something I definitely think we should do, so I'm in support of the idea, but I have some thoughts. I think separating the two in this repo into two classes, is a good first step.

In the long run, I think that this project pyDeltaRCM (drop the _WMT eventually) should have no code relating to the BMI whatsoever. We should open another repository called bmiDeltaRCM or something, which provides the wrapper around pyDeltaRCM, making it BMI compliant and WMT compliant.

To this end, I think moving the dictionary of BMI variable names into pyDeltaRCM is wrong. In fact, I would support removing the long tuples and dicts defined at the top of pyDeltaRCM too.

A good thing to try to make sure we get this right is to write a few simple tests. One where you initialize pyDeltaRCM directly from the .yml file, run a few timesteps, check values. One where you initialize through the bmi from the .yml file, run a few timesteps never touching pyDeltaRCM at all, check values.

@elbeejay
Copy link
Member

Eric, I see what you are saying. I guess I mean keeping the bmi variables separate in the wrapper and passing them to the corresponding model variables was intentional. Likely in-line with the BMI 'best practices' https://bmi.readthedocs.io/en/latest/bmi.best_practices.html#best-practices Which suggests leaving internal model variable names unchanged, but provided a BMI 'standard' name which maps to the internal name.

@amoodie
Copy link
Member

amoodie commented Apr 16, 2020

@ericbarefoot the scripts you put in look basically like tests! Can you wrap them into tests for real though? Or let me know if you want me to do it?

Not sure if you saw my edit above, you need to change the bmipy add here too

@amoodie amoodie self-requested a review April 16, 2020 18:42
@ericbarefoot
Copy link
Collaborator Author

@ericbarefoot the scripts you put in look basically like tests! Can you wrap them into tests for real though? Or let me know if you want me to do it?

Not sure if you saw my edit above, you need to change the bmipy add here too

I have no idea how to do this! I can change the bmipy call, but I do not know what you mean when you say wrap it into tests.

@ericbarefoot
Copy link
Collaborator Author

ericbarefoot commented Apr 16, 2020

Eric, I see what you are saying. I guess I mean keeping the bmi variables separate in the wrapper and passing them to the corresponding model variables was intentional. Likely in-line with the BMI 'best practices' https://bmi.readthedocs.io/en/latest/bmi.best_practices.html#best-practices Which suggests leaving internal model variable names unchanged, but provided a BMI 'standard' name which maps to the internal name.

@elbeejay this makes a lot of sense, but I'm not quite sure how to resolve the issue that the yaml file uses these dictionaries to parse in the core model run itself. If we leave it the original way, the model actually can't run without the BMI wrapper.

@amoodie
Copy link
Member

amoodie commented Apr 16, 2020

the yaml file uses these dictionaries to parse in the core model run itself. If we leave it the original way, the model actually can't run without the BMI wrapper.

Then we should change the .yaml to have variable names we have in the model. I mean, that is my personal preference.

The BMI wrapper should be a wrapper, defining the things it needs (like a dictionary to map BMI variables to pyDeltaRCM variables) in its own world.

This will be a breaking change to the API for sure, but I think it's a good move...

@ericbarefoot
Copy link
Collaborator Author

Then we should change the .yaml to have variable names we have in the model. I mean, that is my personal preference.

Groovy, thanks for the thoughts and feedback all. This will probably require a little more effort and time then, so I'll close this PR, and re-open when I've repaired the yaml

@amoodie
Copy link
Member

amoodie commented Apr 16, 2020

Okay that sounds good, let me know when you've got something workable in your repo branch and we can chat about tests / I can write a few.

Copy link
Collaborator Author

@ericbarefoot ericbarefoot 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 the thoughts fellas!

pyDeltaRCM/deltaRCM_driver.py Show resolved Hide resolved
pyDeltaRCM/bmi_delta.py Show resolved Hide resolved
pyDeltaRCM/bmi_delta.py Show resolved Hide resolved
pyDeltaRCM/bmi_delta.py Show resolved Hide resolved
pyDeltaRCM/default.yml Show resolved Hide resolved
pyDeltaRCM/init_tools.py Show resolved Hide resolved
pyDeltaRCM/init_tools.py Show resolved Hide resolved
@ericbarefoot
Copy link
Collaborator Author

OK, I made a few linting changes, and added a tiny bit of documentation @amoodie, but I will defer the new testing till later, if you don't mind, since that seems like more of a project.

@amoodie
Copy link
Member

amoodie commented Apr 22, 2020

Okay, do you mind if I write some tests for it? I'll work on it this afternoon and send your way.

@amoodie
Copy link
Member

amoodie commented Apr 23, 2020

BUG

@ericbarefoot have you tried running this from outside the repository? I think that this line with os.getcwd() is problematic, because it implies the user will always have their terminal at the package root. Obviously this is not the case.

I fixed this locally with:

self._file_dir = os.path.realpath(os.path.dirname(__file__)) 
self.default_file = os.path.join(self._file_dir, 'default.yml')

Figured it may be easier for you to just implement this and push it than for my to PR again.

@ericbarefoot
Copy link
Collaborator Author

BUG

@ericbarefoot have you tried running this from outside the repository? I think that this line with os.getcwd() is problematic, because it implies the user will always have their terminal at the package root. Obviously this is not the case.

I fixed this locally with:

self._file_dir = os.path.realpath(os.path.dirname(__file__)) 
self.default_file = os.path.join(self._file_dir, 'default.yml')

Figured it may be easier for you to just implement this and push it than for my to PR again.

I have done this, and pushed again. I agree that anything that depends on knowing which directory the user is in is bad, but what is the good python solution for this? In R there's a set of packages where you specify the project root, and all paths are defined from there. So everything just works even though specifying paths is a little verbose.

@ericbarefoot
Copy link
Collaborator Author

Ok now that this works for everyone, I'll go ahead and merge this! As of now, BMI development will be separate from model development, with the eventual goal of separating the BMI into a different repository entirely.

@ericbarefoot ericbarefoot merged commit 8117c2d into DeltaRCM:develop Apr 23, 2020
@ericbarefoot ericbarefoot deleted the separate_bmi branch April 23, 2020 15:19
@amoodie
Copy link
Member

amoodie commented Apr 23, 2020

🎉 thanks @ericbarefoot

amoodie pushed a commit to amoodie/pyDeltaRCM that referenced this pull request Feb 25, 2021
Separate BMI wrapper from core model
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Do we need to keep the bmiDelta code and WMT code in this model repository?
4 participants