gs2crmod 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.document +5 -0
  2. data/Gemfile +13 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.md +4 -0
  5. data/README.rdoc +19 -0
  6. data/Rakefile +56 -0
  7. data/VERSION +1 -0
  8. data/ext/extconf.rb +9 -0
  9. data/ext/gs2crmod_ext.c +366 -0
  10. data/gs2crmod.gemspec +98 -0
  11. data/include/gs2crmod_ext.h +58 -0
  12. data/lib/gs2crmod/astrogk/astrogk.rb +201 -0
  13. data/lib/gs2crmod/astrogk/calculations.rb +57 -0
  14. data/lib/gs2crmod/astrogk/check_convergence.rb +7 -0
  15. data/lib/gs2crmod/astrogk/deleted_variables.rb +76 -0
  16. data/lib/gs2crmod/astrogk/graphs.rb +13 -0
  17. data/lib/gs2crmod/astrogk/gsl_data.rb +13 -0
  18. data/lib/gs2crmod/astrogk/gsl_tools.rb +182 -0
  19. data/lib/gs2crmod/astrogk/ingen.rb +18 -0
  20. data/lib/gs2crmod/astrogk/input_file_tools.rb +7 -0
  21. data/lib/gs2crmod/astrogk/namelist_tools.rb +14 -0
  22. data/lib/gs2crmod/astrogk/namelists.rb +2800 -0
  23. data/lib/gs2crmod/astrogk/properties.rb +17 -0
  24. data/lib/gs2crmod/astrogk/species_dependent_namelists.rb +228 -0
  25. data/lib/gs2crmod/astrogk/test_gs2.rb +231 -0
  26. data/lib/gs2crmod/astrogk.rb +200 -0
  27. data/lib/gs2crmod/calculations.rb +780 -0
  28. data/lib/gs2crmod/check_convergence.rb +179 -0
  29. data/lib/gs2crmod/deleted_variables.rb +916 -0
  30. data/lib/gs2crmod/graphs.rb +1899 -0
  31. data/lib/gs2crmod/graphs_rdoc.rb +556 -0
  32. data/lib/gs2crmod/gs2.rb +1143 -0
  33. data/lib/gs2crmod/gsl_data.rb +1181 -0
  34. data/lib/gs2crmod/gsl_data_3d.rb +705 -0
  35. data/lib/gs2crmod/gsl_tools.rb +187 -0
  36. data/lib/gs2crmod/ingen.rb +218 -0
  37. data/lib/gs2crmod/namelists.rb +5142 -0
  38. data/lib/gs2crmod/properties.rb +22 -0
  39. data/lib/gs2crmod/species_dependent_namelists.rb +228 -0
  40. data/lib/gs2crmod/test_gs2.rb +231 -0
  41. data/lib/gs2crmod.rb +2 -0
  42. data/lib/gs2crmod_extension.rb +1 -0
  43. data/test/helper.rb +18 -0
  44. data/test/test_gs2crmod.rb +7 -0
  45. metadata +176 -0
