Code Monkey home page Code Monkey logo

Comments (10)

SnowHydrology avatar SnowHydrology commented on September 28, 2024 1

@madMatchstick, we could also consider more elegant solutions in the future. I can't track it down at the moment, but I believe one of the Kratzert papers suggested limiting runoff to an observed baseflow value. We could then predict this for ungaged basins using the CAMELS attribute sets.

For now, though, I agree with your suggestion on where to put the logic that prevents negative values.


    def scale_output(self):

        if self.cfg_train['target_variables'][0] == 'qobs_mm_per_hour':
            self.surface_runoff_mm = (self.lstm_output[0,0,0].numpy().tolist() * self.out_std + self.out_mean)

        elif self.cfg_train['target_variables'][0] == 'QObs(mm/d)':
            self.surface_runoff_mm = (self.lstm_output[0,0,0].numpy().tolist() * self.out_std + self.out_mean) * (1/24)
        
        if self.surface_runoff_mm < 0: self.surface_runoff_mm = 0
    
        self._values['land_surface_water__runoff_depth'] = self.surface_runoff_mm/1000.0
        setattr(self, 'land_surface_water__runoff_depth', self.surface_runoff_mm/1000.0)
        self.streamflow_cms = self.surface_runoff_mm * self.output_factor_cms

        self._values['land_surface_water__runoff_volume_flux'] = self.streamflow_cms
        setattr(self, 'land_surface_water__runoff_volume_flux', self.streamflow_cms)

from lstm.

madMatchstick avatar madMatchstick commented on September 28, 2024

There are two bmi outputs in this lstm model, land_surface_water__runoff_volume_flux & land_surface_water__runoff_depth. Called in bmi.update(), we have scaled_output(),

    def scale_output(self):

        if self.cfg_train['target_variables'][0] == 'qobs_mm_per_hour':
            self.surface_runoff_mm = (self.lstm_output[0,0,0].numpy().tolist() * self.out_std + self.out_mean)

        elif self.cfg_train['target_variables'][0] == 'QObs(mm/d)':
            self.surface_runoff_mm = (self.lstm_output[0,0,0].numpy().tolist() * self.out_std + self.out_mean) * (1/24)
            
        self._values['land_surface_water__runoff_depth'] = self.surface_runoff_mm/1000.0
        setattr(self, 'land_surface_water__runoff_depth', self.surface_runoff_mm/1000.0)
        self.streamflow_cms = self.surface_runoff_mm * self.output_factor_cms

        self._values['land_surface_water__runoff_volume_flux'] = self.streamflow_cms
        setattr(self, 'land_surface_water__runoff_volume_flux', self.streamflow_cms)

@peckhams I believe the most appropriate place to apply the lower bound is just after the if-else statement? Id est

if self.surface_runoff_mm < 0: self.surface_runoff_mm = 0

This way, both outputs are guaranteed a positive value.

from lstm.

madMatchstick avatar madMatchstick commented on September 28, 2024

I can't track it down at the moment, but I believe one of the Kratzert papers suggested limiting runoff to an observed baseflow value.

@SnowHydrology, is this the paper you are after? Section 3.2 Using LSTMs as hydrological models states, about Fig 9,

A rather simple solution for this issue is to introduce just one additional parameter and to limit the simulated discharge not to zero, but to the minimum observed flow from the calibration period

Also, from A glimpse into the Unobserved: Runoff simulation for ungauged catchments with LSTMs, section 2.2 Model,

Furthermore, because negative predictions of the runoff are physically implausible, we clip negative
predictions to 0.

To your point, let's just set lower bound to zero for now :)

from lstm.

SnowHydrology avatar SnowHydrology commented on September 28, 2024

@madMatchstick thanks for tracking that down. And agreed on the fix for now.

from lstm.

peckhams avatar peckhams commented on September 28, 2024

There are two bmi outputs in this lstm model, land_surface_water__runoff_volume_flux & land_surface_water__runoff_depth. Called in bmi.update(), we have scaled_output(),

    def scale_output(self):

        if self.cfg_train['target_variables'][0] == 'qobs_mm_per_hour':
            self.surface_runoff_mm = (self.lstm_output[0,0,0].numpy().tolist() * self.out_std + self.out_mean)

        elif self.cfg_train['target_variables'][0] == 'QObs(mm/d)':
            self.surface_runoff_mm = (self.lstm_output[0,0,0].numpy().tolist() * self.out_std + self.out_mean) * (1/24)
            
        self._values['land_surface_water__runoff_depth'] = self.surface_runoff_mm/1000.0
        setattr(self, 'land_surface_water__runoff_depth', self.surface_runoff_mm/1000.0)
        self.streamflow_cms = self.surface_runoff_mm * self.output_factor_cms

        self._values['land_surface_water__runoff_volume_flux'] = self.streamflow_cms
        setattr(self, 'land_surface_water__runoff_volume_flux', self.streamflow_cms)

@peckhams I believe the most appropriate place to apply the lower bound is just after the if-else statement? Id est

if self.surface_runoff_mm < 0: self.surface_runoff_mm = 0

This way, both outputs are guaranteed a positive value.

from lstm.

peckhams avatar peckhams commented on September 28, 2024

This seems fine here, but I would normally do this kind of thing with the following syntax:
np.maximum( self.surface_runoff_mm, 0, self.surface_runoff_mm)
This numpy function says to replace the value with whichever is bigger, the value or 0, and to write the new value "in-place" (in the same memory location). A numpy function is compiled C code, so a Python conditional (if-then-else) will likely be slower. Since the statement gets evaluated many times, this speed difference can add up.

from lstm.

peckhams avatar peckhams commented on September 28, 2024

And this syntax works the same for variables that are scalars or n-dimensional arrays.

from lstm.

SnowHydrology avatar SnowHydrology commented on September 28, 2024

@peckhams Thanks for that explanation. A fair bit of Fortran code is written with that syntax, as well. Is it common for a compiled language's max statement to be faster than its if-else function?

from lstm.

peckhams avatar peckhams commented on September 28, 2024

For compiled languages (vs. an interpreted language like Python), it may not matter. But to get maximum performance from Python code, you want to use compiled numpy (or other) functions whenever possible. Several of the numpy functions, like maximum, have an optional third argument, which is the memory location to place the result -- in this case, overwriting in-place. Note the syntax; it's not of the form: self.x = np.maximum(self.x, 0), but rather just: np.maximum(self.x, 0, self.x). When modifying a variable, especially if it's a big array, you want to just overwrite the same memory location, which is faster. Another example of this kind of in-place modification is: self.x[:] = self.x + dx; instead of self.x = self.x + dx.

from lstm.

madMatchstick avatar madMatchstick commented on September 28, 2024

@peckhams Thanks for suggestion. I'll use this method,

np.maximum( self.surface_runoff_mm, 0, self.surface_runoff_mm)

from lstm.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.