-
Notifications
You must be signed in to change notification settings - Fork 12
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
Wrapper of options #471
Wrapper of options #471
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.
Thanks very much for doing this Tim, I think this will enable us to lots of exciting things!
I think the methods for the MixedOptions
wrapper look perfect and I think it's the right thing to have a new test, which also looks good.
However (as I've written in a detailed comment) I think we actually might want to separate the new object into two objects:
MixedFSOptions
to live inconfiguration.py
MixedFSWrapper
to live inwrappers.py
and would be very close to what you've implemented already
The reason for this is that it keeps the code structure used for the existing Options
/Wrappers
.
My other thought (as I've written in another comment) is about whether we can avoid having the mixed_options
property for wrapper objects?
I'm very happy to discuss these things offline.
gusto/time_discretisation.py
Outdated
self.wrapper = RecoveryWrapper(self, options) | ||
elif self.wrapper_name == "supg": | ||
self.wrapper = SUPGWrapper(self, options) | ||
if type(options) == MixedOptions: |
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 type(options) == MixedOptions: | |
if type(options) is MixedOptions: |
I think when type checking, it's slightly better to use is
rather than ==
gusto/time_discretisation.py
Outdated
elif self.wrapper_name == "supg": | ||
self.wrapper = SUPGWrapper(self, options) | ||
if type(options) == MixedOptions: | ||
self.wrapper = options |
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.
Not that what you've done isn't a sensible way of implementing this, but it's occurred to me that it isn't quite consistent with how the existing wrappers work.
At the moment we have two broad types of objects: Options
and Wrappers
, and I think what we've done here is mix up the two a bit.
So now I'm thinking the best way of mirroring this structure for mixed function spaces would be to have:
MixedFSOptions
which is defined inconfiguration.py
and is essentially a glorified dictionaryMixedFSWrapper
which is defined inwrappers.py
and has the same methods as otherWrapper
objects
assert isinstance(self.options, RecoveryOptions), \ | ||
'Embedded DG wrapper can only be used with Recovery Options' | ||
'Recovery wrapper can only be used with Recovery Options' |
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 correcting this!
else: | ||
x_in_sub.assign(field) | ||
|
||
def post_apply(self, x_out): |
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 method looks exactly right to me
self.x_out = Function(self.function_space) | ||
|
||
def pre_apply(self, x_in): | ||
""" |
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 method looks exactly right to me
gusto/wrappers.py
Outdated
@@ -33,6 +33,9 @@ def __init__(self, time_discretisation, wrapper_options): | |||
self.time_discretisation = time_discretisation | |||
self.options = wrapper_options | |||
self.solver_parameters = None | |||
self.idx = None | |||
self.mixed_options = False |
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 a little bit uncertain about having a general Wrapper
object know about mixed_options
...
It feels like the inheritance of properties could be the wrong way around, as a MixedFSWrapper
has several subwrappers, but the subwrappers then need to know that they are subwrapper. Ideally it would be better if they don't know that they are subwrappers!
Looking below at why you've had to do this, it's to set the function space. I'm just wondering if we can find a cleaner way to set original_space
? Maybe idx
could be an argument to the __init__
or setup
method of the base Wrapper
class?
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 again for this Tim. I think we're nearly there.
I understand why you needed to introduce the tracer_fs
to the other wrappers. I think we could go a bit further with this by:
- renaming it as
original_space
(since it might not always be used with tracers!) - having it set more explicitly through the
setup
method, which will help make the code clearer
gusto/wrappers.py
Outdated
@@ -161,13 +169,19 @@ class RecoveryWrapper(Wrapper): | |||
def setup(self): | |||
"""Sets up function spaces and fields needed for this wrapper.""" | |||
|
|||
print(self.options) |
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 just needs removing!
gusto/wrappers.py
Outdated
@@ -33,6 +33,7 @@ def __init__(self, time_discretisation, wrapper_options): | |||
self.time_discretisation = time_discretisation | |||
self.options = wrapper_options | |||
self.solver_parameters = None | |||
self.tracer_fs = 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.
self.tracer_fs = None | |
self.original_space = None |
gusto/time_discretisation.py
Outdated
|
||
self.residual = self.wrapper.label_terms(self.residual) | ||
subwrapper.setup() |
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.
subwrapper.setup() | |
subwrapper.setup(self.equation.spaces[field_idx]) |
gusto/time_discretisation.py
Outdated
field_idx = equation.field_names.index(field) | ||
|
||
# Store the original space of the tracer | ||
subwrapper.tracer_fs = self.equation.spaces[field_idx] |
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.
subwrapper.tracer_fs = self.equation.spaces[field_idx] |
gusto/time_discretisation.py
Outdated
map_if_true=replace_test_function(new_test_mixed)) | ||
|
||
else: | ||
self.wrapper.setup() |
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.
self.wrapper.setup() | |
self.wrapper.setup(self.fs) |
I think this should be self.fs
but I'm not sure...? Maybe it is just 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.
I think we would want this to be None
, as we want self.original_space=None
when not using the mixed wrapper.
gusto/wrappers.py
Outdated
original_space = self.tracer_fs | ||
else: | ||
original_space = self.time_discretisation.fs | ||
|
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.
Again this snippet changes to
self.original_space = original_space
gusto/wrappers.py
Outdated
@@ -184,10 +198,17 @@ def setup(self): | |||
# Internal variables to be used | |||
# -------------------------------------------------------------------- # | |||
|
|||
self.x_in_tmp = Function(self.time_discretisation.fs) | |||
if self.tracer_fs is not 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.
if self.tracer_fs is not None: | |
if self.original_space is not None: |
gusto/wrappers.py
Outdated
@@ -184,10 +198,17 @@ def setup(self): | |||
# Internal variables to be used | |||
# -------------------------------------------------------------------- # | |||
|
|||
self.x_in_tmp = Function(self.time_discretisation.fs) | |||
if self.tracer_fs is not None: | |||
self.x_in_tmp = Function(self.tracer_fs) |
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.
self.x_in_tmp = Function(self.tracer_fs) | |
self.x_in_tmp = Function(self.original_space) |
gusto/wrappers.py
Outdated
self.x_in = Function(self.function_space) | ||
self.x_out = Function(self.function_space) | ||
if self.time_discretisation.idx is None: | ||
|
||
if self.tracer_fs is not 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.
if self.tracer_fs is not None: | |
if self.original_space is not None: |
gusto/wrappers.py
Outdated
if self.time_discretisation.idx is None: | ||
|
||
if self.tracer_fs is not None: | ||
self.x_projected = Function(self.tracer_fs) |
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.
self.x_projected = Function(self.tracer_fs) | |
self.x_projected = Function(self.original_space) |
Thanks for the comments, Tom! |
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 Tim for doing all of this! I think this is ready now.
This adds the ability to apply the different wrapper options of EmbeddedDG and Recovery to different tracers/prognostic variables in the transport step. This will enable tracers to live in different spaces and will fix a current compatibility issue where a DG and theta tracer can't both be used within the CoupledTransportEquation with respective sublimiters, as the ThetaLimiter requires the use of EmbeddedDG.
This is implemented by defining a new dictionary wrapper 'MixedOptions' which will store the individual wrapper options of the transported variables. Tracers have different options, or may have none defined at all. Currently, SUPG has not been implemented as an option for a tracer.
A new test file, 'test_mixed_fs_options.py' is made to test combinations of these different options for two tracers solved with a CoupledTransportEquation. Limiters are also used on all these tests. As this new script includes the DG1-DG1_equispaced combination, this test has been removed from the 'test_limiters.py' file.