Skip to content

Working with Data

Integrating real-world data with your Dyad models is essential for creating accurate simulations and performing model validation. The DyadData package provides the tools you need to incorporate experimental data, historical measurements, and lookup tables into your Dyad components and analyses.

DyadData offers three main types of datasets:

  • DyadTimeseries: For time-series data with an independent variable (typically time) and one or more dependent variables. Use this for driving model inputs over time or validating model outputs against experimental data.

  • DyadTable: For general tabular data with named columns, useful for steady-state calibration data or multi-row tables.

  • DyadInterpolationTable2D: For 2D lookup tables where a value depends on two independent variables (e.g., damper force as a function of velocity and temperature).

Setup: Creating a Component Library

To use datasets in your Dyad models, you'll need to create a component library (which is a Julia package) and place your data files in the assets/ folder. This allows you to reference data using the portable dyad:// URI scheme.

Here's the recommended directory structure:

.
└── InterpolationDemo/
    ├── dyad/
    │   └── models.dyad           # Your Dyad component definitions
    ├── assets/
    │   ├── force_time.csv        # Time-series data files
    │   └── damper_table.csv      # 2D lookup table files
    ├── src/
    │   ├── InterpolationDemo.jl
    │   └── generated/
    ├── test/
    └── Project.toml

The assets/ folder is where you place CSV files and other data. When you reference data with dyad://InterpolationDemo/force_time.csv, Dyad looks for the file at InterpolationDemo/assets/force_time.csv.

For more help on creating a component library, see the Getting Started instructions.

Data Sources

Datasets can be loaded from multiple sources using URI strings:

URI SchemeExampleDescription
dyad://"dyad://MyPackage/data.csv"Recommended. References files in a package's assets/ folder. Portable across machines.
file://"file://data.csv" or "file:///absolute/path/data.csv"Local files. Supports both relative and absolute paths, but less portable than dyad://.
dyad+juliahub://"dyad+juliahub://juliahub.com/datasets/user/name"JuliaHub datasets for collaborative work.

Recommendation

Use dyad:// URIs for production code. This ensures your models work correctly regardless of where the package is installed, making your code more portable and easier to share.

Interpolation Components

The BlockComponents library provides interpolation components to use datasets in your models:

ComponentUse Case
Interpolation1D interpolation from a dataset. The interpolator is fixed at construction time.
ParameterizedInterpolation1D interpolation from a dataset, with data exposed as tunable parameters for runtime modification.
InterpolatedTable2D interpolation for lookup tables with two independent variables.

Interpolation Types (1D)

The Interpolation and ParameterizedInterpolation components accept an interpolation_type parameter from the InterpolationType enum. These types map to interpolation methods from DataInterpolations.jl:

TypeDescription
ConstantInterpolation()Step-wise constant interpolation. Maintains the value from the left data point until the next point.
SmoothedConstantInterpolation()Similar to constant interpolation but with smooth transitions between values. Continuously differentiable.
LinearInterpolation()Linear interpolation between adjacent data points. The most common choice for general-purpose interpolation.
QuadraticInterpolation()Fits parabolas through nearest points. Continuously differentiable.
LagrangeInterpolation(n)Fits a polynomial of degree n through the data points. Higher degrees capture more complex patterns but may oscillate.
QuadraticSpline()Piecewise quadratic curves that pass through all data points exactly. Continuous first derivative.
CubicSpline()Cubic spline interpolation. Twice continuously differentiable. Good balance of smoothness and accuracy.
AkimaInterpolation()Piecewise cubic polynomials using only neighboring points. Avoids overshooting and oscillation common in global methods.

Extrapolation Types

Both Interpolation and ParameterizedInterpolation accept an optional extrapolation_type parameter that controls behavior when the input is outside the data range. The default is ExtrapolationType.None().

TypeDescription
ExtrapolationType.None()Throws an error when the input is outside the data range. (Default)
ExtrapolationType.Constant()Extends the interpolation with the boundary values of the data.
ExtrapolationType.Linear()Extends the interpolation with a linear continuation, making it C¹ smooth at the data boundaries.
ExtrapolationType.Extension()Extends the interpolation with a continuation of the boundary interval expression for maximum smoothness.
ExtrapolationType.Periodic()Wraps the interpolation periodically, so A(t + T) == A(t) where T is the data range.
ExtrapolationType.Reflective()Mirrors the interpolation at the data boundaries.

