ctioga 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +340 -0
- data/ctioga/bin/ctable +28 -0
- data/ctioga/bin/ctioga +37 -0
- data/ctioga/doc/ctable.1 +156 -0
- data/ctioga/doc/ctioga.1 +2363 -0
- data/ctioga/examples/README +46 -0
- data/ctioga/examples/ctioga.gnuplot +4 -0
- data/ctioga/examples/ctioga_within_tioga.rb +53 -0
- data/ctioga/examples/ctiogarc.rb +24 -0
- data/ctioga/examples/include_1.rb +15 -0
- data/ctioga/examples/noise.dat +100 -0
- data/ctioga/examples/noise.rb +13 -0
- data/ctioga/examples/trig.csv +100 -0
- data/ctioga/examples/trig.dat +100 -0
- data/ctioga/examples/trig.rb +14 -0
- data/ctioga/examples/trigh.dat +100 -0
- data/ctioga/examples/trigh.rb +10 -0
- data/ctioga/examples/tutorial +763 -0
- data/ctioga/examples/tutorial.sh +269 -0
- data/ctioga/tests/README +14 -0
- data/ctioga/tests/axes.sh +40 -0
- data/ctioga/tests/basic.sh +11 -0
- data/ctioga/tests/draw.sh +24 -0
- data/ctioga/tests/histograms.sh +14 -0
- data/ctioga/tests/insets.sh +41 -0
- data/ctioga/tests/layouts.sh +29 -0
- data/ctioga/tests/legends.sh +113 -0
- data/ctioga/tests/styles.sh +43 -0
- data/ctioga/tests/test_style.sh +8 -0
- data/ctioga/tests/tests.sh +24 -0
- data/ctioga/tests/text_backend.sh +83 -0
- data/ctioga/tests/tioga_defaults.rb +18 -0
- data/lib/CTioga/axes.rb +904 -0
- data/lib/CTioga/backends.rb +88 -0
- data/lib/CTioga/boundaries.rb +224 -0
- data/lib/CTioga/ctable.rb +134 -0
- data/lib/CTioga/curve_style.rb +246 -0
- data/lib/CTioga/debug.rb +199 -0
- data/lib/CTioga/dimension.rb +133 -0
- data/lib/CTioga/elements.rb +17 -0
- data/lib/CTioga/elements/base.rb +84 -0
- data/lib/CTioga/elements/containers.rb +578 -0
- data/lib/CTioga/elements/curves.rb +368 -0
- data/lib/CTioga/elements/tioga_primitives.rb +440 -0
- data/lib/CTioga/layout.rb +595 -0
- data/lib/CTioga/legends.rb +29 -0
- data/lib/CTioga/legends/cmdline.rb +187 -0
- data/lib/CTioga/legends/item.rb +164 -0
- data/lib/CTioga/legends/style.rb +257 -0
- data/lib/CTioga/log.rb +73 -0
- data/lib/CTioga/movingarrays.rb +131 -0
- data/lib/CTioga/partition.rb +271 -0
- data/lib/CTioga/plot_style.rb +230 -0
- data/lib/CTioga/plotmaker.rb +1677 -0
- data/lib/CTioga/shortcuts.rb +69 -0
- data/lib/CTioga/structures.rb +82 -0
- data/lib/CTioga/styles.rb +140 -0
- data/lib/CTioga/themes.rb +581 -0
- data/lib/CTioga/themes/classical.rb +82 -0
- data/lib/CTioga/themes/demo.rb +63 -0
- data/lib/CTioga/themes/fits.rb +91 -0
- data/lib/CTioga/themes/mono.rb +33 -0
- data/lib/CTioga/tioga.rb +32 -0
- data/lib/CTioga/utils.rb +173 -0
- data/lib/MetaBuilder/Parameters/dates.rb +38 -0
- data/lib/MetaBuilder/Parameters/lists.rb +132 -0
- data/lib/MetaBuilder/Parameters/numbers.rb +69 -0
- data/lib/MetaBuilder/Parameters/strings.rb +86 -0
- data/lib/MetaBuilder/Parameters/styles.rb +75 -0
- data/lib/MetaBuilder/Qt4/Parameters/dates.rb +51 -0
- data/lib/MetaBuilder/Qt4/Parameters/numbers.rb +65 -0
- data/lib/MetaBuilder/Qt4/Parameters/strings.rb +106 -0
- data/lib/MetaBuilder/Qt4/parameter.rb +172 -0
- data/lib/MetaBuilder/Qt4/parameters.rb +9 -0
- data/lib/MetaBuilder/descriptions.rb +603 -0
- data/lib/MetaBuilder/factory.rb +101 -0
- data/lib/MetaBuilder/group.rb +57 -0
- data/lib/MetaBuilder/metabuilder.rb +10 -0
- data/lib/MetaBuilder/parameter.rb +374 -0
- data/lib/MetaBuilder/parameters.rb +11 -0
- data/lib/MetaBuilder/qt4.rb +8 -0
- data/lib/SciYAG/Backends/backend.rb +379 -0
- data/lib/SciYAG/Backends/binner.rb +168 -0
- data/lib/SciYAG/Backends/cache.rb +102 -0
- data/lib/SciYAG/Backends/dataset.rb +158 -0
- data/lib/SciYAG/Backends/descriptions.rb +469 -0
- data/lib/SciYAG/Backends/filters.rb +25 -0
- data/lib/SciYAG/Backends/filters/average.rb +134 -0
- data/lib/SciYAG/Backends/filters/cumulate.rb +37 -0
- data/lib/SciYAG/Backends/filters/filter.rb +70 -0
- data/lib/SciYAG/Backends/filters/norm.rb +39 -0
- data/lib/SciYAG/Backends/filters/smooth.rb +63 -0
- data/lib/SciYAG/Backends/filters/sort.rb +43 -0
- data/lib/SciYAG/Backends/filters/strip.rb +34 -0
- data/lib/SciYAG/Backends/filters/trim.rb +64 -0
- data/lib/SciYAG/Backends/gnuplot.rb +131 -0
- data/lib/SciYAG/Backends/math.rb +108 -0
- data/lib/SciYAG/Backends/mdb.rb +462 -0
- data/lib/SciYAG/Backends/multitext.rb +96 -0
- data/lib/SciYAG/Backends/source.rb +64 -0
- data/lib/SciYAG/Backends/text.rb +339 -0
- data/lib/SciYAG/backends.rb +16 -0
- metadata +191 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# parameters.rb: A file grouping all the require necessary to have a
|
|
2
|
+
# working parameter system.
|
|
3
|
+
# This file is copyright 2006 by Vincent Fourmond, but you can do whatever
|
|
4
|
+
# you want with it.
|
|
5
|
+
|
|
6
|
+
require 'MetaBuilder/parameter'
|
|
7
|
+
require 'MetaBuilder/Parameters/numbers'
|
|
8
|
+
require 'MetaBuilder/Parameters/strings'
|
|
9
|
+
require 'MetaBuilder/Parameters/lists'
|
|
10
|
+
require 'MetaBuilder/Parameters/styles'
|
|
11
|
+
require 'MetaBuilder/Parameters/dates'
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# backend.rb : The base of the arcitecture of the Backends
|
|
2
|
+
# Copyright (C) 2006 Vincent Fourmond
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software
|
|
16
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
|
|
18
|
+
# Filters and descriptions
|
|
19
|
+
require 'MetaBuilder/metabuilder'
|
|
20
|
+
require 'SciYAG/Backends/descriptions'
|
|
21
|
+
require 'SciYAG/Backends/filters'
|
|
22
|
+
require 'SciYAG/Backends/dataset'
|
|
23
|
+
require 'SciYAG/Backends/cache'
|
|
24
|
+
|
|
25
|
+
# Document-class: SciYAG
|
|
26
|
+
# The SciYAG module contains all the classes related to Backend and Filter.
|
|
27
|
+
# It will also contain additionnal features in a not-so-distant future.
|
|
28
|
+
module SciYAG
|
|
29
|
+
|
|
30
|
+
module Backends
|
|
31
|
+
|
|
32
|
+
# A simple class to describe a backend. See the
|
|
33
|
+
# Descriptions::Description
|
|
34
|
+
class BackendDescription < MetaBuilder::Description
|
|
35
|
+
# The backend's banner.
|
|
36
|
+
def banner(instance)
|
|
37
|
+
"Backend '#{instance.long_name}'"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Creates a new backend and optionnally registers it
|
|
41
|
+
def initialize(cls, name, long_name, desc, register = true)
|
|
42
|
+
super(cls, name, long_name, desc)
|
|
43
|
+
if register
|
|
44
|
+
Backend.register_class(self)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# This class provides the infrastructure for accessing data sets. It
|
|
51
|
+
# shouldn't be used directly, but rather subclassed and reimplemented.
|
|
52
|
+
# The aim of this class is to provide any software which is interested
|
|
53
|
+
# to retrive data from some source with a consistent way to do so,
|
|
54
|
+
# independent the kind of source accessed.
|
|
55
|
+
#
|
|
56
|
+
# Subclasses should:
|
|
57
|
+
# * provide a consistent method for creating themselves,
|
|
58
|
+
# with as much information as necessary, including options and default
|
|
59
|
+
# parameters. Actually, their initialize function should take no value
|
|
60
|
+
# bu on the other side, the BackendDescription associated with it
|
|
61
|
+
# should make it easy to set all the parameters necessary to
|
|
62
|
+
# get one set of data.
|
|
63
|
+
# * provide a way to fill an OptionParser with their own parameters
|
|
64
|
+
# * provide a way to retrieve the data via named 'sets' (either 2D or 3D
|
|
65
|
+
# data, depending on the subclass)
|
|
66
|
+
# * provide a way to obtain all meta-informations on one dataset,
|
|
67
|
+
# such as the date, the meaning of the columns (if any), and so on.
|
|
68
|
+
# * provide a way to know which named sets are available, or at least
|
|
69
|
+
# a subset (or nothing if we don't know a thing).
|
|
70
|
+
# * wether the actual reading of the data is done at initialization time
|
|
71
|
+
# or at query time is left to the implementor ;-) !
|
|
72
|
+
|
|
73
|
+
class Backend
|
|
74
|
+
|
|
75
|
+
# Import the main description functions into the appropriate
|
|
76
|
+
# namespaces
|
|
77
|
+
extend MetaBuilder::DescriptionExtend
|
|
78
|
+
include MetaBuilder::DescriptionInclude
|
|
79
|
+
|
|
80
|
+
# Backend is a factory, but no autoregistering is made.
|
|
81
|
+
create_factory false
|
|
82
|
+
|
|
83
|
+
# Sets up a few things, such as the filters.
|
|
84
|
+
def initialize()
|
|
85
|
+
@xy_filters = []
|
|
86
|
+
@xy_filters_apply = false
|
|
87
|
+
|
|
88
|
+
@xyz_filters = []
|
|
89
|
+
@xyz_filters_apply = false
|
|
90
|
+
|
|
91
|
+
# If set, uses this set as a baseline for all the others.
|
|
92
|
+
@base_line = ""
|
|
93
|
+
@base_line_cache = false
|
|
94
|
+
|
|
95
|
+
# Filters are classes that have one method apply, which takes a
|
|
96
|
+
# pair/triplet of vectors and return the same number of stuff,
|
|
97
|
+
# modified or not.
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# We create a cache by default. Doesn't take up much space anyway:
|
|
101
|
+
@cache = Cache.new
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def push_xy_filter(f)
|
|
105
|
+
@xy_filters << f
|
|
106
|
+
@xy_filters_apply = true
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Removes a filter from the top
|
|
110
|
+
def pop_xy_filter
|
|
111
|
+
return @xy_filters.pop
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Removes all filters on the list.
|
|
115
|
+
def clear_xy_filters
|
|
116
|
+
@xy_filters = []
|
|
117
|
+
@xy_filters_apply = false
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# Creates a description object with the given texts and associates
|
|
122
|
+
# it with the class. It is necessary to have this statement
|
|
123
|
+
# *before* any parameter declaration. If you don't set any description,
|
|
124
|
+
# you will not be able to benefit from the plugin system.
|
|
125
|
+
# To be used in Backend subclasses, simply this way:
|
|
126
|
+
#
|
|
127
|
+
# describe "biniou", "Biniou backend", "A backend to deal with Binious"
|
|
128
|
+
#
|
|
129
|
+
def Backend.describe(name, longname, desc, register = true)
|
|
130
|
+
d = BackendDescription.new(self, name, longname, desc, register)
|
|
131
|
+
set_description(d)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Returns a hash containing the description of all available backends
|
|
135
|
+
def Backend.list_backends
|
|
136
|
+
return factory_description_hash
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def Backend.list_descriptions
|
|
140
|
+
warn "Backend.list_descriptions should not be used, use Backend.list_backends instead"
|
|
141
|
+
list_backends
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
describe 'backend', 'The base class for backends', <<EOD, false
|
|
145
|
+
This is the base class for backends. It should never be used directly.
|
|
146
|
+
EOD
|
|
147
|
+
|
|
148
|
+
# A hook to set a baseline:
|
|
149
|
+
param_reader :base_line=, :base_line, "baseline", "Base line",
|
|
150
|
+
{:type => :string, }, "Sets a baseline for subsequent data sets"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def base_line=(str)
|
|
154
|
+
if str =~ /^no$/ or str.empty?
|
|
155
|
+
@base_line = ""
|
|
156
|
+
else
|
|
157
|
+
@base_line = expand_sets(str)[0]
|
|
158
|
+
# Fill the cache.
|
|
159
|
+
ary = query_xy_data(@base_line)
|
|
160
|
+
@base_line_cache = if ary.is_a?(Array)
|
|
161
|
+
ary[0]
|
|
162
|
+
else
|
|
163
|
+
ary
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# This function should return a hash containing all meta-information
|
|
169
|
+
# available about the given set. The hash can contain elements such as:
|
|
170
|
+
#
|
|
171
|
+
# <tt>:date</tt>:: the date at which the set was recorded
|
|
172
|
+
# <tt>:x_legend</tt>:: legend of the X axis
|
|
173
|
+
# <tt>:x_unit</tt>:: unit of the X axis
|
|
174
|
+
# (and the same kind for Y and Z axis)
|
|
175
|
+
#
|
|
176
|
+
# Of course, this list is not limitative; you are encouraged at any rate
|
|
177
|
+
# to add as much metadata as possible. Later on, I'll provide a method
|
|
178
|
+
# to register metadata types.
|
|
179
|
+
|
|
180
|
+
def meta_data(set)
|
|
181
|
+
warn "No metadata implementation for backend " +
|
|
182
|
+
"#{description.name}"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Returns true if the backend can provide data for the given set.
|
|
186
|
+
def has_set?(set)
|
|
187
|
+
return false
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
alias set? has_set?
|
|
191
|
+
|
|
192
|
+
# Returns :xy or :xyz depending on the kind of the given set.
|
|
193
|
+
def set_type(set)
|
|
194
|
+
raise "Shouldn't be called for the base class"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# This function must be redefined by children who provide
|
|
198
|
+
# 2D datasets. This function must return either:
|
|
199
|
+
# * a Function alone, representing the 2D data of the set
|
|
200
|
+
# * or an Array, whose first element is a Function and whose
|
|
201
|
+
# second element is an array containing metadata about the
|
|
202
|
+
# dataset.
|
|
203
|
+
def query_xy_data(set)
|
|
204
|
+
raise "query_xy_data must be redefined by children !"
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Used by other classes to query for the Backends data. This may
|
|
208
|
+
# include processing with data filters. Please note that the
|
|
209
|
+
# query_xy_data functions *must* return a Dobjects::Function.
|
|
210
|
+
# The return value *must* be modifiable without consequences to
|
|
211
|
+
# the backend.
|
|
212
|
+
def xy_data(set)
|
|
213
|
+
retval = query_xy_data(set)
|
|
214
|
+
if retval.is_a?(Array)
|
|
215
|
+
ary,errors,meta = *retval
|
|
216
|
+
else
|
|
217
|
+
ary = retval
|
|
218
|
+
errors = {}
|
|
219
|
+
meta = {}
|
|
220
|
+
end
|
|
221
|
+
if (not @base_line.empty?) and @base_line_cache
|
|
222
|
+
ary.y.sub!(@base_line_cache.y)
|
|
223
|
+
end
|
|
224
|
+
# Now, this is the fun part: we create the Dataset object
|
|
225
|
+
dataset = DataSet2D.new(self, ary, errors, meta)
|
|
226
|
+
# apply the filters if necessary
|
|
227
|
+
if @xy_filters_apply
|
|
228
|
+
for filter in @xy_filters
|
|
229
|
+
filter.apply!(dataset)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
return dataset
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Returns the XYZ data for the given set
|
|
236
|
+
def xyz_data(set)
|
|
237
|
+
ary = query_xyz_data(set)
|
|
238
|
+
# apply the filters if necessary
|
|
239
|
+
if @xyz_filters_apply
|
|
240
|
+
for filter in @xyz_filters
|
|
241
|
+
ary = filter.apply(ary)
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
return ary
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# When converting a user input into a set, a program should
|
|
249
|
+
# *always* use this function, unless it has really good reasons for
|
|
250
|
+
# that.
|
|
251
|
+
#
|
|
252
|
+
# The default implementation is to expand 2##4 to 2, 3, 4. Can be
|
|
253
|
+
# useful even for mathematical stuff.
|
|
254
|
+
#
|
|
255
|
+
# Another thing is recognised and expanded:
|
|
256
|
+
# #<2<i*2>,5> runs the code i*2 with the values from 2 to 5 and
|
|
257
|
+
# returns the result. The code in the middle is a Ruby block, and therefore
|
|
258
|
+
# should be valid !
|
|
259
|
+
#
|
|
260
|
+
# A third expansion is now available:
|
|
261
|
+
# #<a = 2<a * sin(x)>10> will expand into 2*sin(x) , 3*sin(x) ... 10*sin(x)
|
|
262
|
+
# it is different than the previous in the sense that the code in the
|
|
263
|
+
# middle is not a Ruby code, but a mere string, which means there won't be
|
|
264
|
+
# compilation problems.
|
|
265
|
+
#
|
|
266
|
+
# Unless your backend can't accomodate for that, all redefinitions
|
|
267
|
+
# of this function should check for their specific signatures
|
|
268
|
+
# first and call this function if they fail. This way, they
|
|
269
|
+
# will profit from improvements in this code while keeping
|
|
270
|
+
# old stuff working.
|
|
271
|
+
def expand_sets(spec)
|
|
272
|
+
if m = /(\d+)##(\d+)/.match(spec)
|
|
273
|
+
debug "Using expansion rule #1"
|
|
274
|
+
a = m[1].to_i
|
|
275
|
+
b = m[2].to_i
|
|
276
|
+
ret = []
|
|
277
|
+
a.upto(b) do |i|
|
|
278
|
+
ret << m.pre_match + i.to_s + m.post_match
|
|
279
|
+
end
|
|
280
|
+
return ret
|
|
281
|
+
elsif m = /\#<(\d+)<(.*?)>(\d+)>/.match(spec)
|
|
282
|
+
debug "Using expansion rule #2"
|
|
283
|
+
from = m[1].to_i
|
|
284
|
+
to = m[3].to_i
|
|
285
|
+
debug "Ruby code used for expansion: {|i| #{m[2]} }"
|
|
286
|
+
code = eval "proc {|i| #{m[2]} }"
|
|
287
|
+
ret = []
|
|
288
|
+
from.upto(to) do |i|
|
|
289
|
+
ret << m.pre_match + code.call(i).to_s + m.post_match
|
|
290
|
+
end
|
|
291
|
+
return ret
|
|
292
|
+
elsif m = /\#<\s*(\w+)\s*=\s*(\d+)\s*<(.*?)>\s*(\d+)\s*>/.match(spec)
|
|
293
|
+
debug "Using expansion rule #3"
|
|
294
|
+
var = m[1]
|
|
295
|
+
from = m[2].to_i
|
|
296
|
+
to = m[4].to_i
|
|
297
|
+
# Then we replace all occurences of the variable
|
|
298
|
+
literal = '"' + m[3].gsub(/\b#{var}\b/, '#{' + var + '}') + '"'
|
|
299
|
+
debug "Ruby code used for expansion: {|#{var}| #{literal} }"
|
|
300
|
+
code = eval "proc {|#{var}| #{literal} }"
|
|
301
|
+
ret = []
|
|
302
|
+
from.upto(to) do |i|
|
|
303
|
+
ret << m.pre_match + code.call(i).to_s + m.post_match
|
|
304
|
+
end
|
|
305
|
+
return ret
|
|
306
|
+
end
|
|
307
|
+
# Fallback
|
|
308
|
+
return [spec]
|
|
309
|
+
rescue Exception => ex
|
|
310
|
+
# In case something went wrong in the eval.
|
|
311
|
+
warn "An error occured during expansion of '#{spec}': #{ex.message}"
|
|
312
|
+
debug "Error backtrace: #{ex.backtrace.join "\n"}"
|
|
313
|
+
warn "Ignoring, but you're nearly garanteed something will "+
|
|
314
|
+
"fail later on"
|
|
315
|
+
return [spec]
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Some backends have a pretty good idea of the sets available for use.
|
|
319
|
+
# Some really don't. You can choose to reimplement this function if
|
|
320
|
+
# you can provide a useful list of sets for your backend. This list
|
|
321
|
+
# doesn't need to be exhaustive (and is most unlikely to be). It can
|
|
322
|
+
# also return something that would need further expansion using
|
|
323
|
+
# expand_sets.
|
|
324
|
+
def sets_available
|
|
325
|
+
return []
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Gets a cached entry or generate it and cache it. See Cache#cache
|
|
329
|
+
# for more details. The cache's meta_data is constructed as following:
|
|
330
|
+
# * the current state of the backend is taken
|
|
331
|
+
# * keys inside _exclude_ are removed.
|
|
332
|
+
# * _supp_info_ is added
|
|
333
|
+
def get_cached_entry(name, exclude = [], supp_info = {}, &code)
|
|
334
|
+
state = save_state
|
|
335
|
+
for k in exclude
|
|
336
|
+
state.delete(k)
|
|
337
|
+
end
|
|
338
|
+
state.merge!(supp_info)
|
|
339
|
+
return @cache.get_cache(name, state, &code)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Returns the default state of the named backend,
|
|
343
|
+
# or nil if it wasn't found.
|
|
344
|
+
def self.default_state(name)
|
|
345
|
+
desc = factory_description_hash[name]
|
|
346
|
+
if desc
|
|
347
|
+
return desc.default_state
|
|
348
|
+
else
|
|
349
|
+
return nil
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
@@log = nil
|
|
354
|
+
|
|
355
|
+
# Set the logger for Backends
|
|
356
|
+
def self.logger= (logger)
|
|
357
|
+
@@log = logger
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# Facilities for logging: we forward all functions that look
|
|
361
|
+
# like logging facility to the @@log class variable, if that
|
|
362
|
+
# one responds to it.
|
|
363
|
+
methods = %w(warn info debug error fatal)
|
|
364
|
+
for meth in methods
|
|
365
|
+
eval <<"EOE"
|
|
366
|
+
def #{meth}(*args)
|
|
367
|
+
if @@log.respond_to? :#{meth}
|
|
368
|
+
@@log.#{meth}(*args)
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
EOE
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# ruby.el really isn't happy with this one
|
|
375
|
+
private *methods.map {|t| t.to_sym}
|
|
376
|
+
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
end
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# binner.rb : a binning backend to make histograms
|
|
2
|
+
# Copyright (C) 2008 Vincent Fourmond
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software
|
|
16
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
require 'SciYAG/Backends/backend'
|
|
21
|
+
require 'Dobjects/Dvector'
|
|
22
|
+
require 'Dobjects/Function'
|
|
23
|
+
|
|
24
|
+
module SciYAG
|
|
25
|
+
|
|
26
|
+
module Backends
|
|
27
|
+
|
|
28
|
+
class BinnerBackend < Backend
|
|
29
|
+
|
|
30
|
+
include Dobjects
|
|
31
|
+
|
|
32
|
+
describe 'binner', 'Automatic binner', <<EOD
|
|
33
|
+
Reads 1D data and bins them, giving the average position of the
|
|
34
|
+
bin and counts.
|
|
35
|
+
EOD
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
param_accessor :number, 'number', "Number of bins", :integer,
|
|
39
|
+
"Number of bins"
|
|
40
|
+
|
|
41
|
+
param_accessor :x_range, 'xrange', "X Range", :float_range,
|
|
42
|
+
"X range (a:b)"
|
|
43
|
+
|
|
44
|
+
param_accessor :normalize, 'norm', "Normalize", :boolean,
|
|
45
|
+
"Whether to divide by the total"
|
|
46
|
+
|
|
47
|
+
param_accessor :bin_size, 'bin', "Step size", :float,
|
|
48
|
+
"The bin size"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def initialize
|
|
52
|
+
@number = 20
|
|
53
|
+
@skip = 0
|
|
54
|
+
@separator = /\s+/
|
|
55
|
+
@default_column_spec = "1"
|
|
56
|
+
|
|
57
|
+
@x_range = false
|
|
58
|
+
@normalize = false
|
|
59
|
+
|
|
60
|
+
@bin_size = false
|
|
61
|
+
|
|
62
|
+
super()
|
|
63
|
+
|
|
64
|
+
@cache = {}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Reads data from a file. If needed, extract the file from the set
|
|
69
|
+
# specification.
|
|
70
|
+
def read_file(file)
|
|
71
|
+
if file =~ /(.*)@.*/
|
|
72
|
+
file = $1
|
|
73
|
+
end
|
|
74
|
+
name = file # As file will be modified.
|
|
75
|
+
if ! @cache.key?(file) # Read the file if it is not cached.
|
|
76
|
+
if file == "-"
|
|
77
|
+
file = $stdin
|
|
78
|
+
elsif file =~ /(.*?)\|\s*$/
|
|
79
|
+
file = IO.popen($1)
|
|
80
|
+
end
|
|
81
|
+
fancy_read_options = {'index_col' => true,
|
|
82
|
+
'skip_first' => @skip,
|
|
83
|
+
'sep' => @separator
|
|
84
|
+
}
|
|
85
|
+
debug "Fancy read '#{file}', options #{fancy_read_options.inspect}"
|
|
86
|
+
@cache[name] = Dvector.fancy_read(file, nil, fancy_read_options)
|
|
87
|
+
end
|
|
88
|
+
return @cache[name]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# This is called by the architecture to get the data. It splits
|
|
93
|
+
# the set name into filename@cols, reads the file if necessary and
|
|
94
|
+
# calls get_data
|
|
95
|
+
def query_xy_data(set)
|
|
96
|
+
if set =~ /(.*)@(.*)/
|
|
97
|
+
col_spec = $2
|
|
98
|
+
file = $1
|
|
99
|
+
else
|
|
100
|
+
col_spec = @default_column_spec
|
|
101
|
+
file = set
|
|
102
|
+
end
|
|
103
|
+
if file.length > 0
|
|
104
|
+
@current_data = read_file(file)
|
|
105
|
+
@current = file
|
|
106
|
+
end
|
|
107
|
+
x,y = get_data(col_spec)
|
|
108
|
+
return Function.new(x,y)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Reads the data using the columns specification, provided that
|
|
112
|
+
# the appropriate fle has already been loaded into @current. For now
|
|
113
|
+
# no single sanity check.
|
|
114
|
+
def get_data(col_spec)
|
|
115
|
+
if col_spec =~ /\$/ # There is a formula in the specification
|
|
116
|
+
formula = col_spec.gsub(/\$(\d+)/, 'column[\1]')
|
|
117
|
+
debug "Using formula '#{formula}'"
|
|
118
|
+
data = Dvector.
|
|
119
|
+
compute_formula(formula,
|
|
120
|
+
@current_data,[])
|
|
121
|
+
else
|
|
122
|
+
data = @current_data[col_spec.to_i].dup
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Now, data is a single Dvector of numbers. We want to bin it:
|
|
126
|
+
if @x_range
|
|
127
|
+
min = @x_range.first
|
|
128
|
+
max = @x_range.last
|
|
129
|
+
else
|
|
130
|
+
min = data.min
|
|
131
|
+
max = data.max
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if @bin_size
|
|
136
|
+
delta = @bin_size
|
|
137
|
+
nb_bins = (((max - min).abs)/@bin_size).ceil.to_i + 1
|
|
138
|
+
else
|
|
139
|
+
nb_bins = @number
|
|
140
|
+
delta = (max - min)/(nb_bins - 1)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# We create a X Dvector:
|
|
144
|
+
x_values = Dvector.new(nb_bins)
|
|
145
|
+
y_values = Dvector.new(nb_bins)
|
|
146
|
+
nb_bins.times do |i|
|
|
147
|
+
x_values[i] = min + delta * i
|
|
148
|
+
y_values[i] = 0
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Now, we bin it:
|
|
152
|
+
for val in data
|
|
153
|
+
i = ((val - min)/delta).round
|
|
154
|
+
y_values[i] += 1
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
if @normalize
|
|
158
|
+
y_values /= data.size
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
return [x_values, y_values]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
end
|