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_Xrekent zonder tussenbestand, voor wie het ontkoppelen helemaal uit wil zetten.Read_Xleest het bestaande bestand.Make_Xlevert het bestand plus zijn zijbestand. Het rekenen en schrijven zelf staat inWrite_X, dat deStorageNamedraagt;Make_X := Write_XheeftDecoupling/Write_FingerprintalsExplicitSupplier.
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:
- 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.
- Het gaat niet alleen om
ModelParameters, maar ook om variantinstellingen, klassenindelingen en de datum of versie van de brondata. - Neem bij een klassenset niet alleen de naam op maar ook de inhoud. Een wijziging binnen de set
Defaultverandert 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 oppand/WP5_relvangt 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, bestandenClaims/TXL_<versie>/<scenario>/<variant>/<zichtjaar>/<regio>/Wonen.csvenWerken.csv): de TIGRIS-versie zit in de mapnaam, de regio-indeling waarop is gesommeerd niet. Die staat alsTIGRIS_claims_CBSjaarinModelParameters, met een integriteitscontrole opClaimDirdie het model laat stoppen zodra die jaargang afwijkt vanRegioIndelingen_CBSjaar. De bestanden schrijven bovendien een kolomregiomee als regiosleutel. - Het proxybestand
WP2xVSSH_Proxyper 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, bestandLogistiekPandenAanvulling_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 inModelParametersmaar wordt aan beide kanten uitgeschreven, inSourceData/Vastgoed.dmsen inWritePrivData/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 metBAG_LogistiekDynamischop 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
ExplicitSuppliersop een container lift niet mee wanneer je een los kind opvraagt. Bij containers die metfor_eachworden 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 alsStorageNamegebruikt, maar niet als invoer voorExistingFile. Zet het pad dan als eigenparameter<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.