Module main

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

This is the main file.

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

This is the main file.
"""

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

import numpy as np
import os
import shutil
from datetime import datetime
from pathlib import Path

#Own function and class
from Write_txt import Write_txt
from Create_i_AC import Create_i_AC_local
from Create_LG_IC import LG_tempo, From_LG_tempo_to_usable
import Owntools
import Grain
import Contact
import Contact_gw
import Report
import User

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

def main_iteration(dict_algorithm, dict_geometry, dict_material, dict_sollicitations, dict_sample, dict_tracker, simulation_report):
    '''
    Description of one PDEM iteration.

    The iteration is composed by a DEM step (to obtain a steady state configuration) and a PF step (to obtain dissolution and precipitation).

        Input :
            an algorithm dictionnary (a dict)
            a geometry dictionnary (a dict)
            a material dictionnary (a dict)
            a sollicitation dictionnary (a dict)
            a sample dictionnary (a dict)
            a tracker dictionnary (a dict)
            a simulation report (a Report)
        Output :
            Nothing but the dictionnaies and the report are updated
    '''
    # update element in dict
    dict_algorithm['i_PF'] = dict_algorithm['i_PF'] + 1

    simulation_report.write_and_print('\nIteration '+str(dict_algorithm['i_PF'])+' / '+str(dict_algorithm['n_t_PFDEM'])+'\n','\nITERATION PF '+str(dict_algorithm['i_PF'])+' / '+str(dict_algorithm['n_t_PFDEM'])+'\n')

    #prepare iteration
    if Path('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])).exists():
        shutil.rmtree('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF']))
    os.mkdir('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF']))
    os.mkdir('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/txt')
    os.mkdir('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/png')

    if dict_algorithm['MovePF_selector'] == 'Interpolation':
        # Saving to compute a rigid body motion
        L_center_g = []
        for grain in dict_sample['L_g']:
            L_center_g.append(grain.center.copy())

    # Compute kinetic energy criteria
    Ecin_stop = 0
    for grain in dict_sample['L_g']:
        Ecin_stop = Ecin_stop + 0.5*grain.m*(dict_algorithm['Ecin_ratio']*grain.r_mean/dict_algorithm['dt_DEM'])**2/len(dict_sample['L_g'])

    #Update element in dict
    dict_algorithm['Ecin_stop'] = Ecin_stop

    #Trackers and add element in dict
    dict_tracker['Ecin'] = []
    dict_tracker['Force_applied'] = []
    dict_tracker['k0_xmin'] = []
    dict_tracker['k0_xmax'] = []
    dict_tracker['y_box_max'] = [dict_sample['y_box_max']]
    dict_tracker['Force_on_upper_wall'] = []

    dict_algorithm['i_DEM'] = - 1
    DEM_loop_statut = True
    simulation_report.write('\n')
    simulation_report.tic_tempo(datetime.now())

    #-----------------------------------------------------------------------------
    # DEM iteration
    #-----------------------------------------------------------------------------

    while DEM_loop_statut :
        # update element in dict
        dict_algorithm['i_DEM'] = dict_algorithm['i_DEM'] + 1

        for grain in dict_sample['L_g']:
            grain.init_f_control(dict_sollicitations)

        # Detection of contacts between grains
        if dict_algorithm['i_DEM'] % dict_algorithm['i_update_neighborhoods']  == 0:
            Contact.Update_Neighborhoods(dict_algorithm,dict_sample)
        Contact.Grains_Polyhedral_contact_Neighborhoods(dict_material,dict_sample)

        # Detection of contacts between grain and walls
        if dict_algorithm['i_DEM'] % dict_algorithm['i_update_neighborhoods']  == 0:
            Contact_gw.Update_wall_Neighborhoods(dict_algorithm, dict_sample)
        Contact_gw.Grains_Polyhedral_Wall_contact_Neighborhood(dict_material,dict_sample)

        #Compute contact interactions (g-g and g-w)
        for contact in dict_sample['L_contact']:
            if dict_algorithm['Spring_type'] == 'Ponctual':
                contact.DEM_2grains_Polyhedral_normal()
                contact.DEM_2grains_Polyhedral_tangential(dict_algorithm['dt_DEM'])
            elif dict_algorithm['Spring_type'] == 'Surface':
                contact.DEM_2grains_Polyhedral_normal_surface()
                contact.DEM_2grains_Polyhedral_tangential_surface(dict_algorithm['dt_DEM'])
            else :
                simulation_report.write('Spring type not available !')
                raise ValueError('Spring type not available !')
        for contact in dict_sample['L_contact_gw'] :
            if dict_algorithm['Spring_type'] == 'Ponctual':
                contact.DEM_gw_Polyhedral_normal()
                contact.DEM_gw_Polyhedral_tangential(dict_algorithm['dt_DEM'])
            else : #Surface must be coded for contact gw
                simulation_report.write('Spring type not available !')
                raise ValueError('Spring type not available !')

        #Move particles and trackers
        #Semi implicit euler scheme
        Ecin = 0
        Force_applied = 0
        for grain in dict_sample['L_g']:
            a_i = grain.f/grain.m
            v_i = grain.v + a_i*dict_algorithm['dt_DEM']
            dw_i = grain.mz/grain.inertia
            w_i = grain.w + dw_i*dict_algorithm['dt_DEM']
            grain.update_geometry_kinetic(v_i,a_i,w_i,dict_algorithm['dt_DEM']) #Move grains
            Ecin = Ecin + 0.5*grain.m*np.linalg.norm(grain.v)**2/len(dict_sample['L_g'])
            Force_applied = Force_applied + np.linalg.norm(grain.f)/len(dict_sample['L_g'])

        #Control the y_max to verify vertical confinement
        Owntools.Control_y_max_NR(dict_sample,dict_sollicitations)
        Owntools.Compute_k0(dict_sample,dict_sollicitations)
        #trackers
        dict_tracker['Ecin'].append(Ecin)
        dict_tracker['Force_applied'].append(Force_applied)
        dict_tracker['y_box_max'].append(dict_sample['y_box_max'])
        dict_tracker['Force_on_upper_wall'].append(dict_sollicitations['Force_on_upper_wall'])
        dict_tracker['k0_xmin'].append(dict_sample['k0_xmin'])
        dict_tracker['k0_xmax'].append(dict_sample['k0_xmax'])

        if dict_algorithm['i_DEM'] %dict_algorithm['i_print_plot'] == 0:
            print('\nPF '+str(dict_algorithm['i_PF'])+' -> i_DEM '+str(dict_algorithm['i_DEM']+1)+' / '+str(dict_algorithm['i_DEM_stop']+1)+' (max)')
            print('Ecin',int(Ecin),'/',int(dict_algorithm['Ecin_stop']),'('+str(int(100*Ecin/dict_algorithm['Ecin_stop'])),' %)')
            print('F_confinement',int(dict_sollicitations['Force_on_upper_wall']),'/',int(dict_sollicitations['Vertical_Confinement_Force']),'('+str(int(100*dict_sollicitations['Force_on_upper_wall']/dict_sollicitations['Vertical_Confinement_Force'])),' %)')

            Owntools.save_DEM_tempo(dict_algorithm,dict_sample,dict_sollicitations,dict_tracker)

            if dict_algorithm['Debug_DEM'] :
                Owntools.Debug_DEM_f(dict_algorithm, dict_sample)
                Write_txt(dict_algorithm,dict_sample)

        #-----------------------------------------------------------------------------
        # Stop conditions
        #-----------------------------------------------------------------------------

        if dict_algorithm['i_DEM'] >= dict_algorithm['i_DEM_stop'] :
            DEM_loop_statut = False
            print("DEM loop stopped by too many iterations.")
            simulation_report.write('/!\ End of DEM steps with '+str(dict_algorithm['i_DEM']+1)+' iterations / '+str(dict_algorithm['i_DEM_stop']+1)+'/!\ \n')
        if Ecin < dict_algorithm['Ecin_stop'] and dict_algorithm['i_DEM'] > dict_algorithm['n_window_stop'] and (dict_sollicitations['Vertical_Confinement_Force']*0.95<dict_sollicitations['Force_on_upper_wall'] and dict_sollicitations['Force_on_upper_wall']<dict_sollicitations['Vertical_Confinement_Force']*1.05):
            k0_xmin_window = dict_tracker['k0_xmin'][dict_algorithm['i_DEM']+1-dict_algorithm['n_window_stop']:dict_algorithm['i_DEM']+1]
            k0_xmax_window = dict_tracker['k0_xmax'][dict_algorithm['i_DEM']+1-dict_algorithm['n_window_stop']:dict_algorithm['i_DEM']+1]
            y_box_max_window = dict_tracker['y_box_max'][dict_algorithm['i_DEM']+1-dict_algorithm['n_window_stop']:dict_algorithm['i_DEM']+1]
            if max(k0_xmin_window) - min(k0_xmin_window) < dict_algorithm['dk0_stop'] and max(k0_xmax_window) - min(k0_xmax_window) < dict_algorithm['dk0_stop'] and max(y_box_max_window) - min(y_box_max_window) < dict_algorithm['dy_box_max_stop']:
                DEM_loop_statut = False
                print("DEM loop stopped by steady state reached.")
                simulation_report.write("DEM loop stopped by steady state reached with "+str(dict_algorithm['i_DEM']+1)+' iterations / '+str(dict_algorithm['i_DEM_stop']+1)+"\n")

    #-----------------------------------------------------------------------------
    # Debugging at the end of DEM step
    #-----------------------------------------------------------------------------

    if dict_algorithm['Debug'] :
        Owntools.Debug_configuration(dict_algorithm,dict_sample)
        Owntools.Debug_Trackers_DEM(dict_algorithm,dict_sollicitations,dict_tracker)
        Write_txt(dict_algorithm,dict_sample)
        Owntools.Plot_chain_force(dict_algorithm['i_PF'],dict_algorithm['i_DEM'])

        Owntools.save_DEM_final(dict_algorithm,dict_sample,dict_sollicitations,dict_tracker)

    #-----------------------------------------------------------------------------
    # Compute Vertical and horizontal sollicitations to compute k0
    #-----------------------------------------------------------------------------

    Owntools.Control_y_max_NR(dict_sample,dict_sollicitations)
    Owntools.Compute_k0(dict_sample,dict_sollicitations)

    simulation_report.write('k0_xmin : '+str(round(dict_sample['k0_xmin'],2))+' / k0_xmax : '+str(round(dict_sample['k0_xmax'],2))+'\n')

    #Update element in dict
    dict_tracker['k0_xmin_L'].append(dict_sample['k0_xmin'])
    dict_tracker['k0_xmax_L'].append(dict_sample['k0_xmax'])

    simulation_report.tac_tempo(datetime.now(),'DEM loop '+str(dict_algorithm['i_PF']))

    #-----------------------------------------------------------------------------
    # PF Simulation
    #-----------------------------------------------------------------------------

    simulation_report.tic_tempo(datetime.now())

    for grain in dict_sample['L_g']:
        if grain.dissolved :
            Create_i_AC_local(grain,dict_algorithm, dict_material, dict_sample,dict_sollicitations)
            os.system('mpiexec -n '+str(dict_algorithm['np_proc'])+' ~/projects/moose/modules/combined/combined-opt -i PF_'+str(dict_algorithm['i_PF'])+'_g'+str(grain.id)+'.i')
            j_str = Owntools.Sort_Files('PF_'+str(dict_algorithm['i_PF'])+'_g'+str(grain.id),dict_algorithm)
            grain.PFtoDEM_Multi_local('Output/PF_'+str(dict_algorithm['i_PF'])+'_g'+str(grain.id)+'/PF_'+str(dict_algorithm['i_PF'])+'_g'+str(grain.id)+'_other_'+str(j_str),dict_algorithm)
            grain.Geometricstudy_local(dict_geometry,dict_sample,simulation_report)

    #Geometric study
    S_grains = 0
    S_grains_dissolvable = 0
    for grain in dict_sample['L_g']:
        S_grains = S_grains + grain.surface
        if grain.dissolved :
            S_grains_dissolvable = S_grains_dissolvable + grain.surface
    simulation_report.write('Total Surface '+str(int(S_grains))+' µm2\n')
    simulation_report.write('Total Surface dissolvable '+str(int(S_grains_dissolvable))+' µm2\n')

    # Tracker
    dict_tracker['t_L'].append(dict_tracker['t_L'][-1] + dict_algorithm['dt_PF']*dict_algorithm['n_t_PF'])
    dict_tracker['S_grains_L'].append(S_grains)
    dict_tracker['S_dissolved_L'].append(dict_tracker['S_grains_L'][0]-S_grains)
    dict_tracker['S_dissolved_perc_L'].append((dict_tracker['S_grains_L'][0]-S_grains)/(dict_tracker['S_grains_L'][0])*100)
    dict_tracker['n_grains_L'].append(len(dict_sample['L_g']))
    dict_tracker['S_grains_dissolvable_L'].append(S_grains_dissolvable)
    dict_tracker['S_dissolved_perc_dissolvable_L'].append((dict_tracker['S_grains_dissolvable_L'][0]-S_grains_dissolvable)/(dict_tracker['S_grains_dissolvable_L'][0])*100)

    simulation_report.tac_tempo(datetime.now(),'PF iteration '+str(dict_algorithm['i_PF']))

    #-----------------------------------------------------------------------------
    # Reinitialisation of contact for the next step
    #-----------------------------------------------------------------------------

    for contact in dict_sample['L_contact']:
        contact.init_contact(dict_sample['L_g'])
    for contact in dict_sample['L_contact_gw']:
        contact.init_contact_gw(dict_sample['L_g'])

    #-----------------------------------------------------------------------------
    # Print Grains configuration
    #-----------------------------------------------------------------------------

    if dict_algorithm['Debug'] :
        Owntools.Debug_configuration(dict_algorithm,dict_sample)
        #Trackers
        Owntools.Debug_Trackers(dict_tracker)

    #-----------------------------------------------------------------------------
    # Save tempo
    #-----------------------------------------------------------------------------

    if dict_algorithm['SaveData'] :
        Owntools.save_tempo(dict_algorithm,dict_tracker)
        Owntools.save_dicts(dict_algorithm, dict_geometry, dict_material, dict_sample, dict_sollicitations, dict_tracker)
        shutil.copy('Debug/Report.txt','../'+dict_algorithm['main_folder_name']+'/Report_'+dict_algorithm['name_folder']+'_tempo.txt')

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

def close_simulation(dict_algorithm, dict_tracker, simulation_report):
    '''
    Close the PFDEM.

        Input :
            an algorithm dictionnary (a dict)
            a tracker dictionnary (a dict)
            a simulation report (a Report)
        Output :
            Nothing but the dictionnaries and the report are updated
    '''
    # toc
    simulation_report.end(datetime.now())

    # Debugging and Output
    if dict_algorithm['Debug'] :

        #Making movies
        Owntools.make_mp4()
        #Trackers
        Owntools.Debug_Trackers(dict_tracker)

    #Saving data
    if dict_algorithm['SaveData'] :

        #clean memory
        if dict_algorithm['clean_memory']:
            shutil.rmtree('Data')
            shutil.rmtree('Input')
            shutil.rmtree('Output')

        Owntools.save_final(dict_algorithm,dict_tracker)
        name_actual_folder = os.path.dirname(os.path.realpath(__file__))
        shutil.copytree(name_actual_folder, '../'+dict_algorithm['main_folder_name']+'/'+dict_algorithm['name_folder'])
        os.remove('../'+dict_algorithm['main_folder_name']+'/User_'+dict_algorithm['name_folder']+'_tempo.txt')
        os.remove('../'+dict_algorithm['main_folder_name']+'/Report_'+dict_algorithm['name_folder']+'_tempo.txt')
        os.remove(dict_algorithm['name_folder']+'_save_dicts')

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

if '__main__' == __name__:
    #-------------------------------------------------------------------------------
    #Plan simulation
    #-------------------------------------------------------------------------------

    if Path('Debug').exists():
        shutil.rmtree('Debug')
    if Path('Input').exists():
        shutil.rmtree('Input')
    if Path('Output').exists():
        shutil.rmtree('Output')
    if Path('Data').exists():
        shutil.rmtree('Data')

    os.mkdir('Debug')
    os.mkdir('Debug/DEM_ite')
    os.mkdir('Debug/DEM_ite/Init')
    os.mkdir('Input')
    os.mkdir('Output')
    os.mkdir('Data')

    #-------------------------------------------------------------------------------
    # tic
    #-------------------------------------------------------------------------------

    simulation_report = Report.Report('Debug/Report',datetime.now())

    #-------------------------------------------------------------------------------
    #Initial conditions
    #-------------------------------------------------------------------------------

    simulation_report.tic_tempo(datetime.now())

    dict_algorithm, dict_geometry, dict_ic, dict_material, dict_sample, dict_sollicitations = User.All_parameters()

    if dict_algorithm['Debug'] or dict_algorithm['Debug_DEM'] :
        simulation_report.write('This simulation can be debugged\n')
    if dict_algorithm['SaveData'] :
        if not Path('../'+dict_algorithm['main_folder_name']).exists():
            os.mkdir('../'+dict_algorithm['main_folder_name'])
        simulation_report.write('This simulation is saved\n')
        i_run = 1
        folderpath = Path('../'+dict_algorithm['main_folder_name']+'/'+dict_algorithm['template_simulation_name']+str(i_run))
        while folderpath.exists():
            i_run = i_run + 1
            folderpath = Path('../'+dict_algorithm['main_folder_name']+'/'+dict_algorithm['template_simulation_name']+str(i_run))

        #add element in dict
        dict_algorithm['name_folder'] = dict_algorithm['template_simulation_name']+str(i_run)

        #tempo save of the user file
        shutil.copy('User.py','../'+dict_algorithm['main_folder_name']+'/User_'+dict_algorithm['name_folder']+'_tempo.txt')

    if dict_algorithm['SaveData'] or dict_algorithm['Debug'] or dict_algorithm['Debug_DEM']:
        simulation_report.write('\n')

    #Creation of the grain (without PF)
    simulation_report.write_and_print('Creation of the grains\n','\nCREATION OF THE GRAINS\n')
    LG_tempo(dict_algorithm, dict_geometry, dict_ic, dict_material, dict_sample, dict_sollicitations, simulation_report)

    simulation_report.tac_tempo(datetime.now(),'Initialisation')

    #-------------------------------------------------------------------------------
    #Creation of polygonal particles
    #-------------------------------------------------------------------------------

    simulation_report.tic_tempo(datetime.now())

    # Creation of the real list of grains
    From_LG_tempo_to_usable(dict_ic, dict_geometry, dict_material, dict_sample)#creation pf the dissolution .txt

    simulation_report.tac_tempo(datetime.now(),'Creation of polygonal particles')

    #-------------------------------------------------------------------------------
    #Distribution etai
    #-------------------------------------------------------------------------------

    simulation_report.tic_tempo(datetime.now())

    #Saving the grain surface
    S_grains_dissolvable = 0
    S_grains = 0
    for grain in dict_sample['L_g']:
        S_grains = S_grains + grain.surface
        if grain.dissolved :
            S_grains_dissolvable = S_grains_dissolvable + grain.surface
    simulation_report.write('Total Surface '+str(round(S_grains,0))+' µm2\n')
    simulation_report.write('Total Surface dissolvable '+str(round(S_grains_dissolvable,0))+' µm2\n')
    simulation_report.tac_tempo(datetime.now(),'Dissolution distribution')

    #-------------------------------------------------------------------------------
    #Main
    #-------------------------------------------------------------------------------

    #Tracker and create an new dict
    dict_tracker = {
        't_L' : [0],
        'S_grains_L' : [],
        'S_grains_dissolvable_L' : [],
        'S_dissolved_L' : [],
        'S_dissolved_perc_L' : [],
        'S_dissolved_perc_dissolvable_L' : [],
        'n_grains_L' : [len(dict_sample['L_g'])],
        'k0_xmin_L' : [],
        'k0_xmax_L' : []
    }

    # Preparation and add elements in dicts
    dict_algorithm['i_PF'] = 0
    dict_sample['L_contact_gw'] = []
    dict_sample['L_ij_contact_gw'] = []
    dict_sample['id_contact_gw'] = 0
    dict_sample['L_contact'] = []
    dict_sample['L_ij_contact'] = []
    dict_sample['id_contact'] = 0

    if dict_algorithm['Debug'] :
        Owntools.Debug_configuration(dict_algorithm,dict_sample)

    while not User.Criteria_StopSimulation(dict_algorithm):

        main_iteration(dict_algorithm, dict_geometry, dict_material, dict_sollicitations, dict_sample, dict_tracker, simulation_report)

    #-------------------------------------------------------------------------------
    # close simulation
    #-------------------------------------------------------------------------------

    close_simulation(dict_algorithm, dict_tracker, simulation_report)

Functions

def close_simulation(dict_algorithm, dict_tracker, simulation_report)

Close the PFDEM.

Input :
    an algorithm dictionnary (a dict)
    a tracker dictionnary (a dict)
    a simulation report (a Report)
Output :
    Nothing but the dictionnaries and the report are updated
Expand source code
def close_simulation(dict_algorithm, dict_tracker, simulation_report):
    '''
    Close the PFDEM.

        Input :
            an algorithm dictionnary (a dict)
            a tracker dictionnary (a dict)
            a simulation report (a Report)
        Output :
            Nothing but the dictionnaries and the report are updated
    '''
    # toc
    simulation_report.end(datetime.now())

    # Debugging and Output
    if dict_algorithm['Debug'] :

        #Making movies
        Owntools.make_mp4()
        #Trackers
        Owntools.Debug_Trackers(dict_tracker)

    #Saving data
    if dict_algorithm['SaveData'] :

        #clean memory
        if dict_algorithm['clean_memory']:
            shutil.rmtree('Data')
            shutil.rmtree('Input')
            shutil.rmtree('Output')

        Owntools.save_final(dict_algorithm,dict_tracker)
        name_actual_folder = os.path.dirname(os.path.realpath(__file__))
        shutil.copytree(name_actual_folder, '../'+dict_algorithm['main_folder_name']+'/'+dict_algorithm['name_folder'])
        os.remove('../'+dict_algorithm['main_folder_name']+'/User_'+dict_algorithm['name_folder']+'_tempo.txt')
        os.remove('../'+dict_algorithm['main_folder_name']+'/Report_'+dict_algorithm['name_folder']+'_tempo.txt')
        os.remove(dict_algorithm['name_folder']+'_save_dicts')
def main_iteration(dict_algorithm, dict_geometry, dict_material, dict_sollicitations, dict_sample, dict_tracker, simulation_report)

Description of one PDEM iteration.

The iteration is composed by a DEM step (to obtain a steady state configuration) and a PF step (to obtain dissolution and precipitation).

Input :
    an algorithm dictionnary (a dict)
    a geometry dictionnary (a dict)
    a material dictionnary (a dict)
    a sollicitation dictionnary (a dict)
    a sample dictionnary (a dict)
    a tracker dictionnary (a dict)
    a simulation report (a Report)
Output :
    Nothing but the dictionnaies and the report are updated
Expand source code
def main_iteration(dict_algorithm, dict_geometry, dict_material, dict_sollicitations, dict_sample, dict_tracker, simulation_report):
    '''
    Description of one PDEM iteration.

    The iteration is composed by a DEM step (to obtain a steady state configuration) and a PF step (to obtain dissolution and precipitation).

        Input :
            an algorithm dictionnary (a dict)
            a geometry dictionnary (a dict)
            a material dictionnary (a dict)
            a sollicitation dictionnary (a dict)
            a sample dictionnary (a dict)
            a tracker dictionnary (a dict)
            a simulation report (a Report)
        Output :
            Nothing but the dictionnaies and the report are updated
    '''
    # update element in dict
    dict_algorithm['i_PF'] = dict_algorithm['i_PF'] + 1

    simulation_report.write_and_print('\nIteration '+str(dict_algorithm['i_PF'])+' / '+str(dict_algorithm['n_t_PFDEM'])+'\n','\nITERATION PF '+str(dict_algorithm['i_PF'])+' / '+str(dict_algorithm['n_t_PFDEM'])+'\n')

    #prepare iteration
    if Path('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])).exists():
        shutil.rmtree('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF']))
    os.mkdir('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF']))
    os.mkdir('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/txt')
    os.mkdir('Debug/DEM_ite/PF_'+str(dict_algorithm['i_PF'])+'/png')

    if dict_algorithm['MovePF_selector'] == 'Interpolation':
        # Saving to compute a rigid body motion
        L_center_g = []
        for grain in dict_sample['L_g']:
            L_center_g.append(grain.center.copy())

    # Compute kinetic energy criteria
    Ecin_stop = 0
    for grain in dict_sample['L_g']:
        Ecin_stop = Ecin_stop + 0.5*grain.m*(dict_algorithm['Ecin_ratio']*grain.r_mean/dict_algorithm['dt_DEM'])**2/len(dict_sample['L_g'])

    #Update element in dict
    dict_algorithm['Ecin_stop'] = Ecin_stop

    #Trackers and add element in dict
    dict_tracker['Ecin'] = []
    dict_tracker['Force_applied'] = []
    dict_tracker['k0_xmin'] = []
    dict_tracker['k0_xmax'] = []
    dict_tracker['y_box_max'] = [dict_sample['y_box_max']]
    dict_tracker['Force_on_upper_wall'] = []

    dict_algorithm['i_DEM'] = - 1
    DEM_loop_statut = True
    simulation_report.write('\n')
    simulation_report.tic_tempo(datetime.now())

    #-----------------------------------------------------------------------------
    # DEM iteration
    #-----------------------------------------------------------------------------

    while DEM_loop_statut :
        # update element in dict
        dict_algorithm['i_DEM'] = dict_algorithm['i_DEM'] + 1

        for grain in dict_sample['L_g']:
            grain.init_f_control(dict_sollicitations)

        # Detection of contacts between grains
        if dict_algorithm['i_DEM'] % dict_algorithm['i_update_neighborhoods']  == 0:
            Contact.Update_Neighborhoods(dict_algorithm,dict_sample)
        Contact.Grains_Polyhedral_contact_Neighborhoods(dict_material,dict_sample)

        # Detection of contacts between grain and walls
        if dict_algorithm['i_DEM'] % dict_algorithm['i_update_neighborhoods']  == 0:
            Contact_gw.Update_wall_Neighborhoods(dict_algorithm, dict_sample)
        Contact_gw.Grains_Polyhedral_Wall_contact_Neighborhood(dict_material,dict_sample)

        #Compute contact interactions (g-g and g-w)
        for contact in dict_sample['L_contact']:
            if dict_algorithm['Spring_type'] == 'Ponctual':
                contact.DEM_2grains_Polyhedral_normal()
                contact.DEM_2grains_Polyhedral_tangential(dict_algorithm['dt_DEM'])
            elif dict_algorithm['Spring_type'] == 'Surface':
                contact.DEM_2grains_Polyhedral_normal_surface()
                contact.DEM_2grains_Polyhedral_tangential_surface(dict_algorithm['dt_DEM'])
            else :
                simulation_report.write('Spring type not available !')
                raise ValueError('Spring type not available !')
        for contact in dict_sample['L_contact_gw'] :
            if dict_algorithm['Spring_type'] == 'Ponctual':
                contact.DEM_gw_Polyhedral_normal()
                contact.DEM_gw_Polyhedral_tangential(dict_algorithm['dt_DEM'])
            else : #Surface must be coded for contact gw
                simulation_report.write('Spring type not available !')
                raise ValueError('Spring type not available !')

        #Move particles and trackers
        #Semi implicit euler scheme
        Ecin = 0
        Force_applied = 0
        for grain in dict_sample['L_g']:
            a_i = grain.f/grain.m
            v_i = grain.v + a_i*dict_algorithm['dt_DEM']
            dw_i = grain.mz/grain.inertia
            w_i = grain.w + dw_i*dict_algorithm['dt_DEM']
            grain.update_geometry_kinetic(v_i,a_i,w_i,dict_algorithm['dt_DEM']) #Move grains
            Ecin = Ecin + 0.5*grain.m*np.linalg.norm(grain.v)**2/len(dict_sample['L_g'])
            Force_applied = Force_applied + np.linalg.norm(grain.f)/len(dict_sample['L_g'])

        #Control the y_max to verify vertical confinement
        Owntools.Control_y_max_NR(dict_sample,dict_sollicitations)
        Owntools.Compute_k0(dict_sample,dict_sollicitations)
        #trackers
        dict_tracker['Ecin'].append(Ecin)
        dict_tracker['Force_applied'].append(Force_applied)
        dict_tracker['y_box_max'].append(dict_sample['y_box_max'])
        dict_tracker['Force_on_upper_wall'].append(dict_sollicitations['Force_on_upper_wall'])
        dict_tracker['k0_xmin'].append(dict_sample['k0_xmin'])
        dict_tracker['k0_xmax'].append(dict_sample['k0_xmax'])

        if dict_algorithm['i_DEM'] %dict_algorithm['i_print_plot'] == 0:
            print('\nPF '+str(dict_algorithm['i_PF'])+' -> i_DEM '+str(dict_algorithm['i_DEM']+1)+' / '+str(dict_algorithm['i_DEM_stop']+1)+' (max)')
            print('Ecin',int(Ecin),'/',int(dict_algorithm['Ecin_stop']),'('+str(int(100*Ecin/dict_algorithm['Ecin_stop'])),' %)')
            print('F_confinement',int(dict_sollicitations['Force_on_upper_wall']),'/',int(dict_sollicitations['Vertical_Confinement_Force']),'('+str(int(100*dict_sollicitations['Force_on_upper_wall']/dict_sollicitations['Vertical_Confinement_Force'])),' %)')

            Owntools.save_DEM_tempo(dict_algorithm,dict_sample,dict_sollicitations,dict_tracker)

            if dict_algorithm['Debug_DEM'] :
                Owntools.Debug_DEM_f(dict_algorithm, dict_sample)
                Write_txt(dict_algorithm,dict_sample)

        #-----------------------------------------------------------------------------
        # Stop conditions
        #-----------------------------------------------------------------------------

        if dict_algorithm['i_DEM'] >= dict_algorithm['i_DEM_stop'] :
            DEM_loop_statut = False
            print("DEM loop stopped by too many iterations.")
            simulation_report.write('/!\ End of DEM steps with '+str(dict_algorithm['i_DEM']+1)+' iterations / '+str(dict_algorithm['i_DEM_stop']+1)+'/!\ \n')
        if Ecin < dict_algorithm['Ecin_stop'] and dict_algorithm['i_DEM'] > dict_algorithm['n_window_stop'] and (dict_sollicitations['Vertical_Confinement_Force']*0.95<dict_sollicitations['Force_on_upper_wall'] and dict_sollicitations['Force_on_upper_wall']<dict_sollicitations['Vertical_Confinement_Force']*1.05):
            k0_xmin_window = dict_tracker['k0_xmin'][dict_algorithm['i_DEM']+1-dict_algorithm['n_window_stop']:dict_algorithm['i_DEM']+1]
            k0_xmax_window = dict_tracker['k0_xmax'][dict_algorithm['i_DEM']+1-dict_algorithm['n_window_stop']:dict_algorithm['i_DEM']+1]
            y_box_max_window = dict_tracker['y_box_max'][dict_algorithm['i_DEM']+1-dict_algorithm['n_window_stop']:dict_algorithm['i_DEM']+1]
            if max(k0_xmin_window) - min(k0_xmin_window) < dict_algorithm['dk0_stop'] and max(k0_xmax_window) - min(k0_xmax_window) < dict_algorithm['dk0_stop'] and max(y_box_max_window) - min(y_box_max_window) < dict_algorithm['dy_box_max_stop']:
                DEM_loop_statut = False
                print("DEM loop stopped by steady state reached.")
                simulation_report.write("DEM loop stopped by steady state reached with "+str(dict_algorithm['i_DEM']+1)+' iterations / '+str(dict_algorithm['i_DEM_stop']+1)+"\n")

    #-----------------------------------------------------------------------------
    # Debugging at the end of DEM step
    #-----------------------------------------------------------------------------

    if dict_algorithm['Debug'] :
        Owntools.Debug_configuration(dict_algorithm,dict_sample)
        Owntools.Debug_Trackers_DEM(dict_algorithm,dict_sollicitations,dict_tracker)
        Write_txt(dict_algorithm,dict_sample)
        Owntools.Plot_chain_force(dict_algorithm['i_PF'],dict_algorithm['i_DEM'])

        Owntools.save_DEM_final(dict_algorithm,dict_sample,dict_sollicitations,dict_tracker)

    #-----------------------------------------------------------------------------
    # Compute Vertical and horizontal sollicitations to compute k0
    #-----------------------------------------------------------------------------

    Owntools.Control_y_max_NR(dict_sample,dict_sollicitations)
    Owntools.Compute_k0(dict_sample,dict_sollicitations)

    simulation_report.write('k0_xmin : '+str(round(dict_sample['k0_xmin'],2))+' / k0_xmax : '+str(round(dict_sample['k0_xmax'],2))+'\n')

    #Update element in dict
    dict_tracker['k0_xmin_L'].append(dict_sample['k0_xmin'])
    dict_tracker['k0_xmax_L'].append(dict_sample['k0_xmax'])

    simulation_report.tac_tempo(datetime.now(),'DEM loop '+str(dict_algorithm['i_PF']))

    #-----------------------------------------------------------------------------
    # PF Simulation
    #-----------------------------------------------------------------------------

    simulation_report.tic_tempo(datetime.now())

    for grain in dict_sample['L_g']:
        if grain.dissolved :
            Create_i_AC_local(grain,dict_algorithm, dict_material, dict_sample,dict_sollicitations)
            os.system('mpiexec -n '+str(dict_algorithm['np_proc'])+' ~/projects/moose/modules/combined/combined-opt -i PF_'+str(dict_algorithm['i_PF'])+'_g'+str(grain.id)+'.i')
            j_str = Owntools.Sort_Files('PF_'+str(dict_algorithm['i_PF'])+'_g'+str(grain.id),dict_algorithm)
            grain.PFtoDEM_Multi_local('Output/PF_'+str(dict_algorithm['i_PF'])+'_g'+str(grain.id)+'/PF_'+str(dict_algorithm['i_PF'])+'_g'+str(grain.id)+'_other_'+str(j_str),dict_algorithm)
            grain.Geometricstudy_local(dict_geometry,dict_sample,simulation_report)

    #Geometric study
    S_grains = 0
    S_grains_dissolvable = 0
    for grain in dict_sample['L_g']:
        S_grains = S_grains + grain.surface
        if grain.dissolved :
            S_grains_dissolvable = S_grains_dissolvable + grain.surface
    simulation_report.write('Total Surface '+str(int(S_grains))+' µm2\n')
    simulation_report.write('Total Surface dissolvable '+str(int(S_grains_dissolvable))+' µm2\n')

    # Tracker
    dict_tracker['t_L'].append(dict_tracker['t_L'][-1] + dict_algorithm['dt_PF']*dict_algorithm['n_t_PF'])
    dict_tracker['S_grains_L'].append(S_grains)
    dict_tracker['S_dissolved_L'].append(dict_tracker['S_grains_L'][0]-S_grains)
    dict_tracker['S_dissolved_perc_L'].append((dict_tracker['S_grains_L'][0]-S_grains)/(dict_tracker['S_grains_L'][0])*100)
    dict_tracker['n_grains_L'].append(len(dict_sample['L_g']))
    dict_tracker['S_grains_dissolvable_L'].append(S_grains_dissolvable)
    dict_tracker['S_dissolved_perc_dissolvable_L'].append((dict_tracker['S_grains_dissolvable_L'][0]-S_grains_dissolvable)/(dict_tracker['S_grains_dissolvable_L'][0])*100)

    simulation_report.tac_tempo(datetime.now(),'PF iteration '+str(dict_algorithm['i_PF']))

    #-----------------------------------------------------------------------------
    # Reinitialisation of contact for the next step
    #-----------------------------------------------------------------------------

    for contact in dict_sample['L_contact']:
        contact.init_contact(dict_sample['L_g'])
    for contact in dict_sample['L_contact_gw']:
        contact.init_contact_gw(dict_sample['L_g'])

    #-----------------------------------------------------------------------------
    # Print Grains configuration
    #-----------------------------------------------------------------------------

    if dict_algorithm['Debug'] :
        Owntools.Debug_configuration(dict_algorithm,dict_sample)
        #Trackers
        Owntools.Debug_Trackers(dict_tracker)

    #-----------------------------------------------------------------------------
    # Save tempo
    #-----------------------------------------------------------------------------

    if dict_algorithm['SaveData'] :
        Owntools.save_tempo(dict_algorithm,dict_tracker)
        Owntools.save_dicts(dict_algorithm, dict_geometry, dict_material, dict_sample, dict_sollicitations, dict_tracker)
        shutil.copy('Debug/Report.txt','../'+dict_algorithm['main_folder_name']+'/Report_'+dict_algorithm['name_folder']+'_tempo.txt')