# Naming Simulations

Here we overview functionality that helps you quickly produce containers of parameters and name them using a consistent and intuitive naming scheme.

## Naming Schemes

A robust naming scheme allows you to create quick names for simulations, create lists of simulations, check existing simulations, etc. More importantly it allows you to easily create simulation-based names consistently and deterministically.

This is what the function savename does. Of course, you don't have to use it only for using names to save files. You could use it for anything that fits you (like e.g. adding identifiers to tabular data). savename is also surprisingly useful for creating titles of figures, e.g. savename(c; connector = ", ").

DrWatson.savenameFunction
savename([prefix,], c [, suffix]; kwargs...)

Create a shorthand name, commonly used for saving a file or as a figure title, based on the parameters in the container c (Dict, NamedTuple or any other Julia composite type). If provided use the prefix and end the name with .suffix (i.e. you don't have to include the . in your suffix).

The function chains keys and values into a string of the form:

key1=val1_key2=val2_key3=val3

while the keys are sorted alphabetically by default. If you provide the prefix/suffix the function will do:

prefix_key1=val1_key2=val2_key3=val3.suffix

assuming you chose the default connector, see below. Notice that prefix should not contain path separators to avoid compatibility issues on different operating systems. For constructing paths, use the *dir() methods or joinpath with savename() as the last parameter. See default_prefix for more.

savename can be conveniently combined with @dict or @ntuple. See also parse_savename and @savename.

Keywords

• allowedtypes = default_allowed(c) : Only values of type subtyping anything in allowedtypes are used in the name. By default this is (Real, String, Symbol, TimeType).
• accesses = allaccess(c) : specify which specific keys you want to use with the keyword accesses. By default this is all possible keys c can be accessed with, see allaccess.
• ignores = allignore(c) : You can also specify keys that you want to ignore with the keyword ignores. By default this is an empty tuple, see allignore. (keys in ignore are ignored even if they are in accesses)
• digits = 3 : Floating point values are rounded to digits. In addition if the following holds:
round(val; digits = digits) == round(Int, val)
then the integer value is used in the name instead.
• scientific = nothing : Number of significant digits used for rounding of floating point values using scientific notation (e.g. 1.65e-7). If nothing, normal rounding is done.
• connector = "_" : string used to connect the various entries.
• expand::Vector{String} = default_expand(c) : keys that will be expanded to the savename of their contents, to allow for nested containers. By default is empty. Notice that the type of the container must also be allowed in allowedtypes for expand to take effect! Empty containers are always skipped and the savename of the nested arguments is always called with its default arguments (so customization here is possible only by rolling your own container type). If the savename of the nested containers is "", it is also skipped.
• sort = true : Indicate whether the pairs are sorted alphabetically by keys. If not, they are sorted by the order of accesses. WARNING: the default accesses is not deterministic for Dict inputs.

Examples

d = (a = 0.153456453, b = 5.0, mode = "double")
savename(d; digits = 4) == "a=0.1535_b=5_mode=double"
savename("n", d) == "n_a=0.153_b=5_mode=double"
savename(d, "n") == "a=0.153_b=5_mode=double.n"
savename("n", d, "n"; connector = "-") == "n-a=0.153-b=5-mode=double.n"
savename(d, allowedtypes = (String,)) == "mode=double"

rick = (never = "gonna", give = "you", up = "!");
savename(rick) == "give=you_never=gonna_up=!" # keys are sorted!
savename(rick; ignores = ["up"]) == "give=you_never=gonna"
source

Notice that this naming scheme integrates perfectly with Parameters.jl.

## Convenience functions

Convenience functions are provided to shorten common function calls and easily create named tuples, dictionaries as well as switch between them:

DrWatson.@dictMacro
@dict vars...

Create a dictionary out of the given variables that has as keys the variable names and as values their values.

Notice: @dict a b is the correct way to call the macro. @dict a, b is incorrect. If you want to use commas you have to do @dict(a, b).

Examples

julia> ω = 5; χ = "test"; ζ = π/3;

julia> @dict ω χ ζ
Dict{Symbol,Any} with 3 entries:
:ω => 5
:χ => "test"
:ζ => 1.0472
source
DrWatson.@ntupleMacro
@ntuple vars...

Create a NamedTuple out of the given variables that has as keys the variable names and as values their values.

Examples

julia> ω = 5; χ = "test"; ζ = 3.14;

julia> @ntuple ω χ ζ
(ω = 5, χ = "test", ζ = 3.14)
source
DrWatson.dict2ntupleFunction
dict2ntuple(dict) -> ntuple

Convert a dictionary (with Symbol or String as key type) to a NamedTuple.

source

Notice that we also re-export the convenient @pack!, @unpack tools from UnPack.jl, because they play very well with @dict and similar functions. Be aware of the syntactic , difference: d = @dict a b c versus @unpack a, b, c = d.

UnPack.@unpackMacro
@unpack a, b, c, ... = dict_or_typeinstance

Unpack fields/properties/keys from a composite type, a Dict{Symbol}, a Dict{String}, or a module into variables.

Example with dict:

d = Dict{Symbol,Any}(:a=>5.0,:b=>2,:c=>"Hi!")
@unpack a, c = d
a == 5.0 #true
c == "Hi!" #true

Example with type:

struct A; a; b; c; end
d = A(4,7.0,"Hi")
@unpack a, c = d
a == 4 #true
c == "Hi" #true

Note that its functionality can be extended by adding methods to the UnPack.unpack function.

UnPack.@pack!Macro
@pack! dict_or_typeinstance = a, b, c, ...

Pack variables into a mutable composite type, a Dict{Symbol}, or a Dict{String}.

Example with dict:

a = 5.0
c = "Hi!"
d = Dict{Symbol,Any}()
@pack! d = a, c
d # Dict{Symbol,Any}(:a=>5.0,:c=>"Hi!")

Example with type:

a = 99
c = "HaHa"
mutable struct A; a; b; c; end
d = A(4,7.0,"Hi")
@pack! d = a, c
d.a == 99 #true
d.c == "HaHa" #true

Note that its functionality can be extended by adding methods to the UnPack.pack! function.

To "pack" immutables use the package Setfield.jl.

## Customizing savename

You can customize savename for your own Types. For example you could make it so that it only uses some specific keys instead of all of them, only specific types, or you could make it access data in a different way (maybe even loading files!). You can even make it have a custom prefix!

To do that you may extend any of the following functions:

DrWatson.allaccessFunction
allaccess(c)

Return all the keys c can be accessed using access. For dictionaries/named tuples this is keys(c), for everything else it is fieldnames(typeof(c)).

source
DrWatson.accessFunction
access(c, key)

Access c with given key. For AbstractDict this is getindex, for anything else it is getproperty.

access(c, keys...)

When given multiple keys, access is called recursively, i.e. access(c, key1, key2) = access(access(c, key1), key2) and so on. For example, if c, c.k1 are NamedTuples then access(c, k1, k2) == c.k1.k2.

Note

Please only extend the single key method when customizing access for your own Types.

source
DrWatson.default_prefixFunction
default_prefix(c) = ""

Return the prefix that will be used by default in savename.

Notice that if default_prefix is defined for c but a prefix is also given to savename then the two values are merged via joinpath for convenience (if they are not the same of course).

E.g. defining default_prefix(c::MyType) = "lala" and calling

savename(datadir(), mytype)

will in fact return a string that looks like

"path/to/data/lala_p1=..."

This allows savename to work well with produce_or_load.

source

See Real World Examples for an example of customizing savename. Specifically, have a look at savename and nested containers for a way to

## Reverse-engineering savename

DrWatson.parse_savenameFunction
parse_savename(filename::AbstractString; kwargs...)

Try to convert a shorthand name produced with savename into a dictionary containing the parameters and their values, a prefix and suffix string. Return prefix, parameters, suffix.

Parsing the key-value parts of filename is performed under the assumption that the value is delimited by = and the closest connector. This allows the user to have connector (eg. _) in a key name (variable name) but not in the value part.

Keywords

• connector = "_" : string used to connect the various entries.
• parsetypes = (Int, Float64) : tuple used to define the types which should be tried when parsing the values given in filename. Fallback is String.
source