GEP 10: annotate the taxes and transfers system with units (#1192)#1212
Open
MImmesberger wants to merge 65 commits into
Open
GEP 10: annotate the taxes and transfers system with units (#1192)#1212MImmesberger wants to merge 65 commits into
MImmesberger wants to merge 65 commits into
Conversation
Register the [currency] dimension for Germany (GEP 10): the euro as the base currency, the Deutsche Mark at the Euro-Einführungsgesetz rate of 1/1.95583 euro, and the dated statutory-currency mapping. The mapping declares the euro for all dates, matching how pre-2002 parameters are stored today; a follow-up flips it to DM through 2001. Also register the grouping levels (hh, ehe, fg, bg, eg, wthh, sn) with the fluent unit builder so decorators can spell `Unit.X.PER_HH` etc. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reference namespace for the GEP-10 sweep: money flows carry Unit.CURRENCY.PER_MONTH, eligibility flags Unit.DIMENSIONLESS, the lookup-table converter UNSET_UNIT, and the SUM agg_by_p_id auto-derives. Re-export Unit and UNSET_UNIT from gettsim.tt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sync gettsim's params-schema.json copy with ttsim's GEP-10 grammar (unit tokens + input_unit/output_unit; reference_period dropped) and re-export cast_unit from gettsim.tt for body-level unit overrides. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the familie namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the kindergeld namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the kinderbonus namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the unterhalt namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the unterhaltsvorschuss namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the einnahmen namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the wohnen namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the solidaritätszuschlag namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the vorrangprüfungen namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the lohnsteuer namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the einkommensteuer namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the sozialversicherung namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the arbeitslosengeld_2 namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the bürgergeld namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the grundsicherung namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the elterngeld namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the erziehungsgeld namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the kinderzuschlag namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorator unit=, YAML unit tokens (reference_period dropped), and aggregation / rounding-spec units for the wohngeld namespace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
) The Mehrbedarf für Alleinerziehende is a dimensionless share (applied as `out * (1 + mehrbedarf_alleinerziehend)`), so a monthly `_m` flow suffix is wrong under GEP-10's name/period consistency rule — the auto `_y` variant (×12 on a share) would be nonsense. Drop the suffix (D4). Bürgergeld and Arbeitslosengeld II twins. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The per-leaf unit mapping must match the value's nesting (per age group kleinkind/schulkind/jugendliche), else the mandatory-units check reports each leaf as unannotated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A dimensionless share carries no flow period, so the _y suffix violates the name/period consistency rule. No external consumers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#1192) Resolve GEP-10 name↔unit consistency and start the dry-run body checks: - Rename flow-valued leaves to carry their period suffix: rentenwert_m, minijobgrenze_m, betrag_versicherter_regulärer_beitragssatz_m, lohnersatzanteil_einkommen_*_grenze_m, einkommensgrenze_ohne_geschwisterbonus*_y, kinderzuschlag satz_m, rente mean_entgeltpunkte_pro_bewertungsmonat_{m,y} + mean_entgeltpunkte_zuschlag_m (+ consumers and test cases). - familie: cast_unit(alter/anzahl, DIMENSIONLESS) for the dimensioned-literal age/count comparisons. - verify_units=False on dry-run-opaque bodies (midijob faktor_f/bemessungsentgelt, wohngeld abzugsanteil, lohnsteuer tarif_klassen_5_und_6, soli betrag_y_sn, rente erwerbsminderung/grundrente lookups). - Fix two SUM-aggregation declarations to DIMENSIONLESS.PER_<level> (a person-level count collapses to dimensionless, so the sum derives 1/[level]). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Annotate the structured-parameter dataclass fields (Regelbedarfsstufen, Existenzminimum elements, SatzMitAltersgrenzen, Altersgrenzen) with Annotated[..., Unit...] so the dry-run gives plucked values their unit at the structure instead of at every pluck site (GEP 10). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- cast_unit(count, DIMENSIONLESS) for n-1/n+1/n>1 count arithmetic (bürgergeld, einkommensteuer alleinerziehend, elterngeld, grundsicherung). - lohnsteuer: the yearly Tarif/Vorsorge leaves declared bare CURRENCY now carry _y + CURRENCY.PER_YEAR (basistarif_y, splittingtarif_y, tarif_klassen_5_und_6_y, their _mit_kinderfreibetrag twins, vorsorge_*_y), fixing betrag_y/vorsorgepauschale_y. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Kinderzuschlag _bg needs (kosten_der_unterkunft, erwachsenenbedarf, anzurechnendes_einkommen, basisbetrag, vermögensfreibetrag, maximales/ mindest-einkommen) are genuine Bedarfsgemeinschaft totals, so they declare CURRENCY.PER_MONTH.PER_BG; anspruchshöhe_m = _bg / anzahl_personen_bg then resolves to the per-person amount. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#1192) wohngeld: - miete_m_wthh / min_miete_m_hh: cast the per-capita reweighting and the count-keyed min-rent look-up to their per-person currency. - miete_m_hh (2009+): the max-Miete / heating look-ups are dimensionless-indexed, so cast the person-per-hh count to a plain index; the pre-2009 Baujahr variant uses `xnp.searchsorted` over three axes and opts out (GEP 10 D3). - einkommen_m_wthh: raw `xnp`/`.shape` index clamping opts out. - basisbetrag_m_wthh: the Basisformel is quadratic in (Miete, Einkommen) with per-currency coefficients the grammar cannot spell, so it opts out (GEP 10 D1). - mindesteinkommen_erreicht / einkommen_für_mindesteinkommen / vermögensgrenze: reconcile the wthh totals and per-person threshold levels. Cross-level casts surfaced once the masking errors cleared: - bürgergeld anspruchshöhe_m: fold the EG surplus into the BG pool and distribute by Bedarf share (Vertikalmethode). - kinderzuschlag anspruchshöhe_m_bg: net excess wealth (a stock) against the monthly claim. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…il (#1192) The pre-2023 ALG II namespace mirrors bürgergeld, so it needs the same casts: per-capita miete/heizkosten/wohnfläche, mehrbedarf count multiplier, the Bedarfsanteils-/Vertikalmethode (EG surplus → BG pool, distribute by Bedarf share), überschusseinkommen BG→person, differenz_kindergeld reconciliation, and the dynamically built Eigentum lookup opt-out. - nettoquote: the net/gross ratio is dimensionless; re-tag it so the `max(…, 0)` floor branch does not read as reciprocal money. - Annotate the `RegelsatzAnteilsbasiert` dataclass tree (basissatz CURRENCY.PER_ MONTH, the Anteile DIMENSIONLESS, the age bounds YEARS) so the erwachsenensatz / kindersatz / grundsicherung mehrbedarf-schwerbehinderung plucks carry units. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The pre-2023 Kindergeld-nach-Kinderzahl look-up returns an untagged value, so tag its currency at each `.look_up` site: kindergeld betrag_gestaffelt_m, kinderzuschlag satz_m, einkommensteuer relevantes_kindergeld_m. Also tag the age-10 literal in kind_bis_10_mit_kindergeld. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…1192) - erziehungsgeld: reconcile the fg-level Abzug against the per-person Anspruch and re-tag the fg-level Einkommensgrenze (with the child count as a dimensionless multiplier); the base thresholds pluck dict-typed dataclass fields the dry-run cannot follow through the subscript, so those two functions opt out. - einkommensteuer vorsorge (regime_bis_2004 + vorwegabzug_lohnsteuer): per-capita Splitting formulas (÷ then × anzahl_personen_sn) the level model cannot follow. - einkünfte sonstige ertragsanteil_gesetzliche_rente: the look-up index is built with raw `xnp.floor(...).astype(int)`. The `fail_msg_if_included` placeholder stubs (elterngeld/lohnsteuer/unterhalts- vorschuss betrag_m etc.) are handled upstream — the dry-run now skips them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…1192) - altersfreibetrag_y_bis_2004 reads `maximaler_altersentlastungsbetrag` / `altersentlastungsquote`, declared `type: require_converter` though consumed as plain scalars, so the dry-run sees them as opaque; tag them at the site. - bezugsmonate_unter_grenze_fg: read the fg-level cumulative Bezugsmonate as a plain month count and tag the +1-month step, so the comparisons against the month thresholds line up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Its `require_converter`-typed scalar parameters and the age-threshold comparison leave the body un-followable by the dry-run (like its ab-2005 twin), so opt out; declared unit and edges stay checked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Apply ruff's import ordering to the Round C files, reword two comments off the ambiguous ÷/× unicode signs, and restore the plain-prose `familie__anzahl_ erwachsene_bg >= 1` condition in the kinderzuschlag docstrings (a stray cast_unit had leaked in from a replace-all). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…1192) The units sweep renamed the kinderzuschlag `satz` param_functions and all consumers to `satz_m` (the `_m` suffix for a monthly EUR amount) but left the raw YAML `satz` param under the old name, splitting one leaf into two. The raw param carries the explicit 2023 value (§ 6a-Sonderregelung, €250), so 2023 lost its `satz_m` producer and every 2023 kinderzuschlag / bürgergeld / gemischte-BG case failed with `kinderzuschlag satz_m missing`. Rename the raw param to `satz_m` so it coexists with the param_functions exactly as on main. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…1192) Completing the satz_m rename unblocked the pre-2021 kinderzuschlag subtree (its env build no longer fails on the missing leaf), revealing that `maximales_nettoeinkommen_ m_bg` adds the BG Erwachsenenbedarf to `satz_m * anzahl_kinder_bg`, whose per-child Satz and per-BG count leave a stray person level. Cast the product to the BG-level child total. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nless (#1192) The Grundbewertung average earning points divide total Entgeltpunkte by the belegungsfähiger Gesamtzeitraum — a plain count of Bewertungseinheiten — so the ratio is DIMENSIONLESS, not per-year. Drop the spurious `_y` suffix (which pulled the value through auto time-conversion and broke the post-2002 Erwerbsminderungs- rente Zurechnungszeit credit) and cast the valuation-span counts at the divisor and in the `zusätzliche_entgeltpunkte` multiply. Restores the expected entgeltpunkte_west. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The GEP-10 rollout needs the units-and-dimensionality ttsim, which is not yet on ttsim main. Pin CI to the GEP-10 mettsim branch so the suite runs against the right backend. Revert to `branch = "main"` once the ttsim GEP-10 stack merges, before this PR merges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ttsim's main gained the knob with the boundary conversion, and gettsim's wrapper forwards its own locals(), so without the parameter there was no way to tell the engine which currency the input data is denominated in. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The renames are breaking and ship without aliases, so they belong in the changelog by name rather than as a note that some things moved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
for more information, see https://pre-commit.ci
Stacked on #1192's rollout branch (`gep10-rollout`), which declares the euro for every policy date. This branch flips the statutory currency to the Deutsche Mark through 2001 and restates every pre-2002 currency parameter accordingly. ## Method Every Deutsche-Mark value here was **read out of the Bundesgesetzblatt**, not obtained by multiplying a stored euro value by 1,95583. That matters more than it sounds: the stored euro values are lossy, and inverting them is ambiguous or plainly wrong. - The Rentenwert (Ost) for 1994 is **33,34 DM**. The stored 17,05 EUR is equally consistent with 33,35; only § 2 V. v. 01.12.1993 settles it. - The Arbeitnehmerpauschbetrag stored 1 044 EUR from 1990, but that is 2 042 DM, and the law read **2 000 DM** until 2001. Inverting would have enshrined 2 042. Where the law states a monthly amount and the parameter is yearly (§ 32 Abs. 6 EStG's "261 Deutsche Mark", § 15 WoGG's "1 200 Deutsche Mark im Jahr"), the entries say so. ## Corrections found by reading the gazette Fourteen pre-existing errors surfaced. The ones that change results: | parameter | was | gazette | |---|---|---| | Wohngeld Höchstbetrag, 4 Mitglieder, Baujahr ≤1965, Mietstufe V | 585 EUR | **485** (948,58 DM) — broke Mietstufe monotonicity, overstating the cap by 100 EUR/month | | same, 3 Mitglieder, Mietstufe I | 322 EUR | **320** (625,87 DM) | | Wohngeld Mehrbetrag 1985, Baujahr 1972–1977 | copy of the 1966–1971 column | **85/90/95/100/105 DM** | | Wohngeld Mehrbetrag 1990-10, Baujahr ≥1978 | near-copy of the 1966–1977 column | **95/105/110/120/125/135 DM** | | Wohngeld 1993, 4 Mitglieder, Mietstufe VI | 437 EUR (a duplicate of Mietstufe II) | **578** (1 130 DM) | | Rentenversicherung-Grenze 1999 West | 4 339 EUR (= 8 486 DM) | **8 500 DM** = 4 346 EUR | | Bezugsgröße Ost 1998 | 3 710 DM (the 1999 value) | **3 640 DM** | | Wohngeld Behinderten-Freibetrag 1986 | halved to 612 EUR | unchanged at **2 400 DM** — the parameter's own description had suspected this | | Kinderfreibetrag, Betreuung 2000 | 774 EUR | **1 512 DM** | Three parameters held the *next* law's value a year early: `arbeitnehmerpauschbetrag` (1 044 EUR from 1990), `maximales_einkommen_des_kindes` (14 040 DM, which applies "ab dem Veranlagungszeitraum 2002"), and the Wohngeld Höchstbeträge, whose 2001 entry held the Artikel-6 euro table. The Haushaltsfreibetrag's 2 916 EUR is likewise the changeover value: the Steuer-Euroglättungsgesetz glättet § 32 "durch 54 teilbare" Euro-Beträge, and 5 616 DM lands on 54 × 54. Three cross-checks did the catching, and are worth keeping in mind for future work: § 8 SGB IV puts the Minijobgrenze at a seventh of the Bezugsgröße, § 6 Abs. 7 SGB V puts the Krankenversicherung grenze at three quarters of the Rentenversicherung one, and the Wohngeld tables rise monotonically in the Mietstufe. ## Mid-year changes The Ost Rechengrößen changed twice in 1991, so the entries are split at 1 July, as the law is: the Bezugsgröße goes 1 540 → 1 750 DM, the Grenze 3 000 → 3 400, the Minijobgrenze 220 → 250. The Minijobgrenze had held 235 — the average of the two, and the only year that broke the seventh rule. ## Three values are not in the Bundesgesetzblatt Each entry records why: - The Beitrittsgebiet's 1990 and first-half-1991 Rechengrößen rest on the DDR's *Gesetz über die Sozialversicherung* (GBl. I Nr. 38 S. 486). - § 68 Abs. 1 SGB VI defines the first aktueller Rentenwert not as an amount but as the December 1991 pension for one Entgeltpunkt, so no gazette prints it. - `beitragsvolumen` and `gesamtes_rentenvolumen` are statistics of the Deutsche Rentenversicherung, and no function reads either; their pre-2002 entries are dropped rather than converted. `mietobergrenze_pro_qm` now starts in 2005: Arbeitslosengeld II does not exist earlier, and the value is a rule of thumb that, as its description says, is not in the law. ## Results Only five cases move: the Erwerbsminderungsrente at 2001, by 0.006 to 0.013 percent. The Rentenwert had been stored as 24,84 EUR, a rounded conversion, where the Verordnung gives 48,58 DM. Policy cases stay denominated in euro; the model computes in the statutory currency and converts at the column boundary. - gettsim, numpy: 1319 passed - gettsim policy cases, jax: 1300 passed - `pixi run ty`: unchanged from the 11 pre-existing workspace diagnostics - the policy environment builds at every date from 1958 to 2024 ## Two things to decide before merging The Wohngeld typo fixes and the 1999 Grenze also change results for **2002 to 2008**. They are genuine corrections rather than redenomination, and could be split out. `gep10-rollout` still carries the temporary ttsim pin, which reverts to `main` before that branch merges. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
`unit=` is a required argument now, so the guide's `@policy_function()` and `@param_function()` no longer run. Both functions it builds are the yearly Kinderfreibetrag, annotated CURRENCY.PER_YEAR in germany/einkommensteuer, so the guide says the same. This is what CI's "Run ty" was failing on. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Resolve the codex review of the unit annotations and Deutsche-Mark restatement, and fix the two red CI checks on this PR. Review response: - Cite the Bundesgesetzblatt for the three results-changing DM corrections that lacked a reference: arbeitnehmerpauschbetrag 1990 (2000 DM, Art. 1 G. v. 25.07.1988), the Krankenversicherung Bezugsgröße Ost 1998 (3640 DM, V. v. 02.12.1997), and the Minijobgrenze Ost 1991 (220 DM, § 8 Abs. 1 Nr. 1 SGB IV; the Beitrittsgebiet Rechengröße rests on the DDR statute, noted). - geschwisterbonus_altersgrenzen carries PERSON_COUNT: its dict values are child counts, so the consumer casts a headcount rather than erasing the count dimension. - Restore the pre-2002 beitragsvolumen / gesamtes_rentenvolumen entries in Deutsche Mark. These are Deutsche-Rentenversicherung statistics, not gazetted amounts, so they are converted at the official rate; only their ratio enters the Rentenanpassungsformel, unchanged to 3e-9. CI fixes: - Fold seven over-long reference: lines to satisfy yamllint (88-char limit), matching the repo's existing folded-reference style. - Declare the mandatory unit on every parameter the policy-environment how-to and the introductory tutorial construct (ScalarParam, DictParam, the lookup table, piecewise, and raw params), so the notebooks execute under GEP 10; drop the removed reference_period from the attribute list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stacked on #1212 (`gep10-rollout`). Adopts the per-policy-system unit registry from ttsim (ttsim-dev/ttsim#145), so GETTSIM's euro base and mettsim's castar base coexist in one process — the dev repo's full test suite runs in a single command. ## What - `germany/__init__.py`: the four import-time registration calls (`register_currency` × 2, `register_statutory_currencies`, `register_unit_builder_levels`) become one declared value: ```python UNIT_SYSTEM = UnitSystem( base_currency="EUR", other_currencies={"DM": "EUR / 1.95583"}, statutory_currencies={"0001-01-01": "DM", "2002-01-01": "EUR"}, grouping_levels=["hh", "ehe", "fg", "bg", "eg", "wthh", "sn"], ) ``` - `main()` and `plot.dag.tt()` gain a `unit_system` parameter, defaulted from `germany.UNIT_SYSTEM` exactly as `orig_policy_objects` is defaulted from `germany.ROOT_PATH`. Users never name it. - Tests exercising the currency API (`test_currency.py`) or ttsim's test helpers (`test_policy_cases.py`) pass the system through. No `unit=` decorator, policy module, or parameter YAML changes. ## Temporary pin `ttsim-backend` is pinned to the `gep10-per-system-unit-registries` branch (re-locked in the same commit). Revert to `main` once the ttsim GEP-10 stack merges, before this PR merges — same pattern as the existing rollout pin. ## Verification (from the dev repo) - The original reproducer `pixi run -e py314 tests ttsim/tests/tt/test_units.py gettsim/src/gettsim/tests_germany/test_currency.py` now passes (173 tests); it died at collection before, on the EUR/CASTAR base collision. - `pixi run -e py314 tests -n 7` (whole dev repo): every ttsim, gettsim, and mettsim test passes. The only remaining failures are pre-existing soep-preparation import issues (sklearn / wealth-imputation models), unrelated to units. - `pixi run ty`: back to the 9-diagnostic baseline (7 soep, 2 unrelated) — the GEP-10 stack adds none. - `prek run --all-files`: passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…c065aa - restore eg→bg transfer cast in anspruchshöhe_m; berechtigte_wohnfläche cast-free via the schedule param-function output unit + max/min shim - wohngeld basisbetrag/anspruchshöhe/betrag chain to .PER_WTHH; drop redundant casts - wohngeld EUR-only heating/climate lookups: raw output_unit -> _PER_HH, drop casts
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Repoint the ttsim-backend pin to the mettsim worked-example branch, which sits on top of the merged per-system-unit-registries work and carries the Python 3.11 mappingproxy import fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The UTF-8 repro-script fix moved down into the ttsim infra branch, so #141 was restacked; re-pin the lock to its live head. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 'tax deduction only for families with at least two children' example compares `familie__anzahl_kinder_fg` (a count at the Familiengemeinschaft level) against a threshold parameter. Under GEP 10 a comparison requires both operands in the same unit, so the threshold must be declared `PERSON_COUNT.PER_FG`, not plain `PERSON_COUNT`. Without this the docs build fails with a UnitConsistencyError while executing the notebook. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Keep `alter` in YEARS at age comparisons and tag the literal instead (`alter <= cast_unit(N, TTSIMUnit.YEARS)`), matching the kindergeld convention and preserving the unit contract (familie, individual_characteristics). - Rename the parameter `midijobgrenze` to `midijobgrenze_m` for symmetry with its sibling `minijobgrenze_m`; both are EUR_PER_MONTH. - Bump the local ty-jax hook pin to ty==0.0.52 to match the ty-pre-commit rev. - Annotate the new test_currency.py functions with mandatory type hints. - Run the ty / ty-jax commands in AGENTS.md through pixi. - Fix the `volljährig` docstring to match `alter >= 18`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1192. Implements GEP 10 (#1193) across the German taxes and transfers system, and
restates every pre-2002 currency parameter in Deutsche Mark. (#1211 is merged into this
branch; there is one PR, not a stack.)
Every
@policy_function,@policy_input,@param_functionand@group_creation_functionnow declares a unit, as does every parameter in the YAML files.The engine checks those declarations against what the function bodies actually do, so a
column that claims to be a monthly amount cannot quietly be built from a yearly one. The
euro is the base currency, the Deutsche Mark is registered relative to it, and policy
dates before 2002 compute in the currency of their day.
Why this is one large PR
unit=is a required argument of@policy_function. gettsim cannot import until everydecorator carries one, so the migration is atomic: there is no intermediate state that
runs. The commits are split per namespace to keep the diff readable, but only the final
state is green.
The work came in three passes.
Annotation — one commit per namespace, declaring the unit of every function, input and
parameter.
Round C — making the bodies agree with the declarations. This is where the interesting
failures were: literals needing a
cast_unitto say which quantity a bare number is,comparisons across levels, counts multiplying amounts, and places where the declared unit
turned out to be the wrong one.
Deutsche Mark — the statutory currency is the Deutsche Mark through 2001 and the euro
from 2002, and the pre-2002 parameters are restated accordingly.
Renames
Where a name did not match the quantity, the name changed. Most columns gain the suffix of
the period they are measured over (
basistarif→basistarif_y,rentenwert→rentenwert_m). Two lose one they should never have had, being shares rather than flows:anteil_steuerfälliger_einnahmen_yandmehrbedarf_alleinerziehend_m.gesamteinkommen_ybecomesgesamteinkommen_y_sn, naming the level it aggregates to.These are breaking and ship without aliases, so the changelog lists all 22 by name.
The Deutsche-Mark values were read from the Bundesgesetzblatt
Not obtained by multiplying the stored euro values by 1,95583. That matters more than it
sounds, because the stored values are lossy and inverting them is ambiguous or wrong:
consistent with 33,35; only § 2 V. v. 01.12.1993 settles it.
law read 2 000 DM until 2001. Inverting would have enshrined 2 042.
Where the law states a monthly amount and the parameter is yearly (§ 32 Abs. 6 EStG's
"261 Deutsche Mark", § 15 WoGG's "1 200 Deutsche Mark im Jahr"), the entries say so.
Corrections found by reading the gazette
Fourteen pre-existing errors surfaced. The ones that change results:
Three parameters held the next law's value a year early:
arbeitnehmerpauschbetrag,maximales_einkommen_des_kindes(14 040 DM applies "ab dem Veranlagungszeitraum 2002"),and the Wohngeld Höchstbeträge, whose 2001 entry held the Artikel-6 euro table. The
Haushaltsfreibetrag's 2 916 EUR is likewise the changeover value: the
Steuer-Euroglättungsgesetz glättet § 32 "durch 54 teilbare" Euro-Beträge, and 5 616 DM
lands on 54 × 54.
Three cross-checks did the catching, and are worth keeping in mind for future work: § 8
SGB IV puts the Minijobgrenze at a seventh of the Bezugsgröße, § 6 Abs. 7 SGB V puts the
Krankenversicherung grenze at three quarters of the Rentenversicherung one, and the
Wohngeld tables rise monotonically in the Mietstufe.
Mid-year changes and three values that are not in the Bundesgesetzblatt
The Ost Rechengrößen changed twice in 1991, so those entries are split at 1 July, as the
law is. The Minijobgrenze had held 235 — the average of 220 and 250, and the only year
that broke the seventh rule.
Three values are genuinely not gazetted, and each entry records why: the Beitrittsgebiet's
1990 and first-half-1991 Rechengrößen rest on the DDR's Gesetz über die
Sozialversicherung (GBl. I Nr. 38 S. 486); § 68 Abs. 1 SGB VI defines the first aktueller
Rentenwert as the December 1991 pension rather than an amount; and
beitragsvolumenandgesamtes_rentenvolumenare Deutsche-Rentenversicherung statistics that no functionreads, so their pre-2002 entries are dropped rather than converted.
mietobergrenze_pro_qmnow starts in 2005: Arbeitslosengeld II does not exist earlier, andthe value is a rule of thumb that, as its description says, is not in the law.
Two further decisions worth knowing about
mean_entgeltpunkte_pro_bewertungsmonatis dimensionless — total Entgeltpunkte divided bya plain count of months. It had carried
_y, which pulled it through automatic timeconversion and changed the number. Dropping the suffix fixes the Erwerbsminderungsrente.
The Kinderzuschlag
satz→satz_mrename had been applied to the param functions andtheir consumers but not to the raw parameter, which silently left 2023 without a producer.
A missing root node prunes a whole subtree from the unit check, so it also masked a genuine
unit error further down.
Results
Only five cases move: the Erwerbsminderungsrente at 2001, by 0.006 to 0.013 percent. The
Rentenwert had been stored as 24,84 EUR, a rounded conversion, where the Verordnung gives
48,58 DM. Policy cases stay denominated in euro; the model computes in the statutory
currency and converts at the column boundary.
That so little moves is the point: a wrong Deutsche-Mark value would show up as a
policy-sized jump, not a fourth decimal.
Before merging
The Wohngeld typo fixes and the 1999 Grenze also change results for 2002 to 2008. They
are genuine corrections rather than redenomination.
This branch temporarily pins
ttsim-backendto the GEP-10 ttsim branch so that CI installsan engine with the units machinery. The pin reverts to
mainbefore this merges, oncettsim-dev/ttsim#138 and #141 are in.
🤖 Generated with Claude Code