Skip to content

Python

The package is bijective-gamut on PyPI, imported as gamut (installing). This page is generated from its docstrings, which help() shows in Python, and every example has a Python tab. Positions count from 0, and a refusal raises GamutError naming the rule. gamut.FORMAT is the format the package writes, gamut/1, and gamut.__version__ the package's own version.

Files

create

create(
    path: str,
    *,
    writer: str,
    parameters: Mapping[str, Any] | None = None,
    labels: Mapping[str, Any] | None = None,
    scalars: Mapping[str, Any] | None = None,
    fields: Mapping[str, Any] | None = None,
    mesh_labels: Mapping[str, Any] | None = None,
    constants: Mapping[str, Any] | None = None,
    mesh: str | None = None,
    per: str | None = None,
    coordinates: Mapping[str, Any] | None = None,
    precision: Mapping[str, Any] | None = None,
    round_bits: Mapping[str, int] | None = None,
    title: str | None = None,
    license: str | None = None,
    references: str | None = None,
    source: str | None = None,
    portion_marking: str | None = None,
    generalisation_group: str | None = None,
    compression: bool = True,
) -> Study

Creates a data file and returns it open to add runs: use it in a with block, or call close(). Everything the file holds is declared here by name. The destination must not exist; a refused declaration removes only the new file, leaving any existing destination unchanged.

  • parameters: inputs that place a run, as {"alpha": {"units": "degree", "role": "condition"}}. Role "design" (the default), "condition" or "time"; optional "lower", "upper". float64 whatever the values given, or "dtype": "int64" for whole numbers such as a count or a seed.
  • labels: names rather than numbers, as {"status": {"role": "status", "categories": ["converged", "failed"]}}. Role "categorical" (the default), "group", "status" or "split". Only the status "converged" marks a run as usable. A name not yet in "categories" is added to them. bool declares a false/true label.
  • scalars: one number per run, as {"cl": "1"} (its units), or a mapping with "units" and a relation ("of" with "statistic" and "q", or "with_respect_to").
  • fields: one number per node or cell, as {"p": {"units": "Pa", "at": "node"}}; needs a mesh.
  • constants: one number for the whole study, as {"area": (12.5, "m2")}.
  • mesh, per: no mesh (a run table); "shared" (one mesh for every run) with per "fixed", "run" or "group:<label>"; or "changing" (a mesh per run) with per "run" or "group:<label>". With a mesh, coordinates={"units": "m", "axes": 2}.
  • mesh_labels: zones or patches, as {"surface": {"at": "node", "categories": ["field", "wall"]}}.
  • generalisation_group: the group label a fair test holds out whole.
  • title, license, references, source: describe the file.
  • portion_marking: a marking the author set, as free text; evaluate copies it into every answer, extract and write_copy keep it.
  • precision, round_bits, compression: storage; the defaults suit most studies.

Units use the UDUNITS grammar: "1", "degree", "m s-1", "W m-2".

open

open(path: str, mode: str = 'r', threads: int = 1) -> Study

Opens a file to read (mode "r") or to add runs in place (mode "a"), as a Study; use it in a with block, or call close(). threads decompresses large field reads in that many threads.

create_model

create_model(
    path: str,
    *,
    writer: str,
    parameters: Mapping[str, Any] | None = None,
    labels: Mapping[str, Any] | None = None,
    scalars: Mapping[str, Any] | None = None,
    fields: Mapping[str, Any] | None = None,
    constants: Mapping[str, Any] | None = None,
    mesh: str | None = None,
    per: str = "fixed",
    coordinates: Mapping[str, Any] | None = None,
    trained_on: Sequence[str] | None = None,
    title: str | None = None,
    license: str | None = None,
    references: str | None = None,
    source: str | None = None,
    portion_marking: str | None = None,
) -> ModelFile

