Module Owntools
@author: Alexandre Sac–Morane alexandre.sac-morane@uclouvain.be
This file contains the different functions used in the simulation. This tools can be : - a little function - a plot to debug or print something - a postprocess
Expand source code
# -*- coding: utf-8 -*-
"""
@author: Alexandre Sac--Morane
alexandre.sac-morane@uclouvain.be
This file contains the different functions used in the simulation.
This tools can be : - a little function
- a plot to debug or print something
- a postprocess
"""
#-------------------------------------------------------------------------------
#Librairies
#-------------------------------------------------------------------------------
import numpy as np
import math
import Report
import matplotlib.pyplot as plt
import os
import shutil
from pathlib import Path
import pickle
from pathlib import Path
from datetime import datetime
import random
import imageio
import Contact
import Contact_gw
import Grain
#-------------------------------------------------------------------------------
class Grain_pp:
def __init__(self, Id, Dissolved, Center, Coordinate_x, Coordinate_y):
"""
Defining a grain for the postprocess
Input :
itself (a grain_pp)
an id (a int)
a Boolean to know if the grain is dissolvable (a Boolean)
a center (a 1 x 2 numpy array)
two lists of vertices coordinates x and y (two lists)
Output :
Nothing, but a post process grain is generated
"""
self.id = Id
self.dissolved = Dissolved
self.center = Center
self.coordinate_x = Coordinate_x
self.coordinate_y = Coordinate_y
#-------------------------------------------------------------------------------
class Contact_pp:
def __init__(self, Id_g1, Id_g2, L_g, Normal):
"""
Defining a contact grain - grain for the postprocess.
Input :
itself (a contact_pp)
the ids of the grains (two int)
a list of post process grains (a list)
the value of normal reaction of the contact (a float)
Output :
Nothing, but a post process contact grain - grain is generated
"""
for g in L_g:
if g.id == Id_g1:
self.g1 = g
elif g.id == Id_g2:
self.g2 = g
self.normal = Normal
def plot(self, normal_ref):
"""
Prepare the chain force plot.
Input :
itself (a contact_pp)
a reference value (a float)
Output :
Nothing, but the post process contact gets the ratio of the normal force with the reference value as a new attribut (a float)
"""
L_x = [self.g1.center[0], self.g2.center[0]]
L_y = [self.g1.center[1], self.g2.center[1]]
ratio_normal = self.normal/normal_ref
return L_x, L_y, ratio_normal
#-------------------------------------------------------------------------------
class Contact_gw_pp:
def __init__(self, Id_g, L_g, Nature, Limit, Normal):
"""
Defining a contact grain-wall for the postprocess.
Input :
itself (a contact_gw_pp)
a id of the grain (a float)
the list of the post process grain (a list)
the nature of the wall (a string)
the value of normal reaction of the contact (a float)
"""
for g in L_g:
if g.id == Id_g:
self.g = g
self.nature = Nature
self.limit = Limit
self.normal = Normal
def plot(self, normal_ref):
"""
Prepare the chain force plot.
Input :
itself (a contact_gw_pp)
a reference value (a float)
Output :
Nothing, but the post process contact gets the ratio of the normal force with the reference value as a new attribut (a float)
"""
if self.nature == 'gwx_min' or self.nature == 'gwx_max':
virtual_center = [self.limit, self.g.center[1]]
elif self.nature == 'gwy_min' or self.nature == 'gwy_max':
virtual_center = [ self.g.center[0], self.limit]
L_x = [self.g.center[0], virtual_center[0]]
L_y = [self.g.center[1], virtual_center[1]]
ratio_normal = self.normal/normal_ref
return L_x, L_y, ratio_normal
#-------------------------------------------------------------------------------
#functions
#-------------------------------------------------------------------------------
def index_to_str(j):
"""
Convert an integer to a float with 3 components
Input :
an integer (a int)
Output :
a string (a string)
"""
if j < 10:
j_str = '00'+str(j)
elif 10 <= j and j < 100:
j_str = '0'+str(j)
else :
j_str = str(j)
return j_str
#-------------------------------------------------------------------------------
def Stop_Debug(simulation_report):
"""
Stop simulation for debugging.
Input :
the simulation report (a report)
Output :
Nothing, but simulations stops
"""
simulation_report.write('Stop because after it is not corrected !\n')
simulation_report.end(datetime.now())
raise ValueError('Stop because after it is not corrected !')
#-------------------------------------------------------------------------------
def Debug_DEM_f(dict_algorithm, dict_sample):
"""
Plot the configuration of the grains during a DEM step.
Input:
an algorithm dictionnary (a dict)
a sample dictionnary (a dict)
Output :
Nothing, but a .png file is generated (a file)
"""
#-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
#load data needed
x_min = dict_sample['x_box_min']
x_max = dict_sample['x_box_max']
y_min = dict_sample['y_box_min']
y_max = dict_sample['y_box_max']
#-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
fig = plt.figure(1,figsize=(16,9.12))
for grain in dict_sample['L_g']:
plt.plot(grain.l_border_x,grain.l_border_y,'k')
for contact in dict_sample['L_contact'] :
alpha = (contact.g1.r_mean*0.1 + contact.g2.r_mean*0.1)/2
M = (contact.g1.center + contact.g2.center)/2
plt.plot([M[0], M[0]+contact.pc_normal[0]*alpha], [M[1], M[1]+contact.pc_normal[1]*alpha],'k')
plt.plot([M[0], M[0]+contact.pc_tangential[0]*alpha], [M[1], M[1]+contact.pc_tangential[1]*alpha],'k')
for contact in dict_sample['L_contact_gw'] :
alpha = contact.g.r_mean*0.1
if contact.nature == 'gwy_min' or contact.nature == 'gwy_max':
M = np.array([contact.g.center[0],contact.limit])
elif contact.nature == 'gwx_min' or contact.nature == 'gwx_max':
M = np.array([contact.limit,contact.g.center[1]])
plt.plot([M[0], M[0]+contact.nwg[0]*alpha], [M[1], M[1]+contact.nwg[1]*alpha],'k')
plt.plot([M[0], M[0]+contact.twg[0]*alpha], [M[1], M[1]+contact.twg[1]*alpha],'k')
plt.plot([x_min,x_max,x_max,x_min,x_min],[y_min,y_min,y_max,y_max,y_min],'k')
plt.axis("equal")
fig.savefig('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/png/Config_'+str(dict_algorithm['i_DEM'])+'.png')
plt.close(1)
#-------------------------------------------------------------------------------
def Debug_configuration(dict_algorithm,dict_sample):
"""
Plot the configuration of the grains before / after the DEM step.
Input:
an algorithm dictionnary (a dict)
a sample dictionnary (a dict)
Output :
Nothing, but a .png file is generated (a file)
"""
#-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
#load data needed
x_min = dict_sample['x_box_min']
x_max = dict_sample['x_box_max']
y_min = dict_sample['y_box_min']
y_max = dict_sample['y_box_max']
#-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
#look for the name of the new plot
template_name = 'Debug/DEM_ite/PF_ite_'
j = 0
plotpath = Path(template_name+str(j)+'.png')
while plotpath.exists():
j = j + 1
plotpath = Path(template_name+str(j)+'.png')
name = template_name+str(j)+'.png'
#create the plot
fig = plt.figure(1,figsize=(16,9.12))
for grain in dict_sample['L_g']:
if grain.dissolved:
plt.plot(grain.l_border_x,grain.l_border_y,'k-.')
else:
plt.plot(grain.l_border_x,grain.l_border_y,'k')
plt.plot([x_min,x_max,x_max,x_min,x_min],[y_min,y_min,y_max,y_max,y_min],'k')
plt.axis("equal")
fig.savefig(name)
plt.close(1)
#-------------------------------------------------------------------------------
def Debug_Trackers(dict_tracker):
"""
Plot the trakers used during PFDEM simulation
Input :
a tracker dictionnary (a dict)
Output :
Nothing, but severals .png files are generated (files)
"""
#Trackers
fig = plt.figure(1,figsize=(16,9.12))
plt.plot(dict_tracker['t_L'],dict_tracker['S_grains_dissolvable_L'])
plt.title('Evolution of the dissolvable grains surface')
fig.savefig('Debug/Evolution_Dissolvable_Surface.png')
plt.close(1)
fig = plt.figure(1,figsize=(16,9.12))
plt.plot(dict_tracker['S_dissolved_perc_dissolvable_L'][:-1],dict_tracker['k0_xmin_L'],label='k0 with xmin')
plt.plot(dict_tracker['S_dissolved_perc_dissolvable_L'][:-1],dict_tracker['k0_xmax_L'],label='k0 with xmax')
plt.title('Evolution of the k0')
plt.xlabel('Percentage of dissolvable grains surface dissolved (%)')
fig.savefig('Debug/Evolution_k0_with_percentage_dissolved.png')
plt.close(1)
#-------------------------------------------------------------------------------
def Debug_Trackers_DEM(dict_algorithm,dict_sollicitations,dict_tracker):
"""
Plot the trakers used during DEM simulation.
Input :
an algorithm dictionnary (a dict)
a sollicitations dictionnary (a dict)
a tracker dictionnary (a dict)
Output :
Nothing, but .png file is generated (a file)
"""
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2,2, figsize=(16,9),num=1)
ax1.set_title('Mean kinetic energy (e-12 J)')
ax1.plot(dict_tracker['Ecin'])
ax1.plot([0, len(dict_tracker['Ecin'])-1],[dict_algorithm['Ecin_stop'], dict_algorithm['Ecin_stop']],'r')
ax2.set_title('Mean force applied (µN)')
ax2.plot(dict_tracker['Force_applied'])
ax3.set_title('k0s (-)')
ax3.plot(dict_tracker['k0_xmin'],label='xmin')
ax3.plot(dict_tracker['k0_xmax'],label='xmax')
ax3.legend()
ax4.set_title('About the upper plate')
ax4.plot(dict_tracker['y_box_max'], color = 'blue')
ax4.set_ylabel('Coordinate y (µm)', color = 'blue')
ax4.tick_params(axis ='y', labelcolor = 'blue')
ax4a = ax4.twinx()
ax4a.plot(range(50,len(dict_tracker['Force_on_upper_wall'])),dict_tracker['Force_on_upper_wall'][50:], color = 'orange')
ax4a.plot([50, len(dict_tracker['Force_on_upper_wall'])-1],[dict_sollicitations['Vertical_Confinement_Force'], dict_sollicitations['Vertical_Confinement_Force']], color = 'red')
ax4a.set_ylabel('Force applied (µN)', color = 'orange')
ax4a.tick_params(axis ='y', labelcolor = 'orange')
fig.savefig('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/Trackers.png')
plt.close(1)
#-------------------------------------------------------------------------------
def Sort_Files(name_template,dict_algorithm):
"""
Sort files generated by MOOSE to different directories.
Input :
a template of the simulation files (a string)
an algorithm dictionnary (a dict)
Output :
Nothing, but files are sorted
"""
#master simulation
os.rename(name_template+'_out.e','Output/'+name_template+'_out.e')
os.rename(name_template+'.i','Input/'+name_template+'.i')
if Path('Output/'+name_template).exists():
shutil.rmtree('Output/'+name_template)
os.mkdir('Output/'+name_template)
j = 0
j_str = index_to_str(j)
folderpath = Path(name_template+'_other_'+j_str+'.pvtu')
while folderpath.exists():
for i_proc in range(dict_algorithm['np_proc']):
os.rename(name_template+'_other_'+j_str+'_'+str(i_proc)+'.vtu','Output/'+name_template+'/'+name_template+'_other_'+j_str+'_'+str(i_proc)+'.vtu')
os.rename(name_template+'_other_'+j_str+'.pvtu','Output/'+name_template+'/'+name_template+'_other_'+j_str+'.pvtu')
j = j + 1
j_str = index_to_str(j)
folderpath = Path(name_template+'_other_'+j_str+'.pvtu')
return index_to_str(j-1)
#-------------------------------------------------------------------------------
def error_on_ymax_f(dy,overlap_L,k_L,Force_target) :
"""
Compute the function f to control the upper wall. It is the difference between the force applied and the target value.
Input :
an increment of the upper wall position (a float)
a list of overlap for contact between grain and upper wall (a list)
a list of spring for contact between grain and upper wall (a list)
a confinement force (a float)
Output :
the difference between the force applied and the confinement (a float)
"""
f = Force_target
for i in range(len(overlap_L)):
f = f - k_L[i]*(max(overlap_L[i]-dy,0))**(3/2)
return f
#-------------------------------------------------------------------------------
def error_on_ymax_df(dy,overlap_L,k_L) :
"""
Compute the derivative function df to control the upper wall (error_on_ymax_f()).
Input :
an increment of the upper wall position (a float)
a list of overlap for contact between grain and upper wall (a list)
a list of spring for contact between grain and upper wall (a list)
Output :
the derivative of error_on_ymax_f() (a float)
"""
df = 0
for i in range(len(overlap_L)):
df = df + 3/2*k_L[i]*(max(overlap_L[i]-dy,0))**(1/2)
return df
#-------------------------------------------------------------------------------
def Control_y_max_NR(dict_sample,dict_sollicitations):
"""
Control the upper wall to apply force.
A Newton-Raphson method is applied to verify the confinement.
Input :
a sample dictionnary (a dict)
a sollicitations dictionnary (a dict)
Output :
Nothing, but the sample dictionnary is updated, concerning the upper wall position and force applied (two floats)
"""
#-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
#load data needed
Force_target = dict_sollicitations['Vertical_Confinement_Force']
#-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
F = 0
overlap_L = []
k_L = []
for contact in dict_sample['L_contact_gw']:
if contact.nature == 'gwy_max':
F = F + contact.Fwg_n
overlap_L.append(contact.overlap)
k_L.append(contact.k)
#compute force applied, save contact overlap and spring
if overlap_L != []:
i_NR = 0
dy = 0
ite_criteria = True
if -0.01*Force_target<error_on_ymax_f(dy,overlap_L,k_L,Force_target) and error_on_ymax_f(dy,overlap_L,k_L,Force_target)<0.01*Force_target:
ite_criteria = False
while ite_criteria :
i_NR = i_NR + 1
dy = dy - error_on_ymax_f(dy,overlap_L,k_L,Force_target)/error_on_ymax_df(dy,overlap_L,k_L)
if i_NR > 100:
ite_criteria = False
if -0.01*Force_target<error_on_ymax_f(dy,overlap_L,k_L,Force_target) and error_on_ymax_f(dy,overlap_L,k_L,Force_target)<0.01*Force_target:
ite_criteria = False
dict_sample['y_box_max'] = dict_sample['y_box_max'] + dy
else :
#if there is no contact with the upper wall, the wall is reset
dict_sample['y_box_max'] = Reset_y_max(dict_sample['L_g'],Force_target)
for contact in dict_sample['L_contact_gw']:
if contact.nature == 'gwy_max':
#reactualisation
contact.limit = dict_sample['y_box_max']
#Update dict
dict_sollicitations['Force_on_upper_wall'] = F
#-------------------------------------------------------------------------------
def Reset_y_max(L_g,Force):
"""
The upper wall is located as a single contact verify the target value.
Input :
the list of temporary grains (a list)
the confinement force (a float)
Output :
the upper wall position (a float)
"""
print('Reset of the y_max on the upper grain')
y_max = None
id_grain_max = None
for id_grain in range(len(L_g)):
grain = L_g[id_grain]
y_max_grain = max(grain.l_border_y)
if y_max != None and y_max_grain > y_max:
y_max = y_max_grain
id_grain_max = id_grain
elif y_max == None:
y_max = y_max_grain
id_grain_max = id_grain
k = 5*4/3*L_g[id_grain_max].y/(1-L_g[id_grain_max].nu*L_g[id_grain_max].nu)*math.sqrt(L_g[id_grain_max].r_mean)
y_max = y_max - (Force/k)**(2/3)
return y_max
#-------------------------------------------------------------------------------
def Compute_k0(dict_sample,dict_sollicitations):
"""
Compute the k0 = sigma_2 / sigma_1 ratio.
Input :
a sample dictionnary (a dict)
a sollicitations dictionnary (a dict)
Output :
Nothing, but the sample dictionnary gets updated value concerning the k0
"""
#-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
#Load data needed
L_contact_gw = dict_sample['L_contact_gw']
x_min = dict_sample['x_box_min']
x_max = dict_sample['x_box_max']
y_min = dict_sample['y_box_min']
y_max = dict_sample['y_box_max']
F_y_max = dict_sollicitations['Force_on_upper_wall']
#-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
#compute the forces applied on left and right walls (to compute k0)
F_x_min = 0
F_x_max = 0
for contact in L_contact_gw:
if contact.nature == 'gwx_min':
F_x_min = F_x_min + contact.Fwg_n
elif contact.nature == 'gwx_max':
F_x_max = F_x_max + contact.Fwg_n
#compute k0 = (sigma_2/sigma_1)
sigma_x_min = F_x_min / (y_max-y_min)
sigma_x_max = F_x_max / (y_max-y_min)
sigma_y_max = F_y_max / (x_max-x_min)
if sigma_y_max!= 0:
k0_xmin = abs(sigma_x_min/sigma_y_max)
k0_xmax = abs(sigma_x_min/sigma_y_max)
else :
k0_xmin = 0
k0_xmax = 0
#update element in dicts
dict_sample['k0_xmin'] = k0_xmin
dict_sample['k0_xmax'] = k0_xmax
#-------------------------------------------------------------------------------
def Write_e_dissolution_txt(dict_sample,dict_sollicitations):
"""
Write an .txt file for MOOSE. This file described an homogenous dissolution field.
Input :
a sample dictionnary (a dict)
a sollicitations dictionnary (a dict)
Output :
Nothing, but a .txt file is generated (a file)
"""
#-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
#Load data needed
x_L = dict_sample['x_L']
y_L = dict_sample['y_L']
e_dissolution = dict_sollicitations['Dissolution_Energy']
#-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
file_to_write = open('Data/e_dissolution.txt','w')
file_to_write.write('AXIS X\n')
line = ''
for x in x_L:
line = line + str(x)+ ' '
line = line + '\n'
file_to_write.write(line)
file_to_write.write('AXIS Y\n')
line = ''
for y in y_L:
line = line + str(y)+ ' '
line = line + '\n'
file_to_write.write(line)
file_to_write.write('DATA\n')
for l in range(len(y_L)):
for c in range(len(x_L)):
file_to_write.write(str(e_dissolution)+'\n')
file_to_write.close()
#-------------------------------------------------------------------------------
def Plot_chain_force(i_PF,i_DEM):
"""
Plot the chain force.
Input :
the iteration PFDEM (a int)
the iteration DEM (a int)
Output :
Nothing, but a .png file is generated (a file)
"""
normal_ref = 5*10**6 #can be changed
file_name = 'Debug/DEM_ite/PF_'+str(i_PF)+'/txt/ite_DEM_'+str(i_DEM)+'.txt'
file = open(file_name,'r')
lines = file.readlines()
file.close()
L_g = []
L_contact = []
L_contact_gw = []
Read_one_grain = False
Read_one_contact = False
Read_one_contact_wall = False
for line in lines :
if line == '<grain_c>\n':
Read_one_grain = False
L_g.append(Grain_pp(id,dissolved,center,coordinate_x,coordinate_y))
elif line == '<contact_c>\n':
Read_one_contact = False
L_contact.append(Contact_pp(L_id_g[0], L_id_g[1], L_g, normal_reaction+normal_damping))
elif line == '<contact_w_c>\n':
Read_one_contact_wall = False
L_contact_gw.append(Contact_gw_pp(id_g, L_g, type, limit, normal_reaction+normal_damping))
if Read_one_grain:
if line[:len('\tid : ')] == '\tid : ':
Read_data = False
for c in range(len('\tid : ')-1,len(line)):
if (line[c]!=' ') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or c==len(line)-1) and Read_data:
id = float(line[c_start:c])
Read_data = False
elif line[:len('\tDissolved : ')] == '\tDissolved : ':
Read_data = False
for c in range(len('\tDissolved : ')-1,len(line)):
if (line[c]!=' ') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or c==len(line)-1) and Read_data:
if line[c_start:c] == 'True':
dissolved = True
else :
dissolved = False
Read_data = False
elif line[:len('\tCenter : ')] == '\tCenter : ':
Read_data = False
center = []
for c in range(len('\tCenter : ')-1,len(line)):
if (line[c]!=' ' and line[c]!='[' and line[c]!=']' and line[c]!=',') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or line[c]==',' or line[c]==']') and Read_data:
data = float(line[c_start:c])
center.append(data)
Read_data = False
elif line[:len('\tCoordinate X of the border : ')] == '\tCoordinate X of the border : ':
Read_data = False
coordinate_x = []
for c in range(len('\tCoordinate X of the border : ')-1,len(line)):
if (line[c]!=' ' and line[c]!='[' and line[c]!=']' and line[c]!=',') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or line[c]==',' or line[c]==']') and Read_data:
data = float(line[c_start:c])
coordinate_x.append(data)
Read_data = False
elif line[:len('\tCoordinate Y of the border : ')] == '\tCoordinate Y of the border : ':
Read_data = False
coordinate_y = []
for c in range(len('\tCoordinate Y of the border : ')-1,len(line)):
if (line[c]!=' ' and line[c]!='[' and line[c]!=']' and line[c]!=',') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or line[c]==',' or line[c]==']') and Read_data:
data = float(line[c_start:c])
coordinate_y.append(data)
Read_data = False
if Read_one_contact:
if line[:len('\tGrains : ')] == '\tGrains : ':
Read_data = False
L_id_g = []
for c in range(len('\tGrains : ')-1,len(line)):
if (line[c]!=' ' and line[c]!='-') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or line[c]=='-' or c==len(line)-1) and Read_data:
data = float(line[c_start:c])
L_id_g.append(data)
Read_data = False
elif line[:len('\tNormal reaction : ')] == '\tNormal reaction : ':
Read_data = False
for c in range(len('\tNormal reaction : ')-1,len(line)):
if (line[c]!=' ') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or c==len(line)-1) and Read_data:
normal_reaction = float(line[c_start:c])
Read_data = False
elif line[:len('\tNormal damping : ')] == '\tNormal damping : ':
Read_data = False
for c in range(len('\tNormal damping : ')-1,len(line)):
if (line[c]!=' ') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or c==len(line)-1) and Read_data:
normal_damping = float(line[c_start:c])
Read_data = False
if Read_one_contact_wall:
if line[:len('\tType : ')] == '\tType : ':
Read_data = False
for c in range(len('\tType : ')-1,len(line)):
if (line[c]!=' ') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or c==len(line)-1) and Read_data:
type = line[c_start:c]
Read_data = False
elif line[:len('\tGrain : ')] == '\tGrain : ':
Read_data = False
for c in range(len('\tGrain : ')-1,len(line)):
if (line[c]!=' ') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or c==len(line)-1) and Read_data:
id_g = float(line[c_start:c])
Read_data = False
elif line[:len('\tLimit : ')] == '\tLimit : ':
Read_data = False
for c in range(len('\tLimit : ')-1,len(line)):
if (line[c]!=' ') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or c==len(line)-1) and Read_data:
limit = float(line[c_start:c])
Read_data = False
if type == 'gwx_min':
x_min = limit
elif type == 'gwx_max':
x_max = limit
elif type == 'gwy_min':
y_min = limit
elif type == 'gwy_max':
y_max = limit
elif line[:len('\tNormal reaction : ')] == '\tNormal reaction : ':
Read_data = False
for c in range(len('\tNormal reaction : ')-1,len(line)):
if (line[c]!=' ') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or c==len(line)-1) and Read_data:
normal_reaction = float(line[c_start:c])
Read_data = False
elif line[:len('\tNormal damping : ')] == '\tNormal damping : ':
Read_data = False
for c in range(len('\tNormal damping : ')-1,len(line)):
if (line[c]!=' ') and not Read_data:
c_start = c
Read_data = True
elif (line[c]==' ' or c==len(line)-1) and Read_data:
normal_damping = float(line[c_start:c])
Read_data = False
if line == '<grain_o>\n':
Read_one_grain = True
elif line == '<contact_o>\n':
Read_one_contact = True
elif line == '<contact_w_o>\n':
Read_one_contact_wall = True
plt.figure(1,figsize=(16,9))
plt.plot([x_min, x_max, x_max, x_min, x_min],[y_min, y_min, y_max, y_max, y_min],'k')
for grain in L_g:
if grain.dissolved:
plt.plot(grain.coordinate_x,grain.coordinate_y,color= 'k',linestyle='-.')
else :
plt.plot(grain.coordinate_x,grain.coordinate_y,color= 'k')
for contact in L_contact+L_contact_gw:
L_x, L_y, ratio_normal = contact.plot(normal_ref)
plt.plot(L_x,L_y,linewidth = ratio_normal,color = 'k')
plt.axis("equal")
plt.savefig('Debug/DEM_ite/Chain_force_'+str(i_PF)+'.png')
plt.close(1)
#-------------------------------------------------------------------------------
def make_mp4():
"""
The goal of this function is to create a movie with all configuration pictures.
From https://www.blog.pythonlibrary.org/2021/06/23/creating-an-animated-gif-with-python/
Input :
Nothing
Output :
Nothing, but a .mp4 file is generated (a file)
"""
#look for the largest iteration
template_name = 'Debug/DEM_ite/PF_ite_'
i_f = 0
plotpath = Path(template_name+str(i_f)+'.png')
while plotpath.exists():
i_f = i_f + 1
plotpath = Path(template_name+str(i_f)+'.png')
fileList = []
for i in range(0,i_f):
fileList.append(template_name+str(i)+'.png')
duration_movie = 10 #sec
writer = imageio.get_writer('Debug/PF_ite.mp4', fps=int(i_f/duration_movie))
for im in fileList:
writer.append_data(imageio.imread(im))
writer.close()
#-------------------------------------------------------------------------------
def save_DEM_tempo(dict_algorithm,dict_sample,dict_sollicitations,dict_tracker):
"""
Save trackers and configuration during DEM interations.
Input :
an algorithm dictionnary (a dict)
a sample dictionnary (a dict)
a sollicitations dictionnary (a dict)
a tracker dictionnary (a dict)
Output :
Nothing, but a save file is generated (a file)
"""
outfile = open('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/save_tempo','wb')
dict_save = {}
dict_save['E_cin_stop'] = dict_algorithm['Ecin_stop']
dict_save['n_window_stop'] = dict_algorithm['n_window_stop']
dict_save['dy_box_max_stop'] = dict_algorithm['dy_box_max_stop']
dict_save['dk0_stop'] = dict_algorithm['dk0_stop']
dict_save['L_g'] = dict_sample['L_g']
dict_save['L_contact'] = dict_sample['L_contact']
dict_save['L_ij_contact'] = dict_sample['L_ij_contact']
dict_save['L_contact_gw'] = dict_sample['L_contact_gw']
dict_save['L_ij_contact_gw'] = dict_sample['L_ij_contact_gw']
dict_save['F_on_ymax'] = dict_sollicitations['Force_on_upper_wall']
dict_save['E_cin'] = dict_tracker['Ecin']
dict_save['Force'] = dict_tracker['Force_applied']
dict_save['k0_xmin_tracker'] = dict_tracker['k0_xmin']
dict_save['k0_xmax_tracker'] = dict_tracker['k0_xmax']
dict_save['y_box_max'] = dict_tracker['y_box_max'][:-1]
pickle.dump(dict_save,outfile)
outfile.close()
#-------------------------------------------------------------------------------
def save_DEM_final(dict_algorithm,dict_sample,dict_sollicitations,dict_tracker):
"""
Save trackers and configuration at the end of DEM iteration.
Input :
an algorithm dictionnary (a dict)
a sample dictionnary (a dict)
a sollicitations dictionnary (a dict)
a tracker dictionnary (a dict)
Output :
Nothing, but a save file is generated (a file)
"""
os.remove('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/save_tempo')
outfile = open('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/save','wb')
dict_save = {}
dict_save['E_cin_stop'] = dict_algorithm['Ecin_stop']
dict_save['n_window_stop'] = dict_algorithm['n_window_stop']
dict_save['dy_box_max_stop'] = dict_algorithm['dy_box_max_stop']
dict_save['dk0_stop'] = dict_algorithm['dk0_stop']
dict_save['L_g'] = dict_sample['L_g']
dict_save['L_contact'] = dict_sample['L_contact']
dict_save['L_ij_contact'] = dict_sample['L_ij_contact']
dict_save['L_contact_gw'] = dict_sample['L_contact_gw']
dict_save['L_ij_contact_gw'] = dict_sample['L_ij_contact_gw']
dict_save['F_on_ymax'] = dict_sollicitations['Force_on_upper_wall']
dict_save['E_cin'] = dict_tracker['Ecin']
dict_save['Force'] = dict_tracker['Force_applied']
dict_save['k0_xmin_tracker'] = dict_tracker['k0_xmin']
dict_save['k0_xmax_tracker'] = dict_tracker['k0_xmax']
dict_save['y_box_max'] = dict_tracker['y_box_max'][:-1]
pickle.dump(dict_save,outfile)
outfile.close()
#-------------------------------------------------------------------------------
def save_dicts(dict_algorithm, dict_geometry, dict_material, dict_sample, dict_sollicitations, dict_tracker):
"""
Save dictionnaries at the end of PFDEM iteration.
Input :
an algorithm dictionnary (a dict)
a geometry dictionnary (a dict)
a material dictionnary (a dict)
a sample dictionnary (a dict)
a sollicitations dictionnary (a dict)
a tracker dictionnary (a dict)
Output :
Nothing, but a save file is generated (a file)
"""
outfile = open(dict_algorithm['name_folder']+'_save_dicts','wb')
dict_save = {}
dict_save['algorithm'] = dict_algorithm
dict_save['geometry'] = dict_geometry
dict_save['material'] = dict_material
dict_save['sample'] = dict_sample
dict_save['sollicitations'] = dict_sollicitations
dict_save['tracker'] = dict_tracker
pickle.dump(dict_save,outfile)
outfile.close()
#-------------------------------------------------------------------------------
def save_tempo(dict_algorithm,dict_tracker):
"""
Save trackers and configuration during PFDEM iteration.
Input :
an algorithm dictionnary (a dict)
a tracker dictionnary (a dict)
Output :
Nothing, but a save file is generated (a file)
"""
outfile = open('../'+dict_algorithm['main_folder_name']+'/'+dict_algorithm['name_folder']+'_save_tempo','wb')
dict_save = {}
dict_save['k0_xmin_L'] = dict_tracker['k0_xmin_L']
dict_save['k0_xmax_L'] = dict_tracker['k0_xmax_L']
dict_save['S_dissolved_L'] = dict_tracker['S_dissolved_L']
dict_save['S_dissolved_perc_L'] = dict_tracker['S_dissolved_perc_L']
dict_save['S_grains_L'] = dict_tracker['S_grains_L']
pickle.dump(dict_save,outfile)
outfile.close()
#-------------------------------------------------------------------------------
def save_final(dict_algorithm,dict_tracker):
"""
Save trackers and configuration at the end of simulation.
Input :
an algorithm dictionnary (a dict)
a tracker dictionnary (a dict)
Output :
Nothing, but a save file is generated (a file)
"""
os.remove('../'+dict_algorithm['main_folder_name']+'/'+dict_algorithm['name_folder']+'_save_tempo')
outfile = open('../'+dict_algorithm['main_folder_name']+'/'+dict_algorithm['name_folder']+'_save','wb')
dict_save = {}
dict_save['k0_xmin_L'] = dict_tracker['k0_xmin_L']
dict_save['k0_xmax_L'] = dict_tracker['k0_xmax_L']
dict_save['S_dissolved_L'] = dict_tracker['S_dissolved_L']
dict_save['S_dissolved_perc_L'] = dict_tracker['S_dissolved_perc_L']
dict_save['S_grains_L'] = dict_tracker['S_grains_L']
pickle.dump(dict_save,outfile)
outfile.close()
Functions
def Compute_k0(dict_sample, dict_sollicitations)
-
Compute the k0 = sigma_2 / sigma_1 ratio.
Input : a sample dictionnary (a dict) a sollicitations dictionnary (a dict) Output : Nothing, but the sample dictionnary gets updated value concerning the k0
Expand source code
def Compute_k0(dict_sample,dict_sollicitations): """ Compute the k0 = sigma_2 / sigma_1 ratio. Input : a sample dictionnary (a dict) a sollicitations dictionnary (a dict) Output : Nothing, but the sample dictionnary gets updated value concerning the k0 """ #-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. #Load data needed L_contact_gw = dict_sample['L_contact_gw'] x_min = dict_sample['x_box_min'] x_max = dict_sample['x_box_max'] y_min = dict_sample['y_box_min'] y_max = dict_sample['y_box_max'] F_y_max = dict_sollicitations['Force_on_upper_wall'] #-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. #compute the forces applied on left and right walls (to compute k0) F_x_min = 0 F_x_max = 0 for contact in L_contact_gw: if contact.nature == 'gwx_min': F_x_min = F_x_min + contact.Fwg_n elif contact.nature == 'gwx_max': F_x_max = F_x_max + contact.Fwg_n #compute k0 = (sigma_2/sigma_1) sigma_x_min = F_x_min / (y_max-y_min) sigma_x_max = F_x_max / (y_max-y_min) sigma_y_max = F_y_max / (x_max-x_min) if sigma_y_max!= 0: k0_xmin = abs(sigma_x_min/sigma_y_max) k0_xmax = abs(sigma_x_min/sigma_y_max) else : k0_xmin = 0 k0_xmax = 0 #update element in dicts dict_sample['k0_xmin'] = k0_xmin dict_sample['k0_xmax'] = k0_xmax
def Control_y_max_NR(dict_sample, dict_sollicitations)
-
Control the upper wall to apply force.
A Newton-Raphson method is applied to verify the confinement. Input : a sample dictionnary (a dict) a sollicitations dictionnary (a dict) Output : Nothing, but the sample dictionnary is updated, concerning the upper wall position and force applied (two floats)
Expand source code
def Control_y_max_NR(dict_sample,dict_sollicitations): """ Control the upper wall to apply force. A Newton-Raphson method is applied to verify the confinement. Input : a sample dictionnary (a dict) a sollicitations dictionnary (a dict) Output : Nothing, but the sample dictionnary is updated, concerning the upper wall position and force applied (two floats) """ #-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. #load data needed Force_target = dict_sollicitations['Vertical_Confinement_Force'] #-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. F = 0 overlap_L = [] k_L = [] for contact in dict_sample['L_contact_gw']: if contact.nature == 'gwy_max': F = F + contact.Fwg_n overlap_L.append(contact.overlap) k_L.append(contact.k) #compute force applied, save contact overlap and spring if overlap_L != []: i_NR = 0 dy = 0 ite_criteria = True if -0.01*Force_target<error_on_ymax_f(dy,overlap_L,k_L,Force_target) and error_on_ymax_f(dy,overlap_L,k_L,Force_target)<0.01*Force_target: ite_criteria = False while ite_criteria : i_NR = i_NR + 1 dy = dy - error_on_ymax_f(dy,overlap_L,k_L,Force_target)/error_on_ymax_df(dy,overlap_L,k_L) if i_NR > 100: ite_criteria = False if -0.01*Force_target<error_on_ymax_f(dy,overlap_L,k_L,Force_target) and error_on_ymax_f(dy,overlap_L,k_L,Force_target)<0.01*Force_target: ite_criteria = False dict_sample['y_box_max'] = dict_sample['y_box_max'] + dy else : #if there is no contact with the upper wall, the wall is reset dict_sample['y_box_max'] = Reset_y_max(dict_sample['L_g'],Force_target) for contact in dict_sample['L_contact_gw']: if contact.nature == 'gwy_max': #reactualisation contact.limit = dict_sample['y_box_max'] #Update dict dict_sollicitations['Force_on_upper_wall'] = F
def Debug_DEM_f(dict_algorithm, dict_sample)
-
Plot the configuration of the grains during a DEM step.
Input: an algorithm dictionnary (a dict) a sample dictionnary (a dict) Output : Nothing, but a .png file is generated (a file)
Expand source code
def Debug_DEM_f(dict_algorithm, dict_sample): """ Plot the configuration of the grains during a DEM step. Input: an algorithm dictionnary (a dict) a sample dictionnary (a dict) Output : Nothing, but a .png file is generated (a file) """ #-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.- #load data needed x_min = dict_sample['x_box_min'] x_max = dict_sample['x_box_max'] y_min = dict_sample['y_box_min'] y_max = dict_sample['y_box_max'] #-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.- fig = plt.figure(1,figsize=(16,9.12)) for grain in dict_sample['L_g']: plt.plot(grain.l_border_x,grain.l_border_y,'k') for contact in dict_sample['L_contact'] : alpha = (contact.g1.r_mean*0.1 + contact.g2.r_mean*0.1)/2 M = (contact.g1.center + contact.g2.center)/2 plt.plot([M[0], M[0]+contact.pc_normal[0]*alpha], [M[1], M[1]+contact.pc_normal[1]*alpha],'k') plt.plot([M[0], M[0]+contact.pc_tangential[0]*alpha], [M[1], M[1]+contact.pc_tangential[1]*alpha],'k') for contact in dict_sample['L_contact_gw'] : alpha = contact.g.r_mean*0.1 if contact.nature == 'gwy_min' or contact.nature == 'gwy_max': M = np.array([contact.g.center[0],contact.limit]) elif contact.nature == 'gwx_min' or contact.nature == 'gwx_max': M = np.array([contact.limit,contact.g.center[1]]) plt.plot([M[0], M[0]+contact.nwg[0]*alpha], [M[1], M[1]+contact.nwg[1]*alpha],'k') plt.plot([M[0], M[0]+contact.twg[0]*alpha], [M[1], M[1]+contact.twg[1]*alpha],'k') plt.plot([x_min,x_max,x_max,x_min,x_min],[y_min,y_min,y_max,y_max,y_min],'k') plt.axis("equal") fig.savefig('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/png/Config_'+str(dict_algorithm['i_DEM'])+'.png') plt.close(1)
def Debug_Trackers(dict_tracker)
-
Plot the trakers used during PFDEM simulation
Input : a tracker dictionnary (a dict) Output : Nothing, but severals .png files are generated (files)
Expand source code
def Debug_Trackers(dict_tracker): """ Plot the trakers used during PFDEM simulation Input : a tracker dictionnary (a dict) Output : Nothing, but severals .png files are generated (files) """ #Trackers fig = plt.figure(1,figsize=(16,9.12)) plt.plot(dict_tracker['t_L'],dict_tracker['S_grains_dissolvable_L']) plt.title('Evolution of the dissolvable grains surface') fig.savefig('Debug/Evolution_Dissolvable_Surface.png') plt.close(1) fig = plt.figure(1,figsize=(16,9.12)) plt.plot(dict_tracker['S_dissolved_perc_dissolvable_L'][:-1],dict_tracker['k0_xmin_L'],label='k0 with xmin') plt.plot(dict_tracker['S_dissolved_perc_dissolvable_L'][:-1],dict_tracker['k0_xmax_L'],label='k0 with xmax') plt.title('Evolution of the k0') plt.xlabel('Percentage of dissolvable grains surface dissolved (%)') fig.savefig('Debug/Evolution_k0_with_percentage_dissolved.png') plt.close(1)
def Debug_Trackers_DEM(dict_algorithm, dict_sollicitations, dict_tracker)
-
Plot the trakers used during DEM simulation.
Input : an algorithm dictionnary (a dict) a sollicitations dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but .png file is generated (a file)
Expand source code
def Debug_Trackers_DEM(dict_algorithm,dict_sollicitations,dict_tracker): """ Plot the trakers used during DEM simulation. Input : an algorithm dictionnary (a dict) a sollicitations dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but .png file is generated (a file) """ fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2,2, figsize=(16,9),num=1) ax1.set_title('Mean kinetic energy (e-12 J)') ax1.plot(dict_tracker['Ecin']) ax1.plot([0, len(dict_tracker['Ecin'])-1],[dict_algorithm['Ecin_stop'], dict_algorithm['Ecin_stop']],'r') ax2.set_title('Mean force applied (µN)') ax2.plot(dict_tracker['Force_applied']) ax3.set_title('k0s (-)') ax3.plot(dict_tracker['k0_xmin'],label='xmin') ax3.plot(dict_tracker['k0_xmax'],label='xmax') ax3.legend() ax4.set_title('About the upper plate') ax4.plot(dict_tracker['y_box_max'], color = 'blue') ax4.set_ylabel('Coordinate y (µm)', color = 'blue') ax4.tick_params(axis ='y', labelcolor = 'blue') ax4a = ax4.twinx() ax4a.plot(range(50,len(dict_tracker['Force_on_upper_wall'])),dict_tracker['Force_on_upper_wall'][50:], color = 'orange') ax4a.plot([50, len(dict_tracker['Force_on_upper_wall'])-1],[dict_sollicitations['Vertical_Confinement_Force'], dict_sollicitations['Vertical_Confinement_Force']], color = 'red') ax4a.set_ylabel('Force applied (µN)', color = 'orange') ax4a.tick_params(axis ='y', labelcolor = 'orange') fig.savefig('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/Trackers.png') plt.close(1)
def Debug_configuration(dict_algorithm, dict_sample)
-
Plot the configuration of the grains before / after the DEM step.
Input: an algorithm dictionnary (a dict) a sample dictionnary (a dict) Output : Nothing, but a .png file is generated (a file)
Expand source code
def Debug_configuration(dict_algorithm,dict_sample): """ Plot the configuration of the grains before / after the DEM step. Input: an algorithm dictionnary (a dict) a sample dictionnary (a dict) Output : Nothing, but a .png file is generated (a file) """ #-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.- #load data needed x_min = dict_sample['x_box_min'] x_max = dict_sample['x_box_max'] y_min = dict_sample['y_box_min'] y_max = dict_sample['y_box_max'] #-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.- #look for the name of the new plot template_name = 'Debug/DEM_ite/PF_ite_' j = 0 plotpath = Path(template_name+str(j)+'.png') while plotpath.exists(): j = j + 1 plotpath = Path(template_name+str(j)+'.png') name = template_name+str(j)+'.png' #create the plot fig = plt.figure(1,figsize=(16,9.12)) for grain in dict_sample['L_g']: if grain.dissolved: plt.plot(grain.l_border_x,grain.l_border_y,'k-.') else: plt.plot(grain.l_border_x,grain.l_border_y,'k') plt.plot([x_min,x_max,x_max,x_min,x_min],[y_min,y_min,y_max,y_max,y_min],'k') plt.axis("equal") fig.savefig(name) plt.close(1)
def Plot_chain_force(i_PF, i_DEM)
-
Plot the chain force.
Input : the iteration PFDEM (a int) the iteration DEM (a int) Output : Nothing, but a .png file is generated (a file)
Expand source code
def Plot_chain_force(i_PF,i_DEM): """ Plot the chain force. Input : the iteration PFDEM (a int) the iteration DEM (a int) Output : Nothing, but a .png file is generated (a file) """ normal_ref = 5*10**6 #can be changed file_name = 'Debug/DEM_ite/PF_'+str(i_PF)+'/txt/ite_DEM_'+str(i_DEM)+'.txt' file = open(file_name,'r') lines = file.readlines() file.close() L_g = [] L_contact = [] L_contact_gw = [] Read_one_grain = False Read_one_contact = False Read_one_contact_wall = False for line in lines : if line == '<grain_c>\n': Read_one_grain = False L_g.append(Grain_pp(id,dissolved,center,coordinate_x,coordinate_y)) elif line == '<contact_c>\n': Read_one_contact = False L_contact.append(Contact_pp(L_id_g[0], L_id_g[1], L_g, normal_reaction+normal_damping)) elif line == '<contact_w_c>\n': Read_one_contact_wall = False L_contact_gw.append(Contact_gw_pp(id_g, L_g, type, limit, normal_reaction+normal_damping)) if Read_one_grain: if line[:len('\tid : ')] == '\tid : ': Read_data = False for c in range(len('\tid : ')-1,len(line)): if (line[c]!=' ') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or c==len(line)-1) and Read_data: id = float(line[c_start:c]) Read_data = False elif line[:len('\tDissolved : ')] == '\tDissolved : ': Read_data = False for c in range(len('\tDissolved : ')-1,len(line)): if (line[c]!=' ') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or c==len(line)-1) and Read_data: if line[c_start:c] == 'True': dissolved = True else : dissolved = False Read_data = False elif line[:len('\tCenter : ')] == '\tCenter : ': Read_data = False center = [] for c in range(len('\tCenter : ')-1,len(line)): if (line[c]!=' ' and line[c]!='[' and line[c]!=']' and line[c]!=',') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or line[c]==',' or line[c]==']') and Read_data: data = float(line[c_start:c]) center.append(data) Read_data = False elif line[:len('\tCoordinate X of the border : ')] == '\tCoordinate X of the border : ': Read_data = False coordinate_x = [] for c in range(len('\tCoordinate X of the border : ')-1,len(line)): if (line[c]!=' ' and line[c]!='[' and line[c]!=']' and line[c]!=',') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or line[c]==',' or line[c]==']') and Read_data: data = float(line[c_start:c]) coordinate_x.append(data) Read_data = False elif line[:len('\tCoordinate Y of the border : ')] == '\tCoordinate Y of the border : ': Read_data = False coordinate_y = [] for c in range(len('\tCoordinate Y of the border : ')-1,len(line)): if (line[c]!=' ' and line[c]!='[' and line[c]!=']' and line[c]!=',') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or line[c]==',' or line[c]==']') and Read_data: data = float(line[c_start:c]) coordinate_y.append(data) Read_data = False if Read_one_contact: if line[:len('\tGrains : ')] == '\tGrains : ': Read_data = False L_id_g = [] for c in range(len('\tGrains : ')-1,len(line)): if (line[c]!=' ' and line[c]!='-') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or line[c]=='-' or c==len(line)-1) and Read_data: data = float(line[c_start:c]) L_id_g.append(data) Read_data = False elif line[:len('\tNormal reaction : ')] == '\tNormal reaction : ': Read_data = False for c in range(len('\tNormal reaction : ')-1,len(line)): if (line[c]!=' ') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or c==len(line)-1) and Read_data: normal_reaction = float(line[c_start:c]) Read_data = False elif line[:len('\tNormal damping : ')] == '\tNormal damping : ': Read_data = False for c in range(len('\tNormal damping : ')-1,len(line)): if (line[c]!=' ') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or c==len(line)-1) and Read_data: normal_damping = float(line[c_start:c]) Read_data = False if Read_one_contact_wall: if line[:len('\tType : ')] == '\tType : ': Read_data = False for c in range(len('\tType : ')-1,len(line)): if (line[c]!=' ') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or c==len(line)-1) and Read_data: type = line[c_start:c] Read_data = False elif line[:len('\tGrain : ')] == '\tGrain : ': Read_data = False for c in range(len('\tGrain : ')-1,len(line)): if (line[c]!=' ') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or c==len(line)-1) and Read_data: id_g = float(line[c_start:c]) Read_data = False elif line[:len('\tLimit : ')] == '\tLimit : ': Read_data = False for c in range(len('\tLimit : ')-1,len(line)): if (line[c]!=' ') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or c==len(line)-1) and Read_data: limit = float(line[c_start:c]) Read_data = False if type == 'gwx_min': x_min = limit elif type == 'gwx_max': x_max = limit elif type == 'gwy_min': y_min = limit elif type == 'gwy_max': y_max = limit elif line[:len('\tNormal reaction : ')] == '\tNormal reaction : ': Read_data = False for c in range(len('\tNormal reaction : ')-1,len(line)): if (line[c]!=' ') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or c==len(line)-1) and Read_data: normal_reaction = float(line[c_start:c]) Read_data = False elif line[:len('\tNormal damping : ')] == '\tNormal damping : ': Read_data = False for c in range(len('\tNormal damping : ')-1,len(line)): if (line[c]!=' ') and not Read_data: c_start = c Read_data = True elif (line[c]==' ' or c==len(line)-1) and Read_data: normal_damping = float(line[c_start:c]) Read_data = False if line == '<grain_o>\n': Read_one_grain = True elif line == '<contact_o>\n': Read_one_contact = True elif line == '<contact_w_o>\n': Read_one_contact_wall = True plt.figure(1,figsize=(16,9)) plt.plot([x_min, x_max, x_max, x_min, x_min],[y_min, y_min, y_max, y_max, y_min],'k') for grain in L_g: if grain.dissolved: plt.plot(grain.coordinate_x,grain.coordinate_y,color= 'k',linestyle='-.') else : plt.plot(grain.coordinate_x,grain.coordinate_y,color= 'k') for contact in L_contact+L_contact_gw: L_x, L_y, ratio_normal = contact.plot(normal_ref) plt.plot(L_x,L_y,linewidth = ratio_normal,color = 'k') plt.axis("equal") plt.savefig('Debug/DEM_ite/Chain_force_'+str(i_PF)+'.png') plt.close(1)
def Reset_y_max(L_g, Force)
-
The upper wall is located as a single contact verify the target value.
Input : the list of temporary grains (a list) the confinement force (a float) Output : the upper wall position (a float)
Expand source code
def Reset_y_max(L_g,Force): """ The upper wall is located as a single contact verify the target value. Input : the list of temporary grains (a list) the confinement force (a float) Output : the upper wall position (a float) """ print('Reset of the y_max on the upper grain') y_max = None id_grain_max = None for id_grain in range(len(L_g)): grain = L_g[id_grain] y_max_grain = max(grain.l_border_y) if y_max != None and y_max_grain > y_max: y_max = y_max_grain id_grain_max = id_grain elif y_max == None: y_max = y_max_grain id_grain_max = id_grain k = 5*4/3*L_g[id_grain_max].y/(1-L_g[id_grain_max].nu*L_g[id_grain_max].nu)*math.sqrt(L_g[id_grain_max].r_mean) y_max = y_max - (Force/k)**(2/3) return y_max
def Sort_Files(name_template, dict_algorithm)
-
Sort files generated by MOOSE to different directories.
Input : a template of the simulation files (a string) an algorithm dictionnary (a dict) Output : Nothing, but files are sorted
Expand source code
def Sort_Files(name_template,dict_algorithm): """ Sort files generated by MOOSE to different directories. Input : a template of the simulation files (a string) an algorithm dictionnary (a dict) Output : Nothing, but files are sorted """ #master simulation os.rename(name_template+'_out.e','Output/'+name_template+'_out.e') os.rename(name_template+'.i','Input/'+name_template+'.i') if Path('Output/'+name_template).exists(): shutil.rmtree('Output/'+name_template) os.mkdir('Output/'+name_template) j = 0 j_str = index_to_str(j) folderpath = Path(name_template+'_other_'+j_str+'.pvtu') while folderpath.exists(): for i_proc in range(dict_algorithm['np_proc']): os.rename(name_template+'_other_'+j_str+'_'+str(i_proc)+'.vtu','Output/'+name_template+'/'+name_template+'_other_'+j_str+'_'+str(i_proc)+'.vtu') os.rename(name_template+'_other_'+j_str+'.pvtu','Output/'+name_template+'/'+name_template+'_other_'+j_str+'.pvtu') j = j + 1 j_str = index_to_str(j) folderpath = Path(name_template+'_other_'+j_str+'.pvtu') return index_to_str(j-1)
def Stop_Debug(simulation_report)
-
Stop simulation for debugging.
Input : the simulation report (a report) Output : Nothing, but simulations stops
Expand source code
def Stop_Debug(simulation_report): """ Stop simulation for debugging. Input : the simulation report (a report) Output : Nothing, but simulations stops """ simulation_report.write('Stop because after it is not corrected !\n') simulation_report.end(datetime.now()) raise ValueError('Stop because after it is not corrected !')
def Write_e_dissolution_txt(dict_sample, dict_sollicitations)
-
Write an .txt file for MOOSE. This file described an homogenous dissolution field.
Input : a sample 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_txt(dict_sample,dict_sollicitations): """ Write an .txt file for MOOSE. This file described an homogenous dissolution field. Input : a sample dictionnary (a dict) a sollicitations dictionnary (a dict) Output : Nothing, but a .txt file is generated (a file) """ #-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. #Load data needed x_L = dict_sample['x_L'] y_L = dict_sample['y_L'] e_dissolution = dict_sollicitations['Dissolution_Energy'] #-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. file_to_write = open('Data/e_dissolution.txt','w') file_to_write.write('AXIS X\n') line = '' for x in x_L: line = line + str(x)+ ' ' line = line + '\n' file_to_write.write(line) file_to_write.write('AXIS Y\n') line = '' for y in y_L: line = line + str(y)+ ' ' line = line + '\n' file_to_write.write(line) file_to_write.write('DATA\n') for l in range(len(y_L)): for c in range(len(x_L)): file_to_write.write(str(e_dissolution)+'\n') file_to_write.close()
def error_on_ymax_df(dy, overlap_L, k_L)
-
Compute the derivative function df to control the upper wall (error_on_ymax_f()).
Input : an increment of the upper wall position (a float) a list of overlap for contact between grain and upper wall (a list) a list of spring for contact between grain and upper wall (a list) Output : the derivative of error_on_ymax_f() (a float)
Expand source code
def error_on_ymax_df(dy,overlap_L,k_L) : """ Compute the derivative function df to control the upper wall (error_on_ymax_f()). Input : an increment of the upper wall position (a float) a list of overlap for contact between grain and upper wall (a list) a list of spring for contact between grain and upper wall (a list) Output : the derivative of error_on_ymax_f() (a float) """ df = 0 for i in range(len(overlap_L)): df = df + 3/2*k_L[i]*(max(overlap_L[i]-dy,0))**(1/2) return df
def error_on_ymax_f(dy, overlap_L, k_L, Force_target)
-
Compute the function f to control the upper wall. It is the difference between the force applied and the target value.
Input : an increment of the upper wall position (a float) a list of overlap for contact between grain and upper wall (a list) a list of spring for contact between grain and upper wall (a list) a confinement force (a float) Output : the difference between the force applied and the confinement (a float)
Expand source code
def error_on_ymax_f(dy,overlap_L,k_L,Force_target) : """ Compute the function f to control the upper wall. It is the difference between the force applied and the target value. Input : an increment of the upper wall position (a float) a list of overlap for contact between grain and upper wall (a list) a list of spring for contact between grain and upper wall (a list) a confinement force (a float) Output : the difference between the force applied and the confinement (a float) """ f = Force_target for i in range(len(overlap_L)): f = f - k_L[i]*(max(overlap_L[i]-dy,0))**(3/2) return f
def index_to_str(j)
-
Convert an integer to a float with 3 components
Input : an integer (a int) Output : a string (a string)
Expand source code
def index_to_str(j): """ Convert an integer to a float with 3 components Input : an integer (a int) Output : a string (a string) """ if j < 10: j_str = '00'+str(j) elif 10 <= j and j < 100: j_str = '0'+str(j) else : j_str = str(j) return j_str
def make_mp4()
-
The goal of this function is to create a movie with all configuration pictures.
From https://www.blog.pythonlibrary.org/2021/06/23/creating-an-animated-gif-with-python/
Input : Nothing Output : Nothing, but a .mp4 file is generated (a file)
Expand source code
def make_mp4(): """ The goal of this function is to create a movie with all configuration pictures. From https://www.blog.pythonlibrary.org/2021/06/23/creating-an-animated-gif-with-python/ Input : Nothing Output : Nothing, but a .mp4 file is generated (a file) """ #look for the largest iteration template_name = 'Debug/DEM_ite/PF_ite_' i_f = 0 plotpath = Path(template_name+str(i_f)+'.png') while plotpath.exists(): i_f = i_f + 1 plotpath = Path(template_name+str(i_f)+'.png') fileList = [] for i in range(0,i_f): fileList.append(template_name+str(i)+'.png') duration_movie = 10 #sec writer = imageio.get_writer('Debug/PF_ite.mp4', fps=int(i_f/duration_movie)) for im in fileList: writer.append_data(imageio.imread(im)) writer.close()
def save_DEM_final(dict_algorithm, dict_sample, dict_sollicitations, dict_tracker)
-
Save trackers and configuration at the end of DEM iteration.
Input : an algorithm dictionnary (a dict) a sample dictionnary (a dict) a sollicitations dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but a save file is generated (a file)
Expand source code
def save_DEM_final(dict_algorithm,dict_sample,dict_sollicitations,dict_tracker): """ Save trackers and configuration at the end of DEM iteration. Input : an algorithm dictionnary (a dict) a sample dictionnary (a dict) a sollicitations dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but a save file is generated (a file) """ os.remove('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/save_tempo') outfile = open('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/save','wb') dict_save = {} dict_save['E_cin_stop'] = dict_algorithm['Ecin_stop'] dict_save['n_window_stop'] = dict_algorithm['n_window_stop'] dict_save['dy_box_max_stop'] = dict_algorithm['dy_box_max_stop'] dict_save['dk0_stop'] = dict_algorithm['dk0_stop'] dict_save['L_g'] = dict_sample['L_g'] dict_save['L_contact'] = dict_sample['L_contact'] dict_save['L_ij_contact'] = dict_sample['L_ij_contact'] dict_save['L_contact_gw'] = dict_sample['L_contact_gw'] dict_save['L_ij_contact_gw'] = dict_sample['L_ij_contact_gw'] dict_save['F_on_ymax'] = dict_sollicitations['Force_on_upper_wall'] dict_save['E_cin'] = dict_tracker['Ecin'] dict_save['Force'] = dict_tracker['Force_applied'] dict_save['k0_xmin_tracker'] = dict_tracker['k0_xmin'] dict_save['k0_xmax_tracker'] = dict_tracker['k0_xmax'] dict_save['y_box_max'] = dict_tracker['y_box_max'][:-1] pickle.dump(dict_save,outfile) outfile.close()
def save_DEM_tempo(dict_algorithm, dict_sample, dict_sollicitations, dict_tracker)
-
Save trackers and configuration during DEM interations.
Input : an algorithm dictionnary (a dict) a sample dictionnary (a dict) a sollicitations dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but a save file is generated (a file)
Expand source code
def save_DEM_tempo(dict_algorithm,dict_sample,dict_sollicitations,dict_tracker): """ Save trackers and configuration during DEM interations. Input : an algorithm dictionnary (a dict) a sample dictionnary (a dict) a sollicitations dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but a save file is generated (a file) """ outfile = open('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/save_tempo','wb') dict_save = {} dict_save['E_cin_stop'] = dict_algorithm['Ecin_stop'] dict_save['n_window_stop'] = dict_algorithm['n_window_stop'] dict_save['dy_box_max_stop'] = dict_algorithm['dy_box_max_stop'] dict_save['dk0_stop'] = dict_algorithm['dk0_stop'] dict_save['L_g'] = dict_sample['L_g'] dict_save['L_contact'] = dict_sample['L_contact'] dict_save['L_ij_contact'] = dict_sample['L_ij_contact'] dict_save['L_contact_gw'] = dict_sample['L_contact_gw'] dict_save['L_ij_contact_gw'] = dict_sample['L_ij_contact_gw'] dict_save['F_on_ymax'] = dict_sollicitations['Force_on_upper_wall'] dict_save['E_cin'] = dict_tracker['Ecin'] dict_save['Force'] = dict_tracker['Force_applied'] dict_save['k0_xmin_tracker'] = dict_tracker['k0_xmin'] dict_save['k0_xmax_tracker'] = dict_tracker['k0_xmax'] dict_save['y_box_max'] = dict_tracker['y_box_max'][:-1] pickle.dump(dict_save,outfile) outfile.close()
def save_dicts(dict_algorithm, dict_geometry, dict_material, dict_sample, dict_sollicitations, dict_tracker)
-
Save dictionnaries at the end of PFDEM iteration.
Input : an algorithm dictionnary (a dict) a geometry dictionnary (a dict) a material dictionnary (a dict) a sample dictionnary (a dict) a sollicitations dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but a save file is generated (a file)
Expand source code
def save_dicts(dict_algorithm, dict_geometry, dict_material, dict_sample, dict_sollicitations, dict_tracker): """ Save dictionnaries at the end of PFDEM iteration. Input : an algorithm dictionnary (a dict) a geometry dictionnary (a dict) a material dictionnary (a dict) a sample dictionnary (a dict) a sollicitations dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but a save file is generated (a file) """ outfile = open(dict_algorithm['name_folder']+'_save_dicts','wb') dict_save = {} dict_save['algorithm'] = dict_algorithm dict_save['geometry'] = dict_geometry dict_save['material'] = dict_material dict_save['sample'] = dict_sample dict_save['sollicitations'] = dict_sollicitations dict_save['tracker'] = dict_tracker pickle.dump(dict_save,outfile) outfile.close()
def save_final(dict_algorithm, dict_tracker)
-
Save trackers and configuration at the end of simulation.
Input : an algorithm dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but a save file is generated (a file)
Expand source code
def save_final(dict_algorithm,dict_tracker): """ Save trackers and configuration at the end of simulation. Input : an algorithm dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but a save file is generated (a file) """ os.remove('../'+dict_algorithm['main_folder_name']+'/'+dict_algorithm['name_folder']+'_save_tempo') outfile = open('../'+dict_algorithm['main_folder_name']+'/'+dict_algorithm['name_folder']+'_save','wb') dict_save = {} dict_save['k0_xmin_L'] = dict_tracker['k0_xmin_L'] dict_save['k0_xmax_L'] = dict_tracker['k0_xmax_L'] dict_save['S_dissolved_L'] = dict_tracker['S_dissolved_L'] dict_save['S_dissolved_perc_L'] = dict_tracker['S_dissolved_perc_L'] dict_save['S_grains_L'] = dict_tracker['S_grains_L'] pickle.dump(dict_save,outfile) outfile.close()
def save_tempo(dict_algorithm, dict_tracker)
-
Save trackers and configuration during PFDEM iteration.
Input : an algorithm dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but a save file is generated (a file)
Expand source code
def save_tempo(dict_algorithm,dict_tracker): """ Save trackers and configuration during PFDEM iteration. Input : an algorithm dictionnary (a dict) a tracker dictionnary (a dict) Output : Nothing, but a save file is generated (a file) """ outfile = open('../'+dict_algorithm['main_folder_name']+'/'+dict_algorithm['name_folder']+'_save_tempo','wb') dict_save = {} dict_save['k0_xmin_L'] = dict_tracker['k0_xmin_L'] dict_save['k0_xmax_L'] = dict_tracker['k0_xmax_L'] dict_save['S_dissolved_L'] = dict_tracker['S_dissolved_L'] dict_save['S_dissolved_perc_L'] = dict_tracker['S_dissolved_perc_L'] dict_save['S_grains_L'] = dict_tracker['S_grains_L'] pickle.dump(dict_save,outfile) outfile.close()
Classes
class Contact_gw_pp (Id_g, L_g, Nature, Limit, Normal)
-
Defining a contact grain-wall for the postprocess.
Input : itself (a contact_gw_pp) a id of the grain (a float) the list of the post process grain (a list) the nature of the wall (a string) the value of normal reaction of the contact (a float)
Expand source code
class Contact_gw_pp: def __init__(self, Id_g, L_g, Nature, Limit, Normal): """ Defining a contact grain-wall for the postprocess. Input : itself (a contact_gw_pp) a id of the grain (a float) the list of the post process grain (a list) the nature of the wall (a string) the value of normal reaction of the contact (a float) """ for g in L_g: if g.id == Id_g: self.g = g self.nature = Nature self.limit = Limit self.normal = Normal def plot(self, normal_ref): """ Prepare the chain force plot. Input : itself (a contact_gw_pp) a reference value (a float) Output : Nothing, but the post process contact gets the ratio of the normal force with the reference value as a new attribut (a float) """ if self.nature == 'gwx_min' or self.nature == 'gwx_max': virtual_center = [self.limit, self.g.center[1]] elif self.nature == 'gwy_min' or self.nature == 'gwy_max': virtual_center = [ self.g.center[0], self.limit] L_x = [self.g.center[0], virtual_center[0]] L_y = [self.g.center[1], virtual_center[1]] ratio_normal = self.normal/normal_ref return L_x, L_y, ratio_normal
Methods
def plot(self, normal_ref)
-
Prepare the chain force plot.
Input : itself (a contact_gw_pp) a reference value (a float) Output : Nothing, but the post process contact gets the ratio of the normal force with the reference value as a new attribut (a float)
Expand source code
def plot(self, normal_ref): """ Prepare the chain force plot. Input : itself (a contact_gw_pp) a reference value (a float) Output : Nothing, but the post process contact gets the ratio of the normal force with the reference value as a new attribut (a float) """ if self.nature == 'gwx_min' or self.nature == 'gwx_max': virtual_center = [self.limit, self.g.center[1]] elif self.nature == 'gwy_min' or self.nature == 'gwy_max': virtual_center = [ self.g.center[0], self.limit] L_x = [self.g.center[0], virtual_center[0]] L_y = [self.g.center[1], virtual_center[1]] ratio_normal = self.normal/normal_ref return L_x, L_y, ratio_normal
class Contact_pp (Id_g1, Id_g2, L_g, Normal)
-
Defining a contact grain - grain for the postprocess.
Input : itself (a contact_pp) the ids of the grains (two int) a list of post process grains (a list) the value of normal reaction of the contact (a float) Output : Nothing, but a post process contact grain - grain is generated
Expand source code
class Contact_pp: def __init__(self, Id_g1, Id_g2, L_g, Normal): """ Defining a contact grain - grain for the postprocess. Input : itself (a contact_pp) the ids of the grains (two int) a list of post process grains (a list) the value of normal reaction of the contact (a float) Output : Nothing, but a post process contact grain - grain is generated """ for g in L_g: if g.id == Id_g1: self.g1 = g elif g.id == Id_g2: self.g2 = g self.normal = Normal def plot(self, normal_ref): """ Prepare the chain force plot. Input : itself (a contact_pp) a reference value (a float) Output : Nothing, but the post process contact gets the ratio of the normal force with the reference value as a new attribut (a float) """ L_x = [self.g1.center[0], self.g2.center[0]] L_y = [self.g1.center[1], self.g2.center[1]] ratio_normal = self.normal/normal_ref return L_x, L_y, ratio_normal
Methods
def plot(self, normal_ref)
-
Prepare the chain force plot.
Input : itself (a contact_pp) a reference value (a float) Output : Nothing, but the post process contact gets the ratio of the normal force with the reference value as a new attribut (a float)
Expand source code
def plot(self, normal_ref): """ Prepare the chain force plot. Input : itself (a contact_pp) a reference value (a float) Output : Nothing, but the post process contact gets the ratio of the normal force with the reference value as a new attribut (a float) """ L_x = [self.g1.center[0], self.g2.center[0]] L_y = [self.g1.center[1], self.g2.center[1]] ratio_normal = self.normal/normal_ref return L_x, L_y, ratio_normal
class Grain_pp (Id, Dissolved, Center, Coordinate_x, Coordinate_y)
-
Defining a grain for the postprocess
Input : itself (a grain_pp) an id (a int) a Boolean to know if the grain is dissolvable (a Boolean) a center (a 1 x 2 numpy array) two lists of vertices coordinates x and y (two lists) Output : Nothing, but a post process grain is generated
Expand source code
class Grain_pp: def __init__(self, Id, Dissolved, Center, Coordinate_x, Coordinate_y): """ Defining a grain for the postprocess Input : itself (a grain_pp) an id (a int) a Boolean to know if the grain is dissolvable (a Boolean) a center (a 1 x 2 numpy array) two lists of vertices coordinates x and y (two lists) Output : Nothing, but a post process grain is generated """ self.id = Id self.dissolved = Dissolved self.center = Center self.coordinate_x = Coordinate_x self.coordinate_y = Coordinate_y