NSI :TP simulation numérique de percolation

1. Le principe de la percolation en physique.

2. Mise en place de la recherche des amas et de l’existence ou non de la percolation

Proposition de correction pour la recherche d’amas :

from random import *
import matplotlib.pyplot as plt
import numpy as np

def grille(largeur,hauteur,densite):
    '''
    génère une grille sous la forme d'un tuple de tuples
    de largeur et hauteur données
    chaque élément de la grille a la probabilité densité
    d'être plein
    largeur : int
    hauteur : int
    densite : float
    return : tuple
    exemple : grille(10,10,0.1) retournera une grille aléatoire
    du type
    ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
    (0, 1, 0, 0, 0, 0, 0, 0, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
    (0, 0, 0, 0, 0, 1, 0, 0, 0, 1),
    (0, 0, 0, 0, 0, 0, 0, 0, 1, 0),
    (0, 0, 1, 0, 0, 0, 0, 1, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 0, 1, 0),
    (0, 0, 0, 1, 0, 1, 0, 0, 0, 1),
    (0, 1, 0, 0, 0, 0, 0, 1, 0, 0))
    '''
    grille=()
    for j in range(hauteur):
        ligne=()
        for i in range(largeur):
            if random()<densite:
                ligne+=(1,)
            else :
                ligne+=(0,)
        grille+=(ligne,)
    return grille
                
def frequence(grille):
    '''
    retourne la frequence des 1 présents dans la grille
    grille : tuple de tuples
    return : float
    >>> g=((0, 0, 0, 0, 0, 0, 1, 0, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 1, 0, 1),
    (0, 0, 0, 1, 1, 0, 0, 0, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 1, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
    (0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    >>>print(frequence(g))
    0.06
    '''
    compteur=0
    for j in range(len(grille)):
        for i in range(len(grille)):
            if grille[j][i]==1:
                compteur+=1
    return compteur/((i+1)*(j+1))

def afficher(grille):
    '''
    affichage de la grille
    '''
    hauteur=len(grille)
    largeur=len(grille[0])
    grille_a_afficher=np.zeros((largeur,hauteur))
    for i in range(hauteur):
        for j in range(largeur):
            grille_a_afficher[j,i]=grille[j][i]    
    plt.spy(grille_a_afficher)
    plt.show()

def afficher_amas(grille,mes_amas):
    hauteur=len(grille)
    largeur=len(grille[0])
    grille_a_afficher=np.zeros((largeur,hauteur))
    for i in range(hauteur):
        for j in range(largeur):
            if not(cellule_pas_dans_mes_amas(mes_amas,i,j)):
                grille_a_afficher[j][i]=1
    plt.spy(grille_a_afficher)
    plt.show()
    
    
    
def amas(grille):
    '''
    détermine les amas de cases vides de la grille
    en partant du haut et de manière ordonnée
    grille : tuple de tuples contenant des 0 et des 1
    return : tuple constitué de tuples contenant les coordonnées
    des cases formant un même amas
    >>>g=(
    (0, 0, 1, 0, 0, 0, 1, 0, 1, 1),
    (1, 1, 0, 1, 0, 0, 1, 0, 0, 1),
    (0, 0, 1, 1, 1, 1, 0, 1, 0, 1),
    (1, 0, 1, 1, 1, 0, 0, 1, 0, 0),
    (0, 1, 1, 1, 0, 1, 0, 1, 1, 0),
    (1, 1, 0, 0, 0, 1, 1, 0, 0, 0),
    (0, 1, 0, 1, 0, 0, 1, 0, 0, 1),
    (1, 0, 1, 0, 0, 1, 0, 1, 0, 0),
    (0, 0, 0, 1, 1, 0, 0, 1, 1, 0),
    (1, 1, 1, 0, 0, 0, 0, 1, 0, 0))
    >>> amas(grille)
    (((0,0),(1,0)),
    ((3,0),(4,0),(5,0),(4,1),(5,1)),
    ((7,0),(7,1),(8,1),(8,2),(8,3),(9,3),
    (9,4),(7,5),(8,5),(9,5),(7,6),(8,6),
    (8,7),(9,7),(9,8),(8,9),(9,9)))
    '''
    mes_amas=()
    for i in range(len(grille[0])):
        if grille[0][i]==0 and cellule_pas_dans_mes_amas(mes_amas,i,0):
            amas=((i,0),)
            frontiere=bord_cellules_amas(amas,grille,mes_amas)
            while len(frontiere)!=0:
                amas+=frontiere
                frontiere=bord_cellules_amas(frontiere,grille,amas)
            mes_amas+=(amas,)
    
    
    return mes_amas

