Skip to main content

fieldlines

Lignes de champ électrique et magnétique, et équipotentielles, pour des figures de cours : des charges, des fils et des plans chargés, des dipôles, des fils et des nappes de courant, des spires, des solénoïdes, des aimants, un champ uniforme. On les pose dans une carte, on choisit où partent les lignes, on retouche celles qui gênent en les désignant par leur nom, et on trace avec matplotlib.

import matplotlib.pyplot as plt
from fieldlines import Carte, Charge

carte = Carte([Charge(2, (-1, 0)), Charge(-1, (1, 0))], cadre=(-4, 4, -3, 3))
carte.lignes_selon_flux(nombre=16)

fig, ax = plt.subplots()
carte.trace(ax)  # lignes, flèches, charges
carte.trace_equipotentielles(ax, carte.niveaux(20, echelle="log", symetrique=True))
plt.show()

Le paquet s'installe depuis GitHub (ci-dessous). Pas de PyPI.

Sommaire

Ce qu'il fait

  • Des lignes intégrées sur le champ exact, pas interpolé sur une grille : chaque ligne suit scipy.integrate.solve_ivp et s'arrête sur un événement localisé exactement — arrivée sur une charge (la ligne la touche), sortie du cadre, ligne refermée (champ magnétique), point de champ nul.
  • Des départs qui ont un sens physique. Autour des charges à angles réguliers ; selon le flux, en nombre proportionnel à la charge et sans ligne tracée deux fois ; le long d'un segment, à flux égal dans la figure ou dans l'espace.
  • Des lignes nommées et retouchables : ajuste(12, fleches=[0.3, 0.8]), retire(range(23, 29)), des numéros qui ne bougent jamais.
  • Le champ magnétique à la hauteur du champ électrique : spire exacte (intégrales elliptiques), solénoïde, bobines de Helmholtz, aimant, nappe de courant, champ uniforme ; lignes fermées ; fonction de flux Ψ exacte, dont les lignes de niveau sont les lignes de champ, pour des tubes de flux égal.
  • Des sources personnalisées en une vingtaine de lignes (Source), pour un potentiel imposé ou un champ qui n'est celui d'aucune distribution connue.
  • Des unités réduites pour les lignes, et deux constantes pour revenir aux volts et aux teslas.

Installation

Avec pip depuis PyPI, quand le réseau et pip sont disponibles :

pip install fieldlines

(ou uv add fieldlines). Depuis GitHub, pour la version de développement :

pip install git+https://github.com/antnardo/fieldlines

Avec la roue d'une release, pour un poste qui a pip mais pas git : chaque release porte fieldlines-<version>-py3-none-any.whl (et le sdist .tar.gz, avec examples/), qui s'installe par pip install fieldlines-<version>-py3-none-any.whl.

Copier le dossier, sans pip ni réseau : le dossier src/fieldlines/ se pose à côté des scripts, et from fieldlines import Carte, Charge fonctionne, à condition d'exécuter le script depuis son dossier.

Pour développer, un clone et uv :

git clone https://github.com/antnardo/fieldlines
cd fieldlines && uv sync && uv run pytest

Prérequis

Python 3.11 ou plus récent, avec numpy (1.26 ou plus), scipy (1.11 ou plus) et matplotlib (3.8 ou plus). Le paquet est testé sous Python 3.11 et 3.14.

Prise en main

Carte(sources, cadre) prend la liste des sources et le cadre (xmin, xmax, ymin, ymax) de la figure ; une carte ne mêle pas sources électriques et magnétiques. Les positions sont des couples (x, y), les angles sont en radians, comptés depuis Ox.

On y place des lignes de quatre façons, qui se combinent :

  • lignes_autour(nombre, rayon, angle) : nombre lignes autour de chaque source où des lignes aboutissent (charges, fils chargés, dipôles), régulièrement espacées en angle (en sin θ pour un dipôle). Une ligne qui va d'une charge à une autre est tracée depuis chacune ;
  • lignes_selon_flux(nombre, rayon, angle) : à flux égal, en nombre proportionnel à la charge (nombre pour la plus forte), sans tracer deux fois la même ligne : autour d'une charge négative, on n'ajoute que les lignes qui ne lui sont pas déjà arrivées ;
  • lignes_le_long(debut, fin, nombre, repartition) : depuis un segment, à flux égal du champ de la figure ("flux"), à flux égal dans l'espace pour des sources de révolution ("flux_revolution"), ou à intervalles réguliers ("reguliere"). C'est ainsi qu'on place les lignes du champ magnétique, qui n'aboutissent nulle part ;
  • ajoute_ligne(depart, nom=None) : une ligne à la main.