Creates a model file and returns it open to add models: use it in a with block, or call close(). The destination must not exist. A model file holds no runs. It is declared as create declares a data file (labels, constants, mesh, per, coordinates, title, license, references, source, portion_marking), except:

  • parameters: each needs "lower" and "upper", the range the model was fitted on, as {"alpha": {"units": "degree", "lower": 0.0, "upper": 10.0}}. evaluate warns (W4) of an input outside it, and evaluates it.
  • scalars, fields: each names the model and the output that computes it, as {"cl": {"units": "1", "model": "fit", "output": "cl"}}. A spread or a derivative is declared as in create ("of", "statistic", "with_respect_to"), and the model's call is asked for it.
  • trained_on: the file_id (study.describe()["file_id"]) of each study the model was fitted on.
  • portion_marking: a marking the model's author set, as free text; evaluate copies it into every answer file.

Then add_model(id, model, inputs) stores each model: an object of a type registered with register_model_type, or the built-in Nearest. The file keeps the numbers its save returns, never code; a program that evaluates the file registers the type first. Closing refuses a file with no model, or a result whose model was never added.

The whole pattern, with a model type of one's own:

import numpy as np
import gamut

@gamut.register_model_type
class Polynomial:
    type = "example.polynomial"        # the name the file records

    def __init__(self, coefficients):
        self.coefficients = np.asarray(coefficients, dtype=float)

    @classmethod
    def load(cls, numbers):            # numbers: the dictionary save returned
        return cls(numbers["coefficients"])

    def save(self):                    # numbers, text, booleans, arrays, dictionaries
        return {"coefficients": self.coefficients}

    def call(self, inputs, wanted):    # an array per input, a value per row
        (x,) = inputs.values()
        return {output: {"values": np.polyval(self.coefficients, x)}
                for output, *_ in wanted}

alpha = np.array([0.0, 2.0, 4.0, 6.0])
with gamut.create_model("lift.gamut", writer="my fit",
                        parameters={"alpha": {"units": "degree", "lower": 0.0, "upper": 6.0}},
                        scalars={"cl": {"units": "1", "model": "fit", "output": "cl"}}) as model:
    model.add_model("fit", Polynomial(np.polyfit(alpha, 0.11 * alpha, 2)), inputs=["alpha"])

with gamut.evaluate("lift.gamut", {"alpha": [1.0, 5.0]}) as answer:   # an ordinary data file
    print(answer.get_scalar("cl"))

The study

Study

A study file open to write (from create) or to read (from open).

Writing: add_run, add_runs, set_mesh, notes, set_private. Reading: row_count, names, describe, select, get_parameter, get_label, get_scalar, get_constant, get_field, get_points, get_cells, get_mesh_label, categories, table, related, outside. Rows are positions counted from 0; a rows argument takes a position, a list of them, a slice, or None for every row.

Arrays it returns are the caller's own; it is a context manager, and close() releases the file.

kind property

kind: str

"data" (runs) or "model" (a fitted model).

type property

type: str

"table" (no mesh), "shared" or "changing", after the mesh.

addons property

addons: list[str]

What the file holds beyond its type: "time", "uncertainty", "gradients".

row_count property

row_count: int

The number of rows: runs, or saved steps of transient runs.

notes property

notes: Any

Free-form public notes: a dict when reading; assign items when writing (study.notes["solver"] = "OpenFOAM v2406").

close

close() -> None

Finishes the file. A refused close (a shared mesh never set, a model file without a model) keeps it open to fix and close again.

set_mesh

set_mesh(
    points: Any = None,
    cells: Any = None,
    labels: Mapping[str, Any] | None = None,
    n_nodes: int | None = None,
) -> None

A shared mesh: its cells (types, offsets, connectivity), its fixed mesh labels, and its points when the geometry is fixed. When the coordinates come with the runs, give n_nodes instead of points.

add_run

add_run(
    values: Mapping[str, Any],
    *,
    fields: Mapping[str, Any] | None = None,
    points: Any = None,
    cells: Any = None,
    mesh_labels: Mapping[str, Any] | None = None,
) -> int