def cellule_pas_dans_amas(mon_amas,x,y):
    '''
    détermine si une case appartient à un amas
    : mes_amas : tuple de tuples contenant nos amas
    : x,y : int coordonnées de la case
    : return : bolléen
    >>> amas=(
    ((0,0)),
    ((3,0),(4,0),(4,1))
    )
    >>> cellule_pas_dans_amas_finaux(amas,4,0)
    False
    >>> cellule_pas_dans_amas_finaux(amas_finaux,3,1)
    True
    '''
    if (x,y) in mon_amas :
            return False
    return True

def cellule_pas_dans_mes_amas(mes_amas,x,y):
    '''
    détermine si une case appartient à un ensemble d'amas
    : mes_amas : tuple de tuples contenant nos amas
    : x,y : int coordonnées de la case
    : return : bolléen
    >>> amas=(
    ((0,0)),
    ((3,0),(4,0),(4,1))
    )
    >>> cellule_pas_dans_amas_finaux(amas,4,0)
    False
    >>> cellule_pas_dans_amas_finaux(amas_finaux,3,1)
    True
    '''
    for amas in mes_amas:
        if (x,y) in amas :
            return False
    return True
               
            
def bord_cellules_amas(amas,grille,mes_amas):
    '''
    détermine les cellules adjacentes l'amas
    et n'appartenant pas à cet amas en construction et aux
    amas déjà déterminés
    : amas : tuple
    : grille : tuple
    : amas : tuple
    : return : tuple contenant les coordonnées des cellules voisines d'un amas
    >>>g=(
    (0, 0, 1, 0, 0, 0, 1, 0, 1, 1),
    (1, 1, 0, 1, 0, 0, 1, 0, 0, 1),
    (0, 0, 1, 1, 1, 1, 0, 1, 0, 1),
    (1, 0, 1, 1, 1, 0, 0, 1, 0, 0),
    (0, 1, 1, 1, 0, 1, 0, 1, 1, 0),
    (1, 1, 0, 0, 0, 1, 1, 0, 0, 0),
    (0, 1, 0, 1, 0, 0, 1, 0, 0, 1),
    (1, 0, 1, 0, 0, 1, 0, 1, 0, 0),
    (0, 0, 0, 1, 1, 0, 0, 1, 1, 0),
    (1, 1, 1, 0, 0, 0, 0, 1, 0, 0))
    >>> mes_amas=(
    ((0,0),(1,0)),
    ((3,0),(4,0)),
    )
    >>> amas=(
    (5,0),(4,1)
    )
    >>> print(bord_cellules_amas(cases,g,mes_amas))
    ((5,1),)
    >>>>>> amas=(
    (7,0),(1,0)
    )
    >>> print(bord_cellules_amas(cases,g,mes_amas))
    ((7,1),)
    '''
    retour=()
    for case in amas :
        x=case[0]
        y=case[1]
        if x<len(grille[0])-1:
            if grille[y][x+1]==0 and cellule_pas_dans_amas(mes_amas,x+1,y) and cellule_pas_dans_mes_amas(mes_amas,x+1,y):
                retour+=((x+1,y),)
        if x>0 :
            if grille[y][x-1]==0 and cellule_pas_dans_amas(mes_amas,x-1,y) and cellule_pas_dans_mes_amas(mes_amas,x-1,y):
                retour+=((x-1,y),)
        if y>0 :
            if grille[y-1][x]==0 and cellule_pas_dans_amas(mes_amas,x,y-1) and cellule_pas_dans_mes_amas(mes_amas,y-1,y):
                retour+=((x,y-1),)
        if y<len(grille)-1:
            if grille[y+1][x]==0 and cellule_pas_dans_amas(mes_amas,x,y+1) and cellule_pas_dans_mes_amas(mes_amas,x,y+1):
                retour+=((x,y+1),)
    return on_enleve_les_doublons(retour)

