Passed
Pull Request — dev (#934)
by
unknown
02:02
created

cts_heat_demand_share()   A

Complexity

Conditions 2

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 25
rs 9.75
c 0
b 0
f 0
cc 2
nop 1
1
"""
2
This module does sanity checks for both the eGon2035 and the eGon100RE scenario
3
separately where a percentage error is given to showcase difference in output
4
and input values. Please note that there are missing input technologies in the
5
supply tables.
6
Authors: @ALonso, @dana, @nailend, @nesnoj, @khelfen
7
"""
8
from math import isclose
9
from pathlib import Path
10
11
from sqlalchemy import Numeric
12
from sqlalchemy.sql import and_, cast, func, or_
13
import matplotlib.pyplot as plt
14
import numpy as np
15
import pandas as pd
16
import seaborn as sns
17
18
from egon.data import config, db, logger
19
from egon.data.datasets import Dataset
20
from egon.data.datasets.electricity_demand_timeseries.cts_buildings import (
21
    EgonCtsElectricityDemandBuildingShare,
22
    EgonCtsHeatDemandBuildingShare,
23
)
24
from egon.data.datasets.emobility.motorized_individual_travel.db_classes import (
25
    EgonEvCountMunicipality,
26
    EgonEvCountMvGridDistrict,
27
    EgonEvCountRegistrationDistrict,
28
    EgonEvMvGridDistrict,
29
    EgonEvPool,
30
    EgonEvTrip,
31
)
32
from egon.data.datasets.emobility.motorized_individual_travel.helpers import (
33
    DATASET_CFG,
34
    read_simbev_metadata_file,
35
)
36
from egon.data.datasets.etrago_setup import (
37
    EgonPfHvLink,
38
    EgonPfHvLinkTimeseries,
39
    EgonPfHvLoad,
40
    EgonPfHvLoadTimeseries,
41
    EgonPfHvStore,
42
    EgonPfHvStoreTimeseries,
43
)
44
from egon.data.datasets.power_plants.pv_rooftop_buildings import (
45
    EPSG,
46
    PV_CAP_PER_SQ_M,
47
    ROOF_FACTOR,
48
    SCENARIOS,
49
    add_overlay_id_to_buildings,
50
    drop_buildings_outside_grids,
51
    federal_state_data,
52
    grid_districts,
53
    load_building_data,
54
    overlay_grid_districts_with_counties,
55
    scenario_data,
56
)
57
from egon.data.datasets.scenario_parameters import get_sector_parameters
58
import egon.data
59
60
TESTMODE_OFF = (
61
    config.settings()["egon-data"]["--dataset-boundary"] == "Everything"
62
)
63
64
65
class SanityChecks(Dataset):
66
    def __init__(self, dependencies):
67
        super().__init__(
68
            name="SanityChecks",
69
            version="0.0.5",
70
            dependencies=dependencies,
71
            tasks={
72
                etrago_eGon2035_electricity,
73
                etrago_eGon2035_heat,
74
                residential_electricity_annual_sum,
75
                residential_electricity_hh_refinement,
76
                cts_electricity_demand_share,
77
                cts_heat_demand_share,
78
                sanitycheck_emobility_mit,
79
                sanitycheck_pv_rooftop_buildings,
80
            },
81
        )
82
83
84
def etrago_eGon2035_electricity():
85
    """Execute basic sanity checks.
86
87
    Returns print statements as sanity checks for the electricity sector in
88
    the eGon2035 scenario.
89
90
    Parameters
91
    ----------
92
    None
93
94
    Returns
95
    -------
96
    None
97
    """
98
99
    scn = "eGon2035"
100
101
    # Section to check generator capacities
102
    logger.info(f"Sanity checks for scenario {scn}")
103
    logger.info(
104
        "For German electricity generators the following deviations between "
105
        "the inputs and outputs can be observed:"
106
    )
107
108
    carriers_electricity = [
109
        "others",
110
        "reservoir",
111
        "run_of_river",
112
        "oil",
113
        "wind_onshore",
114
        "wind_offshore",
115
        "solar",
116
        "solar_rooftop",
117
        "biomass",
118
    ]
119
120
    for carrier in carriers_electricity:
121
122
        if carrier == "biomass":
123
            sum_output = db.select_dataframe(
124
                """SELECT scn_name, SUM(p_nom::numeric) as output_capacity_mw
125
                    FROM grid.egon_etrago_generator
126
                    WHERE bus IN (
127
                        SELECT bus_id FROM grid.egon_etrago_bus
128
                        WHERE scn_name = 'eGon2035'
129
                        AND country = 'DE')
130
                    AND carrier IN ('biomass', 'industrial_biomass_CHP',
131
                    'central_biomass_CHP')
132
                    GROUP BY (scn_name);
133
                """,
134
                warning=False,
135
            )
136
137
        else:
138
            sum_output = db.select_dataframe(
139
                f"""SELECT scn_name,
140
                 SUM(p_nom::numeric) as output_capacity_mw
141
                         FROM grid.egon_etrago_generator
142
                         WHERE scn_name = '{scn}'
143
                         AND carrier IN ('{carrier}')
144
                         AND bus IN
145
                             (SELECT bus_id
146
                               FROM grid.egon_etrago_bus
147
                               WHERE scn_name = 'eGon2035'
148
                               AND country = 'DE')
149
                         GROUP BY (scn_name);
150
                    """,
151
                warning=False,
152
            )
153
154
        sum_input = db.select_dataframe(
155
            f"""SELECT carrier, SUM(capacity::numeric) as input_capacity_mw
156
                     FROM supply.egon_scenario_capacities
157
                     WHERE carrier= '{carrier}'
158
                     AND scenario_name ='{scn}'
159
                     GROUP BY (carrier);
160
                """,
161
            warning=False,
162
        )
163
164 View Code Duplication
        if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
165
            sum_output.output_capacity_mw.sum() == 0
166
            and sum_input.input_capacity_mw.sum() == 0
167
        ):
168
            logger.info(
169
                f"No capacity for carrier '{carrier}' needed to be"
170
                f" distributed. Everything is fine"
171
            )
172
173
        elif (
174
            sum_input.input_capacity_mw.sum() > 0
175
            and sum_output.output_capacity_mw.sum() == 0
176
        ):
177
            logger.info(
178
                f"Error: Capacity for carrier '{carrier}' was not distributed "
179
                f"at all!"
180
            )
181
182
        elif (
183
            sum_output.output_capacity_mw.sum() > 0
184
            and sum_input.input_capacity_mw.sum() == 0
185
        ):
