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

Duty cycle repetition #78

Closed
JW-Kraft opened this issue Aug 10, 2023 · 17 comments
Closed

Duty cycle repetition #78

JW-Kraft opened this issue Aug 10, 2023 · 17 comments
Assignees
Labels
enhancement New feature or request

Comments

@JW-Kraft
Copy link
Collaborator

I ran into this issue modeling the operation of a mill in a rural mini-grid using a duty cycle.

This is a simplified example to illustrate:

A duty cycle is in total 10min long and consists of a short start-up peak and a steady-state power demand. Switch on events can occur randomly between 8:00 and 18:00.

The issue:
Currently, RAMP models a small number of switch-on events (usually 2 to 4). The duration of these is significantly longer than one duty cycle and the load profile is “filled” with repetitions of the duty cycle.

image

The source:
In generate_load_profile (core.py) the function rand_switch_on_window is called to randomly draw the indexes of a switch-on window.

The duration of this switch-on window is randomly drawn between the user defined func_cylce and a maximum duration given either by the end of the user defined functioning window or the remaining daily functioning time of the appliance. This leads to the generation of a small number of switch-on windows that are getting smaller with every iteration. (1st iteration: duration_1 drawn from [func_cyclefunc_time], 2nd iteration: duration_2 drawn from [func_cyclefunc_time – duration_1], 3rd iteration: duration_3 drawn from [func_cycle func_time – duration_1 – duration_2] and so on)

The function update_daily_use then updates the appliance’s individual load profile (self.daily_use) at the indexes of the switch-on window using numpy.put. This repeats the passed duty cycle to fill the whole switch-on window.

For my application, this doesn’t work. I’m planning to model productive uses such as welding, milling or the use of machinery in a carpentry, where appliances are switched on many times within the working hours. Switch-on events can occur randomly throughout the working hours and shouldn’t be “lumped together” in only a few switch-on windows.

Is the observed behavior intended? For duty cycles I don’t really see any benefit.

@JW-Kraft
Copy link
Collaborator Author

I also found a fairly easy fix for the issue:

In the update_daily_use function I shorten the passend switch-on window before updating self.daily_use when a duty cycle is applied.

 if evaluate in range(self.cw11[0], self.cw11[1]) or evaluate in range(self.cw12[0], self.cw12[1]):
                # Correct selected switch_on windows for duty cycle duration
                indexes = indexes[0:len(self.random_cycle1)]    # limit switch_on_window to duration of duty_cycle
                np.put(self.daily_use, indexes, (self.random_cycle1 * coincidence))

(the same for the other duty cycles)

To keep track of the appliance functioning time I tweaked the "functioning time counter" slightly to subtract the shortened switch-on windows.

This produces the result I was looking for:

image

If the original behavior was actually unwanted I can create a pull request with the changes.

@Bachibouzouk
Copy link
Collaborator

@JW-Kraft, thanks for your issue and for a potential fix of it :)

At the moment the initial core RAMP team should be on summer holiday, we will be sure to bring the issue to the next RAMP dev meeting (in which you have been invited if I am not mistaken :) )

@FLomb FLomb self-assigned this Aug 23, 2023
@FLomb
Copy link
Contributor

FLomb commented Aug 23, 2023

Hi @JW-Kraft, thanks for opening this issue. I think the behaviour you initially experienced is what we actually wanted the first time we introduced duty cycles for appliances like fridges and freezers in the El Espino case study. Except, as you noted, it does not make as much sense when modelling appliances that do not cycle continuously.

I recall we may have realised the problem some time ago when we introduced cooking cycles, but then the solution was found in a workaround: since each cooking appliance is representative of a distinct meal/cooking task, it suffices to limit the total time of use of the given cooking appliance to the length of the cycle itself. This way, each appliance operates just for one cycle and then goes off. But this workaround does not work for the case you introduced. What is more, even for cooking, it might lead to unwanted behaviour when stochasticity messes up the imagined task/window durations.

So: I agree that a fix is needed. However, the fix should not conflict with the need for continuously cycling appliances, like fridges etc. Should we introduce another parameter (e.g., 'continuous_cycle': yes/no) to deal with this conditionality? Or can we come up with a smarter solution?