Interpolation Dimensions (2D)

The InterpolatedTable component uses InterpolationDimension to specify the interpolation method for each axis. These types map to methods from DataInterpolationsND.jl:

TypeDescription
LinearInterpolationDimension()Linear interpolation along this axis. The most common choice.
ConstantInterpolationDimension()Step-wise constant interpolation along this axis.
BSplineInterpolationDimension(n)B-spline interpolation with maximum derivative order n. Provides smooth curves with configurable smoothness.

Example 1: Time-Based Boundary Condition

The Interpolation component creates a fixed interpolator from a dataset at construction time. This example shows a spring-damper system driven by a time-varying force loaded from a CSV file.

Setting Up the Component Library

Create a component library named InterpolationDemo with the following structure:

.
└── InterpolationDemo/
    ├── dyad/
    │   └── spring_damper.dyad
    ├── assets/
    │   └── force_time.csv
    ├── src/
    │   ├── InterpolationDemo.jl
    │   └── generated/
    └── Project.toml

Data File

Create the force profile in assets/force_time.csv:

csv
timestamp,force
0.0,0.0
0.5,5.0
1.0,10.0
1.5,10.0
2.0,5.0
2.5,0.0
3.0,-5.0
3.5,-5.0
4.0,0.0
5.0,0.0

Model Definition

Create dyad/spring_damper.dyad:

dyad
component SpringDamperSystem
  # Parameters
  parameter m::Mass = 1.0
  parameter c::TranslationalSpringConstant = 10.0
  parameter d::TranslationalDampingConstant = 2.0

  # State variables
  variable x::Position
  variable v::Velocity

  # Load force vs time data from file
  structural parameter force_dataset::DyadData.DyadTimeseries = DyadData.DyadTimeseries(
    "dyad://InterpolationDemo/force_time.csv",
    independent_var = "timestamp",
    dependent_vars = ["force"]
  )

  # Create interpolation block from dataset
  force_interp = BlockComponents.Interpolation(
    interpolation_type = BlockComponents.InterpolationType.LinearInterpolation(),
    dataset = force_dataset
  )

  variable F_applied::Force

relations
  F_applied = force_interp.y
  force_interp.u = time

  # Kinematics
  v = der(x)

  # Dynamics: m*a = F_applied - c*x - d*v
  m * der(v) = F_applied - c * x - d * v
end

Key points:

  • The DyadTimeseries loads the CSV and specifies which columns to use

  • The Interpolation component takes a dataset parameter and extracts the columns internally

  • Connect force_interp.u to time and read the interpolated value from force_interp.y

  • The interpolator is computed once at construction time and cannot be changed at runtime

Running the Analysis

Add the test component and analysis to the same file:

dyad
test component TestSpringDamperSystem
  system = SpringDamperSystem()
relations
  initial system.x = 0.0
  initial system.v = 0.0
end

analysis SpringDamperTransient
  extends TransientAnalysis(stop=5.0, saveat=0.01)
  model = TestSpringDamperSystem()
end

After compiling the library, run the analysis in Julia:

julia
using InterpolationDemo
result = SpringDamperTransient()
plot(result)

Example 2: Parameterized Interpolation

The ParameterizedInterpolation component also takes a dataset, but unlike Interpolation, it exposes the data and independent variable as tunable symbolic parameters. This allows you to modify the interpolation data at runtime through analysis parameters.

Using the same InterpolationDemo library and force_time.csv data file:

dyad
component ParamSpringDamperSystem
  parameter m::Mass = 1.0
  parameter c::TranslationalSpringConstant = 10.0
  parameter d::TranslationalDampingConstant = 2.0

  variable x::Position
  variable v::Velocity

  # Load force vs time data from file
  structural parameter input_file::String = "dyad://InterpolationDemo/force_time.csv"
  structural parameter force_dataset::DyadData.DyadTimeseries = DyadData.DyadTimeseries(
    input_file,
    independent_var = "timestamp",
    dependent_vars = ["force"]
  )

  # Create interpolation block with dataset interface
  force_interp = BlockComponents.ParameterizedInterpolation(
    interpolation_type = BlockComponents.InterpolationType.LinearInterpolation(),
    dataset = force_dataset
  )

  variable F_applied::Force