186
            logger.info(
187
                f"Error: Eventhough no input capacity was provided for carrier"
188
                f"'{carrier}' a capacity got distributed!"
189
            )
190
191
        else:
192
            sum_input["error"] = (
193
                (sum_output.output_capacity_mw - sum_input.input_capacity_mw)
194
                / sum_input.input_capacity_mw
195
            ) * 100
196
            g = sum_input["error"].values[0]
197
198
            logger.info(f"{carrier}: " + str(round(g, 2)) + " %")
199
200
    # Section to check storage units
201
202
    logger.info(f"Sanity checks for scenario {scn}")
203
    logger.info(
204
        "For German electrical storage units the following deviations between"
205
        "the inputs and outputs can be observed:"
206
    )
207
208
    carriers_electricity = ["pumped_hydro"]
209
210
    for carrier in carriers_electricity:
211
212
        sum_output = db.select_dataframe(
213
            f"""SELECT scn_name, SUM(p_nom::numeric) as output_capacity_mw
214
                         FROM grid.egon_etrago_storage
215
                         WHERE scn_name = '{scn}'
216
                         AND carrier IN ('{carrier}')
217
                         AND bus IN
218
                             (SELECT bus_id
219
                               FROM grid.egon_etrago_bus
220
                               WHERE scn_name = 'eGon2035'
221
                               AND country = 'DE')
222
                         GROUP BY (scn_name);
223
                    """,
224
            warning=False,
225
        )
226
227
        sum_input = db.select_dataframe(
228
            f"""SELECT carrier, SUM(capacity::numeric) as input_capacity_mw
229
                     FROM supply.egon_scenario_capacities
230
                     WHERE carrier= '{carrier}'
231
                     AND scenario_name ='{scn}'
232
                     GROUP BY (carrier);
233
                """,
234
            warning=False,
235
        )
236
237 View Code Duplication
        if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
238
            sum_output.output_capacity_mw.sum() == 0
239
            and sum_input.input_capacity_mw.sum() == 0
240
        ):
241
            print(
242
                f"No capacity for carrier '{carrier}' needed to be "
243
                f"distributed. Everything is fine"
244
            )
245
246
        elif (
247
            sum_input.input_capacity_mw.sum() > 0
248
            and sum_output.output_capacity_mw.sum() == 0
249
        ):
250
            print(
251
                f"Error: Capacity for carrier '{carrier}' was not distributed"
252
                f" at all!"
253
            )
254
255
        elif (
256
            sum_output.output_capacity_mw.sum() > 0
257
            and sum_input.input_capacity_mw.sum() == 0
258
        ):
259
            print(
260
                f"Error: Eventhough no input capacity was provided for carrier"
261
                f" '{carrier}' a capacity got distributed!"
262
            )
263
264
        else:
265
            sum_input["error"] = (
266
                (sum_output.output_capacity_mw - sum_input.input_capacity_mw)
267
                / sum_input.input_capacity_mw
268
            ) * 100
269
            g = sum_input["error"].values[0]
270
271
            print(f"{carrier}: " + str(round(g, 2)) + " %")
272
273
    # Section to check loads
274
275
    print(
276
        "For German electricity loads the following deviations between the"
277
        " input and output can be observed:"
278
    )
279
280
    output_demand = db.select_dataframe(
281
        """SELECT a.scn_name, a.carrier,  SUM((SELECT SUM(p)
282
        FROM UNNEST(b.p_set) p))/1000000::numeric as load_twh
283
            FROM grid.egon_etrago_load a
284
            JOIN grid.egon_etrago_load_timeseries b
285
            ON (a.load_id = b.load_id)
286
            JOIN grid.egon_etrago_bus c
287
            ON (a.bus=c.bus_id)
288
            AND b.scn_name = 'eGon2035'
289
            AND a.scn_name = 'eGon2035'
290
            AND a.carrier = 'AC'
291
            AND c.scn_name= 'eGon2035'
292
            AND c.country='DE'
293
            GROUP BY (a.scn_name, a.carrier);
294
295
    """,
296
        warning=False,
297
    )["load_twh"].values[0]
298
299
    input_cts_ind = db.select_dataframe(
300
        """SELECT scenario,
301
         SUM(demand::numeric/1000000) as demand_mw_regio_cts_ind
302
            FROM demand.egon_demandregio_cts_ind
303
            WHERE scenario= 'eGon2035'
304
            AND year IN ('2035')
305
            GROUP BY (scenario);
306
307
        """,
308
        warning=False,
309
    )["demand_mw_regio_cts_ind"].values[0]
310
311
    input_hh = db.select_dataframe(
312
        """SELECT scenario, SUM(demand::numeric/1000000) as demand_mw_regio_hh
313
            FROM demand.egon_demandregio_hh
314
            WHERE scenario= 'eGon2035'
315
            AND year IN ('2035')
316
            GROUP BY (scenario);
317
        """,
318
        warning=False,
319
    )["demand_mw_regio_hh"].values[0]
320
321
    input_demand = input_hh + input_cts_ind
322
323
    e = round((output_demand - input_demand) / input_demand, 2) * 100
324
325
    print(f"electricity demand: {e} %")
326
327
328
def etrago_eGon2035_heat():
329
    """Execute basic sanity checks.
330
331
    Returns print statements as sanity checks for the heat sector in
332
    the eGon2035 scenario.
333
334
    Parameters
335
    ----------
336
    None
337
338
    Returns
339
    -------
340
    None
341
    """
342
343
    # Check input and output values for the carriers "others",
344
    # "reservoir", "run_of_river" and "oil"
345
346
    scn = "eGon2035"
347
348
    # Section to check generator capacities
349
    print(f"Sanity checks for scenario {scn}")
350
    print(
351
        "For German heat demands the following deviations between the inputs"
352
        " and outputs can be observed:"
353
    )
354
355
    # Sanity checks for heat demand
356
357
    output_heat_demand = db.select_dataframe(
358
        """SELECT a.scn_name,
359
          (SUM(
360
          (SELECT SUM(p) FROM UNNEST(b.p_set) p))/1000000)::numeric as load_twh
361
            FROM grid.egon_etrago_load a
362
            JOIN grid.egon_etrago_load_timeseries b
363
            ON (a.load_id = b.load_id)
364
            JOIN grid.egon_etrago_bus c
365
            ON (a.bus=c.bus_id)
366
            AND b.scn_name = 'eGon2035'
367
            AND a.scn_name = 'eGon2035'
368
            AND c.scn_name= 'eGon2035'
369
            AND c.country='DE'
370
            AND a.carrier IN ('rural_heat', 'central_heat')
371
            GROUP BY (a.scn_name);
372
        """,
373
        warning=False,
374
    )["load_twh"].values[0]
375
376
    input_heat_demand = db.select_dataframe(
377
        """SELECT scenario, SUM(demand::numeric/1000000) as demand_mw_peta_heat
378
            FROM demand.egon_peta_heat
379
            WHERE scenario= 'eGon2035'
380
            GROUP BY (scenario);
381
        """,
382
        warning=False,
383
    )["demand_mw_peta_heat"].values[0]
384
385
    e_demand = (
386
        round((output_heat_demand - input_heat_demand) / input_heat_demand, 2)
387
        * 100
388
    )
389
390
    logger.info(f"heat demand: {e_demand} %")
391
392
    # Sanity checks for heat supply
393
394
    logger.info(
395
        "For German heat supplies the following deviations between the inputs "
396
        "and outputs can be observed:"
397
    )
398
399
    # Comparison for central heat pumps
400
    heat_pump_input = db.select_dataframe(
401
        """SELECT carrier, SUM(capacity::numeric) as Urban_central_heat_pump_mw
402
            FROM supply.egon_scenario_capacities
403
            WHERE carrier= 'urban_central_heat_pump'
404
            AND scenario_name IN ('eGon2035')
405
            GROUP BY (carrier);
406
        """,
407
        warning=False,
408
    )["urban_central_heat_pump_mw"].values[0]
409
410
    heat_pump_output = db.select_dataframe(
411
        """SELECT carrier, SUM(p_nom::numeric) as Central_heat_pump_mw
412
            FROM grid.egon_etrago_link
413
            WHERE carrier= 'central_heat_pump'
414
            AND scn_name IN ('eGon2035')
415
            GROUP BY (carrier);
416
    """,
417
        warning=False,
418
    )["central_heat_pump_mw"].values[0]
419
420
    e_heat_pump = (
421
        round((heat_pump_output - heat_pump_input) / heat_pump_output, 2) * 100
422
    )
423
424
    logger.info(f"'central_heat_pump': {e_heat_pump} % ")
425
426
    # Comparison for residential heat pumps
427
428
    input_residential_heat_pump = db.select_dataframe(
429
        """SELECT carrier, SUM(capacity::numeric) as residential_heat_pump_mw
430
            FROM supply.egon_scenario_capacities
431
            WHERE carrier= 'residential_rural_heat_pump'
432
            AND scenario_name IN ('eGon2035')
433
            GROUP BY (carrier);
434
        """,
435
        warning=False,
436
    )["residential_heat_pump_mw"].values[0]
437
438
    output_residential_heat_pump = db.select_dataframe(
439
        """SELECT carrier, SUM(p_nom::numeric) as rural_heat_pump_mw
440
            FROM grid.egon_etrago_link
441
            WHERE carrier= 'rural_heat_pump'
442
            AND scn_name IN ('eGon2035')
443
            GROUP BY (carrier);
444
    """,
445
        warning=False,
446
    )["rural_heat_pump_mw"].values[0]
447
448
    e_residential_heat_pump = (
449
        round(
450
            (output_residential_heat_pump - input_residential_heat_pump)
451
            / input_residential_heat_pump,
452
            2,
453
        )
454
        * 100
455
    )
456
    logger.info(f"'residential heat pumps': {e_residential_heat_pump} %")
457
458
    # Comparison for resistive heater
459
    resistive_heater_input = db.select_dataframe(
460
        """SELECT carrier,
461
         SUM(capacity::numeric) as Urban_central_resistive_heater_MW
462
            FROM supply.egon_scenario_capacities
463
            WHERE carrier= 'urban_central_resistive_heater'
464
            AND scenario_name IN ('eGon2035')
465
            GROUP BY (carrier);
466
        """,
467
        warning=False,
468
    )["urban_central_resistive_heater_mw"].values[0]
469
470
    resistive_heater_output = db.select_dataframe(
471
        """SELECT carrier, SUM(p_nom::numeric) as central_resistive_heater_MW
472
            FROM grid.egon_etrago_link
473
            WHERE carrier= 'central_resistive_heater'
474
            AND scn_name IN ('eGon2035')
475
            GROUP BY (carrier);
476
        """,
477
        warning=False,
478
    )["central_resistive_heater_mw"].values[0]
479
480
    e_resistive_heater = (
481
        round(
482
            (resistive_heater_output - resistive_heater_input)
483
            / resistive_heater_input,
484
            2,
485
        )
486
        * 100
487
    )
488
489
    logger.info(f"'resistive heater': {e_resistive_heater} %")
490
491
    # Comparison for solar thermal collectors
492
493
    input_solar_thermal = db.select_dataframe(
494
        """SELECT carrier, SUM(capacity::numeric) as solar_thermal_collector_mw
495
            FROM supply.egon_scenario_capacities
496
            WHERE carrier= 'urban_central_solar_thermal_collector'
497
            AND scenario_name IN ('eGon2035')
498
            GROUP BY (carrier);
499
        """,
500
        warning=False,
501
    )["solar_thermal_collector_mw"].values[0]
502
503
    output_solar_thermal = db.select_dataframe(
504
        """SELECT carrier, SUM(p_nom::numeric) as solar_thermal_collector_mw
505
            FROM grid.egon_etrago_generator
506
            WHERE carrier= 'solar_thermal_collector'
507
            AND scn_name IN ('eGon2035')
508
            GROUP BY (carrier);
509
        """,
510
        warning=False,
511
    )["solar_thermal_collector_mw"].values[0]
512
513
    e_solar_thermal = (
514
        round(
515
            (output_solar_thermal - input_solar_thermal) / input_solar_thermal,
516
            2,
517
        )
518
        * 100
519
    )
520
    logger.info(f"'solar thermal collector': {e_solar_thermal} %")
521
522
    # Comparison for geothermal
523
524
    input_geo_thermal = db.select_dataframe(
525
        """SELECT carrier,
526
         SUM(capacity::numeric) as Urban_central_geo_thermal_MW
527
            FROM supply.egon_scenario_capacities
528
            WHERE carrier= 'urban_central_geo_thermal'
529
            AND scenario_name IN ('eGon2035')
530
            GROUP BY (carrier);
531
        """,
532
        warning=False,
533
    )["urban_central_geo_thermal_mw"].values[0]
534
535
    output_geo_thermal = db.select_dataframe(
536
        """SELECT carrier, SUM(p_nom::numeric) as geo_thermal_MW
537
            FROM grid.egon_etrago_generator
538
            WHERE carrier= 'geo_thermal'
539
            AND scn_name IN ('eGon2035')
540
            GROUP BY (carrier);
541
    """,
542
        warning=False,
543
    )["geo_thermal_mw"].values[0]
544
545
    e_geo_thermal = (
546
        round((output_geo_thermal - input_geo_thermal) / input_geo_thermal, 2)
547
        * 100
548
    )
549
    logger.info(f"'geothermal': {e_geo_thermal} %")
550
551
552
def residential_electricity_annual_sum(rtol=1e-5):
553
    """Sanity check for dataset electricity_demand_timeseries :
554
    Demand_Building_Assignment
555
556
    Aggregate the annual demand of all census cells at NUTS3 to compare
557
    with initial scaling parameters from DemandRegio.
558
    """
559
560
    df_nuts3_annual_sum = db.select_dataframe(
561
        sql="""
562
        SELECT dr.nuts3, dr.scenario, dr.demand_regio_sum, profiles.profile_sum
563
        FROM (
564
            SELECT scenario, SUM(demand) AS profile_sum, vg250_nuts3
565
            FROM demand.egon_demandregio_zensus_electricity AS egon,
566
             boundaries.egon_map_zensus_vg250 AS boundaries
567
            Where egon.zensus_population_id = boundaries.zensus_population_id
568
            AND sector = 'residential'
569
            GROUP BY vg250_nuts3, scenario
570
            ) AS profiles
571
        JOIN (
572
            SELECT nuts3, scenario, sum(demand) AS demand_regio_sum
573
            FROM demand.egon_demandregio_hh
574
            GROUP BY year, scenario, nuts3
575
              ) AS dr
576
        ON profiles.vg250_nuts3 = dr.nuts3 and profiles.scenario  = dr.scenario
577
        """
578
    )
579
580
    np.testing.assert_allclose(
581
        actual=df_nuts3_annual_sum["profile_sum"],
582
        desired=df_nuts3_annual_sum["demand_regio_sum"],
583
        rtol=rtol,
584
        verbose=False,
585
    )
586
587
    logger.info(
588
        "Aggregated annual residential electricity demand"
589
        " matches with DemandRegio at NUTS-3."
590
    )
591
592
593
def residential_electricity_hh_refinement(rtol=1e-5):
594
    """Sanity check for dataset electricity_demand_timeseries :
595
    Household Demands
596
597
    Check sum of aggregated household types after refinement method
598
    was applied and compare it to the original census values."""
599
600
    df_refinement = db.select_dataframe(
601
        sql="""
602
        SELECT refined.nuts3, refined.characteristics_code,
603
                refined.sum_refined::int, census.sum_census::int
604
        FROM(
605
            SELECT nuts3, characteristics_code, SUM(hh_10types) as sum_refined
606
            FROM society.egon_destatis_zensus_household_per_ha_refined
607
            GROUP BY nuts3, characteristics_code)
608
            AS refined
609
        JOIN(
610
            SELECT t.nuts3, t.characteristics_code, sum(orig) as sum_census
611
            FROM(
612
                SELECT nuts3, cell_id, characteristics_code,
613
                        sum(DISTINCT(hh_5types))as orig
614
                FROM society.egon_destatis_zensus_household_per_ha_refined
615
                GROUP BY cell_id, characteristics_code, nuts3) AS t
616
            GROUP BY t.nuts3, t.characteristics_code    ) AS census
617
        ON refined.nuts3 = census.nuts3
618
        AND refined.characteristics_code = census.characteristics_code
619
    """
620
    )
621
622
    np.testing.assert_allclose(
623
        actual=df_refinement["sum_refined"],
624
        desired=df_refinement["sum_census"],
625
        rtol=rtol,
626
        verbose=False,
627
    )
628
629
    logger.info("All Aggregated household types match at NUTS-3.")
630
631
632
def cts_electricity_demand_share(rtol=1e-5):
633
    """Sanity check for dataset electricity_demand_timeseries :
634
    CtsBuildings
635
636
    Check sum of aggregated cts electricity demand share which equals to one
637
    for every substation as the substation profile is linearly disaggregated
638
    to all buildings."""
639
640
    with db.session_scope() as session:
641
        cells_query = session.query(EgonCtsElectricityDemandBuildingShare)
642
643
    df_demand_share = pd.read_sql(
644
        cells_query.statement, cells_query.session.bind, index_col=None
645
    )
646
647
    np.testing.assert_allclose(
648
        actual=df_demand_share.groupby(["bus_id", "scenario"])[
649
            "profile_share"
650
        ].sum(),
651
        desired=1,
652
        rtol=rtol,
653
        verbose=False,
654
    )
655
656
    logger.info("The aggregated demand shares equal to one!.")
657
658
659
def cts_heat_demand_share(rtol=1e-5):
660
    """Sanity check for dataset electricity_demand_timeseries
661
    : CtsBuildings
662
663
    Check sum of aggregated cts heat demand share which equals to one
664
    for every substation as the substation profile is linearly disaggregated
665
    to all buildings."""
666
667
    with db.session_scope() as session:
668
        cells_query = session.query(EgonCtsHeatDemandBuildingShare)
669
670
    df_demand_share = pd.read_sql(
671
        cells_query.statement, cells_query.session.bind, index_col=None
672
    )
673
674
    np.testing.assert_allclose(
675
        actual=df_demand_share.groupby(["bus_id", "scenario"])[
676
            "profile_share"
677
        ].sum(),
678
        desired=1,
679
        rtol=rtol,
680
        verbose=False,
681
    )
682
683
    logger.info("The aggregated demand shares equal to one!.")
684
685
686
def sanitycheck_pv_rooftop_buildings():
687
    def egon_power_plants_pv_roof_building():
688
        sql = """
689
        SELECT *
690
        FROM supply.egon_power_plants_pv_roof_building
691
        """
692
693
        return db.select_dataframe(sql, index_col="index")
694
695
    pv_roof_df = egon_power_plants_pv_roof_building()
696
697
    valid_buildings_gdf = load_building_data()
