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

sanitycheck_pv_rooftop_buildings()   B

Complexity

Conditions 3

Size

Total Lines 86
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 52
dl 0
loc 86
rs 8.5709
c 0
b 0
f 0
cc 3
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
    add_overlay_id_to_buildings,
49
    drop_buildings_outside_grids,
50
    federal_state_data,
51
    grid_districts,
52
    load_building_data,
53
    overlay_grid_districts_with_counties,
54
    scenario_data,
55
)
56
from egon.data.datasets.scenario_parameters import get_sector_parameters
57
import egon.data
58
59
TESTMODE_OFF = (
60
    config.settings()["egon-data"]["--dataset-boundary"] == "Everything"
61
)
62
63
64
class SanityChecks(Dataset):
65
    def __init__(self, dependencies):
66
        super().__init__(
67
            name="SanityChecks",
68
            version="0.0.5",
69
            dependencies=dependencies,
70
            tasks={
71
                etrago_eGon2035_electricity,
72
                etrago_eGon2035_heat,
73
                residential_electricity_annual_sum,
74
                residential_electricity_hh_refinement,
75
                cts_electricity_demand_share,
76
                cts_heat_demand_share,
77
                sanitycheck_emobility_mit,
78
                sanitycheck_pv_rooftop_buildings,
79
            },
80
        )
81
82
83
def etrago_eGon2035_electricity():
84
    """Execute basic sanity checks.
85
86
    Returns print statements as sanity checks for the electricity sector in
87
    the eGon2035 scenario.
88
89
    Parameters
90
    ----------
91
    None
92
93
    Returns
94
    -------
95
    None
96
    """
97
98
    scn = "eGon2035"
99
100
    # Section to check generator capacities
101
    logger.info(f"Sanity checks for scenario {scn}")
102
    logger.info(
103
        "For German electricity generators the following deviations between "
104
        "the inputs and outputs can be observed:"
105
    )
106
107
    carriers_electricity = [
108
        "other_non_renewable",
109
        "other_renewable",
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 "other_non_renewable",
344
    # "other_renewable", "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
    buildings_gdf = load_building_data()
698
    grid_districts_gdf = grid_districts(EPSG)
699
    federal_state_gdf = federal_state_data(grid_districts_gdf.crs)
700
701
    grid_federal_state_gdf = overlay_grid_districts_with_counties(
702
        grid_districts_gdf,
703
        federal_state_gdf,
704
    )
705
706
    buildings_overlay_gdf = add_overlay_id_to_buildings(
707
        buildings_gdf,
708
        grid_federal_state_gdf,
709
    )
710
711
    valid_buildings_gdf = drop_buildings_outside_grids(buildings_overlay_gdf)
712
    valid_buildings_gdf = valid_buildings_gdf.assign(
713
        bus_id=valid_buildings_gdf.bus_id.astype(int),
714
        overlay_id=valid_buildings_gdf.overlay_id.astype(int),
715
        max_cap=valid_buildings_gdf.building_area.multiply(
716
            ROOF_FACTOR * PV_CAP_PER_SQ_M
717
        ),
718
    )
719
720
    merge_df = pv_roof_df.merge(
721
        valid_buildings_gdf[["building_area"]],
722
        how="left",
723
        left_on="building_id",
724
        right_index=True,
725
    )
726
727
    assert len(merge_df.loc[merge_df.building_area.isna()]) == 0
728
729
    scenarios = ["status_quo", "eGon2035"]
730
731
    base_path = Path(egon.data.__path__[0]).resolve()
732
733
    res_dir = base_path / "sanity_checks"
734
735
    res_dir.mkdir(parents=True, exist_ok=True)
736
737
    for scenario in scenarios:
738
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 8))
739
740
        scenario_df = merge_df.loc[merge_df.scenario == scenario]
741
742
        logger.info(
743
            scenario + " Capacity:\n" + str(scenario_df.capacity.describe())
744
        )
