Skip to content

Commit 74036ea

Browse files
committed
Adding solarfield optimizer code
Optimizer simulates the failure and repair of heliostat components over the life of the plant, tracking an objective function equal to total revenue loss plus labor and repair costs. The optimzier assumes that the cost function is submodular, and adds staff as long as the return on additional labor investment is positive.
1 parent 014a99a commit 74036ea

File tree

10 files changed

+135
-7
lines changed

10 files changed

+135
-7
lines changed

build_linux/Makefile-libsolar

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ OBJECTS = \
1010
solarfield_structures.o\
1111
solarfield_heliostat.o\
1212
solarfield_staff.o\
13-
solarfield_avail.o
13+
solarfield_avail.o\
14+
solarfield_opt.o
1415

1516
TARGET = libsolar.a
1617

build_vs2017/libsolar.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121
<ItemGroup>
2222
<ClCompile Include="..\libsolar\solarfield_avail.cpp" />
2323
<ClCompile Include="..\libsolar\solarfield_heliostat.cpp" />
24+
<ClCompile Include="..\libsolar\solarfield_opt.cpp" />
2425
<ClCompile Include="..\libsolar\solarfield_staff.cpp" />
2526
<ClCompile Include="..\libsolar\solarfield_structures.cpp" />
2627
</ItemGroup>
2728
<ItemGroup>
2829
<ClInclude Include="..\libsolar\solarfield_avail.h" />
2930
<ClInclude Include="..\libsolar\solarfield_heliostat.h" />
31+
<ClInclude Include="..\libsolar\solarfield_opt.h" />
3032
<ClInclude Include="..\libsolar\solarfield_staff.h" />
3133
<ClInclude Include="..\libsolar\solarfield_structures.h" />
3234
</ItemGroup>

libsolar/solarfield_avail.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void solarfield_availability::initialize()
3636
double problem_scale = (double)m_settings.n_helio / hscale;
3737
int n_helio_s = (int)hscale;
3838
int n_components = (int)m_settings.helio_components.size();
39-
create_om_staff(m_settings.n_om_staff[0], m_settings.max_hours_per_day, m_settings.max_hours_per_week);
39+
create_om_staff(m_settings.n_om_staff, m_settings.max_hours_per_day, m_settings.max_hours_per_week);
4040
get_operating_hours();
4141
create_helio_field(n_components, n_helio_s, problem_scale);
4242
initialize_results();
@@ -60,6 +60,7 @@ void solarfield_availability::initialize_results()
6060
m_results.n_repairs = 0;
6161
m_results.n_repairs_per_component.assign(m_settings.helio_components.size(), 0);
6262
m_results.n_failures_per_component.assign(m_settings.helio_components.size(), 0);
63+
m_results.repair_cost_per_year.assign(m_settings.n_years, 0);
6364

6465
m_results.staff_utilization = 0.;
6566

@@ -331,6 +332,7 @@ void solarfield_availability::process_repair()
331332
*/
332333
m_current_availability += m_settings.helio_performance[m_current_event.helio_id] / m_settings.sum_performance;
333334
m_results.n_repairs_per_component[m_current_event.component_idx] += 1;
335+
m_results.repair_cost_per_year[int((m_current_event.time-DBL_EPSILON) / 8760)] += m_field.m_components.at(m_current_event.component_idx)->get_repair_cost();
334336
m_field.m_helios.at(m_current_event.helio_id)->end_repair(
335337
m_current_event.time,
336338
m_current_event.component_idx
@@ -485,7 +487,7 @@ void solarfield_availability::simulate(bool(*callback)(float prg, const char *ms
485487
while (t < (double)nhours)
486488
{
487489
m_current_event = m_event_queue.top();
488-
m_current_event.print();
490+
//m_current_event.print();
489491
run_current_event(t);
490492
t = m_current_event.time;
491493
m_event_queue.pop();

libsolar/solarfield_avail.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,6 @@ class solarfield_availability
5656

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

59-
60-
61-
6259
};
6360

6461
#endif

libsolar/solarfield_heliostat.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ double solarfield_helio_component::get_mean_repair_time()
8686
return m_properties.m_mean_repair_time;
8787
}
8888

89+
double solarfield_helio_component::get_repair_cost()
90+
{
91+
return m_properties.m_repair_cost;
92+
}
93+
8994

9095

9196

libsolar/solarfield_heliostat.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class solarfield_helio_component
5757
double gen_repair_time(WELLFiveTwelve &gen);
5858

5959
double get_mean_repair_time();
60+
double get_repair_cost();
6061
};
6162

6263

libsolar/solarfield_opt.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include "./solarfield_opt.h"
2+
#include "./solarfield_avail.h"
3+
#include <iostream>
4+
5+
void solarfield_opt::initialize()
6+
{
7+
m_sfa.initialize();
8+
}
9+
10+
double solarfield_opt::calculate_rev_loss()
11+
{
12+
double ann_revenue = m_sfa.m_settings.sum_performance * m_settings.price_per_kwh * m_settings.system_efficiency;
13+
double factor = 1.0;
14+
double rev_loss = 0.;
15+
for (size_t t = 0; t < m_sfa.m_settings.n_years; t++)
16+
{
17+
rev_loss += ann_revenue * factor * (1-m_sfa.m_results.yearly_avg_avail[t]);
18+
factor *= (1.0 - m_settings.revenue_discount_rate);
19+
}
20+
return rev_loss;
21+
}
22+
23+
double solarfield_opt::calculate_labor_cost()
24+
{
25+
double ann_cost = 52 * m_sfa.m_settings.max_hours_per_week * m_settings.hourly_cost_per_staff * m_sfa.m_settings.n_om_staff;
26+
double total_cost = 0.;
27+
for (size_t t = 0; t < m_sfa.m_settings.n_years; t++)
28+
{
29+
total_cost += ann_cost;
30+
ann_cost *= (1.0 - m_settings.labor_discount_rate);
31+
}
32+
return total_cost;
33+
}
34+
35+
double solarfield_opt::calculate_repair_cost()
36+
{
37+
double factor = 1.0;
38+
double total_cost = 0.;
39+
for (size_t t = 0; t < m_sfa.m_settings.n_years; t++)
40+
{
41+
total_cost += factor * m_sfa.m_results.repair_cost_per_year[t];
42+
factor *= (1.0 - m_settings.repair_discount_rate);
43+
}
44+
return total_cost;
45+
}
46+
47+
void solarfield_opt::optimize_staff(bool(*callback)(float prg, const char *msg), std::string *results_file_name)
48+
{
49+
WELLFiveTwelve gen(0);
50+
gen.assignStates(m_sfa.m_settings.seed % 100);
51+
m_sfa.assign_generator(gen);
52+
double total_cost;
53+
double best_cost = INFINITY;
54+
m_sfa.m_settings.n_om_staff = 1;
55+
while (m_sfa.m_settings.n_om_staff <= m_settings.max_num_staff)
56+
{
57+
gen.assignStates(m_sfa.m_settings.seed % 100);
58+
m_sfa.initialize();
59+
m_sfa.simulate();
60+
total_cost = calculate_rev_loss() + calculate_labor_cost() + calculate_repair_cost();
61+
std::cerr << "num staff: " << m_sfa.m_settings.n_om_staff << "\n";
62+
std::cerr << "avg_avail: " << m_sfa.m_results.avg_avail << "\n";
63+
std::cerr << "total_cost: " << total_cost << "\n";
64+
if (total_cost > best_cost)
65+
break;
66+
best_cost = total_cost;
67+
m_results = m_sfa.m_results;
68+
m_sfa.m_settings.n_om_staff++;
69+
}
70+
m_sfa.m_settings.n_om_staff--;
71+
std::cerr << "final cost: " << best_cost << "\n";
72+
std::cerr << "optimal # of ft staff: " << m_sfa.m_settings.n_om_staff << "\n";
73+
std::cerr << "avg_avail: " << m_results.avg_avail << "\n";
74+
75+
}

libsolar/solarfield_opt.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#ifndef _SOLARFIELD_OPT
2+
#define _SOLARFIELD_OPT
3+
4+
#include "./solarfield_avail.h"
5+
#include "./solarfield_heliostat.h"
6+
#include "./solarfield_staff.h"
7+
#include "./solarfield_structures.h"
8+
9+
class solarfield_opt
10+
{
11+
bool m_sim_available;
12+
public:
13+
solarfield_availability m_sfa;
14+
solarfield_opt_settings m_settings;
15+
solarfield_results m_results;
16+
17+
void initialize();
18+
double calculate_rev_loss();
19+
double calculate_labor_cost();
20+
double calculate_repair_cost();
21+
void optimize_staff(bool(*callback)(float prg, const char *msg) = 0, std::string *results_file_name = 0);
22+
23+
};
24+
25+
26+
#endif

libsolar/solarfield_structures.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,4 @@ void solarfield_results::print_avail_and_queue_schedule()
7373
std::cerr << t << "," << avail_schedule[t] << "," << queue_size_vs_time[t] << "\n";
7474
std::cerr << "\n";
7575
}
76+

libsolar/solarfield_structures.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ struct solarfield_settings
1616

1717

1818
//-- Staff
19-
std::vector<int> n_om_staff; // Total number of staff per year
19+
int n_om_staff; // Total number of full-time staff
2020
double max_hours_per_day; // Maximum time per day for full-time staff [hr]
2121
double max_hours_per_week; // Maximum time per week for full-time staff [hr]
2222

@@ -57,10 +57,12 @@ struct solarfield_results
5757

5858
std::vector<double> avail_schedule; // Availability schedule
5959
std::vector<double> yearly_avg_avail; // Yearly-average availability
60+
6061

6162
double n_repairs; // Total number of repairs completed
6263
std::vector<double> n_repairs_per_component; // Total number of repairs per component
6364
std::vector<double> n_failures_per_component; // Total number of failures per component
65+
std::vector<double> repair_cost_per_year; // Total repair cost by year
6466

6567
double staff_utilization;
6668

@@ -112,5 +114,21 @@ struct solarfield_event
112114

113115
bool operator<(const solarfield_event& e1, const solarfield_event& e2);
114116

117+
struct solarfield_opt_settings
118+
{
119+
//inputs
120+
int max_num_staff;
121+
double hourly_cost_per_staff; //per hour
122+
double labor_discount_rate;
123+
double revenue_discount_rate;
124+
double repair_discount_rate;
125+
double temporary_staff_cost_multiple; //in m^2 per hour
126+
double system_efficiency; //assumed efficiency including receiver, TES losses, power cycle
127+
double price_per_kwh; //assumed average for grid output
128+
double num_years; //years of operation to calculate NPV of annual costs
129+
130+
solarfield_opt_settings() {};
131+
//void print();
132+
};
115133

116134
#endif

0 commit comments

Comments
 (0)