relations
  F_applied = force_interp.y
  force_interp.u = time
  v = der(x)
  m * der(v) = F_applied - c * x - d * v
end

Key differences from Interpolation:

  • Both components take a dataset parameter, but ParameterizedInterpolation additionally exposes data and independent_var as tunable symbolic parameters

  • The interpolation data can be overridden at runtime (e.g., from an analysis), while Interpolation fixes the interpolator at construction time

Runtime Data Modification

You can pass different data through the test component and analysis:

dyad
test component TestParamSpringDamperSystem
  parameter data::Force[10]
  system = ParamSpringDamperSystem(force_interp(data=data))
relations
  initial system.x = 0.0
  initial system.v = 0.0
end

analysis ParamSpringDamperTransient
  extends TransientAnalysis(stop=5.0, saveat=0.01)
  parameter data::Force[10] = rand(10)
  model = TestParamSpringDamperSystem(data=data)
end
julia
using InterpolationDemo

# Use default random data
result1 = ParamSpringDamperTransient()

# Override with custom data
result2 = ParamSpringDamperTransient(data=zeros(10))
result3 = ParamSpringDamperTransient(data=ones(10))

plot(result1)
plot(result2)
plot(result3)

Limitation

The size of the data array cannot be changed at runtime. The array size is fixed when the model is compiled.

Example 3: 2D Table Interpolation

The InterpolatedTable component handles 2D lookup tables where a value depends on two independent variables. This is useful for modeling components whose behavior depends on multiple operating conditions.

Data File Format

2D tables use a special CSV format:

              axis2 values (columns)
              ↓     ↓     ↓     ↓
         ┌────┬─────┬─────┬─────┬─────┐
         │    │250.0│300.0│350.0│400.0│  ← header row
         ├────┼─────┼─────┼─────┼─────┤
axis1 →  │-5.0│-125 │-100 │ -75 │ -50 │
values   │-2.0│ -50 │ -40 │ -30 │ -20 │
(rows)   │ 0.0│   0 │   0 │   0 │   0 │  ← data matrix
         │ 2.0│  50 │  40 │  30 │  20 │
         │ 5.0│ 125 │ 100 │  75 │  50 │
         └────┴─────┴─────┴─────┴─────┘

         first column = axis1 values
  • First row: axis2 values (e.g., temperature) as column headers

  • First column: axis1 values (e.g., velocity)

  • Remaining cells: data values (e.g., damping force)

Create assets/damper_table.csv:

csv
,250.0,300.0,350.0,400.0
-5.0,-125.0,-100.0,-75.0,-50.0
-2.0,-50.0,-40.0,-30.0,-20.0
0.0,0.0,0.0,0.0,0.0
2.0,50.0,40.0,30.0,20.0
5.0,125.0,100.0,75.0,50.0

This represents damping force as a function of velocity (rows: -5 to 5 m/s) and temperature (columns: 250 to 400 K).

Model Definition

dyad
component TemperatureDependentDamper
  parameter m::Mass = 1.0
  parameter k::TranslationalSpringConstant = 100.0

  variable x::Position
  variable v::Velocity
  variable T::Temperature
  variable F_damper::Force

  # Load 2D lookup table
  structural parameter input_file::String = "dyad://InterpolationDemo/damper_table.csv"
  structural parameter damper_table::DyadData.DyadInterpolationTable2D = DyadData.DyadInterpolationTable2D(
    input_file,
    axis1_name = "velocity",
    axis2_name = "temperature",
    data_name = "damping_force"
  )

  # 2D interpolation
  damper_interp = BlockComponents.InterpolatedTable(
    axis1 = damper_table["velocity"],
    axis2 = damper_table["temperature"],
    data = damper_table["damping_force"],
    interpolation_dim1 = BlockComponents.InterpolationDimension.LinearInterpolationDimension(),
    interpolation_dim2 = BlockComponents.InterpolationDimension.LinearInterpolationDimension()
  )

relations
  damper_interp.u1 = v
  damper_interp.u2 = T
  F_damper = damper_interp.y

  # Temperature varies with time
  T = 300.0 + 50.0 * sin(2*pi*0.1*time)

  v = der(x)
  m * der(v) = -k * x - F_damper