698
699
    valid_buildings_gdf = valid_buildings_gdf.assign(
700
        bus_id=valid_buildings_gdf.bus_id.astype(int),
701
        overlay_id=valid_buildings_gdf.overlay_id.astype(int),
702
        max_cap=valid_buildings_gdf.building_area.multiply(
703
            ROOF_FACTOR * PV_CAP_PER_SQ_M
704
        ),
705
    )
706
707
    merge_df = pv_roof_df.merge(
708
        valid_buildings_gdf[["building_area"]],
709
        how="left",
710
        left_on="building_id",
711
        right_index=True,
712
    )
713
714
    assert (
715
        len(merge_df.loc[merge_df.building_area.isna()]) == 0
716
    ), f"{len(merge_df.loc[merge_df.building_area.isna()])} != 0"
717
718
    scenarios = ["status_quo", "eGon2035"]
719
720
    base_path = Path(egon.data.__path__[0]).resolve()
721
722
    res_dir = base_path / "sanity_checks"
723
724
    res_dir.mkdir(parents=True, exist_ok=True)
725
726
    for scenario in scenarios:
727
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 8))
728
729
        scenario_df = merge_df.loc[merge_df.scenario == scenario]
730
731
        logger.info(
732
            scenario + " Capacity:\n" + str(scenario_df.capacity.describe())
733
        )
734
735
        small_gens_df = scenario_df.loc[scenario_df.capacity < 100]
736
737
        sns.histplot(data=small_gens_df, x="capacity", ax=ax1).set_title(
738
            scenario
739
        )
740
741
        sns.scatterplot(
742
            data=small_gens_df, x="capacity", y="building_area", ax=ax2
743
        ).set_title(scenario)
744
745
        plt.tight_layout()
746
747
        plt.savefig(
748
            res_dir / f"{scenario}_pv_rooftop_distribution.png",
749
            bbox_inches="tight",
750
        )
751
752
    for scenario in SCENARIOS:
753
        if scenario == "eGon2035":
754
            assert isclose(
755
                scenario_data(scenario=scenario).capacity.sum(),
756
                merge_df.loc[merge_df.scenario == scenario].capacity.sum(),
757
                rel_tol=1e-02,
758
            ), (
759
                f"{scenario_data(scenario=scenario).capacity.sum()} != "
760
                f"{merge_df.loc[merge_df.scenario == scenario].capacity.sum()}"
761
            )
762
        elif scenario == "eGon100RE":
763
            sources = config.datasets()["solar_rooftop"]["sources"]
764
765
            target = db.select_dataframe(
766
                f"""
767
                SELECT capacity
768
                FROM {sources['scenario_capacities']['schema']}.
769
                {sources['scenario_capacities']['table']} a
770
                WHERE carrier = 'solar_rooftop'
771
                AND scenario_name = '{scenario}'
772
                """
773
            ).capacity[0]
774
775
            dataset = config.settings()["egon-data"]["--dataset-boundary"]
776
777
            if dataset == "Schleswig-Holstein":
778
                # since the required data is missing for a SH run, it is implemented
779
                # manually here
780
                total_2035 = 84070
781
                sh_2035 = 2700
782
783
                share = sh_2035 / total_2035
784
785
                target *= share
786
787
            assert isclose(
788
                target,
789
                merge_df.loc[merge_df.scenario == scenario].capacity.sum(),
790
            ), (
791
                f"{target} != "
792
                f"{merge_df.loc[merge_df.scenario == scenario].capacity.sum()}"
793
            )
794
        else:
795
            raise ValueError(f"Scenario {scenario} is not valid.")
796
797
798
def sanitycheck_emobility_mit():
799
    """Execute sanity checks for eMobility: motorized individual travel
800
801
    Checks data integrity for eGon2035, eGon2035_lowflex and eGon100RE scenario
802
    using assertions:
803
      1. Allocated EV numbers and EVs allocated to grid districts
804
      2. Trip data (original inout data from simBEV)
805
      3. Model data in eTraGo PF tables (grid.egon_etrago_*)
806
807
    Parameters
808
    ----------
809
    None
810
811
    Returns
812
    -------
813
    None
814
    """
815
816
    def check_ev_allocation():
817
        # Get target number for scenario
818
        ev_count_target = scenario_variation_parameters["ev_count"]
819
        print(f"  Target count: {str(ev_count_target)}")
820
821
        # Get allocated numbers
822
        ev_counts_dict = {}
823
        with db.session_scope() as session:
824
            for table, level in zip(
825
                [
826
                    EgonEvCountMvGridDistrict,
827
                    EgonEvCountMunicipality,
828
                    EgonEvCountRegistrationDistrict,
829
                ],
830
                ["Grid District", "Municipality", "Registration District"],
831
            ):
832
                query = session.query(
833
                    func.sum(
834
                        table.bev_mini
835
                        + table.bev_medium
836
                        + table.bev_luxury
837
                        + table.phev_mini
838
                        + table.phev_medium
839
                        + table.phev_luxury
840
                    ).label("ev_count")
841
                ).filter(
842
                    table.scenario == scenario_name,
843
                    table.scenario_variation == scenario_var_name,
844
                )
845
846
                ev_counts = pd.read_sql(
847
                    query.statement, query.session.bind, index_col=None
848
                )
849
                ev_counts_dict[level] = ev_counts.iloc[0].ev_count
850
                print(
851
                    f"    Count table: Total count for level {level} "
852
                    f"(table: {table.__table__}): "
853
                    f"{str(ev_counts_dict[level])}"
854
                )
855
856
        # Compare with scenario target (only if not in testmode)
857
        if TESTMODE_OFF:
858
            for level, count in ev_counts_dict.items():
859
                np.testing.assert_allclose(
860
                    count,
861
                    ev_count_target,
862
                    rtol=0.0001,
863
                    err_msg=f"EV numbers in {level} seems to be flawed.",
864
                )
865
        else:
866
            print("    Testmode is on, skipping sanity check...")
867
868
        # Get allocated EVs in grid districts
869
        with db.session_scope() as session:
870
            query = session.query(
871
                func.count(EgonEvMvGridDistrict.egon_ev_pool_ev_id).label(
872
                    "ev_count"
873
                ),
874
            ).filter(
875
                EgonEvMvGridDistrict.scenario == scenario_name,
876
                EgonEvMvGridDistrict.scenario_variation == scenario_var_name,
877
            )
878
        ev_count_alloc = (
879
            pd.read_sql(query.statement, query.session.bind, index_col=None)
880
            .iloc[0]
881
            .ev_count
882
        )
