download
raw
8.72 kB
"""
Solveur FEniCS pour l'équation de Poisson
-Δu = f dans Ω
avec conditions de Dirichlet et/ou Neumann sur les bords
"""
import numpy as np
import time
from typing import Dict, Any, List, Tuple
import json
def solve_poisson(params: Dict[str, Any]) -> Dict[str, Any]:
"""
Résout l'équation de Poisson avec les paramètres donnés
Retourne:
- mesh_data: données du maillage pour visualisation 3D
- solution_data: valeurs de la solution aux noeuds
- animation_frames: frames pour l'animation
- statistics: min, max, temps de calcul
"""
import dolfinx as df
from mpi4py import MPI
from dolfinx.fem.petsc import LinearProblem
from petsc4py.PETSc import ScalarType
import ufl
start_time = time.time()
# Extraction des paramètres
domain = params['domain']
mesh_params = params['mesh']
source_params = params['source']
dirichlet_params = params['dirichlet']
neumann_params = params['neumann']
# 1. Création du maillage
nx, ny = mesh_params['nx'], mesh_params['ny']
msh = df.mesh.create_rectangle(
comm=MPI.COMM_WORLD,
points=[[domain['x_min'], domain['y_min']],
[domain['x_max'], domain['y_max']]],
n=[nx, ny],
cell_type=df.mesh.CellType.triangle,
)
# Espace de fonctions (Lagrange P1)
V = df.fem.functionspace(msh, ("Lagrange", 1))
# 2. Extraction des données du maillage pour la visualisation 3D
mesh_data = extract_mesh_data(msh, V)
# 3. Conditions aux limites de Dirichlet
bcs = []
if dirichlet_params['enabled']:
boundaries = dirichlet_params['boundaries']
x_min, x_max = domain['x_min'], domain['x_max']
y_min, y_max = domain['y_min'], domain['y_max']
def dirichlet_boundary(x):
on_boundary = np.zeros(x.shape[1], dtype=bool)
if 'left' in boundaries:
on_boundary |= np.isclose(x[0], x_min)
if 'right' in boundaries:
on_boundary |= np.isclose(x[0], x_max)
if 'bottom' in boundaries:
on_boundary |= np.isclose(x[1], y_min)
if 'top' in boundaries:
on_boundary |= np.isclose(x[1], y_max)
return on_boundary
facets = df.mesh.locate_entities_boundary(
msh,
dim=(msh.topology.dim - 1),
marker=dirichlet_boundary,
)
dofs = df.fem.locate_dofs_topological(V=V, entity_dim=1, entities=facets)
bc = df.fem.dirichletbc(value=ScalarType(dirichlet_params['value']), dofs=dofs, V=V)
bcs.append(bc)
# 4. Formulation variationnelle
u = ufl.TrialFunction(V)
v = ufl.TestFunction(V)
x = ufl.SpatialCoordinate(msh)
# Terme source
f = create_source_term(x, source_params)
# Mesures d'intégration
dx = ufl.Measure("dx", domain=msh)
ds = ufl.Measure("ds", domain=msh)
# Forme bilinéaire
a = ufl.inner(ufl.grad(u), ufl.grad(v)) * dx
# Forme linéaire (terme source)
L = ufl.inner(f, v) * dx
# Condition de Neumann si activée
if neumann_params['enabled']:
g = create_neumann_term(x, neumann_params)
L = L + ufl.inner(g, v) * ds
# 5. Résolution
# Essayer avec petsc_options_prefix (versions récentes de dolfinx)
# sinon sans (versions plus anciennes)
try:
problem = LinearProblem(
a, L,
bcs=bcs,
petsc_options_prefix="poisson",
petsc_options={
"ksp_type": "preonly",
"pc_type": "lu",
},
)
except TypeError:
# Version ancienne de dolfinx sans petsc_options_prefix
problem = LinearProblem(a, L, bcs=bcs)
uh = problem.solve()
computation_time = time.time() - start_time
# 6. Extraction des données de la solution
solution_values = uh.x.array.copy()
# 7. Création des frames d'animation (évolution progressive de la solution)
animation_frames = create_animation_frames(solution_values, num_frames=30)
# 8. Préparation des résultats
result = {
'mesh_data': mesh_data,
'solution_data': {
'values': solution_values.tolist(),
'min': float(solution_values.min()),
'max': float(solution_values.max()),
},
'animation_frames': animation_frames,
'statistics': {
'min': float(solution_values.min()),
'max': float(solution_values.max()),
'mean': float(solution_values.mean()),
'computation_time': computation_time,
'num_dofs': len(solution_values),
'num_elements': nx * ny * 2, # triangles
}
}
return result
def extract_mesh_data(msh, V) -> Dict[str, Any]:
"""Extrait les données du maillage pour la visualisation 3D"""
# Coordonnées des noeuds
geometry = msh.geometry.x
coordinates = geometry[:, :2].tolist() # x, y seulement
# Connectivité des éléments (triangles)
topology = msh.topology
topology.create_connectivity(2, 0) # faces vers sommets
cells = msh.geometry.dofmap
triangles = []
for i in range(cells.shape[0]):
triangles.append(cells[i].tolist())
# Calcul des arêtes pour le wireframe
edges = set()
for tri in triangles:
for i in range(3):
edge = tuple(sorted([tri[i], tri[(i+1) % 3]]))
edges.add(edge)
return {
'vertices': coordinates,
'triangles': triangles,
'edges': list(edges),
'num_vertices': len(coordinates),
'num_triangles': len(triangles),
'bounds': {
'x_min': float(geometry[:, 0].min()),
'x_max': float(geometry[:, 0].max()),
'y_min': float(geometry[:, 1].min()),
'y_max': float(geometry[:, 1].max()),
}
}
def create_source_term(x, params: Dict[str, Any]):
"""Crée le terme source selon le type choisi"""
import ufl
source_type = params['type']
amplitude = params['amplitude']
if source_type == 'gaussian':
cx, cy = params['center_x'], params['center_y']
width = params['width']
return amplitude * ufl.exp(-((x[0] - cx)**2 + (x[1] - cy)**2) / width)
elif source_type == 'constant':
return ufl.Constant(x.ufl_domain(), amplitude)
elif source_type == 'sinusoidal':
freq = params['frequency']
return amplitude * ufl.sin(freq * ufl.pi * x[0]) * ufl.sin(freq * ufl.pi * x[1])
else: # custom ou default
return amplitude * ufl.exp(-((x[0] - 0.5)**2 + (x[1] - 0.5)**2) / 0.02)
def create_neumann_term(x, params: Dict[str, Any]):
"""Crée le terme de Neumann selon le type choisi"""
import ufl
neumann_type = params['type']
amplitude = params['amplitude']
frequency = params['frequency']
if neumann_type == 'constant':
return ufl.Constant(x.ufl_domain(), amplitude)
elif neumann_type == 'sinusoidal':
return amplitude * ufl.sin(frequency * x[0])
elif neumann_type == 'linear':
return amplitude * x[0]
else:
return amplitude * ufl.sin(frequency * x[0])
def create_animation_frames(solution: np.ndarray, num_frames: int = 30) -> List[List[float]]:
"""
Crée des frames d'animation montrant l'évolution progressive de la solution
(simulation d'un processus de diffusion jusqu'à l'état stationnaire)
"""
frames = []
for i in range(num_frames):
# Facteur de progression (0 à 1)
t = i / (num_frames - 1)
# Interpolation de 0 vers la solution finale avec une courbe ease-in-out
ease_t = 0.5 * (1 - np.cos(np.pi * t))
frame_values = (solution * ease_t).tolist()
frames.append(frame_values)
return frames
def generate_preview_mesh(params: Dict[str, Any]) -> Dict[str, Any]:
"""
Génère uniquement les données du maillage pour un aperçu rapide
(sans résoudre l'équation)
"""
import dolfinx as df
from mpi4py import MPI
domain = params['domain']
mesh_params = params['mesh']
nx, ny = mesh_params['nx'], mesh_params['ny']
msh = df.mesh.create_rectangle(
comm=MPI.COMM_WORLD,
points=[[domain['x_min'], domain['y_min']],
[domain['x_max'], domain['y_max']]],
n=[nx, ny],
cell_type=df.mesh.CellType.triangle,
)
V = df.fem.functionspace(msh, ("Lagrange", 1))
return extract_mesh_data(msh, V)

Xet Storage Details

Size:
8.72 kB
·
Xet hash:
0ea99eaba5795eecea2a89d56b42172e217be288aae5a07e966b3e1b127935b8

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.