Skip to content

Commit 8be1ca3

Browse files
authored
Merge pull request #378 from gafusion/omas_mds_convert_to_64bit
Force 64bit encoding for MDSplus data
2 parents 63debc2 + a55b364 commit 8be1ca3

File tree

8 files changed

+440
-189
lines changed

8 files changed

+440
-189
lines changed

.github/workflows/regression.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
python -m pip install --upgrade pip
2929
python -m pip install numpy
3030
python -m pip install -r requirements.txt
31-
python -m pip install boto3 pymongo xmltodict
31+
python -m pip install boto3 xmltodict
3232
python -m pip install wheel
3333
python -m pip install --no-build-isolation .[machine]
3434
python -c "import omas; print(omas.__version__)"

omas/machine_mappings/_common.py

Lines changed: 279 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import numpy as np
22
from omas import *
3-
from omas.omas_utils import printd
3+
from omas.omas_utils import printd, printe
44
import os
55
import glob
66
from omas.omas_setup import omas_dir
7+
from omas.omas_physics import omas_environment
78
from omas.utilities.omas_mds import mdsvalue, get_pulse_id
89

910
__support_files_cache__ = {}
@@ -174,7 +175,7 @@ def D3Dmagnetics_weights(pulse, name=None):
174175
raise ValueError(f"{name} is part of the d3d fitweight")
175176

176177

