diff --git a/omas/machine_mappings/_common.py b/omas/machine_mappings/_common.py index 2f8ad1bf..4d2c8909 100644 --- a/omas/machine_mappings/_common.py +++ b/omas/machine_mappings/_common.py @@ -4,7 +4,7 @@ import os import glob from omas.omas_setup import omas_dir -from omas.utilities.omas_mds import mdsvalue +from omas.utilities.omas_mds import mdsvalue, get_pulse_id __support_files_cache__ = {} @@ -47,30 +47,33 @@ def get_support_file(object_type, filename): __MDS_gEQDSK_COCOS_identify_cache__ = {} -def MDS_gEQDSK_COCOS_identify(machine, pulse, EFIT_tree): +def MDS_gEQDSK_COCOS_identify(machine, pulse, EFIT_tree, EFIT_run_id): """ Python function that queries MDS+ EFIT tree to figure out COCOS convention used for a particular reconstruction :param machine: machine name - :param pulse: pulse + :param pulse: pulse number :param EFIT_tree: MDS+ EFIT tree name + :param EFIT_run_id: with id extension for non-standard shot numbers. E.g. 19484401 for EFIT tree + :return: integer cocos convention """ - if (machine, pulse, EFIT_tree) in __MDS_gEQDSK_COCOS_identify_cache__: - return __MDS_gEQDSK_COCOS_identify_cache__[(machine, pulse, EFIT_tree)] + pulse_id = get_pulse_id(pulse, EFIT_run_id) + if (machine, pulse_id, EFIT_tree) in __MDS_gEQDSK_COCOS_identify_cache__: + return __MDS_gEQDSK_COCOS_identify_cache__[(machine, pulse_id, EFIT_tree)] TDIs = {'bt': f'mean(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.BCENTR)', 'ip': f'mean(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.CPASMA)'} - res = mdsvalue(machine, EFIT_tree, pulse, TDIs).raw() + res = mdsvalue(machine, EFIT_tree, pulse_id, TDIs).raw() bt = res['bt'] ip = res['ip'] g_cocos = {(+1, +1): 1, (+1, -1): 3, (-1, +1): 5, (-1, -1): 7, (+1, 0): 1, (-1, 0): 3} sign_Bt = int(np.sign(bt)) sign_Ip = int(np.sign(ip)) cocosio = g_cocos.get((sign_Bt, sign_Ip), None) - __MDS_gEQDSK_COCOS_identify_cache__[(machine, pulse, EFIT_tree)] = cocosio + __MDS_gEQDSK_COCOS_identify_cache__[(machine, pulse_id, EFIT_tree)] = cocosio return cocosio diff --git a/omas/machine_mappings/_efit.json b/omas/machine_mappings/_efit.json index 0b6efd04..6a67a1f4 100644 --- a/omas/machine_mappings/_efit.json +++ b/omas/machine_mappings/_efit.json @@ -2,7 +2,7 @@ "__cocos_rules__": { "EFIT_tree": { "eval2TDI": "py2tdi(MDS_gEQDSK_COCOS_identify, 'data(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.BCENTR)', 'data(\\{EFIT_tree}::TOP.RESULTS.GEQDSK.CPASMA)')", - "PYTHON": "MDS_gEQDSK_COCOS_identify({machine!r}, {pulse}, {EFIT_tree!r})" + "PYTHON": "MDS_gEQDSK_COCOS_identify({machine!r}, {pulse}, {EFIT_tree!r}, {EFIT_run_id!r})" } }, "equilibrium.code.name": { diff --git a/omas/machine_mappings/d3d.json b/omas/machine_mappings/d3d.json index 063d9384..bb8c035d 100644 --- a/omas/machine_mappings/d3d.json +++ b/omas/machine_mappings/d3d.json @@ -12,7 +12,9 @@ "default_tree": "D3D", "fast_ece": false, "nref": 0, - "revision": "BLESSED" + "revision": "BLESSED", + "PROFILES_run_id": null, + "EFIT_run_id": null }, "bolometer.channel.:": { "PYTHON": "bolometer_hardware(ods, {pulse})" @@ -113,36 +115,33 @@ "coils_non_axisymmetric.coil.:.name": { "PYTHON": "coils_non_axisymmetric_hardware(ods, {pulse})" }, - "core_profiles": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" - }, "core_profiles.global_quantities.v_loop": { "COCOSIO": 11, "PYTHON": "core_profiles_global_quantities_data(ods, {pulse})" }, "core_profiles.ids_properties.homogeneous_time": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.e_field.radial": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.e_field.radial_error_upper": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.electrons.density_error_upper": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.electrons.density_fit.measured": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.electrons.density_fit.measured_error_upper": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.electrons.density_fit.psi_norm": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.electrons.density_thermal": { "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" @@ -151,28 +150,28 @@ "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" }, "core_profiles.profiles_1d.:.electrons.temperature": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.electrons.temperature_error_upper": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.electrons.temperature_fit.measured": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.electrons.temperature_fit.measured_error_upper": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.electrons.temperature_fit.psi_norm": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.grid.rho_pol_norm": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.grid.rho_tor_norm": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.density_fit.measured": { "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" @@ -195,61 +194,64 @@ "core_profiles.profiles_1d.:.ion.:.density_thermal_fit.measured_error_upper": { "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" }, + "core_profiles.profiles_1d.:.ion.:.density_fit.psi_norm": { + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" + }, "core_profiles.profiles_1d.:.ion.:.element.:": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.element.:.a": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.element.:.z_n": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.label": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.temperature": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.temperature_error_upper": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.temperature_fit.measured": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.temperature_fit.measured_error_upper": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.temperature_fit.psi_norm": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.velocity.toroidal": { "COCOSIO": 11, - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.velocity.toroidal_error_upper": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.velocity.toroidal_fit.measured": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.velocity.toroidal_fit.measured_error_upper": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.ion.:.velocity.toroidal_fit.psi_norm": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.j_total": { "COCOSIO": 11, - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.pressure_perpendicular": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "core_profiles.profiles_1d.:.time": { "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" }, "core_profiles.time": { - "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r})" + "PYTHON": "core_profiles_profile_1d(ods, {pulse}, {PROFILES_tree!r}, {PROFILES_run_id!r})" }, "ec_launchers.beam.:": { "PYTHON": "ec_launcher_active_hardware(ods, {pulse})" diff --git a/omas/machine_mappings/d3d.py b/omas/machine_mappings/d3d.py index fa9cab44..1dbe35ba 100644 --- a/omas/machine_mappings/d3d.py +++ b/omas/machine_mappings/d3d.py @@ -11,6 +11,7 @@ from omas.omas_core import ODS from omas.omas_structure import add_extra_structures from omas.omas_physics import omas_environment +import copy __all__ = [] __regression_arguments__ = {'__all__': __all__} @@ -1383,12 +1384,13 @@ def add_extra_profile_structures(): add_extra_structures(extra_structures) -@machine_mapping_function(__regression_arguments__, pulse=194842001, PROFILES_tree="OMFIT_PROFS") -def core_profiles_profile_1d(ods, pulse, PROFILES_tree="OMFIT_PROFS"): +@machine_mapping_function(__regression_arguments__, pulse=194844, PROFILES_tree="OMFIT_PROFS", PROFILES_run_id='001') +def core_profiles_profile_1d(ods, pulse, PROFILES_tree="OMFIT_PROFS", PROFILES_run_id=None): add_extra_profile_structures() ods["core_profiles.ids_properties.homogeneous_time"] = 1 sh = "core_profiles.profiles_1d" if "OMFIT_PROFS" in PROFILES_tree: + pulse_id = int(str(pulse) + PROFILES_run_id) omfit_profiles_node = '\\TOP.' query = { "electrons.density_thermal": "N_E", @@ -1419,11 +1421,11 @@ def core_profiles_profile_1d(ods, pulse, PROFILES_tree="OMFIT_PROFS"): query[entry] = omfit_profiles_node + query[entry] for entry in uncertain_entries: query[entry + "_error_upper"] = "error_of(" + query[entry] + ")" - data = mdsvalue('d3d', treename=PROFILES_tree, pulse=pulse, TDI=query).raw() + data = mdsvalue('d3d', treename=PROFILES_tree, pulse=pulse_id, TDI=query).raw() if data is None: print("No mds+ data") raise ValueError(f"Could not find any data in MDS+ for {pulse} and {PROFILES_tree}") - dim_info = mdsvalue('d3d', treename=PROFILES_tree, pulse=pulse, TDI="\\TOP.n_e") + dim_info = mdsvalue('d3d', treename=PROFILES_tree, pulse=pulse_id, TDI="\\TOP.n_e") data['time'] = dim_info.dim_of(1) * 1.e-3 psi_n = dim_info.dim_of(0) data['grid.rho_pol_norm'] = np.zeros((data['time'].shape + psi_n.shape)) @@ -1469,6 +1471,8 @@ def core_profiles_profile_1d(ods, pulse, PROFILES_tree="OMFIT_PROFS"): ods[f"{sh}[{i_time}].ion[1].element[0].a"] = 12.011 ods[f"{sh}[{i_time}].ion[0].label"] = "D" ods[f"{sh}[{i_time}].ion[1].label"] = "C" + ods[f"{sh}[{i_time}].electrons.density_thermal"] = copy.deepcopy(ods[f"{sh}[{i_time}].electrons.density"]) + ods[f"{sh}[{i_time}].electrons.density_thermal_error_upper"] = copy.deepcopy(ods[f"{sh}[{i_time}].electrons.density_error_upper"]) else: profiles_node = '\\TOP.PROFILES.' query = { @@ -1483,6 +1487,14 @@ def core_profiles_profile_1d(ods, pulse, PROFILES_tree="OMFIT_PROFS"): data = mdsvalue('d3d', treename=PROFILES_tree, pulse=pulse, TDI=query).raw() dim_info = mdsvalue('d3d', treename=PROFILES_tree, pulse=pulse, TDI="\\TOP.PROFILES.EDENSFIT") data['time'] = dim_info.dim_of(1) * 1.e-3 + dim_info = mdsvalue('d3d', treename=PROFILES_tree, pulse=pulse, TDI="\\TOP.PROFILES.ETEMPFIT") + data['time_te'] = dim_info.dim_of(1) * 1.e-3 + ods[f"core_profiles.time"] = data['time'][np.in1d(data['time'], data['time_te'])] + mask_dict = { + "electrons.density": np.in1d(data['time'], ods[f"core_profiles.time"]), + "electrons.density_thermal": np.in1d(data['time'], ods[f"core_profiles.time"]), + "electrons.temperature": np.in1d(data['time_te'], ods[f"core_profiles.time"]) + } rho_tor_norm = dim_info.dim_of(0) data['grid.rho_tor_norm'] = np.zeros((data['time'].shape + rho_tor_norm.shape)) data['grid.rho_tor_norm'][:] = rho_tor_norm @@ -1491,7 +1503,7 @@ def core_profiles_profile_1d(ods, pulse, PROFILES_tree="OMFIT_PROFS"): if isinstance(data[entry], Exception): continue for i_time, time in enumerate(data["time"]): - ods[f"core_profiles.profiles_1d[{i_time}]."+entry] = data[entry][i_time] + ods[f"core_profiles.profiles_1d[{i_time}]."+entry] = data[entry][mask_dict[entry]][i_time] #Needed for ion components #for i_time, time in enumerate(data["time"]): # ods[f"{sh}[{i_time}].ion[0].element[0].z_n"] = 1 @@ -1502,19 +1514,25 @@ def core_profiles_profile_1d(ods, pulse, PROFILES_tree="OMFIT_PROFS"): # ods[f"{sh}[{i_time}].ion[1].label"] = "C" # ================================ -@machine_mapping_function(__regression_arguments__, pulse=133221) -def core_profiles_global_quantities_data(ods, pulse): +@machine_mapping_function(__regression_arguments__, pulse=133221, PROFILES_tree="ZIPFIT01", PROFILES_run_id=None) +def core_profiles_global_quantities_data(ods, pulse, PROFILES_tree="ZIPFIT01", PROFILES_run_id=None): from scipy.interpolate import interp1d ods1 = ODS() unwrap(magnetics_hardware)(ods1, pulse) - with omas_environment(ods, cocosio=1): cp = ods['core_profiles'] gq = ods['core_profiles.global_quantities'] if 'time' not in cp: - m = mdsvalue('d3d', pulse=pulse, TDI="\\TOP.PROFILES.EDENSFIT", treename="ZIPFIT01") - cp['time'] = m.dim_of(1) * 1e-3 + if "ZIPFIT0" in PROFILES_tree: + m = mdsvalue('d3d', pulse=pulse, TDI="\\TOP.PROFILES.EDENSFIT", treename=PROFILES_tree) + cp['time'] = m.dim_of(1) * 1e-3 + elif "OMFIT_PROFS" in PROFILES_tree and PROFILES_run_id is not None: + pulse_id = int(str(pulse) + PROFILES_run_id) + dim_info = mdsvalue('d3d', treename=PROFILES_tree, pulse=pulse_id, TDI="\\TOP.n_e") + cp['time'] = dim_info.dim_of(1) * 1.e-3 + else: + raise ValueError(f"Trying to access global_quantities with unknown profiles tree: {PROFILES_tree}") t = cp['time'] m = mdsvalue('d3d', pulse=pulse, TDI=f"ptdata2(\"VLOOP\",{pulse})", treename=None) diff --git a/omas/omas_machine.py b/omas/omas_machine.py index 52003449..ef8e8140 100644 --- a/omas/omas_machine.py +++ b/omas/omas_machine.py @@ -9,7 +9,7 @@ from omas.machine_mappings import d3d, nstx, nstxu, east from omas.machine_mappings.d3d import __regression_arguments__ from omas.utilities.machine_mapping_decorator import machine_mapping_function -from omas.utilities.omas_mds import mdsvalue +from omas.utilities.omas_mds import mdsvalue, check_for_pulse_id try: from MDSplus.connection import MdsIpException from MDSplus.mdsExceptions import TreeNODATA, TreeNNF @@ -102,7 +102,7 @@ def machine_to_omas(ods, machine, pulse, location, options={}, branch='', user_m :param user_mappings: allow specification of external mappings - :param cache: if cache is a dictionary, this will be used to establiish a cash + :param cache: if cache is a dictionary, this will be used to establish a cash :return: updated ODS and data before being assigned to the ODS """ @@ -119,7 +119,7 @@ def machine_to_omas(ods, machine, pulse, location, options={}, branch='', user_m options_with_defaults.update(options) options_with_defaults.update({'machine': machine, 'pulse': pulse, 'location': location}) try: - if not location.endswith(".*"): + if not location.endswith(".*"): # location = "core_profiles.*" mapped = mappings[location] break except KeyError as e: @@ -134,7 +134,7 @@ def machine_to_omas(ods, machine, pulse, location, options={}, branch='', user_m if location.endswith(".*"): root = location.split(".*")[0] for key in mappings: - if root in key: + if root in key and key not in ods: try: resolve_mapped(ods, machine, pulse, mappings, key, idm, options_with_defaults, branch, cache=cache) except (TreeNODATA, MdsIpException) as e: @@ -176,7 +176,7 @@ def resolve_mapped(ods, machine, pulse, mappings, location, idm, options_with_d :param branch: load machine mappings and mapping functions from a specific GitHub branch - :param cache: if cache is a dictionary, this will be used to establiish a cash + :param cache: if cache is a dictionary, this will be used to establish a cash :return: updated ODS and data before being assigned to the ODS """ @@ -249,9 +249,13 @@ def resolve_mapped(ods, machine, pulse, mappings, location, idm, options_with_d # MDS+ elif 'TDI' in mapped: try: + if 'treename' in mapped: + pulse_id = check_for_pulse_id(pulse, mapped['treename'], options_with_defaults) + else: + pulse_id = pulse TDI = mapped['TDI'].format(**options_with_defaults) treename = mapped['treename'].format(**options_with_defaults) if 'treename' in mapped else None - data0 = data = mdsvalue(machine, treename, pulse, TDI).raw() + data0 = data = mdsvalue(machine, treename, pulse_id, TDI).raw() if data is None: raise ValueError('data is None') except Exception as e: diff --git a/omas/omas_physics.py b/omas/omas_physics.py index bb3d378a..47659859 100644 --- a/omas/omas_physics.py +++ b/omas/omas_physics.py @@ -291,6 +291,12 @@ def map_flux_coordinate_to_pol_flux(ods, time_index, origin, values): :return: Transformed values """ + if origin == "psi_norm" or origin == "rho_pol_norm": + if origin == "rho_pol_norm": + values = values**2 + return (values * (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"]) 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"]) @@ -335,10 +341,14 @@ def map_pol_flux_to_flux_coordinate(ods, time_index, destination, values): :return: Transformed values """ - 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"])) + if destination == "psi_norm" or destination == "rho_pol_norm": + psi_n = ((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(psi_n) + else: + return psi_n 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]) diff --git a/omas/omas_plot.py b/omas/omas_plot.py index 184e4101..66ef9b74 100644 --- a/omas/omas_plot.py +++ b/omas/omas_plot.py @@ -526,9 +526,12 @@ def use_subplot(fig, *args, **kw): with key (*args*, *kwargs*) then it will simply make that subplot current and return it. """ - from matplotlib import pyplot - pyplot.figure(num=fig.number) - return pyplot.subplot(*args, **kw) + if hasattr(fig, "number"): + from matplotlib import pyplot + pyplot.figure(num=fig.number) + return pyplot.subplot(*args, **kw) + else: + return fig.add_subplot(*args, **kw) def cached_add_subplot(fig, ax_cache, *args, **kw): @@ -1010,7 +1013,7 @@ def equilibrium_summary(ods, time_index=None, time=None, fig=None, ggd_points_tr :return: figure handler """ - + from omas.omas_physics import remap_flux_coordinates # caching of ggd data if ggd_points_triangles is None and 'equilibrium.grids_ggd' in ods: from .omas_physics import grids_ggd_points_triangles @@ -1047,18 +1050,22 @@ def equilibrium_summary(ods, time_index=None, time=None, fig=None, ggd_points_tr x = eq['profiles_1d'][raw_xName] xName = nice_names.get(raw_xName, raw_xName) else: - raw_xName = 'psi' - x = ((eq['profiles_1d']['psi'] - eq['global_quantities']['psi_axis']) + raw_xName = 'psi_norm' + x = ((eq['profiles_1d']['psi'] - eq['global_quantities']['psi_axis']) / ( eq['global_quantities']['psi_boundary'] - eq['global_quantities']['psi_axis'])) xName = r"$\Psi_\mathrm{n}$" # pressure ax = cached_add_subplot(fig, axs, 2, 3, 2) if omas_viewer: - ax.plot(-ods[f"equilibrium.code.parameters.time_slice.{time_index}.in1.rpress"], - ods[f"equilibrium.code.parameters.time_slice.{time_index}.in1.pressr"]/1.e3, ".r") - plot_1d_equilbrium_quantity(ax, x, eq['profiles_1d']['pressure'] * 1.e-3, - xName, r"$p$ [kPa]", r'$\,$ Pressure', + x_constr = remap_flux_coordinates(ods, time_index, "psi", raw_xName, + ods[f"equilibrium.time_slice.{time_index}.constraints.pressure.:.position.psi"]) + plot_1d_equilbrium_quantity(ax, x_constr, ods[f"equilibrium.time_slice.{time_index}.constraints.pressure.:.measured"] * 1.e-3, + xName, r"$p$ [kPa]", r'$\,$ Pressure', + visible_x=omas_viewer, linestyle="None", marker=".", + color='red') + plot_1d_equilbrium_quantity(ax, x, eq['profiles_1d']['pressure'] * 1.e-3, + xName, r"$p$ [kPa]", r'$\,$ Pressure', visible_x=omas_viewer, **kw) kw.setdefault('color', ax.lines[-1].get_color()) @@ -1082,19 +1089,30 @@ def equilibrium_summary(ods, time_index=None, time=None, fig=None, ggd_points_tr if not omas_viewer: pyplot.setp(ax.get_xticklabels(), visible=False) if omas_viewer: - ax = cached_add_subplot(fig, axs, 2, 3, 3, sharex=ax) - ax.plot(ods[f"equilibrium.code.parameters.time_slice.{time_index}.inwant.sizeroj"], - ods[f"equilibrium.code.parameters.time_slice.{time_index}.inwant.vzeroj"] / 1.e6, ".r") - plot_1d_equilbrium_quantity(ax, x, eq['profiles_1d']['j_tor']/1.e6, - xName, r"$\langle j_\mathrm{tor} / R \rangle$ [MA m$^{-2}$]", - r"$j_\mathrm{tor}$", - visible_x=omas_viewer, **kw) + try: + ax = cached_add_subplot(fig, axs, 2, 3, 3, sharex=ax) + x_constr = remap_flux_coordinates(ods, time_index, "psi", raw_xName, + ods[f"equilibrium.time_slice.{time_index}.constraints.j_tor.:.position.psi"]) + plot_1d_equilbrium_quantity(ax, x_constr, eq["constraints.j_tor.:.measured"] / 1.e6, + xName, r"$\langle j_\mathrm{tor} / R \rangle$ [MA m$^{-2}$]", + r"$j_\mathrm{tor}$", visible_x=omas_viewer, linestyle="None", marker=".", + color='red') + except ValueError: + print("WARNING No data for j_tor constraints") + try: + ax = cached_add_subplot(fig, axs, 2, 3, 3, sharex=ax) + plot_1d_equilbrium_quantity(ax, x, eq['profiles_1d']['j_tor']/1.e6, + xName, r"$\langle j_\mathrm{tor} / R \rangle$ [MA m$^{-2}$]", + r"$j_\mathrm{tor}$", + visible_x=omas_viewer, **kw) + except ValueError: + print("WARNING j_tor not yet implemtented.") else: ax = cached_add_subplot(fig, axs, 2, 3, 5, sharex=ax) + plot_1d_equilbrium_quantity(ax, x, eq['profiles_1d']['dpressure_dpsi'] * 1.e-3, - xName, r'$P\,^\prime$ [kPa Wb$^{-1}$]', - r"$P\,^\prime$ source function", - visible_x=True, **kw) + xName, r'$P\,^\prime$ [kPa Wb$^{-1}$]', + r"$P\,^\prime$ source function", visible_x=True, **kw) if raw_xName.endswith('norm'): ax.set_xlim([0, 1]) if omas_viewer: diff --git a/omas/utilities/omas_mds.py b/omas/utilities/omas_mds.py index 07a4cdfd..9bb577a2 100644 --- a/omas/utilities/omas_mds.py +++ b/omas/utilities/omas_mds.py @@ -4,9 +4,43 @@ __all__ = [ 'mdstree', - 'mdsvalue' + 'mdsvalue', + 'get_pulse_id' ] +def get_pulse_id(pulse, run_id=None): + """ + Converts the pulse number into a MDS+ run_id + + :param pulse: Regular shot number + + :param run_id: Extension that contains the pulse number. E.g."01". Should be of type string or None + + :return: Pulse id, i.e. shot number with run_id extension if available + """ + if run_id is None: + return pulse + else: + return int(str(pulse) + run_id) + +def check_for_pulse_id(pulse, treename, options_with_defaults): + """ + Checks if the tree has a run_id associated with it and returns the pulse id + + :param pulse: Regular shot number + + :param treename: Name of tree being loaded + + :param options_with_defaults: Dictionary with options for the current machine + + + """ + if 'EFIT' in treename: + return get_pulse_id(pulse, options_with_defaults["EFIT_run_id"]) + else: + return pulse + + _mds_connection_cache = {} # ===================