fourierflows / fourierflows.jl Goto Github PK
View Code? Open in Web Editor NEWTools for building fast, hackable, pseudospectral partial differential equation solvers on periodic domains
Home Page: https://bit.ly/FourierFlows
License: MIT License
Tools for building fast, hackable, pseudospectral partial differential equation solvers on periodic domains
Home Page: https://bit.ly/FourierFlows
License: MIT License
Let's remove the sandbox. I don't think anybody out there benefits from that. To the least, I think, it only adds confusion.
@JuliaRegistrator register()
Rather than Array
s of Array
s. A named tuple is better (it has names) and we get loop unrolling, which is probably faster than using broadcasting as done, for example, here:
FourierFlows.jl/src/timesteppers.jl
Line 211 in 1028120
In its current form, the ProblemDiagnostic
only supports function that output scalars.
What if we want to save, e.g., the kx=0 component of the solution every 10 time-steps as a diagnostic?
We need to generalize:
FourierFlows.jl/src/diagnostics.jl
Lines 8 to 18 in a479cc7
It seems that our docs don't keep all the versions of our releases... See, e.g., here: https://fourierflows.github.io/FourierFlows.jl/stable/
@mortenpi, do you have any insights on this? Is something in our .travis.yml
or docs/make.jl
files causing this?
Btw, possibly unrelated, should we remove the cap on Documenter.jl
v0.19 and use the latest version?
We need to restart / finish the time-stepper overhaul. These are the issues that should be addressed:
The function get_etd_coeffs
is not as general or well-written as it could be.
I believe that all "helper" functions (like get_etd_coeffs
) should be moved to the top of the file. The time-stepper definitions can then go below. This may improve code readability.
The time-stepper definitions / constructors are too restrictive. Many of them explicitly require 2- or 3-dimensional solution arrays, which is unnecessary. We should use type parameterization to generalize these definitions.
The syntax used in the step-forward functions is inconsistent (regarding the use of @.
, mostly), and sometimes not as efficient as it could be. For example, I believe that the algorithm
a .= b .+ c
a .*= d
is less efficient than
a .= d .* (b .+ c)
The reason is because in the first example, the compiler does not know that it can pass the values of d, b, c to the processor simultaneously. I think in the second example, the compiler may be able to produce faster code that sends the values of all 3 array to the processors for an operation, thus consolidating and perhaps reducing memory-transfer operations. I'm not 100% sure I'm right about this, but we can test it.
The way ForwardEulerTimeStepper
is constructed it aliases ts.N
with eq.LC
. This implies that eq.LC
changes every time the time stepper calls calcN!
.
The reason for that is that the outer constructor written after the ForwardEulerTimeStepper definition is never being used because its arguments are of exactly the same type as the ForwardEulerTimeStepper properties. That is, lines 138-141 below are never used:
FourierFlows.jl/src/timesteppers.jl
Lines 133 to 141 in ee85328
To fix that we need to implement a so-called inner constructor.
We'd like to refactor the internal type system of FourierFlows.
Consider the current form of Problem
and State
:
mutable struct Problem <: AbstractProblem
grid::AbstractGrid
vars::AbstractVars
params::AbstractParams
eqn::AbstractEquation
ts::AbstractTimeStepper
state::AbstractState
t::Float64
step::Int
end
mutable struct State{T,dim} <: AbstractState
t::Float64
step::Int
dt::Float64
sol::Array{T,dim}
end
Both are mutable. Also, both redundantly store t
and step
, and State
mysteriously includes dt
among it's fields. This is not currently used, though it might be if one of the time-steppers were adaptive.
We feel that the Grid, Vars, Params, Equation, and TimeStepper types are effective. However, State should change.
One proposal is to eliminate State
and create a new type called Clock
, defined thusly:
mutable struct Clock{T}
t::T
dt::T
step::Int
end
Then Problem becomes
struct Problem{Tsol,d}
grid::AbstractGrid
vars::AbstractVars
params::AbstractParams
eqn::AbstractEquation
ts::AbstractTimeStepper
clock::Clock
sol::Array{Tsol,d}
end
or something of the sort. But not mutable!
This will require us to retool all of the timesteppers, the physical modules, and the docs. It's not a minor change. For example, sol
must be passed explicitly to stepforward!
rather than s::State
, and a new variable cl::Clock
must be passed as well.
It may make our lives easier down the road, however.
Need to add tests for traceradvdiff.jl
!
At some point we should make sure the code is compatible with Julia 0.7. For the moment it does not pass the julia 0.7 tests.
With topography present, viscosity diffuses topography. This needs to be resolved. Viscosity should act only on relative vorticity, not on topography.
Hey, I am the developer of DifferentialEquations.jl and am looking to add an ETDRK4 method to our suite. Thank you for building this code since it is a great resource! I am looking at the portion:
https://github.com/FourierFlows/FourierFlows.jl/blob/master/src/timesteppers.jl#L298-L355
Could you explain a little bit about what's going on there? Is there a non-complex version or does it require a complex linear operator (LC is the linear operator?) for the contour integral (does it make sense for non-complex?)? Does that trivially extend to arbitrary dimensions as well?
And what are the dual and filtered versions?
The README claims that "Julia v0.7 v0.1.0 Tests fail". This is not what it should say.
How was this badge generated and how can we fix it?
Do we really need our wavenumber arrays to be defined as Complex128?
(see e.g. domain.jl, ... k::Array{Complex128, 1})
It creates confusion. Let's see if it results in any speedup otherwise we go back to Float64 and sanity.
We need to add more detailed documentation. Specifically, we need to add documentation for:
traceradvdiff
moduleverticallyfourierboussinesq
moduleverticallycosineboussinesq
moduleWe should also include some documentation explaining how the code is structures. For example, explaining the hierarchy among AbstractGrid
, AbstractParams
, AbstractVars
, AbstractEquation
, AbstractTimeStepper
, and AbstractProblem
types.
Also, we need to document the cuda
version of the code.
Last, we need to add somewhere in the documentation the discussion on stochastic forcing implementation and proper calculation of energy budgets.
There is a problem with test/test_twodturb.jl
when it uses the Filtered version of the timesteppers. That's why the lines:
FourierFlows.jl/test/test_twodturb.jl
Line 60 in e8952f0
FourierFlows.jl/test/test_twodturb.jl
Line 64 in e8952f0
We should extend the diffusion module to add a case in which sol is an abstract array. This way we'd be able to test timesteppers.jl
for AbstractArrays
, e.g.,
FourierFlows.jl/src/timesteppers.jl
Lines 103 to 111 in b3bb89e
Perhaps something along the lines of:
L1, L2 = [kappa1], [kappa2]
L = [L1, L2]
So I coded up the QL equations in BarotropicQG
. Do you think I should include them inside BarotropicQG
or add a new module BarotropicQGQL
?
Including them in BarotropicQG
module would make the module more cumbersome.
Also should we really add a new module for QL in the code? It's not the most widely used equations. Is there a way we have a separate repo with modules we use and add the BarotropicQGQL
there? That way only people who want to use QL would load it.
It looks like may be using norm(matrix)
. In Julia 0.7, this will compute the Frobenius norm (vecnorm
in Julia 0.6), due to JuliaLang/julia#27401. If you want the induced/operator norm as in Julia 0.6, use opnorm(matrix)
instead, or Compat.opnorm(matrix)
to work in 0.6 and 0.7 (JuliaLang/Compat.jl#577).
Note that, for testing purposes, rather than @test norm(A - B) ≤ tol
, it is usually preferred to do @test A ≈ B
or @test A ≈ B rtol=...
(which uses isapprox
).
I think that as the code stands now this instance can never be called (because the code calls simply the Output constructor with these arguments).
Lines 32 to 36 in 6b45bac
Am I wrong? None of the tests in test_output.jl
manages to call these lines...
Fix or delete?
@oliasselin mention to me a way to test twodturb
(and in principle any physics)
Say we want to write a test for some physics that we don't really know an analytical solution. Take twodturb
as an example:
∂ζ/∂t + J(ψ,ζ) = ν Δζ.
The procedure goes as follows: Assume there exist an analytical solution in closed form ζ_guess(x,y,t)=...
(you can take any guess that satisfies the boundary conditions). Plug then this guess back into the equation and compute the residual R(x,y,t)
analytically. Then add this residual as a forcing on the r.h.s.:
∂ζ/∂t + J(ψ,ζ) = ν Δζ + R.
Now your guessed solution is actually a solution of the forced problem. If, for simplicity, you guess a steady solution ζ_guess(x,y)
then time-stepping ∂ζ/∂t + J(ψ,ζ) = ν Δζ + R
starting from rest should lead you to ζ(x,y,t)=ζ_guess
.
Given that you have computed R
analytically if indeed the forced problem converges to your guessed solution it means that all other terms in the equation were coded correctly.
It's amazing how anything that slipped out of the tests potentially is wrong. Even very simple things.
I'll push to test coverage increase!
The last substep of RK4 time-stepper is wrong.
Lines:
FourierFlows.jl/src/timesteppers.jl
Line 205 in 7a929e3
FourierFlows.jl/src/timesteppers.jl
Line 224 in 7a929e3
should be substepsol!(ts.sol₁, sol, 2*ts.RHS₃, cl.dt)
(since substepsol!
has a 0.5
in front of dt
. Better solution is to rewrite substepsol!
without the 0.5 factor.
@glwagner: I'm submitting a PR to fix this.
These moments seems that they don't really belong here anymore:
Lines 258 to 266 in 6e87e04
Possibly other things should/could be cleared out from utils.jl
.
With the new GPU direction we should add the automatic travis-like test for GPU using Gitlab.
@glwagner can you attend this? It shouldn't be too difficult. You (and by this I don't necessarily mean you personally...) seem to have done it for Oceananigans.jl. FourierFlows.jl is already in JuliaGPU at gilab.
Next should come GeophysicalFlows.jl.
Is this stale enough that we ought to delete perhaps?
I propose changing the name of the variables:
q
--> zeta
U
--> u
V
--> v
This smoothes out differences between TwoDTurb
and BarotropicQG
modules.
We could use Parameters.jl
for constructing Vars
and Params
. It's claimed that Parameters.jl
will be included in julia base v.1.x
. Perhaps we should wait until that happens?
I know that:
test_grid.jl
test_fft.jl
test_baroQG_timestep.jl
are OK. Also, test_twodturb.jl
is OK but somehow broken now. Perhaps due to the new grid? Or probably due to various updates of the code that the .jl file hasn't caught up.
But what about all other tests?
(twomodeutils.jl
, test_twomodeboussinesq.jl
, test_niwqg.jl
, test_niwqg_rochawagneryoung.jl
, test_twomodeboussinesq.jl
)
Are they good or drafts/tests/leftovers?
I'm sure some are good ones but I don't know which.
branch:2DQGturb
file solver.jl
line 170
I tried to introduce an in-place-function calc_nl_2DQG!
It calculates NLqh correct but it does NOT update the qts.NL
Do you have any idea why?
We need some basic tests for the niwqg and twomodeboussinesq physics modules.
After the ClockReplacesState #96 refactor we are now able to construct a module whose sol
is an array of arrays of different types (see, for example, https://github.com/FourierFlows/GeophysicalFlows.jl/blob/96fbb1b7844da0aa35764769e487125ef4f4de5b/src/barotropicqg.jl#L167). This brings up the necessity to generalize the makefilter
since at its current form it won't work with the BarotropicQG
with U(t)
as implemented in the branch above.
Actually, a better suggestion is to remove FilteredTimeSteppers all together and instead add the optional post-timestep application of a function poststep!
to sol
. This way the user can prescribe filters in the form of the solution within any module and then apply them, e.g., through
function poststep!(sol, params)
@. sol *= params.filter
end
@JuliaRegistrator register()
It'd be nice to have a (lightweight) abstraction for a "Field" in FourierFlows
. The FourierFlows field could store either data in physical/grid space, or data in transform/coefficient space, or both (we should decide on semantics as well).
Type information can also be used to indicate whether the coefficient-space data is in "conjugate symmetric" form (currently we hack a fragile query into functions like parsevalsum
that attempts to diagnose this fact from the size of the array with an if
-statement, rather than using multiple dispatch on types).
Syntax could get a little nicer because we can define a function compute_grid_space!(field)
and compute_coefficient_space!(field)
functions that do fourier transforms (rather than requiring users / external packages like GeophysicalFlows
to manually write FFTs).
Food for thought.
Should we remove the __precompile__()
headers from most of the .jl
files?
Our initial intention was to have them so as to speed-up tests in the development.
@glwagner: Have a look at the FixTracerAdvDiff branch.
Commit 60307b fixed some omissions from the traceradvdiff.jl
. But still I can't get a simple example to work..
I tried putting a fixed flow from ψ=sinx siny and start with c(x, y, t=0) = 1. Have a look at example.jl.
@glwagner: Isn’t one of the argument types wrong?I guess the first one...
FourierFlows.jl/src/domains.jl
Line 177 in 9222f2e
FourierFlows.jl/src/domains.jl
Line 184 in 9222f2e
The repository name should be changed to "FourierFlows.jl" to conform with Julia's convention that packages are always appended by ".jl" :-D
within the gpu
branch try running this on a machine with GPU:
using
FourierFlows,
FourierFlows.Diffusion,
FFTW,
LinearAlgebra,
Printf,
JLD2,
Test
using FourierFlows: parsevalsum2
using LinearAlgebra: mul!, ldiv!, norm
@hascuda using CuArrays
dev = GPU()
prob = Problem(nx=32, Lx=2π, kappa=1e-2, dt=1e-7, stepper="ForwardEuler", dev=dev)
filename = joinpath(".", "testoutput.jld2")
if isfile(filename); rm(filename); end
ctest = devzeros(dev, Float64, (prob.grid.nx, ))
ctest[3] = π
prob.vars.c .= ctest
get_c(prob) = prob.vars.c
out = Output(prob, filename, (:c, get_c))
saveproblem(out)
saveoutput(out)
file = jldopen(filename)
return isfile(filename) && isapprox(file["snapshots"]["c"]["0"], ctest, rtol=rtol_output) && isapprox(file["grid"]["Lx"], prob.grid.Lx, rtol=rtol_output) && isapprox(file["eqn"]["L"], prob.eqn.L, rtol=rtol_output)
You'll get an error at the saveproblem(out)
. Is it because JLD2
cannot write CuArrays to file?
I think we need a new Composite Type called, for example, State
Right now, the 'problem agnostic' fields t
and sol
are contained in the Vars
type. I think we should move these to a new type, defined perhaps in FourierFlows.jl
or somewhere equivalently appropriate. Something like
mutable struct State{dim} <: AbstractState
t::Float64
sol::Array{Complex{Float64},dim}
step::Int
end
and
mutable struct DualState{dimc,dimr} <: AbstractState
t::Float64
solc::Array{Complex{Float64},dimc}
solr::Array{Complex{Float64},dimr}
step::Int
end
Then the call signature for calcN!
would be calcN!(N, sol, t, v, p, g, s)
where s
is a variable of type State
.
We would also add State
to the Problem
type.
Or maybe ProblemState
and DualProblemState
. Something like that. This would require extensive changes to all the modules as well as timesteppers.jl
, but I bet I could complete it all in an hour or too. Thoughts?
We need to figure out how to test physics. For example, we need to have a test for twodturb.jl
which does something like:
Generating the initial condition and output is not difficult, but determining what tolerance is acceptable is hard. The tolerance may have to do with the number of timesteps taken.
Having such a test would speed up testing new features substantially, since we would then be free to make changes to time steppers (or even introduce new ones!)
Interpolations recently released a new, breaking release---see JuliaMath/Interpolations.jl#226 for details.
This package appears to be affected by the bounds-checking change. I recently placed an upper bound for the package manager on your behalf, but if you want to continue following the development of Interpolations you may need to make changes. Sorry for any inconvenience.
The REQUIRE file could not be found.
cc: @glwagner
Should we remove these
Lines 284 to 288 in b22d444
utils.jl
?
Or if we are to keep them, we should generalize them to work for grids where dx
≠dy
. (From what I understand the moment definition as it stands it assumes dx
=dy
.)
I think we should consider changing how the "diagnostics" work. This change will break old code (it is possible for us to define a new Diagnostics type, but I feel we should make these dramatic changes and clean up the code sooner rather than later.) I think this is probably a good time to do it.
I am envisioning something similar to how the "Output" type is currently defined:
https://github.com/navidcy/FourierFlows.jl/blob/master/src/output.jl#L17
In Output, the fields are given in a dictionary. The dictionary keys give the name of the variable, and the item corresponding to each key is a function that calculates the field. The result is then stored in the file specified by "filename".
Right now, Diagnostics are defined individually, which can be collected into an array and passed to stepforward!.
I think though that it will be simpler to have a single object called "Diagnostics", which collects all the diagnostics into a dictionary-like object. We can then define a getindex
function for the object, so that diagnostic values can be accessed with the syntax diags[:energy]
(to retrieve the current value of energy) and diags.timeseries[:energy]
(to retrieve a time-series of energy), for example. I think this will make the code clearer. Also, because fields are then required to be defined with names, the saving of diagnostics fields will be simplified.
There is a problem here:
FourierFlows.jl/src/domains.jl
Lines 116 to 120 in 1028120
If somebody prescribes
Lx=2π
as Float64
and then calls gr=TwoDGrid(nx, Lx, T=Float32)
, the resulting grid has
julia> typeof(gr.Lx)
Float64
while, e.g,
julia> typeof(gr.x)
Array{Float32,2}
I feel that the solution is to make sure Lx
is typed correctly by adding
Lx = T(Lx)
Ly = T(Ly)
in the beginning of TwoDGrid
function.
(Similarly in OneDGrid
...)
I suggest using nu
instead of ν
for the viscosity/hyper-viscosity coefficient.
I also suggest defining the hyper-viscosity order nun
so that the operators is
-(-1)^nun*\nabla^{2*nun}
This way the term actually acts as dissipation for any positive integer value for nun
. For nun=1
is plain-old viscosity, while nun>=2
is hyper-viscosity.
We don't test saveoutput
.
The utility functions parsevalsum
and parsevalsum2
likely trigger scalar operations. These functions are defined here:
Line 147 in 3549d03
Some diagnostics in GeophysicalFlows.TwoDTurb
depend on parsevalsum
. I think this probably slows them down.
It should be possible to define a slightly different algorithm for computing the sum. The main issue is that sum
cannot be called on "non-contiguous" data (in memory), which I think uh[2:end, :]
is, indeed (the first index is the fast index, so [2:end, :]
requires "skipping" the first element of each column).
Thus we should rewrite these functions to compute sum
on contiguous data only. I think this is possible by rewriting parsevalsum
, for example, to:
"""
parsevalsum(uh, g)
Returns `real(Σ uh)` on the grid `g`.
"""
function parsevalsum(uh, g::TwoDGrid)
if size(uh, 1) == g.nkr # uh is conjugate symmetric
U = 2 * sum(uh) # sum all modes twice
U -= @views sum(uh[1, :]) # subtract redundant k=1 mode
else # count every mode once
U = sum(uh)
end
norm = g.Lx*g.Ly/(g.nx^2*g.ny^2) # weird normalization for dft
return norm*real(U)
end
As an aside, we should adopt the practice of always writing explicit return
statements within functions. It makes them easier to interpret.
I defined the rectangular grid in a very stupid repetitive way. Can you have a look at domain.jl and make it smarter?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.