Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AbstractSchedules for scheduling output and diagnostics #1070

Merged
merged 25 commits into from
Oct 19, 2020
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
580d3a1
Adds AbstractTrigger and refactors diagnostics and output writers to …
glwagner Oct 14, 2020
8d141b6
Bugfix in JLD2OutputWriter constructor
glwagner Oct 14, 2020
30172c0
Merge branch 'master' into glw/flexible-output-criteria
glwagner Oct 15, 2020
4e0c423
Adds AbstractSchedule concept and integrates it into Diagnostics and …
glwagner Oct 16, 2020
fa3ad84
Better show methods for NetCDF and JLD2
glwagner Oct 16, 2020
1012779
Updates validation scripts for new OutputWriters API
glwagner Oct 16, 2020
cec6475
Merge branch 'master' into glw/flexible-output-criteria
glwagner Oct 16, 2020
e734d8a
Assumed units for showing stuff and updated docs
glwagner Oct 16, 2020
cbf3d69
Fixes regression tests and output writers tests
glwagner Oct 16, 2020
2ebba8e
Fix typo in netcdf output writer constructor
glwagner Oct 16, 2020
75e2fcf
Cleans up test output writers
glwagner Oct 17, 2020
9b8baad
Update test_output_writers.jl
glwagner Oct 17, 2020
84a5f2c
Update docs/src/model_setup/output_writers.md
glwagner Oct 17, 2020
87e2440
Update jldoctests
ali-ramadhan Oct 17, 2020
3f56879
Fix checkpointer jldoctest
ali-ramadhan Oct 17, 2020
6ba7837
Merge branch 'glw/flexible-output-criteria' of github.com:CliMA/Ocean…
ali-ramadhan Oct 17, 2020
a33af8a
Update internal wave example
ali-ramadhan Oct 17, 2020
0d717ce
Import IterationInterval into one_dimensional_diffusion
glwagner Oct 17, 2020
6d071e6
Merge branch 'glw/flexible-output-criteria' of https://github.com/Cli…
glwagner Oct 17, 2020
c5a71ea
Update pipeline.yml
glwagner Oct 17, 2020
974039d
Update output_writers.md
glwagner Oct 18, 2020
87e4d71
Update windowed_time_average.jl
glwagner Oct 18, 2020
8abd2c2
Update output_writers.md
glwagner Oct 18, 2020
e16979f
Unique filenames for output writer doctests
glwagner Oct 18, 2020
ecb956f
Adds WindowedTimeAverage page to docs library
glwagner Oct 19, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 48 additions & 105 deletions docs/src/model_setup/output_writers.md
Original file line number Diff line number Diff line change
@@ -1,130 +1,73 @@
# Output writers

Saving model data to disk can be done in a flexible manner using output writers. The two main output writers currently
implemented are a NetCDF output writer (relying on [NCDatasets.jl](https://github.com/Alexander-Barth/NCDatasets.jl))
and a JLD2 output writer (relying on [JLD2.jl](https://github.com/JuliaIO/JLD2.jl)).
`AbstractOutputWriter`s save data to disk.
`Oceananigans` provides three ways to write output:

Output writers are stored as a list of output writers in `simulation.output_writers`. Output writers can be specified
at model creation time or be specified at any later time and appended (or assigned with a key value pair) to
`simulation.output_writers`.
1. `NetCDFOutputWriter` for output of arrays and scalars that uses [NCDatasets.jl](https://github.com/Alexander-Barth/NCDatasets.jl)
2. `JLD2OutputWriter` for arbitrary julia data structures that uses [JLD2.jl](https://github.com/JuliaIO/JLD2.jl)
3. `Checkpointer` that automatically saves as much model data as possible, using [JLD2.jl](https://github.com/JuliaIO/JLD2.jl)

## NetCDF output writer

Model data can be saved to NetCDF files along with associated metadata. The NetCDF output writer is generally used by
passing it a dictionary of (label, field) pairs and any indices for slicing if you don't want to save the full 3D field.

The following example shows how to construct NetCDF output writers for two different kinds of outputs (3D fields and
slices) along with output attributes

```jldoctest netcdf1
using Oceananigans, Oceananigans.OutputWriters

grid = RegularCartesianGrid(size=(16, 16, 16), extent=(1, 1, 1));

model = IncompressibleModel(grid=grid);

simulation = Simulation(model, Δt=12, stop_time=3600);

fields = Dict("u" => model.velocities.u, "T" => model.tracers.T);

simulation.output_writers[:field_writer] =
NetCDFOutputWriter(model, fields, filepath="output_fields.nc", time_interval=60)

# output
NetCDFOutputWriter (time_interval=60): output_fields.nc
├── dimensions: zC(16), zF(17), xC(16), yF(16), xF(16), yC(16), time(0)
└── 2 outputs: ["T", "u"]
```

```jldoctest netcdf1
simulation.output_writers[:surface_slice_writer] =
NetCDFOutputWriter(model, fields, filepath="output_surface_xy_slice.nc",
time_interval=60, field_slicer=FieldSlicer(k=grid.Nz))
The `Checkpointer` is discussed on a separate documentation page.

# output
NetCDFOutputWriter (time_interval=60): output_surface_xy_slice.nc
├── dimensions: zC(1), zF(1), xC(16), yF(16), xF(16), yC(16), time(0)
└── 2 outputs: ["T", "u"]
```

Writing a scalar, profile, and slice to NetCDF:

```jldoctest
using Oceananigans, Oceananigans.OutputWriters

grid = RegularCartesianGrid(size=(16, 16, 16), extent=(1, 2, 3));
## Basic usage

model = IncompressibleModel(grid=grid);
`NetCDFOutputWriter` and `JLD2OutputWriter` require four inputs:

simulation = Simulation(model, Δt=1.25, stop_iteration=3);
1. The `model` from which output data is sourced (required to initialize the `OutputWriter`).
2. A key-value pairing of output "names" and "output" objects. `JLD2OutputWriter` accepts `NamedTuple`s and `Dict`s;
`NetCDFOutputWriter` accepts `Dict`s with string-valued keys. Output objects are either `AbstractField`s or
functions that return data when called via `func(model)`.
3. A `schedule` on which output is written. `TimeInterval`, `IterationInterval`, `WallTimeInterval` schedule
periodic output according to the simulation time, simulation interval, or "wall time" (the physical time
according to a clock on your wall). A fourth `schedule` called `AveragedTimeInterval` specifies
periodic output that is time-averaged over a `window` prior to being written.
4. The filename and directory. Currently `NetCDFOutputWriter` accepts one `filepath` argument, while
`JLD2OutputWriter` accepts a filename `prefix` and `dir`ectory.

f(model) = model.clock.time^2; # scalar output
Other important keyword arguments are

g(model) = model.clock.time .* exp.(znodes(Cell, grid)); # vector/profile output
* `field_slicer::FieldSlicer` for outputting subregions, two- and one-dimensional slices of fields.
By default a `FieldSlicer` is used to remove halo regions from fields so that only the physical
portion of model data is saved to disk.

h(model) = model.clock.time .* ( sin.(xnodes(Cell, grid, reshape=true)[:, :, 1])
.* cos.(ynodes(Face, grid, reshape=true)[:, :, 1])); # xy slice output
* `array_type` for specifying the type of the array that holds outputted field data. The default is
`Array{Float32}`, or arrays of single-precision floating point numbers.

outputs = Dict("scalar" => f, "profile" => g, "slice" => h);
Once an `OutputWriter` is created, it can be used to write output by adding it the
ordered dictionary `simulation.output_writers`. prior to calling `run!(simulation)`.

dims = Dict("scalar" => (), "profile" => ("zC",), "slice" => ("xC", "yC"));
More specific detail about the `NetCDFOutputWriter` and `JLD2OutputWriter` is given below.

output_attributes = Dict(
"scalar" => Dict("longname" => "Some scalar", "units" => "bananas"),
"profile" => Dict("longname" => "Some vertical profile", "units" => "watermelons"),
"slice" => Dict("longname" => "Some slice", "units" => "mushrooms")
);

global_attributes = Dict("location" => "Bay of Fundy", "onions" => 7);
## NetCDF output writer

simulation.output_writers[:stuff] =
NetCDFOutputWriter(model, outputs,
iteration_interval=1, filepath="stuff.nc", dimensions=dims, verbose=true,
global_attributes=global_attributes, output_attributes=output_attributes)
Model data can be saved to NetCDF files along with associated metadata. The NetCDF output writer is generally used by
passing it a dictionary of (label, field) pairs and any indices for slicing if you don't want to save the full 3D field.

# output
NetCDFOutputWriter (iteration_interval=1): stuff.nc
├── dimensions: zC(16), zF(17), xC(16), yF(16), xF(16), yC(16), time(0)
└── 3 outputs: ["profile", "slice", "scalar"]
```@docs
NetCDFOutputWriter
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯


See [`NetCDFOutputWriter`](@ref) for more details and options.

## JLD2 output writer

JLD2 is a an HDF5 compatible file format written in pure Julia and is generally pretty fast. JLD2 files can be opened in
Python with the [h5py](https://www.h5py.org/) package.

The JLD2 output writer is generally used by passing it a dictionary or named tuple of (label, function) pairs where the
functions have a single input `model`. Whenever output needs to be written, the functions will be called and the output
of the function will be saved to the JLD2 file. For example, to write out 3D fields for w and T and a horizontal average
of T every 1 hour of simulation time to a file called `some_data.jld2`

```julia
using Oceananigans
using Oceananigans.OutputWriters
using Oceananigans.Utils: hour, minute
JLD2 is a fast HDF5 compatible file format written in pure Julia.
JLD2 files can be opened in julia and in Python with the [h5py](https://www.h5py.org/)
package.
glwagner marked this conversation as resolved.
Show resolved Hide resolved

model = IncompressibleModel(grid=RegularCartesianGrid(size=(16, 16, 16), extent=(1, 1, 1)))
simulation = Simulation(model, Δt=12, stop_time=1hour)
The `JLD2OutputWriter` receives either a `Dict`ionary or `NamedTuple` containing
`name, output` pairs. The `name` can be a symbol or string. The `output` must either be
an `AbstractField` or a function called with `func(model)` that returns arbitrary output.
Whenever output needs to be written, the functions will be called and the output
of the function will be saved to the JLD2 file.

function init_save_some_metadata(file, model)
file["author"] = "Chim Riggles"
file["parameters/coriolis_parameter"] = 1e-4
file["parameters/density"] = 1027
end

T_avg = AveragedField(model.tracers.T, dims=(1, 2))
```@docs
JLD2OutputWriter
```

outputs = Dict(
:w => model -> model.velocities.u,
:T => model -> model.tracers.T,
:T_avg => model -> T_avg(model)
)
## Time-averaged output

jld2_writer = JLD2OutputWriter(model, outputs, init=init_save_some_metadata, interval=20minute, prefix="some_data")
Time-averaged output is specified by setting the `schedule` keyword argument for either `NetCDFOutputWriter` or
`JLD2OutputWriter` to `AveragedTimeInterval`:

push!(simulation.output_writers, jld2_writer)
```@docs
AveragedTimeInterval
```

See [`JLD2OutputWriter`](@ref) for more details and options.
8 changes: 4 additions & 4 deletions examples/eady_turbulence.jl
Original file line number Diff line number Diff line change
Expand Up @@ -287,12 +287,12 @@ nothing # hide
# we create a `JLD2OutputWriter` that saves `ζ` and `δ` and add it to
# `simulation`.

using Oceananigans.OutputWriters: JLD2OutputWriter
using Oceananigans.OutputWriters: JLD2OutputWriter, TimeInterval

simulation.output_writers[:fields] = JLD2OutputWriter(model, (ζ=ζ, δ=δ),
time_interval = 2hour,
prefix = "eady_turbulence",
force = true)
schedule = TimeInterval(2hour),
prefix = "eady_turbulence",
force = true)
nothing # hide

# All that's left is to press the big red button:
Expand Down
2 changes: 1 addition & 1 deletion examples/langmuir_turbulence.jl
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ using Oceananigans.Utils: minute
fields_to_output = merge(model.velocities, model.tracers, (νₑ=model.diffusivities.νₑ,))

simulation.output_writers[:fields] =
JLD2OutputWriter(model, fields_to_output, time_interval = 2minute,
JLD2OutputWriter(model, fields_to_output, schedule=TimeInterval(2minute),
prefix = "langmuir_turbulence", force = true)
nothing # hide

Expand Down
2 changes: 1 addition & 1 deletion examples/ocean_wind_mixing_and_convection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ nothing # hide

## Instantiate a JLD2OutputWriter to write fields. We will add it to the simulation before
## running it.
field_writer = JLD2OutputWriter(model, fields_to_output; time_interval=hour/4,
field_writer = JLD2OutputWriter(model, fields_to_output; schedule=TimeInterval(hour/4),
prefix="ocean_wind_mixing_and_convection", force=true)

# ## Running the simulation
Expand Down
2 changes: 1 addition & 1 deletion examples/one_dimensional_diffusion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ using Oceananigans.OutputWriters: JLD2OutputWriter

simulation.output_writers[:temperature] =
JLD2OutputWriter(model, model.tracers, prefix = "one_dimensional_diffusion",
iteration_interval = 100, force = true)
schedule=IterationInterval(100), force = true)

## Run simulation for 10,000 more iterations
simulation.stop_iteration += 10000
Expand Down
2 changes: 1 addition & 1 deletion examples/two_dimensional_turbulence.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ simulation = Simulation(model, Δt=0.1, stop_iteration=2000)
using Oceananigans.OutputWriters

simulation.output_writers[:fields] =
JLD2OutputWriter(model, (ω = ω,), iteration_interval = 20,
JLD2OutputWriter(model, (ω = ω,), schedule=IterationInterval(20),
prefix = "2d_turbulence_vorticity", force = true)

# ## Running the simulation
Expand Down
10 changes: 6 additions & 4 deletions src/Diagnostics/Diagnostics.jl
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
module Diagnostics

export
TimeSeries, FieldMaximum,
CFL, AdvectiveCFL, DiffusiveCFL, NaNChecker,
run_diagnostic!
NaNChecker,
FieldMaximum,
CFL, AdvectiveCFL, DiffusiveCFL,
run_diagnostic!,
TimeInterval, IterationInterval, WallTimeInterval

using Oceananigans
using Oceananigans.Operators
using Oceananigans.Utils: TimeInterval, IterationInterval, WallTimeInterval

using Oceananigans: AbstractDiagnostic

include("nan_checker.jl")
include("time_series.jl")
include("field_maximum.jl")
include("cfl.jl")

Expand Down
2 changes: 1 addition & 1 deletion src/Diagnostics/cfl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ using Oceananigans.TurbulenceClosures: cell_diffusion_timescale
An object for computing the Courant-Freidrichs-Lewy (CFL) number.
"""
struct CFL{D, S}
Δt :: D
Δt :: D
timescale :: S
end

Expand Down
17 changes: 7 additions & 10 deletions src/Diagnostics/nan_checker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,18 @@

A diagnostic that checks for `NaN` values and aborts the simulation if any are found.
"""
struct NaNChecker{F} <: AbstractDiagnostic
iteration_interval :: Int
fields :: F
struct NaNChecker{T, F} <: AbstractDiagnostic
schedule :: T
fields :: F
end

"""
NaNChecker(model; iteration_interval, fields)
NaNChecker(; schedule, fields)

Construct a `NaNChecker` for `model`. `fields` should be a `Dict{Symbol,Field}`. An
`iteration_interval` should be passed to indicate how often to check for NaNs (in
number of iterations).
Returns a `NaNChecker` that checks for `NaN` anywhere within `fields`
when `schedule` actuates.
"""
function NaNChecker(model; iteration_interval, fields)
return NaNChecker(iteration_interval, fields)
end
NaNChecker(model=nothing; schedule, fields) = NaNChecker(schedule, fields)

function run_diagnostic!(nc::NaNChecker, model)
for (name, field) in nc.fields
Expand Down
Loading