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

cts_electricity_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
    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
    for scenario in SCENARIOS:
764
        if scenario == "eGon2035":
765
            assert isclose(
766
                scenario_data(scenario=scenario).capacity.sum(),
767
                merge_df.loc[merge_df.scenario == scenario].capacity.sum(),
768
                rel_tol=1e-02,
769
            ), (
770
                f"{scenario_data(scenario=scenario).capacity.sum()} != "
771
                f"{merge_df.loc[merge_df.scenario == scenario].capacity.sum()}"
772
            )
773
        elif scenario == "eGon100RE":
774
            sources = config.datasets()["solar_rooftop"]["sources"]
775
776
            target = db.select_dataframe(
777
                f"""
778
                SELECT capacity
779
                FROM {sources['scenario_capacities']['schema']}.
780
                {sources['scenario_capacities']['table']} a
781
                WHERE carrier = 'solar_rooftop'
782
                AND scenario_name = '{scenario}'
783
                """
784
            ).capacity[0]
785
786
            dataset = config.settings()["egon-data"]["--dataset-boundary"]
787
788
            if dataset == "Schleswig-Holstein":
789
                # since the required data is missing for a SH run, it is implemented
790
                # manually here
791
                total_2035 = 84070
792
                sh_2035 = 2700
793
794
                share = sh_2035 / total_2035
795
796
                target *= share
797
798
            assert isclose(
799
                target,
800
                merge_df.loc[merge_df.scenario == scenario].capacity.sum(),
801
            ), (
802
                f"{target} != "
803
                f"{merge_df.loc[merge_df.scenario == scenario].capacity.sum()}"
804
            )
805
        else:
806
            raise ValueError(f"Scenario {scenario} is not valid.")
807
808
809
def sanitycheck_emobility_mit():
810
    """Execute sanity checks for eMobility: motorized individual travel
811
812
    Checks data integrity for eGon2035, eGon2035_lowflex and eGon100RE scenario
813
    using assertions:
814
      1. Allocated EV numbers and EVs allocated to grid districts
815
      2. Trip data (original inout data from simBEV)
816
      3. Model data in eTraGo PF tables (grid.egon_etrago_*)
817
818
    Parameters
819
    ----------
820
    None
821
822
    Returns
823
    -------
824
    None
825
    """
826
827
    def check_ev_allocation():
828
        # Get target number for scenario
829
        ev_count_target = scenario_variation_parameters["ev_count"]
830
        print(f"  Target count: {str(ev_count_target)}")
831
832
        # Get allocated numbers
833
        ev_counts_dict = {}
834
        with db.session_scope() as session:
835
            for table, level in zip(
836
                [
837
                    EgonEvCountMvGridDistrict,
838
                    EgonEvCountMunicipality,
839
                    EgonEvCountRegistrationDistrict,
840
                ],
841
                ["Grid District", "Municipality", "Registration District"],
842
            ):
843
                query = session.query(
844
                    func.sum(
845
                        table.bev_mini
846
                        + table.bev_medium
847
                        + table.bev_luxury
848
                        + table.phev_mini
849
                        + table.phev_medium
850
                        + table.phev_luxury
851
                    ).label("ev_count")
852
                ).filter(
853
                    table.scenario == scenario_name,
854
                    table.scenario_variation == scenario_var_name,
855
                )
856
857
                ev_counts = pd.read_sql(
858
                    query.statement, query.session.bind, index_col=None
859
                )
860
                ev_counts_dict[level] = ev_counts.iloc[0].ev_count
861
                print(
862
                    f"    Count table: Total count for level {level} "
863
                    f"(table: {table.__table__}): "
864
                    f"{str(ev_counts_dict[level])}"
865
                )
866
867
        # Compare with scenario target (only if not in testmode)
868
        if TESTMODE_OFF:
869
            for level, count in ev_counts_dict.items():
870
                np.testing.assert_allclose(
871
                    count,
872
                    ev_count_target,
873
                    rtol=0.0001,
874
                    err_msg=f"EV numbers in {level} seems to be flawed.",
875
                )
