Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F80390951
compute_python.cc
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sat, Aug 31, 11:56
Size
13 KB
Mime Type
text/x-c++
Expires
Mon, Sep 2, 11:56 (2 d)
Engine
blob
Format
Raw Data
Handle
20359290
Attached To
rLIBMULTISCALE LibMultiScale
compute_python.cc
View Options
/**
* @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 "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
::
dict
kwargs
,
Cont
&
cont
,
bool
gather_flag
,
CommGroup
&
group
,
bool
gather_root_proc
)
{
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
;
}
LM_ASSERT
(
vdata_ptr
,
"null pointer detected: abort"
);
ContainerArray
<
Real
>
&
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
<
py
::
dict
>
(
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
<
py
::
dict
>
();
converter
=
nullptr
;
this
->
createInput
(
"input"
);
}
/* -------------------------------------------------------------------------- */
ComputePython
::~
ComputePython
()
{
if
(
Py_IsInitialized
())
{
kwargs
.
reset
();
}
}
/* -------------------------------------------------------------------------- */
void
ComputePython
::
init
()
{
try
{
converter
=
std
::
make_shared
<
py
::
module
>
(
"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
<
std
::
string
>
output_list
;
if
(
not
this
->
inputs
[
"input"
].
has_value
())
this
->
removeInput
(
"input"
);
// else
// DUMP("AAAA", DBG_MESSAGE);
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
.
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
<
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 ("
<<
this
->
getID
()
<<
":"
<<
name
<<
")"
<<
std
::
endl
<<
e
.
what
());
}
try
{
py
::
array
nb_array
=
ret_value
;
auto
&
output
=
this
->
getOutput
(
name
).
get
().
cast
<
ContainerArray
<
Real
>>
();
using
EigenArray
=
ContainerArray
<
Real
>::
EigenArray
;
auto
vec
=
nb_array
.
cast
<
EigenArray
>
();
output
=
vec
;
DUMP
(
"compute python "
<<
this
->
getID
()
<<
" computed "
<<
this
->
getOutputAsArray
(
name
).
size
()
<<
" values"
,
DBG_INFO
);
}
catch
(
std
::
exception
&
e
)
{
auto
&
ret
=
py
::
cast
<
ComputeInterface
&>
(
ret_value
);
this
->
getOutput
(
name
)
=
static_cast
<
Component
&>
(
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
<
typename
T
>
enable_if_not_component
<
T
>
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
()
<
py
::
return_value_policy
::
reference
>
(
"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
<
typename
T
>
enable_if_component
<
T
>
ComputePython
::
compute_with_arg
(
T
&
cont
)
{
this
->
clear
();
for
(
auto
&&
c
:
cont
.
evalOutputs
())
{
auto
&
array
=
dynamic_cast
<
ComputeInterface
&>
(
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
()
<
py
::
return_value_policy
::
reference
>
(
"input"
,
dynamic_cast
<
ComputeInterface
&>
(
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
<
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
->
inputs
.
count
(
"input"
))
this
->
compute_with_arg
<
dispatch
>
(
this
->
getInput
(
"input"
));
else
this
->
compute_no_arg
();
}
__END_LIBMULTISCALE__
Event Timeline
Log In to Comment