Repository: gammasim/simtools (language: python)

Issue:
Remove unused defaults_init option in layout_array?
The option `defaults_init` in

https://github.com/gammasim/gammasim-tools/blob/8cdb37a97cf9c4fcf37ed0380927c28e4735cb3a/simtools/layout/layout_array.py#L193

is not used. @GernotMaier, can I remove it or should I implement a usage for it?


Discussion / hints:

Fix patch:
diff --git a/applications/add_file_to_db.py b/applications/add_file_to_db.py
index f31d4fd71..f675df8d2 100644
--- a/applications/add_file_to_db.py
+++ b/applications/add_file_to_db.py
@@ -94,7 +94,7 @@ def main():
         help=(
             "A directory with files to upload to the DB. "
             "All files in the directory with the following extensions "
-            "will be uploaded: {}".format(", ".join(_db_tmp.ALLOWED_FILE_EXTENSIONS))
+            f"will be uploaded: {', '.join(_db_tmp.ALLOWED_FILE_EXTENSIONS)}"
         ),
         type=Path,
     )
@@ -125,12 +125,12 @@ def main():
                 files_to_insert.append(file_now)
             else:
                 logger.warning(
-                    "The file {} will not be uploaded to the DB because its extension is not "
-                    "in the allowed extension list: {}".format(file_now, db.ALLOWED_FILE_EXTENSIONS)
+                    f"The file {file_now} will not be uploaded to the DB because its extension "
+                    f"is not in the allowed extension list: {db.ALLOWED_FILE_EXTENSIONS}"
                 )
     else:
         for ext_now in db.ALLOWED_FILE_EXTENSIONS:
-            files_to_insert.extend(Path(args_dict["input_path"]).glob("*{}".format(ext_now)))
+            files_to_insert.extend(Path(args_dict["input_path"]).glob(f"*{ext_now}"))
 
     plural = "s"
     if len(files_to_insert) < 1:
@@ -146,9 +146,9 @@ def main():
     if _user_confirm():
         for file_to_insert_now in files_to_insert:
             db.insert_file_to_db(file_to_insert_now, args_dict["db"])
-            logger.info("File {} inserted to {} DB".format(file_to_insert_now, args_dict["db"]))
+            logger.info(f"File {file_to_insert_now} inserted to {args_dict['db']} DB")
     else:
-        logger.info("Aborted, did not insert file {} to the {} DB".format(plural, args_dict["db"]))
+        logger.info(f"Aborted, did not insert file {plural} to the {args_dict['db']} DB")
 
 
 if __name__ == "__main__":
diff --git a/applications/compare_cumulative_psf.py b/applications/compare_cumulative_psf.py
index b89086edb..32bced698 100644
--- a/applications/compare_cumulative_psf.py
+++ b/applications/compare_cumulative_psf.py
@@ -165,7 +165,7 @@ def main():
     # Plotting cumulative PSF
     im = ray.images()[0]
 
-    print("d80 in cm = {}".format(im.get_psf()))
+    print(f"d80 in cm = {im.get_psf()}")
 
     # Plotting cumulative PSF
     # Measured cumulative PSF
diff --git a/applications/db_development_tools/mark_non_optics_parameters_non_applicable.py b/applications/db_development_tools/mark_non_optics_parameters_non_applicable.py
index 7d1f19b9c..31b83abe2 100644
--- a/applications/db_development_tools/mark_non_optics_parameters_non_applicable.py
+++ b/applications/db_development_tools/mark_non_optics_parameters_non_applicable.py
@@ -80,7 +80,7 @@ def main():
             for par_now in non_optic_parameters:
                 db.update_parameter_field(
                     db_name=db.DB_CTA_SIMULATION_MODEL,
-                    telescope="{}-MST-Structure-D".format(site),
+                    telescope=f"{site}-MST-Structure-D",
                     version=version_now,
                     parameter=par_now,
                     field="Applicable",
@@ -88,7 +88,7 @@ def main():
                 )
             pars = db.read_mongo_db(
                 db_name=db.DB_CTA_SIMULATION_MODEL,
-                telescope_model_name_db="{}-MST-Structure-D".format(site),
+                telescope_model_name_db=f"{site}-MST-Structure-D",
                 model_version=version_now,
                 run_location="",
                 write_files=False,
diff --git a/applications/derive_mirror_rnda.py b/applications/derive_mirror_rnda.py
index a7bdfb913..4eaa040b7 100644
--- a/applications/derive_mirror_rnda.py
+++ b/applications/derive_mirror_rnda.py
@@ -250,21 +250,19 @@ def _print_and_write_results(
     containment_fraction_percent = int(args_dict["containment_fraction"] * 100)
 
     # Printing results to stdout
-    print("\nMeasured D{:}:".format(containment_fraction_percent))
+    print(f"\nMeasured D{containment_fraction_percent}:")
     if args_dict["psf_measurement_containment_sigma"] is not None:
         print(
-            "Mean = {:.3f} cm, StdDev = {:.3f} cm".format(
-                args_dict["psf_measurement_containment_mean"],
-                args_dict["psf_measurement_containment_sigma"],
-            )
+            f"Mean = {args_dict['psf_measurement_containment_mean']:.3f} cm, "
+            f"StdDev = {args_dict['psf_measurement_containment_sigma']:.3f} cm"
         )
     else:
-        print("Mean = {:.3f} cm".format(args_dict["psf_measurement_containment_mean"]))
-    print("\nSimulated D{:}:".format(containment_fraction_percent))
-    print("Mean = {:.3f} cm, StdDev = {:.3f} cm".format(mean_d80, sig_d80))
+        print(f"Mean = {args_dict['psf_measurement_containment_mean']:.3f} cm")
+    print(f"\nSimulated D{containment_fraction_percent}:")
+    print(f"Mean = {mean_d80:.3f} cm, StdDev = {sig_d80:.3f} cm")
     print("\nmirror_random_reflection_angle")
-    print("Previous value = {:.6f}".format(rnda_start))
-    print("New value = {:.6f}\n".format(rnda_opt))
+    print(f"Previous value = {rnda_start:.6f}")
+    print(f"New value = {rnda_opt:.6f}\n")
 
     # Result table written to ecsv file using file_writer
     # First entry is always the best fit result
@@ -308,17 +306,13 @@ def _get_psf_containment(logger, args_dict):
         )
     except KeyError:
         logger.debug(
-            "Missing column for psf measurement (psf_opt) in {}".format(
-                args_dict["psf_measurement"]
-            )
+            f"Missing column for psf measurement (psf_opt) in {args_dict['psf_measurement']}"
         )
         raise
 
     logger.info(
-        "Determined PSF containment to {:.4} +- {:.4} cm".format(
-            args_dict["psf_measurement_containment_mean"],
-            args_dict["psf_measurement_containment_sigma"],
-        )
+        f"Determined PSF containment to {args_dict['psf_measurement_containment_mean']:.4} "
+        f"+- {args_dict['psf_measurement_containment_sigma']:.4} cm"
     )
 
 
@@ -365,7 +359,7 @@ def run(rnda):
         if isinstance(rnda_start, str):
             rnda_start = float(rnda_start.split()[0])
 
-    logger.info("Start value for mirror_reflection_random_angle: {:}".format(rnda_start))
+    logger.info(f"Start value for mirror_reflection_random_angle: {rnda_start}")
 
     results_rnda = list()
     results_mean = list()
diff --git a/applications/get_file_from_db.py b/applications/get_file_from_db.py
index c339e94a9..4b492e153 100644
--- a/applications/get_file_from_db.py
+++ b/applications/get_file_from_db.py
@@ -81,9 +81,8 @@ def main():
                     db_name, args_dict["output_path"], args_dict["file_name"]
                 )
                 logger.info(
-                    "Got file {} from DB {} and saved into {}".format(
-                        args_dict["file_name"], db_name, args_dict["output_path"]
-                    )
+                    f"Got file {args_dict['file_name']} from DB {db_name} "
+                    f"and saved into {args_dict['output_path']}"
                 )
                 break
             except FileNotFoundError:
@@ -91,13 +90,11 @@ def main():
 
         if file_id is None:
             logger.error(
-                "The file {} was not found in any of the available DBs.".format(
-                    args_dict["file_name"]
-                )
+                f"The file {args_dict['file_name']} was not found in any of the available DBs."
             )
             raise FileNotFoundError
     else:
-        logger.error("Aborted, directory {} does not exist".format(args_dict["output_path"]))
+        logger.error(f"Aborted, directory {args_dict['output_path']} does not exist")
 
 
 if __name__ == "__main__":
diff --git a/applications/make_regular_arrays.py b/applications/make_regular_arrays.py
index 58bfa4791..c44083aa1 100644
--- a/applications/make_regular_arrays.py
+++ b/applications/make_regular_arrays.py
@@ -105,7 +105,7 @@ def main():
 
     for site in ["South", "North"]:
         for array_name in ["1SST", "4SST", "1MST", "4MST", "1LST", "4LST"]:
-            logger.info("Processing array {}".format(array_name))
+            logger.info(f"Processing array {array_name}")
             layout = LayoutArray(
                 label=label,
                 name=site + "-" + array_name,
@@ -113,13 +113,12 @@ def main():
                 corsika_telescope_data=corsika_telescope_data[site],
             )
 
