diff --git a/configs/1d.json b/configs/1d.json new file mode 100644 index 00000000..dae5e204 --- /dev/null +++ b/configs/1d.json @@ -0,0 +1,16 @@ +{ + "solver": "fwave", + "simulationSizeX": 10, + "simulationSizeY": 1, + "offsetX": 0, + "offsetY": 0, + "nx":10, + "ny":1, + "setup":"DAMBREAK1D", + "writingFrequency":10, + "endTime":10, + "baseHeight":5, + "height":100, + "outputMethod":"csv", + "timeStepScaling":0.2 +} \ No newline at end of file diff --git a/configs/config.json b/configs/config.json index 2cfee871..e7efd8f7 100644 --- a/configs/config.json +++ b/configs/config.json @@ -8,10 +8,8 @@ "ny":100, "setup":"CIRCULARDAMBREAK2D", "writingFrequency":50, - "endTime":50, - "stations":[ - { "name":"station_1", "locX":0, "locY":10 }, - { "name":"station_2", "locX":5, "locY":10 }, - { "name":"station_3", "locX":10, "locY":10 } - ] + "endTime":30, + "baseHeight":5, + "diameter":10, + "height":100 } \ No newline at end of file diff --git a/docs/Doxyfile b/docs/Doxyfile index b1ec0618..c6b6d2ef 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -943,7 +943,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../../src +INPUT = ../../src ../../lib # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/docs/source/conf.py b/docs/source/conf.py index c5fecdfd..f0793f59 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -9,12 +9,11 @@ # Doxygen subprocess.call('doxygen ../Doxyfile', shell=True) - # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'tsunami_lab' -copyright = '2023, Luca-Philipp Grumbach & Richard Hofmann' +copyright = '2024, Luca-Philipp Grumbach & Richard Hofmann' author = 'Luca-Philipp Grumbach & Richard Hofmann' # -- General configuration --------------------------------------------------- diff --git a/docs/source/files/assignments/08.rst b/docs/source/files/assignments/08.rst index c6b372cc..4bb8266b 100644 --- a/docs/source/files/assignments/08.rst +++ b/docs/source/files/assignments/08.rst @@ -1,15 +1,17 @@ +################ 8. Optimization -***************** +################ +********** 8.1 ARA -======== +********** .. figure:: https://wiki.uni-jena.de/download/attachments/22453005/IMG_7381_0p5.JPG?version=1&modificationDate=1625042348365&api=v2 HPC-Cluster ARA. Source: https://wiki.uni-jena.de/pages/viewpage.action?pageId=22453005 8.1.1 - Uploading and running the code ----------------------------------------- +======================================== First we cloned our github repository to "beegfs" and transfered the bythymetry and displacement data with "wget https://cloud.uni-jena.de/s/CqrDBqiMyKComPc/download/data_in.tar.xz -O tsunami_lab_data_in.tar.xz" there. @@ -41,9 +43,10 @@ sbatch file: Since we only want to use one node, we set ``nodes`` and ``ntasks`` to 1 and ``cpus-per-task`` to 72. 8.1.2 - Visualizations --------------------------- +======================== -**Tohoku 5000** +Tohoku 5000 +----------- .. raw:: html @@ -52,7 +55,8 @@ Since we only want to use one node, we set ``nodes`` and ``ntasks`` to 1 and ``c -**Tohoku 1000** +Tohoku 1000 +----------- .. raw:: html @@ -62,7 +66,8 @@ Since we only want to use one node, we set ``nodes`` and ``ntasks`` to 1 and ``c -**Chile 5000** +Chile 5000 +----------- .. raw:: html @@ -70,7 +75,8 @@ Since we only want to use one node, we set ``nodes`` and ``ntasks`` to 1 and ``c -**Chile 1000** +Chile 1000 +----------- .. raw:: html @@ -82,7 +88,7 @@ Since we only want to use one node, we set ``nodes`` and ``ntasks`` to 1 and ``c Comparing to the simulations from assignment 6, it is clear that all simulations behave equally. 8.1.3 - Private PC vs ARA ---------------------------- +=========================== .. note:: @@ -90,7 +96,7 @@ Comparing to the simulations from assignment 6, it is clear that all simulations The benchmarking mode disables all file output (and also skips all imports of ````). Setups -^^^^^^^^^^ +------- If you are interested, you can view the used configurations here: @@ -103,7 +109,7 @@ If you are interested, you can view the used configurations here: :download:`tohoku1000.json <../../_static/text/tohoku1000.json>` Results -^^^^^^^^^^ +-------- .. list-table:: execution times on different devices :header-rows: 1 @@ -187,17 +193,18 @@ Results and stopped after the program has finished and all memory has been freed. Observations -^^^^^^^^^^^^^^ +-------------- In every scenario, ARA had a faster setup time but slower computation times. We conclude that ARA has faster data/file access (because the setup heavily depends on data reading speed from a file) while the private PC seems to have better single core performance. +************** 8.2 Compilers -=============== +************** 8.2.1 - Generic compiler support ---------------------------------- +================================= We enabled generic compiler support by adding the following lines to our ``SConstruct`` file @@ -221,10 +228,10 @@ Now, scons can be invoked with a compiler of choice, for example by running CXX=icpc scons 8.2.2 & 8.2.3 - Test runs --------------------------- +=========================== Time measurements -^^^^^^^^^^^^^^^^^^^^^^^^^ +------------------ For each run, we used the following configuration: @@ -313,7 +320,7 @@ We therefore ended up using ``compiler/intel/2018-Update1`` and ``gcc (GCC) 4.8. This configuration was the only one that worked for us, as we did not manage to fix all the errors that were thrown at us. Observations from the table -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------- As one would intuitively expect, the higher the optimization level is, the quicker the process finished. @@ -328,7 +335,7 @@ We would also need to ensure that there are no other intensive processes running Nonetheless, by using the table as a rough estimate it seems that ``g++`` is faster when using ``-O0`` and ``-Ofast`` while ``icpc`` is preferable for ``-O2``. 8.2.3 - Optimization flags ---------------------------- +=========================== To allow for an easy switch between optimization flag, we added following code to our SConstruct: @@ -355,7 +362,7 @@ and env.Append( CXXFLAGS = [ env['opt'] ] ) The dangers of -Ofast -^^^^^^^^^^^^^^^^^^^^^^^ +---------------------- One of the options that ``-Ofast`` enables is ``-ffast-math``. With that, a whole lot of other options get activated as well, such as @@ -386,7 +393,7 @@ and ``_ 8.2.4 - Compiler reports ------------------------- +========================= We added the support for a compiler report flag with the following lines in our ``SConstruct`` @@ -435,7 +442,7 @@ This snippet refers to the loops that provide our solver with data from a setup: } F-Wave optimization report -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +--------------------------- The full report can be found :download:`here. <../../_static/text/task8-2-4_fwave_optrpt.txt>` @@ -484,7 +491,7 @@ For ``netUpdates``, the report tells us that We can conclude that the compiler is able to inline our calls to ``computeEigenvalues`` and ``computeEigencoefficients``. WavePropagation2d optimization report -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +-------------------------------------- The full report can be found :download:`here. <../../_static/text/task8-2-4_waveprop2d_optrpt.txt>` @@ -514,12 +521,12 @@ could not be vectorized: Lines 86 and 88 are the two for-loops for y- and x-axis of the x-sweep and lines 152 and 154 are the two for-loops for y- and x-axis of the y-sweep. - +********************************************* 8.3 Instrumentation and Performance Counters -============================================== +********************************************* 8.3.1 to 8.3.4 - VTune ------------------------ +======================= First we used the gui of Intel vTune to specify our reports. @@ -542,7 +549,7 @@ Then the following batch script was used to run the hotspots measurement: /cluster/intel/vtune_profiler_2020.2.0.610396/bin64/vtune -collect hotspots -app-working-dir /beegfs/xe63nel/tsunami_lab/build -- /beegfs/xe63nel/tsunami_lab/build/tsunami_lab ../configs/config.json Hotspots -^^^^^^^^^^ +--------- .. image:: ../../_static/assets/task_8-3-1_hotspot_bottomUp.png @@ -564,7 +571,7 @@ It was interesting to see (although it should not come as a surprise) that the ` of the CPU time. Threads -^^^^^^^^^^ +-------- .. image:: ../../_static/assets/task_8-3-1_threads.png @@ -573,10 +580,10 @@ Threads The poor result for the thread report was also expected, because we only compute sequentially. 8.3.5 - Code optimizations ---------------------------- +=========================== TsunamiEvent2d speedup -^^^^^^^^^^^^^^^^^^^^^^^ +----------------------- In order to increase the speed of this setup, we introduced a variable ``lastnegativeIndex`` for the X and Y direction for the bathymetry and displacement. The idea is the following: @@ -634,7 +641,7 @@ Code snippets of the implementation: } F-Wave solver optimization -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------- In ``computeEigencoefficients``, we changed @@ -677,7 +684,7 @@ Furthermore, we established a constant for :code:`t_real(0.5) * m_g`: Coarse Output optimization -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------- Inside the ``write()`` function in ``NetCdf.cpp`` we calculated @@ -699,8 +706,9 @@ once and then reuse it wherever we need it: This way, the division only happens once. +************************ Individual phase ideas -======================== +************************ For the individual phase, we plan on building a graphical user interface using `ImGui `_. diff --git a/docs/source/files/assignments/09.rst b/docs/source/files/assignments/09.rst index e651c553..5697ff51 100644 --- a/docs/source/files/assignments/09.rst +++ b/docs/source/files/assignments/09.rst @@ -1,11 +1,13 @@ +================== 9. Parallelization -******************** +================== +************ 9.1 OpenMP -============ +************ 9.1.1 - Parallelization with OpenMP ----------------------------------------- +==================================== An easy way to parallelize our for loops is using @@ -22,7 +24,7 @@ example: ... 9.1.2 - Parallelization speedup ------------------------------------------- +==================================== We have used following batch script for ara: @@ -42,7 +44,8 @@ We have used following batch script for ara: And got following results: -**Without parallelization** +Without parallelization +----------------------- .. code:: text @@ -54,7 +57,8 @@ And got following results: = 1941.01 seconds = 32.3501 minutes -**With parallelization on 72 cores with 72 threads** +With parallelization on 72 cores with 72 threads +------------------------------------------------ .. code:: text @@ -72,7 +76,8 @@ And got following results: Speedup: :math:`\frac{1941}{75.5} = 25.7` -**With parallelization on 72 cores with 144 threads** +With parallelization on 72 cores with 144 threads +------------------------------------------------- .. code:: text @@ -90,7 +95,7 @@ We can see that having twice the amount of threads resulted in a much slower com We conclude that using more threads than cores results in a slowed down performance. 9.1.3 - 2D for loop parallelization ------------------------------------------- +==================================== The results from above used parallelization in the outer loop. The parallelized inner loops leads to following time: @@ -105,9 +110,10 @@ The parallelized inner loops leads to following time: It is clear, that parallelizing the outer loop is more effficient. 9.1.4 - Pinning and Scheduling ------------------------------------------- +=============================== -**Scheduling** +Scheduling +---------- The upper implementation used the basic :code:`scheduling(static)`. @@ -136,7 +142,8 @@ For :code:`scheduling(auto)` we get: = 84.5467 seconds = 1.40911 minutes -**Pinning** +Pinning +------- Using :code:`OMP_PLACES={0}:36:1` we get: diff --git a/docs/source/files/assignments/project.rst b/docs/source/files/assignments/project.rst index 2c0ee1c0..4e4236ad 100644 --- a/docs/source/files/assignments/project.rst +++ b/docs/source/files/assignments/project.rst @@ -1,112 +1,148 @@ +################### 10. Project Phase -******************** +################### -In the project phase we decided to implement a userfriendly Gui. The aim is to make the usage of our Tsunami solver +In the project phase we decided to implement a user-friendly Gui. The aim was to make the usage of our Tsunami solver as easy and interactive as possible. -GUI -=========== +********************* +Preface +********************* -.. image:: ../../_static/assets/task-10-Gui_help.png +While we tried to stick to our plan as best as we could, we decided to do some things a little different. +The biggest difference being a Client-Server approach although we explicitly mentioned we would not realize this. +The original triggering point for this were all kinds of loading times and issues with concurrency: +for any GUI application, one of the key features is its responsiveness to user input. Be it numerical input or just clicking and moving things around - the GUI should react as fast as possible. +Integrating a GUI directly into the solver application while maintaining this responsiveness meant that the GUI and every other task the actual solver does, need to be separated in different threads +to assure that the GUI thread has always time for the user. -For the layout we chose to use multiple tabs, in order to make the use more clear. -The first page gives general informations about the project and refers to our website. +We started off with this directly integrated and threaded approach and quickly realized that handling correct communication between two or more asynchronous running threads was getting closer and closer to just having two separate applications communicating over an API. +Knowing that this approach meant we would have to invest a lot more effort and work overtime, we decided on taking this path nontheless as it would come with a lot of other benefits as well. +Now, using the Server-Client model we have a GUI that can just send commands to the server without needing to wait for a response (most of the time, unless you're specifically asking for data such as water/bathymetry data). -.. image:: ../../_static/assets/task-10-Gui_connectivity.png +It can also be used in scenarios when using a more powerful machine (e.g. HPC-Cluster) without display access. Just start the program as a server on the remote machine and control it from home via laptop - its that easy. -Tab number two handles the the connection to thhe server. The properties for interacting with the server are getting set here. -Furthermore, the connection can also be disconnected here. +********************* +GUI (Client-side) +********************* -.. image:: ../../_static/assets/task-10-Gui_windows.png +********************* +Server-side +********************* -On the next page the user can find all options of configuration for the simulation. The simulation parameters like cell amount, size and offset can be set here. -In Addition to that, compiler and run time options can be found here. These contain differetn modes, compiler options, flags and usage choices. -After selecting the simulation has to be recompiled with the according button below. +The first thing we did was to modularize our main ``tsunami_lab`` program. -.. image:: ../../_static/assets/task-10-Gui_select.png +The original ``main.cpp`` had one main function that executed the whole program loop. +The code was moved to ``Simulator.cpp`` and we now have separate functions for the different parts of the program, such as ``prepareForCalculation()`` and ``runCalculation()``. +``prepareForCalculation()`` itself also calls different functions, such as ``setupFolders()``, ``loadConfiguration()``, ``constructSetup()`` and so on. +We did this to have finer control, as we can now execute single parts of the program without having to run the whole simulation. -The last tab contains further actions to interact with the simulation. First, the simulation can be started or killed here. -Also files for the bathymetry and displacement can be chosen. As an addition, the user can get data like the heigth from the simulation. +The next step was to implement a server that can be controlled via a GUI. The code for this can be found in ``Server.cpp``. +If the program is started with the ``server`` flag, it will start a server on the local machine and wait for connections. +Once a connection is established, the server will wait for commands from the client and execute them. +To not iterate through all message keys to find the right one, a message contains extra information such as urgency and response expectation. +The actual simulation is started by sending a ``START_SIMULATION`` message to the server. +We then use an ``std::thread`` to run the simulation in a separate thread, so the server can still receive commands while the simulation is running. +Note that only one simulation thread can be running at a time, but the GUI also provides an option to kill the current thread. +********************* +Compiling +********************* -Communicator -============================= +SConstruct +====================== -For communication between simulation and the gui Luca-Philipp introduced a communication library. -The **Communicator.cpp** library can be used to easily create a client-server TCP connection and handle its communication and logging. +As a base four our GUI, we decided to use OpenGL, as it is one of or maybe even the most widely compatible and commonly known graphics standard. +We chose the `GLFW `_ implementation as it is cross-platform and also very well known. -**communicator_api** +Currently we support building the sources on Linux, MacOS and Windows and therefore had to implement different include processes for each platform: -Since all communication happens using text over TCP, we had to implement a structure that both server and client can adhere to -in order to guarantee correct communication. For this, we decided to send all data in JSON format. Furthermore, each message -follows a strict pattern. It consists of 3 parts: the TYPE which provides information on the nature of the message, -the KEY which is a unique identifier for each message, and ARGS containing other message data (such as parameters) in JSON format. +.. code-block:: python -.. code:: cpp + if 'yes' in env['gui']: + if OS == "Linux": + if not conf.CheckLib('glfw'): + print ('Did not find the glfw library!') + exit(1) + elif not conf.CheckLib('GL'): + print ('Did not find the GL library!') + exit(1) - enum MessagePart - { - TYPE, - KEY, - ARGS - }; + elif OS == "Darwin": + if not conf.CheckLib('glfw'): + print ('Did not find the glfw library!') + exit(1) - enum MessageType - { - SERVER_CALL, - FUNCTION_CALL, - OTHER, - SERVER_RESPONSE - }; + elif OS == "Windows": + if not conf.CheckLib('glfw3'): + print ('Did not find the glfw3 library!') + exit(1) + elif not conf.CheckLib('gdi32'): + print ('Did not find the gdi32 library!') + exit(1) + elif not conf.CheckLib('opengl32'): + print ('Did not find the opengl32 library!') + exit(1) + elif not conf.CheckLib('imm32'): + print ('Did not find the imm32 library!') + exit(1) - struct Message - { - MessageType type = MessageType::OTHER; - std::string key = "NONE"; - json args = ""; - }; +We also added the ImGui and ImPlot sources to the build path. -It also implements various functions to convert from and to json format, such as ``messageToJson()``, ``messageToJsonString()`` -and ``jsonToMessage()``. +If ``gui=no`` is set in the compile command, the GUI will not be built (therefore all GUI libraries ignored) and the program can be run normally using ``./tsunami_lab``. -**Communicator.hpp** +Known error when building documentary +====================================== -The library provides functions to start server and client and to send messages to each other. -Transmitted data is logged and can be retrieved via ``getLog()``. -Describing the detailed code here would be too extensive and not necessary, as this library -is rather just a tool for us to develop the actually interesting code. +When building the documentary, the following error may occur: -**Communication example** +.. code-block:: bash -``communicator_api.h`` + home/lpmg/tsunami_lab/docs/source/files/namespaces/lib.rst:6: WARNING: Error when parsing function declaration. + If the function has no return type: + Error in declarator or parameters-and-qualifiers + Invalid C++ declaration: Expected identifier in nested name. [error at 50] + NLOHMANN_JSON_SERIALIZE_ENUM (MessageExpectation, {{NO_RESPONSE, "no_response"}, {EXPECT_RESPONSE, "expect_response"}}) + --------------------------------------------------^ -.. code:: cpp + [...] - inline const Message START_SIMULATION = {MessageType::SERVER_CALL, "start_simulation"}; +This is because the C++ parser does know now about this macro and therefore identifies it as wrong syntax. +However the code is correct `(view the documentation here) `_ +and we have not found away to supress this message. The error does not seem to be on our side, which is why there will be no fix for this. +The documentation should still build correctly. -Client-side usage: +********************* +Libraries +********************* -.. code:: cpp +To keep the main code tidy, we decided to export most of the code associated with communication to external libraries: +the Communicator and the API. The code itself is already extensively documented, so we will only give a brief overview here. - xlpmg::Communicator m_communicator; - m_communicator.startClient(IPADDRESS, PORT) +.. note:: + + The Communicator itself and the API do not depend on eachother, so the Communicator can be used without the API and vice versa. + The Communicator provides functionality for sending text over TCP and the API provides a structure for the messages that are sent. + That is why messages from the API need to be converted to text before they can be sent. - [...] +Communicator +===================== - xlpmg::Message startSimMsg = xlpmg::START_SIMULATION; - m_communicator.sendToServer(messageToJsonString(startSimMsg)); +For communication between simulation and the GUI we implemented a communication library. +The **Communicator.hpp** library can be used to easily create a client-server TCP connection and handle its communication and logging. +Both the server and client use the same library, but call different functions to initialize the connection and send/receive messages. -Server-side usage: +There are also features such as different log messages with time stamps or automatic buffered sending if the message exceeds the buffer size. +All this code is hidden behind a simple interface, so the actual code stays clean and easy to read. -.. code:: cpp +Communicator API +===================== - xlpmg::Communicator m_communicator; - m_communicator.startServer(m_PORT); +(**File: communicator_api.h**) - // listen for key +Since all communication happens using text over TCP, we had to implement a structure that both server and client can adhere to +in order to guarantee correct communication. For this, we decided to send all data in JSON format and a ``Message`` struct. -Server -============ -TODO \ No newline at end of file +.. note:: For further information, see :ref:`ns-lib` \ No newline at end of file diff --git a/docs/source/files/namespaces/calculations.rst b/docs/source/files/namespaces/calculations.rst index f7e3a4e7..9c5bfd3b 100644 --- a/docs/source/files/namespaces/calculations.rst +++ b/docs/source/files/namespaces/calculations.rst @@ -5,6 +5,4 @@ Calculations .. doxygennamespace:: tsunami_lab::calculations :project: tsunami_lab - :private-members: - :content-only: - :no-link: \ No newline at end of file + :private-members: \ No newline at end of file diff --git a/docs/source/files/namespaces/io.rst b/docs/source/files/namespaces/io.rst index cdbcb4db..595eafea 100644 --- a/docs/source/files/namespaces/io.rst +++ b/docs/source/files/namespaces/io.rst @@ -5,6 +5,4 @@ I/O .. doxygennamespace:: tsunami_lab::io :project: tsunami_lab - :private-members: - :content-only: - :no-link: \ No newline at end of file + :private-members: \ No newline at end of file diff --git a/docs/source/files/namespaces/lib.rst b/docs/source/files/namespaces/lib.rst new file mode 100644 index 00000000..d7b5cb35 --- /dev/null +++ b/docs/source/files/namespaces/lib.rst @@ -0,0 +1,8 @@ +.. _ns-lib: + +lib +========== + +.. doxygenfile:: communicator_api.h + +.. doxygenclass:: xlpmg::Communicator \ No newline at end of file diff --git a/docs/source/files/namespaces/patches.rst b/docs/source/files/namespaces/patches.rst index 5095d1c8..d1078b52 100644 --- a/docs/source/files/namespaces/patches.rst +++ b/docs/source/files/namespaces/patches.rst @@ -5,6 +5,4 @@ Patches .. doxygennamespace:: tsunami_lab::patches :project: tsunami_lab - :private-members: - :content-only: - :no-link: \ No newline at end of file + :private-members: \ No newline at end of file diff --git a/docs/source/files/namespaces/setups.rst b/docs/source/files/namespaces/setups.rst index df9504db..38726174 100644 --- a/docs/source/files/namespaces/setups.rst +++ b/docs/source/files/namespaces/setups.rst @@ -6,6 +6,4 @@ Setups .. doxygennamespace:: tsunami_lab::setups :project: tsunami_lab :private-members: - :content-only: - :no-link: diff --git a/docs/source/files/namespaces/solvers.rst b/docs/source/files/namespaces/solvers.rst index 9a648469..71d9ad74 100644 --- a/docs/source/files/namespaces/solvers.rst +++ b/docs/source/files/namespaces/solvers.rst @@ -6,5 +6,3 @@ Solvers .. doxygennamespace:: tsunami_lab::solvers :project: tsunami_lab :private-members: - :content-only: - :no-link: diff --git a/docs/source/files/namespaces/ui.rst b/docs/source/files/namespaces/ui.rst index ac932cfa..0ab4af03 100644 --- a/docs/source/files/namespaces/ui.rst +++ b/docs/source/files/namespaces/ui.rst @@ -5,6 +5,4 @@ UI .. doxygennamespace:: tsunami_lab::ui :project: tsunami_lab - :private-members: - :content-only: - :no-link: \ No newline at end of file + :private-members: \ No newline at end of file diff --git a/lib/xlpmg/Communicator.hpp b/lib/xlpmg/Communicator.hpp index 4cf2ad80..f7219c97 100644 --- a/lib/xlpmg/Communicator.hpp +++ b/lib/xlpmg/Communicator.hpp @@ -4,8 +4,8 @@ * # Description * Library to easily create a client-server connection and handle its communication and logging. **/ -#ifndef COMMUNICATOR_H -#define COMMUNICATOR_H +#ifndef XLPMG_COMMUNICATOR_H +#define XLPMG_COMMUNICATOR_H #include #include @@ -27,28 +27,38 @@ namespace xlpmg class Communicator { private: - int TIMEOUT = 2; + // Log data for storing communication logs std::string logData = ""; + // Client socket related variables int sockStatus, sockValread, sockClient_fd = -1; + // Server socket related variables int server_fd, new_socket; + /** + * @enum LogType + * @brief Defines different types of log messages that can be logged during communication. + */ enum LogType { - SENT, - RECEIVED, - ERROR, - INFO, - DEBUG + SENT, // Sent message log type + RECEIVED, // Received message log type + ERROR, // Error log type + INFO, // Info log type + DEBUG // Debug log type }; - /// @brief Adds a string to the log with correct formatting. - /// @param message string to add to the log. - /// @param logtype @see xlpmg::Communicator::LogType - /// @param log Whether the message should be logged or not. - /// @param logtype type of message. + /** + * @brief Adds a string to the log with correct formatting. + * @param message string to add to the log. + * @param logtype xlpmg::Communicator::LogType + * @param log Whether the message should be logged or not. + * @param replaceLastLine Whether the last line should be replaced or not. + * @see xlpmg::Communicator::LogType + */ void logEvent(std::string message, LogType logtype, bool log, bool replaceLastLine = false) { - if(!log){ + if (!log) + { return; } @@ -101,25 +111,60 @@ namespace xlpmg } } + /** + * Sets the send timeout for a socket. + * @param socket The socket to set the send timeout for. + * @param timeout The timeout value in seconds. + */ + void setSendTimeout(int socket, long timeout){ + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(struct timeval)); + } + + /** + * Sets the receive timeout for a socket. + * @param socket The socket to set the receive timeout for. + * @param timeout The timeout value in seconds. + */ + void setRecvTimeout(int socket, long timeout){ + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)); + } + public: + // Timeout value for normal socket operations in seconds + static const long TIMEOUT = 2; //! default size of the reading buffer - const unsigned int BUFF_SIZE_READ_DEFAULT = 8096; + unsigned int BUFF_SIZE_READ_DEFAULT = 8096; //! actual size of the reading buffer unsigned int BUFF_SIZE_READ = BUFF_SIZE_READ_DEFAULT; - //! default size of the sending buffer - const unsigned int BUFF_SIZE_SEND_DEFAULT = 8096; + unsigned int BUFF_SIZE_SEND_DEFAULT = 8096; //! actual size of the sending buffer unsigned int BUFF_SIZE_SEND = BUFF_SIZE_SEND_DEFAULT; //! true if there is a connection bool isConnected = false; + /** + * @brief Sets the read buffer size. + * + * @param newSize The new size of the read buffer. + */ void setReadBufferSize(unsigned int newSize) { BUFF_SIZE_READ = newSize; } + /** + * @brief Sets the send buffer size. + * + * @param newSize The new size of the send buffer. + */ void setSendBufferSize(unsigned int newSize) { BUFF_SIZE_SEND = newSize; @@ -129,15 +174,14 @@ namespace xlpmg // CLIENT // //////////////////// - /// @brief Creates a client socket and searches for a server. - /// @param PORT Port for communication with server. - /// @return 0 on success, -1 for errors. + /** + * @brief Creates a client socket and searches for a server. + * @param PORT Port for communication with server. + * @return 0 on success, -1 for errors. + */ int startClient(char *IPADDRESS, int PORT) { struct sockaddr_in serv_addr; - struct timeval tv; - tv.tv_sec = TIMEOUT; - tv.tv_usec = 0; if ((sockClient_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { @@ -147,8 +191,8 @@ namespace xlpmg } logEvent("Socket created.", INFO, true); - setsockopt(sockClient_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)); - setsockopt(sockClient_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(struct timeval)); + setSendTimeout(sockClient_fd, TIMEOUT); + setRecvTimeout(sockClient_fd, TIMEOUT); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); @@ -175,7 +219,9 @@ namespace xlpmg return sockStatus; } - /// @brief Stops all connections of the client socket. + /** + * @brief Stops all connections of the client socket. + */ void stopClient() { // closing the connected socket @@ -183,10 +229,13 @@ namespace xlpmg isConnected = false; } - /// @brief Receives a message from the server. - /// @return Message as string. - /// @param log Whether the message should be logged or not. - std::string receiveFromServer(bool log = true) + /** + * @brief Receives a message from the server. + * @return Message as string. + * @param timeout Timeout for the operation in seconds. + * @param log Whether the message should be logged or not. + */ + std::string receiveFromServer(long timeout = TIMEOUT, bool log = true) { if (sockClient_fd < 0) { @@ -199,6 +248,8 @@ namespace xlpmg bool finished = false; unsigned long totalBytes = 0; + setRecvTimeout(sockClient_fd, timeout); + logEvent(std::to_string(totalBytes) + " Bytes (" + std::to_string(totalBytes / 1000000) + " MB) received ", DEBUG, log); while (!finished) @@ -237,35 +288,19 @@ namespace xlpmg finished = true; } } + setRecvTimeout(sockClient_fd, TIMEOUT); isConnected = true; return message; } - /// @brief Checks if the server responded with OK. - /// @return true if server responded - bool checkServerResponse() - { - // char readBuffer[BUFF_SIZE_READ]; - // memset(readBuffer, 0, BUFF_SIZE_READ); - // sockValread = read(sockClient_fd, readBuffer, - // BUFF_SIZE_READ - 1); // subtract 1 for the null - // bool returnValue = true; - // if (std::string(readBuffer).compare("OK") != 0) - // returnValue = false; - - // // DONE - // sockValread = read(sockClient_fd, readBuffer, - // BUFF_SIZE_READ - 1); // subtract 1 for the null - // if (std::string(readBuffer).compare("DONE") != 0) - // returnValue = false; - - return true; - } - - /// @brief Sends a message to the server. - /// @param message String to send. - /// @param log Whether the message should be logged or not. - int sendToServer(std::string message, bool log = true) + /** + * @brief Sends a message to the server. + * @param message String to send. + * @param timeout Timeout for the operation in seconds. + * @param log Whether the message should be logged or not. + * @return 0 if successful, 1 otherwise. + */ + int sendToServer(std::string message, long timeout = TIMEOUT, bool log = true) { if (sockClient_fd < 0) { @@ -273,6 +308,9 @@ namespace xlpmg isConnected = false; return 1; } + + setSendTimeout(sockClient_fd, timeout); + // terminator message.append("#!"); @@ -308,18 +346,23 @@ namespace xlpmg logEvent(bytesSentStr.c_str(), DEBUG, log, true); } } + setSendTimeout(sockClient_fd, TIMEOUT); isConnected = true; return 0; } - /// @brief Gets the log data. - /// @param o_logData Pointer to the string which the log will be written into. + /** + * @brief Gets the log data. + * @param o_logData Pointer to the string which the log will be written into. + */ void getLog(std::string &o_logData) { o_logData = logData; } - /// @brief Clears the log data. + /** + * @brief Clears the log data. + */ void clearLog() { logData.clear(); @@ -329,8 +372,10 @@ namespace xlpmg // SERVER // //////////////////// - /// @brief Creates a server socket and waits for connections. - /// @param PORT Port for communication with client. + /** + * @brief Creates a server socket and waits for connections. + * @param PORT Port for communication with client. + */ void startServer(int PORT) { ssize_t valread; @@ -355,10 +400,13 @@ namespace xlpmg isConnected = false; exit(EXIT_FAILURE); } + address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); + setSendTimeout(new_socket, TIMEOUT); + // Forcefully attaching socket to the port 8080 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) @@ -383,7 +431,9 @@ namespace xlpmg isConnected = true; } - /// @brief Stops all connections of the server. + /** + * @brief Stops all connections of the server. + */ void stopServer() { // closing the connected socket @@ -394,9 +444,11 @@ namespace xlpmg isConnected = false; } - /// @brief Receives a message froma client. - /// @return Message as string. - /// @param log Whether the message should be logged or not. + /** + * @brief Receives a message from a client. + * @return Message as string. + * @param log Whether the message should be logged or not. + */ std::string receiveFromClient(bool log = true) { if (new_socket < 0) @@ -443,9 +495,11 @@ namespace xlpmg return message; } - /// @brief Sends a message to a client. - /// @param message Message to send. - /// @param log Whether the message should be logged or not. + /** + * @brief Sends a message to a client. + * @param message Message to send. + * @param log Whether the message should be logged or not. + */ void sendToClient(std::string message, bool log = true) { // terminator @@ -485,4 +539,4 @@ namespace xlpmg } }; } -#endif \ No newline at end of file +#endif diff --git a/lib/xlpmg/communicator_api.h b/lib/xlpmg/communicator_api.h index d8633fd4..83d58fd1 100644 --- a/lib/xlpmg/communicator_api.h +++ b/lib/xlpmg/communicator_api.h @@ -2,45 +2,78 @@ * @author Luca-Philipp Grumbach * * @brief Communicator API. - * + * + * # Description * This file provides an interface with pre-defined * messages to enable a correct transmission of * information between client and server. **/ + #ifndef XLPMG_COMMUNICATOR_API_H #define XLPMG_COMMUNICATOR_API_H #include #include - #include namespace xlpmg { + /** + * @brief Enum representing the different parts of a message. + * + * The MessagePart enum defines the different parts of a message, including the expectation, urgency, key, and arguments. + */ enum MessagePart { - TYPE, + EXPECTATION, + URGENCY, KEY, ARGS }; - enum MessageType + enum MessageExpectation { - SERVER_CALL, - FUNCTION_CALL, - OTHER, - SERVER_RESPONSE + NO_RESPONSE, + EXPECT_RESPONSE }; - NLOHMANN_JSON_SERIALIZE_ENUM(MessageType, {{SERVER_CALL, "server_call"}, - {FUNCTION_CALL, "function_call"}, - {OTHER, "other"}, - {SERVER_RESPONSE, "server_response"}}); + enum MessageUrgency + { + CRITICAl, + HIGH, + MEDIUM, + LOW + }; + + /** + * @brief Macro to briefly define a mapping between MessageExpectation enum and JSON + */ + NLOHMANN_JSON_SERIALIZE_ENUM(MessageExpectation, {{NO_RESPONSE, "no_response"}, + {EXPECT_RESPONSE, "expect_response"}}); + + /** + * @brief Macro to briefly define a mapping between MessageUrgency enum and JSON + */ + NLOHMANN_JSON_SERIALIZE_ENUM(MessageUrgency, {{CRITICAl, "critical"}, + {HIGH, "high"}, + {MEDIUM, "medium"}, + {LOW, "low"}}); + /** + * Struct representing a message in the communicator API. + * + * # Description + * This struct contains information about the urgency, key, and arguments of a message. + */ struct Message { - MessageType type = MessageType::OTHER; + // The expectation of the message. + MessageExpectation expectation = MessageExpectation::NO_RESPONSE; + // The urgency of the message. + MessageUrgency urgency = MessageUrgency::MEDIUM; + // The key associated with the message. std::string key = "NONE"; + // The arguments of the message. json args = ""; }; @@ -53,7 +86,8 @@ namespace xlpmg json messageToJson(Message i_message) { json msg; - msg[MessagePart::TYPE] = i_message.type; + msg[MessagePart::EXPECTATION] = i_message.expectation; + msg[MessagePart::URGENCY] = i_message.urgency; msg[MessagePart::KEY] = i_message.key; msg[MessagePart::ARGS] = i_message.args; return msg; @@ -79,98 +113,104 @@ namespace xlpmg Message jsonToMessage(json i_json) { Message l_message; - l_message.type = i_json.at(MessagePart::TYPE); + l_message.expectation = i_json.at(MessagePart::EXPECTATION); + l_message.urgency = i_json.at(MessagePart::URGENCY); l_message.key = i_json.at(MessagePart::KEY); l_message.args = i_json.at(MessagePart::ARGS); return l_message; } - //! should not not induce any functionality and is only used to check if the other side responds - inline const Message CHECK = {MessageType::SERVER_CALL, "XCHECKX"}; + ///////////////////////////////// + // NO_RESPONSE // + ///////////////////////////////// - //! Will provide information on the simulation - inline const Message GET_SIMULATION_STATS= {MessageType::SERVER_CALL, "get_simulation_stats"}; + // CRITICAL + //! Server will stop the running simulation. + inline const Message KILL_SIMULATION = {MessageExpectation::NO_RESPONSE, MessageUrgency::CRITICAl, "kill_simulation"}; + //! Tells the Simulator to write a checkpoint. + inline const Message WRITE_CHECKPOINT = {MessageExpectation::NO_RESPONSE, MessageUrgency::CRITICAl, "write_checkpoint"}; + //! Pauses a simulation. + inline const Message PAUSE_SIMULATION = {MessageExpectation::NO_RESPONSE, MessageUrgency::CRITICAl, "pause_simulation"}; //! Tells the server to shutdown. - inline const Message SHUTDOWN_SERVER = {MessageType::SERVER_CALL, "shutdown_server"}; + inline const Message SHUTDOWN_SERVER = {MessageExpectation::NO_RESPONSE, MessageUrgency::CRITICAl, "shutdown_server"}; - //! Tells the server to restart. - inline const Message START_SIMULATION = {MessageType::SERVER_CALL, "start_simulation"}; + // HIGH - //! Server will stop the running simulation. - inline const Message KILL_SIMULATION = {MessageType::SERVER_CALL, "kill_simulation"}; + //! Should not not induce any functionality and is only used to check if the other side responds + inline const Message CHECK = {MessageExpectation::NO_RESPONSE, MessageUrgency::HIGH, "CHECK"}; + //! Tells the server to start the simulator. + inline const Message START_SIMULATION = {MessageExpectation::NO_RESPONSE, MessageUrgency::HIGH, "start_simulation"}; + //! Continues a simulation. + inline const Message CONTINUE_SIMULATION = {MessageExpectation::NO_RESPONSE, MessageUrgency::HIGH, "continue_simulation"}; + //! Tells the Simulator to reset. + inline const Message RESET_SIMULATOR = {MessageExpectation::NO_RESPONSE, MessageUrgency::HIGH, "reset_simulator"}; + //! Tells the Simulator to toggle file i/o usage to given argument. + inline const Message TOGGLE_FILEIO = {MessageExpectation::NO_RESPONSE, MessageUrgency::HIGH, "toggle_fileio"}; - //! Server will recompile with provided arguments. - inline const Message COMPILE = {MessageType::SERVER_CALL, "compile", ""}; + // MEDIUM + //! Server will recompile with provided arguments. + inline const Message COMPILE = {MessageExpectation::NO_RESPONSE, MessageUrgency::MEDIUM, "compile", ""}; //! Server will recompile with provided arguments and run using a bash script. - inline const Message COMPILE_RUN_BASH = {MessageType::SERVER_CALL, "compile_run_bash", ""}; - + inline const Message COMPILE_RUN_BASH = {MessageExpectation::NO_RESPONSE, MessageUrgency::MEDIUM, "compile_run_bash", ""}; //! Server will recompile with provided arguments and run using an sbatch script. - inline const Message COMPILE_RUN_SBATCH = {MessageType::SERVER_CALL, "compile_run_sbatch", ""}; - - //! For sending a file to the server - inline const Message SEND_FILE = {MessageType::SERVER_CALL, "send_file"}; + inline const Message COMPILE_RUN_SBATCH = {MessageExpectation::NO_RESPONSE, MessageUrgency::MEDIUM, "compile_run_sbatch", ""}; + //! Deletes checkpoints. + inline const Message DELETE_CHECKPOINTS = {MessageExpectation::NO_RESPONSE, MessageUrgency::MEDIUM, "delete_checkpoints"}; + //! Deletes stations. + inline const Message DELETE_STATIONS = {MessageExpectation::NO_RESPONSE, MessageUrgency::MEDIUM, "delete_stations"}; + //! Sets the cell amount of the simulation. + inline const Message SET_OFFSET = {MessageExpectation::NO_RESPONSE, MessageUrgency::MEDIUM, "set_offset"}; + //! Sets the offset of the simulation. + inline const Message SET_CELL_AMOUNT = {MessageExpectation::NO_RESPONSE, MessageUrgency::MEDIUM, "set_cell_amount"}; - //! For receiving a file from the server - inline const Message RECV_FILE = {MessageType::SERVER_CALL, "recv_file"}; + // LOW + //! For sending a file to the server + inline const Message SEND_FILE = {MessageExpectation::NO_RESPONSE, MessageUrgency::LOW, "send_file"}; + //! Tells the Simulator to load config from json data. + inline const Message LOAD_CONFIG_JSON = {MessageExpectation::NO_RESPONSE, MessageUrgency::LOW, "load_config_json"}; + //! Tells the Simulator to load config from .json config file. + inline const Message LOAD_CONFIG_FILE = {MessageExpectation::NO_RESPONSE, MessageUrgency::LOW, "load_config_file"}; //! Tells the server to change the read buffer size. - inline const Message SET_READ_BUFFER_SIZE = {MessageType::SERVER_CALL, "set_read_buffer_size"}; - - //! Tells the server to change the send buffer size. - inline const Message SET_SEND_BUFFER_SIZE = {MessageType::SERVER_CALL, "set_send_buffer_size"}; - - //! Simulator will reset. - inline const Message RESET_SIMULATOR = {MessageType::FUNCTION_CALL, "reset_simulator"}; - - //! Simulator will write a checkpoint. - inline const Message WRITE_CHECKPOINT = {MessageType::FUNCTION_CALL, "write_checkpoint"}; - - //! Simulator will load config from json data. - inline const Message LOAD_CONFIG_JSON = {MessageType::FUNCTION_CALL, "load_config_json"}; + inline const Message SET_READ_BUFFER_SIZE = {MessageExpectation::NO_RESPONSE, MessageUrgency::MEDIUM, "set_read_buffer_size"}; + //! Tells the server to change the send buffer size. + inline const Message SET_SEND_BUFFER_SIZE = {MessageExpectation::NO_RESPONSE, MessageUrgency::MEDIUM, "set_send_buffer_size"}; - //! Simulator will load config from .json config file. - inline const Message LOAD_CONFIG_FILE = {MessageType::FUNCTION_CALL, "load_config_file"}; + //////////////////////////////// + // EXPECT_RESPONSE // + //////////////////////////////// - //! Simulator will toggle file i/o usage to given arg. - inline const Message TOGGLE_FILEIO = {MessageType::FUNCTION_CALL, "toggle_fileio"}; + // CRITICAL //! Returns the current timestep from the simulator. - inline const Message GET_TIME_VALUES = {MessageType::FUNCTION_CALL, "get_time_values"}; - + inline const Message GET_TIME_VALUES = {MessageExpectation::EXPECT_RESPONSE, MessageUrgency::CRITICAl, "get_time_values"}; + //! Gets system info such as CPU and RAM usage. + inline const Message GET_SYSTEM_INFORMATION = {MessageExpectation::EXPECT_RESPONSE, MessageUrgency::CRITICAl, "get_system_information"}; //! Returns the current simulation sizes from the simulator. - inline const Message GET_SIMULATION_SIZES = {MessageType::FUNCTION_CALL, "get_simulation_sizes"}; + inline const Message GET_SIMULATION_SIZES = {MessageExpectation::EXPECT_RESPONSE, MessageUrgency::CRITICAl, "get_simulation_sizes"}; + + // HIGH //! Tells the server to start sending height data. (buffered) - inline const Message GET_HEIGHT_DATA = {MessageType::FUNCTION_CALL, "get_height_data"}; + inline const Message GET_HEIGHT_DATA = {MessageExpectation::EXPECT_RESPONSE, MessageUrgency::HIGH, "get_height_data"}; + //! Tells the server to start sending bathymetry data. (buffered) + inline const Message GET_BATHYMETRY_DATA = {MessageExpectation::EXPECT_RESPONSE, MessageUrgency::HIGH, "get_bathymetry_data"}; - //! Tells the server to start sending bathymetry data. (buffered) - inline const Message GET_BATHYMETRY_DATA = {MessageType::FUNCTION_CALL, "get_bathymetry_data"}; + // MEDIUM - //! Sets the cell amount of the simulation. - inline const Message SET_OFFSET = {MessageType::FUNCTION_CALL, "set_offset"}; + //! For receiving a file from the server + inline const Message RECV_FILE = {MessageExpectation::EXPECT_RESPONSE, MessageUrgency::LOW, "recv_file"}; - //! Sets the offset of the simulation. - inline const Message SET_CELL_AMOUNT = {MessageType::FUNCTION_CALL, "set_cell_amount"}; + //////////////////////////////// + // SERVER_RESPONSE // + //////////////////////////////// //! Tells the client that a buffered sending operation has finished. - inline const Message BUFFERED_SEND_FINISHED = {MessageType::SERVER_RESPONSE, "buff_send_finished"}; - - //! Deletes checkpoints. - inline const Message DELETE_CHECKPOINTS = {MessageType::SERVER_CALL, "delete_checkpoints"}; - - //! Deletes stations. - inline const Message DELETE_STATIONS = {MessageType::SERVER_CALL, "delete_stations"}; - - //! pause simulation - inline const Message PAUSE_SIMULATION = {MessageType::SERVER_CALL, "pause_simulation"}; - - //! continue simulation - inline const Message CONTINUE_SIMULATION = {MessageType::SERVER_CALL, "continue_simulation"}; - - //! gets the system info such as CPU and RAM usage - inline const Message GET_SYSTEM_INFORMATION = {MessageType::SERVER_CALL, "get_system_information"}; + inline const Message BUFFERED_SEND_FINISHED = {MessageExpectation::NO_RESPONSE, MessageUrgency::CRITICAl, "buff_send_finished"}; + //! Server response template + inline const Message SERVER_RESPONSE = {MessageExpectation::NO_RESPONSE, MessageUrgency::CRITICAl, "server_response"}; } #endif \ No newline at end of file diff --git a/src/Server.cpp b/src/Server.cpp index e58b461d..16dac8f7 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -16,14 +16,11 @@ std::thread m_updateThread; std::atomic m_stopUpdating = false; bool m_isSimulationRunning = false; -// updating +// last update time point std::chrono::time_point m_lastDataUpdate = std::chrono::high_resolution_clock::now(); +// data update frequency in ms int m_dataUpdateFrequency = 10; -// temp files -std::string m_bathTempFile = "bathymetry_temp.nc"; -std::string m_displTempFile = "displacement_temp.nc"; - // system info tsunami_lab::systeminfo::SystemInfo l_systemInfo; double l_usedRAM = 0; @@ -126,322 +123,384 @@ int main(int i_argc, char *i_argv[]) json l_parsedData = json::parse(l_rawData); xlpmg::Message l_message = xlpmg::jsonToMessage(l_parsedData); - xlpmg::MessageType l_type = l_message.type; + xlpmg::MessageExpectation l_expectation = l_message.expectation; + xlpmg::MessageUrgency l_urgency = l_message.urgency; std::string l_key = l_message.key; json l_args = l_message.args; - //-------------------------------------------// - //---------------SERVER CALLS----------------// - //-------------------------------------------// - if (l_type == xlpmg::SERVER_CALL) + ///////////////////////////////// + // NO_RESPONSE // + ///////////////////////////////// + if (l_expectation == xlpmg::NO_RESPONSE) { - if (l_key == xlpmg::CHECK.key) - { - l_communicator.sendToClient("OK"); - } - else if (l_key == xlpmg::SHUTDOWN_SERVER.key) + // CRITICAL + if (l_urgency == xlpmg::CRITICAl) { - m_EXIT = true; - exitSimulationThread(); - l_communicator.stopServer(); + if (l_key == xlpmg::KILL_SIMULATION.key) + { + exitSimulationThread(); + } + else if (l_key == xlpmg::WRITE_CHECKPOINT.key) + { + std::cout << "Writing checkpoint" << std::endl; + simulator->writeCheckpoint(); + } + + else if (l_key == xlpmg::PAUSE_SIMULATION.key) + { + std::cout << "Pause simulation" << std::endl; + simulator->setPausingStatus(true); + } + else if (l_key == xlpmg::SHUTDOWN_SERVER.key) + { + m_EXIT = true; + exitSimulationThread(); + l_communicator.stopServer(); + } } - else if (l_key == xlpmg::START_SIMULATION.key) + // HIGH + else if (l_urgency == xlpmg::HIGH) { - std::string l_config = l_args; - if (simulator->isPreparing() || simulator->isCalculating()) + if (l_key == xlpmg::CHECK.key) { - std::cout << "Warning: Did not start simulator because it is still preparing or calculating." << std::endl; + std::cout << "A client requested a check." << std::endl; } - else + else if (l_key == xlpmg::START_SIMULATION.key) { + std::string l_config = l_args; + if (simulator->isPreparing() || simulator->isCalculating()) + { + std::cout << "Warning: Did not start simulator because it is still preparing or calculating." << std::endl; + } + else + { + if (canRunThread()) + { + m_simulationThread = std::thread(&tsunami_lab::Simulator::start, simulator, l_config); + } + else + { + std::cout << "Warning: Did not start simulator because it is still running." << std::endl; + } + } + } + else if (l_key == xlpmg::CONTINUE_SIMULATION.key) + { + std::cout << "Continue simulation" << std::endl; + simulator->setPausingStatus(false); + } + else if (l_key == xlpmg::RESET_SIMULATOR.key) + { + exitSimulationThread(); if (canRunThread()) { - m_simulationThread = std::thread(&tsunami_lab::Simulator::start, simulator, l_config); + m_simulationThread = std::thread(&tsunami_lab::Simulator::resetSimulator, simulator); } else { - std::cout << "Warning: Did not start simulator because it is still running." << std::endl; + std::cout << "Warning: Could not reset because the simulation is still running." << std::endl; + } + } + else if (l_key == xlpmg::TOGGLE_FILEIO.key) + { + if (l_args == "true") + { + simulator->toggleFileIO(true); + } + else if (l_args == "false") + { + simulator->toggleFileIO(false); } } } - else if (l_key == xlpmg::KILL_SIMULATION.key) - { - exitSimulationThread(); - } - else if (l_key == xlpmg::GET_SYSTEM_INFORMATION.key) - { - xlpmg::Message l_response = {xlpmg::SERVER_RESPONSE, "system_information"}; - json l_data; - l_data["USED_RAM"] = l_usedRAM; - l_data["TOTAL_RAM"] = l_totalRAM; - l_data["CPU_USAGE"] = l_cpuUsage; - l_response.args = l_data; - l_communicator.sendToClient(xlpmg::messageToJsonString(l_response), false); - } - else if (l_key == xlpmg::COMPILE.key) + // MEDIUM + else if (l_urgency == xlpmg::MEDIUM) { - // Shutdown server - m_EXIT = true; - l_communicator.stopServer(); - exitSimulationThread(); + if (l_key == xlpmg::COMPILE.key) + { + // Shutdown server + m_EXIT = true; + l_communicator.stopServer(); + exitSimulationThread(); - std::string env = l_args.value("ENV", ""); // environment var - std::string opt = l_args.value("OPT", ""); // compiler opt + std::string env = l_args.value("ENV", ""); // environment var + std::string opt = l_args.value("OPT", ""); // compiler opt - // compile - exec("chmod +x scripts/compile-bash.sh"); - exec("./scripts/compile-bash.sh \"" + env + "\" \"" + opt + "\" &"); - } - else if (l_key == xlpmg::COMPILE_RUN_BASH.key) - { - // Shutdown server - m_EXIT = true; - l_communicator.stopServer(); - exitSimulationThread(); - - std::string l_env = l_args.value("ENV", ""); // environment var - std::string l_opt = l_args.value("OPT", ""); // compiler opt - int l_port = l_args.value("POR", 8080); - - // compile - exec("chmod +x scripts/compile-bash.sh"); - exec("./scripts/compile-bash.sh \"" + l_env + "\" \"" + l_opt + "\""); - - // run - exec("chmod +x run-bash.sh"); - exec("./run-bash.sh " + std::to_string(l_port) + " &"); - } - else if (l_key == xlpmg::COMPILE_RUN_SBATCH.key) - { - // Shutdown server - m_EXIT = true; - l_communicator.stopServer(); - exitSimulationThread(); - - std::string l_env = l_args.value("ENV", ""); // environment var - std::string l_opt = l_args.value("OPT", ""); // compiler opt - int l_port = l_args.value("POR", 8080); - - std::string l_job = l_args.value("JOB", ""); - std::string l_out = l_args.value("OUT", ""); - std::string l_err = l_args.value("ERR", ""); - std::string l_tim = l_args.value("TIM", ""); - - // compile - exec("chmod +x scripts/compile-bash.sh"); - exec("./scripts/compile-bash.sh \"" + l_env + "\" \"" + l_opt + "\""); - - // generate sbatch - exec("chmod +x scripts/generateSbatch.sh"); - exec("./scripts/generateSbatch.sh " + l_job + " " + l_out + " " + l_err + " " + l_tim + " > run-sbatch.sh"); - - // run - exec("chmod +x run-sbatch.sh"); - exec("sbatch run-sbatch.sh " + std::to_string(l_port)); - } - else if (l_key == xlpmg::SET_READ_BUFFER_SIZE.key) - { - l_communicator.setReadBufferSize(l_args); - } - else if (l_key == xlpmg::SET_SEND_BUFFER_SIZE.key) - { - l_communicator.setSendBufferSize(l_args); - } - else if (l_key == xlpmg::SEND_FILE.key) - { - std::vector l_byteVector = l_args["data"]["bytes"]; - auto l_writeFile = std::fstream(l_args.value("path", ""), std::ios::out | std::ios::binary); - l_writeFile.write((char *)&l_byteVector[0], l_byteVector.size()); - l_writeFile.close(); - } - else if (l_key == xlpmg::RECV_FILE.key) - { - std::string l_file = l_args.value("path", ""); - std::string l_fileDestination = l_args.value("pathDestination", ""); - - if (l_file.length() > 0 && l_fileDestination.length() > 0) + // compile + exec("chmod +x scripts/compile-bash.sh"); + exec("./scripts/compile-bash.sh \"" + env + "\" \"" + opt + "\" &"); + } + else if (l_key == xlpmg::COMPILE_RUN_BASH.key) { - xlpmg::Message l_response = {xlpmg::SERVER_RESPONSE, "file_data"}; - json l_arguments; - l_arguments["path"] = l_fileDestination; - - std::ifstream l_fileData(l_file, std::ios::binary); - l_fileData.unsetf(std::ios::skipws); - std::streampos l_fileSize; - l_fileData.seekg(0, std::ios::end); - l_fileSize = l_fileData.tellg(); - l_fileData.seekg(0, std::ios::beg); - std::vector vec; - vec.reserve(l_fileSize); - vec.insert(vec.begin(), - std::istream_iterator(l_fileData), - std::istream_iterator()); - l_arguments["data"] = json::binary(vec); - l_response.args = l_arguments; - l_communicator.sendToClient(xlpmg::messageToJsonString(l_response)); + // Shutdown server + m_EXIT = true; + l_communicator.stopServer(); + exitSimulationThread(); + + std::string l_env = l_args.value("ENV", ""); // environment var + std::string l_opt = l_args.value("OPT", ""); // compiler opt + int l_port = l_args.value("POR", 8080); + + // compile + exec("chmod +x scripts/compile-bash.sh"); + exec("./scripts/compile-bash.sh \"" + l_env + "\" \"" + l_opt + "\""); + + // run + exec("chmod +x run-bash.sh"); + exec("./run-bash.sh " + std::to_string(l_port) + " &"); } - } - else if (l_key == xlpmg::CONTINUE_SIMULATION.key) - { - std::cout << "Continue simulation" << std::endl; - simulator->setPausingStatus(false); - } - else if (l_key == xlpmg::PAUSE_SIMULATION.key) - { - std::cout << "Pause simulation" << std::endl; - simulator->setPausingStatus(true); - } - } - //-------------------------------------------// - //--------------FUNCTION CALLS---------------// - //-------------------------------------------// - else if (l_type == xlpmg::FUNCTION_CALL) - { - - if (l_key == xlpmg::RESET_SIMULATOR.key) - { - exitSimulationThread(); - if (canRunThread()) + else if (l_key == xlpmg::COMPILE_RUN_SBATCH.key) + { + // Shutdown server + m_EXIT = true; + l_communicator.stopServer(); + exitSimulationThread(); + + std::string l_env = l_args.value("ENV", ""); // environment var + std::string l_opt = l_args.value("OPT", ""); // compiler opt + int l_port = l_args.value("POR", 8080); + + std::string l_job = l_args.value("JOB", ""); + std::string l_out = l_args.value("OUT", ""); + std::string l_err = l_args.value("ERR", ""); + std::string l_tim = l_args.value("TIM", ""); + + // compile + exec("chmod +x scripts/compile-bash.sh"); + exec("./scripts/compile-bash.sh \"" + l_env + "\" \"" + l_opt + "\""); + + // generate sbatch + exec("chmod +x scripts/generateSbatch.sh"); + exec("./scripts/generateSbatch.sh " + l_job + " " + l_out + " " + l_err + " " + l_tim + " > run-sbatch.sh"); + + // run + exec("chmod +x run-sbatch.sh"); + exec("sbatch run-sbatch.sh " + std::to_string(l_port)); + } + else if (l_key == xlpmg::DELETE_CHECKPOINTS.key) + { + simulator->deleteCheckpoints(); + } + else if (l_key == xlpmg::DELETE_STATIONS.key) + { + simulator->deleteStations(); + } + else if (l_key == xlpmg::SET_OFFSET.key) { - m_simulationThread = std::thread(&tsunami_lab::Simulator::resetSimulator, simulator); + tsunami_lab::t_real l_offsetX = l_args.value("offsetX", 0); + tsunami_lab::t_real l_offsetY = l_args.value("offsetY", 0); + simulator->setOffset(l_offsetX, l_offsetY); } - else + else if (l_key == xlpmg::SET_CELL_AMOUNT.key) { - std::cout << "Warning: Could not reset because the simulation is still running." << std::endl; + tsunami_lab::t_idx l_nCellsX = l_args["cellsX"]; + tsunami_lab::t_idx l_nCellsY = l_args["cellsY"]; + simulator->setCellAmount(l_nCellsX, l_nCellsY); } } - else if (l_key == xlpmg::GET_TIME_VALUES.key) + // LOW + else if (l_urgency == xlpmg::LOW) { - xlpmg::Message response = {xlpmg::SERVER_RESPONSE, "get_time_values"}; - tsunami_lab::t_idx l_currentTimeStep, l_maxTimeStep; - tsunami_lab::t_real l_timePerTimeStep; - simulator->getTimeValues(l_currentTimeStep, l_maxTimeStep, l_timePerTimeStep); - json l_data; - l_data["currentTimeStep"] = l_currentTimeStep; - l_data["maxTimeStep"] = l_maxTimeStep; - l_data["timePerTimeStep"] = l_timePerTimeStep; - if (simulator->isCalculating()) + if (l_key == xlpmg::SEND_FILE.key) { - l_data["status"] = "CALCULATING"; + std::vector l_byteVector = l_args["data"]["bytes"]; + auto l_writeFile = std::fstream(l_args.value("path", ""), std::ios::out | std::ios::binary); + l_writeFile.write((char *)&l_byteVector[0], l_byteVector.size()); + l_writeFile.close(); } - else if (simulator->isPreparing()) + else if (l_key == xlpmg::LOAD_CONFIG_JSON.key) { - l_data["status"] = "PREPARING"; + simulator->loadConfigDataJson(l_args); + if (canRunThread()) + { + m_simulationThread = std::thread(&tsunami_lab::Simulator::resetSimulator, simulator); + } + else + { + std::cout << "Warning: Could not reset because the simulation is still running." << std::endl; + } } - else if (simulator->isResetting()) + else if (l_key == xlpmg::LOAD_CONFIG_FILE.key) { - l_data["status"] = "RESETTING"; + simulator->loadConfigDataFromFile(l_args); + if (canRunThread()) + { + m_simulationThread = std::thread(&tsunami_lab::Simulator::resetSimulator, simulator); + } + else + { + std::cout << "Warning: Could not reset because the simulation is still running." << std::endl; + } } - else + else if (l_key == xlpmg::SET_READ_BUFFER_SIZE.key) { - l_data["status"] = "IDLE"; + l_communicator.setReadBufferSize(l_args); + } + else if (l_key == xlpmg::SET_SEND_BUFFER_SIZE.key) + { + l_communicator.setSendBufferSize(l_args); } - response.args = l_data; - l_communicator.sendToClient(xlpmg::messageToJsonString(response), false); } - else if (l_key == xlpmg::TOGGLE_FILEIO.key) + } + //////////////////////////////// + // EXPECT_RESPONSE // + //////////////////////////////// + else if (l_expectation == xlpmg::EXPECT_RESPONSE) + { + // CRITICAL + if (l_urgency == xlpmg::CRITICAl) { - if (l_args == "true") + if (l_key == xlpmg::GET_TIME_VALUES.key) + { + xlpmg::Message response = xlpmg::SERVER_RESPONSE; + response.key = "time_values"; + tsunami_lab::t_idx l_currentTimeStep, l_maxTimeStep; + tsunami_lab::t_real l_timePerTimeStep; + simulator->getTimeValues(l_currentTimeStep, l_maxTimeStep, l_timePerTimeStep); + json l_data; + l_data["currentTimeStep"] = l_currentTimeStep; + l_data["maxTimeStep"] = l_maxTimeStep; + l_data["timePerTimeStep"] = l_timePerTimeStep; + if (simulator->isCalculating()) + { + l_data["status"] = "CALCULATING"; + } + else if (simulator->isPreparing()) + { + l_data["status"] = "PREPARING"; + } + else if (simulator->isResetting()) + { + l_data["status"] = "RESETTING"; + } + else + { + l_data["status"] = "IDLE"; + } + response.args = l_data; + l_communicator.sendToClient(xlpmg::messageToJsonString(response), false); + } + else if (l_key == xlpmg::GET_SYSTEM_INFORMATION.key) { - simulator->toggleFileIO(true); + xlpmg::Message l_response = xlpmg::SERVER_RESPONSE; + l_response.key = "system_information"; + json l_data; + l_data["USED_RAM"] = l_usedRAM; + l_data["TOTAL_RAM"] = l_totalRAM; + l_data["CPU_USAGE"] = l_cpuUsage; + l_response.args = l_data; + l_communicator.sendToClient(xlpmg::messageToJsonString(l_response), false); } - else if (l_args == "false") + + else if (l_key == xlpmg::GET_SIMULATION_SIZES.key) { - simulator->toggleFileIO(false); + xlpmg::Message l_msg = xlpmg::SERVER_RESPONSE; + l_msg.key = "simulation_sizes"; + json l_data; + tsunami_lab::t_idx l_ncellsX, l_ncellsY; + tsunami_lab::t_real l_simulationSizeX, l_simulationSizeY, l_offsetX, l_offsetY; + simulator->getCellAmount(l_ncellsX, l_ncellsY); + simulator->getSimulationSize(l_simulationSizeX, l_simulationSizeY); + simulator->getSimulationOffset(l_offsetX, l_offsetY); + l_data["cellsX"] = l_ncellsX; + l_data["cellsY"] = l_ncellsY; + l_data["simulationSizeX"] = l_simulationSizeX; + l_data["simulationSizeY"] = l_simulationSizeY; + l_data["offsetX"] = l_offsetX; + l_data["offsetY"] = l_offsetY; + l_msg.args = l_data; + l_communicator.sendToClient(xlpmg::messageToJsonString(l_msg)); } } - else if (l_key == xlpmg::GET_HEIGHT_DATA.key) + // HIGH + else if (l_urgency == xlpmg::HIGH) { - xlpmg::Message l_heightDataMsg = {xlpmg::SERVER_RESPONSE, "height_data", nullptr}; - - // get data from simulation - if (simulator->getWaveProp() != nullptr) + if (l_key == xlpmg::GET_HEIGHT_DATA.key) { - tsunami_lab::patches::WavePropagation *l_waveprop = simulator->getWaveProp(); - const tsunami_lab::t_real *l_heightData = l_waveprop->getHeight(); - const tsunami_lab::t_real *l_bathymetryData = l_waveprop->getBathymetry(); - // calculate array size - tsunami_lab::t_idx l_ncellsX, l_ncellsY; - simulator->getCellAmount(l_ncellsX, l_ncellsY); - for (tsunami_lab::t_idx y = 0; y < l_ncellsY; y++) + xlpmg::Message l_heightDataMsg = xlpmg::SERVER_RESPONSE; + l_heightDataMsg.key = "height_data"; + json l_data; + // get data from simulation + if (simulator->getWaveProp() != nullptr) { - for (tsunami_lab::t_idx x = 0; x < l_ncellsX; x++) + tsunami_lab::patches::WavePropagation *l_waveprop = simulator->getWaveProp(); + const tsunami_lab::t_real *l_heightData = l_waveprop->getHeight(); + // calculate array size + tsunami_lab::t_idx l_ncellsX, l_ncellsY; + simulator->getCellAmount(l_ncellsX, l_ncellsY); + for (tsunami_lab::t_idx y = 0; y < l_ncellsY; y++) { - l_heightDataMsg.args.push_back(l_heightData[x + l_waveprop->getStride() * y] + l_bathymetryData[x + l_waveprop->getStride() * y]); + for (tsunami_lab::t_idx x = 0; x < l_ncellsX; x++) + { + l_data.push_back(l_heightData[x + l_waveprop->getStride() * y]); + } } } + l_heightDataMsg.args = l_data; + l_communicator.sendToClient(xlpmg::messageToJsonString(l_heightDataMsg)); + } - l_communicator.sendToClient(xlpmg::messageToJsonString(l_heightDataMsg)); - } - else if (l_key == xlpmg::GET_BATHYMETRY_DATA.key) - { - xlpmg::Message l_bathyDataMsg = {xlpmg::SERVER_RESPONSE, "bathymetry_data", nullptr}; - - // get data from simulation - if (simulator->getWaveProp() != nullptr) + else if (l_key == xlpmg::GET_BATHYMETRY_DATA.key) { - tsunami_lab::patches::WavePropagation *l_waveprop = simulator->getWaveProp(); - const tsunami_lab::t_real *l_bathymetryData = l_waveprop->getBathymetry(); - // calculate array size - tsunami_lab::t_idx l_ncellsX, l_ncellsY; - simulator->getCellAmount(l_ncellsX, l_ncellsY); - for (tsunami_lab::t_idx y = 0; y < l_ncellsY; y++) + xlpmg::Message l_bathyDataMsg = xlpmg::SERVER_RESPONSE; + l_bathyDataMsg.key = "bathymetry_data"; + json l_data; + + // get data from simulation + if (simulator->getWaveProp() != nullptr) { - for (tsunami_lab::t_idx x = 0; x < l_ncellsX; x++) + tsunami_lab::patches::WavePropagation *l_waveprop = simulator->getWaveProp(); + const tsunami_lab::t_real *l_bathymetryData = l_waveprop->getBathymetry(); + // calculate array size + tsunami_lab::t_idx l_ncellsX, l_ncellsY; + simulator->getCellAmount(l_ncellsX, l_ncellsY); + for (tsunami_lab::t_idx y = 0; y < l_ncellsY; y++) { - l_bathyDataMsg.args.push_back(l_bathymetryData[x + l_waveprop->getStride() * y]); + for (tsunami_lab::t_idx x = 0; x < l_ncellsX; x++) + { + l_data.push_back(l_bathymetryData[x + l_waveprop->getStride() * y]); + } } } + l_bathyDataMsg.args = l_data; + l_communicator.sendToClient(xlpmg::messageToJsonString(l_bathyDataMsg)); } - l_communicator.sendToClient(xlpmg::messageToJsonString(l_bathyDataMsg)); } - else if (l_key == xlpmg::LOAD_CONFIG_JSON.key) + // MEDIUM + else if (l_urgency == xlpmg::MEDIUM) { - simulator->loadConfigDataJson(l_args); - if (canRunThread()) - { - m_simulationThread = std::thread(&tsunami_lab::Simulator::resetSimulator, simulator); - } - else + if (l_key == xlpmg::RECV_FILE.key) { - std::cout << "Warning: Could not reset because the simulation is still running." << std::endl; + std::string l_file = l_args.value("path", ""); + std::string l_fileDestination = l_args.value("pathDestination", ""); + + if (l_file.length() > 0 && l_fileDestination.length() > 0) + { + xlpmg::Message l_response = xlpmg::SERVER_RESPONSE; + l_response.key = "file_data"; + json l_arguments; + l_arguments["path"] = l_fileDestination; + + std::ifstream l_fileData(l_file, std::ios::binary); + l_fileData.unsetf(std::ios::skipws); + std::streampos l_fileSize; + l_fileData.seekg(0, std::ios::end); + l_fileSize = l_fileData.tellg(); + l_fileData.seekg(0, std::ios::beg); + std::vector vec; + vec.reserve(l_fileSize); + vec.insert(vec.begin(), + std::istream_iterator(l_fileData), + std::istream_iterator()); + l_arguments["data"] = json::binary(vec); + l_response.args = l_arguments; + l_communicator.sendToClient(xlpmg::messageToJsonString(l_response)); + } } } - else if (l_key == xlpmg::DELETE_CHECKPOINTS.key) - { - simulator->deleteCheckpoints(); - } - else if (l_key == xlpmg::DELETE_STATIONS.key) - { - simulator->deleteStations(); - } - else if (l_key == xlpmg::GET_SIMULATION_SIZES.key) - { - xlpmg::Message l_msg = {xlpmg::SERVER_RESPONSE, "simulation_sizes", nullptr}; - tsunami_lab::t_idx l_ncellsX, l_ncellsY; - tsunami_lab::t_real l_simulationSizeX, l_simulationSizeY, l_offsetX, l_offsetY; - simulator->getCellAmount(l_ncellsX, l_ncellsY); - simulator->getSimulationSize(l_simulationSizeX, l_simulationSizeY); - simulator->getSimulationOffset(l_offsetX, l_offsetY); - l_msg.args["cellsX"] = l_ncellsX; - l_msg.args["cellsY"] = l_ncellsY; - l_msg.args["simulationSizeX"] = l_simulationSizeX; - l_msg.args["simulationSizeY"] = l_simulationSizeY; - l_msg.args["offsetX"] = l_offsetX; - l_msg.args["offsetY"] = l_offsetY; - l_communicator.sendToClient(xlpmg::messageToJsonString(l_msg)); - } - else if (l_key == xlpmg::SET_OFFSET.key) - { - tsunami_lab::t_real l_offsetX = l_args.value("offsetX", 0); - tsunami_lab::t_real l_offsetY = l_args.value("offsetY", 0); - simulator->setOffset(l_offsetX, l_offsetY); - } - else if (l_key == xlpmg::SET_CELL_AMOUNT.key) + // LOW + else if (l_urgency == xlpmg::LOW) { - tsunami_lab::t_idx l_nCellsX = l_args["cellsX"]; - tsunami_lab::t_idx l_nCellsY = l_args["cellsY"]; - simulator->setCellAmount(l_nCellsX, l_nCellsY); } } } @@ -456,8 +515,6 @@ int main(int i_argc, char *i_argv[]) { m_updateThread.join(); } - std::remove(m_bathTempFile.c_str()); - std::remove(m_displTempFile.c_str()); } //------------------------------------------// //--------------STANDARD MODE---------------// diff --git a/src/Simulator.cpp b/src/Simulator.cpp index 73d42772..4b27d80f 100644 --- a/src/Simulator.cpp +++ b/src/Simulator.cpp @@ -161,7 +161,7 @@ void tsunami_lab::Simulator::constructSetup() } else if (m_setupChoice == "DAMBREAK1D") { - m_setup = new tsunami_lab::setups::DamBreak1d(10, 5, m_simulationSizeX / 2); + m_setup = new tsunami_lab::setups::DamBreak1d(m_height, m_baseHeight, m_simulationSizeX / 2); } else if (m_setupChoice == "CIRCULARDAMBREAK2D") { diff --git a/src/setups/CircularDamBreak2d.cpp b/src/setups/CircularDamBreak2d.cpp index 175df67e..2c30e5e2 100644 --- a/src/setups/CircularDamBreak2d.cpp +++ b/src/setups/CircularDamBreak2d.cpp @@ -23,7 +23,7 @@ tsunami_lab::t_real tsunami_lab::setups::CircularDamBreak2d::getHeight(t_real i_ t_real i_y) const { tsunami_lab::t_real sumOfSquares = i_x * i_x + i_y * i_y; - return std::sqrt(sumOfSquares) < m_radius ? m_height : m_baseHeight; + return std::sqrt(sumOfSquares) < m_radius ? m_height-getBathymetry(i_x, i_y) : m_baseHeight-getBathymetry(i_x, i_y); } tsunami_lab::t_real tsunami_lab::setups::CircularDamBreak2d::getMomentumX(t_real, t_real) const @@ -38,5 +38,5 @@ tsunami_lab::t_real tsunami_lab::setups::CircularDamBreak2d::getMomentumY(t_real tsunami_lab::t_real tsunami_lab::setups::CircularDamBreak2d::getBathymetry(t_real, t_real) const { - return 0; + return -100; } \ No newline at end of file diff --git a/src/ui/GUI.cpp b/src/ui/GUI.cpp index b631b880..d9765de7 100644 --- a/src/ui/GUI.cpp +++ b/src/ui/GUI.cpp @@ -209,6 +209,7 @@ int tsunami_lab::ui::GUI::launch() bool showSimulationParameterWindow = false; bool showSystemInfoWindow = false; bool showSimulationControlsWindow = false; + bool showStationDataVisualizer = false; bool showDataVisualizer = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); @@ -313,6 +314,7 @@ int tsunami_lab::ui::GUI::launch() ImGui::SameLine(); if (ImGui::Button("Check connection")) { + m_communicator.sendToServer(xlpmg::messageToJsonString(xlpmg::CHECK)); m_connected = m_communicator.isConnected; } ImGui::BeginDisabled(!m_connected); @@ -488,12 +490,14 @@ int tsunami_lab::ui::GUI::launch() ImGui::SameLine(); HelpMarker("The continuous dimension of time is discretized using 'time steps'. The length of a time step is computed anew for each simulation in a way such that the waves do not interact with each other."); - ImGui::Text("Time per time step: %i", m_timePerTimeStep); + ImGui::ProgressBar((float)m_currentTimeStep / (float)m_maxTimeSteps, ImVec2(0.0f, 0.0f)); + + ImGui::Text("Time per time step: %ims", m_timePerTimeStep); ImGui::SetItemTooltip("in milliseconds"); ImGui::SameLine(); HelpMarker("The time the solver takes to compute one time step."); - ImGui::Text("Estimated time left: %f", m_estimatedTimeLeft); + ImGui::Text("Estimated time left: %is", (int)m_estimatedTimeLeft); ImGui::SetItemTooltip("in seconds"); ImGui::SameLine(); HelpMarker("Time until the solver has finished the whole computation."); @@ -541,6 +545,7 @@ int tsunami_lab::ui::GUI::launch() ImGui::Checkbox("Show simulation controls", &showSimulationControlsWindow); ImGui::Checkbox("Edit compiler/runtime options", &showCompilerOptionsWindow); ImGui::Checkbox("Edit simulation parameters", &showSimulationParameterWindow); + ImGui::Checkbox("Show station data visualizer", &showStationDataVisualizer); ImGui::Checkbox("Show data visualizer", &showDataVisualizer); ImGui::Checkbox("Show client log", &showClientLog); ImGui::Checkbox("Show system info", &showSystemInfoWindow); @@ -620,7 +625,7 @@ int tsunami_lab::ui::GUI::launch() } } ImGui::SeparatorText("INFO"); - ImGui::TextWrapped(""); + ImGui::TextWrapped("Our file transfer implementaion uses byte transfer over standard tcp sockets without additional security or performance. For large or confident files we recommend using other services such as sftp."); ImGui::EndTabItem(); } //------------------------------------------// @@ -854,13 +859,26 @@ int tsunami_lab::ui::GUI::launch() short width = 24; + ImGui::InputTextWithHint("Config file path", "configs/config.json", m_configFilePath, IM_ARRAYSIZE(m_configFilePath)); + if (ImGui::Button("Load config")) + { + xlpmg::Message l_loadConfigMsg = xlpmg::LOAD_CONFIG_FILE; + l_loadConfigMsg.args = m_configFilePath; + m_communicator.sendToServer(messageToJsonString(l_loadConfigMsg)); + } + + ImGui::SeparatorText("Custom options"); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * width); ImGui::Combo("Tsunami Event", &m_tsunamiEvent, m_tsunamiEvents, IM_ARRAYSIZE(m_tsunamiEvents)); if (!strcmp(m_tsunamiEvents[m_tsunamiEvent], "CIRCULARDAMBREAK2D")) { + ImGui::SetNextItemWidth(ImGui::GetFontSize() * width); ImGui::InputInt("Waterheight", &m_height, 0); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * width); ImGui::InputInt("Base water Height", &m_baseHeight, 0); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * width); ImGui::InputInt("Diameter", &m_diameter, 0); } @@ -1127,60 +1145,62 @@ int tsunami_lab::ui::GUI::launch() ImGui::End(); // STATIONS - ImGui::Begin("Station data"); + if (showStationDataVisualizer) + { + ImGui::Begin("Station data", &showStationDataVisualizer); - if (ImGui::Button("Select station data file")) - fileDialogStation.Open(); + if (ImGui::Button("Select station data file")) + fileDialogStation.Open(); - fileDialogStation.Display(); + fileDialogStation.Display(); - if (fileDialogStation.HasSelected()) - { - m_stationFilePath = fileDialogStation.GetSelected().string(); - fileDialogStation.ClearSelected(); - - m_stationTime.clear(); - m_stationMomentumX.clear(); - m_stationMomentumY.clear(); - m_stationBathymetry.clear(); - m_stationTotalHeight.clear(); - - // load data - std::ifstream l_inputFile(m_stationFilePath); - std::vector l_row; - std::string l_line; - std::getline(l_inputFile, l_line); // skip header - while (getline(l_inputFile, l_line)) + if (fileDialogStation.HasSelected()) { - tsunami_lab::io::Csv::splitLine(std::stringstream(l_line), ',', l_row); - m_stationTime.push_back(stof(l_row[0])); - m_stationMomentumX.push_back(stof(l_row[2])); - m_stationMomentumY.push_back(stof(l_row[3])); - m_stationBathymetry.push_back(stof(l_row[4])); - m_stationTotalHeight.push_back(stof(l_row[5])); + m_stationFilePath = fileDialogStation.GetSelected().string(); + fileDialogStation.ClearSelected(); + + m_stationTime.clear(); + m_stationMomentumX.clear(); + m_stationMomentumY.clear(); + m_stationBathymetry.clear(); + m_stationTotalHeight.clear(); + + // load data + std::ifstream l_inputFile(m_stationFilePath); + std::vector l_row; + std::string l_line; + std::getline(l_inputFile, l_line); // skip header + while (getline(l_inputFile, l_line)) + { + tsunami_lab::io::Csv::splitLine(std::stringstream(l_line), ',', l_row); + m_stationTime.push_back(stof(l_row[0])); + m_stationMomentumX.push_back(stof(l_row[2])); + m_stationMomentumY.push_back(stof(l_row[3])); + m_stationBathymetry.push_back(stof(l_row[4])); + m_stationTotalHeight.push_back(stof(l_row[5])); + } } - } - ImGui::SameLine(); - ImGui::Text("Selected file: %s", m_stationFilePath.c_str()); + ImGui::SameLine(); + ImGui::Text("Selected file: %s", m_stationFilePath.c_str()); - std::string l_plotName = m_stationFilePath.substr(m_stationFilePath.find_last_of("/\\") + 1) + ": heights"; - if (ImPlot::BeginPlot(l_plotName.c_str())) - { - ImPlot::SetupAxes("time in seconds", "height in metres"); - ImPlot::PlotLine("bathymetry", &m_stationTime[0], &m_stationBathymetry[0], m_stationTime.size()); - ImPlot::PlotLine("water level", &m_stationTime[0], &m_stationTotalHeight[0], m_stationTime.size()); - ImPlot::EndPlot(); - } - l_plotName = m_stationFilePath.substr(m_stationFilePath.find_last_of("/\\") + 1) + ": momenta"; - if (ImPlot::BeginPlot(l_plotName.c_str())) - { - ImPlot::SetupAxes("time in seconds", "momentum in m^2/s"); - ImPlot::PlotLine("momentum in x-direction", &m_stationTime[0], &m_stationMomentumX[0], m_stationTime.size()); - ImPlot::PlotLine("momentum in y-direction", &m_stationTime[0], &m_stationMomentumY[0], m_stationTime.size()); - ImPlot::EndPlot(); + std::string l_plotName = m_stationFilePath.substr(m_stationFilePath.find_last_of("/\\") + 1) + ": heights"; + if (ImPlot::BeginPlot(l_plotName.c_str())) + { + ImPlot::SetupAxes("time in seconds", "height in metres"); + ImPlot::PlotLine("bathymetry", &m_stationTime[0], &m_stationBathymetry[0], m_stationTime.size()); + ImPlot::PlotLine("water level", &m_stationTime[0], &m_stationTotalHeight[0], m_stationTime.size()); + ImPlot::EndPlot(); + } + l_plotName = m_stationFilePath.substr(m_stationFilePath.find_last_of("/\\") + 1) + ": momenta"; + if (ImPlot::BeginPlot(l_plotName.c_str())) + { + ImPlot::SetupAxes("time in seconds", "momentum in m^2/s"); + ImPlot::PlotLine("momentum in x-direction", &m_stationTime[0], &m_stationMomentumX[0], m_stationTime.size()); + ImPlot::PlotLine("momentum in y-direction", &m_stationTime[0], &m_stationMomentumY[0], m_stationTime.size()); + ImPlot::EndPlot(); + } + ImGui::End(); } - ImGui::End(); - if (showDataVisualizer) { ImGui::SetNextWindowSize(ImVec2(640, 640), ImGuiCond_FirstUseEver); @@ -1206,35 +1226,34 @@ int tsunami_lab::ui::GUI::launch() currCellsX = l_simSizes.args.value("cellsX", 0); currCellsY = l_simSizes.args.value("cellsY", 0); - currOffsetX = l_simSizes.args.value("offsetX", 0); - currOffsetY = l_simSizes.args.value("offsetY", 0); - currSimSizeX = l_simSizes.args.value("simulationSizeX", 0); - currSimSizeY = l_simSizes.args.value("simulationSizeY", 0); - m_heightData = new tsunami_lab::t_real[currCellsX * currCellsY]{0}; + currOffsetX = l_simSizes.args.value("offsetX", 0.f); + currOffsetY = l_simSizes.args.value("offsetY", 0.f); + currSimSizeX = l_simSizes.args.value("simulationSizeX", 0.f); + currSimSizeY = l_simSizes.args.value("simulationSizeY", 0.f); m_bathymetryData = new tsunami_lab::t_real[currCellsX * currCellsY]{0}; + m_heightData = new tsunami_lab::t_real[currCellsX * currCellsY]{0}; - if (m_communicator.sendToServer(messageToJsonString(xlpmg::GET_HEIGHT_DATA)) == 0) + if (m_communicator.sendToServer(messageToJsonString(xlpmg::GET_BATHYMETRY_DATA)) == 0) { - std::string l_response = m_communicator.receiveFromServer(); + std::string l_response = m_communicator.receiveFromServer(600); if (json::accept(l_response)) { xlpmg::Message msg = xlpmg::jsonToMessage(json::parse(l_response)); - std::stringstream l_stream(msg.args.dump().substr(1, msg.args.dump().size() - 2)); std::string l_num; unsigned long l_index = 0; while (getline(l_stream, l_num, ',')) { - m_heightData[l_index] = std::stof(l_num); + m_bathymetryData[l_index] = std::stof(l_num); l_index++; } } } - if (m_communicator.sendToServer(messageToJsonString(xlpmg::GET_BATHYMETRY_DATA)) == 0) + if (m_communicator.sendToServer(messageToJsonString(xlpmg::GET_HEIGHT_DATA)) == 0) { - std::string l_response = m_communicator.receiveFromServer(); + std::string l_response = m_communicator.receiveFromServer(600); if (json::accept(l_response)) { @@ -1245,7 +1264,7 @@ int tsunami_lab::ui::GUI::launch() unsigned long l_index = 0; while (getline(l_stream, l_num, ',')) { - m_bathymetryData[l_index] = std::stof(l_num); + m_heightData[l_index] = std::stof(l_num) + m_bathymetryData[l_index]; l_index++; } } @@ -1253,29 +1272,34 @@ int tsunami_lab::ui::GUI::launch() } } + ImGui::SameLine(); + HelpMarker("The GUI might freeze as its waiting for the server response. This might take several minutes depending on how many cells need to be processed."); + ImGui::SetNextItemWidth(225); ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.01f, -20, 20); ImPlot::PushColormap("WATERHEIGHTSMAP"); ImPlot::ColormapScale("Colormap scale", scale_min, scale_max, ImVec2(60, 225)); ImGui::SameLine(); - if (ImPlot::BeginPlot("Water height and bathymetry", ImVec2(550, 550))) + if (m_bathymetryData != nullptr || m_heightData != nullptr) { - ImPlot::SetupAxesLimits(currOffsetX, currOffsetX + currSimSizeX, currOffsetY, currOffsetY + currSimSizeY); - if (m_heightData != nullptr) - { - ImPlot::PlotHeatmap("water level", m_heightData, currCellsY, currCellsX, scale_min, scale_max, nullptr, ImPlotPoint(currOffsetX, currOffsetY), ImPlotPoint(currOffsetX + currSimSizeX, currOffsetY + currSimSizeY), 0); - } - if (m_bathymetryData != nullptr) + + if (ImPlot::BeginPlot("Water height and bathymetry", ImVec2(550, 550))) { - ImPlot::PlotHeatmap("bathymetry", m_bathymetryData, currCellsY, currCellsX, scale_min, scale_max, nullptr, ImPlotPoint(currOffsetX, currOffsetY), ImPlotPoint(currOffsetX + currSimSizeX, currOffsetY + currSimSizeY), 0); + ImPlot::SetupAxesLimits(currOffsetX, currOffsetX + currSimSizeX, currOffsetY, currOffsetY + currSimSizeY); + if (m_bathymetryData != nullptr) + { + ImPlot::PlotHeatmap("bathymetry", m_bathymetryData, currCellsY, currCellsX, scale_min, scale_max, nullptr, ImPlotPoint(currOffsetX, currOffsetY), ImPlotPoint(currOffsetX + currSimSizeX, currOffsetY + currSimSizeY), 0); + } + if (m_heightData != nullptr) + { + ImPlot::PlotHeatmap("water level", m_heightData, currCellsY, currCellsX, scale_min, scale_max, nullptr, ImPlotPoint(currOffsetX, currOffsetY), ImPlotPoint(currOffsetX + currSimSizeX, currOffsetY + currSimSizeY), 0); + } + ImPlot::EndPlot(); } - ImPlot::EndPlot(); } ImPlot::PopColormap(); - ImGui::End(); } - // Rendering ImGui::Render(); int display_w, display_h; diff --git a/src/ui/GUI.h b/src/ui/GUI.h index 057e79c2..231846ad 100644 --- a/src/ui/GUI.h +++ b/src/ui/GUI.h @@ -74,6 +74,7 @@ class tsunami_lab::ui::GUI char m_sbTim[256] = "10:00:00"; // simulation parameters + char m_configFilePath[256] = ""; const char *m_tsunamiEvents[3] = {"CUSTOM", "ARTIFICIAL2D", "CIRCULARDAMBREAK2D"}; int m_tsunamiEvent = 0; int m_nx = 1; @@ -150,10 +151,10 @@ class tsunami_lab::ui::GUI float scale_max = 1; long currCellsX = 0; long currCellsY = 0; - int currOffsetX = 0; - int currOffsetY = 0; - int currSimSizeX = 0; - int currSimSizeY = 0; + tsunami_lab::t_real currOffsetX = 0; + tsunami_lab::t_real currOffsetY = 0; + tsunami_lab::t_real currSimSizeX = 0; + tsunami_lab::t_real currSimSizeY = 0; /** * Executes a shell command.