diff --git a/src/GRHD/GRHD.jl b/src/GRHD/GRHD.jl index b4c8c6c7184b74cf33656da507779a7e68758dc3..f5c63495c8fd0233927c35c7f75f20d0e90f9d23 100644 --- a/src/GRHD/GRHD.jl +++ b/src/GRHD/GRHD.jl @@ -292,6 +292,18 @@ dg1d.@parameters GRHD begin """ freeze_vars = String[] + """ + List of 0d variables to analyze during simulations. + Variable names enequeued here become available for `Output.variables0d`. + + Available variables: + - `Mtot`: Total restmass density, Mtot = ∫ √(γ) D dΩ. + """ + variables0d_analyze = String[] + @check variables0d_analyze isa Vector{String} + @check allunique(variables0d_analyze) + @check variables0d_analyze ⊂ ["Mtot"] + """ If `true` then the doublecartoon derivative terms are not accounted for in the source terms. This appears to be necessary in order to perform TOV star simulations with the doublecartoon diff --git a/src/GRHD/callbacks.jl b/src/GRHD/callbacks.jl index 265baf3e6b354c206d2302dc0662506b9f9c44e1..1b302c14e9d2e0372c5e4956d3ec7f0a8bca77ba 100644 --- a/src/GRHD/callbacks.jl +++ b/src/GRHD/callbacks.jl @@ -21,6 +21,12 @@ function make_callback(env, P) CallbackTiming(every_iteration=1,every_dt=0)) push!(callbackset, cb) end + if !isempty(P.prmsdb["GRHD"]["variables0d_analyze"]) + cbfn_analyzed0d(u, t) = callback_analyze0d(env, P, env.mesh) + cb = FunctionCallback(cbfn_analyzed0d, + CallbackTiming(every_iteration=1,every_dt=0)) + push!(callbackset, cb) + end return callbackset end @@ -475,3 +481,22 @@ function callback_analyze1d_error(env, P, mesh::Mesh1d) @. v_err = v .- v_ref end end + + +callback_analyze0d(env, P::Project, mesh) = TODO() +function callback_analyze0d(env, P::Project{:spherical1d}, mesh::Mesh1d) + vars = P.prmsdb["GRHD"]["variables0d_analyze"] + if "Mtot" in vars + @unpack D = get_dynamic_variables(mesh) + @unpack γrr, γθθ, γϕϕ, r = get_static_variables(mesh) + @unpack Mtot = get_global_variables(mesh) + Mtot .= dg1d.integrate(mesh) do i + D[i] * sqrt(γrr[i]*γθθ[i]*γϕϕ[i]) * 4*Ï€ + end + if dg1d.is_domain_symmetric(mesh) + # in this case we integrated the sphere twice, so halving the result + # should correspond to an averaging of the integral from either side + Mtot ./= 2 + end + end +end diff --git a/src/GRHD/initialdata.jl b/src/GRHD/initialdata.jl index 5268e247b93031ee81b5fe5a7b38591c3e8f9a40..46a93144a13cc7162826b705470e7658b574042d 100644 --- a/src/GRHD/initialdata.jl +++ b/src/GRHD/initialdata.jl @@ -841,6 +841,16 @@ function initialdata_equation!(::Val{:tov}, env, P::Project{:spherical1d}, prms) @. init_Ï = Ï @. init_ϵ = ϵ + Mtot = dg1d.integrate(mesh) do i + v = D[i] * sqrt(γrr[i]*γθθ[i]*γϕϕ[i]) * 4*Ï€ + v + end + if dg1d.is_domain_symmetric(mesh) + Mtot /= 2 + end + Madm = data.M + @show Mtot, Madm + end diff --git a/src/GRHD/setup.jl b/src/GRHD/setup.jl index f7e18af8da12347f0aada5b49d4e41d879a5ba3c..6e85e21af62693f7a56d5caf4671bbee2d30f04b 100644 --- a/src/GRHD/setup.jl +++ b/src/GRHD/setup.jl @@ -481,6 +481,9 @@ function register_analysis!(mesh::Mesh1d, P) static_variablenames = (:c2p_reset_ϵ, :c2p_reset_atm, :c2p_limit_vr, :c2p_freeze_atm, :c2p_init_admissible, :v) ) + if "Mtot" in P.prmsdb["GRHD"]["variables0d_analyze"] + register_variables!(mesh, global_variablenames = (:Mtot,)) + end end function register_analysis!(mesh::Mesh2d, P) register_variables!(mesh, @@ -488,6 +491,10 @@ function register_analysis!(mesh::Mesh2d, P) :c2p_freeze_atm, :c2p_init_admissible, :v, :buf_c2p_reset_atm,) ) + if "Mtot" in P.prmsdb["GRHD"]["variables0d_analyze"] + TODO() + register_variables!(mesh, global_variablenames = (:Mtot,)) + end end diff --git a/src/glgl.jl b/src/glgl.jl index 11198584b2db11996305e5a93a3431157d656929..980b8e927c1335fa4312358a000efed04441618b 100644 --- a/src/glgl.jl +++ b/src/glgl.jl @@ -62,6 +62,20 @@ function integrate(w, v, D, f) return sum end +function integrate(w, v, D, fn::Function, idxs::AbstractVector{<:Integer}) + + N = length(w) + @toggled_assert (N,N) == size(D) + @toggled_assert N == length(idxs) + + sum = 0.0 + for i in 1:N + fi = fn(idxs[i]) + sum += (w[i] + v * (D[1,i] - D[N,i])) * fi + end + + return sum +end function integrate(w, v, D, f1, f2) diff --git a/src/lgl.jl b/src/lgl.jl index 4c009eacae8cbe851ec193a706a122d25951e97d..493d7f4b1f562d4a60b63f475d6479adacc0a0dc 100644 --- a/src/lgl.jl +++ b/src/lgl.jl @@ -30,6 +30,14 @@ function integrate(w, f) @toggled_assert length(w) == length(f) return dot(w, f) end +function integrate(w, f::Function, idxs::AbstractVector{<:Integer}) + @toggled_assert length(w) == length(idxs) + sum = 0.0 + for i in 1:length(w) + sum += w[i] * f(idxs[i]) + end + return sum +end function integrate(w, f, g) diff --git a/src/mesh.jl b/src/mesh.jl index c86989cfcd97a54c14d8c9a75156846fa497e028..75ed954155d9ccde2257617119d4c157466e19d9 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -9,6 +9,7 @@ abstract type AbstractMesh end struct Mesh{Element,N_Dim,N_Sides} <: AbstractMesh tree::Tree{N_Dim,N_Sides} # abstract representation of mesh boxes::Vector{Box{N_Dim}} # physical extends of each cell + # TODO Fix spelling extends -> extents extends::NTuple{N_Dim,Tuple{Float64,Float64}} # total extend of mesh element::Element cache::Cache @@ -286,6 +287,11 @@ function isperiodic(m::Mesh) return all(m.tree.periodic) end +is_domain_symmetric(m::Mesh) = TODO() +function is_domain_symmetric(m::Mesh1d) + (Lmin, Lmax), = m.extends + return Lmin ≈ -Lmax +end Base.broadcastable(mesh::Mesh) = Ref(mesh) @@ -389,6 +395,31 @@ function integrate_cell(u, k, mesh::Mesh2d) end +function integrate(fn::Function, mesh::Mesh1d{SpectralElement}) + @unpack invdetJ = get_static_variables(mesh) + Npts, K = layout(mesh) + mat_invdetJ = vreshape(invdetJ, (Npts,K)) + integral = 0.0 + for k in 1:K + idxs = cellindices(mesh, k) + integral += integrate(fn, idxs, mesh.element) / mat_invdetJ[first(idxs)] + end + return integral +end +function integrate(fn::Function, mesh::Mesh1d{FVElement}) + L, = widths(mesh) + K, = mesh.tree.dims + @assert K >= 2 + dL = L/K + integral = 0.0 + fk, fkp1 = fn(1), fn(2) + for k in 1:K-1 + integral += (fk+fkp1)*dL/2 + fk, fkp1 = fkp1, fn(k+1) + end + return integral +end + function integrate_fv_central(integrand::Function, δu, uavg, mesh::Mesh{<:FVElement}) # assume vanilla FV method so that solution in jth cell [x_(j-1/2),x_(j+1/2)] can # be approximated by diff --git a/src/output.jl b/src/output.jl index ee47f3830a26f0139da6e66f5d2b35db5df8e746..79150b42d7d0e804587814c744654b989bd259e1 100644 --- a/src/output.jl +++ b/src/output.jl @@ -61,7 +61,7 @@ Base.length(o::Output) = length(o.times) has_variable(o::Output, variable) = variable in o.variables -function _smart_read_variable!(storage, h5_data) +function _smart_read_variable!(storage::AbstractArray{<:Number}, h5_data) # figure out compatibility between storage and data size_data = size(h5_data) ndims_data = ndims(h5_data) @@ -131,7 +131,7 @@ Similar to `read_variable`, but instead read data into `storage`. This read is smart in the sense that it recognizes the shape of the `storage` and reads the data accordingly. """ -function read_variable!(storage, o::Output, variable::AbstractString, t::Real) +function read_variable!(storage::AbstractArray{<:Number}, o::Output, variable::AbstractString, t::Real) if !has_variable(o, variable) error("Output does not contain data for 'variable = $variable'") end @@ -153,7 +153,7 @@ Similar to `read_variable`, but instead read data into `storage`. This read is smart in the sense that it recognizes the shape of the `storage` and reads the data accordingly. """ -function read_variable!(storage, o::Output, variable::AbstractString, index_t::Int) +function read_variable!(storage::AbstractArray{<:Number}, o::Output, variable::AbstractString, index_t::Int) if !has_variable(o, variable) error("Output does not contain data for 'variable = $variable'") end @@ -165,3 +165,28 @@ function read_variable!(storage, o::Output, variable::AbstractString, index_t::I _smart_read_variable!(storage, h5_variable) return end + + +function read_variable(o::Output, variable::AbstractString) + var1 = read_variable(o, variable, 1) + N = length(o.times) + allvars = Vector{typeof(var1)}(undef, N) + allvars[1] = var1 + for i in 2:N + allvars[i] = read_variable(o, variable, i) + end + return allvars +end + + +function read_variable!(storage::AbstractVector{<:AbstractArray{<:Number}}, o::Output, + variable::AbstractString) + N = length(o.times) + if length(storage) != N + resize!(storage, N) + end + for i in 1:N + read_variable!(storage[i], o, variable, i) + end + return +end diff --git a/src/spectralelement.jl b/src/spectralelement.jl index 76649a7516baa4a0a839b8523f197d1cd82321b1..1fbbf23f6082e72dc20b1dd95e8386147b934756 100644 --- a/src/spectralelement.jl +++ b/src/spectralelement.jl @@ -111,6 +111,17 @@ end end +@inline function integrate(fn::Function, idxs::AbstractVector{<:Integer}, el::SpectralElement) + @unpack quadr = el + @toggled_assert quadr in (:LGL, :LGL_lumped, :GLGL) + if quadr === :LGL || quadr === :LGL_lumped + return LGL.integrate(el.w, fn, idxs) + else # quadr === :GLGL + return GLGL.integrate(el.w, el.v, el.D, fn, idxs) + end +end + + @inline function differentiate(u, el::SpectralElement) return el.D * u end diff --git a/test/IntegrationTests/refs/grhd_bondi_accretion/grhd_bondi_accretion.toml b/test/IntegrationTests/refs/grhd_bondi_accretion/grhd_bondi_accretion.toml index 27ba7b96e6919694fc235c27c8a248220c600d92..91c070ec89f1fbc498b14475f1b65d60f25c9d11 100644 --- a/test/IntegrationTests/refs/grhd_bondi_accretion/grhd_bondi_accretion.toml +++ b/test/IntegrationTests/refs/grhd_bondi_accretion/grhd_bondi_accretion.toml @@ -1,29 +1,30 @@ [EquationOfState] -eos = "polytrope" -polytrope_k = 0.02143872868864732 polytrope_gamma = "$(4/3)" +polytrope_k = 0.02143872868864732 +eos = "polytrope" + +[Evolution] +cfl = 0.2 +tend = 5.0 + +[Output] +variables0d = ["D_err_norm", "Mtot"] +aligned_ts = "$(collect(range(0.5,5.0,step=0.5)))" +variables1d = ["D_err", "D", "Sr", "Ï„", "p", "Ï", "ϵ", "vr"] [GRHD] -bc = "from_id" -id = "bondi_accretion" -id_filename = "$(joinpath(ROOTDIR,\"initialdata\",\"bondi_accretion.h5\"))" -c2p_enforce_causal_atm = false analyze_error_reference_solution = "bondi_accretion" +id_filename = "$(joinpath(ROOTDIR,\"initialdata\",\"bondi_accretion.h5\"))" +variables0d_analyze = ["Mtot"] +id = "bondi_accretion" variables0d_analyze_error = ["D"] variables1d_analyze_error = ["D"] +c2p_enforce_causal_atm = false +bc = "from_id" [Mesh] -range = [ 1.8, 10.0 ] -n = 4 +periodic = false +range = [1.8, 10.0] k = 35 basis = "lgl" -periodic = false - -[Output] -variables0d = [ "D_err_norm" ] -variables1d = [ "D_err", "D", "Sr", "Ï„", "p", "Ï", "ϵ", "vr" ] -aligned_ts = "$(collect(range(0.5,5.0,step=0.5)))" - -[Evolution] -cfl = 0.2 -tend = 5.0 +n = 4 diff --git a/test/IntegrationTests/refs/grhd_bondi_accretion/output0d.h5 b/test/IntegrationTests/refs/grhd_bondi_accretion/output0d.h5 index 9931e08123ca1eec7aeddf3d3b8406eaa683877c..2c258cd6e73e717289c01b4a4a953373f070538d 100644 Binary files a/test/IntegrationTests/refs/grhd_bondi_accretion/output0d.h5 and b/test/IntegrationTests/refs/grhd_bondi_accretion/output0d.h5 differ diff --git a/test/IntegrationTests/refs/grhd_bondi_accretion/output1d.h5 b/test/IntegrationTests/refs/grhd_bondi_accretion/output1d.h5 index 7219b39bc289421afdd21452654ec09cca851686..740e709f24c311ee88cb745632d6f3fb0f3db056 100644 Binary files a/test/IntegrationTests/refs/grhd_bondi_accretion/output1d.h5 and b/test/IntegrationTests/refs/grhd_bondi_accretion/output1d.h5 differ diff --git a/test/IntegrationTests/refs/testconfig.toml b/test/IntegrationTests/refs/testconfig.toml index 8ae0756116439d53d34c62f4b10071f7eb9b301b..5b254ec5ecda84e0d509d04a29d1ce52af4c0721 100644 --- a/test/IntegrationTests/refs/testconfig.toml +++ b/test/IntegrationTests/refs/testconfig.toml @@ -123,7 +123,7 @@ variables1d = [ "D", "S", "tau" ] reltol1d = 1e-6 [grhd_bondi_accretion] -variables0d = [ "D_err_norm" ] +variables0d = [ "D_err_norm", "Mtot" ] variables1d = [ "D_err", "D", "Sr", "Ï„", "p", "Ï", "ϵ", "vr" ] [grhd_bondi_infall_avmda]