Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test new efit mappings for ida #257

Merged
merged 41 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f0e5394
Extended magnetic mapping
AreWeDreaming Nov 30, 2022
2f0e0b3
Fixed some horrible math errors from last commit
AreWeDreaming Jan 5, 2023
a634444
Fixed error in rho_tor to rho_pol conversion
AreWeDreaming Feb 16, 2023
1d7ab5e
Merged master into this branch
AreWeDreaming Feb 16, 2023
a35e79d
Fixed some bugs, but the result is still wrong
AreWeDreaming Feb 16, 2023
df2cf6b
Fixed broken psi <-> conversion
AreWeDreaming Feb 16, 2023
989b295
Merge remote-tracking branch 'origin/master' into mapping_to_from_rho…
AreWeDreaming Jul 13, 2023
fbdd940
Some changes that allow imas_set for ids objects
AreWeDreaming Jul 17, 2023
37fb8f0
Merge branch 'EFIT_mdsplus_mapping' into test_new_EFIT_mappings_for_IDA
AreWeDreaming Jul 18, 2023
ed3700f
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 18, 2023
bae815f
Merge remote-tracking branch 'origin/imas_set_for_ids_objects' into t…
AreWeDreaming Jul 18, 2023
d0a7197
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 18, 2023
432a26b
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 18, 2023
97d4aba
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 18, 2023
537f900
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 18, 2023
a71f710
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 18, 2023
8bf84e6
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 18, 2023
b1df43a
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 19, 2023
a8c90d9
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 19, 2023
699ec70
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 19, 2023
1382e78
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 19, 2023
48371b6
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 19, 2023
8d958d7
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 19, 2023
be1243f
Merge remote-tracking branch 'origin/cherry_pick_omas_viewer_dev' int…
AreWeDreaming Jul 19, 2023
6b0bf16
Fixed missing raised exception
AreWeDreaming Jul 19, 2023
ada7df0
Merge remote-tracking branch 'origin/cherry_pick_omas_viewer_dev' int…
AreWeDreaming Jul 19, 2023
79fd988
Merge remote-tracking branch 'origin/cherry_pick_omas_viewer_dev' int…
AreWeDreaming Jul 19, 2023
3d43089
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 20, 2023
19204ba
Merge remote-tracking branch 'origin/EFIT_mdsplus_mapping' into test_…
AreWeDreaming Jul 20, 2023
767e7b9
Merge remote-tracking branch 'origin/cherry_pick_omas_viewer_dev' int…
AreWeDreaming Jul 20, 2023
ad2dc06
Changes needed to get ITPA IDA to work
AreWeDreaming Jul 27, 2023
98c74dd
Merge branch 'EFIT_mdsplus_mapping' into test_new_EFIT_mappings_for_IDA
AreWeDreaming Jul 28, 2023
946b5d0
Merge branch 'EFIT_mdsplus_mapping' into test_new_EFIT_mappings_for_IDA
AreWeDreaming Jul 28, 2023
0d9a398
Merge branch 'EFIT_mdsplus_mapping' into test_new_EFIT_mappings_for_IDA
AreWeDreaming Jul 29, 2023
2470512
Merge branch 'test_new_EFIT_mappings_for_IDA' of github.com:gafusion/…
AreWeDreaming Jul 29, 2023
e12d609
Merge branch 'test_new_EFIT_mappings_for_IDA' of github.com:gafusion/…
AreWeDreaming Jul 31, 2023
dc92fa9
Merge branch 'EFIT_mdsplus_mapping' into test_new_EFIT_mappings_for_IDA
AreWeDreaming Jul 31, 2023
a613a21
Merge branch 'master' into test_new_EFIT_mappings_for_IDA
AreWeDreaming Aug 15, 2023
13c9570
Merge branch 'master' into test_new_EFIT_mappings_for_IDA
AreWeDreaming Dec 11, 2023
a06c7d8
Merge branch 'cherry_pick_omas_viewer_dev' into test_new_EFIT_mapping…
AreWeDreaming Dec 12, 2023
bc99513
Merge branch 'master' into test_new_EFIT_mappings_for_IDA
AreWeDreaming Jun 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion omas/machine_mappings/d3d.json
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@
"PYTHON": "ec_launcher_active_hardware(ods, {pulse})"
},
"ece": {
"PYTHON": "electron_cyclotron_emission_hardware(ods, {pulse}, {fast_ece!r})"
"PYTHON": "electron_cyclotron_emission_hardware(ods, {pulse}, {fast_ece!r})"
},
"ece.channel.:": {
"PYTHON": "electron_cyclotron_emission_hardware(ods, {pulse}, {fast_ece!r})"
Expand All @@ -310,6 +310,9 @@
"ece.channel.:.identifier": {
"PYTHON": "electron_cyclotron_emission_hardware(ods, {pulse}, {fast_ece!r})"
},
"ece.channel.:.if_bandwidth": {
"PYTHON": "electron_cyclotron_emission_hardware(ods, {pulse}, {fast_ece!r})"
},
"ece.channel.:.name": {
"PYTHON": "electron_cyclotron_emission_hardware(ods, {pulse}, {fast_ece!r})"
},
Expand All @@ -319,6 +322,9 @@
"ece.channel.:.time": {
"PYTHON": "electron_cyclotron_emission_hardware(ods, {pulse}, {fast_ece!r})"
},
"ece.ids_properties.homogeneous_time": {
"PYTHON": "electron_cyclotron_emission_hardware(ods, {pulse}, {fast_ece!r})"
},
"ece.line_of_sight.first_point.phi": {
"COCOSIO": 11,
"PYTHON": "electron_cyclotron_emission_hardware(ods, {pulse}, {fast_ece!r})"
Expand Down Expand Up @@ -697,5 +703,8 @@
2
],
"treename": "{EFIT_tree}"
},
"wall.ids_properties.homogeneous_time": {
"VALUE": 1
}
}
5 changes: 3 additions & 2 deletions omas/machine_mappings/d3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ def electron_cyclotron_emission_data(ods, pulse=133221, fast_ece=False, _measure
TECE = '\\ECE::TOP.TECE.TECE' + fast_ece

query = {}
for node, quantities in zip([setup, cal], [['ECEPHI', 'ECETHETA', 'ECEZH', 'FREQ'], ['NUMCH']]):
for node, quantities in zip([setup, cal], [['ECEPHI', 'ECETHETA', 'ECEZH', 'FREQ', "FLTRWID"], ['NUMCH']]):
for quantity in quantities:
query[quantity] = node + quantity
query['TIME'] = f"dim_of({TECE + '01'})"
Expand All @@ -786,7 +786,7 @@ def electron_cyclotron_emission_data(ods, pulse=133221, fast_ece=False, _measure
for ich in range(1, N_ch + 1):
query[f'T{ich}'] = TECE + '{0:02d}'.format(ich)
ece_data = mdsvalue('d3d', treename='ELECTRONS', pulse=pulse, TDI=query).raw()

ods['ece.ids_properties.homogeneous_time'] = 0
# Not in mds+
if not _measurements:
points = [{}, {}]
Expand All @@ -813,6 +813,7 @@ def electron_cyclotron_emission_data(ods, pulse=133221, fast_ece=False, _measure
ch['time'] = ece_map['TIME'] * 1.0e-3
f[:] = ece_map['FREQ'][ich]
ch['frequency']['data'] = f * 1.0e9
ch['if_bandwidth'] = ece_map['FLTRWID'][ich] * 1.0e9


@machine_mapping_function(__regression_arguments__, pulse=133221)
Expand Down
39 changes: 31 additions & 8 deletions omas/omas_imas.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def imas_open(user, machine, pulse, run, occurrence={}, new=False, imas_major_ve
return IDS(DBentry, occurrence)


def imas_set(ids, path, value, skip_missing_nodes=False, allocate=False):
def imas_set(ids, path, value, skip_missing_nodes=False, allocate=False, ids_is_subtype=False, only_allocate=True):
"""
assign a value to a path of an open IMAS ids

Expand All @@ -100,21 +100,25 @@ def imas_set(ids, path, value, skip_missing_nodes=False, allocate=False):
:return: path if set was done, otherwise None
"""
# handle uncertain data
if type(path) != list:
path = p2l(path)
if is_uncertain(value):
path = copy.deepcopy(path)
tmp = imas_set(ids, path, nominal_values(value), skip_missing_nodes=skip_missing_nodes, allocate=allocate)
tmp = imas_set(ids, path, nominal_values(value), skip_missing_nodes=skip_missing_nodes, allocate=allocate, only_allocate=only_allocate)
path[-1] = path[-1] + '_error_upper'
imas_set(ids, path, std_devs(value), skip_missing_nodes=skip_missing_nodes, allocate=allocate)
imas_set(ids, path, std_devs(value), skip_missing_nodes=skip_missing_nodes, allocate=allocate, only_allocate=only_allocate)
return tmp

ds = path[0]
path = path[1:]

# identify data dictionary to use, from this point on `m` points to the IDS
debug_path = ''
if hasattr(ids, ds):
if hasattr(ids, ds) or ids_is_subtype:
debug_path += '%s' % ds
m = getattr(ids, ds)
if ids_is_subtype:
m = ids
else:
m = getattr(ids, ds)
if hasattr(m, 'time') and not isinstance(m.time, float) and not m.time.size:
m.time= numpy.resize(m.time, 1)
m.time[0] = -1.0
Expand All @@ -130,13 +134,32 @@ def imas_set(ids, path, value, skip_missing_nodes=False, allocate=False):

# traverse IMAS structure until reaching the leaf
out = m
done = allocate
for kp, p in enumerate(path):
location = l2i([ds] + path[: kp + 1])
if isinstance(p, str):
if hasattr(out, p):
if p == ":":
if allocate and len(out) != len(value):
out.resize(len(value))
done = True
if kp == len(path) - 1:
break
else:
if len(value) == 1:
out = out[0]
break
else:
for i in range(value.shape[0]):
if len(path[kp + 1:]) == 1:
setattr(out[i], path[-1], value[i])
else:
imas_set(out[i], path[kp + 1:], value[i], skip_missing_nodes=False, allocate=allocate,only_allocate=only_allocate)
return [ds] + path
elif hasattr(out, p):
if kp < (len(path) - 1):
debug_path += '.' + p
out = getattr(out, p)

elif skip_missing_nodes is not False:
if skip_missing_nodes is None:
printe('WARNING: %s is not part of IMAS' % location)
Expand All @@ -157,7 +180,7 @@ def imas_set(ids, path, value, skip_missing_nodes=False, allocate=False):
out = out[p]

# if we are allocating data, simply stop here
if allocate:
if done and only_allocate:
return [ds] + path

# assign data to leaf node
Expand Down
158 changes: 133 additions & 25 deletions omas/omas_physics.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,72 @@ def equilibrium_ggd_to_rectangular(ods, time_index=None, resolution=None, method
profiles_2d['grid.dim2'] = z
return ods_n

@add_to__ODS__
@preprocess_ods('equilibrium')
def add_rho_pol_norm_to_equilbrium_profiles_1d_ods(ods, time_index):
try:
if (len(ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["rho_pol-norm"]) ==
len(ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["psi"])):
return
else:
raise LookupError
except LookupError:
ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["rho_pol_norm"] = (
ods.map_pol_flux_to_flux_coordinate(ods, time_index, "rho_pol_norm",
ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["psi"])
)

def mask_SOL(ods, time_index, psi_values):
"""
Returns a numpy array of `dtype=bool`/
The array is true for all values inside and on the LCFS
:param ods: input ods

:param time_index: time slices to process

:param values: Psi values that need to masked

:return: mask that is True for values inside and on the LCFS
"""
if (ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_axis"] <
ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_boundary"]):
return psi_values <= ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_boundary"]
else:
return psi_values >= ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_boundary"]

@add_to__ODS__
@preprocess_ods('equilibrium')
def add_phi_to_equilbrium_profiles_1d_ods(ods, time_index):
"""
Adds `profiles_1d.phi` to an ODS using q
:param ods: input ods

:param time_index: time slices to process
"""
try:
if (len(ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["phi"]) ==
len(ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["psi"])):
return
else:
raise LookupError
except (LookupError, ValueError):
from scipy.interpolate import InterpolatedUnivariateSpline
#TODO:
# - Any cocos needed here?
psi = ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["psi"]
mask = mask_SOL(ods, time_index, psi)
q_spline = InterpolatedUnivariateSpline(
ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["psi"][mask],
ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["q"][mask])
phi_spline = q_spline.antiderivative(1)
ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["phi"] = numpy.zeros(psi.shape)
ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["phi"][mask] = (
phi_spline(ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["psi"][mask]))
ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["phi"][mask==False] = numpy.inf


def map_flux_coordinate_to_pol_flux(ods, time_index, origin, values):
import numpy as np
"""
Maps from one magnetic coordinate system to psi
:param ods: input ods
Expand All @@ -227,15 +291,31 @@ def map_flux_coordinate_to_pol_flux(ods, time_index, origin, values):

:return: Transformed values
"""
if origin == "rho_pol":
return (
values**2
* (
ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_sep"]
- ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_axis"]
)
+ ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_axis"]
)
if origin == "rho_pol_norm":
return (values**2 * (ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_boundary"]
- ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_axis"])
+ ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_axis"])
elif origin == "rho_tor_norm":
phi = values**2
phi *= map_pol_flux_to_flux_coordinate(ods, time_index, "phi",
np.array([ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_boundary"]]))
return map_flux_coordinate_to_pol_flux(ods, time_index, "phi", phi)
elif origin == "phi":
from scipy.interpolate import InterpolatedUnivariateSpline
psi_grid = ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["psi"]
psi_mask = mask_SOL(ods, time_index, psi_grid)
phi_grid = ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["phi"][psi_mask]
if phi_grid[-1] < phi_grid[0]:
psi_spl = InterpolatedUnivariateSpline(phi_grid[psi_mask][::-1], psi_grid[psi_mask][::-1])
else:
psi_spl = InterpolatedUnivariateSpline(phi_grid[psi_mask], psi_grid[psi_mask])
phi_min = np.min(phi_grid)
phi_max = np.max(phi_grid)
values_mask = np.logical_and(values >= phi_min, values <= phi_max)
psi = np.zeros(values.shape)
psi[:] = np.nan
psi[values_mask] = psi_spl(values[values_mask])
return psi
else:
raise NotImplementedError(f"Conversion from {origin} not yet implemented.")

Expand All @@ -255,14 +335,42 @@ def map_pol_flux_to_flux_coordinate(ods, time_index, destination, values):

:return: Transformed values
"""
if destination == "rho_pol":
return np.sqrt(
(values - ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_axis"])
/ (
ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_boundary"]
- ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_axis"]
)
)
if destination == "rho_pol_norm":
return np.sqrt((values - ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_axis"]) /
(ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_boundary"]
- ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_axis"]))
elif destination == "rho_tor_norm":
mask = mask_SOL(ods, time_index, values)
phi = map_pol_flux_to_flux_coordinate(ods, time_index, "phi", values[mask])
rho_tor_norm = np.zeros(values.shape)
phi_boundary = map_pol_flux_to_flux_coordinate(ods, time_index, "phi",
np.array([ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_boundary"]]))
rho_tor_norm[mask] = np.sqrt(phi / phi_boundary)
rho_tor_norm[mask==False] = np.inf
return rho_tor_norm
elif destination == "phi":
from scipy.interpolate import InterpolatedUnivariateSpline
psi_grid = ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["psi"]
psi_grid_mask = mask_SOL(ods, time_index, psi_grid)
try:
phi_spl = InterpolatedUnivariateSpline(psi_grid[psi_grid_mask],
ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["phi"][psi_grid_mask])
except ValueError:
add_phi_to_equilbrium_profiles_1d_ods(ods, time_index)
phi_spl = InterpolatedUnivariateSpline(psi_grid[psi_grid_mask],
ods["equilibrium"]["time_slice"][time_index]["profiles_1d"]["phi"][psi_grid_mask])
mask = mask_SOL(ods, time_index, values)
phi = np.zeros(values.shape)
phi[mask] = phi_spl(values[mask])
phi_bound = phi_spl(ods["equilibrium"]["time_slice"][time_index]["global_quantities"]["psi_boundary"])
phi[mask==False] = np.inf * np.sign(phi_bound)
wrong_sign_mask = phi_bound * phi < 0
if np.any(wrong_sign_mask):
if np.any(np.abs(phi[wrong_sign_mask]/phi_bound) > 1.e-4):
raise ValueError("Unphysical phi encountered when mapping to phi")
else:
phi[wrong_sign_mask] = 0.0
return phi
else:
raise NotImplementedError(f"Conversion to {destination} not yet implemented.")

Expand Down Expand Up @@ -350,17 +458,17 @@ def derive_equilibrium_profiles_2d_quantity(ods, time_index, grid_index, quantit
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.psi'],
)
cocos = define_cocos(11)
if quantity == "b_r":
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.b_r'] = (
if quantity == "b_field_r":
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.b_field_r'] = (
psi_spl(r, z, dy=1, grid=False) * cocos['sigma_RpZ'] * cocos['sigma_Bp'] / ((2.0 * numpy.pi) ** cocos['exp_Bp'] * r)
)
return ods
elif quantity == "b_z":
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.b_z'] = (
elif quantity == "b_field_z":
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.b_field_z'] = (
-psi_spl(r, z, dx=1, grid=False) * cocos['sigma_RpZ'] * cocos['sigma_Bp'] / ((2.0 * numpy.pi) ** cocos['exp_Bp'] * r)
)
return ods
elif quantity == "b_tor":
elif quantity == "b_field_tor":
mask = numpy.logical_and(
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.psi']
< numpy.max(ods[f'equilibrium.time_slice.{time_index}.profiles_1d.psi']),
Expand All @@ -370,11 +478,11 @@ def derive_equilibrium_profiles_2d_quantity(ods, time_index, grid_index, quantit
f_spl = InterpolatedUnivariateSpline(
ods[f'equilibrium.time_slice.{time_index}.profiles_1d.psi'], ods[f'equilibrium.time_slice.{time_index}.profiles_1d.f']
)
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.b_tor'] = numpy.zeros(r.shape)
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.b_tor'][mask] = (
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.b_field_tor'] = numpy.zeros(r.shape)
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.b_field_tor'][mask] = (
f_spl(psi_spl(r[mask], z[mask], grid=False)) / r[mask]
)
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.b_tor'][mask == False] = (
ods[f'equilibrium.time_slice.{time_index}.profiles_2d.{grid_index}.b_field_tor'][mask == False] = (
ods[f'equilibrium.time_slice.{time_index}.profiles_1d.f'][-1] / r[mask == False]
)
return ods
Expand Down
Loading