Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f0dafbe
Update REopt.jl to multiple-chps
Bill-Becker Jan 31, 2026
2153761
Update REopt.jl to chp-expand
Bill-Becker Jan 31, 2026
f26e8ac
Update s.chp to s.chps[1] for the multiple CHPs update
Bill-Becker Jan 31, 2026
bb22d6a
Update REopt.jl#chp-expand after merging offgrid-chp
Bill-Becker Feb 2, 2026
6a5e329
Update REopt.jl#chp-expand after prodfactor update
Bill-Becker Feb 3, 2026
ae06f30
Update REopt.jl#chp-expand after merging develop
Bill-Becker Feb 3, 2026
b2f0fa7
Add output CHP.electric_load_curtailed_series_kw to model db that was…
Bill-Becker Feb 3, 2026
b7b19a9
Merge branch 'master' into chp-expand
Bill-Becker Aug 3, 2026
cba46ff
Merge migrations after merging master
Bill-Becker Aug 3, 2026
0ca96a7
Update REopt.jl#chp-expand
Bill-Becker Aug 3, 2026
726b02b
Merge branch 'heuristic-dispatch-mpc' into chp-expand
Bill-Becker Aug 4, 2026
809eff3
Merge migrations after merging dispatch
Bill-Becker Aug 4, 2026
18c2e3f
Update REopt.jl to chp-expand with dispatch branch
Bill-Becker Aug 4, 2026
0827500
Agent Host changes for agents/update-reopt-api-inputs-outputs
Bill-Becker Aug 4, 2026
43991b1
Update REopt.jl manifest tree sha
Bill-Becker Aug 4, 2026
6f78ca8
Add validation for duplicate CHP names and require name for CHP list …
Bill-Becker Aug 4, 2026
23b2f73
Add 'name' field to multi-tech defaults from Julia and handle multipl…
Bill-Becker Aug 4, 2026
44206c8
Handle multiple CHPs coming from REopt.jl for defaults updating
Bill-Becker Aug 4, 2026
b4238a8
Merge branch 'agents/update-reopt-api-inputs-outputs' into chp-expand
Bill-Becker Aug 4, 2026
dfa7aed
Migration with new CHP inputs, outputs, and multiple handling
Bill-Becker Aug 4, 2026
28dd158
Add CHP heating/thermal following input
Bill-Becker Aug 5, 2026
0e5e213
Update REopt.jl to chp-expand now that it has every CHP update in it
Bill-Becker Aug 10, 2026
e7d3270
Add CHP annual thermal curtailed output for optimal and BAU
Bill-Becker Aug 10, 2026
9aa5221
Update REopt.jl with thermal curtailed output
Bill-Becker Aug 10, 2026
9060241
Merge branch 'master' into chp-expand
Bill-Becker Aug 11, 2026
70e2c3d
Update REopt.jl to develop branch
Bill-Becker Aug 11, 2026
c924dcd
Update CHANGELOG for v3.22.0 enhancements
Bill-Becker Aug 11, 2026
53bc460
Update REopt.jl to v0.61.0 latest registered version
Bill-Becker Aug 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ Classify the change according to the following categories:
##### Removed
### Patches

