Giter Site home page Giter Site logo

juliaocean / aibecs.jl Goto Github PK

View Code? Open in Web Editor NEW
36.0 5.0 6.0 373.53 MB

The ideal tool for exploring global marine biogeochemical cycles.

License: MIT License

Julia 100.00%
ocean oceanography ocean-circulation modeling model models global optimization inverse-model marine

aibecs.jl's People

Contributors

briochemc avatar github-actions[bot] avatar juliatagbot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

aibecs.jl's Issues

Issues with replicating documentation example

Hi @briochemc,

I was going through the AIBECS model with the example provided by the documentation. In the Radiocarbon dating example, I noticed that plotting function was not working as expected: It throws an error whenever I wanted to plot the horizontal slice graph:

plothorizontalslice(age_in_yrs, grd, depth=2000u"m", color=:magma)
ERROR: MethodError: no method matching plot(::AIBECS.HorizontalPlane; color=:magma)

Similar error is encountered in the horizontal mean plot. Do you know what when wrong there?

Thanks

Precompute mismatch scaling factor

Specifically, refactor these to allow the user to provide transpose(o) * W * o instead of recalculating it every time:

## new functions for more generic obs packages
# TODO Add an optional function argument to transform the data before computingn the mismatch
# Example if for isotope tracers X where one ususally wants to minimize the mismatch in δ or ε.
function mismatch(x, grd::OceanGrid, obs; c=identity, W=I, M=interpolationmatrix(grd, obs.metadata), iwet=iswet(grd, obs))
o = view(obs, iwet)
δx = M * c(x) - o
return 0.5 * transpose(δx) * W * δx / (transpose(o) * W * o)
end
mismatch(x, grd::OceanGrid, ::Missing; kwargs...) = 0
function ∇mismatch(x, grd::OceanGrid, obs; c=identity, W=I, M=interpolationmatrix(grd, obs.metadata), iwet=iswet(grd, obs))
∇c = Diagonal(ForwardDiff.derivative-> c(x .+ λ), 0.0))
o = view(obs, iwet)
δx = M * c(x) - o
return transpose(W * δx) * M * ∇c / (transpose(o) * W * o)
end
∇mismatch(x, grd::OceanGrid, ::Missing; kwargs...) = transpose(zeros(length(x)))
# In case the mismatch is not based on the tracer but on some function of it
function indirectmismatch(xs::Tuple, grd::OceanGrid, modify::Function, obs, i, M=interpolationmatrix(grd, obs[i].metadata), iwet=iswet(grd, obs[i]))
x2 = modify(xs...)
out = 0.0
M = interpolationmatrix(grd, obs[i].metadata)
iwet = iswet(grd, obs[i])
o = view(obs[i], iwet)
δx = M * x2[i] - o
return 0.5 * transpose(δx) * δx / (transpose(o) * o)
end
function ∇indirectmismatch(xs::Tuple, grd::OceanGrid, modify::Function, obs, i, M=interpolationmatrix(grd, obs[i].metadata), iwet=iswet(grd, obs[i]))
nt, nb = length(xs), length(iswet(grd))
x2 = modify(xs...)
o = view(obs[i], iwet)
δx = M * x2[i] - o
∇modᵢ = ∇modify(modify, xs, i)
return transpose(δx) * M * ∇modᵢ / (transpose(o) * o)
end

Move from using JLD2 to using BSON

JLD2 not being actively maintained is an issue. BSON.jl has been suggested a couple times already as being offering similar functionality, but is actively maintained, so I should use that instead.

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

Add Makie recipes

Leveraging MakieLayout, Makie seems posed to become the default plotting package for publication quality figures, so it would be great to add some Makie plot recipes, add tests, and some tutorials/how-to's that showcase these recipes.

Merge into DifferentialEquations, NLSolve