Chaque ligne a un nom : un numéro, attribué dans l'ordre de création et jamais réutilisé, ou un texte choisi. trace_noms(ax) les écrit sur la figure ; on retouche ou retire ensuite par nom, autant de fois qu'on veut, et on relit ce qu'on a réglé :

carte.lignes_autour(nombre=16, rayon=0.2)
carte.ajuste(30, 38, fleches=[0.45])          # flèche à 45 % de la longueur visible
carte.ajuste(12, marge=2, style={"color": "r"})
carte.retire(range(23, 29), 47)
carte.options(12).fleches, carte.depart(12)   # les réglages et le départ de la ligne 12

Les réglages d'une ligne (Options, dans fieldlines.integration) :

Réglage Défaut Rôle
fleches (0.5,) positions des flèches, en fractions de la longueur visible ; une liste ou un seul nombre
marge 0.25 le calcul continue jusqu'à cette fraction du cadre au-delà de ses bords
sens "deux" "aval" ou "amont" pour ne suivre la ligne que d'un côté du départ
longueur_max 20 diagonales longueur au-delà de laquelle on abandonne
style {} arguments de Axes.plot propres à la ligne (ses flèches prennent sa couleur)

Le tracé :

Méthode Ce qu'elle trace
trace_lignes(ax, **style) les lignes (noires par défaut)
trace_fleches(ax, taille=8, style="fancy") les flèches, taille en points
trace_noms(ax) le nom de chaque ligne, pour choisir quoi retoucher
trace_equipotentielles(ax, niveaux, contraste=3, cmap="RdYlBu_r", norm=None) les équipotentielles (champ électrique)
trace_lignes_de_flux(ax, niveaux) les lignes de champ comme lignes de niveau de la fonction de flux
trace_sources(ax, taille=10) charges (aire ∝ |q|), dipôles, courants ⊙ et ⊗, plans, aimants
trace(ax) lignes, flèches et sources, cadre et repère orthonormé
enregistre(chemin, figsize=None) trace dans une figure neuve, enregistrée puis fermée

Les niveaux des équipotentielles viennent de carte.niveaux(nombre, echelle, symetrique, vmin, vmax, seuil, ajoute) : linéaires ou logarithmiques (echelle="log"), symétriques (± une progression de seuil × max(|V|) au maximum de |V|, seuil=1e-3 par défaut) ou non, entre les extrêmes du potentiel sur la grille ou entre des bornes données, plus des niveaux ajoutés. Sur une carte magnétique, niveaux porte sur la fonction de flux (grandeur="flux"), pour trace_lignes_de_flux. Les couleurs couvrent [Vmin, Vmax]/10^contraste ; trace_equipotentielles rend le QuadContourSet, à passer à ax.clabel pour écrire les valeurs, ou dont on réutilise le norm pour tracer une autre carte avec les mêmes couleurs.

Les valeurs elles-mêmes : carte.champ(x, y), carte.potentiel(x, y) et carte.flux(x, y) (vectorisés) ; carte.grille("potentiel") ou carte.grille("flux") sur une grille du cadre.

Les sources

Chaque source électrique a son symétrique magnétique quand il existe. Les formules sont en unités réduites (ci-dessous).

Électrique Magnétique Champ
Charge(q, position) — E = q/r², V = q/r
FilCharge(lineique, position) FilCourant(intensite, position) E = 2λ/r, V = −2λ ln r ; B = 2I/r, courant vers le lecteur si I > 0 (⊙)
PlanCharge(surfacique, position, angle) NappeCourant(surfacique, position, angle) E = 2πσ normal au plan ; B = 2πj parallèle à la nappe, opposé de part et d'autre
Dipole(moment, position, angle) DipoleMagnetique(moment, position, angle) (3 (p·r) r − p r²)/r⁵, dipôle ponctuel de l'espace
— Spire(intensite, centre, rayon, angle) exact, intégrales elliptiques ; vue par la tranche, angle : son axe
— Solenoide(intensite, centre, rayon, longueur, spires, angle) somme de spires, vu en coupe
— Aimant(aimantation, centre, rayon, hauteur, angle, spires=64) le solénoïde équivalent, dessiné avec ses pôles N et S
ChampUniforme(E=(Ex, Ey)) ChampUniforme(B=(Bx, By)) uniforme, s'ajoute aux autres sources

