gs2crmod 0.11.58 → 0.11.59
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/VERSION +1 -1
- data/gs2crmod.gemspec +7 -5
- data/lib/gs2crmod/gs2.rb +841 -837
- data/lib/gs2crmod/ingen.rb +203 -194
- metadata +64 -42
data/lib/gs2crmod/gs2.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
##########################################
|
2
|
-
# = Code Runner GS2 Module
|
2
|
+
# = Code Runner GS2 Module
|
3
3
|
##########################################
|
4
4
|
#
|
5
5
|
# Authors: Edmund Highcock
|
6
6
|
# Copyright: 2009 Edmund Highcock
|
7
7
|
#
|
8
|
-
# This is free software released under the GPL v3
|
8
|
+
# This is free software released under the GPL v3
|
9
9
|
#
|
10
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
11
|
#
|
@@ -20,35 +20,35 @@
|
|
20
20
|
|
21
21
|
#
|
22
22
|
|
23
|
-
begin
|
24
|
-
|
23
|
+
begin
|
24
|
+
require "numru/netcdf"
|
25
25
|
rescue LoadError
|
26
26
|
eputs "Error: No NetCDF: data analysis for gs2 not possible"
|
27
27
|
end
|
28
28
|
|
29
29
|
|
30
30
|
class CodeRunner
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
52
|
|
53
53
|
class Gs2 < Run::FortranNamelist
|
54
54
|
|
@@ -57,15 +57,15 @@ GS2_CRMOD_VERSION = Version.new('0.5.0')
|
|
57
57
|
|
58
58
|
|
59
59
|
def agk?
|
60
|
-
|
60
|
+
false
|
61
61
|
end
|
62
62
|
|
63
63
|
def spectrogk?
|
64
|
-
|
64
|
+
false
|
65
65
|
end
|
66
66
|
|
67
67
|
def gryfx?
|
68
|
-
|
68
|
+
false
|
69
69
|
end
|
70
70
|
|
71
71
|
CODE_SCRIPT_FOLDER = MODULE_FOLDER = File.dirname(File.expand_path(__FILE__))
|
@@ -88,13 +88,13 @@ NaN = GSL::NAN
|
|
88
88
|
|
89
89
|
|
90
90
|
def code_run_environment
|
91
|
-
|
92
|
-
|
93
|
-
|
91
|
+
case CodeRunner::SYS
|
92
|
+
when /iridis/
|
93
|
+
<<EOF
|
94
94
|
module load openmpi
|
95
95
|
EOF
|
96
|
-
|
97
|
-
|
96
|
+
when /helios/
|
97
|
+
<<EOF
|
98
98
|
module purge
|
99
99
|
module load intel
|
100
100
|
module load bullxmpi
|
@@ -104,18 +104,18 @@ module load fftw/3.3.3
|
|
104
104
|
module load bullxde papi
|
105
105
|
module load scalasca
|
106
106
|
EOF
|
107
|
-
|
108
|
-
|
107
|
+
#when /archer/
|
108
|
+
#<<EOF
|
109
109
|
#module swap PrgEnv-cray PrgEnv-intel
|
110
110
|
#module load intel/14.0.0.080
|
111
111
|
#module load fftw
|
112
112
|
#module load netcdf-hdf5parallel
|
113
113
|
#module load cray-hdf5-parallel
|
114
114
|
#EOF
|
115
|
-
|
115
|
+
else
|
116
116
|
|
117
|
-
|
118
|
-
|
117
|
+
@code_run_environment
|
118
|
+
end
|
119
119
|
end
|
120
120
|
|
121
121
|
eval(%[
|
@@ -130,44 +130,44 @@ eval(%[
|
|
130
130
|
|
131
131
|
|
132
132
|
@results = [
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
133
|
+
:converged,
|
134
|
+
:decaying,
|
135
|
+
:growth_rates,
|
136
|
+
:real_frequencies,
|
137
|
+
:growth_rates_by_ky, # deprecated
|
138
|
+
:growth_rates_by_kx, # deprecated
|
139
|
+
:growth_rate_at_ky,
|
140
|
+
:growth_rate_at_kx,
|
141
|
+
:growth_rate_at_ky_at_kx,
|
142
|
+
:frequency_at_ky_at_kx,
|
143
|
+
:real_frequencies_by_ky,
|
144
|
+
:max_growth_rate,
|
145
|
+
:fastest_growing_mode,
|
146
|
+
:freq_of_max_growth_rate,
|
147
|
+
:ky,
|
148
|
+
:gamma_r,
|
149
|
+
:gamma_i,
|
150
|
+
:run_time,
|
151
|
+
:hflux_tot_stav,
|
152
|
+
:phi2_tot_stav,
|
153
|
+
:saturation_time_index,
|
154
|
+
:es_heat_flux_stav,
|
155
|
+
:es_mom_flux_stav,
|
156
|
+
:hflux_tot_stav_error,
|
157
|
+
:es_heat_flux_stav_error,
|
158
|
+
:es_mom_flux_stav_error,
|
159
|
+
:saturated,
|
160
|
+
:shot_time,
|
161
|
+
:spectrum_check,
|
162
|
+
:par_mom_flux_stav,
|
163
|
+
:perp_mom_flux_stav,
|
164
|
+
:transient_amplification_at_kx,
|
165
|
+
:transient_amplification_at_ky,
|
166
|
+
:transient_amplification_at_ky_at_kx,
|
167
|
+
:transient_es_heat_flux_amplification_at_species_at_kx,
|
168
|
+
:transient_es_heat_flux_amplification_at_species_at_ky,
|
169
|
+
:transient_es_heat_flux_amplification_at_species_at_ky_at_kx,
|
170
|
+
:vspace_check
|
171
171
|
]
|
172
172
|
|
173
173
|
|
@@ -183,30 +183,30 @@ eval(%[
|
|
183
183
|
# For backwards compatibility with CodeRunner version 0.5.0
|
184
184
|
##############################################################
|
185
185
|
|
186
|
-
@run_info_0_5_0 = {
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
186
|
+
@run_info_0_5_0 = {
|
187
|
+
time: :to_f,
|
188
|
+
percent_of_total_time: :to_f,
|
189
|
+
checked_converged: :to_b
|
190
|
+
}
|
191
191
|
|
192
192
|
@results_0_5_0 = {
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
#
|
198
|
-
#
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
#
|
209
|
-
|
193
|
+
converged: :to_b,
|
194
|
+
decaying: :to_b,
|
195
|
+
:growth_rates => :to_h,
|
196
|
+
:real_frequencies => :to_h,
|
197
|
+
# :ky_list => :to_h,
|
198
|
+
# :kx_list => :to_h,
|
199
|
+
:growth_rates_by_ky => :to_s,
|
200
|
+
:real_frequencies_by_ky => :to_s,
|
201
|
+
:max_growth_rate => :to_f,
|
202
|
+
:fastest_growing_mode => :to_f,
|
203
|
+
:freq_of_max_growth_rate => :to_f,
|
204
|
+
:ky => :to_f,
|
205
|
+
:gamma_r => :to_f,
|
206
|
+
:gamma_i => :to_f,
|
207
|
+
:run_time => :to_f
|
208
|
+
# :theta_list => :to_h
|
209
|
+
}
|
210
210
|
|
211
211
|
###############################################################
|
212
212
|
|
@@ -220,7 +220,7 @@ Phi = Struct.new("Phi", :phi, :ri, :theta_index, :kx_index, :ky_index)
|
|
220
220
|
@naming_pars = []
|
221
221
|
|
222
222
|
# def self.finish_setting_up_class
|
223
|
-
#
|
223
|
+
# @@variables += [
|
224
224
|
# end
|
225
225
|
|
226
226
|
# 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).
|
@@ -228,178 +228,178 @@ Phi = Struct.new("Phi", :phi, :ri, :theta_index, :kx_index, :ky_index)
|
|
228
228
|
# 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.
|
229
229
|
|
230
230
|
def process_directory_code_specific
|
231
|
-
|
231
|
+
run_namelist_backwards_compatibility
|
232
232
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
eputs "Run #@status: #@run_name" if [:Complete,:Failed].include? @status
|
233
|
+
unless @status == :Queueing
|
234
|
+
get_status
|
235
|
+
end
|
238
236
|
|
239
|
-
|
240
|
-
@sys = @@successful_trial_system
|
237
|
+
eputs "Run #@status: #@run_name" if [:Complete,:Failed].include? @status
|
241
238
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
239
|
+
try_to_get_error_file
|
240
|
+
@sys = @@successful_trial_system
|
241
|
+
|
242
|
+
return if @status == :NotStarted or @status == :Failed or @status == :Queueing
|
243
|
+
begin
|
244
|
+
percent_complete = get_completed_timesteps/@nstep
|
245
|
+
@percent_of_total_time = percent_complete
|
246
|
+
rescue
|
247
|
+
get_time
|
248
|
+
@percent_of_total_time = @time / (@delt*@nstep) * 100.0 rescue 0.0
|
249
|
+
end
|
250
|
+
return if @status == :Incomplete
|
251
251
|
|
252
|
-
|
252
|
+
get_run_time
|
253
253
|
|
254
|
-
|
254
|
+
calculate_results
|
255
255
|
|
256
256
|
end
|
257
257
|
def calculate_results
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
258
|
+
return if ENV['CODE_RUNNER_NO_ANALYSIS'] =~ /true/
|
259
|
+
|
260
|
+
|
261
|
+
eputs "Analysing run"
|
262
|
+
|
263
|
+
if @nonlinear_mode == "off"
|
264
|
+
|
265
|
+
calculate_growth_rates_and_frequencies
|
266
266
|
calculate_transient_amplifications
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
267
|
+
elsif @nonlinear_mode == "on"
|
268
|
+
calculate_saturation_time_index
|
269
|
+
calculate_time_averaged_fluxes
|
270
|
+
begin
|
271
|
+
calculate_spectral_checks
|
272
|
+
calculate_vspace_checks
|
273
|
+
rescue
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
@growth_rates ||={}
|
278
|
+
@real_frequencies ||={}
|
279
279
|
end
|
280
|
-
|
280
|
+
|
281
281
|
|
282
282
|
# Try to read the runtime in minutes from the GS2 standard out.
|
283
283
|
|
284
284
|
def get_run_time
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
285
|
+
logf(:get_run_time)
|
286
|
+
output = @output_file || try_to_get_output_file
|
287
|
+
return nil unless output
|
288
|
+
begin
|
289
|
+
Regexp.new("total from timer is:\\s*#{LongRegexen::NUMBER}", Regexp::IGNORECASE).match FileUtils.tail(output, 300)
|
290
|
+
logi $~
|
291
|
+
@run_time = $~[:number].to_f
|
292
|
+
rescue
|
293
|
+
@run_time = nil
|
294
|
+
end
|
295
295
|
end
|
296
296
|
|
297
297
|
# 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.
|
298
|
-
#
|
299
|
-
#
|
300
|
-
#
|
301
|
-
#
|
298
|
+
# ncdump
|
299
|
+
# ncdump(:hflux)
|
300
|
+
# ncdump([:hflux, :phi])
|
301
|
+
# ncdump([:hflux, :phi], :dims)
|
302
302
|
|
303
303
|
|
304
304
|
def ncdump(names=nil, values=nil, extension = '.out.nc')
|
305
|
-
|
306
|
-
|
307
|
-
|
305
|
+
names = [names] unless !names or names.class == Array
|
306
|
+
names.map!{|name| name.to_s} if names
|
307
|
+
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}
|
308
308
|
end
|
309
309
|
|
310
310
|
|
311
311
|
#
|
312
312
|
|
313
313
|
def generate_component_runs
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
#
|
327
|
-
#
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
#
|
335
|
-
#
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
314
|
+
@component_runs = []
|
315
|
+
logf(:generate_component_runs)
|
316
|
+
return if @grid_option == "single" and @scan_type == "none"
|
317
|
+
begin
|
318
|
+
list(:ky) # This will fail unless the run has output the netcdf file
|
319
|
+
rescue
|
320
|
+
return
|
321
|
+
end
|
322
|
+
return unless @status == :Complete #and @converged
|
323
|
+
log(@run_name)
|
324
|
+
if @grid_option == "box" and @nonlinear_mode == "off"
|
325
|
+
@ky = nil
|
326
|
+
# raise CRFatal.new("no @ky_list") unless @ky_list
|
327
|
+
# log list(:ky)
|
328
|
+
list(:ky).each do |id, ky|
|
329
|
+
component_run = create_component #self.dup
|
330
|
+
component_run.ky = ky
|
331
|
+
component_run.gamma_r = @growth_rates[ky]
|
332
|
+
component_run.gamma_i = @real_frequencies[ky]
|
333
|
+
log @runner.component_ids
|
334
|
+
# log('@runner.class', @runner.class)
|
335
|
+
# @runner.add_component_run(component_run)
|
336
|
+
end
|
337
|
+
elsif (not gryfx?) and @scan_type and @scan_type != "none"
|
338
|
+
t = gsl_vector('t')
|
339
|
+
scan_vals = gsl_vector('scan_parameter_value')
|
340
|
+
current = scan_vals[0]
|
341
|
+
start = 0
|
342
|
+
for i in 0...t.size
|
343
|
+
if scan_vals[i] != current
|
344
|
+
component = create_component
|
345
|
+
component.scan_index_window = [start+1, i] #remember indexes are elements + 1
|
346
|
+
#ep 'scan_index_window', component.scan_index_window
|
347
|
+
component.scan_parameter_value = current
|
348
|
+
component.growth_rate_at_ky = nil
|
349
|
+
component.growth_rate_at_kx = nil
|
350
|
+
component.growth_rate_at_ky_at_kx = nil
|
351
|
+
component.calculate_results
|
352
|
+
current = scan_vals[i]
|
353
|
+
start = i
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
357
|
end
|
358
358
|
|
359
359
|
|
360
360
|
|
361
361
|
def get_time
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
#
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
#
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
362
|
+
begin
|
363
|
+
lt = list(:t)
|
364
|
+
return lt.values.max if lt.size>0
|
365
|
+
rescue
|
366
|
+
end
|
367
|
+
time = nil
|
368
|
+
# eputs File.readlines(@run_name +".out").slice(-4..-1).reverse.join( "\n"); gets
|
369
|
+
raise CRFatal.new("Couldn't find outfile #{@run_name}.out") unless FileTest.exist?(@run_name + ".out")
|
370
|
+
tail = FileUtils.tail("#@run_name.out", 4)
|
371
|
+
#File.readlines(@run_name +".out").slice(-4..-1).reverse.join( "\n")
|
372
|
+
tail.sub(LongRegexen::FLOAT) do
|
373
|
+
# eputs $~.inspect
|
374
|
+
time = $~[:float].to_f
|
375
|
+
end #if FileTest.exist? (@run_name +".out")
|
376
|
+
#raise CRFatal.new("couldn't get the time from #{tail}") unless time
|
377
|
+
@time = time
|
378
378
|
end
|
379
379
|
|
380
|
-
def get_completed_timesteps
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
#
|
380
|
+
def get_completed_timesteps
|
381
|
+
#raise CRFatal.new("Couldn't find outfile #{@run_name}.out") unless FileTest.exist?(@run_name + ".out")
|
382
|
+
#p 'try to get completed_timesteps', Dir.pwd, 'nwrite', @nwrite, 'delt', @delt
|
383
|
+
@completed_timesteps = (list(:t).size - 1) * (@nwrite || 1)
|
384
|
+
#p 'tried to get completed_timesteps'
|
385
|
+
#rescue
|
386
|
+
#`grep time= #@run_name.out`.split.size
|
387
|
+
# File.read("#@run_name.out").scan(/^\s+time\s*=\s+/).size * @nwrite
|
388
388
|
end
|
389
389
|
|
390
390
|
def incomplete
|
391
|
-
|
391
|
+
return (not 100 == percent_complete)
|
392
392
|
end
|
393
393
|
|
394
394
|
def parameter_transition(run)
|
395
395
|
end
|
396
396
|
# @@executable_location = nil
|
397
397
|
# def executable_location
|
398
|
-
#
|
398
|
+
# return "~/gs2_newterm" #(@@executable_location || ($gs2_new_term ? "~/gs2_newterm" : "~/gs2"))
|
399
399
|
# end
|
400
|
-
#
|
400
|
+
#
|
401
401
|
# def executable_name
|
402
|
-
#
|
402
|
+
# "gs2"
|
403
403
|
# end
|
404
404
|
|
405
405
|
@code_long = "GS2 Gyrokinetic Flux Tube Code"
|
@@ -410,114 +410,114 @@ attr_accessor :theta_list, :ky_list, :ky_graphs, :eigenfunctions, :ky_list, :t_l
|
|
410
410
|
attr_accessor :scan_index_window, :scan_parameter_value
|
411
411
|
|
412
412
|
class << self
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
413
|
+
aliold(:check_and_update)
|
414
|
+
def check_and_update
|
415
|
+
old_check_and_update
|
416
|
+
@readout_list = (@variables + @results - [:growth_rates_by_ky, :growth_rates, :real_frequencies, :real_frequencies_by_ky, :ky_list, :kx_list, :theta_list, :t_list])
|
417
|
+
end
|
418
418
|
end
|
419
419
|
|
420
420
|
def data_string
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
#
|
425
|
-
|
421
|
+
logf(:data_string)
|
422
|
+
return "" unless @converged unless @grid_option == 'single'
|
423
|
+
logi(@ky, @growth_rates, @real_frequencies)
|
424
|
+
# log(:@@readout_list, @@readout_list)
|
425
|
+
return rcp.readout_list.inject(""){|str,(var,_)| str+"#{(send(var) || "0")}\t"} + "\n"
|
426
426
|
|
427
|
-
#
|
427
|
+
# @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)
|
428
428
|
end
|
429
429
|
|
430
430
|
def percent_complete
|
431
|
-
|
431
|
+
@completed_timesteps ? @completed_timesteps.to_f / @nstep.to_f * 100.0 : @percent_of_total_time
|
432
432
|
end
|
433
433
|
|
434
434
|
def print_out_line
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
#
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
end
|
435
|
+
logf(:print_out_line)
|
436
|
+
name = @run_name
|
437
|
+
name += " (res: #@restart_id)" if @restart_id
|
438
|
+
name += " real_id: #@real_id" if @real_id
|
439
|
+
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)
|
440
|
+
if @ky
|
441
|
+
beginning += sprintf("%3s %4s %4s", @ky, @growth_rates[@ky], @real_frequencies[@ky])
|
442
|
+
elsif @nonlinear_mode == "off"
|
443
|
+
beginning += sprintf("%3s %4s %4s",
|
444
|
+
@fastest_growing_mode, @max_growth_rate,
|
445
|
+
@freq_of_max_growth_rate)
|
446
|
+
elsif @nonlinear_mode == "on"
|
447
|
+
# p @hflux_tot_stav
|
448
|
+
beginning += " sat:#{saturated.to_s[0]}"
|
449
|
+
beginning += sprintf(" hflux:%1.2e", @hflux_tot_stav) if @hflux_tot_stav
|
450
|
+
beginning += sprintf("+/-%1.2e", @hflux_tot_stav_error) if @hflux_tot_stav_error
|
451
|
+
beginning += sprintf(" momflux:%1.2e", @es_mom_flux_stav.values.sum) if @es_mom_flux_stav and @es_mom_flux_stav.values[0]
|
452
|
+
beginning += ' SC:' + @spectrum_check.map{|c| c.to_s}.join(',') if @spectrum_check
|
453
|
+
beginning += ' VC:' + @vspace_check.map{|c| sprintf("%d", ((c*10.0).to_i rescue -1))}.join(',') if @vspace_check
|
454
|
+
end
|
455
|
+
beginning += " ---#{@comment}" if @comment
|
456
|
+
beginning
|
457
|
+
|
458
|
+
end
|
459
459
|
|
460
460
|
|
461
461
|
def get_list_of(*args)
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
#
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
#
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
#
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
462
|
+
#args can be any list of e.g. :ky, :kx, :theta, :t ...
|
463
|
+
logf(:get_list_of)
|
464
|
+
refresh = args[-1] == true ? true : false
|
465
|
+
args.pop if args[-1] == true
|
466
|
+
logd
|
467
|
+
Dir.chdir(@directory) do
|
468
|
+
args.each do |var|
|
469
|
+
# eputs "Loading #{var}"
|
470
|
+
list_name = var + :_list
|
471
|
+
log list_name
|
472
|
+
|
473
|
+
# self.class.send(:attr_accessor, list_name)
|
474
|
+
next if (cache[list_name] and [:Failed, :Complete].include? status and not refresh)
|
475
|
+
|
476
|
+
cache[list_name] = {}
|
477
|
+
if netcdf_file.var(var.to_s)
|
478
|
+
netcdf_file.var(var.to_s).get.to_a.each_with_index do |value, element|
|
479
|
+
# print '.'
|
480
|
+
cache[list_name][element+1]=value
|
481
|
+
end
|
482
|
+
|
483
|
+
else
|
484
|
+
netcdf_file.dim(var.to_s).length.times.each do |element|
|
485
|
+
cache[list_name][element+1]='unknown'
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
# eputs send(var+:_list)
|
490
|
+
end
|
491
|
+
end
|
492
|
+
logfc :get_list_of
|
493
|
+
return cache[args[0] + :_list] if args.size == 1
|
494
494
|
end
|
495
495
|
|
496
496
|
alias :list :get_list_of
|
497
497
|
|
498
498
|
def visually_check_growth_rate(ky=nil)
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
#
|
507
|
-
|
508
|
-
|
509
|
-
|
499
|
+
logf :visually_check_growth_rate
|
500
|
+
phi_vec = gsl_vector(:phi2_by_ky_over_time, {ky: ky})
|
501
|
+
t_vec = gsl_vector(:t)
|
502
|
+
constant, growth_rate = GSL::Fit::linear(t_vec, 0.5*GSL::Sf::log(phi_vec)).slice(0..1)
|
503
|
+
eputs growth_rate
|
504
|
+
|
505
|
+
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'})
|
506
|
+
# eputs graph.inline_data.inspect
|
507
|
+
graph.show
|
508
|
+
gets
|
509
|
+
graph.kill
|
510
510
|
|
511
511
|
end
|
512
512
|
|
513
513
|
|
514
514
|
def show_graph
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
#
|
520
|
-
|
515
|
+
thegraph = special_graph('phi2tot_vs_time_all_kys')
|
516
|
+
thegraph.title += " for g_exb = #{@g_exb.to_f.to_s}"
|
517
|
+
thegraph.show
|
518
|
+
sleep 1.5
|
519
|
+
# @decaying = Feedback.get_boolean("Is the graph decaying?")
|
520
|
+
thegraph.kill
|
521
521
|
end
|
522
522
|
|
523
523
|
# @@phi2tot_vs_time_template = {title: "Phi^2 Total vs Time", xlabel: " Time ", ylabel: "Phi^2 Total"})
|
@@ -526,70 +526,70 @@ end
|
|
526
526
|
|
527
527
|
|
528
528
|
def restart(new_run)
|
529
|
-
|
530
|
-
|
529
|
+
#new_run = self.dup
|
530
|
+
(rcp.variables).each{|v| new_run.set(v, send(v)) if send(v)}
|
531
531
|
@naming_pars.delete(:preamble)
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
#
|
532
|
+
SUBMIT_OPTIONS.each{|v| new_run.set(v, self.send(v)) unless new_run.send(v)}
|
533
|
+
#(rcp.results + rcp.gs2_run_info).each{|result| new_run.set(result, nil)}
|
534
|
+
new_run.is_a_restart = true
|
535
|
+
new_run.ginit_option = "many"
|
536
|
+
new_run.delt_option = "check_restart"
|
537
|
+
#if Dir.entries(@directory).include? "nc"
|
538
|
+
#old_restart_run_name = (@restart_run_name or Dir.entries(@directory + '/nc').grep(/\.nc/)[0].sub(/\.nc\.\d+$/, ''))
|
539
|
+
#new_run.restart_file = File.expand_path("#@directory/nc/#{old_restart_run_name}.nc")
|
540
|
+
#else
|
541
|
+
#new_run.restart_file = File.expand_path("#@directory/#@run_name.nc")
|
542
|
+
#end
|
543
|
+
new_run.restart_id = @id
|
544
|
+
new_run.restart_run_name = @run_name
|
545
|
+
@runner.nprocs = @nprocs if @runner.nprocs == "1" # 1 is the default so this means the user probably didn't specify nprocs
|
546
|
+
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
|
547
|
+
# @runner.parameters.each{|var, value| new_run.set(var,value)} if @runner.parameters
|
548
548
|
# ep @runner.parameters
|
549
549
|
new_run.run_name = nil
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
end
|
574
|
-
|
575
|
-
# Return a list of restart file paths (relative to the run directory).
|
550
|
+
new_run.naming_pars = @naming_pars
|
551
|
+
new_run.update_submission_parameters(new_run.parameter_hash_string, false) if new_run.parameter_hash
|
552
|
+
new_run.naming_pars.delete(:restart_id)
|
553
|
+
new_run.generate_run_name
|
554
|
+
eputs 'Copying Restart files', ''
|
555
|
+
FileUtils.makedirs(new_run.directory + '/nc')
|
556
|
+
#old_dir = File.dirname(@restart_file)
|
557
|
+
new_run.restart_file = "#@run_name.nc" #+ File.basename(@restart_file) #.sub(/\.nc/, '')
|
558
|
+
new_run.restart_dir = "nc"
|
559
|
+
#files = Dir.entries(old_dir).grep(/\.nc(?:\.\d|_ene)/)
|
560
|
+
#files = Dir.entries(old_dir).grep(/^\.\d+$/) if files.size == 0
|
561
|
+
files = list_of_restart_files.map do |file|
|
562
|
+
@directory + "/" + file
|
563
|
+
end
|
564
|
+
files.each_with_index do |file , index|
|
565
|
+
eputs "\033[2A" # Terminal jargon - go back one line
|
566
|
+
eputs "#{index+1} out of #{files.size}"
|
567
|
+
num = file.scan(/(?:\.\d+|_ene)$/)[0]
|
568
|
+
#FileUtils.cp("#{old_dir}/#{file}", "nc/#@restart_file#{num}")
|
569
|
+
FileUtils.cp(file, new_run.directory + "/nc/#{new_run.restart_file}#{num}")
|
570
|
+
end
|
571
|
+
#@runner.submit(new_run)
|
572
|
+
new_run
|
573
|
+
end
|
574
|
+
|
575
|
+
# Return a list of restart file paths (relative to the run directory).
|
576
576
|
|
577
577
|
def list_of_restart_files
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
578
|
+
Dir.chdir(@directory) do
|
579
|
+
files = Dir.entries.grep(/^\.\d+$/)
|
580
|
+
files = Dir.entries.grep(/\.nc(?:\.\d|_ene)/) if files.size == 0
|
581
|
+
if files.size == 0
|
582
|
+
(Dir.entries.find_all{|dir| FileTest.directory? dir} - ['.', '..']).each do |dir|
|
583
|
+
files = Dir.entries(dir).grep(/\.nc(?:\.\d|_ene)/).map{|file| dir + "/" + file}
|
584
|
+
break if files.size == 0
|
585
|
+
end
|
586
|
+
end #if files.size == 0
|
587
587
|
# This just finds a .nc file (w/o a number) in the nc folder if using single restart file
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
588
|
+
if files.size == 0
|
589
|
+
files = Dir.entries('nc').grep(/\.nc/).map{|file| 'nc' + "/" + file}
|
590
|
+
end #if files.size == 0
|
591
|
+
return files
|
592
|
+
end # Dir.chdir(@directory) do
|
593
593
|
end
|
594
594
|
|
595
595
|
alias :lorf :list_of_restart_files
|
@@ -597,14 +597,14 @@ alias :lorf :list_of_restart_files
|
|
597
597
|
# Put restart files in the conventional location, i.e. nc/run_name.proc
|
598
598
|
|
599
599
|
def standardize_restart_files
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
600
|
+
Dir.chdir(@directory) do
|
601
|
+
FileUtils.makedirs('nc')
|
602
|
+
list_of_restart_files.each do |file|
|
603
|
+
proc_id = file.scan(/\.\d+$|_ene$/)[0]
|
604
|
+
#p 'proc_id', proc_id
|
605
|
+
FileUtils.mv(file, "nc/#@run_name.nc#{proc_id}")
|
606
|
+
end
|
607
|
+
end
|
608
608
|
end
|
609
609
|
|
610
610
|
# Delete all the restart files (irreversible!)
|
@@ -613,267 +613,271 @@ end
|
|
613
613
|
def delete_restart_files(options={})
|
614
614
|
puts 'You are about to delete the restart files for:'
|
615
615
|
puts @run_name
|
616
|
-
|
617
|
-
|
616
|
+
return unless Feedback.get_boolean("This action cannot be reversed. Do you wish to continue?") unless options[:no_confirm]
|
617
|
+
list_of_restart_files.each{|file| FileUtils.rm file}
|
618
618
|
end
|
619
619
|
|
620
620
|
|
621
|
-
|
621
|
+
|
622
622
|
|
623
623
|
|
624
624
|
def species_letter
|
625
|
-
|
625
|
+
species_type(1).downcase[0,1]
|
626
626
|
end
|
627
627
|
|
628
|
-
def species_type(index)
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
628
|
+
def species_type(index)
|
629
|
+
if rcp.variables.include? :type_1
|
630
|
+
type = send(:type_ + index.to_sym)
|
631
|
+
else
|
632
|
+
types = rcp.variables.find_all{|var| var.to_s =~ /^type/}.map{|var| send(var)}
|
633
|
+
type = types[index.to_i - 1]
|
634
|
+
end
|
635
|
+
type
|
636
636
|
end
|
637
637
|
|
638
638
|
|
639
639
|
# 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.
|
640
640
|
|
641
641
|
def no_restarts
|
642
|
-
|
643
|
-
|
642
|
+
raise NoRunnerError unless @runner
|
643
|
+
!(@runner.runs.find{|run| run.restart_id == @id})
|
644
644
|
end
|
645
645
|
|
646
646
|
|
647
647
|
def restart_chain
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
648
|
+
if @restart_id
|
649
|
+
return @runner.run_list[@restart_id].restart_chain
|
650
|
+
end
|
651
|
+
chain = []
|
652
|
+
currid = @id
|
653
|
+
loop do
|
654
|
+
chain.push currid
|
655
|
+
break unless (restrt = @runner.runs.find{|run| run.restart_id == currid})
|
656
|
+
currid = restrt.id
|
657
|
+
end
|
658
|
+
return chain
|
659
659
|
end
|
660
660
|
|
661
661
|
|
662
662
|
|
663
663
|
|
664
|
-
|
664
|
+
|
665
665
|
|
666
666
|
def get_status
|
667
|
-
#
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
667
|
+
# eputs 'Checking Status'
|
668
|
+
logf(:get_status)
|
669
|
+
|
670
|
+
Dir.chdir(@directory) do
|
671
|
+
if @running
|
672
|
+
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\=/
|
673
|
+
@status = :Incomplete
|
674
|
+
else
|
675
|
+
@status = :NotStarted
|
676
|
+
end
|
677
|
+
|
678
|
+
else
|
679
|
+
if FileTest.exist?(@run_name + ".out") and FileUtils.tail(@run_name + ".out", 5).split(/\n/).size > 4
|
680
|
+
#eputs "HERE", @scan_type
|
681
|
+
if @nonlinear_mode == "off" and FileUtils.tail(@run_name + ".out",200) =~ /omega converged/
|
682
|
+
eputs 'Omega converged...'
|
683
|
+
@status = :Complete
|
684
|
+
elsif @scan_type and @scan_type != "none" and FileUtils.tail(@run_name + ".par_scan",200) =~ /scan\s+is\s+complete/i
|
685
|
+
eputs 'Scan complete...'
|
686
|
+
@status = :Complete
|
687
|
+
elsif @nonlinear_mode == "on" or !@omegatol or @omegatol < 0.0 or (@exit_when_converged and @exit_when_converged.fortran_false?)
|
688
|
+
eputs 'No omegatol'
|
689
|
+
if FileTest.exist?(@run_name + ".out.nc")
|
690
|
+
#p ['pwd', Dir.pwd, netcdf_file, netcdf_file.dim('t'), netcdf_file.dims]
|
691
|
+
if netcdf_file.dim('t').length > 0
|
692
|
+
get_completed_timesteps
|
693
|
+
else
|
694
|
+
@status = :Failed
|
695
|
+
return
|
696
|
+
end
|
697
|
+
else
|
698
|
+
eputs "Warning: no netcdf file #@run_name.out.nc"
|
699
|
+
@status = :Failed
|
700
|
+
return
|
701
|
+
end
|
702
|
+
#ep "completed_timesteps", @completed_timesteps
|
703
|
+
eputs "#{percent_complete}% of Timesteps Complete"
|
704
|
+
if percent_complete >= 100.0
|
705
|
+
@status = :Complete
|
706
|
+
elsif percent_complete > 5 and FileUtils.tail(output_file, 200) =~ /total from timer is/
|
707
|
+
@status = :Complete
|
708
|
+
else
|
709
|
+
@status = :Failed
|
710
|
+
end
|
711
|
+
else
|
712
|
+
@status = :Failed
|
713
|
+
end
|
714
|
+
else
|
715
|
+
@status=:Failed
|
716
|
+
end
|
717
|
+
end
|
718
|
+
end
|
719
719
|
end
|
720
720
|
|
721
721
|
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
722
|
+
def self.modify_job_script(runner, runs_in, script)
|
723
|
+
if CODE_OPTIONS[:gs2] and CODE_OPTIONS[:gs2][:list]
|
724
|
+
if (list_size = CODE_OPTIONS[:gs2][:list]).kind_of? Integer
|
725
|
+
raise "The total number of runs must be a multiple of the list size!" unless runs_in.size % list_size == 0
|
726
|
+
pieces = runs_in.pieces(runs_in.size/list_size)
|
727
|
+
else
|
728
|
+
pieces = [runs_in]
|
729
|
+
end
|
730
|
+
script = ""
|
731
|
+
pieces.each do |runs|
|
732
|
+
#ep 'there is a list'
|
733
|
+
FileUtils.makedirs('job_lists')
|
734
|
+
jid = "#{runs[0].id}-#{runs[-1].id}"
|
735
|
+
list_file = "job_lists/gs2_list_#{jid}.list"
|
736
|
+
File.open(list_file,'w') do |file|
|
737
|
+
file.puts runs.size
|
738
|
+
file.puts runs.map{|r| "#{r.relative_directory}/#{r.run_name}"}.join("\n")
|
739
|
+
end
|
740
|
+
raise "runs must all have the same nprocs" unless runs.map{|r| r.nprocs}.uniq.size == 1
|
741
|
+
runs.each do |r|
|
742
|
+
# Make sure the restart file name includes the relative directory for
|
743
|
+
# list runs
|
744
|
+
reldir = r.relative_directory
|
745
|
+
rdir = r.restart_dir
|
746
|
+
#puts rdir[0...reldir.size] == reldir, rdir[0...reldir.size], reldir
|
747
|
+
#raise ""
|
748
|
+
if rdir
|
749
|
+
r.restart_dir = reldir + '/' + rdir if not rdir[0...reldir.size] == reldir
|
750
|
+
else
|
751
|
+
r.restart_dir = reldir
|
752
|
+
end
|
753
|
+
Dir.chdir(r.directory){r.write_input_file}
|
754
|
+
end
|
755
|
+
np = runs[0].nprocs.split('x').map{|n| n.to_i}
|
756
|
+
np[0] *= runs.size
|
757
|
+
nprocs = np.map{|n| n.to_s}.join('x')
|
758
|
+
@runner.nprocs = nprocs
|
759
|
+
ls = ListSubmitter.new(@runner, nprocs, list_file, jid)
|
760
|
+
script << ls.run_command
|
761
|
+
end
|
762
|
+
end
|
763
|
+
return script
|
764
|
+
end
|
765
|
+
|
766
|
+
class ListSubmitter
|
767
|
+
include CodeRunner::SYSTEM_MODULE
|
768
|
+
@uses_mpi = true
|
769
|
+
attr_reader :executable_location, :executable_name, :parameter_string
|
770
|
+
attr_reader :job_identifier
|
771
|
+
def initialize(runner, nprocs, list_file, jid)
|
772
|
+
@executable_location = runner.executable_location
|
773
|
+
@executable_name = runner.executable_name
|
774
|
+
@parameter_string = list_file
|
775
|
+
@job_identifier = jid
|
776
|
+
@nprocs = nprocs
|
777
|
+
end
|
778
|
+
def rcp
|
779
|
+
self.class.rcp
|
780
|
+
end
|
781
|
+
def self.rcp
|
782
|
+
@rcp ||= CodeRunner::Run::RunClassPropertyFetcher.new(self)
|
783
|
+
end
|
784
|
+
|
785
|
+
end #class ListSubmitter
|
786
786
|
|
787
787
|
def recheck
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
788
|
+
logf(:recheck)
|
789
|
+
Dir.chdir(@directory) do
|
790
|
+
logi('@runner.object_id', @runner.object_id)
|
791
|
+
log('@runner.class', @runner.class)
|
792
|
+
#runner = @runner
|
793
|
+
instance_variables.each{|var| instance_variable_set(var, nil) unless var == :@runner}
|
794
|
+
begin File.delete(".code_runner_run_data") rescue Errno::ENOENT end
|
795
|
+
begin File.delete("code_runner_results.rb") rescue Errno::ENOENT end
|
796
|
+
logi(:@checked_converged, @checked_converged)
|
797
|
+
logi('@runner.object_id after reset', @runner.object_id)
|
798
|
+
log('@runner.class', @runner.class)
|
799
|
+
process_directory
|
800
|
+
end
|
801
801
|
end
|
802
802
|
|
803
803
|
|
804
804
|
def generate_input_file(&block)
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
805
|
+
raise CRFatal("No Input Module File Given or Module Corrupted") unless methods.include? (:input_file_text)
|
806
|
+
run_namelist_backwards_compatibility
|
807
|
+
if @restart_id and (not @is_a_restart or @resubmit_id) # The second test checks that the restart function has not been called manually earlier (e.g. in Trinity), but we must check that it is not in fact a resubmitted run
|
808
|
+
@runner.run_list[@restart_id].restart(self)
|
809
|
+
elsif @save_for_restart and @save_for_restart.fortran_true? and (not @is_a_restart or @resubmit_id)
|
810
|
+
@restart_dir = "nc"
|
811
|
+
#if CODE_OPTIONS[:gs2] and CODE_OPTIONS[:gs2][:list]
|
812
|
+
#FileUtils.makedirs "#{@runner.root_folder}/#@restart_dir"
|
813
|
+
#else
|
814
|
+
FileUtils.makedirs @restart_dir
|
815
|
+
#end
|
816
|
+
@restart_file = "#@run_name.nc"
|
817
|
+
|
818
|
+
end
|
819
|
+
|
820
|
+
# Let Gs2 know how much wall clock time is available. avail_cpu_time is a GS2 input parameter.
|
821
|
+
@avail_cpu_time = @wall_mins * 60 if @wall_mins
|
822
|
+
|
823
|
+
# 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.
|
824
|
+
|
825
|
+
set_nprocs
|
826
|
+
|
827
|
+
|
828
|
+
if block
|
829
|
+
##### Allow the user to define their own pre-flight checks and changes
|
830
|
+
instance_eval(&block)
|
831
|
+
else
|
832
|
+
######### Check for errors and inconsistencies
|
833
|
+
check_parameters
|
834
|
+
#########
|
835
835
|
end
|
836
|
-
|
837
836
|
|
838
|
-
|
837
|
+
|
838
|
+
write_input_file
|
839
|
+
|
840
|
+
######### Generate a report using the ingen tool if possible
|
841
|
+
ingen unless block
|
842
|
+
########
|
839
843
|
end
|
840
844
|
|
841
845
|
def write_input_file
|
842
|
-
|
846
|
+
File.open(@run_name + ".in", 'w'){|file| file.puts input_file_text}
|
843
847
|
end
|
844
848
|
|
845
849
|
def set_nprocs
|
846
850
|
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
851
|
+
if (nprocs_in = @nprocs) =~ /^x/
|
852
|
+
max = max_nprocs_no_x
|
853
|
+
nodes = 0
|
854
|
+
@nprocs = "#{nodes}#{nprocs_in}"
|
855
|
+
loop do
|
856
|
+
nodes += 1
|
857
|
+
@nprocs = "#{nodes}#{nprocs_in}"
|
858
|
+
if actual_number_of_processors > max
|
859
|
+
nodes -= 1
|
860
|
+
@nprocs = "#{nodes}#{nprocs_in}"
|
861
|
+
break
|
862
|
+
end
|
863
|
+
end
|
864
|
+
end
|
861
865
|
end
|
862
866
|
|
863
867
|
def actual_number_of_processors
|
864
868
|
raise "Please specify the processor layout using the -n or (n:) option" unless @nprocs
|
865
|
-
|
869
|
+
@nprocs.split('x').map{|n| n.to_i}.inject(1){|ntot, n| ntot*n}
|
866
870
|
end
|
867
871
|
|
868
872
|
alias :anop :actual_number_of_processors
|
869
873
|
|
870
874
|
def approximate_grid_size
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
875
|
+
case @grid_option
|
876
|
+
when "box"
|
877
|
+
(2*(@nx-1)/3+1).to_i * (@naky||(@ny-1)/3+1).to_i * @ntheta * (2 * @ngauss + @ntheta/2).to_i * @negrid * 2 * @nspec
|
878
|
+
else
|
879
|
+
@ntheta * (2 * @ngauss + @ntheta/2).to_i * @negrid * 2 * @nspec
|
880
|
+
end
|
877
881
|
end
|
878
882
|
|
879
883
|
alias :agridsze :approximate_grid_size
|
@@ -882,51 +886,51 @@ alias :agridsze :approximate_grid_size
|
|
882
886
|
# can be parallelized (i.e. excluding ntheta)
|
883
887
|
#
|
884
888
|
def parallelizable_meshpoints
|
885
|
-
|
889
|
+
approximate_grid_size / ntheta
|
886
890
|
end
|
887
891
|
|
888
892
|
# Gives a guess as to the maximum number of nodes which can be
|
889
893
|
# can be utilized on the current system
|
890
894
|
#
|
891
895
|
def estimated_nodes
|
892
|
-
|
896
|
+
parallelizable_meshpoints / max_ppn
|
893
897
|
end
|
894
898
|
|
895
899
|
alias :estnod :estimated_nodes
|
896
900
|
|
897
|
-
|
901
|
+
|
898
902
|
|
899
903
|
|
900
904
|
def parameter_string
|
901
|
-
|
905
|
+
return "#{@run_name}.in"
|
902
906
|
end
|
903
907
|
|
904
908
|
|
905
909
|
def self.list_code_commands
|
906
|
-
|
910
|
+
puts (methods - Run.methods).sort
|
907
911
|
end
|
908
912
|
|
909
913
|
def self.add_variable_to_namelist(namelist, var, value)
|
910
|
-
|
911
|
-
|
914
|
+
var = :stir_ + var if namelist == :stir
|
915
|
+
super(namelist, var, value)
|
912
916
|
end
|
913
917
|
|
914
918
|
def input_file_header
|
915
|
-
|
916
|
-
|
919
|
+
run_namelist_backwards_compatibility
|
920
|
+
<<EOF
|
917
921
|
!==============================================================================
|
918
|
-
!
|
922
|
+
! GS2 INPUT FILE automatically generated by CodeRunner
|
919
923
|
!==============================================================================
|
920
924
|
!
|
921
|
-
! GS2 is a gyrokinetic flux tube initial value turbulence code
|
925
|
+
! GS2 is a gyrokinetic flux tube initial value turbulence code
|
922
926
|
! which can be used for fusion or astrophysical plasmas.
|
923
|
-
!
|
924
|
-
! See http://gyrokinetics.sourceforge.net
|
925
927
|
!
|
926
|
-
!
|
927
|
-
!
|
928
|
+
! See http://gyrokinetics.sourceforge.net
|
929
|
+
!
|
930
|
+
! CodeRunner is a framework for the automated running and analysis
|
931
|
+
! of large simulations.
|
928
932
|
!
|
929
|
-
!
|
933
|
+
! See http://coderunner.sourceforge.net
|
930
934
|
!
|
931
935
|
! Created on #{Time.now.to_s}
|
932
936
|
! by CodeRunner version #{CodeRunner::CODE_RUNNER_VERSION.to_s}
|
@@ -937,7 +941,7 @@ EOF
|
|
937
941
|
end
|
938
942
|
|
939
943
|
def self.defaults_file_header
|
940
|
-
|
944
|
+
<<EOF1
|
941
945
|
######################################################################
|
942
946
|
# Automatically generated defaults file for GS2 CodeRunner module #
|
943
947
|
# #
|
@@ -956,96 +960,96 @@ end
|
|
956
960
|
# Customize this method from Run::FortranNamelist by saying when diagnostics are not switched on.
|
957
961
|
|
958
962
|
#def namelist_text(namelist, enum = nil)
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
##
|
968
|
-
|
969
|
-
##
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
## #
|
983
|
-
|
984
|
-
|
963
|
+
#hash = rcp.namelists[namelist]
|
964
|
+
#text = ""
|
965
|
+
#ext = enum ? "_#{enum}" : ""
|
966
|
+
#text << "!#{'='*30}\n!#{hash[:description]} #{enum} \n!#{'='*30}\n" if hash[:description]
|
967
|
+
#text << "&#{namelist}#{ext}\n"
|
968
|
+
#hash[:variables].each do |var, var_hash|
|
969
|
+
#code_var = (var_hash[:code_name] or var)
|
970
|
+
#cr_var = var+ext.to_sym
|
971
|
+
## ep cr_var, namelist
|
972
|
+
#if send(cr_var) and (not var_hash[:should_include] or eval(var_hash[:should_include]))
|
973
|
+
## var_hash[:tests].each{|tst| eval(tst).test(send(cr_var), cr_var)}
|
974
|
+
#if String::FORTRAN_BOOLS.include? send(cr_var) # var is a Fortran Bool, not really a string
|
975
|
+
#output = send(cr_var).to_s
|
976
|
+
#elsif (v = send(cr_var)).kind_of? Complex
|
977
|
+
#output = "(#{v.real}, #{v.imag})"
|
978
|
+
#else
|
979
|
+
#output = send(cr_var).inspect
|
980
|
+
#end
|
981
|
+
#text << " #{code_var} = #{output} #{var_hash[:description] ? "! #{var_hash[:description]}": ""}\n"
|
982
|
+
#elsif namelist == :gs2_diagnostics_knobs or namelist == :diagnostics
|
983
|
+
#text << " ! #{code_var} not specified --- #{var_hash[:description]}\n"
|
984
|
+
#end
|
985
|
+
#end
|
986
|
+
## # end
|
987
|
+
#text << "/\n\n"
|
988
|
+
#text
|
985
989
|
#end
|
986
990
|
|
987
991
|
@namelists_to_print_not_specified = [:gs2_diagnostics_knobs, :diagnostics]
|
988
992
|
|
989
993
|
# def self.add_code_var
|
990
|
-
#
|
991
|
-
#
|
992
|
-
#
|
993
|
-
#
|
994
|
-
#
|
995
|
-
#
|
996
|
-
#
|
994
|
+
# rcp.namelists.each do |namelist, hash|
|
995
|
+
# hash[:variables].each do |var, var_hash|
|
996
|
+
# p var
|
997
|
+
# var_hash[:code_name] = var_hash[:gs2_name] if var_hash[:gs2_name]
|
998
|
+
# end
|
999
|
+
# end
|
1000
|
+
# save_namelists
|
997
1001
|
# end
|
998
|
-
|
999
|
-
|
1002
|
+
|
1003
|
+
|
1000
1004
|
def update_physics_parameters_from_miller_input_file(file)
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1005
|
+
hash = self.class.parse_input_file(file)
|
1006
|
+
hash[:parameters].each do |var, val|
|
1007
|
+
set(var,val)
|
1008
|
+
end
|
1009
|
+
hash[:theta_grid_parameters].each do |var, val|
|
1010
|
+
next if [:ntheta, :nperiod].include? var
|
1011
|
+
set(var, val)
|
1012
|
+
end
|
1013
|
+
hash[:dist_fn_knobs].each do |var, val|
|
1014
|
+
next unless [:g_exb].include? var
|
1015
|
+
set(var, val)
|
1016
|
+
end
|
1017
|
+
hash[:theta_grid_eik_knobs].each do |var, val|
|
1018
|
+
next unless [:s_hat_input, :beta_prime_input].include? var
|
1019
|
+
set(var, val)
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
hash[:species_parameters_2].each do |var, val|
|
1023
|
+
#next unless [:s_hat_input, :beta_prime_input].include? var
|
1024
|
+
set((var.to_s + '_2').to_sym, val)
|
1025
|
+
end
|
1026
|
+
hash[:species_parameters_1].each do |var, val|
|
1027
|
+
#next unless [:s_hat_input, :beta_prime_input].include? var
|
1028
|
+
set((var.to_s + '_1').to_sym, val)
|
1029
|
+
end
|
1026
1030
|
end
|
1027
1031
|
|
1028
1032
|
|
1029
1033
|
|
1030
1034
|
def renew_info_file
|
1031
|
-
|
1035
|
+
Dir.chdir(@directory){make_info_file("#@run_name.in")}
|
1032
1036
|
end
|
1033
|
-
|
1037
|
+
|
1034
1038
|
# 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.
|
1035
|
-
|
1039
|
+
|
1036
1040
|
def run_heuristic_analysis
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1041
|
+
ep 'run_heuristic_analysis', Dir.pwd
|
1042
|
+
infiles = Dir.entries.grep(/^[^\.].*\.in$/)
|
1043
|
+
ep infiles
|
1044
|
+
raise CRMild.new('No input file') unless infiles[0]
|
1045
|
+
raise CRError.new("More than one input file in this directory: \n\t#{infiles}") if infiles.size > 1
|
1046
|
+
input_file = infiles[0]
|
1047
|
+
ep 'asdf'
|
1048
|
+
@nprocs ||= "1"
|
1049
|
+
@executable ||= "/dev/null"
|
1050
|
+
make_info_file(input_file, false)
|
1047
1051
|
end
|
1048
|
-
|
1052
|
+
|
1049
1053
|
@source_code_subfolders = ['utils', 'geo', 'diagnostics']
|
1050
1054
|
|
1051
1055
|
attr_accessor :iphi00, :saturation_time #Necessary for back. comp. due to an old bug
|
@@ -1053,122 +1057,122 @@ attr_accessor :iphi00, :saturation_time #Necessary for back. comp. due to an old
|
|
1053
1057
|
folder = File.dirname(File.expand_path(__FILE__)) # i.e. the directory this file is in
|
1054
1058
|
|
1055
1059
|
SPECIES_DEPENDENT_NAMELISTS = eval(File.read(folder + '/species_dependent_namelists.rb'), binding, folder + '/species_dependent_namelists.rb')
|
1056
|
-
#
|
1060
|
+
#
|
1057
1061
|
SPECIES_DEPENDENT_VARIABLES_WITH_HELP = SPECIES_DEPENDENT_NAMELISTS.values.inject({}) do |hash, namelist_hash|
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
+
namelist_hash[:variables].each do |var, var_hash|
|
1063
|
+
hash[var] = var_hash[:help]
|
1064
|
+
end
|
1065
|
+
hash
|
1062
1066
|
end
|
1063
1067
|
|
1064
1068
|
SPECIES_DEPENDENT_VARIABLES = SPECIES_DEPENDENT_VARIABLES_WITH_HELP.keys
|
1065
1069
|
SPECIES_DEPENDENT_VARIABLES.each{|var| attr_accessor var} # for backwards compatibility
|
1066
1070
|
|
1067
|
-
['i', 'e'].each do |n|
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
+
['i', 'e'].each do |n|
|
1072
|
+
SPECIES_DEPENDENT_VARIABLES_WITH_HELP.each do |name, help|
|
1073
|
+
attr_accessor name + "_#{n}".to_sym #for backwards compatibility
|
1074
|
+
end
|
1071
1075
|
end
|
1072
1076
|
|
1073
1077
|
old_vars = %w[
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1078
|
+
:TiTe
|
1079
|
+
:Rmaj
|
1080
|
+
:R_geo
|
1081
|
+
:invLp_input
|
1082
|
+
:D_hypervisc
|
1083
|
+
:D_hyperres
|
1084
|
+
:D_hyper
|
1085
|
+
:C_par
|
1086
|
+
:C_perp
|
1083
1087
|
].map{|n| n.to_s.sub(/^:/, '').to_sym}
|
1084
1088
|
|
1085
1089
|
old_vars.each do |var|
|
1086
|
-
|
1087
|
-
|
1090
|
+
alias_method(var, var.to_s.downcase.to_sym)
|
1091
|
+
alias_method("#{var}=".to_sym, "#{var.downcase}=".to_sym)
|
1088
1092
|
end
|
1089
1093
|
|
1090
1094
|
|
1091
1095
|
|
1092
1096
|
|
1093
1097
|
def run_namelist_backwards_compatibility
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
+
SPECIES_DEPENDENT_VARIABLES.each do |var|
|
1099
|
+
set(var + "_1".to_sym, (send(var + "_1".to_sym) or send(var + "_i".to_sym) or send(var)))
|
1100
|
+
set(var + "_2".to_sym, (send(var + "_2".to_sym) or send(var + "_e".to_sym)))
|
1101
|
+
end
|
1098
1102
|
end
|
1099
1103
|
|
1100
1104
|
|
1101
1105
|
def stop
|
1102
|
-
|
1106
|
+
`touch #@directory/#@run_name.stop`
|
1103
1107
|
end
|
1104
1108
|
|
1105
1109
|
def vim_output
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1110
|
+
system "vim -Ro #{output_file} #{error_file} #@directory/#@run_name.error #@directory/#@run_name.out "
|
1111
|
+
end
|
1112
|
+
alias :vo :vim_output
|
1109
1113
|
def vim_stdout
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1114
|
+
system "vim -Ro #{output_file} "
|
1115
|
+
end
|
1116
|
+
alias :vo1 :vim_stdout
|
1117
|
+
def plot_efit_file
|
1118
|
+
Dir.chdir(@directory) do
|
1119
|
+
text = File.read(@eqfile)
|
1120
|
+
text_lines = text.split("\n")
|
1121
|
+
first_line = text_lines[0].split(/\s+/)
|
1122
|
+
second_line = text_lines[1].split(/\s+/)
|
1123
|
+
nr = first_line[-2].to_i
|
1124
|
+
nz = first_line[-1].to_i
|
1125
|
+
rwidth = second_line[1].to_f
|
1126
|
+
zwidth = second_line[2].to_f
|
1127
|
+
rmag = second_line[3].to_f
|
1128
|
+
nlines = (nr.to_f/5.0).ceil
|
1129
|
+
nlines_psi = ((nr*nz).to_f/5.0).ceil
|
1130
|
+
start = 5
|
1131
|
+
f = text_lines[start...(start+=nlines)].join(" ").split(nil).map{|s| s.to_f}.to_gslv
|
1132
|
+
pres = text_lines[(start)...(start += nlines)].join(" ").split(nil).map{|s| s.to_f}.to_gslv
|
1133
|
+
_ = text_lines[(start)...(start += nlines)].join(" ").split(nil).map{|s| s.to_f}.to_gslv
|
1134
|
+
_ffprime = text_lines[(start)...(start+= nlines)].join(" ").split(nil).map{|s| s.to_f}.to_gslv
|
1135
|
+
psi = text_lines[(start)...(start += nlines_psi)].join(" ")
|
1136
|
+
q = text_lines[(start)...(start += nlines)].join(" ").split(nil).map{|s| s.to_f}.to_gslv
|
1137
|
+
nbound = text_lines[start...start+=1].join(" ").to_i
|
1138
|
+
rz = text_lines[(start)...(start += nbound*2)].join(" ").split(/\s+/)
|
1139
|
+
rz.shift
|
1140
|
+
rbound, zbound, _ = rz.inject([[], [], true]){|arr,val| arr[2] ? [arr[0].push(val), arr[1], false] : [arr[0], arr[1].push(val), true]}
|
1141
|
+
#rbound.shift
|
1142
|
+
|
1143
|
+
psi = psi.split(/\s+/)
|
1144
|
+
psi.shift
|
1145
|
+
psi.map!{|v| v.to_f}
|
1146
|
+
psi_arr = SparseTensor.new(2)
|
1147
|
+
k = 0
|
1148
|
+
for i in 0...nz
|
1149
|
+
for j in 0...nr
|
1150
|
+
psi_arr[j,i] = psi[k]
|
1151
|
+
k+=1
|
1152
|
+
end
|
1153
|
+
end
|
1154
|
+
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}])
|
1155
|
+
kit.gp.contour = ""
|
1156
|
+
kit.gp.view = "map"
|
1157
|
+
#kit.gp.nosurface = ""
|
1158
|
+
kit.gp.cntrparam = "levels 20"
|
1159
|
+
kit.data[0].gp.with = 'l'
|
1160
|
+
kit.data[1].gp.with = 'l lw 2 nocontours'
|
1161
|
+
kit.gnuplot
|
1162
|
+
|
1163
|
+
kit2 = GraphKit.quick_create([pres/pres.max],[f/f.max],[q/q.max])
|
1164
|
+
kit2.data[0].title = 'Pressure/Max Pressure'
|
1165
|
+
kit2.data[1].title = 'Poloidal current function/Max poloidal current function'
|
1166
|
+
kit2.data[2].title = 'Safety factor/Max Safety factor'
|
1167
|
+
kit2.gnuplot
|
1168
|
+
|
1169
|
+
|
1170
|
+
|
1171
|
+
#p ['f', f, 'p', pres, 'ffprime', ffprime, 'nlines', nlines, 'psi', psi, 'q', q, 'nbound', nbound, 'rbound', rbound, 'zbound', zbound]
|
1172
|
+
|
1173
|
+
|
1174
|
+
end
|
1175
|
+
end
|
1172
1176
|
|
1173
1177
|
#This function will handle running the correlation analysis and writing the results to a NetCDF file.
|
1174
1178
|
#Cases need to be handled differently since perp, par and full are just subsets of the full correlation function
|
@@ -1188,14 +1192,14 @@ end
|
|
1188
1192
|
# why the perp/par/full splitting is implemented, allowing one dimension to be taken out essentially.
|
1189
1193
|
def correlation_analysis(options={})
|
1190
1194
|
|
1191
|
-
#Sanity checks:
|
1192
|
-
#Cannot only have one bin since require difference between bins for index calculation
|
1195
|
+
#Sanity checks:
|
1196
|
+
#Cannot only have one bin since require difference between bins for index calculation
|
1193
1197
|
if options[:nbins_array].include?1
|
1194
1198
|
raise('Cannot have only one bin in nbins_array. Minuimum is two.')
|
1195
1199
|
end
|
1196
1200
|
#Thetamin shouldn't be equal to thetamax to avoid possibili
|
1197
1201
|
#
|
1198
|
-
|
1202
|
+
|
1199
1203
|
case options[:correlation_type]
|
1200
1204
|
when 'perp', 'par', 'full'
|
1201
1205
|
gsl_tensor = field_correlation_gsl_tensor(options)
|
@@ -1240,7 +1244,7 @@ end
|
|
1240
1244
|
file = NumRu::NetCDF.create(@run_name + "_correlation_analysis_#{options[:correlation_type]}.nc")
|
1241
1245
|
ydim = file.def_dim('x',shape[0])
|
1242
1246
|
xdim = file.def_dim('y',shape[1])
|
1243
|
-
zdim = file.def_dim('z',shape[2])
|
1247
|
+
zdim = file.def_dim('z',shape[2])
|
1244
1248
|
tdim = file.def_dim('t',shape[3])
|
1245
1249
|
end
|
1246
1250
|
file.redef
|
@@ -1255,9 +1259,9 @@ end
|
|
1255
1259
|
end
|
1256
1260
|
end
|
1257
1261
|
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1262
|
+
def input_file_extension
|
1263
|
+
'.in'
|
1264
|
+
end
|
1261
1265
|
|
1262
1266
|
#This function will interpolate and output either phi or density at the outboard midplane
|
1263
1267
|
#on a 40x40 grid appropriate to analyse as experimental data. It called as a run_command
|
@@ -1304,8 +1308,8 @@ end
|
|
1304
1308
|
raise 'Need to specify amin AND v_ref options when specifying omega to move to LAB frame!'
|
1305
1309
|
end
|
1306
1310
|
if options[:output_box_size] and options[:output_box_size].kind_of?Array
|
1307
|
-
|
1308
|
-
|
1311
|
+
_r_box_size = options[:output_box_size][0] # EGH These variables are marked as unused... are they used anywhere?
|
1312
|
+
_z_box_size = options[:output_box_size][1]
|
1309
1313
|
#else
|
1310
1314
|
# raise 'Option output_box_size must be specified (in units of amin) and must be an Array.'
|
1311
1315
|
end
|
@@ -1321,7 +1325,7 @@ end
|
|
1321
1325
|
options[:t_index] = t_index_beg
|
1322
1326
|
kit = field_real_space_poloidal_plane_graphkit(options)
|
1323
1327
|
x = kit.data[0].x.data
|
1324
|
-
|
1328
|
+
_y = kit.data[0].y.data
|
1325
1329
|
z = kit.data[0].z.data
|
1326
1330
|
|
1327
1331
|
#Set up NetCDf file
|
@@ -1340,13 +1344,13 @@ end
|
|
1340
1344
|
|
1341
1345
|
#Loop over time, load field as function of space at each time index, write to file
|
1342
1346
|
for i in t_index_beg...t_index_end #inclusive of end
|
1343
|
-
|
1344
|
-
|
1347
|
+
Terminal.erewind(1) #go back one line in terminal
|
1348
|
+
eputs sprintf("Writing time index = %d of %d#{Terminal::CLEAR_LINE}", i, t_index_end-t_index_beg+1) #clear line and print time index
|
1345
1349
|
options[:t_index] = i
|
1346
1350
|
#Need to test whether omega is specified to change torphi at each time step. If not, do nothing since torphi must be
|
1347
1351
|
#set to a value to call the graphkit below
|
1348
1352
|
if options[:omega]
|
1349
|
-
options[:torphi] = omega * (gsl_vector(:t)[i] - gsl_vector(:t)[0]) * (amin/v_ref)
|
1353
|
+
options[:torphi] = omega * (gsl_vector(:t)[i] - gsl_vector(:t)[0]) * (amin/v_ref)
|
1350
1354
|
end
|
1351
1355
|
kit = field_real_space_poloidal_plane_graphkit(options)
|
1352
1356
|
t_var.put(gsl_vector(:t)[i], 'index'=>[i-t_index_beg]) #Write time to unlimited time NetCDF variable
|
@@ -1355,7 +1359,7 @@ end
|
|
1355
1359
|
file.close
|
1356
1360
|
|
1357
1361
|
#Ignore this until interpolation issue is sorted
|
1358
|
-
=begin
|
1362
|
+
=begin
|
1359
1363
|
#**************************
|
1360
1364
|
# Set up new regular grid *
|
1361
1365
|
#**************************
|
@@ -1372,14 +1376,14 @@ end
|
|
1372
1376
|
z_reg[i,j] = z_vec_reg[j]
|
1373
1377
|
end
|
1374
1378
|
end
|
1375
|
-
|
1379
|
+
|
1376
1380
|
#************************************************
|
1377
1381
|
# Find the field at every point on regular grid *
|
1378
1382
|
#************************************************
|
1379
1383
|
#To evaluate field on a regular grid given the field on an irregular grid, need to interpolate. The rubygem
|
1380
|
-
#gsl_extras contains an interpolation routine called ScatterInterp which does exactly this based on a
|
1384
|
+
#gsl_extras contains an interpolation routine called ScatterInterp which does exactly this based on a
|
1381
1385
|
#'Radial Basis Function' method.
|
1382
|
-
|
1386
|
+
|
1383
1387
|
#Have R, Z, and field on an irregular grid in the form of matrices. ScatterInterp only takes in GSL vectors
|
1384
1388
|
#so simply convert these matrices to vectors (of size row*col) since the order of the pts don't matter.
|
1385
1389
|
x_vec = GSL::Vector.alloc(x.shape[0]*x.shape[1])
|
@@ -1402,15 +1406,15 @@ end
|
|
1402
1406
|
for j in 0...z_vec_reg.size
|
1403
1407
|
field_reg[i,j] = interp.eval(x_vec_reg[i], z_vec_reg[j])
|
1404
1408
|
end
|
1405
|
-
end
|
1409
|
+
end
|
1406
1410
|
|
1407
|
-
|
1408
|
-
|
1411
|
+
kit = GraphKit.quick_create([x_vec_reg, z_vec_reg, field_reg])
|
1412
|
+
#kit2 = GraphKit.quick_create([x_vec, z_vec, field_vec])
|
1409
1413
|
=end
|
1410
1414
|
|
1411
1415
|
end
|
1412
|
-
|
1413
|
-
|
1416
|
+
end # class GS2
|
1417
|
+
# For backwards compatibility
|
1414
1418
|
|
1415
1419
|
Gs2BoxNtRun = Gs2CycloneRun = Gs2BoxCollisionalRun = Gs2Jet42982Run = Gs2ArtunRun = Gs2LinskerRun = Gs2BarnesLinskerRun = Gs2BoxMovieRun = Gs2Run = Gs2
|
1416
1420
|
end # class CodeRunner
|
@@ -1420,56 +1424,56 @@ end # class CodeRunner
|
|
1420
1424
|
|
1421
1425
|
class Float
|
1422
1426
|
def <=>(other) # necessary because of netcdf quirks
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1427
|
+
|
1428
|
+
d = (self - other)
|
1429
|
+
if d.abs / (self.abs + 1) < 1e-10
|
1430
|
+
return 0
|
1431
|
+
else
|
1432
|
+
return (d / d.abs).to_i
|
1433
|
+
end
|
1430
1434
|
end
|
1431
1435
|
def ==(other)
|
1432
|
-
|
1433
|
-
|
1436
|
+
return false unless other.kind_of? Numeric
|
1437
|
+
return (self - other).abs < 1e-14
|
1434
1438
|
end
|
1435
|
-
end
|
1436
|
-
|
1439
|
+
end
|
1440
|
+
|
1437
1441
|
class Hash
|
1438
|
-
|
1439
|
-
#
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
#
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
#
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1442
|
+
|
1443
|
+
# puts self
|
1444
|
+
|
1445
|
+
def convert_to_index(run, *names)
|
1446
|
+
if self[:strongest_non_zonal_mode]
|
1447
|
+
ky_element, kx_element = run.gsl_matrix('spectrum_over_ky_over_kx', no_zonal: true).max_index
|
1448
|
+
p self[:kx_index] = kx_element + 1
|
1449
|
+
p self[:ky_index] = ky_element + 1
|
1450
|
+
self[:strongest_non_zonal_mode] = false
|
1451
|
+
end
|
1452
|
+
raise "No names specified" if names.size == 0
|
1453
|
+
|
1454
|
+
|
1455
|
+
# ep run
|
1456
|
+
names.each do |name|
|
1457
|
+
if name == :kx
|
1458
|
+
if lkx = self[:lagrangian_kx]
|
1459
|
+
self[:lagrangian_kx_index] = list(:kx).key(lkx)
|
1460
|
+
end
|
1461
|
+
if lkxi = self[:lagrangian_kx_index] ||= self[:lkx_index]
|
1462
|
+
self[:kx_index] = run.eulerian_kx_index(kx_index: lkxi, ky_index: self[:ky_index], t_index: self[:t_index])
|
1463
|
+
end
|
1464
|
+
end
|
1465
|
+
|
1466
|
+
#ep 'name', name
|
1467
|
+
self[:ky_index] = 1 if name == :ky and run.grid_option == "single"
|
1468
|
+
self[:kx_index] = 1 if name == :kx and run.grid_option == "single"
|
1469
|
+
# ep run.list(name)
|
1470
|
+
self[name + :_index] ||= run.list(name).key(self[name]) || (raise ("#{name} not specified"))
|
1471
|
+
end
|
1472
|
+
|
1473
|
+
end
|
1474
|
+
def setup_time_window
|
1475
|
+
self[:t_index_window] ||= [self[:t_index],self[:t_index]] if self[:t_index]
|
1476
|
+
self[:begin_element], self[:end_element] = (self[:t_index_window] ? self[:t_index_window].map{|ind| ind - 1} : [0, -1])
|
1477
|
+
end
|
1474
1478
|
|
1475
1479
|
end
|