Skip to content

Commit c539188

Browse files
Merge pull request #121 from ProjectTorreyPines/add_sanitize
Add sanitize
2 parents 14b581c + 06e98d9 commit c539188

File tree

11 files changed

+68
-169
lines changed

11 files changed

+68
-169
lines changed

src/expressions/dynamic.jl

Lines changed: 37 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,6 @@ dyexp["core_profiles.profiles_1d[:].j_tor"] =
118118
Jpar_2_Jtor(rho_tor_norm, profiles_1d.j_total, true, eqt)
119119
end
120120

121-
# core_profiles.vacuum_toroidal_field #
122-
123-
dyexp["core_profiles.vacuum_toroidal_field.b0"] =
124-
(time; dd, _...) -> vacuum_r0_b0_time(dd, time)[2]
125-
126-
dyexp["core_profiles.vacuum_toroidal_field.r0"] =
127-
(; dd, _...) -> vacuum_r0_b0_time(dd)[1]
128-
129121
# core_profiles.global_quantities #
130122

131123
dyexp["core_profiles.global_quantities.current_non_inductive"] =
@@ -153,13 +145,6 @@ dyexp["core_profiles.profiles_1d[:].time"] =
153145
#= ============ =#
154146
# core_transport #
155147
#= ============ =#
156-
dyexp["core_transport.vacuum_toroidal_field.b0"] =
157-
(time; dd, _...) -> vacuum_r0_b0_time(dd, time)[2]
158-
159-
dyexp["core_transport.vacuum_toroidal_field.r0"] =
160-
(; dd, _...) -> vacuum_r0_b0_time(dd)[1]
161-
162-
163148
dyexp["core_transport.model[:].profiles_1d[:].time"] =
164149
(; core_transport, profiles_1d_index, _...) -> begin
165150
return core_transport.time[profiles_1d_index]
@@ -168,12 +153,6 @@ dyexp["core_transport.model[:].profiles_1d[:].time"] =
168153
#= ========= =#
169154
# equilibrium #
170155
#= ========= =#
171-
# IMAS does not hold B0 information in a given time slice, but we can get that info from `B0=f/R0`
172-
# This trick propagates the B0 information to a time_slice even when that time_slice has not been initialized with profiles_1d data
173-
dyexp["equilibrium.time_slice[:].profiles_1d.f"] =
174-
(psi; equilibrium, time_slice_index, _...) ->
175-
(psi === missing ? [1] : ones(size(psi))) .* (equilibrium.vacuum_toroidal_field.b0[time_slice_index] * equilibrium.vacuum_toroidal_field.r0)
176-
177156
dyexp["equilibrium.time_slice[:].global_quantities.energy_mhd"] =
178157
(; time_slice, _...) -> 3 / 2 * integrate(time_slice.profiles_1d.volume, time_slice.profiles_1d.pressure)
179158

@@ -198,9 +177,15 @@ dyexp["equilibrium.time_slice[:].global_quantities.magnetic_axis.r"] =
198177
dyexp["equilibrium.time_slice[:].global_quantities.magnetic_axis.z"] =
199178
(; time_slice, _...) -> time_slice.profiles_1d.geometric_axis.z[1]
200179

180+
181+
dyexp["equilibrium.time_slice[:].global_quantities.vacuum_toroidal_field.b0"] =
182+
(; dd, time_slice, _...) -> get_time_array(dd.pulse_schedule.tf.b_field_tor_vacuum, :reference, time_slice.time, :linear)
183+
184+
dyexp["equilibrium.time_slice[:].global_quantities.vacuum_toroidal_field.r0"] =
185+
(; dd, _...) -> dd.pulse_schedule.tf.r0
186+
201187
dyexp["equilibrium.time_slice[:].global_quantities.magnetic_axis.b_field_tor"] =
202-
(; equilibrium, time_slice_index, _...) ->
203-
equilibrium.vacuum_toroidal_field.b0[time_slice_index] * equilibrium.vacuum_toroidal_field.r0 / equilibrium.time_slice[time_slice_index].boundary.geometric_axis.r
188+
(; time_slice, _...) -> time_slice.global_quantities.vacuum_toroidal_field.b0 * time_slice.global_quantities.vacuum_toroidal_field.r0 / time_slice.boundary.geometric_axis.r
204189

205190
dyexp["equilibrium.time_slice[:].profiles_1d.geometric_axis.r"] =
206191
(psi; time_slice, _...) -> (time_slice.profiles_1d.r_outboard .+ time_slice.profiles_1d.r_inboard) .* 0.5
@@ -209,10 +194,25 @@ dyexp["equilibrium.time_slice[:].profiles_1d.geometric_axis.z"] =
209194
(psi; time_slice, _...) -> psi .* 0.0 .+ time_slice.global_quantities.magnetic_axis.z
210195

211196
dyexp["equilibrium.time_slice[:].boundary.geometric_axis.r"] =
212-
(; time_slice, _...) -> time_slice.profiles_1d.geometric_axis.r[end]
197+
(; time_slice, _...) -> begin
198+
if !ismissing(time_slice.profiles_1d.geometric_axis, :r)
199+
return time_slice.profiles_1d.geometric_axis.r[end]
200+
else
201+
minR, maxR = extrema(time_slice.boundary.outline.r)
202+
return (minR + maxR) / 2
203+
end
204+
end
213205

214206
dyexp["equilibrium.time_slice[:].boundary.geometric_axis.z"] =
215-
(; time_slice, _...) -> time_slice.profiles_1d.geometric_axis.z[end]
207+
(; time_slice, _...) -> begin
208+
if !ismissing(time_slice.profiles_1d.geometric_axis, :z)
209+
return time_slice.profiles_1d.geometric_axis.z[end]
210+
else
211+
minZ, maxZ = extrema(time_slice.boundary.outline.z)
212+
return (minZ + maxZ) / 2
213+
end
214+
end
215+
216216

217217
dyexp["equilibrium.time_slice[:].boundary.minor_radius"] =
218218
(; time_slice, _...) -> (time_slice.profiles_1d.r_outboard[end] - time_slice.profiles_1d.r_inboard[end]) * 0.5
@@ -287,13 +287,6 @@ dyexp["equilibrium.time_slice[:].profiles_1d.dpsi_drho_tor"] =
287287
dyexp["equilibrium.time_slice[:].profiles_1d.psi_norm"] =
288288
(psi; _...) -> norm01(psi)
289289

290-
291-
dyexp["equilibrium.vacuum_toroidal_field.b0"] =
292-
(time; dd, _...) -> vacuum_r0_b0_time(dd, time)[2]
293-
294-
dyexp["equilibrium.vacuum_toroidal_field.r0"] =
295-
(; dd, _...) -> vacuum_r0_b0_time(dd)[1]
296-
297290
# 2D
298291
dyexp["equilibrium.time_slice[:].profiles_2d[:].r"] =
299292
(dim1, dim2; _...) -> ones(length(dim2))' .* dim1
@@ -412,14 +405,6 @@ dyexp["core_sources.source[:].profiles_1d[:].ion[:].particles"] =
412405
gradient(profiles_1d.grid.volume, ion.particles_inside)
413406
end
414407

415-
416-
dyexp["core_sources.vacuum_toroidal_field.b0"] =
417-
(time; dd, _...) -> vacuum_r0_b0_time(dd, time)[2]
418-
419-
dyexp["core_sources.vacuum_toroidal_field.r0"] =
420-
(; dd, _...) -> vacuum_r0_b0_time(dd)[1]
421-
422-
423408
dyexp["core_sources.source[:].profiles_1d[:].time"] =
424409
(; core_sources, profiles_1d_index, _...) -> begin
425410
return core_sources.time[profiles_1d_index]
@@ -587,6 +572,18 @@ dyexp["pulse_schedule.time"] =
587572
return sort!(unique(all_times))
588573
end
589574

