-
Notifications
You must be signed in to change notification settings - Fork 195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(0.95.3) Reset model clock and skip time_step!
ing if next actuation time is tiny
#3606
base: main
Are you sure you want to change the base?
Conversation
src/Simulations/run.jl
Outdated
time_step!(sim.model, Δt, callbacks=model_callbacks) | ||
else | ||
println("Skipping aligned time step, which is of ", Δt) | ||
sim.model.clock.time = next_actuation_time(sim) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this also be
sim.model.clock.time = next_actuation_time(sim) | |
sim.model.clock.time += Δt |
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this works when we're almost at next_actuation_time
since it would skip that actuation time, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is
sim.model.clock.time + Δt
vs
next_actuation_time(sim)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tomchor still have this question
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry I missed this. It's been so long that I forgot the details, but looking at the code for both, it's possible they end up calculating the same thing since Δt
is calculated with aligned_time_step()
.
Nice work! I'm curious about the criteria. Should it be something like dt = 10 * eps(dt) * sim.dt ? Or does it have to be larger than that (hence the factor 1e10). It'd be nice not to have to define |
I actually don't know what the proper criterion should be. With the one you proposed, the error doesn't go away in this example since the tiny time-step is about
Yeah, agree. I'm not sure of a good workaround here though. Do you have suggestions? For the time being we can just set a fallback method as Also, nice to see that tests pass and nothing is breaking :) |
I did a few tests with some criteria for timestep-skipping with a couple of my own simulations in addition to the MWE included here. In summary:
So only options 1 and 4 fully fix the problem (at least in the simulations I've tried so far). For me both those options rely on pretty arbitrary numbers though, so I'm not very happy with neither. From the point of view seeing the timestep-skipping as an approximation ( I see three possible ways to go about it right now:
|
Note that |
@glwagner are you okay if I just add |
This isn't a problem of the nonhydrostatic model specifically. Why would we add it as a property there? |
This PR is supposed to fix the output of the pressure, which can be unreasonably large if the time step is really small due to, I believe, the nonhydrostatic pressure correction, which is specific to the This wouldn't necessarily fix #3056, which may be what you're thinking of. I can also add |
I think we should fix the problem once. Otherwise we'll end up with unnecessary code somewhere that has to be deleted. |
@glwagner Can you please be clearer? Does that mean adding |
I think the solution discussed here, where the time-step change associated with If we could indeed solve the problem simply by eliminating round off error, then this would almost certainly be preferred since it might be much simpler (eg just fixing an floating-point-unstable arithmetic operation by rearranging terms). That could be really easy. @Sbozzolo might be able to help because I believe they do something special to avoid round off issues in I would hesitate to establish an absolute |
I'll try to get going with the MWE here. Is the immersed boundary acually part of the MWE (ie the error does not occur with it?) And buoyancy? |
It seems that without the immersed boundary, there is still a problem of super small time-steps, but the pressure does not blow up. |
Ok getting closer maybe... I think this problem is generic and cannot be solved in general for arbitrary time steps. Here's a few thoughts:
|
PR #3617 helps with this MWE |
With regards to floating point instabilities due to arithmetic with time and time intervals, ultimately, we will be solving this issue (and others) by moving away from a floating point time in favor of an integer one (e.g., milliseconds from a start date). As a fun fact, if you are using Float32 and keep track of your time in seconds, |
Looks good, maybe bump the minor version |
Done! I wanna test this code in my actual simulation before sending it off for reviews though. Also, I'm thinking of using the MWE at the top comment as a test. It's the most reliable way I could reproduce this issue. Thoughts? |
Co-authored-by: Gregory L. Wagner <[email protected]>
I would just put in a very simple test that time-stepping is skipped correctly, by manually taking two time-steps and changing I think the kind of test you are describing may not be appropriate for CI; it's more a validation test. I think if you can put a unit test to show the feature works correctly, you can later show that using the feature solves pressure solver issues and be happy that the unit test ensures it will continue to work as in the validation test. |
I tested this branch on my simulation and things work fine. However, when trying to include a test I came across some weird behavior. Namely the snippet below fails: using Oceananigans
using Test
grid = RectilinearGrid(size=(1, 1, 1), extent=(1, 1, 1))
model = NonhydrostaticModel(; grid)
simulation = Simulation(model, Δt=3, stop_iteration=1)
run!(simulation)
stop_time = 1.0
simulation = Simulation(model, Δt=1; stop_time)
run!(simulation)
@test simulation.model.clock.time == stop_time with
If I re-build using Oceananigans
using Test
grid = RectilinearGrid(size=(1, 1, 1), extent=(1, 1, 1))
model = NonhydrostaticModel(; grid)
simulation = Simulation(model, Δt=3, stop_iteration=1)
run!(simulation)
stop_time = 1.0
model = NonhydrostaticModel(; grid)
simulation = Simulation(model, Δt=1; stop_time)
run!(simulation)
@test simulation.model.clock.time == stop_time So there seems to be some attribute of |
Δt = aligned_time_step(sim, sim.Δt) | ||
if Δt < sim.minimum_relative_step * sim.Δt | ||
next_time = next_actuation_time(sim) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, next_actuation_time(sim)
and sim.model.clock.time + Δt
are equivalent. I chose to use the former here to avoid errors related to the addition operation. But let me know if I should change it to just use the latter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't it complicate the code to define next_actuation_time
in addition to aligned_time_step
? Things go wrong if you change one but not the other. It's usually best to have one "source" of reality / truth
Indeed, the clock is not reset to zero when the simulation is built, so you are restarting from |
Ah, true! I'm ashamed I forgot about that. Although, in this case shouldn't the simulation just not iterate and keep the clock at 3? |
time_step!
ing if next actuation time is tinytime_step!
ing if next actuation time is tiny
@@ -88,7 +93,8 @@ function Simulation(model; Δt, | |||
0.0, | |||
false, | |||
false, | |||
verbose) | |||
verbose, | |||
Float64(minimum_relative_step)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why Float64?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was following the other floats in the Simulation
constructor, which are also converted to Float64
. I can't remember the PR where this was decided, but it minimizes errors in time-step alignment. The error that this PR solves is an example of this type of error .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably we should make it optional.
This will not work if we want to support Metal architectures that do not want Float64.
Closes #3593
This is somewhat of a hack to investigate (and eventually solve) #3593. For now I just want to investigate if this breaks too much stuff in the code, but it does seem to solve the problem. Here's a MWE to demonstrate:
On main this produces stuff like:
The last two lines are of note where we went from
time: 99.99999999999999
totime: 100.0
, implying a very tiny time-step, which results in a weird pressure field, as quantified by the last output of the last line:std(pNHS) = 2.72e+10
. Note that because of this,time
anditeration×Δt
don't match up anymore in the last line. Namelytime: 100.0, iteration×Δt: 125.0
. This "misstep" happens many times throughout the run onmain
.On this branch this doesn't happen anymore, and even after many time-steps things remain aligned (albeit with a very small round-off error):
Ideally the way to really fix this would be to figure out a way to avoid round-off errors, but I haven't been able to do that yet.