markaren: Mhh.. the PyCharm formatter uses more indents :/ We should probably standardize on the formatiing rules.
markaren: It looks to be the `Continuation indent` setting is set to 8 in PyCharm, while you're formatter uses 6 as indent.
fcollonval: This is weird, the convention in Python is to use N * 4 spaces as indent convention. Where did you see a 6 spaces indent?
What I know to be different in PyCharm compare to black is the handling of long string and maybe the line length (black uses 88).

For reference the [black rules](https://black.readthedocs.io/en/stable/the_black_code_style.html)
markaren: In was wrong with 6 in black. I'ts 4
markaren: I can set the continuation indent to 4 in PyCharm. Merging this.
diff --git a/pythonfmu/builder.py b/pythonfmu/builder.py
index 90a6b49..feb8fcf 100644
--- a/pythonfmu/builder.py
+++ b/pythonfmu/builder.py
@@ -53,7 +53,7 @@ class ModelDescriptionFetcher:
             instance = getattr(fmu_interface, class_name)("dummyInstance")
         finally:
             sys.path.remove(str(filepath.parent))  # remove inserted temporary path
-        
+
         if not isinstance(instance, Fmi2Slave):
             raise TypeError(
                 f"The provided class '{class_name}' does not inherit from {Fmi2Slave.__qualname__}"
@@ -88,7 +88,9 @@ class FmuBuilder:
         if documentation_folder is not None:
             documentation_folder = Path(documentation_folder)
             if not documentation_folder.exists():
-                raise ValueError(f"The documentation folder does not exists {documentation_folder!s}")
+                raise ValueError(
+                    f"The documentation folder does not exists {documentation_folder!s}"
+                )
 
         script_parent = script_file.resolve().parent.absolute()
         module_name = script_file.stem
@@ -159,7 +161,9 @@ class FmuBuilder:
                 binaries = Path("binaries")
                 src_binaries = HERE / "resources" / "binaries"
                 for f in itertools.chain(
-                    src_binaries.rglob("*.dll"), src_binaries.rglob("*.so"), src_binaries.rglob("*.dylib")
+                    src_binaries.rglob("*.dll"),
+                    src_binaries.rglob("*.so"),
+                    src_binaries.rglob("*.dylib"),
                 ):
                     relative_f = f.relative_to(src_binaries)
                     arcname = (
@@ -215,7 +219,10 @@ def main():
     )
 
     parser.add_argument(
-        "--doc", dest="documentation_folder", help="Documentation folder to include in the FMU.", default=None
+        "--doc",
+        dest="documentation_folder",
+        help="Documentation folder to include in the FMU.",
+        default=None,
     )
 
     # for option in FMI2_MODEL_OPTIONS:
diff --git a/pythonfmu/enums.py b/pythonfmu/enums.py
index 2cdece7..3557150 100644
--- a/pythonfmu/enums.py
+++ b/pythonfmu/enums.py
@@ -1,23 +1,24 @@
+"""FMI enumerations"""
 from enum import Enum
 
 
 class Fmi2Causality(Enum):
-    parameter = 0,
-    calculatedParameter = 1,
-    input = 2,
-    output = 3,
+    parameter = 0
+    calculatedParameter = 1
+    input = 2
+    output = 3
     local = 4
 
 
 class Fmi2Initial(Enum):
-    exact = 0,
-    approx = 1,
+    exact = 0
+    approx = 1
     calculated = 2
 
 
 class Fmi2Variability(Enum):
-    constant = 0,
-    fixed = 1,
-    tunable = 2,
-    discrete = 3,
+    constant = 0
+    fixed = 1
+    tunable = 2
+    discrete = 3
     continuous = 4
diff --git a/pythonfmu/fmi2slave.py b/pythonfmu/fmi2slave.py
index bdb89ad..b418e14 100644
--- a/pythonfmu/fmi2slave.py
+++ b/pythonfmu/fmi2slave.py
@@ -1,14 +1,16 @@
+"""Define the abstract facade class."""
 import datetime
 from abc import ABC, abstractmethod
-from collections import namedtuple, OrderedDict
+from collections import OrderedDict, namedtuple
 from typing import Any, ClassVar, Dict, List, Optional
 from uuid import uuid1
 from xml.etree.ElementTree import Element, SubElement
 
+from ._version import __version__ as VERSION
 from .enums import Fmi2Causality, Fmi2Initial, Fmi2Variability
 from .variables import Boolean, Integer, Real, ScalarVariable, String
 
-ModelOptions = namedtuple('ModelOptions', ['name', 'value', 'cli'])
+ModelOptions = namedtuple("ModelOptions", ["name", "value", "cli"])
 
 FMI2_MODEL_OPTIONS: List[ModelOptions] = [
     ModelOptions("needsExecutionTool", True, "no-external-tool"),
@@ -22,6 +24,7 @@ FMI2_MODEL_OPTIONS: List[ModelOptions] = [
 
 
 class Fmi2Slave(ABC):
+    """Abstract facade class to execute Python through FMI standard."""
 
     guid: ClassVar[str] = uuid1()
     author: ClassVar[Optional[str]] = None
@@ -48,52 +51,54 @@ class Fmi2Slave(ABC):
         """
 
         t = datetime.datetime.now(datetime.timezone.utc)
-        date_str = t.isoformat(timespec='seconds')
+        date_str = t.isoformat(timespec="seconds")
 
-        attrib=dict(
+        attrib = dict(
             fmiVersion="2.0",
             modelName=self.modelName,
             guid=f"{self.guid!s}",
-            generationTool="PythonFMU",
+            generationTool=f"PythonFMU {VERSION}",
             generationDateAndTime=date_str,
-            variableNamingConvention="structured"
+            variableNamingConvention="structured",
         )
         if self.description is not None:
-            attrib['description'] = self.description
+            attrib["description"] = self.description
         if self.author is not None:
-            attrib['author'] = self.author
+            attrib["author"] = self.author
         if self.license is not None:
-            attrib['license'] = self.license
+            attrib["license"] = self.license
         if self.version is not None:
-            attrib['version'] = self.version
+            attrib["version"] = self.version
         if self.copyright is not None:
-            attrib['copyright'] = self.copyright
+            attrib["copyright"] = self.copyright
 
-        root = Element('fmiModelDescription', attrib)
+        root = Element("fmiModelDescription", attrib)
 
         options = dict()
         for option in FMI2_MODEL_OPTIONS:
             value = model_options.get(option.name, option.value)
             v = "true" if value else "false"
             options[option.name] = v
-        options["modelIdentifier"]=self.modelName
+        options["modelIdentifier"] = self.modelName
 
-        SubElement(root, 'CoSimulation', attrib=options)
+        SubElement(root, "CoSimulation", attrib=options)
 
-        variables = SubElement(root, 'ModelVariables')
+        variables = SubElement(root, "ModelVariables")
         for v in self.vars.values():
             if ScalarVariable.requires_start(v):
                 self.__apply_start_value(v)
             variables.append(v.to_xml())
 
-        structure = SubElement(root, 'ModelStructure')
-        outputs = list(filter(lambda v: v.causality == Fmi2Causality.output, self.vars.values()))
-        
+        structure = SubElement(root, "ModelStructure")
+        outputs = list(
+            filter(lambda v: v.causality == Fmi2Causality.output, self.vars.values())
+        )
+
         if outputs:
-            outputs_node = SubElement(structure, 'Outputs')
+            outputs_node = SubElement(structure, "Outputs")
             for i, v in enumerate(self.vars.values()):
                 if v.causality == Fmi2Causality.output:
-                    SubElement(outputs_node, 'Unknown', attrib=dict(index=str(i+1)))
+                    SubElement(outputs_node, "Unknown", attrib=dict(index=str(i + 1)))
 
         return root
 
@@ -165,7 +170,9 @@ class Fmi2Slave(ABC):
             if isinstance(var, Integer):
                 refs.append(int(self.get_value(var.name)))
             else:
-                raise TypeError(f"Variable with valueReference={vr} is not of type Integer!")
+                raise TypeError(
+                    f"Variable with valueReference={vr} is not of type Integer!"
+                )
         return refs
 
     def get_real(self, vrs: List[int]) -> List[float]:
@@ -175,7 +182,9 @@ class Fmi2Slave(ABC):
             if isinstance(var, Real):
                 refs.append(float(self.get_value(var.name)))
             else:
-                raise TypeError(f"Variable with valueReference={vr} is not of type Real!")
+                raise TypeError(
+                    f"Variable with valueReference={vr} is not of type Real!"
+                )
         return refs
 
     def get_boolean(self, vrs: List[int]) -> List[bool]:
@@ -185,7 +194,9 @@ class Fmi2Slave(ABC):
             if isinstance(var, Boolean):
                 refs.append(bool(self.get_value(var.name)))
             else:
-                raise TypeError(f"Variable with valueReference={vr} is not of type Boolean!")
+                raise TypeError(
+                    f"Variable with valueReference={vr} is not of type Boolean!"
+                )
         return refs
 
     def get_string(self, vrs: List[int]) -> List[str]:
@@ -195,7 +206,9 @@ class Fmi2Slave(ABC):
             if isinstance(var, String):
                 refs.append(str(self.get_value(var.name)))
             else:
-                raise TypeError(f"Variable with valueReference={vr} is not of type String!")
+                raise TypeError(
+                    f"Variable with valueReference={vr} is not of type String!"
+                )
         return refs
 
     def set_integer(self, vrs: List[int], values: List[int]):
@@ -204,7 +217,9 @@ class Fmi2Slave(ABC):
             if isinstance(var, Integer):
                 self.set_value(var.name, value)
             else:
-                raise TypeError(f"Variable with valueReference={vr} is not of type Integer!")
+                raise TypeError(
+                    f"Variable with valueReference={vr} is not of type Integer!"
+                )
 
     def set_real(self, vrs: List[int], values: List[float]):
         for vr, value in zip(vrs, values):
@@ -212,7 +227,9 @@ class Fmi2Slave(ABC):
             if isinstance(var, Real):
                 self.set_value(var.name, value)
             else:
-                raise TypeError(f"Variable with valueReference={vr} is not of type Real!")
+                raise TypeError(
+                    f"Variable with valueReference={vr} is not of type Real!"
+                )
 
     def set_boolean(self, vrs: List[int], values: List[bool]):
         for vr, value in zip(vrs, values):
@@ -220,7 +237,9 @@ class Fmi2Slave(ABC):
             if isinstance(var, Boolean):
                 self.set_value(var.name, value)
             else:
-                raise TypeError(f"Variable with valueReference={vr} is not of type Boolean!")
+                raise TypeError(
+                    f"Variable with valueReference={vr} is not of type Boolean!"
+                )
 
     def set_string(self, vrs: List[int], values: List[str]):
         for vr, value in zip(vrs, values):
@@ -228,4 +247,6 @@ class Fmi2Slave(ABC):
             if isinstance(var, String):
                 self.set_value(var.name, value)
             else:
-                raise TypeError(f"Variable with valueReference={vr} is not of type String!")
+                raise TypeError(
+                    f"Variable with valueReference={vr} is not of type String!"
+                )
diff --git a/pythonfmu/variables.py b/pythonfmu/variables.py
index 1d169e8..741f752 100644
--- a/pythonfmu/variables.py
+++ b/pythonfmu/variables.py
@@ -1,3 +1,4 @@
+"""Classes describing interface variables."""
 from abc import ABC
 from enum import Enum
 from typing import Any, Optional
@@ -57,11 +58,13 @@ class ScalarVariable(ABC):
 
     @staticmethod
     def requires_start(v) -> bool:
-        return v.initial == Fmi2Initial.exact or \
-               v.initial == Fmi2Initial.approx or \
-               v.causality == Fmi2Causality.input or \
-               v.causality == Fmi2Causality.parameter or \
-               v.variability == Fmi2Variability.constant
+        return (
+            v.initial == Fmi2Initial.exact
+            or v.initial == Fmi2Initial.approx
+            or v.causality == Fmi2Causality.input
+            or v.causality == Fmi2Causality.parameter
+            or v.variability == Fmi2Variability.constant
+        )
 
     def to_xml(self) -> Element:
         attrib = dict()

Add PythonFMU version to modelDescription.xml
Version should be read from _version.py
diff --git a/pythonfmu/tests/test_fmi2slave.py b/pythonfmu/tests/test_fmi2slave.py
index 10c727d..814499a 100644
--- a/pythonfmu/tests/test_fmi2slave.py
+++ b/pythonfmu/tests/test_fmi2slave.py
@@ -1,6 +1,7 @@
 import pytest
 
 from pythonfmu import Fmi2Slave, Boolean, Integer, Real, String
+from pythonfmu import __version__ as VERSION
 
 # TODO test xml
 
@@ -27,6 +28,16 @@ def test_Fmi2Slave_constructor(model):
         _ = Slave("instanceSlave")
         assert Slave.modelName == model
 
+def test_Fmi2Slave_generation_tool():
+    class Slave(Fmi2Slave):
+        def do_step(self, t, dt):
+            return True
+    
+    slave = Slave("instance")
+    xml = slave.to_xml()
+
+    assert xml.attrib['generationTool'] == f"PythonFMU {VERSION}"
+
 @pytest.mark.parametrize("fmi_type", FMI2PY)
 @pytest.mark.parametrize("value", [
     False, 

pip install -e .[tests]
parse_log_pytest
pytest pyfmi
pytest
apt-get update apt-get install -y gcc cmake
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
Assimulo==3.3
attrs @ file:///croot/attrs_1668696182826/work
certifi @ file:///croot/certifi_1671487769961/work/certifi
cycler==0.11.0
Cython==3.0.12
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
fonttools==4.38.0
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1648562407465/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
kiwisolver==1.4.5
lxml @ file:///opt/conda/conda-bld/lxml_1657545139709/work
matplotlib==3.5.3
nose==1.3.7
numpy @ file:///opt/conda/conda-bld/numpy_and_numpy_base_1653915516269/work
packaging @ file:///croot/packaging_1671697413597/work
Pillow==9.5.0
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
PyFMI==2.9.8
pyparsing==3.1.4
pytest==7.1.2
python-dateutil==2.9.0.post0
-e git+https://github.com/NTNU-IHB/PythonFMU.git@893ec30ca017a19382f332b36cf36f497032ba14#egg=pythonfmu
scipy @ file:///opt/conda/conda-bld/scipy_1661390393401/work
six==1.17.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typing_extensions @ file:///croot/typing_extensions_1669924550328/work
zipp @ file:///croot/zipp_1672387121353/work

name: PythonFMU
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - assimulo=3.3=py37h0a1b189_0
  - attrs=22.1.0=py37h06a4308_0
  - blas=1.0=openblas
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - fftw=3.3.9=h5eee18b_2
  - flit-core=3.6.0=pyhd3eb1b0_0
  - fmilib=2.4.1=hac33072_1
  - gmp=6.3.0=h6a678d5_0
  - icu=58.2=he6710b0_3
  - importlib-metadata=4.11.3=py37h06a4308_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libblas=3.9.0=16_linux64_openblas
  - libffi=3.4.4=h6a678d5_1
  - libgcc=14.2.0=h767d61c_2
  - libgcc-ng=14.2.0=h69a702a_2
  - libgfortran-ng=11.2.0=h00389a5_1
  - libgfortran5=11.2.0=h1234567_1
  - libgomp=14.2.0=h767d61c_2
  - liblapack=3.9.0=16_linux64_openblas
  - libopenblas=0.3.21=h043d6bf_0
  - libstdcxx=14.2.0=h8f9b012_2
  - libstdcxx-ng=14.2.0=h4852527_2
  - libxml2=2.9.14=h74e7548_0
  - libxslt=1.1.35=h4e12654_0
  - lxml=4.9.1=py37h1edc446_0
  - metis=5.1.0=hf484d3e_4
  - mpfr=4.2.1=h5eee18b_0
  - ncurses=6.4=h6a678d5_0
  - numpy=1.21.5=py37hf838250_3
  - numpy-base=1.21.5=py37h1e6e340_3
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pyfmi=2.9.8=py37hc105733_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - python_abi=3.7=2_cp37m
  - readline=8.2=h5eee18b_0
  - scipy=1.7.3=py37hf838250_2
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - suitesparse=5.10.1=he2db622_2
  - sundials=6.3.0=h89a52a3_0
  - tbb=2022.0.0=hdb19cb5_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - typing_extensions=4.4.0=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - cycler==0.11.0
      - cython==3.0.12
      - fonttools==4.38.0
      - kiwisolver==1.4.5
      - matplotlib==3.5.3
      - nose==1.3.7
      - pillow==9.5.0
      - pyparsing==3.1.4
      - python-dateutil==2.9.0.post0
      - six==1.17.0
prefix: /opt/conda/envs/PythonFMU

pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_generation_tool
pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_constructor[theModelName] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_constructor[None] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[False-Boolean] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[False-Integer] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[False-Real] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[False-String] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[22-Boolean] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[22-Integer] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[22-Real] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[22-String] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[0.6666666666666666-Boolean] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[0.6666666666666666-Integer] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[0.6666666666666666-Real] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[0.6666666666666666-String] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[hello_world-Boolean] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[hello_world-Integer] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[hello_world-Real] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_getters[hello_world-String] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[False-Boolean] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[False-Integer] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[False-Real] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[False-String] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[22-Boolean] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[22-Integer] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[22-Real] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[22-String] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[0.6666666666666666-Boolean] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[0.6666666666666666-Integer] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[0.6666666666666666-Real] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[0.6666666666666666-String] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[hello_world-Boolean] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[hello_world-Integer] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[hello_world-Real] pythonfmu/tests/test_fmi2slave.py::test_Fmi2Slave_setters[hello_world-String]
pythonfmu/builder.py pythonfmu/fmi2slave.py pythonfmu/enums.py pythonfmu/variables.py
pythonfmu/builder.py pythonfmu/fmi2slave.py pythonfmu/enums.py pythonfmu/variables.py