876
        else:
877
            print("    Testmode is on, skipping sanity check...")
878
879
        # Get allocated EVs in grid districts
880
        with db.session_scope() as session:
881
            query = session.query(
882
                func.count(EgonEvMvGridDistrict.egon_ev_pool_ev_id).label(
883
                    "ev_count"
884
                ),
885
            ).filter(
886
                EgonEvMvGridDistrict.scenario == scenario_name,
887
                EgonEvMvGridDistrict.scenario_variation == scenario_var_name,
888
            )
889
        ev_count_alloc = (
890
            pd.read_sql(query.statement, query.session.bind, index_col=None)
891
            .iloc[0]
892
            .ev_count
893
        )
894
        print(
895
            f"    EVs allocated to Grid Districts "
896
            f"(table: {EgonEvMvGridDistrict.__table__}) total count: "
897
            f"{str(ev_count_alloc)}"
898
        )
899
900
        # Compare with scenario target (only if not in testmode)
901
        if TESTMODE_OFF:
902
            np.testing.assert_allclose(
903
                ev_count_alloc,
904
                ev_count_target,
905
                rtol=0.0001,
906
                err_msg=(
907
                    "EV numbers allocated to Grid Districts seems to be "
908
                    "flawed."
909
                ),
910
            )
911
        else:
912
            print("    Testmode is on, skipping sanity check...")
913
914
        return ev_count_alloc
915
916
    def check_trip_data():
917
        # Check if trips start at timestep 0 and have a max. of 35040 steps
918
        # (8760h in 15min steps)
919
        print("  Checking timeranges...")
920
        with db.session_scope() as session:
921
            query = session.query(
922
                func.count(EgonEvTrip.event_id).label("cnt")
923
            ).filter(
924
                or_(
925
                    and_(
926
                        EgonEvTrip.park_start > 0,
927
                        EgonEvTrip.simbev_event_id == 0,
928
                    ),
929
                    EgonEvTrip.park_end
930
                    > (60 / int(meta_run_config.stepsize)) * 8760,
931
                ),
932
                EgonEvTrip.scenario == scenario_name,
933
            )
934
        invalid_trips = pd.read_sql(
935
            query.statement, query.session.bind, index_col=None
936
        )
937
        np.testing.assert_equal(
938
            invalid_trips.iloc[0].cnt,
939
            0,
940
            err_msg=(
941
                f"{str(invalid_trips.iloc[0].cnt)} trips in table "
942
                f"{EgonEvTrip.__table__} have invalid timesteps."
943
            ),
944
        )
945
946
        # Check if charging demand can be covered by available charging energy
947
        # while parking
948
        print("  Compare charging demand with available power...")
949
        with db.session_scope() as session:
950
            query = session.query(
951
                func.count(EgonEvTrip.event_id).label("cnt")
952
            ).filter(
953
                func.round(
954
                    cast(
955
                        (EgonEvTrip.park_end - EgonEvTrip.park_start + 1)
956
                        * EgonEvTrip.charging_capacity_nominal
957
                        * (int(meta_run_config.stepsize) / 60),
958
                        Numeric,
959
                    ),
960
                    3,
961
                )
962
                < cast(EgonEvTrip.charging_demand, Numeric),
963
                EgonEvTrip.scenario == scenario_name,
964
            )
965
        invalid_trips = pd.read_sql(
966
            query.statement, query.session.bind, index_col=None
967
        )
968
        np.testing.assert_equal(
969
            invalid_trips.iloc[0].cnt,
970
            0,
971
            err_msg=(
972
                f"In {str(invalid_trips.iloc[0].cnt)} trips (table: "
973
                f"{EgonEvTrip.__table__}) the charging demand cannot be "
974
                f"covered by available charging power."
975
            ),
976
        )
977
978
    def check_model_data():
979
        # Check if model components were fully created
980
        print("  Check if all model components were created...")
981
        # Get MVGDs which got EV allocated
982
        with db.session_scope() as session:
