Ontkoppelde bestanden

Terug naar Home


Een deel van de berekeningen in RSopen wordt niet elke run opnieuw gedaan, maar eenmalig weggeschreven naar een bestand dat een volgende run terugleest. Dat heet in dit model een ontkoppeld bestand. Deze pagina beschrijft wanneer dat gebeurt, hoe het model bepaalt of zo’n bestand nog bruikbaar is, en wat je moet doen als je iets verandert dat de inhoud ervan bepaalt.

Waarom ontkoppelen

Sommige stappen kosten veel rekentijd en veranderen zelden: de BGT-afgeleiden op 2,5 meter, de pandtypering van de hele BAG, de opbrengsten per ontwikkelpakket. Die hoeven niet bij elke run opnieuw.

GeoDMS houdt zelf niet bij welke uitkomsten door een configuratiewijziging ongeldig zijn geworden. Ontkoppelen is daarom handwerk: de configuratie bepaalt zelf wanneer een bestand hergebruikt mag worden. Het voordeel is snelheid, het risico is dat het model stilzwijgend met een verouderd bestand rekent. De fingerprint hieronder is er om dat risico af te dekken.

De drieslag

Een ontkoppeld item heeft drie takken, gestuurd door een schakelaar:

attribute<...> X := =!ModelParameters/<Iets>Ontkoppeld ? 'Calc_X' : Decoupling/MayReuse ? 'Read_X' : 'Make_X';
  • Calc_X rekent zonder tussenbestand, voor wie het ontkoppelen helemaal uit wil zetten.
  • Read_X leest het bestaande bestand.
  • Make_X levert het bestand plus zijn zijbestand. Het rekenen en schrijven zelf staat in Write_X, dat de StorageName draagt; Make_X := Write_X heeft Decoupling/Write_Fingerprint als ExplicitSupplier.

Die opsplitsing legt de volgorde vast en is geen vormkwestie. Het sjabloon krijgt als derde argument Writers, de naam of namen van de schrijvende items, en hangt die onder Write_Fingerprint. GeoDMS werkt een leverancier bij voordat het item zelf wordt uitgerekend, dus staat het bestand er voordat het zijbestand ontstaat. Andersom, met Write_Fingerprint als leverancier van het schrijvende item, komt het zijbestand als eerste, en een run die afbreekt terwijl het bestand nog wordt uitgerekend laat dan een nieuwe fingerprint naast een oud bestand achter, dat de volgende run als geldig leest.

Vraag daarom altijd Make_X en nooit Write_X: dat laatste schrijft het bestand zonder zijbestand. Een naam in Writers die niet oplost valt op bij het rekenen, met exit 1 en de melding dat de ExplicitSupplier niet is gevonden, voordat er een zijbestand ontstaat.

De schakelaars staan in ModelParameters:

Schakelaar Betekenis
BaseDataOntkoppeld Voorbereidende data die voor alle varianten gelijk is
VariantDataOntkoppeld Voorbereidende data per variant; in batchmodus gestuurd via de omgevingsvariabele met dezelfde naam
StandAllocatieOntkoppeld De standbestanden per zichtjaar; idem via omgevingsvariabele
IndicatorenOntkoppeld Wat een zichtjaar in de indicatoren van zijn voorganger nodig heeft, uit de ketentifs in plaats van uit de levende container van dat zichtjaar; idem via omgevingsvariabele
TabellenUitExport De regionale en landelijke tabellen lezen de kaarten terug die de export van hetzelfde zichtjaar heeft geschreven, in plaats van het levende item te berekenen; idem via omgevingsvariabele
OpbrengstdervingOntkoppeld De opbrengstdervingskaarten van de landbouw als tif, in plaats van ze opnieuw af te leiden uit de WWL-relatiedatabase
BAG_WP5_rel_Ontkoppeld De pandtypering van de BAG
AlwaysRemakeDecoupledFiles Zet alle hergebruik in een keer uit, ongeacht de fingerprints

De fingerprint

Naast een ontkoppeld bestand staat een zijbestand met dezelfde naam plus .params.txt. Daarin staat de lijst waarden die de uitkomst bepalen, als sleutel=waarde gescheiden door puntkomma’s, met Engelse sleutelnamen:

bgt_date=20260819;study_area=Nederland;variant=BAU;class_set=Default;...

Bij de start vergelijkt het model de opgeslagen fingerprint met de huidige. Zijn ze gelijk en staat het bestand er, dan mag het teruggelezen worden. Anders wordt het opnieuw gemaakt en overschreven. Het mechanisme staat in Templates/DecoupledFile_T.dms en wordt aangeroepen als Templates/DecoupledFile_T(FileName, Fingerprint).

Drie regels bij het samenstellen van een fingerprint:

  1. Zet er alle waarden in die de uitkomst bepalen, ook de waarden die al in de bestandsnaam zitten. Dat is bewust dubbelop: zo blijft de regel zonder uitzonderingen en is het zijbestand een leesbaar verslag van waar het bestand vandaan komt.
  2. Het gaat niet alleen om ModelParameters, maar ook om variantinstellingen, klassenindelingen en de datum of versie van de brondata.
  3. Neem bij een klassenset niet alleen de naam op maar ook de inhoud. Een wijziging binnen de set Default verandert de naam niet, dus zonder de vlaggenreeks zelf zou die wijziging onopgemerkt blijven.

Een waarde die vast in de code staat en niet als parameter, zoals een kernelstraal die via het itempad wordt gekozen of een drempel in een expressie, staat als letterlijke waarde in de fingerprint. Verandert die regel in de code, dan hoort de sleutel mee te veranderen, anders blijft een verouderd bestand geldig ogen.

Het mechanisme faalt de goede kant op. Ontbreekt het zijbestand, dan valt het model terug op cfg/no_fingerprint.txt, klopt de vergelijking nooit en wordt er opnieuw gerekend. Ontbreekt het bestand zelf, idem. Wie wil weten waarom een bestand wel of niet opnieuw gemaakt wordt, vraagt het item Decoupling/Explanation op: dat geeft het pad, of het bestand bestaat, de opgeslagen en de huidige fingerprint, en de uitkomst.

Wat nu een fingerprint heeft