745
746
        small_gens_df = scenario_df.loc[scenario_df.capacity < 100]
747
748
        sns.histplot(data=small_gens_df, x="capacity", ax=ax1).set_title(
749
            scenario
750
        )
751
752
        sns.scatterplot(
753
            data=small_gens_df, x="capacity", y="building_area", ax=ax2
754
        ).set_title(scenario)
755
756
        plt.tight_layout()
757
758
        plt.savefig(
759
            res_dir / f"{scenario}_pv_rooftop_distribution.png",
760
            bbox_inches="tight",
761
        )
762
763
    scenarios = ["eGon2035"]  # "eGon100RE"]
764
765
    for scenario in scenarios:
766
        assert isclose(
767
            scenario_data(scenario=scenario).capacity.sum() * 1000,
768
            merge_df.loc[merge_df.scenario == scenario].capacity.sum(),
769
            rel_tol=1e-02,
770
        ), (
771
            f"{scenario_data(scenario=scenario).capacity.sum() * 1000} != "
772
            f"{merge_df.loc[merge_df.scenario == scenario].capacity.sum()}"
773
        )
774
775
776
def sanitycheck_emobility_mit():
777
    """Execute sanity checks for eMobility: motorized individual travel
778
779
    Checks data integrity for eGon2035, eGon2035_lowflex and eGon100RE scenario
780
    using assertions:
781
      1. Allocated EV numbers and EVs allocated to grid districts
782
      2. Trip data (original inout data from simBEV)
783
      3. Model data in eTraGo PF tables (grid.egon_etrago_*)
784
785
    Parameters
786
    ----------
787
    None
788
789
    Returns
790
    -------
791
    None
792
    """
793
794
    def check_ev_allocation():
795
        # Get target number for scenario
796
        ev_count_target = scenario_variation_parameters["ev_count"]
797
        print(f"  Target count: {str(ev_count_target)}")
798
799
        # Get allocated numbers
800
        ev_counts_dict = {}
801
        with db.session_scope() as session:
802
            for table, level in zip(
803
                [
804
                    EgonEvCountMvGridDistrict,
805
                    EgonEvCountMunicipality,
806
                    EgonEvCountRegistrationDistrict,
807
                ],
808
                ["Grid District", "Municipality", "Registration District"],
809
            ):
810
                query = session.query(
811
                    func.sum(
812
                        table.bev_mini
813
                        + table.bev_medium
814
                        + table.bev_luxury
815
                        + table.phev_mini
816
                        + table.phev_medium
817
                        + table.phev_luxury
818
                    ).label("ev_count")
819
                ).filter(
820
                    table.scenario == scenario_name,
821
                    table.scenario_variation == scenario_var_name,
822
                )
823
824
                ev_counts = pd.read_sql(
825
                    query.statement, query.session.bind, index_col=None
826
                )
827
                ev_counts_dict[level] = ev_counts.iloc[0].ev_count
828
                print(
829
                    f"    Count table: Total count for level {level} "
830
                    f"(table: {table.__table__}): "
831
                    f"{str(ev_counts_dict[level])}"
832
                )
833
834
        # Compare with scenario target (only if not in testmode)
835
        if TESTMODE_OFF:
836
            for level, count in ev_counts_dict.items():
837
                np.testing.assert_allclose(
838
                    count,
839
                    ev_count_target,
840
                    rtol=0.0001,
841
                    err_msg=f"EV numbers in {level} seems to be flawed.",
842
                )
843
        else:
844
            print("    Testmode is on, skipping sanity check...")
845
846
        # Get allocated EVs in grid districts
847
        with db.session_scope() as session:
848
            query = session.query(
849
                func.count(EgonEvMvGridDistrict.egon_ev_pool_ev_id).label(
850
                    "ev_count"
851
                ),
852
            ).filter(
853
                EgonEvMvGridDistrict.scenario == scenario_name,
854
                EgonEvMvGridDistrict.scenario_variation == scenario_var_name,
855
            )
856
        ev_count_alloc = (
857
            pd.read_sql(query.statement, query.session.bind, index_col=None)
858
            .iloc[0]
859
            .ev_count
860
        )
861
        print(
862
            f"    EVs allocated to Grid Districts "
863
            f"(table: {EgonEvMvGridDistrict.__table__}) total count: "
864
            f"{str(ev_count_alloc)}"
865
        )
866
867
        # Compare with scenario target (only if not in testmode)
868
        if TESTMODE_OFF:
869
            np.testing.assert_allclose(
870
                ev_count_alloc,
871
                ev_count_target,
872
                rtol=0.0001,
873
                err_msg=(
874
                    "EV numbers allocated to Grid Districts seems to be "
875
                    "flawed."
876
                ),
877
            )
878
        else:
879
            print("    Testmode is on, skipping sanity check...")
880
881
        return ev_count_alloc
882
883
    def check_trip_data():
884
        # Check if trips start at timestep 0 and have a max. of 35040 steps
885
        # (8760h in 15min steps)
886
        print("  Checking timeranges...")
887
        with db.session_scope() as session:
888
            query = session.query(
889
                func.count(EgonEvTrip.event_id).label("cnt")
890
            ).filter(
891
                or_(
892
                    and_(
893
                        EgonEvTrip.park_start > 0,
894
                        EgonEvTrip.simbev_event_id == 0,
895
                    ),
896
                    EgonEvTrip.park_end
897
                    > (60 / int(meta_run_config.stepsize)) * 8760,
898
                ),
899
                EgonEvTrip.scenario == scenario_name,
900
            )
901
        invalid_trips = pd.read_sql(
902
            query.statement, query.session.bind, index_col=None
903
        )
904
        np.testing.assert_equal(
905
            invalid_trips.iloc[0].cnt,
906
            0,
907
            err_msg=(
908
                f"{str(invalid_trips.iloc[0].cnt)} trips in table "
909
                f"{EgonEvTrip.__table__} have invalid timesteps."
910
            ),
911
        )
912
913
        # Check if charging demand can be covered by available charging energy
914
        # while parking
915
        print("  Compare charging demand with available power...")
916
        with db.session_scope() as session:
917
            query = session.query(
918
                func.count(EgonEvTrip.event_id).label("cnt")
919
            ).filter(
920
                func.round(
921
                    cast(
922
                        (EgonEvTrip.park_end - EgonEvTrip.park_start + 1)
923
                        * EgonEvTrip.charging_capacity_nominal
924
                        * (int(meta_run_config.stepsize) / 60),
925
                        Numeric,
926
                    ),
927
                    3,
928
                )
929
                < cast(EgonEvTrip.charging_demand, Numeric),
930
                EgonEvTrip.scenario == scenario_name,
931
            )
932
        invalid_trips = pd.read_sql(
933
            query.statement, query.session.bind, index_col=None
934
        )
935
        np.testing.assert_equal(
936
            invalid_trips.iloc[0].cnt,
937
            0,
938
            err_msg=(
939
                f"In {str(invalid_trips.iloc[0].cnt)} trips (table: "
940
                f"{EgonEvTrip.__table__}) the charging demand cannot be "
941
                f"covered by available charging power."
942
            ),
943
        )
944
945
    def check_model_data():
946
        # Check if model components were fully created
947
        print("  Check if all model components were created...")
948
        # Get MVGDs which got EV allocated
949
        with db.session_scope() as session:
950
            query = (
951
                session.query(
952
                    EgonEvMvGridDistrict.bus_id,
953
                )
954
                .filter(
955
                    EgonEvMvGridDistrict.scenario == scenario_name,
956
                    EgonEvMvGridDistrict.scenario_variation
957
                    == scenario_var_name,
958
                )
959
                .group_by(EgonEvMvGridDistrict.bus_id)
960
            )
