ESSIM API

Note

This documentation is work in progress and far from finished. New sections will be added in the near future. Whenever questions or feedback is received from end users, we’re trying to update this documentation on the fly. So don’t hesitate to contact us, whenever you run into problems.

Sequence

ESSIM provides a REST API that allows users to interact with ESSIM for starting a simulation, requesting its progress, etc. The usual sequence to invoking a simulation and interacting with it is as follows:

../_images/APISequence.png

APIs:

/simulation

  • HTTP Method: POST

  • Description: Create a new simulation

  • Request Body:
    • endDate: End date of simulation in ISO-8601 format (YYYY-MM-DDTHH:mm:ss±hh:mm)

    • esdlContents: 64-bit encoded ESDL string

    • influxURL: URL of InfluxDB instance to store simulation results in

    • scenarioID: String ID representing the scenario being simulated. This ID is used to name the database in InfluxDB.

    • simulationDescription: Human-readable description of the simulation visible in the dashboard

    • startDate: Start date of simulation in ISO-8601 format (YYYY-MM-DDTHH:mm:ss±hh:mm)

    • user: Name of the user running the simulation. Used to tag the name of the Grafana dashboard

    • csvFilesLocation: (Optional) To export ESSIM simulation data into CSV, specify a location here

    • mqttURL: (Optional) To publish ESSIM data to an MQTT bus, specify the URL to an MQTT server here

    • amqpURL: (Optional) To publish ESSIM data to an AMQP bus, specify the URL to an AMQP server here

    • kafkaURL: (Optional) To publish ESSIM data to an Apache Kafka server, specify the URL to the server here

    • nodeConfig: (Optional) To use a remote node with ESSIM, specify the following:

      • esdlNodeId: The ESDL ID of the asset represented by this node

      • config: Any key-value configuration to provide this node

      • mqttHost: MQTT Host URL

      • mqttPort: MQTT Port number

      • mqttTopic: Topic to reach the ESDL Node

    Example:

    {
        "user": "john doe",
        "scenarioID": "essim",
        "simulationDescription": "A simple ES with one geothermal source and a heat demand",
        "startDate": "2019-01-01T00:00:00+0100",
        "endDate": "2020-01-01T00:00:00+0100",
        "influxURL": "http://influxdb:8086",
        "esdlContents": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz4KPGVzZGw6RW5lcmd5U3lzdGVtIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOmVzZGw9Imh0dHA6Ly93d3cudG5vLm5sL2VzZGwiIGVzZGxWZXJzaW9uPSJ2MjEwMiIgdmVyc2lvbj0iMSIgaWQ9ImVlMTBjYTVmLWEwOGQtNDIwMS04MjNjLWZhN2FiY2MxYmExYiIgbmFtZT0iVW50aXRsZWQgRW5lcmd5U3lzdGVtIiBkZXNjcmlwdGlvbj0iIj4KICA8ZW5lcmd5U3lzdGVtSW5mb3JtYXRpb24geHNpOnR5cGU9ImVzZGw6RW5lcmd5U3lzdGVtSW5mb3JtYXRpb24iIGlkPSI3NTE4NDJiNy04MGUxLTRlODItYTIyMi1mZGViZWNhOGE3OTciPgogICAgPGNhcnJpZXJzIHhzaTp0eXBlPSJlc2RsOkNhcnJpZXJzIiBpZD0iMGEzY2I2OTYtMjU1OC00OGY0LTllMjMtYWZmMTFlMTAyMGE0Ij4KICAgICAgPGNhcnJpZXIgeHNpOnR5cGU9ImVzZGw6SGVhdENvbW1vZGl0eSIgbmFtZT0iSGVhdCIgaWQ9ImZjZDYyZThmLWVmN2EtNDA0Yy04ZDNmLWQ5NGU5ZGQ0OGNkMyIgc3VwcGx5VGVtcGVyYXR1cmU9IjgwLjAiIHJldHVyblRlbXBlcmF0dXJlPSI0MC4wIi8+CiAgICA8L2NhcnJpZXJzPgogICAgPHF1YW50aXR5QW5kVW5pdHMgeHNpOnR5cGU9ImVzZGw6UXVhbnRpdHlBbmRVbml0cyIgaWQ9IjNmMWEzNjAzLTIwMTgtNGExNi05N2U5LTY3ZWVlZTE5NWE1YiI+CiAgICAgIDxxdWFudGl0eUFuZFVuaXQgeHNpOnR5cGU9ImVzZGw6UXVhbnRpdHlBbmRVbml0VHlwZSIgcGh5c2ljYWxRdWFudGl0eT0iRU5FUkdZIiB1bml0PSJKT1VMRSIgbXVsdGlwbGllcj0iR0lHQSIgaWQ9ImViMDdiY2NiLTIwM2YtNDA3ZS1hZjk4LWU2ODc2NTZhMjIxZCIgZGVzY3JpcHRpb249IkVuZXJneSBpbiBHSiIvPgogICAgPC9xdWFudGl0eUFuZFVuaXRzPgogIDwvZW5lcmd5U3lzdGVtSW5mb3JtYXRpb24+CiAgPGluc3RhbmNlIHhzaTp0eXBlPSJlc2RsOkluc3RhbmNlIiBuYW1lPSJVbnRpdGxlZCBJbnN0YW5jZSIgaWQ9IjQyYTY0ODU1LWRlODYtNGE2Ni1iMjY3LWIwMGM2NmI0M2I1OCI+CiAgICA8YXJlYSB4c2k6dHlwZT0iZXNkbDpBcmVhIiBuYW1lPSJVbnRpdGxlZCBBcmVhIiBpZD0iN2JjZjVjZDktZDNmZS00NjI2LTg1OWEtYzE2ZDg5ZGRmNTUyIj4KICAgICAgPGFzc2V0IHhzaTp0eXBlPSJlc2RsOkdlb3RoZXJtYWxTb3VyY2UiIG5hbWU9Ikdlb3RoZXJtYWxTb3VyY2VfYjU3MiIgaWQ9ImI1NzJkNmNiLTMxM2UtNDg5Zi1iNDg5LWE4NmJiZDZlY2QyMSI+CiAgICAgICAgPGdlb21ldHJ5IHhzaTp0eXBlPSJlc2RsOlBvaW50IiBsb249IjQuNzAyNzkyMTY3NjYzNTc1IiBDUlM9IldHUzg0IiBsYXQ9IjUyLjEyMTcwNjEzMzM3ODU5Ii8+CiAgICAgICAgPHBvcnQgeHNpOnR5cGU9ImVzZGw6T3V0UG9ydCIgaWQ9IjE1ZmY3MDk5LThiN2YtNDZhZi1iY2M0LTYzZTAzZjE3NzIyZSIgbmFtZT0iT3V0IiBjb25uZWN0ZWRUbz0iYTA5ODU3YjUtNTA5My00OTljLTgzY2UtNTRlY2M1NGE3NTE4IiBjYXJyaWVyPSJmY2Q2MmU4Zi1lZjdhLTQwNGMtOGQzZi1kOTRlOWRkNDhjZDMiPgogICAgICAgICAgPHByb2ZpbGUgeHNpOnR5cGU9ImVzZGw6U2luZ2xlVmFsdWUiIHZhbHVlPSI1LjAiIGlkPSJhMzgwNGI2Mi00MjA1LTRhZmItYmY3OC02MGNkNzNkMzM5ZjIiPgogICAgICAgICAgICA8cHJvZmlsZVF1YW50aXR5QW5kVW5pdCB4c2k6dHlwZT0iZXNkbDpRdWFudGl0eUFuZFVuaXRSZWZlcmVuY2UiIHJlZmVyZW5jZT0iZWIwN2JjY2ItMjAzZi00MDdlLWFmOTgtZTY4NzY1NmEyMjFkIi8+CiAgICAgICAgICA8L3Byb2ZpbGU+CiAgICAgICAgPC9wb3J0PgogICAgICA8L2Fzc2V0PgogICAgICA8YXNzZXQgeHNpOnR5cGU9ImVzZGw6SGVhdGluZ0RlbWFuZCIgbmFtZT0iSGVhdGluZ0RlbWFuZF9iNTA1IiBpZD0iYjUwNWMxMGItYmRlNC00NjA2LThkNjgtN2Y4ZTAxODhjMzgzIj4KICAgICAgICA8Z2VvbWV0cnkgeHNpOnR5cGU9ImVzZGw6UG9pbnQiIGxvbj0iNC43MTIzODM3NDcxMDA4MzEiIENSUz0iV0dTODQiIGxhdD0iNTIuMTIxOTE2OTI4MzE4ODYiLz4KICAgICAgICA8cG9ydCB4c2k6dHlwZT0iZXNkbDpJblBvcnQiIGNvbm5lY3RlZFRvPSIxNWZmNzA5OS04YjdmLTQ2YWYtYmNjNC02M2UwM2YxNzcyMmUiIGlkPSJhMDk4NTdiNS01MDkzLTQ5OWMtODNjZS01NGVjYzU0YTc1MTgiIG5hbWU9IkluIiBjYXJyaWVyPSJmY2Q2MmU4Zi1lZjdhLTQwNGMtOGQzZi1kOTRlOWRkNDhjZDMiPgogICAgICAgICAgPHByb2ZpbGUgeHNpOnR5cGU9ImVzZGw6U2luZ2xlVmFsdWUiIHZhbHVlPSI1LjAiIGlkPSJlNGU2OTE5YS1jNTY4LTRlNTgtODkwNC1jZmUzMWI5YjVkN2MiPgogICAgICAgICAgICA8cHJvZmlsZVF1YW50aXR5QW5kVW5pdCB4c2k6dHlwZT0iZXNkbDpRdWFudGl0eUFuZFVuaXRSZWZlcmVuY2UiIHJlZmVyZW5jZT0iZWIwN2JjY2ItMjAzZi00MDdlLWFmOTgtZTY4NzY1NmEyMjFkIi8+CiAgICAgICAgICA8L3Byb2ZpbGU+CiAgICAgICAgPC9wb3J0PgogICAgICA8L2Fzc2V0PgogICAgPC9hcmVhPgogIDwvaW5zdGFuY2U+CjwvZXNkbDpFbmVyZ3lTeXN0ZW0+Cg"
    }
    
  • Response:
    • CREATED (HTTP status code - 201)
      • status: CREATED

      • id: Unique ID for this simulation run. This ID will be tagged in all simulation data from this run.

      Example:

      {
        "status": "CREATED",
        "id": "60c0c0a84840404e8b19df9b"
      }
      
    • BAD REQUEST (HTTP status code - 400)
      • status: ERROR

      • description: Description of the error

      Example:

      {
        "status": "ERROR",
        "description": "Internal error: Error in Observation Manager init: Connecting to InfluxDB @ http://non-existing-host:8088 timed out. Please check URL!"
      }
      
    • SERVICE UNAVAILABLE (HTTP status code - 503)
      • If a simulation is already running while a new one is started, HTTP status code 503 is returned with a text body Busy.

