--- title: "Using fz for parametric studies" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Using fz for parametric studies} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE # requires funz-fz Python package and a configured calculator ) ``` ```{r setup} library(fz) ``` ## Introduction The `fz` package provides R bindings to the [funz-fz](https://pypi.org/project/funz-fz/) Python package. It lets you: - run **parameter sweeps** over any simulation code, - drive **design of experiments** with adaptive algorithms, - read and collect **output files** into data frames. fz works with any simulation code that reads text input files and writes text output files. You describe the model in a small dict (or install it as a named alias with `fz install`). ## Installation ```{r install} # Install the funz-fz Python package into the active reticulate environment fz_install() # Verify fz_available() ``` ## Concepts ### Template files A **template** is an ordinary input file for your simulator with variable placeholders, e.g.: ``` # Perfect Gas parameters pressure = ${P~1.013} # variable P, default 1.013 volume = ${V~22.4} # variable V, default 22.4 moles = ${n~1.0} ``` The placeholder syntax (`$`, `{}`) is defined by the **model** dict. ### Model dict The model dict tells fz how to: 1. find variable placeholders (`varprefix`, `delim`), 2. extract output values from the result files (`output`). ```{r model_dict} model <- list( varprefix = "$", delim = "{}", formulaprefix = "@", commentline = "#", output = list( pressure = "grep 'pressure =' output.txt | cut -d= -f2" ) ) ``` You can also use an installed model alias (a string) instead of an inline dict: ```{r model_alias} fzl()$models # lists installed aliases, e.g. "PerfectGas" ``` ## Basic workflow The typical fz workflow has four steps. ### Step 1 — Inspect the template `fzi` parses the template and returns the variable names together with their default values: ```{r fzi} vars <- fzi("input.txt", model) # $P [1] 1.013 # $V [1] 22.4 # $n [1] 1.0 ``` ### Step 2 — Compile (substitute values) `fzc` writes one copy of the input file per parameter combination into `output_dir`. Each copy goes into a subdirectory named `var1=val1,var2=val2,...`: ```{r fzc_single} # Single case fzc("input.txt", list(P = 2.0, V = 11.2), model, output_dir = "compiled") # writes: compiled/P=2,V=11.2/input.txt (placeholder replaced with 2.0 / 11.2) ``` Supply vectors to generate a full-factorial grid: ```{r fzc_grid} # 2 x 3 = 6 cases fzc("input.txt", list(P = c(1.0, 2.0), V = c(10.0, 20.0, 30.0)), model, output_dir = "compiled") ``` ### Step 3 — Run the model and collect outputs `fzr` wraps steps 1–3 and output collection into a single call. It compiles the template, runs the calculator for every case, and returns a data frame: ```{r fzr} results <- fzr( "input.txt", list(P = c(1.0, 2.0, 3.0), V = 22.4), # 3 cases (V fixed) model, results_dir = "results", calculators = "sh://bash run.sh" # run.sh executes the simulator ) # results is a data frame: # P V pressure # 1 1.0 22.4 ... # 2 2.0 22.4 ... # 3 3.0 22.4 ... ``` The `calculators` argument accepts: - `"sh://bash run.sh"` — run a local shell command - `"sh://"` — execute the input file directly as a shell script - `"ssh://user@host"` — run over SSH ### Step 4 — Read outputs from existing directories If you already ran the simulator externally, `fzo` reads the output files: ```{r fzo} values <- fzo("results/P=2,V=22.4", model) # $pressure [1] "2.026" # Glob to read all cases at once: all_values <- fzo("results/*", model) ``` ## Algorithm-driven design of experiments `fzd` runs an adaptive experiment: the algorithm decides which parameter combinations to evaluate based on previous results. Input variable ranges use `"[min;max]"` strings: ```{r fzd} result <- fzd( "input.txt", list(P = "[1;5]", V = "[10;30]"), model, output_expression = "pressure", algorithm = "algorithms/montecarlo_uniform.py", algorithm_options = "batch_sample_size=10;max_iterations=5;seed=42" ) ``` Algorithms are Python files; `fz` ships several in `algorithms/` (Monte Carlo, surrogate-based optimization, …). You can also write your own. ## Listing installed models `fzl` shows which model aliases and calculators are installed in `~/.fz/`: ```{r fzl} info <- fzl() names(info$models) # e.g. c("PerfectGas", "Moret") names(info$calculators) # e.g. c("sh://") # Filter by pattern fzl(models = "Perfect*") # Probe calculators to verify they are reachable fzl(check = TRUE) ``` ## Best practices 1. **Test with `fzi` first** — verify the correct variable names are found before running anything. 2. **Use `fzc` for a dry run** — inspect compiled files to confirm placeholder substitution is correct. 3. **Start small** — run a handful of cases before launching a large sweep. 4. **Save results** — persist the data frame for reproducibility. ```{r save_results} saveRDS(results, "fz_results.rds") write.csv(results, "fz_results.csv", row.names = FALSE) ``` ## Troubleshooting **`fz` Python package not found:** ```{r troubleshoot_install} fz_install() # install funz-fz into the reticulate environment fz_available() # should return TRUE afterwards ``` **Variables not found in template:** Check that `varprefix` and `delim` in your model dict match the syntax used in your template file. Run `fzi` and inspect the returned list. **Calculator errors:** Run the simulator manually on one compiled directory to confirm it works before using `fzr`. ## Further reading - funz-fz documentation: - reticulate: ## Session info ```{r session_info} sessionInfo() ```