883
        print(
884
            f"    EVs allocated to Grid Districts "
885
            f"(table: {EgonEvMvGridDistrict.__table__}) total count: "
886
            f"{str(ev_count_alloc)}"
887
        )
888
889
        # Compare with scenario target (only if not in testmode)
890
        if TESTMODE_OFF:
891
            np.testing.assert_allclose(
892
                ev_count_alloc,
893
                ev_count_target,
894
                rtol=0.0001,
895
                err_msg=(
896
                    "EV numbers allocated to Grid Districts seems to be "
897
                    "flawed."
898
                ),
899
            )
900
        else:
901
            print("    Testmode is on, skipping sanity check...")
902
903
        return ev_count_alloc
904
905
    def check_trip_data():
906
        # Check if trips start at timestep 0 and have a max. of 35040 steps
907
        # (8760h in 15min steps)
908
        print("  Checking timeranges...")
909
        with db.session_scope() as session:
910
            query = session.query(
911
                func.count(EgonEvTrip.event_id).label("cnt")
912
            ).filter(
913
                or_(
914
                    and_(
915
                        EgonEvTrip.park_start > 0,
916
                        EgonEvTrip.simbev_event_id == 0,
917
                    ),
918
                    EgonEvTrip.park_end
919
                    > (60 / int(meta_run_config.stepsize)) * 8760,
920
                ),
921
                EgonEvTrip.scenario == scenario_name,
922
            )
923
        invalid_trips = pd.read_sql(
924
            query.statement, query.session.bind, index_col=None
925
        )
926
        np.testing.assert_equal(
927
            invalid_trips.iloc[0].cnt,
928
            0,
929
            err_msg=(
930
                f"{str(invalid_trips.iloc[0].cnt)} trips in table "
931
                f"{EgonEvTrip.__table__} have invalid timesteps."
932
            ),
933
        )
934
935
        # Check if charging demand can be covered by available charging energy
936
        # while parking
937
        print("  Compare charging demand with available power...")
938
        with db.session_scope() as session:
939
            query = session.query(
940
                func.count(EgonEvTrip.event_id).label("cnt")
941
            ).filter(
942
                func.round(
943
                    cast(
944
                        (EgonEvTrip.park_end - EgonEvTrip.park_start + 1)
945
                        * EgonEvTrip.charging_capacity_nominal
946
                        * (int(meta_run_config.stepsize) / 60),
947
                        Numeric,
948
                    ),
949
                    3,
950
                )
951
                < cast(EgonEvTrip.charging_demand, Numeric),
952
                EgonEvTrip.scenario == scenario_name,
953
            )
954
        invalid_trips = pd.read_sql(
955
            query.statement, query.session.bind, index_col=None
956
        )
957
        np.testing.assert_equal(
958
            invalid_trips.iloc[0].cnt,
959
            0,
960
            err_msg=(
961
                f"In {str(invalid_trips.iloc[0].cnt)} trips (table: "
962
                f"{EgonEvTrip.__table__}) the charging demand cannot be "
963
                f"covered by available charging power."
964
            ),
965
        )
966
967
    def check_model_data():
968
        # Check if model components were fully created
969
        print("  Check if all model components were created...")
970
        # Get MVGDs which got EV allocated
971
        with db.session_scope() as session:
972
            query = (
973
                session.query(
974
                    EgonEvMvGridDistrict.bus_id,
975
                )
976
                .filter(
977
                    EgonEvMvGridDistrict.scenario == scenario_name,
978
                    EgonEvMvGridDistrict.scenario_variation
979
                    == scenario_var_name,
980
                )
981
                .group_by(EgonEvMvGridDistrict.bus_id)
982
            )
983
        mvgds_with_ev = (
984
            pd.read_sql(query.statement, query.session.bind, index_col=None)
985
            .bus_id.sort_values()
986
            .to_list()
987
        )
988
989
        # Load model components
990
        with db.session_scope() as session:
991
            query = (
992
                session.query(
993
                    EgonPfHvLink.bus0.label("mvgd_bus_id"),
994
                    EgonPfHvLoad.bus.label("emob_bus_id"),
995
                    EgonPfHvLoad.load_id.label("load_id"),
996
                    EgonPfHvStore.store_id.label("store_id"),
997
                )
998
                .select_from(EgonPfHvLoad, EgonPfHvStore)
999
                .join(
1000
                    EgonPfHvLoadTimeseries,
1001
                    EgonPfHvLoadTimeseries.load_id == EgonPfHvLoad.load_id,
1002
                )
1003
                .join(
1004
                    EgonPfHvStoreTimeseries,
1005
                    EgonPfHvStoreTimeseries.store_id == EgonPfHvStore.store_id,
1006
                )
1007
                .filter(
1008
                    EgonPfHvLoad.carrier == "land transport EV",
1009
                    EgonPfHvLoad.scn_name == scenario_name,
1010
                    EgonPfHvLoadTimeseries.scn_name == scenario_name,
1011
                    EgonPfHvStore.carrier == "battery storage",
1012
                    EgonPfHvStore.scn_name == scenario_name,
1013
                    EgonPfHvStoreTimeseries.scn_name == scenario_name,
1014
                    EgonPfHvLink.scn_name == scenario_name,
1015
                    EgonPfHvLink.bus1 == EgonPfHvLoad.bus,
1016
                    EgonPfHvLink.bus1 == EgonPfHvStore.bus,
1017
                )
1018
            )
1019
        model_components = pd.read_sql(
1020
            query.statement, query.session.bind, index_col=None
1021
        )
1022
1023
        # Check number of buses with model components connected
1024
        mvgd_buses_with_ev = model_components.loc[
1025
            model_components.mvgd_bus_id.isin(mvgds_with_ev)
1026
        ]
1027
        np.testing.assert_equal(
1028
            len(mvgds_with_ev),
1029
            len(mvgd_buses_with_ev),
1030
            err_msg=(
1031
                f"Number of Grid Districts with connected model components "
1032
                f"({str(len(mvgd_buses_with_ev))} in tables egon_etrago_*) "
1033
                f"differ from number of Grid Districts that got EVs "
1034
                f"allocated ({len(mvgds_with_ev)} in table "
1035
                f"{EgonEvMvGridDistrict.__table__})."
1036
            ),
1037
        )
1038
1039
        # Check if all required components exist (if no id is NaN)
1040
        np.testing.assert_equal(
1041
            model_components.drop_duplicates().isna().any().any(),
1042
            False,
1043
            err_msg=(
1044
                f"Some components are missing (see True values): "
1045
                f"{model_components.drop_duplicates().isna().any()}"
1046
            ),
1047
        )
1048
1049
        # Get all model timeseries
1050
        print("  Loading model timeseries...")
1051
        # Get all model timeseries
1052
        model_ts_dict = {
1053
            "Load": {
1054
                "carrier": "land transport EV",
1055
                "table": EgonPfHvLoad,
1056
                "table_ts": EgonPfHvLoadTimeseries,
1057
                "column_id": "load_id",
1058
                "columns_ts": ["p_set"],
1059
                "ts": None,
1060
            },
1061
            "Link": {
1062
                "carrier": "BEV charger",
1063
                "table": EgonPfHvLink,
1064
                "table_ts": EgonPfHvLinkTimeseries,
1065
                "column_id": "link_id",
1066
                "columns_ts": ["p_max_pu"],
1067
                "ts": None,
1068
            },
1069
            "Store": {
1070
                "carrier": "battery storage",
1071
                "table": EgonPfHvStore,
1072
                "table_ts": EgonPfHvStoreTimeseries,
1073
                "column_id": "store_id",
1074
                "columns_ts": ["e_min_pu", "e_max_pu"],
1075
                "ts": None,
1076
            },
1077
        }
1078
1079
        with db.session_scope() as session:
1080
            for node, attrs in model_ts_dict.items():
1081
                print(f"    Loading {node} timeseries...")
1082
                subquery = (
1083
                    session.query(getattr(attrs["table"], attrs["column_id"]))
1084
                    .filter(attrs["table"].carrier == attrs["carrier"])
1085
                    .filter(attrs["table"].scn_name == scenario_name)
1086
                    .subquery()
1087
                )
1088
1089
                cols = [
1090
                    getattr(attrs["table_ts"], c) for c in attrs["columns_ts"]
1091
                ]
1092
                query = session.query(
1093
                    getattr(attrs["table_ts"], attrs["column_id"]), *cols
1094
                ).filter(
1095
                    getattr(attrs["table_ts"], attrs["column_id"]).in_(
1096
                        subquery
1097
                    ),
1098
                    attrs["table_ts"].scn_name == scenario_name,
1099
                )
1100
                attrs["ts"] = pd.read_sql(
1101
                    query.statement,
1102
                    query.session.bind,
1103
                    index_col=attrs["column_id"],
1104
                )
1105
1106
        # Check if all timeseries have 8760 steps
1107
        print("    Checking timeranges...")
1108
        for node, attrs in model_ts_dict.items():
1109
            for col in attrs["columns_ts"]:
1110
                ts = attrs["ts"]
1111
                invalid_ts = ts.loc[ts[col].apply(lambda _: len(_)) != 8760][
1112
                    col
1113
                ].apply(len)
1114
                np.testing.assert_equal(
1115
                    len(invalid_ts),
1116
                    0,
1117
                    err_msg=(
1118
                        f"{str(len(invalid_ts))} rows in timeseries do not "
1119
                        f"have 8760 timesteps. Table: "
1120
                        f"{attrs['table_ts'].__table__}, Column: {col}, IDs: "
1121
                        f"{str(list(invalid_ts.index))}"
1122
                    ),
1123
                )
1124
1125
        # Compare total energy demand in model with some approximate values
1126
        # (per EV: 14,000 km/a, 0.17 kWh/km)
1127
        print("  Checking energy demand in model...")
1128
        total_energy_model = (
1129
            model_ts_dict["Load"]["ts"].p_set.apply(lambda _: sum(_)).sum()
1130
            / 1e6
1131
        )
1132
        print(f"    Total energy amount in model: {total_energy_model} TWh")
1133
        total_energy_scenario_approx = ev_count_alloc * 14000 * 0.17 / 1e9
1134
        print(
1135
            f"    Total approximated energy amount in scenario: "
1136
            f"{total_energy_scenario_approx} TWh"
1137
        )
1138
        np.testing.assert_allclose(
1139
            total_energy_model,
1140
            total_energy_scenario_approx,
1141
            rtol=0.1,
1142
            err_msg=(
1143
                "The total energy amount in the model deviates heavily "
1144
                "from the approximated value for current scenario."
1145
            ),
1146
        )
1147
1148
        # Compare total storage capacity
1149
        print("  Checking storage capacity...")
1150
        # Load storage capacities from model
1151
        with db.session_scope() as session:
1152
            query = session.query(
1153
                func.sum(EgonPfHvStore.e_nom).label("e_nom")
1154
            ).filter(
1155
                EgonPfHvStore.scn_name == scenario_name,
1156
                EgonPfHvStore.carrier == "battery storage",
1157
            )
1158
        storage_capacity_model = (
1159
            pd.read_sql(
1160
                query.statement, query.session.bind, index_col=None
1161
            ).e_nom.sum()
1162
            / 1e3
1163
        )
1164
        print(
1165
            f"    Total storage capacity ({EgonPfHvStore.__table__}): "
1166
            f"{round(storage_capacity_model, 1)} GWh"
1167
        )
1168
1169
        # Load occurences of each EV
1170
        with db.session_scope() as session:
1171
            query = (
1172
                session.query(
1173
                    EgonEvMvGridDistrict.bus_id,
1174
                    EgonEvPool.type,
1175
                    func.count(EgonEvMvGridDistrict.egon_ev_pool_ev_id).label(
1176
                        "count"
1177
                    ),
1178
                )
1179
                .join(
1180
                    EgonEvPool,
1181
                    EgonEvPool.ev_id
1182
                    == EgonEvMvGridDistrict.egon_ev_pool_ev_id,
1183
                )
1184
                .filter(
1185
                    EgonEvMvGridDistrict.scenario == scenario_name,
1186
                    EgonEvMvGridDistrict.scenario_variation
1187
                    == scenario_var_name,
1188
                    EgonEvPool.scenario == scenario_name,
1189
                )
1190
                .group_by(EgonEvMvGridDistrict.bus_id, EgonEvPool.type)
1191
            )
1192
        count_per_ev_all = pd.read_sql(
1193
            query.statement, query.session.bind, index_col="bus_id"
1194
        )
1195
        count_per_ev_all["bat_cap"] = count_per_ev_all.type.map(
1196
            meta_tech_data.battery_capacity
1197
        )
1198
        count_per_ev_all["bat_cap_total_MWh"] = (
1199
            count_per_ev_all["count"] * count_per_ev_all.bat_cap / 1e3
1200
        )
