Skip to content
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

Newalarm #340

Draft
wants to merge 45 commits into
base: develop
Choose a base branch
from
Draft

Newalarm #340

wants to merge 45 commits into from

Conversation

feiliuesmf and others added 30 commits June 17, 2021 11:39
…e api documentation; add test for the case ringTime is not specified.
Resolved Conflicts:
	src/Infrastructure/TimeMgr/src/ESMCI_Alarm.C
@billsacks billsacks mentioned this pull request Dec 26, 2024
@billsacks billsacks marked this pull request as draft December 26, 2024 20:26
@billsacks
Copy link
Member Author

I found what I think is an instance where the cleanup on this branch has broken some behavior, and this makes me nervous about what else might have gotten broken by the cleanup here.

The case that I think is now broken involves:

  • Setting a one-shot (i.e., ringTime but no ringInterval) sticky alarm.
  • After running the clock forward for some time, setting its direction to reverse
  • Turning off the alarm, possibly multiple times

What I noticed was that, each time the alarm is turned off, its ringTime is adjusted by one time step. It looks like this is coming from the ringerOff function, which has this code:

    if (clock->direction == ESMF_DIRECTION_FORWARD) {
      // remember this time, so if we go in reverse, we will know when
      //   to turn the alarm back on.
      //   TODO:  Assumes constant ringInterval between successive ringEnds;
      //          saves only last ringEnd.  Make ringEnd an array to save all
      //          end times, which may vary (e.g. due to variable timeSteps).
      ringEnd = clock->currTime;

    } else {      // ESMF_DIRECTION_REVERSE
      // for sticky alarms, step back ring times
      if (sticky && ringTime != firstRingTime) {
        ringTime     -= ringInterval;
        prevRingTime -= ringInterval; 

        // get clock's timestep direction: positive or negative
        bool positive =
               (clock->currAdvanceTimeStep.absValue() ==
                clock->currAdvanceTimeStep) ? true : false;

        // step back ringEnd only if it was advanced past ringTime
        //  before the clock loop ended (covers case where last ringTime
        //  equals the clock->stopTime and alarm is processed before
        //  the clockAdvance()).
        if ((positive  && ringEnd > ringTime) ||
            (!positive && ringEnd < ringTime)) {
          ringEnd -= ringInterval;
        }
      }
    }

This code – or something like it – is also on develop, but there are a few differences between this branch and develop that lead this to be a problem on this branch whereas it doesn't appear to be a problem on develop:

First, on this branch, there is code that sets ringInterval to the negative time step if it isn't set initially. (On develop, it appears that ringInterval remains 0 if it isn't set.):

      // A negative ringInterval indicates a one shot alarm at ringTime only
      TimeInterval zeroTimeInterval(0,0,1,0,0,0);
      if(clock->timeStep < zeroTimeInterval)
        alarm->ringInterval = clock->timeStep;
      alarm->ringInterval = -clock->timeStep;

