Skip to content

Commit 2fe78f8

Browse files
authored
Merge pull request #2140 from Hukai0/draft/kyle
add tpdv
2 parents 8ad7587 + a660a52 commit 2fe78f8

File tree

328 files changed

+38475
-6987
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

328 files changed

+38475
-6987
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
from fealpy.backend import bm
3+
from lafemeit.data import *
4+
5+
from matplotlib import pyplot as plt
6+
7+
8+
num = 2
9+
clim = (-0.9, 0.9, -0.9, 0.9)
10+
XLIM = (-1, 1)
11+
YLIM = (-1, 1)
12+
13+
# model = random_gaussian2d_model(num, clim, (2, 6), (0.5, 1))
14+
# model = random_unioned_circles_model(num)
15+
model = random_unioned_triangles_model(num, clim, rlim=(0.2, 0.5), kind="equ")
16+
print(model)
17+
18+
X = bm.linspace(XLIM[0], XLIM[1], 100)
19+
Y = bm.linspace(YLIM[0], YLIM[1], 100)
20+
X, Y = bm.meshgrid(X, Y)
21+
points = bm.stack((X, Y), axis=-1)
22+
phi = model.coef(points)
23+
ai = plt.pcolormesh(X, Y, phi, cmap="jet")
24+
plt.colorbar(ai)
25+
26+
plt.show()

app/lafem-eit/lafemeit/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11

22
from fealpy.backend import backend_manager as bm
33

4-
__version__ = "0.1.0"
5-
6-
bm.set_backend('pytorch')
4+
__version__ = "0.2.0"

