Skip to content

Commit

Permalink
Merge pull request #1870 from ibpsa/1869_scheduleHeader
Browse files Browse the repository at this point in the history
removed weekly schedule header impelementation for #1869
  • Loading branch information
mwetter authored Apr 15, 2024
2 parents ddab26c + dc5ce85 commit 29f6ebc
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 127 deletions.
164 changes: 52 additions & 112 deletions IBPSA/Resources/C-Sources/WeeklySchedule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
This code implements a weekly schedule.
Changelog:
April 9, 2024 by Filip Jorissen, Builtwins
Revisions for #1869 to remove a header requirement that contains the number of rows/columns.
March 30, 2024 by Filip Jorissen, Builtwins
Revisions for #1860 to avoid memory leaks when calling ModelicaFormatError.
May 25, 2022 by Michael Wetter, LBNL
Expand Down Expand Up @@ -33,6 +35,7 @@ int cmpfun(const void * tuple1, const void * tuple2) {

void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t_offset, const char* stringData) {
const int bufLen = 255;
const int allocSize = 50;
WeeklySchedule* scheduleID = NULL;


Expand All @@ -45,10 +48,11 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
int foundHeader = 0; /* indicates whether a header has been found */
int isHeaderLine = 0; /* indicates whether we are currently parsing the header line */
int tokensInLine = 0; /* keeps track of the number of tokens in the current line: column index and sanity check */
int tokensInFirstLine = 0;
int comment = 0; /* we are parsing a comment */
int n_rulesInMem = 0; /* number of rules for which we have allocated memory */
int n_reservedRules = 0;/* number of rules for which we have allocated memory */
int n_reservedColumns = allocSize;/* number of columns for which we have allocated memory */
int n_rulesInRow = 0; /* number of rules that exist in the current row */
int n_rowsPacked = 0; /* number of rules */
int n_newLines = 0; /* number of newlines */
int mustHaveNewLine = 0;/* The next character must be a newline */
char c; /* the character that is being parsed in this iteration */
Expand Down Expand Up @@ -95,6 +99,14 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
ModelicaFormatError("Failed to allocate memory for buff in WeeklySchedule.c.");
}

scheduleID->rules = (TimeDataTuple**)calloc(sizeof(TimeDataTuple *), allocSize);
if ( scheduleID->rules == NULL){
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Failed to allocate memory for rules in WeeklySchedule.c.");
}
n_reservedRules = allocSize;

/* Identify 'tokens' by splitting on (one or more) whitespace characters. */
/* Each token is parsed and special behaviour is created for comments and the header. */
/* The first column is analysed and split in (one or more) days (which can be comma separated), and hours, minutes, seconds. */
Expand Down Expand Up @@ -160,87 +172,11 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
tokenLen = strlen(scheduleID->token);
index = 0;

/* ModelicaFormatWarning("Parsing token %s", scheduleID->token); */

if (foundHeader == 0 && strcmp("double", scheduleID->token) == 0) {
/* we found a header line, we expect a specific format after the whitespace */
isHeaderLine = 1;
foundHeader = 1;
} else if ( isHeaderLine == 1 ) { /* parse the header columns and rows */
char *source;
int ncharsRow;
int ncharsCol;

if (strncmp("tab1(", scheduleID->token, 4) != 0) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect header when reading weekly schedule '%s'. It should start with 'tab1('.", name);
}

source = scheduleID->token + 5;
ncharsRow = strcspn(source, ",");

if (tokenLen == ncharsRow + 5 ) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect header when reading weekly schedule '%s'. No comma was found in the header.", name);
}
if ( ncharsRow > bufLen - 2 ) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Header length exceeds buffer size.");
}
strncpy(scheduleID->buff2, source, ncharsRow);
scheduleID->buff2[ncharsRow] = '\0';

if (sscanf(scheduleID->buff2, "%i", &scheduleID->n_rows_in) != 1) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Error in intenger conversion in header while parsing %s in weekly schedule '%s'.", scheduleID->buff2, name);
}