/simulation/<simulation-id>

  • HTTP Method: GET

  • Description: Retrieve meta-data of simulation run. This includes information provided by the user at the time of starting the simulation and some run-time data. This API call can be used to retrieve the dashboard URL as soon as a simulation is CREATED

  • Request Body:
    • None

  • Response:
    • NOT FOUND (HTTP status code - 404)
      • status: ERROR

      • description: Description of the error

      Example:

      {
          "status": "ERROR",
          "description": "SimulationID 60c0c0a84840404e8b19479b not found!"
      }
      
    • OK (HTTP status code - 200)
      • status: The last known status of the simulation.

      • simRunDate: The date when the simulation was run in ISO-8601 format (YYYY-MM-DDTHH:mm:ss±hh:mm).

      • transport: An HTML visualisation of ESSIM’s internal transport network trees created per commodity. The HTML pages are URL-encoded and tagged with the name of the network.

      • dashboardURL: A link to the Grafana dashboard created by ESSIM for this simulation.

      Example:

      {
          "esdlContents": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz4KPGVzZGw6RW5lcmd5U3lzdGVtIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOmVzZGw9Imh0dHA6Ly93d3cudG5vLm5sL2VzZGwiIGVzZGxWZXJzaW9uPSJ2MjEwMiIgdmVyc2lvbj0iMSIgaWQ9ImVlMTBjYTVmLWEwOGQtNDIwMS04MjNjLWZhN2FiY2MxYmExYiIgbmFtZT0iVW50aXRsZWQgRW5lcmd5U3lzdGVtIiBkZXNjcmlwdGlvbj0iIj4KICA8ZW5lcmd5U3lzdGVtSW5mb3JtYXRpb24geHNpOnR5cGU9ImVzZGw6RW5lcmd5U3lzdGVtSW5mb3JtYXRpb24iIGlkPSI3NTE4NDJiNy04MGUxLTRlODItYTIyMi1mZGViZWNhOGE3OTciPgogICAgPGNhcnJpZXJzIHhzaTp0eXBlPSJlc2RsOkNhcnJpZXJzIiBpZD0iMGEzY2I2OTYtMjU1OC00OGY0LTllMjMtYWZmMTFlMTAyMGE0Ij4KICAgICAgPGNhcnJpZXIgeHNpOnR5cGU9ImVzZGw6SGVhdENvbW1vZGl0eSIgbmFtZT0iSGVhdCIgaWQ9ImZjZDYyZThmLWVmN2EtNDA0Yy04ZDNmLWQ5NGU5ZGQ0OGNkMyIgc3VwcGx5VGVtcGVyYXR1cmU9IjgwLjAiIHJldHVyblRlbXBlcmF0dXJlPSI0MC4wIi8+CiAgICA8L2NhcnJpZXJzPgogICAgPHF1YW50aXR5QW5kVW5pdHMgeHNpOnR5cGU9ImVzZGw6UXVhbnRpdHlBbmRVbml0cyIgaWQ9IjNmMWEzNjAzLTIwMTgtNGExNi05N2U5LTY3ZWVlZTE5NWE1YiI+CiAgICAgIDxxdWFudGl0eUFuZFVuaXQgeHNpOnR5cGU9ImVzZGw6UXVhbnRpdHlBbmRVbml0VHlwZSIgcGh5c2ljYWxRdWFudGl0eT0iRU5FUkdZIiB1bml0PSJKT1VMRSIgbXVsdGlwbGllcj0iR0lHQSIgaWQ9ImViMDdiY2NiLTIwM2YtNDA3ZS1hZjk4LWU2ODc2NTZhMjIxZCIgZGVzY3JpcHRpb249IkVuZXJneSBpbiBHSiIvPgogICAgPC9xdWFudGl0eUFuZFVuaXRzPgogIDwvZW5lcmd5U3lzdGVtSW5mb3JtYXRpb24+CiAgPGluc3RhbmNlIHhzaTp0eXBlPSJlc2RsOkluc3RhbmNlIiBuYW1lPSJVbnRpdGxlZCBJbnN0YW5jZSIgaWQ9IjQyYTY0ODU1LWRlODYtNGE2Ni1iMjY3LWIwMGM2NmI0M2I1OCI+CiAgICA8YXJlYSB4c2k6dHlwZT0iZXNkbDpBcmVhIiBuYW1lPSJVbnRpdGxlZCBBcmVhIiBpZD0iN2JjZjVjZDktZDNmZS00NjI2LTg1OWEtYzE2ZDg5ZGRmNTUyIj4KICAgICAgPGFzc2V0IHhzaTp0eXBlPSJlc2RsOkdlb3RoZXJtYWxTb3VyY2UiIG5hbWU9Ikdlb3RoZXJtYWxTb3VyY2VfYjU3MiIgaWQ9ImI1NzJkNmNiLTMxM2UtNDg5Zi1iNDg5LWE4NmJiZDZlY2QyMSI+CiAgICAgICAgPGdlb21ldHJ5IHhzaTp0eXBlPSJlc2RsOlBvaW50IiBsb249IjQuNzAyNzkyMTY3NjYzNTc1IiBDUlM9IldHUzg0IiBsYXQ9IjUyLjEyMTcwNjEzMzM3ODU5Ii8+CiAgICAgICAgPHBvcnQgeHNpOnR5cGU9ImVzZGw6T3V0UG9ydCIgaWQ9IjE1ZmY3MDk5LThiN2YtNDZhZi1iY2M0LTYzZTAzZjE3NzIyZSIgbmFtZT0iT3V0IiBjb25uZWN0ZWRUbz0iYTA5ODU3YjUtNTA5My00OTljLTgzY2UtNTRlY2M1NGE3NTE4IiBjYXJyaWVyPSJmY2Q2MmU4Zi1lZjdhLTQwNGMtOGQzZi1kOTRlOWRkNDhjZDMiPgogICAgICAgICAgPHByb2ZpbGUgeHNpOnR5cGU9ImVzZGw6U2luZ2xlVmFsdWUiIHZhbHVlPSI1LjAiIGlkPSJhMzgwNGI2Mi00MjA1LTRhZmItYmY3OC02MGNkNzNkMzM5ZjIiPgogICAgICAgICAgICA8cHJvZmlsZVF1YW50aXR5QW5kVW5pdCB4c2k6dHlwZT0iZXNkbDpRdWFudGl0eUFuZFVuaXRSZWZlcmVuY2UiIHJlZmVyZW5jZT0iZWIwN2JjY2ItMjAzZi00MDdlLWFmOTgtZTY4NzY1NmEyMjFkIi8+CiAgICAgICAgICA8L3Byb2ZpbGU+CiAgICAgICAgPC9wb3J0PgogICAgICA8L2Fzc2V0PgogICAgICA8YXNzZXQgeHNpOnR5cGU9ImVzZGw6SGVhdGluZ0RlbWFuZCIgbmFtZT0iSGVhdGluZ0RlbWFuZF9iNTA1IiBpZD0iYjUwNWMxMGItYmRlNC00NjA2LThkNjgtN2Y4ZTAxODhjMzgzIj4KICAgICAgICA8Z2VvbWV0cnkgeHNpOnR5cGU9ImVzZGw6UG9pbnQiIGxvbj0iNC43MTIzODM3NDcxMDA4MzEiIENSUz0iV0dTODQiIGxhdD0iNTIuMTIxOTE2OTI4MzE4ODYiLz4KICAgICAgICA8cG9ydCB4c2k6dHlwZT0iZXNkbDpJblBvcnQiIGNvbm5lY3RlZFRvPSIxNWZmNzA5OS04YjdmLTQ2YWYtYmNjNC02M2UwM2YxNzcyMmUiIGlkPSJhMDk4NTdiNS01MDkzLTQ5OWMtODNjZS01NGVjYzU0YTc1MTgiIG5hbWU9IkluIiBjYXJyaWVyPSJmY2Q2MmU4Zi1lZjdhLTQwNGMtOGQzZi1kOTRlOWRkNDhjZDMiPgogICAgICAgICAgPHByb2ZpbGUgeHNpOnR5cGU9ImVzZGw6U2luZ2xlVmFsdWUiIHZhbHVlPSI1LjAiIGlkPSJlNGU2OTE5YS1jNTY4LTRlNTgtODkwNC1jZmUzMWI5YjVkN2MiPgogICAgICAgICAgICA8cHJvZmlsZVF1YW50aXR5QW5kVW5pdCB4c2k6dHlwZT0iZXNkbDpRdWFudGl0eUFuZFVuaXRSZWZlcmVuY2UiIHJlZmVyZW5jZT0iZWIwN2JjY2ItMjAzZi00MDdlLWFmOTgtZTY4NzY1NmEyMjFkIi8+CiAgICAgICAgICA8L3Byb2ZpbGU+CiAgICAgICAgPC9wb3J0PgogICAgICA8L2Fzc2V0PgogICAgPC9hcmVhPgogIDwvaW5zdGFuY2U+CjwvZXNkbDpFbmVyZ3lTeXN0ZW0+Cg",
          "user": "john doe",
          "scenarioID": "essim",
          "simulationDescription": "A simple ES with one geothermal source and a heat demand",
          "startDate": "2018-12-31T23:00:00+0000",
          "endDate": "2019-12-31T23:00:00+0000",
          "status": {
              "state": "COMPLETE",
              "description": "Finished in PT1.099S"
          },
          "influxURL": "http://influxdb:8086",
          "simRunDate": "2021-06-09T13:22:48+0000",
          "transport": [
              {
                  "name": "Untitled EnergySystem Heat Network 0",
                  "networkHTMLDiag": "%3C%21DOCTYPE+html%3E%0A%3Chtml+lang%3D%22en%22%3E%0A++%3Chead%3E%0A++++%3Cmeta+charset%3D%22utf-8%22%3E%0A%0A++++%3Ctitle%3E%0A%09Untitled+EnergySystem+Heat+Network+0%0A%09%3C%2Ftitle%3E%0A%0A++++%3Cstyle%3E%0A++++%0A++++.node+%7B%0A++++++++cursor%3A+pointer%3B%0A++++%7D%0A%0A++++.node+circle+%7B%0A++++++fill%3A+%23fff%3B%0A++++++stroke%3A+steelblue%3B%0A++++++stroke-width%3A+3px%3B%0A++++%7D%0A%0A++++.node+text+%7B%0A++++++font%3A+12px+sans-serif%3B%0A++++%7D%0A%0A++++.link+%7B%0A++++++fill%3A+none%3B%0A++++++stroke%3A+%23ccc%3B%0A++++++stroke-width%3A+2px%3B%0A++++%7D%0A++++%0A++++%3C%2Fstyle%3E%0A%0A++%3C%2Fhead%3E%0A%0A++%3Cbody%3E%0A%0A%3C%21--+load+the+d3.js+library+--%3E+%0A%3Cscript+src%3D%22http%3A%2F%2Fd3js.org%2Fd3.v3.min.js%22%3E%3C%2Fscript%3E%0A++++%0A%3Cscript%3E%0A%0Avar+treeData%3D%5B%7B%22parent%22%3A%22null%22%2C%22children%22%3A%5B%7B%22parent%22%3A%22GeothermalSource_b572%28PRODUCER%29%22%2C%22name%22%3A%22b505c10b-bde4-4606-8d68-7f8e0188c383%28CONSUMER%29%22%7D%5D%2C%22name%22%3A%22GeothermalSource_b572%28PRODUCER%29%22%7D%5D%3B%0A%0A%2F%2F+**************+Generate+the+tree+diagram++*****************%0Avar+margin+%3D+%7Btop%3A+20%2C+right%3A+120%2C+bottom%3A+20%2C+left%3A+200%7D%2C%0A++++width+%3D+1960+-+margin.right+-+margin.left%2C%0A++++height+%3D+500+-+margin.top+-+margin.bottom%3B%0A++++%0Avar+i+%3D+0%2C%0A++++duration+%3D+750%2C%0A++++root%3B%0A%0Avar+tree+%3D+d3.layout.tree%28%29%0A++++.size%28%5Bheight%2C+width%5D%29%3B%0A%0Avar+diagonal+%3D+d3.svg.diagonal%28%29%0A++++.projection%28function%28d%29+%7B+return+%5Bd.y%2C+d.x%5D%3B+%7D%29%3B%0A%0Avar+svg+%3D+d3.select%28%22body%22%29.append%28%22svg%22%29%0A++++.attr%28%22width%22%2C+width+%2B+margin.right+%2B+margin.left%29%0A++++.attr%28%22height%22%2C+height+%2B+margin.top+%2B+margin.bottom%29%0A++.append%28%22g%22%29%0A++++.attr%28%22transform%22%2C+%22translate%28%22+%2B+margin.left+%2B+%22%2C%22+%2B+margin.top+%2B+%22%29%22%29%3B%0A%0Aroot+%3D+treeData%5B0%5D%3B%0Aroot.x0+%3D+height+%2F+2%3B%0Aroot.y0+%3D+0%3B%0A++%0Aupdate%28root%29%3B%0A%0Ad3.select%28self.frameElement%29.style%28%22height%22%2C+%22500px%22%29%3B%0A%0Afunction+update%28source%29+%7B%0A%0A++%2F%2F+Compute+the+new+tree+layout.%0A++var+nodes+%3D+tree.nodes%28root%29.reverse%28%29%2C%0A++++++links+%3D+tree.links%28nodes%29%3B%0A%0A++%2F%2F+Normalize+for+fixed-depth.%0A++nodes.forEach%28function%28d%29+%7B+d.y+%3D+d.depth+*+250%3B+%7D%29%3B%0A%0A++%2F%2F+Update+the+nodes%E2%80%A6%0A++var+node+%3D+svg.selectAll%28%22g.node%22%29%0A++++++.data%28nodes%2C+function%28d%29+%7B+return+d.id+%7C%7C+%28d.id+%3D+%2B%2Bi%29%3B+%7D%29%3B%0A%0A++%2F%2F+Enter+any+new+nodes+at+the+parent%27s+previous+position.%0A++var+nodeEnter+%3D+node.enter%28%29.append%28%22g%22%29%0A++++++.attr%28%22class%22%2C+%22node%22%29%0A++++++.attr%28%22transform%22%2C+function%28d%29+%7B+return+%22translate%28%22+%2B+source.y0+%2B+%22%2C%22+%2B+source.x0+%2B+%22%29%22%3B+%7D%29%0A++++++.on%28%22click%22%2C+click%29%3B%0A%0A++nodeEnter.append%28%22circle%22%29%0A++++++.attr%28%22r%22%2C+1e-6%29%0A++++++.style%28%22fill%22%2C+function%28d%29+%7B+return+d._children+%3F+%22lightsteelblue%22+%3A+%22%23fff%22%3B+%7D%29%3B%0A%0A++nodeEnter.append%28%22text%22%29%0A++++++.attr%28%22x%22%2C+function%28d%29+%7B+return+d.children+%7C%7C+d._children+%3F+-13+%3A+13%3B+%7D%29%0A++++++.attr%28%22dy%22%2C+%22.35em%22%29%0A++++++.attr%28%22text-anchor%22%2C+function%28d%29+%7B+return+d.children+%7C%7C+d._children+%3F+%22end%22+%3A+%22start%22%3B+%7D%29%0A++++++.text%28function%28d%29+%7B+return+d.name%3B+%7D%29%0A++++++.style%28%22fill-opacity%22%2C+1e-6%29%3B%0A%0A++%2F%2F+Transition+nodes+to+their+new+position.%0A++var+nodeUpdate+%3D+node.transition%28%29%0A++++++.duration%28duration%29%0A++++++.attr%28%22transform%22%2C+function%28d%29+%7B+return+%22translate%28%22+%2B+d.y+%2B+%22%2C%22+%2B+d.x+%2B+%22%29%22%3B+%7D%29%3B%0A%0A++nodeUpdate.select%28%22circle%22%29%0A++++++.attr%28%22r%22%2C+10%29%0A++++++.style%28%22fill%22%2C+function%28d%29+%7B+return+d._children+%3F+%22lightsteelblue%22+%3A+%22%23fff%22%3B+%7D%29%3B%0A%0A++nodeUpdate.select%28%22text%22%29%0A++++++.style%28%22fill-opacity%22%2C+1%29%3B%0A%0A++%2F%2F+Transition+exiting+nodes+to+the+parent%27s+new+position.%0A++var+nodeExit+%3D+node.exit%28%29.transition%28%29%0A++++++.duration%28duration%29%0A++++++.attr%28%22transform%22%2C+function%28d%29+%7B+return+%22translate%28%22+%2B+source.y+%2B+%22%2C%22+%2B+source.x+%2B+%22%29%22%3B+%7D%29%0A++++++.remove%28%29%3B%0A%0A++nodeExit.select%28%22circle%22%29%0A++++++.attr%28%22r%22%2C+1e-6%29%3B%0A%0A++nodeExit.select%28%22text%22%29%0A++++++.style%28%22fill-opacity%22%2C+1e-6%29%3B%0A%0A++%2F%2F+Update+the+links%E2%80%A6%0A++var+link+%3D+svg.selectAll%28%22path.link%22%29%0A++++++.data%28links%2C+function%28d%29+%7B+return+d.target.id%3B+%7D%29%3B%0A%0A++%2F%2F+Enter+any+new+links+at+the+parent%27s+previous+position.%0A++link.enter%28%29.insert%28%22path%22%2C+%22g%22%29%0A++++++.attr%28%22class%22%2C+%22link%22%29%0A++++++.attr%28%22d%22%2C+function%28d%29+%7B%0A++++++++var+o+%3D+%7Bx%3A+source.x0%2C+y%3A+source.y0%7D%3B%0A++++++++return+diagonal%28%7Bsource%3A+o%2C+target%3A+o%7D%29%3B%0A++++++%7D%29%3B%0A%0A++%2F%2F+Transition+links+to+their+new+position.%0A++link.transition%28%29%0A++++++.duration%28duration%29%0A++++++.attr%28%22d%22%2C+diagonal%29%3B%0A%0A++%2F%2F+Transition+exiting+nodes+to+the+parent%27s+new+position.%0A++link.exit%28%29.transition%28%29%0A++++++.duration%28duration%29%0A++++++.attr%28%22d%22%2C+function%28d%29+%7B%0A++++++++var+o+%3D+%7Bx%3A+source.x%2C+y%3A+source.y%7D%3B%0A++++++++return+diagonal%28%7Bsource%3A+o%2C+target%3A+o%7D%29%3B%0A++++++%7D%29%0A++++++.remove%28%29%3B%0A%0A++%2F%2F+Stash+the+old+positions+for+transition.%0A++nodes.forEach%28function%28d%29+%7B%0A++++d.x0+%3D+d.x%3B%0A++++d.y0+%3D+d.y%3B%0A++%7D%29%3B%0A%7D%0A%0A%2F%2F+Toggle+children+on+click.%0Afunction+click%28d%29+%7B%0A++if+%28d.children%29+%7B%0A++++d._children+%3D+d.children%3B%0A++++d.children+%3D+null%3B%0A++%7D+else+%7B%0A++++d.children+%3D+d._children%3B%0A++++d._children+%3D+null%3B%0A++%7D%0A++update%28d%29%3B%0A%7D%0A%0A%3C%2Fscript%3E%0A++++%0A++%3C%2Fbody%3E%0A%3C%2Fhtml%3E"
              }
          ],
          "dashboardURL": "https://essim-dashboard.hesi.energy/d/gUtvSu6Mk/untitled-energysystem-john-doe-2021-06-09t13-22-48-55"
      }
      

