int NRELPythonApplicInterface::derived_map_ac(const Dakota::String& ac_name)
  {
    printf ("entered python:derived_map_ac\n");

  // probably need to convert all of the following with SWIG or Boost!!
  // (there is minimal error checking for now)
  // need to cleanup ref counts on Python objects
  int fail_code = 0;

  // probably want to load the modules and functions at construction time, incl.
  // validation and store the objects for later, but need to resolve use of
  // analysisDriverIndex

  // for now we presume a single analysis component containing module:function
  const std::string& an_comp = analysisComponents[analysisDriverIndex][0];
  size_t pos = an_comp.find(":");
  std::string module_name = an_comp.substr(0,pos);
  std::string function_name = an_comp.substr(pos+1);

  printf ("importing the module %s\n", module_name.c_str());
  // import the module and function and test for callable
  PyObject *pModule = PyImport_Import(PyString_FromString(module_name.c_str()));
  if (pModule == NULL) {
    Cerr << "Error (Direct:Python): Failure importing module " << module_name 
	 << ".\n                       Consider setting PYTHONPATH."
	 << std::endl;
    abort_handler(-1);
  }
  printf ("imported the module\n");

  // Microsoft compiler chokes on this:
  //  char fn[function_name.size()+1];
  char *fn = new char[function_name.size()+1];
  strcpy(fn, function_name.c_str());
  PyObject *pFunc = PyObject_GetAttrString(pModule, fn);
  if (!pFunc || !PyCallable_Check(pFunc)) {
    Cerr << "Error (Direct:Python): Function " << function_name  << "not found "
	 << "or not callable" << std::endl;
    abort_handler(-1);
  }
  delete fn;

  // must use empty tuple here to pass to function taking only kwargs
  PyObject *pArgs = PyTuple_New(0);
  PyObject *pDict = PyDict_New();

  // convert DAKOTA data types to Python objects (lists and/or numpy arrays)
  PyObject *cv, *cv_labels, *div, *div_labels, *drv, *drv_labels,
    *av, *av_labels, *asv, *dvv;
  python_convert(xC, &cv);
  python_convert(xCLabels, &cv_labels);
  python_convert_int(xDI, xDI.length(), &div);
  python_convert(xDILabels, &div_labels);
  python_convert(xDR, &drv);
  python_convert(xDRLabels, &drv_labels);
  python_convert(xC, xDI, xDR, &av);
  python_convert(xCLabels, xDILabels, xDRLabels, &av_labels);
  python_convert_int(directFnASV, directFnASV.size(), &asv);
  python_convert_int(directFnDVV, directFnASV.size(), &dvv);
  // TO DO: analysis components

  // assemble everything into a dictionary to pass to user function
  // this should eat references to the objects declared above
  PyDict_SetItem(pDict, PyString_FromString("variables"), 
		 PyInt_FromLong((long) numVars));
  PyDict_SetItem(pDict, PyString_FromString("functions"), 
		 PyInt_FromLong((long) numFns)); 
  PyDict_SetItem(pDict, PyString_FromString("cv"), cv);
  PyDict_SetItem(pDict, PyString_FromString("cv_labels"), cv_labels);
  PyDict_SetItem(pDict, PyString_FromString("div"), div);
  PyDict_SetItem(pDict, PyString_FromString("div_labels"), div_labels);
  PyDict_SetItem(pDict, PyString_FromString("drv"), drv);
  PyDict_SetItem(pDict, PyString_FromString("drv_labels"), drv_labels);
  PyDict_SetItem(pDict, PyString_FromString("av"), av);
  PyDict_SetItem(pDict, PyString_FromString("av_labels"), av_labels);
  PyDict_SetItem(pDict, PyString_FromString("asv"), asv);
  PyDict_SetItem(pDict, PyString_FromString("dvv"), dvv);
  // Does not appear to exist in Windows version of Dakota, and I don't recall using it:
  //  PyDict_SetItem(pDict, PyString_FromString("fnEvalId"), 
  //		 PyInt_FromLong((long) fnEvalId)); 

  ///////
  ///PyDict_SetItem(pDict, PyString_FromString("data"), gData); 
  printf ("I have data at : %lx,   NOW WHAT do I do!?\n",  pUserData); 
  ///////
  bp::object * tmp2 = NULL;
  if (pUserData != NULL)
    {
      printf ("Setting raw PyObject in dict\n");
      tmp2 = (bp::object*)pUserData;
      PyDict_SetItem(pDict, PyString_FromString("user_data"), 
		     tmp2->ptr());
    }
  else
    printf("no data\n");


  // perform analysis
  if (outputLevel > NORMAL_OUTPUT)
    Cout << "Info (Direct:Python): Calling function " << function_name 
	 << " in module " << module_name << "." << std::endl;
  PyObject *retVal = PyObject_Call(pFunc, pArgs, pDict);
  Py_DECREF(pDict);
  Py_DECREF(pArgs);    
  Py_DECREF(pFunc);
  Py_DECREF(pModule);
  if (!retVal) {
    // TODO: better error reporting from Python
    Cerr << "Error (Direct:Python): Unknown error evaluating python "
	 << "function." << std::endl;
    abort_handler(-1);
  }
      
  bool fn_flag = false;
  for (int i=0; i<numFns; ++i)
    if (directFnASV[i] & 1) {
      fn_flag = true;
      break;
    }

  // process return type as dictionary, else assume list of functions only
  if (PyDict_Check(retVal)) {
    // or the user may return a dictionary containing entires fns, fnGrads,
    // fnHessians, fnLabels, failure (int)
    // fnGrads, e.g. is a list of lists of doubles
    // this is where Boost or SWIG could really help
    // making a lot of assumptions on types being returned
    PyObject *obj;
    if (fn_flag) {
      if ( !(obj = PyDict_GetItemString(retVal, "fns")) ) {
	Cerr << "Python dictionary must contain list 'fns'" << std::endl;
	Py_DECREF(retVal);
	abort_handler(-1);
      }
      if (!python_convert(obj, fnVals, numFns)) {
	Py_DECREF(retVal);
	abort_handler(-1);
      }
    }
    if (gradFlag) {
      if ( !(obj = PyDict_GetItemString(retVal, "fnGrads")) ) {
	Cerr << "Python dictionary must contain list 'fnGrads'" << std::endl;
	Py_DECREF(retVal);
	abort_handler(-1);
      }
      if (!python_convert(obj, fnGrads)) {
	Py_DECREF(retVal);
	abort_handler(-1);
      }
    }
    if (hessFlag) {
      if ( !(obj = PyDict_GetItemString(retVal, "fnHessians")) ) {
	Cerr << "Python dictionary must contain list 'fnHessians'" << std::endl;
	Py_DECREF(retVal);
	abort_handler(-1);
      }
      if (!python_convert(obj, fnHessians)){
	Py_DECREF(retVal);
	abort_handler(-1);
      }
    }
    // optional returns
    if (obj = PyDict_GetItemString(retVal, "failure"))
      fail_code = PyInt_AsLong(obj);

    if (obj = PyDict_GetItemString(retVal, "fnLabels")) {
      if (!PyList_Check(obj) || PyList_Size(obj) != numFns) {
	Cerr << "'fnLabels' must be list of length numFns." << std::endl;
	Py_DECREF(retVal);
	abort_handler(-1);
      }
      for (int i=0; i<numFns; ++i)
	fnLabels[i] = PyString_AsString(PyList_GetItem(obj, i));
    }
  }
  else {
    // asssume list/numpy array containing only functions
    if (fn_flag)
      python_convert(retVal, fnVals, numFns);
  }
  Py_DECREF(retVal);

  return(fail_code);    
  }
