diff --git a/other/materials_designer/create_adatom_defect.ipynb b/other/materials_designer/create_adatom_defect.ipynb index 663cf617..98fbe122 100644 --- a/other/materials_designer/create_adatom_defect.ipynb +++ b/other/materials_designer/create_adatom_defect.ipynb @@ -95,8 +95,8 @@ { "cell_type": "markdown", "source": [ - "### 1.3. Get input material and \n", - "Materials are loaded with `get_data()`. The first material is assigned as substrate and the second as film." + "### 1.3. Get input material\n", + "Materials are loaded with `get_materials()`." ], "metadata": { "collapsed": false @@ -107,12 +107,8 @@ "cell_type": "code", "outputs": [], "source": [ - "from mat3ra.made.material import Material\n", - "from utils.jupyterlite import get_data\n", - "\n", - "# Get the list of input materials and load them into `materials_in` variable\n", - "get_data(\"materials_in\", globals())\n", - "materials = list(map(Material, globals()[\"materials_in\"]))" + "from utils.jupyterlite import get_materials\n", + "materials = get_materials(globals())" ], "metadata": { "collapsed": false @@ -258,9 +254,9 @@ "cell_type": "code", "outputs": [], "source": [ - "from utils.jupyterlite import set_data\n", + "from utils.jupyterlite import set_materials\n", "\n", - "set_data(\"materials\", [slab_with_adatom_at_equidistant_position.to_json(), slab_with_adatom_at_crystal_site.to_json()])" + "set_materials([slab_with_adatom_at_equidistant_position, slab_with_adatom_at_crystal_site])" ], "metadata": { "collapsed": false diff --git a/other/materials_designer/create_interface_with_min_strain_zsl.ipynb b/other/materials_designer/create_interface_with_min_strain_zsl.ipynb index a4afb821..54d1f57d 100644 --- a/other/materials_designer/create_interface_with_min_strain_zsl.ipynb +++ b/other/materials_designer/create_interface_with_min_strain_zsl.ipynb @@ -50,8 +50,27 @@ "source": [ "# Enable interactive selection of terminations via UI prompt\n", "IS_TERMINATIONS_SELECTION_INTERACTIVE = False \n", + "\n", + "FILM_INDEX = 1 # Index in the list of materials, to access as materials[FILM_INDEX]\n", + "FILM_MILLER_INDICES = (0, 0, 1)\n", + "FILM_THICKNESS = 1 # in atomic layers\n", + "FILM_VACUUM = 0.0 # in angstroms\n", + "FILM_XY_SUPERCELL_MATRIX = [[1, 0], [0, 1]]\n", + "FILM_USE_ORTHOGONAL_Z = True\n", + "\n", + "SUBSTRATE_INDEX = 0\n", + "SUBSTRATE_MILLER_INDICES = (1, 1, 1)\n", + "SUBSTRATE_THICKNESS = 3 # in atomic layers\n", + "SUBSTRATE_VACUUM = 3.0 # in angstroms\n", + "SUBSTRATE_XY_SUPERCELL_MATRIX = [[1, 0], [0, 1]]\n", + "SUBSTRATE_USE_ORTHOGONAL_Z = True\n", + "\n", "# Maximum area for the superlattice search algorithm\n", - "MAX_AREA = 50" + "MAX_AREA = 50 # in Angstrom^2\n", + "# Set the termination pair indices\n", + "TERMINATION_PAIR_INDEX = 0 # Will be overridden in interactive selection is used\n", + "INTERFACE_DISTANCE = 3.0 # in Angstrom\n", + "INTERFACE_VACUUM = 20.0 # in Angstrom" ], "metadata": { "collapsed": false @@ -76,6 +95,7 @@ "\n", "if sys.platform == \"emscripten\":\n", " import micropip\n", + " \n", " await micropip.install('mat3ra-api-examples', deps=False)\n", " from utils.jupyterlite import install_packages\n", " await install_packages(\"create_interface_with_min_strain_zsl.ipynb\", \"../../config.yml\")" @@ -97,14 +117,15 @@ "cell_type": "code", "outputs": [], "source": [ - "from mat3ra.made.material import Material\n", - "from utils.jupyterlite import get_data\n", + "from utils.jupyterlite import get_materials\n", "\n", - "# Get the list of input materials and load them into `materials_in` variable\n", - "get_data(\"materials_in\", globals())\n", - "materials = list(map(Material, globals()[\"materials_in\"]))\n", - "substrate = materials[0]\n", - "film = materials[1]" + "materials = get_materials(globals())\n", + "substrate = materials[SUBSTRATE_INDEX]\n", + "try: \n", + " film = materials[FILM_INDEX]\n", + "except IndexError:\n", + " print(\"Film material not found. Re-using substrate material as film.\")\n", + " film = substrate" ], "metadata": { "collapsed": false @@ -151,20 +172,20 @@ "\n", "film_slab_configuration = SlabConfiguration(\n", " bulk=film,\n", - " miller_indices=(0, 0, 1),\n", - " thickness=1, # in atomic layers\n", - " vacuum=0, # in atomic layers\n", - " xy_supercell_matrix=[[1, 0], [0, 1]],\n", - " use_orthogonal_z=True\n", + " miller_indices=FILM_MILLER_INDICES,\n", + " thickness=FILM_THICKNESS, # in atomic layers\n", + " vacuum=FILM_VACUUM, # in angstroms\n", + " xy_supercell_matrix=FILM_XY_SUPERCELL_MATRIX,\n", + " use_orthogonal_z=FILM_USE_ORTHOGONAL_Z\n", ")\n", "\n", "substrate_slab_configuration = SlabConfiguration(\n", " bulk=substrate,\n", - " miller_indices=(1,1,1),\n", - " thickness=3, # in atomic layers\n", - " vacuum=3, # in atomic layers\n", - " xy_supercell_matrix=[[1, 0], [0, 1]],\n", - " use_orthogonal_z=True\n", + " miller_indices=SUBSTRATE_MILLER_INDICES,\n", + " thickness=SUBSTRATE_THICKNESS, # in atomic layers\n", + " vacuum=SUBSTRATE_VACUUM, # in angstroms\n", + " xy_supercell_matrix=SUBSTRATE_XY_SUPERCELL_MATRIX,\n", + " use_orthogonal_z=SUBSTRATE_USE_ORTHOGONAL_Z\n", ")" ], "metadata": {}, @@ -255,10 +276,9 @@ "source": [ "from utils.io import ui_prompt_select_array_element_by_index, ui_prompt_select_array_element_by_index_pyodide\n", "\n", - "# Set the termination pair indices\n", - "TERMINATION_PAIR_INDEX = 0\n", + "termination_pair_index = TERMINATION_PAIR_INDEX\n", "\n", - "termination_pair = termination_pairs[TERMINATION_PAIR_INDEX]\n", + "termination_pair = termination_pairs[termination_pair_index]\n", "if IS_TERMINATIONS_SELECTION_INTERACTIVE:\n", " if sys.platform == \"emscripten\":\n", " termination_pair = await ui_prompt_select_array_element_by_index_pyodide(termination_pairs, element_name=\"film/substrate termination pair\")\n", @@ -293,8 +313,8 @@ " substrate_configuration=substrate_slab_configuration,\n", " film_termination=film_termination,\n", " substrate_termination=substrate_termination,\n", - " distance=3.0, # in Angstrom\n", - " vacuum=20.0 # in Angstrom\n", + " distance=INTERFACE_DISTANCE,\n", + " vacuum= INTERFACE_VACUUM\n", ")" ], "metadata": { @@ -437,10 +457,8 @@ "metadata": {}, "outputs": [], "source": [ - "from utils.jupyterlite import set_data\n", - "\n", - "materials_as_json = [selected_interface.to_json() for selected_interface in selected_interfaces]\n", - "set_data(\"materials\", materials_as_json)" + "from utils.jupyterlite import set_materials\n", + "set_materials(selected_interfaces)" ] } ], diff --git a/other/materials_designer/create_interface_with_no_strain_matching.ipynb b/other/materials_designer/create_interface_with_no_strain_matching.ipynb index 10c81394..4a2940c4 100644 --- a/other/materials_designer/create_interface_with_no_strain_matching.ipynb +++ b/other/materials_designer/create_interface_with_no_strain_matching.ipynb @@ -43,7 +43,26 @@ "# Enable interactive selection of terminations via UI prompt\n", "IS_TERMINATIONS_SELECTION_INTERACTIVE = False \n", "# Enable scaling of the film slab atomic coordinates to match the substrate lattice (preserve coordinates in crystal units).\n", - "ENABLE_FILM_SCALING = True" + "ENABLE_FILM_SCALING = True\n", + "\n", + "FILM_INDEX = 1\n", + "FILM_MILLER_INDICES = (0, 0, 1)\n", + "FILM_THICKNESS = 1 # in atomic layers\n", + "FILM_VACUUM = 0.0 # in angstroms\n", + "FILM_XY_SUPERCELL_MATRIX = [[1, 0], [0, 1]]\n", + "FILM_USE_ORTHOGONAL_Z = True\n", + "\n", + "SUBSTRATE_INDEX = 0\n", + "SUBSTRATE_MILLER_INDICES = (1, 1, 1)\n", + "SUBSTRATE_THICKNESS = 3 # in atomic layers\n", + "SUBSTRATE_VACUUM = 3.0 # in angstroms\n", + "SUBSTRATE_XY_SUPERCELL_MATRIX = [[1, 0], [0, 1]]\n", + "SUBSTRATE_USE_ORTHOGONAL_Z = True\n", + "\n", + "# Set the termination pair indices\n", + "TERMINATION_PAIR_INDEX = 0 # Will be overridden in interactive selection is used\n", + "INTERFACE_DISTANCE = 3.0 # in Angstrom\n", + "INTERFACE_VACUUM = 20.0 # in Angstrom" ], "metadata": { "collapsed": false @@ -92,14 +111,15 @@ "cell_type": "code", "outputs": [], "source": [ - "from mat3ra.made.material import Material\n", - "from utils.jupyterlite import get_data\n", + "from utils.jupyterlite import get_materials\n", "\n", - "# Get the list of input materials and load them into `materials_in` variable\n", - "get_data(\"materials_in\", globals())\n", - "materials = list(map(Material, globals()[\"materials_in\"]))\n", - "substrate = materials[0]\n", - "film = materials[1]" + "materials = get_materials(globals())\n", + "substrate = materials[SUBSTRATE_INDEX]\n", + "try: \n", + " film = materials[FILM_INDEX]\n", + "except IndexError:\n", + " print(\"Please select a film material. Film is set to substrate.\")\n", + " film = substrate" ], "metadata": { "collapsed": false @@ -148,20 +168,20 @@ "\n", "film_slab_configuration = SlabConfiguration(\n", " bulk=film,\n", - " miller_indices=(0, 0, 1),\n", - " thickness=1, # in atomic layers\n", - " vacuum=0, # in atomic layers\n", - " xy_supercell_matrix=[[1, 0], [0, 1]],\n", - " use_orthogonal_z=True\n", + " miller_indices=FILM_MILLER_INDICES,\n", + " thickness=FILM_THICKNESS, # in atomic layers\n", + " vacuum=FILM_VACUUM, # in angstroms\n", + " xy_supercell_matrix=FILM_XY_SUPERCELL_MATRIX,\n", + " use_orthogonal_z=FILM_USE_ORTHOGONAL_Z\n", ")\n", "\n", "substrate_slab_configuration = SlabConfiguration(\n", " bulk=substrate,\n", - " miller_indices=(1,1,1),\n", - " thickness=3, # in atomic layers\n", - " vacuum=3, # in atomic layers\n", - " xy_supercell_matrix=[[1, 0], [0, 1]],\n", - " use_orthogonal_z=True\n", + " miller_indices=SUBSTRATE_MILLER_INDICES,\n", + " thickness=SUBSTRATE_THICKNESS, # in atomic layers\n", + " vacuum=SUBSTRATE_VACUUM, # in angstroms\n", + " xy_supercell_matrix=SUBSTRATE_XY_SUPERCELL_MATRIX,\n", + " use_orthogonal_z=SUBSTRATE_USE_ORTHOGONAL_Z\n", ")" ], "metadata": { @@ -206,8 +226,8 @@ "film_slabs = [create_slab(film_slab_configuration, termination) for termination in film_slab_terminations]\n", "substrate_slabs = [create_slab(substrate_slab_configuration, termination) for termination in substrate_slab_terminations]\n", "\n", - "visualize([{\"material\":slab, \"title\": slab.metadata[\"termination\"]} for slab in film_slabs ], repetitions=[3, 3, 1], rotation=\"-90x\")\n", - "visualize([{\"material\":slab, \"title\": slab.metadata[\"termination\"]} for slab in substrate_slabs ], repetitions=[3, 3, 1], rotation=\"-90x\") " + "visualize([{\"material\":slab, \"title\": slab.metadata[\"build\"][\"termination\"]} for slab in film_slabs ], repetitions=[3, 3, 1], rotation=\"-90x\")\n", + "visualize([{\"material\":slab, \"title\": slab.metadata[\"build\"][\"termination\"]} for slab in substrate_slabs ], repetitions=[3, 3, 1], rotation=\"-90x\") " ], "metadata": { "collapsed": false @@ -255,10 +275,9 @@ "source": [ "from utils.io import ui_prompt_select_array_element_by_index, ui_prompt_select_array_element_by_index_pyodide\n", "\n", - "# Set the termination pair indices\n", - "TERMINATION_PAIR_INDEX = 0\n", + "termination_pair_index = TERMINATION_PAIR_INDEX\n", "\n", - "termination_pair = termination_pairs[TERMINATION_PAIR_INDEX]\n", + "termination_pair = termination_pairs[termination_pair_index]\n", "if IS_TERMINATIONS_SELECTION_INTERACTIVE:\n", " if sys.platform == \"emscripten\":\n", " termination_pair = await ui_prompt_select_array_element_by_index_pyodide(termination_pairs, element_name=\"film/substrate termination pair\")\n", @@ -293,6 +312,8 @@ " substrate_configuration=substrate_slab_configuration,\n", " film_termination=film_termination,\n", " substrate_termination=substrate_termination,\n", + " interface_distance=INTERFACE_DISTANCE, # in Angstrom\n", + " interface_vacuum=INTERFACE_VACUUM # in Angstrom\n", ")\n", "\n", "interface = create_interface(interface_configuration)" @@ -355,9 +376,8 @@ "metadata": {}, "outputs": [], "source": [ - "from utils.jupyterlite import set_data\n", - "\n", - "set_data(\"materials\", [interface.to_json()])" + "from utils.jupyterlite import set_materials\n", + "set_materials(interface)" ] } ], diff --git a/other/materials_designer/create_interface_with_relaxation_ase_emt.ipynb b/other/materials_designer/create_interface_with_relaxation_ase_emt.ipynb index b5a58f12..17a0a03e 100644 --- a/other/materials_designer/create_interface_with_relaxation_ase_emt.ipynb +++ b/other/materials_designer/create_interface_with_relaxation_ase_emt.ipynb @@ -93,13 +93,10 @@ "metadata": {}, "outputs": [], "source": [ - "from mat3ra.made.material import Material\n", - "from utils.jupyterlite import get_data\n", + "from utils.jupyterlite import get_materials\n", "\n", - "# Get the list of input materials and load them into `materials_in` variable\n", - "get_data(\"materials_in\", globals())\n", - "materials = list(map(Material, globals()[\"materials_in\"]))\n", - "interface = materials[2]" + "materials = get_materials(globals())\n", + "interface = materials[0]" ] }, { @@ -165,6 +162,7 @@ "source": [ "from utils.plot import create_realtime_plot, plot_update_callback\n", "from mat3ra.made.tools.convert import from_ase\n", + "from mat3ra.made.material import Material\n", "\n", "# Add calculator to the interface for relaxation\n", "ase_interface = to_ase(interface)\n", @@ -243,9 +241,9 @@ "cell_type": "code", "outputs": [], "source": [ - "from utils.jupyterlite import set_data\n", + "from utils.jupyterlite import set_materials\n", "final_interface.name = f\"{interface.name}, Relaxed with EMT\" if \"Relaxed\" not in interface.name else interface.name\n", - "set_data(\"materials\", [final_interface.to_json()])" + "set_materials(final_interface)" ], "metadata": { "collapsed": false diff --git a/other/materials_designer/create_nanoribbon.ipynb b/other/materials_designer/create_nanoribbon.ipynb index 9c5bcebf..ed372b59 100644 --- a/other/materials_designer/create_nanoribbon.ipynb +++ b/other/materials_designer/create_nanoribbon.ipynb @@ -89,7 +89,7 @@ "cell_type": "markdown", "source": [ "### 1.3. Get input materials\n", - "Materials are loaded with `get_data()`. The first material is assigned as substrate and the second as film." + "Materials are loaded with `get_materials()`." ], "metadata": { "collapsed": false @@ -100,12 +100,8 @@ "cell_type": "code", "outputs": [], "source": [ - "from mat3ra.made.material import Material\n", - "from utils.jupyterlite import get_data\n", - "\n", - "# Get the list of input materials and load them into `materials_in` variable\n", - "get_data(\"materials_in\", globals())\n", - "materials = list(map(Material, globals()[\"materials_in\"]))" + "from utils.jupyterlite import get_materials\n", + "materials = get_materials(globals())" ], "metadata": { "collapsed": false @@ -129,7 +125,7 @@ "source": [ "from utils.visualize import visualize_materials as visualize\n", "\n", - "material = materials[2]\n", + "material = materials[0]\n", "visualize(material, repetitions=[3, 3, 1], rotation=\"0x\")" ], "metadata": { @@ -232,9 +228,8 @@ "cell_type": "code", "outputs": [], "source": [ - "from utils.jupyterlite import set_data\n", - "\n", - "set_data(\"materials\", [nanoribbon.to_json()])" + "from utils.jupyterlite import set_materials\n", + "set_materials(nanoribbon)" ], "metadata": { "collapsed": false diff --git a/other/materials_designer/create_perturbation.ipynb b/other/materials_designer/create_perturbation.ipynb index d5045ed7..2bf77513 100644 --- a/other/materials_designer/create_perturbation.ipynb +++ b/other/materials_designer/create_perturbation.ipynb @@ -45,7 +45,7 @@ "cell_type": "code", "outputs": [], "source": [ - "SUPERCELL_MATRIX = [[5, 0, 0], [0, 5, 0], [0, 0, 1]] " + "SUPERCELL_MATRIX = [[30, 0, 0], [0, 30, 0], [0, 0, 1]] " ], "metadata": { "collapsed": false @@ -86,7 +86,7 @@ "cell_type": "markdown", "source": [ "### 1.3. Get input materials\n", - "Materials are loaded with `get_data()`. The first material is assigned as substrate and the second as film." + "Materials are loaded with `get_materials()`." ], "metadata": { "collapsed": false @@ -97,12 +97,8 @@ "cell_type": "code", "outputs": [], "source": [ - "from mat3ra.made.material import Material\n", - "from utils.jupyterlite import get_data\n", - "\n", - "# Get the list of input materials and load them into `materials_in` variable\n", - "get_data(\"materials_in\", globals())\n", - "materials = list(map(Material, globals()[\"materials_in\"]))" + "from utils.jupyterlite import get_materials\n", + "materials = get_materials(globals())" ], "metadata": { "collapsed": false @@ -260,7 +256,8 @@ "metadata": { "collapsed": false }, - "id": "8d90932312c418ee" + "id": "8d90932312c418ee", + "execution_count": null }, { "cell_type": "markdown", @@ -281,7 +278,8 @@ "metadata": { "collapsed": false }, - "id": "69ccc90b8c5c1191" + "id": "69ccc90b8c5c1191", + "execution_count": null }, { "cell_type": "markdown", @@ -305,7 +303,8 @@ "metadata": { "collapsed": false }, - "id": "cbfe0878a16f6c83" + "id": "cbfe0878a16f6c83", + "execution_count": null }, { "cell_type": "markdown", @@ -321,9 +320,9 @@ "cell_type": "code", "outputs": [], "source": [ - "from utils.jupyterlite import set_data\n", + "from utils.jupyterlite import set_materials\n", "\n", - "set_data(\"materials\", [material_with_perturbation.to_json(), material_with_custom_perturbation.to_json()])" + "set_materials([material_with_perturbation, material_with_custom_perturbation])" ], "metadata": { "collapsed": false diff --git a/other/materials_designer/create_point_defect.ipynb b/other/materials_designer/create_point_defect.ipynb index db0fbed1..b6947837 100644 --- a/other/materials_designer/create_point_defect.ipynb +++ b/other/materials_designer/create_point_defect.ipynb @@ -91,7 +91,7 @@ "cell_type": "markdown", "source": [ "### 1.3. Get input materials\n", - "Materials are loaded with `get_data()`. The first material is assigned as substrate and the second as film." + "Materials are loaded with `get_materials()`." ], "metadata": { "collapsed": false @@ -102,12 +102,8 @@ "cell_type": "code", "outputs": [], "source": [ - "from mat3ra.made.material import Material\n", - "from utils.jupyterlite import get_data\n", - "\n", - "# Get the list of input materials and load them into `materials_in` variable\n", - "get_data(\"materials_in\", globals())\n", - "materials = list(map(Material, globals()[\"materials_in\"]))" + "from utils.jupyterlite import get_materials\n", + "materials = get_materials(globals())" ], "metadata": { "collapsed": false @@ -237,9 +233,9 @@ "cell_type": "code", "outputs": [], "source": [ - "from utils.jupyterlite import set_data\n", + "from utils.jupyterlite import set_materials\n", "\n", - "set_data(\"materials\", [material_with_defect_at_site_id.to_json(), material_with_defect_at_position.to_json()])" + "set_materials([material_with_defect_at_site_id, material_with_defect_at_position])" ], "metadata": { "collapsed": false diff --git a/utils/jupyterlite.py b/utils/jupyterlite.py index 2f1ea7f6..25e548fe 100644 --- a/utils/jupyterlite.py +++ b/utils/jupyterlite.py @@ -1,10 +1,22 @@ +import inspect import json import os +import sys from enum import Enum -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional from IPython.display import Javascript, display + +async def install_setup(): + if sys.platform == "emscripten": + import micropip + + await micropip.install("mat3ra-made") + await micropip.install("mat3ra-code") + await micropip.install("mat3ra-utils") + + UPLOADS_FOLDER = "uploads" @@ -13,6 +25,12 @@ class EnvironmentEnum(Enum): PYTHON = "python" +class SeverityLevelEnum(Enum): + INFO = "INFO" + WARNING = "WARNING" + ERROR = "ERROR" + + # Environment detection # default value for env.HOME from https://pyodide.org/en/stable/usage/api/js-api.html ENVIRONMENT = EnvironmentEnum.PYODIDE if os.environ.get("HOME") == "/home/pyodide" else EnvironmentEnum.PYTHON @@ -22,7 +40,35 @@ class EnvironmentEnum(Enum): if ENVIRONMENT == EnvironmentEnum.PYTHON: import subprocess - import sys + + +def log(message: str, level: Optional[SeverityLevelEnum] = None, force_verbose=None): + """ + Log a message based on the VERBOSE flag in the caller's globals(). + + Args: + message (str): The message to log. + level (SeverityLevelEnum): The severity level of the message (e.g., INFO, WARNING, ERROR). + force_verbose (bool): If True, log the message regardless of the VERBOSE flag in globals() + """ + if force_verbose is True: + should_log = True + elif force_verbose is False: + should_log = False + else: + # Inspect the caller's globals to get VERBOSE flag + frame = inspect.currentframe() + try: + caller_frame = frame.f_back # type: ignore + caller_globals = caller_frame.f_globals # type: ignore + should_log = caller_globals.get("VERBOSE", os.environ.get("VERBOSE", True)) + finally: + del frame # Avoid reference cycles + if should_log: + if level is None: + print(message) + else: + print(f"{level.value}: {message}") async def install_package_pyodide(pkg: str, verbose=True): @@ -37,7 +83,7 @@ async def install_package_pyodide(pkg: str, verbose=True): await micropip.install(pkg, deps=are_dependencies_installed) pkg_name = pkg.split("/")[-1].split("-")[0] if is_url else pkg.split("==")[0] if verbose: - print(f"Installed {pkg_name}") + log(f"Installed {pkg_name}", force_verbose=verbose) def install_package_python(pkg: str, verbose=True): @@ -49,7 +95,7 @@ def install_package_python(pkg: str, verbose=True): """ subprocess.check_call([sys.executable, "-m", "pip", "install", pkg]) if verbose: - print(f"Installed {pkg}") + log(f"Installed {pkg}", force_verbose=verbose) async def install_packages(notebook_name: str, requirements_path="config.yml", verbose=True): @@ -61,6 +107,7 @@ async def install_packages(notebook_name: str, requirements_path="config.yml", v verbose (bool): Whether to print the names of the installed packages and status of installation. """ if ENVIRONMENT == EnvironmentEnum.PYODIDE: + await install_setup() await micropip.install("pyyaml") # PyYAML has to be installed before being imported in Pyodide and can't appear at the top of the file import yaml @@ -110,7 +157,7 @@ async def install_packages(notebook_name: str, requirements_path="config.yml", v install_package_python(pkg, verbose) if verbose: - print("Packages installed successfully.") + log("Packages installed successfully.", force_verbose=verbose) os.environ["requirements_hash"] = requirements_hash @@ -135,7 +182,7 @@ def set_data_pyodide(key: str, value: Any): }})(); """ display(Javascript(js_code)) - print(f"Status: {key} sent to host.") + log(f"Data for {key} sent to host.") set_data_python(key, value) @@ -153,7 +200,7 @@ def set_data_python(key: str, value: Any): file_path = os.path.join(UPLOADS_FOLDER, f"{safe_name}.json") with open(file_path, "w") as file: json.dump(item, file) - print(f"Data for {key} written to {file_path}") + log(f"Data for {key} written to {file_path}") def set_data(key: str, value: Any): @@ -195,7 +242,7 @@ def get_data_python(key: str, globals_dict: Optional[Dict] = None): with open(os.path.join(UPLOADS_FOLDER, filename), "r") as file: data = json.load(file) name = os.path.splitext(filename)[0] - print(f"{index}: Data from {name} has been read successfully.") + log(f"{index}: Data from {name} has been read successfully.") index += 1 data_from_host.append(data) if globals_dict is not None: @@ -215,3 +262,48 @@ def get_data(key: str, globals_dict: Optional[Dict] = None): get_data_pyodide(key, globals_dict) elif ENVIRONMENT == EnvironmentEnum.PYTHON: get_data_python(key, globals_dict) + + +def get_materials(globals_dict: Optional[Dict] = None) -> List[Any]: + """ + Retrieve materials from the environment and assign them to globals_dict["materials_in"]. + + Args: + globals_dict (dict): The globals dictionary to populate. + + Returns: + List[Material]: A list of Material objects. + """ + from mat3ra.made.material import Material + + if globals_dict is None: + frame = inspect.currentframe() + try: + caller_frame = frame.f_back # type: ignore + caller_globals = caller_frame.f_globals # type: ignore + globals_dict = caller_globals + finally: + del frame # Avoid reference cycles + get_data("materials_in", globals_dict) + + if "materials_in" in globals_dict and globals_dict["materials_in"]: + materials = [Material(item) for item in globals_dict["materials_in"]] + log(f"Retrieved {len(materials)} materials.") + return materials + else: + log("No materials found.") + return [] + + +def set_materials(materials: List[Any]): + """ + Serialize and send a list of Material objects to the environment. + + Args: + materials (List[Material]): The list of Material objects to send. + """ + from mat3ra.utils.array import convert_to_array_if_not + + materials = convert_to_array_if_not(materials) + materials_data = [material.to_json() for material in materials] + set_data("materials", materials_data)