I think in the long run it will be good to somehow merge the solving functionality of this package (the solver(s) in particular) with the well maintained packages of DifferentialEquations and/or NLSolve. This would provide lots of added functionality (e.g., for #4, #5, etc.). Also this might help to provide a DSL as suggested in #18

Add function to unpack state to 3D array(s)

Right now I do this via

const iDIP = 1:nb
const iDOP = iDIP .+ nb
const iPOP = iDOP .+ nb
function unpack_state(x)
DIP = x[iDIP]
DOP = x[iDOP]
POP = x[iPOP]
return DIP, DOP, POP
end

and

DIP, DOP, POP = unpack_state(x)

and

DIP_3d = NaN * wet3d; DIP_3d[iwet] = DIP

Probably good to have a unpack_state and vector_to_3D functions in AIBECS

Add generic diagnostics capability

Let's try to add some diagnostic functionality to AIBECS that generalizes the work done in Pasquier and Holzer, 2018.


Preliminary notes

Let's start from the generic equation

(∂ₜ + T) x = G(x).

x could represent a multitude of tracers and processes. Within x, there may be separate groups of tracers with a group per compound or element. E.g., one could be tracking many elements including, e.g., phosphorus, whose group could be composed of three pools, like PO4, DOP, and POP. For the diagnostics that I am interested in, we will assume for simplicity and without any loss of generality, that there is only one element (one group) here.

We can then express the group's system without loss of generality as a bunch of

  • source terms sp(x) that inject x into the system,
  • of "transfers" terms that exchange the element between tracers of our group Jij(x),
  • and of "death" processes dq(x), which ultimately remove x from the system.

Mathematically, that means we can write

G(x) = ∑p sp(x) + ∑ij Jij(x) + ∑q dq(x).

We construct the LEM by first creating linear-equivalent terms for Jij(x) and dq(x), evaluated at the steady-state solution x given by

T x = G(x)

In other words, the LEM is built such that

G(x) = ∑p sp + ∑ij Lij x + ∑q Lq x

when x is the steady-state, and where

  • Lij is a block matrix where only 2 blocks are non-zero, (i,j) and (j,i), which have diagonals -(Jij(x))i / xi, and +(Jij(x))j / xi, respectively. (Note we use xi to "linearize" Jij so that the rate of transfer is specific to the removed tracer.)
  • and Lq is a diagonal matrix with diagonal dq(x) / x.

We then construct the LEM simply as

(∂ₜ + H) x = ∑p sp

where

H = T - ∑ij Lij - ∑q Lq

and we can then exploit this for powerful diagnostics as in Pasquier and Holzer, 2018. Fractions that came from source sp, or fraction that will be removed via process dq, are available from a single backslash with H. One can also further partition according to each ij passage by removing Jij from H into a F operator and iteratively reapplying the source term. Direct computations leveraging the classical identities ∑n xn and ∑n n xn also allow for direct computations of, e.g., the number of ij passages.

Plot Recipes refactor

Probably a good idea to bundle them into a submodule to control what's exported.

Also better to bring the factor out the computation part into OceanGrids. That is, have a zonalaverage function in OceanGrids, or maybe even an overload of StatsBase/Statistics' likemean(::ZonalAverage, x, grd) or sum(::ZonalIntegral, x, grd) to just spit out the array, and then have the recipes use those functions directly instead.

Memoize some functions

It might be beneficial performance-wise to memoize some functions with a single memory cache when running optimizations, especially if I can keep a lot in memory :) Memoize.jl seems to be able to do just that... From its ReadMe:

using Memoize
using LRUCache
@memoize LRU{Tuple{Any,Any},Any}(maxsize=2) function x(a, b)
    println("Running")
    a + b
end

Evaluate parameter cost in λ space

This might allow me to use LogitNormal as a prior.

Assume p has a D(p) = a + (b-a) * LogitNormal() prior. λ = subfun⁻¹(p) = logit((p - a) / (b - a)) (or is it the reverse) is then Normal() distributed in the sense that obj(λ) contains a -gradlogpdf(Normal(), λ) penalty. This is a priori great for Optim because this function is log-convex, real-valued — it's a quadratic afterall. However, when obj(λ) and its derivatives are computed, and subsequently λ is updated by Optim, if λ has been pushed a bit too far to one side, it may very well be that p = subfun(λ) is floating-arithmetic-rounded to be exactly a (or b), i.e., one of the bounds of the support of the prior. Then mismatch(p) = -gradlogpdf(D(p), a) evaluates to +Inf and everything falls apart. In reality, however, I don't think the mismatch should be that big, since it should just be -gradlogpdf(D(p), λ), and the penalty should ensure that this is not even close to +Inf. The solution could be to always evaluate the mismatch in λ space.

