I am trying to solve “Convection in the Earth’s Mantle” provided in the FEniCS book. Currently I don’t know how to handle the filter for the composition “filter_properties” developed by Lenardic and Kaula (1993)
I know you can access data from the solution of a PDE using phi.vector().get_local() as a numpy array, which is easy to manipulate, but I could not figure out how to convert the new values back to a function and use it for the next iteration.
The problem iterates over many timesteps, so the filtered \phi^k is necessary as a function to compute \phi^{k+1}
the filter bounds the value of the composition \phi between 0 and 1
What the filter is expected to do
(1) compute the sum of all values of composition \phi, S0
(2) Find Minimal value below 0 , assign to \phi_{min}
(3) Find Maximal value over 1, assign to \phi_{max}
(4) Assign to \phi \leq |\phi_{min}| value 0
(5) Assign to \phi \geq \phi_{max} value 1
(6) Compute the sum of all values of composition \phi, S1
(7) Compute the number num of 0 < \phi< 1
(8) Add dist = (S1 - S0)/num to all 0 < \phi < 1
Code example
def filter_properties(phi):
phi_numpy = phi.vector().get_local()
S0 = np.sum(phi_numpy)
phi_min = np.min(phi_numpy)
phi_min = np.min([phi_min, 0])# in case phi_min > 0
phi_max = np.max(phi)
phi_max = np.max([phi_max, 1])# in case phi_max < 1
for j in np.arange(len(phi_numpy)):
if phi_numpy[j] < np.abs(phi_min):
phi_numpy[j] = 0
elif phi_numpy[j] > 2 - phi_max:
phi_numpy[j] = 1
S1 = np.sum(phi_numpy)
num = 0
for j in np.arange(len(phi_numpy)):
if not(phi_numpy[j]==0 or phi_numpy[j] == 1):
num += 1
dist = (S1 - S0)/num
for j in np.arange(len(phi_numpy)):
if not(phi[j]_numpy==0 or phi[j]_numpy == 1):
phi_numpy[j] += dist
#convert phi_numpy back to Function, How?
return phi_Function
Is there any way to assign the filtered new values in \phi_{numpy} to reemplace the values in the function \phi
Thanks in advance
This paper describes the numerical method employed and provides example scripts for FEniCS in the supplementary material.
Sadly I cannot access the supplement at the moment since I don’t have access to the journal. But I remember it being written with FEniCS ~1.5. It will take a little work to update this to more recent versions such as 2019.2.0, but those differences will be mostly cosmetic.
Thankyou very much, I could access the supplement. They have a script just for filters alone.
I will post the solution when I disentangle the code, for future coders that mayhavethe same question
def Filter(phi):
values = phi.vector()[:] #recover values of phi as numpy array
# do the filtering algorithm to values #--------------------------------------------
filtered = Function(phi.function_space) # generate a new function
filtered.vector()[:] = values
return filtered
I solve the problem of mixing two streams, my mesh is large enough and the Peclet number is high enough, but I noticed that discontinuous Galerkin methods are more numerically stable, but my concentration without a filter is greater than 1. Do you solve similar problems? Thank you for your answers.
def filter_properties(phi):
# filtro para acotar los valores del escalar phi entre 0 y 1
# retorna funcion filtrada
# extraer copia de phi como un numpy array, para poder manipular los datos
vector = phi.vector()[:]
space = phi.function_space()
# valores minimos y maximos
vectorMin = np.min(vector)
vectorMax = np.max(vector)
# definimos la función filtrada, para ser retornada
filtered = Function(space)
S0 = np.sum(vector)
n = len(vector)
# filtrar valores bajo 0 a 0 y sobre 1 a 1
vector = np.where(vector < 0, 0, vector)
vector = np.where(vector > 1, 1, vector)
# filtrar valoresmenos a abs(min) a 0 y sobre 2 - max a 1
vector = np.where(vector < np.abs(vectorMin), 0, vector)
vector = np.where(vector > (2 - vectorMax), 1, vector)
# contamos los valores en el rango (0,1)
num = 0
for j in np.arange(n):
if not(vector[j] == 0 or vector[j] == 1):
num += 1
# si no hay error que distribuirse retorna
if num == 0:
filtered.vector()[:] = vector
return filtered
S1 = np.sum(vector)
dist = (S0-S1)/num
for j in np.arange(n):
if (vector[j] > 0 and vector[j] < 1):
vector[j] += dist
# volvemos al espacio de funciones asignando los valores filtrados al vector funcion
# filtrada
filtered.vector()[:] = vector
return filtered