/simulation/<simulation-id>/status

  • HTTP Method: GET

  • Description: Retrieve status of a simulation run. This API can be used as soon as a simulation is CREATED

  • Request Body:
    • None

  • Response:
    • NOT FOUND (HTTP status code - 404)
      • status: ERROR

      • description: Description of the error

      Example:

      {
          "status": "ERROR",
          "description": "SimulationID 60c0c0a84840404e8b19479b not found!"
      }
      
    • OK (HTTP status code - 200)
      • Running
        • State: RUNNING

        • Description: Percentage progress of the simulation as a limiting to 1.0

          {
              "State": "RUNNING"
              "Description": "0.7604529616724739",
          }
          
      • Finished
        • State: COMPLETE

        • Description: Time for simulation to complete

          {
              "State": "COMPLETE"
              "Description": "Finished in PT35.306S",
          }
          
      • Error
        • State: ERROR

        • Description: Description of the error

          {
              "State": "ERROR"
              "Description": "Cannot connect to InfluxDB service at [http://non-existing-host:8086] to query profile with id 3499a337-1785-4601-8098-3c46b6d42b7c. Please verify the URL!",
          }
          

Code Example

Following is a simple code example to access the ESSIM APIs using python. Before you run this example:

  1. Copy the ESDL file below the python example to the same folder as the script. Adjust the name of the file in the script (line 21) appropriately if the name of the ESDL file is changed.

  2. Install the necessary python libraries using pip install requests pytz.

  3. Start ESSIM following the instructions here.