961
        mvgds_with_ev = (
962
            pd.read_sql(query.statement, query.session.bind, index_col=None)
963
            .bus_id.sort_values()
964
            .to_list()
965
        )
966
967
        # Load model components
968
        with db.session_scope() as session:
969
            query = (
970
                session.query(
971
                    EgonPfHvLink.bus0.label("mvgd_bus_id"),
972
                    EgonPfHvLoad.bus.label("emob_bus_id"),
973
                    EgonPfHvLoad.load_id.label("load_id"),
974
                    EgonPfHvStore.store_id.label("store_id"),
975
                )
976
                .select_from(EgonPfHvLoad, EgonPfHvStore)
977
                .join(
978
                    EgonPfHvLoadTimeseries,
979
                    EgonPfHvLoadTimeseries.load_id == EgonPfHvLoad.load_id,
980
                )
981
                .join(
982
                    EgonPfHvStoreTimeseries,
983
                    EgonPfHvStoreTimeseries.store_id == EgonPfHvStore.store_id,
984
                )
985
                .filter(
986
                    EgonPfHvLoad.carrier == "land transport EV",
987
                    EgonPfHvLoad.scn_name == scenario_name,
988
                    EgonPfHvLoadTimeseries.scn_name == scenario_name,
989
                    EgonPfHvStore.carrier == "battery storage",
990
                    EgonPfHvStore.scn_name == scenario_name,
991
                    EgonPfHvStoreTimeseries.scn_name == scenario_name,
992
                    EgonPfHvLink.scn_name == scenario_name,
993
                    EgonPfHvLink.bus1 == EgonPfHvLoad.bus,
994
                    EgonPfHvLink.bus1 == EgonPfHvStore.bus,
995
                )
996
            )
997
        model_components = pd.read_sql(
998
            query.statement, query.session.bind, index_col=None
999
        )
1000
1001
        # Check number of buses with model components connected
1002
        mvgd_buses_with_ev = model_components.loc[
1003
            model_components.mvgd_bus_id.isin(mvgds_with_ev)
1004
        ]
1005
        np.testing.assert_equal(
1006
            len(mvgds_with_ev),
1007
            len(mvgd_buses_with_ev),
1008
            err_msg=(
1009
                f"Number of Grid Districts with connected model components "
1010
                f"({str(len(mvgd_buses_with_ev))} in tables egon_etrago_*) "
1011
                f"differ from number of Grid Districts that got EVs "
1012
                f"allocated ({len(mvgds_with_ev)} in table "
1013
                f"{EgonEvMvGridDistrict.__table__})."
1014
            ),
1015
        )
1016
1017
        # Check if all required components exist (if no id is NaN)
1018
        np.testing.assert_equal(
1019
            model_components.drop_duplicates().isna().any().any(),
1020
            False,
1021
            err_msg=(
1022
                f"Some components are missing (see True values): "
1023
                f"{model_components.drop_duplicates().isna().any()}"
1024
            ),
1025
        )
1026
1027
        # Get all model timeseries
1028
        print("  Loading model timeseries...")
1029
        # Get all model timeseries
1030
        model_ts_dict = {
1031
            "Load": {
1032
                "carrier": "land transport EV",
1033
                "table": EgonPfHvLoad,
1034
                "table_ts": EgonPfHvLoadTimeseries,
1035
                "column_id": "load_id",
1036
                "columns_ts": ["p_set"],
1037
                "ts": None,
1038
            },
1039
            "Link": {
1040
                "carrier": "BEV charger",
1041
                "table": EgonPfHvLink,
1042
                "table_ts": EgonPfHvLinkTimeseries,
1043
                "column_id": "link_id",
1044
                "columns_ts": ["p_max_pu"],
1045
                "ts": None,
1046
            },
1047
            "Store": {
1048
                "carrier": "battery storage",
1049
                "table": EgonPfHvStore,
1050
                "table_ts": EgonPfHvStoreTimeseries,
1051
                "column_id": "store_id",
1052
                "columns_ts": ["e_min_pu", "e_max_pu"],
1053
                "ts": None,
1054
            },
1055
        }
1056
1057
        with db.session_scope() as session:
1058
            for node, attrs in model_ts_dict.items():
1059
                print(f"    Loading {node} timeseries...")
1060
                subquery = (
1061
                    session.query(getattr(attrs["table"], attrs["column_id"]))
1062
                    .filter(attrs["table"].carrier == attrs["carrier"])
1063
                    .filter(attrs["table"].scn_name == scenario_name)
1064
                    .subquery()
1065
                )
1066
1067
                cols = [
1068
                    getattr(attrs["table_ts"], c) for c in attrs["columns_ts"]
1069
                ]
1070
                query = session.query(
1071
                    getattr(attrs["table_ts"], attrs["column_id"]), *cols
1072
                ).filter(
1073
                    getattr(attrs["table_ts"], attrs["column_id"]).in_(
1074
                        subquery
1075
                    ),
1076
                    attrs["table_ts"].scn_name == scenario_name,
1077
                )
1078
                attrs["ts"] = pd.read_sql(
1079
                    query.statement,
1080
                    query.session.bind,
1081
                    index_col=attrs["column_id"],
1082
                )
1083
1084
        # Check if all timeseries have 8760 steps
1085
        print("    Checking timeranges...")
1086
        for node, attrs in model_ts_dict.items():
1087
            for col in attrs["columns_ts"]:
1088
                ts = attrs["ts"]
1089
                invalid_ts = ts.loc[ts[col].apply(lambda _: len(_)) != 8760][
1090
                    col
1091
                ].apply(len)
1092
                np.testing.assert_equal(
1093
                    len(invalid_ts),
1094
                    0,
1095
                    err_msg=(
1096
                        f"{str(len(invalid_ts))} rows in timeseries do not "
1097
                        f"have 8760 timesteps. Table: "
1098
                        f"{attrs['table_ts'].__table__}, Column: {col}, IDs: "
1099
                        f"{str(list(invalid_ts.index))}"
1100
                    ),
1101
                )
1102
1103
        # Compare total energy demand in model with some approximate values
1104
        # (per EV: 14,000 km/a, 0.17 kWh/km)
1105
        print("  Checking energy demand in model...")
1106
        total_energy_model = (
1107
            model_ts_dict["Load"]["ts"].p_set.apply(lambda _: sum(_)).sum()
1108
            / 1e6
1109
        )
1110
        print(f"    Total energy amount in model: {total_energy_model} TWh")
1111
        total_energy_scenario_approx = ev_count_alloc * 14000 * 0.17 / 1e9
1112
        print(
1113
            f"    Total approximated energy amount in scenario: "
1114
            f"{total_energy_scenario_approx} TWh"
1115
        )
1116
        np.testing.assert_allclose(
1117
            total_energy_model,
1118
            total_energy_scenario_approx,
1119
            rtol=0.1,
1120
            err_msg=(
1121
                "The total energy amount in the model deviates heavily "
1122
                "from the approximated value for current scenario."
1123
            ),
1124
        )
1125
1126
        # Compare total storage capacity
1127
        print("  Checking storage capacity...")
1128
        # Load storage capacities from model
1129
        with db.session_scope() as session:
1130
            query = session.query(
1131
                func.sum(EgonPfHvStore.e_nom).label("e_nom")
1132
            ).filter(
1133
                EgonPfHvStore.scn_name == scenario_name,
1134
                EgonPfHvStore.carrier == "battery storage",
1135
            )
1136
        storage_capacity_model = (
1137
            pd.read_sql(
1138
                query.statement, query.session.bind, index_col=None
1139
            ).e_nom.sum()
1140
            / 1e3
1141
        )
1142
        print(
1143
            f"    Total storage capacity ({EgonPfHvStore.__table__}): "
1144
            f"{round(storage_capacity_model, 1)} GWh"
1145
        )