Fils, plans et nappes sont perpendiculaires à la figure (problème plan) ; spires, solénoïdes, aimants et dipôles sont dans un plan méridien qui contient leur axe. Un plan ou une nappe est la droite qui passe par position avec l'angle angle.

Une source personnalisée hérite de Source et donne son noyau vectorisé (lots) et ses singularités (singularites), plus potentiel, flux_plan ou flux_revolution si elle les a : fieldlines.sources en est la documentation, ChampUniforme le plus court modèle, et l'exercice EM-ES26 du cours (un potentiel imposé) un autre.

Unités réduites et valeurs SI

Le paquet calcule avec 1/(4π ε0) = 1 et μ0/(4π) = 1 : une charge q crée E = q/r², un fil parcouru par I crée B = 2I/r. Les lignes de champ et les équipotentielles ne dépendent pas de ce choix ; les valeurs, si. Ce que ax.clabel écrit sur les équipotentielles est q/r, pas des volts.

Pour revenir au SI, les charges en coulombs (λ en C/m, σ en C/m²), les courants en ampères (j et M en A/m) et les longueurs en mètres :

from fieldlines import K_COULOMB, MU0_SUR_4PI  # 1/(4π ε0) = 8,99·10⁹ ; μ0/(4π) = 10⁻⁷

ex, ey = carte.champ(x, y)
E_si = K_COULOMB * ex, K_COULOMB * ey          # V/m, et V = K_COULOMB * carte.potentiel(x, y)
bx, by = carte_magnetique.champ(x, y)
B_si = MU0_SUR_4PI * bx, MU0_SUR_4PI * by      # T
ax.clabel(contours, fmt=lambda v: f"{K_COULOMB * v:.2g} V")

E et B n'ont pas le même facteur : les cartes de ce paquet ne se comparent pas d'un champ à l'autre en valeur.

Recettes pour le champ magnétique

La convention d'axe. La figure est un plan méridien. L'axe d'une spire, d'un solénoïde ou d'un aimant passe par centre dans la direction angle (radians depuis Ox ; Ox par défaut, donc un axe horizontal) ; le courant tourne dans le sens direct autour de cet axe quand I > 0, et la spire perce la figure en ⊙ du côté de l'axe tourné de +90° (en haut pour un axe horizontal) et en ⊗ de l'autre. Pour un axe vertical, angle=math.pi / 2. Les fils et les nappes sont perpendiculaires à la figure, I > 0 vers le lecteur (⊙).

Où faire partir les lignes. Elles n'aboutissent nulle part : lignes_autour refuse, et lignes_le_long prend un segment que chaque ligne de la famille voulue coupe une fois, sans passer à moins du rayon d'arrêt d'une source.

Source Segment de départ Exemple
un fil du fil vers le bord, (r0, 0) → (R, 0) ; à flux égal, les rayons des cercles sont en progression géométrique fils_courants.py
plusieurs fils un segment par famille de lignes (autour d'un fil, autour de deux…), depuis le fil ou depuis un point de champ nul fils_courants.py, exercices EM-MS03 et EM-MS04
une spire son diamètre dans la figure, (0, −0.9 R) → (0, 0.9 R) ; "flux" suit l'intensité du champ dans le dessin, "flux_revolution" fait des tubes de flux égaux dans l'espace spire.py
un solénoïde son plan médian, à l'intérieur, (0, −0.95 R) → (0, 0.95 R) solenoide.py
des bobines de Helmholtz le plan médian entre les deux spires, "flux_revolution" bobines_de_helmholtz.py
un aimant son plan médian, à l'intérieur ; les lignes le traversent du pôle S au pôle N et se referment dehors aimant.py
un dipôle magnétique lignes_autour convient (les lignes sortent du dipôle et y rentrent), ou un segment sur l'équateur dipole_magnetique.py
un champ uniforme et un fil un segment que toutes les lignes coupent, l'axe vertical par exemple exercice EM-MS03

Un nombre pair de départs sur le diamètre d'une spire évite la ligne de l'axe, qui ne se referme qu'à l'infini. Un aimant est le solénoïde équivalent : Aimant(M, centre, rayon, hauteur, angle) vaut Solenoide(M * hauteur / spires, centre, rayon, hauteur, spires, angle), et se dessine en coupe avec ses pôles ; pour un aimant réel, M en A/m et B (T) = MU0_SUR_4PI × le champ calculé (exercice OO-EM65).

La fonction de flux. Pour des fils de courant (problème plan), Ψ = A_z ; pour des sources qui ont toutes le même axe de révolution (charges sur l'axe, dipôles, spires, solénoïdes et aimants alignés sur lui, champ uniforme parallèle), Ψ est la fonction de Stokes, ρ A_φ pour une spire. Les lignes de champ sont les lignes de niveau de Ψ : trace_lignes_de_flux(ax, niveaux) les trace directement et exactement, carte.niveaux(nombre, grandeur="flux") donne des niveaux, et lignes_le_long(..., repartition="flux_revolution") place les départs à ΔΨ égal (spire.py, panneau de droite).

Pièges

  • lignes_autour et lignes_selon_flux sur une carte magnétique lèvent ValueError : les lignes n'y aboutissent nulle part, prendre lignes_le_long ou ajoute_ligne.
  • lignes_le_long refuse un segment qui passe à moins du rayon d'arrêt d'une source (Reglages.arret × diagonale du cadre, 10⁻³ par défaut) : les lignes s'y arrêteraient aussitôt. Écarter le segment, ou réduire arret.
  • ajuste(nom, color="r") lève TypeError : un style de tracé se donne dans style={"color": "r"}. fleches=0.3 (un seul nombre) est accepté.
  • niveaux(..., symetrique=True, seuil=s) : seuil est une fraction de max(|V|), pas une valeur absolue. Un seuil absolu empilait des niveaux sur la surface V = 0.
  • Une carte ne mêle pas E et B : deux cartes, tracées sur les mêmes axes.
  • flux, trace_lignes_de_flux et repartition="flux_revolution" demandent une symétrie : fils de courant seuls, ou sources de même axe (dipôles et spires alignés sur lui, charges dessus). Sinon, ValueError.
  • Les valeurs sont en unités réduites : voir ci-dessus avant de lire un clabel.
  • lignes_autour trace deux fois chaque ligne qui va d'une charge à une autre ; les retirer par nom, ou prendre lignes_selon_flux.

Les modules

Module Contenu
fieldlines la tête de paquet : Carte, les sources, Source, Singularite, Noyau, Nature, K_COULOMB, MU0_SUR_4PI
fieldlines.carte Carte
fieldlines.sources les sources et leurs noyaux vectorisés, Lots, les constantes
fieldlines.champ Distribution (champ, potentiel, flux, symétrie, grilles), Grille
fieldlines.departs departs_autour, departs_le_long, CercleDeFlux, PlacementSelonFlux
fieldlines.integration Integrateur, Options, Reglages, LigneDeChamp, Arret
fieldlines.niveaux niveaux
fieldlines.trace les fonctions de tracé (lignes, flèches, noms, contours, sources)
fieldlines.geometrie Cadre, Axe, distance_au_segment

Le calcul des lignes

Chaque ligne est intégrée par scipy.integrate.solve_ivp, vers l'aval puis vers l'amont de son départ, et s'arrête sur un événement : sortie du cadre agrandi de sa marge, arrivée sur une source (la ligne finit alors au centre de la source), retour au point de départ (ligne fermée du champ magnétique), ou champ nul (point d'arrêt). Près d'une source ou d'un point d'arrêt, l'intégration ralentit pour ne pas les enjamber. La précision se règle par Carte(..., reglages=Reglages(...)) ; verbose=True écrit comment chaque ligne s'est arrêtée. Le champ se calcule par lots : un noyau numpy par sorte de source, pour tous les points et toutes les sources d'un coup.

Passer d'efieldlines à fieldlines

efieldlines fieldlines
Figure(charges, xmin, xmax, ymin, ymax) Carte(charges, cadre=(xmin, xmax, ymin, ymax))
Ligne(q, pos) FilCharge(q / 2, pos)
Dipole(p, pos, alpha=a) Dipole(p, pos, angle=math.pi / 2 - a)
set_default_SL_integration_parameters(...) inutile : réglages par défaut, ou par ligne
create_SL_with_automatic_starting_points(R, N) lignes_autour(nombre=N, rayon=R)
append_new_SL(x0, y0, arrow_positions=…, enlarge=c) ajoute_ligne((x0, y0), fleches=…, marge=(c - 1) / 2) (cadre centré en 0)
tweak_SL(i, ...) ajuste(i, ...)
remove_SL([i, j]) retire(i, j), sans décalage des numéros
compute_streamlines(), compute_arrows(...) inutiles : le calcul se fait au tracé
compute_potential(gridsize) inutile, ou trace_equipotentielles(..., points=gridsize)
set_automatic_levels(Nlevels, logscale, symmetric, Vmin, Vmax, deltaV) niveaux(nombre, echelle, symetrique, vmin, vmax, seuil), seuil relatif
set_levels(l), add_levels(l) une liste de niveaux, ou niveaux(..., ajoute=l)
plot_streamlines(ax), plot_arrows(ax) trace_lignes(ax), trace_fleches(ax)
display_streamline_identifier(ax) trace_noms(ax)
plot_contours(ax, contraste, cmap, scale) trace_equipotentielles(ax, niveaux, contraste, cmap, norm)
plot_contours_label(ax) ax.clabel(trace_equipotentielles(...))
plot_charges(ax, chargesize) trace_sources(ax, taille)

Les flèches se placent désormais sur la partie visible de la ligne, et leurs tailles sont en points : arrowsize, qui dépendait du cadre, disparaît.

Exemples

Le dossier examples/ reprend les figures du cours (cartes de champ de charges, de fils chargés, quadrupôle, approximation dipolaire, exercice EM-ES15), montre les départs selon le flux et une charge dans un champ uniforme, et ajoute le champ magnétique : fils, spire, bobines de Helmholtz, solénoïde, aimant, dipôle magnétique. Chaque script enregistre sa figure en PDF à côté de lui.

Tester

uv run --python 3.11 pytest
uv run --python 3.14 pytest
uv run ruff check src tests examples && uv run ruff format --check src tests examples
uv run mypy
uv build

Les tests confrontent les champs aux formules (Coulomb, fil infini, nappe de fils, axe d'une spire, loi de Biot et Savart, dipôle lointain, solénoïde, aimant) et vérifient les propriétés des lignes : tangentes au champ, d'une charge positive à une négative, potentiel décroissant, lignes du champ magnétique fermées et à flux constant. L'intégration continue (.github/workflows/ci.yml) rejoue tout cela à chaque push sous Python 3.11 et 3.14 ; à chaque release publiée, publish.yml vérifie que l'étiquette est la version du paquet et joint la roue et le sdist à la release.

Antériorité

La méthode — suivre pas à pas la direction du champ depuis un point de départ — vient du billet de H. Bauke, Visualizing streamlines (numbercrunch.de, 2013). Le paquet reprend et développe efieldlines, module écrit en 2017 à partir de ce billet pour les figures du cours d'électromagnétisme, et y ajoute le champ magnétique.

matplotlib.streamplot trace des lignes interpolées sur une grille, brisées, avec les flèches près des départs ; rien n'y atteint exactement une charge et une boucle y est reparcourue. magpylib calcule le champ d'aimants et de bobines en trois dimensions (sa spire et son solénoïde coïncident avec ceux de ce paquet à 10⁻¹⁰ près) mais trace par streamplot ; il est le bon outil pour un aimant réel ou du 3D. Le placement des lignes selon le flux est propre au paquet : il ne reprend pas le code du paquet electrostatics de T. J. Duck (GPL), qui résout le même problème autrement. Sans équivalent ailleurs : les départs selon le flux sans doublon, l'arrêt exact, les flèches à une fraction de la longueur visible, la fonction de Stokes, la retouche par nom.

Licence

Le code — le paquet (src/), les tests et les scripts — est sous licence MIT (LICENSE). Les exemples (examples/), qui sont du contenu scientifique et pédagogique plutôt que des algorithmes, sont sous licence Creative Commons Attribution - Pas d'utilisation commerciale - Partage dans les mêmes conditions 4.0 International, CC BY-NC-SA 4.0 (LICENSE-CC-BY-NC-SA-4.0).

Metadata

Release files for fieldlines 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fieldlines 0.1.1
File Size Uploaded
fieldlines-0.1.1.tar.gz 56.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fieldlines 0.1.1
File Interpreter ABI Platform
fieldlines-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 112.0 kB

Release files / fieldlines-0.1.1.tar.gz

Download URL fieldlines-0.1.1.tar.gz
Size 56.6 kB
Tags Source
SHA-256 checksum
How to use checksums
ac4d650c0f73eacfdb20bd4df17c18d89de3e9f2837d6670a8089a88e4a97647
BLAKE2b-256 checksum
How to use checksums
96686845f65ffb9a18b057e54dc460f2df5b9de54cb6e920c24c3b2ec5df1a8b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / fieldlines-0.1.1-py3-none-any.whl

Download URL fieldlines-0.1.1-py3-none-any.whl
Size 55.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c2378af2a94be1e0c5ea1e51e70897764f975c5e0eb20df5267e7a99ed3d9db0
BLAKE2b-256 checksum
How to use checksums
d90d877e772850cb00b1aff15b9cfcc78ec3b4d08a40cbae371fa8d01efa08c4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.2

2 release files

This release

0.1.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page