@FLomb FLomb added the enhancement New feature or request label Aug 23, 2023
@JW-Kraft
Copy link
Collaborator Author

Hi everyone,

yes I'll join the next dev meeting. Looking forward to meeting you!

@FLomb thanks for the explanation.
I don't fully understand how this models continuously cycling appliances. A fridge or freezer would be cycling throughout the whole day, with different duty cycles depending on how much it is used, right (total time of "use" = 24h)?
The only randomness then concerns if a standard, intermediate or intensive cycle is selected. Or would there be actually times where the fridge would draw no power at all?

@FLomb
Copy link
Contributor

FLomb commented Aug 23, 2023

I don't fully understand how this models continuously cycling appliances. A fridge or freezer would be cycling throughout the whole day, with different duty cycles depending on how much it is used, right (total time of "use" = 24h)
The only randomness then concerns if a standard, intermediate or intensive cycle is selected. Or would there be actually times where the fridge would draw no power at all?

The (most sophisticated) way we currently model fridges is indeed by assuming they operate non-stop for 24 hours, with cycles varying depending on the temperature and/or user activity patterns. In the model, we achieve this by stating that a fridge has a functioning time of 24 hours but different duty cycles depending on the time windows in which it operates. We can define up to 3 different cycles within a day, each cycle being associated with up to 2 different time windows. You may look at the example input_file_1 for reference.

There's also an appendix in the original publication dedicated specifically to how we model this particular behaviour of fridges. Such a prominence of fridges stems from the El Espino case study, where they were among the heaviest load and the data showed a substantial variation in their consumption depending on the factors above, which was important to capture.

@JW-Kraft
Copy link
Collaborator Author

JW-Kraft commented Aug 23, 2023

With the proposed adaptation, one could still model "nearly" continuous operation by defining the functioning time to be 24h. There would only be small gaps when a duty cycle does not "fit" anymore in between two already placed cycles:
image

