')
2.times{vars.shift}
vars.each do |var_text|
ep var_text
name, type, default, help = var_text.split(' | ')
name, type, default = [name, type, default].map do |str|
str.sub(/^\s+/, '').sub(/\s+\Z/, '').gsub(/<[^>]+>/, '')
end
name = name.sub(/^\s+/, '').sub(/\s+\Z/, '').gsub(/<[^>]+>/, '').to_sym
ep 'name', name
#begin
#p name, type, help, default, rcp.namelists[namelist][:variables][name][:autoscanned_defaults]
#rescue => err
#p err
#p namelist, name
#end
if rcp.namelists[namelist][:variables][name]
names = [name]
else
names = name.to_s.split(/\s*,\s*/).map{|n| n.to_sym}
end
ep 'names', names
help = help.gsub(/<\/?[bi]>/, '').gsub(/ /, '**')
names.each do |name|
unless rcp.namelists[namelist][:variables][name]
var = rcp.namelists[namelist][:variables].keys.find{|var| rcp.namelists[namelist][:variables][var][:code_name] == name}
raise "Can't find #{name.inspect} in #{namelist}" unless var
name = var
end
begin
sync_variable_help(namelist, name, help)
if rcp.namelists[namelist][:variables][name][:help] =~ /<[^>]+>/
edit_variable_help(namelist, name)
end
rescue => err
p namelist, name
raise err
end
end
end
end
end
# This method takes the help written into this module for the various input parameters and writes it in a format suitable for the mediawiki page on input parameters.
def self.write_mediawiki_documentation(filename = "#{rcp.code}_mediawiki.txt")
File.open(filename, 'w') do |file|
file.puts < or the tags'''. Don't edit type/default information (or this introduction) as it will not be kept.
=Namelists=
Each #{rcp.code} module is controlled by its own namelist. For typical applications, not all 32+ namelists should appear in a single file. For a run called runname, this file should be called runname.in. In most cases, defaults are loaded for each namelist element, so that if a namelist or element does not appear, the run will not automatically stop. (It may still be forced to stop if the defaults are incompatible with your other choices.) The namelists and defaults appear below.
EOF
rcp.namelists.each do |namelist, hash|
file.puts "==#{namelist}=="
file.puts "#{hash[:help]}"
file.puts "\n{| border=\"2\" cellpadding=\"5\"\n! Name !! Type !! Def !! CR Name !! Description \n|-"
hash[:variables].keys.sort.each do |var|
var_hash = hash[:variables][var]
#puts "==='''[[#{(var_hash[:code_name] or var)}]]'''==="
file.puts "|-\n|'''[[#{(var_hash[:code_name] or var)}]]''' || #{var_hash[:type]} || #{(var_hash[:autoscanned_defaults]||[]).map{|v| v.to_s}.join(",")} || #{var} \n|\n* #{hash[:variables][var][:help]} "
#puts "''Type'': #{var_hash[:type]} "
#puts "''Autoscanned Defaults'': #{var_hash[:autoscanned_defaults]} "
#puts "''CodeRunner name'': #{var} "
#puts "\n", "#{hash[:variables][var][:help]}".sub(/\A\s+/, '') if hash[:variables][var][:help]
#puts " #{(var_hash[:code_name] or var)} Properties:"
end
file.puts "|}"
end
end #file.open
end
# This method takes the help written into this module for the various input parameters and writes it in a format suitable for the mediawiki page on input parameters.
#def self.write_mediawiki_documentation
#rcp.namelists.each do |namelist, hash|
#puts "==#{namelist}=="
#puts hash[:help]
#hash[:variables].keys.sort.each do |var|
#var_hash = hash[:variables][var]
##puts "==='''[[#{(var_hash[:code_name] or var)}]]'''==="
#puts "* '''[[#{(var_hash[:code_name] or var)}]]''': #{hash[:variables][var][:help]}"
#puts "** Properties: \n*** ''Type'': #{var_hash[:type]} \n*** ''Autoscanned Defaults'': #{var_hash[:autoscanned_defaults]} \n*** ''CodeRunner Name'': #{var}"
##puts "''Type'': #{var_hash[:type]} "
##puts "''Autoscanned Defaults'': #{var_hash[:autoscanned_defaults]} "
##puts "''CodeRunner name'': #{var} "
##puts "\n", "#{hash[:variables][var][:help]}".sub(/\A\s+/, '') if hash[:variables][var][:help]
##puts " #{(var_hash[:code_name] or var)} Properties:"
#end
#end
#end
#def self.write_wiki_documentation
#rcp.namelists.each do |namelist, hash|
#puts "==#{namelist}=="
#puts hash[:help]
#hash[:variables].keys.sort.each do |var|
#var_hash = hash[:variables][var]
##puts "==='''[[#{(var_hash[:code_name] or var)}]]'''==="
#puts "'''[[#{(var_hash[:code_name] or var)}]]'''"
#puts "\n''Type'': #{var_hash[:type]} ''Autoscanned Defaults'': #{var_hash[:autoscanned_defaults]} ''CodeRunner name'': #{var}"
#puts " #{"#{hash[:variables][var][:help]}".sub(/\*{1}/, "\n").sub(/\*\*/, "*")}"
##puts "''Type'': #{var_hash[:type]} "
##puts "''Autoscanned Defaults'': #{var_hash[:autoscanned_defaults]} "
##puts "''CodeRunner name'': #{var} "
##puts "\n", "#{hash[:variables][var][:help]}".sub(/\A\s+/, '') if hash[:variables][var][:help]
##puts " #{(var_hash[:code_name] or var)} Properties:"
#end
#end
#end
# This method scans the source code in the given folder and tries to find what value each parameter will be given if the value is not specified in the input file. It is about as subtle as a sledgehammer and doesn't always find the right answer, but in general is pretty good. The values it finds are stored in the name list hashes as :autoscanned_defaults
def self.update_defaults_from_source_code(source_code_folder = ARGV[-1])
eputs "Scanning - this takes a while..."
# [[, File.dirname(__FILE__) + '/namelists.rb']].each do |namelists, file|
namelists = rcp.namelists
# file = File.dirname(__FILE__) + '/namelists.rb'
string = ""
Dir.chdir(source_code_folder) do
namelists.each do |namelist, hash|
hash[:variables].each do |var, varhash|
string += `grep -h -E '^[ \t]*#{(varhash[:code_name] or var)}[ \t]*=' *`
string += `grep -h -E '^[ \t]*#{(varhash[:code_name] or var)}[ \t]*=' */*`
end
end
end
# string.gsub!(/^.+?:/, '') # Get rid of file names from grep
#File.open('found1','w'){|f| f.puts string}
# exit
defs = scan_text_for_variables(string)
#File.open('found2','w'){|f| f.puts defs.pretty_inspect}
# exit
namelists.each do |namelist, hash|
hash[:variables].each do |var, varhash|
p var if var == :nwrite
values = defs.find_all{|(v, df)| v == (varhash[:code_name] or var)}.map{|(v,df)| df}
values.uniq!
p values if var == :nwrite
values.delete_if{|val| val.kind_of? String} if values.find{|val| val.kind_of? Numeric}
p values if var == :nwrite
values.delete_if{|val| val.kind_of? String and not String::FORTRAN_BOOLS.include? val} if values.find{|val| val.kind_of? String and String::FORTRAN_BOOLS.include? val}
p values if var == :nwrite
values.sort!
hash[:variables][var][:autoscanned_defaults] = values
# ep var, values
end
end
save_namelists
# File.open(file, 'w'){|f| f.puts namelists.pretty_inspect}
# end
end
# Given the folder where the source code resides, return a single string containing all the code
@fortran_namelist_source_file_match = /((\.f9[05])|(\.fpp))$/
def self.get_aggregated_source_code_text(source_code_folder)
#p 'source_code_folder', source_code_folder
string = ""
(rcp.source_code_subfolders.map{|f| '/' + f} + [""]).map{|f| source_code_folder + f}.each do |folder|
Dir.chdir(folder) do
Dir.entries.each do |file|
next unless file =~ rcp.fortran_namelist_source_file_match
next if file =~ /ingen/
ep file
text = File.read(file) + "\n"
text =~ /a/
string += text
end
end
end
string
end
# Find all input namelists and variables by scanning the source code
#
def self.get_namelists_and_variables_from_source_code(source)
nms = {}
all_variables_in_source = {}
namelist_declarations = {}
#source.scan(/^\s*namelist\s*\/(?\w+)\/(?(?:(?:&\s*[\n\r]+)|[^!\n\r])*)/) do
source.scan(Regexp.new("#{/^\s*namelist\s*\/\s*(?\w+)\s*\//}(?#{FORTRAN_SINGLE_LINE})")) do
namelist = $~[:namelist].to_s.downcase.to_sym
variables = $~[:variables].gsub(/!.*/, '')
eputs namelist, variables
namelist_declarations[namelist] = variables
#gets # if namelist == :collisions_knobs
next if [:stuff, :ingen_knobs].include? namelist
nms[namelist] = []
all_variables_in_source[namelist] = []
# puts variables
variables.scan(/\w+/) do
var = $~.to_s.to_sym
# (p variables, namelist; exit) if var == :following or var == :sou
all_variables_in_source[namelist].push var
next if known_code_variable?(namelist, var)
nms[namelist].push var
end
nms[namelist].uniq!
all_variables_in_source[namelist].uniq!
end
return [nms, all_variables_in_source, namelist_declarations]
end
# Try to get a sample value of the
def self.get_sample_value(source, var)
ep var
values_text = source.scan(Regexp.new("\\W#{var}\\s*=\\s*.+")).join("\n")
ep values_text
values = scan_text_for_variables(values_text).map{|(v,val)| val}
values.uniq!
# ep values if var == :nbeta
values.delete_if{|val| val.kind_of? String} if values.find{|val| val.kind_of? Numeric}
values.delete_if{|val| val.kind_of? String and not String::FORTRAN_BOOLS.include? val} if values.find{|val| val.kind_of? String and String::FORTRAN_BOOLS.include? val}
# values.sort!
# ep var
# ep values
sample_val = values[0]
if not values[0] or ( values[0].kind_of? String and not String::FORTRAN_BOOLS.include? values[0])
p source.scan(Regexp.new("^\s*(?integer|float|character|logical|real|double|complex)(?:&[\\n\\r]|.)*\\W#{var}\\W", Regexp::IGNORECASE)).uniq
p var unless $~
case $~[:type]
when /logical/
sample_val = '.false.'
when /int/
sample_val = 0
when 'real', 'float', 'double'
sample_val = 0.0
when /character/
sample_val = ""
when /complex/
sample_val = Complex(0.0, 0.0)
end
# type = Feedback.get_choice("Found the following possible values for '#{var}' in namelist '#{namelist}': #{values.inspect} but cannot determine its type. Please choose its type", ['Float', 'Integer', 'String', 'Unknown' ])
# ep type
n +=1
end
return sample_val
end
# Find unknown input variables in the source code and add them to the database of namelists
# Delete input variables which are no longer present in the source code
def self.synchronise_variables(source_code_folder = ARGV[2])
source = get_aggregated_source_code_text(source_code_folder)
nms, all_variables_in_source, namelist_declarations = get_namelists_and_variables_from_source_code(source)
# ep source.size
variables_to_delete = {}
#pp 'namelists', rcp.namelists
rcp.namelists.each do |namelist, namelist_hash|
namelist_hash[:variables].each do |variable, var_hash|
code_variable = var_hash[:code_name] || variable
unless all_variables_in_source[namelist] and all_variables_in_source[namelist].map{|var| var.to_s.downcase.to_sym}.include? code_variable.to_s.downcase.to_sym
variables_to_delete[namelist] ||= []
variables_to_delete[namelist].push variable
end
end
end
variables_to_delete.each do |namelist, var_array|
#eputs namelist_declarations[namelist]
var_array.each do |var|
p "Namelist: #{namelist} Variable: #{var}"
end
end
if variables_to_delete.find{|namelist, var_array| var_array.size > 0}
delete_old = Feedback.get_boolean("These variables are no longer present in the #{rcp.code_long} source folder. Do you wish to delete them?")
if delete_old
variables_to_delete.each do |namelist, var_array|
var_array.each do |var|
delete_variable(namelist, var)
end
end
end
end
raise "No namelists found" if nms.size == 0
eputs nms.keys.zip(nms.values.map{|vs| vs.size})
eputs "Namelists to be added to. (Press Enter)"; STDIN.gets unless ENV['CR_NON_INTERACTIVE']
n = 0
ep nms
# ep nms.values.sum
nms.values.sum.each do |var|
eputs var if variable_exists? var
end
eputs "Conflicting Variables. (Press Enter)";; STDIN.gets unless ENV['CR_NON_INTERACTIVE']
nms.each do |namelist, vars|
ep namelist
ep vars
vars.each do |var|
# next unless var == :w_antenna
sample_val = get_sample_value(source, var)
p namelist, var, sample_val
add_code_variable_to_namelist(namelist, var, sample_val)
end
end
ep n
end
#FORTRAN_SINGLE_LINE = /(?:(?:&\s*[\n\r]+)|(?:[ \t]*!.*[\n\r]+)|[^!\n\r])*/
#FORTRAN_SINGLE_LINE = /(?:(?:&\s*[\n\r]+)|(?:&[ \t]*!.*[\n\r]+)|[^!\n\r])*/
FORTRAN_SINGLE_LINE = /(?:
(?:&[ \t]*\r?\n? #continuing line with
(?:[ \t]*!.*\r?\n?)+) # multiple comments
|
(?:&\s*[\n\r]+) # continuing line
|
[^!\n\r])* # non continuing line
/x
def self.update_text_options(source_code_folder = ARGV[-1])
source = get_aggregated_source_code_text(source_code_folder)
options = {}
source.scan(/^\s*type\s*\(text_option\)\s*.*?\:\:\s*(?\w+)\s+(?(?:(?:&\s*[\n\r]+)|(?:\s*!.*[\n\r])|[^!\n\r])*)/) do
name = $~[:var]
# eputs $~ if name == "adiabaticopts"
opts = $~[:options].scan(/text_option\('([^']+)/).flatten
name = "collision_model_opts" if opts.include? "krook"
options[name] = opts
end
mapping = {}
# get_option_value &
# (ginit_option, ginitopts, ginitopt_switch, &
source.scan(/^\s*call\s*get_option_value[\s|&]*\((?\w+),[\s|&]*(?\w+)/) do
# p $~
var = $~[:var].to_sym
op = $~[:options]
op = "collision_model_opts" if var == :collision_model
mapping[var] = op
end
# pp mapping
# pp options
# string_vars = []
rcp.namelists.each do |namelist, nhash|
nhash[:variables].each do |var, varhash|
if varhash[:type] == :String
if mapping[(varhash[:code_name] or var)]
varhash[:text_options] = options[mapping[var]].uniq
# pp var, varhash
end
end
end
end
save_namelists
# ep options, string_vars, mapping
# File.open(File.dirname(__FILE__) + '/namelists.rb', 'w'){|f| f.puts NAMELISTS.pretty_inspect}
end
def self.print_doxygen_variable_documentation(variable=ARGV[2])
#rcp.variables_with_help.each do |var, help|
#next if var
#puts var, "\n", help.gsub(/*/, '-'), "\n"
#end
#["! \n", " !>" + (rcp.variables_with_help[variable.to_sym]||"").gsub(
[ " !>" + (rcp.variables_with_help[variable.to_sym]||"").gsub(
/\"].join(" ")
#/[^\A]^/, "\n !!"), "\n !!- NB, this is automatically generated documentation for an input parameter... see also the wiki page!", "\n ! "].join(" ")
end
class << self
alias :pdvd :print_doxygen_variable_documentation
# Work out which module a variable is found in
def get_variable_modules(folder=ARGV[2])
text = get_aggregated_source_code_text(folder)
#puts text
modules = {}
regex = /^\s*module\s+(\w+)((?:.|\n)*?)^\s*end\s+module\s+\g<1>/i
#regex = /^\s*module\s+(\w+\b)/i
p regex
text.scan(regex) do
p $~[1]
modules[$~[1].to_sym] = $~[2]
end
#pp modules
rcp.namelists.each do |nmlist, hash|
hash[:variables].each do |var, varhash|
#regex = Regexp.new("module\\s+(\\w+)(?:.|\\n)*?public.*?(#{var})(?:.|\\n)*?namelist(#{FORTRAN_SINGLE_LINE})(?:.|\\n)*?end\\s+module\\s+\\1")
#regex = Regexp.new("module\\s+(\\w+)(?:.|\\n)*?public.*?(#{var})(?:.|\\n)*?namelist(#{FORTRAN_SINGLE_LINE})")
#regex = Regexp.new("public.*?(#{var})(?:.|\\n)*?namelist#{FORTRAN_SINGLE_LINE}#{var}")
regex = Regexp.new("namelist#{FORTRAN_SINGLE_LINE}#{var}")
#p regex
modules.each do |m, mod|
mod.scan(regex) do
varhash[:module] = m.to_sym
end
end
end
end
save_namelists
end
alias :gvm :get_variable_modules
#Make a file with doxygen style comments to document
#the input parameters
def print_doxygen_documentation
puts "! This file is not part of GS2, but exists to document those variables which are also input parameters. It is generated automatically by CodeRunner from its input parameter database. To update this database, DO NOT edit this file, your changes will be lost: go to the wiki page for the input parameters (http://sourceforge.net/apps/mediawiki/gyrokinetics/index.php?title=Gs2_Input_Parameters) and follow the instructions there. \n\n"
rcp.namelists.each do |nmlist, hash|
hash[:variables].each do |var, varhash|
next unless varhash[:module] and varhash[:help]
puts "module #{varhash[:module]}\n\n\n"
puts " #{print_doxygen_variable_documentation(var)}"
#puts " public :: #{var}"
puts " #{fortran_type(varhash)} :: #{var}"
puts "end module #{varhash[:module]}"
end
end
end
alias :pdd :print_doxygen_documentation
def fortran_type(varhash)
case varhash[:type]
when :Float
'real'
when :Fortran_Bool
'logical'
when :String
'character'
when :Integer
'integer'
else
raise 'unknown type'
end
end
end
end # class FortranNamelist
end # class Run
end # class CodeRunner
|
---|