Bestand Sleutels
VariantData/<variant>/EvidentBenut en de zon-variant daarvan bgt_date, study_area, variant, class_set, class_count, class_flags, grid_size
BaseData/Zonneladder/PerAdminDomain bgt_date, brt_date, bbg_year, study_area, admin_grid, step_count, steps
BaseData/StartState/Verblijfsrecreatie_Trends/Trends regio_unit, object_years, area_years, study_area, max_objects_beta, max_area_beta, bag_year, bbg_year
BaseData/Omgeving/BebouwdGebiedMetRand_<peiljaar>_<buffer>m_<studyarea>_<grid>.tif (bestaand bebouwd gebied met de rand van het stedelijke vrijstaande pakket) bbg_boundary_year, buffer_m, quad_segments, admin_grid, study_area
BaseData/Vastgoed/WoningtypeEengezins/<regio>_<van>_<tot>_BAG<jaar>_<studyarea>.mmd (gebouwd aandeel per woningtype binnen eengezins, voor het quotum in de woonallocatie) alloc_region, cbs_year, build_years, bag_year, bag_date, study_area, min_count, groups, rule
LogitCoef_toegepast_<studyarea>.mmd (herschatting logit verblijfsrecreatie) terms, y, study_area, admin_ref, bbg_year, iters
Verwervingskosten_Woningen_AdminDomain_<studyarea>.tif start_year, bag_recent_year, bag_date, nvm_date, nvm_coeff_year, constr_period_in_price, constr_period_count, constr_period_breaks, wp4_classes, trunc_upper_woononly, trunc_upper, trunc_lower, admin_grid, grid_size, study_area
Verwervingskosten_Niet_Woningen_AdminDomain_<studyarea>.tif woz_year, growth_office, growth_industry, growth_retail, woz_ratio_hall, woz_ratio_other, hall_fraction_default, unit_price_2017, max_storeys_per_cell, unit_price_start_year, start_year, bag_recent_year, bag_date, trunc_upper_woononly, trunc_upper, trunc_lower, combi_split, storey_height_nonres_cm, tolerance_nonres, admin_grid, grid_size, study_area
BaseData/Vastgoed/Sloopkosten_Woningen start_year, bag_date, wp4_classes, de vier sloopkentallen per woningtype (rijtjes, twee-onder-een-kap, vrijstaand, appartement), de truncatiegrenzen, wp5_decoupled, pand_inflate_size, grid en study_area
BaseData/Vastgoed/Sloopkosten_Niet_Woningen start_year, bag_date, bag_recent_year, lisa_start_year, bag_logistics_dynamic, logistics_pipeline_heuristic, de kentallen voor hal, overige utiliteit en asbest met drempeljaar, hall_fraction_default, max_storeys_per_cell, de truncatiegrenzen, storey_height_nonres_cm, tolerance_nonres, grid en study_area
BaseData/Grondgebruik/BRT/IsWoonkern en IsBRTEvidentBenut brt_date, source_layers, het selectielabel of de labels, selection_codes, grid en study_area
BaseData/Grondgebruik/IBIS: IsBedrijfsmatigTerrein, IsVol_grid, RestrictiefWonen, RestrictiefGevoeligeWerkfuncties ibis_year, ibis_sources, stacked_years, grid en study_area, plus per kaart wat haar selectie bepaalt: vol_fraction voor IsVol_grid; buffer_wonen_m, milieucat_count en milieucat_aanvulling voor de twee restrictiekaarten, met min_cat voor de gevoelige werkfuncties; bedrijfsmatig_types, ibis_class_codes en wloc_type_norm voor IsBedrijfsmatigTerrein
BaseData/Grondgebruik/LGN: fr_natuur_tot2500m, fr_water_500m en de twee n_gedekt-noemers lgn_year, de kernelstralen en resoluties, denominator_floor, covered, de klassenlijst met natuur- en watervlaggen, study_area
BaseData/Suitabilities/Werken_raw_<sector> (zes tifs) sector, coeff_date, bag_recent_year, bag_date, bbg_year, uai, de jaartallen van de zes afstandslagen, land_price_year, nodata_fill, grid en study_area
BaseData/Suitabilities/Waterberging: Depth_Norm, IsDiepGenoeg, IsMeer_Nabij_Norm, IsVeen_Nabij_Norm, Kwel_Norm, Wegzijging_Norm, InvUAI_Norm per kaart de bronversie (liwo, kwel, bofek, bag), de normerings- en nabijheidsparameters, bbg_year en lake_classes waar van toepassing, grid en study_area
BaseData/Grondgebruik/Natuur/Ring_rel (NNN-ringkaart) nnn_date, ring_resolution_m, ring_kernels_m, ring_count, ring_names, study_area
BaseData/Omgeving/N2K1000mBuffer en GrootWater1500mBuffer n2k_date respectievelijk bbg_year, buffer_m, grootwater_flags, grid en study_area; de N2K-buffer sluit groot water uit en draagt daarom de volledige fingerprint van de grootwaterbuffer genest mee
BaseData/Grondgebruik/NWB/StichtseRotonde nwb_date, street_name, buffer_out_m, buffer_in_m, grid en study_area
BaseData/Grondgebruik/NBP/nbp<jaargang>_INL_Beheertype_<studyarea>_<resolutie>.tif (Natuurbeheerplan) bron, laag, sql, landschapselementen, beheertypen, study_area, admin_domain
BaseData/StandBasisjaar/Landgebruik/Type en JaarVerandering (stand-administratie basisjaar) beheertype_bron, gaten_vullen, beheertype_passend, beheertype_masker, nbp (de fingerprint van de NBP-tif), mnp (die van de MNP-kaart), lgn_year, brp (die van de BRP-gewascodetif), bbg_year, start_year, study_area, admin_domain, landgebruik_set, de koppelkolommen lgn_hoofdklasse, lgn_bebouwd, lgn_landbouwtype en brp_landbouwtype, en de drie heuristiektabellen
BaseData/Grondgebruik/MNP/mnp_bau_INL_Beheertype_25m_dekking<d>_<studyarea>.tif (MNP-planpotentieelkaart op 25 meter) bron, minimum_dekking, mnp_val, beheertypen, study_area
BaseData/Landbouw/BRP_Gewascode_25m_<jaar>_<studyarea>.tif (gewascode per cel) brp_year, sql (de letterlijke SqlString, want die bepaalt bij overlap wie wint), study_area, grid_size
BaseData/Landbouw/WWL_Opbrengstderving/<levering>_<anker>_<gewas>_<studyarea>.tif (opbrengstderving per hydrologische levering, klimaatanker en gewas) hydrology, anchor, crop, crop_code, study_area, admin_domain, wwl_db (de netcdf), climate en climate_band, crop_codes en soil_codes (de volgorde waarin de banden van de netcdf zijn ingedeeld), ghg, glg en gxg_fill (de grondwaterstanden van de levering en het anker met hun vulling), soil en soil_fill (BOFEK met de vulling), irrigation en irrigation_map, gxg_scale, gxg_sign en lookup (de opzoeklogica); bij een levering met een veenoplegging daarbij veen_rule, veen_table, veen_cells en veen_checksum

