Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
103 changes: 49 additions & 54 deletions benchmarks/core_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
available_serializers,
available_slicing_backends,
create_dbentry,
factory,
hlis,
)

N_SLICES = 32
Expand All @@ -26,13 +24,13 @@ def fill_slices(core_profiles, times):
times: time values to fill a slice for
"""
core_profiles.ids_properties.homogeneous_time = 1 # HOMOGENEOUS
core_profiles.ids_properties.comment = "Generated for the IMAS-Python benchmark suite"
core_profiles.ids_properties.comment = (
"Generated for the IMAS-Python benchmark suite"
)
core_profiles.ids_properties.creation_date = datetime.date.today().isoformat()
core_profiles.code.name = "IMAS-Python ASV benchmark"
core_profiles.code.version = imas.__version__
core_profiles.code.repository = (
"https://github.com/iterorganization/IMAS-Python"
)
core_profiles.code.repository = "https://github.com/iterorganization/IMAS-Python"

core_profiles.time = np.array(times)
core_profiles.profiles_1d.resize(len(times))
Expand All @@ -50,13 +48,13 @@ def fill_slices(core_profiles, times):
profiles_1d.ion.resize(len(ions))
profiles_1d.neutral.resize(len(ions))
for i, ion in enumerate(ions):
if hasattr(profiles_1d.ion[i], 'label'):
if hasattr(profiles_1d.ion[i], "label"):
profiles_1d.ion[i].label = ion
profiles_1d.neutral[i].label = ion
if hasattr(profiles_1d.ion[i], 'name'):
if hasattr(profiles_1d.ion[i], "name"):
profiles_1d.ion[i].name = ion
profiles_1d.neutral[i].name = ion

# profiles_1d.ion[i].label = profiles_1d.neutral[i].label = ion
profiles_1d.ion[i].z_ion = 1.0
profiles_1d.ion[i].neutral_index = profiles_1d.neutral[i].ion_index = i + 1
Expand All @@ -70,31 +68,31 @@ def fill_slices(core_profiles, times):


class GetSlice:
params = [hlis, available_slicing_backends]
param_names = ["hli", "backend"]
params = [available_slicing_backends]
param_names = ["backend"]

def setup(self, hli, backend):
self.dbentry = create_dbentry(hli, backend)
core_profiles = factory[hli].core_profiles()
def setup(self, backend):
self.dbentry = create_dbentry(backend)
core_profiles = imas.IDSFactory().core_profiles()
fill_slices(core_profiles, TIME)
self.dbentry.put(core_profiles)

def time_get_slice(self, hli, backend):
def time_get_slice(self, backend):
for t in TIME:
self.dbentry.get_slice("core_profiles", t, imas.ids_defs.CLOSEST_INTERP)

def teardown(self, hli, backend):
def teardown(self, backend):
if hasattr(self, "dbentry"): # imas + netCDF has no dbentry
self.dbentry.close()


class Get:
params = [hlis, available_backends]
param_names = ["hli", "backend"]
params = [available_backends]
param_names = ["backend"]
setup = GetSlice.setup
teardown = GetSlice.teardown

def time_get(self, hli, backend):
def time_get(self, backend):
self.dbentry.get("core_profiles")


Expand All @@ -103,8 +101,8 @@ class LazyGet:
param_names = ["lazy", "backend"]

def setup(self, lazy, backend):
self.dbentry = create_dbentry("imas", backend)
core_profiles = factory["imas"].core_profiles()
self.dbentry = create_dbentry(backend)
core_profiles = imas.IDSFactory().core_profiles()
fill_slices(core_profiles, TIME)
self.dbentry.put(core_profiles)

Expand All @@ -118,75 +116,72 @@ def teardown(self, lazy, backend):


class Generate:
params = [hlis]
param_names = ["hli"]
def setup(self):
self.core_profiles = imas.IDSFactory().core_profiles()

def setup(self, hli):
self.core_profiles = factory[hli].core_profiles()

def time_generate(self, hli):
def time_generate(self):
fill_slices(self.core_profiles, TIME)

def time_generate_slices(self, hli):
def time_generate_slices(self):
for t in TIME:
fill_slices(self.core_profiles, [t])

def time_create_core_profiles(self, hli):
factory[hli].core_profiles()
def time_create_core_profiles(self):
imas.IDSFactory().core_profiles()


class Put:
params = [["0", "1"], hlis, available_backends]
params = [["0", "1"], available_backends]
param_names = ["disable_validate", "hli", "backend"]

def setup(self, disable_validate, hli, backend):
create_dbentry(hli, backend).close() # catch unsupported combinations
self.core_profiles = factory[hli].core_profiles()
def setup(self, disable_validate, backend):
create_dbentry(backend).close() # catch unsupported combinations
self.core_profiles = imas.IDSFactory().core_profiles()
fill_slices(self.core_profiles, TIME)
os.environ["IMAS_AL_DISABLE_VALIDATE"] = disable_validate

def time_put(self, disable_validate, hli, backend):
with create_dbentry(hli, backend) as dbentry:
def time_put(self, disable_validate, backend):
with create_dbentry(backend) as dbentry:
dbentry.put(self.core_profiles)


class PutSlice:
params = [["0", "1"], hlis, available_slicing_backends]
param_names = ["disable_validate", "hli", "backend"]
params = [["0", "1"], available_slicing_backends]
param_names = ["disable_validate", "backend"]

def setup(self, disable_validate, hli, backend):
create_dbentry(hli, backend).close() # catch unsupported combinations
self.core_profiles = factory[hli].core_profiles()
def setup(self, disable_validate, backend):
create_dbentry(backend).close() # catch unsupported combinations
self.core_profiles = imas.IDSFactory().core_profiles()
os.environ["IMAS_AL_DISABLE_VALIDATE"] = disable_validate

def time_put_slice(self, disable_validate, hli, backend):
with create_dbentry(hli, backend) as dbentry:
def time_put_slice(self, disable_validate, backend):
with create_dbentry(backend) as dbentry:
for t in TIME:
fill_slices(self.core_profiles, [t])
dbentry.put_slice(self.core_profiles)


class Serialize:
params = [hlis, available_serializers]
param_names = ["hli", "serializer"]
params = [available_serializers]
param_names = ["serializer"]

def setup(self, hli, serializer):
self.core_profiles = factory[hli].core_profiles()
def setup(self, serializer):
self.core_profiles = imas.IDSFactory().core_profiles()
fill_slices(self.core_profiles, TIME)

def time_serialize(self, hli, serializer):
def time_serialize(self, serializer):
self.core_profiles.serialize(serializer)


class Deserialize:
params = [hlis, available_serializers]
param_names = ["hli", "serializer"]
params = [available_serializers]
param_names = ["serializer"]

def setup(self, hli, serializer):
self.core_profiles = factory[hli].core_profiles()
def setup(self, serializer):
self.core_profiles = imas.IDSFactory().core_profiles()
fill_slices(self.core_profiles, TIME)
self.data = self.core_profiles.serialize(serializer)
self.core_profiles = factory[hli].core_profiles()
self.core_profiles = imas.IDSFactory().core_profiles()

def time_deserialize(self, hli, serializer):
def time_deserialize(self, serializer):
self.core_profiles.deserialize(self.data)
45 changes: 20 additions & 25 deletions benchmarks/edge_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import imas

from .utils import available_backends, create_dbentry, factory, hlis
from .utils import available_backends, create_dbentry

N_POINTS = 600 # number of random R,Z points
N_LINES = 1200 # number of random lines in R,Z plane
Expand All @@ -27,9 +27,7 @@ def fill_ggd(edge_profiles, times):
edge_profiles.ids_properties.creation_date = datetime.date.today().isoformat()
edge_profiles.code.name = "IMAS-Python ASV benchmark"
edge_profiles.code.version = imas.__version__
edge_profiles.code.repository = (
"https://github.com/iterorganization/IMAS-Python"
)
edge_profiles.code.repository = "https://github.com/iterorganization/IMAS-Python"

# This GGD grid is not a valid description, but it's a good stress test for the
# typical access patterns that exist in GGD grids
Expand Down Expand Up @@ -124,45 +122,42 @@ def fill_ggd(edge_profiles, times):


class Get:
params = [hlis, available_backends]
param_names = ["hli", "backend"]
params = [available_backends]
param_names = ["backend"]

def setup(self, hli, backend):
self.dbentry = create_dbentry(hli, backend)
edge_profiles = factory[hli].edge_profiles()
def setup(self, backend):
self.dbentry = create_dbentry(backend)
edge_profiles = imas.IDSFactory().edge_profiles()
fill_ggd(edge_profiles, TIME)
self.dbentry.put(edge_profiles)

def time_get(self, hli, backend):
def time_get(self, backend):
self.dbentry.get("edge_profiles")

def teardown(self, hli, backend):
def teardown(self, backend):
if hasattr(self, "dbentry"): # imas + netCDF has no dbentry
self.dbentry.close()


class Generate:
params = [hlis]
param_names = ["hli"]

def time_generate(self, hli):
edge_profiles = factory[hli].edge_profiles()
def time_generate(self):
edge_profiles = imas.IDSFactory().edge_profiles()
fill_ggd(edge_profiles, TIME)

def time_create_edge_profiles(self, hli):
factory[hli].edge_profiles()
def time_create_edge_profiles(self):
imas.IDSFactory().edge_profiles()


class Put:
params = [["0", "1"], hlis, available_backends]
param_names = ["disable_validate", "hli", "backend"]
params = [["0", "1"], available_backends]
param_names = ["disable_validate", "backend"]

def setup(self, disable_validate, hli, backend):
create_dbentry(hli, backend).close() # catch unsupported combinations
self.edge_profiles = factory[hli].edge_profiles()
def setup(self, disable_validate, backend):
create_dbentry(backend).close() # catch unsupported combinations
self.edge_profiles = imas.IDSFactory().edge_profiles()
fill_ggd(self.edge_profiles, TIME)
os.environ["IMAS_AL_DISABLE_VALIDATE"] = disable_validate

def time_put(self, disable_validate, hli, backend):
with create_dbentry(hli, backend) as dbentry:
def time_put(self, disable_validate, backend):
with create_dbentry(backend) as dbentry:
dbentry.put(self.edge_profiles)
32 changes: 8 additions & 24 deletions benchmarks/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import importlib
import logging
import uuid
from pathlib import Path

import imas
import imas.exception
import imas.ids_defs

# Backend constants
HDF5 = "HDF5"
Expand Down Expand Up @@ -56,28 +56,12 @@ def backend_exists(backend):
backend for backend in available_backends if backend not in [ASCII, NETCDF]
]

hlis = ["imas"]
DBEntry = {
"imas": imas.DBEntry,
}
factory = {
"imas": imas.IDSFactory(),
}
available_serializers = [imas.ids_defs.ASCII_SERIALIZER_PROTOCOL]

available_serializers = [
imas.ids_defs.ASCII_SERIALIZER_PROTOCOL,
imas.ids_defs.FLEXBUFFERS_SERIALIZER_PROTOCOL,
]

def create_dbentry(hli, backend):
if backend == NETCDF:
if hli == "imas": # check if netcdf backend is available
try:
assert (
imas.DBEntry._select_implementation("x.nc").__name__
== "NCDBEntryImpl"
)
except (AttributeError, AssertionError):
raise NotImplementedError(
"This version of IMAS-Python doesn't implement netCDF."
) from None

path = Path.cwd() / f"DB-{hli}-{backend}"
return DBEntry[hli](create_uri(backend, path), "w")
def create_dbentry(backend):
path = Path.cwd() / f"DB-{backend}"
return imas.DBEntry(create_uri(backend, path), "w")
13 changes: 4 additions & 9 deletions docs/source/courses/basic/analyze.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,10 @@ can use the data.
.. hint::
Use the ASCII data supplied with IMAS-Python for all exercises. It contains two
IDSs (``equilibrium`` and ``core_profiles``) filled with data from three
time slices of ITER reference data. Two convenience methods are available in the
:mod:`imas.training` module to open the DBEntry for this training data.

1. :meth:`imas.training.get_training_db_entry()` returns an opened
``imas.DBEntry`` object. Use this method if you want to use the IMAS-Python
interface.
2. :meth:`imas.training.get_training_imas_db_entry()` returns an opened
``imas.DBEntry`` object. Use this method if you want to use the Python Access
Layer interface.
time slices of ITER reference data. A convenience method is available in the
:mod:`imas.training` module to open the DBEntry for this training data:
:meth:`imas.training.get_training_db_entry()` returns an opened
``imas.DBEntry`` object.

Exercise 1
''''''''''
Expand Down
11 changes: 10 additions & 1 deletion docs/source/lazy_loading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,13 @@ Lazy loading of data may speed up your programs, but also comes with some limita
more efficient to do a full :code:`get()` or :code:`get_slice()` when you intend to
use most of the data stored in an IDS.
5. When using IMAS-Python with remote data access (i.e. the UDA backend), a full
:code:`get()` or :code:`get_slice()` is more efficient than lazy loading.
:code:`get()` or :code:`get_slice()` may be more efficient than using lazy loading.

It is recommended to add the parameter ``;cache_mode=none`` [#cache_mode_none]_ to
the end of a UDA IMAS URI when using lazy loading: otherwise the UDA backend will
still load the full IDS from the remote server.


.. [#cache_mode_none] The option ``cache_mode=none`` requires IMAS Core version 5.5.1 or
newer, and a remote UDA server with `IMAS UDA-Plugins
<https://github.com/iterorganization/UDA-Plugins>`__ version 1.7.0 or newer.
Comment on lines +100 to +102
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@olivhoenen @deepakmaroo can you please check if my assumptions are valid?

  1. https://jira.iter.org/browse/IMAS-5644 will be part of IMAS Core version 5.5.1 (since 5.5.0 is released, and that still has issues).
  2. Since cache_mode=none only works for the test UDA server (and not the production one) I assumed that the next UDA plugins release is required.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You are correct @maarten-ic , IMAS core v5.5.1 will be release for UDA backend fixed and UDA-Plugins v1.7.0 will be release for complex number support. Documentation looks good to me.

5 changes: 1 addition & 4 deletions imas/backends/imas_core/al_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,7 @@ def global_action(self, path: str, rwmode: int, datapath: str = "") -> "ALContex
Returns:
The created context.
"""
args = [self.ctx, path, rwmode]
if datapath: # AL4 compatibility: datapath arg was added in AL5
args.append(datapath)
status, ctx = ll_interface.begin_global_action(*args)
status, ctx = ll_interface.begin_global_action(self.ctx, path, rwmode, datapath)
if status != 0:
raise LowlevelError("global_action", status)
return ALContext(ctx)
Expand Down
Loading
Loading