—package
Lab::Moose::Sweep::Step;
$Lab::Moose::Sweep::Step::VERSION
=
'3.930'
;
#ABSTRACT: Base class for step/list sweeps
use
v5.20;
use
Moose;
use
Data::Dumper;
# Do not import all functions as they clash with the attribute methods.
use
Carp;
#
# Public attributes set by the user
#
has
from
=> (
is
=>
'ro'
,
isa
=>
'Num'
,
predicate
=>
'has_from'
);
has
to
=> (
is
=>
'ro'
,
isa
=>
'Num'
,
predicate
=>
'has_to'
);
has
step
=>
(
is
=>
'ro'
,
isa
=>
'Lab::Moose::PosNum'
,
predicate
=>
'has_step'
);
has
points
=>
(
is
=>
'ro'
,
isa
=>
'ArrayRef[Num]'
,
predicate
=>
'has_points'
);
has
steps
=> (
is
=>
'ro'
,
isa
=>
'ArrayRef[Num]'
,
predicate
=>
'has_steps'
);
has
list
=> (
is
=>
'ro'
,
isa
=>
'ArrayRef[Num]'
,
predicate
=>
'has_list'
,
writer
=>
'_list'
);
has
backsweep
=> (
is
=>
'ro'
,
isa
=>
'Bool'
,
default
=> 0 );
has
both_directions
=> (
is
=>
'ro'
,
isa
=>
'Bool'
,
default
=> 0 );
has
setter
=> (
is
=>
'ro'
,
isa
=>
'CodeRef'
,
required
=> 1 );
#
# Private attributes used internally
#
has
_points
=> (
is
=>
'ro'
,
isa
=>
'ArrayRef[Num]'
,
lazy
=> 1,
init_arg
=>
undef
,
builder
=>
'_build_points'
,
traits
=> [
'Array'
],
handles
=> {
get_point
=>
'get'
,
num_points
=>
'count'
,
points_array
=>
'elements'
},
writer
=>
'write_points'
,
);
has
index
=> (
is
=>
'ro'
,
isa
=>
'Int'
,
default
=> 0,
init_arg
=>
undef
,
writer
=>
'_index'
);
has
current_value
=> (
is
=>
'ro'
,
isa
=>
'Num'
,
init_arg
=>
undef
,
writer
=>
'_current_value'
);
my
$error_msg
=
<<"EOF";
give either (from => ..., to => ..., step => ...)
or (list => [...])
or (points => [...], steps => [....])
or (points => [...], step => )
EOF
sub
_build_points {
my
$self
=
shift
;
my
$has_from_to_step
=
$self
->has_from &&
$self
->has_to &&
$self
->has_step;
my
$has_list
=
$self
->has_list;
my
$has_points_steps
=
$self
->has_points &&
$self
->has_steps;
my
$has_points_step
=
$self
->has_points &&
$self
->has_step;
if
(
$has_from_to_step
+
$has_list
+
$has_points_steps
+
$has_points_step
!= 1 ) {
croak
$error_msg
;
}
my
@points
;
if
(
$has_list
) {
@points
= @{
$self
->list() };
if
(
@points
< 1 ) {
croak
"list needs at least 1 point"
;
}
}
elsif
(
$has_from_to_step
) {
@points
= linspace(
from
=>
$self
->from,
to
=>
$self
->to,
step
=>
$self
->step
);
}
else
{
# points_steps or points_step
my
@steps
;
my
@corner_points
= @{
$self
->points };
if
(
@corner_points
< 2 ) {
croak
"points array needs at least two elements"
;
}
if
(
$has_points_steps
) {
@steps
= @{
$self
->steps };
}
else
{
@steps
= (
$self
->step );
}
if
(
@steps
>=
@corner_points
) {
croak
"steps array exceeds points array"
;
}
push
@steps
,
map
{
$steps
[-1] } ( 1 ..
@corner_points
-
@steps
- 1 );
my
(
$p1
,
$p2
);
$p1
=
shift
@corner_points
;
while
(
@corner_points
) {
my
$p2
=
shift
@corner_points
;
my
$step
=
shift
@steps
;
push
@points
, linspace(
from
=>
$p1
,
to
=>
$p2
,
step
=>
$step
);
$p1
=
$p2
;
}
}
if
(
$self
->backsweep ) {
my
@backsweep_points
=
reverse
@points
;
push
@points
,
@backsweep_points
;
}
if
(
$self
->backsweep &&
$self
->both_directions ) {
croak
"Can't use backsweep and both_directions together."
}
return
\
@points
;
}
sub
start_sweep {
# do nothing
}
sub
go_to_next_point {
my
$self
=
shift
;
my
$index
=
$self
->
index
();
my
$point
=
$self
->get_point(
$index
);
my
$setter
=
$self
->setter();
$self
->
$setter
(
$point
);
$self
->_current_value(
$point
);
$self
->_index( ++
$index
);
}
sub
go_to_sweep_start {
my
$self
=
shift
;
my
$point
=
$self
->get_point(0);
my
$setter
=
$self
->setter();
$self
->
$setter
(
$point
);
$self
->_current_value(
$point
);
$self
->_index(0);
}
sub
sweep_finished {
my
$self
=
shift
;
my
$index
=
$self
->
index
();
if
(
$index
>=
$self
->num_points ) {
if
(
$self
->both_directions ) {
my
@points
=
$self
->points_array;
@points
=
reverse
@points
;
$self
->write_points( \
@points
);
}
return
1;
}
return
0;
}
sub
_in_backsweep {
my
$self
=
shift
;
if
(
$self
->backsweep ) {
if
(
$self
->
index
>
$self
->num_points / 2 ) {
return
1;
}
}
else
{
return
0;
}
}
sub
get_value {
my
$self
=
shift
;
if
( not
defined
$self
->current_value() ) {
croak
"sweep not yet started"
;
}
my
$value
=
sprintf
(
"%.14g"
,
$self
->current_value );
if
(
$self
->_in_backsweep ) {
$value
.=
'_backsweep'
;
}
return
$value
;
}
__PACKAGE__->meta->make_immutable();
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Lab::Moose::Sweep::Step - Base class for step/list sweeps
=head1 VERSION
version 3.930
=head1 SYNOPSIS
use Lab::Moose;
#
# basic 1D sweep (e.g. IV curve)
#
my $source = instrument(
type => ...,
connection_type => ...,
connection_options => {...}
);
my $multimeter = instrument(...);
my $sweep = sweep(
type => 'Step::Voltage',
instrument => $instrument,
from => -1,
to => 1,
step => 0.1,
backsweep => 1, # points: -1, -0.9, ..., 0.9, 1, 0.9, ..., -1
);
my $datafile = sweep_datafile(columns => ['volt', 'current']);
$datafile->add_plot(x => 'volt', y => 'current');
my $meas = sub {
my $sweep = shift;
my $volt = $source->cached_level();
my $current = $multimeter->get_value();
$sweep->log(volt => $volt, current => $current);
};
$sweep->start(
datafiles => [$datafile],
measurement => $meas,
);
#
# 2D sweep (quantum dot stability diagram)
#
my $gate = instrument(...);
my $bias = instrument(...);
my $multimeter = instrument(...);
# master sweep
my $gate_sweep = sweep(
type => 'Step::Voltage',
instrument => $gate,
from => -5,
to => 5,
step => 0.01
);
# slave sweep
my $bias_sweep = sweep(
type => 'Step::Voltage',
instrument => $bias,
from => -1,
to => 1,
step => 0.01
);
my $datafile = sweep_datafile(columns => [qw/gate bias current/]);
$datafile->add_plot(
type => 'pm3d',
x => 'gate',
y => 'bias',
z => 'current'
);
my $meas = sub {
my $sweep = shift;
my $gate_v = $gate->cached_level();
my $bias_v = $bias->cached_level();
my $current = $multimeter->get_value();
$sweep->log(gate => $gate_v, bias => $bias_v, current => $current);
};
$gate_sweep->start(
slaves => [$bias_sweep],
datafiles => [$datafile],
measurement => $meas
);
=head1 DESCRIPTION
This C<sweep> constructor defines the following arguments
=over
=item * from/to/step
define a linear range of points.
=item * list
alternative to from/to/step, give an arbitrary arrayref of points.
=item * points/steps
alternative to from/to/step. Lets define multiple segments with different steps, e.g.
points => [0,1,2],
steps => [0.5, 0.2],
is equivalent to
list => [0, 0.5, 1, 1, 1.2, 1.4, 1.6, 1.8, 2]
If C<steps> has fewer elements than segments provided in C<points>, reuse the last value in C<steps>.
=item * points/step
points => [...],
step => $x, # equivalent to steps => [$x]
=item * backsweep
Include a backsweep: After finishing the sweep, go through all points in
reverse.
=item * setter
A coderef which will be called to change the source level.
Use this if you do some arcane type of sweep which does not justify its own
sweep subclass.
Sweep subclasses like L<Lab::Moose::Sweep::Step::Voltage> will
define defaults for this. E.g. for the Voltage sweep:
sub {
my $sweep = shift;
my $value = shift;
$sweep->instrument->set_level(value => $value);
};
=back
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2025 by the Lab::Measurement team; in detail:
Copyright 2017-2018 Simon Reinhardt
2023 Mia Schambeck
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut