Coverage for pyxfem/solidmechanics/boundary_conditions.py: 37%
84 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-24 07:17 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-24 07:17 +0000
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3"""
4Created on April 10th 07:43:08 2024
6@author: nchevaug
7"""
8from __future__ import annotations
9from warnings import warn
10from collections.abc import Iterable
11from typing import Callable, Mapping, Dict
12import numpy as np
13from pyxfem.core.config import FloatArray, IndexArray, IndexType
14import pyxfem.core.function_spaces as fs
15import pyxfem.core.mesh_simplex_one_level as sm
16import pyxfem.core.quadrature as quad
18def get_space_entity(edge_groupe_names:Iterable[str],
19 mesh:sm.SMesh)-> Dict[str, IndexArray]:
20 ''' get the space's edge id corresponding to the edge groupe name'''
21 edge_groupe_names2eid: Dict[str, IndexArray] = {}
22 for name in edge_groupe_names:
23 edge_groupe_names2eid[name] = mesh.get_edgegroupe(name)[0]
24 return edge_groupe_names2eid
26def get_dofs_and_interpolation_points(
27 mesh: sm.SMesh,
28 edge_groupe_names: Iterable[str],
29 space)->tuple[Dict[str, IndexArray], Dict[str, FloatArray]]:
30 ''' get the space's degree of freedom id and the corresponding interpolation point
31 corresponding to the edge_groupe_names (alist of names). Usefull for Dirichlet BC for example'''
32 groupe_name2dofid:Dict[str, IndexArray]= {}
33 groupe_name2dofxy:Dict[str, FloatArray] = {}
34 func_name = get_dofs_and_interpolation_points.__name__
35 # if space is not None :
36 # interpat = space.interpolatoryAt
37 # vid2dofid = space.vertexid2dofid
38 # if vid2dofid is None and interpat=='vertex':
39 # raise ValueError()
40 for phys_name in edge_groupe_names:
41 eid, ___ = mesh.get_edgegroupe(phys_name)
42 if eid.size == 0:
43 warn('In '+func_name+' no edge found in groupe '+phys_name+'.')
44 if space.interpolatoryAt == 'vertex':
45 vertices = np.unique((mesh.e2v()[eid]).flatten())
46 coord = mesh.vcoord()[vertices]
47 dofsid = space.vertexid2dofid(vertices)
48 groupe_name2dofid[phys_name] = dofsid
49 groupe_name2dofxy[phys_name] = coord #.reshape((-1, 2))
50 elif space.interpolatoryAt == 'midEdge':
51 groupe_name2dofid[phys_name] = space.edgeid2dofid(eid)
52 groupe_name2dofxy[phys_name] = mesh.ecog()[eid]
53 else: raise ValueError(func_name+" not coded for space.interpolatoryAt.")
54 return groupe_name2dofid, groupe_name2dofxy
56def set_values(vxy: FloatArray,
57 evaluator: float|Callable[[FloatArray], FloatArray]
58 )->FloatArray:
59 """ A simple function that return values associated to the points in array vxy
61 according to the evaluator. The evaluator can be a scalar or a function
62 """
63 func_name = set_values.__name__
64 if isinstance(evaluator, float):
65 values = np.ones(len(vxy))*evaluator
66 elif callable(evaluator):
67 values = evaluator(vxy)
68 else:
69 raise ValueError('evaluator of '+str(type(evaluator))+' unknown in '+func_name+'.')
70 return values
72dirname2dirindex = {'x':0, 'y':1, 'z':2}
73def compute_dirichlet(space,
74 physname2dirichlet,
75 groupename2dofid:Mapping[str, IndexArray],
76 groupename2dofxy:Mapping[str, FloatArray]
77 )-> tuple[IndexArray, IndexArray, FloatArray]:
78 ''' return the list of free dofs ids, fixed dofs ids and fixed dofs values '''
79 func_name = compute_dirichlet.__name__
80 fixed_values = np.array([], dtype=np.float64)
81 fixed_dofs = np.array([], dtype=np.int64)
82 for phys_name, dir2value in physname2dirichlet.items():
83 dofid = groupename2dofid[phys_name]
84 dofxy = groupename2dofxy[phys_name]
85 for dirname, value in dir2value.items():
86 direction = dirname2dirindex[dirname]
87 #print('ggg',dirname, direction)
88 if direction is None:
89 raise ValueError('directionName '+dirname+' unknown in '+func_name+'.')
90 fixed_dofs = np.append(fixed_dofs, dofid[:, direction]).astype(np.int64)
91 fixed_values = np.append(fixed_values, set_values(dofxy, value)).astype(np.float64)
92 #nbfixed = len(fixed_dofs)
93 fixed_dofs, index = np.unique(fixed_dofs, return_index=True)
94 # if nbfixed != len(fixed_dofs):
95 # warn(' Some dofs where fixed Twice in '+func_name+'.')
96 #nbfixed = len(fixed_dofs)
97 fixed_values = np.array(fixed_values[index], dtype = np.float64)
98 free_dofs = np.setdiff1d(np.arange(space.size()), fixed_dofs).astype(np.int64)
99 free_dofs = np.asarray(free_dofs, dtype = IndexType)
100 fixed_dofs = np.asarray(fixed_dofs, dtype = IndexType)
101 return free_dofs, fixed_dofs, fixed_values
103def compute_neumann(space:fs.FEMSpace, phys_name2neumann):
104 ''' return the vector F such as F.T.dot(U) = int_neumann ( t.u_h) dGamma_h '''
105 fun_name = compute_neumann.__name__
106 F = np.zeros((space.scalar_size(), space.vecdim)) # pylint: disable=C0103
107 edgequad = quad.Edge_gauss(0)
108 for phys_name, dir2value in phys_name2neumann.items():
109 eids, ___ = space.m.get_edgegroupe(phys_name)
110 N = space.operator_dof2val_edge(eids, edgequad.s) # pylint: disable=C0103
111 xy = space.m.ecog(eids)
112 l = space.m.elength(eids)
113 t = np.zeros((eids.size, 2))
114 for direction_name, value in dir2value.items():
115 direction = dirname2dirindex[direction_name]
116 if direction is None:
117 raise ValueError('directionName '+direction_name+' unknown in '+fun_name)
118 t[:, direction] = l*set_values(xy, value)
119 F += (edgequad.w*N.T.dot(t.flatten())).reshape((-1, 2)) # pylint: disable=C0103
120 return F.flatten()
122def compute_dirichlet_fem_p1_vect2d(nvertex, nodex, valuex, nodey, valuey):
123 ''' more direct way to compute dirichlet bc if node list is already known'''
124 fixed_dofs, index = np.unique(np.append(nodex*2, nodey*2+1), return_index=True)
125 fixed_dofs = fixed_dofs.astype(np.int64)
126 fixed_values = np.append(valuex, valuey)[index]
127 free_dofs = np.setdiff1d(np.arange(2*nvertex), fixed_dofs).astype(np.int64)
128 return free_dofs, fixed_dofs, fixed_values