De fingerprint van de basisjaarstand staat naast de Type-tif van het landgebruik, maar de leeskant van alle 31 standbestanden van het basisjaar toetst hem in haar IntegrityCheck, want die set wordt door Write/Generate in een keer geschreven. Faalt die toets, vraag dan Decoupling_Landgebruik/Explanation op en draai Write/Generate opnieuw, niet een los item, want het zijbestand hangt aan Generate.

Bij de verwervingskosten van niet-woningen is een deel van de fingerprint een benadering. De halfractie per cel komt uit de samenstelling van het vloeroppervlak en hangt aan de BAG-pandset en de hoogteregel op de vbo-oppervlakte, een keten die in geen parameterlijst zit. De sleutels bag_date, bag_recent_year, start_year en de truncatieparameters dekken een nieuwe BAG-levering of een andere truncatie, maar een wijziging in de hal-gewichten of de toedelingsmatrix zelf verandert de fingerprint niet. Zie de Descr bij de fingerprint in BaseData/Suitabilities/Verwervingskosten.dms.

Bij de opbrengstdervingskaarten staan er twee soorten sleutels in die niet uit een parameterlijst komen. De eerste zijn versiesleutels voor de rekenregels: lookup voor de opzoeking in de WWL-relatiedatabase, gxg_fill en soil_fill voor de vulling van de grondwaterstanden en van BOFEK waar de levering niets geeft. Een reparatie aan zo’n regel verandert de uitkomst zonder dat er een invoer verandert, en daarom verandert de sleutel mee met de regel. De tweede is een controlesom over de veenoplegging. Waar een levering de grondwaterstand laat afhangen van de gealloceerde veenbouwsteen (in de NL2120-toepassing NbSGenuanceerd), hangt de kaart aan een allocatie die te breed is om als lijst invoer op te nemen. De fingerprint draagt dan de opgelegde standen per bouwsteen en een som over de cellen van de bouwsteen maal een plekgewicht, in gehele getallen, zodat een verschuiving van bouwstenen de fingerprint verandert ook als het aantal veencellen gelijk blijft. De keerzijde: de fingerprint van zo’n kaart rekent de veenallocatie door, ook als de kaart alleen wordt teruggelezen.

Hoe lang het duurt voordat het zijbestand op de kaart volgt, verschilt per bestand: het is het deel van de fingerprint dat niet al voor de kaart zelf is uitgerekend. Bij de dervingskaarten is dat nul tot een seconde, want de veenallocatie die de sleutel nodig heeft zit ook onder de grondwaterstanden van de kaart. Bij de MNP-planpotentieelkaart, waar de fingerprint naast de kaart staat en apart wordt betaald, is het dertig seconden op een run van honderdvijftig.

Wat nog geen fingerprint heeft