## v3.22.0
### Minor Updates
##### Added
- Expanded **CHP** modeling to support multiple independently-configured CHPs, existing capacity (i.e. in BAU scenario), off-grid operation including the option for CHP to either require or supply operating reserves, user-defined production factors, ramp-rate limits, and heating load following thermal dispatch.
- Added **CHP** inputs **name** (to label the type of system you are modeling), **existing_kw**, **ramp_rate_fraction_per_hour**, **operating_reserve_required_fraction**, **production_factor_series**, **fuel_cost_escalation_rate_fraction**, and **follow_heating_load**. When **follow_heating_load** is `true`, each CHP independently follows its eligible heating load at unfired thermal capacity; **ExistingBoiler** is restricted when CHP capacity exceeds that load, while other onsite heating resources can contribute and supplementary firing remains available only as incremental heat.
- Added **CHP** outputs **name**, **electric_curtailed_series_kw**, and **annual_thermal_curtailed_mmbtu**. Multiple CHP systems are returned as a list of per-unit results under **CHP**, identified by **name**.
- Added **CHP** BAU outputs **size_kw_bau**, **annual_fuel_consumption_mmbtu_bau**, **annual_electric_production_kwh_bau**, **annual_thermal_production_mmbtu_bau**, **annual_thermal_curtailed_mmbtu_bau**, **year_one_fuel_cost_before_tax_bau**, **year_one_fuel_cost_after_tax_bau**, **lifecycle_fuel_cost_after_tax_bau**, **year_one_standby_cost_before_tax_bau**, **year_one_standby_cost_after_tax_bau**, and **lifecycle_standby_cost_after_tax_bau**.

## v3.21.0
### Minor Updates
##### Added
Expand All @@ -50,7 +58,7 @@ Classify the change according to the following categories:

## v3.19.0
### Minor updates
#### Added
##### Added
- New `CHP` fields **serve_absorption_chiller_only**, **months_serving_absorption_chiller_only**, **follow_electrical_load**, and **include_cooling_in_chp_size**
- New output `thermal_to_absorption_chiller_series_mmbtu_per_hour` added to heating technologies `CHPOutputs`, `ElectricHeaterOutputs`, `CSTOutputs`, `BoilerOutputs`, `SteamTurbineOutputs`, and `ExistingBoilerOutputs`, and new output `storage_to_absorption_chiller_series_mmbtu_per_hour` for `HotThermalStorageOutputs` and `HighTempThermalStorageOutputs`.
##### Changed
Expand Down
4 changes: 2 additions & 2 deletions julia_src/Manifest.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 12 additions & 3 deletions julia_src/http.jl
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,18 @@ function reopt(req::HTTP.Request)
]
if haskey(d, "CHP")
inputs_with_defaults_from_julia_chp = [
:name,
:installed_cost_per_kw, :tech_sizes_for_cost_curve, :om_cost_per_kwh,
:electric_efficiency_full_load, :thermal_efficiency_full_load, :min_allowable_kw,
:cooling_thermal_factor, :min_turn_down_fraction, :unavailability_periods, :max_kw,
:size_class, :electric_efficiency_half_load, :thermal_efficiency_half_load,
:macrs_option_years, :macrs_bonus_fraction, :federal_itc_fraction
]
chp_dict = Dict(key=>getfield(model_inputs.s.chp, key) for key in inputs_with_defaults_from_julia_chp)
if length(model_inputs.s.chps) == 1
chp_dict = Dict(key=>getfield(model_inputs.s.chps[1], key) for key in inputs_with_defaults_from_julia_chp)
else
chp_dict = [Dict(key=>getfield(chp, key) for key in inputs_with_defaults_from_julia_chp) for chp in model_inputs.s.chps]
end
else
chp_dict = Dict()
end
Expand Down Expand Up @@ -253,9 +258,13 @@ function reopt(req::HTTP.Request)
end
if haskey(d, "PV")
inputs_with_defaults_from_julia_pv = [
:size_class, :installed_cost_per_kw, :om_cost_per_kw, :macrs_option_years, :macrs_bonus_fraction, :federal_itc_fraction
:name, :size_class, :installed_cost_per_kw, :om_cost_per_kw, :macrs_option_years, :macrs_bonus_fraction, :federal_itc_fraction
]
pv_dict = Dict(key=>getfield(model_inputs.s.pvs[1], key) for key in inputs_with_defaults_from_julia_pv)
if length(model_inputs.s.pvs) == 1
pv_dict = Dict(key=>getfield(model_inputs.s.pvs[1], key) for key in inputs_with_defaults_from_julia_pv)
else
pv_dict = [Dict(key=>getfield(pv, key) for key in inputs_with_defaults_from_julia_pv) for pv in model_inputs.s.pvs]
end
else
pv_dict = Dict()
end
Expand Down
19 changes: 19 additions & 0 deletions reoptjl/migrations/0114_chpoutputs_electric_curtailed_series_kw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.26 on 2026-02-03 16:40

