Source code for UQpy.run_model.model_execution.PythonModel

import logging
import pathlib
import platform

import numpy as np
from beartype import beartype

from UQpy.utilities.ValidationTypes import Numpy2DFloatArray


[docs]class PythonModel: @beartype def __init__(self, model_script: str, model_object_name: str, var_names: list[str] = None, delete_files: bool = False, **model_object_name_kwargs): """ :param model_script: The filename (with .py extension) of the Python script which contains commands to execute the model. The named file must be present in the current working directory from which :class:`.RunModel` is called. :param model_object_name: In the Python workflow, `model_object_name` specifies the name of the function or class within `model_script' that executes the model. If there is only one function or class in the `model_script`, then it is not necessary to specify the model_object_name. If there are multiple objects within the `model_script`, then `model_object_name` must be specified. `model_object_name` is not used in the third-party software model workflow. :param var_names: A list containing the names of the variables present in `input_template`. If `input template` is provided and `var_names` is not passed, i.e. if ``var_names=None``, then the default variable names `x0`, `x1`, `x2`,..., `xn` are created and used by :class:`.RunModel`, where `n` is the number of variables (`n_vars`). The number of variables is equal to the second dimension of `samples` (i.e. ``n_vars=len(samples[0])``). `var_names` is not used in the Python model workflow. :param delete_files: Specifies whether or not to delete individual run output files after model execution and output processing. If `delete_files = True`, :class:`.RunModel` will remove all `run_i...` directories in the `model_dir`. :param model_object_name_kwargs: Additional inputs to the Python object specified by `model_object_name` in the Python model workflow. """ if var_names is None: var_names = [] self.var_names = var_names self._model_output = None self.logger = logging.getLogger(__name__) if platform.system() in ["Windows"]: self.python_command = "python" else: self.python_command = "python3" self.model_object_name = model_object_name self.model_object_name_kwargs = model_object_name_kwargs self.delete_files = delete_files # Check if the model script is a python script model_extension = pathlib.Path(model_script).suffix if model_extension == ".py": self.model_script = model_script else: raise ValueError("\nUQpy: The model script must be the name of a python script, with extension '.py'.") # Import the python module python_model = __import__(self.model_script[:-3]) self.model_object = getattr(python_model, self.model_object_name) # Run function which checks if the python model has the model object self._check_python_model(python_model) self.logger.info('\nUQpy: Performing serial execution of a Python model.\n') def initialize(self, samples): pass def finalize(self): pass def preprocess_single_sample(self, index, sample) -> Numpy2DFloatArray: return np.atleast_2d(sample) def execute_single_sample(self, index, sample_to_send): if len(self.model_object_name_kwargs) == 0: return self.model_object(sample_to_send) else: return self.model_object(sample_to_send, **self.model_object_name_kwargs) def postprocess_single_file(self, index, model_output): return model_output.qoi if self.model_is_class else model_output def _check_python_model(self, python_model): """ Check if python model name is valid This function gets the name of the classes and functions in the imported python module whose names is passed in as the python model to RunModel. There should be at least one class or function in the module - if not there, then the function exits raising a ValueError. If there is at least one class or function in the module, if the model object name is not given as input and there is only one class or function, that class name or function name is used to run the model. If there is a model_object_name given, check if it is a valid name. Else, a ValueError is raised. """ # Get the names of the classes and functions in the imported module import inspect class_list = [] function_list = [] for name, obj in inspect.getmembers(python_model): if inspect.isclass(obj): class_list.append(name) elif inspect.isfunction(obj): function_list.append(name) # There should be at least one class or function in the module - if not there, exit with error. if len(class_list) == 0 and len(function_list) == 0: raise ValueError("\nUQpy: A python model should be defined as a function or class in the script.\n") else: # If there is at least one class or function in the module # If the model object name is not given as input and there is only one class or function, # take that class name or function name to run the model. if self.model_object_name is None and len(class_list) + len(function_list) == 1: if len(class_list) == 1: self.model_object_name = class_list[0] elif len(function_list) == 1: self.model_object_name = function_list[0] # If there is a model_object_name given, check if it is in the list. if self.model_object_name in class_list: self.logger.info("\nUQpy: The model class that will be run: " + self.model_object_name) self.model_is_class = True elif self.model_object_name in function_list: self.logger.info("\nUQpy: The model function that will be run: " + self.model_object_name) self.model_is_class = False else: if self.model_object_name is None: raise ValueError("\nUQpy: There are more than one objects in the module. Specify the name of the " "function or class which has to be executed.\n") else: print("\nUQpy: You specified the model_object_name as: " + str(self.model_object_name)) raise ValueError("\nUQpy: The file does not contain an object which was specified as the model.\n")