Python Code:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import json
import time
import pytz
import base64
import requests
from os import path
from datetime import datetime as dt

# Common Constants
ESSIM_URL = 'http://localhost:8112/essim/simulation'
INFLUXDB_URL = 'http://influxdb:8086'
ESSIM_HEADERS = {'Content-Type': 'application/json', 'Accept': 'application/json'}
ESSIM_DATE_FORMAT = '%Y-%m-%dT%H:%M:%S%z'
PROGRESS_UPDATE_INTERVAL = 1  # in seconds

# Simulation-specific constants
ESSIM_USER = 'John Doe'
ESSIM_SCENARIO_ID = 'TestScenario'
ESDL_FILE = 'SmallestESDL_np.esdl'
SIMULATION_DESCRIPTION = 'Testing ESSIM API'
START_DATE = dt(2021, 1, 1, 0, 0, 0, 0, pytz.UTC)
END_DATE = dt(2022, 1, 1, 0, 0, 0, 0, pytz.UTC)


class ESSIMSimulation:

    def __init__(self, esdl_file):
        """
        Constructor to create an ESSIM Simulation object
        :param esdl_file: Path to ESDL file to simulate with ESSIM
        """
        self.simulation_id = None
        self.dashboardURL = None
        self.esdl_file = esdl_file
        if not path.exists(self.esdl_file):
            raise ValueError('File {} does not exist!'.format(path.abspath(self.esdl_file)))

    def encode_esdl(self):
        """
        Function to encode contents of ESDL file into Base64
        :return: Base64-encoded contents of ESDL file
        """
        with open(self.esdl_file, 'r') as f:
            esdl_string = f.read().replace('\n', '')
        message_bytes = esdl_string.encode('utf-8')
        base64_bytes = base64.b64encode(message_bytes)
        encoded_esdl = base64_bytes.decode('utf-8')
        return encoded_esdl

    def display_dashboard_url(self):
        """
        Function to display the Grafana Dashboard URL
        """
        if self.simulation_id is None:
            print('No simulation is started yet!')
            return
        status_path = '{}/{}'.format(ESSIM_URL, self.simulation_id)
        r = requests.get(url=status_path, headers=ESSIM_HEADERS)
        response = r.json()
        status_code = r.status_code
        if status_code == 200:
            if 'dashboardURL' in response:
                self.dashboardURL = response['dashboardURL']
                print('Dashboard URL: {}'.format(self.dashboardURL))
            else:
                print('Dashboard URL not found! Simulation meta-data looks like so:\n{}'.format(
                    json.dumps(response, indent=4, sort_keys=True)))
        elif status_code == 404:
            print(response['Description'])

    def display_progress(self):
        """
        Function to display progress of ESSIM simulation
        """
        if self.simulation_id is None:
            print('No simulation is started yet!')
            return

        status_path = '{}/{}/status'.format(ESSIM_URL, self.simulation_id)
        while True:
            r = requests.get(url=status_path, headers=ESSIM_HEADERS)
            response = r.json()
            status_code = r.status_code
            if status_code == 200:
                if response['State'] == 'RUNNING':
                    print('{:.1f}% complete'.format(100 * float(response['Description'])))
                    time.sleep(PROGRESS_UPDATE_INTERVAL)
                elif response['State'] == 'COMPLETE':
                    print('Simulation {}'.format(response['Description']))
                    break
                elif response['State'] == 'ERROR':
                    print('Simulation failed because of {}'.format(response['Description']))
                    break
            elif status_code == 404:
                print(response['Description'])
                break

    def start_simulation(self):
        """
        Function to start an ESSIM simulation
        """
        while True:
            data = {
                'user': ESSIM_USER,
                'startDate': START_DATE.strftime(ESSIM_DATE_FORMAT),
                'endDate': END_DATE.strftime(ESSIM_DATE_FORMAT),
                'scenarioID': ESSIM_SCENARIO_ID,
                'simulationDescription': SIMULATION_DESCRIPTION,
                'influxURL': INFLUXDB_URL,
                'esdlContents': self.encode_esdl()
            }
            print('Starting ESSIM Simulation')
            r = requests.post(url=ESSIM_URL, data=json.dumps(data), headers=ESSIM_HEADERS)
            response = r.json()
            status_code = r.status_code
            if status_code == 201:
                self.simulation_id = response['id']
                print(
                    'Successfully started ESSIM Simulation with id {id}'.format(id=response['id']))
                break
            elif status_code == 503:
                print('The ESSIM Engine is busy. Retrying in 5 seconds...')
                time.sleep(5)
            else:
                error = response['description']
                print('ESSIM Simulation failed because: {reason}'.format(reason=error))
                break


if __name__ == '__main__':
    essim = ESSIMSimulation(ESDL_FILE)
    essim.start_simulation()
    essim.display_dashboard_url()
    essim.display_progress()
    print('Done!')

ESDL File (sim.esdl):

<?xml version='1.0' encoding='UTF-8'?>
<esdl:EnergySystem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:esdl="http://www.tno.nl/esdl" esdlVersion="v2102" version="3" id="ee10ca5f-a08d-4201-823c-fa7abcc1ba1b" name="Untitled EnergySystem" description="">
  <energySystemInformation xsi:type="esdl:EnergySystemInformation" id="751842b7-80e1-4e82-a222-fdebeca8a797">
    <carriers xsi:type="esdl:Carriers" id="0a3cb696-2558-48f4-9e23-aff11e1020a4">
      <carrier xsi:type="esdl:HeatCommodity" name="Heat" id="fcd62e8f-ef7a-404c-8d3f-d94e9dd48cd3" supplyTemperature="80.0" returnTemperature="40.0"/>
    </carriers>
    <quantityAndUnits xsi:type="esdl:QuantityAndUnits" id="3f1a3603-2018-4a16-97e9-67eeee195a5b">
      <quantityAndUnit xsi:type="esdl:QuantityAndUnitType" physicalQuantity="ENERGY" unit="JOULE" multiplier="GIGA" id="eb07bccb-203f-407e-af98-e687656a221d" description="Energy in GJ"/>
    </quantityAndUnits>
  </energySystemInformation>
  <instance xsi:type="esdl:Instance" name="Untitled Instance" id="42a64855-de86-4a66-b267-b00c66b43b58">
    <area xsi:type="esdl:Area" name="Untitled Area" id="7bcf5cd9-d3fe-4626-859a-c16d89ddf552">
      <asset xsi:type="esdl:GeothermalSource" name="GeothermalSource_b572" id="b572d6cb-313e-489f-b489-a86bbd6ecd21">
        <geometry xsi:type="esdl:Point" lon="4.702792167663575" CRS="WGS84" lat="52.12170613337859"/>
        <port xsi:type="esdl:OutPort" id="15ff7099-8b7f-46af-bcc4-63e03f17722e" name="Out" connectedTo="a09857b5-5093-499c-83ce-54ecc54a7518" carrier="fcd62e8f-ef7a-404c-8d3f-d94e9dd48cd3">
          <profile xsi:type="esdl:SingleValue" value="5.0" id="a3804b62-4205-4afb-bf78-60cd73d339f2">
            <profileQuantityAndUnit xsi:type="esdl:QuantityAndUnitReference" reference="eb07bccb-203f-407e-af98-e687656a221d"/>
          </profile>
        </port>
      </asset>
      <asset xsi:type="esdl:HeatingDemand" name="HeatingDemand_b505" id="b505c10b-bde4-4606-8d68-7f8e0188c383">
        <geometry xsi:type="esdl:Point" lon="4.712383747100831" CRS="WGS84" lat="52.12191692831886"/>
        <port xsi:type="esdl:InPort" connectedTo="15ff7099-8b7f-46af-bcc4-63e03f17722e" id="a09857b5-5093-499c-83ce-54ecc54a7518" name="In" carrier="fcd62e8f-ef7a-404c-8d3f-d94e9dd48cd3">
          <profile xsi:type="esdl:SingleValue" value="5.0" id="e19ae2d6-3ff7-494c-b8bf-86450d855838">
            <profileQuantityAndUnit xsi:type="esdl:QuantityAndUnitReference" reference="eb07bccb-203f-407e-af98-e687656a221d"/>
          </profile>
        </port>
      </asset>
    </area>
  </instance>
</esdl:EnergySystem>