Ejemplo n.º 1
0
class BmiPCRGlobWB(EBmi):
    #we use the same epoch as pcrglobwb netcdf reporting
    def days_since_industry_epoch(self, modeltime):
        return (modeltime - datetime.date(1901, 1, 1)).days

    def in_modeltime(self, days_since_industry_epoch):
        return (datetime.datetime(1901, 1, 1) +
                datetime.timedelta(days=days_since_industry_epoch)).date()

    def calculate_shape(self):
        # return pcr.pcr2numpy(self.model.landmask, 1e20).shape
        return (pcr.clone().nrRows(), pcr.clone().nrCols())

    #BMI initialize (as a single step)
    def initialize(self, fileName):
        self.initialize_config(fileName)
        self.initialize_model()

    #EBMI initialize (first step of two)
    def initialize_config(self, fileName):
        logger.info("PCRGlobWB: initialize_config")

        try:

            self.configuration = Configuration(fileName,
                                               relative_ini_meteo_paths=True)
            pcr.setclone(self.configuration.cloneMap)

            # set start and end time based on configuration
            self.model_time = ModelTime()
            self.model_time.getStartEndTimeSteps(
                self.configuration.globalOptions['startTime'],
                self.configuration.globalOptions['endTime'])

            self.model_time.update(0)

            self.shape = self.calculate_shape()

            logger.info("Shape of maps is %s", str(self.shape))

            self.model = None

        except:
            import traceback
            traceback.print_exc()
            raise

    #EBMI initialize (second step of two)
    def initialize_model(self):
        if self.model is not None:
            #already initialized
            return

        try:

            logger.info("PCRGlobWB: initialize_model")

            initial_state = None
            self.model = PCRGlobWB(self.configuration, self.model_time,
                                   initial_state)

            self.reporting = Reporting(self.configuration, self.model,
                                       self.model_time)

            logger.info("Shape of maps is %s", str(self.shape))

            logger.info("PCRGlobWB Initialized")

        except:
            import traceback
            traceback.print_exc()
            raise

    def update(self):
        timestep = self.model_time.timeStepPCR

        self.model_time.update(timestep + 1)

        self.model.read_forcings()
        self.model.update(report_water_balance=True)
        self.reporting.report()

    #         #numpy = pcr.pcr2numpy(self.model.landSurface.satDegUpp000005, 1e20)
    #         numpy = pcr.pcr2numpy(self.model.landSurface.satDegUpp000005, np.NaN)
    #         print numpy.shape
    #         print numpy

    def update_until(self, time):
        while self.get_current_time() + 0.001 < time:
            self.update()

    def update_frac(self, time_frac):
        raise NotImplementedError

    def finalize(self):
        pass

    def get_component_name(self):
        return "pcrglobwb"

    def get_input_var_names(self):
        return ["top_layer_soil_saturation"]

    def get_output_var_names(self):
        return ["top_layer_soil_saturation"]

    def get_var_type(self, long_var_name):
        return 'float64'

    def get_var_units(self, long_var_name):
        #TODO: this is not a proper unit
        return '1'

    def get_var_rank(self, long_var_name):
        return 0

    def get_var_size(self, long_var_name):
        return np.prod(self.get_grid_shape(long_var_name))

    def get_var_nbytes(self, long_var_name):
        return self.get_var_size(long_var_name) * np.float64.itemsize

    def get_start_time(self):
        return self.days_since_industry_epoch(self.model_time.startTime)

    def get_current_time(self):
        return self.days_since_industry_epoch(self.model_time.currTime)

    def get_end_time(self):
        return self.days_since_industry_epoch(self.model_time.endTime)

    def get_time_step(self):
        return 1

    def get_time_units(self):
        return "Days since 1901-01-01"

    def get_value(self, long_var_name):
        logger.info("getting value for var %s", long_var_name)

        if (long_var_name == "top_layer_soil_saturation"):

            if self.model is not None and hasattr(self.model.landSurface,
                                                  'satDegUpp000005'):

                #first make all NanS into 0.0 with cover, then cut out the model using the landmask.
                # This should not actually make a difference.
                remasked = pcr.ifthen(
                    self.model.landmask,
                    pcr.cover(self.model.landSurface.satDegUpp000005, 0.0))

                pcr.report(self.model.landSurface.satDegUpp000005, "value.map")
                pcr.report(remasked, "remasked.map")

                value = pcr.pcr2numpy(remasked, np.NaN)

            else:
                logger.info(
                    "model has not run yet, returning empty state for top_layer_soil_saturation"
                )
                value = pcr.pcr2numpy(pcr.scalar(0.0), np.NaN)

            # print "getting var", value
            # sys.stdout.flush()

            doubles = value.astype(np.float64)

            # print "getting var as doubles!!!!", doubles

            result = np.flipud(doubles)

            # print "getting var as doubles flipped!!!!", result
            # sys.stdout.flush()

            return result
        else:
            raise Exception("unknown var name" + long_var_name)

    def get_value_at_indices(self, long_var_name, inds):
        raise NotImplementedError

    #     def get_satDegUpp000005_from_observation(self):
    #
    #         # assumption for observation values
    #         # - this should be replaced by values from the ECV soil moisture value (sattelite data)
    #         # - uncertainty should be included here
    #         # - note that the value should be between 0.0 and 1.0
    #         observed_satDegUpp000005 = pcr.min(1.0,\
    #                                    pcr.max(0.0,\
    #                                    pcr.normal(pcr.boolean(1)) + 1.0))
    #         return observed_satDegUpp000005

    def set_satDegUpp000005(self, src):
        mask = np.isnan(src)
        src[mask] = 1e20
        observed_satDegUpp000005 = pcr.numpy2pcr(pcr.Scalar, src, 1e20)

        pcr.report(observed_satDegUpp000005, "observed.map")

        constrained_satDegUpp000005 = pcr.min(
            1.0, pcr.max(0.0, observed_satDegUpp000005))

        pcr.report(constrained_satDegUpp000005, "constrained.map")

        pcr.report(self.model.landSurface.satDegUpp000005, "origmap.map")
        diffmap = constrained_satDegUpp000005 - self.model.landSurface.satDegUpp000005
        pcr.report(diffmap, "diffmap.map")

        # ratio between observation and model
        ratio_between_observation_and_model = pcr.ifthenelse(self.model.landSurface.satDegUpp000005 > 0.0,
                                                             constrained_satDegUpp000005 / \
                                                             self.model.landSurface.satDegUpp000005, 0.0)

        # updating upper soil states for all lad cover types
        for coverType in self.model.landSurface.coverTypes:
            # correcting upper soil state (storUpp000005)
            self.model.landSurface.landCoverObj[
                coverType].storUpp000005 *= ratio_between_observation_and_model

            # if model value = 0.0, storUpp000005 is calculated based on storage capacity (model parameter) and observed saturation degree
            self.model.landSurface.landCoverObj[coverType].storUpp000005 = pcr.ifthenelse(
                self.model.landSurface.satDegUpp000005 > 0.0, \
                self.model.landSurface.landCoverObj[coverType].storUpp000005, \
                constrained_satDegUpp000005 * self.model.landSurface.parameters.storCapUpp000005)
            # correct for any scaling issues (value < 0 or > 1 do not make sense
            self.model.landSurface.landCoverObj[
                coverType].storUpp000005 = pcr.min(
                    1.0,
                    pcr.max(
                        0.0, self.model.landSurface.landCoverObj[coverType].
                        storUpp000005))

    def set_value(self, long_var_name, src):

        if self.model is None or not hasattr(self.model.landSurface,
                                             'satDegUpp000005'):
            logger.info("cannot set value for %s, as model has not run yet.",
                        long_var_name)
            return

        logger.info("setting value for %s", long_var_name)

        # logger.info("dumping state to %s", self.configuration.endStateDir)
        # self.model.dumpStateDir(self.configuration.endStateDir + "/pre/")

        # print "got value to set", src

        # make sure the raster is the right side up
        src = np.flipud(src)

        # print "flipped", src

        # cast to pcraster precision
        src = src.astype(np.float32)

        # print "as float 32", src

        sys.stdout.flush()

        logger.info("setting value shape %s", src.shape)

        if (long_var_name == "top_layer_soil_saturation"):
            self.set_satDegUpp000005(src)
        else:
            raise Exception("unknown var name" + long_var_name)

        # write state here to facilitate restarting tomorrow
        # logger.info("dumping state to %s", self.configuration.endStateDir)
        # self.model.dumpStateDir(self.configuration.endStateDir + "/post/")

    def set_value_at_indices(self, long_var_name, inds, src):
        raise NotImplementedError

    def get_grid_type(self, long_var_name):
        return BmiGridType.UNIFORM

    def get_grid_shape(self, long_var_name):
        return

    def get_grid_shape(self, long_var_name):
        return self.shape

    def get_grid_spacing(self, long_var_name):

        cellsize = pcr.clone().cellSize()

        return np.array([cellsize, cellsize])

    def get_grid_origin(self, long_var_name):

        north = pcr.clone().north()
        cellSize = pcr.clone().cellSize()
        nrRows = pcr.clone().nrRows()

        south = north - (cellSize * nrRows)

        west = pcr.clone().west()

        return np.array([south, west])

    def get_grid_x(self, long_var_name):
        raise ValueError

    def get_grid_y(self, long_var_name):
        raise ValueError

    def get_grid_z(self, long_var_name):
        raise ValueError

    def get_grid_connectivity(self, long_var_name):
        raise ValueError

    def get_grid_offset(self, long_var_name):
        raise ValueError

    #EBMI functions

    def set_start_time(self, start_time):
        self.model_time.setStartTime(self.in_modeltime(start_time))

    def set_end_time(self, end_time):
        self.model_time.setEndTime(self.in_modeltime(end_time))

    def get_attribute_names(self):
        raise NotImplementedError

    def get_attribute_value(self, attribute_name):
        raise NotImplementedError

    def set_attribute_value(self, attribute_name, attribute_value):
        raise NotImplementedError

    def save_state(self, destination_directory):
        logger.info("saving state to %s", destination_directory)
        self.model.dumpStateDir(destination_directory)

    def load_state(self, source_directory):
        raise NotImplementedError