-            tel_name_root = array_name[1]
             tel_size = array_name[1:4]
 
             # Single telescope at the center
             if array_name[0] == "1":
                 layout.add_telescope(
-                    telescope_name=tel_name_root + "-01",
+                    telescope_name=tel_size + "-01",
                     crs_name="corsika",
                     xx=0 * u.m,
                     yy=0 * u.m,
@@ -128,28 +127,28 @@ def main():
             # 4 telescopes in a regular square grid
             else:
                 layout.add_telescope(
-                    telescope_name=tel_name_root + "-01",
+                    telescope_name=tel_size + "-01",
                     crs_name="corsika",
                     xx=telescope_distance[tel_size],
                     yy=telescope_distance[tel_size],
                     tel_corsika_z=0 * u.m,
                 )
                 layout.add_telescope(
-                    telescope_name=tel_name_root + "-02",
+                    telescope_name=tel_size + "-02",
                     crs_name="corsika",
                     xx=-telescope_distance[tel_size],
                     yy=telescope_distance[tel_size],
                     tel_corsika_z=0 * u.m,
                 )
                 layout.add_telescope(
-                    telescope_name=tel_name_root + "-03",
+                    telescope_name=tel_size + "-03",
                     crs_name="corsika",
                     xx=telescope_distance[tel_size],
                     yy=-telescope_distance[tel_size],
                     tel_corsika_z=0 * u.m,
                 )
                 layout.add_telescope(
-                    telescope_name=tel_name_root + "-04",
+                    telescope_name=tel_size + "-04",
                     crs_name="corsika",
                     xx=-telescope_distance[tel_size],
                     yy=-telescope_distance[tel_size],
diff --git a/applications/plot_simtel_histograms.py b/applications/plot_simtel_histograms.py
index 1009b502e..862253d2f 100644
--- a/applications/plot_simtel_histograms.py
+++ b/applications/plot_simtel_histograms.py
@@ -99,7 +99,7 @@ def main():
 
         title = simtel_histograms[0].get_histogram_title(i_hist)
 
-        logger.debug("Processing: {}".format(title))
+        logger.debug(f"Processing: {title}")
 
         fig, axs = plt.subplots(1, n_lists, figsize=(6 * n_lists, 6))
 
diff --git a/applications/production.py b/applications/production.py
index 7b1aa7466..7f651c6e6 100644
--- a/applications/production.py
+++ b/applications/production.py
@@ -170,7 +170,7 @@ def _proccess_simulation_config_file(config_file, primary_config, logger):
         with open(config_file) as file:
             config_data = yaml.load(file)
     except FileNotFoundError:
-        logger.error("Error loading simulation configuration file from {}".format(config_file))
+        logger.error(f"Error loading simulation configuration file from {config_file}")
         raise
 
     label = config_data.pop("label", dict())
diff --git a/applications/sim_showers_for_trigger_rates.py b/applications/sim_showers_for_trigger_rates.py
index bd8ba6283..942c99c0f 100644
--- a/applications/sim_showers_for_trigger_rates.py
+++ b/applications/sim_showers_for_trigger_rates.py
@@ -173,17 +173,17 @@ def main():
         shower_simulator.simulate()
 
     # Exporting the list of output/log/input files into the application folder
-    output_file_list = output_dir.joinpath("output_files_{}.list".format(args_dict["primary"]))
-    log_file_list = output_dir.joinpath("log_files_{}.list".format(args_dict["primary"]))
+    output_file_list = output_dir.joinpath(f"output_files_{args_dict['primary']}.list")
+    log_file_list = output_dir.joinpath(f"log_files_{args_dict['primary']}.list")
 
     def print_list_into_file(list_of_files, file_name):
         with open(file_name, "w") as f:
             for line in list_of_files:
                 f.write(line + "\n")
 
-    logger.info("List of output files exported to {}".format(output_file_list))
+    logger.info(f"List of output files exported to {output_file_list}")
     print_list_into_file(shower_simulator.get_list_of_output_files(), output_file_list)
-    logger.info("List of log files exported to {}".format(log_file_list))
+    logger.info(f"List of log files exported to {log_file_list}")
     print_list_into_file(shower_simulator.get_list_of_log_files(), log_file_list)
 
 
diff --git a/applications/tune_psf.py b/applications/tune_psf.py
index 7401679ff..c6edc1155 100644
--- a/applications/tune_psf.py
+++ b/applications/tune_psf.py
@@ -180,11 +180,9 @@ def add_parameters(
         parameters to the all_parameters list.
         """
         pars = dict()
-        mrra = "{:.4f},{:.2f},{:.4f}".format(
-            mirror_reflection, mirror_reflection_fraction, mirror_reflection_2
-        )
+        mrra = f"{mirror_reflection:.4f},{mirror_reflection_fraction:.2f},{mirror_reflection_2:.4f}"
         pars["mirror_reflection_random_angle"] = mrra
-        mar = "{:.4f},28.,0.,0.".format(mirror_align)
+        mar = f"{mirror_align:.4f},28.,0.,0."
         pars["mirror_align_random_horizontal"] = mar
         pars["mirror_align_random_vertical"] = mar
         all_parameters.append(pars)
@@ -288,15 +286,14 @@ def run_pars(pars, plot=True):
             ax = fig.get_axes()[0]
             ax.set_ylim(0, 1.05)
             ax.set_title(
-                "refl_rnd={}, align_rnd={}".format(
-                    pars["mirror_reflection_random_angle"], pars["mirror_align_random_vertical"]
-                )
+                f"refl_rnd={pars['mirror_reflection_random_angle']}, "
+                f"align_rnd={pars['mirror_align_random_vertical']}"
             )
 
             ax.text(
                 0.8,
                 0.3,
-                "D80 = {:.3f} cm\nRMSD = {:.4f}".format(d80, rmsd),
+                f"D80 = {d80:.3f} cm\nRMSD = {rmsd:.4f}",
                 verticalalignment="center",
                 horizontalalignment="center",
                 transform=ax.transAxes,
@@ -325,7 +322,7 @@ def run_pars(pars, plot=True):
     # Printing the results
     print("Best parameters:")
     for par, value in best_pars.items():
-        print("{} = {}".format(par, value))
+        print(f"{par} = {value}")
 
 
 if __name__ == "__main__":
diff --git a/applications/validate_camera_efficiency.py b/applications/validate_camera_efficiency.py
index 1cc98a7de..6e834f0c7 100644
--- a/applications/validate_camera_efficiency.py
+++ b/applications/validate_camera_efficiency.py
@@ -101,7 +101,7 @@ def main():
     # For debugging purposes
     tel_model.export_config_file()
 
-    logger.info("Validating the camera efficiency of {}".format(tel_model.name))
+    logger.info(f"Validating the camera efficiency of {tel_model.name}")
 
     ce = CameraEfficiency(
         telescope_model=tel_model,
@@ -116,7 +116,7 @@ def main():
     cherenkov_plot_file = output_dir.joinpath(cherenkov_plot_file_name)
     for f in ["pdf", "png"]:
         fig.savefig(str(cherenkov_plot_file) + "." + f, format=f, bbox_inches="tight")
-    logger.info("Plotted cherenkov efficiency in {}".format(cherenkov_plot_file))
+    logger.info(f"Plotted cherenkov efficiency in {cherenkov_plot_file}")
     fig.clf()
 
     # Plotting the camera efficiency for NSB light
@@ -125,7 +125,7 @@ def main():
     nsb_plot_file = output_dir.joinpath(nsb_plot_file_name)
     for f in ["pdf", "png"]:
         fig.savefig(str(nsb_plot_file) + "." + f, format=f, bbox_inches="tight")
-    logger.info("Plotted NSB efficiency in {}".format(nsb_plot_file))
+    logger.info(f"Plotted NSB efficiency in {nsb_plot_file}")
     fig.clf()
 
 
diff --git a/applications/validate_camera_fov.py b/applications/validate_camera_fov.py
index fecb9a171..306982a09 100644
--- a/applications/validate_camera_fov.py
+++ b/applications/validate_camera_fov.py
@@ -111,16 +111,16 @@ def main():
     )
     tel_model.export_model_files()
 
-    print("\nValidating the camera FoV of {}\n".format(tel_model.name))
+    print(f"\nValidating the camera FoV of {tel_model.name}\n")
 
     focal_length = float(tel_model.get_parameter_value("effective_focal_length"))
     camera = tel_model.camera
 
     fov, r_edge_avg = camera.calc_fov()
 
-    print("\nEffective focal length = " + "{0:.3f} cm".format(focal_length))
-    print("{0} FoV = {1:.3f} deg".format(tel_model.name, fov))
-    print("Avg. edge radius = {0:.3f} cm\n".format(r_edge_avg))
+    print("\nEffective focal length = " + f"{focal_length:.3f} cm")
+    print(f"{tel_model.name} FoV = {fov:.3f} deg")
+    print(f"Avg. edge radius = {r_edge_avg:.3f} cm\n")
 
     # Now plot the camera as well
     try:
@@ -140,7 +140,7 @@ def main():
     for suffix in ["pdf", "png"]:
         file_name = f"{str(plot_file_prefix)}.{suffix}"
         fig.savefig(file_name, format=suffix, bbox_inches="tight")
-        print("\nSaved camera plot in {}\n".format(file_name))
+        print(f"\nSaved camera plot in {file_name}\n")
     fig.clf()
 
 
diff --git a/applications/validate_optics.py b/applications/validate_optics.py
index 088d5441b..25efdcfe4 100644
--- a/applications/validate_optics.py
+++ b/applications/validate_optics.py
@@ -156,10 +156,7 @@ def main():
     # }
     # tel_model.change_multiple_parameters(**pars_to_change)
 
-    print(
-        "\nValidating telescope optics with ray tracing simulations"
-        " for {}\n".format(tel_model.name)
-    )
+    print(f"\nValidating telescope optics with ray tracing simulations for {tel_model.name}\n")
 
     ray = RayTracing.from_kwargs(
         telescope_model=tel_model,
@@ -192,7 +189,7 @@ def main():
         plot_file = output_dir.joinpath(plot_file_name)
         pdf_pages = PdfPages(plot_file)
 
-        logger.info("Plotting images into {}".format(plot_file))
+        logger.info(f"Plotting images into {plot_file}")
 
         for image in ray.images():
             fig = plt.figure(figsize=(8, 6), tight_layout=True)
diff --git a/docs/source/conf.py b/docs/source/conf.py
index b4fd1b67f..1909dae3f 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -50,11 +50,9 @@ def get_authors_from_citation_file(file_name):
 project = "gammasim-tools"
 copyright = "2022, gammasim-tools developers"
 author = get_authors_from_citation_file("../CITATION.cff")
-rst_epilog = """
+rst_epilog = f"""
 .. |author| replace:: {author}
-""".format(
-    author=author
-)
+"""
 
 # The short X.Y version
 version = str(simtools.version.__version__)
diff --git a/simtools/camera_efficiency.py b/simtools/camera_efficiency.py
index d604abf04..a71debf14 100644
--- a/simtools/camera_efficiency.py
+++ b/simtools/camera_efficiency.py
@@ -64,6 +64,7 @@ def __init__(
             test=test,
         )
 
+        self._results = None
         self._has_results = False
 
         _config_data_in = gen.collect_data_from_yaml_or_dict(
@@ -105,7 +106,7 @@ def from_kwargs(cls, **kwargs):
         return cls(**args, config_data=config_data)
 
     def __repr__(self):
-        return "CameraEfficiency(label={})\n".format(self.label)
+        return f"CameraEfficiency(label={self.label})\n"
 
     def _validate_telescope_model(self, tel):
         """Validate TelescopeModel
@@ -123,10 +124,10 @@ def _validate_telescope_model(self, tel):
         if isinstance(tel, TelescopeModel):
             self._logger.debug("TelescopeModel OK")
             return tel
-        else:
-            msg = "Invalid TelescopeModel"
-            self._logger.error(msg)
-            raise ValueError(msg)
+
+        msg = "Invalid TelescopeModel"
+        self._logger.error(msg)
+        raise ValueError(msg)
 
     def _load_files(self):
         """Define the variables for the file names, including the results, simtel and log file."""
@@ -176,8 +177,6 @@ def simulate(self, force=False):
         )
         simtel.run(test=self.test, force=force)
 
-        return
-
     def analyze(self, export=True, force=False):
         """
         Analyze camera efficiency output file and store the results in _results.
@@ -294,7 +293,7 @@ def export_results(self):
         if not self._has_results:
             self._logger.error("Cannot export results because they do not exist")
         else:
-            self._logger.info("Exporting results to {}".format(self._file_results))
+            self._logger.info(f"Exporting results to {self._file_results}")
             astropy.io.ascii.write(
                 self._results, self._file_results, format="basic", overwrite=True
             )
@@ -316,9 +315,7 @@ def calc_tel_efficiency(self):
         """
 
         # Sum(C1) from 300 - 550 nm:
-        c1_reduced_wl = self._results["C1"][
-            [wl_now > 299 and wl_now < 551 for wl_now in self._results["wl"]]
-        ]
+        c1_reduced_wl = self._results["C1"][[299 < wl_now < 551 for wl_now in self._results["wl"]]]
         c1_sum = np.sum(c1_reduced_wl)
         # Sum(C4) from 200 - 999 nm:
         c4_sum = np.sum(self._results["C4"])
@@ -340,13 +337,11 @@ def calc_camera_efficiency(self):
         """
 
         # Sum(C1) from 300 - 550 nm:
-        c1_reduced_wl = self._results["C1"][
-            [wl_now > 299 and wl_now < 551 for wl_now in self._results["wl"]]
-        ]
+        c1_reduced_wl = self._results["C1"][[299 < wl_now < 551 for wl_now in self._results["wl"]]]
         c1_sum = np.sum(c1_reduced_wl)
         # Sum(C4x) from 300 - 550 nm:
         c4x_reduced_wl = self._results["C4x"][
-            [wl_now > 299 and wl_now < 551 for wl_now in self._results["wl"]]
+            [299 < wl_now < 551 for wl_now in self._results["wl"]]
         ]
         c4x_sum = np.sum(c4x_reduced_wl)
         fill_factor = self._telescope_model.camera.get_camera_fill_factor()
@@ -372,9 +367,7 @@ def calc_tot_efficiency(self, tel_efficiency):
         """
 
         # Sum(N1) from 300 - 550 nm:
-        n1_reduced_wl = self._results["N1"][
-            [wl_now > 299 and wl_now < 551 for wl_now in self._results["wl"]]
-        ]
+        n1_reduced_wl = self._results["N1"][[299 < wl_now < 551 for wl_now in self._results["wl"]]]
         n1_sum = np.sum(n1_reduced_wl)
         # Sum(N4) from 200 - 999 nm:
         n4_sum = np.sum(self._results["N4"])
@@ -396,14 +389,10 @@ def calc_reflectivity(self):
         """
 
         # Sum(C1) from 300 - 550 nm:
-        c1_reduced_wl = self._results["C1"][
-            [wl_now > 299 and wl_now < 551 for wl_now in self._results["wl"]]
-        ]
+        c1_reduced_wl = self._results["C1"][[299 < wl_now < 551 for wl_now in self._results["wl"]]]
         c1_sum = np.sum(c1_reduced_wl)
         # Sum(C2) from 300 - 550 nm:
-        c2_reduced_wl = self._results["C2"][
-            [wl_now > 299 and wl_now < 551 for wl_now in self._results["wl"]]
-        ]
+        c2_reduced_wl = self._results["C2"][[299 < wl_now < 551 for wl_now in self._results["wl"]]]
         c2_sum = np.sum(c2_reduced_wl)
         cher_spec_weighted_reflectivity = c2_sum / c1_sum / self._results["masts"][0]
 
@@ -430,12 +419,10 @@ def calc_nsb_rate(self):
 
         # NSB input spectrum is from Benn&Ellison
         # (integral is in ph./(cm² ns sr) ) from 300 - 650 nm:
-        n1_reduced_wl = self._results["N1"][
-            [wl_now > 299 and wl_now < 651 for wl_now in self._results["wl"]]
-        ]
+        n1_reduced_wl = self._results["N1"][[299 < wl_now < 651 for wl_now in self._results["wl"]]]
         n1_sum = np.sum(n1_reduced_wl)
         n1_integral_edges = self._results["N1"][
-            [wl_now == 300 or wl_now == 650 for wl_now in self._results["wl"]]
+            [wl_now in [300, 650] for wl_now in self._results["wl"]]
         ]
         n1_integral_edges_sum = np.sum(n1_integral_edges)
         nsb_integral = 0.0001 * (n1_sum - 0.5 * n1_integral_edges_sum)
@@ -505,7 +492,7 @@ def plot_cherenkov_efficiency(self):
         fig = visualize.plot_table(
             table_to_plot,
             y_title="Cherenkov light efficiency",
-            title="{} response to Cherenkov light".format(self._telescope_model.name),
+            title=f"{self._telescope_model.name} response to Cherenkov light",
             no_markers=True,
         )
 
@@ -535,15 +522,15 @@ def plot_nsb_efficiency(self):
         for column_now, column_title in column_titles.items():
             table_to_plot.rename_column(column_now, column_title)
 
-        plt = visualize.plot_table(
+        plot = visualize.plot_table(
             table_to_plot,
             y_title="Nightsky background light efficiency",
-            title="{} response to nightsky background light".format(self._telescope_model.name),
+            title=f"{self._telescope_model.name} response to nightsky background light",
             no_markers=True,
         )
 
-        plt.gca().set_yscale("log")
-        ylim = plt.gca().get_ylim()
-        plt.gca().set_ylim(1e-3, ylim[1])
+        plot.gca().set_yscale("log")
+        ylim = plot.gca().get_ylim()
+        plot.gca().set_ylim(1e-3, ylim[1])
 
-        return plt
+        return plot
diff --git a/simtools/configuration/commandline_parser.py b/simtools/configuration/commandline_parser.py
index 9bcbc5f25..de3196926 100644
--- a/simtools/configuration/commandline_parser.py
+++ b/simtools/configuration/commandline_parser.py
@@ -1,8 +1,8 @@
 import argparse
 from pathlib import Path
 
-import simtools.util.names as names
 import simtools.version
+from simtools.util import names
 
 __all__ = [
     "CommandLineParser",
@@ -240,7 +240,7 @@ def site(value):
 
         fsite = str(value)
         if not names.validate_site_name(fsite):
-            raise argparse.ArgumentTypeError("{} is an invalid site".format(fsite))
+            raise argparse.ArgumentTypeError(f"{fsite} is an invalid site")
         return fsite
 
     @staticmethod
@@ -262,7 +262,7 @@ def telescope(value):
 
         ftelescope = str(value)
         if not names.validate_telescope_model_name(ftelescope):
-            raise argparse.ArgumentTypeError("{} is an invalid telescope name".format(ftelescope))
+            raise argparse.ArgumentTypeError(f"{ftelescope} is an invalid telescope name")
         return ftelescope
 
     @staticmethod
@@ -285,6 +285,6 @@ def efficiency_interval(value):
         """
         fvalue = float(value)
         if fvalue < 0.0 or fvalue > 1.0:
-            raise argparse.ArgumentTypeError("{} outside of allowed [0,1] interval".format(value))
+            raise argparse.ArgumentTypeError(f"{value} outside of allowed [0,1] interval")
 
         return fvalue
diff --git a/simtools/configuration/configurator.py b/simtools/configuration/configurator.py
index 396b2bfea..687473b85 100644
--- a/simtools/configuration/configurator.py
+++ b/simtools/configuration/configurator.py
@@ -16,8 +16,6 @@
 class InvalidConfigurationParameter(Exception):
     """Exception for Invalid configuration parameter."""
 
-    pass
-
 
 class Configurator:
     """
@@ -217,9 +215,8 @@ def _check_parameter_configuration_status(self, key, value):
         # parameter already set
         if key in self.config and self.config[key] != value:
             self._logger.error(
-                "Inconsistent configuration parameter ({}) definition ({} vs {})".format(
-                    key, self.config[key], value
-                )
+                f"Inconsistent configuration parameter ({key}) definition "
+                f"({self.config[key]} vs {value})"
             )
             raise InvalidConfigurationParameter
 
@@ -243,23 +240,21 @@ def _fill_from_config_file(self, config_file):
         """
 
         try:
-            self._logger.debug("Reading configuration from {}".format(config_file))
+            self._logger.debug(f"Reading configuration from {config_file}")
             with open(config_file, "r") as stream:
                 _config_dict = yaml.safe_load(stream)
             if "CTASIMPIPE" in _config_dict:
                 try:
                     self._fill_from_config_dict(_config_dict["CTASIMPIPE"]["CONFIGURATION"])
                 except KeyError:
-                    self._logger.info(
-                        "No CTASIMPIPE:CONFIGURATION dict found in {}.".format(config_file)
-                    )
+                    self._logger.info(f"No CTASIMPIPE:CONFIGURATION dict found in {config_file}.")
             else:
                 self._fill_from_config_dict(_config_dict)
         # TypeError is raised for config_file=None
         except TypeError:
             pass
         except FileNotFoundError:
-            self._logger.error("Configuration file not found: {}".format(config_file))
+            self._logger.error(f"Configuration file not found: {config_file}")
             raise
 
     def _fill_from_environmental_variables(self):
diff --git a/simtools/corsika/corsika_config.py b/simtools/corsika/corsika_config.py
index bf102b0f0..51769b51e 100644
--- a/simtools/corsika/corsika_config.py
+++ b/simtools/corsika/corsika_config.py
@@ -21,14 +21,10 @@
 class MissingRequiredInputInCorsikaConfigData(Exception):
     """Exception for missing required input in corsika config data."""
 
-    pass
-
 
 class InvalidCorsikaInput(Exception):
     """Exception for invalid corsika input."""
 
-    pass
-
 
 class CorsikaConfig:
     """
@@ -85,6 +81,9 @@ def __init__(
 
         self.label = label
         self.site = names.validate_site_name(site)
+        self.primary = None
+        self._config_file_path = None
+        self._output_generic_file_name = None
 
         self.io_handler = io_handler.IOHandler()
 
@@ -104,8 +103,9 @@ def __init__(
         self._is_file_updated = False
 
     def __repr__(self):
-        text = "<class {}> (site={}, layout={}, label={})".format(
-            self.__class__.__name__, self.site, self.layout_name, self.label
+        text = (
+            f"<class {self.__class__.__name__}> "
+            f"(site={self.site}, layout={self.layout_name}, label={self.label})"
         )
         return text
 
@@ -123,9 +123,7 @@ def _load_corsika_parameters_file(self, filename):
             self._corsika_parameters_file = self.io_handler.get_input_data_file(
                 "parameters", "corsika_parameters.yml"
             )
-        self._logger.debug(
-            "Loading CORSIKA parameters from file {}".format(self._corsika_parameters_file)
-        )
+        self._logger.debug(f"Loading CORSIKA parameters from file {self._corsika_parameters_file}")
         with open(self._corsika_parameters_file, "r") as f:
             self._corsika_parameters = yaml.load(f)
 
@@ -182,7 +180,7 @@ def set_user_parameters(self, corsika_config_data):
 
             # Raising error for an unidentified input.
             if not is_identified:
-                msg = "Argument {} cannot be identified.".format(key_args)
+                msg = f"Argument {key_args} cannot be identified."
                 self._logger.error(msg)
                 raise InvalidCorsikaInput(msg)
 
@@ -191,13 +189,13 @@ def set_user_parameters(self, corsika_config_data):
         for par_name, par_info in user_pars.items():
             if par_name in self._user_parameters:
                 continue
-            elif "default" in par_info.keys():
+            if "default" in par_info.keys():
                 validated_value = self._validate_and_convert_argument(
                     par_name, par_info, par_info["default"]
                 )
                 self._user_parameters[par_name] = validated_value
             else:
-                msg = "Required parameters {} was not given (there may be more).".format(par_name)
+                msg = f"Required parameters {par_name} was not given (there may be more)."
                 self._logger.error(msg)
                 raise MissingRequiredInputInCorsikaConfigData(msg)
 
@@ -230,38 +228,38 @@ def _validate_and_convert_argument(self, par_name, par_info, value_args_in):
             value_args = self._convert_primary_input_and_store_primary_name(value_args)
 
         if len(value_args) != par_info["len"]:
-            msg = "CORSIKA input entry with wrong len: {}".format(par_name)
+            msg = f"CORSIKA input entry with wrong len: {par_name}"
             self._logger.error(msg)
             raise InvalidCorsikaInput(msg)
 
         if "unit" not in par_info.keys():
             return value_args
-        else:
-            # Turning par_info['unit'] into a list, if it is not.
-            par_unit = gen.copy_as_list(par_info["unit"])
-
-            # Checking units and converting them, if needed.
-            value_args_with_units = list()
-            for arg, unit in zip(value_args, par_unit):
-                if unit is None:
-                    value_args_with_units.append(arg)
-                    continue
 
-                if isinstance(arg, str):
-                    arg = u.quantity.Quantity(arg)
+        # Turning par_info['unit'] into a list, if it is not.
+        par_unit = gen.copy_as_list(par_info["unit"])
+
+        # Checking units and converting them, if needed.
+        value_args_with_units = list()
+        for arg, unit in zip(value_args, par_unit):
+            if unit is None:
+                value_args_with_units.append(arg)
+                continue
+
+            if isinstance(arg, str):
+                arg = u.quantity.Quantity(arg)
 
-                if not isinstance(arg, u.quantity.Quantity):
-                    msg = "CORSIKA input given without unit: {}".format(par_name)
-                    self._logger.error(msg)
-                    raise InvalidCorsikaInput(msg)
-                elif not arg.unit.is_equivalent(unit):
-                    msg = "CORSIKA input given with wrong unit: {}".format(par_name)
-                    self._logger.error(msg)
-                    raise InvalidCorsikaInput(msg)
-                else:
-                    value_args_with_units.append(arg.to(unit).value)
+            if not isinstance(arg, u.quantity.Quantity):
+                msg = f"CORSIKA input given without unit: {par_name}"
+                self._logger.error(msg)
+                raise InvalidCorsikaInput(msg)
+            if not arg.unit.is_equivalent(unit):
+                msg = f"CORSIKA input given with wrong unit: {par_name}"
+                self._logger.error(msg)
+                raise InvalidCorsikaInput(msg)
+
+            value_args_with_units.append(arg.to(unit).value)
 
-            return value_args_with_units
+        return value_args_with_units
 
     def _convert_primary_input_and_store_primary_name(self, value):
         """
@@ -287,7 +285,7 @@ def _convert_primary_input_and_store_primary_name(self, value):
             if value[0].upper() == prim_name or value[0].upper() in prim_info["names"]:
                 self.primary = prim_name.lower()
                 return [prim_info["number"]]
-        msg = "Primary not valid: {}".format(value)
+        msg = f"Primary not valid: {value}"
         self._logger.error(msg)
         raise InvalidCorsikaInput(msg)
 
@@ -313,7 +311,7 @@ def get_user_parameter(self, par_name):
         try:
             par_value = self._user_parameters[par_name.upper()]
         except KeyError:
-            self._logger.warning("Parameter {} is not a user parameter".format(par_name))
+            self._logger.warning(f"Parameter {par_name} is not a user parameter")
             raise
 
         return par_value if len(par_value) > 1 else par_value[0]
@@ -321,12 +319,12 @@ def get_user_parameter(self, par_name):
     def print_user_parameters(self):
         """Print user parameters for inspection."""
         for par, value in self._user_parameters.items():
-            print("{} = {}".format(par, value))
+            print(f"{par} = {value}")
 
     def export_input_file(self):
         """Create and export CORSIKA input file."""
         self._set_output_file_and_directory()
-        self._logger.debug("Exporting CORSIKA input file to {}".format(self._config_file_path))
+        self._logger.debug(f"Exporting CORSIKA input file to {self._config_file_path}")
 
         def _get_text_single_line(pars):
             text = ""
@@ -362,9 +360,9 @@ def _get_text_multiple_lines(pars):
 
             # Defining the IACT variables for the output file name
             file.write("\n")
-            file.write("IACT setenv PRMNAME {}\n".format(self.primary))
-            file.write("IACT setenv ZA {}\n".format(int(self._user_parameters["THETAP"][0])))
-            file.write("IACT setenv AZM {}\n".format(int(self._user_parameters["AZM"][0])))
+            file.write(f"IACT setenv PRMNAME {self.primary}\n")
+            file.write(f"IACT setenv ZA {int(self._user_parameters['THETAP'][0])}\n")
+            file.write(f"IACT setenv AZM {int(self._user_parameters['AZM'][0])}\n")
 
             file.write("\n* [ SEEDS ]\n")
             self._write_seeds(file)
@@ -392,7 +390,7 @@ def _get_text_multiple_lines(pars):
             file.write(text_debugging)
 
             file.write("\n* [ OUTUPUT FILE ]\n")
-            file.write("TELFIL {}\n".format(self._output_generic_file_name))
+            file.write(f"TELFIL {self._output_generic_file_name}\n")
 
             file.write("\n* [ IACT TUNING PARAMETERS ]\n")
             text_iact = _get_text_multiple_lines(self._corsika_parameters["IACT_TUNING_PARAMETERS"])
@@ -448,8 +446,7 @@ def get_file_name(self, file_type, run_number=None):
         if file_type == "config_tmp":
             if run_number is not None:
                 return f"corsika_config_run{run_number}_{file_name}.txt"
-            else:
-                raise ValueError("Must provide a run number for a temporary CORSIKA config file")
+            raise ValueError("Must provide a run number for a temporary CORSIKA config file")
         if file_type == "config":
             return f"corsika_config_{file_name}.input"
         if file_type == "output_generic":
@@ -465,7 +462,7 @@ def _set_output_file_and_directory(self):
         config_file_name = self.get_file_name(file_type="config")
         file_directory = self.io_handler.get_output_directory(label=self.label, dir_type="corsika")
         file_directory.mkdir(parents=True, exist_ok=True)
-        self._logger.info("Creating directory {}, if needed.".format(file_directory))
+        self._logger.info(f"Creating directory {file_directory}, if needed.")
         self._config_file_path = file_directory.joinpath(config_file_name)
 
         self._output_generic_file_name = self.get_file_name(file_type="output_generic")
@@ -484,7 +481,7 @@ def _write_seeds(self, file):
         corsika_seeds = [int(rng.uniform(0, 1e7)) for i in range(4)]
 
         for s in corsika_seeds:
-            file.write("SEED {} 0 0\n".format(s))
+            file.write(f"SEED {s} 0 0\n")
 
     def get_input_file(self):
         """
diff --git a/simtools/corsika/corsika_runner.py b/simtools/corsika/corsika_runner.py
index 4d84a2b39..3e3cd6a7a 100644
--- a/simtools/corsika/corsika_runner.py
+++ b/simtools/corsika/corsika_runner.py
@@ -17,8 +17,6 @@
 class MissingRequiredEntryInCorsikaConfig(Exception):
     """Exception for missing required entry in corsika config."""
 
-    pass
-
 
 class CorsikaRunner:
     """
@@ -101,7 +99,7 @@ def __init__(
         self._simtel_source_path = simtel_source_path
         self.io_handler = io_handler.IOHandler()
         self._output_directory = self.io_handler.get_output_directory(self.label, "corsika")
-        self._logger.debug("Creating output dir {}, if needed,".format(self._output_directory))
+        self._logger.debug(f"Creating output dir {self._output_directory}, if needed,")
 
         corsika_config_data = collect_data_from_yaml_or_dict(
             corsika_config_file, corsika_config_data
@@ -195,11 +193,11 @@ def get_run_script(self, **kwargs):
         )
         corsika_input_tmp_file = self._corsika_input_dir.joinpath(corsika_input_tmp_name)
 
-        pfp_command = self._get_pfp_command(run_number, corsika_input_tmp_file)
+        pfp_command = self._get_pfp_command(corsika_input_tmp_file)
         autoinputs_command = self._get_autoinputs_command(run_number, corsika_input_tmp_file)
 
         extra_commands = kwargs["extra_commands"]
-        self._logger.debug("Extra commands to be added to the run script {}".format(extra_commands))
+        self._logger.debug(f"Extra commands to be added to the run script {extra_commands}")
 
         with open(script_file_path, "w") as file:
             # shebang
@@ -210,13 +208,13 @@ def get_run_script(self, **kwargs):
 
             if extra_commands is not None:
                 file.write("\n# Writing extras\n")
-                file.write("{}\n".format(extra_commands))
+                file.write(f"{extra_commands}\n")
                 file.write("# End of extras\n\n")
 
-            file.write("export CORSIKA_DATA={}\n".format(self._corsika_data_dir))
+            file.write(f"export CORSIKA_DATA={self._corsika_data_dir}\n")
             file.write("\n# Creating CORSIKA_DATA\n")
-            file.write("mkdir -p {}\n".format(self._corsika_data_dir))
-            file.write("cd {} || exit 2\n".format(self._corsika_data_dir))
+            file.write(f"mkdir -p {self._corsika_data_dir}\n")
+            file.write(f"cd {self._corsika_data_dir} || exit 2\n")
             file.write("\n# Running pfp\n")
             file.write(pfp_command)
             file.write("\n# Running corsika_autoinputs\n")
@@ -226,15 +224,15 @@ def get_run_script(self, **kwargs):
             file.write('\necho "RUNTIME: $SECONDS"\n')
 
         # Changing permissions
-        os.system("chmod ug+x {}".format(script_file_path))
+        os.system(f"chmod ug+x {script_file_path}")
 
         return script_file_path
 
-    def _get_pfp_command(self, run_number, input_tmp_file):
+    def _get_pfp_command(self, input_tmp_file):
         """Get pfp pre-processor command."""
         cmd = self._simtel_source_path.joinpath("sim_telarray/bin/pfp")
-        cmd = str(cmd) + " -V -DWITHOUT_MULTIPIPE - < {}".format(self._corsika_input_file)
-        cmd += " > {}\n".format(input_tmp_file)
+        cmd = str(cmd) + f" -V -DWITHOUT_MULTIPIPE - < {self._corsika_input_file}"
+        cmd += f" > {input_tmp_file}\n"
         return cmd
 
     def _get_autoinputs_command(self, run_number, input_tmp_file):
@@ -244,12 +242,12 @@ def _get_autoinputs_command(self, run_number, input_tmp_file):
         log_file = self.get_file_name(file_type="log", **self.get_info_for_file_name(run_number))
 
         cmd = self._simtel_source_path.joinpath("sim_telarray/bin/corsika_autoinputs")
-        cmd = str(cmd) + " --run {}".format(corsika_bin_path)
-        cmd += " -R {}".format(run_number)
-        cmd += " -p {}".format(self._corsika_data_dir)
+        cmd = str(cmd) + f" --run {corsika_bin_path}"
+        cmd += f" -R {run_number}"
+        cmd += f" -p {self._corsika_data_dir}"
         if self._keep_seeds:
             cmd += " --keep-seeds"
-        cmd += " {} > {} 2>&1".format(input_tmp_file, log_file)
+        cmd += f" {input_tmp_file} > {log_file} 2>&1"
         cmd += " || exit 1\n"
         return cmd
 
@@ -318,9 +316,7 @@ def get_file_name(self, file_type, **kwargs):
             return self._corsika_log_dir.joinpath(f"log_{file_name}.log")
         if file_type == "corsika_log":
             run_dir = self._get_run_directory(kwargs["run"])
-            return self._corsika_data_dir.joinpath(run_dir).joinpath(
-                "run{}.log".format(kwargs["run"])
-            )
+            return self._corsika_data_dir.joinpath(run_dir).joinpath(f"run{kwargs['run']}.log")
         if file_type == "script":
             script_file_dir = self._output_directory.joinpath("scripts")
             script_file_dir.mkdir(parents=True, exist_ok=True)
@@ -383,7 +379,7 @@ def get_resources(self, run_number=None):
             file_type="sub_log", **self.get_info_for_file_name(run_number), mode="out"
         )
 
-        self._logger.debug("Reading resources from {}".format(sub_log_file))
+        self._logger.debug(f"Reading resources from {sub_log_file}")
 
         _resources = {}
 
@@ -415,9 +411,9 @@ def _validate_run_number(self, run_number):
         """
         if run_number is None:
             return self.corsika_config.get_user_parameter("RUNNR")
-        elif not float(run_number).is_integer() or run_number < 1:
-            msg = "Invalid type of run number ({}) - it must be an uint.".format(run_number)
+        if not float(run_number).is_integer() or run_number < 1:
+            msg = f"Invalid type of run number ({run_number}) - it must be an uint."
             self._logger.error(msg)
             raise ValueError(msg)
-        else:
-            return run_number
+
+        return run_number
diff --git a/simtools/data_model/model_data_writer.py b/simtools/data_model/model_data_writer.py
index 4048edb3d..212cb8d47 100644
--- a/simtools/data_model/model_data_writer.py
+++ b/simtools/data_model/model_data_writer.py
@@ -46,12 +46,12 @@ def write_data(self, product_data):
 
         _file = self.workflow_config.product_data_file_name()
         try:
-            self._logger.info("Writing data to {}".format(_file))
+            self._logger.info(f"Writing data to {_file}")
             product_data.write(
                 _file, format=self.workflow_config.product_data_file_format(), overwrite=True
             )
         except FileNotFoundError:
-            self._logger.error("Error writing model data to {}".format(_file))
+            self._logger.error(f"Error writing model data to {_file}")
             raise
 
     def write_metadata(self, ymlfile=None, keys_lower_case=False):
@@ -81,7 +81,7 @@ def write_metadata(self, ymlfile=None, keys_lower_case=False):
         try:
             if not ymlfile:
                 ymlfile = self.workflow_config.product_data_file_name(".yml")
-            self._logger.info("Writing metadata to {}".format(ymlfile))
+            self._logger.info(f"Writing metadata to {ymlfile}")
             with open(ymlfile, "w", encoding="UTF-8") as file:
                 yaml.safe_dump(
                     gen.change_dict_keys_case(self.workflow_config.top_level_meta, keys_lower_case),
@@ -90,7 +90,7 @@ def write_metadata(self, ymlfile=None, keys_lower_case=False):
                 )
             return ymlfile
         except FileNotFoundError:
-            self._logger.error("Error writing model data to {}".format(ymlfile))
+            self._logger.error(f"Error writing model data to {ymlfile}")
             raise
         except AttributeError:
             self._logger.error("No metadata defined for writing")
diff --git a/simtools/data_model/validate_data.py b/simtools/data_model/validate_data.py
index 816a106f6..74a670793 100644
--- a/simtools/data_model/validate_data.py
+++ b/simtools/data_model/validate_data.py
@@ -72,7 +72,7 @@ def validate_data_file(self):
         Open data file and read data from file
         """
 
-        self._logger.info("Reading data from {}".format(self._data_file_name))
+        self._logger.info(f"Reading data from {self._data_file_name}")
         self.data_table = Table.read(self._data_file_name, guess=True, delimiter=r"\s")
 
     def validate_data_columns(self):
@@ -106,9 +106,9 @@ def _check_required_columns(self):
         for key, value in self._reference_data_columns.items():
             if "required_column" in value and value["required_column"] is True:
                 if key in self.data_table.columns:
-                    self._logger.debug("Found required data column '{}'".format(key))
+                    self._logger.debug(f"Found required data column '{key}'")
                 else:
-                    raise KeyError("Missing required column '{}'".format(key))
+                    raise KeyError(f"Missing required column '{key}'")
 
     def _sort_data(self):
         """
@@ -132,16 +132,14 @@ def _sort_data(self):
                     _columns_by_which_to_reverse_sort.append(key)
 
         if len(_columns_by_which_to_sort) > 0:
-            self._logger.debug("Sorting data columns: {}".format(_columns_by_which_to_sort))
+            self._logger.debug(f"Sorting data columns: {_columns_by_which_to_sort}")
             try:
                 self.data_table.sort(_columns_by_which_to_sort)
             except AttributeError:
                 self._logger.error("No data table defined for sorting")
                 raise
         elif len(_columns_by_which_to_reverse_sort) > 0:
-            self._logger.debug(
-                "Reverse sorting data columns: {}".format(_columns_by_which_to_reverse_sort)
-            )
+            self._logger.debug(f"Reverse sorting data columns: {_columns_by_which_to_reverse_sort}")
             try:
                 self.data_table.sort(_columns_by_which_to_reverse_sort, reverse=True)
             except AttributeError:
@@ -176,9 +174,8 @@ def _check_data_for_duplicates(self):
                 self.data_table = unique(self.data_table)
             else:
                 self._logger.error(
-                    "Failed removal of duplication for column {}, values are not unqiue".format(
-                        _column_with_unique_requirement
-                    )
+                    "Failed removal of duplication for column "
+                    f"{_column_with_unique_requirement}, values are not unqiue"
                 )
                 raise ValueError
 
@@ -197,10 +194,10 @@ def _get_unique_column_requirement(self):
 
         for key, value in self._reference_data_columns.items():
             if "attribute" in value and "remove_duplicates" in value["attribute"]:
-                self._logger.debug("Removing duplicates for column '{}'".format(key))
+                self._logger.debug(f"Removing duplicates for column '{key}'")
                 _unique_required_column.append(key)
 
-        self._logger.debug("Unique required columns: {}".format(_unique_required_column))
+        self._logger.debug(f"Unique required columns: {_unique_required_column}")
         return _unique_required_column
 
     def _get_reference_unit(self, column_name):
@@ -228,7 +225,7 @@ def _get_reference_unit(self, column_name):
             reference_unit = self._reference_data_columns[column_name]["unit"]
         except KeyError:
             self._logger.error(
-                "Data column '{}' not found in reference column definition".format(column_name)
+                f"Data column '{column_name}' not found in reference column definition"
             )
             raise
 
@@ -283,7 +280,7 @@ def _check_and_convert_units(self, col):
 
         """
 
-        self._logger.debug("Checking data column '{}'".format(col.name))
+        self._logger.debug(f"Checking data column '{col.name}'")
 
         try:
             reference_unit = self._get_reference_unit(col.name)
@@ -292,18 +289,16 @@ def _check_and_convert_units(self, col):
                 return col
 
             self._logger.debug(
-                "Data column '{}' with reference unit '{}' and data unit '{}'".format(
-                    col.name, reference_unit, col.unit
-                )
+                f"Data column '{col.name}' with reference unit "
+                f"'{reference_unit}' and data unit '{col.unit}'"
             )
 
             col.convert_unit_to(reference_unit)
 
         except u.core.UnitConversionError:
             self._logger.error(
-                "Invalid unit in data column '{}'. Expected type '{}', found '{}'".format(
-                    col.name, reference_unit, col.unit
-                )
+                f"Invalid unit in data column '{col.name}'. "
+                f"Expected type '{reference_unit}', found '{col.unit}'"
             )
             raise
 
@@ -335,15 +330,15 @@ def _check_range(self, col_name, col_min, col_max, range_type="allowed_range"):
             range columns
 
         """
-        self._logger.debug("Checking data in column '{}' for '{}'".format(col_name, range_type))
+        self._logger.debug(f"Checking data in column '{col_name}' for '{range_type}'")
 
         try:
-            if range_type != "allowed_range" and range_type != "required_range":
+            if range_type not in ("allowed_range", "required_range"):
                 raise KeyError
             if range_type not in self._reference_data_columns[col_name]:
                 return None
-        except KeyError:
-            raise KeyError(f"Missing column '{col_name} in reference range'")
+        except KeyError as e:
+            raise KeyError(f"Missing column '{col_name} in reference range'") from e
 
         try:
             if not self._interval_check(
@@ -356,19 +351,13 @@ def _check_range(self, col_name, col_min, col_max, range_type="allowed_range"):
             ):
                 raise ValueError
         except KeyError:
-            self._logger.error(
-                "Invalid range ('{}') definition for column '{}'".format(range_type, col_name)
-            )
+            self._logger.error(f"Invalid range ('{range_type}') definition for column '{col_name}'")
         except ValueError:
             self._logger.error(
-                "Value for column '{}' out of range. [[{}, {}], {}: [[{}, {}]".format(
-                    col_name,
-                    col_min,
-                    col_max,
-                    range_type,
-                    self._reference_data_columns[col_name][range_type]["min"],
-                    self._reference_data_columns[col_name][range_type]["max"],
-                )
+                f"Value for column '{col_name}' out of range. "
+                f"[[{col_min}, {col_max}], {range_type}: "
+                f"[[{self._reference_data_columns[col_name][range_type]['min']}, "
+                f"{self._reference_data_columns[col_name][range_type]['max']}]"
             )
             raise
 
diff --git a/simtools/data_model/validate_schema.py b/simtools/data_model/validate_schema.py
index 32d57c5a8..68edacf91 100644
--- a/simtools/data_model/validate_schema.py
+++ b/simtools/data_model/validate_schema.py
@@ -50,7 +50,7 @@ def validate_and_transform(self, meta_file_name=None, lower_case=True):
 
         """
         if meta_file_name:
-            self._logger.debug("Reading meta data from {}".format(meta_file_name))
+            self._logger.debug(f"Reading meta data from {meta_file_name}")
             self.data_dict = gen.collect_data_from_yaml_or_dict(meta_file_name, None)
 
         if lower_case:
@@ -143,7 +143,7 @@ def _validate_data_type(self, schema, key, data_field):
 
         """
 
-        self._logger.debug("checking data field {} for {}".format(key, schema["type"]))
+        self._logger.debug(f"checking data field {key} for {schema['type']}")
 
         convert = {"str": type("str"), "float": type(1.0), "int": type(0), "bool": type(True)}
 
@@ -161,9 +161,8 @@ def _validate_data_type(self, schema, key, data_field):
                     raise ValueError
             except ValueError as error:
                 raise ValueError(
-                    "invalid type for key {}. Expected: {}, Found: {}".format(
-                        key, schema["type"], type(data_field).__name__
-                    )
+                    f"invalid type for key {key}. Expected: {schema['type']}, "
+                    f"Found: {type(data_field).__name__}"
                 ) from error
 
     @staticmethod
@@ -191,7 +190,7 @@ def _validate_datetime(data_field, optional_field=False):
         except (ValueError, TypeError) as error:
             if not optional_field:
                 raise ValueError(
-                    "invalid date format. Expected {}; Found {}".format(format_date, data_field)
+                    f"invalid date format. Expected {format_date}; Found {data_field}"
                 ) from error
 
     @staticmethod
@@ -214,7 +213,7 @@ def _validate_email(data_field, key):
         """
         regex = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
         if not re.fullmatch(regex, data_field):
-            raise ValueError("invalid email format in field {}: {}".format(key, data_field))
+            raise ValueError(f"invalid email format in field {key}: {data_field}")
 
     def _validate_list(self, schema_type, data_list):
         """
diff --git a/simtools/data_model/workflow_description.py b/simtools/data_model/workflow_description.py
index 72d61f0c5..eafee7c12 100644
--- a/simtools/data_model/workflow_description.py
+++ b/simtools/data_model/workflow_description.py
@@ -83,7 +83,7 @@ def set_configuration_parameter(self, key, value):
         try:
             self.workflow_config["configuration"][key] = value
         except KeyError:
-            self._logger.error("Missing key {} in configuration".format(key))
+            self._logger.error(f"Missing key {key} in configuration")
             raise
 
     def get_configuration_parameter(self, key):
@@ -109,7 +109,7 @@ def get_configuration_parameter(self, key):
         try:
             return self.workflow_config["configuration"][key]
         except KeyError:
-            self._logger.error("Missing key {} in configuration".format(key))
+            self._logger.error(f"Missing key {key} in configuration")
             raise
 
     def reference_data_columns(self):
@@ -237,7 +237,7 @@ def product_data_directory(self):
         _output_dir = self.io_handler.get_output_directory(
             self.workflow_config["activity"]["name"], "product-data"
         )
-        self._logger.debug("Outputdirectory {}".format(_output_dir))
+        self._logger.debug(f"Outputdirectory {_output_dir}")
         return _output_dir
 
     def _fill_association_meta_from_args(self, association_dict):
@@ -257,7 +257,7 @@ def _fill_association_meta_from_args(self, association_dict):
             if metadata description cannot be filled.
 
         """
-        self._logger.debug("Fill metadata from args: {}".format(self.args_dict))
+        self._logger.debug(f"Fill metadata from args: {self.args_dict}")
 
         _association = {}
         try:
@@ -272,7 +272,7 @@ def _fill_association_meta_from_args(self, association_dict):
             self._logger.error("Error reading association meta data from args")
             raise
         except AttributeError as e:
-            self._logger.debug("Missing parameter on command line, use defaults ({})".format(e))
+            self._logger.debug(f"Missing parameter on command line, use defaults ({e})")
 
         self._fill_context_sim_list(association_dict, _association)
 
@@ -335,7 +335,7 @@ def _fill_product_meta(self, product_dict):
         """
 
         product_dict["id"] = self.workflow_config["activity"]["id"]
-        self._logger.debug("Assigned ACTIVITY UUID {}".format(product_dict["id"]))
+        self._logger.debug(f"Assigned ACTIVITY UUID {product_dict['id']}")
 
         product_dict["data"]["category"] = "SIM"
         product_dict["data"]["level"] = "R0"
@@ -411,9 +411,7 @@ def _read_workflow_configuration(self, workflow_config_file):
                     gen.collect_data_from_yaml_or_dict(workflow_config_file, None)["CTASIMPIPE"],
                     True,
                 )
-                self._logger.debug(
-                    "Reading workflow configuration from {}".format(workflow_config_file)
-                )
+                self._logger.debug(f"Reading workflow configuration from {workflow_config_file}")
             except KeyError:
                 self._logger.debug("Error reading CTASIMPIPE workflow configuration")
 
@@ -447,9 +445,8 @@ def _merge_config_dicts(self, dict_high, dict_low, add_new_fields=False):
                     dict_high[k] = dict_low[k]
                 elif dict_high[k] != dict_low[k] and dict_low[k] is not None:
                     self._logger.debug(
-                        "Conflicting entries between dict: {} vs {} (use {})".format(
-                            dict_high[k], dict_low[k], dict_high[k]
-                        )
+                        f"Conflicting entries between dict: {dict_high[k]} vs {dict_low[k]} "
+                        f"(use {dict_high[k]})"
                     )
             elif add_new_fields:
                 dict_high[k] = dict_low[k]
@@ -486,10 +483,10 @@ def _fill_context_sim_list(product_list, new_entry_dict):
         list
             Updated product list.
 
-
         """
+
         if len(new_entry_dict) == 0:
-            return
+            return []
         try:
             if any(v is not None for v in product_list[0].values()):
                 product_list.append(new_entry_dict)
diff --git a/simtools/db_handler.py b/simtools/db_handler.py
index af4f47305..4f38a5cea 100644
--- a/simtools/db_handler.py
+++ b/simtools/db_handler.py
@@ -19,6 +19,10 @@
 __all__ = ["DatabaseHandler"]
 
 
+# pylint: disable=unsubscriptable-object
+# The above comment is because pylint does not know that DatabaseHandler.db_client is subscriptable
+
+
 class DatabaseHandler:
     """
     DatabaseHandler provides the interface to the DB.
@@ -36,7 +40,6 @@ class DatabaseHandler:
         "db_api_authentication_database" - DB with user info (optional, default is "admin")
     """
 
-    # TODO move into config file?
     DB_TABULATED_DATA = "CTA-Simulation-Model"
     DB_CTA_SIMULATION_MODEL = "CTA-Simulation-Model"
     DB_CTA_SIMULATION_MODEL_DESCRIPTIONS = "CTA-Simulation-Model-Descriptions"
@@ -61,7 +64,7 @@ def __init__(self, mongo_db_config=None):
         self._logger.debug("Initialize DatabaseHandler")
 
         self.mongo_db_config = mongo_db_config
-        self._logger.debug("DB configuration: {}".format(self.mongo_db_config))
+        self._logger.debug(f"DB configuration: {self.mongo_db_config}")
         self.io_handler = io_handler.IOHandler()
 
         self._set_up_connection()
@@ -172,15 +175,14 @@ def get_model_parameters(
                 only_applicable,
             )
             return _pars
-        else:
-            _version_validated = names.validate_model_version_name(model_version)
 
-            return self._get_model_parameters_yaml(
-                _site_validated,
-                _tel_model_name_validated,
-                _version_validated,
-                only_applicable,
-            )
+        _version_validated = names.validate_model_version_name(model_version)
+
+        return self._get_model_parameters_yaml(
+            _tel_model_name_validated,
+            _version_validated,
+            only_applicable,
+        )
 
     def export_file_db(self, db_name, dest, file_name):
         """
@@ -210,7 +212,7 @@ def export_file_db(self, db_name, dest, file_name):
         self._logger.debug(f"Getting {file_name} from {db_name} and writing it to {dest}")
         file_path_instance = self._get_file_mongo_db(db_name, file_name)
         self._write_file_from_mongo_to_disk(db_name, dest, file_path_instance)
-        return file_path_instance._id
+        return file_path_instance._id  # pylint: disable=protected-access;
 
     def export_model_files(self, parameters, dest):
         """
@@ -273,23 +275,21 @@ def _write_model_file_yaml(self, file_name, dest_dir, no_file_ok=False):
             file = gen.find_file(file_name, self.io_handler.model_path)
         except FileNotFoundError:
             if no_file_ok:
-                self._logger.debug("File {} not found but no_file_ok".format(file_name))
+                self._logger.debug(f"File {file_name} not found but no_file_ok")
                 return
-            else:
-                raise
+
+            raise
 
         dest_file.write_text(file.read_text())
 
     def _get_model_parameters_yaml(
-        self, site, telescope_model_name, model_version, only_applicable=False
+        self, telescope_model_name, model_version, only_applicable=False
     ):
         """
         Get parameters from DB for one specific type.
 
         Parameters
         ----------
-        site: str
-            North or South.
         telescope_model_name: str
             Telescope model name (e.g MST-FlashCam-D ...).
         model_version: str
@@ -365,17 +365,17 @@ def _get_model_parameters_mongo_db(
         _tel_name_db = self._get_telescope_model_name_for_db(_site_validated, telescope_model_name)
         _tel_class = get_telescope_class(telescope_model_name)
 
-        self._logger.debug("Tel_name_db: {}".format(_tel_name_db))
-        self._logger.debug("Tel_class: {}".format(_tel_class))
+        self._logger.debug(f"Tel_name_db: {_tel_name_db}")
+        self._logger.debug(f"Tel_class: {_tel_class}")
 
         if _tel_class == "MST":
             # MST-FlashCam or MST-NectarCam
-            _which_tel_labels = ["{}-MST-Structure-D".format(_site_validated), _tel_name_db]
+            _which_tel_labels = [f"{_site_validated}-MST-Structure-D", _tel_name_db]
         elif _tel_class == "SST":
             # SST = SST-Camera + SST-Structure
             _which_tel_labels = [
-                "{}-SST-Camera-D".format(_site_validated),
-                "{}-SST-Structure-D".format(_site_validated),
+                f"{_site_validated}-SST-Camera-D",
+                f"{_site_validated}-SST-Structure-D",
             ]
         else:
             _which_tel_labels = [_tel_name_db]
@@ -383,15 +383,15 @@ def _get_model_parameters_mongo_db(
         # Selecting version and applicable (if on)
         _pars = dict()
         for _tel in _which_tel_labels:
-            self._logger.debug("Getting {} parameters from MongoDB".format(_tel))
+            self._logger.debug(f"Getting {_tel} parameters from MongoDB")
 
             # If tel is a structure, only applicable pars will be collected, always.
             # The default ones will be covered by the camera pars.
             _select_only_applicable = only_applicable or (
                 _tel
                 in [
-                    "{}-MST-Structure-D".format(_site_validated),
-                    "{}-SST-Structure-D".format(_site_validated),
+                    f"{_site_validated}-MST-Structure-D",
+                    f"{_site_validated}-SST-Structure-D",
                 ]
             )
 
@@ -462,7 +462,7 @@ def read_mongo_db(
             "Version": _model_version,
         }
 
-        self._logger.debug("Trying the following query: {}".format(query))
+        self._logger.debug(f"Trying the following query: {query}")
         if only_applicable:
             query["Applicable"] = True
         if collection.count_documents(query) < 1:
@@ -499,9 +499,9 @@ def _get_all_model_parameters_yaml(self, telescope_name_yaml):
 
         """
 
-        _file_name_db = "parValues-{}.yml".format(telescope_name_yaml)
+        _file_name_db = f"parValues-{telescope_name_yaml}.yml"
         _yaml_file = gen.find_file(_file_name_db, self.io_handler.model_path)
-        self._logger.debug("Reading DB file {}".format(_yaml_file))
+        self._logger.debug(f"Reading DB file {_yaml_file}")
         with open(_yaml_file, "r") as stream:
             _all_pars = yaml.safe_load(stream)
         return _all_pars
@@ -540,8 +540,8 @@ def get_site_parameters(
                 only_applicable,
             )
             return _pars
-        else:
-            return self._get_site_parameters_yaml(_site, _model_version, only_applicable)
+
+        return self._get_site_parameters_yaml(_site, _model_version, only_applicable)
 
     def _get_site_parameters_yaml(self, site, model_version, only_applicable=False):
         """
@@ -565,7 +565,7 @@ def _get_site_parameters_yaml(self, site, model_version, only_applicable=False):
         site_yaml = "lapalma" if site == "North" else "paranal"
 
         yaml_file = gen.find_file("parValues-Sites.yml", self.io_handler.model_path)
-        self._logger.info("Reading DB file {}".format(yaml_file))
+        self._logger.info(f"Reading DB file {yaml_file}")
         with open(yaml_file, "r") as stream:
             _all_pars_versions = yaml.safe_load(stream)
 
@@ -750,7 +750,7 @@ def get_derived_values(self, site, telescope_model_name, model_version):
             DatabaseHandler.DB_CTA_SIMULATION_MODEL,
         )
 
-        self._logger.debug("Getting derived values for {} from the DB".format(_tel_name_db))
+        self._logger.debug(f"Getting derived values for {_tel_name_db} from the DB")
 
         _pars = self.read_mongo_db(
             DatabaseHandler.DB_DERIVED_VALUES,
@@ -791,10 +791,8 @@ def _get_file_mongo_db(db_name, file_name):
         file_system = gridfs.GridFS(db)
         if file_system.exists({"filename": file_name}):
             return file_system.find_one({"filename": file_name})
-        else:
-            raise FileNotFoundError(
-                "The file {} does not exist in the database {}".format(file_name, db_name)
-            )
+
+        raise FileNotFoundError(f"The file {file_name} does not exist in the database {db_name}")
 
     @staticmethod
     def _write_file_from_mongo_to_disk(db_name, path, file):
@@ -816,8 +814,6 @@ def _write_file_from_mongo_to_disk(db_name, path, file):
         with open(Path(path).joinpath(file.filename), "wb") as output_file:
             fs_output.download_to_stream_by_name(file.filename, output_file)
 
-        return
-
     def copy_telescope(
         self,
         db_name,
@@ -863,9 +859,8 @@ def copy_telescope(
             collection_to_copy_to = collection_name
 
         self._logger.info(
-            "Copying version {} of {} to the new telescope {} in the {} DB".format(
-                version_to_copy, tel_to_copy, new_tel_name, db_to_copy_to
-            )
+            f"Copying version {version_to_copy} of {tel_to_copy} "
+            f"to the new telescope {new_tel_name} in the {db_to_copy_to} DB"
         )
 
         collection = DatabaseHandler.db_client[db_name][collection_name]
@@ -884,14 +879,12 @@ def copy_telescope(
             post.pop("_id", None)
             db_entries.append(post)
 
-        self._logger.info("Creating new telescope {}".format(new_tel_name))
+        self._logger.info(f"Creating new telescope {new_tel_name}")
         collection = DatabaseHandler.db_client[db_to_copy_to][collection_to_copy_to]
         try:
             collection.insert_many(db_entries)
         except BulkWriteError as exc:
-            raise exc(exc.details)
-
-        return
+            raise BulkWriteError.details from exc
 
     def copy_documents(self, db_name, collection, query, db_to_copy_to, collection_to_copy_to=None):
         """
@@ -935,15 +928,13 @@ def copy_documents(self, db_name, collection, query, db_to_copy_to, collection_t
             db_entries.append(post)
 
         self._logger.info(
-            "Copying documents matching the following query {}\nto {}".format(query, db_to_copy_to)
+            f"Copying documents matching the following query {query}\nto {db_to_copy_to}"
         )
         _collection = DatabaseHandler.db_client[db_to_copy_to][collection_to_copy_to]
         try:
             _collection.insert_many(db_entries)
         except BulkWriteError as exc:
-            raise exc(exc.details)
-
-        return
+            raise BulkWriteError.details from exc
 
     def delete_query(self, db_name, collection, query):
         """
@@ -977,17 +968,10 @@ def delete_query(self, db_name, collection, query):
                 query["Version"], DatabaseHandler.DB_CTA_SIMULATION_MODEL
             )
 
-        self._logger.info(
-            "Deleting {} entries from {}".format(
-                _collection.count_documents(query),
-                db_name,
-            )
-        )
+        self._logger.info(f"Deleting {_collection.count_documents(query)} entries from {db_name}")
 
         _collection.delete_many(query)
 
-        return
-
     def update_parameter(
         self,
         db_name,
@@ -1043,9 +1027,8 @@ def update_parameter(
         old_value = par_entry["Value"]
 
         self._logger.info(
-            "For telescope {}, version {}\nreplacing {} value from {} to {}".format(
-                telescope, _model_version, parameter, old_value, new_value
-            )
+            f"For telescope {telescope}, version {_model_version}\n"
+            f"replacing {parameter} value from {old_value} to {new_value}"
         )
 
         files_to_add_to_db = set()
@@ -1054,11 +1037,11 @@ def update_parameter(
             if file_prefix is None:
                 raise FileNotFoundError(
                     "The location of the file to upload, "
-                    "corresponding to the {} parameter, must be provided."
-                ).format(parameter)
+                    f"corresponding to the {parameter} parameter, must be provided."
+                )
             file_path = Path(file_prefix).joinpath(new_value)
-            files_to_add_to_db.add("{}".format(file_path))
-            self._logger.info("Will also add the file {} to the DB".format(file_path))
+            files_to_add_to_db.add(f"{file_path}")
+            self._logger.info(f"Will also add the file {file_path} to the DB")
         else:
             file = False
 
@@ -1068,8 +1051,6 @@ def update_parameter(
         for file_now in files_to_add_to_db:
             self.insert_file_to_db(file_now, db_name)
 
-        return
-
     def update_parameter_field(
         self,
         db_name,
@@ -1142,9 +1123,7 @@ def update_parameter_field(
         par_entry = collection.find_one(query)
         if par_entry is None:
             self._logger.warning(
-                "The query {} did not return any results. I will not make any changes.".format(
-                    query
-                )
+                f"The query {query} did not return any results. I will not make any changes."
             )
             return
 
@@ -1156,11 +1135,11 @@ def update_parameter_field(
                     f"The value of the field {field} is already {new_value}. No changes necessary"
                 )
                 return
-            else:
-                self._logger.info(
-                    f"For {logger_info}, version {_model_version}, parameter {parameter}, "
-                    f"replacing field {field} value from {old_field_value} to {new_value}"
-                )
+
+            self._logger.info(
+                f"For {logger_info}, version {_model_version}, parameter {parameter}, "
+                f"replacing field {field} value from {old_field_value} to {new_value}"
+            )
         else:
             self._logger.info(
                 f"For {logger_info}, version {_model_version}, parameter {parameter}, "
@@ -1171,8 +1150,6 @@ def update_parameter_field(
 
         collection.update_one(query, query_update)
 
-        return
-
     def add_parameter(
         self,
         db_name,
@@ -1235,22 +1212,20 @@ def add_parameter(
             if file_prefix is None:
                 raise FileNotFoundError(
                     "The location of the file to upload, "
-                    "corresponding to the {} parameter, must be provided."
-                ).format(parameter)
+                    f"corresponding to the {parameter} parameter, must be provided."
+                )
             file_path = Path(file_prefix).joinpath(new_value)
-            files_to_add_to_db.add("{}".format(file_path))
+            files_to_add_to_db.add(f"{file_path}")
         else:
             par_entry["File"] = False
 
-        self._logger.info("Will add the following entry to DB:\n{}".format(par_entry))
+        self._logger.info(f"Will add the following entry to DB:\n{par_entry}")
 
         collection.insert_one(par_entry)
         if len(files_to_add_to_db) > 0:
-            self._logger.info("Will also add the file {} to the DB".format(file_path))
+            self._logger.info(f"Will also add the file {file_path} to the DB")
             self.insert_file_to_db(files_to_add_to_db, db_name)
 
-        return
-
     def add_new_parameter(
         self,
         db_name,
@@ -1322,26 +1297,24 @@ def add_new_parameter(
                     f"corresponding to the {parameter} parameter, must be provided."
                 )
             file_path = Path(file_prefix).joinpath(value)
-            files_to_add_to_db.add("{}".format(file_path))
+            files_to_add_to_db.add(f"{file_path}")
 
         kwargs.pop("Type", None)
         db_entry.update(kwargs)
 
-        self._logger.info("Will add the following entry to DB:\n{}".format(db_entry))
+        self._logger.info(f"Will add the following entry to DB:\n{db_entry}")
 
         collection.insert_one(db_entry)
         for file_to_insert_now in files_to_add_to_db:
-            self._logger.info("Will also add the file {} to the DB".format(file_to_insert_now))
+            self._logger.info(f"Will also add the file {file_to_insert_now} to the DB")
             self.insert_file_to_db(file_to_insert_now, db_name)
 
-        return
-
     def _convert_version_to_tagged(self, model_version, db_name):
         """Convert to tagged version, if needed."""
         if model_version in ["Current", "Latest"]:
             return self._get_tagged_version(db_name, model_version)
-        else:
-            return model_version
+
+        return model_version
 
     @staticmethod
     def _get_tagged_version(db_name, version="Current"):
@@ -1415,7 +1388,9 @@ def insert_file_to_db(self, file_name, db_name=DB_CTA_SIMULATION_MODEL, **kwargs
             self._logger.warning(
                 f"The file {kwargs['filename']} exists in the DB. Returning its ID"
             )
-            return file_system.find_one({"filename": kwargs["filename"]})._id
+            return file_system.find_one(
+                {"filename": kwargs["filename"]}
+            )._id  # pylint: disable=protected-access;
         with open(file_name, "rb") as data_file:
             return file_system.put(data_file, **kwargs)
 
diff --git a/simtools/io_handler.py b/simtools/io_handler.py
index bfe76ac96..c276eaec6 100644
--- a/simtools/io_handler.py
+++ b/simtools/io_handler.py
@@ -89,7 +89,7 @@ def get_output_directory(self, label=None, dir_type=None, test=False):
         try:
             path.mkdir(parents=True, exist_ok=True)
         except FileNotFoundError:
-            self._logger.error("Error creating directory {}".format(str(path)))
+            self._logger.error(f"Error creating directory {str(path)}")
             raise
 
         return path.absolute()
diff --git a/simtools/job_execution/job_manager.py b/simtools/job_execution/job_manager.py
index 57adbe0e3..dc2491966 100644
--- a/simtools/job_execution/job_manager.py
+++ b/simtools/job_execution/job_manager.py
@@ -10,8 +10,6 @@
 class MissingWorkloadManager(Exception):
     """Exception for missing work load manager."""
 
-    pass
-
 
 class JobManager:
     """
@@ -37,13 +35,13 @@ def __init__(self, submit_command=None, test=False):
         self._logger = logging.getLogger(__name__)
         self.submit_command = submit_command
         self.test = test
+        self.run_script = None
+        self.run_out_file = None
 
         try:
             self.test_submission_system()
         except MissingWorkloadManager:
-            self._logger.error(
-                "Requested workflow manager not found: {}".format(self.submit_command)
-            )
+            self._logger.error(f"Requested workflow manager not found: {self.submit_command}")
             raise
 
     def test_submission_system(self):
@@ -58,15 +56,15 @@ def test_submission_system(self):
 
         if self.submit_command is None:
             return
-        elif self.submit_command.find("qsub") >= 0:
+        if self.submit_command.find("qsub") >= 0:
             if gen.program_is_executable("qsub"):
                 return
             raise MissingWorkloadManager
-        elif self.submit_command.find("condor_submit") >= 0:
+        if self.submit_command.find("condor_submit") >= 0:
             if gen.program_is_executable("condor_submit"):
                 return
             raise MissingWorkloadManager
-        elif self.submit_command.find("local") >= 0:
+        if self.submit_command.find("local") >= 0:
             return
 
         raise MissingWorkloadManager
@@ -86,10 +84,10 @@ def submit(self, run_script=None, run_out_file=None):
         self.run_script = str(run_script)
         self.run_out_file = str(run_out_file).replace(".log", "")
 
-        self._logger.info("Submitting script {}".format(self.run_script))
-        self._logger.info("Job output stream {}".format(self.run_out_file + ".out"))
-        self._logger.info("Job error stream {}".format(self.run_out_file + ".err"))
-        self._logger.info("Job log stream {}".format(self.run_out_file + ".job"))
+        self._logger.info(f"Submitting script {self.run_script}")
+        self._logger.info(f"Job output stream {self.run_out_file + '.out'}")
+        self._logger.info(f"Job error stream {self.run_out_file + '.err'}")
+        self._logger.info(f"Job log stream {self.run_out_file + '.job'}")
 
         if self.submit_command.find("qsub") >= 0:
             self._submit_gridengine()
@@ -123,16 +121,16 @@ def _submit_htcondor(self):
         """
 
         _condor_file = self.run_script + ".condor"
-        self._logger.info("Submitting script to HTCondor ({})".format(_condor_file))
+        self._logger.info(f"Submitting script to HTCondor ({_condor_file})")
         try:
             with open(_condor_file, "w") as file:
-                file.write("Executable = {}\n".format(self.run_script))
-                file.write("Output = {}\n".format(self.run_out_file + ".out"))
-                file.write("Error = {}\n".format(self.run_out_file + ".err"))
-                file.write("Log = {}\n".format(self.run_out_file + ".job"))
+                file.write(f"Executable = {self.run_script}\n")
+                file.write(f"Output = {self.run_out_file + '.out'}\n")
+                file.write(f"Error = {self.run_out_file + '.err'}\n")
+                file.write(f"Log = {self.run_out_file + '.job'}\n")
                 file.write("queue 1\n")
         except FileNotFoundError:
-            self._logger.error("Failed creating condor submission file {}".format(_condor_file))
+            self._logger.error(f"Failed creating condor submission file {_condor_file}")
 
         shell_command = self.submit_command + " " + _condor_file
         if not self.test:
diff --git a/simtools/layout/layout_array.py b/simtools/layout/layout_array.py
index 3d410e195..00458bb51 100644
--- a/simtools/layout/layout_array.py
+++ b/simtools/layout/layout_array.py
@@ -16,8 +16,6 @@
 class InvalidTelescopeListFile(Exception):
     """Exception for invalid telescope list file."""
 
-    pass
-
 
 class LayoutArray:
     """
@@ -58,6 +56,7 @@ def __init__(
 
         self._telescope_list = []
         self._epsg = None
+        self.telescope_list_file = None
         if telescope_list_file is None:
             self._initialize_coordinate_systems(layout_center_data)
             self._initialize_corsika_telescope(corsika_telescope_data)
@@ -91,7 +90,7 @@ def from_layout_array_name(cls, layout_array_name, label=None):
         layout = cls(name=valid_layout_array_name, label=label)
 
         telescope_list_file = layout.io_handler.get_input_data_file(
-            "layout", "telescope_positions-{}.ecsv".format(valid_layout_array_name)
+            "layout", f"telescope_positions-{valid_layout_array_name}.ecsv"
         )
         layout.read_telescope_list_file(telescope_list_file)
 
@@ -117,9 +116,7 @@ def _initialize_corsika_telescope(self, corsika_dict=None):
         self._corsika_telescope = {}
 
         if corsika_dict is not None:
-            self._logger.debug(
-                "Initialize CORSIKA telescope parameters from dict: {}".format(corsika_dict)
-            )
+            self._logger.debug(f"Initialize CORSIKA telescope parameters from dict: {corsika_dict}")
             self._initialize_corsika_telescope_from_dict(corsika_dict)
         else:
             self._logger.debug("Initialize CORSIKA telescope parameters from file")
@@ -151,7 +148,7 @@ def _initialize_sphere_parameters(sphere_dict):
         _sphere_dict_cleaned = {}
         try:
             for key, value in sphere_dict.items():
-                if isinstance(value, u.Quantity) or isinstance(value, str):
+                if isinstance(value, (str, u.Quantity)):
                     _sphere_dict_cleaned[key] = u.Quantity(value)
                 else:
                     _sphere_dict_cleaned[key] = value["value"] * u.Unit(value["unit"])
@@ -190,7 +187,7 @@ def _initialize_corsika_telescope_from_dict(self, corsika_dict):
         except (TypeError, KeyError):
             pass
 
-    def _initialize_coordinate_systems(self, center_dict=None, defaults_init=False):
+    def _initialize_coordinate_systems(self, center_dict=None):
         """
         Initialize array center and coordinate systems.
 
@@ -198,8 +195,6 @@ def _initialize_coordinate_systems(self, center_dict=None, defaults_init=False):
         ----------
         center_dict: dict
             dictionary with coordinates of array center.
-        defaults_init: bool
-            default initialisation without transformation in all available projections.
 
         Raises
         ------
@@ -207,7 +202,7 @@ def _initialize_coordinate_systems(self, center_dict=None, defaults_init=False):
             invalid array center definition.
 
         """
-        self._logger.debug("Initialize array center coordinate systems: {}".format(center_dict))
+        self._logger.debug(f"Initialize array center coordinate systems: {center_dict}")
 
         self._array_center = TelescopePosition()
         self._array_center.name = "array_center"
@@ -281,6 +276,8 @@ def _altitude_from_corsika_z(self, pos_z=None, altitude=None, tel_name=None):
                 self._get_corsika_sphere_center(tel_name),
             )
 
+        return np.nan
+
     def _get_corsika_sphere_center(self, tel_name):
         """
         Return CORSIKA sphere center value for given telescope.
@@ -419,12 +416,10 @@ def read_telescope_list_file(self, telescope_list_file):
         try:
             table = Table.read(telescope_list_file, format="ascii.ecsv")
         except FileNotFoundError:
-            self._logger.error(
-                "Error reading list of array elements from {}".format(telescope_list_file)
-            )
+            self._logger.error(f"Error reading list of array elements from {telescope_list_file}")
             raise
 
-        self._logger.info("Reading array elements from {}".format(telescope_list_file))
+        self._logger.info(f"Reading array elements from {telescope_list_file}")
 
         self._initialize_corsika_telescope(table.meta)
         self._initialize_coordinate_systems(table.meta)
@@ -573,7 +568,7 @@ def export_telescope_list(self, crs_name, corsika_z=False):
             pass
 
         self._set_telescope_list_file(crs_name)
-        self._logger.info("Exporting telescope list to {}".format(self.telescope_list_file))
+        self._logger.info(f"Exporting telescope list to {self.telescope_list_file}")
         table.write(self.telescope_list_file, format="ascii.ecsv", overwrite=True)
 
     def get_number_of_telescopes(self):
@@ -623,11 +618,11 @@ def get_corsika_input_list(self):
                 raise
 
             corsika_list += "TELESCOPE"
-            corsika_list += "\t {:.3f}E2".format(pos_x.value)
-            corsika_list += "\t {:.3f}E2".format(pos_y.value)
-            corsika_list += "\t {:.3f}E2".format(pos_z.value)
-            corsika_list += "\t {:.3f}E2".format(sphere_radius.value)
-            corsika_list += "\t # {}\n".format(tel.name)
+            corsika_list += f"\t {pos_x.value:.3f}E2"
+            corsika_list += f"\t {pos_y.value:.3f}E2"
+            corsika_list += f"\t {pos_z.value:.3f}E2"
+            corsika_list += f"\t {sphere_radius.value:.3f}E2"
+            corsika_list += f"\t # {tel.name}\n"
 
         return corsika_list
 
@@ -637,7 +632,7 @@ def _print_all(self):
 
         """
 
-        print("LayoutArray: {}".format(self.name))
+        print(f"LayoutArray: {self.name}")
         print("ArrayCenter")
         print(self._array_center)
         print("Telescopes")
@@ -722,15 +717,17 @@ def _get_crs_local(self):
             if not np.isnan(_center_lat.value) and not np.isnan(_center_lon.value):
                 proj4_string = (
                     "+proj=tmerc +ellps=WGS84 +datum=WGS84"
-                    + " +lon_0={} +lat_0={}".format(_center_lon, _center_lat)
+                    + f" +lon_0={_center_lon} +lat_0={_center_lat}"
                     + " +axis=nwu +units=m +k_0=1.0"
                 )
                 crs_local = pyproj.CRS.from_proj4(proj4_string)
-                self._logger.debug("Local Mercator projection: {}".format(crs_local))
+                self._logger.debug(f"Local Mercator projection: {crs_local}")
                 return crs_local
 
         self._logger.debug("crs_local cannot be built: missing array center lon and lat")
 
+        return None
+
     def _get_crs_utm(self):
         """
         UTM coordinate system definition.
@@ -743,11 +740,13 @@ def _get_crs_utm(self):
         """
         if self._epsg:
             crs_utm = pyproj.CRS.from_user_input(self._epsg)
-            self._logger.debug("UTM system: {}".format(crs_utm))
+            self._logger.debug(f"UTM system: {crs_utm}")
             return crs_utm
 
         self._logger.debug("crs_utm cannot be built because EPSG definition is missing")
 
+        return None
+
     @staticmethod
     def _get_crs_wgs84():
         """
@@ -784,3 +783,5 @@ def get_telescope_type(telescope_name):
 
         except IndexError:
             pass
+
+        return ""
diff --git a/simtools/layout/telescope_position.py b/simtools/layout/telescope_position.py
index 36c7307d7..68d6feaca 100644
--- a/simtools/layout/telescope_position.py
+++ b/simtools/layout/telescope_position.py
@@ -10,14 +10,10 @@
 class InvalidCoordSystem(Exception):
     """Exception for invalid coordinate system."""
 
-    pass
-
 
 class MissingInputForConvertion(Exception):
     """Exception for missing input for convertion."""
 
-    pass
-
 
 class TelescopePosition:
     """
@@ -50,20 +46,23 @@ def __init__(self, name=None):
     def __str__(self):
         telstr = self.name
         if self.has_coordinates("corsika"):
-            telstr += "\t CORSIKA x(->North): {0:0.2f} y(->West): {1:0.2f}".format(
-                self.crs["corsika"]["xx"]["value"], self.crs["corsika"]["yy"]["value"]
+            telstr += (
+                f"\t CORSIKA x(->North): {self.crs['corsika']['xx']['value']:0.2f} "
+                f"y(->West): {self.crs['corsika']['yy']['value']:0.2f}"
             )
         if self.has_coordinates("utm"):
-            telstr += "\t UTM East: {0:0.2f} UTM North: {1:0.2f}".format(
-                self.crs["utm"]["xx"]["value"], self.crs["utm"]["yy"]["value"]
+            telstr += (
+                f"\t UTM East: {self.crs['utm']['xx']['value']:0.2f} "
+                f"UTM North: {self.crs['utm']['yy']['value']:0.2f}"
             )
         if self.has_coordinates("mercator"):
-            telstr += "\t Longitude: {0:0.5f} Latitude: {1:0.5f}".format(
-                self.crs["mercator"]["xx"]["value"], self.crs["mercator"]["yy"]["value"]
+            telstr += (
+                f"\t Longitude: {self.crs['mercator']['xx']['value']:0.5f} "
+                f"Latitude: {self.crs['mercator']['yy']['value']:0.5f}"
             )
-        for _crs_name in self.crs:
+        for _crs_name, _crs_now in self.crs.items():
             if self.has_altitude(_crs_name):
-                telstr += "\t Alt: {:0.2f}".format(self.crs[_crs_name]["zz"]["value"])
+                telstr += f"\t Alt: {_crs_now['zz']['value']:0.2f}"
                 break
 
         return telstr
@@ -109,35 +108,29 @@ def print_compact_format(
                 _zz_header = "pos_z"
 
             if crs_name == "mercator":
-                telstr = "{0} {1:10.8f} {2:10.8f} {3:10.2f}".format(
-                    self.name,
-                    self.crs[crs_name]["xx"]["value"],
-                    self.crs[crs_name]["yy"]["value"],
-                    _zz,
+                telstr = (
+                    f"{self.name} {self.crs[crs_name]['xx']['value']:10.8f} "
+                    f"{self.crs[crs_name]['yy']['value']:10.8f} {_zz:10.2f}"
                 )
             else:
-                telstr = "{0} {1:10.2f} {2:10.2f} {3:10.2f}".format(
-                    self.name,
-                    self.crs[crs_name]["xx"]["value"],
-                    self.crs[crs_name]["yy"]["value"],
-                    _zz,
+                telstr = (
+                    f"{self.name} {self.crs[crs_name]['xx']['value']:10.2f} "
+                    f"{self.crs[crs_name]['yy']['value']:10.2f} {_zz:10.2f}"
                 )
-            headerstr = "{0} {1} {2} {3}".format(
-                "telescope_name",
-                self.crs[crs_name]["xx"]["name"],
-                self.crs[crs_name]["yy"]["name"],
-                _zz_header,
+            headerstr = (
+                f"telescope_name {self.crs[crs_name]['xx']['name']} "
+                f"{self.crs[crs_name]['yy']['name']} {_zz_header}"
             )
 
             if self.geo_code is not None:
-                telstr += "  {0}".format(self.geo_code)
+                telstr += f"  {self.geo_code}"
                 headerstr += "  geo_code"
             if print_header:
                 print(headerstr)
             print(telstr)
-        except KeyError:
-            self._logger.error("Invalid coordinate system ({})".format(crs_name))
-            raise InvalidCoordSystem
+        except KeyError as e:
+            self._logger.error(f"Invalid coordinate system ({crs_name})")
+            raise InvalidCoordSystem from e
 
     def get_coordinates(self, crs_name, coordinate_field=None):
         """
@@ -168,9 +161,9 @@ def get_coordinates(self, crs_name, coordinate_field=None):
                     self.crs[crs_name]["yy"]["value"] * self.crs[crs_name]["yy"]["unit"],
                     self.crs[crs_name]["zz"]["value"] * self.crs[crs_name]["zz"]["unit"],
                 )
-            except KeyError:
-                self._logger.error("Invalid coordinate system ({})".format(crs_name))
-                raise InvalidCoordSystem
+            except KeyError as e:
+                self._logger.error(f"Invalid coordinate system ({crs_name})")
+                raise InvalidCoordSystem from e
         else:
             try:
                 return (
@@ -178,13 +171,12 @@ def get_coordinates(self, crs_name, coordinate_field=None):
                     self.crs[crs_name]["yy"][coordinate_field],
                     self.crs[crs_name]["zz"][coordinate_field],
                 )
-            except KeyError:
+            except KeyError as e:
                 self._logger.error(
-                    "Invalid coordinate system ({}) or coordinate field ({})".format(
-                        crs_name, coordinate_field
-                    )
+                    f"Invalid coordinate system ({crs_name}) "
+                    f"or coordinate field ({coordinate_field})"
                 )
-                raise InvalidCoordSystem
+                raise InvalidCoordSystem from e
 
     def _get_coordinate_value(self, value, unit):
         """
@@ -198,7 +190,7 @@ def _get_coordinate_value(self, value, unit):
             try:
                 return value.to(unit).value
             except u.UnitsError:
-                self._logger.error("Invalid unit given ({}) for value: {})".format(unit, value))
+                self._logger.error(f"Invalid unit given ({unit}) for value: {value})")
                 raise
 
         return value
@@ -236,9 +228,9 @@ def set_coordinates(self, crs_name, xx, yy, zz=None):
                 self.crs[crs_name]["zz"]["value"] = self._get_coordinate_value(
                     zz, self.crs[crs_name]["zz"]["unit"]
                 )
-        except KeyError:
-            self._logger.error("Invalid coordinate system ({})".format(crs_name))
-            raise InvalidCoordSystem
+        except KeyError as e:
+            self._logger.error(f"Invalid coordinate system ({crs_name})")
+            raise InvalidCoordSystem from e
 
     def get_altitude(self):
         """ "
@@ -254,6 +246,8 @@ def get_altitude(self):
             if _crs["zz"]["value"]:
                 return _crs["zz"]["value"] * u.Unit(_crs["zz"]["unit"])
 
+        return np.nan
+
     def set_altitude(self, tel_altitude):
         """
         Set altitude of an array element. Assume that all coordinate system have same altitude \
@@ -351,9 +345,9 @@ def has_coordinates(self, crs_name, crs_check=False):
         try:
             if not self.crs[crs_name]["crs"] and crs_check:
                 return False
-        except KeyError:
-            self._logger.error("Invalid coordinate system ({})".format(crs_name))
-            raise InvalidCoordSystem
+        except KeyError as e:
+            self._logger.error(f"Invalid coordinate system ({crs_name})")
+            raise InvalidCoordSystem from e
 
         return (
             self.crs[crs_name]["xx"]["value"] is not np.nan
@@ -394,9 +388,9 @@ def has_altitude(self, crs_name=None):
                 self.crs[crs_name]["zz"]["value"] is not np.nan
                 and self.crs[crs_name]["zz"]["value"] is not None
             )
-        except KeyError:
-            self._logger.error("Invalid coordinate system ({})".format(crs_name))
-            raise InvalidCoordSystem
+        except KeyError as e:
+            self._logger.error(f"Invalid coordinate system ({crs_name})")
+            raise InvalidCoordSystem from e
 
     def _set_coordinate_system(self, crs_name, crs_system):
         """
@@ -417,9 +411,9 @@ def _set_coordinate_system(self, crs_name, crs_system):
         """
         try:
             self.crs[crs_name]["crs"] = crs_system
-        except KeyError:
-            self._logger.error("Invalid coordinate system ({})".format(crs_name))
-            raise InvalidCoordSystem
+        except KeyError as e:
+            self._logger.error(f"Invalid coordinate system ({crs_name})")
+            raise InvalidCoordSystem from e
 
     @staticmethod
     @u.quantity_input(tel_altitude=u.m, corsika_obs_level=u.m, corsika_sphere_center=u.m)
@@ -510,9 +504,9 @@ def convert_all(self, crs_local=None, crs_wgs84=None, crs_utm=None):
                     self.set_coordinates(
                         _crs_to_name, _x, _y, _crs_from["zz"]["value"] * _crs_from["zz"]["unit"]
                     )
-        except (InvalidCoordSystem, TypeError):
+        except (InvalidCoordSystem, TypeError) as e:
             self._logger.error("No reference coordinate system defined")
-            raise MissingInputForConvertion
+            raise MissingInputForConvertion from e
 
     @staticmethod
     def _default_coordinate_system_definition():
diff --git a/simtools/model/array_model.py b/simtools/model/array_model.py
index dbf29981b..b70be1ad6 100644
--- a/simtools/model/array_model.py
+++ b/simtools/model/array_model.py
@@ -14,8 +14,6 @@
 class InvalidArrayConfigData(Exception):
     """Exception for invalid array configuration data."""
 
-    pass
-
 
 class ArrayModel:
     """
@@ -45,6 +43,7 @@ def __init__(self, mongo_db_config, label=None, array_config_file=None, array_co
         self.layout = None
         self.layout_name = None
         self.model_version = None
+        self._config_file_path = None
         self.io_handler = io_handler.IOHandler()
         array_config_data = collect_data_from_yaml_or_dict(array_config_file, array_config_data)
         self._load_array_data(array_config_data)
@@ -120,7 +119,7 @@ def run_over_pars(pars, data, parent=None):
                 if pp not in all_keys:
                     key = pp if parent is None else parent + "." + pp
                     msg = (
-                        "Key {} was not found in array_config_data ".format(key)
+                        f"Key {key} was not found in array_config_data "
                         + "- impossible to build array model"
                     )
                     self._logger.error(msg)
@@ -133,8 +132,7 @@ def _set_config_file_directory(self):
         self._config_file_directory = self.io_handler.get_output_directory(self.label, "model")
         if not self._config_file_directory.exists():
             self._config_file_directory.mkdir(parents=True, exist_ok=True)
-            self._logger.info("Creating directory {}".format(self._config_file_directory))
-        return
+            self._logger.info(f"Creating directory {self._config_file_directory}")
 
     def _build_array_model(self, mongo_db_config):
         """
@@ -168,7 +166,7 @@ def _build_array_model(self, mongo_db_config):
             if len(pars_to_change) > 0:
                 _all_pars_to_change[tel.name] = pars_to_change
 
-            self._logger.debug("tel_model_name: {}".format(tel_model_name))
+            self._logger.debug(f"tel_model_name: {tel_model_name}")
 
             # Building the basic models - no pars to change yet
             if tel_model_name not in _all_telescope_model_names:
@@ -184,13 +182,11 @@ def _build_array_model(self, mongo_db_config):
             else:
                 # Telescope name already exists.
                 # Finding the TelescopeModel and copying it.
-                for tel in self._telescope_model:
-                    if tel.name != tel_model_name:
+                for tel_now in self._telescope_model:
+                    if tel_now.name != tel_model_name:
                         continue
-                    self._logger.debug(
-                        "Copying tel model {} already loaded from DB".format(tel.name)
-                    )
-                    tel_model = copy(tel)
+                    self._logger.debug(f"Copying tel model {tel_now.name} already loaded from DB")
+                    tel_model = copy(tel_now)
                     break
 
             self._telescope_model.append(tel_model)
@@ -208,11 +204,8 @@ def _build_array_model(self, mongo_db_config):
                 if tel_data.name not in _all_pars_to_change:
                     continue
                 self._logger.debug(
-                    "Changing {} pars of a {}: {}, ...".format(
-                        len(_all_pars_to_change[tel_data.name]),
-                        tel_data.name,
-                        *_all_pars_to_change[tel_data.name]
-                    )
+                    f"Changing {len(_all_pars_to_change[tel_data.name])} pars of a "
+                    f"{tel_data.name}: {*_all_pars_to_change[tel_data.name],}, ..."
                 )
                 tel_model.change_multiple_parameters(**_all_pars_to_change[tel_data.name])
                 tel_model.set_extra_label(tel_data.name)
@@ -250,46 +243,46 @@ def _proccess_single_telescope(data):
                 pars_to_change = {k: v for (k, v) in data.items() if k != "name"}
                 self._logger.debug(
                     "Grabbing tel data as dict - "
-                    + "name: {}, ".format(tel_name)
-                    + "{} pars to change".format(len(pars_to_change))
+                    f"name: {tel_name}, "
+                    f"{len(pars_to_change)} pars to change"
                 )
                 return tel_name, pars_to_change
-            elif isinstance(data, str):
+            if isinstance(data, str):
                 # Case 1: data is string (only name)
                 tel_name = tel_size + "-" + data
                 return tel_name, dict()
-            else:
-                # Case 2: data has a wrong type
-                msg = "ArrayConfig has wrong input for a telescope"
-                self._logger.error(msg)
-                raise InvalidArrayConfigData(msg)
+
+            # Case 2: data has a wrong type
+            msg = "ArrayConfig has wrong input for a telescope"
+            self._logger.error(msg)
+            raise InvalidArrayConfigData(msg)
 
         if tel_name in self._array_config_data.keys():
             # Specific info for this telescope
             return _proccess_single_telescope(self._array_config_data[tel_name])
-        else:
-            # Checking if default option exists in array_config_data
-            not_contains_default_key = (
-                "default" not in self._array_config_data.keys()
-                or tel_size not in self._array_config_data["default"].keys()
-            )
 
-            if not_contains_default_key:
-                msg = (
-                    "default option was not given in array_config_data "
-                    + "for the telescope {}".format(tel_name)
-                )
-                self._logger.error(msg)
-                raise InvalidArrayConfigData(msg)
+        # Checking if default option exists in array_config_data
+        not_contains_default_key = (
+            "default" not in self._array_config_data.keys()
+            or tel_size not in self._array_config_data["default"].keys()
+        )
+
+        if not_contains_default_key:
+            msg = (
+                "default option was not given in array_config_data "
+                + f"for the telescope {tel_name}"
+            )
+            self._logger.error(msg)
+            raise InvalidArrayConfigData(msg)
 
-            # Grabbing the default option
-            return _proccess_single_telescope(self._array_config_data["default"][tel_size])
+        # Grabbing the default option
+        return _proccess_single_telescope(self._array_config_data["default"][tel_size])
 
     def print_telescope_list(self):
         """Print out the list of telescopes for quick inspection."""
 
         for tel_data, tel_model in zip(self.layout, self._telescope_model):
-            print("Name: {}\t Model: {}".format(tel_data.name, tel_model.name))
+            print(f"Name: {tel_data.name}\t Model: {tel_model.name}")
 
     def export_simtel_telescope_config_files(self):
         """
@@ -302,11 +295,11 @@ def export_simtel_telescope_config_files(self):
                 "_" + tel_model.extra_label if tel_model.extra_label != "" else ""
             )
             if name not in exported_models:
-                self._logger.debug("Exporting config file for tel {}".format(name))
+                self._logger.debug(f"Exporting config file for tel {name}")
                 tel_model.export_config_file()
                 exported_models.append(name)
             else:
-                self._logger.debug("Config file for tel {} already exists - skipping".format(name))
+                self._logger.debug(f"Config file for tel {name} already exists - skipping")
 
         self._telescope_model_files_exported = True
 
@@ -322,7 +315,7 @@ def export_simtel_array_config_file(self):
         self._config_file_path = self._config_file_directory.joinpath(config_file_name)
 
         # Writing parameters to the file
-        self._logger.info("Writing array config file into {}".format(self._config_file_path))
+        self._logger.info(f"Writing array config file into {self._config_file_path}")
         simtel_writer = SimtelConfigWriter(
             site=self.site,
             layout_name=self.layout_name,
diff --git a/simtools/model/camera.py b/simtools/model/camera.py
index e8999e78d..7e86c0b29 100644
--- a/simtools/model/camera.py
+++ b/simtools/model/camera.py
@@ -64,8 +64,6 @@ def __init__(self, telescope_model_name, camera_config_file, focal_length):
         # Initialize an empty list of edge pixels, to be calculated only when necessary.
         self._edge_pixel_indices = None
 
-        return
-
     @staticmethod
     def read_pixel_list(camera_config_file):
         """
@@ -89,7 +87,6 @@ def read_pixel_list(camera_config_file):
         clockwise by 30 degrees with respect to those denoted as 1.
         """
 
-        dat_file = open(camera_config_file, "r")
         pixels = dict()
         pixels["pixel_diameter"] = 9999
         pixels["pixel_shape"] = 9999
@@ -101,37 +98,39 @@ def read_pixel_list(camera_config_file):
         pixels["y"] = list()
         pixels["pix_id"] = list()
         pixels["pix_on"] = list()
-        for line in dat_file:
-            pix_info = line.split()
-            if line.startswith("PixType"):
-                pixels["pixel_shape"] = int(pix_info[5].strip())
-                pixels["pixel_diameter"] = float(pix_info[6].strip())
-                pixels["lightguide_efficiency_angle_file"] = pix_info[8].strip().replace('"', "")
-                if len(pix_info) > 9:
-                    pixels["lightguide_efficiency_wavelength_file"] = (
-                        pix_info[9].strip().replace('"', "")
+
+        with open(camera_config_file, "r") as dat_file:
+            for line in dat_file:
+                pix_info = line.split()
+                if line.startswith("PixType"):
+                    pixels["pixel_shape"] = int(pix_info[5].strip())
+                    pixels["pixel_diameter"] = float(pix_info[6].strip())
+                    pixels["lightguide_efficiency_angle_file"] = (
+                        pix_info[8].strip().replace('"', "")
                     )
-            if line.startswith("Rotate"):
-                pixels["rotate_angle"] = np.deg2rad(float(pix_info[1].strip()))
-            if line.startswith("Pixel"):
-                pixels["x"].append(float(pix_info[3].strip()))
-                pixels["y"].append(float(pix_info[4].strip()))
-                pixels["pix_id"].append(int(pix_info[1].strip()))
-                if len(pix_info) > 9:
-                    if int(pix_info[9].strip()) != 0:
-                        pixels["pix_on"].append(True)
+                    if len(pix_info) > 9:
+                        pixels["lightguide_efficiency_wavelength_file"] = (
+                            pix_info[9].strip().replace('"', "")
+                        )
+                if line.startswith("Rotate"):
+                    pixels["rotate_angle"] = np.deg2rad(float(pix_info[1].strip()))
+                if line.startswith("Pixel"):
+                    pixels["x"].append(float(pix_info[3].strip()))
+                    pixels["y"].append(float(pix_info[4].strip()))
+                    pixels["pix_id"].append(int(pix_info[1].strip()))
+                    if len(pix_info) > 9:
+                        if int(pix_info[9].strip()) != 0:
+                            pixels["pix_on"].append(True)
+                        else:
+                            pixels["pix_on"].append(False)
                     else:
-                        pixels["pix_on"].append(False)
-                else:
-                    pixels["pix_on"].append(True)
+                        pixels["pix_on"].append(True)
 
         if pixels["pixel_diameter"] == 9999:
-            raise ValueError(
-                "Could not read the pixel diameter" " from {} file".format(camera_config_file)
-            )
+            raise ValueError(f"Could not read the pixel diameter from {camera_config_file} file")
         if pixels["pixel_shape"] not in [1, 2, 3]:
             raise ValueError(
-                "Pixel shape in {} unrecognized " "(has to be 1, 2 or 3)".format(camera_config_file)
+                f"Pixel shape in {camera_config_file} unrecognized (has to be 1, 2 or 3)"
             )
 
         return pixels
@@ -167,7 +166,7 @@ def _rotate_pixels(self, pixels):
         # To get the camera for an observer facing the camera, need to rotate by 90 degrees.
         rotate_angle += np.deg2rad(90)
 
-        self._logger.debug("Rotating pixels by {}".format(np.rad2deg(rotate_angle)))
+        self._logger.debug(f"Rotating pixels by {np.rad2deg(rotate_angle)}")
 
         if rotate_angle != 0:
             for i_pix, xy_pix_pos in enumerate(zip(pixels["x"], pixels["y"])):
@@ -485,8 +484,8 @@ def get_neighbour_pixels(self, pixels=None):
             if pixels is None:
                 pixels = self._pixels
             return self._calc_neighbour_pixels(pixels)
-        else:
-            return self._neighbours
+
+        return self._neighbours
 
     def _calc_edge_pixels(self, pixels, neighbours):
         """
@@ -544,17 +543,17 @@ def get_edge_pixels(self, pixels=None, neighbours=None):
             if neighbours is None:
                 neighbours = self.get_neighbour_pixels()
             return self._calc_edge_pixels(pixels, neighbours)
-        else:
-            return self._edge_pixel_indices
 
-    def _plot_axes_def(self, plt, rotate_angle):
+        return self._edge_pixel_indices
+
+    def _plot_axes_def(self, plot, rotate_angle):
         """
         Plot three axes definitions on the pyplot.plt instance provided. The three axes are Alt/Az,\
         the camera coordinate system and the original coordinate system the pixel list was provided.
 
         Parameters
         ----------
-        plt: pyplot.plt instance
+        plot: pyplot.plt instance
             A pyplot.plt instance where to add the axes definitions.
         rotate_angle: float
             The rotation angle applied
@@ -584,7 +583,7 @@ def _plot_axes_def(self, plt, rotate_angle):
             "ec": "black",
             "invert_yaxis": invert_yaxis,
         }
-        self._plot_one_axis_def(plt, **kwargs)
+        self._plot_one_axis_def(plot, **kwargs)
 
         x_title = r"$x_{\!cam}$"
         y_title = r"$y_{\!cam}$"
@@ -599,7 +598,7 @@ def _plot_axes_def(self, plt, rotate_angle):
             "ec": "blue",
             "invert_yaxis": invert_yaxis,
         }
-        self._plot_one_axis_def(plt, **kwargs)
+        self._plot_one_axis_def(plot, **kwargs)
 
         x_title = "Alt"
         y_title = "Az"
@@ -614,18 +613,16 @@ def _plot_axes_def(self, plt, rotate_angle):
             "ec": "red",
             "invert_yaxis": invert_yaxis,
         }
-        self._plot_one_axis_def(plt, **kwargs)
-
-        return
+        self._plot_one_axis_def(plot, **kwargs)
 
     @staticmethod
-    def _plot_one_axis_def(plt, **kwargs):
+    def _plot_one_axis_def(plot, **kwargs):
         """
         Plot an axis on the pyplot.plt instance provided.
 
         Parameters
         ----------
-        plt: pyplot.plt instance
+        plot: pyplot.plt instance
             A pyplot.plt instance where to add the axes definitions.
         **kwargs: dict
              x_title: str
@@ -659,7 +656,7 @@ def _plot_one_axis_def(plt, **kwargs):
         x_text2 = x_pos + sign * r * np.cos(np.pi / 2.0 + kwargs["rotate_angle"])
         y_text2 = y_pos + r * np.sin(np.pi / 2.0 + kwargs["rotate_angle"])
 
-        plt.gca().annotate(
+        plot.gca().annotate(
             x_title,
             xy=(x_pos, y_pos),
             xytext=(x_text1, y_text1),
@@ -672,7 +669,7 @@ def _plot_one_axis_def(plt, **kwargs):
             ),
         )
 
-        plt.gca().annotate(
+        plot.gca().annotate(
             y_title,
             xy=(x_pos, y_pos),
             xytext=(x_text2, y_text2),
@@ -685,8 +682,6 @@ def _plot_one_axis_def(plt, **kwargs):
             ),
         )
 
-        return
-
     def plot_pixel_layout(self, camera_in_sky_coor=False, pixels_id_to_print=50):
         """
         Plot the pixel layout for an observer facing the camera. Including in the plot edge pixels,\
@@ -699,7 +694,7 @@ def plot_pixel_layout(self, camera_in_sky_coor=False, pixels_id_to_print=50):
             Figure with the pixel layout.
         """
 
-        self._logger.info("Plotting the {} camera".format(self._telescope_model_name))
+        self._logger.info(f"Plotting the {self._telescope_model_name} camera")
 
         fig, ax = plt.subplots()
         plt.gcf().set_size_inches(8, 8)
@@ -803,7 +798,7 @@ def plot_pixel_layout(self, camera_in_sky_coor=False, pixels_id_to_print=50):
         plt.xlabel("Horizontal scale [cm]", fontsize=18, labelpad=0)
         plt.ylabel("Vertical scale [cm]", fontsize=18, labelpad=0)
         ax.set_title(
-            "Pixels layout in {0:s} camera".format(self._telescope_model_name),
+            f"Pixels layout in {self._telescope_model_name:s} camera",
             fontsize=15,
             y=1.02,
         )
@@ -828,7 +823,7 @@ def plot_pixel_layout(self, camera_in_sky_coor=False, pixels_id_to_print=50):
         ax.text(
             0.02,
             0.96,
-            r"$f_{\mathrm{eff}}$ = " + "{0:.3f} cm".format(self._focal_length),
+            r"$f_{\mathrm{eff}}$ = " + f"{self._focal_length:.3f} cm",
             transform=ax.transAxes,
             color="black",
             fontsize=12,
@@ -836,7 +831,7 @@ def plot_pixel_layout(self, camera_in_sky_coor=False, pixels_id_to_print=50):
         ax.text(
             0.02,
             0.92,
-            "Avg. edge radius = {0:.3f} cm".format(r_edge_avg),
+            f"Avg. edge radius = {r_edge_avg:.3f} cm",
             transform=ax.transAxes,
             color="black",
             fontsize=12,
@@ -844,7 +839,7 @@ def plot_pixel_layout(self, camera_in_sky_coor=False, pixels_id_to_print=50):
         ax.text(
             0.02,
             0.88,
-            "FoV = {0:.3f} deg".format(fov),
+            f"FoV = {fov:.3f} deg",
             transform=ax.transAxes,
             color="black",
             fontsize=12,
diff --git a/simtools/model/mirrors.py b/simtools/model/mirrors.py
index 96ed1e751..357869f78 100644
--- a/simtools/model/mirrors.py
+++ b/simtools/model/mirrors.py
@@ -8,8 +8,6 @@
 class InvalidMirrorListFile(Exception):
     """Exception for invalid mirror list file."""
 
-    pass
-
 
 class Mirrors:
     """
@@ -60,11 +58,11 @@ def _read_mirror_list_from_ecsv(self):
         # TODO - temporary hard wired geopars - should come from DB
         self.diameter = 120
         self.shape = 1
-        self._logger.debug("Shape = {}".format(self.shape))
-        self._logger.debug("Diameter = {}".format(self.diameter))
+        self._logger.debug(f"Shape = {self.shape}")
+        self._logger.debug(f"Diameter = {self.diameter}")
 
         _mirror_table = Table.read(self._mirror_list_file, format="ascii.ecsv")
-        self._logger.debug("Reading mirror properties from {}".format(self._mirror_list_file))
+        self._logger.debug(f"Reading mirror properties from {self._mirror_list_file}")
         try:
             self._mirrors["flen"] = list(_mirror_table["mirror_panel_radius"].to("cm").value / 2.0)
             self.number_of_mirrors = len(self._mirrors["flen"])
@@ -75,9 +73,7 @@ def _read_mirror_list_from_ecsv(self):
             self._mirrors["shape"] = [self.shape] * self.number_of_mirrors
         except KeyError:
             self._logger.debug(
-                "Missing column for mirror panel focal length (flen) in {}".format(
-                    self._mirror_list_file
-                )
+                f"Missing column for mirror panel focal length (flen) in {self._mirror_list_file}"
             )
 
         if self.number_of_mirrors == 0:
@@ -113,8 +109,8 @@ def _read_mirror_list_from_sim_telarray(self):
                     self.diameter = float(line[2])
                     self.shape = int(line[4])
                     collect_geo_pars = False
-                    self._logger.debug("Shape = {}".format(self.shape))
-                    self._logger.debug("Diameter = {}".format(self.diameter))
+                    self._logger.debug(f"Shape = {self.shape}")
+                    self._logger.debug(f"Diameter = {self.diameter}")
 
                 self._mirrors["number"].append(mirror_counter)
                 self._mirrors["pos_x"].append(float(line[0]))
diff --git a/simtools/model/model_utils.py b/simtools/model/model_utils.py
index 33f61b3d4..6ea30c72b 100644
--- a/simtools/model/model_utils.py
+++ b/simtools/model/model_utils.py
@@ -57,9 +57,9 @@ def compute_telescope_transmission(pars, off_axis):
     _deg_to_rad = math.pi / 180.0
     if pars[1] == 0:
         return pars[0]
-    else:
-        t = math.sin(off_axis * _deg_to_rad) / (pars[3] * _deg_to_rad)
-        return pars[0] / (1.0 + pars[2] * t ** pars[4])
+
+    t = math.sin(off_axis * _deg_to_rad) / (pars[3] * _deg_to_rad)
+    return pars[0] / (1.0 + pars[2] * t ** pars[4])
 
 
 def get_camera_name(telescope_model_name):
@@ -104,7 +104,7 @@ def get_camera_name(telescope_model_name):
         _logger.error("Invalid telescope name - please validate it first")
 
     camera_name = names.validate_camera_name(camera_name)
-    _logger.debug("Camera name - {}".format(camera_name))
+    _logger.debug(f"Camera name - {camera_name}")
     return camera_name
 
 
@@ -144,10 +144,10 @@ def is_two_mirror_telescope(telescope_model_name):
     tel_class, tel_type = names.split_telescope_model_name(telescope_model_name)
     if tel_class == "SST":
         # Only 1M is False
-        return False if "1M" in tel_type else True
-    elif tel_class == "SCT":
+        return "1M" not in tel_type
+    if tel_class == "SCT":
         # SCT always two mirrors
         return True
-    else:
-        # All MSTs and LSTs
-        return False
+
+    # All MSTs and LSTs
+    return False
diff --git a/simtools/model/telescope_model.py b/simtools/model/telescope_model.py
index ce6a4559f..4b9555e07 100644
--- a/simtools/model/telescope_model.py
+++ b/simtools/model/telescope_model.py
@@ -21,8 +21,6 @@
 class InvalidParameter(Exception):
     """Exception for invalid parameter."""
 
-    pass
-
 
 class TelescopeModel:
     """
@@ -63,6 +61,13 @@ def __init__(
         self.model_version = names.validate_model_version_name(model_version)
         self.label = label
         self._extra_label = None
+        self._added_parameter_files = None
+        self._single_mirror_list_file_paths = None
+        self.simtel_config_writer = None
+        self._mirrors = None
+        self._reference_data = None
+        self._derived = None
+        self._camera = None
 
         self.io_handler = io_handler.IOHandler()
         self.mongo_db_config = mongo_db_config
@@ -80,7 +85,7 @@ def mirrors(self):
         """
         Load the mirror information if the class instance hasn't done it yet.
         """
-        if not hasattr(self, "_mirrors"):
+        if self._mirrors is None:
             self._load_mirrors()
         return self._mirrors
 
@@ -89,7 +94,7 @@ def camera(self):
         """
         Load the camera information if the class instance hasn't done it yet.
         """
-        if not hasattr(self, "_camera"):
+        if self._camera is None:
             self._load_camera()
         return self._camera
 
@@ -98,7 +103,7 @@ def reference_data(self):
         """
         Load the reference data information if the class instance hasn't done it yet.
         """
-        if not hasattr(self, "_reference_data"):
+        if self._reference_data is None:
             self._load_reference_data()
         return self._reference_data
 
@@ -107,7 +112,7 @@ def derived(self):
         """
         Load the derived values and export them if the class instance hasn't done it yet.
         """
-        if not hasattr(self, "_derived"):
+        if self._derived is None:
             self._load_derived_values()
             self.export_derived_files()
         return self._derived
@@ -186,9 +191,9 @@ def _process_line(words):
                 words = line.split()
                 if len(words) == 0:
                     continue
-                elif "%" in words[0] or "echo" in words:
+                if "%" in words[0] or "echo" in words:
                     continue
-                elif "#" not in line and len(words) > 0:
+                if "#" not in line and len(words) > 0:
                     par, value = _process_line(words)
                     parameters[par] = value
 
@@ -286,10 +291,10 @@ def get_parameter(self, par_name):
             pass  # search in the derived parameters
         try:
             return self.derived[par_name]
-        except KeyError:
+        except KeyError as e:
             msg = f"Parameter {par_name} was not found in the model"
             self._logger.error(msg)
-            raise InvalidParameter(msg)
+            raise InvalidParameter(msg) from e
 
     def get_parameter_value(self, par_name):
         """
@@ -359,16 +364,16 @@ def add_parameter(self, par_name, value, is_file=False, is_aplicable=True):
             If an existing parameter is tried to be added.
         """
         if par_name in self._parameters:
-            msg = "Parameter {} already in the model, use change_parameter instead".format(par_name)
+            msg = f"Parameter {par_name} already in the model, use change_parameter instead"
             self._logger.error(msg)
             raise InvalidParameter(msg)
-        else:
-            self._logger.info("Adding {}={} to the model".format(par_name, value))
-            self._parameters[par_name] = dict()
-            self._parameters[par_name]["Value"] = value
-            self._parameters[par_name]["Type"] = type(value)
-            self._parameters[par_name]["Applicable"] = is_aplicable
-            self._parameters[par_name]["File"] = is_file
+
+        self._logger.info(f"Adding {par_name}={value} to the model")
+        self._parameters[par_name] = dict()
+        self._parameters[par_name]["Value"] = value
+        self._parameters[par_name]["Type"] = type(value)
+        self._parameters[par_name]["Applicable"] = is_aplicable
+        self._parameters[par_name]["File"] = is_file
 
         self._is_config_file_up_to_date = False
         if is_file:
@@ -392,35 +397,35 @@ def change_parameter(self, par_name, value):
             If the parameter to be changed does not exist in this model.
         """
         if par_name not in self._parameters:
-            msg = "Parameter {} not in the model, use add_parameters instead".format(par_name)
+            msg = f"Parameter {par_name} not in the model, use add_parameters instead"
             self._logger.error(msg)
             raise InvalidParameter(msg)
-        else:
-            type_of_par_name = locate(self._parameters[par_name]["Type"])
-            if not isinstance(value, type_of_par_name):
-                self._logger.warning(
-                    f"The type of the provided value ({value}, {type(value)}) "
-                    f"is different from the type of {par_name} "
-                    f"({self._parameters[par_name]['Type']}). "
-                    f"Attempting to cast to the correct type."
-                )
-                try:
-                    value = type_of_par_name(value)
-                except ValueError:
-                    self._logger.error(
-                        f"Could not cast {value} to {self._parameters[par_name]['Type']}."
-                    )
-                    raise
-
-            self._logger.debug(
-                f"Changing parameter {par_name} "
-                f"from {self._parameters[par_name]['Value']} to {value}"
+
+        type_of_par_name = locate(self._parameters[par_name]["Type"])
+        if not isinstance(value, type_of_par_name):
+            self._logger.warning(
+                f"The type of the provided value ({value}, {type(value)}) "
+                f"is different from the type of {par_name} "
+                f"({self._parameters[par_name]['Type']}). "
+                f"Attempting to cast to the correct type."
             )
-            self._parameters[par_name]["Value"] = value
+            try:
+                value = type_of_par_name(value)
+            except ValueError:
+                self._logger.error(
+                    f"Could not cast {value} to {self._parameters[par_name]['Type']}."
+                )
+                raise
+
+        self._logger.debug(
+            f"Changing parameter {par_name} "
+            f"from {self._parameters[par_name]['Value']} to {value}"
+        )
+        self._parameters[par_name]["Value"] = value
 
-            # In case parameter is a file, the model files will be outdated
-            if self._parameters[par_name]["File"]:
-                self._is_exported_model_files_up_to_date = False
+        # In case parameter is a file, the model files will be outdated
+        if self._parameters[par_name]["File"]:
+            self._is_exported_model_files_up_to_date = False
 
         self._is_config_file_up_to_date = False
 
@@ -463,10 +468,10 @@ def remove_parameters(self, *args):
         """
         for par in args:
             if par in self._parameters:
-                self._logger.info("Removing parameter {}".format(par))
+                self._logger.info(f"Removing parameter {par}")
                 del self._parameters[par]
             else:
-                msg = "Could not remove parameter {} because it does not exist".format(par)
+                msg = f"Could not remove parameter {par} because it does not exist"
                 self._logger.error(msg)
                 raise InvalidParameter(msg)
         self._is_config_file_up_to_date = False
@@ -482,7 +487,7 @@ def add_parameter_file(self, par_name, file_path):
         file_path: str
             Path of the file to be added to the config file directory.
         """
-        if not hasattr(self, "_added_parameter_files"):
+        if self._added_parameter_files is None:
             self._added_parameter_files = list()
         self._added_parameter_files.append(par_name)
         shutil.copy(file_path, self._config_file_directory)
@@ -493,7 +498,7 @@ def export_model_files(self):
 
         # Removing parameter files added manually (which are not in DB)
         pars_from_db = copy(self._parameters)
-        if hasattr(self, "_added_parameter_files"):
+        if self._added_parameter_files is not None:
             for par in self._added_parameter_files:
                 pars_from_db.pop(par)
 
@@ -503,7 +508,7 @@ def export_model_files(self):
     def print_parameters(self):
         """Print parameters and their values for debugging purposes."""
         for par, info in self._parameters.items():
-            print("{} = {}".format(par, info["Value"]))
+            print(f"{par} = {info['Value']}")
 
     def export_config_file(self):
         """Export the config file used by sim_telarray."""
@@ -522,12 +527,12 @@ def export_derived_files(self):
         """Write to disk a file from the derived values DB."""
 
         db = db_handler.DatabaseHandler(mongo_db_config=self.mongo_db_config)
-        for par_now in self.derived:
-            if self.derived[par_now]["File"]:
+        for par_now in self.derived.values():
+            if par_now["File"]:
                 db.export_file_db(
                     db_name=db.DB_DERIVED_VALUES,
                     dest=self.io_handler.get_output_directory(self.label, "derived"),
-                    file_name=self.derived[par_now]["Value"],
+                    file_name=par_now["Value"],
                 )
 
     def get_config_file(self, no_export=False):
@@ -584,8 +589,8 @@ def get_telescope_transmission_parameters(self):
         telescope_transmission = self.get_parameter_value("telescope_transmission")
         if isinstance(telescope_transmission, str):
             return [float(v) for v in self.get_parameter_value("telescope_transmission").split()]
-        else:
-            return [float(telescope_transmission), 0, 0, 0]
+
+        return [float(telescope_transmission), 0, 0, 0]
 
     def export_single_mirror_list_file(self, mirror_number, set_focal_length_to_zero):
         """
@@ -600,12 +605,12 @@ def export_single_mirror_list_file(self, mirror_number, set_focal_length_to_zero
         """
         if mirror_number > self.mirrors.number_of_mirrors:
             logging.error("mirror_number > number_of_mirrors")
-            return None
+            return
 
         file_name = names.simtel_single_mirror_list_file_name(
             self.site, self.name, self.model_version, mirror_number, self.label
         )
-        if not hasattr(self, "_single_mirror_list_file_path"):
+        if self._single_mirror_list_file_paths is None:
             self._single_mirror_list_file_paths = dict()
         self._single_mirror_list_file_paths[mirror_number] = self._config_file_directory.joinpath(
             file_name
@@ -695,7 +700,7 @@ def _load_camera(self):
         )
 
     def _load_simtel_config_writer(self):
-        if not hasattr(self, "simtel_config_writer"):
+        if self.simtel_config_writer is None:
             self.simtel_config_writer = SimtelConfigWriter(
                 site=self.site,
                 telescope_model_name=self.name,
@@ -718,7 +723,7 @@ def is_file_2D(self, par):
             True if the file is a 2D map type, False otherwise.
         """
         if not self.has_parameter(par):
-            logging.error("Parameter {} does not exist".format(par))
+            logging.error(f"Parameter {par} does not exist")
             return False
 
         file_name = self.get_parameter_value(par)
@@ -745,13 +750,15 @@ def read_two_dim_wavelength_angle(self, file_name):
         """
 
         _file = self.get_config_directory().joinpath(file_name)
+        line_to_start_from = 0
         with open(_file, "r") as f:
             for i_line, line in enumerate(f):
                 if line.startswith("ANGLE"):
                     degrees = np.array(line.strip().split("=")[1].split(), dtype=np.float16)
+                    line_to_start_from = i_line + 1
                     break  # The rest can be read with np.loadtxt
 
-        _data = np.loadtxt(_file, skiprows=i_line + 1)
+        _data = np.loadtxt(_file, skiprows=line_to_start_from)
 
         return {
             "Wavelength": _data[:, 0],
diff --git a/simtools/psf_analysis.py b/simtools/psf_analysis.py
index 0cd684874..649313d36 100644
--- a/simtools/psf_analysis.py
+++ b/simtools/psf_analysis.py
@@ -41,6 +41,9 @@ def __init__(self, focal_length=None, total_scattered_area=None):
 
         self._logger = logging.getLogger(__name__)
 
+        self._total_photons = None
+        self._number_of_detected_photons = None
+        self._effective_area = None
         self.photon_pos_x = list()
         self.photon_pos_y = list()
         self.photon_r = list()
@@ -69,7 +72,7 @@ def read_photon_list_from_simtel_file(self, file):
             If photon positions X and Y are not compatible or are empty.
 
         """
-        self._logger.info("Reading sim_telarray file {}".format(file))
+        self._logger.info(f"Reading sim_telarray file {file}")
         self._total_photons = 0
         with open(file, "r") as f:
             for line in f:
@@ -125,8 +128,8 @@ def _process_simtel_line(self, line):
             elif total_area_in_file != self._total_area:
                 self._logger.warning(
                     "Conflicting value of the total area found"
-                    + " {} != {}".format(self._total_area, total_area_in_file)
-                    + " - Keeping the original value"
+                    f" {self._total_area} != {total_area_in_file}"
+                    " - Keeping the original value"
                 )
             else:
                 # Do nothing - Keep the original value of _total_area
@@ -151,9 +154,9 @@ def get_effective_area(self):
         """
         if "_effective_area" in self.__dict__ and self._effective_area is not None:
             return self._effective_area
-        else:
-            self._logger.error("Effective Area could not be calculated")
-            return None
+
+        self._logger.error("Effective Area could not be calculated")
+        return None
 
     def set_effective_area(self, value):
         """
@@ -238,7 +241,7 @@ def _find_psf(self, fraction):
             Diameter of the circular container with a certain fraction of the photons.
 
         """
-        self._logger.debug("Finding PSF for fraction = {}".format(fraction))
+        self._logger.debug(f"Finding PSF for fraction = {fraction}")
 
         x_pos_sq = [i**2 for i in self.photon_pos_x]
         y_pos_sq = [i**2 for i in self.photon_pos_y]
@@ -268,9 +271,9 @@ def _find_psf(self, fraction):
         if found_radius:
             # Diameter = 2 * radius
             return 2 * current_radius
-        else:
-            self._logger.warning("Could not find PSF efficiently - trying by scanning")
-            return 2 * self._find_radius_by_scanning(target_number, radius_sig)
+
+        self._logger.warning("Could not find PSF efficiently - trying by scanning")
+        return 2 * self._find_radius_by_scanning(target_number, radius_sig)
 
     def _find_radius_by_scanning(self, target_number, radius_sig):
         """
@@ -311,7 +314,7 @@ def scan(dr, rad_min, rad_max):
             found_radius = False
             while not found_radius:
                 s0, s1 = self._sum_photons_in_radius(r0), self._sum_photons_in_radius(r1)
-                if s0 < target_number and s1 > target_number:
+                if s0 < target_number < s1:
                     found_radius = True
                     break
                 if r1 > rad_max:
@@ -320,9 +323,9 @@ def scan(dr, rad_min, rad_max):
                 r1 += dr
             if found_radius:
                 return (r0 + r1) / 2, r0, r1
-            else:
-                self._logger.error("Could not find PSF by scanning")
-                raise RuntimeError
+
+            self._logger.error("Could not find PSF by scanning")
+            raise RuntimeError
 
         # Run scan few times with smaller dr to optimize search.
         # Step 0
diff --git a/simtools/ray_tracing.py b/simtools/ray_tracing.py
index 5335091d3..104a7abf6 100644
--- a/simtools/ray_tracing.py
+++ b/simtools/ray_tracing.py
@@ -101,6 +101,8 @@ def __init__(
         else:
             self._source_distance = self.config.source_distance
 
+        self._psf_images = None
+        self._results = None
         self._has_results = False
 
         # Results file
@@ -136,22 +138,22 @@ def from_kwargs(cls, **kwargs):
                 "label",
                 "simtel_source_path",
             ],
-            **kwargs
+            **kwargs,
         )
         return cls(**args, config_data=config_data)
 
     def __repr__(self):
-        return "RayTracing(label={})\n".format(self.label)
+        return f"RayTracing(label={self.label})\n"
 
     def _validate_telescope_model(self, tel):
         """Validate TelescopeModel"""
         if isinstance(tel, TelescopeModel):
             self._logger.debug("RayTracing contains a valid TelescopeModel")
             return tel
-        else:
-            msg = "Invalid TelescopeModel"
-            self._logger.error(msg)
-            raise ValueError(msg)
+
+        msg = "Invalid TelescopeModel"
+        self._logger.error(msg)
+        raise ValueError(msg)
 
     def simulate(self, test=False, force=False):
         """
@@ -168,9 +170,7 @@ def simulate(self, test=False, force=False):
         for this_off_axis in self.config.off_axis_angle:
             for this_mirror in all_mirrors:
                 self._logger.info(
-                    "Simulating RayTracing for off_axis={}, mirror={}".format(
-                        this_off_axis, this_mirror
-                    )
+                    f"Simulating RayTracing for off_axis={this_off_axis}, mirror={this_mirror}"
                 )
                 simtel = SimtelRunnerRayTracing(
                     simtel_source_path=self._simtel_source_path,
@@ -236,9 +236,9 @@ def analyze(
         all_mirrors = self._mirror_numbers if self.config.single_mirror_mode else [0]
         for this_off_axis in self.config.off_axis_angle:
             for this_mirror in all_mirrors:
-                self._logger.debug("Analyzing RayTracing for off_axis={}".format(this_off_axis))
+                self._logger.debug(f"Analyzing RayTracing for off_axis={this_off_axis}")
                 if self.config.single_mirror_mode:
-                    self._logger.debug("mirror_number={}".format(this_mirror))
+                    self._logger.debug(f"mirror_number={this_mirror}")
 
                 photons_file_name = names.ray_tracing_file_name(
                     self._telescope_model.site,
@@ -324,22 +324,21 @@ def _process_rx(self, file, containment_fraction=0.8):
 
         try:
             with open(file) as _stdin:
-                rx_output = subprocess.Popen(
+                rx_output = subprocess.Popen(  # pylint: disable=consider-using-with
                     shlex.split(
-                        "{}/sim_telarray/bin/rx -f {:.2f} -v".format(
-                            self._simtel_source_path, containment_fraction
-                        )
+                        f"{self._simtel_source_path}/sim_telarray/bin/rx "
+                        f"-f {containment_fraction:.2f} -v"
                     ),
                     stdin=_stdin,
                     stdout=subprocess.PIPE,
                 ).communicate()[0]
         except FileNotFoundError:
-            self._logger.error("Photon list file not found: {}".format(file))
+            self._logger.error(f"Photon list file not found: {file}")
             raise
         try:
             rx_output = rx_output.splitlines()[-1:][0].split()
         except IndexError:
-            self._logger.error("Invalid output from rx: {}".format(rx_output))
+            self._logger.error(f"Invalid output from rx: {rx_output}")
             raise
         containment_diameter_cm = 2 * float(rx_output[0])
         x_mean = float(rx_output[1])
@@ -352,7 +351,7 @@ def export_results(self):
         if not self._has_results:
             self._logger.error("Cannot export results because it does not exist")
         else:
-            self._logger.info("Exporting results to {}".format(self._file_results))
+            self._logger.info(f"Exporting results to {self._file_results}")
             astropy.io.ascii.write(self._results, self._file_results, format="ecsv", overwrite=True)
 
     def _read_results(self):
@@ -383,9 +382,9 @@ def plot(self, key, save=False, **kwargs):
             self._logger.error(msg)
             raise KeyError(msg)
 
-        self._logger.info("Plotting {} vs off-axis angle".format(key))
+        self._logger.info(f"Plotting {key} vs off-axis angle")
 
-        plt = visualize.plot_table(
+        plot = visualize.plot_table(
             self._results["Off-axis angle", key], self.YLABEL[key], no_legend=True, **kwargs
         )
 
@@ -400,8 +399,8 @@ def plot(self, key, save=False, **kwargs):
             )
             self._output_directory.joinpath("figures").mkdir(exist_ok=True)
             plot_file = self._output_directory.joinpath("figures").joinpath(plot_file_name)
-            self._logger.info("Saving fig in {}".format(plot_file))
-            plt.savefig(plot_file)
+            self._logger.info(f"Saving fig in {plot_file}")
+            plot.savefig(plot_file)
 
     def plot_histogram(self, key, **kwargs):
         """
diff --git a/simtools/simtel/simtel_config_writer.py b/simtools/simtel/simtel_config_writer.py
index 52495ed77..a722b4c38 100644
--- a/simtools/simtel/simtel_config_writer.py
+++ b/simtools/simtel/simtel_config_writer.py
@@ -78,7 +78,7 @@ def write_telescope_config_file(self, config_file_path, parameters):
 
             file.write("#ifdef TELESCOPE\n")
             file.write(
-                "   echo Configuration for {}".format(self._telescope_model_name)
+                f"   echo Configuration for {self._telescope_model_name}"
                 + " - TELESCOPE $(TELESCOPE)\n"
             )
             file.write("#endif\n\n")
@@ -87,7 +87,7 @@ def write_telescope_config_file(self, config_file_path, parameters):
                 if par in self.PARS_NOT_TO_WRITE:
                     continue
                 value = parameters[par]["Value"]
-                file.write("{} = {}\n".format(par, value))
+                file.write(f"{par} = {value}\n")
 
     def write_array_config_file(self, config_file_path, layout, telescope_model, site_parameters):
         """
@@ -115,27 +115,27 @@ def write_array_config_file(self, config_file_path, layout, telescope_model, sit
             # TELESCOPE 0 - global parameters
             file.write("#if TELESCOPE == 0\n")
             file.write(self.TAB + "echo *****************************\n")
-            file.write(self.TAB + "echo Site: {}\n".format(self._site))
-            file.write(self.TAB + "echo LayoutName: {}\n".format(self._layout_name))
-            file.write(self.TAB + "echo ModelVersion: {}\n".format(self._model_version))
+            file.write(self.TAB + f"echo Site: {self._site}\n")
+            file.write(self.TAB + f"echo LayoutName: {self._layout_name}\n")
+            file.write(self.TAB + f"echo ModelVersion: {self._model_version}\n")
             file.write(self.TAB + "echo *****************************\n\n")
 
             # Writing site parameters
             self._write_site_parameters(file, site_parameters)
 
             # Maximum telescopes
-            file.write(self.TAB + "maximum_telescopes = {}\n\n".format(len(telescope_model)))
+            file.write(self.TAB + f"maximum_telescopes = {len(telescope_model)}\n\n")
 
             # Default telescope - 0th tel in telescope list
             tel_config_file = telescope_model[0].get_config_file(no_export=True).name
-            file.write("# include <{}>\n\n".format(tel_config_file))
+            file.write(f"# include <{tel_config_file}>\n\n")
 
             # Looping over telescopes - from 1 to ...
             for count, tel_model in enumerate(telescope_model):
                 tel_config_file = tel_model.get_config_file(no_export=True).name
-                file.write("%{}\n".format(layout[count].name))
-                file.write("#elif TELESCOPE == {}\n\n".format(count + 1))
-                file.write("# include <{}>\n\n".format(tel_config_file))
+                file.write(f"%{layout[count].name}\n")
+                file.write(f"#elif TELESCOPE == {count + 1}\n\n")
+                file.write(f"# include <{tel_config_file}>\n\n")
             file.write("#endif \n\n")
 
     def write_single_mirror_list_file(
@@ -176,9 +176,7 @@ def write_single_mirror_list_file(
             )
             file.write("#\n")
             file.write(
-                "0. 0. {} {} {} 0.\n".format(
-                    diameter, flen if not set_focal_length_to_zero else 0, shape
-                )
+                f"0. 0. {diameter} {flen if not set_focal_length_to_zero else 0} {shape} 0.\n"
             )
 
     def _write_header(self, file, title, comment_char="%"):
@@ -186,25 +184,23 @@ def _write_header(self, file, title, comment_char="%"):
         Writes a generic header. commen_char is the character to be used for comments, which \
         differs among ctypes of config files.
         """
-        header = "{}{}\n".format(comment_char, 50 * "=")
-        header += "{} {}\n".format(comment_char, title)
-        header += "{} Site: {}\n".format(comment_char, self._site)
-        header += "{} ModelVersion: {}\n".format(comment_char, self._model_version)
+        header = f"{comment_char}{50 * '='}\n"
+        header += f"{comment_char} {title}\n"
+        header += f"{comment_char} Site: {self._site}\n"
+        header += f"{comment_char} ModelVersion: {self._model_version}\n"
         header += (
-            "{} TelescopeModelName: {}\n".format(comment_char, self._telescope_model_name)
+            f"{comment_char} TelescopeModelName: {self._telescope_model_name}\n"
             if self._telescope_model_name is not None
             else ""
         )
         header += (
-            "{} LayoutName: {}\n".format(comment_char, self._layout_name)
+            f"{comment_char} LayoutName: {self._layout_name}\n"
             if self._layout_name is not None
             else ""
         )
-        header += (
-            "{} Label: {}\n".format(comment_char, self._label) if self._label is not None else ""
-        )
-        header += "{}{}\n".format(comment_char, 50 * "=")
-        header += "{}\n".format(comment_char)
+        header += f"{comment_char} Label: {self._label}\n" if self._label is not None else ""
+        header += f"{comment_char}{50 * '='}\n"
+        header += f"{comment_char}\n"
         file.write(header)
 
     def _write_site_parameters(self, file, site_parameters):
@@ -214,5 +210,5 @@ def _write_site_parameters(self, file, site_parameters):
             if par in self.PARS_NOT_TO_WRITE:
                 continue
             value = site_parameters[par]["Value"]
-            file.write(self.TAB + "{} = {}\n".format(par, value))
+            file.write(self.TAB + f"{par} = {value}\n")
         file.write("\n")
diff --git a/simtools/simtel/simtel_events.py b/simtools/simtel/simtel_events.py
index 3ba17e442..73e44ef70 100644
--- a/simtools/simtel/simtel_events.py
+++ b/simtools/simtel/simtel_events.py
@@ -12,8 +12,6 @@
 class InconsistentInputFile(Exception):
     """Exception for inconsistent input file."""
 
-    pass
-
 
 class SimtelEvents:
     """
@@ -136,7 +134,6 @@ def _are_headers_consistent(header0, header1):
             self._mc_header["n_use"] * self._mc_header["n_showers"] * self._number_of_files
         )
         self._mc_header["n_triggered"] = number_of_triggered_events
-        return
 
     @u.quantity_input(core_max=u.m)
     def count_triggered_events(self, energy_range=None, core_max=None):
@@ -160,7 +157,7 @@ def count_triggered_events(self, energy_range=None, core_max=None):
 
         is_in_energy_range = list(
             map(
-                lambda e: e > energy_range[0] and e < energy_range[1],
+                lambda e: energy_range[0] < e < energy_range[1],
                 self.summary_events["energy"],
             )
         )
@@ -256,10 +253,10 @@ def _validate_energy_range(self, energy_range):
 
         try:
             return (energy_range[0].to(u.TeV).value, energy_range[1].to(u.TeV).value)
-        except u.core.UnitConversionError:
+        except u.core.UnitConversionError as e:
             msg = "energy_range must be in units of energy"
             self._logger.error(msg)
-            raise TypeError(msg)
+            raise TypeError(msg) from e
 
     def _validate_core_max(self, core_max):
         """
diff --git a/simtools/simtel/simtel_histograms.py b/simtools/simtel/simtel_histograms.py
index 5da1734ea..dcff9bdb9 100644
--- a/simtools/simtel/simtel_histograms.py
+++ b/simtools/simtel/simtel_histograms.py
@@ -13,8 +13,6 @@
 class BadHistogramFormat(Exception):
     """Exception for bad histogram format."""
 
-    pass
-
 
 class SimtelHistograms:
     """
@@ -36,6 +34,7 @@ def __init__(self, histogram_files, test=False):
         self._logger = logging.getLogger(__name__)
         self._histogram_files = histogram_files
         self._is_test = test
+        self.combined_hists = None
 
     def plot_and_save_figures(self, fig_name):
         """
@@ -89,7 +88,7 @@ def _combine_histogram_files(self):
                     try:
                         hists = o.parse()
                     except Exception:
-                        self._logger.warning("Problematic file {}".format(file))
+                        self._logger.warning(f"Problematic file {file}")
                         count_file = False
                         continue
 
@@ -119,9 +118,7 @@ def _combine_histogram_files(self):
 
                     n_files += int(count_file)
 
-        self._logger.debug("End of reading {} files".format(n_files))
-
-        return
+        self._logger.debug(f"End of reading {n_files} files")
 
     def _plot_combined_histograms(self, fig_name):
         """
@@ -138,10 +135,10 @@ def _plot_combined_histograms(self, fig_name):
 
             # Test case: processing only 1/10 of the histograms
             if self._is_test and i_hist % 10 != 0:
-                self._logger.debug("Skipping (test=True): {}".format(histo["title"]))
+                self._logger.debug(f"Skipping (test=True): {histo['title']}")
                 continue
 
-            self._logger.debug("Processing: {}".format(histo["title"]))
+            self._logger.debug(f"Processing: {histo['title']}")
 
             fig = plt.figure(figsize=(8, 6))
             ax = plt.gca()
diff --git a/simtools/simtel/simtel_runner.py b/simtools/simtel/simtel_runner.py
index 6c54be26f..05449cc6b 100644
--- a/simtools/simtel/simtel_runner.py
+++ b/simtools/simtel/simtel_runner.py
@@ -7,18 +7,18 @@
 
 __all__ = ["InvalidOutputFile", "SimtelExecutionError", "SimtelRunner"]
 
+# pylint: disable=no-member
+# The line above is needed because there are methods which are used in this class
+# but are implemented in the classes inheriting from it.
+
 
 class SimtelExecutionError(Exception):
     """Exception for simtel_array execution error."""
 
-    pass
-
 
 class InvalidOutputFile(Exception):
     """Exception for invalid output file."""
 
-    pass
-
 
 class SimtelRunner:
     """
@@ -41,11 +41,13 @@ def __init__(self, simtel_source_path, label=None):
 
         self._simtel_source_path = simtel_source_path
         self.label = label
+        self._script_dir = None
+        self._script_file = None
 
         self.RUNS_PER_SET = 1
 
     def __repr__(self):
-        return "SimtelRunner(label={})\n".format(self.label)
+        return f"SimtelRunner(label={self.label})\n"
 
     def _validate_telescope_model(self, tel):
         """Validate TelescopeModel.
@@ -63,10 +65,10 @@ def _validate_telescope_model(self, tel):
         if isinstance(tel, TelescopeModel):
             self._logger.debug("TelescopeModel is valid")
             return tel
-        else:
-            msg = "Invalid TelescopeModel"
-            self._logger.error(msg)
-            raise ValueError(msg)
+
+        msg = "Invalid TelescopeModel"
+        self._logger.error(msg)
+        raise ValueError(msg)
 
     def _validate_array_model(self, array):
         """Validate ArrayModel
@@ -84,10 +86,10 @@ def _validate_array_model(self, array):
         if isinstance(array, ArrayModel):
             self._logger.debug("ArrayModel is valid")
             return array
-        else:
-            msg = "Invalid ArrayModel"
-            self._logger.error(msg)
-            raise ValueError(msg)
+
+        msg = "Invalid ArrayModel"
+        self._logger.error(msg)
+        raise ValueError(msg)
 
     def get_run_script(self, test=False, input_file=None, run_number=None, extra_commands=None):
         """
@@ -114,11 +116,11 @@ def get_run_script(self, test=False, input_file=None, run_number=None, extra_com
         self._script_dir = self._base_directory.joinpath("scripts")
         self._script_dir.mkdir(parents=True, exist_ok=True)
         self._script_file = self._script_dir.joinpath(
-            "run{}-simtel".format(run_number if run_number is not None else "")
+            f"run{run_number if run_number is not None else ''}-simtel"
         )
-        self._logger.debug("Run bash script - {}".format(self._script_file))
+        self._logger.debug(f"Run bash script - {self._script_file}")
 
-        self._logger.debug("Extra commands to be added to the run script {}".format(extra_commands))
+        self._logger.debug(f"Extra commands to be added to the run script {extra_commands}")
 
         command = self._make_run_command(input_file=input_file, run_number=run_number)
         with self._script_file.open("w") as file:
@@ -130,17 +132,17 @@ def get_run_script(self, test=False, input_file=None, run_number=None, extra_com
             if extra_commands is not None:
                 file.write("# Writing extras\n")
                 for line in extra_commands:
-                    file.write("{}\n".format(line))
+                    file.write(f"{line}\n")
                 file.write("# End of extras\n\n")
 
             N = 1 if test else self.RUNS_PER_SET
             for _ in range(N):
-                file.write("{}\n\n".format(command))
+                file.write(f"{command}\n\n")
 
             # Printing out runtime
             file.write('\necho "RUNTIME: $SECONDS"\n')
 
-        os.system("chmod ug+x {}".format(self._script_file))
+        os.system(f"chmod ug+x {self._script_file}")
         return self._script_file
 
     def run(self, test=False, force=False, input_file=None, run_number=None):
@@ -168,10 +170,10 @@ def run(self, test=False, force=False, input_file=None, run_number=None):
         command = self._make_run_command(input_file=input_file, run_number=run_number)
 
         if test:
-            self._logger.info("Running (test) with command: {}".format(command))
+            self._logger.info(f"Running (test) with command: {command}")
             self._run_simtel_and_check_output(command)
         else:
-            self._logger.debug("Running ({}x) with command: {}".format(self.RUNS_PER_SET, command))
+            self._logger.debug(f"Running ({self.RUNS_PER_SET}x) with command: {command}")
             self._run_simtel_and_check_output(command)
 
             for _ in range(self.RUNS_PER_SET - 1):
@@ -227,16 +229,16 @@ def _run_simtel_and_check_output(self, command):
         if self._simtel_failed(sys_output):
             self._raise_simtel_error()
 
-    def _shall_run(self, **kwargs):
+    def _shall_run(self, **kwargs):  # pylint: disable=unused-argument; applies only to this line
         self._logger.debug(
             "shall_run is being called from the base class - returning False -"
-            + "it should be implemented in the sub class"
+            "it should be implemented in the sub class"
         )
         return False
 
     @staticmethod
     def _config_option(par, value=None):
         """Util function for building sim_telarray command."""
-        c = " -C {}".format(par)
-        c += "={}".format(value) if value is not None else ""
+        c = f" -C {par}"
+        c += f"={value}" if value is not None else ""
         return c
diff --git a/simtools/simtel/simtel_runner_array.py b/simtools/simtel/simtel_runner_array.py
index 60f147d85..d29ed9f21 100644
--- a/simtools/simtel/simtel_runner_array.py
+++ b/simtools/simtel/simtel_runner_array.py
@@ -61,6 +61,7 @@ def __init__(
 
         self.array_model = self._validate_array_model(array_model)
         self.label = label if label is not None else self.array_model.label
+        self._log_file = None
 
         self.io_handler = io_handler.IOHandler()
 
@@ -220,7 +221,7 @@ def get_resources(self, run_number):
         info_for_file_name = self.get_info_for_file_name(run_number)
         sub_log_file = self.get_file_name("sub_log", **info_for_file_name, mode="out")
 
-        self._logger.debug("Reading resources from {}".format(sub_log_file))
+        self._logger.debug(f"Reading resources from {sub_log_file}")
 
         _resources = {}
 
@@ -266,8 +267,8 @@ def _make_run_command(self, **kwargs):
 
         # Array
         command = str(self._simtel_source_path.joinpath("sim_telarray/bin/sim_telarray"))
-        command += " -c {}".format(self.array_model.get_config_file())
-        command += " -I{}".format(self.array_model.get_config_directory())
+        command += f" -c {self.array_model.get_config_file()}"
+        command += f" -I{self.array_model.get_config_directory()}"
         command += super()._config_option("telescope_theta", self.config.zenith_angle)
         command += super()._config_option("telescope_phi", self.config.azimuth_angle)
         command += super()._config_option("power_law", "2.5")
@@ -289,5 +290,5 @@ def _check_run_result(self, **kwargs):
             msg = "sim_telarray output file does not exist."
             self._logger.error(msg)
             raise InvalidOutputFile(msg)
-        else:
-            self._logger.debug("Everything looks fine with the sim_telarray output file.")
+
+        self._logger.debug("Everything looks fine with the sim_telarray output file.")
diff --git a/simtools/simtel/simtel_runner_camera_efficiency.py b/simtools/simtel/simtel_runner_camera_efficiency.py
index 4bc799ce5..fe3df5d9f 100644
--- a/simtools/simtel/simtel_runner_camera_efficiency.py
+++ b/simtools/simtel/simtel_runner_camera_efficiency.py
@@ -42,17 +42,17 @@ def __init__(
         super().__init__(label=label, simtel_source_path=simtel_source_path)
 
         self._telescope_model = telescope_model
-        self.label = label if label is not None else self.telescope_model.label
+        self.label = label if label is not None else self._telescope_model.label
 
         self._file_simtel = file_simtel
         self._file_log = file_log
         self.zenith_angle = zenith_angle
 
-    def _shall_run(self, **kwargs):
+    def _shall_run(self, **kwargs):  # pylint: disable=unused-argument; applies only to this line
         """Tells if simulations should be run again based on the existence of output files."""
         return not self._file_simtel.exists()
 
-    def _make_run_command(self, **kwargs):
+    def _make_run_command(self, **kwargs):  # pylint: disable=unused-argument
         """
         Prepare the command used to run testeff
         """
@@ -134,7 +134,7 @@ def _make_run_command(self, **kwargs):
 
         return command
 
-    def _check_run_result(self, **kwargs):
+    def _check_run_result(self, **kwargs):  # pylint: disable=unused-argument
         """Checking run results
 
         Raises
@@ -147,8 +147,8 @@ def _check_run_result(self, **kwargs):
             msg = "Camera efficiency simulation results file does not exist"
             self._logger.error(msg)
             raise RuntimeError(msg)
-        else:
-            self._logger.debug("Everything looks fine with output file.")
+
+        self._logger.debug("Everything looks fine with output file.")
 
     def _get_one_dim_distribution(self, two_dim_parameter, weighting_distribution_parameter):
         """
diff --git a/simtools/simtel/simtel_runner_ray_tracing.py b/simtools/simtel/simtel_runner_ray_tracing.py
index 6750af79b..ca236c56f 100644
--- a/simtools/simtel/simtel_runner_ray_tracing.py
+++ b/simtools/simtel/simtel_runner_ray_tracing.py
@@ -9,6 +9,10 @@
 
 __all__ = ["SimtelRunnerRayTracing"]
 
+# pylint: disable=no-member
+# The line above is needed because there are members which are created
+# by adding them to the __dict__ of the class rather than directly.
+
 
 class SimtelRunnerRayTracing(SimtelRunner):
     """
@@ -126,15 +130,15 @@ def _load_required_files(self, force_simulate):
         if not file.exists() or force_simulate:
             # Adding header to photon list file.
             with self._photons_file.open("w") as file:
-                file.write("#{}\n".format(50 * "="))
+                file.write(f"#{50 * '='}\n")
                 file.write("# List of photons for RayTracing simulations\n")
-                file.write("#{}\n".format(50 * "="))
-                file.write("# config_file = {}\n".format(self.telescope_model.get_config_file()))
-                file.write("# zenith_angle [deg] = {}\n".format(self.config.zenith_angle))
-                file.write("# off_axis_angle [deg] = {}\n".format(self.config.off_axis_angle))
-                file.write("# source_distance [km] = {}\n".format(self.config.source_distance))
+                file.write(f"#{50 * '='}\n")
+                file.write(f"# config_file = {self.telescope_model.get_config_file()}\n")
+                file.write(f"# zenith_angle [deg] = {self.config.zenith_angle}\n")
+                file.write(f"# off_axis_angle [deg] = {self.config.off_axis_angle}\n")
+                file.write(f"# source_distance [km] = {self.config.source_distance}\n")
                 if self._single_mirror_mode:
-                    file.write("# mirror_number = {}\n\n".format(self.config.mirror_number))
+                    file.write(f"# mirror_number = {self.config.mirror_number}\n\n")
 
             # Filling in star file with a single light source.
             # Parameters defining light source:
@@ -144,16 +148,14 @@ def _load_required_files(self, force_simulate):
             # - distance of light source
             with self._stars_file.open("w") as file:
                 file.write(
-                    "0. {} 1.0 {}\n".format(
-                        90.0 - self.config.zenith_angle, self.config.source_distance
-                    )
+                    f"0. {90.0 - self.config.zenith_angle} 1.0 {self.config.source_distance}\n"
                 )
 
-    def _shall_run(self, **kwargs):
+    def _shall_run(self, **kwargs):  # pylint: disable=unused-argument; applies only to this line
         """Tells if simulations should be run again based on the existence of output files."""
         return not self._is_photon_list_file_ok()
 
-    def _make_run_command(self, **kwargs):
+    def _make_run_command(self, **kwargs):  # pylint: disable=unused-argument
         """Return the command to run simtel_array."""
 
         if self._single_mirror_mode:
@@ -163,9 +165,9 @@ def _make_run_command(self, **kwargs):
 
         # RayTracing
         command = str(self._simtel_source_path.joinpath("sim_telarray/bin/sim_telarray"))
-        command += " -c {}".format(self.telescope_model.get_config_file())
+        command += f" -c {self.telescope_model.get_config_file()}"
         command += " -I../cfg/CTA"
-        command += " -I{}".format(self.telescope_model.get_config_directory())
+        command += f" -I{self.telescope_model.get_config_directory()}"
         command += super()._config_option("IMAGING_LIST", str(self._photons_file))
         command += super()._config_option("stars", str(self._stars_file))
         command += super()._config_option(
@@ -211,7 +213,7 @@ def _make_run_command(self, **kwargs):
 
         return command
 
-    def _check_run_result(self, **kwargs):
+    def _check_run_result(self, **kwargs):  # pylint: disable=unused-argument
         """Checking run results.
 
         Raises
@@ -224,8 +226,8 @@ def _check_run_result(self, **kwargs):
             msg = "Photon list is empty."
             self._logger.error(msg)
             raise RuntimeError(msg)
-        else:
-            self._logger.debug("Everything looks fine with output file.")
+
+        self._logger.debug("Everything looks fine with output file.")
 
     def _is_photon_list_file_ok(self):
         """Check if the photon list is valid,"""
diff --git a/simtools/simulator.py b/simtools/simulator.py
index 3ae8befd9..5f7cac5f1 100644
--- a/simtools/simulator.py
+++ b/simtools/simulator.py
@@ -25,8 +25,6 @@
 class InvalidRunsToSimulate(Exception):
     """Exception for invalid runs to simulate."""
 
-    pass
-
 
 class Simulator:
     """
@@ -114,7 +112,7 @@ def __init__(
         Initialize Simulator class.
         """
         self._logger = logging.getLogger(__name__)
-        self._logger.debug("Init Simulator {}".format(simulator))
+        self._logger.debug(f"Init Simulator {simulator}")
 
         self.label = label
         self._set_simulator(simulator)
@@ -122,6 +120,14 @@ def __init__(
         self._results = defaultdict(list)
         self.test = test
 
+        self._corsika_config_data = None
+        self.site = None
+        self.layout_name = None
+        self._corsika_parameters_file = None
+        self.config = None
+        self.array_model = None
+        self._simulation_runner = None
+
         self.io_handler = io_handler.IOHandler()
         self._output_directory = self.io_handler.get_output_directory(self.label, self.simulator)
         self._simulator_source_path = Path(simulator_source_path)
@@ -254,19 +260,19 @@ def _validate_run_list_and_range(self, run_list, run_range):
                 msg = "run_list must contain only integers."
                 self._logger.error(msg)
                 raise InvalidRunsToSimulate
-            else:
-                self._logger.debug("run_list: {}".format(run_list))
-                validated_runs = list(run_list)
+
+            self._logger.debug(f"run_list: {run_list}")
+            validated_runs = list(run_list)
 
         if run_range is not None:
             if not all(isinstance(r, int) for r in run_range) or len(run_range) != 2:
                 msg = "run_range must contain two integers only."
                 self._logger.error(msg)
                 raise InvalidRunsToSimulate
-            else:
-                run_range = np.arange(run_range[0], run_range[1] + 1)
-                self._logger.debug("run_range: {}".format(run_range))
-                validated_runs.extend(list(run_range))
+
+            run_range = np.arange(run_range[0], run_range[1] + 1)
+            self._logger.debug(f"run_range: {run_range}")
+            validated_runs.extend(list(run_range))
 
         validated_runs_unique = sorted(set(validated_runs))
         return list(validated_runs_unique)
@@ -371,12 +377,12 @@ def simulate(self, input_file_list=None):
 
         """
 
-        self._logger.info("Submission command: {}".format(self._submit_command))
+        self._logger.info(f"Submission command: {self._submit_command}")
 
         runs_and_files_to_submit = self._get_runs_and_files_to_submit(
             input_file_list=input_file_list
         )
-        self._logger.info("Starting submission for {} runs".format(len(runs_and_files_to_submit)))
+        self._logger.info(f"Starting submission for {len(runs_and_files_to_submit)} runs")
 
         for run, file in runs_and_files_to_submit.items():
 
@@ -453,7 +459,7 @@ def _enforce_list_type(input_file_list):
         """Enforce the input list to be a list."""
         if not input_file_list:
             return list()
-        elif not isinstance(input_file_list, list):
+        if not isinstance(input_file_list, list):
             return [input_file_list]
 
         return input_file_list
@@ -477,7 +483,7 @@ def _guess_run_from_file(self, file):
             run_str = re.search("run[0-9]*", file_name).group()
             return int(run_str[3:])
         except (ValueError, AttributeError):
-            msg = "Run number could not be guessed from {} using run = 1".format(file_name)
+            msg = f"Run number could not be guessed from {file_name} using run = 1"
             self._logger.warning(msg)
             return 1
 
@@ -689,9 +695,9 @@ def resources(self, input_file_list=None):
         """
         resources = self._make_resources_report(input_file_list)
         print("-----------------------------")
-        print("Computing Resources Report - {} Simulations".format(self.simulator))
+        print(f"Computing Resources Report - {self.simulator} Simulations")
         for key, value in resources.items():
-            print("{} = {:.2f}".format(key, value))
+            print(f"{key} = {value:.2f}")
         print("-----------------------------")
 
     def _get_runs_to_simulate(self, run_list=None, run_range=None):
@@ -713,16 +719,13 @@ def _get_runs_to_simulate(self, run_list=None, run_range=None):
         """
         if run_list is None and run_range is None:
             if self.runs is None:
-                msg = (
-                    "Runs to simulate were not given as arguments nor "
-                    + "in config_data - aborting"
-                )
+                msg = "Runs to simulate were not given as arguments nor in config_data - aborting"
                 self._logger.error(msg)
                 return list()
-            else:
-                return self.runs
-        else:
-            return self._validate_run_list_and_range(run_list, run_range)
+
+            return self.runs
+
+        return self._validate_run_list_and_range(run_list, run_range)
 
     def _print_list_of_files(self, which):
         """
@@ -736,7 +739,7 @@ def _print_list_of_files(self, which):
         """
 
         if which not in self._results:
-            self._logger.error("Invalid file type {}".format(which))
+            self._logger.error(f"Invalid file type {which}")
             raise KeyError
-        for f in self._results[which]:
-            print(f)
+        for file in self._results[which]:
+            print(file)
diff --git a/simtools/util/general.py b/simtools/util/general.py
index d8dbb9078..c82169d1e 100644
--- a/simtools/util/general.py
+++ b/simtools/util/general.py
@@ -28,26 +28,18 @@
 class UnableToIdentifyConfigEntry(Exception):
     """Exception for unable to indentify configuration entry."""
 
-    pass
-
 
 class MissingRequiredConfigEntry(Exception):
     """Exception for missing required configuration entry."""
 
-    pass
-
 
 class InvalidConfigEntry(Exception):
     """Exception for invalid configuration entry."""
 
-    pass
-
 
 class InvalidConfigData(Exception):
     """Exception for invalid configuration data."""
 
-    pass
-
 
 def file_has_text(file, text):
     """
@@ -72,8 +64,8 @@ def file_has_text(file, text):
         search_result_1 = re_search_1.search(text_file_input)
         if search_result_1 is None:
             return False
-        else:
-            return True
+
+        return True
 
 
 def validate_config_data(config_data, parameters):
@@ -131,7 +123,7 @@ def validate_config_data(config_data, parameters):
 
         # Raising error for an unidentified input.
         if not is_identified:
-            msg = "Entry {} in config_data cannot be identified.".format(key_data)
+            msg = f"Entry {key_data} in config_data cannot be identified."
             logger.error(msg)
             raise UnableToIdentifyConfigEntry(msg)
 
@@ -140,15 +132,14 @@ def validate_config_data(config_data, parameters):
     for par_name, par_info in parameters.items():
         if par_name in out_data:
             continue
-        elif "default" in par_info.keys() and par_info["default"] is not None:
+        if "default" in par_info.keys() and par_info["default"] is not None:
             validated_value = _validate_and_convert_value(par_name, par_info, par_info["default"])
             out_data[par_name] = validated_value
         elif "default" in par_info.keys() and par_info["default"] is None:
             out_data[par_name] = None
         else:
             msg = (
-                "Required entry in config_data {} ".format(par_name)
-                + "was not given (there may be more)."
+                f"Required entry in config_data {par_name} " + "was not given (there may be more)."
             )
             logger.error(msg)
             raise MissingRequiredConfigEntry(msg)
@@ -183,17 +174,17 @@ def _validate_and_convert_value_without_units(value, value_keys, par_name, par_i
     _, undefined_length = _check_value_entry_length(value, par_name, par_info)
 
     # Checking if values have unit and raising error, if so.
-    if all([isinstance(v, str) for v in value]):
+    if all(isinstance(v, str) for v in value):
         # In case values are string, e.g. mirror_numbers = 'all'
         # This is needed otherwise the elif condition will break
         pass
-    elif any([u.Quantity(v).unit != u.dimensionless_unscaled for v in value]):
-        msg = "Config entry {} should not have units".format(par_name)
+    elif any(u.Quantity(v).unit != u.dimensionless_unscaled for v in value):
+        msg = f"Config entry {par_name} should not have units"
         logger.error(msg)
         raise InvalidConfigEntry(msg)
 
     if value_keys:
-        return {k: v for (k, v) in zip(value_keys, value)}
+        return dict(zip(value_keys, value))
     return value if len(value) > 1 or undefined_length else value[0]
 
 
@@ -222,13 +213,13 @@ def _check_value_entry_length(value, par_name, par_info):
 
     # Checking the entry length
     value_length = len(value)
-    logger.debug("Value len of {}: {}".format(par_name, value_length))
+    logger.debug(f"Value len of {par_name}: {value_length}")
     undefined_length = False
     try:
         if par_info["len"] is None:
             undefined_length = True
         elif value_length != par_info["len"]:
-            msg = "Config entry with wrong len: {}".format(par_name)
+            msg = f"Config entry with wrong len: {par_name}"
             logger.error(msg)
             raise InvalidConfigEntry(msg)
     except KeyError:
@@ -264,10 +255,10 @@ def _validate_and_convert_value_with_units(value, value_keys, par_name, par_info
     par_unit = copy_as_list(par_info["unit"])
 
     if undefined_length and len(par_unit) != 1:
-        msg = "Config entry with undefined length should have a single unit: {}".format(par_name)
+        msg = f"Config entry with undefined length should have a single unit: {par_name}"
         logger.error(msg)
         raise InvalidConfigEntry(msg)
-    elif len(par_unit) == 1:
+    if len(par_unit) == 1:
         par_unit *= value_length
 
     # Checking units and converting them, if needed.
@@ -283,18 +274,17 @@ def _validate_and_convert_value_with_units(value, value_keys, par_name, par_info
             arg = u.quantity.Quantity(arg)
 
         if not isinstance(arg, u.quantity.Quantity):
-            msg = "Config entry given without unit: {}".format(par_name)
+            msg = f"Config entry given without unit: {par_name}"
             logger.error(msg)
             raise InvalidConfigEntry(msg)
-        elif not arg.unit.is_equivalent(unit):
-            msg = "Config entry given with wrong unit: {}".format(par_name)
+        if not arg.unit.is_equivalent(unit):
+            msg = f"Config entry given with wrong unit: {par_name}"
             logger.error(msg)
             raise InvalidConfigEntry(msg)
-        else:
-            value_with_units.append(arg.to(unit).value)
+        value_with_units.append(arg.to(unit).value)
 
     if value_keys:
-        return {k: v for (k, v) in zip(value_keys, value_with_units)}
+        return dict(zip(value_keys, value_with_units))
 
     return (
         value_with_units if len(value_with_units) > 1 or undefined_length else value_with_units[0]
@@ -346,16 +336,16 @@ def collect_data_from_yaml_or_dict(in_yaml, in_dict, allow_empty=False):
         with open(in_yaml) as file:
             data = yaml.load(file)
         return data
-    elif in_dict is not None:
+    if in_dict is not None:
         return dict(in_dict)
-    else:
-        msg = "config_data has not been provided (by yaml file neither by dict)"
-        if allow_empty:
-            _logger.debug(msg)
-            return None
-        else:
-            _logger.error(msg)
-            raise InvalidConfigData(msg)
+
+    msg = "config_data has not been provided (by yaml file neither by dict)"
+    if allow_empty:
+        _logger.debug(msg)
+        return None
+
+    _logger.error(msg)
+    raise InvalidConfigData(msg)
 
 
 def collect_kwargs(label, in_kwargs):
@@ -418,8 +408,8 @@ def sort_arrays(*args):
     order_array = copy.copy(args[0])
     new_args = list()
     for arg in args:
-        _, a = zip(*sorted(zip(order_array, arg)))
-        new_args.append(list(a))
+        _, value = zip(*sorted(zip(order_array, arg)))
+        new_args.append(list(value))
     return new_args
 
 
@@ -440,14 +430,12 @@ def collect_final_lines(file, n_lines):
         Final lines collected.
     """
     file_in_lines = list()
-    with open(file, "r") as f:
-        for line in f:
+    with open(file, "r") as opened_file:
+        for line in opened_file:
             file_in_lines.append(line)
     collected_lines = file_in_lines[-n_lines:-1]
-    out = ""
-    for ll in collected_lines:
-        out += ll
-    return out
+
+    return "".join(collected_lines)
 
 
 def get_log_level_from_user(log_level):
@@ -476,12 +464,11 @@ def get_log_level_from_user(log_level):
     log_level_lower = log_level.lower()
     if log_level_lower not in possible_levels:
         raise ValueError(
-            '"{}" is not a logging level, only possible ones are {}'.format(
-                log_level, list(possible_levels.keys())
-            )
+            f"'{log_level}' is not a logging level, "
+            f"only possible ones are {list(possible_levels.keys())}"
         )
-    else:
-        return possible_levels[log_level_lower]
+
+    return possible_levels[log_level_lower]
 
 
 def copy_as_list(value):
@@ -499,11 +486,11 @@ def copy_as_list(value):
     """
     if isinstance(value, str):
         return [value]
-    else:
-        try:
-            return list(value)
-        except Exception:
-            return [value]
+
+    try:
+        return list(value)
+    except Exception:
+        return [value]
 
 
 def separate_args_and_config_data(expected_args, **kwargs):
@@ -589,35 +576,35 @@ def find_file(name, loc):
 
     def _search_directory(directory, filename, rec=False):
         if not Path(directory).exists():
-            msg = "Directory {} does not exist".format(directory)
+            msg = f"Directory {directory} does not exist"
             _logger.debug(msg)
             return None
 
-        f = Path(directory).joinpath(filename)
-        if f.exists():
-            _logger.debug("File {} found in {}".format(filename, directory))
-            return f
+        file = Path(directory).joinpath(filename)
+        if file.exists():
+            _logger.debug(f"File {filename} found in {directory}")
+            return file
         if not rec:  # Not recursively
             return None
 
         for subdir in Path(directory).iterdir():
             if not subdir.is_dir():
                 continue
-            f = _search_directory(subdir, filename, True)
-            if f is not None:
-                return f
+            file = _search_directory(subdir, filename, True)
+            if file is not None:
+                return file
         return None
 
     # Searching file locally
-    ff = _search_directory(".", name)
-    if ff is not None:
-        return ff
+    file = _search_directory(".", name)
+    if file is not None:
+        return file
     # Searching file in given locations
-    for ll in all_locations:
-        ff = _search_directory(ll, name, True)
-        if ff is not None:
-            return ff
-    msg = "File {} could not be found in {}".format(name, all_locations)
+    for location_now in all_locations:
+        file = _search_directory(location_now, name, True)
+        if file is not None:
+            return file
+    msg = f"File {name} could not be found in {all_locations}"
     _logger.error(msg)
     raise FileNotFoundError(msg)
 
@@ -656,7 +643,7 @@ def change_dict_keys_case(data_dict, lower_case=True):
                 _return_dict[_key_changed] = data_dict[key]
     except AttributeError:
         logger = logging.getLogger(__name__)
-        logger.error("Invalid method argument: %s", data_dict)
+        logger.error(f"Invalid method argument: {data_dict}")
         raise
 
     return _return_dict
diff --git a/simtools/util/names.py b/simtools/util/names.py
index 5e17c6768..0c9158e88 100644
--- a/simtools/util/names.py
+++ b/simtools/util/names.py
@@ -90,7 +90,7 @@ def validate_telescope_id_name(name):
         return name
 
     _logger = logging.getLogger(__name__)
-    msg = "Invalid telescope ID name {}".format(name)
+    msg = f"Invalid telescope ID name {name}"
     _logger.error(msg)
     raise ValueError(msg)
 
@@ -188,13 +188,13 @@ def validate_name(name, all_names):
     _logger = logging.getLogger(__name__)
 
     if not is_valid_name(name, all_names):
-        msg = "Invalid name {}".format(name)
+        msg = f"Invalid name {name}"
         _logger.error(msg)
         raise ValueError(msg)
     for main_name, list_of_names in all_names.items():
         if name.lower() in list_of_names + [main_name.lower()]:
             if name != main_name:
-                _logger.debug("Correcting name {} -> {}".format(name, main_name))
+                _logger.debug(f"Correcting name {name} -> {main_name}")
             return main_name
     return None
 
@@ -348,9 +348,9 @@ def convert_telescope_model_name_to_yaml(name):
     }
 
     if new_name not in old_names:
-        raise ValueError("Telescope name {} could not be converted to yml names".format(name))
-    else:
-        return old_names[new_name]
+        raise ValueError(f"Telescope name {name} could not be converted to yml names")
+
+    return old_names[new_name]
 
 
 all_telescope_class_names = {
@@ -476,9 +476,9 @@ def simtel_telescope_config_file_name(
     str
         File name.
     """
-    name = "CTA-{}-{}-{}".format(site, telescope_model_name, model_version)
-    name += "_{}".format(label) if label is not None else ""
-    name += "_{}".format(extra_label) if extra_label is not None else ""
+    name = f"CTA-{site}-{telescope_model_name}-{model_version}"
+    name += f"_{label}" if label is not None else ""
+    name += f"_{extra_label}" if extra_label is not None else ""
     name += ".cfg"
     return name
 
@@ -503,8 +503,8 @@ def simtel_array_config_file_name(array_name, site, version, label):
     str
         File name.
     """
-    name = "CTA-{}-{}-{}".format(array_name, site, version)
-    name += "_{}".format(label) if label is not None else ""
+    name = f"CTA-{array_name}-{site}-{version}"
+    name += f"_{label}" if label is not None else ""
     name += ".cfg"
     return name
 
@@ -533,9 +533,9 @@ def simtel_single_mirror_list_file_name(
     str
         File name.
     """
-    name = "CTA-single-mirror-list-{}-{}-{}".format(site, telescope_model_name, model_version)
-    name += "-mirror{}".format(mirror_number)
-    name += "_{}".format(label) if label is not None else ""
+    name = f"CTA-single-mirror-list-{site}-{telescope_model_name}-{model_version}"
+    name += f"-mirror{mirror_number}"
+    name += f"_{label}" if label is not None else ""
     name += ".dat"
     return name
 
@@ -556,8 +556,8 @@ def layout_telescope_list_file_name(name, label):
     str
         File name.
     """
-    file_name = "telescope_positions-{}".format(name)
-    file_name += "_{}".format(label) if label is not None else ""
+    file_name = f"telescope_positions-{name}"
+    file_name += f"_{label}" if label is not None else ""
     file_name += ".ecsv"
     return file_name
 
@@ -599,11 +599,12 @@ def ray_tracing_file_name(
     str
         File name.
     """
-    name = "{}-{}-{}-d{:.1f}-za{:.1f}-off{:.3f}".format(
-        base, site, telescope_model_name, source_distance, zenith_angle, off_axis_angle
+    name = (
+        f"{base}-{site}-{telescope_model_name}-d{source_distance:.1f}"
+        f"-za{zenith_angle:.1f}-off{off_axis_angle:.3f}"
     )
-    name += "_mirror{}".format(mirror_number) if mirror_number is not None else ""
-    name += "_{}".format(label) if label is not None else ""
+    name += f"_mirror{mirror_number}" if mirror_number is not None else ""
+    name += f"_{label}" if label is not None else ""
     name += ".log" if base == "log" else ".lis"
     return name
 
@@ -630,10 +631,8 @@ def ray_tracing_results_file_name(site, telescope_model_name, source_distance, z
     str
         File name.
     """
-    name = "ray-tracing-{}-{}-d{:.1f}-za{:.1f}".format(
-        site, telescope_model_name, source_distance, zenith_angle
-    )
-    name += "_{}".format(label) if label is not None else ""
+    name = f"ray-tracing-{site}-{telescope_model_name}-d{source_distance:.1f}-za{zenith_angle:.1f}"
+    name += f"_{label}" if label is not None else ""
     name += ".ecsv"
     return name
 
@@ -664,10 +663,11 @@ def ray_tracing_plot_file_name(
     str
         File name.
     """
-    name = "ray-tracing-{}-{}-{}-d{:.1f}-za{:.1f}".format(
-        site, telescope_model_name, key, source_distance, zenith_angle
+    name = (
+        f"ray-tracing-{site}-{telescope_model_name}-{key}-"
+        f"d{source_distance:.1f}-za{zenith_angle:.1f}"
     )
-    name += "_{}".format(label) if label is not None else ""
+    name += f"_{label}" if label is not None else ""
     name += ".pdf"
     return name
 
@@ -692,8 +692,8 @@ def camera_efficiency_results_file_name(site, telescope_model_name, zenith_angle
     str
         File name.
     """
-    name = "camera-efficiency-{}-{}-za{:.1f}".format(site, telescope_model_name, zenith_angle)
-    name += "_{}".format(label) if label is not None else ""
+    name = f"camera-efficiency-{site}-{telescope_model_name}-za{zenith_angle:.1f}"
+    name += f"_{label}" if label is not None else ""
     name += ".ecsv"
     return name
 
@@ -718,8 +718,8 @@ def camera_efficiency_simtel_file_name(site, telescope_model_name, zenith_angle,
     str
         File name.
     """
-    name = "camera-efficiency-{}-{}-za{:.1f}".format(site, telescope_model_name, zenith_angle)
-    name += "_{}".format(label) if label is not None else ""
+    name = f"camera-efficiency-{site}-{telescope_model_name}-za{zenith_angle:.1f}"
+    name += f"_{label}" if label is not None else ""
     name += ".dat"
     return name
 
@@ -744,7 +744,7 @@ def camera_efficiency_log_file_name(site, telescope_model_name, zenith_angle, la
     str
         File name.
     """
-    name = "camera-efficiency-{}-{}-za{:.1f}".format(site, telescope_model_name, zenith_angle)
-    name += "_{}".format(label) if label is not None else ""
+    name = f"camera-efficiency-{site}-{telescope_model_name}-za{zenith_angle:.1f}"
+    name += f"_{label}" if label is not None else ""
     name += ".log"
     return name
diff --git a/simtools/visualization/visualize.py b/simtools/visualization/visualize.py
index cf28e2f1f..aeae2b7ba 100644
--- a/simtools/visualization/visualize.py
+++ b/simtools/visualization/visualize.py
@@ -134,7 +134,7 @@ def _add_unit(title, array):
     if isinstance(array, u.Quantity):
         unit = str(array[0].unit)
         if len(unit) > 0:
-            unit = " [{}]".format(unit)
+            unit = f" [{unit}]"
         if re.search(r"\d", unit):
             unit = re.sub(r"(\d)", r"^\1", unit)
             unit = unit.replace("[", r"$[").replace("]", r"]$")
@@ -145,7 +145,7 @@ def _add_unit(title, array):
             )
             unit = ""
 
-    return "{}{}".format(title, unit)
+    return f"{title}{unit}"
 
 
 def set_style(palette="default", big_plot=False):
@@ -179,7 +179,7 @@ def set_style(palette="default", big_plot=False):
     """
 
     if palette not in COLORS:
-        raise KeyError("palette must be one of {}".format(", ".join(COLORS)))
+        raise KeyError(f"palette must be one of {', '.join(COLORS)}")
 
     fontsize = {"default": 17, "big_plot": 30}
     markersize = {"default": 8, "big_plot": 18}
@@ -232,7 +232,7 @@ def get_colors(palette="default"):
     """
 
     if palette not in COLORS:
-        raise KeyError("palette must be one of {}".format(", ".join(COLORS)))
+        raise KeyError(f"palette must be one of {', '.join(COLORS)}")
 
     return COLORS[palette]
 
@@ -407,11 +407,11 @@ def plot_1D(data, **kwargs):
                 plt.plot(data_now[x_title], y_values, **kwargs)
 
         plt.xlabel(x_title_unit)
-        y_title_ratio = "Ratio to {}".format(data_ref_name)
+        y_title_ratio = f"Ratio to {data_ref_name}"
         if len(y_title_ratio) > 20:
             y_title_ratio = "Ratio"
         if plot_difference:
-            y_title_ratio = "Difference to {}".format(data_ref_name)
+            y_title_ratio = f"Difference to {data_ref_name}"
         plt.ylabel(y_title_ratio)
 
         ylim = plt.gca().get_ylim()


Test patch:
diff --git a/tests/integration_tests/test_applications.py b/tests/integration_tests/test_applications.py
index f16d248d9..68209632b 100644
--- a/tests/integration_tests/test_applications.py
+++ b/tests/integration_tests/test_applications.py
@@ -296,7 +296,7 @@
 @pytest.mark.parametrize("application", APP_LIST.keys())
 def test_applications(application, io_handler, monkeypatch, db):
 
-    logger.info("Testing {}".format(application))
+    logger.info(f"Testing {application}")
 
     # The add_file_to_db.py application requires a user confirmation.
     # With this line we mock the user confirmation to be y for the test
@@ -324,9 +324,9 @@ def make_command(app, args):
 
     for args in APP_LIST[application]:
         app_name = application.partition("::")[0]
-        logger.info("Running with args: {}".format(args))
+        logger.info(f"Running with args: {args}")
         cmd = make_command(app_name, args)
-        logger.info("Running command: {}".format(cmd))
+        logger.info(f"Running command: {cmd}")
         out = os.system(cmd)
         is_output_valid = out == 0
         assert is_output_valid
diff --git a/tests/integration_tests/test_ray_tracing.py b/tests/integration_tests/test_ray_tracing.py
index 033fdea2b..ab7430f6e 100644
--- a/tests/integration_tests/test_ray_tracing.py
+++ b/tests/integration_tests/test_ray_tracing.py
@@ -127,7 +127,7 @@ def test_plot_image(db_config, simtel_path_no_mock, io_handler):
         ax.set_ylabel("Y [cm]")
         image.plot_image(psf_color="b")
         plot_file = io_handler.get_output_file(
-            file_name="test_plot_image_{}.pdf".format(ii), dir_type="plots", test=True
+            file_name=f"test_plot_image_{ii}.pdf", dir_type="plots", test=True
         )
         plt.savefig(plot_file)
 
diff --git a/tests/unit_tests/layout/test_layout_array.py b/tests/unit_tests/layout/test_layout_array.py
index 88ddc7222..2faa94a6b 100644
--- a/tests/unit_tests/layout/test_layout_array.py
+++ b/tests/unit_tests/layout/test_layout_array.py
@@ -66,7 +66,7 @@ def test_initialize_coordinate_systems(layout_center_data_dict):
     _lat, _lon, _z = layout._array_center.get_coordinates("mercator")
     assert np.isnan(_lat) and np.isnan(_lon)
 
-    layout._initialize_coordinate_systems(layout_center_data_dict, False)
+    layout._initialize_coordinate_systems(layout_center_data_dict)
     _x, _y, _z = layout._array_center.get_coordinates("corsika")
     assert _x == 0.0 * u.m and _y == 0.0 * u.m and _z == layout_center_data_dict["center_alt"]
     _lat, _lon, _z = layout._array_center.get_coordinates("mercator")
diff --git a/tests/unit_tests/layout/test_telescope_position.py b/tests/unit_tests/layout/test_telescope_position.py
index ac5d7726c..be027a67c 100644
--- a/tests/unit_tests/layout/test_telescope_position.py
+++ b/tests/unit_tests/layout/test_telescope_position.py
@@ -28,7 +28,7 @@ def crs_local():
     center_lon = -17.8920302
     center_lat = 28.7621661
     proj4_string = "+proj=tmerc +ellps=WGS84 +datum=WGS84"
-    proj4_string += " +lon_0={} +lat_0={}".format(center_lon, center_lat)
+    proj4_string += f" +lon_0={center_lon} +lat_0={center_lat}"
     proj4_string += " +axis=nwu +units=m +k_0=1.0"
     return pyproj.CRS.from_proj4(proj4_string)
 
diff --git a/tests/unit_tests/model/test_mirrors.py b/tests/unit_tests/model/test_mirrors.py
index 12b6b9f2f..8012f10a8 100644
--- a/tests/unit_tests/model/test_mirrors.py
+++ b/tests/unit_tests/model/test_mirrors.py
@@ -22,7 +22,7 @@ def test_read_list(db, io_handler):
     mirror_list_file = gen.find_file(
         test_file_name, io_handler.get_output_directory(dir_type="model", test=True)
     )
-    logger.info("Using mirror list {}".format(mirror_list_file))
+    logger.info(f"Using mirror list {mirror_list_file}")
     mirrors = Mirrors(mirror_list_file)
     assert 198 == mirrors.number_of_mirrors
     assert 151.0 == pytest.approx(mirrors.diameter)
diff --git a/tests/unit_tests/model/test_telescope_model.py b/tests/unit_tests/model/test_telescope_model.py
index 9c9fa53e5..0093b9e4e 100644
--- a/tests/unit_tests/model/test_telescope_model.py
+++ b/tests/unit_tests/model/test_telescope_model.py
@@ -55,9 +55,8 @@ def test_handling_parameters(telescope_model_lst):
     tel_model = telescope_model_lst
 
     logger.info(
-        "Old mirror_reflection_random_angle:{}".format(
-            tel_model.get_parameter_value("mirror_reflection_random_angle")
-        )
+        "Old mirror_reflection_random_angle: "
+        f"{tel_model.get_parameter_value('mirror_reflection_random_angle')}"
     )
     logger.info("Changing mirror_reflection_random_angle")
     new_mrra = "0.0080 0 0"
@@ -102,7 +101,7 @@ def test_flen_type(telescope_model_lst):
 
     tel_model = telescope_model_lst
     flen_info = tel_model.get_parameter("focal_length")
-    logger.info("Focal Length = {}, type = {}".format(flen_info["Value"], flen_info["Type"]))
+    logger.info(f"Focal Length = {flen_info['Value']}, type = {flen_info['Type']}")
 
     assert isinstance(flen_info["Value"], float)
 
@@ -113,8 +112,8 @@ def test_cfg_file(telescope_model_from_config_file, lst_config_file):
 
     tel_model.export_config_file()
 
-    logger.info("Config file (original): {}".format(lst_config_file))
-    logger.info("Config file (new): {}".format(tel_model.get_config_file()))
+    logger.info(f"Config file (original): {lst_config_file}")
+    logger.info(f"Config file (new): {tel_model.get_config_file()}")
 
     assert filecmp.cmp(lst_config_file, tel_model.get_config_file())
 
@@ -126,7 +125,7 @@ def test_cfg_file(telescope_model_from_config_file, lst_config_file):
         config_file_name=cfg_file,
     )
     tel.export_config_file()
-    logger.info("Config file (sst): {}".format(tel.get_config_file()))
+    logger.info(f"Config file (sst): {tel.get_config_file()}")
     # TODO: testing that file can be written and that it is  different,
     #       but not the file has the
     #       correct contents
diff --git a/tests/unit_tests/test_db_handler.py b/tests/unit_tests/test_db_handler.py
index d194bf407..3d9786b41 100644
--- a/tests/unit_tests/test_db_handler.py
+++ b/tests/unit_tests/test_db_handler.py
@@ -302,9 +302,8 @@ def test_separating_get_and_write(db, io_handler):
         io_handler.get_output_directory(dir_type="model", test=True),
     )
     logger.debug(
-        "Checking files were written to {}".format(
-            io_handler.get_output_directory(dir_type="model", test=True)
-        )
+        "Checking files were written to "
+        f"{io_handler.get_output_directory(dir_type='model', test=True)}"
     )
     for file_now in file_list:
         assert io_handler.get_output_file(file_now, dir_type="model", test=True).exists()
@@ -323,9 +322,8 @@ def test_insert_files_db(db, io_handler, db_cleanup_file_sandbox, caplog):
 
     logger.info("----Testing inserting files to the DB-----")
     logger.info(
-        "Creating a temporary file in {}".format(
-            io_handler.get_output_directory(dir_type="model", test=True)
-        )
+        "Creating a temporary file in "
+        f"{io_handler.get_output_directory(dir_type='model', test=True)}"
     )
     file_name = io_handler.get_output_directory(dir_type="model", test=True) / "test_file.dat"
     with open(file_name, "w") as f:
diff --git a/tests/unit_tests/test_visualize.py b/tests/unit_tests/test_visualize.py
index 6c4f8e1af..3c32f97e1 100644
--- a/tests/unit_tests/test_visualize.py
+++ b/tests/unit_tests/test_visualize.py
@@ -42,7 +42,7 @@ def test_plot_1D(db, io_handler):
     for i in range(5):
         new_data = np.copy(data_in)
         new_data[y_title] = new_data[y_title] * (1 - 0.1 * (i + 1))
-        data["{}%% reflectivity".format(100 * (1 - 0.1 * (i + 1)))] = new_data
+        data[f"{100 * (1 - 0.1 * (i + 1))}%% reflectivity"] = new_data
 
     plt = visualize.plot_1D(data, title=title, palette="autumn")
 
@@ -51,7 +51,7 @@ def test_plot_1D(db, io_handler):
         plot_file.unlink()
     plt.savefig(plot_file)
 
-    logger.debug("Produced 1D plot ({}).".format(plot_file))
+    logger.debug(f"Produced 1D plot ({plot_file}).")
 
     assert plot_file.exists()
 
@@ -80,7 +80,7 @@ def test_plot_table(db, io_handler):
         plot_file.unlink()
     plt.savefig(plot_file)
 
-    logger.debug("Produced 1D plot ({}).".format(plot_file))
+    logger.debug(f"Produced 1D plot ({plot_file}).")
 
     assert plot_file.exists()
 
diff --git a/tests/unit_tests/utils/test_names.py b/tests/unit_tests/utils/test_names.py
index 86891094e..5d75e57ff 100644
--- a/tests/unit_tests/utils/test_names.py
+++ b/tests/unit_tests/utils/test_names.py
@@ -14,9 +14,9 @@ def test_validate_telescope_name():
     telescopes = {"sst-d": "SST-D", "mst-flashcam-d": "MST-FlashCam-D", "sct-d": "SCT-D"}
 
     for key, value in telescopes.items():
-        logging.getLogger().info("Validating {}".format(key))
+        logging.getLogger().info(f"Validating {key}")
         new_name = names.validate_telescope_model_name(key)
-        logging.getLogger().info("New name {}".format(new_name))
+        logging.getLogger().info(f"New name {new_name}")
 
         assert value == new_name
 
@@ -30,9 +30,9 @@ def test_validate_telescope_name_db():
     }
 
     for key, value in telescopes.items():
-        logging.getLogger().info("Validating {}".format(key))
+        logging.getLogger().info(f"Validating {key}")
         new_name = names.validate_telescope_name_db(key)
-        logging.getLogger().info("New name {}".format(new_name))
+        logging.getLogger().info(f"New name {new_name}")
 
         assert value == new_name
 
@@ -43,7 +43,7 @@ def test_validate_telescope_name_db():
     }
 
     for key, value in telescopes.items():
-        logging.getLogger().info("Validating {}".format(key))
+        logging.getLogger().info(f"Validating {key}")
         with pytest.raises(ValueError):
             names.validate_telescope_name_db(key)