1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 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 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 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 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
|
package Demeter::Plugins::FileType;
use Moose;
use Moose::Util::TypeConstraints;
with 'Demeter::Dispose';
with 'Demeter::Get';
with 'Demeter::Tools';
with 'Demeter::Project';
use File::Basename;
use File::Spec;
has 'is_binary' => (is => 'ro', isa => 'Bool', default => 0);
has 'description' => (is => 'ro', isa => 'Str', default => "Base class for file type plugins");
has 'version' => (is => 'ro', isa => 'Str', default => 0);
has 'parent' => (is => 'rw', isa => 'Any',);
has 'file' => (is => 'rw', isa => 'Str', default => q{},
trigger => sub{my ($self, $new) = @_;
my ($name, $path, $suffix) = fileparse($new);
$self->filename($name);
$self->folder($path);
});
has 'filename' => (is => 'rw', isa => 'Str', default => q{});
has 'folder' => (is => 'rw', isa => 'Str', default => q{});
has 'fixed' => (is => 'rw', isa => 'Str|ArrayRef', default => q{});
#has 'inifile' => (is => 'rw', isa => 'Str', default => q{});
has 'conffile' => (is => 'rw', isa => 'Str', default => q{});
has 'lower_case' => (is => 'rw', isa => 'Bool', default => 1);
has 'time_consuming' => (is => 'rw', isa => 'Bool', default => 0);
has 'working_message' => (is => 'rw', isa => 'Str', default => q{});
has 'display_new' => (is => 'rw', isa => 'Bool', default => 0);
has 'metadata_ini' => (is => 'rw', isa => 'Str', default => q{});
enum 'OutputTypes' => ['data', 'list', 'project'];
coerce 'OutputTypes', from 'Str', via { lc($_) };
has 'output' => (is => 'ro', isa => 'OutputTypes', default => q{data});
sub Croak {
my ($self, $arg) = @_;
if (Demeter->mo->ui eq 'Wx') {
Wx::Perl::Carp::warn($arg);
} else {
croak $arg;
};
};
sub add_metadata {
my ($self, $data) = @_;
$data->metadata_from_ini($self->metadata_ini) if $self->metadata_ini;
return $self;
};
sub data_attributes {
my ($self, $mode) = @_;
$mode ||= 'fluorescence';
return (file=>$self->fixed, source=>$self->file, $self->suggest($mode));
};
__PACKAGE__->meta->make_immutable;
1;
=head1 NAME
Demeter::Plugins::FileType - base class for file type plugins
=head1 VERSION
This documentation refers to Demeter version 0.9.26.
=head1 SYNOPSIS
my $filetype = Demeter::Plugins::X15B->new(file=>$file);
if ($filetype->is) {
my $converted = $filetype->fix;
my $data = Demeter::Data->new(file => $converted,
$filetype -> suggest("fluorescence")
);
};
=head1 DESCRIPTION
A file type plugin is used to address the common situation of raw XAS
data from a beamline which is in a form that cannot be read directly
by Ifeffit. Some common reasons for this are data in a binary format,
data with an ambiguous separation between header and data, and
ill-formed numbers (e.g. NaN or missing white space between columns)
among the columns of data.
The user of a data analysis program does not much care about the
reasons that a particular data file might be unreadable by Ifeffit --
that person is only concerned with getting some analysis done.
Consequently, a program like Athena must do one of two things:
=over 4
=item 1.
Treat each such file as a special case and include custom code for
dealing with each such case, or
=item 2.
Provide a plugin mechanism with which a small amount of code can be
dropped in some well-advertised location and which can be used to
change the troublesome data into a more easily handled from.
=back
Demeter adopts the second option. Ifeffit (or Larch) is used for data
import. All data files must be readable by Ifeffit (or Larch). For
those kinds of data file that are not easily read by Ifeffit, a plugin
is used to convert the data from the troublesome format into a
standard, well-formatted, column data file. The normal input
mechanism is then used to import the data into a L<Demeter::Data>
object.
All file type plugins are implemented as L<Moose> objects. As such,
all file type plugins must have certain attributes and methods so that
data handling programs (such as Athena) can expect uniformity of
interface. The best way to meet these requirements is to modify an
existing file type plugin.
See the F<022_filetypes.t> test from the Demeter distribution for an
example of using the methods of a file type plugin and creating
Demeter::Data objects from the output of the plugins.
Note that this base class uses the L<Demeter::Project> and
L<Demeter::Tools> roles and so all subclasses have access to the
methods of those roles.
=head2 Plugins shipped with Demeter
The following are some of the plugins that come with Demeter. This is
not a complete list of plugins. Please note that these were written
using example data files that Bruce had available to him. He has not
himself used all of these beamlines. Your mileage may vary with your
own data.
=over 4
=item C<X10C>
Convert files from NSLS beamline X10C. These files have headers which
confuse Ifeffit and sometimes fail to have white space separating
numbers among the data columns. The plugin comments out the headers
and corrects the problem with white space between columns.
=item C<X15B>
Convert files from NSLS beamline X15B. This beamline uses an ancient
data acquisition system which writes files in a cryptic binary
format. This plugin converts these data to a simple column data file,
saving only the scalars containing the XAS-relevant measurement
channels.
=item C<PFBL12C>
Convert files from Photon Factory beamline 12C. These files have
headers which will confuse Ifeffit's file import and store data as a
function of monochromator angle. This plugin comments the header and
converts mono angle to energy using information about the crystal type
contained in the header.
=item C<SSRLB>
Convert SSRL binary data file. Yes, SSRL does provide a program for
converting these binary files to column ASCII data. This plugin does
the same chore, yielding a file easily read by Ifeffit.
=item C<SSRLA>
Convert SSRL ASCII data file. Presumably, these ASCII files are the
result of the SSRL conversion program. These ASCII files are
unreadable by Ifeffit. This plugin, comments out the header lines,
constructs a column label line out of the Data: section, moves the
first column (real time clock) to the third column, and swaps the
requested and acheived energy columns.
=item C<SSRLmicro>
Sam Webb's microprobe data acquisition program writes files with lots
of columns and with a header structure that cannot be easily used by
Ifeffit. This plugin massages that file format into a form more
easily ready by Ifeffit, keeping only the ROI columns. (Note that
this plugin could be modified quite easily to perform a simple ICR/OCR
deadtime correction.)
=item C<HXMA>
Files from the HXMA beamline at the Canadian Light Source are readable
by Ifeffit, but the columns are labeled in a way that Ifeffit is
unable to use. This plugin restructures the header for Ifeffit's
convenience and keeps only the columns containing the ion chambers and
the corrected (presumably by a simple ICR/OCR deadtime correction) ROI
signals from the multi-element detector.
=item C<CMC>
Files from APS beamline 9BM (CMC-XOR) are single-record Spec files.
As a result, these data files contain lots of useless column (for
example, each file inexplicably saves h, k, and l values). This
plugin discard all the useless columns, keeping only those from the
ion chambers and the multi-element detector. It also discards the
problematic "logi0i1" column, which can result in NaN entries in the
case of zero signal on the transmission detector.
=item C<X23A2MED>
Data measured using the Vortex silicon drift detector at NSLS X23A2
are imported and deadtime corrected using the point-by-point iterative
algorithm developed and implemented by Joe Woicik and Bruce Ravel.
The output data file contains columns for each corrected detector
channel as well as columns for the various ion chambers. This is an
example of a file type plugin which uses Ifeffit dirrectly.
=item C<X23A2MultiChannel>
Data measured using the multi-channel ion chambers at NSLS X23A2 are
converted into an Athena project file. This is an example of filetype
plugin which sets the C<output> attribute to "project".
=item C<Lytle>
Import files from the Lytle database. This plugin imports those data
that are recorded by encoder value and which have headers that start
with the word C<NPTS> and have the mono d-spacing and steps-per-degree
in the second line. There is another common file format in the Lytle
database (the header begins with C<CUEDGE> and does not record the
mono parameters) that is not handled by this plugin. See question 3
at L<http://cars9.uchicago.edu/ifeffit/FAQ/Data_Handling>.
=item C<DUBBLE>
Import files from the DUBBLE beamline at ESRF. This plugin converts
mono position stored as millidegrees into energy. It also
disentangles the lines of the data file containing signal recorded
from a multi-element detector.
=back
As you can see, the plugins serve multiple purposes. On one hand,
they massage unreadable data (as in the binary formats) into a
readable form. They can also be used to do some pre-processing of the
data, as in the case of plugins which remove unnecessary columns from
the converted data files.
Plugins could be used for other sorts of data pre-processing. For
example, it would be possible to apply deadtime corrections using
columns contained in the original data file. As another example, a
plugin could also be used to remove a dark current from a data file
which records the dark current in the header but does not remove that
dark signal from the recorded scalars.
=head2 Boilerplate
A file type plugin should start with the boilerplate like the
following:
package Demeter::Plugins::X15B;
use Moose;
extends 'Demeter::Plugins::FileType';
has '+is_binary' => (default => 1);
has '+description' => (default => "Read binary files from NSLS beamline X15B.");
has '+version' => (default => 0.1);
The final two executable lines of the plugin must be the following:
__PACKAGE__->meta->make_immutable;
1;
This will set up the plugin as a class derived from the
L<Demeter::Plugins::FileType> base class and set the correct values
for the C<is_binary> and C<description> attributes. This boilerplate
will be followed by the C<is>, C<fix> and C<suggest> methods which
define the file type plugin interface.
=head2 Common attributes
Every file type plugin has the following attributes, all of which are
inherited from the L<Demeter::Plugin::FileType> base class. Normally,
an individual file type plugin will need to override the C<is_binary>
and C<description>, as shown above. The C<version> attribute should
also be overriden by any plugin.
=over 4
=item C<is_binary>
This is a Boolean which indicates whether the original data file was
in a binary format. The use of this flag is indicate to a GUI program
whether the original data file can be displayed as text.
=item C<description>
A brief textual description of the purpose of the plugin. This should
be succinct and finish a sentence that begins "This file appears to be
from ".
=item C<version>
The version number of the plugin.
=item C<parent>
A placeholder which may be needed by a GUI. Currently not used by any
plugin.
=item C<file>
This takes the name of the original data file and is the only
attribute typically supplied by the caller.
=item C<filename>
This is the file name part of the value of the C<file> attribute.
This is set by a trigger when the C<file> attribute is set. All the
file type plugins that ship with Demeter write the output column data
file to the stash directory with the same name as the input file. The
bit of code that does that copying uses this attribute to name the
output file. You should never need to set this directly.
=item C<folder>
This is the folder part of the value of the C<file> attribute. This
is set by a trigger when the C<file> attribute is set. You should
never need to set this directly.
=item C<fixed>
This contains the fully resolved file name of the converted file. The
converted file typically sits in Demeter's stash folder. For a
list-returning plugin, this contains a reference to an array of fully
resolved filenames, those files being the items extracted from the
original file.
=item C<output>
This string is either "data", "list" or "project" to indicate what
kind of file is written to the stash area by the plugin. Most plugins
filter an input data file into a stash file that Ifeffit can read. A
plugin can also return a reference to a list of files. The plugin may
or may not do some processing of each individual file in the list.
You will need to process each returned file correctly. A plugin can
also make an Athena project file, in which case this attribute tells
Demeter to interpret it that way.
=item C<conffile>
If the plugin requires configuration parameters, these can be
specified in an demeter-style configuration file whise name is given
by this attribute. The default is an empty string, which indicates
that no configuration file is required. The file must be a
demeter_conf file so that a GUI (say, Athena) can provide a consistent
mechanism for modifying the configuration.
=item C<metadata_ini>
If the plugin is for a file from a beamline with metadata tabulated in
Demeter's F<share/xdi/> folder, this attribute should be set to F<.ini>
file for that beamline.
=item C<display_new>
When true, this tells Athena to use the filename of the file copied to
the stash folder in the group list. The value of this is that
information can be inserted by a plugin into the filename. That
information then makes its way into the group list label.
=back
=head2 Common methods
All plugins inherit these methods:
=over 4
=item C<data_attributes>
This returns a list which sets a number of data attributes of the Data
object being set by the plugin, including C<file>, C<source>, and all
the column selection attributes.
my $plugin = Demeter::Plugins::Whatever->new(file=>$file);
$plugin -> fix;
my $mode = 'fluorescence'; # or 'transmission'
my $data = Demeter::Data->new($plugin->data_attributes($mode));
The Data's C<file> attribute will be set to the name of the stash
file, the C<source> attribute will be set to the raw data file, and
the C<energy>, C<numerator>, C<denominator>, and C<ln> attributes will
be set as suggested by the plugin.
=back
=head2 Required methods
All plugins B<must> supply these three methods:
=over 4
=item C<is>
The C<is> method is used to recognize a data file of a particular
format. Typically, this recognition is made by examining the contents
of the data file and finding some identifying feature of the contents.
my $filetype = Demeter::Plugins::X15B->new(file=>$file);
my $is_x15b = $filetype->is;
The C<is> method returns a true value if the file is recognized and a
false value if not.
Note that the C<is> method must be fast. It is possible that a data
file will be checked against many different file types. A poorly
written C<is> method can seriously impede the performance of a data
processing program.
=item C<fix>
The C<fix> method opens the file contained in the C<file> attribute,
applies whatever processing needs to happen and writes that file as a
normal column data file. The fully-resolved filename of the output
file is stored in the C<fixed> attribute B<and> is the return value of
this method. All plugins that come with Demeter write the output file
to Demeter's stash folder and use the same name as the input data
file. This is good practice, but is not required.
my $filetype = Demeter::Plugins::X15B->new(file=>$file);
my $converted_file = $filetype->fix;
## this also works after the call to fix:
## my $converted_file = $filetype->fixed;
The C<fix> method can be written using virtually any tool, including
tools provided by Demeter itself. It can also call Ifeffit directly
if you C<use Ifeffit;> at the top of the plugin file. You can even
use external tools such as L<PDL>, L<Math::Cephes>, or other
heavy-duty mathematics utilities.
Speed is appreciated, but correctness is essential.
=item C<suggest>
This method returns an array containing values for the C<energy>,
C<numerator>, C<denominator>, and C<ln> attributes of the
L<Demeter::Data> object. If the C<fix> method leaves the data file in
a format for which correct column selection for transmission or
fluorescence XAS is predictable, this method helps lower the barrier
of using the plugin.
my $filetype = Demeter::Plugins::X15B->new(file=>$file);
if ($filetype->is) {
my $converted = $filetype->fix;
my $data = Demeter::Data->new(file => $converted,
$filetype -> suggest("fluorescence")
);
};
Here the suggested column numbers for the energy, fluorescence
(numerator), and I0 (denominator) channels are returned by the
C<suggest> method for use in defining the L<Demeter::Data> object.
This method takes a single argument, which should be either
"transmission" or "fluorescence". A plugin can choose which to make
as the default. Most use "transmission" as the default, but a plugin
for an instrument or beamline related to a fluorescence measurement
will make "fluorescence" the default. In any case, spell carefully!+
If the proper columns are not predictable, then this method should
return an empty array.
The return value should be an array and not an array reference.
=item C<clean>
A list-returning plugin, i.e. one which returns a reference to an
array from the C<fix> method, must provide a C<clean> method. This
method, when invoked, will remove from disk the files listed in the
return value of the C<fix> method. Plugins which return a single file
or an Athena project file need not provide this method.
=back
=head2 Namespace and disk locations
All plugins B<must> live in the C<Demeter::Plugins::> namespace. In
the Demeter distribution, therefore, they live in the
F<Demeter/Plugins/> folder beneath the installation location.
An individual user can have additional plugins in user diskspace by
placing them in ...
=over 4
=item unix
$HOME/.horae/Demeter/Plugins/
=item Windows
%APPDATA%\horae\Demeter\Plugins\
=back
=head1 BUGS AND LIMITATIONS
=over 4
=item *
Do I need the C<parent> attribute?
=item *
None of the plugins have enough error testing. It is certainly
possible that C<is> could return true but the rest of the file is
malformed compared to the expectations of C<fix>.
=back
=head1 AUTHOR
Bruce Ravel, L<http://bruceravel.github.io/home>
http://bruceravel.github.io/demeter/
=head1 LICENSE AND COPYRIGHT
Copyright (c) 2006-2019 Bruce Ravel (L<http://bruceravel.github.io/home>). All rights reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself. See L<perlgpl>.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
=cut
|