177-
def MDS_gEQDSK_COCOS_identify(machine, pulse, EFIT_tree, EFIT_run_id):
178+
def MDS_gEQDSK_COCOS_identify(machine, pulse, EFIT_tree, EFIT_run_id=None):
178179
"""
179180
Python function that queries MDSplus EFIT tree to figure
180181
out COCOS convention used for a particular reconstruction
@@ -292,6 +293,282 @@ def pf_coils_to_ods(ods, coil_data):
292293
return ods
293294

294295

296+
def scalar_constraint_data(ods, machine, pulse, EFIT_tree, base, measured, measured_error_upper=None, weight=None, reconstructed=None, chi_squared=None, EFIT_run_id=None, **kw):
297+
"""
298+
Loads EFIT scalar constraint data
299+
"""
300+
301+
pulse_id = get_pulse_id(pulse, EFIT_run_id)
302+
303+
# first form a list of signals to fetch
304+
TDIs = {}
305+
TDIs['time'] = f'data(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.GTIME)'
306+
TDIs['mtime'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.MTIME)'
307+
TDIs['measured'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{measured})'
308+
if measured_error_upper is not None:
309+
TDIs['measured_error_upper'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{measured_error_upper})'
310+
if weight is not None:
311+
TDIs['weight'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{weight})'
312+
if reconstructed is not None:
313+
TDIs['reconstructed'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{reconstructed})'
314+
if chi_squared is not None:
315+
TDIs['chi_squared'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{chi_squared})'
316+
317+
# fetch the data for all signals
318+
all_data = mdsvalue(machine, EFIT_tree, pulse_id, TDIs).raw()
319+
320+
# determine common times for equilibrium data
321+
# this is required because the between shot EFIT filters are not applied to the MEASUREMENTS
322+
ntimes = len(all_data['time'])
323+
if ntimes == len(all_data['mtime']):
324+
it = np.arange(ntimes)
325+
else:
326+
it = np.minimum(all_data['mtime'].searchsorted(all_data['time']), ntimes-1)
327+
328+
# assign the data to the ods
329+
del all_data['time']
330+
del all_data['mtime']
331+
cocosio = MDS_gEQDSK_COCOS_identify(machine, pulse, EFIT_tree, EFIT_run_id)
332+
with omas_environment(ods, cocosio=cocosio):
333+
for entry, data in all_data.items():
334+
try:
335+
for k, ind in enumerate(it):
336+
ods[f'equilibrium.time_slice.{k}.constraints.{base}.{entry}'] = data[ind]
337+
except:
338+
printe(f'EFIT data was not found for {base} {entry}')
339+
340+
341+
def vector_constraint_data(ods, machine, pulse, EFIT_tree, base, measured, measured_error_upper=None, weight=None, reconstructed=None, chi_squared=None, EFIT_run_id=None, **kw):
342+
"""
343+
Loads EFIT vector constraint data
344+
"""
345+
346+
pulse_id = get_pulse_id(pulse, EFIT_run_id)
347+
348+
# first form a list of signals to fetch
349+
operation = 'data'
350+
if base == 'mse_polarisation_angle':
351+
operation = 'ATAN'
352+
TDIs = {}
353+
TDIs['time'] = f'data(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.GTIME)'
354+
TDIs['mtime'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.MTIME)'
355+
TDIs['ndata'] = f'size(\\{EFIT_tree}::TOP.MEASUREMENTS.{measured},0)'
356+
TDIs['measured'] = f'{operation}(\\{EFIT_tree}::TOP.MEASUREMENTS.{measured})'
357+
if measured_error_upper is not None:
358+
TDIs['measured_error_upper'] = f'{operation}(\\{EFIT_tree}::TOP.MEASUREMENTS.{measured_error_upper})'
359+
if weight is not None:
360+
TDIs['weight'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{weight})'
361+
if reconstructed is not None:
362+
TDIs['reconstructed'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{reconstructed})'
363+
if chi_squared is not None:
364+
TDIs['chi_squared'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{chi_squared})'
365+
366+
# fetch the data for all signals
367+
all_data = mdsvalue(machine, EFIT_tree, pulse_id, TDIs).raw()
368+
369+
# determine common times for equilibrium data
370+
# this is required because the between shot EFIT filters are not applied to the MEASUREMENTS
371+
ntimes = len(all_data['time'])
372+
mtimes = len(all_data['mtime'])
373+
if ntimes == mtimes:
374+
it = np.arange(ntimes)
375+
else:
376+
it = np.minimum(all_data['mtime'].searchsorted(all_data['time']), ntimes-1)
377+
378+
# ensure data has correct dimensions
379+
ndata = all_data['ndata']
380+
if len(np.shape(all_data['measured'])) < 2 and ndata == mtimes:
381+
ndata = 1
382+
383+
# assign the data to the ods
384+
del all_data['time']
385+
del all_data['mtime']
386+
del all_data['ndata']
387+
cocosio = MDS_gEQDSK_COCOS_identify(machine, pulse, EFIT_tree, EFIT_run_id)
388+
with omas_environment(ods, cocosio=cocosio):
389+
for entry, data in all_data.items():
390+
try:
391+
if ndata == 1:
392+
data = np.atleast_2d(data).T
393+
for k, ind in enumerate(it):
394+
for n in range(ndata):
395+
ods[f'equilibrium.time_slice.{k}.constraints.{base}.{n}.{entry}'] = data[ind][n]
396+
except:
397+
printe(f'EFIT data was not found for {base} {entry}')
398+
399+
400+
def concat_constraint_data(ods, machine, pulse, EFIT_tree, base, measured, measured_error_upper=None, weight=None, reconstructed=None, chi_squared=None, EFIT_run_id=None, **kw):
401+
"""
402+
Loads EFIT constraint data and concatonates muliple arrays
403+
"""
404+
405+
pulse_id = get_pulse_id(pulse, EFIT_run_id)
406+
407+
# first form a list of signals to fetch
408+
TDIs = {}
409+
TDIs['time'] = f'data(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.GTIME)'
410+
TDIs['mtime'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.MTIME)'
411+
n = len(np.atleast_1d(measured))
412+
for i in range(n):
413+
TDIs[f'ndata_{i}'] = f'size(\\{EFIT_tree}::TOP.MEASUREMENTS.{measured[i]},0)'
414+
TDIs[f'measured_{i}'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{measured[i]})'
415+
if measured_error_upper is not None:
416+
TDIs[f'measured_error_upper_{i}'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{measured_error_upper[i]})'
417+
if weight is not None:
418+
TDIs[f'weight_{i}'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{weight[i]})'
419+
if reconstructed is not None:
420+
TDIs[f'reconstructed_{i}'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{reconstructed[i]})'
421+
if chi_squared is not None:
422+
TDIs[f'chi_squared_{i}'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{chi_squared[i]})'
423+
424+
# fetch the data for all signals
425+
all_data = mdsvalue(machine, EFIT_tree, pulse_id, TDIs).raw()
426+
427+
# determine common times for equilibrium data
428+
# this is required because the between shot EFIT filters are not applied to the MEASUREMENTS
429+
ntimes = len(all_data['time'])
430+
mtimes = len(all_data['mtime'])
431+
if ntimes == mtimes:
432+
it = np.arange(ntimes)
433+
else:
434+
it = np.minimum(all_data['mtime'].searchsorted(all_data['time']), ntimes-1)
435+
436+
# concatonate data
437+
concat_data = {}
438+
concat_data['ndata'] = 0
439+
concat_data['measured'] = np.empty((mtimes,0))
440+
if measured_error_upper is not None:
441+
concat_data['measured_error_upper'] = np.empty((mtimes,0))
442+
if weight is not None:
443+
concat_data['weight'] = np.empty((mtimes,0))
444+
if reconstructed is not None:
445+
concat_data['reconstructed'] = np.empty((mtimes,0))
446+
if chi_squared is not None:
447+
concat_data['chi_squared'] = np.empty((mtimes,0))
448+
for entry in concat_data.keys():
449+
for i in range(n):
450+
if entry == 'ndata':
451+
try:
452+
concat_data[entry] += all_data[f'{entry}_{i}']
453+
except:
454+
printe(f'could not find EFIT constraint {base}')
455+
return
456+
else:
457+
try:
458+
concat_data[entry] = np.concatenate([concat_data[entry], all_data[f'{entry}_{i}']], axis=1)
459+
except:
460+
printe(f'EFIT data was not found for {base} {entry}')
461+
break
462+
463+
# assign the data to the ods
464+
if concat_data['ndata'] <= 0:
465+
printe(f'could not find EFIT constraint {base}')
466+
return
467+
ndata = concat_data['ndata']
468+
del concat_data['ndata']
469+
cocosio = MDS_gEQDSK_COCOS_identify(machine, pulse, EFIT_tree, EFIT_run_id)
470+
with omas_environment(ods, cocosio=cocosio):
471+
for entry, data in concat_data.items():
472+
if len(np.atleast_1d(data)) == 0:
473+
continue
474+
for k, ind in enumerate(it):
475+
for n in range(ndata):
476+
ods[f'equilibrium.time_slice.{k}.constraints.{base}.{n}.{entry}'] = data[ind][n]
477+
478+
479+
def constraint_psi_to_real_psi(ods, machine, pulse, EFIT_tree, base, psin, EFIT_run_id=None, **kw):
480+
"""
481+
Loads psi_norm for constraints, converts to physical psi, and stores at equilibrium times
482+
"""
483+
484+
pulse_id = get_pulse_id(pulse, EFIT_run_id)
485+
486+
# first form a list of signals to fetch
487+
TDIs = {}
488+
TDIs['time'] = f'data(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.GTIME)'
489+
TDIs['mtime'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.MTIME)'
490+
TDIs['ndata'] = f'size(\\{EFIT_tree}::TOP.MEASUREMENTS.{psin},0)'
491+
TDIs['psia'] = f'data(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.SSIMAG)'
492+
TDIs['psib'] = f'data(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.SSIBRY)'
493+
TDIs['psin'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.{psin})'
494+
495+
# fetch the data for all signals
496+
data = mdsvalue(machine, EFIT_tree, pulse_id, TDIs).raw()
497+
498+
try:
499+
ndata = data['ndata']
500+
psin = data['psin']
501+
except:
502+
printe(f'EFIT data was not found for {base} position.psi')
503+
return
504+
505+
# determine common times for equilibrium data
506+
# this is required because the between shot EFIT filters are not applied to the MEASUREMENTS
507+
ntimes = len(data['time'])
508+
mtimes = len(data['mtime'])
509+
if ntimes == mtimes:
510+
it = np.arange(ntimes)
511+
else:
512+
it = np.minimum(data['mtime'].searchsorted(data['time']), ntimes-1)
513+
psin = psin[it]
514+
515+
# ensure psi has correct dimensions
516+
if len(np.shape(psin)) < 2 and ndata == mtimes:
517+
ndata = 1
518+
psin = np.atleast_2d(psin).T
519+
520+
# convert from normalized to real psi
521+
psi = (abs(psin).T * (data['psib'] - data['psia']) + data['psia']).T
522+
523+
# assign the data to the ods
524+
cocosio = MDS_gEQDSK_COCOS_identify(machine, pulse, EFIT_tree, EFIT_run_id)
525+
with omas_environment(ods, cocosio=cocosio):
526+
for k in range(ntimes):
527+
for n in range(ndata):
528+
ods[f'equilibrium.time_slice.{k}.constraints.{base}.{n}.position.psi'] = psi[k][n]
529+
530+
531+
def efit_iteration_number(ods, machine, pulse, EFIT_tree, EFIT_run_id=None, **kw):
532+
"""
533+
Determines the number of EFIT iterations taken for each time and stores in the ods
534+
"""
535+
536+
pulse_id = get_pulse_id(pulse, EFIT_run_id)
537+
538+
# first form a list of signals to fetch
539+
TDIs = {}
540+
TDIs['time'] = f'data(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.GTIME)'
541+
TDIs['mtime'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.MTIME)'
542+
TDIs['ndim'] = f'dim_of(\\{EFIT_tree}::TOP.MEASUREMENTS.CERROR,0)'
543+
TDIs['error'] = f'data(\\{EFIT_tree}::TOP.MEASUREMENTS.CERROR)'
544+
545+
# fetch the data for all signals
546+
data = mdsvalue(machine, EFIT_tree, pulse_id, TDIs).raw()
547+
548+
# determine common times for equilibrium data
549+
# this is required because the between shot EFIT filters are not applied to the MEASUREMENTS
550+
ntimes = len(data['time'])
551+
if ntimes == len(data['mtime']):
552+
it = np.arange(ntimes)
553+
else:
554+
it = np.minimum(data['mtime'].searchsorted(data['time']), ntimes-1)
555+
556+
# find the number of iterations taken
557+
try:
558+
ndim = data['ndim']
559+
error = data['error']
560+
n = np.array([ndim for k in range(error.shape[0])])
561+
n[error == 0] = 0
562+
iterations = np.nanmax(n, axis=1)
563+
except:
564+
printe(f'could not determine the number of EFIT iterations taken')
565+
return
566+
567+
# assign the data to the ods
568+
for k, ind in enumerate(it):
569+
ods[f'equilibrium.time_slice.{k}.convergence.iterations_n'] = iterations[ind]
570+
571+
295572
def fetch_assign(
296573
ods,
297574
ods1,

0 commit comments

Comments
 (0)