Side note/issue: Toying with TransformVariables, Bijectors, and what AIBECS does (in a local Pluto notebook that I should probably save as a gist and reference here), I noticed that I don't recover exactly what I'm supposed to going from p-space to λ-space in terms of gradlogpdf, so it would be good to elucidate that first before diving into this potential rabbit hole.

Multigrid / coarse graining

It would be great to be able to coarse-grain any given matrix to allow for quick testing.

Not sure how to do this easily. In particular, making this generic might not be possible, in particular, if the fine grid has a non-easily divisible size in one dimension (e.g., OCIM2 has size 91 in latitude, which is divisible by 7 🤷), or if the coarse grid would bundle separate boxes together (e.g., boxes originally separated by land diagonally).

Update OCIM2 files

Current OCIM2 files were built with UnitfulAstro's yr. While I don't think this is a problem for T and grd, it is one for the He fluxes, which are in mol/m^3/yr.

Todo list:

  • OCIM2_CTL_He
  • OCIM2_CTL_noHe
  • OCIM2_KiHIGH_He
  • OCIM2_KiHIGH_noHe
  • OCIM2_KiLOW_He
  • OCIM2_KiLOW_noHe
  • OCIM2_KvHIGH_He
  • OCIM2_KvHIGH_KiHIGH_noHe
  • OCIM2_KvHIGH_KiLOW_He
  • OCIM2_KvHIGH_KiLOW_noHe
  • OCIM2_KvHIGH_noHe

include mask into grid

It would make for a cleanar API to just do

julia> grid, T = OCIM1.load()

and have the wet point indices or the mask wet3D inside of grid.

This would mean breaking changes for

  • OceanGrid
  • AIBECS (tests and notebooks)
  • the BSON files on FigShare
  • the hash checksums on AIBECS for those files.

Rivers numerical noise

In the river tutorial, if I change the first solution plot to

cmap = :RdYlBu_4
plothorizontalslice(s, grd, zunit=u"μmol/m^3", depth=0, color=cmap, clim=(-1,1))

I get to see numerical noise at the Amazon outlet and other major rivers at the surface.

With OCIM2:

Screen Shot 2021-03-01 at 11 31 56 am

OCIM1:

Screen Shot 2021-03-01 at 11 35 03 am

OCCA:

Screen Shot 2021-03-01 at 11 36 30 am

Add a two-box model of the circulation

I find I could sometimes use a very simple 2-box model, so let's make one. Should probably go with the values in the Sarmiento and Gruber book or the reference therein, if any.

DSL with macros?

