palma-ice / yelmo

Yelmo ice-sheet model code base
GNU General Public License v3.0
14 stars 3 forks source link

Generalized model output routines #4

Open alex-robinson opened 2 months ago

alex-robinson commented 2 months ago

Currently there are a few output routines defined in src/yelmo_io.f90 and in src/yelmo_regions.f90. The output routine in src/yelmo_regions.f90 is used for writing integrated/averaged quantities over a specific regional domain defined by a mask. This is not the topic here.

The issue is that src/yelmo_io.f90 does not contain a general routine to output model data for the whole domain or a subset. I think ideally, such a routine would have the following features:

  1. The user can specify the spatial extent to output, which can be a subset of xc/yc ranges. By default, the routine could output the entire domain.
  2. The user can specify a list of variables of interest to write as an argument. This would make the routine useful for many different cases.

It should be noted that such a writing routine needs an accompanying write initialization routine. Usually the latter is called at the start of a program and the former is called several times during timestepping. An initialization routine already exists and is pretty general, but may need to be adapted to allow feature 1 above.

Any other features needed for this to work well and be general?

JanJereczek commented 2 months ago

Going along your suggestion, I think it would be very beneficial if the output routine would loop over the variables specified in a (separate) namelist. This would make the code much shorter, easier to maintain and we wouldn't need to recompile the code each time we change the output structure. A pseudo-code example:

vars = [u, v, H, z]
varnames = [...]
varlongnames = [...]
units = [...]
fortranname = [ylmo%u, ylmo%v, ylmo%H, ylmo%z]
selectvars = [1, 2, 3]

for i in selectvars
    write(filename, varnames[i], parse(fortranname[i]), units = units[j], long_name = varlongnames[i], ...)
end

In the example above, this would only write out u, v, H based on selectvars, which can be easily modified by the user. The main issue I face here is that I don't know how to perform the parse(fortranname[i])step. Do you know that @alex-robinson? And do you agree with the basic idea?

alex-robinson commented 2 months ago

Yes, what you propose would be a great solution. But it exposes one of Fortran's fatal flaws in this instance. As far as I know, it is not possible to make a "list" of variables. They can only be defined in derived types with specific names (as with the Yelmo derived types themselves). Then, there is no way to call a variable within a derived type from a string name, or to "parse" it. The only way to access ylmo%dyn%now%ux is to explicitly write it like that.

The only way I've thought of doing it before, is a brute force method, which is probably why I never got around to implementing it:

Psuedo-code:

vanames = ["ux","uy",...\ ! Passed as an argument to routine

for varnm in varnames
    select case(trim(varnm))
        case("ux")
            call nc_write(filename,varnm,ylmo%dyn%now%ux,...)
        case("uy")
            call nc_write(filename,varnm,ylmo%dyn%now%ux,...)
        ...
        case DEFAULT
            ! Throw error - variable name not recognized
    end select
end

And have one writing case for every possible Yelmo variable with case DEFAULT throwing an error if the variable name does not exist).

The resulting functionality would be the same as what you propose, but the code would be much more verbose of course. It might be a good solution, and a number of common variables could be quickly built in. Then we could fill it in as we go.

JanJereczek commented 2 months ago

Makes sense, parsing strings to code should only be possible in dynamic programming languages. Looks like we are aiming at a more "brute force" solution then!