diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index ef8db316..db573661 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -5,6 +5,7 @@ agents: env: JULIA_LOAD_PATH: "${JULIA_LOAD_PATH}:${BUILDKITE_BUILD_CHECKOUT_PATH}/.buildkite" + JULIA_DEBUG: "Documenter" OPENBLAS_NUM_THREADS: 1 OMPI_MCA_opal_warn_on_missing_libcuda: 0 @@ -20,6 +21,9 @@ steps: agents: slurm_mem: 32G slurm_cpus_per_task: 8 + slurm_gpus: 1 + slurm_ntasks: 1 + slurm_gpus_per_task: 1 env: JULIA_NUM_PRECOMPILE_TASKS: 8 @@ -33,11 +37,11 @@ steps: commands: - "julia --project -e 'using Pkg; Pkg.test()'" - - label: "Run ECCO2 tests" - key: "tests_ecco2" + - label: "Run ECCO tests" + key: "tests_ecco" env: CUDA_VISIBLE_DEVICES: "-1" - TEST_GROUP: "ecco2" + TEST_GROUP: "ecco" commands: - "julia --project -e 'using Pkg; Pkg.test()'" @@ -49,16 +53,5 @@ steps: commands: - "julia --project -e 'using Pkg; Pkg.test()'" - # - label: "documentation" - # env: - # JULIA_DEBUG: "Documenter" - # commands: - # - "julia --color=yes --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'" - # - "julia --color=yes --project=docs/ docs/make.jl" - # agents: - # slurm_mem: 120G - # slurm_ntasks: 1 - # slurm_gpus_per_task: 1 - - wait: ~ continue_on_failure: true diff --git a/.github/workflows/Documenter.yml b/.github/workflows/Documenter.yml index 1eb5ef50..95ea069f 100644 --- a/.github/workflows/Documenter.yml +++ b/.github/workflows/Documenter.yml @@ -30,5 +30,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # For authentication with SSH deploy key - JULIA_DEBUG: Documenter - run: julia --color=yes --project=docs/ docs/make.jl + JULIA_DEBUG: Documenter + ECCO_USERNAME: ${{ secrets.ECCO_USERNAME }} # To download ECCO data from the podaac website + ECCO_PASSWORD: ${{ secrets.ECCO_PASSWORD }} # To download ECCO data from the podaac website + run: julia --color=yes --project=docs/ docs/make_without_examples.jl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 101e062c..0bc5604c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,8 @@ jobs: julia --color=yes --project -e 'using Pkg; Pkg.test()' env: TEST_GROUP: "downloading" + ECCO_USERNAME: ${{ secrets.ECCO_USERNAME }} # To download ECCO data from the podaac website + ECCO_PASSWORD: ${{ secrets.ECCO_PASSWORD }} # To download ECCO data from the podaac website - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v3 with: diff --git a/.gitignore b/.gitignore index 7d71b99c..a9fdf43b 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ docs/src/literated/ *.qdrep *.nsys-rep *.ncu-rep +*.sq +*.sqlite \ No newline at end of file diff --git a/Manifest.toml b/Manifest.toml index d88bbd31..aba8f91e 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -1,8 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.10.0" +julia_version = "1.10.4" manifest_format = "2.0" -project_hash = "086b5c52c3f2a61f133762647db63128f8714c56" +project_hash = "2d55e37361bb1470f75342d3cff1fb1d8891fe98" [[deps.AbstractFFTs]] deps = ["LinearAlgebra"] @@ -17,9 +17,9 @@ weakdeps = ["ChainRulesCore", "Test"] [[deps.Accessors]] deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "LinearAlgebra", "MacroTools", "Markdown", "Test"] -git-tree-sha1 = "c0d491ef0b135fd7d63cbc6404286bc633329425" +git-tree-sha1 = "f61b15be1d76846c0ce31d3fcfac5380ae53db6a" uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" -version = "0.1.36" +version = "0.1.37" [deps.Accessors.extensions] AccessorsAxisKeysExt = "AxisKeys" @@ -52,9 +52,9 @@ version = "1.1.1" [[deps.ArrayInterface]] deps = ["Adapt", "LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "ed2ec3c9b483842ae59cd273834e5b46206d6dda" +git-tree-sha1 = "5c9b74c973181571deb6442d41e5c902e6b9f38e" uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.11.0" +version = "7.12.0" [deps.ArrayInterface.extensions] ArrayInterfaceBandedMatricesExt = "BandedMatrices" @@ -138,9 +138,9 @@ version = "0.2.6" [[deps.CUDA]] deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CUDA_Driver_jll", "CUDA_Runtime_Discovery", "CUDA_Runtime_jll", "Crayons", "DataFrames", "ExprTools", "GPUArrays", "GPUCompiler", "KernelAbstractions", "LLVM", "LLVMLoopInfo", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "NVTX", "Preferences", "PrettyTables", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "StaticArrays", "Statistics"] -git-tree-sha1 = "6e945e876652f2003e6ca74e19a3c45017d3e9f6" +git-tree-sha1 = "fdd9dfb67dfefd548f51000cc400bb51003de247" uuid = "052768ef-5323-5732-b1bb-66c8b64840ba" -version = "5.4.2" +version = "5.4.3" [deps.CUDA.extensions] ChainRulesCoreExt = "ChainRulesCore" @@ -154,9 +154,9 @@ version = "5.4.2" [[deps.CUDA_Driver_jll]] deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"] -git-tree-sha1 = "c48f9da18efd43b6b7adb7ee1f93fe5f2926c339" +git-tree-sha1 = "97df9d4d6be8ac6270cb8fd3b8fc413690820cbd" uuid = "4ee394cb-3365-5eb0-8335-949819d2adfc" -version = "0.9.0+0" +version = "0.9.1+1" [[deps.CUDA_Runtime_Discovery]] deps = ["Libdl"] @@ -166,9 +166,9 @@ version = "0.3.4" [[deps.CUDA_Runtime_jll]] deps = ["Artifacts", "CUDA_Driver_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "TOML"] -git-tree-sha1 = "bcba305388e16aa5c879e896726db9e71b4942c6" +git-tree-sha1 = "afea94249b821dc754a8ca6695d3daed851e1f5a" uuid = "76a88914-d11a-5bdc-97e0-2f5a05c973a2" -version = "0.14.0+1" +version = "0.14.1+0" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] @@ -255,7 +255,7 @@ weakdeps = ["Dates", "LinearAlgebra"] [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.0.5+1" +version = "1.1.1+0" [[deps.CompositionsBase]] git-tree-sha1 = "802bb88cd69dfd1509f6670416bd4434015693ad" @@ -268,15 +268,15 @@ weakdeps = ["InverseFunctions"] [[deps.ConcurrentUtilities]] deps = ["Serialization", "Sockets"] -git-tree-sha1 = "6cbbd4d241d7e6579ab354737f4dd95ca43946e1" +git-tree-sha1 = "ea32b83ca4fefa1768dc84e504cc0a94fb1ab8d1" uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" -version = "2.4.1" +version = "2.4.2" [[deps.ConstructionBase]] deps = ["LinearAlgebra"] -git-tree-sha1 = "260fd2400ed2dab602a7c15cf10c1933c59930a2" +git-tree-sha1 = "d8a9c0b6ac2d9081bf76324b39c78ca3ce4f0c98" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.5.5" +version = "1.5.6" [deps.ConstructionBase.extensions] ConstructionBaseIntervalSetsExt = "IntervalSets" @@ -449,9 +449,9 @@ version = "6.2.1+6" [[deps.GPUArrays]] deps = ["Adapt", "GPUArraysCore", "LLVM", "LinearAlgebra", "Printf", "Random", "Reexport", "Serialization", "Statistics"] -git-tree-sha1 = "5c9de6d5af87acd2cf719e214ed7d51e14017b7a" +git-tree-sha1 = "a74c3f1cf56a3dfcdef0605f8cdb7015926aae30" uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" -version = "10.2.2" +version = "10.3.0" [[deps.GPUArraysCore]] deps = ["Adapt"] @@ -460,10 +460,10 @@ uuid = "46192b85-c4d5-4398-a991-12ede77f4527" version = "0.1.6" [[deps.GPUCompiler]] -deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "Scratch", "TimerOutputs", "UUIDs"] -git-tree-sha1 = "518ebd058c9895de468a8c255797b0c53fdb44dd" +deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "Preferences", "Scratch", "Serialization", "TOML", "TimerOutputs", "UUIDs"] +git-tree-sha1 = "ab29216184312f99ff957b32cd63c2fe9c928b91" uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" -version = "0.26.5" +version = "0.26.7" [[deps.Glob]] git-tree-sha1 = "97285bbd5230dd766e9ef6749b80fc617126d496" @@ -496,9 +496,9 @@ version = "0.1.17" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1d334207121865ac8c1c97eb7f42d0339e4635bf" +git-tree-sha1 = "5e19e1e4fa3e71b774ce746274364aef0234634e" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.11.0+0" +version = "2.11.1+0" [[deps.IfElse]] git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" @@ -524,22 +524,23 @@ uuid = "40713840-3770-5561-ab4c-a76e7d0d7895" version = "0.2.1" [[deps.InlineStrings]] -deps = ["Parsers"] -git-tree-sha1 = "86356004f30f8e737eff143d57d41bd580e437aa" +git-tree-sha1 = "45521d31238e87ee9f9732561bfee12d4eebd52d" uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" -version = "1.4.1" +version = "1.4.2" [deps.InlineStrings.extensions] ArrowTypesExt = "ArrowTypes" + ParsersExt = "Parsers" [deps.InlineStrings.weakdeps] ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" + Parsers = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "be50fe8df3acbffa0274a744f1a99d29c45a57f4" +git-tree-sha1 = "14eb2b542e748570b56446f4c50fbfb2306ebc45" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.1.0+0" +version = "2024.2.0+0" [[deps.InteractiveUtils]] deps = ["Markdown"] @@ -547,9 +548,9 @@ uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" [[deps.InverseFunctions]] deps = ["Test"] -git-tree-sha1 = "e7cbed5032c4c397a6ac23d1493f3289e01231c4" +git-tree-sha1 = "18c59411ece4838b18cd7f537e56cf5e41ce5bfd" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.14" +version = "0.1.15" weakdeps = ["Dates"] [deps.InverseFunctions.extensions] @@ -578,9 +579,9 @@ version = "1.0.0" [[deps.JLD2]] deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "Pkg", "PrecompileTools", "Reexport", "Requires", "TranscodingStreams", "UUIDs", "Unicode"] -git-tree-sha1 = "bdbe8222d2f5703ad6a7019277d149ec6d78c301" +git-tree-sha1 = "5fe858cb863e211c6dedc8cce2dc0752d4ab6e2b" uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" -version = "0.4.48" +version = "0.4.50" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] @@ -620,9 +621,9 @@ version = "0.9.22" [[deps.LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Preferences", "Printf", "Requires", "Unicode"] -git-tree-sha1 = "389aea28d882a40b5e1747069af71bdbd47a1cae" +git-tree-sha1 = "020abd49586480c1be84f57da0017b5d3db73f7c" uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "7.2.1" +version = "8.0.0" weakdeps = ["BFloat16s"] [deps.LLVM.extensions] @@ -630,9 +631,9 @@ weakdeps = ["BFloat16s"] [[deps.LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl", "TOML"] -git-tree-sha1 = "88b916503aac4fb7f701bb625cd84ca5dd1677bc" +git-tree-sha1 = "c2636c264861edc6d305e6b4d528f09566d24c5e" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.29+0" +version = "0.0.30+0" [[deps.LLVMLoopInfo]] git-tree-sha1 = "2e5c102cfc41f48ae4740c7eca7743cc7e7b75ea" @@ -750,9 +751,9 @@ version = "1.9.4+0" [[deps.MKL_jll]] deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] -git-tree-sha1 = "80b2833b56d466b3858d565adcd16a4a05f2089b" +git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.1.0+0" +version = "2024.2.0+0" [[deps.MPI]] deps = ["Distributed", "DocStringExtensions", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "PkgVersion", "PrecompileTools", "Requires", "Serialization", "Sockets"] @@ -770,9 +771,9 @@ version = "0.20.16" [[deps.MPICH_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "4099bb6809ac109bfc17d521dad33763bcf026b7" +git-tree-sha1 = "19d4bd098928a3263693991500d05d74dbdc2004" uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" -version = "4.2.1+1" +version = "4.2.2+0" [[deps.MPIPreferences]] deps = ["Libdl", "Preferences"] @@ -884,9 +885,9 @@ version = "1.2.0" [[deps.Oceananigans]] deps = ["Adapt", "CUDA", "Crayons", "CubedSphere", "Dates", "Distances", "DocStringExtensions", "FFTW", "Glob", "IncompleteLU", "InteractiveUtils", "IterativeSolvers", "JLD2", "KernelAbstractions", "LinearAlgebra", "Logging", "MPI", "NCDatasets", "OffsetArrays", "OrderedCollections", "PencilArrays", "PencilFFTs", "Pkg", "Printf", "Random", "Rotations", "SeawaterPolynomials", "SparseArrays", "Statistics", "StructArrays"] -git-tree-sha1 = "745b1c19221e09886cae331450a2a4ea73708a38" +git-tree-sha1 = "942c78ad3c95e47bcbab3eeacb0b8a846bcfb33b" uuid = "9e8cae18-63c1-5223-a75c-80ca9d6e9a09" -version = "0.91.3" +version = "0.91.4" [deps.Oceananigans.extensions] OceananigansEnzymeExt = "Enzyme" @@ -895,9 +896,9 @@ version = "0.91.3" Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" [[deps.OffsetArrays]] -git-tree-sha1 = "e64b4f5ea6b7389f6f046d13d4896a8f9c1ba71e" +git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.14.0" +version = "1.14.1" weakdeps = ["Adapt"] [deps.OffsetArrays.extensions] @@ -906,7 +907,7 @@ weakdeps = ["Adapt"] [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.23+2" +version = "0.3.23+4" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] @@ -915,9 +916,9 @@ version = "0.8.1+2" [[deps.OpenMPI_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"] -git-tree-sha1 = "a9de2f1fc98b92f8856c640bf4aec1ac9b2a0d86" +git-tree-sha1 = "2f0a1d8c79bc385ec3fcda12830c9d0e72b30e71" uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" -version = "5.0.3+0" +version = "5.0.4+0" [[deps.OpenSSL]] deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "OpenSSL_jll", "Sockets"] @@ -1096,9 +1097,9 @@ version = "0.4.2" [[deps.Roots]] deps = ["Accessors", "ChainRulesCore", "CommonSolve", "Printf"] -git-tree-sha1 = "1ab580704784260ee5f45bffac810b152922747b" +git-tree-sha1 = "3484138c9fa4296a0cf46a74ca3f97b59d12b1d0" uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" -version = "2.1.5" +version = "2.1.6" [deps.Roots.extensions] RootsForwardDiffExt = "ForwardDiff" @@ -1150,9 +1151,9 @@ version = "0.3.4" [[deps.SentinelArrays]] deps = ["Dates", "Random"] -git-tree-sha1 = "90b4f68892337554d31cdcdbe19e48989f26c7e6" +git-tree-sha1 = "ff11acffdb082493657550959d4feb4b6149e73a" uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.4.3" +version = "1.4.5" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -1194,9 +1195,9 @@ version = "0.1.1" [[deps.Static]] deps = ["CommonWorldInvalidations", "IfElse", "PrecompileTools"] -git-tree-sha1 = "0bbff21027dd8a107551847528127b62a35f7594" +git-tree-sha1 = "87d51a3ee9a4b0d2fe054bdd3fc2436258db2603" uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" -version = "1.1.0" +version = "1.1.1" [[deps.StaticArrayInterface]] deps = ["ArrayInterface", "Compat", "IfElse", "LinearAlgebra", "PrecompileTools", "Requires", "SparseArrays", "Static", "SuiteSparse"] @@ -1211,9 +1212,9 @@ weakdeps = ["OffsetArrays", "StaticArrays"] [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "20833c5b7f7edf0e5026f23db7f268e4f23ec577" +git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.6" +version = "1.9.7" weakdeps = ["ChainRulesCore", "Statistics"] [deps.StaticArrays.extensions] @@ -1315,10 +1316,10 @@ uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" version = "1.0.1" [[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "cb76cf677714c095e535e3501ac7954732aeea2d" +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.11.1" +version = "1.12.0" [[deps.Tar]] deps = ["ArgTools", "SHA"] @@ -1349,9 +1350,9 @@ uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [[deps.Thermodynamics]] deps = ["DocStringExtensions", "KernelAbstractions", "Random", "RootSolvers"] -git-tree-sha1 = "deac04ad36638b10fde82470d5f128419f627e9a" +git-tree-sha1 = "80b13ddc5ae7b8605ef5a055e7f23c5b5f4775cf" uuid = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c" -version = "0.12.6" +version = "0.12.7" [deps.Thermodynamics.extensions] CreateParametersExt = "ClimaParams" @@ -1378,9 +1379,9 @@ uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" version = "0.5.24" [[deps.TranscodingStreams]] -git-tree-sha1 = "d73336d81cafdc277ff45558bb7eaa2b04a8e472" +git-tree-sha1 = "96612ac5365777520c3c5396314c8cf7408f436a" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.10.10" +version = "0.11.1" weakdeps = ["Random", "Test"] [deps.TranscodingStreams.extensions] diff --git a/Project.toml b/Project.toml index 19abf73e..d295543a 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.2.0" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +CFTime = "179af706-886a-5703-950a-314cd64e0468" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" ClimaSeaIce = "6ba0ff68-24e6-4315-936c-2e99227c95a4" CubicSplines = "9c784101-8907-5a6d-9be6-98f00873c89b" diff --git a/docs/Project.toml b/docs/Project.toml index d7cbe6e6..7331d2d9 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -9,4 +9,4 @@ Oceananigans = "9e8cae18-63c1-5223-a75c-80ca9d6e9a09" CairoMakie = "0.10.12" DataDeps = "0.7" Documenter = "1" -Oceananigans = "0.90.10" +Oceananigans = "0.91.4" diff --git a/docs/make.jl b/docs/make.jl index 0bc69662..3b401b64 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -15,7 +15,7 @@ const EXAMPLES_DIR = joinpath(@__DIR__, "..", "examples") const OUTPUT_DIR = joinpath(@__DIR__, "src/literated") to_be_literated = [ - "inspect_ecco2_data.jl", + "inspect_ecco_data.jl", "generate_surface_fluxes.jl", "single_column_simulation.jl", # "near_global_omip_simulation.jl" @@ -31,9 +31,10 @@ end ##### format = Documenter.HTML( - collapselevel = 2, - prettyurls = get(ENV, "CI", nothing) == "true", - canonical = "https://clima.github.io/ClimaOceanDocumentation/dev/", + collapselevel = 2, + size_threshold = nothing, + prettyurls = get(ENV, "CI", nothing) == "true", + canonical = "https://clima.github.io/ClimaOceanDocumentation/dev/", ) pages = [ @@ -47,7 +48,7 @@ pages = [ ], "Examples" => [ - "Inspect ECCO2 data" => "literated/inspect_ecco2_data.md", + "Inspect ECCO2 data" => "literated/inspect_ecco_data.md", "Surface fluxes" => "literated/generate_surface_fluxes.md", "Single column simulation" => "literated/single_column_simulation.md", ] diff --git a/docs/make_without_examples.jl b/docs/make_without_examples.jl index 9724e318..a550d989 100644 --- a/docs/make_without_examples.jl +++ b/docs/make_without_examples.jl @@ -7,22 +7,6 @@ using ENV["DATADEPS_ALWAYS_ACCEPT"] = "true" -##### -##### Generate examples -##### - -const EXAMPLES_DIR = joinpath(@__DIR__, "..", "examples") -const OUTPUT_DIR = joinpath(@__DIR__, "src/literated") - -to_be_literated = [ - "inspect_ecco2_data.jl", -] - -for file in to_be_literated - filepath = joinpath(EXAMPLES_DIR, file) - Literate.markdown(filepath, OUTPUT_DIR; flavor = Literate.DocumenterFlavor()) -end - ##### ##### Build and deploy docs ##### diff --git a/docs/src/library/internals.md b/docs/src/library/internals.md index 173ade94..0a802faf 100644 --- a/docs/src/library/internals.md +++ b/docs/src/library/internals.md @@ -31,10 +31,10 @@ Modules = [ClimaOcean.DataWrangling] Public = false ``` -## ECCO2 +## ECCO ```@autodocs -Modules = [ClimaOcean.ECCO2] +Modules = [ClimaOcean.ECCO] Public = false ``` @@ -44,3 +44,24 @@ Public = false Modules = [ClimaOcean.Bathymetry] Public = false ``` + +## VerticalGrids + +```@autodocs +Modules = [ClimaOcean.VerticalGrids] +Private = false +``` + +## OceanSeaIceModels + +```@autodocs +Modules = [ClimaOcean.OceanSeaIceModels] +Private = false +``` + +## CrossRealFluxes + +```@autodocs +Modules = [ClimaOcean.OceanSeaIceModels.CrossRealmFluxes] +Private = false +``` diff --git a/docs/src/library/public.md b/docs/src/library/public.md index 65a7fed6..13f94c53 100644 --- a/docs/src/library/public.md +++ b/docs/src/library/public.md @@ -32,10 +32,10 @@ Modules = [ClimaOcean.DataWrangling] Private = false ``` -## ECCO2 +## ECCO ```@autodocs -Modules = [ClimaOcean.ECCO2] +Modules = [ClimaOcean.ECCO] Private = false ``` diff --git a/examples/freely_decaying_mediterranean.jl b/examples/freely_decaying_mediterranean.jl index 83b719c4..2a05e79b 100644 --- a/examples/freely_decaying_mediterranean.jl +++ b/examples/freely_decaying_mediterranean.jl @@ -2,7 +2,7 @@ using GLMakie using Oceananigans using Oceananigans: architecture using ClimaOcean -using ClimaOcean.ECCO2 +using ClimaOcean.ECCO using Oceananigans.TurbulenceClosures.CATKEVerticalDiffusivities: CATKEVerticalDiffusivity using Oceananigans.Coriolis: ActiveCellEnstrophyConserving using Oceananigans.Units @@ -70,14 +70,14 @@ model = HydrostaticFreeSurfaceModel(; grid, # Initializing the model # -# the model can be initialized with custom values or with ecco2 fields. -# In this case, our ECCO2 dataset has access to a temperature and a salinity -# field, so we initialize T and S from ECCO2. +# the model can be initialized with custom values or with ecco fields. +# In this case, our ECCO dataset has access to a temperature and a salinity +# field, so we initialize T and S from ECCO. # We initialize our passive tracer with a surface blob near to the coasts of Libia @info "initializing model" libia_blob(x, y, z) = z > -20 || (x - 15)^2 + (y - 34)^2 < 1.5 ? 1 : 0 -set!(model, T = ECCO2Metadata(:temperature), S = ECCO2Metadata(:salinity), c = libia_blob) +set!(model, T = ECCOMetadata(:temperature), S = ECCOMetadata(:salinity), c = libia_blob) fig = Figure() ax = Axis(fig[1, 1]) diff --git a/examples/generate_atmos_dataset.jl b/examples/generate_atmos_dataset.jl index b4e395bd..24ef1cc0 100644 --- a/examples/generate_atmos_dataset.jl +++ b/examples/generate_atmos_dataset.jl @@ -4,9 +4,9 @@ using JLD2 time_indices = 1:1 -qt = ClimaOcean.JRA55.jra55_field_time_series(:specific_humidity; time_indices) -Tt = ClimaOcean.JRA55.jra55_field_time_series(:temperature; time_indices) -pt = ClimaOcean.JRA55.jra55_field_time_series(:sea_level_pressure; time_indices) +qt = ClimaOcean.JRA55.JRA55_field_time_series(:specific_humidity; time_indices) +Tt = ClimaOcean.JRA55.JRA55_field_time_series(:temperature; time_indices) +pt = ClimaOcean.JRA55.JRA55_field_time_series(:sea_level_pressure; time_indices) Nx, Ny, Nz = size(qt[1]) diff --git a/examples/generate_bathymetry.jl b/examples/generate_bathymetry.jl index 61767fae..4db9c54b 100644 --- a/examples/generate_bathymetry.jl +++ b/examples/generate_bathymetry.jl @@ -17,7 +17,7 @@ h = regrid_bathymetry(grid, height_above_water=1, minimum_depth=5) λ, φ, z = nodes(h) -land = interior(h) .> 0 +land = interior(h) .>= 0 interior(h)[land] .= NaN fig = Figure(size=(2400, 1200)) @@ -46,20 +46,25 @@ grid = LatitudeLongitudeGrid(CPU(); z = (0, 1), halo = (4, 4, 4)) -h_smooth = regrid_bathymetry(grid, height_above_water=1, interpolation_passes = 40) -h_rough = regrid_bathymetry(grid, height_above_water=1, interpolation_passes = 1) +h_smooth = regrid_bathymetry(grid, height_above_water=1, minimum_depth=10, interpolation_passes = 40) +h_rough = regrid_bathymetry(grid, height_above_water=1, minimum_depth=10, interpolation_passes = 1) +h_nolakes = regrid_bathymetry(grid, height_above_water=1, minimum_depth=10, connected_regions_allowed = 0) λ, φ, z = nodes(h_smooth) -land_smooth = interior(h_smooth) .> 0 +land_smooth = interior(h_smooth) .>= 0 interior(h_smooth)[land_smooth] .= NaN -land_rough = interior(h_rough) .> 0 +land_rough = interior(h_rough) .>= 0 interior(h_rough)[land_rough] .= NaN +land_nolakes = interior(h_nolakes) .>= 0 +interior(h_nolakes)[land_nolakes] .= NaN fig = Figure(resolution=(2400, 800)) ax = Axis(fig[1, 1]) -heatmap!(ax, λ, φ, interior(h_smooth, :, :, 1), nan_color=:white) #, colorrange=(-5000, 0)) +heatmap!(ax, λ, φ, interior(h_smooth, :, :, 1), nan_color=:white) #, colorrange=(-5000, 0)) ax = Axis(fig[1, 2]) -heatmap!(ax, λ, φ, interior(h_rough, :, :, 1), nan_color=:white) #, colorrange=(-5000, 0)) +heatmap!(ax, λ, φ, interior(h_rough, :, :, 1), nan_color=:white) #, colorrange=(-5000, 0)) +ax = Axis(fig[1, 3]) +heatmap!(ax, λ, φ, interior(h_nolakes, :, :, 1), nan_color=:white) #, colorrange=(-5000, 0)) display(fig) diff --git a/examples/generate_surface_fluxes.jl b/examples/generate_surface_fluxes.jl index 859c4c6f..1f54ba95 100644 --- a/examples/generate_surface_fluxes.jl +++ b/examples/generate_surface_fluxes.jl @@ -10,7 +10,7 @@ # We also need Oceananigans for the ImmersedBoundaryGrid and Field utilities, and CairoMakie to plot. using ClimaOcean -using ClimaOcean.ECCO2 +using ClimaOcean.ECCO using ClimaOcean.JRA55 using ClimaOcean.OceanSimulations using Oceananigans @@ -23,7 +23,7 @@ using CairoMakie # We can use this mask as an immersed boundary for our grid. # Let's create the grid and visualize the mask. -mask = ecco2_center_mask() +mask = ecco_mask() grid = mask.grid grid = ImmersedBoundaryGrid(grid, GridFittedBoundary(mask)) @@ -65,14 +65,14 @@ ocean = ocean_simulation(grid; momentum_advection = nothing, # Now that we have an atmosphere and a container for the ocean, we need to populate # our ocean with initial conditions. To do this, we can use the ECCO2 dataset by -# `set!`ting the model with the `ECCO2Metadata`. If no date is specified, +# `set!`ting the model with the `ECCOMetadata`. If no date is specified, # the fields corresponding to January 1st, 1992 (the first available date in # ECCO2) are used. # This command will download the fields to the local machine. set!(ocean.model; - T = ECCO2Metadata(:temperature), - S = ECCO2Metadata(:salinity)) + T = ECCOMetadata(:temperature), + S = ECCOMetadata(:salinity)) # The final step is to construct a coupled model. # The coupled model requires an ocean, which we have just constructed and initialized, diff --git a/examples/inspect_JRA55_data.jl b/examples/inspect_JRA55_data.jl index 1c1f0ccb..37403da3 100644 --- a/examples/inspect_JRA55_data.jl +++ b/examples/inspect_JRA55_data.jl @@ -5,8 +5,8 @@ using Oceananigans.Units using Printf time_indices = Colon() -Qswt = ClimaOcean.JRA55.jra55_field_time_series(:downwelling_shortwave_radiation; time_indices) -rht = ClimaOcean.JRA55.jra55_field_time_series(:relative_humidity; time_indices) +Qswt = ClimaOcean.JRA55.JRA55_field_time_series(:downwelling_shortwave_radiation; time_indices) +rht = ClimaOcean.JRA55.JRA55_field_time_series(:relative_humidity; time_indices) function lonlat2xyz(lons::AbstractVector, lats::AbstractVector) x = [cosd(lat) * cosd(lon) for lon in lons, lat in lats] diff --git a/examples/inspect_ecco2_data.jl b/examples/inspect_ecco_data.jl similarity index 76% rename from examples/inspect_ecco2_data.jl rename to examples/inspect_ecco_data.jl index 4c61392b..71ec6609 100644 --- a/examples/inspect_ecco2_data.jl +++ b/examples/inspect_ecco_data.jl @@ -1,28 +1,28 @@ -# # A quick look at ECCO2 data +# # A quick look at ECCO data # -# ClimaOcean can download and utilize data from the "ECCO2" state estimate, +# ClimaOcean can download and utilize data from the "ECCO" state estimate, # which stands for "Estimating the Circulation and Climate of the Ocean" --- two! # # This script shows how to download three-dimensional temperature and salinity fields -# from ECCO2, and makes a short animation to showcase the fields' content. +# from ECCO, and makes a short animation to showcase the fields' content. # # For this example we need Oceananigans for Field utilities, CairoMakie for plotting # Printf for nice labeling, and of course ClimaOcean to actually download and construct -# the ECCO2 fields. +# the ECCO fields. using Oceananigans using CairoMakie using Printf -using ClimaOcean: ECCO2 +using ClimaOcean: ECCO -# The function `ecco2_field` provided by `ClimaOcean.DataWrangling.ECCO2` will automatically -# download ECCO2 data if it doesn't already exist at the default location. +# The function `ecco_field` provided by `ClimaOcean.DataWrangling.ECCO` will automatically +# download ECCO data if it doesn't already exist at the default location. -T = ECCO2.ecco2_field(:temperature) -S = ECCO2.ecco2_field(:salinity) +T = ECCO.ecco_field(:temperature) +S = ECCO.ecco_field(:salinity) -# Next, we massage the ECCO2 data by inserting NaNs in "land cells", which +# Next, we massage the ECCO data by inserting NaNs in "land cells", which # are diagnosed by having an unphysically low temperature. Tp = parent(T) @@ -30,10 +30,10 @@ Sp = parent(S) Sp[Tp .< -10] .= NaN Tp[Tp .< -10] .= NaN -# # Plotting ECCO2 data +# # Plotting ECCO data # # We're ready to plot. We'll make an animation -# that depicts how the ECCO2 data changes with depth. +# that depicts how the ECCO data changes with depth. fig = Figure(size=(1200, 1400)) @@ -76,7 +76,7 @@ text!(axS, 50, 50, text=depth_str, justification=:center, fontsize=24) stillframes = 10 movingframes = Nz -record(fig, "ECCO2_temperature_salinity.gif", framerate=4) do io +record(fig, "ECCO_temperature_salinity.gif", framerate=4) do io [recordframe!(io) for _ = 1:stillframes] diff --git a/examples/mediterranean_simulation_with_ecco_restoring.jl b/examples/mediterranean_simulation_with_ecco_restoring.jl new file mode 100644 index 00000000..c5e37945 --- /dev/null +++ b/examples/mediterranean_simulation_with_ecco_restoring.jl @@ -0,0 +1,206 @@ +# # Mediterranean simulation with restoring to ECCO +# +# This Julia script is a comprehensive example of setting up and running a high-resolution ocean simulation for the Mediterranean Sea using +# the Oceananigans and ClimaOcean packages, with a focus on restoring temperature and salinity fields from the +# ECCO (Estimating the Circulation and Climate of the Ocean) dataset. +# +# The script is divided into several sections, each handling a specific part of the simulation setup and execution process. +# + +# ## Initial Setup with Package Imports +# +# The script begins by importing necessary Julia packages for visualization (GLMakie), +# ocean modeling (Oceananigans, ClimaOcean), and handling of dates and times (CFTime, Dates). +# These packages provide the foundational tools for creating the simulation environment, +# including grid setup, physical processes modeling, and data visualization. + +using GLMakie +using Oceananigans +using Oceananigans: architecture +using ClimaOcean +using ClimaOcean.ECCO +using ClimaOcean.ECCO: ECCO4Monthly +using Oceananigans.Units +using Printf + +using CFTime +using Dates + +# ## Grid Configuration for the Mediterranean Sea +# +# The script defines a high-resolution grid to represent the Mediterranean Sea, specifying the domain in terms of longitude (λ₁, λ₂), +# latitude (φ₁, φ₂), and a stretched vertical grid to capture the depth variation (z_faces). +# The grid resolution is set to approximately 1/15th of a degree, which translates to a spatial resolution of about 7 km. +# This section demonstrates the use of the LatitudeLongitudeGrid function to create a grid that matches the +# Mediterranean's geographical and bathymetric features. + +λ₁, λ₂ = ( 0, 42) # domain in longitude +φ₁, φ₂ = (30, 45) # domain in latitude +z_faces = stretched_vertical_faces(depth = 5000, + surface_layer_Δz = 2.5, + stretching = PowerLawStretching(1.070), + surface_layer_height = 50) + +Nx = 15 * 42 # 1 / 15th of a degree resolution +Ny = 15 * 15 # 1 / 15th of a degree resolution +Nz = length(z_faces) - 1 + +grid = LatitudeLongitudeGrid(CPU(); + size = (Nx, Ny, Nz), + latitude = (φ₁, φ₂), + longitude = (λ₁, λ₂), + z = z_faces, + halo = (7, 7, 7)) + +# ### Bathymetry Interpolation +# +# The script interpolates bathymetric data onto the grid, ensuring that the model accurately represents +# the sea floor's topography. Parameters such as `minimum_depth` and `interpolation_passes` +# are adjusted to refine the bathymetry representation. + +bottom_height = regrid_bathymetry(grid, + height_above_water = 1, + minimum_depth = 10, + interpolation_passes = 25, + connected_regions_allowed = 1) + +grid = ImmersedBoundaryGrid(grid, GridFittedBottom(bottom_height); active_cells_map = true) + +# ## Downloading ECCO data +# +# The model is initialized with temperature and salinity fields from the ECCO dataset, +# using the function `ECCO_restoring_forcing` to apply restoring forcings for these tracers. +# This allows us to nudge the model towards realistic temperature and salinity profiles. + +dates = DateTimeProlepticGregorian(1993, 1, 1) : Month(1) : DateTimeProlepticGregorian(1993, 12, 1) + +temperature = ECCOMetadata(:temperature, dates, ECCO4Monthly()) +salinity = ECCOMetadata(:salinity, dates, ECCO4Monthly()) + +FT = ECCO_restoring_forcing(temperature; timescale = 2days) +FS = ECCO_restoring_forcing(salinity; timescale = 2days) + +# Constructing the Simulation +# +# We construct an ocean simulation that evolves two tracers, temperature (:T), salinity (:S) +# and we pass the previously defined forcing that nudge these tracers + +ocean = ocean_simulation(grid; forcing = (T = FT, S = FS)) + +# Initializing the model +# +# the model can be initialized with custom values or with ecco fields. +# In this case, our ECCO dataset has access to a temperature and a salinity +# field, so we initialize T and S from ECCO. + +set!(ocean.model, T = temperature[1], S = salinity[1]) + +fig = Figure() +ax = Axis(fig[1, 1]) +heatmap!(ax, interior(model.tracers.T, :, :, Nz), colorrange = (10, 20), colormap = :thermal) +ax = Axis(fig[1, 2]) +heatmap!(ax, interior(model.tracers.S, :, :, Nz), colorrange = (35, 40), colormap = :haline) + +function progress(sim) + u, v, w = sim.model.velocities + T, S = sim.model.tracers + + @info @sprintf("Time: %s, Iteration %d, Δt %s, max(vel): (%.2e, %.2e, %.2e), max(T, S): %.2f, %.2f\n", + prettytime(sim.model.clock.time), + sim.model.clock.iteration, + prettytime(sim.Δt), + maximum(abs, u), maximum(abs, v), maximum(abs, w), + maximum(abs, T), maximum(abs, S)) +end + +ocean.callbacks[:progress] = Callback(progress, IterationInterval(10)) + +# ## Simulation warm up! +# +# We have regridded from the coarse solution of the ECCO dataset (half of a degree) to a +# fine grid (1/15th of a degree). The bathymetry might also have little mismatches +# that might crash the simulation. We warm up the simulation with a little +# time step for few iterations to allow the solution to adjust to the new grid +# bathymetry + +ocean.Δt = 10 +ocean.stop_iteration = 1000 +run!(ocean) + +# ## Run the real simulation +# +# Now that the solution has adjusted to the bathymetry we can ramp up the time +# step size. We use a `TimeStepWizard` to automatically adapt to a cfl of 0.2 + +wizard = TimeStepWizard(; cfl = 0.2, max_Δt = 10minutes, max_change = 1.1) + +coean.callbacks[:wizard] = Callback(wizard, IterationInterval(10)) + +# Let's reset the maximum number of iterations +ocean.stop_iteration = Inf + +ocean.output_writers[:surface_fields] = JLD2OutputWriter(model, merge(model.velocities, model.tracers); + indices = (:, :, Nz), + schedule = TimeInterval(1days), + overwrite_existing = true, + filename = "med_surface_field") + +run!(ocean) + +# Record a video +# +# Let's read the data and record a video of the Mediterranean Sea's surface +# (1) Zonal velocity (u) +# (2) Meridional velocity (v) +# (3) Temperature (T) +# (4) Salinity (S) + +u_series = FieldTimeSeries("med_surface_field.jld2", "u") +v_series = FieldTimeSeries("med_surface_field.jld2", "v") +T_series = FieldTimeSeries("med_surface_field.jld2", "T") +S_series = FieldTimeSeries("med_surface_field.jld2", "S") +c_series = FieldTimeSeries("med_surface_field.jld2", "c") +iter = Observable(1) + +u = @lift begin + f = interior(u_series[$iter], :, :, 1) + f[f .== 0] .= NaN + f +end +v = @lift begin + f = interior(v_series[$iter], :, :, 1) + f[f .== 0] .= NaN + f +end +T = @lift begin + f = interior(T_series[$iter], :, :, 1) + f[f .== 0] .= NaN + f +end +S = @lift begin + f = interior(S_series[$iter], :, :, 1) + f[f .== 0] .= NaN + f +end +c = @lift begin + f = interior(c_series[$iter], :, :, 1) + f[f .== 0] .= NaN + f +end + +fig = Figure() +ax = Axis(fig[1, 1], title = "surface zonal velocity ms⁻¹") +heatmap!(u) +ax = Axis(fig[1, 2], title = "surface meridional velocity ms⁻¹") +heatmap!(v) +ax = Axis(fig[2, 1], title = "surface temperature ᵒC") +heatmap!(T) +ax = Axis(fig[2, 2], title = "surface salinity psu") +heatmap!(S) +ax = Axis(fig[2, 3], title = "passive tracer -") +heatmap!(c) + +GLMakie.record(fig, "mediterranean_video.mp4", 1:length(u_series.times); framerate = 5) do i + @info "recording iteration $i" + iter[] = i +end \ No newline at end of file diff --git a/examples/near_global_omip_simulation.jl b/examples/near_global_omip_simulation.jl index 3e7558ef..eca17321 100644 --- a/examples/near_global_omip_simulation.jl +++ b/examples/near_global_omip_simulation.jl @@ -3,10 +3,13 @@ using Oceananigans using Oceananigans.Units using Oceananigans: architecture using ClimaOcean -using ClimaOcean.ECCO2 +using ClimaOcean.ECCO using ClimaOcean.OceanSimulations using ClimaOcean.OceanSeaIceModels +using CFTime +using Dates + ##### ##### Near - Global Ocean at 1/4th of a degree ##### @@ -39,7 +42,7 @@ bottom_height = retrieve_bathymetry(grid; connected_regions_allowed = 0) # An immersed boundary using a staircase representation of bathymetry -grid = ImmersedBoundaryGrid(grid, GridFittedBottom(bottom_height)) +grid = ImmersedBoundaryGrid(grid, GridFittedBottom(bottom_height); active_cells_map = true) ##### ##### The Ocean component @@ -49,11 +52,13 @@ grid = ImmersedBoundaryGrid(grid, GridFittedBottom(bottom_height)) ocean = ocean_simulation(grid) model = ocean.model +date = DateTimeProlepticGregorian(1993, 1, 1) + # We interpolate the initial conditions from the ECCO2 dataset -# (for the moment these are both 1st January 1992) +# (for the moment these are both 1st January 1993) set!(model, - T = ECCO2Metadata(:temperature), - S = ECCO2Metadata(:salinity)) + T = ECCO2Metadata(:temperature, date, ECCO2Daily()), + S = ECCO2Metadata(:salinity, date, ECCO2Daily())) ##### ##### The atmosphere @@ -187,4 +192,4 @@ ax = Axis(fig[2, 1], title = "Surface Temperature [Cᵒ]") heatmap!(ax, interior(T, :, :, grid.Nz), colorrange = (-1, 30), colormap = :magma) ax = Axis(fig[2, 1], title = "Turbulent Kinetic Energy [m²s⁻²]") -heatmap!(ax, interior(e, :, :, grid.Nz), colorrange = (0, 1e-3), colormap = :solar) \ No newline at end of file +heatmap!(ax, interior(e, :, :, grid.Nz), colorrange = (0, 1e-3), colormap = :solar) diff --git a/examples/single_column_simulation.jl b/examples/single_column_simulation.jl index 9b431e79..e8ef7370 100644 --- a/examples/single_column_simulation.jl +++ b/examples/single_column_simulation.jl @@ -20,7 +20,7 @@ using Oceananigans.BuoyancyModels: buoyancy_frequency using Oceananigans.Units: Time using ClimaOcean -using ClimaOcean.ECCO2: ECCO2Metadata +using ClimaOcean.ECCO using ClimaOcean.OceanSimulations using CairoMakie @@ -59,8 +59,8 @@ ocean = ocean_simulation(grid; start_time = time_ns() # Initial conditions -set!(ocean.model, T = ECCO2Metadata(:temperature), - S = ECCO2Metadata(:salinity), +set!(ocean.model, T = ECCOMetadata(:temperature), + S = ECCOMetadata(:salinity), e = 1e-6) elapsed = time_ns() - start_time @@ -324,10 +324,13 @@ Smax = maximum(interior(S)) Smin = minimum(interior(S)) xlims!(axSz, Smin - 0.2, Smax + 0.2) -display(fig) +save("single_column_profiles.png", fig) +nothing # hide -record(fig, "$(location_name)_single_column_simulation.mp4", 1:Nt, framerate=24) do nn - @info "Drawing frame $nn of $Nt..." - n[] = nn -end +# ![](single_column_profiles.png) + +# record(fig, "$(location_name)_single_column_simulation.mp4", 1:8:Nt, framerate=24) do nn +# @info "Drawing frame $nn of $Nt..." +# n[] = nn +# end diff --git a/examples/surface_flux_computation.jl b/examples/surface_flux_computation.jl deleted file mode 100644 index 9b786df6..00000000 --- a/examples/surface_flux_computation.jl +++ /dev/null @@ -1,101 +0,0 @@ - using SurfaceFluxes - using Thermodynamics - using StaticArrays - using ClimaOcean - - import CLIMAParameters - - using Thermodynamics: q_vap_saturation_from_density, partial_pressure_vapor - using SurfaceFluxes.Parameters: SurfaceFluxesParameters - using ClimaOcean.OceanSeaIceModels: - default_universal_function_parameters, - default_surface_flux_parameters - - const CP = CLIMAParameters - - function extrapolate_surface_density(params, atmos_state, surface_temperature) - Tₛ = surface_temperature - Tₐ = air_temperature(params, atmos_state) - Rmₐ = gas_constant_air(params, atmos_state) - ρₐ = air_density(params, atmos_state) - κ = cv_m(params, atmos_state) / Rmₐ - return ρₐ * (Tₛ / Tₐ)^κ -end - -function surface_saturation_specific_humidity(params, surface_temperature, atmos_state) - Tₛ = surface_temperature - ρₛ = atmos_state.ρ #extrapolate_surface_density(thermo_params, atmos_state, Tₛ) - - @show p★ = saturation_vapor_pressure(params, Tₛ, Liquid()) - - q = PhasePartition(thermo_params, atmos_thermo_state) - @show q - - @show pᵥ = partial_pressure_vapor(params, atmos_state.p, q) - @show p★ₐ = saturation_vapor_pressure(params, atmos_state.T, Liquid()) - @show q★ₐ = q_vap_saturation_from_density(params, atmos_state.T, atmos_state.ρ, p★ₐ) - @show pᵥ / p★ₐ - - q★ = q_vap_saturation_from_density(params, Tₛ, ρₛ, p★) - @show q★ - @show q - - return q★ -end - -FT = Float64 -thermo_params = Thermodynamics.Parameters.HierarchicalThermodynamicsParameters(FT) -businger_params = default_universal_function_parameters(FT) -surface_flux_parameters = default_surface_flux_parameters(thermo_params) - -#= -include(joinpath(pkgdir(SurfaceFluxes), "parameters", "create_parameters.jl")) -toml_dict = CP.create_toml_dict(FT; dict_type = "alias") -uf_type = SurfaceFluxes.UniversalFunctions.BusingerType() -param_set = create_parameters(toml_dict, uf_type) -thermo_params = SurfaceFluxes.Parameters.thermodynamics_params(param_set) -=# - -h = 2.0 # height at which measurements are made, in m -surface_velocity = SVector(0.0, 0.0) -atmos_velocity = SVector(4.0, 0.0) - -atmos_pressure = 101350.0 -atmos_temperature = 298.15 -atmos_specific_humidity = 0.03 - -atmos_thermo_state = Thermodynamics.PhaseEquil_pTq(thermo_params, - atmos_pressure, - atmos_temperature, - atmos_specific_humidity) - - -surface_temperature = Tₛ = 297.15 - -q₀ = 0.98 -c₁ = 640380 -c₂ = 5107.4 -qLY = q₀ * c₁ * exp(-c₂ / Tₛ) - -q★ = surface_saturation_specific_humidity(thermo_params, surface_temperature, atmos_thermo_state) -surface_thermo_state = Thermodynamics.PhaseEquil_pTq(thermo_params, - atmos_pressure, - surface_temperature, - q★) - - -# State at z=0, eg the "surface" -surface_dynamic_state = SurfaceFluxes.StateValues(0.0, surface_velocity, surface_thermo_state) - -# State at z=h, eg the "atmosphere" -atmos_dynamic_state = SurfaceFluxes.StateValues(h, atmos_velocity, atmos_thermo_state) - -momentum_roughness_length = 0.01 -buoyancy_roughness_length = 0.001 - -values = SurfaceFluxes.ValuesOnly(atmos_dynamic_state, - surface_dynamic_state, - momentum_roughness_length, - buoyancy_roughness_length) - -conditions = SurfaceFluxes.surface_conditions(surface_flux_parameters, values) diff --git a/experiments/freely_decaying_regional_simulation.jl b/experiments/freely_decaying_regional_simulation.jl index 23585c82..6130064e 100644 --- a/experiments/freely_decaying_regional_simulation.jl +++ b/experiments/freely_decaying_regional_simulation.jl @@ -7,7 +7,7 @@ using Oceananigans.Units: Time using ClimaOcean using ClimaOcean.OceanSeaIceModels: Radiation using ClimaOcean.DataWrangling.JRA55: JRA55_prescribed_atmosphere -using ClimaOcean.DataWrangling.ECCO2: ecco2_field +using ClimaOcean.DataWrangling.ECCO: ecco_field # using GLMakie using Printf @@ -21,9 +21,9 @@ arch = CPU() epoch = Date(1992, 1, 1) date = Date(1992, 10, 1) start_seconds = Second(date - epoch).value -Te = ecco2_field(:temperature, date) -Se = ecco2_field(:salinity, date) -# ℋe = ecco2_field(:sea_ice_thickness, date) +Te = ecco_field(:temperature, date) +Se = ecco_field(:salinity, date) +# ℋe = ecco_field(:sea_ice_thickness, date) land = interior(Te) .< -10 interior(Te)[land] .= NaN diff --git a/prototype_omip_simulation/Manifest.toml b/prototype_omip_simulation/Manifest.toml index 2c4f18be..9ec3daf5 100644 --- a/prototype_omip_simulation/Manifest.toml +++ b/prototype_omip_simulation/Manifest.toml @@ -1,8 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.10.3" +julia_version = "1.10.4" manifest_format = "2.0" -project_hash = "ad94f0873a50599743724c16c7dfb94779a2964d" +project_hash = "1cab777b23bf99dac075dcd3cc1d9faf7933f586" [[deps.ANSIColoredPrinters]] git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" @@ -62,9 +62,9 @@ version = "1.1.1" [[deps.ArrayInterface]] deps = ["Adapt", "LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "133a240faec6e074e07c31ee75619c90544179cf" +git-tree-sha1 = "ed2ec3c9b483842ae59cd273834e5b46206d6dda" uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.10.0" +version = "7.11.0" [deps.ArrayInterface.extensions] ArrayInterfaceBandedMatricesExt = "BandedMatrices" @@ -107,9 +107,9 @@ version = "0.5.0" uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[deps.BitFlags]] -git-tree-sha1 = "2dc09997850d68179b69dafb58ae806167a32b1b" +git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d" uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" -version = "0.1.8" +version = "0.1.9" [[deps.BitTwiddlingConvenienceFunctions]] deps = ["Static"] @@ -142,64 +142,69 @@ version = "0.1.3" [[deps.CPUSummary]] deps = ["CpuId", "IfElse", "PrecompileTools", "Static"] -git-tree-sha1 = "601f7e7b3d36f18790e2caf83a882d88e9b71ff1" +git-tree-sha1 = "585a387a490f1c4bd88be67eea15b93da5e85db7" uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" -version = "0.2.4" +version = "0.2.5" [[deps.CUDA]] deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CUDA_Driver_jll", "CUDA_Runtime_Discovery", "CUDA_Runtime_jll", "Crayons", "DataFrames", "ExprTools", "GPUArrays", "GPUCompiler", "KernelAbstractions", "LLVM", "LLVMLoopInfo", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "NVTX", "Preferences", "PrettyTables", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "StaticArrays", "Statistics"] -git-tree-sha1 = "dd1c682b372b6791b69f6823afe364fc92a0146c" +git-tree-sha1 = "6e945e876652f2003e6ca74e19a3c45017d3e9f6" uuid = "052768ef-5323-5732-b1bb-66c8b64840ba" -version = "5.3.1" -weakdeps = ["ChainRulesCore", "SpecialFunctions"] +version = "5.4.2" [deps.CUDA.extensions] ChainRulesCoreExt = "ChainRulesCore" + EnzymeCoreExt = "EnzymeCore" SpecialFunctionsExt = "SpecialFunctions" + [deps.CUDA.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" + [[deps.CUDA_Driver_jll]] deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"] -git-tree-sha1 = "dc172b558adbf17952001e15cf0d6364e6d78c2f" +git-tree-sha1 = "c48f9da18efd43b6b7adb7ee1f93fe5f2926c339" uuid = "4ee394cb-3365-5eb0-8335-949819d2adfc" -version = "0.8.1+0" +version = "0.9.0+0" [[deps.CUDA_Runtime_Discovery]] deps = ["Libdl"] -git-tree-sha1 = "38f830504358e9972d2a0c3e5d51cb865e0733df" +git-tree-sha1 = "f3b237289a5a77c759b2dd5d4c2ff641d67c4030" uuid = "1af6417a-86b4-443c-805f-a4643ffb695f" -version = "0.2.4" +version = "0.3.4" [[deps.CUDA_Runtime_jll]] deps = ["Artifacts", "CUDA_Driver_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "TOML"] -git-tree-sha1 = "4ca7d6d92075906c2ce871ea8bba971fff20d00c" +git-tree-sha1 = "bcba305388e16aa5c879e896726db9e71b4942c6" uuid = "76a88914-d11a-5bdc-97e0-2f5a05c973a2" -version = "0.12.1+0" +version = "0.14.0+1" [[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "a4c43f59baa34011e303e76f5c8c91bf58415aaf" +git-tree-sha1 = "a2f1c8c668c8e3cb4cca4e57a8efdb09067bb3fd" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.0+1" +version = "1.18.0+2" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] -git-tree-sha1 = "575cd02e080939a33b6df6c5853d14924c08e35b" +git-tree-sha1 = "71acdbf594aab5bbb2cec89b208c41b4c411e49f" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.23.0" +version = "1.24.0" weakdeps = ["SparseArrays"] [deps.ChainRulesCore.extensions] ChainRulesCoreSparseArraysExt = "SparseArrays" [[deps.ClimaOcean]] -deps = ["Adapt", "CUDA", "ClimaSeaIce", "CubicSplines", "DataDeps", "Dates", "Downloads", "FFMPEG", "ImageMorphology", "JLD2", "JSON3", "KernelAbstractions", "NCDatasets", "Oceananigans", "Printf", "SeawaterPolynomials", "StaticArrays", "Statistics", "SurfaceFluxes", "Thermodynamics"] +deps = ["Adapt", "CFTime", "CUDA", "ClimaSeaIce", "CubicSplines", "DataDeps", "Dates", "Downloads", "ImageMorphology", "JLD2", "KernelAbstractions", "NCDatasets", "Oceananigans", "Printf", "Scratch", "SeawaterPolynomials", "StaticArrays", "Statistics", "SurfaceFluxes", "Thermodynamics"] path = ".." uuid = "0376089a-ecfe-4b0e-a64f-9c555d74d754" version = "0.2.0" [[deps.ClimaSeaIce]] deps = ["Adapt", "KernelAbstractions", "Oceananigans", "RootSolvers", "Roots", "SeawaterPolynomials"] -git-tree-sha1 = "e25e43451edd449c3dcc899bd447983d7b76a59f" +git-tree-sha1 = "be4f97676cd18dc1566637e03c189b04c3fb4c59" repo-rev = "main" repo-url = "https://github.com/CliMA/ClimaSeaIce.jl.git" uuid = "6ba0ff68-24e6-4315-936c-2e99227c95a4" @@ -235,15 +240,15 @@ weakdeps = ["SpecialFunctions"] [[deps.Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a" +git-tree-sha1 = "362a287c3aa50601b0bc359053d5c2468f0e7ce0" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.12.10" +version = "0.12.11" [[deps.CommonDataModel]] deps = ["CFTime", "DataStructures", "Dates", "Preferences", "Printf", "Statistics"] -git-tree-sha1 = "d7d7b58e149f19c322840a50d1bc20e8c23addb4" +git-tree-sha1 = "d6fb5bf939a2753c74984b11434ea25d6c397a58" uuid = "1fbeeb36-5f17-413c-809b-666fb144f157" -version = "0.3.5" +version = "0.3.6" [[deps.CommonSolve]] git-tree-sha1 = "0eee5eb66b1cf62cd6ad1b460238e60e4b09400c" @@ -258,9 +263,9 @@ version = "0.3.0" [[deps.Compat]] deps = ["TOML", "UUIDs"] -git-tree-sha1 = "c955881e3c981181362ae4088b35995446298b80" +git-tree-sha1 = "b1c55339b7c6c350ee89f2c1604299660525b248" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.14.0" +version = "4.15.0" weakdeps = ["Dates", "LinearAlgebra"] [deps.Compat.extensions] @@ -396,9 +401,9 @@ version = "0.8.6" [[deps.Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] -git-tree-sha1 = "f15a91e6e3919055efa4f206f942a73fedf5dfe6" +git-tree-sha1 = "5461b2a67beb9089980e2f8f25145186b6d34f91" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.4.0" +version = "1.4.1" [[deps.DocumenterTools]] deps = ["Base64", "DocStringExtensions", "LibGit2"] @@ -424,9 +429,9 @@ version = "0.1.10" [[deps.Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c" +git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.5.0+0" +version = "2.6.2+0" [[deps.ExprTools]] git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" @@ -468,15 +473,15 @@ uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[deps.FixedPointNumbers]] deps = ["Statistics"] -git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" +git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.4" +version = "0.8.5" [[deps.Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] +git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.93+0" +version = "2.13.96+0" [[deps.ForwardDiff]] deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] @@ -490,15 +495,15 @@ weakdeps = ["StaticArrays"] [[deps.FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0" +git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.1+0" +version = "2.13.2+0" [[deps.FriBidi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.10+0" +version = "1.0.14+0" [[deps.Future]] deps = ["Random"] @@ -511,9 +516,9 @@ version = "6.2.1+6" [[deps.GPUArrays]] deps = ["Adapt", "GPUArraysCore", "LLVM", "LinearAlgebra", "Printf", "Random", "Reexport", "Serialization", "Statistics"] -git-tree-sha1 = "68e8ff56a4a355a85d2784b94614491f8c900cde" +git-tree-sha1 = "c154546e322a9c73364e8a60430b0f79b812d320" uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" -version = "10.1.0" +version = "10.2.0" [[deps.GPUArraysCore]] deps = ["Adapt"] @@ -523,9 +528,9 @@ version = "0.1.6" [[deps.GPUCompiler]] deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "Scratch", "TimerOutputs", "UUIDs"] -git-tree-sha1 = "1600477fba37c9fc067b9be21f5e8101f24a8865" +git-tree-sha1 = "518ebd058c9895de468a8c255797b0c53fdb44dd" uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" -version = "0.26.4" +version = "0.26.5" [[deps.Gettext_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] @@ -547,9 +552,9 @@ version = "2.44.0+2" [[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "359a1ba2e320790ddbe4ee8b4d54a305c0ea2aff" +git-tree-sha1 = "7c82e6a6cd34e9d935e9aa4051b66c6ff3af59ba" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.80.0+0" +version = "2.80.2+0" [[deps.Glob]] git-tree-sha1 = "97285bbd5230dd766e9ef6749b80fc617126d496" @@ -576,9 +581,9 @@ version = "1.14.2+1" [[deps.HTTP]] deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "8e59b47b9dc525b70550ca082ce85bcd7f5477cd" +git-tree-sha1 = "d1d712be3164d61d1fb98e7ce9bcbc6cc06b45ed" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.10.5" +version = "1.10.8" [[deps.HarfBuzz_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] @@ -600,9 +605,9 @@ version = "2.10.0+0" [[deps.IOCapture]] deps = ["Logging", "Random"] -git-tree-sha1 = "8b72179abc660bfab5e28472e019392b97d0985c" +git-tree-sha1 = "b6d6bfdd7ce25b0f9b2f6b3dd56b2673a66c8770" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.4" +version = "0.2.5" [[deps.IfElse]] git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" @@ -629,15 +634,21 @@ version = "0.2.1" [[deps.InlineStrings]] deps = ["Parsers"] -git-tree-sha1 = "9cc2baf75c6d09f9da536ddf58eb2f29dedaf461" +git-tree-sha1 = "86356004f30f8e737eff143d57d41bd580e437aa" uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" -version = "1.4.0" +version = "1.4.1" + + [deps.InlineStrings.extensions] + ArrowTypesExt = "ArrowTypes" + + [deps.InlineStrings.weakdeps] + ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5fdf2fe6724d8caabf43b557b84ce53f3b7e2f6b" +git-tree-sha1 = "be50fe8df3acbffa0274a744f1a99d29c45a57f4" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.0.2+0" +version = "2024.1.0+0" [[deps.InteractiveUtils]] deps = ["Markdown"] @@ -645,9 +656,9 @@ uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" [[deps.InverseFunctions]] deps = ["Test"] -git-tree-sha1 = "896385798a8d49a255c398bd49162062e4a4c435" +git-tree-sha1 = "e7cbed5032c4c397a6ac23d1493f3289e01231c4" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.13" +version = "0.1.14" weakdeps = ["Dates"] [deps.InverseFunctions.extensions] @@ -675,10 +686,10 @@ uuid = "82899510-4779-5014-852e-03e436cf321d" version = "1.0.0" [[deps.JLD2]] -deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "Pkg", "PrecompileTools", "Printf", "Reexport", "Requires", "TranscodingStreams", "UUIDs"] -git-tree-sha1 = "5ea6acdd53a51d897672edb694e3cc2912f3f8a7" +deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "Pkg", "PrecompileTools", "Reexport", "Requires", "TranscodingStreams", "UUIDs", "Unicode"] +git-tree-sha1 = "bdbe8222d2f5703ad6a7019277d149ec6d78c301" uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" -version = "0.4.46" +version = "0.4.48" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] @@ -706,9 +717,9 @@ version = "0.2.1+0" [[deps.KernelAbstractions]] deps = ["Adapt", "Atomix", "InteractiveUtils", "LinearAlgebra", "MacroTools", "PrecompileTools", "Requires", "SparseArrays", "StaticArrays", "UUIDs", "UnsafeAtomics", "UnsafeAtomicsLLVM"] -git-tree-sha1 = "ed7167240f40e62d97c1f5f7735dea6de3cc5c49" +git-tree-sha1 = "b8fcefe4418e4a7a2c3aaac883fecddd8efbe286" uuid = "63c18a36-062a-441e-b654-da1e3ab1ce7c" -version = "0.9.18" +version = "0.9.21" [deps.KernelAbstractions.extensions] EnzymeExt = "EnzymeCore" @@ -717,16 +728,16 @@ version = "0.9.18" EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" [[deps.LAME_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd" uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.1+0" +version = "3.100.2+0" [[deps.LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Preferences", "Printf", "Requires", "Unicode"] -git-tree-sha1 = "839c82932db86740ae729779e610f07a1640be9a" +git-tree-sha1 = "389aea28d882a40b5e1747069af71bdbd47a1cae" uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "6.6.3" +version = "7.2.1" weakdeps = ["BFloat16s"] [deps.LLVM.extensions] @@ -759,10 +770,10 @@ weakdeps = ["Serialization"] SerializationExt = ["Serialization"] [[deps.LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "70c5da094887fd2cae843b8db33920bac4b6f07d" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.1+0" +version = "2.10.2+0" [[deps.LaTeXStrings]] git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" @@ -818,16 +829,16 @@ uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" version = "3.2.2+1" [[deps.Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] -git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] +git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.7+0" +version = "1.8.11+0" [[deps.Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.42.0+0" +version = "1.49.0+0" [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -837,15 +848,15 @@ version = "1.17.0+0" [[deps.Libmount_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "dae976433497a2f841baadea93d27e68f1a12a97" +git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.39.3+0" +version = "2.40.1+0" [[deps.Libuuid_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0a04a1318df1bf510beb2562cf90fb0c386f58c4" +git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.39.3+1" +version = "2.40.1+0" [[deps.LinearAlgebra]] deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] @@ -853,9 +864,9 @@ uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [[deps.LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "18144f3e9cbe9b15b070288eef858f71b291ce37" +git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.27" +version = "0.3.28" [deps.LogExpFunctions.extensions] LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -878,9 +889,9 @@ version = "1.0.3" [[deps.LoopVectorization]] deps = ["ArrayInterface", "CPUSummary", "CloseOpenIntervals", "DocStringExtensions", "HostCPUFeatures", "IfElse", "LayoutPointers", "LinearAlgebra", "OffsetArrays", "PolyesterWeave", "PrecompileTools", "SIMDTypes", "SLEEFPirates", "Static", "StaticArrayInterface", "ThreadingUtilities", "UnPack", "VectorizationBase"] -git-tree-sha1 = "a13f3be5d84b9c95465d743c82af0b094ef9c2e2" +git-tree-sha1 = "8f6786d8b2b3248d79db3ad359ce95382d5a6df8" uuid = "bdcacae8-1622-11e9-2a5c-532679323890" -version = "0.12.169" +version = "0.12.170" weakdeps = ["ChainRulesCore", "ForwardDiff", "SpecialFunctions"] [deps.LoopVectorization.extensions] @@ -894,10 +905,10 @@ uuid = "5ced341a-0733-55b8-9ab6-a4889d929147" version = "1.9.4+0" [[deps.MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "72dc3cf284559eb8f53aa593fe62cb33f83ed0c0" +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] +git-tree-sha1 = "80b2833b56d466b3858d565adcd16a4a05f2089b" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.0.0+0" +version = "2024.1.0+0" [[deps.MPI]] deps = ["Distributed", "DocStringExtensions", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "PkgVersion", "PrecompileTools", "Requires", "Serialization", "Sockets"] @@ -915,21 +926,21 @@ version = "0.20.16" [[deps.MPICH_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "656036b9ed6f942d35e536e249600bc31d0f9df8" +git-tree-sha1 = "4099bb6809ac109bfc17d521dad33763bcf026b7" uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" -version = "4.2.0+0" +version = "4.2.1+1" [[deps.MPIPreferences]] deps = ["Libdl", "Preferences"] -git-tree-sha1 = "8f6af051b9e8ec597fa09d8885ed79fd582f33c9" +git-tree-sha1 = "c105fe467859e7f6e9a852cb15cb4301126fac07" uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" -version = "0.1.10" +version = "0.1.11" [[deps.MPItrampoline_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "77c3bd69fdb024d75af38713e883d0f249ce19c2" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "8c35d5420193841b2f367e658540e8d9e0601ed0" uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" -version = "5.3.2+0" +version = "5.4.0+0" [[deps.MacroTools]] deps = ["Markdown", "Random"] @@ -995,9 +1006,9 @@ version = "2023.1.10" [[deps.NCDatasets]] deps = ["CFTime", "CommonDataModel", "DataStructures", "Dates", "DiskArrays", "NetCDF_jll", "NetworkOptions", "Printf"] -git-tree-sha1 = "d40d24d12f710c39d3a66be99c567ce0032f28a7" +git-tree-sha1 = "a640912695952b074672edb5f9aaee2f7f9fd59a" uuid = "85f8d34a-cbdd-5861-8df4-14fed0d494ab" -version = "0.14.3" +version = "0.14.4" [[deps.NVTX]] deps = ["Colors", "JuliaNVTXCallbacks_jll", "Libdl", "NVTX_jll"] @@ -1035,11 +1046,11 @@ version = "1.2.0" [[deps.Oceananigans]] deps = ["Adapt", "CUDA", "Crayons", "CubedSphere", "Dates", "Distances", "DocStringExtensions", "FFTW", "Glob", "IncompleteLU", "InteractiveUtils", "IterativeSolvers", "JLD2", "KernelAbstractions", "LinearAlgebra", "Logging", "MPI", "NCDatasets", "OffsetArrays", "OrderedCollections", "PencilArrays", "PencilFFTs", "Pkg", "Printf", "Random", "Rotations", "SeawaterPolynomials", "SparseArrays", "Statistics", "StructArrays"] -git-tree-sha1 = "82eddd14528a13419be8b75da9daa23fb2a14363" +git-tree-sha1 = "f5005c8637ebbc10dbbec3b3732cca11d39249ce" repo-rev = "main" repo-url = "https://github.com/CliMA/Oceananigans.jl.git" uuid = "9e8cae18-63c1-5223-a75c-80ca9d6e9a09" -version = "0.90.12" +version = "0.91.4" [deps.Oceananigans.extensions] OceananigansEnzymeExt = "Enzyme" @@ -1073,22 +1084,22 @@ uuid = "05823500-19ac-5b8b-9628-191a04bc5112" version = "0.8.1+2" [[deps.OpenMPI_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "PMIx_jll", "TOML", "Zlib_jll", "libevent_jll", "prrte_jll"] -git-tree-sha1 = "f46caf663e069027a06942d00dced37f1eb3d8ad" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"] +git-tree-sha1 = "a9de2f1fc98b92f8856c640bf4aec1ac9b2a0d86" uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" -version = "5.0.2+0" +version = "5.0.3+0" [[deps.OpenSSL]] deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "OpenSSL_jll", "Sockets"] -git-tree-sha1 = "af81a32750ebc831ee28bdaaba6e1067decef51e" +git-tree-sha1 = "38cb508d080d21dc1128f7fb04f20387ed4c0af4" uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" -version = "1.4.2" +version = "1.4.3" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3da7367955dcc5c54c1ba4d402ccdc09a1a3e046" +git-tree-sha1 = "a028ee3cb5641cccc4c24e90c36b0a4f7707bdf5" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.13+1" +version = "3.0.14+0" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -1108,10 +1119,10 @@ uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" version = "1.6.3" [[deps.OrthogonalSphericalShellGrids]] -deps = ["CUDA", "Documenter", "DocumenterTools", "FFMPEG", "JLD2", "JSON3", "KernelAbstractions", "Oceananigans", "OffsetArrays", "Printf"] -git-tree-sha1 = "ad2ad883a638e387818d0e2b270c67fd4dcea89f" +deps = ["Adapt", "CUDA", "Documenter", "DocumenterTools", "FFMPEG", "JLD2", "JSON3", "KernelAbstractions", "Oceananigans", "OffsetArrays", "Printf"] +git-tree-sha1 = "1a31c10d9eeec98bfdbb66282b39be643db31fea" repo-rev = "main" -repo-url = "https://github.com/simone-silvestri/OrthogonalSphericalShellGrids.jl" +repo-url = "https://github.com/simone-silvestri/OrthogonalSphericalShellGrids.jl.git" uuid = "c2be9673-fb75-4747-82dc-aa2bb9f4aed0" version = "0.1.0" @@ -1126,12 +1137,6 @@ deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" version = "10.42.0+1" -[[deps.PMIx_jll]] -deps = ["Artifacts", "Hwloc_jll", "JLLWrappers", "Libdl", "Zlib_jll", "libevent_jll"] -git-tree-sha1 = "360f48126b5f2c2f0c833be960097f7c62705976" -uuid = "32165bc3-0280-59bc-8c0b-c33b6203efab" -version = "4.2.9+0" - [[deps.PackageExtensionCompat]] git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" @@ -1152,15 +1157,17 @@ version = "2.8.1" [[deps.PencilArrays]] deps = ["Adapt", "JSON3", "LinearAlgebra", "MPI", "OffsetArrays", "Random", "Reexport", "StaticArrayInterface", "StaticArrays", "StaticPermutations", "Strided", "TimerOutputs", "VersionParsing"] -git-tree-sha1 = "6510e851700a851944f7ffa5cd990cced4802ad2" +git-tree-sha1 = "fa85ac32172d96cfdb91dbc53e8e57007e5a2b5a" uuid = "0e08944d-e94e-41b1-9406-dcf66b6a9d2e" -version = "0.19.3" +version = "0.19.5" [deps.PencilArrays.extensions] + PencilArraysAMDGPUExt = ["AMDGPU"] PencilArraysDiffEqExt = ["DiffEqBase"] PencilArraysHDF5Ext = ["HDF5"] [deps.PencilArrays.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" @@ -1172,9 +1179,9 @@ version = "0.15.1" [[deps.Pixman_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "64779bc4c9784fee475689a1752ef4d5747c5e87" +git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.42.2+0" +version = "0.43.4+0" [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] @@ -1213,9 +1220,9 @@ version = "1.4.3" [[deps.PrettyTables]] deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "88b895d13d53b5577fd53379d913b9ab9ac82660" +git-tree-sha1 = "66b20dd35966a748321d3b2537c4584cf40387c7" uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "2.3.1" +version = "2.3.2" [[deps.Printf]] deps = ["Unicode"] @@ -1308,9 +1315,9 @@ version = "2.1.5" [[deps.Rotations]] deps = ["LinearAlgebra", "Quaternions", "Random", "StaticArrays"] -git-tree-sha1 = "2a0a5d8569f481ff8840e3b7c84bbf188db6a3fe" +git-tree-sha1 = "5680a9276685d392c87407df00d57c9924d9f11e" uuid = "6038ab10-8711-5258-84ad-4b1120ba62dc" -version = "1.7.0" +version = "1.7.1" weakdeps = ["RecipesBase"] [deps.Rotations.extensions] @@ -1344,9 +1351,9 @@ version = "0.3.4" [[deps.SentinelArrays]] deps = ["Dates", "Random"] -git-tree-sha1 = "0e7508ff27ba32f26cd459474ca2ede1bc10991f" +git-tree-sha1 = "90b4f68892337554d31cdcdbe19e48989f26c7e6" uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.4.1" +version = "1.4.3" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -1372,9 +1379,9 @@ version = "1.10.0" [[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "e2cfc4012a19088254b3950b85c3c1d8882d864d" +git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.3.1" +version = "2.4.0" weakdeps = ["ChainRulesCore"] [deps.SpecialFunctions.extensions] @@ -1405,9 +1412,9 @@ weakdeps = ["OffsetArrays", "StaticArrays"] [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "bf074c045d3d5ffd956fa0a461da38a44685d6b2" +git-tree-sha1 = "6e00379a24597be4ae1ee6b2d882e15392040132" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.3" +version = "1.9.5" weakdeps = ["ChainRulesCore", "Statistics"] [deps.StaticArrays.extensions] @@ -1415,9 +1422,9 @@ weakdeps = ["ChainRulesCore", "Statistics"] StaticArraysStatisticsExt = "Statistics" [[deps.StaticArraysCore]] -git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" +git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.2" +version = "1.4.3" [[deps.StaticPermutations]] git-tree-sha1 = "193c3daa18ff3e55c1dae66acb6a762c4a3bdb0b" @@ -1437,15 +1444,15 @@ version = "1.7.0" [[deps.Strided]] deps = ["LinearAlgebra", "StridedViews", "TupleTools"] -git-tree-sha1 = "40c69be0e1b72ee2f42923b7d1ff13e0b04e675c" +git-tree-sha1 = "bd9bd1c70cfc115cc3a30213fc725125a6b43652" uuid = "5e0ebb24-38b0-5f93-81fe-25c709ecae67" -version = "2.0.4" +version = "2.1.0" [[deps.StridedViews]] deps = ["LinearAlgebra", "PackageExtensionCompat"] -git-tree-sha1 = "5b765c4e401693ab08981989f74a36a010aa1d8e" +git-tree-sha1 = "2917996ce0fa6b8a3a85240a5e9ff930e2aeaa43" uuid = "4db3bf67-4bd7-4b4e-b153-31dc3fb37143" -version = "0.2.2" +version = "0.3.1" weakdeps = ["CUDA"] [deps.StridedViews.extensions] @@ -1487,17 +1494,15 @@ version = "7.2.1+1" [[deps.SurfaceFluxes]] deps = ["DocStringExtensions", "RootSolvers", "Thermodynamics"] -git-tree-sha1 = "c2c43206af0d861e018f746286d1af036aa7bc3a" -repo-rev = "glw/generalize-parameters" -repo-url = "https://github.com/glwagner/SurfaceFluxes.jl.git" +git-tree-sha1 = "89c701c87f378ce95e7ddbcd69b8f1106ba8b968" uuid = "49b00bb7-8bd4-4f2b-b78c-51cd0450215f" -version = "0.9.2" +version = "0.11.0" [deps.SurfaceFluxes.extensions] - CreateParametersExt = "CLIMAParameters" + CreateParametersExt = "ClimaParams" [deps.SurfaceFluxes.weakdeps] - CLIMAParameters = "6eacf6c3-8458-43b9-ae03-caf5306d3d53" + ClimaParams = "5c42b081-d73a-476f-9059-fd94b934656c" [[deps.TOML]] deps = ["Dates"] @@ -1545,11 +1550,9 @@ uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [[deps.Thermodynamics]] deps = ["DocStringExtensions", "KernelAbstractions", "Random", "RootSolvers"] -git-tree-sha1 = "48098ebfee40374b8415b602163e02aa7c43d32b" -repo-rev = "glw/density-example" -repo-url = "https://github.com/glwagner/Thermodynamics.jl.git" +git-tree-sha1 = "deac04ad36638b10fde82470d5f128419f627e9a" uuid = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c" -version = "0.12.5" +version = "0.12.6" [deps.Thermodynamics.extensions] CreateParametersExt = "ClimaParams" @@ -1571,14 +1574,14 @@ version = "0.5.0" [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] -git-tree-sha1 = "f548a9e9c490030e545f72074a41edfd0e5bcdd7" +git-tree-sha1 = "5a13ae8a41237cff5ecf34f73eb1b8f42fff6531" uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" -version = "0.5.23" +version = "0.5.24" [[deps.TranscodingStreams]] -git-tree-sha1 = "71509f04d045ec714c4748c785a59045c3736349" +git-tree-sha1 = "d73336d81cafdc277ff45558bb7eaa2b04a8e472" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.10.7" +version = "0.10.10" weakdeps = ["Random", "Test"] [deps.TranscodingStreams.extensions] @@ -1613,15 +1616,15 @@ version = "0.2.1" [[deps.UnsafeAtomicsLLVM]] deps = ["LLVM", "UnsafeAtomics"] -git-tree-sha1 = "323e3d0acf5e78a56dfae7bd8928c989b4f3083e" +git-tree-sha1 = "d9f5962fecd5ccece07db1ff006fb0b5271bdfdd" uuid = "d80eeb9a-aca5-4d75-85e5-170c8b632249" -version = "0.1.3" +version = "0.1.4" [[deps.VectorizationBase]] deps = ["ArrayInterface", "CPUSummary", "HostCPUFeatures", "IfElse", "LayoutPointers", "Libdl", "LinearAlgebra", "SIMDTypes", "Static", "StaticArrayInterface"] -git-tree-sha1 = "ac377f0a248753a1b1d58bbc92a64f5a726dfb71" +git-tree-sha1 = "e863582a41c5731f51fd050563ae91eb33cf09be" uuid = "3d5dd08c-fd9d-11e8-17fa-ed2836048c2f" -version = "0.21.66" +version = "0.21.68" [[deps.VersionParsing]] git-tree-sha1 = "58d6e80b4ee071f5efd07fda82cb9fbe17200868" @@ -1630,9 +1633,9 @@ version = "1.3.0" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "532e22cf7be8462035d092ff21fada7527e2c488" +git-tree-sha1 = "52ff2af32e591541550bd753c0da8b9bc92bb9d9" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.12.6+0" +version = "2.12.7+0" [[deps.XSLT_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] @@ -1665,16 +1668,16 @@ uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" version = "1.1.4+0" [[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.4+4" +version = "1.3.6+0" [[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.10+4" +version = "0.9.11+0" [[deps.Xorg_libpthread_stubs_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1712,10 +1715,10 @@ uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" version = "1.1.2+0" [[deps.libaom_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1827acba325fdcdf1d2647fc8d5301dd9ba43a9d" uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" -version = "3.4.0+0" +version = "3.9.0+0" [[deps.libass_jll]] deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] @@ -1728,12 +1731,6 @@ deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" version = "5.8.0+1" -[[deps.libevent_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll"] -git-tree-sha1 = "f04ec6d9a186115fb38f858f05c0c4e1b7fc9dcb" -uuid = "1080aeaf-3a6a-583e-a51c-c537b09f60ec" -version = "2.1.13+1" - [[deps.libfdk_aac_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55" @@ -1763,17 +1760,17 @@ deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" version = "1.52.0+1" +[[deps.oneTBB_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" +uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" +version = "2021.12.0+0" + [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" version = "17.4.0+2" -[[deps.prrte_jll]] -deps = ["Artifacts", "Hwloc_jll", "JLLWrappers", "Libdl", "PMIx_jll", "libevent_jll"] -git-tree-sha1 = "5adb2d7a18a30280feb66cad6f1a1dfdca2dc7b0" -uuid = "eb928a42-fffd-568d-ab9c-3f5d54fc65b9" -version = "3.0.2+0" - [[deps.x264_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2" diff --git a/prototype_omip_simulation/Project.toml b/prototype_omip_simulation/Project.toml index a12db012..e8e6b41f 100644 --- a/prototype_omip_simulation/Project.toml +++ b/prototype_omip_simulation/Project.toml @@ -1,7 +1,11 @@ [deps] +Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +CFTime = "179af706-886a-5703-950a-314cd64e0468" ClimaOcean = "0376089a-ecfe-4b0e-a64f-9c555d74d754" ClimaSeaIce = "6ba0ff68-24e6-4315-936c-2e99227c95a4" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" +KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" Oceananigans = "9e8cae18-63c1-5223-a75c-80ca9d6e9a09" OrthogonalSphericalShellGrids = "c2be9673-fb75-4747-82dc-aa2bb9f4aed0" SeawaterPolynomials = "d496a93d-167e-4197-9f49-d3af4ff8fe40" diff --git a/prototype_omip_simulation/comparison_coare.jl b/prototype_omip_simulation/comparison_coare.jl deleted file mode 100644 index 9db067f1..00000000 --- a/prototype_omip_simulation/comparison_coare.jl +++ /dev/null @@ -1,118 +0,0 @@ -using Printf -using Oceananigans.Units -using ClimaOcean -using Oceananigans -using Oceananigans.Operators -using ClimaOcean.ECCO2 -using ClimaOcean.OceanSimulations -using Oceananigans.Units -using ClimaOcean.JRA55: JRA55_prescribed_atmosphere -using ClimaOcean.OceanSeaIceModels: Radiation - -# Upload ECCO2 fields -T = ECCO2.ecco2_field(:temperature) -S = ECCO2.ecco2_field(:salinity) -u = ECCO2.ecco2_field(:u_velocity) -v = ECCO2.ecco2_field(:v_velocity) - -include("ecco2_immersed_grid.jl") -grid = ecco2_immersed_grid() - -atmosphere = JRA55_prescribed_atmosphere(1:2; grid = grid.underlying_grid) - -ocean = ocean_simulation(grid; momentum_advection = nothing, - tracer_advection = nothing) - -ocean_model = ocean.model - -# setting ecco variables in the model -set!(ocean_model, T = T, S = S, u = u, v = v) - -coupled_model = OceanSeaIceModel(ocean; atmosphere, radiation = Radiation()) - -function centered_surface_u_velocity(u) - 𝒰ᶜᶜᶜ = KernelFunctionOperation{Center, Center, Center}(ℑxᶜᵃᵃ, grid, u) - uᶜᶜᶜ = Field(𝒰ᶜᶜᶜ) - compute!(uᶜᶜᶜ) - - Nz = size(uᶜᶜᶜ.grid, 3) - - return Array(interior(uᶜᶜᶜ, :, :, Nz)) -end - -function centered_surface_v_velocity(v) - 𝒱ᶜᶜᶜ = KernelFunctionOperation{Center, Center, Center}(ℑyᵃᶜᵃ, grid, v) - vᶜᶜᶜ = Field(𝒱ᶜᶜᶜ) - compute!(vᶜᶜᶜ) - - Nz = size(vᶜᶜᶜ.grid, 3) - - return Array(interior(vᶜᶜᶜ, :, :, Nz)) -end - -# Write down variables to be read by MATLAB! -using MAT - -matfile = matopen("surface_fluxes.mat", "w") - -# Ocean variables -uo = centered_surface_u_velocity(ocean_model.velocities.u) -vo = centered_surface_v_velocity(ocean_model.velocities.v) -To = Array(interior(ocean_model.tracers.T, :, :, grid.Nz)) -So = Array(interior(ocean_model.tracers.S, :, :, grid.Nz)) -write(matfile, "uo", uo) -write(matfile, "vo", vo) -write(matfile, "To", To) -write(matfile, "So", So) - -# Atmospheric variables -ua = Array(interior(atmosphere.velocities.u[1], :, :, 1)) -va = Array(interior(atmosphere.velocities.v[1], :, :, 1)) -Ta = Array(interior(atmosphere.tracers.T[1], :, :, 1)) -qa = Array(interior(atmosphere.tracers.q[1], :, :, 1)) -ra = Array(interior(atmosphere.tracers.r[1], :, :, 1)) -pa = Array(interior(atmosphere.pressure[1], :, :, 1)) -Rs = Array(interior(atmosphere.downwelling_radiation.shortwave[1], :, :, 1)) -Rl = Array(interior(atmosphere.downwelling_radiation.longwave[1], :, :, 1)) -write(matfile, "ua", ua) -write(matfile, "va", va) -write(matfile, "Ta", Ta) -write(matfile, "qa", qa) -write(matfile, "ra", ra) -write(matfile, "pa", pa) -write(matfile, "Rs", Rs) -write(matfile, "Rl", Rl) - -import ClimaOcean.OceanSeaIceModels.CrossRealmFluxes: net_downwelling_radiation - -@inline net_downwelling_radiation(i::Int, j::Int, k::Int, grid, time, Qs::Field, Ql::Field, args...) = - net_downwelling_radiation(i, j, grid, time, Qs[i, j, k], Ql[i, j, k], args...) - -Rad = KernelFunctionOperation{Center, Center, Center}(net_downwelling_radiation, - grid, - Time(0), - atmosphere.downwelling_radiation.shortwave[1], - atmosphere.downwelling_radiation.longwave[1], - coupled_model.fluxes.radiation) - -Rin = compute!(Field(Rad)) -Rin = Array(interior(Rin, :, :, 1)) -Rou = 0.97 * 5.67e-8 .* (Array(interior(ocean.model.tracers.T, :, :, 50)) .+ 273.15) .^ 4 - -# Turbulent fluxes -Ql = Array(interior(coupled_model.fluxes.turbulent.fields.latent_heat, :, :, 1)) -Qs = Array(interior(coupled_model.fluxes.turbulent.fields.sensible_heat, :, :, 1)) -Mv = Array(interior(coupled_model.fluxes.turbulent.fields.water_vapor, :, :, 1)) -tx = Array(interior(coupled_model.fluxes.turbulent.fields.x_momentum, :, :, 1)) -ty = Array(interior(coupled_model.fluxes.turbulent.fields.y_momentum, :, :, 1)) -Tf = Array(interior(ocean.model.tracers.T.boundary_conditions.top.condition, :, :, 1)) -write(matfile, "Ql", Ql) -write(matfile, "Qs", Qs) -write(matfile, "Mv", Mv) -write(matfile, "tx", tx) -write(matfile, "ty", ty) -write(matfile, "Tf", Tf) -write(matfile, "Ri", Rin) -write(matfile, "Ro", Rou) - -close(matfile) diff --git a/prototype_omip_simulation/prototype_omip_simulation.jl b/prototype_omip_simulation/prototype_omip_simulation.jl index e38c4238..90833c65 100644 --- a/prototype_omip_simulation/prototype_omip_simulation.jl +++ b/prototype_omip_simulation/prototype_omip_simulation.jl @@ -5,18 +5,25 @@ using ClimaOcean using OrthogonalSphericalShellGrids using Oceananigans using Oceananigans: architecture -using ClimaOcean -using ClimaOcean.ECCO2 -using Oceananigans.TurbulenceClosures.CATKEVerticalDiffusivities: CATKEVerticalDiffusivity +using Oceananigans.Grids: on_architecture using Oceananigans.Coriolis: ActiveCellEnstrophyConserving using Oceananigans.Units +using ClimaOcean using ClimaOcean.OceanSimulations using ClimaOcean.OceanSeaIceModels -using ClimaOcean.OceanSeaIceModels.CrossRealmFluxes: Radiation +using ClimaOcean.OceanSeaIceModels.CrossRealmFluxes: Radiation, SimilarityTheoryTurbulentFluxes using ClimaOcean.VerticalGrids: exponential_z_faces using ClimaOcean.JRA55 +using ClimaOcean.ECCO using ClimaOcean.JRA55: JRA55NetCDFBackend, JRA55_prescribed_atmosphere +using ClimaOcean.ECCO: ECCO_restoring_forcing, ECCO4Monthly, ECCO2Daily, ECCOMetadata using ClimaOcean.Bathymetry +using ClimaOcean.OceanSeaIceModels.CrossRealmFluxes: LatitudeDependentAlbedo + +import ClimaOcean: stateindex + +using CFTime +using Dates include("tripolar_specific_methods.jl") @@ -27,19 +34,19 @@ include("tripolar_specific_methods.jl") bathymetry_file = nothing # "bathymetry_tmp.jld2" # 60 vertical levels -z_faces = exponential_z_faces(Nz=60, depth=6500) +z_faces = exponential_z_faces(Nz=60, depth=6000) Nx = 2160 -Ny = 1100 +Ny = 1080 Nz = length(z_faces) - 1 -arch = CPU() #Distributed(GPU(), partition = Partition(2)) +arch = GPU() #Distributed(GPU(), partition = Partition(2)) grid = TripolarGrid(arch; size = (Nx, Ny, Nz), halo = (7, 7, 7), z = z_faces, - north_poles_latitude = 60, + north_poles_latitude = 55, first_pole_longitude = 75) bottom_height = retrieve_bathymetry(grid, bathymetry_file; @@ -48,29 +55,64 @@ bottom_height = retrieve_bathymetry(grid, bathymetry_file; interpolation_passes = 20, connected_regions_allowed = 0) -grid = ImmersedBoundaryGrid(grid, GridFittedBottom(bottom_height)) #; active_cells_map = true) +grid = ImmersedBoundaryGrid(grid, GridFittedBottom(bottom_height); active_cells_map = true) ##### ##### The Ocean component ##### -const Lz = grid.Lz -const h = Nz / 4.5 +free_surface = SplitExplicitFreeSurface(grid; cfl = 0.75, fixed_Δt = 600) + +##### +##### Add restoring to ECCO fields for temperature and salinity in the artic and antarctic +##### + +# Build a mask that goes from 0 to 1 as a cubic function of φ between +# 70 degrees and 90 degrees and zero derivatives at 70 and 90. +x₁ = 70 +x₂ = 90 +y₁ = 0 +y₂ = 1 + +A⁺ = [ x₁^3 x₁^2 x₁ 1 + x₂^3 x₂^2 x₂ 1 + 3*x₁^2 2*x₁ 1 0 + 3*x₂^2 2*x₂ 1 0] + +b⁺ = [y₁, y₂, 0, 0] +c⁺ = A⁺ \ b⁺ + +# Coefficients for the cubic mask +const c₁⁺ = c⁺[1] +const c₂⁺ = c⁺[2] +const c₃⁺ = c⁺[3] +const c₄⁺ = c⁺[4] -@inline exponential_profile(z; Lz, h) = (exp(z / h) - exp( - Lz / h)) / (1 - exp( - Lz / h)) -@inline νz(x, y, z, t) = 1e-4 + (5e-3 - 1e-4) * exponential_profile(z; Lz, h) +const c₁⁻ = - c⁺[1] +const c₂⁻ = c⁺[2] +const c₃⁻ = - c⁺[3] +const c₄⁻ = c⁺[4] -free_surface = SplitExplicitFreeSurface(grid; cfl=0.7, fixed_Δt = 20minutes) -vertical_diffusivity = VerticalScalarDiffusivity(VerticallyImplicitTimeDiscretization(), κ = 5e-5, ν = νz) +@inline mask_f(λ, φ, z) = ifelse(φ >= 70, c₁⁺ * φ^3 + c₂⁺ * φ^2 + c₃⁺ * φ + c₄⁺, + ifelse(φ <= -70, c₁⁻ * φ^3 + c₂⁻ * φ^2 + c₃⁻ * φ + c₄⁻, zero(eltype(φ)))) -closure = (RiBasedVerticalDiffusivity(), vertical_diffusivity) +mask = CenterField(grid) +set!(mask, mask_f) -ocean = ocean_simulation(grid; free_surface, closure) +dates = DateTimeProlepticGregorian(1993, 1, 1) : Month(1) : DateTimeProlepticGregorian(2003, 12, 1) + +temperature = ECCOMetadata(:temperature, dates, ECCO4Monthly()) +salinity = ECCOMetadata(:salinity, dates, ECCO4Monthly()) + +FT = ECCO_restoring_forcing(temperature; mask, grid, architecture = arch, timescale = 30days) +FS = ECCO_restoring_forcing(salinity; mask, grid, architecture = arch, timescale = 30days) + +forcing = (; T = FT, S = FS) + +ocean = ocean_simulation(grid; free_surface, forcing) model = ocean.model -set!(model, - T = ECCO2Metadata(:temperature), - S = ECCO2Metadata(:salinity)) +initial_date = dates[1] ##### ##### The atmosphere @@ -113,20 +155,20 @@ fluxes = (u = model.velocities.u.boundary_conditions.top.condition, ocean.output_writers[:fluxes] = JLD2OutputWriter(model, fluxes, schedule = TimeInterval(0.5days), - overwrite_existing = true, + overwrite_existing = false, array_type = Array{Float32}, filename = "surface_fluxes") ocean.output_writers[:surface] = JLD2OutputWriter(model, merge(model.tracers, model.velocities), schedule = TimeInterval(0.5days), - overwrite_existing = true, + overwrite_existing = false, array_type = Array{Float32}, filename = "surface", indices = (:, :, grid.Nz)) ocean.output_writers[:snapshots] = JLD2OutputWriter(model, merge(model.tracers, model.velocities), schedule = TimeInterval(10days), - overwrite_existing = true, + overwrite_existing = false, array_type = Array{Float32}, filename = "snapshots") @@ -135,19 +177,31 @@ ocean.output_writers[:checkpoint] = Checkpointer(model, overwrite_existing = true, prefix = "checkpoint") -# Simulation warm up! +restart = nothing + +coupled_simulation = Simulation(coupled_model; Δt=1, stop_time) ocean.Δt = 10 -ocean.stop_iteration = 1 -wizard = TimeStepWizard(; cfl = 0.1, max_Δt = 90, max_change = 1.1) -ocean.callbacks[:wizard] = Callback(wizard, IterationInterval(1)) -stop_time = 30days +if isnothing(restart) -coupled_simulation = Simulation(coupled_model; Δt=1, stop_time) + # Set simulation from ECCO2 fields + set!(ocean.model, + T = ECCOMetadata(:temperature, initial_date, ECCO2Daily()), + S = ECCOMetadata(:salinity, initial_date, ECCO2Daily())) + + # Simulation warm up! + wizard = TimeStepWizard(; cfl = 0.1, max_Δt = 1.5minutes, max_change = 1.1) + ocean.callbacks[:wizard] = Callback(wizard, IterationInterval(10)) -run!(coupled_simulation) + stop_time = 25days + + run!(coupled_simulation) +else + # Set the ocean from the restart file + set!(ocean.model, restart) +end -wizard = TimeStepWizard(; cfl = 0.4, max_Δt = 540, max_change = 1.1) +wizard = TimeStepWizard(; cfl = 0.3, max_Δt = 600, max_change = 1.1) ocean.callbacks[:wizard] = Callback(wizard, IterationInterval(10)) # Let's reset the maximum number of iterations diff --git a/prototype_omip_simulation/tripolar_specific_methods.jl b/prototype_omip_simulation/tripolar_specific_methods.jl index 76a436a6..0e3e342d 100644 --- a/prototype_omip_simulation/tripolar_specific_methods.jl +++ b/prototype_omip_simulation/tripolar_specific_methods.jl @@ -2,6 +2,7 @@ using ClimaOcean import ClimaOcean.InitialConditions: interpolate! using Oceananigans +using Oceananigans.Operators using Oceananigans.BoundaryConditions using Oceananigans.Fields: OneField using Oceananigans.Grids: peripheral_node @@ -20,27 +21,55 @@ using Oceananigans.Grids: cpu_face_constructor_x, λnode, φnode using OrthogonalSphericalShellGrids: TRG +import ClimaOcean.OceanSeaIceModels.CrossRealmFluxes: extrinsic_vector, intrinsic_vector @inline hack_cosd(φ) = cos(π * φ / 180) @inline hack_sind(φ) = sin(π * φ / 180) -import ClimaOcean.OceanSeaIceModels.CrossRealmFluxes: convert_to_latlon_frame, convert_to_native_frame +# TODO: when https://github.com/CliMA/Oceananigans.jl/pull/3631 is merged, +# these functions will be transferred to OrthogonalSphericalShellGrids # Here we assume that the tripolar grid is locally orthogonal -@inline function convert_to_latlon_frame(i, j, grid::TRG, uₒ, vₒ) - φ₁ = φnode(i, j, 1, grid, Face(), Center(), Center()) - φ₂ = φnode(i+1, j, 1, grid, Face(), Center(), Center()) - - θ = φ₂ - φ₁ - - return uₒ * hack_cosd(θ) + vₒ * hack_sind(θ), uₒ * hack_sind(θ) + vₒ * hack_cosd(θ) +@inline function extrinsic_vector(i, j, k, grid::TRG, uₒ, vₒ) + + φᶜᶠᵃ₊ = φnode(i, j+1, 1, grid, Center(), Face(), Center()) + φᶜᶠᵃ₋ = φnode(i, j, 1, grid, Center(), Face(), Center()) + Δyᶜᶜᵃ = Δyᶜᶜᶜ(i, j, 1, grid) + + ũ = deg2rad(φᶜᶠᵃ₊ - φᶜᶠᵃ₋) / Δyᶜᶜᵃ + + φᶠᶜᵃ₊ = φnode(i+1, j, 1, grid, Face(), Center(), Center()) + φᶠᶜᵃ₋ = φnode(i, j, 1, grid, Face(), Center(), Center()) + Δxᶜᶜᵃ = Δxᶜᶜᶜ(i, j, 1, grid) + + ṽ = - deg2rad(φᶠᶜᵃ₊ - φᶠᶜᵃ₋) / Δxᶜᶜᵃ + + 𝒰 = sqrt(ũ^2 + ṽ^2) + + d₁ = ũ / 𝒰 + d₂ = ṽ / 𝒰 + + return uₒ * d₁ - vₒ * d₂, uₒ * d₂ + vₒ * d₁ end -@inline function convert_to_native_frame(i, j, grid::TRG, uₒ, vₒ) - φ₁ = φnode(i, j, 1, grid, Face(), Center(), Center()) - φ₂ = φnode(i, j+1, 1, grid, Face(), Center(), Center()) - - θ = φ₂ - φ₁ - - return uₒ * hack_cosd(θ) + vₒ * hack_sind(θ), uₒ * hack_sind(θ) + vₒ * hack_cosd(θ) +@inline function intrinsic_vector(i, j, k, grid::TRG, uₒ, vₒ) + + φᶜᶠᵃ₊ = φnode(i, j+1, 1, grid, Center(), Face(), Center()) + φᶜᶠᵃ₋ = φnode(i, j, 1, grid, Center(), Face(), Center()) + Δyᶜᶜᵃ = Δyᶜᶜᶜ(i, j, 1, grid) + + ũ = deg2rad(φᶜᶠᵃ₊ - φᶜᶠᵃ₋) / Δyᶜᶜᵃ + + φᶠᶜᵃ₊ = φnode(i+1, j, 1, grid, Face(), Center(), Center()) + φᶠᶜᵃ₋ = φnode(i, j, 1, grid, Face(), Center(), Center()) + Δxᶜᶜᵃ = Δxᶜᶜᶜ(i, j, 1, grid) + + ṽ = - deg2rad(φᶠᶜᵃ₊ - φᶠᶜᵃ₋) / Δxᶜᶜᵃ + + 𝒰 = sqrt(ũ^2 + ṽ^2) + + d₁ = ũ / 𝒰 + d₂ = ṽ / 𝒰 + + return uₒ * d₁ + vₒ * d₂, uₒ * d₂ - vₒ * d₁ end diff --git a/src/Bathymetry.jl b/src/Bathymetry.jl index f034a41c..f3f479ca 100644 --- a/src/Bathymetry.jl +++ b/src/Bathymetry.jl @@ -7,6 +7,7 @@ using ..DataWrangling: download_progress using Oceananigans using Oceananigans.Architectures: architecture +using Oceananigans.DistributedComputations: child_architecture using Oceananigans.Grids: halo_size, λnodes, φnodes using Oceananigans.Grids: x_domain, y_domain using Oceananigans.Grids: topology @@ -116,7 +117,7 @@ function regrid_bathymetry(target_grid; close(dataset) # Diagnose target grid information - arch = architecture(target_grid) + arch = child_architecture(architecture(target_grid)) φ₁, φ₂ = y_domain(target_grid) λ₁, λ₂ = x_domain(target_grid) diff --git a/src/ClimaOcean.jl b/src/ClimaOcean.jl index f4aa943b..671a2a53 100644 --- a/src/ClimaOcean.jl +++ b/src/ClimaOcean.jl @@ -13,15 +13,48 @@ export stretched_vertical_faces, exponential_z_faces, PowerLawStretching, LinearStretching, - jra55_field_time_series, + exponential_z_faces, + JRA55_field_time_series, + ecco_field, ECCOMetadata, ocean_simulation, - ecco2_field, ECCO2Metadata, initialize! using Oceananigans using Oceananigans.Operators: ℑxyᶠᶜᵃ, ℑxyᶜᶠᵃ using DataDeps +using Oceananigans.OutputReaders: GPUAdaptedFieldTimeSeries, FieldTimeSeries +using Oceananigans.Grids: node + +const SomeKindOfFieldTimeSeries = Union{FieldTimeSeries, + GPUAdaptedFieldTimeSeries} + +const SKOFTS = SomeKindOfFieldTimeSeries + +@inline stateindex(a::Number, i, j, k, args...) = a +@inline stateindex(a::AbstractArray, i, j, k, args...) = @inbounds a[i, j, k] +@inline stateindex(a::SKOFTS, i, j, k, grid, time, args...) = @inbounds a[i, j, k, time] + +@inline function stateindex(a::Function, i, j, k, grid, time, loc) + LX, LY, LZ = loc + λ, φ, z = node(i, j, k, grid, LX(), LY(), LZ()) + + return a(λ, φ, z, time) +end + +@inline function stateindex(a::Tuple, i, j, k, grid, time) + N = length(a) + ntuple(Val(N)) do n + stateindex(a[n], i, j, k, grid, time) + end +end + +@inline function stateindex(a::NamedTuple, i, j, k, grid, time) + vals = stateindex(values(a), i, j, k, grid, time) + names = keys(a) + return NamedTuple{names}(vals) +end + include("OceanSeaIceModels/OceanSeaIceModels.jl") include("VerticalGrids.jl") include("InitialConditions/InitialConditions.jl") @@ -33,13 +66,13 @@ include("OceanSimulations/OceanSimulations.jl") using .VerticalGrids using .Bathymetry using .DataWrangling: JRA55 -using .DataWrangling: ECCO2 +using .DataWrangling: ECCO using .InitialConditions using .OceanSeaIceModels: OceanSeaIceModel using .OceanSimulations -using .DataWrangling: JRA55, ECCO2 +using .DataWrangling: JRA55, ECCO using ClimaOcean.DataWrangling.JRA55: JRA55_prescribed_atmosphere, JRA55NetCDFBackend -using ClimaOcean.DataWrangling.ECCO2: ecco2_field +using ClimaOcean.DataWrangling.ECCO: ecco_field using .OceanSeaIceModels: OceanSeaIceModel, Radiation diff --git a/src/DataWrangling/DataWrangling.jl b/src/DataWrangling/DataWrangling.jl index 706c7dcf..af9ce21c 100644 --- a/src/DataWrangling/DataWrangling.jl +++ b/src/DataWrangling/DataWrangling.jl @@ -71,9 +71,9 @@ end include("inpaint_mask.jl") include("JRA55.jl") -include("ECCO2.jl") +include("ECCO.jl") using .JRA55 -using .ECCO2 +using .ECCO end # module diff --git a/src/DataWrangling/ECCO.jl b/src/DataWrangling/ECCO.jl new file mode 100644 index 00000000..7f09808f --- /dev/null +++ b/src/DataWrangling/ECCO.jl @@ -0,0 +1,314 @@ +module ECCO + +export ECCOMetadata, ecco_field, ecco_mask, adjusted_ecco_tracers, initialize! +export ECCO2Monthly, ECCO4Monthly, ECCO2Daily +export ECCO_restoring_forcing + +using ClimaOcean.DataWrangling: inpaint_mask! +using ClimaOcean.InitialConditions: three_dimensional_regrid!, interpolate! + +using Oceananigans +using Oceananigans.Architectures: architecture, child_architecture +using Oceananigans.BoundaryConditions +using Oceananigans.DistributedComputations: DistributedField, all_reduce, barrier! +using Oceananigans.Utils +using KernelAbstractions: @kernel, @index +using NCDatasets +using Downloads: download +using Dates + +using Adapt + +include("ecco_metadata.jl") + +# Vertical coordinate +const ECCO_z = [ + -6128.75, + -5683.75, + -5250.25, + -4839.75, + -4452.25, + -4087.75, + -3746.25, + -3427.75, + -3132.25, + -2859.75, + -2610.25, + -2383.74, + -2180.13, + -1999.09, + -1839.64, + -1699.66, + -1575.64, + -1463.12, + -1357.68, + -1255.87, + -1155.72, + -1056.53, + -958.45, + -862.10, + -768.43, + -678.57, + -593.72, + -515.09, + -443.70, + -380.30, + -325.30, + -278.70, + -240.09, + -208.72, + -183.57, + -163.43, + -147.11, + -133.45, + -121.51, + -110.59, + -100.20, + -90.06, + -80.01, + -70.0, + -60.0, + -50.0, + -40.0, + -30.0, + -20.0, + -10.0, + 0.0, +] + +empty_ecco_field(variable_name::Symbol; kw...) = empty_ecco_field(ECCOMetadata(variable_name); kw...) + +function empty_ecco_field(metadata::ECCOMetadata; + architecture = CPU(), + horizontal_halo = (3, 3)) + + Nx, Ny, Nz, _ = size(metadata) + + variable_name = metadata.name + location = field_location(metadata) + + location = ecco_location[variable_name] + + longitude = (0, 360) + latitude = (-90, 90) + TX, TY = (Periodic, Bounded) + + if variable_is_three_dimensional(metadata) + z = ECCO_z + # add vertical halo for 3D fields + halo = (horizontal_halo..., 3) + LZ = Center + TZ = Bounded + N = (Nx, Ny, Nz) + else + z = nothing + halo = horizontal_halo + LZ = Nothing + TZ = Flat + N = (Nx, Ny) + end + + # Flat in z if the variable is two-dimensional + grid = LatitudeLongitudeGrid(architecture; halo, size = N, topology = (TX, TY, TZ), + longitude, latitude, z) + + return Field{location...}(grid) +end + +""" + ecco_field(variable_name; + architecture = CPU(), + horizontal_halo = (1, 1), + user_data = nothing, + url = ecco_urls[variable_name], + filename = ecco_metadata_filenames[variable_name], + short_name = ecco_short_names[variable_name]) + +Retrieve the ecco field corresponding to `variable_name`. +The data is either: +(1) retrieved from `filename`, +(2) dowloaded from `url` if `filename` does not exists, +(3) filled from `user_data` if `user_data` is provided. +""" +function ecco_field(metadata::ECCOMetadata; + architecture = CPU(), + horizontal_halo = (3, 3), + filename = metadata_filename(metadata)) + + shortname = short_name(metadata) + + download_dataset!(metadata) + + ds = Dataset(filename) + if variable_is_three_dimensional(metadata) + data = ds[shortname][:, :, :, 1] + # The surface layer in three-dimensional ECCO fields is at `k = 1` + data = reverse(data, dims = 3) + else + data = ds[shortname][:, :, 1] + end + close(ds) + + field = empty_ecco_field(metadata; architecture, horizontal_halo) + + FT = eltype(field) + data[ismissing.(data)] .= 1e10 # Artificially large number! + data = if location(field)[2] == Face + new_data = zeros(FT, size(field)) + new_data[:, 1:end-1, :] .= data + new_data + else + convert.(FT, data) + end + + # ECCO4 data is on a -180, 180 longitude grid as opposed to ECCO2 data that + # is on a 0, 360 longitude grid. To make the data consistent, we shift ECCO4 + # data by 180 degrees in longitude + if metadata.version isa ECCO4Monthly + Nx = size(data, 1) + data = circshift(data, (Nx ÷ 2, 0, 0)) + end + + set!(field, data) + fill_halo_regions!(field) + + return field +end + +# Fallback +ecco_field(var_name::Symbol; kw...) = ecco_field(ECCOMetadata(var_name); kw...) + +@kernel function _set_ecco2_mask!(mask, Tᵢ, minimum_value, maximum_value) + i, j, k = @index(Global, NTuple) + @inbounds mask[i, j, k] = (Tᵢ[i, j, k] < minimum_value) | (Tᵢ[i, j, k] > maximum_value) +end + +@kernel function _set_ecco4_mask!(mask, Tᵢ, args...) + i, j, k = @index(Global, NTuple) + @inbounds mask[i, j, k] = (Tᵢ[i, j, k] == 0) +end + +@inline mask_kernel(version) = _set_ecco2_mask! +@inline mask_kernel(::ECCO4Monthly) = _set_ecco4_mask! + +""" + ecco_mask(architecture = CPU(); minimum_value = Float32(-1e5)) + +A boolean field where `true` represents a missing value in the ECCO dataset. +""" +function ecco_mask(metadata, architecture = CPU(); + minimum_value = Float32(-1e5), + maximum_value = Float32(1e5), + filename = metadata_filename(metadata)) + + field = ecco_field(metadata; architecture, filename) + mask = Field{location(field)...}(field.grid, Bool) + + # ECCO4 has zeros in place of the missing values, while + # ECCO2 expresses missing values with values < -1e5 + _set_mask! = mask_kernel(metadata.version) + + # Set the mask with zeros where field is defined + launch!(architecture, field.grid, :xyz, _set_mask!, mask, field, minimum_value, maximum_value) + + return mask +end + +# Default +ecco_mask() = ecco_mask(ECCOMetadata(:temperature)) + +""" + inpainted_ecco_field(variable_name; + architecture = CPU(), + filename = "./inpainted_ecco_fields.nc", + mask = ecco_mask(architecture)) + +Retrieve the ECCO field corresponding to `variable_name` inpainted to fill all the +missing values in the original dataset. + +Arguments: +========== + +- `variable_name`: the variable name corresponding to the Dataset. + +Keyword Arguments: +================== + +- `architecture`: either `CPU()` or `GPU()`. + +- `filename`: the path where to retrieve the data from. If the file does not exist, + the data will be downloaded from the ECCO dataset. + +- `mask`: the mask used to inpaint the field (see `inpaint_mask!`). + +- `maxiter`: the maximum number of iterations to inpaint the field (see `inpaint_mask!`). + +""" +function inpainted_ecco_field(metadata::ECCOMetadata; + architecture = CPU(), + filename = metadata_filename(metadata), + mask = ecco_mask(metadata, architecture), + maxiter = Inf, + kw...) + + f = ecco_field(metadata; architecture, filename, kw...) + + # Make sure all values are extended properly + @info "In-painting ecco $(metadata.name)" + inpaint_mask!(f, mask; maxiter) + + fill_halo_regions!(f) + + return f +end + +inpainted_ecco_field(variable_name::Symbol; kw...) = inpainted_ecco_field(ECCOMetadata(variable_name); kw...) + +function set!(field::DistributedField, ecco_metadata::ECCOMetadata; kw...) + # Fields initialized from ECCO + grid = field.grid + arch = architecture(grid) + child_arch = child_architecture(arch) + + f_ecco = if arch.local_rank == 0 # Make sure we read/write the file using only one core + mask = ecco_mask(ecco_metadata, child_arch) + + inpainted_ecco_field(ecco_metadata; mask, + architecture = child_arch, + kw...) + else + empty_ecco_field(ecco_metadata; architecture = child_arch) + end + + barrier!(arch) + + # Distribute ecco field to all workers + parent(f_ecco) .= all_reduce(+, parent(f_ecco), arch) + + f_grid = Field(field_location(ecco_metadata), grid) + interpolate!(f_grid, f_ecco) + set!(field, f_grid) + + return field +end + +function set!(field::Field, ecco_metadata::ECCOMetadata; kw...) + + # Fields initialized from ECCO + grid = field.grid + mask = ecco_mask(ecco_metadata, architecture(grid)) + + f = inpainted_ecco_field(ecco_metadata; mask, + architecture = architecture(grid), + kw...) + + f_grid = Field(field_location(ecco_metadata), grid) + interpolate!(f_grid, f) + set!(field, f_grid) + + return field +end + +include("ecco_restoring.jl") + +end # Module diff --git a/src/DataWrangling/ECCO2.jl b/src/DataWrangling/ECCO2.jl deleted file mode 100644 index c1b88abe..00000000 --- a/src/DataWrangling/ECCO2.jl +++ /dev/null @@ -1,387 +0,0 @@ -module ECCO2 - -export ECCO2Metadata, ecco2_field, ecco2_center_mask, adjusted_ecco_tracers, initialize! - -using ClimaOcean.DataWrangling: inpaint_mask! -using ClimaOcean.InitialConditions: three_dimensional_regrid!, interpolate! - -using Oceananigans -using Oceananigans.Architectures: architecture, child_architecture -using Oceananigans.BoundaryConditions -using Oceananigans.DistributedComputations: DistributedField, all_reduce, barrier! -using Oceananigans.Utils -using KernelAbstractions: @kernel, @index -using NCDatasets -using Downloads: download - -import Oceananigans.Fields: set! - -# Ecco field used to set model's initial conditions -struct ECCO2Metadata - name :: Symbol - year :: Int - month :: Int - day :: Int -end - -const ECCO2_Nx = 1440 -const ECCO2_Ny = 720 -const ECCO2_Nz = 50 - -# Vertical coordinate -const ECCO2_z = [ - -6128.75, - -5683.75, - -5250.25, - -4839.75, - -4452.25, - -4087.75, - -3746.25, - -3427.75, - -3132.25, - -2859.75, - -2610.25, - -2383.74, - -2180.13, - -1999.09, - -1839.64, - -1699.66, - -1575.64, - -1463.12, - -1357.68, - -1255.87, - -1155.72, - -1056.53, - -958.45, - -862.10, - -768.43, - -678.57, - -593.72, - -515.09, - -443.70, - -380.30, - -325.30, - -278.70, - -240.09, - -208.72, - -183.57, - -163.43, - -147.11, - -133.45, - -121.51, - -110.59, - -100.20, - -90.06, - -80.01, - -70.0, - -60.0, - -50.0, - -40.0, - -30.0, - -20.0, - -10.0, - 0.0, -] - -# We only have 1992 at the moment -ECCO2Metadata(name::Symbol) = ECCO2Metadata(name, 1992, 1, 2) - -filename(data::ECCO2Metadata) = "ecco2_" * string(data.name) * "_$(data.year)$(data.month)$(data.day).nc" - -ecco2_file_names = Dict( - :temperature => "THETA.1440x720x50.19920102.nc", - :salinity => "SALT.1440x720x50.19920102.nc", - :sea_ice_thickness => "SIheff.1440x720.19920102.nc", - :sea_ice_area_fraction => "SIarea.1440x720.19920102.nc", - :u_velocity => "UVEL.1440x720.19920102.nc", - :v_velocity => "VVEL.1440x720.19920102.nc", -) - -variable_is_three_dimensional = Dict( - :temperature => true, - :salinity => true, - :u_velocity => true, - :v_velocity => true, - :sea_ice_thickness => false, - :sea_ice_area_fraction => false, -) - -ecco2_short_names = Dict( - :temperature => "THETA", - :salinity => "SALT", - :u_velocity => "UVEL", - :v_velocity => "VVEL", - :sea_ice_thickness => "SIheff", - :sea_ice_area_fraction => "SIarea" -) - -ecco2_location = Dict( - :temperature => (Center, Center, Center), - :salinity => (Center, Center, Center), - :sea_ice_thickness => (Center, Center, Nothing), - :sea_ice_area_fraction => (Center, Center, Nothing), - :u_velocity => (Face, Center, Center), - :v_velocity => (Center, Face, Center), -) - -ecco2_urls = Dict( - :temperature => "https://www.dropbox.com/scl/fi/01h96yo2fhnnvt2zkmu0d/THETA.1440x720x50.19920102.nc?rlkey=ycso2v09gc6v2qb5j0lff0tjs", - :salinity => "https://www.dropbox.com/scl/fi/t068we10j5skphd461zg8/SALT.1440x720x50.19920102.nc?rlkey=r5each0ytdtzh5icedvzpe7bw", - :sea_ice_thickness => "https://www.dropbox.com/scl/fi/x0v9gjrfebwsef4tv1dvn/SIheff.1440x720.19920102.nc?rlkey=2uel3jtzbsplr28ejcnx3u6am", - :sea_ice_area_fraction => "https://www.dropbox.com/scl/fi/q14moq3201zicppu8ff8h/SIarea.1440x720.19920102.nc?rlkey=pt7pt80gr7r6mmjm9e0u4f5n1", - :u_velocity => "https://www.dropbox.com/scl/fi/myur9kpanc5mprrf5ge32/UVEL.1440x720x50.19920102.nc?rlkey=7a5dpvfgoc87yr6q5ktrqwndu", - :v_velocity => "https://www.dropbox.com/scl/fi/buic35gssyeyfqohenkeo/VVEL.1440x720x50.19920102.nc?rlkey=fau48w4t5ruop4s6gm8t7z0a0", -) - -surface_variable(variable_name) = variable_name == :sea_ice_thickness - -function construct_vertical_interfaces(ds, depth_name) - # Construct vertical coordinate - depth = ds[depth_name][:] - zc = -reverse(depth) - - # Interface depths from cell center depths - zf = (zc[1:end-1] .+ zc[2:end]) ./ 2 - push!(zf, 0) - - Δz = zc[2] - zc[1] - pushfirst!(zf, zf[1] - Δz) - - return zf -end - -function empty_ecco2_field(data::ECCO2Metadata; - architecture = CPU(), - horizontal_halo = (5, 5)) - - variable_name = data.name - - location = ecco2_location[variable_name] - - longitude = (0, 360) - latitude = (-90, 90) - TX, TY = (Periodic, Bounded) - - if variable_is_three_dimensional[variable_name] - z = ECCO2_z - # add vertical halo for 3D fields - halo = (horizontal_halo..., 3) - LZ = Center - TZ = Bounded - N = (ECCO2_Nx, ECCO2_Ny, ECCO2_Nz) - else - z = nothing - halo = horizontal_halo - LZ = Nothing - TZ = Flat - N = (ECCO2_Nx, ECCO2_Ny) - end - - # Flat in z if the variable is two-dimensional - grid = LatitudeLongitudeGrid(architecture; halo, size = N, topology = (TX, TY, TZ), - longitude, latitude, z) - - return Field{location...}(grid) -end - -""" - ecco2_field(variable_name; - architecture = CPU(), - horizontal_halo = (1, 1), - user_data = nothing, - url = ecco2_urls[variable_name], - filename = ecco2_file_names[variable_name], - short_name = ecco2_short_names[variable_name]) - -Retrieve the ecco2 field corresponding to `variable_name`. -The data is either: -(1) retrieved from `filename`, -(2) dowloaded from `url` if `filename` does not exists, -(3) filled from `user_data` if `user_data` is provided. -""" -function ecco2_field(variable_name; - architecture = CPU(), - horizontal_halo = (5, 5), - user_data = nothing, - year = 1992, - month = 1, - day = 2, - url = ecco2_urls[variable_name], - filename = ecco2_file_names[variable_name], - short_name = ecco2_short_names[variable_name]) - - ecco2_data = ECCO2Metadata(variable_name, year, month, day) - - isfile(filename) || download(url, filename) - - if user_data isa Nothing - ds = Dataset(filename) - if variable_is_three_dimensional[variable_name] - data = ds[short_name][:, :, :, 1] - # The surface layer in three-dimensional ECCO fields is at `k = 1` - data = reverse(data, dims = 3) - else - data = ds[short_name][:, :, 1] - end - else - data = user_data - end - - field = empty_ecco2_field(ecco2_data; architecture, horizontal_halo) - FT = eltype(field) - data = if location(field)[2] == Face - new_data = zeros(FT, size(field)) - new_data[:, 1:end-1, :] .= data - new_data - else - convert.(FT, data) - end - - set!(field, data) - fill_halo_regions!(field) - - return field -end - -@kernel function _set_ecco2_mask!(mask, Tᵢ, minimum_value) - i, j, k = @index(Global, NTuple) - @inbounds mask[i, j, k] = Tᵢ[i, j, k] < minimum_value -end - -""" - ecco2_center_mask(architecture = CPU(); minimum_value = Float32(-1e5)) - -A boolean field where `true` represents a missing value in the ECCO2 :temperature dataset. -""" -function ecco2_center_mask(architecture = CPU(); minimum_value = Float32(-1e5)) - Tᵢ = ecco2_field(:temperature; architecture) - mask = CenterField(Tᵢ.grid, Bool) - - # Set the mask with ones where T is missing - launch!(architecture, Tᵢ.grid, :xyz, _set_ecco2_mask!, mask, Tᵢ, minimum_value) - - return mask -end - -""" - inpainted_ecco2_field(variable_name; - architecture = CPU(), - filename = "./inpainted_ecco2_fields.nc", - mask = ecco2_center_mask(architecture)) - -Retrieve the ECCO2 field corresponding to `variable_name` inpainted to fill all the -missing values in the original dataset. - -Arguments: -========== - -- `variable_name`: the variable name corresponding to the Dataset. - -Keyword Arguments: -================== - -- `architecture`: either `CPU()` or `GPU()`. - -- `filename`: the path where to retrieve the data from. If the file does not exist, - the data will be retrived from the ECCO2 dataset, inpainted, and - saved to `filename`. - -- `mask`: the mask used to inpaint the field (see `inpaint_mask!`). -""" -function inpainted_ecco2_field(variable_name; - architecture = CPU(), - filename = "./inpainted_ecco2_$(variable_name).nc", - mask = ecco2_center_mask(architecture), - kw...) - - if !isfile(filename) - f = ecco2_field(variable_name; architecture) - - # Make sure all values are extended properly - @info "In-painting ecco $variable_name and saving it in $filename" - inpaint_mask!(f, mask; kw...) - - fill_halo_regions!(f) - - ds = Dataset(filename, "c") - defVar(ds, string(variable_name), Array(interior(f)), ("lat", "lon", "z")) - - close(ds) - else - ds = Dataset(filename, "a") - - if haskey(ds, string(variable_name)) - data = ds[variable_name][:, :, :] - f = ecco2_field(variable_name; architecture, user_data = data) - fill_halo_regions!(f) - else - f = ecco2_field(variable_name; architecture) - # Make sure all values are inpainted properly - @info "In-painting ecco $variable_name and saving it in $filename" - inpaint_mask!(f, mask; kw...) - fill_halo_regions!(f) - - defVar(ds, string(variable_name), Array(interior(f)), ("lat", "lon", "z")) - end - - close(ds) - end - - return f -end - -function set!(field::DistributedField, ecco2_metadata::ECCO2Metadata; - filename="./inpainted_ecco2_$(ecco2_metadata.name).nc", kw...) - - # Fields initialized from ECCO2 - grid = field.grid - arch = architecture(grid) - child_arch = child_architecture(arch) - name = ecco2_metadata.name - - f_ecco = if arch.local_rank == 0 # Make sure we read/write the file using only one core - mask = ecco2_center_mask(child_arch) - - inpainted_ecco2_field(name; filename, mask, - architecture = child_arch, - kw...) - else - empty_ecco2_field(ecco2_metadata; architecture = child_arch) - end - - barrier!(arch) - - # Distribute ecco field to all workers - parent(f_ecco) .= all_reduce(+, parent(f_ecco), arch) - - f_grid = Field(ecco2_location[name], grid) - interpolate!(f_grid, f_ecco) - set!(field, f_grid) - - return field -end - -function set!(field::Field, ecco2_metadata::ECCO2Metadata; - filename="./inpainted_ecco2_$(ecco2_metadata.name).nc", kw...) - - # Fields initialized from ECCO2 - grid = field.grid - name = ecco2_metadata.name - - mask = ecco2_center_mask(architecture(grid)) - - f = inpainted_ecco2_field(name; filename, mask, - architecture = architecture(grid), - kw...) - - f_grid = Field(ecco2_location[name], grid) - interpolate!(f_grid, f) - set!(field, f_grid) - - return field -end - -end # module - - - diff --git a/src/DataWrangling/JRA55.jl b/src/DataWrangling/JRA55.jl index 04dc61ba..a7427ec7 100644 --- a/src/DataWrangling/JRA55.jl +++ b/src/DataWrangling/JRA55.jl @@ -193,16 +193,14 @@ function compute_bounding_indices(longitude, latitude, grid, LX, LY, λc, φc) end # Convert dates to range until Oceananigans supports dates natively -function jra55_times(native_times, start_time=DateTimeNoLeap(1900, 01, 01)) - Nt = length(native_times) - Δt = native_times[2] - native_times[1] # assume all times are equispaced - Δt = Second(Δt).value +function jra55_times(native_times, start_time=native_times[1]) - start_time = native_times[1] - start_time - start_time = Second(start_time).value - - stop_time = start_time + Δt * (Nt - 1) - times = start_time:Δt:stop_time + times = [] + for native_time in native_times + time = native_time - start_time + time = Second(time).value + push!(times, time) + end return times end diff --git a/src/DataWrangling/ecco_metadata.jl b/src/DataWrangling/ecco_metadata.jl new file mode 100644 index 00000000..3855bcac --- /dev/null +++ b/src/DataWrangling/ecco_metadata.jl @@ -0,0 +1,174 @@ +using CFTime +using Dates +import Dates: year, month, day +import Oceananigans.Fields: set! +import Base + +struct ECCO2Monthly end +struct ECCO2Daily end +struct ECCO4Monthly end + +# Metadata holding the ECCO dataset information: +# - `name`: The name of the dataset. +# - `dates`: The dates of the dataset, in a `AbstractCFDateTime` format. +# - `version`: The version of the dataset, could be ECCO2Monthly, ECCO2Daily, or ECCO4Monthly. +struct ECCOMetadata{D, V} + name :: Symbol + dates :: D + version :: V +end + +Base.show(io::IO, metadata::ECCOMetadata) = + print(io, "ECCOMetadata:", '\n', + "├── field: $(metadata.name)", '\n', + "├── dates: $(metadata.dates)", '\n', + "└── data version: $(metadata.version)") + +# The default is the ECCO2Daily dataset at 1993-01-01. +function ECCOMetadata(name::Symbol; + date = DateTimeProlepticGregorian(1993, 1, 1), + version = ECCO2Daily()) + + return ECCOMetadata(name, date, version) +end + +# Treat ECCOMetadata as an array to allow iteration over the dates. +Base.getindex(metadata::ECCOMetadata, i::Int) = @inbounds ECCOMetadata(metadata.name, metadata.dates[i], metadata.version) +Base.length(metadata::ECCOMetadata) = length(metadata.dates) +Base.eltype(metadata::ECCOMetadata) = Base.eltype(metadata.dates) +Base.first(metadata::ECCOMetadata) = @inbounds ECCOMetadata(metadata.name, metadata.dates[1], metadata.version) +Base.last(metadata::ECCOMetadata) = @inbounds ECCOMetadata(metadata.name, metadata.dates[end], metadata.version) +Base.iterate(metadata::ECCOMetadata, i=1) = (@inline; (i % UInt) - 1 < length(metadata) ? (@inbounds ECCOMetadata(metadata.name, metadata.dates[i], metadata.version), i + 1) : nothing) + +Base.axes(metadata::ECCOMetadata{<:AbstractCFDateTime}) = 1 +Base.first(metadata::ECCOMetadata{<:AbstractCFDateTime}) = metadata +Base.last(metadata::ECCOMetadata{<:AbstractCFDateTime}) = metadata +Base.iterate(metadata::ECCOMetadata{<:AbstractCFDateTime}) = (metadata, nothing) +Base.iterate(::ECCOMetadata{<:AbstractCFDateTime}, ::Any) = nothing + +Base.size(data::ECCOMetadata{<:Any, <:ECCO2Daily}) = (1440, 720, 50, length(data.dates)) +Base.size(data::ECCOMetadata{<:Any, <:ECCO2Monthly}) = (1440, 720, 50, length(data.dates)) +Base.size(data::ECCOMetadata{<:Any, <:ECCO4Monthly}) = (720, 360, 50, length(data.dates)) + +Base.size(::ECCOMetadata{<:AbstractCFDateTime, <:ECCO2Daily}) = (1440, 720, 50, 1) +Base.size(::ECCOMetadata{<:AbstractCFDateTime, <:ECCO2Monthly}) = (1440, 720, 50, 1) +Base.size(::ECCOMetadata{<:AbstractCFDateTime, <:ECCO4Monthly}) = (720, 360, 50, 1) + +# The whole range of dates in the different dataset versions +all_ecco_dates(::ECCO4Monthly) = DateTimeProlepticGregorian(1992, 1, 1) : Month(1) : DateTimeProlepticGregorian(2023, 12, 1) +all_ecco_dates(::ECCO2Monthly) = DateTimeProlepticGregorian(1992, 1, 1) : Month(1) : DateTimeProlepticGregorian(2023, 12, 1) +all_ecco_dates(::ECCO2Daily) = DateTimeProlepticGregorian(1992, 1, 4) : Day(1) : DateTimeProlepticGregorian(2023, 12, 31) + +# File name generation specific to each Dataset version +function metadata_filename(metadata::ECCOMetadata{<:AbstractCFDateTime, <:ECCO4Monthly}) + shortname = short_name(metadata) + yearstr = string(Dates.year(metadata.dates)) + monthstr = string(Dates.month(metadata.dates), pad=2) + return shortname * "_" * yearstr * "_" * monthstr * ".nc" +end + +function metadata_filename(metadata::ECCOMetadata{<:AbstractCFDateTime}) + shortname = short_name(metadata) + yearstr = string(Dates.year(metadata.dates)) + monthstr = string(Dates.month(metadata.dates), pad=2) + postfix = variable_is_three_dimensional(metadata) ? ".1440x720x50." : ".1440x720." + + if metadata.version isa ECCO2Monthly + return shortname * postfix * yearstr * monthstr * ".nc" + elseif metadata.version isa ECCO2Daily + daystr = string(Dates.day(metadata.dates), pad=2) + return shortname * postfix * yearstr * monthstr * daystr * ".nc" + end +end + +# Convenience functions +short_name(data::ECCOMetadata{<:Any, <:ECCO2Daily}) = ecco2_short_names[data.name] +short_name(data::ECCOMetadata{<:Any, <:ECCO2Monthly}) = ecco2_short_names[data.name] +short_name(data::ECCOMetadata{<:Any, <:ECCO4Monthly}) = ecco4_short_names[data.name] + +field_location(data::ECCOMetadata) = ecco_location[data.name] + +variable_is_three_dimensional(data::ECCOMetadata) = + data.name == :temperature || + data.name == :salinity || + data.name == :u_velocity || + data.name == :v_velocity + +ecco4_short_names = Dict( + :temperature => "THETA", + :salinity => "SALT", + :u_velocity => "EVEL", + :v_velocity => "NVEL", + :sea_ice_thickness => "SIheff", + :sea_ice_area_fraction => "SIarea" +) + +ecco2_short_names = Dict( + :temperature => "THETA", + :salinity => "SALT", + :u_velocity => "UVEL", + :v_velocity => "VVEL", + :sea_ice_thickness => "SIheff", + :sea_ice_area_fraction => "SIarea" +) + +ecco_location = Dict( + :temperature => (Center, Center, Center), + :salinity => (Center, Center, Center), + :sea_ice_thickness => (Center, Center, Nothing), + :sea_ice_area_fraction => (Center, Center, Nothing), + :u_velocity => (Face, Center, Center), + :v_velocity => (Center, Face, Center), +) + +# URLs for the ECCO datasets specific to each version +urls(::ECCOMetadata{<:Any, <:ECCO2Monthly}) = "https://ecco.jpl.nasa.gov/drive/files/ECCO2/cube92_latlon_quart_90S90N/monthly/" +urls(::ECCOMetadata{<:Any, <:ECCO2Daily}) = "https://ecco.jpl.nasa.gov/drive/files/ECCO2/cube92_latlon_quart_90S90N/daily/" +urls(::ECCOMetadata{<:Any, <:ECCO4Monthly}) = "https://ecco.jpl.nasa.gov/drive/files/Version4/Release4/interp_monthly/" + +""" + download_dataset!(metadata::ECCOMetadata) + +Download the dataset specified by the given metadata. If the metadata contains a single date, +the dataset is downloaded directly. If the metadata contains multiple dates, the dataset is +downloaded for each date individually. +The data download requires a username and password to be provided in the ECCO_USERNAME and ECCO_PASSWORD +environment variables. This can be done by exporting the environment variables in the shell before running the script, +or by launching julia with + +ECCO_USERNAME=myuser ECCO_PASSWORD=mypasswrd julia + +# Arguments +- `metadata::ECCOMetadata`: The metadata specifying the dataset to be downloaded. +""" +function download_dataset!(metadata::ECCOMetadata; + url = urls(metadata)) + + username = get(ENV, "ECCO_USERNAME", nothing) + password = get(ENV, "ECCO_PASSWORD", nothing) + + for data in metadata + filename = metadata_filename(data) + shortname = short_name(data) + + if !isfile(filename) + + isnothing(username) && throw(ArgumentError("Could not find the username for $(url). Please provide a username in the ECCO_USERNAME environment variable.")) + isnothing(password) && throw(ArgumentError("Could not find the username for $(url). Please provide a password in the ECCO_PASSWORD environment variable.")) + + # Version specific download file url + if data.version isa ECCO2Monthly || data.version isa ECCO2Daily + fileurl = joinpath(url, shortname, filename) + elseif data.version isa ECCO4Monthly + year = string(Dates.year(data.dates)) + fileurl = joinpath(url, shortname, year, filename) + end + + cmd = `wget --http-user=$(username) --http-passwd=$(password) $(fileurl)` + + run(cmd) + end + end + + return nothing +end \ No newline at end of file diff --git a/src/DataWrangling/ecco_restoring.jl b/src/DataWrangling/ecco_restoring.jl new file mode 100644 index 00000000..3f063fef --- /dev/null +++ b/src/DataWrangling/ecco_restoring.jl @@ -0,0 +1,284 @@ +using Oceananigans.Units +using Oceananigans.Grids: node, on_architecture +using Oceananigans.Fields: interpolate!, interpolate, location, instantiated_location +using Oceananigans.OutputReaders: Cyclical, TotallyInMemory, AbstractInMemoryBackend, FlavorOfFTS, time_indices +using Oceananigans.Utils: Time + +using CUDA: @allowscalar +using Base + +using NCDatasets +using JLD2 +using Dates + +using ClimaOcean: stateindex + +import Oceananigans.Fields: set! +import Oceananigans.OutputReaders: new_backend, update_field_time_series! + +@inline instantiate(T::DataType) = T() +@inline instantiate(T) = T + +struct ECCONetCDFBackend{N} <: AbstractInMemoryBackend{Int} + start :: Int + length :: Int + + ECCONetCDFBackend{N}(start::Int, length::Int) where N = new{N}(start, length) +end + +""" + ECCONetCDFBackend(length) + +Represents an ECCO FieldTimeSeries backed by ECCO native .nc files. +Each time instance is stored in an individual file. +""" +ECCONetCDFBackend(length; on_native_grid = false) = ECCONetCDFBackend{on_native_grid}(1, length) + +Base.length(backend::ECCONetCDFBackend) = backend.length +Base.summary(backend::ECCONetCDFBackend) = string("ECCONetCDFBackend(", backend.start, ", ", backend.length, ")") + +const ECCONetCDFFTS{N} = FlavorOfFTS{<:Any, <:Any, <:Any, <:Any, <:ECCONetCDFBackend{N}} where N + +new_backend(::ECCONetCDFBackend{N}, start, length) where N = ECCONetCDFBackend{N}(start, length) +on_native_grid(::ECCONetCDFBackend{N}) where N = N + +function set!(fts::ECCONetCDFFTS, path::ECCOMetadata=fts.path, name::String=fts.name) + + backend = fts.backend + start = backend.start + + for t in start:start+length(backend)-1 + + # find the file associated with the time index + metadata = @inbounds path[t] + + arch = architecture(fts) + f = inpainted_ecco_field(metadata; architecture = arch) + if on_native_grid(backend) + set!(fts[t], f) + else + interpolate!(fts[t], f) + end + end + + fill_halo_regions!(fts) + + return nothing +end + +""" + ecco_times(metadata; start_time = metadata.dates[1]) + +Extracts the time values from the given metadata and calculates the time difference +from the start time. + +# Arguments +- `metadata`: The metadata containing the date information. +- `start_time`: The start time for calculating the time difference. Defaults to the first date in the metadata. + +# Returns +An array of time differences in seconds. +""" +function ecco_times(metadata; start_time = first(metadata).dates) + times = zeros(length(metadata)) + for (t, data) in enumerate(metadata) + date = data.dates + time = date - start_time + time = Second(time).value + times[t] = time + end + + return times +end + +""" + ECCO_field_time_series(metadata::ECCOMetadata; + architecture = CPU(), + time_indices_in_memory = 2, + time_indexing = Cyclical(), + grid = nothing) + +Create a field time series object for ECCO data. + +# Arguments: +- metadata: An ECCOMetadata object containing information about the ECCO dataset. + +# Keyword Arguments: +- architecture: The architecture to use for computations (default: CPU()). +- time_indices_in_memory: The number of time indices to keep in memory (default: 2). +- time_indexing: The time indexing scheme to use (default: Cyclical()). +- grid: if not a `nothing`, the ECCO data is directly interpolated on the `grid`, +""" +function ECCO_field_time_series(metadata::ECCOMetadata; + architecture = CPU(), + time_indices_in_memory = 2, + time_indexing = Cyclical(), + grid = nothing) + + # ECCO data is too chunky to allow other backends + backend = ECCONetCDFBackend(time_indices_in_memory; + on_native_grid = isnothing(grid)) + + # Making sure all the required individual files are downloaded + download_dataset!(metadata) + + location = field_location(metadata) + ftmp = empty_ecco_field(first(metadata); architecture) + shortname = short_name(metadata) + + ECCO_native_grid = ftmp.grid + boundary_conditions = FieldBoundaryConditions(ECCO_native_grid, location) + times = ecco_times(metadata) + + fts_grid = isnothing(grid) ? ECCO_native_grid : grid + + fts = FieldTimeSeries{location...}(fts_grid, times; + backend, + time_indexing, + boundary_conditions, + path = metadata, + name = shortname) + + # Let's set the data + set!(fts) + + return fts +end + +ECCO_field_time_series(variable_name::Symbol, version=ECCO4Monthly(); kw...) = + ECCO_field_time_series(ECCOMetadata(variable_name, all_ecco_dates(version), version); kw...) + +# Variable names for restoreable data +struct Temperature end +struct Salinity end +struct UVelocity end +struct VVelocity end + +oceananigans_fieldname = Dict( + :temperature => Temperature(), + :salinity => Salinity(), + :u_velocity => UVelocity(), + :v_velocity => VVelocity()) + +@inline Base.getindex(fields, i, j, k, ::Temperature) = @inbounds fields.T[i, j, k] +@inline Base.getindex(fields, i, j, k, ::Salinity) = @inbounds fields.S[i, j, k] +@inline Base.getindex(fields, i, j, k, ::UVelocity) = @inbounds fields.u[i, j, k] +@inline Base.getindex(fields, i, j, k, ::VVelocity) = @inbounds fields.v[i, j, k] + +""" + struct ECCORestoring{FTS, G, M, V, N} <: Function + +A struct representing ECCO restoring. + +# Fields +- `ecco_fts`: The ECCO FTS on the native ECCO grid. +- `ecco_grid`: The native ECCO grid to interpolate from. +- `mask`: A mask (could be a number, an array, a function or a field). +- `variable_name`: The variable name of the variable that needs restoring. +- `λ⁻¹`: The reciprocal of the restoring timescale. +""" +struct ECCORestoring{FTS, G, M, V, N} <: Function + ecco_fts :: FTS + ecco_grid :: G + mask :: M + variable_name :: V + λ⁻¹ :: N +end + +Adapt.adapt_structure(to, p::ECCORestoring) = + ECCORestoring(Adapt.adapt(to, p.ecco_fts), + Adapt.adapt(to, p.ecco_grid), + Adapt.adapt(to, p.mask), + Adapt.adapt(to, p.variable_name), + Adapt.adapt(to, p.λ⁻¹)) + +@inline function (p::ECCORestoring)(i, j, k, grid, clock, fields) + + # Figure out all the inputs: time, location, and node + time = Time(clock.time) + loc = location(p.ecco_fts) + + # Retrieve the variable to force + @inbounds var = fields[i, j, k, p.variable_name] + + ecco_backend = p.ecco_fts.backend + native_grid = on_native_grid(ecco_backend) + + ecco_var = get_ecco_variable(Val(native_grid), p.ecco_fts, i, j, k, p.ecco_grid, grid, time) + + # Extracting the mask value at the current node + mask = stateindex(p.mask, i, j, k, grid, clock.time, loc) + + return p.λ⁻¹ * mask * (ecco_var - var) +end + +# Differentiating between restoring done with an ECCO FTS +# that lives on the native ecco grid, that requires interpolation in space +# _inside_ the restoring function and restoring based on an ECCO +# FTS defined on the model grid that requires only time interpolation +@inline function get_ecco_variable(::Val{true}, ecco_fts, i, j, k, ecco_grid, grid, time) + # Extracting the ECCO field time series data and parameters + ecco_times = ecco_fts.times + ecco_data = ecco_fts.data + ecco_time_indexing = ecco_fts.time_indexing + ecco_backend = ecco_fts.backend + ecco_location = instantiated_location(ecco_fts) + + X = node(i, j, k, grid, ecco_location...) + + # Interpolating the ECCO field time series data onto the current node and time + return interpolate(X, time, ecco_data, ecco_location, ecco_grid, ecco_times, ecco_backend, ecco_time_indexing) +end + +@inline get_ecco_variable(::Val{false}, ecco_fts, i, j, k, ecco_grid, grid, time) = @inbounds ecco_fts[i, j, k, time] + +""" + ECCO_restoring_forcing(metadata::ECCOMetadata; + architecture = CPU(), + backend = ECCONetCDFBackend(2), + time_indexing = Cyclical(), + mask = 1, + timescale = 5days) + +Create a restoring forcing term that restores to values stored in an ECCO field time series. + +# Arguments: +============= +- `metadata`: The metadata for the ECCO field time series. + +# Keyword Arguments: +==================== +- `architecture`: The architecture. Typically `CPU` or `GPU` +- `time_indices_in_memory`: The number of time indices to keep in memory. trade-off between performance + and memory footprint. +- `time_indexing`: The time indexing scheme for the field time series, see [`FieldTimeSeries`](@ref) +- `mask`: The mask value. Can be a function of `(x, y, z, time)`, an array or a number +- `timescale`: The restoring timescale. +""" +function ECCO_restoring_forcing(variable_name::Symbol, version=ECCO4Monthly(); kw...) + metadata = ECCOMetadata(variable_name, all_ecco_dates(version), version) + return ECCO_restoring_forcing(metadata; kw...) +end + +function ECCO_restoring_forcing(metadata::ECCOMetadata; + architecture = CPU(), + time_indices_in_memory = 2, # Not more than this if we want to use GPU! + time_indexing = Cyclical(), + mask = 1, + timescale = 20days, + grid = nothing) + + ecco_fts = ECCO_field_time_series(metadata; grid, architecture, time_indices_in_memory, time_indexing) + ecco_grid = ecco_fts.grid + + # Grab the correct Oceananigans field to restore + variable_name = metadata.name + field_name = oceananigans_fieldname[variable_name] + + ecco_restoring = ECCORestoring(ecco_fts, ecco_grid, mask, field_name, 1 / timescale) + + # Defining the forcing that depends on the restoring field. + restoring_forcing = Forcing(ecco_restoring; discrete_form = true) + + return restoring_forcing +end \ No newline at end of file diff --git a/src/DataWrangling/inpaint_mask.jl b/src/DataWrangling/inpaint_mask.jl index 449d8f82..a7427934 100644 --- a/src/DataWrangling/inpaint_mask.jl +++ b/src/DataWrangling/inpaint_mask.jl @@ -45,9 +45,14 @@ end propagate_horizontally!(field, ::Nothing, tmp_field=deepcopy(field); kw...) = field -function propagating(field, mask, iter, max_iter) +function propagating(field, mask, iter, maxiter) mask_sum = sum(field; condition=interior(mask)) - return isnan(mask_sum) && iter < max_iter + return isnan(mask_sum) && iter < maxiter +end + +@kernel function _fill_nans!(field) + i, j, k = @index(Global, NTuple) + @inbounds field[i, j, k] = ifelse(isnan(field[i, j, k]), 0, field[i, j, k]) end """ @@ -57,7 +62,7 @@ Horizontally propagate the values of `field` into the `mask`. In other words, cells where `mask[i, j, k] == false` are preserved, and cells where `mask[i, j, k] == true` are painted over. """ -function propagate_horizontally!(field, mask, tmp_field=deepcopy(field); max_iter = Inf) +function propagate_horizontally!(field, mask, tmp_field=deepcopy(field); maxiter = Inf) iter = 0 grid = field.grid arch = architecture(grid) @@ -68,13 +73,15 @@ function propagate_horizontally!(field, mask, tmp_field=deepcopy(field); max_ite # Need temporary field to avoid a race condition parent(tmp_field) .= parent(field) - while propagating(field, mask, iter, max_iter) + while propagating(field, mask, iter, maxiter) launch!(arch, grid, :xyz, _propagate_field!, field, tmp_field) launch!(arch, grid, :xyz, _substitute_values!, field, tmp_field) iter += 1 @debug "Propagate pass $iter with sum $(sum(parent(field)))" end + launch!(arch, grid, :xyz, _fill_nans!, field) + fill_halo_regions!(field) return field @@ -119,9 +126,9 @@ Arguments - `max_iter`: Maximum iterations for inpainting. Non-Inf values mean that NaN's can occur within the mask. """ -function inpaint_mask!(field, mask; max_iter = Inf) +function inpaint_mask!(field, mask; maxiter = 10) continue_downwards!(field, mask) - propagate_horizontally!(field, mask; max_iter) + propagate_horizontally!(field, mask; maxiter) return field end diff --git a/src/OceanSeaIceModels/CrossRealmFluxes/CrossRealmFluxes.jl b/src/OceanSeaIceModels/CrossRealmFluxes/CrossRealmFluxes.jl index 22a7764e..885898e5 100644 --- a/src/OceanSeaIceModels/CrossRealmFluxes/CrossRealmFluxes.jl +++ b/src/OceanSeaIceModels/CrossRealmFluxes/CrossRealmFluxes.jl @@ -7,33 +7,17 @@ export Radiation, OceanSeaIceSurfaceFluxes, SimilarityTheoryTurbulentFluxes -using ..OceanSeaIceModels: SKOFTS, default_gravitational_acceleration +using ..OceanSeaIceModels: default_gravitational_acceleration import ..OceanSeaIceModels: surface_velocities, surface_tracers +import ClimaOcean: stateindex + ##### ##### Utilities ##### -@inline stateindex(a::Number, i, j, k, grid, time) = a -@inline stateindex(a::SKOFTS, i, j, k, grid, time) = @inbounds a[i, j, k, time] -@inline stateindex(a::AbstractArray, i, j, k, grid, time) = @inbounds a[i, j, k] -@inline Δϕt²(i, j, k, grid, ϕ1, ϕ2, time) = (stateindex(ϕ1, i, j, k, grid, time) - stateindex(ϕ2, i, j, k, grid, time))^2 - -@inline function stateindex(a::Tuple, i, j, k, grid, time) - N = length(a) - ntuple(Val(N)) do n - stateindex(a[n], i, j, k, grid, time) - end -end - -@inline function stateindex(a::NamedTuple, i, j, k, grid, time) - vals = stateindex(values(a), i, j, k, grid, time) - names = keys(a) - return NamedTuple{names}(vals) -end - function surface_flux(f::Field) top_bc = f.boundary_conditions.top if top_bc isa BoundaryCondition{<:Oceananigans.BoundaryConditions.Flux} diff --git a/src/OceanSeaIceModels/CrossRealmFluxes/atmosphere_ocean_fluxes.jl b/src/OceanSeaIceModels/CrossRealmFluxes/atmosphere_ocean_fluxes.jl index 2c9c368f..682b5f90 100644 --- a/src/OceanSeaIceModels/CrossRealmFluxes/atmosphere_ocean_fluxes.jl +++ b/src/OceanSeaIceModels/CrossRealmFluxes/atmosphere_ocean_fluxes.jl @@ -109,8 +109,8 @@ function compute_atmosphere_ocean_fluxes!(coupled_model) end # Fallback -@inline convert_to_intrinsic_reference_frame(i, j, k, grid, uₒ, vₒ) = uₒ, vₒ -@inline convert_to_extrinsic_reference_frame(i, j, k, grid, uₒ, vₒ) = uₒ, vₒ +@inline extrinsic_vector(i, j, k, grid, uₒ, vₒ) = uₒ, vₒ +@inline intrinsic_vector(i, j, k, grid, uₒ, vₒ) = uₒ, vₒ # Fallback! limit_fluxes_over_sea_ice!(args...) = nothing @@ -149,7 +149,7 @@ limit_fluxes_over_sea_ice!(args...) = nothing # Convert the native grid velocities to a zonal - meridional # frame of reference (assuming the frame of reference is # latitude - longitude here, we might want to change it) - uₒ, vₒ = convert_to_intrinsic_reference_frame(i, j, kᴺ, grid, uₒ, vₒ) + uₒ, vₒ = extrinsic_vector(i, j, kᴺ, grid, uₒ, vₒ) @inbounds begin # Atmos state, which is _assumed_ to exist at location = (c, c, nothing) @@ -205,9 +205,7 @@ limit_fluxes_over_sea_ice!(args...) = nothing # Convert back from a zonal - meridional flux to the frame of # reference of the native ocean grid - ρτxⁱʲ, ρτyⁱʲ = convert_to_extrinsic_reference_frame(i, j, kᴺ, grid, - turbulent_fluxes.x_momentum, - turbulent_fluxes.y_momentum) + ρτxⁱʲ, ρτyⁱʲ = intrinsic_vector(i, j, kᴺ, grid, turbulent_fluxes.x_momentum, turbulent_fluxes.y_momentum) # Store fluxes Qv = similarity_theory.fields.latent_heat @@ -282,11 +280,11 @@ end # Convert from a mass flux to a volume flux (aka velocity) # by dividing by the density of freshwater. # Also switch the sign, for some reason we are given freshwater flux as positive down. - ρᶠ = freshwater_density - ΣF = - (Mp + Mr) / ρᶠ + ρf⁻¹ = 1 / freshwater_density + ΣF = - (Mp + Mr) * ρf⁻¹ # Add the contribution from the turbulent water vapor flux - Fv = Mv / ρᶠ + Fv = Mv * ρf⁻¹ ΣF += Fv # Compute fluxes for u, v, T, S from momentum, heat, and freshwater fluxes @@ -295,12 +293,12 @@ end Jᵀ = net_tracer_fluxes.T Jˢ = net_tracer_fluxes.S - ρₒ = ocean_reference_density - cₒ = ocean_heat_capacity + ρₒ⁻¹ = 1 / ocean_reference_density + cₒ = ocean_heat_capacity - atmos_ocean_τx = ρτx / ρₒ - atmos_ocean_τy = ρτy / ρₒ - atmos_ocean_Jᵀ = ΣQ / (ρₒ * cₒ) + atmos_ocean_τx = ρτx * ρₒ⁻¹ + atmos_ocean_τy = ρτy * ρₒ⁻¹ + atmos_ocean_Jᵀ = ΣQ * ρₒ⁻¹ / cₒ atmos_ocean_Jˢ = - Sₒ * ΣF # Mask fluxes over land for convenience diff --git a/src/OceanSeaIceModels/OceanSeaIceModels.jl b/src/OceanSeaIceModels/OceanSeaIceModels.jl index 4a9eaf7c..7764faa2 100644 --- a/src/OceanSeaIceModels/OceanSeaIceModels.jl +++ b/src/OceanSeaIceModels/OceanSeaIceModels.jl @@ -17,14 +17,11 @@ using Oceananigans.OutputReaders: FieldTimeSeries, GPUAdaptedFieldTimeSeries using ClimaSeaIce: melting_temperature +using ClimaOcean: stateindex + using KernelAbstractions: @kernel, @index using KernelAbstractions.Extras.LoopInfo: @unroll -const SomeKindOfFieldTimeSeries = Union{FieldTimeSeries, - GPUAdaptedFieldTimeSeries} - -const SKOFTS = SomeKindOfFieldTimeSeries - function surface_velocities end function surface_tracers end function downwelling_radiation end diff --git a/src/OceanSeaIceModels/minimum_temperature_sea_ice.jl b/src/OceanSeaIceModels/minimum_temperature_sea_ice.jl index 3a3c8894..5fcf0375 100644 --- a/src/OceanSeaIceModels/minimum_temperature_sea_ice.jl +++ b/src/OceanSeaIceModels/minimum_temperature_sea_ice.jl @@ -55,7 +55,7 @@ end cooling_sea_ice = sea_ice & (Jᵀ[i, j, 1] > 0) # Don't allow the ocean to cool below the minimum temperature! (make sure it heats up though!) - Jᵀ[i, j, 1] = ifelse(cooling_sea_ice, zero(grid), Jᵀ[i, j, 1]) + Jᵀ[i, j, 1] = ifelse(cooling_sea_ice, zero(grid), Jᵀ[i, j, 1]) # If we are in a "sea ice" region we remove all fluxes Jˢ[i, j, 1] = ifelse(sea_ice, zero(grid), Jˢ[i, j, 1]) diff --git a/src/OceanSeaIceModels/ocean_only_model.jl b/src/OceanSeaIceModels/ocean_only_model.jl index 1fa1b3ce..8ab28eb8 100644 --- a/src/OceanSeaIceModels/ocean_only_model.jl +++ b/src/OceanSeaIceModels/ocean_only_model.jl @@ -1,3 +1,5 @@ +using Oceananigans.OutputReaders: extract_field_time_series, update_field_time_series! + const OceanOnlyModel = OceanSeaIceModel{Nothing} const OceanSimplifiedSeaIceModel = OceanSeaIceModel{<:MinimumTemperatureSeaIce} @@ -28,6 +30,15 @@ end function update_state!(coupled_model::NoSeaIceModel, callbacks=[]; compute_tendencies=false) time = Time(coupled_model.clock.time) update_model_field_time_series!(coupled_model.atmosphere, time) + + ocean_model = coupled_model.ocean.model + + # Do we really have to do this? + if !isempty(ocean_model.forcing) + field_time_series = extract_field_time_series(ocean_model.forcing) + update_field_time_series!(field_time_series, time) + end + compute_atmosphere_ocean_fluxes!(coupled_model) return nothing end diff --git a/src/OceanSimulations/OceanSimulations.jl b/src/OceanSimulations/OceanSimulations.jl index 6e198191..031e3b4c 100644 --- a/src/OceanSimulations/OceanSimulations.jl +++ b/src/OceanSimulations/OceanSimulations.jl @@ -59,6 +59,7 @@ function ocean_simulation(grid; Δt = 5minutes, rotation_rate = Ω_Earth, gravitational_acceleration = g_Earth, bottom_drag_coefficient = 0.003, + forcing = NamedTuple(), coriolis = HydrostaticSphericalCoriolis(; rotation_rate), momentum_advection = default_momentum_advection(), tracer_advection = default_tracer_advection(), @@ -78,12 +79,10 @@ function ocean_simulation(grid; Δt = 5minutes, T = FieldBoundaryConditions(top = FluxBoundaryCondition(Jᵀ)), S = FieldBoundaryConditions(top = FluxBoundaryCondition(Jˢ))) - if !(grid isa ImmersedBoundaryGrid) - forcing = NamedTuple() - else + if grid isa ImmersedBoundaryGrid Fu = Forcing(u_immersed_bottom_drag, discrete_form=true, parameters=bottom_drag_coefficient) Fv = Forcing(v_immersed_bottom_drag, discrete_form=true, parameters=bottom_drag_coefficient) - forcing = (; u = Fu, v = Fv) + forcing = merge(forcing, (; u = Fu, v = Fv)) end # Use the TEOS10 equation of state diff --git a/test/runtests.jl b/test/runtests.jl index 999c9374..adbfd92b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,8 +9,8 @@ if test_group == :jra55 || test_group == :all include("test_jra55.jl") end -if test_group == :ecco2 || test_group == :all - include("test_ecco2.jl") +if test_group == :ecco || test_group == :all + include("test_ecco.jl") end # Tests that we can download JRA55 utilities diff --git a/test/runtests_setup.jl b/test/runtests_setup.jl index d1cca936..61ab08b8 100644 --- a/test/runtests_setup.jl +++ b/test/runtests_setup.jl @@ -3,6 +3,11 @@ using Oceananigans using CUDA using Test +using ClimaOcean.DataWrangling +using ClimaOcean.ECCO +using ClimaOcean.JRA55 +using ClimaOcean.JRA55: JRA55_field_time_series + using Oceananigans.Architectures: architecture using Oceananigans.OutputReaders: interpolate! diff --git a/test/test_ecco.jl b/test/test_ecco.jl new file mode 100644 index 00000000..ee59de09 --- /dev/null +++ b/test/test_ecco.jl @@ -0,0 +1,51 @@ +include("runtests_setup.jl") + +using ClimaOcean +using ClimaOcean.ECCO +using ClimaOcean.ECCO: ecco_field, metadata_filename +using Oceananigans.Grids: topology + +using CFTime +using Dates + +@testset "ECCO fields utilities" begin + for arch in test_architectures + A = typeof(arch) + @info "Testing ecco_field on $A..." + + start_date = DateTimeProlepticGregorian(1993, 1, 1) + end_date = DateTimeProlepticGregorian(1993, 4, 1) + dates = start_date : Month(1) : end_date + + temperature = ECCOMetadata(:temperature, dates, ECCO4Monthly()) + t_restoring = ECCO_restoring_forcing(temperature; timescale = 1000.0) + + ecco_fts = t_restoring.func.ecco_fts + + for metadata in temperature + temperature_filename = metadata_filename(metadata) + @test isfile(temperature_filename) + end + + @test ecco_fts isa FieldTimeSeries + @test ecco_fts.grid isa LatitudeLongitudeGrid + @test topology(ecco_fts.grid) == (Periodic, Bounded, Bounded) + + Nx, Ny, Nz = size(interior(ecco_fts)) + Nt = length(ecco_fts.times) + + @test Nx == size(temperature)[1] + @test Ny == size(temperature)[2] + @test Nz == size(temperature)[3] + @test Nt == size(temperature)[4] + end +end + +@testset "setting a field with ECCO" begin + for arch in test_architectures + grid = LatitudeLongitudeGrid(size = (10, 10, 10), latitude = (-60, -40), longitude = (10, 15), z = (-200, 0)) + field = CenterField(grid) + set!(field, ECCOMetadata(:temperature)) + set!(field, ECCOMetadata(:salinity)) + end +end diff --git a/test/test_ecco2.jl b/test/test_ecco2.jl deleted file mode 100644 index c940c2b3..00000000 --- a/test/test_ecco2.jl +++ /dev/null @@ -1,51 +0,0 @@ -include("runtests_setup.jl") - -using ClimaOcean -using ClimaOcean.ECCO2 -using Oceananigans.Grids: topology - -@testset "ECCO2 fields utilities" begin - for arch in test_architectures - A = typeof(arch) - @info "Testing ecco2_field on $A..." - - temperature_filename = ECCO2.ecco2_file_names[:temperature] - ecco2_temperature = ECCO2.ecco2_field(:temperature; architecture=arch) - - @test isfile(temperature_filename) - rm(temperature_filename) - - @test ecco2_temperature isa Field - @test ecco2_temperature.grid isa LatitudeLongitudeGrid - @test topology(ecco2_temperature.grid) == (Periodic, Bounded, Bounded) - - Nx, Ny, Nz = size(ecco2_temperature) - @test Nx == 1440 - @test Ny == 720 - @test Nz == 50 - - ice_thickness_filename = ECCO2.ecco2_file_names[:sea_ice_thickness] - ecco2_ice_thickness = ECCO2.ecco2_field(:sea_ice_thickness; architecture=arch) - - @test isfile(ice_thickness_filename) - rm(ice_thickness_filename) - - @test ecco2_ice_thickness isa Field - @test ecco2_ice_thickness.grid isa LatitudeLongitudeGrid - @test topology(ecco2_ice_thickness.grid) == (Periodic, Bounded, Flat) - - Nx, Ny, Nz = size(ecco2_ice_thickness) - @test Nx == 1440 - @test Ny == 720 - @test Nz == 1 - end -end - -@testset "setting a field with ECCO2" begin - for arch in test_architectures - grid = LatitudeLongitudeGrid(size = (10, 10, 10), latitude = (-60, -40), longitude = (175, 185), z = (-200, 0)) - field = CenterField(grid) - set!(field, ECCO2Metadata(:temperature)) - set!(field, ECCO2Metadata(:salinity)) - end -end diff --git a/test/test_jra55.jl b/test/test_jra55.jl index b765c90d..34e8d00f 100644 --- a/test/test_jra55.jl +++ b/test/test_jra55.jl @@ -5,13 +5,13 @@ using ClimaOcean.JRA55: download_jra55_cache @testset "JRA55 and data wrangling utilities" begin for arch in test_architectures A = typeof(arch) - @info "Testing JRA55_field_time_series on $A..." + @info "Testing reanalysis_field_time_series on $A..." test_name = :downwelling_shortwave_radiation time_indices = 1:3 # This should download a file called "RYF.rsds.1990_1991.nc" - jra55_fts = ClimaOcean.JRA55.JRA55_field_time_series(test_name; architecture=arch, time_indices) + jra55_fts = JRA55_field_time_series(test_name; architecture=arch, time_indices) test_filename = joinpath(download_jra55_cache, "RYF.rsds.1990_1991.nc") @@ -35,10 +35,10 @@ using ClimaOcean.JRA55: download_jra55_cache end @info "Testing loading preprocessed JRA55 data on $A..." - in_memory_jra55_fts = ClimaOcean.JRA55.JRA55_field_time_series(test_name; - time_indices, - architecture = arch, - backend = InMemory(2)) + in_memory_jra55_fts = JRA55_field_time_series(test_name; + time_indices, + architecture = arch, + backend = InMemory(2)) @test in_memory_jra55_fts isa FieldTimeSeries @@ -72,7 +72,7 @@ using ClimaOcean.JRA55: download_jra55_cache # Random regression test CUDA.@allowscalar begin - @test target_fts[1, 1, 1, 1] == 222.243136478611 + @test target_fts[1, 1, 1, 1] == 222.243136478611 # Only include this if we are filling halo regions within # interpolate_field_time_series