-
Notifications
You must be signed in to change notification settings - Fork 11
/
instr.py
3266 lines (2534 loc) · 118 KB
/
instr.py
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
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from __future__ import print_function
import os
import shutil
import datetime
import yaml
import subprocess
import copy
import warnings
import re
from IPython.display import IFrame
from libpyvinyl.BaseCalculator import BaseCalculator
from libpyvinyl.Parameters.Collections import CalculatorParameters
from mcstasscript.data.pyvinylData import pyvinylMcStasData, pyvinylMCPLData
from mcstasscript.data.MCPLDataFormat import MCPLDataFormat
from mcstasscript.helper.mcstas_objects import DeclareVariable
from mcstasscript.helper.mcstas_objects import provide_parameter
from mcstasscript.helper.mcstas_objects import write_parameter
from mcstasscript.helper.mcstas_objects import Component
from mcstasscript.helper.component_reader import ComponentReader
from mcstasscript.helper.managed_mcrun import ManagedMcrun
from mcstasscript.helper.formatting import is_legal_filename
from mcstasscript.helper.formatting import bcolors
from mcstasscript.helper.unpickler import CustomMcStasUnpickler, CustomMcXtraceUnpickler
from mcstasscript.helper.exceptions import McStasError
from mcstasscript.helper.beam_dump_database import BeamDumpDatabase
from mcstasscript.helper.check_mccode_version import check_mcstas_major_version
from mcstasscript.helper.check_mccode_version import check_mcxtrace_major_version
from mcstasscript.helper.name_inspector import find_python_variable_name
from mcstasscript.helper.search_statement import SearchStatement, SearchStatementList
from mcstasscript.instrument_diagram.make_diagram import instrument_diagram
from mcstasscript.instrument_diagnostics.intensity_diagnostics import IntensityDiagnostics
class McCode_instr(BaseCalculator):
"""
Main class for writing a McCode instrument using McStasScript
Initialization of McCode_instr sets the name of the instrument file
and its methods are used to add all aspects of the instrument file.
The class also holds methods for writing the finished instrument
file to disk and to run the simulation. This is meant as a base class
that McStas_instr and McXtrace_instr inherits from, these have to provide
some attributes. Inherits from libpyvinyl BaseCalculator in order to
harmonize input with other simulation engines.
Required attributes in superclass
---------------------------------
executable : str
Name of executable, mcrun or mxrun
particle : str
Name of probe particle, "neutron" or "x-ray"
package_name : str
Name of package, "McStas" or "McXtrace"
Attributes
----------
name : str
name of instrument file
author : str, default "Python Instrument Generator"
name of user of McStasScript, written to the file
origin : str, default "ESS DMSC"
origin of instrument file (affiliation)
input_path : str, default "."
directory in which simulation is executed, uses components found there
output_path : str
directory in which the data is written
executable_path : str
absolute path of mcrun command, or empty if it is in path
parameters : ParameterContainer
contains all input parameters to be written to file
declare_list : list of DeclareVariable instances
contains all declare parrameters to be written to file
initialize_section : str
string containing entire initialize section to be written
trace_section : str
string containing trace section (OBSOLETE)
finally_section : str
string containing entire finally section to be written
component_list : list of component instances
list of components in the instrument
component_name_list : list of strings
list of names of the components in the instrument
component_class_lib : dict
dict of custom Component classes made at run time
component_reader : ComponentReader
ComponentReader instance for reading component files
package_path : str
Path to mccode package containing component folders
run_settings : dict
Dict of options set with settings
data : list
List of McStasData objects produced by last run
Methods
-------
add_parameter(*args, **kwargs)
Adds input parameter to the define section
add_declare_var(type, name)
Add declared variable called name of given type to the declare section
append_declare(string)
Appends to the declare section
append_initialize(string)
Appends a string to the initialize section, then adds new line
append_initialize_no_new_line(string)
Appends a string to the initialize section
append_finally(string)
Appends a string to finally section, then adds new line
append_finally_no_new_line(string)
Appends a string to finally section
append_trace(string)
Obsolete method, add components instead (used in write_c_files)
append_trace_no_new_line(string)
Obsolete method, add components instead (used in write_c_files)
available_components(string)
Shows available components in given category
component_help(name)
Shows help on component of given name
add_component(instance_name, component_name, **kwargs)
Add a component to the instrument file
copy_component(new_component_name, original_component, **kwargs)
Makes a copy of original_component with new_component_name
get_component(instance_name)
Returns component instance with name instance_name
get_last_component()
Returns component instance of last component
set_component_parameter(instance_name, dict)
Adds parameters as dict to component with instance_name
set_component_AT(instance_name, AT_data, **kwargs)
Sets position of component named instance_name
set_component_ROTATED(instance_name, ROTATED_data, **kwargs)
Sets rotation of component named instance_name
set_component_RELATIVE(instane_name, string)
Sets position and rotation reference for named component
set_component_WHEN(instance_name, string)
Sets WHEN condition of named component, is logical c expression
set_component_GROUP(instance_name, string)
Sets GROUP name of component named instance_name
append_component_EXTEND(instance_name, string)
Appends a line to EXTEND section of named component
set_component_JUMP(instance_name, string)
Sets JUMP code for named component
set_component_SPLIT(instance_name, string)
Sets SPLIT value for named component
set_component_c_code_before(instance_name, string)
Sets c code before the component
set_component_c_code_after(instance_name, string)
Sets c code after the component
set_component_comment(instance_name, string)
Sets comment to be written before named component
print_component(instance_name)
Prints an overview of current state of named component
print_component_short(instance_name)
Prints short overview of current state of named component
show_components()
Prints overview of postion / rotation of all components
write_c_files()
Writes c files for %include in generated_includes folder
write_full_instrument()
Writes full instrument file to current directory
show_instrument()
Shows instrument using mcdisplay
set_parameters(dict)
Inherited from libpyvinyl BaseCalculator
settings(**kwargs)
Sets settings for performing simulation
backengine()
Performs simulation, saves in data attribute
run_full_instrument(**kwargs)
Depricated method for performing the simulation
interface()
Shows interface with jupyter notebook widgets
get_interface_data()
Returns data set from latest simulation in widget
"""
def __init__(self, name, parameters=None, author=None,
origin=None, ncount=None, mpi="not_set", seed=None,
force_compile=None, output_path=None,
increment_folder_name=None, custom_flags=None,
executable_path=None, executable=None,
suppress_output=None, gravity=None, input_path=None,
package_path=None, checks=None, NeXus=None,
save_comp_pars=None, openacc=None):
"""
Initialization of McStas Instrument
Parameters
----------
name : str
Name of project, instrument file will be name + ".instr"
keyword arguments:
parameters : ParameterContainer or CalculatorParameters
Parameters for this instrument
author : str
Name of author, written in instrument file
origin : str
Affiliation of author, written in instrument file
input_path : str
Work directory, will load components from this folder
mpi : int
Number of mpi threads to use in simulation
output_path : str
Sets data_folder_name
increment_folder_name : bool
Will update output_path if folder already exists, default True
ncount : int
Sets ncount
mpi : int
Sets thread count
force_compile : bool
If True (default) new instrument file is written, otherwise not
custom_flags : str
Sets custom_flags passed to mcrun
executable : str
Name of the executable
executable_path : str
Path to mcrun command, "" if already in path
suppress_output : bool
Set to True to surpress output
gravity : bool
If True, gravity will be simulated
save_comp_pars : bool
If True, McStas run writes all comp pars to disk
"""
super().__init__(name, input=[],
output_keys=[name + "_data"],
output_data_types=[pyvinylMcStasData],
parameters=parameters)
# Check required attributes has been set by class that inherits
if not (hasattr(self, "particle") or
hasattr(self, "package_name")):
raise AttributeError("McCode_instr is a base class, use "
+ "McStas_instr or McXtrace_instr instead.")
if not is_legal_filename(self.name + ".instr"):
raise NameError("The instrument is called: \""
+ self.name
+ "\" resulting in an instrument file named: \""
+ self.name + ".instr"
+ "\" which is not a legal filename")
if self.calculator_base_dir == "BaseCalculator":
# If the base_dir was not set, set default to depend on instrument name
self.calculator_base_dir = self.name + "_data"
if author is None:
self.author = "Python " + self.package_name
self.author += " Instrument Generator"
else:
self.author = str(author)
if origin is None:
self.origin = "ESS DMSC"
else:
self.origin = str(origin)
# Attempt to classify given parameters in McStas context
for parameter in self.parameters.parameters.values():
if isinstance(parameter.value, (float, int)):
parameter.type = "double"
elif isinstance(parameter.value, (str)):
parameter.type = "string"
else:
# They will be typed when the instrument is written
parameter.type = None
self._run_settings = {} # Settings for running simulation
# Sets max_line_length and adds paths to run_settings
self._read_calibration()
# Settings that can't be changed later
if input_path is not None:
self.input_path = str(input_path)
if not os.path.isdir(self.input_path):
raise RuntimeError("Given input_path does not point to a "
+ "folder:\"" + self.input_path + '"')
else:
self.input_path = "."
self._run_settings["run_path"] = self.input_path
if package_path is not None:
if not os.path.isdir(str(package_path)):
raise RuntimeError("The package_path provided to mccode_instr "
+ " does not point to a + directory: \""
+ str(package_path) + "\"")
self._run_settings["package_path"] = package_path
# Settings for run that can be adjusted by user
provided_run_settings = {"executable": executable,
"checks": True,
"NeXus": False}
if executable_path is not None:
provided_run_settings["executable_path"] = str(executable_path)
if force_compile is not None:
provided_run_settings["force_compile"] = force_compile
else:
provided_run_settings["force_compile"] = True
if ncount is not None:
provided_run_settings["ncount"] = ncount
if mpi != "not_set":
provided_run_settings["mpi"] = mpi
if gravity is not None:
provided_run_settings["gravity"] = gravity
if seed is not None:
provided_run_settings["seed"] = str(seed)
if custom_flags is not None:
provided_run_settings["custom_flags"] = custom_flags
if suppress_output is not None:
provided_run_settings["suppress_output"] = suppress_output
if checks is not None:
provided_run_settings["checks"] = checks
if output_path is not None:
provided_run_settings["output_path"] = output_path
if increment_folder_name is not None:
provided_run_settings["increment_folder_name"] = increment_folder_name
if NeXus is not None:
provided_run_settings["NeXus"] = NeXus
if save_comp_pars is not None:
provided_run_settings["save_comp_pars"] = save_comp_pars
else:
provided_run_settings["save_comp_pars"] = False
if openacc is not None:
provided_run_settings["openacc"] = openacc
# Set run_settings, perform input sanitation
self.settings(**provided_run_settings)
# Read info on active McStas components
package_path = self._run_settings["package_path"]
run_path = self._run_settings["run_path"]
self.component_reader = ComponentReader(package_path,
input_path=run_path)
self.component_class_lib = {}
self.widget_interface = None
# Holds major version of underlying package
self.mccode_version = None
# Avoid initializing if loading from dump
if not hasattr(self, "declare_list"):
self.declare_list = []
self.user_var_list = []
self.dependency_statement = ""
self.search_statement_list = SearchStatementList()
self.initialize_section = ("// Start of initialize for generated "
+ name + "\n")
self.trace_section = ("// Start of trace section for generated "
+ name + "\n")
self.finally_section = ("// Start of finally for generated "
+ name + "\n")
# Handle components
self.component_list = [] # List of components (have to be ordered)
# Run subset settings
self.run_from_ref = None
self.run_to_ref = None
self.run_to_comment = ""
self.run_to_name = None
self.run_from_component_parameters = None
self.run_to_component_parameters = None
# DumpDatabase
self.dump_database = BeamDumpDatabase(self.name, self.input_path)
@property
def output_path(self) -> str:
return self.base_dir
@output_path.setter
def output_path(self, value: str) -> None:
self.calculator_base_dir = value
def init_parameters(self):
"""
Create empty ParameterContainer for new instrument
"""
self.parameters = CalculatorParameters()
def _read_calibration(self):
"""
Placeholder method that should be overwritten by classes
that inherit from McCode_instr.
"""
pass
def reset_run_points(self):
"""
Reset run_from and run_to points to the full instrument
"""
self.run_from_ref = None
self.run_to_ref = None
def show_run_subset(self):
"""
Shows current subset of instrument selected with run_from and run_to methods
"""
if self.run_from_ref is None and self.run_to_ref is None:
print("No run_from or run_to point set.")
return
if self.run_from_ref is None:
print("Running from start of the instrument", end="")
else:
print(f"Running from component named '{self.run_from_ref}'", end="")
if self.run_to_ref is None:
print(" to the end of the instrument.")
else:
print(f" to component named '{self.run_to_ref}'.")
def run_to(self, component_ref, run_name="Run", comment=None, **kwargs):
"""
Set limit for instrument, only run to given component, save MCPL there
The method accepts keywords for the MCPL output component allowing to
store for example userflags or setting the filename directly.
component_ref : str / component object
Name of component where the instrument simulation should end
run_name : str
Name associated with the generated beam dump
comment : str
Comment asscoiated with the generated beam dump
"""
if isinstance(component_ref, Component):
component_ref = component_ref.name
# Check references are valid
self.subset_check(start_ref=self.run_from_ref, end_ref=component_ref)
self.run_to_ref = component_ref
self.run_to_name = run_name
if comment is not None:
self.run_to_comment = str(comment)
else:
self.run_to_comment = ""
if component_ref is not None:
mcpl_par_name = "run_to_mcpl"
if mcpl_par_name not in self.parameters.parameters:
# Need to add parameter to instrument for mcpl filename
self.add_parameter("string", mcpl_par_name)
if "filename" not in kwargs:
# Generate a reasonable filename
auto_name = '"' + self.name + "_" + component_ref + ".mcpl" + '"'
self.set_parameters({mcpl_par_name: auto_name})
else:
# Set the instrument parameter to the given filename
self.set_parameters({mcpl_par_name: kwargs["filename"]})
# Set the mcpl component parameter to the filename instrument parameter
kwargs["filename"] = mcpl_par_name
# Check the given keywords arguments are legal for the MCPL output component
dummy_MCPL = self._create_component_instance("MCPL_output", "MCPL_output")
try:
dummy_MCPL.set_parameters(kwargs)
except:
# Provide information on what stage caused the problem
print("Problems detected with input arguments for MCPL output component")
# Show the exception for the failure to set parameters on the component
dummy_MCPL.set_parameters(kwargs)
# Store parameters for the MCPL output component
self.run_to_component_parameters = kwargs
def run_from(self, component_ref, run_name=None, tag=None, **kwargs):
"""
Set limit for instrument, only run from given component, load MCPL ot start
The method accepts keywords for the MCPL input component allowing to
set for example the smear for direction / energy / position and
repeat count.
component_ref : str / component object
Name of component where the instrument simulation should start
run_name : str
Run name of dump to use as starting point of simulation
tag : integer
Tag of the desired dump (only allowed if run_name is given)
"""
if isinstance(component_ref, Component):
component_ref = component_ref.name
# Check references are valid
self.subset_check(start_ref=component_ref, end_ref=self.run_to_ref)
self.run_from_ref = component_ref
if component_ref is not None:
mcpl_par_name = "run_from_mcpl"
if mcpl_par_name not in self.parameters.parameters:
# Need to add parameter to instrument for mcpl filename
self.add_parameter("string", mcpl_par_name)
if "filename" not in kwargs:
# Find newest dump from database
newest_dump = self.dump_database.newest_at_point(component_ref, run=run_name)
auto_name = '"' + newest_dump.data["data_path"] + '"'
self.set_parameters({mcpl_par_name: auto_name})
else:
self.set_parameters({mcpl_par_name: kwargs["filename"]})
if run_name is not None and tag is not None:
dump = self.dump_database.get_dump(component_ref, run_name, tag)
dump_filename = '"' + dump.data["data_path"] + '"'
self.set_parameters({mcpl_par_name: dump_filename})
kwargs["filename"] = mcpl_par_name
# Ensure the kwargs are allowed
dummy_MCPL = self._create_component_instance("MCPL_input", "MCPL_input")
try:
dummy_MCPL.set_parameters(kwargs)
except:
print("Problems detected with input arguments for MCPL input component")
dummy_MCPL.set_parameters(kwargs)
self.run_from_component_parameters = kwargs
def show_dumps(self):
component_names = [x.name for x in self.component_list]
self.dump_database.show_in_order(component_names)
def show_dump(self, point, run_name=None, tag=None):
if isinstance(point, Component):
point = point.name
self.dump_database.get_dump(point, run_name, tag).print_all()
def subset_check(self, start_ref, end_ref):
"""
Checks that when the instrument is broken into subsets, it is still valid
start_ref : str
Name of starting component
end_ref : str
Name of end component
"""
start_i, end_i = self.get_component_subset_index_range(start_ref, end_ref)
if start_i > end_i:
raise McStasError("Running from '" + start_ref + "' to '" + end_ref
+ "' not possible as '" + end_ref + "' is before '"
+ start_ref + "' in the component sequence.")
if start_i == end_i:
raise McStasError("Running from and to the same component is not supported "
+ "here both run_from and run_to are set to "
+ "'" + start_ref + "'.")
# Check current subset of instrument is self contained
is_self_contained = True
try:
self.check_for_relative_errors(start_ref=start_ref, end_ref=end_ref)
except McStasError:
is_self_contained = False
if not is_self_contained:
print("When using a subset of the instrument, component references "
"must be only internal as these sections are split into separate "
"files. When seeing only the specified subset of the instrument, "
"this reference can not be resolved.")
self.check_for_relative_errors(start_ref=start_ref, end_ref=end_ref)
if end_ref is None:
# If there is no end_ref, there are no parts after to check
return
# Check the part after is self consistent
is_self_contained = True
try:
self.check_for_relative_errors(start_ref=end_ref, allow_absolute=False)
except McStasError:
is_self_contained = False
if not is_self_contained:
print("When using a subset of the instrument, component references "
"must be only internal as these sections are split into separate "
"files. When seeing only the specified subset of the instrument, "
"this reference can not be resolved. \n"
"In this case the remaining instrument would fail.")
self.check_for_relative_errors(start_ref=end_ref, allow_absolute=False)
def add_parameter(self, *args, **kwargs):
"""
Method for adding input parameter to instrument
Type does not need to be specified, McStas considers that a floating
point value with the type 'double'. Uses libpyvinyl Parameter object.
Examples
--------
Creates a parameter with name wavelength and associated comment
add_parameter("wavelength", comment="wavelength in [AA]")
Creates a parameter with name A3 and default value
add_parameter("A3", value=30, comment="A3 angle in [deg]")
Creates a parameter with type string and name sample_name
add_parameter("string", "sample_name")
Parameters
----------
(optional) parameter type : str
type of input parameter, double, int, string
parameter name : str
name of parameter
keyword arguments
value : float, int or str
Default value of parameter
unit : str
Unit to be displayed
comment : str
Comment displayed next to declaration of parameter
options : list or value
list or value of allowed values for this parameter
"""
names = [x.name for x in self.declare_list
if isinstance(x, DeclareVariable)]
names += [x.name for x in self.user_var_list
if isinstance(x, DeclareVariable)]
names += [x.name for x in self.parameters.parameters.values()]
par = provide_parameter(*args, **kwargs)
if par.name in names:
raise NameError(f"A parameter or variable with name '{par.name}'"
f" already exists!")
self.parameters.add(par)
return par
def show_parameters(self, line_length=None):
"""
Method for displaying current instrument parameters
line_limit : int
Maximum line length for terminal output
"""
if len(self.parameters.parameters) == 0:
print("No instrument parameters available")
return
if line_length is None:
line_length = self.line_limit
# Find longest fields
types = []
names = []
values = []
comments = []
for parameter in self.parameters.parameters.values():
types.append(str(parameter.type))
names.append(str(parameter.name))
values.append(str(parameter.value))
if parameter.comment is None:
comments.append("")
else:
comments.append(str(parameter.comment))
longest_type = len(max(types, key=len))
longest_name = len(max(names, key=len))
longest_value = len(max(values, key=len))
# In addition to the data 11 characters are added before the comment
comment_start_point = longest_type + longest_name + longest_value + 11
longest_comment = len(max(comments, key=len))
length_for_comment = line_length - comment_start_point
# Print to console
for parameter in self.parameters.parameters.values():
print(str(parameter.type).ljust(longest_type), end=' ')
print(str(parameter.name).ljust(longest_name), end=' ')
if parameter.value is None:
print(" ", end=' ')
print(" ".ljust(longest_value + 1), end=' ')
else:
print(" =", end=' ')
print(str(parameter.value).ljust(longest_value + 1), end=' ')
if parameter.comment is None:
c_comment = ""
else:
c_comment = "// " + str(parameter.comment)
if (length_for_comment < 5
or length_for_comment > len(c_comment)):
print(c_comment)
else:
# Split comment into several lines
comment = c_comment
words = comment.split(" ")
words_left = len(words)
last_index = 0
current_index = 0
comment = ""
iterations = 0
max_iterations = 50
while words_left > 0:
iterations += 1
if iterations > max_iterations:
# Something went long, print on one line
break
line_left = length_for_comment
while line_left > 0:
if current_index >= len(words):
current_index = len(words) + 1
break
line_left -= len(str(words[current_index])) + 1
current_index += 1
current_index -= 1
for word in words[last_index:current_index]:
comment += word + " "
words_left = len(words) - current_index
if words_left > 0:
comment += "\n" + " " * comment_start_point
last_index = current_index
if not iterations == max_iterations + 1:
print(comment)
else:
print(c_comment.ljust(longest_comment))
def show_variables(self):
"""
Shows declared variables in instrument
"""
all_variables = [x for x in self.declare_list + self.user_var_list
if isinstance(x, DeclareVariable)]
type_heading = "type"
variable_types = [x.type for x in all_variables]
variable_types.append(type_heading)
max_type_length = len(max(variable_types, key=len))
name_heading = "variable name"
variable_names = [x.name for x in all_variables]
variable_names.append(name_heading)
max_name_length = len(max(variable_names, key=len))
vector_heading = "array length"
variable_vector = [str(x.vector) for x in all_variables]
variable_vector.append(vector_heading)
max_vector_length = len(max(variable_vector, key=len))
value_heading = "value"
variable_values = [str(x.value) for x in all_variables]
variable_values.append(value_heading)
max_value_length = len(max(variable_values, key=len))
padding = 2
header = ""
header += type_heading.ljust(max_type_length + padding)
header += name_heading.ljust(max_name_length + padding)
header += vector_heading.ljust(max_vector_length + padding)
header += value_heading.ljust(max_value_length + padding)
header += "\n"
header += "-"*(max_type_length + max_name_length + max_value_length
+ max_vector_length + 3*padding)
header += "\n"
string = "DECLARE VARIABLES \n"
string += header
if len(self.user_var_list) > 0:
first_user_var = self.user_var_list[0]
else:
first_user_var = None
for variable in all_variables:
if variable is first_user_var:
string += "\n"
string += "USER VARIABLES (per neutron, only use in EXTEND)\n"
string += header
string += str(variable.type).ljust(max_type_length + padding)
string += str(variable.name).ljust(max_name_length + padding)
if variable.vector != 0:
vector_string = str(variable.vector)
else:
vector_string = ""
string += vector_string.ljust(max_vector_length + padding)
if variable.value != "":
value_string = str(variable.value)
else:
value_string = ""
string += value_string.ljust(max_value_length + padding)
string += "\n"
print(string)
def add_declare_var(self, *args, **kwargs):
"""
Method for adding declared variable to instrument
Parameters
----------
parameter type : str
type of input parameter
parameter name : str
name of parameter
keyword arguments
array : int
default 0 for scalar, if specified length of array
value : any
Initial value of parameter, can be list of length vector
comment : str
Comment displayed next to declaration of parameter
"""
# DeclareVariable class documented independently
declare_par = DeclareVariable(*args, **kwargs)
names = [x.name for x in self.declare_list
if isinstance(x, DeclareVariable)]
names += [x.name for x in self.user_var_list
if isinstance(x, DeclareVariable)]
names += [x.name for x in self.parameters.parameters.values()]
if declare_par.name in names:
raise NameError("Variable with name '" + declare_par.name
+ "' already present in instrument!")
self.declare_list.append(declare_par)
return declare_par
def append_declare(self, string):
"""
Method for appending code to the declare section directly
This method is not meant for declaring simple variables which
should be done using add_declare_var. This method can be used
to declare functions, structures and unions directly.
Parameters
----------
string : str
code to be added to declare section
"""
self.declare_list.append(string)
def add_user_var(self, *args, **kwargs):
"""
Method for adding user variable to instrument
Parameters
----------
parameter type : str
type of input parameter
parameter name : str
name of parameter
keyword arguments
array : int
default 0 for scalar, if specified length of array
comment : str
Comment displayed next to declaration of parameter
"""
if "value" in kwargs:
raise ValueError("Value not allowed for UserVars.")
# DeclareVariable class documented independently
user_par = DeclareVariable(*args, **kwargs)
names = [x.name for x in self.declare_list
if isinstance(x, DeclareVariable)]