Skip to content

Set period length parameters as indexed parameters#76

Open
esrawli wants to merge 22 commits into
IDAES:mainfrom
esrawli:indexing-period-lengths
Open

Set period length parameters as indexed parameters#76
esrawli wants to merge 22 commits into
IDAES:mainfrom
esrawli:indexing-period-lengths

Conversation

@esrawli
Copy link
Copy Markdown
Collaborator

@esrawli esrawli commented Apr 16, 2026

The periodLength parameters for each stage (representative period, commitment, and dispatch) are indexed by the number of periods on each respective stage. This will allow to determine different period lengths within each stage.

@esrawli esrawli marked this pull request as draft April 16, 2026 19:42
esrawli and others added 4 commits April 16, 2026 16:35
…and duration of periods while converting them to nested dictionaries for flexible period definitions

In the model class, I included a function to expand scalars/lists to
nested dictionaries for representative, commitment, and dispatch
periods to support flexible period structures.
esrawli added 7 commits April 29, 2026 14:33
…tructure dictionary needed in the expansion model class
… duration from scalars or json file. Also, add test for this.

In the GTEP class, remove function to expand scalars to a period
structure dictionary and add this to a new function in utils
file. Also, include new function that allows both, scalars (to be
expanded to a period structure dictionary) or a .json file with
customed period structure.
@esrawli esrawli marked this pull request as ready for review April 29, 2026 23:49
@esrawli esrawli requested review from blnicho and rundxdi April 29, 2026 23:49
Comment thread gtep/model_library/gen.py
Comment on lines -284 to -286
<= m.rampUpRates[generator]
* b.dispatchPeriod[dispatchPeriod].periodLength
* m.thermalCapacity[generator]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This looks like either the old implementation or the new implementation shouldn't be unit-consistent. Any idea which one is correct (and why the tests didn't catch it?)

Comment thread gtep/model_library/gen.py
Comment on lines 370 to 379
<= max(
pyo.value(m.thermalMin[generator]),
# [ESR: Make sure the time units are consistent
# here since we are only taking the value]
pyo.value(m.rampUpRates[generator])
* pyo.value(
b.dispatchPeriod[dispatchPeriod].periodLength
) # in minutes
* pyo.value(m.thermalCapacity[generator]),
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Design question: I generally don't like putting value() in model definitions. We could replace it with

<= MaxExpression((
     m.thermalMin[generator],
    # [ESR: Make sure the time units are consistent
    # here since we are only taking the value]
    m.rampUpRates[generator]
    * b.dispatchPeriod[dispatchPeriod].periodLength)  # in minutes
    * m.thermalCapacity[generator]
))

(Or if we know these are all NVP expressions (expressions of Params), then use a NPV_MaxExpression)

Comment thread gtep/gtep_data.py
num_commit=24,
num_dispatch=1,
duration_dispatch=60,
duration_representative_period=24,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should this br 24 * units.hour?

Comment thread gtep/utils.py
import pyomo.environ as pyo
from pyomo.environ import units as u

curr_dir = os.path.dirname(os.path.abspath(__file__))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This can be a bit fragile. you might consider pyomo.common.fileutils.this_file_dir()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would suggest renaming the variable, too: it is not holding the current (working) directory: it is holding this file's directory. Maybe:

Suggested change
curr_dir = os.path.dirname(os.path.abspath(__file__))
data_dir = os.path.join(pyomo.common.fileutils.this_file_dir(), 'data')

Comment thread gtep/utils.py
commitment_hr = duration_commitment[rep][com]
if abs(pyo.value(dispatch_sum_hr) - commitment_hr) > 1e-6:
dispatch_errors.append(
f" - Representative period {rep}, commitment period {com}: sum of dispatch durations ({pyo.value(dispatch_sum_hr)} hr) != commitment period duration ({commitment_hr} hr)"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can you break this into multiple lines?

Comment thread gtep/utils.py
rep_period_hr = duration_representative_period[rep]
if abs(commitment_sum_hr - rep_period_hr) > 1e-6:
commitment_errors.append(
f" - Representative period {rep}: sum of commitment durations ({commitment_sum_hr} hr) != representative period duration ({rep_period_hr} hr)"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can you break this into multiple lines?

Comment thread gtep/utils.py
Comment on lines +139 to +141
json_path = os.path.abspath(
os.path.join(curr_dir, "data", period_structure_json_file)
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This implies that .json files are being distributed by IDAES-gtep. Can you confirm that they are being bundled in the wheels?

Comment thread gtep/utils.py

# Helper function to recursively convert string keys to
# integers
def convert_keys_to_int(obj):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This seems like a broadly useful capability. Should this be promoted to a general utility for "recovering" loaded general JSON files?

Comment thread gtep/utils.py
# under the data directory.
filename = (
os.path.abspath(
os.path.join(curr_dir, "data", "period_structure_from_gtep.json")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Again, is this file distributed in the wheel?

Comment thread gtep/gtep_model.py
check_period_structure_consistency,
)

curr_dir = os.path.dirname(os.path.abspath(__file__))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This is fragile (and does not appear to be used). Remove?

Comment thread gtep/utils.py
import pyomo.environ as pyo
from pyomo.environ import units as u

curr_dir = os.path.dirname(os.path.abspath(__file__))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would suggest renaming the variable, too: it is not holding the current (working) directory: it is holding this file's directory. Maybe:

Suggested change
curr_dir = os.path.dirname(os.path.abspath(__file__))
data_dir = os.path.join(pyomo.common.fileutils.this_file_dir(), 'data')

Comment thread gtep/utils.py
json.dump(period_structure, f, indent=2)


def generate_period_structure_utils(
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would recommend rethinking the name of this function (it doesn't generate "utils" does it?)

Comment thread gtep/utils.py
Comment on lines +122 to +125
If a JSON file is specified, it loads the period structure from
that file. Otherwise, generates the period structure from the
provided scalar arguments. Optionally saves the generated
structure to a JSON file.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This seems like three separate things. I would recommend these actions be three separate functions instead of switching them based on the arguments.

@blnicho blnicho added the Priority:High High Priority Issue or PR label May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Priority:High High Priority Issue or PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants