Page MenuHomec4science

compute_python.cc
No OneTemporary

File Metadata

Created
Tue, Jun 18, 13:25

compute_python.cc

/**
* @file compute_python.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#define TIMER
#include "compute_python.hh"
#include "compute_compatibility.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 <Eigen/Dense>
#include <pybind11/eigen.h>
#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
/* -------------------------------------------------------------------------- */
__BEGIN_LIBMULTISCALE__
namespace py = pybind11;
template <typename T>
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<IntegrationSchemeStage>(
py::object &kwargs, const std::string &name,
IntegrationSchemeStage &value) {
std::stringstream sstr;
sstr << value;
std::string str = sstr.str();
appendGeneralVariables<std::string>(kwargs, name, str);
}
/* -------------------------------------------------------------------------- */
auto split_string(const std::string &str, char separator) {
std::istringstream iss(str);
std::vector<std::string> res;
while (!iss.eof()) {
std::string buf;
std::getline(iss, buf, separator);
res.push_back(buf);
}
return res;
}
auto make_dict(std::vector<std::string> 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 <typename Cont>
void appendGeneralArray(py::object &kwargs, Cont &cont, bool gather_flag,
CommGroup &group, bool gather_root_proc) {
// const UInt Dim = cont.getDim();
ContainerArray<Real> *vdata_ptr = nullptr;
if (gather_flag) {
LM_TOIMPLEMENT;
// vdata_ptr = &cont.gatherAllData(gather_root_proc);
} else {
ContainerArray<Real> &tmp = dynamic_cast<ContainerArray<Real> &>(cont);
vdata_ptr = &tmp;
}
if (gather_flag) {
UInt my_rank = group.getMyRank();
if (my_rank != gather_root_proc) {
// this->setDim(0);
return;
}
}
LM_ASSERT(vdata_ptr, "null pointer detected: abort");
ContainerArray<Real> &vdata = *vdata_ptr;
// UInt sz = vdata.size() / Dim;
// Eigen::Map<
// Eigen::Array<Real, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>
// python_input(&vdata[0], sz, Dim);
// py::object pyvalue = py::cast(python_input);
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<bool>(dico.attr("__contains__")(key))) {
dico.attr("__setitem__")(key, py::dict());
}
dico = dico.attr("__getitem__")(key);
}
dico.attr("__setitem__")(split_id[split_id.size() - 1], vdata.to_array());
}
/* -------------------------------------------------------------------------- */
ComputePython::ComputePython(const std::string &name) : LMObject(name) {
gather_root_proc = 0;
gather_flag = false;
kwargs = new py::dict;
converter = nullptr;
this->createInput("input");
}
/* -------------------------------------------------------------------------- */
ComputePython::~ComputePython() {
if (Py_IsInitialized()) {
delete kwargs;
}
}
/* -------------------------------------------------------------------------- */
void ComputePython::init() {
try {
converter =
new py::module("libmultiscale", "libmultiscale internal module");
makeBindings(*converter);
} catch (...) {
// certainly failing because LM was launched from python and the bindings
// are therefore already made.
}
std::list<std::string> output_list;
if (!this->isConnected("input"))
this->removeInput("input");
if (filename != "") {
try {
auto pModule = py::module::import(filename.c_str());
auto builtins = py::module::import("builtins");
auto callable = builtins.attr("callable");
auto vec = py::cast<std::vector<std::string>>(pModule.attr("__dir__")());
for (auto &&f : vec) {
if (f[0] == '_')
continue;
py::object func = pModule.attr(f.c_str());
bool _callable = callable(func).cast<bool>();
if (_callable == false)
continue;
DUMP("register compute: " << f << " " << _callable, DBG_MESSAGE);
pComputes[f] = std::make_shared<py::object>(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<ComputeInterface>(id);
my_compute.build();
appendGeneralArray(*kwargs, my_compute.getArray(), 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<py::object> &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 (" << name << ")"
<< std::endl
<< e.what());
}
try {
py::array nb_array = ret_value;
auto &output =
this->getOutput(name).get(false).cast<ContainerArray<Real>>();
using EigenArray = ContainerArray<Real>::EigenArray;
auto vec = nb_array.cast<EigenArray>();
output = vec;
DUMP("compute python " << this->getID() << " computed "
<< this->getArray(name).size() << " values",
DBG_INFO);
} catch (std::exception &e) {
auto &ret = py::cast<ComputeInterface &>(ret_value);
this->getOutput(name) = make_argument(static_cast<Component &>(ret));
// LM_FATAL("Returned value is not a numpy for compute (" << name << ")"
// << std::endl
// << e.what());
}
STOPTIMER(this->getID());
}
/* ----------------------------------------------------------------------- */
void ComputePython::compute_no_arg() {
this->clear();
// import_array();
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 <typename T>
enable_if_not_component<T> ComputePython::compute_with_arg(T &cont) {
this->clear();
this->copyContainerInfo(cont);
CommGroup &group = this->getCommGroup();
appendGeneralArray(*kwargs, cont, this->gather_flag, group,
this->gather_root_proc);
kwargs->attr("__setitem__")
.
operator()<py::return_value_policy::reference>("input", cont.to_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 <typename T>
enable_if_component<T> ComputePython::compute_with_arg(T &cont) {
this->clear();
for (auto &&c : cont.getOutputs()) {
auto &array = dynamic_cast<ComputeInterface &>(cont).getArray(c.first);
this->copyContainerInfo(array);
CommGroup &group = array.getCommGroup();
DUMP("detected output: " << c.first, DBG_MESSAGE);
appendGeneralArray(*kwargs, array, this->gather_flag, group,
this->gather_root_proc);
}
kwargs->attr("__setitem__")
.
operator()<py::return_value_policy::reference>(
"input", dynamic_cast<ComputeInterface &>(cont));
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
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:\\\\
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<std::string, std::shared_ptr<py::object>>());
/* 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<std::string>{});
}
/* --------------------------------------------------------------------------
*/
void ComputePython::compute_make_call() {
DUMP(this->getID() << ": Compute", DBG_INFO);
if (this->isConnected("input"))
call_compute(*this,
[&](auto &... args) { this->compute_with_arg(args...); },
this->getInput("input"));
else
call_compute(*this, [&]() { this->compute_no_arg(); });
}
__END_LIBMULTISCALE__

Event Timeline