|
7 | 7 | import logging
|
8 | 8 | from functools import lru_cache, partial
|
9 | 9 | from pathlib import Path
|
10 |
| -from typing import Callable, Dict, Iterator, Optional, Set, Tuple |
| 10 | +from typing import Callable, Dict, Iterator, List, Optional, Set, Tuple |
11 | 11 | from xml.etree.ElementTree import Element, ElementTree
|
12 | 12 |
|
13 | 13 | import numpy
|
@@ -87,6 +87,15 @@ def __init__(self) -> None:
|
87 | 87 | converted.
|
88 | 88 | """
|
89 | 89 |
|
| 90 | + self.post_process_ids: List[ |
| 91 | + Callable[[IDSToplevel, IDSToplevel, bool], None] |
| 92 | + ] = [] |
| 93 | + """Postprocess functions to be applied to the whole IDS. |
| 94 | +
|
| 95 | + These postprocess functions should be applied to the whole IDS after all data is |
| 96 | + converted. The arguments supplied are: source IDS, target IDS, deepcopy boolean. |
| 97 | + """ |
| 98 | + |
90 | 99 | self.ignore_missing_paths: Set[str] = set()
|
91 | 100 | """Set of paths that should not be logged when data is present."""
|
92 | 101 |
|
@@ -343,6 +352,13 @@ def _apply_3to4_conversion(self, old: Element, new: Element) -> None:
|
343 | 352 | new_path = self.old_to_new.path.get(old_path, old_path)
|
344 | 353 | self.new_to_old.post_process[new_path] = _cocos_change
|
345 | 354 | self.old_to_new.post_process[old_path] = _cocos_change
|
| 355 | + # Convert equilibrium boundary_separatrix and populate contour_tree |
| 356 | + if self.ids_name == "equilibrium": |
| 357 | + self.old_to_new.post_process_ids.append(_equilibrium_boundary_3to4) |
| 358 | + self.old_to_new.ignore_missing_paths |= { |
| 359 | + "time_slice/boundary_separatrix", |
| 360 | + "time_slice/boundary_secondary_separatrix", |
| 361 | + } |
346 | 362 | # Definition change for pf_active circuit/connections
|
347 | 363 | if self.ids_name == "pf_active":
|
348 | 364 | path = "circuit/connections"
|
@@ -544,6 +560,10 @@ def convert_ids(
|
544 | 560 | else:
|
545 | 561 | _copy_structure(toplevel, target, deepcopy, rename_map)
|
546 | 562 |
|
| 563 | + # Global post-processing functions |
| 564 | + for callback in rename_map.post_process_ids: |
| 565 | + callback(toplevel, target, deepcopy) |
| 566 | + |
547 | 567 | logger.info("Conversion of IDS %s finished.", ids_name)
|
548 | 568 | if provenance_origin_uri:
|
549 | 569 | _add_provenance_entry(target, toplevel._version, provenance_origin_uri)
|
@@ -1063,3 +1083,51 @@ def _pulse_schedule_resample_callback(timebase, item: IDSBase, target_item: IDSB
|
1063 | 1083 | assume_sorted=True,
|
1064 | 1084 | )(timebase)
|
1065 | 1085 | target_item.value = value.astype(numpy.int32) if is_integer else value
|
| 1086 | + |
| 1087 | + |
| 1088 | +def _equilibrium_boundary_3to4(eq3: IDSToplevel, eq4: IDSToplevel, deepcopy: bool): |
| 1089 | + """Convert DD3 boundary[[_secondary]_separatrix] to DD4 contour_tree""" |
| 1090 | + # Implement https://github.com/iterorganization/IMAS-Python/issues/60 |
| 1091 | + copy = numpy.copy if deepcopy else lambda x: x |
| 1092 | + for ts3, ts4 in zip(eq3.time_slice, eq4.time_slice): |
| 1093 | + n_nodes = 1 # magnetic axis |
| 1094 | + if ts3.boundary_separatrix.psi.has_value: |
| 1095 | + n_nodes = 2 |
| 1096 | + if ( # boundary_secondary_separatrix is introduced in DD 3.32.0 |
| 1097 | + hasattr(ts3, "boundary_secondary_separatrix") |
| 1098 | + and ts3.boundary_secondary_separatrix.psi.has_value |
| 1099 | + ): |
| 1100 | + n_nodes = 3 |
| 1101 | + ts4.contour_tree.node.resize(n_nodes) |
| 1102 | + # Magnetic axis (primary O-point) |
| 1103 | + node = ts4.contour_tree.node |
| 1104 | + node[0].critical_type = 0 # minimum (?) |
| 1105 | + node[0].r = ts3.global_quantities.magnetic_axis.r |
| 1106 | + node[0].z = ts3.global_quantities.magnetic_axis.z |
| 1107 | + node[0].psi = -ts3.global_quantities.psi_axis # COCOS change |
| 1108 | + |
| 1109 | + # X-points |
| 1110 | + if n_nodes >= 2: |
| 1111 | + if ts3.boundary_separatrix.type == 0: # limiter plasma |
| 1112 | + node[1].critical_type = 2 # maximum (?) |
| 1113 | + node[1].r = ts3.boundary_separatrix.active_limiter_point.r |
| 1114 | + node[1].z = ts3.boundary_separatrix.active_limiter_point.z |
| 1115 | + else: |
| 1116 | + node[1].critical_type = 1 # saddle-point (x-point) |
| 1117 | + if len(ts3.boundary_separatrix.x_point): |
| 1118 | + node[1].r = ts3.boundary_separatrix.x_point[0].r |
| 1119 | + node[1].z = ts3.boundary_separatrix.x_point[0].z |
| 1120 | + # TODO: what if there are multiple x-points? |
| 1121 | + node[1].psi = -ts3.boundary_separatrix.psi # COCOS change |
| 1122 | + node[1].levelset.r = copy(ts3.boundary_separatrix.outline.r) |
| 1123 | + node[1].levelset.z = copy(ts3.boundary_separatrix.outline.z) |
| 1124 | + |
| 1125 | + if n_nodes >= 3: |
| 1126 | + node[2].critical_type = 1 # saddle-point (x-point) |
| 1127 | + if len(ts3.boundary_secondary_separatrix.x_point): |
| 1128 | + node[2].r = ts3.boundary_secondary_separatrix.x_point[0].r |
| 1129 | + node[2].z = ts3.boundary_secondary_separatrix.x_point[0].z |
| 1130 | + # TODO: what if there are multiple x-points? |
| 1131 | + node[2].psi = -ts3.boundary_secondary_separatrix.psi # COCOS change |
| 1132 | + node[2].levelset.r = copy(ts3.boundary_secondary_separatrix.outline.r) |
| 1133 | + node[2].levelset.z = copy(ts3.boundary_secondary_separatrix.outline.z) |
0 commit comments