From 3e23de56dc98fa477dbc3f3ceb48bac01a719b8d Mon Sep 17 00:00:00 2001 From: L Laniewski-Wollk Date: Wed, 13 Dec 2023 13:49:56 +1000 Subject: [PATCH 1/4] Adding RunPython --- src/Handlers/cbRunR.cpp | 168 ++++++++++++++++++++++++++++++++-------- src/Handlers/cbRunR.h | 4 +- 2 files changed, 136 insertions(+), 36 deletions(-) diff --git a/src/Handlers/cbRunR.cpp b/src/Handlers/cbRunR.cpp index 6df21b301..6514e21c1 100644 --- a/src/Handlers/cbRunR.cpp +++ b/src/Handlers/cbRunR.cpp @@ -5,9 +5,6 @@ #define rNull Rcpp::NumericVector(0) template Rcpp::IntegerVector SingleInteger(T i) { Rcpp::IntegerVector v(1); v[0] = i; return v; } -//RInside RunR::R(0,0,true,false,true); -RInside RunR::R(0,0,true,false,true); - class rWrapper { // Wrapper for all my R objects public: Solver * solver; @@ -613,36 +610,127 @@ void CLB_WriteConsoleEx( const char* message, int len, int oType ){ } } - #define R_INTERFACE_PTRS - #include +#define R_INTERFACE_PTRS +#include -int RunR::Init() { - Callback::Init(); - notice("R: Initializing R environment ..."); - R["CLBFunctionCall"] = Rcpp::InternalFunction( &CLBFunctionCall ); - R["$.CLB"] = Rcpp::InternalFunction( &CLBDollar ); - R["[[.CLB"] = Rcpp::InternalFunction( &CLBDollar ); - R["$<-.CLB"] = Rcpp::InternalFunction( &CLBDollarAssign ); - R["print.CLB"] = Rcpp::InternalFunction( &CLBPrint ); - R["names.CLB"] = Rcpp::InternalFunction( &CLBNames ); - R.parseEval("'CLBFunctionWrap' <- function(obj) { function(...) CLBFunctionCall(obj, list(...)); }"); +namespace RunR { + RInside& GetR() { + static RInside * Rptr; + if (Rptr == NULL) { + notice("R: Initializing R environment ..."); + Rptr = new RInside(0,0,true,false,true); + RInside& R = *Rptr; + + R["CLBFunctionCall"] = Rcpp::InternalFunction( &CLBFunctionCall ); + R["$.CLB"] = Rcpp::InternalFunction( &CLBDollar ); + R["[[.CLB"] = Rcpp::InternalFunction( &CLBDollar ); + R["$<-.CLB"] = Rcpp::InternalFunction( &CLBDollarAssign ); + R["print.CLB"] = Rcpp::InternalFunction( &CLBPrint ); + R["names.CLB"] = Rcpp::InternalFunction( &CLBNames ); + R.parseEval("'CLBFunctionWrap' <- function(obj) { function(...) CLBFunctionCall(obj, list(...)); }"); + ptr_R_WriteConsoleEx = CLB_WriteConsoleEx ; + ptr_R_WriteConsole = NULL; + R_Outputfile = NULL; + R_Consolefile = NULL; + R.parseEval("options(prompt='[ ] R:> ');"); + } + return *Rptr; + }; + + void parseEval(const std::string& source) { + RInside& R = GetR(); + R.parseEval(source); + } + + int replInit() { + R_ReplDLLinit(); + return 0; + } - rWrapper base; - base.solver = solver; - base.hand = this; - R["Solver"] = base.rWrap(new rSolver ()); + int replDo() { + return R_ReplDLLdo1(); + } - ptr_R_WriteConsoleEx = CLB_WriteConsoleEx ; - ptr_R_WriteConsole = NULL; - R_Outputfile = NULL; - R_Consolefile = NULL; + SEXP wrap_solver(Solver* solver, vHandler * hand) { + rWrapper base; + base.solver = solver; + base.hand = hand; + return base.rWrap(new rSolver ()); + } - R.parseEval("options(prompt='[ ] R:> ');"); + SEXP wrap_handler(Solver* solver, vHandler * hand, const pugi::xml_node& par) { + rWrapper base; + base.solver = solver; + base.hand = hand; + return base.rWrap(new rXMLNode (par)); + } +}; +namespace RunPython { + bool has_reticulate = false; + bool py_initialised = false; + void initializePy(); + + void parseEval(const std::string& source) { + if (! py_initialised) initializePy(); + Rcpp::Function py_run_string("py_run_string"); + py_run_string(source); + return; + } + + void initializePy() { + RInside& R = RunR::GetR(); + has_reticulate = R.parseEval("require(reticulate, quietly=TRUE)"); + if (!has_reticulate) throw std::string("Tried to call Python, but no reticulate installed"); + py_initialised = true; + R.parseEval( + "py_names = function(obj) names(obj) \n" + "py_element = function(obj, name) `[[`(obj,name) \n" + "py_element_assign = function(obj, name, value) `[[<-`(obj,name,value) \n" + "r_to_py.CLB = function(x, convert=FALSE) py$S3(reticulate:::py_capsule(x))\n" + ); + parseEval( + "class S3: \n" + " def __init__(self, obj): \n" + " object.__setattr__(self,'obj',obj) \n" + " def print(self): \n" + " return r.print(self.obj) \n" + " def __dir__(self): \n" + " return r.py_names(self.obj) \n" + " def __getattr__(self, index): \n" + " if index.startswith('_'): \n" + " return None \n" + " return r.py_element(self.obj, index) \n" + " def __setattr__(self, index, value): \n" + " return r.py_element_assign(self.obj, index, value) \n" + " def __call__(self): \n" + " raise TypeError('not really callable') \n" + ); + R.parseEval( + "py$Solver = r_to_py(Solver)" + ); + } + + int replRun() { + if (! py_initialised) initializePy(); + Rcpp::Function repl_python("repl_python"); + repl_python(); + return 0; + } +} + +int cbRunR::Init() { + Callback::Init(); + RInside& R = RunR::GetR(); + R["Solver"] = RunR::wrap_solver(solver,this); + + python = false; interactive = false; echo = true; + std::string name = node.name(); + if (name == "RunPython") python = true; pugi::xml_attribute attr; attr = node.attribute("interactive"); if (attr) interactive = attr.as_bool(); @@ -654,7 +742,7 @@ int RunR::Init() { if (par.type() == pugi::node_element) { char nd_name[20]; sprintf(nd_name, "xml_%0zx", par.hash_value()); - R[nd_name] = base.rWrap(new rXMLNode (par)); + R[nd_name] = RunR::wrap_handler(solver,this,par); source = source + nd_name + "()\n"; output("element\n"); @@ -668,15 +756,12 @@ int RunR::Init() { output("Unknown\n"); } } -// output("----- RunR -----\n"); -// output("%s\n",source.c_str()); -// output("----------------\n"); return 0; } -int RunR::DoIt() { +int cbRunR::DoIt() { try { if (source != "") { solver->print("Running R ..."); @@ -685,7 +770,11 @@ int RunR::DoIt() { output("%s\n",source.c_str()); output("----------------\n"); } - R.parseEval(source); + if (python) { + RunPython::parseEval(source); + } else { + RunR::parseEval(source); + } } if (!interactive) { if (echo) NOTICE("You can run interactive R session with Ctrl+X"); @@ -698,10 +787,21 @@ int RunR::DoIt() { } } if (interactive) { - R_ReplDLLinit(); - while( R_ReplDLLdo1() > 0 ) {} + if (python) { + RunPython::replRun(); + } else { + RunR::replInit(); + while( RunR::replDo() > 0 ) {} + } } + } catch (Rcpp::exception& ex) { + ERROR("Caught Rcpp exception"); + return -1; + } catch(std::exception &ex) { + ERROR("Caught std exception: %s", ex.what()); + return -1; } catch (...) { + ERROR("Caught uknown exception"); return -1; } return 0; @@ -713,9 +813,9 @@ int RunR::DoIt() { // Function created only to check to create Handler for specific conditions vHandler * Ask_For_RunR(const pugi::xml_node& node) { std::string name = node.name(); - if (name == "RunR") { + if (name == "RunR" | name == "RunPython") { #ifdef WITH_R - return new RunR; + return new cbRunR; #else ERROR("No R support. configure with --enable-rinside\n"); exit(-1); diff --git a/src/Handlers/cbRunR.h b/src/Handlers/cbRunR.h index b5cf2c67d..167fb229d 100644 --- a/src/Handlers/cbRunR.h +++ b/src/Handlers/cbRunR.h @@ -22,12 +22,12 @@ #ifdef WITH_R -class RunR : public Callback { +class cbRunR : public Callback { std::string source; bool interactive; bool echo; + bool python; public: - static RInside R; int Init (); int DoIt (); }; From 6d88fd2d5b9881aec22a555dc06ae2d6e24f4223 Mon Sep 17 00:00:00 2001 From: L Laniewski-Wollk Date: Wed, 13 Dec 2023 14:37:22 +1000 Subject: [PATCH 2/4] Making RunR output nicer --- src/Handlers/cbRunR.cpp | 25 ++++++++++++++++--------- src/Handlers/cbRunR.h | 2 ++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Handlers/cbRunR.cpp b/src/Handlers/cbRunR.cpp index 6514e21c1..8c66faac0 100644 --- a/src/Handlers/cbRunR.cpp +++ b/src/Handlers/cbRunR.cpp @@ -737,9 +737,16 @@ int cbRunR::Init() { attr = node.attribute("echo"); if (attr) echo = attr.as_bool(); + s_tag++; + tag = s_tag; + source = ""; - for (pugi::xml_node par = node.first_child(); par; par = par.next_sibling()) { + for (pugi::xml_node par = node.first_child(); par; par = par.next_sibling()) { if (par.type() == pugi::node_element) { + if (python) { + ERROR("Code-embedded xml nodes not supported for python"); + return -1; + } char nd_name[20]; sprintf(nd_name, "xml_%0zx", par.hash_value()); R[nd_name] = RunR::wrap_handler(solver,this,par); @@ -756,20 +763,20 @@ int cbRunR::Init() { output("Unknown\n"); } } - + if (echo) { + output("-----[ %9s code %03d ]-----\n", node.name(), tag); + output("%s\n",source.c_str()); + output("--------------------------------\n"); + } return 0; } +int cbRunR::s_tag = 0; int cbRunR::DoIt() { try { if (source != "") { - solver->print("Running R ..."); - if (echo) { - output("----- RunR -----\n"); - output("%s\n",source.c_str()); - output("----------------\n"); - } + output("%8d it Executing %s code %03d\n", solver->iter, node.name(), tag); if (python) { RunPython::parseEval(source); } else { @@ -777,7 +784,7 @@ int cbRunR::DoIt() { } } if (!interactive) { - if (echo) NOTICE("You can run interactive R session with Ctrl+X"); + if (echo) NOTICE("You can run interactive %s session with Ctrl+X", node.name()); int c = kbhit(); if (c == 24) { int a = getchar(); diff --git a/src/Handlers/cbRunR.h b/src/Handlers/cbRunR.h index 167fb229d..a62fa8b3a 100644 --- a/src/Handlers/cbRunR.h +++ b/src/Handlers/cbRunR.h @@ -27,6 +27,8 @@ class cbRunR : public Callback { bool interactive; bool echo; bool python; + static int s_tag; + int tag; public: int Init (); int DoIt (); From e096d7d239572962094e0876eddebb94e5841363 Mon Sep 17 00:00:00 2001 From: L Laniewski-Wollk Date: Wed, 13 Dec 2023 16:17:36 +1000 Subject: [PATCH 3/4] Correcting or operator --- src/Handlers/cbRunR.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Handlers/cbRunR.cpp b/src/Handlers/cbRunR.cpp index 8c66faac0..c3be43ab8 100644 --- a/src/Handlers/cbRunR.cpp +++ b/src/Handlers/cbRunR.cpp @@ -626,6 +626,7 @@ namespace RunR { R["$.CLB"] = Rcpp::InternalFunction( &CLBDollar ); R["[[.CLB"] = Rcpp::InternalFunction( &CLBDollar ); R["$<-.CLB"] = Rcpp::InternalFunction( &CLBDollarAssign ); + R["[[<-.CLB"] = Rcpp::InternalFunction( &CLBDollarAssign ); R["print.CLB"] = Rcpp::InternalFunction( &CLBPrint ); R["names.CLB"] = Rcpp::InternalFunction( &CLBNames ); R.parseEval("'CLBFunctionWrap' <- function(obj) { function(...) CLBFunctionCall(obj, list(...)); }"); @@ -820,7 +821,7 @@ int cbRunR::DoIt() { // Function created only to check to create Handler for specific conditions vHandler * Ask_For_RunR(const pugi::xml_node& node) { std::string name = node.name(); - if (name == "RunR" | name == "RunPython") { + if (name == "RunR" || name == "RunPython") { #ifdef WITH_R return new cbRunR; #else From 3d8a6cce07a09229ee30ed2f26534f61019c7d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20=C5=81aniewski-Wo=C5=82=C5=82k?= Date: Tue, 19 Dec 2023 10:18:30 +1000 Subject: [PATCH 4/4] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f60620b19..2f4bbf8b5 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ Developed at: - [Interdisciplinary Centre for Mathematical and Computational Modelling](https://icm.edu.pl/en) at [University of Warsaw](https://www.uw.edu.pl/) ### Citation -Please use **appropriate citations if using this software** in any research publication. The publication should cite [the original paper about TCLB](https://doi.org/10.1016/j.camwa.2015.12.043) and papers which describe the used LMB models. You can find the list of TCLB publications at [docs.tclb.io/general-info/publications/](https://docs.tclb.io/general-info/publications/). You can also find the information about published articles in the source code of the models. +Please use **appropriate citations if using this software** in any research publication. The publication should cite [the original paper about TCLB](https://doi.org/10.1016/j.camwa.2015.12.043) and papers which describe the used LBM models. You can find the list of TCLB publications at [docs.tclb.io/general-info/publications/](https://docs.tclb.io/general-info/publications/). You can also find the information about published articles in the source code of the models. The code can be cited additionally, by its [Zenodo DOI](https://doi.org/10.5281/zenodo.3550331). ### License @@ -143,4 +143,4 @@ This software is distributed under the [GPL v3 License](LICENSE). If you need this software under a different license, please contact the main author. -Contact: llaniewski([monkey](https://en.wikipedia.org/wiki/At_sign#Names_in_other_languages))meil.pw.edu.pl +Contact: lukasz.laniewski([monkey](https://en.wikipedia.org/wiki/At_sign#Names_in_other_languages))pw.edu.pl