Skip to content

Add Python3 port and example plotting script#2

Open
programmasters wants to merge 1 commit into
4c656554:masterfrom
programmasters:Python_3-update
Open

Add Python3 port and example plotting script#2
programmasters wants to merge 1 commit into
4c656554:masterfrom
programmasters:Python_3-update

Conversation

@programmasters

Copy link
Copy Markdown

Added a Python 3 port of the CREST model as pyCREST_Py_3.py and an example script Example_Py_3.py that generates profiles and provides helper plotting functions (plot_powerfile, plot_occupancy_file, plot_app_profiles). Rename the original pyCREST.py to pyCREST_Py_2.py to preserve the prior copy. The example uses pyCREST_Py_3.create_profiles to produce Pfile_/Qfile_/Occfile_/AppProfiles files and attempts to display or save plots (matplotlib + numpy).

I tried retaining the original python 2 version as close as possible. What do you think?

Add a Python 3 port of the CREST model as pyCREST_Py_3.py and an example script Example_Py_3.py that generates profiles and provides helper plotting functions (plot_powerfile, plot_occupancy_file, plot_app_profiles). Rename the original pyCREST.py to pyCREST_Py_2.py to preserve the prior copy. The example uses pyCREST_Py_3.create_profiles to produce Pfile_/Qfile_/Occfile_/AppProfiles files and attempts to display or save plots (matplotlib + numpy).
Copilot AI review requested due to automatic review settings March 12, 2026 09:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a Python 3-compatible port of the CREST demand model while preserving the prior Python 2 implementation under a new filename, plus an example script for generating output files and plotting results.

Changes:

  • Added pyCREST_Py_3.py (Python 3 port) with helper string decoding for NumPy genfromtxt outputs.
  • Added Example_Py_3.py to generate profiles and plot power/occupancy/appliance profiles using NumPy + Matplotlib.
  • Added pyCREST_Py_2.py to retain the previous Python 2 version under an explicit filename.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 7 comments.

File Description
pyCREST_Py_3.py Introduces the Python 3 version of the model and writes out the generated demand/occupancy/appliance profile files.
pyCREST_Py_2.py Preserves the legacy Python 2 implementation under a new name for reference/backward compatibility.
Example_Py_3.py Provides plotting helpers and a runnable example that calls the Python 3 model and visualizes outputs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread pyCREST_Py_3.py
Comment on lines +326 to +327
writer.writerow([i] + ["P"] + [appliances_in_dwelling[appliance][15]] + sim_dataP[appliance][:].tolist())
writer.writerow([i] + ["Q"] + [appliances_in_dwelling[appliance][15]] + sim_dataQ[appliance][:].tolist())

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

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

appliances_in_dwelling is loaded with genfromtxt(..., encoding=None) and string fields are likely bytes (you already added _to_str for comparisons). When writing AppProfiles...dat, the appliance name is written directly from appliances_in_dwelling[...][15], which can result in tokens like b'FRIDGE' in the output and break downstream parsing (e.g., Example_Py_3.plot_app_profiles). Decode to str (or load with encoding='utf-8') before writing names.

Suggested change
writer.writerow([i] + ["P"] + [appliances_in_dwelling[appliance][15]] + sim_dataP[appliance][:].tolist())
writer.writerow([i] + ["Q"] + [appliances_in_dwelling[appliance][15]] + sim_dataQ[appliance][:].tolist())
writer.writerow([i] + ["P"] + [_to_str(appliances_in_dwelling[appliance][15])] + sim_dataP[appliance][:].tolist())
writer.writerow([i] + ["Q"] + [_to_str(appliances_in_dwelling[appliance][15])] + sim_dataQ[appliance][:].tolist())

Copilot uses AI. Check for mistakes.
Comment thread pyCREST_Py_3.py

# Store the demand
if iTime < 1440:
lighting_demand_data[i,iTime] = iRating

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

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

In the lighting simulation, demand is stored at lighting_demand_data[i, iTime] while the rest of the code treats iTime as 1-based (e.g., irradiance uses iTime-1, and the off case stores at iTime-1). This shifts “on” minutes by +1 and can drop the first minute / misalign the profile. Store lighting demand at iTime-1 for consistency.

Suggested change
lighting_demand_data[i,iTime] = iRating
lighting_demand_data[i,iTime-1] = iRating

Copilot uses AI. Check for mistakes.
Comment thread pyCREST_Py_3.py
Comment on lines +520 to +526
iCurrentstate = 0
# Reset the cumulative probability count
fCumulativeP = 0
# Determine the start state at time 00:00 by checking the random number against the distribution
for iCurrentState in range(0,6):
# Add the probability for this number of active occupants
fCumulativeP = fCumulativeP + start_states[iCurrentstate][household_size-1]

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

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

get_start_state accumulates probabilities from start_states[iCurrentstate] where iCurrentstate is always 0, so the cumulative distribution is built from only the first row (zero active occupants). This makes the initial occupancy state selection incorrect. Use the loop variable (iCurrentState) when indexing start_states (and consider iterating 0..6 to cover all 7 states).

