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

Function arguments in examples #67

Closed
miguelcorralesg opened this issue Oct 8, 2024 · 5 comments
Closed

Function arguments in examples #67

miguelcorralesg opened this issue Oct 8, 2024 · 5 comments

Comments

@miguelcorralesg
Copy link

Hi,
 
I wanted to point out that in some of the functions in your examples (such as the objective_function), there are instances where undefined variables (likely global variables) are used instead of being passed as function arguments. Is this intentional?
 

function objective_function(model, state, Δt, step_i, forces)
    grat = JutulDarcy.compute_well_qoi(model, state, forces, :Producer, SurfaceGasRateTarget)
    return Δt * grat / (inj_rate * total_time)
end

 
 
For example, inj_rate and total_time are used but not defined within the function or passed as arguments.

@moyner
Copy link
Member

moyner commented Oct 8, 2024

Hi,

This is intentional in the sense that the example is intended to be as compact as possible, with the objective function being a bit of a "quick and dirty" setup that also hard-codes the name of the producer in the second line.

These variables are "captured" from the surrounding code in the example, so they are in a sense globals (or at least script level globals). It is a convenient way to use the script variables inside the objective to avoid recomputing them from the forces inside the objective. We can avoid globals by making this a function for the closure instead:

function get_objective(inj_rate, total_time)
    function objective_function(model, state, Δt, step_i, forces)
        grat = JutulDarcy.compute_well_qoi(model, state, forces, :Producer, SurfaceGasRateTarget)
        return Δt * grat / (inj_rate * total_time)
    end

    return objective_function
end

obj = get_objective(inj_rate, total_time)

This is required if you place the objective function inside a module, which is typically what you want to do if the same objective will be reused outside a single script. This can also help with performance in some cases.

Hope this helps,
Olav

@miguelcorralesg
Copy link
Author

Hi Olav,

Thank you so much for your prompt and helpful response—I really appreciate your clarification!

I have another question that I hope you can assist with. Currently, I'm trying to obtain the gradients for just one model parameter with respect to an objective function. I'm following the script for sensitivities you provided, and here's what I'm doing:

First, I calculate all the gradients:

data_domain_with_gradients = JutulDarcy.reservoir_sensitivities(case, results, objective_function, include_parameters = true)

Then, I extract the specific gradient I'm interested in:

∂K = data_domain_with_gradients[:permeability]

Is there a way to avoid calculating the gradients for all model parameters and instead compute only the gradient for the parameter of interest (in this case, permeability)?

Thanks again for your time and assistance!

@moyner
Copy link
Member

moyner commented Oct 8, 2024

Glad to hear that it helped!

If you want to only compute a subset of sensitivities, this is supported by the lower level interface by passing of a config Dict. This is seen in this example under the configuration part.

The config exists as a keyword argument for the high level interface you are using, but it is currently not working since some of the internal code assumes that everything is active:

cfg = optimization_config(model, parameters)
for (model_key, model_prm) in pairs(cfg)
    for (k, v) in pairs(model_prm)
        v[:active] = k == :Transmissibilities
    end
end

data_domain_with_gradients = JutulDarcy.reservoir_sensitivities(case, result, objective_function, include_parameters = true, config = cfg) # errors at the moment

I think it is not too difficult to fix this. Note that the cost of computing gradients in JutulDarcy is mostly proportional to the number of cells in the domain and not actually the number of parameters. So I do not think there will be too much performance difference between what you are doing and the same code with some of the parameters turned off.

@moyner
Copy link
Member

moyner commented Oct 8, 2024

Actually, that part does seem to work:

cfg = optimization_config(model, parameters)
res_cfg = cfg[:Reservoir]
for (k, v) in pairs(res_cfg)
    v[:active] = k == :Transmissibilities
end

data_domain_with_gradients = JutulDarcy.reservoir_sensitivities(case, result, objective_function, include_parameters = true, config = res_cfg)

The full set will still be output, but this will remove some of the numerical parameters from the internal calls.

@miguelcorralesg
Copy link
Author

Thank you so much!

I just tested it, and in terms of numerical results, everything is consistent. As for the performance, I didn't notice any difference in a small model. I'll likely need to test it on a larger model (maybe?) to see the impact. As you mentioned, the overhead is related to the number of dimensions so probably no a huge impact.

Thanks again for your help!

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

No branches or pull requests

2 participants