app/lafem-eit/lafemeit/data.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
2+
__all__ = [
3+
'random_gaussian2d_model',
4+
'random_unioned_circles_model',
5+
'random_unioned_triangles_model',
6+
]
7+
8+
from typing import Literal
9+
from collections import namedtuple
10+
11+
from fealpy.backend import bm, TensorLike as Tensor
12+
13+
from . import shapes as _S
14+
15+
16+
FunctionModel = namedtuple("FunctionModel", ["coef", "levelset", "shape"])
17+
18+
19+
def _gaussian(p: Tensor, centers: Tensor, inv_cov: Tensor, base: float = 10.):
20+
# p (NC, NQ, GD)
21+
# centers (NGuassian, GD)
22+
# inv_cov (NGuassian, GD, GD)
23+
struct = p.shape[:-1]
24+
p = p.reshape(-1, p.shape[-1])
25+
p0 = p[:, None, :] - centers[None, :, :] # (NC*NQ, NGuassian, GD)
26+
ind = bm.exp(-0.5 * bm.einsum("ngi, gij, ngj -> ng", p0, inv_cov, p0))
27+
ind = bm.sum(ind, axis=-1).reshape(struct) # (NC, NQ)
28+
minim = bm.min(ind)
29+
maxim = bm.max(ind)
30+
ind = (ind - minim) / (maxim - minim)
31+
return base**ind
32+
33+
34+
def random_gaussian2d_model(
35+
num: int,
36+
box: tuple[float, float, float, float],
37+
major_lim: tuple[float, float],
38+
ecc_lim: tuple[float, float] = (0.5, 1.0),
39+
base: float = 10.
40+
):
41+
"""
42+
Generate a random Gaussian conductivity model.
43+
44+
Parameters:
45+
num (int): Number of Gaussian
46+
box (tuple[xmin, xmax, ymin, ymax]): The box of Gaussian centers
47+
major_lim (tuple[major_min, major_max]): The major axis covariance range
48+
ecc_lim (tuple[ecc_min, ecc_max]): The eccentricity range
49+
base (float): The ratio of the conductivity value that ranges [1.0, ratio]
50+
51+
Returns:
52+
namedtuple
53+
- coef (Callable): A function that takes points and returns the conductivity values.
54+
- levelset (None): No levelset function.
55+
"""
56+
gaussian = _S.random_gaussian_2d(
57+
num, box, major_lim, ecc_lim
58+
)
59+
60+
def gaussian_conductivity(points: Tensor, *args, **kwargs) -> Tensor:
61+
return _gaussian(
62+
points,
63+
bm.from_numpy(gaussian.centers),
64+
bm.from_numpy(gaussian.invcov),
65+
base
66+
)
67+
68+
gaussian_conductivity.__dict__['coordtype'] = 'cartesian'
69+
70+
return FunctionModel(gaussian_conductivity, None, gaussian)
71+
72+
73+
def random_unioned_circles_model(
74+
num: int,
75+
values: tuple[float, float] = (10.0, 1.0),
76+
):
77+
"""Generate a random unioned circles conductivity model.
78+
79+
Parameters:
80+
num (int): Number of circles
81+
values (tuple[float, float]): The conductivity values of the circles and the rest.
82+
83+
Returns:
84+
namedtuple:
85+
- coef (Callable): A function that takes points and returns the conductivity values.
86+
- levelset (Callable): Level set function.
87+
"""
88+
circles = _S.random_circles(num)
89+
level_set_func = lambda p: _S.circle_union_levelset(p, circles)
90+
91+
def circle_conductivity(points: Tensor, *args, **kwargs) -> Tensor:
92+
inclusion = level_set_func(points) < 0. # a bool tensor on quadrature points.
93+
sigma = bm.empty(points.shape[:2], **bm.context(points)) # (Q, C)
94+
sigma = bm.set_at(sigma, inclusion, values[0])
95+
sigma = bm.set_at(sigma, ~inclusion, values[1])
96+
return sigma
97+
98+
circle_conductivity.__dict__['coordtype'] = 'cartesian'
99+
100+
return FunctionModel(circle_conductivity, level_set_func, circles)
101+
102+
103+
def random_unioned_triangles_model(
104+
num: int,
105+
box: tuple[float, float, float, float] = [-1., 1., -1., 1.],
106+
kind: Literal["", "equ"] = "",
107+
rlim: tuple[float, float] = (0.5, 1.0),
108+
values: tuple[float, float] = (10.0, 1.0),
109+
):
110+
"""Generate a random unioned triangles conductivity model.
111+
112+
Parameters:
113+
num (int): Number of triangles
114+
box (tuple[xmin, xmax, ymin, ymax]): The box of triangles
115+
kind (str): The kind of triangles.
116+
rlim (tuple[r_min, r_max]): The radius range, available when kind is "equ".
117+
values (tuple[float, float]): The conductivity values of the triangles and the rest.
118+
119+
Returns:
120+
namedtuple:
121+
- coef (Callable): A function that takes points and returns the conductivity values.
122+
- levelset (Callable): Level set function.
123+
"""
124+
if kind == "equ":
125+
tris = _S.random_equ_triangles(num, box[0:2], box[2:4], rlim)
126+
else:
127+
tris = _S.random_ccw_triangles(num, box[0:2], box[2:4])
128+
level_set_func = lambda p: _S.triangle_union_levelset(p, tris)
129+
130+
def triangle_conductivity(points: Tensor, *args, **kwargs) -> Tensor:
131+
inclusion = level_set_func(points) < 0. # a bool tensor on quadrature points.
132+
sigma = bm.empty(points.shape[:2], **bm.context(points)) # (Q, C)
133+
sigma = bm.set_at(sigma, inclusion, values[0])
134+
sigma = bm.set_at(sigma, ~inclusion, values[1])
135+
return sigma
136+
137+
triangle_conductivity.__dict__['coordtype'] = 'cartesian'
138+
139+
return FunctionModel(triangle_conductivity, level_set_func, tris)

app/lafem-eit/lafemeit/shapes.py

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
2+
__all__ = [
3+
'random_gaussian_2d',
4+
'triangle_union_levelset',
5+
'random_ccw_triangles',
6+
'circle_union_levelset',
7+
'random_circles',
8+
]
9+
10+
from typing import NamedTuple
11+
import numpy as np
12+
from fealpy.backend import bm, TensorLike as Tensor
13+
14+
15+
# ------------------------------
16+
# Gaussian
17+
# ------------------------------
18+
19+
Gaussian = NamedTuple("Gaussian", [("centers", Tensor), ("invcov", "Tensor")])
20+
21+
22+
def random_gaussian_2d(
23+
num: int,
24+
box: tuple[float, float, float, float],
25+
major_lim: tuple[float, float],
26+
ecc_lim: tuple[float, float] = (0.5, 1.0),
27+
):
28+
"""Generate random means and covariance matrices for Gaussian distibution.
29+
30+
Parameters:
31+
num (int): Number of i.i.d
32+
box (tuple of 4 floats): Limits for centers
33+
major_lim (tuple of 2 floats): Limits for the standard deviations
34+
on their major axis
35+
ecc_lim (tuple of 2 floats): Limits for eccentricity
36+
37+
Returns:
38+
namedtuple:
39+
- Tensor: centers
40+
- Tensor: inverse of covariance matrices
41+
"""
42+
theta = np.random.uniform(0, 2 * np.pi, size=(num,))
43+
major = np.random.uniform(major_lim[0], major_lim[1], size=(num,))
44+
ecc = np.random.uniform(ecc_lim[0], ecc_lim[1], size=(num,))
45+
ecc2 = ecc*ecc
46+
cos = np.cos(theta)
47+
sin = np.sin(theta)
48+
invcov = np.stack([
49+
np.stack([1-ecc2*sin**2, -ecc2*sin*cos], axis=-1),
50+
np.stack([-ecc2*sin*cos, 1-ecc2*cos**2], axis=-1),
51+
], axis=-2) * (major**2)[:, None, None] # (NGuassian, GD, GD)
52+
centers = np.stack([
53+
np.random.uniform(box[0], box[1], size=(num,)),
54+
np.random.uniform(box[2], box[3], size=(num,)),
55+
], axis=-1) # (NGuassian, GD)
56+
return Gaussian(centers, invcov)
57+
58+
59+
# ------------------------------
60+
# Triangles
61+
# ------------------------------
62+
63+
def triangle_union_levelset(points: Tensor, triangles: Tensor):
64+
"""多个三角形并集的水平集函数(内部为负)
65+
66+
Parameters:
67+
triangles (Tensor): shape (num, 3, 2) 逆时针排列的三角形顶点坐标
68+
points (Tensor): shape (N, 2) 待评估点坐标
69+
70+
Returns:
71+
phi (ndarray): shape (N,) 并集的水平集函数值
72+
"""
73+
# triangles: (num, 3, 2)
74+
# points: (N, 2)
75+
struct = points.shape[:-1]
76+
points = points.reshape(-1, points.shape[-1])
77+
78+
# 边向量 e_k = v_{k+1} - v_k
79+
edges = bm.roll(triangles, -1, axis=1) - triangles # (num, 3, 2)
80+
81+
# 外法向量 n = (dy, -dx)
82+
normals = bm.stack((edges[..., 1], -edges[..., 0]), axis=-1) # (num, 3, 2)
83+
84+
# (N, num, 3, 2) = points[:,None,None,:] - triangles[None,:,:,:]
85+
diff = points[:, None, None, :] - triangles[None, :, :, :]
86+
87+
# 点积 (x - v_k) · n_k → (N, num, 3)
88+
vals = bm.sum(diff * normals[None, :, :, :], axis=-1)
89+
90+
# 每个三角形取 max → (N, num)
91+
phi_tri = bm.max(vals, axis=-1)
92+
93+
# 并集取 min → (N,)
94+
phi = bm.min(phi_tri, axis=1)
95+
96+
return phi.reshape(struct)
97+
98+
99+
def random_ccw_triangles(num: int, /, xlim: tuple[float, float], ylim: tuple[float, float]):
100+
"""随机生成满足逆时针排列的三角形
101+
102+
Parameters:
103+
num (int): 三角形数量
104+
xlim (tuple[xmin, xmax]): x 坐标范围
105+
ylim (tuple[ymin, ymax]): y 坐标范围
106+
107+
Returns:
108+
ndarray: 逆时针排列的三角形顶点 (num, 3, 2)
109+
"""
110+
# 随机生成顶点
111+
triangles = np.empty((num, 3, 2), dtype=np.float64)
112+
triangles[..., 0] = np.random.uniform(xlim[0], xlim[1], size=(num, 3))
113+
triangles[..., 1] = np.random.uniform(ylim[0], ylim[1], size=(num, 3))
114+
115+
# 叉积符号判断
116+
v0 = triangles[:, 0]
117+
v1 = triangles[:, 1]
118+
v2 = triangles[:, 2]
119+
120+
cross = (
121+
(v1[:, 0] - v0[:, 0]) * (v2[:, 1] - v0[:, 1])
122+
- (v1[:, 1] - v0[:, 1]) * (v2[:, 0] - v0[:, 0])
123+
)
124+
125+
# 对顺时针的三角形交换 v1, v2
126+
mask = cross < 0
127+
triangles[mask, 1], triangles[mask, 2] = (
128+
np.copy(triangles[mask, 2]),
129+
np.copy(triangles[mask, 1]),
130+
)
131+
132+
return bm.from_numpy(triangles)
133+
134+
135+
def random_equ_triangles(num: int, /, xlim: tuple[float, float], ylim: tuple[float, float], rlim: tuple[float, float]):
136+
"""随机生成逆时针排列的等边三角形
137+
138+
Parameters:
139+
num (int): Number of triangles
140+
xlim (tuple[xmin, xmax]): 三角形中心 x 坐标范围
141+
ylim (tuple[ymin, ymax]): 三角形中心 y 坐标范围
142+
rlim (tuple[rmin, rmax]): 等边三角形外接圆半径范围(尺寸参数)
143+
144+
Returns:
145+
ndarray: 逆时针排列的等边三角形顶点坐标 (num, 3, 2)
146+
"""
147+
# 随机生成尺寸(外接圆半径)
148+
r = np.random.uniform(rlim[0], rlim[1], size=num)
149+
150+
# 随机生成中心点
151+
centers = np.empty((num, 2), dtype=np.float64)
152+
centers[:, 0] = np.random.uniform(xlim[0]+r, xlim[1]-r, size=num)
153+
centers[:, 1] = np.random.uniform(ylim[0]+r, ylim[1]-r, size=num)
154+
155+
# 随机初始角度
156+
theta0 = np.random.uniform(0.0, 2.0 * np.pi, size=num)
157+
158+
# 三个顶点角度(逆时针)
159+
angles = theta0[:, None] + np.array([0.0, 2.0 * np.pi / 3.0, 4.0 * np.pi / 3.0])
160+
161+
# 生成顶点坐标
162+
triangles = np.empty((num, 3, 2), dtype=np.float64)
163+
triangles[..., 0] = centers[:, 0, None] + r[:, None] * np.cos(angles)
164+
triangles[..., 1] = centers[:, 1, None] + r[:, None] * np.sin(angles)
165+
166+
return triangles
167+
168+
169+
# ------------------------------
170+
# Circles
171+
# ------------------------------
172+
173+
Circles = NamedTuple("Circles", [("centers", Tensor), ("radius", Tensor)])
174+
175+
def circle_union_levelset(p: Tensor, circles: tuple[Tensor, Tensor]):
176+
"""Calculate level set function value.
177+
178+
Parameters:
179+
p (Tensor): points (NN, 2) to be evaluated
180+
circles (tuple of two tensors): centers (NC, 2) and radius (NC,)
181+
182+
Returns:
183+
value of the levelset function on the given points, shaped (NN,).
184+
"""
185+
centers, radius = circles
186+
struct = p.shape[:-1]
187+
p = p.reshape(-1, p.shape[-1])
188+
dis = bm.linalg.norm(p[:, None, :] - centers[None, :, :], axis=-1) # (N, NCir)
189+
ret = bm.min(dis - radius[None, :], axis=-1) # (N, )
190+
return ret.reshape(struct)
191+
192+
193+
def random_circles(num: int, /) -> Circles:
194+
"""Generate random circles on [-1, 1]^2"""
195+
ctrs_ = np.random.rand(num, 2) * 1.6 - 0.8 # (NCir, GD)
196+
b = np.min(0.9-np.abs(ctrs_), axis=-1) # (NCir, )
197+
rads_ = np.random.rand(num) * (b-0.1) + 0.1 # (NCir, )
198+
return Circles(bm.from_numpy(ctrs_), bm.from_numpy(rads_))

app/lafem-eit/script/eit_data_config.yaml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ tail: 20
66
data:
77
gd_folder: 'gd'
88
gn_file: 'gn'
9+
model: 'triangle'
10+
kind: 'equ'
11+
num_cir: 2
12+
rlim: [0.2, 0.4]
13+
major_lim: [2.0, 6.0]
14+
ecc_lim: [0.5, 1.0]
915
sigma: [10., 1.]
1016
ext: 63
11-
num_cir: 3
1217
freq: [1, 2, 3, 4, 5, 6, 8, 16]
1318

1419
label:
1520
label_folder: 'inclusion'
16-
dtype: 'bool'
21+
dtype: 'float32'
1722
transition: 'bool'
1823

1924
fem:

0 commit comments

Comments
 (0)