Skip to content
Merged
114 changes: 86 additions & 28 deletions tofu/geom/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
__all__ = ['coords_transform',
'get_nIne1e2', 'get_X12fromflat',
'compute_RaysCones',
'create_config',
'get_available_config', 'create_config',
'create_CamLOS1D', 'create_CamLOS2D']


Expand Down Expand Up @@ -643,8 +643,16 @@ def _compute_CamLOS2D_pinhole(P=None, F=0.1, D12=0.1, N12=100,
_ExpJET = 'JET'
_ExpITER = 'ITER'
_ExpNSTX = 'NSTX'
# Default config
_DEFCONFIG = 'ITER'

_URL_TUTO = ('https://tofuproject.github.io/tofu/auto_examples/tutorials/'
+ 'tuto_plot_create_geometry.html')

# Dictionnary of unique config names
# For each config, indicates which structural elements it comprises
# Elements are sorted by class (Ves, PFC...)
# For each element, a unique txt file containing the geometry will be loaded
_DCONFIG = {'WEST-V1': {'Exp': _ExpWest,
'Ves': ['V1']},
'ITER-V1': {'Exp': _ExpITER,
Expand Down Expand Up @@ -680,28 +688,78 @@ def _compute_CamLOS2D_pinhole(P=None, F=0.1, D12=0.1, N12=100,
'Ves': ['V0']}
}

# Each config can be called by various names (for benchmark and
# retro-compatibility), this table stores the available names for each unique
# config in _DCONFIG
_DCONFIG_TABLE = {'ITER': 'ITER-V2',
'JET': 'JET-V0',
'WEST': 'WEST-V4',
'A1': 'WEST-V1',
'A2': 'ITER-V1',
'A3': 'WEST-Sep',
'B1': 'WEST-V2',
'B2': 'WEST-V3',
'B3': 'WEST-V4',
'B4': 'ITER-V2',
'NSTX': 'NSTX-V0'}
# Each config can be called by various names / shortcuts (for benchmark and
# retro-compatibility), this table stores, for each shortcut,
# the associated unique name it refers to
_DCONFIG_SHORTCUTS = {'ITER': 'ITER-V2',
'JET': 'JET-V0',
'WEST': 'WEST-V4',
'A1': 'WEST-V1',
'A2': 'ITER-V1',
'A3': 'WEST-Sep',
'B1': 'WEST-V2',
'B2': 'WEST-V3',
'B3': 'WEST-V4',
'B4': 'ITER-V2',
'NSTX': 'NSTX-V0'}


def _get_listconfig(dconfig=_DCONFIG, dconfig_shortcuts=_DCONFIG_SHORTCUTS,
returnas=str):
""" Hidden function generating the config names table as a str or dict """
assert returnas in [dict, str]
dc = {k0: [k0] + sorted([k1 for k1, v1 in dconfig_shortcuts.items()
if v1 == k0])
for k0 in sorted(dconfig.keys())}
if returnas is dict:
return dc
else:
l0 = np.max([len(k0) for k0 in dc.keys()] + [len('unique names')])
l1 = np.max([len(str(v0)) for v0 in dc.values()] + [len('shortcuts')])
msg = ("\n\t" + "unique names".ljust(l0) + "\tshortcuts"
+ "\n\t" + "-"*l0 + " \t" + "-"*l1
+ "\n\t- "
+ "\n\t- ".join(["{}\t{}".format(k0.ljust(l0), v0)
for k0, v0 in dc.items()]))
return msg

# Default config
_DEFCONFIG = 'ITER'

def get_available_config(dconfig=_DCONFIG,
dconfig_shortcuts=_DCONFIG_SHORTCUTS,
verb=True, returnas=False):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is returnas a bool here? Shouldn't it be str?
Below there's a check if returnas is str...
What do you think?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, in general in many tofu functions / methods, returnas can be:

  • False => nothing is returned
  • None / True => set to default return format
  • a specific format (here str)

It is because in this function there is only one output possible format, but that may evolve in the future.
The use of True / None is to specify we want output but either the user doesn't know that he has choice between several formats (in that case it is usually set to the most intuitive) or to let the format be decided later by another function.

I agree in this case we just have a choice between False (default) and str, but this way we keep the general workflow / convention

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not implement True / None because I forgot though,
But in 99 % cases users will not ask for returning anything

""" Print a table showing all pre-defined config

Each pre-defined config in tofu can be called by its unique name or
by a series of shortcuts / alterantive names refereing to the same unique
name. this feature is useful for retro-compatibility and for making sure a
standard name always refers to the latest (most detailed) available version
of the geometry.

Can also return the table as str

No input arg needed:
>>> import tofu as tf
>>> tf.geom.utils.get_available_config()

"""
msg = ("A config is the geometry of a tokamak\n"
+ "You can define your own"
+ ", see online tutorial at:\n\t{}\n".format(_URL_TUTO)
+ "tofu also also provides some pre-defined config ready to load\n"
+ "They are available via their name or via shortcuts\n"
+ _get_listconfig(dconfig=dconfig,
dconfig_shortcuts=dconfig_shortcuts)
+ "\n\n => to get a pre-defined config, call for example:\n"
+ "\tconfig = tf.geom.utils.create_config('ITER')")
if verb is True:
print(msg)
if returnas in [None, True, str]:
return msg


def _create_config_testcase(config=None, returnas='object',
path=_path_testcases, dconfig=_DCONFIG,
dconfig_table=_DCONFIG_TABLE):
dconfig_shortcuts=_DCONFIG_SHORTCUTS):
""" Load the desired test case configuration

Choose from one of the reference preset configurations:
Expand All @@ -719,16 +777,14 @@ def _create_config_testcase(config=None, returnas='object',
if config in dconfig.keys():
pass

elif config in dconfig_table.keys():
elif config in dconfig_shortcuts.keys():
# Get corresponding config
config = dconfig_table[config]
config = dconfig_shortcuts[config]

else:
msg = ("The provided config name is not valid:\n"
+ "Please choose among either:\n"
+ "\t - unique keys: {}\n".format(list(dconfig.keys()))
+ "\t - shortcuts : {}\n\n".format(list(dconfig_table.keys()))
+ " => you provided: case = {}\n".format(config))
msg = ("\nThe provided config name is not valid.\n"
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message re-uses the formatted message created by the hidden method

+ get_available_config(verb=False, returnas=str)
+ "\n\n => you provided: {}\n".format(config))
raise Exception(msg)

# Get file names for config
Expand Down Expand Up @@ -823,11 +879,13 @@ def create_config(case=None, Exp='Dummy', Type='Tor',
any([pp is not None for pp in lp])]
if np.sum(lc) > 1:
msg = ("Please provide either:\n"
+ "\t- case: the name of a stored case\n"
+ "\t- geometrical parameters {}".format(lpstr))
+ "\t- case: the name of a pre-defined config\n"
+ "\t- geometrical parameters {}\n\n".format(lpstr))
raise Exception(msg)
elif not any(lc):
case = _DEFCONFIG
msg = get_available_config(verb=False, returnas=str)
raise Exception(msg)
# case = _DEFCONFIG

# Get config, either from known case or geometrical parameterization
if case is not None:
Expand Down
2 changes: 1 addition & 1 deletion tofu/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Do not edit, pipeline versioning governed by git tags!
__version__ = '1.4.2-a5-91-g5e9b601a'
__version__ = '1.4.2-a5-71-g56f30784'