1201
        storage_capacity_simbev = count_per_ev_all.bat_cap_total_MWh.div(
1202
            1e3
1203
        ).sum()
1204
        print(
1205
            f"    Total storage capacity (simBEV): "
1206
            f"{round(storage_capacity_simbev, 1)} GWh"
1207
        )
1208
1209
        np.testing.assert_allclose(
1210
            storage_capacity_model,
1211
            storage_capacity_simbev,
1212
            rtol=0.01,
1213
            err_msg=(
1214
                "The total storage capacity in the model deviates heavily "
1215
                "from the input data provided by simBEV for current scenario."
1216
            ),
1217
        )
1218
1219
        # Check SoC storage constraint: e_min_pu < e_max_pu for all timesteps
1220
        print("  Validating SoC constraints...")
1221
        stores_with_invalid_soc = []
1222
        for idx, row in model_ts_dict["Store"]["ts"].iterrows():
1223
            ts = row[["e_min_pu", "e_max_pu"]]
1224
            x = np.array(ts.e_min_pu) > np.array(ts.e_max_pu)
1225
            if x.any():
1226
                stores_with_invalid_soc.append(idx)
1227
1228
        np.testing.assert_equal(
1229
            len(stores_with_invalid_soc),
1230
            0,
1231
            err_msg=(
1232
                f"The store constraint e_min_pu < e_max_pu does not apply "
1233
                f"for some storages in {EgonPfHvStoreTimeseries.__table__}. "
1234
                f"Invalid store_ids: {stores_with_invalid_soc}"
1235
            ),
1236
        )
1237
1238
    def check_model_data_lowflex_eGon2035():
1239
        # TODO: Add eGon100RE_lowflex
1240
        print("")
1241
        print("SCENARIO: eGon2035_lowflex")
1242
1243
        # Compare driving load and charging load
1244
        print("  Loading eGon2035 model timeseries: driving load...")
1245
        with db.session_scope() as session:
1246
            query = (
1247
                session.query(
1248
                    EgonPfHvLoad.load_id,
1249
                    EgonPfHvLoadTimeseries.p_set,
1250
                )
1251
                .join(
1252
                    EgonPfHvLoadTimeseries,
1253
                    EgonPfHvLoadTimeseries.load_id == EgonPfHvLoad.load_id,
1254
                )
1255
                .filter(
1256
                    EgonPfHvLoad.carrier == "land transport EV",
1257
                    EgonPfHvLoad.scn_name == "eGon2035",
1258
                    EgonPfHvLoadTimeseries.scn_name == "eGon2035",
1259
                )
1260
            )
1261
        model_driving_load = pd.read_sql(
1262
            query.statement, query.session.bind, index_col=None
1263
        )
1264
        driving_load = np.array(model_driving_load.p_set.to_list()).sum(axis=0)
1265
1266
        print(
1267
            "  Loading eGon2035_lowflex model timeseries: dumb charging "
1268
            "load..."
1269
        )
1270
        with db.session_scope() as session:
1271
            query = (
1272
                session.query(
1273
                    EgonPfHvLoad.load_id,
1274
                    EgonPfHvLoadTimeseries.p_set,
1275
                )
1276
                .join(
1277
                    EgonPfHvLoadTimeseries,
1278
                    EgonPfHvLoadTimeseries.load_id == EgonPfHvLoad.load_id,
1279
                )
1280
                .filter(
1281
                    EgonPfHvLoad.carrier == "land transport EV",
1282
                    EgonPfHvLoad.scn_name == "eGon2035_lowflex",
1283
                    EgonPfHvLoadTimeseries.scn_name == "eGon2035_lowflex",
1284
                )
1285
            )
1286
        model_charging_load_lowflex = pd.read_sql(
1287
            query.statement, query.session.bind, index_col=None
1288
        )
1289
        charging_load = np.array(
1290
            model_charging_load_lowflex.p_set.to_list()
1291
        ).sum(axis=0)
1292
1293
        # Ratio of driving and charging load should be 0.9 due to charging
1294
        # efficiency
1295
        print("  Compare cumulative loads...")
1296
        print(f"    Driving load (eGon2035): {driving_load.sum() / 1e6} TWh")
1297
        print(
1298
            f"    Dumb charging load (eGon2035_lowflex): "
1299
            f"{charging_load.sum() / 1e6} TWh"
1300
        )
1301
        driving_load_theoretical = (
1302
            float(meta_run_config.eta_cp) * charging_load.sum()
0 ignored issues
show
introduced by
The variable meta_run_config does not seem to be defined in case the for loop on line 1320 is not entered. Are you sure this can never be the case?
Loading history...
1303
        )
1304
        np.testing.assert_allclose(
1305
            driving_load.sum(),
1306
            driving_load_theoretical,
1307
            rtol=0.01,
1308
            err_msg=(
1309
                f"The driving load (eGon2035) deviates by more than 1% "
1310
                f"from the theoretical driving load calculated from charging "
1311
                f"load (eGon2035_lowflex) with an efficiency of "
1312
                f"{float(meta_run_config.eta_cp)}."
1313
            ),
1314
        )
1315
1316
    print("=====================================================")
1317
    print("=== SANITY CHECKS FOR MOTORIZED INDIVIDUAL TRAVEL ===")
1318
    print("=====================================================")
1319
1320
    for scenario_name in ["eGon2035", "eGon100RE"]:
1321
        scenario_var_name = DATASET_CFG["scenario"]["variation"][scenario_name]
1322
1323
        print("")
1324
        print(f"SCENARIO: {scenario_name}, VARIATION: {scenario_var_name}")
1325
1326
        # Load scenario params for scenario and scenario variation
1327
        scenario_variation_parameters = get_sector_parameters(
1328
            "mobility", scenario=scenario_name
1329
        )["motorized_individual_travel"][scenario_var_name]
1330
1331
        # Load simBEV run config and tech data
1332
        meta_run_config = read_simbev_metadata_file(
1333
            scenario_name, "config"
1334
        ).loc["basic"]
1335
        meta_tech_data = read_simbev_metadata_file(scenario_name, "tech_data")
1336
1337
        print("")
1338
        print("Checking EV counts...")
1339
        ev_count_alloc = check_ev_allocation()
1340
1341
        print("")
1342
        print("Checking trip data...")
1343
        check_trip_data()
1344
1345
        print("")
1346
        print("Checking model data...")
1347
        check_model_data()
1348
1349
    print("")
1350
    check_model_data_lowflex_eGon2035()
1351
1352
    print("=====================================================")
1353