Helmholtz equation over a 2D square domain#

Problem setup#

For a wave number \(k_0 = 2\pi n\) with \(n = 2\), we will solve a Helmholtz equation:

\[ - u_{xx}-u_{yy} - k_0^2 u = f, \qquad \Omega = [0,1]^2 \]

with the Dirichlet boundary conditions

\[ u(x,y)=0, \qquad (x,y)\in \partial \Omega \]

and a source term \(f(x,y) = k_0^2 \sin(k_0 x)\sin(k_0 y)\).

Remark that the exact solution reads: $\( u(x,y)= \sin(k_0 x)\sin(k_0 y) \)$

Dimensional Analysis#

Assigning Physical Units:#

To perform dimensional analysis, we will assign physical units to each variable and parameter in the equation. We’ll ensure that both sides of the Helmholtz equation have consistent dimensions.

Variables and Parameters:#

Variable/Parameter

Symbol

Physical Quantity

Unit (SI)

Dimension

Field Variable

\( u \)

Scalar field (e.g., displacement, pressure)

Dimensionless or [U] [Depends on Physical Context]

\([U]\)

Spatial Coordinate

\( x, y \)

Position in space

meters (m)

Length \([L]\)

Wave Number

\( k_0 \)

Spatial frequency

inverse meters (1/m)

\([L]^{-1}\)

Source Term

\( f \)

External forcing or source

Depends on \( u \)‘s units (e.g., if \( u \) is dimensionless, f has units of 1/m²)

\([U][L]^{-2}\)

Note: The units of \( u \) can vary based on the physical context of the problem. However, based on the exact solution provided, \( u(x,y) = \sin(k_0 x) \sin(k_0 y) \), it suggests that \( u \) is dimensionless. Therefore, for this analysis, we’ll assume \( u \) is dimensionless.

Detailed Assignments:#

  1. Field Variable (\( u \)):

    • Physical Quantity: Scalar field (e.g., displacement, pressure)

    • Unit: Dimensionless

    • Dimension: \([1]\)

  2. Spatial Coordinates (\( x, y \)):

    • Physical Quantity: Position in space

    • Unit: meters (m)

    • Dimension: Length \([L]\)

  3. Wave Number (\( k_0 \)):

    • Physical Quantity: Spatial frequency

    • Unit: inverse meters (1/m)

    • Dimension: \([L]^{-1}\)

  4. Source Term (\( f \)):

    • Physical Quantity: External forcing or source

    • Unit: inverse meters squared (1/m²)

    • Dimension: \([L]^{-2}\)

Dimensional Consistency Check:#

To ensure the Helmholtz equation is dimensionally consistent, both sides of the equation must have the same dimensions.

  1. Left Side (\( -u_{xx} - u_{yy} - k_0^2 u \)):

    • \( u_{xx} = \frac{\partial^2 u}{\partial x^2} \):

      • Dimension: \(\frac{[U]}{[L]^2}\)

      • Since \( u \) is dimensionless: \([U] = 1\), so \( u_{xx} \) has dimension \([L]^{-2}\).

    • \( u_{yy} = \frac{\partial^2 u}{\partial y^2} \):

      • Dimension: Same as \( u_{xx} \), i.e., \([L]^{-2}\).

    • \( k_0^2 u \):

      • Dimension: \([k_0]^2 [U] = [L]^{-2} \times 1 = [L]^{-2}\).

    • Combined Left Side: Each term has dimension \([L]^{-2}\), ensuring consistency.

  2. Right Side (\( f \)):

    • Dimension: \([L]^{-2}\).

    • Conclusion: Both sides of the equation have the same dimension \([L]^{-2}\), confirming dimensional consistency.

Summary of Physical Units:#

Symbol

Physical Quantity

Unit (SI)

Dimension

\( u \)

Scalar field (dimensionless)

Dimensionless

\([1]\)

\( x, y \)

Spatial coordinates

meters (m)

Length \([L]\)

\( k_0 \)

Wave number

inverse meters (1/m)

\([L]^{-1}\)

\( f \)

Source term

inverse meters squared (1/m²)

\([L]^{-2}\)

