-
Notifications
You must be signed in to change notification settings - Fork 61
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
Stricter checks on network construction / simulation parameters. #2264
base: master
Are you sure you want to change the base?
Changes from 3 commits
6a5bdc4
4fb5476
12261a2
2739392
0893669
4797b9f
b85e8e6
73acec6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,8 +54,8 @@ struct cell_connection_base { | |
|
||
cell_connection_base(L src, cell_local_label_type dst, float w, const U::quantity& d): | ||
source(std::move(src)), target(std::move(dst)), weight(w), delay(d.value_as(U::ms)) { | ||
if (std::isnan(weight)) throw std::out_of_range("Connection weight must be finite."); | ||
if (std::isnan(delay) || delay < 0) throw std::out_of_range("Connection delay must be non-negative and infinite in units of [ms]."); | ||
if (std::isnan(weight)) throw std::domain_error("Connection weight must be finite."); | ||
if (std::isnan(delay) || delay <= 0) throw std::domain_error("Connection delay must be positive, finite, and given in units of [ms]."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inasmuch as we're presuming NaNs work as expected, we could just have the test Do we really need to enforce that delay is finite? If so, then the test should include that. Also, not being familiar (yet) with how LLNL units works, why do we need to specify that the quantity is in milliseconds? Can't we just convert as required or else assert in the type? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, the two issues go hand in hand: |
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have quite specific exceptions such as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a bit torn here, |
||
}; | ||
|
||
|
@@ -69,7 +69,7 @@ struct gap_junction_connection { | |
|
||
gap_junction_connection(cell_global_label_type peer, cell_local_label_type local, double g): | ||
peer(std::move(peer)), local(std::move(local)), weight(g) { | ||
if (std::isnan(weight)) throw std::out_of_range("Gap junction weight must be finite."); | ||
if (std::isnan(weight)) throw std::domain_error("Gap junction weight must be finite."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar comments as above: testing finite or testing Nan? We should use an arbor exception. |
||
} | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
#include "threading/threading.hpp" | ||
#include "util/maputil.hpp" | ||
#include "util/span.hpp" | ||
#include "util/strprintf.hpp" | ||
#include "profile/profiler_macro.hpp" | ||
|
||
namespace arb { | ||
|
@@ -355,7 +356,7 @@ void simulation_state::reset() { | |
time_type simulation_state::run(time_type tfinal, time_type dt) { | ||
// Progress simulation to time tfinal, through a series of integration epochs | ||
// of length at most t_interval_. t_interval_ is chosen to be no more than | ||
// than half the network minimum delay. | ||
// than half the network minimum delay and minimally the timestep `dt`. | ||
// | ||
// There are three simulation tasks that can be run partially in parallel: | ||
// | ||
|
@@ -394,10 +395,12 @@ time_type simulation_state::run(time_type tfinal, time_type dt) { | |
// Requires state at end of run(), with epoch_.id==k: | ||
// * U(k) and D(k) have completed. | ||
|
||
if (!std::isfinite(tfinal) || tfinal < 0) throw std::domain_error("simulation: tfinal must be finite, positive, and in [ms]"); | ||
if (!std::isfinite(dt) || tfinal < 0) throw std::domain_error("simulation: dt must be finite, positive, and in [ms]"); | ||
|
||
if (tfinal<=epoch_.t1) return epoch_.t1; | ||
// Compare up to picoseconds | ||
time_type eps = 1e-9; | ||
if (!std::isfinite(dt) || dt < eps) throw std::domain_error("simulation: dt must be finite, positive, and in [ms]"); | ||
if (dt - t_interval_ > eps) throw std::domain_error(util::pprintf("simulation: dt={}ms is larger than epoch length by {}, chose at most half the minimal connection delay {}ms.", dt, dt - t_interval_, t_interval_)); | ||
if (!std::isfinite(tfinal) || tfinal < eps) throw std::domain_error("simulation: tfinal must be finite, positive, and in [ms]"); | ||
if (tfinal - epoch_.t1 < dt) throw std::domain_error(util::pprintf("simulation: tfinal={}ms doesn't make progress of least one dt; current time of simulation is {}ms and dt {}ms", tfinal, epoch_.t1, dt)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems all a bit fiddly; as mentioned in the general comments, I think we should just leave dt interpretation up to the integrators and they can make a sensible choice, e.g. clip it by epoch duration or interpret very small dt values as being larger, based on whatever they have to do. Having eps here is untidy because it splits the responsibility for sane dt interpretation between the main loop and the integrators. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, if someone wants to run arbor for ever, do we really need to stop them? We already have a check for tfinal being less than the end time of the preceding epoch, where we return the correct 'simulated up to' time, so we don't really need tests for it being zero, or negative; in fact a tfinal of zero should be a valid no-op in my opinion. In short, a isnan test for tfinal remains sufficient, I believe. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In endeffect you are suggesting that instead of throwing an error, the cell groups' individual There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No we don't have to pass it down the stack: we know epochs are at most min_delay/2 long. The epoch already has all the info they need, and if there is more info it will be in the cell kind global data, which they also have. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #2053 is just cable cells, and I still want to partially revert it so that we can use flexible time steps with a different cable cell integrator, even if it's just to make them line up correctly with epoch intervals. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As an example, if we have the last epoch (ending on tfinal) having duration 3.1 dt, wouldn't it be best to set the fixed dt for that epoch to be e.g. the duration/4 and then we would know that all cell states across cell groups were actually at the same integration time? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Technically I agree. Practically I know that I'd spent way too much time looking for the reason why my simulation did nothing in that situation. Especially since our There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a general principle, I think we should keep things general unless there is a good reason not to. I mean, the parameter is literally called We can always change the name to The semantics of running a simulation from t = 0 for 0 seconds should be that the state reflects the initial conditions. It's not what we'd expect someone to do in normal circumstances, but it's the consistent result, and may arise in circumstances where the simulator is being driven by another process or co-simulation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Damn you and your arguments :D |
||
|
||
// Compute following epoch, with max time tfinal. | ||
auto next_epoch = [tfinal](epoch e, time_type interval) -> epoch { | ||
|
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.
Message doesn't match test: should this test be
!std::isfinite(weight)
?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.
That's better, yes.