Suggested change
iCurrentstate = 0
# Reset the cumulative probability count
fCumulativeP = 0
# Determine the start state at time 00:00 by checking the random number against the distribution
for iCurrentState in range(0,6):
# Add the probability for this number of active occupants
fCumulativeP = fCumulativeP + start_states[iCurrentstate][household_size-1]
# Reset the cumulative probability count
fCumulativeP = 0
# Determine the start state at time 00:00 by checking the random number against the distribution
for iCurrentState in range(0,7):
# Add the probability for this number of active occupants
fCumulativeP = fCumulativeP + start_states[iCurrentState][household_size-1]

Copilot uses AI. Check for mistakes.
Comment thread pyCREST_Py_3.py
sim_data_outputQ = numpy.sum(sim_dataQ, axis=0)

with open('AppProfiles'+idstring+'.dat', 'a', newline='') as f:
writer = csv.writer(f, delimiter =' ',lineterminator='')

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

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

The lighting row is written with lineterminator='', which produces no newline after the record; subsequent appends will concatenate records onto the same line and make AppProfiles...dat hard to parse. Use a newline terminator (e.g. \n) consistently for all rows.

Suggested change
writer = csv.writer(f, delimiter =' ',lineterminator='')
writer = csv.writer(f, delimiter =' ',lineterminator='\n')

Copilot uses AI. Check for mistakes.
Comment thread pyCREST_Py_3.py
Comment on lines +358 to +367
Pfile = open('Pfile_'+idstring+'.dat', 'w')

numpy.savetxt('Pfile_'+idstring+'.dat',sim_dataP_for_file,fmt="%d", delimiter='\t')
Pfile.close()
Qfile = open('Qfile_'+idstring+'.dat', 'a')
numpy.savetxt('Qfile_'+idstring+'.dat',sim_dataQ_for_file,fmt="%d", delimiter='\t')
Qfile.close()
Occfile = open('Occfile_'+idstring+'.dat', 'a')
numpy.savetxt('Occfile_'+idstring+'.dat',occ_profile_for_file,fmt="%d", delimiter='\t')
Occfile.close()

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

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

numpy.savetxt is called with a filename string, so it opens/truncates the file itself; the separately opened Qfile/Occfile handles (opened in append mode) are unused and the mode doesn’t match the actual write behavior. Prefer with open(..., 'w') as f: numpy.savetxt(f, ...) (or decide explicitly on append vs overwrite) and remove the unused open(...) calls.

Suggested change
Pfile = open('Pfile_'+idstring+'.dat', 'w')
numpy.savetxt('Pfile_'+idstring+'.dat',sim_dataP_for_file,fmt="%d", delimiter='\t')
Pfile.close()
Qfile = open('Qfile_'+idstring+'.dat', 'a')
numpy.savetxt('Qfile_'+idstring+'.dat',sim_dataQ_for_file,fmt="%d", delimiter='\t')
Qfile.close()
Occfile = open('Occfile_'+idstring+'.dat', 'a')
numpy.savetxt('Occfile_'+idstring+'.dat',occ_profile_for_file,fmt="%d", delimiter='\t')
Occfile.close()
with open('Pfile_'+idstring+'.dat', 'w') as Pfile:
numpy.savetxt(Pfile, sim_dataP_for_file, fmt="%d", delimiter='\t')
with open('Qfile_'+idstring+'.dat', 'w') as Qfile:
numpy.savetxt(Qfile, sim_dataQ_for_file, fmt="%d", delimiter='\t')
with open('Occfile_'+idstring+'.dat', 'w') as Occfile:
numpy.savetxt(Occfile, occ_profile_for_file, fmt="%d", delimiter='\t')

Copilot uses AI. Check for mistakes.
Comment thread Example_Py_3.py
appliance token and then interprets subsequent tokens as floats. If no
*appliance* is specified, the first numeric profile encountered is used.
"""
lines = open(filename).read().strip().splitlines()

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

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

plot_app_profiles reads the file via open(filename).read() without closing the handle. Use a context manager (with open(...) as f:) to avoid leaking file descriptors, especially if these helpers are called repeatedly.

Suggested change
lines = open(filename).read().strip().splitlines()
with open(filename) as f:
lines = f.read().strip().splitlines()

Copilot uses AI. Check for mistakes.
Comment thread Example_Py_3.py
Comment on lines +119 to +121
times = np.arange(len(data)) / 60.0
_plt.figure()
_plt.plot(times, data if data.ndim==1 else data[0])

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

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

In the exception fallback plot, times = np.arange(len(data)) / 60.0 uses the number of rows when the loaded file is 2D (n dwellings), but the plotted profile is data[0] (length 1440). For n > 1 this will raise a shape mismatch or produce an incorrect x-axis. Build times from the selected profile length (same approach as plot_powerfile).

Suggested change
times = np.arange(len(data)) / 60.0
_plt.figure()
_plt.plot(times, data if data.ndim==1 else data[0])
if data.ndim == 1:
profile = data
else:
profile = data[0]
times = np.arange(len(profile)) / 60.0
_plt.figure()
_plt.plot(times, profile)

Copilot uses AI. Check for mistakes.
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.

2 participants