From 78e4dc4bcd97c800da3d043e31b4c141055fd912 Mon Sep 17 00:00:00 2001
From: Maik Nijhuis <maik.nijhuis@triopsys.nl>
Date: Wed, 28 Sep 2022 12:19:05 +0000
Subject: [PATCH] Remove singletons

---
 cpp/common/singleton.h                        | 30 --------
 cpp/elementresponse.cc                        |  8 +++
 cpp/elementresponse.h                         | 12 +++-
 cpp/elementresponsefactory.cc                 | 12 ++--
 cpp/hamaker/hamakercoeff.cc                   | 10 +--
 cpp/hamaker/hamakercoeff.h                    | 10 +--
 cpp/hamaker/hamakerelementresponse.cc         | 70 ++++++++-----------
 cpp/hamaker/hamakerelementresponse.h          | 30 ++------
 cpp/lobes/lobeselementresponse.cc             | 51 ++++++--------
 cpp/lobes/lobeselementresponse.h              |  3 +-
 cpp/oskar/oskarelementresponse.cc             | 22 +++---
 cpp/oskar/oskarelementresponse.h              | 31 +++-----
 .../generate_basefunction_plots.py            |  2 +-
 .../make_element_response_image.cpp           |  3 +-
 14 files changed, 117 insertions(+), 177 deletions(-)
 delete mode 100644 cpp/common/singleton.h

diff --git a/cpp/common/singleton.h b/cpp/common/singleton.h
deleted file mode 100644
index 678739f0..00000000
--- a/cpp/common/singleton.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2020 ASTRON (Netherlands Institute for Radio Astronomy)
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#ifndef EVERYBEAM_SINGLETON_H
-#define EVERYBEAM_SINGLETON_H
-
-namespace everybeam {
-namespace common {
-template <typename T>
-struct Singleton {
-  // Factory function to obtain the one-and-only instance of the Singleton
-  static std::shared_ptr<T> GetInstance() {
-    // Static variable, initialized on first call to GetInstance()
-    static std::shared_ptr<T> instance = std::make_shared<T>();
-    return instance;
-  }
-
- private:
-  // Make the constructor private, to prevent direct instantiation
-  // outside of the factory function GetInstance()
-  Singleton() {}
-
-  // Forbid to make copies of the Singleton, by deleting the
-  // copy and assignment constructors
-  Singleton(Singleton const&) = delete;
-  void operator=(Singleton const&) = delete;
-};
-}  // namespace common
-}  // namespace everybeam
-#endif
\ No newline at end of file
diff --git a/cpp/elementresponse.cc b/cpp/elementresponse.cc
index f0438482..6b3a4405 100644
--- a/cpp/elementresponse.cc
+++ b/cpp/elementresponse.cc
@@ -13,6 +13,8 @@
 
 #include <algorithm>
 
+#include "config.h"
+
 #include "common/mathutils.h"
 
 #include "elementresponsefixeddirection.h"
@@ -80,4 +82,10 @@ std::shared_ptr<ElementResponse> ElementResponse::FixateDirection(
       shared_from_this(), thetaphi[0], thetaphi[1]);
 }
 
+std::filesystem::path ElementResponse::GetPath(
+    const std::filesystem::path& relative_path) {
+  // TODO (AST-803) Allow specifying a different data directory at run time.
+  return std::filesystem::path(EVERYBEAM_DATA_DIR) / relative_path;
+}
+
 }  // namespace everybeam
diff --git a/cpp/elementresponse.h b/cpp/elementresponse.h
index 05bd12b2..ba75eefc 100644
--- a/cpp/elementresponse.h
+++ b/cpp/elementresponse.h
@@ -5,6 +5,7 @@
 #define EVERYBEAM_ELEMENTRESPONSE_H
 
 #include <complex>
+#include <filesystem>
 #include <memory>
 #include <ostream>
 #include <vector>
@@ -97,9 +98,18 @@ class ElementResponse : public std::enable_shared_from_this<ElementResponse> {
     return Response(freq, theta, phi);
   }
 
-  static std::shared_ptr<ElementResponse> GetInstance(
+  static std::shared_ptr<const ElementResponse> GetInstance(
       ElementResponseModel model, const std::string& name,
       const Options& options);
+
+ protected:
+  /**
+   * Get the path to an EveryBeam data file or directory.
+   * @param relative_path A path relative to the EveryBeam data directory.
+   * @return The full path of the data file or data directory.
+   */
+  static std::filesystem::path GetPath(
+      const std::filesystem::path& relative_path);
 };
 }  // namespace everybeam
 #endif
diff --git a/cpp/elementresponsefactory.cc b/cpp/elementresponsefactory.cc
index d73ddc7f..374bc825 100644
--- a/cpp/elementresponsefactory.cc
+++ b/cpp/elementresponsefactory.cc
@@ -15,18 +15,18 @@
 
 namespace everybeam {
 
-std::shared_ptr<ElementResponse> ElementResponse::GetInstance(
+std::shared_ptr<const ElementResponse> ElementResponse::GetInstance(
     ElementResponseModel model, const std::string& name,
     const Options& options) {
   switch (model) {
     case ElementResponseModel::kHamaker:
-      return HamakerElementResponse::GetInstance(name);
+      return std::make_shared<HamakerElementResponse>(name);
     case ElementResponseModel::kHamakerLba:
-      return HamakerElementResponse::GetLbaInstance();
+      return std::make_shared<HamakerElementResponse>("LBA");
     case ElementResponseModel::kOSKARDipole:
-      return OSKARElementResponseDipole::GetInstance();
+      return std::make_shared<OSKARElementResponseDipole>();
     case ElementResponseModel::kOSKARSphericalWave:
-      return OSKARElementResponseSphericalWave::GetInstance();
+      return std::make_shared<OSKARElementResponseSphericalWave>();
     case ElementResponseModel::kLOBES:
       try {
         return LOBESElementResponse::GetInstance(name, options);
@@ -35,7 +35,7 @@ std::shared_ptr<ElementResponse> ElementResponse::GetInstance(
                   << " failed because: " << std::endl;
         std::cout << e.what() << std::endl;
         std::cout << "Switching to HamakerElementResponse instead" << std::endl;
-        return GetInstance(ElementResponseModel::kHamaker, name, options);
+        return std::make_shared<HamakerElementResponse>(name);
       }
     default:
       std::stringstream message;
diff --git a/cpp/hamaker/hamakercoeff.cc b/cpp/hamaker/hamakercoeff.cc
index 59964ecc..9e2bef4b 100644
--- a/cpp/hamaker/hamakercoeff.cc
+++ b/cpp/hamaker/hamakercoeff.cc
@@ -12,12 +12,12 @@ H5::CompType GetComplexDoubleType() {
 }
 
 size_t HamakerCoefficients::GetIndex(unsigned int n, unsigned int t,
-                                     unsigned int f) {
+                                     unsigned int f) const {
   return (n * nPowerTheta_ + t) * nPowerFreq_ * nInner_ + f * nInner_;
 }
 
 // Constructor for reading coeff from file
-HamakerCoefficients::HamakerCoefficients(std::string& filename) {
+HamakerCoefficients::HamakerCoefficients(const std::string& filename) {
   ReadCoefficients(filename);
 }
 
@@ -58,13 +58,13 @@ void HamakerCoefficients::SetCoefficients(
 
 void HamakerCoefficients::GetCoefficient(
     unsigned int n, unsigned int t, unsigned int f,
-    std::pair<std::complex<double>, std::complex<double>>& value) {
+    std::pair<std::complex<double>, std::complex<double>>& value) const {
   size_t index = GetIndex(n, t, f);
   value.first = coeff_[index];
   value.second = coeff_[index + 1];
 }
 
-void HamakerCoefficients::ReadCoefficients(std::string& filename) {
+void HamakerCoefficients::ReadCoefficients(const std::string& filename) {
   // Open file
   H5::H5File file(filename, H5F_ACC_RDONLY);
 
@@ -94,7 +94,7 @@ void HamakerCoefficients::ReadCoefficients(std::string& filename) {
   dataset.read(coeff_.data(), data_type, dataspace);
 }
 
-void HamakerCoefficients::WriteCoefficients(std::string& filename) {
+void HamakerCoefficients::WriteCoefficients(const std::string& filename) {
   // Open file
   H5::H5File file(filename, H5F_ACC_TRUNC);
 
diff --git a/cpp/hamaker/hamakercoeff.h b/cpp/hamaker/hamakercoeff.h
index cbcf5c94..da90acd3 100644
--- a/cpp/hamaker/hamakercoeff.h
+++ b/cpp/hamaker/hamakercoeff.h
@@ -21,7 +21,7 @@ class HamakerCoefficients {
   HamakerCoefficients();
 
   //! Constructor for reading coeff from file
-  HamakerCoefficients(std::string& filename);
+  HamakerCoefficients(const std::string& filename);
 
   //! Constructor for writing coeff to file
   HamakerCoefficients(const double freq_center, const double freq_range,
@@ -60,19 +60,19 @@ class HamakerCoefficients {
 
   void GetCoefficient(
       unsigned int n, unsigned int t, unsigned int f,
-      std::pair<std::complex<double>, std::complex<double>>& value);
+      std::pair<std::complex<double>, std::complex<double>>& value) const;
 
   // HDF5 I/O
-  void ReadCoefficients(std::string& filename);
+  void ReadCoefficients(const std::string& filename);
 
-  void WriteCoefficients(std::string& filename);
+  void WriteCoefficients(const std::string& filename);
 
   // Debugging
   void PrintCoefficients();
 
  private:
   // Methods
-  size_t GetIndex(unsigned int n, unsigned int t, unsigned int f);
+  size_t GetIndex(unsigned int n, unsigned int t, unsigned int f) const;
 
   // Parameters
   double freq_center_;
diff --git a/cpp/hamaker/hamakerelementresponse.cc b/cpp/hamaker/hamakerelementresponse.cc
index 45578763..c332e4ac 100644
--- a/cpp/hamaker/hamakerelementresponse.cc
+++ b/cpp/hamaker/hamakerelementresponse.cc
@@ -10,35 +10,30 @@
 #include "config.h"
 
 #include "hamakerelementresponse.h"
-#include "../common/singleton.h"
 
 #include <aocommon/matrix2x2.h>
 
 namespace everybeam {
 
-std::shared_ptr<HamakerElementResponse>
-HamakerElementResponse::GetLbaInstance() {
-  return common::Singleton<HamakerElementResponseLBA>::GetInstance();
-}
-
-std::shared_ptr<HamakerElementResponse> HamakerElementResponse::GetInstance(
-    const std::string& name) {
+HamakerElementResponse::HamakerElementResponse(const std::string& name) {
   if (name.find("LBA") != std::string::npos) {
-    return common::Singleton<HamakerElementResponseLBA>::GetInstance();
-  }
-  if (name.find("HBA") != std::string::npos) {
-    return common::Singleton<HamakerElementResponseHBA>::GetInstance();
+    coefficients_ = cached_lba_coefficients_.lock();
+    if (!coefficients_) {
+      const std::string path = GetPath("HamakerLBACoeff.h5");
+      coefficients_ = std::make_shared<HamakerCoefficients>(path);
+      cached_lba_coefficients_ = coefficients_;
+    }
+  } else if (name.find("HBA") != std::string::npos) {
+    coefficients_ = cached_hba_coefficients_.lock();
+    if (!coefficients_) {
+      const std::string path = GetPath("HamakerHBACoeff.h5");
+      coefficients_ = std::make_shared<HamakerCoefficients>(path);
+      cached_hba_coefficients_ = coefficients_;
+    }
+  } else {
+    throw std::invalid_argument(
+        "HamakerElementResponse: name should end in either 'LBA' or 'HBA'");
   }
-  throw std::invalid_argument(
-      "HamakerElementResponse::GetInstance: name should end in either 'LBA' or "
-      "'HBA'");
-}
-
-std::string HamakerElementResponse::GetPath(const char* filename) const {
-  std::stringstream ss;
-  ss << EVERYBEAM_DATA_DIR << "/";
-  ss << filename;
-  return ss.str();
 }
 
 aocommon::MC2x2 HamakerElementResponse::Response(double freq, double theta,
@@ -50,11 +45,11 @@ aocommon::MC2x2 HamakerElementResponse::Response(double freq, double theta,
     return response;
   }
 
-  const double freq_center = coeffs_->GetFreqCenter();
-  const double freq_range = coeffs_->GetFreqRange();
-  const unsigned int nHarmonics = coeffs_->Get_nHarmonics();
-  const unsigned int nPowerTheta = coeffs_->Get_nPowerTheta();
-  const unsigned int nPowerFreq = coeffs_->Get_nPowerFreq();
+  const double freq_center = coefficients_->GetFreqCenter();
+  const double freq_range = coefficients_->GetFreqRange();
+  const unsigned int nHarmonics = coefficients_->Get_nHarmonics();
+  const unsigned int nPowerTheta = coefficients_->Get_nPowerTheta();
+  const unsigned int nPowerFreq = coefficients_->Get_nPowerFreq();
 
   // The model is parameterized in terms of a normalized frequency in the
   // range [-1, 1]. The appropriate conversion is taken care of below.
@@ -78,19 +73,20 @@ aocommon::MC2x2 HamakerElementResponse::Response(double freq, double theta,
     // start indexing the block of coefficients at the last element
 
     // Evaluate the highest order term.
-    coeffs_->GetCoefficient(k, nPowerTheta - 1, nPowerFreq - 1, P);
+    coefficients_->GetCoefficient(k, nPowerTheta - 1, nPowerFreq - 1, P);
 
     for (unsigned int i = 0; i < nPowerFreq - 1; ++i) {
-      coeffs_->GetCoefficient(k, nPowerTheta - 1, nPowerFreq - i - 2, Pk);
+      coefficients_->GetCoefficient(k, nPowerTheta - 1, nPowerFreq - i - 2, Pk);
       P.first = P.first * freq + Pk.first;
       P.second = P.second * freq + Pk.second;
     }
 
     // Evaluate the remaining terms.
     for (unsigned int j = 0; j < nPowerTheta - 1; ++j) {
-      coeffs_->GetCoefficient(k, nPowerTheta - j - 2, nPowerFreq - 1, Pj);
+      coefficients_->GetCoefficient(k, nPowerTheta - j - 2, nPowerFreq - 1, Pj);
       for (unsigned int i = 0; i < nPowerFreq - 1; ++i) {
-        coeffs_->GetCoefficient(k, nPowerTheta - j - 2, nPowerFreq - i - 2, Pk);
+        coefficients_->GetCoefficient(k, nPowerTheta - j - 2,
+                                      nPowerFreq - i - 2, Pk);
         Pj.first = Pj.first * freq + Pk.first;
         Pj.second = Pj.second * freq + Pk.second;
       }
@@ -115,13 +111,9 @@ aocommon::MC2x2 HamakerElementResponse::Response(double freq, double theta,
   return response;
 }
 
-HamakerElementResponseHBA::HamakerElementResponseHBA() {
-  std::string path = GetPath("HamakerHBACoeff.h5");
-  coeffs_ = std::make_unique<HamakerCoefficients>(path);
-}
+std::weak_ptr<const HamakerCoefficients>
+    HamakerElementResponse::cached_lba_coefficients_;
+std::weak_ptr<const HamakerCoefficients>
+    HamakerElementResponse::cached_hba_coefficients_;
 
-HamakerElementResponseLBA::HamakerElementResponseLBA() {
-  std::string path = GetPath("HamakerLBACoeff.h5");
-  coeffs_ = std::make_unique<HamakerCoefficients>(path);
-}
 }  // namespace everybeam
diff --git a/cpp/hamaker/hamakerelementresponse.h b/cpp/hamaker/hamakerelementresponse.h
index 357fe251..036ed705 100644
--- a/cpp/hamaker/hamakerelementresponse.h
+++ b/cpp/hamaker/hamakerelementresponse.h
@@ -14,6 +14,8 @@ namespace everybeam {
 //! Implementation of the Hamaker response model
 class HamakerElementResponse : public ElementResponse {
  public:
+  explicit HamakerElementResponse(const std::string& name);
+
   ElementResponseModel GetModel() const final override {
     return ElementResponseModel::kHamaker;
   }
@@ -26,29 +28,11 @@ class HamakerElementResponse : public ElementResponse {
    */
   static std::shared_ptr<HamakerElementResponse> GetLbaInstance();
 
-  /**
-   * @brief Get instance of HamakerElementResponse, infer type (LBA/HBA)
-   * from station name.
-   *
-   * @param name Station Name
-   */
-  static std::shared_ptr<HamakerElementResponse> GetInstance(
-      const std::string& name);
-
- protected:
-  std::string GetPath(const char*) const;
-
-  std::unique_ptr<HamakerCoefficients> coeffs_;
-};
-
-class HamakerElementResponseHBA : public HamakerElementResponse {
- public:
-  HamakerElementResponseHBA();
-};
-
-class HamakerElementResponseLBA : public HamakerElementResponse {
- public:
-  HamakerElementResponseLBA();
+ private:
+  // Since coefficients are equal for all LBA and HBA instances, share them.
+  static std::weak_ptr<const HamakerCoefficients> cached_lba_coefficients_;
+  static std::weak_ptr<const HamakerCoefficients> cached_hba_coefficients_;
+  std::shared_ptr<const HamakerCoefficients> coefficients_;
 };
 
 }  // namespace everybeam
diff --git a/cpp/lobes/lobeselementresponse.cc b/cpp/lobes/lobeselementresponse.cc
index 8c82410d..c134cb38 100644
--- a/cpp/lobes/lobeselementresponse.cc
+++ b/cpp/lobes/lobeselementresponse.cc
@@ -71,27 +71,6 @@ static std::optional<AartfaacStation> GetAartfaacStation(
 
 namespace everybeam {
 
-namespace {
-/**
- * @brief Search for LOBES h5 coefficient file
- * on the suggested path \param search_path. Returns
- * an empty string if the file cannot be found.
- *
- * @param search_path Search path
- * @param station_name Station name, as read from MS
- * @return std::string Path to file or empty string if file cannot be found
- */
-std::filesystem::path FindCoeffFile(const std::string& search_path,
-                                    std::string_view station_name) {
-  const std::string station_file = "LOBES_" + std::string{station_name} + ".h5";
-  return search_path.empty()
-             ? std::filesystem::path(std::string{EVERYBEAM_DATA_DIR} +
-                                     std::string{"/lobes"}) /
-                   station_file
-             : std::filesystem::path(search_path) / station_file;
-}
-}  // namespace
-
 static const H5::CompType kH5Dcomplex = [] {
   const std::string REAL("r");
   const std::string IMAG("i");
@@ -163,8 +142,13 @@ LOBESElementResponse::LOBESElementResponse(const std::string& name,
   const std::optional<AartfaacStation> aartfaac_station =
       GetAartfaacStation(name, AartfaacElements::kInner);
 
-  std::filesystem::path coeff_file_path = FindCoeffFile(
-      options.coeff_path, aartfaac_station ? aartfaac_station->station : name);
+  const std::filesystem::path search_path =
+      options.coeff_path.empty() ? GetPath("lobes")
+                                 : std::filesystem::path(options.coeff_path);
+  const std::string_view station_name =
+      aartfaac_station ? aartfaac_station->station : name;
+  const std::string station_file = "LOBES_" + std::string(station_name) + ".h5";
+  const std::filesystem::path coeff_file_path = search_path / station_file;
   H5::H5File h5file;
 
   if (!std::filesystem::exists(coeff_file_path)) {
@@ -278,17 +262,28 @@ aocommon::MC2x2 LOBESElementResponse::Response(
   return response;
 }
 
-std::shared_ptr<LOBESElementResponse> LOBESElementResponse::GetInstance(
+std::shared_ptr<const LOBESElementResponse> LOBESElementResponse::GetInstance(
     const std::string& name, const Options& options) {
-  static std::map<std::string, std::shared_ptr<LOBESElementResponse>>
+  // Using a single LOBESElementResponse object for each name reduces memory
+  // usage since the coefficients are only loaded once.
+  // Using weak pointers in this map ensures that LOBESElementResponse objects,
+  // are deleted when they are no longer used, which saves memory.
+  static std::map<std::string, std::weak_ptr<const LOBESElementResponse>>
       name_response_map;
+  std::shared_ptr<const LOBESElementResponse> instance;
 
   auto entry = name_response_map.find(name);
   if (entry == name_response_map.end()) {
-    entry = name_response_map.insert(
-        entry, {name, std::make_shared<LOBESElementResponse>(name, options)});
+    instance = std::make_shared<const LOBESElementResponse>(name, options);
+    name_response_map.insert({name, instance});
+  } else {
+    instance = entry->second.lock();
+    if (!instance) {
+      instance = std::make_shared<const LOBESElementResponse>(name, options);
+      entry->second = instance;
+    }
   }
-  return entry->second;
+  return instance;
 }
 
 }  // namespace everybeam
diff --git a/cpp/lobes/lobeselementresponse.h b/cpp/lobes/lobeselementresponse.h
index d242cf07..37991769 100644
--- a/cpp/lobes/lobeselementresponse.h
+++ b/cpp/lobes/lobeselementresponse.h
@@ -68,9 +68,8 @@ class LOBESElementResponse : public ElementResponse {
    * @brief Create LOBESElementResponse
    *
    * @param name Station name, e.g. CS302LBA
-   * @return std::shared_ptr<LOBESElementResponse>
    */
-  static std::shared_ptr<LOBESElementResponse> GetInstance(
+  static std::shared_ptr<const LOBESElementResponse> GetInstance(
       const std::string& name, const Options& options);
 
   /**
diff --git a/cpp/oskar/oskarelementresponse.cc b/cpp/oskar/oskarelementresponse.cc
index 94a0cafd..bf6514c3 100644
--- a/cpp/oskar/oskarelementresponse.cc
+++ b/cpp/oskar/oskarelementresponse.cc
@@ -28,15 +28,17 @@ aocommon::MC2x2 OSKARElementResponseDipole::Response(double freq, double theta,
   return response;
 }
 
-OSKARElementResponseSphericalWave::OSKARElementResponseSphericalWave() {
-  std::string path = GetPath("oskar.h5");
-  datafile_.reset(new Datafile(path));
+OSKARElementResponseSphericalWave::OSKARElementResponseSphericalWave()
+    : datafile_(cached_datafile_.lock()) {
+  if (!datafile_) {
+    datafile_ = std::make_shared<Datafile>(GetPath("oskar.h5"));
+    cached_datafile_ = datafile_;
+  }
 }
 
 OSKARElementResponseSphericalWave::OSKARElementResponseSphericalWave(
-    const std::string& path) {
-  datafile_.reset(new Datafile(path));
-}
+    const std::string& filename)
+    : datafile_(std::make_shared<Datafile>(filename)) {}
 
 aocommon::MC2x2 OSKARElementResponseSphericalWave::Response(
     [[maybe_unused]] double freq, [[maybe_unused]] double theta,
@@ -76,12 +78,6 @@ aocommon::MC2x2 OSKARElementResponseSphericalWave::Response(int element_id,
   return response;
 }
 
-std::string OSKARElementResponseSphericalWave::GetPath(
-    const char* filename) const {
-  std::stringstream ss;
-  ss << EVERYBEAM_DATA_DIR << "/";
-  ss << filename;
-  return ss.str();
-}
+std::weak_ptr<Datafile> OSKARElementResponseSphericalWave::cached_datafile_;
 
 }  // namespace everybeam
diff --git a/cpp/oskar/oskarelementresponse.h b/cpp/oskar/oskarelementresponse.h
index e52153c5..f6f8e92f 100644
--- a/cpp/oskar/oskarelementresponse.h
+++ b/cpp/oskar/oskarelementresponse.h
@@ -5,7 +5,6 @@
 #define OSKAR_ELEMENTRESPONSE_H
 
 #include "../elementresponse.h"
-#include "../common/singleton.h"
 
 #include "oskardatafile.h"
 
@@ -16,10 +15,6 @@ namespace everybeam {
 //! Implementation of the OSKAR dipole response model
 class OSKARElementResponseDipole : public ElementResponse {
  public:
-  static std::shared_ptr<OSKARElementResponseDipole> GetInstance() {
-    return common::Singleton<OSKARElementResponseDipole>::GetInstance();
-  }
-
   ElementResponseModel GetModel() const final override {
     return ElementResponseModel::kOSKARDipole;
   }
@@ -31,24 +26,14 @@ class OSKARElementResponseDipole : public ElementResponse {
 //! Implementation of the OSKAR spherical wave response model
 class OSKARElementResponseSphericalWave : public ElementResponse {
  public:
-  /**
-   * A constructor-like static method to instantiate the class
-   *
-   * returns a globally shared instance of the class that is instantiated
-   * in the first call
-   */
-  static std::shared_ptr<OSKARElementResponseSphericalWave> GetInstance() {
-    return common::Singleton<OSKARElementResponseSphericalWave>::GetInstance();
-  }
-
   /** Constructor loading the default coefficients file */
   OSKARElementResponseSphericalWave();
 
-  /** Constructor loading a custom coefficients file
-   *
-   * @param path Path to the coefficients file to load
+  /**
+   * Constructor loading a custom coefficients file
+   * @param filename Filename of HDF5 file with coefficients.
    */
-  OSKARElementResponseSphericalWave(const std::string& path);
+  OSKARElementResponseSphericalWave(const std::string& filename);
 
   ElementResponseModel GetModel() const final override {
     return ElementResponseModel::kOSKARSphericalWave;
@@ -60,10 +45,10 @@ class OSKARElementResponseSphericalWave : public ElementResponse {
   aocommon::MC2x2 Response(int element_id, double freq, double theta,
                            double phi) const final override;
 
- protected:
-  std::string GetPath(const char*) const;
-
-  std::unique_ptr<Datafile> datafile_;
+ private:
+  // This weak pointer allows reusing the default coefficients.
+  static std::weak_ptr<Datafile> cached_datafile_;
+  std::shared_ptr<Datafile> datafile_;
 };
 
 }  // namespace everybeam
diff --git a/demo/comparison-oskar/generate_basefunction_plots.py b/demo/comparison-oskar/generate_basefunction_plots.py
index 2bb7166c..979507e6 100755
--- a/demo/comparison-oskar/generate_basefunction_plots.py
+++ b/demo/comparison-oskar/generate_basefunction_plots.py
@@ -90,7 +90,7 @@ for em_idx in range(2):
             )
 
         subprocess.check_call(
-            ["oskar_csv_to_hdf5.py", "telescope.tm", "oskar.h5"]
+            ["oskar_csv_to_hdf5.py", "telescope.tm", "oskar-comparison.h5"]
         )
         subprocess.check_call(["make_element_response_image", str(npixels)])
 
diff --git a/demo/comparison-oskar/make_element_response_image.cpp b/demo/comparison-oskar/make_element_response_image.cpp
index bc9231f0..24f93588 100644
--- a/demo/comparison-oskar/make_element_response_image.cpp
+++ b/demo/comparison-oskar/make_element_response_image.cpp
@@ -10,7 +10,8 @@
 #include "../../external/npy.hpp"  // to save arrays in numpy format
 
 int main(int argc, char** argv) {
-  everybeam::OSKARElementResponseSphericalWave element_response("oskar.h5");
+  everybeam::OSKARElementResponseSphericalWave element_response(
+      "oskar-comparison.h5");
   double freq = 50e6;
 
   int N;
-- 
GitLab