Boundary Conditions Units:#

  1. Dirichlet Boundary Conditions (\( u(x,y) = 0 \)):

    • Units: Same as \( u \), which is dimensionless.

  2. Exact Solution (\( u(x,y) = \sin(k_0 x) \sin(k_0 y) \)):

    • Units: Dimensionless, consistent with \( u \)‘s units.

Conclusion:#

All variables and parameters in the Helmholtz equation have been assigned consistent physical units, ensuring the dimensional integrity of the equation and its boundary conditions. Specifically:

  • \( u \) is dimensionless.

  • \( x \) and \( y \) are measured in meters (m).

  • \( k_0 \) has units of inverse meters (1/m).

  • \( f \) has units of inverse meters squared (1/m²).

This dimensional assignment ensures that the Helmholtz equation is dimensionally consistent and the boundary conditions are appropriately defined.

Code Implementation#

First, import the necessary libraries and modules for the problem setup and solution:

import braintools
import brainunit as u
import numpy as np

import pinnx

Define the physical units and parameters for the Helmholtz equation:

unit_of_u = u.UNITLESS
unit_of_x = u.meter
unit_of_y = u.meter
unit_of_k0 = 1 / unit_of_x
unit_of_f = 1 / u.meter ** 2

# General parameters
n = 2
precision_train = 10
precision_test = 30
hard_constraint = True  # True or False
weights = 100  # if hard_constraint == False
iterations = 5000
parameters = [1e-3, 3, 150]

learning_rate, num_dense_layers, num_dense_nodes = parameters

Define the PDE function for the Helmholtz equation:

geom = pinnx.geometry.Rectangle([0, 0], [1, 1]).to_dict_point(x=unit_of_x, y=unit_of_y)
k0 = 2 * np.pi * n
wave_len = 1 / n

hx_train = wave_len / precision_train
nx_train = int(1 / hx_train)

hx_test = wave_len / precision_test
nx_test = int(1 / hx_test)


def pde(x, y):
    hessian = net.hessian(x)

    dy_xx = hessian["y"]["x"]["x"]
    dy_yy = hessian["y"]["y"]["y"]

    f = k0 ** 2 * u.math.sin(k0 * x['x'] / unit_of_x) * u.math.sin(k0 * x['y'] / unit_of_y)
    return -dy_xx - dy_yy - (k0 * unit_of_k0) ** 2 * y['y'] - f * unit_of_f

Define the boundary conditions for the Helmholtz equation:



if hard_constraint:
    bc = []
else:
    bc = pinnx.icbc.DirichletBC(lambda x: {'y': 0 * unit_of_u})

net = pinnx.nn.Model(
    pinnx.nn.DictToArray(x=unit_of_x, y=unit_of_y),
    pinnx.nn.FNN([2] + [num_dense_nodes] * num_dense_layers + [1],
                 u.math.sin,
                 braintools.init.KaimingUniform()),
    pinnx.nn.ArrayToDict(y=unit_of_u),
)

if hard_constraint:
    def transform(x, y):
        x = pinnx.utils.array_to_dict(x, ["x", "y"], keep_dim=True)
        res = x['x'] * (1 - x['x']) * x['y'] * (1 - x['y'])
        return res * y


    net.approx.apply_output_transform(transform)

Define the problem and train the model to solve the Helmholtz equation:

problem = pinnx.problem.PDE(
    geom,
    pde,
    bc,
    net,
    num_domain=nx_train ** 2,
    num_boundary=4 * nx_train,
    solution=lambda x: {'y': u.math.sin(k0 * x['x'] / unit_of_x) * u.math.sin(k0 * x['y'] / unit_of_y) * unit_of_u},
    num_test=nx_test ** 2,
    loss_weights=None if hard_constraint else [1, weights],
)

Train the model using the Adam optimizer and the specified learning rate:

trainer = pinnx.Trainer(problem)
trainer.compile(braintools.optim.Adam(learning_rate), metrics=["l2 relative error"]).train(iterations=iterations)
trainer.saveplot(issave=True, isplot=True)
Compiling trainer...
'compile' took 0.059387 s

Training trainer...

Step      Train loss                         Test loss                        Test metric                                       
0         [5213.1675 * metre ** -4]          [6450.17 * metre ** -4]          [{'y': Array(1.0007389, dtype=float32)}]          
1000      [115.11537 * metre ** -4]          [164.17776 * metre ** -4]        [{'y': Array(0.50004345, dtype=float32)}]