#!/usr/bin/env python
# coding: utf-8

# <center>
# <h1>Étude du champ électrique créé par des charges électriques ponctuelles et d'en déduire les caractéristiques du champ électrique créé.</h1>
# </center>
# <hr />
# 
# > L'objectif de cette séance est d'étudier l'allure des lignes de champ créées par une ou plusieurs charges électriques en fonction de leurs caractéristiques.
# 
# 
# ## Rappels
# 
# - Un **champ** est une grandeur physique définie en tout point de l'espace. Cette grandeur peut aussi dépendre du temps.
# 
# - Un **champ** peut être **scalaire** : *On associe un **nombre** à chaque point de l'espace*.
#     **Exemples**
#     - Champ des températures de l'atmosphère dans une pièce,
#     - Champ des pressions en chaque point d'une zone de l'atmosphère, 
#     - etc.
# 
# - Un **champ** peut être **vectoriel** : *On associe un **vecteur** à chaque point de l'espace*.  
#     **Exemples**
#     - Champ des vitesses des particules de fluide dans une zone de l'atmosphère,
#     - Champ électrique créé par une charge électrique,
#     - Champ gravitationnel créé par une masse,
#     - etc.
# 
# - Une **ligne de champ vectoriel** est une *ligne tangente en chacun de ses points au vecteur champ*. Elle est *orientée par une flèche dans le même sens que celui du champ*.
# 
# - Un **champ uniforme** est un *champ dont les caractéristiques (direction, sens et valeur) ne dépendent pas du point de l’espace considéré*.  
# *Les lignes de champ d'un champ uniforme sont toutes parallèles entre elles.
# 
# 
# ## Travail à réaliser
# 
# 1. Repérer dans le code du programme ci-dessous, l'instruction qui **crée** la charge électrique utilisée dans le programme.
# <details>
# <summary>Réponse</summary>
# c = Charge(1, 0, 0)
# </details>
# 
# 2. À partir des informations tirées de cette instruction, donner toutes les caractéristiques de la charge électrique créée.  
# Dire en particulier si cette charge est positive ou négative.
# <details>
# <summary>Réponse</summary>
# Valeur de la charge : q = 1 C ; la charge électrique est positive.
# Abscisse : x = 0
# Ordonnée : y = 0
# </details>
# 
# 3. Éxécuter le code. Observer le résultat à la fin du document.
# 
# 4. Le champ électrique créé par une charge électrique est-il **scalaire** ou **vectoriel** ? Pourquoi ?
# <details>
# <summary>Réponse</summary>
# Les lignes de champ sont orientées, il s'agit d'un champ vectoriel.
# </details>
# 
# 5. Donner toutes les caractéristiques du champ électrique créé par une **charge électrique ponctuelle positive**.
# <details>
# <summary>Réponse</summary>
# - Le champ électrique est radial : quel que soit le point envisagé, sa direction est une droite qui passe par le point où est située la charge électrique.
# - Le sens du champ électrique est tel que le vecteur s'éloigne de la charge électrique.
# </details>
# 
# 6. Modifier l'instruction qui crée la charge électrique : la charge doit désormais se trouver au point de coordonnées (3, -3). Visualiser le résultat.
# <details>
# <summary>Réponse</summary>
# c = Charge(1, 3, -3)
# </details>
# 
# 7. Modifier l'endroit de l'espace où se trouve la charge modifie-t-il les caractéristiques du champ électrique ?
# <details>
# <summary>Réponse</summary>
# Non. Le champ est toujours radial et quitte la charge.
# </details>
# 
# 8. Replacer la charge au point de coordonnées (0,0) et lui attribuer une valeur de charge opposée.
# <details>
# <summary>Réponse</summary>
# c = Charge(-1, 0, 0)
# </details>
# 
# 9. Donner toutes les caractéristiques du champ électrique créé par une **charge électrique ponctuelle négative**.
# <details>
# <summary>Réponse</summary>
# - Le champ électrique est radial : quel que soit le point envisagé, sa direction est une droite qui passe par le point où est située la charge électrique.
# - Le sens du champ électrique est tel que le vecteur est dirigé vers la charge électrique.
# </details>
# 
# 10. Déplacer la charge au point de coordonnées (3,0) et créer une seconde charge de valeur opposée au point de coordonnées (-3,0).  
# Penser à utiliser un nom de variable différent de `c` pour la seconde charge et ne pas oublier de l'ajouter à la liste des charges électriques lc.
# <details>
# <summary>Réponse</summary>
# c = Charge(-1, 3, 0)
# c1 = Charge(1, -3, 0)
# lc.append(c)
# lc.append(c1)
# </details>
# 
# 11. Que peut-on dire des champs créés par différentes charges électriques ?
# <details>
# <summary>Réponse</summary>
# Les champs créés par différentes charges électriques s'ajoutent. Les vecteurs champ s'ajoutent.
# </details>
# 
# 12. Donner maintenant la même valeur aux deux charges électriques. Expliquer l'allure des lignes de champ dans le plan défini par $x=0$.
# <details>
# <summary>Réponse</summary>
# Les champs créés par les deux charges électriques s'ajoutent et s'annulent dans le plan défini par $x=0$.
# </details>
# 
# 13. Ajouter plusieurs charges électriques et examiner la cartographie du champ.
# 
# 14. Remplacer tout le code de création des charges électriques par celui ci-dessous :
# ```python
# nbre_charges = 20
# dx = 0.5
# for i in range(nbre_charges):
#     c = Charge(1, i * dx - 5, 3)
#     lc.append(c)
#     c = Charge(-1, i * dx - 5, -3)
#     lc.append(c)
# ```
# Comment peut-on qualifier le champ entre les charges électriques ?
# <details>
# <summary>Réponse</summary>
# Les lignes de champ entre les charges électriques sont toutes parallèles. Le champ électrique est uniforme.
# </details>
# 