575+
dyexp["pulse_schedule.tf.b_field_tor_vacuum_r.reference"] =
576+
(time; tf, _...) -> tf.r0 .* tf.b_field_tor_vacuum.reference
577+
578+
dyexp["pulse_schedule.tf.r0"] =
579+
(; dd, _...) -> dd.equilibrium.vacuum_toroidal_field.r0
580+
581+
dyexp["pulse_schedule.tf.b_field_tor_vacuum.reference"] =
582+
(time; dd, _...) -> dd.equilibrium.vacuum_toroidal_field.b0
583+
584+
dyexp["pulse_schedule.tf.time"] =
585+
(time; dd, _...) -> dd.equilibrium.time
586+
590587
#= ========= =#
591588
# stability #
592589
#= ========= =#
@@ -617,12 +614,6 @@ dyexp["summary.fusion.power.value"] = # NOTE: This is the fusion power that is c
617614
dyexp["summary.global_quantities.ip.value"] =
618615
(time; dd, summary, _...) -> [dd.equilibrium.time_slice[Float64(time0)].global_quantities.ip for time0 in time]
619616

620-
dyexp["summary.global_quantities.b0.value"] =
621-
(time; dd, _...) -> vacuum_r0_b0_time(dd, time)[2]
622-
623-
dyexp["summary.global_quantities.r0.value"] =
624-
(; dd, summary, _...) -> vacuum_r0_b0_time(dd)[1]
625-
626617
dyexp["summary.global_quantities.current_bootstrap.value"] =
627618
(time; dd, summary, _...) -> begin
628619
tmp = eltype(summary)[]

src/extract.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function update_ExtractFunctionsLibrary!()
3434
ExtractLibFunction(:geometry, :Volume, "", dd -> dd.equilibrium.time_slice[].profiles_1d.volume[end])
3535
ExtractLibFunction(:geometry, :Surface, "", dd -> dd.equilibrium.time_slice[].profiles_1d.surface[end])
3636

37-
ExtractLibFunction(:equilibrium, :B0, "T", dd -> @ddtime(dd.summary.global_quantities.b0.value))
37+
ExtractLibFunction(:equilibrium, :B0, "T", dd -> dd.equilibrium.time_slice[].global_quantities.vacuum_toroidal_field.b0)
3838
ExtractLibFunction(:equilibrium, :ip, "MA", dd -> @ddtime(dd.summary.global_quantities.ip.value) / 1e6)
3939
ExtractLibFunction(:equilibrium, :q95, "-", dd -> dd.equilibrium.time_slice[].global_quantities.q_95)
4040
ExtractLibFunction(:equilibrium, Symbol("<Bpol>"), "T", dd -> IMAS.Bpol(EFL[:a](dd), EFL[](dd), EFL[:ip](dd) * 1e6))

