Hi,
I tried to wrap another python software with pymc. My goal is that to use pymc sampling compatible for a finite element analysis input and output. The input for finite element is three parameters (a0,a1,a2) from normal sampling in pymc that needs to be converted into a float (to be used for each separate analysis) and the output is 1x20 displacement vector in numpy array. These outputs would act as mu for likelihood factor from multivariate normal with observation y is also a 1x20 displacement vector in numpy array.
I tried to use the op to wrap things but so far my implementation resulting in “NotImplementedError: Cannot convert <function FEmcOperator at 0x28c676160> to a tensor variable.” Help is appreciated as I am not understand on how the priors sampling would act in this function. Thanks.
def fenicsFEmc(a0,a1,a2):
g0 = float(a0)
g1 = float(a1)
g2 = float(a2)
dim = domain.topology.dim
degree = dim
shape = (dim,)
V = fem.functionspace(domain, ("P", degree, shape))
x = ufl.SpatialCoordinate(domain)
u_sol = fem.Function(V, name="Displacement")
E = (g0 + g1 * ufl.classes.Sin(np.pi * x[0]) + g2 * ufl.classes.Sin(2. * np.pi * x[0]))
nu = 0.3
lmbd = E * nu / (1 + nu) / (1 - 2 * nu)
mu_ = E / 2 / (1 + nu)
W = fem.functionspace(domain, ("Lagrange", 1))
lmbda_exp = fem.Expression(lmbd, W.element.interpolation_points())
mu_exp = fem.Expression(mu_, W.element.interpolation_points())
lmbda = fem.Function(W)
mu = fem.Function(W)
lmbda.interpolate(lmbda_exp)
mu.interpolate(mu_exp)
u = TrialFunction(V)
v = TestFunction(V)
f = fem.Constant(domain, np.array([1.]))
dx = Measure("dx", domain=domain)
a = inner(sigma(u, lmbda, dim, mu), epsilon(v)) * dx
L = inner(f, v) * dx
left_dofs = fem.locate_dofs_geometrical(V, left)
right_dofs = fem.locate_dofs_geometrical(V, right)
bcs = [fem.dirichletbc(np.zeros((1,)), left_dofs, V), fem.dirichletbc(np.zeros((1,)), right_dofs, V),]
problem = fem.petsc.LinearProblem(a, L, u=u_sol, bcs=bcs, petsc_options={"ksp_type": "preonly", "pc_type": "lu"})
problem.solve()
u = u_sol.x.array
return u
#Create the op function to wrap things from fenics
class FiniteElementOp(Op):
def make_node(self,a0,a1,a2) -> Apply:
# Convert inputs to tensor variables
a0 = pt.as_tensor_variable(a0)
a1 = pt.as_tensor_variable(a1)
a2 = pt.as_tensor_variable(a2)
inputs = [a0,a1,a2]
outputs = [pt.tensor()]
return Apply(self, inputs, outputs)
def perform(self, node: Apply, inputs: list[np.ndarray], outputs: list[list[None]]) -> None:
a0,a1,a2 = inputs # this will contain my variables
# call fenics function
ufe = fenicsFEmc(a0,a1,a2)
outputs[0][0] = np.asarray(ufe, dtype=node.outputs[0].dtype)
def FEmcOperator(a0,a1,a2):
return FiniteElement(a0,a1,a2)
# Create covariance matrix
covariance = np.eye(len(x_cent)) * perturb
#building pymc model
with pm.Model() as model:
# Priors
a0 = pm.Normal('a0', mu=g0, sigma=1.)
a1 = pm.Normal('a1', mu=g1, sigma=0.5)
a2 = pm.Normal('a2', mu=g2, sigma=0.2)
# Likelihood
likelihood = pm.MvNormal('likelihood', mu=FEmcOperator, cov = covariance, observed=y)
# MCMC sampling
trace = pm.sample(draws=1000, tune=1000)