Repository: libKriging/libKriging (language: python)

Issue:
fix python 311 compatibility



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9b669976..5f9b2339 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -65,7 +65,7 @@ jobs:
             mode: Debug
             enable_python: on
           - name: "macOS Debug"
-            os: macOS-11
+            os: macOS-12
             mode: Debug
             enable_python: on
           - name: "Windows Debug"
@@ -79,7 +79,7 @@ jobs:
             enable_octave: on
             force_update: on
           - name: "Octave macOS"
-            os: macOS-11
+            os: macOS-12
             mode: Release
             enable_python: off
             enable_octave: on
@@ -101,7 +101,7 @@ jobs:
             mode: Release
             enable_R: on
           - name: "R macOS"
-            os: macOS-11
+            os: macOS-12
             mode: Release
             enable_R: on
           - name: "R Windows"
diff --git a/.github/workflows/python-installation.yml b/.github/workflows/python-installation.yml
index edd43b87..a3b7ef32 100644
--- a/.github/workflows/python-installation.yml
+++ b/.github/workflows/python-installation.yml
@@ -15,7 +15,7 @@ jobs:
       fail-fast: false
       matrix:
         python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
-        os: [ubuntu-18.04, ubuntu-20.04, macos-11]
+        os: [ubuntu-18.04, ubuntu-20.04, macos-12]
         exclude: # not supported
           - os: ubuntu-20.04
             python-version: 3.6
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 8ca7fd34..bb632913 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -104,7 +104,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ macOS-11 ]
+        os: [ macOS-12 ]
         python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
         include:
           - os: ubuntu-18.04
@@ -188,7 +188,7 @@ jobs:
             os: ubuntu-20.04
             force_update: on
           - name: "Octave macOS"
-            os: macOS-11
+            os: macOS-12
           - name: "Octave Windows"
             os: windows-2019
 
@@ -270,7 +270,7 @@ jobs:
           - name: "R Linux"
             os: ubuntu-20.04
           - name: "R macOS"
-            os: macOS-11
+            os: macOS-12  
           - name: "R Windows"
             os: windows-2019
             
diff --git a/bindings/Octave/Kriging.m b/bindings/Octave/Kriging.m
index 1219d91d..d116082f 100644
--- a/bindings/Octave/Kriging.m
+++ b/bindings/Octave/Kriging.m
@@ -9,6 +9,10 @@
             % fprintf("New Kriging\n");
             obj.ref = mLibKriging("Kriging::new", varargin{:});
         end
+
+        function varargout = copy(obj, varargin)
+            [varargout{1:nargout}] = mLibKriging("Kriging::copy", obj.ref, varargin{:});
+        end
         
         function delete(obj, varargin)
             % disp(["ObjectRef = ", num2str(obj.ref)])
diff --git a/bindings/Octave/Kriging_binding.cpp b/bindings/Octave/Kriging_binding.cpp
index cd01b6a7..523dc479 100644
--- a/bindings/Octave/Kriging_binding.cpp
+++ b/bindings/Octave/Kriging_binding.cpp
@@ -46,6 +46,17 @@ void build(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs) {
   output.set(0, km, "new object reference");
 }
 