983
            query = (
984
                session.query(
985
                    EgonEvMvGridDistrict.bus_id,
986
                )
987
                .filter(
988
                    EgonEvMvGridDistrict.scenario == scenario_name,
989
                    EgonEvMvGridDistrict.scenario_variation
990
                    == scenario_var_name,
991
                )
992
                .group_by(EgonEvMvGridDistrict.bus_id)
993
            )
994
        mvgds_with_ev = (
995
            pd.read_sql(query.statement, query.session.bind, index_col=None)
996
            .bus_id.sort_values()
997
            .to_list()
998
        )
999
1000
        # Load model components
1001
        with db.session_scope() as session:
1002
            query = (
1003
                session.query(
1004
                    EgonPfHvLink.bus0.label("mvgd_bus_id"),
1005
                    EgonPfHvLoad.bus.label("emob_bus_id"),
1006
                    EgonPfHvLoad.load_id.label("load_id"),
1007
                    EgonPfHvStore.store_id.label("store_id"),
1008
                )
1009
                .select_from(EgonPfHvLoad, EgonPfHvStore)
1010
                .join(
1011
                    EgonPfHvLoadTimeseries,
1012
                    EgonPfHvLoadTimeseries.load_id == EgonPfHvLoad.load_id,
1013
                )
1014
                .join(
1015
                    EgonPfHvStoreTimeseries,
1016
                    EgonPfHvStoreTimeseries.store_id == EgonPfHvStore.store_id,
1017
                )
1018
                .filter(
1019
                    EgonPfHvLoad.carrier == "land transport EV",
1020
                    EgonPfHvLoad.scn_name == scenario_name,
1021
                    EgonPfHvLoadTimeseries.scn_name == scenario_name,
1022
                    EgonPfHvStore.carrier == "battery storage",
1023
                    EgonPfHvStore.scn_name == scenario_name,
1024
                    EgonPfHvStoreTimeseries.scn_name == scenario_name,
1025
                    EgonPfHvLink.scn_name == scenario_name,
1026
                    EgonPfHvLink.bus1 == EgonPfHvLoad.bus,
1027
                    EgonPfHvLink.bus1 == EgonPfHvStore.bus,
1028
                )
1029
            )
1030
        model_components = pd.read_sql(
1031
            query.statement, query.session.bind, index_col=None
1032
        )
1033
1034
        # Check number of buses with model components connected
1035
        mvgd_buses_with_ev = model_components.loc[
1036
            model_components.mvgd_bus_id.isin(mvgds_with_ev)
1037
        ]
1038
        np.testing.assert_equal(
1039
            len(mvgds_with_ev),
1040
            len(mvgd_buses_with_ev),
1041
            err_msg=(
1042
                f"Number of Grid Districts with connected model components "
1043
                f"({str(len(mvgd_buses_with_ev))} in tables egon_etrago_*) "
1044
                f"differ from number of Grid Districts that got EVs "
1045
                f"allocated ({len(mvgds_with_ev)} in table "
1046
                f"{EgonEvMvGridDistrict.__table__})."
1047
            ),
1048
        )
1049
1050
        # Check if all required components exist (if no id is NaN)
1051
        np.testing.assert_equal(
1052
            model_components.drop_duplicates().isna().any().any(),
1053
            False,
1054
            err_msg=(
1055
                f"Some components are missing (see True values): "
1056
                f"{model_components.drop_duplicates().isna().any()}"
1057
            ),
1058
        )
1059
1060
        # Get all model timeseries
1061
        print("  Loading model timeseries...")
1062
        # Get all model timeseries
1063
        model_ts_dict = {
1064
            "Load": {
1065
                "carrier": "land transport EV",
1066
                "table": EgonPfHvLoad,
1067
                "table_ts": EgonPfHvLoadTimeseries,
1068
                "column_id": "load_id",
1069
                "columns_ts": ["p_set"],
1070
                "ts": None,
1071
            },
1072
            "Link": {
1073
                "carrier": "BEV charger",
1074
                "table": EgonPfHvLink,
1075
                "table_ts": EgonPfHvLinkTimeseries,
1076
                "column_id": "link_id",
1077
                "columns_ts": ["p_max_pu"],
1078
                "ts": None,
1079
            },
1080
            "Store": {
1081
                "carrier": "battery storage",
1082
                "table": EgonPfHvStore,
1083
                "table_ts": EgonPfHvStoreTimeseries,
1084
                "column_id": "store_id",
1085
                "columns_ts": ["e_min_pu", "e_max_pu"],
1086
                "ts": None,
1087
            },
1088
        }
