diff --git a/src/plugins/python/geogate_phases_python.F90 b/src/plugins/python/geogate_phases_python.F90 index 11e15c2..2f59e9a 100644 --- a/src/plugins/python/geogate_phases_python.F90 +++ b/src/plugins/python/geogate_phases_python.F90 @@ -16,6 +16,7 @@ module geogate_phases_python use ESMF, only: ESMF_VM, ESMF_VMGet, ESMF_VMBarrier, ESMF_Mesh, ESMF_MeshGet use ESMF, only: ESMF_MAXSTR, ESMF_GEOMTYPE_GRID, ESMF_GEOMTYPE_MESH use ESMF, only: ESMF_UtilStringLowerCase, ESMF_FieldBundleIsCreated + use ESMF, only: ESMF_TraceRegionEnter, ESMF_TraceRegionExit use NUOPC, only: NUOPC_CompAttributeGet, NUOPC_GetAttribute use NUOPC_Model, only: NUOPC_ModelGet @@ -74,7 +75,7 @@ subroutine geogate_phases_python_run(gcomp, rc) type(ESMF_Clock) :: clock type(ESMF_VM) :: vm integer, save :: timeStep = 0 - logical :: impOnExpMesh + logical, save :: remapImportFields = .false. character(ESMF_MAXSTR) :: namespace character(ESMF_MAXSTR) :: cvalue, message, timeStr character(len=*), parameter :: subname = trim(modName)//':(geogate_phases_python_run) ' @@ -83,6 +84,9 @@ subroutine geogate_phases_python_run(gcomp, rc) rc = ESMF_SUCCESS call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO) + ! Enter trace region + call ESMF_TraceRegionEnter('geogate_phases_python_run') + ! Get internal state nullify(is_local%wrap) call ESMF_GridCompGetInternalState(gcomp, is_local, rc) @@ -124,11 +128,11 @@ subroutine geogate_phases_python_run(gcomp, rc) call NUOPC_CompAttributeGet(gcomp, name='ImportOnExportMesh', value=cvalue, & isPresent=isPresent, isSet=isSet, rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return - impOnExpMesh = .false. + remapImportFields = .false. if (isPresent .and. isSet) then - if (trim(cvalue) .eq. '.true.' .or. trim(cvalue) .eq. 'true' .or. trim(adjustl(cvalue)) .eq. 'T') impOnExpMesh = .true. + if (trim(cvalue) .eq. '.true.' .or. trim(cvalue) .eq. 'true' .or. trim(adjustl(cvalue)) .eq. 'T') remapImportFields = .true. end if - write(message, fmt='(A,L)') trim(subname)//' : ImportOnExportMesh = ', impOnExpMesh + write(message, fmt='(A,L)') trim(subname)//' : ImportOnExportMesh = ', remapImportFields call ESMF_LogWrite(trim(message), ESMF_LOGMSG_INFO) ! Set flag @@ -158,12 +162,13 @@ subroutine geogate_phases_python_run(gcomp, rc) call FB2Node(is_local%wrap%FBImp(n), "import", trim(is_local%wrap%compName(n)), myMeshImp(n), node, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return ! Interpolate import fields to export mesh and add them to node - if (impOnExpMesh) then + if (remapImportFields) then ! Interpolate import fields to export mesh and create new FB call FB_copy(is_local%wrap%FBImp(n), is_local%wrap%FBImpIntp(n), & 'FBImpOnExp'//trim(is_local%wrap%compName(n)), & is_local%wrap%meshExp, is_local%wrap%RHImp2Exp(n), rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return + ! Load content of FB to Conduit node call FB2Node(is_local%wrap%FBImpIntp(n), "import_on_export_grid", & trim(is_local%wrap%compName(n)), myMeshExp, node, rc=rc) @@ -223,6 +228,9 @@ subroutine geogate_phases_python_run(gcomp, rc) ! Increase time step timeStep = timeStep+1 + ! Exit trace region + call ESMF_TraceRegionExit('geogate_phases_python_run') + call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO) end subroutine geogate_phases_python_run @@ -253,6 +261,9 @@ subroutine FB2Node(FBin, parent, compName, myMesh, node, rc) rc = ESMF_SUCCESS call ESMF_LogWrite(subname//' called for '//trim(compName), ESMF_LOGMSG_INFO) + ! Enter trace region + call ESMF_TraceRegionEnter('FB2Node') + ! Add channel channel = conduit_node_fetch(node, "channels/"//trim(parent)//"/"//trim(compName)) @@ -339,9 +350,12 @@ subroutine FB2Node(FBin, parent, compName, myMesh, node, rc) nullify(farrayPtr) end do - ! Clean memeory + ! Clean memory deallocate(fieldNameList) + ! Exit trace region + call ESMF_TraceRegionExit('FB2Node') + call ESMF_LogWrite(subname//' done for '//trim(compName), ESMF_LOGMSG_INFO) end subroutine FB2Node @@ -371,6 +385,9 @@ subroutine Node2FB(FBin, compName, node, rc) rc = ESMF_SUCCESS call ESMF_LogWrite(subname//' called for '//trim(compName), ESMF_LOGMSG_INFO) + ! Enter trace region + call ESMF_TraceRegionEnter('Node2FB') + ! Create node with fields if (conduit_node_has_path(node, "data/fields")) then fields = conduit_node_fetch(node, "data/fields") @@ -419,6 +436,9 @@ subroutine Node2FB(FBin, compName, node, rc) end do end if + ! Exit trace region + call ESMF_TraceRegionExit('Node2FB') + call ESMF_LogWrite(subname//' done for '//trim(compName), ESMF_LOGMSG_INFO) end subroutine Node2FB diff --git a/src/plugins/python/geogate_python_interface.F90 b/src/plugins/python/geogate_python_interface.F90 index 540959d..42af2b5 100644 --- a/src/plugins/python/geogate_python_interface.F90 +++ b/src/plugins/python/geogate_python_interface.F90 @@ -1,11 +1,13 @@ module geogate_python_interface !----------------------------------------------------------------------------- - ! Void phase for Python interaction + ! Void phase for Python interaction !----------------------------------------------------------------------------- use ESMF, only: ESMF_LogWrite, ESMF_LOGMSG_INFO - use, intrinsic :: iso_c_binding, only : C_PTR, C_CHAR + use ESMF, only: ESMF_TraceRegionEnter, ESMF_TraceRegionExit + + use, intrinsic :: iso_c_binding, only : C_PTR, C_CHAR, C_NULL_CHAR implicit none @@ -14,25 +16,29 @@ module geogate_python_interface !----------------------------------------------------------------------------- interface - subroutine conduit_fort_to_py(nodeIn, py_script) bind(C, name="conduit_fort_to_py") + subroutine c_conduit_fort_to_py(nodeIn, py_script, node_in_name) bind(C, name="conduit_fort_to_py") use iso_c_binding implicit none type(C_PTR), value, intent(in) :: nodeIn character(kind=C_CHAR), intent(in) :: py_script(*) - end subroutine conduit_fort_to_py + character(kind=C_CHAR), intent(in) :: node_in_name(*) + end subroutine c_conduit_fort_to_py - function c_conduit_fort_from_py(py_script) result(resOut) bind(C, name="conduit_fort_from_py") + function c_conduit_fort_from_py(py_script, node_out_name) result(resOut) bind(C, name="conduit_fort_from_py") use iso_c_binding implicit none character(kind=C_CHAR), intent(in) :: py_script(*) + character(kind=C_CHAR), intent(in) :: node_out_name(*) type(C_PTR) :: resOut end function c_conduit_fort_from_py - function c_conduit_fort_to_py_to_fort(nodeIn, py_script) result(nodeOut) bind(C, name="conduit_fort_to_py_to_fort") + function c_conduit_fort_to_py_to_fort(nodeIn, py_script, node_in_name, node_out_name) result(nodeOut) bind(C, name="conduit_fort_to_py_to_fort") use iso_c_binding implicit none type(C_PTR), value, intent(in) :: nodeIn character(kind=C_CHAR), intent(in) :: py_script(*) + character(kind=C_CHAR), intent(in) :: node_in_name(*) + character(kind=C_CHAR), intent(in) :: node_out_name(*) type(C_PTR) :: nodeOut end function c_conduit_fort_to_py_to_fort end interface @@ -48,12 +54,47 @@ end function c_conduit_fort_to_py_to_fort contains !=============================================================================== - function conduit_fort_from_py(py_script) result(nodeOut) + subroutine conduit_fort_to_py(nodeIn, py_script, node_in_name) use iso_c_binding implicit none ! input/output variables + type(C_PTR), intent(in) :: nodeIn character(*), intent(in) :: py_script + character(*), intent(in), optional :: node_in_name + + ! local variables + character(len=*), parameter :: subname = trim(modName)//':(conduit_fort_to_py) ' + !--------------------------------------------------------------------------- + + call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO) + + ! Enter trace region + call ESMF_TraceRegionEnter('conduit_fort_to_py') + + ! Run Python script + if (present(node_in_name)) then + call c_conduit_fort_to_py(nodeIn, trim(py_script)//C_NULL_CHAR, & + trim(node_in_name)//C_NULL_CHAR) + else + call c_conduit_fort_to_py(nodeIn, trim(py_script)//C_NULL_CHAR, & + 'my_node'//C_NULL_CHAR) + end if + + ! Exit trace region + call ESMF_TraceRegionExit('conduit_fort_to_py') + + call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO) + + end subroutine conduit_fort_to_py + + function conduit_fort_from_py(py_script, node_out_name) result(nodeOut) + use iso_c_binding + implicit none + + ! input/output variables + character(*), intent(in) :: py_script + character(*), intent(in), optional :: node_out_name type(C_PTR) :: nodeOut ! local variables @@ -62,19 +103,34 @@ function conduit_fort_from_py(py_script) result(nodeOut) call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO) - nodeOut = c_conduit_fort_from_py(trim(py_script)//C_NULL_CHAR) + ! Enter trace region + call ESMF_TraceRegionEnter('conduit_fort_from_py') + + ! Run Python script + if (present(node_out_name)) then + nodeOut = c_conduit_fort_from_py(trim(py_script)//C_NULL_CHAR, & + trim(node_out_name)//C_NULL_CHAR) + else + nodeOut = c_conduit_fort_from_py(trim(py_script)//C_NULL_CHAR, & + 'my_node_return'//C_NULL_CHAR) + end if + + ! Exit trace region + call ESMF_TraceRegionExit('conduit_fort_from_py') call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO) end function conduit_fort_from_py - function conduit_fort_to_py_to_fort(nodeIn, py_script) result(nodeOut) + function conduit_fort_to_py_to_fort(nodeIn, py_script, node_in_name, node_out_name) result(nodeOut) use iso_c_binding implicit none ! input/output variables type(C_PTR), intent(in) :: nodeIn character(*), intent(in) :: py_script + character(*), intent(in), optional :: node_in_name + character(*), intent(in), optional :: node_out_name type(C_PTR) :: nodeOut ! local variables @@ -83,7 +139,30 @@ function conduit_fort_to_py_to_fort(nodeIn, py_script) result(nodeOut) call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO) - nodeOut = c_conduit_fort_to_py_to_fort(nodeIn, trim(py_script)//C_NULL_CHAR) + ! Enter trace region + call ESMF_TraceRegionEnter('conduit_fort_to_py_to_fort') + + ! Run Python script + if (present(node_in_name) .and. present(node_out_name)) then + nodeOut = c_conduit_fort_to_py_to_fort(nodeIn, trim(py_script)//C_NULL_CHAR, & + trim(node_in_name)//C_NULL_CHAR, & + trim(node_out_name)//C_NULL_CHAR) + else if (present(node_in_name)) then + nodeOut = c_conduit_fort_to_py_to_fort(nodeIn, trim(py_script)//C_NULL_CHAR, & + trim(node_in_name)//C_NULL_CHAR, & + 'my_node_return'//C_NULL_CHAR) + else if (present(node_out_name)) then + nodeOut = c_conduit_fort_to_py_to_fort(nodeIn, trim(py_script)//C_NULL_CHAR, & + 'my_node'//C_NULL_CHAR, & + trim(node_out_name)//C_NULL_CHAR) + else + nodeOut = c_conduit_fort_to_py_to_fort(nodeIn, trim(py_script)//C_NULL_CHAR, & + 'my_node'//C_NULL_CHAR, & + 'my_node_return'//C_NULL_CHAR) + end if + + ! Exit trace region + call ESMF_TraceRegionExit('conduit_fort_to_py_to_fort') call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO) diff --git a/src/plugins/python/geogate_python_interface.cpp b/src/plugins/python/geogate_python_interface.cpp index f22fb1f..8593f04 100644 --- a/src/plugins/python/geogate_python_interface.cpp +++ b/src/plugins/python/geogate_python_interface.cpp @@ -5,7 +5,7 @@ // embedded interp #include "python_interpreter.hpp" -// single python interp instance for our example. +// single python interp instance PythonInterpreter *interp = NULL; extern "C" { @@ -15,7 +15,7 @@ extern "C" { // if not already inited initializes it //---------------------------------------------------------------------------- - PythonInterpreter *init_python_interpreter() + PythonInterpreter *init_python_interpreter() { if( interp == NULL) { @@ -23,13 +23,13 @@ extern "C" { if( !interp->initialize() ) { std::cout << "ERROR: interp->initialize() failed " << std::endl; - return NULL; + return NULL; } // setup for conduit python c api if(!interp->run_script("import conduit")) { std::cout << "ERROR: `import conduit` failed" << std::endl; - return NULL; + return NULL; } if(import_conduit() < 0) @@ -37,10 +37,6 @@ extern "C" { std::cout << "failed to import Conduit Python C-API"; return NULL; } - - // Turn this on if you want to see every line - // the python interpreter executes - //interp->set_echo(true); } return interp; } @@ -49,18 +45,13 @@ extern "C" { // access node passed from fortran to python //---------------------------------------------------------------------------- - void conduit_fort_to_py(conduit_node *data, const char *py_script) { + void conduit_fort_to_py(conduit_node *data, const char *py_script, + const char *node_in_name) { // create python interpreter PythonInterpreter *pyintp = init_python_interpreter(); - // add extra system paths - // pyintp->add_system_path("/usr/local/lib/python3.9/site-packages"); - - // show code - // pyintp->set_echo(true); - // get global dict and insert wrapped conduit node - PyObject *py_mod_dict = pyintp->global_dict(); + PyObject *py_mod_dict = pyintp->global_dict(); // get cpp ref to passed node conduit::Node &n = conduit::cpp_node_ref(data); @@ -68,8 +59,8 @@ extern "C" { // create py object to wrap the conduit node PyObject *py_node = PyConduit_Node_Python_Wrap(&n, 0); // python owns => false - // my_node is set in here statically, it will be used to access node under python - pyintp->set_dict_object(py_mod_dict, py_node, "my_node"); + // insert node into global dict under the given name + pyintp->set_dict_object(py_mod_dict, py_node, node_in_name); // trigger script bool err = pyintp->run_script_file(py_script, py_mod_dict); @@ -80,36 +71,54 @@ extern "C" { } } - conduit_node* conduit_fort_from_py(const char *py_script) { + //---------------------------------------------------------------------------- + // run python script and return node created by script + //---------------------------------------------------------------------------- + + conduit_node* conduit_fort_from_py(const char *py_script, + const char *node_out_name) { // create python interpreter PythonInterpreter *pyintp = init_python_interpreter(); + // get global dict + PyObject *py_mod_dict = pyintp->global_dict(); + // trigger script - bool err = pyintp->run_script_file(py_script); + bool err = pyintp->run_script_file(py_script, py_mod_dict); // check error if (err) { pyintp->check_error(); } - // get global dict and insert wrapped conduit node - PyObject *py_mod_dict = pyintp->global_dict(); - // create py object to get the conduit node - PyObject *py_obj = pyintp->get_dict_object(py_mod_dict, "my_node_return"); + // fetch output node from global dict + PyObject *py_obj = PyDict_GetItemString(py_mod_dict, node_out_name); - // get cpp ref from python node - conduit::Node *n_res = PyConduit_Node_Get_Node_Ptr(py_obj); + if (py_obj != NULL) { + // get cpp ref from python node + conduit::Node *n_res = PyConduit_Node_Get_Node_Ptr(py_obj); - // return the c pointer - return conduit::c_node(n_res); + // return the c pointer + return conduit::c_node(n_res); + } else { + std::cout << "INFO: could not find '" << node_out_name + << "' key returned from Python!" << std::endl; + return NULL; + } } - conduit_node* conduit_fort_to_py_to_fort(conduit_node *data, const char *py_script) { + //---------------------------------------------------------------------------- + // run python script, pass input node, and return node created by script + //---------------------------------------------------------------------------- + + conduit_node* conduit_fort_to_py_to_fort(conduit_node *data, const char *py_script, + const char *node_in_name, + const char *node_out_name) { // create python interpreter PythonInterpreter *pyintp = init_python_interpreter(); // get global dict and insert wrapped conduit node - PyObject *py_mod_dict = pyintp->global_dict(); + PyObject *py_mod_dict = pyintp->global_dict(); // get cpp ref to passed node conduit::Node &n = conduit::cpp_node_ref(data); @@ -117,8 +126,8 @@ extern "C" { // create py object to wrap the conduit node PyObject *py_node = PyConduit_Node_Python_Wrap(&n, 0); // python owns => false - // my_node is set in here statically, it will be used to access node under python - pyintp->set_dict_object(py_mod_dict, py_node, "my_node"); + // insert node into global dict under the given name + pyintp->set_dict_object(py_mod_dict, py_node, node_in_name); // trigger script bool err = pyintp->run_script_file(py_script, py_mod_dict); @@ -128,26 +137,18 @@ extern "C" { pyintp->check_error(); } - // get global dict and fetch wrapped conduit node - py_mod_dict = pyintp->global_dict(); - - // check my_node_return in dictionary - std::string cpp_key = "my_node_return"; - PyObject *py_key = PyUnicode_FromString(cpp_key.c_str()); - int contains = PyDict_Contains(py_mod_dict, py_key); - Py_DECREF(py_key); - - if (contains == 1) { - // create py object to get the conduit node - PyObject *py_obj = pyintp->get_dict_object(py_mod_dict, cpp_key.c_str()); + // fetch output node from global dict + PyObject *py_obj = PyDict_GetItemString(py_mod_dict, node_out_name); + if (py_obj != NULL) { // get cpp ref from python node conduit::Node *n_res = PyConduit_Node_Get_Node_Ptr(py_obj); // return the c pointer return conduit::c_node(n_res); } else { - std::cout << "INFO: could not find 'my_node_return' key returned from Python!" << std::endl; + std::cout << "INFO: could not find '" << node_out_name + << "' key returned from Python!" << std::endl; return NULL; } } diff --git a/src/plugins/python/python_interpreter.cpp b/src/plugins/python/python_interpreter.cpp index e674803..1aaf411 100644 --- a/src/plugins/python/python_interpreter.cpp +++ b/src/plugins/python/python_interpreter.cpp @@ -66,134 +66,12 @@ #include #include #include +#include #include #include #include #include -using namespace std; - - - -#if PY_MAJOR_VERSION >= 3 -#define IS_PY3K -#endif - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// Begin Functions to help with Python 2/3 Compatibility. -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -#if defined(IS_PY3K) - -//----------------------------------------------------------------------------- -int -PyString_Check(PyObject *o) -{ - return PyUnicode_Check(o); -} - - -//----------------------------------------------------------------------------- -char * -PyString_AsString(PyObject *py_obj) -{ - char *res = NULL; - if(PyUnicode_Check(py_obj)) - { - PyObject * temp_bytes = PyUnicode_AsEncodedString(py_obj, - "ASCII", - "strict"); // Owned reference - if(temp_bytes != NULL) - { - res = strdup(PyBytes_AS_STRING(temp_bytes)); - Py_DECREF(temp_bytes); - } - else - { - // TODO: Error - } - } - else if(PyBytes_Check(py_obj)) - { - res = strdup(PyBytes_AS_STRING(py_obj)); - } - else - { - // TODO: ERROR or auto convert? - } - - return res; -} - -//----------------------------------------------------------------------------- -PyObject * -PyString_FromString(const char *s) -{ - return PyUnicode_FromString(s); -} - -//----------------------------------------------------------------------------- -void -PyString_AsString_Cleanup(char *bytes) -{ - free(bytes); -} - -//----------------------------------------------------------------------------- -int -PyInt_Check(PyObject *o) -{ - return PyLong_Check(o); -} - -//----------------------------------------------------------------------------- -long -PyInt_AsLong(PyObject *o) -{ - return PyLong_AsLong(o); -} - -//----------------------------------------------------------------------------- -long -PyInt_AS_LONG(PyObject *o) -{ - return PyLong_AS_LONG(o); -} - -//----------------------------------------------------------------------------- -PyObject * -PyNumber_Int(PyObject *o) -{ - return PyNumber_Long(o); -} - - -#else // python 2.6+ - -//----------------------------------------------------------------------------- -#define PyString_AsString_Cleanup(c) { /* noop */ } - -#endif - -// helper for both python 2 and 3 -//----------------------------------------------------------------------------- -void -PyString_To_CPP_String(PyObject *py_obj, std::string &res) -{ - - char *str = PyString_AsString(py_obj); - res = str; - PyString_AsString_Cleanup(str); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// End Functions to help with Python 2/3 Compatibility. -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - //----------------------------------------------------------------------------- /// /// PythonInterpreter Constructor @@ -210,11 +88,10 @@ PythonInterpreter::PythonInterpreter() m_py_main_module = NULL; m_py_global_dict = NULL; - m_py_trace_module = NULL; - m_py_sio_module = NULL; + m_py_trace_module = NULL; + m_py_sio_module = NULL; m_py_trace_print_exception_func = NULL; - m_py_sio_class = NULL; - + m_py_sio_class = NULL; } //----------------------------------------------------------------------------- @@ -227,7 +104,7 @@ PythonInterpreter::~PythonInterpreter() { // Shutdown the interpreter if running. shutdown(); - } +} //----------------------------------------------------------------------------- @@ -238,13 +115,9 @@ PythonInterpreter::~PythonInterpreter() void PythonInterpreter::set_program_name(const char *prog_name) { -#ifdef IS_PY3K wchar_t *w_prog_name = Py_DecodeLocale(prog_name, NULL); Py_SetProgramName(w_prog_name); PyMem_RawFree(w_prog_name); -#else - Py_SetProgramName(const_cast(prog_name)); -#endif } @@ -256,25 +129,20 @@ PythonInterpreter::set_program_name(const char *prog_name) void PythonInterpreter::set_argv(int argc, char **argv) { -#ifdef IS_PY3K // alloc ptrs for encoded ver std::vector wargv(argc); - + for(int i = 0; i < argc; i++) { wargv[i] = Py_DecodeLocale(argv[i], NULL); } - - PySys_SetArgv(argc,&wargv[0]); - + + PySys_SetArgv(argc, &wargv[0]); + for(int i = 0; i < argc; i++) { PyMem_RawFree(wargv[i]); } - -#else - PySys_SetArgv(argc, argv); -#endif } //----------------------------------------------------------------------------- @@ -303,11 +171,11 @@ PythonInterpreter::initialize(int argc, char **argv) const char *prog_name = "flow_embedded_py"; if(argc == 0 || argv == NULL) - { + { set_program_name(prog_name); } else - { + { set_program_name(argv[0]); } @@ -316,7 +184,6 @@ PythonInterpreter::initialize(int argc, char **argv) PyEval_InitThreads(); // set sys argvs - if(argc == 0 || argv == NULL) { set_argv(1, const_cast(&prog_name)); @@ -340,13 +207,13 @@ PythonInterpreter::initialize(int argc, char **argv) // all of these PyObject*s are borrowed refs m_py_main_module = PyImport_AddModule((char*)"__main__"); - + if(m_py_main_module == NULL) { std::cout << "PythonInterpreter failed to import `__main__` module" << std::endl; return false; } - + m_py_global_dict = PyModule_GetDict(m_py_main_module); if(m_py_global_dict == NULL) @@ -356,8 +223,6 @@ PythonInterpreter::initialize(int argc, char **argv) } // get objects that help us print exceptions - - PyRun_SimpleString("import traceback\n"); if(check_error()) return false; @@ -378,7 +243,7 @@ PythonInterpreter::initialize(int argc, char **argv) CONDUIT_INFO("PythonInterpreter failed to access `traceback` dictionary"); return false; } - + m_py_trace_print_exception_func = PyDict_GetItemString(py_trace_dict, "print_exception"); @@ -389,72 +254,40 @@ PythonInterpreter::initialize(int argc, char **argv) } // get ref to StringIO class - -#ifdef IS_PY3K - const char *sio_module_name = "io"; PyRun_SimpleString("import io\n"); if(check_error()) return false; -#else - const char *sio_module_name = "StringIO"; - PyRun_SimpleString("import StringIO\n"); - if(check_error()) - return false; -#endif - m_py_sio_module = PyImport_ImportModule(sio_module_name); - + m_py_sio_module = PyImport_ImportModule("io"); + if(m_py_sio_module == NULL) { - CONDUIT_INFO("PythonInterpreter failed to import " - << "`" - << sio_module_name - << "` module"); + CONDUIT_INFO("PythonInterpreter failed to import `io` module"); return false; } - + PyObject *py_sio_dict = PyModule_GetDict(m_py_sio_module); - + if(py_sio_dict == NULL) { - CONDUIT_INFO("PythonInterpreter failed to access `" - << sio_module_name - << "` dictionary"); + CONDUIT_INFO("PythonInterpreter failed to access `io` dictionary"); return false; } - // input the class - m_py_sio_class = PyDict_GetItemString(py_sio_dict,"StringIO"); - + m_py_sio_class = PyDict_GetItemString(py_sio_dict, "StringIO"); if(m_py_sio_class == NULL) { - CONDUIT_INFO("PythonInterpreter failed access StringIO class"); + CONDUIT_INFO("PythonInterpreter failed to access StringIO class"); return false; } - + m_running = true; return true; } -//----------------------------------------------------------------------------- -/// -/// Resets the state of the interpreter if it is running -/// -/// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp -//----------------------------------------------------------------------------- -void -PythonInterpreter::reset() -{ - if(m_running) - { - // clean gloal dict. - PyDict_Clear(m_py_global_dict); - } -} - //----------------------------------------------------------------------------- /// /// Shuts down the interpreter if it is running @@ -466,29 +299,25 @@ PythonInterpreter::shutdown() { if(m_running) { + // release cached compiled code objects before finalizing + for(std::map::iterator it = m_script_cache.begin(); + it != m_script_cache.end(); ++it) + { + Py_DECREF(it->second); + } + m_script_cache.clear(); + if(m_handled_init) { Py_Finalize(); } - m_running = false; + m_running = false; m_handled_init = false; } } -//----------------------------------------------------------------------------- -/// -/// Adds passed path to "sys.path" -/// -/// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp -//----------------------------------------------------------------------------- -bool -PythonInterpreter::add_system_path(const std::string &path) -{ - return run_script("sys.path.insert(1,r'" + path + "')\n"); -} - //----------------------------------------------------------------------------- /// /// Executes passed python script in the interpreter @@ -503,7 +332,7 @@ PythonInterpreter::run_script(const std::string &script) //----------------------------------------------------------------------------- /// -/// Executes passed python script in the interpreter +/// Executes passed python script file in the interpreter /// /// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp //----------------------------------------------------------------------------- @@ -545,7 +374,9 @@ PythonInterpreter::run_script(const std::string &script, //----------------------------------------------------------------------------- /// -/// Executes passed python script in the interpreter +/// Executes passed python script in the interpreter. +/// The script is compiled on first use and the resulting code object is +/// cached so that subsequent calls skip both file I/O and Python compilation. /// /// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp //----------------------------------------------------------------------------- @@ -553,57 +384,69 @@ bool PythonInterpreter::run_script_file(const std::string &fname, PyObject *py_dict) { - ifstream ifs(fname.c_str()); - if(!ifs.is_open()) - { - CONDUIT_ERROR("PythonInterpreter::run_script_file " - " failed to open "<< fname); + if(!m_running) return false; - } - string py_script((istreambuf_iterator(ifs)), - istreambuf_iterator()); - ifs.close(); - return run_script(py_script, py_dict); -} + // look up compiled code object in cache + PyObject *code_obj = NULL; + std::map::iterator it = m_script_cache.find(fname); + if(it == m_script_cache.end()) + { + // first call: read and compile the script + std::ifstream ifs(fname.c_str()); + if(!ifs.is_open()) + { + CONDUIT_ERROR("PythonInterpreter::run_script_file " + " failed to open " << fname); + return false; + } + std::string py_script((std::istreambuf_iterator(ifs)), + std::istreambuf_iterator()); + ifs.close(); -//----------------------------------------------------------------------------- -/// -/// Adds C python object to the global dictionary. -/// -/// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp -//----------------------------------------------------------------------------- -bool -PythonInterpreter::set_global_object(PyObject *py_obj, - const string &py_name) -{ - return set_dict_object(m_py_global_dict, py_obj, py_name); -} + if(m_echo) + { + CONDUIT_INFO("PythonInterpreter::run_script_file compiling " << fname); + } -//----------------------------------------------------------------------------- -/// -/// Get C python object from the global dictionary. -/// -/// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp -//----------------------------------------------------------------------------- -PyObject * -PythonInterpreter::get_global_object(const string &py_name) -{ - return get_dict_object(m_py_global_dict, py_name); + code_obj = Py_CompileString(py_script.c_str(), fname.c_str(), Py_file_input); + if(code_obj == NULL) + { + check_error(); + return false; + } + + // store in cache; we hold the reference + m_script_cache[fname] = code_obj; + } + else + { + code_obj = it->second; + } + + // execute the cached code object + PyObject *result = PyEval_EvalCode(code_obj, py_dict, py_dict); + if(result == NULL) + { + check_error(); + return false; + } + Py_DECREF(result); + return !check_error(); } //----------------------------------------------------------------------------- /// -/// Adds C python object to the global dictionary. +/// Adds C python object to the given dictionary. /// /// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp //----------------------------------------------------------------------------- bool PythonInterpreter::set_dict_object(PyObject *py_dict, PyObject *py_obj, - const string &py_name) + const std::string &py_name) { PyDict_SetItemString(py_dict, py_name.c_str(), py_obj); return !check_error(); @@ -611,13 +454,13 @@ PythonInterpreter::set_dict_object(PyObject *py_dict, //----------------------------------------------------------------------------- /// -/// Get C python object from the global dictionary. +/// Get C python object from the given dictionary. /// /// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp //----------------------------------------------------------------------------- PyObject * PythonInterpreter::get_dict_object(PyObject *py_dict, - const string &py_name) + const std::string &py_name) { PyObject *res = PyDict_GetItemString(py_dict, py_name.c_str()); if(check_error()) @@ -630,10 +473,7 @@ PythonInterpreter::get_dict_object(PyObject *py_dict, /// Checks python error state and constructs appropriate error message /// if an error did occur. It can be used to check for errors in both /// python scripts & calls to the C-API. The difference between these -/// to cases is the existence of a python traceback. -/// -/// Note: This method clears the python error state, but it will continue -/// to return "true" indicating an error until clear_error() is called. +/// two cases is the existence of a python traceback. /// /// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp //----------------------------------------------------------------------------- @@ -645,7 +485,7 @@ PythonInterpreter::check_error() m_error = true; m_error_msg = ""; - string sval =""; + std::string sval = ""; PyObject *py_etype; PyObject *py_eval; PyObject *py_etrace; @@ -687,95 +527,6 @@ PythonInterpreter::check_error() return m_error; } -//----------------------------------------------------------------------------- -/// -/// Clears environment error flag and message. -/// -/// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp -//----------------------------------------------------------------------------- -void -PythonInterpreter::clear_error() -{ - if(m_error) - { - m_error = false; - m_error_msg = ""; - } -} - -//----------------------------------------------------------------------------- -/// -/// Helper that converts a python object to a double. -/// Returns true if the conversion succeeds. -/// -/// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp -//----------------------------------------------------------------------------- -bool -PythonInterpreter::PyObject_to_double(PyObject *py_obj, double &res) -{ - if(PyFloat_Check(py_obj)) - { - res = PyFloat_AS_DOUBLE(py_obj); - return true; - } - - if(PyInt_Check(py_obj)) - { - res = (double) PyInt_AS_LONG(py_obj); - return true; - } - - if(PyLong_Check(py_obj)) - { - res = PyLong_AsDouble(py_obj); - return true; - } - - if(PyNumber_Check(py_obj) != 1) - return false; - - PyObject *py_val = PyNumber_Float(py_obj); - if(py_val == NULL) - return false; - res = PyFloat_AS_DOUBLE(py_val); - Py_DECREF(py_val); - return true; -} - -//----------------------------------------------------------------------------- -/// -/// Helper that converts a python object to an int. -/// Returns true if the conversion succeeds. -/// -/// Note: Adapted from VisIt: src/avt/PythonFilters/PythonInterpreter.cpp -//----------------------------------------------------------------------------- -bool -PythonInterpreter::PyObject_to_int(PyObject *py_obj, int &res) -{ - if(PyInt_Check(py_obj)) - { - res = (int)PyInt_AS_LONG(py_obj); - return true; - } - - if(PyLong_Check(py_obj)) - { - res = (int)PyLong_AsLong(py_obj); - return true; - } - - if(PyNumber_Check(py_obj) != 1) - return false; - - PyObject *py_val = PyNumber_Int(py_obj); - - if(py_val == NULL) - return false; - res = (int) PyInt_AS_LONG(py_val); - Py_DECREF(py_val); - return true; -} - //----------------------------------------------------------------------------- /// /// Helper that converts a python object to a C++ string. @@ -790,7 +541,9 @@ PythonInterpreter::PyObject_to_string(PyObject *py_obj, std::string &res) if(py_obj_str == NULL) return false; - PyString_To_CPP_String(py_obj_str,res); + const char *str = PyUnicode_AsUTF8(py_obj_str); + if(str != NULL) + res = str; Py_DECREF(py_obj_str); return true; } @@ -810,7 +563,7 @@ PythonInterpreter::PyTraceback_to_string(PyObject *py_etype, { if(!py_eval) py_eval = Py_None; - + // we can only print traceback if we have fully // inited the interpreter, since it uses imported helpers if(!m_running) @@ -819,7 +572,7 @@ PythonInterpreter::PyTraceback_to_string(PyObject *py_etype, } // create a StringIO object "buffer" to print traceback into. - PyObject *py_args = Py_BuildValue("()"); + PyObject *py_args = Py_BuildValue("()"); PyObject *py_buffer = PyObject_CallObject(m_py_sio_class, py_args); Py_DECREF(py_args); @@ -829,7 +582,7 @@ PythonInterpreter::PyTraceback_to_string(PyObject *py_etype, return false; } - // call traceback.print_tb(etrace,file=buffer) + // call traceback.print_exception(etype, eval, etrace, file=buffer) PyObject *py_res = PyObject_CallFunction(m_py_trace_print_exception_func, (char*)"OOOOO", py_etype, @@ -844,8 +597,7 @@ PythonInterpreter::PyTraceback_to_string(PyObject *py_etype, } // call buffer.getvalue() to get python string object - PyObject *py_str = PyObject_CallMethod(py_buffer,(char*)"getvalue",NULL); - + PyObject *py_str = PyObject_CallMethod(py_buffer, (char*)"getvalue", NULL); if(!py_str) { @@ -854,7 +606,9 @@ PythonInterpreter::PyTraceback_to_string(PyObject *py_etype, } // convert python string object to std::string - PyString_To_CPP_String(py_str,res); + const char *str = PyUnicode_AsUTF8(py_str); + if(str != NULL) + res = str; Py_DECREF(py_buffer); Py_DECREF(py_res); @@ -862,8 +616,3 @@ PythonInterpreter::PyTraceback_to_string(PyObject *py_etype, return true; } - - - - - diff --git a/src/plugins/python/python_interpreter.hpp b/src/plugins/python/python_interpreter.hpp index 981f98e..e01674a 100644 --- a/src/plugins/python/python_interpreter.hpp +++ b/src/plugins/python/python_interpreter.hpp @@ -46,7 +46,6 @@ // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - //----------------------------------------------------------------------------- /// /// file: python_interpreter.hpp @@ -65,78 +64,57 @@ #include #include +#include class PythonInterpreter { public: - PythonInterpreter(); - virtual ~PythonInterpreter(); + PythonInterpreter(); + virtual ~PythonInterpreter(); /// instance lifetime control bool initialize(int argc=0, char **argv=NULL); + void shutdown(); bool is_running() { return m_running; } - /// Note: blows away everything in the main dict - /// use with caution! - void reset(); - void shutdown(); - /// echo (default = false) - /// when enabled, controls if contents of execd python - // scripts are echoed to conduit info + /// when enabled, controls if contents of execd python + /// scripts are echoed to conduit info bool echo_enabled() const { return m_echo; } - /// change echo setting void set_echo(bool value) { m_echo = value; } - void set_program_name(const char *name); - void set_argv(int argc, char **argv); - - /// helper to add a system path to access new modules - bool add_system_path(const std::string &path); - /// script exec bool run_script(const std::string &script); bool run_script_file(const std::string &fname); - + /// script exec in specific dict bool run_script(const std::string &script, PyObject *py_dict); bool run_script_file(const std::string &fname, PyObject *py_dict); - /// set into global dict - bool set_global_object(PyObject *py_obj, - const std::string &name); - /// fetch from global dict, returns borrowed reference - PyObject *get_global_object(const std::string &name); /// access global dict object PyObject *global_dict() { return m_py_global_dict; } - /// set into given dict + /// set/get objects in a given dict bool set_dict_object(PyObject *py_dict, PyObject *py_obj, const std::string &name); - /// fetch from given dict, returns borrowed reference - PyObject *get_dict_object(PyObject *py_dict, + PyObject *get_dict_object(PyObject *py_dict, const std::string &name); /// error checking bool check_error(); - void clear_error(); - std::string error_message() const { return m_error_msg; } - - /// helpers to obtain values from basic objects - static bool PyObject_to_double(PyObject *py_obj, - double &res); + /// convert a python object to a C++ string static bool PyObject_to_string(PyObject *py_obj, std::string &res); - static bool PyObject_to_int(PyObject *py_obj, - int &res); - private: + void set_program_name(const char *name); + void set_argv(int argc, char **argv); + bool PyTraceback_to_string(PyObject *py_etype, PyObject *py_eval, PyObject *py_etrace, @@ -156,13 +134,8 @@ class PythonInterpreter PyObject *m_py_trace_print_exception_func; PyObject *m_py_sio_class; + // cache of compiled code objects, keyed by script file path + std::map m_script_cache; }; - - -#endif -//----------------------------------------------------------------------------- -// -- end header ifdef guard -//----------------------------------------------------------------------------- - - +#endif // PYTHON_INTERPRETER_HPP diff --git a/src/shared/geogate_share.F90 b/src/shared/geogate_share.F90 index 5de113a..9dcec15 100644 --- a/src/shared/geogate_share.F90 +++ b/src/shared/geogate_share.F90 @@ -19,6 +19,7 @@ module geogate_share use ESMF, only: ESMF_FieldBundleRegridStore, ESMF_FieldBundleRegrid use ESMF, only: ESMF_REGRIDMETHOD_BILINEAR, ESMF_POLEMETHOD_ALLAVG, ESMF_UNMAPPEDACTION_IGNORE use ESMF, only: ESMF_REGION_SELECT, ESMF_TERMORDER_SRCSEQ + use ESMF, only: ESMF_TraceRegionEnter, ESMF_TraceRegionExit use NUOPC, only: NUOPC_GetAttribute @@ -149,6 +150,9 @@ subroutine FB_init_pointer(StateIn, FBout, name, rc) rc = ESMF_SUCCESS call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO) + ! Enter trace region + call ESMF_TraceRegionEnter('FB_init_pointer') + ! Create empty field bundle, FBout FBout = ESMF_FieldBundleCreate(name=trim(name), rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return @@ -233,6 +237,9 @@ subroutine FB_init_pointer(StateIn, FBout, name, rc) end do ! fieldCount end if + ! Exit trace region + call ESMF_TraceRegionExit('FB_init_pointer') + call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO) end subroutine FB_init_pointer @@ -261,6 +268,9 @@ subroutine FB_copy(FBin, FBout, name, mesh, rh, rc) rc = ESMF_SUCCESS call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO) + ! Enter trace region + call ESMF_TraceRegionEnter('FB_copy') + ! Check FBout is created or not if (.not. ESMF_FieldBundleIsCreated(FBout, rc=rc)) then ! Create empty field bundle, FBout @@ -313,6 +323,9 @@ subroutine FB_copy(FBin, FBout, name, mesh, rh, rc) termorderflag=(/ ESMF_TERMORDER_SRCSEQ /), rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return + ! Exit trace region + call ESMF_TraceRegionExit('FB_copy') + call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO) end subroutine FB_copy