source = source + ncharsRow + 1;
ncharsCol = strcspn(source, ")");
if (tokenLen == ncharsCol + ncharsRow + 5 + 1) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect header when reading weekly schedule '%s'. No closing bracket was found in the header.", name);
} else if (tokenLen > ncharsCol + ncharsRow + 5 + 1 + 1) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect header when reading weekly schedule '%s'. It has trailing characters: '%s'.", name, scheduleID->token + ncharsRow + ncharsCol + 7);
}
strncpy(scheduleID->buff2, source, ncharsCol);
scheduleID->buff2[ncharsCol] = '\0';
if (sscanf(scheduleID->buff2, "%i", &scheduleID->n_cols_in) != 1) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Error in integer conversion in header while parsing %s in weekly schedule '%s'..", scheduleID->buff2, name);
}
if (scheduleID->n_cols_in < 2) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Illegal number of columns '%i' when reading weekly schedule '%s'.", scheduleID->n_cols_in, name);
}
if (scheduleID->n_rows_in < 1) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Illegal number of rows '%i' when reading weekly schedule '%s'.", scheduleID->n_rows_in, name);
}
isHeaderLine = 0;
foundHeader = 1;
scheduleID->rules = (TimeDataTuple**)calloc(sizeof(TimeDataTuple *), scheduleID->n_rows_in);
if ( scheduleID->rules == NULL){
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Failed to allocate memory for rules in WeeklySchedule.c.");
}

n_rulesInMem = scheduleID->n_rows_in;
} else if (foundHeader == 0) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Illegal file format, no header was found when reading weekly schedule '%s'.", name);
foundHeader = 1; /* Avoid checking header line again */
comment = 1; /* Ignore the rest of the line */
ModelicaFormatWarning("Detected header line when reading weekly schedule '%s'. This is deprecated. Consider removing the line that starts with 'double tab1'.", name);
} else if (tokensInLine == 0) {
/* 0 tokens have been found on this line, so we're parsing a date/time */
const int ncharsDays = strcspn(scheduleID->token, ":");
Expand Down Expand Up @@ -334,9 +270,9 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
}

