gs2crmod 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +20 -0
- data/README.md +4 -0
- data/README.rdoc +19 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/ext/extconf.rb +9 -0
- data/ext/gs2crmod_ext.c +366 -0
- data/gs2crmod.gemspec +98 -0
- data/include/gs2crmod_ext.h +58 -0
- data/lib/gs2crmod/astrogk/astrogk.rb +201 -0
- data/lib/gs2crmod/astrogk/calculations.rb +57 -0
- data/lib/gs2crmod/astrogk/check_convergence.rb +7 -0
- data/lib/gs2crmod/astrogk/deleted_variables.rb +76 -0
- data/lib/gs2crmod/astrogk/graphs.rb +13 -0
- data/lib/gs2crmod/astrogk/gsl_data.rb +13 -0
- data/lib/gs2crmod/astrogk/gsl_tools.rb +182 -0
- data/lib/gs2crmod/astrogk/ingen.rb +18 -0
- data/lib/gs2crmod/astrogk/input_file_tools.rb +7 -0
- data/lib/gs2crmod/astrogk/namelist_tools.rb +14 -0
- data/lib/gs2crmod/astrogk/namelists.rb +2800 -0
- data/lib/gs2crmod/astrogk/properties.rb +17 -0
- data/lib/gs2crmod/astrogk/species_dependent_namelists.rb +228 -0
- data/lib/gs2crmod/astrogk/test_gs2.rb +231 -0
- data/lib/gs2crmod/astrogk.rb +200 -0
- data/lib/gs2crmod/calculations.rb +780 -0
- data/lib/gs2crmod/check_convergence.rb +179 -0
- data/lib/gs2crmod/deleted_variables.rb +916 -0
- data/lib/gs2crmod/graphs.rb +1899 -0
- data/lib/gs2crmod/graphs_rdoc.rb +556 -0
- data/lib/gs2crmod/gs2.rb +1143 -0
- data/lib/gs2crmod/gsl_data.rb +1181 -0
- data/lib/gs2crmod/gsl_data_3d.rb +705 -0
- data/lib/gs2crmod/gsl_tools.rb +187 -0
- data/lib/gs2crmod/ingen.rb +218 -0
- data/lib/gs2crmod/namelists.rb +5142 -0
- data/lib/gs2crmod/properties.rb +22 -0
- data/lib/gs2crmod/species_dependent_namelists.rb +228 -0
- data/lib/gs2crmod/test_gs2.rb +231 -0
- data/lib/gs2crmod.rb +2 -0
- data/lib/gs2crmod_extension.rb +1 -0
- data/test/helper.rb +18 -0
- data/test/test_gs2crmod.rb +7 -0
- metadata +176 -0
data/lib/gs2crmod/gs2.rb
ADDED
@@ -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
|