+void copy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs) {
+  MxMapper input{"Input",
+                 nrhs,
+                 const_cast<mxArray**>(prhs),  // NOLINT(cppcoreguidelines-pro-type-const-cast)
+                 RequiresArg::Exactly{1}};
+  MxMapper output{"Output", nlhs, plhs, RequiresArg::Exactly{1}};
+  const auto* km = input.getObjectFromRef<Kriging>(0, "Kriging reference");
+  auto km_copy = buildObject<Kriging>(*km, ExplicitCopySpecifier{});
+  output.set(0, km_copy, "copied object reference");
+}
+
 void destroy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs) {
   MxMapper input{"Input",
                  nrhs,
diff --git a/bindings/Octave/Kriging_binding.hpp b/bindings/Octave/Kriging_binding.hpp
index e3a9df06..c6a316bf 100644
--- a/bindings/Octave/Kriging_binding.hpp
+++ b/bindings/Octave/Kriging_binding.hpp
@@ -5,6 +5,7 @@
 
 namespace KrigingBinding {
 void build(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
+void copy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
 void destroy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
 void fit(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
 void predict(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
diff --git a/bindings/Octave/NoiseKriging.m b/bindings/Octave/NoiseKriging.m
index c70263b3..44de3315 100644
--- a/bindings/Octave/NoiseKriging.m
+++ b/bindings/Octave/NoiseKriging.m
@@ -10,6 +10,10 @@
             obj.ref = mLibKriging("NoiseKriging::new", varargin{:});
         end
         
+        function varargout = copy(obj, varargin)
+            [varargout{1:nargout}] = mLibKriging("NoiseKriging::copy", obj.ref, varargin{:});
+        end
+
         function delete(obj, varargin)
             % disp(["ObjectRef = ", num2str(obj.ref)])
             % destroy the mex backend
diff --git a/bindings/Octave/NoiseKriging_binding.cpp b/bindings/Octave/NoiseKriging_binding.cpp
index 972358d1..8e0adcdb 100644
--- a/bindings/Octave/NoiseKriging_binding.cpp
+++ b/bindings/Octave/NoiseKriging_binding.cpp
@@ -46,6 +46,17 @@ void build(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs) {
   output.set(0, km, "new object reference");
 }
 
+void copy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs) {
+  MxMapper input{"Input",
+                 nrhs,
+                 const_cast<mxArray**>(prhs),  // NOLINT(cppcoreguidelines-pro-type-const-cast)
+                 RequiresArg::Exactly{1}};
+  MxMapper output{"Output", nlhs, plhs, RequiresArg::Exactly{1}};
+  const auto* km = input.getObjectFromRef<NoiseKriging>(0, "NoiseKriging reference");
+  auto km_copy = buildObject<NoiseKriging>(*km, ExplicitCopySpecifier{});
+  output.set(0, km_copy, "copied object reference");
+}
+
 void destroy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs) {
   MxMapper input{"Input",
                  nrhs,
diff --git a/bindings/Octave/NoiseKriging_binding.hpp b/bindings/Octave/NoiseKriging_binding.hpp
index d273aa6b..ca624072 100644
--- a/bindings/Octave/NoiseKriging_binding.hpp
+++ b/bindings/Octave/NoiseKriging_binding.hpp
@@ -5,6 +5,7 @@
 
 namespace NoiseKrigingBinding {
 void build(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
+void copy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
 void destroy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
 void fit(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
 void predict(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
diff --git a/bindings/Octave/NuggetKriging.m b/bindings/Octave/NuggetKriging.m
index 482b8a50..1d2d3dff 100644
--- a/bindings/Octave/NuggetKriging.m
+++ b/bindings/Octave/NuggetKriging.m
@@ -9,7 +9,11 @@
             % printf("New NuggetKriging\n");
             obj.ref = mLibKriging("NuggetKriging::new", varargin{:});
         end
-        
+
+        function varargout = copy(obj, varargin)
+            [varargout{1:nargout}] = mLibKriging("NuggetKriging::copy", obj.ref, varargin{:});
+        end
+
         function delete(obj, varargin)
             % disp(["ObjectRef = ", num2str(obj.ref)])
             % destroy the mex backend
diff --git a/bindings/Octave/NuggetKriging_binding.cpp b/bindings/Octave/NuggetKriging_binding.cpp
index 51a30fb7..69e1bd03 100644
--- a/bindings/Octave/NuggetKriging_binding.cpp
+++ b/bindings/Octave/NuggetKriging_binding.cpp
@@ -47,6 +47,17 @@ void build(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs) {
   output.set(0, km, "new object reference");
 }
 
+void copy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs) {
+  MxMapper input{"Input",
+                 nrhs,
+                 const_cast<mxArray**>(prhs),  // NOLINT(cppcoreguidelines-pro-type-const-cast)
+                 RequiresArg::Exactly{1}};
+  MxMapper output{"Output", nlhs, plhs, RequiresArg::Exactly{1}};
+  const auto* km = input.getObjectFromRef<NuggetKriging>(0, "NuggetKriging reference");
+  auto km_copy = buildObject<NuggetKriging>(*km, ExplicitCopySpecifier{});
+  output.set(0, km_copy, "copied object reference");
+}
+
 void destroy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs) {
   MxMapper input{"Input",
                  nrhs,
diff --git a/bindings/Octave/NuggetKriging_binding.hpp b/bindings/Octave/NuggetKriging_binding.hpp
index f6990b92..037cc6f7 100644
--- a/bindings/Octave/NuggetKriging_binding.hpp
+++ b/bindings/Octave/NuggetKriging_binding.hpp
@@ -5,6 +5,7 @@
 
 namespace NuggetKrigingBinding {
 void build(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
+void copy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
 void destroy(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
 void fit(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
 void predict(int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs);
diff --git a/bindings/Octave/mLibKriging.cpp b/bindings/Octave/mLibKriging.cpp
index 733b638e..3bc61fe1 100644
--- a/bindings/Octave/mLibKriging.cpp
+++ b/bindings/Octave/mLibKriging.cpp
@@ -73,6 +73,8 @@ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) try
 
     case "Kriging::new"_hash:
       return KrigingBinding::build(nlhs, plhs, nrhs - 1, prhs + 1);
+    case "Kriging::copy"_hash:
+      return KrigingBinding::copy(nlhs, plhs, nrhs - 1, prhs + 1);
     case "Kriging::delete"_hash:
       return KrigingBinding::destroy(nlhs, plhs, nrhs - 1, prhs + 1);
     case "Kriging::fit"_hash:
@@ -143,6 +145,8 @@ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) try
 
     case "NuggetKriging::new"_hash:
       return NuggetKrigingBinding::build(nlhs, plhs, nrhs - 1, prhs + 1);
+    case "NuggetKriging::copy"_hash:
+      return NuggetKrigingBinding::copy(nlhs, plhs, nrhs - 1, prhs + 1);
     case "NuggetKriging::delete"_hash:
       return NuggetKrigingBinding::destroy(nlhs, plhs, nrhs - 1, prhs + 1);
     case "NuggetKriging::fit"_hash:
@@ -213,6 +217,8 @@ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) try
 
     case "NoiseKriging::new"_hash:
       return NoiseKrigingBinding::build(nlhs, plhs, nrhs - 1, prhs + 1);
+    case "NoiseKriging::copy"_hash:
+      return NoiseKrigingBinding::copy(nlhs, plhs, nrhs - 1, prhs + 1);
     case "NoiseKriging::delete"_hash:
       return NoiseKrigingBinding::destroy(nlhs, plhs, nrhs - 1, prhs + 1);
     case "NoiseKriging::fit"_hash:
diff --git a/bindings/Octave/tools/ObjectAccessor.hpp b/bindings/Octave/tools/ObjectAccessor.hpp
index 535fa07e..8e43d242 100644
--- a/bindings/Octave/tools/ObjectAccessor.hpp
+++ b/bindings/Octave/tools/ObjectAccessor.hpp
@@ -11,7 +11,7 @@ struct ObjectRef {};
 struct EmptyObject {};
 
 template <typename T, typename... Args>
-ObjectCollector::ref_t buildObject(Args... args) {
+ObjectCollector::ref_t buildObject(Args&&... args) {
   return ObjectCollector::registerObject(new T{args...});
 }
 
diff --git a/bindings/Python/src/_pylibkriging/Kriging_binding.cpp b/bindings/Python/src/_pylibkriging/Kriging_binding.cpp
index c588ffd0..82ba3163 100644
--- a/bindings/Python/src/_pylibkriging/Kriging_binding.cpp
+++ b/bindings/Python/src/_pylibkriging/Kriging_binding.cpp
@@ -46,6 +46,13 @@ PyKriging::PyKriging(const py::array_t<double>& y,
 
 PyKriging::~PyKriging() {}
 
+PyKriging::PyKriging(const PyKriging& other)
+    : m_internal{std::make_unique<Kriging>(*other.m_internal, ExplicitCopySpecifier{})} {}
+
+PyKriging PyKriging::copy() const {
+  return PyKriging(*this);
+}
+
 void PyKriging::fit(const py::array_t<double>& y,
                     const py::array_t<double>& X,
                     const Trend::RegressionModel& regmodel,
diff --git a/bindings/Python/src/_pylibkriging/Kriging_binding.hpp b/bindings/Python/src/_pylibkriging/Kriging_binding.hpp
index f2c30e10..cf39e464 100644
--- a/bindings/Python/src/_pylibkriging/Kriging_binding.hpp
+++ b/bindings/Python/src/_pylibkriging/Kriging_binding.hpp
@@ -13,6 +13,9 @@
 namespace py = pybind11;
 
 class PyKriging {
+ private:
+  PyKriging(std::unique_ptr<Kriging>&& internal) : m_internal(std::move(internal)) {}
+
  public:
   PyKriging(const std::string& kernel);
   PyKriging(const py::array_t<double>& y,
@@ -33,6 +36,10 @@ class PyKriging {
             const py::dict& dict);
   ~PyKriging();
 
+  PyKriging(const PyKriging& other);
+
+  [[nodiscard]] PyKriging copy() const;
+
   void fit(const py::array_t<double>& y,
            const py::array_t<double>& X,
            const Trend::RegressionModel& regmodel,
diff --git a/bindings/Python/src/_pylibkriging/NoiseKriging_binding.cpp b/bindings/Python/src/_pylibkriging/NoiseKriging_binding.cpp
index 9e37a670..86d89946 100644
--- a/bindings/Python/src/_pylibkriging/NoiseKriging_binding.cpp
+++ b/bindings/Python/src/_pylibkriging/NoiseKriging_binding.cpp
@@ -52,6 +52,13 @@ PyNoiseKriging::PyNoiseKriging(const py::array_t<double>& y,
 
 PyNoiseKriging::~PyNoiseKriging() {}
 
+PyNoiseKriging::PyNoiseKriging(const PyNoiseKriging& other)
+    : m_internal{std::make_unique<NoiseKriging>(*other.m_internal, ExplicitCopySpecifier{})} {}
+
+PyNoiseKriging PyNoiseKriging::copy() const {
+  return PyNoiseKriging(*this);
+}
+
 void PyNoiseKriging::fit(const py::array_t<double>& y,
                          const py::array_t<double>& noise,
                          const py::array_t<double>& X,
diff --git a/bindings/Python/src/_pylibkriging/NoiseKriging_binding.hpp b/bindings/Python/src/_pylibkriging/NoiseKriging_binding.hpp
index c9fde0ef..8b08dc74 100644
--- a/bindings/Python/src/_pylibkriging/NoiseKriging_binding.hpp
+++ b/bindings/Python/src/_pylibkriging/NoiseKriging_binding.hpp
@@ -13,6 +13,9 @@
 namespace py = pybind11;
 
 class PyNoiseKriging {
+ private:
+  PyNoiseKriging(std::unique_ptr<NoiseKriging>&& internal) : m_internal(std::move(internal)) {}
+
  public:
   PyNoiseKriging(const std::string& kernel);
   PyNoiseKriging(const py::array_t<double>& y,
@@ -35,6 +38,10 @@ class PyNoiseKriging {
                  const py::dict& dict);
   ~PyNoiseKriging();
 
+  PyNoiseKriging(const PyNoiseKriging& other);
+
+  PyNoiseKriging copy() const;
+
   void fit(const py::array_t<double>& y,
            const py::array_t<double>& noise,
            const py::array_t<double>& X,
diff --git a/bindings/Python/src/_pylibkriging/NuggetKriging_binding.cpp b/bindings/Python/src/_pylibkriging/NuggetKriging_binding.cpp
index 0f6acb26..3b267840 100644
--- a/bindings/Python/src/_pylibkriging/NuggetKriging_binding.cpp
+++ b/bindings/Python/src/_pylibkriging/NuggetKriging_binding.cpp
@@ -50,6 +50,13 @@ PyNuggetKriging::PyNuggetKriging(const py::array_t<double>& y,
 
 PyNuggetKriging::~PyNuggetKriging() {}
 
+PyNuggetKriging::PyNuggetKriging(const PyNuggetKriging& other)
+    : m_internal{std::make_unique<NuggetKriging>(*other.m_internal, ExplicitCopySpecifier{})} {}
+
+PyNuggetKriging PyNuggetKriging::copy() const {
+  return PyNuggetKriging(*this);
+}
+
 void PyNuggetKriging::fit(const py::array_t<double>& y,
                           const py::array_t<double>& X,
                           const Trend::RegressionModel& regmodel,
@@ -210,4 +217,4 @@ double PyNuggetKriging::nugget() {
 
 bool PyNuggetKriging::is_nugget_estim() {
   return m_internal->is_nugget_estim();
-}
\ No newline at end of file
+}
diff --git a/bindings/Python/src/_pylibkriging/NuggetKriging_binding.hpp b/bindings/Python/src/_pylibkriging/NuggetKriging_binding.hpp
index 07ee1094..0e5ebb49 100644
--- a/bindings/Python/src/_pylibkriging/NuggetKriging_binding.hpp
+++ b/bindings/Python/src/_pylibkriging/NuggetKriging_binding.hpp
@@ -13,6 +13,9 @@
 namespace py = pybind11;
 
 class PyNuggetKriging {
+ private:
+  PyNuggetKriging(std::unique_ptr<NuggetKriging>&& internal) : m_internal(std::move(internal)) {}
+
  public:
   PyNuggetKriging(const std::string& kernel);
   PyNuggetKriging(const py::array_t<double>& y,
@@ -33,6 +36,10 @@ class PyNuggetKriging {
                   const py::dict& dict);
   ~PyNuggetKriging();
 
+  PyNuggetKriging(const PyNuggetKriging& other);
+
+  PyNuggetKriging copy() const;
+
   void fit(const py::array_t<double>& y,
            const py::array_t<double>& X,
            const Trend::RegressionModel& regmodel,
diff --git a/bindings/Python/src/_pylibkriging/pylibkriging.cpp b/bindings/Python/src/_pylibkriging/pylibkriging.cpp
index 43d0d19b..5117fb80 100644
--- a/bindings/Python/src/_pylibkriging/pylibkriging.cpp
+++ b/bindings/Python/src/_pylibkriging/pylibkriging.cpp
@@ -122,6 +122,8 @@ PYBIND11_MODULE(_pylibkriging, m) {
            py::arg("optim") = default_optim,
            py::arg("objective") = default_objective,
            py::arg("parameters") = py::dict{})
+      .def(py::init<const PyKriging&>())
+      .def("copy", &PyKriging::copy)
       .def("fit", &PyKriging::fit)
       .def("predict", &PyKriging::predict)
       .def("simulate", &PyKriging::simulate)
@@ -155,67 +157,6 @@ PYBIND11_MODULE(_pylibkriging, m) {
       .def("sigma2", &PyKriging::sigma2)
       .def("is_sigma2_estim", &PyKriging::is_sigma2_estim);
 
-  // Automated mapper
-  py::class_<Kriging>(m, "PyKriging")
-      .def(py::init<const std::string&>())
-      .def(py::init<const arma::colvec&,
-                    const arma::mat&,
-                    const std::string&,
-                    const Trend::RegressionModel&,
-                    bool,
-                    const std::string&,
-                    const std::string&,
-                    const Kriging::Parameters&>(),
-           py::arg("y"),
-           py::arg("X"),
-           py::arg("kernel"),
-           py::arg("regmodel") = default_regmodel,
-           py::arg("normalize") = default_normalize,
-           py::arg("optim") = default_optim,
-           py::arg("objective") = default_objective,
-           py::arg("parameters") = Kriging::Parameters{})
-      .def("fit",
-           &Kriging::fit,
-           py::arg("y"),
-           py::arg("X"),
-           py::arg("regmodel") = default_regmodel,
-           py::arg("normalize") = default_normalize,
-           py::arg("optim") = default_optim,
-           py::arg("objective") = default_objective,
-           py::arg("parameters") = Kriging::Parameters{})
-      .def("predict", &Kriging::predict)
-      .def("simulate", &Kriging::simulate)
-      .def("update", &Kriging::update)
-      .def("summary", &Kriging::summary)
-      .def("leaveOneOutFun", &Kriging::leaveOneOutFun)
-      .def("logLikelihoodFun", &Kriging::logLikelihoodFun)
-      .def("logMargPostFun", &Kriging::logMargPostFun)
-      .def("leaveOneOut", &Kriging::leaveOneOut)
-      .def("logLikelihood", &Kriging::logLikelihood)
-      .def("logMargPost", &Kriging::logMargPost)
-
-      .def("kernel", &Kriging::kernel)
-      .def("optim", &Kriging::optim)
-      .def("objective", &Kriging::objective)
-      .def("X", &Kriging::X)
-      .def("centerX", &Kriging::centerX)
-      .def("scaleX", &Kriging::scaleX)
-      .def("y", &Kriging::y)
-      .def("centerY", &Kriging::centerY)
-      .def("scaleY", &Kriging::scaleY)
-      .def("normalize", &Kriging::normalize)
-      .def("regmodel", &Kriging::regmodel)
-      .def("F", &Kriging::F)
-      .def("T", &Kriging::T)
-      .def("M", &Kriging::M)
-      .def("z", &Kriging::z)
-      .def("beta", &Kriging::beta)
-      .def("is_beta_estim", &Kriging::is_beta_estim)
-      .def("theta", &Kriging::theta)
-      .def("is_theta_estim", &Kriging::is_theta_estim)
-      .def("sigma2", &Kriging::sigma2)
-      .def("is_sigma2_estim", &Kriging::is_sigma2_estim);
-
   // Quick and dirty manual wrapper (cf optional argument mapping)
   py::class_<NuggetKriging::Parameters>(m, "NuggetKrigingParameters").def(py::init<>());
 
@@ -239,6 +180,8 @@ PYBIND11_MODULE(_pylibkriging, m) {
            py::arg("optim") = default_optim,
            py::arg("objective") = default_objective,
            py::arg("parameters") = py::dict{})
+      .def(py::init<const PyNuggetKriging&>())
+      .def("copy", &PyNuggetKriging::copy)
       .def("fit", &PyNuggetKriging::fit)
       .def("predict", &PyNuggetKriging::predict)
       .def("simulate", &PyNuggetKriging::simulate)
@@ -273,67 +216,6 @@ PYBIND11_MODULE(_pylibkriging, m) {
       .def("nugget", &PyNuggetKriging::nugget)
       .def("is_nugget_estim", &PyNuggetKriging::is_nugget_estim);
 
-  // Automated mapper
-  py::class_<NuggetKriging>(m, "PyNuggetKriging")
-      .def(py::init<const std::string&>())
-      .def(py::init<const arma::colvec&,
-                    const arma::mat&,
-                    const std::string&,
-                    const Trend::RegressionModel&,
-                    bool,
-                    const std::string&,
-                    const std::string&,
-                    const NuggetKriging::Parameters&>(),
-           py::arg("y"),
-           py::arg("X"),
-           py::arg("kernel"),
-           py::arg("regmodel") = default_regmodel,
-           py::arg("normalize") = default_normalize,
-           py::arg("optim") = default_optim,
-           py::arg("objective") = default_objective,
-           py::arg("parameters") = NuggetKriging::Parameters{})
-      .def("fit",
-           &NuggetKriging::fit,
-           py::arg("y"),
-           py::arg("X"),
-           py::arg("regmodel") = default_regmodel,
-           py::arg("normalize") = default_normalize,
-           py::arg("optim") = default_optim,
-           py::arg("objective") = default_objective,
-           py::arg("parameters") = NuggetKriging::Parameters{})
-      .def("predict", &NuggetKriging::predict)
-      .def("simulate", &NuggetKriging::simulate)
-      .def("update", &NuggetKriging::update)
-      .def("summary", &NuggetKriging::summary)
-      .def("logLikelihoodFun", &NuggetKriging::logLikelihoodFun)
-      .def("logMargPostFun", &NuggetKriging::logMargPostFun)
-      .def("logLikelihood", &NuggetKriging::logLikelihood)
-      .def("logMargPost", &NuggetKriging::logMargPost)
-
-      .def("kernel", &NuggetKriging::kernel)
-      .def("optim", &NuggetKriging::optim)
-      .def("objective", &NuggetKriging::objective)
-      .def("X", &NuggetKriging::X)
-      .def("centerX", &NuggetKriging::centerX)
-      .def("scaleX", &NuggetKriging::scaleX)
-      .def("y", &NuggetKriging::y)
-      .def("centerY", &NuggetKriging::centerY)
-      .def("scaleY", &NuggetKriging::scaleY)
-      .def("normalize", &NuggetKriging::normalize)
-      .def("regmodel", &NuggetKriging::regmodel)
-      .def("F", &NuggetKriging::F)
-      .def("T", &NuggetKriging::T)
-      .def("M", &NuggetKriging::M)
-      .def("z", &NuggetKriging::z)
-      .def("beta", &NuggetKriging::beta)
-      .def("is_beta_estim", &NuggetKriging::is_beta_estim)
-      .def("theta", &NuggetKriging::theta)
-      .def("is_theta_estim", &NuggetKriging::is_theta_estim)
-      .def("sigma2", &NuggetKriging::sigma2)
-      .def("is_sigma2_estim", &NuggetKriging::is_sigma2_estim)
-      .def("nugget", &NuggetKriging::nugget)
-      .def("is_nugget_estim", &NuggetKriging::is_nugget_estim);
-
   // Quick and dirty manual wrapper (cf optional argument mapping)
   py::class_<NoiseKriging::Parameters>(m, "NoiseKrigingParameters").def(py::init<>());
 
@@ -359,6 +241,8 @@ PYBIND11_MODULE(_pylibkriging, m) {
            py::arg("optim") = default_optim,
            py::arg("objective") = default_objective,
            py::arg("parameters") = py::dict{})
+      .def(py::init<const PyNoiseKriging&>())
+      .def("copy", &PyNoiseKriging::copy)
       .def("fit", &PyNoiseKriging::fit)
       .def("predict", &PyNoiseKriging::predict)
       .def("simulate", &PyNoiseKriging::simulate)
@@ -389,65 +273,4 @@ PYBIND11_MODULE(_pylibkriging, m) {
       .def("is_theta_estim", &PyNoiseKriging::is_theta_estim)
       .def("sigma2", &PyNoiseKriging::sigma2)
       .def("is_sigma2_estim", &PyNoiseKriging::is_sigma2_estim);
-
-  // Automated mapper
-  py::class_<NoiseKriging>(m, "PyNoiseKriging")
-      .def(py::init<const std::string&>())
-      .def(py::init<const arma::colvec&,
-                    const arma::colvec&,
-                    const arma::mat&,
-                    const std::string&,
-                    const Trend::RegressionModel&,
-                    bool,
-                    const std::string&,
-                    const std::string&,
-                    const NoiseKriging::Parameters&>(),
-           py::arg("y"),
-           py::arg("noise"),
-           py::arg("X"),
-           py::arg("kernel"),
-           py::arg("regmodel") = default_regmodel,
-           py::arg("normalize") = default_normalize,
-           py::arg("optim") = default_optim,
-           py::arg("objective") = default_objective,
-           py::arg("parameters") = NoiseKriging::Parameters{})
-      .def("fit",
-           &NoiseKriging::fit,
-           py::arg("y"),
-           py::arg("noise"),
-           py::arg("X"),
-           py::arg("regmodel") = default_regmodel,
-           py::arg("normalize") = default_normalize,
-           py::arg("optim") = default_optim,
-           py::arg("objective") = default_objective,
-           py::arg("parameters") = NoiseKriging::Parameters{})
-      .def("predict", &NoiseKriging::predict)
-      .def("simulate", &NoiseKriging::simulate)
-      .def("update", &NoiseKriging::update)
-      .def("summary", &NoiseKriging::summary)
-      .def("logLikelihoodFun", &NoiseKriging::logLikelihoodFun)
-      .def("logLikelihood", &NoiseKriging::logLikelihood)
-
-      .def("kernel", &NoiseKriging::kernel)
-      .def("optim", &NoiseKriging::optim)
-      .def("objective", &NoiseKriging::objective)
-      .def("X", &NoiseKriging::X)
-      .def("centerX", &NoiseKriging::centerX)
-      .def("scaleX", &NoiseKriging::scaleX)
-      .def("y", &NoiseKriging::y)
-      .def("centerY", &NoiseKriging::centerY)
-      .def("scaleY", &NoiseKriging::scaleY)
-      .def("noise", &NoiseKriging::noise)
-      .def("normalize", &NoiseKriging::normalize)
-      .def("regmodel", &NoiseKriging::regmodel)
-      .def("F", &NoiseKriging::F)
-      .def("T", &NoiseKriging::T)
-      .def("M", &NoiseKriging::M)
-      .def("z", &NoiseKriging::z)
-      .def("beta", &NoiseKriging::beta)
-      .def("is_beta_estim", &NoiseKriging::is_beta_estim)
-      .def("theta", &NoiseKriging::theta)
-      .def("is_theta_estim", &NoiseKriging::is_theta_estim)
-      .def("sigma2", &NoiseKriging::sigma2)
-      .def("is_sigma2_estim", &NoiseKriging::is_sigma2_estim);
 }
\ No newline at end of file
diff --git a/bindings/R/rlibkriging/R/KrigingClass.R b/bindings/R/rlibkriging/R/KrigingClass.R
index 00a5368c..82b55d51 100644
--- a/bindings/R/rlibkriging/R/KrigingClass.R
+++ b/bindings/R/rlibkriging/R/KrigingClass.R
@@ -769,3 +769,31 @@ logMargPostFun.Kriging <- function(object, theta, grad = FALSE, ...) {
 logMargPost.Kriging <- function(object, ...) {
   return(kriging_logMargPost(object))
 }
+
+## ****************************************************************************
+#' Duplicate a Kriging Model
+#' 
+#' @author Yann Richet \email{yann.richet@irsn.fr}
+#' 
+#' @param object An S3 Kriging object.
+#' @param ... Not used.
+#' 
+#' @return The copy of object.
+#' 
+#' @method copy Kriging
+#' @export 
+#' @aliases copy,Kriging,Kriging-method
+#' 
+#' @examples
+#' f <- function(x) 1 - 1 / 2 * (sin(12 * x) / (1 + x) + 2 * cos(7 * x) * x^5 + 0.7)
+#' set.seed(123)
+#' X <- as.matrix(runif(10))
+#' y <- f(X)
+#' 
+#' k <- Kriging(y, X, kernel = "matern3_2", objective="LMP")
+#' print(k)
+#' 
+#' print(copy(k))
+copy.Kriging <- function(object, ...) {
+  return(kriging_copy(object))
+}
diff --git a/bindings/R/rlibkriging/R/NoiseKrigingClass.R b/bindings/R/rlibkriging/R/NoiseKrigingClass.R
index 0d8ee75a..347b603f 100644
--- a/bindings/R/rlibkriging/R/NoiseKrigingClass.R
+++ b/bindings/R/rlibkriging/R/NoiseKrigingClass.R
@@ -105,7 +105,7 @@ NoiseKriging <- function(y, noise, X, kernel,
         }
     }
     # This will allow to access kriging data/props using `k$d()`
-    for (d in c('kernel','optim','objective','X','centerX','scaleX','y','noise','centerY','scaleY','regmodel','F','T','M','z','beta','is_beta_estim','theta','is_theta_estim','sigma2','is_sigma2_estim')) {
+    for (d in c('copy','kernel','optim','objective','X','centerX','scaleX','y','noise','centerY','scaleY','regmodel','F','T','M','z','beta','is_beta_estim','theta','is_theta_estim','sigma2','is_sigma2_estim')) {
         eval(parse(text=paste0(
             "nk$", d, " <- function() noisekriging_", d, "(nk)"
             )))
@@ -598,3 +598,31 @@ logLikelihoodFun.NoiseKriging <- function(object, theta_sigma2,
 logLikelihood.NoiseKriging <- function(object, ...) {
   return(noisekriging_logLikelihood(object))
 }
+
+## ****************************************************************************
+#' Duplicate a NoiseKriging Model
+#' 
+#' @author Yann Richet \email{yann.richet@irsn.fr}
+#' 
+#' @param object An S3 NoiseKriging object.
+#' @param ... Not used.
+#' 
+#' @return The copy of object.
+#' 
+#' @method copy NoiseKriging
+#' @export 
+#' @aliases copy,NoiseKriging,NoiseKriging-method
+#' 
+#' @examples
+#' f <- function(x) 1 - 1 / 2 * (sin(12 * x) / (1 + x) + 2 * cos(7 * x) * x^5 + 0.7)
+#' set.seed(123)
+#' X <- as.matrix(runif(10))
+#' y <- f(X) + X/10 * rnorm(nrow(X))
+#' 
+#' k <- NoiseKriging(y, (X/10)^2, X, kernel = "matern3_2", objective="LL")
+#' print(k)
+#' 
+#' print(copy(k))
+copy.NoiseKriging <- function(object, ...) {
+  return(noisekriging_copy(object))
+}
diff --git a/bindings/R/rlibkriging/R/NuggetKrigingClass.R b/bindings/R/rlibkriging/R/NuggetKrigingClass.R
index afa8e716..79bf695a 100644
--- a/bindings/R/rlibkriging/R/NuggetKrigingClass.R
+++ b/bindings/R/rlibkriging/R/NuggetKrigingClass.R
@@ -102,7 +102,7 @@ NuggetKriging <- function(y, X, kernel,
         }
     }
     # This will allow to access kriging data/props using `k$d()`
-    for (d in c('kernel','optim','objective','X','centerX','scaleX','y','centerY','scaleY','regmodel','F','T','M','z','beta','is_beta_estim','theta','is_theta_estim','sigma2','is_sigma2_estim','nugget','is_nugget_estim')) {
+    for (d in c('copy','kernel','optim','objective','X','centerX','scaleX','y','centerY','scaleY','regmodel','F','T','M','z','beta','is_beta_estim','theta','is_theta_estim','sigma2','is_sigma2_estim','nugget','is_nugget_estim')) {
         eval(parse(text=paste0(
             "nk$", d, " <- function() nuggetkriging_", d, "(nk)"
             )))
@@ -696,3 +696,31 @@ logMargPostFun.NuggetKriging <- function(object, theta_alpha, grad = FALSE, ...)
 logMargPost.NuggetKriging <- function(object, ...) {
   return(nuggetkriging_logMargPost(object))
 }
+
+## ****************************************************************************
+#' Duplicate a NuggetKriging Model
+#' 
+#' @author Yann Richet \email{yann.richet@irsn.fr}
+#' 
+#' @param object An S3 NuggetKriging object.
+#' @param ... Not used.
+#' 
+#' @return The copy of object.
+#' 
+#' @method copy NuggetKriging
+#' @export 
+#' @aliases copy,NuggetKriging,NuggetKriging-method
+#' 
+#' @examples
+#' f <- function(x) 1 - 1 / 2 * (sin(12 * x) / (1 + x) + 2 * cos(7 * x) * x^5 + 0.7)
+#' set.seed(123)
+#' X <- as.matrix(runif(10))
+#' y <- f(X) + 0.1 * rnorm(nrow(X))
+#' 
+#' k <- NuggetKriging(y, X, kernel = "matern3_2", objective="LMP")
+#' print(k)
+#' 
+#' print(copy(k))
+copy.NuggetKriging <- function(object, ...) {
+  return(nuggetkriging_copy(object))
+}
\ No newline at end of file
diff --git a/bindings/R/rlibkriging/R/allGenerics.R b/bindings/R/rlibkriging/R/allGenerics.R
index 8d4d77f6..b08dff4e 100644
--- a/bindings/R/rlibkriging/R/allGenerics.R
+++ b/bindings/R/rlibkriging/R/allGenerics.R
@@ -135,3 +135,18 @@ logLikelihood <- function (object, ...) {
 logMargPost <- function (object, ...) {
     UseMethod("logMargPost")
 }
+
+## *****************************************************************************
+##' Duplicate a model given in
+##' \code{object}.
+##'
+##' @title Duplicate object.
+##'
+##' @param object An object representing a fitted model.
+##' @param ... Ignored.
+##'
+##' @return The copied object.
+##' @export
+copy <- function (object, ...) {
+    UseMethod("copy")
+}
diff --git a/bindings/R/rlibkriging/src/kriging_binding.cpp b/bindings/R/rlibkriging/src/kriging_binding.cpp
index 80163fc5..e69a2ebb 100644
--- a/bindings/R/rlibkriging/src/kriging_binding.cpp
+++ b/bindings/R/rlibkriging/src/kriging_binding.cpp
@@ -104,6 +104,20 @@ Rcpp::List new_Kriging(arma::vec y,
   return obj;
 }
 
+// [[Rcpp::export]]
+Rcpp::List kriging_copy(Rcpp::List k) {
+  if (!k.inherits("Kriging"))
+    Rcpp::stop("Input must be a Kriging object.");
+  SEXP impl = k.attr("object");
+  Rcpp::XPtr<Kriging> impl_ptr(impl);
+
+  Rcpp::List obj;
+  Rcpp::XPtr<Kriging> impl_copy(new Kriging(*impl_ptr, ExplicitCopySpecifier{}));
+  obj.attr("object") = impl_copy;
+  obj.attr("class") = "Kriging";
+  return obj;
+}
+
 // [[Rcpp::export]]
 Rcpp::List kriging_model(Rcpp::List k) {
   if (!k.inherits("Kriging"))
diff --git a/bindings/R/rlibkriging/src/noisekriging_binding.cpp b/bindings/R/rlibkriging/src/noisekriging_binding.cpp
index 07099962..be01b57f 100644
--- a/bindings/R/rlibkriging/src/noisekriging_binding.cpp
+++ b/bindings/R/rlibkriging/src/noisekriging_binding.cpp
@@ -106,6 +106,20 @@ Rcpp::List new_NoiseKriging(arma::vec y,
   return obj;
 }
 
+// [[Rcpp::export]]
+Rcpp::List noisekriging_copy(Rcpp::List k) {
+  if (!k.inherits("NoiseKriging"))
+    Rcpp::stop("Input must be a NoiseKriging object.");
+  SEXP impl = k.attr("object");
+  Rcpp::XPtr<NoiseKriging> impl_ptr(impl);
+
+  Rcpp::List obj;
+  Rcpp::XPtr<NoiseKriging> impl_copy(new NoiseKriging(*impl_ptr, ExplicitCopySpecifier{}));
+  obj.attr("object") = impl_copy;
+  obj.attr("class") = "NoiseKriging";
+  return obj;
+}
+
 // [[Rcpp::export]]
 Rcpp::List noisekriging_model(Rcpp::List k) {
   if (!k.inherits("NoiseKriging"))
diff --git a/bindings/R/rlibkriging/src/nuggetkriging_binding.cpp b/bindings/R/rlibkriging/src/nuggetkriging_binding.cpp
index a6b27bbf..e175c3ef 100644
--- a/bindings/R/rlibkriging/src/nuggetkriging_binding.cpp
+++ b/bindings/R/rlibkriging/src/nuggetkriging_binding.cpp
@@ -123,6 +123,20 @@ Rcpp::List new_NuggetKriging(arma::vec y,
   return obj;
 }
 
+// [[Rcpp::export]]
+Rcpp::List nuggetkriging_copy(Rcpp::List k) {
+  if (!k.inherits("NuggetKriging"))
+    Rcpp::stop("Input must be a NoiseKriging object.");
+  SEXP impl = k.attr("object");
+  Rcpp::XPtr<NuggetKriging> impl_ptr(impl);
+
+  Rcpp::List obj;
+  Rcpp::XPtr<NuggetKriging> impl_copy(new NuggetKriging(*impl_ptr, ExplicitCopySpecifier{}));
+  obj.attr("object") = impl_copy;
+  obj.attr("class") = "NuggetKriging";
+  return obj;
+}
+
 // [[Rcpp::export]]
 Rcpp::List nuggetkriging_model(Rcpp::List k) {
   if (!k.inherits("NuggetKriging"))
diff --git a/dependencies/pybind11 b/dependencies/pybind11
index ffa34686..80dc998e 160000
--- a/dependencies/pybind11
+++ b/dependencies/pybind11
@@ -1,1 +1,1 @@
-Subproject commit ffa346860b306c9bbfb341aed9c14c067751feb8
+Subproject commit 80dc998efced8ceb2be59756668a7e90e8bef917
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index 24af2bf5..6037a5a5 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -21,7 +21,8 @@ add_library(Kriging
         CacheFunction.cpp include/libKriging/CacheFunction.hpp include/libKriging/utils/custom_hash_function.hpp include/libKriging/utils/cache_details.hpp
         include/libKriging/utils/LinearHashStorage.hpp
         include/libKriging/utils/data_from_arma_vec.hpp
-        include/libKriging/version.hpp ${VERSION_FILE})
+        include/libKriging/utils/ExplicitCopySpecifier.hpp
+        include/libKriging/version.hpp ${VERSION_FILE} )
 
 if (CXX_CLANG_TIDY)
     set_target_properties(Kriging
diff --git a/src/lib/Kriging.cpp b/src/lib/Kriging.cpp
index df3becbb..309335aa 100644
--- a/src/lib/Kriging.cpp
+++ b/src/lib/Kriging.cpp
@@ -77,6 +77,8 @@ LIBKRIGING_EXPORT Kriging::Kriging(const arma::colvec& y,
   fit(y, X, regmodel, normalize, optim, objective, parameters);
 }
 
+LIBKRIGING_EXPORT Kriging::Kriging(const Kriging& other, ExplicitCopySpecifier) : Kriging{other} {}
+
 // arma::mat XtX(arma::mat &X) {
 //   arma::mat XtX = arma::zeros(X.n_cols,X.n_cols);
 //   for (arma::uword i = 0; i < X.n_cols; i++) {
diff --git a/src/lib/NoiseKriging.cpp b/src/lib/NoiseKriging.cpp
index 27a71652..b8576ca2 100644
--- a/src/lib/NoiseKriging.cpp
+++ b/src/lib/NoiseKriging.cpp
@@ -77,6 +77,8 @@ LIBKRIGING_EXPORT NoiseKriging::NoiseKriging(const arma::colvec& y,
   fit(y, X, noise, regmodel, normalize, optim, objective, parameters);
 }
 
+LIBKRIGING_EXPORT NoiseKriging::NoiseKriging(const NoiseKriging& other, ExplicitCopySpecifier) : NoiseKriging{other} {}
+
 // arma::mat XtX(arma::mat &X) {
 //   arma::mat XtX = arma::zeros(X.n_cols,X.n_cols);
 //   for (arma::uword i = 0; i < X.n_cols; i++) {
diff --git a/src/lib/NuggetKriging.cpp b/src/lib/NuggetKriging.cpp
index 5ff44dca..253cc3ba 100644
--- a/src/lib/NuggetKriging.cpp
+++ b/src/lib/NuggetKriging.cpp
@@ -76,6 +76,9 @@ LIBKRIGING_EXPORT NuggetKriging::NuggetKriging(const arma::colvec& y,
   fit(y, X, regmodel, normalize, optim, objective, parameters);
 }
 
+LIBKRIGING_EXPORT NuggetKriging::NuggetKriging(const NuggetKriging& other, ExplicitCopySpecifier)
+    : NuggetKriging{other} {}
+
 // arma::mat XtX(arma::mat &X) {
 //   arma::mat XtX = arma::zeros(X.n_cols,X.n_cols);
 //   for (arma::uword i = 0; i < X.n_cols; i++) {
diff --git a/src/lib/include/libKriging/Kriging.hpp b/src/lib/include/libKriging/Kriging.hpp
index 7e5cd553..1bdfd29b 100644
--- a/src/lib/include/libKriging/Kriging.hpp
+++ b/src/lib/include/libKriging/Kriging.hpp
@@ -7,6 +7,7 @@
 #include "libKriging/utils/lk_armadillo.hpp"
 
 #include "libKriging/Trend.hpp"
+#include "libKriging/utils/ExplicitCopySpecifier.hpp"
 
 #include "libKriging/libKriging_exports.h"
 
@@ -26,6 +27,9 @@ struct KrigingParameters {
  * @ingroup Regression
  */
 class Kriging {
+  Kriging() = delete;
+  Kriging(const Kriging& other) = default;  // Should be specialized is non default copy constructor is required
+
  public:
   using Parameters = KrigingParameters;
 
@@ -113,6 +117,8 @@ class Kriging {
                             const std::string& objective = "LL",
                             const Parameters& parameters = {});
 
+  LIBKRIGING_EXPORT Kriging(const Kriging& other, ExplicitCopySpecifier);
+
   /** Fit the kriging object on (X,y):
    * @param y is n length column vector of output
    * @param X is n*d matrix of input
diff --git a/src/lib/include/libKriging/NoiseKriging.hpp b/src/lib/include/libKriging/NoiseKriging.hpp
index 19b6cfe1..fb13f7e9 100644
--- a/src/lib/include/libKriging/NoiseKriging.hpp
+++ b/src/lib/include/libKriging/NoiseKriging.hpp
@@ -7,6 +7,7 @@
 #include "libKriging/utils/lk_armadillo.hpp"
 
 #include "libKriging/Trend.hpp"
+#include "libKriging/utils/ExplicitCopySpecifier.hpp"
 
 #include "libKriging/libKriging_exports.h"
 
@@ -26,6 +27,10 @@ struct NoiseKrigingParameters {
  * @ingroup Regression
  */
 class NoiseKriging {
+  NoiseKriging() = delete;
+  NoiseKriging(const NoiseKriging& other)
+      = default;  // Should be specialized is non default copy constructor is required
+
  public:
   using Parameters = NoiseKrigingParameters;
 
@@ -108,6 +113,8 @@ class NoiseKriging {
                                  const std::string& objective = "LL",
                                  const Parameters& parameters = Parameters{});
 
+  LIBKRIGING_EXPORT NoiseKriging(const NoiseKriging& other, ExplicitCopySpecifier);
+
   /** Fit the kriging object on (X,y):
    * @param y is n length column vector of output
    * @param noise is n length column vector of output variances
diff --git a/src/lib/include/libKriging/NuggetKriging.hpp b/src/lib/include/libKriging/NuggetKriging.hpp
index 63085d07..92f85806 100644
--- a/src/lib/include/libKriging/NuggetKriging.hpp
+++ b/src/lib/include/libKriging/NuggetKriging.hpp
@@ -7,6 +7,7 @@
 #include "libKriging/utils/lk_armadillo.hpp"
 
 #include "libKriging/Trend.hpp"
+#include "libKriging/utils/ExplicitCopySpecifier.hpp"
 
 #include "libKriging/libKriging_exports.h"
 
@@ -28,6 +29,11 @@ struct NuggetKrigingParameters {
  * @ingroup Regression
  */
 class NuggetKriging {
+ private:
+  NuggetKriging() = delete;
+  NuggetKriging(const NuggetKriging& other)
+      = default;  // Should be specialized is non default copy constructor is required
+
  public:
   using Parameters = NuggetKrigingParameters;
 
@@ -117,6 +123,8 @@ class NuggetKriging {
                                   const std::string& objective = "LL",
                                   const Parameters& parameters = Parameters{});
 
+  LIBKRIGING_EXPORT NuggetKriging(const NuggetKriging& other, ExplicitCopySpecifier);
+
   /** Fit the kriging object on (X,y):
    * @param y is n length column vector of output
    * @param X is n*d matrix of input
diff --git a/src/lib/include/libKriging/utils/ExplicitCopySpecifier.hpp b/src/lib/include/libKriging/utils/ExplicitCopySpecifier.hpp
new file mode 100644
index 00000000..29137bda
--- /dev/null
+++ b/src/lib/include/libKriging/utils/ExplicitCopySpecifier.hpp
@@ -0,0 +1,6 @@
+#ifndef LIBKRIGING_SRC_LIB_INCLUDE_LIBKRIGING_UTILS_EXPLICITCOPYSPECIFIER_HPP
+#define LIBKRIGING_SRC_LIB_INCLUDE_LIBKRIGING_UTILS_EXPLICITCOPYSPECIFIER_HPP
+
+struct ExplicitCopySpecifier {};
+
+#endif  // LIBKRIGING_SRC_LIB_INCLUDE_LIBKRIGING_UTILS_EXPLICITCOPYSPECIFIER_HPP


Test patch:
diff --git a/.travis-ci/linux-macos/test.sh b/.travis-ci/linux-macos/test.sh
index 67203416..0b172871 100755
--- a/.travis-ci/linux-macos/test.sh
+++ b/.travis-ci/linux-macos/test.sh
@@ -19,8 +19,17 @@ else
 
     # Cleanup compiled libs to check right path finding
     rm -fr src/lib
+        
     # add library directory search PATH for executables
-    export LD_LIBRARY_PATH=$PWD/installed/lib:${LD_LIBRARY_PATH}
+    case "$(uname -s)" in
+     Darwin)
+      export DYLD_LIBRARY_PATH=$PWD/installed/lib:${DYLD_LIBRARY_PATH}
+       ;;
+    
+     *)
+      export LD_LIBRARY_PATH=$PWD/installed/lib:${LD_LIBRARY_PATH}
+       ;;
+    esac
 
     ctest -C "${MODE}" ${CTEST_FLAGS}
 fi
diff --git a/bindings/Python/tests/CMakeLists.txt b/bindings/Python/tests/CMakeLists.txt
index 3b5fb90d..2a8161f4 100644
--- a/bindings/Python/tests/CMakeLists.txt
+++ b/bindings/Python/tests/CMakeLists.txt
@@ -62,5 +62,6 @@ add_python_test(NAME WrappedPyLinearRegression FILENAME WrappedPyLinearRegressio
 add_python_test(NAME WrappedPyKrigingParametricTest FILENAME WrappedPyKriging_parametric_test.py)
 add_python_test(NAME PyLinearRegression FILENAME PyLinearRegression_test.py)
 add_python_test(NAME PyKrigingParametricTest FILENAME PyKriging_parametric_test.py)
+add_python_test(NAME KrigingCopyTest FILENAME PyKriging_copy_test.py)
 add_python_test(NAME binding_consistency FILENAME binding_consistency_test.py)
 add_python_test(NAME KrigingDemo FILENAME pylibkriging_demo.py)
diff --git a/bindings/Python/tests/PyKriging_copy_test.py b/bindings/Python/tests/PyKriging_copy_test.py
new file mode 100644
index 00000000..6b4347e1
--- /dev/null
+++ b/bindings/Python/tests/PyKriging_copy_test.py
@@ -0,0 +1,57 @@
+import pylibkriging as lk
+import numpy as np
+import pytest
+
+
+def test_copied_kriging_returns_same_result():
+    X = [0.0, 0.2, 0.5, 0.8, 1.0]
+    f = lambda x: (1 - 1 / 2 * (np.sin(12 * x) / (1 + x) + 2 * np.cos(7 * x) * x ** 5 + 0.7))
+    y = [f(xi) for xi in X]
+
+    rl1 = lk.Kriging(y, X, "gauss", parameters={'sigma2': 1, 'is_theta_estim': False})
+    print(rl1.summary())
+
+    rl2 = rl1.copy()  # true copy not reference copy
+    print(rl2.summary())
+
+    assert id(rl1) != id(rl2)  # not same object reference                  
+
+    x = np.arange(0, 1, 1 / 99)
+
+    p1 = rl1.predict(x, True, False, False)
+    p1 = {"mean": p1[0], "stdev": p1[1], "cov": p1[2], "mean_deriv": p1[3], "stdev_deriv": p1[4]}
+
+    p2 = rl2.predict(x, True, False, False)
+    p2 = {"mean": p2[0], "stdev": p2[1], "cov": p2[2], "mean_deriv": p2[3], "stdev_deriv": p2[4]}
+
+    assert np.array_equal(p1["mean"], p2["mean"])
+    assert np.array_equal(p1["stdev"], p2["stdev"])
+    assert np.array_equal(p1["cov"], p2["cov"])
+    assert np.array_equal(p1["mean_deriv"], p2["mean_deriv"])
+    assert np.array_equal(p1["stdev_deriv"], p2["stdev_deriv"])
+
+
+def test_copied_and_changed_kriging_returns_different_result():
+    X = [0.0, 0.2, 0.5, 0.8, 1.0]
+    f = lambda x: (1 - 1 / 2 * (np.sin(12 * x) / (1 + x) + 2 * np.cos(7 * x) * x ** 5 + 0.7))
+    y = [f(xi) for xi in X]
+
+    rl1 = lk.Kriging(y, X, "gauss", parameters={'sigma2': 1, 'is_theta_estim': False})
+    print(rl1.summary())
+
+    rl2 = rl1.copy()  # true copy not reference copy
+    print(rl2.summary())
+
+    assert id(rl1) != id(rl2)  # not same object reference                  
+
+    x = np.arange(0, 1, 1 / 99)
+
+    p1 = rl1.predict(x, True, False, False)
+    p1 = {"mean": p1[0], "stdev": p1[1], "cov": p1[2], "mean_deriv": p1[3], "stdev_deriv": p1[4]}
+
+    rl2.update([0.6], [f(0.6)])
+    p2 = rl2.predict(x, True, False, False)
+    p2 = {"mean": p2[0], "stdev": p2[1], "cov": p2[2], "mean_deriv": p2[3], "stdev_deriv": p2[4]}
+
+    assert not np.array_equal(p1["mean"], p2["mean"])
+    assert not np.array_equal(p1["stdev"], p2["stdev"])