Deze bestanden worden wel ontkoppeld maar zonder fingerprint. Hieronder staat per bestand welke invoer de uitkomst bepaalt en dus niet vergeten mag worden bij een wijziging:

  • Opbrengsten per ontwikkelpakket (Templates/VariantData_T/Geschiktheden.dms): NVM_filedate, NVM_coeff_Year, Model_StartYear, de eigenschappen van de ontwikkelpakketten (dichtheid, woonoppervlakte, kamers, hoogbouw, wegingen), het perceeloppervlak per pakket, de locatievariabelen, en via de correctie op het gemiddelde wegingseffect ook de restricties- en stimuliselectie voor wonen.
  • Stand basisjaar (BaseData/StateBasisjaar.dms) voor wonen, werken en de andere sectoren: Model_StartYear, BAG_file_date, LISA_StartYear, LISA_FileYear, de AltLISA-schakelaar en -varianten, en de klassenindelingen WP2xVSSH en Jobs6 zitten in geen fingerprint. De set draagt wel de fingerprint van de landgebruikstand hierboven, die de leeskant van alle standbestanden toetst: een andere beheertypenkaart of BRP-jaargang maakt de hele basisjaarstand ongeldig, een nieuwe BAG- of LISA-levering niet.
  • De pandtypering WP5 (SourceData/Vastgoed/BAG/AfleidingPandtype.dms): het aantal tegels, de inflate-grootte, de criteria voor functioneel pand en de WP5-indeling. Dit bestand is bovendien een positionele reeks over de pandenset van dat jaar: hoort het bij een andere pandenset, dan schuift de typering op. De integriteitscontrole op pand/WP5_rel vangt dat af.
  • De BGT-afgeleiden in SourceData/Grondgebruik/bgt.dms (groen, water, verhard, halfverharding, spoor, autoweg).
  • De gemiddelde verharding per BGT-klasse. De inhoud hangt af van ImperviousnessMapYear, maar dat jaar zit niet in de bestandsnaam. Zolang dat zo is, moet het bestand met de hand worden verwijderd wanneer die schakelaar verandert.
  • De geaggregeerde TIGRIS-claims per allocatieregio (Templates/Claims/Claims_TargetUnits_T.dms, bestanden Claims/TXL_<versie>/<scenario>/<variant>/<zichtjaar>/<regio>/Wonen.csv en Werken.csv): de TIGRIS-versie zit in de mapnaam, de regio-indeling waarop is gesommeerd niet. Die staat als TIGRIS_claims_CBSjaar in ModelParameters, met een integriteitscontrole op ClaimDir die het model laat stoppen zodra die jaargang afwijkt van RegioIndelingen_CBSjaar. De bestanden schrijven bovendien een kolom regio mee als regiosleutel.
  • Het proxybestand WP2xVSSH_Proxy per allocatieregio (BaseData/Verdeling_VSSH.dms), waarmee de claim eengezins en meergezins naar vrije sector en sociale huur wordt gesplitst: geen sleutelkolom, geen controle en geen fingerprint, terwijl de inhoud aan de regio-indeling hangt.
  • De logistieke pandenaanvulling (WritePrivData/LogistiekAanvulling.dms, bestand LogistiekPandenAanvulling_BAG<jaar>_LISA<jaar>_<versie>.mmd): BAG_RecentYear, LISA_StartYear en een versienummer zitten in de bestandsnaam. Let op: die naam staat niet als parameter in ModelParameters maar wordt aan beide kanten uitgeschreven, in SourceData/Vastgoed.dms en in WritePrivData/LogistiekAanvulling.dms, met het versienummer hard in de string. De versie moet worden opgehoogd bij elke wijziging aan de regeldrempels (kernaandeel, footprint-ondergrenzen, zoekafstand vanaf de pandrand): op de gedeelde opslag mag een eenmaal geplaatst bestand niet worden overschreven, dus elke inhoudelijke wijziging krijgt een nieuwe naam. Bijzonderheid: hergenereren kan alleen met BAG_LogistiekDynamisch op FALSE, omdat het pand-domein anders het nog te schrijven bestand als supplier meetrekt; een integriteitscontrole op de schrijf-unit dwingt dat af. Zie BAG verwerking voor wat het bestand doet.

