Module Grain
@author: Alexandre Sac–Morane alexandre.sac-morane@uclouvain.be
The goal of this file is to define a new class. The new class is about the grains
Expand source code
# -*- coding: utf-8 -*-
"""
@author: Alexandre Sac--Morane
alexandre.sac-morane@uclouvain.be
The goal of this file is to define a new class.
The new class is about the grains
"""
#-------------------------------------------------------------------------------
#Libs
#-------------------------------------------------------------------------------
import numpy as np
import math
import random
#-------------------------------------------------------------------------------
#Class
#-------------------------------------------------------------------------------
class Grain:
#-------------------------------------------------------------------------------
def __init__(self, dict_ic_to_real, Id_Eta = None, V = np.array([0,0]), A = np.array([0,0])):
"""
Defining the grain.
The ic to real dictionnary is the data transmission between the temporary grain and the real one.
Input :
itself (a grain)
a ic to real dictionnary (a dict)
Output :
a grain is generated (a grain)
"""
#Id of the grain
self.id = dict_ic_to_real['Id']
self.id_eta = Id_Eta
#Material property
self.dissolved = dict_ic_to_real['Type'] == 'Square'
self.y = dict_ic_to_real['Y']
self.nu = dict_ic_to_real['Nu']
self.g = self.y /2/(1+self.nu) #shear modulus
self.rho_surf = dict_ic_to_real['Rho_surf']
#kinematic
self.v = V
self.a = A
self.theta = 0
self.w = 0 #dtheta/dt
#position
self.center = dict_ic_to_real['Center']
self.l_border = dict_ic_to_real['L_border']
self.l_border_x = dict_ic_to_real['L_border_x']
self.l_border_y = dict_ic_to_real['L_border_y']
#characteristic
self.l_r = dict_ic_to_real['L_r']
self.l_theta_r = dict_ic_to_real['L_theta_r']
self.r_min = dict_ic_to_real['R_min']
self.r_max = dict_ic_to_real['R_max']
self.r_mean = dict_ic_to_real['R_mean']
self.surface = dict_ic_to_real['Surface']
self.m = dict_ic_to_real['Mass']
self.inertia = dict_ic_to_real['Inertia']
#-------------------------------------------------------------------------------
def update_geometry_kinetic(self, V, A, W, DT):
"""
Update the acceleration and the velocity of a grain. Update geometrical parameters as border and center nodes.
Input :
itself (a grain)
a speed (a 1 x 2 numpy array)
an acceleration (a 1 x 2 numpy array)
an angular speed (a float)
a time step (a float)
Ouput :
Nothing, but the position of the grain is updated
"""
#translation
self.v = V
self.a = A
for i in range(len(self.l_border)):
self.l_border[i] = self.l_border[i] + self.v*DT
self.l_border_x[i] = self.l_border_x[i] + self.v[0]*DT
self.l_border_y[i] = self.l_border_y[i] + self.v[1]*DT
self.center = self.center + self.v*DT
#rotation
self.w = W
self.theta = self.theta + self.w*DT
for i_theta_r in range(len(self.l_theta_r)) :
theta_r = self.l_theta_r[i_theta_r]
theta_r = theta_r + self.w*DT
while theta_r >= 2*math.pi:
theta_r = theta_r - 2*math.pi
while theta_r < 0 :
theta_r = theta_r + 2*math.pi
self.l_theta_r[i_theta_r] = theta_r
for i in range(len(self.l_border)):
p = self.l_border[i] - self.center
Rot_Matrix = np.array([[math.cos(self.w*DT), -math.sin(self.w*DT)],
[math.sin(self.w*DT), math.cos(self.w*DT)]])
p = np.dot(Rot_Matrix,p)
self.l_border[i] = p + self.center
self.l_border_x[i] = p[0] + self.center[0]
self.l_border_y[i] = p[1] + self.center[1]
#-------------------------------------------------------------------------------
def init_f_control(self,dict_sollicitations):
"""
Initialize the force applied to the grain.
A gravity of g is applied.
Input :
itself (a grain)
a sollicitations dictionnary (a dict)
Ouput :
Nothing, but the force applied on the grain is initialized
"""
self.fx = 0
self.fy = -dict_sollicitations['gravity']*self.m
self.f = np.array([self.fx,self.fy])
self.mz = 0
#-------------------------------------------------------------------------------
def update_f(self, Fx, Fy, p_application):
"""
Add a force to the grain.
Input :
itself (a grain)
the value x and y of the force (two float)
an applicaiton point (a 1 x 2 numpy array)
Output :
Nothing, but a force is applied to the grain
"""
self.fx = self.fx + Fx
self.fy = self.fy + Fy
self.f = np.array([self.fx,self.fy])
v1 = np.array([p_application[0]-self.center[0], p_application[1]-self.center[1], 0])
v2 = np.array([Fx, Fy, 0])
self.mz = self.mz + np.cross(v1,v2)[2]
#-------------------------------------------------------------------------------
def Geometricstudy_local(self,dict_geometry,dict_sample,simulation_report):
"""
Searching border of the grain.
We iterate on y constant, we look for a value under and over 0.5.
If both conditions are verified, there is a limit at this y
Same with iteration on x constant.
Then, searching Surface, Center of mass and Inertia.
A Monte Carlo Method is applied.
A box is defined, we take a random point and we look if it is inside or outside the grain.
Properties are the statistic times the box properties.
Input :
itself (a grain)
a geometry dictionnary (a dict)
a sample dictionnary (a dict)
a simulation report (a report)
Output :
Nothing, but geometric parameters are updated
"""
#-------------------------------------------------------------------------
#load data needed
n = dict_geometry['grain_discretisation']
x_L = self.x_L_local
y_L = self.y_L_local
#-------------------------------------------------------------------------
L_border_old = []
for y_i in range(len(y_L)):
L_extract_x = self.etai_M[y_i][:]
if id == 1:
L_extract_x = list(L_extract_x)
L_extract_x.reverse()
if max(L_extract_x)>0.5 and min(L_extract_x)<0.5:
y_intersect = y_L[len(y_L)-1-y_i]
for x_i in range(len(x_L)-1):
if (L_extract_x[x_i]-0.5)*(L_extract_x[x_i+1]-0.5)<0:
x_intersect = (0.5-L_extract_x[x_i])/(L_extract_x[x_i+1]-L_extract_x[x_i])*\
(x_L[x_i+1]-x_L[x_i]) + x_L[x_i]
L_border_old.append(np.array([x_intersect,y_intersect]))
for x_i in range(len(x_L)):
L_extract_y = []
for y_i in range(len(y_L)):
L_extract_y.append(self.etai_M[y_i][x_i])
if max(L_extract_y)>0.5 and min(L_extract_y)<0.5:
x_intersect = x_L[x_i]
for y_i in range(len(y_L)-1):
if (L_extract_y[y_i]-0.5)*(L_extract_y[y_i+1]-0.5)<0:
y_intersect = (0.5-L_extract_y[y_i])/(L_extract_y[y_i+1]-L_extract_y[y_i])*\
(y_L[len(y_L)-1-y_i-1]-y_L[len(y_L)-1-y_i]) + y_L[len(y_L)-1-y_i]
L_border_old.append(np.array([x_intersect,y_intersect]))
#Adaptating
L_id_used = [0]
L_border = [L_border_old[0]]
HighValue = 100000000 #Large
current_node = L_border_old[0]
for j in range(1,len(L_border_old)):
L_d = list(np.zeros(len(L_border_old)))
for i in range(0,len(L_border_old)):
node = L_border_old[i]
if i not in L_id_used:
d = np.linalg.norm(node - current_node)
L_d[i] = d
else :
L_d[i] = HighValue #Value need to be larger than potential distance between node
index_nearest_node = L_d.index(min(L_d))
nearest_node = L_border_old[index_nearest_node]
current_node = nearest_node
L_border.append(nearest_node)
L_id_used.append(index_nearest_node)
#Correcting
L_d_final = []
for i in range(len(L_border)-1):
L_d_final.append(np.linalg.norm(L_border[i+1] - L_border[i]))
#look for really far points, we assume the first point is accurate
d_final_mean = np.mean(L_d_final)
while np.max(L_d_final) > 5 * d_final_mean : #5 here is an user choixe value
i_error = L_d_final.index(np.max(L_d_final))+1
simulation_report.write('Point '+str(L_border[i_error])+' is deleted because it is detected as an error\n')
L_border.pop(i_error)
L_id_used.pop(i_error)
L_d_final = []
for i in range(len(L_border)-1):
L_d_final.append(np.linalg.norm(L_border[i+1] - L_border[i]))
#-------------------------------------------------------------------------------
#Reduce the number of nodes for a grain
#-------------------------------------------------------------------------------
Perimeter = 0
for i_p in range(len(L_border)-1):
Perimeter = Perimeter + np.linalg.norm(L_border[i_p+1]-L_border[i_p])
Perimeter = Perimeter + np.linalg.norm(L_border[-1]-L_border[0])
distance_min = Perimeter/n
L_border_adapted = [L_border[0]]
for p in L_border[1:]:
distance = np.linalg.norm(p-L_border_adapted[-1])
if distance >= distance_min:
L_border_adapted.append(p)
L_border = L_border_adapted
L_border.append(L_border[0])
self.l_border = L_border
#-------------------------------------------------------------------------------
#Monte carlo method
#-------------------------------------------------------------------------------
min_max_defined = False
for p in L_border[:-1] :
if not min_max_defined:
box_min_x = p[0]
box_max_x = p[0]
box_min_y = p[1]
box_max_y = p[1]
min_max_defined = True
else:
if p[0] < box_min_x:
box_min_x = p[0]
elif p[0] > box_max_x:
box_max_x = p[0]
if p[1] < box_min_y:
box_min_y = p[1]
elif p[1] > box_max_y:
box_max_y = p[1]
N_MonteCarlo = 3000 #The larger it is, the more accurate it is
sigma = self.rho_surf #kg/µm2
M_Mass = 0
M_Center_Mass = np.array([0,0])
M_Inertia = 0
for i in range(N_MonteCarlo):
P = np.array([random.uniform(box_min_x,box_max_x),random.uniform(box_min_y,box_max_y)])
if self.P_is_inside(P):
M_Mass = M_Mass + sigma
M_Center_Mass = M_Center_Mass + sigma*P
M_Inertia = M_Inertia + sigma*np.dot(P,P)
Mass = (box_max_x-box_min_x)*(box_max_y-box_min_y)/N_MonteCarlo*M_Mass
Center_Mass = (box_max_x-box_min_x)*(box_max_y-box_min_y)/N_MonteCarlo*M_Center_Mass/Mass
Inertia = (box_max_x-box_min_x)*(box_max_y-box_min_y)/N_MonteCarlo*M_Inertia-Mass*np.dot(Center_Mass,Center_Mass)
#-------------------------------------------------------------------------------
#Updating the grain geometry and properties
#-------------------------------------------------------------------------------
L_R = []
L_theta_R = []
L_border_x = []
L_border_y = []
for p in L_border[:-1]:
L_R.append(np.linalg.norm(p-Center_Mass))
L_border_x.append(p[0])
L_border_y.append(p[1])
if (p-Center_Mass)[1] > 0:
theta = math.acos((p-Center_Mass)[0]/np.linalg.norm(p-Center_Mass))
else :
theta = 2*math.pi - math.acos((p-Center_Mass)[0]/np.linalg.norm(p-Center_Mass))
L_theta_R.append(theta)
L_border_x.append(L_border_x[0])
L_border_y.append(L_border_y[0])
#reorganize lists
L_R.reverse()
L_theta_R.reverse()
i_min_theta = L_theta_R.index(min(L_theta_R))
L_R = L_R[i_min_theta:]+L_R[:i_min_theta]
L_theta_R = L_theta_R[i_min_theta:]+L_theta_R[:i_min_theta]
self.r_min = np.min(L_R)
self.r_max = np.max(L_R)
self.r_mean = np.mean(L_R)
self.l_r = L_R
self.l_theta_r = L_theta_R
self.surface = Mass/self.rho_surf
self.m = Mass
self.center = Center_Mass
self.l_border_x = L_border_x
self.l_border_y = L_border_y
self.inertia = Inertia
#-------------------------------------------------------------------------------
def P_is_inside(self,P):
"""
Determine if a point P is inside a grain.
See Franklin 1994, see Alonso-Marroquin 2009
Input :
itself (a grain)
a point (a 1 x 2 numpy array)
Output :
a Boolean, True if the point is inside the grain (a Boolean²)
"""
counter = 0
for i_p_border in range(len(self.l_border)-1):
#consider only points if the coordinates frame the y-coordinate of the point
if (self.l_border[i_p_border][1]-P[1])*(self.l_border[i_p_border+1][1]-P[1]) < 0 :
x_border = self.l_border[i_p_border][0] + (self.l_border[i_p_border+1][0]-self.l_border[i_p_border][0])*(P[1]-self.l_border[i_p_border][1])/(self.l_border[i_p_border+1][1]-self.l_border[i_p_border][1])
if x_border > P[0] :
counter = counter + 1
if counter % 2 == 0:
return False
else :
return True
#-------------------------------------------------------------------------------
def Write_e_dissolution_local_txt(self,dict_algorithm,dict_sollicitations):
"""
Write an .txt file for MOOSE. This file described an homogenous dissolution field.
Input :
itself (a grain)
an algorithm dictionnary (a dict)
a sollicitations dictionnary (a dict)
Output :
Nothing, but a .txt file is generated (a file)
"""
file_to_write = open(f"Data/e_diss_g{self.id}_ite{dict_algorithm['i_PF']}.txt",'w')
file_to_write.write('AXIS X\n')
line = ''
for x in self.x_L_local:
line = line + str(x)+ ' '
line = line + '\n'
file_to_write.write(line)
file_to_write.write('AXIS Y\n')
line = ''
for y in self.y_L_local:
line = line + str(y)+ ' '
line = line + '\n'
file_to_write.write(line)
file_to_write.write('DATA\n')
for l in range(len(self.y_L_local)):
for c in range(len(self.x_L_local)):
file_to_write.write(str(dict_sollicitations['Dissolution_Energy'])+'\n')
file_to_write.close()
#-------------------------------------------------------------------------------
def Compute_etaiM_local(self,dict_algorithm,dict_material):
"""
From the grain geometry the phase variable is rebuilt.
The distance between the point of the mesh and the particle center determines the value of the variable
A cosine profile is applied inside the interface
Input :
itself (a grain)
an algorithm dictionnary (a dict)
a material dictionnary (a dict)
Output :
Nothing, but the grain gets an updated phase field (a nx x ny numpy array)
"""
x_min_local = min(self.l_border_x)-dict_material['w']
x_max_local = max(self.l_border_x)+dict_material['w']
y_min_local = min(self.l_border_y)-dict_material['w']
y_max_local = max(self.l_border_y)+dict_material['w']
x_L_local = np.arange(x_min_local,x_max_local+dict_algorithm['dx_local'],dict_algorithm['dx_local'])
y_L_local = np.arange(y_min_local,y_max_local+dict_algorithm['dy_local'],dict_algorithm['dy_local'])
self.x_L_local = x_L_local
self.y_L_local = y_L_local
# compute phase field
etai_M = np.array(np.zeros((len(y_L_local),len(x_L_local))))
for i_x in range(len(x_L_local)):
for i_y in range(len(y_L_local)):
p = np.array([x_L_local[i_x],y_L_local[len(y_L_local)-1-i_y]])
r = np.linalg.norm(self.center - p)
if p[1]>self.center[1]:
theta = math.acos((p[0]-self.center[0])/np.linalg.norm(self.center-p))
else :
theta= 2*math.pi - math.acos((p[0]-self.center[0])/np.linalg.norm(self.center-p))
L_theta_R_i = list(abs(np.array(self.l_theta_r)-theta))
R = self.l_r[L_theta_R_i.index(min(L_theta_R_i))]
#Cosine_Profile
if r<R-dict_material['w']/2:
etai_M[i_y][i_x] = 1
elif r>R+dict_material['w']/2:
etai_M[i_y][i_x] = 0
else :
etai_M[i_y][i_x] = 0.5*(1 + np.cos(math.pi*(r-R+dict_material['w']/2)/dict_material['w']))
self.etai_M = etai_M.copy()
#-------------------------------------------------------------------------------
def Write_txt_Decons_rebuild_local(self,dict_algorithm):
"""
Write a .txt file. This file is used to define initial condition of MOOSE simulation.
Input :
itself (a grain)
an algorithm dictionnary (a dict)
Output :
Nothing, but a .txt file is generated (a file)
"""
file_to_write = open('Data/g'+str(self.id)+'_'+str(dict_algorithm['i_PF'])+'.txt','w')
file_to_write.write('AXIS X\n')
line = ''
for x in self.x_L_local:
line = line + str(x)+ ' '
line = line + '\n'
file_to_write.write(line)
file_to_write.write('AXIS Y\n')
line = ''
for y in self.y_L_local:
line = line + str(y)+ ' '
line = line + '\n'
file_to_write.write(line)
file_to_write.write('DATA\n')
for l in range(len(self.y_L_local)):
for c in range(len(self.x_L_local)):
file_to_write.write(str(self.etai_M[-l-1][c])+'\n')
file_to_write.close()
#-------------------------------------------------------------------------------
def PFtoDEM_Multi_local(self,FileToRead,dict_algorithm):
"""
Read data from the moose simulation.
Input :
itself (a grain)
the template of the name to read (a string)
an algorithm dictionnary (a dict)
Output :
Nothing, but the phase field variable is updated (a nx x ny numpy array)
"""
#---------------------------------------------------------------------------
#Global parameters
#---------------------------------------------------------------------------
etai_M = np.zeros((len(self.y_L_local),len(self.x_L_local))) #etai
id_L = None
eta_selector_len = len(' <DataArray type="Float64" Name="etai')
end_len = len(' </DataArray>')
XYZ_selector_len = len(' <DataArray type="Float64" Name="Points"')
data_jump_len = len(' ')
for i_proc in range(dict_algorithm['np_proc']):
L_Work = [[], #X
[], #Y
[]] #etai
#---------------------------------------------------------------------------
#Reading file
#---------------------------------------------------------------------------
f = open(f'{FileToRead}_{i_proc}.vtu','r')
data = f.read()
f.close
lines = data.splitlines()
#iterations on line
for line in lines:
if line[0:eta_selector_len] == ' <DataArray type="Float64" Name="etai':
id_L = 2
elif line[0:XYZ_selector_len] == ' <DataArray type="Float64" Name="Points"':
id_L = 0
elif (line[0:end_len] == ' </DataArray>' or line[0:len(' <InformationKey')] == ' <InformationKey') and id_L != None:
id_L = None
elif line[0:data_jump_len] == ' ' and id_L == 2: #Read etai
line = line[data_jump_len:]
c_start = 0
for c_i in range(0,len(line)):
if line[c_i]==' ':
c_end = c_i
L_Work[id_L].append(float(line[c_start:c_end]))
c_start = c_i+1
L_Work[id_L].append(float(line[c_start:]))
elif line[0:data_jump_len] == ' ' and id_L == 0: #Read [X, Y, Z]
line = line[data_jump_len:]
XYZ_temp = []
c_start = 0
for c_i in range(0,len(line)):
if line[c_i]==' ':
c_end = c_i
XYZ_temp.append(float(line[c_start:c_end]))
if len(XYZ_temp)==3:
L_Work[0].append(XYZ_temp[0])
L_Work[1].append(XYZ_temp[1])
XYZ_temp = []
c_start = c_i+1
XYZ_temp.append(float(line[c_start:]))
L_Work[0].append(XYZ_temp[0])
L_Work[1].append(XYZ_temp[1])
#Adaptating data
for i in range(len(L_Work[0])):
#Interpolation method
L_dy = []
for y_i in self.y_L_local :
L_dy.append(abs(y_i - L_Work[1][i]))
L_dx = []
for x_i in self.x_L_local :
L_dx.append(abs(x_i - L_Work[0][i]))
etai_M[-1-list(L_dy).index(min(L_dy))][list(L_dx).index(min(L_dx))] = L_Work[2][i]
# Update
self.etai_M = etai_M.copy()
#-------------------------------------------------------------------------------
#Function
#-------------------------------------------------------------------------------
Classes
class Grain (dict_ic_to_real, Id_Eta=None, V=array([0, 0]), A=array([0, 0]))
-
Defining the grain.
The ic to real dictionnary is the data transmission between the temporary grain and the real one. Input : itself (a grain) a ic to real dictionnary (a dict) Output : a grain is generated (a grain)
Expand source code
class Grain: #------------------------------------------------------------------------------- def __init__(self, dict_ic_to_real, Id_Eta = None, V = np.array([0,0]), A = np.array([0,0])): """ Defining the grain. The ic to real dictionnary is the data transmission between the temporary grain and the real one. Input : itself (a grain) a ic to real dictionnary (a dict) Output : a grain is generated (a grain) """ #Id of the grain self.id = dict_ic_to_real['Id'] self.id_eta = Id_Eta #Material property self.dissolved = dict_ic_to_real['Type'] == 'Square' self.y = dict_ic_to_real['Y'] self.nu = dict_ic_to_real['Nu'] self.g = self.y /2/(1+self.nu) #shear modulus self.rho_surf = dict_ic_to_real['Rho_surf'] #kinematic self.v = V self.a = A self.theta = 0 self.w = 0 #dtheta/dt #position self.center = dict_ic_to_real['Center'] self.l_border = dict_ic_to_real['L_border'] self.l_border_x = dict_ic_to_real['L_border_x'] self.l_border_y = dict_ic_to_real['L_border_y'] #characteristic self.l_r = dict_ic_to_real['L_r'] self.l_theta_r = dict_ic_to_real['L_theta_r'] self.r_min = dict_ic_to_real['R_min'] self.r_max = dict_ic_to_real['R_max'] self.r_mean = dict_ic_to_real['R_mean'] self.surface = dict_ic_to_real['Surface'] self.m = dict_ic_to_real['Mass'] self.inertia = dict_ic_to_real['Inertia'] #------------------------------------------------------------------------------- def update_geometry_kinetic(self, V, A, W, DT): """ Update the acceleration and the velocity of a grain. Update geometrical parameters as border and center nodes. Input : itself (a grain) a speed (a 1 x 2 numpy array) an acceleration (a 1 x 2 numpy array) an angular speed (a float) a time step (a float) Ouput : Nothing, but the position of the grain is updated """ #translation self.v = V self.a = A for i in range(len(self.l_border)): self.l_border[i] = self.l_border[i] + self.v*DT self.l_border_x[i] = self.l_border_x[i] + self.v[0]*DT self.l_border_y[i] = self.l_border_y[i] + self.v[1]*DT self.center = self.center + self.v*DT #rotation self.w = W self.theta = self.theta + self.w*DT for i_theta_r in range(len(self.l_theta_r)) : theta_r = self.l_theta_r[i_theta_r] theta_r = theta_r + self.w*DT while theta_r >= 2*math.pi: theta_r = theta_r - 2*math.pi while theta_r < 0 : theta_r = theta_r + 2*math.pi self.l_theta_r[i_theta_r] = theta_r for i in range(len(self.l_border)): p = self.l_border[i] - self.center Rot_Matrix = np.array([[math.cos(self.w*DT), -math.sin(self.w*DT)], [math.sin(self.w*DT), math.cos(self.w*DT)]]) p = np.dot(Rot_Matrix,p) self.l_border[i] = p + self.center self.l_border_x[i] = p[0] + self.center[0] self.l_border_y[i] = p[1] + self.center[1] #------------------------------------------------------------------------------- def init_f_control(self,dict_sollicitations): """ Initialize the force applied to the grain. A gravity of g is applied. Input : itself (a grain) a sollicitations dictionnary (a dict) Ouput : Nothing, but the force applied on the grain is initialized """ self.fx = 0 self.fy = -dict_sollicitations['gravity']*self.m self.f = np.array([self.fx,self.fy]) self.mz = 0 #------------------------------------------------------------------------------- def update_f(self, Fx, Fy, p_application): """ Add a force to the grain. Input : itself (a grain) the value x and y of the force (two float) an applicaiton point (a 1 x 2 numpy array) Output : Nothing, but a force is applied to the grain """ self.fx = self.fx + Fx self.fy = self.fy + Fy self.f = np.array([self.fx,self.fy]) v1 = np.array([p_application[0]-self.center[0], p_application[1]-self.center[1], 0]) v2 = np.array([Fx, Fy, 0]) self.mz = self.mz + np.cross(v1,v2)[2] #------------------------------------------------------------------------------- def Geometricstudy_local(self,dict_geometry,dict_sample,simulation_report): """ Searching border of the grain. We iterate on y constant, we look for a value under and over 0.5. If both conditions are verified, there is a limit at this y Same with iteration on x constant. Then, searching Surface, Center of mass and Inertia. A Monte Carlo Method is applied. A box is defined, we take a random point and we look if it is inside or outside the grain. Properties are the statistic times the box properties. Input : itself (a grain) a geometry dictionnary (a dict) a sample dictionnary (a dict) a simulation report (a report) Output : Nothing, but geometric parameters are updated """ #------------------------------------------------------------------------- #load data needed n = dict_geometry['grain_discretisation'] x_L = self.x_L_local y_L = self.y_L_local #------------------------------------------------------------------------- L_border_old = [] for y_i in range(len(y_L)): L_extract_x = self.etai_M[y_i][:] if id == 1: L_extract_x = list(L_extract_x) L_extract_x.reverse() if max(L_extract_x)>0.5 and min(L_extract_x)<0.5: y_intersect = y_L[len(y_L)-1-y_i] for x_i in range(len(x_L)-1): if (L_extract_x[x_i]-0.5)*(L_extract_x[x_i+1]-0.5)<0: x_intersect = (0.5-L_extract_x[x_i])/(L_extract_x[x_i+1]-L_extract_x[x_i])*\ (x_L[x_i+1]-x_L[x_i]) + x_L[x_i] L_border_old.append(np.array([x_intersect,y_intersect])) for x_i in range(len(x_L)): L_extract_y = [] for y_i in range(len(y_L)): L_extract_y.append(self.etai_M[y_i][x_i]) if max(L_extract_y)>0.5 and min(L_extract_y)<0.5: x_intersect = x_L[x_i] for y_i in range(len(y_L)-1): if (L_extract_y[y_i]-0.5)*(L_extract_y[y_i+1]-0.5)<0: y_intersect = (0.5-L_extract_y[y_i])/(L_extract_y[y_i+1]-L_extract_y[y_i])*\ (y_L[len(y_L)-1-y_i-1]-y_L[len(y_L)-1-y_i]) + y_L[len(y_L)-1-y_i] L_border_old.append(np.array([x_intersect,y_intersect])) #Adaptating L_id_used = [0] L_border = [L_border_old[0]] HighValue = 100000000 #Large current_node = L_border_old[0] for j in range(1,len(L_border_old)): L_d = list(np.zeros(len(L_border_old))) for i in range(0,len(L_border_old)): node = L_border_old[i] if i not in L_id_used: d = np.linalg.norm(node - current_node) L_d[i] = d else : L_d[i] = HighValue #Value need to be larger than potential distance between node index_nearest_node = L_d.index(min(L_d)) nearest_node = L_border_old[index_nearest_node] current_node = nearest_node L_border.append(nearest_node) L_id_used.append(index_nearest_node) #Correcting L_d_final = [] for i in range(len(L_border)-1): L_d_final.append(np.linalg.norm(L_border[i+1] - L_border[i])) #look for really far points, we assume the first point is accurate d_final_mean = np.mean(L_d_final) while np.max(L_d_final) > 5 * d_final_mean : #5 here is an user choixe value i_error = L_d_final.index(np.max(L_d_final))+1 simulation_report.write('Point '+str(L_border[i_error])+' is deleted because it is detected as an error\n') L_border.pop(i_error) L_id_used.pop(i_error) L_d_final = [] for i in range(len(L_border)-1): L_d_final.append(np.linalg.norm(L_border[i+1] - L_border[i])) #------------------------------------------------------------------------------- #Reduce the number of nodes for a grain #------------------------------------------------------------------------------- Perimeter = 0 for i_p in range(len(L_border)-1): Perimeter = Perimeter + np.linalg.norm(L_border[i_p+1]-L_border[i_p]) Perimeter = Perimeter + np.linalg.norm(L_border[-1]-L_border[0]) distance_min = Perimeter/n L_border_adapted = [L_border[0]] for p in L_border[1:]: distance = np.linalg.norm(p-L_border_adapted[-1]) if distance >= distance_min: L_border_adapted.append(p) L_border = L_border_adapted L_border.append(L_border[0]) self.l_border = L_border #------------------------------------------------------------------------------- #Monte carlo method #------------------------------------------------------------------------------- min_max_defined = False for p in L_border[:-1] : if not min_max_defined: box_min_x = p[0] box_max_x = p[0] box_min_y = p[1] box_max_y = p[1] min_max_defined = True else: if p[0] < box_min_x: box_min_x = p[0] elif p[0] > box_max_x: box_max_x = p[0] if p[1] < box_min_y: box_min_y = p[1] elif p[1] > box_max_y: box_max_y = p[1] N_MonteCarlo = 3000 #The larger it is, the more accurate it is sigma = self.rho_surf #kg/µm2 M_Mass = 0 M_Center_Mass = np.array([0,0]) M_Inertia = 0 for i in range(N_MonteCarlo): P = np.array([random.uniform(box_min_x,box_max_x),random.uniform(box_min_y,box_max_y)]) if self.P_is_inside(P): M_Mass = M_Mass + sigma M_Center_Mass = M_Center_Mass + sigma*P M_Inertia = M_Inertia + sigma*np.dot(P,P) Mass = (box_max_x-box_min_x)*(box_max_y-box_min_y)/N_MonteCarlo*M_Mass Center_Mass = (box_max_x-box_min_x)*(box_max_y-box_min_y)/N_MonteCarlo*M_Center_Mass/Mass Inertia = (box_max_x-box_min_x)*(box_max_y-box_min_y)/N_MonteCarlo*M_Inertia-Mass*np.dot(Center_Mass,Center_Mass) #------------------------------------------------------------------------------- #Updating the grain geometry and properties #------------------------------------------------------------------------------- L_R = [] L_theta_R = [] L_border_x = [] L_border_y = [] for p in L_border[:-1]: L_R.append(np.linalg.norm(p-Center_Mass)) L_border_x.append(p[0]) L_border_y.append(p[1]) if (p-Center_Mass)[1] > 0: theta = math.acos((p-Center_Mass)[0]/np.linalg.norm(p-Center_Mass)) else : theta = 2*math.pi - math.acos((p-Center_Mass)[0]/np.linalg.norm(p-Center_Mass)) L_theta_R.append(theta) L_border_x.append(L_border_x[0]) L_border_y.append(L_border_y[0]) #reorganize lists L_R.reverse() L_theta_R.reverse() i_min_theta = L_theta_R.index(min(L_theta_R)) L_R = L_R[i_min_theta:]+L_R[:i_min_theta] L_theta_R = L_theta_R[i_min_theta:]+L_theta_R[:i_min_theta] self.r_min = np.min(L_R) self.r_max = np.max(L_R) self.r_mean = np.mean(L_R) self.l_r = L_R self.l_theta_r = L_theta_R self.surface = Mass/self.rho_surf self.m = Mass self.center = Center_Mass self.l_border_x = L_border_x self.l_border_y = L_border_y self.inertia = Inertia #------------------------------------------------------------------------------- def P_is_inside(self,P): """ Determine if a point P is inside a grain. See Franklin 1994, see Alonso-Marroquin 2009 Input : itself (a grain) a point (a 1 x 2 numpy array) Output : a Boolean, True if the point is inside the grain (a Boolean²) """ counter = 0 for i_p_border in range(len(self.l_border)-1): #consider only points if the coordinates frame the y-coordinate of the point if (self.l_border[i_p_border][1]-P[1])*(self.l_border[i_p_border+1][1]-P[1]) < 0 : x_border = self.l_border[i_p_border][0] + (self.l_border[i_p_border+1][0]-self.l_border[i_p_border][0])*(P[1]-self.l_border[i_p_border][1])/(self.l_border[i_p_border+1][1]-self.l_border[i_p_border][1]) if x_border > P[0] : counter = counter + 1 if counter % 2 == 0: return False else : return True #------------------------------------------------------------------------------- def Write_e_dissolution_local_txt(self,dict_algorithm,dict_sollicitations): """ Write an .txt file for MOOSE. This file described an homogenous dissolution field. Input : itself (a grain) an algorithm dictionnary (a dict) a sollicitations dictionnary (a dict) Output : Nothing, but a .txt file is generated (a file) """ file_to_write = open(f"Data/e_diss_g{self.id}_ite{dict_algorithm['i_PF']}.txt",'w') file_to_write.write('AXIS X\n') line = '' for x in self.x_L_local: line = line + str(x)+ ' ' line = line + '\n' file_to_write.write(line) file_to_write.write('AXIS Y\n') line = '' for y in self.y_L_local: line = line + str(y)+ ' ' line = line + '\n' file_to_write.write(line) file_to_write.write('DATA\n') for l in range(len(self.y_L_local)): for c in range(len(self.x_L_local)): file_to_write.write(str(dict_sollicitations['Dissolution_Energy'])+'\n') file_to_write.close() #------------------------------------------------------------------------------- def Compute_etaiM_local(self,dict_algorithm,dict_material): """ From the grain geometry the phase variable is rebuilt. The distance between the point of the mesh and the particle center determines the value of the variable A cosine profile is applied inside the interface Input : itself (a grain) an algorithm dictionnary (a dict) a material dictionnary (a dict) Output : Nothing, but the grain gets an updated phase field (a nx x ny numpy array) """ x_min_local = min(self.l_border_x)-dict_material['w'] x_max_local = max(self.l_border_x)+dict_material['w'] y_min_local = min(self.l_border_y)-dict_material['w'] y_max_local = max(self.l_border_y)+dict_material['w'] x_L_local = np.arange(x_min_local,x_max_local+dict_algorithm['dx_local'],dict_algorithm['dx_local']) y_L_local = np.arange(y_min_local,y_max_local+dict_algorithm['dy_local'],dict_algorithm['dy_local']) self.x_L_local = x_L_local self.y_L_local = y_L_local # compute phase field etai_M = np.array(np.zeros((len(y_L_local),len(x_L_local)))) for i_x in range(len(x_L_local)): for i_y in range(len(y_L_local)): p = np.array([x_L_local[i_x],y_L_local[len(y_L_local)-1-i_y]]) r = np.linalg.norm(self.center - p) if p[1]>self.center[1]: theta = math.acos((p[0]-self.center[0])/np.linalg.norm(self.center-p)) else : theta= 2*math.pi - math.acos((p[0]-self.center[0])/np.linalg.norm(self.center-p)) L_theta_R_i = list(abs(np.array(self.l_theta_r)-theta)) R = self.l_r[L_theta_R_i.index(min(L_theta_R_i))] #Cosine_Profile if r<R-dict_material['w']/2: etai_M[i_y][i_x] = 1 elif r>R+dict_material['w']/2: etai_M[i_y][i_x] = 0 else : etai_M[i_y][i_x] = 0.5*(1 + np.cos(math.pi*(r-R+dict_material['w']/2)/dict_material['w'])) self.etai_M = etai_M.copy() #------------------------------------------------------------------------------- def Write_txt_Decons_rebuild_local(self,dict_algorithm): """ Write a .txt file. This file is used to define initial condition of MOOSE simulation. Input : itself (a grain) an algorithm dictionnary (a dict) Output : Nothing, but a .txt file is generated (a file) """ file_to_write = open('Data/g'+str(self.id)+'_'+str(dict_algorithm['i_PF'])+'.txt','w') file_to_write.write('AXIS X\n') line = '' for x in self.x_L_local: line = line + str(x)+ ' ' line = line + '\n' file_to_write.write(line) file_to_write.write('AXIS Y\n') line = '' for y in self.y_L_local: line = line + str(y)+ ' ' line = line + '\n' file_to_write.write(line) file_to_write.write('DATA\n') for l in range(len(self.y_L_local)): for c in range(len(self.x_L_local)): file_to_write.write(str(self.etai_M[-l-1][c])+'\n') file_to_write.close() #------------------------------------------------------------------------------- def PFtoDEM_Multi_local(self,FileToRead,dict_algorithm): """ Read data from the moose simulation. Input : itself (a grain) the template of the name to read (a string) an algorithm dictionnary (a dict) Output : Nothing, but the phase field variable is updated (a nx x ny numpy array) """ #--------------------------------------------------------------------------- #Global parameters #--------------------------------------------------------------------------- etai_M = np.zeros((len(self.y_L_local),len(self.x_L_local))) #etai id_L = None eta_selector_len = len(' <DataArray type="Float64" Name="etai') end_len = len(' </DataArray>') XYZ_selector_len = len(' <DataArray type="Float64" Name="Points"') data_jump_len = len(' ') for i_proc in range(dict_algorithm['np_proc']): L_Work = [[], #X [], #Y []] #etai #--------------------------------------------------------------------------- #Reading file #--------------------------------------------------------------------------- f = open(f'{FileToRead}_{i_proc}.vtu','r') data = f.read() f.close lines = data.splitlines() #iterations on line for line in lines: if line[0:eta_selector_len] == ' <DataArray type="Float64" Name="etai': id_L = 2 elif line[0:XYZ_selector_len] == ' <DataArray type="Float64" Name="Points"': id_L = 0 elif (line[0:end_len] == ' </DataArray>' or line[0:len(' <InformationKey')] == ' <InformationKey') and id_L != None: id_L = None elif line[0:data_jump_len] == ' ' and id_L == 2: #Read etai line = line[data_jump_len:] c_start = 0 for c_i in range(0,len(line)): if line[c_i]==' ': c_end = c_i L_Work[id_L].append(float(line[c_start:c_end])) c_start = c_i+1 L_Work[id_L].append(float(line[c_start:])) elif line[0:data_jump_len] == ' ' and id_L == 0: #Read [X, Y, Z] line = line[data_jump_len:] XYZ_temp = [] c_start = 0 for c_i in range(0,len(line)): if line[c_i]==' ': c_end = c_i XYZ_temp.append(float(line[c_start:c_end])) if len(XYZ_temp)==3: L_Work[0].append(XYZ_temp[0]) L_Work[1].append(XYZ_temp[1]) XYZ_temp = [] c_start = c_i+1 XYZ_temp.append(float(line[c_start:])) L_Work[0].append(XYZ_temp[0]) L_Work[1].append(XYZ_temp[1]) #Adaptating data for i in range(len(L_Work[0])): #Interpolation method L_dy = [] for y_i in self.y_L_local : L_dy.append(abs(y_i - L_Work[1][i])) L_dx = [] for x_i in self.x_L_local : L_dx.append(abs(x_i - L_Work[0][i])) etai_M[-1-list(L_dy).index(min(L_dy))][list(L_dx).index(min(L_dx))] = L_Work[2][i] # Update self.etai_M = etai_M.copy()
Methods
def Compute_etaiM_local(self, dict_algorithm, dict_material)
-
From the grain geometry the phase variable is rebuilt.
The distance between the point of the mesh and the particle center determines the value of the variable A cosine profile is applied inside the interface
Input : itself (a grain) an algorithm dictionnary (a dict) a material dictionnary (a dict) Output : Nothing, but the grain gets an updated phase field (a nx x ny numpy array)
Expand source code
def Compute_etaiM_local(self,dict_algorithm,dict_material): """ From the grain geometry the phase variable is rebuilt. The distance between the point of the mesh and the particle center determines the value of the variable A cosine profile is applied inside the interface Input : itself (a grain) an algorithm dictionnary (a dict) a material dictionnary (a dict) Output : Nothing, but the grain gets an updated phase field (a nx x ny numpy array) """ x_min_local = min(self.l_border_x)-dict_material['w'] x_max_local = max(self.l_border_x)+dict_material['w'] y_min_local = min(self.l_border_y)-dict_material['w'] y_max_local = max(self.l_border_y)+dict_material['w'] x_L_local = np.arange(x_min_local,x_max_local+dict_algorithm['dx_local'],dict_algorithm['dx_local']) y_L_local = np.arange(y_min_local,y_max_local+dict_algorithm['dy_local'],dict_algorithm['dy_local']) self.x_L_local = x_L_local self.y_L_local = y_L_local # compute phase field etai_M = np.array(np.zeros((len(y_L_local),len(x_L_local)))) for i_x in range(len(x_L_local)): for i_y in range(len(y_L_local)): p = np.array([x_L_local[i_x],y_L_local[len(y_L_local)-1-i_y]]) r = np.linalg.norm(self.center - p) if p[1]>self.center[1]: theta = math.acos((p[0]-self.center[0])/np.linalg.norm(self.center-p)) else : theta= 2*math.pi - math.acos((p[0]-self.center[0])/np.linalg.norm(self.center-p)) L_theta_R_i = list(abs(np.array(self.l_theta_r)-theta)) R = self.l_r[L_theta_R_i.index(min(L_theta_R_i))] #Cosine_Profile if r<R-dict_material['w']/2: etai_M[i_y][i_x] = 1 elif r>R+dict_material['w']/2: etai_M[i_y][i_x] = 0 else : etai_M[i_y][i_x] = 0.5*(1 + np.cos(math.pi*(r-R+dict_material['w']/2)/dict_material['w'])) self.etai_M = etai_M.copy()
def Geometricstudy_local(self, dict_geometry, dict_sample, simulation_report)
-
Searching border of the grain.
We iterate on y constant, we look for a value under and over 0.5. If both conditions are verified, there is a limit at this y Same with iteration on x constant.
Then, searching Surface, Center of mass and Inertia. A Monte Carlo Method is applied. A box is defined, we take a random point and we look if it is inside or outside the grain. Properties are the statistic times the box properties.
Input : itself (a grain) a geometry dictionnary (a dict) a sample dictionnary (a dict) a simulation report (a report) Output : Nothing, but geometric parameters are updated
Expand source code
def Geometricstudy_local(self,dict_geometry,dict_sample,simulation_report): """ Searching border of the grain. We iterate on y constant, we look for a value under and over 0.5. If both conditions are verified, there is a limit at this y Same with iteration on x constant. Then, searching Surface, Center of mass and Inertia. A Monte Carlo Method is applied. A box is defined, we take a random point and we look if it is inside or outside the grain. Properties are the statistic times the box properties. Input : itself (a grain) a geometry dictionnary (a dict) a sample dictionnary (a dict) a simulation report (a report) Output : Nothing, but geometric parameters are updated """ #------------------------------------------------------------------------- #load data needed n = dict_geometry['grain_discretisation'] x_L = self.x_L_local y_L = self.y_L_local #------------------------------------------------------------------------- L_border_old = [] for y_i in range(len(y_L)): L_extract_x = self.etai_M[y_i][:] if id == 1: L_extract_x = list(L_extract_x) L_extract_x.reverse() if max(L_extract_x)>0.5 and min(L_extract_x)<0.5: y_intersect = y_L[len(y_L)-1-y_i] for x_i in range(len(x_L)-1): if (L_extract_x[x_i]-0.5)*(L_extract_x[x_i+1]-0.5)<0: x_intersect = (0.5-L_extract_x[x_i])/(L_extract_x[x_i+1]-L_extract_x[x_i])*\ (x_L[x_i+1]-x_L[x_i]) + x_L[x_i] L_border_old.append(np.array([x_intersect,y_intersect])) for x_i in range(len(x_L)): L_extract_y = [] for y_i in range(len(y_L)): L_extract_y.append(self.etai_M[y_i][x_i]) if max(L_extract_y)>0.5 and min(L_extract_y)<0.5: x_intersect = x_L[x_i] for y_i in range(len(y_L)-1): if (L_extract_y[y_i]-0.5)*(L_extract_y[y_i+1]-0.5)<0: y_intersect = (0.5-L_extract_y[y_i])/(L_extract_y[y_i+1]-L_extract_y[y_i])*\ (y_L[len(y_L)-1-y_i-1]-y_L[len(y_L)-1-y_i]) + y_L[len(y_L)-1-y_i] L_border_old.append(np.array([x_intersect,y_intersect])) #Adaptating L_id_used = [0] L_border = [L_border_old[0]] HighValue = 100000000 #Large current_node = L_border_old[0] for j in range(1,len(L_border_old)): L_d = list(np.zeros(len(L_border_old))) for i in range(0,len(L_border_old)): node = L_border_old[i] if i not in L_id_used: d = np.linalg.norm(node - current_node) L_d[i] = d else : L_d[i] = HighValue #Value need to be larger than potential distance between node index_nearest_node = L_d.index(min(L_d)) nearest_node = L_border_old[index_nearest_node] current_node = nearest_node L_border.append(nearest_node) L_id_used.append(index_nearest_node) #Correcting L_d_final = [] for i in range(len(L_border)-1): L_d_final.append(np.linalg.norm(L_border[i+1] - L_border[i])) #look for really far points, we assume the first point is accurate d_final_mean = np.mean(L_d_final) while np.max(L_d_final) > 5 * d_final_mean : #5 here is an user choixe value i_error = L_d_final.index(np.max(L_d_final))+1 simulation_report.write('Point '+str(L_border[i_error])+' is deleted because it is detected as an error\n') L_border.pop(i_error) L_id_used.pop(i_error) L_d_final = [] for i in range(len(L_border)-1): L_d_final.append(np.linalg.norm(L_border[i+1] - L_border[i])) #------------------------------------------------------------------------------- #Reduce the number of nodes for a grain #------------------------------------------------------------------------------- Perimeter = 0 for i_p in range(len(L_border)-1): Perimeter = Perimeter + np.linalg.norm(L_border[i_p+1]-L_border[i_p]) Perimeter = Perimeter + np.linalg.norm(L_border[-1]-L_border[0]) distance_min = Perimeter/n L_border_adapted = [L_border[0]] for p in L_border[1:]: distance = np.linalg.norm(p-L_border_adapted[-1]) if distance >= distance_min: L_border_adapted.append(p) L_border = L_border_adapted L_border.append(L_border[0]) self.l_border = L_border #------------------------------------------------------------------------------- #Monte carlo method #------------------------------------------------------------------------------- min_max_defined = False for p in L_border[:-1] : if not min_max_defined: box_min_x = p[0] box_max_x = p[0] box_min_y = p[1] box_max_y = p[1] min_max_defined = True else: if p[0] < box_min_x: box_min_x = p[0] elif p[0] > box_max_x: box_max_x = p[0] if p[1] < box_min_y: box_min_y = p[1] elif p[1] > box_max_y: box_max_y = p[1] N_MonteCarlo = 3000 #The larger it is, the more accurate it is sigma = self.rho_surf #kg/µm2 M_Mass = 0 M_Center_Mass = np.array([0,0]) M_Inertia = 0 for i in range(N_MonteCarlo): P = np.array([random.uniform(box_min_x,box_max_x),random.uniform(box_min_y,box_max_y)]) if self.P_is_inside(P): M_Mass = M_Mass + sigma M_Center_Mass = M_Center_Mass + sigma*P M_Inertia = M_Inertia + sigma*np.dot(P,P) Mass = (box_max_x-box_min_x)*(box_max_y-box_min_y)/N_MonteCarlo*M_Mass Center_Mass = (box_max_x-box_min_x)*(box_max_y-box_min_y)/N_MonteCarlo*M_Center_Mass/Mass Inertia = (box_max_x-box_min_x)*(box_max_y-box_min_y)/N_MonteCarlo*M_Inertia-Mass*np.dot(Center_Mass,Center_Mass) #------------------------------------------------------------------------------- #Updating the grain geometry and properties #------------------------------------------------------------------------------- L_R = [] L_theta_R = [] L_border_x = [] L_border_y = [] for p in L_border[:-1]: L_R.append(np.linalg.norm(p-Center_Mass)) L_border_x.append(p[0]) L_border_y.append(p[1]) if (p-Center_Mass)[1] > 0: theta = math.acos((p-Center_Mass)[0]/np.linalg.norm(p-Center_Mass)) else : theta = 2*math.pi - math.acos((p-Center_Mass)[0]/np.linalg.norm(p-Center_Mass)) L_theta_R.append(theta) L_border_x.append(L_border_x[0]) L_border_y.append(L_border_y[0]) #reorganize lists L_R.reverse() L_theta_R.reverse() i_min_theta = L_theta_R.index(min(L_theta_R)) L_R = L_R[i_min_theta:]+L_R[:i_min_theta] L_theta_R = L_theta_R[i_min_theta:]+L_theta_R[:i_min_theta] self.r_min = np.min(L_R) self.r_max = np.max(L_R) self.r_mean = np.mean(L_R) self.l_r = L_R self.l_theta_r = L_theta_R self.surface = Mass/self.rho_surf self.m = Mass self.center = Center_Mass self.l_border_x = L_border_x self.l_border_y = L_border_y self.inertia = Inertia
def PFtoDEM_Multi_local(self, FileToRead, dict_algorithm)
-
Read data from the moose simulation.
Input : itself (a grain) the template of the name to read (a string) an algorithm dictionnary (a dict) Output : Nothing, but the phase field variable is updated (a nx x ny numpy array)
Expand source code
def PFtoDEM_Multi_local(self,FileToRead,dict_algorithm): """ Read data from the moose simulation. Input : itself (a grain) the template of the name to read (a string) an algorithm dictionnary (a dict) Output : Nothing, but the phase field variable is updated (a nx x ny numpy array) """ #--------------------------------------------------------------------------- #Global parameters #--------------------------------------------------------------------------- etai_M = np.zeros((len(self.y_L_local),len(self.x_L_local))) #etai id_L = None eta_selector_len = len(' <DataArray type="Float64" Name="etai') end_len = len(' </DataArray>') XYZ_selector_len = len(' <DataArray type="Float64" Name="Points"') data_jump_len = len(' ') for i_proc in range(dict_algorithm['np_proc']): L_Work = [[], #X [], #Y []] #etai #--------------------------------------------------------------------------- #Reading file #--------------------------------------------------------------------------- f = open(f'{FileToRead}_{i_proc}.vtu','r') data = f.read() f.close lines = data.splitlines() #iterations on line for line in lines: if line[0:eta_selector_len] == ' <DataArray type="Float64" Name="etai': id_L = 2 elif line[0:XYZ_selector_len] == ' <DataArray type="Float64" Name="Points"': id_L = 0 elif (line[0:end_len] == ' </DataArray>' or line[0:len(' <InformationKey')] == ' <InformationKey') and id_L != None: id_L = None elif line[0:data_jump_len] == ' ' and id_L == 2: #Read etai line = line[data_jump_len:] c_start = 0 for c_i in range(0,len(line)): if line[c_i]==' ': c_end = c_i L_Work[id_L].append(float(line[c_start:c_end])) c_start = c_i+1 L_Work[id_L].append(float(line[c_start:])) elif line[0:data_jump_len] == ' ' and id_L == 0: #Read [X, Y, Z] line = line[data_jump_len:] XYZ_temp = [] c_start = 0 for c_i in range(0,len(line)): if line[c_i]==' ': c_end = c_i XYZ_temp.append(float(line[c_start:c_end])) if len(XYZ_temp)==3: L_Work[0].append(XYZ_temp[0]) L_Work[1].append(XYZ_temp[1]) XYZ_temp = [] c_start = c_i+1 XYZ_temp.append(float(line[c_start:])) L_Work[0].append(XYZ_temp[0]) L_Work[1].append(XYZ_temp[1]) #Adaptating data for i in range(len(L_Work[0])): #Interpolation method L_dy = [] for y_i in self.y_L_local : L_dy.append(abs(y_i - L_Work[1][i])) L_dx = [] for x_i in self.x_L_local : L_dx.append(abs(x_i - L_Work[0][i])) etai_M[-1-list(L_dy).index(min(L_dy))][list(L_dx).index(min(L_dx))] = L_Work[2][i] # Update self.etai_M = etai_M.copy()
def P_is_inside(self, P)
-
Determine if a point P is inside a grain.
See Franklin 1994, see Alonso-Marroquin 2009
Input : itself (a grain) a point (a 1 x 2 numpy array) Output : a Boolean, True if the point is inside the grain (a Boolean²)
Expand source code
def P_is_inside(self,P): """ Determine if a point P is inside a grain. See Franklin 1994, see Alonso-Marroquin 2009 Input : itself (a grain) a point (a 1 x 2 numpy array) Output : a Boolean, True if the point is inside the grain (a Boolean²) """ counter = 0 for i_p_border in range(len(self.l_border)-1): #consider only points if the coordinates frame the y-coordinate of the point if (self.l_border[i_p_border][1]-P[1])*(self.l_border[i_p_border+1][1]-P[1]) < 0 : x_border = self.l_border[i_p_border][0] + (self.l_border[i_p_border+1][0]-self.l_border[i_p_border][0])*(P[1]-self.l_border[i_p_border][1])/(self.l_border[i_p_border+1][1]-self.l_border[i_p_border][1]) if x_border > P[0] : counter = counter + 1 if counter % 2 == 0: return False else : return True
def Write_e_dissolution_local_txt(self, dict_algorithm, dict_sollicitations)
-
Write an .txt file for MOOSE. This file described an homogenous dissolution field.
Input : itself (a grain) an algorithm dictionnary (a dict) a sollicitations dictionnary (a dict) Output : Nothing, but a .txt file is generated (a file)
Expand source code
def Write_e_dissolution_local_txt(self,dict_algorithm,dict_sollicitations): """ Write an .txt file for MOOSE. This file described an homogenous dissolution field. Input : itself (a grain) an algorithm dictionnary (a dict) a sollicitations dictionnary (a dict) Output : Nothing, but a .txt file is generated (a file) """ file_to_write = open(f"Data/e_diss_g{self.id}_ite{dict_algorithm['i_PF']}.txt",'w') file_to_write.write('AXIS X\n') line = '' for x in self.x_L_local: line = line + str(x)+ ' ' line = line + '\n' file_to_write.write(line) file_to_write.write('AXIS Y\n') line = '' for y in self.y_L_local: line = line + str(y)+ ' ' line = line + '\n' file_to_write.write(line) file_to_write.write('DATA\n') for l in range(len(self.y_L_local)): for c in range(len(self.x_L_local)): file_to_write.write(str(dict_sollicitations['Dissolution_Energy'])+'\n') file_to_write.close()
def Write_txt_Decons_rebuild_local(self, dict_algorithm)
-
Write a .txt file. This file is used to define initial condition of MOOSE simulation.
Input : itself (a grain) an algorithm dictionnary (a dict) Output : Nothing, but a .txt file is generated (a file)
Expand source code
def Write_txt_Decons_rebuild_local(self,dict_algorithm): """ Write a .txt file. This file is used to define initial condition of MOOSE simulation. Input : itself (a grain) an algorithm dictionnary (a dict) Output : Nothing, but a .txt file is generated (a file) """ file_to_write = open('Data/g'+str(self.id)+'_'+str(dict_algorithm['i_PF'])+'.txt','w') file_to_write.write('AXIS X\n') line = '' for x in self.x_L_local: line = line + str(x)+ ' ' line = line + '\n' file_to_write.write(line) file_to_write.write('AXIS Y\n') line = '' for y in self.y_L_local: line = line + str(y)+ ' ' line = line + '\n' file_to_write.write(line) file_to_write.write('DATA\n') for l in range(len(self.y_L_local)): for c in range(len(self.x_L_local)): file_to_write.write(str(self.etai_M[-l-1][c])+'\n') file_to_write.close()
def init_f_control(self, dict_sollicitations)
-
Initialize the force applied to the grain.
A gravity of g is applied.
Input : itself (a grain) a sollicitations dictionnary (a dict) Ouput : Nothing, but the force applied on the grain is initialized
Expand source code
def init_f_control(self,dict_sollicitations): """ Initialize the force applied to the grain. A gravity of g is applied. Input : itself (a grain) a sollicitations dictionnary (a dict) Ouput : Nothing, but the force applied on the grain is initialized """ self.fx = 0 self.fy = -dict_sollicitations['gravity']*self.m self.f = np.array([self.fx,self.fy]) self.mz = 0
def update_f(self, Fx, Fy, p_application)
-
Add a force to the grain.
Input : itself (a grain) the value x and y of the force (two float) an applicaiton point (a 1 x 2 numpy array) Output : Nothing, but a force is applied to the grain
Expand source code
def update_f(self, Fx, Fy, p_application): """ Add a force to the grain. Input : itself (a grain) the value x and y of the force (two float) an applicaiton point (a 1 x 2 numpy array) Output : Nothing, but a force is applied to the grain """ self.fx = self.fx + Fx self.fy = self.fy + Fy self.f = np.array([self.fx,self.fy]) v1 = np.array([p_application[0]-self.center[0], p_application[1]-self.center[1], 0]) v2 = np.array([Fx, Fy, 0]) self.mz = self.mz + np.cross(v1,v2)[2]
def update_geometry_kinetic(self, V, A, W, DT)
-
Update the acceleration and the velocity of a grain. Update geometrical parameters as border and center nodes.
Input : itself (a grain) a speed (a 1 x 2 numpy array) an acceleration (a 1 x 2 numpy array) an angular speed (a float) a time step (a float) Ouput : Nothing, but the position of the grain is updated
Expand source code
def update_geometry_kinetic(self, V, A, W, DT): """ Update the acceleration and the velocity of a grain. Update geometrical parameters as border and center nodes. Input : itself (a grain) a speed (a 1 x 2 numpy array) an acceleration (a 1 x 2 numpy array) an angular speed (a float) a time step (a float) Ouput : Nothing, but the position of the grain is updated """ #translation self.v = V self.a = A for i in range(len(self.l_border)): self.l_border[i] = self.l_border[i] + self.v*DT self.l_border_x[i] = self.l_border_x[i] + self.v[0]*DT self.l_border_y[i] = self.l_border_y[i] + self.v[1]*DT self.center = self.center + self.v*DT #rotation self.w = W self.theta = self.theta + self.w*DT for i_theta_r in range(len(self.l_theta_r)) : theta_r = self.l_theta_r[i_theta_r] theta_r = theta_r + self.w*DT while theta_r >= 2*math.pi: theta_r = theta_r - 2*math.pi while theta_r < 0 : theta_r = theta_r + 2*math.pi self.l_theta_r[i_theta_r] = theta_r for i in range(len(self.l_border)): p = self.l_border[i] - self.center Rot_Matrix = np.array([[math.cos(self.w*DT), -math.sin(self.w*DT)], [math.sin(self.w*DT), math.cos(self.w*DT)]]) p = np.dot(Rot_Matrix,p) self.l_border[i] = p + self.center self.l_border_x[i] = p[0] + self.center[0] self.l_border_y[i] = p[1] + self.center[1]