Adds one run; returns its id. values gives each parameter, label (by name) and scalar; fields one value per node or cell.

add_runs

add_runs(
    columns: Mapping[str, Any],
    *,
    fields: Mapping[str, Any] | None = None,
    points: Sequence[Any] | None = None,
    cells: Sequence[Any] | None = None,
    mesh_labels: Sequence[Mapping[str, Any]] | None = None,
) -> list[int]

Adds many runs at once: columns maps each name to one value per run, as a dict of arrays or a pandas DataFrame does; fields to runs x sites (a changing mesh: every run's values end to end); points, cells, mesh_labels one per run (or per new group member) when the geometry changes.

names

names(group: str) -> list[str]

The names in a group: "parameters", "labels", "scalars", "constants", "node_fields", "cell_fields", "node_labels", "cell_labels", "tables" or "models".

describe

describe(name: str | None = None) -> dict[str, str]

A concept's attributes as text, or with no name the file's own.

model_numbers

model_numbers(id: str) -> dict[str, Any]

A model's stored numbers (spec section 12), as the dictionary its save gave; its type need not be registered.

get_row_ids

get_row_ids(rows: Any = None) -> np.ndarray

Each row's id, unique in the file and kept when runs are extracted to another; not its position.

get_parameter

get_parameter(
    name: str, rows: Any = None, units: str | None = None
) -> np.ndarray

A parameter's value in each row, in units if given (which needs the units extra, pint).

outside

outside(name: str, rows: Any = None) -> np.ndarray

How far each row's value of the parameter name lies outside the bounds the file declares for it, in the parameter's units: 0 inside or on a bound, value - lower (negative) below, value - upper (positive) above, NaN where the value is missing. A parameter with no bounds is refused. The answer of evaluate keeps the model's bounds, so this says which of its rows lie outside the range the model was fitted on, and how far.

get_label

get_label(name: str, rows: Any = None) -> np.ndarray

A label's category name in each row.

get_label_codes

get_label_codes(name: str, rows: Any = None) -> np.ndarray

A label's code in each row; categories(name)[code] is its name.

get_scalar

get_scalar(name: str, rows: Any = None, units: str | None = None) -> np.ndarray

A scalar result in each row, NaN where a run has none.

get_constant

get_constant(name: str, units: str | None = None) -> Any

A constant's one value for the whole study.

get_cells

get_cells(row: int | None = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]

The cells as VTK gives them: a type per cell, offsets (one more than the cells), node numbers. A changing mesh needs the row.

get_points

get_points(row: int | None = None, units: str | None = None) -> np.ndarray

Node coordinates, nodes x axes. A geometry that changes with the run (per run or per group member) needs the row.

get_field

get_field(
    name: str,
    rows: Any = None,
    nodes: Any = None,
    cells: Any = None,
    units: str | None = None,
) -> np.ndarray

A field: runs x nodes (or cells) on a shared mesh, one row's values on a changing mesh. nodes or cells choose sites by position, as rows chooses rows.

get_mesh_label

get_mesh_label(name: str, row: int | None = None) -> np.ndarray

A mesh label's code per node or cell; categories(name) names the codes (a zone, a boundary patch).

categories

categories(name: str) -> list[str]

The category table of a label or mesh label: code c names entry c.

table

table(
    names: Sequence[str] | None = None, rows: Any = None
) -> dict[str, np.ndarray]

The run table as columns: parameters, labels (as names), scalars.

set_private

set_private(name: str, data: bytes) -> None

A producer's own record: bytes no other reader interprets.

private_names

private_names() -> list[str]

The names of the producer's own records (set_private).

get_private

get_private(name: str) -> bytes

A producer's own record, the bytes set_private stored.

select

select(where: str) -> np.ndarray

The positions of the rows where selects, e.g. "mach > 0.5 and status == converged" (docs/reference/selecting.md).

related

related(name: str) -> dict[str, Any]

What describes name (spec section 6): its std, its quantiles by q, its derivatives by what they are taken with respect to ("coordinates:x" for node position along x), and the parts of its vector, itself among them.

Models

evaluate

evaluate(
    model_path: str,
    inputs: Mapping[str, Any],
    path: str | None = None,
    coordinates: Any = None,
) -> Study

Evaluates a model file on a table of inputs (spec section 7.4) and returns the resulting data file, open to read. An explicit path must not exist. inputs maps each parameter to values and each label to names from the model file's table, all of one length; coordinates (rows x nodes x axes) when a model reads geometry the file does not store. Each model's type must be registered (register_model_type; Nearest is built in). An input outside a parameter's bounds is evaluated, with a warning (W4), and the answer's outside(name) says how far each row lies outside. Without path the data file is temporary and removed when closed: read it in a with block, or give path to keep it.

register_model_type

register_model_type(cls: type) -> type

Registers a model type, so that evaluate runs the files that name it; usable as a decorator. A file records the type's name and its numbers, never its code, so every program that evaluates the file registers the type first. create_model shows the whole pattern.

The class has (SPEC.md section 7.3):

type its name in files; name it after your project: "mylab.polynomial". load(numbers) a classmethod: the model from the dictionary save returned. save() the model's numbers: a dictionary of numbers, text, booleans, NumPy arrays of booleans, int32, int64 or float64, and dictionaries of those. call(inputs, wanted) inputs maps each input the model reads to an array with a value per row. wanted lists what to return, each item one of (output, "values"), (output, "std"), (output, "quantile", q) or (output, "derivative", name). Returns {output: {"values": ..., "std": ..., "quantiles": {q: ...}, "derivatives": {name: ...}}} with what was wanted: a value per row for a scalar, rows x nodes (or cells) for a field.

Nearest

The built-in model: for each input row, the outputs of the closest stored point (spec section 7.5). Evaluated by the core, so every language gets the same answers.

load classmethod

load(numbers: Mapping[str, Any]) -> 'Nearest'

The model from its stored numbers (spec section 7.5).

save

save() -> dict[str, Any]

Its numbers, to store.

call

call(inputs: Mapping[str, Any], wanted: Sequence[tuple]) -> dict[str, dict]

For each input row, the values of the closest stored point.

Copies

append

append(dst: str, src: str) -> int

Adds the rows of src to dst in place and returns how many. The rows get new ids continuing dst's; category tables merge by name. Both files need the same declarations (bounds may differ), the same mesh, and one geometry for a group member found in both.

extract

extract(src: str, dst: str, rows: Any) -> None

Writes the selected rows of src (positions, a slice, or a strictly increasing array) as a new file dst (which must not exist): ids kept, every table whole, a changing mesh's instances reduced to those the rows use.

write_copy

write_copy(src: str, dst: str) -> None

Rewrites src as dst with the default layout (chunks and compression), as for a file another writer chunked finely; the copy keeps the file's identity (file_id, created). The destination must not exist.

Checking

validate

validate(path: str) -> Report

Checks the file at path against every rule it can decide.

Report dataclass

Every finding of one validation.

errors property

errors: list[Finding]

The findings that make the file invalid.

warnings property

warnings: list[Finding]

The findings a reader of a valid file should know.

ok property

ok: bool

Whether the file has no errors.

error_ids property

error_ids: list[str]

The rules the errors break, each once: ["E7", ...].

warning_ids property

warning_ids: list[str]

The rules the warnings name, each once.

Finding dataclass

One finding: rule ("E7", "W2"), severity ("error" | "warning"), path (a concept path such as "node_fields/p", "" for the file) and a message saying what is wrong and what to do.

Errors and the library

GamutError

The one error: rule is the spec's rule id ("E7") or empty, path a concept path or empty, message what went wrong and how to fix it.

library_path

library_path() -> str

The core library this package loaded. MATLAB and Julia can use the same file (it carries its own HDF5 in a wheel).