1089
1090
        with db.session_scope() as session:
1091
            for node, attrs in model_ts_dict.items():
1092
                print(f"    Loading {node} timeseries...")
1093
                subquery = (
1094
                    session.query(getattr(attrs["table"], attrs["column_id"]))
1095
                    .filter(attrs["table"].carrier == attrs["carrier"])
1096
                    .filter(attrs["table"].scn_name == scenario_name)
1097
                    .subquery()
1098
                )
1099
1100
                cols = [
1101
                    getattr(attrs["table_ts"], c) for c in attrs["columns_ts"]
1102
                ]
1103
                query = session.query(
1104
                    getattr(attrs["table_ts"], attrs["column_id"]), *cols
1105
                ).filter(
1106
                    getattr(attrs["table_ts"], attrs["column_id"]).in_(
1107
                        subquery
1108
                    ),
1109
                    attrs["table_ts"].scn_name == scenario_name,
1110
                )
1111
                attrs["ts"] = pd.read_sql(
1112
                    query.statement,
1113
                    query.session.bind,
1114
                    index_col=attrs["column_id"],
1115
                )
1116
1117
        # Check if all timeseries have 8760 steps
1118
        print("    Checking timeranges...")
1119
        for node, attrs in model_ts_dict.items():
1120
            for col in attrs["columns_ts"]:
1121
                ts = attrs["ts"]
1122
                invalid_ts = ts.loc[ts[col].apply(lambda _: len(_)) != 8760][
1123
                    col
1124
                ].apply(len)
1125
                np.testing.assert_equal(
1126
                    len(invalid_ts),
1127
                    0,
1128
                    err_msg=(
1129
                        f"{str(len(invalid_ts))} rows in timeseries do not "
1130
                        f"have 8760 timesteps. Table: "
1131
                        f"{attrs['table_ts'].__table__}, Column: {col}, IDs: "
1132
                        f"{str(list(invalid_ts.index))}"
1133
                    ),
1134
                )
1135
1136
        # Compare total energy demand in model with some approximate values
1137
        # (per EV: 14,000 km/a, 0.17 kWh/km)
1138
        print("  Checking energy demand in model...")
1139
        total_energy_model = (
1140
            model_ts_dict["Load"]["ts"].p_set.apply(lambda _: sum(_)).sum()
1141
            / 1e6
1142
        )
1143
        print(f"    Total energy amount in model: {total_energy_model} TWh")
1144
        total_energy_scenario_approx = ev_count_alloc * 14000 * 0.17 / 1e9
1145
        print(
1146
            f"    Total approximated energy amount in scenario: "
1147
            f"{total_energy_scenario_approx} TWh"
1148
        )
1149
        np.testing.assert_allclose(
1150
            total_energy_model,
1151
            total_energy_scenario_approx,
1152
            rtol=0.1,
1153
            err_msg=(
1154
                "The total energy amount in the model deviates heavily "
1155
                "from the approximated value for current scenario."
1156
            ),
1157
        )
1158
1159
        # Compare total storage capacity
1160
        print("  Checking storage capacity...")
1161
        # Load storage capacities from model
1162
        with db.session_scope() as session:
1163
            query = session.query(
1164
                func.sum(EgonPfHvStore.e_nom).label("e_nom")
1165
            ).filter(
1166
                EgonPfHvStore.scn_name == scenario_name,
1167
                EgonPfHvStore.carrier == "battery storage",
1168
            )
1169
        storage_capacity_model = (
1170
            pd.read_sql(
1171
                query.statement, query.session.bind, index_col=None
1172
            ).e_nom.sum()
1173
            / 1e3
1174
        )
