Repository: PSLmodels/OG-Core

Issue:
Remittance theory and code need to be fixed
The remittances theory and code need to be fixed. When we ran OG-PHL with calibrated remittances (see OG-PHL [PR #39](https://github.com/EAPD-DRB/OG-PHL/pull/39) and OG-PHL [Issue #40](https://github.com/EAPD-DRB/OG-PHL/issues/40)), the steady state computation gave a resource constraint error. This suggests an error in the theory and how it is coded.

cc: @jdebacker

Discussion:
@jdebacker: @rickecon I agree.  Looking at the [resource constraint equation](https://github.com/PSLmodels/OG-Core/blob/e35cfd535c007d13f4df616813bb4bf626913238/ogcore/aggregates.py#L520), it appears that `RM` enters with the wrong sign.

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a37f1c4..26710624 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 
+## [0.14.4] - 2025-06-23 18:00:00
+
+### Added
+
+- Fixes the sign error on the remittances `RM` term in `aggregates.py`, `resource_constraint()` function.
+- Added a test with positive remittances to `test_aggregates.py`, `test_resource_constraint()` function.
+
 ## [0.14.3] - 2025-04-25 10:00:00
 
 ### Added
@@ -383,6 +390,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Any earlier versions of OG-USA can be found in the [`OG-Core`](https://github.com/PSLmodels/OG-Core) repository [release history](https://github.com/PSLmodels/OG-Core/releases) from [v.0.6.4](https://github.com/PSLmodels/OG-Core/releases/tag/v0.6.4) (Jul. 20, 2021) or earlier.
 
 
+[0.14.4]: https://github.com/PSLmodels/OG-Core/compare/v0.14.3...v0.14.4
 [0.14.3]: https://github.com/PSLmodels/OG-Core/compare/v0.14.2...v0.14.3
 [0.14.2]: https://github.com/PSLmodels/OG-Core/compare/v0.14.1...v0.14.2
 [0.14.1]: https://github.com/PSLmodels/OG-Core/compare/v0.14.0...v0.14.1
diff --git a/ogcore/__init__.py b/ogcore/__init__.py
index bbcb58e1..6ae0f237 100644
--- a/ogcore/__init__.py
+++ b/ogcore/__init__.py
@@ -20,4 +20,4 @@ from ogcore.tax import *
 from ogcore.txfunc import *
 from ogcore.utils import *
 
-__version__ = "0.14.3"
+__version__ = "0.14.4"
diff --git a/ogcore/aggregates.py b/ogcore/aggregates.py
index bd073bfa..bcf5cd35 100644
--- a/ogcore/aggregates.py
+++ b/ogcore/aggregates.py
@@ -526,7 +526,7 @@ def resource_constraint(Y, C, G, I_d, I_g, net_capital_flows, RM):
         \text{rc_error} &= \hat{Y}_t - \hat{C}_t -
       \Bigl(e^{g_y}\bigl[1 + \tilde{g}_{n,t+1}\bigr]\hat{K}^d_{t+1} -
       \hat{K}^d_t\Bigr) - \delta\hat{K}_t - \hat{G}_t - \hat{I}_{g,t} ... \\
-        &\qquad -\: \hat{\text{net capital outflows}}_t - \hat{RM}_t
+        &\qquad -\: \hat{\text{net capital outflows}}_t + \hat{RM}_t
       \end{split}
 
     Args:
@@ -542,7 +542,7 @@ def resource_constraint(Y, C, G, I_d, I_g, net_capital_flows, RM):
         rc_error (array_like): error in the resource constraint
 
     """
-    rc_error = Y - C - I_d - I_g - G - net_capital_flows - RM
+    rc_error = Y - C - I_d - I_g - G - net_capital_flows + RM
 
     return rc_error
 
diff --git a/setup.py b/setup.py
index f04f572d..33749e4b 100755
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
 
 setuptools.setup(
     name="ogcore",
-    version="0.14.3",
+    version="0.14.4",
     author="Jason DeBacker and Richard W. Evans",
     license="CC0 1.0 Universal (CC0 1.0) Public Domain Dedication",
     description="A general equilibrium overlapping generations model for fiscal policy analysis",


Test patch:
diff --git a/tests/test_aggregates.py b/tests/test_aggregates.py
index 6d6237bd..93fff6b2 100644
--- a/tests/test_aggregates.py
+++ b/tests/test_aggregates.py
@@ -1699,21 +1699,57 @@ def test_get_r_p(r, r_gov, p_m, K_vec, K_g, D, MPKg_vec, method, expected):
     assert np.allclose(r_p_test, expected)
 
 
-def test_resource_constraint():
+test_data_rc = [
+    (
+        np.array([48, 55, 2, 99, 8]),  # Y
+        np.array([33, 44, 0.4, 55, 6]),  # C
+        np.array([4, 5, 0.01, 22, 0]),  # G
+        np.array([20, 5, 0.6, 10, 1]),  # I_d
+        np.array([0.0, 0.0, 0.0, 0.0, 0.0]),  # I_g
+        np.array([0.1, 0, 0.016, -1.67, -0.477]),  # net_capital_flows
+        np.array([0.0, 0.0, 0.0, 0.0, 0.0]),  # RM1
+        np.array([-9.1, 1, 0.974, 13.67, 1.477]),  # expected1
+    ),
+    (
+        np.array([48, 55, 2, 99, 8]),  # Y
+        np.array([33, 44, 0.4, 55, 6]),  # C
+        np.array([4, 5, 0.01, 22, 0]),  # G
+        np.array([20, 5, 0.6, 10, 1]),  # I_d
+        np.array([0.0, 0.0, 0.0, 0.0, 0.0]),  # I_g
+        np.array([0.1, 0, 0.016, -1.67, -0.477]),  # net_capital_flows
+        np.array([0.0, 0.0, 0.0, 0.0, 0.03]),  # RM2
+        np.array([-9.1, 1, 0.974, 13.67, 1.507]),  # expected2
+    ),
+]
+
+
+@pytest.mark.parametrize(
+    "Y,C,G,I_d,I_g,net_capital_flows,RM,expected",
+    test_data_rc,
+    ids=["RM=0, M=5", "RM>0, M=5"],
+)
+def test_resource_constraint(
+    Y, C, G, I_d, I_g, net_capital_flows, RM, expected
+):
     """
     Test resource constraint equation.
     """
-    Y = np.array([48, 55, 2, 99, 8])
-    C = np.array([33, 44, 0.4, 55, 6])
-    G = np.array([4, 5, 0.01, 22, 0])
-    I_d = np.array([20, 5, 0.6, 10, 1])
-    I_g = np.zeros_like(I_d)
-    net_capital_flows = np.array([0.1, 0, 0.016, -1.67, -0.477])
-    RM = np.array([0.0, 0.0, 0.0, 0.0, 0.0])
-    expected = np.array([-9.1, 1, 0.974, 13.67, 1.477])
+    # Y = np.array([48, 55, 2, 99, 8])
+    # C = np.array([33, 44, 0.4, 55, 6])
+    # G = np.array([4, 5, 0.01, 22, 0])
+    # I_d = np.array([20, 5, 0.6, 10, 1])
+    # I_g = np.zeros_like(I_d)
+    # net_capital_flows = np.array([0.1, 0, 0.016, -1.67, -0.477])
+    # RM1 = np.array([0.0, 0.0, 0.0, 0.0, 0.0])
+    # expected1 = np.array([-9.1, 1, 0.974, 13.67, 1.477])
     test_RC = aggr.resource_constraint(
         Y, C, G, I_d, I_g, net_capital_flows, RM
     )
+    # RM2 = np.array([0.0, 0.0, 0.0, 0.0, 0.03])
+    # expected2 = np.array([-9.1, 1, 0.974, 13.67, 1.477])
+    # test_RC2 = aggr.resource_constraint(
+    #     Y, C, G, I_d, I_g, net_capital_flows, RM2
+    # )
 
     assert np.allclose(test_RC, expected)