Ejemplo n.º 1
0
    def test_global_attributes(self):
        """
        Test that global dataset attributes can be produced, and successfully
        read back from another read of the file.
        """
        writer = NetCDFWriter()
        writer.global_attribute("description", "A unittest case")
        writer.global_attribute("source", "Arrhenius project unittests")
        writer.global_attribute("history", "Written June 27, 2018")

        filepath = path.join(WRITE_OUTPUT_DIR, "global_attrs.nc")
        writer.write(filepath)

        # Read back the file with a trusted NetCDF library.
        ds = Dataset(filepath, "r")

        # Check expected contents of the three attributes written earlier.
        self.assertEqual(3, len(vars(ds)))
        self.assertEqual(ds.description, "A unittest case")
        self.assertEqual(ds.source, "Arrhenius project unittests")
        self.assertEqual(ds.history, "Written June 27, 2018")

        ds.close()
Ejemplo n.º 2
0
class ModelOutput:
    """
    A general-purpose center for all forms of output. Responsible for
    organization of program output into folders.

    The output of a program is defined as the temperature data produced by
    a run of the model. This data may be saved in the form of a data file
    (such as a NetCDF file) and/or as image representations, and/or in other
    data formats.

    All of these output types are produced side-by-side, and stored in their
    own directory to keep data separate from different model runs.
    """
    def __init__(self: 'ModelOutput', data: List['LatLongGrid']) -> None:
        """
        Instantiate a new ModelOutput object.

        Model data is provided through the data parameter, through a list of
        grid objects. Each grid in the list represents a segment of time, such
        as a month or a season. All grids must have the same dimensions.

        :param data:
            A list of latitude-longitude grids of data
        """
        # Create output directory if it does not already exist.
        parent_out_dir = Path(OUTPUT_FULL_PATH)
        parent_out_dir.mkdir(exist_ok=True)

        self._data = data
        self._grid = data[0].dimensions()
        self._dataset = NetCDFWriter()

    def write_dataset(self: 'ModelOutput', data: List['LatLongGrid'],
                      dir_path: str, dataset_name: str) -> None:
        """
        Produce a NetCDF dataset, with the name given by dataset_name.nc,
        containing the variables in the data parameter that the output
        controller allows. The dataset will be written to the specified
        path in the file system.

        The dataset contains all the dimensions that are used in the data
        (e.g. time, latitude, longitude) as well as variables including
        final temperature, temperature change, humidity, etc. according
        to which of the ReportDatatype output types are enabled in the
        current output controller.

        :param data:
            Output from an Arrhenius model run
        :param dir_path:
            The directory where the dataset will be written
        :param dataset_name:
            The name of the dataset
        """
        # Write the data out to a NetCDF file in the output directory.
        grid_by_count = self._grid.dims_by_count()
        output_path = path.join(dir_path, dataset_name)

        global_output_center().submit_output(Debug.PRINT_NOTICES,
                                             "Writing NetCDF dataset...")
        self._dataset.global_attribute("description", "Output for an"
                                                      "Arrhenius model run.")\
            .dimension('time', np.int32, len(data), (0, len(data)))\
            .dimension('latitude', np.int32, grid_by_count[0], (-90, 90)) \
            .dimension('longitude', np.int32, grid_by_count[1], (-180, 180)) \

        for output_type in ReportDatatype:
            variable_data =\
                extract_multidimensional_grid_variable(data,
                                                       output_type.value)
            global_output_center().submit_output(output_type, variable_data,
                                                 output_type.value)

        self._dataset.write(output_path)

    def write_dataset_variable(self: 'ModelOutput', data: np.ndarray,
                               data_type: str) -> None:
        """
        Prepare to write data into a variable by the name of data_type
        in this instance's NetCDF dataset file. Apply this variable's
        dimensions and type, along with several attributes.

        :param data:
            A single-variable grid taken from Arrhenius model output
        :param data_type:
            The name of the variable as it will appear in the dataset
        """
        dims_map = [[], ['latitude'], ['latitude', 'longitude'],
                    ['time', 'latitude', 'longitude'],
                    ['time', 'level', 'latitude', 'longitude']]

        global_output_center().submit_output(
            Debug.PRINT_NOTICES, "Writing {} to dataset".format(data_type))
        variable_type = VARIABLE_METADATA[data_type][VAR_TYPE]
        self._dataset.variable(data_type, variable_type, dims_map[data.ndim])

        for attr, val in VARIABLE_METADATA[data_type][VAR_ATTRS].items():
            self._dataset.variable_attribute(data_type, attr, val)

        self._dataset.data(data_type, data)

    def write_images(self: 'ModelOutput',
                     data: List['LatLongGrid'],
                     output_path: str,
                     run_id: str = "") -> None:
        """
        Produce a series of maps displaying some of the results of an
        Arrhenius model run according to what variable the output controller
        allows. Images are stored in a directory given by output_path.

        One image will be produced per time segment per variable for which
        output is allowed by the output controller, based on which
        ReportDatatype output types are enabled. The optional argument
        img_base_name specifies a prefix that will be added to each of the
        image files to identify which model run they belong to.

        :param data:
            The output from an Arrhenius model run
        :param output_path:
            The directory where image files will be stored
        :param run_id:
            A prefix that will start off the names of all the image files
        """
        output_controller = global_output_center()

        # Attempt to output images for each variable output type.
        for output_type in ReportDatatype:
            variable_name = output_type.value
            variable = extract_multidimensional_grid_variable(
                data, variable_name)
            img_type_path = path.join(output_path, variable_name)

            output_controller.submit_output(output_type, variable,
                                            img_type_path, variable_name,
                                            run_id)

    def write_output(self: 'ModelOutput', run_title: str) -> None:
        """
        Produce NetCDF data files and image files from the provided data, and
        a directory with the name dir_name to hold them.

        One image file is created per time segment in the data. In the
        case of Arrhenius' model, this is one per season. Only one NetCDF data
        file is produced, in which all time segments are present.
        """
        # Create a directory for this model output if none exists already.
        out_dir_path = path.join(OUTPUT_FULL_PATH, run_title)
        out_dir = Path(out_dir_path)
        out_dir.mkdir(exist_ok=True)

        output_controller = global_output_center()
        output_controller.submit_collection_output(
            (DATASET_VARS, ), self._data, out_dir_path, run_title + ".nc")
        output_controller.submit_collection_output((IMAGES, ), self._data,
                                                   out_dir_path, run_title)