Closed benbovy closed 4 years ago
Alternative names for DimMapping
:
ModelDim
SurrogateDim
Another option:
Such a function would take a process class + a mapping old->new dimension labels and would return a new process class with replaced labels (looking at all declared variables).
model_grid2d = Model({'topo': SurfaceTopography})
model_profile1d = Model({
'topo': xs.replace_dims(SurfaceTopography, {('y', 'x'): 'x'})
})
model_mesh = Model({
'topo': xs.replace_dims(SurfaceTopography, {('y', 'x'): 'nodes'})
})
This is very simple and elegant from the API point of view. To avoid broken process dependencies , the returned class may just be a subclass of the input class (if #45 is merged). This option thus looks like option 1 but with less code to write.
With option 4, xs.replace_dims
could also accept a Model
object (and return another Model object
), for replacing dimensions throughout all the processes in a model:
model_profile1d = xs.replace_dims(model_grid2d, {('y', 'x'): 'x'})
Closing this as I'm not very satisfied by any of the options above. #44 is a better approach IMO, that lefts users do what they want (except that it wouldn't work well with auto-generated docstrings #3).
If anyone else reads this and wants to share thoughts, feel free to comment, I can reopen it later.
Some processes are generic enough to be re-used in contexts where the (space) domain of a model even differs by its number of dimensions. Take for example the following process class:
It would be nice to just re-use this class for driving the evolution of either a 1-d profile (e.g., river channel), a 2-d regular grid (raster DEM) or an irregular mesh. Unfortunately, the class above doesn't allow that flexibility because of the hard-coded
dims=('y', 'x')
for the elevation variable.There are different options to overcome this limitation:
1. Use class inheritance
Create an inherited process class for each case and redefine
dims
for the relevant variables , e.g.,This has the advantage to be explicit. We can adapt the class docstrings and variable
description
depending on the context. A downside is that we need to do create a new process class for every case and every generic process. We might end up with a lot of classes. Also, in the example above we only need to redefine one variable but there might be processes where many variables would need to be redefined. We might end up with a lot of code duplication.2. Set
dims
so that it allows all possible dimension labelsFor example:
This is straightforward (nothing specific to implement), but also confusing and error-prone. It doesn't tell which of these dimension(s) labels are actually used in the model where the process is included.
3. Set
dims
so that dimension labels are defined at model creation using a mappingFor example:
when (an instance of) the
SurfaceTopography
process is attached to a model,xs.DimMapping('space')
will try to get and return the actual dimension labels from the'space'
key of a mapping that is somehow stuck to aModel
instance. This could be done, e.g., by adding an argument toModel()
, e.g.,map_dims
is optional (empty mapping by default). If 'space' is not found in mapping keys, thedefault
value set for thexs.DimMapping
object is returned. If no default value is set, then aKeyError
is raised.It might make sense to somehow stick such a mapping to a specific process class, e.g., the one that defines the grid, so that when this class is included in a model, the latter automatically use it.
This option has several advantages:
DimMapping
for thatfor process classes that use this feature but are maintained in different code bases, it would allow something like this:
possible issues:
Model.update_processes
?