File Coverage

blib/lib/Lab/XPRESS/Sweep/Temperature.pm
Criterion Covered Total %
statement 17 165 10.3
branch 0 52 0.0
condition 0 9 0.0
subroutine 6 15 40.0
pod 1 9 11.1
total 24 250 9.6


line stmt bran cond sub pod time code
1             package Lab::XPRESS::Sweep::Temperature;
2             #ABSTRACT: Temperature sweep
3             $Lab::XPRESS::Sweep::Temperature::VERSION = '3.880';
4 1     1   1724 use v5.20;
  1         4  
5              
6 1     1   13 use Lab::XPRESS::Sweep;
  1         2  
  1         32  
7 1     1   593 use Statistics::Descriptive;
  1         8321  
  1         34  
8 1     1   7 use Time::HiRes qw/usleep/;
  1         3  
  1         11  
9 1     1   108 use strict;
  1         4  
  1         842  
10              
11             our @ISA = ('Lab::XPRESS::Sweep');
12              
13             sub new {
14 0     0 0   my $proto = shift;
15 0           my @args = @_;
16 0   0       my $class = ref($proto) || $proto;
17              
18             # define default values for the config parameters:
19             my $self->{default_config} = {
20 0           id => 'Temperature_sweep',
21             filename_extension => 'T=',
22             interval => 1,
23             points => [ 0, 10 ],
24             duration => [1],
25             stepwidth => 1,
26             pid => undef,
27             mode => 'continuous',
28             allowed_instruments => [
29             qw/Lab::Instrument::ITC Lab::Instrument::TCD
30             Lab::Instrument::OI_ITC503 Lab::Instrument::OI_Triton
31             Lab::Instrument::Lakeshore340 Lab::Instrument::TRMC2/,
32             ],
33             allowed_sweep_modes => [ 'continuous', 'step', 'list' ],
34              
35             sensor => undef,
36             stabilize_measurement_interval => 1,
37             stabilize_observation_time => 3 * 60,
38             tolerance_setpoint => 0.2,
39             std_dev_instrument => 0.15,
40             std_dev_sensor => 0.15,
41              
42             max_stabilization_time => undef,
43             setter_args => [],
44             getter_args => [],
45              
46             };
47              
48             # create self from Sweep basic class:
49 0           $self = $class->SUPER::new( $self->{default_config}, @args );
50 0           bless( $self, $class );
51              
52             # check and adjust config values if necessary:
53 0           $self->check_config_paramters();
54              
55             # init mandatory parameters:
56 0           $self->{DataFile_counter} = 0;
57 0           $self->{DataFiles} = ();
58              
59 0           return $self;
60             }
61              
62             sub check_config_paramters {
63 0     0 0   my $self = shift;
64              
65             # No Backsweep allowed; adjust number of Repetitions if Backsweep is 1:
66 0 0         if ( $self->{config}->{mode} eq 'continuous' ) {
67 0 0         if ( $self->{config}->{backsweep} == 1 ) {
68 0           $self->{config}->{repetitions} /= 2;
69 0           $self->{config}->{backsweep} = 0;
70             }
71             }
72              
73 0 0         if ( defined $self->{config}->{pid} ) {
74 0 0         if ( ref( $self->{config}->{pid} ) eq 'ARRAY' ) {
75 0 0         if ( ref( @{ $self->{config}->{pid} }[0] ) ne 'ARRAY' ) {
  0            
76 0           $self->{config}->{pid} = [ $self->{config}->{pid} ];
77             }
78             }
79             else {
80 0           $self->{config}->{pid} = [ [ $self->{config}->{pid} ] ];
81             }
82             }
83             else {
84 0           $self->{config}->{pid} = [undef];
85             }
86              
87             # Set loop-Interval to Measurement-Interval:
88 0           $self->{loop}->{interval} = $self->{config}->{interval};
89              
90             }
91              
92             sub start_continuous_sweep {
93 0     0 0   my $self = shift;
94              
95 0           print
96 0           "Stabilize Temperature at upper limit (@{$self->{config}->{points}}[1] K) \n";
97 0           $self->stabilize( @{ $self->{config}->{points} }[1] );
  0            
98              
99 0           print "Reached upper limit -> start cooling ... \n";
100 0           $self->{config}->{instrument}->set_heatercontrol('MAN');
101 0           $self->{config}->{instrument}->set_heateroutput(0);
102             }
103              
104             sub go_to_next_step {
105 0     0 0   my $self = shift;
106              
107 0           $self->stabilize( @{ $self->{config}->{points} }[ $self->{iterator} ] );
  0            
108              
109             }
110              
111             sub exit_loop {
112 0     0 0   my $self = shift;
113              
114 0           my $TEMPERATURE;
115              
116 0 0         if ( $self->{config}->{mode} =~ /step|list/ ) {
    0          
117 0 0         if (
118             not
119 0           defined @{ $self->{config}->{points} }[ $self->{iterator} + 1 ] )
120             {
121 0           return 1;
122             }
123             }
124             elsif ( $self->{config}->{mode} =~ /continuous/ ) {
125 0 0         if ( defined $self->{config}->{sensor} ) {
126 0           $TEMPERATURE = $self->{config}->{sensor}->get_value();
127             }
128             else {
129 0           $TEMPERATURE = $self->get_value();
130             }
131 0 0         if ( $TEMPERATURE < @{ $self->{config}->{points} }[0] ) {
  0            
132 0           return 1;
133             }
134             else {
135 0           return 0;
136             }
137              
138             }
139              
140 0           return 0;
141              
142             }
143              
144             sub get_value {
145 0     0 1   my $self = shift;
146             return $self->{config}->{instrument}
147 0           ->get_value( @{ $self->{config}->{getter_args} } );
  0            
148             }
149              
150             sub halt {
151 0     0 0   return shift;
152             }
153              
154             sub stabilize {
155              
156 1     1   11 use Term::ReadKey;
  1         7  
  1         1127  
157              
158 0     0 0   my $self = shift;
159 0           my $setpoint = shift;
160              
161 0           my $time0 = time();
162              
163 0           my @T_INSTR;
164             my @T_SENSOR;
165              
166 0           my @MEDIAN_INSTR;
167 0           my @MEDIAN_SENSOR;
168              
169 0           my $MEDIAN_INSTR_MEDIAN = undef;
170 0           my $INSTR_STD_DEV = undef;
171 0           my $SENSOR_STD_DEV = undef;
172              
173 0           my $criterion_setpoint = 0;
174 0           my $criterion_std_dev_INSTR = 0;
175 0           my $criterion_std_dev_SENSOR = 1;
176              
177 0           my $pid = shift @{ $self->{config}->{pid} };
  0            
178 0 0         if ( defined $pid ) {
179              
180 0           my $p = shift @{$pid};
  0            
181 0           my $i = shift @{$pid};
  0            
182 0           my $d = shift @{$pid};
  0            
183              
184 0           $self->{config}->{instrument}->set_PID( $p, $i, $d );
185              
186             }
187              
188 0           $self->{config}->{instrument}->set_heatercontrol('AUTO');
189             $self->{config}->{instrument}
190 0           ->set_T( $setpoint, @{ $self->{config}->{setter_args} } );
  0            
191              
192 0           local $| = 1;
193              
194 0           my $time0 = time();
195              
196 0           print "Stabilize Temperature at $setpoint K ... (Press 'c' to skip)\n\n";
197              
198             #my $line1 = "\rElapsed: $elapsed_time \n Current Temp INSTR: @T_INSTR[-1] \n Current Temp SENSOR: @T_SENSOR[-1] \n ";
199             #my $line2 = "Current Median: @MEDIAN_INSTR[-1] \n Std. Dev. T Instr. : $INSTR_STD_DEV \n Std. Dev. T Sensor : $SENSOR_STD_DEV \n ";
200             #my $line3 = "CRIT SETPOINT: $criterion_setpoint \n CRIT Std. Dev. T Instr. : $criterion_std_dev_INSTR \n CRIT Std. Dev. T Sensor : $criterion_std_dev_SENSOR \n ";
201 0           print " Time " . " | "
202             . " TEMP " . " | " . "SENS " . " | " . "MED_I" . " | " . "ISD "
203             . " | " . "SSD " . " | " . "C1" . " | " . "C2" . " | " . "C3" . " \n";
204              
205 0           ReadMode('cbreak');
206 0           while (1) {
207              
208             #----------COLLECT DATA--------------------
209 0           my $T_INSTR = $self->get_value();
210 0           push( @T_INSTR, $T_INSTR );
211              
212 0 0         if (
213             scalar @T_INSTR > int(
214             $self->{config}->{stabilize_observation_time}
215             / $self->{config}->{stabilize_measurement_interval}
216             )
217             ) {
218 0           shift(@T_INSTR);
219              
220 0           my $stat = Statistics::Descriptive::Full->new();
221 0           $stat->add_data( \@T_INSTR );
222 0           my $MEDIAN_INSTR = $stat->median();
223              
224 0           push( @MEDIAN_INSTR, $MEDIAN_INSTR );
225              
226 0 0         if (
227             scalar @MEDIAN_INSTR > int(
228             $self->{config}->{stabilize_observation_time}
229             / $self->{config}->{stabilize_measurement_interval}
230             )
231             ) {
232 0           shift(@MEDIAN_INSTR);
233             }
234             }
235              
236 0 0         if ( defined $self->{config}->{sensor} ) {
237 0           my $T_SENSOR = $self->{config}->{sensor}->get_value();
238 0           push( @T_SENSOR, $T_SENSOR );
239              
240 0 0         if (
241             scalar @T_SENSOR > int(
242             $self->{config}->{stabilize_observation_time}
243             / $self->{config}->{stabilize_measurement_interval}
244             )
245             ) {
246 0           shift(@T_SENSOR);
247              
248 0           my $stat = Statistics::Descriptive::Full->new();
249 0           $stat->add_data( \@T_SENSOR );
250 0           my $MEDIAN_SENSOR = $stat->median();
251              
252 0           push( @MEDIAN_SENSOR, $MEDIAN_SENSOR );
253              
254 0 0         if (
255             scalar @MEDIAN_SENSOR > int(
256             $self->{config}->{stabilize_observation_time}
257             / $self->{config}
258             ->{stabilize_measurement_interval}
259             )
260             ) {
261 0           shift(@MEDIAN_SENSOR);
262             }
263             }
264             }
265              
266             #--------CHECK THE CRITERIONS--------------
267              
268 0 0         if ( defined @MEDIAN_INSTR[-1] ) {
269 0 0         if (
270             abs( $setpoint - @MEDIAN_INSTR[-1] )
271             < $self->{config}->{tolerance_setpoint} ) {
272 0           $criterion_setpoint = 1;
273             }
274             else {
275 0           $criterion_setpoint = 0;
276             }
277             }
278              
279             # if (scalar @MEDIAN_INSTR >= int($self->{config}->{stabilize_observation_time}/$self->{config}->{stabilize_measurement_interval}) - 1) {
280              
281             # my $stat = Statistics::Descriptive::Full->new();
282             # $stat->add_data(\@MEDIAN_INSTR);
283             # $MEDIAN_INSTR_MEDIAN = $stat->median();
284              
285             # if (abs($setpoint - $MEDIAN_INSTR_MEDIAN) < $self->{config}->{tolerance_setpoint}) {
286             # $criterion_setpoint = 1;
287             # }
288             # else {
289             # $criterion_setpoint = 0;
290             # }
291             # }
292              
293 0 0         if (
294             scalar @T_INSTR >= int(
295             $self->{config}->{stabilize_observation_time}
296             / $self->{config}->{stabilize_measurement_interval}
297             ) - 1
298             ) {
299 0           my $stat = Statistics::Descriptive::Full->new();
300 0           $stat->add_data( \@T_INSTR );
301 0           $INSTR_STD_DEV = $stat->standard_deviation();
302              
303 0 0         if ( $INSTR_STD_DEV < $self->{config}->{std_dev_instrument} ) {
304 0           $criterion_std_dev_INSTR = 1;
305             }
306             else {
307 0           $criterion_std_dev_INSTR = 0;
308             }
309             }
310              
311 0 0         if ( defined $self->{config}->{sensor} ) {
312 0 0         if (
313             scalar @T_SENSOR >= int(
314             $self->{config}->{stabilize_observation_time}
315             / $self->{config}->{stabilize_measurement_interval}
316             ) - 1
317             ) {
318 0           my $stat = Statistics::Descriptive::Full->new();
319 0           $stat->add_data( \@T_SENSOR );
320 0           $SENSOR_STD_DEV = $stat->standard_deviation();
321              
322 0 0         if ( $SENSOR_STD_DEV < $self->{config}->{std_dev_sensor} ) {
323 0           $criterion_std_dev_SENSOR = 1;
324             }
325             else {
326 0           $criterion_std_dev_SENSOR = 0;
327             }
328             }
329             }
330              
331 0           my $elapsed_time = $self->convert_time( time() - $time0 );
332              
333 0           my $output
334             = $elapsed_time . " | " . sprintf( "%3.4f", @T_INSTR[-1] ) . " | "
335              
336             # . sprintf( "%3.3f", @T_SENSOR[-1] ) . " | "
337             # . sprintf( "%3.3f", @MEDIAN_INSTR[-1] ) . " | "
338             . sprintf( "%2.4f", $INSTR_STD_DEV ) . " | "
339              
340             # . sprintf( "%2.3f", $SENSOR_STD_DEV ) . " | "
341             . $criterion_setpoint . " | "
342             . $criterion_std_dev_INSTR . " | "
343             . $criterion_std_dev_SENSOR;
344              
345 0           print $output;
346              
347 0 0 0       if ( $criterion_std_dev_INSTR
    0          
348             * $criterion_std_dev_SENSOR
349             * $criterion_setpoint ) {
350 0           last;
351             }
352             elsif (
353             defined $self->{config}->{max_stabilization_time}
354             and ( ( time() - $time0 )
355             >= $self->{config}->{max_stabilization_time} )
356             ) {
357 0           last;
358 0           print "\n";
359             }
360              
361             else {
362              
363             }
364              
365 0           my $char = ReadKey(1e-5);
366 0 0 0       if ( defined $char and $char eq 'c' ) {
367 0           last;
368             }
369 0           sleep( $self->{config}->{stabilize_measurement_interval} );
370              
371 0           print "\r";
372              
373             }
374 0           ReadMode('normal');
375 0           $| = 0;
376              
377 0           print "\nTemperature stabilized at $setpoint K \n";
378             }
379              
380             sub convert_time {
381 0     0 0   my $self = shift;
382              
383 0           my $time = shift;
384 0           my $days = int( $time / 86400 );
385 0           $time -= ( $days * 86400 );
386 0           my $hours = int( $time / 3600 );
387 0           $time -= ( $hours * 3600 );
388 0           my $minutes = int( $time / 60 );
389 0           my $seconds = $time % 60;
390              
391 0           $time
392             = sprintf( "%02dh", $hours )
393             . sprintf( "%02dm", $minutes )
394             . sprintf( "%02ds", $seconds );
395 0           return $time;
396             }
397              
398             1;
399              
400             __END__
401              
402             =pod
403              
404             =encoding UTF-8
405              
406             =head1 NAME
407              
408             Lab::XPRESS::Sweep::Temperature - Temperature sweep
409              
410             =head1 VERSION
411              
412             version 3.880
413              
414             =head1 SYNOPSIS
415              
416             use Lab::XPRESS::hub;
417             my $hub = new Lab::XPRESS::hub();
418            
419            
420             my $tsensor = $hub->Instrument('ITC',
421             {
422             connection_type => 'VISA_RS232',
423             gpib_address => 2
424             });
425            
426             my $sweep_temperature = $hub->Sweep('Temperature',
427             {
428             instrument => $tsensor,
429             points => [90,4],
430             mode => 'list'
431             });
432              
433             .
434              
435             =head1 DESCRIPTION
436              
437             Parent: Lab::XPRESS::Sweep
438              
439             The Lab::XPRESS::Sweep::Temperature class implements a module for temperature sweeps in the Lab::XPRESS::Sweep framework.
440              
441             .
442              
443             =head1 CONSTRUCTOR
444              
445             my $sweep_temperature = $hub->Sweep('Temperature',
446             {
447             instrument => $tsensor,
448             points => [90,4],
449             mode => 'list'
450             });
451              
452             Instantiates a new temperature sweep
453              
454             .
455              
456             =head1 PARAMETERS
457              
458             =head2 instrument [Lab::Instrument] (mandatory)
459              
460             Instrument, conducting the sweep. Must be of type Lab:Instrument.
461             Supported instruments: Lab::Instrument::ITC
462              
463             .
464              
465             =head2 mode [string] (default = 'continuous' | 'step' | 'list')
466              
467             continuous: perform a continuous temperature sweep. After the starting temperature has been stabalized by the temperature controller, the heater will be switched off in order to cool down to the final value. Measurements will be performed constantly at the time-interval defined in interval.
468              
469             step: measurements will be performed at discrete values between start and end points defined in parameter points, seperated by a step defined in parameter stepwidth
470              
471             list: measurements will be performed at a list of values defined in parameter points
472              
473             .
474              
475             =head2 points [float array] (mandatory)
476              
477             array of values (in deg) that defines the characteristic points of the sweep.
478             First value is appraoched before measurement begins.
479              
480             Case mode => 'continuous' :
481             List of exactly 2 values, that define start and end point of the sweep. The starting point has to be higher value than the endpont.
482             points => [180, 4] # Start: 180 K / Stop: 4 K
483              
484             Case mode => 'step' :
485             Same as in 'continuous' but the temperature controller will stabalize the temperature at the defined setpoints. A measurement is performed, when the motor is idle.
486              
487             Case mode => 'list' :
488             Array of values, with minimum length 1, that are approached in sequence to perform a measurment.
489              
490             .
491              
492             =head2 stepwidth [float array]
493              
494             This parameter is relevant only if mode = 'step' has been selected.
495             Stepwidth has to be an array of length '1' or greater. The values define the width for each step within the corresponding sweep sequence.
496             If the length of the defined sweep sequence devided by the stepwidth is not an integer number, the last step will be smaller in order to reach the defined points-value.
497              
498             points = [0, 90, 180]
499             stepwidth = [10, 25]
500            
501             ==> steps: 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 115, 140, 165, 180
502              
503             .
504              
505             =head2 number_of_points [int array]
506              
507             can be used instead of 'stepwidth'. Attention: Use only the 'number_of_points' or the 'stepwidth' parameter. Using both will cause an Error!
508             This parameter is relevant only if mode = 'step' has been selected.
509             Number_of_points has to be an array of length '1' or greater. The values defines the number of steps within the corresponding sweep sequence.
510              
511             points = [0, 90, 180]
512             number_of_points = [5, 2]
513            
514             ==> steps: 0, 18, 36, 54, 72, 90, 135, 180
515              
516             .
517              
518             =head2 interval [float] (default = 1)
519              
520             interval in seconds for taking measurement points. Only relevant in mode 'continuous'.
521              
522             .
523              
524             =head2 id [string] (default = 'Temperature_sweep')
525              
526             Just an ID.
527              
528             .
529              
530             =head2 filename_extention [string] (default = 'T=')
531              
532             Defines a postfix, that will be appended to the filenames if necessary.
533              
534             .
535              
536             =head2 delay_before_loop [int] (default = 0)
537              
538             defines the time in seconds to wait after the starting point has been reached.
539              
540             .
541              
542             =head2 delay_in_loop [int] (default = 0)
543              
544             This parameter is relevant only if mode = 'step' or 'list' has been selected.
545             Defines the time in seconds to wait after the value for the next step has been reached.
546              
547             .
548              
549             =head2 delay_after_loop [int] (default = 0)
550              
551             Defines the time in seconds to wait after the sweep has been finished. This delay will be executed before an optional backsweep or optional repetitions of the sweep.
552              
553             =head2 getter_args
554              
555             Setting C<getter_args => [@args]>, the C<get_value> method will be called as
556              
557             $instrument->get_value(@args);
558              
559             =head2 setter_args
560              
561             Setting C<setter_args => [@args]>, the C<set_T> method will be called as
562              
563             $instrument->set_T($setpoint, @args);
564              
565             =head1 CAVEATS/BUGS
566              
567             probably none
568              
569             .
570              
571             =head1 SEE ALSO
572              
573             =over 4
574              
575             =item L<Lab::XPRESS::Sweep>
576              
577             =back
578              
579             =head1 COPYRIGHT AND LICENSE
580              
581             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
582              
583             Copyright 2013 Andreas K. Huettel, Christian Butschkow, Stefan Geissler
584             2014 Christian Butschkow
585             2015 Alois Dirnaichner
586             2016-2017 Andreas K. Huettel, Simon Reinhardt
587             2020 Andreas K. Huettel
588              
589              
590             This is free software; you can redistribute it and/or modify it under
591             the same terms as the Perl 5 programming language system itself.
592              
593             =cut