src/get_from.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ function get_from(dd::IMAS.dd{T}, what::Type{Val{:ip}}, from_where::Symbol; time
1717
end
1818
end
1919

20+
# vacuum_r0_b0 [m], [T]
21+
function get_from(dd::IMAS.dd, what::Type{Val{:vacuum_r0_b0}}, from_where::Symbol; time0::Float64=dd.global_time)
22+
if from_where == :equilibrium
23+
eqt = dd.equilibrium.time_slice[time0]
24+
return (r0=eqt.global_quantities.vacuum_toroidal_field.r0, b0=eqt.global_quantities.vacuum_toroidal_field.b0)
25+
elseif from_where == :pulse_schedule
26+
return (r0=dd.pulse_schedule.tf.r0, b0=IMAS.get_time_array(dd.pulse_schedule.tf.b_field_tor_vacuum, :reference, time0, :linear))
27+
else
28+
error("`get_from(dd, $what, $from_where)` doesn't exist yet")
29+
end
30+
end
31+
2032
# vloop [V]
2133
function get_from(dd::IMAS.dd{T}, what::Type{Val{:vloop}}, from_where::Symbol; time0::Float64=dd.global_time)::T where {T<:Real}
2234
if from_where == :equilibrium

src/physics/boundary.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,16 +256,17 @@ function boundary_shape(;
256256
z = vcat(z1, z2, z3, z4)
257257
zref = vcat(z1ref, z2ref, z3ref, z4ref)
258258

259-
return r, z, zref
259+
return (r=r, z=z, zref=zref)
260260
end
261261

262-
function boundary(pc::IMAS.pulse_schedule__position_control, time0::Float64)
263-
return [extrap1d(interp1d_itp(pc.time, pcb.r.reference); first=:flat, last=:flat).(time0) for pcb in pc.boundary_outline],
264-
[extrap1d(interp1d_itp(pc.time, pcb.z.reference); first=:flat, last=:flat).(time0) for pcb in pc.boundary_outline]
262+
function boundary(pc::IMAS.pulse_schedule__position_control{T}, time0::Float64) where {T<:Real}
263+
return (r=T[extrap1d(interp1d_itp(pc.time, pcb.r.reference); first=:flat, last=:flat).(time0) for pcb in pc.boundary_outline],
264+
z=T[extrap1d(interp1d_itp(pc.time, pcb.z.reference); first=:flat, last=:flat).(time0) for pcb in pc.boundary_outline])
265265
end
266266

267-
function boundary(pc::IMAS.pulse_schedule__position_control, time_index::Int)
268-
return [pcb.r.reference[time_index] for pcb in pc.boundary_outline], [pcb.z.reference[time_index] for pcb in pc.boundary_outline]
267+
function boundary(pc::IMAS.pulse_schedule__position_control{T}, time_index::Int) where {T<:Real}
268+
return (r=T[pcb.r.reference[time_index] for pcb in pc.boundary_outline],
269+
z=T[pcb.z.reference[time_index] for pcb in pc.boundary_outline])
269270
end
270271

271272
"""

src/physics/currents.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ function vloop(cp1d::IMAS.core_profiles__profiles_1d{T}, eqt::IMAS.equilibrium__
142142
rho_eq = eqt.profiles_1d.rho_tor_norm
143143
F = IMAS.interp1d(rho_eq, eqt.profiles_1d.f, :cubic).(rho_tor_norm)
144144
gm1 = IMAS.interp1d(rho_eq, eqt.profiles_1d.gm1, :cubic).(rho_tor_norm) # <R⁻²>
145-
_, B0 = IMAS.vacuum_r0_b0(eqt)
145+
_, B0 = eqt.global_quantities.vacuum_toroidal_field.r0, eqt.global_quantities.vacuum_toroidal_field.b0
146146
Vls = 2π .* cp1d.j_ohmic .* B0 ./ (cp1d.conductivity_parallel .* F .* gm1)
147147
if method === :area
148148
return integrate(cp1d.grid.area, Vls) / cp1d.grid.area[end]

src/physics/equilibrium.jl

Lines changed: 1 addition & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -123,119 +123,13 @@ function symmetrize_equilibrium!(eqt::IMAS.equilibrium__time_slice)
123123
tweak_psi_to_match_psilcfs!(eqt)
124124
end
125125

126-
"""
127-
vacuum_r0_b0(eqt::IMAS.equilibrium__time_slice{T})::Tuple{T,T} where {T<:Real}
128-
129-
Returns vacuum R0 and B0 of a given equilibrium time slice
130-
"""
131-
function vacuum_r0_b0(eqt::IMAS.equilibrium__time_slice{T})::Tuple{T,T} where {T<:Real}
132-
eq = top_ids(eqt)
133-
if eq !== nothing && !ismissing(eq.vacuum_toroidal_field, :r0) && !ismissing(eq.vacuum_toroidal_field, :b0)
134-
R0 = eq.vacuum_toroidal_field.r0
135-
B0 = get_time_array(eq.vacuum_toroidal_field, :b0, eqt.time)
136-
else
137-
R0 = eqt.boundary.geometric_axis.r
138-
B0 = eqt.profiles_1d.f[end] / R0
139-
end
140-
return R0, B0
141-
end
142-
143-
"""
144-
vacuum_r0_b0_time(dd::IMAS.dd{T}) where {T<:Real}
145-
146-
Returns `R0` as well as the `B0` and `time` arrays.
147-
148-
This function solves the issue that in IMAS the information about R0 and B0 is replicated across different IDSs.
149-
"""
150-
function vacuum_r0_b0_time(dd::IMAS.dd{T}) where {T<:Real}
151-
source = Set{Symbol}()
152-
153-
idss_with_vacuum_toroidal_field = keys(dd)
154-
155-
# R0
156-
if hasfield(typeof(dd), :tf) && hasdata(dd.tf, :r0)
157-
R0 = dd.tf.r0::T
158-
push!(source, :tf)
159-
else
160-
for name in idss_with_vacuum_toroidal_field
161-
ids = getfield(dd, name)
162-
if hasfield(typeof(ids), :vacuum_toroidal_field)
163-
if hasdata(ids.vacuum_toroidal_field, :r0)
164-
R0 = ids.vacuum_toroidal_field.r0::T
165-
push!(source, name)
166-
break
167-
end
168-
end
169-
end
170-
end
171-
172-
# B0 and time vectors
173-
# from: tf
174-
if hasfield(typeof(dd), :tf) && hasdata(dd.tf.b_field_tor_vacuum_r, :data)
175-
B0 = dd.tf.b_field_tor_vacuum_r.data::Vector{T} / R0
176-
time = dd.tf.b_field_tor_vacuum_r.time::Vector{Float64}
177-
push!(source, :tf)
178-
179-
# from: pulse_schedule
180-
elseif hasdata(dd.pulse_schedule.tf.b_field_tor_vacuum_r, :reference)
181-
B0 = dd.pulse_schedule.tf.b_field_tor_vacuum_r.reference::Vector{T} / R0
182-
time = dd.pulse_schedule.tf.time::Vector{Float64}
183-
push!(source, :pulse_schedule)
184-
185-
# from: all other IDSs that have that info
186-
else
187-
B0 = eltype(dd)[]
188-
time = Float64[]
189-
for name in idss_with_vacuum_toroidal_field
190-
ids = getfield(dd, name)
191-
if hasfield(typeof(ids), :vacuum_toroidal_field)
192-
if hasdata(ids.vacuum_toroidal_field, :b0) && hasdata(ids, :time)
193-
B0_ = ids.vacuum_toroidal_field.b0::Vector{T}
194-
time_ = ids.time::Vector{Float64}
195-
append!(time, time_)
196-
append!(B0, B0_)
197-
reps = length(time_) - length(B0_)
198-
append!(B0, (B0_[end] for k in 1:reps))
199-
push!(source, name)
200-
end
201-
end
202-
end
203-
index = unique_indices(time)
204-
B0 = B0[index]
205-
time = time[index]
206-
end
207-
208-
return R0, B0, time, source
209-
end
210-
211-
"""
212-
vacuum_r0_b0_time(dd::IMAS.dd, time::Vector{Float64})
213-
214-
Returns R0 and B0 interpolated at a given set of times
215-
"""
216-
function vacuum_r0_b0_time(dd::IMAS.dd, time::Vector{Float64})
217-
R0, B0_, time_, source = vacuum_r0_b0_time(dd)
218-
B0 = extrap1d(interp1d_itp(time_, B0_); first=:flat, last=:flat).(time)
219-
return R0, B0
220-
end
221-
222-
"""
223-
vacuum_r0_b0_time(dd::IMAS.dd{T}, time0::Float64)::Tuple{T,T}
224-
225-
Returns R0 and B0 interpolated at one time
226-
"""
227-
function vacuum_r0_b0_time(dd::IMAS.dd{T}, time0::Float64)::Tuple{T,T} where{T<:Real}
228-
R0, B0 = vacuum_r0_b0_time(dd, [time0])
229-
return R0, B0[1]
230-
end
231-
232126
"""
233127
B0_geo(eqt::IMAS.equilibrium__time_slice{T})::T where{T<:Real}
234128
235129
Returns vacuum B0 at the plasma geometric center
236130
"""
237131
function B0_geo(eqt::IMAS.equilibrium__time_slice{T})::T where{T<:Real}
238-
R0, B0 = vacuum_r0_b0(eqt)
132+
R0, B0 = eqt.global_quantities.vacuum_toroidal_field.r0, eqt.global_quantities.vacuum_toroidal_field.b0
239133
Rgeo = eqt.boundary.geometric_axis.r
240134
return R0 .* B0 ./ Rgeo
241135
end

src/physics/fluxsurfaces.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ Update flux surface averaged and geometric quantities for a given equilibrum IDS
649649
The original psi grid can be upsampled by a `upsample_factor` to get higher resolution flux surfaces
650650
"""
651651
function flux_surfaces(eqt::equilibrium__time_slice; upsample_factor::Int=1)
652-
R0, B0 = vacuum_r0_b0(eqt)
652+
R0, B0 = eqt.global_quantities.vacuum_toroidal_field.r0, eqt.global_quantities.vacuum_toroidal_field.b0
653653
return flux_surfaces(eqt, B0, R0; upsample_factor)
654654
end
655655

src/physics/pf_active.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function set_coils_function(coils::IDSvector{<:IMAS.pf_active__coil})
5252
geometry_types = name_2_index(coils[1].element[1].geometry)
5353
for coil in coils
5454
for element in coil.element
55-
for geometry_type in keys(element.geometry)
55+
for geometry_type in (:outline, :rectangle, :oblique)
5656
if geometry_type != :geometry_type && !ismissing(getproperty(element.geometry, geometry_type), :r)
5757
element.geometry.geometry_type = geometry_types[geometry_type]
5858
break

src/physics/profiles.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ function tau_e_h98(eqt::IMAS.equilibrium__time_slice, cp1d::IMAS.core_profiles__
220220
integrate(cp1d.grid.volume, sum(ion.density .* ion.element[1].a for ion in cp1d.ion if ion.element[1].z_n == 1.0)) /
221221
integrate(cp1d.grid.volume, sum(ion.density for ion in cp1d.ion if ion.element[1].z_n == 1.0))
222222

223-
R0, B0 = vacuum_r0_b0(eqt)
223+
R0, B0 = eqt.global_quantities.vacuum_toroidal_field.r0, eqt.global_quantities.vacuum_toroidal_field.b0
224224

225225
tau98 = (
226226
0.0562 *
@@ -255,7 +255,7 @@ function tau_e_ds03(eqt::IMAS.equilibrium__time_slice, cp1d::IMAS.core_profiles_
255255
integrate(cp1d.grid.volume, sum(ion.density .* ion.element[1].a for ion in cp1d.ion if ion.element[1].z_n == 1.0)) /
256256
integrate(cp1d.grid.volume, sum(ion.density for ion in cp1d.ion if ion.element[1].z_n == 1.0))
257257

258-
R0, B0 = vacuum_r0_b0(eqt)
258+
R0, B0 = eqt.global_quantities.vacuum_toroidal_field.r0, eqt.global_quantities.vacuum_toroidal_field.b0
259259

260260
tauds03 = (
261261
0.028 *

src/physics/sol.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ function sol(eqt::IMAS.equilibrium__time_slice, wall_r::Vector{T}, wall_z::Vecto
133133
end
134134

135135
############
136-
R0, B0 = vacuum_r0_b0(eqt)
136+
R0, B0 = eqt.global_quantities.vacuum_toroidal_field.r0, eqt.global_quantities.vacuum_toroidal_field.b0
137137
RA = eqt.global_quantities.magnetic_axis.r
138138
ZA = eqt.global_quantities.magnetic_axis.z
139139

@@ -541,7 +541,7 @@ Parallel heat flux [W/m^2] at the outer midplane based on Eigh λ_q
541541
"""
542542
function q_par_omp_eich(eqt::IMAS.equilibrium__time_slice, cp1d::IMAS.core_profiles__profiles_1d, core_sources::IMAS.core_sources)
543543
eq1d = eqt.profiles_1d
544-
R0, B0 = vacuum_r0_b0(eqt)
544+
R0, B0 = eqt.global_quantities.vacuum_toroidal_field.r0, eqt.global_quantities.vacuum_toroidal_field.b0
545545
R_omp = eq1d.r_outboard[end]
546546
Bt_omp = B0 * R0 / R_omp
547547
return q_pol_omp_eich(eqt, cp1d, core_sources) / sin(atan(Bpol_omp(eqt) / Bt_omp))

0 commit comments

Comments
 (0)