1175
        print(
1176
            f"    Total storage capacity ({EgonPfHvStore.__table__}): "
1177
            f"{round(storage_capacity_model, 1)} GWh"
1178
        )
1179
1180
        # Load occurences of each EV
1181
        with db.session_scope() as session:
1182
            query = (
1183
                session.query(
1184
                    EgonEvMvGridDistrict.bus_id,
1185
                    EgonEvPool.type,
1186
                    func.count(EgonEvMvGridDistrict.egon_ev_pool_ev_id).label(
1187
                        "count"
1188
                    ),
1189
                )
1190
                .join(
1191
                    EgonEvPool,
1192
                    EgonEvPool.ev_id
1193
                    == EgonEvMvGridDistrict.egon_ev_pool_ev_id,
1194
                )
1195
                .filter(
1196
                    EgonEvMvGridDistrict.scenario == scenario_name,
1197
                    EgonEvMvGridDistrict.scenario_variation
1198
                    == scenario_var_name,
1199
                    EgonEvPool.scenario == scenario_name,
1200
                )
1201
                .group_by(EgonEvMvGridDistrict.bus_id, EgonEvPool.type)
1202
            )
1203
        count_per_ev_all = pd.read_sql(
1204
            query.statement, query.session.bind, index_col="bus_id"
1205
        )
1206
        count_per_ev_all["bat_cap"] = count_per_ev_all.type.map(
1207
            meta_tech_data.battery_capacity
1208
        )
1209
        count_per_ev_all["bat_cap_total_MWh"] = (
1210
            count_per_ev_all["count"] * count_per_ev_all.bat_cap / 1e3
1211
        )
1212
        storage_capacity_simbev = count_per_ev_all.bat_cap_total_MWh.div(
1213
            1e3
1214
        ).sum()
1215
        print(
1216
            f"    Total storage capacity (simBEV): "
1217
            f"{round(storage_capacity_simbev, 1)} GWh"
1218
        )
1219
1220
        np.testing.assert_allclose(
1221
            storage_capacity_model,
1222
            storage_capacity_simbev,
1223
            rtol=0.01,
1224
            err_msg=(
1225
                "The total storage capacity in the model deviates heavily "
1226
                "from the input data provided by simBEV for current scenario."
1227
            ),
1228
        )
1229
1230
        # Check SoC storage constraint: e_min_pu < e_max_pu for all timesteps
1231
        print("  Validating SoC constraints...")
1232
        stores_with_invalid_soc = []
1233
        for idx, row in model_ts_dict["Store"]["ts"].iterrows():
1234
            ts = row[["e_min_pu", "e_max_pu"]]
1235
            x = np.array(ts.e_min_pu) > np.array(ts.e_max_pu)
1236
            if x.any():
1237
                stores_with_invalid_soc.append(idx)
1238
1239
        np.testing.assert_equal(
1240
            len(stores_with_invalid_soc),
1241
            0,
1242
            err_msg=(
1243
                f"The store constraint e_min_pu < e_max_pu does not apply "
1244
                f"for some storages in {EgonPfHvStoreTimeseries.__table__}. "
1245
                f"Invalid store_ids: {stores_with_invalid_soc}"
1246
            ),
1247
        )
1248
1249
    def check_model_data_lowflex_eGon2035():
1250
        # TODO: Add eGon100RE_lowflex
1251
        print("")
1252
        print("SCENARIO: eGon2035_lowflex")
1253
1254
        # Compare driving load and charging load
1255
        print("  Loading eGon2035 model timeseries: driving load...")
1256
        with db.session_scope() as session:
1257
            query = (
1258
                session.query(
1259
                    EgonPfHvLoad.load_id,
1260
                    EgonPfHvLoadTimeseries.p_set,
1261
                )
1262
                .join(
1263
                    EgonPfHvLoadTimeseries,
1264
                    EgonPfHvLoadTimeseries.load_id == EgonPfHvLoad.load_id,
1265
                )
1266
                .filter(
1267
                    EgonPfHvLoad.carrier == "land transport EV",
1268
                    EgonPfHvLoad.scn_name == "eGon2035",
1269
                    EgonPfHvLoadTimeseries.scn_name == "eGon2035",
1270
                )
1271
            )
1272
        model_driving_load = pd.read_sql(
1273
            query.statement, query.session.bind, index_col=None
1274
        )
1275
        driving_load = np.array(model_driving_load.p_set.to_list()).sum(axis=0)
1276
1277
        print(
1278
            "  Loading eGon2035_lowflex model timeseries: dumb charging "
1279
            "load..."
1280
        )
1281
        with db.session_scope() as session:
1282
            query = (
1283
                session.query(
1284
                    EgonPfHvLoad.load_id,
1285
                    EgonPfHvLoadTimeseries.p_set,
1286
                )
1287
                .join(
1288
                    EgonPfHvLoadTimeseries,
1289
                    EgonPfHvLoadTimeseries.load_id == EgonPfHvLoad.load_id,
1290
                )
1291
                .filter(
1292
                    EgonPfHvLoad.carrier == "land transport EV",
1293
                    EgonPfHvLoad.scn_name == "eGon2035_lowflex",
1294
                    EgonPfHvLoadTimeseries.scn_name == "eGon2035_lowflex",
1295
                )
1296
            )
1297
        model_charging_load_lowflex = pd.read_sql(
1298
            query.statement, query.session.bind, index_col=None
1299
        )
1300
        charging_load = np.array(
1301
            model_charging_load_lowflex.p_set.to_list()
1302
        ).sum(axis=0)
1303
1304
        # Ratio of driving and charging load should be 0.9 due to charging
1305
        # efficiency
1306
        print("  Compare cumulative loads...")
1307
        print(f"    Driving load (eGon2035): {driving_load.sum() / 1e6} TWh")
1308
        print(
1309
            f"    Dumb charging load (eGon2035_lowflex): "
1310
            f"{charging_load.sum() / 1e6} TWh"
1311
        )
1312
        driving_load_theoretical = (
1313
            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 1331 is not entered. Are you sure this can never be the case?
Loading history...
1314
        )
1315
        np.testing.assert_allclose(
1316
            driving_load.sum(),
1317
            driving_load_theoretical,
1318
            rtol=0.01,
1319
            err_msg=(
1320
                f"The driving load (eGon2035) deviates by more than 1% "
1321
                f"from the theoretical driving load calculated from charging "
1322
                f"load (eGon2035_lowflex) with an efficiency of "
1323
                f"{float(meta_run_config.eta_cp)}."
1324
            ),
1325
        )
1326
1327
    print("=====================================================")
1328
    print("=== SANITY CHECKS FOR MOTORIZED INDIVIDUAL TRAVEL ===")
1329
    print("=====================================================")
1330
1331
    for scenario_name in ["eGon2035", "eGon100RE"]:
1332
        scenario_var_name = DATASET_CFG["scenario"]["variation"][scenario_name]
1333
1334
        print("")
1335
        print(f"SCENARIO: {scenario_name}, VARIATION: {scenario_var_name}")
1336
1337
        # Load scenario params for scenario and scenario variation
1338
        scenario_variation_parameters = get_sector_parameters(
1339
            "mobility", scenario=scenario_name
1340
        )["motorized_individual_travel"][scenario_var_name]
1341
1342
        # Load simBEV run config and tech data
1343
        meta_run_config = read_simbev_metadata_file(
1344
            scenario_name, "config"
1345
        ).loc["basic"]
1346
        meta_tech_data = read_simbev_metadata_file(scenario_name, "tech_data")
1347
1348
        print("")
1349
        print("Checking EV counts...")
1350
        ev_count_alloc = check_ev_allocation()
1351
1352
        print("")
1353
        print("Checking trip data...")
1354
        check_trip_data()
1355
1356
        print("")
1357
        print("Checking model data...")
1358
        check_model_data()
1359
1360
    print("")
1361
    check_model_data_lowflex_eGon2035()
1362
1363
    print("=====================================================")
1364