1146
1147
        # Load occurences of each EV
1148
        with db.session_scope() as session:
1149
            query = (
1150
                session.query(
1151
                    EgonEvMvGridDistrict.bus_id,
1152
                    EgonEvPool.type,
1153
                    func.count(EgonEvMvGridDistrict.egon_ev_pool_ev_id).label(
1154
                        "count"
1155
                    ),
1156
                )
1157
                .join(
1158
                    EgonEvPool,
1159
                    EgonEvPool.ev_id
1160
                    == EgonEvMvGridDistrict.egon_ev_pool_ev_id,
1161
                )
1162
                .filter(
1163
                    EgonEvMvGridDistrict.scenario == scenario_name,
1164
                    EgonEvMvGridDistrict.scenario_variation
1165
                    == scenario_var_name,
1166
                    EgonEvPool.scenario == scenario_name,
1167
                )
1168
                .group_by(EgonEvMvGridDistrict.bus_id, EgonEvPool.type)
1169
            )
1170
        count_per_ev_all = pd.read_sql(
1171
            query.statement, query.session.bind, index_col="bus_id"
1172
        )
1173
        count_per_ev_all["bat_cap"] = count_per_ev_all.type.map(
1174
            meta_tech_data.battery_capacity
1175
        )
1176
        count_per_ev_all["bat_cap_total_MWh"] = (
1177
            count_per_ev_all["count"] * count_per_ev_all.bat_cap / 1e3
1178
        )
1179
        storage_capacity_simbev = count_per_ev_all.bat_cap_total_MWh.div(
1180
            1e3
1181
        ).sum()
1182
        print(
1183
            f"    Total storage capacity (simBEV): "
1184
            f"{round(storage_capacity_simbev, 1)} GWh"
1185
        )
1186
1187
        np.testing.assert_allclose(
1188
            storage_capacity_model,
1189
            storage_capacity_simbev,
1190
            rtol=0.01,
1191
            err_msg=(
1192
                "The total storage capacity in the model deviates heavily "
1193
                "from the input data provided by simBEV for current scenario."
1194
            ),
1195
        )
1196
1197
        # Check SoC storage constraint: e_min_pu < e_max_pu for all timesteps
1198
        print("  Validating SoC constraints...")
1199
        stores_with_invalid_soc = []
1200
        for idx, row in model_ts_dict["Store"]["ts"].iterrows():
1201
            ts = row[["e_min_pu", "e_max_pu"]]
1202
            x = np.array(ts.e_min_pu) > np.array(ts.e_max_pu)
1203
            if x.any():
1204
                stores_with_invalid_soc.append(idx)
1205
1206
        np.testing.assert_equal(
1207
            len(stores_with_invalid_soc),
1208
            0,
1209
            err_msg=(
1210
                f"The store constraint e_min_pu < e_max_pu does not apply "
1211
                f"for some storages in {EgonPfHvStoreTimeseries.__table__}. "
1212
                f"Invalid store_ids: {stores_with_invalid_soc}"
1213
            ),
1214
        )
1215
1216
    def check_model_data_lowflex_eGon2035():
1217
        # TODO: Add eGon100RE_lowflex
1218
        print("")
1219
        print("SCENARIO: eGon2035_lowflex")
1220
1221
        # Compare driving load and charging load
1222
        print("  Loading eGon2035 model timeseries: driving load...")
1223
        with db.session_scope() as session:
1224
            query = (
1225
                session.query(
1226
                    EgonPfHvLoad.load_id,
1227
                    EgonPfHvLoadTimeseries.p_set,
1228
                )
1229
                .join(
1230
                    EgonPfHvLoadTimeseries,
1231
                    EgonPfHvLoadTimeseries.load_id == EgonPfHvLoad.load_id,
1232
                )
1233
                .filter(
1234
                    EgonPfHvLoad.carrier == "land transport EV",
1235
                    EgonPfHvLoad.scn_name == "eGon2035",
1236
                    EgonPfHvLoadTimeseries.scn_name == "eGon2035",
1237
                )
1238
            )
