00001
00022 #include <stdlib.h>
00023 #include <stdio.h>
00024 #include <errno.h>
00025 #include <math.h>
00026
00027 #include "solar.h"
00028
00029 #define RAD(x) (x*PI)/180
00030
00031 CLASS *solar::oclass = NULL;
00032 solar *solar::defaults = NULL;
00033
00034 static PASSCONFIG passconfig = PC_BOTTOMUP|PC_POSTTOPDOWN;
00035 static PASSCONFIG clockpass = PC_BOTTOMUP;
00036
00037
00038 solar::solar(MODULE *module)
00039 {
00040 if (oclass==NULL)
00041 {
00042 oclass = gl_register_class(module,"solar",sizeof(solar),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN|PC_AUTOLOCK);
00043 if (oclass==NULL)
00044 throw "unable to register class solar";
00045 else
00046 oclass->trl = TRL_PROOF;
00047
00048
00049 if (gl_publish_variable(oclass,
00050 PT_enumeration,"generator_mode",PADDR(gen_mode_v),
00051 PT_KEYWORD,"UNKNOWN",(enumeration)UNKNOWN,
00052 PT_KEYWORD,"CONSTANT_V",(enumeration)CONSTANT_V,
00053 PT_KEYWORD,"CONSTANT_PQ",(enumeration)CONSTANT_PQ,
00054 PT_KEYWORD,"CONSTANT_PF",(enumeration)CONSTANT_PF,
00055 PT_KEYWORD,"SUPPLY_DRIVEN",(enumeration)SUPPLY_DRIVEN,
00056
00057 PT_enumeration,"generator_status",PADDR(gen_status_v),
00058 PT_KEYWORD,"OFFLINE",(enumeration)OFFLINE,
00059 PT_KEYWORD,"ONLINE",(enumeration)ONLINE,
00060
00061 PT_enumeration,"panel_type", PADDR(panel_type_v),
00062 PT_KEYWORD, "SINGLE_CRYSTAL_SILICON", (enumeration)SINGLE_CRYSTAL_SILICON,
00063 PT_KEYWORD, "MULTI_CRYSTAL_SILICON", (enumeration)MULTI_CRYSTAL_SILICON,
00064 PT_KEYWORD, "AMORPHOUS_SILICON", (enumeration)AMORPHOUS_SILICON,
00065 PT_KEYWORD, "THIN_FILM_GA_AS", (enumeration)THIN_FILM_GA_AS,
00066 PT_KEYWORD, "CONCENTRATOR", (enumeration)CONCENTRATOR,
00067
00068 PT_enumeration,"power_type",PADDR(power_type_v),
00069 PT_KEYWORD,"AC",(enumeration)AC,
00070 PT_KEYWORD,"DC",(enumeration)DC,
00071
00072 PT_enumeration, "INSTALLATION_TYPE", PADDR(installation_type_v),
00073 PT_KEYWORD, "ROOF_MOUNTED", (enumeration)ROOF_MOUNTED,
00074 PT_KEYWORD, "GROUND_MOUNTED",(enumeration)GROUND_MOUNTED,
00075
00076 PT_enumeration, "SOLAR_TILT_MODEL", PADDR(solar_model_tilt), PT_DESCRIPTION, "solar tilt model used to compute insolation values",
00077 PT_KEYWORD, "DEFAULT", (enumeration)LIUJORDAN,
00078 PT_KEYWORD, "SOLPOS", (enumeration)SOLPOS,
00079 PT_KEYWORD, "PLAYERVALUE", (enumeration)PLAYERVAL,
00080
00081 PT_enumeration, "SOLAR_POWER_MODEL", PADDR(solar_power_model),
00082 PT_KEYWORD, "DEFAULT", (enumeration)BASEEFFICIENT,
00083 PT_KEYWORD, "FLATPLATE", (enumeration)FLATPLATE,
00084
00085 PT_double, "a_coeff", PADDR(module_acoeff), PT_DESCRIPTION, "a coefficient for module temperature correction formula",
00086 PT_double, "b_coeff[s/m]", PADDR(module_bcoeff), PT_DESCRIPTION, "b coefficient for module temperature correction formula",
00087 PT_double, "dT_coeff[m*m*degC/kW]", PADDR(module_dTcoeff), PT_DESCRIPTION, "Temperature difference coefficient for module temperature correction formula",
00088 PT_double, "T_coeff[%/degC]", PADDR(module_Tcoeff), PT_DESCRIPTION, "Maximum power temperature coefficient for module temperature correction formula",
00089
00090 PT_double, "NOCT[degF]", PADDR(NOCT),
00091 PT_double, "Tmodule[degF]", PADDR(Tmodule),
00092 PT_double, "Tambient[degC]", PADDR(Tambient),
00093 PT_double, "wind_speed[mph]", PADDR(wind_speed),
00094 PT_double, "ambient_temperature[degF]", PADDR(Tamb), PT_DESCRIPTION, "Current ambient temperature of air",
00095 PT_double, "Insolation[W/sf]", PADDR(Insolation),
00096 PT_double, "Rinternal[Ohm]", PADDR(Rinternal),
00097 PT_double, "Rated_Insolation[W/sf]", PADDR(Rated_Insolation),
00098 PT_double, "Pmax_temp_coeff", PADDR(Pmax_temp_coeff),
00099 PT_double, "Voc_temp_coeff", PADDR(Voc_temp_coeff),
00100 PT_complex, "V_Max[V]", PADDR(V_Max),
00101 PT_complex, "Voc_Max[V]", PADDR(Voc_Max),
00102 PT_complex, "Voc[V]", PADDR(Voc),
00103 PT_double, "efficiency[unit]", PADDR(efficiency),
00104 PT_double, "area[sf]", PADDR(area),
00105 PT_double, "soiling[pu]", PADDR(soiling_factor), PT_DESCRIPTION, "Soiling of array factor - representing dirt on array",
00106 PT_double, "derating[pu]", PADDR(derating_factor), PT_DESCRIPTION, "Panel derating to account for manufacturing variances",
00107 PT_double, "Tcell[degC]", PADDR(Tcell),
00108
00109 PT_double, "Rated_kVA[kVA]", PADDR(Rated_kVA), PT_DEPRECATED, PT_DESCRIPTION, "This variable has issues with inconsistent handling in the code, so we will deprecate this in the future (VA maps to kVA, for example).",
00110 PT_double, "rated_power[W]", PADDR(Max_P), PT_DESCRIPTION, "Used to define the size of the solar panel in power rather than square footage.",
00111 PT_complex, "P_Out[kW]", PADDR(P_Out),
00112 PT_complex, "V_Out[V]", PADDR(V_Out),
00113 PT_complex, "I_Out[A]", PADDR(I_Out),
00114 PT_complex, "VA_Out[VA]", PADDR(VA_Out),
00115 PT_object, "weather", PADDR(weather),
00116
00117 PT_double, "shading_factor[pu]", PADDR(shading_factor), PT_DESCRIPTION, "Shading factor for scaling solar power to the array",
00118 PT_double, "tilt_angle[deg]", PADDR(tilt_angle), PT_DESCRIPTION, "Tilt angle of PV array",
00119 PT_double, "orientation_azimuth[deg]", PADDR(orientation_azimuth), PT_DESCRIPTION, "Facing direction of the PV array",
00120 PT_bool, "latitude_angle_fix", PADDR(fix_angle_lat), PT_DESCRIPTION, "Fix tilt angle to installation latitude value",
00121
00122 PT_complex, "default_voltage_variable", PADDR(default_voltage_array),PT_ACCESS,PA_HIDDEN,PT_DESCRIPTION,"Accumulator/placeholder for default voltage value, when solar is run without an inverter",
00123 PT_complex, "default_current_variable", PADDR(default_current_array),PT_ACCESS,PA_HIDDEN,PT_DESCRIPTION,"Accumulator/placeholder for default current value, when solar is run without an inverter",
00124
00125 PT_enumeration, "orientation", PADDR(orientation_type),
00126 PT_KEYWORD, "DEFAULT", (enumeration)DEFAULT,
00127 PT_KEYWORD, "FIXED_AXIS", (enumeration)FIXED_AXIS,
00128
00129
00130
00131
00132
00133
00134
00135 PT_set, "phases", PADDR(phases),
00136 PT_KEYWORD, "A",(set)PHASE_A,
00137 PT_KEYWORD, "B",(set)PHASE_B,
00138 PT_KEYWORD, "C",(set)PHASE_C,
00139 PT_KEYWORD, "N",(set)PHASE_N,
00140 PT_KEYWORD, "S",(set)PHASE_S,
00141 NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__);
00142
00143
00144 if (gl_publish_function(oclass, "interupdate_gen_object", (FUNCTIONADDR)interupdate_solar)==NULL)
00145 GL_THROW("Unable to publish solar deltamode function");
00146
00147 defaults = this;
00148 memset(this,0,sizeof(solar));
00149
00150 }
00151 }
00152
00153
00154 int solar::create(void)
00155 {
00156
00157
00158 NOCT = 118.4;
00159 Tcell = 21.0;
00160 Tambient = 25.0;
00161 Tamb = 77;
00162 wind_speed = 0.0;
00163 Insolation = 0;
00164 Rinternal = 0.05;
00165 prevTemp = 15.0;
00166 currTemp = 15.0;
00167 prevTime = 0;
00168 Rated_Insolation = 92.902;
00169 V_Max = complex (27.1,0);
00170 Voc_Max = complex(34,0);
00171 Voc = complex (34,0);
00172 P_Out = 0.0;
00173
00174 area = 0;
00175
00176
00177 efficiency = 0;
00178 Pmax_temp_coeff = 0.0;
00179 Voc_temp_coeff = 0.0;
00180
00181
00182 pTout = NULL;
00183 pWindSpeed = NULL;
00184
00185 module_acoeff = -2.81;
00186 module_bcoeff = -0.0455;
00187 module_dTcoeff = 0.0;
00188 module_Tcoeff = -0.5;
00189
00190 shading_factor = 1;
00191 tilt_angle = 45;
00192 orientation_azimuth = 180.0;
00193 orientation_azimuth_corrected = 0;
00194 fix_angle_lat = false;
00195
00196 soiling_factor = 0.95;
00197 derating_factor = 0.95;
00198
00199 orientation_type = DEFAULT;
00200 solar_model_tilt = LIUJORDAN;
00201 solar_power_model = BASEEFFICIENT;
00202
00203
00204 calc_solar_radiation = NULL;
00205
00206
00207 deltamode_inclusive = false;
00208 first_sync_delta_enabled = false;
00209
00210
00211 inverter_voltage_property = NULL;
00212 inverter_current_property = NULL;
00213
00214
00215 default_voltage_array = complex(0.0,0.0);
00216 default_current_array = complex(0.0,0.0);
00217
00218 return 1;
00219 }
00220
00221
00226 int solar::init_climate()
00227 {
00228 OBJECT *hdr = OBJECTHDR(this);
00229 OBJECT *obj = NULL;
00230
00231
00232 FINDLIST *climates = NULL;
00233
00234 if (solar_model_tilt != PLAYERVAL)
00235 {
00236 if (weather!=NULL)
00237 {
00238 if(!gl_object_isa(weather, "climate")){
00239
00240 gl_error("weather property refers to a(n) \"%s\" object and not a climate object", weather->oclass->name);
00241
00242
00243
00244
00245
00246 return 0;
00247 }
00248 obj = weather;
00249 }
00250 else
00251 {
00252 climates = gl_find_objects(FL_NEW,FT_CLASS,SAME,"climate",FT_END);
00253 if (climates==NULL)
00254 {
00255
00256 weather = NULL;
00257 }
00258 else if (climates->hit_count==0)
00259 {
00260
00261 weather = NULL;
00262 }
00263 else
00264 {
00265 if (climates->hit_count>1)
00266 {
00267 gl_warning("solarpanel: %d climates found, using first one defined", climates->hit_count);
00268
00269
00270
00271 }
00272
00273
00274 gl_verbose("solar init: climate data was found!");
00275
00276 obj = gl_find_next(climates,NULL);
00277 weather = obj;
00278 }
00279 }
00280
00281
00282 if (weather == NULL)
00283 {
00284
00285 gl_warning("solarpanel: no climate data found, using static data");
00286
00287
00288
00289
00290
00291
00292
00293 Tamb = 59.0;
00294 wind_speed = 0.0;
00295
00296 if (orientation_type==FIXED_AXIS)
00297 {
00298 GL_THROW("FIXED_AXIS requires a climate file!");
00299
00300
00301
00302
00303 }
00304 }
00305 else if (!gl_object_isa(weather,"climate"))
00306 {
00307 GL_THROW("weather object is not a climate object!");
00308
00309
00310
00311
00312
00313 }
00314 else
00315 {
00316 if((obj->flags & OF_INIT) != OF_INIT){
00317 char objname[256];
00318 gl_verbose("solar::init(): deferring initialization on %s", gl_name(obj, objname, 255));
00319 return 2;
00320 }
00321 if (obj->rank<=hdr->rank)
00322 gl_set_dependent(obj,hdr);
00323
00324
00325 if (isnan(hdr->longitude))
00326 {
00327
00328 hdr->longitude = obj->longitude;
00329 }
00330
00331
00332 if (isnan(hdr->latitude))
00333 {
00334
00335 hdr->latitude = obj->latitude;
00336 }
00337
00338
00339 pTout = new gld_property(obj,"temperature");
00340
00341
00342 if ((pTout->is_valid() != true) || (pTout->is_double() != true))
00343 {
00344 GL_THROW("solar:%d - %s - Failed to map outside temperature",hdr->id,(hdr->name ? hdr->name : "Unnamed"));
00345
00346
00347
00348
00349 }
00350
00351
00352 pWindSpeed = new gld_property(obj,"wind_speed");
00353
00354
00355 if ((pWindSpeed->is_valid() != true) || (pWindSpeed->is_double() != true))
00356 {
00357 GL_THROW("solar:%d - %s - Failed to map wind speed",hdr->id,(hdr->name ? hdr->name : "Unnamed"));
00358
00359
00360
00361
00362 }
00363
00364
00365 if (fix_angle_lat==true)
00366 {
00367 if (hdr->latitude < 0)
00368 {
00369
00370 tilt_angle = -hdr->latitude;
00371 }
00372 else
00373 {
00374
00375 tilt_angle = hdr->latitude;
00376 }
00377 }
00378
00379
00380 if (tilt_angle < 0)
00381 {
00382 GL_THROW("Invalid tilt_angle - tilt must be between 0 and 90 degrees");
00383
00384
00385
00386
00387 }
00388 else if (tilt_angle > 90.0)
00389 {
00390 GL_THROW("Invalid tilt angle - values above 90 degrees are unsupported!");
00391
00392
00393
00394
00395
00396 }
00397
00398
00399
00400 if (orientation_type == DEFAULT)
00401 {
00402 calc_solar_radiation = (FUNCTIONADDR)(gl_get_function(obj,"calc_solar_ideal_shading_position_radians"));
00403 }
00404 else if (orientation_type == FIXED_AXIS)
00405 {
00406
00407 if (solar_model_tilt==LIUJORDAN)
00408 {
00409
00410
00411 calc_solar_radiation = (FUNCTIONADDR)(gl_get_function(obj,"calculate_solar_radiation_shading_position_radians"));
00412 }
00413 else if (solar_model_tilt==SOLPOS)
00414 {
00415
00416
00417 calc_solar_radiation = (FUNCTIONADDR)(gl_get_function(obj,"calculate_solpos_radiation_shading_position_radians"));
00418 }
00419
00420
00421 if (calc_solar_radiation == NULL)
00422 {
00423 GL_THROW("Unable to map solar radiation function on %s in %s",obj->name,hdr->name);
00424
00425
00426
00427
00428 }
00429
00430
00431 if ((orientation_azimuth<0.0) || (orientation_azimuth > 360.0))
00432 {
00433 GL_THROW("orientation_azimuth must be a value representing a valid cardinal direction of 0 to 360 degrees!");
00434
00435
00436
00437
00438 }
00439
00440
00441 if (solar_model_tilt == LIUJORDAN)
00442 {
00443 if (obj->latitude>0.0)
00444 {
00445 orientation_azimuth_corrected = 180.0 - orientation_azimuth;
00446 }
00447 else if (obj->latitude<0.0)
00448 {
00449 gl_warning("solar:%s - Default solar position model is not recommended for southern hemisphere!",hdr->name);
00450
00451
00452
00453
00454
00455
00456
00457 if ((orientation_azimuth >= 0.0) && (orientation_azimuth <= 180.0))
00458 {
00459 orientation_azimuth_corrected = orientation_azimuth;
00460 }
00461 else if (orientation_azimuth == 360.0)
00462 {
00463 orientation_azimuth_corrected = 0.0;
00464 }
00465 else
00466 {
00467 orientation_azimuth_corrected = orientation_azimuth - 360.0;
00468 }
00469 }
00470 else
00471 {
00472 GL_THROW("Exact equator location of array detected - unknown how to handle orientation");
00473
00474
00475
00476
00477 }
00478 }
00479 else
00480 {
00481 orientation_azimuth_corrected = orientation_azimuth;
00482 }
00483 }
00484
00485 }
00486 }
00487 else
00488 {
00489 gl_warning("Solar object:%s is in player mode - be sure to specify relevant values",hdr->name);
00490
00491
00492
00493
00494
00495
00496 }
00497
00498 return 1;
00499 }
00500
00501
00502 int solar::init(OBJECT *parent)
00503 {
00504 OBJECT *obj = OBJECTHDR(this);
00505 int climate_result;
00506 gld_property *temp_property_pointer;
00507 gld_wlock *test_rlock;
00508 bool temp_bool_val;
00509 double temp_double_val, temp_inv_p_rated, temp_p_efficiency, temp_p_eta;
00510 double temp_double_div_value_eta, temp_double_div_value_eff;
00511 enumeration temp_enum_val;
00512 int32 temp_phase_count_val;
00513
00514 if (parent != NULL)
00515 {
00516 if((parent->flags & OF_INIT) != OF_INIT)
00517 {
00518 char objname[256];
00519 gl_verbose("solar::init(): deferring initialization on %s", gl_name(parent, objname, 255));
00520 return 2;
00521 }
00522 }
00523
00524 if (gen_mode_v == UNKNOWN)
00525 {
00526 gl_warning("Generator control mode is not specified! Using default: SUPPLY_DRIVEN");
00527 gen_mode_v = SUPPLY_DRIVEN;
00528 } else if(gen_mode_v == CONSTANT_V){
00529 gl_error("Generator control mode is CONSTANT_V. The Solar object only operates in SUPPLY_DRIVEN generator control mode.");
00530 return 0;
00531 } else if(gen_mode_v == CONSTANT_PQ){
00532 gl_error("Generator control mode is CONSTANT_PQ. The Solar object only operates in SUPPLY_DRIVEN generator control mode.");
00533 return 0;
00534 } else if(gen_mode_v == CONSTANT_PF){
00535 gl_error("Generator control mode is CONSTANT_PF. The Solar object only operates in SUPPLY_DRIVEN generator control mode.");
00536 return 0;
00537 }
00538 if (gen_status_v == UNKNOWN)
00539 {
00540 gl_warning("Solar panel status is unknown! Using default: ONLINE");
00541 gen_status_v = ONLINE;
00542 }
00543 if (panel_type_v == UNKNOWN)
00544 {
00545 gl_warning("Solar panel type is unknown! Using default: SINGLE_CRYSTAL_SILICON");
00546 panel_type_v = SINGLE_CRYSTAL_SILICON;
00547 }
00548 switch(panel_type_v)
00549 {
00550 case SINGLE_CRYSTAL_SILICON:
00551 if (efficiency==0.0)
00552 efficiency = 0.2;
00553
00554 if (Pmax_temp_coeff==0.0)
00555 Pmax_temp_coeff = -0.00437/33.8 ;
00556 if (Voc_temp_coeff==0.0)
00557 Voc_temp_coeff = -0.00393/33.8;
00558 break;
00559 case MULTI_CRYSTAL_SILICON:
00560 if (efficiency==0.0)
00561 efficiency = 0.15;
00562 if (Pmax_temp_coeff==0.0)
00563 Pmax_temp_coeff = -0.00416/33.8;
00564 if (Voc_temp_coeff==0.0)
00565 Voc_temp_coeff = -0.0039/33.8;
00566 break;
00567 case AMORPHOUS_SILICON:
00568 if (efficiency==0.0)
00569 efficiency = 0.07;
00570 if (Pmax_temp_coeff==0.0)
00571 Pmax_temp_coeff = 0.1745/33.8;
00572 if (Voc_temp_coeff==0.0)
00573 Voc_temp_coeff = -0.00407/33.8;
00574 break;
00575 case THIN_FILM_GA_AS:
00576 if (efficiency==0.0)
00577 efficiency = 0.3;
00578 break;
00579 case CONCENTRATOR:
00580 if (efficiency==0.0)
00581 efficiency = 0.15;
00582 break;
00583 default:
00584 if (efficiency == 0)
00585 efficiency = 0.10;
00586 break;
00587 }
00588
00589
00590
00591
00592 if (Max_P == 0) {
00593 Max_P = Rated_Insolation * efficiency * area;
00594 gl_verbose("init(): Rated_kVA was not specified. Calculating from other defaults.");
00595
00596
00597
00598
00599
00600 if(Max_P == 0) {
00601 gl_warning("init(): Rated_kVA or {area or rated insolation or effiency} were not specified or specified as zero. Leads to maximum power output of 0.");
00602
00603
00604
00605
00606 }
00607 }
00608 else {
00609 if (efficiency != 0 && area != 0 && Rated_Insolation != 0) {
00610 double temp = Rated_Insolation * efficiency * area;
00611
00612 if (temp < Max_P * .99 || temp > Max_P* 1.01) {
00613 Max_P = temp;
00614 gl_warning("init(): Rated_kVA, efficiency, Rate_Insolation, and area did not calculated to the same value. Ignoring Rated_kVA.");
00615
00616
00617
00618
00619
00620 }
00621 }
00622 }
00623
00624 if (Rated_Insolation == 0) {
00625 if (area != 0 && efficiency != 0)
00626 Rated_Insolation = Max_P / area / efficiency;
00627 else {
00628 gl_error("init(): Rated Insolation was not specified (or zero). Power outputs cannot be calculated without a rated insolation value.");
00629
00630
00631
00632
00633 }
00634 }
00635
00636 Rated_kVA = Max_P / 1000;
00637
00638
00639 if (parent != NULL)
00640 {
00641 if (gl_object_isa(parent,"inverter","generators") == true)
00642 {
00643
00644 inverter_voltage_property = new gld_property(parent,"V_In");
00645
00646
00647 if ((inverter_voltage_property->is_valid() != true) || (inverter_voltage_property->is_complex() != true))
00648 {
00649 GL_THROW("solar:%d - %s - Unable to map inverter power interface field",obj->id,(obj->name ? obj->name : "Unnamed"));
00650
00651
00652
00653
00654 }
00655
00656
00657 inverter_current_property = new gld_property(parent,"I_In");
00658
00659
00660 if ((inverter_current_property->is_valid() != true) || (inverter_current_property->is_complex() != true))
00661 {
00662 GL_THROW("solar:%d - %s - Unable to map inverter power interface field",obj->id,(obj->name ? obj->name : "Unnamed"));
00663
00664 }
00665
00666
00667 temp_property_pointer = new gld_property(parent,"use_multipoint_efficiency");
00668
00669
00670 if ((temp_property_pointer->is_valid() != true) || (temp_property_pointer->is_bool() != true))
00671 {
00672 GL_THROW("solar:%d - %s - Unable to map inverter property",obj->id,(obj->name ? obj->name : "Unnamed"));
00673
00674
00675
00676
00677 }
00678
00679
00680 temp_property_pointer->getp<bool>(temp_bool_val,*test_rlock);
00681
00682
00683 delete temp_property_pointer;
00684
00685
00686 temp_property_pointer = new gld_property(parent,"maximum_dc_power");
00687
00688
00689 if ((temp_property_pointer->is_valid() != true) || (temp_property_pointer->is_double() != true))
00690 {
00691 GL_THROW("solar:%d - %s - Unable to map inverter property",obj->id,(obj->name ? obj->name : "Unnamed"));
00692
00693 }
00694
00695
00696 temp_double_val = temp_property_pointer->get_double();
00697
00698
00699 delete temp_property_pointer;
00700
00701
00702 temp_property_pointer = new gld_property(parent,"inverter_type");
00703
00704
00705 if ((temp_property_pointer->is_valid() != true) || (temp_property_pointer->is_enumeration() != true))
00706 {
00707 GL_THROW("solar:%d - %s - Unable to map inverter property",obj->id,(obj->name ? obj->name : "Unnamed"));
00708
00709 }
00710
00711
00712 temp_enum_val = temp_property_pointer->get_enumeration();
00713
00714
00715 delete temp_property_pointer;
00716
00717
00718 temp_property_pointer = new gld_property(parent,"number_of_phases_out");
00719
00720
00721 if ((temp_property_pointer->is_valid() != true) || (temp_property_pointer->is_integer() != true))
00722 {
00723 GL_THROW("solar:%d - %s - Unable to map inverter property",obj->id,(obj->name ? obj->name : "Unnamed"));
00724
00725 }
00726
00727
00728 temp_phase_count_val = temp_property_pointer->get_integer();
00729
00730
00731 delete temp_property_pointer;
00732
00733
00734 temp_property_pointer = new gld_property(parent,"rated_power");
00735
00736
00737 if ((temp_property_pointer->is_valid() != true) || (temp_property_pointer->is_double() != true))
00738 {
00739 GL_THROW("solar:%d - %s - Unable to map inverter property",obj->id,(obj->name ? obj->name : "Unnamed"));
00740
00741 }
00742
00743
00744 temp_inv_p_rated = temp_property_pointer->get_double();
00745
00746
00747 delete temp_property_pointer;
00748
00749
00750 temp_property_pointer = new gld_property(parent,"efficiency_value");
00751
00752
00753 if ((temp_property_pointer->is_valid() != true) || (temp_property_pointer->is_double() != true))
00754 {
00755 GL_THROW("solar:%d - %s - Unable to map inverter property",obj->id,(obj->name ? obj->name : "Unnamed"));
00756
00757 }
00758
00759
00760 temp_p_efficiency = temp_property_pointer->get_double();
00761
00762
00763 delete temp_property_pointer;
00764
00765
00766 temp_property_pointer = new gld_property(parent,"inverter_efficiency");
00767
00768
00769 if ((temp_property_pointer->is_valid() != true) || (temp_property_pointer->is_double() != true))
00770 {
00771 GL_THROW("solar:%d - %s - Unable to map inverter property",obj->id,(obj->name ? obj->name : "Unnamed"));
00772
00773 }
00774
00775
00776 temp_p_eta = temp_property_pointer->get_double();
00777
00778
00779 delete temp_property_pointer;
00780
00781
00782 temp_double_div_value_eta = temp_inv_p_rated/temp_p_eta;
00783 temp_double_div_value_eff = temp_inv_p_rated/temp_p_efficiency;
00784
00785 if(temp_bool_val == TRUE)
00786 {
00787 if(Max_P > temp_double_val){
00788 gl_warning("The PV is over rated for its parent inverter.");
00789
00790
00791
00792
00793 }
00794 }
00795 else if(temp_enum_val == 4)
00796 {
00797 if(temp_phase_count_val == 4){
00798 if(Max_P > temp_double_div_value_eta){
00799 gl_warning("The PV is over rated for its parent inverter.");
00800
00801 }
00802 } else {
00803 if(Max_P > temp_phase_count_val*temp_double_div_value_eta){
00804 gl_warning("The PV is over rated for its parent inverter.");
00805
00806 }
00807 }
00808 } else {
00809 if(temp_phase_count_val == 4){
00810 if(Max_P > temp_double_div_value_eff){
00811 gl_warning("The PV is over rated for its parent inverter.");
00812
00813 }
00814 } else {
00815 if(Max_P > temp_phase_count_val*temp_double_div_value_eff){
00816 gl_warning("The PV is over rated for its parent inverter.");
00817
00818 }
00819 }
00820 }
00821
00822 }
00823 else
00824 {
00825 GL_THROW("Solar panel can only have an inverter as its parent.");
00826
00827
00828
00829 }
00830 }
00831 else
00832 {
00833 gl_warning("solar panel:%d has no parent defined. Using static voltages.", obj->id);
00834
00835
00836
00837
00838 inverter_voltage_property = new gld_property(obj,"default_voltage_variable");
00839
00840
00841 if ((inverter_voltage_property->is_valid() != true) || (inverter_voltage_property->is_complex() != true))
00842 {
00843 GL_THROW("solar:%d - %s - Unable to map a default power interface field",obj->id,(obj->name ? obj->name : "Unnamed"));
00844
00845
00846
00847
00848 }
00849
00850
00851 inverter_current_property = new gld_property(obj,"default_current_variable");
00852
00853
00854 if ((inverter_current_property->is_valid() != true) || (inverter_current_property->is_complex() != true))
00855 {
00856 GL_THROW("solar:%d - %s - Unable to map a default power interface field",obj->id,(obj->name ? obj->name : "Unnamed"));
00857
00858 }
00859
00860
00861 default_voltage_array = complex(V_Max.Re()/sqrt(3.0),0);
00862 }
00863
00864 climate_result=init_climate();
00865
00866
00867 if ((soiling_factor<0) || (soiling_factor>1.0))
00868 {
00869 soiling_factor = 0.95;
00870 gl_warning("Invalid soiling factor specified, defaulting to 95%%");
00871
00872
00873
00874
00875 }
00876
00877 if ((derating_factor<0) || (derating_factor>1.0))
00878 {
00879 derating_factor = 0.95;
00880 gl_warning("Invalid derating factor specified, defaulting to 95%%");
00881
00882
00883
00884
00885 }
00886
00887
00888 if ((obj->flags & OF_DELTAMODE) == OF_DELTAMODE)
00889 {
00890 deltamode_inclusive = true;
00891 }
00892
00893 if (deltamode_inclusive == true)
00894 {
00895
00896 if (enable_subsecond_models!=true)
00897 {
00898 gl_warning("solar:%s indicates it wants to run deltamode, but the module-level flag is not set!",obj->name?obj->name:"unnamed");
00899
00900
00901
00902
00903 }
00904 else
00905 {
00906 gen_object_count++;
00907 first_sync_delta_enabled = true;
00908 }
00909
00910
00911 if (gl_object_isa(parent,"inverter","generators"))
00912 {
00913
00914 if ((parent->flags & OF_DELTAMODE) != OF_DELTAMODE)
00915 {
00916 GL_THROW("solar:%d %s is attached to an inverter, but that inverter is not set up for deltamode",obj->id, (obj->name ? obj->name : "Unnamed"));
00917
00918
00919
00920
00921 }
00922
00923 }
00924 else
00925 {
00926 GL_THROW("solar:%d %s is not parented to an inverter -- deltamode operations do not support this",obj->id, (obj->name ? obj->name : "Unnamed"));
00927
00928
00929
00930 }
00931 }
00932 else
00933 {
00934 if (enable_subsecond_models == true)
00935 {
00936 gl_warning("solar:%d %s - Deltamode is enabled for the module, but not this solar array!",obj->id,(obj->name ? obj->name : "Unnamed"));
00937
00938
00939
00940
00941
00942 }
00943 }
00944
00945 return climate_result;
00946 }
00947
00948 TIMESTAMP solar::presync(TIMESTAMP t0, TIMESTAMP t1)
00949 {
00950 I_Out = complex(0.0,0.0);
00951
00952 TIMESTAMP t2 = TS_NEVER;
00953 return t2;
00954 }
00955
00956 TIMESTAMP solar::sync(TIMESTAMP t0, TIMESTAMP t1)
00957 {
00958 int64 ret_value;
00959 OBJECT *obj = OBJECTHDR(this);
00960 double insolwmsq, corrwindspeed, Tback, Ftempcorr;
00961 gld_wlock *test_rlock;
00962
00963
00964 if ((shading_factor <0) || (shading_factor > 1))
00965 {
00966 GL_THROW("Shading factor outside [0 1] limits in %s",obj->name);
00967
00968
00969
00970
00971 }
00972
00973 if (first_sync_delta_enabled == true)
00974 {
00975
00976 if ((deltamode_inclusive == true) && (enable_subsecond_models == true))
00977 {
00978 if ((gen_object_current == -1) || (delta_objects==NULL))
00979 {
00980
00981 allocate_deltamode_arrays();
00982 }
00983
00984
00985 if (gen_object_current>=gen_object_count)
00986 {
00987 GL_THROW("Too many objects tried to populate deltamode objects array in the generators module!");
00988
00989
00990
00991
00992
00993 }
00994
00995
00996 delta_objects[gen_object_current] = obj;
00997
00998
00999 delta_functions[gen_object_current] = (FUNCTIONADDR)(gl_get_function(obj,"interupdate_gen_object"));
01000
01001
01002 if (delta_functions[gen_object_current] == NULL)
01003 {
01004 GL_THROW("Failure to map deltamode function for device:%s",obj->name);
01005
01006
01007
01008
01009
01010 }
01011
01012
01013 post_delta_functions[gen_object_current] = NULL;
01014
01015
01016 delta_preupdate_functions[gen_object_current] = NULL;
01017
01018
01019 gen_object_current++;
01020
01021
01022 first_sync_delta_enabled = false;
01023 }
01024 else
01025 {
01026 first_sync_delta_enabled = false;
01027 }
01028 }
01029
01030
01031 if (solar_model_tilt != PLAYERVAL)
01032 {
01033
01034 if (weather != NULL)
01035 {
01036 wind_speed = pWindSpeed->get_double();
01037 Tamb = pTout->get_double();
01038 }
01039
01040
01041
01042 switch (orientation_type) {
01043 case DEFAULT:
01044 {
01045 if (weather == NULL)
01046 {
01047
01048
01049
01050 Insolation = 92.902 * (shading_factor + 1.0 + 0.1* (1.0 - cos(tilt_angle)));
01051 }
01052 else
01053 {
01054 ret_value = ((int64 (*)(OBJECT *, double, double, double, double, double *))(*calc_solar_radiation))(weather, RAD(tilt_angle), obj->latitude, obj->longitude, shading_factor, &Insolation);
01055 }
01056 break;
01057 }
01058 case FIXED_AXIS:
01059 {
01060
01061 ret_value = ((int64 (*)(OBJECT *, double, double, double, double, double, double *))(*calc_solar_radiation))(weather,RAD(tilt_angle),RAD(orientation_azimuth_corrected),obj->latitude,obj->longitude,shading_factor,&Insolation);
01062
01063
01064
01065 if (ret_value == 0)
01066 {
01067 GL_THROW("Calculation of solar radiation failed in %s",obj->name);
01068
01069
01070
01071
01072
01073 }
01074
01075 break;
01076 }
01077 case ONE_AXIS:
01078 case TWO_AXIS:
01079 case AZIMUTH_AXIS:
01080 default:
01081 {
01082 GL_THROW("Unknown or unsupported orientation detected");
01083
01084
01085
01086
01087 }
01088 }
01089 }
01090
01091
01092
01093 if (Insolation < 0.0){
01094 Insolation = 0.0;
01095 }
01096
01097 if (solar_power_model==BASEEFFICIENT)
01098 {
01099 Tambient = (Tamb-32.0)*5.0/9.0;
01100
01101 Tmodule = Tamb + (NOCT-68)/74.32 * Insolation;
01102
01103 VA_Out = Max_P * Insolation/Rated_Insolation *(1+(Pmax_temp_coeff)*( Tmodule-77))*derating_factor*soiling_factor;
01104 }
01105 else if (solar_power_model==FLATPLATE)
01106 {
01107
01108
01109
01110 if (prevTime != t0)
01111 {
01112 prevTemp = currTemp;
01113
01114 currTemp = (Tamb-32.0)*5.0/9.0;
01115
01116 prevTime = t0;
01117 }
01118
01119
01120
01121 Tambient = prevTemp + (currTemp-prevTemp)/2.0;
01122
01123
01124
01125 insolwmsq = Insolation*10.7639104*soiling_factor;
01126
01127
01128 corrwindspeed = wind_speed*0.44704;
01129
01130
01131 Tback = (insolwmsq*exp(module_acoeff + module_bcoeff*corrwindspeed) + Tambient);
01132
01133
01134 Tcell = Tback + insolwmsq/1000.0*module_dTcoeff;
01135
01136
01137 Tmodule=Tcell*9.0/5.0 + 32.0;
01138
01139
01140 Ftempcorr = 1.0 + module_Tcoeff*(Tcell-25.0)/100.0;
01141
01142
01143
01144 P_Out = Insolation*soiling_factor*derating_factor*(Max_P / Rated_Insolation)*Ftempcorr;
01145
01146
01147 VA_Out = P_Out;
01148 }
01149 else
01150 {
01151 GL_THROW("Unknown solar power output model selected!");
01152
01153
01154
01155
01156
01157 }
01158
01159 Voc = Voc_Max * (1+(Voc_temp_coeff)*(Tmodule-77));
01160
01161 V_Out = V_Max * (Voc/Voc_Max);
01162
01163 I_Out = (VA_Out/V_Out);
01164
01165
01166 inverter_voltage_property->setp<complex>(V_Out,*test_rlock);
01167 inverter_current_property->setp<complex>(I_Out,*test_rlock);
01168
01169 return TS_NEVER;
01170 }
01171
01172
01173 TIMESTAMP solar::postsync(TIMESTAMP t0, TIMESTAMP t1)
01174 {
01175 TIMESTAMP t2 = TS_NEVER;
01176
01177
01178 return t2;
01179 }
01180
01181
01182
01183 SIMULATIONMODE solar::inter_deltaupdate(unsigned int64 delta_time, unsigned long dt, unsigned int iteration_count_val)
01184 {
01185 double deltat, deltatimedbl, currentDBLtime;
01186 TIMESTAMP time_passin_value, ret_value;
01187
01188
01189 deltat = (double)dt/(double)DT_SECOND;
01190
01191 if (iteration_count_val == 0)
01192 {
01193
01194 deltatimedbl = (double)delta_time/(double)DT_SECOND;
01195
01196
01197 currentDBLtime = (double)gl_globalclock + deltatimedbl;
01198
01199
01200 time_passin_value = (TIMESTAMP)currentDBLtime;
01201
01202
01203
01204 ret_value = sync(time_passin_value,TS_NEVER);
01205 }
01206
01207
01208 return SM_EVENT;
01209 }
01210
01212
01214
01215 EXPORT int create_solar(OBJECT **obj, OBJECT *parent)
01216 {
01217 try
01218 {
01219 *obj = gl_create_object(solar::oclass);
01220 if (*obj!=NULL)
01221 {
01222 solar *my = OBJECTDATA(*obj,solar);
01223 gl_set_parent(*obj,parent);
01224 return my->create();
01225 }
01226 else
01227 return 0;
01228 }
01229 CREATE_CATCHALL(solar);
01230 }
01231
01232 EXPORT int init_solar(OBJECT *obj, OBJECT *parent)
01233 {
01234 try
01235 {
01236 if (obj!=NULL)
01237 return OBJECTDATA(obj,solar)->init(parent);
01238 else
01239 return 0;
01240 }
01241 INIT_CATCHALL(solar);
01242 }
01243
01244 EXPORT TIMESTAMP sync_solar(OBJECT *obj, TIMESTAMP t1, PASSCONFIG pass)
01245 {
01246 TIMESTAMP t2 = TS_NEVER;
01247 solar *my = OBJECTDATA(obj,solar);
01248 try
01249 {
01250 switch (pass) {
01251 case PC_PRETOPDOWN:
01252 t2 = my->presync(obj->clock,t1);
01253 break;
01254 case PC_BOTTOMUP:
01255 t2 = my->sync(obj->clock,t1);
01256 break;
01257 case PC_POSTTOPDOWN:
01258 t2 = my->postsync(obj->clock,t1);
01259 break;
01260 default:
01261 GL_THROW("invalid pass request (%d)", pass);
01262 break;
01263 }
01264 if (pass==clockpass)
01265 obj->clock = t1;
01266 }
01267 SYNC_CATCHALL(solar);
01268 return t2;
01269 }
01270
01271
01272 EXPORT SIMULATIONMODE interupdate_solar(OBJECT *obj, unsigned int64 delta_time, unsigned long dt, unsigned int iteration_count_val)
01273 {
01274 solar *my = OBJECTDATA(obj,solar);
01275 SIMULATIONMODE status = SM_ERROR;
01276 try
01277 {
01278 status = my->inter_deltaupdate(delta_time,dt,iteration_count_val);
01279 return status;
01280 }
01281 catch (char *msg)
01282 {
01283 gl_error("interupdate_solar(obj=%d;%s): %s", obj->id, obj->name?obj->name:"unnamed", msg);
01284 return status;
01285 }
01286 }
01287