import django.contrib.postgres.fields
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0113_merge_20251209_2338'),
]

operations = [
migrations.AddField(
model_name='chpoutputs',
name='electric_curtailed_series_kw',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='Electric power curtailed time-series array [kW]', size=None),
),
]
14 changes: 14 additions & 0 deletions reoptjl/migrations/0117_merge_20260803_1544.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 4.2.26 on 2026-08-03 15:44

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0114_chpoutputs_electric_curtailed_series_kw'),
('reoptjl', '0116_alter_apimeta_api_key_alter_apimeta_job_type'),
]

operations = [
]
14 changes: 14 additions & 0 deletions reoptjl/migrations/0122_merge_20260804_1339.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 4.2.26 on 2026-08-04 13:39

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0117_merge_20260803_1544'),
('reoptjl', '0121_electricstorageinputs_can_export_beyond_nem_limit_and_more'),
]

operations = [
]
121 changes: 121 additions & 0 deletions reoptjl/migrations/0123_chpinputs_existing_kw_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Generated by Django 4.2.26 on 2026-08-04 22:24

import django.contrib.postgres.fields
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0122_merge_20260804_1339'),
]

operations = [
migrations.AddField(
model_name='chpinputs',
name='existing_kw',
field=models.FloatField(blank=True, default=0, help_text='Existing CHP electric capacity (based on rated electric power)', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(100000.0)]),
),
migrations.AddField(
model_name='chpinputs',
name='fuel_cost_escalation_rate_fraction',
field=models.FloatField(blank=True, help_text='Annual nominal chp fuel cost escalation rate, as a decimal.', null=True, validators=[django.core.validators.MinValueValidator(-1.0), django.core.validators.MaxValueValidator(1.0)]),
),
migrations.AddField(
model_name='chpinputs',
name='name',
field=models.TextField(blank=True, default='CHP', help_text='CHP description for distinguishing between multiple CHP models'),
),
migrations.AddField(
model_name='chpinputs',
name='operating_reserve_required_fraction',
field=models.FloatField(blank=True, help_text='Only applicable when off_grid_flag=True. Required operating reserves applied to each timestep as a fraction of CHP generation serving load in that timestep.', null=True, validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(1.0)]),
),
migrations.AddField(
model_name='chpinputs',
name='production_factor_series',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True), blank=True, default=list, help_text='Optional user-defined production factors. Must be normalized to units of kW-AC/kW-AC nameplate, representing the AC power (kW) output per 1 kW-AC of CHP capacity in each time step. The series must be one year (January through December) of hourly, 30-minute, or 15-minute data.', size=None),
),
migrations.AddField(
model_name='chpinputs',
name='ramp_rate_fraction_per_hour',
field=models.FloatField(blank=True, default=1.0, help_text='Maximum rate of change in electric production per hour as a fraction of size_kw [kW/size_kw/hour].', validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(1000.0)]),
),
migrations.AddField(
model_name='chpoutputs',
name='annual_electric_production_kwh_bau',
field=models.FloatField(blank=True, help_text='Electric energy produced in a year by the existing CHP system in BAU [kWh]', null=True),
),
migrations.AddField(
model_name='chpoutputs',
name='annual_fuel_consumption_mmbtu_bau',
field=models.FloatField(blank=True, help_text='Fuel consumed in a year by the existing CHP system in BAU [MMBtu]', null=True),
),
migrations.AddField(
model_name='chpoutputs',
name='annual_thermal_production_mmbtu_bau',
field=models.FloatField(blank=True, help_text='Thermal energy produced in a year by the existing CHP system in BAU [MMBtu]', null=True),
),
migrations.AddField(
model_name='chpoutputs',
name='lifecycle_fuel_cost_after_tax_bau',
field=models.FloatField(blank=True, help_text='Present value of cost of fuel consumed by the existing CHP system in BAU, after tax [$]', null=True),
),
migrations.AddField(
model_name='chpoutputs',
name='lifecycle_standby_cost_after_tax_bau',
field=models.FloatField(blank=True, help_text='Present value of all CHP standby charges for the existing CHP system in BAU, after tax.', null=True),
),
migrations.AddField(
model_name='chpoutputs',
name='name',
field=models.TextField(blank=True, default='CHP', help_text='CHP description for distinguishing between multiple CHP models'),
),
migrations.AddField(
model_name='chpoutputs',
name='size_kw_bau',
field=models.FloatField(blank=True, help_text='Power capacity size of the existing CHP system in BAU [kW]', null=True),
),
migrations.AddField(
model_name='chpoutputs',
name='year_one_fuel_cost_after_tax_bau',
field=models.FloatField(blank=True, help_text='Cost of fuel consumed by the existing CHP system in year one in BAU, after tax [$]', null=True),
),
migrations.AddField(
model_name='chpoutputs',
name='year_one_fuel_cost_before_tax_bau',
field=models.FloatField(blank=True, help_text='Cost of fuel consumed by the existing CHP system in year one in BAU [$]', null=True),
),
migrations.AddField(
model_name='chpoutputs',
name='year_one_standby_cost_after_tax_bau',
field=models.FloatField(blank=True, help_text='CHP standby charges in year one for the existing CHP system in BAU, after tax [$]', null=True),
),
migrations.AddField(
model_name='chpoutputs',
name='year_one_standby_cost_before_tax_bau',
field=models.FloatField(blank=True, help_text='CHP standby charges in year one for the existing CHP system in BAU [$]', null=True),
),
migrations.AlterField(
model_name='chpinputs',
name='max_kw',
field=models.FloatField(blank=True, help_text='Maximum new CHP size (in kWe) constraint for optimization (upper bound on additional capacity beyond existing_kw). Set to zero to disable CHP', null=True, validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(100000000.0)]),
),
migrations.AlterField(
model_name='chpinputs',
name='meta',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='CHPInputs', to='reoptjl.apimeta'),
),
migrations.AlterField(
model_name='chpinputs',
name='min_kw',
field=models.FloatField(blank=True, default=0, help_text='Minimum new CHP size constraint for optimization (lower bound on additional capacity beyond existing_kw).', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)]),
),
migrations.AlterField(
model_name='chpoutputs',
name='meta',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='CHPOutputs', to='reoptjl.apimeta'),
),
]
18 changes: 18 additions & 0 deletions reoptjl/migrations/0124_chpinputs_follow_heating_load.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.26 on 2026-08-05 14:19

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0123_chpinputs_existing_kw_and_more'),
]

operations = [
migrations.AddField(
model_name='chpinputs',
name='follow_heating_load',
field=models.BooleanField(blank=True, default=False, help_text='Boolean indicator if CHP follows the heating load by running at capacity or meeting the load only', null=True),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.26 on 2026-08-10 22:35

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0124_chpinputs_follow_heating_load'),
]

operations = [
migrations.AddField(
model_name='chpoutputs',
name='annual_thermal_curtailed_mmbtu',
field=models.FloatField(blank=True, help_text='Thermal energy wasted/unused/vented in a year [MMBtu]', null=True),
),
migrations.AddField(
model_name='chpoutputs',
name='annual_thermal_curtailed_mmbtu_bau',
field=models.FloatField(blank=True, help_text='Thermal energy wasted/unused/vented in a year by the existing CHP system in BAU [MMBtu]', null=True),
),
]
Loading