Second, on this branch, it appears that firstRingTime is never set. (On develop, it looks like we never get into this conditional that adjusts ringTime for a one-shot sticky alarm, presumably because ringTime == firstRingTime (though I haven't confirmed that)... though even if we did, the adjustment would be by 0 because ringInterval is 0.)

So here's a task for this PR:

  • Fix this issue with ringTime being adjusted by a time step whenever setting an alarm to off.

Comment on lines +168 to 173
// A negative ringInterval indicates a one shot alarm at ringTime only
TimeInterval zeroTimeInterval(0,0,1,0,0,0);
if(clock->timeStep < zeroTimeInterval)
alarm->ringInterval = clock->timeStep;
alarm->ringInterval = -clock->timeStep;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • This seems problematic, particularly given the code in ringerOff that decrements ringTime by ringInterval. (See also Newalarm #340 (comment).) I think it would be better and safer to have a boolean flag that says whether this alarm rings on an interval or is a one-shot alarm.

@@ -129,59 +128,61 @@ int Alarm::count=0;
sprintf(alarm->name, "Alarm%3.3d", alarm->id);
}

if (ringTime != ESMC_NULL_POINTER) {
alarm->ringTime = alarm->prevRingTime = alarm->firstRingTime = *ringTime;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I think it's a problem that firstRingTime is no longer set: it's still referenced in ringerOff (see Newalarm #340 (comment))

Comment on lines +439 to +443
void Alarm::enableSticky(void){
// 1/100th of a second granuality to ensure integer return when being divided by
//this->ringInterval=TimeInterval(0, 1, 100, 0, 0, 0);
this->ringInterval=TimeInterval(1, 0, 1, 0, 0, 0);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • This warrants investigation: First, note that this is now using a 1 second granularity rather than the 1/100 second mentioned in the comment. But more importantly, it looks like this setting of ringInterval will differ depending on whether an alarm is initially created as sticky (in which case I don't think enableSticky is executed, and so ringInterval is either kept at what is set or set to a negative value for a one-shot), or if it's later set as sticky (in which case this enableSticky is called).

Comment on lines +1205 to +1209
if(old_direction == ESMF_DIRECTION_FORWARD)
ringTime = ringTime - ringInterval; // ringTime goes backward by 1 interval
else
ringTime = ringTime + ringInterval; // ringTime goes forward by 1 interval
}
Copy link
Member Author

@billsacks billsacks Jan 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • This warrants investigation: Although I haven't investigated carefully yet, it seems like this will result in different behavior in these two cases: (1) An alarm is set on a forward-running clock and then the clock is later set to be running in the reverse direction (so this function is called); (2) An alarm is set on a clock that is already running in reverse (in which case I don't think this function is called).
    • Is it right that the behavior differs in these two cases?
    • If so, is this intended?

I also wonder more generally about the purpose of adjusting ringTime like this when changing the clock direction. I think I understand why this is done: so that the alarm is ringing for the duration of the same Nth time step in reverse as it was forwards. But I want to confirm that this is what we want to do, because it could be confusing to a user if the ringTime is adjusted when reversing the clock's direction: what if they really intended for the ring time to be what they set it as? At the very least, we should make sure this is clearly documented – e.g., that ringTime is the time at the start of the time step in the forward direction, or the end of the time step in the reverse direction if the clock is later reversed. (I think that's the current behavior.)

Copy link
Member Author

@billsacks billsacks Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, though, that this is at least somewhat similar to what's done on develop if userChangedDirection is true.

Copy link
Member Author

@billsacks billsacks Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think my initial understanding of why this is done was wrong:

I think I was thinking about this in terms of a one-shot alarm, and I actually think we probably don't want to apply this in the case of a one-shot alarm (this is currently problematic because of the way ringInterval is set for a one-shot alarm; it should probably either be 0 or we should have an explicit flag saying if this is a one-shot alarm, and if so, should avoid doing this adjustment).

But I actually see the rationale for why this is done for a repeating alarm: If ringTime gives the next time the alarm will ring, then we reverse directions in between two rings, then we need to adjust ringTime by one ringInterval so that it gives the "next time" in this new direction.

So maybe we want this in place, but we want to avoid triggering it in some cases: for one-shot alarms and maybe some other cases:

  • e.g., what if ringTime is already exactly at the current time? (I'm not sure about this case.)
  • e.g., what if we haven't reached the first ringTime yet (i.e., this alarm hasn't rung yet)? It seems like we shouldn't adjust ringTime in this case.

This seemed like an incomplete test that wasn't testing anything new (at
least at this point, with the test being incomplete). Note that a
reverse sticky alarm is currently covered with Test_AlarmHang,
particularly after the extra testing I added in that test.
More rework is still needed for this test so that it actually tests what
it says it's testing.
It looks like this has been effectively replaced by canRingAtTime
Go for an extra couple of time steps to make sure the alarm isn't
ringing at those additional time steps. (But don't bother with this for
the test that runs both forward & reverse, because that would be
messier.)
Demonstrate that, currently, a sticky alarm operates the same way as a
non-sticky alarm - though I feel this is unintuitive behavior.
!call ESMF_AlarmDebug(alarm,'test3',rc=status)
end subroutine
!------------------------------------------------------------------------
subroutine ForwardAlarm_Test4(rc)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The current behavior as demonstrated in this test seems problematic and unintuitive, especially for sticky alarms: As demonstrated here, when the alarm dt doesn't align with the clock dt, the alarm will only ring when the two come into alignment, even for a sticky alarm. This is (I think) in contrast to the previous behavior, when the alarm would ring when we first pass the ring time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants