11.08., 9:00 - 11:00: Due to updates GitLab will be unavailable for some minutes between 09:00 and 11:00.

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
This diff is collapsed.
This diff is collapsed.
import os
import unittest
from suqc.opp.config_parser import OppConfigFileBase, OppConfigType
class OppConfigFileBaseTest(unittest.TestCase):
NEW_FILE = "omnetpp_2.ini"
NEW_FILE_COMP = "omnetpp_compare.ini"
NEW_FILE_DEL = "omnetpp_del.ini"
@staticmethod
def get_object(
config, cfg_type=OppConfigType.EDIT_LOCAL, is_parent=False, path="omnetpp.ini"
):
return OppConfigFileBase.from_path(
os.path.join(os.path.split(__file__)[0], path), config, cfg_type, is_parent
)
@staticmethod
def save_object(obj: OppConfigFileBase, path):
with open(os.path.join(os.path.split(__file__)[0], path), "w") as f:
obj.writer(f)
@staticmethod
def get_lines(path):
with open(os.path.join(os.path.split(__file__)[0], path), "r") as f:
return f.readlines()
def tearDown(self) -> None:
f = os.path.join(os.path.split(__file__)[0], self.NEW_FILE)
if os.path.exists(f):
os.remove(f)
def test_set_default_exits(self):
opp = self.get_object("HighTrafficSettings", OppConfigType.READ_ONLY)
# set default on exiting must work
self.assertEqual(opp.setdefault("opt_3", '"val_3"'), '"val_3"')
# set new will ignore default on existing
self.assertEqual(opp.setdefault("opt_3", '"new"'), '"val_3"')
def test_set_default_not_exits(self):
opp = self.get_object("HighTrafficSettings", OppConfigType.READ_ONLY)
# must raise error on OppConfigType.READ_ONLY
self.assertRaises(NotImplementedError, opp.setdefault, "new_key", "42")
opp = self.get_object("HighTrafficSettings", OppConfigType.EDIT_LOCAL)
# must raise error on OppConfigType.EDIT_LOCAL
self.assertRaises(NotImplementedError, opp.setdefault, "new_key", "42")
opp = self.get_object("HighTrafficSettings", OppConfigType.EXT_DEL_LOCAL)
# must work on OppConfigType.EXT_DEL_LOCAL
ret = opp.setdefault("new_key", "42")
self.assertEqual(ret, "42")
self.assertEqual(opp["new_key"], "42")
# new key must be local
self.assertTrue(opp.is_local("new_key"))
def test_read_only(self):
opp = self.get_object("HighTrafficSettings", OppConfigType.READ_ONLY)
# reading must work
self.assertEqual(opp["opt_3"], '"val_3"')
self.assertEqual(opp["general_option"], '"VAL1"')
# setting new values must not work for (local and parent options)
self.assertRaises(NotImplementedError, opp.__setitem__, "opt_3", '"new_val"')
self.assertRaises(
NotImplementedError, opp.__setitem__, "general_option", '"new_val"'
)
def test_edit_local(self):
opp = self.get_object("HighTrafficSettings", OppConfigType.EDIT_LOCAL)
# reading must work
self.assertEqual(opp["opt_3"], '"val_3"')
self.assertEqual(opp["general_option"], '"VAL1"')
# setting new values must work for local options only
opp["opt_3"] = '"new_val"'
self.assertEqual(opp["opt_3"], '"new_val"')
# general_option belongs to parent config 'General'
self.assertRaises(
NotImplementedError, opp.__setitem__, "general_option", '"new_val"'
)
def test_hierarchy(self):
""" Ensure correct lookup order for extended configurations"""
opp = self.get_object("SlottedAloha2b", OppConfigType.EDIT_LOCAL)
self.assertListEqual(
opp.section_hierarchy,
[
"Config SlottedAloha2b",
"Config SlottedAloha2",
"Config SlottedAlohaBase",
"Config HighTrafficSettings",
"General",
],
)
opp = self.get_object("SlottedAloha1", OppConfigType.EDIT_LOCAL)
self.assertListEqual(
opp.section_hierarchy,
[
"Config SlottedAloha1",
"Config SlottedAlohaBase",
"Config LowTrafficSettings",
"General",
],
)
opp = self.get_object("General", OppConfigType.EDIT_LOCAL)
self.assertListEqual(opp.section_hierarchy, ["General"])
opp = self.get_object("SlottedAlohaBase", OppConfigType.EDIT_LOCAL)
self.assertListEqual(
opp.section_hierarchy, ["Config SlottedAlohaBase", "General"]
)
def test_override(self):
opp = self.get_object("SlottedAloha2", OppConfigType.EXT_DEL_LOCAL)
opp["opt_5"] = '"val_55"'
self.save_object(opp, self.NEW_FILE)
opp2 = opp = self.get_object(
"SlottedAloha2", OppConfigType.EXT_DEL_LOCAL, path=self.NEW_FILE
)
self.assertEqual(opp2["opt_5"], '"val_55"')
lines_new = [
line for line in self.get_lines(self.NEW_FILE) if not line.startswith("\n")
]
lines_comp = [
line
for line in self.get_lines(self.NEW_FILE_COMP)
if not line.startswith("\n")
]
self.assertListEqual(lines_new, lines_comp)
def test_safe_to_file(self):
sa_2 = self.get_object("SlottedAloha2", OppConfigType.EXT_DEL_LOCAL)
hts = self.get_object("HighTrafficSettings", OppConfigType.EXT_DEL_LOCAL)
self.assertEqual(sa_2["opt_HT"], '"overwritten_val_HT"')
self.assertEqual(
sa_2["general_option"],
'"general_option option overwritten by SlottedAloha2"',
)
self.assertEqual(hts["opt_HT"], '"val_HT"')
self.assertEqual(hts["general_option"], '"VAL1"')
def test_delete_key(self):
opp = self.get_object("SlottedAloha2", OppConfigType.EXT_DEL_LOCAL)
self.assertEqual(
opp["general_option"],
'"general_option option overwritten by SlottedAloha2"',
)
del opp["general_option"]
# after deletion value from General section must be accessible.
self.assertEqual(opp["general_option"], '"VAL1"')
self.save_object(opp, self.NEW_FILE)
lines_new = [
line for line in self.get_lines(self.NEW_FILE) if not line.startswith("\n")
]
lines_del = [
line
for line in self.get_lines(self.NEW_FILE_DEL)
if not line.startswith("\n")
]
self.assertListEqual(lines_new, lines_del)
[General]
general_option = "VAL1"
[Config SlottedAlohaBase]
opt_1 = "val_1"
[Config LowTrafficSettings]
opt_2 = "val_2"
[Config HighTrafficSettings]
opt_3 = "val_3"
opt_HT = "val_HT"
[Config SlottedAloha1]
extends = SlottedAlohaBase, LowTrafficSettings
opt_4 = "val_4"
[Config SlottedAloha2]
extends = SlottedAlohaBase, HighTrafficSettings
opt_5 = "val_5"
opt_HT = "overwritten_val_HT"
general_option = "general_option option overwritten by SlottedAloha2"
[Config SlottedAloha2a]
extends = SlottedAloha2
[Config SlottedAloha2b]
extends = SlottedAloha2
opt_6 = "val_6"
[General]
general_option = "VAL1"
[Config SlottedAlohaBase]
opt_1 = "val_1"
[Config LowTrafficSettings]
opt_2 = "val_2"
[Config HighTrafficSettings]
opt_3 = "val_3"
opt_HT = "val_HT"
[Config SlottedAloha1]
extends = SlottedAlohaBase, LowTrafficSettings
opt_4 = "val_4"
[Config SlottedAloha2]
extends = SlottedAlohaBase, HighTrafficSettings
opt_5 = "val_55"
opt_HT = "overwritten_val_HT"
general_option = "general_option option overwritten by SlottedAloha2"
[Config SlottedAloha2a]
extends = SlottedAloha2
[Config SlottedAloha2b]
extends = SlottedAloha2
opt_6 = "val_6"
[General]
general_option = "VAL1"
[Config SlottedAlohaBase]
opt_1 = "val_1"
[Config LowTrafficSettings]
opt_2 = "val_2"
[Config HighTrafficSettings]
opt_3 = "val_3"
opt_HT = "val_HT"
[Config SlottedAloha1]
extends = SlottedAlohaBase, LowTrafficSettings
opt_4 = "val_4"
[Config SlottedAloha2]
extends = SlottedAlohaBase, HighTrafficSettings
opt_5 = "val_5"
opt_HT = "overwritten_val_HT"
[Config SlottedAloha2a]
extends = SlottedAloha2
[Config SlottedAloha2b]
extends = SlottedAloha2
opt_6 = "val_6"
#!/usr/bin/env python3 #!/usr/bin/env python3
# TODO: """ << INCLUDE DOCSTRING (one-line or multi-line) >> """ # TODO: """ << INCLUDE DOCSTRING (one-line or multi-line) >> """
......
...@@ -8,7 +8,6 @@ from suqc.utils.dict_utils import change_dict ...@@ -8,7 +8,6 @@ from suqc.utils.dict_utils import change_dict
class PostScenarioChangesBase(object): class PostScenarioChangesBase(object):
def __init__(self, apply_default=False): def __init__(self, apply_default=False):
self._apply_scenario_changes = {} self._apply_scenario_changes = {}
...@@ -22,28 +21,38 @@ class PostScenarioChangesBase(object): ...@@ -22,28 +21,38 @@ class PostScenarioChangesBase(object):
self.add_scenario_change(AlwaysEnableMetaData()) self.add_scenario_change(AlwaysEnableMetaData())
self.add_scenario_change(ChangeDescription()) self.add_scenario_change(ChangeDescription())
def add_scenario_change(self, scenario_change: 'PostScenarioChange'): def add_scenario_change(self, scenario_change: "PostScenarioChange"):
# ABCScenarioChange in '' to support forward reference, # ABCScenarioChange in '' to support forward reference,
# see https://www.python.org/dev/peps/pep-0484/#forward-references # see https://www.python.org/dev/peps/pep-0484/#forward-references
if scenario_change.name in self._apply_scenario_changes.keys(): if scenario_change.name in self._apply_scenario_changes.keys():
raise KeyError(f"Scenario change with {scenario_change.name} is already present.") raise KeyError(
f"Scenario change with {scenario_change.name} is already present."
)
self._apply_scenario_changes[scenario_change.name] = scenario_change self._apply_scenario_changes[scenario_change.name] = scenario_change
def _collect_changes(self, scenario, parameter_id, run_id, parameter_variation): def _collect_changes(self, scenario, parameter_id, run_id, parameter_variation):
changes = {} changes = {}
for chn in self._apply_scenario_changes.values(): for chn in self._apply_scenario_changes.values():
changes.update(chn.get_changes_dict(scenario=scenario, changes.update(
parameter_id=parameter_id, chn.get_changes_dict(
run_id=run_id, scenario=scenario,
parameter_variation=parameter_variation)) parameter_id=parameter_id,
run_id=run_id,
parameter_variation=parameter_variation,
)
)
return changes return changes
def change_scenario(self, scenario, parameter_id, run_id, parameter_variation): def change_scenario(self, scenario, parameter_id, run_id, parameter_variation):
return change_dict(scenario, changes=self._collect_changes(scenario, parameter_id, run_id, parameter_variation)) return change_dict(
scenario,
changes=self._collect_changes(
scenario, parameter_id, run_id, parameter_variation
),
)
class PostScenarioChange(metaclass=abc.ABCMeta): class PostScenarioChange(metaclass=abc.ABCMeta):
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
...@@ -61,12 +70,15 @@ class AlwaysEnableMetaData(PostScenarioChange): ...@@ -61,12 +70,15 @@ class AlwaysEnableMetaData(PostScenarioChange):
class ChangeRealTimeSimTimeRatio(PostScenarioChange): class ChangeRealTimeSimTimeRatio(PostScenarioChange):
def __init__(self): def __init__(self):
super(ChangeRealTimeSimTimeRatio, self).__init__(name="real_time_sim_time_ratio") super(ChangeRealTimeSimTimeRatio, self).__init__(
name="real_time_sim_time_ratio"
)
def get_changes_dict(self, scenario, parameter_id, run_id, parameter_variation): def get_changes_dict(self, scenario, parameter_id, run_id, parameter_variation):
return {"realTimeSimTimeRatio": 0.0} # Speeds up the non-visual computations in case a ratio was set! return {
"realTimeSimTimeRatio": 0.0
} # Speeds up the non-visual computations in case a ratio was set!
class ChangeRandomNumber(PostScenarioChange): class ChangeRandomNumber(PostScenarioChange):
...@@ -75,7 +87,9 @@ class ChangeRandomNumber(PostScenarioChange): ...@@ -75,7 +87,9 @@ class ChangeRandomNumber(PostScenarioChange):
KEY_SIM_SEED = "simulationSeed" KEY_SIM_SEED = "simulationSeed"
def __init__(self, fixed=False, randint=False, par_and_run_id=False): def __init__(self, fixed=False, randint=False, par_and_run_id=False):
assert fixed + randint + par_and_run_id == 1, "Exactly one parameter has to be set to true" assert (
fixed + randint + par_and_run_id == 1
), "Exactly one parameter has to be set to true"
self._isfixed = fixed self._isfixed = fixed
self._fixed_randnr = None self._fixed_randnr = None
...@@ -91,17 +105,21 @@ class ChangeRandomNumber(PostScenarioChange): ...@@ -91,17 +105,21 @@ class ChangeRandomNumber(PostScenarioChange):
def get_changes_dict(self, scenario, parameter_id, run_id, parameter_variation): def get_changes_dict(self, scenario, parameter_id, run_id, parameter_variation):
if self._isfixed: if self._isfixed:
assert self._fixed_randnr is not None, "Fixed random number has to be set with method set_fixed_random_nr" assert (
self._fixed_randnr is not None
), "Fixed random number has to be set with method set_fixed_random_nr"
rnr = self._fixed_randnr rnr = self._fixed_randnr
elif self._israndint: elif self._israndint:
# 4294967295 = max unsigned 32 bit integer # 4294967295 = max unsigned 32 bit integer
rnr = np.random.randint(0, 4294967295) rnr = np.random.randint(0, 4294967295)
else: # --> self._isparid else: # --> self._isparid
rnr = (parameter_id * 1E6 + run_id) # the 1E6 is required to not have rnr = parameter_id * 1e6 + run_id # the 1E6 is required to not have
return {ChangeRandomNumber.KEY_FIXED: True, return {
ChangeRandomNumber.KEY_SEED: rnr, ChangeRandomNumber.KEY_FIXED: True,
ChangeRandomNumber.KEY_SIM_SEED: rnr} ChangeRandomNumber.KEY_SEED: rnr,
ChangeRandomNumber.KEY_SIM_SEED: rnr,
}
class ChangeScenarioName(PostScenarioChange): class ChangeScenarioName(PostScenarioChange):
...@@ -123,8 +141,13 @@ class ChangeDescription(PostScenarioChange): ...@@ -123,8 +141,13 @@ class ChangeDescription(PostScenarioChange):
def __init__(self): def __init__(self):
super(ChangeDescription, self).__init__(name="description") super(ChangeDescription, self).__init__(name="description")
def get_changes_dict(self, scenario, parameter_id, run_id, parameter_variation): def get_changes_dict(self, scenario, parameter_id, run_id, parameter_variation):
changes_in_description = " ".join(["applied parameter variation=", str(parameter_variation)]) changes_in_description = " ".join(
return {ChangeDescription.KEY_DESCRIPTION: "--".join([f"par_id={parameter_id} and run_id={run_id}", ["applied parameter variation=", str(parameter_variation)]
changes_in_description])} )
return {
ChangeDescription.KEY_DESCRIPTION: "--".join(
[f"par_id={parameter_id} and run_id={run_id}", changes_in_description]
)
}
...@@ -2,33 +2,43 @@ ...@@ -2,33 +2,43 @@
import os import os
import re import re
import time
from typing import * from typing import *
import pandas as pd import pandas as pd
from suqc.environment import VadereEnvironmentManager
from suqc.environment import EnvironmentManager
from suqc.utils.dict_utils import deep_dict_lookup from suqc.utils.dict_utils import deep_dict_lookup
class FileDataInfo(object): class FileDataInfo(object):
# Implemented in Vadere merge request !38, this is only a fallback mode and requires manual updating if there are # Implemented in Vadere merge request !38, this is only a fallback mode and requires
# changes in Vadere. See also vadere issue #199 and #201. # manual updating if there are changes in Vadere. See also Vadere issue #199 and #201.
map_outputtype2index = {"IdOutputFile": 1,
"LogEventOutputFile": 1, # all output types are from Vadere BUT "GeneralOutputFile"
"NoDataKeyOutputFile": 0, map_outputtype2index = {
"PedestrianIdOutputFile": 1, "IdOutputFile": 1,
"TimestepOutputFile": 1, "LogEventOutputFile": 1,
"TimestepPedestrianIdOutputFile": 2, "NoDataKeyOutputFile": 0,
"TimestepPedestrianIdOverlapOutputFile": 3, "PedestrianIdOutputFile": 1,
"TimestepPositionOutputFile": 3, "TimestepOutputFile": 1,
"TimestepRowOutputFile": 2} "TimestepPedestrianIdOutputFile": 2,
"TimestepPedestrianIdOverlapOutputFile": 3,
"TimestepPositionOutputFile": 3,
"TimestepRowOutputFile": 2,
"GeneralOutputFile": 1,
}
printFallbackMsg = False printFallbackMsg = False
def __init__(self, process_file, processors): def __init__(
self, process_file, processors=None, outputkey=None,
):
self.filename = process_file["filename"] self.filename = process_file["filename"]
self.output_key = process_file["type"].split(".")[-1] if outputkey is None:
self.output_key = process_file["type"].split(".")[-1]
else:
self.output_key = outputkey
self.processors = processors # not really needed yet, but maybe in future. self.processors = processors # not really needed yet, but maybe in future.
try: try:
...@@ -36,72 +46,39 @@ class FileDataInfo(object): ...@@ -36,72 +46,39 @@ class FileDataInfo(object):
except KeyError: except KeyError:
if not self.printFallbackMsg: if not self.printFallbackMsg:
self.printFallbackMsg = True self.printFallbackMsg = True
print(f"WARNING: file type {self.output_key} was not found in list, this may require an update. Setting " print(
f"number of index columns to 1.") f"WARNING: file type {self.output_key} was not found in list, this may require an update. Setting "
self.nr_row_indices = 1 # use simply first column as index f"number of index columns to 1."
)