Maybe worth reworking the interface to be simpler and yielding more efficient code.
I was thinking of something along the lines of

  1. Define tracers, e.g., DIP, DOP, POP, DFe, DO₂, maybe with a macro like

    @define_tracers DIP DOP POP DFe DO₂
  2. Then define functions and who they apply to, maybe something like

    @add_BGC_sink DIP  uptake : 1 / τ * DIP^2 / (DIP + k) * (z  zₑ)
    @add_BGC_source POP : σ * uptake
    @add_BGC_source DOP : (1-σ) * uptake
    @add_BGC_transfer DOP  DIP : kDOP * DOP
    @add_BGC_transfer POP  DOP : kPOP * POP
    @add_restoring DIP : DIPgeo τgeo
    @add_external_source DFe : aeolian_DFe_source
    etc.
  3. Then be able to pack/unpack the tracers and also create efficient inplace F (as suggested in #10)

Improve river tutorial

This tutorial needs a few improvements

  • Add a plot of the original-data sources and the mouth-location nearest-neghbor interpolation.
  • Chose a better colormap
  • find better things to plot
  • Make sure the output of code cells is not too long

AO functionality

A worthy successor to the AWESOME OCIM (AO) should include all the functionality of the AO.
I try to reuse the AO nomenclature for reference, and will update with AIBECS names if I change them.

Thi issue will serve me as a check/TODO list to incorporate things one can do in the AO into AIBECS:

  • backstage + data In the AO this is were the preperocessing of data occured. I would like to add functionality to provide this datasets and fields in a grid-agnostic way:
    • Nepholoid source for nepholoid layer flux. The original data comes either from eddy kinetic energy (EKE) or from some global interpolation of ~9000 particle observations. Check the references in the AO paper.
    • HEFLUX for hydrothermal flux. The AO data comes from the OCIM output, as the He source injection points and magnitudes are part of the inversion. These can now be loaded after via grd, T, He3, He4 = OCIM2.load().
      • Upload the He source fields online and write a tiny separate package so that it can be then regridded to other grids
    • WJ18 for a state-of-the-art estimate of P-cycling from Weber and John (2018). At some point it would be great to reproduce this model in AIBECS and provide it as a fully-fledged example/notebook, probably living separately from the docs if it is too big of course.
    • alkalinity from GLODAP
    • dust Contacted Tom Weber's for his dust sources, which is IIRC an optimized combination of 10ish atmospheric models. Providing for the functionality to do that on the fly would be great. (I.e., load the datasets and combine them in whatever way one wants). Will probably require a separate package. The aeolian sources used in the AO are now available and for any grid using the regrid function. See tutorials.
    • model grid. To check, but I think there is nothing new here, just the grid and some masks. Using OceanBasins.jl or a similar package should provide these masks as functions.
    • WOA. This should be sorted with WorldOceanAtlasTools.jl.
    • GEOTRACES. This should be sorted with GEOTRACES.jl. Although programmatic download of the data is still unavailable due to GEOTRACES' data committee restrictions.
    • GLODAP. An external package for using this data is probably required. Check that it does not already exist to be sure.
    • Rivers. I should add the rivers as present in the AO for Nd modelling into an extra file. But I need to make the locations and flows to be grid-agnostic before. The 200 major rivers from the Dai and Trenberth dataset are now available.
  • srcsnk. This are the functions for creating sources and sinks and transport matrices. I should check them all by one and make sure I have a functionality to match it. Some of them use prescribed data though, so if the data is loaded through a grid-agnostic function, I may need to rethink the formulation. Also the obvious translation from implicit to explicit transport might require some work. (A 👮‍♀ smiley indicates that it is used in the Cu model)
    • bioalpha.m
    • biobeta.m
    • fbiobeta.m 👮‍♀
    • bioredfield.m
    • boundcon.m (sort of natural to implement in AIBECS with a fast relaxation term)
    • conc.m (with a slow relaxation term)
    • decay.m 👮‍♀ (with a decay term)
    • dust.m 👮‍♀
    • hydrothermal.m
    • nephloid.m 👮‍♀
    • [] nonrevscavPOC.m
    • revscav.m (using a transport operator and reaction constant)
    • revscavPOC.m
    • frevscavPOC.m 👮‍♀
    • river.m 👮‍♀
    • photoout.m 👮‍♀
  • GUI. Not sure this will be implemented. Jupyter notebooks are a great (better?) alternative. They require a bit more involvement, but François thinks exposing users to a bit of code is as an advantage.

Add feature to figure out independent tracers and use it

Assuming one wants to simulate two independent tracers at the same time, there is no need to construct the full matrix. A better approach is to split the model into separate sub-models. Maybe a good solution is to raise a warning if some tracers are independent. The user can then think of it as separate models with separate inputs and outputs, that can be combined after the fact if needed.

Fix transportoperator for OCCA matrix

I'm unsure at this stage but it seems to me that I should revisit the transport operator function to accommodate for the OCCA matrix because its cell volumes already account for subgrid topography (and the transport too?) and I think this causes mass-conservation problems for DIP–POP-like systems.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.