-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprepare_im_runs.py
More file actions
2378 lines (1737 loc) · 89 KB
/
Copy pathprepare_im_runs.py
File metadata and controls
2378 lines (1737 loc) · 89 KB
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
import json
import os,datetime,sys
import shutil
import getpass
import numpy as np
import pickle
import math
import functools
import re
from scipy import integrate
from scipy.interpolate import interp1d, UnivariateSpline
#import idstools
#from idstools import *
from packaging import version
from os import path
from pathlib import Path
from prepare_im_input import MissingDataError
import inspect
import types
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython import display
import xml.sax
import xml.sax.handler
try:
import setup_nbi_input
except ImportError:
print('setup_input_nbi not present, might cause problems when trying to setup the nbi')
'''
The tools in this script are useful to:
Setup integrated modelling simulations
Setup sensitivities
Run sensitivities
Compare integrated modelling with experimental data
'''
min_imas_version_str = "3.28.0"
min_imasal_version_str = "4.7.2"
try:
import imas
except ImportError:
warnings.warn("IMAS Python module not found or not configured properly, tools need IDS to work!", UserWarning)
if imas is not None:
from imas import imasdef
vsplit = imas.names[0].split("_")
imas_version = version.parse(".".join(vsplit[1:4]))
ual_version = version.parse(".".join(vsplit[5:]))
if imas_version < version.parse(min_imas_version_str):
raise ImportError("IMAS version must be >= %s! Aborting!" % (min_imas_version_str))
if ual_version < version.parse(min_imasal_version_str):
raise ImportError("IMAS AL version must be >= %s! Aborting!" % (min_imasal_version_str))
import sys
username_jetto_tools = getpass.getuser()
sys.path.insert(0, '/afs/eufus.eu/user/g/' + username_jetto_tools + '/python_tools/jetto-pythontools')
try:
import jetto_tools
except ImportError:
warnings.warn("Jetto tools not available. Please check that they are installed in /python_tools/jetto-pythontools or change the previous sys path in the code", UserWarning)
#print(jetto_tools.__version__)
#print(jetto_tools.__file__)
#import duqtools
import copy
'''
--------------- AVAILABLE FUNCTIONS: ------------------
Class IntegratedModellingRuns: sets up everything needed for an integrated modelling simulation.
instructions_list: possibilities are 'setup base', 'setup sens', 'create base', 'create sens', 'run base', 'run sens'
'setup base' - Setup the input for a baserun
'setup sens' - Setup the input for a sensitivity
'create base' - Create the baserun folder
'create sens' - Create the sensitivities folders
'run base' - Run the baserun
'run sens' - Run the sensitivities
Action: setup_create_compare()
db: Name of the ids database
run input: The run where the experimental data are
run start: Where the actual input for the run is taken after manipulation
generator_name: Name of the generator from which all the settings that will not be changed will be taken
time_start: Starting time for the simulation. Options are: time_start, 'core_profiles', 'equilibrium'. For the latter 2 the first time in the respective ids will be used
time_end: Time_end, auto (untested)
esco_timesteps: Number of times esco will be called (homogeneous)
output_timesteps: Number of times the output will be printed (homogeneous)
force_run: If true, will not stop if the output ids aready exists
density_feedback: If True, will setup the density feedback, with the density in summary.line_averaged.density. Does not use the pulse scheduler (It does not work yet)
zeff_options: Describe how to set the time trace for zeff
-- 'flat maximum' Sets the maximum zeff everywhere
-- 'flat minimum' Sets the minimum zeff everywhere
-- 'flat median' Sets the median zeff everywhere
-- 'impurity from flattop' Auto detects the flattop, averages the impurity composition there and imposes during the ramp-up
-- 'linear descending zeff' Zeff descends linearly
-- 'ip ne scaled' Uses scaling from ASDEX
-- 'hyperbole' Decreases zeff rapidly, starting from 4 and merging to the zeff value at the end of the ramp-up
sensitivity_list: Can contain some basic sensitivities. Best to use the duqtools unless interested in peaking of te or setting initial q profile keeping q95
input_instructions: Options for the input. They will be applied one by one generating new idss, starting from time_start-len(input_instructions)
-- average - rebase Averages relevant IDSs or rebase the equilibrium IDS with the core profiles time base
-- flipping ip Can be added, but will be added authomatically when the current is positive because I still cannot make positive current work
-- nbi heating Sets up NBI. Not ready yet
-- set boundaries Will setup the boundaries for te and ti.
-- 'constant' Can set them constant
-- 'add' To add a constant value and keep the time evolution
-- 'linear' To increase linearly between two extremes
-- correct boundaries Will increase the boundaries when below 20 eV
-- add early profiles Will extrapolate the profiles to 0.01. Not implemented for the 2d equilibrium yet
-- parabolic zeff, peaked zeff Sets a hollow or a peaked profile for Zeff
-- correct zeff Corrects Zeff where it is below 1.02 or above 4
-- flat q profile Sets up a flat q profile with the boundary values everywhere
boundary_instructions: Options to modify the boundaries for te and ti. It modifies the edge and keeps the axis the same, linearly
1 - setup_input_baserun(verbose = False):
2 - setup_input_sensitivities()
3 - create_baserun()
4 - create_sensitivities(force_run = False)
5 - run_baserun()
6 - run_sensitivities(force_run = False)
# Setting up a single folder ready for integrated modelling
setup_jetto_simulation()
setup_feedback_on_density()
# Tools: modify the jset and llcmd files
modify_jset(path, sensitivity_name, ids_number, ids_output_number, db, username, shot)
modify_jset_line(sensitivity_name, line_start, new_content)
modify_llcmd(sensitivity_name, baserun_name)
add_item_lookup
# Used to modify jetto extranamelist
get_extraname_fields()
add_extraname_fields()
put_extraname_fields()
# Small utilities, hopefully temporary
check_and_flip_ip(db, shot, run, shot_target, run_target)
flip_ip(db, shot, run, shot_target, run_target)
'''
class IntegratedModellingRuns:
def __init__(
self,
shot,
instructions_list,
generator_name,
baserun_name,
db = 'tcv',
run_input = 1,
run_start = None,
run_output = 100,
time_start = None,
time_end = 100,
esco_timesteps = None,
output_timesteps = None,
force_run = False,
force_input_overwrite = False,
density_feedback = False,
set_sep_boundaries = False,
boundary_conditions = {},
setup_time_polygon_flag = False,
change_impurity_puff_flag = False,
setup_time_polygon_impurities_flag = False,
select_impurities_from_ids_flag = True,
add_extra_transport_flag = False,
setup_nbi_flag = False,
path_nbi_config = None,
json_input = None,
sensitivity_list = [],
):
# db is the name of the machine. Needs to be the name of the imas database.
# shot is the shot number. It is an int.
# run input is where the input is. It will not be the input for the simulations though, since it needs to be massaged
# run output is the output number for the baserun. The sensitivities will start here and increase by 1 as in the list
# generator name is the name of the generator as it appears in the run folder
self.username = getpass.getuser()
self.db = db
self.shot = shot
self.run_input = run_input
self.run_start = run_start
self.run_output = run_output
self.time_start = time_start
self.time_end = time_end
self.esco_timesteps = esco_timesteps
self.output_timesteps = output_timesteps
self.force_run = force_run
self.force_input_overwrite = force_input_overwrite
self.density_feedback = density_feedback
self.set_sep_boundaries = set_sep_boundaries
self.boundary_conditions = boundary_conditions
self.setup_time_polygon_flag = setup_time_polygon_flag
self.change_impurity_puff_flag = change_impurity_puff_flag
self.setup_time_polygon_impurities_flag = setup_time_polygon_impurities_flag
self.select_impurities_from_ids_flag = select_impurities_from_ids_flag
self.add_extra_transport_flag = add_extra_transport_flag
self.setup_nbi_flag = setup_nbi_flag
self.path_nbi_config = path_nbi_config
self.core_profiles = None
self.equilibrium = None
self.line_ave_density = None
self.json_input = json_input
self.sensitivity_list = sensitivity_list
self.backend_input = get_backend(self.db, self.shot, self.run_input)
# Trying to be a little flexible with the generator name. It is not used if I am only setting the input.
# Still mandatory argument, should not be forgotten
if self.json_input:
self.setup_nbi_flag = self.json_input['instructions']['nbi heating']
self.path = '/pfs/work/' + self.username + '/jetto/runs/'
self.generator_username = ''
if generator_name.startswith('/pfs/work'):
self.path_generator = generator_name
self.generator_name = generator_name.split('/')[-2]
self.generator_username = generator_name.split('/')[3]
elif generator_name.startswith('rungenerator_'):
self.generator_name = generator_name
self.path_generator = self.path + self.generator_name
self.generator_username = self.username # new
else:
self.generator_name = 'rungenerator_' + generator_name
self.path_generator = self.path + self.generator_name
self.generator_username = self.username # new
self.baserun_name = baserun_name
# Default instructions: do nothing
self.instructions = {
'setup base' : False,
'setup sens' : False,
'create base' : False,
'create sens' : False,
'run base' : False,
'run sens' : False
}
for key in instructions_list:
if key in self.instructions:
self.instructions[key] = True
# Default sensitivity list. The sensitivity list can be omitted and will not be used when only dealing with the baserun
# Example of sensitivity list. Not default.
#if not sensitivity_list:
# self.sensitivity_list = ['te 0.8', 'te 1.2', 'ne 0.8', 'ne 1.2', 'zeff 0.8', 'zeff 1.2', 'q95 0.8', 'q95 1.2']
# Default baserun name is 'run000'. It is not used if I am only setting the input. Baserun name should always start with 'run###'
if self.baserun_name == '':
self.baserun_name = 'run000' + str(self.shot) + 'base'
self.path_baserun = self.path + self.baserun_name
self.tag_list = []
for sensitivity in self.sensitivity_list:
tag = sensitivity.replace(' ', '_')
tag = tag.replace('.', '_')
tag = '_' + tag
self.tag_list.append(tag)
# Default nbi path is in public. setup_nbi should take care of this
#if not self.path_nbi_config:
# self.path_nbi_config = '/afs/eufus.eu/user/g/g2mmarin/public/tcv_inputs/jetto.nbicfg'
# New instructions are just an array with six True/False (or 0/1). They correspond orderly to what to do in the instruction list
def update_instructions(self, new_instructions):
for i, key in enumerate(self.instructions):
self.instructions[key] = new_instructions[i]
def update_sensitivities(self, new_sensitivities_list):
self.sensitivity_list = new_sensitivities_list
def setup_create_compare(self, verbose = False):
if self.instructions['setup base']:
self.setup_input_baserun(verbose = False)
if self.instructions['setup sens']:
self.setup_input_sensitivities()
if self.instructions['create base']:
self.create_baserun()
if self.instructions['create sens']:
self.create_sensitivities()
if self.instructions['run base']:
self.run_baserun()
if self.instructions['run sens']:
self.run_sensitivities()
def setup_input_baserun(self, verbose = False):
'''
Modified the setup function to have it in a separate file as an extra option. The new script can be used standalone.
If the setup is not used importing the script will not be necessary
'''
try:
import prepare_im_input
except ImportError:
print('prepare_input.py not found and needed for this option. Aborting')
exit()
if not self.json_input:
json_file_name = '/afs/eufus.eu/user/g/g2mmarin/public/scripts/template_prepare_input.json'
print('json input to prepare the runs not specified, using dummy file')
json_input_raw = open(json_file_name)
self.json_input = json.load(json_input_raw)
self.core_profiles, self.equilibrium = prepare_im_input.setup_input(self.db, self.shot, self.run_input, self.run_start, json_input = self.json_input, time_start = self.time_start, time_end = self.time_end, force_input_overwrite = self.force_input_overwrite, core_profiles = self.core_profiles, equilibrium = self.equilibrium)
print('input generated correctly')
def create_baserun(self):
'''
Automatically sets up the folder for the baserun of a specific scan.
The type of the baserun will determine which kind of runs the sensitivity should be carried out from. Default options are given
'''
os.chdir(self.path)
if os.path.exists(self.path_generator):
shutil.copytree(self.path_generator, self.path_baserun)
else:
print('generator not recognized. Aborting')
exit()
# To save time, equilibrium and core profiles are not extracted if they already exist
if not self.core_profiles:
self.core_profiles = open_and_get_ids(self.db, self.shot, self.run_input, 'core_profiles')
if not self.equilibrium:
self.equilibrium = open_and_get_ids(self.db, self.shot, self.run_input, 'equilibrium')
time_eq = self.equilibrium.time
time_cp = self.core_profiles.time
if self.time_start == None:
self.time_start = max(min(time_eq), min(time_cp))
elif self.time_start == 'core_profiles':
self.time_start = min(time_cp)
elif self.time_start == 'equilibrium':
self.time_start = min(time_eq)
elif self.time_start == 'equilibrium + 1':
self.time_start = time_eq[1]
if self.time_end == 100:
self.time_end = min(max(time_eq), max(time_cp))
if self.time_end == 'auto':
summary = open_and_get_ids(self.db, self.shot, self.run_input, summary)
kfactor = 0.05
mu0 = 4 * np.pi * 1.0e-7
time_sim = kfactor * mu0 * np.abs(summary.global_quantities.ip.value[0] * summary.global_quantities.r0.value)
time_end = self.time_start + time_sim
b0, r0 = self.get_r0_b0()
if self.density_feedback == True:
self.get_feedback_on_density_quantities()
if 'interpretive' not in self.path_generator:
interpretive_flag = False
else:
interpretive_flag = True
imp_data = None
if self.select_impurities_from_ids_flag:
imp_data_ids = self.select_impurities_from_ids()
self.modify_jetto_in_impurities(imp_data_ids)
self.modify_impurities_jset(imp_data_ids)
# Still cannot run with positive current...
self.modify_jetto_in(self.baserun_name, r0, abs(b0), self.time_start, self.time_end, num_times_print = self.output_timesteps, num_times_eq = self.esco_timesteps, interpretive_flag = interpretive_flag)
#I am still not sure if the magnetic field should be turned to 0
#self.modify_jetto_in(self.baserun_name, r0, b0, self.time_start, self.time_end, imp_datas_ids = imp_data, num_times_print = self.output_timesteps, num_times_eq = self.esco_timesteps, interpretive_flag = interpretive_flag)
self.setup_jetto_simulation()
if self.density_feedback == True:
self.setup_feedback_on_density()
if self.set_sep_boundaries:
self.setup_boundary_values()
self.modify_jset(self.path, self.baserun_name, self.run_start, self.run_output, abs(b0), r0)
#self.modify_jset(self.path, self.baserun_name, self.run_start, self.run_output, b0, r0)
if self.setup_nbi_flag:
nbi = open_and_get_ids(self.db, self.shot, self.run_start, 'nbi')
#if nbi.time != np.asarray([]):
if nbi.time.size != 0:
#self.setup_nbi(path_nbi_config = self.path_nbi_config)
#run_target = self.run_start suppresses ids generation while allowing for script to be used standalone. Might not be the best way to do it
setup_nbi_input.setup_nbi(self.db, self.shot, self.run_start, self.path + self.baserun_name, run_target = self.run_start, path_nbi_config = self.path_nbi_config)
else:
print('You are trying to setup the nbi but the nbi ids is empty. Aborting')
exit()
if self.setup_time_polygon_flag:
self.setup_time_polygon()
if self.change_impurity_puff_flag:
self.change_impurity_puff()
if self.add_extra_transport_flag:
self.add_extra_transport()
# Currently only working with one impurity
if self.setup_time_polygon_impurities_flag:
self.setup_time_polygon_impurity_puff()
modify_llcmd(self.baserun_name, self.generator_name, self.generator_username)
# ------------------ TESTING HERE --------------------------
if os.path.exists(self.generator_name + '/jintrac.launch'):
modify_jintrac_launch(self.baserun_name, self.generator_name, self.generator_username, self.db, self.shot, self.time_start, self.time_end)
if self.backend_input == imasdef.MDSPLUS_BACKEND:
self.copy_ids_input_mdsplus()
elif self.backend_input == imasdef.HDF5_BACKEND:
self.copy_ids_input_hdf5()
def select_impurities_from_ids(self):
# This should not be needed and should be handled by the jetto_tools. It's not though...
imp_data = []
first_imp_density = None
for ion in self.core_profiles.profiles_1d[0].ion:
imp_density = np.average(ion.density)
z_ion = ion.element[0].z_n
a_ion = ion.element[0].a
z_bundle = round(z_ion)
if z_ion > 1:
if not first_imp_density:
imp_relative_density = 1.0
first_imp_density = imp_density
else:
imp_relative_density = imp_density/first_imp_density
imp_data.append([imp_relative_density, a_ion, z_bundle, z_ion])
return imp_data
def modify_impurities_jset(self, imp_data):
# Selecting the impurity correctly in the jset
impurity_jset_linestarts = ['ImpOptionPanel.impuritySelect[]',
'ImpOptionPanel.impurityMass[]',
'ImpOptionPanel.impurityCharge[]',
'ImpOptionPanel.impuritySuperStates[]'
]
for index in range(6):
if index < len(imp_data):
for jset_linestart in impurity_jset_linestarts:
line_start = jset_linestart[:-2] + str(index) + jset_linestart[-1]
if jset_linestart == 'ImpOptionPanel.impuritySelect[]':
new_content = '1'
elif jset_linestart == 'ImpOptionPanel.impurityMass[]':
new_content = str(imp_data[index][1])
elif jset_linestart == 'ImpOptionPanel.impurityCharge[]':
new_content = str(imp_data[index][2])
elif jset_linestart == 'ImpOptionPanel.impuritySuperStates[]':
new_content = str(imp_data[index][3])
modify_jset_line(self.baserun_name, line_start, new_content)
else:
line_start = 'ImpOptionPanel.impuritySelect[' + str(index) + ']'
new_content = 'false'
modify_jset_line(self.baserun_name, line_start, new_content)
def copy_ids_input_mdsplus(self):
if self.run_start < 10:
run_str = '000' + str(self.run_start)
elif self.run_start < 100:
run_str = '00' + str(self.run_start)
elif self.run_start < 1000:
run_str = '0' + str(self.run_start)
else:
run_str = str(self.run_start)
path_ids_input = '/afs/eufus.eu/user/g/' + self.username + '/public/imasdb/' + self.db + '/3/0/ids_' + str(self.shot) + run_str
path_characteristics = path_ids_input + '.characteristics'
path_datafile = path_ids_input + '.datafile'
path_tree = path_ids_input + '.tree'
path_output = self.path_baserun+ '/imasdb/' + self.db + '/3/0/ids_' + str(self.shot) + '0001'
# This creates the folder when the machine is not the same as in the generator case
if not os.path.exists(self.path_baserun+ '/imasdb/' + self.db):
db_generator = os.listdir(self.path_baserun+ '/imasdb/')[0]
shutil.copytree(self.path_baserun+ '/imasdb/' + db_generator, self.path_baserun+ '/imasdb/' + self.db)
shutil.rmtree(self.path_baserun+ '/imasdb/' + db_generator)
# This deletes the IDS of the generator
self.delete_generator()
shutil.copyfile(path_ids_input + '.characteristics', path_output + '.characteristics')
shutil.copyfile(path_ids_input + '.datafile', path_output + '.datafile')
shutil.copyfile(path_ids_input + '.tree', path_output + '.tree')
def copy_ids_input_hdf5(self):
path_ids_input = '/afs/eufus.eu/user/g/' + self.username + '/public/imasdb/' + self.db + '/3/' + str(self.shot) + '/' + str(self.run_start)
path_output = self.path_baserun+ '/imasdb/' + self.db + '/3/' + str(self.shot) + '/' + str(1)
# This creates the folder when the machine is not the same as in the generator case
if not os.path.exists(self.path_baserun+ '/imasdb/' + self.db):
db_generator = os.listdir(self.path_baserun+ '/imasdb/')[0]
shutil.copytree(self.path_baserun+ '/imasdb/' + db_generator, self.path_baserun+ '/imasdb/' + self.db)
shutil.rmtree(self.path_baserun+ '/imasdb/' + db_generator)
# This deletes the IDS of the generator
self.delete_generator()
folder_path = self.path_baserun+ '/imasdb/' + self.db + '/3/' + str(self.shot) + '/' + str(1)
if not os.path.exists(folder_path):
os.makedirs(folder_path)
copy_files(path_ids_input, path_output)
def delete_generator(self):
self.db_generator = os.listdir(self.path_baserun+ '/imasdb/')[0]
self.shot_generator = os.listdir(self.path_baserun+ '/imasdb/' + self.db_generator + '/3/')[0]
if os.path.exists(self.path_baserun+ '/imasdb/' + self.db_generator):
shutil.rmtree(self.path_baserun+ '/imasdb/' + self.db_generator)
def run_baserun(self):
# ------- Not working yet, jetto_tool automatically setup a slurm environment -------
# manager = jetto_tools.job.JobManager()
# manager.submit_job_to_batch(config, baserun_name + 'tmp', run=False)
# ------- Substitute this to the custom automatic run when available --------
os.chdir(self.path + self.baserun_name)
print('running ' + self.baserun_name)
#os.system('sbatch ./.llcmd')
os.system('sbatch .llcmd')
def get_r0_b0(self):
# -------------------- GET b0 and r0 ---------------------
# Only extract once. This takes time so it's only for speed purposes
if not self.core_profiles:
self.core_profiles = open_and_get_ids(self.db, self.shot, self.run_input, 'core_profiles')
if not self.equilibrium:
self.equilibrium = open_and_get_ids(self.db, self.shot, self.run_input, 'equilibrium')
# Here I can set the initial time as the time where I can find the first measurement in core profiles or equilibrium
time_eq = self.equilibrium.time
time_cp = self.core_profiles.time
index_start = np.abs(time_eq - self.time_start).argmin(0)
index_end = np.abs(time_eq - self.time_end).argmin(0)
if index_start != index_end:
b0 = np.average(self.equilibrium.vacuum_toroidal_field.b0[index_start:index_end])
else:
b0 = self.equilibrium.vacuum_toroidal_field.b0[0]
r0 = self.equilibrium.vacuum_toroidal_field.r0*100
# -----------------------------------------------------
return b0, r0
def setup_ibtsign_config(self, b0, extranamelist, config):
ip_from_ids = read_jettoin_line(self.path_baserun + '/jetto.in', ' IDSPULSESCHEDIN_IPL')
if ip_from_ids is not None: ip_from_ids = int(ip_from_ids[0])
if ip_from_ids:
current = self.equilibrium.time_slice[0].global_quantities.ip
else:
current = read_jettoin_line(self.path_baserun + '/jetto.in', ' CURTI')[0]
if (b0 > 0 and current < 0) or (b0 < 0 and current > 0):
ibtsign = 1
extranamelist = add_extraname_fields(extranamelist, 'IBTSIGN', ['1'])
else:
ibtsign = -1
extranamelist = add_extraname_fields(extranamelist, 'IBTSIGN', ['-1'])
if 'interpretive' not in self.path_generator:
if (b0 > 0 and current < 0) or (b0 < 0 and current > 0):
config['ibtsign'] = 1
else:
config['ibtsign'] = -1
return extranamelist, config, ibtsign
def setup_jetto_simulation(self):
'''
Uses the jetto_tools to setup various parameters for the jetto simulation.
Updates the magnetic field and the radius. Can be used to update the output and the and the impurity composition.
Updates the jetto starting time as the first time for which data are available both for the
equilibrium and the core profiles. Strongly advised!
'''
# In the future an option to operate with the correct ip sign could be added here. Not currently working
# Also, IBTSIGN seems not to be in the list as it should. not sure what is happening...
#lookup = jetto_tools.lookup.from_file(self.path + '/lookup_json/lookup.json')
#jset = jetto_tools.jset.read(self.path_generator + '/jetto.jset')
#namelist = jetto_tools.namelist.read(self.path_generator + '/jetto.in')
b0, r0 = self.get_r0_b0()
if not self.core_profiles:
self.core_profiles = open_and_get_ids(self.db, self.shot, self.run_input, 'core_profiles')
if not self.equilibrium:
self.equilibrium = open_and_get_ids(self.db, self.shot, self.run_input, 'equilibrium')
if not os.path.exists(self.path_baserun):
shutil.copytree(self.path_generator, self.path_baserun)
else:
shutil.copyfile(self.path + '/lookup_json/lookup.json', self.path_baserun + '/lookup.json') # Just this line should be fine
# shutil.copyfile(self.path_generator + '/jetto.in', self.path_baserun + '/jetto.in')
# Changing the orientation when necessary
# Add IBTSING if ip sign and b0 sign are opposite. There is still a bug.
extranamelist = get_extraname_fields(self.path_baserun)
if 'interpretive' not in self.path_generator:
add_item_lookup('btin', 'EquilEscoRefPanel.BField.ConstValue', 'NLIST1', 'real', 'scalar', self.path_baserun)
add_item_lookup('rmj', 'EquilEscoRefPanel.refMajorRadius', 'NLIST1', 'real', 'scalar', self.path_baserun)
add_item_lookup('ibtsign', 'null', 'NLIST1', 'int', 'scalar', self.path_baserun)
put_extraname_fields(self.path_baserun, extranamelist)
# A temporary function to handle arrays since the pythontools do not do it yet. When the option comes online again use that.
self.tmp_handle_arrays_open()
template = jetto_tools.template.from_directory(self.path_baserun)
config = jetto_tools.config.RunConfig(template)
# For some reason this works for TCV and not for JET. Will have to understand this
if self.db == 'tcv':
extranamelist, config, ibtsign = self.setup_ibtsign_config(b0, extranamelist, config)
self.setup_ibtsign_jetto_in(ibtsign)
if 'interpretive' not in self.path_generator:
config['btin'] = abs(b0)
# Absolute value should not be needed anymore since the fix on the ip sign
#config['btin'] = b0
config['rmj'] = r0
if self.esco_timesteps:
config.esco_timesteps = self.esco_timesteps
if self.output_timesteps:
config.profile_timesteps = self.output_timesteps
config['ntint'] = self.output_timesteps
config.start_time = self.time_start
config.end_time = self.time_end
# I could introduce a way not to do this if there are no impurities. Need to add impurities if not there, modifying the various files. Maybe in the future. For now I modify the jset anyway so this part does nothing...
if self.db == 'tcv':
config['atmi'] = 6.0
config['nzeq'] = 12.0
config['zipi'] = 6
config.export(self.path_baserun + 'tmp')
shutil.copyfile(self.path_baserun + 'tmp' + '/jetto.jset', self.path_baserun + '/jetto.jset')
#shutil.copyfile(self.path_baserun + 'tmp' + '/jetto.in', self.path_baserun + '/jetto.in')
shutil.rmtree(self.path_baserun + 'tmp')
# A temporary function to handle arrays since the pythontools do not do it yet. When the option comes online again use that.
self.tmp_handle_arrays_close()
def tmp_handle_arrays_open(self):
extranamelist = get_extraname_fields(self.path_baserun)
for key in extranamelist:
if '(' in extranamelist[key][0] and ')' in extranamelist[key][0]:
extranamelist[key][0] = '\'' + extranamelist[key][0] + '\''
put_extraname_fields(self.path_baserun, extranamelist)
def tmp_handle_arrays_close(self):
extranamelist = get_extraname_fields(self.path_baserun)
for key in extranamelist:
if '(' in extranamelist[key][0] and ')' in extranamelist[key][0]:
extranamelist[key][0] = extranamelist[key][0].strip('\'')
put_extraname_fields(self.path_baserun, extranamelist)
def increase_processors(self, processors = 8, walltime = 24):
binary, userid = 'v210921_gateway_imas', 'g2fkoech'
template = jetto_tools.template.from_directory(self.path_baserun)
config = jetto_tools.config.RunConfig(template)
config.binary = binary
config.userid = userid
config.processors = processors
config.walltime = walltime
def get_boundary_values_quantities(self):
if not self.boundary_conditions:
# Saves the boundary conditions in lists
core_profiles = open_and_get_ids(self.db, self.shot, self.run_start, 'core_profiles')
self.boundary_conditions['te'] = []
self.boundary_conditions['ti'] = []
self.boundary_conditions['ne'] = []
for profile_1d in core_profiles.profiles_1d:
self.boundary_conditions['te'].append(profile_1d.electrons.temperature[-1])
self.boundary_conditions['ti'].append(profile_1d.ion[0].temperature[-1])
self.boundary_conditions['ne'].append(profile_1d.electrons.density[-1]*1e-6)
self.boundary_conditions['times'] = core_profiles.time.tolist()
def get_feedback_on_density_quantities(self):
# Could add a check if run_exp exists. Should become the runinput though...
#summary = open_and_get_ids(self.db, self.shot, self.run_exp, 'summary')
#self.summary_time = summary.time
#self.line_ave_density = summary.line_average.n_e.value
pulse_schedule = open_and_get_ids(self.db, self.shot, self.run_start, 'pulse_schedule')
self.dens_feedback_time = pulse_schedule.time
self.line_ave_density = pulse_schedule.density_control.n_e_line.reference.data
def setup_boundary_values(self):
self.get_boundary_values_quantities()
self.setup_boundary_values_jset()
self.setup_boundary_values_jetto_in()
def setup_boundary_values_jetto_in(self):
run_name = self.path_baserun
modify_jettoin_line(run_name, ' NTEB', len(self.boundary_conditions['te']))
modify_jettoin_line(run_name, ' NTIB', len(self.boundary_conditions['ti']))
modify_jettoin_line(run_name, ' NDNHB1', len(self.boundary_conditions['ne']))
modify_jettoin_line(run_name, ' TEB', self.boundary_conditions['te'])
modify_jettoin_line(run_name, ' TIB', self.boundary_conditions['ti'])
modify_jettoin_line(run_name, ' DNHB1', self.boundary_conditions['ne'])
modify_jettoin_line(run_name, ' TTEB', self.boundary_conditions['times'])
modify_jettoin_line(run_name, ' TTIB', self.boundary_conditions['times'])
modify_jettoin_line(run_name, ' TDNHB1', self.boundary_conditions['times'])
#modify_jettoin_line(run_name, ' BCINTRHON', '\n')
modify_jettoin_line(run_name, ' BCINTRHON', 1.0)
modify_jettoin_line(run_name, ' qlk_rhomax', 0.995) #For some reason the run crashes immediately with 1.0
def add_extra_transport(self):
run_name = self.path_baserun
self.add_extra_transport_jettoin(run_name)
self.add_extra_transport_jettojset(run_name)
def add_extra_transport_jettoin(self, run_name):
modify_jettoin_line(run_name, ' IFORME', 2)
modify_jettoin_line(run_name, ' IFORMD', 2)
if self.add_extra_transport_flag == True:
modify_jettoin_line(run_name, ' FORME', [20000.0, 1.0, 0.01])
modify_jettoin_line(run_name, ' FORMD', [20000.0, 1.0, 0.01])
else:
modify_jettoin_line(run_name, ' FORME', [self.add_extra_transport_flag, 1.0, 0.01])
modify_jettoin_line(run_name, ' FORMD', [self.add_extra_transport_flag, 1.0, 0.01])
def add_extra_transport_jettojset(self, run_name):
modify_jset_line(run_name, 'TransportAddFormDialog.model', 'Gaussian')
modify_jset_line(run_name, 'TransportAddPanel.FormulaElectronThermal', 'true')
if self.add_extra_transport_flag == True:
modify_jset_line(run_name, 'TransportAddFormDialog.gaussian.eleForm[0]', str(20000))
else:
modify_jset_line(run_name, 'TransportAddFormDialog.gaussian.eleForm[0]', str(self.add_extra_transport_flag))
modify_jset_line(run_name, 'TransportAddFormDialog.gaussian.eleForm[1]', str(1.0))
modify_jset_line(run_name, 'TransportAddFormDialog.gaussian.eleForm[2]', str(0.01))
# Also changing the particle transport. Will see if I want it here
modify_jset_line(run_name, 'TransportAddPanel.FormulaParticle', 'true')
if self.add_extra_transport_flag == True:
modify_jset_line(run_name, 'TransportAddFormDialog.gaussian.parForm[0]', str(20000))
else:
modify_jset_line(run_name, 'TransportAddFormDialog.gaussian.parForm[0]', str(self.add_extra_transport_flag))
modify_jset_line(run_name, 'TransportAddFormDialog.gaussian.parForm[1]', str(1.0))
modify_jset_line(run_name, 'TransportAddFormDialog.gaussian.parForm[2]', str(0.01))
def setup_feedback_on_density(self):
'''
Still deciding what exactly this will be. Some step to setup a run with automatic density feedback control
Strategy should be: add this when setting up the correponding baserun.
'''
dneflfb_strs = []
for density in self.line_ave_density*1e-6:
dneflfb_strs.append(str(density))
dtneflfb_strs = []
for time in self.dens_feedback_time:
dtneflfb_strs.append(str(time))
extranamelist = get_extraname_fields(self.path_baserun)
if not dneflfb_strs:
print('No quantity to set the density feedback. Aborting')
exit()
extranamelist = add_extraname_fields(extranamelist, 'DNEFLFB', dneflfb_strs)
extranamelist = add_extraname_fields(extranamelist, 'DTNEFLFB', dtneflfb_strs)
put_extraname_fields(self.path_baserun, extranamelist)
# Ideally the following should be enough. Currently it is not working.
'''
add_item_lookup('dneflfb', 'null', 'NLIST4', 'real', 'vector', self.path_baserun)
add_item_lookup('dtneflfb', 'null', 'NLIST4', 'real', 'vector', self.path_baserun)
template = jetto_tools.template.from_directory(self.path_baserun)
config = jetto_tools.config.RunConfig(template)
config['dneflfb'] = self.line_ave_density*1e-6
config['dtneflfb'] = self.dens_feedback_time
# ------- Can use to create the baseruns when I understand how to create a template from a run without the lookup file (probably just creating the lookup file there)
config.export(self.path_baserun + 'tmp')
shutil.copyfile(self.path_baserun + 'tmp' + '/jetto.jset', self.path_baserun + '/jetto.jset')
shutil.rmtree(self.path_baserun + 'tmp')
'''
def setup_boundary_values_jset(self):
run_name = self.path_baserun
panel_name = 'BoundCondPanel.eleTemp'
modify_jset_time_list(run_name, panel_name, self.boundary_conditions['times'], self.boundary_conditions['te'])
panel_name = 'BoundCondPanel.ionTemp'
modify_jset_time_list(run_name, panel_name, self.boundary_conditions['times'], self.boundary_conditions['ti'])
panel_name = 'BoundCondPanel.ionDens[0]'
modify_jset_time_list(run_name, panel_name, self.boundary_conditions['times'], self.boundary_conditions['ne'])
# Need to also turn off bcintrsanco. This should be a function
line_start = identify_line_start_extranamelist(run_name, 'BCINTRHON')
if line_start:
line_start = line_start.replace('[0]', '[2]')
new_content = '1.0'
modify_jset_line(run_name, line_start, new_content)
#Modifying qlk boundaries. Should do the same for TGLF.
line_start = identify_line_start_extranamelist(run_name, 'qlk_rhomax')
if line_start:
line_start = line_start.replace('[0]', '[2]')
new_content = '0.995'
modify_jset_line(run_name, line_start, new_content)
def modify_jset(self, path, run_name, ids_number, ids_output_number, b0, r0):
'''
Modifies the jset file to accomodate a new run name, username, shot and run. Database not really implemented yet
'''
# Might want more flexibility with the run list here. Maybe set more options in the future
# The last values with the final times should be handled within the config, but are not. They should be temporary
line_start_list = [
'Creation Name',
'JobProcessingPanel.runDirNumber',
'SetUpPanel.idsIMASDBRunid',
'JobProcessingPanel.idsRunid',
'AdvancedPanel.catMachID',
'AdvancedPanel.catMachID_R',
'SetUpPanel.idsIMASDBMachine',
'SetUpPanel.machine',
'SetUpPanel.idsIMASDBUser',
'AdvancedPanel.catOwner',
'AdvancedPanel.catOwner_R',
'AdvancedPanel.catShotID',
'AdvancedPanel.catShotID_R',
'SetUpPanel.idsIMASDBShot',
'SetUpPanel.shotNum',
'SetUpPanel.endTime',
'EquilEscoRefPanel.tvalue.tinterval.endRange',
'EquilIdsRefPanel.rangeEnd',
'OutputStdPanel.profileRangeEnd',
'SetUpPanel.startTime',
'EquilEscoRefPanel.tvalue.tinterval.startRange',
'EquilIdsRefPanel.rangeStart',
'OutputStdPanel.profileRangeStart',
'EquilEscoRefPanel.BField.ConstValue',
'EquilEscoRefPanel.BField ',
'EquilEscoRefPanel.refMajorRadius'
]
new_content_list = [
path + run_name + '/jetto.jset',
run_name[3:],
str(ids_number),
str(ids_output_number),
self.db,
self.db,
self.db,
self.db,
self.username,
self.username,
self.username,
str(self.shot),
str(self.shot),
str(self.shot),
str(self.shot),
str(self.time_end),
str(self.time_end),
str(self.time_end),
str(self.time_end),
str(self.time_start),
str(self.time_start),
str(self.time_start),
str(self.time_start),
str(b0),
str(b0),
str(r0)
]
# ImpOptionPanel.impuritySelect[1] : false to deselect the impurity
for line_start, new_content in zip(line_start_list, new_content_list):
modify_jset_line(run_name, line_start, new_content)
def modify_ascot_cntl(self, run_name):
line_start = 'Creation Name'
modify_ascot_cntl_line(run_name, line_start, run_name + '/ascot.cntl')
def modify_jset_nbi(self, run_name, nbi_config_name):