end

Key points:

  • DyadInterpolationTable2D specifies names for the two axes and the data

  • Access using string indexing: damper_table["velocity"], damper_table["temperature"], damper_table["damping_force"]

  • InterpolatedTable has two inputs (u1, u2) and one output (y)

  • Each dimension can have its own interpolation method

Running the Analysis

dyad
test component TestTemperatureDamper
  system = TemperatureDependentDamper()
relations
  initial system.x = 1.0
  initial system.v = 0.0
end

analysis DamperTransient
  extends TransientAnalysis(stop=10.0, saveat=0.01)
  model = TestTemperatureDamper()
end
julia
using InterpolationDemo
result = DamperTransient()
plot(result)

Using Datasets in Analyses

Calibration Analysis

Datasets are commonly used in calibration analyses to fit model parameters against experimental data. For a complete guide, see Calibration Analysis.

dyad
using DyadModelOptimizer: CalibrationAnalysis
using DyadData: DyadTimeseries

analysis MyCalibrationAnalysis
  extends CalibrationAnalysis(
    data=DyadTimeseries("dyad://MyComponentLibrary/data/measurements.csv",
                        independent_var = "time",
                        dependent_vars = ["measured_current"]),
    N_cols=1,
    depvars_cols=["measured_current"],
    depvars_names=["sensor.i"],
    N_tunables = 2,
    search_space_names=["resistor.R", "capacitor.C"],
    search_space_lb=[1.0, 1e-6],
    search_space_ub=[100.0, 1e-3],
    optimizer_maxiters=100
  )
  model = MyCircuit()
end

Using DyadData in Julia

DyadTimeseries

For time-series data with an independent variable and dependent variables.

URI-based constructor:

julia
ts = DyadTimeseries(
    "dyad://PackageName/data/measurements.csv";
    independent_var = "timestamp",
    dependent_vars = ["x", "y"]
)

From raw data:

julia
data = rand(10, 3)
ts = DyadTimeseries(data; independent_var="time", dependent_vars=["x", "y"])

Accessing columns:

julia
time_values = ts["timestamp"]
x_values = ts["x"]

DyadTable

For general tabular data with named columns.

URI-based constructor:

julia
tbl = DyadTable(
    "dyad://PackageName/calibration.csv";
    columns = ["param1", "param2", "param3"]
)

From raw data:

julia
# Single row (e.g., steady-state data)
steady_state = DyadTable([1.0, 2.0, 3.0]; columns=["x", "y", "z"])

# Multiple rows
data = rand(10, 3)
tbl = DyadTable(data; columns=["x", "y", "z"])

Accessing columns:

julia
param1_values = tbl["param1"]

DyadInterpolationTable2D

For 2D lookup tables with two independent variables.

URI-based constructor:

julia
tbl = DyadInterpolationTable2D(
    "dyad://PackageName/damper.csv";
    axis1_name = "velocity",
    axis2_name = "temperature",
    data_name = "damping_force"
)

From raw data:

julia
velocity = [-5.0, -2.0, 0.0, 2.0, 5.0]
temperature = [250.0, 300.0, 350.0, 400.0]
forces = [-125.0 -100.0 -75.0 -50.0;
          -50.0 -40.0 -30.0 -20.0;
          0.0 0.0 0.0 0.0;
          50.0 40.0 30.0 20.0;
          125.0 100.0 75.0 50.0]

tbl = DyadInterpolationTable2D(velocity, temperature, forces;
                               axis1_name="velocity",
                               axis2_name="temperature",
                               data_name="damping_force")

Accessing data:

julia
velocity_values = tbl["velocity"]        # Vector of axis1 values
temp_values = tbl["temperature"]         # Vector of axis2 values
force_matrix = tbl["damping_force"]      # Matrix of data values

Working with Tables

Use build_table() to get a TypedTables Table for advanced operations:

julia
ts = DyadTimeseries("file:///path/to/data.csv";
                    independent_var="time",
                    dependent_vars=["x", "y"])

table = build_table(ts)  # Returns a Table from TypedTables.jl

For advanced table operations like filtering, joining, or aggregation, see the TypedTables.jl documentation.

CSV Options