In case of a fridge, I believe the gaps are actually quite realistic though:
image
(from: https://ieeexplore.ieee.org/document/6576261)

Still, to keep the current way of modeling duty cycles we could simply introduce the continuous_cycle parameter.

@FLomb
Copy link
Contributor

FLomb commented Sep 4, 2023

With the proposed adaptation, one could still model "nearly" continuous operation by defining the functioning time to be 24h. There would only be small gaps when a duty cycle does not "fit" anymore in between two already placed cycles.

This sounds like an acceptable solution. Could you try and test it via the 'qualitative testing' functionality we suggest in our docs? Essentially, you should be able to check how this change affects the output of our example input files (especially files 1 and 3, in this case).

@FLomb
Copy link
Contributor

FLomb commented Oct 25, 2023

Hi @JW-Kraft, have you had the chance to test your proposed fix? I received an email from a user today who faced a similar issue, but with application to cooking cycles. I would be happy to implement this fix if it has been properly tested.

@JW-Kraft
Copy link
Collaborator Author

Hi @FLomb, unfortunately not. I'm still traveling for my thesis project and will only be back in Berlin in mid November. Until then I will not have time to do this.
@Bachibouzouk mentioned in the dev meeting he could help implementing my changes. Could you take this over?

@FLomb
Copy link
Contributor

FLomb commented Oct 25, 2023

Ok, no worries, I just wanted to check. Actually, after further inspection, it now seems the issue found by this other user who reached out to me is unrelated to yours, so it's probably not a reason to hurry. If it turns out it's needed, I can take care of testing your proposed solution.

Enjoy your thesis work!

JW-Kraft pushed a commit to JW-Kraft/RAMP that referenced this issue Feb 2, 2024
- in Appliance -> .update_daily_use:
  - duration of switch-on event is corrected to be only as long as duty cylce
  - corrected duration of switch-on event is returned
- in Appliance -> .generate_load_profile
  - calculation of total time of operation is performed using corrected duration of switch-on event
@JW-Kraft
Copy link
Collaborator Author

JW-Kraft commented Feb 2, 2024

Testing the implemented fix
For the test use case, I model one appliance with one duty cycle and usage window with the following parameters:

func_time=4*60,  # runs for 4 hours per day
window_1=[6 * 60, 20 * 60],  # used between 6:00 and 20:00

num_windows =1,

fixed_cycle=1,  # appliance uses duty cycles

 # Duty cycle 1
p_11=8000,  # power of the first cycle
t_11=2,  # time needed for the first cycle
p_12=2000,  # power of the second cycle
t_12=18,  # time needed for the second cycle

Running an use case with this one appliance for 3 days without the fix yields the previously observed result: The duty cycles are "lumped together" in a few switch-on events with decreasing length. The number and duration of these switch-on events should be distributed as described in issue 79.
image

One additional problem: Often, short fragments of duty cycles are modeled, resulting in spikes that, in some cases, are only 1 min long.
image

Once the described fix is implemented, the load profile for the test use case looks like this:
image

Every duty cycle has an individual switch-on event. Therefore, the number of switch-on events is significantly higher. In the test case, one duty cycle has a duration of 20 min. With the defined func_time of 4 hours (240 min), we expect 12 switch-on events per day. The test checks for the number of switch-on events to match the defined duty cycle duration and functioning time.

The second problem, of duty cycles being shortened to short spikes, to fill the remaining func_time of a day is fixed in this case, because func_time is a integer multiple of the duty cycle duration making 12 duty cycles "fit exactly". If this is not the case or a random variation is defined for func_cycle or the duty cycle duration, one duty cycle fragment "spike" might still exist. (the last one to "fill up" the remaining func_time. This issue is similar to the problem described in issue 103 and cannot be fixed within this issue.

JW-Kraft pushed a commit to JW-Kraft/RAMP that referenced this issue Feb 2, 2024
@JW-Kraft
Copy link
Collaborator Author

JW-Kraft commented Feb 2, 2024

When I run the unit tests, 3 other tests are failing. Could you please take a look at it @Bachibouzouk ?
image

@Bachibouzouk
Copy link
Collaborator

When I run the uni tests, 3 other tests are failing. Could you please take a look at it @Bachibouzouk ?

It seems they fail because you don't have the example files locally or because the filepath is not Windows compatible (I assume you have windows, sorry if it is not the case). These fail not because of anything you might have changed, we could look at it in a bilateral meeting

@JW-Kraft
Copy link
Collaborator Author

JW-Kraft commented Feb 2, 2024

Yes, that's what I assumed. This should be ok for now, right?

@JW-Kraft
Copy link
Collaborator Author

JW-Kraft commented Feb 2, 2024

I just realized that the qualitative tests with the input files in "ramp/example/" don't work as described in the docs. Their results are noch actually saved in the folder "ramp/test/".

What is currently the best way to do qualitative testing? Should I just add a few lines of code to the input files or am I missing something?

@Bachibouzouk
Copy link
Collaborator

The qualitative testing was done with human eye, so far I can tell, @FLomb knows more as those tests were already there when I joined the devs team

JW-Kraft pushed a commit to JW-Kraft/RAMP that referenced this issue Feb 6, 2024
- in Appliance -> .update_daily_use:
  - duration of switch-on event is corrected to be only as long as duty cylce
  - corrected duration of switch-on event is returned
- in Appliance -> .generate_load_profile
  - calculation of total time of operation is performed using corrected duration of switch-on event
JW-Kraft pushed a commit to JW-Kraft/RAMP that referenced this issue Feb 6, 2024
Bachibouzouk pushed a commit to JW-Kraft/RAMP that referenced this issue Mar 19, 2024
- in Appliance -> .update_daily_use:
  - duration of switch-on event is corrected to be only as long as duty cylce
  - corrected duration of switch-on event is returned
- in Appliance -> .generate_load_profile
  - calculation of total time of operation is performed using corrected duration of switch-on event
Bachibouzouk pushed a commit to JW-Kraft/RAMP that referenced this issue Mar 19, 2024
@Bachibouzouk
Copy link
Collaborator

Closed by #111

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

No branches or pull requests

3 participants