Skip to content

Gamut documentation

Gamut, by Bijective Systems, is an open file format for parametric simulation studies and the models fitted to them. A .gamut file is HDF5 laid out as valid netCDF-4, with libraries for Python, MATLAB, Julia, C++, C and Rust.

A study in four languages

A sweep of a wing's angle of attack: six runs written at once, a seventh added by reopening the file, then the lift of the runs that converged read back. The same program in each language; choosing one here shows it in every example.

alpha = np.arange(0.0, 11.0, 2.0)  # six angles of attack, in degrees
cl = 0.11 * alpha                  # stands for the solver's lift coefficients

with gamut.create("sweep.gamut", writer="example sweep",
                   parameters={"alpha": {"units": "degree", "role": "condition"}},
                   labels={"status": {"role": "status",
                                      "categories": ["converged", "failed"]}},
                   scalars={"cl": "1"}) as study:
    study.add_runs({"alpha": alpha, "cl": cl,
                    "status": ["converged"] * len(alpha)})

with gamut.open("sweep.gamut", mode="a") as study:
    study.add_run({"alpha": 12, "status": "failed"})

with gamut.open("sweep.gamut") as study:
    lift = study.get_scalar("cl", rows=study.select("status == converged"))
alpha = (0:2:10)';   % six angles of attack, in degrees
cl = 0.11 * alpha;   % stands for the solver's lift coefficients

study = gamut.create('sweep.gamut', 'writer', 'example sweep', ...
    'parameters', struct('alpha', struct('units', 'degree', ...
                                         'role', 'condition')), ...
    'labels', struct('status', struct('role', 'status', ...
                                      'categories', {{'converged', 'failed'}})), ...
    'scalars', struct('cl', '1'));
status = repmat({'converged'}, numel(alpha), 1);
study.addRuns(struct('alpha', alpha, 'cl', cl, 'status', {status}));
study.close();

study = gamut.open('sweep.gamut', 'mode', 'a');
study.addRun(struct('alpha', 12, 'status', 'failed'));
study.close();

study = gamut.open('sweep.gamut');
lift = study.getScalar('cl', 'rows', study.select('status == converged'));
study.close();
alpha = collect(0.0:2.0:10.0)  # six angles of attack, in degrees
cl = 0.11 .* alpha             # stands for the solver's lift coefficients

Gamut.create("sweep.gamut"; writer="example sweep",
              parameters=Dict("alpha" => (units="degree", role="condition")),
              labels=Dict("status" => (role="status",
                                       categories=["converged", "failed"])),
              scalars=Dict("cl" => "1")) do study
    status = fill("converged", length(alpha))
    Gamut.add_runs!(study, Dict("alpha" => alpha, "cl" => cl, "status" => status))
end

Gamut.open("sweep.gamut"; mode="a") do study
    Gamut.add_run!(study, Dict("alpha" => 12.0, "status" => "failed"))
end

lift = Gamut.open("sweep.gamut") do study
    Gamut.get_scalar(study, "cl"; rows=Gamut.select(study, "status == converged"))
end
const std::vector<double> alpha = {0, 2, 4, 6, 8, 10};  // angles of attack, degrees
std::vector<double> cl;  // stands for the solver's lift coefficients
for (const double a : alpha) cl.push_back(0.11 * a);

auto study = gamut::create("sweep.gamut", "example sweep");
study.declare_parameter("alpha", "condition", "degree");
study.declare_label("status", "status", {"converged", "failed"});
study.declare_scalar("cl", "1");
const std::vector<std::string> status(alpha.size(), "converged");
study.add_runs({{"alpha", alpha}, {"cl", cl}, {"status", status}});
study.close();

study = gamut::open("sweep.gamut", "a");
study.add_run({{"alpha", 12.0}, {"status", "failed"}});
study.close();

study = gamut::open("sweep.gamut");
const std::vector<double> lift =
    study.get_scalar("cl", study.select("status == converged"));
let alpha = vec![0.0, 2.0, 4.0, 6.0, 8.0, 10.0];   // angles of attack, degrees
let cl: Vec<f64> = alpha.iter().map(|a| 0.11 * a).collect();   // stands for the solver's lift coefficients

let mut study = gamut::create("sweep.gamut", "example sweep", "", "")?;
study.declare_parameter("alpha", "condition", "degree")?;
study.declare_label("status", "status", &["converged", "failed"])?;
study.declare_scalar("cl", "1")?;
let status = vec!["converged"; alpha.len()];
study.add_runs(&[("alpha", alpha.into()), ("cl", cl.into()), ("status", status.into())], &[])?;
study.close()?;

let mut study = gamut::open_append("sweep.gamut")?;
study.add_run(&[("alpha", 12.0.into()), ("status", "failed".into())], &[])?;
study.close()?;

let study = gamut::open("sweep.gamut")?;
let lift = study.get_scalar("cl", study.select("status == converged")?)?;

Each prints:

lift of the runs that converged: 0.00 0.22 0.44 0.66 0.88 1.10

Where next

  • Installing: each language, and the platforms the packages are built for.
  • How it works: what a file holds, and why it is shaped as it is.
  • Examples: a study built up one idea at a time, from a table of runs to meshes, models and VTK, in each language.
  • Reference: each language's calls, the command line and the specification, which defines the format.

For coding agents, llms.txt collects the API's patterns and plain-text links to these pages. To cite Gamut, use its citation metadata.