From b926763389614902ed09a4c8a6c08c3fdae7716c Mon Sep 17 00:00:00 2001 From: Anchal Gupta Date: Wed, 7 Feb 2024 12:57:20 -0800 Subject: [PATCH] Adding check for duplicates in add_interferometer! This commit resolves https://github.com/ProjectTorreyPines/SynthDiag.jl/issues/17 add_interferometer! function has been modified to read the json file using JSON.parsefile directly and pass it to its method that takes as input a dictionary. Note that JSON.parsefile returns a Dict{String, Any} dictionary while OMAS.imas2dict constructs a Dict{Symbol, Any} dictionary. Thus after reading the josn file it is converted into a Dict{Symbol, Any}. Then a duplicacy check is run to ensure that new file has no channels that have an overlapping name or identifier. If a duplicate is found, an error is thrown and it recommends the user to use overwrite=true. In case of overwrite=true, the duplicate channels in existing ids are deletec and the new channels are added. This may change the indices of the channel in ids.interferometer.channel array. In case there is no duplicacy or overwrite=true with additional channels, the new channels are appended at the end of the ids.interferometer.channel array. Test cases have been expanded to cover the new functionality. --- Project.toml | 1 + samples/test_interferometer_new_channels.json | 35 ++++ samples/test_interferometer_same_names.json | 122 ++++++++++++ src/interferometer.jl | 85 ++++++++- test/runtests.jl | 180 ++++++++++++------ 5 files changed, 366 insertions(+), 57 deletions(-) create mode 100644 samples/test_interferometer_new_channels.json create mode 100644 samples/test_interferometer_same_names.json diff --git a/Project.toml b/Project.toml index 8e48643..bda4323 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,7 @@ authors = ["Anchal Gupta "] version = "0.2.0" [deps] +ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2" GGDUtils = "b7b5e640-9b39-4803-84eb-376048795def" OMAS = "91cfaa06-6526-4804-8666-b540b3feef2f" diff --git a/samples/test_interferometer_new_channels.json b/samples/test_interferometer_new_channels.json new file mode 100644 index 0000000..24aa631 --- /dev/null +++ b/samples/test_interferometer_new_channels.json @@ -0,0 +1,35 @@ +{ + "interferometer": { + "channel": [ + { + "name": "V1.5", + "identifier": "V1.5", + "line_of_sight": { + "first_point": { + "phi": 0.5, + "r": 5.5, + "z": -5.0 + }, + "second_point": { + "phi": 0.5, + "r": 5.5, + "z": 6.0 + }, + "third_point": { + "phi": 0.5, + "r": 5.5, + "z": -5.0 + } + }, + "wavelength": [ + { + "value": 10.6e-6 + }, + { + "value": 6.33e-7 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/samples/test_interferometer_same_names.json b/samples/test_interferometer_same_names.json new file mode 100644 index 0000000..33fb179 --- /dev/null +++ b/samples/test_interferometer_same_names.json @@ -0,0 +1,122 @@ +{ + "interferometer": { + "channel": [ + { + "name": "V1", + "identifier": "V1", + "line_of_sight": { + "first_point": { + "phi": 0.2, + "r": 5.2, + "z": -5.0 + }, + "second_point": { + "phi": 0.2, + "r": 5.2, + "z": 6.0 + }, + "third_point": { + "phi": 0.2, + "r": 5.2, + "z": -5.0 + } + }, + "wavelength": [ + { + "value": 10.6e-6 + }, + { + "value": 6.33e-7 + } + ] + }, + { + "name": "V2", + "identifier": "V2", + "line_of_sight": { + "first_point": { + "phi": 0.2, + "r": 6.2, + "z": -5.0 + }, + "second_point": { + "phi": 0.2, + "r": 6.2, + "z": 6.0 + }, + "third_point": { + "phi": 0.2, + "r": 6.2, + "z": -5.0 + } + }, + "wavelength": [ + { + "value": 10.6e-6 + }, + { + "value": 6.33e-7 + } + ] + }, + { + "name": "V3", + "identifier": "V3", + "line_of_sight": { + "first_point": { + "phi": 0.2, + "r": 7.2, + "z": -5.0 + }, + "second_point": { + "phi": 0.2, + "r": 7.2, + "z": 6.0 + }, + "third_point": { + "phi": 0.2, + "r": 7.2, + "z": -5.0 + } + }, + "wavelength": [ + { + "value": 10.6e-6 + }, + { + "value": 6.33e-7 + } + ] + }, + { + "name": "H1", + "identifier": "H1", + "line_of_sight": { + "first_point": { + "phi": 0.2, + "r": 9.2, + "z": 0.0 + }, + "second_point": { + "phi": 0.2, + "r": 3.2, + "z": 0.0 + }, + "third_point": { + "phi": 0.2, + "r": 9.2, + "z": 0.0 + } + }, + "wavelength": [ + { + "value": 10.6e-6 + }, + { + "value": 6.33e-7 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/interferometer.jl b/src/interferometer.jl index 0eb4b60..969df51 100644 --- a/src/interferometer.jl +++ b/src/interferometer.jl @@ -1,3 +1,35 @@ + +struct OverwriteAttemptError <: Exception + var::String +end + +Base.showerror(io::IO, e::OverwriteAttemptError) = print(io, e.var) + +function convert_strings_to_symbols(d::Dict{String, Any}) + new_d = Dict{Symbol, Any}() + for (k, v) ∈ d + if isa(v, Dict{String, Any}) + new_d[Symbol(k)] = convert_strings_to_symbols(v) + elseif isa(v, Vector{Any}) + if length(v) > 0 + if isa(v[1], Dict{String, Any}) + new_d[Symbol(k)] = Vector{Dict{Symbol, Any}}(undef, length(v)) + for ii ∈ eachindex(v) + new_d[Symbol(k)][ii] = convert_strings_to_symbols(v[ii]) + end + else + new_d[Symbol(k)] = v + end + else + new_d[Symbol(k)] = v + end + else + new_d[Symbol(k)] = v + end + end + return new_d +end + """ add_interferometer( @nospecialize(ids::OMAS.dd)=OMAS.dd(), @@ -10,10 +42,12 @@ line integrated electron density if not present """ function add_interferometer!( config::String=default_ifo, - @nospecialize(ids::OMAS.dd)=OMAS.dd(), + @nospecialize(ids::OMAS.dd)=OMAS.dd(); + overwrite::Bool=false, verbose::Bool=false, )::OMAS.dd if endswith(config, ".json") - OMAS.json2imas(config, ids) + config_dict = convert_strings_to_symbols(OMAS.JSON.parsefile(config)) + add_interferometer!(config_dict, ids; overwrite=overwrite, verbose=verbose) else error("Only JSON files are supported.") end @@ -33,9 +67,52 @@ electron density if not present """ function add_interferometer!( config::Dict{Symbol, Any}, - @nospecialize(ids::OMAS.dd)=OMAS.dd(), + @nospecialize(ids::OMAS.dd)=OMAS.dd(); + overwrite::Bool=false, verbose::Bool=false, )::OMAS.dd - OMAS.dict2imas(config, ids) + # Check for duplicates + if length(ids.interferometer.channel) > 0 + duplicate_indices = [] + new_channels = Dict( + ch[:name] => ch[:identifier] for + ch ∈ config[:interferometer][:channel] + ) + for (ii, ch) ∈ enumerate(ids.interferometer.channel) + if ch.name in keys(new_channels) || + ch.identifier in values(new_channels) + append!(duplicate_indices, ii) + end + end + if overwrite + for ii ∈ reverse(duplicate_indices) + println( + "Overwriting interferometer channel ", + "$(ids.interferometer.channel[ii].name)...", + ) + deleteat!(ids.interferometer.channel, ii) + end + else + if length(duplicate_indices) > 0 + err_msg = + "Duplicate interferometer channels found with " * + "overlapping names or identifiers.\n" * "Identifier: Name\n" + for ii ∈ duplicate_indices + err_msg *= + "$(ids.interferometer.channel[ii].identifier): " * + "$(ids.interferometer.channel[ii].name)\n" + end + err_msg *= "Use overwrite=true to replace them." + throw(OverwriteAttemptError(err_msg)) + end + end + config[:interferometer] = + mergewith( + append!, + OMAS.imas2dict(ids.interferometer), + config[:interferometer], + ) + end + OMAS.dict2imas(config, ids; verbose=verbose) compute_interferometer(ids) return ids end diff --git a/test/runtests.jl b/test/runtests.jl index c227035..e807a43 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,66 +1,140 @@ -using SynthDiag: add_interferometer!, add_langmuir_probes!, Noise +using SynthDiag: add_interferometer!, add_langmuir_probes!, Noise, OverwriteAttemptError using OMAS: json2imas using Test using Printf +using ArgParse: ArgParse -@testset "interferometer" begin - ids = - json2imas("$(@__DIR__)/../samples/time_dep_edge_profiles_with_equilibrium.json") - add_interferometer!("$(@__DIR__)/../src/default_interferometer.json", ids) - # Just checking if the function runs through for now - for ch ∈ ids.interferometer.channel - println() - println("-"^49) - println("-"^49) - println("Channel $(ch.name)") - println("-"^49) - @printf("|%15s|%15s|%15s|\n", "time", "int_n_e", "n_e_average") - println("-"^49) - for ii ∈ eachindex(ch.n_e_line.data) - @printf( - "|%15.3e|%15.3e|%15.3e|\n", - ch.n_e_line.time[ii], - ch.n_e_line.data[ii], - ch.n_e_line_average.data[ii] +function parse_commandline() + s = ArgParse.ArgParseSettings(; description="Run tests. Default is all tests.") + + ArgParse.add_arg_table!(s, + ["--interferometer"], + Dict(:help => "Test only interferometer", + :action => :store_true), + ["--langmuir_probes"], + Dict(:help => "Test only langmuir probes", + :action => :store_true), + ) + args = ArgParse.parse_args(s) + if !any(values(args)) # If no flags are set, run all tests + for k ∈ keys(args) + args[k] = true + end + end + return args +end +args = parse_commandline() + +if args["interferometer"] + @testset "interferometer" begin + ids = + json2imas( + "$(@__DIR__)/../samples/time_dep_edge_profiles_with_equilibrium.json", + ) + add_interferometer!("$(@__DIR__)/../src/default_interferometer.json", ids) + # Just checking if the function runs through for now + for ch ∈ ids.interferometer.channel + println() + println("-"^49) + println("-"^49) + println("Channel $(ch.name)") + println("-"^49) + @printf("|%15s|%15s|%15s|\n", "time", "int_n_e", "n_e_average") + println("-"^49) + for ii ∈ 1:20:length(ch.n_e_line.time) + @printf( + "|%15.3e|%15.3e|%15.3e|\n", + ch.n_e_line.time[ii], + ch.n_e_line.data[ii], + ch.n_e_line_average.data[ii] + ) + end + println("-"^49) + end + @test true + # Try overwriting the interferometer data + try + add_interferometer!( + "$(@__DIR__)/../samples/test_interferometer_same_names.json", + ids, ) + catch err + @test isa(err, OverwriteAttemptError) end - println("-"^49) + # Try overwriting the interferometer data with the overwrite flag + add_interferometer!( + "$(@__DIR__)/../samples/test_interferometer_same_names.json", + ids; + overwrite=true, + ) + @test ids.interferometer.channel[1].name == "V1" + @test ids.interferometer.channel[1].line_of_sight.first_point.r == 5.2 + @test ids.interferometer.channel[2].name == "V2" + @test ids.interferometer.channel[2].line_of_sight.first_point.r == 6.2 + # Try appending new interfermeter channels + add_interferometer!( + "$(@__DIR__)/../samples/test_interferometer_new_channels.json", ids; + ) + for ch ∈ ids.interferometer.channel + println() + println("-"^49) + println("-"^49) + println("Channel $(ch.name)") + println("-"^49) + @printf("|%15s|%15s|%15s|\n", "time", "int_n_e", "n_e_average") + println("-"^49) + for ii ∈ 1:20:length(ch.n_e_line.time) + @printf( + "|%15.3e|%15.3e|%15.3e|\n", + ch.n_e_line.time[ii], + ch.n_e_line.data[ii], + ch.n_e_line_average.data[ii] + ) + end + println("-"^49) + end + @test length(ids.interferometer.channel) == 5 + @test ids.interferometer.channel[5].name == "V1.5" + @test ids.interferometer.channel[5].line_of_sight.first_point.r == 5.5 end - @test true end -@testset "langmuir_probes" begin - ids = - json2imas("$(@__DIR__)/../samples/time_dep_edge_profiles_with_equilibrium.json") - # Assume a 5% noise level in ne values - ff = 0.0:0.1:1000 - df = ff[2] - ff[1] - lpf = [f < 10.0 ? 1.0 : 100.0 * f^(-2) for f ∈ ff] - ne_noise_power = 1e14 * df * lpf - ne_noise = Noise(ne_noise_power, ff) - add_langmuir_probes!( - "$(@__DIR__)/../src/default_langmuir_probes.json", - ids; - ne_noise=ne_noise, - ) - # Just checking if the function runs through for now - for lp ∈ ids.langmuir_probes.embedded - println() - println("-"^49) - println("-"^49) - println("Probe: $(lp.name)") - println("-"^49) - @printf("|%15s|%15s|%15s|\n", "time", "n_e", "t_e") - println("-"^49) - for ii ∈ eachindex(lp.time) - @printf( - "|%15.3e|%15.3e|%15.3e|\n", - lp.time[ii], - lp.n_e.data[ii], - lp.t_e.data[ii] +if args["langmuir_probes"] + @testset "langmuir_probes" begin + ids = + json2imas( + "$(@__DIR__)/../samples/time_dep_edge_profiles_with_equilibrium.json", ) + # Assume a 5% noise level in ne values + ff = 0.0:0.1:1000 + df = ff[2] - ff[1] + lpf = [f < 10.0 ? 1.0 : 100.0 * f^(-2) for f ∈ ff] + ne_noise_power = 1e14 * df * lpf + ne_noise = Noise(ne_noise_power, ff) + add_langmuir_probes!( + "$(@__DIR__)/../src/default_langmuir_probes.json", + ids; + ne_noise=ne_noise, + ) + # Just checking if the function runs through for now + for lp ∈ ids.langmuir_probes.embedded + println() + println("-"^49) + println("-"^49) + println("Probe: $(lp.name)") + println("-"^49) + @printf("|%15s|%15s|%15s|\n", "time", "n_e", "t_e") + println("-"^49) + for ii ∈ eachindex(lp.time) + @printf( + "|%15.3e|%15.3e|%15.3e|\n", + lp.time[ii], + lp.n_e.data[ii], + lp.t_e.data[ii] + ) + end + println("-"^49) end - println("-"^49) + @test true end - @test true end