Esempio n. 2
0
int PythonInterface::python_run(const String& ac_name)
{
  // probably need to convert all of the following with SWIG or Boost!!
  // (there is minimal error checking for now)
  // need to cleanup ref counts on Python objects
  int fail_code = 0;

  // probably want to load the modules and functions at construction time, incl.
  // validation and store the objects for later, but need to resolve use of
  // analysisDriverIndex

  // must use empty tuple here to pass to function taking only kwargs
  PyObject *pArgs = PyTuple_New(0);
  PyObject *pDict = PyDict_New();

  // convert DAKOTA data types to Python objects (lists and/or numpy arrays)
  PyObject *cv, *cv_labels, *div, *div_labels, *drv, *drv_labels,
    *av, *av_labels, *asv, *dvv, *an_comps;
  python_convert(xC, &cv);
  python_convert_strlist(xCLabels, &cv_labels);
  python_convert_int(xDI, xDI.length(), &div);
  python_convert_strlist(xDILabels, &div_labels);
  python_convert(xDR, &drv);
  python_convert_strlist(xDRLabels, &drv_labels);
  python_convert(xC, xDI, xDR, &av);
  python_convert(xCLabels, xDILabels, xDRLabels, &av_labels);
  python_convert_int(directFnASV, directFnASV.size(), &asv);
  python_convert_int(directFnDVV, directFnDVV.size(), &dvv);
  // send analysis components, or an empty list
  if (analysisComponents.size() > 0)
    python_convert_strlist(analysisComponents[analysisDriverIndex], &an_comps);
  else
    an_comps = PyList_New(0);

  // assemble everything into a dictionary to pass to user function
  // this should eat references to the objects declared above
  PyDict_SetItem(pDict, PyString_FromString("variables"), 
		 PyInt_FromLong((long) numVars));
  PyDict_SetItem(pDict, PyString_FromString("functions"), 
		 PyInt_FromLong((long) numFns)); 
  PyDict_SetItem(pDict, PyString_FromString("cv"), cv);
  PyDict_SetItem(pDict, PyString_FromString("cv_labels"), cv_labels);
  PyDict_SetItem(pDict, PyString_FromString("div"), div);
  PyDict_SetItem(pDict, PyString_FromString("div_labels"), div_labels);
  PyDict_SetItem(pDict, PyString_FromString("drv"), drv);
  PyDict_SetItem(pDict, PyString_FromString("drv_labels"), drv_labels);
  PyDict_SetItem(pDict, PyString_FromString("av"), av);
  PyDict_SetItem(pDict, PyString_FromString("av_labels"), av_labels);
  PyDict_SetItem(pDict, PyString_FromString("asv"), asv);
  PyDict_SetItem(pDict, PyString_FromString("dvv"), dvv);
  PyDict_SetItem(pDict, PyString_FromString("analysis_components"), an_comps);
  PyDict_SetItem(pDict, PyString_FromString("currEvalId"), 
		 PyInt_FromLong((long) currEvalId));


  // The active analysis_driver is passed in ac_name (in form
  // module:function); could make module optional.  We pass any
  // analysis components as string arguments to the Python function.
  size_t pos = ac_name.find(":");
  std::string module_name = ac_name.substr(0,pos);
  std::string function_name = ac_name.substr(pos+1);
  if (module_name.size() == 0 || function_name.size() == 0) {
    Cerr << "\nError: invalid Python analysis_driver '" << ac_name
	 << "'\n       Should have form 'module:function'." << std::endl;
    abort_handler(-1);
  }

  // import the module and function and test for callable
  PyObject *pModule = PyImport_Import(PyString_FromString(module_name.c_str()));
  if (pModule == NULL) {
    Cerr << "Error (PythonInterface): Failure importing module '" 
	 << module_name  << "'.\n                         Consider setting "
	 << "PYTHONPATH." << std::endl;
    abort_handler(-1);
  }

  PyObject *pFunc = PyObject_GetAttrString(pModule, function_name.c_str());
  if (!pFunc || !PyCallable_Check(pFunc)) {
    Cerr << "Error (PythonInterface): Function '" << function_name  
	 << "' not found or not callable" << std::endl;
    abort_handler(-1);
  }

  // perform analysis
  if (outputLevel > NORMAL_OUTPUT)
    Cout << "Info (PythonInterface): Calling function " << function_name 
	 << " in module " << module_name << "." << std::endl;
  PyObject *retVal = PyObject_Call(pFunc, pArgs, pDict);
  if (!retVal) {
    // TODO: better error reporting from Python
    Cerr << "Error (PythonInterface): Unknown error evaluating python "
	 << "function." << std::endl;
    abort_handler(-1);
  }

  Py_DECREF(pDict);
  Py_DECREF(pArgs);    
  Py_DECREF(pFunc);
  Py_DECREF(pModule);

  // process the return data

  bool fn_flag = false;
  for (int i=0; i<numFns; ++i)
    if (directFnASV[i] & 1) {
      fn_flag = true;
      break;
    }

  // process return type as dictionary, else assume list of functions only
  if (PyDict_Check(retVal)) {
    // or the user may return a dictionary containing entires fns, fnGrads,
    // fnHessians, fnLabels, failure (int)
    // fnGrads, e.g. is a list of lists of doubles
    // this is where Boost or SWIG could really help
    // making a lot of assumptions on types being returned
    PyObject *obj;
    if (fn_flag) {
      if ( !(obj = PyDict_GetItemString(retVal, "fns")) ) {
	Cerr << "Python dictionary must contain list 'fns'" << std::endl;
	Py_DECREF(retVal);
	abort_handler(-1);
      }
      if (!python_convert(obj, fnVals, numFns)) {
	Py_DECREF(retVal);
	abort_handler(-1);
      }
    }
    if (gradFlag) {
      if ( !(obj = PyDict_GetItemString(retVal, "fnGrads")) ) {
	Cerr << "Python dictionary must contain list 'fnGrads'" << std::endl;
	Py_DECREF(retVal);
	abort_handler(-1);
      }
      if (!python_convert(obj, fnGrads)) {
	Py_DECREF(retVal);
	abort_handler(-1);
      }
    }
    if (hessFlag) {
      if ( !(obj = PyDict_GetItemString(retVal, "fnHessians")) ) {
	Cerr << "Python dictionary must contain list 'fnHessians'" << std::endl;
	Py_DECREF(retVal);
	abort_handler(-1);
      }
      if (!python_convert(obj, fnHessians)){
	Py_DECREF(retVal);
	abort_handler(-1);
      }
    }
    // optional returns
    if (obj = PyDict_GetItemString(retVal, "failure"))
      fail_code = PyInt_AsLong(obj);

    if (obj = PyDict_GetItemString(retVal, "fnLabels")) {
      if (!PyList_Check(obj) || PyList_Size(obj) != numFns) {
	Cerr << "'fnLabels' must be list of length numFns." << std::endl;
	Py_DECREF(retVal);
	abort_handler(-1);
      }
      for (int i=0; i<numFns; ++i)
	fnLabels[i] = PyString_AsString(PyList_GetItem(obj, i));
    }
  }
  else {
    // asssume list/numpy array containing only functions
    if (fn_flag)
      python_convert(retVal, fnVals, numFns);
  }
  Py_DECREF(retVal);

  return(fail_code);
}