1239
        model_driving_load = pd.read_sql(
1240
            query.statement, query.session.bind, index_col=None
1241
        )
1242
        driving_load = np.array(model_driving_load.p_set.to_list()).sum(axis=0)
1243
1244
        print(
1245
            "  Loading eGon2035_lowflex model timeseries: dumb charging "
1246
            "load..."
1247
        )
1248
        with db.session_scope() as session:
1249
            query = (
1250
                session.query(
1251
                    EgonPfHvLoad.load_id,
1252
                    EgonPfHvLoadTimeseries.p_set,
1253
                )
1254
                .join(
1255
                    EgonPfHvLoadTimeseries,
1256
                    EgonPfHvLoadTimeseries.load_id == EgonPfHvLoad.load_id,
1257
                )
1258
                .filter(
1259
                    EgonPfHvLoad.carrier == "land transport EV",
1260
                    EgonPfHvLoad.scn_name == "eGon2035_lowflex",
1261
                    EgonPfHvLoadTimeseries.scn_name == "eGon2035_lowflex",
1262
                )
1263
            )
1264
        model_charging_load_lowflex = pd.read_sql(
1265
            query.statement, query.session.bind, index_col=None
1266
        )
1267
        charging_load = np.array(
1268
            model_charging_load_lowflex.p_set.to_list()
1269
        ).sum(axis=0)
1270
1271
        # Ratio of driving and charging load should be 0.9 due to charging
1272
        # efficiency
1273
        print("  Compare cumulative loads...")
1274
        print(f"    Driving load (eGon2035): {driving_load.sum() / 1e6} TWh")
1275
        print(
1276
            f"    Dumb charging load (eGon2035_lowflex): "
1277
            f"{charging_load.sum() / 1e6} TWh"
1278
        )
1279
        driving_load_theoretical = (
1280
            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 1298 is not entered. Are you sure this can never be the case?
Loading history...
1281
        )
1282
        np.testing.assert_allclose(
1283
            driving_load.sum(),
1284
            driving_load_theoretical,
1285
            rtol=0.01,
1286
            err_msg=(
1287
                f"The driving load (eGon2035) deviates by more than 1% "
1288
                f"from the theoretical driving load calculated from charging "
1289
                f"load (eGon2035_lowflex) with an efficiency of "
1290
                f"{float(meta_run_config.eta_cp)}."
1291
            ),
1292
        )
1293
1294
    print("=====================================================")
1295
    print("=== SANITY CHECKS FOR MOTORIZED INDIVIDUAL TRAVEL ===")
1296
    print("=====================================================")
1297
1298
    for scenario_name in ["eGon2035", "eGon100RE"]:
1299
        scenario_var_name = DATASET_CFG["scenario"]["variation"][scenario_name]
1300
1301
        print("")
1302
        print(f"SCENARIO: {scenario_name}, VARIATION: {scenario_var_name}")
1303
1304
        # Load scenario params for scenario and scenario variation
1305
        scenario_variation_parameters = get_sector_parameters(
1306
            "mobility", scenario=scenario_name
1307
        )["motorized_individual_travel"][scenario_var_name]
1308
1309
        # Load simBEV run config and tech data
1310
        meta_run_config = read_simbev_metadata_file(
1311
            scenario_name, "config"
1312
        ).loc["basic"]
1313
        meta_tech_data = read_simbev_metadata_file(scenario_name, "tech_data")
1314
1315
        print("")
1316
        print("Checking EV counts...")
1317
        ev_count_alloc = check_ev_allocation()
1318
1319
        print("")
1320
        print("Checking trip data...")
1321
        check_trip_data()
1322
1323
        print("")
1324
        print("Checking model data...")
1325
        check_model_data()
1326
1327
    print("")
1328
    check_model_data_lowflex_eGon2035()
1329
1330
    print("=====================================================")
1331