Ejemplo n.º 2
0
class pcrglobwbBMI(object):
    def initialize(self, config_file_location=None):
        """
        Initializes the model: read config file, load variables, get timestep information, etc.
        """
        self.configuration = Configuration(config_file_location)

        self.initial_state = None

        self.currTimeStep = ModelTime(
        )  # timeStep info: year, month, day, doy, hour, etc

        self.currTimeStep.getStartEndTimeSteps(
            self.configuration.globalOptions['startTime'],
            self.configuration.globalOptions['endTime'])

        self.deterministic_runner = DeterministicRunner(
            self.configuration, self.currTimeStep, self.initial_state)

        self.dynamic_framework = DynamicFramework(self.deterministic_runner, 1)
        self.dynamic_framework.setQuiet(True)
        self.dynamic_framework._runInitial()
        self.dynamic_framework._runResume()

        # set timestep (to allow updates on a per-timestep-basis)
        self.currenttimestep = 0

        logger.info('Model initialized. Spin-up might be required.')

    def finalize(self):
        """
        Finalizes the model: shut down the model run, clean up resources, etc.
        """
        self.dynamic_framework._runSuspend()
        #dynamic_framework._wf_shutdown()   # commented out, special function from wflow Dynamic Framework

    def spinup(self):
        """
        Spin-up the model. This is required to obtain realistic starting conditions for the model run.
        It runs on a yearly basis until the required convergence or max. allowed spin-up runs is reached.
        """
        spin_up = SpinUp(self.configuration)  # object for spin_up

        self.currTimeStep = ModelTime(
        )  # timeStep info: year, month, day, doy, hour, etc

        # spin-up
        noSpinUps = int(self.configuration.globalOptions['maxSpinUpsInYears'])
        if noSpinUps > 0:

            logger.info('Spin-Up #Total Years: ' + str(noSpinUps))

            spinUpRun = 0
            has_converged = False
            while spinUpRun < noSpinUps and has_converged == False:
                spinUpRun += 1
                self.currTimeStep.getStartEndTimeStepsForSpinUp(
                    self.configuration.globalOptions['startTime'], spinUpRun,
                    noSpinUps)
                logger.info('Spin-Up Run No. ' + str(spinUpRun))
                deterministic_runner = DeterministicRunner(
                    self.configuration, self.currTimeStep, self.initial_state)

                all_state_begin = deterministic_runner.model.getAllState()

                self.dynamic_framework = DynamicFramework(
                    deterministic_runner, self.currTimeStep.nrOfTimeSteps)
                self.dynamic_framework.setQuiet(True)
                self.dynamic_framework.run()

                all_state_end = deterministic_runner.model.getAllState()

                has_converged = spin_up.checkConvergence(
                    all_state_begin, all_state_end, spinUpRun,
                    deterministic_runner.model.routing.cellArea)

                self.initial_state = deterministic_runner.model.getState()

                # setting model ready after spin-up
                self.currTimeStep.getStartEndTimeSteps(
                    self.configuration.globalOptions['startTime'],
                    self.configuration.globalOptions['endTime'])

                self.deterministic_runner = DeterministicRunner(
                    self.configuration, self.currTimeStep, self.initial_state)

        logger.info(
            'End of spin-up. Model is ready for transient simulation run.')

    def update(self, dt=-1):
        """
        Updates the model a number of timesteps, dependent on specified dt:
        dt = -1	-> runs the entire model from start time to end time
        dt = 1  -> updates the model 1 timestep (1 day)
        dt > 1  -> updates the model a number of timesteps (dt days)
        
        NOTE: the model can only run on a daily timestep!
        """

        if dt == 1:
            # update timestep
            self.currenttimestep += 1
            self.currTimeStep.update(self.currenttimestep)

            # commented out, already stated at initialization and at end of spin-up, not required at every timestep?
            #deterministic_runner = DeterministicRunner(self.configuration, self.currTimeStep, self.initial_state)

            # update model
            self.dynamic_framework = DynamicFramework(
                self.deterministic_runner, self.currenttimestep,
                self.currenttimestep)
            self.dynamic_framework.setQuiet(True)
            self.dynamic_framework.run()

            # update states (commented out, not required?)
            #self.initial_state = deterministic_runner.model.getState()

        elif dt == -1:
            # commented out, already stated at initialization and at end of spin-up, not required here as well?
            #deterministic_runner = DeterministicRunner(self.configuration, self.currTimeStep, self.initial_state)

            self.dynamic_framework = DynamicFramework(
                self.deterministic_runner, self.currTimeStep.nrOfTimeSteps)
            self.dynamic_framework.setQuiet(True)
            self.dynamic_framework.run()

        else:
            # update timestep
            self.currenttimestep += 1
            self.currTimeStep.update(self.currenttimestep)

            # update model
            self.dynamic_framework = DynamicFramework(
                self.deterministic_runner, self.currenttimestep + (dt - 1),
                self.currenttimestep)
            self.dynamic_framework.setQuiet(True)
            self.dynamic_framework.run()

            # update time
            self.currenttimestep += (dt - 1)
            self.currTimeStep.update(self.currenttimestep)

    def get_start_time(self):
        """
        Returns model start time
        Input:  -
        Output: time as datetime (YYYY,MM,DD)
        """
        return self.currTimeStep.startTime

    def get_end_time(self):
        """
        Returns model end time
        Input:  -
        Output: time as datetime (YYYY,MM,DD)
        """
        return self.currTimeStep.endTime

    def get_current_time(self):
        """
        Returns current model time
        Input:  -
        Output: time as datetime (YYYY,MM,DD)
        """
        return self.currTimeStep.currTime

    def get_time_step(self):
        """
        Return current model timestep
        Input:  -
        Output: timestep as int
        """
        return self.currTimeStep.timeStepPCR

    def get_var(self, name, missingValues=-999):
        """
        Returns a numpy array from model library
        Input:  variable/map name (string)
        Output: numpy array or single variable, depending on input
        
        NOTE1: to get a variable from a specific landCover type, a tuple containing two strings should be used, with:
        - string 1 = name of landCover type
        - string 2 = name of variable
        
        NOTE2: there are two options to create a numpy array:
        - pcr2numpy    -> requires a value for MV (optional, default = -999)
        - pcr_as_numpy -> automatically sets nan for all MV
        Currently using pcr2numpy!
        """

        # check size of name input
        if numpy.size(name) == 1:

            # check for 'name' in the different sections of the model
            if hasattr(self.deterministic_runner.model.landSurface, name):
                pcrmap = getattr(self.deterministic_runner.model.landSurface,
                                 name)
            elif hasattr(self.deterministic_runner.model.routing, name):
                pcrmap = getattr(self.deterministic_runner.model.routing, name)
            elif hasattr(self.deterministic_runner.model.meteo, name):
                pcrmap = getattr(self.deterministic_runner.model.meteo, name)
            elif hasattr(self.deterministic_runner.model.groundwater, name):
                pcrmap = getattr(self.deterministic_runner.model.groundwater,
                                 name)
            else:
                logger.warn(
                    name +
                    " cannot be found in the model, returning empty list!")

        else:

            # first check if a specific model section was used as input
            if name[0] == 'landSurface':
                if hasattr(self.deterministic_runner.model.landSurface,
                           name[1]):
                    pcrmap = getattr(
                        self.deterministic_runner.model.landSurface, name[1])
            elif name[0] == 'routing':
                if hasattr(self.deterministic_runner.model.routing, name[1]):
                    pcrmap = getattr(self.deterministic_runner.model.routing,
                                     name[1])
            elif name[0] == 'WaterBodies':
                if hasattr(self.deterministic_runner.model.routing.WaterBodies,
                           name[1]):
                    pcrmap = getattr(
                        self.deterministic_runner.model.routing.WaterBodies,
                        name[1])
            elif name[0] == 'pcrglobwb':
                if hasattr(self.deterministic_runner.model, name[1]):
                    pcrmap = getattr(self.deterministic_runner.model, name[1])
            # otherwise check if it is a variable from a landCover type
            else:
                # use the first entry of 'name' to find correct landCover type, second entry to find variable
                try:
                    if hasattr(
                            self.deterministic_runner.model.landSurface.
                            landCoverObj[name[0]], name[1]):
                        pcrmap = getattr(
                            self.deterministic_runner.model.landSurface.
                            landCoverObj[name[0]], name[1])
                    else:
                        logger.warn(
                            '(' + name[0] + ', ' + name[1] +
                            ") cannot be found in the model, returning empty list!"
                        )
                except:
                    logger.warn(
                        '(' + name[0] + ', ' + name[1] +
                        ") cannot be found in the model, returning empty list!"
                    )

        # attempt to create a numpy array, otherwise try to give the single value, or return empty list if this is both not possible
        try:
            return_value = pcr2numpy(pcrmap, missingValues)
            #return_value = pcr_as_numpy(pcrmap)
        except:
            try:
                return_value = pcrmap
            except:
                return []

        return return_value

    def set_var(self, name, var, missingValues=-999):
        """
        Sets a pcr map with values from a numpy array.
        Input:  variable/map name (string), values (numpy array or single value), missing values (optional, default = -999)
        Output: -
        
        NOTE: to set a variable from a specific landCover type, a tuple containing two strings should be used, with:
        - string 1 = name of landCover type
        - string 2 = name of variable
        """

        # try to create a pcr map from numpy array, otherwise just use the single value
        try:
            pcrmap = numpy2pcr(Scalar, var, missingValues)
        except:
            pcrmap = var

        # check if LDD (requires additional step)
        if 'lddMap' in name:
            pcrmap = ldd(pcrmap)

        # check size of name input
        if numpy.size(name) == 1:

            # update pcr map used in model with set values
            if hasattr(self.deterministic_runner.model.groundwater, name):
                exec "self.deterministic_runner.model.groundwater." + name + " = pcrmap"
            elif hasattr(self.deterministic_runner.model.landSurface, name):
                exec "self.deterministic_runner.model.landSurface." + name + " = pcrmap"
            elif hasattr(self.deterministic_runner.model.meteo, name):
                exec "self.deterministic_runner.model.meteo." + name + " = pcrmap"
            elif hasattr(self.deterministic_runner.model.routing, name):
                exec "self.deterministic_runner.model.routing." + name + " = pcrmap"
            else:
                logger.warn(name +
                            " is not defined in the model, doing nothing!")

        else:

            # first check if a specific model section was used as input
            if name[0] == 'landSurface':
                if hasattr(self.deterministic_runner.model.landSurface,
                           name[1]):
                    exec "self.deterministic_runner.model.landSurface." + name[
                        1] + " = pcrmap"
            elif name[0] == 'routing':
                if hasattr(self.deterministic_runner.model.routing, name[1]):
                    exec "self.deterministic_runner.model.routing." + name[
                        1] + " = pcrmap"
            elif name[0] == 'WaterBodies':
                if hasattr(self.deterministic_runner.model.routing.WaterBodies,
                           name[1]):
                    exec "self.deterministic_runner.model.routing.WaterBodies." + name[
                        1] + " = pcrmap"
            elif name[0] == 'pcrglobwb':
                if hasattr(self.deterministic_runner.model, name[1]):
                    exec "self.deterministic_runner.model." + name[
                        1] + " = pcrmap"
            # otherwise check if it is a variable from a landCover type
            else:
                try:
                    if hasattr(
                            self.deterministic_runner.model.landSurface.
                            landCoverObj[name[0]], name[1]):
                        exec "self.deterministic_runner.model.landSurface.landCoverObj['" + name[
                            0] + "']." + name[1] + " = pcrmap"
                    else:
                        logger.warn(
                            '(' + name[0] + ', ' + name[1] +
                            + ") is not defined in the model, doing nothing!")
                except:
                    logger.warn(
                        '(' + name[0] + ', ' + name[1] +
                        + ") is not defined in the model, doing nothing!")