Model Axes

An axis is a configurable dimension for your model’s runs that can be wired into other parts of your model configuration to enable powerful features like fanning a single model out to N model runs per trigger based on a combination or partition of input data.

For example, declare a ref_year axis with the values 2023, 2024, and 2025, and every trigger queues three runs instead of one: the same model, run once per reference year, each run reading its own year’s slice of the same input data.

A trigger is whatever starts a run — new input data arriving, or someone pressing “Run model” — as described in Run modes and scheduling.

When to Use an Axis

Declare an axis when the same forecast logic runs over a dimension of your data and each version reads a different slice of one file: for example, one run per reference year. Your model code stays as it is, and every result stays under a single model that people switch between in the pd4castr app.

Sensitivities are distinctly different in that they change the input data for a specific run via SQL, whereas an axis is used to trigger additional model runs for the inputs you already have.

Combined, they can be used to trigger fan-out model runs of individualised input data, with sensitivities applied to each run.

How Axes Change Your Runs

A trigger queues one run per combination of axis values, then multiplies that by your sensitivities:

runs per trigger = (product of each axis's value count) × (1 base run + number of sensitivities)

A model with a three-value ref_year axis, a second axis with two values, and three sensitivities queues 3 × 2 × (1 + 3) = 24 runs every time it triggers. All 24 share the same runDatetime.

Each axis’s default value picks the model’s base run: the run people get without choosing anything. The app opens on it, and the platform’s latest-run API endpoints answer with it. When a model declares more than one axis, the base run is the combination of every axis’s default.

The platform caps a model at 200 runs per trigger and 100 values per axis. The CLI checks your configuration against them on your own machine before it uploads anything, so a publish over either limit fails in your terminal. If you need to trigger more runs than these limits allow, contact support to discuss your requirements.

What Your Model Container Sees

Adding an axis changes nothing inside your model code. The container receives the same environment variables it always did: one INPUT_<KEY>_URL per input, plus OUTPUT_URL and MODEL_RUN_ID. No environment variable carries the run’s axis values.

What changes is the file behind INPUT_<KEY>_URL. For a partitioned input, that file holds only the rows for this run’s value, so the 2024 run downloads the 2024 rows and nothing else. The column the file was partitioned on stays in the file, which means your existing queries that filter or group by it keep working — they just match every row now.

Configuration

Declare axes in the axes array in .pd4castrrc.json. Each entry has the following fields.

FieldTypeRequiredDescription
keystringYesStable identifier for this axis. Lowercase alphanumeric, with hyphens/underscores. Must be unique within the model.
labelstringNoName shown above the axis’s values in the pd4castr app.
descriptionstringNoHelp text shown in the axis’s tooltip in the pd4castr app.
typestringYesThe value type: number, string, or boolean.
valuesFromobjectYesWhere the axis’s values come from. Only { "kind": "static", "values": [...] } is supported today, with at least one value.
defaultmatches typeYesThe value that defines this axis’s base run. Must be one of valuesFrom.values.

Declared values must be unique within the axis and match the declared type. A number value must be a whole number between zero and 9,007,199,254,740,991. Both limits keep a value’s text identical everywhere the platform writes it, whether that’s a storage path, a label, or a query. A string value can’t be empty. It can hold letters, digits, underscores, dots, and hyphens, and it must start with a letter or a digit.

Here’s a ref_year axis with three fixed values and a default:

{
  "axes": [
    {
      "key": "ref_year",
      "label": "Reference Year",
      "type": "number",
      "valuesFrom": {
        "kind": "static",
        "values": [2023, 2024, 2025]
      },
      "default": 2024
    }
  ]
}

Partitioning Inputs and Datasets by an Axis

You can partition an input or dataset by mapping an axis to a specific column in its file, using the input’s or dataset’s own axes field. Each run then reads only the rows carrying its own axis value. A file that maps no axis is shared, and every run gets all of it.

An example:

"inputs": [
  {
    "key": "forecast_values",
    "axes": { "ref_year": { "kind": "partition", "column": "REF_YR" } }
  },
  { "key": "mt" }
]

forecast_values reads only its own year’s rows on every run. mt maps no axis, so every run gets the whole file, unchanged.

Four rules govern partitioning:

  • Only a static input can be partitioned, and it must declare both uploadFileFormat and targetFileFormat as parquet.
  • A partitioned dataset must declare fileFormat as parquet.
  • An input or dataset can be partitioned on at most one axis.
  • The key naming the partition must match an axis declared in the model’s axes array.

Axis Values in Model Run Views

A model run view parameter can name an axis with defaultFrom: "run.axis.<key>". The platform accepts that reference and checks the key against your axes array, but it doesn’t yet hand the running view its axis value: an optional parameter resolves to null, and a required one is rejected when the view runs. Set that parameter another way for now.

What You See in the App

You can configure how axes are represented in the pd4castr app with the label and description fields. The app opens on the base run, with every axis at its default.

Changing Axes on a Published Model

Publishing an existing model comes in two shapes: updating its current revision in place, or creating a new revision. Updating an existing model covers how you pick between them. Axes constrain that choice.

Updating a model’s current revision in place can’t add an axis, remove an axis, or move an axis’s default to a different value — any of those changes the revision’s base run, so publish fails and you publish a new revision instead. Adding or removing a non-default value in place is allowed, but removing will hide the related model run variants from the model run history, though their data stays available through the API.

An axis key’s type can’t change across the revisions of one model group, whether you’re updating in place or publishing a new revision. To change an axis’s value type, declare a new key instead of reusing the old one.

Custom Run Datetime Queries

If your model declares axes, keep your custom run datetime query reading only shared inputs, meaning the ones that map no axis. Every run from a trigger is supposed to carry the same runDatetime. A query reading a partitioned input would read a different set of rows in each run and hand each one a different runDatetime, which breaks that.

Next Steps

  • See Model inputs for the full input configuration reference, including static and data-fetcher inputs.
  • See Model datasets for publishing reference data alongside your model.
  • See Sensitivities for how sensitivity runs multiply alongside axis runs.
  • See Publishing for the full publish workflow.
  • See the Configuration file reference for the complete schema.