Rešavanje na domenu oblika kvadrata sa šupljinom
U odnosu na prethodni primer Rešavanje na domenu oblika kvadrata dodajemo šupljinu u sredini kvadratnog domena i propisujemo odgovarajuće Nojmanove granične uslove na obodu šupljine. Da se podsetimo, domen problema \(\Omega\) je kvadrat stranice \(L, \, L=1\), iz koga isključujemo krug poluprečnika \(R=\frac{1}{4}\). Za talasni broj \(k_0=2 \pi n\) i \(n=1\), rešavamo Helmholcovu jednačinu:
gde je član koji specificira izvor \(f(x,y)=k_0^2 \sin(k_0x) \sin(k_0 y)\). Oblik domena može se videti na Sl. 40.
Postoji analitičko rešenje ovog problema i ono glasi:
Ovde ćemo specificirati Dirihleove granične uslove prema analitičkom rešenju na spoljnoj granici domena \(\Omega\) koju označavamo sa \(\Gamma_{outer}\):
Na sličan način, možemo da definišemo granični uslov na unutrašnjoj granici, ovog puta Nojmanov:
gde je \(n\) vektor normale. Konciznije napisano, Nojmanov granični uslov na unutrašnjoj granici glasi:
Implementacija
Na sledećem listingu su dati glavni detalji implementacije. Namerno su izostavljeni delovi koji su irelevantni za samo rešavanje, kao što je crtanje dijagrama. Celokupna skripta se, kao i ostale, nalazi u repozitorijumu sa primerima.
1import deepxde as dde
2import matplotlib.pyplot as plt
3import numpy as np
4from deepxde.backend import tf
5sin = tf.sin
6
7# Opsti parametri
8n = 2
9length = 1
10R = 1 / 4
11
12precision_train = 15
13precision_test = 30
14
15weight_inner = 10
16weight_outer = 100
17iterations = 5000
18learning_rate = 1e-3
19num_dense_layers = 3
20num_dense_nodes = 350
21activation = "sin"
22
23k0 = 2 * np.pi * n
24wave_len = 1 / n
25
26# Parcijalna diferencijalna jednacina
27def pde(x, y):
28 dy_xx = dde.grad.hessian(y, x, i=0, j=0)
29 dy_yy = dde.grad.hessian(y, x, i=1, j=1)
30 f = k0**2 * sin(k0 * x[:, 0:1]) * sin(k0 * x[:, 1:2])
31 return -dy_xx - dy_yy - k0**2 * y - f
32
33# Egzaktno resenje
34def func(x):
35 return np.sin(k0 * x[:, 0:1]) * np.sin(k0 * x[:, 1:2])
36
37# Da li je tacka na granici?
38def boundary(_, on_boundary):
39 return on_boundary
40
41# Njumanovi granicni uslovi prema egzaktnom resenju
42def neumann(x):
43 grad = np.array([
44 k0 * np.cos(k0 * x[:, 0:1]) * np.sin(k0 * x[:, 1:2]),
45 k0 * np.sin(k0 * x[:, 0:1]) * np.cos(k0 * x[:, 1:2]),])
46
47 normal = -inner.boundary_normal(x)
48 normal = np.array([normal]).T
49 result = np.sum(grad * normal, axis=0)
50 return result
51
52# Geometrija
53outer = dde.geometry.Rectangle([-length / 2, -length / 2], [length / 2, length / 2])
54inner = dde.geometry.Disk([0, 0], R)
55
56# Da li je tacka na spoljnoj granici?
57def boundary_outer(x, on_boundary):
58 return on_boundary and outer.on_boundary(x)
59
60# Da li je tacka na unutrasnjoj granici?
61def boundary_inner(x, on_boundary):
62 return on_boundary and inner.on_boundary(x)
63
64# Iskljuci krug iz kvadrata
65geom = outer - inner
66
67hx_train = wave_len / precision_train
68nx_train = int(1 / hx_train)
69
70hx_test = wave_len / precision_test
71nx_test = int(1 / hx_test)
72
73# Na unutrasnjoj granici Njuman, na spoljnoj Dirihleovi
74bc_inner = dde.icbc.NeumannBC(geom, neumann, boundary_inner)
75bc_outer = dde.icbc.DirichletBC(geom, func, boundary_outer)
76
77data = dde.data.PDE(
78 geom,
79 pde,
80 [bc_inner, bc_outer],
81 num_domain=nx_train**2,
82 num_boundary=16 * nx_train,
83 solution=func,
84 num_test=nx_test**2,
85)
86
87net = dde.nn.FNN(
88 [2] + [num_dense_nodes] * num_dense_layers + [1], activation, "Glorot uniform"
89)
90
91model = dde.Model(data, net)
92
93loss_weights = [1, weight_inner, weight_outer]
94model.compile("adam", lr=learning_rate, metrics=["l2 relative error"], loss_weights=loss_weights)
95
96losshistory, train_state = model.train(iterations=iterations)
Koristićemo Tensorflow kao backend u svim našim primerima, ali treba imati u vidu da okvir DeepXDE podržava i PyTorch i još neke. Nakon standardne specifikacije opštih parametara i hiper-parametara, kao u primeru Rešavanje na domenu oblika kvadrata, uz jedinu modifikaciju dodavanja nešto više neurona po sloju (350), definišemo Nojmanov granični uslov prema jednačini (30):
def neumann(x):
grad = np.array([
k0 * np.cos(k0 * x[:, 0:1]) * np.sin(k0 * x[:, 1:2]),
k0 * np.sin(k0 * x[:, 0:1]) * np.cos(k0 * x[:, 1:2]),])
normal = -inner.boundary_normal(x)
normal = np.array([normal]).T
result = np.sum(grad * normal, axis=0)
return result
Kao što se vidi iz koda, postoje uslužne funkcije koje računaju normale na pravilne granice u kolokacionim tačkama. Ponderske težine graničnih uslova u obuci weight_inner
i weight_outer
takođe spadaju u neku vrstu hiper-parametara, pa i njima treba posvetiti pažnju uz nekoliko manuelnih proba. Dalje, sledi specifikacija geometrije problema kao razlike kvadrata i diska:
outer = dde.geometry.Rectangle([-length / 2, -length / 2], [length / 2, length / 2])
inner = dde.geometry.Disk([0, 0], R)
geom = outer - inner
Ostatak skripte je sličan primeru bez šupljine Rešavanje na domenu oblika kvadrata, pa ga nećemo dodatno pojašnjavati. Dovoljno je reći da pažnju treba obratiti da bude dovoljno kolokacionih tačaka na spoljnoj i na unutrašnjoj granici.
Rezultati
Dobijeni rezultati su prikazani u formi konturnog grafika na Sl. 41. Oko unutrašnje granice prikazani su pravci vektora normala.
Mera relativne greške modela iznosi 0,048. Uz obraćanje posebne pažnje na forsiranje graničnih uslova, zatim arhitekturu NMPFZ i najzad tip aktivacione funkcije, uspeli smo da dobijemo prilično dobro rešenje. Čitalac može samostalno da proba kako bi promena frekvencije (a samim tim i talasne dužine), gustine kolokacionih tačaka, arhitekture, uticala na proces obuke modela.