Skip to content

Instantly share code, notes, and snippets.

@SiccarPoint
Created December 2, 2015 00:23
Show Gist options
  • Save SiccarPoint/1bd979e42bb48d6ad0ab to your computer and use it in GitHub Desktop.
Save SiccarPoint/1bd979e42bb48d6ad0ab to your computer and use it in GitHub Desktop.
This shows the different ways one could have a component "time" parameter work
# #1: As a "run until" style:
# approach seems fiddly and annoying to me, but has advantage of always being general:
time_elapsed = 0.
dt = 5. # or whatever
while elapsed_time < total_time:
new_time = elapsed_time + dt
if new_time > total_time:
new_time = total_time - elapsed_time
my_component.run_component(new_time, *args, **kwds)
elapsed_time = new_time
# if actual "clock time" matters to the component (e.g., time of day for solar rad), this is clearly trivial
# to implement
# #2: As a timestep style.
# very easy to implement, iff total_time%dt == 0; <0.5 the lines of the other way
loops = int(total_time//dt)
assert total_time%dt == 0. # don't actually need to do this check if you know ahead of time you know this is true
for i in xrange(loops):
my_component.run_component(dt, *args, **kwds)
# if you can't be totally sure the total_time is a multiple of your dt (...but you can always *make* it so...!),
# you can do this:
loops = int(total_time//dt)
final_time_interval = total_time%dt
for i in xrange(loops):
my_component.run_component(dt, *args, **kwds)
my.component.run_component(final_time_interval, *args, **kwds)
# If your component requires an internal clock time, you'll have to track it separately. It can either be tracked
# internally to the component (I might prefer this? - after all, discritized differential equation components have
# to do this in "run until" cases already anyway), or passed in as an argument, or that kind of component just
# has a different signture that takes total time.
# Individual storm tracking is really easy this way, as the storms are easy to envision as just a duration and
# intensity pair:
from landlab.components.uniform_precip.generate_uniform_precip import PrecipitationDistribution
precip = PrecipitationDistribution(input_file)
for (dt, intensity) in precip.yield_storm_interstorm_duration_intensity():
my_component.run_component(dt, intensity=intensity)
# The equivalent set up for a run_until signature would look much uglier, and be a lot less intuitive.
"""
So, the way I see it, the dt method is almost always cleaner to work with, and arguably more pythonic
(look at that lovely generator in the final example!) but has the disadvantages that:
1. You have to be thinking about what you want to do. With run_until styles, the way it runs is totally
uniform every time (but that boilerplate text is twice as long, will run a bit slower, is arguably
more opaque, and requires logic testing in every iteration of the loop).
2. Any component that needs to know actual clock time doesn't get it "for free" any more. It either has
(a) to be tracked and passed in separately by the driver, (b) have a fundamentally nonstandard
signature, or (c) track clock time on its own internally. This actually might be a false choice anyway,
as internal tracking must already be occurring to handle the conversion of elapsed_time into
time_of_day, and we would be doing the equivalent for mass flux methods based on e.g., von Neumann
stability conditions (expressed as intervals, not clock times) if we chose the other option.
"""
@SiccarPoint
Copy link
Author

NB: the "run_component" method is just a dummy. I wasn't intending to suggest standardisation of this! I take your point on BMI, but I still like retaining component-specific method names, e.g., erode, route_flow, etc because I think it's clearer what each component is doing. There could also be components with more than one run method (e.g., the storm generating component). These would rule out using "..._for", grammatically as well. We could always have standard named wrappers for the functions in addition as a compromise.

Random thoughts:

  • Unit standardisation remains problematic. I think we can't enforce this, because different components could have totally justified need to use different order of magnitude times. Good documentation will be key, and the information is also available through var_units. Hypothetically, the property could be used to check component inter-compatibility at instantiation.
  • Steady state models just don't take it, e.g., fr.route_flow() we have already. Seems uncontroversial to me.
  • It's up to each component to subdivide the "imposed" time step if it needs to internally, clearly. Can you give an example of a case with a minimum step size?? That would be very problematic, but I can't envision a case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment