MoDeNa-EUProject / MoDeNa

Software Framework for MOdelling of morphology DEvelopment of micro- and NAnostructures (MoDeNa)
17 stars 19 forks source link

unable to call function in library with arbitrary inputs and parameters from python #2

Open japaf opened 9 years ago

japaf commented 9 years ago

In the early version of Modena interface it was possible to call C function compiled in the database from python using pymodena.modena_function_call(). It was used to determine fitting parameters of a surrogate model in python if we had precalculated data from detailed model or just to compare results of surrogate and detailed model in a plot. As far as I know, there is no easy alternative now. Possible workaround is to just define python function and copy (and translate to python) the algorithm from C function of surrogate model into this python function. This is neither elegant nor convenient.

henrus commented 9 years ago

Yes, this has become MUCH easier with v0.5 since the core interface library is now loadable from python AND C. Just call the callModel function on SurrogateModel

inputs = {'a': 1; 'b': 2}
model=SurrogateModel.load('modelName')
outputs=model.callModel(inputs)

If you want to provide your own parameters, then you need to go little further down and do

inputs = {'a': 1; 'b': 2}
model=SurrogateModel.load('modelName')
cModel = modena.libmodena.modena_model_t(
    model=model,
    parameters=list(parameters)
)
in_i = list()
i = [0] * (1 + model.inputs_max_argPos())
for k, v in model.inputs.iteritems():
    i[v.argPos] = inputs[k]
outputs=cModel.call(in_i, i, checkBounds=False)

Note: You should not need to do this by hand. This case should be handled by the framework! But if this is really needed, I can provide an optional parameter argument option on callModel.

japaf commented 9 years ago

Thanks, this really is quite simple. As far as model fitting is concerned, I see this as only temporary solution.

I had to make a few adjustments, because model.inputs is MinMaxOpt object and doesn't have argPos key. Following code works for me:

inputs = {'a': 1, 'b': 2}
model=SurrogateModel.load('modelName')
cModel = modena.libmodena.modena_model_t(
    model=model,
    parameters=list(parameters)
)
in_i = list()
i = [0] * (1 + model.inputs_max_argPos())
for k, v in inputs.iteritems():
    i[model.inputs_argPos(k)] = inputs[k]
outputs=cModel.call(in_i, i, checkBounds=False)
sigveka commented 9 years ago

Just to put it into perspective, take look at a minimal working example:

Import modena modules

from modena import ForwardMappingModel, SurrogateModel, CFunction, libmodena
from fireworks import Firework, Workflow, LaunchPad
from fireworks.core.rocket_launcher import rapidfire

Define a surrogate function. NOTE: If following (long) error message starting along the lines of By not providing "FindMODENA.cmake" in CMAKE_MODULE_PATH this project has occurs, set the following environment variable: export CMAKE_PREFIX_PATH=${HOME}.

r = CFunction(
    Ccode= '''
#include "modena.h"
#include "math.h"

void idealGas
(
    const double* parameters,
    const double* inherited_inputs,
    const double* inputs,
    double *outputs
)
{
    const double p0 = inputs[0];
    const double T0 = inputs[1];

    const double R = parameters[0];

    outputs[0] = p0/R/T0;
}
''',
    # These are global bounds for the function
    inputs={
        'p0': { 'min': 0, 'max': 9e99, 'argPos': 0 },
        'T0': { 'min': 0, 'max': 9e99, 'argPos': 1 },
    },
    outputs={
        'rho0': { 'min': 9e99, 'max': -9e99, 'argPos': 0 },
    },
    parameters={
        'R': { 'min': 0.0, 'max': 9e99, 'argPos': 0 }
    },
)

Define a forward mapping model. A backward mapping model requires more inputs, but they are a member of the same base class (surrogateModel).

m1 = ForwardMappingModel(
    _id= 'idealGas',
    surrogateFunction= r,
    substituteModels= [ ],
    parameters= [ 287.0 ],
    inputs={
        'p0': { 'min': 0, 'max': 9e99 },
        'T0': { 'min': 0, 'max': 9e99 },
    },
    outputs={
        'rho0': {'min': 0, 'max': 9e99 },
    },
)

Initiate all surrogate models that have been defined using fireworks:

# set up the LaunchPad and reset it
launchpad = LaunchPad()
launchpad.reset('', require_password=False)

initFws = []
for m in SurrogateModel.get_instances():
    initFws.append(m.initialisationFw())

# store workflow and launch it locally
launchpad.add_wf(Workflow(initFws, {}, name="initialisation"))
rapidfire(launchpad)

Finally, call the surrogate model as the question was answered:

inputs = {'T0': 298, 'p0': 10000} # inputs key:val (argument position is arbitrary)
model=SurrogateModel.load('idealGas') # load model from database
cModel = modena.libmodena.modena_model_t(
    model=model
) 
in_i = list() # empty list 
i = [0] * (1 + model.inputs_max_argPos()) # input vector
for k, v in inputs.iteritems(): # iterate over keys in input
    i[model.inputs_argPos(k)] = inputs[k] # Place inputs in the correct position in 'input vector'

out=cModel.call(in_i, i, checkBounds=False) # call model to perform calculation
outputs = { key: out[val.argPos]  for key, val in model.surrogateFunction.outputs.iteritems() }

print outputs