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

Move metadata to BMI helper functions #489

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

SouthEndMusic
Copy link
Contributor

Issue addressed

Fixes #432

Explanation

I am working on some 'metaprogramming' functions to convert the metadata that is now present at the struct definitions into compact functions that can be used for the BMI:

function extract_metadata(line)
    metadata = split(line, "|")

    unit = if length(metadata) < 2
        "\"mm dt-1\""
    else
        metadata[2]
    end

    exchange = if length(metadata) < 3
        1
    else
        Int(metadata[3])
    end

    grid_type = if length(metadata) < 4
        "unstructured"
    else
        metadata[4]
    end

    grid_location = if length(metadata) < 5
        "node"
    else
        metadata[5]
    end

    (; unit, exchange, grid_type, grid_location)
end

function add_to_dict(dict, key, value)
    if key  keys(dict)
        dict[key] = []
    end
    push!(dict[key], value)
end

function build_bmi_function(function_name, type, dict)
    function_code = "function $function_name(::$type, var_name, name)" 

    if_ = "if"

    for (value, var_names) in dict
        # A functional smiley, my life is complete
        names = "(:" * join(var_names, ", :")
        if length(var_names) == 1
            names *= ",)"
        else
            names *= ")"
        end
        function_code *= "\n  $if_ var_name in $names\n      $value"
        if_ = "elseif"
    end

    function_code *= "\n  else\n      var_error(name)\n  end\nend"

    return function_code
end

function build_bmi_functions(input)
    lines = split(input, "\n")
    type = match(r"struct (.*?){", first(lines))[1]

    unit_dict = Dict{String, Vector{String}}()
    grid_type_dict = Dict{String, Vector{String}}()
    grid_location_dict = Dict{String, Vector{String}}()

    for line in lines
        line = replace(line, r"\s" => "")
        var_name = split(line, "::")[1]
        (occursin("|0", line) || line == "" || !occursin("::", line)) && continue
        metadata = extract_metadata(line)
        add_to_dict(unit_dict, metadata.unit, var_name)
        add_to_dict(grid_type_dict, metadata.grid_type, var_name)
        add_to_dict(grid_location_dict, metadata.grid_location, var_name)
    end

    get_var_units = build_bmi_function("get_var_units", type, unit_dict)
    get_var_location = build_bmi_function("get_var_location", type, grid_location_dict)

    (; get_var_units, get_var_location)
end

function strip_metadata(input)
    output = ""
    for line in split(input, "\n")
        if occursin("::", line)
            first_bar_loc = findfirst("|", line)[1]
            output *= line[1:first_bar_loc-1] * "\n"
        else
            output *= line * "\n"
        end
    end
    output
end

Using the model from run_flextopo.jl, I get desired behavior:

BMI.get_var_units(model, "vertical.wb_snow") # "mmdt-1"
BMI.get_var_location(model, "vertical.wb_snow") # "node"

@SouthEndMusic
Copy link
Contributor Author

@verseve before I refactor all relevant structs with this, let me know whether you this this is a good approach.

@SouthEndMusic SouthEndMusic marked this pull request as draft October 23, 2024 05:38
@verseve
Copy link
Member

verseve commented Oct 23, 2024

Hi @SouthEndMusic , thanks for looking into this and welcome to the wflow team 😃! I think this could be a nice approach if we want to stick to storing metadata in the structs. An alternative could be that we store this information somewhere else, also related to issue #481 (in that mapping we could also store metadata).

PR #486 has quite some struct changes (and removal of structs), so probably best to start implementing this (if we stick to metatdata in structs) when that PR has been reviewed and merged, I think.

@SouthEndMusic
Copy link
Contributor Author

SouthEndMusic commented Oct 23, 2024

Thanks (: I don't fully oversee #481, but my approach here is to remove all metadata from the structs, and move that data to functions only called in the relevant BMI functions. I have only applied this to the FLEXTOPO struct as a proof of concept.

To be clear: the functions above are not meant to become part of the source code. They are only meant to automate the code changes for this PR, and I posted them to show what I am doing.

@SouthEndMusic SouthEndMusic changed the title Generating get_var_* functions POC Move metadata to BMI helper functions Oct 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Metadata without macros
2 participants