/* expand the memory if the initially assigned memory block does not suffice*/
if (rule_i >= n_rulesInMem) {
n_rulesInMem += scheduleID->n_rows_in;
scheduleID->rules = (TimeDataTuple**)realloc(scheduleID->rules, sizeof(TimeDataTuple*) * n_rulesInMem);
if (rule_i >= n_reservedRules) {
n_reservedRules += allocSize;
scheduleID->rules = (TimeDataTuple**)realloc(scheduleID->rules, sizeof(TimeDataTuple*) * n_reservedRules);
if (scheduleID->rules == NULL) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
Expand All @@ -354,7 +290,7 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
scheduleID->n_allocatedRules++;

scheduleID->rules[rule_i]->time = time_i;
scheduleID->rules[rule_i]->data = (double*)calloc(sizeof(double), (scheduleID->n_cols_in - 1));
scheduleID->rules[rule_i]->data = (double*)calloc(sizeof(double), n_reservedColumns);
if ( scheduleID->rules[rule_i]->data == NULL){
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
Expand All @@ -364,8 +300,6 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t

rule_i++;
n_rulesInRow++;
if (offset == 0) /* only for the first rule in this row*/
n_rowsPacked++;

if (strlen(startIndex) == 3) { /*reached the end of the substring*/
break;
Expand All @@ -378,10 +312,18 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
double val;

/* a token has been found on this line before, so we're parsing some numerical data*/
if (tokensInLine >= scheduleID->n_cols_in) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Too many columns on row %i when reading weekly schedule '%s'.", line, name);
if (tokensInFirstLine != 0 && tokensInLine + 1 > tokensInFirstLine){
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Too many columns on data line %i when reading weekly schedule '%s'. %i instead of %i.", line, name, tokensInLine + 1, tokensInFirstLine);
}else if (tokensInLine >= n_reservedColumns) { /* This code should only be reached upon passing the first line of data */
n_reservedColumns += allocSize;
scheduleID->rules[rule_i-1]->data = (double*)realloc(scheduleID->rules[rule_i-1]->data, sizeof(double)*n_reservedColumns);
if ( scheduleID->rules[rule_i-1]->data == NULL){
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Failed to reallocate memory for rules[rule_i-1]->data in WeeklySchedule.c.");
}
}

if (sscanf(scheduleID->token, "%lf", &val) != 1) {
Expand All @@ -406,12 +348,16 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
ModelicaFormatError("Logic error when reading weekly schedule '%s'.", name); /*should not be able to end up here*/
}
}
if (c == '\n') { /*reset some internal variables*/
if (tokensInLine > 0 && tokensInLine != scheduleID->n_cols_in) {
if (c == '\n') {
if (tokensInFirstLine == 0 && tokensInLine > 0){
tokensInFirstLine = tokensInLine;
}else if (tokensInLine > 0 && tokensInLine != tokensInFirstLine) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect number of columns on line %i when reading weekly schedule '%s'.", line, name);
ModelicaFormatError("Too few columns on data line %i when reading weekly schedule '%s'. %i instead of %i", line, name, tokensInLine, tokensInFirstLine);
}

/*reset some internal variables*/
line++;
tokensInLine = 0;
comment = 0;
Expand All @@ -426,38 +372,27 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
ModelicaFormatError("In weekly schedule '%s': The provided %s is incorrectly formatted since it does not contain newline characters.", name, tableOnFile ? "file": "string parameter");
}

if (n_rowsPacked != scheduleID->n_rows_in) {
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Incorrect number of rows when reading weekly schedule '%s': %i instead of %i.", name, n_rowsPacked, scheduleID->n_rows_in);
}
if (scheduleID->n_cols_in < 1){
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("The number of columns in the weekly schedule '%s' is %i, which is too small. ", name, scheduleID->n_cols_in);
}

/* sort all data by time stamp*/
qsort(scheduleID->rules, rule_i, sizeof(TimeDataTuple*), cmpfun);

{
/* working vector with zero initial value*/
scheduleID->lastData = (double*)calloc(sizeof(double), scheduleID->n_cols_in - 1);
scheduleID->lastData = (double*)calloc(sizeof(double), tokensInFirstLine - 1);
if (scheduleID->lastData == NULL){
weeklyScheduleFreeInit(scheduleID);
weeklyScheduleFree(scheduleID);
ModelicaFormatError("Failed to allocate memory for lastData in WeeklySchedule.c., scheduleID->n_cols_in -1 = %d", scheduleID->n_cols_in - 1);
ModelicaFormatError("Failed to allocate memory for lastData in WeeklySchedule.c., tokensInFirstLine - 1 = %d", tokensInFirstLine - 1);
}

memset(scheduleID->lastData, (char)(double)0, scheduleID->n_cols_in - 1); /* set vector to zero initial guess*/
memset(scheduleID->lastData, (char)(double)0, tokensInFirstLine - 1); /* set vector to zero initial guess*/

/* Loop over all data and fill in wildcards using the last preceeding value.*/
/* This may wrap back to the end of last week, therefore loop the data twice.*/
/* If an entire column contains wildcards then use a default value of zero.*/

for (i = 0; i < 2; ++i) {
for (j = 0; j < rule_i; ++j) {
for (k = 0; k < scheduleID->n_cols_in - 1; ++k) {
for (k = 0; k < tokensInFirstLine - 1; ++k) {
if ( scheduleID->rules[j]->data[k] != HUGE_VAL ) {
scheduleID->lastData[k] = scheduleID->rules[j]->data[k];
} else if (i > 0) {
Expand All @@ -474,6 +409,7 @@ void* weeklyScheduleInit(const int tableOnFile, const char* name, const double t
scheduleID->t_offset = t_offset;
scheduleID->previousIndex = 0;
scheduleID->previousTimestamp = HUGE_VAL;
scheduleID->n_data_cols = tokensInFirstLine - 1;

weeklyScheduleFreeInit(scheduleID);

Expand All @@ -499,12 +435,16 @@ void weeklyScheduleFreeInit(void * ID) {


void weeklyScheduleFree(void * ID) {
int i;
WeeklySchedule* scheduleID = (WeeklySchedule*)ID;

int i;
if (ID == NULL) /* Otherwise OM segfaults when IBPSA.Utilities.IO.Files.Examples.WeeklySchedule triggers an error */
return;

for (i = 0; i < scheduleID->n_allocatedRulesData; ++i) {
free(scheduleID->rules[i]->data);
}

for (i = 0; i < scheduleID->n_allocatedRules; ++i) {
free(scheduleID->rules[i]);
}
Expand All @@ -528,8 +468,8 @@ double getScheduleValue(void * ID, const int column, const double modelicaTime)

/* Not calling weeklyScheduleFreeInit() or weeklyScheduleFree() since weeklyScheduleFreeInit() has already been called at the end of the
initialization and Modelica will call weeklyScheduleFree() upon a call of ModelicaFormatError) */
if (column < 0 || column > scheduleID->n_cols_in - 1) {
ModelicaFormatError("The requested column index '%i' is outside of the table range.", column + 1);
if (column < 0 || column > scheduleID->n_data_cols) {
ModelicaFormatError("The requested column index '%i' is outside of the table range '%i'.", column + 1, scheduleID->n_data_cols);
}
if (column == 0 ) {
ModelicaFormatError("The column index 1 is not a data column and is reserved for 'time'. It should not be read.");
Expand Down
20 changes: 10 additions & 10 deletions IBPSA/Resources/C-Sources/WeeklySchedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ typedef struct TimeDataTuple {


typedef struct WeeklySchedule {
double t_offset; /* Time offset for monday, midnight. */
int n_rows_in; /* Number of input rows */
int n_cols_in; /* Number of input columns */
double t_offset; /* Time offset for monday, midnight. */
int n_data_cols; /* Number of used input columns */
int n_allocatedRules; /* Number rules for which a struct was allocated */
int n_allocatedRulesData; /* Number rules for which a data vector was allocated */

double previousTimestamp; /* Time where the schedule was called the previous time */
int previousIndex; /* Index where the schedule was called the previous time */

int n_allocatedRules;
int n_allocatedRulesData;
double * lastData;
char * token;
FILE* fp;
char* buff2;
struct TimeDataTuple **rules;

double * lastData; /* A work vector */
char * token; /* A piece of input string that is being parsed */
FILE* fp; /* A file pointer */
char* buff2; /* A text buffer */
struct TimeDataTuple **rules; /* An array of rule pointers */

} WeeklySchedule;

Expand Down
2 changes: 0 additions & 2 deletions IBPSA/Resources/Data/schedule.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# Comments start with a #
# The user is responsible for making sure that the header dimensions are correct
double tab1(3,5) # Comments can be added at the end of a line for adding local documentation
mon:0:0:10 - 3 1 -
# Comments can be added in the table too
tue,thu:20:30:59 123 - 45 -
Expand Down
2 changes: 0 additions & 2 deletions IBPSA/Resources/Data/scheduleWindows.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# Comments start with a #
# The user is responsible for making sure that the header dimensions are correct
double tab1(3,5) # Comments can be added at the end of a line for adding local documentation
mon:0:0:10 - 3 1 -
# Comments can be added in the table too
tue,thu:20:30:59 123 - 45 -
Expand Down
2 changes: 1 addition & 1 deletion IBPSA/Utilities/IO/Files/Examples/WeeklySchedule.mo
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
within IBPSA.Utilities.IO.Files.Examples;
model WeeklySchedule "Weekly schedule example"
extends Modelica.Icons.Example;
parameter String data = "double tab1(3,5) #test:
parameter String data = "#test:
mon:0:0:10 - 3 1 -
tue,thu:20:30:59 123 - 45 -
wed 12 1 4 -" "Contents of schedule.txt";
Expand Down

0 comments on commit 29f6ebc

Please sign in to comment.