Hi everyone!
I am implementing a Lubrication + Wear model in FeniCSx for my Masters Thesis.
While i was implementing further improvements i noticed, my previous approach will not be suitable to implement something i would need.
What I am trying to do is the following:
In my variational formulation i have a call to a height function, that returns the separation between the two geometries I am simulating.
Inside this function, i would want to obtain the value from a dictionary, which maps each node in the mesh to a certain value which changes over time.
However, i am unable to obtain the numerical coordinates (which I would need, as they are the key to the dictionary), as when solving, this height function is called with my ufl.SpatialCoordinate object.
There are probably many ways of doing such, do you have any suggestions? For context, the reason i am using a dictionary is because there is no analytic expression to calculate the value i map.
Many thanks!
Code:
from abc import ABC, abstractmethod ## Para clases abstractas
import ufl ## Parte de FeniCSx
from dolfinx import fem, io, mesh, plot, geometry ## Parte de FeniCSx
from mpi4py import MPI ## Parte de FeniCSx
from petsc4py.PETSc import ScalarType ## Parte de FeniCSx
import math, random ## Operaciones matemƔticas bƔsicas
import numpy as np ## Numpy para poder operar con arrays
import matplotlib.pyplot as mpl ## Para pintamiento
class Problema:
## Constructor
def __init__(self, datos):
self.datos = datos ## Diccionario que contiene los datos del problema
self.deltat = 0.1 ## Placeholder
self.datos["speed"] = ((self.datos["Ut"] - self.datos["Ub"])**2 + (self.datos["Vt"] - self.datos["Vb"])**2)**(1/2)
self.BC = [] ## Array que contiene las condiciones de contorno
## Diccionario que contiene los valores de diversos parĆ”metros para la iteraciĆ³n inicial.
self.timestepvals= {"Zh": 3,
"DeltaZh": 0,
"DeltaZ0": 0,
"S1" : 0}
## Setter para la variable h0 para el calculo de la altura. Necesario para poder variar la altura en calculos iterativos.
def set_h0(self,h0):
self.datos["h0"] = h0
## Metodo abstracto para el calculo de la altura. Obligamos a las clases hijas a definirla de forma que represente el problema
@abstractmethod
def height(self):
pass
## Clase para problemas tipo esfera - point contact
class ProblemaRodillo(Problema):
## Override del constructor para calcular el Ć”rea del contacto de forma especĆfica para cada caso.
def __init__(self, datos):
Problema.__init__(self, datos) # Llamamos primero al constructor por defecto
self.datos["Area_nom"] = math.pi* self.datos["rRod"]**2
def height(self,x):
## THIS VALUE SHOULD BE SOMETHING LIKE
# worn = self.worn[(x[0], x[1], x[2])].
worn = 0.001
return self.datos["h0"] + (x[0]**2)/(2*self.datos["rRod"]) # + worn
datosPruebaPsi={
"rRod": 0.03,
"Load": 10e4,
"h0min" : 5e-6,
"h0max": 6e-6,
"rho": 860,
"mu": 0.1,
"Ub":1,
"Ut":0,
"Vb":0,
"Vt":0,
"domain":{
"xMin":-0.05,
"xMax":0.05,
"yMin":-0.5,
"yMax":0.5,
"numNodes":50
},
}
Prueba = ProblemaRodillo(datosPruebaPsi)
xMin = Prueba.datos["domain"]["xMin"]
xMax = Prueba.datos["domain"]["xMax"]
yMin = Prueba.datos["domain"]["yMin"]
yMax = Prueba.datos["domain"]["yMax"]
numNodes = Prueba.datos["domain"]["numNodes"]
msh = mesh.create_rectangle(comm=MPI.COMM_WORLD,
points=((xMin, yMin), (xMax, yMax)), n=(numNodes, numNodes),
cell_type=mesh.CellType.triangle)
Prueba.FUNCSPACE = fem.FunctionSpace(msh, ("Lagrange", 2))
Prueba.PSPACE = ufl.TrialFunction(Prueba.FUNCSPACE)
Prueba.VSPACE = ufl.TestFunction(Prueba.FUNCSPACE)
# Definimos las fronteras para aplicar la condicion de contorno
facets = mesh.locate_entities_boundary(msh, dim=1, marker=lambda x: np.logical_or(np.logical_or(np.isclose(x[0],Prueba.datos["domain"]["xMin"]),np.isclose(x[0], Prueba.datos["domain"]["xMax"])),
np.logical_or(np.isclose(x[1], Prueba.datos["domain"]["yMin"]),np.isclose(x[1],Prueba.datos["domain"]["yMax"]))))
# Localizamos los grados de libertad en las fronteras
dofs = fem.locate_dofs_topological(V=Prueba.FUNCSPACE, entity_dim=1, entities=facets)
# Aplicamos condiciones de contorno tipo dirichlet
bc = fem.dirichletbc(value=ScalarType(0), dofs=dofs, V=Prueba.FUNCSPACE)
## Introducimos en el array que contiene las condiciones de contorno
Prueba.set_h0(Prueba.datos["h0max"])
rho = Prueba.datos["rho"]
mu = Prueba.datos["mu"]
Um = 0.5*(Prueba.datos["Ub"]+Prueba.datos["Ut"])
Vm = 0.5*(Prueba.datos["Vb"]+Prueba.datos["Vt"])
x = ufl.SpatialCoordinate(msh)
p = Prueba.PSPACE
v = Prueba.VSPACE
## FormulaciĆ³n variacional
a = (rho*(Prueba.height(x)**3)/(12*mu))*ufl.inner(ufl.grad(p),ufl.grad(v))*ufl.dx
L = rho*Prueba.height(x)*ufl.inner(ufl.tensors.as_vector([Um,Vm]),ufl.grad(v))*ufl.dx
## Asignamos el problema a nuestra clase
problem = fem.petsc.LinearProblem(a, L, bcs=[bc], petsc_options={"ksp_type": "ibcgs", "pc_type":"lu"})
Prueba.solution = problem.solve()
points = np.zeros((3, len(msh.geometry.x[:,0])))
points[0] = msh.geometry.x[:,0]
bb_tree = geometry.BoundingBoxTree(msh, msh.topology.dim)
cell_candidates = geometry.compute_collisions(bb_tree, points.T)
colliding_cells = geometry.compute_colliding_cells(msh, cell_candidates, points.T)
cells = [colliding_cells.links(i)[0] for i, point in enumerate(points.T)]
p_values = Prueba.solution.eval(points.T, cells)
mpl.scatter(points[0,:], p_values, label="Numerical solution")