Poisson on a square (field learning)¤
This recipe solves a simple elliptic PDE by learning a field \(u_\theta(x)\).
Problem¤
Let \(\Omega=[-1,1]^2\). We solve
\[
\Delta u = 4 \quad \text{in }\Omega,\qquad
u = g \quad \text{on }\partial\Omega,
\]
with \(g(x,y)=x^2+y^2\). The exact solution is \(u^\star(x,y)=x^2+y^2\).
Domain and components¤
- Domain:
Square(center=(0,0), side=2)(label"x"with \(d=2\)). - Interior component:
geom.component()(default). - Boundary component:
geom.component({"x": Boundary()}).
Soft PINN (PDE residual + boundary penalty)¤
Example
import jax.random as jr
import optax
import phydrax as phx
geom = phx.domain.Square(center=(0.0, 0.0), side=2.0)
@geom.Function("x")
def g(x):
return x[0] ** 2 + x[1] ** 2
model = phx.nn.MLP(
in_size=2, out_size="scalar", width_size=16, depth=2, scan=False, key=jr.key(0)
)
u = geom.Model("x")(model)
structure = phx.domain.ProductStructure((("x",),))
pde = phx.constraints.ContinuousPointwiseInteriorConstraint(
"u",
geom,
operator=lambda f: phx.operators.laplacian(f, var="x") - 4.0,
num_points=64,
structure=structure,
reduction="mean",
)
boundary = geom.component({"x": phx.domain.Boundary()})
bc = phx.constraints.ContinuousDirichletBoundaryConstraint(
"u",
boundary,
target=g,
num_points=32,
structure=structure,
weight=10.0,
reduction="mean",
)
solver = phx.solver.FunctionalSolver(functions={"u": u}, constraints=[pde, bc])
solver = solver.solve(num_iter=20, optim=optax.adam(1e-3), seed=0)
Enforced Dirichlet boundary (ansatz + interior residual)¤
Instead of penalizing boundary mismatch, enforce \(u=g\) by construction using
enforce_dirichlet, staged by an EnforcedConstraintPipeline (via constraint_terms=...).
Example
import jax.random as jr
import optax
import phydrax as phx
geom = phx.domain.Square(center=(0.0, 0.0), side=2.0)
@geom.Function("x")
def g(x):
return x[0] ** 2 + x[1] ** 2
model = phx.nn.MLP(
in_size=2, out_size="scalar", width_size=16, depth=2, scan=False, key=jr.key(0)
)
u = geom.Model("x")(model)
structure = phx.domain.ProductStructure((("x",),))
pde = phx.constraints.ContinuousPointwiseInteriorConstraint(
"u",
geom,
operator=lambda f: phx.operators.laplacian(f, var="x") - 4.0,
num_points=64,
structure=structure,
)
boundary = geom.component({"x": phx.domain.Boundary()})
term = phx.solver.SingleFieldEnforcedConstraint(
"u",
boundary,
lambda f: phx.constraints.enforce_dirichlet(f, boundary, var="x", target=g),
)
solver = phx.solver.FunctionalSolver(
functions={"u": u},
constraints=[pde],
constraint_terms=[term],
boundary_weight_num_reference=128,
boundary_weight_key=jr.key(1),
)
solver = solver.solve(num_iter=20, optim=optax.adam(1e-3), seed=0)
Grid evaluation (coord-separable batches)¤
When you want axis-aware evaluation (e.g. spectral/basis derivatives, operator learning), sample coordinate axes and
evaluate on the implied Cartesian grid. For a 2D geometry label "x", coord-separable sampling provides a tuple
\((x_{\text{axis}},y_{\text{axis}})\).
Example
import jax.random as jr
import phydrax as phx
# Basis/FD backends require the field to support structured inputs (a tuple of 1D axes).
# Use a structured model like SeparableMLP/FNO/DeepONet for grid-native evaluation.
geom = phx.domain.Square(center=(0.0, 0.0), side=2.0)
model = phx.nn.SeparableMLP(
in_size=2,
out_size="scalar",
latent_size=16,
width_size=16,
depth=2,
key=jr.key(0),
)
u = geom.Model("x")(model)
batch = geom.component().sample_coord_separable({"x": (32, 32)}, key=jr.key(1))
# Evaluate a basis-aware Laplacian on the grid.
du = phx.operators.laplacian(u, var="x", backend="basis", basis="poly")
out = du(batch)
See Guides → Domains and sampling and Guides → Differential operators.