-
Notifications
You must be signed in to change notification settings - Fork 64
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
Constituents #495
Constituents #495
Conversation
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.
@peverwhee @gold2718 Looks fine to me. Thanks for this monster effort. I don't have anything that would hold up this PR from being merged, assuming the new tests all work as expected.
I'm a bit confused about the need for metadata for fields within the DDTs, but maybe this is because some DDTs have column data that may need to be referenced outside of their home DDT?
@peverwhee Can you update this branch with updates from feature/capgen? |
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.
Yowza, that is a lot to digest!
I have some question and a few suggested changes but overall, I think this is good.
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'll add these as comments since I don't understand enough of the big picture to call this a proper "review".
@@ -563,29 +563,39 @@ def parse_preamble_data(statements, pobj, spec_name, endmatch, run_env): | |||
module=spec_name, | |||
var_dict=var_dict) | |||
mheaders.append(mheader) | |||
if run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG): |
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.
This might be outside the scope of this PR specifically, but It seems like this same debug logic is used a lot in capgen, does it make sense to pull this out into a function so it can be a single line every time?
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.
Added new function debug_on() routine to CCPPFrameworkEnv
ctx = context_string(pobj, nodir=True) | ||
msg = 'Adding header {}{}' | ||
run_env.logger.debug(msg.format(mheader.table_name, ctx)) | ||
# end if |
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 understand the need for helper comments like this for very long or convoluted logic blocks, but here (and other if blocks that are only a few lines) it seems superfluous.
I see that this is already present in many places so probably doesn't need to be addressed in this PR, but I thought I would throw it out there.
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 would rather have consistent usage and have been saved more than once by these statements showing me where some code block was incorrectly indented.
This particular choice is part of the style guide of some projects I work on. Others use a blank line at the end of every block. I do not know any other project that has different rules for different sized blocks.
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.
My personal preference would be blank lines, but since this project has started out with # end if
(lowercase nowadays, for consistency) it makes sense to continue using it. I shall add those missing statements in my PR #512 later!
scripts/constituents.py
Outdated
init_args = [f'std_name="{std_name}"', f'long_name="{long_name}"', | ||
f'units="{units}"', f'vertical_dim="{vertical_dim}"', | ||
f'advected={advect_str}', | ||
f'errcode={errvar_names["ccpp_error_code"]}', | ||
f'errmsg={errvar_names["ccpp_error_message"]}'] |
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'm curious what our new minimum python version is? If we require Python 3.8 or newer then this and similar expressions can be greatly simplified
init_args = [f'std_name="{std_name}"', f'long_name="{long_name}"', | |
f'units="{units}"', f'vertical_dim="{vertical_dim}"', | |
f'advected={advect_str}', | |
f'errcode={errvar_names["ccpp_error_code"]}', | |
f'errmsg={errvar_names["ccpp_error_message"]}'] | |
init_args = [f'{std_name=}', f'{long_name=}', | |
f'{units=}', f'{vertical_dim=}', | |
f'{advect_str=}', | |
f'errcode={errvar_names["ccpp_error_code"]}', | |
f'errmsg={errvar_names["ccpp_error_message"]}'] |
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.
Well that is a very interesting feature I had not noticed creeping in. Very Fortran friendly :)
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.
BTW, I think line 315 has to stay as is. It is a Python string but a Fortran literal.
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.
changed to new fancy python 3.8 version!
src/ccpp_constituent_prop_mod.F90
Outdated
astat = 1 | ||
call set_errvars(astat, subname, & | ||
errcode=errcode, errmsg=errmsg, & | ||
errmsg2=" ERROR: num_advected_vars index out of bounds") |
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.
Can this error message also print num_vars
and the current index?
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.
yep! done
src/ccpp_constituent_prop_mod.F90
Outdated
if (check) then | ||
index_advect = index_advect + 1 | ||
if (index_advect > this%num_advected_vars) then | ||
call set_errvars(1, subname, & |
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.
Same here in these two blocks, print the OOB index and the limit it has exceeded
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.
yep! done
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.
Great work @peverwhee!! Just wanted to clarify a few things.
scripts/metavar.py
Outdated
else: | ||
element_names = None |
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.
Is this else
needed since we don't enter the if
block that assigns element_names=[]
?
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.
agreed. removed.
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.
But if element_names
is not given a value, what is returned at the end?
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.
Good point. At the end of the function if element_names
is not defined, python throws an exception.
Would a better suggestion have been to set element_names = None
by default before starting the if
checks?
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 don't know if it is "better" but it is sufficient. The nice thing about raising exceptions in logic corners that should never be hit is that you can find out if future coding ever takes this routine out of its design range.
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.
thanks for this discussion, guys. I thoroughly confused myself and will blame the recursion (it definitely isn't a me problem!). Updated to initialize element_names to None at the top of the routine and then also raise an exception if we get to the else of if ddt_lib and (dtitle in ddt_lib):
scripts/metavar.py
Outdated
else: | ||
element_names = None |
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.
But if element_names
is not given a value, what is returned at the end?
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.
Finished a review of ac18c44
and resolved all the conversations I started. However, I still see a few things I think should be changed.
|
||
if (associated(this%prop)) then | ||
call this%prop%vertical_dimension(dimname) | ||
is_2d = len_trim(dimname) == 0 |
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.
For CAM at least, 2-D means there is no vertical dimension so there is no name for it. It might be cleaner and more understandable if a is_2d
method was added to the prop
obect.
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.
This is a very high-level review. I was not looking at every single of the 10000-ish lines in the modified/added constituent code, but rather trying to get a bit of an understanding and look at things I know about (and care about more ...).
I am a little concerned about how complicated and extensive this code is, but as long as none of this is mandatory and host models can use capgen without the constituents logic (not saying the should, but at least for transitioning from prebuild to capgen that would be good) then that's all ok.
My only real concern is the newly added debug_on
feature, which is inconsistent with how the arguments to capgen
and prebuild
are called and therefore should be renamed to just verbose
. I started suggesting those changes until I got tired, and I certainly overlooked a few.
scripts/framework_env.py
Outdated
@@ -275,6 +276,11 @@ def kind_types(self): | |||
CCPPFrameworkEnv object.""" | |||
return self.__kind_dict.keys() | |||
|
|||
def debug_on(self): |
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.
This should not be debug_on
but simply verbose
to match the flag that gets passed to capgen
, and also to avoid confusion with what the --debug
switch for capgen is doing (see PR #512)
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.
good call! Changed to a verbose property
scripts/ccpp_suite.py
Outdated
@@ -427,7 +427,7 @@ def analyze(self, host_model, scheme_library, ddt_library, run_env): | |||
phase = RUN_PHASE_NAME | |||
# end if | |||
lmsg = "Group {}, schemes = {}" | |||
if run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG): | |||
if run_env.debug_on(): |
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.
See below, should be just run_env.verbose()
or even better a property run_env.verbose
. This way it's consistent with #512 where I added run_env.debug
.
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.
changed!
scripts/ccpp_suite.py
Outdated
@@ -490,7 +490,7 @@ def write(self, output_dir, run_env): | |||
(calling the group caps one after another)""" | |||
# Set name of module and filename of cap | |||
filename = '{module_name}.F90'.format(module_name=self.module) | |||
if run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG): | |||
if run_env.debug_on(): |
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.
Ditto
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.
changed!
cap.write("", 0) | ||
cap.write("! Dummy arguments", 2) | ||
cap.comment("Update constituent field info from <const_array>", 2) | ||
cap.blank_line() |
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.
These (new?) additions blank_line
and comment
are useful!
scripts/ddt_library.py
Outdated
if ((not recur) and | ||
run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG)): | ||
run_env.debug_on()): |
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.
run_env.debug_on()): | |
run_env.verbose()): |
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.
changed!
scripts/suite_objects.py
Outdated
@@ -1271,7 +1287,7 @@ def __init__(self, index_name, context, parent, run_env, items=None): | |||
# self._local_dim_name is the variable name for self._dim_name | |||
self._local_dim_name = None | |||
super().__init__(index_name, context, parent, run_env) | |||
if run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG): | |||
if run_env.debug_on(): |
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.
if run_env.debug_on(): | |
if run_env.verbose(): |
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.
changed!
end if | ||
|
||
end function ccp_is_initialized | ||
end function ccp_is_instantiated |
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 have a feeling that I asked this question 2 +/- years ago when the constituents where added originally. What does ccp
and ccpt
stand for? Should we be concerned about confusion with ccpp
that also shows up as acronym in the same part of the code? (This is just for my curiosity or for the future, not really related to this PR)
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.
Sorry, this is just me trying to save space with recursive acronyms (hey, I remember the first infinintely-recursive acronym I ran across, GNU, back when it was still pretty new in 1984).
I figured they are harmless because they are all defined within a type with a well defined name so it should be clear what they are for. However, for the record:
CCP
stands for C
cpp_C
onstituent_P
roperties_t
and
CCPT
stands for C
cpp_C
onstituent_prop_P
tr_T
These acronyms should not show up outside this module (can they even be called from outside?).
|
||
if (associated(this%prop)) then | ||
call this%prop%vertical_dimension(dimname) | ||
is_2d = len_trim(dimname) == 0 |
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.
Another point: While many models move to one horizontal dimension and one vertical dimension, we all have a basic understanding that 2d is something on a surface and 3d something in a volume, therefore this makes sense.
I second this. I think the name probably came from the name of the logging level but it is certainly verbose! |
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.
9db9477 looks good, thanks!
@peverwhee We were wondering in our tag up today when/if you were planning to make the |
@climbfuji Yes! I will try to finish up addressing the remaining comments tomorrow. |
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.
Thanks for making the debug_on -> verbose change!
Summary
Updates to bring in constituents object and constituent properties object.
Description
User interface changes?: Yes
Can now optionally add constituent via metadata property advected=True
Addresses #282
Testing: