diff --git a/.gitignore b/.gitignore index ddeaaf8b235dc217fbfb6559e66bd665d1f31745..9bae25ebff81d077253fd8f1227aad98545d28a0 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ tests/input.hdf5 tests/testSingle tests/testTimeIntegration tests/testSPHStep +tests/testParser theory/latex/swift.pdf diff --git a/src/Makefile.am b/src/Makefile.am index f44d47819672d10445fd969fe2ff20dbcb49463b..15c05a2a00d33ad86e7144b4a8e377252a2eedce 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,13 +35,13 @@ endif # List required headers include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \ engine.h swift.h serial_io.h timers.h debug.h scheduler.h proxy.h parallel_io.h \ - common_io.h single_io.h multipole.h map.h tools.h partition.h clocks.h + common_io.h single_io.h multipole.h map.h tools.h partition.h clocks.h parser.h # Common source files AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \ serial_io.c timers.c debug.c scheduler.c proxy.c parallel_io.c \ units.c common_io.c single_io.c multipole.c version.c map.c \ - kernel.c tools.c part.c partition.c clocks.c + kernel.c tools.c part.c partition.c clocks.c parser.c # Include files for distribution, not installation. nobase_noinst_HEADERS = approx_math.h atomic.h cycle.h error.h inline.h kernel.h vector.h \ diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000000000000000000000000000000000000..06dc819842d54d952704e4e0c40ebec5b561f691 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,265 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 James Willis (james.s.willis@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +/* Needs to be included so that strtok returns char * instead of a int *. */ +#include <string.h> +#include <stdlib.h> + +/* This object's header. */ +#include "parser.h" + +/* Local headers. */ +#include "error.h" + +/* Private functions. */ +static int count_char(char *str, char val); +static void parse_line(FILE *fp, struct swift_params *params); + +/** + * @brief Reads an input file and stores each parameter in a structure. + * + * @param file_name Name of file to be read + * @param params Structure to be populated from file + */ + +void parser_read_file(const char *file_name, struct swift_params *params) { + + FILE *fp; + + params->count = 0; + + /* Open file for reading */ + fp = fopen(file_name, "r"); + + if (fp == NULL) { + error("Error opening parameter file: %s", file_name); + } + + /* Read until the end of the file is reached.*/ + while (!feof(fp)) { + parse_line(fp, params); + } + + fclose(fp); +} + +/** + * @brief Counts the number of times a specific character appears in a string. + * + * @param str String to be checked + * @param val Character to be counted + */ + +static int count_char(char *str, char val) { + + int count = 0; + + /* Check if the line contains the character */ + while (*str) { + if (*str++ == val) ++count; + } + + return count; +} + +/** + * @brief Parses a line from a file and stores any parameters in a structure. + * + * @param fp File pointer to file to be read + * @param params Structure to be populated from file + * + */ + +static void parse_line(FILE *fp, struct swift_params *params) { + + char line[PARSER_MAX_LINE_SIZE]; + char trim_line[PARSER_MAX_LINE_SIZE]; + + /* Read a line of the file */ + if (fgets(line, PARSER_MAX_LINE_SIZE, fp) != NULL) { + + char *token; + /* Remove comments */ + token = strtok(line, PARSER_COMMENT_CHAR); + strcpy(trim_line, token); + + /* Check if the line contains a value */ + if (strchr(trim_line, PARSER_VALUE_CHAR)) { + /* Check for more than one parameter on the same line. */ + if (count_char(trim_line, PARSER_VALUE_CHAR) > 1) { + error("Found more than one parameter in '%s', only one allowed.", line); + } else { + /* Take first token as the parameter name. */ + token = strtok(trim_line, PARSER_VALUE_STRING); + strcpy(params->data[params->count].name, token); + + /* Take second token as the parameter value. */ + token = strtok(NULL, " #\n"); + strcpy(params->data[params->count++].value, token); + } + } + } +} + +/** + * @brief Retrieve integer parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param retParam Value of the parameter found + * + */ + +void parser_get_param_int(struct swift_params *params, char *name, + int *retParam) { + + char str[128]; + + for (int i = 0; i < params->count; i++) { + + /*strcmp returns 0 if both strings are the same.*/ + if (!strcmp(name, params->data[i].name)) { + + /* Check that exactly one number is parsed. */ + if (sscanf(params->data[i].value, "%d%s", retParam, str) != 1) { + error( + "Tried parsing int '%s' but found '%s' with illegal integer " + "characters '%s'.", + params->data[i].name, params->data[i].value, str); + } + + return; + } + } + + message("Cannot find '%s' in the structure.", name); +} + +/** + * @brief Retrieve float parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param retParam Value of the parameter found + * + */ + +void parser_get_param_float(struct swift_params *params, char *name, + float *retParam) { + + char str[128]; + + for (int i = 0; i < params->count; i++) { + + /*strcmp returns 0 if both strings are the same.*/ + if (!strcmp(name, params->data[i].name)) { + + /* Check that exactly one number is parsed. */ + if (sscanf(params->data[i].value, "%f%s", retParam, str) != 1) { + error( + "Tried parsing float '%s' but found '%s' with illegal float " + "characters '%s'.", + params->data[i].name, params->data[i].value, str); + } + + return; + } + } + + message("Cannot find '%s' in the structure.", name); +} + +/** + * @brief Retrieve double parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param retParam Value of the parameter found + * + */ + +void parser_get_param_double(struct swift_params *params, char *name, + double *retParam) { + + char str[128]; + + for (int i = 0; i < params->count; i++) { + + /*strcmp returns 0 if both strings are the same.*/ + if (!strcmp(name, params->data[i].name)) { + + /* Check that exactly one number is parsed. */ + if (sscanf(params->data[i].value, "%lf", retParam) != 1) { + error( + "Tried parsing double '%s' but found '%s' with illegal double " + "characters '%s'.", + params->data[i].name, params->data[i].value, str); + } + + return; + } + } + + message("Cannot find '%s' in the structure.", name); +} + +/** + * @brief Retrieve string parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param retParam Value of the parameter found + * + */ + +void parser_get_param_string(struct swift_params *params, char *name, + char *retParam) { + + for (int i = 0; i < params->count; i++) { + + /*strcmp returns 0 if both strings are the same.*/ + if (!strcmp(name, params->data[i].name)) { + strcpy(retParam, params->data[i].value); + return; + } + } +} + +/** + * @brief Prints the contents of the parameter structure. + * + * @param params Structure that holds the parameters + * + */ + +void parser_print_params(struct swift_params *params) { + + printf("\n--------------------------\n"); + printf("| SWIFT Parameter File |\n"); + printf("--------------------------\n"); + + for (int i = 0; i < params->count; i++) { + printf("Parameter name: %s\n", params->data[i].name); + printf("Parameter value: %s\n", params->data[i].value); + } +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000000000000000000000000000000000000..2fb4148944cd423da016341744cb6d58e222182e --- /dev/null +++ b/src/parser.h @@ -0,0 +1,54 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 James Willis (james.s.willis@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_PARSER_H +#define SWIFT_PARSER_H + +#include <stdio.h> + +#define PARSER_MAX_LINE_SIZE 128 +#define PARSER_MAX_NO_OF_PARAMS 512 + +#define PARSER_COMMENT_CHAR "#" +#define PARSER_VALUE_CHAR ':' +#define PARSER_VALUE_STRING ":" +#define PARSER_END_OF_FILE "..." + +struct parameter { + char name[PARSER_MAX_LINE_SIZE]; + char value[PARSER_MAX_LINE_SIZE]; +}; + +struct swift_params { + struct parameter data[PARSER_MAX_NO_OF_PARAMS]; + int count; +}; + +/* Public API. */ +void parser_read_file(const char *file_name, struct swift_params *params); +void parser_print_params(struct swift_params *params); +void parser_get_param_int(struct swift_params *params, char *name, + int *retParam); +void parser_get_param_float(struct swift_params *params, char *name, + float *retParam); +void parser_get_param_double(struct swift_params *params, char *name, + double *retParam); +void parser_get_param_string(struct swift_params *params, char *name, + char *retParam); + +#endif /* SWIFT_PARSER_H */ diff --git a/src/swift.h b/src/swift.h index 9ab090dccd195ff4927d3e614e446b36d273f824..e568a28c888295affc9ec45b6d059d34f5b4bf04 100644 --- a/src/swift.h +++ b/src/swift.h @@ -27,7 +27,6 @@ #include "cell.h" #include "clocks.h" #include "const.h" -#include "const.h" #include "cycle.h" #include "debug.h" #include "engine.h" @@ -38,7 +37,9 @@ #include "map.h" #include "multipole.h" #include "parallel_io.h" +#include "parser.h" #include "part.h" +#include "partition.h" #include "queue.h" #include "runner.h" #include "scheduler.h" @@ -47,9 +48,8 @@ #include "space.h" #include "task.h" #include "timers.h" -#include "units.h" #include "tools.h" -#include "partition.h" +#include "units.h" #include "version.h" #endif /* SWIFT_SWIFT_H */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 7edf6e0659c91e66c7eee3e3815e8f83e8551ec2..d66282059d874f345437d779d59ec3edb08e47cb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -22,11 +22,11 @@ AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) # List of programs and scripts to run in the test suite TESTS = testGreetings testReading.sh testSingle testPair.sh testPairPerturbed.sh \ - test27cells.sh test27cellsPerturbed.sh + test27cells.sh test27cellsPerturbed.sh testParser.sh # List of test programs to compile check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \ - testSPHStep testPair test27cells + testSPHStep testPair test27cells testParser # Sources for the individual programs testGreetings_SOURCES = testGreetings.c @@ -43,6 +43,9 @@ testPair_SOURCES = testPair.c test27cells_SOURCES = test27cells.c +testParser_SOURCES = testParser.c + # Files necessary for distribution EXTRA_DIST = testReading.sh makeInput.py testPair.sh testPairPerturbed.sh \ - test27cells.sh test27cellsPerturbed.sh tolerance.dat + test27cells.sh test27cellsPerturbed.sh tolerance.dat testParser.sh \ + testParserInput.yaml diff --git a/tests/testParser.c b/tests/testParser.c new file mode 100644 index 0000000000000000000000000000000000000000..a4b8789fca056fef659bca78eae9d0effb2ceb66 --- /dev/null +++ b/tests/testParser.c @@ -0,0 +1,67 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2016 James Willis (james.s.willis@durham.ac.uk). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#include "parser.h" +#include <assert.h> +#include <string.h> +#include <math.h> + +int main(int argc, char *argv[]) { + + const char *input_file = argv[1]; + + /* Create a structure to read file into. */ + struct swift_params param_file; + + /* Create variables that will be set from the parameter file. */ + int no_of_threads = 0; + int no_of_time_steps = 0; + float max_h = 0.0f; + double start_time = 0.0; + char ic_file[PARSER_MAX_LINE_SIZE]; + + /* Read the parameter file. */ + parser_read_file(input_file, ¶m_file); + + /* Print the contents of the structure. */ + parser_print_params(¶m_file); + + /* Retrieve parameters and store them in variables defined above. + * Have to specify the name of the parameter as it appears in the + * input file: testParserInput.yaml.*/ + parser_get_param_int(¶m_file, "no_of_threads", &no_of_threads); + parser_get_param_int(¶m_file, "no_of_time_steps", &no_of_time_steps); + parser_get_param_float(¶m_file, "max_h", &max_h); + parser_get_param_double(¶m_file, "start_time", &start_time); + parser_get_param_string(¶m_file, "ic_file", ic_file); + + /* Print the variables to check their values are correct. */ + printf( + "no_of_threads: %d, no_of_time_steps: %d, max_h: %f, start_time: %lf, " + "ic_file: %s\n", + no_of_threads, no_of_time_steps, max_h, start_time, ic_file); + + assert(no_of_threads == 16); + assert(no_of_time_steps == 10); + assert(fabs(max_h - 1.1255) < 0.00001); + assert(fabs(start_time - 1.23456789) < 0.00001); + assert(strcmp(ic_file, "ic_file.ini") == 0); /*strcmp returns 0 if correct.*/ + + return 0; +} diff --git a/tests/testParser.sh b/tests/testParser.sh new file mode 100755 index 0000000000000000000000000000000000000000..3dad7f386f792ff2beb6e94eb093bad4085023a4 --- /dev/null +++ b/tests/testParser.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./testParser testParserInput.yaml diff --git a/tests/testParserInput.yaml b/tests/testParserInput.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d695e6a8ddd327e31224f36a6e34767ea8d36408 --- /dev/null +++ b/tests/testParserInput.yaml @@ -0,0 +1,9 @@ +--- +no_of_threads: 16 # The number of threads that will be used. +no_of_time_steps: 10 +max_h: 1.1255 +start_time: 1.23456789 +#Input file +ic_file: ic_file.ini + +...