From 20d712d5a1902b5c877d368657d67a883dcb5ddd Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Tue, 24 Sep 2024 12:38:36 +0100 Subject: [PATCH 01/33] update column --- validation/PISCES/column.jl | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/validation/PISCES/column.jl b/validation/PISCES/column.jl index 0de8efa29..283b045aa 100644 --- a/validation/PISCES/column.jl +++ b/validation/PISCES/column.jl @@ -38,7 +38,7 @@ nothing #hide @inline temp(z, t) = 2.4 * (1 + cos(t * 2π / year + 50days)) * ifelse(z > MLD(t), 1, exp((z - MLD(t))/20)) + 8 -grid = RectilinearGrid(topology = (Flat, Flat, Bounded), size = (100, ), extent = (400, )) +grid = RectilinearGrid(GPU(), topology = (Flat, Flat, Bounded), size = (100, ), extent = (400, )) clock = Clock(; time = 0.0) @@ -58,8 +58,7 @@ biogeochemistry = PISCES(; grid, mixed_layer_depth = zₘₓₗ, mean_mixed_layer_vertical_diffusivity = ConstantField(1e-2), # this is by default computed now surface_photosynthetically_active_radiation = PAR⁰, - carbon_chemistry)#, - #sinking_speeds = (POC = 2/day, GOC = 50/day)) + carbon_chemistry) CO₂_flux = CarbonDioxideGasExchangeBoundaryCondition(; carbon_chemistry) O₂_flux = OxygenGasExchangeBoundaryCondition() @@ -112,22 +111,6 @@ end add_callback!(simulation, update_temperature!, IterationInterval(1)) -#NaN Checker function. Could be removed to improve speed, if confident of model stability -function non_zero_fields!(model) - @inbounds for (idx, tracer) in enumerate(model.tracers) - for i in 1:50 - if isnan(tracer[1,1,i]) - throw("$(keys(model.tracers)[idx]) has gone NaN") - else - tracer[1, 1, i] = max(0, tracer[1, 1, i]) - end - end - - end - return nothing -end - -simulation.callbacks[:non_zero_fields] = Callback(non_zero_fields!, callsite = UpdateStateCallsite()) filename = "column" simulation.output_writers[:tracers] = JLD2OutputWriter(model, model.tracers, filename = "$filename.jld2", @@ -168,8 +151,9 @@ carbon_export = zeros(length(times)) S = ConstantField(35) using Oceananigans.Biogeochemistry: biogeochemical_drift_velocity +using CUDA -for (n, t) in enumerate(times) +CUDA.@allowscalar for (n, t) in enumerate(times) clock.time = t k_export = floor(Int, grid.Nz + MLD(t)/minimum_zspacing(grid)) @@ -203,8 +187,8 @@ for (n, name) in enumerate(keys(model.tracers)) end end -ax = Axis(fig[7, 3]; title = "log₁₀((Calcite saturation)", axis_kwargs...) -hm = heatmap!(ax, times[start_day:end_day]./days, z, log10.(interior(internal_fields["calcite_saturation"], 1, 1, :, start_day:end_day)'), colorrange = (-173, -95)) +ax = Axis(fig[7, 3]; title = "log₁₀(Calcite saturation)", axis_kwargs...) +hm = heatmap!(ax, times[start_day:end_day]./days, z, log10.(interior(internal_fields["calcite_saturation"], 1, 1, :, start_day:end_day)')) Colorbar(fig[7, 4], hm) ax = Axis(fig[7, 5]; title = "log₁₀(PAR)", axis_kwargs...) @@ -221,6 +205,7 @@ lines!(axDIC, times[start_day:end_day] / days, carbon_export[start_day:end_day] Legend(fig[7, 8], axDIC, framevisible = false) fig +save("gpu_column.png", fig) # TODO: From c7505cbdeda73395f031b52266b4eb8b4cb34c71 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Tue, 24 Sep 2024 12:40:04 +0100 Subject: [PATCH 02/33] refactored add src/Models/AdvectedPopulations/PISCES/! who doesn't love a many thousand line commit --- .../AdvectedPopulations/PISCES/PISCES.jl | 225 ++++--------- .../AdvectedPopulations/PISCES/adapts.jl | 35 --- .../AdvectedPopulations/PISCES/calcite.jl | 79 ----- .../PISCES/carbonate_system.jl | 88 ------ .../AdvectedPopulations/PISCES/common.jl | 8 +- .../PISCES/compute_calcite_saturation.jl | 35 --- .../PISCES/coupling_utils.jl | 42 --- .../PISCES/dissolved_organic_matter.jl | 152 --------- .../dissolved_organic_carbon.jl | 101 ++++++ .../dissolved_organic_matter.jl | 14 + .../PISCES/generic_functions.jl | 6 + .../PISCES/group_methods.jl | 160 ---------- src/Models/AdvectedPopulations/PISCES/hack.jl | 18 -- .../PISCES/inorganic_carbon.jl | 53 ++++ src/Models/AdvectedPopulations/PISCES/iron.jl | 88 ------ .../AdvectedPopulations/PISCES/iron/iron.jl | 34 ++ .../PISCES/iron/simple_iron.jl | 59 ++++ .../PISCES/iron_in_particles.jl | 142 --------- .../PISCES/mean_mixed_layer_properties.jl | 117 ------- .../PISCES/nitrate_ammonia.jl | 109 ------- .../PISCES/nitrogen/nitrate_ammonia.jl | 86 +++++ .../PISCES/nitrogen/nitrogen.jl | 12 + .../AdvectedPopulations/PISCES/oxygen.jl | 68 ++-- .../PISCES/particulate_organic_carbon.jl | 104 ------ .../PISCES/particulate_organic_matter.jl | 49 --- .../particulate_organic_matter/calcite.jl | 52 +++ .../particulate_organic_matter/carbon.jl | 56 ++++ .../PISCES/particulate_organic_matter/iron.jl | 133 ++++++++ .../micro_meso_zoo_coupling.jl | 29 ++ .../nano_diatom_coupling.jl | 90 ++++++ .../particulate_organic_matter.jl | 18 ++ .../particulate_organic_matter/silicate.jl | 47 +++ .../two_size_class.jl | 95 ++++++ .../AdvectedPopulations/PISCES/phosphate.jl | 31 ++ .../AdvectedPopulations/PISCES/phosphates.jl | 39 --- .../PISCES/phytoplankton.jl | 296 ------------------ .../growth_rate.jl} | 45 ++- .../PISCES/phytoplankton/mixed_mondo.jl | 229 ++++++++++++++ .../phytoplankton/mixed_mondo_nano_diatoms.jl | 111 +++++++ .../PISCES/phytoplankton/nano_and_diatoms.jl | 32 ++ .../nutrient_limitation.jl | 32 +- .../PISCES/phytoplankton/phytoplankton.jl | 18 ++ .../PISCES/phytoplankton/waste.jl | 7 + .../PISCES/show_methods.jl | 100 ------ .../AdvectedPopulations/PISCES/silicate.jl | 24 ++ .../AdvectedPopulations/PISCES/silicon.jl | 25 -- .../PISCES/silicon_in_particles.jl | 52 --- .../PISCES/update_state.jl | 18 -- .../AdvectedPopulations/PISCES/zooplankton.jl | 293 ----------------- .../PISCES/zooplankton/defaults.jl | 38 +++ .../zooplankton/food_quality_dependant.jl | 201 ++++++++++++ .../PISCES/zooplankton/grazing_waste.jl | 70 +++++ .../PISCES/zooplankton/iron_grazing.jl | 51 +++ .../PISCES/zooplankton/micro_and_meso.jl | 130 ++++++++ .../PISCES/zooplankton/mortality_waste.jl | 37 +++ .../PISCES/zooplankton/zooplankton.jl | 17 + 56 files changed, 2017 insertions(+), 2283 deletions(-) delete mode 100644 src/Models/AdvectedPopulations/PISCES/adapts.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/calcite.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/carbonate_system.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/compute_calcite_saturation.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/coupling_utils.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_carbon.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_matter.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/generic_functions.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/group_methods.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/hack.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/iron.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/iron/iron.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/iron_in_particles.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/mean_mixed_layer_properties.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/nitrate_ammonia.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/nitrogen/nitrate_ammonia.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/nitrogen/nitrogen.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/particulate_organic_carbon.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/particulate_organic_matter.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/calcite.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/particulate_organic_matter.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/silicate.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/phosphate.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/phosphates.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/phytoplankton.jl rename src/Models/AdvectedPopulations/PISCES/{base_production.jl => phytoplankton/growth_rate.jl} (79%) create mode 100644 src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl rename src/Models/AdvectedPopulations/PISCES/{ => phytoplankton}/nutrient_limitation.jl (76%) create mode 100644 src/Models/AdvectedPopulations/PISCES/phytoplankton/phytoplankton.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/phytoplankton/waste.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/show_methods.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/silicate.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/silicon.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/silicon_in_particles.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/update_state.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/zooplankton.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/zooplankton/zooplankton.jl diff --git a/src/Models/AdvectedPopulations/PISCES/PISCES.jl b/src/Models/AdvectedPopulations/PISCES/PISCES.jl index 5b4a00efb..7dcfe7e06 100644 --- a/src/Models/AdvectedPopulations/PISCES/PISCES.jl +++ b/src/Models/AdvectedPopulations/PISCES/PISCES.jl @@ -50,12 +50,10 @@ import OceanBioME: maximum_sinking_velocity import Base: show, summary -struct PISCES{NP, DP, SZ, BZ, DM, PM, NI, FE, SI, OX, PO, CA, CE, FT, LA, DL, ML, EU, MS, VD, MP, CC, CS, SS} <: AbstractContinuousFormBiogeochemistry - nanophytoplankton :: NP - diatoms :: DP +struct PISCES{PP, ZP, DM, PM, NI, FE, SI, OX, PO, CA, CE, FT, LA, DL, ML, EU, MS, VD, MP, CC, CS, SS} <: AbstractBiogeochemistry + phytoplankton :: PP - microzooplankton :: SZ - mesozooplankton :: BZ + zooplankton :: ZP dissolved_organic_matter :: DM particulate_organic_matter :: PM @@ -94,13 +92,21 @@ struct PISCES{NP, DP, SZ, BZ, DM, PM, NI, FE, SI, OX, PO, CA, CE, FT, LA, DL, ML sinking_velocities :: SS end -const NANO_PHYTO = Union{Val{:P}, Val{:PChl}, Val{:PFe}} -const DIATOMS = Union{Val{:D}, Val{:DChl}, Val{:DFe}, Val{:DSi}} -const PARTICLES = Union{Val{:POC}, Val{:SFe}, Val{:GOC}, Val{:BFe}, Val{:PSi}} -const NITROGEN = Union{Val{:NO₃}, Val{:NH₄}} -const CARBON_SYSTEM = Union{Val{:DIC}, Val{:Alk}} +@inline required_biogeochemical_tracers(bgc::PISCES) = + (required_biogeochemical_tracers(bgc.zooplankton)..., + required_biogeochemical_tracers(bgc.phytoplankon)..., + required_biogeochemical_tracers(bgc.dissolved_organic_matter)..., + required_biogeochemical_tracers(bgc.particulate_organic_matter)..., + required_biogeochemical_tracers(bgc.nitrogen)..., + required_biogeochemical_tracers(bgc.phosphate)..., + required_biogeochemical_tracers(bgc.iron)..., + required_biogeochemical_tracers(bgc.silicate), + required_biogeochemical_tracers(bgc.carbon_system)... + required_biogeochemical_tracers(bgc.oxygen)..., + :T, :S) -include("group_methods.jl") +@inline required_biogeochemical_auxiliary_fields(::PISCES) = + (:zₘₓₗ, :zₑᵤ, :Si′, :Ω, :κ, :mixed_layer_PAR, :wPOC, :wGOC, :PAR, :PAR₁, :PAR₂, :PAR₃) @inline biogeochemical_auxiliary_fields(bgc::PISCES) = (zₘₓₗ = bgc.mixed_layer_depth, @@ -112,106 +118,52 @@ include("group_methods.jl") wPOC = bgc.sinking_velocities.POC, wGOC = bgc.sinking_velocities.GOC) -@inline required_biogeochemical_tracers(::PISCES) = - (:P, :D, :Z, :M, :PChl, :DChl, :PFe, :DFe, :DSi, - :DOC, :POC, :GOC, :SFe, :BFe, :PSi, # its really silly that this is called PSi when DSi also exists - :NO₃, :NH₄, :PO₄, :Fe, :Si, - :CaCO₃, :DIC, :Alk, :O₂, :T, :S) +biogeochemical_drift_velocity(bgc::PISCES, val_name) = + biogeochemical_drift_velocity(bgc.particulate_organic_matter, val_name) -@inline required_biogeochemical_auxiliary_fields(::PISCES) = - (:zₘₓₗ, :zₑᵤ, :Si′, :Ω, :κ, :mixed_layer_PAR, :wPOC, :wGOC, :PAR, :PAR₁, :PAR₂, :PAR₃) +include("zooplankton/zooplankton.jl") + +using .Zooplankton + +include("phytoplankton/phytoplankton.jl") + +using .Phytoplankton + +include("dissolved_organic_matter/dissolved_organic_matter.jl") + +using .DissolvedOrganicMatter + +include("particulate_organic_matter/particulate_organic_matter.jl") + +using .ParticulateOrganicMatter + +include("nitrogen/nitrogen.jl") + +using .Nitrogen + +include("iron/iron.jl") + +using .Iron + +include("silicate.jl") + +using .Silicates -const small_particle_components = Union{Val{:POC}, Val{:SFe}} -const large_particle_components = Union{Val{:GOC}, Val{:BFe}, Val{:PSi}, Val{:CaCO₃}} - -biogeochemical_drift_velocity(bgc::PISCES, ::small_particle_components) = (u = ZeroField(), v = ZeroField(), w = bgc.sinking_velocities.POC) -biogeochemical_drift_velocity(bgc::PISCES, ::large_particle_components) = (u = ZeroField(), v = ZeroField(), w = bgc.sinking_velocities.GOC) - -include("common.jl") -include("phytoplankton.jl") -include("zooplankton.jl") -include("dissolved_organic_matter.jl") -include("particulate_organic_matter.jl") -include("nitrate_ammonia.jl") -include("phosphates.jl") -include("iron.jl") -include("silicon.jl") -include("calcite.jl") -include("carbonate_system.jl") include("oxygen.jl") -include("mean_mixed_layer_properties.jl") -include("compute_calcite_saturation.jl") -include("update_state.jl") -include("coupling_utils.jl") -include("show_methods.jl") -include("adapts.jl") -include("hack.jl") + +using .OxygenModels + +include("phosphate.jl") + +using .Phosphates + +include("inorganic_carbon.jl") + +using .InorganicCarbons """ PISCES(; grid, - nanophytoplankton = - MixedMondoPhytoplankton( - growth_rate = GrowthRespirationLimitedProduction(dark_tollerance = 3days), - nutrient_limitation = - NitrogenIronPhosphateSilicateLimitation(minimum_ammonium_half_saturation = 0.013, - minimum_nitrate_half_saturation = 0.13, - minimum_phosphate_half_saturation = 0.8, - half_saturation_for_iron_uptake = 1.0, - silicate_limited = false), - blue_light_absorption = 2.1, - green_light_absorption = 0.42, - red_light_absorption = 0.4, - maximum_quadratic_mortality = 0.0, - maximum_chlorophyll_ratio = 0.033), - - diatoms = - MixedMondoPhytoplankton( - growth_rate = GrowthRespirationLimitedProduction(dark_tollerance = 4days), - nutrient_limitation = - NitrogenIronPhosphateSilicateLimitation(minimum_ammonium_half_saturation = 0.039, - minimum_nitrate_half_saturation = 0.39, - minimum_phosphate_half_saturation = 2.4, - half_saturation_for_iron_uptake = 3.0, - silicate_limited = true), - blue_light_absorption = 1.6, - green_light_absorption = 0.69, - red_light_absorption = 0.7, - maximum_quadratic_mortality = 0.03/day, - maximum_chlorophyll_ratio = 0.05), - - microzooplankton = Zooplankton(maximum_grazing_rate = 3/day, - preference_for_nanophytoplankton = 1.0, - preference_for_diatoms = 0.5, - preference_for_particulates = 0.1, - preference_for_zooplankton = 0.0, - quadratic_mortality = 0.004/day, - linear_mortality = 0.03/day, - minimum_growth_efficiency = 0.3, - maximum_flux_feeding_rate = 0.0, - undissolved_calcite_fraction = 0.5), - - mesozooplankton = Zooplankton(maximum_grazing_rate = 0.75/day, - preference_for_nanophytoplankton = 0.3, - preference_for_diatoms = 1.0, - preference_for_particulates = 0.3, - preference_for_zooplankton = 1.0, - quadratic_mortality = 0.03/day, - linear_mortality = 0.005/day, - minimum_growth_efficiency = 0.35, - maximum_flux_feeding_rate = 2e3 / 1e6 / day, - undissolved_calcite_fraction = 0.75), - - dissolved_organic_matter = DissolvedOrganicMatter(), - particulate_organic_matter = TwoCompartementParticulateOrganicMatter(), - nitrogen = NitrateAmmonia(), - iron = SimpleIron(), - silicate = Silicate(), - oxygen = Oxygen(), - phosphate = Phosphate(), - - calcite = Calcite(), - carbon_system = CarbonateSystem(), # from Aumount 2005 rather than 2015 since it doesn't work the other way around first_anoxia_thresehold = 6.0, @@ -313,64 +265,10 @@ the classes to a single `phytoplankton` if more classes are required (see was desired a way to specify arbitary tracers for arguments would be required. """ function PISCES(; grid, - nanophytoplankton = - MixedMondoPhytoplankton( - growth_rate = GrowthRespirationLimitedProduction(dark_tollerance = 3days), - nutrient_limitation = - NitrogenIronPhosphateSilicateLimitation(minimum_ammonium_half_saturation = 0.013, - minimum_nitrate_half_saturation = 0.13, - minimum_phosphate_half_saturation = 0.8, - half_saturation_for_iron_uptake = 1.0, - silicate_limited = false), - blue_light_absorption = 2.1, - green_light_absorption = 0.42, - red_light_absorption = 0.4, - maximum_quadratic_mortality = 0.0, - maximum_chlorophyll_ratio = 0.033), - - diatoms = - MixedMondoPhytoplankton( - growth_rate = GrowthRespirationLimitedProduction(dark_tollerance = 4days), - nutrient_limitation = - NitrogenIronPhosphateSilicateLimitation(minimum_ammonium_half_saturation = 0.039, - minimum_nitrate_half_saturation = 0.39, - minimum_phosphate_half_saturation = 2.4, - half_saturation_for_iron_uptake = 3.0, - silicate_limited = true), - blue_light_absorption = 1.6, - green_light_absorption = 0.69, - red_light_absorption = 0.7, - maximum_quadratic_mortality = 0.03/day, - maximum_chlorophyll_ratio = 0.05), - - microzooplankton = Zooplankton(maximum_grazing_rate = 3/day, - preference_for_nanophytoplankton = 1.0, - preference_for_diatoms = 0.5, - preference_for_particulates = 0.1, - preference_for_zooplankton = 0.0, - quadratic_mortality = 0.004/day, - linear_mortality = 0.03/day, - minimum_growth_efficiency = 0.3, - maximum_flux_feeding_rate = 0.0, - undissolved_calcite_fraction = 0.5, - iron_ratio = 0.01), - - mesozooplankton = Zooplankton(maximum_grazing_rate = 0.75/day, - preference_for_nanophytoplankton = 0.3, - preference_for_diatoms = 1.0, - preference_for_particulates = 0.3, - preference_for_zooplankton = 1.0, - quadratic_mortality = 0.03/day, - linear_mortality = 0.005/day, - minimum_growth_efficiency = 0.35, - # not documented but the below must implicitly contain a factor of second/day - # to be consistent in the NEMO namelist to go from this * mol / L * m/s to mol / L / day - maximum_flux_feeding_rate = 2e3 / 1e6 / day, # (day * meter/s * mol/L)^-1 to (meter * μ mol/L)^-1 - undissolved_calcite_fraction = 0.75, - iron_ratio = 0.015), - - dissolved_organic_matter = DissolvedOrganicMatter(), - particulate_organic_matter = TwoCompartementParticulateOrganicMatter(), + phytoplankton = MixedMondoNanoAndDiatoms(), + zooplankton = MicroAndMezoZooplankton(), + dissolved_organic_matter = DissolvedOrganicCarbon(), + particulate_organic_matter = TwoCompartementCarbonIronParticles(), nitrogen = NitrateAmmonia(), iron = SimpleIron(), @@ -378,8 +276,7 @@ function PISCES(; grid, oxygen = Oxygen(), phosphate = Phosphate(), - calcite = Calcite(), - carbon_system = CarbonateSystem(), + carbon_system = InorganicCarbon(), # from Aumount 2005 rather than 2015 since it doesn't work the other way around first_anoxia_thresehold = 6.0, diff --git a/src/Models/AdvectedPopulations/PISCES/adapts.jl b/src/Models/AdvectedPopulations/PISCES/adapts.jl deleted file mode 100644 index 8f9e6459e..000000000 --- a/src/Models/AdvectedPopulations/PISCES/adapts.jl +++ /dev/null @@ -1,35 +0,0 @@ -using Adapt - -import Adapt: adapt_structure - -# we can throw away all of the fields since they're delt with outside of the kernels -Adapt.adapt_structure(to, bgc::PISCES) = - PISCES(adapt(to, bgc.nanophytoplankton), - adapt(to, bgc.diatoms), - adapt(to, bgc.microzooplankton), - adapt(to, bgc.mesozooplankton), - adapt(to, bgc.dissolved_organic_matter), - adapt(to, bgc.particulate_organic_matter), - adapt(to, bgc.nitrogen), - adapt(to, bgc.iron), - adapt(to, bgc.silicate), - adapt(to, bgc.oxygen), - adapt(to, bgc.phosphate), - adapt(to, bgc.calcite), - adapt(to, bgc.carbon_system), - adapt(to, bgc.first_anoxia_threshold), - adapt(to, bgc.second_anoxia_threshold), - adapt(to, bgc.nitrogen_redfield_ratio), - adapt(to, bgc.phosphate_redfield_ratio), - adapt(to, bgc.mixed_layer_shear), - adapt(to, bgc.background_shear), - adapt(to, bgc.latitude), - adapt(to, bgc.day_length), - adapt(to, bgc.mixed_layer_depth), - adapt(to, bgc.euphotic_depth), - adapt(to, bgc.silicate_climatology), - adapt(to, bgc.mean_mixed_layer_vertical_diffusivity), - adapt(to, bgc.mean_mixed_layer_light), - adapt(to, bgc.carbon_chemistry), - adapt(to, bgc.silicate_climatology), - adapt(to, bgc.sinking_velocities)) diff --git a/src/Models/AdvectedPopulations/PISCES/calcite.jl b/src/Models/AdvectedPopulations/PISCES/calcite.jl deleted file mode 100644 index 744f368e6..000000000 --- a/src/Models/AdvectedPopulations/PISCES/calcite.jl +++ /dev/null @@ -1,79 +0,0 @@ -""" - Calcite - -Stores the parameter values for calcite (`CaCO₃`) evolution. - -Keyword Arguments -================= -- `base_rain_ratio`: the base fraction of Coccolithophores -- `base_dissolution_rate`: base rate of calcite dissolution (1/s) -- `dissolution_exponent`: exponent of calcite excess for dissolution rate - -""" -@kwdef struct Calcite{FT} - base_rain_ratio :: FT = 0.3 # - base_dissolution_rate :: FT = 0.197 / day # 1 / s - dissolution_exponent :: FT = 1.0 # -end - -@inline function (calcite::Calcite)(::Val{:CaCO₃}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - production = calcite_production(calcite, bgc, z, P, D, PChl, PFe, Z, M, POC, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, PAR) - - dissolution = calcite_dissolution(calcite, CaCO₃, Ω) - - return production - dissolution -end - -@inline function calcite_production(calcite, bgc, z, P, D, PChl, PFe, Z, M, POC, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, PAR) - R = rain_ratio(calcite, bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, PAR) - - microzooplankton = specific_calcite_grazing_loss(bgc.microzooplankton, P, D, Z, POC, T) * Z - mesozooplankton = specific_calcite_grazing_loss(bgc.mesozooplankton, P, D, Z, POC, T) * M - - linear_mortality, quadratic_mortality = mortality(bgc.nanophytoplankton, bgc, z, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - - total_mortality = 0.5 * (linear_mortality + quadratic_mortality) - - return R * (microzooplankton + mesozooplankton + total_mortality) -end - -# should this be in the particles thing? -@inline function rain_ratio(calcite::Calcite, bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, PAR) - r = calcite.base_rain_ratio - - # assuming this is a type in Aumont 2015 based on Aumont 2005 - L, = bgc.nanophytoplankton.nutrient_limitation(bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′) - - L_CaCO₃ = L # maybe this is wrong, can't find the reference, others had it as min(Lₙᴾ, concentration_limitation(Fe, 6e-11), concentration_limitation(PO₄, Kₙₕ₄ᴾ)) - - phytoplankton_concentration_factor = max(1, P / 2) - - low_light_factor = max(0, PAR - 1) / (4 + PAR) - high_light_factor = 30 / (30 + PAR) - - low_temperature_factor = max(0, T / (T + 0.1)) # modified from origional as it goes negative and does not achieve goal otherwise - high_temperature_factor = 1 + exp(-(T - 10)^2 / 25) - - depth_factor = min(1, -50/zₘₓₗ) - - return r * L_CaCO₃ * phytoplankton_concentration_factor * low_light_factor * high_light_factor * low_temperature_factor * high_temperature_factor * depth_factor -end - -@inline function calcite_dissolution(calcite, CaCO₃, Ω) - λ = calcite.base_dissolution_rate - nca = calcite.dissolution_exponent - - ΔCaCO₃ = max(0, 1 - Ω) - - return λ * ΔCaCO₃ ^ nca * CaCO₃ -end diff --git a/src/Models/AdvectedPopulations/PISCES/carbonate_system.jl b/src/Models/AdvectedPopulations/PISCES/carbonate_system.jl deleted file mode 100644 index e17cd5a26..000000000 --- a/src/Models/AdvectedPopulations/PISCES/carbonate_system.jl +++ /dev/null @@ -1,88 +0,0 @@ -""" - CarbonateSystem - -Default parameterisation for `DIC`` and `Alk`alinity evolution. -""" -struct CarbonateSystem end - -@inline function (carbonates::CarbonateSystem)(::Val{:DIC}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - microzooplankton_respiration = specific_inorganic_grazing_waste(bgc.microzooplankton, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) * Z - mesozooplankton_respiration = specific_inorganic_grazing_waste(bgc.mesozooplankton, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) * M - - zooplankton_respiration = microzooplankton_respiration + mesozooplankton_respiration - - upper_trophic_respiration = inorganic_upper_trophic_respiration_product(bgc.mesozooplankton, M, T) - - dissolved_degredation = bacterial_degradation(bgc.dissolved_organic_matter, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, T, zₘₓₗ, zₑᵤ) - - calcite_diss = calcite_dissolution(bgc.calcite, CaCO₃, Ω) - - calcite_prod = calcite_production(bgc.calcite, bgc, z, P, D, PChl, PFe, Z, M, POC, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, PAR) - - nanophytoplankton_consumption, = total_production(bgc.nanophytoplankton, bgc, y, t, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - diatom_consumption, = total_production(bgc.diatoms, bgc, y, t, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - consumption = nanophytoplankton_consumption + diatom_consumption - - return zooplankton_respiration + upper_trophic_respiration + dissolved_degredation + calcite_diss - calcite_prod - consumption -end - -@inline function (carbonates::CarbonateSystem)(::Val{:Alk}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - θ = bgc.nitrogen_redfield_ratio - - nitrate_production = bgc.nitrogen(Val(:NO₃), bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) * θ - - ammonia_production = bgc.nitrogen(Val(:NH₄), bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) * θ - - calcite_production = bgc.calcite(Val(:CaCO₃), bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - # I think there are typos in Aumount 2015 but this is what it should be - - return ammonia_production - nitrate_production - 2 * calcite_production -end diff --git a/src/Models/AdvectedPopulations/PISCES/common.jl b/src/Models/AdvectedPopulations/PISCES/common.jl index fbf386bde..cabf5e0dd 100644 --- a/src/Models/AdvectedPopulations/PISCES/common.jl +++ b/src/Models/AdvectedPopulations/PISCES/common.jl @@ -1,7 +1,7 @@ using KernelAbstractions: @kernel, @index using Oceananigans.Fields: flatten_node -using Oceananigans.Grids: znode, zspacing +using Oceananigans.Grids: znode, zspacing, φnode import Oceananigans.Fields: flatten_node @@ -27,7 +27,9 @@ struct PrescribedLatitude{FT} end @inline (pl::PrescribedLatitude)(y) = pl.latitude -@inline (::ModelLatitude)(y) = y + +@inline (::ModelLatitude)(φ) = φ +@inline (::ModelLatitude)(i, j, k, grid) = φnode(i, j, k, grid, Center(), Center(), Center()) """ day_length_function(φ, t) @@ -77,4 +79,4 @@ end min_2 = bgc.second_anoxia_threshold return min(1, max(0, 0.4 * (min_1 - O₂) / (min_2 + O₂))) -end \ No newline at end of file +end diff --git a/src/Models/AdvectedPopulations/PISCES/compute_calcite_saturation.jl b/src/Models/AdvectedPopulations/PISCES/compute_calcite_saturation.jl deleted file mode 100644 index 12e0a0ca4..000000000 --- a/src/Models/AdvectedPopulations/PISCES/compute_calcite_saturation.jl +++ /dev/null @@ -1,35 +0,0 @@ -using Oceananigans.Architectures: architecture -using Oceananigans.BoundaryConditions: fill_halo_regions! -using Oceananigans.BuoyancyModels: g_Earth -using Oceananigans.Models: fields -using Oceananigans.Utils: launch! - -using OceanBioME.Models: CarbonChemistryModel - -function compute_calcite_saturation!(carbon_chemistry, calcite_saturation, model) - grid = model.grid - - arch = architecture(grid) - - launch!(arch, grid, :xyz, _compute_calcite_saturation!, carbon_chemistry, calcite_saturation, grid, fields(model)) - - fill_halo_regions!(calcite_saturation) - - return nothing -end - -@kernel function _compute_calcite_saturation!(carbon_chemistry, calcite_saturation, grid, model_fields) - i, j, k = @index(Global, NTuple) - - T = @inbounds model_fields.T[i, j, k] - S = @inbounds model_fields.S[i, j, k] - DIC = @inbounds model_fields.DIC[i, j, k] - Alk = @inbounds model_fields.Alk[i, j, k] - silicate = @inbounds model_fields.Si[i, j, k] # might get rid of this since it doesn't do anything - - z = znode(i, j, k, grid, Center(), Center(), Center()) - - P = abs(z) * g_Earth * 1026 / 100000 # rough but I don't think we should bother integrating the actual density - - @inbounds calcite_saturation[i, j, k] = CarbonChemistryModel.calcite_saturation(carbon_chemistry; DIC, T, S, Alk, P, silicate) -end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/coupling_utils.jl b/src/Models/AdvectedPopulations/PISCES/coupling_utils.jl deleted file mode 100644 index 12eff15e5..000000000 --- a/src/Models/AdvectedPopulations/PISCES/coupling_utils.jl +++ /dev/null @@ -1,42 +0,0 @@ -import OceanBioME.Models.Sediments: nitrogen_flux, carbon_flux, remineralisation_receiver, sinking_tracers - -# sediment models -@inline redfield(val_name, bgc::PISCES, tracers) = bgc.nitrogen_redfield_ratio - -@inline nitrogen_flux(i, j, k, grid, advection, bgc::PISCES, tracers) = bgc.nitrogen_redfield_ratio * carbon_flux(i, j, k, grid, advection, bgc, tracers) - -@inline carbon_flux(i, j, k, grid, advection, bgc::PISCES, tracers) = sinking_flux(i, j, k, grid, adveciton, bgc, Val(:POC), tracers) + - sinking_flux(i, j, k, grid, adveciton, bgc, Val(:GOC), tracers) - -@inline remineralisation_receiver(::PISCES) = :NH₄ - -@inline sinking_tracers(::PISCES) = (:POC, :GOC, :SFe, :BFe, :PSi, :CaCO₃) # please list them here - -# light attenuation model -@inline chlorophyll(::PISCES, model) = model.tracers.PChl + model.tracers.DChl - -# negative tracer scaling -# TODO: deal with remaining (PChl, DChl, O₂, Alk) - latter two should never be near zero -@inline function conserved_tracers(bgc::PISCES; ntuple = false) - carbon = (:P, :D, :Z, :M, :DOC, :POC, :GOC, :DIC, :CaCO₃) - - # iron ratio for DOC might be wrong - iron = (tracers = (:PFe, :DFe, :Z, :M, :SFe, :BFe, :Fe), - scalefactors = (1, 1, bgc.microzooplankton.iron_ratio, bgc.mesozooplankton.iron_ratio, 1, 1, 1)) - - θ_PO₄ = bgc.phosphate_redfield_ratio - phosphate = (tracers = (:P, :D, :Z, :M, :DOC, :POC, :GOC, :PO₄), - scalefactors = (θ_PO₄, θ_PO₄, θ_PO₄, θ_PO₄, θ_PO₄, θ_PO₄, θ_PO₄, 1)) - - silicon = (:DSi, :Si, :PSi) - - θN = bgc.nitrogen_redfield_ratio - nitrogen = (tracers = (:NH₄, :NO₃, :P, :D, :Z, :M, :DOC, :POC, :GOC), - scalefactors = (1, 1, θN, θN, θN, θN, θN, θN, θN)) - - if ntuple - return (; carbon, iron, phosphate, silicon, nitrogen) - else - return (carbon, iron, phosphate, silicon, nitrogen) - end -end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter.jl b/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter.jl deleted file mode 100644 index 0f0e69894..000000000 --- a/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter.jl +++ /dev/null @@ -1,152 +0,0 @@ -""" - DissolvedOrganicMatter - -Parameterisation of dissolved organic matter which depends on a bacterial -concentration derived from the concentration of zooplankton. -""" -@kwdef struct DissolvedOrganicMatter{FT, AP} - remineralisation_rate :: FT = 0.3/day # 1 / s - microzooplankton_bacteria_concentration :: FT = 0.7 # - mesozooplankton_bacteria_concentration :: FT = 1.4 # - maximum_bacteria_concentration :: FT = 4.0 # mmol C / m³ - bacteria_concentration_depth_exponent :: FT = 0.684 # - reference_bacteria_concentration :: FT = 1.0 # mmol C / m³ - temperature_sensetivity :: FT = 1.066 # - doc_half_saturation_for_bacterial_activity :: FT = 417.0 # mmol C / m³ - nitrate_half_saturation_for_bacterial_activity :: FT = 0.03 # mmol N / m³ - ammonia_half_saturation_for_bacterial_activity :: FT = 0.003 # mmol N / m³ - phosphate_half_saturation_for_bacterial_activity :: FT = 0.003 # mmol P / m³ - iron_half_saturation_for_bacterial_activity :: FT = 0.01 # μmol Fe / m³ -# (1 / (mmol C / m³), 1 / (mmol C / m³), 1 / (mmol C / m³), 1 / (mmol C / m³) / s, 1 / (mmol C / m³) / s) - aggregation_parameters :: AP = (0.37, 102, 3530, 5095, 114) .* (10^-6 / day) - maximum_iron_ratio_in_bacteria :: FT = 0.06 # μmol Fe / mmol C - iron_half_saturation_for_bacteria :: FT = 0.3 # μmol Fe / m³ - maximum_bacterial_growth_rate :: FT = 0.6 / day # 1 / s -end - -@inline function (dom::DissolvedOrganicMatter)(::Val{:DOC}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - nanophytoplankton_exudation = dissolved_exudate(bgc.nanophytoplankton, bgc, y, t, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - diatom_exudation = dissolved_exudate(bgc.diatoms, bgc, y, t, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - phytoplankton_exudation = nanophytoplankton_exudation + diatom_exudation - - particulate_degredation = specific_degredation_rate(bgc.particulate_organic_matter, bgc, O₂, T) * POC - - respiration_product = dissolved_upper_trophic_respiration_product(bgc.mesozooplankton, M, T) - - microzooplankton_grazing_waste = specific_dissolved_grazing_waste(bgc.microzooplankton, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) * Z - mesozooplankton_grazing_waste = specific_dissolved_grazing_waste(bgc.mesozooplankton, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) * M - - grazing_waste = microzooplankton_grazing_waste + mesozooplankton_grazing_waste - - degredation = bacterial_degradation(dom, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, T, zₘₓₗ, zₑᵤ) - - aggregation_to_particles, = aggregation(dom, bgc, z, DOC, POC, GOC, zₘₓₗ) - - return phytoplankton_exudation + particulate_degredation + respiration_product + grazing_waste - degredation - aggregation_to_particles -end - -@inline function bacteria_concentration(dom::DissolvedOrganicMatter, z, Z, M, zₘₓₗ, zₑᵤ) - bZ = dom.microzooplankton_bacteria_concentration - bM = dom.mesozooplankton_bacteria_concentration - a = dom.bacteria_concentration_depth_exponent - - zₘ = min(zₘₓₗ, zₑᵤ) - - surface_bacteria = min(4, bZ * Z + bM * M) - - depth_factor = (zₘ / z) ^ a - - return ifelse(z >= zₘ, 1, depth_factor) * surface_bacteria -end - -@inline function bacteria_activity(dom::DissolvedOrganicMatter, DOC, NO₃, NH₄, PO₄, Fe) - K_DOC = dom.doc_half_saturation_for_bacterial_activity - K_NO₃ = dom.nitrate_half_saturation_for_bacterial_activity - K_NH₄ = dom.ammonia_half_saturation_for_bacterial_activity - K_PO₄ = dom.phosphate_half_saturation_for_bacterial_activity - K_Fe = dom.iron_half_saturation_for_bacterial_activity - - DOC_limit = DOC / (DOC + K_DOC) - - L_N = (K_NO₃ * NH₄ + K_NH₄ * NO₃) / (K_NO₃ * K_NH₄ + K_NO₃ * NH₄ + K_NH₄ * NO₃) - - L_PO₄ = PO₄ / (PO₄ + K_PO₄) - - L_Fe = Fe / (Fe + K_Fe) - - # assuming typo in paper otherwise it doesn't make sense to formulate L_NH₄ like this - limiting_quota = min(L_N, L_PO₄, L_Fe) - - return limiting_quota * DOC_limit -end - -@inline function bacterial_degradation(dom::DissolvedOrganicMatter, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, T, zₘₓₗ, zₑᵤ) - Bact_ref = dom.reference_bacteria_concentration - b = dom.temperature_sensetivity - λ = dom.remineralisation_rate - - f = b^T - - Bact = bacteria_concentration(dom, z, Z, M, zₘₓₗ, zₑᵤ) - - LBact = bacteria_activity(dom, DOC, NO₃, NH₄, PO₄, Fe) - - return λ * f * LBact * Bact / Bact_ref * DOC # differes from Aumont 2015 since the dimensions don't make sense -end - -@inline function oxic_remineralisation(dom::DissolvedOrganicMatter, bgc, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, O₂, T, zₘₓₗ, zₑᵤ) - ΔO₂ = anoxia_factor(bgc, O₂) - - degredation = bacterial_degradation(dom, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, T, zₘₓₗ, zₑᵤ) - - return (1 - ΔO₂) * degredation -end - -@inline function denitrifcation(dom::DissolvedOrganicMatter, bgc, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, O₂, T, zₘₓₗ, zₑᵤ) - ΔO₂ = anoxia_factor(bgc, O₂) - - degredation = bacterial_degradation(dom, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, T, zₘₓₗ, zₑᵤ) - - return ΔO₂ * degredation -end - -@inline function aggregation(dom::DissolvedOrganicMatter, bgc, z, DOC, POC, GOC, zₘₓₗ) - a₁, a₂, a₃, a₄, a₅ = dom.aggregation_parameters - - backgroound_shear = bgc.background_shear - mixed_layer_shear = bgc.mixed_layer_shear - - shear = ifelse(z < zₘₓₗ, backgroound_shear, mixed_layer_shear) - - Φ₁ = shear * (a₁ * DOC + a₂ * POC) * DOC - Φ₂ = shear * (a₃ * GOC) * DOC - Φ₃ = (a₄ * POC + a₅ * DOC) * DOC - - return Φ₁ + Φ₂ + Φ₃, Φ₁, Φ₂, Φ₃ -end - -@inline function bacterial_iron_uptake(dom::DissolvedOrganicMatter, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, T, zₘₓₗ, zₑᵤ) - μ₀ = dom.maximum_bacterial_growth_rate - b = dom.temperature_sensetivity - θ = dom.iron_half_saturation_for_bacteria - K = dom.iron_half_saturation_for_bacteria - - μ = μ₀ * b^T - - Bact = bacteria_concentration(dom, z, Z, M, zₘₓₗ, zₑᵤ) - - L = bacteria_activity(dom, DOC, NO₃, NH₄, PO₄, Fe) - - return μ * L * θ * Fe / (Fe + K) * Bact -end diff --git a/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_carbon.jl b/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_carbon.jl new file mode 100644 index 000000000..4b205b50a --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_carbon.jl @@ -0,0 +1,101 @@ +""" + DissolvedOrganicCarbon + +Parameterisation of dissolved organic matter which depends on a bacterial +concentration. +""" +@kwdef struct DissolvedOrganicCarbon{FT, AP} + remineralisation_rate :: FT = 0.3/day # 1 / s + bacteria_concentration_depth_exponent :: FT = 0.684 # + reference_bacteria_concentration :: FT = 1.0 # mmol C / m³ + temperature_sensetivity :: FT = 1.066 # +# (1 / (mmol C / m³), 1 / (mmol C / m³), 1 / (mmol C / m³), 1 / (mmol C / m³) / s, 1 / (mmol C / m³) / s) + aggregation_parameters :: AP = (0.37, 102, 3530, 5095, 114) .* (10^-6 / day) +end + +required_biogeochemical_tracers(::DissolvedOrganicCarbon) = tuple(:DOC) + +@inline function (bgc::PISCES{<:Any, <:Any, DissolvedOrganicCarbon})(i, j, k, grid, ::Val{:DOC}, clock, fields) + phytoplankton_exudate = dissolved_exudate(bgc.phytoplankton, bgc, i, j, k, grid, bgc, clock, fields) + upper_trophic_exudate = upper_trophic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + grazing_waste = organic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + particulate_breakdown = degredation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + dissolved_breakdown = degredation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + aggrgation_to_particles, = aggregation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + + return (phytoplankton_exudate + upper_trophic_exudate + grazing_waste + particulate_breakdown + - dissolved_breakdown - aggregation_to_particles) +end + +@inline function degredation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields) + Bact_ref = dom.reference_bacteria_concentration + b = dom.temperature_sensetivity + λ = dom.remineralisation_rate + + T = @inbounds fields.T[i, j, k] + DOC = @inbounds fields.DOC[i, j, k] + + f = b^T + + Bact = bacteria_concentration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + LBact = bacteria_activity(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + return λ * f * LBact * Bact / Bact_ref * DOC # differes from Aumont 2015 since the dimensions don't make sense +end + +@inline function aggregation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields) + a₁, a₂, a₃, a₄, a₅ = dom.aggregation_parameters + + backgroound_shear = bgc.background_shear + mixed_layer_shear = bgc.mixed_layer_shear + + z = znode(i, j, k, grid, Center(), Center(), Center()) + + zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] + + DOC = @inbounds fields.DOC[i, j, k] + POC = @inbounds fields.POC[i, j, k] + GOC = @inbounds fields.GOC[i, j, k] + + shear = ifelse(z < zₘₓₗ, backgroound_shear, mixed_layer_shear) + + Φ₁ = shear * (a₁ * DOC + a₂ * POC) * DOC + Φ₂ = shear * (a₃ * GOC) * DOC + Φ₃ = (a₄ * POC + a₅ * DOC) * DOC + + return Φ₁ + Φ₂ + Φ₃, Φ₁, Φ₂, Φ₃ +end + +@inline function aggregation_of_colloidal_iron(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields) + _, Φ₁, Φ₂, Φ₃ = aggregation(dom, i, j, k, grid, bgc, clock, fields) + + Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields) + ligand_iron = Fe - Fe′ + colloidal_iron = 0.5 * ligand_iron + + CgFe1 = (Φ₁ + Φ₃) * colloidal_iron / (DOC + eps(0.0)) + CgFe2 = Φ₂ * colloidal_iron / (DOC + eps(0.0)) + + return CgFe1 + CgFe2, CgFe1, CgFe2 +end + +@inline function oxic_remineralisation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields) + O₂ = @inbounds fields.O₂[i, j, k] + + ΔO₂ = anoxia_factor(bgc, O₂) + + degredation = degredation(dom, i, j, k, grid, bgc, clock, fields) + + return (1 - ΔO₂) * degredation +end + +@inline function anoxic_remineralisation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields) + O₂ = @inbounds fields.O₂[i, j, k] + + ΔO₂ = anoxia_factor(bgc, O₂) + + degredation = degredation(dom, i, j, k, grid, bgc, clock, fields) + + return ΔO₂ * degredation +end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_matter.jl b/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_matter.jl new file mode 100644 index 000000000..00f8fc7b3 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_matter.jl @@ -0,0 +1,14 @@ +module DissolvedOrganicMatter + +export DissolvedOrganicCarbon + +using OceanBioME.Models.PISCESModel: degredation, aggregation, PISCES +using OceanBioME.Models.PISCESModel.Phytoplankton: dissolved_exudate +using OceanBioME.Models.PISCESModel.Zooplankton: organic_excretion, upper_trophic_excretion + +import Oceananigans.Biogeochemistry: required_biogeochemical_tracers +import OceanBioME.Models.PISCESModel: degredation, aggregation + +include("dissolved_organic_carbon.jl") + +end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/generic_functions.jl b/src/Models/AdvectedPopulations/PISCES/generic_functions.jl new file mode 100644 index 000000000..6465ae169 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/generic_functions.jl @@ -0,0 +1,6 @@ +# function that need to be defined and accessed in several sub-modules + +function degredation end +function aggregation end +function mortality end +function free_iron end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/group_methods.jl b/src/Models/AdvectedPopulations/PISCES/group_methods.jl deleted file mode 100644 index 50e505a65..000000000 --- a/src/Models/AdvectedPopulations/PISCES/group_methods.jl +++ /dev/null @@ -1,160 +0,0 @@ -# Please excuse this file, origionally each one was a single line like: -# (bgc::PISCES)(val_name::NANO_PHYTO, args...) = bgc.nanophytoplankton(val_name, bgc, args...) -# but that doesn't work on GPU because there are too many arguments -# see https://github.com/CliMA/Oceananigans.jl/discussions/3784 - -(bgc::PISCES)(val_name::NANO_PHYTO, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.nanophytoplankton(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::DIATOMS, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.diatoms(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::Val{:Z}, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.microzooplankton(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::Val{:M}, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.mesozooplankton(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::Val{:DOC}, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.dissolved_organic_matter(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::PARTICLES, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.particulate_organic_matter(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - -(bgc::PISCES)(val_name::NITROGEN, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.nitrogen(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::Val{:Fe}, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.iron(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::Val{:Si}, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.silicate(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::Val{:CaCO₃}, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.calcite(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::Val{:O₂}, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.oxygen(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::Val{:PO₄}, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.phosphate(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - -(bgc::PISCES)(val_name::CARBON_SYSTEM, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃,NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) = - bgc.carbon_system(val_name, bgc, x, y, z, t, P, D, Z, M, PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) diff --git a/src/Models/AdvectedPopulations/PISCES/hack.jl b/src/Models/AdvectedPopulations/PISCES/hack.jl deleted file mode 100644 index bb87d6e0c..000000000 --- a/src/Models/AdvectedPopulations/PISCES/hack.jl +++ /dev/null @@ -1,18 +0,0 @@ -using Oceananigans.Biogeochemistry: required_biogeochemical_tracers, required_biogeochemical_auxiliary_fields, extract_biogeochemical_fields -using Oceananigans.Grids: xnode, ynode, znode - -import Oceananigans.Biogeochemistry: biogeochemical_transition - -@inline function biogeochemical_transition(i, j, k, grid, bgc::PISCES, val_tracer_name, clock, fields) - tracer_names_to_extract = required_biogeochemical_tracers(bgc) - auxiliary_names_to_extract = required_biogeochemical_auxiliary_fields(bgc) - - tracer_fields_ijk = extract_biogeochemical_fields(i, j, k, grid, fields, tracer_names_to_extract) - auxiliary_fields_ijk = extract_biogeochemical_fields(i, j, k, grid, fields, auxiliary_names_to_extract) - - x = xnode(i, j, k, grid, Center(), Center(), Center()) - y = ynode(i, j, k, grid, Center(), Center(), Center()) - z = znode(i, j, k, grid, Center(), Center(), Center()) - - return bgc(val_tracer_name, x, y, z, clock.time, tracer_fields_ijk..., auxiliary_fields_ijk...) -end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl b/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl new file mode 100644 index 000000000..061f819ce --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl @@ -0,0 +1,53 @@ +module InorganicCarbons + +export InorganicCarbon + +using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: degredation + +using OceanBioME.Models.PISCESModel.ParticulateOrganicMatter: + calcite_production, calcite_dissolution + +using OceanBioME.Models.PISCESModel.Phytoplankton: total_production + +using OceanBioME.Models.PISCESModel.Zooplankton: + inorganic_excretion, upper_trophic_respiration + +""" + InorganicCarbon + +Default parameterisation for `DIC`` and `Alk`alinity evolution. +""" +struct InorganicCarbon end + +const PISCESCarbon = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, InorganicCarbon} + +@inline function (bgc::PISCESCarbon)(i, j, k, grid, val_name::Val{:DIC}, clock, fields) + zooplankton_respiration = inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + upper_trophic_respiration = upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + remineralisation = degredation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + + calcite_dissolution = calcite_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + calcite_production = calcite_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + + consumption = total_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + + return (zooplankton_respiration + upper_trophic_respiration + remineralisation + + calcite_dissolution - calcite_production + - consumption) +end + +@inline function (bgc::PISCESCarbon)(i, j, k, grid, val_name::Val{:Alk}, clock, fields) + θ = bgc.nitrogen_redfield_ratio + + nitrate_production = bgc(i, j, k, grid, Val(:NO₃), clock, fields) + ammonia_production = bgc(i, j, k, grid, Val(:NH₄), clock, fields) + calcite_production = bgc(i, j, k, grid, Val(:CaCO₃), clock, fields) + + # I think there are typos in Aumount 2015 but this is what it should be ( I think ???) + return ammonia_production - nitrate_production - 2 * calcite_production +end + +end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/iron.jl b/src/Models/AdvectedPopulations/PISCES/iron.jl deleted file mode 100644 index 824bd8209..000000000 --- a/src/Models/AdvectedPopulations/PISCES/iron.jl +++ /dev/null @@ -1,88 +0,0 @@ -""" - SimpleIron(; excess_scavenging_enhancement = 1000) - -Parameterisation for iron evolution, not the "complex chemistry" model -of Aumount et al, 2015. Iron is scavenged (i.e. perminemtly removed from -the model) when the free iron concentration exeeds the ligand concentration -at a rate modified by `excess_scavenging_enhancement`. -""" -@kwdef struct SimpleIron{FT} - excess_scavenging_enhancement :: FT = 1000 # unitless -end - -@inline function (iron::SimpleIron)(::Val{:Fe}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - λ̄ = iron.excess_scavenging_enhancement - - λFe = iron_scavenging_rate(bgc.particulate_organic_matter, POC, GOC, CaCO₃, PSi) - - Fe′ = free_iron(iron, Fe, DOC, T) - total_ligand_concentration = max(0.6, 0.09 * (DOC + 40) - 3) - - # terminal process which removes iron from the ocean - ligand_aggregation = λ̄ * λFe * max(0, Fe - total_ligand_concentration) * Fe′ - - # other aggregation - colloidal_aggregation, = aggregation_of_colloidal_iron(iron, bgc.dissolved_organic_matter, bgc, z, DOC, POC, GOC, Fe, T, zₘₓₗ) - - aggregation = colloidal_aggregation + ligand_aggregation - - # scavening and bacterial uptake to particles - scav = λFe * (POC + GOC) * Fe′ - - BactFe = bacterial_iron_uptake(bgc.dissolved_organic_matter, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, T, zₘₓₗ, zₑᵤ) - - λPOC = specific_degredation_rate(bgc.particulate_organic_matter, bgc, O₂, T) - - # particle breakdown - particulate_degredation = λPOC * SFe - - # consumption - nanophytoplankton_consumption, = iron_uptake(bgc.nanophytoplankton, bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T) - diatom_consumption, = iron_uptake(bgc.diatoms, bgc, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T) - - consumption = nanophytoplankton_consumption + diatom_consumption - - # grazing waste - this is the excess non assimilated into zooplankton when they consume iron rich phytoplankton - microzooplankton_waste = specific_non_assimilated_iron(bgc.microzooplankton, bgc, P, D, PFe, DFe, Z, POC, GOC, SFe, BFe, T, wPOC, wGOC) * Z - mesozooplankton_waste = specific_non_assimilated_iron(bgc.mesozooplankton, bgc, P, D, PFe, DFe, Z, POC, GOC, SFe, BFe, T, wPOC, wGOC) * M - - zooplankton_waste = microzooplankton_waste + mesozooplankton_waste - - # type in Aumount 2015, γ should not be present since DOC doesn't contain iron/there is no DOFe pool - respiration_product = upper_trophic_respiration_product(bgc.mesozooplankton, M, T) * bgc.mesozooplankton.iron_ratio - - return zooplankton_waste + respiration_product + particulate_degredation - consumption - scav - aggregation - BactFe -end - -@inline function free_iron(::SimpleIron, Fe, DOC, T) - # maybe some of these numbers should be parameters - ligands = max(0.6, 0.09 * (DOC + 40) - 3) - K = exp(16.27 - 1565.7 / max(T + 273.15, 5)) - Δ = 1 + K * ligands - K * Fe - - return (-Δ + √(Δ^2 + 4K * Fe)) / 2K -end - -# this should be dispatched on an abstract type if we implement complex chemistry -@inline function aggregation_of_colloidal_iron(iron::SimpleIron, dom, bgc, z, DOC, POC, GOC, Fe, T, zₘₓₗ) - _, Φ₁, Φ₂, Φ₃ = aggregation(dom, bgc, z, DOC, POC, GOC, zₘₓₗ) - - Fe′ = free_iron(iron, Fe, DOC, T) - ligand_iron = Fe - Fe′ - colloidal_iron = 0.5 * ligand_iron - - CgFe1 = (Φ₁ + Φ₃) * colloidal_iron / (DOC + eps(0.0)) - CgFe2 = Φ₂ * colloidal_iron / (DOC + eps(0.0)) - - return CgFe1 + CgFe2, CgFe1, CgFe2 -end diff --git a/src/Models/AdvectedPopulations/PISCES/iron/iron.jl b/src/Models/AdvectedPopulations/PISCES/iron/iron.jl new file mode 100644 index 000000000..fd07884d3 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/iron/iron.jl @@ -0,0 +1,34 @@ +module Iron + +export SimpleIron + +using OceanBioME.Models.PISCESModel: PISCES + +using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: + aggregation_of_colloidal_iron, bacterial_iron_uptake + +using OceanBioME.Models.PISCESModel.ParticulateOrganicMatter: + iron_scavenging, iron_scavenging_rate, bacterial_iron_uptake + +using OceanBioME.Models.PISCESModel.Phytoplankton: uptake + +using OceanBioME.Models.PISCESModel.Zooplankton: + non_assimilated_iron, upper_trophic_iron_waste + +import OceanBioME.Models.PISCESModel: free_iron + +include("simple_iron.jl") + +@inline function free_iron(::SimpleIron, i, j, k, grid, bgc, clock, fields) + DOC = @inbounds fields.DOC[i, j, k] + Fe = @inbounds fields.Fe[i, j, k] + + # maybe some of these numbers should be parameters + ligands = max(0.6, 0.09 * (DOC + 40) - 3) + K = exp(16.27 - 1565.7 / max(T + 273.15, 5)) + Δ = 1 + K * ligands - K * Fe + + return (-Δ + √(Δ^2 + 4K * Fe)) / 2K +end + +end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl b/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl new file mode 100644 index 000000000..998b98f02 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl @@ -0,0 +1,59 @@ +""" + SimpleIron(; excess_scavenging_enhancement = 1000) + +Parameterisation for iron evolution, not the "complex chemistry" model +of Aumount et al, 2015. Iron is scavenged (i.e. perminemtly removed from +the model) when the free iron concentration exeeds the ligand concentration +at a rate modified by `excess_scavenging_enhancement`. +""" +@kwdef struct SimpleIron{FT} + excess_scavenging_enhancement :: FT = 1000 # unitless + maximum_ligand_concentration :: FT = 0.6 # μmol Fe / m³ + dissolved_ligand_ratio :: FT = 0.09 # μmol Fe / mmol C +end + +const SimpleIronPISCES = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, SimpleIron} + +@inline function (bgc::SimpleIronPISCES)(i, j, k, grid, val_name::Val{:Fe}, clock, fields) + λ̄ = iron.excess_scavenging_enhancement + + λFe = iron_scavenging_rate(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields) + + total_ligand_concentration = ligand_concentration(bgc.iron, i, j, k, grid, bgc, clock, fields) + + # terminal process which removes iron from the ocean + ligand_aggregation = λ̄ * λFe * max(0, Fe - total_ligand_concentration) * Fe′ + + colloidal_aggregation, = aggregation_of_colloidal_iron(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + + # scavenging and bacterial uptake + scavenging = iron_scavenging(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + BactFe = bacterial_iron_uptake(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + # particle breakdown + small_particles = degredation(bgc.particulate_organic_matter, Val(:SFe), i, j, k, grid, bgc, clock, fields) + + # consumption + consumption = uptake(bgc.phytoplankton, val_name, i, j, k, grid, bgc, clock, fields) + + # waste + grazing_waste = non_assimilated_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + upper_trophic_waste = upper_trophic_iron_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + return (small_particles + grazing_waste + upper_trophic_waste + - consumption - ligand_aggregation - colloidal_aggregation - scavenging - BactFe) +end + +@inline function ligand_concentration(iron::SimpleIron, i, j, k, grid, bgc, clock, fields) + Lₜᵐᵃˣ = iron.maximum_ligand_concentration + + DOC = @inbounds fields.DOC[i, j, k] + + Lₜ = iron.dissolved_ligand_ratio * DOC - Lₜᵐᵃˣ + + return max(Lₜᵐᵃˣ, Lₜ) +end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/iron_in_particles.jl b/src/Models/AdvectedPopulations/PISCES/iron_in_particles.jl deleted file mode 100644 index b0c2081fb..000000000 --- a/src/Models/AdvectedPopulations/PISCES/iron_in_particles.jl +++ /dev/null @@ -1,142 +0,0 @@ -@inline function (poc::TwoCompartementParticulateOrganicMatter)(::Val{:SFe}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - grazing_waste = specific_non_assimilated_iron_waste(bgc.microzooplankton, bgc, P, D, PFe, DFe, Z, POC, GOC, SFe, BFe, T, wPOC, wGOC) * Z - - # mortality terms - R_CaCO₃ = rain_ratio(bgc.calcite, bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, PAR) - - nanophytoplankton_linear_mortality, nanophytoplankton_quadratic_mortality = mortality(bgc.nanophytoplankton, bgc, z, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - - nanophytoplankton_mortality = (1 - 0.5 * R_CaCO₃) * (nanophytoplankton_linear_mortality + nanophytoplankton_quadratic_mortality) * PFe / (P + eps(0.0)) - - diatom_linear_mortality, = mortality(bgc.diatoms, bgc, z, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - - diatom_mortality = 0.5 * diatom_linear_mortality * DFe / (D + eps(0.0)) - - microzooplankton_mortality = mortality(bgc.microzooplankton, bgc, Z, O₂, T) * bgc.microzooplankton.iron_ratio - - # degredation - λ = specific_degredation_rate(poc, bgc, O₂, T) - - large_particle_degredation = λ * BFe - degredation = λ * SFe - - # grazing - microzooplankton_grazing = particulate_grazing(bgc.microzooplankton, P, D, Z, POC, T) * Z - mesozooplankton_grazing = particulate_grazing(bgc.mesozooplankton, P, D, Z, POC, T) * M - - small_flux_feeding = specific_flux_feeding(bgc.mesozooplankton, POC, T, wPOC) * M - - grazing = (microzooplankton_grazing + mesozooplankton_grazing + small_flux_feeding) * SFe / (POC + eps(0.0)) - - # aggregation - - aggregation_to_large = aggregation(poc, bgc, z, POC, GOC, zₘₓₗ) - - total_aggregation = aggregation_to_large * SFe / (POC + eps(0.0)) - - # scavenging - λFe = iron_scavenging_rate(poc, POC, GOC, CaCO₃, PSi) - - Fe′ = free_iron(bgc.iron, Fe, DOC, T) - - scavenging = λFe * POC * Fe′ - - # bacterial uptake of dissolved iron - κ = poc.small_fraction_of_bacterially_consumed_iron - - BactFe = bacterial_iron_uptake(bgc.dissolved_organic_matter, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, T, zₘₓₗ, zₑᵤ) - - bacterial_assimilation = κ * BactFe - - # colloidal iron aggregation - _, colloidal_aggregation = aggregation_of_colloidal_iron(bgc.iron, bgc.dissolved_organic_matter, bgc, z, DOC, POC, GOC, Fe, T, zₘₓₗ) - - return (grazing_waste - + nanophytoplankton_mortality + diatom_mortality + microzooplankton_mortality - + large_particle_degredation + scavenging + bacterial_assimilation + colloidal_aggregation - - total_aggregation - - grazing - degredation) -end - - -@inline function (poc::TwoCompartementParticulateOrganicMatter)(::Val{:BFe}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - grazing_waste = specific_non_assimilated_iron_waste(bgc.mesozooplankton, bgc, P, D, PFe, DFe, Z, POC, GOC, SFe, BFe, T, wPOC, wGOC) * M - - # mortality terms - R_CaCO₃ = rain_ratio(bgc.calcite, bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, PAR) - - nanophytoplankton_linear_mortality, nanophytoplankton_quadratic_mortality = mortality(bgc.nanophytoplankton, bgc, z, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - - nanophytoplankton_mortality = 0.5 * R_CaCO₃ * (nanophytoplankton_linear_mortality + nanophytoplankton_quadratic_mortality) * PFe / (P + eps(0.0)) - - diatom_linear_mortality, diatom_quadratic_mortality = mortality(bgc.diatoms, bgc, z, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - - diatom_mortality = (0.5 * diatom_linear_mortality + diatom_quadratic_mortality) * DFe / (D + eps(0.0)) - - mesozooplankton_mortality = linear_mortality(bgc.mesozooplankton, bgc, M, O₂, T) * bgc.mesozooplankton.iron_ratio - - # degredation - λ = specific_degredation_rate(poc, bgc, O₂, T) - - degredation = λ * BFe - - # grazing - grazing = specific_flux_feeding(bgc.mesozooplankton, GOC, T,wGOC) * M * BFe / (GOC + eps(0.0)) - - # aggregation - small_particle_aggregation = aggregation(poc, bgc, z, POC, GOC, zₘₓₗ) - - total_aggregation = small_particle_aggregation * SFe / (POC + eps(0.0)) - - # fecal pelet prodiction - fecal_pelet_production = upper_trophic_fecal_product(bgc.mesozooplankton, M, T) * bgc.mesozooplankton.iron_ratio - - # scavenging - λFe = iron_scavenging_rate(poc, POC, GOC, CaCO₃, PSi) - - Fe′ = free_iron(bgc.iron, Fe, DOC, T) - - scavenging = λFe * GOC * Fe′ - - # bacterial uptake of dissolved iron - κ = poc.large_fraction_of_bacterially_consumed_iron - - BactFe = bacterial_iron_uptake(bgc.dissolved_organic_matter, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, T, zₘₓₗ, zₑᵤ) - - bacterial_assimilation = κ * BactFe - - # colloidal iron aggregation - _, _, colloidal_aggregation = aggregation_of_colloidal_iron(bgc.iron, bgc.dissolved_organic_matter, bgc, z, DOC, POC, GOC, Fe, T, zₘₓₗ) - - return (grazing_waste - + nanophytoplankton_mortality + diatom_mortality + mesozooplankton_mortality - + total_aggregation + fecal_pelet_production + scavenging + bacterial_assimilation + colloidal_aggregation - - grazing - degredation) -end - -@inline function iron_scavenging_rate(pom, POC, GOC, CaCO₃, PSi) - λ₀ = pom.minimum_iron_scavenging_rate - λ₁ = pom.load_specific_iron_scavenging_rate - - return λ₀ + λ₁ * (POC + GOC + CaCO₃ + PSi) -end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/mean_mixed_layer_properties.jl b/src/Models/AdvectedPopulations/PISCES/mean_mixed_layer_properties.jl deleted file mode 100644 index 13d893126..000000000 --- a/src/Models/AdvectedPopulations/PISCES/mean_mixed_layer_properties.jl +++ /dev/null @@ -1,117 +0,0 @@ -using Oceananigans.Architectures: architecture -using Oceananigans.AbstractOperations: AbstractOperation -using Oceananigans.BoundaryConditions: fill_halo_regions! -using Oceananigans.Utils: launch! - -##### -##### generic integration -##### - -function compute_mixed_layer_mean!(Cₘₓₗ, mixed_layer_depth, C, grid) - arch = architecture(grid) - - launch!(arch, grid, :xy, _compute_mixed_layer_mean!, Cₘₓₗ, mixed_layer_depth, C, grid) - - fill_halo_regions!(Cₘₓₗ) - - return nothing -end - -compute_mixed_layer_mean!(Cₘₓₗ::AbstractOperation, mixed_layer_depth, C, grid) = nothing -compute_mixed_layer_mean!(Cₘₓₗ::ConstantField, mixed_layer_depth, C, grid) = nothing -compute_mixed_layer_mean!(Cₘₓₗ::ZeroField, mixed_layer_depth, C, grid) = nothing -compute_mixed_layer_mean!(Cₘₓₗ::Nothing, mixed_layer_depth, C, grid) = nothing - -@kernel function _compute_mixed_layer_mean!(Cₘₓₗ, mixed_layer_depth, C, grid) - i, j = @index(Global, NTuple) - - zₘₓₗ = @inbounds mixed_layer_depth[i, j, 1] - - @inbounds Cₘₓₗ[i, j, 1] = 0 - - integration_depth = 0 - - for k in grid.Nz:-1:1 - zₖ = znode(i, j, k, grid, Center(), Center(), Face()) - zₖ₊₁ = znode(i, j, k + 1, grid, Center(), Center(), Face()) - - Δzₖ = zₖ₊₁ - zₖ - Δzₖ₊₁ = ifelse(zₖ₊₁ > zₘₓₗ, zₖ₊₁ - zₘₓₗ, 0) - - Δz = ifelse(zₖ >= zₘₓₗ, Δzₖ, Δzₖ₊₁) - - Cₘₓₗ[i, j, 1] += C[i, j, k] * Δz - - integration_depth += Δz - end - - Cₘₓₗ[i, j, 1] /= integration_depth -end - -##### -##### Mean mixed layer diffusivity -##### - - -compute_mean_mixed_layer_vertical_diffusivity!(κ, mixed_layer_depth, model) = - compute_mean_mixed_layer_vertical_diffusivity!(model.closure, κ, mixed_layer_depth, model.diffusivity_fields, model.grid) - -# need these to catch when model doesn't have closure (i.e. box model) -compute_mean_mixed_layer_vertical_diffusivity!(κ::ConstantField, mixed_layer_depth, model) = nothing -compute_mean_mixed_layer_vertical_diffusivity!(κ::ZeroField, mixed_layer_depth, model) = nothing -compute_mean_mixed_layer_vertical_diffusivity!(κ::Nothing, mixed_layer_depth, model) = nothing - -# if no closure is defined we just assume its pre-set -compute_mean_mixed_layer_vertical_diffusivity!(closure::Nothing, - mean_mixed_layer_vertical_diffusivity, - mixed_layer_depth, - diffusivity_fields, grid) = nothing - -function compute_mean_mixed_layer_vertical_diffusivity!(closure, mean_mixed_layer_vertical_diffusivity, mixed_layer_depth, diffusivity_fields, grid) - # this is going to get messy - κ = phytoplankton_diffusivity(closure, diffusivity_fields) - - compute_mixed_layer_mean!(mean_mixed_layer_vertical_diffusivity, mixed_layer_depth, κ, grid) - - return nothing -end - -##### -##### Mean mixed layer light -##### - -compute_mean_mixed_layer_light!(mean_PAR, mixed_layer_depth, PAR, model) = - compute_mixed_layer_mean!(mean_PAR, mixed_layer_depth, PAR, model.grid) - -##### -##### Informaiton about diffusivity fields -##### - -# this does not belong here - lets add them when a particular closure is needed -using Oceananigans.TurbulenceClosures: ScalarDiffusivity, ScalarBiharmonicDiffusivity, VerticalFormulation, ThreeDimensionalFormulation, formulation - -phytoplankton_diffusivity(closure, diffusivity_fields) = - phytoplankton_diffusivity(formulation(closure), closure, diffusivity_fields) - -phytoplankton_diffusivity(closure::Tuple, diffusivity_fields) = - sum(map(n -> phytoplankton_diffusivity(closure[n], diffusivity_fields[n]), 1:length(closure))) - -phytoplankton_diffusivity(formulation, closure, diffusivit_fields) = ZeroField() - -const NotHorizontalFormulation = Union{VerticalFormulation, ThreeDimensionalFormulation} - -phytoplankton_diffusivity(::NotHorizontalFormulation, closure, diffusivity_fields) = - throw(ErrorException("Mean mixed layer vertical diffusivity can not be calculated for $(closure)")) - -phytoplankton_diffusivity(::NotHorizontalFormulation, - closure::Union{ScalarDiffusivity, ScalarBiharmonicDiffusivity}, - diffusivity_fields) = - phytoplankton_diffusivity(closure.κ) - -phytoplankton_diffusivity(diffusivity_field) = diffusivity_field -phytoplankton_diffusivity(diffusivity_field::Number) = ConstantField(diffusivity_field) -phytoplankton_diffusivity(diffusivity_fields::NamedTuple) = phytoplankton_diffusivity(diffusivity_fields.P) -phytoplankton_diffusivity(::Function) = - throw(ErrorException("Can not compute mean mixed layer vertical diffusivity for `Function` type diffusivity, changing to a `FunctionField` would work")) - - diff --git a/src/Models/AdvectedPopulations/PISCES/nitrate_ammonia.jl b/src/Models/AdvectedPopulations/PISCES/nitrate_ammonia.jl deleted file mode 100644 index 87492acc3..000000000 --- a/src/Models/AdvectedPopulations/PISCES/nitrate_ammonia.jl +++ /dev/null @@ -1,109 +0,0 @@ -""" - NitrateAmmonia - -A parameterisation for the evolution of nitrate (`NO₃`) and ammonia (`NH₄`) -where ammonia can be `nitrif`ied into nitrate, nitrate and ammonia are supplied -by the bacterial degredation of dissolved organic matter, and consumed by -phytoplankton. Additionally waste produces ammonia through various means. - -""" -@kwdef struct NitrateAmmonia{FT} - maximum_nitrifcation_rate :: FT = 0.05 / day # 1 / s - maximum_fixation_rate :: FT = 0.013 / day # mmol N / m³ (maybe shouldn't be a rate) - iron_half_saturation_for_fixation :: FT = 0.1 # μmol Fe / m³ - phosphate_half_saturation_for_fixation :: FT = 0.8 # mmol P / m³ - light_saturation_for_fixation :: FT = 50.0 # W / m² -end - -@inline function (nitrogen::NitrateAmmonia)(::Val{:NO₃}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - θ = bgc.nitrogen_redfield_ratio - - nitrif = nitrification(nitrogen, bgc, NH₄, O₂, mixed_layer_PAR) * θ - - remin = oxic_remineralisation(bgc.dissolved_organic_matter, bgc, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, O₂, T, zₘₓₗ, zₑᵤ) * θ - - nanophytoplankton_consumption = nitrate_uptake(bgc.nanophytoplankton, bgc, y, t, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - diatom_consumption = nitrate_uptake(bgc.diatoms, bgc, y, t, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - consumption = (nanophytoplankton_consumption + diatom_consumption) * θ - - return nitrif + remin - consumption # an extra term is present in Aumount 2015 but I suspect it is a typo - # to conserve nitrogen I've dropped some ratios for denit etc, and now have bacterial_degregation go to denit in NO3 and remineralisation in NH4_half_saturation_const_for_DOC_remin - # need to check... -end - -@inline function (nitrogen::NitrateAmmonia)(::Val{:NH₄}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - θ = bgc.nitrogen_redfield_ratio - - nitrif = nitrification(nitrogen, bgc, NH₄, O₂, mixed_layer_PAR) * θ - - respiration_product = inorganic_upper_trophic_respiration_product(bgc.mesozooplankton, M, T) * θ - - microzooplankton_grazing_waste = specific_inorganic_grazing_waste(bgc.microzooplankton, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) * Z - mesozooplankton_grazing_waste = specific_inorganic_grazing_waste(bgc.mesozooplankton, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) * M - - grazing_waste = (microzooplankton_grazing_waste + mesozooplankton_grazing_waste) * θ - - denit = denitrifcation(bgc.dissolved_organic_matter, bgc, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, O₂, T, zₘₓₗ, zₑᵤ) * θ - - nanophytoplankton_consumption = ammonia_uptake(bgc.nanophytoplankton, bgc, y, t, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - diatom_consumption = ammonia_uptake(bgc.diatoms, bgc, y, t, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - consumption = (nanophytoplankton_consumption + diatom_consumption) * θ - - fixation = nitrogen_fixation(nitrogen, bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, PAR) - - # again an extra term is present in Aumount 2015 but I suspect it is a typo - return fixation + respiration_product + grazing_waste + denit - consumption - nitrif -end - -@inline function nitrification(nitrogen, bgc, NH₄, O₂, PAR) - λ = nitrogen.maximum_nitrifcation_rate - - ΔO₂ = anoxia_factor(bgc, O₂) - - return λ * NH₄ / (1 + PAR) * (1 - ΔO₂) -end - -@inline function nitrogen_fixation(nitrogen, bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, PAR) - Nₘ = nitrogen.maximum_fixation_rate - K_Fe = nitrogen.iron_half_saturation_for_fixation - K_PO₄ = nitrogen.phosphate_half_saturation_for_fixation - E = nitrogen.light_saturation_for_fixation - - phyto = bgc.nanophytoplankton - - _, _, _, LN, _, _ = phyto.nutrient_limitation(bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′) - - fixation_limit = ifelse(LN >= 0.8, 0.01, 1 - LN) - - μ = base_production_rate(bgc.nanophytoplankton.growth_rate, T) - - growth_requirment = max(0, μ - 2.15) - - nutrient_limitation = min(Fe / (Fe + K_Fe), PO₄ / (PO₄ + K_PO₄)) - - light_limitation = 1 - exp(-PAR / E) - - return Nₘ * growth_requirment * fixation_limit * nutrient_limitation * light_limitation -end diff --git a/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrate_ammonia.jl b/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrate_ammonia.jl new file mode 100644 index 000000000..cbfd3ccc3 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrate_ammonia.jl @@ -0,0 +1,86 @@ +""" + NitrateAmmonia + +A parameterisation for the evolution of nitrate (`NO₃`) and ammonia (`NH₄`) +where ammonia can be `nitrif`ied into nitrate, nitrate and ammonia are supplied +by the bacterial degredation of dissolved organic matter, and consumed by +phytoplankton. Additionally waste produces ammonia through various means. + +""" +@kwdef struct NitrateAmmonia{FT} + maximum_nitrifcation_rate :: FT = 0.05 / day # 1 / s + maximum_fixation_rate :: FT = 0.013 / day # mmol N / m³ (maybe shouldn't be a rate) + iron_half_saturation_for_fixation :: FT = 0.1 # μmol Fe / m³ + phosphate_half_saturation_for_fixation :: FT = 0.8 # mmol P / m³ + light_saturation_for_fixation :: FT = 50.0 # W / m² +end + +const NitrateAmnmoniaPISCES = PISCES{<:Any, <:Any, <:Any, <:Any, NitrateAmmonia} + +@inline function (bgc::NitrateAmnmoniaPISCES)(i, j, k, grid, val_name::Val{:NO₃}, clock, fields) + θ = bgc.nitrogen_redfield_ratio + + nitrif = nitrifcation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields) + + remineralisation = oxic_remineralisation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + + consumption = uptake(bgc.phytoplankton, val_name, i, j, k, grid, bgc, clock, fields) + + return nitrif + θ * (remineralisation - consumption) +end + +@inline function (bgc::NitrateAmnmoniaPISCES)(i, j, k, grid, val_name::Val{:NH₄}, clock, fields) + θ = bgc.nitrogen_redfield_ratio + + nitrif = nitrifcation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields) + + remineralisation = anoxic_remineralisation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + + consumption = uptake(bgc.phytoplankton, val_name, i, j, k, grid, bgc, clock, fields) + + grazing_waste = inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + upper_trophic_waste = upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + fixation = nitrogen_fixation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields) + + return fixation + θ * (remineralisation + grazing_waste + upper_trophic_waste - consumption) - nitrif +end + +@inline function nitrification(nitrogen, i, j, k, grid, bgc, clock, fields) + λ = nitrogen.maximum_nitrifcation_rate + + O₂ = @inbounds fields.O₂[i, j, k] + PAR = @inbounds fields.mean_mixed_layer_light[i, j, k] + NH₄ = @inbounds fields.NH₄[i, j, k] + + ΔO₂ = anoxia_factor(bgc, O₂) + + return λ * NH₄ / (1 + PAR) * (1 - ΔO₂) +end + +@inline function nitrogen_fixation(nitrogen, i, j, k, grid, bgc, clock, fields) + Nₘ = nitrogen.maximum_fixation_rate + K_Fe = nitrogen.iron_half_saturation_for_fixation + K_PO₄ = nitrogen.phosphate_half_saturation_for_fixation + E = nitrogen.light_saturation_for_fixation + + PAR = @inbounds fields.PAR[i, j, k] + + Fe = @inbounds fields.Fe[i, j, k] + PO₄ = @inbounds fields.PO₄[i, j, k] + + availability_limitation = nitrogen_availability_limitation(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + + fixation_limit = ifelse(availability_limitation >= 0.8, 0.01, 1 - availability_limitation) + + μ = base_production_rate(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + + growth_requirment = max(0, μ - 2.15) + + nutrient_limitation = min(Fe / (Fe + K_Fe), PO₄ / (PO₄ + K_PO₄)) + + light_limitation = 1 - exp(-PAR / E) + + return Nₘ * growth_requirment * fixation_limit * nutrient_limitation * light_limitation +end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrogen.jl b/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrogen.jl new file mode 100644 index 000000000..21daf42d4 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrogen.jl @@ -0,0 +1,12 @@ +module Nitrogen + +export NitrateAmmonia + +using OceanBioME.Models.PISCESModel: anoxia_factor, PISCES +using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: oxic_remineralisation, anoxic_remineralisaiton +using OceanBioME.Models.PISCESModel.Phytoplankton: uptake, nitrogen_availability_limitation, base_production_rate +using OceanBioME.Models.PISCESModel.Zooplankton: upper_trophic_respiration, inorganic_excretion + +include("nitrate_ammonia.jl") + +end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/oxygen.jl b/src/Models/AdvectedPopulations/PISCES/oxygen.jl index 9cb7433b3..c99754857 100644 --- a/src/Models/AdvectedPopulations/PISCES/oxygen.jl +++ b/src/Models/AdvectedPopulations/PISCES/oxygen.jl @@ -1,55 +1,47 @@ -""" - Oxygen +module OxygenModels -Parameterisation for oxygen which is supplied by phyotsynthesis and denitrifcation, -and removed by various respiration terms and nitrifcation. -""" -@kwdef struct Oxygen{FT} - ratio_for_respiration :: FT = 133/122 # mol O₂ / mol C - ratio_for_nitrifcation :: FT = 32/122 # mol O₂ / mol C -end +export Oxygen -@inline function (oxy::Oxygen)(::Val{:O₂}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) +using OceanBioME.Models.PISCESModel: PISCES - θ_resp = oxy.ratio_for_respiration - θ_nitrif = oxy.ratio_for_nitrifcation +using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: + oxic_remineralisation, anoxic_remineralisation - microzooplankton_respiration = specific_inorganic_grazing_waste(bgc.microzooplankton, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) * Z - mesozooplankton_respiration = specific_inorganic_grazing_waste(bgc.mesozooplankton, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) * M +using OceanBioME.Models.PISCESModel.Nitrogen: nitrifcation, nitrogen_fixation - zooplankton_respiration = θ_resp * (microzooplankton_respiration + mesozooplankton_respiration) +using OceanBioME.Models.PISCESModel.Phytoplankton: uptake - upper_trophic_respiration = θ_resp * inorganic_upper_trophic_respiration_product(bgc.mesozooplankton, M, T) +using OceanBioME.Models.PISCESModel.Zooplankton: + inorganic_excretion, upper_trophic_respiration - remin = (θ_resp + θ_nitrif) * oxic_remineralisation(bgc.dissolved_organic_matter, bgc, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, O₂, T, zₘₓₗ, zₑᵤ) - denit = θ_resp * denitrifcation(bgc.dissolved_organic_matter, bgc, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, O₂, T, zₘₓₗ, zₑᵤ) +@kwdef struct Oxygen{FT} + ratio_for_respiration :: FT = 133/122 # mol O₂ / mol C + ratio_for_nitrifcation :: FT = 32/122 # mol O₂ / mol C +end - nitrif = θ_nitrif * nitrification(bgc.nitrogen, bgc, NH₄, O₂, mixed_layer_PAR) - - nanophytoplankton_nitrate_consumption = nitrate_uptake(bgc.nanophytoplankton, bgc, y, t, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) +const PISCESOxygen = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, Oxygen} - diatom_nitrate_consumption = nitrate_uptake(bgc.diatoms, bgc, y, t, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) +@inline function (bgc::PISCESOxygen)(i, j, k, grid, val_name::Val{:O₂}, clock, fields) + θ_resp = oxy.ratio_for_respiration + θ_nitrif = oxy.ratio_for_nitrifcation - nitrate_consumption = (θ_resp + θ_nitrif) * (nanophytoplankton_nitrate_consumption + diatom_nitrate_consumption) + zooplankton = θ_resp * inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) - nanophytoplankton_ammonia_consumption = ammonia_uptake(bgc.nanophytoplankton, bgc, y, t, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) + upper_trophic = θ_resp * upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) - diatom_ammonia_consumption = ammonia_uptake(bgc.diatoms, bgc, y, t, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) + remineralisation = ((θ_resp + θ_nitrif) * oxic_remineralisation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + + θ_resp * anoxic_remineralisaiton(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields)) - ammonia_consumption = θ_resp * (nanophytoplankton_ammonia_consumption + diatom_ammonia_consumption) + ammonia_photosynthesis = θ_resp * uptake(bgc.phytoplankton, Val(:NH₄), i, j, k, grid, bgc, clock, fields) + nitrate_photosynthesis = (θ_resp + θ_nitrif) * uptake(bgc.phytoplankton, Val(:NO₃), i, j, k, grid, bgc, clock, fields) - photosynthesis = nitrate_consumption + ammonia_consumption + # I think (?) that we need the redfield raito here since θ_nitrif is per oxygen + nitrif = θ_nitrif * nitrification(bgc.nitrogen, i, j, k, grid, bgc, clock, fields) / bgc.nitrogen_redfield_ratio - fixation = θ_nitrif * nitrogen_fixation(bgc.nitrogen, bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, PAR) + fixation = θ_nitrif * nitrogen_fixation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields) / bgc.nitrogen_redfield_ratio - return photosynthesis + fixation - zooplankton_respiration - upper_trophic_respiration - nitrif - remin - denit + return (ammonia_photosynthesis + nitrate_photosynthesis + fixation + - zooplankton - upper_trophic - nitrif) end + +end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_carbon.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_carbon.jl deleted file mode 100644 index 2e3ad8b74..000000000 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_carbon.jl +++ /dev/null @@ -1,104 +0,0 @@ -@inline function (poc::TwoCompartementParticulateOrganicMatter)(::Val{:POC}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - grazing_waste = specific_non_assimilated_waste(bgc.microzooplankton, P, D, Z, POC, GOC, T, wPOC, wGOC) * Z - - # mortality terms - R_CaCO₃ = rain_ratio(bgc.calcite, bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, PAR) - - nanophytoplankton_linear_mortality, nanophytoplankton_quadratic_mortality = mortality(bgc.nanophytoplankton, bgc, z, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - - nanophytoplankton_mortality = (1 - 0.5 * R_CaCO₃) * (nanophytoplankton_linear_mortality + nanophytoplankton_quadratic_mortality) - - diatom_linear_mortality, = mortality(bgc.diatoms, bgc, z, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - - diatom_mortality = 0.5 * diatom_linear_mortality - - microzooplankton_mortality = mortality(bgc.microzooplankton, bgc, Z, O₂, T) - - # degredation - λ = specific_degredation_rate(poc, bgc, O₂, T) - - large_particle_degredation = λ * GOC - degredation = λ * POC - - # grazing - microzooplankton_grazing = particulate_grazing(bgc.microzooplankton, P, D, Z, POC, T) * Z - mesozooplankton_grazing = particulate_grazing(bgc.mesozooplankton, P, D, Z, POC, T) * M - - small_flux_feeding = specific_flux_feeding(bgc.mesozooplankton, POC, T, wPOC) * M - - grazing = microzooplankton_grazing + mesozooplankton_grazing + small_flux_feeding - - # aggregation - _, Φ₁, _, Φ₃ = aggregation(bgc.dissolved_organic_matter, bgc, z, DOC, POC, GOC, zₘₓₗ) - dissolved_aggregation = Φ₁ + Φ₃ - - aggregation_to_large = aggregation(poc, bgc, z, POC, GOC, zₘₓₗ) - - total_aggregation = dissolved_aggregation - aggregation_to_large - - return (grazing_waste - + nanophytoplankton_mortality + diatom_mortality + microzooplankton_mortality - + large_particle_degredation + total_aggregation - - grazing - degredation) -end - -@inline function (poc::TwoCompartementParticulateOrganicMatter)(::Val{:GOC}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - grazing_waste = specific_non_assimilated_waste(bgc.mesozooplankton, P, D, Z, POC, GOC, T, wPOC, wGOC) * M - - # mortality terms - R_CaCO₃ = rain_ratio(bgc.calcite, bgc, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, PAR) - - nanophytoplankton_linear_mortality, nanophytoplankton_quadratic_mortality = mortality(bgc.nanophytoplankton, bgc, z, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - - nanophytoplankton_mortality = 0.5 * R_CaCO₃ * (nanophytoplankton_linear_mortality + nanophytoplankton_quadratic_mortality) - - diatom_linear_mortality, diatom_quadratic_mortality = mortality(bgc.diatoms, bgc, z, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - - diatom_mortality = 0.5 * diatom_linear_mortality + diatom_quadratic_mortality - - mesozooplankton_mortality = linear_mortality(bgc.mesozooplankton, bgc, M, O₂, T) - - # degredation - λ = specific_degredation_rate(poc, bgc, O₂, T) - - degredation = λ * GOC - - # grazing - grazing = specific_flux_feeding(bgc.mesozooplankton, GOC, T, wGOC) * M - - # aggregation - _, _, dissolved_aggregation = aggregation(bgc.dissolved_organic_matter, bgc, z, DOC, POC, GOC, zₘₓₗ) - - small_particle_aggregation = aggregation(poc, bgc, z, POC, GOC, zₘₓₗ) - - total_aggregation = dissolved_aggregation + small_particle_aggregation - - # fecal pelet prodiction - fecal_pelet_production = upper_trophic_fecal_product(bgc.mesozooplankton, M, T) - - return (grazing_waste - + nanophytoplankton_mortality + diatom_mortality + mesozooplankton_mortality - + total_aggregation + fecal_pelet_production - - grazing - - degredation) -end diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter.jl deleted file mode 100644 index 0dd3f9ac7..000000000 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter.jl +++ /dev/null @@ -1,49 +0,0 @@ -""" - TwoCompartementParticulateOrganicMatter - -A quota parameterisation for particulate organic matter with two size classes, -each with carbon and iron compartements, and a silicate compartement for the -large size class. - -Confusingly we decided to name these compartmenets `POC` and `GOC` for the small -and large carbon classes, `SFe` and `BFe` for the small and ̶l̶a̶r̶g̶e̶ big iron -compartements, and `PSi` for the ̶l̶a̶r̶g̶e̶ particulate silicon (*not* the -phytoplankton silicon). -""" -@kwdef struct TwoCompartementParticulateOrganicMatter{FT, AP} - temperature_sensetivity :: FT = 1.066 # - base_breakdown_rate :: FT = 0.025 / day # 1 / s -# (1 / (mmol C / m³), 1 / (mmol C / m³), 1 / (mmol C / m³) / s, 1 / (mmol C / m³) / s) - aggregation_parameters :: AP = (25.9, 4452, 3.3, 47.1) .* (10^-6 / day) - minimum_iron_scavenging_rate :: FT = 3e-5/day # 1 / s - load_specific_iron_scavenging_rate :: FT = 0.005/day # 1 / (mmol C / m³) / s - small_fraction_of_bacterially_consumed_iron :: FT = 0.5 # - large_fraction_of_bacterially_consumed_iron :: FT = 0.5 # - base_liable_silicate_fraction :: FT = 0.5 # - fast_dissolution_rate_of_silicate :: FT = 0.025/day # 1 / s - slow_dissolution_rate_of_silicate :: FT = 0.003/day # 1 / s -end - -@inline function specific_degredation_rate(poc::TwoCompartementParticulateOrganicMatter, bgc, O₂, T) - λ₀ = poc.base_breakdown_rate - b = poc.temperature_sensetivity - - ΔO₂ = anoxia_factor(bgc, O₂) - - return λ₀ * b^T * (1 - 0.45 * ΔO₂) -end - -@inline function aggregation(poc::TwoCompartementParticulateOrganicMatter, bgc, z, POC, GOC, zₘₓₗ) - a₁, a₂, a₃, a₄ = poc.aggregation_parameters - - backgroound_shear = bgc.background_shear - mixed_layer_shear = bgc.mixed_layer_shear - - shear = ifelse(z < zₘₓₗ, backgroound_shear, mixed_layer_shear) - - return shear * (a₁ * POC^2 + a₂ * POC * GOC) + a₃ * POC * GOC + a₄ * POC^2 -end - -include("particulate_organic_carbon.jl") -include("iron_in_particles.jl") -include("silicon_in_particles.jl") \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/calcite.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/calcite.jl new file mode 100644 index 000000000..eb0e17661 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/calcite.jl @@ -0,0 +1,52 @@ +@inline function (bgc::PISCES{<:Any, <:Any, <:Any, TwoCompartementCarbonIronParticles})(i, j, k, + grid, + val_name::Val{:CaCO₃}, + clock, fields) + R = rain_ratio(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + phytoplankton_production = calcite_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + + return R * phytoplankton_production - dissolution +end + +@inline function rain_ratio(pom::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) + r = pom.base_rain_ratio + + T = @inbounds fields.T[i, j, k] + PAR = @inbounds fields.PAR[i, j, k] + zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] + + L_CaCO₃ = coccolithophore_nutrient_limitation(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + + phytoplankton_concentration_factor = + coccolithophore_phytoplankton_factor(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + + low_light_factor = max(0, PAR - 1) / (4 + PAR) + high_light_factor = 30 / (30 + PAR) + + # modified from origional as it goes negative and does not achieve goal otherwise + low_temperature_factor = max(0, T / (T + 0.1)) + high_temperature_factor = 1 + exp(-(T - 10)^2 / 25) + + depth_factor = min(1, -50/zₘₓₗ) + + return (r * L_CaCO₃ + * phytoplankton_concentration_factor + * low_light_factor + * high_light_factor + * low_temperature_factor + * high_temperature_factor + * depth_factor) +end + +@inline function calcite_dissolution(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) + λ = poc.base_calcite_dissolution_rate + nca = poc.calcite_dissolution_exponent + + Ω = @inbounds fields.Ω[i, j, k] + CaCO₃ = @inbounds fields.CaCO₃[i, j, k] + + ΔCaCO₃ = max(0, 1 - Ω) + + return λ * ΔCaCO₃ ^ nca * CaCO₃ +end diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl new file mode 100644 index 000000000..6ad86f08f --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl @@ -0,0 +1,56 @@ +# these are just completly different to eachother so not going to try and define a generic function + +@inline function (bgc::PISCES{<:Any, <:Any, <:Any, TwoCompartementCarbonIronParticles})(i, j, k, grid, val_name::Val{:POC}, clock, fields) + # gains + grazing_waste = small_non_assimilated_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + phytoplankton_mortality = small_mortality(bgc.phytoplankon, i, j, k, grid, bgc, clock, fields) + + zooplankton_mortality = small_mortality(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + _, Φ₁, _, Φ₃ = aggregation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + + dissolved_aggregation = Φ₁ + Φ₃ + + large_breakdown = degredation(bgc.particulate_organic_matter, Val(:GOC), i, j, k, grid, bgc, clock, fields) + + # losses + grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields) + + aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + small_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields) + + return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + dissolved_aggregation + large_breakdown + - grazing - aggregation_to_large - small_breakdown) +end + +@inline function (bgc::PISCES{<:Any, <:Any, <:Any, TwoCompartementCarbonIronParticles})(i, j, k, grid, val_name::Val{:GOC}, clock, fields) + # gains + grazing_waste = large_non_assimilated_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + phytoplankton_mortality = large_mortality(bgc.phytoplankon, i, j, k, grid, bgc, clock, fields) + + zooplankton_mortality = large_mortality(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + upper_trophic_feces = upper_trophic_fecal_production(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + # losses + grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields) + + large_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields) + + return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + upper_trophic_feces + - grazing - large_breakdown) +end + +@inline degredation(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) = # for going to DOC + degredation(poc::TwoCompartementCarbonIronParticles, Val(:POC), i, j, k, grid, bgc, clock, fields) + +@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:POC}, i, j, k, grid, bgc, clock, fields) = + @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields) * fields.POC[i, j, k] + +@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:GOC}, i, j, k, grid, bgc, clock, fields) = + @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields) * fields.GOC[i, j, k] \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl new file mode 100644 index 000000000..c8e228d85 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl @@ -0,0 +1,133 @@ + +@inline function (bgc::TwoCompartementPOCPISCES)(i, j, k, grid, val_name::Val{:SFe}, clock, fields) + POC = @inbounds fields.POC[i, j, k] + SFe = @inbounds fields.SFe[i, j, k] + + θ = SFe / (POC + eps(0.0)) + + # gains + grazing_waste = + small_non_assimilated_iron_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + phytoplankton_mortality = + small_mortality_iron(bgc.phytoplankon, i, j, k, grid, bgc, clock, fields) + + zooplankton_mortality = + small_mortality_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + large_breakdown = + degredation(bgc.particulate_organic_matter, Val(:BFe), i, j, k, grid, bgc, clock, fields) + + λFe = iron_scavenging_rate(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields) + + scavenging = λFe * POC * Fe′ + + κ = poc.small_fraction_of_bacterially_consumed_iron + + BactFe = bacterial_iron_uptake(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + bacterial_assimilation = κ * BactFe + + _, colloidal_aggregation = aggregation_of_colloidal_iron(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + + # losses + grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields) * θ + + aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) * θ + + small_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields) + + return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + + large_breakdown + scavenging + bacterial_assimilation + colloidal_aggregation + - grazing - aggregation_to_large - small_breakdown) +end + +@inline function (bgc::TwoCompartementPOCPISCES)(i, j, k, grid, val_name::Val{:BFe}, clock, fields) + POC = @inbounds fields.POC[i, j, k] + SFe = @inbounds fields.SFe[i, j, k] + GOC = @inbounds fields.GOC[i, j, k] + BFe = @inbounds fields.BFe[i, j, k] + + θS = SFe / (POC + eps(0.0)) + θB = BFe / (GOC + eps(0.0)) + + # gains + grazing_waste = large_non_assimilated_iron_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + phytoplankton_mortality = large_mortality_iron(bgc.phytoplankon, i, j, k, grid, bgc, clock, fields) + + zooplankton_mortality = large_mortality_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) * θS + + upper_trophic_feces = upper_trophic_fecal_iron_production(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + λFe = iron_scavenging_rate(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields) + + scavenging = λFe * GOC * Fe′ + + κ = poc.small_fraction_of_bacterially_consumed_iron + + BactFe = bacterial_iron_uptake(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + bacterial_assimilation = κ * BactFe + + _, _, colloidal_aggregation = aggregation_of_colloidal_iron(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + + # losses + grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields) * θB + + large_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields) + + return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + upper_trophic_feces + + scavenging + bacterial_assimilation + colloidal_aggregation + - grazing - large_breakdown) +end + +@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:SFe}, i, j, k, grid, bgc, clock, fields) = + @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields) * fields.SFe[i, j, k] + +@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:BFe}, i, j, k, grid, bgc, clock, fields) = + @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields) * fields.BFe[i, j, k] + +@inline function iron_scavenging_rate(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) + λ₀ = pom.minimum_iron_scavenging_rate + λ₁ = pom.load_specific_iron_scavenging_rate + + POC = @inbounds fields.POC[i, j, k] + GOC = @inbounds fields.GOC[i, j, k] + CaCO₃ = @inbounds fields.CaCO₃[i, j, k] + PSi = @inbounds fields.PSi[i, j, k] + + return λ₀ + λ₁ * (POC + GOC + CaCO₃ + PSi) +end + +@inline function bacterial_iron_uptake(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) + μ₀ = poc.maximum_bacterial_growth_rate + b = poc.temperature_sensetivity + θ = poc.maximum_iron_ratio_in_bacteria + K = poc.iron_half_saturation_for_bacteria + + μ = μ₀ * b^T + + Bact = bacteria_concentration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + LBact = bacteria_activity(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + return μ * LBact * θ * Fe / (Fe + K) * Bact +end + +@inline function iron_scavenging(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) + POC = @inbounds fields.POC[i, j, k] + GOC = @inbounds fields.GOC[i, j, k] + + λFe = iron_scavenging_rate(poc, i, j, k, grid, bgc, clock, fields) + + Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields) + + return λFe * (POC + GOC) * Fe′ +end diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl new file mode 100644 index 000000000..0e02a8bd8 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl @@ -0,0 +1,29 @@ +using OceanBioME.Models.PISCESModel.Zooplankton: non_assimilated_waste + +@inline small_non_assimilated_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + non_assimilated_waste(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) + +@inline large_non_assimilated_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + non_assimilated_waste(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) + +@inline small_non_assimilated_iron_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + non_assimilated_iron_waste(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) + +@inline large_non_assimilated_iron_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + non_assimilated_iron_waste(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) + +@inline small_mortality(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + mortality(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) + +@inline large_mortality(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + linear_mortality(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) + +@inline small_mortality_iron(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + mortality(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) * zoo.micro.iron_ratio + +@inline large_mortality_iron(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + linear_mortality(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) * zoo.meso.iron_ratio + +@inline total_grazing(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields) = + (grazing(zoo, val_prey_name, i, j, k, grid, bgc, clock, fields) + + flux_feeding(zoo, val_prey_name, i, j, k, grid, bgc, clock, fields)) diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl new file mode 100644 index 000000000..55f644fb0 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl @@ -0,0 +1,90 @@ +@inline function small_mortality(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) + P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) + + R = rain_ratio(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + D_linear_mortality, = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields) + + return (1 - R / 2) * (P_linear_mortality + P_quadratic_mortality) + D_linear_mortality / 2 +end + +@inline function large_mortality(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) + P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) + + R = rain_ratio(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + D_linear_mortality, D_quadratic_mortality = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields) + + return R / 2 * (P_linear_mortality + P_quadratic_mortality) + D_linear_mortality / 2 + D_quadratic_mortality +end + +@inline function small_mortality_iron(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) + P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) + + R = rain_ratio(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + D_linear_mortality, = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields) + + P = @inbounds fields.P[i, j, k] + PFe = @inbounds fields.PFe[i, j, k] + D = @inbounds fields.D[i, j, k] + DFe = @inbounds fields.DFe[i, j, k] + + θP = PFe / (P + eps(0.0)) + θD = DFe / (D + eps(0.0)) + + return (1 - R / 2) * (P_linear_mortality + P_quadratic_mortality) * θP + D_linear_mortality * θD / 2 +end + +@inline function large_mortality_iron(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) + P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) + + R = rain_ratio(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + D_linear_mortality, D_quadratic_mortality = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields) + + P = @inbounds fields.P[i, j, k] + PFe = @inbounds fields.PFe[i, j, k] + D = @inbounds fields.D[i, j, k] + DFe = @inbounds fields.DFe[i, j, k] + + θP = PFe / (P + eps(0.0)) + θD = DFe / (D + eps(0.0)) + + return R / 2 * (P_linear_mortality + P_quadratic_mortality) * θP + (D_linear_mortality / 2 + D_quadratic_mortality) * θD +end + +@inline function coccolithophore_nutrient_limitation(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) + _, _, L_PO₄, LN = phyto.nano(Val(:P), phyto.nano, i, j, k, grid, bgc, clock, fields) + + L_Fe = Fe / (Fe + 0.05) + + # from NEMO we should replace LPO₄ with : zlim2 = trb(ji,jj,jk,jppo4) / ( trb(ji,jj,jk,jppo4) + concnnh4 ) + # but that has to be a typo + + return min(LN, L_Fe, L_PO₄) +end + +@inline coccolithophore_phytoplankton_factor(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) = + @inbounds max(one(grid), fields.P[i, j, k] / 2) + +@inline function particulate_silicate_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) + D = @inbounds fields.D[i, j, k] + DSi = @inbounds fields.DSi[i, j, k] + + θ = DSi / (D + eps(0.0)) + + D_linear_mortality, D_quadratic_mortality = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields) + + total_grazing = grazing(bgc.zooplankton, Val(:D), i, j, k, grid, bgc, clock, fields) + + return (total_grazing + D_linear_mortality + D_quadratic_mortality) * θ +end + +@inline function calcite_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) + linear_mortality, quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) + + total_grazing_loss = calcite_loss(bgc.zooplankton, Val(:P), i, j, k, grid, bgc, clock, fields) + + return total_grazing_loss + (linear_mortality + quadratic_mortality) / 2 +end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/particulate_organic_matter.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/particulate_organic_matter.jl new file mode 100644 index 000000000..bc5bfad5f --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/particulate_organic_matter.jl @@ -0,0 +1,18 @@ +module ParticulateOrganicMatter + +export TwoCompartementCarbonIronParticles + +using OceanBioME.Models.PISCESModel: degredation, aggregation, free_iron, PISCES +using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: aggregation_of_colloidal_iron +using OceanBioME.Models.PISCESModel.Phytoplankton: dissolved_exudate, NanoAndDiatoms +using OceanBioME.Models.PISCESModel.Zooplankton: + organic_excretion, upper_trophic_excretion, grazing, MicroAndMeso, upper_trophic_fecal_production, + upper_trophic_fecal_iron_production, calcite_loss + +import Oceananigans.Biogeochemistry: required_biogeochemical_tracers, biogeochemical_drift_velocity +import OceanBioME.Models.PISCESModel: degredation, aggregation +import OceanBioME.Models.PISCESModel.Zooplankton: edible_flux_rate, edible_iron_flux_rate + +include("two_size_class.jl") + +end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/silicate.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/silicate.jl new file mode 100644 index 000000000..5272fdcaf --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/silicate.jl @@ -0,0 +1,47 @@ +@inline function (bgc::PISCES{<:Any, <:Any, <:Any, TwoCompartementCarbonIronParticles})(i, j, k, grid, val_name::Val{:PSi}, clock, fields) + # this generalisation still assumes that we're only getting PSi from phytoplankton being grazed, will need changes if zooplankton get Si compartment + + phytoplankton_production = particulate_silicate_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + + dissolution = particulate_silicate_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + return phytoplankton_production - dissolution +end + +@inline function particulate_silicate_dissolution(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) + PSi = @inbounds fields.PSi[i, j, k] + Si = @inbounds fields.Si[i, j, k] + + T = @inbounds fields.T[i, j, k] + + λₗ = poc.fast_dissolution_rate_of_silicate + λᵣ = poc.slow_dissolution_rate_of_silicate + + χ = particulate_silicate_liable_fraction(poc, i, j, k, grid, bgc, clock, fields) + + λ₀ = χ * λₗ + (1 - χ) * λᵣ + + equilibrium_silicate = 10^(6.44 - 968 / (T + 273.15)) + silicate_saturation = (equilibrium_silicate - Si) / equilibrium_silicate + + λ = λ₀ * (0.225 * (1 + T/15) * silicate_saturation + 0.775 * ((1 + T/400)^4 * silicate_saturation)^9) + + return λ * PSi # assuming the Diss_Si is typo in Aumont 2015, consistent with Aumont 2005 +end + +@inline function particulate_silicate_liable_fraction(poc, i, j, k, grid, bgc, clock, fields) + χ₀ = poc.base_liable_silicate_fraction + λₗ = poc.fast_dissolution_rate_of_silicate + λᵣ = poc.slow_dissolution_rate_of_silicate + + z = znode(i, j, k, grid, Center(), Center(), Center()) + + zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] + zₑᵤ = @inbounds fields.zₑᵤ[i, j, k] + + wGOC = ℑzᵃᵃᶜ(i, j, k, grid, fields.wGOC) # this isn't actually correct since wGOC isn't constant but nm + + zₘ = min(zₘₓₗ, zₑᵤ) + + return χ₀ * ifelse(z >= zₘ, 1, exp((λₗ - λᵣ) * (zₘ - z) / wGOC)) +end diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl new file mode 100644 index 000000000..64de5d09e --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl @@ -0,0 +1,95 @@ +using Oceananigans.Operators: ℑzᵃᵃᶜ + +""" + TwoCompartementCarbonIronParticles + +A quota parameterisation for particulate organic matter with two size classes, +each with carbon and iron compartements, and a silicate compartement for the +large size class. + +Confusingly we decided to name these compartmenets `POC` and `GOC` for the small +and large carbon classes, `SFe` and `BFe` for the small and ̶l̶a̶r̶g̶e̶ big iron +compartements, and `PSi` for the ̶l̶a̶r̶g̶e̶ particulate silicon (*not* the +phytoplankton silicon). +""" +@kwdef struct TwoCompartementCarbonIronParticles{FT, AP} + temperature_sensetivity :: FT = 1.066 # + base_breakdown_rate :: FT = 0.025 / day # 1 / s +# (1 / (mmol C / m³), 1 / (mmol C / m³), 1 / (mmol C / m³) / s, 1 / (mmol C / m³) / s) + aggregation_parameters :: AP = (25.9, 4452, 3.3, 47.1) .* (10^-6 / day) + minimum_iron_scavenging_rate :: FT = 3e-5/day # 1 / s + load_specific_iron_scavenging_rate :: FT = 0.005/day # 1 / (mmol C / m³) / s + small_fraction_of_bacterially_consumed_iron :: FT = 0.5 # + large_fraction_of_bacterially_consumed_iron :: FT = 0.5 # + base_liable_silicate_fraction :: FT = 0.5 # + fast_dissolution_rate_of_silicate :: FT = 0.025/day # 1 / s + slow_dissolution_rate_of_silicate :: FT = 0.003/day # 1 / s + + # calcite + base_rain_ratio :: FT = 0.3 # + base_calcite_dissolution_rate :: FT = 0.197 / day # 1 / s + calcite_dissolution_exponent :: FT = 1.0 # + + # iron in particles + maximum_iron_ratio_in_bacteria :: FT = 0.06 # μmol Fe / mmol C + iron_half_saturation_for_bacteria :: FT = 0.3 # μmol Fe / m³ + maximum_bacterial_growth_rate :: FT = 0.6 / day # 1 / s +end + +const TwoCompartementPOCPISCES = PISCES{<:Any, <:Any, <:Any, TwoCompartementCarbonIronParticles} + +required_biogeochemical_tracers(::TwoCompartementCarbonIronParticles) = (:POC, :GOC, :SFe, :BFe, :PSi, :CaCO₃) + +@inline edible_flux_rate(poc, i, j, k, grid, fields) = flux_rate(Val(:POC), i, j, k, grid, fields) + flux_rate(Val(:GOC), i, j, k, grid, fields) +@inline edible_iron_flux_rate(poc, i, j, k, grid, fields) = flux_rate(Val(:SFe), i, j, k, grid, fields) + flux_rate(Val(:BFe), i, j, k, grid, fields) + +@inline flux_rate(::Val{:POC}, i, j, k, grid, fields) = @inbounds fields.POC[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, fields.wPOC) +@inline flux_rate(::Val{:GOC}, i, j, k, grid, fields) = @inbounds fields.GOC[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, fields.wGOC) +@inline flux_rate(::Val{:SFe}, i, j, k, grid, fields) = @inbounds fields.SFe[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, fields.wPOC) +@inline flux_rate(::Val{:BFe}, i, j, k, grid, fields) = @inbounds fields.BFe[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, fields.wGOC) + +const small_particle_components = Union{Val{:POC}, Val{:SFe}} +const large_particle_components = Union{Val{:GOC}, Val{:BFe}, Val{:PSi}, Val{:CaCO₃}} + +biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::small_particle_components) = + (u = ZeroField(), v = ZeroField(), w = bgc.sinking_velocities.POC) + +biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::large_particle_components) = + (u = ZeroField(), v = ZeroField(), w = bgc.sinking_velocities.GOC) + +@inline function aggregation(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) + a₁, a₂, a₃, a₄ = poc.aggregation_parameters + + backgroound_shear = bgc.background_shear + mixed_layer_shear = bgc.mixed_layer_shear + + + z = znode(i, j, k, grid, Center(), Center(), Center()) + + zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] + + POC = @inbounds fields.POC[i, j, k] + GOC = @inbounds fields.GOC[i, j, k] + + shear = ifelse(z < zₘₓₗ, backgroound_shear, mixed_layer_shear) + + return shear * (a₁ * POC^2 + a₂ * POC * GOC) + a₃ * POC * GOC + a₄ * POC^2 +end + +@inline function specific_degredation_rate(poc::TwoCompartementParticulateOrganicMatter, i, j, k, grid, bgc, clock, fields) + λ₀ = poc.base_breakdown_rate + b = poc.temperature_sensetivity + + O₂ = @inbounds fields.O₂[i, j, k] + + ΔO₂ = anoxia_factor(bgc, O₂) + + return λ₀ * b^T * (1 - 0.45 * ΔO₂) +end + +include("carbon.jl") +include("iron.jl") +include("silicate.jl") +include("calcite.jl") +include("nano_diatom_coupling.jl") +include("micro_meso_zoo_coupling.jl") \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/phosphate.jl b/src/Models/AdvectedPopulations/PISCES/phosphate.jl new file mode 100644 index 000000000..26b870455 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/phosphate.jl @@ -0,0 +1,31 @@ +module Phosphates + +export Phosphate + +using OceanBioME.Models.PISCESModel: PISCES + +using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: degredation + +using OceanBioME.Models.PISCESModel.Phytoplankton: total_production + +using OceanBioME.Models.PISCESModel.Zooplankton: inorganic_excretion, upper_trophic_respiration + +struct Phosphate end + +const PISCESPhosphate = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, Phosphate} + +@inline function (bgc::PISCESPhosphate)(i, j, k, grid, val_name::Val{:PO₄}, clock, fields) + θ = bgc.phosphate_redfield_ratio + + phytoplankton_uptake = total_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + + grazing_waste = inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + respiration_product = upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + + remineralisation = degredation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + + return θ * (grazing_waste + respiration_product + remineralisation - phytoplankton_uptake) +end + +end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/phosphates.jl b/src/Models/AdvectedPopulations/PISCES/phosphates.jl deleted file mode 100644 index b75b8fefc..000000000 --- a/src/Models/AdvectedPopulations/PISCES/phosphates.jl +++ /dev/null @@ -1,39 +0,0 @@ -""" - Phosphate - -Evolution of phosphate (PO₄). -""" -struct Phosphate end - -@inline function (phosphate::Phosphate)(::Val{:PO₄}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - θ = bgc.phosphate_redfield_ratio - - microzooplankton_grazing_waste = specific_inorganic_grazing_waste(bgc.microzooplankton, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) * Z - mesozooplankton_grazing_waste = specific_inorganic_grazing_waste(bgc.mesozooplankton, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) * M - - grazing_waste = microzooplankton_grazing_waste + mesozooplankton_grazing_waste - - respiration_product = inorganic_upper_trophic_respiration_product(bgc.mesozooplankton, M, T) - - remineralisation = oxic_remineralisation(bgc.dissolved_organic_matter, bgc, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, O₂, T, zₘₓₗ, zₑᵤ) - - denit = denitrifcation(bgc.dissolved_organic_matter, bgc, z, Z, M, DOC, NO₃, NH₄, PO₄, Fe, O₂, T, zₘₓₗ, zₑᵤ) - - nanophytoplankton_consumption, = total_production(bgc.nanophytoplankton, bgc, y, t, P, PChl, PFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - diatom_consumption, = total_production(bgc.diatoms, bgc, y, t, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - consumption = (nanophytoplankton_consumption + diatom_consumption) - - return θ * (grazing_waste + respiration_product + remineralisation + denit - consumption) -end diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton.jl deleted file mode 100644 index a794f540e..000000000 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton.jl +++ /dev/null @@ -1,296 +0,0 @@ -include("base_production.jl") -include("nutrient_limitation.jl") - -""" - MixedMondoPhytoplankton - -Holds the parameters for the PISCES mixed mondo phytoplankton -parameterisation where nutrient limitation is modelled using the -mondo approach for nitrate (NO₃), ammonia (NH₄), phosphate (PO₄), -and silicate (Si), but the quota approach is used for iron (Fe) -and light (PAR). - -Therefore each class has a carbon compartement (generically `I`), -chlorophyll (`IChl`), and iron (`IFe`), and may also have silicate -(`ISi`) if the `nutrient_limitation` specifies that the growth is -silicate limited, despite the fact that the silicate still limits -the growth in a mondo fashion. - -The `growth_rate` may be different parameterisations, currently -either `NutrientLimitedProduction` or -`GrowthRespirationLimitedProduction`, which represent the typical -and `newprod` versions of PISCES. -""" -@kwdef struct MixedMondoPhytoplankton{GR, NL, FT} - growth_rate :: GR - nutrient_limitation :: NL - - exudated_fracton :: FT = 0.05 # - - blue_light_absorption :: FT # - green_light_absorption :: FT # - red_light_absorption :: FT # - - mortality_half_saturation :: FT = 0.2 # mmol C / m³ - linear_mortality_rate :: FT = 0.01 / day # 1 / s - - base_quadratic_mortality :: FT = 0.01 / day # 1 / s / (mmol C / m³) - maximum_quadratic_mortality :: FT # 1 / s / (mmol C / m³) - zero for nanophytoplankton - - minimum_chlorophyll_ratio :: FT = 0.0033 # mg Chl / mg C - maximum_chlorophyll_ratio :: FT # mg Chl / mg C - - maximum_iron_ratio :: FT = 0.06 # μmol Fe / mmol C - - silicate_half_saturation :: FT = 2.0 # mmol Si / m³ - enhanced_silicate_half_saturation :: FT = 20.9 # mmol Si / m³ - optimal_silicate_ratio :: FT = 0.159 # mmol Si / mmol C -end - -@inline phytoplankton_concentration(::NANO_PHYTO, P, D) = P -@inline phytoplankton_concentration(::DIATOMS, P, D) = D - -@inline phytoplankton_grazing(::NANO_PHYTO, args...) = nanophytoplankton_grazing(args...) -@inline phytoplankton_grazing(::DIATOMS, args...) = diatom_grazing(args...) - -@inline function (phyto::MixedMondoPhytoplankton)(val_name::Union{Val{:P}, Val{:D}}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, # we should get rid of DSi and the rest of the Si since it doesn't do anything... - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - # production - δ = phyto.exudated_fracton - - I = phytoplankton_concentration(val_name, P, D) - IChl = phytoplankton_concentration(val_name, PChl, DChl) - IFe = phytoplankton_concentration(val_name, PFe, DFe) - - μI, L = total_production(phyto, bgc, y, t, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - production = (1 - δ) * μI - - # mortality - linear_mortality, quadratic_mortality = mortality(phyto, bgc, z, I, zₘₓₗ, L) - - # grazing - gZ = phytoplankton_grazing(val_name, bgc.microzooplankton, P, D, Z, POC, T) - gM = phytoplankton_grazing(val_name, bgc.mesozooplankton, P, D, Z, POC, T) - - grazing = gZ * Z + gM * M - - return production - linear_mortality - quadratic_mortality - grazing -end - -@inline function (phyto::MixedMondoPhytoplankton)(val_name::Union{Val{:PChl}, Val{:DChl}}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, # we should get rid of DSi and the rest of the Si since it doesn't do anything... - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - I = phytoplankton_concentration(val_name, P, D) - IChl = phytoplankton_concentration(val_name, PChl, DChl) - IFe = phytoplankton_concentration(val_name, PFe, DFe) - - # production - δ = phyto.exudated_fracton - - θ₀ = phyto.minimum_chlorophyll_ratio - θ₁ = phyto.maximum_chlorophyll_ratio - - L, = phyto.nutrient_limitation(bgc, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′) - - μ, ρ = production_and_energy_assimilation_absorption_ratio(phyto.growth_rate, phyto, bgc, y, t, I, IChl, T, zₘₓₗ, zₑᵤ, κ, PAR, PAR₁, PAR₂, PAR₃, L) - - production = (1 - δ) * 12 * (θ₀ + (θ₁ - θ₀) * ρ) * μ * I - - # mortality - θChl = IChl / (12 * I + eps(0.0)) - - linear_mortality, quadratic_mortality = mortality(phyto, bgc, z, I, zₘₓₗ, L) - - linear_mortality *= θChl * 12 - quadratic_mortality *= θChl * 12 - - # grazing - - gZ = phytoplankton_grazing(val_name, bgc.microzooplankton, P, D, Z, POC, T) - gM = phytoplankton_grazing(val_name, bgc.mesozooplankton, P, D, Z, POC, T) - - grazing = (gZ * Z + gM * M) * θChl * 12 - - return production - linear_mortality - quadratic_mortality - grazing -end - -@inline function (phyto::MixedMondoPhytoplankton)(val_name::Union{Val{:PFe}, Val{:DFe}}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - I = phytoplankton_concentration(val_name, P, D) - IChl = phytoplankton_concentration(val_name, PChl, DChl) - IFe = phytoplankton_concentration(val_name, PFe, DFe) - - # production - production, L = iron_uptake(phyto, bgc, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T) - - # mortality - linear_mortality, quadratic_mortality = mortality(phyto, bgc, z, I, zₘₓₗ, L) - - linear_mortality *= IFe / (I + eps(0.0)) - quadratic_mortality *= IFe / (I + eps(0.0)) - - # grazing - gZ = phytoplankton_grazing(val_name, bgc.microzooplankton, P, D, Z, POC, T) - gM = phytoplankton_grazing(val_name, bgc.mesozooplankton, P, D, Z, POC, T) - - grazing = (gZ * Z + gM * M) * IFe / (I + eps(0.0)) - - return production - linear_mortality - quadratic_mortality - grazing -end - -@inline function iron_uptake(phyto::MixedMondoPhytoplankton, bgc, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T) - δ = phyto.exudated_fracton - θFeₘ = phyto.maximum_iron_ratio - - θFe = IFe / (I + eps(0.0)) # μmol Fe / mmol C - - L, LFe = phyto.nutrient_limitation(bgc, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′) - - μᵢ = base_production_rate(phyto.growth_rate, T) - - L₁ = iron_uptake_limitation(phyto.nutrient_limitation, I, Fe) # assuming bFe = Fe - - L₂ = 4 - 4.5 * LFe / (LFe + 1) # typo in Aumount 2015 - - return (1 - δ) * θFeₘ * L₁ * L₂ * max(0, (1 - θFe / θFeₘ) / (1.05 - θFe / θFeₘ)) * μᵢ * I, L -end - -@inline function (phyto::MixedMondoPhytoplankton)(::Val{:DSi}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - # production - production, L = silicate_uptake(phyto, bgc, y, t, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - # mortality - linear_mortality, quadratic_mortality = mortality(phyto, bgc, z, D, zₘₓₗ, L) - - linear_mortality *= DSi / (D + eps(0.0)) - quadratic_mortality *= DSi / (D + eps(0.0)) - - # grazing - gZ = diatom_grazing(bgc.microzooplankton, P, D, Z, POC, T) - gM = diatom_grazing(bgc.mesozooplankton, P, D, Z, POC, T) - - grazing = (gZ * Z + gM * M) * DSi / (D + eps(0.0)) - - return production - linear_mortality - quadratic_mortality - grazing -end - -@inline function silicate_uptake(phyto::MixedMondoPhytoplankton, bgc, y, t, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - δ = phyto.exudated_fracton - - K₁ = phyto.silicate_half_saturation - K₂ = phyto.enhanced_silicate_half_saturation - θ₀ = phyto.optimal_silicate_ratio - - L, LFe, LPO₄, LN = phyto.nutrient_limitation(bgc, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, Si′) - - μ = phyto.growth_rate(phyto, bgc, y, t, D, DChl, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃, L) - - μᵢ = base_production_rate(phyto.growth_rate, T) - - L₁ = Si / (Si + K₁ + eps(0.0)) - - # enhanced silication in southern ocean - φ = bgc.latitude(y) - - L₂ = ifelse(φ < 0, Si^3 / (Si^3 + K₂^3), 0) - - F₁ = min(μ / (μᵢ * L + eps(0.0)), LFe, LPO₄, LN) - - F₂ = min(1, 2.2 * max(0, L₁ - 0.5)) - - θ₁ = θ₀ * L₁ * min(5.4, (4.4 * exp(-4.23 * F₁) * F₂ + 1) * (1 + 2 * L₂)) - - return (1 - δ) * θ₁ * μ * D, L -end - -@inline function dissolved_exudate(phyto::MixedMondoPhytoplankton, bgc, y, t, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - δ = phyto.exudated_fracton - - μI, = total_production(phyto, bgc, y, t, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - return δ * μI -end - -@inline function mortality(phyto::MixedMondoPhytoplankton, bgc, z, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - L, = phyto.nutrient_limitation(bgc, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′) - - return mortality(phyto, bgc, z, I, zₘₓₗ, L) -end - -@inline function mortality(phyto::MixedMondoPhytoplankton, bgc, z, I, zₘₓₗ, L) - K = phyto.mortality_half_saturation - m = phyto.linear_mortality_rate - - background_shear = bgc.background_shear - mixed_layer_shear = bgc.mixed_layer_shear - - linear_mortality = m * I / (I + K) * I - - w₀ = phyto.base_quadratic_mortality - w₁ = phyto.maximum_quadratic_mortality - - w = w₀ + w₁ * 0.25 * (1 - L^2) / (0.25 + L^2) - - shear = ifelse(z < zₘₓₗ, background_shear, mixed_layer_shear) - - quadratic_mortality = shear * w * I^2 - - return linear_mortality, quadratic_mortality -end - -@inline function nitrate_uptake(phyto::MixedMondoPhytoplankton, bgc, y, t, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - L, _, _, LN, L_NO₃ = phyto.nutrient_limitation(bgc, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′) - - μ = phyto.growth_rate(phyto, bgc, y, t, I, IChl, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃, L) * I - - return μ * L_NO₃ / (LN + eps(0.0)) -end - -@inline function ammonia_uptake(phyto::MixedMondoPhytoplankton, bgc, y, t, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - L, _, _, LN, _, L_NH₄ = phyto.nutrient_limitation(bgc, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′) - - μ = phyto.growth_rate(phyto, bgc, y, t, I, IChl, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃, L) * I - - return μ * L_NH₄ / (LN + eps(0.0)) -end - -@inline function total_production(phyto::MixedMondoPhytoplankton, bgc, y, t, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, T, Si′, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - L, = phyto.nutrient_limitation(bgc, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′) - - return phyto.growth_rate(phyto, bgc, y, t, I, IChl, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃, L) * I, L -end diff --git a/src/Models/AdvectedPopulations/PISCES/base_production.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/growth_rate.jl similarity index 79% rename from src/Models/AdvectedPopulations/PISCES/base_production.jl rename to src/Models/AdvectedPopulations/PISCES/phytoplankton/growth_rate.jl index 96a470b21..63e0625bc 100644 --- a/src/Models/AdvectedPopulations/PISCES/base_production.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/growth_rate.jl @@ -1,20 +1,29 @@ abstract type BaseProduction end -@inline function (μ::BaseProduction)(phyto, bgc, y, t, I, IChl, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃, L) +@inline function (μ::BaseProduction)(val_name, i, j, k, grid, bgc, clock, fields, L) bₜ = μ.temperature_sensetivity μ₀ = μ.base_growth_rate - α₀ = μ.initial_slope_of_PI_curve - β = μ.low_light_adaptation dark_tollerance = μ.dark_tollerance β₁ = phyto.blue_light_absorption β₂ = phyto.green_light_absorption β₃ = phyto.red_light_absorption + + PAR₁ = @inbounds fields.PAR₁[i, j, k] + PAR₂ = @inbounds fields.PAR₂[i, j, k] + PAR₃ = @inbounds fields.PAR₃[i, j, k] + + zₑᵤ = @inbounds fields.zₑᵤ[i, j, k] + zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] + + I, IChl, IFe = phytoplankton_concentration(val_name, i, j, k, fields) + + T = @inbounds fields.T[i, j, k] PAR = β₁ * PAR₁ + β₂ * PAR₂ + β₃ * PAR₃ - φ = bgc.latitude(y) + φ = bgc.latitude(i, j, k, grid) day_length = bgc.day_length(φ, t) dark_residence_time = max(0, zₑᵤ - zₘₓₗ) ^ 2 / κ @@ -29,13 +38,9 @@ abstract type BaseProduction end α = α₀ * (1 + β * exp(-PAR)) - return μᵢ * f₁ * f₂ * light_limitation(μ, I, IChl, T, PAR, day_length, L, α) * L -end + fₗ = light_limitation(μ, I, IChl, T, PAR, day_length, L, α) -@inline function (μ::BaseProduction)(phyto, bgc, y, t, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - L, = phyto.nutrient_limitation(bgc, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′) - - return μ(phyto, bgc, y, t, I, IChl, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃, L) + return μᵢ * f₁ * f₂ * fₗ * L end """ @@ -115,20 +120,32 @@ end return 1 - exp(-α * θ * PAR / (day_length * (bᵣ + μᵣ))) end -@inline function production_and_energy_assimilation_absorption_ratio(growth_rate, phyto, bgc, y, t, I, IChl, T, zₘₓₗ, zₑᵤ, κ, PAR, PAR₁, PAR₂, PAR₃, L) +@inline function production_and_energy_assimilation_absorption_ratio(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) α₀ = growth_rate.initial_slope_of_PI_curve β = growth_rate.low_light_adaptation + β₁ = phyto.blue_light_absorption + β₂ = phyto.green_light_absorption + β₃ = phyto.red_light_absorption + + I, IChl, IFe = phytoplankton_concentration(val_name, i, j, k, fields) + + PAR₁ = @inbounds fields.PAR₁[i, j, k] + PAR₂ = @inbounds fields.PAR₂[i, j, k] + PAR₃ = @inbounds fields.PAR₃[i, j, k] - α = α₀ * (1 + β * exp(-PAR)) + PAR = β₁ * PAR₁ + β₂ * PAR₂ + β₃ * PAR₃ - φ = bgc.latitude(y) + φ = bgc.latitude(i, j, k, grid) day_length = bgc.day_length(φ, t) f₁ = 1.5 * day_length / (day_length + 0.5day) - μ = growth_rate(phyto, bgc, y, t, I, IChl, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃, L) + μ = growth_rate(val_name, i, j, k, grid, bgc, clock, fields, L) + μ̌ = μ / f₁ * day_length + α = α₀ * (1 + β * exp(-PAR)) + return μ, 12 * μ̌ * I / (α * IChl * PAR + eps(0.0)) * L # (1 / s, unitless) end diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl new file mode 100644 index 000000000..df014d14f --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl @@ -0,0 +1,229 @@ +""" + MixedMondo + +Holds the parameters for the PISCES mixed mondo phytoplankton +parameterisation where nutrient limitation is modelled using the +mondo approach for nitrate (NO₃), ammonia (NH₄), phosphate (PO₄), +and silicate (Si), but the quota approach is used for iron (Fe) +and light (PAR). + +Therefore each class has a carbon compartement (generically `I`), +chlorophyll (`IChl`), and iron (`IFe`), and may also have silicate +(`ISi`) if the `nutrient_limitation` specifies that the growth is +silicate limited, despite the fact that the silicate still limits +the growth in a mondo fashion. + +The `growth_rate` may be different parameterisations, currently +either `NutrientLimitedProduction` or +`GrowthRespirationLimitedProduction`, which represent the typical +and `newprod` versions of PISCES. +""" +@kwdef struct MixedMondo{GR, NL, FT} + growth_rate :: GR + nutrient_limitation :: NL + + exudated_fracton :: FT = 0.05 # + + blue_light_absorption :: FT # + green_light_absorption :: FT # + red_light_absorption :: FT # + + mortality_half_saturation :: FT = 0.2 # mmol C / m³ + linear_mortality_rate :: FT = 0.01 / day # 1 / s + + base_quadratic_mortality :: FT = 0.01 / day # 1 / s / (mmol C / m³) + maximum_quadratic_mortality :: FT # 1 / s / (mmol C / m³) - zero for nanophytoplankton + + minimum_chlorophyll_ratio :: FT = 0.0033 # mg Chl / mg C + maximum_chlorophyll_ratio :: FT # mg Chl / mg C + + maximum_iron_ratio :: FT = 0.06 # μmol Fe / mmol C + + silicate_half_saturation :: FT = 2.0 # mmol Si / m³ + enhanced_silicate_half_saturation :: FT = 20.9 # mmol Si / m³ + optimal_silicate_ratio :: FT = 0.159 # mmol Si / mmol C + + half_saturation_for_iron_uptake :: FT # μmol Fe / m³ + + threshold_for_size_dependency :: FT = 1.0 # mmol C / m³ + size_ratio :: FT = 3.0 # +end + +required_biogeochemical_tracers(phyto::MixedMondo, base) = + (base, Symbol(base, :Chl), Symbol(base, :Fe), + ifelse(phyto.nutrient_limitation.silicate_limited, (Symbol(base, :Si), ), ())...) + +##### +##### Production/mortality functions +##### + +@inline function carbon_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) + I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, kfields) + + # production + δ = phyto.exudated_fracton + + μI = total_production(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + return (1 - δ) * μI +end + +@inline function chlorophyll_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) + I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) + + # production + δ = phyto.exudated_fracton + + θ₀ = phyto.minimum_chlorophyll_ratio + θ₁ = phyto.maximum_chlorophyll_ratio + + L, = phyto.nutrient_limitation(val_name, i, j, k, grid, bgc, clock, fields) + + μ, ρ = production_and_energy_assimilation_absorption_ratio(phyto.growth_rate, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + return (1 - δ) * 12 * (θ₀ + (θ₁ - θ₀) * ρ) * μ * I +end + +# production (we already account for the (1 - δ) term because it just goes straight back into Fe) +@inline iron_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) = + iron_uptake(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + +@inline silicate_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) = + silicate_uptake(phyto, val_name, i, j, k, grid, bgc, clock, fields) + +##### +##### Underlying parameterisations +##### + +@inline function mortality(phyto::MixedMondo, bgc, z, I, zₘₓₗ, L) + K = phyto.mortality_half_saturation + m = phyto.linear_mortality_rate + + background_shear = bgc.background_shear + mixed_layer_shear = bgc.mixed_layer_shear + + linear_mortality = m * I / (I + K) * I + + w₀ = phyto.base_quadratic_mortality + w₁ = phyto.maximum_quadratic_mortality + + w = w₀ + w₁ * 0.25 * (1 - L^2) / (0.25 + L^2) + + shear = ifelse(z < zₘₓₗ, background_shear, mixed_layer_shear) + + quadratic_mortality = shear * w * I^2 + + return linear_mortality, quadratic_mortality +end + +@inline function mortality(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) + I, = phytoplankton_concentration(val_name, i, j, k, fields) + zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] + + z = znode(i, j, k, grid, Center(), Center(), Center()) + + L, = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) + + return mortality(phyto, bgc, z, I, zₘₓₗ, L) +end + +@inline function total_production(phyto::MixedMondo, val_name, bgc, i, j, k, grid, bgc, clock, fields) + I, = phytoplankton_concentration(val_name, i, j, k, fields) + + L, = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) + + return phyto.growth_rate(val_name, i, j, k, grid, bgc, clock, fields, L) * I +end + +@inline function iron_uptake(phyto::MixedMondo, val_name, bgc, i, j, k, grid, bgc, clock, fields) + δ = phyto.exudated_fracton + θFeₘ = phyto.maximum_iron_ratio + + T = @inbounds fields.T[i, j, k] + Fe = @inbounds fields.Fe[i, j, k] + + I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) + + θFe = IFe / (I + eps(0.0)) # μmol Fe / mmol C + + L, LFe = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) + + μᵢ = base_production_rate(phyto.growth_rate, T) + + L₁ = iron_uptake_limitation(phyto, I, Fe) # assuming bFe = Fe + + L₂ = 4 - 4.5 * LFe / (LFe + 1) # typo in Aumount 2015 + + return (1 - δ) * θFeₘ * L₁ * L₂ * max(0, (1 - θFe / θFeₘ) / (1.05 - θFe / θFeₘ)) * μᵢ * I +end + +@inline function iron_uptake_limitation(phyto, I, Fe) + k = phyto.half_saturation_for_iron_uptake + + K = k * size_factor(phyto, I) + + return Fe / (Fe + K + eps(0.0)) +end + +@inline function size_factor(phyto, I) + Iₘ = phyto.threshold_for_size_dependency + S = phyto.size_ratio + + I₁ = min(I, Iₘ) + I₂ = max(0, I - Iₘ) + + return (I₁ + S * I₂) / (I₁ + I₂ + eps(0.0)) +end + +@inline function silicate_uptake(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) + δ = phyto.exudated_fracton + + K₁ = phyto.silicate_half_saturation + K₂ = phyto.enhanced_silicate_half_saturation + θ₀ = phyto.optimal_silicate_ratio + + I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) + + T = @inbounds fields.T[i, j, k] + Si = @inbounds fields.Si[i, j, k] + + L, LFe, LPO₄, LN = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) + + μ = phyto.growth_rate(val_name, i, j, k, grid, bgc, clock, fields, L) + + μᵢ = base_production_rate(phyto.growth_rate, T) + + L₁ = Si / (Si + K₁ + eps(0.0)) + + # enhanced silication in southern ocean + φ = bgc.latitude(i, j, k, grid) + + L₂ = ifelse(φ < 0, Si^3 / (Si^3 + K₂^3), 0) + + F₁ = min(μ / (μᵢ * L + eps(0.0)), LFe, LPO₄, LN) + + F₂ = min(1, 2.2 * max(0, L₁ - 0.5)) + + θ₁ = θ₀ * L₁ * min(5.4, (4.4 * exp(-4.23 * F₁) * F₂ + 1) * (1 + 2 * L₂)) + + return (1 - δ) * θ₁ * μ * I +end + +@inline function uptake(phyto::MixedMondo, val_name, ::Val{:NO₃}, i, j, k, grid, bgc, clock, fields) + _, _, _, LN, LNO₃ = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) + + μI = total_production(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + return μI * LNO₃ / (LN + eps(0.0)) +end + +@inline function uptake(phyto::MixedMondo, val_name, ::Val{:NH₄}, i, j, k, grid, bgc, clock, fields) + _, _, _, LN, _, LNH₄ = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) + + μI = total_production(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + return μI * LNH₄ / (LN + eps(0.0)) +end + +@inline uptake(phyto::MixedMondo, val_name, ::Val{:Fe}, i, j, k, grid, bgc, clock, fields) = + iron_uptake(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl new file mode 100644 index 000000000..8cee02c58 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl @@ -0,0 +1,111 @@ +function MixedMondoNanoAndDiatoms(; nano = MixedMondo(growth_rate = GrowthRespirationLimitedProduction(dark_tollerance = 3days), + nutrient_limitation = + NitrogenIronPhosphateSilicateLimitation(minimum_ammonium_half_saturation = 0.013, + minimum_nitrate_half_saturation = 0.13, + minimum_phosphate_half_saturation = 0.8, + silicate_limited = false), + blue_light_absorption = 2.1, + green_light_absorption = 0.42, + red_light_absorption = 0.4, + maximum_quadratic_mortality = 0.0, + maximum_chlorophyll_ratio = 0.033, + half_saturation_for_iron_uptake = 1.0), + diatoms = MixedMondo(growth_rate = GrowthRespirationLimitedProduction(dark_tollerance = 4days), + nutrient_limitation = + NitrogenIronPhosphateSilicateLimitation(minimum_ammonium_half_saturation = 0.039, + minimum_nitrate_half_saturation = 0.39, + minimum_phosphate_half_saturation = 2.4, + silicate_limited = true), + blue_light_absorption = 1.6, + green_light_absorption = 0.69, + red_light_absorption = 0.7, + maximum_quadratic_mortality = 0.03/day, + maximum_chlorophyll_ratio = 0.05, + half_saturation_for_iron_uptake = 3.0)) + + return NanoAndDiatoms(nano, diatoms) +end + +const NANO_PHYTO = Union{Val{:P}, Val{:PChl}, Val{:PFe}} +const DIATOMS = Union{Val{:D}, Val{:DChl}, Val{:DFe}, Val{:DSi}} + +@inline phytoplankton_concentration(::NANO_PHYTO, i, j, k, fields) = @inbounds fields.P[i, j, k], fields.PChl[i, j, k], fields.PFe[i, j, k] +@inline phytoplankton_concentration(::DIATOMS, i, j, k, fields) = @inbounds fields.D[i, j, k], fields.DChl[i, j, k], fields.DFe[i, j, k] + +@inline carbon_name(::NANO_PHYTO) = Val(:P) +@inline carbon_name(::DIATOMS) = Val(:D) + +@inline parameterisation(::NANO_PHYTO, phyto::NanoAndDiatoms) = phyto.nano +@inline parameterisation(::DIATOMS, phyto::NanoAndDiatoms) = phyto.diatoms + +# I think these could be abstracted more so that we have a few simple functions in nano_and_diatoms +# and most only exist in `mixed_mondo.jl` +# also maybe should be dispatched on PISCES{NanoAndDiatoms{MixedMondo, MixedMondo}} +@inline function (bgc::PISCES{NanoAndDiatoms})(i, j, k, grid, val_name::Union{Val{:P}, Val{:D}}, clock, fields) + phyto = parameterisaion(val_name, bgc.phytoplankon) + + growth = carbon_growth(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + linear_mortality, quadratic_mortality = mortality(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + death = linear_mortality + quadratic_mortality + + grazing = grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields) + + return growth - death - grazing +end + +@inline function (bgc::PISCES{NanoAndDiatoms})(i, j, k, grid, val_name::Union{Val{:PChl}, Val{:DChl}}, clock, fields) + phyto = parameterisaion(val_name, bgc.phytoplankon) + + growth = chlorophyll_growth(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) + + θChl = IChl / (12 * I + eps(0.0)) + + linear_mortality, quadratic_mortality = mortality(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + death = (linear_mortality + quadratic_mortality) + + grazing = grazing(bgc.zooplankton, carbon_name(val_name), i, j, k, grid, bgc, clock, fields) + + return growth - (death + grazing) * θChl * 12 +end + +@inline function (bgc::PISCES{NanoAndDiatoms})(i, j, k, grid, val_name::Union{Val{:PFe}, Val{:DFe}}, clock, fields) + phyto = parameterisaion(val_name, bgc.phytoplankon) + + growth = chlorophyll_growth(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) + + θFe = IFe / (I + eps(0.0)) + + linear_mortality, quadratic_mortality = mortality(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + death = (linear_mortality + quadratic_mortality) + + grazing = grazing(bgc.zooplankton, carbon_name(val_name), i, j, k, grid, bgc, clock, fields) + + return growth - (death + grazing) * θFe +end + +@inline function (bgc::PISCES{NanoAndDiatoms})(i, j, k, grid, val_name::Val{:DSi}, clock, fields) + phyto = parameterisaion(val_name, bgc.phytoplankon) + + growth = chlorophyll_growth(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + D = @inbounds fields.D[i, j, k] + DSi = @inbounds fields.DSi[i, j, k] + + θSi = DSi / (D + eps(0.0)) + + linear_mortality, quadratic_mortality = mortality(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + death = (linear_mortality + quadratic_mortality) + + grazing = grazing(bgc.zooplankton, Val(:D), i, j, k, grid, bgc, clock, fields) + + return growth - (death + grazing) * θSi +end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl new file mode 100644 index 000000000..5f3aff834 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl @@ -0,0 +1,32 @@ +struct NanoAndDiatoms{N, D} + nano :: N + diatoms :: D +end + +required_biogeochemical_tracers(phyto::NanoAndDiatoms) = (required_biogeochemical_tracers(phyto.nano, :P)..., + required_biogeochemical_tracers(phyto.diatoms, :D)...) + +@inline dissolved_exudate(phyto::NanoAndDiatoms, bgc, i, j, k, grid, bgc, clock, fields) = + (dissolved_exudate(phyto.nano, Val(:P), bgc, i, j, k, grid, bgc, clock, fields) + + dissolved_exudate(phyto.diatoms, Val(:D), bgc, i, j, k, grid, bgc, clock, fields)) + +@inline uptake(phyto::NanoAndDiatoms, val_uptake_name, i, j, k, grid, bgc, clock, fields) = + (uptake(phyto.nano, Val(:P), val_uptake_name, bgc, i, j, k, grid, bgc, clock, fields) + + uptake(phyto.diatoms, Val(:D), val_uptake_name, bgc, i, j, k, grid, bgc, clock, fields)) + +@inline function nitrogen_availability_limitation(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) + _, _, _, LN = phyto.nano.nutrient_limitation(Val(:P), phyto.nano, i, j, k, grid, bgc, clock, fields) + + return LN +end + +@inline base_production_rate(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) = + @inbounds base_production_rate(phyto.nano.growth_rate, fields.T[i, j, k]) + +@inline silicate_uptake(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) = + (silicate_uptake(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) + + silicate_uptake(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields)) + +@inline total_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) = + (total_production(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) + + total_production(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields)) \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/nutrient_limitation.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl similarity index 76% rename from src/Models/AdvectedPopulations/PISCES/nutrient_limitation.jl rename to src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl index 1de3840e6..722446c54 100644 --- a/src/Models/AdvectedPopulations/PISCES/nutrient_limitation.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl @@ -11,26 +11,13 @@ setting `silicate_limited=false`. minimum_ammonium_half_saturation :: FT # mmol N / m³ minimum_nitrate_half_saturation :: FT # mmol N / m³ minimum_phosphate_half_saturation :: FT # mmol P / m³ - threshold_for_size_dependency :: FT = 1.0 # mmol C / m³ - size_ratio :: FT = 3.0 # optimal_iron_quota :: FT = 0.007 # μmol Fe / mmol C silicate_limited :: BT # Bool minimum_silicate_half_saturation :: FT = 1.0 # mmol Si / m³ silicate_half_saturation_parameter :: FT = 16.6 # mmol Si / m³ - half_saturation_for_iron_uptake :: FT # μmol Fe / m³ end -@inline function size_factor(L, I) - Iₘ = L.threshold_for_size_dependency - S = L.size_ratio - - I₁ = min(I, Iₘ) - I₂ = max(0, I - Iₘ) - - return (I₁ + S * I₂) / (I₁ + I₂ + eps(0.0)) -end - -@inline function (L::NitrogenIronPhosphateSilicateLimitation)(bgc, I, IChl, IFe, NO₃, NH₄, PO₄, Fe, Si, Si′) +@inline function (L::NitrogenIronPhosphateSilicateLimitation)(val_name, phyto, i, j, k, grid, bgc, clock, fields) kₙₒ = L.minimum_nitrate_half_saturation kₙₕ = L.minimum_ammonium_half_saturation kₚ = L.minimum_phosphate_half_saturation @@ -39,11 +26,18 @@ end θₒ = L.optimal_iron_quota + I, IChl, IFe = phytoplankton_concentrations(val_name, fields) + + NO₃ = @inbounds fields.NO₃[i, j, k] + NH₄ = @inbounds fields.NH₄[i, j, k] + PO₄ = @inbounds fields.PO₄[i, j, k] + Si = @inbounds fields.Si[i, j, k] + # quotas θFe = ifelse(I == 0, 0, IFe / (I + eps(0.0))) θChl = ifelse(I == 0, 0, IChl / (12 * I + eps(0.0))) - K̄ = size_factor(L, I) + K̄ = size_factor(phyto, I) Kₙₒ = kₙₒ * K̄ Kₙₕ = kₙₕ * K̄ @@ -74,11 +68,3 @@ end end @inline nitrogen_limitation(N₁, N₂, K₁, K₂) = (K₂ * N₁) / (K₁ * K₂ + K₁ * N₂ + K₂ * N₁ + eps(0.0)) - -@inline function iron_uptake_limitation(L, I, Fe) - k = L.half_saturation_for_iron_uptake - - K = k * size_factor(L, I) - - return Fe / (Fe + K + eps(0.0)) -end diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/phytoplankton.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/phytoplankton.jl new file mode 100644 index 000000000..d2e134b43 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/phytoplankton.jl @@ -0,0 +1,18 @@ +module Phytoplankton + +export NanoAndDiatoms, MixedMondoPhytoplankton, MixedMondoNanoAndDiatoms + +using OceanBioME.Models.PISCESModel: PISCES + +using OceanBioME.Models.PISCESModel.Zooplankton: grazing + +import Oceananigans.Biogeochemistry: required_biogeochemical_tracers +import OceanBioME.Models.PISCESModel: mortality + +include("nano_and_diatoms.jl") +include("mixed_mondo.jl") +include("growth_rate.jl") +include("nutrient_limitation.jl") +include("mixed_mono_nano_diatoms.jl") + +end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/waste.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/waste.jl new file mode 100644 index 000000000..52ae6af29 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/waste.jl @@ -0,0 +1,7 @@ +@inline function dissolved_exudate(phyto::MixedMondo, val_name, bgc, i, j, k, grid, bgc, clock, fields) + δ = phyto.exudated_fracton + + μI = total_production(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + + return δ * μI +end diff --git a/src/Models/AdvectedPopulations/PISCES/show_methods.jl b/src/Models/AdvectedPopulations/PISCES/show_methods.jl deleted file mode 100644 index 80c2ce4cf..000000000 --- a/src/Models/AdvectedPopulations/PISCES/show_methods.jl +++ /dev/null @@ -1,100 +0,0 @@ -summary(::PISCES) = string("PISCES biogeochemical model") - -function show(io::IO, bgc::PISCES) - - FT = typeof(bgc.background_shear) - - output = "Pelagic Interactions Scheme for Carbon and Ecosystem Studies (PISCES) model {$FT}" - - output *= "\n Nanophytoplankton: $(summary(bgc.nanophytoplankton))" - - output *= "\n Diatoms: $(summary(bgc.diatoms))" - - output *= "\n Microzooplankton: $(summary(bgc.microzooplankton))" - - output *= "\n Mesozooplankton: $(summary(bgc.mesozooplankton))" - - output *= "\n Microzooplankton: $(summary(bgc.microzooplankton))" - - output *= "\n Dissolved organic matter: $(summary(bgc.dissolved_organic_matter))" - - output *= "\n Particulate organic matter: $(summary(bgc.particulate_organic_matter))" - - output *= "\n Nitrogen: $(summary(bgc.nitrogen))" - - output *= "\n Iron: $(summary(bgc.iron))" - - output *= "\n Silicate: $(summary(bgc.silicate))" - - output *= "\n Oxygen: $(summary(bgc.oxygen))" - - output *= "\n Phosphate: $(summary(bgc.phosphate))" - - output *= "\n Calcite: $(summary(bgc.calcite))" - - output *= "\n Carbon system: $(summary(bgc.carbon_system))" - - output *= "\n Latitude: $(summary(bgc.latitude))" - - output *= "\n Day length: $(nameof(bgc.day_length))" - - output *= "\n Mixed layer depth: $(summary(bgc.mixed_layer_depth))" - - output *= "\n Euphotic depth: $(summary(bgc.euphotic_depth))" - - output *= "\n Silicate climatology: $(summary(bgc.silicate_climatology))" - - output *= "\n Mixed layer mean diffusivity: $(summary(bgc.mean_mixed_layer_vertical_diffusivity))" - - output *= "\n Mixed layer mean light: $(summary(bgc.mean_mixed_layer_light))" - - output *= "\n Carbon chemistry: $(summary(bgc.carbon_chemistry))" - - output *= "\n Calcite saturation: $(summary(bgc.calcite_saturation))" - - output *= "\n Sinking velocities:" - output *= "\n Small particles: $(summary(bgc.sinking_velocities.POC))" - output *= "\n Large particles: $(summary(bgc.sinking_velocities.GOC))" - - print(io, output) - - return nothing -end - -function summary(phyto::MixedMondoPhytoplankton{<:Any, <:Any, FT}) where FT - growth_rate = summary(phyto.growth_rate) - nutrient_limitation = summary(phyto.nutrient_limitation) - - names = "\n I (mmol C / m³), IChl (mg Chl / m³), IFe (μmol Fe / m³)" - - if phyto.nutrient_limitation.silicate_limited - names *= ", ISi (mmol Si / m³)" - end - - return string("MixedMondoPhytoplankton{$(growth_rate), $(nutrient_limitation), $FT} - "*names) -end - -summary(::NutrientLimitedProduction) = string("NutrientLimitedProduction") -summary(::GrowthRespirationLimitedProduction) = string("NutrientLimitedProduction") - -summary(::NitrogenIronPhosphateSilicateLimitation) = string("NitrogenIronPhosphateSilicateLimitation") - -summary(::Zooplankton{FT}) where FT = string("Zooplankton{$FT} - I (mmol C / m³)") - -summary(::DissolvedOrganicMatter{FT}) where FT = string("DissolvedOrganicMatter{$FT} - DOC (mmol C / m³)") -summary(::TwoCompartementParticulateOrganicMatter{FT}) where FT = - string("TwoCompartementParticulateOrganicMatter{$FT} - - POC (mmol C / m³), GOC (mmol C / m³), SFe (μmol Fe / m³), BFe (μmol Fe / m³), PSi (mmol Si / m³)") - -summary(::NitrateAmmonia{FT}) where FT = string("NitrateAmmonia{$FT} - NO₃ (mmol N / m³), NH₄(mmol N / m³)") -summary(::SimpleIron{FT}) where FT = string("SimpleIron{$FT} - Fe (μmol Fe / m³)") -summary(::Oxygen{FT}) where FT = string("Oxygen{$FT} - O₂ (mmol O₂ / m³)") -summary(::Silicate) = string("Silicate - Si (mmol Si / m³)") -summary(::Phosphate) = string("Phosphate - PO₄ (mmol P / m³)") -summary(::Calcite{FT}) where FT = string("Calcite{$FT} - CaCO₃ (mmol C / m³)") -summary(::CarbonateSystem) = string("CarbonateSystem - DIC (mmol C / m³), Alk (mequiv / m³)") - -summary(::ModelLatitude) = string("ModelLatitude") -summary(lat::PrescribedLatitude{FT}) where FT = string("PrescribedLatitude $(lat.latitude)° {FT}") - -# TODO: add show methods \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/silicate.jl b/src/Models/AdvectedPopulations/PISCES/silicate.jl new file mode 100644 index 000000000..f0eed471f --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/silicate.jl @@ -0,0 +1,24 @@ +module Silicates + +export Silicate + +using OceanBioME.Models.PISCESModel: PISCES + +using OceanBioME.Models.PISCESModel.ParticulateOrganicMatter: + particulate_silicate_dissolution + +using OceanBioME.Models.PISCESModel.Phytoplankton: silicate_uptake + +struct Silicate end + +const PISCESSilicate = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, Silicate} + +@inline function (bgc::PISCESSilicate)(i, j, k, grid, val_name::Val{:Si}, clock, fields) + consumption = silicate_uptake(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + + dissolution = particulate_silicate_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + + return dissolution - consumption +end + +end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/silicon.jl b/src/Models/AdvectedPopulations/PISCES/silicon.jl deleted file mode 100644 index 2b95e40ef..000000000 --- a/src/Models/AdvectedPopulations/PISCES/silicon.jl +++ /dev/null @@ -1,25 +0,0 @@ -""" - Silicate - -Parameterisation for silicate (Si) which is consumed by diatoms -and dissolutioned from particles. -""" -struct Silicate end - -@inline function (silicate::Silicate)(::Val{:Si}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - consumption, = silicate_uptake(bgc.diatoms, bgc, y, t, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, Si′, T, zₘₓₗ, zₑᵤ, κ, PAR₁, PAR₂, PAR₃) - - dissolution = particulate_silicate_dissolution(bgc.particulate_organic_matter, z, PSi, Si, T, zₘₓₗ, zₑᵤ, wGOC) - - return dissolution - consumption -end diff --git a/src/Models/AdvectedPopulations/PISCES/silicon_in_particles.jl b/src/Models/AdvectedPopulations/PISCES/silicon_in_particles.jl deleted file mode 100644 index 85b713a26..000000000 --- a/src/Models/AdvectedPopulations/PISCES/silicon_in_particles.jl +++ /dev/null @@ -1,52 +0,0 @@ -@inline function (poc::TwoCompartementParticulateOrganicMatter)(::Val{:PSi}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - # diatom grazing - gZ = diatom_grazing(bgc.microzooplankton, P, D, Z, POC, T) - gM = diatom_grazing(bgc.mesozooplankton, P, D, Z, POC, T) - - grazing = (gZ * Z + gM * M) * DSi / (D + eps(0.0)) - - # diatom mortality - diatom_linear_mortality, diatom_quadratic_mortality = mortality(bgc.diatoms, bgc, z, D, DChl, DFe, NO₃, NH₄, PO₄, Fe, Si, Si′, zₘₓₗ) - - diatom_mortality = (diatom_linear_mortality + diatom_quadratic_mortality) * DSi / (D + eps(0.0)) - - # dissolution - dissolution = particulate_silicate_dissolution(poc, z, PSi, Si, T, zₘₓₗ, zₑᵤ, wGOC) - - return grazing + diatom_mortality - dissolution -end - -@inline function particulate_silicate_dissolution(poc, z, PSi, Si, T, zₘₓₗ, zₑᵤ, wGOC) - λₗ = poc.fast_dissolution_rate_of_silicate - λᵣ = poc.slow_dissolution_rate_of_silicate - - χ = particulate_silicate_liable_fraction(poc, z, zₘₓₗ, zₑᵤ, wGOC) - - λ₀ = χ * λₗ + (1 - χ) * λᵣ - - equilibrium_silicate = 10^(6.44 - 968 / (T + 273.15)) - silicate_saturation = (equilibrium_silicate - Si) / equilibrium_silicate - - λ = λ₀ * (0.225 * (1 + T/15) * silicate_saturation + 0.775 * ((1 + T/400)^4 * silicate_saturation)^9) - - return λ * PSi # assuming the Diss_Si is typo in Aumont 2015, consistent with Aumont 2005 -end - -@inline function particulate_silicate_liable_fraction(poc, z, zₘₓₗ, zₑᵤ, wGOC) - χ₀ = poc.base_liable_silicate_fraction - λₗ = poc.fast_dissolution_rate_of_silicate - λᵣ = poc.slow_dissolution_rate_of_silicate - - zₘ = min(zₘₓₗ, zₑᵤ) - - return χ₀ * ifelse(z >= zₘ, 1, exp((λₗ - λᵣ) * (zₘ - z) / wGOC)) -end diff --git a/src/Models/AdvectedPopulations/PISCES/update_state.jl b/src/Models/AdvectedPopulations/PISCES/update_state.jl deleted file mode 100644 index c864af135..000000000 --- a/src/Models/AdvectedPopulations/PISCES/update_state.jl +++ /dev/null @@ -1,18 +0,0 @@ -function update_biogeochemical_state!(model, bgc::PISCES) - # this should come from utils - #update_mixed_layer_depth!(bgc, model) - - PAR = biogeochemical_auxiliary_fields(model.biogeochemistry.light_attenuation).PAR - - compute_euphotic_depth!(bgc.euphotic_depth, PAR) - - compute_mean_mixed_layer_vertical_diffusivity!(bgc.mean_mixed_layer_vertical_diffusivity, bgc.mixed_layer_depth, model) - - compute_mean_mixed_layer_light!(bgc.mean_mixed_layer_light, bgc.mixed_layer_depth, PAR, model) - - compute_calcite_saturation!(bgc.carbon_chemistry, bgc.calcite_saturation, model) - - #update_silicate_climatology!(bgc, model) - - return nothing -end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton.jl deleted file mode 100644 index 0b3443125..000000000 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton.jl +++ /dev/null @@ -1,293 +0,0 @@ -""" - Zooplankton - -The PISCES zooplankton growth model where each class has preferences -for grazing on nanophytoplankton (P), diatoms (D), microzooplankton (Z), -and particulate organic matter (POC), and can flux feed on sinking -particulates (POC and GOC). - -This model assumes a fixed ratio for all other elements (i.e. N, P, Fe). -""" -@kwdef struct Zooplankton{FT} - temperature_sensetivity :: FT = 1.079 # - maximum_grazing_rate :: FT # 1 / s - - preference_for_nanophytoplankton :: FT # - preference_for_diatoms :: FT # - preference_for_particulates :: FT # - preference_for_zooplankton :: FT # - - food_threshold_concentration :: FT = 0.3 # mmol C / m³ - specific_food_thresehold_concentration :: FT = 0.001 # mmol C / m³ - - grazing_half_saturation :: FT = 20.0 # mmol C / m³ - - maximum_flux_feeding_rate :: FT # m / (mmol C / m³) - - iron_ratio :: FT # μmol Fe / mmol C - - minimum_growth_efficiency :: FT # - non_assililated_fraction :: FT = 0.3 # - - mortality_half_saturation :: FT = 0.2 # mmol C / m³ - quadratic_mortality :: FT # 1 / (mmol C / m³) / s - linear_mortality :: FT # 1 / s - - dissolved_excretion_fraction :: FT = 0.6 # - undissolved_calcite_fraction :: FT # -end - -@inline zooplankton_concentration(::Val{:Z}, Z, M) = Z -@inline zooplankton_concentration(::Val{:M}, Z, M) = M - -@inline function specific_grazing(zoo::Zooplankton, P, D, Z, POC, T) - g₀ = zoo.maximum_grazing_rate - b = zoo.temperature_sensetivity - pP = zoo.preference_for_nanophytoplankton - pD = zoo.preference_for_diatoms - pPOC = zoo.preference_for_particulates - pZ = zoo.preference_for_zooplankton - J = zoo.specific_food_thresehold_concentration - K = zoo.grazing_half_saturation - - food_threshold_concentration = zoo.food_threshold_concentration - - base_grazing_rate = g₀ * b ^ T - - food_availability = pP * P + pD * D + pPOC * POC + pZ * Z - - weighted_food_availability = pP * max(0, P - J) + pD * max(0, D - J) + pPOC * max(0, POC - J) + pZ * max(0, Z - J) - - concentration_limited_grazing = max(0, weighted_food_availability - min(weighted_food_availability / 2, food_threshold_concentration)) - - total_specific_grazing = base_grazing_rate * concentration_limited_grazing / (K + food_availability) - - phytoplankton_grazing = pP * max(0, P - J) * total_specific_grazing / (weighted_food_availability + eps(0.0)) - diatom_grazing = pD * max(0, D - J) * total_specific_grazing / (weighted_food_availability + eps(0.0)) - particulate_grazing = pPOC * max(0, POC - J) * total_specific_grazing / (weighted_food_availability + eps(0.0)) - zooplankton_grazing = pZ * max(0, Z - J) * total_specific_grazing / (weighted_food_availability + eps(0.0)) - - return total_specific_grazing, phytoplankton_grazing, diatom_grazing, particulate_grazing, zooplankton_grazing -end - -@inline function specific_flux_feeding(zoo::Zooplankton, POC, T, w) - g₀ = zoo.maximum_flux_feeding_rate - b = zoo.temperature_sensetivity - - base_flux_feeding_rate = g₀ * b ^ T - - return base_flux_feeding_rate * w * POC -end - -@inline function (zoo::Zooplankton)(val_name::Union{Val{:Z}, Val{:M}}, bgc, - x, y, z, t, - P, D, Z, M, - PChl, DChl, PFe, DFe, DSi, - DOC, POC, GOC, - SFe, BFe, PSi, - NO₃, NH₄, PO₄, Fe, Si, - CaCO₃, DIC, Alk, - O₂, T, S, - zₘₓₗ, zₑᵤ, Si′, Ω, κ, mixed_layer_PAR, wPOC, wGOC, PAR, PAR₁, PAR₂, PAR₃) - - I = zooplankton_concentration(val_name, Z, M) - - # grazing - total_specific_grazing, gP, gD, gPOC, gZ = specific_grazing(zoo, P, D, Z, POC, T) - - grazing = total_specific_grazing * I - - # flux feeding - small_flux_feeding = specific_flux_feeding(zoo, POC, T, wPOC) - large_flux_feeding = specific_flux_feeding(zoo, GOC, T, wGOC) - - flux_feeding = (small_flux_feeding + large_flux_feeding) * I - - # grazing mortality - specific_grazing_mortality = zooplankton_grazing_mortality(val_name, bgc.mesozooplankton, P, D, Z, POC, T) - - grazing_mortality = specific_grazing_mortality * M - - # mortality - total_mortality = mortality(zoo, bgc, I, O₂, T) - - growth_efficiency = grazing_growth_efficiency(zoo, P, D, PFe, DFe, POC, SFe, total_specific_grazing, gP, gD, gPOC, gZ) - - return growth_efficiency * (grazing + flux_feeding) - grazing_mortality - total_mortality -end - -@inline function nanophytoplankton_grazing(zoo::Zooplankton, P, D, Z, POC, T) - _, g = specific_grazing(zoo, P, D, Z, POC, T) - - return g -end - -@inline function diatom_grazing(zoo::Zooplankton, P, D, Z, POC, T) - _, _, g = specific_grazing(zoo, P, D, Z, POC, T) - - return g -end - -@inline function particulate_grazing(zoo::Zooplankton, P, D, Z, POC, T) - _, _, _, g = specific_grazing(zoo, P, D, Z, POC, T) - - return g -end - -@inline function zooplankton_grazing(zoo::Zooplankton, P, D, Z, POC, T) - _, _, _, _, g = specific_grazing(zoo, P, D, Z, POC, T) - - return g -end - -@inline zooplankton_grazing_mortality(val_name, zoo, P, D, Z, POC, T) = 0 -@inline zooplankton_grazing_mortality(::Val{:Z}, zoo, P, D, Z, POC, T) = zooplankton_grazing(zoo, P, D, Z, POC, T) - -@inline function dissolved_upper_trophic_respiration_product(zoo, M, T) - γ = zoo.dissolved_excretion_fraction - - R = upper_trophic_respiration_product(zoo, M, T) - - return (1 - γ) * R -end - -@inline function inorganic_upper_trophic_respiration_product(zoo, M, T) - γ = zoo.dissolved_excretion_fraction - - R = upper_trophic_respiration_product(zoo, M, T) - - return γ * R -end - -@inline function upper_trophic_waste(zoo, M, T) - e₀ = zoo.minimum_growth_efficiency - b = zoo.temperature_sensetivity - m₀ = zoo.quadratic_mortality - - temperature_factor = b^T - - return 1 / (1 - e₀) * m₀ * temperature_factor * M^2 -end - -@inline upper_trophic_respiration_product(zoo, M, T) = - (1 - zoo.minimum_growth_efficiency - zoo.non_assililated_fraction) * upper_trophic_waste(zoo, M, T) - -@inline upper_trophic_fecal_product(zoo, M, T) = - zoo.non_assililated_fraction * upper_trophic_waste(zoo, M, T) - -@inline function grazing_growth_efficiency(zoo, P, D, PFe, DFe, POC, SFe, g, gP, gD, gPOC, gZ) - θFe = zoo.iron_ratio - e₀ = zoo.minimum_growth_efficiency - σ = zoo.non_assililated_fraction - - iron_grazing = PFe / (P + eps(0.0)) * gP + DFe / (D + eps(0.0)) * gD + SFe / (POC + eps(0.0)) * gPOC + θFe * gZ - - iron_grazing_ratio = iron_grazing / (θFe * g + eps(0.0)) - - food_quality = min(1, iron_grazing_ratio) - - return food_quality * min(e₀, (1 - σ) * iron_grazing_ratio) -end - -@inline function specific_excretion(zoo, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) - σ = zoo.non_assililated_fraction - - total_specific_grazing, gP, gD, gPOC, gZ = specific_grazing(zoo, P, D, Z, POC, T) - - small_flux_feeding = specific_flux_feeding(zoo, POC, T, wPOC) - large_flux_feeding = specific_flux_feeding(zoo, GOC, T, wGOC) - - total_specific_flux_feeding = small_flux_feeding + large_flux_feeding - - e = grazing_growth_efficiency(zoo, P, D, PFe, DFe, POC, SFe, total_specific_grazing, gP, gD, gPOC, gZ) - - return (1 - e - σ) * (total_specific_grazing + total_specific_flux_feeding) -end - -@inline specific_dissolved_grazing_waste(zoo, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) = - (1 - zoo.dissolved_excretion_fraction) * specific_excretion(zoo, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) - -@inline specific_inorganic_grazing_waste(zoo, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) = - zoo.dissolved_excretion_fraction * specific_excretion(zoo, P, D, PFe, DFe, Z, POC, GOC, SFe, T, wPOC, wGOC) - -@inline function specific_non_assimilated_waste(zoo, P, D, Z, POC, GOC, T, wPOC, wGOC) - σ = zoo.non_assililated_fraction - - g, = specific_grazing(zoo, P, D, Z, POC, T) - - small_flux_feeding = specific_flux_feeding(zoo, POC, T, wPOC) - large_flux_feeding = specific_flux_feeding(zoo, GOC, T, wGOC) - - return σ * (g + small_flux_feeding + large_flux_feeding) -end - -@inline function specific_non_assimilated_iron_waste(zoo, bgc, P, D, PFe, DFe, Z, POC, GOC, SFe, BFe, T, wPOC, wGOC) - _, gP, gD, gPOC, gZ = specific_grazing(zoo, P, D, Z, POC, T) - - θᶻ = bgc.microzooplankton.iron_ratio - σ = zoo.non_assililated_fraction - - small_flux_feeding = specific_flux_feeding(zoo, POC, T, wPOC) - large_flux_feeding = specific_flux_feeding(zoo, GOC, T, wGOC) - - return σ * (gP * PFe / (P + eps(0.0)) + gD * DFe / (D + eps(0.0)) + gPOC * SFe / (POC + eps(0.0)) + gZ * θᶻ - + small_flux_feeding * SFe / (POC + eps(0.0)) + large_flux_feeding * BFe / (GOC + eps(0.0))) -end - -@inline function specific_non_assimilated_iron(zoo, bgc, P, D, PFe, DFe, Z, POC, GOC, SFe, BFe, T, wPOC, wGOC) - θ = zoo.iron_ratio - θᶻ = bgc.microzooplankton.iron_ratio - - σ = zoo.non_assililated_fraction - - g, gP, gD, gPOC, gZ = specific_grazing(zoo, P, D, Z, POC, T) - - small_flux_feeding = specific_flux_feeding(zoo, POC, T, wPOC) - large_flux_feeding = specific_flux_feeding(zoo, GOC, T, wGOC) - - total_iron_consumed = (gP * PFe / (P + eps(0.0)) + gD * DFe / (D + eps(0.0)) + gZ * θᶻ - + (gPOC + small_flux_feeding) * SFe / (POC + eps(0.0)) - + large_flux_feeding * BFe / (GOC + eps(0.0))) - - - grazing_iron_ratio = (1 - σ) * total_iron_consumed / (g + small_flux_feeding + large_flux_feeding + eps(0.0)) - - growth_efficiency = grazing_growth_efficiency(zoo, P, D, PFe, DFe, POC, SFe, g, gP, gD, gPOC, gZ) * θ - - non_assimilated_iron_ratio = max(0, grazing_iron_ratio - growth_efficiency) - - return non_assimilated_iron_ratio * g -end - -@inline function specific_calcite_grazing_loss(zoo, P, D, Z, POC, T) - η = zoo.undissolved_calcite_fraction - - _, gP = specific_grazing(zoo, P, D, Z, POC, T) - - return η * gP -end - -@inline function mortality(zoo::Zooplankton, bgc, I, O₂, T) - b = zoo.temperature_sensetivity - m₀ = zoo.quadratic_mortality - Kₘ = zoo.mortality_half_saturation - r = zoo.linear_mortality - - temperature_factor = b^T - - concentration_factor = I / (I + Kₘ) - - return temperature_factor * I * (m₀ * I + r * (concentration_factor + 3 * anoxia_factor(bgc, O₂))) -end - -@inline function linear_mortality(zoo::Zooplankton, bgc, I, O₂, T) - b = zoo.temperature_sensetivity - Kₘ = zoo.mortality_half_saturation - r = zoo.linear_mortality - - temperature_factor = b^T - - concentration_factor = I / (I + Kₘ) - - return temperature_factor * I * r * (concentration_factor + 3 * anoxia_factor(bgc, O₂)) -end diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl new file mode 100644 index 000000000..69b68ebc5 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl @@ -0,0 +1,38 @@ +# this file sets up the default configuration of Z and M which graze on P, D, (Z, ) and POC +function MicroAndMesoZooplankton(; micro = Zooplankton(maximum_grazing_rate = 3/day, + food_preferences = (P = 1.0, D = 0.5, POC = 0.1, Z = 0), + quadratic_mortality = 0.004/day, + linear_mortality = 0.03/day, + minimum_growth_efficiency = 0.3, + maximum_flux_feeding_rate = 0.0, + undissolved_calcite_fraction = 0.5, + iron_ratio = 0.01), + meso = Zooplankton(maximum_grazing_rate = 0.75/day, + food_preferences = (P = 0.3, D = 1.0, POC = 0.3, Z = 1.0), + quadratic_mortality = 0.03/day, + linear_mortality = 0.005/day, + minimum_growth_efficiency = 0.35, + # not documented but the below must implicitly contain a factor of second/day + # to be consistent in the NEMO namelist to go from this * mol / L * m/s to mol / L / day + maximum_flux_feeding_rate = 2e3 / 1e6 / day, # (day * meter/s * mol/L)^-1 to (meter * μ mol/L)^-1 + undissolved_calcite_fraction = 0.75, + iron_ratio = 0.015)) + + return MicroAndMesoZooplankton(micro, meso) +end + +@inline concentration(::Val{:P}, i, j, k, fields) = @inbounds fields.P[i, j, k] +@inline concentration(::Val{:D}, i, j, k, fields) = @inbounds fields.D[i, j, k] +@inline concentration(::Val{:Z}, i, j, k, fields) = @inbounds fields.Z[i, j, k] +@inline concentration(::Val{:POC}, i, j, k, fields) = @inbounds fields.POC[i, j, k] + +@inline iron_ratio(::Val{:P}, i, j, k, bgc, fields) = @inbounds fields.PFe[i, j, k] / (fields.P[i, j, k] + eps(0.0)) +@inline iron_ratio(::Val{:D}, i, j, k, bgc, fields) = @inbounds fields.DFe[i, j, k] / (fields.D[i, j, k] + eps(0.0)) +@inline iron_ratio(::Val{:Z}, i, j, k, bgc, fields) = @inbounds fields.Z[i, j, k] * bgc.zooplankton.micro.iron_ratio +@inline iron_ratio(::Val{:POC}, i, j, k, bgc, fields) = @inbounds fields.SFe[i, j, k] / (fields.POC[i, j, k] + eps(0.0)) + +@inline grazing_preference(val_prey_name, preferences) = 0 +@inline grazing_preference(::Val{:P}, preferences) = preferences.P +@inline grazing_preference(::Val{:D}, preferences) = preferences.D +@inline grazing_preference(::Val{:Z}, preferences) = preferences.Z +@inline grazing_preference(::Val{:POC}, preferences) = preferences.POC diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl new file mode 100644 index 000000000..c5d42d1b7 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl @@ -0,0 +1,201 @@ +""" + QualityDependantZooplankton + +The PISCES zooplankton growth model where each class has preferences +for grazing on nanophytoplankton (P), diatoms (D), microzooplankton (Z), +and particulate organic matter (POC), and can flux feed on sinking +particulates (POC and GOC). + +This model assumes a fixed ratio for all other elements (i.e. N, P, Fe). +""" +@kwdef struct QualityDependantZooplankton{FT, FP} + temperature_sensetivity :: FT = 1.079 # + maximum_grazing_rate :: FT # 1 / s + + food_preferences :: FP + food_names :: FN = keys(food_preferences) + + food_threshold_concentration :: FT = 0.3 # mmol C / m³ + specific_food_thresehold_concentration :: FT = 0.001 # mmol C / m³ + + grazing_half_saturation :: FT = 20.0 # mmol C / m³ + + maximum_flux_feeding_rate :: FT # m / (mmol C / m³) + + iron_ratio :: FT # μmol Fe / mmol C + + minimum_growth_efficiency :: FT # + non_assililated_fraction :: FT = 0.3 # + + mortality_half_saturation :: FT = 0.2 # mmol C / m³ + quadratic_mortality :: FT # 1 / (mmol C / m³) / s + linear_mortality :: FT # 1 / s + + # this should be called inorganic excretion factor + dissolved_excretion_fraction :: FT = 0.6 # + undissolved_calcite_fraction :: FT # +end + +required_biogeochemical_tracers(::QualityDependantZooplankton, name_base) = name_base + +@inline function growth_death(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + gI, e = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) + gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields) + mI = mortality(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + return e * (gI + gfI) - mI +end + +@inline extract_food_availability(i, j, k, fields, names::NTuple{N}) where N = + ntuple(n -> concentration(Val(names[n]), i, j, k, fields), Val(N)) + +@inline extract_iron_availability(i, j, k, bgc, fields, names::NTuple{N}) where N = + ntuple(n -> iron_ratio(Val(names[n]), i, j, k, bgc, fields), Val(N)) + +@inline function grazing(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + # food quantity + g₀ = zoo.maximum_grazing_rate + b = zoo.temperature_sensetivity + p = zoo.food_preferences + food = zoo.food_names + J = zoo.specific_food_thresehold_concentration + K = zoo.grazing_half_saturation + food_threshold_concentration = zoo.food_threshold_concentration + + N = length(food) + + I = zooplankton_concentration(val_name, i, j, k, fields) + + base_grazing_rate = g₀ * b ^ T + + food_availability = extract_food_availability(i, j, k, fields, food) + + total_food = sum(ntuple(n->food_availability[n] * p[n], Val(N))) + + available_total_food = sum(ntuple(n->max(zero(grid), (food_availability[n] - J)) * p[n], Val(N))) + + concentration_limited_grazing = max(0, available_total_food - min(available_total_food / 2, food_threshold_concentration)) + + total_specific_grazing = base_grazing_rate * concentration_limited_grazing / (K + total_food) + + # food quality + θFe = zoo.iron_ratio + e₀ = zoo.minimum_growth_efficiency + σ = zoo.non_assililated_fraction + + iron_availabillity = extract_iron_availability(i, j, k, bgc, fields, food) + + total_iron = sum(ntuple(n->iron_availabillity[n] * p[n], Val(N))) + + iron_grazing_ratio = iron_grazing / (θFe * total_specific_grazing + eps(0.0)) + + food_quality = min(1, iron_grazing_ratio) + + growth_efficiency = food_quality * min(e₀, (1 - σ) * iron_grazing_ratio) + + return total_specific_grazing * I, growth_efficiency +end + +@inline function flux_feeding(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + g₀ = zoo.maximum_flux_feeding_rate + b = zoo.temperature_sensetivity + + I = zooplankton_concentration(val_name, i, j, k, fields) + + T = @inbounds fields.T[i, j, k] + + sinking_flux = edible_flux_rate(bgc.particulate_organic_matter, i, j, k, grid, fields) + + base_flux_feeding_rate = g₁ * b ^ T + + total_specific_flux_feeding = base_flux_feeding_rate * sinking_flux + + return total_specific_flux_feeding * I +end + +@inline function mortality(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + b = zoo.temperature_sensetivity + m₀ = zoo.quadratic_mortality + Kₘ = zoo.mortality_half_saturation + r = zoo.linear_mortality + + temperature_factor = b^T + + I = zooplankton_concentration(val_name, i, j, k, fields) + + O₂ = @inbounds fields.O₂[i, j, k] + + concentration_factor = I / (I + Kₘ) + + return temperature_factor * I * (m₀ * I + r * (concentration_factor + 3 * anoxia_factor(bgc, O₂))) +end + +@inline function linear_mortality(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + b = zoo.temperature_sensetivity + Kₘ = zoo.mortality_half_saturation + r = zoo.linear_mortality + + temperature_factor = b^T + + I = zooplankton_concentration(val_name, i, j, k, fields) + + O₂ = @inbounds fields.O₂[i, j, k] + + concentration_factor = I / (I + Kₘ) + + return temperature_factor * r * (concentration_factor + 3 * anoxia_factor(bgc, O₂)) * I +end + +##### +##### Effect on other compartements +##### + +@inline function grazing(zoo::QualityDependantZooplankton, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields) + g₀ = zoo.maximum_grazing_rate + b = zoo.temperature_sensetivity + p = zoo.food_preferences + food = zoo.food_names + J = zoo.specific_food_thresehold_concentration + K = zoo.grazing_half_saturation + food_threshold_concentration = zoo.food_threshold_concentration + + N = length(food) + + I = zooplankton_concentration(val_name, i, j, k, fields) + + base_grazing_rate = g₀ * b ^ T + + food_availability = extract_food_availability(i, j, k, fields, food, p) + + total_food = sum(ntuple(n->food_availability[n] * p[n], Val(N))) + + available_total_food = sum(ntuple(n->max(zero(grid), (food_availability[n] - J)) * p[n], Val(N))) + + concentration_limited_grazing = max(0, available_total_food - min(available_total_food / 2, food_threshold_concentration)) + + total_specific_grazing = base_grazing_rate * concentration_limited_grazing / (K + total_food) + + P = concentration(val_prey_name, i, j, k, fields) + + return grazing_preference(val_prey_name, p) * max(0, P - J) * total_specific_grazing / (available_total_food + eps(0.0)) * I +end + +@inline function flux_feeding(zoo::QualityDependantZooplankton, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields) + g₀ = zoo.maximum_flux_feeding_rate + b = zoo.temperature_sensetivity + + I = zooplankton_concentration(val_name, i, j, k, fields) + + T = @inbounds fields.T[i, j, k] + + sinking_flux = flux_rate(val_prey_name, i, j, k, grid, fields) + + base_flux_feeding_rate = g₁ * b ^ T + + total_specific_flux_feeding = base_flux_feeding_rate * sinking_flux + + return total_specific_flux_feeding * I +end + +include("grazing_waste.jl") +include("mortality_waste.jl") \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl new file mode 100644 index 000000000..f19e76712 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl @@ -0,0 +1,70 @@ +include("iron_grazing.jl") + +@inline function non_assimilated_waste(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + σ = zoo.non_assililated_fraction + + gI, = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + return σ * (gI + gfI) +end + +@inline function excretion(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + σ = zoo.non_assililated_fraction + + gI, growth_efficiency = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + return (1 - σ - e) * (gI + gfI) +end + +@inline function inorganic_excretion(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + γ = zoo.dissolved_excretion_fraction + + return γ * excretion(zoo, val_name, i, j, k, grid, bgc, clock, fields) +end + +@inline function organic_excretion(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + γ = zoo.dissolved_excretion_fraction + + return (1 - γ) * excretion(zoo, val_name, i, j, k, grid, bgc, clock, fields) +end + +@inline function non_assimilated_iron_waste(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + σ = zoo.non_assililated_fraction + + gI = iron_grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + gfI = iron_flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + return σ * (gI + gfI) +end + +@inline function non_assimilated_iron(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + θ = zoo.iron_ratio + σ = zoo.non_assililated_fraction + + gI, growth_efficiency = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) + gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + total_carbon = gI + gfI + + total_iron = (iron_grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + iron_flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields)) + + grazing_iron_ratio = (1 - σ) * total_iron / (total_carbon + eps(0.0)) + + non_assimilated_iron_ratio = max(0, grazing_iron_ratio - growth_efficiency * θ) + + return non_assililated_fraction * gI +end + +@inline function calcite_loss(zoo::QualityDependantZooplankton, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields) + η = zoo.undissolved_calcite_fraction + + g = grazing(zoo, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields) + + return η * g +end diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl new file mode 100644 index 000000000..b056ec605 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl @@ -0,0 +1,51 @@ + +@inline function iron_grazing(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + # food quantity + g₀ = zoo.maximum_grazing_rate + b = zoo.temperature_sensetivity + p = zoo.food_preferences + food = zoo.food_names + J = zoo.specific_food_thresehold_concentration + K = zoo.grazing_half_saturation + food_threshold_concentration = zoo.food_threshold_concentration + + N = length(food) + + I = zooplankton_concentration(val_name, i, j, k, fields) + + base_grazing_rate = g₀ * b ^ T + + food_availability = extract_food_availability(i, j, k, fields, food) + + total_food = sum(ntuple(n->food_availability[n] * p[n], Val(N))) + + available_total_food = sum(ntuple(n->max(zero(grid), (food_availability[n] - J)) * p[n], Val(N))) + + concentration_limited_grazing = max(0, available_total_food - min(available_total_food / 2, food_threshold_concentration)) + + total_specific_grazing = base_grazing_rate * concentration_limited_grazing / (K + total_food) + + iron_ratios = extract_iron_availability(i, j, k, bgc, fields, food) + + total_specific_iron_grazing = sum(ntuple(n->max(zero(grid), (food_availability[n] - J)) * p[n] iron_ratios[n], Val(N))) * total_specific_grazing / available_total_food + + return total_specific_grazing * I +end + +@inline function iron_flux_feeding(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + g₀ = zoo.maximum_flux_feeding_rate + b = zoo.temperature_sensetivity + + I = zooplankton_concentration(val_name, i, j, k, fields) + + T = @inbounds fields.T[i, j, k] + + sinking_flux = edible_iron_flux_rate(bgc.particulate_organic_matter, i, j, k, grid, fields) + + base_flux_feeding_rate = g₁ * b ^ T + + total_specific_flux_feeding = base_flux_feeding_rate * sinking_flux + + return total_specific_flux_feeding * I +end + diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl new file mode 100644 index 000000000..d7ef98aac --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl @@ -0,0 +1,130 @@ +struct MicroAndMeso{μ, M, FT} + micro :: μ + meso :: M + + microzooplankton_bacteria_concentration :: FT = 0.7 + mesozooplankton_bacteria_concentration :: FT = 1.4 + maximum_bacteria_concentration :: FT = 4.0 # mmol C / m³ + + doc_half_saturation_for_bacterial_activity :: FT = 417.0 # mmol C / m³ + nitrate_half_saturation_for_bacterial_activity :: FT = 0.03 # mmol N / m³ + ammonia_half_saturation_for_bacterial_activity :: FT = 0.003 # mmol N / m³ + phosphate_half_saturation_for_bacterial_activity :: FT = 0.003 # mmol P / m³ + iron_half_saturation_for_bacterial_activity :: FT = 0.01 # μmol Fe / m³ +end + +required_biogeochemical_tracers(zoo::MicroAndMeso) = (required_biogeochemical_tracers(zoo.micro, :Z)..., + required_biogeochemical_tracers(zoo.meso, :M)...) + +@inline zooplankton_concentration(::Val{:Z}, i, j, k, fields) = @inbounds fields.Z[i, j, k] +@inline zooplankton_concentration(::Val{:M}, i, j, k, fields) = @inbounds fields.M[i, j, k] + +@inline parameterisation(::Val{:Z}, zoo::MicroAndMeso) = zoo.micro +@inline parameterisation(::Val{:M}, zoo::MicroAndMeso) = zoo.meso + +@inline predator_parameterisation(val_name, zoo) = nothing +@inline predator_parameterisation(::Val{:Z}, zoo::MicroAndMeso) = zoo.meso + +@inline predator_name(val_name, zoo) = nothing +@inline predator_name(::Val{:Z}, zoo::MicroAndMeso) = Val(:M) + +@inline grazing(zoo, ::Val{:M}, ::Val{:Z}, i, j, k, grid, args...) = zero(grid) + +@inline function (bgc::PISCES{<:Any, MicroAndMeso})(i, j, k, grid, val_name{Val{:Z}, Val{:M}}, clock, fields) + zoo = parameterisaion(bgc.zooplankton) + + growth_death = growth_death(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + # M preying on Z + predator_zoo = predator_parameterisation(val_name, bgc.zooplankton) + val_predator_name = predator_name(val_name, bgc.zooplankton) + + grazing = grazing(predator_zoo, val_predator_name, val_name, i, j, k, grid, bgc, clock, fields) + + return growth_death - grazing +end + +@inline grazing(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields) = + (grazing(zoo.micro, Val(:Z), val_prey_name, i, j, k, grid, bgc, clock, fields) + + grazing(zoo.meso, Val(:M), val_prey_name, i, j, k, grid, bgc, clock, fields)) + +@inline flux_feeding(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields) = + (flux_feeding(zoo.micro, Val(:Z), val_prey_name, i, j, k, grid, bgc, clock, fields) + + flux_feeding(zoo.meso, Val(:M), val_prey_name, i, j, k, grid, bgc, clock, fields)) + +@inline inorganic_excretion(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + (inorganic_excretion(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) + + inorganic_excretion(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields)) + +@inline organic_excretion(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + (organic_excretion(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) + + organic_excretion(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields)) + +@inline non_assimilated_iron(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + (non_assimilated_iron(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) + + non_assimilated_iron(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields)) + +@inline upper_trophic_excretion(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + upper_trophic_excretion(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) + +@inline upper_trophic_respiration(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + upper_trophic_respiration(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) + +@inline upper_trophic_fecal_production(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + upper_trophic_fecal_production(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) + +@inline function bacteria_concentration(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) + bZ = zoo.microzooplankton_bacteria_concentration + bM = zoo.mesozooplankton_bacteria_concentration + a = zoo.bacteria_concentration_depth_exponent + + z = znode(i, j, k, grid, Center(), Center(), Center()) + + zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] + zₑᵤ = @inbounds fields.zₑᵤ[i, j, k] + + Z = @inbounds fields.Z[i, j, k] + M = @inbounds fields.M[i, j, k] + + zₘ = min(zₘₓₗ, zₑᵤ) + + surface_bacteria = min(4, bZ * Z + bM * M) + + depth_factor = (zₘ / z) ^ a + + return ifelse(z >= zₘ, 1, depth_factor) * surface_bacteria +end + +@inline function bacteria_activity(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) + K_DOC = zoo.doc_half_saturation_for_bacterial_activity + K_NO₃ = zoo.nitrate_half_saturation_for_bacterial_activity + K_NH₄ = zoo.ammonia_half_saturation_for_bacterial_activity + K_PO₄ = zoo.phosphate_half_saturation_for_bacterial_activity + K_Fe = zoo.iron_half_saturation_for_bacterial_activity + + NH₄ = @inbounds fields.NH₄[i, j, k] + NO₃ = @inbounds fields.NO₃[i, j, k] + PO₄ = @inbounds fields.PO₄[i, j, k] + Fe = @inbounds fields.Fe[i, j, k] + DOC = @inbounds fields.DOC[i, j, k] + + DOC_limit = DOC / (DOC + K_DOC) + + L_N = (K_NO₃ * NH₄ + K_NH₄ * NO₃) / (K_NO₃ * K_NH₄ + K_NO₃ * NH₄ + K_NH₄ * NO₃) + + L_PO₄ = PO₄ / (PO₄ + K_PO₄) + + L_Fe = Fe / (Fe + K_Fe) + + # assuming typo in paper otherwise it doesn't make sense to formulate L_NH₄ like this + limiting_quota = min(L_N, L_PO₄, L_Fe) + + return limiting_quota * DOC_limit +end + +@inline calcite_loss(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields) = + (calcite_loss(zoo.micro, Val(:Z), val_prey_name, i, j, k, grid, bgc, clock, fields) + + calcite_loss(zoo.meso, Val(:M), val_prey_name, i, j, k, grid, bgc, clock, fields)) + +@inline upper_trophic_iron_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = + upper_trophic_respiration(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) * zoo.meso.iron_ratio \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl new file mode 100644 index 000000000..d7cb1226b --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl @@ -0,0 +1,37 @@ + +@inline function upper_trophic_excretion(zoo, val_name, i, j, k, grid, bgc, clock, fields) + γ = zoo.dissolved_excretion_fraction + + R = upper_trophic_respiration_product(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + return (1 - γ) * R +end + +@inline function upper_trophic_respiration(zoo, val_name, i, j, k, grid, bgc, clock, fields) + γ = zoo.dissolved_excretion_fraction + + R = upper_trophic_respiration_product(zoo, val_name, i, j, k, grid, bgc, clock, fields) + + return γ * R +end + +@inline upper_trophic_respiration_product(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) = + (1 - zoo.minimum_growth_efficiency - zoo.non_assililated_fraction) * upper_trophic_waste(zoo, val_name, i, j, k, grid, bgc, clock, fields) + +@inline upper_trophic_fecal_production(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) = + zoo.non_assililated_fraction * upper_trophic_waste(zoo, val_name, i, j, k, grid, bgc, clock, fields) + +@inline upper_trophic_fecal_iron_production(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) = + upper_trophic_fecal_production(zoo, val_name, i, j, k, grid, bgc, clock, fields) * zoo.iron_ratio + +@inline function upper_trophic_waste(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) + e₀ = zoo.minimum_growth_efficiency + b = zoo.temperature_sensetivity + m₀ = zoo.quadratic_mortality + + temperature_factor = b^T + + I = zooplankton_concentration(val_name, i, j, k, fields) + + return 1 / (1 - e₀) * m₀ * temperature_factor * I^2 +end diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/zooplankton.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/zooplankton.jl new file mode 100644 index 000000000..86694c28b --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/zooplankton.jl @@ -0,0 +1,17 @@ +module Zooplankton + +export MicroAndMezoZooplankton, QualityDependantZooplankton, MicroAndMeso + +using OceanBioME.Models.PISCESModel: anoxia_factor, PISCES + +import Oceananigans.Biogeochemistry: required_biogeochemical_tracers +import OceanBioME.Models.PISCESModel: mortality + +function edible_flux_rate end +function edible_iron_flux_rate end + +include("food_quality_dependant.jl") +include("micro_and_meso.jl") +include("defaults.jl") + +end # module \ No newline at end of file From 9d3082f67d51f267f540ba6023d46e45e88d4aa6 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Tue, 24 Sep 2024 16:47:27 +0100 Subject: [PATCH 03/33] so so many changes, mainly to make box models work with discrete models --- src/BoxModel/timesteppers.jl | 11 ++- .../AdvectedPopulations/PISCES/PISCES.jl | 38 ++++---- .../AdvectedPopulations/PISCES/common.jl | 1 + .../PISCES/coupling_utils.jl | 42 +++++++++ .../dissolved_organic_carbon.jl | 52 ++++++----- .../dissolved_organic_matter.jl | 8 +- .../PISCES/generic_functions.jl | 3 +- .../PISCES/inorganic_carbon.jl | 37 ++++---- .../AdvectedPopulations/PISCES/iron/iron.jl | 9 +- .../PISCES/iron/simple_iron.jl | 38 ++++---- .../PISCES/nitrogen/nitrate_ammonia.jl | 43 +++++----- .../PISCES/nitrogen/nitrogen.jl | 6 +- .../AdvectedPopulations/PISCES/oxygen.jl | 34 +++++--- .../particulate_organic_matter/calcite.jl | 49 ++--------- .../particulate_organic_matter/carbon.jl | 46 +++++----- .../PISCES/particulate_organic_matter/iron.jl | 77 +++++++++-------- .../micro_meso_zoo_coupling.jl | 41 +++++---- .../nano_diatom_coupling.jl | 86 +++++++++++++------ .../particulate_organic_matter.jl | 10 ++- .../particulate_organic_matter/silicate.jl | 19 ++-- .../two_size_class.jl | 26 +++--- .../AdvectedPopulations/PISCES/phosphate.jl | 16 ++-- .../PISCES/phytoplankton/growth_rate.jl | 35 ++++---- .../PISCES/phytoplankton/mixed_mondo.jl | 76 ++++++++-------- .../phytoplankton/mixed_mondo_nano_diatoms.jl | 56 ++++++------ .../PISCES/phytoplankton/nano_and_diatoms.jl | 37 ++++---- .../phytoplankton/nutrient_limitation.jl | 8 +- .../PISCES/phytoplankton/phytoplankton.jl | 4 +- .../PISCES/phytoplankton/waste.jl | 7 -- .../AdvectedPopulations/PISCES/silicate.jl | 12 ++- .../PISCES/zooplankton/defaults.jl | 39 +++++---- .../zooplankton/food_quality_dependant.jl | 83 +++++++++--------- .../PISCES/zooplankton/grazing_waste.jl | 42 ++++----- .../PISCES/zooplankton/iron_grazing.jl | 12 +-- .../PISCES/zooplankton/micro_and_meso.jl | 80 +++++++++-------- .../PISCES/zooplankton/mortality_waste.jl | 27 +++--- .../PISCES/zooplankton/zooplankton.jl | 6 +- src/Models/Sediments/Sediments.jl | 2 +- src/Models/Sediments/coupled_timesteppers.jl | 10 ++- src/OceanBioME.jl | 86 +++++++++++-------- src/Particles/Particles.jl | 11 ++- 41 files changed, 748 insertions(+), 577 deletions(-) create mode 100644 src/Models/AdvectedPopulations/PISCES/coupling_utils.jl delete mode 100644 src/Models/AdvectedPopulations/PISCES/phytoplankton/waste.jl diff --git a/src/BoxModel/timesteppers.jl b/src/BoxModel/timesteppers.jl index ca586eee0..bb27dd774 100644 --- a/src/BoxModel/timesteppers.jl +++ b/src/BoxModel/timesteppers.jl @@ -1,5 +1,5 @@ using Oceananigans.Architectures: device -using Oceananigans.Biogeochemistry: update_tendencies!, biogeochemical_auxiliary_fields +using Oceananigans.Biogeochemistry: update_tendencies!, biogeochemical_auxiliary_fields, AbstractBiogeochemistry, AbstractContinuousFormBiogeochemistry using Oceananigans.Grids: nodes, Center using Oceananigans.TimeSteppers: rk3_substep_field!, store_field_tendencies!, RungeKutta3TimeStepper, QuasiAdamsBashforth2TimeStepper using Oceananigans.Utils: work_layout, launch! @@ -41,7 +41,7 @@ function compute_tendencies!(model::BoxModel, callbacks) for tracer in required_biogeochemical_tracers(model.biogeochemistry) forcing = @inbounds model.forcing[tracer] - @inbounds Gⁿ[tracer][1, 1, 1] = tracer_tendency(Val(tracer), model.biogeochemistry, forcing, model.clock.time, model.field_values, model.grid) + @inbounds Gⁿ[tracer][1, 1, 1] = tracer_tendency(Val(tracer), model.biogeochemistry, forcing, model.clock, model.fields, model.field_values, model.grid) end for callback in callbacks @@ -57,8 +57,11 @@ end @inline boxmodel_coordinate(::Nothing, grid) = zero(grid) @inline boxmodel_coordinate(nodes, grid) = @inbounds nodes[1] -@inline tracer_tendency(val_name, biogeochemistry, forcing, time, model_fields, grid) = - biogeochemistry(val_name, boxmodel_xyz(nodes(grid, Center(), Center(), Center()), grid)..., time, model_fields...) + forcing(time, model_fields...) +@inline tracer_tendency(val_name, biogeochemistry::AbstractContinuousFormBiogeochemistry, forcing, clock, model_fields, model_field_values, grid) = + biogeochemistry(val_name, boxmodel_xyz(nodes(grid, Center(), Center(), Center()), grid)..., clock.time, model_field_values...) + forcing(time, model_fields...) + +@inline tracer_tendency(val_name, biogeochemistry::AbstractBiogeochemistry, forcing, clock, model_fields, model_field_values, grid) = + biogeochemistry(1, 1, 1, grid, val_name, clock, model_fields) + forcing(clock, model_fields) function rk3_substep!(model::BoxModel, Δt, γⁿ, ζⁿ) model_fields = prognostic_fields(model) diff --git a/src/Models/AdvectedPopulations/PISCES/PISCES.jl b/src/Models/AdvectedPopulations/PISCES/PISCES.jl index 7dcfe7e06..6cb2ef014 100644 --- a/src/Models/AdvectedPopulations/PISCES/PISCES.jl +++ b/src/Models/AdvectedPopulations/PISCES/PISCES.jl @@ -30,11 +30,11 @@ using Oceananigans: KernelFunctionOperation using Oceananigans.Fields: Field, TracerFields, CenterField, ZeroField, ConstantField, Center, Face using OceanBioME.Light: MultiBandPhotosyntheticallyActiveRadiation, default_surface_PAR, compute_euphotic_depth! -using OceanBioME: setup_velocity_fields, show_sinking_velocities, Biogeochemistry, ScaleNegativeTracers +using OceanBioME: setup_velocity_fields, show_sinking_velocities, Biogeochemistry, DiscreteBiogeochemistry, ScaleNegativeTracers using OceanBioME.BoxModels: BoxModel using OceanBioME.Models.CarbonChemistryModel: CarbonChemistry -using Oceananigans.Biogeochemistry: AbstractContinuousFormBiogeochemistry +using Oceananigans.Biogeochemistry: AbstractBiogeochemistry using Oceananigans.Fields: set! using Oceananigans.Grids: φnodes, RectilinearGrid @@ -50,7 +50,7 @@ import OceanBioME: maximum_sinking_velocity import Base: show, summary -struct PISCES{PP, ZP, DM, PM, NI, FE, SI, OX, PO, CA, CE, FT, LA, DL, ML, EU, MS, VD, MP, CC, CS, SS} <: AbstractBiogeochemistry +struct PISCES{PP, ZP, DM, PM, NI, FE, SI, OX, PO, IC, FT, LA, DL, ML, EU, MS, VD, MP, CC, CS, SS} <: AbstractBiogeochemistry phytoplankton :: PP zooplankton :: ZP @@ -64,8 +64,7 @@ struct PISCES{PP, ZP, DM, PM, NI, FE, SI, OX, PO, CA, CE, FT, LA, DL, ML, EU, MS oxygen :: OX phosphate :: PO - calcite :: CA - carbon_system :: CE + inorganic_carbon :: IC first_anoxia_threshold :: FT second_anoxia_threshold :: FT @@ -93,15 +92,15 @@ struct PISCES{PP, ZP, DM, PM, NI, FE, SI, OX, PO, CA, CE, FT, LA, DL, ML, EU, MS end @inline required_biogeochemical_tracers(bgc::PISCES) = - (required_biogeochemical_tracers(bgc.zooplankton)..., - required_biogeochemical_tracers(bgc.phytoplankon)..., + (required_biogeochemical_tracers(bgc.phytoplankton)..., + required_biogeochemical_tracers(bgc.zooplankton)..., required_biogeochemical_tracers(bgc.dissolved_organic_matter)..., required_biogeochemical_tracers(bgc.particulate_organic_matter)..., required_biogeochemical_tracers(bgc.nitrogen)..., required_biogeochemical_tracers(bgc.phosphate)..., required_biogeochemical_tracers(bgc.iron)..., - required_biogeochemical_tracers(bgc.silicate), - required_biogeochemical_tracers(bgc.carbon_system)... + required_biogeochemical_tracers(bgc.silicate)..., + required_biogeochemical_tracers(bgc.inorganic_carbon)..., required_biogeochemical_tracers(bgc.oxygen)..., :T, :S) @@ -121,6 +120,14 @@ end biogeochemical_drift_velocity(bgc::PISCES, val_name) = biogeochemical_drift_velocity(bgc.particulate_organic_matter, val_name) +(bgc::PISCES)(i, j, k, grid, val_name, clock, fields, auxiliary_fields) = zero(grid) + +(bgc::DiscreteBiogeochemistry{<:PISCES})(i, j, k, grid, val_name, clock, fields) = + bgc.underlying_biogeochemistry(i, j, k, grid, val_name, clock, fields, biogeochemical_auxiliary_fields(bgc)) + +include("common.jl") +include("generic_functions.jl") + include("zooplankton/zooplankton.jl") using .Zooplankton @@ -161,6 +168,8 @@ include("inorganic_carbon.jl") using .InorganicCarbons +include("coupling_utils.jl") + """ PISCES(; grid, @@ -266,7 +275,7 @@ was desired a way to specify arbitary tracers for arguments would be required. """ function PISCES(; grid, phytoplankton = MixedMondoNanoAndDiatoms(), - zooplankton = MicroAndMezoZooplankton(), + zooplankton = MicroAndMesoZooplankton(), dissolved_organic_matter = DissolvedOrganicCarbon(), particulate_organic_matter = TwoCompartementCarbonIronParticles(), @@ -276,7 +285,7 @@ function PISCES(; grid, oxygen = Oxygen(), phosphate = Phosphate(), - carbon_system = InorganicCarbon(), + inorganic_carbon = InorganicCarbon(), # from Aumount 2005 rather than 2015 since it doesn't work the other way around first_anoxia_thresehold = 6.0, @@ -344,17 +353,16 @@ function PISCES(; grid, end # just incase we're in the default state with no closure model - # this highlights that the darkness term for phytoplankton growth is obviously wrong because not all phytoplankon + # this highlights that the darkness term for phytoplankton growth is obviously wrong because not all phytoplankton # cells spend an infinite amount of time in the dark if the diffusivity is zero, it should depend on where they are... if !(mean_mixed_layer_vertical_diffusivity isa ConstantField) set!(mean_mixed_layer_vertical_diffusivity, 1) end - underlying_biogeochemistry = PISCES(nanophytoplankton, diatoms, - microzooplankton, mesozooplankton, + underlying_biogeochemistry = PISCES(phytoplankton, zooplankton, dissolved_organic_matter, particulate_organic_matter, nitrogen, iron, silicate, oxygen, phosphate, - calcite, carbon_system, + inorganic_carbon, first_anoxia_thresehold, second_anoxia_thresehold, nitrogen_redfield_ratio, phosphate_redfield_ratio, mixed_layer_shear, background_shear, diff --git a/src/Models/AdvectedPopulations/PISCES/common.jl b/src/Models/AdvectedPopulations/PISCES/common.jl index cabf5e0dd..25d92682b 100644 --- a/src/Models/AdvectedPopulations/PISCES/common.jl +++ b/src/Models/AdvectedPopulations/PISCES/common.jl @@ -27,6 +27,7 @@ struct PrescribedLatitude{FT} end @inline (pl::PrescribedLatitude)(y) = pl.latitude +@inline (pl::PrescribedLatitude)(i, j, k, grid) = pl.latitude @inline (::ModelLatitude)(φ) = φ @inline (::ModelLatitude)(i, j, k, grid) = φnode(i, j, k, grid, Center(), Center(), Center()) diff --git a/src/Models/AdvectedPopulations/PISCES/coupling_utils.jl b/src/Models/AdvectedPopulations/PISCES/coupling_utils.jl new file mode 100644 index 000000000..7684f5e9b --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/coupling_utils.jl @@ -0,0 +1,42 @@ +import OceanBioME.Models.Sediments: nitrogen_flux, carbon_flux, remineralisation_receiver, sinking_tracers + +# sediment models +@inline redfield(val_name, bgc::PISCES, tracers) = bgc.nitrogen_redfield_ratio + +@inline nitrogen_flux(i, j, k, grid, advection, bgc::PISCES, tracers) = bgc.nitrogen_redfield_ratio * carbon_flux(i, j, k, grid, advection, bgc, tracers) + +@inline carbon_flux(i, j, k, grid, advection, bgc::PISCES, tracers) = sinking_flux(i, j, k, grid, adveciton, bgc, Val(:POC), tracers) + + sinking_flux(i, j, k, grid, adveciton, bgc, Val(:GOC), tracers) + +@inline remineralisation_receiver(::PISCES) = :NH₄ + +@inline sinking_tracers(::PISCES) = (:POC, :GOC, :SFe, :BFe, :PSi, :CaCO₃) # please list them here + +# light attenuation model +@inline chlorophyll(::PISCES, model) = model.tracers.PChl + model.tracers.DChl + +# negative tracer scaling +# TODO: deal with remaining (PChl, DChl, O₂, Alk) - latter two should never be near zero +@inline function conserved_tracers(bgc::PISCES; ntuple = false) + carbon = (:P, :D, :Z, :M, :DOC, :POC, :GOC, :DIC, :CaCO₃) + + # iron ratio for DOC might be wrong + iron = (tracers = (:PFe, :DFe, :Z, :M, :SFe, :BFe, :Fe), + scalefactors = (1, 1, bgc.zooplankton.micro.iron_ratio, bgc.zooplankton.micro.iron_ratio, 1, 1, 1)) + + θ_PO₄ = bgc.phosphate_redfield_ratio + phosphate = (tracers = (:P, :D, :Z, :M, :DOC, :POC, :GOC, :PO₄), + scalefactors = (θ_PO₄, θ_PO₄, θ_PO₄, θ_PO₄, θ_PO₄, θ_PO₄, θ_PO₄, 1)) + + silicon = (:DSi, :Si, :PSi) + + θN = bgc.nitrogen_redfield_ratio + nitrogen = (tracers = (:NH₄, :NO₃, :P, :D, :Z, :M, :DOC, :POC, :GOC), + scalefactors = (1, 1, θN, θN, θN, θN, θN, θN, θN)) + + if ntuple + return (; carbon, iron, phosphate, silicon, nitrogen) + else + return (carbon, iron, phosphate, silicon, nitrogen) + end +end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_carbon.jl b/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_carbon.jl index 4b205b50a..551e2960d 100644 --- a/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_carbon.jl +++ b/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_carbon.jl @@ -1,3 +1,5 @@ +using Oceananigans.Grids: znode, Center + """ DissolvedOrganicCarbon @@ -15,19 +17,24 @@ end required_biogeochemical_tracers(::DissolvedOrganicCarbon) = tuple(:DOC) -@inline function (bgc::PISCES{<:Any, <:Any, DissolvedOrganicCarbon})(i, j, k, grid, ::Val{:DOC}, clock, fields) - phytoplankton_exudate = dissolved_exudate(bgc.phytoplankton, bgc, i, j, k, grid, bgc, clock, fields) - upper_trophic_exudate = upper_trophic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) - grazing_waste = organic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) - particulate_breakdown = degredation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) - dissolved_breakdown = degredation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) - aggrgation_to_particles, = aggregation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) +@inline function (bgc::PISCES{<:Any, <:Any, <:DissolvedOrganicCarbon})(i, j, k, grid, ::Val{:DOC}, clock, fields, auxiliary_fields) + phytoplankton_exudate = dissolved_exudate(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + upper_trophic_exudate = upper_trophic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + grazing_waste = organic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + particulate_breakdown = degredation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + dissolved_breakdown = degredation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + aggregation_to_particles, = aggregation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (phytoplankton_exudate + upper_trophic_exudate + grazing_waste + particulate_breakdown - dissolved_breakdown - aggregation_to_particles) end -@inline function degredation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields) +@inline function degredation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields, auxiliary_fields) Bact_ref = dom.reference_bacteria_concentration b = dom.temperature_sensetivity λ = dom.remineralisation_rate @@ -37,14 +44,14 @@ end f = b^T - Bact = bacteria_concentration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + Bact = bacteria_concentration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - LBact = bacteria_activity(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + LBact = bacteria_activity(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return λ * f * LBact * Bact / Bact_ref * DOC # differes from Aumont 2015 since the dimensions don't make sense end -@inline function aggregation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields) +@inline function aggregation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields, auxiliary_fields) a₁, a₂, a₃, a₄, a₅ = dom.aggregation_parameters backgroound_shear = bgc.background_shear @@ -52,7 +59,7 @@ end z = znode(i, j, k, grid, Center(), Center(), Center()) - zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] + zₘₓₗ = @inbounds auxiliary_fields.zₘₓₗ[i, j, k] DOC = @inbounds fields.DOC[i, j, k] POC = @inbounds fields.POC[i, j, k] @@ -67,10 +74,13 @@ end return Φ₁ + Φ₂ + Φ₃, Φ₁, Φ₂, Φ₃ end -@inline function aggregation_of_colloidal_iron(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields) - _, Φ₁, Φ₂, Φ₃ = aggregation(dom, i, j, k, grid, bgc, clock, fields) +@inline function aggregation_of_colloidal_iron(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + _, Φ₁, Φ₂, Φ₃ = aggregation(dom, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + Fe = @inbounds fields.Fe[i, j, k] + DOC = @inbounds fields.DOC[i, j, k] - Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields) + Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields, auxiliary_fields) ligand_iron = Fe - Fe′ colloidal_iron = 0.5 * ligand_iron @@ -80,22 +90,22 @@ end return CgFe1 + CgFe2, CgFe1, CgFe2 end -@inline function oxic_remineralisation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields) +@inline function oxic_remineralisation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields, auxiliary_fields) O₂ = @inbounds fields.O₂[i, j, k] ΔO₂ = anoxia_factor(bgc, O₂) - degredation = degredation(dom, i, j, k, grid, bgc, clock, fields) + total_degredation = degredation(dom, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - return (1 - ΔO₂) * degredation + return (1 - ΔO₂) * total_degredation end -@inline function anoxic_remineralisation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields) +@inline function anoxic_remineralisation(dom::DissolvedOrganicCarbon, i, j, k, grid, bgc, clock, fields, auxiliary_fields) O₂ = @inbounds fields.O₂[i, j, k] ΔO₂ = anoxia_factor(bgc, O₂) - degredation = degredation(dom, i, j, k, grid, bgc, clock, fields) + total_degredation = degredation(dom, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - return ΔO₂ * degredation + return ΔO₂ * total_degredation end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_matter.jl b/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_matter.jl index 00f8fc7b3..9273a479e 100644 --- a/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_matter.jl +++ b/src/Models/AdvectedPopulations/PISCES/dissolved_organic_matter/dissolved_organic_matter.jl @@ -2,9 +2,13 @@ module DissolvedOrganicMatter export DissolvedOrganicCarbon -using OceanBioME.Models.PISCESModel: degredation, aggregation, PISCES +using Oceananigans.Units + +using OceanBioME.Models.PISCESModel: + degredation, aggregation, PISCES, free_iron, anoxia_factor using OceanBioME.Models.PISCESModel.Phytoplankton: dissolved_exudate -using OceanBioME.Models.PISCESModel.Zooplankton: organic_excretion, upper_trophic_excretion +using OceanBioME.Models.PISCESModel.Zooplankton: + organic_excretion, upper_trophic_excretion, bacteria_concentration, bacteria_activity import Oceananigans.Biogeochemistry: required_biogeochemical_tracers import OceanBioME.Models.PISCESModel: degredation, aggregation diff --git a/src/Models/AdvectedPopulations/PISCES/generic_functions.jl b/src/Models/AdvectedPopulations/PISCES/generic_functions.jl index 6465ae169..b573ef24f 100644 --- a/src/Models/AdvectedPopulations/PISCES/generic_functions.jl +++ b/src/Models/AdvectedPopulations/PISCES/generic_functions.jl @@ -3,4 +3,5 @@ function degredation end function aggregation end function mortality end -function free_iron end \ No newline at end of file +function free_iron end +function flux_rate end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl b/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl index 061f819ce..140c472fa 100644 --- a/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl +++ b/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl @@ -2,6 +2,10 @@ module InorganicCarbons export InorganicCarbon +using Oceananigans.Units + +using OceanBioME.Models.PISCESModel: PISCES + using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: degredation using OceanBioME.Models.PISCESModel.ParticulateOrganicMatter: @@ -12,6 +16,8 @@ using OceanBioME.Models.PISCESModel.Phytoplankton: total_production using OceanBioME.Models.PISCESModel.Zooplankton: inorganic_excretion, upper_trophic_respiration +import Oceananigans.Biogeochemistry: required_biogeochemical_tracers + """ InorganicCarbon @@ -19,32 +25,33 @@ Default parameterisation for `DIC`` and `Alk`alinity evolution. """ struct InorganicCarbon end -const PISCESCarbon = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, InorganicCarbon} +required_biogeochemical_tracers(::InorganicCarbon) = (:DIC, :Alk) + +const PISCESCarbon = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:InorganicCarbon} -@inline function (bgc::PISCESCarbon)(i, j, k, grid, val_name::Val{:DIC}, clock, fields) - zooplankton_respiration = inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) +@inline function (bgc::PISCESCarbon)(i, j, k, grid, val_name::Val{:DIC}, clock, fields, auxiliary_fields) + zooplankton_respiration = inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - upper_trophic_respiration = upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + upper_trophic = upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - remineralisation = degredation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + remineralisation = degredation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - calcite_dissolution = calcite_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + calcite_diss = calcite_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - calcite_production = calcite_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + calcite_prod = calcite_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - consumption = total_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + consumption = total_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - return (zooplankton_respiration + upper_trophic_respiration + remineralisation - + calcite_dissolution - calcite_production - - consumption) + return (zooplankton_respiration + upper_trophic + remineralisation + + calcite_diss - calcite_prod - consumption) end -@inline function (bgc::PISCESCarbon)(i, j, k, grid, val_name::Val{:Alk}, clock, fields) +@inline function (bgc::PISCESCarbon)(i, j, k, grid, val_name::Val{:Alk}, clock, fields, auxiliary_fields) θ = bgc.nitrogen_redfield_ratio - nitrate_production = bgc(i, j, k, grid, Val(:NO₃), clock, fields) - ammonia_production = bgc(i, j, k, grid, Val(:NH₄), clock, fields) - calcite_production = bgc(i, j, k, grid, Val(:CaCO₃), clock, fields) + nitrate_production = bgc(i, j, k, grid, Val(:NO₃), clock, fields, auxiliary_fields) + ammonia_production = bgc(i, j, k, grid, Val(:NH₄), clock, fields, auxiliary_fields) + calcite_production = bgc(i, j, k, grid, Val(:CaCO₃), clock, fields, auxiliary_fields) # I think there are typos in Aumount 2015 but this is what it should be ( I think ???) return ammonia_production - nitrate_production - 2 * calcite_production diff --git a/src/Models/AdvectedPopulations/PISCES/iron/iron.jl b/src/Models/AdvectedPopulations/PISCES/iron/iron.jl index fd07884d3..259bb29ee 100644 --- a/src/Models/AdvectedPopulations/PISCES/iron/iron.jl +++ b/src/Models/AdvectedPopulations/PISCES/iron/iron.jl @@ -2,10 +2,12 @@ module Iron export SimpleIron +using Oceananigans.Units + using OceanBioME.Models.PISCESModel: PISCES using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: - aggregation_of_colloidal_iron, bacterial_iron_uptake + aggregation_of_colloidal_iron, degredation using OceanBioME.Models.PISCESModel.ParticulateOrganicMatter: iron_scavenging, iron_scavenging_rate, bacterial_iron_uptake @@ -15,14 +17,17 @@ using OceanBioME.Models.PISCESModel.Phytoplankton: uptake using OceanBioME.Models.PISCESModel.Zooplankton: non_assimilated_iron, upper_trophic_iron_waste +import Oceananigans.Biogeochemistry: required_biogeochemical_tracers import OceanBioME.Models.PISCESModel: free_iron include("simple_iron.jl") -@inline function free_iron(::SimpleIron, i, j, k, grid, bgc, clock, fields) +@inline function free_iron(::SimpleIron, i, j, k, grid, bgc, clock, fields, auxiliary_fields) DOC = @inbounds fields.DOC[i, j, k] Fe = @inbounds fields.Fe[i, j, k] + T = @inbounds fields.T[i, j, k] + # maybe some of these numbers should be parameters ligands = max(0.6, 0.09 * (DOC + 40) - 3) K = exp(16.27 - 1565.7 / max(T + 273.15, 5)) diff --git a/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl b/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl index 998b98f02..fdda45cee 100644 --- a/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl +++ b/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl @@ -7,48 +7,52 @@ the model) when the free iron concentration exeeds the ligand concentration at a rate modified by `excess_scavenging_enhancement`. """ @kwdef struct SimpleIron{FT} - excess_scavenging_enhancement :: FT = 1000 # unitless - maximum_ligand_concentration :: FT = 0.6 # μmol Fe / m³ - dissolved_ligand_ratio :: FT = 0.09 # μmol Fe / mmol C + excess_scavenging_enhancement :: FT = 1000.0 # unitless + maximum_ligand_concentration :: FT = 0.6 # μmol Fe / m³ + dissolved_ligand_ratio :: FT = 0.09 # μmol Fe / mmol C end -const SimpleIronPISCES = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, SimpleIron} +required_biogeochemical_tracers(::SimpleIron) = tuple(:Fe) -@inline function (bgc::SimpleIronPISCES)(i, j, k, grid, val_name::Val{:Fe}, clock, fields) - λ̄ = iron.excess_scavenging_enhancement +const SimpleIronPISCES = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:SimpleIron} - λFe = iron_scavenging_rate(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) +@inline function (bgc::SimpleIronPISCES)(i, j, k, grid, val_name::Val{:Fe}, clock, fields, auxiliary_fields) + λ̄ = bgc.iron.excess_scavenging_enhancement + + Fe = @inbounds fields.Fe[i, j, k] + + λFe = iron_scavenging_rate(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields) + Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - total_ligand_concentration = ligand_concentration(bgc.iron, i, j, k, grid, bgc, clock, fields) + total_ligand_concentration = ligand_concentration(bgc.iron, i, j, k, grid, bgc, clock, fields, auxiliary_fields) # terminal process which removes iron from the ocean ligand_aggregation = λ̄ * λFe * max(0, Fe - total_ligand_concentration) * Fe′ - colloidal_aggregation, = aggregation_of_colloidal_iron(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + colloidal_aggregation, = aggregation_of_colloidal_iron(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) # scavenging and bacterial uptake - scavenging = iron_scavenging(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + scavenging = iron_scavenging(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - BactFe = bacterial_iron_uptake(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + BactFe = bacterial_iron_uptake(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) # particle breakdown - small_particles = degredation(bgc.particulate_organic_matter, Val(:SFe), i, j, k, grid, bgc, clock, fields) + small_particles = degredation(bgc.particulate_organic_matter, Val(:SFe), i, j, k, grid, bgc, clock, fields, auxiliary_fields) # consumption - consumption = uptake(bgc.phytoplankton, val_name, i, j, k, grid, bgc, clock, fields) + consumption = uptake(bgc.phytoplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) # waste - grazing_waste = non_assimilated_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + grazing_waste = non_assimilated_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - upper_trophic_waste = upper_trophic_iron_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + upper_trophic_waste = upper_trophic_iron_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (small_particles + grazing_waste + upper_trophic_waste - consumption - ligand_aggregation - colloidal_aggregation - scavenging - BactFe) end -@inline function ligand_concentration(iron::SimpleIron, i, j, k, grid, bgc, clock, fields) +@inline function ligand_concentration(iron::SimpleIron, i, j, k, grid, bgc, clock, fields, auxiliary_fields) Lₜᵐᵃˣ = iron.maximum_ligand_concentration DOC = @inbounds fields.DOC[i, j, k] diff --git a/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrate_ammonia.jl b/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrate_ammonia.jl index cbfd3ccc3..cf4029382 100644 --- a/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrate_ammonia.jl +++ b/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrate_ammonia.jl @@ -8,73 +8,76 @@ phytoplankton. Additionally waste produces ammonia through various means. """ @kwdef struct NitrateAmmonia{FT} - maximum_nitrifcation_rate :: FT = 0.05 / day # 1 / s + maximum_nitrification_rate :: FT = 0.05 / day # 1 / s maximum_fixation_rate :: FT = 0.013 / day # mmol N / m³ (maybe shouldn't be a rate) iron_half_saturation_for_fixation :: FT = 0.1 # μmol Fe / m³ phosphate_half_saturation_for_fixation :: FT = 0.8 # mmol P / m³ light_saturation_for_fixation :: FT = 50.0 # W / m² end -const NitrateAmnmoniaPISCES = PISCES{<:Any, <:Any, <:Any, <:Any, NitrateAmmonia} +required_biogeochemical_tracers(::NitrateAmmonia) = (:NO₃, :NH₄) -@inline function (bgc::NitrateAmnmoniaPISCES)(i, j, k, grid, val_name::Val{:NO₃}, clock, fields) +const NitrateAmnmoniaPISCES = PISCES{<:Any, <:Any, <:Any, <:Any, <:NitrateAmmonia} + +@inline function (bgc::NitrateAmnmoniaPISCES)(i, j, k, grid, val_name::Val{:NO₃}, clock, fields, auxiliary_fields) θ = bgc.nitrogen_redfield_ratio - nitrif = nitrifcation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields) + nitrif = nitrification(bgc.nitrogen, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - remineralisation = oxic_remineralisation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + remineralisation = oxic_remineralisation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - consumption = uptake(bgc.phytoplankton, val_name, i, j, k, grid, bgc, clock, fields) + consumption = uptake(bgc.phytoplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return nitrif + θ * (remineralisation - consumption) end -@inline function (bgc::NitrateAmnmoniaPISCES)(i, j, k, grid, val_name::Val{:NH₄}, clock, fields) +@inline function (bgc::NitrateAmnmoniaPISCES)(i, j, k, grid, val_name::Val{:NH₄}, clock, fields, auxiliary_fields) θ = bgc.nitrogen_redfield_ratio - nitrif = nitrifcation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields) + nitrif = nitrification(bgc.nitrogen, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - remineralisation = anoxic_remineralisation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + remineralisation = anoxic_remineralisation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - consumption = uptake(bgc.phytoplankton, val_name, i, j, k, grid, bgc, clock, fields) + consumption = uptake(bgc.phytoplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - grazing_waste = inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + grazing_waste = inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - upper_trophic_waste = upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + upper_trophic_waste = upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - fixation = nitrogen_fixation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields) + fixation = nitrogen_fixation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return fixation + θ * (remineralisation + grazing_waste + upper_trophic_waste - consumption) - nitrif end -@inline function nitrification(nitrogen, i, j, k, grid, bgc, clock, fields) - λ = nitrogen.maximum_nitrifcation_rate +@inline function nitrification(nitrogen, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + λ = nitrogen.maximum_nitrification_rate O₂ = @inbounds fields.O₂[i, j, k] - PAR = @inbounds fields.mean_mixed_layer_light[i, j, k] NH₄ = @inbounds fields.NH₄[i, j, k] + PAR = @inbounds auxiliary_fields.mixed_layer_PAR[i, j, k] + ΔO₂ = anoxia_factor(bgc, O₂) return λ * NH₄ / (1 + PAR) * (1 - ΔO₂) end -@inline function nitrogen_fixation(nitrogen, i, j, k, grid, bgc, clock, fields) +@inline function nitrogen_fixation(nitrogen, i, j, k, grid, bgc, clock, fields, auxiliary_fields) Nₘ = nitrogen.maximum_fixation_rate K_Fe = nitrogen.iron_half_saturation_for_fixation K_PO₄ = nitrogen.phosphate_half_saturation_for_fixation E = nitrogen.light_saturation_for_fixation - PAR = @inbounds fields.PAR[i, j, k] + PAR = @inbounds auxiliary_fields.PAR[i, j, k] Fe = @inbounds fields.Fe[i, j, k] PO₄ = @inbounds fields.PO₄[i, j, k] - availability_limitation = nitrogen_availability_limitation(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + availability_limitation = nitrogen_availability_limitation(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) fixation_limit = ifelse(availability_limitation >= 0.8, 0.01, 1 - availability_limitation) - μ = base_production_rate(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + μ = base_production_rate(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) growth_requirment = max(0, μ - 2.15) diff --git a/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrogen.jl b/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrogen.jl index 21daf42d4..84ff28ddd 100644 --- a/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrogen.jl +++ b/src/Models/AdvectedPopulations/PISCES/nitrogen/nitrogen.jl @@ -2,11 +2,15 @@ module Nitrogen export NitrateAmmonia +using Oceananigans.Units + using OceanBioME.Models.PISCESModel: anoxia_factor, PISCES -using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: oxic_remineralisation, anoxic_remineralisaiton +using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: oxic_remineralisation, anoxic_remineralisation using OceanBioME.Models.PISCESModel.Phytoplankton: uptake, nitrogen_availability_limitation, base_production_rate using OceanBioME.Models.PISCESModel.Zooplankton: upper_trophic_respiration, inorganic_excretion +import Oceananigans.Biogeochemistry: required_biogeochemical_tracers + include("nitrate_ammonia.jl") end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/oxygen.jl b/src/Models/AdvectedPopulations/PISCES/oxygen.jl index c99754857..3756a9566 100644 --- a/src/Models/AdvectedPopulations/PISCES/oxygen.jl +++ b/src/Models/AdvectedPopulations/PISCES/oxygen.jl @@ -2,43 +2,49 @@ module OxygenModels export Oxygen +using Oceananigans.Units + using OceanBioME.Models.PISCESModel: PISCES using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: oxic_remineralisation, anoxic_remineralisation -using OceanBioME.Models.PISCESModel.Nitrogen: nitrifcation, nitrogen_fixation +using OceanBioME.Models.PISCESModel.Nitrogen: nitrification, nitrogen_fixation using OceanBioME.Models.PISCESModel.Phytoplankton: uptake using OceanBioME.Models.PISCESModel.Zooplankton: inorganic_excretion, upper_trophic_respiration +import Oceananigans.Biogeochemistry: required_biogeochemical_tracers + @kwdef struct Oxygen{FT} ratio_for_respiration :: FT = 133/122 # mol O₂ / mol C - ratio_for_nitrifcation :: FT = 32/122 # mol O₂ / mol C + ratio_for_nitrification :: FT = 32/122 # mol O₂ / mol C end -const PISCESOxygen = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, Oxygen} +required_biogeochemical_tracers(::Oxygen) = tuple(:O₂) + +const PISCESOxygen = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Oxygen} -@inline function (bgc::PISCESOxygen)(i, j, k, grid, val_name::Val{:O₂}, clock, fields) - θ_resp = oxy.ratio_for_respiration - θ_nitrif = oxy.ratio_for_nitrifcation +@inline function (bgc::PISCESOxygen)(i, j, k, grid, val_name::Val{:O₂}, clock, fields, auxiliary_fields) + θ_resp = bgc.oxygen.ratio_for_respiration + θ_nitrif = bgc.oxygen.ratio_for_nitrification - zooplankton = θ_resp * inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + zooplankton = θ_resp * inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - upper_trophic = θ_resp * upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + upper_trophic = θ_resp * upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - remineralisation = ((θ_resp + θ_nitrif) * oxic_remineralisation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) - + θ_resp * anoxic_remineralisaiton(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields)) + remineralisation = ((θ_resp + θ_nitrif) * oxic_remineralisation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + θ_resp * anoxic_remineralisation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields)) - ammonia_photosynthesis = θ_resp * uptake(bgc.phytoplankton, Val(:NH₄), i, j, k, grid, bgc, clock, fields) - nitrate_photosynthesis = (θ_resp + θ_nitrif) * uptake(bgc.phytoplankton, Val(:NO₃), i, j, k, grid, bgc, clock, fields) + ammonia_photosynthesis = θ_resp * uptake(bgc.phytoplankton, Val(:NH₄), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + nitrate_photosynthesis = (θ_resp + θ_nitrif) * uptake(bgc.phytoplankton, Val(:NO₃), i, j, k, grid, bgc, clock, fields, auxiliary_fields) # I think (?) that we need the redfield raito here since θ_nitrif is per oxygen - nitrif = θ_nitrif * nitrification(bgc.nitrogen, i, j, k, grid, bgc, clock, fields) / bgc.nitrogen_redfield_ratio + nitrif = θ_nitrif * nitrification(bgc.nitrogen, i, j, k, grid, bgc, clock, fields, auxiliary_fields) / bgc.nitrogen_redfield_ratio - fixation = θ_nitrif * nitrogen_fixation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields) / bgc.nitrogen_redfield_ratio + fixation = θ_nitrif * nitrogen_fixation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields, auxiliary_fields) / bgc.nitrogen_redfield_ratio return (ammonia_photosynthesis + nitrate_photosynthesis + fixation - zooplankton - upper_trophic - nitrif) diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/calcite.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/calcite.jl index eb0e17661..1cd6e8857 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/calcite.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/calcite.jl @@ -1,50 +1,17 @@ -@inline function (bgc::PISCES{<:Any, <:Any, <:Any, TwoCompartementCarbonIronParticles})(i, j, k, - grid, - val_name::Val{:CaCO₃}, - clock, fields) - R = rain_ratio(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) - - phytoplankton_production = calcite_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) - - return R * phytoplankton_production - dissolution -end - -@inline function rain_ratio(pom::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) - r = pom.base_rain_ratio - - T = @inbounds fields.T[i, j, k] - PAR = @inbounds fields.PAR[i, j, k] - zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] - - L_CaCO₃ = coccolithophore_nutrient_limitation(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) +@inline function (bgc::TwoCompartementPOCPISCES)(i, j, k, grid, val_name::Val{:CaCO₃}, clock, fields, auxiliary_fields) + phytoplankton_production = calcite_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - phytoplankton_concentration_factor = - coccolithophore_phytoplankton_factor(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) - - low_light_factor = max(0, PAR - 1) / (4 + PAR) - high_light_factor = 30 / (30 + PAR) - - # modified from origional as it goes negative and does not achieve goal otherwise - low_temperature_factor = max(0, T / (T + 0.1)) - high_temperature_factor = 1 + exp(-(T - 10)^2 / 25) - - depth_factor = min(1, -50/zₘₓₗ) - - return (r * L_CaCO₃ - * phytoplankton_concentration_factor - * low_light_factor - * high_light_factor - * low_temperature_factor - * high_temperature_factor - * depth_factor) + dissolution = calcite_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + return phytoplankton_production - dissolution end -@inline function calcite_dissolution(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) +@inline function calcite_dissolution(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields, auxiliary_fields) λ = poc.base_calcite_dissolution_rate nca = poc.calcite_dissolution_exponent - Ω = @inbounds fields.Ω[i, j, k] - CaCO₃ = @inbounds fields.CaCO₃[i, j, k] + Ω = @inbounds auxiliary_fields.Ω[i, j, k] + CaCO₃ = @inbounds fields.CaCO₃[i, j, k] ΔCaCO₃ = max(0, 1 - Ω) diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl index 6ad86f08f..ac625a862 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl @@ -1,56 +1,56 @@ # these are just completly different to eachother so not going to try and define a generic function -@inline function (bgc::PISCES{<:Any, <:Any, <:Any, TwoCompartementCarbonIronParticles})(i, j, k, grid, val_name::Val{:POC}, clock, fields) +@inline function (bgc::TwoCompartementPOCPISCES)(i, j, k, grid, val_name::Val{:POC}, clock, fields, auxiliary_fields) # gains - grazing_waste = small_non_assimilated_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + grazing_waste = small_non_assimilated_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - phytoplankton_mortality = small_mortality(bgc.phytoplankon, i, j, k, grid, bgc, clock, fields) + phytoplankton_mortality = small_mortality(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - zooplankton_mortality = small_mortality(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + zooplankton_mortality = small_mortality(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - _, Φ₁, _, Φ₃ = aggregation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + _, Φ₁, _, Φ₃ = aggregation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) dissolved_aggregation = Φ₁ + Φ₃ - large_breakdown = degredation(bgc.particulate_organic_matter, Val(:GOC), i, j, k, grid, bgc, clock, fields) + large_breakdown = degredation(bgc.particulate_organic_matter, Val(:GOC), i, j, k, grid, bgc, clock, fields, auxiliary_fields) # losses - grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields) + grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - small_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields) + small_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + dissolved_aggregation + large_breakdown - grazing - aggregation_to_large - small_breakdown) end -@inline function (bgc::PISCES{<:Any, <:Any, <:Any, TwoCompartementCarbonIronParticles})(i, j, k, grid, val_name::Val{:GOC}, clock, fields) +@inline function (bgc::TwoCompartementPOCPISCES)(i, j, k, grid, val_name::Val{:GOC}, clock, fields, auxiliary_fields) # gains - grazing_waste = large_non_assimilated_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + grazing_waste = large_non_assimilated_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - phytoplankton_mortality = large_mortality(bgc.phytoplankon, i, j, k, grid, bgc, clock, fields) + phytoplankton_mortality = large_mortality(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - zooplankton_mortality = large_mortality(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + zooplankton_mortality = large_mortality(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - upper_trophic_feces = upper_trophic_fecal_production(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + upper_trophic_feces = upper_trophic_fecal_production(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) # losses - grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields) + grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - large_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields) + large_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + upper_trophic_feces - grazing - large_breakdown) end -@inline degredation(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) = # for going to DOC - degredation(poc::TwoCompartementCarbonIronParticles, Val(:POC), i, j, k, grid, bgc, clock, fields) +@inline degredation(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = # for going to DOC + degredation(poc::TwoCompartementCarbonIronParticles, Val(:POC), i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:POC}, i, j, k, grid, bgc, clock, fields) = - @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields) * fields.POC[i, j, k] +@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:POC}, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields, auxiliary_fields) * fields.POC[i, j, k] -@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:GOC}, i, j, k, grid, bgc, clock, fields) = - @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields) * fields.GOC[i, j, k] \ No newline at end of file +@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:GOC}, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields, auxiliary_fields) * fields.GOC[i, j, k] \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl index c8e228d85..29ad4d078 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl @@ -1,5 +1,5 @@ -@inline function (bgc::TwoCompartementPOCPISCES)(i, j, k, grid, val_name::Val{:SFe}, clock, fields) +@inline function (bgc::TwoCompartementPOCPISCES)(i, j, k, grid, val_name::Val{:SFe}, clock, fields, auxiliary_fields) POC = @inbounds fields.POC[i, j, k] SFe = @inbounds fields.SFe[i, j, k] @@ -7,44 +7,44 @@ # gains grazing_waste = - small_non_assimilated_iron_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + small_non_assimilated_iron_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) phytoplankton_mortality = - small_mortality_iron(bgc.phytoplankon, i, j, k, grid, bgc, clock, fields) + small_mortality_iron(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) zooplankton_mortality = - small_mortality_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + small_mortality_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) large_breakdown = - degredation(bgc.particulate_organic_matter, Val(:BFe), i, j, k, grid, bgc, clock, fields) + degredation(bgc.particulate_organic_matter, Val(:BFe), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - λFe = iron_scavenging_rate(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + λFe = iron_scavenging_rate(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields) + Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields, auxiliary_fields) scavenging = λFe * POC * Fe′ - κ = poc.small_fraction_of_bacterially_consumed_iron + κ = bgc.particulate_organic_matter.small_fraction_of_bacterially_consumed_iron - BactFe = bacterial_iron_uptake(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + BactFe = bacterial_iron_uptake(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) bacterial_assimilation = κ * BactFe - _, colloidal_aggregation = aggregation_of_colloidal_iron(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + _, colloidal_aggregation = aggregation_of_colloidal_iron(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) # losses - grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields) * θ + grazing = total_grazing(bgc.zooplankton, Val(:POC), i, j, k, grid, bgc, clock, fields, auxiliary_fields) * θ - aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) * θ + aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) * θ - small_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields) + small_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + large_breakdown + scavenging + bacterial_assimilation + colloidal_aggregation - grazing - aggregation_to_large - small_breakdown) end -@inline function (bgc::TwoCompartementPOCPISCES)(i, j, k, grid, val_name::Val{:BFe}, clock, fields) +@inline function (bgc::TwoCompartementPOCPISCES)(i, j, k, grid, val_name::Val{:BFe}, clock, fields, auxiliary_fields) POC = @inbounds fields.POC[i, j, k] SFe = @inbounds fields.SFe[i, j, k] GOC = @inbounds fields.GOC[i, j, k] @@ -54,47 +54,47 @@ end θB = BFe / (GOC + eps(0.0)) # gains - grazing_waste = large_non_assimilated_iron_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + grazing_waste = large_non_assimilated_iron_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - phytoplankton_mortality = large_mortality_iron(bgc.phytoplankon, i, j, k, grid, bgc, clock, fields) + phytoplankton_mortality = large_mortality_iron(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - zooplankton_mortality = large_mortality_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + zooplankton_mortality = large_mortality_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) * θS + aggregation_to_large = aggregation(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) * θS - upper_trophic_feces = upper_trophic_fecal_iron_production(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + upper_trophic_feces = upper_trophic_fecal_iron_production(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - λFe = iron_scavenging_rate(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + λFe = iron_scavenging_rate(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields) + Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields, auxiliary_fields) scavenging = λFe * GOC * Fe′ - κ = poc.small_fraction_of_bacterially_consumed_iron + κ = bgc.particulate_organic_matter.small_fraction_of_bacterially_consumed_iron - BactFe = bacterial_iron_uptake(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + BactFe = bacterial_iron_uptake(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) bacterial_assimilation = κ * BactFe - _, _, colloidal_aggregation = aggregation_of_colloidal_iron(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + _, _, colloidal_aggregation = aggregation_of_colloidal_iron(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) # losses - grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields) * θB + grazing = total_grazing(bgc.zooplankton, Val(:GOC), i, j, k, grid, bgc, clock, fields, auxiliary_fields) * θB - large_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields) + large_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + upper_trophic_feces + scavenging + bacterial_assimilation + colloidal_aggregation - grazing - large_breakdown) end -@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:SFe}, i, j, k, grid, bgc, clock, fields) = - @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields) * fields.SFe[i, j, k] +@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:SFe}, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields, auxiliary_fields) * fields.SFe[i, j, k] -@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:BFe}, i, j, k, grid, bgc, clock, fields) = - @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields) * fields.BFe[i, j, k] +@inline degredation(poc::TwoCompartementCarbonIronParticles, ::Val{:BFe}, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + @inbounds specific_degredation_rate(poc, i, j, k, grid, bgc, clock, fields, auxiliary_fields) * fields.BFe[i, j, k] -@inline function iron_scavenging_rate(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) +@inline function iron_scavenging_rate(pom::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields, auxiliary_fields) λ₀ = pom.minimum_iron_scavenging_rate λ₁ = pom.load_specific_iron_scavenging_rate @@ -106,28 +106,31 @@ end return λ₀ + λ₁ * (POC + GOC + CaCO₃ + PSi) end -@inline function bacterial_iron_uptake(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) +@inline function bacterial_iron_uptake(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields, auxiliary_fields) μ₀ = poc.maximum_bacterial_growth_rate b = poc.temperature_sensetivity θ = poc.maximum_iron_ratio_in_bacteria K = poc.iron_half_saturation_for_bacteria + T = @inbounds fields.T[i, j, k] + Fe = @inbounds fields.Fe[i, j, k] + μ = μ₀ * b^T - Bact = bacteria_concentration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + Bact = bacteria_concentration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - LBact = bacteria_activity(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + LBact = bacteria_activity(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return μ * LBact * θ * Fe / (Fe + K) * Bact end -@inline function iron_scavenging(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) +@inline function iron_scavenging(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields, auxiliary_fields) POC = @inbounds fields.POC[i, j, k] GOC = @inbounds fields.GOC[i, j, k] - λFe = iron_scavenging_rate(poc, i, j, k, grid, bgc, clock, fields) + λFe = iron_scavenging_rate(poc, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields) + Fe′ = free_iron(bgc.iron, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return λFe * (POC + GOC) * Fe′ end diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl index 0e02a8bd8..25f0affad 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl @@ -1,29 +1,32 @@ using OceanBioME.Models.PISCESModel.Zooplankton: non_assimilated_waste -@inline small_non_assimilated_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - non_assimilated_waste(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) +@inline small_non_assimilated_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + non_assimilated_waste(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline large_non_assimilated_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - non_assimilated_waste(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) +@inline large_non_assimilated_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + non_assimilated_waste(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline small_non_assimilated_iron_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - non_assimilated_iron_waste(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) +@inline small_non_assimilated_iron_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + non_assimilated_iron_waste(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline large_non_assimilated_iron_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - non_assimilated_iron_waste(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) +@inline large_non_assimilated_iron_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + non_assimilated_iron_waste(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline small_mortality(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - mortality(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) +@inline small_mortality(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + mortality(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline large_mortality(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - linear_mortality(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) +@inline large_mortality(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + linear_mortality(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline small_mortality_iron(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - mortality(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) * zoo.micro.iron_ratio +@inline small_mortality_iron(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + mortality(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields, auxiliary_fields) * zoo.micro.iron_ratio -@inline large_mortality_iron(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - linear_mortality(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) * zoo.meso.iron_ratio +@inline large_mortality_iron(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + linear_mortality(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) * zoo.meso.iron_ratio -@inline total_grazing(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields) = - (grazing(zoo, val_prey_name, i, j, k, grid, bgc, clock, fields) - + flux_feeding(zoo, val_prey_name, i, j, k, grid, bgc, clock, fields)) +@inline total_grazing(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (grazing(zoo, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + flux_feeding(zoo, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields)) + +@inline total_grazing(zoo::MicroAndMeso, val_prey_name::Val{:GOC}, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + flux_feeding(zoo, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl index 55f644fb0..446615c21 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl @@ -1,29 +1,29 @@ -@inline function small_mortality(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) - P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) +@inline function small_mortality(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - R = rain_ratio(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + R = rain_ratio(phyto, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - D_linear_mortality, = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields) + D_linear_mortality, = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (1 - R / 2) * (P_linear_mortality + P_quadratic_mortality) + D_linear_mortality / 2 end -@inline function large_mortality(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) - P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) +@inline function large_mortality(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - R = rain_ratio(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + R = rain_ratio(phyto, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - D_linear_mortality, D_quadratic_mortality = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields) + D_linear_mortality, D_quadratic_mortality = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields) return R / 2 * (P_linear_mortality + P_quadratic_mortality) + D_linear_mortality / 2 + D_quadratic_mortality end -@inline function small_mortality_iron(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) - P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) +@inline function small_mortality_iron(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - R = rain_ratio(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + R = rain_ratio(phyto, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - D_linear_mortality, = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields) + D_linear_mortality, = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields) P = @inbounds fields.P[i, j, k] PFe = @inbounds fields.PFe[i, j, k] @@ -36,12 +36,12 @@ end return (1 - R / 2) * (P_linear_mortality + P_quadratic_mortality) * θP + D_linear_mortality * θD / 2 end -@inline function large_mortality_iron(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) - P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) +@inline function large_mortality_iron(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + P_linear_mortality, P_quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - R = rain_ratio(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + R = rain_ratio(phyto, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - D_linear_mortality, D_quadratic_mortality = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields) + D_linear_mortality, D_quadratic_mortality = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields) P = @inbounds fields.P[i, j, k] PFe = @inbounds fields.PFe[i, j, k] @@ -54,8 +54,10 @@ end return R / 2 * (P_linear_mortality + P_quadratic_mortality) * θP + (D_linear_mortality / 2 + D_quadratic_mortality) * θD end -@inline function coccolithophore_nutrient_limitation(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) - _, _, L_PO₄, LN = phyto.nano(Val(:P), phyto.nano, i, j, k, grid, bgc, clock, fields) +@inline function coccolithophore_nutrient_limitation(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + _, _, L_PO₄, LN = phyto.nano.nutrient_limitation(Val(:P), i, j, k, grid, bgc, phyto.nano, clock, fields, auxiliary_fields) + + Fe = @inbounds fields.Fe[i, j, k] L_Fe = Fe / (Fe + 0.05) @@ -65,26 +67,58 @@ end return min(LN, L_Fe, L_PO₄) end -@inline coccolithophore_phytoplankton_factor(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) = +@inline coccolithophore_phytoplankton_factor(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = @inbounds max(one(grid), fields.P[i, j, k] / 2) -@inline function particulate_silicate_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) +@inline function particulate_silicate_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) D = @inbounds fields.D[i, j, k] DSi = @inbounds fields.DSi[i, j, k] θ = DSi / (D + eps(0.0)) - D_linear_mortality, D_quadratic_mortality = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields) + D_linear_mortality, D_quadratic_mortality = mortality(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - total_grazing = grazing(bgc.zooplankton, Val(:D), i, j, k, grid, bgc, clock, fields) + total_grazing = grazing(bgc.zooplankton, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (total_grazing + D_linear_mortality + D_quadratic_mortality) * θ end -@inline function calcite_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) - linear_mortality, quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) +@inline function calcite_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + R = rain_ratio(phyto, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + linear_mortality, quadratic_mortality = mortality(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + total_grazing_loss = calcite_loss(bgc.zooplankton, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + return R * (total_grazing_loss + (linear_mortality + quadratic_mortality) / 2) +end + +@inline function rain_ratio(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + r = phyto.base_rain_ratio + + T = @inbounds fields.T[i, j, k] + PAR = @inbounds auxiliary_fields.PAR[i, j, k] + zₘₓₗ = @inbounds auxiliary_fields.zₘₓₗ[i, j, k] + + L_CaCO₃ = coccolithophore_nutrient_limitation(phyto, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + phytoplankton_concentration_factor = + coccolithophore_phytoplankton_factor(phyto, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + low_light_factor = max(0, PAR - 1) / (4 + PAR) + high_light_factor = 30 / (30 + PAR) + + # modified from origional as it goes negative and does not achieve goal otherwise + low_temperature_factor = max(0, T / (T + 0.1)) + high_temperature_factor = 1 + exp(-(T - 10)^2 / 25) - total_grazing_loss = calcite_loss(bgc.zooplankton, Val(:P), i, j, k, grid, bgc, clock, fields) + depth_factor = min(1, -50/zₘₓₗ) - return total_grazing_loss + (linear_mortality + quadratic_mortality) / 2 + return (r * L_CaCO₃ + * phytoplankton_concentration_factor + * low_light_factor + * high_light_factor + * low_temperature_factor + * high_temperature_factor + * depth_factor) end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/particulate_organic_matter.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/particulate_organic_matter.jl index bc5bfad5f..f517cb0a2 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/particulate_organic_matter.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/particulate_organic_matter.jl @@ -2,15 +2,19 @@ module ParticulateOrganicMatter export TwoCompartementCarbonIronParticles -using OceanBioME.Models.PISCESModel: degredation, aggregation, free_iron, PISCES +using Oceananigans.Units + +using OceanBioME.Models.PISCESModel: + degredation, aggregation, free_iron, PISCES, anoxia_factor, mortality using OceanBioME.Models.PISCESModel.DissolvedOrganicMatter: aggregation_of_colloidal_iron using OceanBioME.Models.PISCESModel.Phytoplankton: dissolved_exudate, NanoAndDiatoms using OceanBioME.Models.PISCESModel.Zooplankton: organic_excretion, upper_trophic_excretion, grazing, MicroAndMeso, upper_trophic_fecal_production, - upper_trophic_fecal_iron_production, calcite_loss + upper_trophic_fecal_iron_production, calcite_loss, flux_feeding, linear_mortality, + non_assimilated_iron_waste, bacteria_concentration, bacteria_activity import Oceananigans.Biogeochemistry: required_biogeochemical_tracers, biogeochemical_drift_velocity -import OceanBioME.Models.PISCESModel: degredation, aggregation +import OceanBioME.Models.PISCESModel: degredation, aggregation, flux_rate import OceanBioME.Models.PISCESModel.Zooplankton: edible_flux_rate, edible_iron_flux_rate include("two_size_class.jl") diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/silicate.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/silicate.jl index 5272fdcaf..66c260c7b 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/silicate.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/silicate.jl @@ -1,14 +1,14 @@ -@inline function (bgc::PISCES{<:Any, <:Any, <:Any, TwoCompartementCarbonIronParticles})(i, j, k, grid, val_name::Val{:PSi}, clock, fields) +@inline function (bgc::TwoCompartementPOCPISCES)(i, j, k, grid, val_name::Val{:PSi}, clock, fields, auxiliary_fields) # this generalisation still assumes that we're only getting PSi from phytoplankton being grazed, will need changes if zooplankton get Si compartment - phytoplankton_production = particulate_silicate_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + phytoplankton_production = particulate_silicate_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - dissolution = particulate_silicate_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + dissolution = particulate_silicate_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return phytoplankton_production - dissolution end -@inline function particulate_silicate_dissolution(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) +@inline function particulate_silicate_dissolution(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields, auxiliary_fields) PSi = @inbounds fields.PSi[i, j, k] Si = @inbounds fields.Si[i, j, k] @@ -17,7 +17,7 @@ end λₗ = poc.fast_dissolution_rate_of_silicate λᵣ = poc.slow_dissolution_rate_of_silicate - χ = particulate_silicate_liable_fraction(poc, i, j, k, grid, bgc, clock, fields) + χ = particulate_silicate_liable_fraction(poc, i, j, k, grid, bgc, clock, fields, auxiliary_fields) λ₀ = χ * λₗ + (1 - χ) * λᵣ @@ -29,17 +29,18 @@ end return λ * PSi # assuming the Diss_Si is typo in Aumont 2015, consistent with Aumont 2005 end -@inline function particulate_silicate_liable_fraction(poc, i, j, k, grid, bgc, clock, fields) +@inline function particulate_silicate_liable_fraction(poc, i, j, k, grid, bgc, clock, fields, auxiliary_fields) χ₀ = poc.base_liable_silicate_fraction λₗ = poc.fast_dissolution_rate_of_silicate λᵣ = poc.slow_dissolution_rate_of_silicate z = znode(i, j, k, grid, Center(), Center(), Center()) - zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] - zₑᵤ = @inbounds fields.zₑᵤ[i, j, k] + zₘₓₗ = @inbounds auxiliary_fields.zₘₓₗ[i, j, k] + zₑᵤ = @inbounds auxiliary_fields.zₑᵤ[i, j, k] - wGOC = ℑzᵃᵃᶜ(i, j, k, grid, fields.wGOC) # this isn't actually correct since wGOC isn't constant but nm + # this isn't actually correct since wGOC isn't constant but nm + wGOC = ℑzᵃᵃᶜ(i, j, k, grid, auxiliary_fields.wGOC) zₘ = min(zₘₓₗ, zₑᵤ) diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl index 64de5d09e..fc488d461 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl @@ -1,3 +1,4 @@ +using Oceananigans.Grids: znode, Center using Oceananigans.Operators: ℑzᵃᵃᶜ """ @@ -26,7 +27,6 @@ phytoplankton silicon). slow_dissolution_rate_of_silicate :: FT = 0.003/day # 1 / s # calcite - base_rain_ratio :: FT = 0.3 # base_calcite_dissolution_rate :: FT = 0.197 / day # 1 / s calcite_dissolution_exponent :: FT = 1.0 # @@ -36,17 +36,19 @@ phytoplankton silicon). maximum_bacterial_growth_rate :: FT = 0.6 / day # 1 / s end -const TwoCompartementPOCPISCES = PISCES{<:Any, <:Any, <:Any, TwoCompartementCarbonIronParticles} +const TwoCompartementPOCPISCES = PISCES{<:Any, <:Any, <:Any, <:TwoCompartementCarbonIronParticles} required_biogeochemical_tracers(::TwoCompartementCarbonIronParticles) = (:POC, :GOC, :SFe, :BFe, :PSi, :CaCO₃) -@inline edible_flux_rate(poc, i, j, k, grid, fields) = flux_rate(Val(:POC), i, j, k, grid, fields) + flux_rate(Val(:GOC), i, j, k, grid, fields) -@inline edible_iron_flux_rate(poc, i, j, k, grid, fields) = flux_rate(Val(:SFe), i, j, k, grid, fields) + flux_rate(Val(:BFe), i, j, k, grid, fields) +@inline edible_flux_rate(poc, i, j, k, grid, fields, auxiliary_fields) = + flux_rate(Val(:POC), i, j, k, grid, fields, auxiliary_fields) + flux_rate(Val(:GOC), i, j, k, grid, fields, auxiliary_fields) +@inline edible_iron_flux_rate(poc, i, j, k, grid, fields, auxiliary_fields) = + flux_rate(Val(:SFe), i, j, k, grid, fields, auxiliary_fields) + flux_rate(Val(:BFe), i, j, k, grid, fields, auxiliary_fields) -@inline flux_rate(::Val{:POC}, i, j, k, grid, fields) = @inbounds fields.POC[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, fields.wPOC) -@inline flux_rate(::Val{:GOC}, i, j, k, grid, fields) = @inbounds fields.GOC[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, fields.wGOC) -@inline flux_rate(::Val{:SFe}, i, j, k, grid, fields) = @inbounds fields.SFe[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, fields.wPOC) -@inline flux_rate(::Val{:BFe}, i, j, k, grid, fields) = @inbounds fields.BFe[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, fields.wGOC) +@inline flux_rate(::Val{:POC}, i, j, k, grid, fields, auxiliary_fields) = @inbounds fields.POC[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, auxiliary_fields.wPOC) +@inline flux_rate(::Val{:GOC}, i, j, k, grid, fields, auxiliary_fields) = @inbounds fields.GOC[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, auxiliary_fields.wGOC) +@inline flux_rate(::Val{:SFe}, i, j, k, grid, fields, auxiliary_fields) = @inbounds fields.SFe[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, auxiliary_fields.wPOC) +@inline flux_rate(::Val{:BFe}, i, j, k, grid, fields, auxiliary_fields) = @inbounds fields.BFe[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, auxiliary_fields.wGOC) const small_particle_components = Union{Val{:POC}, Val{:SFe}} const large_particle_components = Union{Val{:GOC}, Val{:BFe}, Val{:PSi}, Val{:CaCO₃}} @@ -57,16 +59,15 @@ biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::small_p biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::large_particle_components) = (u = ZeroField(), v = ZeroField(), w = bgc.sinking_velocities.GOC) -@inline function aggregation(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields) +@inline function aggregation(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields, auxiliary_fields) a₁, a₂, a₃, a₄ = poc.aggregation_parameters backgroound_shear = bgc.background_shear mixed_layer_shear = bgc.mixed_layer_shear - z = znode(i, j, k, grid, Center(), Center(), Center()) - zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] + zₘₓₗ = @inbounds auxiliary_fields.zₘₓₗ[i, j, k] POC = @inbounds fields.POC[i, j, k] GOC = @inbounds fields.GOC[i, j, k] @@ -76,11 +77,12 @@ biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::large_p return shear * (a₁ * POC^2 + a₂ * POC * GOC) + a₃ * POC * GOC + a₄ * POC^2 end -@inline function specific_degredation_rate(poc::TwoCompartementParticulateOrganicMatter, i, j, k, grid, bgc, clock, fields) +@inline function specific_degredation_rate(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields, auxiliary_fields) λ₀ = poc.base_breakdown_rate b = poc.temperature_sensetivity O₂ = @inbounds fields.O₂[i, j, k] + T = @inbounds fields.T[i, k, k] ΔO₂ = anoxia_factor(bgc, O₂) diff --git a/src/Models/AdvectedPopulations/PISCES/phosphate.jl b/src/Models/AdvectedPopulations/PISCES/phosphate.jl index 26b870455..3a0221214 100644 --- a/src/Models/AdvectedPopulations/PISCES/phosphate.jl +++ b/src/Models/AdvectedPopulations/PISCES/phosphate.jl @@ -10,20 +10,24 @@ using OceanBioME.Models.PISCESModel.Phytoplankton: total_production using OceanBioME.Models.PISCESModel.Zooplankton: inorganic_excretion, upper_trophic_respiration +import Oceananigans.Biogeochemistry: required_biogeochemical_tracers + struct Phosphate end -const PISCESPhosphate = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, Phosphate} +required_biogeochemical_tracers(::Phosphate) = tuple(:PO₄) + +const PISCESPhosphate = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Phosphate} -@inline function (bgc::PISCESPhosphate)(i, j, k, grid, val_name::Val{:PO₄}, clock, fields) +@inline function (bgc::PISCESPhosphate)(i, j, k, grid, val_name::Val{:PO₄}, clock, fields, auxiliary_fields) θ = bgc.phosphate_redfield_ratio - phytoplankton_uptake = total_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) + phytoplankton_uptake = total_production(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - grazing_waste = inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + grazing_waste = inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - respiration_product = upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields) + respiration_product = upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - remineralisation = degredation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields) + remineralisation = degredation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return θ * (grazing_waste + respiration_product + remineralisation - phytoplankton_uptake) end diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/growth_rate.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/growth_rate.jl index 63e0625bc..26f92ab22 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/growth_rate.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/growth_rate.jl @@ -1,8 +1,10 @@ abstract type BaseProduction end -@inline function (μ::BaseProduction)(val_name, i, j, k, grid, bgc, clock, fields, L) +@inline function (μ::BaseProduction)(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields, L) bₜ = μ.temperature_sensetivity μ₀ = μ.base_growth_rate + α₀ = μ.initial_slope_of_PI_curve + β = μ.low_light_adaptation dark_tollerance = μ.dark_tollerance @@ -10,21 +12,22 @@ abstract type BaseProduction end β₂ = phyto.green_light_absorption β₃ = phyto.red_light_absorption - PAR₁ = @inbounds fields.PAR₁[i, j, k] - PAR₂ = @inbounds fields.PAR₂[i, j, k] - PAR₃ = @inbounds fields.PAR₃[i, j, k] + PAR₁ = @inbounds auxiliary_fields.PAR₁[i, j, k] + PAR₂ = @inbounds auxiliary_fields.PAR₂[i, j, k] + PAR₃ = @inbounds auxiliary_fields.PAR₃[i, j, k] - zₑᵤ = @inbounds fields.zₑᵤ[i, j, k] - zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] + zₑᵤ = @inbounds auxiliary_fields.zₑᵤ[i, j, k] + zₘₓₗ = @inbounds auxiliary_fields.zₘₓₗ[i, j, k] + κ = @inbounds auxiliary_fields.κ[i, j, k] - I, IChl, IFe = phytoplankton_concentration(val_name, i, j, k, fields) + I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) T = @inbounds fields.T[i, j, k] PAR = β₁ * PAR₁ + β₂ * PAR₂ + β₃ * PAR₃ φ = bgc.latitude(i, j, k, grid) - day_length = bgc.day_length(φ, t) + day_length = bgc.day_length(φ, clock.time) dark_residence_time = max(0, zₑᵤ - zₘₓₗ) ^ 2 / κ @@ -120,27 +123,29 @@ end return 1 - exp(-α * θ * PAR / (day_length * (bᵣ + μᵣ))) end -@inline function production_and_energy_assimilation_absorption_ratio(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) +@inline function production_and_energy_assimilation_absorption_ratio(growth_rate, val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) α₀ = growth_rate.initial_slope_of_PI_curve β = growth_rate.low_light_adaptation β₁ = phyto.blue_light_absorption β₂ = phyto.green_light_absorption β₃ = phyto.red_light_absorption - I, IChl, IFe = phytoplankton_concentration(val_name, i, j, k, fields) + I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) - PAR₁ = @inbounds fields.PAR₁[i, j, k] - PAR₂ = @inbounds fields.PAR₂[i, j, k] - PAR₃ = @inbounds fields.PAR₃[i, j, k] + PAR₁ = @inbounds auxiliary_fields.PAR₁[i, j, k] + PAR₂ = @inbounds auxiliary_fields.PAR₂[i, j, k] + PAR₃ = @inbounds auxiliary_fields.PAR₃[i, j, k] PAR = β₁ * PAR₁ + β₂ * PAR₂ + β₃ * PAR₃ φ = bgc.latitude(i, j, k, grid) - day_length = bgc.day_length(φ, t) + day_length = bgc.day_length(φ, clock.time) f₁ = 1.5 * day_length / (day_length + 0.5day) - μ = growth_rate(val_name, i, j, k, grid, bgc, clock, fields, L) + L, = phyto.nutrient_limitation(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) + + μ = growth_rate(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields, L) μ̌ = μ / f₁ * day_length diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl index df014d14f..96c67d05e 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl @@ -1,3 +1,5 @@ +using Oceananigans.Grids: znode, Center + """ MixedMondo @@ -57,18 +59,18 @@ required_biogeochemical_tracers(phyto::MixedMondo, base) = ##### Production/mortality functions ##### -@inline function carbon_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) - I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, kfields) +@inline function carbon_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) # production δ = phyto.exudated_fracton - μI = total_production(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + μI = total_production(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (1 - δ) * μI end -@inline function chlorophyll_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) +@inline function chlorophyll_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) # production @@ -77,19 +79,17 @@ end θ₀ = phyto.minimum_chlorophyll_ratio θ₁ = phyto.maximum_chlorophyll_ratio - L, = phyto.nutrient_limitation(val_name, i, j, k, grid, bgc, clock, fields) - - μ, ρ = production_and_energy_assimilation_absorption_ratio(phyto.growth_rate, val_name, bgc, i, j, k, grid, bgc, clock, fields) + μ, ρ = production_and_energy_assimilation_absorption_ratio(phyto.growth_rate, val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) return (1 - δ) * 12 * (θ₀ + (θ₁ - θ₀) * ρ) * μ * I end # production (we already account for the (1 - δ) term because it just goes straight back into Fe) -@inline iron_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) = - iron_uptake(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) +@inline iron_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + iron_uptake(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline silicate_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) = - silicate_uptake(phyto, val_name, i, j, k, grid, bgc, clock, fields) +@inline silicate_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + silicate_uptake(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) ##### ##### Underlying parameterisations @@ -116,26 +116,26 @@ end return linear_mortality, quadratic_mortality end -@inline function mortality(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) - I, = phytoplankton_concentration(val_name, i, j, k, fields) - zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] +@inline function mortality(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + I, = phytoplankton_concentrations(val_name, i, j, k, fields) + zₘₓₗ = @inbounds auxiliary_fields.zₘₓₗ[i, j, k] z = znode(i, j, k, grid, Center(), Center(), Center()) - L, = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) + L, = phyto.nutrient_limitation(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) return mortality(phyto, bgc, z, I, zₘₓₗ, L) end -@inline function total_production(phyto::MixedMondo, val_name, bgc, i, j, k, grid, bgc, clock, fields) - I, = phytoplankton_concentration(val_name, i, j, k, fields) +@inline function total_production(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + I, = phytoplankton_concentrations(val_name, i, j, k, fields) - L, = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) + L, = phyto.nutrient_limitation(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) - return phyto.growth_rate(val_name, i, j, k, grid, bgc, clock, fields, L) * I + return phyto.growth_rate(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields, L) * I end -@inline function iron_uptake(phyto::MixedMondo, val_name, bgc, i, j, k, grid, bgc, clock, fields) +@inline function iron_uptake(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) δ = phyto.exudated_fracton θFeₘ = phyto.maximum_iron_ratio @@ -146,7 +146,7 @@ end θFe = IFe / (I + eps(0.0)) # μmol Fe / mmol C - L, LFe = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) + L, LFe = phyto.nutrient_limitation(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) μᵢ = base_production_rate(phyto.growth_rate, T) @@ -154,7 +154,7 @@ end L₂ = 4 - 4.5 * LFe / (LFe + 1) # typo in Aumount 2015 - return (1 - δ) * θFeₘ * L₁ * L₂ * max(0, (1 - θFe / θFeₘ) / (1.05 - θFe / θFeₘ)) * μᵢ * I + return θFeₘ * L₁ * L₂ * max(0, (1 - θFe / θFeₘ) / (1.05 - θFe / θFeₘ)) * μᵢ * I end @inline function iron_uptake_limitation(phyto, I, Fe) @@ -175,9 +175,8 @@ end return (I₁ + S * I₂) / (I₁ + I₂ + eps(0.0)) end -@inline function silicate_uptake(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields) +@inline function silicate_uptake(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) δ = phyto.exudated_fracton - K₁ = phyto.silicate_half_saturation K₂ = phyto.enhanced_silicate_half_saturation θ₀ = phyto.optimal_silicate_ratio @@ -187,9 +186,9 @@ end T = @inbounds fields.T[i, j, k] Si = @inbounds fields.Si[i, j, k] - L, LFe, LPO₄, LN = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) + L, LFe, LPO₄, LN = phyto.nutrient_limitation(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) - μ = phyto.growth_rate(val_name, i, j, k, grid, bgc, clock, fields, L) + μ = phyto.growth_rate(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields, L) μᵢ = base_production_rate(phyto.growth_rate, T) @@ -206,24 +205,33 @@ end θ₁ = θ₀ * L₁ * min(5.4, (4.4 * exp(-4.23 * F₁) * F₂ + 1) * (1 + 2 * L₂)) + # δ * ... is immediatly returned to Fe pool return (1 - δ) * θ₁ * μ * I end -@inline function uptake(phyto::MixedMondo, val_name, ::Val{:NO₃}, i, j, k, grid, bgc, clock, fields) - _, _, _, LN, LNO₃ = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) +@inline function uptake(phyto::MixedMondo, val_name, ::Val{:NO₃}, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + _, _, _, LN, LNO₃ = phyto.nutrient_limitation(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) - μI = total_production(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + μI = total_production(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return μI * LNO₃ / (LN + eps(0.0)) end -@inline function uptake(phyto::MixedMondo, val_name, ::Val{:NH₄}, i, j, k, grid, bgc, clock, fields) - _, _, _, LN, _, LNH₄ = phyto.nutrient_limitation(val_name, phyto, i, j, k, grid, bgc, clock, fields) +@inline function uptake(phyto::MixedMondo, val_name, ::Val{:NH₄}, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + _, _, _, LN, _, LNH₄ = phyto.nutrient_limitation(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) - μI = total_production(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + μI = total_production(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return μI * LNH₄ / (LN + eps(0.0)) end -@inline uptake(phyto::MixedMondo, val_name, ::Val{:Fe}, i, j, k, grid, bgc, clock, fields) = - iron_uptake(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) \ No newline at end of file +@inline uptake(phyto::MixedMondo, val_name, ::Val{:Fe}, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + iron_uptake(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + +@inline function dissolved_exudate(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + δ = phyto.exudated_fracton + + μI = total_production(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + return δ * μI +end diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl index 8cee02c58..74a146ee1 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl @@ -23,14 +23,14 @@ function MixedMondoNanoAndDiatoms(; nano = MixedMondo(growth_rate = GrowthRespir maximum_chlorophyll_ratio = 0.05, half_saturation_for_iron_uptake = 3.0)) - return NanoAndDiatoms(nano, diatoms) + return NanoAndDiatoms(; nano, diatoms) end const NANO_PHYTO = Union{Val{:P}, Val{:PChl}, Val{:PFe}} const DIATOMS = Union{Val{:D}, Val{:DChl}, Val{:DFe}, Val{:DSi}} -@inline phytoplankton_concentration(::NANO_PHYTO, i, j, k, fields) = @inbounds fields.P[i, j, k], fields.PChl[i, j, k], fields.PFe[i, j, k] -@inline phytoplankton_concentration(::DIATOMS, i, j, k, fields) = @inbounds fields.D[i, j, k], fields.DChl[i, j, k], fields.DFe[i, j, k] +@inline phytoplankton_concentrations(::NANO_PHYTO, i, j, k, fields) = @inbounds fields.P[i, j, k], fields.PChl[i, j, k], fields.PFe[i, j, k] +@inline phytoplankton_concentrations(::DIATOMS, i, j, k, fields) = @inbounds fields.D[i, j, k], fields.DChl[i, j, k], fields.DFe[i, j, k] @inline carbon_name(::NANO_PHYTO) = Val(:P) @inline carbon_name(::DIATOMS) = Val(:D) @@ -41,71 +41,71 @@ const DIATOMS = Union{Val{:D}, Val{:DChl}, Val{:DFe}, Val{:DSi}} # I think these could be abstracted more so that we have a few simple functions in nano_and_diatoms # and most only exist in `mixed_mondo.jl` # also maybe should be dispatched on PISCES{NanoAndDiatoms{MixedMondo, MixedMondo}} -@inline function (bgc::PISCES{NanoAndDiatoms})(i, j, k, grid, val_name::Union{Val{:P}, Val{:D}}, clock, fields) - phyto = parameterisaion(val_name, bgc.phytoplankon) +@inline function (bgc::PISCES{<:NanoAndDiatoms})(i, j, k, grid, val_name::Union{Val{:P}, Val{:D}}, clock, fields, auxiliary_fields) + phyto = parameterisation(val_name, bgc.phytoplankton) - growth = carbon_growth(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + growth = carbon_growth(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - linear_mortality, quadratic_mortality = mortality(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + linear_mortality, quadratic_mortality = mortality(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - death = linear_mortality + quadratic_mortality + death = (linear_mortality + quadratic_mortality) - grazing = grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields) + getting_grazed = grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - return growth - death - grazing + return growth - death - getting_grazed end -@inline function (bgc::PISCES{NanoAndDiatoms})(i, j, k, grid, val_name::Union{Val{:PChl}, Val{:DChl}}, clock, fields) - phyto = parameterisaion(val_name, bgc.phytoplankon) +@inline function (bgc::PISCES{<:NanoAndDiatoms})(i, j, k, grid, val_name::Union{Val{:PChl}, Val{:DChl}}, clock, fields, auxiliary_fields) + phyto = parameterisation(val_name, bgc.phytoplankton) - growth = chlorophyll_growth(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + growth = chlorophyll_growth(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) θChl = IChl / (12 * I + eps(0.0)) - linear_mortality, quadratic_mortality = mortality(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + linear_mortality, quadratic_mortality = mortality(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) death = (linear_mortality + quadratic_mortality) - grazing = grazing(bgc.zooplankton, carbon_name(val_name), i, j, k, grid, bgc, clock, fields) + getting_grazed = grazing(bgc.zooplankton, carbon_name(val_name), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - return growth - (death + grazing) * θChl * 12 + return growth - (death + getting_grazed) * θChl * 12 end -@inline function (bgc::PISCES{NanoAndDiatoms})(i, j, k, grid, val_name::Union{Val{:PFe}, Val{:DFe}}, clock, fields) - phyto = parameterisaion(val_name, bgc.phytoplankon) +@inline function (bgc::PISCES{<:NanoAndDiatoms})(i, j, k, grid, val_name::Union{Val{:PFe}, Val{:DFe}}, clock, fields, auxiliary_fields) + phyto = parameterisation(val_name, bgc.phytoplankton) - growth = chlorophyll_growth(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + growth = iron_growth(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) θFe = IFe / (I + eps(0.0)) - linear_mortality, quadratic_mortality = mortality(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + linear_mortality, quadratic_mortality = mortality(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) death = (linear_mortality + quadratic_mortality) - grazing = grazing(bgc.zooplankton, carbon_name(val_name), i, j, k, grid, bgc, clock, fields) + getting_grazed = grazing(bgc.zooplankton, carbon_name(val_name), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - return growth - (death + grazing) * θFe + return growth - (death + getting_grazed) * θFe end -@inline function (bgc::PISCES{NanoAndDiatoms})(i, j, k, grid, val_name::Val{:DSi}, clock, fields) - phyto = parameterisaion(val_name, bgc.phytoplankon) +@inline function (bgc::PISCES{<:NanoAndDiatoms})(i, j, k, grid, val_name::Val{:DSi}, clock, fields, auxiliary_fields) + phyto = parameterisation(val_name, bgc.phytoplankton) - growth = chlorophyll_growth(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + growth = chlorophyll_growth(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) D = @inbounds fields.D[i, j, k] DSi = @inbounds fields.DSi[i, j, k] θSi = DSi / (D + eps(0.0)) - linear_mortality, quadratic_mortality = mortality(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) + linear_mortality, quadratic_mortality = mortality(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) death = (linear_mortality + quadratic_mortality) - grazing = grazing(bgc.zooplankton, Val(:D), i, j, k, grid, bgc, clock, fields) + getting_grazed = grazing(bgc.zooplankton, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - return growth - (death + grazing) * θSi + return growth - (death + getting_grazed) * θSi end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl index 5f3aff834..90da975dd 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl @@ -1,32 +1,33 @@ -struct NanoAndDiatoms{N, D} - nano :: N - diatoms :: D +@kwdef struct NanoAndDiatoms{N, D, FT} + nano :: N + diatoms :: D + base_rain_ratio :: FT = 0.3 end required_biogeochemical_tracers(phyto::NanoAndDiatoms) = (required_biogeochemical_tracers(phyto.nano, :P)..., required_biogeochemical_tracers(phyto.diatoms, :D)...) -@inline dissolved_exudate(phyto::NanoAndDiatoms, bgc, i, j, k, grid, bgc, clock, fields) = - (dissolved_exudate(phyto.nano, Val(:P), bgc, i, j, k, grid, bgc, clock, fields) - + dissolved_exudate(phyto.diatoms, Val(:D), bgc, i, j, k, grid, bgc, clock, fields)) +@inline dissolved_exudate(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (dissolved_exudate(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + dissolved_exudate(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields)) -@inline uptake(phyto::NanoAndDiatoms, val_uptake_name, i, j, k, grid, bgc, clock, fields) = - (uptake(phyto.nano, Val(:P), val_uptake_name, bgc, i, j, k, grid, bgc, clock, fields) - + uptake(phyto.diatoms, Val(:D), val_uptake_name, bgc, i, j, k, grid, bgc, clock, fields)) +@inline uptake(phyto::NanoAndDiatoms, val_uptake_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (uptake(phyto.nano, Val(:P), val_uptake_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + uptake(phyto.diatoms, Val(:D), val_uptake_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields)) -@inline function nitrogen_availability_limitation(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) - _, _, _, LN = phyto.nano.nutrient_limitation(Val(:P), phyto.nano, i, j, k, grid, bgc, clock, fields) +@inline function nitrogen_availability_limitation(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + _, _, _, LN = phyto.nano.nutrient_limitation(Val(:P), i, j, k, grid, bgc, phyto.nano, clock, fields, auxiliary_fields) return LN end -@inline base_production_rate(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) = +@inline base_production_rate(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = @inbounds base_production_rate(phyto.nano.growth_rate, fields.T[i, j, k]) -@inline silicate_uptake(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) = - (silicate_uptake(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) - + silicate_uptake(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields)) +@inline silicate_uptake(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (silicate_uptake(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + silicate_uptake(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields)) -@inline total_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields) = - (total_production(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields) - + total_production(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields)) \ No newline at end of file +@inline total_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (total_production(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + total_production(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields)) \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl index 722446c54..ca8cab0f3 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl @@ -17,7 +17,7 @@ setting `silicate_limited=false`. silicate_half_saturation_parameter :: FT = 16.6 # mmol Si / m³ end -@inline function (L::NitrogenIronPhosphateSilicateLimitation)(val_name, phyto, i, j, k, grid, bgc, clock, fields) +@inline function (L::NitrogenIronPhosphateSilicateLimitation)(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) kₙₒ = L.minimum_nitrate_half_saturation kₙₕ = L.minimum_ammonium_half_saturation kₚ = L.minimum_phosphate_half_saturation @@ -26,13 +26,15 @@ end θₒ = L.optimal_iron_quota - I, IChl, IFe = phytoplankton_concentrations(val_name, fields) + I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) NO₃ = @inbounds fields.NO₃[i, j, k] NH₄ = @inbounds fields.NH₄[i, j, k] PO₄ = @inbounds fields.PO₄[i, j, k] Si = @inbounds fields.Si[i, j, k] + Si′ = @inbounds bgc.silicate_climatology[i, j, k] + # quotas θFe = ifelse(I == 0, 0, IFe / (I + eps(0.0))) θChl = ifelse(I == 0, 0, IChl / (12 * I + eps(0.0))) @@ -54,7 +56,7 @@ end # iron limitation # Flynn and Hipkin (1999) - photosphotosyntheis, respiration (?), nitrate reduction - θₘ = 10^3 * (0.0016 / 55.85 * 12 * θChl + 1.5 * 1.21e-5 * 14 / (55.85 * 7.625) * LN + 1.15e-4 * 14 / (55.85 * 7.625) * LNO₃) + θₘ = 0.0016 / 55.85 * 12 * θChl + 1.5 * 1.21e-5 * 14 / (55.85 * 7.625) * LN + 1.15e-4 * 14 / (55.85 * 7.625) * LNO₃ LFe = min(1, max(0, (θFe - θₘ) / θₒ)) diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/phytoplankton.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/phytoplankton.jl index d2e134b43..98278e450 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/phytoplankton.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/phytoplankton.jl @@ -2,6 +2,8 @@ module Phytoplankton export NanoAndDiatoms, MixedMondoPhytoplankton, MixedMondoNanoAndDiatoms +using Oceananigans.Units + using OceanBioME.Models.PISCESModel: PISCES using OceanBioME.Models.PISCESModel.Zooplankton: grazing @@ -13,6 +15,6 @@ include("nano_and_diatoms.jl") include("mixed_mondo.jl") include("growth_rate.jl") include("nutrient_limitation.jl") -include("mixed_mono_nano_diatoms.jl") +include("mixed_mondo_nano_diatoms.jl") end # module \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/waste.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/waste.jl deleted file mode 100644 index 52ae6af29..000000000 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/waste.jl +++ /dev/null @@ -1,7 +0,0 @@ -@inline function dissolved_exudate(phyto::MixedMondo, val_name, bgc, i, j, k, grid, bgc, clock, fields) - δ = phyto.exudated_fracton - - μI = total_production(phyto, val_name, bgc, i, j, k, grid, bgc, clock, fields) - - return δ * μI -end diff --git a/src/Models/AdvectedPopulations/PISCES/silicate.jl b/src/Models/AdvectedPopulations/PISCES/silicate.jl index f0eed471f..251a49d5d 100644 --- a/src/Models/AdvectedPopulations/PISCES/silicate.jl +++ b/src/Models/AdvectedPopulations/PISCES/silicate.jl @@ -9,14 +9,18 @@ using OceanBioME.Models.PISCESModel.ParticulateOrganicMatter: using OceanBioME.Models.PISCESModel.Phytoplankton: silicate_uptake +import Oceananigans.Biogeochemistry: required_biogeochemical_tracers + struct Silicate end -const PISCESSilicate = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, Silicate} +required_biogeochemical_tracers(::Silicate) = tuple(:Si) + +const PISCESSilicate = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Silicate} -@inline function (bgc::PISCESSilicate)(i, j, k, grid, val_name::Val{:Si}, clock, fields) - consumption = silicate_uptake(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields) +@inline function (bgc::PISCESSilicate)(i, j, k, grid, val_name::Val{:Si}, clock, fields, auxiliary_fields) + consumption = silicate_uptake(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - dissolution = particulate_silicate_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields) + dissolution = particulate_silicate_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return dissolution - consumption end diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl index 69b68ebc5..d3917eefb 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl @@ -1,24 +1,25 @@ # this file sets up the default configuration of Z and M which graze on P, D, (Z, ) and POC -function MicroAndMesoZooplankton(; micro = Zooplankton(maximum_grazing_rate = 3/day, - food_preferences = (P = 1.0, D = 0.5, POC = 0.1, Z = 0), - quadratic_mortality = 0.004/day, - linear_mortality = 0.03/day, - minimum_growth_efficiency = 0.3, - maximum_flux_feeding_rate = 0.0, - undissolved_calcite_fraction = 0.5, - iron_ratio = 0.01), - meso = Zooplankton(maximum_grazing_rate = 0.75/day, - food_preferences = (P = 0.3, D = 1.0, POC = 0.3, Z = 1.0), - quadratic_mortality = 0.03/day, - linear_mortality = 0.005/day, - minimum_growth_efficiency = 0.35, - # not documented but the below must implicitly contain a factor of second/day - # to be consistent in the NEMO namelist to go from this * mol / L * m/s to mol / L / day - maximum_flux_feeding_rate = 2e3 / 1e6 / day, # (day * meter/s * mol/L)^-1 to (meter * μ mol/L)^-1 - undissolved_calcite_fraction = 0.75, - iron_ratio = 0.015)) +function MicroAndMesoZooplankton(; + micro = QualityDependantZooplankton(maximum_grazing_rate = 3/day, + food_preferences = (P = 1.0, D = 0.5, POC = 0.1, Z = 0), + quadratic_mortality = 0.004/day, + linear_mortality = 0.03/day, + minimum_growth_efficiency = 0.3, + maximum_flux_feeding_rate = 0.0, + undissolved_calcite_fraction = 0.5, + iron_ratio = 0.01), + meso = QualityDependantZooplankton(maximum_grazing_rate = 0.75/day, + food_preferences = (P = 0.3, D = 1.0, POC = 0.3, Z = 1.0), + quadratic_mortality = 0.03/day, + linear_mortality = 0.005/day, + minimum_growth_efficiency = 0.35, + # not documented but the below must implicitly contain a factor of second/day + # to be consistent in the NEMO namelist to go from this * mol / L * m/s to mol / L / day + maximum_flux_feeding_rate = 2e3 / 1e6 / day, # (day * meter/s * mol/L)^-1 to (meter * μ mol/L)^-1 + undissolved_calcite_fraction = 0.75, + iron_ratio = 0.015)) - return MicroAndMesoZooplankton(micro, meso) + return MicroAndMeso(; micro, meso) end @inline concentration(::Val{:P}, i, j, k, fields) = @inbounds fields.P[i, j, k] diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl index c5d42d1b7..0c41b0d4e 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl @@ -8,40 +8,40 @@ particulates (POC and GOC). This model assumes a fixed ratio for all other elements (i.e. N, P, Fe). """ -@kwdef struct QualityDependantZooplankton{FT, FP} - temperature_sensetivity :: FT = 1.079 # - maximum_grazing_rate :: FT # 1 / s +@kwdef struct QualityDependantZooplankton{FT, FP, FN} + temperature_sensetivity :: FT = 1.079 # + maximum_grazing_rate :: FT # 1 / s - food_preferences :: FP - food_names :: FN = keys(food_preferences) + food_preferences :: FP + food_names :: FN = keys(food_preferences) - food_threshold_concentration :: FT = 0.3 # mmol C / m³ - specific_food_thresehold_concentration :: FT = 0.001 # mmol C / m³ + food_threshold_concentration :: FT = 0.3 # mmol C / m³ + specific_food_thresehold_concentration :: FT = 0.001 # mmol C / m³ - grazing_half_saturation :: FT = 20.0 # mmol C / m³ + grazing_half_saturation :: FT = 20.0 # mmol C / m³ - maximum_flux_feeding_rate :: FT # m / (mmol C / m³) + maximum_flux_feeding_rate :: FT # m / (mmol C / m³) - iron_ratio :: FT # μmol Fe / mmol C + iron_ratio :: FT # μmol Fe / mmol C - minimum_growth_efficiency :: FT # - non_assililated_fraction :: FT = 0.3 # + minimum_growth_efficiency :: FT # + non_assililated_fraction :: FT = 0.3 # - mortality_half_saturation :: FT = 0.2 # mmol C / m³ - quadratic_mortality :: FT # 1 / (mmol C / m³) / s - linear_mortality :: FT # 1 / s + mortality_half_saturation :: FT = 0.2 # mmol C / m³ + quadratic_mortality :: FT # 1 / (mmol C / m³) / s + linear_mortality :: FT # 1 / s # this should be called inorganic excretion factor - dissolved_excretion_fraction :: FT = 0.6 # - undissolved_calcite_fraction :: FT # + dissolved_excretion_fraction :: FT = 0.6 # + undissolved_calcite_fraction :: FT # end -required_biogeochemical_tracers(::QualityDependantZooplankton, name_base) = name_base +required_biogeochemical_tracers(::QualityDependantZooplankton, name_base) = tuple(name_base) -@inline function growth_death(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) - gI, e = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) - gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields) - mI = mortality(zoo, val_name, i, j, k, grid, bgc, clock, fields) +@inline function growth_death(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + gI, e = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + mI = mortality(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return e * (gI + gfI) - mI end @@ -52,7 +52,7 @@ end @inline extract_iron_availability(i, j, k, bgc, fields, names::NTuple{N}) where N = ntuple(n -> iron_ratio(Val(names[n]), i, j, k, bgc, fields), Val(N)) -@inline function grazing(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function grazing(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) # food quantity g₀ = zoo.maximum_grazing_rate b = zoo.temperature_sensetivity @@ -65,6 +65,7 @@ end N = length(food) I = zooplankton_concentration(val_name, i, j, k, fields) + T = @inbounds fields.T[i, j, k] base_grazing_rate = g₀ * b ^ T @@ -87,7 +88,7 @@ end total_iron = sum(ntuple(n->iron_availabillity[n] * p[n], Val(N))) - iron_grazing_ratio = iron_grazing / (θFe * total_specific_grazing + eps(0.0)) + iron_grazing_ratio = total_iron / (θFe * total_specific_grazing + eps(0.0)) food_quality = min(1, iron_grazing_ratio) @@ -96,7 +97,7 @@ end return total_specific_grazing * I, growth_efficiency end -@inline function flux_feeding(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function flux_feeding(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) g₀ = zoo.maximum_flux_feeding_rate b = zoo.temperature_sensetivity @@ -104,42 +105,43 @@ end T = @inbounds fields.T[i, j, k] - sinking_flux = edible_flux_rate(bgc.particulate_organic_matter, i, j, k, grid, fields) + sinking_flux = edible_flux_rate(bgc.particulate_organic_matter, i, j, k, grid, fields, auxiliary_fields) - base_flux_feeding_rate = g₁ * b ^ T + base_flux_feeding_rate = g₀ * b ^ T total_specific_flux_feeding = base_flux_feeding_rate * sinking_flux return total_specific_flux_feeding * I end -@inline function mortality(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function mortality(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) b = zoo.temperature_sensetivity m₀ = zoo.quadratic_mortality Kₘ = zoo.mortality_half_saturation r = zoo.linear_mortality - temperature_factor = b^T - I = zooplankton_concentration(val_name, i, j, k, fields) + T = @inbounds fields.T[i, j, k] O₂ = @inbounds fields.O₂[i, j, k] + temperature_factor = b^T + concentration_factor = I / (I + Kₘ) return temperature_factor * I * (m₀ * I + r * (concentration_factor + 3 * anoxia_factor(bgc, O₂))) end -@inline function linear_mortality(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function linear_mortality(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) b = zoo.temperature_sensetivity Kₘ = zoo.mortality_half_saturation r = zoo.linear_mortality - - temperature_factor = b^T - + + T = @inbounds fields.T[i, j, k] + O₂ = @inbounds fields.O₂[i, j, k] I = zooplankton_concentration(val_name, i, j, k, fields) - O₂ = @inbounds fields.O₂[i, j, k] + temperature_factor = b^T concentration_factor = I / (I + Kₘ) @@ -150,7 +152,7 @@ end ##### Effect on other compartements ##### -@inline function grazing(zoo::QualityDependantZooplankton, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields) +@inline function grazing(zoo::QualityDependantZooplankton, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) g₀ = zoo.maximum_grazing_rate b = zoo.temperature_sensetivity p = zoo.food_preferences @@ -162,10 +164,11 @@ end N = length(food) I = zooplankton_concentration(val_name, i, j, k, fields) + T = @inbounds fields.T[i, j, k] base_grazing_rate = g₀ * b ^ T - food_availability = extract_food_availability(i, j, k, fields, food, p) + food_availability = extract_food_availability(i, j, k, fields, food) total_food = sum(ntuple(n->food_availability[n] * p[n], Val(N))) @@ -180,7 +183,7 @@ end return grazing_preference(val_prey_name, p) * max(0, P - J) * total_specific_grazing / (available_total_food + eps(0.0)) * I end -@inline function flux_feeding(zoo::QualityDependantZooplankton, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields) +@inline function flux_feeding(zoo::QualityDependantZooplankton, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) g₀ = zoo.maximum_flux_feeding_rate b = zoo.temperature_sensetivity @@ -188,9 +191,9 @@ end T = @inbounds fields.T[i, j, k] - sinking_flux = flux_rate(val_prey_name, i, j, k, grid, fields) + sinking_flux = flux_rate(val_prey_name, i, j, k, grid, fields, auxiliary_fields) - base_flux_feeding_rate = g₁ * b ^ T + base_flux_feeding_rate = g₀ * b ^ T total_specific_flux_feeding = base_flux_feeding_rate * sinking_flux diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl index f19e76712..8db87a9ee 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl @@ -1,70 +1,70 @@ include("iron_grazing.jl") -@inline function non_assimilated_waste(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function non_assimilated_waste(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) σ = zoo.non_assililated_fraction - gI, = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) + gI, = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields) + gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return σ * (gI + gfI) end -@inline function excretion(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function excretion(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) σ = zoo.non_assililated_fraction - gI, growth_efficiency = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) + gI, e = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields) + gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (1 - σ - e) * (gI + gfI) end -@inline function inorganic_excretion(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function inorganic_excretion(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) γ = zoo.dissolved_excretion_fraction - return γ * excretion(zoo, val_name, i, j, k, grid, bgc, clock, fields) + return γ * excretion(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) end -@inline function organic_excretion(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function organic_excretion(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) γ = zoo.dissolved_excretion_fraction - return (1 - γ) * excretion(zoo, val_name, i, j, k, grid, bgc, clock, fields) + return (1 - γ) * excretion(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) end -@inline function non_assimilated_iron_waste(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function non_assimilated_iron_waste(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) σ = zoo.non_assililated_fraction - gI = iron_grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) + gI = iron_grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - gfI = iron_flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields) + gfI = iron_flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return σ * (gI + gfI) end -@inline function non_assimilated_iron(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function non_assimilated_iron(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) θ = zoo.iron_ratio σ = zoo.non_assililated_fraction - gI, growth_efficiency = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) - gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields) + gI, growth_efficiency = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) total_carbon = gI + gfI - total_iron = (iron_grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields) - + iron_flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields)) + total_iron = (iron_grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + iron_flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields)) grazing_iron_ratio = (1 - σ) * total_iron / (total_carbon + eps(0.0)) non_assimilated_iron_ratio = max(0, grazing_iron_ratio - growth_efficiency * θ) - return non_assililated_fraction * gI + return σ * gI end -@inline function calcite_loss(zoo::QualityDependantZooplankton, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields) +@inline function calcite_loss(zoo::QualityDependantZooplankton, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) η = zoo.undissolved_calcite_fraction - g = grazing(zoo, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields) + g = grazing(zoo, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return η * g end diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl index b056ec605..bdf0d94f8 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl @@ -1,5 +1,5 @@ -@inline function iron_grazing(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function iron_grazing(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) # food quantity g₀ = zoo.maximum_grazing_rate b = zoo.temperature_sensetivity @@ -13,6 +13,8 @@ I = zooplankton_concentration(val_name, i, j, k, fields) + T = @inbounds fields.T[i, j, k] + base_grazing_rate = g₀ * b ^ T food_availability = extract_food_availability(i, j, k, fields, food) @@ -27,12 +29,12 @@ iron_ratios = extract_iron_availability(i, j, k, bgc, fields, food) - total_specific_iron_grazing = sum(ntuple(n->max(zero(grid), (food_availability[n] - J)) * p[n] iron_ratios[n], Val(N))) * total_specific_grazing / available_total_food + total_specific_iron_grazing = sum(ntuple(n->max(zero(grid), (food_availability[n] - J)) * p[n] * iron_ratios[n], Val(N))) * total_specific_grazing / available_total_food return total_specific_grazing * I end -@inline function iron_flux_feeding(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function iron_flux_feeding(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) g₀ = zoo.maximum_flux_feeding_rate b = zoo.temperature_sensetivity @@ -40,9 +42,9 @@ end T = @inbounds fields.T[i, j, k] - sinking_flux = edible_iron_flux_rate(bgc.particulate_organic_matter, i, j, k, grid, fields) + sinking_flux = edible_iron_flux_rate(bgc.particulate_organic_matter, i, j, k, grid, fields, auxiliary_fields) - base_flux_feeding_rate = g₁ * b ^ T + base_flux_feeding_rate = g₀ * b ^ T total_specific_flux_feeding = base_flux_feeding_rate * sinking_flux diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl index d7ef98aac..88c6b43e9 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl @@ -1,10 +1,13 @@ -struct MicroAndMeso{μ, M, FT} +using Oceananigans.Grids: znode, Center + +@kwdef struct MicroAndMeso{μ, M, FT} micro :: μ meso :: M microzooplankton_bacteria_concentration :: FT = 0.7 mesozooplankton_bacteria_concentration :: FT = 1.4 maximum_bacteria_concentration :: FT = 4.0 # mmol C / m³ + bacteria_concentration_depth_exponent :: FT = 0.684 # doc_half_saturation_for_bacterial_activity :: FT = 417.0 # mmol C / m³ nitrate_half_saturation_for_bacterial_activity :: FT = 0.03 # mmol N / m³ @@ -28,60 +31,63 @@ required_biogeochemical_tracers(zoo::MicroAndMeso) = (required_biogeochemical_tr @inline predator_name(val_name, zoo) = nothing @inline predator_name(::Val{:Z}, zoo::MicroAndMeso) = Val(:M) -@inline grazing(zoo, ::Val{:M}, ::Val{:Z}, i, j, k, grid, args...) = zero(grid) +@inline grazing(::Nothing, ::Nothing, val_prey_name, i, j, k, grid, args...) = zero(grid) -@inline function (bgc::PISCES{<:Any, MicroAndMeso})(i, j, k, grid, val_name{Val{:Z}, Val{:M}}, clock, fields) - zoo = parameterisaion(bgc.zooplankton) +@inline function (bgc::PISCES{<:Any, <:MicroAndMeso})(i, j, k, grid, val_name::Union{Val{:Z}, Val{:M}}, clock, fields, auxiliary_fields) + zoo = parameterisation(val_name, bgc.zooplankton) - growth_death = growth_death(zoo, val_name, i, j, k, grid, bgc, clock, fields) + net_production = growth_death(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) # M preying on Z predator_zoo = predator_parameterisation(val_name, bgc.zooplankton) val_predator_name = predator_name(val_name, bgc.zooplankton) - grazing = grazing(predator_zoo, val_predator_name, val_name, i, j, k, grid, bgc, clock, fields) + predatory_grazing = grazing(predator_zoo, val_predator_name, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - return growth_death - grazing + return net_production - predatory_grazing end -@inline grazing(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields) = - (grazing(zoo.micro, Val(:Z), val_prey_name, i, j, k, grid, bgc, clock, fields) - + grazing(zoo.meso, Val(:M), val_prey_name, i, j, k, grid, bgc, clock, fields)) +@inline grazing(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (grazing(zoo.micro, Val(:Z), val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + grazing(zoo.meso, Val(:M), val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields)) -@inline flux_feeding(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields) = - (flux_feeding(zoo.micro, Val(:Z), val_prey_name, i, j, k, grid, bgc, clock, fields) - + flux_feeding(zoo.meso, Val(:M), val_prey_name, i, j, k, grid, bgc, clock, fields)) +@inline flux_feeding(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (flux_feeding(zoo.micro, Val(:Z), val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + flux_feeding(zoo.meso, Val(:M), val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields)) -@inline inorganic_excretion(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - (inorganic_excretion(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) - + inorganic_excretion(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields)) +@inline inorganic_excretion(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (inorganic_excretion(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + inorganic_excretion(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields)) -@inline organic_excretion(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - (organic_excretion(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) - + organic_excretion(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields)) +@inline organic_excretion(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (organic_excretion(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + organic_excretion(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields)) -@inline non_assimilated_iron(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - (non_assimilated_iron(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields) - + non_assimilated_iron(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields)) +@inline non_assimilated_iron(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (non_assimilated_iron(zoo.micro, Val(:Z), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + non_assimilated_iron(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields)) -@inline upper_trophic_excretion(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - upper_trophic_excretion(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) +@inline upper_trophic_excretion(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + upper_trophic_excretion(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline upper_trophic_respiration(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - upper_trophic_respiration(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) +@inline upper_trophic_respiration(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + upper_trophic_respiration(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline upper_trophic_fecal_production(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - upper_trophic_fecal_production(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) +@inline upper_trophic_fecal_production(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + upper_trophic_fecal_production(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + +@inline upper_trophic_fecal_iron_production(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + upper_trophic_fecal_iron_production(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline function bacteria_concentration(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) +@inline function bacteria_concentration(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) bZ = zoo.microzooplankton_bacteria_concentration bM = zoo.mesozooplankton_bacteria_concentration a = zoo.bacteria_concentration_depth_exponent z = znode(i, j, k, grid, Center(), Center(), Center()) - zₘₓₗ = @inbounds fields.zₘₓₗ[i, j, k] - zₑᵤ = @inbounds fields.zₑᵤ[i, j, k] + zₘₓₗ = @inbounds auxiliary_fields.zₘₓₗ[i, j, k] + zₑᵤ = @inbounds auxiliary_fields.zₑᵤ[i, j, k] Z = @inbounds fields.Z[i, j, k] M = @inbounds fields.M[i, j, k] @@ -95,7 +101,7 @@ end return ifelse(z >= zₘ, 1, depth_factor) * surface_bacteria end -@inline function bacteria_activity(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) +@inline function bacteria_activity(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) K_DOC = zoo.doc_half_saturation_for_bacterial_activity K_NO₃ = zoo.nitrate_half_saturation_for_bacterial_activity K_NH₄ = zoo.ammonia_half_saturation_for_bacterial_activity @@ -122,9 +128,9 @@ end return limiting_quota * DOC_limit end -@inline calcite_loss(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields) = - (calcite_loss(zoo.micro, Val(:Z), val_prey_name, i, j, k, grid, bgc, clock, fields) - + calcite_loss(zoo.meso, Val(:M), val_prey_name, i, j, k, grid, bgc, clock, fields)) +@inline calcite_loss(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (calcite_loss(zoo.micro, Val(:Z), val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + + calcite_loss(zoo.meso, Val(:M), val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields)) -@inline upper_trophic_iron_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields) = - upper_trophic_respiration(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields) * zoo.meso.iron_ratio \ No newline at end of file +@inline upper_trophic_iron_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + upper_trophic_respiration(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) * zoo.meso.iron_ratio \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl index d7cb1226b..ed67900e7 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl @@ -1,37 +1,38 @@ -@inline function upper_trophic_excretion(zoo, val_name, i, j, k, grid, bgc, clock, fields) +@inline function upper_trophic_excretion(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) γ = zoo.dissolved_excretion_fraction - R = upper_trophic_respiration_product(zoo, val_name, i, j, k, grid, bgc, clock, fields) + R = upper_trophic_respiration_product(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (1 - γ) * R end -@inline function upper_trophic_respiration(zoo, val_name, i, j, k, grid, bgc, clock, fields) +@inline function upper_trophic_respiration(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) γ = zoo.dissolved_excretion_fraction - R = upper_trophic_respiration_product(zoo, val_name, i, j, k, grid, bgc, clock, fields) + R = upper_trophic_respiration_product(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return γ * R end -@inline upper_trophic_respiration_product(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) = - (1 - zoo.minimum_growth_efficiency - zoo.non_assililated_fraction) * upper_trophic_waste(zoo, val_name, i, j, k, grid, bgc, clock, fields) +@inline upper_trophic_respiration_product(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + (1 - zoo.minimum_growth_efficiency - zoo.non_assililated_fraction) * upper_trophic_waste(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline upper_trophic_fecal_production(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) = - zoo.non_assililated_fraction * upper_trophic_waste(zoo, val_name, i, j, k, grid, bgc, clock, fields) +@inline upper_trophic_fecal_production(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + zoo.non_assililated_fraction * upper_trophic_waste(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) -@inline upper_trophic_fecal_iron_production(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) = - upper_trophic_fecal_production(zoo, val_name, i, j, k, grid, bgc, clock, fields) * zoo.iron_ratio +@inline upper_trophic_fecal_iron_production(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + upper_trophic_fecal_production(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) * zoo.iron_ratio -@inline function upper_trophic_waste(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields) +@inline function upper_trophic_waste(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) e₀ = zoo.minimum_growth_efficiency b = zoo.temperature_sensetivity m₀ = zoo.quadratic_mortality - temperature_factor = b^T - + T = @inbounds fields.T[i, j, k] I = zooplankton_concentration(val_name, i, j, k, fields) + temperature_factor = b^T + return 1 / (1 - e₀) * m₀ * temperature_factor * I^2 end diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/zooplankton.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/zooplankton.jl index 86694c28b..871502a23 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/zooplankton.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/zooplankton.jl @@ -1,8 +1,10 @@ module Zooplankton -export MicroAndMezoZooplankton, QualityDependantZooplankton, MicroAndMeso +export MicroAndMesoZooplankton, QualityDependantZooplankton, MicroAndMeso -using OceanBioME.Models.PISCESModel: anoxia_factor, PISCES +using Oceananigans.Units + +using OceanBioME.Models.PISCESModel: anoxia_factor, PISCES, flux_rate import Oceananigans.Biogeochemistry: required_biogeochemical_tracers import OceanBioME.Models.PISCESModel: mortality diff --git a/src/Models/Sediments/Sediments.jl b/src/Models/Sediments/Sediments.jl index e8d4e7f28..eb7699e2f 100644 --- a/src/Models/Sediments/Sediments.jl +++ b/src/Models/Sediments/Sediments.jl @@ -4,7 +4,7 @@ export SimpleMultiG, InstantRemineralisation using KernelAbstractions -using OceanBioME: Biogeochemistry, BoxModelGrid +using OceanBioME: DiscreteBiogeochemistry, ContinuousBiogeochemistry, BoxModelGrid using Oceananigans using Oceananigans.Architectures: device, architecture, on_architecture diff --git a/src/Models/Sediments/coupled_timesteppers.jl b/src/Models/Sediments/coupled_timesteppers.jl index a603dede4..32be917bb 100644 --- a/src/Models/Sediments/coupled_timesteppers.jl +++ b/src/Models/Sediments/coupled_timesteppers.jl @@ -8,7 +8,11 @@ using Oceananigans.Architectures: AbstractArchitecture import Oceananigans.TimeSteppers: ab2_step!, rk3_substep! -@inline function ab2_step!(model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Biogeochemistry{<:Any, <:Any, <:FlatSediment}}, Δt) +const BGC_WITH_FLAT_SEDIMENT = Union{<:DiscreteBiogeochemistry{<:Any, <:Any, <:FlatSediment}, + <:ContinuousBiogeochemistry{<:Any, <:Any, <:FlatSediment}} + +# This is definitly type piracy +@inline function ab2_step!(model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, BGC_WITH_FLAT_SEDIMENT}, Δt) workgroup, worksize = work_layout(model.grid, :xyz) arch = model.architecture step_field_kernel! = ab2_step_field!(device(arch), workgroup, worksize) @@ -46,7 +50,7 @@ import Oceananigans.TimeSteppers: ab2_step!, rk3_substep! return nothing end -@inline function ab2_step!(model::HydrostaticFreeSurfaceModel{<:Any, <:Any, <:AbstractArchitecture, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Biogeochemistry{<:Any, <:Any, <:FlatSediment}}, Δt) +@inline function ab2_step!(model::HydrostaticFreeSurfaceModel{<:Any, <:Any, <:AbstractArchitecture, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, BGC_WITH_FLAT_SEDIMENT}, Δt) χ = model.timestepper.χ # Step locally velocity and tracers @@ -78,7 +82,7 @@ end @inbounds u[i, j, 1] += Δt * ((one_point_five + χ) * Gⁿ[i, j, 1] - (oh_point_five + χ) * G⁻[i, j, 1]) end -function rk3_substep!(model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Biogeochemistry{<:Any, <:Any, <:FlatSediment}}, Δt, γⁿ, ζⁿ) +function rk3_substep!(model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, BGC_WITH_FLAT_SEDIMENT}, Δt, γⁿ, ζⁿ) workgroup, worksize = work_layout(model.grid, :xyz) arch = model.architecture substep_field_kernel! = rk3_substep_field!(device(arch), workgroup, worksize) diff --git a/src/OceanBioME.jl b/src/OceanBioME.jl index c5b87e0e6..b211bf652 100644 --- a/src/OceanBioME.jl +++ b/src/OceanBioME.jl @@ -41,7 +41,7 @@ export ScaleNegativeTracers, ZeroNegativeTracers export ColumnField, isacolumn using Oceananigans.Architectures: architecture, device, CPU -using Oceananigans.Biogeochemistry: AbstractContinuousFormBiogeochemistry +using Oceananigans.Biogeochemistry: AbstractBiogeochemistry, AbstractContinuousFormBiogeochemistry using Oceananigans.Grids: RectilinearGrid, Flat using Adapt @@ -58,25 +58,24 @@ import Oceananigans.Biogeochemistry: required_biogeochemical_tracers, import Adapt: adapt_structure import Base: show, summary -struct Biogeochemistry{B, L, S, P, M} <: AbstractContinuousFormBiogeochemistry +struct ContinuousBiogeochemistry{B, L, S, P, M} <: AbstractContinuousFormBiogeochemistry underlying_biogeochemistry :: B light_attenuation :: L sediment :: S particles :: P modifiers :: M - - Biogeochemistry(underlying_biogeochemistry::B, - light_attenuation::L, - sediment::S, - particles::P, - modifiers::M) where {B, L, S, P, M} = - new{B, L, S, P, M}(underlying_biogeochemistry, - light_attenuation, - sediment, - particles, - modifiers) end +struct DiscreteBiogeochemistry{B, L, S, P, M} <: AbstractBiogeochemistry + underlying_biogeochemistry :: B + light_attenuation :: L + sediment :: S + particles :: P + modifiers :: M +end + +const CompleteBiogeochemistry = Union{<:ContinuousBiogeochemistry, <:DiscreteBiogeochemistry} + """ Biogeochemistry(underlying_biogeochemistry; light_attenuation = nothing, @@ -100,26 +99,45 @@ Biogeochemistry(underlying_biogeochemistry; sediment = nothing, particles = nothing, modifiers = nothing) = - Biogeochemistry(underlying_biogeochemistry, - light_attenuation, - sediment, - particles, - modifiers) + DiscreteBiogeochemistry(underlying_biogeochemistry, + light_attenuation, + sediment, + particles, + modifiers) -required_biogeochemical_tracers(bgc::Biogeochemistry) = required_biogeochemical_tracers(bgc.underlying_biogeochemistry) +Biogeochemistry(underlying_biogeochemistry::AbstractContinuousFormBiogeochemistry; + light_attenuation = nothing, + sediment = nothing, + particles = nothing, + modifiers = nothing) = + ContinuousBiogeochemistry(underlying_biogeochemistry, + light_attenuation, + sediment, + particles, + modifiers) -required_biogeochemical_auxiliary_fields(bgc::Biogeochemistry) = required_biogeochemical_auxiliary_fields(bgc.underlying_biogeochemistry) +required_biogeochemical_tracers(bgc::CompleteBiogeochemistry) = required_biogeochemical_tracers(bgc.underlying_biogeochemistry) -biogeochemical_drift_velocity(bgc::Biogeochemistry, val_name) = biogeochemical_drift_velocity(bgc.underlying_biogeochemistry, val_name) +required_biogeochemical_auxiliary_fields(bgc::CompleteBiogeochemistry) = required_biogeochemical_auxiliary_fields(bgc.underlying_biogeochemistry) -biogeochemical_auxiliary_fields(bgc::Biogeochemistry) = merge(biogeochemical_auxiliary_fields(bgc.underlying_biogeochemistry), +biogeochemical_drift_velocity(bgc::CompleteBiogeochemistry, val_name) = biogeochemical_drift_velocity(bgc.underlying_biogeochemistry, val_name) + +biogeochemical_auxiliary_fields(bgc::CompleteBiogeochemistry) = merge(biogeochemical_auxiliary_fields(bgc.underlying_biogeochemistry), biogeochemical_auxiliary_fields(bgc.light_attenuation)) -@inline chlorophyll(bgc::Biogeochemistry, model) = chlorophyll(bgc.underlying_biogeochemistry, model) +@inline chlorophyll(bgc::CompleteBiogeochemistry, model) = chlorophyll(bgc.underlying_biogeochemistry, model) + +@inline adapt_structure(to, bgc::ContinuousBiogeochemistry) = adapt(to, bgc.underlying_biogeochemistry) + +@inline adapt_structure(to, bgc::DiscreteBiogeochemistry) = + DiscreteBiogeochemistry(adapt(to, bgc.underlying_biogeochemistry), + adapt(to, bgc.light_attenuation), + nothing, + nothing, + nothing) -@inline adapt_structure(to, bgc::Biogeochemistry) = adapt(to, bgc.underlying_biogeochemistry) -function update_tendencies!(bgc::Biogeochemistry, model) +function update_tendencies!(bgc::CompleteBiogeochemistry, model) update_tendencies!(bgc, bgc.sediment, model) update_tendencies!(bgc, bgc.particles, model) update_tendencies!(bgc, bgc.modifiers, model) @@ -130,12 +148,12 @@ update_tendencies!(bgc, modifier, model) = nothing update_tendencies!(bgc, modifiers::Tuple, model) = [update_tendencies!(bgc, modifier, model) for modifier in modifiers] # do we still need this for CPU kernels??? -@inline biogeochemical_transition(i, j, k, grid, bgc::Biogeochemistry, val_tracer_name, clock, fields) = +@inline biogeochemical_transition(i, j, k, grid, bgc::CompleteBiogeochemistry, val_tracer_name, clock, fields) = biogeochemical_transition(i, j, k, grid, bgc.underlying_biogeochemistry, val_tracer_name, clock, fields) -@inline (bgc::Biogeochemistry)(args...) = bgc.underlying_biogeochemistry(args...) +@inline (bgc::CompleteBiogeochemistry)(args...) = bgc.underlying_biogeochemistry(args...) -function update_biogeochemical_state!(bgc::Biogeochemistry, model) +function update_biogeochemical_state!(bgc::CompleteBiogeochemistry, model) # TODO: change the order of arguments here since they should definitly be the other way around update_biogeochemical_state!(model, bgc.modifiers) synchronize(device(architecture(model))) @@ -165,9 +183,9 @@ Returns the redfield ratio of `tracer_name` from `bgc` when it is constant acros @inline redfield(val_tracer_name, bgc) = NaN # fallbacks -@inline redfield(i, j, k, val_tracer_name, bgc::Biogeochemistry, tracers) = redfield(i, j, k, val_tracer_name, bgc.underlying_biogeochemistry, tracers) -@inline redfield(val_tracer_name, bgc::Biogeochemistry) = redfield(val_tracer_name, bgc.underlying_biogeochemistry) -@inline redfield(val_tracer_name, bgc::Biogeochemistry, tracers) = redfield(val_tracer_name, bgc.underlying_biogeochemistry, tracers) +@inline redfield(i, j, k, val_tracer_name, bgc::CompleteBiogeochemistry, tracers) = redfield(i, j, k, val_tracer_name, bgc.underlying_biogeochemistry, tracers) +@inline redfield(val_tracer_name, bgc::CompleteBiogeochemistry) = redfield(val_tracer_name, bgc.underlying_biogeochemistry) +@inline redfield(val_tracer_name, bgc::CompleteBiogeochemistry, tracers) = redfield(val_tracer_name, bgc.underlying_biogeochemistry, tracers) @inline redfield(val_tracer_name, bgc, tracers) = redfield(val_tracer_name, bgc) """ @@ -175,10 +193,10 @@ Returns the redfield ratio of `tracer_name` from `bgc` when it is constant acros Returns the names of tracers which together are conserved in `model` """ -conserved_tracers(model::Biogeochemistry, args...; kwargs...) = conserved_tracers(model.underlying_biogeochemistry, args...; kwargs...) +conserved_tracers(model::CompleteBiogeochemistry, args...; kwargs...) = conserved_tracers(model.underlying_biogeochemistry, args...; kwargs...) -summary(bgc::Biogeochemistry) = string("Biogeochemical model based on $(summary(bgc.underlying_biogeochemistry))") -show(io::IO, model::Biogeochemistry) = +summary(bgc::CompleteBiogeochemistry) = string("Biogeochemical model based on $(summary(bgc.underlying_biogeochemistry))") +show(io::IO, model::CompleteBiogeochemistry) = print(io, summary(model.underlying_biogeochemistry), " \n", " Light attenuation: ", summary(model.light_attenuation), "\n", " Sediment: ", summary(model.sediment), "\n", diff --git a/src/Particles/Particles.jl b/src/Particles/Particles.jl index 910b77d67..22a684417 100644 --- a/src/Particles/Particles.jl +++ b/src/Particles/Particles.jl @@ -1,7 +1,7 @@ module Particles using Oceananigans: NonhydrostaticModel, HydrostaticFreeSurfaceModel -using OceanBioME: Biogeochemistry +using OceanBioME: DiscreteBiogeochemistry, ContinuousBiogeochemistry import Oceananigans.Biogeochemistry: update_tendencies! import Oceananigans.Models.LagrangianParticleTracking: update_lagrangian_particle_properties!, step_lagrangian_particles! @@ -12,18 +12,21 @@ abstract type BiogeochemicalParticles end # TODO: add model.particles passing +const BGC_WITH_PARTICLES = Union{<:DiscreteBiogeochemistry{<:Any, <:Any, <:Any, <:BiogeochemicalParticles}, + <:ContinuousBiogeochemistry{<:Any, <:Any, <:Any, <:BiogeochemicalParticles}} + @inline step_lagrangian_particles!(::Nothing, model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, - <:Biogeochemistry{<:Any, <:Any, <:Any, <:BiogeochemicalParticles}}, + BGC_WITH_PARTICLES}, Δt) = update_lagrangian_particle_properties!(model, model.biogeochemistry, Δt) @inline step_lagrangian_particles!(::Nothing, model::HydrostaticFreeSurfaceModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, - <:Biogeochemistry{<:Any, <:Any, <:Any, <:BiogeochemicalParticles}, + BGC_WITH_PARTICLES, <:Any, <:Any, <:Any, <:Any, <:Any,}, Δt) = update_lagrangian_particle_properties!(model, model.biogeochemistry, Δt) -@inline update_lagrangian_particle_properties!(model, bgc::Biogeochemistry{<:Any, <:Any, <:Any, <:BiogeochemicalParticles}, Δt) = +@inline update_lagrangian_particle_properties!(model, bgc::BGC_WITH_PARTICLES, Δt) = update_lagrangian_particle_properties!(bgc.particles, model, bgc, Δt) @inline update_lagrangian_particle_properties!(::BiogeochemicalParticles, model, bgc, Δt) = nothing From e964fe76ef9a6cd9acb3391d3cf286da4bc16574 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Tue, 24 Sep 2024 23:06:10 +0100 Subject: [PATCH 04/33] conservation works now --- Manifest.toml | 913 +++++++++++++++++- Project.toml | 1 + .../PISCES/coupling_utils.jl | 2 +- .../AdvectedPopulations/PISCES/iron/iron.jl | 2 +- .../PISCES/iron/simple_iron.jl | 2 +- .../particulate_organic_matter/carbon.jl | 2 +- .../PISCES/particulate_organic_matter/iron.jl | 2 +- .../micro_meso_zoo_coupling.jl | 2 +- .../two_size_class.jl | 8 +- .../phytoplankton/nutrient_limitation.jl | 2 +- .../PISCES/zooplankton/defaults.jl | 2 +- .../PISCES/zooplankton/grazing_waste.jl | 13 +- .../PISCES/zooplankton/iron_grazing.jl | 6 +- .../PISCES/zooplankton/micro_and_meso.jl | 6 +- .../PISCES/zooplankton/mortality_waste.jl | 3 + test/dependencies_for_runtests.jl | 2 +- test/test_PISCES.jl | 2 +- 17 files changed, 939 insertions(+), 31 deletions(-) diff --git a/Manifest.toml b/Manifest.toml index e6f2f97be..f0915a524 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.10.2" manifest_format = "2.0" -project_hash = "d9a7ed1497e9d29249634fa08034c82fcc96d9c4" +project_hash = "0f443e6e4f3f77224894e0809e37a63fc52baac5" [[deps.AbstractFFTs]] deps = ["LinearAlgebra"] @@ -15,6 +15,11 @@ weakdeps = ["ChainRulesCore", "Test"] AbstractFFTsChainRulesCoreExt = "ChainRulesCore" AbstractFFTsTestExt = "Test" +[[deps.AbstractTrees]] +git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.4.5" + [[deps.Accessors]] deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "LinearAlgebra", "MacroTools", "Markdown", "Test"] git-tree-sha1 = "f61b15be1d76846c0ce31d3fcfac5380ae53db6a" @@ -46,6 +51,23 @@ weakdeps = ["StaticArrays"] [deps.Adapt.extensions] AdaptStaticArraysExt = "StaticArrays" +[[deps.AdaptivePredicates]] +git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6" +uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7" +version = "1.2.0" + +[[deps.AliasTables]] +deps = ["PtrArrays", "Random"] +git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" +uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" +version = "1.1.3" + +[[deps.Animations]] +deps = ["Colors"] +git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d" +uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340" +version = "0.4.1" + [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.1" @@ -59,6 +81,24 @@ git-tree-sha1 = "c06a868224ecba914baa6942988e2f2aade419be" uuid = "a9b6321e-bd34-4604-b9c9-b65b8de01458" version = "0.1.0" +[[deps.Automa]] +deps = ["PrecompileTools", "TranscodingStreams"] +git-tree-sha1 = "014bc22d6c400a7703c0f5dc1fdc302440cf88be" +uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b" +version = "1.0.4" + +[[deps.AxisAlgorithms]] +deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] +git-tree-sha1 = "01b8ccb13d68535d73d2b0c23e39bd23155fb712" +uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950" +version = "1.1.0" + +[[deps.AxisArrays]] +deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"] +git-tree-sha1 = "16351be62963a67ac4083f748fdb3cca58bfd52f" +uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9" +version = "0.4.7" + [[deps.BFloat16s]] deps = ["LinearAlgebra", "Printf", "Random", "Test"] git-tree-sha1 = "2c7cc21e8678eff479978a0a2ef5ce2f51b63dff" @@ -91,6 +131,21 @@ git-tree-sha1 = "5afb5c5ba2688ca43a9ad2e5a91cbb93921ccfa1" uuid = "179af706-886a-5703-950a-314cd64e0468" version = "0.1.3" +[[deps.CRC32c]] +uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" + +[[deps.CRlibm]] +deps = ["CRlibm_jll"] +git-tree-sha1 = "32abd86e3c2025db5172aa182b982debed519834" +uuid = "96374032-68de-5a5b-8d9e-752f78720389" +version = "1.0.1" + +[[deps.CRlibm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" +uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" +version = "1.0.1+0" + [[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 = "fdd9dfb67dfefd548f51000cc400bb51003de247" @@ -125,6 +180,24 @@ git-tree-sha1 = "afea94249b821dc754a8ca6695d3daed851e1f5a" uuid = "76a88914-d11a-5bdc-97e0-2f5a05c973a2" version = "0.14.1+0" +[[deps.Cairo]] +deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] +git-tree-sha1 = "7b6ad8c35f4bc3bca8eb78127c8b99719506a5fb" +uuid = "159f3aea-2a34-519c-b102-8c37f9878175" +version = "1.1.0" + +[[deps.CairoMakie]] +deps = ["CRC32c", "Cairo", "Cairo_jll", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"] +git-tree-sha1 = "4f827b38d3d9ffe6e3b01fbcf866c625fa259ca5" +uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +version = "0.12.11" + +[[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 = "a2f1c8c668c8e3cb4cca4e57a8efdb09067bb3fd" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.18.0+2" + [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] git-tree-sha1 = "71acdbf594aab5bbb2cec89b208c41b4c411e49f" @@ -135,12 +208,34 @@ weakdeps = ["SparseArrays"] [deps.ChainRulesCore.extensions] ChainRulesCoreSparseArraysExt = "SparseArrays" +[[deps.ColorBrewer]] +deps = ["Colors", "JSON", "Test"] +git-tree-sha1 = "61c5334f33d91e570e1d0c3eb5465835242582c4" +uuid = "a2cac450-b92f-5266-8821-25eda20663c8" +version = "0.4.0" + +[[deps.ColorSchemes]] +deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] +git-tree-sha1 = "b5278586822443594ff615963b0c09755771b3e0" +uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" +version = "3.26.0" + [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d" uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" version = "0.11.5" +[[deps.ColorVectorSpace]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] +git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249" +uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" +version = "0.10.0" +weakdeps = ["SpecialFunctions"] + + [deps.ColorVectorSpace.extensions] + SpecialFunctionsExt = "SpecialFunctions" + [[deps.Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] git-tree-sha1 = "362a287c3aa50601b0bc359053d5c2468f0e7ce0" @@ -186,16 +281,17 @@ weakdeps = ["InverseFunctions"] git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" version = "1.5.8" +weakdeps = ["IntervalSets", "LinearAlgebra", "StaticArrays"] [deps.ConstructionBase.extensions] ConstructionBaseIntervalSetsExt = "IntervalSets" ConstructionBaseLinearAlgebraExt = "LinearAlgebra" ConstructionBaseStaticArraysExt = "StaticArrays" - [deps.ConstructionBase.weakdeps] - IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" - LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +[[deps.Contour]] +git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" +uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" +version = "0.6.3" [[deps.Crayons]] git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" @@ -234,6 +330,12 @@ version = "1.0.0" deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +[[deps.DelaunayTriangulation]] +deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "Random"] +git-tree-sha1 = "94eb20e6621600f4315813b1d1fc9b8a5a6a34db" +uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" +version = "1.4.0" + [[deps.DiskArrays]] deps = ["LRUCache", "OffsetArrays"] git-tree-sha1 = "ef25c513cad08d7ebbed158c91768ae32f308336" @@ -255,6 +357,22 @@ weakdeps = ["ChainRulesCore", "SparseArrays"] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" +[[deps.Distributions]] +deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] +git-tree-sha1 = "e6c693a0e4394f8fda0e51a5bdf5aef26f8235e9" +uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" +version = "0.25.111" + + [deps.Distributions.extensions] + DistributionsChainRulesCoreExt = "ChainRulesCore" + DistributionsDensityInterfaceExt = "DensityInterface" + DistributionsTestExt = "Test" + + [deps.Distributions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + [[deps.DocStringExtensions]] deps = ["LibGit2"] git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" @@ -266,11 +384,50 @@ deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" +[[deps.EarCut_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e3290f2d49e661fbd94046d7e3726ffcb2d41053" +uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" +version = "2.2.4+0" + +[[deps.EnumX]] +git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" +uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" +version = "1.0.4" + +[[deps.ErrorfreeArithmetic]] +git-tree-sha1 = "d6863c556f1142a061532e79f611aa46be201686" +uuid = "90fa49ef-747e-5e6f-a989-263ba693cf1a" +version = "0.5.2" + +[[deps.ExactPredicates]] +deps = ["IntervalArithmetic", "Random", "StaticArraysCore", "Test"] +git-tree-sha1 = "276e83bc8b21589b79303b9985c321024ffdf59c" +uuid = "429591f6-91af-11e9-00e2-59fbe8cec110" +version = "2.2.5" + +[[deps.Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.6.2+0" + [[deps.ExprTools]] git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" version = "0.1.10" +[[deps.Extents]] +git-tree-sha1 = "81023caa0021a41712685887db1fc03db26f41f5" +uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" +version = "0.1.4" + +[[deps.FFMPEG_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] +git-tree-sha1 = "8cc47f299902e13f90405ddb5bf87e5d474c0d38" +uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" +version = "6.1.2+0" + [[deps.FFTW]] deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" @@ -283,21 +440,91 @@ git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" version = "3.3.10+0" +[[deps.FastRounding]] +deps = ["ErrorfreeArithmetic", "LinearAlgebra"] +git-tree-sha1 = "6344aa18f654196be82e62816935225b3b9abe44" +uuid = "fa42c844-2597-5d31-933b-ebd51ab2693f" +version = "0.3.1" + [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] git-tree-sha1 = "82d8afa92ecf4b52d78d869f038ebfb881267322" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" version = "1.16.3" +[[deps.FilePaths]] +deps = ["FilePathsBase", "MacroTools", "Reexport", "Requires"] +git-tree-sha1 = "919d9412dbf53a2e6fe74af62a73ceed0bce0629" +uuid = "8fc22ac5-c921-52a6-82fd-178b2807b824" +version = "0.8.3" + +[[deps.FilePathsBase]] +deps = ["Compat", "Dates"] +git-tree-sha1 = "7878ff7172a8e6beedd1dea14bd27c3c6340d361" +uuid = "48062228-2e41-5def-b9a4-89aafe57970f" +version = "0.9.22" +weakdeps = ["Mmap", "Test"] + + [deps.FilePathsBase.extensions] + FilePathsBaseMmapExt = "Mmap" + FilePathsBaseTestExt = "Test" + [[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" +[[deps.FillArrays]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" +uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" +version = "1.13.0" +weakdeps = ["PDMats", "SparseArrays", "Statistics"] + + [deps.FillArrays.extensions] + FillArraysPDMatsExt = "PDMats" + FillArraysSparseArraysExt = "SparseArrays" + FillArraysStatisticsExt = "Statistics" + [[deps.FixedPointNumbers]] deps = ["Statistics"] git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" version = "0.8.5" +[[deps.Fontconfig_jll]] +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.96+0" + +[[deps.Format]] +git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" +uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" +version = "1.3.7" + +[[deps.FreeType]] +deps = ["CEnum", "FreeType2_jll"] +git-tree-sha1 = "907369da0f8e80728ab49c1c7e09327bf0d6d999" +uuid = "b38be410-82b0-50bf-ab77-7b57e271db43" +version = "4.1.1" + +[[deps.FreeType2_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" +uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" +version = "2.13.2+0" + +[[deps.FreeTypeAbstraction]] +deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"] +git-tree-sha1 = "2493cdfd0740015955a8e46de4ef28f49460d8bc" +uuid = "663a7486-cb36-511b-a19d-713bb74d65c9" +version = "0.10.3" + +[[deps.FriBidi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" +uuid = "559328eb-81f9-559d-9380-de523a88c83c" +version = "1.0.14+0" + [[deps.Future]] deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" @@ -325,6 +552,29 @@ git-tree-sha1 = "ab29216184312f99ff957b32cd63c2fe9c928b91" uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" version = "0.26.7" +[[deps.GeoFormatTypes]] +git-tree-sha1 = "59107c179a586f0fe667024c5eb7033e81333271" +uuid = "68eda718-8dee-11e9-39e7-89f7f65f511f" +version = "0.4.2" + +[[deps.GeoInterface]] +deps = ["Extents", "GeoFormatTypes"] +git-tree-sha1 = "5921fc0704e40c024571eca551800c699f86ceb4" +uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" +version = "1.3.6" + +[[deps.GeometryBasics]] +deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "b62f2b2d76cee0d61a2ef2b3118cd2a3215d3134" +uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" +version = "0.4.11" + +[[deps.Gettext_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" +uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" +version = "0.21.0+0" + [[deps.GibbsSeaWater]] deps = ["GibbsSeaWater_jll", "Libdl", "Test"] git-tree-sha1 = "d1642ddc78d0754603d747d76fac0042a5a85104" @@ -337,6 +587,12 @@ git-tree-sha1 = "c91ca76546871efaa1aefdd2b19cc41c3ead2160" uuid = "6727f6b2-98ea-5d0a-8239-2f72283ddb11" version = "3.5.2+0" +[[deps.Glib_jll]] +deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "7c82e6a6cd34e9d935e9aa4051b66c6ff3af59ba" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.80.2+0" + [[deps.Glob]] git-tree-sha1 = "97285bbd5230dd766e9ef6749b80fc617126d496" uuid = "c27321d9-0574-5035-807b-f59d2c89b15c" @@ -348,24 +604,105 @@ git-tree-sha1 = "383db7d3f900f4c1f47a8a04115b053c095e48d3" uuid = "0951126a-58fd-58f1-b5b3-b08c7c4a876d" version = "3.8.4+0" +[[deps.Graphics]] +deps = ["Colors", "LinearAlgebra", "NaNMath"] +git-tree-sha1 = "d61890399bc535850c4bf08e4e0d3a7ad0f21cbd" +uuid = "a2bd30eb-e257-5431-a919-1863eab51364" +version = "1.1.2" + +[[deps.Graphite2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" +uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" +version = "1.3.14+0" + +[[deps.GridLayoutBase]] +deps = ["GeometryBasics", "InteractiveUtils", "Observables"] +git-tree-sha1 = "fc713f007cff99ff9e50accba6373624ddd33588" +uuid = "3955a311-db13-416c-9275-1d80ed98e5e9" +version = "0.11.0" + +[[deps.Grisu]] +git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" +uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" +version = "1.0.2" + [[deps.HDF5_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"] git-tree-sha1 = "38c8874692d48d5440d5752d6c74b0c6b0b60739" uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" version = "1.14.2+1" +[[deps.HarfBuzz_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] +git-tree-sha1 = "401e4f3f30f43af2c8478fc008da50096ea5240f" +uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" +version = "8.3.1+0" + [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "5e19e1e4fa3e71b774ce746274364aef0234634e" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" version = "2.11.1+0" +[[deps.HypergeometricFunctions]] +deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] +git-tree-sha1 = "7c4195be1649ae622304031ed46a2f4df989f1eb" +uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" +version = "0.3.24" + +[[deps.ImageAxes]] +deps = ["AxisArrays", "ImageBase", "ImageCore", "Reexport", "SimpleTraits"] +git-tree-sha1 = "2e4520d67b0cef90865b3ef727594d2a58e0e1f8" +uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac" +version = "0.6.11" + +[[deps.ImageBase]] +deps = ["ImageCore", "Reexport"] +git-tree-sha1 = "eb49b82c172811fd2c86759fa0553a2221feb909" +uuid = "c817782e-172a-44cc-b673-b171935fbb9e" +version = "0.1.7" + +[[deps.ImageCore]] +deps = ["ColorVectorSpace", "Colors", "FixedPointNumbers", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "PrecompileTools", "Reexport"] +git-tree-sha1 = "b2a7eaa169c13f5bcae8131a83bc30eff8f71be0" +uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534" +version = "0.10.2" + +[[deps.ImageIO]] +deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs"] +git-tree-sha1 = "437abb322a41d527c197fa800455f79d414f0a3c" +uuid = "82e4d734-157c-48bb-816b-45c225c6df19" +version = "0.6.8" + +[[deps.ImageMetadata]] +deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"] +git-tree-sha1 = "355e2b974f2e3212a75dfb60519de21361ad3cb7" +uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49" +version = "0.9.9" + +[[deps.Imath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "0936ba688c6d201805a83da835b55c61a180db52" +uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1" +version = "3.1.11+0" + [[deps.IncompleteLU]] deps = ["LinearAlgebra", "SparseArrays"] git-tree-sha1 = "6c676e79f98abb6d33fa28122cad099f1e464afe" uuid = "40713840-3770-5561-ab4c-a76e7d0d7895" version = "0.2.1" +[[deps.IndirectArrays]] +git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" +uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" +version = "1.0.0" + +[[deps.Inflate]] +git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d" +uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" +version = "0.1.5" + [[deps.InlineStrings]] git-tree-sha1 = "45521d31238e87ee9f9732561bfee12d4eebd52d" uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" @@ -389,6 +726,33 @@ version = "2024.2.1+0" deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +[[deps.Interpolations]] +deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"] +git-tree-sha1 = "88a101217d7cb38a7b481ccd50d21876e1d1b0e0" +uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" +version = "0.15.1" +weakdeps = ["Unitful"] + + [deps.Interpolations.extensions] + InterpolationsUnitfulExt = "Unitful" + +[[deps.IntervalArithmetic]] +deps = ["CRlibm", "FastRounding", "LinearAlgebra", "Markdown", "Random", "RecipesBase", "RoundingEmulator", "SetRounding", "StaticArrays"] +git-tree-sha1 = "5ab7744289be503d76a944784bac3f2df7b809af" +uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" +version = "0.20.9" + +[[deps.IntervalSets]] +git-tree-sha1 = "dba9ddf07f77f60450fe5d2e2beb9854d9a49bd0" +uuid = "8197267c-284f-5f27-9208-e0e47529a953" +version = "0.7.10" +weakdeps = ["Random", "RecipesBase", "Statistics"] + + [deps.IntervalSets.extensions] + IntervalSetsRandomExt = "Random" + IntervalSetsRecipesBaseExt = "RecipesBase" + IntervalSetsStatisticsExt = "Statistics" + [[deps.InverseFunctions]] git-tree-sha1 = "2787db24f4e03daf859c6509ff87764e4182f7d1" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" @@ -404,6 +768,22 @@ git-tree-sha1 = "0dc7b50b8d436461be01300fd8cd45aa0274b038" uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" version = "1.3.0" +[[deps.IrrationalConstants]] +git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.2.2" + +[[deps.Isoband]] +deps = ["isoband_jll"] +git-tree-sha1 = "f9b6d97355599074dc867318950adaa6f9946137" +uuid = "f1662d9f-8043-43de-a69a-05efc1cc6ff4" +version = "0.1.1" + +[[deps.IterTools]] +git-tree-sha1 = "42d5f897009e7ff2cf88db414a389e5ed1bdd023" +uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" +version = "1.10.0" + [[deps.IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] git-tree-sha1 = "59545b0a2b27208b0650df0a46b8e3019f85055b" @@ -427,6 +807,24 @@ git-tree-sha1 = "f389674c99bfcde17dc57454011aa44d5a260a40" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" version = "1.6.0" +[[deps.JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.4" + +[[deps.JpegTurbo]] +deps = ["CEnum", "FileIO", "ImageCore", "JpegTurbo_jll", "TOML"] +git-tree-sha1 = "fa6d0bcff8583bac20f1ffa708c3913ca605c611" +uuid = "b835a17e-a41a-41e7-81f0-2f016b05efe0" +version = "0.1.5" + +[[deps.JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c84a835e1a09b289ffcd2271bf2a337bbdda6637" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "3.0.3+0" + [[deps.JuliaNVTXCallbacks_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "af433a10f3942e882d3c671aacb203e006a5808f" @@ -449,6 +847,18 @@ version = "0.9.25" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +[[deps.KernelDensity]] +deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] +git-tree-sha1 = "7d703202e65efa1369de1279c162b915e245eed1" +uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" +version = "0.6.9" + +[[deps.LAME_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd" +uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" +version = "3.100.2+0" + [[deps.LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Preferences", "Printf", "Requires", "Unicode"] git-tree-sha1 = "2470e69781ddd70b8878491233cd09bc1bd7fc96" @@ -485,6 +895,12 @@ weakdeps = ["Serialization"] [deps.LRUCache.extensions] SerializationExt = ["Serialization"] +[[deps.LZO_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "70c5da094887fd2cae843b8db33920bac4b6f07d" +uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" +version = "2.10.2+0" + [[deps.LaTeXStrings]] git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" @@ -494,6 +910,11 @@ version = "1.3.1" deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" +[[deps.LazyModules]] +git-tree-sha1 = "a560dd966b386ac9ae60bdd3a3d3a326062d3c3e" +uuid = "8cdb02fc-e678-4876-92c5-9defec4f444e" +version = "0.3.1" + [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" @@ -521,16 +942,62 @@ version = "1.11.0+1" [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +[[deps.Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.2.2+1" + +[[deps.Libgcrypt_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] +git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" +uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" +version = "1.8.11+0" + +[[deps.Libgpg_error_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" +uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" +version = "1.49.0+0" + [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" version = "1.17.0+0" +[[deps.Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.40.1+0" + +[[deps.Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.40.1+0" + [[deps.LinearAlgebra]] deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +[[deps.LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.3.28" + + [deps.LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [deps.LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" @@ -584,10 +1051,33 @@ git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" version = "0.5.13" +[[deps.Makie]] +deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"] +git-tree-sha1 = "2281aaf0685e5e8a559982d32f17d617a949b9cd" +uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" +version = "0.21.11" + +[[deps.MakieCore]] +deps = ["ColorTypes", "GeometryBasics", "IntervalSets", "Observables"] +git-tree-sha1 = "22fed09860ca73537a36d4e5a9bce0d9e80ee8a8" +uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" +version = "0.8.8" + +[[deps.MappedArrays]] +git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e" +uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" +version = "0.4.2" + [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +[[deps.MathTeXEngine]] +deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"] +git-tree-sha1 = "e1641f32ae592e415e3dbae7f4a188b5316d4b62" +uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" +version = "0.6.1" + [[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" @@ -608,6 +1098,12 @@ version = "1.2.0" [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" +[[deps.MosaicViews]] +deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"] +git-tree-sha1 = "7b86a5d4d70a9f5cdf2dacb3cbe6d251d1a61dbe" +uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389" +version = "0.3.4" + [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2023.1.10" @@ -630,12 +1126,24 @@ git-tree-sha1 = "ce3269ed42816bf18d500c9f63418d4b0d9f5a3b" uuid = "e98f9f5b-d649-5603-91fd-7774390e6439" version = "3.1.0+2" +[[deps.NaNMath]] +deps = ["OpenLibm_jll"] +git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" +uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" +version = "1.0.2" + [[deps.NetCDF_jll]] deps = ["Artifacts", "Blosc_jll", "Bzip2_jll", "HDF5_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenMPI_jll", "XML2_jll", "Zlib_jll", "Zstd_jll", "libzip_jll"] git-tree-sha1 = "a8af1798e4eb9ff768ce7fdefc0e957097793f15" uuid = "7243133f-43d8-5620-bbf4-c2c921802cf3" version = "400.902.209+0" +[[deps.Netpbm]] +deps = ["FileIO", "ImageCore", "ImageMetadata"] +git-tree-sha1 = "d92b107dbb887293622df7697a2223f9f8176fcd" +uuid = "f09324ee-3d7c-5217-9330-fc30815ba969" +version = "1.1.1" + [[deps.Nettle_jll]] deps = ["Artifacts", "GMP_jll", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "eca63e3847dad608cfa6a3329b95ef674c7160b4" @@ -646,6 +1154,11 @@ version = "3.7.2+0" uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" +[[deps.Observables]] +git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" +uuid = "510215fc-4207-5dde-b226-833fc4488ee2" +version = "0.5.5" + [[deps.Oceananigans]] deps = ["Adapt", "CUDA", "Crayons", "CubedSphere", "Dates", "Distances", "DocStringExtensions", "FFTW", "Glob", "IncompleteLU", "InteractiveUtils", "IterativeSolvers", "JLD2", "KernelAbstractions", "LinearAlgebra", "Logging", "MPI", "NCDatasets", "OffsetArrays", "OrderedCollections", "Pkg", "Printf", "Random", "Rotations", "SeawaterPolynomials", "SparseArrays", "Statistics", "StructArrays"] git-tree-sha1 = "9b1b114e7853bd744ad3feff93232a1e5747ffa1" @@ -670,11 +1183,34 @@ weakdeps = ["Adapt"] [deps.OffsetArrays.extensions] OffsetArraysAdaptExt = "Adapt" +[[deps.Ogg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" +uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" +version = "1.3.5+1" + [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" version = "0.3.23+4" +[[deps.OpenEXR]] +deps = ["Colors", "FileIO", "OpenEXR_jll"] +git-tree-sha1 = "327f53360fdb54df7ecd01e96ef1983536d1e633" +uuid = "52e1d378-f018-4a11-a4be-720524705ac7" +version = "0.3.2" + +[[deps.OpenEXR_jll]] +deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "8292dd5c8a38257111ada2174000a33745b06d4e" +uuid = "18a262bb-aa17-5467-a713-aee519bc75cb" +version = "3.2.4+0" + +[[deps.OpenLibm_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.1+2" + [[deps.OpenMPI_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"] git-tree-sha1 = "bfce6d523861a6c562721b262c0d1aaeead2647f" @@ -687,6 +1223,18 @@ git-tree-sha1 = "1b35263570443fdd9e76c76b7062116e2f374ab8" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" version = "3.0.15+0" +[[deps.OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.5+0" + +[[deps.Opus_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6703a85cb3781bd5909d48730a67205f3f31a575" +uuid = "91d4177d-7536-5919-b921-800302f37372" +version = "1.3.3+0" + [[deps.OrderedCollections]] git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" @@ -698,6 +1246,53 @@ git-tree-sha1 = "2cd396108e178f3ae8dedbd8e938a18726ab2fbf" uuid = "c2071276-7c44-58a7-b746-946036e04d0a" version = "0.24.1+0" +[[deps.PCRE2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" +version = "10.42.0+1" + +[[deps.PDMats]] +deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] +git-tree-sha1 = "949347156c25054de2db3b166c52ac4728cbad65" +uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" +version = "0.11.31" + +[[deps.PNGFiles]] +deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"] +git-tree-sha1 = "67186a2bc9a90f9f85ff3cc8277868961fb57cbd" +uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883" +version = "0.4.3" + +[[deps.Packing]] +deps = ["GeometryBasics"] +git-tree-sha1 = "ec3edfe723df33528e085e632414499f26650501" +uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566" +version = "0.5.0" + +[[deps.PaddedViews]] +deps = ["OffsetArrays"] +git-tree-sha1 = "0fac6313486baae819364c52b4f483450a9d793f" +uuid = "5432bcbf-9aad-5242-b902-cca2824c8663" +version = "0.5.12" + +[[deps.Pango_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e127b609fb9ecba6f201ba7ab753d5a605d53801" +uuid = "36c8627f-9965-5494-a995-c6b170f724f3" +version = "1.54.1+0" + +[[deps.Parsers]] +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.8.1" + +[[deps.Pixman_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] +git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +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"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" @@ -709,6 +1304,17 @@ git-tree-sha1 = "f9501cc0430a26bc3d156ae1b5b0c1b47af4d6da" uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688" version = "0.3.3" +[[deps.PlotUtils]] +deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "Statistics"] +git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5" +uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" +version = "1.4.1" + +[[deps.PolygonOps]] +git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6" +uuid = "647866c9-e3ac-4575-94e7-e3d426903924" +version = "0.1.2" + [[deps.PooledArrays]] deps = ["DataAPI", "Future"] git-tree-sha1 = "36d8b4b899628fb92c2749eb488d884a926614d3" @@ -737,6 +1343,35 @@ version = "2.3.2" deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" +[[deps.ProgressMeter]] +deps = ["Distributed", "Printf"] +git-tree-sha1 = "8f6bc219586aef8baf0ff9a5fe16ee9c70cb65e4" +uuid = "92933f4c-e287-5a05-a399-4b506db050ca" +version = "1.10.2" + +[[deps.PtrArrays]] +git-tree-sha1 = "77a42d78b6a92df47ab37e177b2deac405e1c88f" +uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" +version = "1.2.1" + +[[deps.QOI]] +deps = ["ColorTypes", "FileIO", "FixedPointNumbers"] +git-tree-sha1 = "18e8f4d1426e965c7b532ddd260599e1510d26ce" +uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65" +version = "1.0.0" + +[[deps.QuadGK]] +deps = ["DataStructures", "LinearAlgebra"] +git-tree-sha1 = "cda3b045cf9ef07a08ad46731f5a3165e56cf3da" +uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" +version = "2.11.1" + + [deps.QuadGK.extensions] + QuadGKEnzymeExt = "Enzyme" + + [deps.QuadGK.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + [[deps.Quaternions]] deps = ["LinearAlgebra", "Random", "RealDot"] git-tree-sha1 = "994cc27cdacca10e68feb291673ec3a76aa2fae9" @@ -763,6 +1398,21 @@ git-tree-sha1 = "c6ec94d2aaba1ab2ff983052cf6a606ca5985902" uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143" version = "1.6.0" +[[deps.RangeArrays]] +git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5" +uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d" +version = "0.3.2" + +[[deps.Ratios]] +deps = ["Requires"] +git-tree-sha1 = "1342a47bf3260ee108163042310d26f2be5ec90b" +uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439" +version = "0.4.5" +weakdeps = ["FixedPointNumbers"] + + [deps.Ratios.extensions] + RatiosFixedPointNumbersExt = "FixedPointNumbers" + [[deps.RealDot]] deps = ["LinearAlgebra"] git-tree-sha1 = "9f0a1b71baaf7650f4fa8a1d168c7fb6ee41f0c9" @@ -780,12 +1430,30 @@ git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" version = "1.2.2" +[[deps.RelocatableFolders]] +deps = ["SHA", "Scratch"] +git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" +uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" +version = "1.0.1" + [[deps.Requires]] deps = ["UUIDs"] git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" +[[deps.Rmath]] +deps = ["Random", "Rmath_jll"] +git-tree-sha1 = "852bd0f55565a9e973fcfee83a84413270224dc4" +uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" +version = "0.8.0" + +[[deps.Rmath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "58cdd8fb2201a6267e1db87ff148dd6c1dbd8ad8" +uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" +version = "0.5.1+0" + [[deps.Roots]] deps = ["Accessors", "ChainRulesCore", "CommonSolve", "Printf"] git-tree-sha1 = "48a7925c1d971b03bb81183b99d82c1dc7a3562f" @@ -814,10 +1482,21 @@ weakdeps = ["RecipesBase"] [deps.Rotations.extensions] RotationsRecipesBaseExt = "RecipesBase" +[[deps.RoundingEmulator]] +git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" +uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" +version = "0.2.1" + [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" version = "0.7.0" +[[deps.SIMD]] +deps = ["PrecompileTools"] +git-tree-sha1 = "98ca7c29edd6fc79cd74c61accb7010a4e7aee33" +uuid = "fdea26ae-647d-5447-a871-4b548cad5224" +version = "3.6.0" + [[deps.Scratch]] deps = ["Dates"] git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386" @@ -838,6 +1517,45 @@ version = "1.4.5" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +[[deps.SetRounding]] +git-tree-sha1 = "d7a25e439d07a17b7cdf97eecee504c50fedf5f6" +uuid = "3cc68bcd-71a2-5612-b932-767ffbe40ab0" +version = "0.2.1" + +[[deps.ShaderAbstractions]] +deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "79123bc60c5507f035e6d1d9e563bb2971954ec8" +uuid = "65257c39-d410-5151-9873-9b3e5be5013e" +version = "0.4.1" + +[[deps.SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[deps.Showoff]] +deps = ["Dates", "Grisu"] +git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" +uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" +version = "1.0.3" + +[[deps.SignedDistanceFields]] +deps = ["Random", "Statistics", "Test"] +git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9" +uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96" +version = "0.4.0" + +[[deps.SimpleTraits]] +deps = ["InteractiveUtils", "MacroTools"] +git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" +uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" +version = "0.9.4" + +[[deps.Sixel]] +deps = ["Dates", "FileIO", "ImageCore", "IndirectArrays", "OffsetArrays", "REPL", "libsixel_jll"] +git-tree-sha1 = "2da10356e31327c7096832eb9cd86307a50b1eb6" +uuid = "45858cf5-a6b0-47a3-bbea-62219f50df47" +version = "0.1.3" + [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" @@ -852,6 +1570,22 @@ deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" version = "1.10.0" +[[deps.SpecialFunctions]] +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "2.4.0" +weakdeps = ["ChainRulesCore"] + + [deps.SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" + +[[deps.StackViews]] +deps = ["OffsetArrays"] +git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c" +uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15" +version = "0.1.1" + [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" @@ -879,6 +1613,23 @@ git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" version = "1.7.0" +[[deps.StatsBase]] +deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21" +uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +version = "0.34.3" + +[[deps.StatsFuns]] +deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] +git-tree-sha1 = "b423576adc27097764a90e163157bcfc9acf0f46" +uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" +version = "1.3.2" +weakdeps = ["ChainRulesCore", "InverseFunctions"] + + [deps.StatsFuns.extensions] + StatsFunsChainRulesCoreExt = "ChainRulesCore" + StatsFunsInverseFunctionsExt = "InverseFunctions" + [[deps.StringManipulation]] deps = ["PrecompileTools"] git-tree-sha1 = "a04cabe79c5f01f4d723cc6704070ada0b9d46d5" @@ -898,6 +1649,10 @@ weakdeps = ["Adapt", "GPUArraysCore", "SparseArrays", "StaticArrays"] StructArraysSparseArraysExt = "SparseArrays" StructArraysStaticArraysExt = "StaticArrays" +[[deps.SuiteSparse]] +deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] +uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" + [[deps.SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" @@ -943,10 +1698,22 @@ version = "0.17.8" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +[[deps.TensorCore]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" +uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" +version = "0.1.1" + [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +[[deps.TiffImages]] +deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "SIMD", "UUIDs"] +git-tree-sha1 = "bc7fd5c91041f44636b2c134041f7e5263ce58ae" +uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69" +version = "0.10.0" + [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] git-tree-sha1 = "5a13ae8a41237cff5ecf34f73eb1b8f42fff6531" @@ -958,6 +1725,11 @@ git-tree-sha1 = "e84b3a11b9bece70d14cce63406bbc79ed3464d2" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" version = "0.11.2" +[[deps.TriplotBase]] +git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" +uuid = "981d1d27-644d-49a2-9326-4793e63143c3" +version = "0.1.0" + [[deps.UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" @@ -965,6 +1737,23 @@ uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +[[deps.UnicodeFun]] +deps = ["REPL"] +git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" +uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" +version = "0.4.1" + +[[deps.Unitful]] +deps = ["Dates", "LinearAlgebra", "Random"] +git-tree-sha1 = "d95fe458f26209c66a187b1114df96fd70839efd" +uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" +version = "1.21.0" +weakdeps = ["ConstructionBase", "InverseFunctions"] + + [deps.Unitful.extensions] + ConstructionBaseUnitfulExt = "ConstructionBase" + InverseFunctionsUnitfulExt = "InverseFunctions" + [[deps.UnsafeAtomics]] git-tree-sha1 = "6331ac3440856ea1988316b46045303bef658278" uuid = "013be700-e6cd-48c3-b4a1-df204f14c38f" @@ -976,18 +1765,78 @@ git-tree-sha1 = "2d17fabcd17e67d7625ce9c531fb9f40b7c42ce4" uuid = "d80eeb9a-aca5-4d75-85e5-170c8b632249" version = "0.2.1" +[[deps.WoodburyMatrices]] +deps = ["LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "c1a7aa6219628fcd757dede0ca95e245c5cd9511" +uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6" +version = "1.0.0" + [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] git-tree-sha1 = "1165b0443d0eca63ac1e32b8c0eb69ed2f4f8127" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" version = "2.13.3+0" +[[deps.XSLT_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" +uuid = "aed1982a-8fda-507f-9586-7b0439959a61" +version = "1.1.41+0" + [[deps.XZ_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "ac88fb95ae6447c8dda6a5503f3bafd496ae8632" uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" version = "5.4.6+0" +[[deps.Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.8.6+0" + +[[deps.Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.11+0" + +[[deps.Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.4+0" + +[[deps.Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.6+0" + +[[deps.Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.11+0" + +[[deps.Xorg_libpthread_stubs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" +uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" +version = "0.1.1+0" + +[[deps.Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.17.0+0" + +[[deps.Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.5.0+0" + [[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" @@ -999,17 +1848,59 @@ git-tree-sha1 = "e678132f07ddb5bfa46857f0d7620fb9be675d3b" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" version = "1.5.6+0" +[[deps.isoband_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "51b5eeb3f98367157a7a12a1fb0aa5328946c03c" +uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4" +version = "0.2.3+0" + [[deps.libaec_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" version = "1.1.2+0" +[[deps.libaom_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1827acba325fdcdf1d2647fc8d5301dd9ba43a9d" +uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" +version = "3.9.0+0" + +[[deps.libass_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "e17c115d55c5fbb7e52ebedb427a0dca79d4484e" +uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" +version = "0.15.2+0" + [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" version = "5.8.0+1" +[[deps.libfdk_aac_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8a22cf860a7d27e4f3498a0fe0811a7957badb38" +uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" +version = "2.0.3+0" + +[[deps.libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "d7015d2e18a5fd9a4f47de711837e980519781a4" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.43+1" + +[[deps.libsixel_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] +git-tree-sha1 = "d4f63314c8aa1e48cd22aa0c17ed76cd1ae48c3c" +uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" +version = "1.10.3+0" + +[[deps.libvorbis_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] +git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3" +uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" +version = "1.3.7+2" + [[deps.libzip_jll]] deps = ["Artifacts", "Bzip2_jll", "GnuTLS_jll", "JLLWrappers", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] git-tree-sha1 = "3282b7d16ae7ac3e57ec2f3fa8fafb564d8f9f7f" @@ -1031,3 +1922,15 @@ version = "2021.12.0+0" deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" version = "17.4.0+2" + +[[deps.x264_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "35976a1216d6c066ea32cba2150c4fa682b276fc" +uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" +version = "10164.0.0+0" + +[[deps.x265_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "dcc541bb19ed5b0ede95581fb2e41ecf179527d2" +uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" +version = "3.6.0+0" diff --git a/Project.toml b/Project.toml index bf71dfbee..c87bedccf 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.11.1" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" GibbsSeaWater = "9a22fb26-0b63-4589-b28e-8f9d0b5c3d05" JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" diff --git a/src/Models/AdvectedPopulations/PISCES/coupling_utils.jl b/src/Models/AdvectedPopulations/PISCES/coupling_utils.jl index 7684f5e9b..f9a4b7d56 100644 --- a/src/Models/AdvectedPopulations/PISCES/coupling_utils.jl +++ b/src/Models/AdvectedPopulations/PISCES/coupling_utils.jl @@ -22,7 +22,7 @@ import OceanBioME.Models.Sediments: nitrogen_flux, carbon_flux, remineralisation # iron ratio for DOC might be wrong iron = (tracers = (:PFe, :DFe, :Z, :M, :SFe, :BFe, :Fe), - scalefactors = (1, 1, bgc.zooplankton.micro.iron_ratio, bgc.zooplankton.micro.iron_ratio, 1, 1, 1)) + scalefactors = (1, 1, bgc.zooplankton.micro.iron_ratio, bgc.zooplankton.meso.iron_ratio, 1, 1, 1)) θ_PO₄ = bgc.phosphate_redfield_ratio phosphate = (tracers = (:P, :D, :Z, :M, :DOC, :POC, :GOC, :PO₄), diff --git a/src/Models/AdvectedPopulations/PISCES/iron/iron.jl b/src/Models/AdvectedPopulations/PISCES/iron/iron.jl index 259bb29ee..d0edc7519 100644 --- a/src/Models/AdvectedPopulations/PISCES/iron/iron.jl +++ b/src/Models/AdvectedPopulations/PISCES/iron/iron.jl @@ -15,7 +15,7 @@ using OceanBioME.Models.PISCESModel.ParticulateOrganicMatter: using OceanBioME.Models.PISCESModel.Phytoplankton: uptake using OceanBioME.Models.PISCESModel.Zooplankton: - non_assimilated_iron, upper_trophic_iron_waste + non_assimilated_iron, upper_trophic_dissolved_iron import Oceananigans.Biogeochemistry: required_biogeochemical_tracers import OceanBioME.Models.PISCESModel: free_iron diff --git a/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl b/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl index fdda45cee..44cd9ac72 100644 --- a/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl +++ b/src/Models/AdvectedPopulations/PISCES/iron/simple_iron.jl @@ -46,7 +46,7 @@ const SimpleIronPISCES = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:SimpleIron} # waste grazing_waste = non_assimilated_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - upper_trophic_waste = upper_trophic_iron_waste(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + upper_trophic_waste = upper_trophic_dissolved_iron(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (small_particles + grazing_waste + upper_trophic_waste - consumption - ligand_aggregation - colloidal_aggregation - scavenging - BactFe) diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl index ac625a862..6d3b327df 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl @@ -42,7 +42,7 @@ end large_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + upper_trophic_feces + return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + upper_trophic_feces + aggregation_to_large - grazing - large_breakdown) end diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl index 29ad4d078..adc86302b 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/iron.jl @@ -84,7 +84,7 @@ end large_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + upper_trophic_feces - + scavenging + bacterial_assimilation + colloidal_aggregation + + scavenging + bacterial_assimilation + colloidal_aggregation + aggregation_to_large - grazing - large_breakdown) end diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl index 25f0affad..dc1cb979f 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/micro_meso_zoo_coupling.jl @@ -28,5 +28,5 @@ using OceanBioME.Models.PISCESModel.Zooplankton: non_assimilated_waste (grazing(zoo, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + flux_feeding(zoo, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields)) -@inline total_grazing(zoo::MicroAndMeso, val_prey_name::Val{:GOC}, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = +@inline total_grazing(zoo::MicroAndMeso, val_prey_name::LARGE_PARTICLE_COMPONENTS, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = flux_feeding(zoo, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl index fc488d461..67fd01ba0 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl @@ -50,13 +50,13 @@ required_biogeochemical_tracers(::TwoCompartementCarbonIronParticles) = (:POC, : @inline flux_rate(::Val{:SFe}, i, j, k, grid, fields, auxiliary_fields) = @inbounds fields.SFe[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, auxiliary_fields.wPOC) @inline flux_rate(::Val{:BFe}, i, j, k, grid, fields, auxiliary_fields) = @inbounds fields.BFe[i, j, k] * ℑzᵃᵃᶜ(i, j, k, grid, auxiliary_fields.wGOC) -const small_particle_components = Union{Val{:POC}, Val{:SFe}} -const large_particle_components = Union{Val{:GOC}, Val{:BFe}, Val{:PSi}, Val{:CaCO₃}} +const SMALL_PARTICLE_COMPONENTS = Union{Val{:POC}, Val{:SFe}} +const LARGE_PARTICLE_COMPONENTS = Union{Val{:GOC}, Val{:BFe}, Val{:PSi}, Val{:CaCO₃}} -biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::small_particle_components) = +biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::SMALL_PARTICLE_COMPONENTS) = (u = ZeroField(), v = ZeroField(), w = bgc.sinking_velocities.POC) -biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::large_particle_components) = +biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::LARGE_PARTICLE_COMPONENTS) = (u = ZeroField(), v = ZeroField(), w = bgc.sinking_velocities.GOC) @inline function aggregation(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields, auxiliary_fields) diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl index ca8cab0f3..6b0c33109 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl @@ -56,7 +56,7 @@ end # iron limitation # Flynn and Hipkin (1999) - photosphotosyntheis, respiration (?), nitrate reduction - θₘ = 0.0016 / 55.85 * 12 * θChl + 1.5 * 1.21e-5 * 14 / (55.85 * 7.625) * LN + 1.15e-4 * 14 / (55.85 * 7.625) * LNO₃ + θₘ = 10^3 * (0.0016 / 55.85 * 12 * θChl + 1.5 * 1.21e-5 * 14 / (55.85 * 7.625) * LN + 1.15e-4 * 14 / (55.85 * 7.625) * LNO₃) # 1 / 1 to 10^-3 / 1 LFe = min(1, max(0, (θFe - θₘ) / θₒ)) diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl index d3917eefb..c2bec7b9e 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl @@ -29,7 +29,7 @@ end @inline iron_ratio(::Val{:P}, i, j, k, bgc, fields) = @inbounds fields.PFe[i, j, k] / (fields.P[i, j, k] + eps(0.0)) @inline iron_ratio(::Val{:D}, i, j, k, bgc, fields) = @inbounds fields.DFe[i, j, k] / (fields.D[i, j, k] + eps(0.0)) -@inline iron_ratio(::Val{:Z}, i, j, k, bgc, fields) = @inbounds fields.Z[i, j, k] * bgc.zooplankton.micro.iron_ratio +@inline iron_ratio(::Val{:Z}, i, j, k, bgc, fields) = @inbounds bgc.zooplankton.micro.iron_ratio @inline iron_ratio(::Val{:POC}, i, j, k, bgc, fields) = @inbounds fields.SFe[i, j, k] / (fields.POC[i, j, k] + eps(0.0)) @inline grazing_preference(val_prey_name, preferences) = 0 diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl index 8db87a9ee..e7f08bfd8 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/grazing_waste.jl @@ -49,16 +49,17 @@ end gI, growth_efficiency = grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) gfI = flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - total_carbon = gI + gfI + zoo_assimilated_iron = θ * growth_efficiency * (gI + gfI) - total_iron = (iron_grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - + iron_flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields)) + gIFe = iron_grazing(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - grazing_iron_ratio = (1 - σ) * total_iron / (total_carbon + eps(0.0)) + gfIFe = iron_flux_feeding(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - non_assimilated_iron_ratio = max(0, grazing_iron_ratio - growth_efficiency * θ) + lost_to_particles = σ * (gIFe + gfIFe) - return σ * gI + total_iron_grazed = gIFe + gfIFe + + return total_iron_grazed - lost_to_particles - zoo_assimilated_iron # feels like a more straight forward way to write it end @inline function calcite_loss(zoo::QualityDependantZooplankton, val_name, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl index bdf0d94f8..225782e5b 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl @@ -29,13 +29,13 @@ iron_ratios = extract_iron_availability(i, j, k, bgc, fields, food) - total_specific_iron_grazing = sum(ntuple(n->max(zero(grid), (food_availability[n] - J)) * p[n] * iron_ratios[n], Val(N))) * total_specific_grazing / available_total_food + total_specific_iron_grazing = sum(ntuple(n->max(zero(grid), (food_availability[n] - J)) * p[n] * iron_ratios[n], Val(N))) * total_specific_grazing / (available_total_food + eps(0.0)) - return total_specific_grazing * I + return total_specific_iron_grazing * I end @inline function iron_flux_feeding(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - g₀ = zoo.maximum_flux_feeding_rate + g₀ = zoo.maximum_flux_feeding_rate b = zoo.temperature_sensetivity I = zooplankton_concentration(val_name, i, j, k, fields) diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl index 88c6b43e9..8a9d0993d 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/micro_and_meso.jl @@ -73,6 +73,9 @@ end @inline upper_trophic_respiration(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = upper_trophic_respiration(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) +@inline upper_trophic_dissolved_iron(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + upper_trophic_dissolved_iron(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) + @inline upper_trophic_fecal_production(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = upper_trophic_fecal_production(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) @@ -131,6 +134,3 @@ end @inline calcite_loss(zoo::MicroAndMeso, val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = (calcite_loss(zoo.micro, Val(:Z), val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + calcite_loss(zoo.meso, Val(:M), val_prey_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields)) - -@inline upper_trophic_iron_waste(zoo::MicroAndMeso, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = - upper_trophic_respiration(zoo.meso, Val(:M), i, j, k, grid, bgc, clock, fields, auxiliary_fields) * zoo.meso.iron_ratio \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl index ed67900e7..baa3c4898 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/mortality_waste.jl @@ -15,6 +15,9 @@ end return γ * R end +@inline upper_trophic_dissolved_iron(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = + zoo.iron_ratio * upper_trophic_respiration_product(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + @inline upper_trophic_respiration_product(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = (1 - zoo.minimum_growth_efficiency - zoo.non_assililated_fraction) * upper_trophic_waste(zoo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) diff --git a/test/dependencies_for_runtests.jl b/test/dependencies_for_runtests.jl index 6b6250ad8..56818e8c5 100644 --- a/test/dependencies_for_runtests.jl +++ b/test/dependencies_for_runtests.jl @@ -1,3 +1,3 @@ -using OceanBioME, Test, CUDA, Oceananigans, JLD2, Oceananigans.Units, Documenter +using OceanBioME, Test, CUDA, Oceananigans, JLD2, Oceananigans.Units#, Documenter architecture = CUDA.has_cuda() ? GPU() : CPU() \ No newline at end of file diff --git a/test/test_PISCES.jl b/test/test_PISCES.jl index 6dc01c288..8a199b152 100644 --- a/test/test_PISCES.jl +++ b/test/test_PISCES.jl @@ -104,7 +104,7 @@ function test_PISCES_update_state(arch) light_attenuation, mixed_layer_depth) - closure = ScalarDiffusivity(ν = 1e-5, κ = FunctionField{Center, Center, Center}(κ_func, grid)) + closure = ScalarDiffusivity(ν = 1e-2, κ = FunctionField{Center, Center, Center}((z) -> ifelse(z > -25, 2, 1), grid)) model = NonhydrostaticModel(; grid, biogeochemistry, closure) # this updates the biogeochemical state From b71fd026c998f644159e08863a479438b5be1109 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Tue, 24 Sep 2024 23:06:19 +0100 Subject: [PATCH 05/33] had to reduce tollerances --- test/test_PISCES.jl | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/test_PISCES.jl b/test/test_PISCES.jl index 8a199b152..50acf29b6 100644 --- a/test/test_PISCES.jl +++ b/test/test_PISCES.jl @@ -25,6 +25,8 @@ end value(field; indices = (1, 1, 1)) = on_architecture(CPU(), interior(field, indices...))[1] function test_PISCES_conservation() # only on CPU please + @info "Testing PISCES element conservation (C, Fe, P, Si)" + validation_warning = "This implementation of PISCES is in early development and has not yet been validated" grid = BoxModelGrid(; z = -5) @@ -50,7 +52,7 @@ function test_PISCES_conservation() # only on CPU please mean_mixed_layer_light, mean_mixed_layer_vertical_diffusivity, # turn off permanent iron removal and nitrogen fixaiton - iron = SimpleIron(0), + iron = SimpleIron(excess_scavenging_enhancement = 0.0), nitrogen = NitrateAmmonia(maximum_fixation_rate = 0.0)) model = BoxModel(; grid, biogeochemistry) @@ -74,12 +76,12 @@ function test_PISCES_conservation() # only on CPU please total_phosphate_tendencies = sum([value(model.timestepper.Gⁿ[n]) * sf for (n, sf) in zip(conserved_tracers.phosphate.tracers, conserved_tracers.phosphate.scalefactors)]) total_nitrogen_tendencies = sum([value(model.timestepper.Gⁿ[n]) * sf for (n, sf) in zip(conserved_tracers.nitrogen.tracers, conserved_tracers.nitrogen.scalefactors)]) - # should these be exactly zero? - @test isapprox(total_carbon_tendencies, 0, atol = 10^-20) - @test isapprox(total_iron_tendencies, 0, atol = 10^-21) - @test isapprox(total_silicon_tendencies, 0, atol = 10^-21) - @test isapprox(total_phosphate_tendencies, 0, atol = 10^-21) - @test isapprox(total_nitrogen_tendencies, 0, atol = 10^-21) + # should these be exactly zero? - I would expect actual errors to be O(10^-9) to O(10^-6) from experiance + @test isapprox(total_carbon_tendencies, 0, atol = 2*10^-18) # I think this is okay ... it seems to reduce when I remove a load of 1/(x + eps(0.0)) but that would make it crash sometimes + @test isapprox(total_iron_tendencies, 0, atol = 10^-21) # this has lower tollerance because its about 100x smaller + @test isapprox(total_silicon_tendencies, 0, atol = 10^-21) # and this has lower tollerance because its simpler + @test isapprox(total_phosphate_tendencies, 0, atol = 2*10^-20) # I think ... + @test isapprox(total_nitrogen_tendencies, 0, atol = 2*10^-19) return nothing end @@ -89,6 +91,7 @@ end @inline κ_func(z) = ifelse(z > -25, 2, 1) function test_PISCES_update_state(arch) + @info "Testing PISCES auxiliary field computation and timestepping" # TODO: implement and test mixed layer depth computaiton elsewhere grid = RectilinearGrid(arch, topology = (Flat, Flat, Bounded), size = (10, ), extent = (100, )) @@ -119,6 +122,7 @@ function test_PISCES_update_state(arch) end function test_PISCES_negativity_protection(arch) + @info "Testing PISCES negativity protection" grid = RectilinearGrid(arch, topology = (Flat, Flat, Bounded), size = (10, ), extent = (100, )) PAR₁ = PAR₂ = PAR₃ = FunctionField{Center, Center, Center}(light, grid) @@ -160,12 +164,10 @@ end @testset "PISCES" begin if architecture isa CPU - @info "Testing PISCES element conservation (C, Fe, P, Si)" test_PISCES_conservation() #test_PISCES_box_model() #TODO end - @info "Testing PISCES auxiliary field computation and timestepping" test_PISCES_update_state(architecture) test_PISCES_negativity_protection(architecture) From b164835f58cd68041ec997916c6ae59735cdb5a1 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Tue, 24 Sep 2024 23:56:08 +0100 Subject: [PATCH 06/33] fixed the things --- .../AdvectedPopulations/PISCES/PISCES.jl | 7 +- .../PISCES/compute_calcite_saturation.jl | 35 ++++++ .../PISCES/mean_mixed_layer_properties.jl | 115 ++++++++++++++++++ .../two_size_class.jl | 5 +- .../PISCES/update_state.jl | 18 +++ 5 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 src/Models/AdvectedPopulations/PISCES/compute_calcite_saturation.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/mean_mixed_layer_properties.jl create mode 100644 src/Models/AdvectedPopulations/PISCES/update_state.jl diff --git a/src/Models/AdvectedPopulations/PISCES/PISCES.jl b/src/Models/AdvectedPopulations/PISCES/PISCES.jl index 6cb2ef014..12b9121f3 100644 --- a/src/Models/AdvectedPopulations/PISCES/PISCES.jl +++ b/src/Models/AdvectedPopulations/PISCES/PISCES.jl @@ -42,7 +42,6 @@ import OceanBioME: redfield, conserved_tracers, maximum_sinking_velocity, chloro import Oceananigans.Biogeochemistry: required_biogeochemical_tracers, required_biogeochemical_auxiliary_fields, - biogeochemical_drift_velocity, biogeochemical_auxiliary_fields, update_biogeochemical_state! @@ -117,9 +116,6 @@ end wPOC = bgc.sinking_velocities.POC, wGOC = bgc.sinking_velocities.GOC) -biogeochemical_drift_velocity(bgc::PISCES, val_name) = - biogeochemical_drift_velocity(bgc.particulate_organic_matter, val_name) - (bgc::PISCES)(i, j, k, grid, val_name, clock, fields, auxiliary_fields) = zero(grid) (bgc::DiscreteBiogeochemistry{<:PISCES})(i, j, k, grid, val_name, clock, fields) = @@ -127,6 +123,9 @@ biogeochemical_drift_velocity(bgc::PISCES, val_name) = include("common.jl") include("generic_functions.jl") +include("mean_mixed_layer_properties.jl") +include("compute_calcite_saturation.jl") +include("update_state.jl") include("zooplankton/zooplankton.jl") diff --git a/src/Models/AdvectedPopulations/PISCES/compute_calcite_saturation.jl b/src/Models/AdvectedPopulations/PISCES/compute_calcite_saturation.jl new file mode 100644 index 000000000..112e97b77 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/compute_calcite_saturation.jl @@ -0,0 +1,35 @@ +using Oceananigans.Architectures: architecture +using Oceananigans.BoundaryConditions: fill_halo_regions! +using Oceananigans.BuoyancyModels: g_Earth +using Oceananigans.Models: fields +using Oceananigans.Utils: launch! + +using OceanBioME.Models: CarbonChemistryModel + +function compute_calcite_saturation!(carbon_chemistry, calcite_saturation, model) + grid = model.grid + + arch = architecture(grid) + + launch!(arch, grid, :xyz, _compute_calcite_saturation!, carbon_chemistry, calcite_saturation, grid, fields(model)) + + fill_halo_regions!(calcite_saturation) + + return nothing +end + +@kernel function _compute_calcite_saturation!(carbon_chemistry, calcite_saturation, grid, model_fields) + i, j, k = @index(Global, NTuple) + + T = @inbounds model_fields.T[i, j, k] + S = @inbounds model_fields.S[i, j, k] + DIC = @inbounds model_fields.DIC[i, j, k] + Alk = @inbounds model_fields.Alk[i, j, k] + silicate = @inbounds model_fields.Si[i, j, k] # might get rid of this since it doesn't do anything + + z = znode(i, j, k, grid, Center(), Center(), Center()) + + P = abs(z) * g_Earth * 1026 / 100000 # very rough - don't think we should bother integrating the actual density + + @inbounds calcite_saturation[i, j, k] = CarbonChemistryModel.calcite_saturation(carbon_chemistry; DIC, T, S, Alk, P, silicate) +end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/mean_mixed_layer_properties.jl b/src/Models/AdvectedPopulations/PISCES/mean_mixed_layer_properties.jl new file mode 100644 index 000000000..cd034666a --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/mean_mixed_layer_properties.jl @@ -0,0 +1,115 @@ +using Oceananigans.Architectures: architecture +using Oceananigans.AbstractOperations: AbstractOperation +using Oceananigans.BoundaryConditions: fill_halo_regions! +using Oceananigans.Utils: launch! + +##### +##### generic integration +##### + +function compute_mixed_layer_mean!(Cₘₓₗ, mixed_layer_depth, C, grid) + arch = architecture(grid) + + launch!(arch, grid, :xy, _compute_mixed_layer_mean!, Cₘₓₗ, mixed_layer_depth, C, grid) + + fill_halo_regions!(Cₘₓₗ) + + return nothing +end + +compute_mixed_layer_mean!(Cₘₓₗ::AbstractOperation, mixed_layer_depth, C, grid) = nothing +compute_mixed_layer_mean!(Cₘₓₗ::ConstantField, mixed_layer_depth, C, grid) = nothing +compute_mixed_layer_mean!(Cₘₓₗ::ZeroField, mixed_layer_depth, C, grid) = nothing +compute_mixed_layer_mean!(Cₘₓₗ::Nothing, mixed_layer_depth, C, grid) = nothing + +@kernel function _compute_mixed_layer_mean!(Cₘₓₗ, mixed_layer_depth, C, grid) + i, j = @index(Global, NTuple) + + zₘₓₗ = @inbounds mixed_layer_depth[i, j, 1] + + @inbounds Cₘₓₗ[i, j, 1] = 0 + + integration_depth = 0 + + for k in grid.Nz:-1:1 + zₖ = znode(i, j, k, grid, Center(), Center(), Face()) + zₖ₊₁ = znode(i, j, k + 1, grid, Center(), Center(), Face()) + + Δzₖ = zₖ₊₁ - zₖ + Δzₖ₊₁ = ifelse(zₖ₊₁ > zₘₓₗ, zₖ₊₁ - zₘₓₗ, 0) + + Δz = ifelse(zₖ >= zₘₓₗ, Δzₖ, Δzₖ₊₁) + + Cₘₓₗ[i, j, 1] += C[i, j, k] * Δz + + integration_depth += Δz + end + + Cₘₓₗ[i, j, 1] /= integration_depth +end + +##### +##### Mean mixed layer diffusivity +##### + + +compute_mean_mixed_layer_vertical_diffusivity!(κ, mixed_layer_depth, model) = + compute_mean_mixed_layer_vertical_diffusivity!(model.closure, κ, mixed_layer_depth, model.diffusivity_fields, model.grid) + +# need these to catch when model doesn't have closure (i.e. box model) +compute_mean_mixed_layer_vertical_diffusivity!(κ::ConstantField, mixed_layer_depth, model) = nothing +compute_mean_mixed_layer_vertical_diffusivity!(κ::ZeroField, mixed_layer_depth, model) = nothing +compute_mean_mixed_layer_vertical_diffusivity!(κ::Nothing, mixed_layer_depth, model) = nothing + +# if no closure is defined we just assume its pre-set +compute_mean_mixed_layer_vertical_diffusivity!(closure::Nothing, + mean_mixed_layer_vertical_diffusivity, + mixed_layer_depth, + diffusivity_fields, grid) = nothing + +function compute_mean_mixed_layer_vertical_diffusivity!(closure, mean_mixed_layer_vertical_diffusivity, mixed_layer_depth, diffusivity_fields, grid) + # this is going to get messy + κ = phytoplankton_diffusivity(closure, diffusivity_fields) + + compute_mixed_layer_mean!(mean_mixed_layer_vertical_diffusivity, mixed_layer_depth, κ, grid) + + return nothing +end + +##### +##### Mean mixed layer light +##### + +compute_mean_mixed_layer_light!(mean_PAR, mixed_layer_depth, PAR, model) = + compute_mixed_layer_mean!(mean_PAR, mixed_layer_depth, PAR, model.grid) + +##### +##### Informaiton about diffusivity fields +##### + +# this does not belong here - lets add them when a particular closure is needed +using Oceananigans.TurbulenceClosures: ScalarDiffusivity, ScalarBiharmonicDiffusivity, VerticalFormulation, ThreeDimensionalFormulation, formulation + +phytoplankton_diffusivity(closure, diffusivity_fields) = + phytoplankton_diffusivity(formulation(closure), closure, diffusivity_fields) + +phytoplankton_diffusivity(closure::Tuple, diffusivity_fields) = + sum(map(n -> phytoplankton_diffusivity(closure[n], diffusivity_fields[n]), 1:length(closure))) + +phytoplankton_diffusivity(formulation, closure, diffusivit_fields) = ZeroField() + +const NotHorizontalFormulation = Union{VerticalFormulation, ThreeDimensionalFormulation} + +phytoplankton_diffusivity(::NotHorizontalFormulation, closure, diffusivity_fields) = + throw(ErrorException("Mean mixed layer vertical diffusivity can not be calculated for $(closure)")) + +phytoplankton_diffusivity(::NotHorizontalFormulation, + closure::Union{ScalarDiffusivity, ScalarBiharmonicDiffusivity}, + diffusivity_fields) = + phytoplankton_diffusivity(closure.κ) + +phytoplankton_diffusivity(diffusivity_field) = diffusivity_field +phytoplankton_diffusivity(diffusivity_field::Number) = ConstantField(diffusivity_field) +phytoplankton_diffusivity(diffusivity_fields::NamedTuple) = phytoplankton_diffusivity(diffusivity_fields.P) +phytoplankton_diffusivity(::Function) = + throw(ErrorException("Can not compute mean mixed layer vertical diffusivity for `Function` type diffusivity, changing to a `FunctionField` would work")) diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl index 67fd01ba0..2822fc4c3 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl @@ -1,3 +1,4 @@ +using Oceananigans.Fields: ZeroField using Oceananigans.Grids: znode, Center using Oceananigans.Operators: ℑzᵃᵃᶜ @@ -53,10 +54,10 @@ required_biogeochemical_tracers(::TwoCompartementCarbonIronParticles) = (:POC, : const SMALL_PARTICLE_COMPONENTS = Union{Val{:POC}, Val{:SFe}} const LARGE_PARTICLE_COMPONENTS = Union{Val{:GOC}, Val{:BFe}, Val{:PSi}, Val{:CaCO₃}} -biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::SMALL_PARTICLE_COMPONENTS) = +biogeochemical_drift_velocity(bgc::TwoCompartementPOCPISCES, ::SMALL_PARTICLE_COMPONENTS) = (u = ZeroField(), v = ZeroField(), w = bgc.sinking_velocities.POC) -biogeochemical_drift_velocity(bgc::TwoCompartementCarbonIronParticles, ::LARGE_PARTICLE_COMPONENTS) = +biogeochemical_drift_velocity(bgc::TwoCompartementPOCPISCES, ::LARGE_PARTICLE_COMPONENTS) = (u = ZeroField(), v = ZeroField(), w = bgc.sinking_velocities.GOC) @inline function aggregation(poc::TwoCompartementCarbonIronParticles, i, j, k, grid, bgc, clock, fields, auxiliary_fields) diff --git a/src/Models/AdvectedPopulations/PISCES/update_state.jl b/src/Models/AdvectedPopulations/PISCES/update_state.jl new file mode 100644 index 000000000..f01a44f92 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/update_state.jl @@ -0,0 +1,18 @@ +function update_biogeochemical_state!(model, bgc::PISCES) + # this should come from utils + #update_mixed_layer_depth!(bgc, model) + + PAR = biogeochemical_auxiliary_fields(model.biogeochemistry.light_attenuation).PAR + + compute_euphotic_depth!(bgc.euphotic_depth, PAR) + + compute_mean_mixed_layer_vertical_diffusivity!(bgc.mean_mixed_layer_vertical_diffusivity, bgc.mixed_layer_depth, model) + + compute_mean_mixed_layer_light!(bgc.mean_mixed_layer_light, bgc.mixed_layer_depth, PAR, model) + + compute_calcite_saturation!(bgc.carbon_chemistry, bgc.calcite_saturation, model) + + #update_silicate_climatology!(bgc, model) + + return nothing +end \ No newline at end of file From bbf64b2e93d255e860c14419ff6e4f0e937a0b5f Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 00:13:54 +0100 Subject: [PATCH 07/33] oops --- src/Particles/Particles.jl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Particles/Particles.jl b/src/Particles/Particles.jl index 22a684417..66c7117ab 100644 --- a/src/Particles/Particles.jl +++ b/src/Particles/Particles.jl @@ -16,14 +16,11 @@ const BGC_WITH_PARTICLES = Union{<:DiscreteBiogeochemistry{<:Any, <:Any, <:Any, <:ContinuousBiogeochemistry{<:Any, <:Any, <:Any, <:BiogeochemicalParticles}} @inline step_lagrangian_particles!(::Nothing, - model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, - BGC_WITH_PARTICLES}, + model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:BGC_WITH_PARTICLES}, Δt) = update_lagrangian_particle_properties!(model, model.biogeochemistry, Δt) @inline step_lagrangian_particles!(::Nothing, - model::HydrostaticFreeSurfaceModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, - BGC_WITH_PARTICLES, - <:Any, <:Any, <:Any, <:Any, <:Any,}, + model::HydrostaticFreeSurfaceModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:BGC_WITH_PARTICLES}, Δt) = update_lagrangian_particle_properties!(model, model.biogeochemistry, Δt) @inline update_lagrangian_particle_properties!(model, bgc::BGC_WITH_PARTICLES, Δt) = From e0a2d73fe33e420167c20edcc310a95bea584ba7 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 00:26:48 +0100 Subject: [PATCH 08/33] oops pt 2 --- src/Models/Sediments/coupled_timesteppers.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Models/Sediments/coupled_timesteppers.jl b/src/Models/Sediments/coupled_timesteppers.jl index 32be917bb..45941974d 100644 --- a/src/Models/Sediments/coupled_timesteppers.jl +++ b/src/Models/Sediments/coupled_timesteppers.jl @@ -8,11 +8,11 @@ using Oceananigans.Architectures: AbstractArchitecture import Oceananigans.TimeSteppers: ab2_step!, rk3_substep! -const BGC_WITH_FLAT_SEDIMENT = Union{<:DiscreteBiogeochemistry{<:Any, <:Any, <:FlatSediment}, +const <:BGC_WITH_FLAT_SEDIMENT = Union{<:DiscreteBiogeochemistry{<:Any, <:Any, <:FlatSediment}, <:ContinuousBiogeochemistry{<:Any, <:Any, <:FlatSediment}} # This is definitly type piracy -@inline function ab2_step!(model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, BGC_WITH_FLAT_SEDIMENT}, Δt) +@inline function ab2_step!(model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:BGC_WITH_FLAT_SEDIMENT}, Δt) workgroup, worksize = work_layout(model.grid, :xyz) arch = model.architecture step_field_kernel! = ab2_step_field!(device(arch), workgroup, worksize) @@ -50,7 +50,7 @@ const BGC_WITH_FLAT_SEDIMENT = Union{<:DiscreteBiogeochemistry{<:Any, <:Any, <:F return nothing end -@inline function ab2_step!(model::HydrostaticFreeSurfaceModel{<:Any, <:Any, <:AbstractArchitecture, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, BGC_WITH_FLAT_SEDIMENT}, Δt) +@inline function ab2_step!(model::HydrostaticFreeSurfaceModel{<:Any, <:Any, <:AbstractArchitecture, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:BGC_WITH_FLAT_SEDIMENT}, Δt) χ = model.timestepper.χ # Step locally velocity and tracers @@ -82,7 +82,7 @@ end @inbounds u[i, j, 1] += Δt * ((one_point_five + χ) * Gⁿ[i, j, 1] - (oh_point_five + χ) * G⁻[i, j, 1]) end -function rk3_substep!(model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, BGC_WITH_FLAT_SEDIMENT}, Δt, γⁿ, ζⁿ) +function rk3_substep!(model::NonhydrostaticModel{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:BGC_WITH_FLAT_SEDIMENT}, Δt, γⁿ, ζⁿ) workgroup, worksize = work_layout(model.grid, :xyz) arch = model.architecture substep_field_kernel! = rk3_substep_field!(device(arch), workgroup, worksize) From c6b2a6e897814aa98cd0d62c7dfafe2dc742806c Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 00:29:34 +0100 Subject: [PATCH 09/33] oops pt 3 --- src/Models/Sediments/coupled_timesteppers.jl | 2 +- src/Particles/tracer_tendencies.jl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Models/Sediments/coupled_timesteppers.jl b/src/Models/Sediments/coupled_timesteppers.jl index 45941974d..8cfa0d9d4 100644 --- a/src/Models/Sediments/coupled_timesteppers.jl +++ b/src/Models/Sediments/coupled_timesteppers.jl @@ -8,7 +8,7 @@ using Oceananigans.Architectures: AbstractArchitecture import Oceananigans.TimeSteppers: ab2_step!, rk3_substep! -const <:BGC_WITH_FLAT_SEDIMENT = Union{<:DiscreteBiogeochemistry{<:Any, <:Any, <:FlatSediment}, +const BGC_WITH_FLAT_SEDIMENT = Union{<:DiscreteBiogeochemistry{<:Any, <:Any, <:FlatSediment}, <:ContinuousBiogeochemistry{<:Any, <:Any, <:FlatSediment}} # This is definitly type piracy diff --git a/src/Particles/tracer_tendencies.jl b/src/Particles/tracer_tendencies.jl index 8aeaa4553..39632d1f6 100644 --- a/src/Particles/tracer_tendencies.jl +++ b/src/Particles/tracer_tendencies.jl @@ -2,3 +2,5 @@ using Oceananigans.Grids: AbstractGrid, Bounded, Periodic @inline get_node(::Bounded, i, N) = min(max(i, 1), N) @inline get_node(::Periodic, i, N) = ifelse(i < 1, N, ifelse(i > N, 1, i)) + +# TODO: update this with giant kelp stuff \ No newline at end of file From 7b0f75edd261643c611a510dbcfb8e643da94631 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 10:32:34 +0100 Subject: [PATCH 10/33] oops pt 4 --- test/dependencies_for_runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dependencies_for_runtests.jl b/test/dependencies_for_runtests.jl index 56818e8c5..6b6250ad8 100644 --- a/test/dependencies_for_runtests.jl +++ b/test/dependencies_for_runtests.jl @@ -1,3 +1,3 @@ -using OceanBioME, Test, CUDA, Oceananigans, JLD2, Oceananigans.Units#, Documenter +using OceanBioME, Test, CUDA, Oceananigans, JLD2, Oceananigans.Units, Documenter architecture = CUDA.has_cuda() ? GPU() : CPU() \ No newline at end of file From 992229c62af65d1ee6cacad067295640a21efd9d Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 15:35:03 +0100 Subject: [PATCH 11/33] fixed a bunch of things and conservations actually correct now --- .../AdvectedPopulations/PISCES/common.jl | 2 +- .../particulate_organic_matter/carbon.jl | 5 +++- .../nano_diatom_coupling.jl | 2 +- .../PISCES/phytoplankton/mixed_mondo.jl | 13 ++++------- .../phytoplankton/mixed_mondo_nano_diatoms.jl | 2 +- .../PISCES/phytoplankton/nano_and_diatoms.jl | 3 +-- .../phytoplankton/nutrient_limitation.jl | 2 +- .../AdvectedPopulations/PISCES/silicate.jl | 2 +- test/test_PISCES.jl | 23 +++++++++++-------- 9 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/Models/AdvectedPopulations/PISCES/common.jl b/src/Models/AdvectedPopulations/PISCES/common.jl index 25d92682b..6353fb321 100644 --- a/src/Models/AdvectedPopulations/PISCES/common.jl +++ b/src/Models/AdvectedPopulations/PISCES/common.jl @@ -41,7 +41,7 @@ Returns the length of day in seconds at the latitude `φ`, `t`seconds after the # as per Forsythe et al., 1995 (https://doi.org/10.1016/0304-3800(94)00034-F) p = asind(0.39795 * cos(0.2163108 + 2 * atan(0.9671396 * tan(0.00860 * (floor(Int, t / day) - 186))))) - return (24 - 24 / 180 * acosd((sind(0.8333) + sind(φ) * sind(p)) / (cosd(φ) * cosd(p)))) * day + return (24 - 24 / 180 * acosd(max(-1, min(1, (sind(0.8333) + sind(φ) * sind(p)) / (cosd(φ) * cosd(p)))))) * hour end """ diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl index 6d3b327df..fec438466 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/carbon.jl @@ -37,12 +37,15 @@ end upper_trophic_feces = upper_trophic_fecal_production(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + _, _, dissolved_aggregation = aggregation(bgc.dissolved_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + # losses grazing = total_grazing(bgc.zooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) large_breakdown = degredation(bgc.particulate_organic_matter, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + upper_trophic_feces + aggregation_to_large + return (grazing_waste + phytoplankton_mortality + zooplankton_mortality + upper_trophic_feces + + aggregation_to_large + dissolved_aggregation - grazing - large_breakdown) end diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl index 446615c21..9bec8c5d2 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/nano_diatom_coupling.jl @@ -67,7 +67,7 @@ end return min(LN, L_Fe, L_PO₄) end -@inline coccolithophore_phytoplankton_factor(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = +@inline coccolithophore_phytoplankton_factor(::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = @inbounds max(one(grid), fields.P[i, j, k] / 2) @inline function particulate_silicate_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl index 96c67d05e..56807ea6c 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo.jl @@ -45,10 +45,10 @@ and `newprod` versions of PISCES. enhanced_silicate_half_saturation :: FT = 20.9 # mmol Si / m³ optimal_silicate_ratio :: FT = 0.159 # mmol Si / mmol C - half_saturation_for_iron_uptake :: FT # μmol Fe / m³ + half_saturation_for_iron_uptake :: FT # μmol Fe / m³ - threshold_for_size_dependency :: FT = 1.0 # mmol C / m³ - size_ratio :: FT = 3.0 # + threshold_for_size_dependency :: FT = 1.0 # mmol C / m³ + size_ratio :: FT = 3.0 # end required_biogeochemical_tracers(phyto::MixedMondo, base) = @@ -60,8 +60,6 @@ required_biogeochemical_tracers(phyto::MixedMondo, base) = ##### @inline function carbon_growth(phyto::MixedMondo, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) - I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) - # production δ = phyto.exudated_fracton @@ -154,7 +152,7 @@ end L₂ = 4 - 4.5 * LFe / (LFe + 1) # typo in Aumount 2015 - return θFeₘ * L₁ * L₂ * max(0, (1 - θFe / θFeₘ) / (1.05 - θFe / θFeₘ)) * μᵢ * I + return (1 - δ) * θFeₘ * L₁ * L₂ * max(0, (1 - θFe / θFeₘ) / (1.05 - θFe / θFeₘ)) * μᵢ * I end @inline function iron_uptake_limitation(phyto, I, Fe) @@ -183,7 +181,7 @@ end I, IChl, IFe = phytoplankton_concentrations(val_name, i, j, k, fields) - T = @inbounds fields.T[i, j, k] + T = @inbounds fields.T[i, j, k] Si = @inbounds fields.Si[i, j, k] L, LFe, LPO₄, LN = phyto.nutrient_limitation(val_name, i, j, k, grid, bgc, phyto, clock, fields, auxiliary_fields) @@ -205,7 +203,6 @@ end θ₁ = θ₀ * L₁ * min(5.4, (4.4 * exp(-4.23 * F₁) * F₂ + 1) * (1 + 2 * L₂)) - # δ * ... is immediatly returned to Fe pool return (1 - δ) * θ₁ * μ * I end diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl index 74a146ee1..833160801 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/mixed_mondo_nano_diatoms.jl @@ -94,7 +94,7 @@ end @inline function (bgc::PISCES{<:NanoAndDiatoms})(i, j, k, grid, val_name::Val{:DSi}, clock, fields, auxiliary_fields) phyto = parameterisation(val_name, bgc.phytoplankton) - growth = chlorophyll_growth(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) + growth = silicate_growth(phyto, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) D = @inbounds fields.D[i, j, k] DSi = @inbounds fields.DSi[i, j, k] diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl index 90da975dd..a0dacb2ee 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nano_and_diatoms.jl @@ -25,8 +25,7 @@ end @inbounds base_production_rate(phyto.nano.growth_rate, fields.T[i, j, k]) @inline silicate_uptake(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = - (silicate_uptake(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - + silicate_uptake(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields)) + silicate_uptake(phyto.diatoms, Val(:D), i, j, k, grid, bgc, clock, fields, auxiliary_fields) @inline total_production(phyto::NanoAndDiatoms, i, j, k, grid, bgc, clock, fields, auxiliary_fields) = (total_production(phyto.nano, Val(:P), i, j, k, grid, bgc, clock, fields, auxiliary_fields) diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl index 6b0c33109..7fd4c7eaa 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl @@ -56,7 +56,7 @@ end # iron limitation # Flynn and Hipkin (1999) - photosphotosyntheis, respiration (?), nitrate reduction - θₘ = 10^3 * (0.0016 / 55.85 * 12 * θChl + 1.5 * 1.21e-5 * 14 / (55.85 * 7.625) * LN + 1.15e-4 * 14 / (55.85 * 7.625) * LNO₃) # 1 / 1 to 10^-3 / 1 + θₘ = 10^3 * (0.0016 / 55.85 * 12 * θChl + 1.5 * 1.21e-5 * 14 / (55.85 * 7.625) * LN + 1.15e-4 * 14 / (55.85 * 7.625) * LNO₃) # 1 / 1 to 1/10^3 / 1 LFe = min(1, max(0, (θFe - θₘ) / θₒ)) diff --git a/src/Models/AdvectedPopulations/PISCES/silicate.jl b/src/Models/AdvectedPopulations/PISCES/silicate.jl index 251a49d5d..2e5382298 100644 --- a/src/Models/AdvectedPopulations/PISCES/silicate.jl +++ b/src/Models/AdvectedPopulations/PISCES/silicate.jl @@ -17,7 +17,7 @@ required_biogeochemical_tracers(::Silicate) = tuple(:Si) const PISCESSilicate = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Silicate} -@inline function (bgc::PISCESSilicate)(i, j, k, grid, val_name::Val{:Si}, clock, fields, auxiliary_fields) +@inline function (bgc::PISCESSilicate)(i, j, k, grid, ::Val{:Si}, clock, fields, auxiliary_fields) consumption = silicate_uptake(bgc.phytoplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) dissolution = particulate_silicate_dissolution(bgc.particulate_organic_matter, i, j, k, grid, bgc, clock, fields, auxiliary_fields) diff --git a/test/test_PISCES.jl b/test/test_PISCES.jl index 50acf29b6..82e080220 100644 --- a/test/test_PISCES.jl +++ b/test/test_PISCES.jl @@ -5,10 +5,15 @@ using Oceananigans.Fields: ConstantField, FunctionField using OceanBioME.Models.PISCESModel: SimpleIron, NitrateAmmonia -const PISCES_INITIAL_VALUES = (P = 7, D = 7, Z = 0.5, M = 0.5, PChl = 1.5, DChl = 1.5, PFe = 35e-3, DFe = 35e-3, DSi = 1, - NO₃ = 6, NH₄ = 1, PO₄ = 1, Fe = 1, Si = 7, CaCO₃ = 10^-3, - DIC = 2200, Alk = 2400, O₂ = 240, T = 10, S = 35) - +const PISCES_INITIAL_VALUES = (P = 0.5, PChl = 0.02, PFe = 0.005, + D = 0.1, DChl = 0.004, DFe = 0.001, DSi = 0.01, + Z = 0.1, M = 0.7, + DOC = 2.1, + POC = 7.8, SFe = 0.206, + GOC = 38, BFe = 1.1, PSi = 0.1, CaCO₃ = 10^-10, + NO₃ = 2.3, NH₄ = 0.9, PO₄ = 0.6, Fe = 0.13, Si = 8.5, + DIC = 2205, Alk = 2566, O₂ = 317, + T = 10, S = 35) function set_PISCES_initial_values!(tracers; values = PISCES_INITIAL_VALUES) for (name, field) in pairs(tracers) @@ -77,11 +82,11 @@ function test_PISCES_conservation() # only on CPU please total_nitrogen_tendencies = sum([value(model.timestepper.Gⁿ[n]) * sf for (n, sf) in zip(conserved_tracers.nitrogen.tracers, conserved_tracers.nitrogen.scalefactors)]) # should these be exactly zero? - I would expect actual errors to be O(10^-9) to O(10^-6) from experiance - @test isapprox(total_carbon_tendencies, 0, atol = 2*10^-18) # I think this is okay ... it seems to reduce when I remove a load of 1/(x + eps(0.0)) but that would make it crash sometimes - @test isapprox(total_iron_tendencies, 0, atol = 10^-21) # this has lower tollerance because its about 100x smaller - @test isapprox(total_silicon_tendencies, 0, atol = 10^-21) # and this has lower tollerance because its simpler - @test isapprox(total_phosphate_tendencies, 0, atol = 2*10^-20) # I think ... - @test isapprox(total_nitrogen_tendencies, 0, atol = 2*10^-19) + @test isapprox(total_carbon_tendencies, 0, atol = 10^-20) + @test isapprox(total_iron_tendencies, 0, atol = 10^-21) + @test isapprox(total_silicon_tendencies, 0, atol = 10^-30) + @test isapprox(total_phosphate_tendencies, 0, atol = 10^-22) + @test isapprox(total_nitrogen_tendencies, 0, atol = 10^-21) return nothing end From e5f8e304ce833175ebfd4a73f410572c4a018d84 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 16:02:19 +0100 Subject: [PATCH 12/33] made the box model seem to work again --- .../AdvectedPopulations/PISCES/PISCES.jl | 26 ++++++++++----- .../phytoplankton/nutrient_limitation.jl | 3 +- validation/PISCES/box.jl | 33 ++++++++----------- validation/PISCES/column.jl | 2 +- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/Models/AdvectedPopulations/PISCES/PISCES.jl b/src/Models/AdvectedPopulations/PISCES/PISCES.jl index 12b9121f3..660026036 100644 --- a/src/Models/AdvectedPopulations/PISCES/PISCES.jl +++ b/src/Models/AdvectedPopulations/PISCES/PISCES.jl @@ -171,7 +171,18 @@ include("coupling_utils.jl") """ PISCES(; grid, + phytoplankton = MixedMondoNanoAndDiatoms(), + zooplankton = MicroAndMesoZooplankton(), + dissolved_organic_matter = DissolvedOrganicCarbon(), + particulate_organic_matter = TwoCompartementCarbonIronParticles(), + nitrogen = NitrateAmmonia(), + iron = SimpleIron(), + silicate = Silicate(), + oxygen = Oxygen(), + phosphate = Phosphate(), + + inorganic_carbon = InorganicCarbon(), # from Aumount 2005 rather than 2015 since it doesn't work the other way around first_anoxia_thresehold = 6.0, @@ -208,7 +219,7 @@ include("coupling_utils.jl") GOC = Field(KernelFunctionOperation{Center, Center, Face}(DepthDependantSinkingSpeed(), grid, mixed_layer_depth, - euphotic_depth)), + euphotic_depth))), open_bottom = true, scale_negatives = false, @@ -225,19 +236,16 @@ Keyword Arguments ================= - `grid`: (required) the geometry to build the model on -- `nanophytoplankton`: nanophytoplankton (`P`, `PChl`, `PFe``) evolution parameterisation such as `MixedMondoPhytoplankton` -- `diatoms`: diatom (`D`, `DChl`, `DFe`, `DSi`) evolution parameterisation such as `MixedMondoPhytoplankton` -- `microzooplankton`: microzooplankton (`Z`) evolution parameterisation -- `mesozooplankton`: mesozooplankton (`M`) evolution parameterisation +- `phytoplankton`: phytoplankton evolution parameterisation, defaults to nanophyto and diatom size classes with `MixedMondo` growth +- `zooplankton`: zooplankton evolution parameterisation, defaults to two class `Z` and `M` - `dissolved_organic_matter`: parameterisaion for the evolution of dissolved organic matter (`DOC`) -- `particulate_organic_matter`: parameterisation for the evolution of particulate organic matter (`POC`, `GOC`, `SFe`, `BFe`, `PSi`) +- `particulate_organic_matter`: parameterisation for the evolution of particulate organic matter (`POC`, `GOC`, `SFe`, `BFe`, `PSi`, `CaCO₃`) - `nitrogen`: parameterisation for the nitrogen compartements (`NH₄` and `NO₃`) - `iron`: parameterisation for iron (`Fe`), currently the "complex chemistry" of Aumount 2015 is not implemented - `silicate`: parameterisaion for silicate (`Si`) - `oxygen`: parameterisaion for oxygen (`O₂`) - `phosphate`: parameterisaion for phosphate (`PO₄`) -- `calcite`: parameterisaion for calcite (`CaCO₃`) -- `carbon_system`: parameterisation for the evolution of the carbon system (`DIC` and `Alk`alinity) +- `inorganic_carbon`: parameterisation for the evolution of the inorganic carbon system (`DIC` and `Alk`alinity) - `first_anoxia_thresehold` and `second_anoxia_thresehold`: thresholds in anoxia parameterisation - `nitrogen_redfield_ratio` and `phosphate_redfield_ratio`: the assumed element ratios N/C and P/C - `mixed_layer_shear` and `background_shear`: the mixed layer and background shear rates, TODO: move this to a computed field @@ -331,7 +339,7 @@ function PISCES(; grid, particles = nothing, modifiers = nothing) - @warn "This implementation of PISCES is in early development and has not yet been validated" + @warn "This implementation of PISCES is in early development and has not yet been validated against the operational version" if !isnothing(sediment) && !open_bottom @warn "You have specified a sediment model but not `open_bottom` which will not work as the tracer will settle in the bottom cell" diff --git a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl index 7fd4c7eaa..92bc393ab 100644 --- a/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl +++ b/src/Models/AdvectedPopulations/PISCES/phytoplankton/nutrient_limitation.jl @@ -44,6 +44,7 @@ end Kₙₒ = kₙₒ * K̄ Kₙₕ = kₙₕ * K̄ Kₚ = kₚ * K̄ + Kₛᵢ = kₛᵢ * K̄ # nitrogen limitation LNO₃ = nitrogen_limitation(NO₃, NH₄, Kₙₒ, Kₙₕ) @@ -61,7 +62,7 @@ end LFe = min(1, max(0, (θFe - θₘ) / θₒ)) # silicate limitation - KSi = kₛᵢ + 7 * Si′^2 / (pk^2 + Si′^2) + KSi = Kₛᵢ + 7 * Si′^2 / (pk^2 + Si′^2) LSi = Si / (Si + KSi) LSi = ifelse(L.silicate_limited, LSi, Inf) diff --git a/validation/PISCES/box.jl b/validation/PISCES/box.jl index 777d9e630..e3f8df70b 100644 --- a/validation/PISCES/box.jl +++ b/validation/PISCES/box.jl @@ -51,13 +51,13 @@ biogeochemistry = PISCES(; grid, silicate_climatology, mean_mixed_layer_light, mean_mixed_layer_vertical_diffusivity, - iron = SimpleIron(0), + iron = SimpleIron(excess_scavenging_enhancement = 0.0), nitrogen = NitrateAmmonia(maximum_fixation_rate = 0.0)) # Set up the model. Here, first specify the biogeochemical model, followed by initial conditions and the start and end times model = BoxModel(; grid, biogeochemistry, clock, prescribed_tracers = (; T = temp)) -set!(model, P = 6.95, D = 6.95, Z = 0.695, M = 0.695, +#=set!(model, P = 6.95, D = 6.95, Z = 0.695, M = 0.695, PChl = 1.671, DChl = 1.671, PFe = 6.95/7, DFe = 6.95/7, DSi = 1.162767, @@ -65,10 +65,19 @@ set!(model, P = 6.95, D = 6.95, Z = 0.695, M = 0.695, PO₄ = 0.8722, Fe = 1.256, Si = 7.313, CaCO₃ = 100, DIC = 2139.0, Alk = 2366.0, - O₂ = 237.0, S = 35, T = 10) + O₂ = 237.0, S = 35, T = 10) =# + +set!(model, P = 0.5, PChl = 0.02, PFe = 0.005, + D = 0.5, DChl = 0.02, DFe = 0.005, DSi = 0.01, + Z = 0.1, M = 0.7, + DOC = 2.1, + POC = 7.8, SFe = 0.206, + GOC = 38, BFe = 1.1, PSi = 0.1, CaCO₃ = 10^-10, + NO₃ = 5.0, NH₄ = 0.9, PO₄ = 5.0, Fe = 0.13, Si = 8.5, + DIC = 2205, Alk = 2566, O₂ = 317) -simulation = Simulation(model; Δt = 20minutes, stop_time = 4years) +simulation = Simulation(model; Δt = 40minutes, stop_time = 4years) simulation.output_writers[:fields] = JLD2OutputWriter(model, model.fields; filename = "box.jld2", schedule = TimeInterval(0.5day), overwrite_existing = true) @@ -78,21 +87,7 @@ simulation.output_writers[:par] = JLD2OutputWriter(model, (; PAR = PAR_field); f prog(sim) = @info "$(prettytime(time(sim))) in $(prettytime(simulation.run_wall_time))" -#NaN checker function, could be removed if confident of model stability -function non_zero_fields!(model) - @inbounds for (idx, field) in enumerate(model.fields) - if isnan(field[1,1,1]) - throw("$(keys(model.fields)[idx]) has gone NaN") - else - field[1, 1, 1] = max(0, field[1, 1, 1]) - end - - end - return nothing -end - simulation.callbacks[:progress] = Callback(prog, TimeInterval(182days)) -#simulation.callbacks[:non_zero_fields] = Callback(non_zero_fields!, callsite = UpdateStateCallsite()) @info "Running the model..." run!(simulation) @@ -110,7 +105,7 @@ fig = Figure(size = (2400, 3600), fontsize = 24) axs = [] -n_start = 731 +n_start = 1 for name in Oceananigans.Biogeochemistry.required_biogeochemical_tracers(biogeochemistry) idx = (length(axs)) diff --git a/validation/PISCES/column.jl b/validation/PISCES/column.jl index 283b045aa..11cd51920 100644 --- a/validation/PISCES/column.jl +++ b/validation/PISCES/column.jl @@ -38,7 +38,7 @@ nothing #hide @inline temp(z, t) = 2.4 * (1 + cos(t * 2π / year + 50days)) * ifelse(z > MLD(t), 1, exp((z - MLD(t))/20)) + 8 -grid = RectilinearGrid(GPU(), topology = (Flat, Flat, Bounded), size = (100, ), extent = (400, )) +grid = RectilinearGrid(topology = (Flat, Flat, Bounded), size = (100, ), extent = (400, )) clock = Clock(; time = 0.0) From efac74a5ae94a3856936c29c207922b7805845ae Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 17:13:49 +0100 Subject: [PATCH 13/33] fixed oxygen --- src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl | 2 +- src/Models/AdvectedPopulations/PISCES/oxygen.jl | 8 ++++---- src/Models/Models.jl | 2 +- src/OceanBioME.jl | 2 +- src/Particles/Particles.jl | 1 + 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl b/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl index 140c472fa..f783299b9 100644 --- a/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl +++ b/src/Models/AdvectedPopulations/PISCES/inorganic_carbon.jl @@ -29,7 +29,7 @@ required_biogeochemical_tracers(::InorganicCarbon) = (:DIC, :Alk) const PISCESCarbon = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:InorganicCarbon} -@inline function (bgc::PISCESCarbon)(i, j, k, grid, val_name::Val{:DIC}, clock, fields, auxiliary_fields) +@inline function (bgc::PISCESCarbon)(i, j, k, grid, ::Val{:DIC}, clock, fields, auxiliary_fields) zooplankton_respiration = inorganic_excretion(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) upper_trophic = upper_trophic_respiration(bgc.zooplankton, i, j, k, grid, bgc, clock, fields, auxiliary_fields) diff --git a/src/Models/AdvectedPopulations/PISCES/oxygen.jl b/src/Models/AdvectedPopulations/PISCES/oxygen.jl index 3756a9566..389951a50 100644 --- a/src/Models/AdvectedPopulations/PISCES/oxygen.jl +++ b/src/Models/AdvectedPopulations/PISCES/oxygen.jl @@ -19,7 +19,7 @@ using OceanBioME.Models.PISCESModel.Zooplankton: import Oceananigans.Biogeochemistry: required_biogeochemical_tracers @kwdef struct Oxygen{FT} - ratio_for_respiration :: FT = 133/122 # mol O₂ / mol C + ratio_for_respiration :: FT = 133/122 # mol O₂ / mol C ratio_for_nitrification :: FT = 32/122 # mol O₂ / mol C end @@ -27,7 +27,7 @@ required_biogeochemical_tracers(::Oxygen) = tuple(:O₂) const PISCESOxygen = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Oxygen} -@inline function (bgc::PISCESOxygen)(i, j, k, grid, val_name::Val{:O₂}, clock, fields, auxiliary_fields) +@inline function (bgc::PISCESOxygen)(i, j, k, grid, ::Val{:O₂}, clock, fields, auxiliary_fields) θ_resp = bgc.oxygen.ratio_for_respiration θ_nitrif = bgc.oxygen.ratio_for_nitrification @@ -41,13 +41,13 @@ const PISCESOxygen = PISCES{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:O ammonia_photosynthesis = θ_resp * uptake(bgc.phytoplankton, Val(:NH₄), i, j, k, grid, bgc, clock, fields, auxiliary_fields) nitrate_photosynthesis = (θ_resp + θ_nitrif) * uptake(bgc.phytoplankton, Val(:NO₃), i, j, k, grid, bgc, clock, fields, auxiliary_fields) - # I think (?) that we need the redfield raito here since θ_nitrif is per oxygen + # I think (?) that we need the redfield raito here since θ_nitrif is oxygen per carbon nitrif = θ_nitrif * nitrification(bgc.nitrogen, i, j, k, grid, bgc, clock, fields, auxiliary_fields) / bgc.nitrogen_redfield_ratio fixation = θ_nitrif * nitrogen_fixation(bgc.nitrogen, i, j, k, grid, bgc, clock, fields, auxiliary_fields) / bgc.nitrogen_redfield_ratio return (ammonia_photosynthesis + nitrate_photosynthesis + fixation - - zooplankton - upper_trophic - nitrif) + - remineralisation - zooplankton - upper_trophic - nitrif) end end # module \ No newline at end of file diff --git a/src/Models/Models.jl b/src/Models/Models.jl index 81c5f7cf5..a7b256509 100644 --- a/src/Models/Models.jl +++ b/src/Models/Models.jl @@ -5,7 +5,7 @@ export Sediments export NPZD, NutrientPhytoplanktonZooplanktonDetritus, LOBSTER, - PISCES, DepthDependantSinkingSpeed, PrescribedLatitude, ModelLatitude + PISCES, DepthDependantSinkingSpeed, PrescribedLatitude, ModelLatitude, PISCESModel export SLatissima diff --git a/src/OceanBioME.jl b/src/OceanBioME.jl index b211bf652..ba898722d 100644 --- a/src/OceanBioME.jl +++ b/src/OceanBioME.jl @@ -6,7 +6,7 @@ module OceanBioME # Biogeochemistry models and useful things export Biogeochemistry, LOBSTER, PISCES, NutrientPhytoplanktonZooplanktonDetritus, NPZD, redfield -export DepthDependantSinkingSpeed, PrescribedLatitude, ModelLatitude +export DepthDependantSinkingSpeed, PrescribedLatitude, ModelLatitude, PISCESModel # Macroalgae models export SLatissima diff --git a/src/Particles/Particles.jl b/src/Particles/Particles.jl index 66c7117ab..0b570dcdb 100644 --- a/src/Particles/Particles.jl +++ b/src/Particles/Particles.jl @@ -51,4 +51,5 @@ function fetch_output(particles::BiogeochemicalParticles, model) end include("tracer_tendencies.jl") + end #module From 09237272ab99d011905f471b292589111161833a Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 17:16:58 +0100 Subject: [PATCH 14/33] changed validation initial conditions --- validation/PISCES/box.jl | 26 ++++++++------------------ validation/PISCES/column.jl | 18 ++++++++---------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/validation/PISCES/box.jl b/validation/PISCES/box.jl index e3f8df70b..209ef6331 100644 --- a/validation/PISCES/box.jl +++ b/validation/PISCES/box.jl @@ -57,24 +57,14 @@ biogeochemistry = PISCES(; grid, # Set up the model. Here, first specify the biogeochemical model, followed by initial conditions and the start and end times model = BoxModel(; grid, biogeochemistry, clock, prescribed_tracers = (; T = temp)) -#=set!(model, P = 6.95, D = 6.95, Z = 0.695, M = 0.695, - PChl = 1.671, DChl = 1.671, - PFe = 6.95/7, DFe = 6.95/7, - DSi = 1.162767, - NO₃ = 6.202, NH₄ = 0.25*6.202, - PO₄ = 0.8722, Fe = 1.256, Si = 7.313, - CaCO₃ = 100, - DIC = 2139.0, Alk = 2366.0, - O₂ = 237.0, S = 35, T = 10) =# - -set!(model, P = 0.5, PChl = 0.02, PFe = 0.005, - D = 0.5, DChl = 0.02, DFe = 0.005, DSi = 0.01, - Z = 0.1, M = 0.7, - DOC = 2.1, - POC = 7.8, SFe = 0.206, - GOC = 38, BFe = 1.1, PSi = 0.1, CaCO₃ = 10^-10, - NO₃ = 5.0, NH₄ = 0.9, PO₄ = 5.0, Fe = 0.13, Si = 8.5, - DIC = 2205, Alk = 2566, O₂ = 317) +set!(model, P = 0.1, PChl = 0.025, PFe = 0.005, + D = 0.01, DChl = 0.003, DFe = 0.0006, DSi = 0.004, + Z = 0.06, M = 0.5, + DOC = 4, + POC = 5.4, SFe = 0.34, + GOC = 8.2, BFe = 0.5, PSi = 0.04, CaCO₃ = 10^-10, + NO₃ = 10, NH₄ = 0.1, PO₄ = 5.0, Fe = 0.6, Si = 8.6, + DIC = 2205, Alk = 2560, O₂ = 317, S = 35) simulation = Simulation(model; Δt = 40minutes, stop_time = 4years) diff --git a/validation/PISCES/column.jl b/validation/PISCES/column.jl index 11cd51920..345cfc9ee 100644 --- a/validation/PISCES/column.jl +++ b/validation/PISCES/column.jl @@ -76,16 +76,14 @@ model = HydrostaticFreeSurfaceModel(; grid, @info "Setting initial values..." - -set!(model, P = 6.95, D = 6.95, Z = 0.695, M = 0.695, - PChl = 0.35, DChl = 0.35, - PFe = 6.95*50e-3, DFe = 6.95*50e-3, - DSi = 0.159*6.95, - NO₃ = 6.202, NH₄ = 0.25*6.202, - PO₄ = 0.8722, Fe = 0.2, Si = 2, - CaCO₃ = 0.001, - DIC = 2139.0, Alk = 2366.0, - O₂ = 237.0, S = 35, T = 10) +set!(model, P = 0.1, PChl = 0.025, PFe = 0.005, + D = 0.01, DChl = 0.003, DFe = 0.0006, DSi = 0.004, + Z = 0.06, M = 0.5, + DOC = 4, + POC = 5.4, SFe = 0.34, + GOC = 8.2, BFe = 0.5, PSi = 0.04, CaCO₃ = 10^-10, + NO₃ = 10, NH₄ = 0.1, PO₄ = 5.0, Fe = 0.6, Si = 8.6, + DIC = 2205, Alk = 2560, O₂ = 317, S = 35) # maybe get to 1.5hours after initial stuff simulation = Simulation(model, Δt = 1.5hours, stop_time = 10years) From 5c025ad2218782694a92d126c8b64bd71a791ecb Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 17:42:38 +0100 Subject: [PATCH 15/33] hopeflly fixed for gpu --- src/Light/multi_band.jl | 26 +++++--- src/Light/prescribed.jl | 18 ++++-- .../AdvectedPopulations/PISCES/PISCES.jl | 2 + .../AdvectedPopulations/PISCES/adapts.jl | 62 +++++++++++++++++++ .../PISCES/zooplankton/defaults.jl | 5 ++ .../zooplankton/food_quality_dependant.jl | 5 +- 6 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 src/Models/AdvectedPopulations/PISCES/adapts.jl diff --git a/src/Light/multi_band.jl b/src/Light/multi_band.jl index d3cbebcda..495d1c60d 100644 --- a/src/Light/multi_band.jl +++ b/src/Light/multi_band.jl @@ -115,14 +115,10 @@ function numerical_mean(λ, C, idx1, idx2) return ∫Cdλ / ∫dλ end -function par_symbol(n) - subscripts = Symbol[] - for digit in reverse(digits(n)) - push!(subscripts, Symbol(Char('\xe2\x82\x80'+digit))) - end +par_symbol(n) = Symbol(:PAR, number_subscript(tuple(reverse(digits(n))...))...) - return Symbol(:PAR, subscripts...) -end +number_subscript(digits::NTuple{N}) where N = + ntuple(n->Symbol(Char('\xe2\x82\x80'+digits[n])), Val(N)) @kernel function update_MultiBandPhotosyntheticallyActiveRadiation!(grid, field, kʷ, e, χ, _surface_PAR, surface_PAR_division, @@ -146,7 +142,6 @@ end end end - function update_biogeochemical_state!(model, PAR::MultiBandPhotosyntheticallyActiveRadiation) grid = model.grid @@ -179,7 +174,18 @@ summary(par::MultiBandPhotosyntheticallyActiveRadiation) = show(io::IO, model::MultiBandPhotosyntheticallyActiveRadiation) = print(io, summary(model)) biogeochemical_auxiliary_fields(par::MultiBandPhotosyntheticallyActiveRadiation) = - merge((PAR = par.total, ), NamedTuple{par.field_names}(par.fields)) + merge((PAR = par.total, ), NamedTuple{field_names(par.field_names, par.fields)}(par.fields)) + +@inline field_names(field_names, fields) = field_names +@inline field_names(::Nothing, fields::NTuple{N}) where N = ntuple(n -> par_symbol(n), Val(N)) # avoid passing this into kernels -Adapt.adapt_structure(to, par::MultiBandPhotosyntheticallyActiveRadiation) = nothing +Adapt.adapt_structure(to, par::MultiBandPhotosyntheticallyActiveRadiation) = + MultiBandPhotosyntheticallyActiveRadiation(adapt(to, par.total), + adapt(to, par.fields), + nothing, + nothing, + nothing, + nothing, + nothing) + diff --git a/src/Light/prescribed.jl b/src/Light/prescribed.jl index b3949977e..99e1a3e93 100644 --- a/src/Light/prescribed.jl +++ b/src/Light/prescribed.jl @@ -1,3 +1,4 @@ +using Oceananigans.Architectures: architecture, GPU using Oceananigans.Fields: compute!, AbstractField function maybe_named_fields(field) @@ -22,7 +23,7 @@ one field is present the field will be named `PAR`. """ struct PrescribedPhotosyntheticallyActiveRadiation{F, FN} - fields :: F + fields :: F field_names :: FN PrescribedPhotosyntheticallyActiveRadiation(fields::F, names::FN) where {F, FN} = @@ -31,6 +32,11 @@ struct PrescribedPhotosyntheticallyActiveRadiation{F, FN} function PrescribedPhotosyntheticallyActiveRadiation(fields) names, values = maybe_named_fields(fields) + isa(architecture(values[1]), GPU) || + @warn "On `GPU` prescribed fields will be renamed to `PAR`, `PAR₁`, etc., as symbols can not be passed to the GPU. +Please make sure they are in this order. +(We're assuming that you're only using this for testing purposes.)" + F = typeof(values) FN = typeof(names) @@ -51,7 +57,11 @@ show(io::IO, model::PrescribedPhotosyntheticallyActiveRadiation{F}) where {F} = " Fields:", "\n", " └── $(model.field_names)") -biogeochemical_auxiliary_fields(par::PrescribedPhotosyntheticallyActiveRadiation) = NamedTuple{par.field_names}(par.fields) +biogeochemical_auxiliary_fields(par::PrescribedPhotosyntheticallyActiveRadiation) = + NamedTuple{prescribed_field_names(par.field_names, par.fields)}(par.fields) + +@inline prescribed_field_names(field_names, fields) = field_names +@inline prescribed_field_names(::Nothing, fields::NTuple{N}) where N = tuple(:PAR, ntuple(n -> par_symbol(n), Val(N-1))...) -adapt_structure(to, par::PrescribedPhotosyntheticallyActiveRadiation) = PrescribedPhotosyntheticallyActiveRadiation(adapt(to, par.fields), - adapt(to, par.field_names)) +adapt_structure(to, par::PrescribedPhotosyntheticallyActiveRadiation) = + PrescribedPhotosyntheticallyActiveRadiation(adapt(to, par.fields), nothing) diff --git a/src/Models/AdvectedPopulations/PISCES/PISCES.jl b/src/Models/AdvectedPopulations/PISCES/PISCES.jl index 660026036..8ae4f7b7b 100644 --- a/src/Models/AdvectedPopulations/PISCES/PISCES.jl +++ b/src/Models/AdvectedPopulations/PISCES/PISCES.jl @@ -169,6 +169,8 @@ using .InorganicCarbons include("coupling_utils.jl") +include("adapts.jl") + """ PISCES(; grid, phytoplankton = MixedMondoNanoAndDiatoms(), diff --git a/src/Models/AdvectedPopulations/PISCES/adapts.jl b/src/Models/AdvectedPopulations/PISCES/adapts.jl new file mode 100644 index 000000000..19a81a091 --- /dev/null +++ b/src/Models/AdvectedPopulations/PISCES/adapts.jl @@ -0,0 +1,62 @@ +using Adapt + +import Adapt: adapt_structure + +Adapt.adapt_structure(to, bgc::PISCES) = + PISCES(adapt(to, bgc.phytoplankton), + adapt(to, bgc.zooplankton), + adapt(to, bgc.dissolved_organic_matter), + adapt(to, bgc.particulate_organic_matter), + adapt(to, bgc.nitrogen), + adapt(to, bgc.iron), + adapt(to, bgc.silicate), + adapt(to, bgc.oxygen), + adapt(to, bgc.phosphate), + adapt(to, bgc.inorganic_carbon), + adapt(to, bgc.first_anoxia_threshold), + adapt(to, bgc.second_anoxia_threshold), + adapt(to, bgc.nitrogen_redfield_ratio), + adapt(to, bgc.phosphate_redfield_ratio), + adapt(to, bgc.mixed_layer_shear), + adapt(to, bgc.background_shear), + adapt(to, bgc.latitude), + adapt(to, bgc.day_length), + adapt(to, bgc.mixed_layer_depth), + adapt(to, bgc.euphotic_depth), + adapt(to, bgc.silicate_climatology), + adapt(to, bgc.mean_mixed_layer_vertical_diffusivity), + adapt(to, bgc.mean_mixed_layer_light), + adapt(to, bgc.carbon_chemistry), + adapt(to, bgc.calcite_saturation), + adapt(to, bgc.sinking_velocities)) + +Adapt.adapt_structure(to, zoo::MicroAndMeso) = + MicroAndMeso(adapt(to, zoo.micro), + adapt(to, zoo.meso), + + adapt(to, zoo.microzooplankton_bacteria_concentration), + adapt(to, zoo.mesozooplankton_bacteria_concentration), + adapt(to, zoo.maximum_bacteria_concentration), + adapt(to, zoo.bacteria_concentration_depth_exponent), + adapt(to, zoo.doc_half_saturation_for_bacterial_activity), + adapt(to, zoo.nitrate_half_saturation_for_bacterial_activity), + adapt(to, zoo.ammonia_half_saturation_for_bacterial_activity), + adapt(to, zoo.phosphate_half_saturation_for_bacterial_activity), + adapt(to, zoo.iron_half_saturation_for_bacterial_activity)) + +Adapt.adapt_structure(to, zoo::QualityDependantZooplankton) = + QualityDependantZooplankton(adapt(to, zoo.temperature_sensetivity), + adapt(to, zoo.maximum_grazing_rate), + adapt(to, zoo.food_preferences), # the only one that isn't already bits + adapt(to, zoo.food_threshold_concentration), + adapt(to, zoo.specific_food_thresehold_concentration), + adapt(to, zoo.grazing_half_saturation), + adapt(to, zoo.maximum_flux_feeding_rate), + adapt(to, zoo.iron_ratio), + adapt(to, zoo.minimum_growth_efficiency), + adapt(to, zoo.non_assililated_fraction), + adapt(to, zoo.mortality_half_saturation), + adapt(to, zoo.quadratic_mortality), + adapt(to, zoo.linear_mortality), + adapt(to, zoo.dissolved_excretion_fraction), + adapt(to, zoo.undissolved_calcite_fraction)) diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl index c2bec7b9e..ac38f40c5 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl @@ -37,3 +37,8 @@ end @inline grazing_preference(::Val{:D}, preferences) = preferences.D @inline grazing_preference(::Val{:Z}, preferences) = preferences.Z @inline grazing_preference(::Val{:POC}, preferences) = preferences.POC + +# TODO: this should dispatch on PISCES{<:NanoAndDiatoms, <:MicroAndMeso, <:Any, <:Two...} but the phyto and POC +# classes are not yet defined +@inline prey_names(::PISCES{<:Any, <:MicroAndMeso}, ::Val{:Z}) = (:P, :D, :POC) +@inline prey_names(::PISCES{<:Any, <:MicroAndMeso}, ::Val{:M}) = (:P, :D, :Z, :POC) diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl index 0c41b0d4e..abd363006 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl @@ -8,12 +8,11 @@ particulates (POC and GOC). This model assumes a fixed ratio for all other elements (i.e. N, P, Fe). """ -@kwdef struct QualityDependantZooplankton{FT, FP, FN} +@kwdef struct QualityDependantZooplankton{FT, FP} temperature_sensetivity :: FT = 1.079 # maximum_grazing_rate :: FT # 1 / s food_preferences :: FP - food_names :: FN = keys(food_preferences) food_threshold_concentration :: FT = 0.3 # mmol C / m³ specific_food_thresehold_concentration :: FT = 0.001 # mmol C / m³ @@ -57,7 +56,7 @@ end g₀ = zoo.maximum_grazing_rate b = zoo.temperature_sensetivity p = zoo.food_preferences - food = zoo.food_names + food = prey_names(bgc, val_name) J = zoo.specific_food_thresehold_concentration K = zoo.grazing_half_saturation food_threshold_concentration = zoo.food_threshold_concentration From acf4a5d04e63511f3c0acbf28efc6463b7915024 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 20:35:58 +0100 Subject: [PATCH 16/33] oops --- .../PISCES/zooplankton/food_quality_dependant.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl index abd363006..21d03b26a 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl @@ -155,7 +155,7 @@ end g₀ = zoo.maximum_grazing_rate b = zoo.temperature_sensetivity p = zoo.food_preferences - food = zoo.food_names + food = prey_names(bgc, val_name) J = zoo.specific_food_thresehold_concentration K = zoo.grazing_half_saturation food_threshold_concentration = zoo.food_threshold_concentration From 535ba2d8e6549145ca6604cf436f3cc0f448c0b7 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 21:41:34 +0200 Subject: [PATCH 17/33] I have no idea how this worked for the GPU tests --- .../AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl index 225782e5b..8bb506958 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl @@ -4,7 +4,7 @@ g₀ = zoo.maximum_grazing_rate b = zoo.temperature_sensetivity p = zoo.food_preferences - food = zoo.food_names + food = prey_names(bgc, val_name) J = zoo.specific_food_thresehold_concentration K = zoo.grazing_half_saturation food_threshold_concentration = zoo.food_threshold_concentration From 5c72e7c2dcf4925729a8d8752223d9bf9d1c7f69 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 21:44:53 +0200 Subject: [PATCH 18/33] fix light --- src/Light/prescribed.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Light/prescribed.jl b/src/Light/prescribed.jl index 99e1a3e93..6de547c34 100644 --- a/src/Light/prescribed.jl +++ b/src/Light/prescribed.jl @@ -1,5 +1,5 @@ using Oceananigans.Architectures: architecture, GPU -using Oceananigans.Fields: compute!, AbstractField +using Oceananigans.Fields: compute!, AbstractField, ConstantField function maybe_named_fields(field) @@ -10,6 +10,9 @@ end maybe_named_fields(fields::NamedTuple) = (keys(fields), values(fields)) +is_on_gpu(field) = isa(architecture(field), GPU) +is_on_gpu(::ConstantField) = false + """ PrescribedPhotosyntheticallyActiveRadiation(fields) @@ -21,7 +24,6 @@ fields which are user specified, e.g. they may be `FunctionField`s or fields which will be returned in `biogeochemical_auxiliary_fields`, if only one field is present the field will be named `PAR`. """ - struct PrescribedPhotosyntheticallyActiveRadiation{F, FN} fields :: F field_names :: FN @@ -32,7 +34,7 @@ struct PrescribedPhotosyntheticallyActiveRadiation{F, FN} function PrescribedPhotosyntheticallyActiveRadiation(fields) names, values = maybe_named_fields(fields) - isa(architecture(values[1]), GPU) || + is_on_gpu(values[1]) || @warn "On `GPU` prescribed fields will be renamed to `PAR`, `PAR₁`, etc., as symbols can not be passed to the GPU. Please make sure they are in this order. (We're assuming that you're only using this for testing purposes.)" From 74a4d69ec27cc52e41d64bd9581da583ca5eab01 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Wed, 25 Sep 2024 21:52:26 +0200 Subject: [PATCH 19/33] Change timestep --- validation/PISCES/column.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/PISCES/column.jl b/validation/PISCES/column.jl index 345cfc9ee..32ca1f236 100644 --- a/validation/PISCES/column.jl +++ b/validation/PISCES/column.jl @@ -86,7 +86,7 @@ set!(model, P = 0.1, PChl = 0.025, PFe = 0.005, DIC = 2205, Alk = 2560, O₂ = 317, S = 35) # maybe get to 1.5hours after initial stuff -simulation = Simulation(model, Δt = 1.5hours, stop_time = 10years) +simulation = Simulation(model, Δt = 20minutes, stop_time = 5years) progress_message(sim) = @printf("Iteration: %04d, time: %s, Δt: %s, wall time: %s\n", iteration(sim), From 099ded4dc67aaea3b842f194c2e835679e9670af Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Thu, 26 Sep 2024 09:43:37 +0200 Subject: [PATCH 20/33] some optimisations... we shall see if they work --- src/Light/multi_band.jl | 6 +++--- .../PISCES/zooplankton/defaults.jl | 19 +++++++++++++++++++ .../zooplankton/food_quality_dependant.jl | 11 ++++++----- .../PISCES/zooplankton/iron_grazing.jl | 4 ++-- src/OceanBioME.jl | 6 +----- test/test_PISCES.jl | 4 ++-- 6 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/Light/multi_band.jl b/src/Light/multi_band.jl index 495d1c60d..7461783e0 100644 --- a/src/Light/multi_band.jl +++ b/src/Light/multi_band.jl @@ -163,9 +163,9 @@ function update_biogeochemical_state!(model, PAR::MultiBandPhotosyntheticallyAct k′) end - for field in PAR.fields - fill_halo_regions!(field, model.clock, fields(model)) - end + #for field in PAR.fields + # fill_halo_regions!(field, model.clock, fields(model)) + #end end summary(par::MultiBandPhotosyntheticallyActiveRadiation) = diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl index ac38f40c5..69e0d98f6 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl @@ -42,3 +42,22 @@ end # classes are not yet defined @inline prey_names(::PISCES{<:Any, <:MicroAndMeso}, ::Val{:Z}) = (:P, :D, :POC) @inline prey_names(::PISCES{<:Any, <:MicroAndMeso}, ::Val{:M}) = (:P, :D, :Z, :POC) + +# TODO: move these somewhere else so they can be dispatched on ::PISCES{<:NanoAndDiatoms, <:MicroAndMeso, <:Any, <:TwoCompartementCarbonIronParticles} +@inline function extract_food_availability(::PISCES, i, j, k, fields, ::NTuple{N}) where N + P = @inbounds fields.P[i, j, k] + D = @inbounds fields.D[i, j, k] + Z = @inbounds fields.Z[i, j, k] + POC = @inbounds fields.POC[i, j, k] + + return (; P, D, Z, POC) +end + +@inline function extract_iron_availability(bgc::PISCES, i, j, k, fields, ::NTuple{N}) where N + P = @inbounds fields.PFe[i, j, k] / fields.P[i, j, k] + D = @inbounds fields.DFe[i, j, k] / fields.D[i, j, k] + Z = bgc.zooplankton.micro.iron_ratio + POC = @inbounds fields.POC[i, j, k] / fields.SFe[i, j, k] + + return (; P, D, Z, POC) +end \ No newline at end of file diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl index 21d03b26a..2b705ea9c 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/food_quality_dependant.jl @@ -45,10 +45,11 @@ required_biogeochemical_tracers(::QualityDependantZooplankton, name_base) = tupl return e * (gI + gfI) - mI end -@inline extract_food_availability(i, j, k, fields, names::NTuple{N}) where N = +# fallback +@inline extract_food_availability(bgc, i, j, k, fields, names::NTuple{N}) where N = ntuple(n -> concentration(Val(names[n]), i, j, k, fields), Val(N)) -@inline extract_iron_availability(i, j, k, bgc, fields, names::NTuple{N}) where N = +@inline extract_iron_availability(bgc, i, j, k, fields, names::NTuple{N}) where N = ntuple(n -> iron_ratio(Val(names[n]), i, j, k, bgc, fields), Val(N)) @inline function grazing(zoo::QualityDependantZooplankton, val_name, i, j, k, grid, bgc, clock, fields, auxiliary_fields) @@ -68,7 +69,7 @@ end base_grazing_rate = g₀ * b ^ T - food_availability = extract_food_availability(i, j, k, fields, food) + food_availability = extract_food_availability(bgc, i, j, k, fields, food) total_food = sum(ntuple(n->food_availability[n] * p[n], Val(N))) @@ -83,7 +84,7 @@ end e₀ = zoo.minimum_growth_efficiency σ = zoo.non_assililated_fraction - iron_availabillity = extract_iron_availability(i, j, k, bgc, fields, food) + iron_availabillity = extract_iron_availability(bgc, i, j, k, fields, food) total_iron = sum(ntuple(n->iron_availabillity[n] * p[n], Val(N))) @@ -167,7 +168,7 @@ end base_grazing_rate = g₀ * b ^ T - food_availability = extract_food_availability(i, j, k, fields, food) + food_availability = extract_food_availability(bgc, i, j, k, fields, food) total_food = sum(ntuple(n->food_availability[n] * p[n], Val(N))) diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl index 8bb506958..8ab91c55f 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/iron_grazing.jl @@ -17,7 +17,7 @@ base_grazing_rate = g₀ * b ^ T - food_availability = extract_food_availability(i, j, k, fields, food) + food_availability = extract_food_availability(bgc, i, j, k, fields, food) total_food = sum(ntuple(n->food_availability[n] * p[n], Val(N))) @@ -27,7 +27,7 @@ total_specific_grazing = base_grazing_rate * concentration_limited_grazing / (K + total_food) - iron_ratios = extract_iron_availability(i, j, k, bgc, fields, food) + iron_ratios = extract_iron_availability(bgc, i, j, k, fields, food) total_specific_iron_grazing = sum(ntuple(n->max(zero(grid), (food_availability[n] - J)) * p[n] * iron_ratios[n], Val(N))) * total_specific_grazing / (available_total_food + eps(0.0)) diff --git a/src/OceanBioME.jl b/src/OceanBioME.jl index ba898722d..a7d0a3004 100644 --- a/src/OceanBioME.jl +++ b/src/OceanBioME.jl @@ -147,11 +147,7 @@ end update_tendencies!(bgc, modifier, model) = nothing update_tendencies!(bgc, modifiers::Tuple, model) = [update_tendencies!(bgc, modifier, model) for modifier in modifiers] -# do we still need this for CPU kernels??? -@inline biogeochemical_transition(i, j, k, grid, bgc::CompleteBiogeochemistry, val_tracer_name, clock, fields) = - biogeochemical_transition(i, j, k, grid, bgc.underlying_biogeochemistry, val_tracer_name, clock, fields) - -@inline (bgc::CompleteBiogeochemistry)(args...) = bgc.underlying_biogeochemistry(args...) +@inline (bgc::ContinuousBiogeochemistry)(args...) = bgc.underlying_biogeochemistry(args...) function update_biogeochemical_state!(bgc::CompleteBiogeochemistry, model) # TODO: change the order of arguments here since they should definitly be the other way around diff --git a/test/test_PISCES.jl b/test/test_PISCES.jl index 82e080220..98d3a2f62 100644 --- a/test/test_PISCES.jl +++ b/test/test_PISCES.jl @@ -32,7 +32,7 @@ value(field; indices = (1, 1, 1)) = on_architecture(CPU(), interior(field, indic function test_PISCES_conservation() # only on CPU please @info "Testing PISCES element conservation (C, Fe, P, Si)" - validation_warning = "This implementation of PISCES is in early development and has not yet been validated" + validation_warning = "This implementation of PISCES is in early development and has not yet been validated against the operational version" grid = BoxModelGrid(; z = -5) @@ -81,7 +81,7 @@ function test_PISCES_conservation() # only on CPU please total_phosphate_tendencies = sum([value(model.timestepper.Gⁿ[n]) * sf for (n, sf) in zip(conserved_tracers.phosphate.tracers, conserved_tracers.phosphate.scalefactors)]) total_nitrogen_tendencies = sum([value(model.timestepper.Gⁿ[n]) * sf for (n, sf) in zip(conserved_tracers.nitrogen.tracers, conserved_tracers.nitrogen.scalefactors)]) - # should these be exactly zero? - I would expect actual errors to be O(10^-9) to O(10^-6) from experiance + # double precision floats are only valid to 17 bits so this tollerance is actually good @test isapprox(total_carbon_tendencies, 0, atol = 10^-20) @test isapprox(total_iron_tendencies, 0, atol = 10^-21) @test isapprox(total_silicon_tendencies, 0, atol = 10^-30) From 34cef200d064bc533681dcf714a5f30143e9b342 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Thu, 26 Sep 2024 09:49:23 +0200 Subject: [PATCH 21/33] oops --- src/Light/prescribed.jl | 2 +- .../AdvectedPopulations/PISCES/zooplankton/defaults.jl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Light/prescribed.jl b/src/Light/prescribed.jl index 6de547c34..f0cb603bb 100644 --- a/src/Light/prescribed.jl +++ b/src/Light/prescribed.jl @@ -34,7 +34,7 @@ struct PrescribedPhotosyntheticallyActiveRadiation{F, FN} function PrescribedPhotosyntheticallyActiveRadiation(fields) names, values = maybe_named_fields(fields) - is_on_gpu(values[1]) || + is_on_gpu(values[1]) && @warn "On `GPU` prescribed fields will be renamed to `PAR`, `PAR₁`, etc., as symbols can not be passed to the GPU. Please make sure they are in this order. (We're assuming that you're only using this for testing purposes.)" diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl index 69e0d98f6..64b82a212 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl @@ -54,10 +54,10 @@ end end @inline function extract_iron_availability(bgc::PISCES, i, j, k, fields, ::NTuple{N}) where N - P = @inbounds fields.PFe[i, j, k] / fields.P[i, j, k] - D = @inbounds fields.DFe[i, j, k] / fields.D[i, j, k] + P = @inbounds fields.PFe[i, j, k] / (fields.P[i, j, k] + eps(0.0)) + D = @inbounds fields.DFe[i, j, k] / (fields.D[i, j, k] + eps(0.0)) Z = bgc.zooplankton.micro.iron_ratio - POC = @inbounds fields.POC[i, j, k] / fields.SFe[i, j, k] + POC = @inbounds fields.POC[i, j, k] / (fields.SFe[i, j, k] + eps(0.0)) return (; P, D, Z, POC) end \ No newline at end of file From eeb24ba575f3c00c41cf1e07a5674f718d0d8f95 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Thu, 26 Sep 2024 09:55:36 +0200 Subject: [PATCH 22/33] more oops --- .../AdvectedPopulations/PISCES/zooplankton/defaults.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl index 64b82a212..ff33993f4 100644 --- a/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl +++ b/src/Models/AdvectedPopulations/PISCES/zooplankton/defaults.jl @@ -47,17 +47,17 @@ end @inline function extract_food_availability(::PISCES, i, j, k, fields, ::NTuple{N}) where N P = @inbounds fields.P[i, j, k] D = @inbounds fields.D[i, j, k] - Z = @inbounds fields.Z[i, j, k] POC = @inbounds fields.POC[i, j, k] + Z = @inbounds fields.Z[i, j, k] - return (; P, D, Z, POC) + return (; P, D, POC, Z) end @inline function extract_iron_availability(bgc::PISCES, i, j, k, fields, ::NTuple{N}) where N P = @inbounds fields.PFe[i, j, k] / (fields.P[i, j, k] + eps(0.0)) D = @inbounds fields.DFe[i, j, k] / (fields.D[i, j, k] + eps(0.0)) + POC = @inbounds fields.SFe[i, j, k] / (fields.POC[i, j, k] + eps(0.0)) Z = bgc.zooplankton.micro.iron_ratio - POC = @inbounds fields.POC[i, j, k] / (fields.SFe[i, j, k] + eps(0.0)) - return (; P, D, Z, POC) + return (; P, D, POC, Z) end \ No newline at end of file From f50e6cbd8ec4bce2d9a11c499a0a4fb8f2250b4b Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Thu, 26 Sep 2024 10:03:15 +0100 Subject: [PATCH 23/33] fix some gpu issues --- src/Light/multi_band.jl | 17 ++++++++++++----- src/Models/AdvectedPopulations/PISCES/adapts.jl | 1 - 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Light/multi_band.jl b/src/Light/multi_band.jl index 7461783e0..f25462855 100644 --- a/src/Light/multi_band.jl +++ b/src/Light/multi_band.jl @@ -91,11 +91,17 @@ function MultiBandPhotosyntheticallyActiveRadiation(; grid, sum(surface_PAR_division) == 1 || throw(ArgumentError("surface_PAR_division does not sum to 1")) - fields = [CenterField(grid; - boundary_conditions = - regularize_field_boundary_conditions( - FieldBoundaryConditions(top = ValueBoundaryCondition(ScaledSurfaceFunction(surface_PAR, surface_PAR_division[n]))), grid, name)) - for (n, name) in enumerate(field_names)] + n_fields = length(field_names) + + surface_boundary_conditions = + ntuple(n-> ValueBoundaryCondition(ScaledSurfaceFunction(surface_PAR, surface_PAR_division[n])), Val(n_fields)) + + field_boundary_conditions = + ntuple(n -> regularize_field_boundary_conditions(FieldBoundaryConditions(top = surface_boundary_conditions[n]), + grid, field_names[n]), + Val(n_fields)) + + fields = ntuple(n -> CenterField(grid; boundary_conditions = field_boundary_conditions[n]), Val(n_fields)) total_PAR = sum(fields) @@ -187,5 +193,6 @@ Adapt.adapt_structure(to, par::MultiBandPhotosyntheticallyActiveRadiation) = nothing, nothing, nothing, + nothing, nothing) diff --git a/src/Models/AdvectedPopulations/PISCES/adapts.jl b/src/Models/AdvectedPopulations/PISCES/adapts.jl index 19a81a091..6ccd1fccc 100644 --- a/src/Models/AdvectedPopulations/PISCES/adapts.jl +++ b/src/Models/AdvectedPopulations/PISCES/adapts.jl @@ -33,7 +33,6 @@ Adapt.adapt_structure(to, bgc::PISCES) = Adapt.adapt_structure(to, zoo::MicroAndMeso) = MicroAndMeso(adapt(to, zoo.micro), adapt(to, zoo.meso), - adapt(to, zoo.microzooplankton_bacteria_concentration), adapt(to, zoo.mesozooplankton_bacteria_concentration), adapt(to, zoo.maximum_bacteria_concentration), From cc158a87c25d0d7838a4584e04afd2f591e06be4 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Thu, 26 Sep 2024 15:21:58 +0100 Subject: [PATCH 24/33] seems very slow... --- src/Light/multi_band.jl | 10 +++++----- src/Light/prescribed.jl | 29 +++++------------------------ validation/PISCES/column.jl | 26 +++++++++++++++++++------- 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/Light/multi_band.jl b/src/Light/multi_band.jl index f25462855..bb3f55fe2 100644 --- a/src/Light/multi_band.jl +++ b/src/Light/multi_band.jl @@ -71,7 +71,7 @@ function MultiBandPhotosyntheticallyActiveRadiation(; grid, base_water_attenuation_coefficient = MOREL_kʷ, base_chlorophyll_exponent = MOREL_e, base_chlorophyll_attenuation_coefficient = MOREL_χ, - field_names = [par_symbol(n) for n in 1:length(bands)], + field_names = ntuple(n->par_symbol(n), Val(length(bands))), surface_PAR = default_surface_PAR, surface_PAR_division = fill(1 / length(bands), length(bands))) Nbands = length(bands) @@ -101,7 +101,7 @@ function MultiBandPhotosyntheticallyActiveRadiation(; grid, grid, field_names[n]), Val(n_fields)) - fields = ntuple(n -> CenterField(grid; boundary_conditions = field_boundary_conditions[n]), Val(n_fields)) + fields = NamedTuple{field_names}(ntuple(n -> CenterField(grid; boundary_conditions = field_boundary_conditions[n]), Val(n_fields))) total_PAR = sum(fields) @@ -121,9 +121,9 @@ function numerical_mean(λ, C, idx1, idx2) return ∫Cdλ / ∫dλ end -par_symbol(n) = Symbol(:PAR, number_subscript(tuple(reverse(digits(n))...))...) +@inline par_symbol(n) = Symbol(:PAR, Char('\xe2\x82\x80'+n)) #Symbol(:PAR, number_subscript(tuple(reverse(digits(n))...))...) -number_subscript(digits::NTuple{N}) where N = +@inline number_subscript(digits::NTuple{N}) where N = ntuple(n->Symbol(Char('\xe2\x82\x80'+digits[n])), Val(N)) @kernel function update_MultiBandPhotosyntheticallyActiveRadiation!(grid, field, kʷ, e, χ, @@ -180,7 +180,7 @@ summary(par::MultiBandPhotosyntheticallyActiveRadiation) = show(io::IO, model::MultiBandPhotosyntheticallyActiveRadiation) = print(io, summary(model)) biogeochemical_auxiliary_fields(par::MultiBandPhotosyntheticallyActiveRadiation) = - merge((PAR = par.total, ), NamedTuple{field_names(par.field_names, par.fields)}(par.fields)) + merge((PAR = par.total, ), par.fields)#NamedTuple{field_names(par.field_names, par.fields)}(par.fields)) @inline field_names(field_names, fields) = field_names @inline field_names(::Nothing, fields::NTuple{N}) where N = ntuple(n -> par_symbol(n), Val(N)) diff --git a/src/Light/prescribed.jl b/src/Light/prescribed.jl index f0cb603bb..facbe682b 100644 --- a/src/Light/prescribed.jl +++ b/src/Light/prescribed.jl @@ -24,30 +24,12 @@ fields which are user specified, e.g. they may be `FunctionField`s or fields which will be returned in `biogeochemical_auxiliary_fields`, if only one field is present the field will be named `PAR`. """ -struct PrescribedPhotosyntheticallyActiveRadiation{F, FN} - fields :: F - field_names :: FN - - PrescribedPhotosyntheticallyActiveRadiation(fields::F, names::FN) where {F, FN} = - new{F, FN}(fields, names) - - function PrescribedPhotosyntheticallyActiveRadiation(fields) - names, values = maybe_named_fields(fields) - - is_on_gpu(values[1]) && - @warn "On `GPU` prescribed fields will be renamed to `PAR`, `PAR₁`, etc., as symbols can not be passed to the GPU. -Please make sure they are in this order. -(We're assuming that you're only using this for testing purposes.)" - - F = typeof(values) - FN = typeof(names) - - return new{F, FN}(values, names) - end +struct PrescribedPhotosyntheticallyActiveRadiation{F} + fields :: F end function update_biogeochemical_state!(model, PAR::PrescribedPhotosyntheticallyActiveRadiation) - for field in PAR.fields + for field in values(PAR.fields) compute!(field) end @@ -59,11 +41,10 @@ show(io::IO, model::PrescribedPhotosyntheticallyActiveRadiation{F}) where {F} = " Fields:", "\n", " └── $(model.field_names)") -biogeochemical_auxiliary_fields(par::PrescribedPhotosyntheticallyActiveRadiation) = - NamedTuple{prescribed_field_names(par.field_names, par.fields)}(par.fields) +biogeochemical_auxiliary_fields(par::PrescribedPhotosyntheticallyActiveRadiation) = par.fields @inline prescribed_field_names(field_names, fields) = field_names @inline prescribed_field_names(::Nothing, fields::NTuple{N}) where N = tuple(:PAR, ntuple(n -> par_symbol(n), Val(N-1))...) adapt_structure(to, par::PrescribedPhotosyntheticallyActiveRadiation) = - PrescribedPhotosyntheticallyActiveRadiation(adapt(to, par.fields), nothing) + PrescribedPhotosyntheticallyActiveRadiation(adapt(to, par.fields)) diff --git a/validation/PISCES/column.jl b/validation/PISCES/column.jl index 32ca1f236..34a32e850 100644 --- a/validation/PISCES/column.jl +++ b/validation/PISCES/column.jl @@ -38,7 +38,7 @@ nothing #hide @inline temp(z, t) = 2.4 * (1 + cos(t * 2π / year + 50days)) * ifelse(z > MLD(t), 1, exp((z - MLD(t))/20)) + 8 -grid = RectilinearGrid(topology = (Flat, Flat, Bounded), size = (100, ), extent = (400, )) +grid = RectilinearGrid(GPU(), topology = (Flat, Flat, Bounded), size = (100, ), extent = (400, )) clock = Clock(; time = 0.0) @@ -67,6 +67,7 @@ O₂_flux = OxygenGasExchangeBoundaryCondition() model = HydrostaticFreeSurfaceModel(; grid, velocities = PrescribedVelocityFields(), tracer_advection = TracerAdvection(nothing, nothing, WENOFifthOrder(grid)), + momentum_advection = nothing, buoyancy = nothing, clock, closure = ScalarDiffusivity(VerticallyImplicitTimeDiscretization(), κ = κ_field), @@ -86,7 +87,7 @@ set!(model, P = 0.1, PChl = 0.025, PFe = 0.005, DIC = 2205, Alk = 2560, O₂ = 317, S = 35) # maybe get to 1.5hours after initial stuff -simulation = Simulation(model, Δt = 20minutes, stop_time = 5years) +simulation = Simulation(model, Δt = 30minutes, stop_time = 5years) progress_message(sim) = @printf("Iteration: %04d, time: %s, Δt: %s, wall time: %s\n", iteration(sim), @@ -97,12 +98,23 @@ progress_message(sim) = @printf("Iteration: %04d, time: %s, Δt: %s, wall time: add_callback!(simulation, progress_message, TimeInterval(10day)) # prescribe the temperature +using KernelAbstractions: @index, @kernel +using Oceananigans.Architectures: architecture +using Oceananigans.Grids: znode, Center +using Oceananigans.Utils: launch! + +@kernel function fill_T!(T, grid, temp, t) + i, j, k = @index(Global, NTuple) + + z = znode(i, j, k, grid, Center(), Center(), Center()) + + @inbounds T[i, j, k] = temp(z, t) + +end function update_temperature!(simulation) t = time(simulation) - T = reshape(map(z -> temp(z, t), znodes(simulation.model.grid, Center())), (1, 1, size(grid, 3))) - - set!(simulation.model.tracers.T, T) + launch!(architecture(grid), grid, :xyz, fill_T!, model.tracers.T, grid, temp, t) return nothing end @@ -112,7 +124,7 @@ add_callback!(simulation, update_temperature!, IterationInterval(1)) filename = "column" simulation.output_writers[:tracers] = JLD2OutputWriter(model, model.tracers, filename = "$filename.jld2", - schedule = TimeInterval(1day), + schedule = TimeInterval(3day), overwrite_existing = true) PAR = Field(Oceananigans.Biogeochemistry.biogeochemical_auxiliary_fields(biogeochemistry.light_attenuation).PAR) @@ -124,7 +136,7 @@ internal_fields = (; biogeochemistry.underlying_biogeochemistry.calcite_saturati simulation.output_writers[:internals] = JLD2OutputWriter(model, internal_fields, filename = "$(filename)_internal_fields.jld2", - schedule = TimeInterval(1day), + schedule = TimeInterval(3day), overwrite_existing = true) # ## Run! # We are ready to run the simulation From ddb3858794e97aab57418cd934898308f1f3f95f Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Thu, 26 Sep 2024 15:22:25 +0100 Subject: [PATCH 25/33] might need to revert this --- src/OceanBioME.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/OceanBioME.jl b/src/OceanBioME.jl index a7d0a3004..51323683a 100644 --- a/src/OceanBioME.jl +++ b/src/OceanBioME.jl @@ -141,7 +141,6 @@ function update_tendencies!(bgc::CompleteBiogeochemistry, model) update_tendencies!(bgc, bgc.sediment, model) update_tendencies!(bgc, bgc.particles, model) update_tendencies!(bgc, bgc.modifiers, model) - synchronize(device(architecture(model))) end update_tendencies!(bgc, modifier, model) = nothing @@ -152,7 +151,7 @@ update_tendencies!(bgc, modifiers::Tuple, model) = [update_tendencies!(bgc, modi function update_biogeochemical_state!(bgc::CompleteBiogeochemistry, model) # TODO: change the order of arguments here since they should definitly be the other way around update_biogeochemical_state!(model, bgc.modifiers) - synchronize(device(architecture(model))) + #synchronize(device(architecture(model))) update_biogeochemical_state!(model, bgc.light_attenuation) update_biogeochemical_state!(model, bgc.underlying_biogeochemistry) end From 3ed229c240e0da8eebb282055bf92a2f10a7addb Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Fri, 27 Sep 2024 08:27:28 +0100 Subject: [PATCH 26/33] spurious project/manifest changes --- Manifest.toml | 913 +------------------------------------------------- Project.toml | 1 - 2 files changed, 5 insertions(+), 909 deletions(-) diff --git a/Manifest.toml b/Manifest.toml index f0915a524..e6f2f97be 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.10.2" manifest_format = "2.0" -project_hash = "0f443e6e4f3f77224894e0809e37a63fc52baac5" +project_hash = "d9a7ed1497e9d29249634fa08034c82fcc96d9c4" [[deps.AbstractFFTs]] deps = ["LinearAlgebra"] @@ -15,11 +15,6 @@ weakdeps = ["ChainRulesCore", "Test"] AbstractFFTsChainRulesCoreExt = "ChainRulesCore" AbstractFFTsTestExt = "Test" -[[deps.AbstractTrees]] -git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" -uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" -version = "0.4.5" - [[deps.Accessors]] deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "LinearAlgebra", "MacroTools", "Markdown", "Test"] git-tree-sha1 = "f61b15be1d76846c0ce31d3fcfac5380ae53db6a" @@ -51,23 +46,6 @@ weakdeps = ["StaticArrays"] [deps.Adapt.extensions] AdaptStaticArraysExt = "StaticArrays" -[[deps.AdaptivePredicates]] -git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6" -uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7" -version = "1.2.0" - -[[deps.AliasTables]] -deps = ["PtrArrays", "Random"] -git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" -uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" -version = "1.1.3" - -[[deps.Animations]] -deps = ["Colors"] -git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d" -uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340" -version = "0.4.1" - [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.1" @@ -81,24 +59,6 @@ git-tree-sha1 = "c06a868224ecba914baa6942988e2f2aade419be" uuid = "a9b6321e-bd34-4604-b9c9-b65b8de01458" version = "0.1.0" -[[deps.Automa]] -deps = ["PrecompileTools", "TranscodingStreams"] -git-tree-sha1 = "014bc22d6c400a7703c0f5dc1fdc302440cf88be" -uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b" -version = "1.0.4" - -[[deps.AxisAlgorithms]] -deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] -git-tree-sha1 = "01b8ccb13d68535d73d2b0c23e39bd23155fb712" -uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950" -version = "1.1.0" - -[[deps.AxisArrays]] -deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"] -git-tree-sha1 = "16351be62963a67ac4083f748fdb3cca58bfd52f" -uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9" -version = "0.4.7" - [[deps.BFloat16s]] deps = ["LinearAlgebra", "Printf", "Random", "Test"] git-tree-sha1 = "2c7cc21e8678eff479978a0a2ef5ce2f51b63dff" @@ -131,21 +91,6 @@ git-tree-sha1 = "5afb5c5ba2688ca43a9ad2e5a91cbb93921ccfa1" uuid = "179af706-886a-5703-950a-314cd64e0468" version = "0.1.3" -[[deps.CRC32c]] -uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" - -[[deps.CRlibm]] -deps = ["CRlibm_jll"] -git-tree-sha1 = "32abd86e3c2025db5172aa182b982debed519834" -uuid = "96374032-68de-5a5b-8d9e-752f78720389" -version = "1.0.1" - -[[deps.CRlibm_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" -uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" -version = "1.0.1+0" - [[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 = "fdd9dfb67dfefd548f51000cc400bb51003de247" @@ -180,24 +125,6 @@ git-tree-sha1 = "afea94249b821dc754a8ca6695d3daed851e1f5a" uuid = "76a88914-d11a-5bdc-97e0-2f5a05c973a2" version = "0.14.1+0" -[[deps.Cairo]] -deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] -git-tree-sha1 = "7b6ad8c35f4bc3bca8eb78127c8b99719506a5fb" -uuid = "159f3aea-2a34-519c-b102-8c37f9878175" -version = "1.1.0" - -[[deps.CairoMakie]] -deps = ["CRC32c", "Cairo", "Cairo_jll", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"] -git-tree-sha1 = "4f827b38d3d9ffe6e3b01fbcf866c625fa259ca5" -uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" -version = "0.12.11" - -[[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 = "a2f1c8c668c8e3cb4cca4e57a8efdb09067bb3fd" -uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.0+2" - [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] git-tree-sha1 = "71acdbf594aab5bbb2cec89b208c41b4c411e49f" @@ -208,34 +135,12 @@ weakdeps = ["SparseArrays"] [deps.ChainRulesCore.extensions] ChainRulesCoreSparseArraysExt = "SparseArrays" -[[deps.ColorBrewer]] -deps = ["Colors", "JSON", "Test"] -git-tree-sha1 = "61c5334f33d91e570e1d0c3eb5465835242582c4" -uuid = "a2cac450-b92f-5266-8821-25eda20663c8" -version = "0.4.0" - -[[deps.ColorSchemes]] -deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "b5278586822443594ff615963b0c09755771b3e0" -uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.26.0" - [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d" uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" version = "0.11.5" -[[deps.ColorVectorSpace]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] -git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249" -uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" -version = "0.10.0" -weakdeps = ["SpecialFunctions"] - - [deps.ColorVectorSpace.extensions] - SpecialFunctionsExt = "SpecialFunctions" - [[deps.Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] git-tree-sha1 = "362a287c3aa50601b0bc359053d5c2468f0e7ce0" @@ -281,17 +186,16 @@ weakdeps = ["InverseFunctions"] git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" version = "1.5.8" -weakdeps = ["IntervalSets", "LinearAlgebra", "StaticArrays"] [deps.ConstructionBase.extensions] ConstructionBaseIntervalSetsExt = "IntervalSets" ConstructionBaseLinearAlgebraExt = "LinearAlgebra" ConstructionBaseStaticArraysExt = "StaticArrays" -[[deps.Contour]] -git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" -uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.6.3" + [deps.ConstructionBase.weakdeps] + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [[deps.Crayons]] git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" @@ -330,12 +234,6 @@ version = "1.0.0" deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -[[deps.DelaunayTriangulation]] -deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "Random"] -git-tree-sha1 = "94eb20e6621600f4315813b1d1fc9b8a5a6a34db" -uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" -version = "1.4.0" - [[deps.DiskArrays]] deps = ["LRUCache", "OffsetArrays"] git-tree-sha1 = "ef25c513cad08d7ebbed158c91768ae32f308336" @@ -357,22 +255,6 @@ weakdeps = ["ChainRulesCore", "SparseArrays"] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -[[deps.Distributions]] -deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "e6c693a0e4394f8fda0e51a5bdf5aef26f8235e9" -uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.111" - - [deps.Distributions.extensions] - DistributionsChainRulesCoreExt = "ChainRulesCore" - DistributionsDensityInterfaceExt = "DensityInterface" - DistributionsTestExt = "Test" - - [deps.Distributions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" - Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - [[deps.DocStringExtensions]] deps = ["LibGit2"] git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" @@ -384,50 +266,11 @@ deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" -[[deps.EarCut_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e3290f2d49e661fbd94046d7e3726ffcb2d41053" -uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" -version = "2.2.4+0" - -[[deps.EnumX]] -git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" -uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" -version = "1.0.4" - -[[deps.ErrorfreeArithmetic]] -git-tree-sha1 = "d6863c556f1142a061532e79f611aa46be201686" -uuid = "90fa49ef-747e-5e6f-a989-263ba693cf1a" -version = "0.5.2" - -[[deps.ExactPredicates]] -deps = ["IntervalArithmetic", "Random", "StaticArraysCore", "Test"] -git-tree-sha1 = "276e83bc8b21589b79303b9985c321024ffdf59c" -uuid = "429591f6-91af-11e9-00e2-59fbe8cec110" -version = "2.2.5" - -[[deps.Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" -uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.6.2+0" - [[deps.ExprTools]] git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" version = "0.1.10" -[[deps.Extents]] -git-tree-sha1 = "81023caa0021a41712685887db1fc03db26f41f5" -uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" -version = "0.1.4" - -[[deps.FFMPEG_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] -git-tree-sha1 = "8cc47f299902e13f90405ddb5bf87e5d474c0d38" -uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" -version = "6.1.2+0" - [[deps.FFTW]] deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" @@ -440,91 +283,21 @@ git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" version = "3.3.10+0" -[[deps.FastRounding]] -deps = ["ErrorfreeArithmetic", "LinearAlgebra"] -git-tree-sha1 = "6344aa18f654196be82e62816935225b3b9abe44" -uuid = "fa42c844-2597-5d31-933b-ebd51ab2693f" -version = "0.3.1" - [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] git-tree-sha1 = "82d8afa92ecf4b52d78d869f038ebfb881267322" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" version = "1.16.3" -[[deps.FilePaths]] -deps = ["FilePathsBase", "MacroTools", "Reexport", "Requires"] -git-tree-sha1 = "919d9412dbf53a2e6fe74af62a73ceed0bce0629" -uuid = "8fc22ac5-c921-52a6-82fd-178b2807b824" -version = "0.8.3" - -[[deps.FilePathsBase]] -deps = ["Compat", "Dates"] -git-tree-sha1 = "7878ff7172a8e6beedd1dea14bd27c3c6340d361" -uuid = "48062228-2e41-5def-b9a4-89aafe57970f" -version = "0.9.22" -weakdeps = ["Mmap", "Test"] - - [deps.FilePathsBase.extensions] - FilePathsBaseMmapExt = "Mmap" - FilePathsBaseTestExt = "Test" - [[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" -[[deps.FillArrays]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" -uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.13.0" -weakdeps = ["PDMats", "SparseArrays", "Statistics"] - - [deps.FillArrays.extensions] - FillArraysPDMatsExt = "PDMats" - FillArraysSparseArraysExt = "SparseArrays" - FillArraysStatisticsExt = "Statistics" - [[deps.FixedPointNumbers]] deps = ["Statistics"] git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" version = "0.8.5" -[[deps.Fontconfig_jll]] -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.96+0" - -[[deps.Format]] -git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" -uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" -version = "1.3.7" - -[[deps.FreeType]] -deps = ["CEnum", "FreeType2_jll"] -git-tree-sha1 = "907369da0f8e80728ab49c1c7e09327bf0d6d999" -uuid = "b38be410-82b0-50bf-ab77-7b57e271db43" -version = "4.1.1" - -[[deps.FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" -uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.2+0" - -[[deps.FreeTypeAbstraction]] -deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"] -git-tree-sha1 = "2493cdfd0740015955a8e46de4ef28f49460d8bc" -uuid = "663a7486-cb36-511b-a19d-713bb74d65c9" -version = "0.10.3" - -[[deps.FriBidi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" -uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.14+0" - [[deps.Future]] deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" @@ -552,29 +325,6 @@ git-tree-sha1 = "ab29216184312f99ff957b32cd63c2fe9c928b91" uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" version = "0.26.7" -[[deps.GeoFormatTypes]] -git-tree-sha1 = "59107c179a586f0fe667024c5eb7033e81333271" -uuid = "68eda718-8dee-11e9-39e7-89f7f65f511f" -version = "0.4.2" - -[[deps.GeoInterface]] -deps = ["Extents", "GeoFormatTypes"] -git-tree-sha1 = "5921fc0704e40c024571eca551800c699f86ceb4" -uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" -version = "1.3.6" - -[[deps.GeometryBasics]] -deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "b62f2b2d76cee0d61a2ef2b3118cd2a3215d3134" -uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -version = "0.4.11" - -[[deps.Gettext_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" -uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.21.0+0" - [[deps.GibbsSeaWater]] deps = ["GibbsSeaWater_jll", "Libdl", "Test"] git-tree-sha1 = "d1642ddc78d0754603d747d76fac0042a5a85104" @@ -587,12 +337,6 @@ git-tree-sha1 = "c91ca76546871efaa1aefdd2b19cc41c3ead2160" uuid = "6727f6b2-98ea-5d0a-8239-2f72283ddb11" version = "3.5.2+0" -[[deps.Glib_jll]] -deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "7c82e6a6cd34e9d935e9aa4051b66c6ff3af59ba" -uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.80.2+0" - [[deps.Glob]] git-tree-sha1 = "97285bbd5230dd766e9ef6749b80fc617126d496" uuid = "c27321d9-0574-5035-807b-f59d2c89b15c" @@ -604,105 +348,24 @@ git-tree-sha1 = "383db7d3f900f4c1f47a8a04115b053c095e48d3" uuid = "0951126a-58fd-58f1-b5b3-b08c7c4a876d" version = "3.8.4+0" -[[deps.Graphics]] -deps = ["Colors", "LinearAlgebra", "NaNMath"] -git-tree-sha1 = "d61890399bc535850c4bf08e4e0d3a7ad0f21cbd" -uuid = "a2bd30eb-e257-5431-a919-1863eab51364" -version = "1.1.2" - -[[deps.Graphite2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" -uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" -version = "1.3.14+0" - -[[deps.GridLayoutBase]] -deps = ["GeometryBasics", "InteractiveUtils", "Observables"] -git-tree-sha1 = "fc713f007cff99ff9e50accba6373624ddd33588" -uuid = "3955a311-db13-416c-9275-1d80ed98e5e9" -version = "0.11.0" - -[[deps.Grisu]] -git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" -uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" -version = "1.0.2" - [[deps.HDF5_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"] git-tree-sha1 = "38c8874692d48d5440d5752d6c74b0c6b0b60739" uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" version = "1.14.2+1" -[[deps.HarfBuzz_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] -git-tree-sha1 = "401e4f3f30f43af2c8478fc008da50096ea5240f" -uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" -version = "8.3.1+0" - [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "5e19e1e4fa3e71b774ce746274364aef0234634e" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" version = "2.11.1+0" -[[deps.HypergeometricFunctions]] -deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] -git-tree-sha1 = "7c4195be1649ae622304031ed46a2f4df989f1eb" -uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.24" - -[[deps.ImageAxes]] -deps = ["AxisArrays", "ImageBase", "ImageCore", "Reexport", "SimpleTraits"] -git-tree-sha1 = "2e4520d67b0cef90865b3ef727594d2a58e0e1f8" -uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac" -version = "0.6.11" - -[[deps.ImageBase]] -deps = ["ImageCore", "Reexport"] -git-tree-sha1 = "eb49b82c172811fd2c86759fa0553a2221feb909" -uuid = "c817782e-172a-44cc-b673-b171935fbb9e" -version = "0.1.7" - -[[deps.ImageCore]] -deps = ["ColorVectorSpace", "Colors", "FixedPointNumbers", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "PrecompileTools", "Reexport"] -git-tree-sha1 = "b2a7eaa169c13f5bcae8131a83bc30eff8f71be0" -uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534" -version = "0.10.2" - -[[deps.ImageIO]] -deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs"] -git-tree-sha1 = "437abb322a41d527c197fa800455f79d414f0a3c" -uuid = "82e4d734-157c-48bb-816b-45c225c6df19" -version = "0.6.8" - -[[deps.ImageMetadata]] -deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"] -git-tree-sha1 = "355e2b974f2e3212a75dfb60519de21361ad3cb7" -uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49" -version = "0.9.9" - -[[deps.Imath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0936ba688c6d201805a83da835b55c61a180db52" -uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1" -version = "3.1.11+0" - [[deps.IncompleteLU]] deps = ["LinearAlgebra", "SparseArrays"] git-tree-sha1 = "6c676e79f98abb6d33fa28122cad099f1e464afe" uuid = "40713840-3770-5561-ab4c-a76e7d0d7895" version = "0.2.1" -[[deps.IndirectArrays]] -git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" -uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" -version = "1.0.0" - -[[deps.Inflate]] -git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d" -uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" -version = "0.1.5" - [[deps.InlineStrings]] git-tree-sha1 = "45521d31238e87ee9f9732561bfee12d4eebd52d" uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" @@ -726,33 +389,6 @@ version = "2024.2.1+0" deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -[[deps.Interpolations]] -deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"] -git-tree-sha1 = "88a101217d7cb38a7b481ccd50d21876e1d1b0e0" -uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" -version = "0.15.1" -weakdeps = ["Unitful"] - - [deps.Interpolations.extensions] - InterpolationsUnitfulExt = "Unitful" - -[[deps.IntervalArithmetic]] -deps = ["CRlibm", "FastRounding", "LinearAlgebra", "Markdown", "Random", "RecipesBase", "RoundingEmulator", "SetRounding", "StaticArrays"] -git-tree-sha1 = "5ab7744289be503d76a944784bac3f2df7b809af" -uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" -version = "0.20.9" - -[[deps.IntervalSets]] -git-tree-sha1 = "dba9ddf07f77f60450fe5d2e2beb9854d9a49bd0" -uuid = "8197267c-284f-5f27-9208-e0e47529a953" -version = "0.7.10" -weakdeps = ["Random", "RecipesBase", "Statistics"] - - [deps.IntervalSets.extensions] - IntervalSetsRandomExt = "Random" - IntervalSetsRecipesBaseExt = "RecipesBase" - IntervalSetsStatisticsExt = "Statistics" - [[deps.InverseFunctions]] git-tree-sha1 = "2787db24f4e03daf859c6509ff87764e4182f7d1" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" @@ -768,22 +404,6 @@ git-tree-sha1 = "0dc7b50b8d436461be01300fd8cd45aa0274b038" uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" version = "1.3.0" -[[deps.IrrationalConstants]] -git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.2" - -[[deps.Isoband]] -deps = ["isoband_jll"] -git-tree-sha1 = "f9b6d97355599074dc867318950adaa6f9946137" -uuid = "f1662d9f-8043-43de-a69a-05efc1cc6ff4" -version = "0.1.1" - -[[deps.IterTools]] -git-tree-sha1 = "42d5f897009e7ff2cf88db414a389e5ed1bdd023" -uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -version = "1.10.0" - [[deps.IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] git-tree-sha1 = "59545b0a2b27208b0650df0a46b8e3019f85055b" @@ -807,24 +427,6 @@ git-tree-sha1 = "f389674c99bfcde17dc57454011aa44d5a260a40" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" version = "1.6.0" -[[deps.JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.4" - -[[deps.JpegTurbo]] -deps = ["CEnum", "FileIO", "ImageCore", "JpegTurbo_jll", "TOML"] -git-tree-sha1 = "fa6d0bcff8583bac20f1ffa708c3913ca605c611" -uuid = "b835a17e-a41a-41e7-81f0-2f016b05efe0" -version = "0.1.5" - -[[deps.JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c84a835e1a09b289ffcd2271bf2a337bbdda6637" -uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.3+0" - [[deps.JuliaNVTXCallbacks_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "af433a10f3942e882d3c671aacb203e006a5808f" @@ -847,18 +449,6 @@ version = "0.9.25" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -[[deps.KernelDensity]] -deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] -git-tree-sha1 = "7d703202e65efa1369de1279c162b915e245eed1" -uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" -version = "0.6.9" - -[[deps.LAME_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd" -uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.2+0" - [[deps.LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Preferences", "Printf", "Requires", "Unicode"] git-tree-sha1 = "2470e69781ddd70b8878491233cd09bc1bd7fc96" @@ -895,12 +485,6 @@ weakdeps = ["Serialization"] [deps.LRUCache.extensions] SerializationExt = ["Serialization"] -[[deps.LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "70c5da094887fd2cae843b8db33920bac4b6f07d" -uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.2+0" - [[deps.LaTeXStrings]] git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" @@ -910,11 +494,6 @@ version = "1.3.1" deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" -[[deps.LazyModules]] -git-tree-sha1 = "a560dd966b386ac9ae60bdd3a3d3a326062d3c3e" -uuid = "8cdb02fc-e678-4876-92c5-9defec4f444e" -version = "0.3.1" - [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" @@ -942,62 +521,16 @@ version = "1.11.0+1" [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -[[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" -uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+1" - -[[deps.Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] -git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" -uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.11+0" - -[[deps.Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" -uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.49.0+0" - [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" version = "1.17.0+0" -[[deps.Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" -uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.40.1+0" - -[[deps.Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" -uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.40.1+0" - [[deps.LinearAlgebra]] deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -[[deps.LogExpFunctions]] -deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.28" - - [deps.LogExpFunctions.extensions] - LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" - LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" - LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - - [deps.LogExpFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" @@ -1051,33 +584,10 @@ git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" version = "0.5.13" -[[deps.Makie]] -deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"] -git-tree-sha1 = "2281aaf0685e5e8a559982d32f17d617a949b9cd" -uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" -version = "0.21.11" - -[[deps.MakieCore]] -deps = ["ColorTypes", "GeometryBasics", "IntervalSets", "Observables"] -git-tree-sha1 = "22fed09860ca73537a36d4e5a9bce0d9e80ee8a8" -uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" -version = "0.8.8" - -[[deps.MappedArrays]] -git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e" -uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" -version = "0.4.2" - [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -[[deps.MathTeXEngine]] -deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"] -git-tree-sha1 = "e1641f32ae592e415e3dbae7f4a188b5316d4b62" -uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" -version = "0.6.1" - [[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" @@ -1098,12 +608,6 @@ version = "1.2.0" [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" -[[deps.MosaicViews]] -deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"] -git-tree-sha1 = "7b86a5d4d70a9f5cdf2dacb3cbe6d251d1a61dbe" -uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389" -version = "0.3.4" - [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2023.1.10" @@ -1126,24 +630,12 @@ git-tree-sha1 = "ce3269ed42816bf18d500c9f63418d4b0d9f5a3b" uuid = "e98f9f5b-d649-5603-91fd-7774390e6439" version = "3.1.0+2" -[[deps.NaNMath]] -deps = ["OpenLibm_jll"] -git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" -uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "1.0.2" - [[deps.NetCDF_jll]] deps = ["Artifacts", "Blosc_jll", "Bzip2_jll", "HDF5_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenMPI_jll", "XML2_jll", "Zlib_jll", "Zstd_jll", "libzip_jll"] git-tree-sha1 = "a8af1798e4eb9ff768ce7fdefc0e957097793f15" uuid = "7243133f-43d8-5620-bbf4-c2c921802cf3" version = "400.902.209+0" -[[deps.Netpbm]] -deps = ["FileIO", "ImageCore", "ImageMetadata"] -git-tree-sha1 = "d92b107dbb887293622df7697a2223f9f8176fcd" -uuid = "f09324ee-3d7c-5217-9330-fc30815ba969" -version = "1.1.1" - [[deps.Nettle_jll]] deps = ["Artifacts", "GMP_jll", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "eca63e3847dad608cfa6a3329b95ef674c7160b4" @@ -1154,11 +646,6 @@ version = "3.7.2+0" uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" -[[deps.Observables]] -git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" -uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.5.5" - [[deps.Oceananigans]] deps = ["Adapt", "CUDA", "Crayons", "CubedSphere", "Dates", "Distances", "DocStringExtensions", "FFTW", "Glob", "IncompleteLU", "InteractiveUtils", "IterativeSolvers", "JLD2", "KernelAbstractions", "LinearAlgebra", "Logging", "MPI", "NCDatasets", "OffsetArrays", "OrderedCollections", "Pkg", "Printf", "Random", "Rotations", "SeawaterPolynomials", "SparseArrays", "Statistics", "StructArrays"] git-tree-sha1 = "9b1b114e7853bd744ad3feff93232a1e5747ffa1" @@ -1183,34 +670,11 @@ weakdeps = ["Adapt"] [deps.OffsetArrays.extensions] OffsetArraysAdaptExt = "Adapt" -[[deps.Ogg_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" -uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" -version = "1.3.5+1" - [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" version = "0.3.23+4" -[[deps.OpenEXR]] -deps = ["Colors", "FileIO", "OpenEXR_jll"] -git-tree-sha1 = "327f53360fdb54df7ecd01e96ef1983536d1e633" -uuid = "52e1d378-f018-4a11-a4be-720524705ac7" -version = "0.3.2" - -[[deps.OpenEXR_jll]] -deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "8292dd5c8a38257111ada2174000a33745b06d4e" -uuid = "18a262bb-aa17-5467-a713-aee519bc75cb" -version = "3.2.4+0" - -[[deps.OpenLibm_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" - [[deps.OpenMPI_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"] git-tree-sha1 = "bfce6d523861a6c562721b262c0d1aaeead2647f" @@ -1223,18 +687,6 @@ git-tree-sha1 = "1b35263570443fdd9e76c76b7062116e2f374ab8" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" version = "3.0.15+0" -[[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" -uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+0" - -[[deps.Opus_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6703a85cb3781bd5909d48730a67205f3f31a575" -uuid = "91d4177d-7536-5919-b921-800302f37372" -version = "1.3.3+0" - [[deps.OrderedCollections]] git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" @@ -1246,53 +698,6 @@ git-tree-sha1 = "2cd396108e178f3ae8dedbd8e938a18726ab2fbf" uuid = "c2071276-7c44-58a7-b746-946036e04d0a" version = "0.24.1+0" -[[deps.PCRE2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.42.0+1" - -[[deps.PDMats]] -deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "949347156c25054de2db3b166c52ac4728cbad65" -uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" -version = "0.11.31" - -[[deps.PNGFiles]] -deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"] -git-tree-sha1 = "67186a2bc9a90f9f85ff3cc8277868961fb57cbd" -uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883" -version = "0.4.3" - -[[deps.Packing]] -deps = ["GeometryBasics"] -git-tree-sha1 = "ec3edfe723df33528e085e632414499f26650501" -uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566" -version = "0.5.0" - -[[deps.PaddedViews]] -deps = ["OffsetArrays"] -git-tree-sha1 = "0fac6313486baae819364c52b4f483450a9d793f" -uuid = "5432bcbf-9aad-5242-b902-cca2824c8663" -version = "0.5.12" - -[[deps.Pango_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e127b609fb9ecba6f201ba7ab753d5a605d53801" -uuid = "36c8627f-9965-5494-a995-c6b170f724f3" -version = "1.54.1+0" - -[[deps.Parsers]] -deps = ["Dates", "PrecompileTools", "UUIDs"] -git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.8.1" - -[[deps.Pixman_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" -uuid = "30392449-352a-5448-841d-b1acce4e97dc" -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"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" @@ -1304,17 +709,6 @@ git-tree-sha1 = "f9501cc0430a26bc3d156ae1b5b0c1b47af4d6da" uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688" version = "0.3.3" -[[deps.PlotUtils]] -deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "Statistics"] -git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5" -uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.4.1" - -[[deps.PolygonOps]] -git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6" -uuid = "647866c9-e3ac-4575-94e7-e3d426903924" -version = "0.1.2" - [[deps.PooledArrays]] deps = ["DataAPI", "Future"] git-tree-sha1 = "36d8b4b899628fb92c2749eb488d884a926614d3" @@ -1343,35 +737,6 @@ version = "2.3.2" deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -[[deps.ProgressMeter]] -deps = ["Distributed", "Printf"] -git-tree-sha1 = "8f6bc219586aef8baf0ff9a5fe16ee9c70cb65e4" -uuid = "92933f4c-e287-5a05-a399-4b506db050ca" -version = "1.10.2" - -[[deps.PtrArrays]] -git-tree-sha1 = "77a42d78b6a92df47ab37e177b2deac405e1c88f" -uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" -version = "1.2.1" - -[[deps.QOI]] -deps = ["ColorTypes", "FileIO", "FixedPointNumbers"] -git-tree-sha1 = "18e8f4d1426e965c7b532ddd260599e1510d26ce" -uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65" -version = "1.0.0" - -[[deps.QuadGK]] -deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "cda3b045cf9ef07a08ad46731f5a3165e56cf3da" -uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.11.1" - - [deps.QuadGK.extensions] - QuadGKEnzymeExt = "Enzyme" - - [deps.QuadGK.weakdeps] - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - [[deps.Quaternions]] deps = ["LinearAlgebra", "Random", "RealDot"] git-tree-sha1 = "994cc27cdacca10e68feb291673ec3a76aa2fae9" @@ -1398,21 +763,6 @@ git-tree-sha1 = "c6ec94d2aaba1ab2ff983052cf6a606ca5985902" uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143" version = "1.6.0" -[[deps.RangeArrays]] -git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5" -uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d" -version = "0.3.2" - -[[deps.Ratios]] -deps = ["Requires"] -git-tree-sha1 = "1342a47bf3260ee108163042310d26f2be5ec90b" -uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439" -version = "0.4.5" -weakdeps = ["FixedPointNumbers"] - - [deps.Ratios.extensions] - RatiosFixedPointNumbersExt = "FixedPointNumbers" - [[deps.RealDot]] deps = ["LinearAlgebra"] git-tree-sha1 = "9f0a1b71baaf7650f4fa8a1d168c7fb6ee41f0c9" @@ -1430,30 +780,12 @@ git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" version = "1.2.2" -[[deps.RelocatableFolders]] -deps = ["SHA", "Scratch"] -git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" -uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" -version = "1.0.1" - [[deps.Requires]] deps = ["UUIDs"] git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" -[[deps.Rmath]] -deps = ["Random", "Rmath_jll"] -git-tree-sha1 = "852bd0f55565a9e973fcfee83a84413270224dc4" -uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" -version = "0.8.0" - -[[deps.Rmath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "58cdd8fb2201a6267e1db87ff148dd6c1dbd8ad8" -uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" -version = "0.5.1+0" - [[deps.Roots]] deps = ["Accessors", "ChainRulesCore", "CommonSolve", "Printf"] git-tree-sha1 = "48a7925c1d971b03bb81183b99d82c1dc7a3562f" @@ -1482,21 +814,10 @@ weakdeps = ["RecipesBase"] [deps.Rotations.extensions] RotationsRecipesBaseExt = "RecipesBase" -[[deps.RoundingEmulator]] -git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" -uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" -version = "0.2.1" - [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" version = "0.7.0" -[[deps.SIMD]] -deps = ["PrecompileTools"] -git-tree-sha1 = "98ca7c29edd6fc79cd74c61accb7010a4e7aee33" -uuid = "fdea26ae-647d-5447-a871-4b548cad5224" -version = "3.6.0" - [[deps.Scratch]] deps = ["Dates"] git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386" @@ -1517,45 +838,6 @@ version = "1.4.5" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -[[deps.SetRounding]] -git-tree-sha1 = "d7a25e439d07a17b7cdf97eecee504c50fedf5f6" -uuid = "3cc68bcd-71a2-5612-b932-767ffbe40ab0" -version = "0.2.1" - -[[deps.ShaderAbstractions]] -deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "79123bc60c5507f035e6d1d9e563bb2971954ec8" -uuid = "65257c39-d410-5151-9873-9b3e5be5013e" -version = "0.4.1" - -[[deps.SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" - -[[deps.Showoff]] -deps = ["Dates", "Grisu"] -git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" -uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" -version = "1.0.3" - -[[deps.SignedDistanceFields]] -deps = ["Random", "Statistics", "Test"] -git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9" -uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96" -version = "0.4.0" - -[[deps.SimpleTraits]] -deps = ["InteractiveUtils", "MacroTools"] -git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" -uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" -version = "0.9.4" - -[[deps.Sixel]] -deps = ["Dates", "FileIO", "ImageCore", "IndirectArrays", "OffsetArrays", "REPL", "libsixel_jll"] -git-tree-sha1 = "2da10356e31327c7096832eb9cd86307a50b1eb6" -uuid = "45858cf5-a6b0-47a3-bbea-62219f50df47" -version = "0.1.3" - [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" @@ -1570,22 +852,6 @@ deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" version = "1.10.0" -[[deps.SpecialFunctions]] -deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" -uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.4.0" -weakdeps = ["ChainRulesCore"] - - [deps.SpecialFunctions.extensions] - SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - -[[deps.StackViews]] -deps = ["OffsetArrays"] -git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c" -uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15" -version = "0.1.1" - [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" @@ -1613,23 +879,6 @@ git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" version = "1.7.0" -[[deps.StatsBase]] -deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21" -uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.34.3" - -[[deps.StatsFuns]] -deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] -git-tree-sha1 = "b423576adc27097764a90e163157bcfc9acf0f46" -uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" -version = "1.3.2" -weakdeps = ["ChainRulesCore", "InverseFunctions"] - - [deps.StatsFuns.extensions] - StatsFunsChainRulesCoreExt = "ChainRulesCore" - StatsFunsInverseFunctionsExt = "InverseFunctions" - [[deps.StringManipulation]] deps = ["PrecompileTools"] git-tree-sha1 = "a04cabe79c5f01f4d723cc6704070ada0b9d46d5" @@ -1649,10 +898,6 @@ weakdeps = ["Adapt", "GPUArraysCore", "SparseArrays", "StaticArrays"] StructArraysSparseArraysExt = "SparseArrays" StructArraysStaticArraysExt = "StaticArrays" -[[deps.SuiteSparse]] -deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] -uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" - [[deps.SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" @@ -1698,22 +943,10 @@ version = "0.17.8" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" -[[deps.TensorCore]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" -uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" -version = "0.1.1" - [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -[[deps.TiffImages]] -deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "SIMD", "UUIDs"] -git-tree-sha1 = "bc7fd5c91041f44636b2c134041f7e5263ce58ae" -uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69" -version = "0.10.0" - [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] git-tree-sha1 = "5a13ae8a41237cff5ecf34f73eb1b8f42fff6531" @@ -1725,11 +958,6 @@ git-tree-sha1 = "e84b3a11b9bece70d14cce63406bbc79ed3464d2" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" version = "0.11.2" -[[deps.TriplotBase]] -git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" -uuid = "981d1d27-644d-49a2-9326-4793e63143c3" -version = "0.1.0" - [[deps.UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" @@ -1737,23 +965,6 @@ uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -[[deps.UnicodeFun]] -deps = ["REPL"] -git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" -uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" -version = "0.4.1" - -[[deps.Unitful]] -deps = ["Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "d95fe458f26209c66a187b1114df96fd70839efd" -uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.21.0" -weakdeps = ["ConstructionBase", "InverseFunctions"] - - [deps.Unitful.extensions] - ConstructionBaseUnitfulExt = "ConstructionBase" - InverseFunctionsUnitfulExt = "InverseFunctions" - [[deps.UnsafeAtomics]] git-tree-sha1 = "6331ac3440856ea1988316b46045303bef658278" uuid = "013be700-e6cd-48c3-b4a1-df204f14c38f" @@ -1765,78 +976,18 @@ git-tree-sha1 = "2d17fabcd17e67d7625ce9c531fb9f40b7c42ce4" uuid = "d80eeb9a-aca5-4d75-85e5-170c8b632249" version = "0.2.1" -[[deps.WoodburyMatrices]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "c1a7aa6219628fcd757dede0ca95e245c5cd9511" -uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6" -version = "1.0.0" - [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] git-tree-sha1 = "1165b0443d0eca63ac1e32b8c0eb69ed2f4f8127" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" version = "2.13.3+0" -[[deps.XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" -uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.41+0" - [[deps.XZ_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "ac88fb95ae6447c8dda6a5503f3bafd496ae8632" uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" version = "5.4.6+0" -[[deps.Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" -uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.8.6+0" - -[[deps.Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" -uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.11+0" - -[[deps.Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" -uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.4+0" - -[[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" -uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.6+0" - -[[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" -uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.11+0" - -[[deps.Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" -uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.1+0" - -[[deps.Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" -uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.17.0+0" - -[[deps.Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" -uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.5.0+0" - [[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" @@ -1848,59 +999,17 @@ git-tree-sha1 = "e678132f07ddb5bfa46857f0d7620fb9be675d3b" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" version = "1.5.6+0" -[[deps.isoband_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "51b5eeb3f98367157a7a12a1fb0aa5328946c03c" -uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4" -version = "0.2.3+0" - [[deps.libaec_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" version = "1.1.2+0" -[[deps.libaom_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1827acba325fdcdf1d2647fc8d5301dd9ba43a9d" -uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" -version = "3.9.0+0" - -[[deps.libass_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "e17c115d55c5fbb7e52ebedb427a0dca79d4484e" -uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" -version = "0.15.2+0" - [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" version = "5.8.0+1" -[[deps.libfdk_aac_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8a22cf860a7d27e4f3498a0fe0811a7957badb38" -uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" -version = "2.0.3+0" - -[[deps.libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "d7015d2e18a5fd9a4f47de711837e980519781a4" -uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.43+1" - -[[deps.libsixel_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] -git-tree-sha1 = "d4f63314c8aa1e48cd22aa0c17ed76cd1ae48c3c" -uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" -version = "1.10.3+0" - -[[deps.libvorbis_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] -git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3" -uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.7+2" - [[deps.libzip_jll]] deps = ["Artifacts", "Bzip2_jll", "GnuTLS_jll", "JLLWrappers", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] git-tree-sha1 = "3282b7d16ae7ac3e57ec2f3fa8fafb564d8f9f7f" @@ -1922,15 +1031,3 @@ version = "2021.12.0+0" deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" version = "17.4.0+2" - -[[deps.x264_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "35976a1216d6c066ea32cba2150c4fa682b276fc" -uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" -version = "10164.0.0+0" - -[[deps.x265_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "dcc541bb19ed5b0ede95581fb2e41ecf179527d2" -uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" -version = "3.6.0+0" diff --git a/Project.toml b/Project.toml index c87bedccf..bf71dfbee 100644 --- a/Project.toml +++ b/Project.toml @@ -6,7 +6,6 @@ version = "0.11.1" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" -CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" GibbsSeaWater = "9a22fb26-0b63-4589-b28e-8f9d0b5c3d05" JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" From eb8beeae767e4646a191b9d8bc0dc8b457c7ac97 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Mon, 30 Sep 2024 10:08:08 +0100 Subject: [PATCH 27/33] typo --- docs/src/model_components/biogeochemical/PISCES/PISCES.md | 2 +- .../PISCES/particulate_organic_matter/two_size_class.jl | 2 +- test/dependencies_for_runtests.jl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/model_components/biogeochemical/PISCES/PISCES.md b/docs/src/model_components/biogeochemical/PISCES/PISCES.md index 748c872f8..3dbbba6bf 100644 --- a/docs/src/model_components/biogeochemical/PISCES/PISCES.md +++ b/docs/src/model_components/biogeochemical/PISCES/PISCES.md @@ -7,7 +7,7 @@ An overview of the model structure is available from the [PISCES community websi ![PISCES model structure](https://www.pisces-community.org/wp-content/uploads/2021/12/PISCES_Operational-1.png) -More documentation to follow..., for now see our list of key differences from [Aumont2015](@citet) and queries we have about the parameterisations on the [notable differences](@ref PISCES_queries). +More documentation to follow..., for now see our list of key differences from [Aumont2015](@citet) and queries we have about the parametrisations on the [notable differences](@ref PISCES_queries). ## Model conservation When the permanent scavenging of iron, and nitrogen fixation are turned off, PISCES conserves: diff --git a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl index 2822fc4c3..6d4a7eed3 100644 --- a/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl +++ b/src/Models/AdvectedPopulations/PISCES/particulate_organic_matter/two_size_class.jl @@ -83,7 +83,7 @@ end b = poc.temperature_sensetivity O₂ = @inbounds fields.O₂[i, j, k] - T = @inbounds fields.T[i, k, k] + T = @inbounds fields.T[i, j, k] ΔO₂ = anoxia_factor(bgc, O₂) diff --git a/test/dependencies_for_runtests.jl b/test/dependencies_for_runtests.jl index 6b6250ad8..56818e8c5 100644 --- a/test/dependencies_for_runtests.jl +++ b/test/dependencies_for_runtests.jl @@ -1,3 +1,3 @@ -using OceanBioME, Test, CUDA, Oceananigans, JLD2, Oceananigans.Units, Documenter +using OceanBioME, Test, CUDA, Oceananigans, JLD2, Oceananigans.Units#, Documenter architecture = CUDA.has_cuda() ? GPU() : CPU() \ No newline at end of file From 899aaf02f72c1557c5795806b85fa0b538720cc3 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Mon, 30 Sep 2024 12:09:37 +0100 Subject: [PATCH 28/33] fixed prescribed light for one field --- src/Light/prescribed.jl | 10 +++++++++- src/OceanBioME.jl | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Light/prescribed.jl b/src/Light/prescribed.jl index facbe682b..0146693cb 100644 --- a/src/Light/prescribed.jl +++ b/src/Light/prescribed.jl @@ -5,7 +5,7 @@ function maybe_named_fields(field) isa(field, AbstractField) || @warn "fields: $field is not an `AbstractField" - return ((:PAR, ), (field, )) + return NamedTuple{(:PAR, )}((field, )) end maybe_named_fields(fields::NamedTuple) = (keys(fields), values(fields)) @@ -26,6 +26,14 @@ one field is present the field will be named `PAR`. """ struct PrescribedPhotosyntheticallyActiveRadiation{F} fields :: F + + function PrescribedPhotosyntheticallyActiveRadiation(fields) + fields = maybe_named_fields(fields) + + F = typeof(fields) + + return new{F}(fields) + end end function update_biogeochemical_state!(model, PAR::PrescribedPhotosyntheticallyActiveRadiation) diff --git a/src/OceanBioME.jl b/src/OceanBioME.jl index 51323683a..375304cb6 100644 --- a/src/OceanBioME.jl +++ b/src/OceanBioME.jl @@ -123,7 +123,7 @@ required_biogeochemical_auxiliary_fields(bgc::CompleteBiogeochemistry) = require biogeochemical_drift_velocity(bgc::CompleteBiogeochemistry, val_name) = biogeochemical_drift_velocity(bgc.underlying_biogeochemistry, val_name) biogeochemical_auxiliary_fields(bgc::CompleteBiogeochemistry) = merge(biogeochemical_auxiliary_fields(bgc.underlying_biogeochemistry), - biogeochemical_auxiliary_fields(bgc.light_attenuation)) + biogeochemical_auxiliary_fields(bgc.light_attenuation)) @inline chlorophyll(bgc::CompleteBiogeochemistry, model) = chlorophyll(bgc.underlying_biogeochemistry, model) From 32025db3a0c38e58b86d211f9c10beec5d340be5 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Mon, 30 Sep 2024 12:09:56 +0100 Subject: [PATCH 29/33] fixed runtest deps --- test/dependencies_for_runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dependencies_for_runtests.jl b/test/dependencies_for_runtests.jl index 56818e8c5..6b6250ad8 100644 --- a/test/dependencies_for_runtests.jl +++ b/test/dependencies_for_runtests.jl @@ -1,3 +1,3 @@ -using OceanBioME, Test, CUDA, Oceananigans, JLD2, Oceananigans.Units#, Documenter +using OceanBioME, Test, CUDA, Oceananigans, JLD2, Oceananigans.Units, Documenter architecture = CUDA.has_cuda() ? GPU() : CPU() \ No newline at end of file From 9e9e8d1d7f420463ba0daed5d41602de005ed7e2 Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Mon, 30 Sep 2024 12:23:00 +0100 Subject: [PATCH 30/33] more light fix: --- src/Light/prescribed.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Light/prescribed.jl b/src/Light/prescribed.jl index 0146693cb..484e0b3fb 100644 --- a/src/Light/prescribed.jl +++ b/src/Light/prescribed.jl @@ -8,10 +8,8 @@ function maybe_named_fields(field) return NamedTuple{(:PAR, )}((field, )) end -maybe_named_fields(fields::NamedTuple) = (keys(fields), values(fields)) +maybe_named_fields(fields::NamedTuple) = fields -is_on_gpu(field) = isa(architecture(field), GPU) -is_on_gpu(::ConstantField) = false """ PrescribedPhotosyntheticallyActiveRadiation(fields) @@ -47,7 +45,7 @@ end summary(::PrescribedPhotosyntheticallyActiveRadiation) = string("Prescribed PAR") show(io::IO, model::PrescribedPhotosyntheticallyActiveRadiation{F}) where {F} = print(io, summary(model), "\n", " Fields:", "\n", - " └── $(model.field_names)") + " └── $(keys(model.fields))") biogeochemical_auxiliary_fields(par::PrescribedPhotosyntheticallyActiveRadiation) = par.fields From e2648424a3ebb4acafcce6ad9542bbd991ded97f Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Mon, 30 Sep 2024 13:43:25 +0100 Subject: [PATCH 31/33] fixed docs --- docs/src/appendix/library.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/src/appendix/library.md b/docs/src/appendix/library.md index 8a3451e3e..9e83cff12 100644 --- a/docs/src/appendix/library.md +++ b/docs/src/appendix/library.md @@ -27,6 +27,30 @@ Modules = [OceanBioME.Models.LOBSTERModel] Modules = [OceanBioME.Models.PISCESModel] ``` +```@autodocs +Modules = [OceanBioME.Models.PISCESModel.DissolvedOrganicCarbon] +``` + +```@autodocs +Modules = [OceanBioME.Models.PISCESModel.TwoCompartementCarbonIronParticles] +``` + +```@autodocs +Modules = [OceanBioME.Models.PISCESModel.SimpleIron] +``` + +```@autodocs +Modules = [OceanBioME.Models.PISCESModel.InorganicCarbon] +``` + +```@autodocs +Modules = [OceanBioME.Models.PISCESModel.QualityDependantZooplankton] +``` + +```@autodocs +Modules = [OceanBioME.Models.PISCESModel.NitrateAmmonia] +``` + ### Sugar kelp (Saccharina latissima) ```@autodocs From 9d8f1d1f990b904be7b073185934915a12d2e68e Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Tue, 1 Oct 2024 10:21:54 +0100 Subject: [PATCH 32/33] oops --- docs/src/appendix/library.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/src/appendix/library.md b/docs/src/appendix/library.md index 9e83cff12..d875119ff 100644 --- a/docs/src/appendix/library.md +++ b/docs/src/appendix/library.md @@ -28,27 +28,39 @@ Modules = [OceanBioME.Models.PISCESModel] ``` ```@autodocs -Modules = [OceanBioME.Models.PISCESModel.DissolvedOrganicCarbon] +Modules = [OceanBioME.Models.PISCESModel.DissolvedOrganicMatter] ``` ```@autodocs -Modules = [OceanBioME.Models.PISCESModel.TwoCompartementCarbonIronParticles] +Modules = [OceanBioME.Models.PISCESModel.ParticulateOrganicMatter] ``` ```@autodocs -Modules = [OceanBioME.Models.PISCESModel.SimpleIron] +Modules = [OceanBioME.Models.PISCESModel.Iron] ``` ```@autodocs -Modules = [OceanBioME.Models.PISCESModel.InorganicCarbon] +Modules = [OceanBioME.Models.PISCESModel.InorganicCarbons] ``` ```@autodocs -Modules = [OceanBioME.Models.PISCESModel.QualityDependantZooplankton] +Modules = [OceanBioME.Models.PISCESModel.Zooplankton] ``` ```@autodocs -Modules = [OceanBioME.Models.PISCESModel.NitrateAmmonia] +Modules = [OceanBioME.Models.PISCESModel.Phytoplankton] +``` + +```@autodocs +Modules = [OceanBioME.Models.PISCESModel.Phosphates] +``` + +```@autodocs +Modules = [OceanBioME.Models.PISCESModel.Silicates] +``` + +```@autodocs +Modules = [OceanBioME.Models.PISCESModel.Nitrogen] ``` ### Sugar kelp (Saccharina latissima) From 5c1b69208f8e5842fe5a5bb2038f0a58f6cb446b Mon Sep 17 00:00:00 2001 From: Jago Strong-Wright Date: Tue, 1 Oct 2024 12:02:17 +0100 Subject: [PATCH 33/33] minor change --- test/test_PISCES.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_PISCES.jl b/test/test_PISCES.jl index 98d3a2f62..e11f13462 100644 --- a/test/test_PISCES.jl +++ b/test/test_PISCES.jl @@ -30,7 +30,7 @@ end value(field; indices = (1, 1, 1)) = on_architecture(CPU(), interior(field, indices...))[1] function test_PISCES_conservation() # only on CPU please - @info "Testing PISCES element conservation (C, Fe, P, Si)" + @info "Testing PISCES element conservation (C, Fe, P, Si, N)" validation_warning = "This implementation of PISCES is in early development and has not yet been validated against the operational version"