|
| 1 | +using Oceananigans.OutputReaders: extract_field_time_series, update_field_time_series! |
| 2 | + |
| 3 | +import Oceananigans.TimeSteppers: time_step!, update_state!, reset!, tick! |
| 4 | +import Oceananigans.Models: timestepper, update_model_field_time_series! |
| 5 | + |
| 6 | +import ClimaOcean: reference_density, heat_capacity |
| 7 | +import Oceananigans.Architectures: on_architecture |
| 8 | + |
| 9 | +##### |
| 10 | +##### A prescribed ocean... |
| 11 | +##### |
| 12 | + |
| 13 | +struct PrescribedOcean{A, G, C, U, T, F} <: AbstractModel{Nothing} |
| 14 | + architecture :: A |
| 15 | + grid :: G |
| 16 | + clock :: Clock{C} |
| 17 | + velocities :: U |
| 18 | + tracers :: T |
| 19 | + timeseries :: F |
| 20 | +end |
| 21 | + |
| 22 | +function PrescribedOcean(timeseries; |
| 23 | + grid, |
| 24 | + clock=Clock{Float64}(time = 0)) |
| 25 | + |
| 26 | + # Make sure all elements of the timeseries are on the same grid |
| 27 | + for (k, v) in timeseries |
| 28 | + if !isa(v, FieldTimeSeries) |
| 29 | + throw(ArgumentError("All variables in the `timeseries` argument must be `FieldTimeSeries`")) |
| 30 | + end |
| 31 | + if v.grid != grid |
| 32 | + throw(ArgumentError("All variables in the timeseries reside on the provided grid")) |
| 33 | + end |
| 34 | + end |
| 35 | + |
| 36 | + τx = Field{Face, Center, Nothing}(grid) |
| 37 | + τy = Field{Center, Face, Nothing}(grid) |
| 38 | + Jᵀ = Field{Center, Center, Nothing}(grid) |
| 39 | + Jˢ = Field{Center, Center, Nothing}(grid) |
| 40 | + |
| 41 | + u = XFaceField(grid, boundary_conditions=FieldBoundaryConditions(grid, (Face, Center, Center), top = FluxBoundaryCondition(τx))) |
| 42 | + v = YFaceField(grid, boundary_conditions=FieldBoundaryConditions(grid, (Center, Face, Center), top = FluxBoundaryCondition(τy))) |
| 43 | + T = CenterField(grid, boundary_conditions=FieldBoundaryConditions(grid, (Center, Center, Center), top = FluxBoundaryCondition(Jᵀ))) |
| 44 | + S = CenterField(grid, boundary_conditions=FieldBoundaryConditions(grid, (Center, Center, Center), top = FluxBoundaryCondition(Jˢ))) |
| 45 | + |
| 46 | + PrescribedOcean(architecture(grid), grid, clock, (; u, v, w=ZeroField()), (; T, S), timeseries) |
| 47 | +end |
| 48 | + |
| 49 | +##### |
| 50 | +##### Need to extend a couple of methods |
| 51 | +##### |
| 52 | + |
| 53 | +function time_step!(model::PrescribedOcean, Δt; callbacks=[], euler=true) |
| 54 | + tick!(model.clock, Δt) |
| 55 | + time = Time(model.clock.time) |
| 56 | + |
| 57 | + possible_fts = merge(model.velocities, model.tracers) |
| 58 | + time_series_tuple = extract_field_time_series(possible_fts) |
| 59 | + |
| 60 | + for fts in time_series_tuple |
| 61 | + update_field_time_series!(fts, time) |
| 62 | + end |
| 63 | + |
| 64 | + update_u_velocity = haskey(model.timeseries, :u) |
| 65 | + update_v_velocity = haskey(model.timeseries, :v) |
| 66 | + update_temperature = haskey(model.timeseries, :T) |
| 67 | + update_salinity = haskey(model.timeseries, :S) |
| 68 | + |
| 69 | + update_u_velocity && iterpolate!(model.velocities.u, model.timeseries.u[time]) |
| 70 | + update_v_velocity && iterpolate!(model.velocities.v, model.timeseries.v[time]) |
| 71 | + update_temperature && iterpolate!(model.tracers.T, model.timeseries.T[time]) |
| 72 | + update_salinity && iterpolate!(model.tracers.S, model.timeseries.S[time]) |
| 73 | + |
| 74 | + return nothing |
| 75 | +end |
| 76 | + |
| 77 | +update_state!(::PrescribedOcean) = nothing |
| 78 | +timestepper(::PrescribedOcean) = nothing |
| 79 | + |
| 80 | +reference_density(ocean::Simulation{<:PrescribedOcean}) = 1025.6 |
| 81 | +heat_capacity(ocean::Simulation{<:PrescribedOcean}) = 3995.6 |
0 commit comments