Commit c9faf5a6 authored by Daniel Lehmberg's avatar Daniel Lehmberg

Merge branch 'update_suqc' into 'master'

Update suqc

See merge request !136
parents 7a9937ed 974b435a
Pipeline #280200 passed with stages
in 129 minutes and 38 seconds
...@@ -45,3 +45,27 @@ not installed successfully. ...@@ -45,3 +45,27 @@ not installed successfully.
### Introduction ### Introduction
See [SRC_PATH]/tutorial See [SRC_PATH]/tutorial
#### Using SUQC and Vadere
Here a few hints for your .scenario file for Vadere:
1. ScenarioChecker
Before running your scenario automatically on suqc, activate the ``ScenarioChecker`` (Project > Activate ScenarioChecker) and run it in the ``VadereGui``.
The ScenarioChecker will point out potential problems with your scenario file.
2. Default parameters
Make sure to set ``realTimeSimTimeRatio`` to 0.0. (Values > 0.0 slow down the simulation for the visualisation)
Another point that may cost a lot of computation time is the ``optimizationType``, consider using ``DISCRETE`` (discrete optimization) instead of ``NELDER_MEAD``. Please note, that ``varyStepDirection`` should always be activated with discrete optimization.
Remove ``attributesCar`` from the .scenario file if you are not using any vehicles to avoid confusion of attributes.
3. Visual check
Visually check the results of your simulation, maybe check upper and lower parameter bounds.
4. Clean topography
Remove elements in your topography that are not used. Sometimes through the interaction with the mouse, tiny obstacles or targets are created unintentionally.
Check the elements in your topography, you can take a look at the ``ElementTree`` in the Topography creator tab. Remove all elements that are unused, especially focusing on targets.
5. Data processors
Remove all data processors and output files that you don't use. In particular, remove the overlap processors, they are intended for testing purposes.
6. Reproducibility
Make sure that your runs are reproducible - work with a ``fixedSeed`` by activating ``useFixedSeed`` or save all the ``simulationSeed``s that have been used.
(Another way is to provide a ``fixedSeed`` for each runs with suqc, in this case make sure that ``useFixedSeed`` is true.)
setuptools>=39.1.0 setuptools>=39.1.0
fabric==2.4.0 fabric==2.4.0
pandas>=0.24.0,<1.0.0 pandas>=1.0.3
numpy>=1.17.0 numpy>=1.17.0
scikit-learn>=0.21.0 scikit-learn>=0.21.0
...@@ -2,15 +2,15 @@ ...@@ -2,15 +2,15 @@
import os import os
from setuptools import setup, find_packages from setuptools import find_packages, setup
from suqc.configuration import SuqcConfig
from suqc import __version__ from suqc import __version__
from suqc.configuration import SuqcConfig
# To generate a new requirements.txt file run in console (install vis 'pip3 install pipreqs'): # To generate a new requirements.txt file run in console (install vis 'pip3 install pipreqs'):
# pipreqs --use-local --force /home/daniel/REPOS/suq-controller # pipreqs --use-local --force /home/daniel/REPOS/suq-controller
with open('requirements.txt', "r") as f: with open("requirements.txt", "r") as f:
requirements = f.read().splitlines() requirements = f.read().splitlines()
# Writes a file that gives information about the version such that "suqc.__version__" provides the current version, # Writes a file that gives information about the version such that "suqc.__version__" provides the current version,
...@@ -27,7 +27,7 @@ setup( ...@@ -27,7 +27,7 @@ setup(
url="www.vadere.org", url="www.vadere.org",
packages=find_packages(), packages=find_packages(),
install_requires=requirements, install_requires=requirements,
data_files=[('suqc', ["suqc/PACKAGE.txt"])] data_files=[("suqc", ["suqc/PACKAGE.txt"])],
) )
os.remove(SuqcConfig.path_package_indicator_file()) os.remove(SuqcConfig.path_package_indicator_file())
......
...@@ -5,4 +5,4 @@ from suqc.parameter.postchanges import PostScenarioChangesBase ...@@ -5,4 +5,4 @@ from suqc.parameter.postchanges import PostScenarioChangesBase
from suqc.qoi import * from suqc.qoi import *
from suqc.request import * from suqc.request import *
__version__ = "2.0" __version__ = "2.1"
#!/usr/bin/env python3 #!/usr/bin/env python3
import os
import json import json
import os
import os.path as p import os.path as p
import pathlib import pathlib
# configuration of the suq-controller # configuration of the suq-controller
DEFAULT_SUQC_CONFIG = {"default_vadere_src_path": "TODO", # TODO Feature: #25 DEFAULT_SUQC_CONFIG = {
"server": { "default_vadere_src_path": "TODO", # TODO Feature: #25
"host": "", "server": {"host": "", "user": "", "port": -1},
"user": "", }
"port": -1}}
def check_setup(_paths_class): def check_setup(_paths_class):
if not os.path.exists(_paths_class.path_cfg_folder()) and _paths_class.is_package_paths(): if (
not os.path.exists(_paths_class.path_cfg_folder())
and _paths_class.is_package_paths()
):
print(f"INFO: Setting up configuration folder {_paths_class.path_cfg_folder()}") print(f"INFO: Setting up configuration folder {_paths_class.path_cfg_folder()}")
# the next two checks will fail automatically too, because the folder is empty # the next two checks will fail automatically too, because the folder is empty
os.mkdir(_paths_class.path_cfg_folder()) os.mkdir(_paths_class.path_cfg_folder())
if not os.path.exists(_paths_class.path_suq_config_file()): if not os.path.exists(_paths_class.path_suq_config_file()):
print(f"INFO: Setting up default configuration file located at {_paths_class.path_suq_config_file()}") print(
f"INFO: Setting up default configuration file located at "
f"{_paths_class.path_suq_config_file()}"
)
_paths_class.store_config(DEFAULT_SUQC_CONFIG) _paths_class.store_config(DEFAULT_SUQC_CONFIG)
if not os.path.exists(_paths_class.path_container_folder()): if not os.path.exists(_paths_class.path_container_folder()):
print(f"INFO: Setting up the default container path (which will store output of simulation runs). " print(
f"Location {_paths_class.path_container_folder()}") f"INFO: Setting up the default container path "
f"(which will store output of simulation runs). "
f"Location {_paths_class.path_container_folder()}"
)
os.mkdir(_paths_class.path_container_folder()) os.mkdir(_paths_class.path_container_folder())
return _paths_class return _paths_class
...@@ -128,4 +135,4 @@ class SuqcConfig(object): ...@@ -128,4 +135,4 @@ class SuqcConfig(object):
if __name__ == "__main__": if __name__ == "__main__":
print(SuqcConfig) print(SuqcConfig)
\ No newline at end of file
#!/usr/bin/env python3 #!/usr/bin/env python3
import platform
import json
import glob import glob
import json
import os
import shutil
import subprocess import subprocess
import time import time
import os from shutil import copytree, ignore_patterns, rmtree
from shutil import rmtree
from typing import * from typing import *
from suqc.configuration import SuqcConfig import pandas as pd
from suqc.utils.general import user_query_yes_no, get_current_suqc_state, str_timestamp
from suqc.configuration import SuqcConfig
from suqc.opp.config_parser import OppConfigFileBase, OppConfigType, OppParser
from suqc.utils.general import (
get_current_suqc_state,
str_timestamp,
user_query_yes_no,
include_patterns,
removeEmptyFolders,
)
# configuration of the suq-controller # configuration of the suq-controller
DEFAULT_SUQ_CONFIG = {"default_vadere_src_path": "TODO", DEFAULT_SUQ_CONFIG = {
"server": { "default_vadere_src_path": "TODO",
"host": "", "server": {"host": "", "user": "", "port": -1},
"user": "", }
"port": -1
}}
class AbstractConsoleWrapper(object):
@classmethod
def infer_model(cls, model) -> "AbstractConsoleWrapper":
if isinstance(model, str):
if model == "Coupled":
return CoupledConsoleWrapper(model)
else:
return VadereConsoleWrapper.infer_model(model)
elif isinstance(model, VadereConsoleWrapper) or isinstance(
model, CoupledConsoleWrapper
):
return model
else:
raise ValueError(
f"Model must be of type string or VadereConsoleWrapper or CoupledConsoleWrapper. Got type {type(model)}."
)
class CoupledConsoleWrapper(AbstractConsoleWrapper):
def __init__(self, model):
self.simulator = model
def run_simulation(
self, dirname, start_file, required_files: Union[str, List[str]]
):
terminal_command = ["python3", start_file, "--qoi"]
terminal_command.extend(required_files)
terminal_command.extend(["--run-name", os.path.basename(dirname)])
terminal_command.extend(["--create-vadere-container"])
class VadereConsoleWrapper(object): time_started = time.time()
t = time.strftime("%H:%M:%S", time.localtime(time_started))
print(f"{t}\t Call {os.path.basename(dirname)}/{start_file} ")
return_code = subprocess.check_call(
terminal_command,
env=os.environ,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
cwd=dirname,
timeout=10800 # stop simulation after 3h
)
process_duration = time.time() - time_started
output_subprocess = None
return return_code, process_duration, output_subprocess
# Current log level choices, requires to manually add, if there are changes
ALLOWED_LOGLVL = ["OFF", "FATAL", "TOPOGRAPHY_ERROR", "TOPOGRAPHY_WARN", "INFO", "DEBUG", "ALL"]
def __init__(self, model_path: str, loglvl="INFO", jvm_flags: Optional[List] = class VadereConsoleWrapper(AbstractConsoleWrapper):
None, timeout_sec=None):
# Current log level choices, requires to manually add, if there are changes in Vadere
ALLOWED_LOGLVL = [
"OFF",
"FATAL",
"TOPOGRAPHY_ERROR",
"TOPOGRAPHY_WARN",
"INFO",
"DEBUG",
"ALL",
]
def __init__(
self,
model_path: str,
loglvl="INFO",
jvm_flags: Optional[List] = None,
timeout_sec=None,
):
self.jar_path = os.path.abspath(model_path) self.jar_path = os.path.abspath(model_path)
if not os.path.exists(self.jar_path): if not os.path.exists(self.jar_path):
raise FileNotFoundError( raise FileNotFoundError(
f"Vadere console .jar file {self.jar_path} does not exist.") f"Vadere console .jar file {self.jar_path} does not exist."
)
loglvl = loglvl.upper() loglvl = loglvl.upper()
if loglvl not in self.ALLOWED_LOGLVL: if loglvl not in self.ALLOWED_LOGLVL:
raise ValueError(f"set loglvl={loglvl} not contained " raise ValueError(
f"in allowed: {self.ALLOWED_LOGLVL}") f"set loglvl={loglvl} not contained "
f"in allowed: {self.ALLOWED_LOGLVL}"
)
if jvm_flags is not None and not isinstance(jvm_flags, list): if jvm_flags is not None and not isinstance(jvm_flags, list):
raise TypeError( raise TypeError(
f"jvm_flags are required to be a list. Got: {type(jvm_flags)}") f"jvm_flags are required to be a list. Got: {type(jvm_flags)}"
)
if timeout_sec is None: if timeout_sec is None:
pass # do nothing, no timeout pass # do nothing, no timeout
elif not isinstance(timeout_sec, int) or timeout_sec <= 0: elif not isinstance(timeout_sec, int) or timeout_sec <= 0:
raise TypeError("vadere_run_timeout_sec must be of type int and positive " raise TypeError(
"value") "vadere_run_timeout_sec must be of type int and positive " "value"
)
self.loglvl = loglvl self.loglvl = loglvl
# Additional Java Virtual Machine options / flags # Additional Java Virtual Machine options / flags
self.jvm_flags = jvm_flags if jvm_flags is not None else [] self.jvm_flags = jvm_flags if jvm_flags is not None else []
self.timeout_sec = timeout_sec self.timeout_sec = timeout_sec
@classmethod
def infer_model(cls, model):
if isinstance(model, str):
if os.path.exists(model):
return VadereConsoleWrapper.from_model_path(os.path.abspath(model))
else:
return VadereConsoleWrapper.from_default_models(model)
elif isinstance(model, VadereConsoleWrapper):
return model
else:
raise ValueError(f"Failed to infer Vadere model. \n {model}")
def run_simulation(self, scenario_fp, output_path): def run_simulation(self, scenario_fp, output_path):
start = time.time() start = time.time()
...@@ -69,9 +156,9 @@ class VadereConsoleWrapper(object): ...@@ -69,9 +156,9 @@ class VadereConsoleWrapper(object):
output_subprocess = dict() output_subprocess = dict()
try: try:
subprocess.check_output(subprocess_cmd, subprocess.check_output(
timeout=self.timeout_sec, subprocess_cmd, timeout=self.timeout_sec, stderr=subprocess.PIPE
stderr=subprocess.PIPE) )
process_duration = time.time() - start process_duration = time.time() - start
# if return_code != 0 a subprocess.CalledProcessError is raised # if return_code != 0 a subprocess.CalledProcessError is raised
...@@ -100,28 +187,13 @@ class VadereConsoleWrapper(object): ...@@ -100,28 +187,13 @@ class VadereConsoleWrapper(object):
def from_model_path(cls, model_path): def from_model_path(cls, model_path):
return cls(model_path) return cls(model_path)
@classmethod
def infer_model(cls, model):
if isinstance(model, str):
if os.path.exists(model):
return VadereConsoleWrapper.from_model_path(os.path.abspath(model))
else:
return VadereConsoleWrapper.from_default_models(model)
elif isinstance(model, VadereConsoleWrapper):
return model
else:
raise ValueError(f"Failed to infer Vadere model. \n {model}")
class EnvironmentManager(object):
PREFIX_BASIS_SCENARIO = "BASIS_"
VADERE_SCENARIO_FILE_TYPE = ".scenario"
vadere_output_folder = "vadere_output"
class AbstractEnvironmentManager(object):
def __init__(self, base_path, env_name: str): def __init__(self, base_path, env_name: str):
self.base_path, self.env_name = self.handle_path_and_env_input(base_path, env_name) self.base_path, self.env_name = self.handle_path_and_env_input(
base_path, env_name
)
self.env_name = env_name self.env_name = env_name
self.env_path = self.output_folder_path(self.base_path, self.env_name) self.env_path = self.output_folder_path(self.base_path, self.env_name)
...@@ -135,48 +207,54 @@ class EnvironmentManager(object): ...@@ -135,48 +207,54 @@ class EnvironmentManager(object):
print(f"INFO: Set environment path to {self.env_path}") print(f"INFO: Set environment path to {self.env_path}")
if not os.path.exists(self.env_path): if not os.path.exists(self.env_path):
raise FileNotFoundError(f"Environment {self.env_path} does not exist. Use function " raise FileNotFoundError(
f"'EnvironmentManager.create_new_environment'") f"Environment {self.env_path} does not exist. Use function "
self._scenario_basis = None f"'EnvironmentManager.create_new_environment'"
)
self._vadere_scenario_basis = None
@property @property
def basis_scenario(self): def vadere_basis_scenario(self):
if self._scenario_basis is None: if self._vadere_scenario_basis is None:
path_basis_scenario = self.path_basis_scenario path_basis_scenario = self.vadere_path_basis_scenario
with open(path_basis_scenario, "r") as f: with open(path_basis_scenario, "r") as f:
basis_file = json.load(f) basis_file = json.load(f)
self._scenario_basis = basis_file self._vadere_scenario_basis = basis_file
return self._scenario_basis return self._vadere_scenario_basis
@property @property
def path_basis_scenario(self): def vadere_path_basis_scenario(self):
sc_files = glob.glob(os.path.join(self.env_path, f"*{self.VADERE_SCENARIO_FILE_TYPE}")) sc_files = glob.glob(
os.path.join(self.env_path, f"*{self.VADERE_SCENARIO_FILE_TYPE}")
)
if len(sc_files) != 1: if len(sc_files) != 1:
raise RuntimeError(f"None or too many '{self.VADERE_SCENARIO_FILE_TYPE}' files " raise RuntimeError(
"found in environment.") f"None or too many '{self.VADERE_SCENARIO_FILE_TYPE}' files "
"found in environment."
)
return sc_files[0] return sc_files[0]
@classmethod @classmethod
def from_full_path(cls, env_path): def create_variation_env_from_info_file(cls, path_info_file):
assert os.path.isdir(env_path) raise NotImplemented
base_path = os.path.dirname(env_path)
if env_path.endswith(os.pathsep):
env_path = env_path.rstrip(os.path.sep)
env_name = os.path.basename(env_path)
cls(base_path=base_path, env_name=env_name)
@classmethod @classmethod
def create_new_environment(cls, base_path=None, env_name=None, handle_existing="ask_user_replace"): def create_new_environment(
cls, base_path=None, env_name=None, handle_existing="ask_user_replace"
):
base_path, env_name = cls.handle_path_and_env_input(base_path, env_name) base_path, env_name = cls.handle_path_and_env_input(base_path, env_name)
# TODO: Refactor, make handle_existing an Enum # TODO: Refactor, make handle_existing an Enum
assert handle_existing in ["ask_user_replace", "force_replace", "write_in_if_exist_else_create", "write_in"] assert handle_existing in [
"ask_user_replace",
"force_replace",
"write_in_if_exist_else_create",
"write_in",
]
# set to True if env already exists, and it shouldn't be overwritten # set to True if env already exists, and it shouldn't be overwritten
about_creating_env = False about_creating_env = False
...@@ -191,7 +269,9 @@ class EnvironmentManager(object): ...@@ -191,7 +269,9 @@ class EnvironmentManager(object):
if env_exists: if env_exists:
cls.remove_environment(base_path, env_name, force=True) cls.remove_environment(base_path, env_name, force=True)
elif handle_existing == "write_in": elif handle_existing == "write_in":
assert env_exists, f"base_path={base_path} env_name={env_name} does not exist" assert (
env_exists
), f"base_path={base_path} env_name={env_name} does not exist"
env_man = cls(base_path=base_path, env_name=env_name) env_man = cls(base_path=base_path, env_name=env_name)
elif handle_existing == "write_in_if_exist_else_create": elif handle_existing == "write_in_if_exist_else_create":
if env_exists: if env_exists:
...@@ -207,13 +287,137 @@ class EnvironmentManager(object): ...@@ -207,13 +287,137 @@ class EnvironmentManager(object):
return env_man return env_man
@classmethod
def remove_environment(cls, base_path, name, force=False):
target_path = cls.output_folder_path(base_path, name)
if force or user_query_yes_no(
question=f"Are you sure you want to remove the current environment? Path: \n "
f"{target_path}"
):
try:
rmtree(target_path)
except FileNotFoundError:
print(f"INFO: Tried to remove environment {name}, but did not exist.")
return True
return False
@classmethod @classmethod
def create_variation_env(cls, basis_scenario: Union[str, dict], base_path=None, env_name=None, def from_full_path(cls, env_path):
handle_existing="ask_user_replace"): assert os.path.isdir(env_path)
base_path = os.path.dirname(env_path)
if env_path.endswith(os.pathsep):
env_path = env_path.rstrip(os.path.sep)
env_name = os.path.basename(env_path)
cls(base_path=base_path, env_name=env_name)
@staticmethod
def handle_path_and_env_input(base_path, env_name):
if env_name is None:
env_name = "_".join(["output", str_timestamp()])
if base_path is None:
base_path = SuqcConfig.path_container_folder()
return base_path, env_name
@staticmethod
def output_folder_path(base_path, env_name):
base_path, env_name = VadereEnvironmentManager.handle_path_and_env_input(
base_path, env_name
)
assert os.path.isdir(base_path)
output_folder_path = os.path.join(base_path, env_name)
return output_folder_path
def scenario_variation_path(self, par_id, run_id):
return os.path.join(
self.get_env_outputfolder_path(),
self._scenario_variation_filename(par_id, run_id),
)
def save_scenario_variation(self, par_id, run_id, content):
scenario_path = self.scenario_variation_path(par_id, run_id)
assert not os.path.exists(
scenario_path
), f"File {scenario_path} already exists!"
with open(scenario_path, "w") as outfile:
json.dump(content, outfile, indent=4)
return scenario_path
def get_temp_folder(self):
raise NotImplemented
def get_env_outputfolder_path(self):
raise NotImplemented
def get_variation_output_folder(self, parameter_id, run_id):
scenario_filename = self._scenario_variation_filename(
parameter_id=parameter_id, run_id=run_id
)
scenario_filename = scenario_filename.replace(
self.VADERE_SCENARIO_FILE_TYPE, ""
)
return os.path.join(
self.get_env_outputfolder_path(), "".join([scenario_filename, "_output"])
)