Pass additional keyword arguments to customize CSV reading:

julia
ts = DyadTimeseries("file:///path/to/data.tsv";
                    independent_var="time",
                    dependent_vars=["x", "y"],
                    delim='\t')  # Tab-separated values

See the CSV.jl documentation for available options.

Helper Functions

julia
# DyadTimeseries
get_independent_var(ts)  # Returns the independent variable name
get_dependent_vars(ts)   # Returns the dependent variable names

# DyadTable
get_columns(tbl)         # Returns the column names

API Reference

DyadData.DyadTimeseries Type
julia
DyadTimeseries(
uri::AbstractString;
independent_var::AbstractString,
dependent_vars::Vector{<:AbstractString},
kwargs...)

Represent a timeseries-like dataset specified by a URI.

Supported URI schemes

  • file:///absolute/path - local file with absolute path

  • dyad://package_name/local_path - dyad package asset (relative to assets/ folder)

  • dyad+juliahub://juliahub.com/datasets/username/dataset_name - JuliaHub dataset

Keyword arguments

  • independent_var: the name of the column that represents the independent variable (usually the time)

  • dependent_vars: a vector of the names of the columns for the dependent variables

When reading CSV files, additional keyword arguments will be passed to CSV.read. This can help with changing settings such as the delimiter used in the file. See https://csv.juliadata.org/stable/reading.html for more details.

Examples

julia
# Local file
ds = DyadTimeseries("file:///home/user/data.csv";
                    independent_var="timestamp",
                    dependent_vars=["x", "y"])

# Dyad package asset
ds = DyadTimeseries("dyad://DyadData/lotka.csv";
                    independent_var="timestamp",
                    dependent_vars=["x(t)", "y(t)"])

# JuliaHub dataset
ds = DyadTimeseries("dyad+juliahub://juliahub.com/datasets/user/dataset_name";
                    independent_var="time",
                    dependent_vars=["var1", "var2"])
source
julia
DyadTimeseries(
data::AbstractMatrix;
independent_var::AbstractString,
dependent_vars::Vector{<:AbstractString})

Represent a timeseries-like dataset that is backed by raw data (matrix).

Keyword arguments

  • independent_var: the name of the column that represents the independent variable (usually the time)

  • dependent_vars: a vector of the names of the columns for the dependent variables

Example

julia
data = rand(10, 3)
ds = DyadTimeseries(data; independent_var="time", dependent_vars=["x", "y"])
source
DyadData.DyadTable Type
julia
DyadTable(
uri::AbstractString;
columns::Vector{<:AbstractString},
kwargs...)

Represent a table specified by a URI.

Supported URI schemes

  • file:///absolute/path - local file with absolute path

  • dyad://package_name/local_path - dyad package asset (relative to assets/ folder)

  • dyad+juliahub://juliahub.com/datasets/username/dataset_name - JuliaHub dataset

Keyword arguments

  • columns: a vector of the names of the columns

When reading CSV files, additional keyword arguments will be passed to CSV.read. This can help with changing settings such as the delimiter used in the file. See https://csv.juliadata.org/stable/reading.html for more details.

Examples

julia
# Local file
tbl = DyadTable("file:///home/user/data.csv";
                columns=["var1", "var2", "var3"])

# Dyad package asset
tbl = DyadTable("dyad://DyadData/data.csv";
                columns=["s1(t)", "s1s2(t)", "s2(t)"])

# JuliaHub dataset
tbl = DyadTable("dyad+juliahub://juliahub.com/datasets/user/dataset_name";
                columns=["x", "y", "z"])
source
julia
DyadTable(
data::AbstractMatrix;
columns::Vector{<:AbstractString})

Represent a table that is backed by raw data (e.g. a vector for a single row).

Keyword arguments

  • columns: a vector of the names of the columns

Example

julia
data = [1.0, 2.0, 3.0]
tbl = DyadTable(data; columns=["x", "y", "z"])
source
julia
DyadTable(
data;
columns::Vector{<:AbstractString})

Represent a table that is backed by raw data (e.g. a matrix).

Keyword arguments

  • columns: a vector of the names of the columns

Example