def on_enleve_les_doublons(mon_tuple):
    '''
    retourne un tuple sans éléments en plusieurs exemplaires
    : mon_tuple : tuple
    : retour : tuple
    >>> mon_tuple=((1,1),(0,1),(1,1),(2,1))
    >>> print(on_enleve_les_doublons(mon_tuple))
    ((1,1),(0,1),(2,1))
    '''
    retour =()
    for elt in mon_tuple :
        if not(elt in retour):
            retour+=(elt,)
    return retour


g=grille(10,10,0.4)
afficher(g)
afficher_amas(g,amas(g))

3. Il ne reste plus qu’à mettre en place une fonction permettant de déterminer si il y a percolation ou non de notre milieu.

def percolation(grille,mes_amas):
    '''
    détermine si un mas présent dans les amas réalise
    la percolation
    : grille : tuple de tuples
    : mes_amas : tuple de tuples
    : return : True si il y a percolation, False sinon
    >>> g=((1, 0, 1, 1, 0, 1, 0, 1, 0, 1),
    (0, 1, 1, 0, 1, 0, 1, 1, 1, 0),
    (0, 0, 0, 0, 0, 1, 0, 0, 1, 0),
    (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
    (1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
    (0, 1, 0, 0, 0, 0, 1, 0, 1, 0),
    (1, 1, 0, 0, 0, 1, 1, 0, 0, 0), 
    0, 0, 1, 0, 0, 0, 1, 1, 0, 0),
    (1, 0, 1, 0, 0, 1, 0, 1, 1, 0),
    (0, 1, 0, 0, 1, 1, 1, 1, 0, 1))
    >>> amas=(((1, 0),), ((4, 0),), ((6, 0),), ((8, 0),))
    >>> print(percolation(grille,mes_amas))
    True
    >>>g=((0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 
    (0, 1, 0, 0, 1, 0, 1, 0, 0, 0), 
    (0, 0, 1, 1, 0, 0, 0, 1, 0, 0), 
    (0, 0, 1, 1, 1, 1, 1, 0, 1, 0), 
    (1, 0, 0, 1, 0, 0, 1, 0, 1, 1), 
    (1, 0, 0, 1, 0, 0, 1, 1, 0, 1), 
    (1, 1, 1, 0, 1, 0, 0, 0, 0, 0), 
    (0, 1, 0, 1, 0, 0, 0, 1, 1, 0), 
    (1, 0, 0, 0, 0, 0, 1, 1, 0, 0), 
    (0, 0, 1, 1, 1, 0, 0, 0, 0, 0))
    >>> amas=(((0, 0), (1, 0), (0, 1), (2, 0), (0, 2), (3, 0), (2, 1), (1, 2), (0, 3), (4, 0), (3, 1), (1, 3), (5, 0), (1, 4), (6, 0), (5, 1), (2, 4), (1, 5), (7, 0), (5, 2), (2, 5), (8, 0), (7, 1), (6, 2), (4, 2), (9, 0), (8, 1), (9, 1), (8, 2), (9, 2), (9, 3)),)
    >>> print(percolation(grille,mes_amas))
    False
    '''

4. Dictionnaires et étude statistiques de phénomène de percolation.

Enregistrer le programme précédent dans un répertoire dans votre espace personnel avec le nom « percolation.py ».
Pour réaliser l’étude statistiques d’un certain nombre d’expériences, on va créer de nouvelles fonctions dans un nouveau programme.
On va importer dans celui-ci les fonctions de notre module « percolation.py ».
Ouvrir un nouveau fichier python et le nomme « simulation_experiences.py ».
Ajouter le code ci-dessous en début de programme

# on importe le module percolation avec le prefixe perco
import percolation as perco
# on peut utiliser les fonctions du module perco en les préfixant par perco
g=perco.grille(10,10,0.8)
perco.afficher(g)

On va s’intéresser à présent aux résultats de nos simulations de percolation.
Pour cela, on va utiliser un dictionnaire pour stocker les données de nos expériences sous la forme :
{‘dimensions’: (10, 10), ‘densité’: 0.5, ‘fréquence’: 0.43, ‘percolation’: False}

Recopier et compléter la fonction ci-dessous :

def experience(largeur,hauteur,densite):
    '''
    donne le résultat de l'expérience sous la forme d'un dictionnaire
    : largeur: int
    : hauteur : int
    : densité : float entre 0 et 1
    '''
    g=perco.grille(largeur,hauteur,densite)
    amas=perco.amas(g)
    retour=dict()
    retour["dimensions"]=
    retour["densité"]=
    retour["fréquence"]=
    retour["percolation"]=
    return retour

On va à présent simuler un grand nombre d’expériences de même taille et de même densité, pour étudier la fréquence de percolation en fonction de ces paramètres.
On va également faire appel à un dictionnaire pour avoir un résultat du type, pour 10 expériences :
{‘taille’: (10, 10), ‘densité’: 0.5, 0: True, 1: False, 2: True, 3: False, 4: False, 5: False, 6: True, 7: False, 8: False, 9: False}

Recopier et compléter la fonction suivante:

def serie_experiences(largeur,hauteur,densite,nombre):
    '''
    simule le nombre d'expériences désiré et renvoie le
    resultat de la percolation pour chaque experience.
    Le resultat est stocké sous la forme d'un dictionnaire.
    largeur : int
    hauteur : int
    densite : float entre 0 et 1
    nombre :int
    retour : dict avec pour clés le numero de l'expérience et valeurs le résultat de la percolation
    '''
    retour={}
    retour["taille"]=
    retour["densité"]=
    for i in range(nombre):
        resultat_exp=experience(largeur,hauteur,densite)
        retour[i]=
    return retour

On va déterminer à présent la fréquence de percolations sur notre échantillon d’expériences

Recopier et compléter la fonction suivante:

def frequence(serie):
    '''
    retourne le nombre de valeurs True présenter dans le dictionnaire
    serie : dict
    >>>d={'taille': (10, 10), 'densité': 0.5, 0: False, 1: True, 2: False, 3: True, 4: False, 5: False, 6: False, 7: False, 8: False, 9: False}
    >>>print(frequence(d))
    0.2
    '''

Un dernier effort

Il ne reste plus qu’à définir une fonction qui permettra de déterminer plus ou moins précisément le seuil de percolation ( c’est à dire la valeur approximative de la densité qui fait passer de la non percolation à la percolation ) en fonction de la dimension.
Pour cela, on va effectuer un balayage des densités de 0 à 1 avec par exemple un pas de 0.1.
On renverra le résultats sous la forme d’un dictionnaire :

Par exemple :
{‘taille’: (10, 10), « nombre d’expériences »: 100, ‘densité= 0’: 1.0, ‘densité= 0.1’: 1.0, ‘densité= 0.2’: 0.99, ‘densité= 0.30000000000000004’: 0.9, ‘densité= 0.4’: 0.52, ‘densité= 0.5’: 0.14, ‘densité= 0.6’: 0.01, ‘densité= 0.7’: 0.0, ‘densité= 0.7999999999999999’: 0.0, ‘densité= 0.8999999999999999’: 0.0, ‘densité= 0.9999999999999999’: 0.0}
Nous permet de dire que le seuil de percolation pour une dimension (10,10) est proche de 0.4

On pourra améliorer la lisibilité des résultats par une fonction affichage et un éventuel graphique.