diff --git a/src/filter/compute_python.cc b/src/filter/compute_python.cc index 9f6e318..8557408 100644 --- a/src/filter/compute_python.cc +++ b/src/filter/compute_python.cc @@ -1,441 +1,441 @@ /** * @file compute_python.cc * * @author Guillaume Anciaux * * @date Tue Jul 22 14:47:56 2014 * * @brief This compute allows to use Python to produce computed values as input * to LM * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * LibMultiScale is free software: you can redistribute it and/or modify it * under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * LibMultiScale is distributed in the hope that it will be useful, but * WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with LibMultiScale. If not, see . * */ /* -------------------------------------------------------------------------- */ #define TIMER #include "compute_python.hh" #include "factory_multiscale.hh" #include "lib_dd.hh" #include "lib_md.hh" #include "lm_common.hh" #include "lm_communicator.hh" #include "lm_parser.hh" #include "lm_python_bindings.hh" #include "units.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include #include /* -------------------------------------------------------------------------- */ __BEGIN_LIBMULTISCALE__ namespace py = pybind11; template void appendGeneralVariables(py::object &kwargs, const std::string &name, T &value) { py::object pyvalue = py::cast(value); kwargs.attr("__setitem__")(name, pyvalue); } /* -------------------------------------------------------------------------- */ template <> void appendGeneralVariables( py::object &kwargs, const std::string &name, IntegrationSchemeStage &value) { std::stringstream sstr; sstr << value; std::string str = sstr.str(); appendGeneralVariables(kwargs, name, str); } /* -------------------------------------------------------------------------- */ auto split_string(const std::string &str, char separator) { std::istringstream iss(str); std::vector res; while (!iss.eof()) { std::string buf; std::getline(iss, buf, separator); res.push_back(buf); } return res; } auto make_dict(std::vector ids, py::object obj) { py::object key = obj; std::for_each(ids.rbegin(), ids.rend(), [&key](auto &val) { py::dict new_key; new_key.attr("__setitem__")(val, key); key = new_key; }); return key; } /* -------------------------------------------------------------------------- */ template void appendGeneralArray(py::dict kwargs, Cont &cont, bool gather_flag, CommGroup &group [[gnu::unused]], bool gather_root_proc [[gnu::unused]]) { ContainerArray *vdata_ptr = nullptr; if (gather_flag) { LM_TOIMPLEMENT; // vdata_ptr = &cont.gatherAllData(gather_root_proc); } else { ContainerArray &tmp = dynamic_cast &>(cont); vdata_ptr = &tmp; } LM_ASSERT(vdata_ptr, "null pointer detected: abort"); ContainerArray &vdata = *vdata_ptr; auto id = cont.getID(); DUMP("adding the compute " << id, DBG_INFO); auto split_id = split_string(id, ':'); py::object dico = kwargs; for (UInt i = 0; i < split_id.size() - 1; ++i) { std::string key = split_id[i]; if (not py::cast(dico).contains(py::cast(key))) { dico.attr("__setitem__")(key, py::dict()); } dico = kwargs.attr("__getitem__")(key); } dico.attr("__setitem__")(split_id[split_id.size() - 1], vdata.array()); } /* -------------------------------------------------------------------------- */ ComputePython::ComputePython(const std::string &name) : LMObject(name) { gather_root_proc = 0; gather_flag = false; kwargs = std::make_shared(); converter = nullptr; this->createInput("input"); } /* -------------------------------------------------------------------------- */ ComputePython::~ComputePython() { if (Py_IsInitialized()) { kwargs.reset(); } } /* -------------------------------------------------------------------------- */ void ComputePython::init() { try { converter = std::make_shared("libmultiscale", "libmultiscale internal module"); makeBindings(*converter); } catch (std::exception &e) { if (std::string(e.what()) == "ModuleNotFoundError: No module named 'prologue'") LM_FATAL( "could not import prologue module necessary for python bindings"); // certainly failing because LM was launched from python and the bindings // are therefore already made. } std::list output_list; if (not this->inputs["input"].has_value()) this->removeInput("input"); // else // DUMP("AAAA", DBG_MESSAGE); if (filename != "") { try { auto sys = py::module::import("sys"); auto os = py::module::import("os"); py::print("path: ", sys.attr("path")); py::print("getcwd: ", os.attr("getcwd")()); - py::print("environ: ", os.attr("environ")()); + py::print("environ: ", os.attr("environ")); auto pModule = py::module::import(filename.c_str()); auto builtins = py::module::import("builtins"); auto callable = builtins.attr("callable"); auto vec = py::cast>(pModule.attr("__dir__")()); for (auto &&f : vec) { if (f[0] == '_') continue; py::object func = pModule.attr(f.c_str()); bool _callable = callable(func).cast(); if (_callable == false) continue; DUMP("register compute: " << f << " " << _callable, DBG_MESSAGE); pComputes[f] = std::make_shared(func); } } catch (py::error_already_set &e) { DUMP(std::endl << "Python error while loading " << filename << ":" << std::endl << e.what() << std::endl << std::endl, DBG_MESSAGE); LM_FATAL("Failed to load " << filename); } } if (pComputes.size() == 0) { LM_FATAL("No compute function was set: use FILENAME or FUNC keywords"); } for (auto &&pair : this->pComputes) output_list.push_back(pair.first); this->createArrayOutputs(output_list); units_dict = decltype(units_dict){{"mvv2e", code_unit_system.mvv2e}, {"f_m2v_t", code_unit_system.f_m2v_t}, {"ft_m2v", code_unit_system.ft_m2v}, {"mv_t2f", code_unit_system.mv_t2f}, {"kT2e", code_unit_system.kT2e}, {"kT2fd", code_unit_system.kT2fd}, {"m_tt2f_d", code_unit_system.m_tt2f_d}, {"e_dd2m_tt", code_unit_system.e_dd2m_tt}, {"e2fd", code_unit_system.e2fd}, {"e_m2dd_tt", code_unit_system.e_m2dd_tt}, {"fd2e", code_unit_system.fd2e}}; kwargs->attr("__setitem__")("units", units_dict); try { } catch (py::error_already_set &e) { DUMP(std::endl << "Python error in " << filename << ":" << std::endl << e.what() << std::endl << std::endl, DBG_MESSAGE); PyErr_Print(); LM_FATAL("Cannot find entry point: compute()"); } kwargs->attr("__setitem__")("constants", Parser::getAlgebraicVariables()); } /* ------------------------------------------------------------------------ */ void ComputePython::appendGeneralInfo(CommGroup &group) { UInt my_rank = group.getMyRank(); for (UInt comp = 0; comp < compute_list.size(); ++comp) { LMID id = compute_list[comp]; auto &my_compute = FilterManager::getManager().getCastedObject(id); my_compute.compute(); appendGeneralArray(*kwargs, my_compute.evalArrayOutput(), this->gather_flag, group, this->gather_root_proc); } if (this->gather_flag && (my_rank != gather_root_proc)) return; appendGeneralVariables(*kwargs, "step", current_step); appendGeneralVariables(*kwargs, "current_stage", current_stage); appendGeneralVariables(*kwargs, "compute_name", this->getID()); appendGeneralVariables(*kwargs, "rank", lm_my_proc_id); } /* ----------------------------------------------------------------------- */ void ComputePython::callPythonRoutine(const std::string &name, std::shared_ptr &pCompute) { STARTTIMER(this->getID()); py::object ret_value; try { ret_value = (*pCompute)(***kwargs); } catch (std::exception &e) { LM_FATAL("Problem in Python execution of compute (" << this->getID() << ":" << name << ")" << std::endl << e.what()); } try { py::array nb_array = ret_value; auto &output = this->getOutput(name).get().cast>(); using EigenArray = ContainerArray::EigenArray; auto vec = nb_array.cast(); output = vec; DUMP("compute python " << this->getID() << " computed " << this->getOutputAsArray(name).size() << " values", DBG_INFO); } catch (std::exception &e) { auto &ret = py::cast(ret_value); this->getOutput(name) = static_cast(ret); } STOPTIMER(this->getID()); } /* ----------------------------------------------------------------------- */ void ComputePython::compute_no_arg() { this->clear(); this->setCommGroup(Communicator::getCommunicator().getObject("all")); CommGroup &group = this->getCommGroup(); this->appendGeneralInfo(group); for (auto &&key_val : this->pComputes) { auto &pCompute = key_val.second; auto name = key_val.first; this->callPythonRoutine(name, pCompute); } } /* ----------------------------------------------------------------------- */ template enable_if_not_component ComputePython::compute_with_arg(T &cont) { this->clear(); CommGroup &group = this->getCommGroup(); appendGeneralArray(*kwargs, cont, this->gather_flag, group, this->gather_root_proc); kwargs->attr("__setitem__") . operator()("input", cont.array()); this->appendGeneralInfo(group); for (auto &&key_val : this->pComputes) { auto &pCompute = key_val.second; auto name = key_val.first; this->callPythonRoutine(name, pCompute); } } /* ----------------------------------------------------------------------- */ template enable_if_component ComputePython::compute_with_arg(T &cont) { this->clear(); for (auto &&c : cont.evalOutputs()) { auto &array = dynamic_cast(cont).evalArrayOutput(c.first); this->acquireContext(array); CommGroup &group = array.getCommGroup(); DUMP("detected output: " << c.first, DBG_MESSAGE); appendGeneralArray(*kwargs, array, this->gather_flag, group, this->gather_root_proc); } try { kwargs->attr("__setitem__") . operator()( "input", dynamic_cast(cont)); } catch (std::exception &e) { LM_FATAL("fucking fuck" << e.what()); } CommGroup &group = this->getCommGroup(); this->appendGeneralInfo(group); for (auto &&key_val : this->pComputes) { auto &pCompute = key_val.second; auto name = key_val.first; this->callPythonRoutine(name, pCompute); } } /* ----------------------------------------------------------------------- */ /* LMDESC PYTHON This computes an array of reals based on a python script */ /* LMEXAMPLE .. code-block:: COMPUTE disp EXTRACT INPUT md FIELD displacement COMPUTE disp2 PYTHON INPUT disp FILENAME script with a python script stored in a file script.py with the content such as: .. code-block:: python import numpy as np def compute(**kwargs): vec = np.copy(kwargs["disp"]) vec *= 2. return vec */ void ComputePython::declareParams() { ComputeInterface::declareParams(); /* LMKEYWORD FILENAME Specify the name of the python script to use */ this->parseKeyword("FILENAME", filename, ""); /* LMKEYWORD OUTPUTS Specify the name of the outputs which will be produced */ this->parseKeyword("FUNC", pComputes, std::map>()); /* LMKEYWORD GATHER_ROOT Specify the processor where to gather data before inputing to python script */ this->parseKeyword("GATHER_ROOT", gather_root_proc, 0u); /* LMKEYWORD GATHER Specify the processor where to gather data before inputing to python script */ this->parseTag("GATHER", gather_flag, false); /* LMKEYWORD ADD_COMPUTE Add computes to input to the kwargs of the python script */ this->parseKeyword("ADD_COMPUTE", compute_list, std::vector{}); } /* -------------------------------------------------------------------------- */ void ComputePython::compute_make_call() { DUMP(this->getID() << ": Compute", DBG_INFO); if (this->inputs.count("input")) this->compute_with_arg(this->getInput("input")); else this->compute_no_arg(); } __END_LIBMULTISCALE__