Module Etai

@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 unconserved variables used in phase-field simulation.

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 unconserved variables used in phase-field simulation.
"""

#-------------------------------------------------------------------------------
#Libs
#-------------------------------------------------------------------------------

import numpy as np
import random

#Own
import Report
import Grain

#-------------------------------------------------------------------------------
#Class
#-------------------------------------------------------------------------------

class Etai:

    #-------------------------------------------------------------------------------
    def __init__(self, ID, L_ig, L_g):
        '''
        Defining the phase variables etai.

        One eta represents several grains.

            Input :
                an id (an integer)
                a list of grain id represented by the variable (a list)
                the list of the grains (a list)
            Output :
                an eta (an eta)
        '''
        self.id = ID
        self.l_ig = L_ig
        self.update_etai_M(L_g)
        for i_grain in self.l_ig :
            L_g[i_grain].etai = self.id

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

    def update_etai_M(self,L_g):
        '''
        Sum variables field of all the grain assigned to this eta.

            Input :
                itself (an eta)
                the list of grains (a list)
            Output :
                Nothing, but the phase field attribute is updated (a nx x ny numpy array)
        '''
        self.etai_M = np.array(L_g[self.l_ig[0]].etai_M.copy())
        for i in range(1,len(self.l_ig)):
            self.etai_M = self.etai_M + np.array(L_g[self.l_ig[i]].etai_M.copy())

#-------------------------------------------------------------------------------
#Function
#-------------------------------------------------------------------------------

def etai_distribution(dict_algorithm, dict_sample, simulation_report):
    '''
    Assign grains to etai.

    A minimal distance between grains with same eta is computed from the factor variable

        Input :
            an algorithm dictionnary (a dict)
            a sample dictionnary (a dict)
            a simulation report (a report)
        Output :
            Nothing, but the sample dictionnary is updated with the list of the etai (a list)
    '''
    #initialisation
    for grain in dict_sample['L_g']:
        grain.ig_near_L = []
        grain.eta_near_L = []
    #look for grains near
    for i_grain1 in range(1,len(dict_sample['L_g'])):
        grain1 = dict_sample['L_g'][i_grain1]
        for i_grain2 in range(0,i_grain1):
            grain2 = dict_sample['L_g'][i_grain2]
            if np.linalg.norm(grain1.center - grain2.center) < dict_algorithm['factor_etai'] * (grain1.r_max+grain2.r_max):
                grain1.ig_near_L.append(i_grain2)
                grain2.ig_near_L.append(i_grain1)
    #first try
    n_etai_L = [1]
    L_etai = [0]
    L_ig_etai = [[0]]
    dict_sample['L_g'][0].id_eta = 0
    for grain in dict_sample['L_g']:
        if 0 in grain.ig_near_L:
            grain.eta_near_L.append(0)
    for i_grain in range(1,len(dict_sample['L_g'])):
        grain = dict_sample['L_g'][i_grain]
        etai_defined = False
        etai = 0
        while etai < len(L_etai) and not etai_defined:
            if etai not in grain.eta_near_L:
                grain.id_eta = etai
                etai_defined = True
                n_etai_L[etai] = n_etai_L[etai] + 1
                L_ig_etai[etai].append(i_grain)
                for grain2 in dict_sample['L_g']:
                    if i_grain in grain2.ig_near_L:
                        grain2.eta_near_L.append(etai)
            etai = etai + 1
        if etai == len(L_etai) and not etai_defined:
            grain.id_eta = etai
            L_etai.append(etai)
            n_etai_L.append(1)
            L_ig_etai.append([i_grain])
            for grain2 in dict_sample['L_g']:
                if i_grain in grain2.ig_near_L:
                    grain2.eta_near_L.append(etai)
    #adaptation (try to have the same number of grain assigned to all eta)
    n_etai_mean = np.mean(n_etai_L)
    adaptation_done = False
    adaptation_i = 0
    while not adaptation_done :
        adaptation_i = adaptation_i + 1
        L_ig_over =  L_ig_etai[n_etai_L.index(max(n_etai_L))]
        id_g_to_work = L_ig_over[random.randint(0,len(L_ig_over)-1)]
        etai_over = n_etai_L.index(max(n_etai_L))
        etai_under = n_etai_L.index(min(n_etai_L))
        grain = dict_sample['L_g'][id_g_to_work]
        if etai_under not in grain.eta_near_L:
            grain.id_eta = etai_under
            n_etai_L[etai_over] = n_etai_L[etai_over] - 1
            n_etai_L[etai_under] = n_etai_L[etai_under] + 1
            L_ig_etai[etai_over].remove(id_g_to_work)
            L_ig_etai[etai_under].append(id_g_to_work)
            for grain2 in dict_sample['L_g']:
                if id_g_to_work in grain2.ig_near_L:
                    grain2.eta_near_L.remove(etai_over)
                    grain2.eta_near_L.append(etai_under)
        #check the quality
        adaptation_done = True
        for n_etai in n_etai_L :
            if n_etai_mean-2 < n_etai and n_etai < n_etai_mean+2:
                adaptation_done = False
        if adaptation_i > len(dict_sample['L_g']):
            adaptation_done = True

    #Create etai
    L_etai = []
    for i in range(len(L_ig_etai)) :
        etai = Etai(i, L_ig_etai[i], dict_sample['L_g'])
        L_etai.append(etai)

    #update the dict
    dict_sample['L_etai'] = L_etai
    simulation_report.write(f"{len(dict_sample['L_etai'])} phase variables used.\n")
    simulation_report.write(f"{round(len(dict_sample['L_g'])/len(dict_sample['L_etai']),1)} grains described for one phase variable.\n")

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

def PFtoDEM_Multi(FileToRead,dict_algorithm,dict_material,dict_sample):
    '''
    Read file from MOOSE simulation to reconstruct the phase field of the grain.

        Input :
            the name of the file to read (a string)
            an algorithm dictionnary (a dictionnary)
            a material dictionnary (a dictionnary)
            a sample dictionnary (a dictionnary)
        Output :
            Nothing but the grain gets an updated attribute (a n_y x n_x numpy array)
    '''
    #--------------------------------------------------------------------------
    #Global parameters
    #---------------------------------------------------------------------------

    L_etai_M = np.array(np.zeros((len(dict_sample['L_etai']),len(dict_sample['y_L']),len(dict_sample['x_L']))))

    id_L = None
    eta_selector_len = len('        <DataArray type="Float64" Name="eta')
    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
        for etai in dict_sample['L_etai']:
            L_Work.append([]) #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="eta':
                id_L = 1 + int(line[eta_selector_len])

            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 id_L != None :
                if 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 and update of etai_M
        for i in range(len(L_Work[0])):
            #Interpolation method
            L_dy = []
            for y_i in dict_sample['y_L'] :
                L_dy.append(abs(y_i - L_Work[1][i]))
            L_dx = []
            for x_i in dict_sample['x_L'] :
                L_dx.append(abs(x_i - L_Work[0][i]))
            for j in range(2,len(L_Work)):
                L_etai_M[j-2][-1-list(L_dy).index(min(L_dy))][list(L_dx).index(min(L_dx))] = L_Work[j][i]

    #---------------------------------------------------------------------------
    #Transmit data to grains
    #---------------------------------------------------------------------------

    for grain in dict_sample['L_g']:
        grain.ExtractPF_from_Eta(L_etai_M, dict_algorithm, dict_material, dict_sample)
        grain.geometric_study(dict_sample)

Functions

def PFtoDEM_Multi(FileToRead, dict_algorithm, dict_material, dict_sample)

Read file from MOOSE simulation to reconstruct the phase field of the grain.

Input :
    the name of the file to read (a string)
    an algorithm dictionnary (a dictionnary)
    a material dictionnary (a dictionnary)
    a sample dictionnary (a dictionnary)
Output :
    Nothing but the grain gets an updated attribute (a n_y x n_x numpy array)
Expand source code
def PFtoDEM_Multi(FileToRead,dict_algorithm,dict_material,dict_sample):
    '''
    Read file from MOOSE simulation to reconstruct the phase field of the grain.

        Input :
            the name of the file to read (a string)
            an algorithm dictionnary (a dictionnary)
            a material dictionnary (a dictionnary)
            a sample dictionnary (a dictionnary)
        Output :
            Nothing but the grain gets an updated attribute (a n_y x n_x numpy array)
    '''
    #--------------------------------------------------------------------------
    #Global parameters
    #---------------------------------------------------------------------------

    L_etai_M = np.array(np.zeros((len(dict_sample['L_etai']),len(dict_sample['y_L']),len(dict_sample['x_L']))))

    id_L = None
    eta_selector_len = len('        <DataArray type="Float64" Name="eta')
    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
        for etai in dict_sample['L_etai']:
            L_Work.append([]) #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="eta':
                id_L = 1 + int(line[eta_selector_len])

            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 id_L != None :
                if 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 and update of etai_M
        for i in range(len(L_Work[0])):
            #Interpolation method
            L_dy = []
            for y_i in dict_sample['y_L'] :
                L_dy.append(abs(y_i - L_Work[1][i]))
            L_dx = []
            for x_i in dict_sample['x_L'] :
                L_dx.append(abs(x_i - L_Work[0][i]))
            for j in range(2,len(L_Work)):
                L_etai_M[j-2][-1-list(L_dy).index(min(L_dy))][list(L_dx).index(min(L_dx))] = L_Work[j][i]

    #---------------------------------------------------------------------------
    #Transmit data to grains
    #---------------------------------------------------------------------------

    for grain in dict_sample['L_g']:
        grain.ExtractPF_from_Eta(L_etai_M, dict_algorithm, dict_material, dict_sample)
        grain.geometric_study(dict_sample)
def etai_distribution(dict_algorithm, dict_sample, simulation_report)

Assign grains to etai.

A minimal distance between grains with same eta is computed from the factor variable

Input :
    an algorithm dictionnary (a dict)
    a sample dictionnary (a dict)
    a simulation report (a report)
Output :
    Nothing, but the sample dictionnary is updated with the list of the etai (a list)
Expand source code
def etai_distribution(dict_algorithm, dict_sample, simulation_report):
    '''
    Assign grains to etai.

    A minimal distance between grains with same eta is computed from the factor variable

        Input :
            an algorithm dictionnary (a dict)
            a sample dictionnary (a dict)
            a simulation report (a report)
        Output :
            Nothing, but the sample dictionnary is updated with the list of the etai (a list)
    '''
    #initialisation
    for grain in dict_sample['L_g']:
        grain.ig_near_L = []
        grain.eta_near_L = []
    #look for grains near
    for i_grain1 in range(1,len(dict_sample['L_g'])):
        grain1 = dict_sample['L_g'][i_grain1]
        for i_grain2 in range(0,i_grain1):
            grain2 = dict_sample['L_g'][i_grain2]
            if np.linalg.norm(grain1.center - grain2.center) < dict_algorithm['factor_etai'] * (grain1.r_max+grain2.r_max):
                grain1.ig_near_L.append(i_grain2)
                grain2.ig_near_L.append(i_grain1)
    #first try
    n_etai_L = [1]
    L_etai = [0]
    L_ig_etai = [[0]]
    dict_sample['L_g'][0].id_eta = 0
    for grain in dict_sample['L_g']:
        if 0 in grain.ig_near_L:
            grain.eta_near_L.append(0)
    for i_grain in range(1,len(dict_sample['L_g'])):
        grain = dict_sample['L_g'][i_grain]
        etai_defined = False
        etai = 0
        while etai < len(L_etai) and not etai_defined:
            if etai not in grain.eta_near_L:
                grain.id_eta = etai
                etai_defined = True
                n_etai_L[etai] = n_etai_L[etai] + 1
                L_ig_etai[etai].append(i_grain)
                for grain2 in dict_sample['L_g']:
                    if i_grain in grain2.ig_near_L:
                        grain2.eta_near_L.append(etai)
            etai = etai + 1
        if etai == len(L_etai) and not etai_defined:
            grain.id_eta = etai
            L_etai.append(etai)
            n_etai_L.append(1)
            L_ig_etai.append([i_grain])
            for grain2 in dict_sample['L_g']:
                if i_grain in grain2.ig_near_L:
                    grain2.eta_near_L.append(etai)
    #adaptation (try to have the same number of grain assigned to all eta)
    n_etai_mean = np.mean(n_etai_L)
    adaptation_done = False
    adaptation_i = 0
    while not adaptation_done :
        adaptation_i = adaptation_i + 1
        L_ig_over =  L_ig_etai[n_etai_L.index(max(n_etai_L))]
        id_g_to_work = L_ig_over[random.randint(0,len(L_ig_over)-1)]
        etai_over = n_etai_L.index(max(n_etai_L))
        etai_under = n_etai_L.index(min(n_etai_L))
        grain = dict_sample['L_g'][id_g_to_work]
        if etai_under not in grain.eta_near_L:
            grain.id_eta = etai_under
            n_etai_L[etai_over] = n_etai_L[etai_over] - 1
            n_etai_L[etai_under] = n_etai_L[etai_under] + 1
            L_ig_etai[etai_over].remove(id_g_to_work)
            L_ig_etai[etai_under].append(id_g_to_work)
            for grain2 in dict_sample['L_g']:
                if id_g_to_work in grain2.ig_near_L:
                    grain2.eta_near_L.remove(etai_over)
                    grain2.eta_near_L.append(etai_under)
        #check the quality
        adaptation_done = True
        for n_etai in n_etai_L :
            if n_etai_mean-2 < n_etai and n_etai < n_etai_mean+2:
                adaptation_done = False
        if adaptation_i > len(dict_sample['L_g']):
            adaptation_done = True

    #Create etai
    L_etai = []
    for i in range(len(L_ig_etai)) :
        etai = Etai(i, L_ig_etai[i], dict_sample['L_g'])
        L_etai.append(etai)

    #update the dict
    dict_sample['L_etai'] = L_etai
    simulation_report.write(f"{len(dict_sample['L_etai'])} phase variables used.\n")
    simulation_report.write(f"{round(len(dict_sample['L_g'])/len(dict_sample['L_etai']),1)} grains described for one phase variable.\n")

Classes

class Etai (ID, L_ig, L_g)

Defining the phase variables etai.

One eta represents several grains.

Input :
    an id (an integer)
    a list of grain id represented by the variable (a list)
    the list of the grains (a list)
Output :
    an eta (an eta)
Expand source code
class Etai:

    #-------------------------------------------------------------------------------
    def __init__(self, ID, L_ig, L_g):
        '''
        Defining the phase variables etai.

        One eta represents several grains.

            Input :
                an id (an integer)
                a list of grain id represented by the variable (a list)
                the list of the grains (a list)
            Output :
                an eta (an eta)
        '''
        self.id = ID
        self.l_ig = L_ig
        self.update_etai_M(L_g)
        for i_grain in self.l_ig :
            L_g[i_grain].etai = self.id

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

    def update_etai_M(self,L_g):
        '''
        Sum variables field of all the grain assigned to this eta.

            Input :
                itself (an eta)
                the list of grains (a list)
            Output :
                Nothing, but the phase field attribute is updated (a nx x ny numpy array)
        '''
        self.etai_M = np.array(L_g[self.l_ig[0]].etai_M.copy())
        for i in range(1,len(self.l_ig)):
            self.etai_M = self.etai_M + np.array(L_g[self.l_ig[i]].etai_M.copy())

Methods

def update_etai_M(self, L_g)

Sum variables field of all the grain assigned to this eta.

Input :
    itself (an eta)
    the list of grains (a list)
Output :
    Nothing, but the phase field attribute is updated (a nx x ny numpy array)
Expand source code
def update_etai_M(self,L_g):
    '''
    Sum variables field of all the grain assigned to this eta.

        Input :
            itself (an eta)
            the list of grains (a list)
        Output :
            Nothing, but the phase field attribute is updated (a nx x ny numpy array)
    '''
    self.etai_M = np.array(L_g[self.l_ig[0]].etai_M.copy())
    for i in range(1,len(self.l_ig)):
        self.etai_M = self.etai_M + np.array(L_g[self.l_ig[i]].etai_M.copy())