Skip to content

Commit 67a5560

Browse files
taking automation to 11 example (#302)
* automation 11 example * up version and update message * use Scratch.jl for update messages * some typos * correct usage of Scratch.jl * Add some corrections * try with init version * fix double `init` existing. Co-authored-by: Sebastian Pech <sebastian.pech@me.com>
1 parent 2838129 commit 67a5560

File tree

4 files changed

+67
-26
lines changed

4 files changed

+67
-26
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DrWatson"
22
uuid = "634d3b9d-ee7a-5ddf-bec9-22491ea816e1"
33
repo = "https://github.com/JuliaDynamics/DrWatson.jl.git"
4-
version = "2.7.2"
4+
version = "2.7.3"
55

66
[deps]
77
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
@@ -12,13 +12,15 @@ MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
1212
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
1313
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1414
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
15+
Scratch = "6c6a2e73-6563-6170-7368-637461726353"
1516
UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
1617

1718
[compat]
1819
FileIO = "1.0.6"
1920
JLD2 = "0.4.15"
2021
MacroTools = "0.5"
2122
Requires = "0.5.2, 0.6, 1"
23+
Scratch = "1"
2224
UnPack = "1.0.1"
2325
julia = "1.0"
2426

docs/src/index.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ You can also watch this 8-minutes video that introduces DrWatson in JuliaCon2020
1818
<iframe width="560" height="400" src="https://www.youtube-nocookie.com/embed/jKATlEAu8eE" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
1919
```
2020

21-
2221
To install, simply type `] add DrWatson` in your Julia session.
2322
DrWatson is part of [JuliaDynamics](https://juliadynamics.github.io/JuliaDynamics/), check out our [website](https://juliadynamics.github.io/JuliaDynamics/) for more cool stuff!
2423

docs/src/real_world.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,11 @@ and each file is a dictionary that has my data fields: `:U, :V, :simulation`, bu
100100

101101
## Customizing `savename`
102102
Here is a simple example for customizing [`savename`](@ref). We are using a common struct `Experiment` across different experiments with cats and mice.
103-
In this example we are also using Parameters.jl for a convenient default constructor.
104103

105104
We first define the relevant types.
106105
```@example customizing
107106
using DrWatson, Dates
108-
using Base: @kwdef
107+
using Base: @kwdef # for defining structs with keyword values
109108
110109
# Define a type hierarchy we use at experiments
111110
abstract type Species end
@@ -126,7 +125,7 @@ e1 = Experiment()
126125
e2 = Experiment(species = Cat())
127126
```
128127

129-
For analyzing our experiments we need information about the species used, and to use multiple dispatch latter on we decided to make this information associated with a Type. This is why we defined `Species`.
128+
For analyzing our experiments we need information about the species used, and to use multiple dispatch later on we decided to make this information associated with a Type. This is why we defined `Species`.
130129

131130
Now, we want to customize [`savename`](@ref). We start by extending [`DrWatson.default_prefix`](@ref):
132131
```@example customizing
@@ -514,3 +513,36 @@ julia> expensive_computation(5)
514513
2021-05-19 19:20:28 | n = 4 | error = 0.11 | maxrss = 326.27 MiB
515514
2021-05-19 19:20:29 | n = 5 | error = 0.15 | maxrss = 326.27 MiB
516515
```
516+
517+
## Taking project input-output automation to 11
518+
The point of this section is to show how far one can take the interplay between [`savename`](@ref) and [`produce_or_load`](@ref) to **automate project input-to-output and eliminate as many duplicate lines of code as possible**. Read [Customizing `savename`](@ref) first, as knowledge of that section is used here.
519+
520+
The key ingredient is that [`produce_or_load`](@ref) was made to work well with [`savename`](@ref). You can use this to automate the input-to-output pipeline of your project by following these steps:
521+
1. Define a custom struct that represents the input configuration for an experiment or a simulation.
522+
2. Extend [`savename`](@ref) appropriately for it.
523+
3. Define a "main" function that takes as input an instance of this configuration type, and returns the output of the experiment or simulation as dictionary (We're not changing here the "default" way to save files in Julia as `.jld2` files. To save files this way you need your data to be in a dictionary with `Symbol` as keys).
524+
4. All your input-output scripts are simply put together by first defining the input configuration type, and then calling [`produce_or_load`](@ref) with your pre-defined "main" function (Alternatively, this function can internally call `produce_or_load` and return something else that is of special interest to your specific case).
525+
526+
An example of where this approach is used in the "real world" is e.g. in our paper [Effortless estimation of basins of attraction](https://arxiv.org/abs/2110.04358). Its codebase is here: https://github.com/Datseris/EffortlessBasinsOfAttraction. Don't worry, you need to know nothing about the topic to follow the rest. The point is that we needed to run some kind of simulations for many different dynamical systems, which have different parameters, different dimensionality, etc. But they did have one thing in common: our output was always coming from the same function, `basins_of_attraction`, which allowed using the pipeline we discuss here using [`produce_or_load`](@ref).
527+
528+
So we defined a struct called `BasinConfig` that stored configuration options and system parameters. Then we extended `savename` for it. We defined some function `produce_basins` that takes this configuration file, initializes a dynamical system accordingly, and then makes the output **using `produce_or_load`**. This ensures that we're not running simulations twice if they exist. And keep in mind when you have so many parameters and different possible systems, it is quite easy to unintentionally run the same simulation twice because you "forgot about it". All of this can be found in this file: https://github.com/Datseris/EffortlessBasinsOfAttraction/blob/master/src/produce_basins.jl
529+
530+
The benefit? All of our scripts that actually produce what we care about are this short:
531+
```julia
532+
using DrWatson
533+
@quickactivate :EffortlessBasinsOfAttraction
534+
535+
a, b = 1.4, 0.3
536+
p = @ntuple a b
537+
system = :henon
538+
539+
basin_kwargs = (horizon_limit=100.0, mx_chk_fnd_att=30, mx_chk_lost=2)
540+
Z = 201
541+
xg = range(-1.5, 1.5; length = Z)
542+
yg = range(-0.5, 0.5; length = Z)
543+
grid = (xg, yg)
544+
545+
config = BasinConfig(; system, p, basin_kwargs, grid)
546+
basins, attractors = produce_basins(config)
547+
```
548+
and more importantly, the only lines that are genuinely "copy-pasted" from script to script are the last two. All other lines are unique for each script. This minimization of copy-pasting duplicate information makes the workflow robust and makes bugs easier to find.

src/DrWatson.jl

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,31 +43,39 @@ include("dict_list.jl")
4343

4444
# Functionality that requires Dataframes and other heavy dependencies:
4545
using Requires
46+
47+
# Update messages
48+
using Scratch
49+
const display_update = true
50+
const update_version = "2.7.3"
51+
const update_name = "update_v$update_version"
52+
53+
# Get scratch space for this package
4654
function __init__()
4755
@require DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" begin
4856
include("result_collection.jl")
4957
end
50-
end
58+
if display_update
59+
versions_dir = @get_scratch!("versions")
5160

52-
# Update messages
53-
const display_update = true
54-
const update_version = "2.0.0"
55-
const update_name = "update_v$update_version"
56-
if display_update
57-
if !isfile(joinpath(@__DIR__, update_name))
58-
printstyled(stdout,
59-
"""
60-
\nUpdate message: DrWatson v$update_version
61-
62-
In this new major release, the following breaking changes have occured:
63-
1. DrWatson now uses, and suggests using, JLD2.jl instead of BSON.jl
64-
for saving files.
65-
2. The behavior of `savename` with respect to rounding has changed,
66-
see its docstring for more. (no longer is `1.0` output as `1` in `savename`)
67-
\n
68-
"""; color = :light_magenta)
69-
touch(joinpath(@__DIR__, update_name))
70-
end
71-
end
61+
if !isfile(joinpath(versions_dir, update_name))
7262

63+
printstyled(stdout,
64+
"""
65+
\nUpdate message: DrWatson v$update_version
66+
67+
* New section "Taking project input-output automation to 11" in the documentation.
68+
It showcases how to eliminate code duplication and streamline your simulation setup
69+
and run phase using `savename` and `produce_or_load`.
70+
* By default now `gitpatch` is NOT saved when calling `tag!` and derivative functions.
71+
This is due to an unknown problem that causes collecting the git patch to
72+
never hault, potentially not saving a user's output.
73+
\n
74+
"""; color = :light_magenta)
75+
touch(joinpath(versions_dir, update_name))
76+
end
77+
end
7378
end
79+
80+
81+
end # Module

0 commit comments

Comments
 (0)