/* ------------------------------------------------------------------------ */ /* */ /* DISPATCH.C */ /* LARRY CONRAD */ /* EE SPECIAL PROJECT COURSE */ /* FALL/WINTER 1990 */ /* */ /* ------------------------------------------------------------------------ */ #include #include #include #include #include #include #include #include "realtype.h" #include "gj.h" #include "mulreg.h" #include "curvefit.h" #include "worlddr.h" #include "segraph.h" /* #include "screen.h" for screen dumps */ /* #include "async2.h" for plotter */ #include "edit.h" #include "getkey.h" #include "userio.h" #include "dispatch.h" /* --------------------------- MAIN ROUTINE ------------------------------- */ /* */ /* MAIN HANDLES THE USER INTERFACE */ /* */ /* ------------------------------------------------------------------------ */ main() { int i; int c; char flag; strcpy( mp, "WELCOME! Most I/O will be on this line in green. Select a lesson."); /* print_all = 'Y'; */ /* GET THE CONFIGURATION DATA AS NEEDED */ c = get_config(); /* SET UP INITIAL VIDEO MODE */ InitSEGraphics(16, font_path, 0); SetScatMarkRelSize( scatter_mark_size ); /* Save original foreground, background, and text position. */ oldfgd = _settextcolor( _WHITE ); oldbgd = _setbkcolor( _RED ); oldpos = _gettextposition(); _clearscreen( _GCLEARSCREEN ); if( show_message("INTRO") == 'F') return ( 'F' ); c = any_key( 25, -1, prompt_color ); START_MENU: _settextcolor( _WHITE ); _setbkcolor( _RED ); _displaycursor( _GCURSOROFF ); _clearscreen( _GCLEARSCREEN ); /* PRINT MENU */ center_message( 1, "DISPATCH.C - By Larry Conrad" ); center_message( 3, "Use a L-etter key to make your choice" ); center_message( 5, "------------- LESSON MENU -------------" ); _settextposition(7,20); _outtext( "T-he Cost of Generation" ); _settextposition(8,20); _outtext( "G-eneral Requirements for Finding Minimum Cost" ); _settextposition(9,20); _outtext( "F-itting the Cost to an Equation" ); _settextposition(10,20); _outtext( "M-inimum Cost Concept with two Generators" ); _settextposition(11,20); _outtext( "D-ispatching Several Generators" ); _settextposition(12,20); _outtext( "A-ccounting for System Losses" ); _settextposition(13,20); _outtext( "I-terating to a Solution" ); _settextposition(14,20); _outtext( "S-imple Solution with Text Report" ); _settextposition(15,20); sprintf(buffer, "R-ead Data from a File. [%s]",genfile_buff); _outtext( buffer ); _settextposition(16,20); _outtext( "V-alid listing of files" ); _settextposition(17,20); _outtext( "E-dit or Create New Data" ); _settextposition(18,20); _outtext( "W-rite Data to a File" ); _settextposition(19,20); _outtext( "e-X-it This Program" ); _settextposition(20,20); sprintf(buffer, "L-earn messages displayed? %s", teach_mode == 'T' ? "[YES]" : "[NO]"); _outtext( buffer ); _settextposition(21,20); sprintf(buffer, "Z show intermediate steps %s", print_all == 'Y' ? "[YES]" : "[NO]"); _outtext( buffer ); /* PRINT CURRENT MESSAGE ON INPUT LINE & ASK USER TO SELECT A FUNCTION */ c = prompt( INROW, -1, prompt_color, mp ); switch (toupper(c)) { /* THE COST OF GENERATION */ case 'T': if( show_message( "THECOSTOF1" ) == 'F') return ( 'F' ); if( teach_mode == 'T') any_key( 25, -1, prompt_color ); if( show_message( "THECOSTOF2" ) == 'F') return ( 'F' ); if( teach_mode == 'T') any_key( 25, -1, prompt_color ); if( show_message( "THECOSTOF3" ) == 'F') return ( 'F' ); if( teach_mode == 'T') any_key( 25, -1, prompt_color ); strcpy( mp, "The Cost of Generation Finished normally."); goto START_MENU; /* FITTING THE COST TO AN EQUATION */ case 'F': if( show_message( "GENPOLY1" ) == 'F') goto START_MENU; if( teach_mode == 'T') any_key( 25, -1, prompt_color ); if( input_gen_poly( 0 ) == 'T' ) strcpy( mp, "Cost Fitting Finished Normally"); goto START_MENU; /* MINIMUM COST CONCEPT */ case 'M': c = teach_equal_lambda (); strcpy( mp, "All finished with learning about minimum cost"); goto START_MENU; /* GENERAL REQUIREMENTS */ case 'G': if( show_message( "GENERAL1" ) == 'F') goto START_MENU; if( teach_mode == 'T') any_key( 25, -1, prompt_color ); if( show_message( "GENERAL2" ) == 'F') goto START_MENU; if( teach_mode == 'T') any_key( 25, -1, prompt_color ); if( show_message( "GENERAL3" ) == 'F') goto START_MENU; if( teach_mode == 'T') any_key( 25, -1, prompt_color ); strcpy( mp, "\aGeneral Requirements is temporarily out of service"); goto START_MENU; /* ACCOUNTING FOR SYSTEM LOSSES */ case 'A': if( show_message( "LOSSES1" ) == 'F') goto START_MENU; if( teach_mode == 'T') any_key( 25, -1, prompt_color ); if( valid_data == 'T' ) { show_files( "*.LOS" ); get_loss_data(); } if( (line_data=='N') || (bus_data=='N') || (a_data=='N') ) { goto START_MENU; } c = update_loss_penalty(); if( c == 'T') { strcpy( mp, "New loss factors have been calculated"); up_to_date = 'F'; goto START_MENU; } goto START_MENU; /* DISPATCHING SEVERAL GENERATORS */ case 'D': /* LOAD UP A DATA SET IF NO VALID DATA IS LOADED */ _clearscreen( _GCLEARSCREEN ); if( show_message( "MAKEAGUESS" ) == 'F') goto START_MENU; c = prompt( 25, -1, prompt_color, "Would you like me to load suggested data Y/N?" ); if( toupper( c ) == 'Y' ) { strcpy( genfile_buff, "TEN.GEN"); c = get_gen_data(); } _clearscreen( _GCLEARSCREEN ); if (valid_data != 'T') { strcpy( mp, "\aNo Valid Data Loaded at This Time"); goto START_MENU; } c = prompt( 25, -1, prompt_color, "Press any key to set up your first guess" ); c = guess_a_lambda(); strcpy( mp, "Dispatching Several Generators Finished Normally"); goto START_MENU; /* ITERATING TO A SOLUTION */ case 'I': if (valid_data != 'T') { strcpy( mp, "\aNo Valid Data Loaded at This Time"); goto START_MENU; } /* DISPLAY MESSAGE & QUIT IF MESSAGE NOT FOUND */ _setbkcolor( _RED ); _settextcolor( _WHITE ); _clearscreen( _GCLEARSCREEN ); if( show_message( "BINARY1" ) == 'F') { goto START_MENU; } if( teach_mode == 'T') any_key( 25, -1, prompt_color ); if( show_message( "BINARY2" ) == 'F') { goto START_MENU; } if( teach_mode == 'T') any_key( 25, -1, prompt_color ); if( show_message( "BINARY3" ) == 'F') { goto START_MENU; } if( teach_mode == 'T') any_key( 25, -1, prompt_color ); if( show_message( "BINARY4" ) == 'F') { goto START_MENU; } if( teach_mode == 'T') any_key( 25, -1, prompt_color ); /* MAKE SURE WE ARE UP TO DATE */ calc_dfdp_constants(); flag = solve_a_case(); if( binary_solution() == 'T') { plot_iterations( lam_guess, num_guesses ); strcpy( mp, "Binary solution finished normally."); } /* NOW SHOW A SOLUTION USING THE SLOPE OF LAMBDA & THE ERROR */ _setbkcolor( _RED ); _settextcolor( _WHITE ); _clearscreen( _GCLEARSCREEN ); if( show_message( "BINARY5" ) == 'F') { goto START_MENU; } if( teach_mode == 'T') any_key( 25, -1, prompt_color ); c = slope_solution( .6 ); { plot_iterations( lam_guess, num_guesses ); strcpy( mp, "Slope solution finished normally."); } goto START_MENU; case 'E': valid_data = input_data(); up_to_date = 'F'; if( valid_data == 'T' ) strcpy( mp, "Input Data Complete"); if( valid_data == 'F' ) strcpy( mp, "Input Data Has Errors"); goto START_MENU; /* READ DATA FROM A FILE */ case 'R': /* REQUEST USER TO INPUT GENERATOR FILE NAME */ _clearscreen( _GCLEARSCREEN ); show_files( "*.GEN" );; _settextposition(24, 10); fflush( stdin ); sprintf( buffer, "Enter of generator data file name: " ); _outtext( buffer ); _displaycursor( _GCURSORON ); gets( genfile_buff ); _displaycursor( _GCURSOROFF ); /* consider passing the file name to this someday */ c = get_gen_data(); if( c == 'T') { valid_data = 'T'; up_to_date = 'T'; file_open = 'T'; strcpy( mp, "File loaded"); } if( c == 'F') { valid_data = 'F'; /* get_gen_data SETS THE ERROR MESSAGE */ } goto START_MENU; /* TOGGLE THE LEARNING SCREENS */ case 'L': if ( teach_mode == 'F') { teach_mode = 'T'; strcpy( mp, "Learn message screens WILL be shown."); } else { teach_mode = 'F'; strcpy( mp, "Learn message screens have been DISABLED."); } goto START_MENU; /* SOLVE A CASE */ case 'S': if (valid_data == 'T') { /* ALLOW MULTIPLE CASES TO BE RUN 'esc' KEY EXITS */ SOLVE_ANOTHER: _setbkcolor( _RED ); _settextcolor( _WHITE ); flag = solve_a_case(); if( flag == 'T') flag = binary_solution(); if( flag == 'T') print_solution(); if( flag == 'F') goto START_MENU; if( flag == 'N') goto START_MENU; /* WAIT FOR KEY HIT TO DO ANOTHER CASE */ _settextposition(INROW,INCOL); center_message( INROW, "Would you like to see a graph of" " this solution (Y/N)?"); if( toupper(getch()) == 'Y') { _setbkcolor( _BLACK ); _clearscreen( _GCLEARSCREEN ); display_unit_incrementals( lambda, 'N' ); } _settextposition(INROW,INCOL); center_message(INROW,"Press any key for another solution" " - ESC key exits." ); if( getch() == KEY_ESCAPE ) { strcpy( mp, "Solution routine ended normally"); goto START_MENU; } goto SOLVE_ANOTHER; } else { strcpy( mp, "\aNo Valid Data Loaded at This Time"); } goto START_MENU; /* UPDATE LOSS FACTORS */ case 'U': if( (line_data=='N') || (bus_data=='N') || (a_data=='N') ) { strcpy( mp, "\aNot enough data to update loss factors"); goto START_MENU; } c = update_loss_penalty(); if( c == 'T') { strcpy( mp, "New loss factors have been calculated"); up_to_date = 'F'; goto START_MENU; } goto START_MENU; case 'V': show_files( "*.GEN" );; c = any_key( 25, -1, prompt_color ); goto START_MENU; /* DISPLAY FILE LIST AND REQUEST WRITE DATA FILE NAME */ case 'W': show_files( "*.GEN" );; _settextposition( 24, 10 ); c = save_data(); if( c == 'S') { strcpy( mp, "Save to file was successful"); up_to_date = 'T'; file_open = 'F'; } else { strcpy( mp, "Save to file FAILED - Try a different name"); up_to_date = 'F'; } goto START_MENU; /* EXIT TO DOS */ case 'X': /* MAKE SURE DATA FILE IS UP TO DATE BEFORE QUITTING */ if( up_to_date == 'T' ) { break; } /* DATA IS NOT UP TO DATE - WHAT DO I DO? */ else { _clearscreen( _GCLEARSCREEN ); _settextposition( INROW, INCOL); printf("\aCurrent Data is not saved on disk - "); printf("Quit anyway Y?:"); c = getch(); if( (c == 'y') || (c == 'Y') ) { break; } strcpy( mp, "\aActive Data is not saved on disk."); goto START_MENU; } /* TOGGLE SEND INTERMEDIATE OUTPUT TO SCREEN */ case 'Z': if( print_all == 'N') { print_all = 'Y'; strcpy( mp, "Hit CTRL-PRTSCR to capture intermediate steps"); } else { print_all = 'N'; strcpy( mp, "Intermediate steps will be suppressed"); } goto START_MENU; /* DEFAULT */ default: strcpy( mp, "\aInvalid Key"); goto START_MENU; } /* Restore original foreground and background. */ _settextcolor( oldfgd ); _setvideomode( _DEFAULTMODE ); _setbkcolor( oldbgd ); _clearscreen( _GCLEARSCREEN ); _settextposition( oldpos.row, oldpos.col ); _displaycursor( _GCURSORON ); exit(0); } /* -------------------------- GET GEN DATA ------------------------------ */ /* ALLOW USER TO REQUEST DATA FROM A FORMATTED FILE */ /* CHECK FOR VALID FILE AND VALID DATA - CALCULATE Y MATRIX */ /* */ /* FOUR DATA BLOCKS ARE READ. 1) GENERATOR COST AND LIMITS */ /* 2) LINE IMPEDANCE AND CONNECTIVITY */ /* 3) BUS VOLTAGES */ /* 4) LOSS COEFFECIENTS FOR EACH BUS */ /* */ /* FILE STRUCTURE ASSUMES ALL GENERATORS ARE AVAILABLE FOR DISPATCH */ /* */ /* RETURNS: T-rue */ /* F-alse */ /* ALSO SETS VALID DATA FLAG ACCORDINGLY */ /* ----------------------------------------------------------------------- */ char get_gen_data(void) { int j, k, i, n; /* Y(j,k) LINE(i) GEN-n */ valid_data = 'T'; /* ASSUME DATA IS GOOD-WE'LL FIND ERRORS */ if( (genfile = fopen( genfile_buff, "rt" )) == NULL ) { /* File open failed */ strcpy( mp, "\aUnable to open data file!"); return( 'F' ); } /* File opened successfully - read data */ fscanf( genfile, "%i\n", &num_gen); if( print_all == 'Y' ) { printf( "\n%i\n", num_gen); } if( num_gen > MAXGEN) { strcpy( mp,"\aToo many Generators. The limit is MAXGEN"); return( 'F' ); } for( i = 0; i < num_gen; i++) { fscanf( genfile, "%f%f%f%f%f%f%f%f%f\n", &gen_name[i], &min_out[i], &max_out[i], &fuel_cost[i], &h_3[i], &h_2[i], &h_1[i], &h_0[i], &loss_dpdl[i]); /* OPTIONAL DUMP OF DATA BACK OUT */ if( print_all == 'Y' ) { printf( "%5.1f%7.1f%7.1f%8.4f%11.6f%8.4f%8.3f%8.2f%8.4f\n", gen_name[i], min_out[i], max_out[i], fuel_cost[i], h_3[i], h_2[i], h_1[i], h_0[i], loss_dpdl[i]); } /* BASIC DATA ERROR CHECKING HERE */ if( check_gen_data( i ) == 'F' ) return( 'F' ); /* SET REGULATING FLAGS TO 'R' ------------------------------------ */ gen_status[i] = 'R'; } return( 'T' ); } /* -------------------------- INPUT DATA ---------------------------------- */ /* ALLOW USER TO LOAD DATA FROM SCRATCH OR TO EDIT A FILE */ /* VALIDATE DATA AS IT IS BEING LOADED */ /* RETURNS T-rue - This function worked */ /* F-alse - This function failed */ /* ALSO SETS EXTERNAL VALID DATA FLAG */ /* ------------------------------------------------------------------------ */ char input_data(void) { char c; /* CHARACTER */ char line[81]; /* LINE OF TEXT */ int i; /* COUNTER */ int count; /* GRAPHICS POSITION COUNTER */ int start_pos; /* STARTING POSITION OF EDIT */ int end_pos; /* POSITION WHERE EDIT ENDED */ unsigned ret_key; /* KEY RETURNED FROM EDITLINE */ char flag; /* T-rue F-alse FLAG */ /* SET THE MAXIMUM TO MAXGEN IF WE DON'T HAVE VALID DATA */ if( valid_data == 'F' ) num_gen = MAXGEN; _clearscreen( _GCLEARSCREEN ); flag = 'F'; /* LOOP THROUGH THIS GENERATOR UNTIL WE GET GOOD DATA */ _settextposition( 1, 1); _outtext( "Input data until complete. No more than 10 generators" " allowed"); _settextposition( 2, 1); _outtext( "Enter '999' in Gen Name to signal end of data"); _settextposition( 4, 1); _outtext( " NUM MINOUT MAXOUT FUEL_C P^3 P^2 P" " NL COST DP/DL"); /* PRINT OUT THE DATA POINTS FOR EDITING */ for( i = 0; i < MAXGEN; i++) { gen_status[i] = 'R'; /* SET NUMGEN TO 999 */ if( i == num_gen ) gen_name[i] = 999.; _settextposition( 6 + i, 0 ); sprintf( line, "%5.1f%7.1f%7.1f%8.4f%11.6f%8.4f%8.3f%8.2f%8.4f\n", gen_name[i], min_out[i], max_out[i], fuel_cost[i], h_3[i], h_2[i], h_1[i], h_0[i], loss_dpdl[i]); _outtext( line ); } /* LOOP THROUGH ADDING DATA POINTS UNTIL FINISHED */ i = 0; ret_key = 0; start_pos = 0; while( (ret_key != KEY_ENTER) && (ret_key != KEY_ESCAPE) ) { _settextposition( 6 + i, 0 ); sprintf( line, "%5.1f%7.1f%7.1f%8.4f%11.6f%8.4f%8.3f%8.2f%8.4f\n", gen_name[i], min_out[i], max_out[i], fuel_cost[i], h_3[i], h_2[i], h_1[i], h_0[i], loss_dpdl[i]); _displaycursor( _GCURSORON ); ret_key = editline( line, start_pos, &end_pos ); _displaycursor( _GCURSOROFF ); start_pos = end_pos; sscanf( line, "%f%f%f%f%f%f%f%f%f", &gen_name[i], &min_out[i], &max_out[i], &fuel_cost[i], &h_3[i], &h_2[i], &h_1[i], &h_0[i], &loss_dpdl[i]); /* SEE IF WE ARE DONE */ if( gen_name[i] == 999 ) { num_gen = i; return( flag ); } /* PRINT THE CURRENT UNIT NUMBER THAT WAS CHECKED */ sprintf( buffer, "Checking Generator%5.1f", gen_name[i] ); center_message( 24, buffer ); /* CHECK FOR ERRORS -PRINT MESSAGE FOR ERRORS */ if( check_gen_data( i ) == 'T' ) { center_message( 25, "Unit Data is OK" ); flag = 'T'; /* MOVE AROUND DEPENDING ON KEY PRESS */ switch( ret_key ) { case KEY_UP: if( i > 0 ) i--; break; case KEY_DOWN: if( i < MAXGEN - 1 ) i++; break; default: break; } } else { /* ISSUE BAD DATA MESSAGE AND STAY ON THAT LINE */ center_message( 25, mp ); flag = 'F'; } } return( flag ); } /* -------------------------- SAVE DATA ---------------------------------- */ /* ALLOW USER TO SAVE DATA TO A FILE */ /* */ /* */ /* ----------------------------------------------------------------------- */ char save_data(void) { FILE *genfile; int i; /* COUNTER */ char flag = 'F'; /* FLAG FOR SUCCESSUL SAVE */ /* REQUEST USER TO INPUT GENERATOR FILE NAME */ fflush( stdin ); sprintf( buffer, "Save generator data to file named: " ); _outtext( buffer ); _displaycursor( _GCURSORON ); gets( genfile_buff ); _displaycursor( _GCURSOROFF ); if( (genfile = fopen( genfile_buff, "wt" )) == NULL ) { /* FILE OPEN FAILED - EXIT */ fclose( genfile ); return( 'F'); } /* File opened successfully - save data */ fprintf(genfile, "%4i\n", num_gen); for( i = 0; i < num_gen; i++) { fprintf( genfile, "%5.1f%7.1f%7.1f%8.4f%11.6f%8.4f%8.3f%8.2f%8.4f\n", gen_name[i], min_out[i], max_out[i], fuel_cost[i], h_3[i], h_2[i], h_1[i], h_0[i], loss_dpdl[i]); } fclose( genfile ); return( 'S' ); } /* ---------------------------- SHOW_DATA ------------------------------- */ /* GIVEN VALID DATA, ECHO THE GENERATOR CAPABILITIES AND COST FACTORS */ /* BACK TO THE USER. CALCULATE MINIMUM AND MAXIMUM FEASIBLE LOADS. */ /* REQUIRES KNOWLEDGE OF useloss VALUE TO USE LOSSES OR NOT */ /* ----------------------------------------------------------------------- */ void show_data(void) { char c; char flag; /* ERROR FLAG */ int i, n; /* COUNTER */ /* RESET THE FEASABILITY COUNTERS EACH TIME WE ENTER THE SOLUTION */ /* OTHERWISE WE WILL MISCALCULATE FEASABLE LIMITS */ maximum_feasable = 0; minimum_feasable = 0; /* ECHO THE DATA BACK OUT AND ASK FOR LOAD TO MEET */ _clearscreen( _GCLEARSCREEN ); _settextposition(OUTROW, OUTCOL); sprintf(buffer,"%i Generators, %i busses, %i lines in Data file: %s\n", num_gen, num_bus, num_line, genfile_buff); _outtext( buffer ); sprintf( buffer, "Losses included ? %s\n", useloss == 'Y' ? "[YES]" : "[NO]"); _settextposition( OUTROW + 1, OUTCOL ); _outtext( buffer ); _settextposition( 4, 1); _outtext( " NUM MINOUT MAXOUT FUEL_C P^3 P^2 P" " NL COST DP/DL"); /* PRINT OUT THE DATA POINTS FOR EDITING */ for( i = 0; i < num_gen; i++) { /* CALCULATE FEASABLE LOADS WHILE ECHOING DATA */ /* Dispatchable units have the full range of output capability */ if( (gen_status[i] == 'R') || (gen_status[i] == 'H') || (gen_status[i] == 'L') ) { maximum_feasable = maximum_feasable + max_out[i]; minimum_feasable = minimum_feasable + min_out[i]; /* RESET FLAGS TO 'R' SINCE WE ARE STARTING A NEW CASE */ /* NOTE: THIS ONLY RESETS DISPATCHABLE GENERATOR FLAGS */ gen_status[i] = 'R'; sprintf( buffer, "%5.1f%7.1f%7.1f%8.4f%11.6f" "%8.4f%8.3f%8.2f%8.4f\n", gen_name[i], min_out[i], max_out[i], fuel_cost[i], h_3[i], h_2[i], h_1[i], h_0[i], loss_dpdl[i]); } /* Fixed output units have only one level of output */ if( gen_status[i] == 'F' ) { maximum_feasable = maximum_feasable + gen_load[i]; minimum_feasable = minimum_feasable + gen_load[i]; sprintf( buffer, "%5.1f%7.1f%7.1f This unit output is held!", gen_name[i], gen_load[i], gen_load[i]); } /* Check Units that are off are off so say so */ if( gen_status[i] == 'O' ) { sprintf( buffer, "%5.1f " "This unit is scheduled off!", gen_name[i]); } _settextposition( 6 + i, 0 ); _outtext( buffer ); } /* PRINT OUT FEASABLE LIMITS FOR USER AND REQUEST INPUT DATA */ sprintf( buffer, "Minimum feasable = %8.1f MW " "Maximum feasable = %8.1f MW", minimum_feasable, maximum_feasable); _settextposition(INROW - 3, INCOL); _outtext( buffer ); } /* ---------------------------- SOLVE_A_CASE ----------------------------- */ /* */ /* GIVEN VALID DATA, ECHO THE GENERATOR CAPABILITIES AND COST FACTORS */ /* BACK TO THE USER AND REQUEST A LOAD LEVEL TO DISPATCH. CHECK */ /* FEASABILITY. RETURNS 'T' IF FEASIBLE */ /* */ /* REQUIRES KNOWLEDGE OF useloss VALUE TO USE LOSSES OR NOT */ /* ----------------------------------------------------------------------- */ char solve_a_case(void) { int i, n; /* COUNTER */ char c; char flag; /* ERROR FLAG */ char line[8]; /* INPUT LINE STRING */ unsigned ret_key; /* KEY RETURNED FROM EDITLINE */ int start_pos; /* STARTING POSITION OF EDIT */ int end_pos; /* POSITION WHERE EDIT ENDED */ /* UPDATE DISPATCH CONSTANTS */ calc_dfdp_constants(); /* ECHO THE DATA BACK OUT AND ASK FOR LOAD TO MEET */ show_data(); _settextposition( INROW, INCOL ); _outtext( "Enter a value for the system load: " ); /* SET UP THE INPUT LINE AND EDIT THE LINE */ while( (ret_key != KEY_ENTER) && (ret_key != KEY_ESCAPE) ) { _settextposition( INROW, INCOL + 34 ); strcpy( line, " 000.0 " ); ret_key = 0; _displaycursor( _GCURSORON ); ret_key = editline( line, 2, &end_pos ); _displaycursor( _GCURSOROFF ); } /* LEAVE IF THE USER TRIED TO ESCAPE */ if( ret_key == KEY_ESCAPE ) { strcpy( mp, "Solution terminated by user."); return( 'N' ); } /* READ THE INPUT LINE */ sscanf( line, "%f", &system_load); /* FEASABILITY CHECK - STOP PROCESSING IF NOT FEASABLE */ if((system_load > maximum_feasable) || (system_load < minimum_feasable)) { sprintf( mp, "NOT FEASABLE - %7.1f MW Load is outside system limits.", system_load); return( 'N' ); } /* PREVENT PROBLEMS FROM ZERO LOAD */ if( system_load == 0 ) { strcpy( mp, "\aZERO LOAD -- BLACKOUT IS AT HAND!"); return( 'N' ); } /* DATA IS OK. CALL THE DISPATCH */ return('T'); } /* ------------------------ CHECK_GEN_DATA ------------------------------- */ /* */ /* CHECK TO SEE THAT THE DATA FOR THIS GENERATOR IS GOOD */ /* */ /* ----------------------------------------------------------------------- */ char check_gen_data( int unitno ) { float dlam_dp_min; /* SLOPE OF LAMBDA AT Pmin */ float dlam_dp_max; /* SLOPE OF LAMBDA AT Pmax */ float dlam_zero; /* POINT WHERE dLAMBDA IS AERO */ /* MAKE SURE MAXIMUM OUTPUT IS GREATER THAN THE MINIMUM & MIN > ZERO */ if( (max_out[unitno] <= 0) || (min_out[unitno] <= 0) || (min_out[unitno] > max_out[unitno])) { strcpy( mp, "\aInvalid generator output limits"); return( 'F' ); } /* MAKE SURE THE FUEL COST IS POSITIVE */ if( fuel_cost[unitno] < 0 ) { strcpy( mp, "\aFuel cost must be greater than zero"); return( 'F'); } /* CALCULATE THE SLOPE OF THE INCREMENTAL COST AT MIN & MAX OUTPUT */ dlam_dp_min = 6 * h_3[unitno] * min_out[unitno] + 2 * h_2[unitno]; dlam_dp_max = 6 * h_3[unitno] * max_out[unitno] + 2 * h_2[unitno]; /* OPTIONAL PRINTOUT */ if( print_all == 'Y' ) printf( "unit number = %3i slope at min=%9.5f at max=%9.5f\n", unitno, dlam_dp_min, dlam_dp_max); /* CHECK THE COEFFECIENTS FOR POSITIVE SLOPE AT THE MIN & MAX LIMITS */ if( (dlam_dp_min <= 0) || (dlam_dp_max <= 0) ) { strcpy( mp, "\aNegative or zero lambda at the generator output limits."); return( 'F' ); } /* FIND WHERE SLOPE OF LAMBDA = 0; MUST BE OUTSIDE Pmin Pmax */ if( h_3[unitno] != 0 ) { dlam_zero = - h_2[unitno] / 3 / h_3[unitno]; /* OPTIONAL PRINTOUT */ if( print_all == 'Y' ) printf(" zero lambda at %9.2f MW\n", dlam_zero); if( (dlam_zero > min_out[unitno]) && (dlam_zero < max_out[unitno])) { strcpy( mp, "\aLambda is Zero within the generator output limits"); return( 'F' ); } } /* WE MADE IT */ return( 'T' ); } /* ------------------------CALC DF/DP CONSTANTS---------------------------- */ /* */ /* TAKE THE FIRST DERIVATIVE OF THE COST FUNCTION AND CALCULATE THE */ /* RESULTING QUADRATIC CONSTANTS TO DEVELOP AN EQUATION FOR GENEROTOR */ /* OUTPUT AS A FUNCTION OF LAMBDA, THE INCREMENTAL COST. */ /* */ /* ALSO CALCULATES MINIMUM AND MAXIMUM LAMBDAS OF THE GENERATORS ON LINE */ /* */ /* ------------------------------------------------------------------------ */ void calc_dfdp_constants (void) { int i; /* COUNTER */ float loss, min, max; /* CALCULATE THE FIRST GENERATOR SO WE HAVE SOME INITIAL CONDITIONS */ /* FOR MINIMUM & MAXIMUM VALUES OF LAMBDA BEFORE CALCULATING THE REST */ if(useloss == 'N') loss = 0; /* CALCULATE THE CONSTANT AND Pi VALUES FOR THE GENERATORS */ for( i = 0; i < num_gen; i++) { loss = loss_dpdl[i]; /* CALCULATION DEPENDS ON IF WE WANT TO USE LOSSES OR NOT */ if(useloss == 'N') loss = 0; dfdp_0[i] = h_1[i] * fuel_cost[i] * (1/(1-loss)); dfdp_1[i] = 2 * h_2[i] * fuel_cost[i] * (1/(1-loss)); dfdp_2[i] = 3 * h_3[i] * fuel_cost[i] * (1/(1-loss)); /* CALCULATE MAX AND MIN POSSIBLE LAMBDA */ gen_min_lambda[i] = dfdp_2[i] * min_out[i] * min_out[i] + dfdp_1[i] * min_out[i] + dfdp_0[i]; gen_max_lambda[i] = dfdp_2[i] * max_out[i] * max_out[i] + dfdp_1[i] * max_out[i] + dfdp_0[i]; /* optional printout */ if( print_all == 'Y') { printf( "\nMax=%6.2f Min=%6.2f d0=%10.5f d1=%10.5f d2=%10.5f", gen_max_lambda[i], gen_min_lambda[i], dfdp_0[i], dfdp_1[i], dfdp_2[i]); } } /* FIND THE MINIMUM AND MAXIMUMS FOR LAMBDA */ min_lambda = gen_min_lambda[0]; max_lambda = gen_max_lambda[0]; for( i = 1; i < num_gen; i++) { if( gen_max_lambda[i] > max_lambda ) max_lambda = gen_max_lambda[i]; if( gen_min_lambda[i] < min_lambda ) min_lambda = gen_min_lambda[i]; } } /* ------------------------- CALC_OUTPUT --------------------------------- */ /* */ /* CALCULATE THE GENERATOR OUTPUT GIVEN AN ESTIMATE FOR THE SYSTEM */ /* LAMBDA. THE RESPONSIBILITY OF THIS ROUTINE IS TO CALCULATE: */ /* gen_load[i] */ /* */ /* IT COULD ALSO CALCULATE THE GENERATOR LAMBDA gen_lambda[] but THAT */ /* WOULD SLOW THE ROUTINE DOWN TOO MUCH. */ /* */ /* ----------------------------------------------------------------------- */ char calc_output (void) { int i; /* COUNTERS */ int n; /* SET NET OUTPUT TO ZERO */ net_out = 0; /* LOOP THROUGH ALL OF THE THE GENERATORS */ for( i = 0; i < num_gen; i++ ) { while( (gen_status[i] != 'O') && (gen_status[i] != 'F') ) { /* SET ALL DISPATCHABLE GENERATORS TO REGULATING */ gen_status[i] = 'R'; /* LOCK GENERATOR LOW IF LAMBDA IS < MINIMUM */ if( lambda < gen_min_lambda[i] ) { gen_load[i] = min_out[i]; gen_status[i] = 'L'; break; } /* LOCK GENERATOR HIGH IF LAMBDA IS > MAXIMUM */ if( lambda > gen_max_lambda[i] ) { gen_load[i] = max_out[i]; gen_status[i] = 'H'; break; } /* CALCULATE THE LOAD - USE A DIFFERENT EQUATION DEPENDING */ /* ON THE STRUCTURE OF THE COST FUNCTION */ if( (dfdp_2[i] != 0) && (gen_status[i] == 'R')) { /* DF/DP IS QUADRATIC & WE KNOW WHICH ROOT IS CORRECT */ gen_load[i] = ( - dfdp_1[i] + sqrt( dfdp_1[i] * dfdp_1[i] - 4 * dfdp_2[i] * ( dfdp_0[i] - lambda ) ) ) / 2 / dfdp_2[i]; break; } else { /* DF/DP IS LINEAR & dfdp_1[] CAN'T BE ZERO */ if( gen_status[i] == 'R') gen_load[i] = ( lambda - dfdp_0[i] ) / dfdp_1[i]; break; } } /* ADD UP NET OUTPUT FOR GENERATORS ON LINE */ if( gen_status[i] != 'O' ) net_out = net_out + gen_load[i]; } /* optional printout */ if( print_all == 'Y') { printf( "\nIL=%6.3f%5.0f", lambda, net_out); for( n = 0; n < num_gen; n++) { printf( "%5.0f%c", gen_load[n], gen_status[n]); } } } /* ------------------------- BINARY SOLUTION ----------------------------- */ /* THIS METHOD STARTS MIDWAY BETWEEN THE POSSIBLE RANGE OF LAMBDAS FOR */ /* THE CASE. EACH ITERATION MOVES HALF THE DISTANCE OF THE LAST ONE. */ /* ----------------------------------------------------------------------- */ char binary_solution(void) { char solved; /* SOLUTION FLAG */ int i; /* COUNTER */ float del_lambda; /* LAMBDA INCREMENT */ float tolerance; /* SOLUTION TOLERANCE */ float mw_error; /* MW ERROR */ /* START WITH LAMBDA AT THE MIDDLE */ lambda = (max_lambda + min_lambda) / 2; /* START THE INCREMENT AT 1/4 THE RANGE OF LAMBDAS */ del_lambda = (max_lambda - min_lambda) / 4; /* SET UP ITERATION VARIABLES */ solved = 'F'; tolerance = TOL; /* TOL is defined by a compiler directive */ i = 0; while( (i < MAXITERATIONS) && (solved == 'F') ) { /* CALCULATE THE OUTPUT FOR THIS GUESS */ calc_output(); /* LOG THE QUANTITIES FOR PLOTTING */ mw_error = net_out - system_load; lam_guess[i] = lambda; num_guesses = i + 1; if( print_all == 'Y' ) printf( "\n MW Error = %12.6f", mw_error); /* SEE IF WE MET THE SYSTEM LOAD */ if( fabs(mw_error) < tolerance) { /* WE FOUND A SOLUTION */ solved = 'T'; } else { /* NO GOOD - TRY AGAIN */ if( mw_error < 0 ) { /* RATCHET UP ONE INCREMENT IF WE ARE SHORT */ lambda = lambda + del_lambda; } if( mw_error > 0 ) { /* RATCHET DOWN ONE INCREMENT IF WE HAVE EXCESS */ lambda = lambda - del_lambda; } /* TRUE BINARY - HALVE THE INCREMENT FOR THE NEXT ITERATION */ del_lambda = del_lambda / 2; i++; } } return( solved ); } /* -------------------------- SLOPE_SOLUTION ----------------------------- */ /* */ /* THIS METHOD STARTS AT THE MIDDLE AND WORKS TILL IT MAKES IT */ /* IT IS BASICALLY BARE TO SHOW FAST SOLUTIONS Returns T-rue F-ailed */ /* */ /* ----------------------------------------------------------------------- */ char slope_solution( float acceleration ) /* Iteration Acceleration Factor */ { char c; /* CHARACTER */ char solved; /* SOLUTION FLAG */ int i; /* COUNTER */ float lambda_slope; /* SLOPE OF LAMBDA IN LAMBDA PER MW */ float prev_lambda; /* PREVIOUS VALUE OF LAMBDA */ float prev_load; /* PREVIOUS LOAD LEVEL */ float tolerance; /* SOLUTION TOLERANCE */ float mw_error; /* MW ERROR */ /* CALCULATE THE SLOPE OF LAMBDA PER MW */ lambda_slope = (max_lambda - min_lambda) / (maximum_feasable - minimum_feasable); /* ASSUME THE FIRST GUESS WAS THE MINIMUM LAMBDA */ mw_error = system_load - minimum_feasable; prev_load = minimum_feasable; prev_lambda = min_lambda; /* SET UP ITERATION VARIABLES */ solved = 'F'; tolerance = TOL; i = 0; while( (i < MAXITERATIONS) && (solved == 'F') ) { /* MAKE A GUESS FOR LAMBDA AND CALCULATE THE OUTPUT */ lambda = lambda + (mw_error * acceleration * lambda_slope); calc_output(); /* LOG THE QUANTITIES FOR PLOTTING (Opposite of Binary ) */ mw_error = system_load - net_out; lam_guess[i] = lambda; num_guesses = i + 1; if( print_all == 'Y' ) printf( "\n MW Error = %12.6f", mw_error); /* SEE IF WE MET THE SYSTEM LOAD */ if( fabs(mw_error) < tolerance) { /* WE FOUND A SOLUTION */ solved = 'T'; } else { /* NO GOOD - TRY AGAIN */ if( net_out == prev_load ) /* Get out before it blows up */ return( 'F' ); lambda_slope = fabs( lambda - prev_lambda)/ fabs( net_out - prev_load ); prev_lambda = lambda; prev_load = net_out; /* CALCULATE A NEW SLOPE FOR LAMBDA */ i++; } } return( solved ); } /* ------------------------- CALC ENERGY COST ----------------------------- */ /* */ /* CALCULATE UNIT OUTPUT COST AND ENERGY COST EXCLUDING TRANSMISSION LOSS */ /* */ /* ADD PENALTY FACTOR FOR TOTAL LOADED COST */ /* */ /* ------------------------------------------------------------------------ */ void calc_energy_cost (void) { int i; /* COUNTER */ for( i = 0; i < num_gen; i++) { gen_output_cost[i] = fuel_cost[i] * ( h_0[i] + h_1[i] * gen_load[i] + h_2[i] * gen_load[i] * gen_load[i] + h_3[i] * gen_load[i] * gen_load[i] * gen_load[i] ); if( gen_status[i] == 'O') gen_output_cost[i] = 0; if( useloss == 'Y') { gen_output_cost[i] = gen_output_cost[i] * 1/(1-loss_dpdl[i]); } /* Avoid dividing by zero when gen_load is zero */ if( gen_load[i] > 0 ) { gen_energy_cost[i] = gen_output_cost[i] / gen_load[i]; } else { gen_energy_cost[i] = 0; } } } /* ---------------------- PRINT SOLUTION --------------------------------- */ /* */ /* WE ALREADY KNOW THIS IS A GOOD CASE - THIS JUST PRINTS IT. */ /* */ /* RETURNS: NOTHING */ /* */ /* ----------------------------------------------------------------------- */ void print_solution(void) { int i; int largest_unit_num = 0; /* LARGEST UNIT NUMBER */ int largest_load_num = 0; /* LARGEST LOADED UNIT NUMBER */ float largest_unit = 0; /* LARGEST UNIT */ float largest_load = 0; /* LARGEST LOADED UNIT */ float reserves; /* SPINNING RESERVES */ float system_hourly_cost = 0; /* TOTAL ENERGY COST OF SYSTEM IN $ */ float net_output = 0; /* SYSTEM OUTPUT IN MW */ float system_energy_cost = 0; /* ENERGY COST IN $ / MWh */ /* CLEAR SCREEN AND PRINT HEADER */ _clearscreen( _GCLEARSCREEN ); _settextposition(OUTROW,OUTCOL); printf( "%s System Lambda = %8.4f", genfile_buff, lambda); printf(" %s", useloss == 'Y' ? "OUTPUT COSTS ARE PENALIZED" : "No losses case."); calc_energy_cost(); printf( "\n\n GEN STATUS LOAD OUTPUT COST ENERGY COST\n" ); printf( " --- ------ ----- ----------- -----------\n" ); /* LOOP THROUGH EACH GENERATOR PRINTING DATA */ for( i = 0; i < num_gen; i++) { system_hourly_cost = system_hourly_cost + gen_output_cost[i]; net_output = net_output + gen_load[i]; printf( "%3.0f %c %6.1f %7.1f %7.3f\n", gen_name[i], gen_status[i], gen_load[i], gen_output_cost[i], gen_energy_cost[i]); /* CHECK FOR LARGEST UNIT AND UNIT WITH THE MOST LOAD */ if( largest_load < gen_load[i] ) { largest_load = gen_load[i]; largest_load_num = i; } if( largest_unit < max_out[i] ) { largest_unit = max_out[i]; largest_unit_num = i; } } /* CALCULATE SYSTEM ENERGY COST AND PRINT TOTALS */ system_energy_cost = system_hourly_cost / net_output; reserves = maximum_feasable - system_load; printf( " ------- -------- -------" ); printf( "\nTOTALS %11.1f%12.1f%12.3f", net_output, system_hourly_cost, system_energy_cost); _settextposition( INROW - 4, INCOL ); printf( "%7.1f MW Total Spinning Reserves", reserves); printf( "\n%7.1f MW Reserves after losing the largest unit. (%3.0f)", reserves - max_out[largest_unit_num], gen_name[largest_unit_num]); printf("\n%7.1f MW Reserves after loss of largest loaded unit. (%3.0f)", reserves - max_out[largest_load_num], gen_name[largest_load_num]); } /* ----------------------- UPDATE LOSS PENALTY --------------------------- */ /* */ /* UPDATES dPl/dPn FROM LOAD FLOW DATA */ /* */ /* INPUT: REQUIRES GLOBAL VARIABLE loss_dpdl[n] FOR EACH GENERATOR */ /* */ /* RETURNS: T-rue ALSO REQUIRES Y BUS DATA, VOLTAGE DATA & A[j][n] */ /* F-alse */ /* ----------------------------------------------------------------------- */ char update_loss_penalty(void) { int n, j, k; /* STANDARD POINTERS FOR GEN & BUSSES */ char c; /* CHARACTER */ /* Calculate dPloss / dTHETA */ for( j = 0; j < num_bus; j++) { dpl_dtheta[j] = 0; for( k = 0; k < num_bus; k++) { dpl_dtheta[j] = dpl_dtheta[j] + v[j] * v[k] * y[j][k].x * sin((theta[k] - theta[j]) /180 *3.14159); } } /* Calculate dPloss / dPgen load = loss_dpdl */ for( n = 0; n < num_gen; n++) { loss_dpdl[n] = 0; for( j = 0; j < num_bus; j++) { loss_dpdl[n] = loss_dpdl[n] + dpl_dtheta[j] * a[j][n]; } /* JUST MAKE SURE loss_dpdl is less than 1 */ if( loss_dpdl[n] >= 1 ) { strcpy( mp, "\aMore than one MW of Loss per MW increase on generator."); return( 'F' ); } } /* WE DID IT! LETS GET OUT OF HERE */ return( 'T' ); } /* ------------------------ plot_gen_poly--------------------------------- */ /* */ /* DEVELOPS THE POLYNOMIAL VALUES FOR AN INDIVIDUAL GENERATOR */ /* */ /* ----------------------------------------------------------------------- */ char plot_gen_poly( float min_load, /* MINIMUM LOAD FOR THIS UNIT */ float max_load, /* MAXIMUM LOAD FOR THIS UNIT */ float *mw_out, /* HEAT RATE DATA - MW OUTPUT */ float *mbtu_in, /* HEAT RATE DATA - MBTU INPUT */ int numobs, /* NUMBER OF HEAT RATE DATA POINTS */ float *coef) /* COEFFECIENTS OF POLYNOMIAL */ { int order; /* THIRD ORDER POLYNOMIAL */ int i; /* COUNTER */ float yest[10]; /* ESTIMATED Y VALUE FROM POLYN */ float resid[10]; /* RESIDUAL VALUE FROM POLYNOMIAL */ float see; /* STANDARD ERROR OF THE ESTIMATE */ float coefsig[4]; /* STANDARD ERROR OF COEFFECIENT */ float rsq; /* R SQUARED VALUE */ float r; /* CORRELATION COEFFECIENT */ char cferror = 0; /* ERROR = 1 IF SINGULAR */ char c; /* CHARACTER */ float x[11]; /* OUTPUT X DATA FOR PLOTS */ float y[11]; /* OUTPUT Y DATA FOR PLOTS */ float min_x; /* MINIMUM X FOR PLOTTING */ float max_x; /* MAXIMUM X FOR PLOTTING */ /* MAKE SURE WE HAVE AT LEAST TWO DATA POINTS */ if( numobs < 2 ) { strcpy( mp, "\aNot enough data points to compute polynomial!"); return( 'F' ); } /* NO MORE THAN 8 POINTS ARE ALLOWED AT THIS TIME */ if( numobs > 8 ) { strcpy( mp, "\aToo many data points!"); return( 'F' ); } /* require the order to be 3 */ order = 3; /* SORT DATA IN ASCENDING ORDER JUST TO BE SURE IT IS OK */ SortDataX( mw_out, mbtu_in, numobs, 1); /* CHOOSE BETWEEN MW_OUT AND MIN/MAX_LOAD FOR MIN/MAX X */ min_x = mw_out[0]; if( min_load < min_x ) min_x = min_load; max_x = mw_out[numobs -1]; if( max_load > max_x ) max_x = max_load; /* CALCULATE THE POLYNOMIAL FOR THIS UNIT */ PolyCurveFit(mw_out, mbtu_in, numobs, order, coef, yest, resid, &see, coefsig, &rsq, &r, &cferror); /* CHECK FOR ERRORS FROM POLYCURVEFIT */ if( cferror == 1 ) { strcpy( mp, "\aSingular Matrix"); return( 'F' ); } /* CALCULATE ACTUAL NUMBERS FROM POLYNOMIAL */ for( i = 0; i < 11; i++) { x[i] = min_x + ( i * ( max_x - min_x ) / 10 ); y[i] = coef[0] + coef[1] * x[i] + coef[2] * x[i] * x[i] + coef[3] * x[i] * x[i] * x[i]; } /* SET UP GRAPHICS */ _setbkcolor( _BLACK ); _clearscreen( _GCLEARSCREEN ); /* WINDOW 1 IS TOP LEFT OF SCREEN */ SetPercentWindow(0,0,.5,.8,1); SetWin2PlotRatio(1, 0.2,0.14,0.05,0.14 ); /* WINDOW 2 IS TOP RIGHT OF SCREEN */ SetPercentWindow(.5,0,1,.8,2); SetWin2PlotRatio(2, 0.2,0.14,0.05,0.14 ); /* DRAW WINDOW 1 - CURVE AND FITTED DATA */ SetCurrentWindow(1); setlinestyleXX(0,0,1); BorderCurrentWindow( border_color ); SelectColor( border_color ); TitleWindow("PLOT OF CURVE AND DATA"); SelectColor( axis_color ); TitleXAxis("MW OUTPUT", 0); TitleYAxis("MBTU per HOUR", 0); SetAxesType(0,0); AutoAxes(x, y, 11, 1); /* PLOT OUT THE CALCULATED VS INPUT POINTS */ LinePlotData(x, y, 11, 2,0); /* DRAW IN THE ORIGINAL DATA POINTS */ ScatterPlotData(mw_out, mbtu_in, numobs, 3, 1); setlinestyleXX(1,0,1); DrawGrid(10); /* MOVE TO WINDOW 2 - THE ERROR BAR WINDOW */ SetCurrentWindow(2); setlinestyleXX(0,0,1); BorderCurrentWindow( border_color ); SelectColor( border_color ); TitleWindow("CURVE FIT ERROR ANALYSIS"); SelectColor( axis_color ); TitleXAxis("MW OUTPUT", 0); TitleYAxis("MBTU ERROR", 0); SetAxesType(0,0); AutoAxes(mw_out,resid,numobs,0); DrawGrid(10); /* DRAW THE ERROR BARS */ BargraphData(mw_out, resid, numobs, (max_x - min_x)/60 , 3, 1, 1, 1); setlinestyleXX(1,0,1); /* ^ width of bar ^ */ /* PLACE SUMMARY DATA ON THE BOTTOM OF THE SCREEN */ _settextcolor( _RED ); strcpy( buffer, "THIS IS THE CUBIC POLYNOMIAL HEAT RATE EQUATION"); center_message( 21, buffer); /* PRINT OUT THE INDIVIDUAL POINTS AND THE ERRORS */ sprintf( buffer, "H(p) = (%8.6f)*p^3 +" " (%7.4f)*p^2 +" " (%6.3f)*p +" " (%6.2f)", coef[3], coef[2], coef[1], coef[0]); center_message( 23, buffer); /* ALL DONE. */ c = prompt( 25, -1, prompt_color, "All Finished - Press any key." ); strcpy( mp, "Normal Return of Polynomial Plot"); return( 'T' ); } /* ------------------------ input_gen_poly ------------------------------- */ /* */ /* INPUT THE DATA FOR AN INDIVIDUAL GENERATOR */ /* */ /* ----------------------------------------------------------------------- */ char input_gen_poly( int unitno ) { char c; /* CHARACTER */ char line[81]; /* LINE OF TEXT */ int suggest = 0; /* DID USER WANT TO USE SUGGESTED */ int numobs; /* NUMBER OF POINTS IN HEAT DATA */ int order; /* THIRD ORDER POLYNOMIAL */ int i; /* COUNTER */ int count; /* GRAPHICS POSITION COUNTER */ int start_pos; /* STARTING POSITION OF EDIT */ int end_pos; /* POSITION WHERE EDIT ENDED */ float mw_out[10], /* GENERATOR OUTPUT IN MEGAWATTS */ mbtu_in[10]; /* BTU INPUT */ float min_out; /* MINIMUM OUTPUT */ float max_out; /* MAXIMUM OUTPUT */ float coef[4]; /* POLYNOMIAL COEFFECIENTS */ unsigned ret_key; /* KEY RETURNED FROM EDITLINE */ /* SET UP VIDEO COLORS */ _setbkcolor( _RED ); _settextcolor( _WHITE ); _clearscreen( _GCLEARSCREEN ); _displaycursor( _GCURSORON ); center_message( 1, "Now, let's build a polynomial for a generator." " Hit ENTER when finished."); /* GIVE THE USER THE OPPORTUNITY TO USE SUGGESTED DATA */ c = prompt( 3, 0, prompt_color, "Would you like me to suggest data? (Y/N):" ); switch( toupper( c ) ) { case 'Y': suggest = 1; center_message(3,"Here are six points for MW_Out and MBtu_In." " Edit them if you like."); mw_out[0] = 140; mw_out[1] = 267; mw_out[2] = 310; mw_out[3] = 430; mw_out[4] = 499; mw_out[5] = 615; mbtu_in[0] = 1600; mbtu_in[1] = 2800; mbtu_in[2] = 3200; mbtu_in[3] = 4547; mbtu_in[4] = 5108; mbtu_in[5] = 6078; numobs = 6; break; default: suggest = 0; _settextposition( 3, 5 ); _outtext( "Input six points for MW_Out " "and MBtu_In below. ENTER when done."); /* upgrade - allow various numbers of data points */ numobs = 6; break; } _settextposition( 5, 30); _outtext( "MW Output MBtu Input" ); /* PRINT OUT THE DATA POINTS FOR EDITING */ for( i = 0; i < numobs; i++) { _settextposition( 6 + i, 30 ); if( suggest != 1 ) { mw_out[i] = 0; mbtu_in[i] = 0; } sprintf( line, "%8.1f%12.1f ", mw_out[i], mbtu_in[i]); _outtext( line ); } /* LOOP THROUGH ADDING DATA POINTS UNTIL FINISHED */ i = 0; ret_key = 0; start_pos = 0; while( (ret_key != KEY_ENTER) && (ret_key != KEY_ESCAPE) ) { _settextposition( 6 + i, 30 ); sprintf( line, "%8.1f%12.1f ", mw_out[i], mbtu_in[i]); ret_key = editline( line, start_pos, &end_pos ); start_pos = end_pos; sscanf( line, "%f %f", &mw_out[i], &mbtu_in[i] ); /* BASIC DATA CHECKING */ _settextposition( 16, 23 ); if( (mw_out[i] < 0) || (mbtu_in[i] <= 0) ) { _outtext( "Negative data not allowed. Redo the last one." ); } else { sprintf( buffer," Data point number%2i is OK. ", i); _outtext( buffer ); } /* MOVE AROUND DEPENDING ON KEY PRESS */ switch( ret_key ) { case KEY_UP: if( i > 0 ) i--; break; case KEY_DOWN: if( i < numobs - 1 ) i++; break; default: break; } } /* CHECK FOR TERMINATION BY ESCAPE KEY */ if( ret_key == KEY_ESCAPE ) { strcpy( mp, "Polynomial terminated by user" ); return( 'F' ); } /* NOW ASK FOR THE MINIMUM AND MAXIMUM OUTPUT OF THE UNIT */ strcpy( buffer, "Now input the minimum and maximum output for this unit." ); center_message( 16, buffer ); if( suggest == 1 ) { strcpy( line, " 200.0 700.0 "); } else { strcpy( line, " 0.0 0.0 "); } _settextposition( 18, 30 ); /* EDIT THIS LINE UNTIL FINISHED */ ret_key = 0; while( (ret_key != KEY_ENTER) && (ret_key != KEY_ESCAPE) ) { ret_key = editline( line, 0, &end_pos ); } sscanf( line, "%f %f", &min_out, &max_out ); _displaycursor( _GCURSOROFF ); /* CHECK FOR TERMINATION BY ESCAPE KEY */ if( ret_key == KEY_ESCAPE ) { strcpy( mp, "Polynomial terminated by user" ); return( 'F' ); } /* CALL THE POLYNOMIAL PLOT */ c = plot_gen_poly( min_out, max_out, mw_out, mbtu_in, numobs, coef); /* ALL DONE. */ if( c == 'T' ) { strcpy( mp, "Normal Return"); return( 'T' ); } return( 'F' ); } /* ------------------------- PLOT_ITERATIONS ----------------------------- */ /* PLOT THE ITERATIONS FROM WHATEVER SOLUTION METHOD WAS USED */ /* REQUIRES AN ARRAY FOR EACH GUESS OF LAMBDA AND THE SYSTEM LOAD AT */ /* EACH GUESS. It also requires the size of the array. */ /* ----------------------------------------------------------------------- */ void plot_iterations( float *each_guess, int num_guess ) { char c; int i; float *iteration; if( print_all == 'Y' ) prompt( 25, -1, prompt_color, "Entered Plot" ); iteration = GetMem( num_guess ); if( BadMem( iteration ) ) { printf("Can't allocate memory for iteration counter"); exit( 0 ); } /* BUILD AN X ARRAY FOR THE AUTO AXIS ROUTINE */ for( i = 0; i < num_guess; i++) { iteration[i] = i; } /* SET UP GRAPHICS WINDOW FOR PLOTTING */ _setbkcolor( _BLACK ); _clearscreen( _GCLEARSCREEN ); /* WINDOW 1 */ SetPercentWindow(0,0,.85,.9,1); SetCurrentWindow(1); SetWin2PlotRatio(1, 0.12,0.1,0.05,0.14 ); setlinestyleXX( 0, 0, 1); BorderCurrentWindow( border_color ); SelectColor( border_color ); TitleWindow("ITERATION SUMMARY"); SelectColor( axis_color ); SetAxesType(0,0); ScalePlotArea( 0, floor(min_lambda), MAXITERATIONS, ceil(max_lambda)); SetXYIntercepts( 0, floor(min_lambda) ); DrawXAxis( 1, 0 ); LabelXAxis( 5, 0); DrawYAxis( 1, 0 ); LabelYAxis( 2, 0 ); TitleXAxis("ITERATION NUMBER", 0); TitleYAxis("GUESSED VALUE OF LAMBDA", 0); setlinestyleXX(3,0,1); /* Set line style for grids */ DrawGridX( 5 ); DrawGridY( 2 ); LinePlotData( iteration, each_guess, num_guess, iter_color, 0); /* PRINT OUT THE ITERATION SUMMARY */ _settextcolor( iter_color ); _settextposition( 1, 70 ); _outtext( " # Lambda"); for( i = 0; i < num_guess; i++ ) { sprintf( buffer, "%2i %7.4f", i, each_guess[i] ); _settextposition( i + 2, 70 ); _outtext( buffer ); } if( fabs( system_load - net_out ) < TOL ) { sprintf( buffer, "This case converged in %2i iterations.", num_guess); } else { sprintf( buffer, "This case NEVER CONVERGED after %2i iterations.", num_guess); } _settextcolor( _BRIGHTWHITE ); center_message( 24, buffer ); c = any_key( 25, -1, prompt_color ); free( iteration ); } /* ------------------------ DISPLAY_UNIT_INCREMENTALS--------------------- */ /* */ /* THIS ROUTINE SIMPLY GRAPHS THE UNIT LAMBDA CURVES, SHOWS THE SYSTEM */ /* LAMBDA, AND DISPLAYS THE SUMMARY DATA FOR THE SYSTEM. */ /* */ /* IT ALSO ALLOWS SHOWING THE INTERMEDIATE STEPS. */ /* */ /* ----------------------------------------------------------------------- */ char display_unit_incrementals( float disp_lambda, char show_steps ) { int i; /* COUNTER */ int j; /* COUNTER */ float minX; /* MINIMUM X RANGE FOR PLOT */ float maxX; /* MAXIMUM X RANGE FOR PLOT */ float minY; /* MINIMUM Y RANGE FOR PLOT */ float maxY; /* MAXIMUM Y RANGE FOR PLOT */ int count; /* GRAPHICS POSITION COUNTER */ float x[11]; /* GENERATOR OUTPUT IN MEGAWATTS */ float y[11]; /* INCREMENTAL COST */ char c; /* CHARACTER */ char status[8]; /* STATUS STRING POINTER */ char det_stat[101]; /* DETAILED STATUS STRING POINTER */ /* TEMPORARY VALUES FOR MAX X AND Y */ minX = 0; maxX = 0; for( i = 0; i < num_gen; i++) { if( max_out[i] > maxX ) maxX = max_out[i]; } minY = min_lambda; maxY = max_lambda; x[0] = minX; x[1] = maxX; y[0] = minY; y[1] = maxY; /* SET UP GRAPHICS WINDOW FOR PLOTTING */ _setbkcolor( _BLACK ); _clearscreen( _GCLEARSCREEN ); /* WINDOW 1 IS TOP 80% OF SCREEN */ SetPercentWindow(0,0,1,.8,1); SetCurrentWindow(1); SetWin2PlotRatio(1, 0.12,0.1,0.05,0.14 ); BorderCurrentWindow( border_color ); SelectColor( border_color ); TitleWindow("INDIVIDUAL UNIT INCREMENTAL COST CURVES"); SelectColor( axis_color ); TitleXAxis("MW OUTPUT", 0); TitleYAxis("LAMBDA", 0); SetAxesType(0,0); AutoAxes( x, y, 2, 0); DrawGridX(20); DrawGridY(10); setlinestyleXX(3,0,1); /* Set line style for grids */ /* SET UP DATA AND PLOT LAMBDA AS A HORIZONTAL LINE */ x[0] = minX; x[1] = maxX; y[0] = disp_lambda; y[1] = disp_lambda; LinePlotData( x, y, 2, lambda_color, 0); /* PRINT OUT SUMMARY DATA ON INFORMATION PANEL */ _settextcolor(15); _settextposition( 21, 2); sprintf(buffer, "---- TOTAL GENERATION =%7.1f " "LOAD =%7.1f SYSTEM LAMBDA =%7.3f ----", net_out, system_load, disp_lambda); _outtext( buffer ); _settextposition( 22, 2); _outtext( "LABMDA" ); _settextposition( 23, 2); _outtext( "UNIT LOAD" ); _settextposition( 24, 2); _outtext( "STATUS" ); /* LOOP THROUGH EACH GENERATOR and GRAPH THE INCREMENTAL COST */ for( i = 0; i < num_gen; i++) { /* DISPLAY GENERATOR LAMBDAS */ /* OTHER SECTIONS MADE SURE WE CAN DO THIS */ switch(gen_status[i]) { case 'R': gen_lambda[i] = disp_lambda; strcpy( status, "Reg."); strcpy( det_stat, "System lambda is withing min/max criteria." " This unit is regulating."); break; case 'L': gen_lambda[i] = gen_min_lambda[i]; strcpy( status, "Low"); strcpy(det_stat, "System lambda is lower than this unit's " "minimum. Therefore the unit is held to " "minimum output."); break; case 'H': gen_lambda[i] = gen_max_lambda[i]; strcpy( status, "High"); strcpy( det_stat, "System lambda is higher than this unit's " "maximum. Run it at full output."); break; case 'O': gen_lambda[i] = 0; strcpy( status, "Off"); strcpy( det_stat, "This unit is scheduled off line."); break; case 'F': gen_lambda[i] = 0; strcpy( status, "Held"); sprintf( det_stat, "This unit is set for %6.1 MW and is " "not available for regulated output.", gen_load[i]); break; default : gen_lambda[i] = 0; strcpy( status, "UNK."); strcpy( det_stat, "I don't know what is going on with this one!"); break; } if( show_steps == 'T' ) { c = any_key( 25, -1, prompt_color ); /* DISPLAY A SUMMARY OF THE INFORMATION */ /* SWAP PAGES TO PRESERVE THE DEVELOPING GRAPH */ _setactivepage(1); _settextcolor( line_color[i] ); _clearscreen(_GCLEARSCREEN); _setvisualpage(1); _settextposition( 3, 20); sprintf(buffer,"Condition Summary for Unit %3.0f",gen_name[i]); _outtext( buffer ); _settextposition( 5, 20); _outtext( "CONDITION MIN OUTPUT MAX OUTPUT"); _settextposition( 6, 20); sprintf(buffer,"Capacity%15.1f%15.1f", min_out[i], max_out[i]); _outtext( buffer ); _settextposition( 7, 20); sprintf( buffer, "Lambda%17.3f%15.3f", gen_min_lambda[i], gen_max_lambda[i]); _outtext( buffer ); _settextposition( 9, 25 ); sprintf( buffer, "System Lambda =%7.3f", disp_lambda ); _outtext( buffer ); _settextposition( 11, 5); _outtext( det_stat ); _settextposition( 14, 20); sprintf( buffer, "This unit is contributing%6.1f" " to the system load.", gen_load[i]); _outtext( buffer ); _settextposition( 15, 20); _outtext( "Let's see how this unit fits on the lambda graph." ); _settextposition( 16, 20); _outtext( "Look for it in this color." ); c = any_key( 25, -1, i+1 ); _setactivepage(0); _setvisualpage(0); } /* CALCULATE TEN POINTS FOR THE INCREMENTAL COST GRAPH */ for( j = 0; j < 11; j++) { x[j] = min_out[i] + ( j * ( max_out[i] - min_out[i] ) / 10 ); y[j] = dfdp_0[i] + dfdp_1[i] * x[j] + dfdp_2[i] * x[j] * x[j]; } /* PLOT EACH GENERATOR IN A DIFFERENT COLOR AND LABEL AT BOTTOM */ LinePlotData(x, y, 11, line_color[i], 0); /* GRAPH THE VERTICAL LINES TO THE LAMBDA CURVE */ x[0] = gen_load[i]; x[1] = gen_load[i]; y[0] = minX; y[1] = gen_lambda[i]; LinePlotData( x, y, 2, line_color[i], 0); /* PRINT SUMMARY DATA ALONG THE BOTTOM LINE */ _settextcolor( line_color[i] ); _settextposition( 22, (i + 1) * 7 + 3); sprintf( buffer, "%7.3f", gen_lambda[i]); _outtext( buffer ); _settextposition( 23, (i + 1) * 7 + 4); sprintf(buffer, "%6.1f", gen_load[i]); _outtext( buffer ); _settextposition( 24, (i + 1) * 7 + 6); _outtext( status ); } } /* ------------------------ GUESS_A_LAMBDA ------------------------------- */ /* */ /* THIS ROUTINE SIMPLY ASKS THE USER TO MAKE A GUESS FOR LAMBDA AND THEN */ /* DISPLAYS THE RESULTS GRAPHICALLY. */ /* */ /* */ /* */ /* ----------------------------------------------------------------------- */ char guess_a_lambda( void ) { float guess; /* GUESS FOR LAMBDA */ char c; int num_guesses; /* NUMBER OF GUESSES */ unsigned ret_key; /* RETURN KEY FROM EDITLINE */ char line[10]; /* LINE OF TEXT */ int end_pos; /* ENDING POSITION FROM EDITLINE */ /* UPDATE DISPATCH CONSTANTS & FIND LAMBDA RANGE */ calc_dfdp_constants(); _setbkcolor( _BLACK ); _clearscreen( _GCLEARSCREEN ); system_load = 3000; guess = 0; num_guesses = 1; while( num_guesses < 10 ) { /* SET UP INPUT LINE, TURN ON CURSOR, AND ASK FOR A LAMBDA */ _settextcolor( prompt_color ); _settextposition( 25, 1 ); sprintf( buffer, "Make a guess for Lambda between %6.3f and %6.3f " "or ESCAPE to Exit:", min_lambda, max_lambda); _outtext( buffer ); /* SET UP THE EDITOR */ ret_key = 0; strcpy( line, " 0.000" ); while( (ret_key != KEY_ENTER ) && (ret_key != KEY_ESCAPE ) ) { _displaycursor( _GCURSORON ); _settextposition( 25, 70); ret_key = editline( line, 1, &end_pos ); _displaycursor( _GCURSOROFF ); } /* CHECK TO SEE IF USER WANTS TO QUIT */ if( ret_key == KEY_ESCAPE ) { strcpy( mp, "Make a Guess Terminated by User"); return( 'F' ); } /* RESET THE RETURN KEY FOR THE NEXT INPUT */ ret_key = 0; /* CHECK INPUT FOR VALID DATA */ sscanf( line, "%f", &guess ); /* ADD ERROR HANDLING ON LAMBDA */ if( guess > max_lambda) { c = prompt(25, -1, _RED, "Your guess is too HIGH to graph! Press any key."); } if( guess < min_lambda) { c = prompt(25, -1, _RED, "Your guess is too LOW to graph! Press any key."); } /* DO THE GRAPH IF THE GUESS IS WITHIN RANGE */ if( (guess >= min_lambda) && (guess <= max_lambda) ) { lambda = guess; calc_output(); c = display_unit_incrementals( lambda, 'T' ); } num_guesses++; } } /* ------------------------ TEACH_EQUAL_LAMBDA --------------------------- */ /* */ /* THIS IS AN INDEPENDENT ROUTINE THAT TEACHES THE BASICS THAT MINIMUM */ /* COST IS ACHIEVED WHEN THE LAMBDAS OF TWO UNITS ARE EQUAL. */ /* IT DOES USE SOME OF THE EXTERNAL VARIABLES FOR SIMPLICITY. */ /* */ /* */ /* ----------------------------------------------------------------------- */ char teach_equal_lambda (void) { int i; /* COUNTER */ int j; /* COUNTER */ unsigned ret_key; /* RETURN KEY FROM EDITLINE */ int start_pos; /* STARTING POSITION FOR EDITLINE */ int end_pos; /* ENDING POSITION FROM EDITLINE */ char line[81]; /* LINE OF DATA FOR EDITING */ float min_lambda; /* LAMBDA AT MINIMUM SOLUTION */ float minX; /* MINIMUM X RANGE FOR PLOT */ float maxX; /* MAXIMUM X RANGE FOR PLOT */ float minY; /* MINIMUM Y RANGE FOR PLOT */ float maxY; /* MAXIMUM Y RANGE FOR PLOT */ float minGen; /* MINIMUM GENERATION FOR PLOT */ float maxGen; /* MAXIMUM GENERATION FOR PLOT */ float Cost; /* ENERGY COST FOR SYSTEM */ float minCost; /* MINIMUM ENERGY COST FOR PLOT */ float maxCost; /* MAXIMUM ENERGY COST FOR PLOT */ int count; /* GRAPHICS POSITION COUNTER */ float x[11]; /* TEMPORARY DATA FOR PLOTTING */ float y[11]; /* TEMPORARY DATA FOR PLOTTING */ float yy[11]; /* DATA FOR AXIS PLOTTING */ float tcost; /* TOTAL OUTPUT COST OF GENERATORS */ char c; /* CHARACTER */ int color; /* COLOR TO FOR EACH ITERATION */ /* ARBITRARILY SET THE SYSTEM LOAD */ system_load = 800; /* LOAD UP THE FILE DATA WE NEED AND CALCULATE CONSTANTS */ /* THIS SETS UP ALL OF THE NEEDED QUANTITIES FOR OTHER ROUTINES */ strcpy( genfile_buff, "TEACHMIN.GEN" ); c = get_gen_data(); calc_dfdp_constants(); if( show_message( "EQUALLAMBDA1" ) == 'F') return ( 'F' ); if( teach_mode == 'T') any_key( 25, 1, _RED ); if( show_message( "EQUALLAMBDA2" ) == 'F') return ( 'F' ); /* CLEAR THE SCREEN IF WE ARE NOT IN TEACH MODE */ if( teach_mode != 'T' ) _clearscreen( _GCLEARSCREEN ); /* PRINT OUT THE EQUATION AND SET UP THE MIN/MAX PLOT DATA */ for( i = 0; i < 2; i++) { sprintf( buffer, "%6.1f%8.1f%15.8f*P^3 +%8.5f*P^2 +" "%7.4f*P +%6.2f", min_out[i], max_out[i], h_3[i], h_2[i], h_1[i], h_0[i]); _settextposition( 8 + i, 8); _outtext( buffer ); /* This is an array of genrator limits and Lambda limits */ x[i] = min_out[i]; x[i+2] = max_out[i]; /* YY is the axis scaler for the right graph (incremental) */ yy[i] = gen_min_lambda[i]; yy[i+2] = gen_max_lambda[i]; } /* THIS SECTION FINDS THE VERTICAL AXIS SCALING FOR THE LEFT PLOT */ /* First set unit 0 at minimum */ gen_load[0] = min_out[0]; gen_load[1] = system_load - min_out[0]; calc_energy_cost(); maxCost = (gen_output_cost[0] + gen_output_cost[1])/system_load; /* Now set unit 1 at minimum */ gen_load[1] = min_out[1]; gen_load[0] = system_load - min_out[1]; calc_energy_cost(); Cost = (gen_output_cost[0] + gen_output_cost[1])/system_load; if( Cost > maxCost) maxCost = Cost; /* Now find the minimum cost by dispatching */ c = binary_solution(); min_lambda = lambda; calc_energy_cost(); minCost = (gen_output_cost[0] + gen_output_cost[1])/system_load; /* FIND THE GENERATION RANGES */ if( min_out[0] < min_out[1] ) { minGen = min_out[0]; } else { minGen = min_out[1]; } if( max_out[0] > max_out[1] ) { maxGen = max_out[0]; } else { maxGen = max_out[1]; } /* END OF ESTIMATING RANGE FOR AXIS */ c = any_key( 25, -1, prompt_color ); /* INITIALIZE GRAPHICS HERE */ _setbkcolor( _BLACK ); _clearscreen( _GCLEARSCREEN ); /* SET UP GRAPHICS WINDOW # 1 FOR PLOTTING */ SetPercentWindow(0,0,.5,.8,1); /* WINDOW 1 IS UPPER LEFT SCREEN */ SetCurrentWindow(1); SetWin2PlotRatio(1, 0.2,0.1,0.05,0.14 ); setlinestyleXX(0,0,1); BorderCurrentWindow( border_color ); SelectColor( border_color ); TitleWindow("ENERGY COST mil/kWH"); SetAxesType(0,0); ScalePlotArea( floor(minGen/100)*100, floor(minCost), ceil(maxGen/100)*100, ceil(maxCost)); SelectColor( axis_color ); SetXYIntercepts( floor(minGen/100)*100, floor(minCost) ); DrawXAxis( 10, 0 ); LabelXAxis( 10, 0); DrawYAxis( 1, 0 ); LabelYAxis( 1, 0 ); TitleXAxis("UNIT 1 MW OUTPUT", 0); TitleYAxis("COST MILS/kWH", 0); setlinestyleXX(3,0,1); DrawGridX(10); DrawGridY(1); /* SET UP GRAPHICS WINDOW # 2 FOR PLOTTING */ SetPercentWindow(.5,0,1,.8,2); /* WINDOW 2 IS UPPER RIGHT SCREEN */ SetCurrentWindow(2); SetWin2PlotRatio(2, 0.2,0.1,0.05,0.14 ); /* How plot fills window */ setlinestyleXX(0,0,1); BorderCurrentWindow( border_color ); SelectColor( border_color ); TitleWindow("INCREMENTAL COST CURVES"); SetAxesType(0,0); SelectColor( axis_color ); setlinestyleXX(0,0,1); AutoAxes( x, yy, 4, 0); TitleXAxis("UNIT 1 & UNIT 2 MW OUTPUT", 0); TitleYAxis("LAMBDA", 0); setlinestyleXX(3,0,1); DrawGridX(10); DrawGridY(10); setlinestyleXX(0,0,1); /* Return to plotting line style */ /* LOOP THROUGH EACH GENERATOR and GRAPH THE INCREMENTAL COST */ for( i = 0; i < num_gen; i++) { /* CALCULATE TEN POINTS FOR THE INCREMENTAL COST GRAPH */ for( j = 0; j < 11; j++) { x[j] = min_out[i] + ( j * ( max_out[i] - min_out[i] ) / 10 ); y[j] = dfdp_0[i] + dfdp_1[i] * x[j] + dfdp_2[i] * x[j] * x[j]; } /* PLOT EACH GENERATOR IN A DIFFERENT COLOR AND LABEL */ if( i == 0 ) { LinePlotData(x, y, 11, gen1_color, 0); } else { LinePlotData(x, y, 11, gen2_color, 0); } } /* SET UP THE INTERACTIVE SESSION LABELS */ sprintf( buffer, "MAKE A FEW LOAD GUESSES FOR UNIT_1. UNIT_2 =%6.1f - UNIT_1.", system_load ); center_message( 21, buffer ); _settextposition( 22, 1 ); _settextcolor( gen1_color ); /* Match color to the line graph */ _outtext( "Unit_1 MW Load" ); _settextposition( 23, 1 ); _settextcolor( gen2_color ); /* Match color to the line graph */ _outtext( "Unit_2 MW Load" ); _settextposition( 24, 1 ); _settextcolor( _RED ); _outtext( "Energy Cost" ); /* LOOP THROUGH UP TO 8 TIMES AND PLOT EACH NEW POINT ON THE GRAPHS */ _displaycursor( _GCURSORON ); ret_key = 0; for( i = 0; i < 8; i++ ) { /* CHANGE COLOR FOR EACH OUTPUT CASE */ _settextcolor( line_color[i] ); strcpy( line, " 00.0" ); while( (ret_key != KEY_ENTER ) && (ret_key != KEY_ESCAPE ) ) { _settextposition( 22, 15 + 7 * i ); ret_key = editline( line, 2, &end_pos ); } /* ALLOW USER TO ESCAPE */ if( ret_key == KEY_ESCAPE ) { strcpy( mp, "Equal lambda demonstration terminated by user."); return( 'F' ); } /* RESET THE RETURN KEY FOR THE NEXT INPUT */ ret_key = 0; /* GET THE DATA FROM THE EDITING SESSION */ sscanf( line, "%f", &gen_load[0] ); /* CHECK FOR INPUT ERRORS ON UNIT 1 */ if( (gen_load[0] >= min_out[0]) && (gen_load[0] <= max_out[0]) ) { /* NO ERRORS ON UNIT 1 CALCULATE AND PLOT */ gen_load[1] = system_load - gen_load[0]; calc_energy_cost(); sprintf( buffer, "%7.1f", gen_load[1]); _settextposition( 23, 15 + 7 * i ); _outtext( buffer ); /* CALCULATE ENERGY COST $/MWH OR mil/KWH */ tcost = (gen_output_cost[0] + gen_output_cost[1])/system_load; sprintf( buffer, "%7.3f", tcost ); _settextposition( 24, 15 + 7 * i ); _outtext( buffer ); center_message(25, "Look at the new points." " Is the cost at a minimum?" " Are the lambdas equal?" ); /* PLOT THE POINT ON THE ENERGY COST CURVE */ SetCurrentWindow(1); x[0] = gen_load[0]; y[0] = tcost; ScatterPlotData( x, y, 1, line_color[i], 2); /* CALCULATE & PLOT BOTH UNITS ON THE INCREMENTAL CURVE */ SetCurrentWindow(2); for( j = 0; j < 2; j++ ) { x[j] = gen_load[j]; y[j] = dfdp_0[j] + dfdp_1[j] * x[j] + dfdp_2[j] * x[j] * x[j]; } ScatterPlotData( x, y, 2, line_color[i], 2); } /* LOAD IS TOO BIG OR SMALL - ISSUE WARNING */ else { sprintf( buffer, "Unit_1 = %6.1fMW. Must be between%6.1f and%6.1f", gen_load[0], min_out[0], max_out[0]); center_message( 25, buffer ); i--; } } _displaycursor( _GCURSOROFF ); _settextcolor( _RED ); sprintf( buffer, "Minimum cost when both lambdas are equal." " Lambda =%7.3f. Press any key....", min_lambda); center_message( 25, buffer ); c = getch(); return( 'T' ); } /* -------------------------- GET LOSS DATA ------------------------------ */ /* ALLOW USER TO REQUEST LOSS DATA FROM A FORMATTED FILE */ /* CHECK FOR VALID FILE AND VALID DATA - CALCULATE Y MATRIX */ /* */ /* THREE DATA BLOCKS ARE READ. */ /* 1) LINE IMPEDANCE AND CONNECTIVITY */ /* 2) BUS VOLTAGES */ /* 3) LOSS COEFFECIENTS FOR EACH BUS */ /* RETURNS: T-rue */ /* F-alse */ /* ALSO SETS VALID DATA FLAG ACCORDINGLY */ /* ----------------------------------------------------------------------- */ char get_loss_data(void) { int j, k, i, n; /* Y(j,k) LINE(i) GEN-n */ int from, to; /* FROM BUS & TO BUS */ float r, /* RESISTANCE OF LINE DATA */ x, /* REACTANCE OF LINE DATA */ b; /* LINE SHUNT REACTANCE */ char loss_file_buff[81]; /* LOSS DATA FILE BUFFER */ FILE *loss_file; /* FILE POINTER FOR LOSS DATA */ struct complex zline[MAXLINE]; /* LINE IMPEDANCE */ struct complex yline; struct complex one; /* 1.0 angle zero */ one.x = 1.0; one.y = 0.0; valid_data = 'T'; /* ASSUME DATA IS GOOD-WE'LL FIND ERRORS */ _settextposition(24, 10); fflush( stdin ); printf( "Enter of loss data file name: " ); gets( loss_file_buff ); if( (loss_file = fopen( loss_file_buff, "rt" )) == NULL ) { /* File open failed */ strcpy( mp, "\aUnable to open data file!"); return( 'F' ); } /* File opened successfully - read data */ fscanf( loss_file, "%i%i\n", &num_line, &num_bus); if( print_all == 'Y' ) { printf( "\n%i %i\n", num_line, num_bus); } /* Since I do not have a callable load flow I will have to read in */ /* all of the voltages and the A coeffecients. I can build the Y bus */ /* from the line data. */ /* CHECK TO SEE IF WE HAVE LINE DATA EXIT WITH WARNING IF NOT */ if( (num_line <= 0) || (num_line > MAXLINE)) { strcpy( mp,"\Invalid line data in this case!"); line_data = 'N'; return( 'F' ); } /* WE HAVE LINE DATA AVAILABLE - TRY TO READ IT AND MAKE THE Y MATRIX */ /* RESET Y BUS VALUES */ for( j = 0; j < MAXBUS; j++) /* C STARTS THE MATRIX AT 0, not 1 */ { for( k = 0; k MAXBUS) || (to > MAXBUS) ) { strcpy( mp, "\aFrom or To bus outside limits of 1 to MAXGEN in Y matrix"); return( 'F' ); } if( (r == 0) && (x == 0) ) { strcpy( mp, "\aZero impedance line building Y matrix."); return( 'F' ); } /* NO ERRORS - BUILD Y MATRIX */ /* HANDLE C's QUIRK ABOUT STARTING A MATRIX AT ZERO BY SUBTRACTING */ /* ONE FROM THE FROM AND TO BUS */ from--; to--; zline[i].x = r; zline[i].y = x; yline = cdiv( one, zline[i] ); y[from][from].x = y[from][from].x + yline.x + 0; y[from][from].y = y[from][from].y + yline.y + b/2; y[to ][to ].x = y[to ][to ].x + yline.x + 0; y[to ][to ].y = y[to ][to ].y + yline.y + b/2; y[from][to ].x = y[from][to ].x - yline.x; y[from][to ].y = y[from][to ].y - yline.y; y[to ][from].x = y[to ][from].x - yline.x; y[to ][from].y = y[to ][from].y - yline.y; } /* LINE DATA SEEMS TO BE OK */ line_data = 'Y'; /* PRINT OUT Y BUS JUST TO BE SURE IT IS OK */ /* THIS REQUIRES PRINT SCREEN TO BE ON IF YOU WANT TO SEE IT */ if( print_all == 'Y') { _settextposition( 1, 1); printf(" j k y[j][k].x y[j][k].y\n"); for( j = 0; j < num_bus; j++) { for( k = 0; k < num_bus; k++) { printf( "%4i%4i %10.5f %10.5f\n", j, k, y[j][k].x, y[j][k].y); } } } /* THAT WORKED OK NOW READ BUS VOLTAGE DATA (IF WE HAVE SOME) */ /* CHECK TO SEE IF WE HAVE BUS DATA EXIT WITH WARNING IF NOT */ if( (num_bus <= 0) || (num_bus > MAXBUS)) { strcpy( mp, "No bus data in this case!"); bus_data = 'N'; return( 'F' ); } /* SO FOR SO GOOD TRY TO READ IT */ if( print_all == 'Y') { printf("\nBus Volts Bus Angle\n"); } for( j = 0; j < num_bus; j++) { if( fscanf( loss_file, "%f %f\n", &v[j], &theta[j]) == EOF) { strcpy( mp, "\aEnd of file reached reading bus voltages"); return( 'F' ); } if( print_all == 'Y') { printf( "%10.5f %10.5f\n", v[j], theta[j]); } } if( print_all == 'Y') { printf("\nA( bus, gen ) Data\n"); } bus_data = 'Y'; /* LAST CALL - READ IN THE A(j,n) data */ for( j = 0; j < num_bus; j++) { for( n = 0; n < num_gen; n++) { if( fscanf( loss_file, "%f", &a[j][n]) == EOF) { strcpy( mp, "\aEnd of file reached reading A values"); return( 'F' ); } if( print_all == 'Y') { printf( "%5i %5i %10.5f \n", j, n, a[j][n]); } } if( fscanf( loss_file, "\n" ) == EOF) { strcpy( mp,"\aEnd of file reached reading next A value line"); return( 'F' ); } } /* LOOKS LIKE ALL DATA IS IN - LET'S DO IT */ a_data = 'T'; return( 'T' ); } /* --------------------------- CDIV -------------------------------------- */ /* */ /* COMPLEX DIVIDE A/B */ /* INPUT: COMPLEX a AND COMPLEX b */ /* RETURNS: COMPLEX QUOTIENT A/B */ /* */ /* ----------------------------------------------------------------------- */ struct complex cdiv(struct complex a, struct complex b) { double tmp; struct complex c; tmp = b.x * b.x + b.y * b.y; c.x = ( a.x * b.x + a.y * b.y ) / tmp; c.y = ( a.y * b.x - a.x * b.y ) / tmp; return(c); }