@@ -0,0 +1,1143 @@
1
+ ##########################################
2
+ # = Code Runner GS2 Module
3
+ ##########################################
4
+ #
5
+ # Authors: Edmund Highcock
6
+ # Copyright: 2009 Edmund Highcock
7
+ #
8
+ # This is free software released under the GPL v3
9
+ #
10
+ # This module allows easy running of the plasma turbulence simulation code gs2 using Code Runner, by automatically organising, naming and submitting runs, and analysing the run data.
11
+ #
12
+ # See Code Runner documentation, or documentation for individual methods.
13
+ #
14
+ # Notes
15
+ #
16
+ # index variables, e.g. kx_index, ky_index etc always refer to the 1-based Fortran index, to keep correspondance with the gs2 indices. Element variables, e.g. kx_element, always refer to the 0-based C/ruby index
17
+ #
18
+ # raw NumRu::NetCDF grids are in Fortran row-major order. This means that when you access grids using the NetCDF function NetCDF#get, you must specify the indices in fortran order (but 0-based!). The NetCDF#get function then returns a C-like NArray with the indices in the opposite order. You can convert this to a Ruby Array using the method NArray#to_a (the indices will still be in the same order).
19
+
20
+
21
+ #
22
+
23
+ begin
24
+ require "numru/netcdf"
25
+ rescue LoadError
26
+ eputs "Error: No NetCDF: data analysis for gs2 not possible"
27
+ end
28
+
29
+
30
+ class CodeRunner
31
+
32
+
33
+
34
+ # This is a customised subclass of CodeRunner::Run which allows CodeRunner to submit and analyse simulations from the gyrokinetic flux tube code GS2, which is principally used for simulating plasmas in magnetic confinement fusion.
35
+ #
36
+ # It performs two distinct roles: submitting simulations and analysing the data.
37
+ #
38
+ # = Submitting Simulations
39
+ #
40
+ # This principally involves generating the input file, which is a very nontrivial task. In order to do this, it maintains a complete record of every possible input parameter for GS2, as well as what datatype that parameter is, and sometimes what values it is allowed to take. This allows that not only to generate the input file, but to check that the input file makes sense. However, although generating the input file works beautifully, the set of sanity checks that it makes is not exhaustive: intelligent use is still required!
41
+ #
42
+ # In tandem with this, it maintains a whole set of tools for manipulating its database of input parameters. This includes updating their allowed values and also editing and accessing help for every input parameter.
43
+ #
44
+ # = Analysing Simulations
45
+ #
46
+ # The amount of analysis possible on GS2 data is enormous, and CodeRunner hasn't got close to getting there. What it can do is:
47
+ #
48
+ # * Check if the run is complete by comparing the number of completed timesteps against nstep
49
+ # * Calculate growth rates for linear runs.
50
+ # * Check if non-linear runs have saturated and calculate fluxes for those runs.
51
+ # * Automatically plot a huge variety of different graphs, ranging from simple plots of heat flux versus time to three-dimensional plots of the spectrum and potential.
52
+
53
+ class Gs2 < Run::FortranNamelist
54
+
55
+ GS2_CRMOD_VERSION = Version.new(Gem.loaded_specs['gs2crmod'].version.to_s)
56
+
57
+
58
+ def agk?
59
+ false
60
+ end
61
+
62
+ MODULE_FOLDER = File.dirname(File.expand_path(__FILE__))
63
+
64
+ # Include the other files
65
+ @code_module_folder = folder = File.dirname(File.expand_path(__FILE__)) # i.e. the directory this file is in
66
+ setup_namelists(folder)
67
+ require folder + '/graphs.rb'
68
+ require folder + '/gsl_data.rb'
69
+ require folder + '/gsl_data_3d.rb'
70
+ require folder + '/check_convergence.rb'
71
+ require folder + '/calculations.rb'
72
+ require folder + '/ingen.rb'
73
+ require folder + '/properties.rb'
74
+ require folder + '/test_gs2.rb'
75
+
76
+ NaN = GSL::NAN
77
+ # GSL::Neg
78
+
79
+
80
+ def code_run_environment
81
+ case CodeRunner::SYS
82
+ when /helios/
83
+ <<EOF
84
+ module load intel
85
+ module load bullxmpi
86
+ module load netcdf
87
+ EOF
88
+ else
89
+ ""
90
+ end
91
+ end
92
+
93
+ eval(%[
94
+ ], GLOBAL_BINDING)
95
+
96
+
97
+ ################################################
98
+ # Quantities that are calculated or determined by CodeRunner
99
+ # after the simulation has ended, i.e. quantities
100
+ # that are not available from the GS2 output files.
101
+ ################################################
102
+
103
+
104
+ @results = [
105
+ :converged,
106
+ :decaying,
107
+ :growth_rates,
108
+ :real_frequencies,
109
+ :growth_rates_by_ky, # deprecated
110
+ :growth_rates_by_kx, # deprecated
111
+ :growth_rate_at_ky,
112
+ :growth_rate_at_kx,
113
+ :growth_rate_at_ky_at_kx,
114
+ :real_frequencies_by_ky,
115
+ :max_growth_rate,
116
+ :fastest_growing_mode,
117
+ :freq_of_max_growth_rate,
118
+ :ky,
119
+ :gamma_r,
120
+ :gamma_i,
121
+ :run_time,
122
+ :hflux_tot_stav,
123
+ :phi2_tot_stav,
124
+ :saturation_time_index,
125
+ :es_heat_flux_stav,
126
+ :es_mom_flux_stav,
127
+ :hflux_tot_stav_error,
128
+ :es_heat_flux_stav_error,
129
+ :es_mom_flux_stav_error,
130
+ :saturated,
131
+ :shot_time,
132
+ :spectrum_check,
133
+ :par_mom_flux_stav,
134
+ :perp_mom_flux_stav,
135
+ :transient_amplification_at_kx,
136
+ :transient_amplification_at_ky,
137
+ :transient_amplification_at_ky_at_kx,
138
+ :transient_es_heat_flux_amplification_at_species_at_kx,
139
+ :transient_es_heat_flux_amplification_at_species_at_ky,
140
+ :transient_es_heat_flux_amplification_at_species_at_ky_at_kx,
141
+ :vspace_check
142
+ ]
143
+
144
+
145
+ ###############################################
146
+ # Other useful information about the run
147
+ ###############################################
148
+
149
+ @gs2_run_info = [:time, :percent_of_total_time, :checked_converged, :is_a_restart, :restart_id, :restart_run_name, :completed_timesteps]
150
+
151
+ @run_info = @gs2_run_info.dup
152
+
153
+ ##############################################################
154
+ # For backwards compatibility with CodeRunner version 0.5.0
155
+ ##############################################################
156
+
157
+ @run_info_0_5_0 = {
158
+ time: :to_f,
159
+ percent_of_total_time: :to_f,
160
+ checked_converged: :to_b
161
+ }
162
+
163
+ @results_0_5_0 = {
164
+ converged: :to_b,
165
+ decaying: :to_b,
166
+ :growth_rates => :to_h,
167
+ :real_frequencies => :to_h,
168
+ # :ky_list => :to_h,
169
+ # :kx_list => :to_h,
170
+ :growth_rates_by_ky => :to_s,
171
+ :real_frequencies_by_ky => :to_s,
172
+ :max_growth_rate => :to_f,
173
+ :fastest_growing_mode => :to_f,
174
+ :freq_of_max_growth_rate => :to_f,
175
+ :ky => :to_f,
176
+ :gamma_r => :to_f,
177
+ :gamma_i => :to_f,
178
+ :run_time => :to_f
179
+ # :theta_list => :to_h
180
+ }
181
+
182
+ ###############################################################
183
+
184
+ @uses_mpi = true
185
+
186
+ @modlet_required = false
187
+
188
+ @use_graphs = false
189
+ Phi = Struct.new("Phi", :phi, :ri, :theta_index, :kx_index, :ky_index)
190
+
191
+ @naming_pars = []
192
+
193
+ # def self.finish_setting_up_class
194
+ # @@variables += [
195
+ # end
196
+
197
+ # This method, as its name suggests, is called whenever CodeRunner is asked to analyse a run directory.this happens if the run status is not :Complete, or if the user has specified recalc_all(-A on the command line) or reprocess_all (-a on the command line).
198
+ #
199
+ # the structure of this function is very simple: first it calls get_status to determine the directory status, i.e. :Complete, :Incomplete, :NotStarted or :Failed, then it gets the time, which is the GS2 time at the end of the run, and it also gets the run_time, which is the wall clock time of the run. Finally,if non-linear mode is switched off, it calls calculate_growth_rates_and_frequencies, and if the non-linear mode is switched on, it calls calculate_time_averaged_fluxes.
200
+
201
+ def process_directory_code_specific
202
+ run_namelist_backwards_compatibility
203
+
204
+ unless @status == :Queueing
205
+ get_status
206
+ end
207
+
208
+ eputs "Run #@status: #@run_name" if [:Complete,:Failed].include? @status
209
+
210
+ try_to_get_error_file
211
+ @sys = @@successful_trial_system
212
+
213
+ return if @status == :NotStarted or @status == :Failed or @status == :Queueing
214
+ begin
215
+ percent_complete = get_completed_timesteps/@nstep
216
+ @percent_of_total_time = percent_complete
217
+ rescue
218
+ get_time
219
+ @percent_of_total_time = @time / (@delt*@nstep) * 100.0 rescue 0.0
220
+ end
221
+ return if @status == :Incomplete
222
+
223
+ get_run_time
224
+
225
+ calculate_results
226
+
227
+ end
228
+ def calculate_results
229
+ return if ENV['CODE_RUNNER_NO_ANALYSIS'] =~ /true/
230
+
231
+
232
+ eputs "Analysing run"
233
+
234
+ if @nonlinear_mode == "off"
235
+
236
+ calculate_growth_rates_and_frequencies
237
+ elsif @nonlinear_mode == "on"
238
+ calculate_saturation_time_index
239
+ calculate_time_averaged_fluxes
240
+ calculate_spectral_checks
241
+ begin
242
+ calculate_vspace_checks
243
+ rescue
244
+ end
245
+ end
246
+
247
+ @growth_rates ||={}
248
+ @real_frequencies ||={}
249
+ end
250
+
251
+
252
+ # Try to read the runtime in minutes from the GS2 standard out.
253
+
254
+ def get_run_time
255
+ logf(:get_run_time)
256
+ output = @output_file || try_to_get_output_file
257
+ return nil unless output
258
+ begin
259
+ Regexp.new("total from timer is:\\s*#{LongRegexen::NUMBER}", Regexp::IGNORECASE).match FileUtils.tail(output, 300)
260
+ logi $~
261
+ @run_time = $~[:number].to_f
262
+ rescue
263
+ @run_time = nil
264
+ end
265
+ end
266
+
267
+ # Output useful information from the NetCDF file. If no names are provided, output a list of all variables in the NetCDF file. <tt>names</tt> can either be a symbol or an array of symbols, in which case information will be output for the variables with those names. If values are provided, for example :dims,:get, :ndims, this information is retrieved from the file for every variable named.
268
+ # ncdump
269
+ # ncdump(:hflux)
270
+ # ncdump([:hflux, :phi])
271
+ # ncdump([:hflux, :phi], :dims)
272
+
273
+
274
+ def ncdump(names=nil, values=nil, extension = '.out.nc')
275
+ names = [names] unless !names or names.class == Array
276
+ names.map!{|name| name.to_s} if names
277
+ pp NumRu::NetCDF.open(@run_name + extension).vars(names).to_a.sort{|var1, var2| var1.name <=> var2.name}.map{|var| values ? [var.name, var.send(values)] : var.name.to_sym}
278
+ end
279
+
280
+
281
+ #
282
+
283
+ def generate_phantom_runs
284
+ @phantom_runs = []
285
+ logf(:generate_phantom_runs)
286
+ return if @grid_option == "single" and @scan_type == "none"
287
+ begin
288
+ list(:ky) # This will fail unless the run has output the netcdf file
289
+ rescue
290
+ return
291
+ end
292
+ return unless @status == :Complete #and @converged
293
+ log(@run_name)
294
+ if @grid_option == "box" and @nonlinear_mode == "off"
295
+ @ky = nil
296
+ # raise CRFatal.new("no @ky_list") unless @ky_list
297
+ # log list(:ky)
298
+ list(:ky).each do |id, ky|
299
+ phantom_run = create_phantom #self.dup
300
+ phantom_run.ky = ky
301
+ phantom_run.gamma_r = @growth_rates[ky]
302
+ phantom_run.gamma_i = @real_frequencies[ky]
303
+ log @runner.phantom_ids
304
+ # log('@runner.class', @runner.class)
305
+ # @runner.add_phantom_run(phantom_run)
306
+ end
307
+ elsif @scan_type and @scan_type != "none"
308
+ t = gsl_vector('t')
309
+ scan_vals = gsl_vector('scan_parameter_value')
310
+ current = scan_vals[0]
311
+ start = 0
312
+ for i in 0...t.size
313
+ if scan_vals[i] != current
314
+ phantom = create_phantom
315
+ phantom.scan_index_window = [start+1, i] #remember indexes are elements + 1
316
+ #ep 'scan_index_window', phantom.scan_index_window
317
+ phantom.scan_parameter_value = current
318
+ phantom.growth_rate_at_ky = nil
319
+ phantom.growth_rate_at_kx = nil
320
+ phantom.growth_rate_at_ky_at_kx = nil
321
+ phantom.calculate_results
322
+ current = scan_vals[i]
323
+ start = i
324
+ end
325
+ end
326
+ end
327
+ end
328
+
329
+
330
+
331
+ def get_time
332
+ begin
333
+ lt = list(:t)
334
+ return lt.values.max if lt.size>0
335
+ rescue
336
+ end
337
+ time = nil
338
+ # eputs File.readlines(@run_name +".out").slice(-4..-1).reverse.join( "\n"); gets
339
+ raise CRFatal.new("Couldn't find outfile #{@run_name}.out") unless FileTest.exist?(@run_name + ".out")
340
+ tail = FileUtils.tail("#@run_name.out", 4)
341
+ #File.readlines(@run_name +".out").slice(-4..-1).reverse.join( "\n")
342
+ tail.sub(LongRegexen::FLOAT) do
343
+ # eputs $~.inspect
344
+ time = $~[:float].to_f
345
+ end #if FileTest.exist? (@run_name +".out")
346
+ #raise CRFatal.new("couldn't get the time from #{tail}") unless time
347
+ @time = time
348
+ end
349
+
350
+ def get_completed_timesteps
351
+ #raise CRFatal.new("Couldn't find outfile #{@run_name}.out") unless FileTest.exist?(@run_name + ".out")
352
+ #p 'try to get completed_timesteps', Dir.pwd, 'nwrite', @nwrite, 'delt', @delt
353
+ @completed_timesteps = (list(:t).size - 1) * (@nwrite || 1)
354
+ #p 'tried to get completed_timesteps'
355
+ #rescue
356
+ #`grep time= #@run_name.out`.split.size
357
+ # File.read("#@run_name.out").scan(/^\s+time\s*=\s+/).size * @nwrite
358
+ end
359
+
360
+ def incomplete
361
+ return (not 100 == percent_complete)
362
+ end
363
+
364
+ def parameter_transition(run)
365
+ end
366
+ # @@executable_location = nil
367
+ # def executable_location
368
+ # return "~/gs2_newterm" #(@@executable_location || ($gs2_new_term ? "~/gs2_newterm" : "~/gs2"))
369
+ # end
370
+ #
371
+ # def executable_name
372
+ # "gs2"
373
+ # end
374
+
375
+ @code_long = "GS2 Gyrokinetic Flux Tube Code"
376
+
377
+ @excluded_sub_folders =[]
378
+
379
+ attr_accessor :theta_list, :ky_list, :ky_graphs, :eigenfunctions, :ky_list, :t_list
380
+ attr_accessor :scan_index_window, :scan_parameter_value
381
+
382
+ class << self
383
+ aliold(:check_and_update)
384
+ def check_and_update
385
+ old_check_and_update
386
+ @readout_list = (@variables + @results - [:growth_rates_by_ky, :growth_rates, :real_frequencies, :real_frequencies_by_ky, :ky_list, :kx_list, :theta_list, :t_list])
387
+ end
388
+ end
389
+
390
+ def data_string
391
+ logf(:data_string)
392
+ return "" unless @converged unless @grid_option == 'single'
393
+ logi(@ky, @growth_rates, @real_frequencies)
394
+ # log(:@@readout_list, @@readout_list)
395
+ return rcp.readout_list.inject(""){|str,(var,type_co)| str+"#{(send(var) || "0")}\t"} + "\n"
396
+
397
+ # @ky ? (@@variables + @@results - ).inject(""){|str,(var,type_co)| str+"#{(send(var) || "0")}\t"} + sprintf("%e\t%e\t%e\n", @ky, @growth_rates[@ky], @real_frequencies[@ky]) : (@@variables + @@results).inject(""){|str,(var,type_co)| str+"#{(send(var) || "0")}\t"} + sprintf("%e\t%e\t%e\n", @fastest_growing_mode, @max_growth_rate, @freq_of_max_growth_rate)
398
+ end
399
+
400
+ def percent_complete
401
+ @completed_timesteps ? @completed_timesteps.to_f / @nstep.to_f * 100.0 : @percent_of_total_time
402
+ end
403
+
404
+ def print_out_line
405
+ logf(:print_out_line)
406
+ name = @run_name
407
+ name += " (res: #@restart_id)" if @restart_id
408
+ name += " real_id: #@real_id" if @real_id
409
+ beginning = sprintf("%2d:%d %-60s %1s:%2.1f(%s) %3s%1s %1s", @id, @job_no, name, @status.to_s[0,1], @run_time.to_f / 60.0, @nprocs.to_s, percent_complete, "%", @converged.to_s)
410
+ if @ky
411
+ beginning += sprintf("%3s %4s %4s", @ky, @growth_rates[@ky], @real_frequencies[@ky])
412
+ elsif @nonlinear_mode == "off"
413
+ beginning += sprintf("%3s %4s %4s",
414
+ @fastest_growing_mode, @max_growth_rate,
415
+ @freq_of_max_growth_rate)
416
+ elsif @nonlinear_mode == "on"
417
+ # p @hflux_tot_stav
418
+ beginning += " sat:#{saturated.to_s[0]}"
419
+ beginning += sprintf(" hflux:%1.2e", @hflux_tot_stav) if @hflux_tot_stav
420
+ beginning += sprintf("+/-%1.2e", @hflux_tot_stav_error) if @hflux_tot_stav_error
421
+ beginning += sprintf(" momflux:%1.2e", @es_mom_flux_stav.values.sum) if @es_mom_flux_stav and @es_mom_flux_stav.values[0]
422
+ beginning += ' SC:' + @spectrum_check.map{|c| c.to_s}.join(',') if @spectrum_check
423
+ beginning += ' VC:' + @vspace_check.map{|c| sprintf("%d", ((c*10.0).to_i rescue -1))}.join(',') if @vspace_check
424
+ end
425
+ beginning += " ---#{@comment}" if @comment
426
+ beginning
427
+
428
+ end
429
+
430
+
431
+ def get_list_of(*args)
432
+ #args can be any list of e.g. :ky, :kx, :theta, :t ...
433
+ logf(:get_list_of)
434
+ refresh = args[-1] == true ? true : false
435
+ args.pop if args[-1] == true
436
+ logd
437
+ Dir.chdir(@directory) do
438
+ args.each do |var|
439
+ # eputs "Loading #{var}"
440
+ list_name = var + :_list
441
+ log list_name
442
+
443
+ # self.class.send(:attr_accessor, list_name)
444
+ next if (cache[list_name] and [:Failed, :Complete].include? status and not refresh)
445
+
446
+ cache[list_name] = {}
447
+ netcdf_file.var(var.to_s).get.to_a.each_with_index do |value, element|
448
+ # print '.'
449
+ cache[list_name][element+1]=value
450
+ end
451
+ # eputs send(var+:_list)
452
+ end
453
+ end
454
+ logfc :get_list_of
455
+ return cache[args[0] + :_list] if args.size == 1
456
+ end
457
+
458
+ alias :list :get_list_of
459
+
460
+ def visually_check_growth_rate(ky=nil)
461
+ logf :visually_check_growth_rate
462
+ phi_vec = gsl_vector(:phi2_by_ky_over_time, {ky: ky})
463
+ t_vec = gsl_vector(:t)
464
+ constant, growth_rate = GSL::Fit::linear(t_vec, 0.5*GSL::Sf::log(phi_vec)).slice(0..1)
465
+ eputs growth_rate
466
+
467
+ graph = @@phi2tot_vs_time_template.graph(["#{constant} * exp (2 * #{growth_rate} * x)"], [[[t_vec, phi_vec], "u 1:2 title 'phi2tot #{@run_name}' w p"]], {"set_show_commands" => "\nset log y\n", "point_size"=>'1.0'})
468
+ # eputs graph.inline_data.inspect
469
+ graph.show
470
+ gets
471
+ graph.kill
472
+
473
+ end
474
+
475
+
476
+ def show_graph
477
+ thegraph = special_graph('phi2tot_vs_time_all_kys')
478
+ thegraph.title += " for g_exb = #{@g_exb.to_f.to_s}"
479
+ thegraph.show
480
+ sleep 1.5
481
+ # @decaying = Feedback.get_boolean("Is the graph decaying?")
482
+ thegraph.kill
483
+ end
484
+
485
+ # @@phi2tot_vs_time_template = {title: "Phi^2 Total vs Time", xlabel: " Time ", ylabel: "Phi^2 Total"})
486
+
487
+
488
+
489
+
490
+ def restart(new_run)
491
+ #new_run = self.dup
492
+ (rcp.variables).each{|v| new_run.set(v, send(v)) if send(v)}
493
+ SUBMIT_OPTIONS.each{|v| new_run.set(v, self.send(v)) unless new_run.send(v)}
494
+ #(rcp.results + rcp.gs2_run_info).each{|result| new_run.set(result, nil)}
495
+ new_run.is_a_restart = true
496
+ new_run.ginit_option = "many"
497
+ new_run.delt_option = "default"
498
+ #if Dir.entries(@directory).include? "nc"
499
+ #old_restart_run_name = (@restart_run_name or Dir.entries(@directory + '/nc').grep(/\.nc/)[0].sub(/\.nc\.\d+$/, ''))
500
+ #new_run.restart_file = File.expand_path("#@directory/nc/#{old_restart_run_name}.nc")
501
+ #else
502
+ #new_run.restart_file = File.expand_path("#@directory/#@run_name.nc")
503
+ #end
504
+ new_run.restart_id = @id
505
+ new_run.restart_run_name = @run_name
506
+ @runner.nprocs = @nprocs if @runner.nprocs == "1" # 1 is the default so this means the user probably didn't specify nprocs
507
+ raise "Restart must be on the same number of processors as the previous run: new is #{new_run.nprocs.inspect} and old is #{@nprocs.inspect}" if !new_run.nprocs or new_run.nprocs != @nprocs
508
+ # @runner.parameters.each{|var, value| new_run.set(var,value)} if @runner.parameters
509
+ # ep @runner.parameters
510
+ new_run.run_name = nil
511
+ new_run.naming_pars = @naming_pars
512
+ new_run.update_submission_parameters(new_run.parameter_hash.inspect, false) if new_run.parameter_hash
513
+ new_run.naming_pars.delete(:restart_id)
514
+ new_run.generate_run_name
515
+ #@runner.submit(new_run)
516
+ new_run
517
+ end
518
+
519
+ # Return a list of restart file paths (relative to the run directory).
520
+
521
+ def list_of_restart_files
522
+ Dir.chdir(@directory) do
523
+ files = Dir.entries.grep(/^\.\d+$/)
524
+ files = Dir.entries.grep(/\.nc(?:\.\d|_ene)/) if files.size == 0
525
+ if files.size == 0
526
+ (Dir.entries.find_all{|dir| FileTest.directory? dir} - ['.', '..']).each do |dir|
527
+ files = Dir.entries(dir).grep(/\.nc(?:\.\d|_ene)/).map{|file| dir + "/" + file}
528
+ break if files.size == 0
529
+ end
530
+ end #if files.size == 0
531
+ return files
532
+ end # Dir.chdir(@directory) do
533
+ end
534
+
535
+ alias :lorf :list_of_restart_files
536
+
537
+ # Put restart files in the conventional location, i.e. nc/run_name.proc
538
+
539
+ def standardize_restart_files
540
+ Dir.chdir(@directory) do
541
+ FileUtils.makedirs('nc')
542
+ list_of_restart_files.each do |file|
543
+ proc_id = file.scan(/\.\d+$|_ene$/)[0]
544
+ #p 'proc_id', proc_id
545
+ FileUtils.mv(file, "nc/#@run_name.nc#{proc_id}")
546
+ end
547
+ end
548
+ end
549
+
550
+ # Delete all the restart files (irreversible!)
551
+ #
552
+
553
+ def delete_restart_files(options={})
554
+ return unless Feedback.get_boolean("Deleting restart files. This action cannot be reversed. Do you wish to continue?") unless options[:no_confirm]
555
+ list_of_restart_files.each{|file| FileUtils.rm file}
556
+ end
557
+
558
+
559
+
560
+
561
+
562
+ def species_letter
563
+ species_type(1).downcase[0,1]
564
+ end
565
+
566
+ def species_type(index)
567
+ if rcp.variables.include? :type_1
568
+ type = send(:type_ + index.to_sym)
569
+ else
570
+ types = rcp.variables.find_all{|var| var.to_s =~ /^type/}.map{|var| send(var)}
571
+ type = types[index.to_i - 1]
572
+ end
573
+ type
574
+ end
575
+
576
+
577
+ # Returns true if this run has not been restarted, false if it has. This allows one to get data from the final run of a series of restarts.
578
+
579
+ def no_restarts
580
+ raise NoRunnerError unless @runner
581
+ !(@runner.runs.find{|run| run.restart_id == @id})
582
+ end
583
+
584
+
585
+ def restart_chain
586
+ if @restart_id
587
+ return @runner.run_list[@restart_id].restart_chain
588
+ end
589
+ chain = []
590
+ currid = @id
591
+ loop do
592
+ chain.push currid
593
+ break unless (restrt = @runner.runs.find{|run| run.restart_id == currid})
594
+ currid = restrt.id
595
+ end
596
+ return chain
597
+ end
598
+
599
+
600
+
601
+
602
+
603
+
604
+ def get_status
605
+ # eputs 'Checking Status'
606
+ logf(:get_status)
607
+
608
+ Dir.chdir(@directory) do
609
+ if @running
610
+ if FileTest.exist?(@run_name + ".out") and FileUtils.tail(@run_name + ".out", 5).split(/\n/).size > 4 and FileUtils.tail(@run_name + ".out", 200) =~ /t\=/
611
+ @status = :Incomplete
612
+ else
613
+ @status = :NotStarted
614
+ end
615
+
616
+ else
617
+ if FileTest.exist?(@run_name + ".out") and FileUtils.tail(@run_name + ".out", 5).split(/\n/).size > 4
618
+ #eputs "HERE", @scan_type
619
+ if @nonlinear_mode == "off" and FileUtils.tail(@run_name + ".out",200) =~ /omega converged/
620
+ eputs 'Omega converged...'
621
+ @status = :Complete
622
+ elsif @scan_type and @scan_type != "none" and FileUtils.tail(@run_name + ".par_scan",200) =~ /scan\s+is\s+complete/i
623
+ eputs 'Scan complete...'
624
+ @status = :Complete
625
+ elsif @nonlinear_mode == "on" or !@omegatol or @omegatol < 0.0 or (@exit_when_converged and @exit_when_converged.fortran_false?)
626
+ eputs 'No omegatol'
627
+ if FileTest.exist?(@run_name + ".out.nc")
628
+ get_completed_timesteps
629
+ else
630
+ eputs "Warning: no netcdf file #@run_name.out.nc"
631
+ @status = :Failed
632
+ return
633
+ end
634
+ #ep "completed_timesteps", @completed_timesteps
635
+ eputs "#{percent_complete}% of Timesteps Complete"
636
+ if percent_complete == 100.0
637
+ @status = :Complete
638
+ elsif percent_complete > 5 and FileUtils.tail(output_file, 200) =~ /total from timer is/
639
+ @status = :Complete
640
+ else
641
+ @status = :Failed
642
+ end
643
+ else
644
+ @status = :Failed
645
+ end
646
+ else
647
+ @status=:Failed
648
+ end
649
+ end
650
+ end
651
+ end
652
+
653
+
654
+ def self.modify_job_script(runner, runs, script)
655
+ if CODE_OPTIONS[:gs2] and CODE_OPTIONS[:gs2][:list]
656
+ if (list_size = CODE_OPTIONS[:gs2][:list]).kind_of? Integer
657
+ raise "The total number of runs must be a multiple of the list size!" unless runs.size % list_size == 0
658
+ pieces = runs.pieces(runs.size/list_size)
659
+ else
660
+ pieces = [runs]
661
+ end
662
+ script = ""
663
+ pieces.each do |runs|
664
+ #ep 'there is a list'
665
+ FileUtils.makedirs('job_lists')
666
+ jid = "#{runs[0].id}-#{runs[-1].id}"
667
+ list_file = "job_lists/gs2_list_#{jid}.list"
668
+ File.open(list_file,'w') do |file|
669
+ file.puts runs.size
670
+ file.puts runs.map{|r| "#{r.relative_directory}/#{r.run_name}"}.join("\n")
671
+ end
672
+ raise "runs must all have the same nprocs" unless runs.map{|r| r.nprocs}.uniq.size == 1
673
+ runs.each do |r|
674
+ # Make sure the restart file name includes the relative directory for
675
+ # list runs
676
+ reldir = r.relative_directory
677
+ rdir = r.restart_dir
678
+ #puts rdir[0...reldir.size] == reldir, rdir[0...reldir.size], reldir
679
+ #raise ""
680
+ if rdir
681
+ r.restart_dir = reldir + '/' + rdir if not rdir[0...reldir.size] == reldir
682
+ else
683
+ r.restart_dir = reldir
684
+ end
685
+ Dir.chdir(r.directory){r.write_input_file}
686
+ end
687
+ np = runs[0].nprocs.split('x').map{|n| n.to_i}
688
+ np[0] *= runs.size
689
+ nprocs = np.map{|n| n.to_s}.join('x')
690
+ @runner.nprocs = nprocs
691
+ ls = ListSubmitter.new(@runner, nprocs, list_file, jid)
692
+ script << ls.run_command
693
+ end
694
+ end
695
+ return script
696
+ end
697
+
698
+ class ListSubmitter
699
+ include CodeRunner::SYSTEM_MODULE
700
+ @uses_mpi = true
701
+ attr_reader :executable_location, :executable_name, :parameter_string
702
+ attr_reader :job_identifier
703
+ def initialize(runner, nprocs, list_file, jid)
704
+ @executable_location = runner.executable_location
705
+ @executable_name = runner.executable_name
706
+ @parameter_string = list_file
707
+ @job_identifier = jid
708
+ @nprocs = nprocs
709
+ end
710
+ def rcp
711
+ self.class.rcp
712
+ end
713
+ def self.rcp
714
+ @rcp ||= CodeRunner::Run::RunClassPropertyFetcher.new(self)
715
+ end
716
+
717
+ end #class ListSubmitter
718
+
719
+ def recheck
720
+ logf(:recheck)
721
+ Dir.chdir(@directory) do
722
+ logi('@runner.object_id', @runner.object_id)
723
+ log('@runner.class', @runner.class)
724
+ runner = @runner
725
+ instance_variables.each{|var| instance_variable_set(var, nil) unless var == :@runner}
726
+ begin File.delete(".code_runner_run_data") rescue Errno::ENOENT end
727
+ begin File.delete("code_runner_results.rb") rescue Errno::ENOENT end
728
+ logi(:@checked_converged, @checked_converged)
729
+ logi('@runner.object_id after reset', @runner.object_id)
730
+ log('@runner.class', @runner.class)
731
+ process_directory
732
+ end
733
+ end
734
+
735
+
736
+ def generate_input_file
737
+ raise CRFatal("No Input Module File Given or Module Corrupted") unless methods.include? (:input_file_text)
738
+ run_namelist_backwards_compatibility
739
+ if @restart_id
740
+ @runner.run_list[@restart_id].restart(self)
741
+ eputs 'Copying Restart files', ''
742
+ FileUtils.makedirs('nc')
743
+ #old_dir = File.dirname(@restart_file)
744
+ @restart_file = "#@restart_run_name.nc" #+ File.basename(@restart_file) #.sub(/\.nc/, '')
745
+ @restart_dir = "nc"
746
+ #files = Dir.entries(old_dir).grep(/\.nc(?:\.\d|_ene)/)
747
+ #files = Dir.entries(old_dir).grep(/^\.\d+$/) if files.size == 0
748
+ files = @runner.run_list[@restart_id].list_of_restart_files.map do |file|
749
+ @runner.run_list[@restart_id].directory + "/" + file
750
+ end
751
+ files.each_with_index do |file , index|
752
+ eputs "\033[2A" # Terminal jargon - go back one line
753
+ eputs "#{index+1} out of #{files.size}"
754
+ num = file.scan(/(?:\.\d+|_ene)$/)[0]
755
+ #FileUtils.cp("#{old_dir}/#{file}", "nc/#@restart_file#{num}")
756
+ FileUtils.cp(file, "nc/#@restart_file#{num}")
757
+ end
758
+ elsif @save_for_restart.fortran_true?
759
+ @restart_dir = "nc"
760
+ #if CODE_OPTIONS[:gs2] and CODE_OPTIONS[:gs2][:list]
761
+ #FileUtils.makedirs "#{@runner.root_folder}/#@restart_dir"
762
+ #else
763
+ FileUtils.makedirs @restart_dir
764
+ #end
765
+ @restart_file = "#@run_name.nc"
766
+
767
+ end
768
+
769
+ # Let Gs2 know how much wall clock time is available. avail_cpu_time is a GS2 input parameter.
770
+ @avail_cpu_time = @wall_mins * 60 if @wall_mins
771
+
772
+ # Automatically set the number of nodes to be the maximum possible without parallelising over x, if the user has left the number of nodes unspecified.
773
+
774
+ set_nprocs
775
+
776
+
777
+ ######### Check for errors and inconsistencies
778
+ ingen
779
+ #########
780
+
781
+ write_input_file
782
+ end
783
+
784
+ def write_input_file
785
+ File.open(@run_name + ".in", 'w'){|file| file.puts input_file_text}
786
+ end
787
+
788
+ def set_nprocs
789
+
790
+ if (nprocs_in = @nprocs) =~ /^x/
791
+ max = max_nprocs_no_x
792
+ nodes = 0
793
+ @nprocs = "#{nodes}#{nprocs_in}"
794
+ loop do
795
+ nodes += 1
796
+ @nprocs = "#{nodes}#{nprocs_in}"
797
+ if actual_number_of_processors > max
798
+ nodes -= 1
799
+ @nprocs = "#{nodes}#{nprocs_in}"
800
+ break
801
+ end
802
+ end
803
+ end
804
+ end
805
+
806
+ def actual_number_of_processors
807
+ raise "Please specify the processor layout using the -n or (n:) option" unless @nprocs
808
+ @nprocs.split('x').map{|n| n.to_i}.inject(1){|ntot, n| ntot*n}
809
+ end
810
+
811
+
812
+ def parameter_string
813
+ return "#{@run_name}.in"
814
+ end
815
+
816
+
817
+ def self.list_code_commands
818
+ puts (methods - superclass.methods).sort
819
+ end
820
+
821
+ def self.add_variable_to_namelist(namelist, var, value)
822
+ var = :stir_ + var if namelist == :stir
823
+ super(namelist, var, value)
824
+ end
825
+
826
+ def input_file_header
827
+ run_namelist_backwards_compatibility
828
+ <<EOF
829
+ !==============================================================================
830
+ ! GS2 INPUT FILE automatically generated by CodeRunner
831
+ !==============================================================================
832
+ !
833
+ ! GS2 is a gyrokinetic flux tube initial value turbulence code
834
+ ! which can be used for fusion or astrophysical plasmas.
835
+ !
836
+ ! See http://gyrokinetics.sourceforge.net
837
+ !
838
+ ! CodeRunner is a framework for the automated running and analysis
839
+ ! of large simulations.
840
+ !
841
+ ! See http://coderunner.sourceforge.net
842
+ ! by CodeRunner version #{CodeRunner::CODE_RUNNER_VERSION.to_s}
843
+ !
844
+ !==============================================================================
845
+
846
+ EOF
847
+ end
848
+
849
+ def self.defaults_file_header
850
+ <<EOF1
851
+ ######################################################################
852
+ # Automatically generated defaults file for GS2 CodeRunner module #
853
+ # #
854
+ # This defaults file specifies a set of defaults for GS2 which are #
855
+ # used by CodeRunner to set up and run GS2 simulations. #
856
+ # #
857
+ # Created #{Time.now.to_s} #
858
+ # #
859
+ ######################################################################
860
+
861
+ @defaults_file_description = ""
862
+ EOF1
863
+ end
864
+
865
+
866
+ # Customize this method from Run::FortranNamelist by saying when diagnostics are not switched on.
867
+
868
+ def namelist_text(namelist, enum = nil)
869
+ hash = rcp.namelists[namelist]
870
+ text = ""
871
+ ext = enum ? "_#{enum}" : ""
872
+ text << "!#{'='*30}\n!#{hash[:description]} #{enum} \n!#{'='*30}\n" if hash[:description]
873
+ text << "&#{namelist}#{ext}\n"
874
+ hash[:variables].each do |var, var_hash|
875
+ code_var = (var_hash[:code_name] or var)
876
+ cr_var = var+ext.to_sym
877
+ # ep cr_var, namelist
878
+ if send(cr_var) and (not var_hash[:should_include] or eval(var_hash[:should_include]))
879
+ # var_hash[:tests].each{|tst| eval(tst).test(send(cr_var), cr_var)}
880
+ if String::FORTRAN_BOOLS.include? send(cr_var) # var is a Fortran Bool, not really a string
881
+ output = send(cr_var).to_s
882
+ elsif (v = send(cr_var)).kind_of? Complex
883
+ output = "(#{v.real}, #{v.imag})"
884
+ else
885
+ output = send(cr_var).inspect
886
+ end
887
+ text << " #{code_var} = #{output} #{var_hash[:description] ? "! #{var_hash[:description]}": ""}\n"
888
+ elsif namelist == :gs2_diagnostics_knobs or namelist == :diagnostics
889
+ text << " ! #{code_var} not specified --- #{var_hash[:description]}\n"
890
+ end
891
+ end
892
+ # # end
893
+ text << "/\n\n"
894
+ text
895
+ end
896
+
897
+ # def self.add_code_var
898
+ # rcp.namelists.each do |namelist, hash|
899
+ # hash[:variables].each do |var, var_hash|
900
+ # p var
901
+ # var_hash[:code_name] = var_hash[:gs2_name] if var_hash[:gs2_name]
902
+ # end
903
+ # end
904
+ # save_namelists
905
+ # end
906
+
907
+
908
+ def update_physics_parameters_from_miller_input_file(file)
909
+ hash = self.class.parse_input_file(file)
910
+ hash[:parameters].each do |var, val|
911
+ set(var,val)
912
+ end
913
+ hash[:theta_grid_parameters].each do |var, val|
914
+ next if [:ntheta, :nperiod].include? var
915
+ set(var, val)
916
+ end
917
+ hash[:dist_fn_knobs].each do |var, val|
918
+ next unless [:g_exb].include? var
919
+ set(var, val)
920
+ end
921
+ hash[:theta_grid_eik_knobs].each do |var, val|
922
+ next unless [:s_hat_input, :beta_prime_input].include? var
923
+ set(var, val)
924
+ end
925
+
926
+ hash[:species_parameters_2].each do |var, val|
927
+ #next unless [:s_hat_input, :beta_prime_input].include? var
928
+ set((var.to_s + '_2').to_sym, val)
929
+ end
930
+ hash[:species_parameters_1].each do |var, val|
931
+ #next unless [:s_hat_input, :beta_prime_input].include? var
932
+ set((var.to_s + '_1').to_sym, val)
933
+ end
934
+ end
935
+
936
+
937
+
938
+ def renew_info_file
939
+ Dir.chdir(@directory){make_info_file("#@run_name.in")}
940
+ end
941
+
942
+ # This method overrides a method defined in heuristic_run_methods.rb in the CodeRunner source. It is called when CodeRunner cannot find any of its own files in the folder being analysed. It takes a GS2 input file and generates a CodeRunner info file. This means that GS2 runs which were not run using CodeRunner can nonetheless be analysed by it. In order for it to be called the -H flag must be specified on the command line.
943
+
944
+ def run_heuristic_analysis
945
+ ep 'run_heuristic_analysis', Dir.pwd
946
+ infiles = Dir.entries.grep(/^[^\.].*\.in$/)
947
+ ep infiles
948
+ raise CRMild.new('No input file') unless infiles[0]
949
+ raise CRError.new("More than one input file in this directory: \n\t#{infiles}") if infiles.size > 1
950
+ input_file = infiles[0]
951
+ ep 'asdf'
952
+ @nprocs ||= "1"
953
+ @executable ||= "/dev/null"
954
+ make_info_file(input_file, false)
955
+ end
956
+
957
+ @source_code_subfolders = ['utils', 'geo']
958
+
959
+ attr_accessor :iphi00, :saturation_time #Necessary for back. comp. due to an old bug
960
+
961
+ folder = File.dirname(File.expand_path(__FILE__)) # i.e. the directory this file is in
962
+
963
+ SPECIES_DEPENDENT_NAMELISTS = eval(File.read(folder + '/species_dependent_namelists.rb'), binding, folder + '/species_dependent_namelists.rb')
964
+ #
965
+ SPECIES_DEPENDENT_VARIABLES_WITH_HELP = SPECIES_DEPENDENT_NAMELISTS.values.inject({}) do |hash, namelist_hash|
966
+ namelist_hash[:variables].each do |var, var_hash|
967
+ hash[var] = var_hash[:help]
968
+ end
969
+ hash
970
+ end
971
+
972
+ SPECIES_DEPENDENT_VARIABLES = SPECIES_DEPENDENT_VARIABLES_WITH_HELP.keys
973
+ SPECIES_DEPENDENT_VARIABLES.each{|var| attr_accessor var} # for backwards compatibility
974
+
975
+ ['i', 'e'].each do |n|
976
+ SPECIES_DEPENDENT_VARIABLES_WITH_HELP.each do |name, help|
977
+ attr_accessor name + "_#{n}".to_sym #for backwards compatibility
978
+ end
979
+ end
980
+
981
+ old_vars = %w[
982
+ :TiTe
983
+ :Rmaj
984
+ :R_geo
985
+ :invLp_input
986
+ :D_hypervisc
987
+ :D_hyperres
988
+ :D_hyper
989
+ :C_par
990
+ :C_perp
991
+ ].map{|n| n.to_s.sub(/^:/, '').to_sym}
992
+
993
+ old_vars.each do |var|
994
+ alias_method(var, var.to_s.downcase.to_sym)
995
+ alias_method("#{var}=".to_sym, "#{var.downcase}=".to_sym)
996
+ end
997
+
998
+
999
+
1000
+
1001
+ def run_namelist_backwards_compatibility
1002
+ SPECIES_DEPENDENT_VARIABLES.each do |var|
1003
+ set(var + "_1".to_sym, (send(var + "_1".to_sym) or send(var + "_i".to_sym) or send(var)))
1004
+ set(var + "_2".to_sym, (send(var + "_2".to_sym) or send(var + "_e".to_sym)))
1005
+ end
1006
+ end
1007
+
1008
+
1009
+ def stop
1010
+ `touch #@directory/#@run_name.stop`
1011
+ end
1012
+
1013
+ def vim_output
1014
+ system "vim -Ro #{output_file} #{error_file} #@directory/#@run_name.error #@directory/#@run_name.out "
1015
+ end
1016
+ alias :vo :vim_output
1017
+ def vim_stdout
1018
+ system "vim -Ro #{output_file} "
1019
+ end
1020
+ alias :vo1 :vim_stdout
1021
+ def plot_efit_file
1022
+ Dir.chdir(@directory) do
1023
+ text = File.read(@eqfile)
1024
+ text_lines = text.split("\n")
1025
+ first_line = text_lines[0].split(/\s+/)
1026
+ second_line = text_lines[1].split(/\s+/)
1027
+ nr = first_line[-2].to_i
1028
+ nz = first_line[-1].to_i
1029
+ rwidth = second_line[1].to_f
1030
+ zwidth = second_line[2].to_f
1031
+ rmag = second_line[3].to_f
1032
+ nlines = (nr.to_f/5.0).ceil
1033
+ nlines_psi = ((nr*nz).to_f/5.0).ceil
1034
+ start = 5
1035
+ f = text_lines[start...(start+=nlines)].join(" ").split(nil).map{|s| s.to_f}.to_gslv
1036
+ pres = text_lines[(start)...(start += nlines)].join(" ").split(nil).map{|s| s.to_f}.to_gslv
1037
+ dumy = text_lines[(start)...(start += nlines)].join(" ").split(nil).map{|s| s.to_f}.to_gslv
1038
+ ffprime = text_lines[(start)...(start+= nlines)].join(" ").split(nil).map{|s| s.to_f}.to_gslv
1039
+ psi = text_lines[(start)...(start += nlines_psi)].join(" ")
1040
+ q = text_lines[(start)...(start += nlines)].join(" ").split(nil).map{|s| s.to_f}.to_gslv
1041
+ nbound = text_lines[start...start+=1].join(" ").to_i
1042
+ rz = text_lines[(start)...(start += nbound*2)].join(" ").split(/\s+/)
1043
+ rz.shift
1044
+ rbound, zbound, dummy = rz.inject([[], [], true]){|arr,val| arr[2] ? [arr[0].push(val), arr[1], false] : [arr[0], arr[1].push(val), true]}
1045
+ #rbound.shift
1046
+
1047
+ psi = psi.split(/\s+/)
1048
+ psi.shift
1049
+ psi.map!{|v| v.to_f}
1050
+ psi_arr = SparseTensor.new(2)
1051
+ k = 0
1052
+ for i in 0...nz
1053
+ for j in 0...nr
1054
+ psi_arr[j,i] = psi[k]
1055
+ k+=1
1056
+ end
1057
+ end
1058
+ kit = GraphKit.quick_create([((0...nr).to_a.to_gslv - nr/2 - 1 )/(nr-1)*rwidth+rmag, ((0...nz).to_a.to_gslv-nz/2 + 1)/(nz-1) * zwidth, psi_arr], [rbound, zbound, rbound.map{|r| 0}])
1059
+ kit.gp.contour = ""
1060
+ kit.gp.view = "map"
1061
+ #kit.gp.nosurface = ""
1062
+ kit.gp.cntrparam = "levels 20"
1063
+ kit.data[0].gp.with = 'l'
1064
+ kit.data[1].gp.with = 'l lw 2 nocontours'
1065
+ kit.gnuplot
1066
+
1067
+ kit2 = GraphKit.quick_create([pres/pres.max],[f/f.max],[q/q.max])
1068
+ kit2.data[0].title = 'Pressure/Max Pressure'
1069
+ kit2.data[1].title = 'Poloidal current function/Max poloidal current function'
1070
+ kit2.data[2].title = 'Safety factor/Max Safety factor'
1071
+ kit2.gnuplot
1072
+
1073
+
1074
+
1075
+ #p ['f', f, 'p', pres, 'ffprime', ffprime, 'nlines', nlines, 'psi', psi, 'q', q, 'nbound', nbound, 'rbound', rbound, 'zbound', zbound]
1076
+
1077
+
1078
+ end
1079
+ end
1080
+
1081
+ end # class GS2
1082
+ # For backwards compatibility
1083
+
1084
+ Gs2BoxNtRun = Gs2CycloneRun = Gs2BoxCollisionalRun = Gs2Jet42982Run = Gs2ArtunRun = Gs2LinskerRun = Gs2BarnesLinskerRun = Gs2BoxMovieRun = Gs2Run = Gs2
1085
+ end # class CodeRunner
1086
+
1087
+ # ep CodeRunner::Gs2CycloneRun.ancestors
1088
+
1089
+
1090
+ class Float
1091
+ def <=>(other) # necessary because of netcdf quirks
1092
+
1093
+ d = (self - other)
1094
+ if d.abs / (self.abs + 1) < 1e-10
1095
+ return 0
1096
+ else
1097
+ return (d / d.abs).to_i
1098
+ end
1099
+ end
1100
+ def ==(other)
1101
+ return false unless other.kind_of? Numeric
1102
+ return (self - other).abs < 1e-14
1103
+ end
1104
+ end
1105
+
1106
+ class Hash
1107
+
1108
+ # puts self
1109
+
1110
+ def convert_to_index(run, *names)
1111
+ if self[:strongest_non_zonal_mode]
1112
+ ky_element, kx_element = run.gsl_matrix('spectrum_over_ky_over_kx', no_zonal: true).max_index
1113
+ p self[:kx_index] = kx_element + 1
1114
+ p self[:ky_index] = ky_element + 1
1115
+ self[:strongest_non_zonal_mode] = false
1116
+ end
1117
+
1118
+
1119
+ # ep run
1120
+ names.each do |name|
1121
+ if name == :kx
1122
+ if lkx = self[:lagrangian_kx]
1123
+ self[:lagrangian_kx_index] = list(:kx).key(lkx)
1124
+ end
1125
+ if lkxi = self[:lagrangian_kx_index] ||= self[:lkx_index]
1126
+ self[:kx_index] = run.eulerian_kx_index(kx_index: lkxi, ky_index: self[:ky_index], t_index: self[:t_index])
1127
+ end
1128
+ end
1129
+
1130
+ #ep 'name', name
1131
+ self[:ky_index] = 1 if name == :ky and run.grid_option == "single"
1132
+ self[:kx_index] = 1 if name == :kx and run.grid_option == "single"
1133
+ # ep run.list(name)
1134
+ self[name + :_index] ||= run.list(name).key(self[name]) || (raise ("#{name} not specified"))
1135
+ end
1136
+
1137
+ end
1138
+ def setup_time_window
1139
+ self[:t_index_window] ||= [self[:t_index],self[:t_index]] if self[:t_index]
1140
+ self[:begin_element], self[:end_element] = (self[:t_index_window] ? self[:t_index_window].map{|ind| ind -1} : [0, -1])
1141
+ end
1142
+
1143
+ end