Skip to content

Commit 378604b

Browse files
committed
Updating the solution heliostat groupings
New groupings will allow for assignments to change by period (i.e., month). Additional updates: - added heliostat replacement threshold calculation to avoid replacements too late in the plant's life to achieve a positive expected ROI - updated parameter loading in O()
1 parent 57dabd5 commit 378604b

File tree

6 files changed

+124
-111
lines changed

6 files changed

+124
-111
lines changed

app/project.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,9 @@ bool Project::O()
17211721
wc.m_settings.capital_cost_per_crew = m_parameters.wash_crew_capital_cost.as_number();
17221722
wc.m_settings.heliostat_size = (double)(helio_width * helio_height);
17231723
wc.m_settings.crew_hours_per_week = m_parameters.wash_crew_max_hours_week.as_number();
1724-
wc.m_settings.discount_rate = term_int_rate * 0.01;
1724+
wc.m_settings.discount_rate_rev = term_int_rate * 0.01;
1725+
wc.m_settings.discount_rate_capital = term_int_rate * 0.01;
1726+
wc.m_settings.discount_rate_labor = term_int_rate * 0.01;
17251727
wc.m_settings.hourly_cost_per_crew = m_parameters.wash_crew_cost.as_number();
17261728
wc.m_settings.num_years = m_parameters.plant_lifetime.as_number();
17271729
wc.m_settings.price_per_kwh = m_parameters.price_per_kwh.as_number();
@@ -1731,7 +1733,7 @@ bool Project::O()
17311733
wc.m_settings.use_uniform_assignment = true;
17321734
wc.m_settings.max_num_crews = 10;
17331735
wc.OptimizeWashCrews();
1734-
while (wc.m_settings.max_num_crews == wc.m_results.num_wash_crews)
1736+
while (wc.m_settings.max_num_crews == wc.m_results.num_vehicles)
17351737
{
17361738
wc.m_settings.max_num_crews *= 2;
17371739
wc.OptimizeWashCrews();
@@ -1747,7 +1749,7 @@ bool Project::O()
17471749
);
17481750

17491751
od.m_settings.n_hr_sim = m_parameters.finance_period.as_integer() * 8760;
1750-
od.m_settings.n_wash_crews = wc.m_results.num_wash_crews;
1752+
od.m_settings.n_wash_crews = wc.m_results.num_crews_by_period[0];
17511753
od.m_settings.n_helio = m_design_outputs.number_heliostats.as_integer();
17521754
od.m_settings.degr_loss_per_hr = m_parameters.degr_per_hour.as_number();
17531755
od.m_settings.degr_accel_per_year = m_parameters.degr_accel_per_year.as_number();
@@ -1777,7 +1779,7 @@ bool Project::O()
17771779
od.m_results.heliostat_refurbish_cost = calc_real_dollars( od.m_results.heliostat_refurbish_cost_y1 ) * ann_fact;
17781780

17791781
//assign results to structure
1780-
m_optical_outputs.n_wash_crews.assign(wc.m_results.num_wash_crews);
1782+
m_optical_outputs.n_wash_crews.assign(wc.m_results.num_crews_by_period[0]);
17811783
m_optical_outputs.n_replacements.assign(od.m_results.n_replacements);
17821784
m_optical_outputs.heliostat_refurbish_cost.assign(od.m_results.heliostat_refurbish_cost);
17831785
m_optical_outputs.heliostat_refurbish_cost_y1.assign(od.m_results.heliostat_refurbish_cost_y1);

liboptical/optical_degr.cpp

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -48,26 +48,9 @@ float* optical_degradation::get_replacement_totals(int *length)
4848
}
4949

5050
//------------------------------------------
51-
double optical_degradation::get_replacement_threshold(
52-
double interval
53-
)
54-
{
55-
if (m_settings.degr_loss_per_hr < DBL_EPSILON)
56-
{
57-
return 0.0;
58-
}
59-
if (m_settings.degr_accel_per_year < DBL_EPSILON)
60-
{
61-
return 1.0 - interval * m_settings.degr_loss_per_hr;
62-
}
63-
else
64-
{
65-
double c = (1 + m_settings.degr_accel_per_year);
66-
return 1.0 - m_settings.degr_loss_per_hr * interval * std::pow(c, interval / 8760);
67-
}
68-
}
6951

70-
double optical_degradation::get_replacement_interval(
52+
53+
double optical_degradation::get_replacement_threshold(
7154
double mirror_output,
7255
int num_mirrors
7356
)
@@ -83,13 +66,13 @@ double optical_degradation::get_replacement_interval(
8366
(rev_loss_rate * m_settings.degr_loss_per_hr)
8467
);
8568
//std::cerr << "rev loss " << rev_loss_rate << " time " << time_threshold << " loss " << ((refurb_cost /time_threshold) + (rev_loss_rate * m_settings.degr_loss_per_hr * time_threshold/2.)) << "\n";
86-
return time_threshold;
69+
return 1.0 - time_threshold * m_settings.degr_loss_per_hr;
8770
}
8871
else
8972
{
9073

9174
//use bisection to obtain the optimal time interval.
92-
double b, c, logc, z_lo, z_med, z_hi, lo, med, hi, interval, min_t, max_t;
75+
double b, c, logc, r, z_lo, z_med, z_hi, lo, med, hi, interval, min_t, max_t;
9376

9477
b = rev_loss_rate * m_settings.degr_loss_per_hr;
9578
c = (1 + m_settings.degr_accel_per_year);
@@ -200,15 +183,15 @@ double optical_degradation::get_replacement_interval(
200183
//rate to obtain the optimal replacement threshold
201184
if (z_lo <= z_med && z_lo <= z_hi)
202185
{
203-
return lo;
186+
return 1.0 - m_settings.degr_loss_per_hr * lo * std::pow(c, lo / 8760);
204187
}
205188
else if (z_hi <= z_med && z_hi <= z_lo)
206189
{
207-
return hi;
190+
return 1.0 - m_settings.degr_loss_per_hr * hi * std::pow(c, hi / 8760);
208191
}
209192
else
210193
{
211-
return med;
194+
return 1.0 - m_settings.degr_loss_per_hr * med * std::pow(c, med / 8760);
212195
}
213196
}
214197
}
@@ -255,8 +238,8 @@ void optical_degradation::simulate(bool(*callback)(float prg, const char *msg),
255238
for (int i = 0; i < m_settings.n_wash_crews; i++)
256239
{
257240
c = opt_crew();
258-
c.start_heliostat = m_wc_results.assignments.at(i);
259-
c.end_heliostat = m_wc_results.assignments.at(i+1);
241+
c.start_heliostat = m_wc_results.assignments_by_crews.at(i).at(0);
242+
c.end_heliostat = m_wc_results.assignments_by_crews.at(i+1).at(0);
260243
crews.push_back(c);
261244
}
262245

@@ -277,28 +260,27 @@ void optical_degradation::simulate(bool(*callback)(float prg, const char *msg),
277260
total_mirrors += m_solar_data.num_mirrors_by_group[i];
278261
//std::cerr << i << " " << helios.at(i).replacement_threshold << " " << m_solar_data.mirror_output[i] << " " << m_solar_data.num_mirrors_by_group[i] << "\n";
279262
}
280-
double repl_interval = get_replacement_interval(
281-
m_solar_data.total_mirror_output / total_mirrors, 1
263+
double mean_threshold = get_replacement_threshold(
264+
m_solar_data.total_mirror_output / total_mirrors,
265+
1
282266
);
283-
double mean_threshold = get_replacement_threshold(repl_interval);
284267
for (int i = 0; i < n_helio_s; i++)
285268
{
286269
helios.at(i).replacement_threshold = mean_threshold;
287-
helios.at(i).replacement_interval = repl_interval;
270+
helios.at(i).replacement_interval = mean_threshold / m_settings.degr_loss_per_hr;
288271
}
289272
}
290273
else
291274
{
292275
//at this point, we optimize the threshold for each heliostat.
293276
for (int i = 0; i < n_helio_s; i++)
294277
{
295-
helios.at(i).replacement_interval = get_replacement_interval(
278+
helios.at(i).replacement_threshold = get_replacement_threshold(
296279
m_solar_data.mirror_output[i],
297280
m_solar_data.num_mirrors_by_group[i]
298281
);
299-
helios.at(i).replacement_threshold = get_replacement_threshold(
300-
helios.at(i).replacement_interval
301-
);
282+
helios.at(i).replacement_interval = helios.at(i).replacement_threshold / m_settings.degr_loss_per_hr;
283+
//std::cerr << i << " " << helios.at(i).replacement_threshold << " " << m_solar_data.mirror_output[i] << " " << m_solar_data.num_mirrors_by_group[i] << "\n";
302284
}
303285
}
304286

@@ -533,7 +515,7 @@ void optical_degradation::simulate(bool(*callback)(float prg, const char *msg),
533515
replacements_made += crews.at(s).replacements_made;
534516
}
535517

536-
m_results.n_replacements = (float)replacements_made;
518+
m_results.n_replacements = replacements_made;
537519

538520
m_results.soil_schedule = new float[m_settings.n_hr_sim];
539521
m_results.degr_schedule = new float[m_settings.n_hr_sim];
@@ -545,13 +527,13 @@ void optical_degradation::simulate(bool(*callback)(float prg, const char *msg),
545527

546528
for (int i = 0; i<m_settings.n_hr_sim; i++)
547529
{
548-
float s = (float)soil.at(i);
549-
float d = (float)degr.at(i);
530+
float s = soil.at(i);
531+
float d = degr.at(i);
550532

551533
m_results.soil_schedule[i] = s;
552534
m_results.degr_schedule[i] = d;
553-
m_results.repl_schedule[i] = (float)repr.at(i);
554-
m_results.repl_total[i] = (float)repr_cum.at(i);
535+
m_results.repl_schedule[i] = repr.at(i);
536+
m_results.repl_total[i] = repr_cum.at(i);
555537

556538
m_results.avg_degr += d;
557539
m_results.avg_soil += s;

liboptical/optical_degr.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ class optical_degradation
1616
opt_settings m_settings;
1717
opt_results m_results;
1818

19-
double get_replacement_threshold(double interval);
20-
double get_replacement_interval(double mirror_output, int num_mirrors);
19+
double get_replacement_threshold(double mirror_output, int num_mirrors);
2120

2221
void simulate(bool(*callback)(float prg, const char *msg)=0, std::string *results_file_name = 0, std::string *trace_file_name = 0);
2322

liboptical/wash_opt.cpp

Lines changed: 92 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <vector>
55
#include <string>
66
#include <algorithm>
7+
#include <set>
78
#include "wash_opt.h"
89

910
WashCrewOptimizer::WashCrewOptimizer()
@@ -439,79 +440,95 @@ void WashCrewOptimizer::GroupSolutionMirrors(int hours)
439440
m_solution_data = solar_field_data();
440441
m_solution_data.scale = hours * (m_settings.wash_rate / m_settings.heliostat_size);
441442
m_solution_data.groupings.clear();
442-
443-
std::vector<double> mirror_output = {};
444-
std::vector<int> num_mirrors_by_group = {};
445-
std::vector<int> new_assignments = { 0 };
443+
for (int t = 0; t < m_results.num_crews_by_period.size(); t++)
444+
m_results.solution_assignments[t] = {0};
446445

447446

448-
//determine the number of mirror groups;
449-
int mirror_idx = 0;
450-
double output = 0;
451-
int group_idx = 0;
452-
m_solution_data.groupings[group_idx] = {};
453-
int num_assigned_mirrors;
454-
for (int i = 0; i < m_results.num_vehicles; i++)
455-
{ //i = wash crew index
456-
num_assigned_mirrors = 0;
457-
for ( //j = condensed data mirror group index
458-
int j = 0;//m_results.assignments.at(i);
459-
j < 0;//m_results.assignments.at(i + 1);
460-
j++
461-
)
447+
//detremine the breakpoints that may be the end of a crew's assignment.
448+
std::set<int> assignment_breaks = {};
449+
if (m_settings.use_uniform_assignment)
450+
for (int t = 0; t < m_results.num_crews_by_period.size(); t++)
451+
for (int c = 0; c < m_results.num_crews_by_period.at(t); c++)
452+
assignment_breaks.insert((int)(
453+
m_solar_data.num_mirrors
454+
* c / m_results.num_crews_by_period.at(t)
455+
+ 0.5
456+
));
457+
else
458+
for (int t = 0; t < m_results.num_crews_by_period.size(); t++)
459+
for (int c = 0; c < m_results.num_crews_by_period.at(t); c++)
460+
assignment_breaks.insert((int)(
461+
GetNumberOfMirrors(
462+
m_results.assignments_by_crews[m_results.num_crews_by_period.at(t)].at(c),
463+
m_results.assignments_by_crews[m_results.num_crews_by_period.at(t)].at(c+1)
464+
)));
465+
466+
//Add the hourly breaks.
467+
for (int c = 0; c <= m_solar_data.num_mirrors; c += m_solution_data.scale)
468+
assignment_breaks.insert(c);
469+
470+
//Create a vector consisting of all the elemnents of the set
471+
//(which are unique and ordered).
472+
m_solution_data.num_mirror_groups = assignment_breaks.size();
473+
m_solution_data.num_mirrors_by_group = new int[assignment_breaks.size() - 1];
474+
m_solution_data.mirror_output = new double[assignment_breaks.size() - 1];
475+
//std::vector<int> breakpoints = {};
476+
//for (int c : assignment_breaks)
477+
// breakpoints.push_back(c);
478+
int idx = 0;
479+
int m;
480+
for (int c : assignment_breaks)
481+
{
482+
if (c != 0)
462483
{
463-
for (int k = 0; k < m_condensed_data.num_mirrors_by_group[j]; k++)
464-
{ //k = mirror index within condensed data
465-
output += m_solar_data.mirror_output[mirror_idx];
466-
m_solution_data.groupings[group_idx].push_back(m_solar_data.names[mirror_idx]);
467-
num_assigned_mirrors++;
468-
mirror_idx++;
469-
if (num_assigned_mirrors == m_solution_data.scale)
470-
{
471-
mirror_output.push_back(output);
472-
num_mirrors_by_group.push_back(num_assigned_mirrors);
473-
num_assigned_mirrors = 0;
474-
output = 0.;
475-
group_idx++;
476-
m_solution_data.groupings[group_idx] = {};
477-
}
484+
m_solution_data.num_mirrors_by_group[idx] = c - m;
485+
}
486+
m = c;
487+
idx++;
488+
}
489+
490+
//assign mirrors to the groups, and update assignments as breakpoints are hit.
491+
int cumulative_mirrors = 0;
492+
int solar_data_idx = 0;
493+
int solar_mirrors = m_solar_data.num_mirrors_by_group[0];
494+
int solution_mirrors;
495+
double sol_group_output;
496+
for (int solution_idx = 0; solution_idx < assignment_breaks.size() - 1; solution_idx++)
497+
{
498+
solution_mirrors = m_solution_data.num_mirrors_by_group[solution_idx]*1;
499+
sol_group_output = 0.;
500+
while (solution_mirrors > 0)
501+
{
502+
if (solution_mirrors < solar_mirrors)
503+
{
504+
sol_group_output += m_solar_data.mirror_output[solar_data_idx] * (double)solution_mirrors / m_solar_data.num_mirrors_by_group[solar_data_idx];
505+
cumulative_mirrors += solution_mirrors;
506+
solar_mirrors -= solution_mirrors;
507+
solution_mirrors = 0;
508+
}
509+
else
510+
{
511+
sol_group_output += m_solar_data.mirror_output[solar_data_idx] * (double)solar_mirrors / m_solar_data.num_mirrors_by_group[solar_data_idx];
512+
cumulative_mirrors += solar_mirrors;
513+
solution_mirrors -= solar_mirrors;
514+
solar_data_idx++;
515+
solar_mirrors = m_solar_data.num_mirrors_by_group[solar_data_idx];
478516
}
479517
}
480-
if (num_assigned_mirrors > 0)
518+
m_solution_data.mirror_output[solution_idx] = sol_group_output;
519+
for (int t = 0; t < m_results.num_crews_by_period.size(); t++)
481520
{
482-
mirror_output.push_back(output);
483-
num_mirrors_by_group.push_back(num_assigned_mirrors);
484-
num_assigned_mirrors = 0;
485-
output = 0.;
486-
group_idx++;
487-
m_solution_data.groupings[group_idx] = {};
521+
if (
522+
std::find(
523+
m_results.assignments_by_crews.at(m_results.num_crews_by_period.at(t)).begin(),
524+
m_results.assignments_by_crews.at(m_results.num_crews_by_period.at(t)).end(),
525+
cumulative_mirrors
526+
)
527+
!= m_results.assignments_by_crews.at(m_results.num_crews_by_period.at(t)).end()
528+
)
529+
m_results.solution_assignments[t].push_back(solution_idx);
488530
}
489-
new_assignments.push_back(group_idx);
490531
}
491-
/*
492-
std::cerr << "Path: ";
493-
for (int j = 0; j < m_results.assignments.size(); j++)
494-
{
495-
std::cerr << m_results.assignments.at(j) << ",";
496-
}
497-
std::cerr << "\n";
498-
for (int i = 0; i < group_idx; i++)
499-
{
500-
std::cerr << "Group " << i << " num mirrors: "
501-
<< num_mirrors_by_group[i]
502-
<< " power: " << mirror_output[i] << "\n";
503-
}
504-
*/
505-
m_solution_data.num_mirrors_by_group = new int[num_mirrors_by_group.size()]; &num_mirrors_by_group[0];
506-
m_solution_data.mirror_output = new double[mirror_output.size()];
507-
for (size_t i = 0; i < mirror_output.size(); i++)
508-
{
509-
m_solution_data.mirror_output[i] = mirror_output.at(i);
510-
m_solution_data.num_mirrors_by_group[i] = num_mirrors_by_group.at(i);
511-
}
512-
m_solution_data.total_mirror_output = m_condensed_data.total_mirror_output*1.0;
513-
m_solution_data.num_mirror_groups = mirror_output.size();
514-
//m_results.assignments = new_assignments;
515532
}
516533

517534
void WashCrewOptimizer::CalculateRevenueAndCosts()
@@ -959,6 +976,7 @@ void WashCrewOptimizer::OptimizeWashCrews(int scale, bool output)
959976
double cost, cost_eq;
960977
double field_eff, field_eff_eq;
961978
double min_cost = INFINITY;
979+
int cum_mirrors;
962980
for (int i = 1; i <= m_settings.max_num_crews; i++)
963981
{
964982
path = RetracePath(
@@ -978,10 +996,18 @@ void WashCrewOptimizer::OptimizeWashCrews(int scale, bool output)
978996
std::cerr << "\nAverage field efficiency: " << field_eff << "\n";
979997
*/
980998
//use the equal-assignment path if specified; otherwise, use DP output.
999+
1000+
m_results.assignments_by_crews[i] = {0};
1001+
for (int c = 0; c < path.size()-1; c++)
1002+
m_results.assignments_by_crews[i].push_back(
1003+
GetNumberOfMirrors(path[c], path[c + 1])
1004+
);
9811005
m_results.assignments_by_crews[i] = path;
9821006
for (int t = 0; t < 12; t++)
9831007
{
984-
rev_losses[int_pair_to_string(i, t+1)] = cost * m_solar_data.dni_by_period.at(t);
1008+
rev_losses[int_pair_to_string(i, t+1)] = (
1009+
cost * m_solar_data.dni_by_period.at(t)
1010+
);
9851011
}
9861012
}
9871013
}

liboptical/wash_opt_structure.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ solar_field_data::solar_field_data()
125125
scale = 1;
126126

127127
num_mirror_groups = NULL;
128+
num_mirrors = NULL;
128129
dni_by_period = {};
129130
labor_by_period = {};
130131
groupings = {};
@@ -147,6 +148,7 @@ solar_field_data::~solar_field_data()
147148
wash_crew_opt_results::wash_crew_opt_results()
148149
{
149150
assignments_by_crews = {};
151+
solution_assignments = {};
150152
num_crews_by_period = {};
151153
objective_values = 0;
152154
parents = 0;

0 commit comments

Comments
 (0)