Calling user-defined python modulesΒΆ
ExaDiS python interface allows the code to interact with user-defined, python-based modules implemented within the specifications of OpenDiS. For instance, a generic module that performs calculations and modifications on the dislocation network can be created as:
class MyModule:
""" Prototype of an python-based OpenDiS module """
def __init__(self, state: dict, **kwargs) -> None:
# do some initialization
pass
def some_internal_function(self, G, param):
# compute some stuff
# values = ...
return values
def Compute(self, N: DisNetManager, state: dict) -> dict:
# Get dislocation network in module-specific format
G = N.get_disnet(MyLibraryDisNet)
# Make some calculations on the network
values = self.some_internal_function(G, state["param"])
# Make some changes to the network
G, flag = change_network(G)
# Update state dictionary
state["nodevalues"] = np.array(values)
state["param"] = param
state["flag"] = flag
# other state values...
return state
and the results can then be passed to ExaDiS modules, e.g.:
# Create initial network
G = ExaDisNet(...)
N = DisNetManager(G)
# Compute forces
forces = CalForce(...)
forces.Compute(N, state)
# Perform some user-defined computations/modifications on the network
mymodule = MyModule(...)
mymodule.Compute(N, state)
# Recompute forces on the modified network
forces.Compute(N, state)
It is also possible to create user-defined, python-based CalForce
, MobilityLaw
, etc, modules. For instance, we can create a python-based CalForce
module following the OpenDiS specification template below
class MyCalForceModule:
""" Template for a CalForce python-based module: """
def __init__(self, state: dict, **kwargs) -> None:
# Do some initialization
pass
def PreCompute(self, N: DisNetManager, state: dict) -> dict:
# Do some force pre-computation
# ...
return state
def NodeForce(self, N: DisNetManager, state: dict) -> dict:
# Get dislocation network in module-specific format
G = N.get_disnet(MyLibraryDisNet)
# Compute forces on network G
# f = ...
# Store forces in state dictionary
state["nodeforces"] = np.array(f)
state["nodeforcetags"] = data.get("nodes")["tags"]
return state
def OneNodeForce(self, N: DisNetManager, state: dict, tag, update_state=True) -> np.array:
# Get dislocation network in module-specific format
G = N.get_disnet(MyLibraryDisNet)
tags = G.get_tags()
# Find node index for which to compute the force
ind = np.where((tags[:,0]==tag[0])&(tags[:,1]==tag[1]))[0]
if ind.size != 1:
raise ValueError("Cannot find node tag (%d,%d) in OneNodeForce" % tuple(tag))
# Compute force on node tag and return it
# fnode = ...
# Update node force in state dictionary if needed:
if update_state:
if "nodeforces" in state and "nodeforcetags" in state:
nodeforcetags = state["nodeforcetags"]
ind = np.where((nodeforcetags[:,0]==tag[0])&(nodeforcetags[:,1]==tag[1]))[0]
if ind.size == 1:
state["nodeforces"][ind[0]] = fnode
else:
state["nodeforces"] = np.vstack((state["nodeforces"], fnode))
state["nodeforcetags"] = np.vstack((state["nodeforcetags"], tag))
else:
state["nodeforces"] = np.array([fnode])
state["nodeforcetags"] = np.array([tag])
return fnode
and call this module from within ExaDiS, even when compiled/running on GPU, e.g.
# Declare modules
mycalforce = MyCalForceModule(...) # user-defined class
exadis_mobility = MobilityLaw(...) # imported from pyexadis_base
exadis_timeint = TimeIntegration(..., force=mycalforce, mobility=exadis_mobility) # imported from pyexadis_base
# Integrate the system
mycalforce.NodeForce(N, state)
exadis_mobility.Mobility(N, state)
exadis_timeint.Update(N, state)
This usage is allowed by the CalForcePython
/ForcePython
wrappers implemented in ExaDiS enabling python objects to be called from the C++ code. The same mechanism allows for mixing ExaDiS and PyDiS modules in an OpenDiS simulation, e.g. as exemplified in the examples/02_frank_read_src/test_frank_read_src_pydis_exadis.py
example file in the OpenDiS repository.