diff --git a/shared/lib_battery_dispatch.cpp b/shared/lib_battery_dispatch.cpp index f5327da41..a98e1c428 100644 --- a/shared/lib_battery_dispatch.cpp +++ b/shared/lib_battery_dispatch.cpp @@ -651,6 +651,7 @@ dispatch_automatic_t::dispatch_automatic_t( bool can_clip_charge, bool can_grid_charge, bool can_fuelcell_charge, + bool can_curtail_charge, std::vector battReplacementCostPerkWh, int battCycleCostChoice, std::vector battCycleCost, @@ -684,6 +685,7 @@ dispatch_automatic_t::dispatch_automatic_t( _safety_factor = 0.0; m_batteryPower->canClipCharge = can_clip_charge; + m_batteryPower->canCurtailCharge = can_curtail_charge; m_batteryPower->canSystemCharge = can_charge; m_batteryPower->canGridCharge = can_grid_charge; m_batteryPower->canFuelCellCharge = can_fuelcell_charge; diff --git a/shared/lib_battery_dispatch.h b/shared/lib_battery_dispatch.h index df49cd3bf..c1f701b2e 100644 --- a/shared/lib_battery_dispatch.h +++ b/shared/lib_battery_dispatch.h @@ -317,6 +317,7 @@ class dispatch_automatic_t : public dispatch_t bool can_clipcharge, bool can_grid_charge, bool can_fuelcell_charge, + bool can_curtail_charge, std::vector battReplacementCostPerkWh, int battCycleCostChoice, std::vector battCycleCost, diff --git a/shared/lib_battery_dispatch_automatic_btm.cpp b/shared/lib_battery_dispatch_automatic_btm.cpp index 4582dd83f..9ef100e67 100644 --- a/shared/lib_battery_dispatch_automatic_btm.cpp +++ b/shared/lib_battery_dispatch_automatic_btm.cpp @@ -61,6 +61,7 @@ dispatch_automatic_behind_the_meter_t::dispatch_automatic_behind_the_meter_t( bool can_clip_charge, bool can_grid_charge, bool can_fuelcell_charge, + bool can_curtail_charge, rate_data* util_rate, std::vector battReplacementCostPerkWh, int battCycleCostChoice, @@ -73,7 +74,8 @@ dispatch_automatic_behind_the_meter_t::dispatch_automatic_behind_the_meter_t( double SOC_min_outage, int load_forecast_mode ) : dispatch_automatic_t(Battery, dt_hour, SOC_min, SOC_max, current_choice, Ic_max, Id_max, Pc_max_kwdc, Pd_max_kwdc, Pc_max_kwac, Pd_max_kwac, - t_min, dispatch_mode, weather_forecast_mode, pv_dispatch, nyears, look_ahead_hours, dispatch_update_frequency_hours, can_charge, can_clip_charge, can_grid_charge, can_fuelcell_charge, + t_min, dispatch_mode, weather_forecast_mode, pv_dispatch, nyears, look_ahead_hours, dispatch_update_frequency_hours, + can_charge, can_clip_charge, can_grid_charge, can_fuelcell_charge, can_curtail_charge, battReplacementCostPerkWh, battCycleCostChoice, battCycleCost, battOMCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, behindTheMeterDischargeToGrid, SOC_min_outage) { @@ -401,10 +403,10 @@ double dispatch_automatic_behind_the_meter_t::compute_costs(size_t idx, size_t y year++; } for (size_t step = 0; step != _steps_per_hour && idx < _P_load_ac.size(); step++) - { + { double power = _P_load_ac[idx] - _P_pv_ac[idx]; - // One at a time so we can sort grid points by no-dispatch cost - std::vector forecast_power = { -power }; // Correct sign convention for cost forecast + // One at a time so we can sort grid points by no-dispatch cost; TODO: consider curtailment limits - would need to pass in a forecast of these... + std::vector forecast_power = { std::fmin(-1.0*power, m_batteryPower->powerInterconnectionLimit)}; // Correct sign convention for cost forecast double step_cost = noDispatchForecast->forecastCost(forecast_power, year, (hour_of_year + hour) % 8760, step); no_dispatch_cost += step_cost; @@ -707,6 +709,30 @@ void dispatch_automatic_behind_the_meter_t::plan_dispatch_for_cost(dispatch_plan } } + // Iterate over sorted grid to prioritize curtail charging + i = 0; + if (m_batteryPower->canCurtailCharge || m_batteryPower->canSystemCharge) { + while (i < _num_steps) { + // Don't plan to charge if we were already planning to discharge. 0 is no plan, negative is clipped energy + index = sorted_grid[i].Hour() * _steps_per_hour + sorted_grid[i].Step(); + if (plan.plannedDispatch[index] <= 0.0) + { + double requiredPower = 0.0; + if (sorted_grid[i].Grid() < 0) { + double powerLimit = std::fmin(m_batteryPower->powerInterconnectionLimit, m_batteryPower->powerCurtailmentLimit); + requiredPower = sorted_grid[i].Grid() + powerLimit; + requiredPower = std::fmin(0.0, requiredPower); + } + // Add to existing clipped energy + requiredPower += plan.plannedDispatch[index]; + // Clipped energy was already counted once, so subtract that off incase requiredPower + clipped hit a current restriction + requiredEnergy += (requiredPower - plan.plannedDispatch[index]) * _dt_hour; + plan.plannedDispatch[index] = requiredPower; + } + i++; + } + } + // Iterating over sorted grid std::stable_sort(sorted_grid.begin(), sorted_grid.end(), byLowestMarginalCost()); // Find m hours to get required energy - hope we got today's energy yesterday (for morning peaks). Apportion between hrs of lowest marginal cost @@ -765,9 +791,9 @@ void dispatch_automatic_behind_the_meter_t::plan_dispatch_for_cost(dispatch_plan // Clipped energy was already counted once, so subtract that off incase requiredPower + clipped hit a current restriction requiredEnergy += (requiredPower - plan.plannedDispatch[index]) * _dt_hour; - } - plan.plannedDispatch[index] = requiredPower; + plan.plannedDispatch[index] = requiredPower; + } } i++; diff --git a/shared/lib_battery_dispatch_automatic_btm.h b/shared/lib_battery_dispatch_automatic_btm.h index 2d963a18a..34d3bbd5b 100644 --- a/shared/lib_battery_dispatch_automatic_btm.h +++ b/shared/lib_battery_dispatch_automatic_btm.h @@ -86,6 +86,7 @@ class dispatch_automatic_behind_the_meter_t : public dispatch_automatic_t bool can_clipcharge, bool can_grid_charge, bool can_fuelcell_charge, + bool can_curtail_charge, rate_data* util_rate, std::vector battReplacementCostPerkWh, int battCycleCostChoice, diff --git a/shared/lib_battery_dispatch_automatic_fom.cpp b/shared/lib_battery_dispatch_automatic_fom.cpp index 66a042470..9e828030b 100644 --- a/shared/lib_battery_dispatch_automatic_fom.cpp +++ b/shared/lib_battery_dispatch_automatic_fom.cpp @@ -60,6 +60,7 @@ dispatch_automatic_front_of_meter_t::dispatch_automatic_front_of_meter_t( bool can_clip_charge, bool can_grid_charge, bool can_fuelcell_charge, + bool can_curtail_charge, double inverter_paco, std::vector battReplacementCostPerkWh, int battCycleCostChoice, @@ -71,7 +72,8 @@ dispatch_automatic_front_of_meter_t::dispatch_automatic_front_of_meter_t( double etaGridCharge, double etaDischarge, double interconnection_limit) : dispatch_automatic_t(Battery, dt_hour, SOC_min, SOC_max, current_choice, Ic_max, Id_max, Pc_max_kwdc, Pd_max_kwdc, Pc_max_kwac, Pd_max_kwac, - t_min, dispatch_mode, weather_forecast_mode, pv_dispatch, nyears, look_ahead_hours, dispatch_update_frequency_hours, can_charge, can_clip_charge, can_grid_charge, can_fuelcell_charge, + t_min, dispatch_mode, weather_forecast_mode, pv_dispatch, nyears, look_ahead_hours, dispatch_update_frequency_hours, + can_charge, can_clip_charge, can_grid_charge, can_fuelcell_charge, can_curtail_charge, battReplacementCostPerkWh, battCycleCostChoice, battCycleCost, battOMCost, interconnection_limit) { // if look behind, only allow 24 hours @@ -272,11 +274,18 @@ void dispatch_automatic_front_of_meter_t::update_dispatch(size_t year, size_t ho /*! Energy need to charge the battery (kWh) */ double energyNeededToFillBattery = _Battery->energy_to_fill(m_batteryPower->stateOfChargeMax); + // Positive: spare power to discharge. Negative: system power will be curtailed. Negative number can be fed directly into powerBattery to support charging + double interconnectionCapacity = std::fmin(m_batteryPower->powerInterconnectionLimit, m_batteryPower->powerCurtailmentLimit) - m_batteryPower->powerSystem; + /* Booleans to assist decisions */ bool highDischargeValuePeriod = ppa_cost >= discharge_ppa_cost && ppa_cost >= charge_ppa_cost; bool highChargeValuePeriod = ppa_cost <= charge_ppa_cost && ppa_cost <= discharge_ppa_cost; bool excessAcCapacity = _inverter_paco > m_batteryPower->powerSystemThroughSharedInverter; bool batteryHasDischargeCapacity = _Battery->SOC() >= m_batteryPower->stateOfChargeMin + 1.0; + bool interconnectionHasCapacity = interconnectionCapacity > 0.0; + bool canChargeFromCurtailedPower = interconnectionCapacity < 0.0 && (m_batteryPower->canCurtailCharge || m_batteryPower->canSystemCharge); + + revenueToCurtailCharge = canChargeFromCurtailedPower ? *max_ppa_cost * m_etaDischarge - m_cycleCost - m_omCost : 0; // Always Charge if PV is clipping if (m_batteryPower->canClipCharge && m_batteryPower->powerSystemClipped > 0 && revenueToClipCharge >= 0) @@ -284,6 +293,12 @@ void dispatch_automatic_front_of_meter_t::update_dispatch(size_t year, size_t ho powerBattery = -m_batteryPower->powerSystemClipped; } + // Always charge if system power is curtailed + if (canChargeFromCurtailedPower && revenueToCurtailCharge >= 0) { + // powerBattery is DC. Convert from AC to DC units to maximize utilization + powerBattery = interconnectionCapacity * m_batteryPower->singlePointEfficiencyACToDC; + } + // Increase charge from system (PV) if it is more valuable later than selling now if (m_batteryPower->canSystemCharge && revenueToPVCharge > 0 && @@ -332,7 +347,7 @@ void dispatch_automatic_front_of_meter_t::update_dispatch(size_t year, size_t ho } // Discharge if we are in a high-price period and have battery and inverter capacity - if (highDischargeValuePeriod && revenueToDischarge > 0 && excessAcCapacity && batteryHasDischargeCapacity) { + if (highDischargeValuePeriod && revenueToDischarge > 0 && excessAcCapacity && batteryHasDischargeCapacity && interconnectionHasCapacity) { double loss_kw = _Battery->calculate_loss(m_batteryPower->powerBatteryTarget, lifetimeIndex); // Battery is responsible for covering discharge losses if (m_batteryPower->connectionMode == BatteryPower::DC_CONNECTED) { powerBattery = _inverter_paco + loss_kw - m_batteryPower->powerSystem; @@ -340,6 +355,7 @@ void dispatch_automatic_front_of_meter_t::update_dispatch(size_t year, size_t ho else { powerBattery = _inverter_paco; // AC connected battery is already maxed out by AC power limit, cannot increase dispatch to ccover losses } + powerBattery = std::fmin(powerBattery, interconnectionCapacity); } // save for extraction m_batteryPower->powerBatteryTarget = powerBattery; diff --git a/shared/lib_battery_dispatch_automatic_fom.h b/shared/lib_battery_dispatch_automatic_fom.h index c64c169dc..ee23663f2 100644 --- a/shared/lib_battery_dispatch_automatic_fom.h +++ b/shared/lib_battery_dispatch_automatic_fom.h @@ -70,6 +70,7 @@ class dispatch_automatic_front_of_meter_t : public dispatch_automatic_t bool can_clipcharge, bool can_grid_charge, bool can_fuelcell_charge, + bool can_curtail_charge, double inverter_paco, std::vector battReplacementCostPerkWh, int battCycleCostChoice, @@ -142,6 +143,7 @@ class dispatch_automatic_front_of_meter_t : public dispatch_automatic_t double revenueToGridCharge; double revenueToClipCharge; double revenueToDischarge; + double revenueToCurtailCharge; }; #endif // __LIB_BATTERY_DISPATCH_AUTOMATIC_FOM_H__ diff --git a/shared/lib_battery_dispatch_manual.cpp b/shared/lib_battery_dispatch_manual.cpp index ad3e53041..d9c038389 100644 --- a/shared/lib_battery_dispatch_manual.cpp +++ b/shared/lib_battery_dispatch_manual.cpp @@ -44,12 +44,12 @@ dispatch_manual_t::dispatch_manual_t(battery_t * Battery, double dt, double SOC_ double t_min, int mode, int battMeterPosition, util::matrix_t dm_dynamic_sched, util::matrix_t dm_dynamic_sched_weekend, std::vector dm_charge, std::vector dm_discharge, std::vector dm_gridcharge, std::vector dm_fuelcellcharge, std::vector dm_btm_to_grid, - std::map dm_percent_discharge, std::map dm_percent_gridcharge, bool can_clip_charge, double interconnection_limit, + std::map dm_percent_discharge, std::map dm_percent_gridcharge, bool can_clip_charge, bool can_curtail_charge, double interconnection_limit, bool chargeOnlySystemExceedLoad, bool dischargeOnlyLoadExceedSystem, double SOC_min_outage, bool priorityChargeBattery) : dispatch_t(Battery, dt, SOC_min, SOC_max, current_choice, Ic_max, Id_max, Pc_max_kwdc, Pd_max_kwdc, Pc_max_kwac, Pd_max_kwac, t_min, mode, battMeterPosition, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, SOC_min_outage) { - init_with_vects(dm_dynamic_sched, dm_dynamic_sched_weekend, dm_charge, dm_discharge, dm_gridcharge, dm_fuelcellcharge, dm_btm_to_grid, dm_percent_discharge, dm_percent_gridcharge, can_clip_charge, priorityChargeBattery); + init_with_vects(dm_dynamic_sched, dm_dynamic_sched_weekend, dm_charge, dm_discharge, dm_gridcharge, dm_fuelcellcharge, dm_btm_to_grid, dm_percent_discharge, dm_percent_gridcharge, can_clip_charge, can_curtail_charge, priorityChargeBattery); } void dispatch_manual_t::init_with_vects( @@ -63,6 +63,7 @@ void dispatch_manual_t::init_with_vects( std::map dm_percent_discharge, std::map dm_percent_gridcharge, bool can_clip_charge, + bool can_curtail_charge, bool priorityChargeBattery) { _sched = dm_dynamic_sched; @@ -75,6 +76,7 @@ void dispatch_manual_t::init_with_vects( _percent_discharge_array = dm_percent_discharge; _percent_charge_array = dm_percent_gridcharge; _can_clip_charge = can_clip_charge; + _can_curtail_charge = can_curtail_charge; _priority_charge_battery = priorityChargeBattery; } @@ -85,7 +87,7 @@ dispatch_t(dispatch) const dispatch_manual_t * tmp = dynamic_cast(&dispatch); init_with_vects(tmp->_sched, tmp->_sched_weekend, tmp->_charge_array, tmp->_discharge_array, tmp->_gridcharge_array, tmp->_fuelcellcharge_array, tmp->_discharge_grid_array, - tmp->_percent_discharge_array, tmp->_percent_charge_array, tmp->_can_clip_charge, tmp->_priority_charge_battery); + tmp->_percent_discharge_array, tmp->_percent_charge_array, tmp->_can_clip_charge, tmp->_can_curtail_charge, tmp->_priority_charge_battery); } // shallow copy from dispatch to this @@ -95,7 +97,7 @@ void dispatch_manual_t::copy(const dispatch_t * dispatch) const dispatch_manual_t * tmp = dynamic_cast(dispatch); init_with_vects(tmp->_sched, tmp->_sched_weekend, tmp->_charge_array, tmp->_discharge_array, tmp->_gridcharge_array, tmp->_fuelcellcharge_array, tmp->_discharge_grid_array, - tmp->_percent_discharge_array, tmp->_percent_charge_array, tmp->_can_clip_charge, tmp->_priority_charge_battery); + tmp->_percent_discharge_array, tmp->_percent_charge_array, tmp->_can_clip_charge, tmp->_can_curtail_charge, tmp->_priority_charge_battery); } void dispatch_manual_t::prepareDispatch(size_t hour_of_year, size_t ) @@ -115,6 +117,7 @@ void dispatch_manual_t::prepareDispatch(size_t hour_of_year, size_t ) m_batteryPower->canDischarge = _discharge_array[iprofile - 1]; m_batteryPower->canGridCharge = _gridcharge_array[iprofile - 1]; m_batteryPower->canClipCharge = _can_clip_charge; + m_batteryPower->canCurtailCharge = _can_curtail_charge; if (iprofile <= _fuelcellcharge_array.size()) { m_batteryPower->canFuelCellCharge = _fuelcellcharge_array[iprofile - 1]; @@ -128,7 +131,7 @@ void dispatch_manual_t::prepareDispatch(size_t hour_of_year, size_t ) _percent_charge = 0.; if (m_batteryPower->canDischarge){ _percent_discharge = _percent_discharge_array[iprofile]; } - if (m_batteryPower->canClipCharge || m_batteryPower->canSystemCharge || m_batteryPower->canFuelCellCharge){ _percent_charge = 100.; } + if (m_batteryPower->canCurtailCharge || m_batteryPower->canClipCharge || m_batteryPower->canSystemCharge || m_batteryPower->canFuelCellCharge){ _percent_charge = 100.; } if (m_batteryPower->canGridCharge){ _percent_charge = _percent_charge_array[iprofile]; } } void dispatch_manual_t::dispatch(size_t year, @@ -209,6 +212,13 @@ bool dispatch_manual_t::check_constraints(double &I, size_t count) else I -= (m_batteryPower->powerBatteryToGrid / std::abs(m_batteryPower->powerBatteryAC)) * std::abs(I); } + // Back off discharge if we're violating interconnection limits + else if (m_batteryPower->powerInterconnectionLoss > 0 && m_batteryPower->powerBatteryAC > 0) { + I -= m_batteryPower->powerInterconnectionLoss / std::abs(m_batteryPower->powerBatteryAC) * std::abs(I); + if (I < 0) { + I = 0.0; + } + } else iterate = false; diff --git a/shared/lib_battery_dispatch_manual.h b/shared/lib_battery_dispatch_manual.h index 1c185c209..06728145c 100644 --- a/shared/lib_battery_dispatch_manual.h +++ b/shared/lib_battery_dispatch_manual.h @@ -65,6 +65,7 @@ class dispatch_manual_t : public dispatch_t std::map dm_percent_discharge, std::map dm_percent_gridcharge, bool can_clip_charge, + bool can_curtail_charge, double interconnection_limit, bool chargeOnlySystemExceedLoad = true, bool dischargeOnlyLoadExceedSystem = true, @@ -100,6 +101,7 @@ class dispatch_manual_t : public dispatch_t std::map dm_percent_discharge, std::map dm_percent_gridcharge, bool can_clip_charge, + bool can_curtail_charge, bool priorityChargeBattery); void SOC_controller() override; @@ -114,6 +116,7 @@ class dispatch_manual_t : public dispatch_t std::vector _fuelcellcharge_array; std::vector _discharge_grid_array; bool _can_clip_charge; + bool _can_curtail_charge; bool _priority_charge_battery; double _percent_discharge; diff --git a/shared/lib_battery_dispatch_pvsmoothing_fom.cpp b/shared/lib_battery_dispatch_pvsmoothing_fom.cpp index bc9fc0000..ca1d22ef2 100644 --- a/shared/lib_battery_dispatch_pvsmoothing_fom.cpp +++ b/shared/lib_battery_dispatch_pvsmoothing_fom.cpp @@ -60,6 +60,7 @@ dispatch_pvsmoothing_front_of_meter_t::dispatch_pvsmoothing_front_of_meter_t( bool can_clip_charge, bool can_grid_charge, bool can_fuelcell_charge, + bool can_curtail_charge, double inverter_paco, std::vector battReplacementCostPerkWh, int battCycleCostChoice, @@ -86,7 +87,8 @@ dispatch_pvsmoothing_front_of_meter_t::dispatch_pvsmoothing_front_of_meter_t( double interconnection_limit ) : dispatch_automatic_t(Battery, dt_hour, SOC_min, SOC_max, current_choice, Ic_max, Id_max, Pc_max_kwdc, Pd_max_kwdc, Pc_max_kwac, Pd_max_kwac, - t_min, dispatch_mode, weather_forecast_mode, pv_dispatch, nyears, look_ahead_hours, dispatch_update_frequency_hours, can_charge, can_clip_charge, can_grid_charge, can_fuelcell_charge, + t_min, dispatch_mode, weather_forecast_mode, pv_dispatch, nyears, look_ahead_hours, dispatch_update_frequency_hours, + can_charge, can_clip_charge, can_grid_charge, can_fuelcell_charge, can_curtail_charge, battReplacementCostPerkWh, battCycleCostChoice, battCycleCost, battOMCost, interconnection_limit), m_batt_dispatch_pvs_nameplate_ac(batt_dispatch_pvs_nameplate_ac), m_batt_dispatch_pvs_ac_lb(batt_dispatch_pvs_ac_lb), diff --git a/shared/lib_battery_dispatch_pvsmoothing_fom.h b/shared/lib_battery_dispatch_pvsmoothing_fom.h index 29b9bc593..9fa120572 100644 --- a/shared/lib_battery_dispatch_pvsmoothing_fom.h +++ b/shared/lib_battery_dispatch_pvsmoothing_fom.h @@ -67,6 +67,7 @@ class dispatch_pvsmoothing_front_of_meter_t : public dispatch_automatic_t bool can_clipcharge, bool can_grid_charge, bool can_fuelcell_charge, + bool can_curtail_charge, double inverter_paco, std::vector battReplacementCostPerkWh, // required for base class int battCycleCostChoice, diff --git a/shared/lib_battery_powerflow.cpp b/shared/lib_battery_powerflow.cpp index 5a9b0ae08..883538bc7 100644 --- a/shared/lib_battery_powerflow.cpp +++ b/shared/lib_battery_powerflow.cpp @@ -94,6 +94,7 @@ BatteryPower::BatteryPower(double dtHour) : inverterEfficiencyCutoff(5), canSystemCharge(false), canClipCharge(false), + canCurtailCharge(false), canGridCharge(false), canDischarge(false), canDischargeToGrid(false), @@ -162,6 +163,7 @@ BatteryPower::BatteryPower(const BatteryPower& orig) { inverterEfficiencyCutoff = orig.inverterEfficiencyCutoff; canSystemCharge = orig.canSystemCharge; canClipCharge = orig.canClipCharge; + canCurtailCharge = orig.canCurtailCharge; canGridCharge = orig.canGridCharge; canDischarge = orig.canDischarge; canDischargeToGrid = orig.canDischargeToGrid; @@ -280,12 +282,20 @@ void BatteryPowerFlow::initialize(double stateOfCharge, bool systemPriorityCharg m_BatteryPower->powerBatteryDC = m_BatteryPower->powerBatteryDischargeMaxDC; } // Is there extra power from system - else if ((((m_BatteryPower->powerSystem > m_BatteryPower->powerLoad) || !m_BatteryPower->chargeOnlySystemExceedLoad) && m_BatteryPower->canSystemCharge) || m_BatteryPower->canGridCharge || m_BatteryPower->canClipCharge) + else if ((((m_BatteryPower->powerSystem > m_BatteryPower->powerLoad) || !m_BatteryPower->chargeOnlySystemExceedLoad) && m_BatteryPower->canSystemCharge) || m_BatteryPower->canGridCharge || m_BatteryPower->canClipCharge || m_BatteryPower->canCurtailCharge) { if (m_BatteryPower->canClipCharge) { m_BatteryPower->powerBatteryDC = -m_BatteryPower->powerSystemClipped; } + if (m_BatteryPower->canCurtailCharge) { + double interconnectionCapacity = std::fmin(m_BatteryPower->powerInterconnectionLimit, m_BatteryPower->powerCurtailmentLimit) - m_BatteryPower->powerSystem; + if (interconnectionCapacity < 0.0 ) + { + m_BatteryPower->powerBatteryDC = interconnectionCapacity * m_BatteryPower->singlePointEfficiencyACToDC; + } + } + if (m_BatteryPower->canSystemCharge) { if (systemPriorityCharge) { @@ -364,7 +374,7 @@ void BatteryPowerFlow::calculateACConnected() if (P_battery_ac <= 0) { // Test if battery is charging erroneously - if (!(m_BatteryPower->canSystemCharge || m_BatteryPower->canGridCharge || m_BatteryPower->canFuelCellCharge) && P_battery_ac < 0) { + if (!(m_BatteryPower->canSystemCharge || m_BatteryPower->canGridCharge || m_BatteryPower->canFuelCellCharge || m_BatteryPower->canCurtailCharge) && P_battery_ac < 0) { P_pv_to_batt_ac = P_grid_to_batt_ac = P_fuelcell_to_batt_ac = 0; P_battery_ac = 0; } @@ -381,7 +391,7 @@ void BatteryPowerFlow::calculateACConnected() } // Excess PV can go to battery, if PV can cover charging losses - if (m_BatteryPower->canSystemCharge) { + if (m_BatteryPower->canSystemCharge || m_BatteryPower->canCurtailCharge) { P_pv_to_batt_ac = std::abs(P_battery_ac); P_available_pv = P_pv_ac - P_pv_to_load_ac - P_system_loss_ac; if (P_pv_to_batt_ac > P_available_pv) @@ -606,6 +616,9 @@ void BatteryPowerFlow::calculateACConnected() P_grid_ac = 0; if (std::abs(P_crit_load_unmet_ac) < m_BatteryPower->tolerance) P_crit_load_unmet_ac = 0; + if (std::abs(P_interconnection_loss_ac) < m_BatteryPower->tolerance) { + P_interconnection_loss_ac = 0; + } // assign outputs m_BatteryPower->powerBatteryAC = P_battery_ac; @@ -699,7 +712,7 @@ void BatteryPowerFlow::calculateDCConnected() { // First check whether battery charging came from PV. // Assumes that if battery is charging and can charge from PV, that it will charge from PV before using the grid - if (m_BatteryPower->canSystemCharge || m_BatteryPower->canClipCharge) { + if (m_BatteryPower->canSystemCharge || m_BatteryPower->canClipCharge || m_BatteryPower->canCurtailCharge) { P_pv_to_batt_dc = std::abs(P_battery_dc); if (P_pv_to_batt_dc > P_pv_dc - P_system_loss_dc) { P_pv_to_batt_dc = P_pv_dc - P_system_loss_dc; @@ -1011,6 +1024,9 @@ void BatteryPowerFlow::calculateDCConnected() P_grid_ac = 0; if (std::abs(P_crit_load_unmet_ac) < m_BatteryPower->tolerance) P_crit_load_unmet_ac = 0; + if (std::abs(P_interconnection_loss_ac) < m_BatteryPower->tolerance) { + P_interconnection_loss_ac = 0; + } // assign outputs m_BatteryPower->singlePointEfficiencyDCToAC = efficiencyDCAC; diff --git a/shared/lib_battery_powerflow.h b/shared/lib_battery_powerflow.h index 10d0da630..5fbda57de 100644 --- a/shared/lib_battery_powerflow.h +++ b/shared/lib_battery_powerflow.h @@ -188,7 +188,7 @@ struct BatteryPower double powerBatteryDischargeMaxAC; ///< The maximum sustained power the battery can discharge (kWac) double powerSystemLoss; ///< The auxiliary power loss in the system (kW) double powerConversionLoss; ///< The power loss due to conversions in the battery power electronics (kW) - double powerInterconnectionLimit; ///< The size of the grid interconnection (kW). As of July 2021 only applies to discharging, should apply to charging & dispatch + double powerInterconnectionLimit; ///< The size of the grid interconnection (kW). double powerInterconnectionLoss; ///< The power loss due to interconnection limit, outage, or curtailment (kW) double powerCurtailmentLimit; ///< The curtailment limit for the current step (kW) double voltageSystem; ///< The system voltage @@ -214,6 +214,7 @@ struct BatteryPower bool canSystemCharge; ///< A boolean specifying whether the battery is allowed to charge from PV in the timestep bool canClipCharge; ///< A boolean specifying whether the battery is allowed to charge from otherwise clipped PV in the timestep + bool canCurtailCharge; ///< A boolean specifying whether the battery is allowed to charge from otherwise curtailed energy in the timestep bool canGridCharge; ///< A boolean specifying whether the battery is allowed to charge from the Grid in the timestep bool canDischarge; ///< A boolean specifying whether the battery is allowed to discharge in the timestep bool canDischargeToGrid; ///< A boolean specifying whether the battery is allowed to discharge to grid in the timestep diff --git a/shared/lib_irradproc.cpp b/shared/lib_irradproc.cpp index f1c51c8d9..74ce0ed0a 100644 --- a/shared/lib_irradproc.cpp +++ b/shared/lib_irradproc.cpp @@ -49,10 +49,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "lib_weatherfile.h" static const int __nday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -std::unordered_map> spa_table; -int spa_table_day; - /// Compute the Julian day of year static int julian(int yr, int month, int day) { int i = 1, jday = 0, k; @@ -803,47 +799,96 @@ double sun_rise_and_set(double *m_rts, double *h_rts, double *delta_prime, doubl (360.0 * cos(DTOR * (delta_prime[sun])) * cos(DTOR * (latitude)) * sin(DTOR * (h_prime[sun]))); } -void clear_spa_table() { +bool spa_table_key::operator==(const spa_table_key &other) const { + return (jd == other.jd + && delta_t == other.delta_t + && pressure == other.pressure + && temp == other.temp + && ascension == other.ascension + && declination == other.declination + ); +} + +spa_table_key::spa_table_key(double j, double dt, double p, double t, double a, double d): + jd(j), delta_t(dt), ascension(a), declination(d) { + int pressure_bucket = 10; + pressure = ((int)(p + pressure_bucket/2) / pressure_bucket) * pressure_bucket; + pressure = (int)pressure; + int temp_bucket = 5; + temp = ((int)(t + temp_bucket / 2) / temp_bucket) * temp_bucket; + temp = (int)temp; + } + +std::size_t std::hash::operator()(const spa_table_key& k) const +{ +using std::hash; +// Compute individual hash values for first, second, etc and combine them using XOR and bit shifting: +return +(((( + ((((hash()(k.jd) + ^ (hash()(k.delta_t) << 1)) >> 1) + ^ (hash()(k.pressure) << 1)) >> 1) + ^ (hash()(k.temp) << 1)) >> 1) + ^ (hash()(k.ascension) << 1)) >> 1) + ^ (hash()(k.declination) << 1) + ; +} + +solarpos_lookup::solarpos_lookup(){ + clear_spa_table(); +} + +void solarpos_lookup::clear_spa_table() { spa_table.clear(); spa_table_day = 0; }; // The algorithm reuses the outputs from the last 3 days or so, so the hash table is emptied every 3 days to reduce size -void roll_spa_table_forward(int day) { +void solarpos_lookup::roll_spa_table_forward(int day) { if (std::abs(spa_table_day - day) > 3){ spa_table_day = day; spa_table.clear(); } }; +std::vector* solarpos_lookup::find(spa_table_key spa_key_inputs) { + auto spa_pos = spa_table.end(); + spa_pos = spa_table.find(spa_key_inputs); + if (spa_pos != spa_table.end()) + return &spa_pos->second; + return nullptr; +} + +void solarpos_lookup::insert(spa_table_key spa_key_inputs, std::vector spa_outputs) { + spa_table[spa_key_inputs] = spa_outputs; +} + void calculate_spa(double jd, double lat, double lng, double alt, double pressure, double temp, double delta_t, double tilt, - double azm_rotation, double ascension_and_declination[2], double needed_values[9]) { + double azm_rotation, double ascension_and_declination[2], double needed_values[9], std::shared_ptr spa_table) { //Calculate the Julian and Julian Ephemeris, Day, Century, and Millennium (3.1) //double jd = julian_day(year, month, day, hour, minute, second, delta_t, tz); double jc = julian_century(jd); // for 2000 standard epoch double jde = julian_ephemeris_day(jd, delta_t); //Adjusted for difference between Earth rotation time and the Terrestrial Time (TT) (derived from observation, reported yearly in Astronomical Almanac) - bool use_table = true; - spa_table_key spa_key_inputs(jd, delta_t, pressure, temp, ascension_and_declination[0], ascension_and_declination[1]); - auto spa_pos = spa_table.end(); - if (use_table) - spa_pos = spa_table.find(spa_key_inputs); - - if (spa_pos != spa_table.end()){ - needed_values[0] = spa_pos->second[0]; - needed_values[1] = spa_pos->second[1]; - needed_values[2] = spa_pos->second[2]; - needed_values[3] = spa_pos->second[3]; - needed_values[4] = spa_pos->second[4]; - needed_values[5] = spa_pos->second[5]; - needed_values[6] = spa_pos->second[6]; - needed_values[7] = spa_pos->second[7]; - needed_values[8] = spa_pos->second[8]; - ascension_and_declination[0] = spa_pos->second[9]; - ascension_and_declination[1] = spa_pos->second[10]; - return; + spa_table_key spa_key_inputs = {jd, delta_t, pressure, temp, ascension_and_declination[0], ascension_and_declination[1]}; + if (spa_table) { + std::vector* results = spa_table->find(spa_key_inputs); + if (results){ + needed_values[0] = (*results)[0]; + needed_values[1] = (*results)[1]; + needed_values[2] = (*results)[2]; + needed_values[3] = (*results)[3]; + needed_values[4] = (*results)[4]; + needed_values[5] = (*results)[5]; + needed_values[6] = (*results)[6]; + needed_values[7] = (*results)[7]; + needed_values[8] = (*results)[8]; + ascension_and_declination[0] = (*results)[9]; + ascension_and_declination[1] = (*results)[10]; + return; + } } double jce = julian_ephemeris_century(jde); //for 2000 standard epoch @@ -942,11 +987,11 @@ calculate_spa(double jd, double lat, double lng, double alt, double pressure, do azimuth = M_PI; } - std::vector spa_outputs = {needed_values[0], needed_values[1], needed_values[2], needed_values[3], needed_values[4], needed_values[5], needed_values[6], needed_values[7], needed_values[8], - ascension_and_declination[0], ascension_and_declination[1]}; - - if (use_table) - spa_table[spa_key_inputs] = spa_outputs; + if (spa_table) { + std::vector spa_outputs = {needed_values[0], needed_values[1], needed_values[2], needed_values[3], needed_values[4], needed_values[5], needed_values[6], needed_values[7], needed_values[8], + ascension_and_declination[0], ascension_and_declination[1]}; + spa_table->insert(spa_key_inputs, spa_outputs); + } //Calculate the incidence angle for a selected surface (3.16) //double aoi = surface_incidence_angle(zenith, azimuth_astro, azm_rotation, tilt); //incidence angle for a surface oriented in any direction (degrees) @@ -957,7 +1002,7 @@ void calculate_eot_and_sun_rise_transit_set(double jme, double tz, double alpha, double del_psi, double epsilon, double jd, int year, int month, int day, double lat, double lng, double alt, double pressure, double temp, double tilt, double delta_t, double azm_rotation, - double needed_values[4]) { + double needed_values[4], std::shared_ptr spa_table) { // Equation of Time (A.1) double M = sun_mean_longitude(jme); // degrees double E = eot(M, alpha, del_psi, epsilon); //Equation of Time (degrees) @@ -966,7 +1011,7 @@ calculate_eot_and_sun_rise_transit_set(double jme, double tz, double alpha, doub // Sunrise, Sunset, and Sun Transit (A.2) int i; //initialize iterator - double sun_declination_and_ascension[2]; // storage for iterating sun declination and ascension results + double sun_declination_and_ascension[2] {0, 0}; // storage for iterating sun declination and ascension results double needed_values2[13]; //where is this used? double alpha_array[3]; //storage for alphas in iteration double delta_array[3]; //storage for deltas in iteration @@ -977,7 +1022,7 @@ calculate_eot_and_sun_rise_transit_set(double jme, double tz, double alpha, doub 0); //initialize Julian array with day in question (0 UT (0h0mm0s0tz) //run calculate_spa to obtain apparent sidereal time at Greenwich at 0 UT (v, degrees) calculate_spa(jd_array[1], lat, lng, alt, pressure, temp, 67, tilt, azm_rotation, sun_declination_and_ascension, - needed_values_nu); + needed_values_nu, spa_table); double delta_test = sun_declination_and_ascension[1]; double nu = needed_values_nu[4]; //store apparent sidereal time at Greenwich jd_array[0] = jd_array[1] - 1; //Julian day for the day prior to the day in question @@ -985,7 +1030,7 @@ calculate_eot_and_sun_rise_transit_set(double jme, double tz, double alpha, doub for (i = 0; i < 3; i++) { // iterate through day behind (0), day in question (1), and day following (2) //Calculate the spa for each day at 0 TT by setting the delta_T difference between UT and TT to 0 calculate_spa(jd_array[i], lat, lng, alt, pressure, temp, 0, tilt, azm_rotation, sun_declination_and_ascension, - needed_values2); + needed_values2, spa_table); alpha_array[i] = sun_declination_and_ascension[0]; //store geocentric right ascension for each iteration (degrees) delta_array[i] = sun_declination_and_ascension[1]; //store geocentric declination for each iteration (degrees) } @@ -1075,7 +1120,8 @@ calculate_eot_and_sun_rise_transit_set(double jme, double tz, double alpha, doub void solarpos_spa(int year, int month, int day, int hour, double minute, double second, double lat, double lng, double tz, - double dut1, double alt, double pressure, double temp, double tilt, double azm_rotation, double sunn[9]) { + double dut1, double alt, double pressure, double temp, double tilt, double azm_rotation, double sunn[9], + std::shared_ptr spa_table) { int t; double delta_t; @@ -1101,13 +1147,14 @@ solarpos_spa(int year, int month, int day, int hour, double minute, double secon double needed_values_eot[4]; //preallocate storage for output from calculate_spa double needed_values_eot_check[4]; - roll_spa_table_forward(day); + if (spa_table){ + spa_table->roll_spa_table_forward(day); + } calculate_spa(jd, lat, lng, alt, pressure, temp, delta_t, tilt, azm_rotation, ascension_and_declination, - needed_values_spa); //calculate solar position algorithm values + needed_values_spa, spa_table); //calculate solar position algorithm values calculate_eot_and_sun_rise_transit_set(needed_values_spa[0], tz, ascension_and_declination[0], needed_values_spa[2], needed_values_spa[3], jd, year, month, day, lat, lng, alt, pressure, temp, - tilt, delta_t, azm_rotation, - needed_values_eot); //calculate Equation of Time and sunrise/sunset values + tilt, delta_t, azm_rotation, needed_values_eot, spa_table); //calculate Equation of Time and sunrise/sunset values double __n_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; @@ -1119,17 +1166,17 @@ solarpos_spa(int year, int month, int day, int hour, double minute, double secon calculate_eot_and_sun_rise_transit_set(needed_values_spa[0], tz, ascension_and_declination[0], needed_values_spa[2], needed_values_spa[3], jd, year, month, day + 1, lat, lng, alt, pressure, temp, tilt, delta_t, azm_rotation, - needed_values_eot_check); //calculate Equation of Time and sunrise/sunset values + needed_values_eot_check, spa_table); //calculate Equation of Time and sunrise/sunset values else if (month < 12) //on the 1st of the month, need to switch to the last day of previous month calculate_eot_and_sun_rise_transit_set(needed_values_spa[0], tz, ascension_and_declination[0], needed_values_spa[2], needed_values_spa[3], jd, year, month + 1, 1, lat, lng, alt, pressure, temp, tilt, delta_t, azm_rotation, - needed_values_eot_check); + needed_values_eot_check, spa_table); else //on the first day of the year, need to switch to Dec 31 of last year calculate_eot_and_sun_rise_transit_set(needed_values_spa[0], tz, ascension_and_declination[0], needed_values_spa[2], needed_values_spa[3], jd, year + 1, 1, 1, lat, lng, alt, pressure, temp, tilt, delta_t, azm_rotation, - needed_values_eot_check); + needed_values_eot_check, spa_table); //on the last day of endless days, sunset is returned as 100 (hour angle too large for calculation), so use today's sunset time as a proxy needed_values_eot[3] = needed_values_eot_check[3] + 24; @@ -1817,7 +1864,7 @@ void irrad::setup() { irrad::irrad() { setup(); - clear_spa_table(); + spa_table = std::make_shared(solarpos_lookup()); } irrad::irrad(weather_header hdr, @@ -2240,7 +2287,7 @@ int irrad::calc() { if (!getStoredSolarposOutputs()) { // calculate sunrise and sunset hours in local standard time for the current day - solarpos_spa(year, month, day, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunAnglesRadians); + solarpos_spa(year, month, day, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunAnglesRadians, spa_table); double t_sunrise = sunAnglesRadians[4]; double t_sunset = sunAnglesRadians[5]; @@ -2250,11 +2297,11 @@ int irrad::calc() { { double sunanglestemp[9]; if (day > 1) //simply decrement day during month - solarpos_spa(year, month, day - 1, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp); + solarpos_spa(year, month, day - 1, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp, spa_table); else if (month > 1) //on the 1st of the month, need to switch to the last day of previous month - solarpos_spa(year, month - 1, __nday[month - 2], 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp); + solarpos_spa(year, month - 1, __nday[month - 2], 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp, spa_table); else //on the first day of the year, need to switch to Dec 31 of last year - solarpos_spa(year - 1, 12, 31, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp); + solarpos_spa(year - 1, 12, 31, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp, spa_table); //on the last day of endless days, sunset is returned as 100 (hour angle too large for calculation), so use today's sunset time as a proxy if (sunanglestemp[5] == 100.0) t_sunset -= 24.0; @@ -2268,11 +2315,11 @@ int irrad::calc() { { double sunanglestemp[9]; if (day < __nday[month - 1]) //simply increment the day during the month, month is 1-indexed and __nday is 0-indexed - solarpos_spa(year, month, day + 1, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp); + solarpos_spa(year, month, day + 1, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp, spa_table); else if (month < 12) //on the last day of the month, need to switch to the first day of the next month - solarpos_spa(year, month + 1, 1, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp); + solarpos_spa(year, month + 1, 1, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp, spa_table); else //on the last day of the year, need to switch to Jan 1 of the next year - solarpos_spa(year + 1, 1, 1, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp); + solarpos_spa(year + 1, 1, 1, 12, 0.0, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunanglestemp, spa_table); //on the last day of endless days, sunrise would be returned as -100 (hour angle too large for calculations), so use today's sunrise time as a proxy if (sunanglestemp[4] == -100.0) t_sunrise += 24.0; @@ -2291,7 +2338,7 @@ int irrad::calc() { timeStepSunPosition[0] = hr_calc; timeStepSunPosition[1] = (int) min_calc; - solarpos_spa(year, month, day, hr_calc, min_calc, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunAnglesRadians); + solarpos_spa(year, month, day, hr_calc, min_calc, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunAnglesRadians, spa_table); timeStepSunPosition[2] = 2; } @@ -2304,7 +2351,7 @@ int irrad::calc() { timeStepSunPosition[0] = hr_calc; timeStepSunPosition[1] = (int) min_calc; - solarpos_spa(year, month, day, hr_calc, min_calc, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunAnglesRadians); + solarpos_spa(year, month, day, hr_calc, min_calc, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunAnglesRadians, spa_table); timeStepSunPosition[2] = 3; } @@ -2315,12 +2362,12 @@ int irrad::calc() { { timeStepSunPosition[0] = hour; timeStepSunPosition[1] = (int)minute; - solarpos_spa(year, month, day, hour, minute, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunAnglesRadians); + solarpos_spa(year, month, day, hour, minute, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunAnglesRadians, spa_table); timeStepSunPosition[2] = 1; } else { // sun is down, assign sundown values - solarpos_spa(year, month, day, hour, minute, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunAnglesRadians); + solarpos_spa(year, month, day, hour, minute, 0.0, latitudeDegrees, longitudeDegrees, timezone, dut1, elevation, pressure, tamb, tiltDegrees, surfaceAzimuthDegrees, sunAnglesRadians, spa_table); timeStepSunPosition[0] = hour; timeStepSunPosition[1] = (int) minute; timeStepSunPosition[2] = 0; diff --git a/shared/lib_irradproc.h b/shared/lib_irradproc.h index 4b2d1dee0..bb5ce7699 100644 --- a/shared/lib_irradproc.h +++ b/shared/lib_irradproc.h @@ -602,6 +602,48 @@ double rts_sun_altitude(double latitude, double delta_prime, double h_prime); double sun_rise_and_set(double* m_rts, double* h_rts, double* delta_prime, double latitude, double* h_prime, double h0_prime, int sun); +/** + * Table for storing the recently computed solarpos_spa intermediate outputs + * The algorithm reuses the outputs from the last 3 days or so, so the hash table is emptied every 3 days to reduce size + * Latitude, Longitude, Altitude, Tilt and Azimuth are not in the key because they remain constant throughout + */ + +struct spa_table_key { + double jd; + double delta_t; + int pressure; + int temp; + // these are both inputs and outputs (e.g. also stored in the output vector) + double ascension; + double declination; + + bool operator==(const spa_table_key &other) const; + + spa_table_key(double j, double dt, double p, double t, double a, double d); +}; + +template <> +struct std::hash +{ + std::size_t operator()(const spa_table_key& k) const; +}; + +class solarpos_lookup +{ + std::unordered_map> spa_table; + int spa_table_day; + void clear_spa_table(); + + public: + + solarpos_lookup(); + void roll_spa_table_forward(int day); + + std::vector* find(spa_table_key spa_key_inputs); + void insert(spa_table_key spa_key_inputs, std::vector spa_outputs); +}; + + /** * calculate_spa function combines numerous functions to calculate the Solar Position Algorithm parameters * @@ -629,66 +671,10 @@ double sun_rise_and_set(double* m_rts, double* h_rts, double* delta_prime, doubl * \param[out] needed_values[8] azimuth topocentric azimuth angle (degrees) */ - void calculate_spa(double jd, double lat, double lng, double alt, double pressure, double temp, - double delta_t, double tilt, double azm_rotation, double ascension_and_declination[2], double needed_values[9]); + double delta_t, double tilt, double azm_rotation, double ascension_and_declination[2], double needed_values[9], + std::shared_ptr spa_table=nullptr); -/** - * Table for storing the recently computed solarpos_spa intermediate outputs - * The algorithm reuses the outputs from the last 3 days or so, so the hash table is emptied every 3 days to reduce size - * Latitude, Longitude, Altitude, Tilt and Azimuth are not in the key because they remain constant throughout - */ -struct spa_table_key { - double jd; - double delta_t; - int pressure; - int temp; - // these are both inputs and outputs (e.g. also stored in the output vector) - double ascension; - double declination; - - bool operator==(const spa_table_key &other) const - { return (jd == other.jd - && delta_t == other.delta_t - && pressure == other.pressure - && temp == other.temp - && ascension == other.ascension - && declination == other.declination - ); - } - - spa_table_key(double j, double dt, double p, double t, double a, double d): - jd(j), delta_t(dt), ascension(a), declination(d) - { - int pressure_bucket = 10; - pressure = ((int)(p + pressure_bucket/2) / pressure_bucket) * pressure_bucket; - pressure = (int)pressure; - int temp_bucket = 5; - temp = ((int)(t + temp_bucket / 2) / temp_bucket) * temp_bucket; - temp = (int)temp; - } -}; - -template <> -struct std::hash -{ - std::size_t operator()(const spa_table_key& k) const - { - using std::hash; - // Compute individual hash values for first, second, etc and combine them using XOR and bit shifting: - return - (((( - ((((hash()(k.jd) - ^ (hash()(k.delta_t) << 1)) >> 1) - ^ (hash()(k.pressure) << 1)) >> 1) - ^ (hash()(k.temp) << 1)) >> 1) - ^ (hash()(k.ascension) << 1)) >> 1) - ^ (hash()(k.declination) << 1) - ; - } -}; - -void clear_spa_table(); /** * @@ -763,7 +749,8 @@ void calculate_eot_and_sun_rise_transit_set(double jme, double tz, double alpha, * \param[out] sunn[7] true solar time (hrs) * \param[out] sunn[8] extraterrestrial solar irradiance on horizontal at particular time (W/m2) */ -void solarpos_spa(int year, int month, int day, int hour, double minute, double second, double lat, double lng, double tz, double dut1, double alt, double pressure, double temp, double tilt, double azm_rotation, double sunn[9]); +void solarpos_spa(int year, int month, int day, int hour, double minute, double second, double lat, double lng, double tz, double dut1, double alt, double pressure, double temp, double tilt, double azm_rotation, double sunn[9], + std::shared_ptr spa_table=nullptr); /** @} */ // end of solarpos_spa group /** @@ -1112,6 +1099,9 @@ class irrad std::vector> solarpos_outputs_for_lifetime; ///< Table of solarpos outputs stored for lifetime simulations void storeSolarposOutputs(); + + std::shared_ptr spa_table; + public: /// Directive to indicate that if delt_hr is less than zero, do not interpolate sunrise and sunset hours diff --git a/ssc/CMakeLists.txt b/ssc/CMakeLists.txt index c2652da09..4bf18b386 100644 --- a/ssc/CMakeLists.txt +++ b/ssc/CMakeLists.txt @@ -32,6 +32,7 @@ set(SSC_SRC cmod_csp_subcomponent.cpp cmod_csp_trough_eqns.cpp cmod_csp_trough_eqns.h + cmod_custom_generation.cpp cmod_etes_electric_resistance.cpp cmod_etes_ptes.cpp cmod_equpartflip.cpp @@ -44,7 +45,6 @@ set(SSC_SRC cmod_fuelcell.h cmod_generic_system-builder.cpp cmod_generic_system-builder.h - cmod_generic_system.cpp cmod_geothermal.cpp cmod_geothermal_costs.cpp cmod_geothermal_eqns.cpp diff --git a/ssc/cmod_battery.cpp b/ssc/cmod_battery.cpp index a0b895010..50b0a330b 100644 --- a/ssc/cmod_battery.cpp +++ b/ssc/cmod_battery.cpp @@ -167,6 +167,7 @@ var_info vtab_battery_inputs[] = { { SSC_INPUT, SSC_NUMBER, "batt_dispatch_auto_can_gridcharge", "Grid charging allowed for automated dispatch?", "0/1", "", "BatteryDispatch", "", "", "" }, { SSC_INPUT, SSC_NUMBER, "batt_dispatch_auto_can_charge", "System charging allowed for automated dispatch?", "0/1", "", "BatteryDispatch", "", "", "" }, { SSC_INPUT, SSC_NUMBER, "batt_dispatch_auto_can_clipcharge", "Battery can charge from clipped power?", "0/1", "", "BatteryDispatch", "", "", "" }, + { SSC_INPUT, SSC_NUMBER, "batt_dispatch_auto_can_curtailcharge", "Battery can charge from grid-limited system power?", "0/1", "", "BatteryDispatch", "", "", "" }, { SSC_INPUT, SSC_NUMBER, "batt_dispatch_auto_btm_can_discharge_to_grid", "Behind the meter battery can discharge to grid?", "0/1", "", "BatteryDispatch", "", "", "" }, { SSC_INPUT, SSC_NUMBER, "batt_dispatch_charge_only_system_exceeds_load", "Battery can charge from system only when system output exceeds load", "0/1", "", "BatteryDispatch", "en_batt=1&en_standalone_batt=0&batt_meter_position=0", "", "" }, { SSC_INPUT, SSC_NUMBER, "batt_dispatch_discharge_only_load_exceeds_system","Battery can discharge battery only when load exceeds system output", "0/1", "", "BatteryDispatch", "en_batt=1&en_standalone_batt=0&batt_meter_position=0", "", "" }, @@ -746,6 +747,7 @@ battstor::battstor(var_table& vt, bool setup_model, size_t nrec, double dt_hr, c // Common to automated methods batt_vars->batt_dispatch_auto_can_charge = true; batt_vars->batt_dispatch_auto_can_clipcharge = false; + batt_vars->batt_dispatch_auto_can_curtailcharge = false; batt_vars->batt_dispatch_auto_can_gridcharge = false; batt_vars->batt_dispatch_auto_can_fuelcellcharge = true; batt_vars->batt_dispatch_auto_btm_can_discharge_to_grid = false; @@ -759,6 +761,9 @@ battstor::battstor(var_table& vt, bool setup_model, size_t nrec, double dt_hr, c if (vt.is_assigned("batt_dispatch_auto_can_clipcharge")) { batt_vars->batt_dispatch_auto_can_clipcharge = vt.as_boolean("batt_dispatch_auto_can_clipcharge"); } + if (vt.is_assigned("batt_dispatch_auto_can_curtailcharge")) { + batt_vars->batt_dispatch_auto_can_curtailcharge = vt.as_boolean("batt_dispatch_auto_can_curtailcharge"); + } if (vt.is_assigned("batt_dispatch_auto_can_fuelcellcharge")) { batt_vars->batt_dispatch_auto_can_fuelcellcharge = vt.as_boolean("batt_dispatch_auto_can_fuelcellcharge"); } @@ -1263,7 +1268,7 @@ battstor::battstor(var_table& vt, bool setup_model, size_t nrec, double dt_hr, c batt_vars->batt_dispatch, batt_vars->batt_meter_position, batt_vars->batt_discharge_schedule_weekday, batt_vars->batt_discharge_schedule_weekend, batt_vars->batt_can_charge, batt_vars->batt_can_discharge, batt_vars->batt_can_gridcharge, batt_vars->batt_can_fuelcellcharge, batt_vars->batt_btm_can_discharge_to_grid, - dm_percent_discharge, dm_percent_gridcharge, batt_vars->batt_dispatch_auto_can_clipcharge, batt_vars->grid_interconnection_limit_kW, + dm_percent_discharge, dm_percent_gridcharge, batt_vars->batt_dispatch_auto_can_clipcharge, batt_vars->batt_dispatch_auto_can_curtailcharge, batt_vars->grid_interconnection_limit_kW, batt_vars->batt_dispatch_charge_only_system_exceeds_load, batt_vars->batt_dispatch_discharge_only_load_exceeds_system, batt_vars->batt_minimum_outage_SOC, @@ -1296,7 +1301,8 @@ battstor::battstor(var_table& vt, bool setup_model, size_t nrec, double dt_hr, c batt_vars->batt_minimum_modetime, batt_vars->batt_dispatch, batt_vars->batt_dispatch_wf_forecast, batt_vars->batt_meter_position, nyears, batt_vars->batt_look_ahead_hours, batt_vars->batt_dispatch_update_frequency_hours, - batt_vars->batt_dispatch_auto_can_charge, batt_vars->batt_dispatch_auto_can_clipcharge, batt_vars->batt_dispatch_auto_can_gridcharge, batt_vars->batt_dispatch_auto_can_fuelcellcharge, + batt_vars->batt_dispatch_auto_can_charge, batt_vars->batt_dispatch_auto_can_clipcharge, batt_vars->batt_dispatch_auto_can_gridcharge, + batt_vars->batt_dispatch_auto_can_fuelcellcharge, batt_vars->batt_dispatch_auto_can_curtailcharge, batt_vars->inverter_paco, batt_vars->batt_cost_per_kwh, batt_vars->batt_cycle_cost_choice, batt_vars->batt_cycle_cost, batt_vars->om_batt_variable_cost_per_kwh, eta_pvcharge, eta_gridcharge, eta_discharge, batt_vars->batt_dispatch_pvs_nameplate_ac, @@ -1315,7 +1321,8 @@ battstor::battstor(var_table& vt, bool setup_model, size_t nrec, double dt_hr, c batt_vars->batt_minimum_modetime, batt_vars->batt_dispatch, batt_vars->batt_dispatch_wf_forecast, batt_vars->batt_meter_position, nyears, batt_vars->batt_look_ahead_hours, batt_vars->batt_dispatch_update_frequency_hours, - batt_vars->batt_dispatch_auto_can_charge, batt_vars->batt_dispatch_auto_can_clipcharge, batt_vars->batt_dispatch_auto_can_gridcharge, batt_vars->batt_dispatch_auto_can_fuelcellcharge, + batt_vars->batt_dispatch_auto_can_charge, batt_vars->batt_dispatch_auto_can_clipcharge, batt_vars->batt_dispatch_auto_can_gridcharge, + batt_vars->batt_dispatch_auto_can_fuelcellcharge, batt_vars->batt_dispatch_auto_can_curtailcharge, batt_vars->inverter_paco, batt_vars->batt_cost_per_kwh, batt_vars->batt_cycle_cost_choice, batt_vars->batt_cycle_cost, batt_vars->om_batt_variable_cost_per_kwh, batt_vars->forecast_price_series_dollar_per_kwh, utilityRate, @@ -1350,7 +1357,8 @@ battstor::battstor(var_table& vt, bool setup_model, size_t nrec, double dt_hr, c batt_vars->batt_minimum_modetime, batt_vars->batt_dispatch, batt_vars->batt_dispatch_wf_forecast, batt_vars->batt_meter_position, nyears, batt_vars->batt_look_ahead_hours, batt_vars->batt_dispatch_update_frequency_hours, - batt_vars->batt_dispatch_auto_can_charge, batt_vars->batt_dispatch_auto_can_clipcharge, batt_vars->batt_dispatch_auto_can_gridcharge, batt_vars->batt_dispatch_auto_can_fuelcellcharge, + batt_vars->batt_dispatch_auto_can_charge, batt_vars->batt_dispatch_auto_can_clipcharge, batt_vars->batt_dispatch_auto_can_gridcharge, + batt_vars->batt_dispatch_auto_can_fuelcellcharge, batt_vars->batt_dispatch_auto_can_curtailcharge, util_rate_data, batt_vars->batt_cost_per_kwh, batt_vars->batt_cycle_cost_choice, batt_vars->batt_cycle_cost, batt_vars->om_batt_variable_cost_per_kwh, batt_vars->grid_interconnection_limit_kW, batt_vars->batt_dispatch_charge_only_system_exceeds_load, batt_vars->batt_dispatch_discharge_only_load_exceeds_system, batt_vars->batt_dispatch_auto_btm_can_discharge_to_grid, batt_vars->batt_minimum_outage_SOC, batt_vars->batt_dispatch_load_forecast diff --git a/ssc/cmod_battery.h b/ssc/cmod_battery.h index 3909b05c3..a1d4cacf8 100644 --- a/ssc/cmod_battery.h +++ b/ssc/cmod_battery.h @@ -89,6 +89,9 @@ struct batt_variables /*! Determines if the battery is allowed to charge from PV clipping using automated control*/ bool batt_dispatch_auto_can_clipcharge; + /*! Determines if the battery is allowed to charge from curtailed energy using automated control*/ + bool batt_dispatch_auto_can_curtailcharge; + /*! Determines if the battery is allowed to charge from fuel cell using automated control*/ bool batt_dispatch_auto_can_fuelcellcharge; diff --git a/ssc/cmod_custom_generation.cpp b/ssc/cmod_custom_generation.cpp new file mode 100644 index 000000000..f4615e708 --- /dev/null +++ b/ssc/cmod_custom_generation.cpp @@ -0,0 +1,260 @@ +/* +BSD 3-Clause License + +Copyright (c) Alliance for Sustainable Energy, LLC. See also https://github.com/NREL/ssc/blob/develop/LICENSE +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "core.h" +#include "lib_windfile.h" +#include "lib_windwatts.h" + +// for adjustment factors +#include "common.h" + +static var_info _cm_vtab_custom_generation[] = { +// VARTYPE DATATYPE NAME LABEL UNITS META GROUP REQUIRED_IF CONSTRAINTS UI_HINTS + { SSC_INPUT, SSC_NUMBER, "spec_mode", "Spec mode: 0=constant CF,1=profile", "", "", "Plant", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "derate", "Derate", "%", "", "Plant", "*", "", "" }, + { SSC_INOUT, SSC_NUMBER, "system_capacity", "Nameplace Capcity", "kW", "", "Plant", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "user_capacity_factor", "Capacity Factor", "%", "", "Plant", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "heat_rate", "Heat Rate", "MMBTUs/MWhe", "", "Plant", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "conv_eff", "Conversion Efficiency", "%", "", "Plant", "*", "", "" }, + { SSC_INPUT, SSC_ARRAY, "energy_output_array", "Array of Energy Output Profile", "kW", "", "Plant", "spec_mode=1", "", "" }, + + // optional for lifetime analysis + { SSC_INPUT, SSC_NUMBER, "system_use_lifetime_output", "Custom generation profile lifetime simulation", "0/1", "", "Lifetime", "?=0", "INTEGER,MIN=0,MAX=1", "" }, + { SSC_INPUT, SSC_NUMBER, "analysis_period", "Lifetime analysis period", "years", "", "Lifetime", "system_use_lifetime_output=1", "", "" }, + { SSC_INPUT, SSC_ARRAY, "generic_degradation", "Annual AC degradation", "%/year", "", "Lifetime", "system_use_lifetime_output=1", "", "" }, + + +// OUTPUTS ---------------------------------------------------------------------------- +// VARTYPE DATATYPE NAME LABEL UNITS META GROUP REQUIRED_IF CONSTRAINTS UI_HINTS +// { SSC_OUTPUT, SSC_ARRAY, "hourly_energy", "Hourly Energy", "kWh", "", "Time Series", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "monthly_energy", "Monthly Energy Gross", "kWh", "", "Monthly", "*", "LENGTH=12", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_energy", "Annual Energy", "kWh", "", "Annual", "*", "", "" }, + + { SSC_OUTPUT, SSC_NUMBER, "annual_fuel_usage", "Annual Fuel Usage", "kWht", "", "Annual", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "water_usage", "Annual Water Usage", "", "", "Annual", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "system_heat_rate", "Heat Rate Conversion Factor", "MMBTUs/MWhe", "", "Annual", "*", "", "" }, + + { SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "Annual", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "Annual", "*", "", "" }, + + +var_info_invalid }; + +class cm_custom_generation : public compute_module +{ +private: +public: + + cm_custom_generation() + { + add_var_info( _cm_vtab_custom_generation ); + + // performance adjustment factors + add_var_info(vtab_adjustment_factors); + add_var_info(vtab_technology_outputs); + add_var_info(vtab_hybrid_tech_om_outputs); + } + + void exec( ) + { + int spec_mode = as_integer("spec_mode"); + bool system_use_lifetime_output = (as_integer("system_use_lifetime_output") == 1); + + // Warning workaround + static bool is32BitLifetime = (__ARCHBITS__ == 32 && system_use_lifetime_output); + if (is32BitLifetime) + throw exec_error( "custom_generation", "Lifetime simulation of custom generation profile systems is only available in the 64 bit version of SAM."); + + // Lifetime setup + ssc_number_t *enet = nullptr; + size_t nyears = 1; + if (system_use_lifetime_output) { + nyears = as_integer("analysis_period"); + } + + // Load parsing + std::vector load; + size_t nrec_load = 8760; + + if (is_assigned("load")) { + load = as_vector_double("load"); + nrec_load = load.size(); + } + size_t nlifetime = nrec_load * nyears; + size_t steps_per_hour = nrec_load / 8760; + double ts_hour = 1 / (double)(steps_per_hour); + + // Degradation and adjustments + std::vector sys_degradation; + sys_degradation.reserve(nyears); + double derate = (1 - (double)as_number("derate") / 100); + + adjustment_factors haf(this, "adjust"); + if (!haf.setup(nrec_load, nyears)) + throw exec_error("custom_generation", "failed to setup adjustment factors: " + haf.error()); + + if (system_use_lifetime_output) + { + // setup system degradation + size_t i, count_degrad = 0; + ssc_number_t *degrad = 0; + degrad = as_array("generic_degradation", &count_degrad); + + if (count_degrad == 1) + { + for (i = 0; i < nyears; i++) + sys_degradation.push_back((ssc_number_t)pow((1.0 - (double)degrad[0] / 100.0), i)); + } + else if (count_degrad > 0) + { + for (i = 0; i < nyears && i < (int)count_degrad; i++) sys_degradation.push_back((ssc_number_t)(1.0 - (double)degrad[i] / 100.0)); + } + } + else { + sys_degradation.push_back(1); // single year mode - degradation handled in financial models. + } + + size_t idx = 0; + double annual_output = 0; + + // Constant generation profile + if (spec_mode == 0) + { + double output = (double)as_number("system_capacity") + * (double)as_number("user_capacity_factor") / 100 + * derate; // kW + + annual_output = 8760 * output; // kWh + enet = allocate("gen", nlifetime); + for (size_t iyear = 0; iyear < nyears; iyear++) + { + for (size_t ihour = 0; ihour < 8760; ihour++) + { + for (size_t ihourstep = 0; ihourstep < steps_per_hour; ihourstep++) + { + enet[idx] = (ssc_number_t)(output*haf(ihour)) * sys_degradation[iyear]; // kW + idx++; + } + } + } + } + // Input generation profile + else + { + size_t nrec_gen = 0; + ssc_number_t *enet_in = as_array("energy_output_array", &nrec_gen); // kW + size_t steps_per_hour_gen = nrec_gen / 8760; + + if (!enet_in) { + throw exec_error("custom_generation", util::format("energy_output_array variable had no values.")); + } + + if (nrec_gen < nrec_load) { + throw exec_error("custom_generation", util::format("energy_output_array %d must be greater than or equal to load array %d", nrec_gen, nrec_load)); + } + else { + nlifetime = nrec_gen * nyears; + steps_per_hour = steps_per_hour_gen; + ts_hour = 1 / (double)(steps_per_hour); + } + + enet = allocate("gen", nlifetime); + for (size_t iyear = 0; iyear < nyears; iyear++){ + for (size_t ihour = 0; ihour < 8760; ihour++){ + for (size_t ihourstep = 0; ihourstep < steps_per_hour_gen; ihourstep++) + { + enet[idx] = enet_in[ihour* steps_per_hour_gen + ihourstep] * (ssc_number_t)(derate* haf(ihour))* sys_degradation[iyear]; + idx++; + } + } + } + } + double annual_ac_pre_avail = 0, annual_energy = 0; + idx = 0; + + // Run generic system + for (size_t iyear = 0; iyear < nyears; iyear++){ + for (size_t hour = 0; hour < 8760; hour++){ + for (size_t jj = 0; jj < steps_per_hour; jj++) + { + + // accumulate system generation before curtailment and availability + if (iyear == 0) { + annual_ac_pre_avail += enet[idx] * ts_hour; + } + + //apply availability and curtailment + enet[idx] *= haf(hour); + + if (iyear == 0) { + annual_energy += (ssc_number_t)(enet[idx] * ts_hour); + } + + idx++; + } + } + } + + ssc_number_t* p_annual_energy_dist_time = gen_heatmap(this, steps_per_hour); + + accumulate_monthly_for_year("gen", "monthly_energy", ts_hour, steps_per_hour); + annual_output = accumulate_annual_for_year("gen", "annual_energy", ts_hour, steps_per_hour); + + // if conversion efficiency is zero then set fuel usage to zero per email from Paul 5/17/12 + double fuel_usage = 0.0; + if (as_double("conv_eff") != 0.0) + fuel_usage = annual_output * 100.0 / as_double("conv_eff"); + assign("annual_fuel_usage", (ssc_number_t)fuel_usage); + + assign("water_usage", 0.0); + assign("system_heat_rate", (ssc_number_t)(as_number("heat_rate") * as_number("conv_eff") / 100.0)); + + // metric outputs moved to technology + double kWhperkW = 0.0; + double nameplate = as_double("system_capacity"); + + + if (nameplate <= 0) { + nameplate = annual_output / (8760 * (double)(as_number("user_capacity_factor") / 100) * derate); + } + assign("system_capacity", (var_data)((ssc_number_t)nameplate)); + + if (nameplate > 0) { + kWhperkW = annual_output / nameplate; + } + assign("capacity_factor", var_data((ssc_number_t)(kWhperkW / 87.6))); + assign("kwh_per_kw", var_data((ssc_number_t)kWhperkW)); + } // exec +}; + +DEFINE_MODULE_ENTRY( custom_generation, "Custom Generation Profile (formerly Generic System)", 1 ); + diff --git a/ssc/cmod_hybrid.cpp b/ssc/cmod_hybrid.cpp index 3054c84dc..104653ca9 100644 --- a/ssc/cmod_hybrid.cpp +++ b/ssc/cmod_hybrid.cpp @@ -91,7 +91,7 @@ class cm_hybrid : public compute_module for (size_t i = 0; i < vec_cms.size(); i++) { std::string computemodulename = vec_cms[i].str; - if ((computemodulename == "pvsamv1") || (computemodulename == "pvwattsv8") || (computemodulename == "windpower") || (computemodulename == "generic_system")) + if ((computemodulename == "pvsamv1") || (computemodulename == "pvwattsv8") || (computemodulename == "windpower") || (computemodulename == "custom_generation")) generators.push_back(computemodulename); else if (computemodulename == "battery") batteries.push_back(computemodulename); @@ -216,7 +216,7 @@ class cm_hybrid : public compute_module else { size_t count_degrad = 0; ssc_number_t* degrad = input.as_array("degradation", &count_degrad); - if (compute_module == "generic_system") + if (compute_module == "custom_generation") input.assign("generic_degradation", *input.lookup("degradation")); if (count_degrad == 1) { for (int i = 1; i <= analysisPeriod; i++) diff --git a/ssc/cmod_mhk_eqns.h b/ssc/cmod_mhk_eqns.h index d2756cbc5..18806ed60 100644 --- a/ssc/cmod_mhk_eqns.h +++ b/ssc/cmod_mhk_eqns.h @@ -48,6 +48,11 @@ static const char* me_array_cable_length_doc = " 'number_rows' - double [-]\\n" " 'row_spacing' - double [m]\\n" " 'cable_system_overbuild' - double [%]\\n" + " 'floating_array - int [-]\\n" + " 'export_cable_redundancy - int [-]\\n" + " 'water_depth - double [m]\\n" + " 'number_devices - int [-]\\n" + " 'distance_to_shore - double [m]\\n" "Output: key-value pairs added to var_table\\n" " 'inter_array_cable_length' - double [m]\\n"; @@ -63,10 +68,12 @@ static const char* tidal_turbine_calculate_powercurve_doc = " 'cut_in' - double [m/s]\\n" " 'cut_out' - double [m/s]\\n" " 'tidal_resource' - matrix [-]\\n" -" 'generator_rated_capacity' - matrix [-]\\n" "Output: key-value pairs added to var_table\\n" " 'tidal_turbine_powercurve_tidespeeds' - array [m/s]\\n" " 'tidal_turbine_powercurve_powerout' - array [kW]\\n" +" 'tidal_turine_powercurve_powerout_rated' - array [kW]\\n" +" 'tidal_turbine_rated_power' - double [kW]\\n" +" 'tidal_turbine_rated_power_rotor' - double [kW]\\n" " 'error - string [-]\\n"; diff --git a/ssc/cmod_pvwattsv8.cpp b/ssc/cmod_pvwattsv8.cpp index 5a357b1ac..667370150 100644 --- a/ssc/cmod_pvwattsv8.cpp +++ b/ssc/cmod_pvwattsv8.cpp @@ -143,7 +143,7 @@ static var_info _cm_vtab_pvwattsv8[] = { { SSC_INPUT, SSC_NUMBER, "rotlim", "Tracker rotation angle limit", "degrees", "", "System Design", "?=45.0", "", "" }, { SSC_INPUT, SSC_ARRAY, "soiling", "Soiling loss", "%", "", "System Design", "?", "", "" }, - { SSC_INPUT, SSC_NUMBER, "losses", "Other DC losses", "%", "total system losses", "System Design", "*", "MIN=-5,MAX=99", "" }, + { SSC_INPUT, SSC_NUMBER, "losses", "DC system losses", "%", "total system losses", "System Design", "*", "MIN=-5,MAX=99", "" }, { SSC_INPUT, SSC_NUMBER, "enable_wind_stow", "Enable tracker stow at high wind speeds", "0/1", "", "System Design", "?=0", "BOOLEAN", "" }, { SSC_INPUT, SSC_NUMBER, "stow_wspd", "Tracker stow wind speed threshold", "m/s", "", "System Design", "?=10", "", "" }, diff --git a/ssc/cmod_singleowner.cpp b/ssc/cmod_singleowner.cpp index 879ecbf93..121e436f2 100644 --- a/ssc/cmod_singleowner.cpp +++ b/ssc/cmod_singleowner.cpp @@ -125,8 +125,8 @@ static var_info _cm_vtab_singleowner[] = { { SSC_OUTPUT, SSC_ARRAY, "cf_revenue_dispatch9", "PPA revenue by year for TOD period 9", "$", "", "Cash Flow Revenue by Month and TOD Period", "ppa_multiplier_model=0", "LENGTH_EQUAL=cf_length", "" }, { SSC_OUTPUT, SSC_NUMBER, "firstyear_revenue_dispatch1", "PPA revenue in Year 1 TOD period 1", "$", "", "Cash Flow Revenue by Month and TOD Period", "ppa_multiplier_model=0", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "firstyear_revenue_dispatch2", "PPA revenue from in Year 1 TOD period 2", "$", "", "Cash Flow Revenue by Month and TOD Period", "ppa_multiplier_model=0", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "firstyear_revenue_dispatch3", "PPA revenue from in Year 1 TOD period 3", "$", "", "Cash Flow Revenue by Month and TOD Period", "ppa_multiplier_model=0", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "firstyear_revenue_dispatch2", "PPA revenue in Year 1 TOD period 2", "$", "", "Cash Flow Revenue by Month and TOD Period", "ppa_multiplier_model=0", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "firstyear_revenue_dispatch3", "PPA revenue in Year 1 TOD period 3", "$", "", "Cash Flow Revenue by Month and TOD Period", "ppa_multiplier_model=0", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "firstyear_revenue_dispatch4", "PPA revenue in Year 1 TOD period 4", "$", "", "Cash Flow Revenue by Month and TOD Period", "ppa_multiplier_model=0", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "firstyear_revenue_dispatch5", "PPA revenue in Year 1 TOD period 5", "$", "", "Cash Flow Revenue by Month and TOD Period", "ppa_multiplier_model=0", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "firstyear_revenue_dispatch6", "PPA revenue in Year 1 TOD period 6", "$", "", "Cash Flow Revenue by Month and TOD Period", "ppa_multiplier_model=0", "", "" }, diff --git a/ssc/common.cpp b/ssc/common.cpp index 00f6046c0..200e60799 100644 --- a/ssc/common.cpp +++ b/ssc/common.cpp @@ -109,10 +109,10 @@ var_info vtab_oandm[] = { { SSC_INPUT, SSC_NUMBER, "om_production_escal", "Production-based O&M escalation", "%/year", "", "System Costs", "?=0.0", "", "" }, { SSC_INPUT, SSC_ARRAY, "om_capacity", "Capacity-based O&M amount", "$/kWcap", "!battery,!fuelcell", "System Costs", "?=0.0", "", "" }, { SSC_INPUT, SSC_NUMBER, "om_capacity_escal", "Capacity-based O&M escalation", "%/year", "", "System Costs", "?=0.0", "", "" }, -{ SSC_INPUT, SSC_ARRAY, "om_fuel_cost", "Fuel cost", "$/MMBtu", "generic_system,fuelcell,tcslinearfresnel,tcstroughempirical,tcsgenericsolar,fresnelphysical", "System Costs", "?=0.0", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "om_fuel_cost_escal", "Fuel cost escalation", "%/year", "generic_system,fuelcell,tcslinearfresnel,tcstroughempirical,tcsgenericsolar,fresnelphysical", "System Costs", "?=0.0", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "annual_fuel_usage", "Fuel usage (yr 1)", "kWht", "generic_system,fuelcell,tcslinearfresnel,tcstroughempirical,tcsgenericsolar,fresnelphysical", "System Costs", "?=0", "MIN=0", "" }, -{ SSC_INPUT, SSC_ARRAY, "annual_fuel_usage_lifetime", "Fuel usage (lifetime)", "kWht", "generic_system,fuelcell,tcslinearfresnel,tcstroughempirical,tcsgenericsolar,fresnelphysical", "System Costs", "", "", "" }, +{ SSC_INPUT, SSC_ARRAY, "om_fuel_cost", "Fuel cost", "$/MMBtu", "custom_generation,fuelcell,tcslinearfresnel,tcstroughempirical,tcsgenericsolar,fresnelphysical", "System Costs", "?=0.0", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "om_fuel_cost_escal", "Fuel cost escalation", "%/year", "custom_generation,fuelcell,tcslinearfresnel,tcstroughempirical,tcsgenericsolar,fresnelphysical", "System Costs", "?=0.0", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "annual_fuel_usage", "Fuel usage (yr 1)", "kWht", "custom_generation,fuelcell,tcslinearfresnel,tcstroughempirical,tcsgenericsolar,fresnelphysical", "System Costs", "?=0", "MIN=0", "" }, +{ SSC_INPUT, SSC_ARRAY, "annual_fuel_usage_lifetime", "Fuel usage (lifetime)", "kWht", "custom_generation,fuelcell,tcslinearfresnel,tcstroughempirical,tcsgenericsolar,fresnelphysical", "System Costs", "", "", "" }, // replacements { SSC_INPUT,SSC_ARRAY , "om_batt_replacement_cost" , "Replacement cost 1" , "$/kWh" , "battery" , "System Costs" , "?=0.0" , "" , ""}, diff --git a/ssc/sscapi.cpp b/ssc/sscapi.cpp index 05904cb2e..e8ad9f96d 100644 --- a/ssc/sscapi.cpp +++ b/ssc/sscapi.cpp @@ -103,7 +103,7 @@ extern module_entry_info cm_entry_geothermal_costs, cm_entry_windpower, cm_entry_snowmodel, - cm_entry_generic_system, + cm_entry_custom_generation, cm_entry_wfcsvconv, cm_entry_tcstrough_empirical, cm_entry_tcstrough_physical, @@ -203,7 +203,7 @@ static module_entry_info *module_table[] = { &cm_entry_geothermal_costs, &cm_entry_windpower, &cm_entry_snowmodel, - &cm_entry_generic_system, + &cm_entry_custom_generation, &cm_entry_wfcsvconv, &cm_entry_tcstrough_empirical, &cm_entry_tcstrough_physical, diff --git a/test/input_cases/biomass_common.h b/test/input_cases/biomass_common.h index b1602a806..61845c5c8 100644 --- a/test/input_cases/biomass_common.h +++ b/test/input_cases/biomass_common.h @@ -42,7 +42,7 @@ namespace biomass_test { char file_name[256]; int n1 = sprintf(file_name, "%s/test/input_cases/swh_residential_data/fargo_nd_46.9_-96.8_mts1_60_tmy.csv", SSCDIR); char dispatch_factors[256]; - int n2 = sprintf(dispatch_factors, "%s/test/input_cases/generic_system_data/dispatch_factors_ts.csv", SSCDIR); + int n2 = sprintf(dispatch_factors, "%s/test/input_cases/custom_generation_data/dispatch_factors_ts.csv", SSCDIR); } diff --git a/test/input_cases/generic_common_data.h b/test/input_cases/custom_generation_common_data.h similarity index 96% rename from test/input_cases/generic_common_data.h rename to test/input_cases/custom_generation_common_data.h index 870addd0f..b25088cff 100644 --- a/test/input_cases/generic_common_data.h +++ b/test/input_cases/custom_generation_common_data.h @@ -31,13 +31,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _GENERIC_COMMON_DATA_H_ -#define _GENERIC_COMMON_DATA_H_ +#ifndef _CUSTOMGENERATION_COMMON_DATA_H_ +#define _CUSTOMGENERATION_COMMON_DATA_H_ #include #include "code_generator_utilities.h" -namespace generictest { +namespace customgenerationtest { char load_profile_path_60min[256]; char load_profile_path_30min[256]; char gen_path_60min[256]; @@ -50,20 +50,20 @@ namespace generictest { char temperature_path_30min[256]; - int n1 = sprintf(load_profile_path_60min, "%s/test/input_cases/generic_system_data/load_60min.csv",SSCDIR); - int n2 = sprintf(load_profile_path_30min, "%s/test/input_cases/generic_system_data/load_30min.csv",SSCDIR); - int n3 = sprintf(gen_path_60min, "%s/test/input_cases/generic_system_data/energy_output_array_60min.csv",SSCDIR); - int n4 = sprintf(gen_path_30min, "%s/test/input_cases/generic_system_data/energy_output_array_30min.csv",SSCDIR); - int n5 = sprintf(batt_dispatch_path_30min, "%s/test/input_cases/generic_system_data/batt_custom_dispatch_30min.csv",SSCDIR); - int n6 = sprintf(batt_dispatch_path_60min, "%s/test/input_cases/generic_system_data/batt_custom_dispatch_60min.csv",SSCDIR); - int n7 = sprintf(dispatch_factors_unused, "%s/test/input_cases/generic_system_data/dispatch_factors_ts.csv",SSCDIR); - int n8 = sprintf(sell_rate_unused, "%s/test/input_cases/generic_system_data/ur_ts_sell_rate.csv",SSCDIR); + int n1 = sprintf(load_profile_path_60min, "%s/test/input_cases/custom_generation_data/load_60min.csv",SSCDIR); + int n2 = sprintf(load_profile_path_30min, "%s/test/input_cases/custom_generation_data/load_30min.csv",SSCDIR); + int n3 = sprintf(gen_path_60min, "%s/test/input_cases/custom_generation_data/energy_output_array_60min.csv",SSCDIR); + int n4 = sprintf(gen_path_30min, "%s/test/input_cases/custom_generation_data/energy_output_array_30min.csv",SSCDIR); + int n5 = sprintf(batt_dispatch_path_30min, "%s/test/input_cases/custom_generation_data/batt_custom_dispatch_30min.csv",SSCDIR); + int n6 = sprintf(batt_dispatch_path_60min, "%s/test/input_cases/custom_generation_data/batt_custom_dispatch_60min.csv",SSCDIR); + int n7 = sprintf(dispatch_factors_unused, "%s/test/input_cases/custom_generation_data/dispatch_factors_ts.csv",SSCDIR); + int n8 = sprintf(sell_rate_unused, "%s/test/input_cases/custom_generation_data/ur_ts_sell_rate.csv",SSCDIR); int n9 = sprintf(temperature_path, "%s/test/input_cases/battery_data/batt_room_temperature_celsius_60min.csv",SSCDIR); int n10 = sprintf(temperature_path_30min, "%s/test/input_cases/battery_data/batt_room_temperature_celsius_30min.csv",SSCDIR); } -void generic_singleowner_battery_60min(ssc_data_t &data) +void custom_generation_singleowner_battery_60min(ssc_data_t &data) { ssc_data_set_number( data, "spec_mode", 1 ); ssc_data_set_number( data, "derate", 0 ); @@ -71,7 +71,7 @@ void generic_singleowner_battery_60min(ssc_data_t &data) ssc_data_set_number( data, "user_capacity_factor", 43.599998474121094 ); ssc_data_set_number( data, "heat_rate", 0 ); ssc_data_set_number( data, "conv_eff", 0 ); - set_array( data, "energy_output_array", generictest::gen_path_60min, 8760); + set_array( data, "energy_output_array", customgenerationtest::gen_path_60min, 8760); ssc_data_set_number( data, "system_use_lifetime_output", 0 ); ssc_data_set_number( data, "analysis_period", 25 ); ssc_number_t p_generic_degradation[1] ={ 0 }; @@ -152,7 +152,7 @@ void generic_singleowner_battery_60min(ssc_data_t &data) ssc_data_set_number( data, "batt_surface_area", 2694 ); ssc_data_set_number( data, "batt_Cp", 1004 ); ssc_data_set_number( data, "batt_h_to_ambient", 20 ); - set_array(data, "batt_room_temperature_celsius", generictest::temperature_path, 8760); + set_array(data, "batt_room_temperature_celsius", customgenerationtest::temperature_path, 8760); ssc_number_t p_cap_vs_temp[8] ={ -10, 60, 0, 80, 25, 100, 40, 100 }; ssc_data_set_matrix( data, "cap_vs_temp", p_cap_vs_temp, 4, 2 ); ssc_number_t p_dispatch_manual_charge[6] ={ 1, 1, 1, 0, 0, 0 }; @@ -169,7 +169,7 @@ void generic_singleowner_battery_60min(ssc_data_t &data) ssc_data_set_matrix( data, "dispatch_manual_sched", p_dispatch_manual_sched, 12, 24 ); ssc_number_t p_dispatch_manual_sched_weekend[288] ={ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1 }; ssc_data_set_matrix( data, "dispatch_manual_sched_weekend", p_dispatch_manual_sched_weekend, 12, 24 ); - set_array( data, "batt_custom_dispatch", generictest::batt_dispatch_path_60min, 8760); + set_array( data, "batt_custom_dispatch", customgenerationtest::batt_dispatch_path_60min, 8760); ssc_data_set_number( data, "batt_dispatch_choice", 3 ); ssc_number_t p_batt_pv_clipping_forecast[1] ={ 0 }; ssc_data_set_array( data, "batt_pv_clipping_forecast", p_batt_pv_clipping_forecast, 1 ); @@ -352,7 +352,7 @@ void generic_singleowner_battery_60min(ssc_data_t &data) ssc_data_set_number( data, "loan_moratorium", 0 ); ssc_data_set_number( data, "system_use_recapitalization", 0 ); ssc_data_set_number( data, "ppa_multiplier_model", 0 ); - set_array( data, "dispatch_factors_ts", generictest::dispatch_factors_unused, 8147); + set_array( data, "dispatch_factors_ts", customgenerationtest::dispatch_factors_unused, 8147); ssc_number_t p_dispatch_tod_factors[9] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }; ssc_data_set_array(data, "dispatch_tod_factors", p_dispatch_tod_factors, 9); ssc_data_set_number( data, "total_installed_cost", 1554456064 ); @@ -432,7 +432,7 @@ void generic_singleowner_battery_60min(ssc_data_t &data) ssc_data_set_number( data, "batt_replacement_cost_escal", 0 ); } -void generic_commerical_battery_60min(ssc_data_t &data) +void custom_generation_commerical_battery_60min(ssc_data_t &data) { ssc_data_set_number(data, "spec_mode", 1); ssc_data_set_number(data, "derate", 4); @@ -440,7 +440,7 @@ void generic_commerical_battery_60min(ssc_data_t &data) ssc_data_set_number(data, "user_capacity_factor", 43.599998474121094); ssc_data_set_number(data, "heat_rate", 10); ssc_data_set_number(data, "conv_eff", 34.118049621582031); - set_array(data, "energy_output_array", generictest::gen_path_60min, 8760); + set_array(data, "energy_output_array", customgenerationtest::gen_path_60min, 8760); ssc_data_set_number(data, "system_use_lifetime_output", 0); ssc_data_set_number(data, "analysis_period", 25); ssc_number_t p_generic_degradation[1] = { 0 }; @@ -450,7 +450,7 @@ void generic_commerical_battery_60min(ssc_data_t &data) ssc_data_set_number(data, "en_batt", 1); ssc_data_set_number(data, "en_standalone_batt", 0); - set_array(data, "load", generictest::load_profile_path_60min, 8760); + set_array(data, "load", customgenerationtest::load_profile_path_60min, 8760); ssc_data_set_number(data, "batt_replacement_option", 0); ssc_data_set_number(data, "batt_chem", 1); ssc_data_set_number(data, "batt_ac_or_dc", 1); @@ -522,7 +522,7 @@ void generic_commerical_battery_60min(ssc_data_t &data) ssc_data_set_number(data, "batt_surface_area", 2.05); ssc_data_set_number(data, "batt_Cp", 1000); ssc_data_set_number(data, "batt_h_to_ambient", 20); - set_array(data, "batt_room_temperature_celsius", generictest::temperature_path, 8760); + set_array(data, "batt_room_temperature_celsius", customgenerationtest::temperature_path, 8760); ssc_number_t p_cap_vs_temp[8] = { -15, 65, 0, 85, 25, 100, 40, 104 }; ssc_data_set_matrix(data, "cap_vs_temp", p_cap_vs_temp, 4, 2); ssc_number_t p_dispatch_manual_charge[6] = { 1, 1, 1, 0, 0, 0 }; @@ -546,7 +546,7 @@ void generic_commerical_battery_60min(ssc_data_t &data) ssc_number_t p_batt_target_power_monthly[1] = { 0 }; ssc_data_set_array(data, "batt_target_power_monthly", p_batt_target_power_monthly, 1); ssc_data_set_number(data, "batt_target_choice", 0); - set_array(data, "batt_custom_dispatch", generictest::batt_dispatch_path_60min, 8760); + set_array(data, "batt_custom_dispatch", customgenerationtest::batt_dispatch_path_60min, 8760); ssc_data_set_number(data, "batt_dispatch_choice", 3); ssc_data_set_number(data, "batt_dispatch_auto_can_gridcharge", 0); ssc_data_set_number(data, "batt_dispatch_auto_can_charge", 1); @@ -572,7 +572,7 @@ void generic_commerical_battery_60min(ssc_data_t &data) ssc_data_set_number(data, "ur_monthly_min_charge", 0); ssc_data_set_number(data, "ur_annual_min_charge", 0); ssc_data_set_number(data, "ur_en_ts_sell_rate", 0); - set_array(data, "ur_ts_sell_rate", generictest::sell_rate_unused, 8760); + set_array(data, "ur_ts_sell_rate", customgenerationtest::sell_rate_unused, 8760); ssc_data_set_number(data, "ur_dc_enable", 1); ssc_number_t p_ur_dc_sched_weekday[288] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; ssc_data_set_matrix(data, "ur_dc_sched_weekday", p_ur_dc_sched_weekday, 12, 24); diff --git a/test/input_cases/generic_system_data/batt_custom_dispatch.csv b/test/input_cases/custom_generation_data/batt_custom_dispatch.csv similarity index 100% rename from test/input_cases/generic_system_data/batt_custom_dispatch.csv rename to test/input_cases/custom_generation_data/batt_custom_dispatch.csv diff --git a/test/input_cases/generic_system_data/batt_custom_dispatch_30min.csv b/test/input_cases/custom_generation_data/batt_custom_dispatch_30min.csv similarity index 100% rename from test/input_cases/generic_system_data/batt_custom_dispatch_30min.csv rename to test/input_cases/custom_generation_data/batt_custom_dispatch_30min.csv diff --git a/test/input_cases/generic_system_data/batt_custom_dispatch_60min.csv b/test/input_cases/custom_generation_data/batt_custom_dispatch_60min.csv similarity index 100% rename from test/input_cases/generic_system_data/batt_custom_dispatch_60min.csv rename to test/input_cases/custom_generation_data/batt_custom_dispatch_60min.csv diff --git a/test/input_cases/generic_system_data/cp_variable_amount.csv b/test/input_cases/custom_generation_data/cp_variable_amount.csv similarity index 100% rename from test/input_cases/generic_system_data/cp_variable_amount.csv rename to test/input_cases/custom_generation_data/cp_variable_amount.csv diff --git a/test/input_cases/generic_system_data/cp_variable_percent_nameplate.csv b/test/input_cases/custom_generation_data/cp_variable_percent_nameplate.csv similarity index 100% rename from test/input_cases/generic_system_data/cp_variable_percent_nameplate.csv rename to test/input_cases/custom_generation_data/cp_variable_percent_nameplate.csv diff --git a/test/input_cases/generic_system_data/curtailment_30min_0percent.csv b/test/input_cases/custom_generation_data/curtailment_30min_0percent.csv similarity index 100% rename from test/input_cases/generic_system_data/curtailment_30min_0percent.csv rename to test/input_cases/custom_generation_data/curtailment_30min_0percent.csv diff --git a/test/input_cases/generic_system_data/curtailment_30min_50percent.csv b/test/input_cases/custom_generation_data/curtailment_30min_50percent.csv similarity index 100% rename from test/input_cases/generic_system_data/curtailment_30min_50percent.csv rename to test/input_cases/custom_generation_data/curtailment_30min_50percent.csv diff --git a/test/input_cases/generic_system_data/dispatch_factors_ts.csv b/test/input_cases/custom_generation_data/dispatch_factors_ts.csv similarity index 100% rename from test/input_cases/generic_system_data/dispatch_factors_ts.csv rename to test/input_cases/custom_generation_data/dispatch_factors_ts.csv diff --git a/test/input_cases/generic_system_data/energy_output_array.csv b/test/input_cases/custom_generation_data/energy_output_array.csv similarity index 100% rename from test/input_cases/generic_system_data/energy_output_array.csv rename to test/input_cases/custom_generation_data/energy_output_array.csv diff --git a/test/input_cases/generic_system_data/energy_output_array_30min-2yr.csv b/test/input_cases/custom_generation_data/energy_output_array_30min-2yr.csv similarity index 100% rename from test/input_cases/generic_system_data/energy_output_array_30min-2yr.csv rename to test/input_cases/custom_generation_data/energy_output_array_30min-2yr.csv diff --git a/test/input_cases/generic_system_data/energy_output_array_30min.csv b/test/input_cases/custom_generation_data/energy_output_array_30min.csv similarity index 100% rename from test/input_cases/generic_system_data/energy_output_array_30min.csv rename to test/input_cases/custom_generation_data/energy_output_array_30min.csv diff --git a/test/input_cases/generic_system_data/energy_output_array_30min.xlsx b/test/input_cases/custom_generation_data/energy_output_array_30min.xlsx similarity index 100% rename from test/input_cases/generic_system_data/energy_output_array_30min.xlsx rename to test/input_cases/custom_generation_data/energy_output_array_30min.xlsx diff --git a/test/input_cases/generic_system_data/energy_output_array_60min.csv b/test/input_cases/custom_generation_data/energy_output_array_60min.csv similarity index 100% rename from test/input_cases/generic_system_data/energy_output_array_60min.csv rename to test/input_cases/custom_generation_data/energy_output_array_60min.csv diff --git a/test/input_cases/generic_system_data/generic.sam b/test/input_cases/custom_generation_data/generic.sam similarity index 100% rename from test/input_cases/generic_system_data/generic.sam rename to test/input_cases/custom_generation_data/generic.sam diff --git a/test/input_cases/generic_system_data/load.csv b/test/input_cases/custom_generation_data/load.csv similarity index 100% rename from test/input_cases/generic_system_data/load.csv rename to test/input_cases/custom_generation_data/load.csv diff --git a/test/input_cases/generic_system_data/load_30min.csv b/test/input_cases/custom_generation_data/load_30min.csv similarity index 100% rename from test/input_cases/generic_system_data/load_30min.csv rename to test/input_cases/custom_generation_data/load_30min.csv diff --git a/test/input_cases/generic_system_data/load_60min.csv b/test/input_cases/custom_generation_data/load_60min.csv similarity index 100% rename from test/input_cases/generic_system_data/load_60min.csv rename to test/input_cases/custom_generation_data/load_60min.csv diff --git a/test/input_cases/generic_system_data/ur_ts_sell_rate.csv b/test/input_cases/custom_generation_data/ur_ts_sell_rate.csv similarity index 100% rename from test/input_cases/generic_system_data/ur_ts_sell_rate.csv rename to test/input_cases/custom_generation_data/ur_ts_sell_rate.csv diff --git a/test/input_cases/grid_common_data.h b/test/input_cases/grid_common_data.h index 5dbe072a1..4c394c6e0 100644 --- a/test/input_cases/grid_common_data.h +++ b/test/input_cases/grid_common_data.h @@ -46,11 +46,11 @@ namespace { char gen_path_grid_30_2yr[256]; char grid_curtailment_default_MW[256]; - int nfc1 = sprintf(gen_path_grid, "%s/test/input_cases/generic_system_data/energy_output_array.csv", SSCDIR); + int nfc1 = sprintf(gen_path_grid, "%s/test/input_cases/custom_generation_data/energy_output_array.csv", SSCDIR); int nfc2 = sprintf(load_profile_path_grid, "%s/test/input_cases/general_data/commercial_load.csv", SSCDIR); - int nfc3 = sprintf(gen_path_grid_30, "%s/test/input_cases/generic_system_data/energy_output_array_30min.csv", SSCDIR); - int nfc4 = sprintf(load_profile_path_grid_30, "%s/test/input_cases/generic_system_data/load_30min.csv", SSCDIR); - int nfc5 = sprintf(gen_path_grid_30_2yr, "%s/test/input_cases/generic_system_data/energy_output_array_30min-2yr.csv", SSCDIR); + int nfc3 = sprintf(gen_path_grid_30, "%s/test/input_cases/custom_generation_data/energy_output_array_30min.csv", SSCDIR); + int nfc4 = sprintf(load_profile_path_grid_30, "%s/test/input_cases/custom_generation_data/load_30min.csv", SSCDIR); + int nfc5 = sprintf(gen_path_grid_30_2yr, "%s/test/input_cases/custom_generation_data/energy_output_array_30min-2yr.csv", SSCDIR); int nfc6 = sprintf(grid_curtailment_default_MW, "%s/test/input_cases/general_data/grid_curtailment_default_MW.csv", SSCDIR); } diff --git a/test/input_json/hybrids/Generic PVWatts Wind FuelCell Battery Hybrid_Single Owner.json b/test/input_json/hybrids/CustomGeneration PVWatts Wind FuelCell Battery Hybrid_Single Owner.json similarity index 99% rename from test/input_json/hybrids/Generic PVWatts Wind FuelCell Battery Hybrid_Single Owner.json rename to test/input_json/hybrids/CustomGeneration PVWatts Wind FuelCell Battery Hybrid_Single Owner.json index 0303912d7..2ba39814e 100644 --- a/test/input_json/hybrids/Generic PVWatts Wind FuelCell Battery Hybrid_Single Owner.json +++ b/test/input_json/hybrids/CustomGeneration PVWatts Wind FuelCell Battery Hybrid_Single Owner.json @@ -1,6 +1,6 @@ { "input" : { - "compute_modules" : ["generic_system","pvwattsv8","windpower","fuelcell","battery","grid","utilityrate5","singleowner"], + "compute_modules" : ["custom_generation","pvwattsv8","windpower","fuelcell","battery","grid","utilityrate5","singleowner"], "windpower" : { "om_fixed_escal" : 0, "turb_specific_loss" : 0.81000000000000005, @@ -53,7 +53,7 @@ "om_production" : [ 0 ], "number table entries" : 50 }, - "generic_system" : { + "custom_generation" : { "adjust_en_timeindex" : 0, "om_capacity" : [ 40 ], "om_fuel_cost" : [ 10 ], diff --git a/test/shared_test/lib_battery_dispatch_automatic_btm_test.cpp b/test/shared_test/lib_battery_dispatch_automatic_btm_test.cpp index fd290c3b0..07fdde8e2 100644 --- a/test/shared_test/lib_battery_dispatch_automatic_btm_test.cpp +++ b/test/shared_test/lib_battery_dispatch_automatic_btm_test.cpp @@ -44,7 +44,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridCharging) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -92,7 +92,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMPVCharging) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -136,7 +136,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMPVChargeAndDischarge) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm for (size_t h = 0; h < 24; h++) { @@ -187,7 +187,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMPVChargeAndDischargeSubh max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -255,7 +255,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMDCClipCharge) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, - false, true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, + false, true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -309,7 +309,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestBasicForecast) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -365,7 +365,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestSummerPeak) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); load_prediction = { 1.44289, 1.27067, 1.1681, 1.09342, 1.12921, 1.39345, 1.57299, 1.63055, 1.85622, 2.44991, 2.61812, 2.90909, 3.29601, 3.64366, 3.88232, 3.99237, 4.09673, 4.11102, 4.09175, 4.13445, 3.91011, 3.27815, 2.67845, 2.11802, 1.78025, 1.57142, 1.42908, 1.32466, @@ -405,7 +405,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestSummerPeakNetMeteringCredits) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); load_prediction = { 1.44289, 1.27067, 1.1681, 1.09342, 1.12921, 1.39345, 1.57299, 1.63055, 1.85622, 2.44991, 2.61812, 2.90909, 3.29601, 3.64366, 3.88232, 3.99237, 4.09673, 4.11102, 4.09175, 4.13445, 3.91011, 3.27815, 2.67845, 2.11802, 1.78025, 1.57142, 1.42908, 1.32466, @@ -445,7 +445,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestSummerPeakGridCharging) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, canGridCharge, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, + true, canGridCharge, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); load_prediction = { 1.44289, 1.27067, 1.1681, 1.09342, 1.12921, 1.39345, 1.57299, 1.63055, 1.85622, 2.44991, 2.61812, 2.90909, 3.29601, 3.64366, 3.88232, 3.99237, 4.09673, 4.11102, 4.09175, 4.13445, 3.91011, 3.27815, 2.67845, 2.11802, 1.78025, 1.57142, 1.42908, 1.32466, @@ -486,7 +486,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestSummerPeakGridChargingSubhourly) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, canGridCharge, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, + true, canGridCharge, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); load_prediction = { 1.44289, 1.27067, 1.1681, 1.09342, 1.12921, 1.39345, 1.57299, 1.63055, 1.85622, 2.44991, 2.61812, 2.90909, 3.29601, 3.64366, 3.88232, 3.99237, 4.09673, 4.11102, 4.09175, 4.13445, 3.91011, 3.27815, 2.67845, 2.11802, 1.78025, 1.57142, 1.42908, 1.32466, @@ -540,7 +540,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestCommercialPeakForecasting) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, true, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, + true, true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); load_prediction = { 49.9898, 42.4037, 42.1935, 43.3778, 39.4545, 59.3723, 84.6907, 180.423, 180.836, 186.225, 197.275, 205.302, 231.362, @@ -584,7 +584,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMPVChargeAndDischargeSmal max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -637,7 +637,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMPVChargeAndDischargeSmal max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -690,7 +690,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMCustomDispatch) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup custom dispatch signal - signal and expected AC power are the same without losses @@ -726,7 +726,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMCustomDispatchDischargeT max_current, max_current, max_power, max_power, max_power, max_power , 0, dispatch_t::BTM_MODES::CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup custom dispatch signal - signal and expected AC power are the same without losses. Load is 40 kW, so this will exceed load by 8 kW @@ -760,7 +760,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMCustomDispatchWithLosses max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup custom dispatch signal - need to account for losses when discharging @@ -796,7 +796,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMCustomWExcessPV) { max_current, max_current, max_power, max_power, max_power, max_power, 0, dispatch_t::BTM_MODES::CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup custom dispatch signal - signal is greater than expected power since constraints should prevent dispatch hours 13 through 17 @@ -838,7 +838,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutagePeakShavingDai max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -908,7 +908,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutageWithAvailabili max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -979,7 +979,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutageWithAvailabili max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -1051,7 +1051,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutageWithInverterLo max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -1124,7 +1124,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutagePeakShavingEmp max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -1242,7 +1242,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutageRetailRAteEmpt max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -1362,7 +1362,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutageCustomEmptyAnd max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, 0, dispatch_t::BTM_MODES::CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -1484,7 +1484,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutagePeakShavingDC) max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -1554,7 +1554,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutageFuelCellCharge max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, 0, dispatch_t::BTM_MODES::PEAK_SHAVING, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, - true, false, true, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, true, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm @@ -1635,7 +1635,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMSetupRateForecastMultiYe max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, nyears, 24, 1, true, - true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, + true, false, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); // Setup pv and load signal for peak shaving algorithm diff --git a/test/shared_test/lib_battery_dispatch_automatic_fom_test.cpp b/test/shared_test/lib_battery_dispatch_automatic_fom_test.cpp index 13fd5f1b7..082a97cea 100644 --- a/test/shared_test/lib_battery_dispatch_automatic_fom_test.cpp +++ b/test/shared_test/lib_battery_dispatch_automatic_fom_test.cpp @@ -38,7 +38,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOMInput) { double dtHourFOM = 1.0; CreateBattery(dtHourFOM); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHourFOM, 15, 95, 1, 999, 999, max_power, max_power, - max_power, max_power, 1, dispatch_t::FOM_CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 24, 1, true, true, false, true, 0, + max_power, max_power, 1, dispatch_t::FOM_CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 24, 1, true, true, false, true, false, 0, replacementCost, 0, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); std::vector P_batt = {-336.062, 336.062}; @@ -74,7 +74,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOMInputWithLosses) { double dtHourFOM = 1.0; CreateBatteryWithLosses(dtHourFOM); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHourFOM, 15, 95, 1, 999, 999, max_power, max_power, - max_power, max_power, 1, dispatch_t::FOM_CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 24, 1, true, true, false, true, 0, + max_power, max_power, 1, dispatch_t::FOM_CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 24, 1, true, true, false, true, false, 0, replacementCost, 0, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); std::vector P_batt = { -336.062, 336.062 }; @@ -112,7 +112,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOMInputSubhourly) { double dtHourFOM = 0.5; CreateBattery(dtHourFOM); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHourFOM, 15, 95, 1, 999, 999, max_power, max_power, - max_power, max_power, 1, dispatch_t::FOM_CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 24, 1, true, true, false, true, 0, + max_power, max_power, 1, dispatch_t::FOM_CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 24, 1, true, true, false, true, false, 0, replacementCost, 0, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); std::vector P_batt = {-336.062, 336.062}; @@ -149,7 +149,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_DCCustomCharge) { CreateBattery(dtHour); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, - true, true, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, + true, true, false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); std::vector P_batt(6, -25000); @@ -201,7 +201,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_DCCustomChargeSubhourly) { CreateBattery(dtHour); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, - true, true, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, + true, true, false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); std::vector P_batt(12, -25000); @@ -256,7 +256,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_DCAuto) { CreateBattery(dtHour); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_AUTOMATED_ECONOMIC, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, true, false, - false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); + false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); // battery setup dispatchAuto->update_pv_data(pv); @@ -298,7 +298,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_DCAutoWithLosses) { CreateBatteryWithLosses(dtHour); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_AUTOMATED_ECONOMIC, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, true, false, - false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); + false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); // battery setup dispatchAuto->update_pv_data(pv); @@ -339,7 +339,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_DCAutoSubhourly) { CreateBattery(dtHour); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_AUTOMATED_ECONOMIC, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, true, false, - false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); + false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); // battery setup dispatchAuto->update_pv_data(pv); @@ -375,7 +375,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_ACCustomCharge) { CreateBattery(dtHour); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, - true, true, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, + true, true, false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); std::vector P_batt(6, -25000); @@ -426,7 +426,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_ACCustomChargeSubhourly) { CreateBattery(dtHour); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_CUSTOM_DISPATCH, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, - true, true, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, + true, true, false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); std::vector P_batt(12, -25000); @@ -480,7 +480,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_ACAuto) { CreateBattery(dtHour); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_AUTOMATED_ECONOMIC, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, true, false, - false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); + false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); // battery setup dispatchAuto->update_pv_data(pv); // PV Resource is available for the 1st 10 hrs @@ -523,7 +523,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_ACAutoWithLosses) { CreateBatteryWithLosses(dtHour); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_AUTOMATED_ECONOMIC, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, true, false, - false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); + false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); // battery setup dispatchAuto->update_pv_data(pv); // PV Resource is available for the 1st 10 hrs @@ -566,7 +566,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_ACAutoSubhourly) { CreateBattery(dtHour); dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_AUTOMATED_ECONOMIC, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, true, false, - false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); + false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); // battery setup dispatchAuto->update_pv_data(pv); @@ -624,7 +624,7 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_DCAuto_ClipChargeFlatRate) { dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, max_power, max_power, max_power, 1, dispatch_t::FOM_AUTOMATED_ECONOMIC, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, true, false, - false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); + false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); // battery setup dispatchAuto->update_pv_data(pv); @@ -664,3 +664,91 @@ TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_DCAuto_ClipChargeFlatRate) { } } + +TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_DCAutoWithInterconnectionLimit) { + double dtHour = 1; + CreateBattery(dtHour); + interconnection_limit = 50000; + dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, + max_power, max_power, max_power, 1, dispatch_t::FOM_AUTOMATED_ECONOMIC, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, true, false, + false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); + + // battery setup + dispatchAuto->update_pv_data(pv); + dispatchAuto->update_cliploss_data(clip); + batteryPower = dispatchAuto->getBatteryPower(); + batteryPower->connectionMode = ChargeController::DC_CONNECTED; + batteryPower->voltageSystem = 600; + batteryPower->setSharedInverter(m_sharedInverter); + + std::vector targetkW = { 28396.7, -19294.27, 5515.3, -35296.51, -35570.30, -34754.11, // 0 - 5 + -12969.9, -1797.387, -250.22, -35.16, 0., 0., // 6 - 11 + 0., 0., 0., 0., 0., 50000, // 12 - 17 + 50000, 50000, 50000, 50000, 50000, 0. }; // 18 - 23 + std::vector dispatchedkW = { 27100.68, -19294.27, 5515.3, -28862.56, -29079.57, -29217.40, // 0 - 5 + -12969.9, -1797.387, -250.22, -35.16, 0., 0., // 6 - 11 + 0., 0., 0., 0., 0., 28116.27, // 18 + 27947.03, 27664.92, 27100.53, 25407.36, 9381.21, 0. }; // 24 + std::vector SOC = { 33.33, 44.65, 41.35, 58.02, 74.68, 91.35, // 0 - 5 + 98.8, 99.8, 99.97, 99.99, 99.99, 99.99, // 6 - 11 + 99.99, 99.99, 99.99, 99.99, 99.99, 83.33, // 12 - 17 + 66.66, 50.0, 33.33, 16.66, 10.0, 10.0 }; // 18 - 23 + for (size_t h = 0; h < 24; h++) { + batteryPower->powerGeneratedBySystem = pv[h]; + batteryPower->powerSystem = pv[h]; + batteryPower->powerSystemClipped = clip[h]; + + dispatchAuto->update_dispatch(0, h, 0, h); + EXPECT_NEAR(batteryPower->powerBatteryTarget, targetkW[h], 0.1) << "error in expected target at hour " << h; + + dispatchAuto->dispatch(0, h, 0); + EXPECT_NEAR(batteryPower->powerBatteryDC, dispatchedkW[h], 0.1) << "error in dispatched power at hour " << h; + EXPECT_NEAR(dispatchAuto->battery_soc(), SOC[h], 0.1) << "error in SOC at hour " << h; + + } +} + +TEST_F(AutoFOM_lib_battery_dispatch, DispatchFOM_ACAutoWithInterconnectionLimit) { + double dtHour = 1; + CreateBattery(dtHour); + interconnection_limit = 50000; + dispatchAuto = new dispatch_automatic_front_of_meter_t(batteryModel, dtHour, 10, 100, 1, 49960, 49960, max_power, + max_power, max_power, max_power, 1, dispatch_t::FOM_AUTOMATED_ECONOMIC, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, dispatch_t::FRONT, 1, 18, 1, true, false, false, + false, false, 77000, replacementCost, 1, cyclingCost, omCost, ppaRate, ur, 98, 98, 98, interconnection_limit); + + // battery setup + dispatchAuto->update_pv_data(pv); // PV Resource is available for the 1st 10 hrs + dispatchAuto->update_cliploss_data(clip); // Clip charging is available for the 1st 5 hrs + batteryPower = dispatchAuto->getBatteryPower(); + batteryPower->connectionMode = ChargeController::AC_CONNECTED; + batteryPower->voltageSystem = 600; + batteryPower->setSharedInverter(m_sharedInverter); + + // A few hours at the beginnging flip from discharge to charge since the PV will be curtailed + // Note on hour 3: the price here is both the 2nd highest price and the 2nd lowest price. A non-zero replacement cost could prevent cycling here. + std::vector targetkW = { 28396.7, -19294.27, 5515.3, -35296.51, -35570.30, -34754.11, // 0 - 5 + -84205.39, -78854.6, -67702.19, -31516.09, 0., 0., // 6 - 11 + 0., 0., 0., 0., 0., 50000, // 12 - 17 + 50000, 50000, 50000, 50000, 50000, 0. }; // 18 - 23 + std::vector dispatchedkW = { 27100.68, -19294.27, 5515.3, -28862.56, -29079.57, -29217.40, // 0 - 5 + -15235.74, 0.0, 0.0, 0.0, 0., 0.,// 6 - 11 + 0., 0., 0., 0., 0., 28116.30, // 12 - 17 + 27947.03, 27664.92, 27100.69, 25408.03, 9385.81, 0. }; // 18 - 23 + std::vector SOC = { 33.3, 44.65, 41.35, 58.02, 74.68, 91.35, // 0 - 5 + 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, // 6 - 11 + 100.0, 100.0, 100.0, 100.0, 100.0, 83.33, // 12 - 17 + 66.66, 50.0, 33.33, 16.66, 10.0, 10.0 }; // 18 - 23 + for (size_t h = 0; h < 24; h++) { + batteryPower->powerGeneratedBySystem = pv[h]; + batteryPower->powerSystem = pv[h]; + batteryPower->powerSystemClipped = clip[h]; + + dispatchAuto->update_dispatch(0, h, 0, h); + EXPECT_NEAR(batteryPower->powerBatteryTarget, targetkW[h], 0.1) << "error in expected target at hour " << h; + + dispatchAuto->dispatch(0, h, 0); + + EXPECT_NEAR(batteryPower->powerBatteryDC, dispatchedkW[h], 0.1) << "error in dispatched power at hour " << h; + EXPECT_NEAR(dispatchAuto->battery_soc(), SOC[h], 0.1) << "error in SOC at hour " << h; + } +} diff --git a/test/shared_test/lib_battery_dispatch_manual_test.cpp b/test/shared_test/lib_battery_dispatch_manual_test.cpp index 715fdb89a..ce8b1f33a 100644 --- a/test/shared_test/lib_battery_dispatch_manual_test.cpp +++ b/test/shared_test/lib_battery_dispatch_manual_test.cpp @@ -43,7 +43,7 @@ TEST_F(ManualTest_lib_battery_dispatch, PowerLimitsDispatchManualAC) { powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::AC_CONNECTED; @@ -74,7 +74,7 @@ TEST_F(ManualTest_lib_battery_dispatch, PowerLimitsDispatchManualDC) { powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::DC_CONNECTED; @@ -103,7 +103,7 @@ TEST_F(ManualTest_lib_battery_dispatch, CurrentLimitsDispatchManualAC) { powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::AC_CONNECTED; @@ -133,7 +133,7 @@ TEST_F(ManualTest_lib_battery_dispatch, CurrentLimitsDispatchManualDC) { powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::DC_CONNECTED; @@ -166,7 +166,7 @@ TEST_F(ManualTest_lib_battery_dispatch, BothLimitsDispatchManualAC) { testDischargeMaxPower, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::AC_CONNECTED; @@ -217,7 +217,7 @@ TEST_F(ManualTest_lib_battery_dispatch, BothLimitsDispatchManualDC) { powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::DC_CONNECTED; @@ -267,7 +267,7 @@ TEST_F(ManualTest_lib_battery_dispatch, DispatchChangeFrequency) { powerDischargeMax, testMinTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::AC_CONNECTED; @@ -314,7 +314,7 @@ TEST_F(ManualTest_lib_battery_dispatch, SOCLimitsOnDispatch) { powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::AC_CONNECTED; @@ -373,7 +373,7 @@ TEST_F(ManualTest_lib_battery_dispatch, ManualGridChargingOffTest) powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::DC_CONNECTED; @@ -399,7 +399,7 @@ TEST_F(ManualTest_lib_battery_dispatch, ManualGridChargingOnTest) powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, testCanGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - testPercentGridCharge, canClipCharge, interconnection_limit); + testPercentGridCharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::AC_CONNECTED; @@ -425,7 +425,7 @@ TEST_F(ManualTest_lib_battery_dispatch, ManualGridChargingOnDCConnectedTest) powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, testCanGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - testPercentGridCharge, canClipCharge, interconnection_limit); + testPercentGridCharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::DC_CONNECTED; @@ -452,7 +452,7 @@ TEST_F(ManualTest_lib_battery_dispatch, NoGridChargingWhilePVIsOnTest) powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, testCanGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - testPercentGridCharge, canClipCharge, interconnection_limit); + testPercentGridCharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::AC_CONNECTED; @@ -471,7 +471,7 @@ TEST_F(ManualTest_lib_battery_dispatch, EfficiencyLimitsDispatchManualDC) powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::DC_CONNECTED; @@ -502,7 +502,7 @@ TEST_F(ManualTest_lib_battery_dispatch, InverterEfficiencyCutoffDC) currentDischargeMax, powerChargeMax, powerDischargeMax, powerChargeMax, powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, - canDischarge, testCanGridcharge, canDischargeToGrid, canGridcharge, testPercentGridCharge, testPercentGridCharge, canClipCharge, interconnection_limit); + canDischarge, testCanGridcharge, canDischargeToGrid, canGridcharge, testPercentGridCharge, testPercentGridCharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::DC_CONNECTED; @@ -545,7 +545,7 @@ TEST_F(ManualTest_lib_battery_dispatch_losses, TestLossesWithDispatch) powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::DC_CONNECTED; @@ -574,7 +574,7 @@ TEST_F(ManualTest_lib_battery_dispatch, TestDischargeToGrid) powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canGridcharge, testCanDischargeToGrid, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::AC_CONNECTED; @@ -600,7 +600,7 @@ TEST_F(ManualTest_lib_battery_dispatch, TestClipCharging) powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, testDoNothingExceptClipCharge, testDoNothingExceptClipCharge, testDoNothingExceptClipCharge, testDoNothingExceptClipCharge, testDoNothingExceptClipCharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::DC_CONNECTED; @@ -622,7 +622,7 @@ TEST_F(ManualTest_lib_battery_dispatch, OutageWithManualDispatch) { powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canGridcharge, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit); + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); batteryPower = dispatchManual->getBatteryPower(); batteryPower->connectionMode = ChargeController::AC_CONNECTED; @@ -695,7 +695,7 @@ TEST_F(ManualTest_lib_battery_dispatch, PVPriorityLoadFirst) powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit, + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, SOC_min_outage, priorityChargeBattery); batteryPower = dispatchManual->getBatteryPower(); @@ -722,7 +722,7 @@ TEST_F(ManualTest_lib_battery_dispatch, PVPriorityBatteryFirst) powerDischargeMax, minimumModeTime, dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, - percentGridcharge, canClipCharge, interconnection_limit, + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, SOC_min_outage, priorityChargeBattery); batteryPower = dispatchManual->getBatteryPower(); @@ -737,3 +737,52 @@ TEST_F(ManualTest_lib_battery_dispatch, PVPriorityBatteryFirst) EXPECT_NEAR(batteryPower->powerGridToLoad, 50.0, 1.0); } + +TEST_F(ManualTest_lib_battery_dispatch, ManualInterconnectionLimitTest) +{ + interconnection_limit = 500; + canCurtailCharge = true; + // canGridCharge is false by default + dispatchManual = new dispatch_manual_t(batteryModel, dtHour, SOC_min, SOC_max, currentChoice, currentChargeMax, + currentDischargeMax, powerChargeMax, powerDischargeMax, powerChargeMax, + powerDischargeMax, minimumModeTime, + dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, + canDischarge, canGridcharge, canDischargeToGrid, canGridcharge, percentDischarge, + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); + + batteryPower = dispatchManual->getBatteryPower(); + batteryPower->connectionMode = ChargeController::DC_CONNECTED; + batteryPower->setSharedInverter(m_sharedInverter); + + // Should charge since PV is being curtailed + batteryPower->powerSystem = 600; batteryPower->voltageSystem = 600; batteryPower->powerGridToBattery = 0; + dispatchManual->dispatch(year, hour_of_year, step_of_hour); + EXPECT_NEAR(batteryPower->powerBatteryDC, -47.9, 0.1); + EXPECT_NEAR(batteryPower->powerSystemToBatteryDC, 48.4, 0.1); +} + +TEST_F(ManualTest_lib_battery_dispatch, TestDischargeToGridInterconnectionLimit) +{ + interconnection_limit = 10; + std::vector testCanDischargeToGrid; + for (int p = 0; p < 6; p++) { + testCanDischargeToGrid.push_back(1); + } + dispatchManual = new dispatch_manual_t(batteryModel, dtHour, SOC_min, SOC_max, currentChoice, currentChargeMax, + currentDischargeMax, powerChargeMax, powerDischargeMax, powerChargeMax, + powerDischargeMax, minimumModeTime, + dispatchChoice, meterPosition, scheduleWeekday, scheduleWeekend, canCharge, + canDischarge, canGridcharge, canGridcharge, testCanDischargeToGrid, percentDischarge, + percentGridcharge, canClipCharge, canCurtailCharge, interconnection_limit); + + batteryPower = dispatchManual->getBatteryPower(); + batteryPower->connectionMode = ChargeController::AC_CONNECTED; + batteryPower->inverterEfficiencyCutoff = 0; + + // Test discharge to grid - should only charge at interconnection limit + batteryPower->powerSystem = 0; batteryPower->powerLoad = 30; batteryPower->voltageSystem = 600; batteryPower->powerBatteryDC = 1000; + dispatchManual->dispatch(year, hour_of_year, step_of_hour); + EXPECT_NEAR(batteryPower->powerBatteryDC, 41.66, 1.0); + EXPECT_NEAR(batteryPower->powerBatteryToLoad, batteryPower->powerLoad, 2.0); + EXPECT_NEAR(batteryPower->powerBatteryToGrid, 10, 0.1); +} diff --git a/test/shared_test/lib_battery_dispatch_pvsmoothing_fom_test.cpp b/test/shared_test/lib_battery_dispatch_pvsmoothing_fom_test.cpp index 5e7e44f16..e9af94db3 100644 --- a/test/shared_test/lib_battery_dispatch_pvsmoothing_fom_test.cpp +++ b/test/shared_test/lib_battery_dispatch_pvsmoothing_fom_test.cpp @@ -98,7 +98,7 @@ TEST_F(PVSmoothing_lib_battery_dispatch, Generic_w_PV_input_all_on) { // Run with fixed output // auto mod1 = ssc_module_create("generic_system"); - int errors = run_module(dat, "generic_system"); + int errors = run_module(dat, "custom_generation"); errors += run_module(dat, "battery"); // minimize memory usage for Travis // errors = run_module(dat, "grid"); diff --git a/test/shared_test/lib_battery_dispatch_test.h b/test/shared_test/lib_battery_dispatch_test.h index 60cd7d682..e9bf22deb 100644 --- a/test/shared_test/lib_battery_dispatch_test.h +++ b/test/shared_test/lib_battery_dispatch_test.h @@ -72,6 +72,7 @@ struct DispatchProperties { std::map percentDischarge; std::map percentGridcharge; bool canClipCharge = false; + bool canCurtailCharge = false; // resource std::vector pv; diff --git a/test/shared_test/lib_irradproc_test.cpp b/test/shared_test/lib_irradproc_test.cpp index 38798248f..eb210967f 100644 --- a/test/shared_test/lib_irradproc_test.cpp +++ b/test/shared_test/lib_irradproc_test.cpp @@ -153,7 +153,6 @@ TEST_F(IrradTest, sunriseAndSunsetAtDifferentLocationsTest_spa_lib_irradproc) { for (size_t i = 0; i < latitudes.size(); i++) { //run the solarpos function and check sunrise and sunset for each location - clear_spa_table(); solarpos_spa(2010, month[i], day[i], 14, 30, 0, latitudes[i], longitudes[i], time_zones[i], 0, alt[i], 0, 1016, 15, 180, sun_results); EXPECT_NEAR((double)sun_results[4], sunrise_times[i], e) << "sunrise time for lat " << latitudes[i] << " long " << longitudes[i] << " failed\n"; EXPECT_NEAR((double)sun_results[5], sunset_times[i], e) << "sunset time for lat " << latitudes[i] << " long " << longitudes[i] << "failed\n"; diff --git a/test/ssc_test/cmod_cashloan_test.cpp b/test/ssc_test/cmod_cashloan_test.cpp index b404e7295..4eb6d6380 100644 --- a/test/ssc_test/cmod_cashloan_test.cpp +++ b/test/ssc_test/cmod_cashloan_test.cpp @@ -166,7 +166,7 @@ TEST_F(CmodCashLoanTest, PVBatteryCommercial) { Test("cashloan", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodCashLoanTest, GenericResidential) { +TEST_F(CmodCashLoanTest, CustomGenerationResidential) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/cashloan/2022.08.08_develop_branch_Generic_System_Residential_cmod_cashloan.json"; std::string file_outputs = SSCDIR; @@ -177,7 +177,7 @@ TEST_F(CmodCashLoanTest, GenericResidential) { Test("cashloan", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodCashLoanTest, GenericCommercial) { +TEST_F(CmodCashLoanTest, CustomGenerationCommercial) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/cashloan/2022.08.08_develop_branch_Generic_System_Commercial_cmod_cashloan.json"; std::string file_outputs = SSCDIR; @@ -188,7 +188,7 @@ TEST_F(CmodCashLoanTest, GenericCommercial) { Test("cashloan", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodCashLoanTest, GenericBatteryResidential) { +TEST_F(CmodCashLoanTest, CustomGenerationBatteryResidential) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/cashloan/2022.08.08_develop_branch_Generic_Battery_Residential_cmod_cashloan.json"; std::string file_outputs = SSCDIR; @@ -199,7 +199,7 @@ TEST_F(CmodCashLoanTest, GenericBatteryResidential) { Test("cashloan", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodCashLoanTest, GenericBatteryCommercial) { +TEST_F(CmodCashLoanTest, CustomGenerationBatteryCommercial) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/cashloan/2022.08.08_develop_branch_Generic_Battery_Commercial_cmod_cashloan.json"; std::string file_outputs = SSCDIR; diff --git a/test/ssc_test/cmod_generic_test.cpp b/test/ssc_test/cmod_custom_generation_test.cpp similarity index 67% rename from test/ssc_test/cmod_generic_test.cpp rename to test/ssc_test/cmod_custom_generation_test.cpp index d9ab77a38..603751e3a 100644 --- a/test/ssc_test/cmod_generic_test.cpp +++ b/test/ssc_test/cmod_custom_generation_test.cpp @@ -33,12 +33,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include "cmod_generic_test.h" +#include "cmod_custom_generation_test.h" -/// Test Generic System with Battery for SingleOwner PPA -TEST_F(CMGeneric, SingleOwnerWithBattery_cmod_generic) { +/// Test Custom Generation with Battery for SingleOwner PPA +TEST_F(CMCustomGeneration, SingleOwnerWithBattery_cmod_custom_generation) { - generic_singleowner_battery_60min(data); + custom_generation_singleowner_battery_60min(data); // Test different dispatch strategies std::vector dispatch_options{ 0,2,3 }; @@ -46,33 +46,33 @@ TEST_F(CMGeneric, SingleOwnerWithBattery_cmod_generic) { // Run with hourly data for (size_t i = 0; i < dispatch_options.size(); i++) { ssc_data_set_number(data, "batt_dispatch_choice", (ssc_number_t)dispatch_options[i]); - EXPECT_FALSE(run_module(data, "generic_system")); + EXPECT_FALSE(run_module(data, "custom_generation")); EXPECT_FALSE(run_module(data, "battery")); EXPECT_FALSE(run_module(data, "singleowner")); } // Run with subhourly data - set_array(data, "energy_output_array", generictest::gen_path_30min, 8760 * 2); - set_array(data, "batt_custom_dispatch", generictest::batt_dispatch_path_30min, 8760 * 2); - set_array(data, "batt_room_temperature_celsius", generictest::temperature_path_30min, 8760 * 2); + set_array(data, "energy_output_array", customgenerationtest::gen_path_30min, 8760 * 2); + set_array(data, "batt_custom_dispatch", customgenerationtest::batt_dispatch_path_30min, 8760 * 2); + set_array(data, "batt_room_temperature_celsius", customgenerationtest::temperature_path_30min, 8760 * 2); for (size_t i = 0; i < dispatch_options.size(); i++) { ssc_data_set_number(data, "batt_dispatch_choice", (ssc_number_t)dispatch_options[i]); - EXPECT_FALSE(run_module(data, "generic_system")); + EXPECT_FALSE(run_module(data, "custom_generation")); EXPECT_FALSE(run_module(data, "battery")); EXPECT_FALSE(run_module(data, "singleowner")); } // Test with incorrect combo of data sizes ssc_data_set_number(data, "batt_dispatch_choice", 3); - set_array(data, "batt_custom_dispatch", generictest::batt_dispatch_path_30min, 8760*2); // 8760 or 8760 * 3 fails with execution error on Linux and windows - EXPECT_FALSE(run_module(data, "generic_system")); + set_array(data, "batt_custom_dispatch", customgenerationtest::batt_dispatch_path_30min, 8760*2); // 8760 or 8760 * 3 fails with execution error on Linux and windows + EXPECT_FALSE(run_module(data, "custom_generation")); EXPECT_FALSE(run_module(data, "battery")); } /// Test Generic System with Battery for various timesteps -TEST_F(CMGeneric, CommercialWithBattery_cmod_generic) { +TEST_F(CMCustomGeneration, CommercialWithBattery_cmod_generic) { - generic_commerical_battery_60min(data); + custom_generation_commerical_battery_60min(data); // Test different dispatch strategies std::vector dispatch_options{ 0,2,3 }; @@ -82,7 +82,7 @@ TEST_F(CMGeneric, CommercialWithBattery_cmod_generic) { ssc_data_set_number(data, "system_use_lifetime_output", l); for (size_t i = 0; i < dispatch_options.size(); i++) { ssc_data_set_number(data, "batt_dispatch_choice", (ssc_number_t)dispatch_options[i]); - EXPECT_FALSE(run_module(data, "generic_system")); + EXPECT_FALSE(run_module(data, "custom_generation")); EXPECT_FALSE(run_module(data, "battery")); EXPECT_FALSE(run_module(data, "utilityrate5")); EXPECT_FALSE(run_module(data, "cashloan")); @@ -90,17 +90,17 @@ TEST_F(CMGeneric, CommercialWithBattery_cmod_generic) { } // Run with subhourly data - set_array(data, "energy_output_array", generictest::gen_path_30min, 8760 * 2); - set_array(data, "batt_custom_dispatch", generictest::batt_dispatch_path_30min, 8760 * 2); - set_array(data, "batt_room_temperature_celsius", generictest::temperature_path_30min, 8760 * 2); - set_array(data, "load", generictest::load_profile_path_30min, 8760 * 2); + set_array(data, "energy_output_array", customgenerationtest::gen_path_30min, 8760 * 2); + set_array(data, "batt_custom_dispatch", customgenerationtest::batt_dispatch_path_30min, 8760 * 2); + set_array(data, "batt_room_temperature_celsius", customgenerationtest::temperature_path_30min, 8760 * 2); + set_array(data, "load", customgenerationtest::load_profile_path_30min, 8760 * 2); // With and without lifetime for (size_t l = 0; l < 2; l++) { ssc_data_set_number(data, "system_use_lifetime_output", l); for (size_t i = 0; i < dispatch_options.size(); i++) { ssc_data_set_number(data, "batt_dispatch_choice", (ssc_number_t)dispatch_options[i]); - EXPECT_FALSE(run_module(data, "generic_system")); + EXPECT_FALSE(run_module(data, "custom_generation")); EXPECT_FALSE(run_module(data, "battery")); EXPECT_FALSE(run_module(data, "utilityrate5")); EXPECT_FALSE(run_module(data, "cashloan")); @@ -109,14 +109,14 @@ TEST_F(CMGeneric, CommercialWithBattery_cmod_generic) { // Test with hourly load, subhourly gen ssc_data_set_number(data, "batt_dispatch_choice", 3); - set_array(data, "batt_custom_dispatch", generictest::batt_dispatch_path_30min, 8760 * 2); - set_array(data, "energy_output_array", generictest::gen_path_30min, 2*8760); - set_array(data, "batt_room_temperature_celsius", generictest::temperature_path_30min, 8760 * 2); - set_array(data, "load", generictest::load_profile_path_60min, 8760); + set_array(data, "batt_custom_dispatch", customgenerationtest::batt_dispatch_path_30min, 8760 * 2); + set_array(data, "energy_output_array", customgenerationtest::gen_path_30min, 2*8760); + set_array(data, "batt_room_temperature_celsius", customgenerationtest::temperature_path_30min, 8760 * 2); + set_array(data, "load", customgenerationtest::load_profile_path_60min, 8760); for (size_t l = 0; l < 2; l++) { ssc_data_set_number(data, "system_use_lifetime_output", l); - EXPECT_FALSE(run_module(data, "generic_system")); + EXPECT_FALSE(run_module(data, "custom_generation")); EXPECT_FALSE(run_module(data, "battery")); } } @@ -127,11 +127,11 @@ handled robustly in a cross-platform environment https://docs.microsoft.com/en-us/cpp/cpp/errors-and-exception-handling-modern-cpp?view=vs-2017#c-exceptions-versus-windows-seh-exceptions */ /* -TEST_F(CMGeneric, CommericalWithBatteryWrongSizes) +TEST_F(CMCustomGeneration, CommericalWithBatteryWrongSizes) { // Test with hourly gen, subhourly load, should fail everywhere generic_commerical_battery_60min(data); - set_array(data, "load", generictest::load_profile_path_30min, 2 * 8760); + set_array(data, "load", customgenerationtest::load_profile_path_30min, 2 * 8760); EXPECT_TRUE(run_module(data, "generic_system")); EXPECT_TRUE(run_module(data, "battery")); diff --git a/test/ssc_test/cmod_generic_test.h b/test/ssc_test/cmod_custom_generation_test.h similarity index 93% rename from test/ssc_test/cmod_generic_test.h rename to test/ssc_test/cmod_custom_generation_test.h index 2571f6e0c..cdc25d43f 100644 --- a/test/ssc_test/cmod_generic_test.h +++ b/test/ssc_test/cmod_custom_generation_test.h @@ -32,8 +32,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#ifndef _CMOD_GENERIC_TEST_H_ -#define _CMOD_GENERIC_TEST_H_ +#ifndef _CMOD_CUSTOMGENERATION_TEST_H_ +#define _CMOD_CUSTOMGENERATION_TEST_H_ #include #include @@ -44,14 +44,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "vartab.h" #include "../ssc/common.h" #include "../input_cases/code_generator_utilities.h" -#include "../input_cases/generic_common_data.h" +#include "../input_cases/custom_generation_common_data.h" /** * CMFuelCell tests the cmod_fuelcell using the SAM code generator to generate data * Eventually a method can be written to write this data to a vartable so that lower-level methods of pvsamv1 can be tested * For now, this uses the SSCAPI interfaces to run the compute module and compare results */ -class CMGeneric : public ::testing::Test { +class CMCustomGeneration : public ::testing::Test { public: diff --git a/test/ssc_test/cmod_equpartflip_test.cpp b/test/ssc_test/cmod_equpartflip_test.cpp index a5113dade..fb47176f4 100644 --- a/test/ssc_test/cmod_equpartflip_test.cpp +++ b/test/ssc_test/cmod_equpartflip_test.cpp @@ -82,7 +82,7 @@ TEST_F(CmodAllEquityPartnershipFlipTest, PV) { } -TEST_F(CmodAllEquityPartnershipFlipTest, GenericBattery) { +TEST_F(CmodAllEquityPartnershipFlipTest, CustomGenerationBattery) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/equpartflip/2023.10.27_om-expense-cash-flow_Generic_Battery_All_Equity_Partnership_Flip_cmod_equpartflip.json"; std::string file_outputs = SSCDIR; @@ -104,7 +104,7 @@ TEST_F(CmodAllEquityPartnershipFlipTest, GenericCSP) { Test("equpartflip", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodAllEquityPartnershipFlipTest, Generic) { +TEST_F(CmodAllEquityPartnershipFlipTest, CustomGeneration) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/equpartflip/2023.10.27_om-expense-cash-flow_Generic_System_All_Equity_Partnership_Flip_cmod_equpartflip.json"; std::string file_outputs = SSCDIR; diff --git a/test/ssc_test/cmod_host_developer_test.cpp b/test/ssc_test/cmod_host_developer_test.cpp index 6399dfd6d..d3f291168 100644 --- a/test/ssc_test/cmod_host_developer_test.cpp +++ b/test/ssc_test/cmod_host_developer_test.cpp @@ -148,7 +148,7 @@ TEST_F(CmodHostDeveloperTest, PVBattery) { } -TEST_F(CmodHostDeveloperTest, Generic) { +TEST_F(CmodHostDeveloperTest, CustomGeneration) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/host_developer/2023.10.27_om-expense-cash-flow_Generic_System_Host_Developer_cmod_host_developer.json"; std::string file_outputs = SSCDIR; @@ -160,7 +160,7 @@ TEST_F(CmodHostDeveloperTest, Generic) { } -TEST_F(CmodHostDeveloperTest, GenericBattery) { +TEST_F(CmodHostDeveloperTest, CustomGenerationBattery) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/host_developer/2023.10.27_om-expense-cash-flow_Generic_Battery_Host_Developer_cmod_host_developer.json"; std::string file_outputs = SSCDIR; diff --git a/test/ssc_test/cmod_hybrid_test.cpp b/test/ssc_test/cmod_hybrid_test.cpp index 155f32ddf..eeb279b68 100644 --- a/test/ssc_test/cmod_hybrid_test.cpp +++ b/test/ssc_test/cmod_hybrid_test.cpp @@ -153,9 +153,9 @@ TEST_F(CmodHybridTest, PVWattsv8WindBatteryHostDeveloper) { dat = nullptr; } -TEST_F(CmodHybridTest, GenericPVWattsWindFuelCellBatteryHybrid_SingleOwner) { +TEST_F(CmodHybridTest, CustomGenerationPVWattsWindFuelCellBatteryHybrid_SingleOwner) { char file_path[256]; - int nfc1 = sprintf(file_path, "%s/test/input_json/hybrids/Generic PVWatts Wind FuelCell Battery Hybrid_Single Owner.json", SSCDIR); + int nfc1 = sprintf(file_path, "%s/test/input_json/hybrids/CustomGeneration PVWatts Wind FuelCell Battery Hybrid_Single Owner.json", SSCDIR); std::ifstream file(file_path); std::ostringstream tmp; tmp << file.rdbuf(); @@ -183,7 +183,7 @@ TEST_F(CmodHybridTest, GenericPVWattsWindFuelCellBatteryHybrid_SingleOwner) { int len; auto outputs = ssc_data_get_table(dat, "output"); - auto gs_outputs = ssc_data_get_table(outputs, "generic_system"); + auto gs_outputs = ssc_data_get_table(outputs, "custom_generation"); ssc_data_get_number(gs_outputs, "annual_energy", &genericannualenergy); auto gs_om_expenses = ssc_data_get_array(gs_outputs, "cf_operating_expenses", &len); diff --git a/test/ssc_test/cmod_lcoefcr_test.cpp b/test/ssc_test/cmod_lcoefcr_test.cpp index b4bf37ae3..ddc6542b5 100644 --- a/test/ssc_test/cmod_lcoefcr_test.cpp +++ b/test/ssc_test/cmod_lcoefcr_test.cpp @@ -92,7 +92,7 @@ TEST_F(CmodLCOEFCRTest, GenericCSP) { Test("lcoefcr", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodLCOEFCRTest, Generic) { +TEST_F(CmodLCOEFCRTest, CustomGeneration) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/lcoefcr/2022.08.08_develop_branch_Generic_System_LCOE_Calculator_cmod_lcoefcr.json"; std::string file_outputs = SSCDIR; diff --git a/test/ssc_test/cmod_levpartflip_test.cpp b/test/ssc_test/cmod_levpartflip_test.cpp index 6b549c9b2..72df4b4b2 100644 --- a/test/ssc_test/cmod_levpartflip_test.cpp +++ b/test/ssc_test/cmod_levpartflip_test.cpp @@ -82,7 +82,7 @@ TEST_F(CmodLeveragedPartnershipFlipTest, PV) { } -TEST_F(CmodLeveragedPartnershipFlipTest, GenericBattery) { +TEST_F(CmodLeveragedPartnershipFlipTest, CustomGenerationBattery) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/levpartflip/2023.10.27_om-expense-cash-flow_Generic_Battery_Leveraged_Partnership_Flip_cmod_levpartflip.json"; std::string file_outputs = SSCDIR; @@ -104,7 +104,7 @@ TEST_F(CmodLeveragedPartnershipFlipTest, GenericCSP) { Test("levpartflip", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodLeveragedPartnershipFlipTest, Generic) { +TEST_F(CmodLeveragedPartnershipFlipTest, CustomGeneration) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/levpartflip/2023.10.27_om-expense-cash-flow_Generic_System_Leveraged_Partnership_Flip_cmod_levpartflip.json"; std::string file_outputs = SSCDIR; diff --git a/test/ssc_test/cmod_merchantplant_test.cpp b/test/ssc_test/cmod_merchantplant_test.cpp index 61e9aa08a..cdfff6d9d 100644 --- a/test/ssc_test/cmod_merchantplant_test.cpp +++ b/test/ssc_test/cmod_merchantplant_test.cpp @@ -82,7 +82,7 @@ TEST_F(CmodMerchantPlantTest, PV) { } -TEST_F(CmodMerchantPlantTest, GenericBattery) { +TEST_F(CmodMerchantPlantTest, CustomGenerationBattery) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/merchantplant/2022.08.08_develop_branch_Generic_Battery_Merchant_Plant_cmod_merchantplant.json"; std::string file_outputs = SSCDIR; @@ -93,7 +93,7 @@ TEST_F(CmodMerchantPlantTest, GenericBattery) { Test("merchantplant", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodMerchantPlantTest, GenericBattery_LCOS) { +TEST_F(CmodMerchantPlantTest, CustomGenerationBattery_LCOS) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/merchantplant/2022.10.21_develop_branch_Generic_Battery_Merchant_Plant_cmod_merchantplant.json"; std::string file_outputs = SSCDIR; @@ -115,7 +115,7 @@ TEST_F(CmodMerchantPlantTest, GenericCSP) { Test("merchantplant", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodMerchantPlantTest, Generic) { +TEST_F(CmodMerchantPlantTest, CustomGeneration) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/merchantplant/2022.08.08_develop_branch_Generic_System_Merchant_Plant_cmod_merchantplant.json"; std::string file_outputs = SSCDIR; diff --git a/test/ssc_test/cmod_saleleaseback_test.cpp b/test/ssc_test/cmod_saleleaseback_test.cpp index b95dbe078..d3a2a375b 100644 --- a/test/ssc_test/cmod_saleleaseback_test.cpp +++ b/test/ssc_test/cmod_saleleaseback_test.cpp @@ -82,7 +82,7 @@ TEST_F(CmodSaleLeasebackTest, PV) { } -TEST_F(CmodSaleLeasebackTest, GenericBattery) { +TEST_F(CmodSaleLeasebackTest, CustomGenerationBattery) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/saleleaseback/2023.10.27_om-expense-cash-flow_Generic_Battery_Sale_Leaseback_cmod_saleleaseback.json"; std::string file_outputs = SSCDIR; @@ -104,7 +104,7 @@ TEST_F(CmodSaleLeasebackTest, GenericCSP) { Test("saleleaseback", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodSaleLeasebackTest, Generic) { +TEST_F(CmodSaleLeasebackTest, CustomGeneration) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/saleleaseback/2023.10.27_om-expense-cash-flow_Generic_System_Sale_Leaseback_cmod_saleleaseback.json"; std::string file_outputs = SSCDIR; diff --git a/test/ssc_test/cmod_singleowner_test.cpp b/test/ssc_test/cmod_singleowner_test.cpp index b070e2880..04ca430ad 100644 --- a/test/ssc_test/cmod_singleowner_test.cpp +++ b/test/ssc_test/cmod_singleowner_test.cpp @@ -125,7 +125,7 @@ TEST_F(CmodSingleOwnerTest, FuelCell) { Test("singleowner", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodSingleOwnerTest, GenericBattery) { +TEST_F(CmodSingleOwnerTest, CustomGenerationBattery) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/singleowner/2022.08.08_develop_branch_Generic_Battery_Single_Owner_cmod_singleowner.json"; std::string file_outputs = SSCDIR; @@ -147,7 +147,7 @@ TEST_F(CmodSingleOwnerTest, GenericCSP) { Test("singleowner", file_inputs, file_outputs, compare_number_variables, compare_array_variables); } -TEST_F(CmodSingleOwnerTest, Generic) { +TEST_F(CmodSingleOwnerTest, CustomGeneration) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/singleowner/2022.08.08_develop_branch_Generic_System_Single_Owner_cmod_singleowner.json"; std::string file_outputs = SSCDIR; diff --git a/test/ssc_test/cmod_thirdpartyownership_test.cpp b/test/ssc_test/cmod_thirdpartyownership_test.cpp index 131968c88..dd2e314d4 100644 --- a/test/ssc_test/cmod_thirdpartyownership_test.cpp +++ b/test/ssc_test/cmod_thirdpartyownership_test.cpp @@ -82,7 +82,7 @@ TEST_F(CmodThirdPartyOwnershipTest, PVBattery) { } -TEST_F(CmodThirdPartyOwnershipTest, Generic) { +TEST_F(CmodThirdPartyOwnershipTest, CustomGeneration) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/thirdpartyownership/2022.08.08_develop_branch_Generic_System_Third_Party_cmod_thirdpartyownership.json"; std::string file_outputs = SSCDIR; @@ -94,7 +94,7 @@ TEST_F(CmodThirdPartyOwnershipTest, Generic) { } -TEST_F(CmodThirdPartyOwnershipTest, GenericBattery) { +TEST_F(CmodThirdPartyOwnershipTest, CustomGenerationBattery) { std::string file_inputs = SSCDIR; file_inputs += "/test/input_json/FinancialModels/thirdpartyownership/2022.08.08_develop_branch_Generic_Battery_Third_Party_cmod_thirdpartyownership.json"; std::string file_outputs = SSCDIR;