# # Programme Python
# **Imports des modules**
from __future__ import annotations
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle

# **Création d'un objet "Charge" auquel on pourra attribuer une valeur et une position**
#    Attributs :
#      q : valeur de la charge
#      x : abscisse du point où se trouve la charge
#      y : ordonnée du point où se trouve la charge
class Charge:
    def __init__(self: Charge, q: float, x: int = 0, y: int = 0) -> None:
        self.q = q
        self.x = x
        self.y = y

# **Création d'un objet "Champ"**
#    Attributs :
#      ex : composante du champ selon l'axe (Ox)
#      ey : composante du champ selon l'axe (Oy)
class Champ:
# Méthode : On ajoute la contribution d'une charge au champ
#   1. Création d'un champ vide  
    def __init__(self: Champ, nbre_x: int, nbre_y: int) -> None:
        self.ex = np.zeros((nbre_x, nbre_y))  # composante x du champ
        self.ey = np.zeros((nbre_x, nbre_y))  # composante y du champ
# Calcul du champ créé par la charge c en tout point
    def calcule(self: Champ, c: Charge, xx: np.ndarray, yy: np.ndarray) -> None:
        k = 1
        r = np.sqrt((xx - c.x)**2 + (yy - c.y)**2)
        self.ex += k * c.q * (xx - c.x) / r**3
        self.ey += k * c.q * (yy - c.y) / r**3

# **Création de l'espace : grille de points**
nbre_x, nbre_y = 200, 200    # nbre points selon chaque axe
x_min, x_max = -10, 10       # valeurs extrêmes selon x
y_min, y_max = -10, 10       # valeurs extrêmes selon y
x = np.linspace(x_min, x_max, nbre_x)  # abscisses
y = np.linspace(y_min, y_max, nbre_y)  # ordonnées
xx, yy = np.meshgrid(x, y)  # tableaux abscisses et ordonnées

# **Création d'un champ vide**
E = Champ(nbre_x, nbre_y)

# **Création de la liste des charges électriques**
lc = []  # Aucune charge pour l'instant

# **Définition des charges utilisées dans le programme et influence de ces dernières sur le champ**
c = Charge(1, 0, 0)

# Ajout de la charge à la liste des charges
lc.append(c)

# **Mise à jour du champ**
for charge in lc:                 # Pour chaque charge dans la liste
    E.calcule(charge, xx, yy)     # on calcule le champ qu'elle crée

# **Création du graphique**
fig = plt.figure(figsize=(8, 8), dpi=100)
ax = fig.add_subplot(111)

# Tracé des lignes de champ
color = 2 * np.log(np.hypot(E.ex, E.ey))
ax.streamplot(x, y, E.ex, E.ey, 
              color=color, linewidth=1, cmap=plt.cm.inferno,
              density=2, arrowstyle='->', arrowsize=1.5)

# Charges : '#aa0000' positive, '#0000aa' négative
couleurs_charges = {True: '#aa0000', False: '#0000aa'}
for c in lc:  # pour chaque charge dans la liste des charges
    ax.add_artist(Circle((c.x, c.y), 0.1, color=couleurs_charges[c.q>0]))

ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_aspect('equal')
plt.show()
