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

Computation speed up via parallelization #54

Merged

Conversation

Bachibouzouk
Copy link
Collaborator

@Bachibouzouk Bachibouzouk commented Nov 23, 2022

See the updated documentation for a usecase

The command

ramp -i <some input file> -n 365

takes 1min 40 seconds to complete on the develpoment branch.

The same command takes 22 seconds to complete on this branch

and the command

ramp -i <some input file> -n 365 -p

takes 18 seconds ( I have 4 cores on my laptop)

There might be room for further improvement playing with the parameter chunksize of the imap_unordered function of multiprocessing package and maybe the parallelization help more on larger usecases and with more cores

@Bachibouzouk
Copy link
Collaborator Author

Bachibouzouk commented Dec 2, 2022

numpy.random might be much faster than random.random, this should be tried

--> this is the case if we can generate a lot of numbers to be used then, but not if we have to iterate and adapt the free windows of time available for switch on events

@Bachibouzouk
Copy link
Collaborator Author

@FLomb @mohammadamint - it seems to me a lot of time is wasted picking switch_on wrongly (ie the remaining window size is smaller than the time the appliance should be on and the switch_on must be resampled). For an appliance we know the windows of available times before we enter the while loop. Each time indexes of the switch_on event are computed and if they fit within the window, there are assigned, thus reducing the range of "free_spots". By simply keeping track of the start and stop times of the free_spots we can easily know in advance that a switch on event will not be possible within a remaining free spot (for example if the minimal on time of the appliance is 100 and there is no range within random_window1, it is pointless to even pick the switch on even within this window. The algorithm rules it out, but only after having already calculated the indexes which will not be used.
I think it would make more sense to pick free_spots cleverly and avoid unnecessary calculations, like if you have only 4 out of 7 remaining free spots which could fit, then we can randomly select one of the 4 spots and not consider the 3 spots which will never fit anyway.
What do you think?

@FLomb
Copy link
Contributor

FLomb commented Jan 17, 2023

@FLomb @mohammadamint - it seems to me a lot of time is wasted picking switch_on wrongly (ie the remaining window size is smaller than the time the appliance should be on and the switch_on must be resampled). For an appliance we know the windows of available times before we enter the while loop. Each time indexes of the switch_on event are computed and if they fit within the window, there are assigned, thus reducing the range of "free_spots". By simply keeping track of the start and stop times of the free_spots we can easily know in advance that a switch on event will not be possible within a remaining free spot (for example if the minimal on time of the appliance is 100 and there is no range within random_window1, it is pointless to even pick the switch on even within this window. The algorithm rules it out, but only after having already calculated the indexes which will not be used. I think it would make more sense to pick free_spots cleverly and avoid unnecessary calculations, like if you have only 4 out of 7 remaining free spots which could fit, then we can randomly select one of the 4 spots and not consider the 3 spots which will never fit anyway. What do you think?

What you suggest makes a lot of sense to me. I am convinced that the greatest margins for improving the code lie in these little details that were not done efficiently back then

@Bachibouzouk
Copy link
Collaborator Author

What you suggest makes a lot of sense to me. I am convinced that the greatest margins for improving the code lie in these little details that were not done efficiently back then

Good, I didn't want to start implementing it before you had your say on it, maybe the code is like it is because of some other reason and I want to avoid breaking anything :) I will hopefully come up with more speed improvements before our next meeting ^^

@Bachibouzouk
Copy link
Collaborator Author

Bachibouzouk commented Jan 20, 2023

This is what the aggregated profiles of a year looked like before

comp_model_old

And this is what the aggregated profiles for a year look like after the modification of the switch on event

comp_year_new

I guess this is not acceptable and I will try to see where the two diverge

EDIT I made a typo mistake between func_time and func_cycle variables ... this is what the profiles look like after correction:
comp_year_newest

@Bachibouzouk
Copy link
Collaborator Author

Thes are the profiling before and after

calc_peak_time_range required0.0002579689025878906 seconds for execution.
         1742875 function calls (1702265 primitive calls) in 1.365 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    1.365    1.365 cProfile.py:106(runcall)
        1    0.002    0.002    1.365    1.365 simple.py:52(calculate)
     1000    0.040    0.000    1.363    0.001 core.py:1208(generate_load_profile)
     3610    0.016    0.000    0.586    0.000 core.py:897(update_daily_use)
79341/44951    0.086    0.000    0.434    0.000 {built-in method numpy.core._multiarray_umath.implement_array_function}
     3610    0.006    0.000    0.425    0.000 core.py:1976(masked_greater_equal)
     4610    0.013    0.000    0.268    0.000 core.py:1828(masked_where)
     4610    0.045    0.000    0.216    0.000 core.py:1010(__call__)
    20050    0.097    0.000    0.209    0.000 core.py:2966(__array_finalize__)
     2610    0.003    0.000    0.200    0.000 extras.py:1808(notmasked_contiguous)
     3610    0.002    0.000    0.195    0.000 <__array_function__ internals>:177(in1d)
     3610    0.069    0.000    0.189    0.000 arraysetops.py:524(in1d)
     2610    0.146    0.000    0.150    0.000 extras.py:1751(flatnotmasked_contiguous)
33490/27270    0.012    0.000    0.117    0.000 {method 'view' of 'numpy.ndarray' objects}
     8220    0.005    0.000    0.113    0.000 <__array_function__ internals>:177(zeros_like)
    11830    0.016    0.000    0.109    0.000 core.py:1555(make_mask)
     8220    0.012    0.000    0.102    0.000 numeric.py:77(zeros_like)
    23660    0.057    0.000    0.090    0.000 core.py:2940(_update_from)
    40790    0.080    0.000    0.080    0.000 {method 'reduce' of 'numpy.ufunc' objects}
     3790    0.026    0.000    0.075    0.000 core.py:1153(calc_indexes_for_rand_switch_on)
     8220    0.005    0.000    0.071    0.000 <__array_function__ internals>:177(empty_like)
    34880    0.019    0.000    0.069    0.000 {built-in method numpy.array}
     7220    0.004    0.000    0.068    0.000 <__array_function__ internals>:177(put)
     1000    0.002    0.000    0.066    0.000 core.py:2054(masked_not_equal)
    22660    0.010    0.000    0.061    0.000 {method 'any' of 'numpy.ndarray' objects}
    18130    0.017    0.000    0.060    0.000 fromnumeric.py:69(_wrapreduction)
     7220    0.003    0.000    0.055    0.000 fromnumeric.py:486(put)
     4405    0.014    0.000    0.055    0.000 core.py:1136(switch_on)
    22660    0.007    0.000    0.052    0.000 _methods.py:55(_any)
     3000    0.034    0.000    0.051    0.000 core.py:927(calc_rand_window)
    11830    0.005    0.000    0.049    0.000 core.py:587(filled)
     2610    0.004    0.000    0.048    0.000 core.py:8025(asarray)
     3610    0.007    0.000    0.046    0.000 core.py:4741(put)
     3610    0.005    0.000    0.044    0.000 core.py:3115(view)
     2610    0.009    0.000    0.044    0.000 core.py:2808(__new__)
     4610    0.009    0.000    0.042    0.000 core.py:3770(filled)
    18440    0.010    0.000    0.041    0.000 <__array_function__ internals>:177(copyto)
     4610    0.002    0.000    0.039    0.000 core.py:3510(mask)
    13830    0.017    0.000    0.039    0.000 _ufunc_config.py:33(seterr)
    11830    0.005    0.000    0.037    0.000 core.py:1545(_shrink_mask)
     7170    0.004    0.000    0.037    0.000 <__array_function__ internals>:177(amin)
     4610    0.030    0.000    0.036    0.000 core.py:3429(__setmask__)
     3610    0.009    0.000    0.033    0.000 core.py:1704(mask_or)
     7170    0.003    0.000    0.031    0.000 <__array_function__ internals>:177(amax)
     6000    0.003    0.000    0.030    0.000 <__array_function__ internals>:177(diff)
     7170    0.006    0.000    0.029    0.000 fromnumeric.py:2829(amin)
    19669    0.027    0.000    0.029    0.000 random.py:415(uniform)
     1000    0.010    0.000    0.029    0.000 core.py:1118(rand_total_time_of_use)
     3790    0.003    0.000    0.029    0.000 <__array_function__ internals>:177(any)
     7170    0.005    0.000    0.024    0.000 fromnumeric.py:2703(amax)
     6000    0.021    0.000    0.023    0.000 function_base.py:1320(diff)
    15440    0.004    0.000    0.023    0.000 core.py:1329(make_mask_descr)
    16825    0.022    0.000    0.022    0.000 {built-in method numpy.arange}
     7220    0.005    0.000    0.021    0.000 core.py:1424(getmaskarray)
     4610    0.003    0.000    0.021    0.000 _ufunc_config.py:430(__enter__)
     3790    0.004    0.000    0.021    0.000 fromnumeric.py:2333(any)
   239820    0.020    0.000    0.020    0.000 {built-in method builtins.getattr}
    15440    0.011    0.000    0.018    0.000 core.py:1315(_replace_dtype_fields)
   100250    0.015    0.000    0.015    0.000 {method 'update' of 'dict' objects}
     3000    0.003    0.000    0.015    0.000 numeric.py:290(full)
    70980    0.015    0.000    0.015    0.000 core.py:3401(dtype)
     4405    0.003    0.000    0.015    0.000 <__array_function__ internals>:177(concatenate)
    13830    0.013    0.000    0.015    0.000 _ufunc_config.py:132(geterr)
    46320    0.009    0.000    0.013    0.000 core.py:1362(getmask)
     9220    0.008    0.000    0.013    0.000 core.py:671(getdata)
     4610    0.003    0.000    0.013    0.000 _ufunc_config.py:435(__exit__)
    10830    0.012    0.000    0.012    0.000 {method 'put' of 'numpy.ndarray' objects}
    10755    0.012    0.000    0.012    0.000 getlimits.py:668(__init__)
     4610    0.008    0.000    0.011    0.000 core.py:644(get_masked_subclass)
   110690    0.011    0.000    0.011    0.000 {built-in method builtins.isinstance}
     3610    0.002    0.000    0.010    0.000 <__array_function__ internals>:177(shape)
    17415    0.009    0.000    0.009    0.000 {built-in method numpy.zeros}
     8220    0.004    0.000    0.009    0.000 utils.py:90(random_variation)
     4405    0.003    0.000    0.009    0.000 random.py:285(choice)
    11830    0.005    0.000    0.008    0.000 core.py:3676(_get_data)
     3610    0.004    0.000    0.008    0.000 core.py:433(_check_fill_value)
    28880    0.007    0.000    0.007    0.000 core.py:3417(shape)
     7195    0.003    0.000    0.007    0.000 {built-in method builtins.all}
    15440    0.007    0.000    0.007    0.000 core.py:1283(_replace_dtype_fields_recursive)
     8220    0.007    0.000    0.007    0.000 {method 'astype' of 'numpy.ndarray' objects}
     3000    0.007    0.000    0.007    0.000 {built-in method numpy.empty}
     4610    0.002    0.000    0.006    0.000 core.py:1644(make_mask_none)
    18130    0.006    0.000    0.006    0.000 fromnumeric.py:70(<dictcomp>)
     3610    0.004    0.000    0.006    0.000 fromnumeric.py:1991(shape)
     5610    0.005    0.000    0.005    0.000 {method 'view' of 'numpy.generic' objects}
    13830    0.005    0.000    0.005    0.000 {built-in method numpy.seterrobj}
     4405    0.004    0.000    0.005    0.000 random.py:250(_randbelow_with_getrandbits)
    37390    0.004    0.000    0.004    0.000 {built-in method builtins.len}
    13830    0.004    0.000    0.004    0.000 {built-in method numpy.asarray}
    27660    0.004    0.000    0.004    0.000 {built-in method numpy.geterrobj}
    10830    0.004    0.000    0.004    0.000 arraysetops.py:630(<genexpr>)
     1486    0.001    0.000    0.003    0.000 <__array_function__ internals>:177(where)
     3610    0.002    0.000    0.003    0.000 core.py:1171(calc_coincident_switch_on)
     8501    0.003    0.000    0.003    0.000 {built-in method builtins.min}
     3610    0.003    0.000    0.003    0.000 {method 'copy' of 'numpy.ndarray' objects}
    19050    0.003    0.000    0.003    0.000 {built-in method builtins.hasattr}
    18440    0.002    0.000    0.002    0.000 multiarray.py:1079(copyto)
     7170    0.002    0.000    0.002    0.000 getlimits.py:692(max)
     9830    0.002    0.000    0.002    0.000 {method 'ravel' of 'numpy.ndarray' objects}
    19669    0.002    0.000    0.002    0.000 {method 'random' of '_random.Random' objects}
     6195    0.002    0.000    0.002    0.000 {built-in method builtins.max}
    26894    0.002    0.000    0.002    0.000 {method 'append' of 'list' objects}
     4610    0.002    0.000    0.002    0.000 core.py:658(<listcomp>)
    18130    0.002    0.000    0.002    0.000 {method 'items' of 'dict' objects}
    12830    0.002    0.000    0.002    0.000 {built-in method builtins.issubclass}
     3585    0.001    0.000    0.001    0.000 getlimits.py:679(min)
     4610    0.001    0.000    0.001    0.000 _ufunc_config.py:426(__init__)
     8220    0.001    0.000    0.001    0.000 numeric.py:73(_zeros_like_dispatcher)
     6000    0.001    0.000    0.001    0.000 {built-in method numpy.core._multiarray_umath.normalize_axis_index}
     1486    0.001    0.000    0.001    0.000 core.py:1156(<listcomp>)
     7220    0.001    0.000    0.001    0.000 fromnumeric.py:482(_put_dispatcher)
     7170    0.001    0.000    0.001    0.000 fromnumeric.py:2824(_amin_dispatcher)
     8220    0.001    0.000    0.001    0.000 multiarray.py:84(empty_like)
     7170    0.001    0.000    0.001    0.000 fromnumeric.py:2698(_amax_dispatcher)
     3610    0.001    0.000    0.001    0.000 constants.py:1(switch_on_parameters)
     6259    0.001    0.000    0.001    0.000 {method 'getrandbits' of '_random.Random' objects}
     6000    0.001    0.000    0.001    0.000 function_base.py:1316(_diff_dispatcher)
     6000    0.001    0.000    0.001    0.000 {built-in method numpy.asanyarray}
     3790    0.001    0.000    0.001    0.000 fromnumeric.py:2328(_any_dispatcher)
     4405    0.001    0.000    0.001    0.000 multiarray.py:152(concatenate)
     1000    0.001    0.000    0.001    0.000 {built-in method builtins.round}
     3610    0.001    0.000    0.001    0.000 arraysetops.py:519(_in1d_dispatcher)
     3610    0.001    0.000    0.001    0.000 fromnumeric.py:1987(_shape_dispatcher)
     4405    0.000    0.000    0.000    0.000 {method 'bit_length' of 'int' objects}
     1000    0.000    0.000    0.000    0.000 core.py:872(assign_random_cycles)
     1486    0.000    0.000    0.000    0.000 multiarray.py:345(where)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {method 'enable' of '_lsprof.Profiler' objects}


calc_peak_time_range required0.0002899169921875 seconds for execution.
         231437 function calls in 0.227 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.227    0.227 cProfile.py:106(runcall)
        1    0.001    0.001    0.227    0.227 simple.py:52(calculate)
     1000    0.024    0.000    0.225    0.000 core.py:1276(generate_load_profile)
     3447    0.020    0.000    0.052    0.000 core.py:1240(calc_coincident_switch_on)
    14611    0.010    0.000    0.050    0.000 {built-in method numpy.core._multiarray_umath.implement_array_function}
     3472    0.028    0.000    0.044    0.000 core.py:1196(rand_switch_on_window)
     3000    0.027    0.000    0.036    0.000 core.py:978(calc_rand_window)
     3447    0.005    0.000    0.030    0.000 core.py:952(update_daily_use)
     2582    0.001    0.000    0.022    0.000 <__array_function__ internals>:177(amax)
     2582    0.002    0.000    0.020    0.000 fromnumeric.py:2703(amax)
     3000    0.002    0.000    0.018    0.000 <__array_function__ internals>:177(diff)
     2582    0.005    0.000    0.017    0.000 fromnumeric.py:69(_wrapreduction)
     9472    0.004    0.000    0.015    0.000 random.py:244(randint)
     3000    0.003    0.000    0.015    0.000 numeric.py:290(full)
     3000    0.011    0.000    0.014    0.000 function_base.py:1320(diff)
     9472    0.006    0.000    0.012    0.000 random.py:200(randrange)
     3447    0.010    0.000    0.011    0.000 core.py:915(update_available_time_for_switch_on_events)
     3447    0.002    0.000    0.010    0.000 <__array_function__ internals>:177(put)
     2582    0.010    0.000    0.010    0.000 {method 'reduce' of 'numpy.ufunc' objects}
    12501    0.007    0.000    0.008    0.000 random.py:415(uniform)
     3000    0.006    0.000    0.006    0.000 {built-in method numpy.empty}
     9472    0.004    0.000    0.006    0.000 random.py:250(_randbelow_with_getrandbits)
     6054    0.006    0.000    0.006    0.000 {built-in method numpy.arange}
     3447    0.001    0.000    0.005    0.000 fromnumeric.py:486(put)
     4447    0.002    0.000    0.004    0.000 utils.py:90(random_variation)
     3447    0.004    0.000    0.004    0.000 {method 'put' of 'numpy.ndarray' objects}
     3000    0.001    0.000    0.004    0.000 <__array_function__ internals>:177(copyto)
     2582    0.001    0.000    0.004    0.000 <__array_function__ internals>:177(where)
     1000    0.002    0.000    0.003    0.000 core.py:1169(rand_total_time_of_use)
     3000    0.002    0.000    0.002    0.000 {built-in method numpy.asanyarray}
      865    0.001    0.000    0.001    0.000 random.py:616(gauss)
     3000    0.001    0.000    0.001    0.000 {built-in method numpy.asarray}
     2582    0.001    0.000    0.001    0.000 {built-in method builtins.getattr}
     4337    0.001    0.000    0.001    0.000 {built-in method builtins.min}
     2582    0.001    0.000    0.001    0.000 fromnumeric.py:70(<dictcomp>)
    13365    0.001    0.000    0.001    0.000 {method 'random' of '_random.Random' objects}
    13814    0.001    0.000    0.001    0.000 {method 'getrandbits' of '_random.Random' objects}
     3447    0.001    0.000    0.001    0.000 utils.py:194(within_peak_time_window)
     1000    0.001    0.000    0.001    0.000 {built-in method numpy.zeros}
     7473    0.001    0.000    0.001    0.000 {built-in method builtins.len}
     1000    0.001    0.000    0.001    0.000 core.py:1337(<listcomp>)
     9472    0.001    0.000    0.001    0.000 {method 'bit_length' of 'int' objects}
     6858    0.001    0.000    0.001    0.000 {method 'insert' of 'list' objects}
     3447    0.001    0.000    0.001    0.000 {method 'pop' of 'list' objects}
     3447    0.001    0.000    0.001    0.000 constants.py:1(switch_on_parameters)
     3000    0.001    0.000    0.001    0.000 {built-in method numpy.core._multiarray_umath.normalize_axis_index}
     3447    0.000    0.000    0.000    0.000 fromnumeric.py:482(_put_dispatcher)
     1000    0.000    0.000    0.000    0.000 {built-in method builtins.round}
     3000    0.000    0.000    0.000    0.000 multiarray.py:1079(copyto)
     3000    0.000    0.000    0.000    0.000 function_base.py:1316(_diff_dispatcher)
     2582    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
     2582    0.000    0.000    0.000    0.000 multiarray.py:345(where)
     2582    0.000    0.000    0.000    0.000 fromnumeric.py:2698(_amax_dispatcher)
     3000    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
      865    0.000    0.000    0.000    0.000 {built-in method builtins.max}
      865    0.000    0.000    0.000    0.000 {built-in method math.ceil}
      432    0.000    0.000    0.000    0.000 {built-in method math.log}
     1000    0.000    0.000    0.000    0.000 core.py:891(assign_random_cycles)
      432    0.000    0.000    0.000    0.000 {built-in method math.cos}
      432    0.000    0.000    0.000    0.000 {built-in method math.sin}
      432    0.000    0.000    0.000    0.000 {built-in method math.sqrt}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {method 'enable' of '_lsprof.Profiler' objects}

@Bachibouzouk
Copy link
Collaborator Author

@FLomb - I would like to merge this PR before we merge development into master, I will push on it today so you can review it

@Bachibouzouk Bachibouzouk force-pushed the feature/vectorize_only_apps branch 3 times, most recently from 5265e89 to 7b311b7 Compare February 6, 2023 09:43
Change profiling of 1000s profiles from 2.657s --> 1.965s
As ma.masked_greater_equal returns a masked array from an
unmasked one there was no need to provide a masked one
as input argument

    Change profiling of 1000s profiles from 1.241s --> 1.055s
Change profiling of 1000s profiles from 1.055 --> 0.947s
The operation of ma.notmasked_contiguous on daily_use_masked
takes quite some time and can be replaced keeping a list of
available ranges.
The method update_available_time_for_switch_on_events update
this list given the time indexes of a new switch on event
Profiling time went from 1s --> 0.365s
If tot_time == rand_time, then the loop will run one more time
and in that case the index_adj will always be empty, thus no
need to update the daily_use attribute
from 0.241 to 0.210s
The problem was the identification of the correct free
window of time
@Bachibouzouk
Copy link
Collaborator Author

@FLomb @mohammadamint - this is ready for review, I would suggest you run some of your usecases on the development branch and you compare with the results obtain with this new branch.

indexes_choice = []
for s in self.free_spots:
if s.stop - s.start >= self.func_cycle:
indexes_choice += [*range(s.start, s.stop - self.func_cycle + 1)] # this will be fast with cython
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This solves the problem raised in #28 as the switch_on can by design only be chosen in the part of the available windows such that switch_on + func_cycle <= window upper limit

Copy link
Contributor

@FLomb FLomb left a comment

Choose a reason for hiding this comment

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

I've noticed a few issues that affect both this PR and the latest updates of the development branch, which I hadn't tested this way either.

I am running the code from the perspective of a user that relies on an interface, such as Spyder, from which they run the ramp_run.py file, for the example input file 1.

The first issue that arises is due to an outdated path in the initialise.py file, which tries to look for the input files in a path that no longer exists (file_module = importlib.import_module(f"ramp.input_files.input_file_{j}")). This can be easily fixed by updating the path to f"ramp.input_files.input_file_{j}". When doing so, however, a second issue arises, as reported below:

  File "/home/fl/GitHub-repos/RAMP/ramp/../ramp/example/input_file_1.py", line 49, in <module>
    Ch_indoor_bulb.windows([1200,1440],[0,0],0.1)

  File "/home/fl/GitHub-repos/RAMP/ramp/../ramp/core/core.py", line 829, in windows
    window_time = window_time + np.diff(getattr(self, f"window_{i}"))[0]

AttributeError: 'Appliance' object has no attribute 'window_4'

This seems to be something more structural. I am not sure why it went unnoticed in the latest updates of the development branch. I haven't yet had time to dig deeper into it, but perhaps it's something straightforward for you based on the latest changes you made?

@Bachibouzouk
Copy link
Collaborator Author

AttributeError: 'Appliance' object has no attribute 'window_4'

This would happen because the attribute num_windows of appliance is greater than 3

@FLomb
Copy link
Contributor

FLomb commented Feb 9, 2023

Indeed, it looks like all the example input files are deprecated because they used to define a 'name' for the appliance's user as a first parameter (as in: Ch_outdoor_bulb = Church.Appliance(Church,7,26,1,240,0.2,60, 'yes', flat = 'yes')). In the current version of the code, such first 'name' parameter is not foreseen anymore and gets interpreted as the first real parameter (i.e., number), leading to the problem above. I guess we could update all the examples accordingly; that is no problem. But I am wondering how did this slip away in all previous PRs. The same error arises if trying to convert the example input files into an Excel equivalent (which I initially thought could've been the reason why you guys hadn't had the issue)

@FLomb
Copy link
Contributor

FLomb commented Feb 9, 2023

Another issue is that some parts of the code (namely, the post-processing) try to save results in a results directory that, however, is not part of the code structure by default. So the user needs to create it first. Once the folder is in place, and all the input files are updated as commented above, the code from this PR works fine.

I have tested the code based on the qualitative testing functionality I had implemented some time ago. There were some issues in the testing script, too, which I fixed. You can see the results below for the 3 default example input files (this is the automated output of the testing script):

image

The results seem highly satisfactory, so I would say that the refactoring of the code is consistent with the correct functioning of the algorithm and could be merged. However, we first need to take care of the issues above. I will try to update the PR with my own fixes. In addition, I haven't tested yet the parallel-computing case.

@FLomb
Copy link
Contributor

FLomb commented Feb 9, 2023

In the updated documentation, there are some broken links to images: https://rampdemand-rtd--54.org.readthedocs.build/en/54/examples/parallel_processing/parallel_processing.html

@FLomb
Copy link
Contributor

FLomb commented Feb 9, 2023

Tried testing parallel computing, too. ramp -i example/input_file_1.py -n 30 -p returns the following error:

usage: python ramp_run.py [-h] [-i FNAME_PATH [FNAME_PATH ...]]
                          [-n NUM_PROFILES [NUM_PROFILES ...]]
python ramp_run.py: error: unrecognized arguments: -p

@mohammadamint
Copy link
Collaborator

mohammadamint commented Feb 10, 2023

In the updated documentation, there are some broken links to images: https://rampdemand-rtd--54.org.readthedocs.build/en/54/examples/parallel_processing/parallel_processing.html

"<img alt="examples/parallel_processing/output_8_1.png" src="examples/parallel_processing/output_8_1.png> "

The tag source needs to be updated with the GitHub repo image dir in docs/source/_static

@mohammadamint
Copy link
Collaborator

Indeed, it looks like all the example input files are deprecated because they used to define a 'name' for the appliance's user as a first parameter (as in: Ch_outdoor_bulb = Church.Appliance(Church,7,26,1,240,0.2,60, 'yes', flat = 'yes')). In the current version of the code, such first 'name' parameter is not foreseen anymore and gets interpreted as the first real parameter (i.e., number), leading to the problem above. I guess we could update all the examples accordingly; that is no problem. But I am wondering how did this slip away in all previous PRs. The same error arises if trying to convert the example input files into an Excel equivalent (which I initially thought could've been the reason why you guys hadn't had the issue)

I would suggest to update the examples scripts with the last changes of the code, mainly due to the fact that in those examples, in most of the cases, positional arguments are used, which are changed now. I can take care of this, by updating all the example files (and preferably use keyword arguments to avoid similar issue in the future)

@mohammadamint
Copy link
Collaborator

Another issue is that some parts of the code (namely, the post-processing) try to save results in a results directory that, however, is not part of the code structure by default. So the user needs to create it first. Once the folder is in place, and all the input files are updated as commented above, the code from this PR works fine.

I have tested the code based on the qualitative testing functionality I had implemented some time ago. There were some issues in the testing script, too, which I fixed. You can see the results below for the 3 default example input files (this is the automated output of the testing script):

image

The results seem highly satisfactory, so I would say that the refactoring of the code is consistent with the correct functioning of the algorithm and could be merged. However, we first need to take care of the issues above. I will try to update the PR with my own fixes. In addition, I haven't tested yet the parallel-computing case.

I specifically am working on the postprocessing and this issue will be solved in the upcoming changes related to postprocessing. However, for now, we can solve the issue by adding the results folder. This will be however a little bit tricky, as if someone installs RAMP using pip, finding the result directory would be in difficult!

@Bachibouzouk
Copy link
Collaborator Author

Tried testing parallel computing, too. ramp -i example/input_file_1.py -n 30 -p returns the following error:

Right now the -p option is not supposed to work with .py files, only with .xlsx ones, this is to be solved within #58

@Bachibouzouk
Copy link
Collaborator Author

I specifically am working on the postprocessing and this issue will be solved in the upcoming changes related to postprocessing. However, for now, we can solve the issue by adding the results folder. This will be however a little bit tricky, as if someone installs RAMP using pip, finding the result directory would be in difficult!

@mohammadamint - do you think you would be done soon with the postprocessing? Otherwise it should be possible to add a line of code to create the results folder if it does not exists with the shutils module if a new release is to be made soon. (that way you are not under stress to wrap up the post-processing PR)

@FLomb
Copy link
Contributor

FLomb commented Feb 15, 2023

Tried testing parallel computing, too. ramp -i example/input_file_1.py -n 30 -p returns the following error:

Right now the -p option is not supposed to work with .py files, only with .xlsx ones, this is to be solved within #58

Ok, I did manage to test parallel computing based on .xlsxl files created from the example .py files. Results are satisfactory also in this case, as shown by the testing report below:
image

@FLomb
Copy link
Contributor

FLomb commented Feb 15, 2023

In the updated documentation, there are some broken links to images: https://rampdemand-rtd--54.org.readthedocs.build/en/54/examples/parallel_processing/parallel_processing.html

"<img alt="examples/parallel_processing/output_8_1.png" src="examples/parallel_processing/output_8_1.png> "

The tag source needs to be updated with the GitHub repo image dir in docs/source/_static

Indeed, all I see in the docs/source/_static folder is output files 5_1 & 5_2 and 7_1 & 7_2, as opposed to the .rst file that looks for 6_1 & 6_2 and 8_1 & 8_2. Am I right in assuming we can correct the .rst to look for files 5 and 7 in place of 6 and 8?

@Bachibouzouk
Copy link
Collaborator Author

Indeed, all I see in the docs/source/_static folder is output files 5_1 & 5_2 and 7_1 & 7_2, as opposed to the .rst file that looks for 6_1 & 6_2 and 8_1 & 8_2. Am I right in assuming we can correct the .rst to look for files 5 and 7 in place of 6 and 8?

I did this in 7159cb2 and it works

Copy link
Contributor

@FLomb FLomb left a comment

Choose a reason for hiding this comment

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

I reviewed the PR and made edits which were pushed to the rl-institut branch and thereby re-incorporated into this PR. The PR is now ready for merging, the only remaining issue being the missing 'results' folder that leads to conflicts with some post-processing parts of the code. The question is whether to make the results folder a default part of the repository, e.g., by adding default results for the three example input files, or not. I would probably go for the option above, for the time being, so that we can have a fully functioning version of the code for release.

@FLomb
Copy link
Contributor

FLomb commented Feb 16, 2023

I reviewed the PR and made edits which were pushed to the rl-institut branch and thereby re-incorporated into this PR. The PR is now ready for merging, the only remaining issue being the missing 'results' folder that leads to conflicts with some post-processing parts of the code. The question is whether to make the results folder a default part of the repository, e.g., by adding default results for the three example input files, or not. I would probably go for the option above, for the time being, so that we can have a fully functioning version of the code for release.

I think we can merge this PR now, and open a separate PR for the 'results' folder issue.

@FLomb FLomb merged commit 57be31e into RAMP-project:development Feb 16, 2023
@Bachibouzouk Bachibouzouk deleted the feature/vectorize_only_apps branch February 16, 2023 11:31
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.

3 participants