Module User

@author: Alexandre Sac–Morane alexandre.sac-morane@uclouvain.be

This is the file where the user can change the different parameters for the simulation.

Expand source code
# -*- coding: utf-8 -*-
"""
@author: Alexandre Sac--Morane
alexandre.sac-morane@uclouvain.be

This is the file where the user can change the different parameters for the simulation.
"""

#-------------------------------------------------------------------------------
#Librairy
#-------------------------------------------------------------------------------

import math
import numpy as np
from pathlib import Path

#Own functions and classes
import Grain

#-------------------------------------------------------------------------------
#User
#-------------------------------------------------------------------------------

def All_parameters():
    '''
    This function is called in main.py to have all the parameters needed in the simulation.

        Input :
            Nothing
        Output :
            an algorithm dictionnary (a dictionnary)
            a material dictionnary (a dictionnary)
            a sample dictionnary (a dictionnary)
            a sollicitation dictionnary (a dictionnary)
    '''
    #---------------------------------------------------------------------------
    #Sample parameters

    #spatial discretisation
    x_min = -230
    x_max = 230
    nx = 180
    x_L = np.linspace(x_min,x_max,nx)
    y_min = -130
    y_max = 130
    ny = 100
    y_L = np.linspace(y_min,y_max,ny)

    #approximatively the number of vertices for one grain during DEM simulation
    grain_discretisation = 20

    dict_sample = {
    'x_L' : x_L,
    'y_L' : y_L,
    'grain_discretisation' : grain_discretisation,
    'Emec_M' : np.array(np.zeros((len(y_L),len(x_L)))),
    'Eche_M' : np.array(np.zeros((len(y_L),len(x_L)))),
    'Ed_M' : np.array(np.zeros((len(y_L),len(x_L))))
    }

    #---------------------------------------------------------------------------
    #Algorithm parameters

    np_proc = 4 #number of processor used
    n_t_PFDEM = 200 #number of cycle PF-DEM

    #Time step for phase field
    n_t_PF = 8
    dt_PF_init = 0.3
    dt_PF_level1 = dt_PF_init/2
    dt_PF_level2 = dt_PF_level1/2
    dt_PF_level3 = dt_PF_level2/2

    #criteria to switch level
    Ed_level1 = 10
    Ed_level2 = 20
    Ed_level3 = 25

    #Visual parameters
    c_min = 0
    c_max = 0.08

    #Margin for sphericity study
    sphericity_margin = 0.05
    #Discretisation to find the inscribing (number of nodes in one direction)
    n_spatial_inscribing = 100

    #List of plot to do
    # Config, C_at_P, Diff_Solute, dt, Ed, Eta_c, Init_Current_Shape, Kc, Movie (need Config to work), Sint_MinEtai, Sphericity, sum_Ed
    L_flag_plot = ['Config', 'C_at_P', 'Diff_Solute', 'dt', 'Sphericity', 'Kc', 'Movie', 'sum_Ed']

    #Find a simulation ame
    template = 'PS_Long_Run' #template of the name of the simulation
    SaveData = False #Save data or not
    foldername = 'Data_2G_ACS' #name of the folder where data are saved
    if SaveData :
        i_run = 1
        folderpath = Path('../'+foldername+'/'+template+'_'+str(i_run))
        while folderpath.exists():
            i_run = i_run + 1
            folderpath = Path('../'+foldername+'/'+template+'_'+str(i_run))
        namefile = template+'_'+str(i_run)
    else :
        namefile = template

    dict_algorithm = {
    'c_min' : c_min,
    'c_max' : c_max,
    'sphericity_margin' : sphericity_margin,
    'n_spatial_inscribing' : n_spatial_inscribing,
    'np_proc' : np_proc,
    'SaveData' : SaveData,
    'namefile' : namefile,
    'dt_PF' : dt_PF_init,
    'dt_PF_init' : dt_PF_init,
    'dt_PF_level1' : dt_PF_level1,
    'dt_PF_level2' : dt_PF_level2,
    'dt_PF_level3' : dt_PF_level3,
    'Ed_level1' : Ed_level1,
    'Ed_level2' : Ed_level2,
    'Ed_level3' : Ed_level3,
    'n_t_PF' : n_t_PF,
    'foldername' : foldername,
    'n_t_PFDEM' : n_t_PFDEM,
    'L_flag_plot' : L_flag_plot
    }

    #---------------------------------------------------------------------------
    #External sollicitation parameters

    overlap_target = 10 #overlap verified before each phase field iteration
    chi = 0.5 #coefficient applied to the chemical energy

    dict_sollicitation = {
    'overlap_target' : overlap_target,
    'chi' : chi
    }

    #---------------------------------------------------------------------------
    #Material parameters

    #phase field
    width_int = math.sqrt((x_L[4]-x_L[0])**2+(y_L[4]-y_L[0])**2)
    Mobility = 1 #L1, L2 in .i
    #Diffusion of etai
    kappa_eta = 0.01
    #Energy barrier of etai
    Energy_barrier = 20*kappa_eta/(width_int)**2
    #Diffusion of c, the solute generated by the dissolution
    kappa_c = 50
    method_to_compute_kc = 'dilation' #dilation or interpolation
    #Definition of the evolution of the penalty term inside a grain (for interpolation)
    tau_kappa_c = 5 #kc = kc0 e(-(R_i-d)/R_i/tau_kappa_c)
    #Define the spring value
    Y = 70*10**9*10**(-6*2)
    nu = 0.3


    dict_material = {
    'w' : width_int,
    'M' : Mobility,
    'kappa_eta' : kappa_eta,
    'kappa_c' : kappa_c,
    'tau_kappa_c' : tau_kappa_c,
    'method_to_compute_kc' : method_to_compute_kc,
    'Energy_barrier' : Energy_barrier,
    'Y' : Y,
    'nu' : nu
    }

    #---------------------------------------------------------------------------

    return dict_algorithm, dict_material, dict_sample, dict_sollicitation

#-------------------------------------------------------------------------------

def Add_2grains(dict_material,dict_sample):
    '''
    Generate two grains in the sample.

        Input :
            a material dictionnary (a dictionnary)
            a sample dictionnary (a dictionnary)
        Output :
            Nothing but the sample dictionnary gets a new grains configuration (a list)
    '''
    #grain 1
    radius = 100
    center = np.array([np.mean(dict_sample['x_L'])-radius,np.mean(dict_sample['y_L'])])
    grain_1 = Grain.Grain(1,radius,center,dict_material,dict_sample)

    #grain 2
    radius = 100
    center = np.array([np.mean(dict_sample['x_L'])+radius,np.mean(dict_sample['y_L'])])
    grain_2 = Grain.Grain(2,radius,center,dict_material,dict_sample)

    #add element in dict
    dict_sample['L_g'] = [grain_1, grain_2]

#-------------------------------------------------------------------------------

def Add_S0(dict_sample, dict_sollicitation):
    '''
    Compute the initial intersection surface between two disk particles.
    See https://calculis.net/q/aire-intersection-disques-35

        Input :
            a sample dictionnary (a dictionnary)
        Output :
            Nothing but the sample dictionnary gets a new entry (a float)
    '''
    #notation from the forum
    R = dict_sample['L_g'][0].r_mean
    R_prime = dict_sample['L_g'][1].r_mean
    D = R + R_prime - dict_sollicitation['overlap_target']
    d  = (R**2 + D**2 - R_prime**2)/2/D
    d_prime = D - d
    #Part from particle 1
    S1 = R**2*math.acos(d/R)-d*math.sqrt(R**2-d**2)
    #Part from particle 2
    S2 = R_prime**2*math.acos(d_prime/R_prime)-d_prime*math.sqrt(R_prime**2-d_prime**2)

    #add element in dictionnary
    dict_sample['S_int_0'] = S1 + S2

#-------------------------------------------------------------------------------

def Add_solute(dict_sample):
    '''
    Generate solute in the sample.

        Input :
            a sample dictionnary (a dictionnary)
        Output :
            Nothing but the sample dictionnary gets a new solute configuration (a n_y x n_x numpy array)
    '''
    #solute
    solute_M = np.zeros((len(dict_sample['y_L']),len(dict_sample['x_L'])))

    #add element in dict
    dict_sample['solute_M'] = solute_M

#-------------------------------------------------------------------------------

def Add_alpha_emec(dict_sample, dict_sollicitation):
    '''
    Compute the coefficient alpha applied to the mechanical energy.

        Input :
            a sample dictionnary (a dictionnary)
            a sollicitation dictionnary (a dictionnary)
        Output :
            Nothing but the sollicitation dictionnary gets a new attribut (a float)
    '''
    alpha =  0.2*dict_sample['S_int_0']

    #update dict
    dict_sollicitation['alpha'] = alpha

#-------------------------------------------------------------------------------

def Criteria_StopSimulation(dict_algorithm):
    '''
    Define a stop criteria for the PFDEM simulation.

    The simulation stops if the number of iterations reaches a maximum value.

        Input :
            an algorithm dictionnary (a dictionnary)
        Output :
            The result depends on the fact if the stop criteria is reached or not (a bool)
    '''
    Criteria_Verified = False
    if dict_algorithm['i_PFDEM'] >= dict_algorithm['n_t_PFDEM']:
        Criteria_Verified = True
    return Criteria_Verified

Functions

def Add_2grains(dict_material, dict_sample)

Generate two grains in the sample.

Input :
    a material dictionnary (a dictionnary)
    a sample dictionnary (a dictionnary)
Output :
    Nothing but the sample dictionnary gets a new grains configuration (a list)
Expand source code
def Add_2grains(dict_material,dict_sample):
    '''
    Generate two grains in the sample.

        Input :
            a material dictionnary (a dictionnary)
            a sample dictionnary (a dictionnary)
        Output :
            Nothing but the sample dictionnary gets a new grains configuration (a list)
    '''
    #grain 1
    radius = 100
    center = np.array([np.mean(dict_sample['x_L'])-radius,np.mean(dict_sample['y_L'])])
    grain_1 = Grain.Grain(1,radius,center,dict_material,dict_sample)

    #grain 2
    radius = 100
    center = np.array([np.mean(dict_sample['x_L'])+radius,np.mean(dict_sample['y_L'])])
    grain_2 = Grain.Grain(2,radius,center,dict_material,dict_sample)

    #add element in dict
    dict_sample['L_g'] = [grain_1, grain_2]
def Add_S0(dict_sample, dict_sollicitation)

Compute the initial intersection surface between two disk particles. See https://calculis.net/q/aire-intersection-disques-35

Input :
    a sample dictionnary (a dictionnary)
Output :
    Nothing but the sample dictionnary gets a new entry (a float)
Expand source code
def Add_S0(dict_sample, dict_sollicitation):
    '''
    Compute the initial intersection surface between two disk particles.
    See https://calculis.net/q/aire-intersection-disques-35

        Input :
            a sample dictionnary (a dictionnary)
        Output :
            Nothing but the sample dictionnary gets a new entry (a float)
    '''
    #notation from the forum
    R = dict_sample['L_g'][0].r_mean
    R_prime = dict_sample['L_g'][1].r_mean
    D = R + R_prime - dict_sollicitation['overlap_target']
    d  = (R**2 + D**2 - R_prime**2)/2/D
    d_prime = D - d
    #Part from particle 1
    S1 = R**2*math.acos(d/R)-d*math.sqrt(R**2-d**2)
    #Part from particle 2
    S2 = R_prime**2*math.acos(d_prime/R_prime)-d_prime*math.sqrt(R_prime**2-d_prime**2)

    #add element in dictionnary
    dict_sample['S_int_0'] = S1 + S2
def Add_alpha_emec(dict_sample, dict_sollicitation)

Compute the coefficient alpha applied to the mechanical energy.

Input :
    a sample dictionnary (a dictionnary)
    a sollicitation dictionnary (a dictionnary)
Output :
    Nothing but the sollicitation dictionnary gets a new attribut (a float)
Expand source code
def Add_alpha_emec(dict_sample, dict_sollicitation):
    '''
    Compute the coefficient alpha applied to the mechanical energy.

        Input :
            a sample dictionnary (a dictionnary)
            a sollicitation dictionnary (a dictionnary)
        Output :
            Nothing but the sollicitation dictionnary gets a new attribut (a float)
    '''
    alpha =  0.2*dict_sample['S_int_0']

    #update dict
    dict_sollicitation['alpha'] = alpha
def Add_solute(dict_sample)

Generate solute in the sample.

Input :
    a sample dictionnary (a dictionnary)
Output :
    Nothing but the sample dictionnary gets a new solute configuration (a n_y x n_x numpy array)
Expand source code
def Add_solute(dict_sample):
    '''
    Generate solute in the sample.

        Input :
            a sample dictionnary (a dictionnary)
        Output :
            Nothing but the sample dictionnary gets a new solute configuration (a n_y x n_x numpy array)
    '''
    #solute
    solute_M = np.zeros((len(dict_sample['y_L']),len(dict_sample['x_L'])))

    #add element in dict
    dict_sample['solute_M'] = solute_M
def All_parameters()

This function is called in main.py to have all the parameters needed in the simulation.

Input :
    Nothing
Output :
    an algorithm dictionnary (a dictionnary)
    a material dictionnary (a dictionnary)
    a sample dictionnary (a dictionnary)
    a sollicitation dictionnary (a dictionnary)