Let bij een ontkoppeld bestand per regio op een tweede vorm van veroudering, naast de inhoud. Een csv of mmd over een regio-eenheid draagt geen rijlabels, dus de koppeling loopt op rijvolgorde. Verandert de regio-indeling terwijl het aantal regio’s gelijk blijft, en dat blijft het: NVM staat al jaren op 76, COROP op 40 en provincie op 12, dan hangen de waarden aan verschoven regio’s zonder dat er iets fout gaat. Er is geen foutmelding en geen kanarie. Wat daar wel tegen helpt is de indeling als parameter vastleggen in plaats van hem uit de brondata af te leiden, en de gebruikte jaargang naast het bestand registreren en bij het inlezen toetsen. Voor de claims is dat de opzet met TIGRIS_claims_CBSjaar en de kolom regio; voor het proxybestand nog niet.

De standbestanden per zichtjaar hebben bewust geen fingerprint. Die hangen af van vrijwel de hele configuratie, inclusief alle bestanden hierboven, dus een fingerprint zou daar de hele configuratie moeten omvatten. Ze worden per run beheerd met StandAllocatieOntkoppeld. Hetzelfde geldt voor de ketentifs en de kaarten van de indicatorenexport: die worden per run beheerd met IndicatorenOntkoppeld en TabellenUitExport, een bestaande tif wordt gelezen, en de batch toetst alleen of ze er staan en of ze jonger zijn dan de stand van dat zichtjaar. Zie Batchrun draaien.

Wat te doen bij een wijziging

Verander je iets dat de uitkomst van een ontkoppeld bestand bepaalt, dan moet die invoer in de fingerprint staan, of moet het bestand op een andere manier ongeldig worden gemaakt: een nieuwe bestandsnaam, het bestand weggooien, of AlwaysRemakeDecoupledFiles aanzetten voor die run.

Dat geldt ook voor bestanden zonder fingerprint. Een voorbeeld: het peiljaar van de begrenzing bebouwd gebied (BBG_Year) werkt via de grondproductiekosten door in de residuele waarde en dus in de allocatie zelf, terwijl de standbestanden per zichtjaar geen fingerprint hebben. Zo’n wijziging vraagt dus een volledige herberekening van alle zichtjaren.

Twee GeoDMS-valkuilen

  • ExplicitSuppliers op een container lift niet mee wanneer je een los kind opvraagt. Bij containers die met for_each worden opgebouwd kan het schrijven van het zijbestand dus niet aan de container hangen. Vraag je zo’n item rechtstreeks op, dan wordt het wel gerekend en geschreven maar komt er geen zijbestand, en rekent de volgende run het opnieuw uit.
  • PropValue(item, 'StorageName') geeft de expressietekst terug, niet de uitkomst. Dat werkt wanneer je hem weer als StorageName gebruikt, maar niet als invoer voor ExistingFile. Zet het pad dan als eigen parameter<String> neer en verwijs daar vanuit beide kanten naar.

Verantwoording

De opzet hierboven is gevormd in de volgende issues; daar staan de afwegingen en de metingen.

  • #585: de waarneming dat een regiobestand zonder rijlabels bij een gewijzigde indeling stil aan verschoven regio’s gaat hangen.
  • #613: de BRP-gewascode per cel als ontkoppelde tif, met de SqlString in de fingerprint omdat die bij overlap beslist wie wint.
  • #691: de jaargang van de regio-indeling als parameter naast de claims, de toets bij het inlezen en de kolom regio in de claimbestanden.
  • #751: de stand-administratie van het landgebruik in het basisjaar, met de koppelkolommen en de heuristiek als invoer.
  • #759: de zonneladder op AdminDomain.
  • #770: IsVol_grid op AdminDomain, met de resolutie in de bestandsnaam.
  • #776: de beheertypenkaart uit het Natuurbeheerplan met eigen fingerprint, en de fingerprint van de basisjaarstand die de leeskant van alle standbestanden toetst.
  • #795: de MNP-planpotentieelkaart op 25 meter met eigen fingerprint.
  • #824: de ketens tussen de zichtjaren in de indicatoren via tifs, bewust zonder fingerprint.
  • #831: de tabellen lezen de kaarten terug die de export heeft geschreven.
  • #837: de opbrengstdervingskaarten met eigen fingerprint, met versiesleutels voor de opzoeklogica en de twee vullingen en een controlesom over de veenoplegging.