Skip to content

Use your own data

This example builds a dataset from two coordinate arrays and one field array, which is the form most simulation output and most gridded measurements already have. The other examples start from a dataset shipped with KD.

The field below is generated with NumPy to give the example concrete data. Substituting your own arrays requires no other change.

Build the dataset

kd.PDEDataset.from_arrays needs three things: one 1-D array per coordinate axis, one field array shaped to those axes in order, and the name of the term that goes on the left-hand side. The full contract is under Data requirements.

import numpy as np

import kd

k = 0.1
x = np.linspace(0.0, 1.0, 128)
t = np.linspace(0.0, 0.5, 101)
X, T = np.meshgrid(x, t, indexing="ij")
u = np.exp(-(np.pi**2) * k * T) * np.sin(np.pi * X) + 0.5 * np.exp(
    -4 * (np.pi**2) * k * T
) * np.sin(2 * np.pi * X)

dataset = kd.PDEDataset.from_arrays(
    coords={"x": x, "t": t},
    fields={"u": u},
    lhs="u_t",
    name="my-data",
)
kd.preview(dataset)
Dataset: my-data
Axes:
     x | n=128  | range [0.000, 1.000] | step 0.007874 (uniform)
     t | n=101  | range [0.000, 0.500] | step 0.005 (uniform)
Fields:
     u | dtype=float64 | shape=(128, 101) | min=0.000 max=1.299 mean=0.499 (NaN=0)
LHS: u_t  (field='u', axis='t')
Status: ready to fit

kd.preview reports what KD read: the axes and their spacing, the field and its range, and the left-hand side it will fit. Status: ready to fit means the dataset satisfies the contract.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(6.4, 3.0), dpi=90)
ax.pcolormesh(t, x, u, shading="auto")
ax.set(xlabel="t", ylabel="x", title="my-data: u(x, t)")
fig.tight_layout()
plt.show()

png

Fit an algorithm

Fitting is a single call. seed makes the run reproducible, and generations sets the search budget.

model = kd.Model(algorithm="sga", generations=20, seed=2, verbose=False).fit(dataset)

print(f"Discovered : {model.best_expr_}")
print(f"Score      : {model.best_score_:.4g}   (SGA-PDE AIC, lower is better)")
Discovered : u_t = 0.09998*diff2_x(u)
Score      : -24.38   (SGA-PDE AIC, lower is better)

diff2_x(u) is KD's notation for the second derivative in x, so the recovered law reads as the heat equation the field was built from. Every algorithm writes its answer in this notation; the term vocabulary is under Equation representation.

Render the report

kd.VizEngine.render_all writes every figure that applies to this run, plus one HTML page collecting them.

from pathlib import Path

report = kd.VizEngine(output_dir=Path("../out/my_data")).render_all(
    model.result_, algorithm=model.algorithm_, dataset=dataset
)
print(f"Report  : {report.report}")
print(f"Figures : {len(report.figures)}")
Report  : ../out/my_data/report.html
Figures : 15

Measured data

The derivatives above came from finite differences on the grid, which suits clean simulation output. For measured data, pass derivatives="autograd" to kd.Model: KD then fits a smooth surrogate to the field and differentiates that, which handles noise and points that are not evenly spaced. The two routes are compared under Derivative sources.

Next steps

  • Getting started -- the same workflow on a bundled benchmark, with more of the result surface explained.
  • Breaking waves -- a published result reproduced on real wave-tank measurements.
  • Algorithms -- what each of the seven searches, and a worked example for each.