julia
data = rand(10, 3)
tbl = DyadTable(data; columns=["x", "y", "z"])
source
DyadData.DyadInterpolationTable2D Type
julia
DyadInterpolationTable2D(
uri::AbstractString;
axis1_name::AbstractString,
axis2_name::AbstractString,
data_name::AbstractString,
kwargs...)

Represent a 2D interpolation table specified by a URI.

The CSV file should have the following format:

  • First row: column headers (first cell empty or ignored, then axis2 values)

  • Subsequent rows: first column is axis1 values, remaining columns are data matrix

Example CSV:

,250.0,300.0,350.0,400.0
-5.0,-125.0,-100.0,-75.0,-50.0
-2.0,-50.0,-40.0,-30.0,-20.0
0.0,0.0,0.0,0.0,0.0
2.0,50.0,40.0,30.0,20.0
5.0,125.0,100.0,75.0,50.0

Supported URI schemes

  • file:///absolute/path - local file with absolute path

  • dyad://package_name/local_path - dyad package asset (relative to assets/ folder)

  • dyad+juliahub://juliahub.com/datasets/username/dataset_name - JuliaHub dataset

Keyword arguments

  • axis1_name: the name for the first axis (row labels, e.g., "velocity")

  • axis2_name: the name for the second axis (column labels, e.g., "temperature")

  • data_name: the name for the data values (e.g., "damping_force")

When reading CSV files, additional keyword arguments will be passed to CSV.read.

Examples

julia
# Local file
tbl = DyadInterpolationTable2D("file:///home/user/damper.csv";
                               axis1_name="velocity",
                               axis2_name="temperature",
                               data_name="damping_force")

# Dyad package asset
tbl = DyadInterpolationTable2D("dyad://MyPackage/damper.csv";
                               axis1_name="velocity",
                               axis2_name="temperature",
                               data_name="damping_force")

# Access the data
velocity_values = tbl["velocity"]        # Vector of axis1 values
temp_values = tbl["temperature"]         # Vector of axis2 values
force_matrix = tbl["damping_force"]      # Matrix of data values
source
julia
DyadInterpolationTable2D(
axis1_values::AbstractVector,
axis2_values::AbstractVector,
data_matrix::AbstractMatrix;
axis1_name::AbstractString,
axis2_name::AbstractString,
data_name::AbstractString)

Represent a 2D interpolation table backed by raw data.

Arguments

  • axis1_values: vector of values for the first axis

  • axis2_values: vector of values for the second axis

  • data_matrix: matrix of data values (size: length(axis1_values) × length(axis2_values))

Keyword arguments

  • axis1_name: the name for the first axis (e.g., "velocity")

  • axis2_name: the name for the second axis (e.g., "temperature")

  • data_name: the name for the data values (e.g., "damping_force")

Example

julia
velocity = [-5.0, -2.0, 0.0, 2.0, 5.0]
temperature = [250.0, 300.0, 350.0, 400.0]
forces = [-125.0 -100.0 -75.0 -50.0;
          -50.0 -40.0 -30.0 -20.0;
          0.0 0.0 0.0 0.0;
          50.0 40.0 30.0 20.0;
          125.0 100.0 75.0 50.0]

tbl = DyadInterpolationTable2D(velocity, temperature, forces;
                               axis1_name="velocity",
                               axis2_name="temperature",
                               data_name="damping_force")
source
DyadData.build_table Function
julia
build_table(d::DyadTimeseries)

Build a Table from TypedTables.jl out of the specified timeseries dataset. The column names will correspond to the names of the independent variable & the ones for the dependent variables.

Note that the order of the columns is dictated by the order in the file, not by the order inside the dependent_vars argument for DyadTimeseries. The dependent_vars argument only specifies the available columns to use, not their order.

Example

julia
ds = DyadTimeseries("file:///path/to/data.csv";
                    independent_var="time",
                    dependent_vars=["x", "y"])
table = build_table(ds)
source
julia
build_table(d::DyadTable)

Build a Table from TypedTables.jl out of the specified table dataset. The column names will correspond to the columns.

Example

julia
tbl = DyadTable("file:///path/to/data.csv";
                columns=["x", "y", "z"])
table = build_table(tbl)
source
julia
build_table(d::DyadInterpolationTable2D)

Read the full table from the dataset (all columns). This delegates to the appropriate build_table method for the dataset type.

source