Expand source code
def All_parameters():
    '''
    This function is called in main.py to have all the parameters needed in the simulation.

        Input :
            Nothing
        Output :
            an algorithm dictionnary (a dictionnary)
            a material dictionnary (a dictionnary)
            a sample dictionnary (a dictionnary)
            a sollicitation dictionnary (a dictionnary)
    '''
    #---------------------------------------------------------------------------
    #Sample parameters

    #spatial discretisation
    x_min = -230
    x_max = 230
    nx = 180
    x_L = np.linspace(x_min,x_max,nx)
    y_min = -130
    y_max = 130
    ny = 100
    y_L = np.linspace(y_min,y_max,ny)

    #approximatively the number of vertices for one grain during DEM simulation
    grain_discretisation = 20

    dict_sample = {
    'x_L' : x_L,
    'y_L' : y_L,
    'grain_discretisation' : grain_discretisation,
    'Emec_M' : np.array(np.zeros((len(y_L),len(x_L)))),
    'Eche_M' : np.array(np.zeros((len(y_L),len(x_L)))),
    'Ed_M' : np.array(np.zeros((len(y_L),len(x_L))))
    }

    #---------------------------------------------------------------------------
    #Algorithm parameters

    np_proc = 4 #number of processor used
    n_t_PFDEM = 200 #number of cycle PF-DEM

    #Time step for phase field
    n_t_PF = 8
    dt_PF_init = 0.3
    dt_PF_level1 = dt_PF_init/2
    dt_PF_level2 = dt_PF_level1/2
    dt_PF_level3 = dt_PF_level2/2

    #criteria to switch level
    Ed_level1 = 10
    Ed_level2 = 20
    Ed_level3 = 25

    #Visual parameters
    c_min = 0
    c_max = 0.08

    #Margin for sphericity study
    sphericity_margin = 0.05
    #Discretisation to find the inscribing (number of nodes in one direction)
    n_spatial_inscribing = 100

    #List of plot to do
    # Config, C_at_P, Diff_Solute, dt, Ed, Eta_c, Init_Current_Shape, Kc, Movie (need Config to work), Sint_MinEtai, Sphericity, sum_Ed
    L_flag_plot = ['Config', 'C_at_P', 'Diff_Solute', 'dt', 'Sphericity', 'Kc', 'Movie', 'sum_Ed']

    #Find a simulation ame
    template = 'PS_Long_Run' #template of the name of the simulation
    SaveData = False #Save data or not
    foldername = 'Data_2G_ACS' #name of the folder where data are saved
    if SaveData :
        i_run = 1
        folderpath = Path('../'+foldername+'/'+template+'_'+str(i_run))
        while folderpath.exists():
            i_run = i_run + 1
            folderpath = Path('../'+foldername+'/'+template+'_'+str(i_run))
        namefile = template+'_'+str(i_run)
    else :
        namefile = template

    dict_algorithm = {
    'c_min' : c_min,
    'c_max' : c_max,
    'sphericity_margin' : sphericity_margin,
    'n_spatial_inscribing' : n_spatial_inscribing,
    'np_proc' : np_proc,
    'SaveData' : SaveData,
    'namefile' : namefile,
    'dt_PF' : dt_PF_init,
    'dt_PF_init' : dt_PF_init,
    'dt_PF_level1' : dt_PF_level1,
    'dt_PF_level2' : dt_PF_level2,
    'dt_PF_level3' : dt_PF_level3,
    'Ed_level1' : Ed_level1,
    'Ed_level2' : Ed_level2,
    'Ed_level3' : Ed_level3,
    'n_t_PF' : n_t_PF,
    'foldername' : foldername,
    'n_t_PFDEM' : n_t_PFDEM,
    'L_flag_plot' : L_flag_plot
    }

    #---------------------------------------------------------------------------
    #External sollicitation parameters

    overlap_target = 10 #overlap verified before each phase field iteration
    chi = 0.5 #coefficient applied to the chemical energy

    dict_sollicitation = {
    'overlap_target' : overlap_target,
    'chi' : chi
    }

    #---------------------------------------------------------------------------
    #Material parameters

    #phase field
    width_int = math.sqrt((x_L[4]-x_L[0])**2+(y_L[4]-y_L[0])**2)
    Mobility = 1 #L1, L2 in .i
    #Diffusion of etai
    kappa_eta = 0.01
    #Energy barrier of etai
    Energy_barrier = 20*kappa_eta/(width_int)**2
    #Diffusion of c, the solute generated by the dissolution
    kappa_c = 50
    method_to_compute_kc = 'dilation' #dilation or interpolation
    #Definition of the evolution of the penalty term inside a grain (for interpolation)
    tau_kappa_c = 5 #kc = kc0 e(-(R_i-d)/R_i/tau_kappa_c)
    #Define the spring value
    Y = 70*10**9*10**(-6*2)
    nu = 0.3


    dict_material = {
    'w' : width_int,
    'M' : Mobility,
    'kappa_eta' : kappa_eta,
    'kappa_c' : kappa_c,
    'tau_kappa_c' : tau_kappa_c,
    'method_to_compute_kc' : method_to_compute_kc,
    'Energy_barrier' : Energy_barrier,
    'Y' : Y,
    'nu' : nu
    }

    #---------------------------------------------------------------------------

    return dict_algorithm, dict_material, dict_sample, dict_sollicitation
def Criteria_StopSimulation(dict_algorithm)

Define a stop criteria for the PFDEM simulation.

The simulation stops if the number of iterations reaches a maximum value.

Input :
    an algorithm dictionnary (a dictionnary)
Output :
    The result depends on the fact if the stop criteria is reached or not (a bool)
Expand source code
def Criteria_StopSimulation(dict_algorithm):
    '''
    Define a stop criteria for the PFDEM simulation.

    The simulation stops if the number of iterations reaches a maximum value.

        Input :
            an algorithm dictionnary (a dictionnary)
        Output :
            The result depends on the fact if the stop criteria is reached or not (a bool)
    '''
    Criteria_Verified = False
    if dict_algorithm['i_PFDEM'] >= dict_algorithm['n_t_PFDEM']:
        Criteria_Verified = True
    return Criteria_Verified