Page MenuHomec4science

compute_python.cc
No OneTemporary

File Metadata

Created
Sat, Aug 24, 19:36

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 "communicator.hh"
#include "compute_compatibility.hh"
#include "factory_multiscale.hh"
#include "lib_dd.hh"
#include "lib_md.hh"
#include "lm_common.hh"
#include "lm_parser.hh"
#include "units.hh"
#include <Eigen/Dense>
#include <pybind11/eigen.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, ':');
kwargs.attr("update")(make_dict(split_id, pyvalue));
// py::dict pyThisNameComputed;
// for (UInt i = 0; i < cont.name_computed.size(); ++i) {
// pyThisNameComputed.attr("__setitem__")(cont.name_computed[i], i);
// }
// py::object pyNameComputed = make_dict(split_id, pyThisNameComputed);
// py::dict updater;
// updater.attr("__setitem__")("name_computed", pyNameComputed);
// kwargs.attr("update")(updater);
}
/* -------------------------------------------------------------------------- */
ComputePython::ComputePython(const std::string &name) : LMObject(name) {
gather_root_proc = 0;
gather_flag = false;
// pModule = new py::object;
// pCompute = new py::none;
kwargs = new py::dict;
this->createInput("input");
};
/* -------------------------------------------------------------------------- */
ComputePython::~ComputePython() {
if (Py_IsInitialized()) {
delete kwargs;
// delete pModule;
// delete pCompute;
}
}
/* -------------------------------------------------------------------------- */
void ComputePython::init() {
this->createArrayOutputs(this->output_list);
if (!this->isConnected("input"))
this->removeInput("input");
if (filename != "") {
try {
auto pModule = py::module::import(filename.c_str());
LM_TOIMPLEMENT;
//*pCompute = pModule.attr("compute");
} 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);
}
} else if (pComputes.size() == 0) {
LM_FATAL("No compute function was set: use FILENAME or FUNC keywords");
}
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(std::shared_ptr<py::object> &pCompute) {
STARTTIMER(this->getID());
// py::array_t<Real> pValue = (*pCompute)(***kwargs);
py::object pValue = (*pCompute)(***kwargs);
std::map<std::string, py::object> results;
try {
results = pValue.cast<std::map<std::string, py::object>>();
} catch (...) {
py::object res = pValue;
results["output"] = res;
}
using EigenArray = ContainerArray<Real>::EigenArray;
for (auto &&pair : results) {
auto &key = pair.first;
auto vec = pair.second.cast<EigenArray>();
auto &output = this->getOutput(key).get(false).cast<ContainerArray<Real>>();
output = vec;
DUMP("compute python " << this->getID() << " computed "
<< this->getArray().size() << " values",
DBG_INFO);
}
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(pCompute);
}
}
/* ----------------------------------------------------------------------- */
void ComputePython::compute_with_arg(ContainerArray<Real> &cont) {
this->clear();
this->copyContainerInfo(cont);
// import_array();
CommGroup &group = this->getCommGroup();
appendGeneralArray(*kwargs, cont, this->gather_flag, group,
this->gather_root_proc);
this->appendGeneralInfo(group);
for (auto &&key_val : this->pComputes) {
auto &pCompute = key_val.second;
auto name = key_val.first;
this->callPythonRoutine(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